nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #ifdef _MSC_VER nuclear@0: #include nuclear@0: #else nuclear@0: #include nuclear@0: #endif nuclear@0: #include "optcfg.h" nuclear@0: nuclear@0: struct optcfg { nuclear@0: struct optcfg_option *optlist; nuclear@0: nuclear@0: optcfg_opt_callback opt_func; nuclear@0: void *opt_cls; nuclear@0: optcfg_arg_callback arg_func; nuclear@0: void *arg_cls; nuclear@0: nuclear@0: int err_abort; nuclear@0: nuclear@0: /* argument parsing state */ nuclear@0: char **argv; nuclear@0: int argidx; nuclear@0: nuclear@0: /* config file parsing state */ nuclear@0: const char *cfg_fname; nuclear@0: int cfg_nline; nuclear@0: char *cfg_value; nuclear@0: }; nuclear@0: nuclear@0: static int get_opt(struct optcfg *oc, const char *s); nuclear@0: static char *skip_spaces(char *s); nuclear@0: static void strip_comments(char *s); nuclear@0: static void strip_trailing_spaces(char *s); nuclear@0: static char *parse_keyval(char *line); nuclear@0: nuclear@0: nuclear@0: struct optcfg *optcfg_init(struct optcfg_option *optv) nuclear@0: { nuclear@0: struct optcfg *oc; nuclear@0: nuclear@0: if(!(oc = calloc(1, sizeof *oc))) { nuclear@0: return 0; nuclear@0: } nuclear@0: oc->optlist = optv; nuclear@0: oc->err_abort = 1; nuclear@0: return oc; nuclear@0: } nuclear@0: nuclear@0: void optcfg_destroy(struct optcfg *oc) nuclear@0: { nuclear@0: memset(oc, 0, sizeof *oc); nuclear@0: free(oc); nuclear@0: } nuclear@0: nuclear@0: void optcfg_set_opt_callback(struct optcfg *oc, optcfg_opt_callback func, void *cls) nuclear@0: { nuclear@0: oc->opt_func = func; nuclear@0: oc->opt_cls = cls; nuclear@0: } nuclear@0: nuclear@0: void optcfg_set_arg_callback(struct optcfg *oc, optcfg_arg_callback func, void *cls) nuclear@0: { nuclear@0: oc->arg_func = func; nuclear@0: oc->arg_cls = cls; nuclear@0: } nuclear@0: nuclear@0: void optcfg_set_error_action(struct optcfg *oc, int act) nuclear@0: { nuclear@0: if(act == OPTCFG_ERROR_FAIL) { nuclear@0: oc->err_abort = 1; nuclear@0: } else if(act == OPTCFG_ERROR_IGNORE) { nuclear@0: oc->err_abort = 0; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: int optcfg_parse_args(struct optcfg *oc, int argc, char **argv) nuclear@0: { nuclear@0: int i; nuclear@0: nuclear@0: oc->argv = argv; nuclear@0: nuclear@0: for(i=1; iargidx = i; nuclear@0: nuclear@0: if(argv[i][0] == '-') { nuclear@0: if(oc->opt_func) { nuclear@0: int o = get_opt(oc, argv[i]); nuclear@0: if(o == -1 || oc->opt_func(oc, o, oc->opt_cls) == -1) { nuclear@0: if(oc->err_abort) { nuclear@0: return -1; nuclear@0: } nuclear@0: } nuclear@0: } else { nuclear@0: fprintf(stderr, "unexpected option: %s\n", argv[i]); nuclear@0: if(oc->err_abort) { nuclear@0: return -1; nuclear@0: } nuclear@0: } nuclear@0: } else { nuclear@0: if(oc->arg_func) { nuclear@0: if(oc->arg_func(oc, argv[i], oc->arg_cls) == -1) { nuclear@0: if(oc->err_abort) { nuclear@0: return -1; nuclear@0: } nuclear@0: } nuclear@0: } else { nuclear@0: fprintf(stderr, "unexpected argument: %s\n", argv[i]); nuclear@0: if(oc->err_abort) { nuclear@0: return -1; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: i = oc->argidx; nuclear@0: } nuclear@0: nuclear@0: oc->argidx = 0; /* done parsing args */ nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: int optcfg_parse_config_file(struct optcfg *oc, const char *fname) nuclear@0: { nuclear@0: int res; nuclear@0: FILE *fp = fopen(fname, "rb"); nuclear@0: if(!fp) { nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: oc->cfg_fname = fname; nuclear@0: res = optcfg_parse_config_stream(oc, fp); nuclear@0: oc->cfg_fname = 0; nuclear@0: fclose(fp); nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp) nuclear@0: { nuclear@0: char buf[512]; nuclear@0: nuclear@0: oc->cfg_nline = 0; nuclear@0: while(fgets(buf, sizeof buf, fp)) { nuclear@0: ++oc->cfg_nline; nuclear@0: nuclear@0: if(optcfg_parse_config_line(oc, buf) == -1) { nuclear@0: if(oc->err_abort) { nuclear@0: return -1; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: int optcfg_parse_config_line(struct optcfg *oc, const char *line) nuclear@0: { nuclear@0: int opt, len; nuclear@0: char *start, *val, *buf; nuclear@0: nuclear@0: len = strlen(line); nuclear@0: buf = alloca(len + 1); nuclear@0: memcpy(buf, line, len); nuclear@0: nuclear@0: start = skip_spaces(buf); nuclear@0: strip_comments(start); nuclear@0: strip_trailing_spaces(start); nuclear@0: if(!*start) { nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if(!(val = parse_keyval(start))) { nuclear@0: fprintf(stderr, "error parsing %s line %d: invalid syntax\n", oc->cfg_fname ? oc->cfg_fname : "", oc->cfg_nline); nuclear@0: return -1; nuclear@0: } nuclear@0: oc->cfg_value = val; nuclear@0: if((opt = get_opt(oc, start)) == -1) { nuclear@0: fprintf(stderr, "error parsing %s line %d: unknown option: %s\n", oc->cfg_fname ? oc->cfg_fname : "", nuclear@0: oc->cfg_nline, start); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: if(oc->opt_func) { nuclear@0: if(oc->opt_func(oc, opt, oc->opt_cls) == -1) { nuclear@0: return -1; nuclear@0: } nuclear@0: } nuclear@0: oc->cfg_value = 0; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: char *optcfg_next_value(struct optcfg *oc) nuclear@0: { nuclear@0: if(oc->argidx) { /* we're in the middle of parsing arguments, so get the next one */ nuclear@0: return oc->argv[++oc->argidx]; nuclear@0: } nuclear@0: if(oc->cfg_value) { nuclear@0: char *val = oc->cfg_value; nuclear@0: oc->cfg_value = 0; nuclear@0: return val; nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void optcfg_print_options(struct optcfg *oc) nuclear@0: { nuclear@0: int i; nuclear@0: for(i=0; oc->optlist[i].opt != -1; i++) { nuclear@0: struct optcfg_option *opt = oc->optlist + i; nuclear@0: nuclear@0: if(opt->c) { nuclear@0: printf(" -%c", opt->c); nuclear@0: } else { nuclear@0: printf(" "); nuclear@0: } nuclear@0: if(opt->s) { nuclear@0: printf("%c-%s: ", opt->c ? ',' : ' ', opt->s); nuclear@0: } else { nuclear@0: printf(": "); nuclear@0: } nuclear@0: printf("%s\n", opt->desc ? opt->desc : "undocumented"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: int optcfg_bool_value(char *s, int *valret) nuclear@0: { nuclear@0: if(strcasecmp(s, "yes") == 0 || strcasecmp(s, "true") == 0 || strcmp(s, "1") == 0) { nuclear@0: *valret = 1; nuclear@0: return 0; nuclear@0: } nuclear@0: if(strcasecmp(s, "no") == 0 || strcasecmp(s, "false") == 0 || strcmp(s, "0") == 0) { nuclear@0: *valret = 0; nuclear@0: return 0; nuclear@0: } nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: int optcfg_int_value(char *str, int *valret) nuclear@0: { nuclear@0: char *endp; nuclear@0: *valret = strtol(str, &endp, 0); nuclear@0: if(endp == str) { nuclear@0: return -1; nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: int optcfg_float_value(char *str, float *valret) nuclear@0: { nuclear@0: char *endp; nuclear@0: *valret = strtod(str, &endp); nuclear@0: if(endp == str) { nuclear@0: return -1; nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: static int get_opt(struct optcfg *oc, const char *arg) nuclear@0: { nuclear@0: int i; nuclear@0: nuclear@0: if(arg[0] != '-' || !arg[1]) { nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: if(arg[2]) { /* match long options */ nuclear@0: for(i=0; oc->optlist[i].opt != -1; i++) { nuclear@0: if(strcmp(arg + 1, oc->optlist[i].s) == 0) { nuclear@0: return i; nuclear@0: } nuclear@0: } nuclear@0: } else { nuclear@0: for(i=0; oc->optlist[i].opt != -1; i++) { nuclear@0: if(arg[1] == oc->optlist[i].c) { nuclear@0: return i; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: static char *skip_spaces(char *s) nuclear@0: { nuclear@0: while(*s && isspace(*s)) ++s; nuclear@0: return s; nuclear@0: } nuclear@0: nuclear@0: static void strip_comments(char *s) nuclear@0: { nuclear@0: while(*s && *s != '#') ++s; nuclear@0: if(*s == '#') *s = 0; nuclear@0: } nuclear@0: nuclear@0: static void strip_trailing_spaces(char *s) nuclear@0: { nuclear@0: char *end = s + strlen(s) - 1; nuclear@0: while(end >= s && isspace(*end)) { nuclear@0: *end-- = 0; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static char *parse_keyval(char *line) nuclear@0: { nuclear@0: char *val; nuclear@0: char *eq = strchr(line, '='); nuclear@0: if(!eq) return 0; nuclear@0: nuclear@0: *eq = 0; nuclear@0: strip_trailing_spaces(line); nuclear@0: val = skip_spaces(eq + 1); nuclear@0: nuclear@0: if(!*line || !*val) { nuclear@0: return 0; nuclear@0: } nuclear@0: return val; nuclear@0: }