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 static bool compare_trees(const directory_tree &source,
108 const directory_tree &target, filename_list &differences,
109 file_info::file_similarity how_to_compare);
110 //!< compares the tree in "source" with the tree in "target".
111 /*!< the two root names may be different, but everything below the root
112 in "source" will be checked against "target". the "differences" between
113 the two trees will be compiled. note that this does not perform any disk
114 access; it merely compares the two trees' current contents.
115 the "differences" list's members will have a primary filename set to
116 the source path and an alternate filename set to the location in the
117 target. the "how_to_compare" value will dictate what aspects of file
118 equality are used. */
120 static bool compare_trees(const directory_tree &source,
121 const basis::astring &source_start, const directory_tree &target,
122 const basis::astring &target_start, filename_list &differences,
123 file_info::file_similarity how_to_compare);
124 // compares the trees but not at their roots. the location on the source
125 // side is specified by "source_start", which must be a path found under
126 // the "source" tree. similarly, the "target_start" will be the location
127 // compared with the "source" + "source_start". the "diffs" will still
128 // be valid with respect to "source" rather than "source_start".
130 void text_form(basis::astring &tree_dump, bool show_files = true);
131 //!< provides a visual representation of the tree in "tree_dump".
132 /*!< if "show_files" is not true, then only the directories will be
135 // Note on the iterator functions: the iterator becomes invalid if the
136 // directory tree is reset. the only valid operation on the iterator
137 // at that point is to call throw_out().
139 enum traversal_types {
140 prefix, //!< prefix means that subnodes are processed after their parent.
141 infix, //!< infix (for binary trees) goes 1) left, 2) current, 3) right.
142 postfix //!< postfix means that subnodes are traversed first (depth first).
145 dir_tree_iterator *start(traversal_types type) const;
146 //!< starts an iterator on the directory tree.
148 dir_tree_iterator *start_at(filename_tree *start,
149 traversal_types type) const;
150 //!< starts the iterator at a specific "start" node.
152 static bool jump_to(dir_tree_iterator &scanning, const basis::astring &sub_path);
153 //!< seeks to a "sub_path" below the iterator's current position.
154 /*!< tries to take the iterator "scanning" down to a "sub_path" that is
155 underneath its current position. true is returned on success. */
157 static bool current_dir(dir_tree_iterator &scanning, filename &dir_name);
158 //!< sets "dir_name" to the directory name at the "scanning" location.
160 static bool current(dir_tree_iterator &scanning, filename &dir_name,
161 structures::string_array &to_fill);
162 //!< retrieves the information for the iterator's current location.
163 /*!< fills the "to_fill" array with filenames that are found at the
164 "scanning" iterator's current position in the tree. the "dir_name"
165 for that location is also set. if the iterator has ended, then false
168 static bool current(dir_tree_iterator &scanning, filename &dir_name,
169 filename_list &to_fill);
170 //!< similar to the above but provides a list of the real underlying type.
172 static filename_list *access(dir_tree_iterator &scanning);
173 //!< more dangerous operation that lets the actual list be manipulated.
174 /*!< NIL is returned if there was a problem accessing the tree
175 at the iterator position. */
177 static bool depth(dir_tree_iterator &scanning, int &depth);
178 //!< returns the current depth of the iterator.
179 /*!< a depth of zero means the iterator is at the root node for the tree. */
181 static bool children(dir_tree_iterator &scanning, int &children);
182 //!< returns the number of children for the current node.
184 static bool next(dir_tree_iterator &scanning);
185 //!< goes to the next filename in the "scanning" iterator.
186 /*!< true is returned if there is an entry there. */
188 static void throw_out(dir_tree_iterator * &to_whack);
189 //!< cleans up an iterator that was previously opened with start().
192 bool _scanned_okay; //!< did this directory work out?
193 basis::astring *_path; //!< the directory we're looking at.
194 basis::astring *_pattern; //!< the pattern used to find the files.
195 filename_tree *_real_tree; //!< the tree of directory contents we build.
196 bool _ignore_files; //!< true if they don't care about the files.
197 fname_tree_creator *_creator; //!< creates blank trees during unpacking.
199 static filename_tree *goto_current(dir_tree_iterator &scanning);
200 //!< goes to the current node for "scanning" and returns the tree there.
201 /*!< if there are no nodes left, NIL is returned. */
203 void traverse(const basis::astring &path, const char *pattern,
204 filename_tree &add_to);
205 //!< recursively adds a "path" given the filename "pattern".
206 /*!< assuming that we want to add the files at "path" using the "pattern"
207 into the current node "add_to", we will also scoot down all sub-dirs
208 and recursively invoke traverse() to add those also. */
210 basis::outcome find_common_root(const basis::astring &path, bool exists,
211 filename_tree * &common_root, basis::astring &common_path,
212 structures::string_array &pieces, int &match_place);
213 //!< locates the node where this tree and "path" have membership in common.
214 /*!< if "exists" is true, then the "path" is tested for existence and
215 otherwise it's assumed that the path no longer exists. the "common_root"
216 is the last node that's in both places, the "common_path" is the name of
217 that location, the list of "pieces" is "path" broken into its components,
218 and the "match_place" is the index in "pieces" of the common node. */