Added parg.h header

master
Sean McArdle 2017-06-21 22:43:25 -07:00
parent 33c953604b
commit ca46fe2457
1 changed files with 481 additions and 0 deletions

481
include/parg.h Normal file
View File

@ -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. <http://creativecommons.org/publicdomain/zero/1.0/>
*/
#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. <http://creativecommons.org/publicdomain/zero/1.0/>
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/*
* 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<char**>(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;
}