xref: /haiku/src/kits/tracker/NodeWalker.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
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 viruals, 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(), fQuery(), fVolRoster(), fVol()
636 {
637 	fPredicate = strdup(predicate);
638 	NextVolume();
639 }
640 
641 
642 TQueryWalker::~TQueryWalker()
643 {
644 	free((char*)fPredicate);
645 	fPredicate = NULL;
646 }
647 
648 
649 status_t
650 TQueryWalker::GetNextEntry(BEntry* entry, bool traverse)
651 {
652 	status_t result;
653 	do {
654 		result = fQuery.GetNextEntry(entry, traverse);
655 		if (result == B_ENTRY_NOT_FOUND) {
656 			if (NextVolume() != B_OK)
657 				break;
658 		}
659 	} while (result == B_ENTRY_NOT_FOUND);
660 
661 	return result;
662 }
663 
664 
665 status_t
666 TQueryWalker::GetNextRef(entry_ref* ref)
667 {
668 	status_t result;
669 
670 	for (;;) {
671 		result = fQuery.GetNextRef(ref);
672 		if (result != B_ENTRY_NOT_FOUND)
673 			break;
674 
675 		result = NextVolume();
676 		if (result != B_OK)
677 			break;
678 	}
679 
680 	return result;
681 }
682 
683 
684 int32
685 TQueryWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count)
686 {
687 	int32 result;
688 
689 	for (;;) {
690 		result = fQuery.GetNextDirents(ent, size, count);
691 		if (result != 0)
692 			return result;
693 
694 		if (NextVolume() != B_OK)
695 			return 0;
696 	}
697 
698 	return result;
699 }
700 
701 
702 status_t
703 TQueryWalker::NextVolume()
704 {
705 	status_t result;
706 	do {
707 		result = fVolRoster.GetNextVolume(&fVol);
708 		if (result != B_OK)
709 			break;
710 	} while (!fVol.KnowsQuery());
711 
712 	if (result == B_OK) {
713 		result = fQuery.Clear();
714 		result = fQuery.SetVolume(&fVol);
715 		result = fQuery.SetPredicate(fPredicate);
716 		result = fQuery.Fetch();
717 	}
718 
719 	return result;
720 }
721 
722 
723 int32
724 TQueryWalker::CountEntries()
725 {
726 	// should not be calling this
727 	TRESPASS();
728 	return -1;
729 }
730 
731 
732 status_t
733 TQueryWalker::Rewind()
734 {
735 	fVolRoster.Rewind();
736 	return NextVolume();
737 }
738 
739 }	// namespace BTrackerPrivate
740