4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 // Stefan Bund <g0dil@berlios.de>
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.
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.
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.
24 \brief Executor non-inline non-template implementation */
26 #include "Executor.hh"
27 //#include "Executor.ih"
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"
40 //#include "Executor.mpp"
42 ///////////////////////////////cc.p////////////////////////////////////////
46 struct TraversTokens {
47 typedef std::string const & result_type;
48 result_type operator()(senf::console::Token const & token) const {
55 ///////////////////////////////////////////////////////////////////////////
56 // senf::console::Executor
58 prefix_ senf::console::DirectoryNode & senf::console::Executor::cwd()
61 SENF_ASSERT( ! cwd_.empty() );
62 while (cwd_.size()>1 && (cwd_.back().expired() || ! cwd_.back().lock()->active()))
64 return * cwd_.back().lock();
67 prefix_ std::string senf::console::Executor::cwdPath()
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))),
80 prefix_ void senf::console::Executor::execute(std::ostream & output,
81 ParseCommandInfo const & command)
83 SENF_LOG(( "Executing: " << command ));
86 (void) cwd(); // Prune the cwd path of expired entries
89 switch(command.builtin()) {
90 case ParseCommandInfo::NoBuiltin :
93 exec(output, command);
96 case ParseCommandInfo::BuiltinCD :
100 // The parser ensures, we have exactly one argument
101 cd(command.commandPath());
103 catch (IgnoreCommandException &) {
104 throw SyntaxErrorException(
105 "'cd' cannot be skipped (don't use 'cd' in conf-files)");
109 case ParseCommandInfo::BuiltinLS :
112 // The parser ensures, we have either one or no argument
113 ls( output, command.commandPath() );
116 case ParseCommandInfo::BuiltinLR :
119 // The parser ensures, we have either one or no argument
120 lr( output, command.commandPath() );
123 case ParseCommandInfo::BuiltinPUSHD :
124 // The parser ensures, we have exactly one argument
126 pushd(command.commandPath());
128 exec(output, command);
131 case ParseCommandInfo::BuiltinPOPD :
132 // The parser ensures, we have no arguments
136 case ParseCommandInfo::BuiltinEXIT :
139 // The parser ensures, we have no arguments
143 case ParseCommandInfo::BuiltinHELP :
146 // The parser ensures, we have either one or no arguments
147 help( output, command.commandPath() );
152 catch (InvalidPathException & ex) {
153 throw SyntaxErrorException("invalid path") << " '" << ex.path << "'";
155 catch (InvalidDirectoryException & ex) {
156 throw SyntaxErrorException("invalid directory") << " '" << ex.path << "'";
158 catch (InvalidCommandException &) {
159 throw SyntaxErrorException("invalid command");
161 catch (IgnoreCommandException &) {}
164 prefix_ senf::console::GenericNode &
165 senf::console::Executor::getNode(ParseCommandInfo const & command)
167 return traverseNode(command.commandPath());
170 prefix_ void senf::console::Executor::exec(std::ostream & output,
171 ParseCommandInfo const & command)
174 GenericNode & node ( traverseNode(command.commandPath()) );
175 DirectoryNode * dir ( dynamic_cast<DirectoryNode*>(&node) );
177 if (! command.tokens().empty())
178 throw InvalidCommandException();
179 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD)
180 pushd( command.commandPath() );
182 cd(command.commandPath());
185 throw InvalidCommandException();
188 dynamic_cast<CommandNode &>(node)(rv, output, command);
189 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD) {
190 DirectoryNode::ptr rvdir;
192 rvdir = boost::any_cast<DirectoryNode::ptr>(rv);
194 catch (boost::bad_any_cast &) {
195 throw InvalidCommandException();
198 newDir.push_back(rvdir);
199 dirstack_.push_back(Path());
200 dirstack_.back().swap(cwd_);
205 catch (IgnoreCommandException &) {
206 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD) {
207 dirstack_.push_back(Path());
208 dirstack_.back().swap(cwd_);
216 prefix_ void senf::console::Executor::cd(ParseCommandInfo::TokensRange dir)
218 if (dir.size() == 1 && *dir.begin() == WordToken("-")) {
220 (void) cwd(); // Prune any expired items
223 // We need to use a temporary so an error somewhere while traversing the dir does not cause
224 // the current directory to change.
226 traverseDirectory(dir, newDir);
232 prefix_ void senf::console::Executor::ls(std::ostream & output,
233 ParseCommandInfo::TokensRange path)
237 width = senf::console::Client::get(output).width();
239 catch (std::bad_cast &)
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)
253 % ( i->second->isDirectory()
255 : i->second->isLink()
258 % i->second->shorthelp().substr(0,width);
263 typedef std::map<senf::console::DirectoryNode*,std::string> NodesMap;
265 void dolr(std::ostream & output, unsigned width, NodesMap & nodes, std::string const & base,
266 unsigned level, senf::console::DirectoryNode & node)
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) {
276 % ( i->second->isDirectory()
278 : i->second->isLink()
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())
291 nodes.insert(std::make_pair(&subnode, subbase));
292 dolr(output, width, nodes, subbase, level+1, subnode);
294 output << pad << " -> " << j->second << "\n";
301 prefix_ void senf::console::Executor::lr(std::ostream & output,
302 ParseCommandInfo::TokensRange path)
306 width = senf::console::Client::get(output).width();
308 catch (std::bad_cast &)
314 traverseDirectory(path, dir);
315 DirectoryNode & node (*dir.back().lock());
317 dolr(output, width, nodes, "", 0, node);
320 prefix_ void senf::console::Executor::pushd(ParseCommandInfo::TokensRange dir)
325 traverseDirectory(dir, newDir);
327 catch (IgnoreCommandException &) {
331 dirstack_.push_back(Path());
332 dirstack_.back().swap(cwd_);
336 prefix_ void senf::console::Executor::popd()
338 if (! dirstack_.empty()) {
339 cwd_.swap(dirstack_.back());
340 dirstack_.pop_back();
344 prefix_ void senf::console::Executor::exit()
346 throw ExitException();
349 prefix_ void senf::console::Executor::help(std::ostream & output,
350 ParseCommandInfo::TokensRange path)
352 GenericNode const & node (traverseNode(path));
353 // output << prettyName(typeid(node)) << " at " << node.path() << "\n\n";
355 output << std::flush;
358 prefix_ senf::console::GenericNode &
359 senf::console::Executor::traverseNode(ParseCommandInfo::TokensRange const & path)
362 return *cwd_.back().lock();
365 traverseDirectory(boost::make_iterator_range(
367 boost::prior(path.end())),
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("..")) {
374 return *dir.back().lock();
376 DirectoryNode & base (*dir.back().lock());
377 if (tok == WordToken(".") || tok == NoneToken())
379 std::string const & name (complete(base, tok.value()));
381 policy_( base, name );
382 return dir.back().lock()->get(name);
384 catch (UnknownNodeNameException &) {
385 throw InvalidPathException(
387 senf::make_transform_range(path, boost::bind(&Token::value, _1)),
393 senf::console::Executor::traverseDirectory(ParseCommandInfo::TokensRange const & path,
396 std::string errorPath;
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())
403 errorPath += i->value();
404 if (*i == NoneToken()) {
405 if (i == path.begin()) {
407 dir.push_back(root_);
410 else if (*i == WordToken("..")) {
414 else if (*i == WordToken("."))
417 DirectoryNode & base (*dir.back().lock());
418 std::string name (complete(base, i->value()));
420 policy_( base, name );
421 dir.push_back(base[name].thisptr());
425 catch (std::bad_cast &) {
426 throw InvalidDirectoryException(errorPath);
428 catch (UnknownNodeNameException &) {
429 throw InvalidDirectoryException(errorPath);
433 prefix_ std::string senf::console::Executor::complete(DirectoryNode & dir,
434 std::string const & name)
436 if (! dir.hasChild(name)) {
437 DirectoryNode::ChildrenRange completions (dir.completions(name));
438 if (has_one_elt(completions))
439 return completions.begin()->first;
444 prefix_ void senf::console::senf_console_format_value(DirectoryNode::ptr value,
448 os << "<Directory at '" << value->path() << "'>";
450 os << "<Null Directory>";
453 ///////////////////////////////cc.e////////////////////////////////////////
455 //#include "Executor.mpp"
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"