4898794edd81be5285ea6e544acbedeaeb31bf78
max
  Tue Nov 23 08:10:57 2021 -0800
Fixing pointers to README file for license in all source code files. refs #27614

diff --git src/hg/hgTables/bedList.c src/hg/hgTables/bedList.c
index 4f18acf..892b560 100644
--- src/hg/hgTables/bedList.c
+++ src/hg/hgTables/bedList.c
@@ -1,842 +1,842 @@
 /* bedList - get list of beds in region that pass filtering. */
 
 /* Copyright (C) 2014 The Regents of the University of California 
- * See README in this or parent directory for licensing information. */
+ * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "hash.h"
 #include "linefile.h"
 #include "localmem.h"
 #include "dystring.h"
 #include "jksql.h"
 #include "cheapcgi.h"
 #include "trackDb.h"
 #include "customTrack.h"
 #include "hdb.h"
 #include "hui.h"
 #include "hCommon.h"
 #include "web.h"
 #include "featureBits.h"
 #include "portable.h"
 #include "hgTables.h"
 #include "wiggle.h"
 #include "correlate.h"
 #include "bedCart.h"
 #include "trashDir.h"
 #include "wikiTrack.h"
 
 
 boolean htiIsPsl(struct hTableInfo *hti)
 /* Return TRUE if table looks to be in psl format. */
 {
 return sameString("tStarts", hti->startsField);
 }
 
 void bedSqlFieldsExceptForChrom(struct hTableInfo *hti,
 	int *retFieldCount, char **retFields)
 /* Given tableInfo figure out what fields are needed to
  * add to a database query to have information to create
  * a bed. The chromosome is not one of these fields - we
  * assume that is already known since we're processing one
  * chromosome at a time.   Return a comma separated (no last
  * comma) list of fields that can be freeMem'd, and the count
  * of fields (this *including* the chromosome). */
 {
 struct dyString *fields = dyStringNew(128);
 int fieldCount = hTableInfoBedFieldCount(hti);
 
 dyStringPrintf(fields, "%s,%s", hti->startField, hti->endField);
 if (fieldCount >= 4)
     {
     if (hti->nameField[0] != 0)
 	dyStringPrintf(fields, ",%s", hti->nameField);
     else /* Put in . as placeholder. */
         dyStringPrintf(fields, ",'.'");
     }
 if (fieldCount >= 5)
     {
     if (hti->scoreField[0] != 0)
 	dyStringPrintf(fields, ",%s", hti->scoreField);
     else
 	dyStringPrintf(fields, ",0");
     }
 if (fieldCount >= 6)
     {
     if (hti->strandField[0] != 0)
 	dyStringPrintf(fields, ",%s", hti->strandField);
     else
         dyStringPrintf(fields, ",'.'");
     }
 if (fieldCount >= 8)
     {
     if (hti->cdsStartField[0] != 0)
 	dyStringPrintf(fields, ",%s,%s", hti->cdsStartField, hti->cdsEndField);
     else
         dyStringPrintf(fields, ",%s,%s", hti->startField, hti->endField);
     }
 if (fieldCount >= 12)
     {
     dyStringPrintf(fields, ",%s,%s,%s", hti->countField,
         hti->endsSizesField, hti->startsField);
     }
 if (htiIsPsl(hti))
     {
     /* For psl format we need to know chrom size as well
      * to handle reverse strand case. */
     dyStringPrintf(fields, ",tSize");
     }
 *retFieldCount = fieldCount;
 *retFields = dyStringCannibalize(&fields);
 }
 
 struct bed *bedFromRow(
         char *chrom,              /* Chromosome bed is on. */
         char **row,               /* Row with other data for bed. */
         int fieldCount,           /* Number of fields in final bed. */
         boolean isPsl,            /* True if in PSL format. */
         boolean isGenePred,       /* True if in GenePred format. */
         boolean isBedWithBlocks,  /* True if BED with block list. */
         boolean *pslKnowIfProtein,/* Have we figured out if psl is protein? */
 	boolean *pslIsProtein,    /* True if we know psl is protien. */
 	struct lm *lm)		  /* Local memory pool */
 /* Create bed from a database row when we already understand
  * the format pretty well.  The bed is allocated inside of
  * the local memory pool lm.  Generally use this in conjunction
  * with the results of a SQL query constructed with the aid
  * of the bedSqlFieldsExceptForChrom function. */
 {
 char *strand, tStrand, qStrand;
 struct bed *bed;
 int i, blockCount;
 
 lmAllocVar(lm, bed);
 bed->chrom = chrom;
 bed->chromStart = sqlUnsigned(row[0]);
 bed->chromEnd = sqlUnsigned(row[1]);
 
 if (fieldCount < 4)
     return bed;
 bed->name = lmCloneString(lm, row[2]);
 if (fieldCount < 5)
     return bed;
 bed->score = atoi(row[3]);
 if (fieldCount < 6)
     return bed;
 strand = row[4];
 qStrand = strand[0];
 tStrand = strand[1];
 if (tStrand == 0)
     bed->strand[0] = qStrand;
 else
     {
     /* psl: use XOR of qStrand,tStrand if both are given. */
     if (tStrand == qStrand)
 	bed->strand[0] = '+';
     else
 	bed->strand[0] = '-';
     }
 if (fieldCount < 8)
     return bed;
 bed->thickStart = sqlUnsigned(row[5]);
 bed->thickEnd   = sqlUnsigned(row[6]);
 if (fieldCount < 12)
     return bed;
 bed->blockCount = blockCount = sqlUnsigned(row[7]);
 lmAllocArray(lm, bed->blockSizes, blockCount);
 sqlSignedArray(row[8], bed->blockSizes, blockCount);
 lmAllocArray(lm, bed->chromStarts, blockCount);
 sqlSignedArray(row[9], bed->chromStarts, blockCount);
 if (isGenePred)
     {
     /* Translate end coordinates to sizes. */
     for (i=0; i<bed->blockCount; ++i)
 	bed->blockSizes[i] -= bed->chromStarts[i];
     }
 else if (isPsl)
     {
     if (!*pslKnowIfProtein)
 	{
 	/* Figure out if is protein using a rather elaborate but
 	 * working test I think Angie or Brian must have figured out. */
 	if (tStrand == '-')
             {
             int tSize = sqlUnsigned(row[10]);
             *pslIsProtein = (bed->chromStart == tSize -
                                                 ( 3*bed->blockSizes[bed->blockCount - 1]
                                                 + bed->chromStarts[bed->blockCount - 1]));
             }
         else
             {
             *pslIsProtein = (bed->chromEnd == 3*bed->blockSizes[bed->blockCount - 1]
                                               + bed->chromStarts[bed->blockCount - 1]);
             }
         *pslKnowIfProtein = TRUE;
 	}
     if (*pslIsProtein)
 	{
 	/* if protein then blockSizes are in protein space */
 	for (i=0; i<blockCount; ++i)
 	    bed->blockSizes[i] *= 3;
 	}
     if (tStrand == '-')
 	{
 	/* psl: if target strand is '-', flip the coords.
 	 * (this is the target part of pslRc from src/lib/psl.c) */
 	int tSize = sqlUnsigned(row[10]);
 	for (i=0; i<blockCount; ++i)
 	    {
             bed->chromStarts[i] = tSize -
 		    (bed->chromStarts[i] + bed->blockSizes[i]);
 	    }
 	reverseInts(bed->chromStarts, bed->blockCount);
 	reverseInts(bed->blockSizes, bed->blockCount);
 	}
     }
 if (!isBedWithBlocks)
     {
     /* non-bed: translate absolute starts to relative starts */
     for (i=0;  i < bed->blockCount;  i++)
 	bed->chromStarts[i] -= bed->chromStart;
     }
 return bed;
 }
 
 struct bed *getRegionAsBed(
         char *db, char *table,  /* Database and table. */
         struct region *region,  /* Region to get data for. */
         char *filter,           /* Filter to add to SQL where clause if any. */
         struct hash *idHash,    /* Restrict to id's in this hash if non-NULL. */
         struct lm *lm,          /* Where to allocate memory. */
         int *retFieldCount)     /* Number of fields. */
 /* Return a bed list of all items in the given range in table.
  * Cleanup result via lmCleanup(&lm) rather than bedFreeList.  */
 {
 char *fields = NULL;
 struct sqlResult *sr;
 struct hTableInfo *hti;
 struct bed *bedList=NULL, *bed;
 char **row;
 int fieldCount;
 boolean isPsl, isGenePred, isBedWithBlocks;
 boolean pslKnowIfProtein = FALSE, pslIsProtein = FALSE;
 struct sqlConnection *conn = NULL;
 char *dbTable = NULL;
 
 if (isCustomTrack(table))
     {
     struct customTrack *ct = ctLookupName(table);
     dbTable = ct->dbTableName;
     conn = hAllocConn(CUSTOM_TRASH);
     hti = hFindTableInfo(CUSTOM_TRASH, region->chrom, dbTable);
     }
 else
     {
     dbTable = table;
     struct trackDb *tdb;
     if(sameWord(db, database))
         tdb = tdbForTrack(db, table, &fullTrackList);
     else
         tdb = hTrackDbForTrack(db, table);
     conn = (tdb ? hAllocConnTrack(db, tdb) : hAllocConn(db));
     hti = hFindTableInfo(db, region->chrom, table);
     }
 if (hti == NULL)
     errAbort("Could not find table info for table %s.%s", db,table);
 
 
 if (isWiggle(db, table))
     {
     bedList = getWiggleAsBed(db, table, region, filter, idHash, lm, conn);
     fieldCount = 4;
     }
 else
     {
     bedSqlFieldsExceptForChrom(hti, &fieldCount, &fields);
     isPsl = htiIsPsl(hti);
     isGenePred = sameString("exonEnds", hti->endsSizesField);
     isBedWithBlocks = (
         (sameString("chromStarts", hti->startsField) ||
 	 sameString("blockStarts", hti->startsField))
 	     && sameString("blockSizes", hti->endsSizesField));
 
     /* All beds have at least chrom,start,end.  We omit the chrom
      * from the query since we already know it. */
     sr = regionQuery(conn, dbTable, fields, region, TRUE, filter);
     while (sr != NULL && (row = sqlNextRow(sr)) != NULL)
 	{
 	/* If have a name field apply hash filter. */
 	if (fieldCount >= 4 && idHash != NULL)
 	    if (!hashLookup(idHash, row[2]))
 		continue;
 	bed = bedFromRow(region->chrom, row, fieldCount, isPsl, isGenePred,
 			 isBedWithBlocks, &pslKnowIfProtein, &pslIsProtein, lm);
 	slAddHead(&bedList, bed);
 	}
     freez(&fields);
     sqlFreeResult(&sr);
     slReverse(&bedList);
     }
 hFreeConn(&conn);
 if (retFieldCount)
     *retFieldCount = fieldCount;
 return(bedList);
 }
 
 struct bed *getFilteredBeds(struct sqlConnection *conn,
 	char *table, struct region *region, struct lm *lm, int *retFieldCount)
 /* Get list of beds on single region that pass filtering. */
 {
 boolean isTabix = FALSE;
 /* region may be part of a list, and the routines we call work on lists of
  * regions.  Temporarily force region->next to NULL and restore at end. */
 struct region *oldNext = region->next;
 struct bed *bedList = NULL;
 region->next = NULL;
 
 if (isBigBed(database, table, curTrack, ctLookupName))
     bedList = bigBedGetFilteredBedsOnRegions(conn, database, table, region, lm, retFieldCount);
 else if (isLongTabixTable(table))
     bedList = longTabixGetFilteredBedsOnRegions(conn, database, table, region, lm, retFieldCount);
 else if (isBamTable(table))
     bedList = bamGetFilteredBedsOnRegions(conn, database, table, region, lm, retFieldCount);
 else if (isVcfTable(table, &isTabix))
     bedList = vcfGetFilteredBedsOnRegions(conn, database, table, region, lm, retFieldCount,
 					  isTabix);
 else if (isHicTable(table))
     bedList = hicGetFilteredBedsOnRegions(conn, database, table, region, lm, retFieldCount);
 else if (isCustomTrack(table))
     bedList = customTrackGetFilteredBeds(database, table, region, lm, retFieldCount);
 else if (sameWord(table, WIKI_TRACK_TABLE))
     bedList = wikiTrackGetFilteredBeds(table, region, lm, retFieldCount);
 else
     bedList = dbGetFilteredBedsOnRegions(conn, database, database, table, table, region, lm,
                                          retFieldCount);
 region->next = oldNext;
 return bedList;
 }
 
 /* Droplist menu for custom track visibility: */
 char *ctVisMenu[] =
 {
     "hide",
     "dense",
     "squish",
     "pack",
     "full",
 };
 int ctVisMenuSize = 5;
 
 char *ctVisWigMenu[] =
 {
     "hide",
     "dense",
     "full",
 };
 int ctVisWigMenuSize = 3;
 
 void doBedOrCtOptions(char *table, struct sqlConnection *conn,
                       boolean doCt)
 /* Put up form to get options on BED or custom track output. */
 /* (Taken from hgText.c/doBedCtOptions) */
 {
 char *table2 = NULL;	/* For now... */
 struct hTableInfo *hti = getHti(database, table, conn);
 char buf[256];
 char *setting;
 htmlOpen("Output %s as %s", table, (doCt ? "Custom Track" : "BED"));
 if (doGalaxy())
     startGalaxyForm();
 else if (doGreat())
     {
     verifyGreatAssemblies();
     startGreatForm();
     }
 else
     hPrintf("<FORM ACTION=\"%s\" METHOD=GET>\n", getScriptName());
 cartSaveSession(cart);
 if (!doGreat())
 {
     hPrintf("%s\n", "<TABLE><TR><TD>");
     if (doCt)
         {
         hPrintf("%s\n", "</TD><TD>"
                 "<A HREF=\"../goldenPath/help/customTrack.html\" TARGET=_blank>"
                 "Custom track</A> header: </B>");
         }
     else
         {
         cgiMakeCheckBox(hgtaPrintCustomTrackHeaders,
                         cartCgiUsualBoolean(cart, hgtaPrintCustomTrackHeaders, FALSE));
         hPrintf("%s\n", "</TD><TD> <B> Include "
                         "<A HREF=\"../goldenPath/help/customTrack.html\" TARGET=_blank>"
                         "custom track</A> header: </B>");
         }
     hPrintf("%s\n", "</TD></TR><TR><TD></TD><TD>name=");
     safef(buf, sizeof(buf), "tb_%s", hti->rootName);
     setting = cgiUsualString(hgtaCtName, buf);
     cgiMakeTextVar(hgtaCtName, setting, 16);
     hPrintf("%s\n", "</TD></TR><TR><TD></TD><TD>description=");
     safef(buf, sizeof(buf), "table browser query on %s%s%s",
           table, (table2 ? ", " : ""), (table2 ? table2 : ""));
     setting = cgiUsualString(hgtaCtDesc, buf);
     cgiMakeTextVar(hgtaCtDesc, setting, 50);
     hPrintf("%s\n", "</TD></TR><TR><TD></TD><TD>visibility=");
     if (isWiggle(database, table) || isBigWigTable(table))
         {
         setting = cartCgiUsualString(cart, hgtaCtVis, ctVisWigMenu[2]);
         cgiMakeDropList(hgtaCtVis, ctVisWigMenu, ctVisWigMenuSize, setting);
         }
     else
         {
         setting = cartCgiUsualString(cart, hgtaCtVis, ctVisMenu[3]);
         cgiMakeDropList(hgtaCtVis, ctVisMenu, ctVisMenuSize, setting);
         }
     hPrintf("%s\n", "</TD></TR><TR><TD></TD><TD>url=");
     setting = cartCgiUsualString(cart, hgtaCtUrl, "");
     cgiMakeTextVar(hgtaCtUrl, setting, 50);
     hPrintf("%s\n", "</TD></TR><TR><TD></TD><TD>");
     hPrintf("%s\n", "</TD></TR></TABLE>");
 }
 if (isWiggle(database, table) || isBedGraph(table) || isBigWigTable(table) )
     {
     char *setting = NULL;
     hPrintf("<P> <B> Select type of data output: </B> <BR>\n");
     setting = cartCgiUsualString(cart, hgtaCtWigOutType, outWigData);
     cgiMakeRadioButton(hgtaCtWigOutType, outWigBed, sameString(setting, outWigBed));
     hPrintf("BED format (no data value information, only position)<BR>\n");
     cgiMakeRadioButton(hgtaCtWigOutType, outWigData, sameString(setting, outWigData));
     hPrintf("DATA VALUE format (position and real valued data)</P>\n");
     }
 else
     {
     cgiDown(0.9);
     hPrintf("<B> Create one BED record per: </B>\n");
     if ((anyIntersection() && intersectionIsBpWise()) ||
 	(anySubtrackMerge(database, table) && subtrackMergeIsBpWise()))
 	{
         /* The original table may have blocks/CDS, described in hti, but
          * that info will be lost after base pair-wise operations.  So make
          * a temporary copy of hti with its flags tweaked: */
 	struct hTableInfo simplifiedHti;
 	memcpy(&simplifiedHti, hti, sizeof(simplifiedHti));
 	simplifiedHti.hasBlocks = FALSE;
 	simplifiedHti.hasCDS = FALSE;
 	fbOptionsHtiCart(&simplifiedHti, cart);
 	}
     else
 	fbOptionsHtiCart(hti, cart);
     }
 if (doCt)
     {
     if (doGalaxy())
         {
         /* send the action parameter with the form as well */
         cgiMakeHiddenVar(hgtaDoGetCustomTrackFile, "get custom track in file");
         printGalaxySubmitButtons();
         }
     else
         {
         cgiMakeButton(hgtaDoGetCustomTrackTb, "get custom track in table browser");
         hPrintf(" ");
         cgiMakeButton(hgtaDoGetCustomTrackFile, "get custom track in file");
         hPrintf("<BR>\n");
         cgiMakeButton(hgtaDoGetCustomTrackGb, "get custom track in genome browser");
         }
     }
 else
     {
     if (doGalaxy())
         {
         cgiMakeHiddenVar(hgtaDoGetBed, "get BED");
         printGalaxySubmitButtons();
         }
     else if (doGreat())
         {
         cgiMakeHiddenVar(hgtaDoGetBed, "get BED");
         printGreatSubmitButtons();
         }
     else
         cgiMakeButton(hgtaDoGetBed, "get BED");
     }
 if (!doGalaxy() && !doGreat())
     {
     hPrintf(" ");
     cgiMakeButton(hgtaDoMainPage, "cancel");
     hPrintf("</FORM>\n");
     }
 cgiDown(0.9);
 htmlClose();
 }
 
 void doOutBed(char *table, struct sqlConnection *conn)
 /* Put up form to select BED output format. */
 {
 doBedOrCtOptions(table, conn, FALSE);
 }
 
 void doOutCustomTrack(char *table, struct sqlConnection *conn)
 /* Put up form to select Custom Track output format. */
 {
 doBedOrCtOptions(table, conn, TRUE);
 }
 
 static struct customTrack *beginCustomTrack(char *table, int fields,
 	boolean doCt, boolean doWigHdr, boolean doDataPoints)
 /* If doCt, return a new custom track object from TB cart settings and params;
  * if !doCt, return NULL but print out a header line. */
 {
 char *ctName = cgiUsualString(hgtaCtName, table);
 char *ctDesc = cgiUsualString(hgtaCtDesc, table);
 char *ctUrl  = cgiUsualString(hgtaCtUrl, "");
 char *ctVis  = cgiUsualString(hgtaCtVis, "dense");
 int visNum = (int) hTvFromStringNoAbort(ctVis);
 struct customTrack *ctNew = NULL;
 
 if (visNum < 0)
     visNum = 0;
 if (doCt)
     {
     ctNew = newCt(ctName, ctDesc, visNum, ctUrl, fields);
     if (doDataPoints)
 	{
 	struct dyString *wigSettings = newDyString(0);
 	struct tempName tn;
 	trashDirFile(&tn, "ct", hgtaCtTempNamePrefix, ".wib");
 	ctNew->wibFile = cloneString(tn.forCgi);
 	char *wiggleFile = cloneString(ctNew->wibFile);
 	chopSuffix(wiggleFile);
 	strcat(wiggleFile, ".wia");
 	ctNew->wigAscii = cloneString(wiggleFile);
 	chopSuffix(wiggleFile);
 	/* .wig file will be created upon encoding in customFactory */
 	/*strcat(wiggleFile, ".wig");
 	ctNew->wigFile = cloneString(wiggleFile);
 	*/
 	ctNew->wiggle = TRUE;
 	dyStringPrintf(wigSettings,
 		       "type wiggle_0\nwibFile %s\n", ctNew->wibFile);
 	ctNew->tdb->settings = dyStringCannibalize(&wigSettings);
 	freeMem(wiggleFile);
 	}
     }
 else
     {
     if (doWigHdr)
 	hPrintf("track type=wiggle_0 name=\"%s\" description=\"%s\" "
 		"visibility=%d url=%s \n",
 		ctName, ctDesc, visNum, ctUrl);
     else
 	hPrintf("track name=\"%s\" description=\"%s\" visibility=%d url=%s \n",
 		ctName, ctDesc, visNum, ctUrl);
     }
 return ctNew;
 }
 
 boolean doGetBedOrCt(struct sqlConnection *conn, boolean doCt,
                      boolean doCtFile, boolean redirectToGb)
 /* Actually output bed or custom track. Return TRUE unless no results. */
 {
 char *db = cloneString(database);
 char *table = curTable;
 struct hTableInfo *hti = getHti(db, table, conn);
 struct featureBits *fbList = NULL, *fbPtr;
 struct customTrack *ctNew = NULL;
 boolean doCtHdr = (cartUsualBoolean(cart, hgtaPrintCustomTrackHeaders, FALSE)
 	|| doCt || doCtFile);
 char *ctWigOutType = cartCgiUsualString(cart, hgtaCtWigOutType, outWigData);
 char *fbQual = fbOptionsToQualifier();
 char fbTQ[128];
 int fields = hTableInfoBedFieldCount(hti);
 boolean gotResults = FALSE;
 struct region *region, *regionList = getRegions();
 boolean isBedGr = isBedGraph(curTable);
 boolean isBgWg = isBigWigTable(curTable);
 boolean needSubtrackMerge = anySubtrackMerge(database, curTable);
 boolean doDataPoints = FALSE;
 boolean isWig = isWiggle(database, table);
 struct wigAsciiData *wigDataList = NULL;
 struct dataVector *dataVectorList = NULL;
 boolean doRgb = bedItemRgb(hTrackDbForTrack(db, curTable));
 
 if (!cartUsualBoolean(cart, hgtaDoGreatOutput, FALSE) && !doCt)
     {
     textOpen();
     }
 
 if (cartUsualBoolean(cart, hgtaDoGreatOutput, FALSE))
     fputs("#", stdout);
 
 if ((isWig || isBedGr || isBgWg) && sameString(outWigData, ctWigOutType))
     doDataPoints = TRUE;
 
 for (region = regionList; region != NULL; region = region->next)
     {
     struct bed *bedList = NULL, *bed;
     struct lm *lm = lmInit(64*1024);
     struct dataVector *dv = NULL;
 
     if (isWig && doDataPoints)
         {
         if (needSubtrackMerge)
             {
             dv = wiggleDataVector(curTrack, curTable, conn, region);
             if (dv != NULL)
                 slAddHead(&dataVectorList, dv);
             }
         else
             {
             int count = 0;
             struct wigAsciiData *wigData = NULL;
             struct wigAsciiData *asciiData;
             struct wigAsciiData *next;
 
             wigData = getWiggleAsData(conn, curTable, region);
             for (asciiData = wigData; asciiData; asciiData = next)
                 {
                 next = asciiData->next;
                 if (asciiData->count)
                     {
                     slAddHead(&wigDataList, asciiData);
                     ++count;
                     }
                 }
             slReverse(&wigDataList);
             }
         }
     else if (isBedGr && doDataPoints)
         {
         dv = bedGraphDataVector(curTable, conn, region);
         if (dv != NULL)
             slAddHead(&dataVectorList, dv);
         }
     else if (isBgWg && doDataPoints)
         {
         dv = bigWigDataVector(curTable, conn, region);
         if (dv != NULL)
             slAddHead(&dataVectorList, dv);
         }
     else if (isWig || isBgWg)
         {
         dv = wiggleDataVector(curTrack, curTable, conn, region);
         bedList = dataVectorToBedList(dv);
         dataVectorFree(&dv);
         }
     else if (isBedGr)
         {
         bedList = getBedGraphAsBed(conn, curTable, region);
         }
     else
         {
         bedList = cookedBedList(conn, curTable, region, lm, &fields);
         }
 
     /*  this is a one-time only initial creation of the custom track
      *  structure to receive the results.  gotResults turns it off after
      *  the first time.
      */
     if (doCtHdr && !gotResults &&
 	((bedList != NULL) || (wigDataList != NULL) ||
          (dataVectorList != NULL)))
         {
         ctNew = beginCustomTrack(table, fields,
                                  doCt, (isWig || isBedGr || isBgWg), doDataPoints);
         }
 
     if (doDataPoints && (wigDataList || dataVectorList))
         gotResults = TRUE;
     else
         {
         if ((fbQual == NULL) || (fbQual[0] == 0))
             {
             for (bed = bedList;  bed != NULL;  bed = bed->next)
                 {
                 if (bed->name != NULL)
                     {
                     subChar(bed->name, ' ', '_');
 		    if (strcmp(bed->name, "") == 0)
 			bed->name = cloneString(".");
                     }
                 if (doCt)
                     {
                     struct bed *dupe = cloneBed(bed); /* Out of local memory. */
                     slAddHead(&ctNew->bedList, dupe);
                     }
                 else
                     {
                     if (doRgb)
                         bedTabOutNitemRgb(bed, fields, stdout);
                     else
                         bedTabOutN(bed, fields, stdout);
                     }
 
                 gotResults = TRUE;
                 }
             }
         else
             {
             safef(fbTQ, sizeof(fbTQ), "%s:%s", hti->rootName, fbQual);
             fbList = fbFromBed(db, fbTQ, hti, bedList, 0, 0, FALSE, FALSE);
             if (fields >= 6)
                 fields = 6;
             else if (fields >= 4)
                 fields = 4;
             else
                 fields = 3;
             if (doCt && ctNew)
                 {
                 ctNew->fieldCount = fields;
                 safef(ctNew->tdb->type, strlen(ctNew->tdb->type)+1,
                       "bed %d", fields);
                 }
             for (fbPtr=fbList;  fbPtr != NULL;  fbPtr=fbPtr->next)
                 {
                 if (fbPtr->name != NULL)
                     {
                     char *ptr = strchr(fbPtr->name, ' ');
                     if (ptr != NULL)
                         *ptr = 0;
                     }
                 if (doCt)
                     {
                     struct bed *fbBed = fbToBedOne(fbPtr);
                     slAddHead(&ctNew->bedList, fbBed );
                     }
                 else
                     {
                     if (fields >= 6)
                         hPrintf("%s\t%d\t%d\t%s\t%d\t%c\n",
                                 fbPtr->chrom, fbPtr->start, fbPtr->end, fbPtr->name,
                                 0, fbPtr->strand);
                     else if (fields >= 4)
                         hPrintf("%s\t%d\t%d\t%s\n",
                                 fbPtr->chrom, fbPtr->start, fbPtr->end, fbPtr->name);
                     else
                         hPrintf("%s\t%d\t%d\n",
                                 fbPtr->chrom, fbPtr->start, fbPtr->end);
                     }
                 gotResults = TRUE;
                 }
             featureBitsFreeList(&fbList);
             }
         }
     bedList = NULL;
     lmCleanup(&lm);
     }
 if (!gotResults)
     {
     hPrintf(NO_RESULTS);
     }
 else if (doCt)
     {
     int wigDataSize = 0;
     /* Load existing custom tracks and add this new one: */
     struct customTrack *ctList = getCustomTracks();
     removeNamedCustom(&ctList, ctNew->tdb->table);
     if (doDataPoints)
         {
         if (needSubtrackMerge || isBedGr || isBgWg)
             {
             slReverse(&dataVectorList);
             wigDataSize = dataVectorWriteWigAscii(dataVectorList, ctNew->wigAscii, 0, NULL);
             // TODO: see if can make prettier wig output here that
             // doesn't necessarily have one value per base
             }
         else
             {
             struct wiggleDataStream *wds = NULL;
             /* create an otherwise empty wds so we can print out the list */
             wds = wiggleDataStreamNew();
             wds->ascii = wigDataList;
             wigDataSize = wds->asciiOut(wds, db, ctNew->wigAscii, TRUE, FALSE);
 #if defined(DEBUG)    /*      dbg     */
             /* allow file readability for debug */
             chmod(ctNew->wigAscii, 0666);
 #endif
             wiggleDataStreamFree(&wds);
             }
         }
     else
         slReverse(&ctNew->bedList);
 
     slAddHead(&ctList, ctNew);
     /* Save the custom tracks out to file (overwrite the old file): */
     customTracksSaveCart(db, cart, ctList);
     /*  Put up redirect-to-browser page. */
     if (redirectToGb)
         {
         char browserUrl[256];
         char headerText[512];
         int redirDelay = 3;
         safef(browserUrl, sizeof(browserUrl),
               "%s?%s&db=%s", hgTracksName(), cartSidUrlString(cart), database);
         safef(headerText, sizeof(headerText),
               "<META HTTP-EQUIV=\"REFRESH\" CONTENT=\"%d;URL=%s\">",
               redirDelay, browserUrl);
         webStartHeader(cart, database, headerText,
                        "Table Browser: %s %s: %s", hOrganism(database),
                        freezeName, "get custom track");
         if (doDataPoints)
             {
             hPrintf("There are %d data points in custom track. ", wigDataSize);
             }
         else
             {
             hPrintf("There are %d items in custom track. ",
                     slCount(ctNew->bedList));
             }
         hPrintf("You will be automatically redirected to the genome browser in\n"
                 "%d seconds, or you can \n"
                 "<A HREF=\"%s\">click here to continue</A>.\n",
                 redirDelay, browserUrl);
         }
     }
 else if (doDataPoints)
     {
     if (needSubtrackMerge || isBedGr || isBgWg)
         {
         slReverse(&dataVectorList);
         dataVectorWriteWigAscii(dataVectorList, "stdout", 0, NULL);
         }
     else
         {
         /*	create an otherwise empty wds so we can print out the list */
         struct wiggleDataStream *wds = NULL;
         wds = wiggleDataStreamNew();
         wds->ascii = wigDataList;
         wds->asciiOut(wds, db, "stdout", TRUE, FALSE);
         wiggleDataStreamFree(&wds);
         }
     }
 return gotResults;
 }
 
 void doGetBed(struct sqlConnection *conn)
 /* Get BED output (UI has already told us how). */
 {
 doGetBedOrCt(conn, FALSE, FALSE, FALSE);
 }
 
 void doGetCustomTrackGb(struct sqlConnection *conn)
 /* Get Custom Track output (UI has already told us how). */
 {
 doGetBedOrCt(conn, TRUE, FALSE, TRUE);
 }
 
 void doGetCustomTrackTb(struct sqlConnection *conn)
 /* Get Custom Track output (UI has already told us how). */
 {
 boolean gotResults = doGetBedOrCt(conn, TRUE, FALSE, FALSE);
 if (gotResults)
     {
     flushCustomTracks();
     initGroupsTracksTables();
     doMainPage(conn);
     }
 }
 
 void doGetCustomTrackFile(struct sqlConnection *conn)
 /* Get Custom Track file output (UI has already told us how). */
 {
 doGetBedOrCt(conn, FALSE, TRUE, FALSE);
 }