31 using namespace basis;
33 using namespace nodes;
41 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
47 class dir_tree_iterator :
public filename_tree::iterator
50 filename_tree *_current;
52 dir_tree_iterator(
const filename_tree *initial,
54 : filename_tree::iterator(initial, dir), _current(
NULL_POINTER) {}
59 directory_tree::directory_tree()
60 : _scanned_okay(false),
71 : _scanned_okay(false),
75 _ignore_files(ignore_files),
83 _scanned_okay =
false;
102 attach(packed_form,
int(_scanned_okay));
103 attach(packed_form,
int(_ignore_files));
104 _path->
pack(packed_form);
105 _pattern->
pack(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);
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] +
" ";
148 target += split + parser_bits::platform_eol_to_chars();
159 void directory_tree::traverse(
const astring &
path,
const char *pattern,
166 #ifdef DEBUG_DIRECTORY_TREE
172 if (!curr.good())
return;
174 if (!_ignore_files) {
177 add_to.
_files = curr_stringent.files();
183 for (
int i = 0; i < dirs.
length(); i++) {
186 #ifdef DEBUG_DIRECTORY_TREE
189 if (!filename(new_path).is_normal()) {
191 LOG(
astring(
"bailing on weird dir: ") + new_path);
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);
227 _scanned_okay =
false;
236 #ifdef DEBUG_DIRECTORY_TREE
242 if (!curr.
good())
return false;
247 traverse(
path, pattern, *_real_tree);
248 _scanned_okay =
true;;
257 if (type ==
infix) dir = tree::infix;
258 else if (type ==
postfix) dir = tree::postfix;
260 return new dir_tree_iterator(
start, dir);
267 if (type ==
infix) dir = tree::infix;
268 else if (type ==
postfix) dir = tree::postfix;
270 return new dir_tree_iterator(_real_tree, dir);
280 for (
int i = 0; i < pieces.
length(); i++) {
282 #ifdef DEBUG_DIRECTORY_TREE
287 curr_path.
join(rooted, sub_pieces);
290 #ifdef DEBUG_DIRECTORY_TREE
293 if (!curr)
return false;
294 bool found_it =
false;
295 for (
int j = 0; j < curr->
branches(); j++) {
297 #ifdef DEBUG_DIRECTORY_TREE
303 #ifdef DEBUG_DIRECTORY_TREE
311 #ifdef DEBUG_DIRECTORY_TREE
320 filename_tree *directory_tree::goto_current(dir_tree_iterator &scanning)
322 if (!scanning._current) {
330 return dynamic_cast<filename_tree *
>(scanning._current);
336 dir_name = astring::empty_string();
338 if (!tof)
return false;
347 dir_name = astring::empty_string();
351 if (!tof)
return false;
364 dir_name = astring::empty_string();
368 if (!tof)
return false;
388 if (!tof)
return false;
397 if (!tof)
return false;
405 return !!scanning._current;
414 bool ignore_initial)
const
420 #ifdef DEBUG_DIRECTORY_TREE
421 LOG(
astring(
"seeking on root of: ") + *_path);
430 #ifdef DEBUG_DIRECTORY_TREE
433 examining += _real_tree;
438 while (examining.
length()) {
444 for (posn = 0; posn < examining.
length(); posn++) {
445 check = examining[posn];
453 #ifdef DEBUG_DIRECTORY_TREE
456 if (
current.compare_prefix(dir_name, sequel)) {
458 #ifdef DEBUG_DIRECTORY_TREE
464 #ifdef DEBUG_DIRECTORY_TREE
469 #ifdef DEBUG_DIRECTORY_TREE
470 LOG(
astring(
"inexact match because sequel=") + sequel);
482 LOG(
"serious logical error: tree was not located.");
486 for (
int i = 0; i < check->
branches(); i++)
494 {
return calculate(_real_tree, just_size); }
499 #ifdef DEBUG_DIRECTORY_TREE
500 LOG(
astring(
"calculate: got tree to start with at ") +
start->_dirname.raw());
514 LOG(
astring(
"calculate: skipping abnormal dir: \"") + curr +
"\"");
520 #ifdef DEBUG_DIRECTORY_TREE
525 for (
int i = 0; i <
files->elements(); i++) {
527 #ifdef DEBUG_DIRECTORY_TREE
528 LOG(
astring(
"got a curr file: ") + curr_file);
532 LOG(
astring(
"skipping abnormal file: \"") + curr +
"\"");
536 if (!
files->borrow(i)->calculate(curr.
raw(), just_size)) {
552 return compare_trees(source, astring::empty_string(), target,
553 astring::empty_string(), differences, how_to_compare);
565 filename source_start(source_start_in);
566 filename target_start(target_start_in);
572 if (source_start.
raw().
t()) {
575 + source_start.
raw();
578 LOG(
astring(
"failed to find source start in tree, given as ")
579 + source_start.
raw());
588 int source_pieces = 0;
593 source_pieces = temp.
length();
596 bool seen_zero_pieces =
false;
601 #ifdef DEBUG_DIRECTORY_TREE
608 #ifdef DEBUG_DIRECTORY_TREE
611 pieces.
zap(0, source_pieces - 1);
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()) {
640 #ifdef DEBUG_DIRECTORY_TREE
641 LOG(
astring(
"target with start is: ") + corresponding_name);
649 LOG(
astring(
"could not find dir in target for ") + curr.
raw()
650 +
" which we computed corresp as " + corresponding_name.
raw());
655 for (
int i = 0; i <
files.elements(); i++) {
659 #ifdef DEBUG_DIRECTORY_TREE
667 #ifdef DEBUG_DIRECTORY_TREE
672 #ifdef DEBUG_DIRECTORY_TREE
673 if (actual_name.
t())
LOG(
astring(
"sname=") + actual_name);
676 actual_name += original_correspondence.
raw();
678 actual_name += new_record->
raw();
679 #ifdef DEBUG_DIRECTORY_TREE
680 if (actual_name.
t())
LOG(
astring(
"sname=") + actual_name);
685 #ifdef DEBUG_DIRECTORY_TREE
686 if (targ_name.
t())
LOG(
astring(
"tname=") + targ_name);
689 targ_name += original;
690 #ifdef DEBUG_DIRECTORY_TREE
691 if (targ_name.
t())
LOG(
astring(
"tname=") + targ_name);
696 differences += new_record;
697 #ifdef DEBUG_DIRECTORY_TREE
712 outcome directory_tree::find_common_root(
const astring &finding,
bool exists,
719 if (exists && !adding.good())
720 return common::BAD_INPUT;
721 int file_subtract = 0;
722 if (exists && !adding.is_directory()) file_subtract = 1;
727 adding.separate(rooted, pieces);
734 temp_file.separate(root_rooted, root_pieces);
739 int list_length = pieces.
length() - file_subtract;
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;
766 bool add_slash =
false;
767 if (reassembled.
length() && (reassembled[reassembled.
end()] !=
'/') )
770 if (add_slash) reassembled +=
"/";
771 reassembled += pieces[match_place];
773 if (reassembled[reassembled.
end()] ==
':') {
774 #ifdef DEBUG_DIRECTORY_TREE
775 LOG(
astring(
"skipping drive component ") + reassembled);
779 reassembled = filename(reassembled).raw();
780 #ifdef DEBUG_DIRECTORY_TREE
783 filename_tree *sought =
seek(reassembled,
false);
785 #ifdef DEBUG_DIRECTORY_TREE
788 if (!exists && (match_place == list_length - 1)) {
793 to_return = common::OKAY;
802 #ifdef DEBUG_DIRECTORY_TREE
803 LOG(
astring(
"found subtree for ") + reassembled);
810 if (match_place >= list_length) {
811 match_place = list_length - 1;
812 to_return = common::OKAY;
824 if (!adding.
good()) {
825 LOG(
astring(
"non-existent new item! ") + new_item);
826 return common::NOT_FOUND;
830 LOG(
astring(
"abnormal new item: ") + new_item);
832 return common::BAD_INPUT;
834 int file_subtract = 0;
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); }
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;
855 if (!file_subtract) {
856 if (ret != common::OKAY) {
858 #ifdef DEBUG_DIRECTORY_TREE
859 LOG(
astring(
"now adding node for ") + reassembled);
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);
881 int levels_missing = pieces.
length() - partial_pieces.
length();
885 for (
int i = 0; i < levels_missing; i++) {
886 #ifdef DEBUG_DIRECTORY_TREE
887 LOG(
astring(
"adding intermediate directory: ") + reassembled);
892 last_match->
attach(new_branch);
893 last_match = new_branch;
894 reassembled +=
astring(
"/") + pieces[partial_pieces.
length() + i];
900 #ifdef DEBUG_DIRECTORY_TREE
902 +
" at " + reassembled);
905 to_add->
calculate(reassembled, just_size);
906 last_match->
_files += to_add;
908 #ifdef DEBUG_DIRECTORY_TREE
910 +
" at " + reassembled);
915 #ifdef DEBUG_DIRECTORY_TREE
917 +
" to add " + reassembled);
919 traverse(reassembled,
"*", *last_match);
935 outcome ret = find_common_root(zap_item,
false, last_match, reassembled,
937 if (!last_match)
return common::NOT_FOUND;
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()) {
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);
960 #ifdef DEBUG_DIRECTORY_TREE
961 LOG(
astring(
"couldn't find file match in common root for ") + zap_item);
963 return common::NOT_FOUND;
966 #ifdef DEBUG_DIRECTORY_TREE
967 LOG(
astring(
"found match to remove for ") + zap_item);
974 #ifdef DEBUG_DIRECTORY_TREE
980 if (!parent || (last_match == _real_tree)) {
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
990 parent->
prune(last_match);
1006 #ifdef DEBUG_DIRECTORY_TREE
1010 #ifdef DEBUG_DIRECTORY_TREE
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 +
"'");
1025 filename dir_to_make(new_root +
"/" + curr);
1026 #ifdef DEBUG_DIRECTORY_TREE
1034 return common::OKAY;
Represents a sequential, ordered, contiguous collection of objects.
void reset(int number=0, const contents *initial_contents=NULL_POINTER)
Resizes this array and sets the contents from an array of contents.
array subarray(int start, int end) const
Returns the array segment between the indices "start" and "end".
int length() const
Returns the current reported length of the allocated C array.
outcome zap(int start, int end)
Deletes from "this" the objects inclusively between "start" and "end".
int last() const
Returns the last valid element in the array.
Provides a dynamically resizable ASCII character string.
bool t() const
t() is a shortcut for the string being "true", as in non-empty.
void pack(byte_array &target) const
stores this string in the "target". it can later be unpacked again.
int packed_size() const
Reports the size required to pack this string into a byte array.
bool substring(astring &target, int start, int end) const
a version that stores the substring in an existing "target" string.
int end() const
returns the index of the last (non-null) character in the string.
int length() const
Returns the current length of the string.
bool unpack(byte_array &source)
retrieves a string (packed with pack()) from "source" into this string.
int find(char to_find, int position=0, bool reverse=false) const
Locates "to_find" in "this".
A very common template for a dynamic array of bytes.
Outcomes describe the state of completion for an operation.
An object that traverses directory trees and provides a view of all files.
static bool current(dir_tree_iterator &scanning, filename &dir_name, structures::string_array &to_fill)
retrieves the information for the iterator's current location.
static bool compare_trees(const directory_tree &source, const directory_tree &target, filename_list &differences, file_info::file_similarity how_to_compare)
compares the tree in "source" with the tree in "target".
void text_form(basis::astring &tree_dump, bool show_files=true)
provides a visual representation of the tree in "tree_dump".
filename_tree * seek(const basis::astring &dir_name, bool ignore_initial) const
finds the "dir_name" in our tree.
basis::outcome add_path(const basis::astring &new_item, bool just_size=false)
adds a "new_item" into the tree.
dir_tree_iterator * start_at(filename_tree *start, traversal_types type) const
starts the iterator at a specific "start" node.
static filename_list * access(dir_tree_iterator &scanning)
more dangerous operation that lets the actual list be manipulated.
static bool depth(dir_tree_iterator &scanning, int &depth)
returns the current depth of the iterator.
@ postfix
postfix means that subnodes are traversed first (depth first).
@ prefix
prefix means that subnodes are processed after their parent.
@ infix
infix (for binary trees) goes 1) left, 2) current, 3) right.
basis::outcome remove_path(const basis::astring &zap_item)
removes the "zap_item" from the tree.
virtual bool unpack(basis::byte_array &packed_form)
unpacks the directory_tree from a byte_array.
static void throw_out(dir_tree_iterator *&to_whack)
cleans up an iterator that was previously opened with start().
bool calculate(bool just_size)
visits each file in the directory_tree and calculates its attributes.
virtual int packed_size() const
reports the size after packing up the tree.
static bool children(dir_tree_iterator &scanning, int &children)
returns the number of children for the current node.
const basis::astring & path() const
returns the root of the directory tree that we manage.
basis::outcome make_directories(const basis::astring new_root)
creates all of the directories in this object, but start at the "new_root".
directory_tree()
constructs an empty tree.
static bool next(dir_tree_iterator &scanning)
goes to the next filename in the "scanning" iterator.
virtual void pack(basis::byte_array &packed_form) const
packs the directory_tree into a byte_array.
dir_tree_iterator * start(traversal_types type) const
starts an iterator on the directory tree.
bool reset(const basis::astring &path, const char *pattern="*")
gets rid of any current files and rescans the directory at "path".
static bool jump_to(dir_tree_iterator &scanning, const basis::astring &sub_path)
seeks to a "sub_path" below the iterator's current position.
static bool current_dir(dir_tree_iterator &scanning, filename &dir_name)
sets "dir_name" to the directory name at the "scanning" location.
Implements a scanner that finds all filenames in the directory specified.
bool good() const
true if the directory existed and its contents were readable.
static bool recursive_create(const basis::astring &directory_name)
returns true if the "directory_name" can be created or already exists.
Encapsulates some measures and calculations based on a file's contents.
const basis::astring & secondary() const
observes the alternate form of the name.
bool calculate(const basis::astring &prefix, bool just_size_n_time, int checksum_edge=1 *basis::KILOBYTE)
fills in the correct file size and checksum information for this file.
basis::astring text_form() const
file_similarity
this enum encapsulates how files may be compared.
bool member(const filename &to_check) const
finds the index for "to_find" or returns a negative number.
bool member_with_state(const file_info &to_check, file_info::file_similarity comparison_method)
returns true if the file "to_check" exists in the list with appropriate equivalence.
const file_info * find(const filename &to_check) const
locates the record of information for the filename "to_check".
int locate(const filename &to_find) const
void fill(structures::string_array &to_fill)
stuffs the array "to_fill" with the filesnames from our list.
int _depth
how far below root node are we.
filename_list _files
the filenames that are at this node in the tree.
filename _dirname
the full directory name at this position.
Provides operations commonly needed on file names.
void join(bool rooted, const structures::string_array &pieces)
undoes a separate() operation to get the filename back.
bool good() const
returns true if the filename seems to be valid.
void separate(bool &rooted, structures::string_array &pieces) const
breaks the filename into its component parts.
bool compare_prefix(const filename &to_compare, basis::astring &sequel)
examines "this" filename to see if it's a prefix of "to_compare".
static basis::astring default_separator()
returns the default separator character for this OS.
bool is_directory() const
const basis::astring & raw() const
returns the astring that we're holding onto for the path.
this is the tree factory used in the recursive_unpack.
void recursive_pack(basis::byte_array &packed_form) const
packs the whole tree starting at this node into the packed form.
int recursive_packed_size() const
spiders the tree starting at this node to calculate the packed size.
A method for tracing a route from a tree's root to a particular node.
virtual int branches() const
Returns the number of branches currently connected to this tree.
virtual void attach(tree *new_branch)
Attaches the specified branch to the current tree.
virtual basis::outcome prune(tree *branch_to_cut)
Removes the specified branch from this tree.
virtual tree * parent() const
Returns the tree node that is the immediate ancestor of this one.
virtual tree * branch(int branch_number) const
Returns the specified branch of this tree.
basis::outcome zap(int start, int end)
Removes a range of indices from the amorph.
void reset()
cleans out all of the contents.
An array of strings with some additional helpful methods.
basis::astring text_form() const
A synonym for the text_format() method.
#define NULL_POINTER
The value representing a pointer to nothing.
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
The guards collection helps in testing preconditions and reporting errors.
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
void attach(byte_array &packed_form, const char *to_attach)
Packs a character string "to_attach" into "packed_form".
bool detach(byte_array &packed_form, astring &to_detach)
Unpacks a character string "to_attach" from "packed_form".
A platform independent way to obtain the timestamp of a file.
A logger that sends to the console screen using the standard output device.
None split_lines(str unsplit_line)
A dynamic container class that holds any kind of object via pointers.
const int PACKED_SIZE_INT32