4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at
9 // http://senf.berlios.de/license.html
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on,
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
15 // Software distributed under the License is distributed on an "AS IS" basis,
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
17 // for the specific language governing rights and limitations under the License.
19 // The Original Code is Fraunhofer FOKUS code.
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V.
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
26 // Stefan Bund <g0dil@berlios.de>
29 \brief Executor non-inline non-template implementation */
31 #include "Executor.hh"
32 //#include "Executor.ih"
35 #include <boost/utility.hpp>
36 #include <boost/range/iterator_range.hpp>
37 #include <boost/bind.hpp>
38 #include <boost/format.hpp>
39 #include <boost/preprocessor/stringize.hpp>
40 #include <senf/Utils/senfassert.hh>
41 #include <senf/Utils/Range.hh>
42 #include "senf/Utils/IgnoreValue.hh"
45 //#include "Executor.mpp"
47 //-/////////////////////////////////////////////////////////////////////////////////////////////////
51 struct TraversTokens {
52 typedef std::string const & result_type;
53 result_type operator()(senf::console::Token const & token) const {
60 //-/////////////////////////////////////////////////////////////////////////////////////////////////
61 // senf::console::Executor
63 prefix_ senf::console::DirectoryNode & senf::console::Executor::cwd()
66 SENF_ASSERT( ! cwd_.empty(), "Internal error: CWD history empty ?" );
67 while (cwd_.size()>1 && (cwd_.back().expired() || ! cwd_.back().lock()->active()))
69 return * cwd_.back().lock();
72 prefix_ std::string senf::console::Executor::cwdPath()
77 senf::IGNORE( cwd() ); // ensure, cwd is live.
78 return "/" + senf::stringJoin(
79 senf::make_transform_range(
80 boost::make_iterator_range(boost::next(cwd_.begin()), cwd_.end()),
81 boost::bind(&DirectoryNode::name, boost::bind(&DirectoryNode::weak_ptr::lock, _1))),
85 prefix_ void senf::console::Executor::execute(std::ostream & output,
86 ParseCommandInfo const & command)
88 SENF_LOG(( "Executing: " << command ));
91 senf::IGNORE( cwd() ); // Prune the cwd path of expired entries
94 switch(command.builtin()) {
95 case ParseCommandInfo::NoBuiltin :
98 exec(output, command);
101 case ParseCommandInfo::BuiltinCD :
105 // The parser ensures, we have exactly one argument
106 cd(command.commandPath());
108 catch (IgnoreCommandException &) {
109 throw SyntaxErrorException(
110 "'cd' cannot be skipped (don't use 'cd' in conf-files)");
114 case ParseCommandInfo::BuiltinLS :
117 // The parser ensures, we have either one or no argument
118 ls( output, command.commandPath() );
121 case ParseCommandInfo::BuiltinLL :
124 // The parser ensures, we have either one or no argument
125 ll( output, command.commandPath() );
128 case ParseCommandInfo::BuiltinLR :
131 // The parser ensures, we have either one or no argument
132 lr( output, command.commandPath() );
135 case ParseCommandInfo::BuiltinPUSHD :
136 // The parser ensures, we have exactly one argument
138 pushd(command.commandPath());
140 exec(output, command);
143 case ParseCommandInfo::BuiltinPOPD :
144 // The parser ensures, we have no arguments
148 case ParseCommandInfo::BuiltinEXIT :
151 // The parser ensures, we have no arguments
155 case ParseCommandInfo::BuiltinHELP :
158 // The parser ensures, we have either one or no arguments
159 help( output, command.commandPath() );
164 catch (InvalidPathException & ex) {
165 throw SyntaxErrorException("invalid path") << " '" << ex.path << "'";
167 catch (InvalidDirectoryException & ex) {
168 throw SyntaxErrorException("invalid directory") << " '" << ex.path << "'";
170 catch (InvalidCommandException &) {
171 throw SyntaxErrorException("invalid command");
173 catch (IgnoreCommandException &) {}
176 prefix_ senf::console::GenericNode &
177 senf::console::Executor::getNode(ParseCommandInfo const & command)
180 return traverseNode(command.commandPath());
182 catch (InvalidPathException & ex) {
183 throw SyntaxErrorException("invalid path") << " '" << ex.path << "'";
185 catch (InvalidDirectoryException & ex) {
186 throw SyntaxErrorException("invalid directory") << " '" << ex.path << "'";
190 prefix_ void senf::console::Executor::exec(std::ostream & output,
191 ParseCommandInfo const & command)
194 GenericNode & node ( traverseNode(command.commandPath()) );
195 DirectoryNode * dir ( dynamic_cast<DirectoryNode*>(&node) );
197 if (! command.tokens().empty())
198 throw InvalidCommandException();
199 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD)
200 pushd( command.commandPath() );
202 cd(command.commandPath());
205 throw InvalidCommandException();
208 dynamic_cast<CommandNode &>(node)(rv, output, command);
209 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD) {
210 DirectoryNode::ptr rvdir;
212 rvdir = boost::any_cast<DirectoryNode::ptr>(rv);
214 catch (boost::bad_any_cast &) {
215 throw InvalidCommandException();
218 newDir.push_back(rvdir);
219 dirstack_.push_back(Path());
220 dirstack_.back().swap(cwd_);
225 catch (IgnoreCommandException &) {
226 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD) {
227 dirstack_.push_back(Path());
228 dirstack_.back().swap(cwd_);
236 prefix_ void senf::console::Executor::cd(ParseCommandInfo::TokensRange dir)
238 if (dir.size() == 1 && *dir.begin() == WordToken("-")) {
240 senf::IGNORE( cwd() ); // Prune any expired items
243 // We need to use a temporary so an error somewhere while traversing the dir does not cause
244 // the current directory to change.
246 traverseDirectory(dir, newDir);
252 prefix_ void senf::console::Executor::ls(std::ostream & output,
253 ParseCommandInfo::TokensRange path)
256 traverseDirectory(path, dir);
257 DirectoryNode & node (*dir.back().lock());
258 DirectoryNode::child_iterator i (node.children().begin());
259 DirectoryNode::child_iterator const i_end (node.children().end());
260 for (; i != i_end; ++i)
261 output << i->first << "\n";
264 prefix_ void senf::console::Executor::ll(std::ostream & output,
265 ParseCommandInfo::TokensRange path)
267 # define HELP_COLUMN 28
269 unsigned width (Client::getWidth(output, 80u, 60u)-(HELP_COLUMN+1));
271 traverseDirectory(path, dir);
272 DirectoryNode & node (*dir.back().lock());
273 DirectoryNode::child_iterator i (node.children().begin());
274 DirectoryNode::child_iterator const i_end (node.children().end());
275 boost::format fmt ("%s%s %|" BOOST_PP_STRINGIZE(HELP_COLUMN) "t|%s\n");
276 for (; i != i_end; ++i)
279 % ( i->second->isDirectory()
281 : i->second->isLink()
284 % i->second->shorthelp().substr(0,width);
289 # define HELP_COLUMN 40
293 typedef std::map<senf::console::DirectoryNode*,std::string> NodesMap;
295 void dolr(std::ostream & output, unsigned width, NodesMap & nodes, std::string const & base,
296 unsigned level, senf::console::DirectoryNode & node)
298 boost::format fmt ("%s%s%s %|" BOOST_PP_STRINGIZE(HELP_COLUMN) "t|%s\n");
299 std::string pad (2*level, ' ');
300 senf::console::DirectoryNode::child_iterator i (node.children().begin());
301 senf::console::DirectoryNode::child_iterator const i_end (node.children().end());
302 for (; i != i_end; ++i) {
303 if (i->second->followLink().isDirectory()) {
304 senf::console::DirectoryNode & subnode (
305 static_cast<senf::console::DirectoryNode&>(i->second->followLink()));
306 NodesMap::iterator j (nodes.find(&subnode));
307 if (j == nodes.end()) {
310 % ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
311 % i->second->shorthelp().substr(0,width);
312 std::string subbase (base);
313 if (! subbase.empty())
316 nodes.insert(std::make_pair(&subnode, subbase));
317 dolr(output, width, nodes, subbase, level+1, subnode);
319 output << pad << i->first
320 << ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
321 << " -> " << j->second << "\n";
325 % ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
326 % i->second->shorthelp().substr(0,width);
333 prefix_ void senf::console::Executor::lr(std::ostream & output,
334 ParseCommandInfo::TokensRange path)
337 traverseDirectory(path, dir);
338 DirectoryNode & node (*dir.back().lock());
340 dolr(output, Client::getWidth(output, 80u, 60u)-(HELP_COLUMN+1),
346 prefix_ void senf::console::Executor::pushd(ParseCommandInfo::TokensRange dir)
351 traverseDirectory(dir, newDir);
353 catch (IgnoreCommandException &) {
357 dirstack_.push_back(Path());
358 dirstack_.back().swap(cwd_);
362 prefix_ void senf::console::Executor::popd()
364 if (! dirstack_.empty()) {
365 cwd_.swap(dirstack_.back());
366 dirstack_.pop_back();
370 prefix_ void senf::console::Executor::exit()
372 throw ExitException();
375 prefix_ void senf::console::Executor::help(std::ostream & output,
376 ParseCommandInfo::TokensRange path)
378 GenericNode const & node (traverseNode(path));
379 // output << prettyName(typeid(node)) << " at " << node.path() << "\n\n";
381 output << std::flush;
384 prefix_ senf::console::GenericNode &
385 senf::console::Executor::traverseNode(ParseCommandInfo::TokensRange const & path)
388 return *cwd_.back().lock();
391 traverseDirectory(boost::make_iterator_range(
393 boost::prior(path.end())),
395 // For auto-cd support we need to check against '.' and '..' here too
396 Token const & tok (*boost::prior(path.end()));
397 if (tok == WordToken("..")) {
400 return *dir.back().lock();
402 DirectoryNode & base (*dir.back().lock());
403 if (tok == WordToken(".") || tok == NoneToken())
405 std::string const & name (complete(base, tok.value()));
407 policy_( base, name );
408 return dir.back().lock()->get(name);
410 catch (UnknownNodeNameException &) {
411 throw InvalidPathException(
413 senf::make_transform_range(path, boost::bind(&Token::value, _1)),
419 senf::console::Executor::traverseDirectory(ParseCommandInfo::TokensRange const & path,
422 std::string errorPath;
424 ParseCommandInfo::TokensRange::const_iterator i (path.begin());
425 ParseCommandInfo::TokensRange::const_iterator const i_end (path.end());
426 for (; i != i_end; ++i) {
427 if (i != path.begin())
429 errorPath += i->value();
430 if (*i == NoneToken()) {
431 if (i == path.begin()) {
433 dir.push_back(root_);
436 else if (*i == WordToken("..")) {
440 else if (*i == WordToken("."))
443 DirectoryNode & base (*dir.back().lock());
444 std::string name (complete(base, i->value()));
446 policy_( base, name );
447 dir.push_back(base[name].thisptr());
451 catch (std::bad_cast &) {
452 throw InvalidDirectoryException(errorPath);
454 catch (UnknownNodeNameException &) {
455 throw InvalidDirectoryException(errorPath);
459 prefix_ std::string senf::console::Executor::complete(DirectoryNode & dir,
460 std::string const & name)
462 if (! dir.hasChild(name)) {
463 DirectoryNode::ChildrenRange completions (dir.completions(name));
464 if (has_one_elt(completions))
465 return completions.begin()->first;
470 prefix_ void senf::console::senf_console_format_value(DirectoryNode::ptr value,
474 os << "<Directory at '" << value->path() << "'>";
476 os << "<Null Directory>";
479 //-/////////////////////////////////////////////////////////////////////////////////////////////////
481 //#include "Executor.mpp"
487 // comment-column: 40
488 // c-file-style: "senf"
489 // indent-tabs-mode: nil
490 // ispell-local-dictionary: "american"
491 // compile-command: "scons -u test"