5e76a5c1f66ec03777d0079acdd605e3d6081e60 andypohl Tue Oct 29 12:41:11 2013 +0100 Negative numbers as arguments to command-line programs using options seemed to be a problem. diff --git src/lib/options.c src/lib/options.c index 9cc4981..2144694 100644 --- src/lib/options.c +++ src/lib/options.c @@ -105,47 +105,68 @@ { valList = newSlName(val); hashAdd(hash, name, valList); } else { struct slName *el = newSlName(val); slAddTail(valList, el); /* added next multi option */ } break; default: errAbort("UNIMPLEMENTED: multiple instances of a non-string option is not currently implemented"); } } -static boolean parseAnOption(struct hash *hash, char *arg, struct optionSpec *optionSpecs) +static boolean parseAnOption(struct hash *hash, char *arg, struct optionSpec *optionSpecs, boolean keepNumbers) /* Parse a single option argument and add to the hash, validating if * optionSpecs is not NULL. Return TRUE if it's arg is an option argument - * FALSE if it's not. + * FALSE if it's not. If boolean keepNumbers is set, then return FALSE + * when encountering negative ints or floats. */ { char *name, *val; char *eqPtr = strchr(arg, '='); if (!((eqPtr != NULL) || (arg[0] == '-'))) return FALSE; /* not an option */ /* A dash by itself is not an option. It can mean * negative strand for some of the DNA oriented utilities. */ if (arg[0] == '-' && (arg[1] == 0 || isspace(arg[1]))) return FALSE; +if ((keepNumbers) && (arg[0] == '-')) + { + int i = 1; + int num_dec = 0; + int num_dig = 0; + int num_other = 0; + while (i < strlen(arg) + { + if (isdigit(arg[i])) + num_dig++; + else if (arg[i] == '.') + num_dec++; + else + num_other++; + i++; + } + if ((num_dig > 0) && (num_dec < 2) && (num_other == 0)) + return FALSE; + } + /* We treat this=that as an option only if the '=' happens before any non-alphanumeric * characters. This lets us have URLs and SQL statements in the command line even though * they can have equals in them. */ if (eqPtr != NULL) { char *s, c; for (s=arg; s < eqPtr; ++s) { c = *s; if (c != '_' && c != '-' && !isalnum(c)) return FALSE; } } name = arg; @@ -169,49 +190,49 @@ { struct optionSpec *spec = matchingOption(name, optionSpecs); if (spec != NULL && (spec->flags & OPTION_MULTI)) /* process multiple instances of option */ parseMultiOption(hash, name, val, spec); else hashAdd(hash, name, val); } if (eqPtr != NULL) *eqPtr = '='; return TRUE; } static struct hash *parseOptions(int *pArgc, char *argv[], boolean justFirst, - struct optionSpec *optionSpecs) + struct optionSpec *optionSpecs, boolean keepNumbers) /* Parse and optionally validate options */ { int i, origArgc, newArgc = 1; char **rdPt = argv+1, **wrPt = argv+1; struct hash *hash = newHash(6); origArgc = *pArgc; /* parse arguments */ for (i=1; i<origArgc; ++i) { if (sameString(*rdPt, "--")) { rdPt++; i++; break; } - if (!parseAnOption(hash, *rdPt, optionSpecs)) + if (!parseAnOption(hash, *rdPt, optionSpecs, keepNumbers)) { /* not an option */ if (justFirst) break; *wrPt++ = *rdPt; newArgc++; } rdPt++; } /* copy any remaining positional args */ for (; i<origArgc; ++i) { *wrPt++ = *rdPt++; newArgc++; @@ -219,52 +240,58 @@ *pArgc = newArgc; *wrPt = NULL; return hash; } struct hash *optionParseIntoHash(int *pArgc, char *argv[], boolean justFirst) /* Read options in command line (only up to first real argument) into * options hash. Options come in three forms: * -option words starting with dash * option=val words with = in the middle * -option=val combining the two. * The resulting hash will be keyed by the option name with the val * string for value. For '-option' types the value is 'on'. */ { -return parseOptions(pArgc, argv, justFirst, NULL); +return parseOptions(pArgc, argv, justFirst, NULL, FALSE); +} + +struct hash *optionParseIntoHashExceptNumbers(int *pArgc, char *argv[], boolean justFirst); +/* Read options in argc/argv into a hash (except negative numbers) of your own choosing. */ +{ +return parseOptions(pArgc, argv, justFirst, NULL, TRUE); } static struct hash *options = NULL; static struct optionSpec *optionSpecification = NULL; static void setOptions(struct hash *hash) /* Set global options hash to hash, and also do processing * of log file and other common options. */ { options = hash; if (optionExists("verbose")) verboseSetLevel(optionInt("verbose", 0)); } void optionHashSome(int *pArgc, char *argv[], boolean justFirst) /* Set up option hash from command line, optionally only adding * up to first non-optional word. */ { if (options == NULL) { - struct hash *hash = parseOptions(pArgc, argv, justFirst, NULL); + struct hash *hash = parseOptions(pArgc, argv, justFirst, NULL, FALSE); setOptions(hash); } } void optionHash(int *pArgc, char *argv[]) /* Read options in command line into options hash. * Options come in three forms: * -option words starting with dash * option=val words with = in the middle * -option=val combining the two. * The resulting hash will be keyed by the option name with the val * string for value. For '-option' types the value is 'on'. */ { optionHashSome(pArgc, argv, FALSE); } @@ -283,31 +310,31 @@ * -option=val combining the two. * The resulting hash will be keyed by the option name with the val * string for value. For '-option' types the value is 'on'. * The words in argv are parsed in assending order. If a word of * "--" is encountered, argument parsing stops. * If optionSpecs is not NULL, it is an array of optionSpec that are * used to validate the options. An option must exist in the array * and the value must be convertable to the type specified in flags. * Boolean options must no value, all other options must have one. * Array is terminated by a optionSpec with a NULL name. * If array NULL, no validation is done. */ { if (options == NULL) { - struct hash *hash = parseOptions(pArgc, argv, FALSE, optionSpecs); + struct hash *hash = parseOptions(pArgc, argv, FALSE, optionSpecs, FALSE); setOptions(hash); optionSpecification = optionSpecs; } } static char *optGet(char *name) /* Lookup option name. Complain if options hash not set. */ { if (options == NULL) errAbort("optGet called before optionHash"); return hashFindVal(options, name); } char *optionVal(char *name, char *defaultVal) /* Return named option if in options hash, otherwise default. */