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