Merge branch 'master' of feistymeow.org:feisty_meow
[feisty_meow.git] / nucleus / applications / utilities / dirtree.cpp
1 /*
2 *  Name   : dirtree
3 *  Author : Chris Koeritz
4 *  Purpose:
5 *    A utility that shows the directory tree specified on the command line.
6
7 * Copyright (c) 2004-$now By Author.  This program is free software; you can
8 * redistribute it and/or modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation; either version 2 of
10 * the License or (at your option) any later version.  This is online at:
11 *     http://www.fsf.org/copyleft/gpl.html
12 * Please send any updates to: fred@gruntose.com
13 */
14
15 #include <application/hoople_main.h>
16 #include <basis/guards.h>
17 #include <filesystem/directory_tree.h>
18 #include <filesystem/filename.h>
19 #include <loggers/console_logger.h>
20 #include <structures/static_memory_gremlin.h>
21 #include <structures/string_array.h>
22 #include <textual/string_manipulation.h>
23
24 using namespace application;
25 using namespace basis;
26 using namespace filesystem;
27 using namespace loggers;
28 using namespace structures;
29 using namespace textual;
30
31 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print)
32
33 class dirtree : public application_shell
34 {
35 public:
36   dirtree() : application_shell() {}
37   DEFINE_CLASS_NAME("dirtree");
38   int execute();
39   int print_instructions_and_exit() {
40     LOG(a_sprintf("\
41 %s: This utility shows the sub-directory structure for a chosen directory.\n\
42 It expects a directory name to be provided on the command line.  If no\n\
43 directory is provided, then the current directory is assumed.  The sub-\n\
44 directories under the chosen directory will be displayed on the console in a\n\
45 stylized textual tree.  If a second parameter is provided, it is taken as a\n\
46 file pattern that causes matching files to be displayed.  Without a pattern,\n\
47 just the directory tree is shown.\n\
48 For example:\n\
49   dirtree\n\
50     => shows the directory structure of the current directory.\n\
51   dirtree udon\n\
52     => shows the structure of directory 'udon'\n\
53   dirtree soba \"*.txt\"\n\
54     => displays all text files and sub-directories of 'soba'\n\
55 ", filename(application::_global_argv[0]).basename().raw().s()));
56     return 23;
57   }
58 };
59
60 astring hier_prefix(int depth, int kids)
61 {
62   astring indent = string_manipulation::indentation( (depth - 1) * 2);
63   if (!depth) return "";
64   else if (!kids) return indent + "|--";
65   else return indent + "+--";
66 }
67
68 int dirtree::execute()
69 {
70   astring path;
71  
72 //hmmm: we really need an abstraction to do some checking if they want --help;
73 //      this comparison way of doing it is lame.
74 astring helpword = astring("--help");
75
76   if (application::_global_argc <= 1) {
77     // plug in our default path if they gave us no parameters.
78         path = ".";
79         } else {
80                 // they gave us some parameters.  but are they asking for help?
81                 if (helpword == astring(application::_global_argv[1])) {
82                         return print_instructions_and_exit();
83                 } else {
84                         // this seems like a serious path request.
85                         path = application::_global_argv[1];
86                 }
87         }
88
89   // check if we should show any of the files.
90   bool show_files = false;
91   astring pattern;
92   if (application::_global_argc >= 3) {
93     pattern = application::_global_argv[2];
94   }
95   if (pattern.t()) {
96     show_files = true;
97   }
98
99 //  log(astring("Scanning directory tree at \"") + path + "\"");
100 //  log(astring("Using pattern-match \"") + pattern + "\"");
101
102   directory_tree dir(path, pattern.s(), !show_files);
103   if (!dir.good()) {
104     continuable_error(class_name(), "tree construction",
105         "the directory could not be read");
106     return print_instructions_and_exit();
107   }
108
109   dir_tree_iterator *ted = dir.start(directory_tree::prefix);
110     // create our iterator to traverse the tree in prefix order.
111
112   filename curr;  // the current path the iterator is at.
113   string_array files;  // the filenames held at the iterator.
114   int depth;  // current depth in tree.
115   int kids;  // number of children below this node.
116
117   while (directory_tree::current(*ted, curr, files)) {
118     // we have a good directory to show.
119     directory_tree::depth(*ted, depth);
120     directory_tree::children(*ted, kids); 
121     astring name_to_log = curr.basename().raw();
122     if (!depth) {
123       name_to_log = curr.raw();
124     }
125     LOG(hier_prefix(depth, kids) + name_to_log);
126     if (show_files) {
127       astring names;
128       for (int i = 0; i < files.length(); i++) names += files[i] + " ";
129       if (names.length()) {
130         astring split;
131         string_manipulation::split_lines(names, split, depth * 2 + 2);
132         // strip eol chars off the string we got back, since we already add that in log.
133         while (parser_bits::is_eol(split[split.end()])) {
134                 split.zap(split.end(), split.end());
135         }
136         LOG(split);
137       }
138     }
139
140     // go to the next place.
141     directory_tree::next(*ted);
142   }
143
144   directory_tree::throw_out(ted);
145   return 0;
146 }
147
148 HOOPLE_MAIN(dirtree, )
149
150 #ifdef __BUILD_STATIC_APPLICATION__
151   // static dependencies found by buildor_gen_deps.sh:
152   #include <application/application_shell.cpp>
153   #include <application/command_line.cpp>
154   #include <application/windoze_helper.cpp>
155   #include <basis/astring.cpp>
156   #include <basis/common_outcomes.cpp>
157   #include <basis/environment.cpp>
158   #include <basis/guards.cpp>
159   #include <basis/mutex.cpp>
160   #include <basis/utf_conversion.cpp>
161   #include <configuration/application_configuration.cpp>
162   #include <configuration/configurator.cpp>
163   #include <configuration/ini_configurator.cpp>
164   #include <configuration/ini_parser.cpp>
165   #include <configuration/table_configurator.cpp>
166   #include <configuration/variable_tokenizer.cpp>
167   #include <filesystem/byte_filer.cpp>
168   #include <filesystem/directory.cpp>
169   #include <filesystem/directory_tree.cpp>
170   #include <filesystem/file_info.cpp>
171   #include <filesystem/file_time.cpp>
172   #include <filesystem/filename.cpp>
173   #include <filesystem/filename_list.cpp>
174   #include <filesystem/filename_tree.cpp>
175   #include <filesystem/huge_file.cpp>
176   #include <loggers/combo_logger.cpp>
177   #include <loggers/console_logger.cpp>
178   #include <loggers/critical_events.cpp>
179   #include <loggers/file_logger.cpp>
180   #include <loggers/program_wide_logger.cpp>
181   #include <nodes/node.cpp>
182   #include <nodes/packable_tree.cpp>
183   #include <nodes/path.cpp>
184   #include <nodes/tree.cpp>
185   #include <structures/bit_vector.cpp>
186   #include <structures/checksums.cpp>
187   #include <structures/object_packers.cpp>
188   #include <structures/static_memory_gremlin.cpp>
189   #include <structures/string_hasher.cpp>
190   #include <structures/string_table.cpp>
191   #include <structures/version_record.cpp>
192   #include <textual/byte_formatter.cpp>
193   #include <textual/parser_bits.cpp>
194   #include <textual/string_manipulation.cpp>
195   #include <timely/earth_time.cpp>
196   #include <timely/time_stamp.cpp>
197 #endif // __BUILD_STATIC_APPLICATION__
198