xref: /haiku/src/kits/tracker/EntryIterator.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 #include <Debug.h>
36 #include <Entry.h>
37 #include <Path.h>
38 
39 #include <new>
40 #include <string.h>
41 
42 #include "EntryIterator.h"
43 #include "NodeWalker.h"
44 #include "ObjectList.h"
45 
46 TWalkerWrapper::TWalkerWrapper(WALKER_NS::TWalker *walker)
47 	:	fWalker(walker),
48 		fStatus(B_OK)
49 {
50 }
51 
52 TWalkerWrapper::~TWalkerWrapper()
53 {
54 	delete fWalker;
55 }
56 
57 status_t
58 TWalkerWrapper::InitCheck() const
59 {
60 	return fStatus;
61 }
62 
63 status_t
64 TWalkerWrapper::GetNextEntry(BEntry *entry, bool traverse)
65 {
66 	fStatus = fWalker->GetNextEntry(entry, traverse);
67 	return fStatus;
68 }
69 
70 status_t
71 TWalkerWrapper::GetNextRef(entry_ref *ref)
72 {
73 	fStatus = fWalker->GetNextRef(ref);
74 	return fStatus;
75 }
76 
77 int32
78 TWalkerWrapper::GetNextDirents(struct dirent *buffer, size_t length, int32 count)
79 {
80 	int32 result = fWalker->GetNextDirents(buffer, length, count);
81 	fStatus = result < 0 ? result : (result ? B_OK : B_ENTRY_NOT_FOUND);
82 	return result;
83 }
84 
85 status_t
86 TWalkerWrapper::Rewind()
87 {
88 	return fWalker->Rewind();
89 }
90 
91 int32
92 TWalkerWrapper::CountEntries()
93 {
94 	return fWalker->CountEntries();
95 }
96 
97 EntryListBase::EntryListBase()
98 	:	fStatus(B_OK)
99 {
100 }
101 
102 status_t
103 EntryListBase::InitCheck() const
104 {
105 	 return fStatus;
106 }
107 
108 dirent *
109 EntryListBase::Next(dirent *ent)
110 {
111 	return (dirent *)((char *)ent + ent->d_reclen + sizeof(dirent));
112 }
113 
114 CachedEntryIterator::CachedEntryIterator(BEntryList *iterator, int32 numEntries,
115 	bool sortInodes)
116 	:	fIterator(iterator),
117 		fEntryRefBuffer(NULL),
118 		fCacheSize(numEntries),
119 		fNumEntries(0),
120 		fIndex(0),
121 		fDirentBuffer(NULL),
122 		fCurrentDirent(NULL),
123 		fSortInodes(sortInodes),
124 		fSortedList(NULL),
125 		fEntryBuffer(NULL)
126 {
127 }
128 
129 
130 CachedEntryIterator::~CachedEntryIterator()
131 {
132 	delete [] fEntryRefBuffer;
133 	free(fDirentBuffer);
134 	delete fSortedList;
135 	delete [] fEntryBuffer;
136 }
137 
138 status_t
139 CachedEntryIterator::GetNextEntry(BEntry *result, bool traverse)
140 {
141 	ASSERT(!fDirentBuffer);
142 	ASSERT(!fEntryRefBuffer);
143 
144 	if (!fEntryBuffer) {
145 		fEntryBuffer = new BEntry [fCacheSize];
146 		ASSERT(fIndex == 0 && fNumEntries == 0);
147 	}
148 	if (fIndex >= fNumEntries) {
149 		// fill up the buffer or stop if error; keep error around
150 		// and return it when appropriate
151 		fStatus = B_OK;
152 		for (fNumEntries = 0; fNumEntries < fCacheSize; fNumEntries++) {
153 			fStatus = fIterator->GetNextEntry(&fEntryBuffer[fNumEntries],
154 				traverse);
155 			if (fStatus != B_OK)
156 				break;
157 		}
158 		fIndex = 0;
159 	}
160 	*result = fEntryBuffer[fIndex++];
161 	if (fIndex > fNumEntries)
162 		// we are at the end of the cache we loaded up, time to return
163 		// an error, if we had one
164 		return fStatus;
165 
166 	return B_OK;
167 }
168 
169 status_t
170 CachedEntryIterator::GetNextRef(entry_ref *ref)
171 {
172 	ASSERT(!fDirentBuffer);
173 	ASSERT(!fEntryBuffer);
174 
175 	if (!fEntryRefBuffer) {
176 		fEntryRefBuffer = new entry_ref[fCacheSize];
177 		ASSERT(fIndex == 0 && fNumEntries == 0);
178 	}
179 
180 	if (fIndex >= fNumEntries) {
181 		// fill up the buffer or stop if error; keep error around
182 		// and return it when appropriate
183 		fStatus = B_OK;
184 		for (fNumEntries = 0; fNumEntries < fCacheSize; fNumEntries++) {
185 			fStatus = fIterator->GetNextRef(&fEntryRefBuffer[fNumEntries]);
186 			if (fStatus != B_OK)
187 				break;
188 		}
189 		fIndex = 0;
190 	}
191 	*ref = fEntryRefBuffer[fIndex++];
192 	if (fIndex > fNumEntries)
193 		// we are at the end of the cache we loaded up, time to return
194 		// an error, if we had one
195 		return fStatus;
196 
197 	return B_OK;
198 }
199 
200 
201 static int
202 CompareInode(const dirent *ent1, const dirent *ent2)
203 {
204 	if (ent1->d_ino < ent2->d_ino)
205 		return -1;
206 	else if (ent1->d_ino == ent2->d_ino)
207 		return 0;
208 	else
209 		return 1;
210 }
211 
212 int32
213 CachedEntryIterator::GetNextDirents(struct dirent *ent, size_t size,
214 	int32 count)
215 {
216 	ASSERT(!fEntryRefBuffer);
217 	if (!fDirentBuffer) {
218 		fDirentBuffer = (dirent *)malloc(kDirentBufferSize);
219 		ASSERT(fIndex == 0 && fNumEntries == 0);
220 		ASSERT(size > sizeof(dirent) + B_FILE_NAME_LENGTH);
221 	}
222 
223 	if (!count)
224 		return 0;
225 
226 	if (fIndex >= fNumEntries) {
227 		// we are out of stock, cache em up
228 		fCurrentDirent = fDirentBuffer;
229 		uint32 bufferRemain = kDirentBufferSize;
230 		for (fNumEntries = 0; fNumEntries < fCacheSize; ) {
231 			int32 count = fIterator->GetNextDirents(fCurrentDirent,
232 				bufferRemain, 1);
233 
234 			if (count <= 0)
235 				break;
236 
237 			fNumEntries += count;
238 
239 			int32 currentDirentSize = fCurrentDirent->d_reclen + (ssize_t)sizeof(dirent);
240 			bufferRemain -= currentDirentSize;
241 			if (bufferRemain < (sizeof(dirent) + B_FILE_NAME_LENGTH))
242 				// cant fit a big entryRef in the buffer, just bail
243 				// and start from scratch
244 				break;
245 
246 			fCurrentDirent = (dirent *)((char *)fCurrentDirent + currentDirentSize);
247 		}
248 		fCurrentDirent = fDirentBuffer;
249 		if (fSortInodes) {
250 			if (!fSortedList)
251 				fSortedList = new BObjectList<dirent>(fCacheSize);
252 			else
253 				fSortedList->MakeEmpty();
254 
255 			for (int32 count = 0; count < fNumEntries; count++) {
256 				fSortedList->AddItem(fCurrentDirent, 0);
257 				fCurrentDirent = Next(fCurrentDirent);
258 			}
259 			fSortedList->SortItems(CompareInode);
260 			fCurrentDirent = fDirentBuffer;
261 		}
262 		fIndex = 0;
263 	}
264 	if (fIndex >= fNumEntries)
265 		// we are done, no more dirents left
266 		return 0;
267 
268 	if (fSortInodes)
269 		fCurrentDirent = fSortedList->ItemAt(fIndex);
270 
271 	fIndex++;
272 	uint32 currentDirentSize = fCurrentDirent->d_reclen + sizeof(dirent);
273 	ASSERT(currentDirentSize <= size);
274 	if (currentDirentSize > size)
275 		return 0;
276 
277 	memcpy(ent, fCurrentDirent, currentDirentSize);
278 
279 	if (!fSortInodes)
280 		fCurrentDirent = (dirent *)((char *)fCurrentDirent + currentDirentSize);
281 
282 	return 1;
283 }
284 
285 status_t
286 CachedEntryIterator::Rewind()
287 {
288 	fIndex = 0;
289 	fNumEntries = 0;
290 	fCurrentDirent = NULL;
291 	fStatus = B_OK;
292 
293 	delete fSortedList;
294 	fSortedList = NULL;
295 
296 	return fIterator->Rewind();
297 }
298 
299 int32
300 CachedEntryIterator::CountEntries()
301 {
302 	return fIterator->CountEntries();
303 }
304 
305 void
306 CachedEntryIterator::SetTo(BEntryList *iterator)
307 {
308 	fIndex = 0;
309 	fNumEntries = 0;
310 	fStatus = B_OK;
311 	fIterator = iterator;
312 }
313 
314 CachedDirectoryEntryList::CachedDirectoryEntryList(const BDirectory &dir)
315 	:	CachedEntryIterator(0, 40, true),
316 		fDir(dir)
317 {
318 	fStatus = fDir.InitCheck();
319 	SetTo(&fDir);
320 }
321 
322 CachedDirectoryEntryList::~CachedDirectoryEntryList()
323 {
324 }
325 
326 
327 DirectoryEntryList::DirectoryEntryList(const BDirectory &dir)
328 	:	fDir(dir)
329 {
330 	fStatus = fDir.InitCheck();
331 }
332 
333 status_t
334 DirectoryEntryList::GetNextEntry(BEntry *entry, bool traverse)
335 {
336 	fStatus = fDir.GetNextEntry(entry, traverse);
337 	return fStatus;
338 }
339 
340 status_t
341 DirectoryEntryList::GetNextRef(entry_ref *ref)
342 {
343 	fStatus = fDir.GetNextRef(ref);
344 	return fStatus;
345 }
346 
347 int32
348 DirectoryEntryList::GetNextDirents(struct dirent *buffer, size_t length,
349 	int32 count)
350 {
351 	fStatus = fDir.GetNextDirents(buffer, length, count);
352 	return fStatus;
353 }
354 
355 status_t
356 DirectoryEntryList::Rewind()
357 {
358 	fStatus = fDir.Rewind();
359 	return fStatus;
360 }
361 
362 int32
363 DirectoryEntryList::CountEntries()
364 {
365 	return fDir.CountEntries();
366 }
367 
368 
369 EntryIteratorList::EntryIteratorList()
370 	:	fList(5, true),
371 		fCurrentIndex(0)
372 {
373 }
374 
375 EntryIteratorList::~EntryIteratorList()
376 {
377 	int32 count = fList.CountItems();
378 	for (;count; count--) {
379 		// workaround for BEntryList not having a proper destructor
380 		BEntryList *entry = fList.RemoveItemAt(count - 1);
381 		EntryListBase *fixedEntry = dynamic_cast<EntryListBase *>(entry);
382 
383 		if (fixedEntry)
384 			delete fixedEntry;
385 		else
386 			delete entry;
387 	}
388 }
389 
390 
391 void
392 EntryIteratorList::AddItem(BEntryList *walker)
393 {
394 	fList.AddItem(walker);
395 }
396 
397 status_t
398 EntryIteratorList::GetNextEntry(BEntry *entry, bool traverse)
399 {
400 	for (;;) {
401 		if (fCurrentIndex >= fList.CountItems()) {
402 			fStatus = B_ENTRY_NOT_FOUND;
403 			break;
404 		}
405 
406 		fStatus = fList.ItemAt(fCurrentIndex)->GetNextEntry(entry, traverse);
407 		if (fStatus != B_ENTRY_NOT_FOUND)
408 			break;
409 
410 		fCurrentIndex++;
411 	}
412 	return fStatus;
413 }
414 
415 status_t
416 EntryIteratorList::GetNextRef(entry_ref *ref)
417 {
418 	for (;;) {
419 		if (fCurrentIndex >= fList.CountItems()) {
420 			fStatus = B_ENTRY_NOT_FOUND;
421 			break;
422 		}
423 
424 		fStatus = fList.ItemAt(fCurrentIndex)->GetNextRef(ref);
425 		if (fStatus != B_ENTRY_NOT_FOUND)
426 			break;
427 
428 		fCurrentIndex++;
429 	}
430 	return fStatus;
431 }
432 
433 int32
434 EntryIteratorList::GetNextDirents(struct dirent *buffer, size_t length, int32 count)
435 {
436 	int32 result = 0;
437 	for (;;) {
438 		if (fCurrentIndex >= fList.CountItems()) {
439 			fStatus = B_ENTRY_NOT_FOUND;
440 			break;
441 		}
442 
443 		result = fList.ItemAt(fCurrentIndex)->GetNextDirents(buffer, length, count);
444 		if (result > 0) {
445 			fStatus = B_OK;
446 			break;
447 		}
448 
449 		fCurrentIndex++;
450 	}
451 	return result;
452 }
453 
454 status_t
455 EntryIteratorList::Rewind()
456 {
457 	fCurrentIndex = 0;
458 	int32 count = fList.CountItems();
459 	for (int32 index = 0; index < count; index++)
460 		fStatus = fList.ItemAt(index)->Rewind();
461 
462 	return fStatus;
463 }
464 
465 int32
466 EntryIteratorList::CountEntries()
467 {
468 	int32 result = 0;
469 
470 	int32 count = fList.CountItems();
471 	for (int32 index = 0; index < count; index++)
472 		result += fList.ItemAt(fCurrentIndex)->CountEntries();
473 
474 	return result;
475 }
476 
477 
478 CachedEntryIteratorList::CachedEntryIteratorList()
479 	:	CachedEntryIterator(0, 10, true)
480 {
481 	fStatus = B_OK;
482 	SetTo(&fIteratorList);
483 }
484 
485 void
486 CachedEntryIteratorList::AddItem(BEntryList *walker)
487 {
488 	fIteratorList.AddItem(walker);
489 }
490 
491