00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00026 #include "Executor.hh"
00027
00028
00029
00030 #include <boost/utility.hpp>
00031 #include <boost/range/iterator_range.hpp>
00032 #include <boost/bind.hpp>
00033 #include <boost/format.hpp>
00034 #include <boost/preprocessor/stringize.hpp>
00035 #include <senf/Utils/senfassert.hh>
00036 #include <senf/Utils/Range.hh>
00037 #include "senf/Utils/IgnoreValue.hh"
00038 #include "Server.hh"
00039
00040
00041 #define prefix_
00042
00043
00044 namespace {
00045
00046 struct TraversTokens {
00047 typedef std::string const & result_type;
00048 result_type operator()(senf::console::Token const & token) const {
00049 return token.value();
00050 }
00051 };
00052
00053 }
00054
00055
00056
00057
00058 prefix_ senf::console::DirectoryNode & senf::console::Executor::cwd()
00059 const
00060 {
00061 SENF_ASSERT( ! cwd_.empty(), "Internal error: CWD history empty ?" );
00062 while (cwd_.size()>1 && (cwd_.back().expired() || ! cwd_.back().lock()->active()))
00063 cwd_.pop_back();
00064 return * cwd_.back().lock();
00065 }
00066
00067 prefix_ std::string senf::console::Executor::cwdPath()
00068 const
00069 {
00070 if (skipping())
00071 return "";
00072 senf::IGNORE( cwd() );
00073 return "/" + senf::stringJoin(
00074 senf::make_transform_range(
00075 boost::make_iterator_range(boost::next(cwd_.begin()), cwd_.end()),
00076 boost::bind(&DirectoryNode::name, boost::bind(&DirectoryNode::weak_ptr::lock, _1))),
00077 "/" );
00078 }
00079
00080 prefix_ void senf::console::Executor::execute(std::ostream & output,
00081 ParseCommandInfo const & command)
00082 {
00083 SENF_LOG(( "Executing: " << command ));
00084
00085 if (! skipping())
00086 senf::IGNORE( cwd() );
00087
00088 try {
00089 switch(command.builtin()) {
00090 case ParseCommandInfo::NoBuiltin :
00091 if (skipping())
00092 return;
00093 exec(output, command);
00094 break;
00095
00096 case ParseCommandInfo::BuiltinCD :
00097 if (skipping())
00098 break;
00099 try {
00100
00101 cd(command.commandPath());
00102 }
00103 catch (IgnoreCommandException &) {
00104 throw SyntaxErrorException(
00105 "'cd' cannot be skipped (don't use 'cd' in conf-files)");
00106 }
00107 break;
00108
00109 case ParseCommandInfo::BuiltinLS :
00110 if (skipping())
00111 break;
00112
00113 ls( output, command.commandPath() );
00114 break;
00115
00116 case ParseCommandInfo::BuiltinLL :
00117 if (skipping())
00118 break;
00119
00120 ll( output, command.commandPath() );
00121 break;
00122
00123 case ParseCommandInfo::BuiltinLR :
00124 if (skipping())
00125 break;
00126
00127 lr( output, command.commandPath() );
00128 break;
00129
00130 case ParseCommandInfo::BuiltinPUSHD :
00131
00132 if (skipping())
00133 pushd(command.commandPath());
00134 else
00135 exec(output, command);
00136 break;
00137
00138 case ParseCommandInfo::BuiltinPOPD :
00139
00140 popd();
00141 break;
00142
00143 case ParseCommandInfo::BuiltinEXIT :
00144 if (skipping())
00145 break;
00146
00147 exit();
00148 break;
00149
00150 case ParseCommandInfo::BuiltinHELP :
00151 if (skipping())
00152 break;
00153
00154 help( output, command.commandPath() );
00155 break;
00156
00157 }
00158 }
00159 catch (InvalidPathException & ex) {
00160 throw SyntaxErrorException("invalid path") << " '" << ex.path << "'";
00161 }
00162 catch (InvalidDirectoryException & ex) {
00163 throw SyntaxErrorException("invalid directory") << " '" << ex.path << "'";
00164 }
00165 catch (InvalidCommandException &) {
00166 throw SyntaxErrorException("invalid command");
00167 }
00168 catch (IgnoreCommandException &) {}
00169 }
00170
00171 prefix_ senf::console::GenericNode &
00172 senf::console::Executor::getNode(ParseCommandInfo const & command)
00173 {
00174 try {
00175 return traverseNode(command.commandPath());
00176 }
00177 catch (InvalidPathException & ex) {
00178 throw SyntaxErrorException("invalid path") << " '" << ex.path << "'";
00179 }
00180 catch (InvalidDirectoryException & ex) {
00181 throw SyntaxErrorException("invalid directory") << " '" << ex.path << "'";
00182 }
00183 }
00184
00185 prefix_ void senf::console::Executor::exec(std::ostream & output,
00186 ParseCommandInfo const & command)
00187 {
00188 try {
00189 GenericNode & node ( traverseNode(command.commandPath()) );
00190 DirectoryNode * dir ( dynamic_cast<DirectoryNode*>(&node) );
00191 if ( dir ) {
00192 if (! command.tokens().empty())
00193 throw InvalidCommandException();
00194 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD)
00195 pushd( command.commandPath() );
00196 else if (autocd_) {
00197 cd(command.commandPath());
00198 }
00199 else
00200 throw InvalidCommandException();
00201 } else {
00202 boost::any rv;
00203 dynamic_cast<CommandNode &>(node)(rv, output, command);
00204 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD) {
00205 DirectoryNode::ptr rvdir;
00206 try {
00207 rvdir = boost::any_cast<DirectoryNode::ptr>(rv);
00208 }
00209 catch (boost::bad_any_cast &) {
00210 throw InvalidCommandException();
00211 }
00212 Path newDir (cwd_);
00213 newDir.push_back(rvdir);
00214 dirstack_.push_back(Path());
00215 dirstack_.back().swap(cwd_);
00216 cwd_.swap(newDir);
00217 }
00218 }
00219 }
00220 catch (IgnoreCommandException &) {
00221 if (command.builtin() == ParseCommandInfo::BuiltinPUSHD) {
00222 dirstack_.push_back(Path());
00223 dirstack_.back().swap(cwd_);
00224 }
00225 else
00226 throw;
00227 }
00228 }
00229
00230
00231 prefix_ void senf::console::Executor::cd(ParseCommandInfo::TokensRange dir)
00232 {
00233 if (dir.size() == 1 && *dir.begin() == WordToken("-")) {
00234 cwd_.swap(oldCwd_);
00235 senf::IGNORE( cwd() );
00236 }
00237 else {
00238
00239
00240 Path newDir (cwd_);
00241 traverseDirectory(dir, newDir);
00242 oldCwd_.swap(cwd_);
00243 cwd_.swap(newDir);
00244 }
00245 }
00246
00247 prefix_ void senf::console::Executor::ls(std::ostream & output,
00248 ParseCommandInfo::TokensRange path)
00249 {
00250 Path dir (cwd_);
00251 traverseDirectory(path, dir);
00252 DirectoryNode & node (*dir.back().lock());
00253 DirectoryNode::child_iterator i (node.children().begin());
00254 DirectoryNode::child_iterator const i_end (node.children().end());
00255 for (; i != i_end; ++i)
00256 output << i->first << "\n";
00257 }
00258
00259 prefix_ void senf::console::Executor::ll(std::ostream & output,
00260 ParseCommandInfo::TokensRange path)
00261 {
00262 # define HELP_COLUMN 28
00263
00264 unsigned width (Client::getWidth(output, 80u, 60u)-(HELP_COLUMN+1));
00265 Path dir (cwd_);
00266 traverseDirectory(path, dir);
00267 DirectoryNode & node (*dir.back().lock());
00268 DirectoryNode::child_iterator i (node.children().begin());
00269 DirectoryNode::child_iterator const i_end (node.children().end());
00270 boost::format fmt ("%s%s %|" BOOST_PP_STRINGIZE(HELP_COLUMN) "t|%s\n");
00271 for (; i != i_end; ++i)
00272 output << fmt
00273 % i->first
00274 % ( i->second->isDirectory()
00275 ? "/"
00276 : i->second->isLink()
00277 ? "@"
00278 : "" )
00279 % i->second->shorthelp().substr(0,width);
00280
00281 # undef HELP_COLUMN
00282 }
00283
00284 # define HELP_COLUMN 40
00285
00286 namespace {
00287
00288 typedef std::map<senf::console::DirectoryNode*,std::string> NodesMap;
00289
00290 void dolr(std::ostream & output, unsigned width, NodesMap & nodes, std::string const & base,
00291 unsigned level, senf::console::DirectoryNode & node)
00292 {
00293 boost::format fmt ("%s%s%s %|" BOOST_PP_STRINGIZE(HELP_COLUMN) "t|%s\n");
00294 std::string pad (2*level, ' ');
00295 senf::console::DirectoryNode::child_iterator i (node.children().begin());
00296 senf::console::DirectoryNode::child_iterator const i_end (node.children().end());
00297 for (; i != i_end; ++i) {
00298 if (i->second->followLink().isDirectory()) {
00299 senf::console::DirectoryNode & subnode (
00300 static_cast<senf::console::DirectoryNode&>(i->second->followLink()));
00301 NodesMap::iterator j (nodes.find(&subnode));
00302 if (j == nodes.end()) {
00303 output << fmt
00304 % pad % i->first
00305 % ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
00306 % i->second->shorthelp().substr(0,width);
00307 std::string subbase (base);
00308 if (! subbase.empty())
00309 subbase += "/";
00310 subbase += i->first;
00311 nodes.insert(std::make_pair(&subnode, subbase));
00312 dolr(output, width, nodes, subbase, level+1, subnode);
00313 } else
00314 output << pad << i->first
00315 << ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
00316 << " -> " << j->second << "\n";
00317 } else {
00318 output << fmt
00319 % pad % i->first
00320 % ( i->second->isDirectory() ? "/" : i->second->isLink() ? "@" : "" )
00321 % i->second->shorthelp().substr(0,width);
00322 }
00323 }
00324 }
00325
00326 }
00327
00328 prefix_ void senf::console::Executor::lr(std::ostream & output,
00329 ParseCommandInfo::TokensRange path)
00330 {
00331 Path dir (cwd_);
00332 traverseDirectory(path, dir);
00333 DirectoryNode & node (*dir.back().lock());
00334 NodesMap nodes;
00335 dolr(output, Client::getWidth(output, 80u, 60u)-(HELP_COLUMN+1),
00336 nodes, "", 0, node);
00337 }
00338
00339 #undef HELP_COLUMN
00340
00341 prefix_ void senf::console::Executor::pushd(ParseCommandInfo::TokensRange dir)
00342 {
00343 Path newDir (cwd_);
00344 if (! skipping()) {
00345 try {
00346 traverseDirectory(dir, newDir);
00347 }
00348 catch (IgnoreCommandException &) {
00349 newDir.clear();
00350 }
00351 }
00352 dirstack_.push_back(Path());
00353 dirstack_.back().swap(cwd_);
00354 cwd_.swap(newDir);
00355 }
00356
00357 prefix_ void senf::console::Executor::popd()
00358 {
00359 if (! dirstack_.empty()) {
00360 cwd_.swap(dirstack_.back());
00361 dirstack_.pop_back();
00362 }
00363 }
00364
00365 prefix_ void senf::console::Executor::exit()
00366 {
00367 throw ExitException();
00368 }
00369
00370 prefix_ void senf::console::Executor::help(std::ostream & output,
00371 ParseCommandInfo::TokensRange path)
00372 {
00373 GenericNode const & node (traverseNode(path));
00374
00375 node.help(output);
00376 output << std::flush;
00377 }
00378
00379 prefix_ senf::console::GenericNode &
00380 senf::console::Executor::traverseNode(ParseCommandInfo::TokensRange const & path)
00381 {
00382 if (path.empty())
00383 return *cwd_.back().lock();
00384 try {
00385 Path dir (cwd_);
00386 traverseDirectory(boost::make_iterator_range(
00387 path.begin(),
00388 boost::prior(path.end())),
00389 dir);
00390
00391 Token const & tok (*boost::prior(path.end()));
00392 if (tok == WordToken("..")) {
00393 if (dir.size() > 1)
00394 dir.pop_back();
00395 return *dir.back().lock();
00396 }
00397 DirectoryNode & base (*dir.back().lock());
00398 if (tok == WordToken(".") || tok == NoneToken())
00399 return base;
00400 std::string const & name (complete(base, tok.value()));
00401 if (policy_)
00402 policy_( base, name );
00403 return dir.back().lock()->get(name);
00404 }
00405 catch (UnknownNodeNameException &) {
00406 throw InvalidPathException(
00407 senf::stringJoin(
00408 senf::make_transform_range(path, boost::bind(&Token::value, _1)),
00409 "/"));
00410 }
00411 }
00412
00413 prefix_ void
00414 senf::console::Executor::traverseDirectory(ParseCommandInfo::TokensRange const & path,
00415 Path & dir)
00416 {
00417 std::string errorPath;
00418 try {
00419 ParseCommandInfo::TokensRange::const_iterator i (path.begin());
00420 ParseCommandInfo::TokensRange::const_iterator const i_end (path.end());
00421 for (; i != i_end; ++i) {
00422 if (i != path.begin())
00423 errorPath += "/";
00424 errorPath += i->value();
00425 if (*i == NoneToken()) {
00426 if (i == path.begin()) {
00427 dir.clear();
00428 dir.push_back(root_);
00429 }
00430 }
00431 else if (*i == WordToken("..")) {
00432 if (dir.size() > 1)
00433 dir.pop_back();
00434 }
00435 else if (*i == WordToken("."))
00436 ;
00437 else {
00438 DirectoryNode & base (*dir.back().lock());
00439 std::string name (complete(base, i->value()));
00440 if (policy_)
00441 policy_( base, name );
00442 dir.push_back(base[name].thisptr());
00443 }
00444 }
00445 }
00446 catch (std::bad_cast &) {
00447 throw InvalidDirectoryException(errorPath);
00448 }
00449 catch (UnknownNodeNameException &) {
00450 throw InvalidDirectoryException(errorPath);
00451 }
00452 }
00453
00454 prefix_ std::string senf::console::Executor::complete(DirectoryNode & dir,
00455 std::string const & name)
00456 {
00457 if (! dir.hasChild(name)) {
00458 DirectoryNode::ChildrenRange completions (dir.completions(name));
00459 if (has_one_elt(completions))
00460 return completions.begin()->first;
00461 }
00462 return name;
00463 }
00464
00465 prefix_ void senf::console::senf_console_format_value(DirectoryNode::ptr value,
00466 std::ostream & os)
00467 {
00468 if (value)
00469 os << "<Directory at '" << value->path() << "'>";
00470 else
00471 os << "<Null Directory>";
00472 }
00473
00474
00475 #undef prefix_
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487