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 thisDir->children.push_back(child); 199 200 // Send a progress report periodically. 201 fVolumeBytesScanned += child->size; 202 fProgress = 100.0 * fVolumeBytesScanned / fVolumeBytesInUse; 203 if (fProgress - fLastReport > kReportInterval) { 204 fLastReport = fProgress; 205 fListener.SendMessage(&fProgressMessage); 206 } 207 } 208 else if (entry.IsDirectory()) { 209 BDirectory childDir(&entry); 210 thisDir->children.push_back(_GetFileInfo(&childDir, thisDir)); 211 } 212 thisDir->count++; 213 } 214 215 vector<FileInfo *>::iterator i = thisDir->children.begin(); 216 while (i != thisDir->children.end()) { 217 thisDir->size += (*i)->size; 218 thisDir->count += (*i)->count; 219 i++; 220 } 221 222 return thisDir; 223 } 224 225 226 void 227 Scanner::_ChangeToDesired() 228 { 229 if (fBusy || fDesiredPath.size() <= 0) 230 return; 231 232 char* workPath = strdup(fDesiredPath.c_str()); 233 char* pathPtr = &workPath[1]; // skip leading '/' 234 char* endOfPath = pathPtr + strlen(pathPtr); 235 236 // Check the name of the root directory. It is a special case because 237 // it has no parent data structure. 238 FileInfo* checkDir = fSnapshot->rootDir; 239 FileInfo* prefDir = NULL; 240 char* sep = strchr(pathPtr, '/'); 241 if (sep != NULL) 242 *sep = '\0'; 243 244 if (strcmp(pathPtr, checkDir->ref.name) == 0) { 245 // Root directory matches, so follow the remainder of the path as 246 // far as possible. 247 prefDir = checkDir; 248 pathPtr += 1 + strlen(pathPtr); 249 while (pathPtr < endOfPath) { 250 sep = strchr(pathPtr, '/'); 251 if (sep != NULL) 252 *sep = '\0'; 253 254 checkDir = prefDir->FindChild(pathPtr); 255 if (checkDir == NULL || checkDir->children.size() == 0) 256 break; 257 258 pathPtr += 1 + strlen(pathPtr); 259 prefDir = checkDir; 260 } 261 } 262 263 // If the desired path is the volume's root directory, default to the 264 // volume display. 265 if (prefDir == fSnapshot->rootDir) 266 prefDir = NULL; 267 268 ChangeDir(prefDir); 269 270 free(workPath); 271 fDesiredPath.erase(); 272 } 273 274 275