1 /* 2 * Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved. 3 * Distributed under the terms of the MIT/X11 license. 4 * 5 * Copyright (c) 1999 Mike Steed. You are free to use and distribute this software 6 * as long as it is accompanied by it's documentation and this copyright notice. 7 * The software comes with no warranty, etc. 8 */ 9 #include "Scanner.h" 10 11 #include <Directory.h> 12 13 #include "Common.h" 14 15 using std::vector; 16 17 Scanner::Scanner(BVolume *v, BHandler *handler) 18 : BLooper(), 19 fListener(handler), 20 fDoneMessage(kScanDone), 21 fProgressMessage(kScanProgress), 22 23 fVolume(v), 24 fSnapshot(NULL), 25 fDesiredPath(), 26 fTask(), 27 fBusy(false), 28 fQuitRequested(false) 29 { 30 fDoneMessage.AddPointer(kNameVolPtr, fVolume); 31 fProgressMessage.AddPointer(kNameVolPtr, fVolume); 32 33 Run(); 34 } 35 36 37 Scanner::~Scanner() 38 { 39 delete fSnapshot; 40 } 41 42 43 void 44 Scanner::MessageReceived(BMessage* message) 45 { 46 switch (message->what) { 47 case kScanRefresh: 48 { 49 FileInfo* startInfo; 50 if (message->FindPointer(kNameFilePtr, (void **)&startInfo) == B_OK) 51 _RunScan(startInfo); 52 break; 53 } 54 55 default: 56 BLooper::MessageReceived(message); 57 break; 58 } 59 } 60 61 62 void 63 Scanner::Refresh(FileInfo* startInfo) 64 { 65 if (fBusy) 66 return; 67 68 fBusy = true; 69 70 // Remember the current directory, if any, so we can return to it after 71 // the scanning is done. 72 if (fSnapshot != NULL && fSnapshot->currentDir != NULL) 73 fSnapshot->currentDir->GetPath(fDesiredPath); 74 75 fTask.assign(kEmptyStr); 76 77 BMessage msg(kScanRefresh); 78 msg.AddPointer(kNameFilePtr, startInfo); 79 PostMessage(&msg); 80 } 81 82 83 void 84 Scanner::SetDesiredPath(string &path) 85 { 86 fDesiredPath = path; 87 88 if (fSnapshot == NULL) 89 Refresh(); 90 else 91 _ChangeToDesired(); 92 } 93 94 95 void 96 Scanner::RequestQuit() 97 { 98 // If the looper thread is scanning, we won't succeed to grab the lock, 99 // since the thread is scanning from within MessageReceived(). Then by 100 // setting fQuitRequested, the thread will stop scanning and only after 101 // that happened we succeed to grab the lock. So this line of action 102 // should be safe. 103 fQuitRequested = true; 104 Lock(); 105 Quit(); 106 } 107 108 109 // #pragma mark - private 110 111 112 void 113 Scanner::_RunScan(FileInfo* startInfo) 114 { 115 if (startInfo == NULL || startInfo == fSnapshot->rootDir) { 116 delete fSnapshot; 117 fSnapshot = new VolumeSnapshot(fVolume); 118 fTask = kStrScanningX + fSnapshot->name; 119 fVolumeBytesInUse = fSnapshot->capacity - fSnapshot->freeBytes; 120 fVolumeBytesScanned = 0; 121 fProgress = 0.0; 122 fLastReport = -100.0; 123 124 BDirectory root; 125 fVolume->GetRootDirectory(&root); 126 fSnapshot->rootDir = _GetFileInfo(&root, NULL); 127 128 FileInfo* freeSpace = new FileInfo; 129 freeSpace->pseudo = true; 130 string s = "Free on " + fSnapshot->name; 131 freeSpace->ref.set_name(s.c_str()); 132 freeSpace->size = fSnapshot->freeBytes; 133 fSnapshot->freeSpace = freeSpace; 134 135 fSnapshot->currentDir = NULL; 136 137 } else { 138 fSnapshot->capacity = fVolume->Capacity(); 139 fSnapshot->freeBytes = fVolume->FreeBytes(); 140 fTask = kStrScanningX + string(startInfo->ref.name); 141 fVolumeBytesInUse = fSnapshot->capacity - fSnapshot->freeBytes; 142 fVolumeBytesScanned = fVolumeBytesInUse - startInfo->size; // best guess 143 fProgress = 100.0 * fVolumeBytesScanned / fVolumeBytesInUse; 144 fLastReport = -100.0; 145 146 BDirectory startDir(&startInfo->ref); 147 if (startDir.InitCheck() == B_OK) { 148 FileInfo *parent = startInfo->parent; 149 vector<FileInfo *>::iterator i = parent->children.begin(); 150 FileInfo* newInfo = _GetFileInfo(&startDir, parent); 151 while (i != parent->children.end() && *i != startInfo) 152 i++; 153 154 int idx = i - parent->children.begin(); 155 parent->children[idx] = newInfo; 156 157 // Fixup count and size fields in parent directory. 158 parent->size += newInfo->size - startInfo->size; 159 parent->count += newInfo->count - startInfo->count; 160 161 delete startInfo; 162 } 163 } 164 165 fBusy = false; 166 _ChangeToDesired(); 167 fListener.SendMessage(&fDoneMessage); 168 } 169 170 171 FileInfo* 172 Scanner::_GetFileInfo(BDirectory* dir, FileInfo* parent) 173 { 174 FileInfo* thisDir = new FileInfo; 175 BEntry entry; 176 dir->GetEntry(&entry); 177 entry.GetRef(&thisDir->ref); 178 thisDir->parent = parent; 179 thisDir->count = 0; 180 181 while (true) { 182 if (fQuitRequested) 183 break; 184 185 if (dir->GetNextEntry(&entry) == B_ENTRY_NOT_FOUND) 186 break; 187 if (entry.IsSymLink()) 188 continue; 189 190 if (entry.IsFile()) { 191 FileInfo *child = new FileInfo; 192 entry.GetRef(&child->ref); 193 entry.GetSize(&child->size); 194 child->parent = thisDir; 195 thisDir->children.push_back(child); 196 197 // Send a progress report periodically. 198 fVolumeBytesScanned += child->size; 199 fProgress = 100.0 * fVolumeBytesScanned / fVolumeBytesInUse; 200 if (fProgress - fLastReport > kReportInterval) { 201 fLastReport = fProgress; 202 fListener.SendMessage(&fProgressMessage); 203 } 204 } 205 else if (entry.IsDirectory()) { 206 BDirectory childDir(&entry); 207 thisDir->children.push_back(_GetFileInfo(&childDir, thisDir)); 208 } 209 thisDir->count++; 210 } 211 212 vector<FileInfo *>::iterator i = thisDir->children.begin(); 213 while (i != thisDir->children.end()) { 214 thisDir->size += (*i)->size; 215 thisDir->count += (*i)->count; 216 i++; 217 } 218 219 return thisDir; 220 } 221 222 223 void 224 Scanner::_ChangeToDesired() 225 { 226 if (fBusy || fDesiredPath.size() <= 0) 227 return; 228 229 char* workPath = strdup(fDesiredPath.c_str()); 230 char* pathPtr = &workPath[1]; // skip leading '/' 231 char* endOfPath = pathPtr + strlen(pathPtr); 232 233 // Check the name of the root directory. It is a special case because 234 // it has no parent data structure. 235 FileInfo* checkDir = fSnapshot->rootDir; 236 FileInfo* prefDir = NULL; 237 char* sep = strchr(pathPtr, '/'); 238 if (sep != NULL) 239 *sep = '\0'; 240 241 if (strcmp(pathPtr, checkDir->ref.name) == 0) { 242 // Root directory matches, so follow the remainder of the path as 243 // far as possible. 244 prefDir = checkDir; 245 pathPtr += 1 + strlen(pathPtr); 246 while (pathPtr < endOfPath) { 247 sep = strchr(pathPtr, '/'); 248 if (sep != NULL) 249 *sep = '\0'; 250 251 checkDir = prefDir->FindChild(pathPtr); 252 if (checkDir == NULL || checkDir->children.size() == 0) 253 break; 254 255 pathPtr += 1 + strlen(pathPtr); 256 prefDir = checkDir; 257 } 258 } 259 260 // If the desired path is the volume's root directory, default to the 261 // volume display. 262 if (prefDir == fSnapshot->rootDir) 263 prefDir = NULL; 264 265 ChangeDir(prefDir); 266 267 free(workPath); 268 fDesiredPath.erase(); 269 } 270 271 272