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_ */