2 * Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
3 * Distributed under the terms of the MIT/X11 license.
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.
17 #include <Directory.h>
19 #include "DiskUsage.h"
21 #undef B_TRANSLATION_CONTEXT
22 #define B_TRANSLATION_CONTEXT "Scanner"
27 Scanner::Scanner(BVolume
*v
, BHandler
*handler
)
31 fDoneMessage(kScanDone
),
32 fProgressMessage(kScanProgress
),
51 Scanner::MessageReceived(BMessage
* message
)
53 switch (message
->what
) {
57 if (message
->FindPointer(kNameFilePtr
, (void **)&startInfo
)
64 BLooper::MessageReceived(message
);
71 Scanner::Refresh(FileInfo
* startInfo
)
78 // Remember the current directory, if any, so we can return to it after
79 // the scanning is done.
80 if (fSnapshot
!= NULL
&& fSnapshot
->currentDir
!= NULL
)
81 fSnapshot
->currentDir
->GetPath(fDesiredPath
);
83 fTask
.assign(kEmptyStr
);
85 BMessage
msg(kScanRefresh
);
86 msg
.AddPointer(kNameFilePtr
, startInfo
);
97 fQuitRequested
= true;
102 Scanner::SetDesiredPath(string
&path
)
106 if (fSnapshot
== NULL
)
114 Scanner::RequestQuit()
116 // If the looper thread is scanning, we won't succeed to grab the lock,
117 // since the thread is scanning from within MessageReceived(). Then by
118 // setting fQuitRequested, the thread will stop scanning and only after
119 // that happened we succeed to grab the lock. So this line of action
121 fQuitRequested
= true;
127 // #pragma mark - private
131 Scanner::_DirectoryContains(FileInfo
* currentDir
, entry_ref
* ref
)
133 vector
<FileInfo
*>::iterator i
= currentDir
->children
.begin();
134 bool contains
= currentDir
->ref
== *ref
;
135 while (!contains
&& i
!= currentDir
->children
.end()) {
136 contains
|= _DirectoryContains((*i
), ref
);
144 Scanner::_RunScan(FileInfo
* startInfo
)
146 fQuitRequested
= false;
147 BString
stringScan(B_TRANSLATE("Scanning %refName%"));
149 if (startInfo
== NULL
|| startInfo
== fSnapshot
->rootDir
) {
150 VolumeSnapshot
* previousSnapshot
= fSnapshot
;
151 fSnapshot
= new VolumeSnapshot(fVolume
);
152 stringScan
.ReplaceFirst("%refName%", fSnapshot
->name
.c_str());
153 fTask
= stringScan
.String();
154 fVolumeBytesInUse
= fSnapshot
->capacity
- fSnapshot
->freeBytes
;
155 fVolumeBytesScanned
= 0;
160 fVolume
->GetRootDirectory(&root
);
161 fSnapshot
->rootDir
= _GetFileInfo(&root
, NULL
);
162 if (fSnapshot
->rootDir
== NULL
) {
164 fSnapshot
= previousSnapshot
;
166 fListener
.SendMessage(&fDoneMessage
);
169 FileInfo
* freeSpace
= new FileInfo
;
170 freeSpace
->pseudo
= true;
171 BString
string(B_TRANSLATE("Free on %refName%"));
172 string
.ReplaceFirst("%refName%", fSnapshot
->name
.c_str());
173 freeSpace
->ref
.set_name(string
.String());
174 freeSpace
->size
= fSnapshot
->freeBytes
;
175 fSnapshot
->freeSpace
= freeSpace
;
177 fSnapshot
->currentDir
= NULL
;
179 delete previousSnapshot
;
181 off_t previousVolumeCapacity
= fSnapshot
->capacity
;
182 off_t previousVolumeFreeBytes
= fSnapshot
->freeBytes
;
183 fSnapshot
->capacity
= fVolume
->Capacity();
184 fSnapshot
->freeBytes
= fVolume
->FreeBytes();
185 stringScan
.ReplaceFirst("%refName%", startInfo
->ref
.name
);
186 fTask
= stringScan
.String();
187 fVolumeBytesInUse
= fSnapshot
->capacity
- fSnapshot
->freeBytes
;
188 fVolumeBytesScanned
= fVolumeBytesInUse
- startInfo
->size
; //best guess
189 fProgress
= fVolumeBytesScanned
/ fVolumeBytesInUse
;
192 BDirectory
startDir(&startInfo
->ref
);
193 if (startDir
.InitCheck() == B_OK
) {
194 FileInfo
*parent
= startInfo
->parent
;
195 vector
<FileInfo
*>::iterator i
= parent
->children
.begin();
196 FileInfo
* newInfo
= _GetFileInfo(&startDir
, parent
);
197 if (newInfo
== NULL
) {
198 fSnapshot
->capacity
= previousVolumeCapacity
;
199 fSnapshot
->freeBytes
= previousVolumeFreeBytes
;
201 fListener
.SendMessage(&fDoneMessage
);
204 while (i
!= parent
->children
.end() && *i
!= startInfo
)
207 int idx
= i
- parent
->children
.begin();
208 parent
->children
[idx
] = newInfo
;
210 // Fixup count and size fields in parent directory.
211 off_t sizeDiff
= newInfo
->size
- startInfo
->size
;
212 off_t countDiff
= newInfo
->count
- startInfo
->count
;
213 while (parent
!= NULL
) {
214 parent
->size
+= sizeDiff
;
215 parent
->count
+= countDiff
;
216 parent
= parent
->parent
;
224 fListener
.SendMessage(&fDoneMessage
);
229 Scanner::_GetFileInfo(BDirectory
* dir
, FileInfo
* parent
)
231 FileInfo
* thisDir
= new FileInfo
;
233 dir
->GetEntry(&entry
);
234 entry
.GetRef(&thisDir
->ref
);
235 thisDir
->parent
= parent
;
239 if (fQuitRequested
) {
244 if (dir
->GetNextEntry(&entry
) == B_ENTRY_NOT_FOUND
)
246 if (entry
.IsSymLink())
250 if (entry
.IsFile()) {
252 if ((entry
.GetRef(&ref
) == B_OK
) && (ref
.device
!= Device()))
254 FileInfo
*child
= new FileInfo
;
255 entry
.GetRef(&child
->ref
);
256 entry
.GetSize(&child
->size
);
257 child
->parent
= thisDir
;
259 thisDir
->children
.push_back(child
);
261 // Send a progress report periodically.
262 fVolumeBytesScanned
+= child
->size
;
263 fProgress
= (float)fVolumeBytesScanned
/ fVolumeBytesInUse
;
264 if (fProgress
- fLastReport
> kReportInterval
) {
265 fLastReport
= fProgress
;
266 fListener
.SendMessage(&fProgressMessage
);
269 else if (entry
.IsDirectory()) {
270 BDirectory
childDir(&entry
);
271 thisDir
->children
.push_back(_GetFileInfo(&childDir
, thisDir
));
276 vector
<FileInfo
*>::iterator i
= thisDir
->children
.begin();
277 while (i
!= thisDir
->children
.end()) {
278 thisDir
->size
+= (*i
)->size
;
279 thisDir
->count
+= (*i
)->count
;
288 Scanner::_ChangeToDesired()
290 if (fBusy
|| fDesiredPath
.size() <= 0)
293 char* workPath
= strdup(fDesiredPath
.c_str());
294 char* pathPtr
= &workPath
[1]; // skip leading '/'
295 char* endOfPath
= pathPtr
+ strlen(pathPtr
);
297 // Check the name of the root directory. It is a special case because
298 // it has no parent data structure.
299 FileInfo
* checkDir
= fSnapshot
->rootDir
;
300 FileInfo
* prefDir
= NULL
;
301 char* sep
= strchr(pathPtr
, '/');
305 if (strcmp(pathPtr
, checkDir
->ref
.name
) == 0) {
306 // Root directory matches, so follow the remainder of the path as
309 pathPtr
+= 1 + strlen(pathPtr
);
310 while (pathPtr
< endOfPath
) {
311 sep
= strchr(pathPtr
, '/');
315 checkDir
= prefDir
->FindChild(pathPtr
);
316 if (checkDir
== NULL
|| checkDir
->children
.size() == 0)
319 pathPtr
+= 1 + strlen(pathPtr
);
324 // If the desired path is the volume's root directory, default to the
326 if (prefDir
== fSnapshot
->rootDir
)
332 fDesiredPath
.erase();