--- /dev/null
+/* -*-c-*-
+ *
+ * Keyword argument handling
+ *
+ * (c) 2015 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Sensible Object Design, an object system for C.
+ *
+ * SOD is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * SOD is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with SOD; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef KEYWORD_H
+#define KEYWORD_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+
+/*----- Function annotations ----------------------------------------------*/
+
+/* Some macros are defined for annotating functions. They may improve
+ * compiler diagnostics when used properly. They should be included as part
+ * of the function's declaration specifiers.
+ *
+ * * @KWCALL@ marks a function as expecting keyword arguments. The
+ * compiler may check that there are an odd number of arguments, that the
+ * even-numbered (starting from zero) arguments have pointer-to-character
+ * type, and that the final argument is null.
+ *
+ * * @KW__NORETURN@ marks a function as never returning. Applied to
+ * @kw_unknown@ and its various friends. Users are not expected to use
+ * this.
+ */
+
+#if defined(__GNUC__)
+# define KW__GCC_VERSION_P(maj, min) \
+ (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min)))
+# if KW__GCC_VERSION_P(2, 5)
+# define KW__NORETURN __attribute__((__noreturn__))
+# endif
+# if KW__GCC_VERSION_P(4, 0)
+# define KWCALL __attribute__((__sentinel__))
+# endif
+#endif
+
+/* --- Trivial definitions, if we don't have compiler support --- */
+
+#ifndef KW__NORETURN
+# define KW__NORETURN
+#endif
+
+#ifndef KWCALL
+# define KWCALL
+#endif
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* A keyword/value pair. A vector of these can be passed as the value of the
+ * special keyword `kw.tab'. This is a rather cumbersome way of constructing
+ * lists of keyword arguments for a function in a programmatic way.
+ */
+struct kwval {
+ const char *kw; /* The keyword name, as a string */
+ const void *val; /* A pointer to the keyword value */
+};
+
+/* A table of keyword/value pairs. This is used as the value of a `kw.tab'
+ * argument which is itself in a @struct kwval@ table, since it's not
+ * possible to store both the vector and length directly.
+ */
+struct kwtab {
+ const struct kwval *v; /* The address of the vector */
+ size_t n; /* The number of keywords */
+};
+
+/* The type of unknown-keyword handler functions. */
+typedef void kw_unkhookfn(const char */*set*/, const char */*kw*/);
+
+/*----- Global variables --------------------------------------------------*/
+
+/* A global hook function for handling unknown-keyword errors. The default
+ * function prints a message to the standard error stream and aborts.
+ *
+ * The hook function must not return. It's not possible to recover from an
+ * unknown-keyword error while parsing a variable-length argument tail, since
+ * it's impossible to find out what type the corresponding argument value is.
+ *
+ * Having a single global hook isn't really very satisfactory, but a fully
+ * adequate solution gets complicated quickly. An external library will
+ * eventually be available to pick up the slack.
+ */
+extern kw_unkhookfn *kw_unkhook;
+
+/*----- Argument list macros ----------------------------------------------*/
+
+/* These macros is intended to be conveniences rather than a proper
+ * abstraction. Functions with more complicated interfaces, and their
+ * callers, will have to make their own arrangements.
+ */
+
+/* --- @KWTAIL@ --- *
+ *
+ * Arguments: ---
+ *
+ * Use: Marker to be included in a function prototype (at the end of
+ * the argument list) to indicate that the function accepts
+ * keyword arguments. It is acceptable for the @KWTAIL@ marker
+ * to be only thing in the argument list.
+ */
+
+#define KWTAIL const char *kwfirst_, ...
+
+/* --- @KWARGS@ --- *
+ *
+ * Arguments: @body@ = a sequence of @K(kw, value)@ macro calls, without
+ * separators
+ *
+ * Use: A package of actual keyword arguments. In C89, the @body@
+ * must not be empty: to pass no keywords, use @NO_KWARGS@
+ * instead.
+ */
+
+#define KWARGS(body) body KW__END
+#define KW__END ((const char *)0)
+
+/* --- @NO_KWARGS@ --- *
+ *
+ * Arguments: ---
+ *
+ * Use: Special marker to include in an actual argument list to
+ * indicate that no keyword arguments are to be passed. See
+ * @KWARGS@ above.
+ */
+
+#define NO_KWARGS KW__END, KW__END
+ /* Slight hack. The @KWCALL@ macro sets GCC and similar compilers up to
+ * check for a sentinal null pointer at the end of the variable-length
+ * argument tail. Alas, if there are no keywords at all, then the null
+ * terminator ends up in the @kwfirst_@ argument, and the tail is propetly
+ * empty, with the result that the compiler gives an annoying warning.
+ * Supplying an extra argument here is obviously harmless, and makes the
+ * otherwise useful warning go away in this case where it's not wanted.
+ */
+
+/* --- @K@ --- *
+ *
+ * Arguments: @kw@ = keyword name, as an unquoted token list
+ * @val@ = keyword value, as an expression
+ *
+ * Use: Bundles a keyword @kw@ and value @val@ together.
+ */
+
+#define K(kw, val) #kw, (val),
+
+/* --- @KW_VALIST@ --- *
+ *
+ * Arguments: @va_list ap@ = argument-list extraction state
+ *
+ * Use: Passes a reified variable-length argument tail into a keyword
+ * function.
+ */
+
+#define K_VALIST(ap) "kw.valist", &(ap),
+
+/* --- @KW_TAB@ --- *
+ *
+ * Arguments: @const struct kwval *v@ = base address of argument vector
+ * @size_t n@ = length of argument vector
+ *
+ * Use: Passes an vector of keyword arguments into a keyword
+ * function.
+ */
+
+#define K_TAB(v, n) "kw.tab", (v), (size_t)(n),
+
+/*----- Keyword set definitions -------------------------------------------*
+ *
+ * A `keyword set' describes the collection of keyword arguments to be
+ * accepted by a function (or group of functions). Keyword sets have names,
+ * which are C identifiers. A keyword set must not be empty: use
+ * @kw_parseempty@ instead of this machinery when defining a function which
+ * may later accept keyword arguments but which currently doesn't define any.
+ *
+ * A keyword set definition is a macro of a single argument, conventionally
+ * named `@_@'. The macro for a keyword set called @foo@ is named
+ * @foo_KWSET@. It should consist of a triple @_(type, key, dflt)@ for each
+ * keyword argument, where @type@ is the C type of the argument, @key@ is the
+ * name of the argument (as a C identifier), and @dflt@ is an expression
+ * (valid to use in an aggregate initializer) to provide the default value
+ * for the argument. The @type@ must be such that @type *@ is the type of a
+ * pointer to object of @type@.
+ */
+
+/* --- @KWSET_STRUCT@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ *
+ * Use: Defines the keyword set argument structure @struct
+ * set_kwargs@.
+ *
+ * The structure is used to communicate argument values between
+ * a function accepting keyword arguments and the argument
+ * parsing function constructed by @KWSET_PARSEFN@. It contains
+ * two members for each keyword argument: one with the name of
+ * the argument and the appropriate type to hold its value; the
+ * other is a one-bit-wide bitfield named with a `_suppliedp'
+ * suffix, and is set to indicate whether the caller provided a
+ * a value for the corresponding keyword argument.
+ */
+
+#define KWSET_STRUCT(set) \
+ struct set##_kwargs { \
+ set##_KWSET(KWSET__SUPPLIEDP) \
+ set##_KWSET(KWSET__STRUCTMEM) \
+ }
+#define KWSET__SUPPLIEDP(type, name, dflt) unsigned name##_suppliedp : 1;
+#define KWSET__STRUCTMEM(type, name, dflt) type name;
+
+/* --- @KWSET_PARSEFN@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ *
+ * Use: Defines the keyword argument parsing function @set_kwparse@.
+ * A call to this macro may be preceded by a storage-class
+ * specifier, e.g., @static@, to specify the linkage for the
+ * parsing function's name.
+ *
+ * This function takes five arguments:
+ *
+ * @struct set_kwargs *kw@ = pointer to keyword set argument
+ * structure to fill in
+ * @const char *kwfirst@ = first keyword argument name from the
+ * variable-length argument tail, or null if the
+ * argument tail is empty
+ * @va_list *ap@ = pointer to variable-length tail extraction
+ * state object
+ * @const struct kwval *v@ = base address of argument vector
+ * @size_t n@ = length of argument vector
+ *
+ * The `kwparse' function extracts keyword arguments from the
+ * argument tail (via @*ap@), and then the vector @v@; it
+ * updates the structure @*kw@ with their values, and sets the
+ * `_suppliedp' flags accordingly. It's unusual to call the
+ * `kwparse' function with both a nontrivial argument tail and
+ * vector, but the effect is nonetheless well-defined.
+ *
+ * The argument tail consists of alternating keyword argument
+ * names (as pointers to null-terminated strings) and values,
+ * terminated by a null pointer. The argument values are simply
+ * copied into the structure. Passing the @kwfirst@ argument
+ * separately allows functions to declare an explicit positional
+ * argument for the first keyword name, which is useful if the
+ * function has no other positional arguments.
+ *
+ * The argument vector consists of @struct kwval@ items, each of
+ * which contains a keyword name (as a pointer to a null-
+ * terminated string) and the address of its value. Argument
+ * values are again copied into the structure. Note that a
+ * vector doesn't store the arguments directly. This makes them
+ * rather cumbersome to set up, but the benefit is a simple and
+ * uniform approach for all keyword arguments.
+ *
+ * The main application for argument vectors is for `front-end'
+ * functions which want to pass on some subset of their keywords
+ * to another function. There isn't currently any macrology
+ * provided for achieving this, but it's not especially
+ * difficult.
+ *
+ * There are (currently) two special keyword arguments, whose
+ * names are not valid identifiers. Future additions will also
+ * have names beginning `kw.'.
+ *
+ * * `kw.valist' -- the corresponding argument has type
+ * @va_list *@, and represents an entire variable-length
+ * argument tail to process, including the first keyword
+ * name.
+ *
+ * * `kw.tab' -- the corresponding argument is a vector of
+ * @struct kwval@ items to process. In a variable-length
+ * argument tail, this is passed as two arguments: the base
+ * address of the vector, and the length (as a @size_t@).
+ * In an argument vector, this is passed instead as a value
+ * of type @struct kwtab@.
+ *
+ * If an unknown keyword is encountered while parsing, the
+ * function @kw_unknown@ is called.
+ *
+ * The keyword argument `kw.unknown' will never be defined.
+ */
+
+#define KWSET_PARSEFN(set) \
+ void set##_kwparse(struct set##_kwargs *kw, \
+ const char *kwfirst, va_list *ap, \
+ const struct kwval *v, size_t n) \
+ { \
+ const char *k, *kk; \
+ va_list *aap; \
+ const struct kwtab *t; \
+ const struct kwval *vv; \
+ size_t nn; \
+ \
+ for (k = kwfirst; k; k = va_arg(*ap, const char *)) { \
+ if (!strcmp(k, "kw.valist")) { \
+ aap = va_arg(*ap, va_list *); \
+ kk = va_arg(*aap, const char *); \
+ set##_kwparse(kw, kk, aap, 0, 0); \
+ } else if (!strcmp(k, "kw.tab")) { \
+ vv = va_arg(*ap, const struct kwval *); \
+ nn = va_arg(*ap, size_t); \
+ set##_kwparse(kw, 0, 0, vv, nn); \
+ } \
+ set##_KWSET(KWSET__ARGVA) \
+ else kw_unknown(#set, k); \
+ } \
+ \
+ while (n) { \
+ if (!strcmp(v->kw, "kw.valist")) { \
+ aap = *(va_list *const *)v->val; \
+ kk = va_arg(*aap, const char *); \
+ set##_kwparse(kw, kk, aap, 0, 0); \
+ } else if (!strcmp(v->kw, "kw.tab")) { \
+ t = (const struct kwtab *)v->val; \
+ set##_kwparse(kw, 0, 0, t->v, t->n); \
+ } \
+ set##_KWSET(KWSET__ARGTAB) \
+ else kw_unknown(#set, v->kw); \
+ v++; n--; \
+ } \
+ }
+#define KWSET__ARGVA(type, name, dflt) \
+ else if (!strcmp(k, #name)) { \
+ kw->name##_suppliedp = 1; \
+ kw->name = va_arg(*ap, type); \
+ }
+#define KWSET__ARGTAB(type, name, dflt) \
+ else if (!strcmp(v->kw, #name)) { \
+ kw->name##_suppliedp = 1; \
+ kw->name = *(type const *)v->val; \
+ }
+
+/*----- Defining keyword-accepting functions ------------------------------*/
+
+/* --- @KWDECL@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ * @kw@ = the name for the keyword argument structure value
+ *
+ * Use: Declares and initializes a keyword argument structure object
+ * @kw@. The `_suppliedp' members are initially all zero; the
+ * argument value members are set to their default values as
+ * specified in the keyword set definition macro.
+ */
+
+#define KWDECL(set, kw) \
+ struct set##_kwargs kw = \
+ { set##_KWSET(KWSET__SPINIT) set##_KWSET(KWSET__DFLT) }
+#define KWSET__SPINIT(type, name, dflt) 0,
+#define KWSET__DFLT(type, name, dflt) dflt,
+
+/* --- @KW_PARSE@, @KW_PARSE_EMPTY@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ * @kw@ = the name of the keyword argument structure
+ * @kwfirst@ = the first keyword argument name from the
+ * variable-length argument tail (and, therefore, the
+ * final positional argument)
+ *
+ * Use: Invokes the appropriate `kwparse' function to process the
+ * function's variable-length argument tail as keyword
+ * arguments.
+ *
+ * It is recommended that functions avoid allocating resources
+ * or making observable changes to program state until they have
+ * successfully parsed their keyword arguments.
+ *
+ * It is not possible to define an empty keyword argument set.
+ * If a function currently accepts no keyword argumets, but
+ * wants to reserve the ability to accept them later, then it
+ * should use @KW_PARSE_EMPTY@ (or, better, @KWPARSE_EMPTY@
+ * below). The keyword argument set name here is used only for
+ * diagnostic purposes, and need not (and probably should not)
+ * correspond to a keyword-set definition macro.
+ */
+
+#define KW_PARSE(set, kw, kwfirst) do { \
+ va_list ap_; \
+ va_start(ap_, kwfirst); \
+ set##_kwparse(&(kw), kwfirst, &ap_, 0, 0); \
+ va_end(ap_); \
+} while (0)
+
+#define KW_PARSE_EMPTY(set, kwfirst) do { \
+ va_list ap_; \
+ va_start(ap_, kwfirst); \
+ kw_parseempty(#set, kwfirst, &ap, 0, 0); \
+ va_end(ap_); \
+} while (0)
+
+/* --- @KWPARSE@, @KWPARSE_EMPTY@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ *
+ * Use: All-in-one keyword parsing for simple cases.
+ *
+ * This declares a keyword argument structure literally named
+ * @kw@, and parses the function's variable-length argument tail
+ * on the assumption that the function's argument list prototype
+ * contains a @KWTAIL@ marker.
+ *
+ * It is recommended that functions avoid allocating resources
+ * or making observable changes to program state until they have
+ * successfully parsed their keyword arguments.
+ *
+ * In C89, this macro must be placed precisely between the
+ * declarations at the start of the function body, and the
+ * statements after them.
+ *
+ * It is not possible to define an empty keyword argument set.
+ * If a function currently accepts no keyword argumets, but
+ * wants to reserve the ability to accept them later, then it
+ * should use @KWPARSE_EMPTY@. The keyword argument set name
+ * here is used only for diagnostic purposes, and need not (and
+ * probably should not) correspond to a keyword-set definition
+ * macro.
+ */
+
+#define KWPARSE(set) KWDECL(set, kw); KW_PARSE(set, kw, kwfirst_)
+
+#define KWPARSE_EMPTY(set) KW_PARSE_EMPTY(set, kwfirst_)
+
+/* --- @KW_COUNT@ --- *
+ *
+ * Arguments: @set@ = the keyword set name
+ *
+ * Use: Expands to the number of keywords defined in the @set@.
+ */
+
+#define KW_COUNT(set) (0u set##_KWSET(KW__COUNT))
+#define KW__COUNT(type, name, dflt) + 1u
+
+/* --- @KW_COPY@ --- *
+ *
+ * Arguments: @fromset@ = the source keyword set name
+ * @toset@ = the destination keyword set name
+ * @kw@ = the source keyword argument structure
+ * @v@ = the destination vector
+ * @n@ = next free index in vector
+ *
+ * Use: Copies arguments from the source structure @kw@ into the
+ * vector @v@. The structure @kw@ must have type @struct
+ * fromset_kwargs *@. The argument @v@ must have type @struct
+ * kwval *@ (after array-to- pointer decay), and there must be a
+ * variable @v_n@ of sufficiently large integral type suitably
+ * initialized. Elements of the vector, starting with element
+ * @n@, will be filled in with those keyword arguments defined
+ * in @toset@ -- which must be a subset of @srcsrc@ from @kw@
+ * for which the `_suppliedp' flags are set. The @val@ members
+ * will point directly into the @kw@ structure. The @n@
+ * counter will be updated, and on completion will contain the
+ * index of the first unused entry in the vector.
+ */
+
+#define KW_COPY(fromset, toset, kw, v, n) do { \
+ const struct fromset##_kwargs *kw_ = &(kw); \
+ struct kwval *v_ = (v); \
+ size_t n_ = (n); \
+ toset##_KWSET(KW__COPY) \
+ (n) = n_; \
+} while (0)
+
+#define KW__COPY(type, name, dflt) \
+ if (kw_->name##_suppliedp) { \
+ v_[n_].kw = #name; \
+ v_[n_].val = &kw_->name; \
+ n_++; \
+ }
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @kw_unknown@ --- *
+ *
+ * Arguments: @const char *set@ = the keyword set name, as a string
+ * @const char *kw@ = the unknown keyword argument, as a string
+ *
+ * Returns: Doesn't.
+ *
+ * Use: Called when an unrecognized keyword argument is encountered
+ * during parsing. This calls the @kw_unkhook@ with the same
+ * arguments. Recovery via @longjmp@ or a similar machanism is
+ * acceptable.
+ */
+
+extern KW__NORETURN void kw_unknown(const char */*set*/, const char */*kw*/);
+
+/* --- @kw_defunknown@ --- *
+ *
+ * Arguments: @const char *set@ = keyword set name
+ * @const char *kw@ = the offending keyword name
+ *
+ * Returns: Doesn't.
+ *
+ * Use: This is the default @kw_unkhook@ hook function.
+ *
+ * In a hosted implementation, this function reports an internal
+ * error to stderr about the unknown keyword and calls @abort@.
+ * It is an implementation responsibility for freestanding
+ * implementations wanting to use this keyword argument
+ * mechanism.
+ */
+
+extern KW__NORETURN kw_unkhookfn kw_defunknown;
+
+/* --- @kw__hookfailed@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: Doesn't.
+ *
+ * Use: Called by @kw_unknown@ if the @kw_unkhook@ hook function
+ * returns.
+ *
+ * User code is not expected to call this function. It exists
+ * as an implementation respensibility for freestanding
+ * implementations wanting to use this keyword argument
+ * mechanism.
+ */
+
+extern KW__NORETURN void kw__hookfailed(void);
+
+/* --- @kw_parseempty@ --- *
+ *
+ * Arguments: @const char *set@ = the keyword set name, as a string
+ * @const char *kwfirst@ = the first keyword argument name
+ * @va_list *ap@ = pointer to argument-tail extraction state
+ * @const struct kwval *v@ = base address of argument vector
+ * @size_t n@ = size of argument vector
+ *
+ * Returns: ---
+ *
+ * Use: Goes through the motions of parsing keyword arguments, but
+ * doesn't in fact handle any other than the standard ones
+ * described above (see @KWSET_PARSEFN@). This is useful when a
+ * function doesn't currently define any keyword arguments but
+ * wants to reserve the right to define some in the future.
+ * (The usual machinery can't be used in this case, since the
+ * argument structure would be empty. Besides, it would be
+ * pointless to include multiple copies of the same boilerplate
+ * code in a program.)
+ */
+
+extern void kw_parseempty(const char */*set*/,
+ const char */*kwfirst*/, va_list */*ap*/,
+ const struct kwval */*v*/, size_t /*n*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif