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