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