aboutsummaryrefslogtreecommitdiffstats
path: root/libsimple-arg.h
diff options
context:
space:
mode:
Diffstat (limited to 'libsimple-arg.h')
-rw-r--r--libsimple-arg.h234
1 files changed, 225 insertions, 9 deletions
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 <string.h>
-
+/**
+ * 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