4141dea14ea022600d97690b388dece619fe8256 angie Mon Jan 26 15:45:56 2015 -0800 All Tracks / All Tables groups are not yet working in hgAi; I have a plan to fix it right, but for now, just omit those groups. refs #14579 diff --git src/hg/lib/cartJson.c src/hg/lib/cartJson.c index 4b517b1..a0e4218 100644 --- src/hg/lib/cartJson.c +++ src/hg/lib/cartJson.c @@ -1,731 +1,743 @@ /* cartJson - parse and execute JSON commands to update cart and/or return cart data as JSON. */ #include "common.h" #include "cartJson.h" #include "cartTrackDb.h" #include "cheapcgi.h" #include "grp.h" #include "hdb.h" #include "hgFind.h" #include "htmshell.h" #include "hubConnect.h" #include "hui.h" #include "jsonParse.h" #include "obscure.h" #include "regexHelper.h" #include "suggest.h" #include "trackHub.h" #include "web.h" char *cartJsonOptionalParam(struct hash *paramHash, char *name) /* Convenience function for a CartJsonHandler function: Look up name in paramHash. * Return the string contained in its jsonElement value, or NULL if not found. */ { struct jsonElement *jel = hashFindVal(paramHash, name); if (jel) return jsonStringVal(jel, name); return NULL; } char *cartJsonRequiredParam(struct hash *paramHash, char *name, struct jsonWrite *jw, char *context) /* Convenience function for a CartJsonHandler function: Look up name in paramHash. * If found, return the string contained in its jsonElement value. * If not, write an error message (using context) and return NULL. */ { char *param = cartJsonOptionalParam(paramHash, name); if (param == NULL) jsonWriteStringf(jw, "error", "%s: required param %s is missing", context, name); return param; } static void printClade(struct jsonWrite *jw, char *clade) /* Print out clade menu's option list and selected item */ { struct slPair *cladeOptions = hGetCladeOptions(); jsonWriteValueLabelList(jw, "cladeOptions", cladeOptions); jsonWriteString(jw, "clade", clade); } static void printOrg(struct jsonWrite *jw, char *clade, char *org) /* Print out org menu's option list and selected item */ { struct slPair *orgOptions = hGetGenomeOptionsForClade(clade); jsonWriteValueLabelList(jw, "orgOptions", orgOptions); jsonWriteString(jw, "org", org); } static void printDb(struct jsonWrite *jw, char *org, char *db) /* Print out db menu's option list and selected item */ { struct slPair *dbOptions = hGetDbOptionsForGenome(org); jsonWriteValueLabelList(jw, "dbOptions", dbOptions); jsonWriteString(jw, "db", db); } static void printPosition(struct jsonWrite *jw, char *position) /* Print the position as given (it's up to caller to ensure this is consistent w/cart). */ { jsonWriteString(jw, "position", position); } static char *stripAnchor(char *textIn) /* If textIn contains an HTML anchor tag, strip it out (and its end tag). */ { regmatch_t matches[3]; if (regexMatchSubstrNoCase(textIn, "]+>", matches, ArraySize(matches))) { char *textOut = cloneString(textIn); memmove(textOut+matches[0].rm_so, textOut+matches[0].rm_eo, strlen(textOut) + 1 - matches[0].rm_eo); if (regexMatchSubstrNoCase(textOut, "", matches, ArraySize(matches))) memmove(textOut+matches[0].rm_so, textOut+matches[0].rm_eo, strlen(textOut) + 1 - matches[0].rm_eo); return textOut; } return textIn; } static void hgPositionsJson(struct jsonWrite *jw, char *db, struct hgPositions *hgp, struct cart *cart) /* Write out JSON description of multiple position matches. */ { struct hgPosTable *table; jsonWriteListStart(jw, "positionMatches"); struct trackDb *tdbList = NULL; for (table = hgp->tableList; table != NULL; table = table->next) { if (table->posList != NULL) { char *tableName = table->name; // clear the tdb cache if this track is a hub track if (isHubTrack(tableName)) tdbList = NULL; struct trackDb *tdb = tdbForTrack(db, tableName, &tdbList); if (!tdb && startsWith("all_", tableName)) tdb = tdbForTrack(db, tableName+strlen("all_"), &tdbList); if (!tdb) errAbort("no track for table \"%s\" found via a findSpec", tableName); char *trackName = tdb->track; jsonWriteObjectStart(jw, NULL); jsonWriteString(jw, "name", table->name); jsonWriteString(jw, "trackName", trackName); jsonWriteString(jw, "description", table->description); jsonWriteString(jw, "vis", hCarefulTrackOpenVis(db, trackName)); jsonWriteListStart(jw, "matches"); struct hgPos *pos; for (pos = table->posList; pos != NULL; pos = pos->next) { char *encMatches = cgiEncode(pos->browserName); jsonWriteObjectStart(jw, NULL); // begin one match if (pos->chrom != NULL) jsonWriteStringf(jw, "position", "%s:%d-%d", pos->chrom, pos->chromStart+1, pos->chromEnd); else // GenBank results set position to GB accession instead of chr:s-e position. jsonWriteString(jw, "position", pos->name); // this is magic to tell the browser to make the // composite and this subTrack visible if (tdb->parent) { if (tdbIsSuperTrackChild(tdb)) jsonWriteStringf(jw, "extraSel", "%s=show&", tdb->parent->track); else { // tdb is a subtrack of a composite or a view jsonWriteStringf(jw, "extraSel", "%s_sel=1&%s_sel=1&", trackName, tdb->parent->track); } } jsonWriteString(jw, "hgFindMatches", encMatches); jsonWriteString(jw, "posName", htmlEncodeText(pos->name, FALSE)); if (pos->description) { stripString(pos->description, "\n"); jsonWriteString(jw, "description", stripAnchor(pos->description)); } jsonWriteObjectEnd(jw); // end one match } jsonWriteListEnd(jw); // end matches jsonWriteObjectEnd(jw); // end one table } } jsonWriteListEnd(jw); // end positionMatches } static struct hgPositions *genomePosCJ(struct jsonWrite *jw, char *db, char *spec, char **retChromName, int *retWinStart, int *retWinEnd, struct cart *cart) /* Search for positions in genome that match user query. * Return an hgp unless there is a problem. hgp->singlePos will be set if a single * position matched. * Otherwise display list of positions, put # of positions in retWinStart, * and return NULL. */ { char *hgAppName = "cartJson"; struct hgPositions *hgp = NULL; char *chrom = NULL; int start = BIGNUM; int end = 0; char *terms[16]; int termCount = chopByChar(cloneString(spec), ';', terms, ArraySize(terms)); boolean multiTerm = (termCount > 1); int i = 0; for (i = 0; i < termCount; i++) { trimSpaces(terms[i]); if (isEmpty(terms[i])) continue; hgp = hgPositionsFind(db, terms[i], "", hgAppName, cart, multiTerm); if (hgp == NULL || hgp->posCount == 0) { jsonWriteStringf(jw, "error", "Sorry, couldn't locate %s in genome database", htmlEncode(terms[i])); if (multiTerm) jsonWriteStringf(jw, "error", "%s not uniquely determined -- can't do multi-position search.", terms[i]); *retWinStart = 0; return NULL; } if (hgp->singlePos != NULL) { if (chrom != NULL && !sameString(chrom, hgp->singlePos->chrom)) { jsonWriteStringf(jw, "error", "Sites occur on different chromosomes: %s, %s.", chrom, hgp->singlePos->chrom); return NULL; } chrom = hgp->singlePos->chrom; if (hgp->singlePos->chromStart < start) start = hgp->singlePos->chromStart; if (hgp->singlePos->chromEnd > end) end = hgp->singlePos->chromEnd; } else { hgPositionsJson(jw, db, hgp, cart); if (multiTerm && hgp->posCount != 1) { jsonWriteStringf(jw, "error", "%s not uniquely determined (%d locations) -- " "can't do multi-position search.", terms[i], hgp->posCount); return NULL; } break; } } if (hgp != NULL) { *retChromName = chrom; *retWinStart = start; *retWinEnd = end; } return hgp; } static void changePosition(struct cartJson *cj, char *newPosition) /* Update position in cart, after performing lookup if necessary. * Usually we don't report what we just changed, but since we might modify it, * print out the final value. */ { char *db = cartString(cj->cart, "db"); char *chrom = NULL; int start=0, end=0; struct hgPositions *hgp = genomePosCJ(cj->jw, db, newPosition, &chrom, &start, &end, cj->cart); // If it resolved to a single position, update the cart; otherwise the app can // present the error (or list of matches) to the user. if (hgp && hgp->singlePos) { char newPosBuf[128]; safef(newPosBuf, sizeof(newPosBuf), "%s:%d-%d", chrom, start+1, end); cartSetString(cj->cart, "position", newPosBuf); printPosition(cj->jw, newPosBuf); } else // Search failed; restore position from cart jsonWriteString(cj->jw, "position", cartUsualString(cj->cart, "position", hDefaultPos(db))); } static void changePositionHandler(struct cartJson *cj, struct hash *paramHash) /* Update position in cart, after performing lookup if necessary. * Usually we don't report what we just changed, but since we might modify it, * print out the final value. */ { char *newPosition = cartJsonRequiredParam(paramHash, "newValue", cj->jw, "changePosition"); if (newPosition) changePosition(cj, newPosition); } static void printGeneSuggestTrack(struct cartJson *cj, char *db) /* Get the gene track used by hgSuggest for db (defaulting to cart db), or null if * there is none for this assembly. */ { if (isEmpty(db)) db = cartString(cj->cart, "db"); char *track = assemblyGeneSuggestTrack(db); jsonWriteString(cj->jw, "geneSuggestTrack", track); } static void changeDb(struct cartJson *cj, char *newDb) /* Change db to new value, update cart and print JSON of new position & gene suggest track. */ { cartSetString(cj->cart, "db", newDb); jsonWriteString(cj->jw, "db", newDb); char *defaultPosition = hDefaultPos(newDb); changePosition(cj, defaultPosition); printGeneSuggestTrack(cj, newDb); } void cartJsonChangeDb(struct cartJson *cj, struct hash *paramHash) /* Change db to new value, update cart and print JSON of new position & gene suggest track. */ { char *newDb = cartJsonRequiredParam(paramHash, "newValue", cj->jw, "changeDb"); if (newDb) changeDb(cj, newDb); } static void changeOrg(struct cartJson *cj, char *newOrg) /* Change org to new value, update cart variables and print JSON of new db menu etc. */ { char *newDb = hDefaultDbForGenome(newOrg); if (isEmpty(newDb)) { jsonWriteStringf(cj->jw, "error", "changeOrg: can't find default db for '%s'", newOrg); return; } cartSetString(cj->cart, "org", newOrg); jsonWriteString(cj->jw, "org", newOrg); printDb(cj->jw, newOrg, newDb); changeDb(cj, newDb); } void cartJsonChangeOrg(struct cartJson *cj, struct hash *paramHash) /* Change org to new value, update cart and print JSON of new db menu, new position etc. */ { char *newOrg = cartJsonRequiredParam(paramHash, "newValue", cj->jw, "changeOrg"); if (newOrg) changeOrg(cj, newOrg); } void cartJsonChangeClade(struct cartJson *cj, struct hash *paramHash) /* Change clade to new value, update cart, and print JSON of new org & db menus, new position etc */ { char *newClade = cartJsonRequiredParam(paramHash, "newValue", cj->jw, "changeClade"); if (! newClade) return; char *newOrg = hDefaultGenomeForClade(newClade); if (isEmpty(newOrg)) { jsonWriteStringf(cj->jw, "error", "changeClade: can't find default genome for '%s'", newClade); return; } cartSetString(cj->cart, "clade", newClade); jsonWriteString(cj->jw, "clade", newClade); printOrg(cj->jw, newClade, newOrg); changeOrg(cj, newOrg); } static void getVar(struct cartJson *cj, struct hash *paramHash) /* Print out the requested cart var(s). varString may be a comma-separated list. * If a var is a list variable, prints out a list of values for that var. */ { char *varString = cartJsonRequiredParam(paramHash, "var", cj->jw, "get"); if (! varString) return; struct slName *varList = slNameListFromComma(varString), *var; for (var = varList; var != NULL; var = var->next) { if (cartListVarExists(cj->cart, var->name)) { // Use cartOptionalSlNameList and return a list: struct slName *valList = cartOptionalSlNameList(cj->cart, var->name); jsonWriteSlNameList(cj->jw, var->name, valList); slFreeList(&valList); } else { // Regular single-value variable (or not in the cart): char *val = cartOptionalString(cj->cart, var->name); //#*** TODO: move jsonStringEscape inside jsonWriteString char *encoded = jsonStringEscape(val); jsonWriteString(cj->jw, var->name, encoded); freeMem(encoded); } } slFreeList(&varList); } static struct slPair *trackLabelPairsFromTdbRefs(struct slRef *tdbRefList) /* For each tdb in tdbRefList, make a pair of {track, shortLabel}. */ { struct slPair *pairList = NULL; struct slRef *slr; for (slr = tdbRefList; slr != NULL; slr = slr->next) { struct trackDb *tdb = slr->val; slAddHead(&pairList, slPairNew(tdb->track, tdb->shortLabel)); } slReverse(&pairList); return pairList; } static int trackDbRefCmpShortLabel(const void *va, const void *vb) /* Case-insensitive comparison of max 32 chars of tdb->shortLabel in slRefs. */ { const struct slRef *aRef = *((struct slRef **)va); const struct slRef *bRef = *((struct slRef **)vb); const struct trackDb *tdbA = aRef->val, *tdbB = bRef->val; char shortLabelA[33], shortLabelB[33]; strncpy(shortLabelA, tdbA->shortLabel, 32); shortLabelA[32] = '\0'; strncpy(shortLabelB, tdbB->shortLabel, 32); shortLabelB[32] = '\0'; tolowers(shortLabelA); tolowers(shortLabelB); return strcmp(shortLabelA, shortLabelB); } static void printAllTracks(struct cartJson *cj, struct trackDb *trackList) /* Print a mapping of "allTracks" to {track, shortLabel} for all tracks, * alphabetized by shortLabel. */ { struct slRef *refList = refListFromSlList(trackList); slSort(&refList, trackDbRefCmpShortLabel); struct slPair *trackLabelPairs = trackLabelPairsFromTdbRefs(refList); jsonWriteValueLabelList(cj->jw, "allTracks", trackLabelPairs); } static void printOneGroupTracks(struct cartJson *cj, char *groupName, struct slRef *tdbRefList) /* Given a group and value is an slRef list of trackDbs, sort the list by trackDb priority * and print out JSON mapping the group name to list of trackDb {value, label} objects. */ { slSort(&tdbRefList, trackDbRefCmp); struct slPair *trackLabelPairs = trackLabelPairsFromTdbRefs(tdbRefList); jsonWriteValueLabelList(cj->jw, groupName, trackLabelPairs); } static void printGroupTracks(struct cartJson *cj, struct trackDb *trackList) /* Hash group names to lists of tracks in those groups, and print out the structure as JSON. */ { struct hash *hash = hashNew(0); struct trackDb *tdb; for (tdb = trackList; tdb != NULL; tdb = tdb->next) { struct hashEl *hel = hashLookup(hash, tdb->grp); struct slRef *slr = slRefNew(tdb); if (hel) slAddHead(&(hel->val), slr); else hashAdd(hash, tdb->grp, slr); } jsonWriteObjectStart(cj->jw, "groupTracks"); printAllTracks(cj, trackList); struct hashCookie cookie = hashFirst(hash); struct hashEl *hel; while ((hel = hashNext(&cookie)) != NULL) { char *groupName = hel->name; struct slRef *tdbRefList = hel->val; printOneGroupTracks(cj, groupName, tdbRefList); } jsonWriteObjectEnd(cj->jw); hashFree(&hash); } static void printTrackTables(struct cartJson *cj, struct trackDb *trackList) /* Hash track names to lists of tables in those tracks, and print out the structure as JSON. */ { jsonWriteObjectStart(cj->jw, "trackTables"); char *db = cartString(cj->cart, "db"); struct trackDb *tdb; for (tdb = trackList; tdb != NULL; tdb = tdb->next) { struct slName *tableList = cartTrackDbTablesForTrack(db, tdb, FALSE); // no useJoiner for now jsonWriteSlNameList(cj->jw, tdb->track, tableList); slFreeList(&tableList); } jsonWriteObjectEnd(cj->jw); } void cartJsonGetGroupsTracksTables(struct cartJson *cj, struct hash *paramHash) /* Print info necessary for group/track/table menus. */ { struct trackDb *fullTrackList = NULL; struct grp *fullGroupList = NULL; cartTrackDbInit(cj->cart, &fullTrackList, &fullGroupList, /* useAccessControl=*/TRUE); -jsonWriteObjectStart(cj->jw, "trackDbInfo"); // Print out options for the track group menu: +// Remove All Tracks & All Tables from the end of the list for now. +struct grp *grp, *nextGrp = NULL; +for (grp = fullGroupList; grp != NULL; grp = nextGrp) + { + nextGrp = grp->next; + if (nextGrp && (sameString(nextGrp->name, "allTracks") || + sameString(nextGrp->name, "allTables"))) + { + grp->next = nextGrp->next; + nextGrp = grp; + } + } +jsonWriteObjectStart(cj->jw, "trackDbInfo"); jsonWriteValueLabelList(cj->jw, "groupOptions", (struct slPair *)fullGroupList); // Print out an object that maps group names to their tracks: printGroupTracks(cj, fullTrackList); // Print out an object that maps track names to tables: printTrackTables(cj, fullTrackList); jsonWriteObjectEnd(cj->jw); } static char *hAssemblyDescription(char *db) /* Return a string containing db's description.html, or NULL if not found. */ //#*** LIBIFY: Code lifted from hgFind.c's hgPositionsHelpHtml. { char *htmlPath = hHtmlPath(db); char *htmlString = NULL; if (htmlPath != NULL) { if (fileExists(htmlPath)) readInGulp(htmlPath, &htmlString, NULL); else if (startsWith("http://" , htmlPath) || startsWith("https://", htmlPath) || startsWith("ftp://" , htmlPath)) { struct lineFile *lf = udcWrapShortLineFile(htmlPath, NULL, 256*1024); htmlString = lineFileReadAll(lf); lineFileClose(&lf); } } return htmlString; } static void getAssemblyInfo(struct cartJson *cj, struct hash *paramHash) /* Return useful things from dbDb (or track hub) and assembly description html (possibly NULL). * If db param is NULL, use db from cart. */ { char *db = cartJsonOptionalParam(paramHash, "db"); if (db == NULL) db = cartString(cj->cart, "db"); jsonWriteString(cj->jw, "db", db); jsonWriteString(cj->jw, "commonName", hGenome(db)); jsonWriteString(cj->jw, "scientificName", hScientificName(db)); jsonWriteString(cj->jw, "dbLabel", hFreezeDate(db)); //#*** TODO: move jsonStringEscape inside jsonWriteString jsonWriteString(cj->jw, "assemblyDescription", jsonStringEscape(hAssemblyDescription(db))); } static void getHasCustomTracks(struct cartJson *cj, struct hash *paramHash) /* Tell whether cart has custom tracks for db. If db param is NULL, use db from cart. */ { char *db = cartJsonOptionalParam(paramHash, "db"); if (db == NULL) db = cartString(cj->cart, "db"); jsonWriteBoolean(cj->jw, "hasCustomTracks", customTracksExistDb(cj->cart, db, NULL)); } static void getIsSpecialHost(struct cartJson *cj, struct hash *paramHash) /* Tell whether we're on a development host, preview, gsid etc. */ { jsonWriteBoolean(cj->jw, "isPrivateHost", hIsPrivateHost()); jsonWriteBoolean(cj->jw, "isBetaHost", hIsBetaHost()); jsonWriteBoolean(cj->jw, "isBrowserbox", hIsBrowserbox()); jsonWriteBoolean(cj->jw, "isPreviewHost", hIsPreviewHost()); } static void getHasHubTable(struct cartJson *cj, struct hash *paramHash) /* Tell whether central database has a hub table (i.e. server can do hubs). */ { jsonWriteBoolean(cj->jw, "hasHubTable", hubConnectTableExists()); } static void setIfUnset(struct cartJson *cj, struct hash *paramHash) /* For each name in paramHash, if that cart variable doesn't already have a non-empty * value, set it to the value. */ { struct hashCookie cookie = hashFirst(paramHash); struct hashEl *hel; while ((hel = hashNext(&cookie)) != NULL) { if (isEmpty(cartOptionalString(cj->cart, hel->name))) { char *val = jsonStringVal((struct jsonElement *)(hel->val), hel->name); if (val) cartSetString(cj->cart, hel->name, val); } } } static void getCladeOrgDbPos(struct cartJson *cj, struct hash *paramHash) /* Get cart's current clade, org, db, position and geneSuggest track. */ { char *db = cartString(cj->cart, "db"); char *genome = hGenome(db); char *clade = hClade(genome); printClade(cj->jw, clade); printOrg(cj->jw, clade, genome); printDb(cj->jw, genome, db); char *position = cartOptionalString(cj->cart, "position"); if (isEmpty(position)) position = hDefaultPos(db); printPosition(cj->jw, position); printGeneSuggestTrack(cj, cartString(cj->cart, "db")); } static void getStaticHtml(struct cartJson *cj, struct hash *paramHash) /* Read HTML text from a relative path under browser.documentRoot and * write it as an encoded JSON string */ { char *tag = cartJsonOptionalParam(paramHash, "tag"); if (isEmpty(tag)) tag = "html"; char *file = cartJsonRequiredParam(paramHash, "file", cj->jw, "getStaticHtml"); char *html = hFileContentsOrWarning(file); //#*** TODO: move jsonStringEscape inside jsonWriteString char *encoded = jsonStringEscape(html); jsonWriteString(cj->jw, tag, encoded); } void cartJsonRegisterHandler(struct cartJson *cj, char *command, CartJsonHandler *handler) /* Associate handler with command; when javascript sends command, handler will be executed. */ { hashAdd(cj->handlerHash, command, handler); } struct cartJson *cartJsonNew(struct cart *cart) /* Allocate and return a cartJson object with default handlers. */ { struct cartJson *cj; AllocVar(cj); cj->handlerHash = hashNew(0); cj->jw = jsonWriteNew(); cj->cart = cart; cartJsonRegisterHandler(cj, "getCladeOrgDbPos", getCladeOrgDbPos); cartJsonRegisterHandler(cj, "changeClade", cartJsonChangeClade); cartJsonRegisterHandler(cj, "changeOrg", cartJsonChangeOrg); cartJsonRegisterHandler(cj, "changeDb", cartJsonChangeDb); cartJsonRegisterHandler(cj, "changePosition", changePositionHandler); cartJsonRegisterHandler(cj, "get", getVar); cartJsonRegisterHandler(cj, "getGroupsTracksTables", cartJsonGetGroupsTracksTables); cartJsonRegisterHandler(cj, "getAssemblyInfo", getAssemblyInfo); cartJsonRegisterHandler(cj, "getHasCustomTracks", getHasCustomTracks); cartJsonRegisterHandler(cj, "getIsSpecialHost", getIsSpecialHost); cartJsonRegisterHandler(cj, "getHasHubTable", getHasHubTable); cartJsonRegisterHandler(cj, "setIfUnset", setIfUnset); cartJsonRegisterHandler(cj, "getStaticHtml", getStaticHtml); return cj; } void cartJsonFree(struct cartJson **pCj) /* Close **pCj's contents and nullify *pCj. */ { if (*pCj == NULL) return; struct cartJson *cj = *pCj; jsonWriteFree(&cj->jw); hashFree(&cj->handlerHash); freez(pCj); } static void doOneCommand(struct cartJson *cj, char *command, struct jsonElement *paramObject) /* Dispatch command by name, checking for required parameters. */ { CartJsonHandler *handler = hashFindVal(cj->handlerHash, command); if (handler) { struct hash *paramHash = jsonObjectVal(paramObject, command); handler(cj, paramHash); } else { jsonWriteStringf(cj->jw, "error", "cartJson: unrecognized command '%s'\"", command); return; } } static int commandCmp(const void *pa, const void *pb) /* Comparison function to put "change" commands ahead of other commands. */ { struct slPair *cmdA = *((struct slPair **)pa); struct slPair *cmdB = *((struct slPair **)pb); boolean aIsChange = startsWith("change", cmdA->name); boolean bIsChange = startsWith("change", cmdB->name); if (aIsChange && !bIsChange) return -1; if (!aIsChange && bIsChange) return 1; return strcmp(cmdA->name, cmdB->name); } // Accumulate warnings so they can be JSON-ified: static struct dyString *dyWarn = NULL; static void cartJsonVaWarn(char *format, va_list args) /* Save warnings for later. */ { dyStringVaPrintf(dyWarn, format, args); } static void cartJsonPrintWarnings(struct jsonWrite *jw) /* If there are warnings, write them out as JSON: */ { if (dyWarn && dyStringLen(dyWarn) > 0) { //#*** TODO: move jsonStringEscape inside jsonWriteString char *encoded = jsonStringEscape(dyWarn->string); jsonWriteString(jw, "warning", encoded); freeMem(encoded); } } static void cartJsonAbort() /* Print whatever warnings we have accumulated and exit. */ { if (dyWarn) puts(dyWarn->string); exit(0); } static void cartJsonPushErrHandlers() /* Push warn and abort handlers for errAbort. */ { if (dyWarn == NULL) dyWarn = dyStringNew(0); else dyStringClear(dyWarn); pushWarnHandler(cartJsonVaWarn); pushAbortHandler(cartJsonAbort); } static void cartJsonPopErrHandlers() /* Pop warn and abort handlers for errAbort. */ { popWarnHandler(); popAbortHandler(); } void cartJsonExecute(struct cartJson *cj) /* Get commands from cgi, print Content-type, execute commands, print results as JSON. */ { cartJsonPushErrHandlers(); puts("Content-Type:text/javascript\n"); // Initialize response JSON object: jsonWriteObjectStart(cj->jw, NULL); // Always send back hgsid: jsonWriteString(cj->jw, cartSessionVarName(), cartSessionId(cj->cart)); char *commandJson = cgiOptionalString(CARTJSON_COMMAND); if (commandJson) { struct jsonElement *commandObj = jsonParse(commandJson); struct hash *commandHash = jsonObjectVal(commandObj, "commandObj"); // change* commands need to go first! Really we need an ordered map type here... // for now, just make a list and sort to put change commands at the front. struct slPair *commandList = NULL, *cmd; struct hashCookie cookie = hashFirst(commandHash); struct hashEl *hel; while ((hel = hashNext(&cookie)) != NULL) slAddHead(&commandList, slPairNew(hel->name, hel->val)); slSort(&commandList, commandCmp); for (cmd = commandList; cmd != NULL; cmd = cmd->next) doOneCommand(cj, cmd->name, (struct jsonElement *)cmd->val); } cartJsonPrintWarnings(cj->jw); jsonWriteObjectEnd(cj->jw); puts(cj->jw->dy->string); cartJsonPopErrHandlers(); }