9b13731e6e466aa0f781179da2d504db2f4c15a9 tdreszer Wed Mar 30 11:28:34 2011 -0700 Made slPairListFromString and slPairListToString optionally handle double quotes. Finally made mdbObjReorderByCv so that priority in cv.ra typeOfTerms is used. diff --git src/lib/common.c src/lib/common.c index cb96a41..d9799a7 100644 --- src/lib/common.c +++ src/lib/common.c @@ -970,90 +970,198 @@ for (el = list; el != NULL; el = el->next) if (sameString(name, el->name)) break; return el; } void *slPairFindVal(struct slPair *list, char *name) /* Return value associated with name in list, or NULL if not found. */ { struct slPair *el = slPairFind(list, name); if (el == NULL) return NULL; return el->val; } -struct slPair *slPairFromString(char *s) -/* Return slPair list parsed from list in string s - * name1=val1 name2=val2 ... - * Returns NULL if parse error. Free this up with - * slPairFreeValsAndList. */ +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; +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; -char *name; -char *ss = cloneString(s); -char *word = ss; -while((name = nextWord(&word))) +for (ix=1; ix < count;ix++) // starting at 1 and looking back to 0 { - char *val = strchr(name,'='); - if (!val) + // name comes from prev token! + char *name = tokens[ix -1]; + char *val = tokens[ix]; + + // Parse of val and set up ptr for next name + char *next = NULL; + if (respectQuotes && val[0] == '"') + next = skipBeyondDelimit(tokens[ix] + 1,'"'); + else + next = skipToSpaces(tokens[ix]); + if (next != NULL) { - warn("missing equals sign in name=value pair: name=[%s] in string=[%s]\n", name, s); - return NULL; + *next++ = '\0'; + tokens[ix] = next; // set up for next round. + } + else if (ix + 1 != count) // Last token should have no 'next'. + { + warn("slPairListFromString() Error parsing string meant to contain name=value pairs: [%s]\n", str); + break; + } + + if (respectQuotes && name[0] == '"') + stripEnclosingDoubleQuotes(name); + else if (hasWhiteSpace(name)) + { + warn("slPairListFromString() Unexpected white space in name=value pair: [%s]=[%s] in string=[%s]\n", name, val, str); + break; + } + if (respectQuotes && val[0] == '"') + stripEnclosingDoubleQuotes(val); + else if (hasWhiteSpace(val)) + { + warn("slPairListFromString() Unexpected white space in name=value pair: [%s]=[%s] in string=[%s]\n", name, val, str); + break; } - *val++ = 0; slPairAdd(&list, name, cloneString(val)); } -freez(&ss); +freez(&strClone); +if (ix != count) // error detected. + { + slPairFreeValsAndList(&list); + return NULL; + } slReverse(&list); return list; } -char *slPairListToString(struct slPair *list) -// Returns an allocated string of pairs in form of -// name1=val1 name2=val2 ... -// Will wrap vals in quotes if contain spaces: name3="val 3" +char *slPairListToString(struct slPair *list,boolean quoteIfSpaces) +// Returns an allocated string of pairs in form of [name1=val1 name2=val2 ...] +// If requested, will wrap name or val in quotes if contain spaces: [name1="val 1" "name 2"=val2] { -// Don't rely on dyString. We can do the accounting ourselves. +// Don't rely on dyString. We should do the accounting ourselves and not create extra dependencies. int count = 0; struct slPair *pair = list; for(;pair != NULL; pair = pair->next) { - if (pair->name == NULL || pair->val == NULL) - continue; + assert(pair->name != NULL && pair->val != NULL); // Better assert and get this over with, complete with stack count += strlen(pair->name); count += strlen((char *)(pair->val)); count += 2; // = and ' ' delimit + if (quoteIfSpaces) + { + if (hasWhiteSpace(pair->name)) + count += 2; // " and " if (hasWhiteSpace((char *)(pair->val))) count += 2; // " and " } + } if (count == 0) return NULL; + char *str = needMem(count+5); // A bit of slop -char *s = str; -for(pair = list;pair != NULL; pair = pair->next) +char *strPtr = str; +for(pair = list; pair != NULL; pair = pair->next, strPtr += strlen(strPtr)) { - if (pair->name == NULL || pair->val == NULL) - continue; + if (pair != list) // Not first cycle + *strPtr++ = ' '; + if (hasWhiteSpace(pair->name)) + { + if (quoteIfSpaces) + sprintf(strPtr,"\"%s\"=",pair->name); + else + { + warn("slPairListToString() Unexpected white space in name: [%s]\n", pair->name); + sprintf(strPtr,"%s=",pair->name); // warn but still make string + } + } + else + sprintf(strPtr,"%s=",pair->name); + strPtr += strlen(strPtr); if (hasWhiteSpace((char *)(pair->val))) - sprintf(s,"%s=\"%s\" ",pair->name,(char *)(pair->val)); + { + if (quoteIfSpaces) + sprintf(strPtr,"\"%s\"",(char *)(pair->val)); else - sprintf(s,"%s=%s ",pair->name,(char *)(pair->val)); - s += strlen(s); + { + warn("slPairListToString() Unexpected white space in val: [%s]\n", (char *)(pair->val)); + sprintf(strPtr,"%s",(char *)(pair->val)); // warn but still make string + } + } + else + sprintf(strPtr,"%s",(char *)(pair->val)); + } +return str; +} + +char *slPairNameToString(struct slPair *list, char delimiter,boolean quoteIfSpaces) +// Return string created by joining all names (ignoring vals) with the delimiter. +// If requested, will wrap name in quotes if contain spaces: [name1,"name 2" ...] +{ +int elCount = 0; +int count = 0; +struct slPair *pair = list; +for (; pair != NULL; pair = pair->next, elCount++) + { + assert(pair->name != NULL); + count += strlen(pair->name); + if (quoteIfSpaces && hasWhiteSpace(pair->name)) + count += 2; + } +count += elCount; +if (count == 0) + return NULL; + +char *str = needMem(count+5); // A bit of slop + +char *strPtr = str; +for(pair = list; pair != NULL; pair = pair->next, strPtr += strlen(strPtr)) + { + if (pair != list) + *strPtr++ = delimiter; + if (hasWhiteSpace(pair->name)) + { + if (quoteIfSpaces) + sprintf(strPtr,"\"%s\"",pair->name); + else + { + if (delimiter == ' ') // if delimied by commas, this is entirely okay! + warn("slPairListToString() Unexpected white space in name delimied by space: [%s]\n", pair->name); + sprintf(strPtr,"%s",pair->name); // warn but still make string + } + } + else + sprintf(strPtr,"%s",pair->name); } -str[strlen(str) - 1] = '\0'; // For sweetness, remove the trailing space. return str; } int slPairCmpCase(const void *va, const void *vb) /* Compare two slPairs, ignore case. */ { const struct slPair *a = *((struct slPair **)va); const struct slPair *b = *((struct slPair **)vb); return strcasecmp(a->name, b->name); } void slPairSortCase(struct slPair **pList) /* Sort slPair list, ignore case. */ { slSort(pList, slPairCmpCase); @@ -1084,30 +1192,44 @@ return strcmp((char *)(a->val), (char *)(b->val)); } void slPairValSortCase(struct slPair **pList) /* Sort slPair list on values (must be string), ignore case. */ { slSort(pList, slPairValCmpCase); } void slPairValSort(struct slPair **pList) /* Sort slPair list on values (must be string). */ { slSort(pList, slPairValCmp); } +int slPairIntCmp(const void *va, const void *vb) +// Compare two slPairs on their integer values. +{ +const struct slPair *a = *((struct slPair **)va); +const struct slPair *b = *((struct slPair **)vb); +return ((char *)(a->val) - (char *)(b->val)); // cast works and val is 0 vased integer +} + +void slPairIntSort(struct slPair **pList) +// Sort slPair list on integer values. +{ +slSort(pList, slPairIntCmp); +} + void gentleFree(void *pt) { if (pt != NULL) freeMem((char*)pt); } int differentWord(char *s1, char *s2) /* strcmp ignoring case - returns zero if strings are * the same (ignoring case) otherwise returns difference * between first non-matching characters. */ { char c1, c2; for (;;) { c1 = toupper(*s1++);