/* Random helpful option parsing functions Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. Written by Miles Bader This program 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 2, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include "parse.h" /* For each string in the comma-separated list in ARG, call ADD_FN; if ARG is empty and DEFAULT_ADD_FN isn't NULL, then call DEFAULT_ADD_FN instead. */ error_t _parse_strlist (char *arg, error_t (*add_fn)(const char *str, struct argp_state *state), error_t (*default_add_fn)(struct argp_state *state), const char *type_name, struct argp_state *state) { if (arg) while (isspace(*arg)) arg++; if (arg == NULL || *arg == '\0') if (default_add_fn) return (*default_add_fn)(state); else { argp_error (state, "Empty %s list", type_name); return EINVAL; } else { error_t err = 0; char *end = arg; void mark_end() { *end++ = '\0'; while (isspace(*end)) end++; } error_t parse_element() { char *cur = arg; if (*cur == '\0') { argp_error (state, "Empty element in %s list", type_name); return EINVAL; } arg = end; return (*add_fn)(cur, state); } while (*end != '\0' && !err) switch (*end) { case ' ': case '\t': mark_end(); if (*end == ',') mark_end(); err = parse_element(); break; case ',': mark_end(); err = parse_element(); break; default: end++; } if (! err) err = parse_element(); return err; } } /* For each string in the comma-separated list in ARG, call ADD_FN; if ARG is empty and DEFAULT_FN isn't NULL, then call ADD_FN on the resutl of calling DEFAULT_FN instead, otherwise signal an error. */ error_t parse_strlist (char *arg, error_t (*add_fn)(const char *str, struct argp_state *state), const char *(*default_fn)(struct argp_state *state), const char *type_name, struct argp_state *state) { error_t default_str_add (struct argp_state *state) { return (*add_fn)((*default_fn)(state), state); } return _parse_strlist (arg, add_fn, default_str_add, type_name, state); } /* For each numeric string in the comma-separated list in ARG, call ADD_FN; if ARG is empty and DEFAULT_FN isn't NULL, then call DEF_FN to get a number, and call ADD_FN on that, otherwise signal an error. If any member of the list isn't a number, and LOOKUP_FN isn't NULL, then it is called to return an integer for the string. LOOKUP_FN should signal an error itself it there's some problem parsing the string. */ error_t parse_numlist (char *arg, error_t (*add_fn)(unsigned num, struct argp_state *state), int (*default_fn)(struct argp_state *state), int (*lookup_fn)(const char *str, struct argp_state *state), const char *type_name, struct argp_state *state) { error_t default_num_add() { return (*add_fn)((*default_fn)(state), state); } error_t add_num_str(const char *str, struct argp_state *state) { const char *p; for (p = str; *p != '\0'; p++) if (!isdigit(*p)) { if (lookup_fn) return (*add_fn)((*lookup_fn)(str, state), state); else { argp_error (state, "%s: Invalid %s", p, type_name); return EINVAL; } return 0; } return (*add_fn) (atoi (str), state); } return _parse_strlist(arg, add_num_str, default_fn ? default_num_add : 0, type_name, state); } /* Return the index of which of a set of strings ARG matches, including non-ambiguous partial matches. CHOICE_FN should be a function that returns the name of the Nth option, or 0 if N is out of range, and KIND should be a string that describes what's being matched, for use in error messages. If ALLOW_MISMATCHES is true, then -1 is returned in the event that ARG matches no entry , otherwise, an error message is printed and the program exits in this event. If ARG is an ambiguous match, an error message is printed and the program exits. */ int parse_enum (const char *arg, const char *(*choice_fn)(unsigned n), const char *kind, int allow_mismatches, struct argp_state *state) { const char *choice; int arglen = strlen (arg); int n = 0; int partial_match = -1; while ((choice = (*choice_fn)(n)) != NULL) if (strcasecmp (choice, arg) == 0) return n; else { if (strncasecmp (choice, arg, arglen) == 0) { if (partial_match >= 0) { argp_error (state, "%s: Ambiguous %s", arg, kind); return -1; } else partial_match = n; } n++; } if (partial_match < 0 && !allow_mismatches) argp_error (state, "%s: Invalid %s", arg, kind); return partial_match; }