liboptcfg

changeset 0:10b89befcaa9

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 13 Nov 2015 23:48:31 +0200
parents
children 8fd2858c6a29
files .hgignore LICENSE Makefile src/optcfg.c src/optcfg.h
diffstat 5 files changed, 450 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/.hgignore	Fri Nov 13 23:48:31 2015 +0200
     1.3 @@ -0,0 +1,6 @@
     1.4 +\.o$
     1.5 +\.swp$
     1.6 +\.d$
     1.7 +\.a$
     1.8 +\.so\.
     1.9 +\.dylib$
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/LICENSE	Fri Nov 13 23:48:31 2015 +0200
     2.3 @@ -0,0 +1,3 @@
     2.4 +liboptcfg, written by John Tsiombikas <nuclear@member.fsf.org> is public domain
     2.5 +software. Feel free to use it any way you like. Attribution appreciated but not
     2.6 +required.
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/Makefile	Fri Nov 13 23:48:31 2015 +0200
     3.3 @@ -0,0 +1,54 @@
     3.4 +src = $(wildcard src/*.c)
     3.5 +obj = $(src:.c=.o)
     3.6 +name = optcfg
     3.7 +
     3.8 +so_major = 0
     3.9 +so_minor = 1
    3.10 +
    3.11 +alib = lib$(name).a
    3.12 +
    3.13 +CFLAGS = -pedantic -Wall -g
    3.14 +
    3.15 +sys := $(shell uname -s)
    3.16 +ifeq ($(sys), Darwin)
    3.17 +	solib = lib$(name).dylib
    3.18 +	shared = -dynamiclib
    3.19 +else
    3.20 +	ldname = lib$(name).so
    3.21 +	soname = lib$(name).so.$(so_major)
    3.22 +	solib = lib$(name).so.$(so_major).$(so_minor)
    3.23 +	shared = -shared -Wl,-soname=$(soname)
    3.24 +	CFLAGS += -fPIC
    3.25 +endif
    3.26 +
    3.27 +.PHONY: all
    3.28 +all: $(solib) $(alib)
    3.29 +
    3.30 +$(solib): $(obj)
    3.31 +	$(CC) -o $@ $(shared) $(obj) $(LDFLAGS)
    3.32 +
    3.33 +$(alib): $(obj)
    3.34 +	$(AR) rcs $@ $(obj)
    3.35 +
    3.36 +.PHONY: clean
    3.37 +clean:
    3.38 +	rm -f $(obj) $(alib) $(solib)
    3.39 +
    3.40 +.PHONY: install
    3.41 +install:
    3.42 +	mkdir -p $(DESTDIR)$(PREFIX)/include $(DESTDIR)$(PREFIX)/lib
    3.43 +	cp src/optcfg.h $(DESTDIR)$(PREFIX)/include/optcfg.h
    3.44 +	cp $(alib) $(DESTDIR)$(PREFIX)/lib/$(alib)
    3.45 +	cp $(solib) $(DESTDIR)$(PREFIX)/lib/$(solib)
    3.46 +	[ -n "$(soname)" ] && cd $(DESTDIR)$(PREFIX)/lib && \
    3.47 +		ln -s $(solib) $(soname) && \
    3.48 +		ln -s $(soname) $(ldname) || true
    3.49 +
    3.50 +.PHONY: uninstall
    3.51 +uninstall:
    3.52 +	rm -f $(DESTDIR)$(PREFIX)/include/optcfg.h
    3.53 +	rm -f $(DESTDIR)$(PREFIX)/lib/$(alib)
    3.54 +	rm -f $(DESTDIR)$(PREFIX)/lib/$(solib)
    3.55 +	[ -n "$(soname)" ] && \
    3.56 +		rm -f $(DESTDIR)$(PREFIX)/lib/$(soname) && \
    3.57 +		rm -f $(DESTDIR)$(PREFIX)/lib/$(ldname) || true
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/optcfg.c	Fri Nov 13 23:48:31 2015 +0200
     4.3 @@ -0,0 +1,317 @@
     4.4 +#include <stdio.h>
     4.5 +#include <stdlib.h>
     4.6 +#include <string.h>
     4.7 +#include <ctype.h>
     4.8 +#ifdef _MSC_VER
     4.9 +#include <malloc.h>
    4.10 +#else
    4.11 +#include <alloca.h>
    4.12 +#endif
    4.13 +#include "optcfg.h"
    4.14 +
    4.15 +struct optcfg {
    4.16 +	struct optcfg_option *optlist;
    4.17 +
    4.18 +	optcfg_opt_callback opt_func;
    4.19 +	void *opt_cls;
    4.20 +	optcfg_arg_callback arg_func;
    4.21 +	void *arg_cls;
    4.22 +
    4.23 +	int err_abort;
    4.24 +
    4.25 +	/* argument parsing state */
    4.26 +	char **argv;
    4.27 +	int argidx;
    4.28 +
    4.29 +	/* config file parsing state */
    4.30 +	const char *cfg_fname;
    4.31 +	int cfg_nline;
    4.32 +	char *cfg_value;
    4.33 +};
    4.34 +
    4.35 +static int get_opt(struct optcfg *oc, const char *s);
    4.36 +static char *skip_spaces(char *s);
    4.37 +static void strip_comments(char *s);
    4.38 +static void strip_trailing_spaces(char *s);
    4.39 +static char *parse_keyval(char *line);
    4.40 +
    4.41 +
    4.42 +struct optcfg *optcfg_init(struct optcfg_option *optv)
    4.43 +{
    4.44 +	struct optcfg *oc;
    4.45 +
    4.46 +	if(!(oc = calloc(1, sizeof *oc))) {
    4.47 +		return 0;
    4.48 +	}
    4.49 +	oc->optlist = optv;
    4.50 +	oc->err_abort = 1;
    4.51 +	return oc;
    4.52 +}
    4.53 +
    4.54 +void optcfg_destroy(struct optcfg *oc)
    4.55 +{
    4.56 +	memset(oc, 0, sizeof *oc);
    4.57 +	free(oc);
    4.58 +}
    4.59 +
    4.60 +void optcfg_set_opt_callback(struct optcfg *oc, optcfg_opt_callback func, void *cls)
    4.61 +{
    4.62 +	oc->opt_func = func;
    4.63 +	oc->opt_cls = cls;
    4.64 +}
    4.65 +
    4.66 +void optcfg_set_arg_callback(struct optcfg *oc, optcfg_arg_callback func, void *cls)
    4.67 +{
    4.68 +	oc->arg_func = func;
    4.69 +	oc->arg_cls = cls;
    4.70 +}
    4.71 +
    4.72 +void optcfg_set_error_action(struct optcfg *oc, int act)
    4.73 +{
    4.74 +	if(act == OPTCFG_ERROR_FAIL) {
    4.75 +		oc->err_abort = 1;
    4.76 +	} else if(act == OPTCFG_ERROR_IGNORE) {
    4.77 +		oc->err_abort = 0;
    4.78 +	}
    4.79 +}
    4.80 +
    4.81 +int optcfg_parse_args(struct optcfg *oc, int argc, char **argv)
    4.82 +{
    4.83 +	int i;
    4.84 +
    4.85 +	oc->argv = argv;
    4.86 +
    4.87 +	for(i=1; i<argc; i++) {
    4.88 +		oc->argidx = i;
    4.89 +
    4.90 +		if(argv[i][0] == '-') {
    4.91 +			if(oc->opt_func) {
    4.92 +				int o = get_opt(oc, argv[i]);
    4.93 +				if(o == -1 || oc->opt_func(oc, o, oc->opt_cls) == -1) {
    4.94 +					if(oc->err_abort) {
    4.95 +						return -1;
    4.96 +					}
    4.97 +				}
    4.98 +			} else {
    4.99 +				fprintf(stderr, "unexpected option: %s\n", argv[i]);
   4.100 +				if(oc->err_abort) {
   4.101 +					return -1;
   4.102 +				}
   4.103 +			}
   4.104 +		} else {
   4.105 +			if(oc->arg_func) {
   4.106 +				if(oc->arg_func(oc, argv[i], oc->arg_cls) == -1) {
   4.107 +					if(oc->err_abort) {
   4.108 +						return -1;
   4.109 +					}
   4.110 +				}
   4.111 +			} else {
   4.112 +				fprintf(stderr, "unexpected argument: %s\n", argv[i]);
   4.113 +				if(oc->err_abort) {
   4.114 +					return -1;
   4.115 +				}
   4.116 +			}
   4.117 +		}
   4.118 +
   4.119 +		i = oc->argidx;
   4.120 +	}
   4.121 +
   4.122 +	oc->argidx = 0;	/* done parsing args */
   4.123 +	return 0;
   4.124 +}
   4.125 +
   4.126 +int optcfg_parse_config_file(struct optcfg *oc, const char *fname)
   4.127 +{
   4.128 +	int res;
   4.129 +	FILE *fp = fopen(fname, "rb");
   4.130 +	if(!fp) {
   4.131 +		return -1;
   4.132 +	}
   4.133 +
   4.134 +	oc->cfg_fname = fname;
   4.135 +	res = optcfg_parse_config_stream(oc, fp);
   4.136 +	oc->cfg_fname = 0;
   4.137 +	fclose(fp);
   4.138 +	return res;
   4.139 +}
   4.140 +
   4.141 +int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp)
   4.142 +{
   4.143 +	char buf[512];
   4.144 +
   4.145 +	oc->cfg_nline = 0;
   4.146 +	while(fgets(buf, sizeof buf, fp)) {
   4.147 +		++oc->cfg_nline;
   4.148 +
   4.149 +		if(optcfg_parse_config_line(oc, buf) == -1) {
   4.150 +			if(oc->err_abort) {
   4.151 +				return -1;
   4.152 +			}
   4.153 +		}
   4.154 +	}
   4.155 +	return 0;
   4.156 +}
   4.157 +
   4.158 +int optcfg_parse_config_line(struct optcfg *oc, const char *line)
   4.159 +{
   4.160 +	int opt, len;
   4.161 +	char *start, *val, *buf;
   4.162 +
   4.163 +	len = strlen(line);
   4.164 +	buf = alloca(len + 1);
   4.165 +	memcpy(buf, line, len);
   4.166 +
   4.167 +	start = skip_spaces(buf);
   4.168 +	strip_comments(start);
   4.169 +	strip_trailing_spaces(start);
   4.170 +	if(!*start) {
   4.171 +		return 0;
   4.172 +	}
   4.173 +
   4.174 +	if(!(val = parse_keyval(start))) {
   4.175 +		fprintf(stderr, "error parsing %s line %d: invalid syntax\n", oc->cfg_fname ? oc->cfg_fname : "", oc->cfg_nline);
   4.176 +		return -1;
   4.177 +	}
   4.178 +	oc->cfg_value = val;
   4.179 +	if((opt = get_opt(oc, start)) == -1) {
   4.180 +		fprintf(stderr, "error parsing %s line %d: unknown option: %s\n", oc->cfg_fname ? oc->cfg_fname : "",
   4.181 +				oc->cfg_nline, start);
   4.182 +		return -1;
   4.183 +	}
   4.184 +
   4.185 +	if(oc->opt_func) {
   4.186 +		if(oc->opt_func(oc, opt, oc->opt_cls) == -1) {
   4.187 +			return -1;
   4.188 +		}
   4.189 +	}
   4.190 +	oc->cfg_value = 0;
   4.191 +	return 0;
   4.192 +}
   4.193 +
   4.194 +char *optcfg_next_value(struct optcfg *oc)
   4.195 +{
   4.196 +	if(oc->argidx) {	/* we're in the middle of parsing arguments, so get the next one */
   4.197 +		return oc->argv[++oc->argidx];
   4.198 +	}
   4.199 +	if(oc->cfg_value) {
   4.200 +		char *val = oc->cfg_value;
   4.201 +		oc->cfg_value = 0;
   4.202 +		return val;
   4.203 +	}
   4.204 +	return 0;
   4.205 +}
   4.206 +
   4.207 +void optcfg_print_options(struct optcfg *oc)
   4.208 +{
   4.209 +	int i;
   4.210 +	for(i=0; oc->optlist[i].opt != -1; i++) {
   4.211 +		struct optcfg_option *opt = oc->optlist + i;
   4.212 +
   4.213 +		if(opt->c) {
   4.214 +			printf(" -%c", opt->c);
   4.215 +		} else {
   4.216 +			printf("   ");
   4.217 +		}
   4.218 +		if(opt->s) {
   4.219 +			printf("%c-%s: ", opt->c ? ',' : ' ', opt->s);
   4.220 +		} else {
   4.221 +			printf(": ");
   4.222 +		}
   4.223 +		printf("%s\n", opt->desc ? opt->desc : "undocumented");
   4.224 +	}
   4.225 +}
   4.226 +
   4.227 +int optcfg_bool_value(char *s, int *valret)
   4.228 +{
   4.229 +	if(strcasecmp(s, "yes") == 0 || strcasecmp(s, "true") == 0 || strcmp(s, "1") == 0) {
   4.230 +		*valret = 1;
   4.231 +		return 0;
   4.232 +	}
   4.233 +	if(strcasecmp(s, "no") == 0 || strcasecmp(s, "false") == 0 || strcmp(s, "0") == 0) {
   4.234 +		*valret = 0;
   4.235 +		return 0;
   4.236 +	}
   4.237 +	return -1;
   4.238 +}
   4.239 +
   4.240 +int optcfg_int_value(char *str, int *valret)
   4.241 +{
   4.242 +	char *endp;
   4.243 +	*valret = strtol(str, &endp, 0);
   4.244 +	if(endp == str) {
   4.245 +		return -1;
   4.246 +	}
   4.247 +	return 0;
   4.248 +}
   4.249 +
   4.250 +int optcfg_float_value(char *str, float *valret)
   4.251 +{
   4.252 +	char *endp;
   4.253 +	*valret = strtod(str, &endp);
   4.254 +	if(endp == str) {
   4.255 +		return -1;
   4.256 +	}
   4.257 +	return 0;
   4.258 +}
   4.259 +
   4.260 +
   4.261 +
   4.262 +static int get_opt(struct optcfg *oc, const char *arg)
   4.263 +{
   4.264 +	int i;
   4.265 +
   4.266 +	if(arg[0] != '-' || !arg[1]) {
   4.267 +		return -1;
   4.268 +	}
   4.269 +
   4.270 +	if(arg[2]) {	/* match long options */
   4.271 +		for(i=0; oc->optlist[i].opt != -1; i++) {
   4.272 +			if(strcmp(arg + 1, oc->optlist[i].s) == 0) {
   4.273 +				return i;
   4.274 +			}
   4.275 +		}
   4.276 +	} else {
   4.277 +		for(i=0; oc->optlist[i].opt != -1; i++) {
   4.278 +			if(arg[1] == oc->optlist[i].c) {
   4.279 +				return i;
   4.280 +			}
   4.281 +		}
   4.282 +	}
   4.283 +	return -1;
   4.284 +}
   4.285 +
   4.286 +static char *skip_spaces(char *s)
   4.287 +{
   4.288 +	while(*s && isspace(*s)) ++s;
   4.289 +	return s;
   4.290 +}
   4.291 +
   4.292 +static void strip_comments(char *s)
   4.293 +{
   4.294 +	while(*s && *s != '#') ++s;
   4.295 +	if(*s == '#') *s = 0;
   4.296 +}
   4.297 +
   4.298 +static void strip_trailing_spaces(char *s)
   4.299 +{
   4.300 +	char *end = s + strlen(s) - 1;
   4.301 +	while(end >= s && isspace(*end)) {
   4.302 +		*end-- = 0;
   4.303 +	}
   4.304 +}
   4.305 +
   4.306 +static char *parse_keyval(char *line)
   4.307 +{
   4.308 +	char *val;
   4.309 +	char *eq = strchr(line, '=');
   4.310 +	if(!eq) return 0;
   4.311 +
   4.312 +	*eq = 0;
   4.313 +	strip_trailing_spaces(line);
   4.314 +	val = skip_spaces(eq + 1);
   4.315 +
   4.316 +	if(!*line || !*val) {
   4.317 +		return 0;
   4.318 +	}
   4.319 +	return val;
   4.320 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/optcfg.h	Fri Nov 13 23:48:31 2015 +0200
     5.3 @@ -0,0 +1,70 @@
     5.4 +/* generic unified commandline option and config file parsing library */
     5.5 +#ifndef LIBOPTCFG_H_
     5.6 +#define LIBOPTCFG_H_
     5.7 +
     5.8 +struct optcfg;
     5.9 +
    5.10 +struct optcfg_option {
    5.11 +	char c;		/* short (optional): used only for argument parsing */
    5.12 +	char *s;	/* long: used for long options and config files */
    5.13 +	int opt;	/* the corresponding option enumeration */
    5.14 +	char *desc;	/* text description for printing usage information */
    5.15 +};
    5.16 +
    5.17 +typedef int (*optcfg_opt_callback)(struct optcfg *oc, int opt, void *cls);
    5.18 +typedef int (*optcfg_arg_callback)(struct optcfg *oc, const char *arg, void *cls);
    5.19 +
    5.20 +/* initialize the optcfg object with a valid option vector terminated by an
    5.21 + * entry with an opt value of -1 (other fields ignored for termination purposes)
    5.22 + *
    5.23 + * Example:
    5.24 + *   struct optcfg_option options[] = {
    5.25 + *       {'f', "foo", OPT_FOO, "Makes sure the foo is bar"},
    5.26 + *       {'h', "help", OPT_HELP, "Print usage information and exit"},
    5.27 + *       {0, 0, -1, 0}
    5.28 + *   };
    5.29 + *   struct optcfg *oc = optcfg_init(options);
    5.30 + */
    5.31 +struct optcfg *optcfg_init(struct optcfg_option *optv);
    5.32 +void optcfg_destroy(struct optcfg *oc);
    5.33 +
    5.34 +/* The parse_* functions call the option callback for each option.
    5.35 + *
    5.36 + * The option callback can then call optcfg_next_value to retrieve any
    5.37 + * values attached to this option. When optcfg_next_value returns 0, there
    5.38 + * are no more values available.
    5.39 + * The option callback must return 0 for success, and -1 to abort parsing.
    5.40 + */
    5.41 +void optcfg_set_opt_callback(struct optcfg *oc, optcfg_opt_callback func, void *cls);
    5.42 +/* the argument callback is only called from optcfg_parse_args(), when a non-option
    5.43 + * argument is encountered (an argument not starting with a dash)
    5.44 + */
    5.45 +void optcfg_set_arg_callback(struct optcfg *oc, optcfg_arg_callback func, void *cls);
    5.46 +
    5.47 +enum { OPTCFG_ERROR_FAIL, OPTCFG_ERROR_IGNORE };
    5.48 +void optcfg_set_error_action(struct optcfg *oc, int act);
    5.49 +
    5.50 +int optcfg_parse_args(struct optcfg *oc, int argc, char **argv);
    5.51 +int optcfg_parse_config_file(struct optcfg *oc, const char *fname);
    5.52 +int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp);
    5.53 +int optcfg_parse_config_line(struct optcfg *oc, const char *line);
    5.54 +/* TODO custom I/O callback version of config file parsing */
    5.55 +
    5.56 +/* call optcfg_next_value in the option callback to retrieve the next value
    5.57 + * of the current option. returns 0 if there is no next value.
    5.58 + */
    5.59 +char *optcfg_next_value(struct optcfg *oc);
    5.60 +
    5.61 +/* helper function which can be used to print the available options */
    5.62 +void optcfg_print_options(struct optcfg *oc);
    5.63 +
    5.64 +/* helper functions to convert value strings to typed values
    5.65 + * returns 0 for success and value is returned through the valret pointer,
    5.66 + * otherwise it returns -1 for type mismatch, and valret contents are undefined
    5.67 + */
    5.68 +int optcfg_bool_value(char *str, int *valret);	/* accepts yes/no, true/false, 1/0 */
    5.69 +int optcfg_int_value(char *str, int *valret);
    5.70 +int optcfg_float_value(char *str, float *valret);
    5.71 +
    5.72 +
    5.73 +#endif	/* LIBOPTCFG_H_ */