directory_tree.cpp

Go to the documentation of this file.
00001 #ifndef DIRECTORY_TREE_IMPLEMENTATION_FILE
00002 #define DIRECTORY_TREE_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : directory_tree                                                    *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *******************************************************************************
00010 * Copyright (c) 2004-$now By Author.  This program is free software; you can  *
00011 * redistribute it and/or modify it under the terms of the GNU General Public  *
00012 * License as published by the Free Software Foundation; either version 2 of   *
00013 * the License or (at your option) any later version.  This is online at:      *
00014 *     http://www.fsf.org/copyleft/gpl.html                                    *
00015 * Please send any updates to: fred@gruntose.com                               *
00016 \*****************************************************************************/
00017 
00018 #include "directory.h"
00019 #include "directory_tree.h"
00020 #include "filename.h"
00021 #include "filename_list.h"
00022 #include "filename_tree.h"
00023 
00024 #include <basis/function.h>
00025 #include <basis/log_base.h>
00026 #include <basis/string_array.h>
00027 #include <nodes/node.h>
00028 #include <textual/string_manipulation.h>
00029 
00030 using namespace basis;
00031 using namespace nodes;
00032 
00033 //#define DEBUG_DIRECTORY_TREE
00034   // uncomment for noisier version.
00035 
00036 #undef LOG
00037 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), s)
00038 
00040 
00041 class dir_tree_iterator : public filename_tree::iterator
00042 {
00043 public:
00044   filename_tree *_current;
00045 
00046   dir_tree_iterator(const filename_tree *initial,
00047       tree::traversal_directions dir)
00048   : filename_tree::iterator(initial, dir), _current(NIL) {}
00049 };
00050 
00052 
00053 directory_tree::directory_tree()
00054 : _scanned_okay(false),
00055   _path(new istring),
00056   _pattern(new istring),
00057   _real_tree(new filename_tree),
00058   _ignore_files(false),
00059   _creator(new fname_tree_creator)
00060 {
00061 }
00062 
00063 directory_tree::directory_tree(const istring &path, const char *pattern,
00064     bool ignore_files)
00065 : _scanned_okay(false),
00066   _path(new istring(path)),
00067   _pattern(new istring(pattern)),
00068   _real_tree(NIL),
00069   _ignore_files(ignore_files),
00070   _creator(new fname_tree_creator)
00071 {
00072   reset(path, pattern);
00073 }
00074 
00075 directory_tree::~directory_tree()
00076 {
00077   _scanned_okay = false;
00078   WHACK(_path);
00079   WHACK(_pattern);
00080   WHACK(_real_tree);
00081   WHACK(_creator);
00082 }
00083 
00084 const istring &directory_tree::path() const { return *_path; }
00085 
00086 void directory_tree::pack(byte_array &packed_form) const
00087 {
00088   attach(packed_form, int(_scanned_okay));
00089   attach(packed_form, int(_ignore_files));
00090   _path->pack(packed_form);
00091   _pattern->pack(packed_form);
00092   _real_tree->recursive_pack(packed_form);
00093 }
00094 
00095 bool directory_tree::unpack(byte_array &packed_form)
00096 {
00097   int temp;
00098   if (!detach(packed_form, temp)) return false;
00099   _scanned_okay = temp;
00100   if (!detach(packed_form, temp)) return false;
00101   _ignore_files = temp;
00102   if (!_path->unpack(packed_form)) return false;
00103   if (!_pattern->unpack(packed_form)) return false;
00104   WHACK(_real_tree);
00105   _real_tree = (filename_tree *)packable_tree::recursive_unpack
00106       (packed_form, *_creator);
00107   if (!_real_tree) {
00108     _real_tree = new filename_tree;  // reset it.
00109     return false;
00110   }
00111   return true;
00112 }
00113 
00114 void directory_tree::text_form(istring &target, bool show_files)
00115 {
00116   dir_tree_iterator *ted = start(directory_tree::prefix);
00117     // create our iterator to do a prefix traversal.
00118 
00119   int depth;  // current depth in tree.
00120   filename curr;  // the current path the iterator is at.
00121   string_array files;  // the filenames held at the iterator.
00122 
00123   while (current(*ted, curr, files)) {
00124     // we have a good directory to show.
00125     directory_tree::depth(*ted, depth);
00126     target += string_manipulation::indentation(depth * 2) + istring("[")
00127         + curr.raw() + "]" + log_base::platform_ending();
00128     if (show_files) {
00129       istring names;
00130       for (int i = 0; i < files.length(); i++) names += files[i] + " ";
00131       if (names.length()) {
00132         istring split;
00133         string_manipulation::split_lines(names, split, depth * 2 + 2);
00134         target += split + log_base::platform_ending();
00135       }
00136     }
00137 
00138     // go to the next place.
00139     next(*ted);
00140   }
00141 
00142   throw_out(ted);
00143 }
00144 
00145 void directory_tree::traverse(const istring &path, const char *pattern,
00146     filename_tree &add_to)
00147 {
00148   FUNCDEF("traverse");
00149   // prepare the current node.
00150   add_to._dirname = filename(path, istring::empty_string());
00151   add_to._files.reset();
00152 #ifdef DEBUG_DIRECTORY_TREE
00153   LOG(istring("working on node ") + add_to._dirname.raw());
00154 #endif
00155 
00156   // open the directory.
00157   directory curr(path, "*");
00158   if (!curr.good()) return;
00159 
00160   if (!_ignore_files) {
00161     // add all the files to the current node.
00162     directory curr_stringent(path, pattern);
00163     add_to._files = curr_stringent.files();
00164   }
00165 
00166   // now iterate across the directories here and add a sub-node for each one,
00167   // and recursively traverse that sub-node also.
00168   const string_array &dirs = curr.directories();
00169   for (int i = 0; i < dirs.length(); i++) {
00170     filename_tree *new_branch = NIL;
00171     istring new_path = path + filename::default_separator() + dirs[i];
00172 #ifdef DEBUG_DIRECTORY_TREE
00173     LOG(istring("seeking path: ") + new_path);
00174 #endif
00175     for (int q = 0; q < add_to.branches(); q++) {
00176       filename_tree *curr_kid = (filename_tree *)add_to.branch(q);
00177 #ifdef DEBUG_DIRECTORY_TREE
00178       LOG(istring("curr kid: ") + curr_kid->_dirname);
00179 #endif
00180       if (filename(new_path).raw().iequals(filename
00181           (curr_kid->_dirname).raw())) {
00182         new_branch = curr_kid;
00183 #ifdef DEBUG_DIRECTORY_TREE
00184         LOG(istring("using existing branch for ") + new_path);
00185 #endif
00186         break;
00187       }
00188     }
00189     if (!new_branch) {
00190 #ifdef DEBUG_DIRECTORY_TREE
00191       LOG(istring("adding new branch for ") + new_path);
00192 #endif
00193       new_branch = new filename_tree;
00194       add_to.attach(new_branch);
00195       new_branch->_depth = add_to._depth + 1;
00196     }
00197 #ifdef DEBUG_DIRECTORY_TREE
00198     LOG(istring("traversing sub-node ") + new_path);
00199 #endif
00200     traverse(new_path, pattern, *new_branch);
00201   }
00202 }
00203 
00204 bool directory_tree::reset(const istring &path, const char *pattern)
00205 {
00206   _scanned_okay = false;
00207   WHACK(_real_tree);
00208   *_path = path;
00209   *_pattern = pattern;
00210   _real_tree = new filename_tree;
00211 
00212   // check that the top-level is healthy.
00213   directory curr(path, "*");
00214   if (!curr.good()) return false;
00215     // our only exit condition; other directories might not be accessible
00216     // underneath, but the top one must be accessible for us to even start
00217     // the scanning.
00218 
00219   traverse(path, pattern, *_real_tree);
00220   _scanned_okay = true;;
00221   return true;
00222 }
00223 
00224 dir_tree_iterator *directory_tree::start_at(filename_tree *start,
00225     traversal_types type) const
00226 {
00227   // translate to the lower level traversal enum.
00228   tree::traversal_directions dir = tree::prefix;
00229   if (type == infix) dir = tree::infix;
00230   else if (type == postfix) dir = tree::postfix;
00231 
00232   return new dir_tree_iterator(start, dir);
00233 }
00234 
00235 dir_tree_iterator *directory_tree::start(traversal_types type) const
00236 {
00237   // translate to the lower level traversal enum.
00238   tree::traversal_directions dir = tree::prefix;
00239   if (type == infix) dir = tree::infix;
00240   else if (type == postfix) dir = tree::postfix;
00241 
00242   return new dir_tree_iterator(_real_tree, dir);
00243 }
00244 
00245 bool directory_tree::jump_to(dir_tree_iterator &scanning,
00246     const istring &sub_path)
00247 {
00248   FUNCDEF("jump_to");
00249   string_array pieces;
00250   filename(sub_path).separate(pieces);
00251   for (int i = 0; i < pieces.length(); i++) {
00252     filename_tree *curr = dynamic_cast<filename_tree *>(scanning.current());
00253 #ifdef DEBUG_DIRECTORY_TREE
00254     LOG(istring("at ") + curr->_dirname.raw());
00255 #endif
00256     string_array sub_pieces = pieces.subarray(i, i);
00257     filename curr_path;
00258     curr_path.join(sub_pieces);
00259     curr_path = filename(curr->_dirname.raw() + filename::default_separator()
00260         + curr_path.raw());
00261 #ifdef DEBUG_DIRECTORY_TREE
00262     LOG(istring("made curr path ") + curr_path.raw());
00263 #endif
00264     if (!curr) return false;
00265     bool found_it = false;
00266     for (int j = 0; j < curr->branches(); j++) {
00267       filename_tree *sub = dynamic_cast<filename_tree *>(curr->branch(j));
00268 #ifdef DEBUG_DIRECTORY_TREE
00269       LOG(istring("looking at ") + sub->_dirname.raw());
00270 #endif
00271       if (sub->_dirname.compare_prefix(curr_path)) {
00272         // this part matches!
00273         scanning.push(sub);
00274 #ifdef DEBUG_DIRECTORY_TREE
00275         LOG(istring("found at ") + sub->_dirname.raw());
00276 #endif
00277         found_it = true;
00278         break;
00279       }
00280     }
00281     if (!found_it) {
00282 #ifdef DEBUG_DIRECTORY_TREE
00283       LOG(istring("could not find ") + curr_path.raw());
00284 #endif
00285       return false;
00286     }
00287   }
00288   return true;
00289 }
00290 
00291 filename_tree *directory_tree::goto_current(dir_tree_iterator &scanning)
00292 {
00293   if (!scanning._current) {
00294     // this one hasn't been advanced yet, or it's already over with.
00295     scanning._current = (filename_tree *)scanning.next();
00296   }
00297   // now check that we're healthy.
00298   if (!scanning._current) return NIL;  // all done.
00299 
00300   // cast the tree to the right type.
00301   return dynamic_cast<filename_tree *>(scanning._current);
00302 }
00303 
00304 bool directory_tree::current_dir(dir_tree_iterator &scanning,
00305     filename &dir_name)
00306 {
00307   dir_name = istring::empty_string();
00308   filename_tree *tof = goto_current(scanning);
00309   if (!tof) return false;
00310   dir_name = tof->_dirname;
00311   return true;
00312 }
00313 
00314 bool directory_tree::current(dir_tree_iterator &scanning,
00315     filename &dir_name, string_array &to_fill)
00316 {
00317   // clear any existing junk.
00318   dir_name = istring::empty_string();
00319   to_fill.reset();
00320 
00321   filename_tree *tof = goto_current(scanning);
00322   if (!tof) return false;
00323 
00324   // fill in what they wanted.
00325   dir_name = tof->_dirname;
00326   tof->_files.fill(to_fill);
00327 
00328   return true;
00329 }
00330 
00331 bool directory_tree::current(dir_tree_iterator &scanning,
00332     filename &dir_name, filename_list &to_fill)
00333 {
00334   // clear any existing junk.
00335   dir_name = istring::empty_string();
00336   to_fill.reset();
00337 
00338   filename_tree *tof = goto_current(scanning);
00339   if (!tof) return false;
00340 
00341   // fill in what they wanted.
00342   dir_name = tof->_dirname;
00343   to_fill = tof->_files;
00344 
00345   return true;
00346 }
00347 
00348 filename_list *directory_tree::access(dir_tree_iterator &scanning)
00349 {
00350   filename_tree *tof = goto_current(scanning);
00351   if (!tof) return NIL;
00352   return &tof->_files;
00353 }
00354 
00355 bool directory_tree::depth(dir_tree_iterator &scanning, int &depth)
00356 {
00357   depth = -1;  // invalid as default.
00358   filename_tree *tof = goto_current(scanning);
00359   if (!tof) return false;
00360   depth = tof->_depth;
00361   return true;
00362 }
00363 
00364 bool directory_tree::children(dir_tree_iterator &scanning, int &kids)
00365 {
00366   kids = -1;  // invalid as default.
00367   filename_tree *tof = goto_current(scanning);
00368   if (!tof) return false;
00369   kids = tof->branches();
00370   return true;
00371 }
00372 
00373 bool directory_tree::next(dir_tree_iterator &scanning)
00374 {
00375   scanning._current = (filename_tree *)scanning.next();
00376   return !!scanning._current;
00377 }
00378 
00379 void directory_tree::throw_out(dir_tree_iterator * &to_whack)
00380 {
00381   WHACK(to_whack);
00382 }
00383 
00384 filename_tree *directory_tree::seek(const istring &dir_name_in,
00385     bool ignore_initial) const
00386 {
00387   FUNCDEF("seek");
00388   array<filename_tree *> examining;
00389     // the list of nodes we're currently looking at.
00390 
00391 #ifdef DEBUG_DIRECTORY_TREE
00392   LOG(istring("seeking on root of: ") + *_path);
00393 #endif
00394 
00395   istring dir_name = filename(dir_name_in).raw();
00396   // set the search path up to have the proper prefix.
00397   if (ignore_initial)
00398     dir_name = path() + filename::default_separator()
00399        + filename(dir_name_in).raw();
00400 
00401 #ifdef DEBUG_DIRECTORY_TREE
00402   LOG(istring("adding root: ") + _real_tree->_dirname);
00403 #endif
00404   examining += _real_tree;
00405 
00406   istring sequel;  // holds extra pieces from filename comparisons.
00407 
00408   // chew on the list of nodes to examine until we run out.
00409   while (examining.length()) {
00410     int posn;
00411     bool found = false;
00412     // start looking at all the items in the list, even though we might have
00413     // to abandon the iteration if we find a match.
00414     filename_tree *check = NIL;
00415     for (posn = 0; posn < examining.length(); posn++) {
00416       check = examining[posn];
00417       filename current(check->_dirname);
00418 #ifdef DEBUG_DIRECTORY_TREE
00419       LOG(istring("looking at ") + current.raw());
00420 #endif
00421       if (current.compare_prefix(dir_name, sequel)) {
00422         // we have a match!
00423 #ifdef DEBUG_DIRECTORY_TREE
00424         LOG(istring("matched! at ") + current.raw());
00425 #endif
00426         found = true;
00427         if (!sequel) {
00428           // whoa!  an exact match.  we're done now.
00429 #ifdef DEBUG_DIRECTORY_TREE
00430           LOG(istring("exact match at ") + current.raw() + "!  done!!!");
00431 #endif
00432           return check;
00433         } else {
00434 #ifdef DEBUG_DIRECTORY_TREE
00435           LOG(istring("inexact match because sequel=") + sequel);
00436 #endif
00437         }
00438         break;
00439       }
00440     }
00441     if (!found) return NIL;  // we found nothing comparable.
00442 
00443     // we found a partial match.  that means we should start looking at this
00444     // node's children for the exact match.
00445     if (!check) {
00446       // this is a serious logical error!
00447       LOG("serious logical error: tree was not located.");
00448       return NIL;
00449     }
00450     examining.reset();  // clear the existing nodes.
00451     for (int i = 0; i < check->branches(); i++)
00452       examining += (filename_tree *)check->branch(i);
00453   }
00454 
00455   return NIL;  // we found nothing that looked like that node.
00456 }
00457 
00458 bool directory_tree::calculate(bool just_size)
00459 { return calculate(_real_tree, just_size); }
00460 
00461 bool directory_tree::calculate(filename_tree *start, bool just_size)
00462 {
00463   FUNCDEF("calculate");
00464   dir_tree_iterator *ted = start_at(start, directory_tree::postfix);
00465     // create our iterator to do a postfix traversal.  why postfix?  well,
00466     // prefix has been used elsewhere and since it doesn't really matter what
00467     // order we visit the nodes here, it's good to change up.
00468 
00469   int depth;  // current depth in tree.
00470   filename curr;  // the current path the iterator is at.
00471   filename_list *files;  // the filenames held at the iterator.
00472 
00473   while (directory_tree::current_dir(*ted, curr)) {
00474     // we have a good directory to show.
00475 #ifdef DEBUG_DIRECTORY_TREE
00476     LOG(istring("calcing node ") + curr.raw());
00477 #endif
00478     files = directory_tree::access(*ted);
00479     directory_tree::depth(*ted, depth);
00480     for (int i = 0; i < files->elements(); i++) {
00481       if (!files->borrow(i)->calculate(curr.raw(), just_size)) {
00482         LOG(istring("failure to calculate ") + files->get(i)->text_form());
00483       }
00484     }
00485 
00486     directory_tree::next(*ted);
00487   }
00488 
00489   directory_tree::throw_out(ted);
00490   return true;
00491 }
00492 
00493 bool directory_tree::compare_trees(const directory_tree &source,
00494     const directory_tree &target, filename_list &differences,
00495     bool compare_size, bool compare_checksum)
00496 {
00497   return compare_trees(source, istring::empty_string(), target,
00498       istring::empty_string(), differences, compare_size, compare_checksum);
00499 }
00500 
00501 bool directory_tree::compare_trees(const directory_tree &source,
00502     const istring &source_start_in, const directory_tree &target,
00503     const istring &target_start_in, filename_list &differences,
00504     bool compare_size, bool compare_checksum)
00505 {
00506   FUNCDEF("compare_trees");
00507   differences.reset();  // prepare it for storage.
00508 
00509   // make sure we get canonical names to work with.
00510   filename source_start(source_start_in);
00511   filename target_start(target_start_in);
00512 
00513   dir_tree_iterator *ted = source.start(directory_tree::prefix);
00514     // create our iterator to do a prefix traversal.
00515 
00516   istring real_source_start = source.path();
00517   if (source_start.raw().t()) {
00518     // move to the right place.
00519     real_source_start = real_source_start + filename::default_separator()
00520         + source_start.raw();
00521     if (!directory_tree::jump_to(*ted, source_start.raw())) {
00522       // can't even start comparing.
00523       LOG(istring("failed to find source start in tree, given as ")
00524           + source_start.raw());
00525       return false;
00526     }
00527   }
00528 
00529   filename curr;  // the current path the iterator is at.
00530   filename_list files;  // the filenames held at the iterator.
00531 
00532   // calculate where our comparison point is on the source.
00533   int source_pieces = 0;
00534   {
00535     string_array temp;
00536     filename(real_source_start).separate(temp);
00537     source_pieces = temp.length();
00538   }
00539 
00540   bool seen_zero_pieces = false;
00541   while (directory_tree::current(*ted, curr, files)) {
00542     // we're in a place in the source tree now.  let's compare it with the
00543     // target's recollection.
00544 
00545 #ifdef DEBUG_DIRECTORY_TREE
00546     LOG(istring("curr dir in tree: ") + curr.raw());
00547 #endif
00548 
00549     string_array pieces;
00550     curr.separate(pieces);  // get the components of the current location.
00551 #ifdef DEBUG_DIRECTORY_TREE
00552     LOG(istring("name in pieces:") + pieces.text_form());
00553 #endif
00554     pieces.zap(0, source_pieces - 1);
00555       // snap the root components out of there.
00556 
00557     filename corresponding_name;
00558     corresponding_name.join(pieces);
00559 #ifdef DEBUG_DIRECTORY_TREE
00560     LOG(istring("computed target name as: ") + corresponding_name);
00561 #endif
00562     filename original_correspondence(corresponding_name);
00563 
00564     if (!corresponding_name.raw().t()) {
00565       if (seen_zero_pieces) {
00566 #ifdef DEBUG_DIRECTORY_TREE
00567         LOG(istring("breaking out now due to empty correspondence"));
00568 #endif
00569         break;
00570       }
00571       seen_zero_pieces = true;
00572     }
00573     if (target_start.raw().t()) {
00574       corresponding_name = filename(target_start.raw()
00575           + filename::default_separator() + corresponding_name.raw());
00576     }
00577 #ifdef DEBUG_DIRECTORY_TREE
00578     LOG(istring("target with start is: ") + corresponding_name);
00579 #endif
00580 
00581     filename_tree *target_now = target.seek(corresponding_name.raw(), true);
00582     if (!target_now) {
00583       // that entire sub-tree is missing.  add all of the files here into
00584       // the list.
00585 #ifdef DEBUG_DIRECTORY_TREE
00586       LOG(istring("could not find dir in target for ") + curr.raw()
00587           + " which we computed corresp as " + corresponding_name.raw());
00588 #endif
00589     }
00590 
00591     // now scan across all the files that are in our source list.
00592     for (int i = 0; i < files.elements(); i++) {
00593       if (!target_now  // there was no node, so we're adding everything...
00594           || !target_now->_files.member(*files[i], compare_size,
00595                   compare_checksum) ) {
00596         // ... or we need to add this file since it's missing.
00597 
00598 #ifdef DEBUG_DIRECTORY_TREE
00599         LOG(istring("adding record: ") + files[i]->text_form());
00600 #endif
00601 
00602         file_info *new_record = new file_info(*files[i]);
00603         istring original = new_record->raw();
00604 #ifdef DEBUG_DIRECTORY_TREE
00605         LOG(istring("current: ") + new_record->raw());
00606 #endif
00607 
00608         istring actual_name = source_start.raw();
00609 #ifdef DEBUG_DIRECTORY_TREE
00610         if (actual_name.t()) LOG(istring("sname=") + actual_name);
00611 #endif
00612         if (actual_name.length()) actual_name += filename::default_separator();
00613         actual_name += original_correspondence.raw();
00614         if (actual_name.length()) actual_name += filename::default_separator();
00615         actual_name += new_record->raw();
00616 #ifdef DEBUG_DIRECTORY_TREE
00617         if (actual_name.t()) LOG(istring("sname=") + actual_name);
00618 #endif
00619         (filename &)(*new_record) = filename(actual_name);
00620 
00621         istring targ_name = corresponding_name.raw();
00622 #ifdef DEBUG_DIRECTORY_TREE
00623         if (targ_name.t()) LOG(istring("tname=") + targ_name);
00624 #endif
00625         if (targ_name.length()) targ_name += filename::default_separator();
00626         targ_name += original;
00627 #ifdef DEBUG_DIRECTORY_TREE
00628         if (targ_name.t()) LOG(istring("tname=") + targ_name);
00629 #endif
00630 
00631         new_record->secondary(targ_name);
00632 
00633         differences += new_record;
00634 #ifdef DEBUG_DIRECTORY_TREE
00635         LOG(istring("came out as: ") + new_record->text_form());
00636 #endif
00637       }
00638     }
00639     
00640     // go to the next place.
00641     directory_tree::next(*ted);
00642   }
00643 
00644   directory_tree::throw_out(ted);
00645 
00646   return true;
00647 }
00648 
00649 outcome directory_tree::find_common_root(const istring &finding, bool exists,
00650     filename_tree * &found, istring &reassembled, string_array &pieces,
00651     int &match_place)
00652 {
00653   FUNCDEF("find_common_root");
00654   // test the path to find what it is.
00655   filename adding(finding);
00656   if (exists && !adding.good())
00657     return common::BAD_INPUT;  // not a good path.
00658   int file_subtract = 0;  // if it's a file, then we remove last component.
00659   if (exists && !adding.is_directory()) file_subtract = 1;
00660 
00661   // break up the path into pieces.
00662   pieces.reset();
00663   adding.separate(pieces);
00664 
00665   // break up our root into pieces; we must take off components that are
00666   // already in the root.
00667   string_array root_pieces;
00668   filename temp_file(path());
00669   temp_file.separate(root_pieces);
00670 
00671   // locate the last place where the path we were given touches our tree.
00672   // it could be totally new, partially new, or already contained.
00673   filename_tree *last_match = _real_tree;  // where the common root is located.
00674   int list_length = pieces.length() - file_subtract;
00675   reassembled = "";
00676 
00677   // we must put all the pieces in that already come from the root.
00678   for (int i = 0; i < root_pieces.length() - 1; i++) {
00679     bool add_slash = false;
00680     if (reassembled.length() && (reassembled[reassembled.end()] != '/') )
00681       add_slash = true;
00682     if (add_slash) reassembled += "/";
00683     reassembled += pieces[i];
00684     if (reassembled[reassembled.end()] == ':') {
00685 #ifdef DEBUG_DIRECTORY_TREE
00686       LOG(istring("skipping drive component ") + reassembled);
00687 #endif
00688       continue;
00689     }
00690   }
00691 
00692 #ifdef DEBUG_DIRECTORY_TREE
00693   LOG(istring("after pre-assembly, path is ") + reassembled);
00694 #endif
00695 
00696   outcome to_return = common::NOT_FOUND;
00697 
00698   for (match_place = root_pieces.length() - 1; match_place < list_length;
00699       match_place++) {
00700     // add a slash if there's not one present already.
00701     bool add_slash = false;
00702     if (reassembled.length() && (reassembled[reassembled.end()] != '/') )
00703       add_slash = true;
00704     // add the next component in to our path.
00705     if (add_slash) reassembled += "/";
00706     reassembled += pieces[match_place];
00707     // special case for dos paths.
00708     if (reassembled[reassembled.end()] == ':') {
00709 #ifdef DEBUG_DIRECTORY_TREE
00710       LOG(istring("skipping drive component ") + reassembled);
00711 #endif
00712       continue;
00713     }
00714     reassembled = filename(reassembled).raw();  // force compliance with OS.
00715 #ifdef DEBUG_DIRECTORY_TREE
00716     LOG(istring("now seeking ") + reassembled);
00717 #endif
00718     filename_tree *sought = seek(reassembled, false);
00719     if (!sought) {
00720 #ifdef DEBUG_DIRECTORY_TREE
00721       LOG(istring("couldn't find ") + reassembled);
00722 #endif
00723       if (!exists && (match_place == list_length - 1)) {
00724         // see if we can get a match on a file rather than a directory, but
00725         // only if we're near the end of the compare.
00726         if (last_match->_files.member(pieces[match_place])) {
00727           // aha!  a file match.
00728           to_return = common::OKAY;
00729           match_place--;
00730           break;
00731         }
00732       }
00733       match_place--;
00734       break;
00735     } else {
00736       // record where we last had some success.
00737 #ifdef DEBUG_DIRECTORY_TREE
00738       LOG(istring("found subtree for ") + reassembled);
00739 #endif
00740       last_match = sought;
00741     }
00742   }
00743   // this is a success, but our loop structure can put us one past the right
00744   // place.
00745   if (match_place >= list_length) {
00746     match_place = list_length - 1;
00747     to_return = common::OKAY;
00748   }
00749 
00750   found = last_match;
00751   return to_return;
00752 }
00753 
00754 outcome directory_tree::add_path(const istring &new_item, bool just_size)
00755 {
00756   FUNCDEF("add_path");
00757   // test the path to find out what it is.
00758   filename adding(new_item);
00759   if (!adding.good()) {
00760     LOG(istring("non-existent new item!  ") + new_item);
00761     return common::BAD_INPUT;  // not a good path.
00762   }
00763   int file_subtract = 0;  // if it's a file, then we remove last component.
00764   if (!adding.is_directory()) file_subtract = 1;
00765 #ifdef DEBUG_DIRECTORY_TREE
00766   if (file_subtract) LOG(istring("adding a file ") + new_item)
00767   else LOG(istring("adding a directory ") + new_item);
00768 #endif
00769 
00770   // find the common root, break up the path into pieces, and tell us where
00771   // we matched.
00772   string_array pieces;
00773   filename_tree *last_match = NIL;
00774   int comp_index;
00775   istring reassembled;  // this will hold the common root.
00776   outcome ret = find_common_root(new_item, true, last_match, reassembled,
00777       pieces, comp_index);
00778   if (!last_match) {
00779     LOG(istring("serious error finding common root for ") + new_item
00780         + ", got NIL tree.");
00781     return common::FAILURE;  // something serious isn't right.
00782   }
00783 
00784   if (!file_subtract) {
00785     if (ret != common::OKAY) {
00786       // if it's a new directory, we add a new node for traverse to work on.
00787 #ifdef DEBUG_DIRECTORY_TREE
00788       LOG(istring("now adding node for ") + reassembled);
00789 #endif
00790       filename_tree *new_branch = new filename_tree;
00791       new_branch->_depth = last_match->_depth + 1;
00792       last_match->attach(new_branch);
00793       last_match = new_branch;
00794     } else {
00795 #ifdef DEBUG_DIRECTORY_TREE
00796       LOG(istring("matched properly.  reassembled set to ") + reassembled);
00797 #endif
00798     }
00799   }
00800 
00801   if (file_subtract) {
00802     if (ret != common::OKAY) {
00803 #ifdef DEBUG_DIRECTORY_TREE
00804       LOG(istring("common gave us posn of: ") + reassembled);
00805 #endif
00806       // handle the case for files now that we have our proper node.
00807       string_array partial_pieces;
00808       filename(reassembled).separate(partial_pieces);
00809       int levels_missing = pieces.length() - partial_pieces.length();
00810 
00811       // we loop over all the pieces that were missing in between the last
00812       // common root and the file's final location.
00813       for (int i = 0; i < levels_missing; i++) {
00814 #ifdef DEBUG_DIRECTORY_TREE
00815         LOG(istring("adding intermediate directory: ") + reassembled);
00816 #endif
00817         filename_tree *new_branch = new filename_tree;
00818         new_branch->_depth = last_match->_depth + 1;
00819         new_branch->_dirname = filename(reassembled).raw();
00820         last_match->attach(new_branch);
00821         last_match = new_branch;
00822         reassembled += istring("/") + pieces[partial_pieces.length() + i];
00823         reassembled = filename(reassembled).raw();  // canonicalize.
00824       }
00825     }
00826 
00827     if (!last_match->_files.find(pieces[pieces.last()])) {
00828 #ifdef DEBUG_DIRECTORY_TREE
00829       LOG(istring("adding new file ") + pieces[pieces.last()]
00830         + " at " + reassembled);
00831 #endif
00832       file_info *to_add = new file_info(pieces[pieces.last()]);
00833       to_add->calculate(reassembled, just_size);
00834       last_match->_files += to_add;
00835     } else {
00836 #ifdef DEBUG_DIRECTORY_TREE
00837       LOG(istring("not adding existing file ") + pieces[pieces.last()]
00838           + " at " + reassembled);
00839 #endif
00840     }
00841   } else {
00842     // handle the case for directories.
00843 #ifdef DEBUG_DIRECTORY_TREE
00844     LOG(istring("doing traverse in ") + last_match->_dirname
00845         + " to add " + reassembled);
00846 #endif
00847     traverse(reassembled, "*", *last_match);
00848 //hmmm: maybe provide pattern capability instead of assuming all files.
00849     calculate(last_match, just_size);
00850   }
00851 
00852   return common::OKAY;
00853 }
00854 
00855 outcome directory_tree::remove_path(const istring &zap_item)
00856 {
00857   FUNCDEF("remove_path");
00858   // find the common root, if one exists.  if not, we're not going to do this.
00859   string_array pieces;
00860   filename_tree *last_match = NIL;
00861   int comp_index;
00862   istring reassembled;
00863   outcome ret = find_common_root(zap_item, false, last_match, reassembled,
00864       pieces, comp_index);
00865   if (!last_match) return common::NOT_FOUND;
00866   // if we didn't actually finish iterating to the file, then we're not
00867   // whacking anything.
00868   if (ret != common::OKAY) {
00869 #ifdef DEBUG_DIRECTORY_TREE
00870     LOG(istring("got error seeking ") + zap_item + " of "
00871         + common::outcome_name(ret));
00872 #endif
00873     return ret;
00874   }
00875 
00876   if (comp_index == pieces.last()) {
00877     // if the names match fully, then we're talking about a directory.
00878 #ifdef DEBUG_DIRECTORY_TREE
00879     LOG(istring("found directory match for ") + zap_item);
00880 #endif
00881   } else {
00882 #ifdef DEBUG_DIRECTORY_TREE
00883     LOG(istring("may have found file match for ") + zap_item);
00884 #endif
00885     filename to_seek(pieces[pieces.last()]);
00886     if (!last_match->_files.member(to_seek)) {
00887       // this file is not a member, so we must say it's not found.
00888 #ifdef DEBUG_DIRECTORY_TREE
00889       LOG(istring("couldn't find file match in common root for ") + zap_item);
00890 #endif
00891       return common::NOT_FOUND;
00892     } else {
00893       int indy = last_match->_files.locate(to_seek);
00894 #ifdef DEBUG_DIRECTORY_TREE
00895       LOG(istring("found match to remove for ") + zap_item);
00896 #endif
00897       last_match->_files.zap(indy, indy);
00898       return common::OKAY;  // done!
00899     }
00900   }
00901 
00902 #ifdef DEBUG_DIRECTORY_TREE
00903   LOG(istring("going to whack node at: ") + last_match->_dirname.raw());
00904 #endif
00905 
00906   // we're whacking directories, so we need to take out last_match and below.
00907   filename_tree *parent = (filename_tree *)last_match->parent();
00908   if (!parent || (last_match == _real_tree)) {
00909     // this seems to be matching the whole tree.  we disallow that.
00910 #ifdef DEBUG_DIRECTORY_TREE
00911     LOG("there's a problem whacking this node; it's the root.");
00912 #endif
00913     return common::BAD_INPUT;
00914   }
00915 #ifdef DEBUG_DIRECTORY_TREE
00916   LOG(istring("pruning tree at ") + last_match->_dirname.raw());
00917 #endif
00918   parent->prune(last_match);
00919   WHACK(last_match);
00920 
00921   return common::OKAY;
00922 }
00923 
00924 
00925 #endif //DIRECTORY_TREE_IMPLEMENTATION_FILE
00926 

Generated on Mon Jul 26 04:22:36 2010 for HOOPLE Libraries by  doxygen 1.5.6