aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libsimple-arg.h234
-rw-r--r--man0/libsimple-arg.h.0325
l---------man3/ARG.3libsimple1
l---------man3/ARGALT.3libsimple1
l---------man3/ARGBEGIN.3libsimple1
l---------man3/ARGBEGIN3.3libsimple1
l---------man3/ARGEND.3libsimple1
l---------man3/ARGHERE.3libsimple1
l---------man3/ARGMAPLONG.3libsimple1
l---------man3/ARGNULL.3libsimple1
l---------man3/ARGNUM.3libsimple1
l---------man3/FLAG.3libsimple1
l---------man3/LFLAG.3libsimple1
l---------man3/NOFLAGS.3libsimple1
l---------man3/NUSAGE.3libsimple1
l---------man3/SUBARGBEGIN.3libsimple1
l---------man3/TESTLONG.3libsimple1
l---------man3/USAGE.3libsimple1
18 files changed, 566 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
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 <libsimple-arg.h>
+
+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 <libsimple-arg.h>
+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 <stdio.h> ,
+.IR <stdlib.h> ,
+and
+.IR <string.h> ,
+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