-
Notifications
You must be signed in to change notification settings - Fork 3
/
cliopts.h
514 lines (432 loc) · 14 KB
/
cliopts.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
#ifndef CLIOPTS_H_
#define CLIOPTS_H_
#include <stddef.h> /* size_t */
#include <limits.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#if defined(_WIN32) && defined(CLIOPTS_BUILDING_DLL)
#define CLIOPTS_API __declspec( dllexport )
#else
#define CLIOPTS_API
#endif
/**
* Various option types
*/
typedef enum {
/** takes no argument, dest should be anything big enough to hold a boolean*/
CLIOPTS_ARGT_NONE,
/** simple int type, dest should be an 'int' */
CLIOPTS_ARGT_INT,
/** dest should be an unsigned int */
CLIOPTS_ARGT_UINT,
/** dest should be an unsigned long long */
CLIOPTS_ARGT_ULONGLONG,
/** dest should be an unsigned int, but command line format is hex */
CLIOPTS_ARGT_HEX,
/** dest should be a char**. Note that the string is allocated, so you should
* free() it when done */
CLIOPTS_ARGT_STRING,
/** dest should be a float* */
CLIOPTS_ARGT_FLOAT,
/**
* Destination should be cliopts_list. Argument type is assumed to be a
* string. You can use this option type to build -Doption=value style
* options which can be processed later on.
*/
CLIOPTS_ARGT_LIST
} cliopts_argtype_t;
typedef struct {
/**
* Input parameters
*/
/** Short option, i.e. -v (0 for none) */
char kshort;
/** long option, i.e. --verbose, NULL for none */
const char *klong;
/** type of value */
cliopts_argtype_t ktype;
/** destination pointer for value */
void *dest;
/** help string for this option */
const char *help;
/** description of the value, e.g. --file=FILE */
const char *vdesc;
/** set this to true if the user must provide this option */
int required;
/** set this to true to disable showing the option in the help text */
int hidden;
/**
* Output parameters
*/
/** whether this option was encountered on the command line */
int found;
} cliopts_entry;
struct cliopts_extra_settings {
/** Assume actual arguments start from argv[0], not argv[1] */
int argv_noskip;
/** Don't exit on error */
int error_noexit;
/** Don't print help on error */
int error_nohelp;
/** Don't interpret --help or -? as help flags */
int help_noflag;
/** Program name (defaults to argv[0]) */
const char *progname;
/** Usage string (defaults to "[OPTIONS..]") */
const char *argstring;
/** Short description (empty by default) */
const char *shortdesc;
/** Print default values as well */
int show_defaults;
/**
* Maximum length of a line when printing help. This may be detected
* using the $COLUMNS environment variable
*/
int line_max;
/** Positional parameters (if found). If this array is non-NULL on input
* then parameters which are not recognized will be placed here. Otherwise
* the parser will return with an error. This array must be large enough
* to contain `argc` count strings.
*/
const char **restargs;
/** Number of positional parameters (if found) */
unsigned nrestargs;
/**
* Indicates whether the restargs are required.
* This text is printed after argstring.
*/
const char *argstring_restargs;
/** The minimum required rest args */
int min_restargs;
};
typedef struct {
/** Array of string pointers. Allocated via standard malloc functions */
char **values;
/** Number of valid entries */
size_t nvalues;
/** Number of entries allocated */
size_t nalloc;
} cliopts_list;
/**
* Clear a list of its contents
* @param l The list
*/
CLIOPTS_API
void
cliopts_list_clear(cliopts_list *l);
/**
* Parse options.
*
* @param entries an array of cliopts_entry structures. The list should be
* terminated with a structure which has its dest field set to NULL
*
* @param argc the count of arguments
* @param argv the actual list of arguments
* @param lastidx populated with the amount of elements from argv actually read
* @params setting a structure defining extra settings for the argument parser.
* May be NULL
*
* @return 0 for success, -1 on error.
*/
CLIOPTS_API
int
cliopts_parse_options(cliopts_entry *entries,
int argc,
char **argv,
int *lastidx,
struct cliopts_extra_settings *settings);
#ifdef __cplusplus
}
#ifdef CLIOPTS_ENABLE_CXX
#include <string>
#include <vector>
#include <list>
#include <cstdlib>
#include <cstring>
#include <cstdio>
namespace cliopts {
class Parser;
/**
* This class should typically not be used directly. It is a simple wrapper
* around the C-based ::cliopts_entry class for further wrapping by the
* cliopts::TOption template class.
*/
class Option : protected cliopts_entry {
public:
bool passed() const { return found != 0; }
void setPassed(bool val = true) { found = val ? 1 : 0; }
int numSpecified() const { return found; }
Option() { memset(this, 0, sizeof (cliopts_entry)); }
private:
friend class Parser;
};
class EmptyPriv {};
/**
* Option template class. This class is not meant to be used by applications
* directly. Applications should use one of the template instantiations
* below (e.g. cliopts::StringOption)
*
* @param T type returned to the application
* @param Targ integer constant indicating the type of the C argument
* @param Taccum raw destination type which will store the parsed value
* @param Tpriv type of private data to be stored for type-specific processing
*/
template <
typename T,
cliopts_argtype_t Targ,
typename Taccum,
typename Tpriv = EmptyPriv
>
class TOption : public Option {
private:
typedef TOption<T,Targ, Taccum, Tpriv> Ttype;
Taccum innerVal; /**< Pointer for cliopts_entry destination */
Tpriv priv; /**< Type-specific data */
public:
/**
* Construct a new option
* @param shortname abbreviated short name
* @param longname long ("GNU-style" name)
* @param deflval default value to be used
* @param helpstr Text explaining the option
*/
TOption(char shortname, const char *longname = NULL,
T deflval = createDefault(), const char *helpstr = NULL) {
memset((cliopts_entry *)this, 0, sizeof(cliopts_entry));
ktype = Targ;
klong = longname;
dest = &innerVal;
abbrev(shortname);
description(helpstr);
setDefault(deflval);
}
/**
* Construct a new option
* @param longname the long ("GNU-Style") name.
*/
TOption(const char *longname) {
memset((cliopts_entry *)this, 0, sizeof(cliopts_entry));
ktype = Targ;
klong = longname;
innerVal = createDefault();
dest = &innerVal;
}
/**
* Copy constructor. This mainly exists to allow chaining (See example)
* @param other the source option to copy
*/
TOption(TOption& other) {
*(cliopts_entry*)this = *(cliopts_entry*) &other;
innerVal = other.innerVal;
dest = &innerVal;
other.dest = NULL;
doCopy(other);
}
/**
* Set the default value for the option
* @param val the default value
* @return the option object, for method chaining.
*/
inline Ttype& setDefault(const T& val) {
innerVal = val;
return *this;
}
/**
* Set the single-character switch
* @param val the switch character, e.g. '-v'
* @return the option object, for method chaining
*/
inline Ttype& abbrev(char val) { kshort = val; return *this; }
/**
* Set the description (or help string) for the option.
* @param msg The help string e.g. "Increases verbosity"
* @return the obtion object, for method chaining.
*/
inline Ttype& description(const char *msg) { help = msg; return *this; }
/**
* Set whether this option must appear
* @param val boolean, set to true if required, false if optional
* @return the option object, for method chaining
*/
inline Ttype& mandatory(bool val = true) { required = val; return *this; }
/**
* Set the value description string for the option value.
* @param desc The short description string, e.g. "RETRIES"
* @return the option object, for method chaining
*/
inline Ttype& argdesc(const char *desc) { vdesc = desc; return *this; }
/**
* Whether to hide this option in the help output
* @param val true if the option should be hidden
* @return the object object, for method chaining.
*/
inline Ttype& hide(bool val = true) { hidden = val; return *this; }
/**
* Returns the result object
* @return a copy of the result object
*/
inline T result() { return (T)innerVal; }
/**
* Returns a reference to the result object
* @return a reference to the result object.
*/
inline T& const_result() { return (T)innerVal; }
operator T() { return result(); }
protected:
/** Called from within copy constructor */
inline void doCopy(TOption&) {}
/** Create the default value for the option */
static inline Taccum createDefault() { return Taccum(); }
};
typedef TOption<std::string,
CLIOPTS_ARGT_STRING,
const char*,
std::string> StringOption;
typedef TOption<std::vector<std::string>,
CLIOPTS_ARGT_LIST,
cliopts_list,
std::vector<std::string> > ListOption;
typedef TOption<bool,
CLIOPTS_ARGT_NONE,
int> BoolOption;
typedef TOption<unsigned,
CLIOPTS_ARGT_UINT,
unsigned> UIntOption;
typedef TOption<unsigned long long,
CLIOPTS_ARGT_ULONGLONG,
unsigned long long> ULongLongOption;
typedef TOption<int,
CLIOPTS_ARGT_INT,
int> IntOption;
typedef TOption<int,
CLIOPTS_ARGT_HEX,
unsigned> HexOption;
typedef TOption<float,
CLIOPTS_ARGT_FLOAT,
float> FloatOption;
// STRING ROUTINES
template<> inline std::string& StringOption::const_result() {
if (innerVal && passed()) {
priv = innerVal;
}
return priv;
}
template<> inline std::string StringOption::result() {
return const_result();
}
template<> inline StringOption& StringOption::setDefault(const std::string& s) {
priv = s;
innerVal = priv.c_str();
return *this;
}
template<> inline void StringOption::doCopy(StringOption& other) {
priv = other.priv;
if (other.innerVal == other.priv.c_str()) {
innerVal = priv.c_str();
}
}
template<> inline const char* StringOption::createDefault() { return ""; }
// LIST ROUTINES
template<> inline std::vector<std::string>& ListOption::const_result() {
if (priv.empty()) {
for (size_t ii = 0; ii < innerVal.nvalues; ii++) {
priv.push_back(innerVal.values[ii]);
}
}
return priv;
}
template<> inline std::vector<std::string> ListOption::result() {
return const_result();
}
// BOOL ROUTINES
template<> inline BoolOption& BoolOption::setDefault(const bool& b) {
innerVal = b ? 1 : 0; return *this;
}
template<> inline bool BoolOption::result() {
return innerVal != 0 ? true : false;
}
/**
* Parser class which contains one or more cliopts::Option objects. Options
* should be added via the #addOption() member function.
*/
class Parser {
public:
/**
* Construct a new parser
* @param name the "program name" which is printed at the top of the
* help message.
*/
Parser(const char *name = NULL) {
memset(&default_settings, 0, sizeof default_settings);
default_settings.progname = name;
}
/**
* Adds an option to the parser. The option is then checked for presence
* on the commandline (in #parse()).
* @param opt the option to add. Note that the application is responsible
* for keeping the option in valid memory.
*/
void addOption(Option *opt) { options.push_back(opt); }
void addOption(Option& opt) { options.push_back(&opt); }
/**
* Parses the options from the commandline
* @param argc number of arguments
* @param argv list of arguments
* @param standalone_args whether to accept (and store) positional arguments
* (after all named options are processed).
* @param min_standalone_args the minimum required standalone args.
* @return true on parse success, false on parse failure
*/
bool parse(int argc, char **argv, const char *standalone_args = NULL,
int min_standalone_args = 0) {
std::vector<cliopts_entry> ents;
cliopts_extra_settings settings = default_settings;
int lastix;
for (unsigned ii = 0; ii < options.size(); ++ii) {
ents.push_back(*options[ii]);
}
if (ents.empty()) { return false; }
ents.push_back(Option());
const char **tmpargs = NULL;
if (standalone_args) {
tmpargs = new const char*[argc];
settings.restargs = tmpargs;
settings.nrestargs = 0;
settings.argstring_restargs = standalone_args;
settings.min_restargs = min_standalone_args;
}
settings.show_defaults = 1;
int rv = cliopts_parse_options(&ents[0], argc, argv, &lastix, &settings);
if (tmpargs != NULL) {
for (unsigned ii = 0; ii < settings.nrestargs; ii++) {
restargs.push_back(tmpargs[ii]);
}
delete[] tmpargs;
}
// Copy the options back
for (unsigned ii = 0; ii < options.size(); ii++) {
*(cliopts_entry *)options[ii] = ents[ii];
}
if (rv == 0 && lastix != 0) {
for (; lastix < argc; lastix++) {
restargs.push_back(argv[lastix]);
}
}
return rv == 0;
}
/**
* Get the list of any positional arguments found on the commandline
* @return A list of positional arguments found.
*/
const std::vector<std::string>& getRestArgs() { return restargs; }
cliopts_extra_settings default_settings;
private:
std::vector<Option*> options;
std::vector<std::string> restargs;
Parser(Parser&);
};
} // namespace
#endif /* CLIOPTS_ENABLE_CXX */
#endif /* __cplusplus */
#endif /* CLIOPTS_H_ */