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