xref: /haiku/src/kits/tracker/NodeWalker.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 
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 
45 namespace BTrackerPrivate {
46 
47 TWalker::~TWalker()
48 {
49 }
50 
51 
52 // all the following calls are pure virtuals, should not get called
53 status_t
54 TWalker::GetNextEntry(BEntry*, bool )
55 {
56 	TRESPASS();
57 	return B_ERROR;
58 }
59 
60 
61 status_t
62 TWalker::GetNextRef(entry_ref*)
63 {
64 	TRESPASS();
65 	return B_ERROR;
66 }
67 
68 
69 int32
70 TWalker::GetNextDirents(struct dirent*, size_t, int32)
71 {
72 	TRESPASS();
73 	return 0;
74 }
75 
76 
77 status_t
78 TWalker::Rewind()
79 {
80 	TRESPASS();
81 	return B_ERROR;
82 }
83 
84 
85 int32
86 TWalker::CountEntries()
87 {
88 	TRESPASS();
89 	return -1;
90 }
91 
92 
93 TNodeWalker::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 
106 TNodeWalker::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 
133 TNodeWalker::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 
160 TNodeWalker::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 
177 TNodeWalker::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 
190 TNodeWalker::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 
217 TNodeWalker::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 
243 TNodeWalker::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 
260 TNodeWalker::~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 
274 status_t
275 TNodeWalker::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 
297 void
298 TNodeWalker::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 
308 status_t
309 TNodeWalker::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 
349 status_t
350 TNodeWalker::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 
390 static int32
391 build_dirent(const BEntry* source, struct dirent* ent,
392 	size_t size, int32 count)
393 {
394 	entry_ref ref;
395 	source->GetRef(&ref);
396 
397 	size_t recordLength = strlen(ref.name) + sizeof(dirent);
398 	if (recordLength > size || count <= 0) {
399 		// can't fit in buffer, bail
400 		return 0;
401 	}
402 
403 	// info about this node
404 	ent->d_reclen = static_cast<ushort>(recordLength);
405 	strcpy(ent->d_name, ref.name);
406 	ent->d_dev = ref.device;
407 	ent->d_ino = ref.directory;
408 
409 	// info about the parent
410 	BEntry parent;
411 	source->GetParent(&parent);
412 	if (parent.InitCheck() == B_OK) {
413 		entry_ref parentRef;
414 		parent.GetRef(&parentRef);
415 		ent->d_pdev = parentRef.device;
416 		ent->d_pino = parentRef.directory;
417 	} else {
418 		ent->d_pdev = 0;
419 		ent->d_pino = 0;
420 	}
421 
422 	return 1;
423 }
424 
425 
426 int32
427 TNodeWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count)
428 {
429 	if (fJustFile != NULL) {
430 		if (count == 0)
431 			return 0;
432 
433 		// simulate GetNextDirents by building a single dirent structure
434 		int32 result = build_dirent(fJustFile, ent, size, count);
435 		fJustFile = 0;
436 		return result;
437 	}
438 
439 	if (fTopDir == NULL) {
440 		// done
441 		return 0;
442 	}
443 
444 	// If requested to include the top directory, return that first.
445 	if (fIncludeTopDir) {
446 		fIncludeTopDir = false;
447 		BEntry entry;
448 		if (fTopDir->GetEntry(&entry) < B_OK)
449 			return 0;
450 
451 		return build_dirent(fJustFile, ent, size, count);
452 	}
453 
454 	// get the next entry
455 	int32 nextDirent = fTopDir->GetNextDirents(ent, size, count);
456 	if (nextDirent == 0) {
457 		status_t result = PopDirCommon();
458 		if (result != B_OK)
459 			return 0;
460 
461 		return GetNextDirents(ent, size, count);
462 	}
463 
464 	// push any directories in the returned entries onto the stack
465 	for (int32 i = 0; i < nextDirent; i++) {
466 		if (fTopDir->Contains(ent->d_name, B_DIRECTORY_NODE)) {
467 			entry_ref ref(ent->d_dev, ent->d_ino, ent->d_name);
468 			PushDirCommon(&ref);
469 		}
470 		ent = (dirent*)((char*)ent + ent->d_reclen);
471 	}
472 
473 	return nextDirent;
474 }
475 
476 
477 status_t
478 TNodeWalker::Rewind()
479 {
480 	if (fOriginalJustFile != NULL) {
481 		// single file mode, rewind by pointing to the original file
482 		fJustFile = fOriginalJustFile;
483 		return B_OK;
484 	}
485 
486 	// pop all the directories and point to the initial one
487 	for (;;) {
488 		BDirectory* directory = fDirs.RemoveItemAt(fTopIndex--);
489 		if (directory == NULL)
490 			break;
491 
492 		delete directory;
493 	}
494 
495 	fTopDir = new BDirectory(fOriginalDirCopy);
496 	fTopIndex = 0;
497 	fIncludeTopDir = fOriginalIncludeTopDir;
498 	fDirs.AddItem(fTopDir);
499 
500 	return fTopDir->Rewind();
501 		// rewind the directory
502 }
503 
504 int32
505 TNodeWalker::CountEntries()
506 {
507 	// should not be calling this
508 	TRESPASS();
509 	return -1;
510 }
511 
512 
513 TVolWalker::TVolWalker(bool knowsAttributes, bool writable,
514 	bool includeTopDirectory)
515 	:
516 	TNodeWalker(includeTopDirectory),
517 	fKnowsAttr(knowsAttributes),
518 	fWritable(writable)
519 {
520 	// Get things initialized. Find first volume, or find the first volume
521 	// that supports attributes.
522  	NextVolume();
523 }
524 
525 
526 TVolWalker::~TVolWalker()
527 {
528 }
529 
530 
531 status_t
532 TVolWalker::NextVolume()
533 {
534 	// The stack of directoies should be empty.
535 	ASSERT(fTopIndex == -1);
536 	ASSERT(fTopDir == NULL);
537 
538 	status_t result;
539 	do {
540 		result = fVolRoster.GetNextVolume(&fVol);
541 		if (result != B_OK)
542 			break;
543 	} while ((fKnowsAttr && !fVol.KnowsAttr())
544 		|| (fWritable && fVol.IsReadOnly()));
545 
546 	if (result == B_OK) {
547 		// Get the root directory to get things started. There's always
548 		// a root directory for a volume. So if there is an error then it
549 		// means that something is really bad, like the system is out of
550 		// memory.  In that case don't worry about truying to skip to the
551 		// next volume.
552 		fTopDir = new BDirectory();
553 		result = fVol.GetRootDirectory(fTopDir);
554 		fIncludeTopDir = fOriginalIncludeTopDir;
555 		fTopIndex = 0;
556 		fDirs.AddItem(fTopDir);
557 	}
558 
559 	return result;
560 }
561 
562 status_t
563 TVolWalker::GetNextEntry(BEntry* entry, bool traverse)
564 {
565 	if (fTopDir == NULL)
566 		return B_ENTRY_NOT_FOUND;
567 
568 	// get the next entry
569 	status_t result = _inherited::GetNextEntry(entry, traverse);
570 	while (result != B_OK) {
571 		// we're done with the current volume, go to the next one
572 		result = NextVolume();
573 		if (result != B_OK)
574 			break;
575 
576 		result = GetNextEntry(entry, traverse);
577 	}
578 
579 	return result;
580 }
581 
582 
583 status_t
584 TVolWalker::GetNextRef(entry_ref* ref)
585 {
586 	if (fTopDir == NULL)
587 		return B_ENTRY_NOT_FOUND;
588 
589 	// Get the next ref.
590 	status_t result = _inherited::GetNextRef(ref);
591 
592 	while (result != B_OK) {
593 		// we're done with the current volume, go to the next one
594 		result = NextVolume();
595 		if (result != B_OK)
596 			break;
597 		result = GetNextRef(ref);
598 	}
599 
600 	return result;
601 }
602 
603 
604 int32
605 TVolWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count)
606 {
607 	if (fTopDir == NULL)
608 		return B_ENTRY_NOT_FOUND;
609 
610 	// get the next dirent
611 	status_t result = _inherited::GetNextDirents(ent, size, count);
612 	while (result != B_OK) {
613 		// we're done with the current volume, go to the next one
614 		result = NextVolume();
615 		if (result != B_OK)
616 			break;
617 
618 		result = GetNextDirents(ent, size, count);
619 	}
620 
621 	return result;
622 }
623 
624 
625 status_t
626 TVolWalker::Rewind()
627 {
628 	fVolRoster.Rewind();
629 	return NextVolume();
630 }
631 
632 
633 TQueryWalker::TQueryWalker(const char* predicate)
634 	:
635 	TWalker(),
636 	fTime(0)
637 {
638 	fPredicate = strdup(predicate);
639 	NextVolume();
640 }
641 
642 
643 TQueryWalker::~TQueryWalker()
644 {
645 	free((char*)fPredicate);
646 	fPredicate = NULL;
647 }
648 
649 
650 status_t
651 TQueryWalker::GetNextEntry(BEntry* entry, bool traverse)
652 {
653 	status_t result;
654 	do {
655 		result = fQuery.GetNextEntry(entry, traverse);
656 		if (result == B_ENTRY_NOT_FOUND) {
657 			if (NextVolume() != B_OK)
658 				break;
659 		}
660 	} while (result == B_ENTRY_NOT_FOUND);
661 
662 	return result;
663 }
664 
665 
666 status_t
667 TQueryWalker::GetNextRef(entry_ref* ref)
668 {
669 	status_t result;
670 
671 	for (;;) {
672 		result = fQuery.GetNextRef(ref);
673 		if (result != B_ENTRY_NOT_FOUND)
674 			break;
675 
676 		result = NextVolume();
677 		if (result != B_OK)
678 			break;
679 	}
680 
681 	return result;
682 }
683 
684 
685 int32
686 TQueryWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count)
687 {
688 	int32 result;
689 
690 	for (;;) {
691 		result = fQuery.GetNextDirents(ent, size, count);
692 		if (result != 0)
693 			return result;
694 
695 		if (NextVolume() != B_OK)
696 			return 0;
697 	}
698 
699 	return result;
700 }
701 
702 
703 status_t
704 TQueryWalker::NextVolume()
705 {
706 	status_t result;
707 	do {
708 		result = fVolRoster.GetNextVolume(&fVol);
709 		if (result != B_OK)
710 			break;
711 	} while (!fVol.KnowsQuery());
712 
713 	if (result == B_OK) {
714 		result = fQuery.Clear();
715 		result = fQuery.SetVolume(&fVol);
716 		result = fQuery.SetPredicate(fPredicate);
717 		result = fQuery.Fetch();
718 	}
719 
720 	return result;
721 }
722 
723 
724 int32
725 TQueryWalker::CountEntries()
726 {
727 	// should not be calling this
728 	TRESPASS();
729 	return -1;
730 }
731 
732 
733 status_t
734 TQueryWalker::Rewind()
735 {
736 	fVolRoster.Rewind();
737 	return NextVolume();
738 }
739 
740 }	// namespace BTrackerPrivate
741