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