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