5e8e0ed53c05f2ef9b51ce897440bae308367f47
chmalee
  Wed Feb 9 14:45:54 2022 -0800
Add csv output option to table browser to support opening files in
Excel, refs #27623. Also change the output filename message to hint
that Excel can auto-recognize .csv extensions and behave appropriately.
Lastly, fix output dropdown selection to properly warn on update
when using GTF output option or a snp table.

diff --git src/hg/hgTables/longRange.c src/hg/hgTables/longRange.c
index fa8e878..ba78291 100644
--- src/hg/hgTables/longRange.c
+++ src/hg/hgTables/longRange.c
@@ -38,31 +38,31 @@
 
 struct hTableInfo *longTabixToHti(char *table)
 /* Get standard fields of longTabix into hti structure. */
 {
 struct hTableInfo *hti;
 AllocVar(hti);
 hti->rootName = cloneString(table);
 hti->isPos= TRUE;
 strcpy(hti->chromField, "chrom");
 strcpy(hti->startField, "chromStart");
 strcpy(hti->nameField, "chromEnd");
 hti->type = cloneString("longTabix");
 return hti;
 }
 
-void longTabixTabOut(char *db, char *table, struct sqlConnection *conn, char *fields, FILE *f)
+void longTabixTabOut(char *db, char *table, struct sqlConnection *conn, char *fields, FILE *f, char outSep)
 /* Print out selected fields from long tabix.  If fields is NULL, then print out all fields. */
 {
 struct hTableInfo *hti = NULL;
 hti = getHti(db, table, conn);
 struct hash *idHash = NULL;
 char *idField = getIdField(db, curTrack, table, hti);
 int idFieldNum = 0;
 
 /* if we know what field to use for the identifiers, get the hash of names */
 if (idField != NULL)
     idHash = identifierHash(db, table);
 
 if (f == NULL)
     f = stdout;
 
@@ -82,33 +82,40 @@
     /* if we know the field for identifiers, save it away */
     if ((idField != NULL) && sameString(idField, bb->name))
 	idFieldNum = i;
     hashAddInt(fieldHash, bb->name, i);
     }
 
 /* Create an array of column indexes corresponding to the selected field list. */
 int *columnArray;
 AllocArray(columnArray, fieldCount);
 for (i=0; i<fieldCount; ++i)
     {
     columnArray[i] = hashIntVal(fieldHash, fieldArray[i]);
     }
 
 /* Output row of labels */
-fprintf(f, "#%s", fieldArray[0]);
+fprintf(f, "#");
+if (outSep == ',') fputc('"', f);
+fprintf(f, "%s", fieldArray[0]);
 for (i=1; i<fieldCount; ++i)
-    fprintf(f, "\t%s", fieldArray[i]);
+    {
+    fputc(outSep, f);
+    if (outSep == ',') fputc('"', f);
+    fprintf(f, "%s", fieldArray[i]);
+    if (outSep == ',') fputc('"', f);
+    }
 fprintf(f, "\n");
 
 struct asObject *as = longTabixAsObj();
 struct asFilter *filter = NULL;
 
 if (anyFilter())
     {
     filter = asFilterFromCart(cart, db, table, as);
     if (filter)
         {
 	fprintf(f, "# Filtering on %d columns\n", slCount(filter->columnList));
 	}
     }
 
 struct region *region, *regionList = getRegions();
@@ -120,33 +127,40 @@
 for (region = regionList; region != NULL && (maxOut > 0); region = region->next)
     {
     if (!lineFileSetTabixRegion(btf->lf, region->chrom, region->start, region->end))
         continue;
     char *row[6];
     int wordCount;
     while (((wordCount = lineFileChopTab(btf->lf, row)) > 0) && (maxOut > 0))
         {
 	if (asFilterOnRow(filter, row))
 	    {
 	    /* if we're looking for identifiers, check if this matches */
 	    if ((idHash != NULL)&&(hashLookup(idHash, row[idFieldNum]) == NULL))
 		continue;
 
 	    int i;
+            if (outSep == ',') fputc('"', f);
 	    fprintf(f, "%s", row[columnArray[0]]);
+            if (outSep == ',') fputc('"', f);
 	    for (i=1; i<fieldCount; ++i)
+                {
+                fputc(outSep, f);
+                if (outSep == ',') fputc('"', f);
 		fprintf(f, "\t%s", row[columnArray[i]]);
+                if (outSep == ',') fputc('"', f);
+                }
 	    fprintf(f, "\n");
 	    maxOut --;
 	    }
 	}
     freeMem(fileName);
     }
 
 if (maxOut == 0)
     errAbort("Reached output limit of %d data values, please make region smaller,\n\tor set a higher output line limit with the filter settings.", bigFileMaxOutput());
 /* Clean up and exit. */
 hashFree(&fieldHash);
 freeMem(fieldArray);
 freeMem(columnArray);
 }