a68de58269a65b534722a154f848a7481c8998b0
larrym
  Wed Apr 18 13:32:59 2012 -0700
support escaped chars in json strings
diff --git src/hg/lib/jsHelper.c src/hg/lib/jsHelper.c
index 5dc89f1..6ccc920 100644
--- src/hg/lib/jsHelper.c
+++ src/hg/lib/jsHelper.c
@@ -747,51 +747,87 @@
     char c = s[*posPtr];
     if (!isspace(c))
 	return;
     (*posPtr)++;
     }
 }
 
 static void getSpecificChar(char c, char *str, int *posPtr)
 {
 // get specified char from string or errAbort
 if(str[*posPtr] != c)
     errAbort("Unexpected character '%c' (expected '%c') - string position %d\n", str[*posPtr], c, *posPtr);
 (*posPtr)++;
 }
 
+static char *getString(char *str, int *posPtr)
+{
+// read a double-quote delimited string; we handle backslash escaping.
+// returns allocated string.
+char prevChar = 0;
+int i;
+struct dyString *ds = dyStringNew(1024);
+getSpecificChar('"', str, posPtr);
+for(i = 0;; i++)
+    {
+    char c = str[*posPtr + i];
+    if(!c)
+        errAbort("Premature end of string (missing trailing double-quote); string position '%d'", *posPtr);
+    else if(prevChar == '\\')
+        {
+        switch(c)
+            {
+            case 'n':
+                c = '\n';
+                break;
+            case 'r':
+                c = '\r';
+                break;
+            case 't':
+                c = '\t';
+                break;
+            }
+        dyStringAppendC(ds, c);
+        prevChar = 0;   // watch out for edge case where this is an escaped backslash
+        }
+    else if(c == '"')
+        break;
+    else if(c != '\\')
+        {
+        dyStringAppendC(ds, c);
+        prevChar = c;
+        }
+    }
+*posPtr += i;
+getSpecificChar('"', str, posPtr);
+return dyStringCannibalize(&ds);
+}
+
 static struct jsonElement *jsonParseExpression(char *str, int *posPtr);
 
 static struct jsonElement *jsonParseObject(char *str, int *posPtr)
 {
 struct hash *h = newHash(0);
 getSpecificChar('{', str, posPtr);
 while(str[*posPtr] != '}')
     {
-    int i;
+    // parse out a name : val pair
     skipLeadingSpacesWithPos(str, posPtr);
-    getSpecificChar('"', str, posPtr);
-    for(i = 0; str[*posPtr + i] && str[*posPtr + i] != '"'; i++)
-        ;
-    char *name = cloneStringZ(str + *posPtr, i);
-    *posPtr += i;
-    getSpecificChar('"', str, posPtr);
-
+    char *name = getString(str, posPtr);
     skipLeadingSpacesWithPos(str, posPtr);
     getSpecificChar(':', str, posPtr);
     skipLeadingSpacesWithPos(str, posPtr);
-
     hashAdd(h, name, jsonParseExpression(str, posPtr));
     skipLeadingSpacesWithPos(str, posPtr);
     if(str[*posPtr] == ',')
         (*posPtr)++;
     else
         break;
     }
 skipLeadingSpacesWithPos(str, posPtr);
 getSpecificChar('}', str, posPtr);
 return (struct jsonElement *) newJsonHash(h);
 }
 
 static struct jsonElement *jsonParseList(char *str, int *posPtr)
 {
 struct slRef *list = NULL;
@@ -805,38 +841,31 @@
     slAddHead(&list, e);
     skipLeadingSpacesWithPos(str, posPtr);
     if(str[*posPtr] == ',')
         (*posPtr)++;
     else
         break;
     }
 skipLeadingSpacesWithPos(str, posPtr);
 getSpecificChar(']', str, posPtr);
 slReverse(&list);
 return (struct jsonElement *) newJsonList(list);
 }
 
 static struct jsonElement *jsonParseString(char *str, int *posPtr)
 {
-int i;
-getSpecificChar('"', str, posPtr);
-for(i = 0; str[*posPtr + i] && str[*posPtr + i] != '"'; i++)
-    ;
-char *val = cloneStringZ(str + *posPtr, i);
-*posPtr += i;
-getSpecificChar('"', str, posPtr);
-return (struct jsonElement *) newJsonString(val);
+return (struct jsonElement *) newJsonString(getString(str, posPtr));
 }
 
 static struct jsonElement *jsonParseBoolean(char *str, int *posPtr)
 {
 struct jsonBooleanElement *ele = NULL;
 int i;
 for(i = 0; str[*posPtr + i] && isalpha(str[*posPtr + i]); i++);
     ;
 char *val = cloneStringZ(str + *posPtr, i);
 if(sameWord(val, "true"))
     ele = newJsonBoolean(TRUE);
 else if(sameWord(val, "false"))
     ele =  newJsonBoolean(FALSE);
 else
     errAbort("Invalid boolean value '%s'; pos: %d", val, *posPtr);