92a295c1ca5859158faa21bed16f4bd43d347917 galt Tue Apr 19 03:38:57 2011 -0700 make slPairListFromString handle "=" inside names(when quoted) and values diff --git src/lib/common.c src/lib/common.c index 9fc63fc..5ed0bbd 100644 --- src/lib/common.c +++ src/lib/common.c @@ -983,30 +983,143 @@ { struct slPair *el = slPairFind(list, name); if (el == NULL) return NULL; return el->val; } struct slPair *slPairListFromString(char *str,boolean respectQuotes) // Return slPair list parsed from list in string like: [name1=val1 name2=val2 ...] // if respectQuotes then string can have double quotes: [name1="val 1" "name 2"=val2 ...] // resulting pair strips quotes: {name1}={val 1},{name 2}={val2} // Returns NULL if parse error. Free this up with slPairFreeValsAndList. { if (isEmpty(str)) return NULL; + +struct slPair *list = NULL; +char name[1024]; +char val[1024]; +char buf[1024]; +char *s = str; +bool inQuote = FALSE; +char *b = buf; +char sep = '='; +char c = ' '; +int mode = 0; +while(1) + { + c = *s++; + if (mode == 0 || mode == 2) // reading name or val + { + boolean term = FALSE; + if (respectQuotes && b == buf && !inQuote && c == '"') + inQuote = TRUE; + else if (inQuote && c == '"') + term = TRUE; + else if ((c == sep || c == 0) && !inQuote) + { + term = TRUE; + --s; // rewind + } + else if (c == ' ' && !inQuote) + { + warn("slPairListFromString: Unexpected whitespace in %s", str); + return NULL; + } + else if (c == 0 && inQuote) + { + warn("slPairListFromString: Unterminated quote in %s", str); + return NULL; + } + else + { + *b++ = c; + if ((b - buf) > sizeof buf) + { + warn("slPairListFromString: pair name or value too long in %s", str); + return NULL; + } + } + if (term) + { + inQuote = FALSE; + *b = 0; + if (mode == 0) + { + safecpy(name, sizeof name, buf); + if (strlen(name)<1) + { + warn("slPairListFromString: Pair name cannot be empty in %s", str); + return NULL; + } + // Shall we check for name being alphanumeric, at least for the respectQuotes=FALSE case? + } + else // mode == 2 + { + safecpy(val, sizeof val, buf); + if (!respectQuotes && (hasWhiteSpace(name) || hasWhiteSpace(val))) // should never happen + { + warn("slPairListFromString() Unexpected white space in name=value pair: [%s]=[%s] in string=[%s]\n", name, val, str); + break; + } + slPairAdd(&list, name, cloneString(val)); + } + ++mode; + } + } + else if (mode == 1) // read required "=" sign + { + if (c != '=') + { + warn("slPairListFromString: Expected character = after name in %s", str); + return NULL; + } + ++mode; + sep = ' '; + b = buf; + } + else // (mode == 3) reading optional separating space + { + if (c == 0) + break; + if (c != ' ') + { + mode = 0; + --s; + b = buf; + sep = '='; + } + } + } +slReverse(&list); +return list; +} + +struct slPair *oopsSlPairListFromString(char *str,boolean respectQuotes) +// Do not use. +// This version does not handle '=' in the name or val field, even when quoted. +// But drosophila/trackDb.ra has fields like this since 2009: +// [best1=Best_Antibody_(FDR=1%) best25=Best_Antibody_(FDR=25%) other1=Other_Antibodies_(FDR=1%) other25=Other_Antibodies_(FDR=25%)] +// +// Return slPair list parsed from list in string like: [name1=val1 name2=val2 ...] +// if respectQuotes then string can have double quotes: [name1="val 1" "name 2"=val2 ...] +// resulting pair strips quotes: {name1}={val 1},{name 2}={val2} +// Returns NULL if parse error. Free this up with slPairFreeValsAndList. +{ +if (isEmpty(str)) + return NULL; int count = countChars(str, '=') + 1; // Should have 1 more token than '=': {"name 1"},{val1 name2},{"val 2"} if (count < 2) { warn("slPairListFromString() No pairs found in string meant to contain name=value pairs [%s]\n", str); return NULL; } char *strClone = cloneString(str); char **tokens = needMem(sizeof(char *) * count); count = chopByChar(strClone, '=',tokens,count); int ix; struct slPair *list = NULL; for (ix=1; ix < count;ix++) // starting at 1 and looking back to 0 { // name comes from prev token!