1 /*****************************************************************************\
3 * Name : directory_tree *
4 * Author : Chris Koeritz *
6 *******************************************************************************
7 * Copyright (c) 2004-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
15 #include "directory.h"
16 #include "directory_tree.h"
18 #include "filename_list.h"
19 #include "filename_tree.h"
21 #include <basis/functions.h>
22 #include <basis/contracts.h>
23 #include <loggers/program_wide_logger.h>
24 #include <structures/object_packers.h>
25 #include <structures/string_array.h>
26 #include <textual/parser_bits.h>
27 #include <textual/string_manipulation.h>
31 using namespace basis;
32 using namespace loggers;
33 using namespace nodes;
34 using namespace structures;
35 using namespace textual;
37 //#define DEBUG_DIRECTORY_TREE
38 // uncomment for noisier version.
41 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
45 namespace filesystem {
47 class dir_tree_iterator : public filename_tree::iterator
50 filename_tree *_current;
52 dir_tree_iterator(const filename_tree *initial,
53 tree::traversal_directions dir)
54 : filename_tree::iterator(initial, dir), _current(NULL_POINTER) {}
59 directory_tree::directory_tree()
60 : _scanned_okay(false),
62 _pattern(new astring),
63 _real_tree(new filename_tree),
65 _creator(new fname_tree_creator)
69 directory_tree::directory_tree(const astring &path, const char *pattern,
71 : _scanned_okay(false),
72 _path(new astring(path)),
73 _pattern(new astring(pattern)),
74 _real_tree(NULL_POINTER),
75 _ignore_files(ignore_files),
76 _creator(new fname_tree_creator)
81 directory_tree::~directory_tree()
83 _scanned_okay = false;
90 const astring &directory_tree::path() const { return *_path; }
92 int directory_tree::packed_size() const
94 return 2 * PACKED_SIZE_INT32
95 + _path->packed_size()
96 + _pattern->packed_size()
97 + _real_tree->recursive_packed_size();
100 void directory_tree::pack(byte_array &packed_form) const
102 attach(packed_form, int(_scanned_okay));
103 attach(packed_form, int(_ignore_files));
104 _path->pack(packed_form);
105 _pattern->pack(packed_form);
106 _real_tree->recursive_pack(packed_form);
109 bool directory_tree::unpack(byte_array &packed_form)
112 if (!detach(packed_form, temp)) return false;
113 _scanned_okay = temp;
114 if (!detach(packed_form, temp)) return false;
115 _ignore_files = temp;
116 if (!_path->unpack(packed_form)) return false;
117 if (!_pattern->unpack(packed_form)) return false;
119 _real_tree = (filename_tree *)packable_tree::recursive_unpack
120 (packed_form, *_creator);
122 _real_tree = new filename_tree; // reset it.
128 void directory_tree::text_form(astring &target, bool show_files)
130 dir_tree_iterator *ted = start(directory_tree::prefix);
131 // create our iterator to do a prefix traversal.
133 int depth; // current depth in tree.
134 filename curr; // the current path the iterator is at.
135 string_array files; // the filenames held at the iterator.
137 while (current(*ted, curr, files)) {
138 // we have a good directory to show.
139 directory_tree::depth(*ted, depth);
140 target += string_manipulation::indentation(depth * 2) + astring("[")
141 + curr.raw() + "]" + parser_bits::platform_eol_to_chars();
144 for (int i = 0; i < files.length(); i++) names += files[i] + " ";
145 if (names.length()) {
147 string_manipulation::split_lines(names, split, depth * 2 + 2);
148 target += split + parser_bits::platform_eol_to_chars();
152 // go to the next place.
159 void directory_tree::traverse(const astring &path, const char *pattern,
160 filename_tree &add_to)
163 // prepare the current node.
164 add_to._dirname = filename(path, astring::empty_string());
165 add_to._files.reset();
166 #ifdef DEBUG_DIRECTORY_TREE
167 LOG(astring("working on node ") + add_to._dirname);
170 // open the directory.
171 directory curr(path, "*");
172 if (!curr.good()) return;
174 if (!_ignore_files) {
175 // add all the files to the current node.
176 directory curr_stringent(path, pattern);
177 add_to._files = curr_stringent.files();
180 // now iterate across the directories here and add a sub-node for each one,
181 // and recursively traverse that sub-node also.
182 const string_array &dirs = curr.directories();
183 for (int i = 0; i < dirs.length(); i++) {
184 filename_tree *new_branch = NULL_POINTER;
185 astring new_path = path + filename::default_separator() + dirs[i];
186 #ifdef DEBUG_DIRECTORY_TREE
187 LOG(astring("seeking path: ") + new_path);
189 if (!filename(new_path).is_normal()) {
190 //#ifdef DEBUG_DIRECTORY_TREE
191 LOG(astring("bailing on weird dir: ") + new_path);
193 continue; // only regular directories please.
195 for (int q = 0; q < add_to.branches(); q++) {
196 filename_tree *curr_kid = (filename_tree *)add_to.branch(q);
197 #ifdef DEBUG_DIRECTORY_TREE
198 LOG(astring("curr kid: ") + curr_kid->_dirname);
200 if (filename(new_path).raw().iequals(filename
201 (curr_kid->_dirname).raw())) {
202 new_branch = curr_kid;
203 #ifdef DEBUG_DIRECTORY_TREE
204 LOG(astring("using existing branch for ") + new_path);
210 #ifdef DEBUG_DIRECTORY_TREE
211 LOG(astring("adding new branch for ") + new_path);
213 new_branch = new filename_tree;
214 add_to.attach(new_branch);
215 new_branch->_depth = add_to._depth + 1;
217 #ifdef DEBUG_DIRECTORY_TREE
218 LOG(astring("traversing sub-node ") + new_path);
220 traverse(new_path, pattern, *new_branch);
224 bool directory_tree::reset(const astring &path, const char *pattern)
227 _scanned_okay = false;
229 // filename temp_path(path);
230 // temp_path.canonicalize();
234 _real_tree = new filename_tree;
236 #ifdef DEBUG_DIRECTORY_TREE
237 LOG(astring("dirtree::reset to path: ") + path);
240 // check that the top-level is healthy.
241 directory curr(path, "*");
242 if (!curr.good()) return false;
243 // our only exit condition; other directories might not be accessible
244 // underneath, but the top one must be accessible for us to even start
247 traverse(path, pattern, *_real_tree);
248 _scanned_okay = true;;
252 dir_tree_iterator *directory_tree::start_at(filename_tree *start,
253 traversal_types type) const
255 // translate to the lower level traversal enum.
256 tree::traversal_directions dir = tree::prefix;
257 if (type == infix) dir = tree::infix;
258 else if (type == postfix) dir = tree::postfix;
260 return new dir_tree_iterator(start, dir);
263 dir_tree_iterator *directory_tree::start(traversal_types type) const
265 // translate to the lower level traversal enum.
266 tree::traversal_directions dir = tree::prefix;
267 if (type == infix) dir = tree::infix;
268 else if (type == postfix) dir = tree::postfix;
270 return new dir_tree_iterator(_real_tree, dir);
273 bool directory_tree::jump_to(dir_tree_iterator &scanning,
274 const astring &sub_path)
279 filename(sub_path).separate(rooted, pieces);
280 for (int i = 0; i < pieces.length(); i++) {
281 filename_tree *curr = dynamic_cast<filename_tree *>(scanning.current());
282 #ifdef DEBUG_DIRECTORY_TREE
283 LOG(astring("at ") + curr->_dirname.raw());
285 string_array sub_pieces = pieces.subarray(i, i);
287 curr_path.join(rooted, sub_pieces);
288 curr_path = filename(curr->_dirname.raw() + filename::default_separator()
290 #ifdef DEBUG_DIRECTORY_TREE
291 LOG(astring("made curr path ") + curr_path.raw());
293 if (!curr) return false;
294 bool found_it = false;
295 for (int j = 0; j < curr->branches(); j++) {
296 filename_tree *sub = dynamic_cast<filename_tree *>(curr->branch(j));
297 #ifdef DEBUG_DIRECTORY_TREE
298 LOG(astring("looking at ") + sub->_dirname.raw());
300 if (sub->_dirname.compare_prefix(curr_path)) {
301 // this part matches!
303 #ifdef DEBUG_DIRECTORY_TREE
304 LOG(astring("found at ") + sub->_dirname.raw());
311 #ifdef DEBUG_DIRECTORY_TREE
312 LOG(astring("could not find ") + curr_path.raw());
320 filename_tree *directory_tree::goto_current(dir_tree_iterator &scanning)
322 if (!scanning._current) {
323 // this one hasn't been advanced yet, or it's already over with.
324 scanning._current = (filename_tree *)scanning.next();
326 // now check that we're healthy.
327 if (!scanning._current) return NULL_POINTER; // all done.
329 // cast the tree to the right type.
330 return dynamic_cast<filename_tree *>(scanning._current);
333 bool directory_tree::current_dir(dir_tree_iterator &scanning,
336 dir_name = astring::empty_string();
337 filename_tree *tof = goto_current(scanning);
338 if (!tof) return false;
339 dir_name = tof->_dirname;
343 bool directory_tree::current(dir_tree_iterator &scanning,
344 filename &dir_name, string_array &to_fill)
346 // clear any existing junk.
347 dir_name = astring::empty_string();
350 filename_tree *tof = goto_current(scanning);
351 if (!tof) return false;
353 // fill in what they wanted.
354 dir_name = tof->_dirname;
355 tof->_files.fill(to_fill);
360 bool directory_tree::current(dir_tree_iterator &scanning,
361 filename &dir_name, filename_list &to_fill)
363 // clear any existing junk.
364 dir_name = astring::empty_string();
367 filename_tree *tof = goto_current(scanning);
368 if (!tof) return false;
370 // fill in what they wanted.
371 dir_name = tof->_dirname;
372 to_fill = tof->_files;
377 filename_list *directory_tree::access(dir_tree_iterator &scanning)
379 filename_tree *tof = goto_current(scanning);
380 if (!tof) return NULL_POINTER;
384 bool directory_tree::depth(dir_tree_iterator &scanning, int &depth)
386 depth = -1; // invalid as default.
387 filename_tree *tof = goto_current(scanning);
388 if (!tof) return false;
393 bool directory_tree::children(dir_tree_iterator &scanning, int &kids)
395 kids = -1; // invalid as default.
396 filename_tree *tof = goto_current(scanning);
397 if (!tof) return false;
398 kids = tof->branches();
402 bool directory_tree::next(dir_tree_iterator &scanning)
404 scanning._current = (filename_tree *)scanning.next();
405 return !!scanning._current;
408 void directory_tree::throw_out(dir_tree_iterator * &to_whack)
413 filename_tree *directory_tree::seek(const astring &dir_name_in,
414 bool ignore_initial) const
417 array<filename_tree *> examining;
418 // the list of nodes we're currently looking at.
420 #ifdef DEBUG_DIRECTORY_TREE
421 LOG(astring("seeking on root of: ") + *_path);
424 astring dir_name = filename(dir_name_in).raw();
425 // set the search path up to have the proper prefix.
427 dir_name = path() + filename::default_separator()
428 + filename(dir_name_in).raw();
430 #ifdef DEBUG_DIRECTORY_TREE
431 LOG(astring("adding root: ") + _real_tree->_dirname);
433 examining += _real_tree;
435 astring sequel; // holds extra pieces from filename comparisons.
437 // chew on the list of nodes to examine until we run out.
438 while (examining.length()) {
441 // start looking at all the items in the list, even though we might have
442 // to abandon the iteration if we find a match.
443 filename_tree *check = NULL_POINTER;
444 for (posn = 0; posn < examining.length(); posn++) {
445 check = examining[posn];
446 filename current(check->_dirname);
447 if (!current.is_normal()) {
448 //#ifdef DEBUG_DIRECTORY_TREE
449 LOG(astring("seek: skipping abnormal dir: \"") + current + "\"");
453 #ifdef DEBUG_DIRECTORY_TREE
454 LOG(astring("looking at ") + current.raw());
456 if (current.compare_prefix(dir_name, sequel)) {
458 #ifdef DEBUG_DIRECTORY_TREE
459 LOG(astring("matched! at ") + current.raw());
463 // whoa! an exact match. we're done now.
464 #ifdef DEBUG_DIRECTORY_TREE
465 LOG(astring("exact match at ") + current.raw() + "! done!!!");
469 #ifdef DEBUG_DIRECTORY_TREE
470 LOG(astring("inexact match because sequel=") + sequel);
476 if (!found) return NULL_POINTER; // we found nothing comparable.
478 // we found a partial match. that means we should start looking at this
479 // node's children for the exact match.
481 // this is a serious logical error!
482 LOG("serious logical error: tree was not located.");
485 examining.reset(); // clear the existing nodes.
486 for (int i = 0; i < check->branches(); i++)
487 examining += (filename_tree *)check->branch(i);
490 return NULL_POINTER; // we found nothing that looked like that node.
493 bool directory_tree::calculate(bool just_size)
494 { return calculate(_real_tree, just_size); }
496 bool directory_tree::calculate(filename_tree *start, bool just_size)
498 FUNCDEF("calculate");
499 #ifdef DEBUG_DIRECTORY_TREE
500 LOG(astring("calculate: got tree to start with at ") + start->_dirname.raw());
502 dir_tree_iterator *ted = start_at(start, directory_tree::postfix);
503 // create our iterator to do a postfix traversal. why postfix? well,
504 // prefix has been used elsewhere and since it doesn't really matter what
505 // order we visit the nodes here, it's good to change up.
507 int depth; // current depth in tree.
508 filename curr; // the current path the iterator is at.
509 filename_list *files; // the filenames held at the iterator.
511 while (directory_tree::current_dir(*ted, curr)) {
512 if (!curr.is_normal()) {
513 //#ifdef DEBUG_DIRECTORY_TREE
514 LOG(astring("calculate: skipping abnormal dir: \"") + curr + "\"");
516 directory_tree::next(*ted);
517 continue; // scary non-simple file type.
519 // we have a good directory to show.
520 #ifdef DEBUG_DIRECTORY_TREE
521 LOG(astring("calcing node ") + curr.raw());
523 files = directory_tree::access(*ted);
524 directory_tree::depth(*ted, depth);
525 for (int i = 0; i < files->elements(); i++) {
526 filename curr_file = curr + "/" + *files->borrow(i);
527 #ifdef DEBUG_DIRECTORY_TREE
528 LOG(astring("got a curr file: ") + curr_file);
530 if (!curr_file.is_normal()) {
531 //#ifdef DEBUG_DIRECTORY_TREE
532 LOG(astring("skipping abnormal file: \"") + curr + "\"");
536 if (!files->borrow(i)->calculate(curr.raw(), just_size)) {
537 LOG(astring("failure to calculate ") + files->get(i)->text_form());
541 directory_tree::next(*ted);
544 directory_tree::throw_out(ted);
548 bool directory_tree::compare_trees(const directory_tree &source,
549 const directory_tree &target, filename_list &differences,
550 file_info::file_similarity how_to_compare)
552 return compare_trees(source, astring::empty_string(), target,
553 astring::empty_string(), differences, how_to_compare);
556 bool directory_tree::compare_trees(const directory_tree &source,
557 const astring &source_start_in, const directory_tree &target,
558 const astring &target_start_in, filename_list &differences,
559 file_info::file_similarity how_compare)
561 FUNCDEF("compare_trees");
562 differences.reset(); // prepare it for storage.
564 // make sure we get canonical names to work with.
565 filename source_start(source_start_in);
566 filename target_start(target_start_in);
568 dir_tree_iterator *ted = source.start(directory_tree::prefix);
569 // create our iterator to do a prefix traversal.
571 astring real_source_start = source.path();
572 if (source_start.raw().t()) {
573 // move to the right place.
574 real_source_start = real_source_start + filename::default_separator()
575 + source_start.raw();
576 if (!directory_tree::jump_to(*ted, source_start.raw())) {
577 // can't even start comparing.
578 LOG(astring("failed to find source start in tree, given as ")
579 + source_start.raw());
584 filename curr; // the current path the iterator is at.
585 filename_list files; // the filenames held at the iterator.
587 // calculate where our comparison point is on the source.
588 int source_pieces = 0;
592 filename(real_source_start).separate(rooted_source, temp);
593 source_pieces = temp.length();
596 bool seen_zero_pieces = false;
597 while (directory_tree::current(*ted, curr, files)) {
598 // we're in a place in the source tree now. let's compare it with the
599 // target's recollection.
601 #ifdef DEBUG_DIRECTORY_TREE
602 LOG(astring("curr dir in tree: ") + curr.raw());
607 curr.separate(curr_rooted, pieces); // get the components of the current location.
608 #ifdef DEBUG_DIRECTORY_TREE
609 LOG(astring("name in pieces:") + pieces.text_form());
611 pieces.zap(0, source_pieces - 1);
612 // snap the root components out of there.
614 filename corresponding_name;
615 //hmmm: is that right decision?
616 corresponding_name.join(false, pieces);
617 #ifdef DEBUG_DIRECTORY_TREE
618 LOG(astring("computed target name as: ") + corresponding_name);
620 filename original_correspondence(corresponding_name);
622 if (!corresponding_name.raw().t()) {
623 if (seen_zero_pieces) {
624 #ifdef DEBUG_DIRECTORY_TREE
625 LOG(astring("breaking out now due to empty correspondence"));
629 seen_zero_pieces = true;
631 if (target_start.raw().t()) {
632 corresponding_name = filename(target_start.raw()
633 + filename::default_separator() + corresponding_name.raw());
634 /*doesn't help, not right yet. } else {
635 // if they didn't give us a place to start, we start at the top.
636 corresponding_name = filename(target.path()
637 + filename::default_separator() + corresponding_name.raw());
640 #ifdef DEBUG_DIRECTORY_TREE
641 LOG(astring("target with start is: ") + corresponding_name);
644 filename_tree *target_now = target.seek(corresponding_name.raw(), true);
646 // that entire sub-tree is missing. add all of the files here into
648 //#ifdef DEBUG_DIRECTORY_TREE
649 LOG(astring("could not find dir in target for ") + curr.raw()
650 + " which we computed corresp as " + corresponding_name.raw());
654 // now scan across all the files that are in our source list.
655 for (int i = 0; i < files.elements(); i++) {
656 if (!target_now // there was no node, so we're adding everything...
657 || !target_now->_files.member_with_state(*files[i], how_compare) ) {
658 // ... or we need to add this file since it's missing.
659 #ifdef DEBUG_DIRECTORY_TREE
660 LOG(astring("adding record: ") + files[i]->text_form());
663 file_info *new_record = new file_info(*files[i]);
664 // record the file time for use later in saving.
665 new_record->calculate(curr, true);
666 astring original = new_record->raw();
667 #ifdef DEBUG_DIRECTORY_TREE
668 LOG(astring("current: ") + new_record->raw());
671 astring actual_name = source_start.raw();
672 #ifdef DEBUG_DIRECTORY_TREE
673 if (actual_name.t()) LOG(astring("sname=") + actual_name);
675 if (actual_name.length()) actual_name += filename::default_separator();
676 actual_name += original_correspondence.raw();
677 if (actual_name.length()) actual_name += filename::default_separator();
678 actual_name += new_record->raw();
679 #ifdef DEBUG_DIRECTORY_TREE
680 if (actual_name.t()) LOG(astring("sname=") + actual_name);
682 (filename &)(*new_record) = filename(actual_name);
684 astring targ_name = corresponding_name.raw();
685 #ifdef DEBUG_DIRECTORY_TREE
686 if (targ_name.t()) LOG(astring("tname=") + targ_name);
688 if (targ_name.length()) targ_name += filename::default_separator();
689 targ_name += original;
690 #ifdef DEBUG_DIRECTORY_TREE
691 if (targ_name.t()) LOG(astring("tname=") + targ_name);
694 new_record->secondary(targ_name);
696 differences += new_record;
697 #ifdef DEBUG_DIRECTORY_TREE
698 LOG(astring("came out as: ") + new_record->text_form());
703 // go to the next place.
704 directory_tree::next(*ted);
707 directory_tree::throw_out(ted);
712 outcome directory_tree::find_common_root(const astring &finding, bool exists,
713 filename_tree * &found, astring &reassembled, string_array &pieces,
716 FUNCDEF("find_common_root");
717 // test the path to find what it is.
718 filename adding(finding);
719 if (exists && !adding.good())
720 return common::BAD_INPUT; // not a good path.
721 int file_subtract = 0; // if it's a file, then we remove last component.
722 if (exists && !adding.is_directory()) file_subtract = 1;
724 // break up the path into pieces.
727 adding.separate(rooted, pieces);
729 // break up our root into pieces; we must take off components that are
730 // already in the root.
731 string_array root_pieces;
733 filename temp_file(path());
734 temp_file.separate(root_rooted, root_pieces);
736 // locate the last place where the path we were given touches our tree.
737 // it could be totally new, partially new, or already contained.
738 filename_tree *last_match = _real_tree; // where the common root is located.
739 int list_length = pieces.length() - file_subtract;
742 // we must put all the pieces in that already come from the root.
743 for (int i = 0; i < root_pieces.length() - 1; i++) {
744 bool add_slash = false;
745 if (reassembled.length() && (reassembled[reassembled.end()] != '/') )
747 if (add_slash) reassembled += "/";
748 reassembled += pieces[i];
749 if (reassembled[reassembled.end()] == ':') {
750 #ifdef DEBUG_DIRECTORY_TREE
751 LOG(astring("skipping drive component ") + reassembled);
757 #ifdef DEBUG_DIRECTORY_TREE
758 LOG(astring("after pre-assembly, path is ") + reassembled);
761 outcome to_return = common::NOT_FOUND;
763 for (match_place = root_pieces.length() - 1; match_place < list_length;
765 // add a slash if there's not one present already.
766 bool add_slash = false;
767 if (reassembled.length() && (reassembled[reassembled.end()] != '/') )
769 // add the next component in to our path.
770 if (add_slash) reassembled += "/";
771 reassembled += pieces[match_place];
772 // special case for dos paths.
773 if (reassembled[reassembled.end()] == ':') {
774 #ifdef DEBUG_DIRECTORY_TREE
775 LOG(astring("skipping drive component ") + reassembled);
779 reassembled = filename(reassembled).raw(); // force compliance with OS.
780 #ifdef DEBUG_DIRECTORY_TREE
781 LOG(astring("now seeking ") + reassembled);
783 filename_tree *sought = seek(reassembled, false);
785 #ifdef DEBUG_DIRECTORY_TREE
786 LOG(astring("couldn't find ") + reassembled);
788 if (!exists && (match_place == list_length - 1)) {
789 // see if we can get a match on a file rather than a directory, but
790 // only if we're near the end of the compare.
791 if (last_match->_files.member(pieces[match_place])) {
792 // aha! a file match.
793 to_return = common::OKAY;
801 // record where we last had some success.
802 #ifdef DEBUG_DIRECTORY_TREE
803 LOG(astring("found subtree for ") + reassembled);
808 // this is a success, but our loop structure can put us one past the right
810 if (match_place >= list_length) {
811 match_place = list_length - 1;
812 to_return = common::OKAY;
819 outcome directory_tree::add_path(const astring &new_item, bool just_size)
822 // test the path to find out what it is.
823 filename adding(new_item);
824 if (!adding.good()) {
825 LOG(astring("non-existent new item! ") + new_item);
826 return common::NOT_FOUND; // not an existing path.
828 if (!adding.is_normal()) {
829 //#ifdef DEBUG_DIRECTORY_TREE
830 LOG(astring("abnormal new item: ") + new_item);
832 return common::BAD_INPUT; // not a good path.
834 int file_subtract = 0; // if it's a file, then we remove last component.
835 if (!adding.is_directory()) file_subtract = 1;
836 #ifdef DEBUG_DIRECTORY_TREE
837 if (file_subtract) { LOG(astring("adding a file ") + new_item); }
838 else { LOG(astring("adding a directory ") + new_item); }
841 // find the common root, break up the path into pieces, and tell us where
844 filename_tree *last_match = NULL_POINTER;
846 astring reassembled; // this will hold the common root.
847 outcome ret = find_common_root(new_item, true, last_match, reassembled,
850 LOG(astring("serious error finding common root for ") + new_item
851 + ", got null tree.");
852 return common::FAILURE; // something serious isn't right.
855 if (!file_subtract) {
856 if (ret != common::OKAY) {
857 // if it's a new directory, we add a new node for traverse to work on.
858 #ifdef DEBUG_DIRECTORY_TREE
859 LOG(astring("now adding node for ") + reassembled);
861 filename_tree *new_branch = new filename_tree;
862 new_branch->_depth = last_match->_depth + 1;
863 last_match->attach(new_branch);
864 last_match = new_branch;
866 #ifdef DEBUG_DIRECTORY_TREE
867 LOG(astring("matched properly. reassembled set to ") + reassembled);
873 if (ret != common::OKAY) {
874 #ifdef DEBUG_DIRECTORY_TREE
875 LOG(astring("common gave us posn of: ") + reassembled);
877 // handle the case for files now that we have our proper node.
878 string_array partial_pieces;
880 filename(reassembled).separate(partial_rooted, partial_pieces);
881 int levels_missing = pieces.length() - partial_pieces.length();
883 // we loop over all the pieces that were missing in between the last
884 // common root and the file's final location.
885 for (int i = 0; i < levels_missing; i++) {
886 #ifdef DEBUG_DIRECTORY_TREE
887 LOG(astring("adding intermediate directory: ") + reassembled);
889 filename_tree *new_branch = new filename_tree;
890 new_branch->_depth = last_match->_depth + 1;
891 new_branch->_dirname = filename(reassembled).raw();
892 last_match->attach(new_branch);
893 last_match = new_branch;
894 reassembled += astring("/") + pieces[partial_pieces.length() + i];
895 reassembled = filename(reassembled).raw(); // canonicalize.
899 if (!last_match->_files.find(pieces[pieces.last()])) {
900 #ifdef DEBUG_DIRECTORY_TREE
901 LOG(astring("adding new file ") + pieces[pieces.last()]
902 + " at " + reassembled);
904 file_info *to_add = new file_info(pieces[pieces.last()], 0);
905 to_add->calculate(reassembled, just_size);
906 last_match->_files += to_add;
908 #ifdef DEBUG_DIRECTORY_TREE
909 LOG(astring("not adding existing file ") + pieces[pieces.last()]
910 + " at " + reassembled);
914 // handle the case for directories.
915 #ifdef DEBUG_DIRECTORY_TREE
916 LOG(astring("doing traverse in ") + last_match->_dirname
917 + " to add " + reassembled);
919 traverse(reassembled, "*", *last_match);
920 //hmmm: maybe provide pattern capability instead of assuming all files.
921 calculate(last_match, just_size);
927 outcome directory_tree::remove_path(const astring &zap_item)
929 FUNCDEF("remove_path");
930 // find the common root, if one exists. if not, we're not going to do this.
932 filename_tree *last_match = NULL_POINTER;
935 outcome ret = find_common_root(zap_item, false, last_match, reassembled,
937 if (!last_match) return common::NOT_FOUND;
938 // if we didn't actually finish iterating to the file, then we're not
939 // whacking anything.
940 if (ret != common::OKAY) {
941 #ifdef DEBUG_DIRECTORY_TREE
942 LOG(astring("got error seeking ") + zap_item + " of "
943 + common::outcome_name(ret));
948 if (comp_index == pieces.last()) {
949 // if the names match fully, then we're talking about a directory.
950 #ifdef DEBUG_DIRECTORY_TREE
951 LOG(astring("found directory match for ") + zap_item);
954 #ifdef DEBUG_DIRECTORY_TREE
955 LOG(astring("may have found file match for ") + zap_item);
957 filename to_seek(pieces[pieces.last()]);
958 if (!last_match->_files.member(to_seek)) {
959 // this file is not a member, so we must say it's not found.
960 #ifdef DEBUG_DIRECTORY_TREE
961 LOG(astring("couldn't find file match in common root for ") + zap_item);
963 return common::NOT_FOUND;
965 int indy = last_match->_files.locate(to_seek);
966 #ifdef DEBUG_DIRECTORY_TREE
967 LOG(astring("found match to remove for ") + zap_item);
969 last_match->_files.zap(indy, indy);
970 return common::OKAY; // done!
974 #ifdef DEBUG_DIRECTORY_TREE
975 LOG(astring("going to whack node at: ") + last_match->_dirname.raw());
978 // we're whacking directories, so we need to take out last_match and below.
979 filename_tree *parent = (filename_tree *)last_match->parent();
980 if (!parent || (last_match == _real_tree)) {
981 // this seems to be matching the whole tree. we disallow that.
982 #ifdef DEBUG_DIRECTORY_TREE
983 LOG("there's a problem whacking this node; it's the root.");
985 return common::BAD_INPUT;
987 #ifdef DEBUG_DIRECTORY_TREE
988 LOG(astring("pruning tree at ") + last_match->_dirname.raw());
990 parent->prune(last_match);
996 basis::outcome directory_tree::make_directories(const basis::astring new_root)
998 FUNCDEF("make_directories");
999 // iterate down the tree, making all the directories requested.
1000 dir_tree_iterator *ted = start(directory_tree::prefix);
1002 int depth; // current depth in tree.
1003 filename curr; // the current path the iterator is at.
1004 string_array files; // the filenames held at the iterator.
1006 #ifdef DEBUG_DIRECTORY_TREE
1007 LOG(astring("new root is ") + new_root);
1009 filename old_root = path();
1010 #ifdef DEBUG_DIRECTORY_TREE
1011 LOG(astring("old root was ") + old_root.raw());
1014 while (current(*ted, curr, files)) {
1015 // we have a good directory to show.
1016 directory_tree::depth(*ted, depth);
1017 int found = curr.find(old_root);
1018 if (found < common::OKAY) {
1019 LOG(astring("failed to find prefix of path '") + old_root
1020 + "' in the current directory '" + curr + "'");
1023 curr = curr.substring(found + old_root.length(), curr.end());
1024 //LOG(astring("curr now is: ") + curr);
1025 filename dir_to_make(new_root + "/" + curr);
1026 #ifdef DEBUG_DIRECTORY_TREE
1027 LOG(astring("creating dir: ") + dir_to_make.raw());
1029 directory::recursive_create(dir_to_make.raw());
1034 return common::OKAY;