Fix documentation build under maverick (doxygen 1.7.1)
[senf.git] / senf / Utils / Console / Executor.cc
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Executor non-inline non-template implementation */
25
26 #include "Executor.hh"
27 //#include "Executor.ih"
28
29 // Custom includes
30 #include <boost/utility.hpp>
31 #include <boost/range/iterator_range.hpp>
32 #include <boost/bind.hpp>
33 #include <boost/format.hpp>
34 #include <boost/preprocessor/stringize.hpp>
35 #include <senf/Utils/senfassert.hh>
36 #include <senf/Utils/Range.hh>
37 #include "Server.hh"
38 #include "senf/Utils/IgnoreValue.hh"
39
40 //#include "Executor.mpp"
41 #define prefix_
42 //-/////////////////////////////////////////////////////////////////////////////////////////////////
43
44 namespace {
45
46     struct TraversTokens {
47         typedef std::string const & result_type;
48         result_type operator()(senf::console::Token const & token) const {
49             return token.value();
50         }
51     };
52
53 }
54
55 //-/////////////////////////////////////////////////////////////////////////////////////////////////
56 // senf::console::Executor
57
58 prefix_ senf::console::DirectoryNode & senf::console::Executor::cwd()
59     const
60 {
61     SENF_ASSERT( ! cwd_.empty(), "Internal error: CWD history empty ?" );
62     while (cwd_.size()>1 && (cwd_.back().expired() || ! cwd_.back().lock()->active()))
63         cwd_.pop_back();
64     return * cwd_.back().lock();
65 }
66
67 prefix_ std::string senf::console::Executor::cwdPath()
68     const
69 {
70     if (skipping())
71         return "";
72     senf::IGNORE( cwd() ); // ensure, cwd is live.
73     return "/" + senf::stringJoin(
74         senf::make_transform_range(
75             boost::make_iterator_range(boost::next(cwd_.begin()), cwd_.end()),
76             boost::bind(&DirectoryNode::name, boost::bind(&DirectoryNode::weak_ptr::lock, _1))),
77         "/" );
78 }
79
80 prefix_ void senf::console::Executor::execute(std::ostream & output,
81                                               ParseCommandInfo const & command)
82 {
83     SENF_LOG(( "Executing: " << command ));
84
85     if (! skipping())
86         senf::IGNORE( cwd() ); // Prune the cwd path of expired entries
87
88     try {
89         switch(command.builtin()) {
90         case ParseCommandInfo::NoBuiltin :
91             if (skipping())
92                 return;
93             exec(output, command);
94             break;
95
96         case ParseCommandInfo::BuiltinCD :
97             if (skipping())
98                 break;
99             try {
100                 // The parser ensures, we have exactly one argument
101                 cd(command.commandPath());
102             }
103             catch (IgnoreCommandException &) {
104                 throw SyntaxErrorException(
105                     "'cd' cannot be skipped (don't use 'cd' in conf-files)");
106             }
107             break;
108
109         case ParseCommandInfo::BuiltinLS :
110             if (skipping())
111                 break;
112             // The parser ensures, we have either one or no argument
113             ls( output, command.commandPath() );
114             break;
115
116         case ParseCommandInfo::BuiltinLL :
117             if (skipping())
118                 break;
119             // The parser ensures, we have either one or no argument
120             ll( output, command.commandPath() );
121             break;
122
123         case ParseCommandInfo::BuiltinLR :
124             if (skipping())
125                 break;
126             // The parser ensures, we have either one or no argument
127             lr( output, command.commandPath() );
128             break;
129
130         case ParseCommandInfo::BuiltinPUSHD :
131             // The parser ensures, we have exactly one argument
132             if (skipping())
133                 pushd(command.commandPath());
134             else
135                 exec(output, command);
136             break;
137
138         case ParseCommandInfo::BuiltinPOPD :
139             // The parser ensures, we have no arguments
140             popd();
141             break;
142
143         case ParseCommandInfo::BuiltinEXIT :
144             if (skipping())
145                 break;
146             // The parser ensures, we have no arguments
147             exit();
148             break;
149
150         case ParseCommandInfo::BuiltinHELP :
151             if (skipping())
152                 break;
153             // The parser ensures, we have either one or no arguments
154             help( output, command.commandPath() );
155             break;
156
157         }
158     }
159     catch (InvalidPathException & ex) {
160         throw SyntaxErrorException("invalid path") << " '" << ex.path << "'";
161     }
162     catch (InvalidDirectoryException & ex) {
163         throw SyntaxErrorException("invalid directory") << " '" << ex.path << "'";
164     }
165     catch (InvalidCommandException &) {
166         throw SyntaxErrorException("invalid command");
167     }
168     catch (IgnoreCommandException &) {}
169 }
170
171 prefix_ senf::console::GenericNode &
172 senf::console::Executor::getNode(ParseCommandInfo const & command)
173 {
174     try {
175         return traverseNode(command.commandPath());
176     }
177     catch (InvalidPathException & ex) {
178         throw SyntaxErrorException("invalid path") << " '" << ex.path << "'";
179     }
180     catch (InvalidDirectoryException & ex) {
181         throw SyntaxErrorException("invalid directory") << " '" << ex.path << "'";
182     }
183 }
184
185 prefix_ void senf::console::Executor::exec(std::ostream & output,
186                                            ParseCommandInfo const & command)
187 {
188     try {
189         GenericNode & node ( traverseNode(command.commandPath()) );
190         DirectoryNode * dir ( dynamic_cast<DirectoryNode*>(&node) );
191         if ( dir ) {
192             if (! command.tokens().empty())
193                 throw InvalidCommandException();
194             if (command.builtin() == ParseCommandInfo::BuiltinPUSHD)
195                 pushd( command.commandPath() );
196             else if (autocd_) {
197                 cd(command.commandPath());
198             }
199             else
200                 throw InvalidCommandException();
201         } else {
202             boost::any rv;
203             dynamic_cast<CommandNode &>(node)(rv, output, command);
204             if (command.builtin() == ParseCommandInfo::BuiltinPUSHD) {
205                 DirectoryNode::ptr rvdir;
206                 try {
207                     rvdir = boost::any_cast<DirectoryNode::ptr>(rv);
208                 }
209                 catch (boost::bad_any_cast &) {
210                     throw InvalidCommandException();
211                 }
212                 Path newDir (cwd_);
213                 newDir.push_back(rvdir);
214                 dirstack_.push_back(Path());
215                 dirstack_.back().swap(cwd_);
216                 cwd_.swap(newDir);
217             }
218         }
219     }
220     catch (IgnoreCommandException &) {
221         if (command.builtin() == ParseCommandInfo::BuiltinPUSHD) {
222             dirstack_.push_back(Path());
223             dirstack_.back().swap(cwd_);
224         }
225         else
226             throw;
227     }
228 }
229
230
231 prefix_ void senf::console::Executor::cd(ParseCommandInfo::TokensRange dir)
232 {
233     if (dir.size() == 1 && *dir.begin() == WordToken("-")) {
234         cwd_.swap(oldCwd_);
235         senf::IGNORE( cwd() ); // Prune any expired items
236     }
237     else {
238         // We need to use a temporary so an error somewhere while traversing the dir does not cause
239         // the current directory to change.
240         Path newDir (cwd_);
241         traverseDirectory(dir, newDir);
242         oldCwd_.swap(cwd_);
243         cwd_.swap(newDir);
244     }
245 }
246
247 prefix_ void senf::console::Executor::ls(std::ostream & output,
248                                          ParseCommandInfo::TokensRange path)
249 {
250     Path dir (cwd_);
251     traverseDirectory(path, dir);
252     DirectoryNode & node (*dir.back().lock());
253     DirectoryNode::child_iterator i (node.children().begin());
254     DirectoryNode::child_iterator const i_end (node.children().end());
255     for (; i != i_end; ++i)
256         output << i->first << "\n";
257 }
258
259 prefix_ void senf::console::Executor::ll(std::ostream & output,
260                                          ParseCommandInfo::TokensRange path)
261 {
262 #   define HELP_COLUMN 28
263
264     unsigned width (senf::console::Client::getWidth(output, 80u, 60u)-(HELP_COLUMN+1));
265     Path dir (cwd_);
266     traverseDirectory(path, dir);
267     DirectoryNode & node (*dir.back().lock());
268     DirectoryNode::child_iterator i (node.children().begin());
269     DirectoryNode::child_iterator const i_end (node.children().end());
270     boost::format fmt ("%s%s  %|" BOOST_PP_STRINGIZE(HELP_COLUMN) "t|%s\n");
271     for (; i != i_end; ++i)
272         output << fmt
273             % i->first
274             % ( i->second->isDirectory()
275                 ? "/"
276                 : i->second->isLink()
277                 ? "@"
278                 : "" )
279             % i->second->shorthelp().substr(0,width);
280
281 #   undef HELP_COLUMN
282 }
283
284 #   define HELP_COLUMN 40
285
286 namespace {
287
288     typedef std::map<senf::console::DirectoryNode*,std::string> NodesMap;
289
290     void dolr(std::ostream & output, unsigned width, NodesMap & nodes, std::string const & base,
291               unsigned level, senf::console::DirectoryNode & node)
292     {
293         boost::format fmt ("%s%s%s  %|" BOOST_PP_STRINGIZE(HELP_COLUMN) "t|%s\n");
294         std::string pad (2*level, ' ');
295         senf::console::DirectoryNode::child_iterator i (node.children().begin());
296         senf::console::DirectoryNode::child_iterator const i_end (node.children().end());
297         for (; i != i_end; ++i) {
298             if (i->second->followLink().isDirectory()) {
299                 senf::console::DirectoryNode & subnode (
300                     static_cast<senf::console::DirectoryNode&>(i->second->followLink()));
301                 NodesMap::iterator j (nodes.find(&subnode));
302                 if (j == nodes.end()) {
303                     output << fmt
304                         % pad % i->first
305                         % ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
306                         % i->second->shorthelp().substr(0,width);
307                     std::string subbase (base);
308                     if (! subbase.empty())
309                         subbase += "/";
310                     subbase += i->first;
311                     nodes.insert(std::make_pair(&subnode, subbase));
312                     dolr(output, width, nodes, subbase, level+1, subnode);
313                 } else
314                     output << pad << i->first
315                            << ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
316                            << " -> " << j->second << "\n";
317             } else {
318                 output << fmt
319                     % pad % i->first
320                     % ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
321                     % i->second->shorthelp().substr(0,width);
322             }
323         }
324     }
325
326 }
327
328 prefix_ void senf::console::Executor::lr(std::ostream & output,
329                                          ParseCommandInfo::TokensRange path)
330 {
331     Path dir (cwd_);
332     traverseDirectory(path, dir);
333     DirectoryNode & node (*dir.back().lock());
334     NodesMap nodes;
335     dolr(output, senf::console::Client::getWidth(output, 80u, 60u)-(HELP_COLUMN+1),
336          nodes, "", 0, node);
337 }
338
339 #undef HELP_COLUMN
340
341 prefix_ void senf::console::Executor::pushd(ParseCommandInfo::TokensRange dir)
342 {
343     Path newDir (cwd_);
344     if (! skipping()) {
345         try {
346             traverseDirectory(dir, newDir);
347         }
348         catch (IgnoreCommandException &) {
349             newDir.clear();
350         }
351     }
352     dirstack_.push_back(Path());
353     dirstack_.back().swap(cwd_);
354     cwd_.swap(newDir);
355 }
356
357 prefix_ void senf::console::Executor::popd()
358 {
359     if (! dirstack_.empty()) {
360         cwd_.swap(dirstack_.back());
361         dirstack_.pop_back();
362     }
363 }
364
365 prefix_ void senf::console::Executor::exit()
366 {
367     throw ExitException();
368 }
369
370 prefix_ void senf::console::Executor::help(std::ostream & output,
371                                            ParseCommandInfo::TokensRange path)
372 {
373     GenericNode const & node (traverseNode(path));
374     // output << prettyName(typeid(node)) << " at " << node.path() << "\n\n";
375     node.help(output);
376     output << std::flush;
377 }
378
379 prefix_ senf::console::GenericNode &
380 senf::console::Executor::traverseNode(ParseCommandInfo::TokensRange const & path)
381 {
382     if (path.empty())
383         return *cwd_.back().lock();
384     try {
385         Path dir (cwd_);
386         traverseDirectory(boost::make_iterator_range(
387                               path.begin(),
388                               boost::prior(path.end())),
389                           dir);
390         // For auto-cd support we need to check against '.' and '..' here too
391         Token const & tok (*boost::prior(path.end()));
392         if (tok == WordToken("..")) {
393             if (dir.size() > 1)
394                 dir.pop_back();
395             return *dir.back().lock();
396         }
397         DirectoryNode & base (*dir.back().lock());
398         if (tok == WordToken(".") || tok == NoneToken())
399             return base;
400         std::string const & name (complete(base, tok.value()));
401         if (policy_)
402             policy_( base, name );
403         return dir.back().lock()->get(name);
404     }
405     catch (UnknownNodeNameException &) {
406         throw InvalidPathException(
407             senf::stringJoin(
408                 senf::make_transform_range(path, boost::bind(&Token::value, _1)),
409                 "/"));
410     }
411 }
412
413 prefix_ void
414 senf::console::Executor::traverseDirectory(ParseCommandInfo::TokensRange const & path,
415                                            Path & dir)
416 {
417     std::string errorPath;
418     try {
419         ParseCommandInfo::TokensRange::const_iterator i (path.begin());
420         ParseCommandInfo::TokensRange::const_iterator const i_end (path.end());
421         for (; i != i_end; ++i) {
422             if (i != path.begin())
423                 errorPath += "/";
424             errorPath += i->value();
425             if (*i == NoneToken()) {
426                 if (i == path.begin()) {
427                     dir.clear();
428                     dir.push_back(root_);
429                 }
430             }
431             else if (*i == WordToken("..")) {
432                 if (dir.size() > 1)
433                     dir.pop_back();
434             }
435             else if (*i ==  WordToken("."))
436                 ;
437             else {
438                 DirectoryNode & base (*dir.back().lock());
439                 std::string name (complete(base, i->value()));
440                 if (policy_)
441                     policy_( base, name );
442                 dir.push_back(base[name].thisptr());
443             }
444         }
445     }
446     catch (std::bad_cast &) {
447         throw InvalidDirectoryException(errorPath);
448     }
449     catch (UnknownNodeNameException &) {
450         throw InvalidDirectoryException(errorPath);
451     }
452 }
453
454 prefix_ std::string senf::console::Executor::complete(DirectoryNode & dir,
455                                                       std::string const & name)
456 {
457     if (! dir.hasChild(name)) {
458         DirectoryNode::ChildrenRange completions (dir.completions(name));
459         if (has_one_elt(completions))
460             return completions.begin()->first;
461     }
462     return name;
463 }
464
465 prefix_ void senf::console::senf_console_format_value(DirectoryNode::ptr value,
466                                                       std::ostream & os)
467 {
468     if (value)
469         os << "<Directory at '" << value->path() << "'>";
470     else
471         os << "<Null Directory>";
472 }
473
474 //-/////////////////////////////////////////////////////////////////////////////////////////////////
475 #undef prefix_
476 //#include "Executor.mpp"
477
478 \f
479 // Local Variables:
480 // mode: c++
481 // fill-column: 100
482 // comment-column: 40
483 // c-file-style: "senf"
484 // indent-tabs-mode: nil
485 // ispell-local-dictionary: "american"
486 // compile-command: "scons -u test"
487 // End: