diff options
Diffstat (limited to 'doc')
| -rw-r--r-- | doc/example.c | 120 | ||||
| -rw-r--r-- | doc/interface.md | 186 | 
2 files changed, 306 insertions, 0 deletions
| diff --git a/doc/example.c b/doc/example.c new file mode 100644 index 0000000..878dbb6 --- /dev/null +++ b/doc/example.c @@ -0,0 +1,120 @@ +#include "../src/simple-opt.h" + +int main(int argc, char **argv) +{ +	/* array containing all options and their types / attributes */ +	struct simple_opt options[] = { +		{ SIMPLE_OPT_FLAG, 'h', "help", false, +			"print this help message and exit" }, +		{ SIMPLE_OPT_INT, '\0', "int", true, +			"this thing needs an integer!" }, +		{ SIMPLE_OPT_UNSIGNED, 'u', "uns", true, +			"this one has a custom_arg_string. normally it would say"  +				" \"UNSIGNED\" rather than \"NON-NEG-INT\"", +			"NON-NEG-INT" }, +		{ SIMPLE_OPT_STRING, 's', NULL, true, +			"this one doesn't have a long opt version" }, +		{ SIMPLE_OPT_BOOL, 'b', "bool", false, +			"(optionally) takes a boolean arg!" }, +		{ SIMPLE_OPT_END }, +	}; + +	/* contains an enum for identifying simple_opt_parse's return value as well +	 * as the argv index of the first non-option and information relevant for +	 * error handling */ +	struct simple_opt_result result; + +	int i; + +	result = simple_opt_parse(argc, argv, options); + +	/* handle errors */ +	switch (result.result_type) { +	case SIMPLE_OPT_RESULT_UNRECOGNISED_OPTION: +		fprintf(stderr, "err: unrecognised option `%s`\n", +				result.option_string); +		return 1; + +	case SIMPLE_OPT_RESULT_BAD_ARG: +		fprintf(stderr, "err: bad argument `%s` passed to option `%s`\n", +				result.argument_string, result.option_string); +		return 1; + +	case SIMPLE_OPT_RESULT_MISSING_ARG: +		fprintf(stderr, "err: argument expected for option `%s`\n", +				result.option_string); +		return 1; + +	case SIMPLE_OPT_RESULT_OPT_ARG_TOO_LONG: +		fprintf(stderr, "internal err: argument passed to option `%s` is too long\n", +				result.option_string); +		return 1; + +	case SIMPLE_OPT_RESULT_TOO_MANY_ARGS: +		fprintf(stderr, "internal err: too many cli arguments passed\n"); +		return 1; + +	case SIMPLE_OPT_RESULT_MALFORMED_OPTION_STRUCT: +		fprintf(stderr, "internal err: malformed option struct\n"); +		return 1; + +	default: +		break; +	} + +	/* if the help flag was passed, print usage */ +	if (options[0].was_seen) { +		simple_opt_print_usage(stdout, 80, argv[0], +				"[OPTION]... [--] [NON-OPTION]...", +				"This is where you would put an overview description of the " +				"program and it's general functionality.", options); +		return 0; +	} + +	/* print a summary of options passed */ +	for (i = 0; options[i].type != SIMPLE_OPT_END; i++) { +		if (options[i].long_name != NULL) +			printf("--%s, ", options[i].long_name); +		else +			printf("-%c, ", options[i].short_name); + +		printf("seen: %s", (options[i].was_seen ? "yes" : "no")); + +		if (options[i].arg_is_stored) { +			switch (options[i].type) { +			case SIMPLE_OPT_INT: +				printf(", val: %d", options[i].val_int); +				break; + +			case SIMPLE_OPT_UNSIGNED: +				printf(", val: %u", options[i].val_unsigned); +				break; + +			case SIMPLE_OPT_STRING: +				printf(", val: %s", options[i].val_string); +				break; + +			case SIMPLE_OPT_BOOL: +				printf(", val: %s", options[i].val_bool ? "true" : "false"); +				break; +				 +			default: +				break; +			} +		} + +		puts(""); +	} + +	/* if any non-option arguments were passed, print them */ +	if (result.argc > 0) { +		printf("\nnon-options:", result.argc); + +		for (i = 0; i < result.argc; i++) +			printf(" %s", result.argv[i]); + +		puts(""); +	} + +	return 0; +} diff --git a/doc/interface.md b/doc/interface.md new file mode 100644 index 0000000..53838c2 --- /dev/null +++ b/doc/interface.md @@ -0,0 +1,186 @@ +simple-opt +========== + +data types +---------- + +### struct simple_opt + +an array of `struct simple_opt`, terminating in an element with a `type` field +equal to `SIMPLE_OPT_END`, must be passed when parsing or usage printing. the +fields which should be defined in these elements are: + +``` +	enum simple_opt_type type; +	const char short_name; +	const char *long_name; +	bool arg_is_required; + +	/* optional, used for usage printing */ +	const char *description; + +	/* optional, a custom string describing the arg, used for usage printing */ +	const char *custom_arg_string; +``` + +if `type` is `SIMPLE_OPT_FLAG`, this option may not accept arguments. if `type` +is `SIMPLE_OPT_END`, parsing and usage printing will return at this point when +iterating through the array and will not see any elements which may follow. + +`short_name` is optional, and may be undefined for this option by passing '\0'. +`long_name` is also optional, and may be left undefined for this option by +passing `NULL`. however, at fewest one of these two must be defined for every +option. + +the fields which are set by `simple_opt_parse` are: + +``` +	bool was_seen; +	bool arg_is_stored; + +	union { +		bool val_bool; +		int val_int; +		unsigned val_unsigned; +		char val_string[SIMPLE_OPT_ARG_MAX_WIDTH]; +	}; +``` + +`was_seen` indicates if this option was encountered during parsing, +`arg_is_stored` if an argument was passed to the option, and the `val_<type>` +fields contain the value passed (with the correct field to set being determined +by the `type` field shown above). + + +### struct simple_opt_result + +``` +struct simple_opt_result { +	enum simple_opt_result_type result_type; +	enum simple_opt_type option_type; +	char option_string[SIMPLE_OPT_OPT_MAX_WIDTH]; +	char argument_string[SIMPLE_OPT_OPT_ARG_MAX_WIDTH]; +	int argc; +	char *argv[SIMPLE_OPT_MAX_ARGC]; +}; +``` + +`simple_opt_parse` returns a `struct simple_opt_result`. upon successful +parsing, its `result_type` field will contain `SIMPLE_OPT_RESULT_SUCCESS`. +otherwise, it will contain an error which should be handled by the caller. the +first three are user-caused errors: + +``` +	SIMPLE_OPT_RESULT_UNRECOGNISED_OPTION, +	SIMPLE_OPT_RESULT_BAD_ARG, +	SIMPLE_OPT_RESULT_MISSING_ARG, +``` + +in the case of `SIMPLE_OPT_RESULT_UNRECOGNISED_OPTION`, `option_string` will +contain the unrecognised option which was passed. + +if the type is `SIMPLE_OPT_BAD_ARG`, then `option_string` will be set, +`option_type` will be set to the type of that option (`SIMPLE_OPT_BOOL` etc), +and the bad argument will be stored in `argument_string`. + +if the type is `SIMPLE_OPT_MISSING_ARG`, `option_string` and `option_type` will +be set. + +the remaining result types are internal errors: + +``` +	SIMPLE_OPT_RESULT_ARG_TOO_LONG, +	SIMPLE_OPT_RESULT_TOO_MANY_ARGS, +	SIMPLE_OPT_RESULT_MALFORMED_OPTION_STRUCT, +``` + +`SIMPLE_OPT_RESULT_OPT_ARG_TOO_LONG` will be returned if an option argument, +passed on the command line by a user, was too long for the internal buffer. the +internal buffer can be resized by defining `SIMPLE_OPT_OPT_MAX_WIDTH` at some +point before `simple-opt.h` is included. + +`SIMPLE_OPT_RESULT_TOO_MANY_ARGS` is returned if the user passed too many +non-option arguments to the command for its internal argv filter buffer. this +limit can also be resized by defining `SIMPLE_OPT_MAX_ARGC` + +finally, `SIMPLE_OPT_RESULT_MALFORMED_OPTION_STRUCT` is returned if the +programmer has passed a `struct simple_opt` array which contains disallowed +option configurations (that is, two options share a `short_name` or +`long_name`, an option has neither a `short_name` nor a `long_name`, or an +option of type `SIMPLE_OPT_FLAG` is marked as requiring an argument) + + +functions +--------- + +`simple-opt.h` defines two functions for external use, `simple_opt_parse` and +`simple_opt_print_usage`. any other functions are prefixed `sub_simple_opt` and +should not be called directly (their visibility is a by-product of simple-opt's +single header file nature) + + +### simple_opt_parse + +`simple_opt_parse` takes three arguments and returns a `struct +simple_opt_result`: + +``` +struct simple_opt_result simple_opt_parse(int argc, char **argv, struct +		simple_opt *options); +``` + +`argc` is the number of arguments contained in `argv`, and `argv` is an array +of character string pointers. normally here the programmer would just pass on +the `argc` and `argv` received as arguments from the `main` function. + +`struct simple_opt` is an array of the options available to be parsed +(described above) and `struct simple_opt_result` contains a set of results +about that parsing (also described above). + + +### simple_opt_print_usage + +`simple_opt_print_usage` takes six arguments and prints a neatly-formatted +usage message, similar to those typical of GNU cli commands: + +``` +void simple_opt_print_usage(FILE *f, unsigned width, char *usage_name, +		char *usage_options, char *usage_summary, struct simple_opt *options) +``` + +`f` is a file pointer to which the message should be printed + +`width` is the column width to which the output should be word-wrapped (passing +0 disables wrapping). a reasonable value here would be 80, but this could also +be used to allow more dynamic behaviour (e.g. using something like `ncurses` or +`ioctl` to get the users's current terminal width) + +`usage_name` is the name of the command as it will be printed in the usage +statement. easiest is just to pass `argv[0]` here. + +`usage_options` is a summary of what options the command takes (e.g. something +like `[OPTION]...`) + +together, these two result in something that looks like: + +``` +Usage: ./a.out [OPTION]... +``` + +if both `usage_name` and `usage_options` are `NULL`, this initial line will not +be printed, allowing more flexibility to the programmer (if you wanted to, for +example, print multiple such lines on your own in order to represent different +use-cases) + +`usage_summary` is usually a one or two sentence overview summary of how the +command behaves. if this is left as `NULL`, no summary will be printed. + +the final argument, `struct simple_opt *options`, is an array of options as +defined above. + +*note:* usage printing's word wrap operates on the assumptions that your +language used delimits words with spaces (i.e. "when i was a child..." vs. +"子供時代に..."), that the font used is fixed-width, and that every character +occupies one column (that is, there are no wide characters, combining +diacritics, etc). if these assumptions do not apply to your use case, you +should use an alternative method for usage printing. | 
