~brenns10/sc-argparse

b52f213b2e24aead6853088849c082eb55867d10 — Stephen Brennan 4 years ago
Initial commit of working argparse
2 files changed, 266 insertions(+), 0 deletions(-)

A include/sc-argparse.h
A src/sc-argparse.c
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");
	}
}