A => include/sc-argparse.h +48 -0
@@ 1,48 @@
+/**
+ * sc-argparse.h: simple argument parsing library
+ */
+
+#ifndef SC_ARGPARSE_H
+#define SC_ARGPARSE_H 1
+
+#include <stdbool.h>
+
+enum sc_arg_type {
+ SC_ARG_TYPE_END = 0,
+ SC_ARG_TYPE_COUNT,
+ SC_ARG_TYPE_STRING,
+ SC_ARG_TYPE_INT,
+};
+
+struct sc_arg {
+ char flag;
+ bool required;
+ enum sc_arg_type type;
+ char *name;
+ char *help;
+
+ char *val_string;
+ int val_int;
+ bool seen;
+};
+
+int sc_argparse(struct sc_arg argspec[], int argc, char **argv);
+int sc_argsort(struct sc_arg argspec[], int argc, char **argv);
+
+#define SC_ARG_DEF_STRING(flag, name, default_, help) \
+ ((struct sc_arg){flag, false, SC_ARG_TYPE_STRING, name, help, default_})
+#define SC_ARG_STRING(flag, name, help) \
+ ((struct sc_arg){flag, true, SC_ARG_TYPE_STRING, name, help})
+
+#define SC_ARG_DEF_INT(flag, name, default_, help) \
+ ((struct sc_arg){flag, false, SC_ARG_TYPE_INT, name, help, NULL, default_})
+#define SC_ARG_INT(flag, name, help) \
+ ((struct sc_arg){flag, true, SC_ARG_TYPE_INT, name, help})
+
+#define SC_ARG_COUNT(flag, name, help) \
+ ((struct sc_arg){flag, false, SC_ARG_TYPE_COUNT, name, help})
+
+#define SC_ARG_END() \
+ ((struct sc_arg){0})
+
+#endif
A => src/sc-argparse.c +218 -0
@@ 1,218 @@
+/**
+ * sc-argparse.c: simple argument parsing library
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "sc-argparse.h"
+
+enum error {
+ NO_ERROR,
+ UNKNOWN_SHORT_FLAG,
+ SHORT_FLAG_WITH_PARAM_NOT_AT_END,
+ SHORT_FLAG_MISSING_VAL,
+ LONG_FLAG_NO_PARAM_HAS_PARAM,
+ LONG_FLAG_MISSING_VAL,
+};
+
+enum arg {
+ ERROR,
+ POSITIONAL, /* a positional arg */
+ FLAG_ONE, /* flag like "-c", or "--key=val" */
+ FLAG_TWO, /* flag like "-o filename" or "--key value" */
+ STOP, /* -- */
+};
+
+struct state {
+ struct sc_arg *argspec;
+ int argc;
+ int pos;
+ char **argv;
+ int argidx;
+};
+
+static struct sc_arg *sc_get_arg_by_flag(struct sc_arg *argspec, char flag)
+{
+ int i;
+ for (i = 0; argspec[i].type != SC_ARG_TYPE_END; i++)
+ if (argspec[i].flag == flag)
+ return &argspec[i];
+
+}
+
+static struct sc_arg *sc_get_arg_by_name(struct sc_arg *argspec,
+ const char *name)
+{
+ int i;
+ for (i = 0; argspec[i].type != SC_ARG_TYPE_END; i++)
+ if (strcmp(name, argspec[i].name) == 0)
+ return &argspec[i];
+}
+
+static enum error sc_process_short_flag(struct state *state, int i)
+{
+ struct sc_arg *arg_struct;
+ char *arg = state->argv[state->argidx];
+
+ arg_struct = sc_get_arg_by_flag(state->argspec, arg[i]);
+ if (!arg_struct)
+ return UNKNOWN_SHORT_FLAG;
+ arg_struct->seen = true;
+
+ if (arg_struct->type == SC_ARG_TYPE_COUNT) {
+ arg_struct->val_int++;
+ return NO_ERROR;
+ }
+
+ /* At this point, we must have a value. Ensure that
+ * there are no short flags after this one. */
+ if (arg[i + 1] != '\0')
+ return SHORT_FLAG_WITH_PARAM_NOT_AT_END;
+
+ if (state->argidx + 1 >= state->argc)
+ return SHORT_FLAG_MISSING_VAL;
+
+ /* TODO assign args */
+ if (arg_struct->type == SC_ARG_TYPE_STRING) {
+ state->argidx++;
+ arg_struct->val_string = state->argv[state->argidx];
+ } else {
+ /* SC_ARG_TYPE_INT */
+ state->argidx++;
+ arg_struct->val_int = atoi(state->argv[state->argidx]);
+ }
+ return NO_ERROR;
+}
+
+static enum error sc_process_long_flag(struct state *state)
+{
+ char *equal, *value, *arg;
+ struct sc_arg *arg_struct;
+
+ arg = state->argv[state->argidx];
+ equal = strchr(arg, '=');
+ if (!equal) {
+ arg_struct = sc_get_arg_by_name(state->argspec, arg);
+ } else {
+ *equal = '\0';
+ arg_struct = sc_get_arg_by_name(state->argspec, arg);
+ *equal = '=';
+ }
+ if (!arg_struct)
+ return ERROR;
+
+ arg_struct->seen = true;
+
+ if (arg_struct->type == SC_ARG_TYPE_COUNT) {
+ if (equal)
+ return LONG_FLAG_NO_PARAM_HAS_PARAM;
+ arg_struct->val_int++;
+ return NO_ERROR;
+ }
+
+ /* At this point we know we have a value */
+ if (equal) {
+ value = equal + 1;
+ } else {
+ if (state->argidx + 1 >= state->argc)
+ return LONG_FLAG_MISSING_VAL;
+ value = state->argv[++state->argidx];
+ }
+
+ if (arg_struct->type == SC_ARG_TYPE_STRING)
+ arg_struct->val_string = value;
+ else
+ arg_struct->val_int = atoi(value);
+ return NO_ERROR;
+}
+
+static enum error sc_process_arg(struct state *state)
+{
+ int i;
+ enum error err = NO_ERROR;
+ struct sc_arg *arg_struct;
+ char *equal;
+ char *arg = state->argv[state->argidx];
+
+ if (strcmp(arg, "-") == 0) {
+ state->argv[state->pos++] = state->argv[state->argidx];
+ } else if (strcmp(arg, "--") == 0) {
+ return STOP;
+ } else if (arg[0] == '-' && arg[1] != '-') {
+ /* Sigle hyphen means we're dealing with short character flags.
+ * Iterate over each one.
+ */
+ for (i = 1; arg[i]; i++) {
+ if ((err = sc_process_short_flag(state, i)) != NO_ERROR)
+ return err;
+ }
+ } else if (arg[0] == '-' && arg[1] == '-') {
+ err = sc_process_long_flag(state);
+ } else {
+ state->argv[state->pos++] = state->argv[state->argidx];
+ }
+ return err;
+}
+
+int sc_argparse(struct sc_arg argspec[], int argc, char **argv)
+{
+ struct state state;
+ enum error err;
+
+ state.argc = argc;
+ state.argv = argv;
+ state.argspec = argspec;
+ state.pos = 0;
+ for (state.argidx = 0; state.argidx < state.argc; state.argidx++) {
+ err = sc_process_arg(&state);
+ if (err == NO_ERROR) {
+ continue;
+ } else if (err == STOP) {
+ break;
+ } else {
+ printf("ERROR: %d\n", err);
+ return -1;
+ }
+ }
+
+ for (state.argidx++; state.argidx < state.argc; state.argidx++)
+ argv[state.pos++] = argv[state.argidx];
+
+ return state.pos;
+}
+
+static void printargs(int argc, char **argv, char *pfx)
+{
+ int i;
+ for (i = 0; i < argc; i++)
+ printf("%s[%d] = %s\n", pfx, i, argv[i]);
+}
+
+enum my_args {
+ ARG_HOST=0,
+ ARG_PORT,
+ ARG_VERBOSE,
+ NUM_ARGS,
+};
+
+void main(int argc, char **argv)
+{
+ struct sc_arg args[NUM_ARGS];
+ int rv;
+ args[ARG_HOST] = SC_ARG_STRING('H', "--host", "host to connect");
+ args[ARG_PORT] = SC_ARG_DEF_INT('p', "--port", 80, "port to connect");
+ args[ARG_VERBOSE] = SC_ARG_COUNT('v', "--verbose", "print more");
+
+ if ((rv = sc_argparse(args, argc-1, argv+1)) >= 0) {
+ printf("Parsing successful!\n", rv);
+ printf("Host: %s\n", args[ARG_HOST].val_string);
+ printf("Port: %d\n", args[ARG_PORT].val_int);
+ printf("Verbose: %d\n", args[ARG_VERBOSE].val_int);
+ printf("Posargs (%d):\n", rv);
+ printargs(rv, argv+1, "posargs");
+ } else {
+ printf("error\n");
+ }
+}