diff options
Diffstat (limited to 'doc/option_parsing.rst')
| -rw-r--r-- | doc/option_parsing.rst | 326 |
1 files changed, 210 insertions, 116 deletions
diff --git a/doc/option_parsing.rst b/doc/option_parsing.rst index fa928e0..a824559 100644 --- a/doc/option_parsing.rst +++ b/doc/option_parsing.rst @@ -12,13 +12,11 @@ Characteristics: * recognition of the double dash as option list terminator * offers POSIX strictness where the option list terminates at the first non-option argument -* option passthrough (conceptuall only works for options taking no argument, - or when the argument is joined to a long option with a '=') -* the parse function is one-shot; there is no context object (like popt), - no global state (like getopt) and no ``while`` loop (either of the two others) -* exclusively uses an option table -* value storing is performed through pointers in the option table -* or user-provided callbacks can be invoked per option +* the parse function is one-shot; there is no state, just a result set +* value storing is possible in different ways + * using pointers with type declarators in the option table + * with user-provided callbacks invoked per option + * by specifying the ITER_OPTS flag and iterating after HX_getopt6 Synopsis @@ -38,7 +36,7 @@ Synopsis const char *help, *htyp; }; - int HX_getopt5(const struct HXoption *options_table, char **argv, int *new_argc, char ***new_argv, unsigned int flags); + int HX_getopt6(const struct HXoption *options_table, int argc, char **argv, struct HXopt6_result *result, unsigned int flags); The various fields of ``struct HXoption`` are: @@ -117,6 +115,29 @@ Type map allocation so that subsequently modifying the original argument string in any way will not falsely propagate. + HXTYPE_STRING is deprecated: When a user passes e.g. ``-S foo -S bar`` + to HX_getopt, ptr would be assigned twice before control returns back + to the HX_getopt caller, thus leaking the memory for "foo" leaks. We + cannot retroactively make the assign routine call free(ptr) inbetween, + as some code might have assigned ptr a static buffer before the call to + HX_getopt, e.g.: + + .. code-block:: c + + char *p = "foo"; // default to foo + options_table[] = { + {0,'S',HXTYPE_STRING,&p,...}, + }; + HX_getopt(...); + // p is always valid, but may point to either static or allocated + // storage. [Also assumes a program that never frees these + // strings for brevity.] + +``HXTYPE_STRP`` + The argument string pointer is stored in ``*(char **)ptr``. + No allocation occurs, but you are responsible for ensuring lifetime + adherence. + ``HXTYPE_STRDQ`` The argument string is duplicated to a new memory region and the resulting pointer is added to the given HXdeque. Note that you often @@ -268,23 +289,23 @@ Invoking the parser .. code-block:: c - int HX_getopt5(const struct HXoption *options_table, char **argv, int *new_argc, char **new_argv, unsigned int flags); - int HX_getopt(const struct HXoption *options_table, int *argc, char ***argv, unsigned int flags); + struct HXopt6_result { + int nargs; + const char **uarg; + char **dup_argv; + }; -``HX_getopt5`` is the central parsing function. ``options_table`` specifies -the options that the parser will recognize. ``argv`` must be a NULL-terminated -array of C strings. + int HX_getopt6(const struct HXoption *options_table, int argc, char **argv, struct HXopt6_result *result, unsigned int flags); + void HX_getopt6_clean(struct HXopt6_result *); -If ``new_argv`` is non-NULL, the leftover arguments will be output as a new -string vector on success. (That array can be freed with ``HX_zvecfree``). If -``new_argc`` is non-NULL, the argument count for new_argv will be output too. +``HX_getopt6`` is the central parsing function. ``options_table`` specifies the +options that the parser will recognize. ``argv`` must be a vector of C strings, +and ``argc`` be the count of strings that should be processed at most. ``argc`` +may be -1, in which case argc is auto-computed from ``argv``, and in this case, +argv must be NULL-terminated. The ``flags`` argument control the general behavior of ``HX_getopt``: -``HXOPT_PTHRU`` - “Passthrough mode”. Any unknown options are passed through into - ``new_argv``. - ``HXOPT_QUIET`` Do not print any diagnostics when encountering errors in the user's input. @@ -299,12 +320,42 @@ The ``flags`` argument control the general behavior of ``HX_getopt``: ``HXOPT_RQ_ORDER`` Specifying this option terminates option processing when the first non-option argument in argv is encountered. This behavior is also - implicit when the environment variable ``POSIXLY_CORRECT`` is set. - -``HXOPT_KEEP_ARGV`` - Do not set ``*new_argc`` and ``*new_argv`` at all. - -The return value can be one of the following: + implicit when the environment variable ``POSIXLY_CORRECT`` is set + (and ``HXOPT_ANY_ORDER`` is not used). + +``HXOPT_ANY_ORDER`` + Specifying this option allows mixing of options and non-options, + basically the opposite of the strict POSIX order. + +``HXOPT_CONST_INPUT`` + Declaration by the user that elements in input argv must *not* be + reordered by the parser. + +``HXOPT_ITER_OPTS`` + ``result->desc`` will be filled with pointers to the definitions of the + parsed options. ``result->oarg`` will be filled with pointers to the + option argument strings (potentially %nullptr if the option did not + take anything). ``result->nopts`` will be filled with the option count. + +``HXOPT_ITER_ARGS`` + ``result->uarg`` will be filled with pointers to leftover arguments + (pointing into the memory regions of the original argv), and + ``result->nargs`` will contain the string count. uarg does *not* + contain NULL sentinel, so you cannot iterate with something like ``for + (const char **p = result.uarg; p != nullptr && *p != nullptr; ++p)`` + but must use ``for (int uidx = 0; uidx < result.nargs; ++uidx)``. + +``HXOPT_ITER_OA`` + Shortcut for ``HXOPT_ITER_OPTS | HXOPT_ITER_ARGS``. + +``HXOPT_DUP_ARGS`` + ``result->dup_argv`` will be filled with copies of leftover arguments, + and ``result->nargs`` will contain the string count. dup_argv will + include the original argv[0]. dup_argv will also include a NULL + sentinel (not counted in nargs). You can move ``dup_argv`` out of the + result struct and free it yourself with ``HX_zvecfree`. + +The return value of HX_getopt6 can be one of the following: ``HXOPT_ERR_SUCCESS`` Parsing was successful. @@ -325,17 +376,15 @@ The return value can be one of the following: ``HXOPT_ERR_AMBIG`` An abbreviation of a long option was ambiguous. +``HXOPT_ERR_FLAGS`` + HX_getopt6 was called with a ``flags`` value that contained illegal or + silly bit combinations. + negative non-zero Failure on behalf of lower-level calls; errno. -``HX_getopt`` is an older API where ``argv`` is both used for input and output. -It recognizes additional flags/has additional behavior: - -``HXOPT_KEEP_ARGV`` - ``argc`` and ``argv`` is not updated. - -``HXOPT_DESTROY_OLD`` - Call ``HX_zvecfree`` on ``argv`` before updating it. +Upon HXOPT_ERR_SUCCESS, ``HX_getopt6_clean`` must be called to release +resources. Pitfalls @@ -358,11 +407,12 @@ The following is an example of a possible pitfall regarding ``HXTYPE_STRDQ``: .help = "Add name"}, HXOPT_TABLEEND, }; - if (HX_getopt5(options_table, *argv, &argc, &argv, + struct HXopt6_result result; + if (HX_getopt6(options_table, -1, *argv, &result, HXOPT_USAGEONERR) != HXOPT_ERR_SUCCESS) return EXIT_FAILURE; /* ... */ - HX_zvecfree(argv); + HX_getopt6_clean(&result); return EXIT_SUCCESS; } @@ -379,38 +429,32 @@ Limitations The HX option parser has been influenced by both popt and Getopt::Long, but eventually, there are differences: -* Long options with a single dash (``-foo bar``). This unsupported - syntax clashes easily with support for option bundling or squashing. In case - of bundling, ``-foo`` might actually be ``-f -o -o``, or ``-f oo`` in case of - squashing. It also introduces redundant ways to specify options, which is not - in the spirit of the author. +* Long options with a single dash (``-foo bar``) are not supported in HXopt. + This syntax clashes easily with support for option bundling or squashing. In + case of bundling, ``-foo`` might actually be ``-f -o -o``, or ``-f oo`` in + case of squashing. It also introduces redundant ways to specify options, + which is not in the spirit of the author. -* Options using a ``+`` as a prefix, as in ``+foo``. Xterm for - example uses it as a way to negate an option. In the author's opinion, using - one character to specify options is enough — by GNU standards, a negator is - named ``--no-foo``. +* Options using a ``+`` as a prefix, as in ``+foo`` are not supported in HXopt. + Xterm for example uses it as a way to negate an option. In the author's + opinion, using one character to specify options is enough — by GNU standards, + a negator is named ``--no-foo``. -* Table nesting like implemented in popt. HXopt has no provision for nested - tables, as the need has not come up yet. It does however support chained - processing. You cannot do nested tables even with callbacks, as the new argv - array is only put in place shortly before ``HX_getopt`` returns. +* Table nesting (like in popt) is not supported in HXopt. The need + has not come up yet. It does however support some forms of chained + processing, e.g. by using the option terminator, "--". Examples ======== -Basic example -------------- - -The following code snippet should provide an equivalent of the -GNU getopt sample.[#f5] - -.. [#f5] http://www.gnu.org/software/libtool/manual/libc/Example-of-Getopt.html\#Example-of-Getopt +Storing through pointers +------------------------ .. code-block:: c #include <stdio.h> - #include <stdilb.h> + #include <stdlib.h> #include <libHX/option.h> int main(int argc, char **argv) @@ -418,7 +462,6 @@ GNU getopt sample.[#f5] int aflag = 0; int bflag = 0; char *cflag = NULL; - struct HXoption options_table[] = { {.sh = 'a', .type = HXTYPE_NONE, .ptr = &aflag}, {.sh = 'b', .type = HXTYPE_NONE, .ptr = &bflag}, @@ -427,20 +470,121 @@ GNU getopt sample.[#f5] HXOPT_TABLEEND, }; - if (HX_getopt5(options_table, argv, &argc, &argv, - HXOPT_USAGEONERR) != HXOPT_ERR_SUCCESS) + if (HX_getopt6(options_table, argc, argv, nullptr, + HXOPT_USAGEONERR) != HXOPT_ERR_SUCCESS) { + free(cflag); return EXIT_FAILURE; + } printf("aflag = %d, bflag = %d, cvalue = %s\n", - aflag, bflag, cvalue); + aflag, bflag, cflag != NULL ? cflag : "(null)"); + free(cflag); + return EXIT_SUCCESS; + } + +Note how HXTYPE_STRING in conjunction with ``.ptr=&cflag`` will allocate a +buffer that needs to be freed. + +Storing via iteration +--------------------- + + #include <stdio.h> + #include <stdlib.h> + #include <libHX/option.h> - while (*++argv != NULL) - printf("Non-option argument %s\n", *argv); + int main(int argc, char **argv) + { + int aflag = 0; + int bflag = 0; + char *cflag = NULL; + struct HXoption options_table[] = { + {.sh = 'a', .type = HXTYPE_NONE}, + {.sh = 'b', .type = HXTYPE_NONE}, + {.sh = 'c', .type = HXTYPE_STRING}, + HXOPT_AUTOHELP, + HXOPT_TABLEEND, + }; + + struct HXopt6_result result; + if (HX_getopt6(options_table, argc, argv, &result, + HXOPT_USAGEONERR | HXOPT_ITER_OPTS) != HXOPT_ERR_SUCCESS) + return EXIT_FAILURE; + for (int i = 1; i < result.nopts; ++i) { + switch (result.desc[i]->sh) { + case 'a': + aflag = 1; + break; + case 'b': + bflag = 1; + break; + case 'c': + cflag = result.oarg[i]; + break; + } + } + printf("aflag = %d, bflag = %d, cvalue = %s\n", + aflag, bflag, cflag ? cflag : "(null)"); + HX_getopt6_clean(&result); + return EXIT_SUCCESS; + } + +Note that the pointers in ``oarg`` point to the original argv and so should not +be freed. Upon success of HX_getopt6, HX_getopt6_clean must be called. + +Obtaining non-option arguments +------------------------------ + +.. code-block:: c + + #include <stdio.h> + #include <stdlib.h> + #include <libHX/option.h> + + int main(int argc, char **argv) + { + int aflag = 0; + int bflag = 0; + char *cflag = NULL; + struct HXoption options_table[] = { + {.sh = 'a', .type = HXTYPE_NONE, .ptr = &aflag}, + {.sh = 'b', .type = HXTYPE_NONE, .ptr = &bflag}, + {.sh = 'c', .type = HXTYPE_STRING, .ptr = &cflag}, + HXOPT_AUTOHELP, + HXOPT_TABLEEND, + }; - HX_zvecfree(argv); + struct HXopt6_result result; + if (HX_getopt6(options_table, argc, argv, &result, + HXOPT_USAGEONERR | HXOPT_ITER_ARGS) != HXOPT_ERR_SUCCESS) { + free(cflag); + return EXIT_FAILURE; + } + printf("aflag = %d, bflag = %d, cvalue = %s\n", + aflag, bflag, cflag); + for (int i = 1; i < result.nargs; ++i) + printf("Non-option argument %s\n", result.uarg[i]); + free(cflag); + HX_getopt6_clean(&result); return EXIT_SUCCESS; } +C++ extension +------------- + +.. code-block:: c++ + + { + struct HXopt6_auto_result result; + auto ret = HX_getopt6(&table, argc, argv, &result, + HXOPT_USAGEONERR | HXOPT_ITER_ARGS); + if (ret != HXOPT_ERR_SUCCESS) + return ret; + } + + +For C++ mode, a struct "HXopt6_auto_result" is offered with a constructor for +zero initialization and a destructor invoking HX_getopt6_clean. + Verbosity levels ---------------- @@ -504,8 +648,8 @@ Mask operations What this options table does is ``cpu_mask &= ~x`` and ``net_mask |= y``, the classic operations of clearing and setting bits. -Support for non-standard actions --------------------------------- +Callbacks +--------- Supporting additional types or custom storage formats is easy, by simply using ``HXTYPE_STRING``, ``NULL`` as the data pointer (usually by not specifying it @@ -539,53 +683,3 @@ the callback function in ``cb``. .uptr = &number, .help = "Do this or that", HXOPT_TABLEEND, }; - -Chained argument processing ---------------------------- - -On the first run, only ``--cake`` and ``--fruit`` is considered, which is then -used to select the next set of accepted options. - -.. code-block:: c - - static int get_cakes(int *argc, char ***argv) - { - struct HXoption cake_table[] = { - ... - }; - if (HX_getopt5(cake_table, *argv, &argc, &argv, - HXOPT_USAGEONERR) != HXOPT_ERR_SUCCESS) - return EXIT_FAILURE; - /* ... */ - HX_zvecfree(argv); - return EXIT_SUCCESS; - } - - static int fruit_main(int argc, char **argv) - { - struct HXoption fruit_table[] = { - ... - }; - if (HX_getopt5(fruit_table, *argv, &argc, &argv, - HXOPT_PTHRU) != HXOPT_ERR_SUCCESS) - return EXIT_FAILURE; - /* ... */ - HX_zvecfree(argv); - return EXIT_SUCCESS; - } - - int main(int argc, char **argv) - { - int cake = 0, fruit = 0; - struct HXoption option_table[] = { - {.ln = "cake", .type = HXTYPE_NONE, .ptr = &cake}, - {.ln = "fruit", .type = HXTYPE_NONE, .ptr = &fruit}, - HXOPT_TABLEEND, - }; - if (HX_getopt5(option_table, *argv, &argc, &argv, - HXOPT_PTHRU) != HXOPT_ERR_SUCCESS) - return EXIT_FAILURE; - int ret = cake ? cake_main(argc, argv) : fruit_main(argc, argv); - HX_zvecfree(argv); - return EXIT_FAILURE; - } |
