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