From ca46fe24571f51e229637b1f99be4f7ae4032107 Mon Sep 17 00:00:00 2001 From: Sean McArdle Date: Wed, 21 Jun 2017 22:43:25 -0700 Subject: [PATCH] Added parg.h header --- include/parg.h | 481 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 481 insertions(+) create mode 100644 include/parg.h diff --git a/include/parg.h b/include/parg.h new file mode 100644 index 0000000..fd786cb --- /dev/null +++ b/include/parg.h @@ -0,0 +1,481 @@ +/* + * parg - parse argv + * + * Written in 2015 by Joergen Ibsen + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + */ + +#ifndef PARG_H_INCLUDED +#define PARG_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define PARG_VER_MAJOR 1 /**< Major version number */ +#define PARG_VER_MINOR 0 /**< Minor version number */ +#define PARG_VER_PATCH 1 /**< Patch version number */ +#define PARG_VER_STRING "1.0.1" /**< Version number as a string */ + +/** + * Structure containing state between calls to parser. + * + * @see parg_init + */ +struct parg_state { + const char *optarg; /**< Pointer to option argument, if any */ + int optind; /**< Next index in argv to process */ + int optopt; /**< Option value resulting in error, if any */ + const char *nextchar; /**< Next character to process */ +}; + +/** + * Structure for supplying long options to `parg_getopt_long()`. + * + * @see parg_getopt_long + */ +struct parg_option { + const char *name; /**< Name of option */ + int has_arg; /**< Option argument status */ + int *flag; /**< Pointer to flag variable */ + int val; /**< Value of option */ +}; + +/** + * Values for the `has_arg` flag in `parg_option`. + * + * @see parg_option + */ +typedef enum { + PARG_NOARG, /**< No argument */ + PARG_REQARG, /**< Required argument */ + PARG_OPTARG /**< Optional argument */ +} parg_arg_num; + +/** + * Initialize `ps`. + * + * Must be called before using state with a parser. + * + * @see parg_state + * + * @param ps pointer to state + */ +void +parg_init(struct parg_state *ps); + +/** + * Parse next short option in `argv`. + * + * Elements in `argv` that contain short options start with a single dash + * followed by one or more option characters, and optionally an option + * argument for the last option character. Examples are '`-d`', '`-ofile`', + * and '`-dofile`'. + * + * Consecutive calls to this function match the command-line arguments in + * `argv` against the short option characters in `optstring`. + * + * If an option character in `optstring` is followed by a colon, '`:`', the + * option requires an argument. If it is followed by two colons, the option + * may take an optional argument. + * + * If a match is found, `optarg` points to the option argument, if any, and + * the value of the option character is returned. + * + * If a match is found, but is missing a required option argument, `optopt` + * is set to the option character. If the first character in `optstring` is + * '`:`', then '`:`' is returned, otherwise '`?`' is returned. + * + * If no option character in `optstring` matches a short option, `optopt` + * is set to the option character, and '`?`' is returned. + * + * If an elements of argv does not contain options (a nonoption element), + * `optarg` points to the element, and `1` is returned. + * + * An element consisting of a single dash, '`-`', is returned as a nonoption. + * + * Parsing stops and `-1` is returned, when the end of `argv` is reached, or + * if an element contains '`--`'. + * + * Works similarly to `getopt`, if `optstring` were prefixed by '`-`'. + * + * @param ps pointer to state + * @param argc number of elements in `argv` + * @param argv array of pointers to command-line arguments + * @param optstring string containing option characters + * @return option value on match, `1` on nonoption element, `-1` on end of + * arguments, '`?`' on unmatched option, '`?`' or '`:`' on option argument + * error + */ +int +parg_getopt(struct parg_state *ps, int argc, char *const argv[], + const char *optstring); + +/** + * Parse next long or short option in `argv`. + * + * Elements in `argv` that contain a long option start with two dashes + * followed by a string, and optionally an equal sign and an option argument. + * Examples are '`--help`' and '`--size=5`'. + * + * If no exact match is found, an unambiguous prefix of a long option will + * match. For example, if '`foo`' and '`foobar`' are valid long options, then + * '`--fo`' is ambiguous and will not match, '`--foo`' matches exactly, and + * '`--foob`' is an unambiguous prefix and will match. + * + * If a long option match is found, and `flag` is `NULL`, `val` is returned. + * + * If a long option match is found, and `flag` is not `NULL`, `val` is stored + * in the variable `flag` points to, and `0` is returned. + * + * If a long option match is found, but is missing a required option argument, + * or has an option argument even though it takes none, `optopt` is set to + * `val` if `flag` is `NULL`, and `0` otherwise. If the first character in + * `optstring` is '`:`', then '`:`' is returned, otherwise '`?`' is returned. + * + * If `longindex` is not `NULL`, the index of the entry in `longopts` that + * matched is stored there. + * + * If no long option in `longopts` matches a long option, '`?`' is returned. + * + * Handling of nonoptions and short options is like `parg_getopt()`. + * + * If no short options are required, an empty string, `""`, should be passed + * as `optstring`. + * + * Works similarly to `getopt_long`, if `optstring` were prefixed by '`-`'. + * + * @see parg_getopt + * + * @param ps pointer to state + * @param argc number of elements in `argv` + * @param argv array of pointers to command-line arguments + * @param optstring string containing option characters + * @param longopts array of `parg_option` structures + * @param longindex pointer to variable to store index of matching option in + * @return option value on match, `0` for flag option, `1` on nonoption + * element, `-1` on end of arguments, '`?`' on unmatched or ambiguous option, + * '`?`' or '`:`' on option argument error + */ +int +parg_getopt_long(struct parg_state *ps, int argc, char *const argv[], + const char *optstring, + const struct parg_option *longopts, int *longindex); + +/** + * Reorder elements of `argv` so options appear first. + * + * If there are no long options, `longopts` may be `NULL`. + * + * @note The current implementation does not permute `argv` in place, it + * allocates a temporary array the same size as `argv`. + * + * @param argc number of elements in `argv` + * @param argv array of pointers to command-line arguments + * @param optstring string containing option characters + * @param longopts array of `parg_option` structures + * @return index of first nonoption in `argv` on success, `-1` on error + */ +int +parg_reorder(int argc, char *argv[], + const char *optstring, + const struct parg_option *longopts); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* PARG_H_INCLUDED */ + +// ================================================================= +// +// parg.c +// + /* + * parg - parse argv + * + * Written in 2015 by Joergen Ibsen + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + */ + +#include +#include +#include + + /* + * Check if state is at end of argv. + */ +static int +is_argv_end(const struct parg_state *ps, int argc, char *const argv[]) +{ + return ps->optind >= argc || argv[ps->optind] == NULL; +} + +/* +* Match nextchar against optstring. +*/ +static int +match_short(struct parg_state *ps, int argc, char *const argv[], + const char *optstring) +{ + const char *p = strchr(optstring, *ps->nextchar); + + if (p == NULL) { + ps->optopt = *ps->nextchar++; + return '?'; + } + + /* If no option argument, return option */ + if (p[1] != ':') { + return *ps->nextchar++; + } + + /* If more characters, return as option argument */ + if (ps->nextchar[1] != '\0') { + ps->optarg = &ps->nextchar[1]; + ps->nextchar = NULL; + return *p; + } + + /* If option argument is optional, return option */ + if (p[2] == ':') { + return *ps->nextchar++; + } + + /* Option argument required, so return next argv element */ + if (is_argv_end(ps, argc, argv)) { + ps->optopt = *ps->nextchar++; + return optstring[0] == ':' ? ':' : '?'; + } + + ps->optarg = argv[ps->optind++]; + ps->nextchar = NULL; + return *p; +} + +/* +* Match string at nextchar against longopts. +*/ +static int +match_long(struct parg_state *ps, int argc, char *const argv[], + const char *optstring, + const struct parg_option *longopts, int *longindex) +{ + size_t len; + int num_match = 0; + int match = -1; + int i; + + len = strcspn(ps->nextchar, "="); + + for (i = 0; longopts[i].name; ++i) { + if (strncmp(ps->nextchar, longopts[i].name, len) == 0) { + match = i; + num_match++; + /* Take if exact match */ + if (longopts[i].name[len] == '\0') { + num_match = 1; + break; + } + } + } + + /* Return '?' on no or ambiguous match */ + if (num_match != 1) { + ps->nextchar = NULL; + return '?'; + } + + assert(match != -1); + + if (longindex) { + *longindex = match; + } + + if (ps->nextchar[len] == '=') { + /* Option argument present, check if extraneous */ + if (longopts[match].has_arg == PARG_NOARG) { + ps->optopt = longopts[match].flag ? 0 : longopts[match].val; + ps->nextchar = NULL; + return optstring[0] == ':' ? ':' : '?'; + } + else { + ps->optarg = &ps->nextchar[len + 1]; + } + } + else if (longopts[match].has_arg == PARG_REQARG) { + /* Option argument required, so return next argv element */ + if (is_argv_end(ps, argc, argv)) { + ps->optopt = longopts[match].flag ? 0 : longopts[match].val; + ps->nextchar = NULL; + return optstring[0] == ':' ? ':' : '?'; + } + + ps->optarg = argv[ps->optind++]; + } + + ps->nextchar = NULL; + + if (longopts[match].flag != NULL) { + *longopts[match].flag = longopts[match].val; + return 0; + } + + return longopts[match].val; +} + +void +parg_init(struct parg_state *ps) +{ + ps->optarg = NULL; + ps->optind = 1; + ps->optopt = '?'; + ps->nextchar = NULL; +} + +int +parg_getopt(struct parg_state *ps, int argc, char *const argv[], + const char *optstring) +{ + return parg_getopt_long(ps, argc, argv, optstring, NULL, NULL); +} + +int +parg_getopt_long(struct parg_state *ps, int argc, char *const argv[], + const char *optstring, + const struct parg_option *longopts, int *longindex) +{ + assert(ps != NULL); + assert(argv != NULL); + assert(optstring != NULL); + + ps->optarg = NULL; + + if (argc < 2) { + return -1; + } + + /* Advance to next element if needed */ + if (ps->nextchar == NULL || *ps->nextchar == '\0') { + if (is_argv_end(ps, argc, argv)) { + return -1; + } + + ps->nextchar = argv[ps->optind++]; + + /* Check for nonoption element (including '-') */ + if (ps->nextchar[0] != '-' || ps->nextchar[1] == '\0') { + ps->optarg = ps->nextchar; + ps->nextchar = NULL; + return 1; + } + + /* Check for '--' */ + if (ps->nextchar[1] == '-') { + if (ps->nextchar[2] == '\0') { + return -1; + } + + if (longopts != NULL) { + ps->nextchar += 2; + + return match_long(ps, argc, argv, optstring, + longopts, longindex); + } + } + + ps->nextchar++; + } + + /* Match nextchar */ + return match_short(ps, argc, argv, optstring); +} + +int +parg_reorder(int argc, char *argv[], + const char *optstring, + const struct parg_option *longopts) +{ + struct parg_state ps; + char **new_argv = NULL; + int start = 1; + int end = argc; + int curind; + int i; + + assert(argv != NULL); + assert(optstring != NULL); + + if (argc < 2) { + return argc; + } + + new_argv = static_cast(calloc((size_t)argc, sizeof(argv[0]))); + + if (new_argv == NULL) { + return -1; + } + + new_argv[0] = argv[0]; + + parg_init(&ps); + + for (;;) { + int c; + + curind = ps.optind; + + c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); + + if (c == -1) { + break; + } + else if (c == 1) { + assert(ps.optind - curind == 1); + + /* Add nonoption to end of new_argv */ + new_argv[--end] = argv[curind]; + } + else { + /* Add option with any argument to start of new_argv */ + while (curind < ps.optind) { + new_argv[start++] = argv[curind++]; + } + } + } + + if (end > start) { + assert(strcmp(argv[curind], "--") == 0); + + /* Copy '--' element */ + new_argv[start++] = argv[curind++]; + + /* Copy remaining nonoptions to end of new_argv */ + for (i = curind; end > start; ++i) { + new_argv[--end] = argv[i]; + } + } + + /* Copy options back to argv */ + for (i = 1; i < end; ++i) { + argv[i] = new_argv[i]; + } + + /* Copy nonoptions in reverse order to argv */ + for (; i < argc; ++i) { + argv[i] = new_argv[argc - i + end - 1]; + } + + free(new_argv); + + return end; +} +