b475ddc8795329be93a783760d7aed704131d479
max
  Wed Feb 17 01:48:32 2021 -0800
adding JSON output to hgBlat, refs #27010

diff --git src/lib/psl.c src/lib/psl.c
index f6b65c4..d438fda 100644
--- src/lib/psl.c
+++ src/lib/psl.c
@@ -160,30 +160,114 @@
 }
 
 void pslFreeList(struct psl **pList)
 /* Free a list of dynamically allocated psl's */
 {
 struct psl *el, *next;
 
 for (el = *pList; el != NULL; el = next)
     {
     next = el->next;
     pslFree(&el);
     }
 *pList = NULL;
 }
 
+void pslOutputJson(struct psl *el, FILE *f) 
+/* print out psl as a json array */
+{
+fputs("[", f);
+fprintf(f, "%u,", el->match);
+fprintf(f, "%u,", el->misMatch);
+fprintf(f, "%u,", el->repMatch);
+fprintf(f, "%u,", el->nCount);
+fprintf(f, "%u,", el->qNumInsert);
+fprintf(f, "%d,", el->qBaseInsert);
+fprintf(f, "%u,", el->tNumInsert);
+fprintf(f, "%d,", el->tBaseInsert);
+fprintf(f, "'%s',", el->strand);
+fprintf(f, "'%s',", el->qName);
+fprintf(f, "%u,", el->qSize);
+fprintf(f, "%u,", el->qStart);
+fprintf(f, "%u,", el->qEnd);
+fprintf(f, "'%s',", el->tName);
+fprintf(f, "%u,", el->tSize);
+fprintf(f, "%u,", el->tStart);
+fprintf(f, "%u,", el->tEnd);
+fprintf(f, "%u,", el->blockCount);
+
+fputs("[", f);
+for (int i=0; i<el->blockCount; ++i)
+    {
+    fprintf(f, "%u", el->blockSizes[i]);
+    if (i-1<el->blockCount)
+        fputc(',', f);
+    }
+fputs("]", f);
+fputs(",", f);
+
+fputs("[", f);
+for (int i=0; i<el->blockCount; ++i)
+    {
+    fprintf(f, "%u", el->qStarts[i]);
+    if (i-1<el->blockCount)
+        fputc(',', f); // json does not allow trailing commas
+    }
+fputs("]", f);
+fputs(",", f);
+
+fputs("[", f);
+for (int i=0; i<el->blockCount; ++i)
+    {
+    fprintf(f, "%u", el->tStarts[i]);
+    if (i-1<el->blockCount)
+        fputc(',', f);
+    }
+fputs("]", f);
+
+if (el->qSequence)
+    {
+    fputc(',',f);
+    fputc('[',f);
+    for (int i=0; i<el->blockCount; ++i)
+	{
+	fprintf(f, "'%s'", el->qSequence[i]);
+        if (i-1<el->blockCount)
+            fputc(',', f);
+	}
+    fputc(']',f);
+    fputc(',',f);
+
+    fputc('[',f);
+    for (int i=0; i<el->blockCount; ++i)
+	{
+	fprintf(f, "%s", el->tSequence[i]);
+        if (i-1<el->blockCount)
+            fputc(',', f);
+	}
+    fputc(']',f);
+    }
+
+if (ferror(f))
+    {
+    perror("Error writing psl file\n");
+    errAbort("\n");
+    }
+
+fputs("]\n", f);
+}
+
 void pslOutput(struct psl *el, FILE *f, char sep, char lastSep) 
 /* Print out psl.  Separate fields with sep. Follow last field with lastSep. */
 {
 int i;
 fprintf(f, "%u", el->match);
 fputc(sep,f);
 fprintf(f, "%u", el->misMatch);
 fputc(sep,f);
 fprintf(f, "%u", el->repMatch);
 fputc(sep,f);
 fprintf(f, "%u", el->nCount);
 fputc(sep,f);
 fprintf(f, "%u", el->qNumInsert);
 fputc(sep,f);
 fprintf(f, "%d", el->qBaseInsert);
@@ -454,58 +538,84 @@
 const struct psl *a = *((struct psl **)va);
 const struct psl *b = *((struct psl **)vb);
 return b->match - a->match;
 }
 
 static void pslLabelColumns(FILE *f)
 /* Write column info. */
 {
 fputs("\n"
 "match\tmis- \trep. \tN's\tQ gap\tQ gap\tT gap\tT gap\tstrand\tQ        \tQ   \tQ    \tQ  \tT        \tT   \tT    \tT  \tblock\tblockSizes \tqStarts\t tStarts\n"
 "     \tmatch\tmatch\t   \tcount\tbases\tcount\tbases\t      \tname     \tsize\tstart\tend\tname     \tsize\tstart\tend\tcount\n" 
 "---------------------------------------------------------------------------------------------------------------------------------------------------------------\n",
 f);
 }
 
+static void pslLabelColumnsJson(FILE *f) 
+/* Write column info as a JSON array */
+{
+fputs("['matches', 'misMatches', 'repMatches', 'nCount', 'qNumInsert', 'qBaseInsert', "
+        "'tNumInsert', 'tBaseInsert', 'strand', 'qName', 'qSize', 'qStart', 'qEnd', 'tName', "
+        "'tSize', 'tEnd', 'blockCount', 'blockSizes', 'qStarts', 'tStarts]", f);
+}
+
 void pslxWriteHead(FILE *f, enum gfType qType, enum gfType tType)
 /* Write header for extended (possibly protein) psl file. */
 {
 fprintf(f, "psLayout version 4 %s %s\n", gfTypeName(qType), gfTypeName(tType));
 pslLabelColumns(f);
 }
 
 void pslWriteHead(FILE *f)
 /* Write head of psl. */
 {
 fputs("psLayout version 3\n", f);
 pslLabelColumns(f);
 }
 
 void pslWriteAll(struct psl *pslList, char *fileName, boolean writeHeader)
 /* Write a psl file from list. */
 {
 FILE *f;
 struct psl *psl;
 
 f = mustOpen(fileName, "w");
 if (writeHeader)
     pslWriteHead(f);
 for (psl = pslList; psl != NULL; psl = psl->next)
     pslTabOut(psl, f);
 fclose(f);
 }
 
+void pslWriteAllJson(struct psl *pslList, FILE *f, boolean writeHeader)
+/* Write a psl file from list as a json array . */
+{
+fputs("[\n", f);
+if (writeHeader)
+    pslLabelColumnsJson(f);
+fputs(",\n", f);
+
+for (struct psl *psl = pslList; psl; psl = psl->next)
+    {
+    pslOutputJson(psl, f);
+    if (psl->next)
+        fputs(",\n", f);
+    }
+
+puts("]\n");
+}
+
 void pslxFileOpen(char *fileName, enum gfType *retQueryType, enum gfType *retTargetType, struct lineFile **retLf)
 /* Read header part of psl and make sure it's right.  Return
  * sequence types and file handle. */
 {
 char *line;
 int lineSize;
 char *words[30];
 char *version;
 int wordCount;
 int i;
 enum gfType qt = gftRna,  tt = gftDna;
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 
 if (!lineFileNext(lf, &line, &lineSize))
     warn("%s is empty", fileName);