├── .gitignore ├── CMakeLists.txt ├── Makefile ├── LICENSE ├── c-example.c ├── cxx-example.cpp ├── README.md ├── cliopts.h └── cliopts.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.o 3 | *.exe 4 | *.dll 5 | *.obj 6 | *.lib 7 | *.exp 8 | *.swp 9 | cxxtest 10 | test 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8.9) 2 | ADD_LIBRARY(cliopts cliopts.c) 3 | ADD_EXECUTABLE(c-example c-example.c) 4 | ADD_EXECUTABLE(cxx-example cxx-example.cpp) 5 | TARGET_LINK_LIBRARIES(c-example cliopts) 6 | TARGET_LINK_LIBRARIES(cxx-example cliopts) 7 | 8 | IF(WIN32) 9 | ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) 10 | ENDIF() 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: c-example cxx-example 2 | 3 | CFLAGS = -Wall -Wextra \ 4 | -Wno-missing-field-initializers \ 5 | -Winit-self -pedantic -g 6 | 7 | CXXFLAGS = -Wall -Wextra -g 8 | 9 | c-example: c-example.c cliopts.o 10 | $(CC) $(CFLAGS) -o $@ $^ 11 | 12 | cxx-example: cxx-example.cpp cliopts.o 13 | $(CXX) $(CXXFLAGS) -o $@ $^ 14 | 15 | clean: 16 | rm -f *.o *.so c-example cxx-example 17 | rm -fr *.dSYM 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 M. Nunberg, mnunberg@haskalah.org 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /c-example.c: -------------------------------------------------------------------------------- 1 | #include "cliopts.h" 2 | #include 3 | #include 4 | 5 | int An_Integer = 0; 6 | unsigned int An_Unsigned = 0; 7 | char *A_String = NULL; 8 | int A_Boolean = 0; 9 | int HexVal = 0x0; 10 | int ALongOption = -1; 11 | int AShortOption; 12 | int Required = 0; 13 | int ComplicatedOption; 14 | cliopts_list ValueList = { NULL }; 15 | 16 | cliopts_entry entries[] = { 17 | {'i', "int", CLIOPTS_ARGT_INT, &An_Integer, "A simple integer", "INT" }, 18 | {'u', "unsigned", CLIOPTS_ARGT_UINT, &An_Unsigned, "An unsigned value"}, 19 | {'s', "string", CLIOPTS_ARGT_STRING, &A_String, "a string" }, 20 | {'H', "hex", CLIOPTS_ARGT_HEX, &HexVal, "Hexadecimal Value" }, 21 | {'v', "verbose", CLIOPTS_ARGT_NONE, &A_Boolean, "a boolean" }, 22 | {0 , "long-option", CLIOPTS_ARGT_NONE, &ALongOption, "Long option" }, 23 | {'C', NULL, CLIOPTS_ARGT_NONE, &AShortOption, NULL }, 24 | {'R', "required", CLIOPTS_ARGT_NONE, &Required, "required", NULL, 1 }, 25 | {'X', "complicated-option", CLIOPTS_ARGT_NONE, &ComplicatedOption, 26 | "This is a very long help text for a complicated option. " 27 | "You may ask why do we need such complicated options, well " 28 | "the answer may be, for example, that we need to test it; or" 29 | }, 30 | {'D', "list", CLIOPTS_ARGT_LIST, &ValueList, "OPTIONS" }, 31 | { 0 } 32 | }; 33 | 34 | 35 | int main(int argc, char **argv) 36 | { 37 | int last_opt; 38 | cliopts_entry *cur; 39 | 40 | cliopts_parse_options(entries, argc, argv, &last_opt, NULL); 41 | 42 | /* Simple */ 43 | 44 | if (A_String) { 45 | printf("Have string: %s\n", A_String); 46 | } 47 | if (An_Integer) { 48 | printf("Have integer %d\n", An_Integer); 49 | } 50 | if (An_Unsigned) { 51 | printf("Have unsigned: %u\n", An_Unsigned); 52 | } 53 | if (HexVal) { 54 | printf("Have hex: %x\n", HexVal); 55 | } 56 | 57 | /* If you feel masochistic, you can use a loop. This is necessary 58 | * if you want to know whether an option was specified on the command 59 | * line, or how many times it was specified 60 | */ 61 | 62 | for (cur = entries; cur->dest; cur++) { 63 | printf("Option [%c,%s] found %d times\n", 64 | cur->kshort, 65 | cur->klong, 66 | cur->found); 67 | } 68 | 69 | printf("Boolean value: %d\n", A_Boolean); 70 | 71 | if (ValueList.nvalues) { 72 | size_t ii; 73 | printf("Have value list:\n"); 74 | for (ii = 0; ii < ValueList.nvalues; ii++) { 75 | printf(" %s\n", ValueList.values[ii]); 76 | } 77 | } 78 | 79 | printf("Rest arguments begin at %d (%s) \n", last_opt, argv[last_opt]); 80 | cliopts_list_clear(&ValueList); 81 | return 0; 82 | 83 | } 84 | -------------------------------------------------------------------------------- /cxx-example.cpp: -------------------------------------------------------------------------------- 1 | #define CLIOPTS_ENABLE_CXX 2 | #include "cliopts.h" 3 | using namespace cliopts; 4 | 5 | static IntOption An_Integer = IntOption("int") 6 | .setDefault(0) 7 | .abbrev('i') 8 | .description("Signed integer value"); 9 | 10 | static UIntOption An_Unsigned = UIntOption("unsigned") 11 | .setDefault(0) 12 | .abbrev('u') 13 | .description("Unsigned integer value"); 14 | 15 | static ULongLongOption A_LongLong = ULongLongOption("longlong-option") 16 | .abbrev('U') 17 | .description("Long long integer option!"); 18 | 19 | static StringOption A_String = StringOption("string") 20 | .setDefault("") 21 | .abbrev('s') 22 | .description("String option"); 23 | 24 | static HexOption HexVal = HexOption("hex") 25 | .setDefault(0) 26 | .abbrev('h') 27 | .description("Hex value"); 28 | 29 | static BoolOption A_Boolean = BoolOption("verbose") 30 | .abbrev('v') 31 | .setDefault(false) 32 | .description("Boolean value"); 33 | 34 | static BoolOption Required = BoolOption("required") 35 | .abbrev('R') 36 | .mandatory(). 37 | description("Required Option"); 38 | 39 | static BoolOption ComplexOption = BoolOption("complicated-option") 40 | .abbrev('X') 41 | .description("This is a very very very long line. This option means " 42 | "nothing, but still should demonstrate text wrapping abilities"); 43 | 44 | static ListOption MultiOption = ListOption("list-option") 45 | .abbrev('D') 46 | .description("Long list option..."); 47 | 48 | int main(int argc, char **argv) 49 | { 50 | Parser parser("Test App"); 51 | 52 | parser.addOption(An_Integer); 53 | parser.addOption(An_Unsigned); 54 | parser.addOption(A_LongLong); 55 | parser.addOption(A_String); 56 | parser.addOption(HexVal); 57 | parser.addOption(A_Boolean); 58 | parser.addOption(Required); 59 | parser.addOption(ComplexOption); 60 | parser.addOption(MultiOption); 61 | parser.parse(argc, argv); 62 | 63 | if (A_String.passed()) { 64 | printf("Have string %s\n", A_String.result().c_str()); 65 | } 66 | 67 | if (An_Integer.passed()) { 68 | printf("Have integer %d\n", (int)An_Integer); 69 | } 70 | 71 | if (An_Unsigned) { 72 | printf("Have unsigned: %u\n", (unsigned)An_Unsigned); 73 | } 74 | if (A_LongLong) { 75 | printf("Have unsigned long long: %llu\n", (unsigned long long)A_LongLong); 76 | } 77 | if (HexVal) { 78 | printf("Have hex: %x\n", (int)HexVal); 79 | } 80 | printf("Boolean value: %d\n", (bool)A_Boolean); 81 | printf("Boolean value specified %d times\n", A_Boolean.numSpecified()); 82 | 83 | const std::vector& kv = MultiOption.const_result(); 84 | printf("Multi Option:\n"); 85 | for (size_t ii = 0; ii < kv.size(); ii++) { 86 | printf(" Multi: %s\n", kv[ii].c_str()); 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cliopts - Single source/header option parsing 2 | 3 | cliopts is a small option parsing library for both C and C++. It offers an 4 | ANSI C ("C89") implementation for C and a more advanced idiomatic C++ based 5 | header-only implementation for C++. 6 | 7 | The API for this library is modified after GLib's 8 | [GOption](https://developer.gnome.org/glib/stable/glib-Commandline-option-parser.html) 9 | interface (which itself is modelled after [popt](http://rpm5.org/files/popt/). 10 | However, I wanted something easily embeddable. 11 | 12 | ## Building 13 | 14 | A `Makefile` is provided for GNU Make. A `CMakeLists.txt` is provided as well 15 | for compilation on any system with CMake. Since the library _only_ depends 16 | on standard C library features, this should work anywhere (I've tested this on 17 | Windows and various unices). 18 | 19 | ## Using 20 | 21 | See the `c-example.c` and `cxx-example.cpp` for examples using the C and C++ 22 | interfaces respectively. 23 | 24 | The default behavior of the parser is to print a help message and exit the 25 | program upon error. This is suitable for most command line applications, 26 | and can be disabled by passing a custom `cliopts_extra_settings` structure. 27 | 28 | ### Using in C 29 | 30 | Declare and statically initialize an array of `cliopts_entry` structures 31 | containing describing each of your options. Typically you will need to declare 32 | the actual target pointers ahead of the declarations (the example is very 33 | useful for this). The last entry should be an empty structure, e.g. 34 | 35 | ```c 36 | int a_number = 42; // default value is 42 37 | char *a_string = "Hello"; // default value is "hello" 38 | 39 | cliopts_entry entries[] = { 40 | {'i', "int-argument", CLIOPTS_ARGT_INT, &a_number, "help message for this option"}, 41 | {'s', "string-argument", CLIOPTS_ARG_STRING, &a_string, "help message for this option"}, 42 | {0} 43 | } 44 | ``` 45 | 46 | The `cliopts_parse_options` should be called to process the arguments. If 47 | everything goes well, the various variable pointers will contain the proper 48 | value (if found on the command line, it will contain the new value, or if not 49 | found, will remain unchanced). 50 | 51 | ```c 52 | int main(int argc, char **argv) 53 | { 54 | cliopts_parse_options(entries, argc, argv, NULL, NULL); 55 | printf("Number is now: %d\n", a_number); 56 | printf("String is now: %s\n", a_string); 57 | return 0; 58 | } 59 | ``` 60 | 61 | If the program is invoked as `./a.out --int-argument 99` then `a_number` will 62 | be 99, and `a_string` will remain "Hello". 63 | 64 | ### Using in C++ 65 | 66 | The C++ API builds upon the C interface, eliminating the need to explicitly 67 | define your container variables before the option is declared. You should 68 | also define the `CLIOPTS_ENABLE_CXX` macro before including the header file 69 | to be able to use the C++ API. 70 | 71 | To define an option entry, create an instance of one of the option classes: 72 | 73 | ```c++ 74 | cliopts::IntOption int_option("int-argument"); 75 | cliopts::StringOption string_option("string-argument"); 76 | ``` 77 | 78 | To modify attributes of the option (for example, to give it a short 79 | single-character name), call the appropriate member functions: 80 | 81 | ```c++ 82 | int_option.abbrev('i').setDefault(42); 83 | string_option.abbrev('s').setDefault("Hello"); 84 | ``` 85 | 86 | You can also use method chaining (as in the example file): 87 | 88 | ```c++ 89 | cliopts::IntOption int_option = cliopts::IntOption("int-argument) 90 | .abbrev('i') 91 | .setDefault(42); 92 | ``` 93 | 94 | To parse the options, create a parser and add the option objects to it: 95 | 96 | ```c++ 97 | cliopts::Parser parser("my-app-name"); 98 | parser.addOption(int_option); 99 | parser.addOption(string_option); 100 | ``` 101 | 102 | To actually parse the options, invoke the `parse` member function. 103 | 104 | ```c++ 105 | parser.parse(argc, argv) 106 | printf("Int option is: %d\n", int_option.result()); 107 | printf("String option is: %s\n", string_option.result().c_str()); 108 | ``` 109 | 110 | ### Memory Usage 111 | 112 | Parsed string values are _copied_ to their destination (via `malloc`). In 113 | the future I may add an option to simply store the pointer from `argv`. 114 | 115 | # Author & Copyright 116 | 117 | Copyright (C) 2012-2015 Mark Nunberg. See `LICENSE` file for licensing. 118 | -------------------------------------------------------------------------------- /cliopts.h: -------------------------------------------------------------------------------- 1 | #ifndef CLIOPTS_H_ 2 | #define CLIOPTS_H_ 3 | 4 | #include /* size_t */ 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif /* __cplusplus */ 10 | 11 | #if defined(_WIN32) && defined(CLIOPTS_BUILDING_DLL) 12 | #define CLIOPTS_API __declspec( dllexport ) 13 | 14 | #else 15 | #define CLIOPTS_API 16 | #endif 17 | 18 | 19 | /** 20 | * Various option types 21 | */ 22 | typedef enum { 23 | /** takes no argument, dest should be anything big enough to hold a boolean*/ 24 | CLIOPTS_ARGT_NONE, 25 | 26 | /** simple int type, dest should be an 'int' */ 27 | CLIOPTS_ARGT_INT, 28 | 29 | /** dest should be an unsigned int */ 30 | CLIOPTS_ARGT_UINT, 31 | 32 | /** dest should be an unsigned long long */ 33 | CLIOPTS_ARGT_ULONGLONG, 34 | 35 | /** dest should be an unsigned int, but command line format is hex */ 36 | CLIOPTS_ARGT_HEX, 37 | 38 | /** dest should be a char**. Note that the string is allocated, so you should 39 | * free() it when done */ 40 | CLIOPTS_ARGT_STRING, 41 | 42 | /** dest should be a float* */ 43 | CLIOPTS_ARGT_FLOAT, 44 | 45 | /** 46 | * Destination should be cliopts_list. Argument type is assumed to be a 47 | * string. You can use this option type to build -Doption=value style 48 | * options which can be processed later on. 49 | */ 50 | CLIOPTS_ARGT_LIST 51 | } cliopts_argtype_t; 52 | 53 | typedef struct { 54 | /** 55 | * Input parameters 56 | */ 57 | 58 | /** Short option, i.e. -v (0 for none) */ 59 | char kshort; 60 | 61 | /** long option, i.e. --verbose, NULL for none */ 62 | const char *klong; 63 | 64 | /** type of value */ 65 | cliopts_argtype_t ktype; 66 | 67 | /** destination pointer for value */ 68 | void *dest; 69 | 70 | /** help string for this option */ 71 | const char *help; 72 | 73 | /** description of the value, e.g. --file=FILE */ 74 | const char *vdesc; 75 | 76 | 77 | /** set this to true if the user must provide this option */ 78 | int required; 79 | 80 | /** set this to true to disable showing the option in the help text */ 81 | int hidden; 82 | 83 | /** 84 | * Output parameters 85 | */ 86 | 87 | /** whether this option was encountered on the command line */ 88 | int found; 89 | 90 | } cliopts_entry; 91 | 92 | struct cliopts_extra_settings { 93 | /** Assume actual arguments start from argv[0], not argv[1] */ 94 | int argv_noskip; 95 | /** Don't exit on error */ 96 | int error_noexit; 97 | /** Don't print help on error */ 98 | int error_nohelp; 99 | /** Don't interpret --help or -? as help flags */ 100 | int help_noflag; 101 | /** Program name (defaults to argv[0]) */ 102 | const char *progname; 103 | /** Usage string (defaults to "[OPTIONS..]") */ 104 | const char *argstring; 105 | /** Short description (empty by default) */ 106 | const char *shortdesc; 107 | /** Print default values as well */ 108 | int show_defaults; 109 | /** 110 | * Maximum length of a line when printing help. This may be detected 111 | * using the $COLUMNS environment variable 112 | */ 113 | int line_max; 114 | 115 | /** Positional parameters (if found). If this array is non-NULL on input 116 | * then parameters which are not recognized will be placed here. Otherwise 117 | * the parser will return with an error. This array must be large enough 118 | * to contain `argc` count strings. 119 | */ 120 | const char **restargs; 121 | 122 | /** Number of positional parameters (if found) */ 123 | unsigned nrestargs; 124 | 125 | /** 126 | * Indicates whether the restargs are required. 127 | * This text is printed after argstring. 128 | */ 129 | const char *argstring_restargs; 130 | 131 | /** The minimum required rest args */ 132 | int min_restargs; 133 | }; 134 | 135 | typedef struct { 136 | /** Array of string pointers. Allocated via standard malloc functions */ 137 | char **values; 138 | /** Number of valid entries */ 139 | size_t nvalues; 140 | /** Number of entries allocated */ 141 | size_t nalloc; 142 | } cliopts_list; 143 | 144 | /** 145 | * Clear a list of its contents 146 | * @param l The list 147 | */ 148 | CLIOPTS_API 149 | void 150 | cliopts_list_clear(cliopts_list *l); 151 | 152 | /** 153 | * Parse options. 154 | * 155 | * @param entries an array of cliopts_entry structures. The list should be 156 | * terminated with a structure which has its dest field set to NULL 157 | * 158 | * @param argc the count of arguments 159 | * @param argv the actual list of arguments 160 | * @param lastidx populated with the amount of elements from argv actually read 161 | * @params setting a structure defining extra settings for the argument parser. 162 | * May be NULL 163 | * 164 | * @return 0 for success, -1 on error. 165 | */ 166 | CLIOPTS_API 167 | int 168 | cliopts_parse_options(cliopts_entry *entries, 169 | int argc, 170 | char **argv, 171 | int *lastidx, 172 | struct cliopts_extra_settings *settings); 173 | #ifdef __cplusplus 174 | } 175 | 176 | #ifdef CLIOPTS_ENABLE_CXX 177 | #include 178 | #include 179 | #include 180 | #include 181 | #include 182 | #include 183 | 184 | namespace cliopts { 185 | class Parser; 186 | 187 | /** 188 | * This class should typically not be used directly. It is a simple wrapper 189 | * around the C-based ::cliopts_entry class for further wrapping by the 190 | * cliopts::TOption template class. 191 | */ 192 | class Option : protected cliopts_entry { 193 | public: 194 | bool passed() const { return found != 0; } 195 | void setPassed(bool val = true) { found = val ? 1 : 0; } 196 | int numSpecified() const { return found; } 197 | Option() { memset(this, 0, sizeof (cliopts_entry)); } 198 | private: 199 | friend class Parser; 200 | }; 201 | 202 | class EmptyPriv {}; 203 | 204 | /** 205 | * Option template class. This class is not meant to be used by applications 206 | * directly. Applications should use one of the template instantiations 207 | * below (e.g. cliopts::StringOption) 208 | * 209 | * @param T type returned to the application 210 | * @param Targ integer constant indicating the type of the C argument 211 | * @param Taccum raw destination type which will store the parsed value 212 | * @param Tpriv type of private data to be stored for type-specific processing 213 | */ 214 | template < 215 | typename T, 216 | cliopts_argtype_t Targ, 217 | typename Taccum, 218 | typename Tpriv = EmptyPriv 219 | > 220 | class TOption : public Option { 221 | 222 | private: 223 | typedef TOption Ttype; 224 | Taccum innerVal; /**< Pointer for cliopts_entry destination */ 225 | Tpriv priv; /**< Type-specific data */ 226 | public: 227 | 228 | /** 229 | * Construct a new option 230 | * @param shortname abbreviated short name 231 | * @param longname long ("GNU-style" name) 232 | * @param deflval default value to be used 233 | * @param helpstr Text explaining the option 234 | */ 235 | TOption(char shortname, const char *longname = NULL, 236 | T deflval = createDefault(), const char *helpstr = NULL) { 237 | 238 | memset((cliopts_entry *)this, 0, sizeof(cliopts_entry)); 239 | ktype = Targ; 240 | klong = longname; 241 | dest = &innerVal; 242 | 243 | abbrev(shortname); 244 | description(helpstr); 245 | setDefault(deflval); 246 | } 247 | 248 | /** 249 | * Construct a new option 250 | * @param longname the long ("GNU-Style") name. 251 | */ 252 | TOption(const char *longname) { 253 | memset((cliopts_entry *)this, 0, sizeof(cliopts_entry)); 254 | ktype = Targ; 255 | klong = longname; 256 | innerVal = createDefault(); 257 | dest = &innerVal; 258 | } 259 | 260 | /** 261 | * Copy constructor. This mainly exists to allow chaining (See example) 262 | * @param other the source option to copy 263 | */ 264 | TOption(TOption& other) { 265 | *(cliopts_entry*)this = *(cliopts_entry*) &other; 266 | innerVal = other.innerVal; 267 | dest = &innerVal; 268 | other.dest = NULL; 269 | doCopy(other); 270 | } 271 | 272 | /** 273 | * Set the default value for the option 274 | * @param val the default value 275 | * @return the option object, for method chaining. 276 | */ 277 | inline Ttype& setDefault(const T& val) { 278 | innerVal = val; 279 | return *this; 280 | } 281 | 282 | /** 283 | * Set the single-character switch 284 | * @param val the switch character, e.g. '-v' 285 | * @return the option object, for method chaining 286 | */ 287 | inline Ttype& abbrev(char val) { kshort = val; return *this; } 288 | 289 | /** 290 | * Set the description (or help string) for the option. 291 | * @param msg The help string e.g. "Increases verbosity" 292 | * @return the obtion object, for method chaining. 293 | */ 294 | inline Ttype& description(const char *msg) { help = msg; return *this; } 295 | 296 | /** 297 | * Set whether this option must appear 298 | * @param val boolean, set to true if required, false if optional 299 | * @return the option object, for method chaining 300 | */ 301 | inline Ttype& mandatory(bool val = true) { required = val; return *this; } 302 | 303 | /** 304 | * Set the value description string for the option value. 305 | * @param desc The short description string, e.g. "RETRIES" 306 | * @return the option object, for method chaining 307 | */ 308 | inline Ttype& argdesc(const char *desc) { vdesc = desc; return *this; } 309 | 310 | /** 311 | * Whether to hide this option in the help output 312 | * @param val true if the option should be hidden 313 | * @return the object object, for method chaining. 314 | */ 315 | inline Ttype& hide(bool val = true) { hidden = val; return *this; } 316 | 317 | /** 318 | * Returns the result object 319 | * @return a copy of the result object 320 | */ 321 | inline T result() { return (T)innerVal; } 322 | 323 | /** 324 | * Returns a reference to the result object 325 | * @return a reference to the result object. 326 | */ 327 | inline T& const_result() { return (T)innerVal; } 328 | 329 | operator T() { return result(); } 330 | 331 | protected: 332 | /** Called from within copy constructor */ 333 | inline void doCopy(TOption&) {} 334 | 335 | /** Create the default value for the option */ 336 | static inline Taccum createDefault() { return Taccum(); } 337 | }; 338 | 339 | typedef TOption StringOption; 343 | 344 | typedef TOption, 345 | CLIOPTS_ARGT_LIST, 346 | cliopts_list, 347 | std::vector > ListOption; 348 | 349 | typedef TOption BoolOption; 352 | 353 | typedef TOption UIntOption; 356 | 357 | typedef TOption ULongLongOption; 360 | 361 | typedef TOption IntOption; 364 | 365 | typedef TOption HexOption; 368 | 369 | typedef TOption FloatOption; 372 | 373 | // STRING ROUTINES 374 | template<> inline std::string& StringOption::const_result() { 375 | if (innerVal && passed()) { 376 | priv = innerVal; 377 | } 378 | return priv; 379 | } 380 | template<> inline std::string StringOption::result() { 381 | return const_result(); 382 | } 383 | template<> inline StringOption& StringOption::setDefault(const std::string& s) { 384 | priv = s; 385 | innerVal = priv.c_str(); 386 | return *this; 387 | } 388 | template<> inline void StringOption::doCopy(StringOption& other) { 389 | priv = other.priv; 390 | if (other.innerVal == other.priv.c_str()) { 391 | innerVal = priv.c_str(); 392 | } 393 | } 394 | template<> inline const char* StringOption::createDefault() { return ""; } 395 | 396 | // LIST ROUTINES 397 | template<> inline std::vector& ListOption::const_result() { 398 | if (priv.empty()) { 399 | for (size_t ii = 0; ii < innerVal.nvalues; ii++) { 400 | priv.push_back(innerVal.values[ii]); 401 | } 402 | } 403 | return priv; 404 | } 405 | template<> inline std::vector ListOption::result() { 406 | return const_result(); 407 | } 408 | 409 | // BOOL ROUTINES 410 | template<> inline BoolOption& BoolOption::setDefault(const bool& b) { 411 | innerVal = b ? 1 : 0; return *this; 412 | } 413 | template<> inline bool BoolOption::result() { 414 | return innerVal != 0 ? true : false; 415 | } 416 | 417 | /** 418 | * Parser class which contains one or more cliopts::Option objects. Options 419 | * should be added via the #addOption() member function. 420 | */ 421 | class Parser { 422 | public: 423 | /** 424 | * Construct a new parser 425 | * @param name the "program name" which is printed at the top of the 426 | * help message. 427 | */ 428 | Parser(const char *name = NULL) { 429 | memset(&default_settings, 0, sizeof default_settings); 430 | default_settings.progname = name; 431 | } 432 | 433 | /** 434 | * Adds an option to the parser. The option is then checked for presence 435 | * on the commandline (in #parse()). 436 | * @param opt the option to add. Note that the application is responsible 437 | * for keeping the option in valid memory. 438 | */ 439 | void addOption(Option *opt) { options.push_back(opt); } 440 | 441 | void addOption(Option& opt) { options.push_back(&opt); } 442 | 443 | /** 444 | * Parses the options from the commandline 445 | * @param argc number of arguments 446 | * @param argv list of arguments 447 | * @param standalone_args whether to accept (and store) positional arguments 448 | * (after all named options are processed). 449 | * @param min_standalone_args the minimum required standalone args. 450 | * @return true on parse success, false on parse failure 451 | */ 452 | bool parse(int argc, char **argv, const char *standalone_args = NULL, 453 | int min_standalone_args = 0) { 454 | std::vector ents; 455 | cliopts_extra_settings settings = default_settings; 456 | int lastix; 457 | 458 | for (unsigned ii = 0; ii < options.size(); ++ii) { 459 | ents.push_back(*options[ii]); 460 | } 461 | 462 | if (ents.empty()) { return false; } 463 | ents.push_back(Option()); 464 | const char **tmpargs = NULL; 465 | if (standalone_args) { 466 | tmpargs = new const char*[argc]; 467 | settings.restargs = tmpargs; 468 | settings.nrestargs = 0; 469 | settings.argstring_restargs = standalone_args; 470 | settings.min_restargs = min_standalone_args; 471 | } 472 | settings.show_defaults = 1; 473 | 474 | int rv = cliopts_parse_options(&ents[0], argc, argv, &lastix, &settings); 475 | 476 | if (tmpargs != NULL) { 477 | for (unsigned ii = 0; ii < settings.nrestargs; ii++) { 478 | restargs.push_back(tmpargs[ii]); 479 | } 480 | delete[] tmpargs; 481 | } 482 | 483 | // Copy the options back 484 | for (unsigned ii = 0; ii < options.size(); ii++) { 485 | *(cliopts_entry *)options[ii] = ents[ii]; 486 | } 487 | 488 | if (rv == 0 && lastix != 0) { 489 | for (; lastix < argc; lastix++) { 490 | restargs.push_back(argv[lastix]); 491 | } 492 | } 493 | 494 | return rv == 0; 495 | } 496 | 497 | /** 498 | * Get the list of any positional arguments found on the commandline 499 | * @return A list of positional arguments found. 500 | */ 501 | const std::vector& getRestArgs() { return restargs; } 502 | 503 | cliopts_extra_settings default_settings; 504 | private: 505 | std::vector options; 506 | std::vector restargs; 507 | Parser(Parser&); 508 | }; 509 | } // namespace 510 | #endif /* CLIOPTS_ENABLE_CXX */ 511 | 512 | #endif /* __cplusplus */ 513 | 514 | #endif /* CLIOPTS_H_ */ 515 | -------------------------------------------------------------------------------- /cliopts.c: -------------------------------------------------------------------------------- 1 | #ifndef _WIN32 2 | #include 3 | #include 4 | #else 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "cliopts.h" 16 | 17 | 18 | enum { 19 | CLIOPTS_ERR_SUCCESS, 20 | CLIOPTS_ERR_NEED_ARG, 21 | CLIOPTS_ERR_ISSWITCH, 22 | CLIOPTS_ERR_BADOPT, 23 | CLIOPTS_ERR_BAD_VALUE, 24 | CLIOPTS_ERR_UNRECOGNIZED 25 | }; 26 | 27 | struct cliopts_priv { 28 | cliopts_entry *entries; 29 | 30 | cliopts_entry *prev; 31 | cliopts_entry *current; 32 | struct cliopts_extra_settings *settings; 33 | 34 | char *errstr; 35 | int errnum; 36 | 37 | int argsplit; 38 | int wanted; 39 | 40 | char current_key[4096]; 41 | char current_value[4096]; 42 | }; 43 | 44 | enum { 45 | WANT_OPTION, 46 | WANT_VALUE, 47 | 48 | MODE_ERROR, 49 | MODE_RESTARGS, 50 | MODE_HELP 51 | }; 52 | 53 | #define INDENT " " 54 | 55 | #ifdef CLIOPTS_DEBUG 56 | 57 | #define cliopt_debug(...) \ 58 | fprintf(stderr, "(%s:%d) ", __func__, __LINE__); \ 59 | fprintf(stderr, __VA_ARGS__); \ 60 | fprintf(stderr, "\n") 61 | 62 | #else 63 | /** variadic macros not c89 */ 64 | static void cliopt_debug(void *unused, ...) { (void)unused; } 65 | #endif /* CLIOPT_DEBUG */ 66 | 67 | static int 68 | parse_option(struct cliopts_priv *ctx, const char *key); 69 | 70 | 71 | static int 72 | parse_value(struct cliopts_priv *ctx, const char *value); 73 | 74 | static void 75 | add_list_value(const char *src, size_t nsrc, cliopts_list *l) 76 | { 77 | char *cp = malloc(nsrc + 1); 78 | 79 | if (!l->nalloc) { 80 | l->nalloc = 2; 81 | l->values = malloc(l->nalloc * sizeof(*l->values)); 82 | } else { 83 | l->nalloc *= 1.5; 84 | l->values = realloc(l->values, sizeof(*l->values) * l->nalloc); 85 | } 86 | 87 | l->values[l->nvalues++] = cp; 88 | cp[nsrc] = '\0'; 89 | memcpy(cp, src, nsrc); 90 | } 91 | 92 | CLIOPTS_API 93 | void 94 | cliopts_list_clear(cliopts_list *l) 95 | { 96 | size_t ii; 97 | for (ii = 0; ii < l->nvalues; ii++) { 98 | free(l->values[ii]); 99 | } 100 | free(l->values); 101 | l->values = NULL; 102 | l->nvalues = 0; 103 | l->nalloc = 0; 104 | } 105 | 106 | /** 107 | * Various extraction/conversion functions for numerics 108 | */ 109 | 110 | #define _VERIFY_INT_COMMON(m1, m2) \ 111 | if (value == m1 || value > m2) { *errp = "Value too large"; return -1; } \ 112 | if (*endptr != '\0') { *errp = "Trailing garbage"; return -1; } 113 | 114 | static int 115 | extract_int(const char *s, void *dest, char **errp) 116 | { 117 | long int value; 118 | char *endptr = NULL; 119 | value = strtol(s, &endptr, 10); 120 | _VERIFY_INT_COMMON(LONG_MAX, INT_MAX) 121 | *(int*)dest = value; 122 | return 0; 123 | } 124 | 125 | static int 126 | extract_uint(const char *s, void *dest, char **errp) 127 | { 128 | unsigned long int value; 129 | char *endptr = NULL; 130 | value = strtoul(s, &endptr, 10); 131 | _VERIFY_INT_COMMON(ULONG_MAX, UINT_MAX) 132 | *(unsigned int*)dest = value; 133 | return 0; 134 | } 135 | 136 | #ifdef ULLONG_MAX 137 | static int 138 | extract_ulonglong(const char *s, void *dest, char **errp) 139 | { 140 | unsigned long long value; 141 | char *endptr = NULL; 142 | #ifdef _WIN32 143 | value = _strtoui64(s, &endptr, 10); 144 | #else 145 | value = strtoull(s, &endptr, 10); 146 | #endif 147 | _VERIFY_INT_COMMON(ULLONG_MAX, ULLONG_MAX); 148 | *(unsigned long long *)dest = value; 149 | return 0; 150 | } 151 | #else 152 | static int extract_ulonglong(const char *s, void *dest, char **errp) 153 | { 154 | *errp = "long long not available"; 155 | return -1; 156 | } 157 | #endif /* ULLONG_MAX */ 158 | 159 | static int 160 | extract_hex(const char *s, void *dest, char **errp) 161 | { 162 | unsigned long value; 163 | char *endptr = NULL; 164 | value = strtoul(s, &endptr, 16); 165 | _VERIFY_INT_COMMON(ULONG_MAX, UINT_MAX); 166 | *(unsigned int*)dest = value; 167 | return 0; 168 | } 169 | 170 | #undef _VERIFY_INT_COMMON 171 | 172 | static int 173 | extract_float(const char *s, void *dest, char **errp) 174 | { 175 | char dummy_buf[4096]; 176 | float value; 177 | if (sscanf(s, "%f%s", &value, dummy_buf) != 1) { 178 | *errp = "Found trailing garbage"; 179 | return -1; 180 | } 181 | *(float*)dest = value; 182 | return 0; 183 | } 184 | 185 | typedef int(*cliopts_extractor_func)(const char*, void*, char**); 186 | 187 | 188 | /** 189 | * This function tries to extract a single value for an option key. 190 | * If it successfully has extracted a value, it returns MODE_VALUE. 191 | * If the entry takes no arguments, then the current string is a key, 192 | * and it will return MODE_OPTION. On error, MODE_ERROR is set, and errp 193 | * will point to a string. 194 | * 195 | * @param entry The current entry 196 | * @param value the string which might be a value 197 | * @errp a pointer which will be populated with the address of the error, if any 198 | * 199 | * @return a MODE_* type 200 | */ 201 | static int 202 | parse_value(struct cliopts_priv *ctx, 203 | const char *value) 204 | { 205 | cliopts_entry *entry = ctx->current; 206 | 207 | size_t vlen = strlen(value); 208 | cliopts_extractor_func exfn = NULL; 209 | int exret; 210 | int is_option = 0; 211 | 212 | cliopt_debug("Called with %s, want=%d", value, ctx->wanted); 213 | 214 | if (ctx->argsplit) { 215 | if (vlen > 2 && strncmp(value, "--", 2) == 0) { 216 | is_option = 1; 217 | } else if (*value == '-') { 218 | is_option = 1; 219 | } 220 | } 221 | 222 | if (is_option) { 223 | ctx->errstr = "Expected option. Got '-' or '--' prefixed value " 224 | "(use = if this is really a value)"; 225 | ctx->errnum = CLIOPTS_ERR_NEED_ARG; 226 | return MODE_ERROR; 227 | } 228 | 229 | if (entry->ktype == CLIOPTS_ARGT_STRING) { 230 | char *vp = malloc(vlen+1); 231 | vp[vlen] = 0; 232 | strcpy(vp, value); 233 | *(char**)entry->dest = vp; 234 | return WANT_OPTION; 235 | } 236 | 237 | if (entry->ktype == CLIOPTS_ARGT_LIST) { 238 | add_list_value(value, vlen, (cliopts_list *)entry->dest); 239 | return WANT_OPTION; 240 | } 241 | 242 | if (entry->ktype == CLIOPTS_ARGT_FLOAT) { 243 | exfn = extract_float; 244 | } else if (entry->ktype == CLIOPTS_ARGT_HEX) { 245 | exfn = extract_hex; 246 | } else if (entry->ktype == CLIOPTS_ARGT_INT) { 247 | exfn = extract_int; 248 | } else if (entry->ktype == CLIOPTS_ARGT_UINT) { 249 | exfn = extract_uint; 250 | } else if (entry->ktype == CLIOPTS_ARGT_ULONGLONG) { 251 | exfn = extract_ulonglong; 252 | } else { 253 | fprintf(stderr, "Unrecognized type %d.\n", entry->ktype); 254 | return MODE_ERROR; 255 | } 256 | 257 | exret = exfn(value, entry->dest, &ctx->errstr); 258 | if (exret == 0) { 259 | return WANT_OPTION; 260 | } else { 261 | ctx->errnum = CLIOPTS_ERR_BAD_VALUE; 262 | } 263 | 264 | return MODE_ERROR; 265 | } 266 | 267 | /** 268 | * Like parse_value, except for keys. 269 | * 270 | * @param entries all option entries 271 | * @param key the current string from argv 272 | * @param errp a pointer which will be populated with the address of an error 273 | * string 274 | * 275 | * @param found_entry a pointer to be populated with the relevant entry 276 | * structure 277 | * @param kp a pointer which will be poplated with the address of the 'sanitized' 278 | * key string 279 | * 280 | * @param valp if the string is actually a key-value pair (i.e. --foo=bar) then 281 | * this will be populated with the address of that string 282 | * 283 | * @return MODE_OPTION if an option was found, MODE_VALUE if the current option 284 | * is a value, or MODE_ERROR on error 285 | */ 286 | static int 287 | parse_option(struct cliopts_priv *ctx, 288 | const char *key) 289 | { 290 | cliopts_entry *cur = NULL; 291 | int prefix_len = 0; 292 | unsigned ii = 0; 293 | const char *valp = NULL; 294 | size_t klen; 295 | 296 | klen = strlen(key); 297 | ctx->errstr = NULL; 298 | ctx->prev = ctx->current; 299 | ctx->current = NULL; 300 | 301 | cliopt_debug("Called with %s, want=%d", key, ctx->wanted); 302 | if (klen == 0) { 303 | ctx->errstr = "Got an empty string"; 304 | ctx->errnum = CLIOPTS_ERR_BADOPT; 305 | return MODE_ERROR; 306 | } 307 | 308 | /** 309 | * figure out what type of option it is.. 310 | * it can either be a -c, --long, or --long=value 311 | */ 312 | while (*key == '-') { 313 | key++; 314 | prefix_len++; 315 | klen--; 316 | } 317 | 318 | for (ii = 0; ii < klen; ii++) { 319 | if (key[ii] == '"' || key[ii] == '\'') { 320 | ii = klen; 321 | break; 322 | 323 | } else if (key[ii] == '=' && prefix_len == 2) { 324 | /* only split on '=' if we're called as '--' */ 325 | valp = key + (ii + 1); 326 | klen = ii; 327 | break; 328 | } 329 | } 330 | 331 | GT_PARSEOPT: 332 | memset(ctx->current_value, 0, sizeof(ctx->current_value)); 333 | memcpy(ctx->current_key, key, klen); 334 | ctx->current_key[ii] = '\0'; 335 | 336 | if (valp) { 337 | strcpy(ctx->current_value, valp); 338 | } 339 | 340 | if (prefix_len == 0 || prefix_len > 2) { 341 | if (ctx->settings->restargs) { 342 | key -= prefix_len; 343 | ctx->settings->restargs[ctx->settings->nrestargs++] = key; 344 | return WANT_OPTION; 345 | } else if (ctx->prev && ctx->prev->ktype == CLIOPTS_ARGT_NONE) { 346 | ctx->errstr = "Option does not accept a value"; 347 | ctx->errnum = CLIOPTS_ERR_ISSWITCH; 348 | } else { 349 | ctx->errstr = "Options must begin with either '-' or '--'"; 350 | ctx->errnum = CLIOPTS_ERR_BADOPT; 351 | } 352 | return MODE_ERROR; 353 | } 354 | 355 | /** 356 | * --help or -? 357 | */ 358 | 359 | if ( (prefix_len == 1 && *key == '?') || 360 | (prefix_len == 2 && strcmp(key, "help") == 0)) { 361 | return MODE_HELP; 362 | } 363 | 364 | /** 365 | * Bare -- 366 | */ 367 | if (prefix_len == 2 && *key == '\0') { 368 | if (ctx->settings->restargs) { 369 | 370 | } 371 | if (ctx->wanted == WANT_VALUE) { 372 | ctx->errnum = CLIOPTS_ERR_NEED_ARG; 373 | ctx->errstr = "Found bare '--', but value wanted"; 374 | return MODE_ERROR; 375 | } 376 | 377 | return MODE_RESTARGS; 378 | } 379 | 380 | for (cur = ctx->entries; cur->dest; cur++) { 381 | int optlen; 382 | if (prefix_len == 1) { 383 | if (cur->kshort == ctx->current_key[0]) { 384 | ctx->current = cur; 385 | break; 386 | } 387 | continue; 388 | } 389 | /** else, prefix_len is 2 */ 390 | if (cur->klong == NULL || 391 | (optlen = strlen(cur->klong) != klen) || 392 | strncmp(cur->klong, ctx->current_key, klen) != 0) { 393 | 394 | continue; 395 | } 396 | 397 | ctx->current = cur; 398 | break; 399 | } 400 | 401 | if (!ctx->current) { 402 | ctx->errstr = "Unknown option"; 403 | ctx->errnum = CLIOPTS_ERR_UNRECOGNIZED; 404 | return MODE_ERROR; 405 | } 406 | 407 | ctx->current->found++; 408 | if (ctx->current->ktype != CLIOPTS_ARGT_NONE) { 409 | ctx->wanted = WANT_VALUE; 410 | } 411 | 412 | if (ctx->current_value[0]) { 413 | /* --foo=bar */ 414 | if (ctx->current->ktype == CLIOPTS_ARGT_NONE) { 415 | ctx->errnum = CLIOPTS_ERR_ISSWITCH; 416 | ctx->errstr = "Option takes no arguments"; 417 | return MODE_ERROR; 418 | } else { 419 | return parse_value(ctx, ctx->current_value); 420 | } 421 | } 422 | 423 | if (ctx->current->ktype == CLIOPTS_ARGT_NONE) { 424 | *(char*)ctx->current->dest = 1; 425 | 426 | if (prefix_len == 1 && klen > 1) { 427 | /** 428 | * e.g. ls -lsh 429 | */ 430 | klen--; 431 | key++; 432 | 433 | /** 434 | * While we can also possibly recurse, this may be a security risk 435 | * as it wouldn't take much to cause a deep recursion on the stack 436 | * which will cause all sorts of nasties. 437 | */ 438 | goto GT_PARSEOPT; 439 | } 440 | return WANT_OPTION; 441 | 442 | } else if (prefix_len == 1 && klen > 1) { 443 | 444 | /* e.g. patch -p0 */ 445 | ctx->wanted = WANT_VALUE; 446 | return parse_value(ctx, key + 1); 447 | } 448 | return WANT_VALUE; 449 | } 450 | 451 | static char * 452 | get_option_name(cliopts_entry *entry, char *buf) 453 | { 454 | /* [-s,--option] */ 455 | char *bufp = buf; 456 | bufp += sprintf(buf, "["); 457 | if (entry->kshort) { 458 | bufp += sprintf(bufp, "-%c", entry->kshort); 459 | } 460 | if (entry->klong) { 461 | if (entry->kshort) { 462 | bufp += sprintf(bufp, ","); 463 | } 464 | bufp += sprintf(bufp, "--%s", entry->klong); 465 | } 466 | sprintf(bufp, "]"); 467 | return buf; 468 | } 469 | 470 | static int get_terminal_width(void) 471 | { 472 | #ifndef _WIN32 473 | struct winsize max; 474 | if (ioctl(0, TIOCGWINSZ, &max) != -1) { 475 | return max.ws_col; 476 | } else { 477 | return 80; 478 | } 479 | #else 480 | CONSOLE_SCREEN_BUFFER_INFO cbsi; 481 | GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cbsi); 482 | return cbsi.srWindow.Right - cbsi.srWindow.Left; 483 | #endif 484 | } 485 | 486 | static char* 487 | format_option_help(cliopts_entry *entry, 488 | char *buf, 489 | struct cliopts_extra_settings *settings) 490 | { 491 | char *bufp = buf; 492 | if (entry->kshort) { 493 | bufp += sprintf(bufp, " -%c ", entry->kshort); 494 | } 495 | 496 | #define _advance_margin(offset) \ 497 | while(bufp-buf < offset || *bufp) { \ 498 | if (!*bufp) { \ 499 | *bufp = ' '; \ 500 | } \ 501 | bufp++; \ 502 | } 503 | 504 | _advance_margin(4) 505 | 506 | if (entry->klong) { 507 | bufp += sprintf(bufp, " --%s ", entry->klong); 508 | } 509 | 510 | if (entry->vdesc) { 511 | bufp += sprintf(bufp, " <%s> ", entry->vdesc); 512 | } 513 | 514 | _advance_margin(35) 515 | #undef _advance_margin 516 | 517 | if (entry->help) { 518 | unsigned initial_indent = bufp - buf + 1; 519 | int curpos = initial_indent; 520 | const char *help_p = entry->help; 521 | 522 | for (; *help_p; help_p++, curpos++, bufp++) { 523 | 524 | if (curpos >= settings->line_max) { 525 | unsigned ii; 526 | if (!isspace(*help_p) && !isspace(*(help_p-1))) { 527 | *bufp = '-'; 528 | bufp++; 529 | } 530 | *bufp = '\n'; 531 | bufp++; 532 | 533 | for (ii = 0; ii < initial_indent+1; ii++, bufp++) { 534 | *bufp = ' '; 535 | } 536 | 537 | curpos = initial_indent; 538 | if (isspace(*help_p)) { 539 | bufp--; 540 | continue; 541 | } 542 | } 543 | *bufp = *help_p; 544 | } 545 | } 546 | 547 | *bufp = '\0'; 548 | return buf; 549 | } 550 | 551 | static void 552 | print_help(struct cliopts_priv *ctx, struct cliopts_extra_settings *settings) 553 | { 554 | cliopts_entry *cur; 555 | cliopts_entry helpent = { 0 }; 556 | char helpbuf[1024] = { 0 }; 557 | 558 | helpent.klong = "help"; 559 | helpent.kshort = '?'; 560 | helpent.help = "this message"; 561 | 562 | fprintf(stderr, "Usage:\n"); 563 | fprintf(stderr, " %s %s", settings->progname, settings->argstring); 564 | 565 | if (settings->argstring_restargs) { 566 | fprintf(stderr, " %s", settings->argstring_restargs); 567 | } 568 | 569 | fprintf(stderr, "\n\n"); 570 | if (settings->shortdesc) { 571 | fprintf(stderr, "%s", settings->shortdesc); 572 | fprintf(stderr, "\n"); 573 | } 574 | 575 | 576 | for (cur = ctx->entries; cur->dest; cur++) { 577 | if (cur->hidden) { 578 | continue; 579 | } 580 | 581 | memset(helpbuf, 0, sizeof(helpbuf)); 582 | format_option_help(cur, helpbuf, settings); 583 | fprintf(stderr, INDENT "%s", helpbuf); 584 | 585 | 586 | if (settings->show_defaults && !cur->required) { 587 | fprintf(stderr, " [Default="); 588 | 589 | switch (cur->ktype) { 590 | case CLIOPTS_ARGT_STRING: 591 | fprintf(stderr, "'%s'", (cur->dest && *(char **)cur->dest) ? 592 | *(char**)cur->dest : ""); 593 | break; 594 | case CLIOPTS_ARGT_LIST: { 595 | size_t ii; 596 | cliopts_list *l = (cliopts_list *)cur->dest; 597 | for (ii = 0; ii < l->nvalues; ii++) { 598 | fprintf(stderr, "'%s'", l->values[ii]); 599 | if (ii != l->nvalues-1) { 600 | fprintf(stderr, ", "); 601 | } 602 | } 603 | break; 604 | } 605 | case CLIOPTS_ARGT_FLOAT: 606 | fprintf(stderr, "%0.2f", *(float*)cur->dest); 607 | break; 608 | case CLIOPTS_ARGT_HEX: 609 | fprintf(stderr, "0x%x", *(int*)cur->dest); 610 | break; 611 | case CLIOPTS_ARGT_INT: 612 | fprintf(stderr, "%d", *(int*)cur->dest); 613 | break; 614 | case CLIOPTS_ARGT_UINT: 615 | fprintf(stderr, "%u", *(unsigned int*)cur->dest); 616 | break; 617 | #ifdef ULLONG_MAX 618 | case CLIOPTS_ARGT_ULONGLONG: 619 | fprintf(stderr, "%llu", *(unsigned long long*)cur->dest); 620 | break; 621 | #endif 622 | case CLIOPTS_ARGT_NONE: 623 | fprintf(stderr, "%s", *(int*)cur->dest ? "TRUE" : "FALSE"); 624 | break; 625 | default: 626 | fprintf(stderr, "Unknown option type '%d'", (int)cur->ktype); 627 | break; 628 | } 629 | fprintf(stderr, "]"); 630 | } 631 | fprintf(stderr, "\n"); 632 | } 633 | memset(helpbuf, 0, sizeof(helpbuf)); 634 | fprintf(stderr, INDENT "%s\n", 635 | format_option_help(&helpent, helpbuf, settings)); 636 | 637 | } 638 | 639 | static void 640 | dump_error(struct cliopts_priv *ctx) 641 | { 642 | fprintf(stderr, "Couldn't parse options: %s\n", ctx->errstr); 643 | if (ctx->errnum == CLIOPTS_ERR_BADOPT) { 644 | fprintf(stderr, "Bad option: %s", ctx->current_key); 645 | } else if (ctx->errnum == CLIOPTS_ERR_BAD_VALUE) { 646 | fprintf(stderr, "Bad value '%s' for %s", 647 | ctx->current_value, 648 | ctx->current_key); 649 | } else if (ctx->errnum == CLIOPTS_ERR_UNRECOGNIZED) { 650 | fprintf(stderr, "No such option: %s", ctx->current_key); 651 | } else if (ctx->errnum == CLIOPTS_ERR_ISSWITCH) { 652 | char optbuf[64] = { 0 }; 653 | fprintf(stderr, "Option %s takes no arguments", 654 | get_option_name(ctx->prev, optbuf)); 655 | } 656 | fprintf(stderr, "\n"); 657 | 658 | } 659 | 660 | CLIOPTS_API 661 | int 662 | cliopts_parse_options(cliopts_entry *entries, 663 | int argc, 664 | char **argv, 665 | int *lastidx, 666 | struct cliopts_extra_settings *settings) 667 | { 668 | /** 669 | * Now let's build ourselves a 670 | */ 671 | int curmode; 672 | int ii, ret = 0, lastidx_s = 0; 673 | struct cliopts_priv ctx = { 0 }; 674 | struct cliopts_extra_settings default_settings = { 0 }; 675 | 676 | if (!lastidx) { 677 | lastidx = &lastidx_s; 678 | } 679 | 680 | ctx.entries = entries; 681 | 682 | if (!settings) { 683 | settings = &default_settings; 684 | settings->show_defaults = 1; 685 | } 686 | if (!settings->progname) { 687 | settings->progname = argv[0]; 688 | } 689 | if (!settings->argstring) { 690 | settings->argstring = "[OPTIONS...]"; 691 | } 692 | settings->nrestargs = 0; 693 | 694 | if (!settings->line_max) { 695 | settings->line_max = get_terminal_width() - 3; 696 | } 697 | 698 | ii = (settings->argv_noskip) ? 0 : 1; 699 | 700 | if (ii >= argc) { 701 | *lastidx = 0; 702 | ret = 0; 703 | goto GT_CHECK_REQ; 704 | return 0; 705 | } 706 | 707 | curmode = WANT_OPTION; 708 | ctx.wanted = curmode; 709 | ctx.settings = settings; 710 | 711 | for (; ii < argc; ii++) { 712 | 713 | if (curmode == WANT_OPTION) { 714 | curmode = parse_option(&ctx, argv[ii]); 715 | } else if (curmode == WANT_VALUE) { 716 | curmode = parse_value(&ctx, argv[ii]); 717 | } 718 | 719 | if (curmode == MODE_ERROR) { 720 | if (settings->error_nohelp == 0) { 721 | dump_error(&ctx); 722 | } 723 | ret = -1; 724 | break; 725 | } else if (curmode == MODE_HELP) { 726 | if (settings->help_noflag) { 727 | /* ignore it ? */ 728 | continue; 729 | } 730 | 731 | print_help(&ctx, settings); 732 | exit(0); 733 | 734 | } else if (curmode == MODE_RESTARGS) { 735 | ii++; 736 | break; 737 | } else { 738 | ctx.wanted = curmode; 739 | } 740 | } 741 | 742 | *lastidx = ii; 743 | 744 | if (curmode == WANT_VALUE) { 745 | ret = -1; 746 | 747 | if (settings->error_nohelp == 0) { 748 | fprintf(stderr, 749 | "Option %s requires argument\n", 750 | ctx.current_key); 751 | } 752 | goto GT_RET; 753 | } 754 | 755 | if (settings->argstring_restargs && 756 | settings->nrestargs < settings->min_restargs) { 757 | ret = -1; 758 | 759 | if (settings->error_nohelp == 0) { 760 | fprintf(stderr, 761 | "Required arguments: %s\n", 762 | settings->argstring_restargs); 763 | } 764 | goto GT_RET; 765 | } 766 | 767 | GT_CHECK_REQ: 768 | { 769 | cliopts_entry *cur_ent; 770 | for (cur_ent = entries; cur_ent->dest; cur_ent++) { 771 | char entbuf[128] = { 0 }; 772 | if (cur_ent->found || cur_ent->required == 0) { 773 | continue; 774 | } 775 | 776 | ret = -1; 777 | if (settings->error_nohelp) { 778 | goto GT_RET; 779 | } 780 | 781 | fprintf(stderr, "Required option %s missing\n", 782 | get_option_name(cur_ent, entbuf)); 783 | } 784 | } 785 | 786 | GT_RET: 787 | if (ret == -1) { 788 | if (settings->error_nohelp == 0) { 789 | print_help(&ctx, settings); 790 | } 791 | if (settings->error_noexit == 0) { 792 | exit(EXIT_FAILURE); 793 | } 794 | } 795 | return ret; 796 | } 797 | --------------------------------------------------------------------------------