1 #ifndef DIRECTORY_TREE_CLASS
2 #define DIRECTORY_TREE_CLASS
4 /*****************************************************************************\
6 * Name : directory_tree *
7 * Author : Chris Koeritz *
9 *******************************************************************************
10 * Copyright (c) 2004-$now By Author. This program is free software; you can *
11 * redistribute it and/or modify it under the terms of the GNU General Public *
12 * License as published by the Free Software Foundation; either version 2 of *
13 * the License or (at your option) any later version. This is online at: *
14 * http://www.fsf.org/copyleft/gpl.html *
15 * Please send any updates to: fred@gruntose.com *
16 \*****************************************************************************/
18 #include "directory.h"
19 #include "file_info.h"
21 #include <basis/byte_array.h>
22 #include <basis/contracts.h>
23 #include <basis/outcome.h>
24 #include <structures/string_array.h>
26 namespace filesystem {
28 // forward declarations.
29 class dir_tree_iterator;
33 class fname_tree_creator;
35 //! An object that traverses directory trees and provides a view of all files.
37 class directory_tree : public virtual basis::packable
40 directory_tree(); //!< constructs an empty tree.
42 directory_tree(const basis::astring &path, const char *pattern = "*",
43 bool ignore_files = false);
44 //!< opens up the "path" specified and scans for files and subdirectories.
45 /*!< if the location was accessible, then the good() method returns true.
46 note that the "path" should just be a bare directory without any
47 wildcards attached. the "pattern" can be specified if you wish to
48 strain out just a subset of the files in the directory. note that
49 unlike the directory object, directory_tree applies the wildcard to
50 filenames only--all sub-directories are included. the pattern must meet
51 the same requirements that the operating system places on wildcard
52 patterns. if "ignore_files" is true, then no files are considered and
53 only the tree of directories is gathered. */
57 DEFINE_CLASS_NAME("directory_tree");
59 bool good() const { return _scanned_okay; }
60 //!< returns true if the directory existed and we read its contents.
62 const basis::astring &path() const;
63 //!< returns the root of the directory tree that we manage.
65 bool reset(const basis::astring &path, const char *pattern = "*");
66 //!< gets rid of any current files and rescans the directory at "path".
67 /*!< a new "pattern" can be specified at this time also. true is returned
68 if the process was started successfully at "path"; there might be
69 problems with subdirectories, but at least the "path" got validated. */
71 filename_tree *seek(const basis::astring &dir_name, bool ignore_initial) const;
72 //!< finds the "dir_name" in our tree.
73 /*!< locates the node that corresponds to the directory name contained in
74 "dir_name" and returns the filename_tree rooted at that node. if the
75 "ignore_initial" flag is true, then dir_name is expected to omit the
76 path() where "this" tree is rooted. */
78 virtual int packed_size() const;
79 //!< reports the size after packing up the tree.
80 virtual void pack(basis::byte_array &packed_form) const;
81 //!< packs the directory_tree into a byte_array.
82 virtual bool unpack(basis::byte_array &packed_form);
83 //!< unpacks the directory_tree from a byte_array.
85 bool calculate(bool just_size);
86 //!< visits each file in the directory_tree and calculates its attributes.
87 /*!< the attributes include file size and checksum. if "just_size" is
88 true, then no checksum is computed. */
90 bool calculate(filename_tree *start, bool just_size);
91 //!< a calculate method that starts at a specific node rather than the root.
93 basis::outcome add_path(const basis::astring &new_item, bool just_size = false);
94 //!< adds a "new_item" into the tree.
95 /*!< this is useful when one knows that new files exist under the
96 directory, but one doesn't want to recalculate the entire tree. the new
97 item will automatically be calculated. the item can be either a file or
98 directory that's under the root. the root directory name should not be
99 included in the "new_item". */
101 basis::outcome remove_path(const basis::astring &zap_item);
102 //!< removes the "zap_item" from the tree.
103 /*!< this only works for cases where one knows that an item has been
104 removed in the filesystem. if the item is still really there, then the
105 next rescan will put it back into the tree. */
107 basis::outcome make_directories(const basis::astring new_root);
108 //!< creates all of the directories in this object, but start at the "new_root".
110 static bool compare_trees(const directory_tree &source,
111 const directory_tree &target, filename_list &differences,
112 file_info::file_similarity how_to_compare);
113 //!< compares the tree in "source" with the tree in "target".
114 /*!< the two root names may be different, but everything below the root
115 in "source" will be checked against "target". the "differences" between
116 the two trees will be compiled. note that this does not perform any disk
117 access; it merely compares the two trees' current contents.
118 the "differences" list's members will have a primary filename set to
119 the source path and an alternate filename set to the location in the
120 target. the "how_to_compare" value will dictate what aspects of file
121 equality are used. */
123 static bool compare_trees(const directory_tree &source,
124 const basis::astring &source_start, const directory_tree &target,
125 const basis::astring &target_start, filename_list &differences,
126 file_info::file_similarity how_to_compare);
127 // compares the trees but not at their roots. the location on the source
128 // side is specified by "source_start", which must be a path found under
129 // the "source" tree. similarly, the "target_start" will be the location
130 // compared with the "source" + "source_start". the "diffs" will still
131 // be valid with respect to "source" rather than "source_start".
133 void text_form(basis::astring &tree_dump, bool show_files = true);
134 //!< provides a visual representation of the tree in "tree_dump".
135 /*!< if "show_files" is not true, then only the directories will be
138 // Note on the iterator functions: the iterator becomes invalid if the
139 // directory tree is reset. the only valid operation on the iterator
140 // at that point is to call throw_out().
142 enum traversal_types {
143 prefix, //!< prefix means that subnodes are processed after their parent.
144 infix, //!< infix (for binary trees) goes 1) left, 2) current, 3) right.
145 postfix //!< postfix means that subnodes are traversed first (depth first).
148 dir_tree_iterator *start(traversal_types type) const;
149 //!< starts an iterator on the directory tree.
151 dir_tree_iterator *start_at(filename_tree *start,
152 traversal_types type) const;
153 //!< starts the iterator at a specific "start" node.
155 static bool jump_to(dir_tree_iterator &scanning, const basis::astring &sub_path);
156 //!< seeks to a "sub_path" below the iterator's current position.
157 /*!< tries to take the iterator "scanning" down to a "sub_path" that is
158 underneath its current position. true is returned on success. */
160 static bool current_dir(dir_tree_iterator &scanning, filename &dir_name);
161 //!< sets "dir_name" to the directory name at the "scanning" location.
163 static bool current(dir_tree_iterator &scanning, filename &dir_name,
164 structures::string_array &to_fill);
165 //!< retrieves the information for the iterator's current location.
166 /*!< fills the "to_fill" array with filenames that are found at the
167 "scanning" iterator's current position in the tree. the "dir_name"
168 for that location is also set. if the iterator has ended, then false
171 static bool current(dir_tree_iterator &scanning, filename &dir_name,
172 filename_list &to_fill);
173 //!< similar to the above but provides a list of the real underlying type.
175 static filename_list *access(dir_tree_iterator &scanning);
176 //!< more dangerous operation that lets the actual list be manipulated.
177 /*!< NULL_POINTER is returned if there was a problem accessing the tree
178 at the iterator position. */
180 static bool depth(dir_tree_iterator &scanning, int &depth);
181 //!< returns the current depth of the iterator.
182 /*!< a depth of zero means the iterator is at the root node for the tree. */
184 static bool children(dir_tree_iterator &scanning, int &children);
185 //!< returns the number of children for the current node.
187 static bool next(dir_tree_iterator &scanning);
188 //!< goes to the next filename in the "scanning" iterator.
189 /*!< true is returned if there is an entry there. */
191 static void throw_out(dir_tree_iterator * &to_whack);
192 //!< cleans up an iterator that was previously opened with start().
195 bool _scanned_okay; //!< did this directory work out?
196 basis::astring *_path; //!< the directory we're looking at.
197 basis::astring *_pattern; //!< the pattern used to find the files.
198 filename_tree *_real_tree; //!< the tree of directory contents we build.
199 bool _ignore_files; //!< true if they don't care about the files.
200 fname_tree_creator *_creator; //!< creates blank trees during unpacking.
202 static filename_tree *goto_current(dir_tree_iterator &scanning);
203 //!< goes to the current node for "scanning" and returns the tree there.
204 /*!< if there are no nodes left, NULL_POINTER is returned. */
206 void traverse(const basis::astring &path, const char *pattern,
207 filename_tree &add_to);
208 //!< recursively adds a "path" given the filename "pattern".
209 /*!< assuming that we want to add the files at "path" using the "pattern"
210 into the current node "add_to", we will also scoot down all sub-dirs
211 and recursively invoke traverse() to add those also. */
213 basis::outcome find_common_root(const basis::astring &path, bool exists,
214 filename_tree * &common_root, basis::astring &common_path,
215 structures::string_array &pieces, int &match_place);
216 //!< locates the node where this tree and "path" have membership in common.
217 /*!< if "exists" is true, then the "path" is tested for existence and
218 otherwise it's assumed that the path no longer exists. the "common_root"
219 is the last node that's in both places, the "common_path" is the name of
220 that location, the list of "pieces" is "path" broken into its components,
221 and the "match_place" is the index in "pieces" of the common node. */