mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
optionparser.cpp
1/*
2 * The Lean Mean C++ Option Parser
3 *
4 * Copyright (C) 2012 Matthias S. Benkmann
5 *
6 * The "Software" in the following 2 paragraphs refers to this file containing
7 * the code to The Lean Mean C++ Option Parser.
8 * The "Software" does NOT refer to any other files which you
9 * may have received alongside this file (e.g. as part of a larger project that
10 * incorporates The Lean Mean C++ Option Parser).
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a copy
13 * of this software, to deal in the Software without restriction, including
14 * without limitation the rights to use, copy, modify, merge, publish,
15 * distribute, sublicense, and/or sell copies of the Software, and to permit
16 * persons to whom the Software is furnished to do so, subject to the following
17 * conditions:
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 */
29
30#include "app/optionparser/optionparser.h"
31
32namespace option
33{
34
35int Option::type() const
36{
37 return desc == 0 ? 0 : desc->type;
38}
39
40int Option::index() const
41{
42 return desc == 0 ? -1 : (int)desc->index;
43}
44
45int Option::count()
46{
47 int c = ( desc == 0 ? 0 : 1 );
48 Option *p = first();
49 while( !p->isLast() )
50 {
51 ++c;
52 p = p->next_;
53 };
54 return c;
55}
56
57bool Option::isFirst() const
58{
59 return isTagged( prev_ );
60}
61
62bool Option::isLast() const
63{
64 return isTagged( next_ );
65}
66
67Option *Option::first()
68{
69 Option *p = this;
70 while( !p->isFirst() )
71 p = p->prev_;
72 return p;
73}
74
75Option *Option::last()
76{
77 return first()->prevwrap();
78}
79
80Option *Option::prev()
81{
82 return isFirst() ? 0 : prev_;
83}
84
85Option *Option::prevwrap()
86{
87 return untag( prev_ );
88}
89
90Option *Option::next()
91{
92 return isLast() ? 0 : next_;
93}
94
95const Option *Option::next() const
96{
97 return isLast() ? 0 : next_;
98}
99
100Option *Option::nextwrap()
101{
102 return untag( next_ );
103}
104
105void Option::append( Option *new_last )
106{
107 Option *p = last();
108 Option *f = first();
109 p->next_ = new_last;
110 new_last->prev_ = p;
111 new_last->next_ = tag( f );
112 f->prev_ = tag( new_last );
113}
114
115Option::Option() : desc( 0 ), name( 0 ), arg( 0 ), namelen( 0 )
116{
117 prev_ = tag( this );
118 next_ = tag( this );
119}
120
121Option::Option( const Descriptor *desc_, const char *name_, const char *arg_ )
122{
123 init( desc_, name_, arg_ );
124}
125
126void Option::operator=( const Option &orig )
127{
128 init( orig.desc, orig.name, orig.arg );
129}
130
131Option::Option( const Option &orig )
132{
133 init( orig.desc, orig.name, orig.arg );
134}
135
136void Option::init( const Descriptor *desc_, const char *name_, const char *arg_ )
137{
138 desc = desc_;
139 name = name_;
140 arg = arg_;
141 prev_ = tag( this );
142 next_ = tag( this );
143 namelen = 0;
144 if( name == 0 )
145 return;
146 namelen = 1;
147 if( name[0] != '-' )
148 return;
149 while( name[namelen] != 0 && name[namelen] != '=' )
150 ++namelen;
151}
152
153Option *Option::tag( Option *ptr )
154{
155 return (Option *)( (unsigned long long)ptr | 1 );
156}
157
158Option *Option::untag( Option *ptr )
159{
160 return (Option *)( (unsigned long long)ptr & ~1ull );
161}
162
163bool Option::isTagged( Option *ptr )
164{
165 return ( (unsigned long long)ptr & 1 );
166}
167
168Stats::Stats() : buffer_max( 1 ), options_max( 1 ) // 1 more than necessary as sentinel
169{
170}
171
172Stats::Stats(
173 bool gnu, const Descriptor usage[], int argc, const char **argv, int min_abbr_len, bool single_minus_longopt )
174 : buffer_max( 1 ), options_max( 1 ) // 1 more than necessary as sentinel
175{
176 add( gnu, usage, argc, argv, min_abbr_len, single_minus_longopt );
177}
178
179Stats::Stats( bool gnu, const Descriptor usage[], int argc, char **argv, int min_abbr_len, bool single_minus_longopt )
180 : buffer_max( 1 ), options_max( 1 ) // 1 more than necessary as sentinel
181{
182 add( gnu, usage, argc, (const char **)argv, min_abbr_len, single_minus_longopt );
183}
184
185Stats::Stats( const Descriptor usage[], int argc, const char **argv, int min_abbr_len, bool single_minus_longopt )
186 : buffer_max( 1 ), options_max( 1 ) // 1 more than necessary as sentinel
187{
188 add( false, usage, argc, argv, min_abbr_len, single_minus_longopt );
189}
190
191Stats::Stats( const Descriptor usage[], int argc, char **argv, int min_abbr_len, bool single_minus_longopt )
192 : buffer_max( 1 ), options_max( 1 ) // 1 more than necessary as sentinel
193{
194 add( false, usage, argc, (const char **)argv, min_abbr_len, single_minus_longopt );
195}
196
197void Stats::add(
198 bool gnu, const Descriptor usage[], int argc, const char **argv, int min_abbr_len, bool single_minus_longopt )
199{
200 // determine size of options array. This is the greatest index used in the usage + 1
201 int i = 0;
202 while( usage[i].shortopt != 0 )
203 {
204 if( usage[i].index + 1 >= options_max )
205 options_max = ( usage[i].index + 1 ) + 1; // 1 more than necessary as sentinel
206
207 ++i;
208 }
209
210 CountOptionsAction action( &buffer_max );
211 Parser::workhorse( gnu, usage, argc, argv, action, single_minus_longopt, false, min_abbr_len );
212}
213
214void Stats::add(
215 bool gnu, const Descriptor usage[], int argc, char **argv, int min_abbr_len, bool single_minus_longopt )
216{
217 add( gnu, usage, argc, (const char **)argv, min_abbr_len, single_minus_longopt );
218}
219
220void Stats::add( const Descriptor usage[], int argc, const char **argv, int min_abbr_len, bool single_minus_longopt )
221{
222 add( false, usage, argc, argv, min_abbr_len, single_minus_longopt );
223}
224
225void Stats::add( const Descriptor usage[], int argc, char **argv, int min_abbr_len, bool single_minus_longopt )
226{
227 add( false, usage, argc, (const char **)argv, min_abbr_len, single_minus_longopt );
228}
229
230void Parser::parse( bool gnu,
231 const Descriptor usage[],
232 int argc,
233 const char **argv,
234 Option options[],
235 Option buffer[],
236 int min_abbr_len,
237 bool single_minus_longopt,
238 int bufmax )
239{
240 StoreOptionAction action( *this, options, buffer, bufmax );
241 err = !workhorse( gnu, usage, argc, argv, action, single_minus_longopt, true, min_abbr_len );
242}
243
244bool Parser::workhorse( bool gnu,
245 const Descriptor usage[],
246 int numargs,
247 const char **args,
248 Action &action,
249 bool single_minus_longopt,
250 bool print_errors,
251 int min_abbr_len )
252{
253 // protect against NULL pointer
254 if( args == 0 )
255 numargs = 0;
256
257 int nonops = 0;
258
259 while( numargs != 0 && *args != 0 )
260 {
261 const char *param = *args; // param can be --long-option, -srto or non-option argument
262
263 // in POSIX mode the first non-option argument terminates the option list
264 // a lone minus character is a non-option argument
265 if( param[0] != '-' || param[1] == 0 )
266 {
267 if( gnu )
268 {
269 ++nonops;
270 ++args;
271 if( numargs > 0 )
272 --numargs;
273 continue;
274 }
275 else
276 break;
277 }
278
279 // -- terminates the option list. The -- itself is skipped.
280 if( param[1] == '-' && param[2] == 0 )
281 {
282 shift( args, nonops );
283 ++args;
284 if( numargs > 0 )
285 --numargs;
286 break;
287 }
288
289 bool handle_short_options;
290 const char *longopt_name;
291 if( param[1] == '-' ) // if --long-option
292 {
293 handle_short_options = false;
294 longopt_name = param + 2;
295 }
296 else
297 {
298 handle_short_options = true;
299 longopt_name = param + 1; // for testing a potential -long-option
300 }
301
302 bool try_single_minus_longopt = single_minus_longopt;
303 bool have_more_args = ( numargs > 1 || numargs < 0 ); // is referencing argv[1] valid?
304
305 do // loop over short options in group, for long options the body is executed only once
306 {
307 int idx = 0;
308
309 const char *optarg = 0;
310
311 /******************** long option **********************/
312 if( handle_short_options == false || try_single_minus_longopt )
313 {
314 idx = 0;
315 while( usage[idx].longopt != 0 && !streq( usage[idx].longopt, longopt_name ) )
316 ++idx;
317
318 if( usage[idx].longopt == 0 && min_abbr_len > 0 ) // if we should try to match abbreviated long options
319 {
320 int i1 = 0;
321 while( usage[i1].longopt != 0 && !streqabbr( usage[i1].longopt, longopt_name, min_abbr_len ) )
322 ++i1;
323 if( usage[i1].longopt != 0 )
324 { // now test if the match is unambiguous by checking for another match
325 int i2 = i1 + 1;
326 while( usage[i2].longopt != 0 && !streqabbr( usage[i2].longopt, longopt_name, min_abbr_len ) )
327 ++i2;
328
329 if( usage[i2].longopt ==
330 0 ) // if there was no second match it's unambiguous, so accept i1 as idx
331 idx = i1;
332 }
333 }
334
335 // if we found something, disable handle_short_options (only relevant if single_minus_longopt)
336 if( usage[idx].longopt != 0 )
337 handle_short_options = false;
338
339 try_single_minus_longopt = false; // prevent looking for longopt in the middle of shortopt group
340
341 optarg = longopt_name;
342 while( *optarg != 0 && *optarg != '=' )
343 ++optarg;
344 if( *optarg == '=' ) // attached argument
345 ++optarg;
346 else
347 // possibly detached argument
348 optarg = ( have_more_args ? args[1] : 0 );
349 }
350
351 /************************ short option ***********************************/
352 if( handle_short_options )
353 {
354 if( *++param == 0 ) // point at the 1st/next option character
355 break; // end of short option group
356
357 idx = 0;
358 while( usage[idx].shortopt != 0 && !instr( *param, usage[idx].shortopt ) )
359 ++idx;
360
361 if( param[1] == 0 ) // if the potential argument is separate
362 optarg = ( have_more_args ? args[1] : 0 );
363 else
364 // if the potential argument is attached
365 optarg = param + 1;
366 }
367
368 const Descriptor *descriptor = &usage[idx];
369
370 if( descriptor->shortopt == 0 ) /************** unknown option ********************/
371 {
372 // look for dummy entry (shortopt == "" and longopt == "") to use as Descriptor for unknown options
373 idx = 0;
374 while( usage[idx].shortopt != 0 && ( usage[idx].shortopt[0] != 0 || usage[idx].longopt[0] != 0 ) )
375 ++idx;
376 descriptor = ( usage[idx].shortopt == 0 ? 0 : &usage[idx] );
377 }
378
379 if( descriptor != 0 )
380 {
381 Option option( descriptor, param, optarg );
382 switch( descriptor->check_arg( option, print_errors ) )
383 {
384 case ARG_ILLEGAL:
385 return false; // fatal
386 case ARG_OK:
387 // skip one element of the argument vector, if it's a separated argument
388 if( optarg != 0 && have_more_args && optarg == args[1] )
389 {
390 shift( args, nonops );
391 if( numargs > 0 )
392 --numargs;
393 ++args;
394 }
395
396 // No further short options are possible after an argument
397 handle_short_options = false;
398
399 break;
400 case ARG_IGNORE:
401 case ARG_NONE:
402 option.arg = 0;
403 break;
404 }
405
406 if( !action.perform( option ) )
407 return false;
408 }
409
410 } while( handle_short_options );
411
412 shift( args, nonops );
413 ++args;
414 if( numargs > 0 )
415 --numargs;
416
417 } // while
418
419 if( numargs > 0 && *args == 0 ) // It's a bug in the caller if numargs is greater than the actual number
420 numargs = 0; // of arguments, but as a service to the user we fix this if we spot it.
421
422 if( numargs < 0 ) // if we don't know the number of remaining non-option arguments
423 { // we need to count them
424 numargs = 0;
425 while( args[numargs] != 0 )
426 ++numargs;
427 }
428
429 return action.finished( numargs + nonops, args - nonops );
430}
431} // namespace option
432// namespace option
constexpr units::realT c()
The speed of light.
Definition constants.hpp:58