xref: /haiku/src/kits/tracker/NodeWalker.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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 	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 
429 int32
430 TNodeWalker::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 
480 status_t
481 TNodeWalker::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 
507 int32
508 TNodeWalker::CountEntries()
509 {
510 	// should not be calling this
511 	TRESPASS();
512 	return -1;
513 }
514 
515 
516 TVolWalker::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 
529 TVolWalker::~TVolWalker()
530 {
531 }
532 
533 
534 status_t
535 TVolWalker::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 
565 status_t
566 TVolWalker::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 
586 status_t
587 TVolWalker::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 
607 int32
608 TVolWalker::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 
628 status_t
629 TVolWalker::Rewind()
630 {
631 	fVolRoster.Rewind();
632 	return NextVolume();
633 }
634 
635 
636 TQueryWalker::TQueryWalker(const char* predicate)
637 	:
638 	TWalker(),
639 	fTime(0)
640 {
641 	fPredicate = strdup(predicate);
642 	NextVolume();
643 }
644 
645 
646 TQueryWalker::~TQueryWalker()
647 {
648 	free((char*)fPredicate);
649 	fPredicate = NULL;
650 }
651 
652 
653 status_t
654 TQueryWalker::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 
669 status_t
670 TQueryWalker::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 
688 int32
689 TQueryWalker::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 
706 status_t
707 TQueryWalker::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 
727 int32
728 TQueryWalker::CountEntries()
729 {
730 	// should not be calling this
731 	TRESPASS();
732 	return -1;
733 }
734 
735 
736 status_t
737 TQueryWalker::Rewind()
738 {
739 	fVolRoster.Rewind();
740 	return NextVolume();
741 }
742 
743 }	// namespace BTrackerPrivate
744