Merge branch 'main' of feistymeow.org:feisty_meow
[feisty_meow.git] / filesystem / directory_tree.h
1 #ifndef DIRECTORY_TREE_CLASS
2 #define DIRECTORY_TREE_CLASS
3
4 /*****************************************************************************\
5 *                                                                             *
6 *  Name   : directory_tree                                                    *
7 *  Author : Chris Koeritz                                                     *
8 *                                                                             *
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 \*****************************************************************************/
17
18 #include "directory.h"
19 #include "file_info.h"
20
21 #include <basis/byte_array.h>
22 #include <basis/contracts.h>
23 #include <basis/outcome.h>
24 #include <structures/string_array.h>
25
26 namespace filesystem {
27
28 // forward declarations.
29 class dir_tree_iterator;
30 class filename;
31 class filename_list;
32 class filename_tree;
33 class fname_tree_creator;
34
35 //! An object that traverses directory trees and provides a view of all files.
36
37 class directory_tree : public virtual basis::packable
38 {
39 public:
40   directory_tree();  //!< constructs an empty tree.
41
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. */
54
55   ~directory_tree();
56
57   DEFINE_CLASS_NAME("directory_tree");
58
59   bool good() const { return _scanned_okay; }
60     //!< returns true if the directory existed and we read its contents.
61
62   const basis::astring &path() const;
63     //!< returns the root of the directory tree that we manage.
64
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. */
70
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. */
77
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.
84
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. */
89
90   bool calculate(filename_tree *start, bool just_size);
91     //!< a calculate method that starts at a specific node rather than the root.
92
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". */
100
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. */
106
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".
109
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. */
122
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".
132
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
136     shown. */
137
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().
141
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).
146   };
147
148   dir_tree_iterator *start(traversal_types type) const;
149     //!< starts an iterator on the directory tree.  
150
151   dir_tree_iterator *start_at(filename_tree *start,
152           traversal_types type) const;
153     //!< starts the iterator at a specific "start" node.
154
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. */
159
160   static bool current_dir(dir_tree_iterator &scanning, filename &dir_name);
161     //!< sets "dir_name" to the directory name at the "scanning" location.
162
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
169     is returned. */
170
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.
174
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. */
179
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. */
183
184   static bool children(dir_tree_iterator &scanning, int &children);
185     //!< returns the number of children for the current node.
186
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. */
190
191   static void throw_out(dir_tree_iterator * &to_whack);
192     //!< cleans up an iterator that was previously opened with start().
193
194 private:
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.
201
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. */
205
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. */
212
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. */
222 };
223
224 } //namespace.
225
226 #endif
227