1#ifndef CLIOPTS_H_ 2#define CLIOPTS_H_ 3 4#include <stddef.h> /* size_t */ 5 6#ifdef __cplusplus 7extern "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 */ 21typedef 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 49typedef 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 88struct 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 122typedef 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 */ 135CLIOPTS_API 136void 137cliopts_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 */ 153CLIOPTS_API 154int 155cliopts_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 171namespace cliopts { 172class 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 */ 179class Option : protected cliopts_entry { 180public: 181 bool passed() const { return found != 0; } 182 void setPassed(bool val = true) { found = val ? 1 : 0; } 183 int numSpecified() const { return found; } 184 Option() { memset(this, 0, sizeof (cliopts_entry)); } 185private: 186 friend class Parser; 187}; 188 189class 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 */ 201template < 202 typename T, 203 cliopts_argtype_t Targ, 204 typename Taccum, 205 typename Tpriv = EmptyPriv 206 > 207class TOption : public Option { 208 209private: 210 typedef TOption<T,Targ, Taccum, Tpriv> Ttype; 211 Taccum innerVal; /**< Pointer for cliopts_entry destination */ 212 Tpriv priv; /**< Type-specific data */ 213public: 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 314 inline T& const_result() { return (T)innerVal; } 315 316 operator T() { return result(); } 317 318protected: 319 /** Called from within copy constructor */ 320 inline void doCopy(TOption&) {} 321 322 /** Create the default value for the option */ 323 static inline Taccum createDefault() { return Taccum(); } 324}; 325 326typedef TOption<std::string, 327 CLIOPTS_ARGT_STRING, 328 const char*, 329 std::string> StringOption; 330 331typedef TOption<std::vector<std::string>, 332 CLIOPTS_ARGT_LIST, 333 cliopts_list, 334 std::vector<std::string> > ListOption; 335 336typedef TOption<bool, 337 CLIOPTS_ARGT_NONE, 338 int> BoolOption; 339 340typedef TOption<unsigned, 341 CLIOPTS_ARGT_UINT, 342 unsigned> UIntOption; 343 344typedef TOption<int, 345 CLIOPTS_ARGT_INT, 346 int> IntOption; 347 348typedef TOption<int, 349 CLIOPTS_ARGT_HEX, 350 unsigned> HexOption; 351 352typedef TOption<float, 353 CLIOPTS_ARGT_FLOAT, 354 float> FloatOption; 355 356// STRING ROUTINES 357template<> inline std::string& StringOption::const_result() { 358 if (innerVal && passed()) { 359 priv = innerVal; 360 } 361 return priv; 362} 363template<> inline std::string StringOption::result() { 364 return const_result(); 365} 366template<> inline StringOption& StringOption::setDefault(const std::string& s) { 367 priv = s; 368 innerVal = priv.c_str(); 369 return *this; 370} 371template<> 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} 377template<> inline const char* StringOption::createDefault() { return ""; } 378 379// LIST ROUTINES 380template<> 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} 388template<> inline std::vector<std::string> ListOption::result() { 389 return const_result(); 390} 391 392// BOOL ROUTINES 393template<> inline BoolOption& BoolOption::setDefault(const bool& b) { 394 innerVal = b ? 1 : 0; return *this; 395} 396template<> 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 */ 404class Parser { 405public: 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 */ 422 void addOption(Option *opt) { options.push_back(opt); } 423 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 */ 480 const std::vector<std::string>& getRestArgs() { return restargs; } 481 482 cliopts_extra_settings default_settings; 483private: 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