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 <stdio.h>
38#include <string.h>
39#include <errno.h>
40#include <SupportDefs.h>
41
42#include "NodeWalker.h"
43
44
45namespace BTrackerPrivate {
46
47TWalker::~TWalker()
48{
49}
50
51
52// all the following calls are pure virtuals, should not get called
53status_t
54TWalker::GetNextEntry(BEntry*, bool )
55{
56	TRESPASS();
57	return B_ERROR;
58}
59
60
61status_t
62TWalker::GetNextRef(entry_ref*)
63{
64	TRESPASS();
65	return B_ERROR;
66}
67
68
69int32
70TWalker::GetNextDirents(struct dirent*, size_t, int32)
71{
72	TRESPASS();
73	return 0;
74}
75
76
77status_t
78TWalker::Rewind()
79{
80	TRESPASS();
81	return B_ERROR;
82}
83
84
85int32
86TWalker::CountEntries()
87{
88	TRESPASS();
89	return -1;
90}
91
92
93TNodeWalker::TNodeWalker(bool includeTopDirectory)
94	:
95	fDirs(20),
96	fTopIndex(-1),
97	fTopDir(NULL),
98	fIncludeTopDir(includeTopDirectory),
99	fOriginalIncludeTopDir(includeTopDirectory),
100	fJustFile(NULL),
101	fOriginalJustFile(NULL)
102{
103}
104
105
106TNodeWalker::TNodeWalker(const char* path, bool includeTopDirectory)
107	:
108	fDirs(20),
109	fTopIndex(-1),
110	fTopDir(NULL),
111	fIncludeTopDir(includeTopDirectory),
112	fOriginalIncludeTopDir(includeTopDirectory),
113	fJustFile(NULL),
114	fOriginalDirCopy(path),
115	fOriginalJustFile(NULL)
116{
117	if (fOriginalDirCopy.InitCheck() != B_OK) {
118		// not a directory, set up walking a single file
119		fJustFile = new BEntry(path);
120		if (fJustFile->InitCheck() != B_OK) {
121			delete fJustFile;
122			fJustFile = NULL;
123		}
124		fOriginalJustFile = fJustFile;
125	} else {
126		fTopDir = new BDirectory(fOriginalDirCopy);
127		fTopIndex++;
128		fDirs.AddItem(fTopDir);
129	}
130}
131
132
133TNodeWalker::TNodeWalker(const entry_ref* ref, bool includeTopDirectory)
134	:
135	fDirs(20),
136	fTopIndex(-1),
137	fTopDir(NULL),
138	fIncludeTopDir(includeTopDirectory),
139	fOriginalIncludeTopDir(includeTopDirectory),
140	fJustFile(NULL),
141	fOriginalDirCopy(ref),
142	fOriginalJustFile(NULL)
143{
144	if (fOriginalDirCopy.InitCheck() != B_OK) {
145		// not a directory, set up walking a single file
146		fJustFile = new BEntry(ref);
147		if (fJustFile->InitCheck() != B_OK) {
148			delete fJustFile;
149			fJustFile = NULL;
150		}
151		fOriginalJustFile = fJustFile;
152	} else {
153		fTopDir = new BDirectory(fOriginalDirCopy);
154		fTopIndex++;
155		fDirs.AddItem(fTopDir);
156	}
157}
158
159
160TNodeWalker::TNodeWalker(const BDirectory* dir, bool includeTopDirectory)
161	:
162	fDirs(20),
163	fTopIndex(-1),
164	fTopDir(NULL),
165	fIncludeTopDir(includeTopDirectory),
166	fOriginalIncludeTopDir(includeTopDirectory),
167	fJustFile(NULL),
168	fOriginalDirCopy(*dir),
169	fOriginalJustFile(NULL)
170{
171	fTopDir = new BDirectory(*dir);
172	fTopIndex++;
173	fDirs.AddItem(fTopDir);
174}
175
176
177TNodeWalker::TNodeWalker()
178	:
179	fDirs(20),
180	fTopIndex(-1),
181	fTopDir(NULL),
182	fIncludeTopDir(false),
183	fOriginalIncludeTopDir(false),
184	fJustFile(NULL),
185	fOriginalJustFile(NULL)
186{
187}
188
189
190TNodeWalker::TNodeWalker(const char* path)
191	:
192	fDirs(20),
193	fTopIndex(-1),
194	fTopDir(NULL),
195	fIncludeTopDir(false),
196	fOriginalIncludeTopDir(false),
197	fJustFile(NULL),
198	fOriginalDirCopy(path),
199	fOriginalJustFile(NULL)
200{
201	if (fOriginalDirCopy.InitCheck() != B_OK) {
202		// not a directory, set up walking a single file
203		fJustFile = new BEntry(path);
204		if (fJustFile->InitCheck() != B_OK) {
205			delete fJustFile;
206			fJustFile = NULL;
207		}
208		fOriginalJustFile = fJustFile;
209	} else {
210		fTopDir = new BDirectory(fOriginalDirCopy);
211		fTopIndex++;
212		fDirs.AddItem(fTopDir);
213	}
214}
215
216
217TNodeWalker::TNodeWalker(const entry_ref* ref)
218	:
219	fDirs(20),
220	fTopIndex(-1),
221	fTopDir(NULL),
222	fIncludeTopDir(false),
223	fOriginalIncludeTopDir(false),
224	fJustFile(NULL),
225	fOriginalDirCopy(ref),
226	fOriginalJustFile(NULL)
227{
228	if (fOriginalDirCopy.InitCheck() != B_OK) {
229		// not a directory, set up walking a single file
230		fJustFile = new BEntry(ref);
231		if (fJustFile->InitCheck() != B_OK) {
232			delete fJustFile;
233			fJustFile = NULL;
234		}
235		fOriginalJustFile = fJustFile;
236	} else {
237		fTopDir = new BDirectory(fOriginalDirCopy);
238		fTopIndex++;
239		fDirs.AddItem(fTopDir);
240	}
241}
242
243TNodeWalker::TNodeWalker(const BDirectory* dir)
244	:
245	fDirs(20),
246	fTopIndex(-1),
247	fTopDir(NULL),
248	fIncludeTopDir(false),
249	fOriginalIncludeTopDir(false),
250	fJustFile(NULL),
251	fOriginalDirCopy(*dir),
252	fOriginalJustFile(NULL)
253{
254	fTopDir = new BDirectory(*dir);
255	fTopIndex++;
256	fDirs.AddItem(fTopDir);
257}
258
259
260TNodeWalker::~TNodeWalker()
261{
262	delete fOriginalJustFile;
263
264	for (;;) {
265		BDirectory* directory = fDirs.RemoveItemAt(fTopIndex--);
266		if (directory == NULL)
267			break;
268
269		delete directory;
270	}
271}
272
273
274status_t
275TNodeWalker::PopDirCommon()
276{
277	ASSERT(fTopIndex >= 0);
278
279	// done with the old dir, pop it
280	fDirs.RemoveItemAt(fTopIndex);
281	fTopIndex--;
282	delete fTopDir;
283	fTopDir = NULL;
284
285	if (fTopIndex == -1) {
286		// done
287		return B_ENTRY_NOT_FOUND;
288	}
289
290	// point to the new top dir
291	fTopDir = fDirs.ItemAt(fTopIndex);
292
293	return B_OK;
294}
295
296
297void
298TNodeWalker::PushDirCommon(const entry_ref* ref)
299{
300	fTopDir = new BDirectory(ref);
301		// OK to ignore error here. Will
302		// catch at next call to GetNextEntry
303	fTopIndex++;
304	fDirs.AddItem(fTopDir);
305}
306
307
308status_t
309TNodeWalker::GetNextEntry(BEntry* entry, bool traverse)
310{
311	if (fJustFile != NULL) {
312		*entry = *fJustFile;
313		fJustFile = 0;
314		return B_OK;
315	}
316
317	if (fTopDir == NULL) {
318		// done
319		return B_ENTRY_NOT_FOUND;
320	}
321
322	// If requested to include the top directory, return that first.
323	if (fIncludeTopDir) {
324		fIncludeTopDir = false;
325		return fTopDir->GetEntry(entry);
326	}
327
328	// Get the next entry.
329	status_t result = fTopDir->GetNextEntry(entry, traverse);
330	if (result != B_OK) {
331		result = PopDirCommon();
332		if (result != B_OK)
333			return result;
334
335		return GetNextEntry(entry, traverse);
336	}
337	// See if this entry is a directory. If it is then push it onto the
338	// stack
339	entry_ref ref;
340	result = entry->GetRef(&ref);
341
342	if (result == B_OK && fTopDir->Contains(ref.name, B_DIRECTORY_NODE))
343		PushDirCommon(&ref);
344
345	return result;
346}
347
348
349status_t
350TNodeWalker::GetNextRef(entry_ref* ref)
351{
352	if (fJustFile != NULL) {
353		fJustFile->GetRef(ref);
354		fJustFile = 0;
355		return B_OK;
356	}
357
358	if (fTopDir == NULL) {
359		// done
360		return B_ENTRY_NOT_FOUND;
361	}
362
363	// If requested to include the top directory, return that first.
364	if (fIncludeTopDir) {
365		fIncludeTopDir = false;
366		BEntry entry;
367		status_t err = fTopDir->GetEntry(&entry);
368		if (err == B_OK)
369			err = entry.GetRef(ref);
370		return err;
371	}
372
373	// get the next entry
374	status_t err = fTopDir->GetNextRef(ref);
375	if (err != B_OK) {
376		err = PopDirCommon();
377		if (err != B_OK)
378			return err;
379		return GetNextRef(ref);
380	}
381
382	// See if this entry is a directory, if it is then push it onto the stack.
383	if (fTopDir->Contains(ref->name, B_DIRECTORY_NODE))
384		PushDirCommon(ref);
385
386	return B_OK;
387}
388
389
390static int32
391build_dirent(const BEntry* source, struct dirent* ent,
392	size_t size, int32 count)
393{
394	if (source == NULL)
395		return 0;
396
397	entry_ref ref;
398	source->GetRef(&ref);
399
400	size_t recordLength = offsetof(struct dirent, d_name) + strlen(ref.name) + 1;
401	if (recordLength > size || count <= 0) {
402		// can't fit in buffer, bail
403		return 0;
404	}
405
406	// info about this node
407	ent->d_reclen = static_cast<ushort>(recordLength);
408	strcpy(ent->d_name, ref.name);
409	ent->d_dev = ref.device;
410	ent->d_ino = ref.directory;
411
412	// info about the parent
413	BEntry parent;
414	source->GetParent(&parent);
415	if (parent.InitCheck() == B_OK) {
416		entry_ref parentRef;
417		parent.GetRef(&parentRef);
418		ent->d_pdev = parentRef.device;
419		ent->d_pino = parentRef.directory;
420	} else {
421		ent->d_pdev = 0;
422		ent->d_pino = 0;
423	}
424
425	return 1;
426}
427
428
429int32
430TNodeWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count)
431{
432	if (fJustFile != NULL) {
433		if (count == 0)
434			return 0;
435
436		// simulate GetNextDirents by building a single dirent structure
437		int32 result = build_dirent(fJustFile, ent, size, count);
438		fJustFile = 0;
439		return result;
440	}
441
442	if (fTopDir == NULL) {
443		// done
444		return 0;
445	}
446
447	// If requested to include the top directory, return that first.
448	if (fIncludeTopDir) {
449		fIncludeTopDir = false;
450		BEntry entry;
451		if (fTopDir->GetEntry(&entry) < B_OK)
452			return 0;
453
454		return build_dirent(fJustFile, ent, size, count);
455	}
456
457	// get the next entry
458	int32 nextDirent = fTopDir->GetNextDirents(ent, size, count);
459	if (nextDirent == 0) {
460		status_t result = PopDirCommon();
461		if (result != B_OK)
462			return 0;
463
464		return GetNextDirents(ent, size, count);
465	}
466
467	// push any directories in the returned entries onto the stack
468	for (int32 i = 0; i < nextDirent; i++) {
469		if (fTopDir->Contains(ent->d_name, B_DIRECTORY_NODE)) {
470			entry_ref ref(ent->d_dev, ent->d_ino, ent->d_name);
471			PushDirCommon(&ref);
472		}
473		ent = (dirent*)((char*)ent + ent->d_reclen);
474	}
475
476	return nextDirent;
477}
478
479
480status_t
481TNodeWalker::Rewind()
482{
483	if (fOriginalJustFile != NULL) {
484		// single file mode, rewind by pointing to the original file
485		fJustFile = fOriginalJustFile;
486		return B_OK;
487	}
488
489	// pop all the directories and point to the initial one
490	for (;;) {
491		BDirectory* directory = fDirs.RemoveItemAt(fTopIndex--);
492		if (directory == NULL)
493			break;
494
495		delete directory;
496	}
497
498	fTopDir = new BDirectory(fOriginalDirCopy);
499	fTopIndex = 0;
500	fIncludeTopDir = fOriginalIncludeTopDir;
501	fDirs.AddItem(fTopDir);
502
503	return fTopDir->Rewind();
504		// rewind the directory
505}
506
507int32
508TNodeWalker::CountEntries()
509{
510	// should not be calling this
511	TRESPASS();
512	return -1;
513}
514
515
516TVolWalker::TVolWalker(bool knowsAttributes, bool writable,
517	bool includeTopDirectory)
518	:
519	TNodeWalker(includeTopDirectory),
520	fKnowsAttr(knowsAttributes),
521	fWritable(writable)
522{
523	// Get things initialized. Find first volume, or find the first volume
524	// that supports attributes.
525 	NextVolume();
526}
527
528
529TVolWalker::~TVolWalker()
530{
531}
532
533
534status_t
535TVolWalker::NextVolume()
536{
537	// The stack of directoies should be empty.
538	ASSERT(fTopIndex == -1);
539	ASSERT(fTopDir == NULL);
540
541	status_t result;
542	do {
543		result = fVolRoster.GetNextVolume(&fVol);
544		if (result != B_OK)
545			break;
546	} while ((fKnowsAttr && !fVol.KnowsAttr())
547		|| (fWritable && fVol.IsReadOnly()));
548
549	if (result == B_OK) {
550		// Get the root directory to get things started. There's always
551		// a root directory for a volume. So if there is an error then it
552		// means that something is really bad, like the system is out of
553		// memory.  In that case don't worry about truying to skip to the
554		// next volume.
555		fTopDir = new BDirectory();
556		result = fVol.GetRootDirectory(fTopDir);
557		fIncludeTopDir = fOriginalIncludeTopDir;
558		fTopIndex = 0;
559		fDirs.AddItem(fTopDir);
560	}
561
562	return result;
563}
564
565status_t
566TVolWalker::GetNextEntry(BEntry* entry, bool traverse)
567{
568	if (fTopDir == NULL)
569		return B_ENTRY_NOT_FOUND;
570
571	// get the next entry
572	status_t result = _inherited::GetNextEntry(entry, traverse);
573	while (result != B_OK) {
574		// we're done with the current volume, go to the next one
575		result = NextVolume();
576		if (result != B_OK)
577			break;
578
579		result = GetNextEntry(entry, traverse);
580	}
581
582	return result;
583}
584
585
586status_t
587TVolWalker::GetNextRef(entry_ref* ref)
588{
589	if (fTopDir == NULL)
590		return B_ENTRY_NOT_FOUND;
591
592	// Get the next ref.
593	status_t result = _inherited::GetNextRef(ref);
594
595	while (result != B_OK) {
596		// we're done with the current volume, go to the next one
597		result = NextVolume();
598		if (result != B_OK)
599			break;
600		result = GetNextRef(ref);
601	}
602
603	return result;
604}
605
606
607int32
608TVolWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count)
609{
610	if (fTopDir == NULL)
611		return B_ENTRY_NOT_FOUND;
612
613	// get the next dirent
614	status_t result = _inherited::GetNextDirents(ent, size, count);
615	while (result != B_OK) {
616		// we're done with the current volume, go to the next one
617		result = NextVolume();
618		if (result != B_OK)
619			break;
620
621		result = GetNextDirents(ent, size, count);
622	}
623
624	return result;
625}
626
627
628status_t
629TVolWalker::Rewind()
630{
631	fVolRoster.Rewind();
632	return NextVolume();
633}
634
635
636TQueryWalker::TQueryWalker(const char* predicate)
637	:
638	TWalker(),
639	fTime(0)
640{
641	fPredicate = strdup(predicate);
642	NextVolume();
643}
644
645
646TQueryWalker::~TQueryWalker()
647{
648	free((char*)fPredicate);
649	fPredicate = NULL;
650}
651
652
653status_t
654TQueryWalker::GetNextEntry(BEntry* entry, bool traverse)
655{
656	status_t result;
657	do {
658		result = fQuery.GetNextEntry(entry, traverse);
659		if (result == B_ENTRY_NOT_FOUND) {
660			if (NextVolume() != B_OK)
661				break;
662		}
663	} while (result == B_ENTRY_NOT_FOUND);
664
665	return result;
666}
667
668
669status_t
670TQueryWalker::GetNextRef(entry_ref* ref)
671{
672	status_t result;
673
674	for (;;) {
675		result = fQuery.GetNextRef(ref);
676		if (result != B_ENTRY_NOT_FOUND)
677			break;
678
679		result = NextVolume();
680		if (result != B_OK)
681			break;
682	}
683
684	return result;
685}
686
687
688int32
689TQueryWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count)
690{
691	int32 result;
692
693	for (;;) {
694		result = fQuery.GetNextDirents(ent, size, count);
695		if (result != 0)
696			return result;
697
698		if (NextVolume() != B_OK)
699			return 0;
700	}
701
702	return result;
703}
704
705
706status_t
707TQueryWalker::NextVolume()
708{
709	status_t result;
710	do {
711		result = fVolRoster.GetNextVolume(&fVol);
712		if (result != B_OK)
713			break;
714	} while (!fVol.KnowsQuery());
715
716	if (result == B_OK) {
717		result = fQuery.Clear();
718		result = fQuery.SetVolume(&fVol);
719		result = fQuery.SetPredicate(fPredicate);
720		result = fQuery.Fetch();
721	}
722
723	return result;
724}
725
726
727int32
728TQueryWalker::CountEntries()
729{
730	// should not be calling this
731	TRESPASS();
732	return -1;
733}
734
735
736status_t
737TQueryWalker::Rewind()
738{
739	fVolRoster.Rewind();
740	return NextVolume();
741}
742
743}	// namespace BTrackerPrivate
744