80c13ab201d59baa021d5c1a2222872ecbdf196f
hiram
  Fri Feb 8 16:17:16 2019 -0800
now using functions in src/lib/jsonWrite.c refs #18869 #22859

diff --git src/hg/hubApi/hubApi.c src/hg/hubApi/hubApi.c
index 8b6078c..1639d99 100644
--- src/hg/hubApi/hubApi.c
+++ src/hg/hubApi/hubApi.c
@@ -12,30 +12,31 @@
 #include "udc.h"
 #include "knetUdc.h"
 #include "genbank.h"
 #include "trackHub.h"
 #include "hgConfig.h"
 #include "hCommon.h"
 #include "hPrint.h"
 #include "bigWig.h"
 #include "hubConnect.h"
 #include "obscure.h"
 #include "errCatch.h"
 #include "vcf.h"
 #include "bedTabix.h"
 #include "bamFile.h"
 #include "jsonParse.h"
+#include "jsonWrite.h"
 #include "chromInfo.h"
 
 #ifdef USE_HAL
 #include "halBlockViz.h"
 #endif
 
 /*
 +------------------+------------------+------+-----+---------+-------+
 | Field            | Type             | Null | Key | Default | Extra |
 +------------------+------------------+------+-----+---------+-------+
 | hubUrl           | longblob         | NO   | PRI | NULL    |       |
 | shortLabel       | varchar(255)     | NO   |     | NULL    |       |
 | longLabel        | varchar(255)     | NO   |     | NULL    |       |
 | registrationTime | varchar(255)     | NO   |     | NULL    |       |
 | dbCount          | int(10) unsigned | NO   |     | NULL    |       |
@@ -64,96 +65,93 @@
 static long totalTracks = 0;
 static boolean measureTiming = FALSE;	/* set by CGI parameters */
 static boolean allTrackSettings = FALSE;	/* checkbox setting */
 static char **shortLabels = NULL;	/* public hub short labels in array */
 struct hubPublic *publicHubList = NULL;
 static int publicHubCount = 0;
 static char *defaultHub = "Plants";
 static char *defaultDb = "ce11";
 static long enteredMainTime = 0;	/* will become = clock1000() on entry */
 		/* to allow calculation of when to bail out, taking too long */
 static long timeOutSeconds = 100;
 static boolean timedOut = FALSE;
 
 /* ######################################################################### */
 
+#ifdef NOT
 static void jsonInteger(FILE *f, char *tag, long long value)
 /* output one json interger: "tag":value appropriately quoted and encoded */
 {
 fprintf(f,"\"%s\":%lld",tag, value);
 }
+#endif
 
 static void jsonStringPrint(FILE *f, char *value)
 /* escape string for output */
 {
 char *a = jsonStringEscape(value);
 if (isEmpty(a))
     fprintf(f, "%s", "null");
 else
     fprintf(f, "\"%s\"", a);
 freeMem(a);
 }
 
 static void jsonTagValue(FILE *f, char *tag, char *value)
 /* output one json string: "tag":"value" appropriately quoted and encoded */
 {
 fprintf(f,"\"%s\":",tag);
 jsonStringPrint(f, value);
 }
 
-static void jsonStartOutput(FILE *f)
+static struct jsonWrite *jsonStartOutput()
 /* begin json output */
 {
-fputc('{',f);
-jsonTagValue(f, "source", "UCSantaCruz");
-fputc(',',f);
+struct jsonWrite *jw = jsonWriteNew();
+jsonWriteObjectStart(jw, NULL);
+jsonWriteString(jw, "source", "UCSantaCurz");
+return jw;
 }
 
 static void jsonErrAbort(char *format, ...)
 /* Issue an error message in json format. */
 {
 char errMsg[2048];
 va_list args;
 va_start(args, format);
 vsnprintf(errMsg, sizeof(errMsg), format, args);
 fputc('{',stdout);
 jsonTagValue(stdout, "error", errMsg);
 fputc('}',stdout);
 }
 
