xglcomp
diff src/optcfg/optcfg.c @ 0:d9b3fba68705
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 21 Jan 2016 08:45:31 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/optcfg/optcfg.c Thu Jan 21 08:45:31 2016 +0200 1.3 @@ -0,0 +1,337 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <ctype.h> 1.8 +#ifdef _MSC_VER 1.9 +#include <malloc.h> 1.10 +#else 1.11 +#include <alloca.h> 1.12 +#endif 1.13 +#include "optcfg.h" 1.14 + 1.15 +struct optcfg { 1.16 + struct optcfg_option *optlist; 1.17 + 1.18 + optcfg_opt_callback opt_func; 1.19 + void *opt_cls; 1.20 + optcfg_arg_callback arg_func; 1.21 + void *arg_cls; 1.22 + 1.23 + int err_abort; 1.24 + 1.25 + /* argument parsing state */ 1.26 + char **argv; 1.27 + int argidx; 1.28 + 1.29 + /* config file parsing state */ 1.30 + const char *cfg_fname; 1.31 + int cfg_nline; 1.32 + char *cfg_value; 1.33 +}; 1.34 + 1.35 +static int get_opt(struct optcfg *oc, const char *s); 1.36 +static char *skip_spaces(char *s); 1.37 +static void strip_comments(char *s); 1.38 +static void strip_trailing_spaces(char *s); 1.39 +static char *parse_keyval(char *line); 1.40 + 1.41 + 1.42 +struct optcfg *optcfg_init(struct optcfg_option *optv) 1.43 +{ 1.44 + struct optcfg *oc; 1.45 + 1.46 + if(!(oc = calloc(1, sizeof *oc))) { 1.47 + return 0; 1.48 + } 1.49 + oc->optlist = optv; 1.50 + oc->err_abort = 1; 1.51 + return oc; 1.52 +} 1.53 + 1.54 +void optcfg_destroy(struct optcfg *oc) 1.55 +{ 1.56 + memset(oc, 0, sizeof *oc); 1.57 + free(oc); 1.58 +} 1.59 + 1.60 +void optcfg_set_opt_callback(struct optcfg *oc, optcfg_opt_callback func, void *cls) 1.61 +{ 1.62 + oc->opt_func = func; 1.63 + oc->opt_cls = cls; 1.64 +} 1.65 + 1.66 +void optcfg_set_arg_callback(struct optcfg *oc, optcfg_arg_callback func, void *cls) 1.67 +{ 1.68 + oc->arg_func = func; 1.69 + oc->arg_cls = cls; 1.70 +} 1.71 + 1.72 +void optcfg_set_error_action(struct optcfg *oc, int act) 1.73 +{ 1.74 + if(act == OPTCFG_ERROR_FAIL) { 1.75 + oc->err_abort = 1; 1.76 + } else if(act == OPTCFG_ERROR_IGNORE) { 1.77 + oc->err_abort = 0; 1.78 + } 1.79 +} 1.80 + 1.81 +int optcfg_parse_args(struct optcfg *oc, int argc, char **argv) 1.82 +{ 1.83 + int i; 1.84 + 1.85 + oc->argv = argv; 1.86 + 1.87 + for(i=1; i<argc; i++) { 1.88 + oc->argidx = i; 1.89 + 1.90 + if(argv[i][0] == '-') { 1.91 + if(oc->opt_func) { 1.92 + int o = get_opt(oc, argv[i]); 1.93 + if(o == -1 || oc->opt_func(oc, o, oc->opt_cls) == -1) { 1.94 + if(oc->err_abort) { 1.95 + return -1; 1.96 + } 1.97 + } 1.98 + } else { 1.99 + fprintf(stderr, "unexpected option: %s\n", argv[i]); 1.100 + if(oc->err_abort) { 1.101 + return -1; 1.102 + } 1.103 + } 1.104 + } else { 1.105 + if(oc->arg_func) { 1.106 + if(oc->arg_func(oc, argv[i], oc->arg_cls) == -1) { 1.107 + if(oc->err_abort) { 1.108 + return -1; 1.109 + } 1.110 + } 1.111 + } else { 1.112 + fprintf(stderr, "unexpected argument: %s\n", argv[i]); 1.113 + if(oc->err_abort) { 1.114 + return -1; 1.115 + } 1.116 + } 1.117 + } 1.118 + 1.119 + i = oc->argidx; 1.120 + } 1.121 + 1.122 + oc->argidx = 0; /* done parsing args */ 1.123 + return 0; 1.124 +} 1.125 + 1.126 +int optcfg_parse_config_file(struct optcfg *oc, const char *fname) 1.127 +{ 1.128 + int res; 1.129 + FILE *fp = fopen(fname, "rb"); 1.130 + if(!fp) { 1.131 + return -1; 1.132 + } 1.133 + 1.134 + oc->cfg_fname = fname; 1.135 + res = optcfg_parse_config_stream(oc, fp); 1.136 + oc->cfg_fname = 0; 1.137 + fclose(fp); 1.138 + return res; 1.139 +} 1.140 + 1.141 +int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp) 1.142 +{ 1.143 + char buf[512]; 1.144 + 1.145 + oc->cfg_nline = 0; 1.146 + while(fgets(buf, sizeof buf, fp)) { 1.147 + ++oc->cfg_nline; 1.148 + 1.149 + if(optcfg_parse_config_line(oc, buf) == -1) { 1.150 + if(oc->err_abort) { 1.151 + return -1; 1.152 + } 1.153 + } 1.154 + } 1.155 + return 0; 1.156 +} 1.157 + 1.158 +int optcfg_parse_config_line(struct optcfg *oc, const char *line) 1.159 +{ 1.160 + int opt, len; 1.161 + char *start, *val, *buf; 1.162 + 1.163 + len = strlen(line); 1.164 + buf = alloca(len + 1); 1.165 + memcpy(buf, line, len + 1); 1.166 + 1.167 + start = skip_spaces(buf); 1.168 + strip_comments(start); 1.169 + strip_trailing_spaces(start); 1.170 + if(!*start) { 1.171 + return 0; 1.172 + } 1.173 + 1.174 + if(!(val = parse_keyval(start))) { 1.175 + fprintf(stderr, "error parsing %s line %d: invalid syntax\n", oc->cfg_fname ? oc->cfg_fname : "", oc->cfg_nline); 1.176 + return -1; 1.177 + } 1.178 + oc->cfg_value = val; 1.179 + if((opt = get_opt(oc, start)) == -1) { 1.180 + fprintf(stderr, "error parsing %s line %d: unknown option: %s\n", oc->cfg_fname ? oc->cfg_fname : "", 1.181 + oc->cfg_nline, start); 1.182 + return -1; 1.183 + } 1.184 + 1.185 + if(oc->opt_func) { 1.186 + if(oc->opt_func(oc, opt, oc->opt_cls) == -1) { 1.187 + return -1; 1.188 + } 1.189 + } 1.190 + oc->cfg_value = 0; 1.191 + return 0; 1.192 +} 1.193 + 1.194 +int optcfg_enabled_value(struct optcfg *oc, int *enabledp) 1.195 +{ 1.196 + if(oc->argidx) { 1.197 + *enabledp = 1; /* TODO take -no- prefix into account */ 1.198 + } else { 1.199 + char *val = optcfg_next_value(oc); 1.200 + if(optcfg_bool_value(val, enabledp) == -1) { 1.201 + return -1; 1.202 + } 1.203 + } 1.204 + return 0; 1.205 +} 1.206 + 1.207 + 1.208 +char *optcfg_next_value(struct optcfg *oc) 1.209 +{ 1.210 + if(oc->argidx) { /* we're in the middle of parsing arguments, so get the next one */ 1.211 + return oc->argv[++oc->argidx]; 1.212 + } 1.213 + if(oc->cfg_value) { 1.214 + char *val = oc->cfg_value; 1.215 + oc->cfg_value = 0; 1.216 + return val; 1.217 + } 1.218 + return 0; 1.219 +} 1.220 + 1.221 +void optcfg_print_options(struct optcfg *oc) 1.222 +{ 1.223 + int i; 1.224 + for(i=0; oc->optlist[i].opt != -1; i++) { 1.225 + struct optcfg_option *opt = oc->optlist + i; 1.226 + 1.227 + if(opt->c) { 1.228 + printf(" -%c", opt->c); 1.229 + } else { 1.230 + printf(" "); 1.231 + } 1.232 + if(opt->s) { 1.233 + printf("%c-%s: ", opt->c ? ',' : ' ', opt->s); 1.234 + } else { 1.235 + printf(": "); 1.236 + } 1.237 + printf("%s\n", opt->desc ? opt->desc : "undocumented"); 1.238 + } 1.239 +} 1.240 + 1.241 +int optcfg_bool_value(char *s, int *valret) 1.242 +{ 1.243 + if(strcasecmp(s, "yes") == 0 || strcasecmp(s, "true") == 0 || strcmp(s, "1") == 0) { 1.244 + *valret = 1; 1.245 + return 0; 1.246 + } 1.247 + if(strcasecmp(s, "no") == 0 || strcasecmp(s, "false") == 0 || strcmp(s, "0") == 0) { 1.248 + *valret = 0; 1.249 + return 0; 1.250 + } 1.251 + return -1; 1.252 +} 1.253 + 1.254 +int optcfg_int_value(char *str, int *valret) 1.255 +{ 1.256 + char *endp; 1.257 + *valret = strtol(str, &endp, 0); 1.258 + if(endp == str) { 1.259 + return -1; 1.260 + } 1.261 + return 0; 1.262 +} 1.263 + 1.264 +int optcfg_float_value(char *str, float *valret) 1.265 +{ 1.266 + char *endp; 1.267 + *valret = strtod(str, &endp); 1.268 + if(endp == str) { 1.269 + return -1; 1.270 + } 1.271 + return 0; 1.272 +} 1.273 + 1.274 + 1.275 + 1.276 +static int get_opt(struct optcfg *oc, const char *arg) 1.277 +{ 1.278 + int i, ndashes = 0; 1.279 + 1.280 + while(*arg && *arg == '-') { 1.281 + ndashes++; 1.282 + arg++; 1.283 + } 1.284 + 1.285 + if(ndashes > 2) { 1.286 + arg -= ndashes; 1.287 + } 1.288 + 1.289 + if(arg[1]) { /* match long options */ 1.290 + for(i=0; oc->optlist[i].opt != -1; i++) { 1.291 + if(strcmp(arg, oc->optlist[i].s) == 0) { 1.292 + return i; 1.293 + } 1.294 + } 1.295 + } else { 1.296 + for(i=0; oc->optlist[i].opt != -1; i++) { 1.297 + if(arg[0] == oc->optlist[i].c) { 1.298 + return i; 1.299 + } 1.300 + } 1.301 + } 1.302 + return -1; 1.303 +} 1.304 + 1.305 +static char *skip_spaces(char *s) 1.306 +{ 1.307 + while(*s && isspace(*s)) ++s; 1.308 + return s; 1.309 +} 1.310 + 1.311 +static void strip_comments(char *s) 1.312 +{ 1.313 + while(*s && *s != '#') ++s; 1.314 + if(*s == '#') *s = 0; 1.315 +} 1.316 + 1.317 +static void strip_trailing_spaces(char *s) 1.318 +{ 1.319 + char *end = s + strlen(s) - 1; 1.320 + while(end >= s && isspace(*end)) { 1.321 + *end-- = 0; 1.322 + } 1.323 +} 1.324 + 1.325 +static char *parse_keyval(char *line) 1.326 +{ 1.327 + char *val; 1.328 + char *eq = strchr(line, '='); 1.329 + if(!eq) return 0; 1.330 + 1.331 + *eq = 0; 1.332 + strip_trailing_spaces(line); 1.333 + val = skip_spaces(eq + 1); 1.334 + strip_trailing_spaces(val); 1.335 + 1.336 + if(!*line || !*val) { 1.337 + return 0; 1.338 + } 1.339 + return val; 1.340 +}