xref: /haiku/src/kits/tracker/NodeWalker.cpp (revision 73ad2473e7874b3702cf5b0fdf4c81b747812ed9)
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 // all the following calls are pure viruals, should not get called
52 status_t
53 TWalker::GetNextEntry(BEntry*, bool )
54 {
55 	TRESPASS();
56 	return B_ERROR;
57 }
58 
59 status_t
60 TWalker::GetNextRef(entry_ref*)
61 {
62 	TRESPASS();
63 	return B_ERROR;
64 }
65 
66 int32
67 TWalker::GetNextDirents(struct dirent*, size_t, int32)
68 {
69 	TRESPASS();
70 	return 0;
71 }
72 
73 
74 status_t
75 TWalker::Rewind()
76 {
77 	TRESPASS();
78 	return B_ERROR;
79 }
80 
81 int32
82 TWalker::CountEntries()
83 {
84 	TRESPASS();
85 	return -1;
86 }
87 
88 
89 TNodeWalker::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 
101 TNodeWalker::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 
127 TNodeWalker::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 
153 TNodeWalker::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 
169 TNodeWalker::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 
180 TNodeWalker::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 
205 TNodeWalker::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 
230 TNodeWalker::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 
245 TNodeWalker::~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 
257 status_t
258 TNodeWalker::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 
278 void
279 TNodeWalker::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 
288 status_t
289 TNodeWalker::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 
327 status_t
328 TNodeWalker::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 
367 static int32
368 build_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 
401 int32
402 TNodeWalker::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 
451 status_t
452 TNodeWalker::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 
476 int32
477 TNodeWalker::CountEntries()
478 {
479 	// should not be calling this
480 	TRESPASS();
481 	return -1;
482 }
483 
484 TVolWalker::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 
494 TVolWalker::~TVolWalker()
495 {
496 }
497 
498 status_t
499 TVolWalker::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 
529 status_t
530 TVolWalker::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 
549 status_t
550 TVolWalker::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 
569 int32
570 TVolWalker::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 
589 status_t
590 TVolWalker::Rewind()
591 {
592 	fVolRoster.Rewind();
593 	return NextVolume();
594 }
595 
596 TQueryWalker::TQueryWalker(const char* predicate)
597 	: TWalker(), fQuery(), fVolRoster(), fVol()
598 {
599 	fPredicate = strdup(predicate);
600 	NextVolume();
601 }
602 
603 TQueryWalker::~TQueryWalker()
604 {
605 	free((char*) fPredicate);
606 	fPredicate = NULL;
607 }
608 
609 status_t
610 TQueryWalker::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 
625 status_t
626 TQueryWalker::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 
643 int32
644 TQueryWalker::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 
660 status_t
661 TQueryWalker::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 
681 int32
682 TQueryWalker::CountEntries()
683 {
684 	// should not be calling this
685 	TRESPASS();
686 	return -1;
687 }
688 
689 status_t
690 TQueryWalker::Rewind()
691 {
692 	fVolRoster.Rewind();
693 	return NextVolume();
694 }
695 
696 }	// namespace BTrackerPrivate
697