1178171Simp/*
2178171SimpOpen Tracker License
3178171Simp
4178171SimpTerms and Conditions
5178171Simp
6178171SimpCopyright (c) 1991-2000, Be Incorporated. All rights reserved.
7178171Simp
8178171SimpPermission is hereby granted, free of charge, to any person obtaining a copy of
9178171Simpthis software and associated documentation files (the "Software"), to deal in
10178171Simpthe Software without restriction, including without limitation the rights to
11178171Simpuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12178171Simpof the Software, and to permit persons to whom the Software is furnished to do
13178171Simpso, subject to the following conditions:
14178171Simp
15178171SimpThe above copyright notice and this permission notice applies to all licensees
16178171Simpand shall be included in all copies or substantial portions of the Software.
17178171Simp
18178171SimpTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19178171SimpIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20178171SimpFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21178171SimpBE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22178171SimpAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23178171SimpWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24178171Simp
25178171SimpExcept as contained in this notice, the name of Be Incorporated shall not be
26178171Simpused in advertising or otherwise to promote the sale, use or other dealings in
27178171Simpthis Software without prior written authorization from Be Incorporated.
28178171Simp
29178171SimpTracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30178171Simpof Be Incorporated in the United States and other countries. Other brand product
31178171Simpnames are registered trademarks or trademarks of their respective holders.
32216671SnwhitehornAll rights reserved.
33178171Simp*/
34178171Simp
35266331Sian
36266331Sian#include <Debug.h>
37266331Sian#include <stdio.h>
38178171Simp#include <string.h>
39178171Simp#include <errno.h>
40178171Simp#include <SupportDefs.h>
41178171Simp
42266331Sian#include "NodeWalker.h"
43266331Sian
44178171Simp
45266331Siannamespace BTrackerPrivate {
46266331Sian
47266331SianTWalker::~TWalker()
48266331Sian{
49266331Sian}
50266331Sian
51178171Simp// all the following calls are pure viruals, should not get called
52178171Simpstatus_t
53215270SimpTWalker::GetNextEntry(BEntry*, bool )
54266331Sian{
55266331Sian	TRESPASS();
56178171Simp	return B_ERROR;
57215270Simp}
58178171Simp
59178171Simpstatus_t
60178171SimpTWalker::GetNextRef(entry_ref*)
61178171Simp{
62178171Simp	TRESPASS();
63215270Simp	return B_ERROR;
64215270Simp}
65178171Simp
66178171Simpint32
67178171SimpTWalker::GetNextDirents(struct dirent*, size_t, int32)
68178171Simp{
69220185Sadrian	TRESPASS();
70185522Ssam	return 0;
71266331Sian}
72178171Simp
73207077Sthompsa
74178171Simpstatus_t
75178171SimpTWalker::Rewind()
76178171Simp{
77178171Simp	TRESPASS();
78178171Simp	return B_ERROR;
79178171Simp}
80178171Simp
81178171Simpint32
82178171SimpTWalker::CountEntries()
83178171Simp{
84178171Simp	TRESPASS();
85178171Simp	return -1;
86178171Simp}
87
88
89TNodeWalker::TNodeWalker(bool includeTopDirectory)
90	:	fDirs(20),
91		fTopIndex(-1),
92		fTopDir(0),
93		fIncludeTopDir(includeTopDirectory),
94		fOriginalIncludeTopDir(includeTopDirectory),
95		fJustFile(0),
96		fOriginalJustFile(0)
97{
98}
99
100
101TNodeWalker::TNodeWalker(const char* path, bool includeTopDirectory)
102	:	fDirs(20),
103		fTopIndex(-1),
104		fTopDir(0),
105		fIncludeTopDir(includeTopDirectory),
106		fOriginalIncludeTopDir(includeTopDirectory),
107		fJustFile(0),
108		fOriginalDirCopy(path),
109		fOriginalJustFile(0)
110{
111	if (fOriginalDirCopy.InitCheck() != B_OK) {
112		// not a directory, set up walking a single file
113		fJustFile = new BEntry(path);
114		if (fJustFile->InitCheck() != B_OK) {
115			delete fJustFile;
116			fJustFile = NULL;
117		}
118		fOriginalJustFile = fJustFile;
119	} else {
120		fTopDir = new BDirectory(fOriginalDirCopy);
121		fTopIndex++;
122		fDirs.AddItem(fTopDir);
123	}
124}
125
126
127TNodeWalker::TNodeWalker(const entry_ref* ref, bool includeTopDirectory)
128	:	fDirs(20),
129		fTopIndex(-1),
130		fTopDir(0),
131		fIncludeTopDir(includeTopDirectory),
132		fOriginalIncludeTopDir(includeTopDirectory),
133		fJustFile(0),
134		fOriginalDirCopy(ref),
135		fOriginalJustFile(0)
136{
137	if (fOriginalDirCopy.InitCheck() != B_OK) {
138		// not a directory, set up walking a single file
139		fJustFile = new BEntry(ref);
140		if (fJustFile->InitCheck() != B_OK) {
141			delete fJustFile;
142			fJustFile = NULL;
143		}
144		fOriginalJustFile = fJustFile;
145	} else {
146		fTopDir = new BDirectory(fOriginalDirCopy);
147		fTopIndex++;
148		fDirs.AddItem(fTopDir);
149	}
150}
151
152
153TNodeWalker::TNodeWalker(const BDirectory* dir, bool includeTopDirectory)
154	:	fDirs(20),
155		fTopIndex(-1),
156		fTopDir(0),
157		fIncludeTopDir(includeTopDirectory),
158		fOriginalIncludeTopDir(includeTopDirectory),
159		fJustFile(0),
160		fOriginalDirCopy(*dir),
161		fOriginalJustFile(0)
162{
163	fTopDir = new BDirectory(*dir);
164	fTopIndex++;
165	fDirs.AddItem(fTopDir);
166}
167
168
169TNodeWalker::TNodeWalker()
170	:	fDirs(20),
171		fTopIndex(-1),
172		fTopDir(0),
173		fIncludeTopDir(false),
174		fOriginalIncludeTopDir(false),
175		fJustFile(0),
176		fOriginalJustFile(0)
177{
178}
179
180TNodeWalker::TNodeWalker(const char* path)
181	:	fDirs(20),
182		fTopIndex(-1),
183		fTopDir(0),
184		fIncludeTopDir(false),
185		fOriginalIncludeTopDir(false),
186		fJustFile(0),
187		fOriginalDirCopy(path),
188		fOriginalJustFile(0)
189{
190	if (fOriginalDirCopy.InitCheck() != B_OK) {
191		// not a directory, set up walking a single file
192		fJustFile = new BEntry(path);
193		if (fJustFile->InitCheck() != B_OK) {
194			delete fJustFile;
195			fJustFile = NULL;
196		}
197		fOriginalJustFile = fJustFile;
198	} else {
199		fTopDir = new BDirectory(fOriginalDirCopy);
200		fTopIndex++;
201		fDirs.AddItem(fTopDir);
202	}
203}
204
205TNodeWalker::TNodeWalker(const entry_ref* ref)
206	:	fDirs(20),
207		fTopIndex(-1),
208		fTopDir(0),
209		fIncludeTopDir(false),
210		fOriginalIncludeTopDir(false),
211		fJustFile(0),
212		fOriginalDirCopy(ref),
213		fOriginalJustFile(0)
214{
215	if (fOriginalDirCopy.InitCheck() != B_OK) {
216		// not a directory, set up walking a single file
217		fJustFile = new BEntry(ref);
218		if (fJustFile->InitCheck() != B_OK) {
219			delete fJustFile;
220			fJustFile = NULL;
221		}
222		fOriginalJustFile = fJustFile;
223	} else {
224		fTopDir = new BDirectory(fOriginalDirCopy);
225		fTopIndex++;
226		fDirs.AddItem(fTopDir);
227	}
228}
229
230TNodeWalker::TNodeWalker(const BDirectory* dir)
231	:	fDirs(20),
232		fTopIndex(-1),
233		fTopDir(0),
234		fIncludeTopDir(false),
235		fOriginalIncludeTopDir(false),
236		fJustFile(0),
237		fOriginalDirCopy(*dir),
238		fOriginalJustFile(0)
239{
240	fTopDir = new BDirectory(*dir);
241	fTopIndex++;
242	fDirs.AddItem(fTopDir);
243}
244
245TNodeWalker::~TNodeWalker()
246{
247	delete fOriginalJustFile;
248
249	for (;;) {
250		BDirectory* directory = fDirs.RemoveItemAt(fTopIndex--);
251		if (directory == NULL)
252			break;
253		delete directory;
254	}
255}
256
257status_t
258TNodeWalker::PopDirCommon()
259{
260	ASSERT(fTopIndex >= 0);
261
262	// done with the old dir, pop it
263	fDirs.RemoveItemAt(fTopIndex);
264	fTopIndex--;
265	delete fTopDir;
266	fTopDir = NULL;
267
268	if (fTopIndex == -1)
269		// done
270		return B_ENTRY_NOT_FOUND;
271
272	// point to the new top dir
273	fTopDir = fDirs.ItemAt(fTopIndex);
274
275	return B_OK;
276}
277
278void
279TNodeWalker::PushDirCommon(const entry_ref* ref)
280{
281	fTopDir = new BDirectory(ref);
282		// OK to ignore error here. Will
283		// catch at next call to GetNextEntry
284	fTopIndex++;
285	fDirs.AddItem(fTopDir);
286}
287
288status_t
289TNodeWalker::GetNextEntry(BEntry* entry, bool traverse)
290{
291	if (fJustFile) {
292		*entry = *fJustFile;
293		fJustFile = 0;
294		return B_OK;
295	}
296
297	if (!fTopDir)
298		// done
299		return B_ENTRY_NOT_FOUND;
300
301	// If requested to include the top directory, return that first.
302	if (fIncludeTopDir) {
303		fIncludeTopDir = false;
304		return fTopDir->GetEntry(entry);
305	}
306
307	// Get the next entry.
308	status_t err = fTopDir->GetNextEntry(entry, traverse);
309
310	if (err != B_OK) {
311		err = PopDirCommon();
312		if (err != B_OK)
313			return err;
314		return GetNextEntry(entry, traverse);
315	}
316	// See if this entry is a directory. If it is then push it onto the
317	// stack
318	entry_ref ref;
319	err = entry->GetRef(&ref);
320
321	if (err == B_OK && fTopDir->Contains(ref.name, B_DIRECTORY_NODE))
322		PushDirCommon(&ref);
323
324	return err;
325}
326
327status_t
328TNodeWalker::GetNextRef(entry_ref* ref)
329{
330	if (fJustFile) {
331		fJustFile->GetRef(ref);
332		fJustFile = 0;
333		return B_OK;
334	}
335
336	if (!fTopDir)
337		// done
338		return B_ENTRY_NOT_FOUND;
339
340	// If requested to include the top directory, return that first.
341	if (fIncludeTopDir) {
342		fIncludeTopDir = false;
343		BEntry entry;
344		status_t err = fTopDir->GetEntry(&entry);
345		if (err == B_OK)
346			err = entry.GetRef(ref);
347		return err;
348	}
349
350	// Get the next entry.
351	status_t err = fTopDir->GetNextRef(ref);
352	if (err != B_OK) {
353		err = PopDirCommon();
354		if (err != B_OK)
355			return err;
356		return GetNextRef(ref);
357	}
358	// See if this entry is a directory. If it is then push it onto the
359	// stack
360
361	if (fTopDir->Contains(ref->name, B_DIRECTORY_NODE))
362		PushDirCommon(ref);
363
364	return B_OK;
365}
366
367static int32
368build_dirent(const BEntry* source, struct dirent* ent,
369	size_t size, int32 count)
370{
371	entry_ref ref;
372	source->GetRef(&ref);
373
374	size_t recordLength = strlen(ref.name) + sizeof(dirent);
375	if (recordLength > size || count <= 0)
376		// can't fit in buffer, bail
377		return 0;
378
379	// info about this node
380	ent->d_reclen = static_cast<ushort>(recordLength);
381	strcpy(ent->d_name, ref.name);
382	ent->d_dev = ref.device;
383	ent->d_ino = ref.directory;
384
385	// info about the parent
386	BEntry parent;
387	source->GetParent(&parent);
388	if (parent.InitCheck() == B_OK) {
389		entry_ref parentRef;
390		parent.GetRef(&parentRef);
391		ent->d_pdev = parentRef.device;
392		ent->d_pino = parentRef.directory;
393	} else {
394		ent->d_pdev = 0;
395		ent->d_pino = 0;
396	}
397
398	return 1;
399}
400
401int32
402TNodeWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count)
403{
404	if (fJustFile) {
405		if (!count)
406			return 0;
407
408		// simulate GetNextDirents by building a single dirent structure
409		int32 result = build_dirent(fJustFile, ent, size, count);
410		fJustFile = 0;
411		return result;
412	}
413
414	if (!fTopDir)
415		// done
416		return 0;
417
418	// If requested to include the top directory, return that first.
419	if (fIncludeTopDir) {
420		fIncludeTopDir = false;
421		BEntry entry;
422		if (fTopDir->GetEntry(&entry) < B_OK)
423			return 0;
424
425		return build_dirent(fJustFile, ent, size, count);
426	}
427
428	// Get the next entry.
429	int32 result = fTopDir->GetNextDirents(ent, size, count);
430
431	if (!result) {
432		status_t err = PopDirCommon();
433		if (err != B_OK)
434			return 0;
435
436		return GetNextDirents(ent, size, count);
437	}
438
439	// push any directories in the returned entries onto the stack
440	for (int32 i = 0; i < result; i++) {
441		if (fTopDir->Contains(ent->d_name, B_DIRECTORY_NODE)) {
442			entry_ref ref(ent->d_dev, ent->d_ino, ent->d_name);
443			PushDirCommon(&ref);
444		}
445		ent = (dirent*)((char*)ent + ent->d_reclen);
446	}
447
448	return result;
449}
450
451status_t
452TNodeWalker::Rewind()
453{
454	if (fOriginalJustFile) {
455		// single file mode, rewind by pointing to the original file
456		fJustFile = fOriginalJustFile;
457		return B_OK;
458	}
459
460	// pop all the directories and point to the initial one
461	for (;;) {
462		BDirectory* directory = fDirs.RemoveItemAt(fTopIndex--);
463		if (!directory)
464			break;
465		delete directory;
466	}
467
468	fTopDir = new BDirectory(fOriginalDirCopy);
469	fTopIndex = 0;
470	fIncludeTopDir = fOriginalIncludeTopDir;
471	fDirs.AddItem(fTopDir);
472	// rewind the directory
473	return fTopDir->Rewind();
474}
475
476int32
477TNodeWalker::CountEntries()
478{
479	// should not be calling this
480	TRESPASS();
481	return -1;
482}
483
484TVolWalker::TVolWalker(bool knowsAttributes, bool writable, bool includeTopDirectory)
485	: 	TNodeWalker(includeTopDirectory),
486		fKnowsAttr(knowsAttributes),
487		fWritable(writable)
488{
489	// Get things initialized. Find first volume, or find the first volume
490	// that supports attributes.
491 	NextVolume();
492}
493
494TVolWalker::~TVolWalker()
495{
496}
497
498status_t
499TVolWalker::NextVolume()
500{
501	status_t err;
502
503	// The stack of directoies should be empty.
504	ASSERT(fTopIndex == -1);
505	ASSERT(fTopDir == NULL);
506
507	do {
508		err = fVolRoster.GetNextVolume(&fVol);
509		if (err != B_OK)
510			break;
511	} while ((fKnowsAttr && !fVol.KnowsAttr()) || (fWritable && fVol.IsReadOnly()));
512
513	if (err == B_OK) {
514		// Get the root directory to get things started. There's always
515		// a root directory for a volume. So if there is an error then it
516		// means that something is really bad, like the system is out of
517		// memory.  In that case don't worry about truying to skip to the
518		// next volume.
519		fTopDir = new BDirectory();
520		err = fVol.GetRootDirectory(fTopDir);
521		fIncludeTopDir = fOriginalIncludeTopDir;
522		fTopIndex = 0;
523		fDirs.AddItem(fTopDir);
524	}
525
526	return err;
527}
528
529status_t
530TVolWalker::GetNextEntry(BEntry* entry, bool traverse)
531{
532	if (!fTopDir)
533		return B_ENTRY_NOT_FOUND;
534
535	// Get the next entry.
536	status_t err = _inherited::GetNextEntry(entry, traverse);
537
538	while (err != B_OK) {
539		// We're done with the current volume. Go to the next one
540		err = NextVolume();
541		if (err != B_OK)
542			break;
543		err = GetNextEntry(entry, traverse);
544	}
545
546	return err;
547}
548
549status_t
550TVolWalker::GetNextRef(entry_ref* ref)
551{
552	if (!fTopDir)
553		return B_ENTRY_NOT_FOUND;
554
555	// Get the next ref.
556	status_t err = _inherited::GetNextRef(ref);
557
558	while (err != B_OK) {
559		// We're done with the current volume. Go to the next one
560		err = NextVolume();
561		if (err != B_OK)
562			break;
563		err = GetNextRef(ref);
564	}
565
566	return err;
567}
568
569int32
570TVolWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count)
571{
572	if (!fTopDir)
573		return B_ENTRY_NOT_FOUND;
574
575	// Get the next dirent.
576	status_t err = _inherited::GetNextDirents(ent, size, count);
577
578	while (err != B_OK) {
579		// We're done with the current volume. Go to the next one
580		err = NextVolume();
581		if (err != B_OK)
582			break;
583		err = GetNextDirents(ent, size, count);
584	}
585
586	return err;
587}
588
589status_t
590TVolWalker::Rewind()
591{
592	fVolRoster.Rewind();
593	return NextVolume();
594}
595
596TQueryWalker::TQueryWalker(const char* predicate)
597	: TWalker(), fQuery(), fVolRoster(), fVol()
598{
599	fPredicate = strdup(predicate);
600	NextVolume();
601}
602
603TQueryWalker::~TQueryWalker()
604{
605	free((char*) fPredicate);
606	fPredicate = NULL;
607}
608
609status_t
610TQueryWalker::GetNextEntry(BEntry* entry, bool traverse)
611{
612	status_t err;
613
614	do {
615		err = fQuery.GetNextEntry(entry, traverse);
616		if (err == B_ENTRY_NOT_FOUND) {
617			if (NextVolume() != B_OK)
618				break;
619		}
620	} while (err == B_ENTRY_NOT_FOUND);
621
622	return err;
623}
624
625status_t
626TQueryWalker::GetNextRef(entry_ref* ref)
627{
628	status_t err;
629
630	for (;;) {
631		err = fQuery.GetNextRef(ref);
632		if (err != B_ENTRY_NOT_FOUND)
633			break;
634
635		err = NextVolume();
636		if (err != B_OK)
637			break;
638	}
639
640	return err;
641}
642
643int32
644TQueryWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count)
645{
646	int32 result;
647
648	for (;;) {
649		result = fQuery.GetNextDirents(ent, size, count);
650		if (result != 0)
651			return result;
652
653		if (NextVolume() != B_OK)
654			return 0;
655	}
656
657	return result;
658}
659
660status_t
661TQueryWalker::NextVolume()
662{
663	status_t err;
664	do {
665		err = fVolRoster.GetNextVolume(&fVol);
666		if (err)
667			break;
668	} while (!fVol.KnowsQuery());
669
670
671	if (err == B_OK) {
672		err = fQuery.Clear();
673		err = fQuery.SetVolume(&fVol);
674		err = fQuery.SetPredicate(fPredicate);
675		err = fQuery.Fetch();
676	}
677
678	return err;
679}
680
681int32
682TQueryWalker::CountEntries()
683{
684	// should not be calling this
685	TRESPASS();
686	return -1;
687}
688
689status_t
690TQueryWalker::Rewind()
691{
692	fVolRoster.Rewind();
693	return NextVolume();
694}
695
696}	// namespace BTrackerPrivate
697