-static void hubPublicJsonData(FILE *f, struct hubPublic *el)
+static void hubPublicJsonData(struct jsonWrite *jw, struct hubPublic *el)
 /* Print array data for one row from hubPublic table, order here
  * must be same as was stated in the columnName header element
  *  TODO: need to figure out how to use the order of the columns as
  *        they are in the 'desc' request
  */
 {
-fputc('[',f);
-jsonStringPrint(f, el->hubUrl);
-fputc(',',f);
-jsonStringPrint(f, el->shortLabel);
-fputc(',',f);
-jsonStringPrint(f, el->longLabel);
-fputc(',',f);
-jsonStringPrint(f, el->registrationTime);
-fputc(',',f);
-fprintf(f, "%lld", (long long)el->dbCount);
-fputc(',',f);
-jsonStringPrint(f, el->dbList);
-fputc(',',f);
-jsonStringPrint(f, el->descriptionUrl);
-fputc(']',f);
+jsonWriteListStart(jw, NULL);
+jsonWriteString(jw, NULL, el->hubUrl);
+jsonWriteString(jw, NULL, el->shortLabel);
+jsonWriteString(jw, NULL, el->longLabel);
+jsonWriteString(jw, NULL, el->registrationTime);
+jsonWriteNumber(jw, NULL, (long long)el->dbCount);
+jsonWriteString(jw, NULL, el->dbList);
+jsonWriteString(jw, NULL, el->descriptionUrl);
+jsonWriteListEnd(jw);
 }
 
 #ifdef NOT
 /* This function should be in hg/lib/hubPublic.c */
 static void hubPublicJsonOutput(FILE *f, struct hubPublic *el)
 /* Print out hubPublic element in JSON format. */
 {
 fputc('{',f);
 jsonTagValue(f, "hubUrl", el->hubUrl);
 fputc(',',f);
 jsonTagValue(f, "shortLabel", el->shortLabel);
 fputc(',',f);
 jsonTagValue(f, "longLabel", el->longLabel);
 fputc(',',f);
 jsonTagValue(f, "registrationTime", el->registrationTime);
@@ -535,66 +533,53 @@
 
 static char *urlFromShortLabel(char *shortLabel)
 {
 char hubUrl[1024];
 char query[1024];
 struct sqlConnection *conn = hConnectCentral();
 // Build a query to select the hubUrl for the given shortLabel
 sqlSafef(query, sizeof(query), "select hubUrl from %s where shortLabel='%s'",
       hubPublicTableName(), shortLabel);
 if (! sqlQuickQuery(conn, query, hubUrl, sizeof(hubUrl)))
     hubUrl[0] = 0;
 hDisconnectCentral(&conn);
 return cloneString(hubUrl);
 }
 
-static void dbDbJsonData(FILE *f, struct dbDb *el)
+static void dbDbJsonData(struct jsonWrite *jw, struct dbDb *el)
 /* Print out dbDb table element in JSON format.
  * must be same as was stated in the columnName header element
  *  TODO: need to figure out how to use the order of the columns as
  *        they are in the 'desc' request
  */
 {
-fputc('[',f);
-jsonStringPrint(f, el->name);
-fputc(',',f);
-jsonStringPrint(f, el->description);
-fputc(',',f);
-jsonStringPrint(f, el->nibPath);
-fputc(',',f);
-jsonStringPrint(f, el->organism);
-fputc(',',f);
-jsonStringPrint(f, el->defaultPos);
-fputc(',',f);
-fprintf(f, "%lld", (long long)el->active);
-fputc(',',f);
-fprintf(f, "%lld", (long long)el->orderKey);
-fputc(',',f);
-jsonStringPrint(f, el->genome);
-fputc(',',f);
-jsonStringPrint(f, el->scientificName);
-fputc(',',f);
-jsonStringPrint(f, el->htmlPath);
-fputc(',',f);
-fprintf(f, "%lld", (long long)el->hgNearOk);
-fputc(',',f);
-fprintf(f, "%lld", (long long)el->hgPbOk);
-fputc(',',f);
-jsonStringPrint(f, el->sourceName);
-fputc(',',f);
-fprintf(f, "%lld", (long long)el->taxId);
-fputc(']',f);
+jsonWriteListStart(jw, NULL);
+jsonWriteString(jw, NULL, el->name);
+jsonWriteString(jw, NULL, el->description);
+jsonWriteString(jw, NULL, el->nibPath);
+jsonWriteString(jw, NULL, el->organism);
+jsonWriteString(jw, NULL, el->defaultPos);
+jsonWriteNumber(jw, NULL, (long long)el->active);
+jsonWriteNumber(jw, NULL, (long long)el->orderKey);
+jsonWriteString(jw, NULL, el->genome);
+jsonWriteString(jw, NULL, el->scientificName);
+jsonWriteString(jw, NULL, el->htmlPath);
+jsonWriteNumber(jw, NULL, (long long)el->hgNearOk);
+jsonWriteNumber(jw, NULL, (long long)el->hgPbOk);
+jsonWriteString(jw, NULL, el->sourceName);
+jsonWriteNumber(jw, NULL, (long long)el->taxId);
+jsonWriteListEnd(jw);
 }
 
 #ifdef NOT
 /* this code should be in hg/lib/dbDb.c */
 static void dbDbJsonOutput(FILE *f, struct dbDb *el)
 /* Print out hubPublic element in JSON format. */
 {
 fputc('{',f);
 jsonTagValue(f, "name", el->name);
 fputc(',',f);
 jsonTagValue(f, "description", el->description);
 fputc(',',f);
 jsonTagValue(f, "nibPath", el->nibPath);
 fputc(',',f);
 jsonTagValue(f, "organism", el->organism);
@@ -610,74 +595,69 @@
 jsonTagValue(f, "scientificName", el->scientificName);
 fputc(',',f);
 jsonTagValue(f, "htmlPath", el->htmlPath);
 fputc(',',f);
 jsonInteger(f, "hgNearOk", el->hgNearOk);
 fputc(',',f);
 jsonInteger(f, "hgPbOk", el->hgPbOk);
 fputc(',',f);
 jsonTagValue(f, "sourceName", el->sourceName);
 fputc(',',f);
 jsonInteger(f, "taxId", el->taxId);
 fputc('}',f);
 }
 #endif
 
-static boolean tableColumns(FILE *f, char *table)
+static boolean tableColumns(struct jsonWrite *jw, char *table)
 /* output the column names for the given table
  * return: TRUE on error, FALSE on success
  */
 {
-fprintf(f, "\"columnNames\":[");
+jsonWriteListStart(jw, "columnNames");
 char query[1024];
 struct sqlConnection *conn = hConnectCentral();
 sqlSafef(query, sizeof(query), "desc %s", table);
 struct sqlResult *sr = sqlGetResult(conn, query);
 char **row;
 row = sqlNextRow(sr);
 if (NULL == row)
     {
     jsonErrAbort("ERROR: can not 'desc' table '%s'\n", table);
     return TRUE;
     }
-fprintf(f, "\"%s\"",row[0]);
-while (row != NULL)
-    {
-    row = sqlNextRow(sr);
-    if (row)
-       fprintf(f, ",\"%s\"",row[0]);
-    }
+while ((row = sqlNextRow(sr)) != NULL)
+    jsonWriteString(jw, NULL, row[0]);
 sqlFreeResult(&sr);
 hDisconnectCentral(&conn);
-fprintf(f, "],");
+jsonWriteListEnd(jw);
 return FALSE;
 }
 
 static void jsonPublicHubs()
 /* output the hubPublic SQL table */
 {
 struct hubPublic *el = publicHubList;
-jsonStartOutput(stdout);
-tableColumns(stdout, hubPublicTableName());
-printf("\"publicHubData\":[");
+struct jsonWrite *jw = jsonStartOutput();
+tableColumns(jw, hubPublicTableName());
+jsonWriteListStart(jw, "publicHubData");
 for ( ; el != NULL; el = el->next )
     {
-    hubPublicJsonData(stdout, el);
-    if (el->next)
-       printf(",");
+    hubPublicJsonData(jw, el);
     }
-printf("]}\n");
+jsonWriteListEnd(jw);
+jsonWriteObjectEnd(jw);
+fputs(jw->dy->string,stdout);
 }
 
 static int dbDbCmpName(const void *va, const void *vb)
 /* Compare two dbDb elements: name, ignore case. */
 {
 const struct dbDb *a = *((struct dbDb **)va);
 const struct dbDb *b = *((struct dbDb **)vb);
 return strcasecmp(a->name, b->name);
 }
 
 static struct dbDb *ucscDbDb()
 /* return the dbDb table as an slList */
 {
 char query[1024];
 struct sqlConnection *conn = hConnectCentral();
@@ -689,198 +669,183 @@
     {
     el = dbDbLoad(row);
     slAddHead(&dbList, el);
     }
 sqlFreeResult(&sr);
 hDisconnectCentral(&conn);
 slSort(&dbList, dbDbCmpName);
 return dbList;
 }
 
 static void jsonDbDb()
 /* output the dbDb SQL table */
 {
 struct dbDb *dbList = ucscDbDb();
 struct dbDb *el;
-jsonStartOutput(stdout);
-tableColumns(stdout, "dbDb");
-printf("\"ucscGenomes\":[");
+struct jsonWrite *jw = jsonStartOutput();
+tableColumns(jw, "dbDb");
+jsonWriteListStart(jw, "ucscGenomes");
 for ( el=dbList; el != NULL; el = el->next )
     {
-    dbDbJsonData(stdout, el);
-    if (el->next)
-       printf(",");
+    dbDbJsonData(jw, el);
     }
-printf("]}\n");
+jsonWriteListEnd(jw);
+jsonWriteObjectEnd(jw);
+fputs(jw->dy->string,stdout);
 }
 
 static void chromInfoJsonOutput(FILE *f, char *db)
 /* for given db, if there is a track, list the chromosomes in that track,
  * for no track, simply list the chromosomes in the sequence
  */
 {
 char *track = cgiOptionalString("track");
 if (track)
     {
     struct sqlConnection *conn = hAllocConn(db);
     if (! sqlTableExists(conn, track))
 	jsonErrAbort("ERROR: endpoint: /list/chromosomes?db=%&table=%s ERROR table does not exist", db, track);
     if (sqlColumnExists(conn, track, "chrom"))
 	{
-	jsonStartOutput(f);
-	jsonTagValue(f, "genome", db);
-	fputc(',',f);
-	jsonTagValue(f, "track", track);
-	fputc(',',f);
+        struct jsonWrite *jw = jsonStartOutput();
+	jsonWriteString(jw, "genome", db);
+	jsonWriteString(jw, "track", track);
         struct slPair *list = NULL;
 	char query[2048];
         sqlSafef(query, sizeof(query), "select distinct chrom from %s", track);
 	struct sqlResult *sr = sqlGetResult(conn, query);
 	char **row;
 	while ((row = sqlNextRow(sr)) != NULL)
     	{
             int size = hChromSize(db, row[0]);
 	    slAddHead(&list, slPairNew(row[0], intToPt(size)));
     	}
 	sqlFreeResult(&sr);
         slPairIntSort(&list);
         slReverse(&list);
-        jsonInteger(f, "chromCount", slCount(list));
-        fputc(',',f);
+        jsonWriteNumber(jw, "chromCount", (long long)slCount(list));
         struct slPair *el = list;
         for ( ; el != NULL; el = el->next )
-	    {
-            jsonInteger(f, el->name, ptToInt(el->val));
-	    if (el->next)
-		fputc(',',f);
-	    }
-        fputc('}',f);
+            jsonWriteNumber(jw, el->name, (long long)ptToInt(el->val));
+        jsonWriteObjectEnd(jw);
+        fputs(jw->dy->string,stdout);
 	}
     else
 	{
 	jsonErrAbort("ERROR: table '%s' is not a position table, no chromosomes for genome: '%s'", track, db);
 	}
     hFreeConn(&conn);
     }
 else
     {
     struct chromInfo *ciList = createChromInfoList(NULL, db);
     struct chromInfo *el = ciList;
-    jsonStartOutput(f);
-    jsonTagValue(f, "genome", db);
-    fputc(',',f);
-    jsonInteger(f, "chromCount", slCount(ciList));
-    fputc(',',f);
+    struct jsonWrite *jw = jsonStartOutput();
+    jsonWriteString(jw, "genome", db);
+    jsonWriteNumber(jw, "chromCount", (long long)slCount(ciList));
     for ( ; el != NULL; el = el->next )
 	{
-        jsonInteger(f, el->chrom, el->size);
-	if (el->next)
-           fputc(',',f);
+        jsonWriteNumber(jw, el->chrom, (long long)el->size);
 	}
-    fputc('}',f);
+    jsonWriteObjectEnd(jw);
+    fputs(jw->dy->string,stdout);
     }
 }
 
 static void trackDbJsonOutput(char *db, FILE *f)
 /* return track list from specified UCSC database name */
 {
 struct trackDb *tdbList = hTrackDb(db);
 struct trackDb *el;
-jsonStartOutput(f);
-jsonTagValue(f, "db", db);
-fputc(',',f);
-fprintf(f, "\"tracks\":[");
+struct jsonWrite *jw = jsonStartOutput();
+jsonWriteString(jw, "db", db);
+jsonWriteListStart(jw, "tracks");
 for (el = tdbList; el != NULL; el = el->next )
-    {
-    jsonStringPrint(f, el->track);
-    if (el->next)
-	fputc(',',f);
-    }
-fprintf(f, "]}\n");
+    jsonWriteString(jw, NULL, el->track);
+jsonWriteListEnd(jw);
+jsonWriteObjectEnd(jw);
+fputs(jw->dy->string,stdout);
 }	/*	static void trackDbJsonOutput(char *db, FILE *f)	*/
 
 #define MAX_PATH_INFO 32
 static void apiList(char *words[MAX_PATH_INFO])
 /* 'list' function words[1] is the subCommand */
 {
 if (sameWord("publicHubs", words[1]))
     jsonPublicHubs();
 else if (sameWord("ucscGenomes", words[1]))
     jsonDbDb();
 else if (sameWord("hubGenomes", words[1]))
     {
     char *hubUrl = cgiOptionalString("hubUrl");
     if (isEmpty(hubUrl))
 	jsonErrAbort("ERROR: must supply hubUrl='http:...' some URL to a hub for /list/genomes\n");
 
     struct trackHub *hub = trackHubOpen(hubUrl, "");
     if (hub->genomeList)
 	{
-        jsonStartOutput(stdout);
-	jsonTagValue(stdout, "hubUrl", hubUrl);
-	fputc(',',stdout);
-	printf("\"genomes\":[");
+        struct jsonWrite *jw = jsonStartOutput();
+	jsonWriteString(jw, "hubUrl", hubUrl);
+        jsonWriteListStart(jw, "genomes");
 	struct slName *theList = genomeList(hub, NULL, NULL);
 	slNameSort(&theList);
 	struct slName *el = theList;
 	for ( ; el ; el = el->next )
 	    {
-	    jsonStringPrint(stdout, el->name);
-	    if (el->next)
-		fputc(',',stdout);
+	    jsonWriteString(jw, NULL, el->name);
 	    }
-	printf("]}\n");
+	jsonWriteListEnd(jw);
+	jsonWriteObjectEnd(jw);
+        fputs(jw->dy->string,stdout);
 	}
     }
 else if (sameWord("tracks", words[1]))
     {
     char *hubUrl = cgiOptionalString("hubUrl");
     char *genome = cgiOptionalString("genome");
     char *db = cgiOptionalString("db");
     if (isEmpty(hubUrl) && isEmpty(db))
       jsonErrAbort("ERROR: must supply hubUrl or db name to return track list");
     if (isEmpty(hubUrl))	// missing hubUrl implies UCSC database
 	{
         trackDbJsonOutput(db, stdout);	// only need db for this function
 	return;
 	}
     if (isEmpty(genome) || isEmpty(hubUrl))
 	{
         if (isEmpty(genome))
 	    warn("# must supply genome='someName' the name of a genome in a hub for /list/tracks\n");
 	if (isEmpty(hubUrl))
             jsonErrAbort("ERROR: must supply hubUrl='http:...' some URL to a hub for /list/genomes\n");
 	}
     struct trackHub *hub = trackHubOpen(hubUrl, "");
     if (hub->genomeList)
 	{
 	struct slName *dbTrackList = NULL;
 	(void) genomeList(hub, &dbTrackList, genome);
-        jsonStartOutput(stdout);
-	jsonTagValue(stdout, "hubUrl", hubUrl);
-	fputc(',',stdout);
-	jsonTagValue(stdout, "genome", genome);
-	fputc(',',stdout);
+        struct jsonWrite *jw = jsonStartOutput();
+	jsonWriteString(jw, "hubUrl", hubUrl);
+        jsonWriteListStart(jw, "genome");
 	slNameSort(&dbTrackList);
 	struct slName *el = dbTrackList;
 	for ( ; el != NULL; el = el->next )
 	    {
-            jsonStringPrint(stdout, el->name);
-	    if (el->next)
-		fputc(',',stdout);
+            jsonWriteString(jw, NULL, el->name);
 	    }
-	printf("]}\n");
+	jsonWriteListEnd(jw);
+	jsonWriteObjectEnd(jw);
+        fputs(jw->dy->string,stdout);
 	}
     }
 else if (sameWord("chromosomes", words[1]))
     {
     char *hubUrl = cgiOptionalString("hubUrl");
 //    char *genome = cgiOptionalString("genome");
     char *db = cgiOptionalString("db");
     if (isEmpty(hubUrl) && isEmpty(db))
         jsonErrAbort("ERROR: must supply hubUrl or db name to return chromosome list");
 
     if (isEmpty(hubUrl))	// missing hubUrl implies UCSC database
 	{
         chromInfoJsonOutput(stdout, db);
 	return;
 	}