From 1949e8b65aea6ddffb408bde7846220f4ab9bc9b Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Fri, 9 Nov 2018 18:52:18 +0100 Subject: Document most of libsimple-arg.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- libsimple-arg.h | 234 +++++++++++++++++++++++++++++-- man0/libsimple-arg.h.0 | 325 ++++++++++++++++++++++++++++++++++++++++++++ man3/ARG.3libsimple | 1 + man3/ARGALT.3libsimple | 1 + man3/ARGBEGIN.3libsimple | 1 + man3/ARGBEGIN3.3libsimple | 1 + man3/ARGEND.3libsimple | 1 + man3/ARGHERE.3libsimple | 1 + man3/ARGMAPLONG.3libsimple | 1 + man3/ARGNULL.3libsimple | 1 + man3/ARGNUM.3libsimple | 1 + man3/FLAG.3libsimple | 1 + man3/LFLAG.3libsimple | 1 + man3/NOFLAGS.3libsimple | 1 + man3/NUSAGE.3libsimple | 1 + man3/SUBARGBEGIN.3libsimple | 1 + man3/TESTLONG.3libsimple | 1 + man3/USAGE.3libsimple | 1 + 18 files changed, 566 insertions(+), 9 deletions(-) create mode 100644 man0/libsimple-arg.h.0 create mode 120000 man3/ARG.3libsimple create mode 120000 man3/ARGALT.3libsimple create mode 120000 man3/ARGBEGIN.3libsimple create mode 120000 man3/ARGBEGIN3.3libsimple create mode 120000 man3/ARGEND.3libsimple create mode 120000 man3/ARGHERE.3libsimple create mode 120000 man3/ARGMAPLONG.3libsimple create mode 120000 man3/ARGNULL.3libsimple create mode 120000 man3/ARGNUM.3libsimple create mode 120000 man3/FLAG.3libsimple create mode 120000 man3/LFLAG.3libsimple create mode 120000 man3/NOFLAGS.3libsimple create mode 120000 man3/NUSAGE.3libsimple create mode 120000 man3/SUBARGBEGIN.3libsimple create mode 120000 man3/TESTLONG.3libsimple create mode 120000 man3/USAGE.3libsimple diff --git a/libsimple-arg.h b/libsimple-arg.h index d63a7ec..5a05cc8 100644 --- a/libsimple-arg.h +++ b/libsimple-arg.h @@ -7,7 +7,10 @@ #include - +/** + * The zeroth command line argument, the name of the process, + * set by the command line parsing macros + */ extern char *argv0; @@ -18,9 +21,68 @@ struct longopt { }; -#define ARGBEGIN ARGBEGIN3(1, argc, argv) +/** + * `ARGBEGIN {} ARGEND;` creates a switch statement + * instead a loop that parses the command line arguments + * according to the POSIX specification for default + * behaviour (extensions of the behaviour is possible) + * + * This macro requires that the variables `argc` and + * `argv` are defined and that `argv[argc]` is `NULL`, + * `argc` shall be a non-negative `int` that tells + * how many elements (all non-`NULL`) are available in + * `argv`, the list of command line arguments + * + * When parsing stops, `argc` and `argv` are updated + * shuch that all parsed arguments are removed; the + * contents of `argv` will not be modified, rather + * the pointer `argv` will be updated to `&argv[n]` + * where `n` is the number of parsed elements in `argv` + * + * Inside `{}` in `ARGBEGIN {} ARGEND;` there user + * shall specify `case` statements for each recognised + * command line option, and `default` for unrecognised + * option. For example: + * + * ARGBEGIN { + * case 'a': + * // handle -a + * break; + * case 'b': + * // handle -b + * break; + * case ARGNUM: + * // handle -0, -1, -2, ..., -9 + * break; + * default: + * // print usage information for other flags + * usage(); + * } ARGEND; + */ +#define ARGBEGIN ARGBEGIN3(1, argc, argv) + +/** + * `SUBARGBEGIN {} ARGEND;` is similar to + * `ARGBEGIN {} ARGEND;`, however, `argv0` + * is not set to `argv[0]`, instead `argv[0]` + * is handled like any other element in `argv` + */ #define SUBARGBEGIN ARGBEGIN3(0, argc, argv) +/** + * Flexible alternative to `ARGBEGIN` + * + * @param WITH_ARGV0 If 0, behave like `SUBARGBEGIN`, + * otherwise, behave like `ARGBEGIN` + * @param argc:int The number of elements in `argv`, will be + * update to the number of arguments remaining + * after parsing stopped + * @param argv:char ** The command line arguments to parse, + * `argv[argc]` must be `NULL`; will be updated, + * via offseting, to the arguments remaining + * after parsing stopped, `argv[argc]` will + * still be `NULL` + */ #define ARGBEGIN3(WITH_ARGV0, argc, argv)\ do {\ char flag_, *lflag_, *arg_;\ @@ -47,6 +109,9 @@ struct longopt { again_ = 0;\ switch (flag_) { +/** + * TODO doc ARGMAPLONG + */ #define ARGMAPLONG(LONGOPTS)\ for (i_ = 0; (LONGOPTS)[i_].long_flag; i_++) {\ if (TESTLONG((LONGOPTS)[i_].long_flag, (LONGOPTS)[i_].with_arg)) {\ @@ -58,6 +123,29 @@ struct longopt { if (again_)\ break +/** + * Allows flags to start with another symbol than '-' + * + * Usage example: + * ARGBEGIN { + * case 'a': // handle -a + * break; + * default: + * usage(); + * } ARGALT('+') { + * case 'a': // handle +a + * break; + * default: + * usage(); + * } ARGALT('/') { + * case 'a': // handle /a + * break; + * default: + * usage(); + * } ARGEND; + * + * @param SYMBOL:char The symbol flags should begin with + */ #define ARGALT(SYMBOL)\ }\ } while (again_);\ @@ -80,6 +168,9 @@ struct longopt { again_ = 0;\ switch (flag_) { +/** + * Refer to `ARGBEGIN`, `SUBARGBEGIN`, and `ARGBEGIN3` + */ #define ARGEND\ }\ } while (again_);\ @@ -97,15 +188,109 @@ struct longopt { } while (0) -#define ARGNUM '0': case '1': case '2': case '3': case '4':\ - case '5': case '6': case '7': case '8': case '9' +/** + * `case ARGNUM` creates a switch statement case for each digit + */ +#define ARGNUM '0': case '1': case '2': case '3': case '4':\ + case '5': case '6': case '7': case '8': case '9' + +/** + * Get the flag character, for example in `case 'a'`, + * 'a' is returned + * + * @return :char The option's identifying character + */ +#define FLAG() (flag_) + +/** + * Get the entire argument that is being parsed + * + * Note that an argument can contain multiple options + * and it can contain the last options value but the + * value can also be in the next argument + * + * @return :char * The current command line argument + */ +#define LFLAG() (lflag_) -#define FLAG() (flag_) -#define LFLAG() (lflag_) -#define ARG() (arg_ ? (brk_ = 1, arg_) : (usage(), (char *)0)) +/** + * Get the current option's value, if it + * does not have a value, call `usage` + * (which terminates the process) + * + * Using this macro lets the parser knows + * that the option has a value + * + * @return :char * The option's value, never `NULL` + */ +#define ARG() (arg_ ? (brk_ = 1, arg_) : (usage(), NULL)) + +/** + * Get the current option's value, if the option + * does not have a value, `NULL` is returned + * + * Note that the value may appear at the next + * argument (next element in `argv`) which in that + * case is returned + * + * Using this macro lets the parser knows + * that the option has a value + * + * @return :char * The option's value, `NULL` if + * the option does not have a value + */ +#define ARGNULL() (arg_ ? (brk_ = 1, arg_) : NULL) + +/** + * Get the remaining part of the current command + * line argument (element in `argv`) — as well as + * the character that specifies the flag — as the + * value of the argument + * + * Using this macro lets the parser knows + * that the option has a value + * + * Usage example: + * + * char *arg; + * ARGBEGIN { + * case ARGNUM: + * arg = ARGHERE(); + * // `arg` is the number after '-', for example, + * // if the command line contains the argument + * // "-12345", `arg` will be `12345` + * break; + * case 'n': + * arg = &ARGHERE()[1]; + * if (*arg) { + * // flag 'n' has a value (`argv`) + * } else { + * // flag 'n' does not have a value + * } + * default: + * usage(); + * } ARGEND; + * + * @return :char * The option's value include the flag + * character, never `NULL` or "" + */ #define ARGHERE() (brk_ = 1, argv[0]) +/** + * `NOFLAG(x);` is an optimised shorthand for + * + * ARGBEGIN { + * default: + * usage(); + * } ARGEND; + * + * if (x) + * usage(); + * + * @param ... If non-zero, the `usage` function + * will be called + */ #define NOFLAGS(...)\ do {\ if (*argv)\ @@ -119,6 +304,9 @@ struct longopt { } while (0) +/** + * TODO doc NOFLAGS + */ #define TESTLONG(FLG, WARG)\ ((WARG)\ ? ((!strncmp(lflag_, (FLG), n_ = strlen(FLG)) && lflag_[n_] == '=')\ @@ -136,9 +324,37 @@ struct longopt { : 0)) +/** + * Define the function `static void usage(void)` + * that prints the error message + * "usage: %s %s\n", argv0, SYNOPSIS + * or + * "usage: %s\n", argv0 + * if `SYNOPSIS` is `NULL` or "", and then + * terminate the process with exit value 1 + * + * This macro also defines `char *argv0` + * + * @param SYNOPSIS Description of the command line argument syntax + */ #define USAGE(SYNOPSIS)\ NUSAGE(1, SYNOPSIS) + +/** + * Define the function `static void usage(void)` + * that prints the error message + * "usage: %s %s\n", argv0, SYNOPSIS + * or + * "usage: %s\n", argv0 + * if `SYNOPSIS` is `NULL` or "", and then + * terminate the process with exit value `STATUS` + * + * This macro also defines `char *argv0` + * + * @param SYNOPSIS Description of the command line argument syntax + * @parma STATUS The exit value for the process + */ #if defined(__GNUC__) || defined(__clang__) # define NUSAGE(STATUS, SYNOPSIS)\ __attribute__((noreturn))\ @@ -148,7 +364,7 @@ struct longopt { fprintf(stderr, "usage: %s%s%s\n", argv0, *syn ? " " : "", syn);\ exit(STATUS);\ }\ - char *argv0 + char *argv0 = NULL #else # define NUSAGE(STATUS, SYNOPSIS)\ static void usage(void)\ @@ -157,7 +373,7 @@ struct longopt { fprintf(stderr, "usage: %s%s%s\n", argv0, *syn ? " " : "", syn);\ exit(STATUS);\ }\ - char *argv0 + char *argv0 = NULL #endif diff --git a/man0/libsimple-arg.h.0 b/man0/libsimple-arg.h.0 new file mode 100644 index 0000000..d6cfd2e --- /dev/null +++ b/man0/libsimple-arg.h.0 @@ -0,0 +1,325 @@ +.TH LIBSIMPLE-ARG.H 0 2018-11-08 libsimple +.SH NAME +libsimple-arg.h \- command line argument parsing header for libsimple +.SH SYNOPSIS +.nf +#include + +extern char *\fIargv0\fP; + +struct longopt { + const char *long_flag; + char short_flag; + int with_arg; +}; + +#define ARGBEGIN ARGBEGIN3(1, argc, argv) +#define SUBARGBEGIN ARGBEGIN3(0, argc, argv) +#define ARGBEGIN3(\fIWITH_ARGV0\fP, \fIargc\fP, \fIargv\fP) /* implementation omitted */ +#define ARGMAPLONG(\fILONGOPTS\fP) /* implementation omitted */ +#define ARGALT(\fISYMBOL\fP) /* implementation omitted */ +#define ARGEND /* implementation omitted */ +#define ARGNUM /* implementation omitted */ +#define FLAG() /* implementation omitted */ +#define LFLAG() /* implementation omitted */ +#define ARG() /* implementation omitted */ +#define ARGHERE() /* implementation omitted */ +#define NOFLAGS(...) /* implementation omitted */ +#define TESTLONG(\fIFLG\fP, \fIWARG\fP) /* implementation omitted */ +#define USAGE(\fISYNOPSIS\fP) /* implementation omitted */ +#define NUSAGE(\fISTATUS\fP, \fISYNOPSIS\fP) /* implementation omitted */ +.fi +.SH DESCRIPTION +The +.I +header define a collection of macros for parsing the +command line arguments, thatis, the data in the +two first parameters of the +.IR main () +function, according to the default behaviour specified +by POSIX (extensions of the specification is available). +It also includes the three headers the macros need: +.IR , +.IR , +and +.IR , +additionally in defines the variable +.I argv0 +which the macros set to the command line argument with +index zero which is the process was invoked with, normally, +but not always, this the filename of or the path to the +binary the process runs; one common exception to this is +for login shells: when a user logs in, the shell that is +started is started with the filename of the shell's binary +prefix with a dash +.RB ( - ) +as the zeroth command line argument. Assuming the second +argument in the +.IR main () +function is named +.IR argv , +the program should set +.I argv0 +to +.I argv[0] +if the process may call a either of the +.BR libsimple_eprintf (3), +.BR libsimple_enprintf (3), +.BR libsimple_weprintf (3), +.BR libsimple_veprintf (3), +.BR libsimple_venprintf (3), +and +.BR libsimple_vweprintf (3) +functions before the either of the +.B ARGBEGIN +macro is used, and must do so if the function +.BR usage (), +defined by the +.B USAGE +and +.B NUSAGE +macros, is called before the +.B ARGBEGIN +macro is used. +.PP +Applications should normally parse or validate the command +line arguments and print usage information on usage error. +The macros +.BI USAGE( SYNOPSIS ) +and +.BI NUSAGE( STATUS ,\ SYNOPSIS ) +define a +.RI ( static ) +function, name +.IR usage (), +that print the usage information and terminate the process, +the also declare the variable +.IR argv0 . +.IR usage () +does take any arguments. +.IR usage () +defined by +.B USAGE +terminate the process with exit value 1. +.IR usage () +defined by +.B NUSAGE +terminate the process with exit value +.IR STATUS. +Both print the an error message to the standard error +in the following format: +.nf + + \fB\(dqusage: %s %s\en\(dq,\fP \fIargv0\fP\fB, \fP\fISYNOPSIS\fP + +.fi +However, if +.I SYNOPSIS +is +.B NULL +or the empty string, the error message printed in the +following format. +.nf + + \fB\(dqusage: %s\en\(dq,\fP \fIargv0\fP +.fi +.PP +The macros +.B ARGBEGIN +and +.B ARGEND +are used to parse command line arguments, they use the variable +named +.I argc +(number of command line arguments) +and +.I argv +(command line arguments) and set +.I argv0 +to +.IR argv[0] , +.I argv[argc] +must be +.BR NULL , +which is the case for the +.IR main () +function. The +value that should be stored to +.I argv0 +is not available at +.IR argv[0] , +the macro +.B SUBARGBEGIN +should be used instead of +.BR ARGBEGIN . +These macros work similar to a switch statement in a loop: +.nf + + ARGBEGIN { + case 'a': + /* handle -a */ + break; + case 'b': + /* handle -b */ + break; + case ARGNUM: + /* handle -0, -1, -2, ..., -9 */ + break; + default: + /* print usage information for other flags */ + usage(); + } ARGEND; + +.fi +After +.BR ARGEND , +.I argc +and +.I argv +are set to only contain the remaining, unparsed, arguments. +.PP +The +.BR ARGBEGIN3 () +macro can be used instead of the +.B ARGBEGIN +and +.B SUBARGBEGIN +macros for greater flexibility. If +.I WITH_ARGV0 +is non-zero it behaves like the +.B ARGBEGIN +macro, otherwise it behaves like the +.B SUBARGBEGIN +macro. The +.BR ARGBEGIN3 () +also lets the user choose which variables to use as +.I argc +and +.IR argv . +.PP +If the application should support flags starting with +another symbol than a dash +.RB ( - ), +the macro +.BR ARGALT () +can be used. Its argument, +.IR SYMBOL , +should be +.B char +and it is used thusly: +.nf + + ARGBEGIN { + case 'a': + /* handle -a */ + break; + default: + usage(); + } ARGALT('+') { + case 'a': + /* handle +a */ + break; + default: + usage(); + } ARGALT('/') { + case 'a': + /* handle /a */ + break; + default: + usage(); + } ARGEND; +.fi +.PP +In multicase statement (any case statement actually), the +.BR FLAG () +macro can be used to identify which flag is being parsed. +For example, in +.I case 'a' +.BR FLAG () +returns +.IR 'a' . +The +.BR LFLAG () +macro returns the entire element in +.IR argv . +Note that one such element can contain multiple flags, +and the it can contain the flags argument, but the +argument can also be in the next element. +.PP +The macros +.BR ARG (), +.BR ARGNULL (), +and +.BR ARGHERE () +are used the get the value provided with an option, one +of the must be used to let the parse know that the option +should have a value. +.BR ARG () +returns the current option's value, or terminates the +by calling +.IR usage () +if the option does not have a value. +.BR ARGNULL () +returns the current option's value, or returns +.B NULL +if the option does not have a value. +.BR ARGHERE () +Remaining part of the current argument in the command +line, including the character the specifies the flag. +It can be used together with +.B ARGNUM +to parse numerical options (a dash followed by a +multidigit number). It can also be used to parse +options where the value is optional be must appear +in the same argument, for example, if the current +argument is +.BR -n1000 , +.BR ARGHERE () +will return +.B \(dqn1000\(dq +when the +.B -n +option is parsed, but if the argument is just +.BR -n , +.BR ARGHERE () +will return +.BR \(dqn\(dq , +even if the next argument is +.BR 1000 . +.PP +If your application, does not recognise any options, +but should still have like normal programs and support +.BR -- , +the macro +.BR NOFLAGS () +can be used instead of the +.B ARGBEGIN +and +.B ARGEND +macros. It behaves just like +.nf + + ARGBEGIN { + default: + usage(); + } ARGEND; + +.fi +but is optimised. Additionally, if the last argument +is non-zero, the +.IR usage () +function is called. All arguments evaluated exactly +once, or if flags are used, never. So if your application +should not have any options or operands, +.I NOFLAGS(argc) +can be used. +.SH APPLICATION USAGE +None. +.SH RATIONALE +None. +.SH FUTURE DIRECTIONS +None. +.SH NOTES +None. +.SH SEE ALSO +.BR libsimple.h (0) diff --git a/man3/ARG.3libsimple b/man3/ARG.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/ARG.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/ARGALT.3libsimple b/man3/ARGALT.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/ARGALT.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/ARGBEGIN.3libsimple b/man3/ARGBEGIN.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/ARGBEGIN.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/ARGBEGIN3.3libsimple b/man3/ARGBEGIN3.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/ARGBEGIN3.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/ARGEND.3libsimple b/man3/ARGEND.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/ARGEND.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/ARGHERE.3libsimple b/man3/ARGHERE.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/ARGHERE.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/ARGMAPLONG.3libsimple b/man3/ARGMAPLONG.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/ARGMAPLONG.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/ARGNULL.3libsimple b/man3/ARGNULL.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/ARGNULL.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/ARGNUM.3libsimple b/man3/ARGNUM.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/ARGNUM.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/FLAG.3libsimple b/man3/FLAG.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/FLAG.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/LFLAG.3libsimple b/man3/LFLAG.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/LFLAG.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/NOFLAGS.3libsimple b/man3/NOFLAGS.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/NOFLAGS.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/NUSAGE.3libsimple b/man3/NUSAGE.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/NUSAGE.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/SUBARGBEGIN.3libsimple b/man3/SUBARGBEGIN.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/SUBARGBEGIN.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/TESTLONG.3libsimple b/man3/TESTLONG.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/TESTLONG.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file diff --git a/man3/USAGE.3libsimple b/man3/USAGE.3libsimple new file mode 120000 index 0000000..d63eec5 --- /dev/null +++ b/man3/USAGE.3libsimple @@ -0,0 +1 @@ +../man0/libsimple-arg.h.0 \ No newline at end of file -- cgit v1.2.3-70-g09d2