044f10c451d3f9f536f4b601c972f05810225a2a
[senf.git] / senf / Utils / Console / ProgramOptions.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 ProgramOptions non-inline non-template implementation */
25
26 #include "ProgramOptions.hh"
27 #include "ProgramOptions.ih"
28
29 // Custom includes
30 #include <boost/algorithm/string/predicate.hpp>
31 #include <boost/format.hpp>
32 #include <senf/Utils/Range.hh>
33 #include "OverloadedCommand.hh"
34
35 //#include "ProgramOptions.mpp"
36 #define prefix_
37 //-/////////////////////////////////////////////////////////////////////////////////////////////////
38
39 //-/////////////////////////////////////////////////////////////////////////////////////////////////
40 // senf::console::detail::ProgramOptionsSource::NonOptionContainer
41
42 prefix_ senf::console::detail::ProgramOptionsSource::NonOptionContainer::~NonOptionContainer()
43 {}
44
45 //-/////////////////////////////////////////////////////////////////////////////////////////////////
46 // senf::console::detail::ProgramOptionsSource
47
48 prefix_ void senf::console::detail::ProgramOptionsSource::v_parse(RestrictedExecutor & executor)
49 {
50     if (nonOptions_)
51         nonOptions_->clear();
52     if (argc_ <= 1)
53         return;
54     char const ** argp (argv_+1);
55     int n (argc_-1);
56     for (; n; --n, ++argp) {
57         std::string arg (*argp);
58         if (arg == "--") {
59             for (; n; --n, ++argp)
60                 parseNonOption(arg, executor);
61             break;
62         }
63         else if (boost::algorithm::starts_with(arg, std::string("--")) && arg.size() > 2)
64             try {
65                 parseLongOption(arg.substr(2), executor);
66             }
67             catch (senf::ExceptionMixin & e) {
68                 e << "\nwhile parsing command line option: " << arg;
69                 throw;
70             }
71         else if (boost::algorithm::starts_with(arg, std::string("-")) && arg.size() > 1) {
72             for (std::string::size_type i (1); i<arg.size(); ++i) {
73                 char opt (arg[i]);
74                 ShortOptions::iterator j (shortOptions_.find(opt));
75                 if (j == shortOptions_.end())
76                     throw SyntaxErrorException(
77                         (boost::format("invalid short option '%c'") % opt).str());
78                 std::string param;
79                 if (j->second.withArg) {
80                     if (i >= arg.size()-1) {
81                         if (n > 0) {
82                             param = *(++argp);
83                             --n;
84                         }
85                     }
86                     else
87                         param = arg.substr(i+1);
88                     i = arg.size();
89                 }
90                 std::string longOpt (j->second.longOpt);
91                 if (! param.empty() ) {
92                     longOpt += "=";
93                     longOpt += param;
94                 }
95                 if (boost::algorithm::starts_with(longOpt, std::string("--")))
96                     longOpt = longOpt.substr(2);
97                 try {
98                     parseLongOption(longOpt, executor);
99                 }
100                 catch (senf::ExceptionMixin & e) {
101                     e << "\nwhile parsing command line option: -" << opt << ' ' << param;
102                     throw;
103                 }
104             }
105         }
106         else
107             parseNonOption(arg, executor);
108     }
109 }
110
111 prefix_ void
112 senf::console::detail::ProgramOptionsSource::parseLongOption(std::string const & arg,
113                                                              RestrictedExecutor & executor)
114 {
115     std::string::size_type ix (arg.find('='));
116     std::string name (arg.substr(0,ix));
117     std::string value (ix==std::string::npos ? std::string() : arg.substr(ix+1));
118
119     typedef std::vector<Token> Path;
120
121     ParseCommandInfo cmd;
122     Path path;
123
124     DirectoryNode::ptr cwd (executor.root().thisptr());
125     std::string::size_type b (0);
126     while (b < name.size()) {
127         std::string::size_type e (name.size());
128         for (;e != std::string::npos && e > b; e = name.rfind('-', e)) {
129             std::string key (name.substr(b,e-b));
130             if (! cwd->hasChild(key)) {
131                 DirectoryNode::ChildrenRange completions (cwd->completions(key));
132                 if (has_one_elt(completions))
133                     key = completions.begin()->first;
134                 else {
135                     e -= 1;
136                     continue;
137                 }
138             }
139             path.push_back(WordToken(key));
140             if (e < name.size())
141                 cwd = cwd->getDirectory(key).thisptr();
142             b = e+1;
143             e = b+1;
144             break;
145         }
146         if (e == std::string::npos || e <= b) {
147             // This will produce a correct error message later or will skip the node,
148             // if parsing is restricted to a subtree
149             path.push_back(WordToken(name.substr(b)));
150             b = name.size();
151         }
152     }
153
154     cmd.command(path);
155     // Here we check, whether the command
156     // - is an overloaded/parsed command
157     // - with a single overload
158     // - taking only a single argument
159     // - which consists of a single token
160     // If all these conditions are met, we pass the parameter value as a single WordToken
161     // otherwise we parse it using the config parser
162     try {
163         GenericNode const & node (executor.getNode(cmd));
164         OverloadedCommandNode const * cmdnode (dynamic_cast<OverloadedCommandNode const *>(&node));
165         if (cmdnode && cmdnode->overloads().size() == 1) {
166             CommandOverload const & overload (**cmdnode->overloads().begin());
167             if (overload.numArguments() == 1) {
168                 ArgumentDoc argdoc;
169                 argdoc.singleToken = false;
170                 overload.argumentDoc(0, argdoc);
171                 if (argdoc.singleToken) {
172                     cmd.addToken(WordToken(value));
173                     goto execute;
174                 }
175             }
176         } /* else */ {
177             parser_.parseArguments(value, cmd);
178         }
179     execute:
180         executor(executor.stream(), cmd);
181     }
182     catch (Executor::IgnoreCommandException &)
183     {}
184 }
185
186 prefix_ void
187 senf::console::detail::ProgramOptionsSource::parseNonOption(std::string const & arg,
188                                                             RestrictedExecutor & executor)
189 {
190     if (! nonOptions_)
191         throw SyntaxErrorException("invalid non-option argument");
192     nonOptions_->push_back(arg);
193 }
194
195 //-/////////////////////////////////////////////////////////////////////////////////////////////////
196
197 prefix_ void senf::console::parseOptions(int argc, char const ** argv, DirectoryNode & root)
198 {
199     ProgramOptions opts (argc, argv, root);
200     opts.parse();
201 }
202
203 //-/////////////////////////////////////////////////////////////////////////////////////////////////
204 #undef prefix_
205 //#include "ProgramOptions.mpp"
206
207 \f
208 // Local Variables:
209 // mode: c++
210 // fill-column: 100
211 // comment-column: 40
212 // c-file-style: "senf"
213 // indent-tabs-mode: nil
214 // ispell-local-dictionary: "american"
215 // compile-command: "scons -u test"
216 // End: