switch to new MPL based Fraunhofer FOKUS Public License
[senf.git] / senf / Utils / Console / ProgramOptions.cc
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 //
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at 
9 // http://senf.berlios.de/license.html
10 //
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on, 
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
14 //
15 // Software distributed under the License is distributed on an "AS IS" basis, 
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
17 // for the specific language governing rights and limitations under the License.
18 //
19 // The Original Code is Fraunhofer FOKUS code.
20 //
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
24 //
25 // Contributor(s):
26 //   Stefan Bund <g0dil@berlios.de>
27
28 /** \file
29     \brief ProgramOptions non-inline non-template implementation */
30
31 #include "ProgramOptions.hh"
32 #include "ProgramOptions.ih"
33
34 // Custom includes
35 #include <boost/algorithm/string/predicate.hpp>
36 #include <boost/format.hpp>
37 #include <senf/Utils/Range.hh>
38 #include "OverloadedCommand.hh"
39
40 //#include "ProgramOptions.mpp"
41 #define prefix_
42 //-/////////////////////////////////////////////////////////////////////////////////////////////////
43
44 //-/////////////////////////////////////////////////////////////////////////////////////////////////
45 // senf::console::detail::ProgramOptionsSource::NonOptionContainer
46
47 prefix_ senf::console::detail::ProgramOptionsSource::NonOptionContainer::~NonOptionContainer()
48 {}
49
50 //-/////////////////////////////////////////////////////////////////////////////////////////////////
51 // senf::console::detail::ProgramOptionsSource
52
53 prefix_ void senf::console::detail::ProgramOptionsSource::v_parse(RestrictedExecutor & executor)
54 {
55     if (nonOptions_)
56         nonOptions_->clear();
57     if (argc_ <= 1)
58         return;
59     char const ** argp (argv_+1);
60     int n (argc_-1);
61     for (; n; --n, ++argp) {
62         std::string arg (*argp);
63         if (arg == "--") {
64             for (; n; --n, ++argp)
65                 parseNonOption(arg, executor);
66             break;
67         }
68         else if (boost::algorithm::starts_with(arg, std::string("--")) && arg.size() > 2)
69             try {
70                 parseLongOption(arg.substr(2), executor);
71             }
72             catch (senf::ExceptionMixin & e) {
73                 e << "\nwhile parsing command line option: " << arg;
74                 throw;
75             }
76         else if (boost::algorithm::starts_with(arg, std::string("-")) && arg.size() > 1) {
77             for (std::string::size_type i (1); i<arg.size(); ++i) {
78                 char opt (arg[i]);
79                 ShortOptions::iterator j (shortOptions_.find(opt));
80                 if (j == shortOptions_.end())
81                     throw SyntaxErrorException(
82                         (boost::format("invalid short option '%c'") % opt).str());
83                 std::string param;
84                 if (j->second.withArg) {
85                     if (i >= arg.size()-1) {
86                         if (n > 0) {
87                             param = *(++argp);
88                             --n;
89                         }
90                     }
91                     else
92                         param = arg.substr(i+1);
93                     i = arg.size();
94                 }
95                 std::string longOpt (j->second.longOpt);
96                 if (! param.empty() ) {
97                     longOpt += "=";
98                     longOpt += param;
99                 }
100                 if (boost::algorithm::starts_with(longOpt, std::string("--")))
101                     longOpt = longOpt.substr(2);
102                 try {
103                     parseLongOption(longOpt, executor);
104                 }
105                 catch (senf::ExceptionMixin & e) {
106                     e << "\nwhile parsing command line option: -" << opt << ' ' << param;
107                     throw;
108                 }
109             }
110         }
111         else
112             parseNonOption(arg, executor);
113     }
114 }
115
116 prefix_ void
117 senf::console::detail::ProgramOptionsSource::parseLongOption(std::string const & arg,
118                                                              RestrictedExecutor & executor)
119 {
120     std::string::size_type ix (arg.find('='));
121     std::string name (arg.substr(0,ix));
122     std::string value (ix==std::string::npos ? std::string() : arg.substr(ix+1));
123
124     typedef std::vector<Token> Path;
125
126     ParseCommandInfo cmd;
127     Path path;
128
129     DirectoryNode::ptr cwd (executor.root().thisptr());
130     std::string::size_type b (0);
131     while (b < name.size()) {
132         std::string::size_type e (name.size());
133         for (;e != std::string::npos && e > b; e = name.rfind('-', e)) {
134             std::string key (name.substr(b,e-b));
135             if (! cwd->hasChild(key)) {
136                 DirectoryNode::ChildrenRange completions (cwd->completions(key));
137                 if (has_one_elt(completions))
138                     key = completions.begin()->first;
139                 else {
140                     e -= 1;
141                     continue;
142                 }
143             }
144             path.push_back(WordToken(key));
145             if (e < name.size())
146                 cwd = cwd->getDirectory(key).thisptr();
147             b = e+1;
148             e = b+1;
149             break;
150         }
151         if (e == std::string::npos || e <= b) {
152             // This will produce a correct error message later or will skip the node,
153             // if parsing is restricted to a subtree
154             path.push_back(WordToken(name.substr(b)));
155             b = name.size();
156         }
157     }
158
159     cmd.command(path);
160     // Here we check, whether the command
161     // - is an overloaded/parsed command
162     // - with a single overload
163     // - taking only a single argument
164     // - which consists of a single token
165     // If all these conditions are met, we pass the parameter value as a single WordToken
166     // otherwise we parse it using the config parser
167     try {
168         GenericNode const & node (executor.getNode(cmd));
169         OverloadedCommandNode const * cmdnode (dynamic_cast<OverloadedCommandNode const *>(&node));
170         if (cmdnode && cmdnode->overloads().size() == 1) {
171             CommandOverload const & overload (**cmdnode->overloads().begin());
172             if (overload.numArguments() == 1) {
173                 ArgumentDoc argdoc;
174                 argdoc.singleToken = false;
175                 overload.argumentDoc(0, argdoc);
176                 if (argdoc.singleToken) {
177                     cmd.addToken(WordToken(value));
178                     goto execute;
179                 }
180             }
181         } /* else */ {
182             parser_.parseArguments(value, cmd);
183         }
184     execute:
185         executor(executor.stream(), cmd);
186     }
187     catch (Executor::IgnoreCommandException &)
188     {}
189 }
190
191 prefix_ void
192 senf::console::detail::ProgramOptionsSource::parseNonOption(std::string const & arg,
193                                                             RestrictedExecutor & executor)
194 {
195     if (! nonOptions_)
196         throw SyntaxErrorException("invalid non-option argument");
197     nonOptions_->push_back(arg);
198 }
199
200 //-/////////////////////////////////////////////////////////////////////////////////////////////////
201
202 prefix_ void senf::console::parseOptions(int argc, char const ** argv, DirectoryNode & root)
203 {
204     ProgramOptions opts (argc, argv, root);
205     opts.parse();
206 }
207
208 //-/////////////////////////////////////////////////////////////////////////////////////////////////
209 #undef prefix_
210 //#include "ProgramOptions.mpp"
211
212 \f
213 // Local Variables:
214 // mode: c++
215 // fill-column: 100
216 // comment-column: 40
217 // c-file-style: "senf"
218 // indent-tabs-mode: nil
219 // ispell-local-dictionary: "american"
220 // compile-command: "scons -u test"
221 // End: