blocxx
CmdLineParser.cpp
Go to the documentation of this file.
1/*******************************************************************************
2* Copyright (C) 2005, Vintela, Inc. All rights reserved.
3* Copyright (C) 2006, Novell, Inc. All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7*
8* * Redistributions of source code must retain the above copyright notice,
9* this list of conditions and the following disclaimer.
10* * Redistributions in binary form must reproduce the above copyright
11* notice, this list of conditions and the following disclaimer in the
12* documentation and/or other materials provided with the distribution.
13* * Neither the name of
14* Vintela, Inc.,
15* nor Novell, Inc.,
16* nor the names of its contributors or employees may be used to
17* endorse or promote products derived from this software without
18* specific prior written permission.
19*
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30* POSSIBILITY OF SUCH DAMAGE.
31*******************************************************************************/
32
33
38#include "blocxx/BLOCXX_config.h"
40#include "blocxx/Array.hpp"
43#include "blocxx/Assertion.hpp"
44
45#include <algorithm>
46
47
48namespace BLOCXX_NAMESPACE
49{
50
52
53namespace
54{
56 struct longOptIs
57 {
58 longOptIs(const String& longOpt) : m_longOpt(longOpt) {}
59
60 bool operator()(const CmdLineParser::Option& x) const
61 {
62 if (x.longopt != 0)
63 {
64 return m_longOpt == x.longopt;
65 }
66 return false;
67 }
68
69 String m_longOpt;
70 };
71
73 struct shortOptIs
74 {
75 shortOptIs(char shortOpt) : m_shortOpt(shortOpt) {}
76
77 bool operator()(const CmdLineParser::Option& x) const
78 {
79 return m_shortOpt == x.shortopt;
80 }
81
83 };
84
85}
86
88CmdLineParser::CmdLineParser(int argc, char const* const* const argv_, const Option* options, EAllowNonOptionArgsFlag allowNonOptionArgs)
89{
90 BLOCXX_ASSERT(argc > 0); // have to get at least the name
91 BLOCXX_ASSERT(argv_ != 0);
92 BLOCXX_ASSERT(options != 0);
93 char const* const* argv = argv_;
94 char const* const* argvEnd = argv + argc;
95
96 // m_options is an array terminated by a final entry that has a '\0' shortopt && 0 longopt.
97 const Option* optionsEnd(options);
98 while (optionsEnd->shortopt != '\0' || optionsEnd->longopt != 0)
99 {
100 ++optionsEnd;
101 }
102
103 // skip the first argv, which is the program name and loop through the rest
104 ++argv;
105 while (argv != argvEnd)
106 {
107 BLOCXX_ASSERT(*argv != 0);
108 String arg(*argv);
109
110 // look for an option
111 if ((arg.length() >= 2) && (arg[0] == '-'))
112 {
113 const Option* theOpt(0);
114 bool longOpt = false;
115 if (arg[1] == '-')
116 {
117 // long option
118 longOpt = true;
119 arg = arg.substring(2); // erase the --
120 // get rid of =
121 size_t idx = arg.indexOf('=');
122 String argNoVal;
123 if (idx != String::npos)
124 {
125 argNoVal = arg.substring(0, idx);
126 }
127 else
128 {
129 argNoVal = arg;
130 }
131 theOpt = std::find_if (options, optionsEnd, longOptIs(argNoVal));
132 }
133 else // short option
134 {
135 longOpt = false;
136 arg = arg.substring(1); // erase the -
137 theOpt = std::find_if (options, optionsEnd, shortOptIs(arg[0]));
138 }
139
140 if (theOpt == optionsEnd)
141 {
143 }
144
145 if (theOpt->argtype == E_NO_ARG)
146 {
147 m_parsedOptions[theOpt->id]; // if one is already there, don't modify it, else insert a new one
148 ++argv;
149 continue;
150 }
151 // now see if we can get the value
152 String val;
153 if ((theOpt->argtype == E_OPTIONAL_ARG) && (theOpt->defaultValue != 0))
154 {
155 val = theOpt->defaultValue;
156 }
157
158 const char* p = ::strchr(arg.c_str(), '=');
159 if (p)
160 {
161 // get everything after the =
162 val = String(p+1);
163 }
164 else
165 {
166 // a short option can have the value together with it (i.e. -I/opt/vintela/include)
167 if (longOpt == false && arg.length() > 1)
168 {
169 val = arg.substring(1);
170 }
171 // look at the next arg
172 else if (argv+1 != argvEnd)
173 {
174 // Save the next arg if it doesn't start with '-' or if is just '-' by itself.
175 if ( **(argv+1) != '-' || (**(argv+1) == '-' && String(*(argv+1)).length() == 1) )
176 {
177 val = *(argv+1);
178 ++argv;
179 }
180 }
181 }
182
183 // make sure we got an arg if one is required
184 if (theOpt->argtype == E_REQUIRED_ARG && val.empty())
185 {
187 }
188
189 m_parsedOptions[theOpt->id].push_back(val);
190 }
191 else
192 {
193 if (allowNonOptionArgs == E_NON_OPTION_ARGS_INVALID)
194 {
196 }
197 else
198 {
200 }
201 }
202 ++argv;
203 }
204}
205
207// static
208String
209CmdLineParser::getUsage(const Option* options, unsigned int maxColumns)
210{
211// looks like this:
212// "Options:\n"
213// " -1, --one first description\n"
214// " -2, --two [arg] second description (default is optional)\n"
215// " -3, --three <arg> third description\n"
216
217 const unsigned int NUM_OPTION_COLUMNS = 28;
218 StringBuffer usage("Options:\n");
219
220 // m_options is an array terminated by a final entry that has a '\0' shortopt && 0 longOpt.
221 for (const Option* curOption = options; curOption->shortopt != '\0' || curOption->longopt != 0; ++curOption)
222 {
223 StringBuffer curLine;
224 curLine += " ";
225 if (curOption->shortopt != '\0')
226 {
227 curLine += '-';
228 curLine += curOption->shortopt;
229 if (curOption->longopt != 0)
230 {
231 curLine += ", ";
232 }
233 }
234 if (curOption->longopt != 0)
235 {
236 curLine += "--";
237 curLine += curOption->longopt;
238 }
239
240 if (curOption->argtype == E_REQUIRED_ARG)
241 {
242 curLine += " <arg>";
243 }
244 else if (curOption->argtype == E_OPTIONAL_ARG)
245 {
246 curLine += " [arg]";
247 }
248
249 size_t bufferlen = (curLine.length() >= NUM_OPTION_COLUMNS-1) ? 1 : (NUM_OPTION_COLUMNS - curLine.length());
250 for (size_t i = 0; i < bufferlen; ++i)
251 {
252 curLine += ' ';
253 }
254
255 if (curOption->description != 0)
256 {
257 curLine += curOption->description;
258 }
259
260 if (curOption->defaultValue != 0)
261 {
262 curLine += " (default is ";
263 curLine += curOption->defaultValue;
264 curLine += ')';
265 }
266
267 // now if curLine is too long or contains newlines, we need to wrap it.
268 while (curLine.length() > maxColumns || curLine.toString().indexOf('\n') != String::npos)
269 {
270 String curLineStr(curLine.toString());
271 // first we look for a \n to cut at
272 size_t newlineIdx = curLineStr.indexOf('\n');
273
274 // next look for the last space to cut at
275 size_t lastSpaceIdx = curLineStr.lastIndexOf(' ', maxColumns);
276
277 size_t cutIdx = 0;
278 size_t nextLineBeginIdx = 0;
279 if (newlineIdx <= maxColumns)
280 {
281 cutIdx = newlineIdx;
282 nextLineBeginIdx = newlineIdx + 1; // skip the newline
283 }
284 else if (lastSpaceIdx > NUM_OPTION_COLUMNS)
285 {
286 cutIdx = lastSpaceIdx;
287 nextLineBeginIdx = lastSpaceIdx + 1; // skip the space
288 }
289 else
290 {
291 // no space to cut it, just cut it in the middle of a word
292 cutIdx = maxColumns;
293 nextLineBeginIdx = maxColumns;
294 }
295
296 // found a place to cut, so do it.
297 usage += curLineStr.substring(0, cutIdx);
298 usage += '\n';
299
300 // cut out the line from curLine
301 StringBuffer spaces;
302 for (size_t i = 0; i < NUM_OPTION_COLUMNS; ++i)
303 {
304 spaces += ' ';
305 }
306 curLine = spaces.releaseString() + curLineStr.substring(nextLineBeginIdx);
307 }
308
309 curLine += '\n';
310 usage += curLine;
311 }
312 return usage.releaseString();
313}
314
316String
317CmdLineParser::getOptionValue(int id, const char* defaultValue) const
318{
320 if (ci != m_parsedOptions.end() && ci->second.size() > 0)
321 {
322 // grab the last one
323 return ci->second[ci->second.size()-1];
324 }
325 return defaultValue;
326}
327
329String
330CmdLineParser::mustGetOptionValue(int id, const char* exceptionMessage) const
331{
333 if (ci != m_parsedOptions.end() && ci->second.size() > 0)
334 {
335 // grab the last one
336 return ci->second[ci->second.size()-1];
337 }
339}
340
344{
345 StringArray rval;
347 if (ci != m_parsedOptions.end() && ci->second.size() > 0)
348 {
349 rval = ci->second;
350 }
351 return rval;
352}
353
356CmdLineParser::mustGetOptionValueList(int id, const char* exceptionMessage) const
357{
359 if (ci != m_parsedOptions.end() && ci->second.size() > 0)
360 {
361 return ci->second;
362 }
364}
365
367bool
369{
370 return m_parsedOptions.count(id) > 0;
371}
372
374size_t
379
381String
383{
384 return m_nonOptionArgs[n];
385}
386
393
394
395
396} // end namespace BLOCXX_NAMESPACE
397
398
399
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
Definition Assertion.hpp:57
char m_shortOpt
String m_longOpt
#define BLOCXX_DEFINE_EXCEPTION_WITH_ID(NAME)
Define a new exception class named <NAME>Exception that derives from Exception.
#define BLOCXX_THROW_ERR(exType, msg, err)
Throw an exception using FILE and LINE.
void push_back(const T &x)
Append an element to the end of the Array.
size_type size() const
Do command line parsing.
StringArray getOptionValueList(int id) const
Read out all occurences of a string option.
String getOptionValue(int id, const char *defaultValue="") const
Read out a string option.
CmdLineParser(int argc, char const *const *const argv, const Option *options, EAllowNonOptionArgsFlag allowNonOptionArgs)
bool isSet(int id) const
Read out a boolean option or check for the presence of string option.
String getNonOptionArg(size_t n) const
Read out an non-option argument.
StringArray mustGetOptionValueList(int id, const char *exceptionMessage="") const
Read out all occurences of a string option.
@ E_REQUIRED_ARG
the option requires an argument
@ E_NO_ARG
the option does not take an argument
@ E_OPTIONAL_ARG
the option might have an argument
size_t getNonOptionCount() const
Read the number of arguments that aren't options (but, for example, filenames).
@ E_INVALID_OPTION
an unknown option was specified
@ E_INVALID_NON_OPTION_ARG
a non-option argument was specified, but they are not allowed
@ E_MISSING_ARGUMENT
an option for which argtype == E_REQUIRED_ARG did not have an argument
@ E_MISSING_OPTION
the option wasn't specified
@ E_NON_OPTION_ARGS_INVALID
Non-option arguments are invalid.
String mustGetOptionValue(int id, const char *exceptionMessage="") const
Read out a string option.
static String getUsage(const Option *options, unsigned int maxColumns=80)
Generate a usage string for the options, for example:
StringArray getNonOptionArgs() const
Read out the non-option args.
size_type count(const key_type &x) const
const_iterator find(const key_type &x) const
This String class is an abstract data type that represents as NULL terminated string of characters.
Definition String.hpp:67
const char * c_str() const
Definition String.cpp:905
size_t length() const
Definition String.cpp:354
String substring(size_t beginIndex, size_t length=npos) const
Create another String object that is comprised of a substring of this String object.
Definition String.cpp:698
size_t indexOf(char ch, size_t fromIndex=0) const
Find the first occurence of a given character in this String object.
Definition String.cpp:556
static const size_t npos
Definition String.hpp:742
size_t lastIndexOf(char ch, size_t fromIndex=npos) const
Find the last occurence of a character in this String object.
Definition String.cpp:603
Taken from RFC 1321.
char shortopt
short option char. Set to '\0' for none.
EArgumentTypeFlag argtype
specifies constraints for the option's argument
const char * longopt
long option string. Set to 0 for none.
const char * defaultValue
if argtype == E_OPTIONAL_ARG and no argument is specified, this value will be returned....
int id
unique option id, used to retrieve option values