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