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