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. */