summaryrefslogtreecommitdiff
path: root/doc/option_parsing.rst
diff options
context:
space:
mode:
Diffstat (limited to 'doc/option_parsing.rst')
-rw-r--r--doc/option_parsing.rst326
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;
- }