1203d7c21e26bd5d57154cc5c0c42780b8b5ca1a
chmalee
  Tue Oct 27 10:11:47 2020 -0700
Fixing bug in mouseOver trackDb setting, if a field to be substituted is a prefix to another field to be substituted, allow user to wrap one/both fields in curly braces like shell in order to protect the substitution

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 0794478..3dc1c91 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -9401,39 +9401,56 @@
 return list;
 }
 
 static struct dyString *subMultiField(char *pattern, int fieldCount,
                                  char *in[], char *out[])
 /* Substitute $in with out values in pattern */
 {
 int i;
 struct dyString *s = newDyString(256), *d = NULL;
 dyStringAppend(s, pattern);
 for (i=0; i<fieldCount; ++i)
     {
     if (out[i]==NULL)
         continue;
 
-    // prefix field with $
+    // If a field is a prefix or suffix to another field, for example 'chrom' and 'chromStart'
+    // we don't want to erroneously sub out the 'chrom' in 'chromStart'. Allow the wrapping
+    // protected fields in ${} to prevent the substitution:
     char *field = in[i];
-    char *spec = needMem(strlen(field) + 2);
+    int fieldLen = strlen(field);
+    char *spec = needMem(fieldLen + 2);
+    char *strictSpec = needMem(fieldLen + 4);
     *spec = '$';
+    *strictSpec = '$';
+    strictSpec[1] = '{';
     strcpy(spec + 1, field);
+    strcpy(strictSpec + 2, field);
+    strictSpec[fieldLen + 2] = '}';
+    strictSpec[fieldLen + 3] = '\0';
 
+    if (stringIn(strictSpec, s->string))
+        {
+        d = dyStringSub(s->string, strictSpec, out[i]);
+        s = d;
+        }
+    // the user may have both a ${} enclosed instance and a non-enclosed one!
     d = dyStringSub(s->string, spec, out[i]);
+
     dyStringFree(&s);
     freeMem(spec);
+    freeMem(strictSpec);
     s = d;
     d = NULL;
     }
 return s;
 }
 
 char *replaceFieldInPattern(char *pattern, int fieldCount, char **fieldNames, char **fieldVals)
 /* Replace $fieldName in pattern with value.  Used in trackDb mouseOver setting */
 {
 struct dyString *ds = subMultiField(pattern, fieldCount, fieldNames, fieldVals);
 return dyStringCannibalize(&ds);
 }
 
 static struct dyString *subMulti(char *orig, int subCount,
                                  char *in[], char *out[])