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