/* This file is part of Smap. Copyright (C) 2008, 2010, 2014, 2017 Sergey Poznyakoff Smap is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. Smap is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Smap. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include static struct smap_option const * find_opt(struct smap_option const *opt, const char *str, const char **value, int flags) { size_t len = strlen(str); int isbool; int delim = flags & SMAP_DELIM_MASK; if (len > 2 && (flags & SMAP_IGNORECASE ? strncasecmp : strncmp)(str, "no", 2) == 0) { *value = NULL; str += 2; isbool = 1; } else { isbool = 0; *value = str; } for (; opt->name; opt++) { if (len >= opt->len && (flags & SMAP_IGNORECASE ? strncasecmp : strncmp)(opt->name, str, opt->len) == 0 && (!isbool || opt->type == smap_opt_bool)) { int eq; const char *vp; if (delim == SMAP_DELIM_EQ) { eq = str[opt->len] == '='; vp = str + opt->len + 1; } else if (delim == SMAP_DELIM_WS) { vp = str + opt->len; eq = isspace(*vp); if (eq) do ++vp; while (*vp && isspace(*vp)); } else abort(); switch (opt->type) { case smap_opt_long: case smap_opt_string: case smap_opt_const_string: case smap_opt_enum: if (!eq) continue; *value = vp; break; case smap_opt_null: if (eq) *value = vp; else *value = NULL; break; default: if (eq) continue; break; } return opt; } } return NULL; } static int find_value(const char **enumstr, const char *value) { int i; for (i = 0; *enumstr; enumstr++, i++) if (strcmp(*enumstr, value) == 0) return i; return -1; } int smap_parseline(struct smap_option const *opt, const char *line, int flags, char **errmsg) { int rc = SMAP_PARSE_SUCCESS; long n; char *s; const char *value; struct smap_option const *p = find_opt(opt, line, &value, flags); if (!p) return SMAP_PARSE_NOENT; switch (p->type) { case smap_opt_long: n = strtol(value, &s, 0); if (*s) { *errmsg = "not a valid number"; rc = SMAP_PARSE_INVAL; break; } *(long*)p->data = n; break; case smap_opt_const: *(long*)p->data = p->v.value; break; case smap_opt_const_string: *(const char**)p->data = value; break; case smap_opt_string: *(const char**)p->data = strdup(value); break; case smap_opt_bool: if (p->v.value) { if (value) *(int*)p->data |= p->v.value; else *(int*)p->data &= ~p->v.value; } else *(int*)p->data = value != NULL; break; case smap_opt_bitmask: *(int*)p->data |= p->v.value; break; case smap_opt_bitmask_rev: *(int*)p->data &= ~p->v.value; break; case smap_opt_enum: n = find_value(p->v.enumstr, value); if (n == -1) { *errmsg = "invalid value"; rc = SMAP_PARSE_INVAL; break; } *(int*)p->data = n; break; case smap_opt_null: break; } if (p->func && p->func(p, value, errmsg)) rc = SMAP_PARSE_INVAL; return rc; } int smap_parseopt(struct smap_option const *opt, int argc, char **argv, int flags, int *pindex) { int i; int rc = 0; const char *modname = argv[0]; for (i = (flags & SMAP_PARSEOPT_PARSE_ARGV0) ? 0 : 1; i < argc; i++) { char *errmsg; int res; res = smap_parseline(opt, argv[i], SMAP_DELIM_EQ, &errmsg); if (res == SMAP_PARSE_SUCCESS) continue; else if (res == SMAP_PARSE_NOENT) { if (pindex) { if (flags & SMAP_PARSEOPT_PERMUTE) { int j; struct smap_option const *p = NULL; const char *value; for (j = i + 1; j < argc; j++) if ((p = find_opt(opt, argv[j], &value, SMAP_DELIM_EQ))) break; if (p) { char *tmp = argv[j]; argv[j] = argv[i]; argv[i] = tmp; --i; } else break; } else break; } else { smap_error("%s: %s: unknown option", modname, argv[i]); rc = 1; } } else if (res == SMAP_PARSE_INVAL) { smap_error("%s: %s: %s", modname, argv[i], errmsg); rc = 1; } } if (pindex) *pindex = i; return rc; }