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