/*
* Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT/X11 license.
*
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
* as long as it is accompanied by it's documentation and this copyright notice.
* The software comes with no warranty, etc.
*/
#include "Scanner.h"
#include <stdlib.h>
#include <string.h>
#include <Catalog.h>
#include <Directory.h>
#include "DiskUsage.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Scanner"
using std::vector;
Scanner::Scanner(BVolume *v, BHandler *handler)
:
BLooper(),
fListener(handler),
fDoneMessage(kScanDone),
fProgressMessage(kScanProgress),
fVolume(v),
fSnapshot(NULL),
fDesiredPath(),
fTask(),
fBusy(false),
fQuitRequested(false)
{
Run();
}
Scanner::~Scanner()
{
delete fSnapshot;
}
void
Scanner::MessageReceived(BMessage* message)
{
switch (message->what) {
case kScanRefresh:
{
FileInfo* startInfo;
if (message->FindPointer(kNameFilePtr, (void **)&startInfo)
== B_OK)
_RunScan(startInfo);
break;
}
default:
BLooper::MessageReceived(message);
break;
}
}
void
Scanner::Refresh(FileInfo* startInfo)
{
if (fBusy)
return;
fBusy = true;
// Remember the current directory, if any, so we can return to it after
// the scanning is done.
if (fSnapshot != NULL && fSnapshot->currentDir != NULL)
fSnapshot->currentDir->GetPath(fDesiredPath);
fTask.assign(kEmptyStr);
BMessage msg(kScanRefresh);
msg.AddPointer(kNameFilePtr, startInfo);
PostMessage(&msg);
}
void
Scanner::Cancel()
{
if (!fBusy)
return;
fQuitRequested = true;
}
void
Scanner::SetDesiredPath(string &path)
{
fDesiredPath = path;
if (fSnapshot == NULL)
Refresh();
else
_ChangeToDesired();
}
void
Scanner::RequestQuit()
{
// If the looper thread is scanning, we won't succeed to grab the lock,
// since the thread is scanning from within MessageReceived(). Then by
// setting fQuitRequested, the thread will stop scanning and only after
// that happened we succeed to grab the lock. So this line of action
// should be safe.
fQuitRequested = true;
Lock();
Quit();
}
// #pragma mark - private
bool
Scanner::_DirectoryContains(FileInfo* currentDir, entry_ref* ref)
{
vector<FileInfo*>::iterator i = currentDir->children.begin();
bool contains = currentDir->ref == *ref;
while (!contains && i != currentDir->children.end()) {
contains |= _DirectoryContains((*i), ref);
i++;
}
return contains;
}
void
Scanner::_RunScan(FileInfo* startInfo)
{
fQuitRequested = false;
BString stringScan(B_TRANSLATE("Scanning %refName%"));
if (startInfo == NULL || startInfo == fSnapshot->rootDir) {
VolumeSnapshot* previousSnapshot = fSnapshot;
fSnapshot = new VolumeSnapshot(fVolume);
stringScan.ReplaceFirst("%refName%", fSnapshot->name.c_str());
fTask = stringScan.String();
fVolumeBytesInUse = fSnapshot->capacity - fSnapshot->freeBytes;
fVolumeBytesScanned = 0;
fProgress = 0.0;
fLastReport = -1.0;
BDirectory root;
fVolume->GetRootDirectory(&root);
fSnapshot->rootDir = _GetFileInfo(&root, NULL);
if (fSnapshot->rootDir == NULL) {
delete fSnapshot;
fSnapshot = previousSnapshot;
fBusy = false;
fListener.SendMessage(&fDoneMessage);
return;
}
FileInfo* freeSpace = new FileInfo;
freeSpace->pseudo = true;
BString string(B_TRANSLATE("Free on %refName%"));
string.ReplaceFirst("%refName%", fSnapshot->name.c_str());
freeSpace->ref.set_name(string.String());
freeSpace->size = fSnapshot->freeBytes;
fSnapshot->freeSpace = freeSpace;
fSnapshot->currentDir = NULL;
delete previousSnapshot;
} else {
off_t previousVolumeCapacity = fSnapshot->capacity;
off_t previousVolumeFreeBytes = fSnapshot->freeBytes;
fSnapshot->capacity = fVolume->Capacity();
fSnapshot->freeBytes = fVolume->FreeBytes();
stringScan.ReplaceFirst("%refName%", startInfo->ref.name);
fTask = stringScan.String();
fVolumeBytesInUse = fSnapshot->capacity - fSnapshot->freeBytes;
fVolumeBytesScanned = fVolumeBytesInUse - startInfo->size; //best guess
fProgress = fVolumeBytesScanned / fVolumeBytesInUse;
fLastReport = -1.0;
BDirectory startDir(&startInfo->ref);
if (startDir.InitCheck() == B_OK) {
FileInfo *parent = startInfo->parent;
vector<FileInfo *>::iterator i = parent->children.begin();
FileInfo* newInfo = _GetFileInfo(&startDir, parent);
if (newInfo == NULL) {
fSnapshot->capacity = previousVolumeCapacity;
fSnapshot->freeBytes = previousVolumeFreeBytes;
fBusy = false;
fListener.SendMessage(&fDoneMessage);
return;
}
while (i != parent->children.end() && *i != startInfo)
i++;
int idx = i - parent->children.begin();
parent->children[idx] = newInfo;
// Fixup count and size fields in parent directory.
off_t sizeDiff = newInfo->size - startInfo->size;
off_t countDiff = newInfo->count - startInfo->count;
while (parent != NULL) {
parent->size += sizeDiff;
parent->count += countDiff;
parent = parent->parent;
}
delete startInfo;
}
}
fBusy = false;
_ChangeToDesired();
fListener.SendMessage(&fDoneMessage);
}
FileInfo*
Scanner::_GetFileInfo(BDirectory* dir, FileInfo* parent)
{
FileInfo* thisDir = new FileInfo;
BEntry entry;
dir->GetEntry(&entry);
entry.GetRef(&thisDir->ref);
thisDir->parent = parent;
thisDir->count = 0;
while (true) {
if (fQuitRequested) {
delete thisDir;
return NULL;
}
if (dir->GetNextEntry(&entry) == B_ENTRY_NOT_FOUND)
break;
if (entry.IsSymLink())
continue;
if (entry.IsFile()) {
entry_ref ref;
if ((entry.GetRef(&ref) == B_OK) && (ref.device != Device()))
continue;
FileInfo *child = new FileInfo;
entry.GetRef(&child->ref);
entry.GetSize(&child->size);
child->parent = thisDir;
child->color = -1;
thisDir->children.push_back(child);
// Send a progress report periodically.
fVolumeBytesScanned += child->size;
fProgress = (float)fVolumeBytesScanned / fVolumeBytesInUse;
if (fProgress - fLastReport > kReportInterval) {
fLastReport = fProgress;
fListener.SendMessage(&fProgressMessage);
}
}
else if (entry.IsDirectory()) {
BDirectory childDir(&entry);
thisDir->children.push_back(_GetFileInfo(&childDir, thisDir));
}
thisDir->count++;
}
vector<FileInfo *>::iterator i = thisDir->children.begin();
while (i != thisDir->children.end()) {
thisDir->size += (*i)->size;
thisDir->count += (*i)->count;
i++;
}
return thisDir;
}
void
Scanner::_ChangeToDesired()
{
if (fBusy || fDesiredPath.size() <= 0)
return;
char* workPath = strdup(fDesiredPath.c_str());
char* pathPtr = &workPath[1]; // skip leading '/'
char* endOfPath = pathPtr + strlen(pathPtr);
// Check the name of the root directory. It is a special case because
// it has no parent data structure.
FileInfo* checkDir = fSnapshot->rootDir;
FileInfo* prefDir = NULL;
char* sep = strchr(pathPtr, '/');
if (sep != NULL)
*sep = '\0';
if (strcmp(pathPtr, checkDir->ref.name) == 0) {
// Root directory matches, so follow the remainder of the path as
// far as possible.
prefDir = checkDir;
pathPtr += 1 + strlen(pathPtr);
while (pathPtr < endOfPath) {
sep = strchr(pathPtr, '/');
if (sep != NULL)
*sep = '\0';
checkDir = prefDir->FindChild(pathPtr);
if (checkDir == NULL || checkDir->children.size() == 0)
break;
pathPtr += 1 + strlen(pathPtr);
prefDir = checkDir;
}
}
// If the desired path is the volume's root directory, default to the
// volume display.
if (prefDir == fSnapshot->rootDir)
prefDir = NULL;
ChangeDir(prefDir);
free(workPath);
fDesiredPath.erase();
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fVolumeBytesInUse, fVolumeBytesScanned, fProgress, fLastReport.