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 <boost/preprocessor/stringize.hpp>
35 #include <senf/Utils/senfassert.hh>
36 #include <senf/Utils/Range.hh>
37 #include <senf/Utils/String.hh>
38 #include <senf/Utils/range.hh>
41 //#include "Executor.mpp"
43 ///////////////////////////////cc.p////////////////////////////////////////
47 struct TraversTokens {
48 typedef std::string const & result_type;
49 result_type operator()(senf::console::Token const & token) const {
56 ///////////////////////////////////////////////////////////////////////////
57 // senf::console::Executor
59 prefix_ senf::console::DirectoryNode & senf::console::Executor::cwd()
62 SENF_ASSERT( ! cwd_.empty() );
63 while (cwd_.size()>1 && (cwd_.back().expired() || ! cwd_.back().lock()->active()))
65 return * cwd_.back().lock();
68 prefix_ std::string senf::console::Executor::cwdPath()
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))),
81 prefix_ void senf::console::Executor::execute(std::ostream & output,
82 ParseCommandInfo const & command)
84 SENF_LOG(( "Executing: " << command ));
87 (void) cwd(); // Prune the cwd path of expired entries
90 switch(command.builtin()) {
91 case ParseCommandInfo::NoBuiltin :
94 exec(output, command);
97 case ParseCommandInfo::BuiltinCD :
101 // The parser ensures, we have exactly one argument
102 cd(command.commandPath());
104 catch (IgnoreCommandException &) {
105 throw SyntaxErrorException(
106 "'cd' cannot be skipped (don't use 'cd' in conf-files)");
110 case ParseCommandInfo::BuiltinLS :
113 // The parser ensures, we have either one or no argument
114 ls( output, command.commandPath() );
117 case ParseCommandInfo::BuiltinLL :
120 // The parser ensures, we have either one or no argument
121 ll( output, command.commandPath() );
124 case ParseCommandInfo::BuiltinLR :
127 // The parser ensures, we have either one or no argument
128 lr( output, command.commandPath() );
131 case ParseCommandInfo::BuiltinPUSHD :
132 // The parser ensures, we have exactly one argument
134 pushd(command.commandPath());
136 exec(output, command);
139 case ParseCommandInfo::BuiltinPOPD :
140 // The parser ensures, we have no arguments
144 case ParseCommandInfo::BuiltinEXIT :
147 // The parser ensures, we have no arguments
151 case ParseCommandInfo::BuiltinHELP :
154 // The parser ensures, we have either one or no arguments
155 help( output, command.commandPath() );
160 catch (InvalidPathException & ex) {
161 throw SyntaxErrorException("invalid path") << " '" << ex.path << "'";
163 catch (InvalidDirectoryException & ex) {
164 throw SyntaxErrorException("invalid directory") << " '" << ex.path << "'";
166 catch (InvalidCommandException &) {
167 throw SyntaxErrorException("invalid command");
169 catch (IgnoreCommandException &) {}
172 prefix_ senf::console::GenericNode &
173 senf::console::Executor::getNode(ParseCommandInfo const & command)
175 return traverseNode(command.commandPath());
178 prefix_ void senf::console::Executor::exec(std::ostream & output,
179 ParseCommandInfo const & command)
182 GenericNode & node ( traverseNode(command.commandPath()) );
183 DirectoryNode * dir ( dynamic_cast<DirectoryNode*>(&node) );
185 if (! command.tokens().empty())
186 throw InvalidCommandException();
187 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD)
188 pushd( command.commandPath() );
190 cd(command.commandPath());
193 throw InvalidCommandException();
196 dynamic_cast<CommandNode &>(node)(rv, output, command);
197 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD) {
198 DirectoryNode::ptr rvdir;
200 rvdir = boost::any_cast<DirectoryNode::ptr>(rv);
202 catch (boost::bad_any_cast &) {
203 throw InvalidCommandException();
206 newDir.push_back(rvdir);
207 dirstack_.push_back(Path());
208 dirstack_.back().swap(cwd_);
213 catch (IgnoreCommandException &) {
214 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD) {
215 dirstack_.push_back(Path());
216 dirstack_.back().swap(cwd_);
224 prefix_ void senf::console::Executor::cd(ParseCommandInfo::TokensRange dir)
226 if (dir.size() == 1 && *dir.begin() == WordToken("-")) {
228 (void) cwd(); // Prune any expired items
231 // We need to use a temporary so an error somewhere while traversing the dir does not cause
232 // the current directory to change.
234 traverseDirectory(dir, newDir);
240 prefix_ void senf::console::Executor::ls(std::ostream & output,
241 ParseCommandInfo::TokensRange path)
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";
252 prefix_ void senf::console::Executor::ll(std::ostream & output,
253 ParseCommandInfo::TokensRange path)
255 # define HELP_COLUMN 28
257 unsigned width (senf::console::Client::getWidth(output, 80u, 60u)-(HELP_COLUMN+1));
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)
267 % ( i->second->isDirectory()
269 : i->second->isLink()
272 % i->second->shorthelp().substr(0,width);
277 # define HELP_COLUMN 40
281 typedef std::map<senf::console::DirectoryNode*,std::string> NodesMap;
283 void dolr(std::ostream & output, unsigned width, NodesMap & nodes, std::string const & base,
284 unsigned level, senf::console::DirectoryNode & node)
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()) {
298 % ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
299 % i->second->shorthelp().substr(0,width);
300 std::string subbase (base);
301 if (! subbase.empty())
304 nodes.insert(std::make_pair(&subnode, subbase));
305 dolr(output, width, nodes, subbase, level+1, subnode);
307 output << pad << i->first
308 << ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
309 << " -> " << j->second << "\n";
313 % ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
314 % i->second->shorthelp().substr(0,width);
321 prefix_ void senf::console::Executor::lr(std::ostream & output,
322 ParseCommandInfo::TokensRange path)
325 traverseDirectory(path, dir);
326 DirectoryNode & node (*dir.back().lock());
328 dolr(output, senf::console::Client::getWidth(output, 80u, 60u)-(HELP_COLUMN+1),
334 prefix_ void senf::console::Executor::pushd(ParseCommandInfo::TokensRange dir)
339 traverseDirectory(dir, newDir);
341 catch (IgnoreCommandException &) {
345 dirstack_.push_back(Path());
346 dirstack_.back().swap(cwd_);
350 prefix_ void senf::console::Executor::popd()
352 if (! dirstack_.empty()) {
353 cwd_.swap(dirstack_.back());
354 dirstack_.pop_back();
358 prefix_ void senf::console::Executor::exit()
360 throw ExitException();
363 prefix_ void senf::console::Executor::help(std::ostream & output,
364 ParseCommandInfo::TokensRange path)
366 GenericNode const & node (traverseNode(path));
367 // output << prettyName(typeid(node)) << " at " << node.path() << "\n\n";
369 output << std::flush;
372 prefix_ senf::console::GenericNode &
373 senf::console::Executor::traverseNode(ParseCommandInfo::TokensRange const & path)
376 return *cwd_.back().lock();
379 traverseDirectory(boost::make_iterator_range(
381 boost::prior(path.end())),
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("..")) {
388 return *dir.back().lock();
390 DirectoryNode & base (*dir.back().lock());
391 if (tok == WordToken(".") || tok == NoneToken())
393 std::string const & name (complete(base, tok.value()));
395 policy_( base, name );
396 return dir.back().lock()->get(name);
398 catch (UnknownNodeNameException &) {
399 throw InvalidPathException(
401 senf::make_transform_range(path, boost::bind(&Token::value, _1)),
407 senf::console::Executor::traverseDirectory(ParseCommandInfo::TokensRange const & path,
410 std::string errorPath;
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())
417 errorPath += i->value();
418 if (*i == NoneToken()) {
419 if (i == path.begin()) {
421 dir.push_back(root_);
424 else if (*i == WordToken("..")) {
428 else if (*i == WordToken("."))
431 DirectoryNode & base (*dir.back().lock());
432 std::string name (complete(base, i->value()));
434 policy_( base, name );
435 dir.push_back(base[name].thisptr());
439 catch (std::bad_cast &) {
440 throw InvalidDirectoryException(errorPath);
442 catch (UnknownNodeNameException &) {
443 throw InvalidDirectoryException(errorPath);
447 prefix_ std::string senf::console::Executor::complete(DirectoryNode & dir,
448 std::string const & name)
450 if (! dir.hasChild(name)) {
451 DirectoryNode::ChildrenRange completions (dir.completions(name));
452 if (has_one_elt(completions))
453 return completions.begin()->first;
458 prefix_ void senf::console::senf_console_format_value(DirectoryNode::ptr value,
462 os << "<Directory at '" << value->path() << "'>";
464 os << "<Null Directory>";
467 ///////////////////////////////cc.e////////////////////////////////////////
469 //#include "Executor.mpp"
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"