2f262ebebd76067ea983cfeb7454ed626a450c61
angie
  Fri Jan 6 15:45:54 2017 -0800
In mysql 5.7 with the default configuration, it is now an error if a SELECT list using DISTINCT does not include all ORDER BY fields.  The solution is to add the ORDER BY fields to the end of the SELECT list if they're not already included.  The extra columns returned by the query are ignored.  refs #18626, #18597

diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c
index 8133315..9486106 100644
--- src/hg/hgc/hgc.c
+++ src/hg/hgc/hgc.c
@@ -1,26472 +1,26475 @@
 /* hgc - Human Genome Click processor - gets called when user clicks
  * on something in human tracks display. */
 
 /* Copyright (C) 2014 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 
 #include "common.h"
 #include <float.h>
 #include "obscure.h"
 #include "hCommon.h"
 #include "hash.h"
 #include "binRange.h"
 #include "bits.h"
 #include "memgfx.h"
 #include "hvGfx.h"
 #include "portable.h"
 #include "regexHelper.h"
 #include "errAbort.h"
 #include "dystring.h"
 #include "nib.h"
 #include "cheapcgi.h"
 #include "htmshell.h"
 #include "cart.h"
 #include "jksql.h"
 #include "dnautil.h"
 #include "dnaseq.h"
 #include "fa.h"
 #include "fuzzyFind.h"
 #include "seqOut.h"
 #include "hdb.h"
 #include "spDb.h"
 #include "hui.h"
 #include "hgRelate.h"
 #include "htmlPage.h"
 #include "psl.h"
 #include "cogs.h"
 #include "cogsxra.h"
 #include "bed.h"
 #include "cgh.h"
 #include "agpFrag.h"
 #include "agpGap.h"
 #include "ctgPos.h"
 #include "contigAcc.h"
 #include "ctgPos2.h"
 #include "clonePos.h"
 #include "bactigPos.h"
 #include "rmskOut.h"
 #include "xenalign.h"
 #include "isochores.h"
 #include "simpleRepeat.h"
 #include "cpgIsland.h"
 #include "cpgIslandExt.h"
 #include "genePred.h"
 #include "genePredReader.h"
 #include "pepPred.h"
 #include "peptideAtlasPeptide.h"
 #include "wabAli.h"
 #include "genomicDups.h"
 #include "est3.h"
 #include "rnaGene.h"
 #include "tRNAs.h"
 #include "gbRNAs.h"
 #include "encode/encodeRna.h"
 #include "hgMaf.h"
 #include "maf.h"
 #include "stsMarker.h"
 #include "stsMap.h"
 #include "rhMapZfishInfo.h"
 #include "recombRate.h"
 #include "recombRateRat.h"
 #include "recombRateMouse.h"
 #include "stsInfo.h"
 #include "stsInfo2.h"
 #include "mouseSyn.h"
 #include "mouseSynWhd.h"
 #include "ensPhusionBlast.h"
 #include "cytoBand.h"
 #include "knownMore.h"
 #include "snp125.h"
 #include "snp125Ui.h"
 #include "snp132Ext.h"
 #include "snp.h"
 #include "snpMap.h"
 #include "snpExceptions.h"
 #include "snp125Exceptions.h"
 #include "snp125CodingCoordless.h"
 #include "cnpIafrate.h"
 #include "cnpIafrate2.h"
 #include "cnpLocke.h"
 #include "cnpSebat.h"
 #include "cnpSebat2.h"
 #include "cnpSharp.h"
 #include "cnpSharp2.h"
 #include "delHinds2.h"
 #include "delConrad2.h"
 #include "dgv.h"
 #include "dgvPlus.h"
 #include "tokenizer.h"
 #include "softberryHom.h"
 #include "borkPseudoHom.h"
 #include "sanger22extra.h"
 #include "ncbiRefLink.h"
 #include "ncbiRefSeqLink.h"
 #include "refLink.h"
 #include "hgConfig.h"
 #include "estPair.h"
 #include "softPromoter.h"
 #include "customTrack.h"
 #include "trackHub.h"
 #include "hubConnect.h"
 #include "sage.h"
 #include "sageExp.h"
 #include "pslWScore.h"
 #include "lfs.h"
 #include "mcnBreakpoints.h"
 #include "fishClones.h"
 #include "featureBits.h"
 #include "web.h"
 #include "dbDb.h"
 #include "jaxOrtholog.h"
 #include "dnaProbe.h"
 #include "ancientRref.h"
 #include "jointalign.h"
 #include "gcPercent.h"
 #include "genMapDb.h"
 #include "altGraphX.h"
 #include "geneGraph.h"
 #include "stsMapMouse.h"
 #include "stsInfoMouse.h"
 #include "dbSnpRs.h"
 #include "genomicSuperDups.h"
 #include "celeraDupPositive.h"
 #include "celeraCoverage.h"
 #include "sample.h"
 #include "axt.h"
 #include "axtInfo.h"
 #include "jaxQTL.h"
 #include "jaxQTL3.h"
 #include "wgRna.h"
 #include "ncRna.h"
 #include "gbProtAnn.h"
 #include "hgSeq.h"
 #include "chain.h"
 #include "chainDb.h"
 #include "chainNetDbLoad.h"
 #include "chainToPsl.h"
 #include "chainToAxt.h"
 #include "netAlign.h"
 #include "stsMapRat.h"
 #include "stsInfoRat.h"
 #include "stsMapMouseNew.h"
 #include "stsInfoMouseNew.h"
 #include "vegaInfo.h"
 #include "vegaInfoZfish.h"
 #include "ensInfo.h"
 #include "scoredRef.h"
 #include "blastTab.h"
 #include "hdb.h"
 #include "hgc.h"
 #include "genbank.h"
 #include "pseudoGeneLink.h"
 #include "axtLib.h"
 #include "ensFace.h"
 #include "bdgpGeneInfo.h"
 #include "flyBaseSwissProt.h"
 #include "flyBase2004Xref.h"
 #include "affy10KDetails.h"
 #include "affy120KDetails.h"
 #include "encode/encodeRegionInfo.h"
 #include "encode/encodeErge.h"
 #include "encode/encodeErgeHssCellLines.h"
 #include "encode/encodeStanfordPromoters.h"
 #include "encode/encodeStanfordPromotersAverage.h"
 #include "encode/encodeIndels.h"
 #include "encode/encodeHapMapAlleleFreq.h"
 #include "hapmapSnps.h"
 #include "hapmapAllelesOrtho.h"
 #include "hapmapAllelesSummary.h"
 #include "sgdDescription.h"
 #include "sgdClone.h"
 #include "tfbsCons.h"
 #include "tfbsConsMap.h"
 #include "tfbsConsSites.h"
 #include "tfbsConsFactors.h"
 #include "simpleNucDiff.h"
 #include "bgiGeneInfo.h"
 #include "bgiSnp.h"
 #include "bgiGeneSnp.h"
 #include "botDelay.h"
 #include "vntr.h"
 #include "zdobnovSynt.h"
 #include "HInv.h"
 #include "bed5FloatScore.h"
 #include "bed6FloatScore.h"
 #include "pscreen.h"
 #include "jalview.h"
 #include "flyreg.h"
 #include "putaInfo.h"
 #include "gencodeIntron.h"
 #include "cutter.h"
 #include "switchDbTss.h"
 #include "chicken13kInfo.h"
 #include "gapCalc.h"
 #include "chainConnect.h"
 #include "dv.h"
 #include "dvBed.h"
 #include "dvXref2.h"
 #include "omimTitle.h"
 #include "dless.h"
 #include "gv.h"
 #include "gvUi.h"
 #include "protVar.h"
 #include "oreganno.h"
 #include "oregannoUi.h"
 #include "pgSnp.h"
 #include "pgPhenoAssoc.h"
 #include "pgSiftPred.h"
 #include "pgPolyphenPred.h"
 #include "bedDetail.h"
 #include "ec.h"
 #include "transMapClick.h"
 #include "retroClick.h"
 #include "mgcClick.h"
 #include "ccdsClick.h"
 #include "gencodeClick.h"
 #include "memalloc.h"
 #include "trashDir.h"
 #include "kg1ToKg2.h"
 #include "wikiTrack.h"
 #include "grp.h"
 #include "omicia.h"
 #include "atomDb.h"
 #include "pcrResult.h"
 #include "twoBit.h"
 #include "itemConf.h"
 #include "chromInfo.h"
 #include "gbWarn.h"
 #include "lsSnpPdbChimera.h"
 #include "mammalPsg.h"
 #include "net.h"
 #include "jsHelper.h"
 #include "virusClick.h"
 #include "gwasCatalog.h"
 #include "parClick.h"
 #include "mdb.h"
 #include "yaleGencodeAssoc.h"
 #include "itemDetailsHtml.h"
 #include "trackVersion.h"
 #include "numtsClick.h"
 #include "geneReviewsClick.h"
 #include "bigBed.h"
 #include "bigPsl.h"
 #include "bedTabix.h"
 #include "longRange.h"
 #include "hmmstats.h"
 #include "aveStats.h"
 #include "trix.h"
 #include "bPlusTree.h"
 
 static char *rootDir = "hgcData";
 
 #define LINESIZE 70  /* size of lines in comp seq feature */
 
 struct cart *cart;	/* User's settings. */
 char *seqName;		/* Name of sequence we're working on. */
 int winStart, winEnd;   /* Bounds of sequence. */
 char *database;		/* Name of mySQL database. */
 char *organism;		/* Colloquial name of organism. */
 char *genome;		/* common name, e.g. Mouse, Human */
 char *scientificName;	/* Scientific name of organism. */
 
 struct hash *trackHash;	/* A hash of all tracks - trackDb valued */
 
 void printLines(FILE *f, char *s, int lineSize);
 
 char mousedb[] = "mm3";
 
 /* JavaScript to automatically submit the form when certain values are
  * changed. */
 char *onChangeAssemblyText = "onchange=\"document.orgForm.submit();\"";
 
 #define NUMTRACKS 9
 int prevColor[NUMTRACKS]; /* used to optimize color change html commands */
 int currentColor[NUMTRACKS]; /* used to optimize color change html commands */
 int maxShade = 9;	/* Highest shade in a color gradient. */
 Color shadesOfGray[10+1];	/* 10 shades of gray from white to black */
 
 Color shadesOfRed[16];
 boolean exprBedColorsMade = FALSE; /* Have the shades of red been made? */
 int maxRGBShade = 16;
 
 struct bed *sageExpList = NULL;
 char ncbiOmimUrl[255] = {"http://www.ncbi.nlm.nih.gov/omim/"};
 
 struct palInfo
 {
     char *chrom;
     int left;
     int right;
     char *rnaName;
 };
 
 /* See this NCBI web doc for more info about entrezFormat:
  * http://www.ncbi.nlm.nih.gov/entrez/query/static/linking.html */
 char *entrezFormat = "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Search&db=%s&term=%s&doptcmdl=%s&tool=genome.ucsc.edu";
 char *entrezPureSearchFormat = "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=PureSearch&db=%s&details_term=%s[%s] ";
 char *ncbiGeneFormat = "http://www.ncbi.nlm.nih.gov/gene/%s";
 char *entrezUidFormat = "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=%s&list_uids=%d&dopt=%s&tool=genome.ucsc.edu";
 /* db=unists is not mentioned in NCBI's doc... so stick with this usage: */
 char *unistsnameScript = "http://www.ncbi.nlm.nih.gov:80/entrez/query.fcgi?db=unists";
 char *unistsScript = "http://www.ncbi.nlm.nih.gov/genome/sts/sts.cgi?uid=";
 char *gdbScript = "http://www.gdb.org/gdb-bin/genera/accno?accessionNum=";
 char *cloneDbScript = "http://www.ncbi.nlm.nih.gov/clone?term=";
 char *traceScript = "http://www.ncbi.nlm.nih.gov/Traces/trace.cgi?cmd=retrieve&val=";
 char *genMapDbScript = "http://genomics.med.upenn.edu/perl/genmapdb/byclonesearch.pl?clone=";
 char *uniprotFormat = "http://www.uniprot.org/uniprot/%s";
 char *dbSnpFormat = "http://www.ncbi.nlm.nih.gov/SNP/snp_ref.cgi?type=rs&rs=%s";
 char *clinVarFormat = "http://www.ncbi.nlm.nih.gov/clinvar/?term=%s[clv_acc]";
 
 /* variables for gv tables */
 char *gvPrevCat = NULL;
 char *gvPrevType = NULL;
 
 /* initialized by getCtList() if necessary: */
 struct customTrack *theCtList = NULL;
 
 /* getDNA stuff actually works when the database doesn't exist! */
 boolean dbIsFound = FALSE;
 
 /* forwards */
 char *getPredMRnaProtSeq(struct genePred *gp);
 void doAltGraphXDetails(struct trackDb *tdb, char *item);
 
 char* getEntrezNucleotideUrl(char *accession)
 /* get URL for Entrez browser on a nucleotide. free resulting string */
 {
 char url[512];
 safef(url, sizeof(url), entrezFormat, "Nucleotide", accession, "GenBank");
 return cloneString(url);
 }
 
 void printNcbiGeneUrl(FILE *f, char *gene)
 /* Print URL for Entrez browser on a nucleotide. */
 {
 fprintf(f, ncbiGeneFormat, gene);
 }
 
 void printEntrezNucleotideUrl(FILE *f, char *accession)
 /* Print URL for Entrez browser on a nucleotide. */
 {
 fprintf(f, entrezFormat, "Nucleotide", accession, "GenBank");
 }
 
 void printEntrezEstUrl(FILE *f, char *accession)
 /* Print URL for Entrez browser on a nucleotide. */
 {
 fprintf(f, entrezFormat, "nucest", accession, "GenBank");
 }
 
 void printEntrezProteinUrl(FILE *f, char *accession)
 /* Print URL for Entrez browser on a protein. */
 {
 fprintf(f, entrezFormat, "Protein", accession, "GenPept");
 }
 
 static void printEntrezPubMedUrl(FILE *f, char *term)
 /* Print URL for Entrez browser on a PubMed search. */
 {
 fprintf(f, entrezFormat, "PubMed", term, "DocSum");
 }
 
 static void printEntrezPubMedPureSearchUrl(FILE *f, char *term, char *keyword)
 /* Print URL for Entrez browser on a PubMed search. */
 {
 fprintf(f, entrezPureSearchFormat, "PubMed", term, keyword);
 }
 
 void printEntrezPubMedUidAbstractUrl(FILE *f, int pmid)
 /* Print URL for Entrez browser on a PubMed search. */
 {
 fprintf(f, entrezUidFormat, "PubMed", pmid, "Abstract");
 }
 
 void printEntrezPubMedUidUrl(FILE *f, int pmid)
 /* Print URL for Entrez browser on a PubMed search. */
 {
 fprintf(f, entrezUidFormat, "PubMed", pmid, "Summary");
 }
 
 void printEntrezGeneUrl(FILE *f, int geneid)
 /* Print URL for Entrez browser on a gene details page. */
 {
 fprintf(f, entrezUidFormat, "gene", geneid, "Graphics");
 }
 
 static void printEntrezOMIMUrl(FILE *f, int id)
 /* Print URL for Entrez browser on an OMIM search. */
 {
 char buf[64];
 snprintf(buf, sizeof(buf), "%d", id);
 fprintf(f, entrezFormat, "OMIM", buf, "Detailed");
 }
 
 void printSwissProtAccUrl(FILE *f, char *accession)
 /* Print URL for Swiss-Prot protein accession. */
 {
 fprintf(f, uniprotFormat, accession);
 }
 
 static void printSwissProtProteinUrl(FILE *f, char *accession)
 /* Print URL for Swiss-Prot NiceProt on a protein. */
 {
 char *spAcc;
 /* make sure accession number is used (not display ID) when linking to Swiss-Prot */
 spAcc = uniProtFindPrimAcc(accession);
 if (spAcc != NULL)
     {
     printSwissProtAccUrl(f, accession);
     }
 else
     {
     fprintf(f, uniprotFormat, accession);
     }
 }
 
 static void printSwissProtVariationUrl(FILE *f, char *accession)
 /* Print URL for Swiss-Prot variation data on a protein. */
 {
 if (accession != NULL)
     {
     fprintf(f, "\"http://www.expasy.org/cgi-bin/get-sprot-variant.pl?%s\"", accession);
     }
 }
 
 static void printOmimUrl(FILE *f, char *term)
 /* Print URL for OMIM data on a protein. */
 {
 if (term != NULL)
     {
     fprintf(f, "\"http://www.ncbi.nlm.nih.gov/omim/%s\"", term);
     }
 }
 
 static void printEntrezUniSTSUrl(FILE *f, char *name)
 /* Print URL for Entrez browser on a STS name. */
 {
 fprintf(f, "\"%s&term=%s\"", unistsnameScript, name);
 }
 
 static void printUnistsUrl(FILE *f, int id)
 /* Print URL for UniSTS record for an id. */
 {
 fprintf(f, "\"%s%d\"", unistsScript, id);
 }
 
 /* Print URL for GDB browser for an id
  * GDB currently inoperative, so have temporarily disabled this function
  *
 static void printGdbUrl(FILE *f, char *id)
 {
 fprintf(f, "%s", id);
 }
 */
 
 static void printCloneDbUrl(FILE *f, char *clone)
 /* Print URL for Clone Registry at NCBI for a clone */
 {
 fprintf(f, "\"%s%s\"", cloneDbScript, clone);
 }
 
 static void printTraceTiUrl(FILE *f, char *name)
 /* Print URL for Trace Archive at NCBI for a trace id (TI) */
 {
 fprintf(f, "\"%s%s\"", traceScript, name);
 }
 
 static void printTraceUrl(FILE *f, char *idType, char *name)
 /* Print URL for Trace Archive at NCBI for an identifier specified by type */
 {
 fprintf(f, "\"%s%s%%3D%%27%s%%27\"", traceScript, idType, name);
 }
 
 static void printGenMapDbUrl(FILE *f, char *clone)
 /* Print URL for GenMapDb at UPenn for a clone */
 {
 fprintf(f, "\"%s%s\"", genMapDbScript, clone);
 }
 
 static void printFlyBaseUrl(FILE *f, char *fbId)
 /* Print URL for FlyBase browser. */
 {
 fprintf(f, "\"http://flybase.net/.bin/fbidq.html?%s\"", fbId);
 }
 
 static void printBDGPUrl(FILE *f, char *bdgpName)
 /* Print URL for Berkeley Drosophila Genome Project browser. */
 {
 fprintf(f, "\"http://www.fruitfly.org/cgi-bin/annot/gene?%s\"", bdgpName);
 }
 
 char *hgTracksPathAndSettings()
 /* Return path with hgTracks CGI path and session state variable. */
 {
 static struct dyString *dy = NULL;
 if (dy == NULL)
     {
     dy = newDyString(128);
     dyStringPrintf(dy, "%s?%s", hgTracksName(), cartSidUrlString(cart));
     }
 return dy->string;
 }
 
 char *hgcPathAndSettings()
 /* Return path with this CGI script and session state variable. */
 {
 static struct dyString *dy = NULL;
 if (dy == NULL)
     {
     dy = newDyString(128);
     dyStringPrintf(dy, "%s?%s", hgcName(), cartSidUrlString(cart));
     }
 return dy->string;
 }
 
 void hgcAnchorSomewhere(char *group, char *item, char *other, char *chrom)
 /* Generate an anchor that calls click processing program with item
  * and other parameters. */
 {
 char *tbl = cgiUsualString("table", cgiString("g"));
 char *itemSafe = cgiEncode(item);
 printf("<A HREF=\"%s&g=%s&i=%s&c=%s&l=%d&r=%d&o=%s&table=%s\">",
        hgcPathAndSettings(), group, itemSafe, chrom, winStart, winEnd, other,
        tbl);
 freeMem(itemSafe);
 }
 
 void hgcAnchorPosition(char *group, char *item)
 /* Generate an anchor that calls click processing program with item
  * and group parameters. */
 {
 char *tbl = cgiUsualString("table", cgiString("g"));
 printf("<A HREF=\"%s&g=%s&i=%s&table=%s\">",
        hgcPathAndSettings(), group, item, tbl);
 }
 
 void hgcAnchorWindow(char *group, char *item, int thisWinStart,
                      int thisWinEnd, char *other, char *chrom)
 /* Generate an anchor that calls click processing program with item
  * and other parameters, INCLUDING the ability to specify left and
  * right window positions different from the current window*/
 {
 printf("<A HREF=\"%s&g=%s&i=%s&c=%s&l=%d&r=%d&o=%s\">",
        hgcPathAndSettings(), group, item, chrom,
        thisWinStart, thisWinEnd, other);
 }
 
 
 void hgcAnchorJalview(char *item, char *fa)
 /* Generate an anchor to jalview. */
 {
 struct dyString *dy = cgiUrlString();
     printf("<A HREF=\"%s?%s&jalview=YES\">",
 	    hgcName(), dy->string);
     dyStringFree(&dy);
 }
 
 void hgcAnchorTranslatedChain(int item, char *other, char *chrom, int cdsStart, int cdsEnd)
 /* Generate an anchor that calls click processing program with item
  * and other parameters. */
 {
 char *tbl = cgiUsualString("table", cgiString("g"));
 printf("<A HREF=\"%s&g=%s&i=%d&c=%s&l=%d&r=%d&o=%s&table=%s&qs=%d&qe=%d\">",
        hgcPathAndSettings(), "htcChainTransAli", item, chrom, winStart, winEnd, other,
        tbl, cdsStart, cdsEnd);
 }
 void hgcAnchorPseudoGene(char *item, char *other, char *chrom, char *tag, int start, int end, char *qChrom, int qStart, int qEnd, int chainId, char *db2)
 /* Generate an anchor to htcPseudoGene. */
 {
 char *encodedItem = cgiEncode(item);
 printf("<A HREF=\"%s&g=%s&i=%s&c=%s&l=%d&r=%d&o=%s&db2=%s&ci=%d&qc=%s&qs=%d&qe=%d&xyzzy=xyzzy#%s\">",
        hgcPathAndSettings(), "htcPseudoGene", encodedItem, chrom, start, end,
        other, db2, chainId, qChrom, qStart, qEnd, tag);
 }
 
 void hgcAnchorSomewhereDb(char *group, char *item, char *other,
                           char *chrom, char *db)
 /* Generate an anchor that calls click processing program with item
  * and other parameters. */
 {
 printf("<A HREF=\"%s&g=%s&i=%s&c=%s&l=%d&r=%d&o=%s&db=%s\">",
        hgcPathAndSettings(), group, item, chrom, winStart, winEnd, other, db);
 }
 
 void hgcAnchor(char *group, char *item, char *other)
 /* Generate an anchor that calls click processing program with item
  * and other parameters. */
 {
 hgcAnchorSomewhere(group, item, other, seqName);
 }
 
 void writeFramesetType()
 /* Write document type that shows a frame set, rather than regular HTML. */
 {
 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Frameset//EN\">\n", stdout);
 }
 
 boolean clipToChrom(int *pStart, int *pEnd)
 /* Clip start/end coordinates to fit in chromosome. */
 {
 static int chromSize = -1;
 
 if (chromSize < 0)
     chromSize = hChromSize(database, seqName);
 if (*pStart < 0) *pStart = 0;
 if (*pEnd > chromSize) *pEnd = chromSize;
 return *pStart < *pEnd;
 }
 
 struct genbankCds getCds(struct sqlConnection *conn, char *acc)
 /* obtain and parse the CDS, errAbort if not found or invalid */
 {
 char query[256];
 sqlSafef(query, sizeof(query), "select c.name from %s,%s c where (acc=\"%s\") and (c.id=cds)",
       gbCdnaInfoTable,cdsTable, acc);
 
 char *cdsStr = sqlQuickString(conn, query);
 if (cdsStr == NULL)
     errAbort("no CDS found for %s", acc);
 struct genbankCds cds;
 if (!genbankCdsParse(cdsStr, &cds))
     errAbort("can't parse CDS for %s: %s", acc, cdsStr);
 return cds;
 }
 
 
 void printCappedSequence(int start, int end, int extra)
 /* Print DNA from start to end including extra at either end.
  * Capitalize bits from start to end. */
 {
 struct dnaSeq *seq;
 int s, e, i;
 struct cfm *cfm;
 
 if (!clipToChrom(&start, &end))
     return;
 s = start - extra;
 e = end + extra;
 clipToChrom(&s, &e);
 
 printf("<P>Here is the sequence around this feature: bases %d to %d of %s. "
        "The bases that contain the feature itself are in upper case.</P>\n",
        s, e, seqName);
 seq = hDnaFromSeq(database, seqName, s, e, dnaLower);
 toUpperN(seq->dna + (start-s), end - start);
 printf("<PRE><TT>");
 cfm = cfmNew(10, 50, TRUE, FALSE, stdout, s);
 for (i=0; i<seq->size; ++i)
     {
     cfmOut(cfm, seq->dna[i], 0);
     }
 cfmFree(&cfm);
 printf("</TT></PRE>");
 }
 
 void printBand(char *chrom, int start, int end, boolean tableFormat)
 /* Print all matching chromosome bands.  */
 /* Ignore end if it is zero. */
 {
 char sband[32], eband[32];
 boolean gotS = FALSE;
 boolean gotE = FALSE;
 
 if (start < 0)
     return;
 gotS = hChromBand(database, chrom, start, sband);
 /* if the start lookup fails, don't bother with the end lookup */
 if (!gotS)
     return;
 /* if no end chrom, print start band and exit */
 if (end == 0)
     {
     if (tableFormat)
         printf("<TR><TH ALIGN=left>Band:</TH><TD>%s</TD></TR>\n",sband);
     else
         printf("<B>Band:</B> %s<BR>\n", sband);
     return;
 }
 gotE = hChromBand(database, chrom, end-1, eband);
 /* if eband equals sband, just use sband */
 if (gotE && sameString(sband,eband))
    gotE = FALSE;
 if (!gotE)
     {
     if (tableFormat)
         printf("<TR><TH ALIGN=left>Band:</TH><TD>%s</TD></TR>\n",sband);
     else
         printf("<B>Band:</B> %s<BR>\n", sband);
     return;
     }
 if (tableFormat)
     printf("<TR><TH ALIGN=left>Bands:</TH><TD>%s - %s</TD></TR>\n",sband, eband);
 else
     printf("<B>Bands:</B> %s - %s<BR>\n", sband, eband);
 
 }
 
 
 void printPosOnChrom(char *chrom, int start, int end, char *strand,
 		     boolean featDna, char *item)
 /* Print position lines referenced to chromosome. Strand argument may be NULL */
 {
 
 printf("<B>Position:</B> "
        "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
        hgTracksPathAndSettings(), database, chrom, start+1, end);
 printf("%s:%d-%d</A><BR>\n", chrom, start+1, end);
 /* printBand(chrom, (start + end)/2, 0, FALSE); */
 printBand(chrom, start, end, FALSE);
 printf("<B>Genomic Size:</B> %d<BR>\n", end - start);
 if (strand != NULL && differentString(strand,".") && isNotEmpty(strand))
     printf("<B>Strand:</B> %s<BR>\n", strand);
 else
     strand = "?";
 if (featDna && end > start)
     {
     char *tbl = cgiUsualString("table", cgiString("g"));
     strand = cgiEncode(strand);
     printf("<A HREF=\"%s&o=%d&g=getDna&i=%s&c=%s&l=%d&r=%d&strand=%s&table=%s\">"
 	   "View DNA for this feature</A>  (%s/%s)<BR>\n",  hgcPathAndSettings(),
 	   start, (item != NULL ? cgiEncode(item) : ""),
 	   chrom, start, end, strand, tbl, trackHubSkipHubName(database), trackHubSkipHubName(hGenome(database)));
     }
 }
 
 void printPosOnScaffold(char *chrom, int start, int end, char *strand)
 /* Print position lines referenced to scaffold.  'strand' argument may be null. */
 {
     char *scaffoldName;
     int scaffoldStart, scaffoldEnd;
 
     if (!hScaffoldPos(database, chrom, start, end, &scaffoldName, &scaffoldStart, &scaffoldEnd))
         {
         printPosOnChrom(chrom, start,end,strand, FALSE, NULL);
         return;
         }
     printf("<B>Scaffold:</B> %s<BR>\n", scaffoldName);
     printf("<B>Begin in Scaffold:</B> %d<BR>\n", scaffoldStart+1);
     printf("<B>End in Scaffold:</B> %d<BR>\n", scaffoldEnd);
     printf("<B>Genomic Size:</B> %d<BR>\n", scaffoldEnd - scaffoldStart);
     if (strand != NULL)
 	printf("<B>Strand:</B> %s<BR>\n", strand);
     else
 	strand = "?";
 }
 
 void printPos(char *chrom, int start, int end, char *strand, boolean featDna,
 	      char *item)
 /* Print position lines.  'strand' argument may be null. */
 {
 if (sameWord(organism, "Fugu"))
     /* Fugu is the only chrUn-based scaffold assembly, so it
      * has non-general code here.  Later scaffold assemblies
      * treat scaffolds as chroms.*/
     printPosOnScaffold(chrom, start, end, strand);
 else
     printPosOnChrom(chrom, start, end, strand, featDna, item);
 }
 
 void samplePrintPos(struct sample *smp, int smpSize)
 /* Print first three fields of a sample 9 type structure in
  * standard format. */
 {
 if ( smpSize != 9 )
     errAbort("Invalid sample entry!\n It has %d fields instead of 9\n",
              smpSize);
 
 printf("<B>Item:</B> %s<BR>\n", smp->name);
 printf("<B>Score:</B> %d<BR>\n", smp->score);
 printf("<B>Strand:</B> %s<BR>\n", smp->strand);
 printPos(smp->chrom, smp->chromStart, smp->chromEnd, NULL, TRUE, smp->name);
 }
 
 
 void bedPrintPos(struct bed *bed, int bedSize, struct trackDb *tdb)
 /* Print first bedSize fields of a bed type structure in
  * standard format. */
 {
 char *strand = NULL;
 if (bedSize >= 4 && bed->name[0] != 0)
     {
     char *label = "Item", *tdbLabel = NULL;
     if (tdb && ((tdbLabel = trackDbSetting(tdb, "bedNameLabel")) != NULL))
 	label = tdbLabel;
     printf("<B>%s:</B> %s<BR>\n", label, bed->name);
     }
 if (bedSize >= 5)
     {
     if (!tdb || !trackDbSetting(tdb, "noScoreFilter"))
         {
         char *scoreLabel = trackDbSettingOrDefault(tdb, "scoreLabel", "Score");
 	printf("<B>%s:</B> %d<BR>\n", scoreLabel, bed->score);
         }
     }
 if (bedSize >= 6)
    {
    strand = bed->strand;
    }
 printPos(bed->chrom, bed->chromStart, bed->chromEnd, strand, TRUE, bed->name);
 
 }
 
 void interactionPrintPos( struct bed *bed, int bedSize, struct trackDb *tdb)
 /* Print first bedSize fields of a bed type structure in
  * standard format. */
 {
 
 if (bed->blockCount == 2)
     {
     printf("<B>Intrachromosomal interaction:</B> <br>\n");
     printf("<B>Positions:</B><br> ");
     printf("<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
            hgTracksPathAndSettings(), database, bed->chrom,
            bed->chromStarts[0]+bed->chromStart,
 	   bed->chromStarts[0]+bed->chromStart + bed->blockSizes[0]);
     printf("%s:%d-%d</A>    \n",
 	   bed->chrom,
            bed->chromStarts[0]+bed->chromStart,
 	   bed->chromStarts[0]+bed->chromStart + bed->blockSizes[0]);
     printf("Size: %d   \n", bed->blockSizes[0]);
     printBand( bed->chrom, bed->chromStarts[0]+bed->chromStart,
 	   bed->chromStarts[0]+bed->chromStart + bed->blockSizes[0], FALSE);
 
     //printf("<BR>\n");
     printf("<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
            hgTracksPathAndSettings(), database, bed->chrom,
            bed->chromStarts[1]+bed->chromStart,
 	   bed->chromStarts[1]+bed->chromStart + bed->blockSizes[1]);
     printf("%s:%d-%d</A>     \n",
 	   bed->chrom,
            bed->chromStarts[1]+bed->chromStart,
 	   bed->chromStarts[1]+bed->chromStart + bed->blockSizes[1]);
     printf("Size: %d   \n", bed->blockSizes[0]);
     printBand( bed->chrom, bed->chromStarts[0]+bed->chromStart,
 	   bed->chromStarts[1]+bed->chromStart + bed->blockSizes[1], FALSE);
 
     printf("<BR>\n");
     printf("<B>Distance apart:</B>\n");
     printLongWithCommas(stdout,
 	bed->chromStarts[1] - bed->chromStarts[0] + bed->blockSizes[0]);
 
     printf("bp<BR>\n");
     }
 else
     {
     printf("<B>Interchromosomal interaction:</B> <br>\n");
     printf("<B>Positions:</B><br> ");
     printf("<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
            hgTracksPathAndSettings(), database, bed->chrom,
            bed->chromStarts[0]+bed->chromStart,
 	   bed->chromStarts[0]+bed->chromStart + bed->blockSizes[0]);
     printf("%s:%d-%d</A>    \n",
 	   bed->chrom,
            bed->chromStarts[0]+bed->chromStart,
 	   bed->chromStarts[0]+bed->chromStart + bed->blockSizes[0]);
     printf("Size: %d   \n", bed->blockSizes[0]);
     printBand( bed->chrom, bed->chromStarts[0]+bed->chromStart,
 	   bed->chromStarts[0]+bed->chromStart + bed->blockSizes[0], FALSE);
 
     char buffer[10 * 1024], *otherChrom = buffer;
     safef(buffer, sizeof buffer, "%s", bed->name);
     char *ptr;
     int otherStart, otherEnd;
 
     if (startsWith(bed->chrom, buffer))
 	{
 	otherChrom = strchr(buffer,'-');
 	otherChrom++;
 	}
 
     ptr = strchr(otherChrom,':');
     *ptr++ = 0;
     otherStart = atoi(ptr);
     ptr = strchr(ptr,'.');
     ptr++;
     ptr++;
     otherEnd = atoi(ptr);
     //printf("<BR>\n");
     printf("<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
 	   hgTracksPathAndSettings(), database, otherChrom,
 	   otherStart, otherEnd);
     printf("%s:%d-%d</A>     \n",
 	   otherChrom, otherStart, otherEnd);
     printf("Size: %d   \n", otherEnd - otherStart);
     printBand( otherChrom, otherStart, otherEnd, FALSE);
 
     printf("<BR>\n");
     }
 }
 
 
 void genericHeader(struct trackDb *tdb, char *item)
 /* Put up generic track info. */
 {
 if (item != NULL && item[0] != 0)
     cartWebStart(cart, database, "%s (%s)", tdb->longLabel, item);
 else
     cartWebStart(cart, database, "%s", tdb->longLabel);
 }
 
 void printItemDetailsHtml(struct trackDb *tdb, char *itemName)
 /* if track has an itemDetailsHtml, retrieve and print the HTML */
 {
 char *tableName = trackDbSetting(tdb, "itemDetailsHtmlTable");
 if (tableName != NULL)
     {
     struct sqlConnection *conn = hAllocConn(database);
     struct itemDetailsHtml *html, *htmls;
     // if the details table has chrom/start/end columns, then use these to lookup html
     if (sqlColumnExists(conn, tableName, "chrom"))
         {
         char *chrom = cgiString("c");
         int start   = cgiInt("o");
         int end     = cgiInt("t");
 
         htmls = sqlQueryObjs(conn, (sqlLoadFunc)itemDetailsHtmlLoad, sqlQueryMulti,
                        "select name, html from %s where \
                        name = '%s' and \
                        chrom = '%s' and \
                        start = '%d' and \
                        end = '%d'", tableName, itemName, chrom, start, end);
         }
     // otherwise, assume that the itemName is unique 
     else 
         htmls = sqlQueryObjs(conn, (sqlLoadFunc)itemDetailsHtmlLoad, sqlQueryMulti,
                        "select name, html from %s where name = '%s'", tableName, itemName);
 
     for (html = htmls; html != NULL; html = html->next)
         printf("<br>\n%s\n", html->html);
     itemDetailsHtmlFreeList(&htmls);
     hFreeConn(&conn);
     }
 }
 
 char *getIdInUrl(struct trackDb *tdb, char *itemName)
 /* If we have an idInUrlSql tag, look up itemName in that, else just
  * return itemName. */
 {
 char *sql = trackDbSetting(tdb, "idInUrlSql");
 char *id = itemName;
 if (sql != NULL)
     {
     char query[1024];
     sqlSafef(query, sizeof(query), sql, itemName);
     struct sqlConnection *conn = hAllocConn(database);
     id = sqlQuickString(conn, query);
     hFreeConn(&conn);
     }
 return id;
 }
 
 char* getUrlSetting(struct trackDb *tdb, char* urlSetting)
 /* get the "url" setting for the current track */
 {
 char *url;
 if (sameWord(urlSetting, "url"))
     url = tdb->url;
 else
     url = trackDbSetting(tdb, urlSetting);
 return url;
 }
 
 void printIframe(struct trackDb *tdb, char *itemName)
 /* print an iframe with the URL specified in trackDb (iframeUrl), can have 
  * the standard codes in it (like $$ for itemName, etc)
  */
 {
 char *url = getUrlSetting(tdb, "iframeUrl");
 if (url==NULL)
     return;
 char *eUrl = replaceInUrl(url, itemName, cart, database, seqName, winStart, winEnd, tdb->track, FALSE);
 if (eUrl==NULL)
     return;
 
 char *iframeOptions = trackDbSettingOrDefault(tdb, "iframeOptions", "width='100%%' height='1024'");
 // Resizing requires the hgcDetails pages to include a bit of javascript.
 //
 // Explanation how this works and why the javascript is needed:
 // http://stackoverflow.com/questions/153152/resizing-an-iframe-based-on-content
 // In short:
 // - iframes have a fixed size in html, resizing can only be done in javascript
 // - the iframed page cannot call the resize() function in the hgc html directly, as they have
 //   been loaded from different webservers
 // - one way around it is that the iframed page includes a helper page on our server and 
 //   send their size to the helper page (pages can call functions of included pages)
 // - the helper page then sends the size back to hgc (pages on the same server can
 //   call each others' functions)
 //   width='%s' height='%s' src='%s' seamless scrolling='%s' frameborder='%s'
 
 printf(" \
 <script> \
 function resizeIframe(height) \
 { \
      document.getElementById('hgcIframe').height = parseInt(height)+10; \
 } \
 </script> \
  \
 <iframe id='hgcIframe' src='%s' %s></iframe> \
 <p>", eUrl, iframeOptions);
 }
 
 void printCustomUrlWithLabel(struct trackDb *tdb, char *itemName, char *itemLabel, char *urlSetting, boolean encode)
 /* Print custom URL specified in trackDb settings. */
 {
 char urlLabelSetting[32];
 
 // replace the $$ and other wildchards with the url given in tdb 
 char *url = getUrlSetting(tdb, "url");
 //char* eUrl = constructUrl(tdb, url, itemName, encode);
 if (url==NULL || isEmpty(url))
     return;
 
 char* eUrl = replaceInUrl(url, itemName, cart, database, seqName, winStart, winEnd, tdb->track, encode);
 if (eUrl==NULL)
     return;
 
 /* create the url label setting for trackDb from the url
    setting prefix */
 safef(urlLabelSetting, sizeof(urlLabelSetting), "%sLabel", urlSetting);
 char *linkLabel = trackDbSettingOrDefault(tdb, urlLabelSetting, "Outside Link:");
 
 // if we got no item name from hgTracks or the item name does not appear in the URL
 // there is no need to show the item name at all
 if (isEmpty(itemName) || !stringIn("$$", url))
     {
     printf("<A TARGET=_blank HREF='%s'>%s</A><BR>",eUrl, linkLabel);
     return;
     }
 
 printf("<B>%s </B>",linkLabel);
 
 printf("<A HREF=\"%s\" target=_blank>", eUrl);
 
 if (sameWord(tdb->table, "npredGene"))
     {
     printf("%s (%s)</A><BR>\n", itemName, "NCBI MapView");
     }
 else
     {
     char *label = itemName;
     if (isNotEmpty(itemLabel) && sameString(itemName, itemLabel))
         label = itemLabel;
     printf("%s</A><BR>\n", label);
     }
 //freeMem(&eUrl); small memory leak
 }
 
 void printCustomUrl(struct trackDb *tdb, char *itemName, boolean encode)
 /* Wrapper to call printCustomUrlWithLabel using the url setting in trackDb */
 {
 char urlSetting[10];
 safef(urlSetting, sizeof(urlSetting), "url");
 
 printCustomUrlWithLabel(tdb, itemName, itemName, urlSetting, encode);
 }
 
 void printOtherCustomUrl(struct trackDb *tdb, char *itemName, char* urlSetting, boolean encode)
 /* Wrapper to call printCustomUrlWithLabel to use another url setting other than url in trackDb e.g. url2, this allows the use of multiple urls for a track
  to be set in trackDb. */
 {
 printCustomUrlWithLabel(tdb, itemName, itemName, urlSetting, encode);
 }
 
 void genericSampleClick(struct sqlConnection *conn, struct trackDb *tdb,
                         char *item, int start, int smpSize)
 /* Handle click in generic sample (wiggle) track. */
 {
 char table[64];
 boolean hasBin;
 struct sample *smp;
 char query[512];
 struct sqlResult *sr;
 char **row;
 boolean firstTime = TRUE;
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
         table, item, seqName, start);
 
 /*errAbort( "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
           table, item, seqName, start);*/
 
 
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     smp = sampleLoad(row+hasBin);
     samplePrintPos(smp, smpSize);
     }
 }
 
 void showBedTopScorers(struct bed *bedList, char *item, int start, int max)
 /* Show a list of track items sorted by descending score,
  *  with current item highlighted.
  *  max is upper bound on how many items will be displayed. */
 {
 int i;
 struct bed *bed;
 
 puts("<B>Top-scoring elements in window:</B><BR>");
 for (i=0, bed=bedList;  bed != NULL && i < max;  bed=bed->next, i++)
     {
     if (sameWord(item, bed->name) && bed->chromStart == start)
         printf("&nbsp;&nbsp;&nbsp;<B>%s</B> ", bed->name);
     else
         printf("&nbsp;&nbsp;&nbsp;%s ", bed->name);
     printf("(%s:%d-%d) %d<BR>\n",
                bed->chrom, bed->chromStart+1, bed->chromEnd, bed->score);
 
     }
 if (bed != NULL)
     printf("(list truncated -- more than %d elements)<BR>\n", max);
 }
 
 void showBedTopScorersInWindow(struct sqlConnection *conn,
 			       struct trackDb *tdb, char *item, int start,
 			       int maxScorers, char *filterTable, int filterCt)
 /* Show a list of track items in the current browser window, ordered by
  * score.  Track must be BED 5 or greater.  maxScorers is upper bound on
  * how many items will be displayed. If filterTable is not NULL and exists,
  * it contains the 100K top-scorers in the entire track, and filterCt
  * is the threshold for how many are candidates for display. */
 {
 struct sqlResult *sr = NULL;
 char **row = NULL;
 struct bed *bedList = NULL, *bed = NULL;
 char table[64];
 boolean hasBin = FALSE;
 char query[512];
 
 if (filterTable)
     {
     /* Track display only shows top-scoring N elements -- restrict
      * the list to these.  Get them from the filter table */
     hasBin = hOffsetPastBin(database, hDefaultChrom(database), filterTable);
     sqlSafef(query, sizeof(query), "select * from %s order by score desc limit %d",
             filterTable, filterCt);
     }
 else
     {
     hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
     sqlSafef(query, sizeof(query),
           "select * from %s where chrom = '%s' and chromEnd > %d and "
           "chromStart < %d order by score desc",
           table, seqName, winStart, winEnd);
     }
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     bed = bedLoadN(row+hasBin, 5);
     if (!filterTable
     ||  (  sameString(bed->chrom, seqName)
         && bed->chromStart < winEnd
         && bed->chromEnd > winStart))
         {
         slAddHead(&bedList, bed);
         }
     else
         bedFree(&bed);
     }
 sqlFreeResult(&sr);
 if (bedList == NULL)
     return;
 slReverse(&bedList);
 showBedTopScorers(bedList, item, start, maxScorers);
 }
 
 void getBedTopScorers(struct sqlConnection *conn, struct trackDb *tdb,
                    char *table, char *item, int start, int bedSize)
 /* This function determines if showTopScorers is set in trackDb and also */
 /* if the filterTopScorers setting is on. Then it passes the relevant */
 /* settings to showBedTopScorersInWindow() so that the top N scoring */
 /* items in the window are listed on the details page */
 {
 char *showTopScorers = trackDbSetting(tdb, "showTopScorers");
 char *filterTopScorers = trackDbSetting(tdb,"filterTopScorers");
 boolean doFilterTopScorers = FALSE;
 char *words[3];
 char cartVar[512];
 int filterTopScoreCt = 0;
 char *filterTopScoreTable = NULL;
 
 safef(cartVar, sizeof cartVar, "%s.%s", table, "filterTopScorersOn");
 if (filterTopScorers != NULL)
     {
     if (chopLine(cloneString(filterTopScorers), words) == 3)
         {
         doFilterTopScorers = sameString(words[0], "on");
         filterTopScoreCt = atoi(words[1]);
         filterTopScoreTable = words[2];
         }
     }
 
 if (bedSize >= 5 && showTopScorers != NULL)
     {
     /* list top-scoring elements in window */
     int maxScorers = sqlUnsigned(showTopScorers);
     doFilterTopScorers = cartCgiUsualBoolean(cart, cartVar, doFilterTopScorers);
     if (doFilterTopScorers && hTableExists(database, filterTopScoreTable))
         {
         /* limit to those in the top N, from table */
         safef(cartVar, sizeof cartVar, "%s.%s", table, "filterTopScorersCt");
         filterTopScoreCt = cartCgiUsualInt(cart, cartVar, filterTopScoreCt);
         }
     else
         /* show all */
         filterTopScoreTable = NULL;
     showBedTopScorersInWindow(conn, tdb, item, start, maxScorers,
                                 filterTopScoreTable, filterTopScoreCt);
     }
 }
 
 void linkToOtherBrowser(char *otherDb, char *chrom, int start, int end);
 void linkToOtherBrowserExtra(char *otherDb, char *chrom, int start, int end, char *extra);
 
 static void printCompareGenomeLinks(struct trackDb *tdb,char *name)
 /* if "compareGenomeLinks" exists then a table of the same name in n different databases is sought.
    if a row exist in the other db table matching the current item, then a link is printed */
 {
 char *setting = trackDbSettingClosestToHome(tdb,"compareGenomeLinks");
 if (setting == NULL)
     return;
 struct sqlConnection *conn = hAllocConn(database); // Need only to connect to one db
 if (conn == NULL)
     return;
 
 char *words[20];
 setting = cloneString(setting);
 int ix,cnt = chopLine(setting, words);
 char query[512];
 char extra[128];
 boolean gotOne = FALSE;
 for (ix=0;ix<cnt;ix++)
     {
     char *db = words[ix];          // db.table.column=title or db.table=title or db=title
     char *table,*column = "name";
     char *title = strchr(words[ix],'=');
     if (title==NULL) // Must have title
         continue;
     *title = '\0';
     title++;
     if ((table = strchr(words[ix],'.')) == NULL)
         table = tdb->table;
     else
         {
         *table++ = '\0';  // assigns before advance
         if ((words[ix] = strchr(table,'.')) != NULL)
             {
             *words[ix] = '\0';
             column = ++words[ix]; // advance before assigns
             }
         }
     sqlSafef(query,sizeof(query),"select chrom,chromStart,chromEnd from %s.%s where %s=\"%s\";",
           db,table,column,name);
     struct sqlResult *sr = sqlGetResult(conn, query);
     if (sr == NULL)
         continue;
     char **row = sqlNextRow(sr);
     if (row == NULL)
         continue;
     char *chrom = *row++;
     int beg = atoi(*row++);
     int end = atoi(*row);
     if (!gotOne)
         {
         gotOne = TRUE;
         printf("<P>The item \"%s\" has been located in other genomes:\n<UL>\n",name);
         }
     printf("<LI>");
     safef(extra,sizeof(extra),"%s=full",tdb->track);
     linkToOtherBrowserExtra(db, chrom, beg, end, extra);
     printf("%s</A></LI>\n",strSwapChar(title,'_',' '));
     sqlFreeResult(&sr);
     }
 hFreeConn(&conn);
 freeMem(setting);
 if (gotOne)
     printf("</UL>\n");
 else
     printf("<P>Currently the item \"%s\" has not been located in another genome.\n",name);
 }
 
 void mafPrettyOut(FILE *f, struct mafAli *maf, int lineSize,
 	boolean onlyDiff, int blockNo);
 
 void doAtom( struct trackDb *tdb, char *item)
 {
 char table[64];
 boolean hasBin;
 //struct bed *bed;
 char query[512];
 struct sqlResult *sr;
 char **row;
 //boolean firstTime = TRUE;
 int start = cartInt(cart, "o");
 //struct sqlConnection *conn = hAllocConn(database);
 char *user = cfgOption("db.user");
 char *password = cfgOption("db.password");
 struct sqlConnection *sc;
 struct atom ret;
 
 genericHeader(tdb, item);
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 #if 0
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d", table, escapedName, seqName, start);
 sr = sqlGetResult(conn, query);
 printf("<B>This is the item you clicked on:</B><BR>\n");
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     bed = bedLoadN(row+hasBin, 4);
     bedPrintPos(bed, 4, tdb);
     }
 sqlFreeResult(&sr);
 
 sqlSafef(query, sizeof query, "select * from %s where name = '%s'", table, escapedName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     bed = bedLoadN(row+hasBin, 4);
     if (bed->chromStart != start)
 	{
 	htmlHorizontalLine();
 	firstTime = FALSE;
 	printf("<B>Another instances on %s:</B><BR>\n",database);
 	bedPrintPos(bed, 4, tdb);
 	}
     }
 sqlFreeResult(&sr);
 #endif
 
 sc = sqlConnectRemote("localhost", user, password, "hgFixed");
 sqlSafef(query, sizeof(query),
       "select * from %s where name = '%s'", table, item);
 sr = sqlGetResult(sc, query);
 printf("<B>Atom %s instances ('*' marks item you clicked on)</B><BR>\n",item);
 printf("<PRE>\n");
 //printf("Ins#\tSpecies\t\tChrom\tStart\tEnd\tStrand\n");
 printf( "     # %-10s %-5s %12s %12s %10s    %s  %-10s %-10s\n",
     "species","chrom", "start", "end", "length", "strand","fivePrime","threePrime");
 while ((row = sqlNextRow(sr)) != NULL)
     {
     atomStaticLoad(row, &ret);
     //atomOutput(&ret, stdout, '\t', '\n');
     linkToOtherBrowser(ret.species, ret.chrom, ret.start, ret.end);
     if (sameString(ret.chrom, seqName) && (start  == ret.start) &&
 	sameString(ret.species, database))
 	printf("* ");
     else
         printf("  ");
     printf( "%4d %-10s %-5s %12d %12d %10d      %c      %-10s %-10s\n",
             ret.instance, ret.species,ret.chrom, ret.start + 1, ret.end,
             ret.end - ret.start + 1, ret.strand[0],ret.fivePrime,ret.threePrime);
     }
 printf("</A>");
 sqlFreeResult(&sr);
 
 if (!sameString("atom20080226d", table))
     return;
 
 printf("<TABLE>");
 printf("<THEAD>");
 printf("<TBODY>");
 printf("<TR><TH>");
 printf("Suh Trees<BR>\n");
 printf("<IMG src=http://hgwdev.cse.ucsc.edu/~braney/suhTrees/%s.tt.png><BR>",item);
 printf("<TD><IMG src=http://hgwdev.cse.ucsc.edu/~braney/suhTrees/%s.gt.png><BR>",item);
 printf("<TR><TH>");
 printf("NJ Trees<BR>\n");
 printf("<IMG src=http://hgwdev.cse.ucsc.edu/~braney/njTrees/%s.tt.png><BR>",item);
 printf("<TD><IMG src=http://hgwdev.cse.ucsc.edu/~braney/njTrees/%s.gt.png><BR>",item);
 printf("<TR><TH>");
 /*
 printf("Gap UPGMA Trees<BR>\n");
 printf("<IMG src=http://hgwdev.cse.ucsc.edu/~braney/gap992Trees/%s.tt.png><BR>",item);
 printf("<TD><IMG src=http://hgwdev.cse.ucsc.edu/~braney/gap992Trees/%s.gt.png><BR>",item);
 printf("</TABLE>");
 
 */
 return;
 
 char buffer[4096];
 struct mafFile *mf;
 safef(buffer, sizeof buffer, "/gbdb/hgFixed/%s/%s.maf",table, item);
 mf = mafMayOpen(buffer);
 if (mf != NULL)
     {
     mafFileFree(&mf);
     mf = mafReadAll(buffer);
     struct mafAli *mafAli;
     int count = 1;
     int numBlocks = 0;
 
     for (mafAli=mf->alignments; mafAli; mafAli = mafAli->next)
 	numBlocks++;
 
     for (mafAli=mf->alignments; mafAli; mafAli = mafAli->next)
 	{
 	printf("<BR><B>Multiple Alignment Block %d of %d</B><BR>",
 	    count, numBlocks);
 	mafPrettyOut(stdout, mafAli, 70, FALSE, count++);
 	if (mafAli->next != NULL)
 	    {
 	    struct mafAli *next = mafAli->next;
 	    struct mafComp *comp1 = mafAli->components;
 	    struct mafComp *comp2 = next->components;
 
 	    printf("<BR><B>Gaps:</B>\n");
 	    for(; comp1 ; comp1 = comp1->next, comp2 = comp2->next)
 		{
 		int diff;
 		char dbOnly[4096];
 
 		diff = comp2->start - (comp1->start + comp1->size);
 
 		safef(dbOnly, sizeof(dbOnly), "%s", comp1->src);
 		chopPrefix(dbOnly);
 		printf("%-20s %d\n",hOrganism(dbOnly), diff);
 		}
 
 	    printf("<BR>");
 	    }
         }
     }
 }
 
 char **getIdNameMap(struct trackDb *tdb, struct asColumn *col, int *size)
 /* Allocate and fill an array mapping id to name.  Currently limited to specific columns. */
 {
 char *idNameTable = trackDbSetting(tdb, "sourceTable");
 if (!idNameTable || differentString("sourceIds", col->name))
     return NULL;
 
 struct sqlResult *sr;
 char query[256];
 char **row;
 char **idNames;
 
 sqlSafef(query, sizeof(query), "select max(id) from %s", idNameTable);
 struct sqlConnection *conn = hAllocConnTrack(database, tdb);
 int maxId = sqlQuickNum(conn, query);
 AllocArray(idNames, maxId+1);
 sqlSafef(query, sizeof(query), "select id, name from %s", idNameTable);
 sr = sqlGetResult(conn, query);
 int id;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     id = sqlUnsigned(row[0]);
     if (id > maxId)
         errAbort("Internal error:  id %d > maxId %d in %s", id, maxId, idNameTable);
     idNames[id] = cloneString(row[1]);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 if (size)
     *size = maxId+1;
 return idNames;
 }
 
 void printIdOrLinks(struct asColumn *col, struct hash *fieldToUrl, struct trackDb *tdb, char *idList)
 /* if trackDb does not contain a "urls" entry for current column name, just print idList as it is.
  * Otherwise treat idList as a comma-sep list of IDs and print one row per id, with a link to url,
  * ($$ in url is OK, wildcards like $P, $p, are also OK)
  * */
 {
 // try to find a fieldName=url setting in the "urls" tdb statement, print id if not found
 char *url = NULL;
 if (fieldToUrl != NULL)
     url = (char*)hashFindVal(fieldToUrl, col->name);
 if (url == NULL)
     {
     printf("<td>%s</td></tr>\n", idList);
     return;
     }
 
 // split the id into parts and print each part as a link
 struct slName *slIds = slNameListFromComma(idList);
 struct slName *itemId = NULL;
 
 // handle id->name mapping for multi-source items
 int nameCount;
 char **idNames = getIdNameMap(tdb, col, &nameCount);
 
 printf("<td>");
 for (itemId = slIds; itemId!=NULL; itemId = itemId->next) 
     {
     if (itemId != slIds)
         printf(", ");
     char *itemName = trimSpaces(itemId->name);
 
     if (idNames)
         {
         unsigned int id = sqlUnsigned(itemName);
         if (id < nameCount)
             itemName = idNames[sqlUnsigned(itemName)];
         }
 
     // a | character can optionally be used to separate the ID used for $$ from the name shown in the link (like in Wikimedia markup)
     char *idForUrl = itemName;
     boolean encode = TRUE;
     if (strstr(itemName, "|"))
         {
         char *parts[2];
 	chopString(itemName, "|", parts, ArraySize(parts));
         idForUrl = parts[0];
         itemName = parts[1];
         encode = FALSE; // assume the link is already encoded
         }
 
     char *idUrl = replaceInUrl(url, idForUrl, cart, database, seqName, winStart, 
                     winEnd, tdb->track, encode);
     printf("<a href=\"%s\" target=\"_blank\">%s</a>", idUrl, itemName);
     } 
 printf("</td></tr>\n");
 freeMem(slIds);
 //freeMem(idNames);
 }
 
 int extraFieldsStart(struct trackDb *tdb, int fieldCount, struct asObject *as)
 /* return the index of the first extra field */
 {
 int start = 0;
 char *type = cloneString(tdb->type);
 char *word = nextWord(&type);
 if (word && (sameWord(word,"bed") || sameWord(word,"bigBed") || sameWord(word,"bigGenePred")))
     {
     if (NULL != (word = nextWord(&type)))
         start = sqlUnsigned(word);
     else // custom beds and bigBeds may not have full type "begBed 9 +"
         start = max(0,slCount(as->columnList) - fieldCount);
     }
 return start;
 }
 
 struct slPair* getExtraFields(struct trackDb *tdb, char **fields, int fieldCount)
 /* return the extra field names and their values as a list of slPairs.  */
 {
 struct asObject *as = asForDb(tdb, database);
 if (as == NULL)
     return NULL;
 struct asColumn *col = as->columnList;
 
 int start = extraFieldsStart(tdb, fieldCount, as);
 // skip past known fields
 for (;start !=0 && col != NULL;col=col->next)
     if (start > 0)
         start--;
 
 struct slPair *extraFields = 0;
 int count = 0;
 for (;col != NULL && count < fieldCount;col=col->next)
     {
     struct slPair *slp;
     AllocVar(slp);
     char *fieldName = col->name;
     char *fieldVal = fields[count];
     slp->name = fieldName;
     slp->val = fieldVal;
     slAddHead(&extraFields, slp);
     count++;
     //printf("name %s, val %s, idx %d<br>", fieldName, fieldVal, count);
     }
 slReverse(extraFields);
 return extraFields;
 }
 
 int extraFieldsPrint(struct trackDb *tdb,struct sqlResult *sr,char **fields,int fieldCount)
 // Any extra bed or bigBed fields (defined in as and occurring after N in bed N + types.
 // sr may be null for bigBeds.
 // Returns number of extra fields actually printed.
 {
 struct asObject *as = asForDb(tdb, database);
 if (as == NULL)
     return 0;
 
 // We are trying to print extra fields so we need to figure out how many fields to skip
 int start = extraFieldsStart(tdb, fieldCount, as);
 
 struct asColumn *col = as->columnList;
 char *urlsStr = trackDbSetting(tdb, "urls");
 struct hash* fieldToUrl = hashFromString(urlsStr);
 boolean skipEmptyFields = trackDbSettingOn(tdb, "skipEmptyFields");
 
 // make list of fields to skip
 char *skipFieldsStr = trackDbSetting(tdb, "skipFields");
 struct slName *skipIds = NULL;
 if (skipFieldsStr)
     skipIds = slNameListFromComma(skipFieldsStr);
 
 // make list of fields that are separated from other fields
 char *sepFieldsStr = trackDbSetting(tdb, "sepFields");
 struct slName *sepFields = NULL;
 if (sepFieldsStr)
     sepFields = slNameListFromComma(sepFieldsStr);
 
 // iterate over fields, print as table rows
 int count = 0;
 for (;col != NULL && count < fieldCount;col=col->next)
     {
     if (start > 0)  // skip past already known fields
         {
         start--;
         continue;
         }
     int ix = count;
     if (sr != NULL)
         {
         ix = sqlFieldColumn(sr, col->name); // If sr provided, name must match sql columnn name!
         if (ix == -1 || ix > fieldCount)      // so extraField really just provides a label
             continue;
         }
 
     char *fieldName = col->name;
 
     if (count == 0)
         printf("<br><table class='bedExtraTbl'>");
     
     count++;
 
     // do not print a row if the fieldName from the .as file is in the "skipFields" list
     // or if a field name starts with _. This maked bigBed extra fields consistent with
     // external extra fields in that _ field names have some meaning and are not shown
     if (startsWith("_", fieldName) || (skipIds && slNameInList(skipIds, fieldName)))
         continue;
 
     // skip this row if it's empty and "skipEmptyFields" option is set
     if (skipEmptyFields && isEmpty(fields[ix]))
         continue;
 
     // split this table to separate current row from the previous one, if the trackDb option is set
     if (sepFields && slNameInList(sepFields, fieldName))
         printf("</tr></table>\n<p>\n<table class='bedExtraTbl'>");
 
     // field description
     char *entry;
     if (sameString(fieldName, "cdsStartStat") && sameString("enum('none','unk','incmpl','cmpl')", col->comment))
         entry = "Status of CDS start annotation (none, unknown, incomplete, or complete)";
     else if (sameString(fieldName, "cdsEndStat") && sameString("enum('none','unk','incmpl','cmpl')", col->comment))
         entry = "Status of CDS end annotation (none, unknown, incomplete, or complete)";
     else
         entry = col->comment;
     printf("<tr><td>%s</td>", entry); // bold style now in HGStyle.css
 
     if (col->isList || col->isArray || col->lowType->stringy || asTypesIsInt(col->lowType->type))
         printIdOrLinks(col, fieldToUrl, tdb, fields[ix]);
     else if (asTypesIsFloating(col->lowType->type))
         {
         double valDouble = strtod(fields[ix],NULL);
         if (errno == 0 && valDouble != 0)
             printf("<td>%g</td></tr>\n", valDouble);
         else
             printf("<td>%s</td></tr>\n", fields[ix]); // decided not to print error
         }
     else
         printf("<td>%s</td></tr>\n", fields[ix]);
     }
 asObjectFree(&as);
 freeMem(fieldToUrl);
 if (skipIds)
     slFreeList(skipIds);
 if (sepFields)
     slFreeList(sepFields);
 
 if (count > 0)
     printf("</table>\n");
 
 return count;
 }
 
 void genericBedClick(struct sqlConnection *conn, struct trackDb *tdb,
 		     char *item, int start, int bedSize)
 /* Handle click in generic BED track. */
 {
 char table[64];
 boolean hasBin;
 struct bed *bed;
 char query[512];
 struct sqlResult *sr;
 char **row;
 boolean firstTime = TRUE;
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 if (bedSize <= 3)
     sqlSafef(query, sizeof query, "select * from %s where chrom = '%s' and chromStart = %d", table, seqName, start);
 else
     {
     struct hTableInfo *hti = hFindTableInfoWithConn(conn, seqName, tdb->table);
     if (hti && *hti->nameField && differentString("name", hti->nameField))
 	sqlSafef(query, sizeof query, "select * from %s where %s = '%s' and chrom = '%s' and chromStart = %d",
 	    table, hti->nameField, item, seqName, start);
     else
 	sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
 	    table, item, seqName, start);
     }
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     bed = bedLoadN(row+hasBin, bedSize);
     if ((tdb->type != NULL) && sameString(tdb->type, "interaction"))
 	{
 	interactionPrintPos( bed, bedSize, tdb);
         }
     else
         bedPrintPos(bed, bedSize, tdb);
 
     extraFieldsPrint(tdb,sr,row,sqlCountColumns(sr));
 
     // check for seq1 and seq2 in columns 7+8 (eg, pairedTagAlign)
     char *setting = trackDbSetting(tdb, BASE_COLOR_USE_SEQUENCE);
     if (bedSize == 6 && setting && sameString(setting, "seq1Seq2"))
 	printf("<br><B>Sequence 1:</B> %s<br><B>Sequence 2:</B> %s<br>\n",row[hasBin+6], row[hasBin+7]);
     printCompareGenomeLinks(tdb,bed->name);
     }
 sqlFreeResult(&sr);
 getBedTopScorers(conn, tdb, table, item, start, bedSize);
 printItemDetailsHtml(tdb, item);
 }
 
 #define INTRON 10
 #define CODINGA 11
 #define CODINGB 12
 #define UTR5 13
 #define UTR3 14
 #define STARTCODON 15
 #define STOPCODON 16
 #define SPLICESITE 17
 #define NONCONSPLICE 18
 #define INFRAMESTOP 19
 #define INTERGENIC 20
 #define REGULATORY 21
 #define LABEL 22
 
 #define RED 0xFF0000
 #define GREEN 0x00FF00
 #define LTGREEN 0x33FF33
 #define BLUE 0x0000FF
 #define MEDBLUE 0x6699FF
 #define PURPLE 0x9900cc
 #define BLACK 0x000000
 #define CYAN 0x00FFFF
 #define ORANGE 0xDD6600
 #define BROWN 0x663300
 #define YELLOW 0xFFFF00
 #define MAGENTA 0xFF00FF
 #define GRAY 0xcccccc
 #define LTGRAY 0x999999
 #define WHITE 0xFFFFFF
 
 int setAttributeColor(int class)
 {
 switch (class)
     {
     case STARTCODON:
 	return GREEN;
     case STOPCODON:
 	return RED;
     case CODINGA:
 	return MEDBLUE;
     case CODINGB:
 	return PURPLE;
     case UTR5:
     case UTR3:
 	return ORANGE;
     case INTRON:
 	return LTGRAY;
     case SPLICESITE:
     case NONCONSPLICE:
 	return BLACK;
     case INFRAMESTOP:
 	return MAGENTA;
     case REGULATORY:
 	return YELLOW;
     case INTERGENIC:
 	return GRAY;
     case LABEL:
     default:
 	return BLACK;
     }
 }
 
 void startColorStr(struct dyString *dy, int color, int track)
 {
 currentColor[track] = color;
 if (prevColor[track] != currentColor[track])
     dyStringPrintf(dy,"</span><span style='color:#%06X'>",color);
 }
 
 void stopColorStr(struct dyString *dy, int track)
 {
 prevColor[track] = currentColor[track];
 }
 
 void addTag(struct dyString *dy, struct dyString *tag)
 {
 dyStringPrintf(dy,"<A name=%s></a>",tag->string);
 }
 
 void setClassStr(struct dyString *dy, int class, int track)
 {
 if (class == STARTCODON)
     dyStringAppend(dy,"<A name=startcodon></a>");
 startColorStr(dy,setAttributeColor(class),track);
 }
 
 void resetClassStr(struct dyString *dy, int track)
 {
 stopColorStr(dy,track);
 }
 
 boolean isBlue(char *s)
 /* check for <a href name=class</a> to see if this is colored blue (coding region)*/
 {
     /* check for blue */
     if (strstr(s,"6699FF") == NULL)
         return FALSE;
     else
         return TRUE;
 }
 int numberOfGaps(char *q,int size)
 /* count number of gaps in a string array */
 {
 int i;
 int count = 0;
 for (i = 0 ; i<size ; i++)
     if (q[i] == '-') count++;
 return (count);
 }
 
 void pseudoGeneClick(struct sqlConnection *conn, struct trackDb *tdb,
                      char *item, int start, int bedSize)
 /* Handle click in track. */
 {
 char table[64];
 boolean hasBin;
 struct bed *bed;
 char query[512];
 struct sqlResult *sr;
 char **row;
 boolean firstTime = TRUE;
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 if (bedSize <= 3)
     sqlSafef(query, sizeof query, "select * from %s where chrom = '%s' and chromStart = %d", table, seqName, start);
 else
     sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
 	    table, item, seqName, start);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     bed = bedLoadN(row+hasBin, bedSize);
     bedPrintPos(bed, bedSize, tdb);
     }
 }
 
 void axtOneGeneOut(char *otherDb, struct axt *axtList, int lineSize,
                    FILE *f, struct genePred *gp, char *nibFile)
 /* Output axt and orf in pretty format. */
 {
 struct axt *axt;
 int oneSize;
 int i;
 int tCodonPos = 1;
 int qCodonPos = 1;
 int tStart;
 int tEnd;
 int nextStart= gp->exonStarts[0] ;
 int nextEnd = gp->exonEnds[0];
 int nextEndIndex = 0;
 int tCoding=FALSE;
 int qCoding=FALSE;
 int qStopCodon = FALSE;
 int tFlip=TRUE; /* flag to control target alternating colors for exons (blue and purple) */
 int qFlip=TRUE; /* flag to control query alternating colors for exons (blue and purple) */
 int qClass=INTERGENIC;
 int tClass=INTERGENIC;
 int prevTclass=INTERGENIC;
 int prevQclass=INTERGENIC;
 int posStrand;
 DNA qCodon[4];
 DNA tCodon[4];
 AA qProt, tProt = 0;
 int tPtr = 0;
 int prevEnd = 500000000;
 int intronTruncated=FALSE;
 
 if (gp->strand[0] == '+')
     {
     nextEndIndex = 0;
     nextStart = gp->exonStarts[nextEndIndex] ;
     nextEnd = gp->exonEnds[nextEndIndex];
     tStart =  gp->cdsStart ;
     tEnd = gp->cdsEnd-3  ;
     posStrand=TRUE;
     if (axtList != NULL)
         tPtr = axtList->tStart;
     }
 else if (gp->strand[0] == '-')
     {
     nextEndIndex = (gp->exonCount)-1;
     nextStart = (gp->exonEnds[nextEndIndex]);
     nextEnd = (gp->exonStarts[nextEndIndex]);
     tStart =  gp->cdsEnd ;
     tEnd = gp->cdsStart ;
     posStrand=FALSE;
     if (axtList != NULL)
         tPtr = axtList->tEnd;
     }
 else
     {
     errAbort("cannot determine start_codon position for %s on %s\n",gp->name,gp->chrom);
     exit(0);
     }
 
 /* safef(nibFile, sizeof(nibFile), "%s/%s.nib",nibDir,gp->chrom); */
 /* if no alignment , make a bad one */
 if (axtList == NULL)
     {
     if (gp->strand[0] == '+')
         axtList = createAxtGap(nibFile,gp->chrom,tStart,tEnd ,gp->strand[0]);
     else
         axtList = createAxtGap(nibFile,gp->chrom,tEnd,tStart ,gp->strand[0]);
     }
 /* append unaligned coding region to list */
 if (posStrand)
     {
     if ((axtList->tStart)-1 > tStart)
         {
         struct axt *axtGap = createAxtGap(nibFile,gp->chrom,tStart,axtList->tStart,gp->strand[0]);
         slAddHead(&axtList, axtGap);
         tPtr = axtList->tStart;
         }
     }
 else
     {
     if (axtList->tEnd < tStart)
         {
         struct axt *axtGap = createAxtGap(nibFile,gp->chrom,axtList->tEnd, tStart+1,gp->strand[0]);
         axtListReverse(&axtGap, database);
         slAddHead(&axtList, axtGap);
         tPtr = axtList->tEnd;
         }
     }
 
 for (axt = axtList; axt != NULL; axt = axt->next)
     {
     char *q = axt->qSym;
     char *t = axt->tSym;
     int size = axt->symCount;
     int sizeLeft = size;
     int qPtr ;
     char qStrand = (axt->qStrand == gp->strand[0] ? '+' : '-');
     int qStart = axt->qStart;
     int qEnd = axt->qEnd;
     int qSize = 0;
     if (!sameString(axt->qName, "gap"))
         qSize = hChromSize(otherDb, axt->qName);
     if (qStrand == '-')
         {
         qStart = qSize - axt->qEnd;
         qEnd = qSize - axt->qStart;
         }
 /*    fprintf(f, ">%s:%d-%d %s:%d-%d (%c) score %d coding %d-%d utr/coding %d-%d gene %c alignment %c\n",
  *          axt->tName, axt->tStart+1, axt->tEnd,
  *          axt->qName, qStart+1, qEnd, qStrand, axt->score,  tStart+1, tEnd, gp->txStart+1, gp->txEnd, gp->strand[0], axt->qStrand); */
 
     qPtr = qStart;
     if (gp->exonFrames == NULL)
         qCodonPos = tCodonPos; /* put translation back in sync */
     if (!posStrand)
         {
         qPtr = qEnd;
         /* skip to next exon if we are starting in the middle of a gene  - should not happen */
         while ((tPtr < nextEnd) && (nextEndIndex > 0))
             {
             nextEndIndex--;
             prevEnd = nextEnd;
             nextStart = (gp->exonEnds[nextEndIndex]);
             nextEnd = (gp->exonStarts[nextEndIndex]);
             if (nextStart > tStart)
                 tClass = INTRON;
             }
         }
     else
         {
         /* skip to next exon if we are starting in the middle of a gene  - should not happen */
         while ((tPtr > nextEnd) && (nextEndIndex < gp->exonCount-2))
             {
             nextEndIndex++;
             prevEnd = nextEnd;
             nextStart = gp->exonStarts[nextEndIndex];
             nextEnd = gp->exonEnds[nextEndIndex];
             if (nextStart > tStart)
                 tClass = INTRON;
             }
         }
     /* loop thru one base at a time */
     while (sizeLeft > 0)
         {
         struct dyString *dyT = newDyString(1024);
         struct dyString *dyQ = newDyString(1024);
         struct dyString *dyQprot = newDyString(1024);
         struct dyString *dyTprot = newDyString(1024);
         struct dyString *exonTag = newDyString(1024);
         oneSize = sizeLeft;
         if (oneSize > lineSize)
             oneSize = lineSize;
         setClassStr(dyT,tClass, 0);
         setClassStr(dyQ,qClass, 1);
 
         /* break up into linesize chunks */
         for (i=0; i<oneSize; ++i)
             {
             if (posStrand)
                 {/*look for start of exon on positive strand*/
                 if ((tClass==INTRON) && (tPtr >= nextStart) && (tPtr >= tStart) && (tPtr < tEnd))
                     {
                     tCoding=TRUE;
                     dyStringPrintf(exonTag, "exon%d",nextEndIndex+1);
                     addTag(dyT,exonTag);
                     if (gp->exonFrames != NULL && gp->exonFrames[nextEndIndex] != -1)
                         tCodonPos = gp->exonFrames[nextEndIndex]+1;
                     if (qStopCodon == FALSE)
                         {
                         qCoding=TRUE;
                         qCodonPos = tCodonPos; /* put translation back in sync */
                         qFlip = tFlip;
                         }
                     }
                 else if ((tPtr >= nextStart) && (tPtr < tStart))
                     { /* start of UTR 5'*/
                     tClass=UTR5; qClass=UTR5;
                     }
                 }
             else{
                 if ((tClass==INTRON) && (tPtr <= nextStart) && (tPtr <= tStart) && (tPtr > tEnd))
                     { /*look for start of exon on neg strand */
                     tCoding=TRUE;
                     dyStringPrintf(exonTag, "exon%d",nextEndIndex+1);
                     addTag(dyT,exonTag);
 
                     if (qStopCodon == FALSE)
                         {
                         qCoding=TRUE;
                         if (gp->exonFrames != NULL && gp->exonFrames[nextEndIndex] != -1)
                             tCodonPos = gp->exonFrames[nextEndIndex]+1;
                         qCodonPos = tCodonPos; /* put translation back in sync */
                         qFlip = tFlip;
                         }
                     }
                 else if ((tPtr <= nextStart-1) && (tPtr > tStart))
                     { /* start of UTR 5'*/
                     tClass=UTR5; qClass=UTR5;
                     }
 	    }
             /* toggle between blue / purple color for exons */
             if (tCoding && tFlip )
                 tClass=CODINGA;
             if (tCoding && (tFlip == FALSE) )
                 tClass=CODINGB;
             if (qCoding && qFlip && !qStopCodon)
                 qClass=CODINGA;
             if (qCoding && (qFlip == FALSE) && !qStopCodon)
                 qClass=CODINGB;
             if (posStrand)
                 {
                 /* look for end of exon */
                 if (tPtr == nextEnd)
                     {
                     tCoding=FALSE;
                     qCoding=FALSE;
                     tClass=INTRON;
                     qClass=INTRON;
                     nextEndIndex++;
                     nextStart = gp->exonStarts[nextEndIndex];
                     prevEnd = nextEnd;
                     nextEnd = gp->exonEnds[nextEndIndex];
                     if (gp->exonFrames != NULL && gp->exonFrames[nextEndIndex] != -1)
                         tCodonPos = gp->exonFrames[nextEndIndex]+1;
                     }
                 }
             else
                 {
                 /* look for end of exon  negative strand */
                 if (tPtr == nextEnd && tPtr != tEnd)
                     {
                     tCoding=FALSE;
                     qCoding=FALSE;
                     tClass=INTRON;
                     qClass=INTRON;
                     nextEndIndex--;
                     nextStart = (gp->exonEnds[nextEndIndex]);
                     prevEnd = nextEnd;
                     nextEnd = (gp->exonStarts[nextEndIndex]);
                     }
                 }
             if (posStrand)
                 {
                 /* look for start codon and color it green*/
                 if ((tPtr >= (tStart)) && (tPtr <=(tStart+2)))
                     {
                     if (gp->exonFrames != NULL && gp->cdsStartStat == cdsComplete)
                         {
                         tClass=STARTCODON;
                         qClass=STARTCODON;
                         }
                     else if(tClass != CODINGB)
                         {
                         tClass=CODINGA;
                         qClass=CODINGA;
                         }
                     tCoding=TRUE;
                     qCoding=TRUE;
                     if (tPtr == tStart)
                         {
                         if (gp->exonFrames != NULL && gp->exonFrames[nextEndIndex] != -1)
                             tCodonPos = gp->exonFrames[nextEndIndex]+1;
                         else
                             tCodonPos=1;
                         qCodonPos=tCodonPos;
                         }
                     }
                 /* look for stop codon and color it red */
                 if ((tPtr >= tEnd) && (tPtr <= (tEnd+2)))
                     {
                     if (gp->exonFrames != NULL && gp->cdsEndStat == cdsComplete)
                         {
                         tClass=STOPCODON;
                         qClass=STOPCODON;
                         }
                     tCoding=FALSE;
                     qCoding=FALSE;
                     }
                 }
             else
                 {
                 /* look for start codon and color it green negative strand case*/
                 if ((tPtr <= (tStart)) && (tPtr >=(tStart-2)))
                     {
                     if (gp->exonFrames != NULL && gp->cdsStartStat == cdsComplete)
                         {
                         tClass=STARTCODON;
                         qClass=STARTCODON;
                         }
                     else if (tClass!=CODINGB)
                         {
                         tClass=CODINGA;
                         qClass=CODINGA;
                         }
                     tCoding=TRUE;
                     qCoding=TRUE;
                     if (tPtr == tStart)
                         {
                         if (gp->exonFrames != NULL && gp->exonFrames[nextEndIndex] != -1)
                             tCodonPos = gp->exonFrames[nextEndIndex]+1;
                         else
                             tCodonPos=1;
                         }
                     qCodonPos=tCodonPos;
                     }
                 /* look for stop codon and color it red - negative strand*/
                 if ((tPtr <= tEnd+3) && (tPtr >= (tEnd+1)))
                     {
                     if (gp->exonFrames != NULL && gp->cdsEndStat == cdsComplete)
                         {
                         tClass=STOPCODON;
                         qClass=STOPCODON;
                         }
                     tCoding=FALSE;
                     qCoding=FALSE;
                     }
                 }
             if (posStrand)
                 {
                 /* look for 3' utr and color it orange */
                 if (tPtr == (tEnd +3) )
                     {
                     tClass = UTR3;
                     qClass = UTR3;
                     }
                 }
             else
                 {
                 /* look for 3' utr and color it orange negative strand case*/
                 if (tPtr == (tEnd) )
                     {
                     tClass = UTR3;
                     qClass = UTR3;
                     }
                 }
 
             if (qCoding && qCodonPos == 3)
                 {
                 /* look for in frame stop codon and color it magenta */
                 qCodon[qCodonPos-1] = q[i];
                 qCodon[3] = 0;
                 qProt = lookupCodon(qCodon);
                 if (qProt == 'X') qProt = ' ';
                 if (qProt == 0)
                     {
                     qProt = '*'; /* stop codon is * */
                     qClass = INFRAMESTOP;
                     }
                 }
 
             /* write html to change color for all above cases t strand */
             if (tClass != prevTclass)
                 {
                 setClassStr(dyT,tClass,0);
                 prevTclass = tClass;
                 }
             dyStringAppendC(dyT,t[i]);
             /* write html to change color for all above cases q strand */
             if (qClass != prevQclass)
                 {
                 setClassStr(dyQ,qClass,0);
                 prevQclass = qClass;
                 }
             dyStringAppendC(dyQ,q[i]);
             if (tCoding && tFlip && (tCodonPos == 3))
                 {
                 tFlip=FALSE;
                 }
             else if (tCoding && (tFlip == FALSE) && (tCodonPos == 3))
                 {
                 tFlip=TRUE;
                 }
             if (qCoding && qFlip && (qCodonPos == 3))
                 {
                 qFlip=FALSE;
                 }
             else if (qCoding && (qFlip == FALSE) && (qCodonPos == 3))
                 {
                 qFlip=TRUE;
                 }
             /* translate dna to protein and append html */
             if (tCoding && tCodonPos == 3)
                 {
                 tCodon[tCodonPos-1] = t[i];
                 tCodon[3] = 0;
                 tProt = lookupCodon(tCodon);
                 if (tProt == 'X') tProt = ' ';
                 if (tProt == 0) tProt = '*'; /* stop codon is * */
                 dyStringAppendC(dyTprot,tProt);
                 }
             else
                 {
                 dyStringAppendC(dyTprot,' ');
                 }
             if (qCoding && qCodonPos == 3)
                 {
                 qCodon[qCodonPos-1] = q[i];
                 qCodon[3] = 0;
                 qProt = lookupCodon(qCodon);
                 if (qProt == 'X') qProt = ' ';
                 if (qProt == 0)
                     {
                     qProt = '*'; /* stop codon is * */
                     /* qClass = INFRAMESTOP; */
                     qStopCodon = FALSE;
                     qCoding = TRUE;
                     }
                 if (tProt == qProt) qProt = '|'; /* if the AA matches  print | */
                 dyStringAppendC(dyQprot,qProt);
                 }
             else
                 {
                 dyStringAppendC(dyQprot,' ');
                 }
             /* move to next base and update reading frame */
             if (t[i] != '-')
                 {
                 if (posStrand)
                     {
                     tPtr++;
                     qPtr++;
                     }
                 else
                     {
                     tPtr--;
                     qPtr--;
                     }
                 if (tCoding)
                     {
                     tCodon[tCodonPos-1] = t[i];
                     tCodonPos++;
                     }
                 if (tCodonPos>3) tCodonPos=1;
                 }
             /*else
 	      {
 	      tClass=INTRON;
 	      }*/
             /* update reading frame on other species */
             if (q[i] != '-')
                 {
                 if (qCoding)
                     {
                     qCodon[qCodonPos-1] = q[i];
                     qCodonPos++;
                     }
                 if (qCodonPos>3) qCodonPos=1;
                 }
             /*else
 	      {
 	      qClass=INTRON;
 	      }*/
             }
         /* write labels in black */
         resetClassStr(dyT,0);
         setClassStr(dyT,LABEL,0);
         if (posStrand)
             {
             dyStringPrintf(dyT, " %d ",tPtr);
             if (tCoding)
                 dyStringPrintf(dyT, "exon %d",(nextEndIndex == 0) ? 1 : nextEndIndex+1);
             }
         else
             {
             dyStringPrintf(dyT, " %d ",tPtr+1);
             if (tCoding)
                 dyStringPrintf(dyT, "exon %d", (nextEndIndex == 0) ? 1 : nextEndIndex+1);
             }
 #if 0 /* debug version */
         if (posStrand)
             dyStringPrintf(dyT, " %d thisExon=%d-%d xon %d",tPtr, gp->exonStarts[(nextEndIndex == 0) ? 0 : nextEndIndex - 1]+1, gp->exonEnds[(nextEndIndex == 0) ? 0 : nextEndIndex - 1],(nextEndIndex == 0) ? 1 : nextEndIndex);
         else
             dyStringPrintf(dyT, " %d thisExon=%d-%d xon %d",tPtr, gp->exonStarts[(nextEndIndex == gp->exonCount) ? gp->exonCount : nextEndIndex ]+1, gp->exonEnds[(nextEndIndex == gp->exonCount) ? gp->exonCount : nextEndIndex ],(nextEndIndex == 0) ? 1 : nextEndIndex);
 #endif
         dyStringAppendC(dyT,'\n');
         resetClassStr(dyT,0);
         resetClassStr(dyQ,1);
         setClassStr(dyQ,LABEL,1);
         if (posStrand)
             dyStringPrintf(dyQ, " %d ",qPtr);
         else
             dyStringPrintf(dyQ, " %d ",qPtr);
 
         dyStringAppendC(dyQ,'\n');
         resetClassStr(dyQ,1);
         dyStringAppendC(dyQprot,'\n');
         dyStringAppendC(dyTprot,'\n');
 
 #if 0 /* debug version */
         if (posStrand)
             printf(" %d nextExon=%d-%d xon %d t %d prevEnd %d diffs %d %d<br>",qPtr, nextStart+1,nextEnd,nextEndIndex+1, tPtr,prevEnd, tPtr-nextStart-70, tPtr-(prevEnd+70));
         else
             printf(" %d nextExon=%d-%d xon %d t %d prevEnd %d diffs %d %d<br>",qPtr, nextStart+1,nextEnd,nextEndIndex, tPtr, prevEnd, tPtr-nextStart-70, tPtr-(prevEnd+70));
 #endif
 
         /* write out alignment, unless we are deep inside an intron */
         if (tClass != INTRON || (tClass == INTRON && tPtr < nextStart-LINESIZE && tPtr< (prevEnd + posStrand ? LINESIZE : -LINESIZE)))
             {
             intronTruncated = 0;
             fputs(dyTprot->string,f);
             fputs(dyT->string,f);
 
             for (i=0; i<oneSize; ++i)
                 {
                 if (toupper(q[i]) == toupper(t[i]) && isalpha(q[i]))
                     fputc('|', f);
                 else
                     fputc(' ', f);
                 }
             fputc('\n', f);
 
             fputs(dyQ->string,f);
             fputs(dyQprot->string,f);
             fputc('\n', f);
             }
         else
             {
             if (!intronTruncated == TRUE)
                 {
                 printf("...intron truncated...<br>");
                 intronTruncated = TRUE;
                 }
             }
         /* look for end of line */
         if (oneSize > lineSize)
             oneSize = lineSize;
         sizeLeft -= oneSize;
         q += oneSize;
         t += oneSize;
         freeDyString(&dyT);
         freeDyString(&dyQ);
         freeDyString(&dyQprot);
         freeDyString(&dyTprot);
         }
     }
 }
 
 struct axt *getAxtListForGene(struct genePred *gp, char *nib, char *fromDb, char *toDb,
 		       struct lineFile *lf)
 /* get all axts for a gene */
 {
 struct axt *axt, *axtGap;
 struct axt *axtList = NULL;
 int prevEnd = gp->txStart;
 // int prevStart = gp->txEnd;  unused variable
 int tmp;
 
 while ((axt = axtRead(lf)) != NULL)
     {
     if (sameString(gp->chrom, axt->tName)
     &&  (  (  (axt->tStart <= gp->cdsStart && axt->tEnd >= gp->cdsStart)
            || (axt->tStart <= gp->cdsEnd   && axt->tEnd >= gp->cdsEnd  ) )
         || (   axt->tStart <  gp->cdsEnd   && axt->tEnd >  gp->cdsStart  ) ) )
         {
         if (gp->strand[0] == '-')
             {
             reverseComplement(axt->qSym, axt->symCount);
             reverseComplement(axt->tSym, axt->symCount);
             tmp = hChromSize(fromDb, axt->qName) - axt->qStart;
             axt->qStart = hChromSize(fromDb, axt->qName) - axt->qEnd;
             axt->qEnd = tmp;
             if (prevEnd < (axt->tStart)-1)
                 {
                 axtGap = createAxtGap(nib,gp->chrom,prevEnd,(axt->tStart),gp->strand[0]);
                 reverseComplement(axtGap->qSym, axtGap->symCount);
                 reverseComplement(axtGap->tSym, axtGap->symCount);
                 slAddHead(&axtList, axtGap);
                 }
             }
         else if (prevEnd < (axt->tStart))
             {
             axtGap = createAxtGap(nib,gp->chrom,prevEnd,(axt->tStart),gp->strand[0]);
             slAddHead(&axtList, axtGap);
             }
         slAddHead(&axtList, axt);
         prevEnd = axt->tEnd;
         // prevStart = axt->tStart;  unused variable
         }
     if (sameString(gp->chrom, axt->tName) && (axt->tStart > gp->txEnd))
         {
         if ((prevEnd < axt->tStart) && prevEnd < min(gp->txEnd, axt->tStart))
             {
             axtGap = createAxtGap(nib,gp->chrom,prevEnd,min(axt->tStart,gp->txEnd),gp->strand[0]);
             if (gp->strand[0] == '-')
                 {
                 reverseComplement(axtGap->qSym, axtGap->symCount);
                 reverseComplement(axtGap->tSym, axtGap->symCount);
                 }
             slAddHead(&axtList, axtGap);
             }
         else
             if (axtList == NULL)
                 {
                 axtGap = createAxtGap(nib,gp->chrom,prevEnd,gp->txEnd,gp->strand[0]);
                 if (gp->strand[0] == '-')
                     {
                     reverseComplement(axtGap->qSym, axtGap->symCount);
                     reverseComplement(axtGap->tSym, axtGap->symCount);
                     }
                 slAddHead(&axtList, axtGap);
                 }
         break;
         }
     }
 if (gp->strand[0] == '+')
     slReverse(&axtList);
 return axtList ;
 }
 
 struct axt *getAxtListForRange(struct genePred *gp, char *nib, char *fromDb, char *toDb,
 		       char *alignment, char *qChrom, int qStart, int qEnd)
 /* get all axts for a chain */
 {
 struct lineFile *lf ;
 struct axt *axt, *axtGap;
 struct axt *axtList = NULL;
 int prevEnd = gp->txStart;
 // int prevStart = gp->txEnd;  unused variable
 int tmp;
 
 lf = lineFileOpen(getAxtFileName(gp->chrom, toDb, alignment, fromDb), TRUE);
 printf("file %s\n",lf->fileName);
 while ((axt = axtRead(lf)) != NULL)
     {
 /*    if (sameString(gp->chrom , axt->tName))
  *       printf("axt %s qstart %d axt tStart %d\n",axt->qName, axt->qStart,axt->tStart); */
     if ( sameString(gp->chrom, axt->tName)
     &&   sameString(   qChrom, axt->qName)
     &&   positiveRangeIntersection(     qStart,      qEnd, axt->qStart, axt->qEnd)
     &&   positiveRangeIntersection(gp->txStart, gp->txEnd, axt->tStart, axt->tEnd) )
         {
         if (gp->strand[0] == '-')
             {
             reverseComplement(axt->qSym, axt->symCount);
             reverseComplement(axt->tSym, axt->symCount);
             tmp = hChromSize(fromDb, axt->qName) - axt->qStart;
             axt->qStart = hChromSize(fromDb, axt->qName) - axt->qEnd;
             axt->qEnd = tmp;
             if (prevEnd < (axt->tStart)-1)
                 {
                 axtGap = createAxtGap(nib,gp->chrom,prevEnd,(axt->tStart)-1,gp->strand[0]);
                 reverseComplement(axtGap->qSym, axtGap->symCount);
                 reverseComplement(axtGap->tSym, axtGap->symCount);
                 slAddHead(&axtList, axtGap);
                 }
             }
         else if (prevEnd < (axt->tStart)-1)
             {
             axtGap = createAxtGap(nib,gp->chrom,prevEnd,(axt->tStart)-1,gp->strand[0]);
             slAddHead(&axtList, axtGap);
             }
         slAddHead(&axtList, axt);
         prevEnd = axt->tEnd;
         // prevStart = axt->tStart;  unused variable
         }
     if (sameString(gp->chrom, axt->tName) && (axt->tStart > gp->txEnd+20000))
         {
         if (axt->tStart > prevEnd)
             {
             axtGap = createAxtGap(nib,gp->chrom,prevEnd+1,(axt->tStart)-1,gp->strand[0]);
             if (gp->strand[0] == '-')
                 {
                 reverseComplement(axtGap->qSym, axtGap->symCount);
                 reverseComplement(axtGap->tSym, axtGap->symCount);
                 }
             slAddHead(&axtList, axtGap);
             }
         break;
         }
     }
 if (gp->strand[0] == '+')
     slReverse(&axtList);
 return axtList ;
 }
 
 void printCdsStatus(enum cdsStatus cdsStatus)
 /* print a description of a genePred cds status */
 {
 switch (cdsStatus)
     {
     case cdsNone:        /* "none" - No CDS (non-coding)  */
         printf("none (non-coding)<br>\n");
         break;
     case cdsUnknown:     /* "unk" - CDS is unknown (coding, but not known)  */
         printf("unknown (coding, but not known)<br>\n");
         break;
     case cdsIncomplete:  /* "incmpl" - CDS is not complete at this end  */
         printf("<em>not</em> complete<br>\n");
         break;
     case cdsComplete:    /* "cmpl" - CDS is complete at this end  */
         printf("complete<br>\n");
         break;
     }
 }
 
 void showGenePos(char *name, struct trackDb *tdb)
 /* Show gene prediction position and other info. */
 {
 char *rootTable = tdb->table;
 char query[512];
 struct sqlConnection *conn = hAllocConn(database);
 struct genePred *gpList = NULL, *gp = NULL;
 boolean hasBin;
 char table[64];
 struct sqlResult *sr = NULL;
 char **row = NULL;
 char *classTable = trackDbSetting(tdb, GENEPRED_CLASS_TBL);
 
 
 hFindSplitTable(database, seqName, rootTable, table, &hasBin);
 sqlSafefFrag(query, sizeof(query), "name = \"%s\"", name);
 gpList = genePredReaderLoadQuery(conn, table, query);
 for (gp = gpList; gp != NULL; gp = gp->next)
     {
     printPos(gp->chrom, gp->txStart, gp->txEnd, gp->strand, FALSE, NULL);
     if(sameString(tdb->type,"genePred")
     && startsWith("ENCODE Gencode",tdb->longLabel)
     && startsWith("ENST",name))
         {
         char *ensemblIdUrl = trackDbSetting(tdb, "ensemblIdUrl");
 
         printf("<b>Ensembl Transcript Id:&nbsp</b>");
         if (ensemblIdUrl != NULL)
             printf("<a href=\"%s%s\" target=\"_blank\">%s</a><br>", ensemblIdUrl,name,name);
         else
             printf("%s<br>",name);
         }
     if (gp->name2 != NULL && strlen(trimSpaces(gp->name2))> 0)
         {
         /* in Ensembl gene info downloaded from ftp site, sometimes the
            name2 field is populated with "noXref" because there is
            no alternate name. Replace this with "none" */
         printf("<b>Gene Symbol:");
         if ((strlen(gp->name2) < 1) || (sameString(gp->name2, "noXref")))
            printf("</b> none<br>\n");
         else
            printf("</b> %s<br>\n",gp->name2);
         }
     char *ensemblSource = NULL;
     if (sameString("ensGene", table))
 	{
 	if (hTableExists(database, "ensemblSource"))
 	    {
 	    sqlSafef(query, sizeof(query),
 		"select source from ensemblSource where name='%s'", name);
 	    ensemblSource = sqlQuickString(conn, query);
 	    }
 	}
     if ((gp->exonFrames != NULL) && (!genbankIsRefSeqNonCodingMRnaAcc(gp->name)))
 	{
 	if (ensemblSource && differentString("protein_coding",ensemblSource))
 	    {
 	    printf("<b>CDS Start: </b> none (non-coding)<BR>\n");
 	    printf("<b>CDS End: </b> none (non-coding)<BR>\n");
 	    }
 	else
 	    {
 	    printf("<b>CDS Start: </b>");
 	    printCdsStatus((gp->strand[0] == '+') ? gp->cdsStartStat : gp->cdsEndStat);
 	    printf("<b>CDS End: </b>");
 	    printCdsStatus((gp->strand[0] == '+') ? gp->cdsEndStat : gp->cdsStartStat);
 	    }
 	}
     /* if a gene class table exists, get gene class and print */
     if (classTable != NULL)
         {
         if (hTableExists(database, classTable))
            {
            sqlSafef(query, sizeof(query),
                 "select class from %s where name = \"%s\"", classTable, name);
            sr = sqlGetResult(conn, query);
            /* print class */
            if ((row = sqlNextRow(sr)) != NULL)
               printf("<b>Prediction Class:</b> %s<br>\n", row[0]);
            sqlFreeResult(&sr);
            if (sqlFieldIndex(conn, classTable, "level") > 0 )
                {
                sqlSafef(query, sizeof(query),
                     "select level from %s where name = \"%s\"", classTable, name);
                sr = sqlGetResult(conn, query);
                if ((row = sqlNextRow(sr)) != NULL)
                   printf("<b>Level:&nbsp</b> %s<br>\n", row[0]);
                sqlFreeResult(&sr);
                }
            if (sqlFieldIndex(conn, classTable, "transcriptType") > 0 )
                {
                sqlSafef(query, sizeof(query),
                     "select transcriptType from %s where name = \"%s\"", classTable, name);
                sr = sqlGetResult(conn, query);
                if ((row = sqlNextRow(sr)) != NULL)
                   printf("<b>Transcript type:&nbsp</b> %s<br>\n", row[0]);
                sqlFreeResult(&sr);
                }
            if (sqlFieldIndex(conn, classTable, "geneDesc") > 0 )
                {
                sqlSafef(query, sizeof(query),
                     "select geneDesc from %s where name = \"%s\"", classTable, name);
                sr = sqlGetResult(conn, query);
                if ((row = sqlNextRow(sr)) != NULL)
                   if (differentString("NULL",row[0]))
                       printf("<b>Gene Description :</b> %s<br>\n", row[0]);
                sqlFreeResult(&sr);
                }
            if (sqlFieldIndex(conn, classTable, "type") > 0 )
                {
                sqlSafef(query, sizeof(query),
                     "select type from %s where name = \"%s\"", classTable, name);
                sr = sqlGetResult(conn, query);
                if ((row = sqlNextRow(sr)) != NULL)
                   if (differentString("NULL",row[0]))
                       printf("<b>Gene Type :</b> %s<br>\n", row[0]);
                }
            }
         }
     if (gp->next != NULL)
         printf("<br>");
     }
 genePredFreeList(&gpList);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void showGenePosMouse(char *name, struct trackDb *tdb,
                       struct sqlConnection *connMm)
 /* Show gene prediction position and other info. */
 {
 char query[512];
 char *rootTable = tdb->table;
 struct sqlResult *sr;
 char **row;
 struct genePred *gp = NULL;
 boolean hasBin;
 int posCount = 0;
 char table[64] ;
 
 hFindSplitTable(database, seqName, rootTable, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s'", table, name);
 sr = sqlGetResult(connMm, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (posCount > 0)
         printf("<BR>\n");
     ++posCount;
     gp = genePredLoad(row + hasBin);
     printPos(gp->chrom, gp->txStart, gp->txEnd, gp->strand, FALSE, NULL);
     genePredFree(&gp);
     }
 sqlFreeResult(&sr);
 }
 
 void linkToPal(char *track,  char *chrom, int start, int end, char *geneName)
 /* Make anchor tag to open pal window */
 {
 printf("<A TITLE=\"%s\" HREF=\"%s?g=%s&i=%s&c=%s&l=%d&r=%d\">",
        geneName, hgPalName(), track, geneName, chrom, start, end);
     printf("CDS FASTA alignment</A> from multiple alignment");
 }
 
 void addPalLink(struct sqlConnection *conn, char *track,
     char *chrom, int start, int end, char *geneName)
 {
 struct slName *list = hTrackTablesOfType(conn, "wigMaf%%");
 
 if (list != NULL)
     {
     puts("<LI>\n");
     linkToPal( track, chrom, start, end, geneName);
     puts("</LI>\n");
     }
 }
 
 void geneShowPosAndLinksPal(char *geneName, char *pepName, struct trackDb *tdb,
                          char *pepTable, char *pepClick,
                          char *mrnaClick, char *genomicClick, char *mrnaDescription,
                          struct palInfo *palInfo)
 /* Show parts of gene common to everything. If pepTable is not null,
  * it's the old table name, but will check gbSeq first. */
 {
 char *geneTable = tdb->table;
 boolean foundPep = FALSE;
 
 showGenePos(geneName, tdb);
 
 if (startsWith("ENCODE Gencode",tdb->longLabel))
     {
     char *yaleTable = trackDbSetting(tdb, "yalePseudoAssoc");
 
     if ((yaleTable != NULL) && (hTableExists(database, yaleTable)))
         {
         struct sqlConnection *conn = hAllocConn(database);
         char query[512];
         sqlSafef(query, sizeof(query),
             "select * from %s where transcript = '%s'", yaleTable, geneName);
         char buffer[512];
         struct sqlResult *sr = sqlGetResult(conn, query);
         char *yaleUrl = trackDbSetting(tdb, "yaleUrl");
         char **row;
         while ((row = sqlNextRow(sr)) != NULL)
             {
             struct yaleGencodeAssoc *ya = yaleGencodeAssocLoad(row);
             safef(buffer, sizeof buffer, "%s/%s",yaleUrl,ya->yaleId);
             printf("<B>Yale pseudogene:</B> <a href=\"%s\" target=\"_blank\">%s</a><br>\n", buffer, ya->yaleId);
 
             }
         sqlFreeResult(&sr);
         hFreeConn(&conn);
         }
     }
 
 printf("<H3>Links to sequence:</H3>\n");
 printf("<UL>\n");
 
 if ((pepTable != NULL) && hGenBankHaveSeq(database, pepName, pepTable))
     {
     puts("<LI>\n");
     hgcAnchorSomewhere(pepClick, pepName, pepTable, seqName);
     printf("Predicted Protein</A> \n");
     puts("</LI>\n");
     foundPep = TRUE;
     }
 if (!foundPep)
     {
     char *autoTranslate = trackDbSetting(tdb, "autoTranslate");
     if (autoTranslate == NULL || differentString(autoTranslate, "0"))
         {
         puts("<LI>\n");
         /* put out correct message to describe translated mRNA */
         if ( sameString(geneTable, "ensGene")
         ||   sameString(geneTable, "ws245Genes")
         ||   sameString(geneTable, "vegaGene")
         ||   sameString(geneTable, "vegaPseudoGene")
         ||   genbankIsRefSeqNonCodingMRnaAcc(geneName)
         ||   sameString(geneTable, "lincRNAsTranscripts") )
             {
             printf("Non-protein coding gene or gene fragment, no protein prediction available.");
             }
         else
             {
             hgcAnchorSomewhere("htcTranslatedPredMRna", geneName, "translate", seqName);
             printf("Translated Protein</A> from ");
             if (sameString(geneTable, "refGene") )
                 {
 		printf("genomic DNA\n");
                 }
 	    else
 		{
                 printf("predicted mRNA \n");
 		}
 	    foundPep = TRUE;
 	    }
 	puts("</LI>\n");
 	}
     }
 
 puts("<LI>\n");
 hgcAnchorSomewhere(mrnaClick, geneName, geneTable, seqName);
 /* hack to put out a correct message describing the mRNA */
 if (sameString(mrnaClick, "htcGeneMrna"))
     printf("%s</A> from genomic sequences\n", mrnaDescription);
 else
     printf("%s</A> (may be different from the genomic sequence)\n",
            mrnaDescription);
 puts("</LI>\n");
 
 puts("<LI>\n");
 hgcAnchorSomewhere(genomicClick, geneName, geneTable, seqName);
 printf("Genomic Sequence</A> from assembly\n");
 puts("</LI>\n");
 
 if (palInfo)
     {
     struct sqlConnection *conn = hAllocConn(database);
     addPalLink(conn, tdb->track,  palInfo->chrom, palInfo->left,
         palInfo->right, palInfo->rnaName);
     hFreeConn(&conn);
     }
 
 printf("</UL>\n");
 }
 
 void geneShowPosAndLinks(char *geneName, char *pepName, struct trackDb *tdb,
 			 char *pepTable, char *pepClick,
 			 char *mrnaClick, char *genomicClick, char *mrnaDescription)
 {
 geneShowPosAndLinksPal(geneName, pepName, tdb,
 			 pepTable, pepClick,
 			 mrnaClick, genomicClick, mrnaDescription, NULL);
 }
 
 void geneShowPosAndLinksDNARefseq(char *geneName, char *pepName, struct trackDb *tdb,
 				  char *pepTable, char *pepClick,
 				  char *mrnaClick, char *genomicClick, char *mrnaDescription)
 /* Show parts of a DNA based RefSeq gene */
 {
 char *geneTable = tdb->table;
 
 showGenePos(geneName, tdb);
 printf("<H3>Links to sequence:</H3>\n");
 printf("<UL>\n");
 puts("<LI>\n");
 hgcAnchorSomewhere(genomicClick, geneName, geneTable, seqName);
 printf("Genomic Sequence</A> from assembly\n");
 puts("</LI>\n");
 printf("</UL>\n");
 }
 
 void geneShowPosAndLinksMouse(char *geneName, char *pepName,
                               struct trackDb *tdb, char *pepTable,
                               struct sqlConnection *connMm, char *pepClick,
 			      char *mrnaClick, char *genomicClick, char *mrnaDescription)
 /* Show parts of gene common to everything */
 {
 char *geneTrack = tdb->track;
 
 showGenePosMouse(geneName, tdb, connMm);
 printf("<H3>Links to sequence:</H3>\n");
 printf("<UL>\n");
 if (pepTable != NULL && hTableExists(database, pepTable))
     {
     hgcAnchorSomewhereDb(pepClick, pepName, pepTable, seqName, mousedb);
     printf("<LI>Translated Protein</A> \n");
     }
 hgcAnchorSomewhereDb(mrnaClick, geneName, geneTrack, seqName, mousedb);
 printf("<LI>%s</A>\n", mrnaDescription);
 hgcAnchorSomewhereDb(genomicClick, geneName, geneTrack, seqName, mousedb);
 printf("<LI>Genomic Sequence</A> DNA sequence from assembly\n");
 printf("</UL>\n");
 }
 
 void geneShowCommon(char *geneName, struct trackDb *tdb, char *pepTable)
 /* Show parts of gene common to everything */
 {
 geneShowPosAndLinks(geneName, geneName, tdb, pepTable, "htcTranslatedProtein",
 		    "htcGeneMrna", "htcGeneInGenome", "Predicted mRNA");
 char *txInfo = trackDbSetting(tdb, "txInfo");
 if (txInfo != NULL)
     showTxInfo(geneName, tdb, txInfo);
 char *cdsEvidence = trackDbSetting(tdb, "cdsEvidence");
 if (cdsEvidence != NULL)
     showCdsEvidence(geneName, tdb, cdsEvidence);
 }
 
 void geneShowMouse(char *geneName, struct trackDb *tdb, char *pepTable,
 		   struct sqlConnection *connMm)
 /* Show parts of gene common to everything */
 {
 geneShowPosAndLinksMouse(geneName, geneName, tdb, pepTable, connMm,
                          "htcTranslatedProtein", "htcGeneMrna", "htcGeneInGenome",
                          "Predicted mRNA");
 }
 
 void genericGenePredClick(struct sqlConnection *conn, struct trackDb *tdb,
                           char *item, int start, char *pepTable, char *mrnaTable)
 /* Handle click in generic genePred track. */
 {
 char *oldToNew = trackDbSetting(tdb, "oldToNew");
 if (oldToNew != NULL && sqlTableExists(conn, oldToNew))
     {
     char query[512];
     sqlSafef(query, sizeof(query),
         "select * from %s where oldId = '%s' and oldChrom='%s' and oldStart=%d",
             oldToNew, item, seqName, start);
     struct sqlResult *sr = sqlGetResult(conn, query);
     char **row;
     while ((row = sqlNextRow(sr)) != NULL)
         {
 	struct kg1ToKg2 *x = kg1ToKg2Load(row);
 	printf("<B>Old ID:</B> %s<BR>\n", x->oldId);
 	printf("<B>New ID:</B> %s<BR>\n", naForEmpty(x->newId));
 	printf("<B>Old/New Mapping:</B> %s<BR>\n", x->status);
 	if (x->note[0] != 0)
 	    printf("<B>Notes:</B> %s<BR>\n", x->note);
 	printf("<BR>\n");
 	}
     sqlFreeResult(&sr);
     }
 geneShowCommon(item, tdb, pepTable);
 printItemDetailsHtml(tdb, item);
 }
 
 void pslDumpHtml(struct psl *pslList)
 /* print out psl header and data */
 {
 struct psl* psl;
 printf("<PRE><TT>\n");
 printf("#match\tmisMatches\trepMatches\tnCount\tqNumInsert\tqBaseInsert\ttNumInsert\tBaseInsert\tstrand\tqName\tqSize\tqStart\tqEnd\ttName\ttSize\ttStart\ttEnd\tblockCount\tblockSizes\tqStarts\ttStarts\n");
 for (psl = pslList; psl != NULL; psl = psl->next)
     {
     pslTabOut(psl, stdout);
     }
 printf("</TT></PRE>\n");
 }
 
 void genericBigPslClick(struct sqlConnection *conn, struct trackDb *tdb,
                      char *item, int start, int end)
 /* Handle click in big psl track. */
 {
 struct psl* pslList = NULL;
 char *fileName = bbiNameFromSettingOrTable(tdb, conn, tdb->table);
 struct bbiFile *bbi = bigBedFileOpen(fileName);
 struct lm *lm = lmInit(0);
 int ivStart = start, ivEnd = end;
 if (start == end)
     {  
     // item is an insertion; expand the search range from 0 bases to 2 so we catch it:
     ivStart = max(0, start-1);
     ivEnd++;
     }  
 
 boolean showAll = trackDbSettingOn(tdb, "showAll");
 unsigned seqTypeField =  bbExtraFieldIndex(bbi, "seqType");
 struct bigBedInterval *bb, *bbList;
 
 // If showAll is on, show all alignments with this qName, not just the
 // selected one.
 if (showAll)
     {
     int fieldIx;
     struct bptFile *bpt = bigBedOpenExtraIndex(bbi, "name", &fieldIx);
     struct lm *lm = lmInit(0);
     bbList = bigBedNameQuery(bbi, bpt, fieldIx, item, lm);
     }
 else
     bbList = bigBedIntervalQuery(bbi, seqName, ivStart, ivEnd, 0, lm);
 
 
 char *bedRow[32];
 char startBuf[16], endBuf[16];
 
 int lastChromId = -1;
 char chromName[bbi->chromBpt->keySize+1];
 
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     bbiCachedChromLookup(bbi, bb->chromId, lastChromId, chromName, sizeof(chromName));
 
     lastChromId=bb->chromId;
     bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, 4);
     if (sameString(bedRow[3], item))
 	{
         struct psl *psl= pslFromBigPsl(chromName, bb, seqTypeField, NULL, NULL);
         slAddHead(&pslList, psl);
 	}
     }
 
 printf("<H3>%s/Genomic Alignments</H3>", item);
 if (pslIsProtein(pslList))
     printAlignmentsSimple(pslList, start, "htcBigPslAli", tdb->table, item);
 else
     printAlignmentsExtra(pslList, start, "htcBigPslAli", "htcBigPslAliInWindow",
         tdb->table, item);
 pslFreeList(&pslList);
 printItemDetailsHtml(tdb, item);
 }
 
 void genericPslClick(struct sqlConnection *conn, struct trackDb *tdb,
                      char *item, int start, char *subType)
 /* Handle click in generic psl track. */
 {
 struct psl* pslList = getAlignments(conn, tdb->table, item);
 
 /* check if there is an alignment available for this sequence.  This checks
  * both genbank sequences and other sequences in the seq table.  If so,
  * set it up so they can click through to the alignment. */
 if (hGenBankHaveSeq(database, item, NULL))
     {
     printf("<H3>%s/Genomic Alignments</H3>", item);
     if (sameString("protein", subType))
         printAlignments(pslList, start, "htcProteinAli", tdb->table, item);
     else
         printAlignments(pslList, start, "htcCdnaAli", tdb->table, item);
     }
 else
     {
     /* just dump the psls */
     pslDumpHtml(pslList);
     }
 pslFreeList(&pslList);
 printItemDetailsHtml(tdb, item);
 }
 
 
 static char *getParentTableName(struct trackDb *tdb)
 /* Get the track table or composite track parent table if applicable. */
 {
 tdb = trackDbTopLevelSelfOrParent(tdb);
 return tdb->table;
 }
 
 static char *getParentTrackName(struct trackDb *tdb)
 /* Get the track name or composite track parent name if applicable. */
 {
 tdb = trackDbTopLevelSelfOrParent(tdb);
 return tdb->track;
 }
 
 
 void printTBSchemaLink(struct trackDb *tdb)
 /* Make link to TB schema -- unless this is an on-the-fly (tableless) track. */
 {
 if (hTableOrSplitExists(database, tdb->table))
     {
     char *trackTable = getParentTableName(tdb);
     printf("<P><A HREF=\"../cgi-bin/hgTables?db=%s&hgta_group=%s&hgta_track=%s"
 	   "&hgta_table=%s&position=%s:%d-%d&"
            "hgta_doSchema=describe+table+schema\" target=ucscSchema title='Open schema in new window'>"
 	   "View table schema</A></P>\n",
 	   database, tdb->grp, trackTable, tdb->table,
 	   seqName, winStart+1, winEnd);
     }
 }
 
 void printTrackUiLink(struct trackDb *tdb)
 /* Make link to hgTrackUi. */
 {
 char *trackName = getParentTrackName(tdb);
 struct trackDb *parentTdb = tdb;
 if (!sameString(trackName, tdb->track))
     parentTdb = hTrackDbForTrack(database, trackName);
 printf("<P><A HREF=\"%s?g=%s&%s\">"
        "Go to %s track controls</A></P>\n",
        hTrackUiForTrack(tdb->track), trackName, cartSidUrlString(cart), parentTdb->shortLabel);
 }
 
 static void printDataVersion(struct trackDb *tdb)
 /* If this annotation has a dataVersion trackDb setting, print it */
 {
 if (trackHubDatabase(database))
     return;
 metadataForTable(database,tdb,NULL);
 const char *version = metadataFindValue(tdb,"dataVersion");
 if(version == NULL)
     version = trackDbSetting(tdb,"dataVersion");
 if (version != NULL)
     printf("<B>Data version:</B> %s <BR>\n", version);
 }
 
 void printDataRestrictionDate(struct trackDb *tdb)
 /* If this annotation has a dateUnrestricted trackDb setting, print it */
 {
 char *restrictionDate = encodeRestrictionDateDisplay(database,tdb);
 if (restrictionDate != NULL)
     {
     printf("<A HREF=\"/ENCODE/terms.html\" TARGET=_BLANK><B>Restricted until</A>:</B> %s <BR>\n",
                 restrictionDate);
     freeMem(restrictionDate);
     }
 }
 
 static void printOrigAssembly(struct trackDb *tdb)
 /* If this annotation has been lifted, print the original
  * freeze, as indicated by the "origAssembly" trackDb setting */
 {
 trackDbPrintOrigAssembly(tdb, database);
 }
 
 static char *getHtmlFromSelfOrParent(struct trackDb *tdb)
 /* Get html from self or from parent if not in self. */
 {
 for (;tdb != NULL; tdb = tdb->parent)
     {
     if (tdb->html != NULL && tdb->html[0] != 0)
         return tdb->html;
     }
 return NULL;
 }
 
 void printTrackHtml(struct trackDb *tdb)
 /* If there's some html associated with track print it out. Also print
  * last update time for data table and make a link
  * to the TB table schema page for this table. */
 {
 if (!isCustomTrack(tdb->track))
     {
     extraUiLinks(database,tdb);
     printTrackUiLink(tdb);
     struct trackVersion *trackVersion = getTrackVersion(database, tdb->track);
     if(trackVersion == NULL)
         printDataVersion(tdb);
     else
         printf("<B>Data version:</B> %s <BR>\n", trackVersion->version);
     printOrigAssembly(tdb);
     printUpdateTime(database, tdb, NULL);
     printDataRestrictionDate(tdb);
     }
 char *html = getHtmlFromSelfOrParent(tdb);
 if (html != NULL && html[0] != 0)
     {
     htmlHorizontalLine();
 
     // Add pennantIcon
     printPennantIconNote(tdb);
 
     // Wrap description html in div with limited width, so when the page is very wide
     // due to long details, the user doesn't have to scroll right to read the description.
     puts("<div class='readableWidth'>");
     puts(html);
     puts("</div>");
     }
 hPrintf("<BR>\n");
 }
 
 void qChainRangePlusStrand(struct chain *chain, int *retQs, int *retQe)
 /* Return range of bases covered by chain on q side on the plus
  * strand. */
 {
 if (chain == NULL)
     errAbort("Can't find range in null query chain.");
 if (chain->qStrand == '-')
     {
     *retQs = chain->qSize - chain->qEnd+1;
     *retQe = chain->qSize - chain->qStart;
     }
 else
     {
     *retQs = chain->qStart+1;
     *retQe = chain->qEnd;
     }
 }
 
 struct chain *chainDbLoad(struct sqlConnection *conn, char *db, char *track,
 			  char *chrom, int id)
 /* Load chain. */
 {
 char table[64];
 char query[256];
 struct sqlResult *sr;
 char **row;
 int rowOffset;
 struct chain *chain;
 
 if (!hFindSplitTable(db, seqName, track, table, &rowOffset))
     errAbort("No %s track in database %s for %s", track, db, seqName);
 sqlSafef(query, sizeof(query),
 	 "select * from %s where id = %d", table, id);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row == NULL)
     errAbort("Can't find %d in %s", id, table);
 chain = chainHeadLoad(row + rowOffset);
 sqlFreeResult(&sr);
 chainDbAddBlocks(chain, track, conn);
 return chain;
 }
 
 void linkToOtherBrowserExtra(char *otherDb, char *chrom, int start, int end, char *extra)
 /* Make anchor tag to open another browser window. */
 {
 printf("<A TARGET=\"_blank\" HREF=\"%s?db=%s&%s&position=%s%%3A%d-%d\">",
        hgTracksName(), otherDb, extra, chrom, start+1, end);
 }
 
 void linkToOtherBrowserSearch(char *otherDb, char *tag)
 /* Make anchor tag to open another browser window. */
 {
 printf("<A TARGET=\"_blank\" HREF=\"%s?db=%s&ct=&position=%s\">",
        hgTracksName(), otherDb, tag);
 }
 
 void linkToOtherBrowser(char *otherDb, char *chrom, int start, int end)
 /* Make anchor tag to open another browser window. */
 {
 printf("<A TARGET=\"_blank\" HREF=\"%s?db=%s&ct=&position=%s%%3A%d-%d\">",
        hgTracksName(), otherDb, chrom, start+1, end);
 }
 
 void linkToOtherBrowserTitle(char *otherDb, char *chrom, int start, int end, char *title)
 /* Make anchor tag to open another browser window. */
 {
 printf("<A TARGET=\"_blank\" TITLE=\"%s\" HREF=\"%s?db=%s&ct=&position=%s%%3A%d-%d\">",
        title, hgTracksName(), otherDb, chrom, start+1, end);
 }
 
 void chainToOtherBrowser(struct chain *chain, char *otherDb, char *otherOrg)
 /* Put up link that lets us use chain to browser on
  * corresponding window of other species. */
 {
 struct chain *subChain = NULL, *toFree = NULL;
 int qs,qe;
 chainSubsetOnT(chain, winStart, winEnd, &subChain, &toFree);
 if (subChain != NULL && otherOrg != NULL)
     {
     qChainRangePlusStrand(subChain, &qs, &qe);
     linkToOtherBrowser(otherDb, subChain->qName, qs-1, qe);
     printf("Open %s browser</A> at position corresponding to the part of chain that is in this window.<BR>\n", otherOrg);
     }
 chainFree(&toFree);
 }
 
 void genericChainClick(struct sqlConnection *conn, struct trackDb *tdb,
                        char *item, int start, char *otherDb)
 /* Handle click in chain track, at least the basics. */
 {
 char *thisOrg = hOrganism(database);
 char *otherOrg = NULL;
 struct chain *chain = NULL, *subChain = NULL, *toFree = NULL;
 int chainWinSize;
 double subSetScore = 0.0;
 int qs, qe;
 boolean nullSubset = FALSE;
 
 if (! sameWord(otherDb, "seq"))
     {
     otherOrg = hOrganism(otherDb);
     }
 if (otherOrg == NULL)
     {
     /* use first word of chain label (count on org name as first word) */
     otherOrg = firstWordInLine(cloneString(tdb->shortLabel));
     }
 
 if (isHubTrack(tdb->track) || isCustomTrack(tdb->track))
     {
     char *fileName = bbiNameFromSettingOrTable(tdb, conn, tdb->table);
     char *linkFileName = trackDbSetting(tdb, "linkDataUrl");
     chain = chainLoadIdRangeHub(fileName, linkFileName, seqName, winStart, winEnd, atoi(item));
     }
 else
     {
     chain = chainLoadIdRange(database, tdb->table, seqName, winStart, winEnd, atoi(item));
     }
 
 chainSubsetOnT(chain, winStart, winEnd, &subChain, &toFree);
 
 if (subChain == NULL)
     nullSubset = TRUE;
 else if (hDbIsActive(otherDb) && subChain != chain)
     {
     char *linearGap = trackDbSettingOrDefault(tdb, "chainLinearGap", "loose");
     struct gapCalc *gapCalc = gapCalcFromFile(linearGap);
     struct axtScoreScheme *scoreScheme = axtScoreSchemeDefault();
     int qStart = subChain->qStart;
     int qEnd   = subChain->qEnd  ;
     struct dnaSeq *tSeq = hDnaFromSeq(database, subChain->tName, subChain->tStart, subChain->tEnd, dnaLower);
     struct dnaSeq *qSeq = NULL;
     char *matrix = trackDbSetting(tdb, "matrix");
     if (matrix != NULL)
         {
         char *words[64];
         int size = chopByWhite(matrix, words, 64) ;
         if (size == 2 && atoi(words[0]) == 16)
             {
             scoreScheme = axtScoreSchemeFromBlastzMatrix(words[1], 400, 30);
             }
         else
             {
             if (size != 2)
                 errAbort("error parsing matrix entry in trackDb, expecting 2 word got %d ",
                         size);
             else
                 errAbort("error parsing matrix entry in trackDb, size 16 matrix, got %d ",
                         atoi(words[0]));
             }
         }
 
     if (subChain->qStrand == '-')
         reverseIntRange(&qStart, &qEnd, subChain->qSize);
     qSeq = hChromSeq(otherDb, subChain->qName, qStart, qEnd);
     if (subChain->qStrand == '-')
         reverseComplement(qSeq->dna, qSeq->size);
     subChain->score = chainCalcScoreSubChain(subChain, scoreScheme, gapCalc,
         qSeq, tSeq);
     subSetScore = subChain->score;
     }
 chainFree(&toFree);
 
 printf("<B>%s position:</B> <A HREF=\"%s?%s&db=%s&position=%s:%d-%d\">%s:%d-%d</A>"
        "  size: %d <BR>\n",
        thisOrg, hgTracksName(), cartSidUrlString(cart), database,
        chain->tName, chain->tStart+1, chain->tEnd, chain->tName, chain->tStart+1, chain->tEnd,
        chain->tEnd-chain->tStart);
 printf("<B>Strand:</B> %c<BR>\n", chain->qStrand);
 qChainRangePlusStrand(chain, &qs, &qe);
 if (sameWord(otherDb, "seq"))
     {
     printf("<B>%s position:</B> %s:%d-%d  size: %d<BR>\n",
 	otherOrg, chain->qName, qs, qe, chain->qEnd - chain->qStart);
     }
 else
     {
     /* prints link to other db browser only if db exists and is active */
     /* else just print position with no link for the other db */
     printf("<B>%s position: </B>", otherOrg);
     if (hDbIsActive(otherDb))
         printf(" <A target=\"_blank\" href=\"%s?db=%s&position=%s%%3A%d-%d\">",
                hgTracksName(), otherDb, chain->qName, qs, qe);
     printf("%s:%d-%d", chain->qName, qs, qe);
     if (hDbIsActive(otherDb))
         printf("</A>");
     printf(" size: %d<BR>\n", chain->qEnd - chain->qStart);
     }
 printf("<B>Chain ID:</B> %s<BR>\n", item);
 printf("<B>Score:</B> %1.0f\n", chain->score);
 
 if (nullSubset)
     printf("<B>Score within browser window:</B> N/A (no aligned bases)<BR>\n");
 else if (hDbIsActive(otherDb) && subChain != chain)
     printf("<B>&nbsp;&nbsp;Approximate Score within browser window:</B> %1.0f<BR>\n",
 	   subSetScore);
 else
     printf("<BR>\n");
 printf("<BR>Fields above refer to entire chain or gap, not just the part inside the window.<BR>\n");
 
 boolean normScoreAvailable = chainDbNormScoreAvailable(tdb);
 
 if (normScoreAvailable)
     {
     boolean hasBin;
     char tableName[HDB_MAX_TABLE_STRING];
     hFindSplitTable(database, chain->tName, tdb->table, tableName, &hasBin);
     char query[256];
     struct sqlResult *sr;
     char **row;
     sqlSafef(query, ArraySize(query),
 	 "select normScore from %s where id = '%s'", tableName, item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
 	printf("<B>Normalized Score:</B> %1.0f (bases matched: %d)<BR>\n",
 	    atof(row[0]), (int) (chain->score/atof(row[0])));
     sqlFreeResult(&sr);
     }
 
 printf("<BR>\n");
 
 chainWinSize = min(winEnd-winStart, chain->tEnd - chain->tStart);
 /* Show alignment if the database exists and */
 /* if there is a chromInfo table for that database and the sequence */
 /* file exists. This means that alignments can be shown on the archive */
 /* server (or in other cases) if there is a database with a chromInfo table, */
 /* the sequences are available and there is an entry added to dbDb for */
 /* the otherDb. */
 if (sqlDatabaseExists(otherDb) && chromSeqFileExists(otherDb, chain->qName))
     {
     if (chainWinSize < 1000000)
         {
         hgcAnchorSomewhere("htcChainAli", item, tdb->track, chain->tName);
         printf("View details of parts of chain within browser "
            "window</A>.<BR>\n");
         }
     else
         {
         printf("Zoom so that browser window covers 1,000,000 bases or less "
            "and return here to see alignment details.<BR>\n");
         }
     if (!sameWord(otherDb, "seq") && (hDbIsActive(otherDb)))
         {
         chainToOtherBrowser(chain, otherDb, otherOrg);
         }
     }
 /*
 if (!sameWord(otherDb, "seq") && (hDbIsActive(otherDb)))
     {
     chainToOtherBrowser(chain, otherDb, otherOrg);
     }
 */
 chainFree(&chain);
 }
 
 char *trackTypeInfo(char *track)
 /* Return type info on track. You can freeMem result when done. */
 {
 struct slName *trackDbs = hTrackDbList(), *oneTrackDb;
 struct sqlConnection *conn = hAllocConn(database);
 char buf[512];
 char query[256];
 for (oneTrackDb = trackDbs; oneTrackDb != NULL; oneTrackDb = oneTrackDb->next)
     {
     if (sqlTableExists(conn, oneTrackDb->name))
         {
         sqlSafef(query, sizeof(query),
               "select type from %s where tableName = '%s'",  oneTrackDb->name, track);
         if (sqlQuickQuery(conn, query, buf, sizeof(buf)) != NULL)
             break;
         }
     }
 if (oneTrackDb == NULL)
     errAbort("%s isn't in the trackDb from the hg.conf", track);
 slNameFreeList(&trackDbs);
 hFreeConn(&conn);
 return cloneString(buf);
 }
 
 void findNib(char *db, char *chrom, char nibFile[512])
 /* Find nib file corresponding to chromosome in given database. */
 {
 struct sqlConnection *conn = sqlConnect(db);
 char query[256];
 
 sqlSafef(query, sizeof(query),
 	 "select fileName from chromInfo where chrom = '%s'", chrom);
 if (sqlQuickQuery(conn, query, nibFile, 512) == NULL)
     errAbort("Sequence %s isn't in database %s", chrom, db);
 sqlDisconnect(&conn);
 }
 
 struct dnaSeq *loadGenomePart(char *db,
                               char *chrom, int start, int end)
 /* Load genomic dna from given database and position. */
 {
 char nibFile[512];
 findNib(db, chrom, nibFile);
 return hFetchSeq(nibFile, chrom, start, end);
 }
 
 void printLabeledNumber(char *org, char *label, long long number)
 /* Print label: in bold face, and number with commas. */
 {
 char *space = " ";
 if (org == NULL)
     org = space = "";
 printf("<B>%s%s%s:</B> ", org, space, label);
 printLongWithCommas(stdout, number);
 printf("<BR>\n");
 }
 
 void printLabeledPercent(char *org, char *label, long p, long q)
 /* Print label: in bold, then p, and then 100 * p/q */
 {
 char *space = " ";
 if (org == NULL)
     org = space = "";
 printf("<B>%s%s%s:</B> ", org, space, label);
 printLongWithCommas(stdout, p);
 if (q != 0)
     printf(" (%3.1f%%)", 100.0 * p / q);
 printf("<BR>\n");
 }
 
 void genericNetClick(struct sqlConnection *conn, struct trackDb *tdb,
                      char *item, int start, char *otherDb, char *chainTrack)
 /* Generic click handler for net tracks. */
 {
 char table[64];
 int rowOffset;
 char query[256];
 struct sqlResult *sr;
 char **row;
 struct netAlign *net;
 char *org = hOrganism(database);
 char *otherOrg = hOrganism(otherDb);
 char *otherOrgBrowser = otherOrg;
 int tSize, qSize;
 int netWinSize;
 struct chain *chain;
 
 if (otherOrg == NULL)
     {
     /* use first word in short track label */
     otherOrg = firstWordInLine(cloneString(tdb->shortLabel));
     }
 hFindSplitTable(database, seqName, tdb->table, table, &rowOffset);
 sqlSafef(query, sizeof(query),
 	 "select * from %s where tName = '%s' and tStart <= %d and tEnd > %d "
 	 "and level = %s",
 	 table, seqName, start, start, item);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find %s:%d in %s", seqName, start, table);
 
 net = netAlignLoad(row+rowOffset);
 sqlFreeResult(&sr);
 tSize = net->tEnd - net->tStart;
 qSize = net->qEnd - net->qStart;
 
 if (net->chainId != 0)
     {
     netWinSize = min(winEnd-winStart, net->tEnd - net->tStart);
     printf("<BR>\n");
     /* Show alignment if the database exists and */
     /* if there is a chromInfo table for that database and the sequence */
     /* file exists. This means that alignments can be shown on the archive */
     /* server (or in other cases) if there is a database with a chromInfo */
     /* table, the sequences are available and there is an entry added to */
     /* dbDb for the otherDb. */
     if (chromSeqFileExists(otherDb, net->qName))
         {
         if (netWinSize < 1000000)
 	    {
 	    int ns = max(winStart, net->tStart);
 	    int ne = min(winEnd, net->tEnd);
 	    if (ns < ne)
 	        {
 	        char id[20];
 	        snprintf(id, sizeof(id), "%d", net->chainId);
 	        hgcAnchorWindow("htcChainAli", id, ns, ne, chainTrack, seqName);
 	        printf("View alignment details of parts of net within browser window</A>.<BR>\n");
 	        }
 	    else
 	        {
 	        printf("Odd, net not in window<BR>\n");
 	        }
 	    }
         else
 	    {
 	    printf("To see alignment details zoom so that the browser window covers 1,000,000 bases or less.<BR>\n");
 	    }
         }
     chain = chainDbLoad(conn, database, chainTrack, seqName, net->chainId);
     if (chain != NULL)
         {
          /* print link to browser for otherDb only if otherDb is active */
         if (hDbIsActive(otherDb))
 	    chainToOtherBrowser(chain, otherDb, otherOrgBrowser);
 	chainFree(&chain);
 	}
     htmlHorizontalLine();
     }
 printf("<B>Type:</B> %s<BR>\n", net->type);
 printf("<B>Level:</B> %d<BR>\n", (net->level+1)/2);
 printf("<B>%s position:</B> %s:%d-%d<BR>\n",
        org, net->tName, net->tStart+1, net->tEnd);
 printf("<B>%s position:</B> %s:%d-%d<BR>\n",
        otherOrg, net->qName, net->qStart+1, net->qEnd);
 printf("<B>Strand:</B> %c<BR>\n", net->strand[0]);
 printLabeledNumber(NULL, "Score", net->score);
 if (net->chainId)
     {
     printf("<B>Chain ID:</B> %u<BR>\n", net->chainId);
     printLabeledNumber(NULL, "Bases aligning", net->ali);
     if (net->qOver >= 0)
 	printLabeledNumber(otherOrg, "parent overlap", net->qOver);
     if (net->qFar >= 0)
 	printLabeledNumber(otherOrg, "parent distance", net->qFar);
     if (net->qDup >= 0)
 	printLabeledNumber(otherOrg, "bases duplicated", net->qDup);
     }
 if (net->tN >= 0)
     printLabeledPercent(org, "N's", net->tN, tSize);
 if (net->qN >= 0)
     printLabeledPercent(otherOrg, "N's", net->qN, qSize);
 if (net->tTrf >= 0)
     printLabeledPercent(org, "tandem repeat (trf) bases", net->tTrf, tSize);
 if (net->qTrf >= 0)
     printLabeledPercent(otherOrg, "tandem repeat (trf) bases", net->qTrf, qSize);
 if (net->tR >= 0)
     printLabeledPercent(org, "RepeatMasker bases", net->tR, tSize);
 if (net->qR >= 0)
     printLabeledPercent(otherOrg, "RepeatMasker bases", net->qR, qSize);
 if (net->tOldR >= 0)
     printLabeledPercent(org, "old repeat bases", net->tOldR, tSize);
 if (net->qOldR >= 0)
     printLabeledPercent(otherOrg, "old repeat bases", net->qOldR, qSize);
 if (net->tNewR >= 0)
     printLabeledPercent(org, "new repeat bases", net->tOldR, tSize);
 if (net->qNewR >= 0)
     printLabeledPercent(otherOrg, "new repeat bases", net->qOldR, qSize);
 if (net->tEnd >= net->tStart)
     printLabeledNumber(org, "size", net->tEnd - net->tStart);
 if (net->qEnd >= net->qStart)
     printLabeledNumber(otherOrg, "size", net->qEnd - net->qStart);
 printf("<BR>Fields above refer to entire chain or gap, not just the part inside the window.<BR>\n");
 netAlignFree(&net);
 }
 
 void tfbsConsSites(struct trackDb *tdb, char *item)
 /* detail page for tfbsConsSites track */
 {
 boolean printedPlus = FALSE;
 boolean printedMinus = FALSE;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 char table[64];
 boolean hasBin;
 char query[512];
 struct sqlResult *sr;
 char **row;
 struct tfbsConsSites *tfbsConsSites;
 struct tfbsConsSites *tfbsConsSitesList = NULL;
 struct tfbsConsFactors *tfbsConsFactor;
 struct tfbsConsFactors *tfbsConsFactorList = NULL;
 boolean firstTime = TRUE;
 char *mappedId = NULL;
 
 genericHeader(tdb, item);
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
 	    table, item, seqName, start);
 sr = sqlGetResult(conn, query);
 
 while ((row = sqlNextRow(sr)) != NULL)
     {
     tfbsConsSites = tfbsConsSitesLoad(row+hasBin);
     slAddHead(&tfbsConsSitesList, tfbsConsSites);
     }
 sqlFreeResult(&sr);
 slReverse(&tfbsConsSitesList);
 
 hFindSplitTable(database, seqName, "tfbsConsFactors", table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' ", table, item);
 sr = sqlGetResult(conn, query);
 
 while ((row = sqlNextRow(sr)) != NULL)
     {
     tfbsConsFactor = tfbsConsFactorsLoad(row+hasBin);
     slAddHead(&tfbsConsFactorList, tfbsConsFactor);
     }
 sqlFreeResult(&sr);
 slReverse(&tfbsConsFactorList);
 
 if (tfbsConsFactorList)
     mappedId = cloneString(tfbsConsFactorList->ac);
 
 printf("<B style='font-size:large;'>Transcription Factor Binding Site information:</B><BR><BR><BR>");
 for(tfbsConsSites=tfbsConsSitesList ; tfbsConsSites != NULL ; tfbsConsSites = tfbsConsSites->next)
     {
     /* print each strand only once */
     if ((printedMinus && (tfbsConsSites->strand[0] == '-')) || (printedPlus && (tfbsConsSites->strand[0] == '+')))
 	continue;
 
     if (!firstTime)
         htmlHorizontalLine();
     else
 	firstTime = FALSE;
 
     printf("<B>Item:</B> %s<BR>\n", tfbsConsSites->name);
     if (mappedId != NULL)
 	printCustomUrl(tdb, mappedId, FALSE);
     printf("<B>Score:</B> %d<BR>\n", tfbsConsSites->score );
     printf("<B>zScore:</B> %.2f<BR>\n", tfbsConsSites->zScore );
     printf("<B>Strand:</B> %s<BR>\n", tfbsConsSites->strand);
     printPos(tfbsConsSites->chrom, tfbsConsSites->chromStart, tfbsConsSites->chromEnd, NULL, TRUE, tfbsConsSites->name);
     printedPlus = printedPlus || (tfbsConsSites->strand[0] == '+');
     printedMinus = printedMinus || (tfbsConsSites->strand[0] == '-');
     }
 
 if (tfbsConsFactorList)
     {
     htmlHorizontalLine();
     printf("<B style='font-size:large;'>Transcription Factors known to bind to this site:</B><BR><BR>");
     for(tfbsConsFactor =tfbsConsFactorList ; tfbsConsFactor  != NULL ; tfbsConsFactor  = tfbsConsFactor ->next)
 	{
 	if (!sameString(tfbsConsFactor->species, "N"))
 	    {
 	    printf("<BR><B>Factor:</B> %s<BR>\n", tfbsConsFactor->factor);
 	    printf("<B>Species:</B> %s<BR>\n", tfbsConsFactor->species);
 	    printf("<B>SwissProt ID:</B> %s<BR>\n", sameString(tfbsConsFactor->id, "N")? "unknown": tfbsConsFactor->id);
 	    }
 	}
     }
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void tfbsCons(struct trackDb *tdb, char *item)
 /* detail page for tfbsCons track */
 {
 boolean printFactors = FALSE;
 boolean printedPlus = FALSE;
 boolean printedMinus = FALSE;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 char table[64];
 boolean hasBin;
 char query[512];
 struct sqlResult *sr;
 char **row;
 struct tfbsCons *tfbs;
 struct tfbsCons *tfbsConsList = NULL;
 struct tfbsConsMap tfbsConsMap;
 boolean firstTime = TRUE;
 char *mappedId = NULL;
 
 genericHeader(tdb, item);
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
 	    table, item, seqName, start);
 sr = sqlGetResult(conn, query);
 
 while ((row = sqlNextRow(sr)) != NULL)
     {
     tfbs = tfbsConsLoad(row+hasBin);
     slAddHead(&tfbsConsList, tfbs);
     }
 sqlFreeResult(&sr);
 slReverse(&tfbsConsList);
 
 if (hTableExists(database, "tfbsConsMap"))
     {
     sqlSafef(query, sizeof query, "select * from tfbsConsMap where id = '%s'", tfbsConsList->name);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
 	{
 	tfbsConsMapStaticLoad(row, &tfbsConsMap);
 	mappedId = cloneString(tfbsConsMap.ac);
 	}
     }
 sqlFreeResult(&sr);
 
 printf("<B style='font-size:large;'>Transcription Factor Binding Site information:</B><BR><BR><BR>");
 for(tfbs=tfbsConsList ; tfbs != NULL ; tfbs = tfbs->next)
     {
     if (!sameString(tfbs->species, "N"))
 	printFactors = TRUE;
 
     /* print each strand only once */
     if ((printedMinus && (tfbs->strand[0] == '-')) || (printedPlus && (tfbs->strand[0] == '+')))
 	continue;
 
     if (!firstTime)
         htmlHorizontalLine();
     else
 	firstTime = FALSE;
 
     printf("<B>Item:</B> %s<BR>\n", tfbs->name);
     if (mappedId != NULL)
 	printCustomUrl(tdb, mappedId, FALSE);
     printf("<B>Score:</B> %d<BR>\n", tfbs->score );
     printf("<B>Strand:</B> %s<BR>\n", tfbs->strand);
     printPos(tfbsConsList->chrom, tfbs->chromStart, tfbs->chromEnd, NULL, TRUE, tfbs->name);
     printedPlus = printedPlus || (tfbs->strand[0] == '+');
     printedMinus = printedMinus || (tfbs->strand[0] == '-');
     }
 
 if (printFactors)
     {
     htmlHorizontalLine();
     printf("<B style='font-size:large;'>Transcription Factors known to bind to this site:</B><BR><BR>");
     for(tfbs=tfbsConsList ; tfbs != NULL ; tfbs = tfbs->next)
 	{
 	/* print only the positive strand when factors are on both strands */
 	if ((tfbs->strand[0] == '-') && printedPlus)
 	    continue;
 
 	if (!sameString(tfbs->species, "N"))
 	    {
 	    printf("<BR><B>Factor:</B> %s<BR>\n", tfbs->factor);
 	    printf("<B>Species:</B> %s<BR>\n", tfbs->species);
 	    printf("<B>SwissProt ID:</B> %s<BR>\n", sameString(tfbs->id, "N")? "unknown": tfbs->id);
 
 	    }
 	}
     }
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void firstEF(struct trackDb *tdb, char *item)
 {
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 char table[64];
 boolean hasBin;
 struct bed *bed;
 char query[512];
 struct sqlResult *sr;
 char **row;
 boolean firstTime = TRUE;
 
 /* itemForUrl = item; */
 genericHeader(tdb, item);
 printCustomUrl(tdb, item, FALSE);
 /* printCustomUrl(tdb, itemForUrl, item == itemForUrl); */
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
 	    table, item, seqName, start);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     bed = bedLoadN(row+hasBin, 6);
 
     printf("<B>Item:</B> %s<BR>\n", bed->name);
     printf("<B>Probability:</B> %g<BR>\n", bed->score / 1000.0);
     printf("<B>Strand:</B> %s<BR>\n", bed->strand);
     printPos(bed->chrom, bed->chromStart, bed->chromEnd, NULL, TRUE, bed->name);
     }
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doBed5FloatScore(struct trackDb *tdb, char *item)
 /* Handle click in BED 5+ track: BED 5 with 0-1000 score (for useScore
  * shading in hgTracks) plus real score for display in details page. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char table[64];
 boolean hasBin;
 struct bed5FloatScore *b5;
 struct dyString *query = newDyString(512);
 char **row;
 boolean firstTime = TRUE;
 int start = cartInt(cart, "o");
 int bedSize = 5;
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlDyStringPrintf(query, "select * from %s where chrom = '%s' and ",
 	       table, seqName);
 hAddBinToQuery(winStart, winEnd, query);
 sqlDyStringPrintf(query, "name = '%s' and chromStart = %d", item, start);
 sr = sqlGetResult(conn, query->string);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     b5 = bed5FloatScoreLoad(row+hasBin);
     bedPrintPos((struct bed *)b5, 4, tdb);
     printf("<B>Score:</B> %f<BR>\n", b5->floatScore);
     if (sameString(tdb->type, "bed5FloatScoreWithFdr"))
         {
         if (row[7] != NULL)
            printf("<B>False Discovery Rate (FDR):</B> %s%%<BR>\n", row[7]);
         }
     }
 getBedTopScorers(conn, tdb, table, item, start, bedSize);
 
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 /* printTrackHtml is done in genericClickHandlerPlus. */
 }
 
 void doBed6FloatScore(struct trackDb *tdb, char *item)
 /* Handle click in BED 4+ track that's like BED 6 but with floating pt score */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char table[64];
 boolean hasBin;
 struct bed6FloatScore *b6 = NULL;
 struct dyString *query = newDyString(512);
 char **row;
 boolean firstTime = TRUE;
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, item);
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlDyStringPrintf(query, "select * from %s where chrom = '%s' and ",
 	       table, seqName);
 hAddBinToQuery(winStart, winEnd, query);
 sqlDyStringPrintf(query, "name = '%s' and chromStart = %d", item, start);
 sr = sqlGetResult(conn, query->string);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     b6 = bed6FloatScoreLoad(row+hasBin);
     bedPrintPos((struct bed *)b6, 4, tdb);
     printf("<B>Score:</B> %f<BR>\n", b6->score);
     printf("<B>Strand:</B> %s<BR>\n", b6->strand);
     }
 sqlFreeResult(&sr);
 
 // Support for motif display if configured in trackDb
 // TODO - share code with factorSource
 char *motifPwmTable = trackDbSetting(tdb, "motifPwmTable");
 struct dnaMotif *motif = NULL;
 if (motifPwmTable != NULL && sqlTableExists(conn, motifPwmTable))
     {
     motif = loadDnaMotif(b6->name, motifPwmTable);
     if (motif == NULL)
         return;
     struct dnaSeq *seq = hDnaFromSeq(database, b6->chrom, b6->chromStart, b6->chromEnd, dnaLower);
     motifLogoAndMatrix(&seq, 1, motif);
     }
 hFreeConn(&conn);
 /* printTrackHtml is done in genericClickHandlerPlus. */
 }
 
 void doColoredExon(struct trackDb *tdb, char *item)
 /* Print information for coloredExon type tracks. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char query[256];
 char **row;
 genericHeader(tdb, item);
 sqlSafef(query, sizeof(query), "select chrom,chromStart,chromEnd,name,score,strand from %s where name='%s'", tdb->table, item);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     struct bed *itemBed = bedLoad6(row);
     bedPrintPos(itemBed, 6, tdb);
     bedFree(&itemBed);
     }
 else
     {
     hPrintf("Could not find info for %s<BR>\n", item);
     }
 sqlFreeResult(&sr);
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doImageItemBed(struct trackDb *tdb, char *item)
 /* Print bed plus an image */
 {
 }
 
 void doChromGraph(struct trackDb *tdb)
 /* Print information for coloredExon type tracks. */
 {
 genericHeader(tdb, NULL);
 printTrackHtml(tdb);
 }
 
 static void genericContainerClick(struct sqlConnection *conn, char *containerType,
 	struct trackDb *tdb, char *item, char *itemForUrl)
 /* Print page for container of some sort. */
 {
 if (sameString(containerType, "multiWig"))
     {
     errAbort("It's suprising that multiWig container gets to hgc. It should go to hgTrackUi.");
     }
 else
     {
     errAbort("Unrecognized container type %s for %s", containerType, tdb->track);
     }
 }
 
 static void doLongTabix(struct trackDb *tdb, char *item)
 /* Handle a click on a long range interaction */
 {
 char *bigDataUrl = hashFindVal(tdb->settingsHash, "bigDataUrl");
 struct bedTabixFile *btf = bedTabixFileMayOpen(bigDataUrl, NULL, 0, 0);
 char *chromName = cartString(cart, "c");
 struct bed *list = bedTabixReadBeds(btf, chromName, winStart, winEnd, bedLoad5);
 bedTabixFileClose(&btf);
 unsigned maxWidth;
 struct longRange *longRangeList = parseLongTabix(list, &maxWidth, 0);
 struct longRange *longRange, *ourLongRange = NULL;
 unsigned itemNum = sqlUnsigned(item);
 unsigned count = slCount(longRangeList);
 double *doubleArray;
 
 AllocArray(doubleArray, count);
 
 int ii = 0;
 for(longRange = longRangeList; longRange; longRange = longRange->next, ii++)
     {
     if (longRange->id == itemNum)
         {
         ourLongRange = longRange;
         }
     doubleArray[ii] = longRange->score;
     }
 
 if (ourLongRange == NULL)
     errAbort("cannot find long range item with id %d\n", itemNum);
 
 struct aveStats *as = aveStatsCalc(doubleArray, count);
 
 printf("Item you clicked on:<BR>\n");
 printf("<B>Score:</B> %g<BR>\n", ourLongRange->score);
 printf("<B>ID:</B> %u<BR>\n", ourLongRange->id);
 unsigned padding =  (ourLongRange->e - ourLongRange->s) / 10;
 int s = ourLongRange->s - padding; 
 int e = ourLongRange->e + padding; 
 if (s < 0 ) 
     s = 0;
 int chromSize = hChromSize(database, seqName);
 if (e > chromSize)
     e = chromSize;
 
 char num1Buf[1024],num2Buf[1024];
 char num3Buf[1024],num4Buf[1024];
 char num5Buf[1024],num6Buf[1024];
 char num7Buf[1024];
 sprintLongWithCommas(num1Buf, ourLongRange->s - ourLongRange->sw/2);
 sprintLongWithCommas(num2Buf, ourLongRange->s + ourLongRange->sw/2);
 sprintLongWithCommas(num3Buf, ourLongRange->e - ourLongRange->ew/2);
 sprintLongWithCommas(num4Buf, ourLongRange->e + ourLongRange->ew/2);
 sprintLongWithCommas(num5Buf, ourLongRange->sw);
 sprintLongWithCommas(num6Buf, ourLongRange->ew);
 sprintLongWithCommas(num7Buf, ourLongRange->ew + ourLongRange->e - ourLongRange->s);
 if (differentString(ourLongRange->sChrom, ourLongRange->eChrom))
     {
     printf("<B>Current region: </B>");
     printf("<A HREF=\"hgTracks?position=%s:%s-%s \" TARGET=_BLANK>%s:%s-%s (%sbp)</A><BR>\n",  
         ourLongRange->sChrom, num1Buf,num2Buf,ourLongRange->sChrom, num1Buf,num2Buf, num5Buf);
     printf("<B>Paired region: </B>");
     printf("<A HREF=\"hgTracks?position=%s:%s-%s \" TARGET=_BLANK>%s:%s-%s (%sbp)<BR></A><BR>\n",  
         ourLongRange->eChrom, num3Buf, num4Buf, ourLongRange->eChrom, num3Buf, num4Buf, num6Buf);
     }
 else
     {
     printf("<B>Lower region: </B>");
     printf("<A HREF=\"hgTracks?position=%s:%s-%s \" TARGET=_BLANK>%s:%s-%s (%sbp)</A><BR>\n",  
         ourLongRange->sChrom, num1Buf,num2Buf, ourLongRange->sChrom, num1Buf,num2Buf, num5Buf);
     printf("<B>Upper region: </B>");
     printf("<A HREF=\"hgTracks?position=%s:%s-%s \" TARGET=_BLANK>%s:%s-%s (%sbp)<BR></A><BR>\n",  
         ourLongRange->eChrom, num3Buf, num4Buf, ourLongRange->eChrom, num3Buf, num4Buf, num6Buf);
     printf("<B>Intrachromasomal interaction region: </B>");
     printf("<A HREF=\"hgTracks?position=%s:%s-%s \" TARGET=_BLANK>%s:%s-%s (%sbp)<BR></A><BR>\n",  
         ourLongRange->eChrom, num1Buf, num4Buf, ourLongRange->eChrom, num1Buf, num4Buf, num7Buf);
     }
 
 printf("<BR>Statistics on the scores of all items in window (go to track controls to set minimum score to display):\n");
 
 printf("<TABLE BORDER=1>\n");
 printf("<TR><TD><B>Q1</B></TD><TD>%f</TD></TR>\n", as->q1);
 printf("<TR><TD><B>median</B></TD><TD>%f</TD></TR>\n", as->median);
 printf("<TR><TD><B>Q3</B></TD><TD>%f</TD></TR>\n", as->q3);
 printf("<TR><TD><B>average</B></TD><TD>%f</TD></TR>\n", as->average);
 printf("<TR><TD><B>min</B></TD><TD>%f</TD></TR>\n", as->minVal);
 printf("<TR><TD><B>max</B></TD><TD>%f</TD></TR>\n", as->maxVal);
 printf("<TR><TD><B>count</B></TD><TD>%d</TD></TR>\n", as->count);
 printf("<TR><TD><B>total</B></TD><TD>%f</TD></TR>\n", as->total);
 printf("<TR><TD><B>standard deviation</B></TD><TD>%f</TD></TR>\n", as->stdDev);
 printf("</TABLE>\n");
 }
 
 void genericClickHandlerPlus(
         struct trackDb *tdb, char *item, char *itemForUrl, char *plus)
 /* Put up generic track info, with additional text appended after item. */
 {
 char *dupe, *type, *words[16], *headerItem;
 int wordCount;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 struct sqlConnection *conn = NULL;
 char *imagePath = trackDbSetting(tdb, ITEM_IMAGE_PATH);
 char *container = trackDbSetting(tdb, "container");
 
 if (!trackHubDatabase(database))
     conn = hAllocConnTrack(database, tdb);
 if (itemForUrl == NULL)
     itemForUrl = item;
 dupe = cloneString(tdb->type);
 wordCount = chopLine(dupe, words);
 headerItem = cloneString(item);
 
 /* Suppress printing item name in page header, as it is not informative for these types of
  * tracks... */
 if (container == NULL && wordCount > 0)
     {
     type = words[0];
     if (sameString(type, "maf") || sameString(type, "wigMaf") || sameString(type, "bigMaf") || sameString(type, "netAlign")
         || sameString(type, "encodePeak"))
         headerItem = NULL;
     else if ((  sameString(type, "narrowPeak")
              || sameString(type, "broadPeak")
              || sameString(type, "gappedPeak") )
          &&  headerItem
          &&  sameString(headerItem, ".") )
         headerItem = NULL;
     }
 
 /* Print header. */
 genericHeader(tdb, headerItem);
 
 itemForUrl = getIdInUrl(tdb, item);
 if (itemForUrl != NULL && trackDbSetting(tdb, "url"))
     {
     printCustomUrl(tdb, itemForUrl, item == itemForUrl);
     printIframe(tdb, itemForUrl);
     }
 
 if (plus != NULL)
     {
     fputs(plus, stdout);
     }
 
 if (container != NULL)
     {
     genericContainerClick(conn, container, tdb, item, itemForUrl);
     }
 else if (wordCount > 0)
     {
     type = words[0];
     if (sameString(type, "bed"))
 	{
 	int num = 0;
 	if (wordCount > 1)
 	    num = atoi(words[1]);
 	if (num < 3) num = 3;
         genericBedClick(conn, tdb, item, start, num);
 	}
     else if (sameString(type, "bigGenePred"))
         {
 	int num = 12;
         genericBigBedClick(conn, tdb, item, start, end, num);
 	}
     else if (sameString(type, "bigBed"))
         {
 	int num = 0;
 	if (wordCount > 1)
 	    num = atoi(words[1]);
 	if (num < 3) num = 3;
         genericBigBedClick(conn, tdb, item, start, end, num);
 	}
     else if (sameString(type, "sample"))
 	{
 	int num = 9;
         genericSampleClick(conn, tdb, item, start, num);
 	}
     else if (sameString(type, "genePred"))
         {
 	char *pepTable = NULL, *mrnaTable = NULL;
 	if ((wordCount > 1) && !sameString(words[1], "."))
 	    pepTable = words[1];
 	if ((wordCount > 2) && !sameString(words[2], "."))
 	    mrnaTable = words[2];
 	genericGenePredClick(conn, tdb, item, start, pepTable, mrnaTable);
 	}
     else if ( sameString(type, "bigPsl"))
         {
 	genericBigPslClick(conn, tdb, item, start, end);
 	}
     else if (sameString(type, "psl"))
         {
 	char *subType = ".";
 	if (wordCount > 1)
 	    subType = words[1];
 	genericPslClick(conn, tdb, item, start, subType);
 	}
     else if (sameString(type, "netAlign"))
         {
 	if (wordCount < 3)
 	    errAbort("Missing field in netAlign track type field");
 	genericNetClick(conn, tdb, item, start, words[1], words[2]);
 	}
     else if (sameString(type, "chain") || sameString(type, "bigChain") )
         {
 	if (wordCount < 2)
 	    errAbort("Missing field in chain track type field");
 	genericChainClick(conn, tdb, item, start, words[1]);
 	}
     else if (sameString(type, "maf"))
         {
 	genericMafClick(conn, tdb, item, start);
 	}
     else if (sameString(type, "wigMaf") ||  sameString(type, "bigMaf"))
         {
 	genericMafClick(conn, tdb, item, start);
         }
     else if (startsWith("wigMafProt", type))
         {
 	genericMafClick(conn, tdb, item, start);
         }
     else if (sameString(type, "axt"))
         {
 	genericAxtClick(conn, tdb, item, start, words[1]);
 	}
     else if (sameString(type, "expRatio"))
         {
 	doExpRatio(tdb, item, NULL);
 	}
     else if (sameString(type, "coloredExon"))
 	{
 	doColoredExon(tdb, item);
 	}
     else if (sameString(type, "encodePeak") || sameString(type, "narrowPeak") ||
 	     sameString(type, "broadPeak") || sameString(type, "gappedPeak"))
 	{
 	doEncodePeak(tdb, NULL, item);
 	}
     else if (sameString(type, "encodeFiveC"))
 	{
 	doEncodeFiveC(conn, tdb);
         }
     else if (sameString(type, "peptideMapping"))
 	{
 	doPeptideMapping(conn, tdb, item);
 	}
     else if (sameString(type, "chromGraph"))
 	{
 	doChromGraph(tdb);
 	}
     else if (sameString(type, "wig"))
         {
 	genericWiggleClick(conn, tdb, item, start);
         }
     else if (sameString(type, "bigWig"))
         {
 	genericBigWigClick(conn, tdb, item, start);
 	}
     else if (sameString(type, "factorSource"))
         {
 	doFactorSource(conn, tdb, item, start);
 	}
     else if (sameString(type, "bed5FloatScore") ||
              sameString(type, "bed5FloatScoreWithFdr"))
 	{
 	doBed5FloatScore(tdb, item);
 	}
     else if (sameString(type, "bed6FloatScore"))
 	{
 	doBed6FloatScore(tdb, item);
 	}
     else if (sameString(type, "altGraphX"))
         {
 	doAltGraphXDetails(tdb,item);
 	}
     //add bedDetail here
     else if (startsWith("bedDetail", type))
         {
         doBedDetail(tdb, NULL, item);
         }
     else if (sameString(type, "interaction") )
 	{
 	int num = 12;
         genericBedClick(conn, tdb, item, start, num);
 	}
     else if (startsWith("gvf", type))
         {
         doGvf(tdb, item);
         }
     else if (sameString(type, "bam"))
 	doBamDetails(tdb, item);
     else if ( startsWith("longTabix", type))
 	doLongTabix(tdb, item);
     }
 if (imagePath)
     {
     char *bigImagePath = trackDbSettingClosestToHome(tdb, ITEM_BIG_IMAGE_PATH);
     char *bothWords[2];
     int shouldBeTwo = chopLine(imagePath, bothWords);
     if (shouldBeTwo != 2)
 	errAbort("itemImagePath setting for %s track incorrect. Needs to be \"itemImagePath <path> <suffix>\".", tdb->track);
     printf("<BR><IMG SRC=\"%s/%s.%s\"><BR><BR>\n", bothWords[0], item, bothWords[1]);
     shouldBeTwo = chopLine(bigImagePath, bothWords);
     if (shouldBeTwo != 2)
 	errAbort("bigItemImagePath setting for %s track incorrect. Needs to be \"itemImagePath <path> <suffix>\".", tdb->track);
     printf("<A HREF=\"%s/%s.%s\">Download Original Image</A><BR>\n", bothWords[0], item, bothWords[1]);
     }
 
 if (sameString(tdb->table,"altLocations") && (!strchr(item,':')))
     {
     char *hgsid = cartSessionId(cart);
     printf("<A HREF=\"/cgi-bin/hgTracks?hgsid=%s&virtModeType=singleAltHaplo&singleAltHaploId=%s\">Show this alternate haplotype placed on its chromosome</A><BR>\n", hgsid, item);
     }
 
 printTrackHtml(tdb);
 freez(&dupe);
 hFreeConn(&conn);
 }
 
 void genericClickHandler(struct trackDb *tdb, char *item, char *itemForUrl)
 /* Put up generic track info */
 {
 #ifdef OLD /* Called now by cartWebStart... */
 jsIncludeFile("jquery.js", NULL);
 jsIncludeFile("utils.js",NULL);
 #endif /* OLD */
 genericClickHandlerPlus(tdb, item, itemForUrl, NULL);
 }
 
 void savePosInTextBox(char *chrom, int start, int end)
 /* Save basic position/database info in text box and hidden var.
    Positions becomes chrom:start-end*/
 {
 char position[128];
 char *newPos;
 snprintf(position, 128, "%s:%d-%d", chrom, start, end);
 newPos = addCommasToPos(database, position);
 cgiMakeTextVar("getDnaPos", newPos, strlen(newPos) + 2);
 cgiContinueHiddenVar("db");
 }
 
 char *hgTablesUrl(boolean usePos, char *track)
 /* Make up URL for table browser. */
 {
 struct dyString *url = dyStringNew(0);
 dyStringAppend(url, "../cgi-bin/hgTables?");
 dyStringAppend(url, cartSidUrlString(cart));
 dyStringPrintf(url, "&db=%s", database);
 if (usePos)
     {
     dyStringPrintf(url, "&position=%s:%d-%d", seqName, winStart+1, winEnd);
     dyStringAppend(url, "&hgta_regionType=range");
     }
 if (track != NULL)
     {
     struct trackDb *tdb = hashFindVal(trackHash, track);
     if (tdb != NULL)
 	{
 	char *grp = tdb->grp;
 	if (grp != NULL && grp[0] != 0)
 	    {
 	    dyStringPrintf(url, "&hgta_group=%s", grp);
 	    dyStringPrintf(url, "&hgta_track=%s", track);
 	    dyStringPrintf(url, "&hgta_table=%s", track);
 	    }
 	}
     }
 return dyStringCannibalize(&url);
 }
 
 char *traceUrl(char *traceId)
 /* Make up URL for trace archive. */
 {
 struct dyString *url = dyStringNew(0);
 dyStringAppend(url, "http://www.ncbi.nlm.nih.gov/Traces/trace.cgi?");
 dyStringPrintf(url, "cmd=retrieve&size=1&val=%s&", traceId);
 dyStringAppend(url, "file=trace&dopt=trace");
 return dyStringCannibalize(&url);
 }
 
 void doGetDna1()
 /* Do first get DNA dialog. */
 {
 struct hTableInfo *hti = NULL;
 char *tbl = cgiUsualString("table", "");
 if (dbIsFound && tbl[0] != 0)
     {
     char rootName[256];
     char parsedChrom[32];
     hParseTableName(database, tbl, rootName, parsedChrom);
     if (!trackHubDatabase(database))
 	hti = hFindTableInfo(database, seqName, rootName);
     }
 char *thisOrg = hOrganism(database);
 cartWebStart(cart, database, "Get DNA in Window (%s/%s)", database, thisOrg);
 printf("<H2>Get DNA for </H2>\n");
 printf("<FORM ACTION=\"%s\">\n\n", hgcName());
 cartSaveSession(cart);
 cgiMakeHiddenVar("g", "htcGetDna2");
 cgiMakeHiddenVar("table", tbl);
 cgiContinueHiddenVar("i");
 cgiContinueHiddenVar("o");
 cgiContinueHiddenVar("t");
 cgiContinueHiddenVar("l");
 cgiContinueHiddenVar("r");
 puts("Position ");
 savePosInTextBox(seqName, winStart+1, winEnd);
 
 /* bypass message about Table Browser for GSID server, since we haven't offered TB for GSID */
 if (!hIsGsidServer())
     {
     if (tbl[0] == 0)
         {
         puts("<P>"
              "Note: This page retrieves genomic DNA for a single region. "
              "If you would prefer to get DNA for many items in a particular track, "
              "or get DNA with formatting options based on gene structure (introns, exons, UTRs, etc.), try using the ");
         printf("<A HREF=\"%s\" TARGET=_blank>", hgTablesUrl(TRUE, NULL));
         puts("Table Browser</A> with the \"sequence\" output format.");
         }
     else
         {
         puts("<P>"
 	     "Note: if you would prefer to get DNA for more than one feature of "
 	     "this track at a time, try the ");
         printf("<A HREF=\"%s\" TARGET=_blank>", hgTablesUrl(FALSE, tbl));
         puts("Table Browser</A> using the output format sequence.");
         }
     }
 
 hgSeqOptionsHtiCart(hti,cart);
 puts("<P>");
 cgiMakeButton("submit", "get DNA");
 if (dbIsFound)
     cgiMakeButton("submit", EXTENDED_DNA_BUTTON);
 puts("</FORM><P>");
 if (dbIsFound)
     puts("Note: The \"Mask repeats\" option applies only to \"get DNA\", not to \"extended case/color options\". <P>");
 }
 
 boolean dnaIgnoreTrack(char *track)
 /* Return TRUE if this is one of the tracks too boring
  * to put DNA on. */
 {
 return (sameString("cytoBand", track) ||
 	sameString("gcPercent", track) ||
 	sameString("gold", track) ||
 	sameString("gap", track) ||
 	startsWith("mouseSyn", track));
 }
 
 struct customTrack *getCtList()
 /* initialize theCtList if necessary and return it */
 {
 if (theCtList == NULL)
     theCtList = customTracksParseCart(database, cart, NULL, NULL);
 return(theCtList);
 }
 
 struct trackDb *tdbForCustomTracks()
 /* Load custom tracks (if any) and translate to list of trackDbs */
 {
 struct customTrack *ctList = getCtList();
 struct customTrack *ct;
 struct trackDb *tdbList = NULL, *tdb;
 
 for (ct=ctList;  ct != NULL;  ct=ct->next)
     {
     AllocVar(tdb);
     tdb->track = ct->tdb->track;
     tdb->table = ct->tdb->table;
     tdb->shortLabel = ct->tdb->shortLabel;
     tdb->type = ct->tdb->type;
     tdb->longLabel = ct->tdb->longLabel;
     tdb->visibility = ct->tdb->visibility;
     tdb->priority = ct->tdb->priority;
     tdb->colorR = ct->tdb->colorR;
     tdb->colorG = ct->tdb->colorG;
     tdb->colorB = ct->tdb->colorB;
     tdb->altColorR = ct->tdb->altColorR;
     tdb->altColorG = ct->tdb->altColorG;
     tdb->altColorB = ct->tdb->altColorB;
     tdb->useScore = ct->tdb->useScore;
     tdb->private = ct->tdb->private;
     tdb->url = ct->tdb->url;
     tdb->grp = ct->tdb->grp;
     tdb->canPack = ct->tdb->canPack;
     trackDbPolish(tdb);
     slAddHead(&tdbList, tdb);
     }
 
 slReverse(&tdbList);
 return(tdbList);
 }
 
 
 struct customTrack *lookupCt(char *name)
 /* Return custom track for name, or NULL. */
 {
 struct customTrack *ct;
 
 for (ct=getCtList();  ct != NULL;  ct=ct->next)
     if (sameString(name, ct->tdb->track))
 	return(ct);
 
 return(NULL);
 }
 
 
 void parseSs(char *ss, char **retPslName, char **retFaName, char **retQName)
 /* Parse space separated 'ss' item. */
 {
 static char buf[512*2];
 int wordCount;
 char *words[4];
 strcpy(buf, ss);
 wordCount = chopLine(buf, words);
 
 if (wordCount < 1)
     errAbort("Empty user cart variable ss.");
 *retPslName = words[0];
 if (retFaName != NULL)
     {
     if (wordCount < 2)
 	errAbort("Expecting psl filename and fa filename in cart variable ss, but only got one word: %s", ss);
     *retFaName = words[1];
     }
 if (retQName != NULL)
     {
     if (wordCount < 3)
 	errAbort("Expecting psl filename, fa filename and query name in cart variable ss, but got this: %s", ss);
     *retQName = words[2];
     }
 }
 
 boolean ssFilesExist(char *ss)
 /* Return TRUE if both files in ss exist. -- Copied from hgTracks! */
 {
 char *faFileName, *pslFileName;
 parseSs(ss, &pslFileName, &faFileName, NULL);
 return fileExists(pslFileName) && fileExists(faFileName);
 }
 
 struct trackDb *tdbForUserPsl()
 /* Load up user's BLAT results into trackDb. */
 {
 char *ss = cartOptionalString(cart, "ss");
 
 if ((ss != NULL) && !ssFilesExist(ss))
     {
     ss = NULL;
     cartRemove(cart, "ss");
     }
 
 if (ss == NULL)
     return(NULL);
 else
     {
     struct trackDb *tdb;
     AllocVar(tdb);
     tdb->track = cloneString(USER_PSL_TRACK_NAME);
     tdb->table = cloneString(USER_PSL_TRACK_NAME);
     tdb->shortLabel = cloneString(USER_PSL_TRACK_LABEL);
     tdb->type = cloneString("psl");
     tdb->longLabel = cloneString(USER_PSL_TRACK_LONGLABEL);
     tdb->visibility = tvFull;
     tdb->priority = 11.0;
     trackDbPolish(tdb);
     return(tdb);
     }
 }
 
 struct trackDb *rFindUnderstandableTrack(char *db, struct trackDb *tdb)
 // If any leaf is usable in getting DNA then that leaf's tdb is returned.
 {
 if (tdb->subtracks != NULL)
     return rFindUnderstandableTrack(db,tdb->subtracks);
 
 if (fbUnderstandTrack(db, tdb->table) && !dnaIgnoreTrack(tdb->table))
     return tdb;
 else
     return NULL;
 }
 
 boolean forestHasUnderstandableTrack(char *db, struct trackDb *tdb)
 // TRUE if any leaf is usable in getting DNA.
 {
 return (rFindUnderstandableTrack(db, tdb) != NULL);
 }
 
 
 void doGetDnaExtended1()
 /* Do extended case/color get DNA options. */
 {
 struct trackDb *tdbList = hTrackDb(database), *tdb;
 struct trackDb *ctdbList = tdbForCustomTracks();
 struct trackDb *utdbList = tdbForUserPsl();
 boolean revComp  = cartUsualBoolean(cart, "hgSeq.revComp", FALSE);
 boolean maskRep  = cartUsualBoolean(cart, "hgSeq.maskRepeats", FALSE);
 int padding5     = cartUsualInt(cart, "hgSeq.padding5", 0);
 int padding3     = cartUsualInt(cart, "hgSeq.padding3", 0);
 int lineWidth    = cartUsualInt(cart, "lineWidth", 60);
 char *casing     = cartUsualString(cart, "hgSeq.casing", "");
 char *repMasking = cartUsualString(cart, "hgSeq.repMasking", "");
 boolean caseUpper= FALSE;
 char *pos = NULL;
 
 
 ctdbList = slCat(ctdbList, tdbList);
 tdbList = slCat(utdbList, ctdbList);
 
 cartWebStart(cart, database, "Extended DNA Case/Color");
 
 if (NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos"))))
     hgParseChromRange(database, pos, &seqName, &winStart, &winEnd);
 if (winEnd - winStart > 1000000)
     {
     printf("Please zoom in to 1 million bases or less to color the DNA");
     return;
     }
 
 printf("<H1>Extended DNA Case/Color Options</H1>\n");
 puts(
      "Use this page to highlight features in genomic DNA text. "
      "DNA covered by a particular track can be highlighted by "
      "case, underline, bold, italic, or color.  See below for "
      "details about color, and for examples. <B>Tracks in "
      "&quot;hide&quot; display mode are not shown in the grid below.</B> <P>");
 
 if (cgiBooleanDefined("hgSeq.maskRepeats"))
     cartSetBoolean(cart, "hgSeq.maskRepeats", maskRep);
 if (*repMasking != 0)
     cartSetString(cart, "hgSeq.repMasking", repMasking);
 if (maskRep)
     {
     struct trackDb *rtdb;
     char *visString = cartOptionalString(cart, "rmsk");
     for (rtdb = tdbList;  rtdb != NULL;  rtdb=rtdb->next)
 	{
 	if (startsWith(rtdb->table, "rmsk"))
 	    break;
 	}
     printf("<P> <B>Note:</B> repeat masking style from previous page will <B>not</B> apply to this page.\n");
     if ((rtdb != NULL) &&
 	((visString == NULL) || !sameString(visString, "hide")))
 	printf("Use the case/color options for the RepeatMasker track below. <P>\n");
     else
 	printf("Unhide the RepeatMasker track in the genome browser, then return to this page and use the case/color options for the RepeatMasker track below. <P>\n");
     }
 cartSetInt(cart, "padding5", padding5);
 cartSetInt(cart, "padding3", padding3);
 if (sameString(casing, "upper"))
     caseUpper = TRUE;
 if (*casing != 0)
     cartSetString(cart, "hgSeq.casing", casing);
 
 printf("<FORM ACTION=\"%s\" METHOD=\"POST\">\n\n", hgcName());
 cartSaveSession(cart);
 cgiMakeHiddenVar("g", "htcGetDna3");
 
 if (NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos"))))
     {
     hgParseChromRange(database, pos, &seqName, &winStart, &winEnd);
     }
 puts("Position ");
 savePosInTextBox(seqName, winStart+1 - (revComp ? padding3 : padding5), winEnd + (revComp ? padding5 : padding3));
 printf(" Reverse complement ");
 cgiMakeCheckBox("hgSeq.revComp", revComp);
 printf("<BR>\n");
 printf("Letters per line ");
 cgiMakeIntVar("lineWidth", lineWidth, 3);
 printf(" Default case: ");
 cgiMakeRadioButton("hgSeq.casing", "upper", caseUpper);
 printf(" Upper ");
 cgiMakeRadioButton("hgSeq.casing", "lower", !caseUpper);
 printf(" Lower ");
 cgiMakeButton("Submit", "submit");
 printf("<BR>\n");
 printf("<TABLE BORDER=1>\n");
 printf("<TR><TD>Track<BR>Name</TD><TD>Toggle<BR>Case</TD><TD>Under-<BR>line</TD><TD>Bold</TD><TD>Italic</TD><TD>Red</TD><TD>Green</TD><TD>Blue</TD></TR>\n");
 for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
     {
     char *table = tdb->table;
     char *track = tdb->track;
     if ( sameString(USER_PSL_TRACK_NAME, table)
     ||   lookupCt(track) != NULL
     ||   (  tdbVisLimitedByAncestors(cart,tdb,TRUE,TRUE) != tvHide
          && forestHasUnderstandableTrack(database, tdb) ) )
         {
         char *visString = cartUsualString(cart, track, hStringFromTv(tdb->visibility));
          if (differentString(visString, "hide") && tdb->parent)
             {
             char *parentVisString = cartUsualString(cart, tdb->parentName,
                                         hStringFromTv(tdb->parent->visibility));
             if (sameString("hide", parentVisString))
                 visString = "hide";
             }
 	char buf[128];
 	if (sameString(visString, "hide"))
 	    {
 	    char varName[256];
 	    safef(varName, sizeof varName, "%s_case", track);
 	    cartSetBoolean(cart, varName, FALSE);
 	    safef(varName, sizeof varName, "%s_u", track);
 	    cartSetBoolean(cart, varName, FALSE);
 	    safef(varName, sizeof varName, "%s_b", track);
 	    cartSetBoolean(cart, varName, FALSE);
 	    safef(varName, sizeof varName, "%s_i", track);
 	    cartSetBoolean(cart, varName, FALSE);
 	    safef(varName, sizeof varName, "%s_red", track);
 	    cartSetInt(cart, varName, 0);
 	    safef(varName, sizeof varName, "%s_green", track);
 	    cartSetInt(cart, varName, 0);
 	    safef(varName, sizeof varName, "%s_blue", track);
 	    cartSetInt(cart, varName, 0);
 	    }
 	else
 	    {
 	    printf("<TR>");
 	    printf("<TD>%s</TD>", tdb->shortLabel);
 	    safef(buf, sizeof buf, "%s_case", tdb->track);
 	    printf("<TD>");
 	    cgiMakeCheckBox(buf, cartUsualBoolean(cart, buf, FALSE));
 	    printf("</TD>");
 	    safef(buf, sizeof buf, "%s_u", tdb->track);
 	    printf("<TD>");
 	    cgiMakeCheckBox(buf, cartUsualBoolean(cart, buf, FALSE));
 	    printf("</TD>");
 	    safef(buf, sizeof buf, "%s_b", tdb->track);
 	    printf("<TD>");
 	    cgiMakeCheckBox(buf, cartUsualBoolean(cart, buf, FALSE));
 	    printf("</TD>");
 	    safef(buf, sizeof buf, "%s_i", tdb->track);
 	    printf("<TD>");
 	    cgiMakeCheckBox(buf, cartUsualBoolean(cart, buf, FALSE));
 	    printf("</TD>");
 	    printf("<TD>");
 	    safef(buf, sizeof buf, "%s_red", tdb->track);
 	    cgiMakeIntVar(buf, cartUsualInt(cart, buf, 0), 3);
 	    printf("</TD>");
 	    printf("<TD>");
 	    safef(buf, sizeof buf, "%s_green", tdb->track);
 	    cgiMakeIntVar(buf, cartUsualInt(cart, buf, 0), 3);
 	    printf("</TD>");
 	    printf("<TD>");
 	    safef(buf, sizeof buf, "%s_blue", tdb->track);
 	    cgiMakeIntVar(buf, cartUsualInt(cart, buf, 0), 3);
 	    printf("</TD>");
 	    printf("</TR>\n");
 	    }
 	}
     }
 printf("</TABLE>\n");
 printf("</FORM>\n");
 if (hIsGsidServer())
     {
     printf("<H3>Coloring Information and Examples</H3>\n");
     puts("The color values range from 0 (darkest) to 255 (lightest) and are additive.\n");
     puts("The examples below show a few ways to highlight individual tracks, "
 	 "and their interplay. It's good to keep it simple at first. It's easy "
 	 "to make pretty, but completely cryptic, displays with this feature.");
     puts(
 	 "<UL>"
 	 "<LI>To put Genes in upper case red text, check the "
 	 "appropriate box in the Toggle Case column and set the color to pure "
 	 "red, RGB (255,0,0). Upon submitting, any Gene within the "
 	 "designated chromosomal interval will now appear in red capital letters.\n"
 	 "<LI>To see the overlap between Genes and InterPro Domains try "
 	 "setting the Genes/Regions to red (255,0,0) and InterPro to green (0,255,0). "
 	 "Places where the Genes and InterPro Domains overlap will be painted yellow "
 	 "(255,255,0).\n"
 	 "<LI>To get a level-of-coverage effect for tracks like Genes with "
 	 "multiple overlapping items, initially select a darker color such as deep "
 	 "green, RGB (0,64,0). Nucleotides covered by a single Gene will appear dark "
 	 "green, while regions covered with more Genes get progressively brighter &mdash; "
 	 "saturating at 4 Genes."
 	 "<LI>Another track can be used to mask unwanted features. Setting the "
 	 "InterPro track to RGB (255,255,255) will white-out Genes within InterPro "
 	 "domains. "
 	 "</UL>");
     puts("<H3>Further Details and Ideas</H3>");
     puts("<P>Copying and pasting the web page output to a text editor such as Word "
 	 "will retain upper case but lose colors and other formatting. That is still "
 	 "useful because other web tools such as "
 	 "<A HREF=\"http://www.ncbi.nlm.nih.gov/blast\" TARGET=_BLANK>NCBI Blast</A> "
 	 "can be set to ignore lower case.  To fully capture formatting such as color "
 	 "and underlining, view the output as \"source\" in your web browser, or download "
 	 "it, or copy the output page into an html editor.</P>");
     puts("<P>The default line width of 60 characters is standard, but if you have "
 	 "a reasonable sized monitor it's useful to set this higher - to 125 characters "
 	 "or more.  You can see more DNA at once this way, and fewer line breaks help "
 	 "in finding DNA strings using the web browser search function.</P>");
     }
 else
     {
     printf("<H3>Coloring Information and Examples</H3>\n");
     puts("The color values range from 0 (darkest) to 255 (lightest) and are additive.\n");
     puts("The examples below show a few ways to highlight individual tracks, "
 	 "and their interplay. It's good to keep it simple at first.  It's easy "
 	 "to make pretty, but completely cryptic, displays with this feature.");
     puts(
 	 "<UL>"
 	 "<LI>To put exons from RefSeq Genes in upper case red text, check the "
 	 "appropriate box in the Toggle Case column and set the color to pure "
 	 "red, RGB (255,0,0). Upon submitting, any RefSeq Gene within the "
 	 "designated chromosomal interval will now appear in red capital letters.\n"
 	 "<LI>To see the overlap between RefSeq Genes and Genscan predictions try "
 	 "setting the RefSeq Genes to red (255,0,0) and Genscan to green (0,255,0). "
 	 "Places where the RefSeq Genes and Genscan overlap will be painted yellow "
 	 "(255,255,0).\n"
 	 "<LI>To get a level-of-coverage effect for tracks like Spliced Ests with "
 	 "multiple overlapping items, initially select a darker color such as deep "
 	 "green, RGB (0,64,0). Nucleotides covered by a single EST will appear dark "
 	 "green, while regions covered with more ESTs get progressively brighter &mdash; "
 	 "saturating at 4 ESTs."
 	 "<LI>Another track can be used to mask unwanted features. Setting the "
 	 "RepeatMasker track to RGB (255,255,255) will white-out Genscan predictions "
 	 "of LINEs but not mainstream host genes; masking with RefSeq Genes will show "
 	 "what is new in the gene prediction sector."
 	 "</UL>");
     puts("<H3>Further Details and Ideas</H3>");
     puts("<P>Copying and pasting the web page output to a text editor such as Word "
 	 "will retain upper case but lose colors and other formatting. That is still "
 	 "useful because other web tools such as "
 	 "<A HREF=\"http://www.ncbi.nlm.nih.gov/blast\" TARGET=_BLANK>NCBI Blast</A> "
 	 "can be set to ignore lower case.  To fully capture formatting such as color "
 	 "and underlining, view the output as \"source\" in your web browser, or download "
 	 "it, or copy the output page into an html editor.</P>");
     puts("<P>The default line width of 60 characters is standard, but if you have "
 	 "a reasonable sized monitor it's useful to set this higher - to 125 characters "
 	 "or more.  You can see more DNA at once this way, and fewer line breaks help "
 	 "in finding DNA strings using the web browser search function.</P>");
     puts("<P>Be careful about requesting complex formatting for a very large "
 	 "chromosomal region.  After all the html tags are added to the output page, "
 	 "the file size may exceed size limits that your browser, clipboard, and "
 	 "other software can safely display.  The tool will format 10 Mb and more, however.</P>");
     }
 trackDbFreeList(&tdbList);
 }
 
 void doGetBlastPep(char *readName, char *table)
 /* get predicted protein */
 {
 int qStart;
 struct psl *psl;
 int start, end;
 struct sqlResult *sr;
 struct sqlConnection *conn = hAllocConn(database);
 struct dnaSeq *tSeq;
 char query[256], **row;
 char fullTable[64];
 boolean hasBin;
 char *buffer, *str;
 int i, j;
 char *ptr;
 
 start = cartInt(cart, "o");
 hFindSplitTable(database, seqName, table, fullTable, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where qName = '%s' and tName = '%s' and tStart=%d",
 	fullTable, readName, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find alignment for %s at %d", readName, start);
 psl = pslLoad(row+hasBin);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printf("<PRE><TT>");
 end = psl->tEnd;
 if (psl->strand[1] == '+')
     end = psl->tStarts[psl->blockCount - 1] + psl->blockSizes[psl->blockCount - 1] *3;
 if ((ptr = strchr(readName, '.')) != NULL)
     *ptr++ = 0;
 
 printf(">%s-%s\n", readName,database);
 tSeq = hDnaFromSeq(database, psl->tName, start, end, dnaLower);
 
 if (psl->strand[1] == '-')
     {
     start = psl->tSize - end;
     reverseComplement(tSeq->dna, tSeq->size);
     }
 
 str = buffer = needMem(psl->qSize + 1);
 
 qStart = 0;
 for (i=0; i<psl->blockCount; ++i)
     {
     int ts = psl->tStarts[i] - start;
     int sz = psl->blockSizes[i];
 
     for (;qStart < psl->qStarts[i]; qStart++)
 	*str++ = 'X';
 
     for (j=0; j<sz; ++j)
 	{
 	int codonStart = ts + 3*j;
 	DNA *codon = &tSeq->dna[codonStart];
 	if ((*str = lookupCodon(codon)) == 0)
 	    *str = '*';
 	str++;
 	qStart++;
 	}
     }
 
 *str = 0;
 printLines(stdout, buffer, 50);
 printf("</TT></PRE>");
 }
 
 
 void doGetDna2()
 /* Do second DNA dialog (or just fetch DNA) */
 {
 char *tbl = cgiUsualString("table", "");
 char *action = cgiUsualString("submit", "");
 int itemCount;
 char *pos = NULL;
 char *chrom = NULL;
 int start = 0;
 int end = 0;
 
 if (sameString(action, EXTENDED_DNA_BUTTON))
     {
     doGetDnaExtended1();
     return;
     }
 // This output probably should be just text/plain but
 // trying to support the fancy warn handler box requires html.
 // But we want to keep it very simple and close to a plain text dump.
 
 cartHtmlStart("DNA");
 hgBotDelay();
 puts("<PRE>");
 if (tbl[0] == 0)
     {
     itemCount = 1;
     if ( NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos"))) &&
          hgParseChromRange((dbIsFound ? database : NULL), pos, &chrom, &start, &end))
         {
         hgSeqRange(database, chrom, start, end, '?', "dna");
         }
     else
         {
         hgSeqRange(database, seqName, cartInt(cart, "l"), cartInt(cart, "r"),
                    '?', "dna");
         }
     }
 else
     {
     struct hTableInfo *hti = NULL;
     char rootName[256];
     char parsedChrom[32];
 
     /* use the values from the dnaPos dialog box */
     if (!( NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos"))) &&
          hgParseChromRange(database, pos, &chrom, &start, &end)))
 	 {
 	 /* if can't get DnaPos from dialog box, use "o" and "t" */
 	 start = cartInt(cart, "o");
 	 end = cartInt(cart, "t");
 	 }
 
     /* Table might be a custom track if it's not in the database,
      * or bigBed if it is in the database but has only one column called 'fileName';
      * in which case, just get DNA as if no table were given. */
     hParseTableName(database, tbl, rootName, parsedChrom);
     if (!trackHubDatabase(database))
 	hti = hFindTableInfo(database, seqName, rootName);
     if (hti == NULL || hti->startField[0] == 0)
 	{
 	itemCount = 1;
 	hgSeqRange(database, seqName, start, end, '?', tbl);
 	}
     else
 	{
 	char *where = NULL;
 	char *item = cgiUsualString("i", "");
 	char buf[256];
 	if ((hti->nameField[0] != 0) && (item[0] != 0))
 	    {
 	    char *quotedItem = makeQuotedString(item, '\'');
 	    safef(buf, sizeof(buf), "%s = %s", hti->nameField, quotedItem);
 	    where = buf;
 	    freeMem(quotedItem);
 	    }
 	itemCount = hgSeqItemsInRange(database, tbl, seqName, start, end, where);
 	}
     }
 if (itemCount == 0)
     printf("\n# No results returned from query.\n\n");
 
 puts("</PRE>");
 }
 
 struct hTableInfo *ctToHti(struct customTrack *ct)
 /* Create an hTableInfo from a customTrack. */
 {
 struct hTableInfo *hti;
 
 AllocVar(hti);
 hti->rootName = cloneString(ct->tdb->table);
 hti->isPos = TRUE;
 hti->isSplit = FALSE;
 hti->hasBin = FALSE;
 hti->type = cloneString(ct->tdb->type);
 int fieldCount = 3;
 if (sameWord(ct->dbTrackType, "bedDetail"))
     fieldCount = ct->fieldCount - 2;
 else if (sameWord(ct->dbTrackType, "pgSnp"))
     fieldCount = 4;
 else
     fieldCount = ct->fieldCount;
 if (fieldCount >= 3)
     {
     strncpy(hti->chromField, "chrom", 32);
     strncpy(hti->startField, "chromStart", 32);
     strncpy(hti->endField, "chromEnd", 32);
     }
 if (fieldCount >= 4)
     {
     strncpy(hti->nameField, "name", 32);
     }
 if (fieldCount >= 5)
     {
     strncpy(hti->scoreField, "score", 32);
     }
 if (fieldCount >= 6)
     {
     strncpy(hti->strandField, "strand", 32);
     }
 if (fieldCount >= 8)
     {
     strncpy(hti->cdsStartField, "thickStart", 32);
     strncpy(hti->cdsEndField, "thickEnd", 32);
     hti->hasCDS = TRUE;
     }
 if (fieldCount >= 12)
     {
     strncpy(hti->countField, "blockCount", 32);
     strncpy(hti->startsField, "chromStarts", 32);
     strncpy(hti->endsSizesField, "blockSizes", 32);
     hti->hasBlocks = TRUE;
     }
 
 return(hti);
 }
 
 struct hTableInfo *htiForUserPsl()
 /* Create an hTableInfo for user's BLAT results. */
 {
 struct hTableInfo *hti;
 
 AllocVar(hti);
 hti->rootName = cloneString(USER_PSL_TRACK_NAME);
 hti->isPos = TRUE;
 hti->isSplit = FALSE;
 hti->hasBin = FALSE;
 hti->type = cloneString("psl");
 strncpy(hti->chromField, "tName", 32);
 strncpy(hti->startField, "tStart", 32);
 strncpy(hti->endField, "tEnd", 32);
 strncpy(hti->nameField, "qName", 32);
 /* psl can be scored... but strictly speaking, does not have a score field! */
 strncpy(hti->strandField, "strand", 32);
 hti->hasCDS = FALSE;
 strncpy(hti->countField, "blockCount", 32);
 strncpy(hti->startsField, "tStarts", 32);
 strncpy(hti->endsSizesField, "tSizes", 32);
 hti->hasBlocks = TRUE;
 
 return(hti);
 }
 
 struct bed *bedFromUserPsl()
 /* Load up user's BLAT results into bedList. */
 {
 struct bed *bedList = NULL;
 char *ss = cartOptionalString(cart, "ss");
 
 if ((ss != NULL) && ! ssFilesExist(ss))
     {
     ss = NULL;
     cartRemove(cart, "ss");
     }
 
 if (ss == NULL)
     return(NULL);
 else
     {
     struct lineFile *f;
     struct psl *psl;
     enum gfType qt, tt;
     char *faFileName, *pslFileName;
     int i;
 
     parseSs(ss, &pslFileName, &faFileName, NULL);
     pslxFileOpen(pslFileName, &qt, &tt, &f);
     while ((psl = pslNext(f)) != NULL)
 	{
 	struct bed *bed;
 	AllocVar(bed);
 	bed->chrom = cloneString(seqName);
 	bed->chromStart = psl->tStart;
 	bed->chromEnd = psl->tEnd;
 	bed->name = cloneString(psl->qName);
 	bed->score = pslScore(psl);
 	if ((psl->strand[0] == '-' && psl->strand[1] == '+') ||
 	    (psl->strand[0] == '+' && psl->strand[1] == '-'))
 	    strncpy(bed->strand, "-", 2);
 	else
 	    strncpy(bed->strand, "+", 2);
 	bed->thickStart = bed->chromStart;
 	bed->thickEnd   = bed->chromEnd;
 	bed->blockCount = psl->blockCount;
 	bed->chromStarts = needMem(bed->blockCount * sizeof(int));
 	bed->blockSizes  = needMem(bed->blockCount * sizeof(int));
 	for (i=0;  i < bed->blockCount;  i++)
 	    {
 	    bed->chromStarts[i] = psl->tStarts[i];
 	    bed->blockSizes[i]  = psl->blockSizes[i];
 	    }
 	if (qt == gftProt)
 	    for (i=0;  i < bed->blockCount;  i++)
 		{
 		/* If query is protein, blockSizes are in aa units; fix 'em. */
 		bed->blockSizes[i] *= 3;
 		}
 	if (psl->strand[1] == '-')
 	    {
 	    /* psl: if target strand is '-', flip the coords.
 	     * (this is the target part of pslRc from src/lib/psl.c) */
 	    for (i=0;  i < bed->blockCount;  ++i)
 		{
 		bed->chromStarts[i] =
 		    psl->tSize - (bed->chromStarts[i] +
 				  bed->blockSizes[i]);
 		}
 	    reverseInts(bed->chromStarts, bed->blockCount);
 	    reverseInts(bed->blockSizes, bed->blockCount);
 	    assert(bed->chromStart == bed->chromStarts[0]);
 	    }
         /* translate absolute starts to relative starts (after handling
          * target-strand coord-flipping) */
 	for (i=0;  i < bed->blockCount;  i++)
 	    {
 	    bed->chromStarts[i] -= bed->chromStart;
 	    }
 	slAddHead(&bedList, bed);
 	pslFree(&psl);
 	}
     lineFileClose(&f);
     slReverse(&bedList);
     return(bedList);
     }
 }
 
 
 void addColorToRange(int r, int g, int b, struct rgbColor *colors, int start, int end)
 /* Add rgb values to colors array from start to end.  Don't let values
  * exceed 255 */
 {
 struct rgbColor *c;
 int rr, gg, bb;
 int i;
 for (i=start; i<end; ++i)
     {
     c = colors+i;
     rr = c->r + r;
     if (rr > 255) rr = 255;
     c->r = rr;
     gg = c->g + g;
     if (gg > 255) gg = 255;
     c->g = gg;
     bb = c->b + b;
     if (bb > 255) bb = 255;
     c->b = bb;
     }
 }
 
 void getDnaHandleBits(char *track, char *type, Bits *bits,
                       int winStart, int winEnd, boolean isRc,
                       struct featureBits *fbList)
 /* See if track_type variable exists, and if so set corresponding bits. */
 {
 char buf[256];
 struct featureBits *fb;
 int s,e;
 int winSize = winEnd - winStart;
 
 safef(buf, sizeof buf, "%s_%s", track, type);
 if (cgiBoolean(buf))
     {
     for (fb = fbList; fb != NULL; fb = fb->next)
 	{
 	s = fb->start - winStart;
 	e = fb->end - winStart;
 	if (isRc)
 	    reverseIntRange(&s, &e, winSize);
 	bitSetRange(bits, s, e - s);
 	}
     }
 }
 
 void doGetDna3()
 /* Fetch DNA in extended color format */
 {
 struct dnaSeq *seq;
 struct cfm *cfm;
 int i;
 boolean isRc = cartUsualBoolean(cart, "hgSeq.revComp", FALSE);
 boolean defaultUpper = sameString(cartString(cart, "hgSeq.casing"), "upper");
 int winSize;
 int lineWidth = cartInt(cart, "lineWidth");
 struct rgbColor *colors;
 struct trackDb *tdbList = hTrackDb(database), *tdb;
 struct trackDb *ctdbList = tdbForCustomTracks();
 struct trackDb *utdbList = tdbForUserPsl();
 char *pos = NULL;
 Bits *uBits;	/* Underline bits. */
 Bits *iBits;    /* Italic bits. */
 Bits *bBits;    /* Bold bits. */
 
 if (NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos"))))
     hgParseChromRange(database, pos, &seqName, &winStart, &winEnd);
 
 winSize = winEnd - winStart;
 uBits = bitAlloc(winSize);	/* Underline bits. */
 iBits = bitAlloc(winSize);	/* Italic bits. */
 bBits = bitAlloc(winSize);	/* Bold bits. */
 
 ctdbList = slCat(ctdbList, tdbList);
 tdbList = slCat(utdbList, ctdbList);
 
 cartWebStart(cart, database, "Extended DNA Output");
 printf("<PRE><TT>");
 printf(">%s:%d-%d %s\n", seqName, winStart+1, winEnd,
        (isRc ? "(reverse complement)" : ""));
 seq = hDnaFromSeq(database, seqName, winStart, winEnd, dnaLower);
 if (isRc)
     reverseComplement(seq->dna, seq->size);
 if (defaultUpper)
     touppers(seq->dna);
 
 AllocArray(colors, winSize);
 for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
     {
     char *track = tdb->track;
     char *table = tdb->table;
     struct featureBits *fbList = NULL, *fb;
     struct customTrack *ct = lookupCt(track);
     if (sameString(USER_PSL_TRACK_NAME, table)
     ||  ct != NULL
     ||  (   tdbVisLimitedByAncestors(cart,tdb,TRUE,TRUE) != tvHide
         && forestHasUnderstandableTrack(database, tdb) ) )
         {
         char buf[256];
         int r,g,b;
         /* to save a LOT of time, don't fetch track features unless some
          * coloring/formatting has been specified for them. */
 	boolean hasSettings = FALSE;
 	safef(buf, sizeof(buf), "%s_u", track);
 	hasSettings |= cgiBoolean(buf);
 	safef(buf, sizeof(buf), "%s_b", track);
 	hasSettings |= cgiBoolean(buf);
 	safef(buf, sizeof(buf), "%s_i", track);
 	hasSettings |= cgiBoolean(buf);
 	safef(buf, sizeof(buf), "%s_case", track);
 	hasSettings |= cgiBoolean(buf);
 	safef(buf, sizeof(buf), "%s_red", track);
 	hasSettings |= (cgiOptionalInt(buf, 0) != 0);
 	safef(buf, sizeof(buf), "%s_green", track);
 	hasSettings |= (cgiOptionalInt(buf, 0) != 0);
 	safef(buf, sizeof(buf), "%s_blue", track);
 	hasSettings |= (cgiOptionalInt(buf, 0) != 0);
 	if (! hasSettings)
 	    continue;
 
 	if (sameString(USER_PSL_TRACK_NAME, track))
 	    {
 	    struct hTableInfo *hti = htiForUserPsl();
 	    struct bedFilter *bf;
 	    struct bed *bedList, *bedList2;
 	    AllocVar(bf);
 	    bedList = bedFromUserPsl();
 	    bedList2 = bedFilterListInRange(bedList, bf, seqName, winStart,
 					    winEnd);
 	    fbList = fbFromBed(database, track, hti, bedList2, winStart, winEnd,
 			       TRUE, FALSE);
 	    bedFreeList(&bedList);
 	    bedFreeList(&bedList2);
 	    }
 	else if (ct != NULL)
 	    {
 	    struct hTableInfo *hti = ctToHti(ct);
 	    struct bedFilter *bf;
 	    struct bed *bedList2, *ctBedList = NULL;
 	    AllocVar(bf);
             if (ct->dbTrack)
                 {
                 struct bed *bed;
                 int fieldCount = ct->fieldCount;
                 char query[512];
                 int rowOffset;
                 char **row;
                 struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
                 struct sqlResult *sr = NULL;
 
                 sqlSafef(query, sizeof(query), "select * from %s", ct->dbTableName);
                 sr = hRangeQuery(conn, ct->dbTableName, seqName,
                     winStart, winEnd, NULL, &rowOffset);
                 while ((row = sqlNextRow(sr)) != NULL)
                     {
                     bed = bedLoadN(row+rowOffset, fieldCount);
                     if (bf == NULL || bedFilterOne(bf, bed))
                         {
                         struct bed *copy = cloneBed(bed);
                         slAddHead(&ctBedList, copy);
                         }
                     }
                 sqlFreeResult(&sr);
                 hFreeConn(&conn);
                 }
             else
                 {
                 ctBedList = ct->bedList;
                 }
 	    bedList2 = bedFilterListInRange(ctBedList, bf, seqName, winStart,
 					    winEnd);
 	    fbList = fbFromBed(database, track, hti, bedList2, winStart, winEnd,
 			       TRUE, FALSE);
 	    bedFreeList(&bedList2);
             if (!ct->bedList)
                 bedFreeList(&ctBedList);
 	    }
 	else
             {
             if (tdb->subtracks)
                 {
                 struct slRef *refLeaves = trackDbListGetRefsToDescendantLeaves(tdb->subtracks);
                 struct slRef *refLeaf = NULL;
                 while ((refLeaf = slPopHead(&refLeaves)) != NULL)
                     {
                     struct trackDb *tdbLeaf = refLeaf->val;
                     if (tdbVisLimitedByAncestors(cart,tdbLeaf,TRUE,TRUE) != tvHide
                     &&  fbUnderstandTrack(database, tdbLeaf->table)
                     && !dnaIgnoreTrack(tdbLeaf->table))
                         {
                         struct featureBits *fbLeafList =
                                     fbGetRange(database, tdbLeaf->table, seqName, winStart, winEnd);
                         if (fbLeafList != NULL)
                             fbList = slCat(fbList,fbLeafList);
                         }
                     freeMem(refLeaf);
                     }
                 }
             else
                 fbList = fbGetRange(database, tdb->table, seqName, winStart, winEnd);
             }
 
         /* Flip underline/italic/bold bits. */
         getDnaHandleBits(track, "u", uBits, winStart, winEnd, isRc, fbList);
         getDnaHandleBits(track, "b", bBits, winStart, winEnd, isRc, fbList);
 	getDnaHandleBits(track, "i", iBits, winStart, winEnd, isRc, fbList);
 
 	/* Toggle case if necessary. */
 	safef(buf, sizeof buf, "%s_case", track);
 	if (cgiBoolean(buf))
 	    {
 	    for (fb = fbList; fb != NULL; fb = fb->next)
 	        {
 		DNA *dna;
 		int start = fb->start - winStart;
 		int end  = fb->end - winStart;
 		int size = fb->end - fb->start;
 		if (isRc)
 		    reverseIntRange(&start, &end, seq->size);
 		dna = seq->dna + start;
 		if (defaultUpper)
 		    toLowerN(dna, size);
 		else
 		    toUpperN(dna, size);
 		}
 	    }
 
 	/* Add in RGB values if necessary. */
 	safef(buf, sizeof buf, "%s_red", track);
 	r = cartInt(cart, buf);
 	safef(buf, sizeof buf, "%s_green", track);
 	g = cartInt(cart, buf);
 	safef(buf, sizeof buf, "%s_blue", track);
 	b = cartInt(cart, buf);
 	if (r != 0 || g != 0 || b != 0)
 	    {
 	    for (fb = fbList; fb != NULL; fb = fb->next)
 	        {
 		int s = fb->start - winStart;
 		int e = fb->end - winStart;
 		if (isRc)
 		    reverseIntRange(&s, &e, winEnd - winStart);
 		addColorToRange(r, g, b, colors, s, e);
 		}
 	    }
 	}
     }
 
 cfm = cfmNew(0, lineWidth, FALSE, FALSE, stdout, 0);
 for (i=0; i<seq->size; ++i)
     {
     struct rgbColor *color = colors+i;
     int c = (color->r<<16) + (color->g<<8) + color->b;
     cfmOutExt(cfm, seq->dna[i], c,
 	      bitReadOne(uBits, i), bitReadOne(bBits, i), bitReadOne(iBits, i));
     }
 cfmFree(&cfm);
 freeDnaSeq(&seq);
 bitFree(&uBits);
 bitFree(&iBits);
 bitFree(&bBits);
 }
 
 void medlineLinkedTermLine(char *title, char *text, char *search, char *keyword)
 /* Produce something that shows up on the browser as
  *     TITLE: value
  * with the value hyperlinked to medline using a specified search term. */
 {
 char *encoded = cgiEncode(search);
 char *encodedKeyword = cgiEncode(keyword);
 
 printf("<B>%s:</B> ", title);
 if (sameWord(text, "n/a") || sameWord(text, "none"))
     printf("n/a<BR>\n");
 else
     {
     printf("<A HREF=\"");
     printEntrezPubMedPureSearchUrl(stdout, encoded, encodedKeyword);
     printf("\" TARGET=_blank>%s</A><BR>\n", text);
     }
 freeMem(encoded);
 }
 void medlineLinkedLine(char *title, char *text, char *search)
 /* Produce something that shows up on the browser as
  *     TITLE: value
  * with the value hyperlinked to medline. */
 {
 char *encoded = cgiEncode(search);
 
 printf("<B>%s:</B> ", title);
 if (sameWord(text, "n/a"))
     printf("n/a<BR>\n");
 else
     {
     printf("<A HREF=\"");
     printEntrezPubMedUrl(stdout, encoded);
     printf("\" TARGET=_blank>%s</A><BR>\n", text);
     }
 freeMem(encoded);
 }
 
 void medlineProductLinkedLine(char *title, char *text)
 /* Produce something that shows up on the browser as
  *     TITLE: value
  * with the value hyperlinked to medline.
  * Replaces commas in the product name with spaces, as commas sometimes
  * interfere with PubMed search */
 {
     subChar(text, ',', ' ');
     medlineLinkedLine(title, text, text);
 }
 
 void appendAuthor(struct dyString *dy, char *gbAuthor, int len)
 /* Convert from  Kent,W.J. to Kent WJ and append to dy.
  * gbAuthor gets eaten in the process.
  * Also strip web URLs since Entrez doesn't like those. */
 {
 char buf[2048];
 char *ptr;
 
 if (len >= sizeof(buf))
     warn("author %s too long to process", gbAuthor);
 else
     {
     memcpy(buf, gbAuthor, len);
     buf[len] = 0;
     stripChar(buf, '.');
     subChar(buf, ',' , ' ');
     if ((ptr = strstr(buf, " http://")) != NULL)
         *ptr = 0;
     dyStringAppend(dy, buf);
     dyStringAppend(dy, " ");
     }
 }
 
 void gbToEntrezAuthor(char *authors, struct dyString *dy)
 /* Convert from Genbank author format:
  *      Kent,W.J., Haussler,D. and Zahler,A.M.
  * to Entrez search format:
  *      Kent WJ,Haussler D,Zahler AM
  */
 {
 char *s = authors, *e;
 
 /* Parse first authors, which will be terminated by '.,' */
 while ((e = strstr(s, ".,i ")) != NULL)
     {
     int len = e - s + 1;
     appendAuthor(dy, s, len);
     s += len+2;
     }
 if ((e = strstr(s, " and")) != NULL)
     {
     int len = e - s;
     appendAuthor(dy, s, len);
     s += len+4;
     }
 if ((s = skipLeadingSpaces(s)) != NULL && s[0] != 0)
     {
     int len = strlen(s);
     appendAuthor(dy, s, len);
     }
 }
 
 /* --- !!! Riken code is under development Fan. 4/16/02 */
 void printRikenInfo(char *acc, struct sqlConnection *conn )
 /* Print Riken annotation info */
 {
 struct sqlResult *sr;
 char **row;
 char query[512];
 char *seqid, *accession, *comment;
 // char *qualifier, *anntext, *datasrc, *srckey, *href, *evidence;
 
 accession = acc;
 sqlSafef(query, sizeof(query),
          "select seqid from rikenaltid where altid='%s';", accession);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 
 if (row != NULL)
     {
     seqid=cloneString(row[0]);
 
     sqlSafef(query, sizeof(query),
              "select Qualifier, Anntext, Datasrc, Srckey, Href, Evidence "
              "from rikenann where seqid='%s';", seqid);
 
     sqlFreeResult(&sr);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
 
     while (row !=NULL)
 	{
 	// qualifier = row[0];  unused variable
 	// anntext   = row[1];  unused variable
 	// datasrc   = row[2];  unused variable
 	// srckey    = row[3];  unused variable
 	// href      = row[4];  unused variable
         // evidence  = row[5];  unused variable
         row = sqlNextRow(sr);
         }
 
     sqlSafef(query, sizeof(query),
              "select comment from rikenseq where id='%s';", seqid);
     sqlFreeResult(&sr);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
 
     if (row != NULL)
 	{
 	comment = row[0];
 	printf("<B>Riken/comment:</B> %s<BR>\n",comment);
 	}
     }
 }
 
 void printGeneCards(char *geneName)
 /* Print out a link to GeneCards (Human only). */
 {
 if (startsWith("hg", database) && isNotEmpty(geneName))
     {
     printf("<B>GeneCards:</B> "
 	   "<A HREF = \"http://www.genecards.org/cgi-bin/cardsearch.pl?"
 	   "search=%s\" TARGET=_blank>%s</A><BR>\n",
 	   geneName, geneName);
     }
 }
 
 int getImageId(struct sqlConnection *conn, char *acc)
 /* get the image id for a clone, or 0 if none */
 {
 int imageId = 0;
 if (sqlTableExists(conn, imageCloneTable))
     {
     struct sqlResult *sr;
     char **row;
     char query[128];
     sqlSafef(query, sizeof(query),
           "select imageId from %s where acc = '%s'",imageCloneTable, acc);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         imageId = sqlUnsigned(row[0]);
     sqlFreeResult(&sr);
     }
 return imageId;
 }
 
 void htcDisplayMrna(char *acc)
 /* Display mRNA available from genback or seq table.. */
 {
 struct dnaSeq *seq = hGenBankGetMrna(database, acc, NULL);
 if (seq == NULL)
     errAbort("mRNA sequence %s not found", acc);
 
 cartHtmlStart("mRNA sequence");
 printf("<PRE><TT>");
 faWriteNext(stdout, seq->name, seq->dna, seq->size);
 printf("</TT></PRE>");
 dnaSeqFree(&seq);
 }
 
 static int getEstTranscriptionDir(struct sqlConnection *conn, struct psl *psl)
 /* get the direction of transcription for an EST; return splice support count */
 {
 char query[256], estOrient[64];
 sqlSafef(query, sizeof(query),
       "select intronOrientation from %s.estOrientInfo where chrom = '%s' and chromStart = %d and name = '%s'",
       database, psl->tName, psl->tStart, psl->qName);
 if (sqlQuickQuery(conn, query, estOrient, sizeof(estOrient)) != NULL)
     return sqlSigned(estOrient) * ((psl->strand[0] == '+') ? 1 : -1);
 else
     return 0;
 }
 
 static struct gbWarn *checkGbWarn(struct sqlConnection *conn, char *acc)
 /* check if there is a gbWarn entry for this accession, return NULL if none */
 {
 struct gbWarn *gbWarn = NULL;
 if (sqlTableExists(conn, gbWarnTable))
     gbWarn = sqlQueryObjs(conn, (sqlLoadFunc)gbWarnLoad, sqlQuerySingle,
                           "SELECT * FROM %s WHERE acc = \"%s\"", gbWarnTable, acc);
 return gbWarn;
 }
 
 static void printGbWarn(char *acc, struct gbWarn *gbWarn)
 /* print descriptive information about an accession in the gbWarn table */
 {
 char *msg = NULL;
 switch (gbWarn->reason) {
 case gbWarnInvitroNorm:
     msg = "is from the InVitroGen/Genoscope full-length library.  Some of the entries "
         "associated with this dataset appear to have been aligned to the reference "
         "genome and the sequences subsequently modified to match the genome. This "
         "process may have resulted in apparent high-quality alignments to pseudogenes.";
     break;
 case gbWarnAthRage:
     msg = "is from the Athersys RAGE library.  These sequences were created by inducing expression and may not "
         "be an indication of in vivo expression.";
     break;
 case gbWarnOrestes:
     msg = "is from an ORESTES library.  This protocol includes a PCR step subject to genomic contamination.";
     break;
 }
 assert(msg != NULL);
 char *msg2= "Care should be taken in using alignments of this sequence as evidence of transcription.";
 printf("<B>Warning:<span style='color:red;'> %s %s %s</span></B><BR>\n", acc, msg, msg2);
 }
 
 static void printRnaSpecs(struct trackDb *tdb, char *acc, struct psl *psl)
 /* Print auxiliarry info on RNA. */
 {
 struct dyString *dy = newDyString(1024);
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn2= hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char rgdEstId[512];
 char query[256];
 char *type,*direction,*orgFullName,*library,*clone,*sex,*tissue,
     *development,*cell,*cds,*description, *author,*geneName,
     *date,*productName;
 // char *source;  unused variable
 // int seqSize,fileSize;  unused variables
 // long fileOffset;  unused variable
 // char *extFile;    unused variable
 boolean hasVersion = hHasField(database, gbCdnaInfoTable, "version");
 boolean haveGbSeq = sqlTableExists(conn, gbSeqTable);
 char *seqTbl = haveGbSeq ? gbSeqTable : "seq";
 char *version = NULL;
 struct trackDb *tdbRgdEst;
 char *chrom = cartString(cart, "c");
 int start = cartInt(cart, "o");
 int end = cartUsualInt(cart, "t",0);
 struct gbWarn *gbWarn = checkGbWarn(conn, acc);
 
 /* This sort of query and having to keep things in sync between
  * the first clause of the select, the from clause, the where
  * clause, and the results in the row ... is really tedious.
  * One of my main motivations for going to a more object
  * based rather than pure relational approach in general,
  * and writing 'autoSql' to help support this.  However
  * the pure relational approach wins for pure search speed,
  * and these RNA fields are searched.  So it looks like
  * the code below stays.  Be really careful when you modify
  * it.
  *
  * Uses the gbSeq table if available, otherwise use seq for older databases.
  */
 sqlDyStringAppend(dy,
                "select g.type,g.direction,"
                "so.name,o.name,l.name,m.name,"
                "se.name,t.name,dev.name,ce.name,cd.name,"
                "des.name,a.name,gene.name,p.name,"
                "gbS.size,g.moddate,gbS.gbExtFile,gbS.file_offset,gbS.file_size ");
 
 /* If the gbCdnaInfoTAble table has a "version" column then will show it */
 if (hasVersion)
     {
     dyStringAppend(dy,
                    ", g.version ");
     }
 
 sqlDyStringPrintf(dy,
                " from %s g,%s gbS,%s so,%s o,%s l,%s m,%s se,%s t,"
                "%s dev,%s ce,%s cd,%s des,%s a,%s gene,%s p"
                " where g.acc = '%s' and g.id = gbS.id ",
                gbCdnaInfoTable,seqTbl, sourceTable, organismTable, libraryTable, mrnaCloneTable, sexTable, tissueTable, developmentTable, cellTable, cdsTable, descriptionTable, authorTable, geneNameTable, productNameTable,  acc);
 dyStringAppend(dy,
                "and g.source = so.id and g.organism = o.id "
                "and g.library = l.id and g.mrnaClone = m.id "
                "and g.sex = se.id and g.tissue = t.id "
                "and g.development = dev.id and g.cell = ce.id "
                "and g.cds = cd.id and g.description = des.id "
                "and g.author = a.id and g.geneName = gene.id "
                "and g.productName = p.id");
 
 sr = sqlMustGetResult(conn, dy->string);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     type=row[0];direction=row[1];
       // source=row[2];  unused variable
     orgFullName=row[3];library=row[4];clone=row[5];
     sex=row[6];tissue=row[7];development=row[8];cell=row[9];cds=row[10];description=row[11];
     author=row[12];geneName=row[13];productName=row[14];
     // seqSize = sqlUnsigned(row[15]);   unused variable
     date = row[16];
     // ext_file = row[17];  unused variable
     // fileOffset=sqlUnsigned(row[18]);  unused variable
     // fileSize=sqlUnsigned(row[19]);    unused variable
     boolean isEst = sameWord(type, "est");
 
     if (hasVersion)
         {
         version = row[20];
         }
 
 
     /* Now we have all the info out of the database and into nicely named
      * local variables.  There's still a few hoops to jump through to
      * format this prettily on the web with hyperlinks to NCBI. */
     printf("<H2>Information on %s <A HREF=\"",  type);
     if (isEst)
 	printEntrezEstUrl(stdout, acc);
     else
 	printEntrezNucleotideUrl(stdout, acc);
     printf("\" TARGET=_blank>%s</A></H2>\n", acc);
 
     printf("<B>Description:</B> %s<BR>\n", description);
     if (gbWarn != NULL)
         printGbWarn(acc, gbWarn);
 
     medlineLinkedLine("Gene", geneName, geneName);
     medlineProductLinkedLine("Product", productName);
     dyStringClear(dy);
     gbToEntrezAuthor(author, dy);
     medlineLinkedLine("Author", author, dy->string);
     printf("<B>Organism:</B> ");
     printf("<A href=\"http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?mode=Undef&name=%s&lvl=0&srchmode=1\" TARGET=_blank>",
 	   cgiEncode(orgFullName));
     printf("%s</A><BR>\n", orgFullName);
     printf("<B>Tissue:</B> %s<BR>\n", tissue);
     printf("<B>Development stage:</B> %s<BR>\n", development);
     printf("<B>Cell line:</B> %s<BR>\n", cell);
     printf("<B>Sex:</B> %s<BR>\n", sex);
     printf("<B>Library:</B> %s<BR>\n", library);
     printf("<B>Clone:</B> %s<BR>\n", clone);
     if (isEst)
         {
         printf("<B>Read direction: </B>");
         if (direction[0] != '0')
             printf("%s' (guessed from GenBank description)<BR>\n", direction);
         else
             printf("unknown (can't guess from GenBank description)<BR>");
         }
     else
         printf("<B>CDS:</B> %s<BR>\n", cds);
     printf("<B>Date:</B> %s<BR>\n", date);
     if (hasVersion)
         {
         printf("<B>Version:</B> %s<BR>\n", version);
         }
     /* print RGD EST Report link if it is Rat genome and it has a link to RGD */
     if (sameWord(organism, "Rat"))
 	{
         if (hTableExists(database, "rgdEstLink"))
             {
             sqlSafef(query, sizeof(query),
                      "select id from %s.rgdEstLink where name = '%s';",  database, acc);
             if (sqlQuickQuery(conn2, query, rgdEstId, sizeof(rgdEstId)) != NULL)
                 {
                 tdbRgdEst = hashFindVal(trackHash, "rgdEst");
                 printf("<B>RGD EST Report: ");
                 printf("<A HREF=\"%s%s\" target=_blank>", tdbRgdEst->url, rgdEstId);
                 printf("RGD:%s</B></A><BR>\n", rgdEstId);
                 }
             }
         }
     if (isEst && hTableExists(database, "estOrientInfo") && (psl != NULL))
         {
         int estOrient = getEstTranscriptionDir(conn2, psl);
         if (estOrient != 0)
             printf("<B>EST transcribed from %c strand </B>(supported by %d splice sites).<BR>\n",
                    (estOrient > 0 ? '+' : '-' ), abs(estOrient));
         }
     if (hGenBankHaveSeq(database, acc, NULL))
         {
         printf("<B>%s sequence:</B> ", type);
         hgcAnchorSomewhere("htcDisplayMrna", acc, tdb->track, seqName);
         printf("%s</A><BR>\n", acc);
         }
     }
 else
     {
     warn("Couldn't find %s in %s table", gbCdnaInfoTable, acc);
     }
 if (end != 0 && differentString(chrom,"0") && isNotEmpty(chrom))
     {
     printf("<B>Position:</B> "
            "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
                   hgTracksPathAndSettings(), database, chrom, start+1, end);
     printf("%s:%d-%d</A><BR>\n", chrom, start+1, end);
     }
 
 gbWarnFree(&gbWarn);
 sqlFreeResult(&sr);
 freeDyString(&dy);
 hFreeConn(&conn);
 hFreeConn(&conn2);
 }
 
 static boolean isPslToPrintByClick(struct psl *psl, int startFirst, boolean isClicked)
 /* Determine if a psl should be printed based on if it was or was not the one that was clicked
  * on.
  */
 {
 return ((psl->tStart == startFirst) && sameString(psl->tName, seqName)) == isClicked;
 }
 
 void printAlignmentsSimple(struct psl *pslList, int startFirst, char *hgcCommand,
                            char *tableName, char *itemIn)
 /* Print list of mRNA alignments, don't add extra textual link when
  * doesn't honor hgcCommand. */
 {
 struct psl *psl;
 int aliCount = slCount(pslList);
 boolean isClicked;
 if (pslList == NULL || tableName == NULL)
     return;
 
 if (aliCount > 1)
     printf("The alignment you clicked on is first in the table below.<BR>\n");
 
 printf("<PRE><TT>");
 if (startsWith("chr", pslList->tName))
     printf("BROWSER | SIZE IDENTITY CHROMOSOME  STRAND    START     END              QUERY      START  END  TOTAL\n");
 else
     printf("BROWSER | SIZE IDENTITY  SCAFFOLD   STRAND    START     END              QUERY      START  END  TOTAL\n");
 printf("-----------------------------------------------------------------------------------------------------\n");
 for (isClicked = 1; isClicked >= 0; isClicked -= 1)
     {
     for (psl = pslList; psl != NULL; psl = psl->next)
 	{
 	if (isPslToPrintByClick(psl, startFirst, isClicked))
 	    {
             char otherString[512];
 	    safef(otherString, sizeof(otherString), "%d&aliTable=%s", psl->tStart, tableName);
             printf("<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">browser</A> | ",
                    hgTracksPathAndSettings(), database, psl->tName, psl->tStart+1, psl->tEnd);
 	    hgcAnchorWindow(hgcCommand, itemIn, psl->tStart, psl->tEnd,  otherString, psl->tName);
 	    printf("%5d  %5.1f%%  %9s     %s %9d %9d  %20s %5d %5d %5d</A>",
 		   psl->match + psl->misMatch + psl->repMatch,
 		   100.0 - pslCalcMilliBad(psl, TRUE) * 0.1,
 		   skipChr(psl->tName), psl->strand, psl->tStart + 1, psl->tEnd,
 		   psl->qName, psl->qStart+1, psl->qEnd, psl->qSize);
 	    printf("\n");
 	    }
 	}
     }
 printf("</TT></PRE>");
 }
 
 void printAlignmentsExtra(struct psl *pslList, int startFirst, char *hgcCommand, char *hgcCommandInWindow,
 		     char *tableName, char *itemIn)
 /* Print list of mRNA alignments with special "in window" alignment function. */
 {
 if (pslList == NULL || tableName == NULL)
     return;
 printAlignmentsSimple(pslList, startFirst, hgcCommand, tableName, itemIn);
 
 struct psl *psl = pslList;
 for (psl = pslList; psl != NULL; psl = psl->next)
     {
     if ( pslTrimToTargetRange(psl, winStart, winEnd) != NULL
         &&
 	!startsWith("xeno", tableName)
 	&& !(startsWith("user", tableName) && pslIsProtein(psl))
 	&& psl->tStart == startFirst
         && sameString(psl->tName, seqName)
 	)
 	{
         char otherString[512];
 	safef(otherString, sizeof(otherString), "%d&aliTable=%s",
 	      psl->tStart, tableName);
 	hgcAnchorSomewhere(hgcCommandInWindow, itemIn, otherString, psl->tName);
 	printf("<BR>View details of parts of alignment within browser window</A>.<BR>\n");
 	}
     }
 }
 
 void printAlignments(struct psl *pslList, int startFirst, char *hgcCommand,
 		     char *tableName, char *itemIn)
 /* Print list of mRNA alignments. */
 {
 printAlignmentsExtra(pslList, startFirst, hgcCommand, "htcCdnaAliInWindow", tableName, itemIn);
 }
 
 struct psl *getAlignments(struct sqlConnection *conn, char *table, char *acc)
 /* get the list of alignments for the specified acc */
 {
 struct sqlResult *sr = NULL;
 char **row;
 struct psl *psl, *pslList = NULL;
 boolean hasBin;
 char splitTable[64];
 char query[256];
 if (!hFindSplitTable(database, seqName, table, splitTable, &hasBin))
     errAbort("can't find table %s or %s_%s", table, seqName, table);
 
 sqlSafef(query, sizeof(query), "select * from %s where qName = '%s'", splitTable, acc);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     psl = pslLoad(row+hasBin);
     slAddHead(&pslList, psl);
     }
 sqlFreeResult(&sr);
 slReverse(&pslList);
 return pslList;
 }
 
 struct psl *loadPslRangeT(char *table, char *qName, char *tName, int tStart, int tEnd)
 /* Load a list of psls given qName tName tStart tEnd */
 {
 struct sqlResult *sr = NULL;
 char **row;
 struct psl *psl = NULL, *pslList = NULL;
 boolean hasBin;
 char splitTable[64];
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 
 hFindSplitTable(database, seqName, table, splitTable, &hasBin);
 sqlSafef(query, sizeof(query), "select * from %s where qName = '%s' and tName = '%s' and tEnd > %d and tStart < %d", splitTable, qName, tName, tStart, tEnd);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     psl = pslLoad(row+hasBin);
     slAddHead(&pslList, psl);
     }
 sqlFreeResult(&sr);
 slReverse(&pslList);
 hFreeConn(&conn);
 return pslList;
 }
 
 void doHgRna(struct trackDb *tdb, char *acc)
 /* Click on an individual RNA. */
 {
 char *track = tdb->track;
 char *table = tdb->table;
 struct sqlConnection *conn = hAllocConn(database);
 char *type;
 int start = cartInt(cart, "o");
 struct psl *pslList = NULL;
 
 if (sameString("xenoMrna", track) || sameString("xenoBestMrna", track) || sameString("xenoEst", track) || sameString("sim4", track) )
     {
     char temp[256];
     safef(temp, sizeof temp, "non-%s RNA", organism);
     type = temp;
     }
 else if ( sameWord("blatzHg17KG", track)  )
     {
     type = "Human mRNA";
     }
 else if (stringIn("estFiltered",track))
     {
     type = "EST";
     }
 else if (stringIn("est", track) || stringIn("Est", track))
     {
     type = "EST";
     //  table = "all_est";	// Should fall out of wash now
     }
 else if (startsWith("psu", track))
     {
     type = "Pseudo & Real Genes";
     table = "psu";
     }
 else if (sameWord("xenoBlastzMrna", track) )
     {
     type = "Blastz to foreign mRNA";
     }
 else if (startsWith("mrnaBlastz",track  ))
     {
     type = "mRNA";
     }
 else if (startsWith("pseudoMrna",track) || startsWith("pseudoGeneLink",track))
     {
     type = "mRNA";
     table = "pseudoMrna";
     }
 else if (startsWith("celeraMrna",track))
     {
     type = "mRNA";
     }
 else if (startsWith("all_mrnaFiltered",track))
     {
     type = "mRNA";
     }
 else
     {
     type = "mRNA";
     // table = "all_mrna";  // should fall out of wash now
     }
 
 /* Print non-sequence info. */
 cartWebStart(cart, database, "%s", acc);
 
 printRnaSpecs(tdb, acc, pslList);
 
 /* Get alignment info. */
 pslList = getAlignments(conn, table, acc);
 if (pslList == NULL)
     {
     /* this was not actually a click on an aligned item -- we just
      * want to display RNA info, so leave here */
     hFreeConn(&conn);
     htmlHorizontalLine();
     printf("mRNA %s alignment does not meet minimum alignment criteria on this assembly.", acc);
     return;
     }
 htmlHorizontalLine();
 printf("<H3>%s/Genomic Alignments</H3>", type);
 if (startsWith("mrnaBlastz",tdb->table))
     slSort(&pslList, pslCmpScoreDesc);
 
 printAlignments(pslList, start, "htcCdnaAli", table, acc);
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void printPslFormat(struct sqlConnection *conn, struct trackDb *tdb, char *item, int start,
                     char *subType)
 /* Handles click in affyU95 or affyU133 tracks */
 {
 struct psl  *pslList = getAlignments(conn, tdb->table, item);
 struct psl *psl;
 char *face = "Times"; /* specifies font face to use */
 char *fsize = "+1"; /* specifies font size */
 
 /* check if there is an alignment available for this sequence.  This checks
  * both genbank sequences and other sequences in the seq table.  If so,
  * set it up so they can click through to the alignment. */
 if (hGenBankHaveSeq(database, item, NULL))
     {
     printf("<H3>%s/Genomic Alignments</H3>", item);
     printAlignments(pslList, start, "htcCdnaAli", tdb->table, item);
     }
 else
     {
     /* print out the psls */
     printf("<PRE><TT>");
     printf("<span style='font-family:%s; font-size:%s;'>\n", face, fsize);
 
     for (psl = pslList;  psl != NULL; psl = psl->next)
        {
        pslOutFormat(psl, stdout, '\n', '\n');
        }
     printf("</span></TT></PRE>\n");
     }
 pslFreeList(&pslList);
 }
 
 void doAffy(struct trackDb *tdb, char *item, char *itemForUrl)
 /* Display information for Affy tracks*/
 
 {
 char *dupe, *type, *words[16];
 char *orthoTable = trackDbSetting(tdb, "orthoTable");
 char *otherDb = trackDbSetting(tdb, "otherDb");
 int wordCount;
 int start = cartInt(cart, "o");
 char query[256];
 char **row;
 struct sqlResult *sr = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 
 if (itemForUrl == NULL)
     itemForUrl = item;
 dupe = cloneString(tdb->type);
 genericHeader(tdb, item);
 wordCount = chopLine(dupe, words);
 printCustomUrl(tdb, itemForUrl, item == itemForUrl);
 
 /* If this is the affyZebrafish track, check for human ortholog information */
 if (sameString("affyZebrafish", tdb->table))
     {
     if (orthoTable != NULL && hTableExists(database, orthoTable))
         {
         sqlSafef(query, sizeof(query), "select geneSymbol, description from %s where name = '%s' ", orthoTable, item);
         sr = sqlMustGetResult(conn, query);
         row = sqlNextRow(sr);
         if (row != NULL)
             {
             printf("<P><HR ALIGN=\"CENTER\"></P>\n<TABLE>\n");
             printf("<TR><TH ALIGN=left><H2>Human %s Ortholog:</H2></TH><TD>%s</TD></TR>\n", otherDb, row[0]);
             printf("<TR><TH ALIGN=left>Ortholog Description:</TH><TD>%s</TD></TR>\n",row[1]);
             printf("</TABLE>\n");
             }
         }
     }
 if (wordCount > 0)
     {
     type = words[0];
 
     if (sameString(type, "psl"))
         {
 	char *subType = ".";
 	if (wordCount > 1)
 	    subType = words[1];
         printPslFormat(conn2, tdb, item, start, subType);
 	}
     }
 printTrackHtml(tdb);
 freez(&dupe);
 hFreeConn(&conn);
 hFreeConn(&conn2);
 }
 
 void doZfishRHmap(struct trackDb *tdb, char *itemName)
 /* Put up Radiation Hybrid map information for Zebrafish */
 {
 char *dupe, *type, *words[16];
 char query[256];
 struct sqlResult *sr = NULL;
 char **row = NULL;
 struct rhMapZfishInfo *rhInfo = NULL;
 int wordCount;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn1 = hAllocConn(database);
 boolean rhMapInfoExists = sqlTableExists(conn, "rhMapZfishInfo");
 
 dupe = cloneString(tdb->type);
 wordCount = chopLine(dupe, words);
 
 genericHeader(tdb, itemName);
 
 /* Print out RH map information if available */
 
 if (rhMapInfoExists)
     {
     sqlSafef(query, sizeof query, "SELECT * FROM rhMapZfishInfo WHERE name = '%s'", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
         rhInfo = rhMapZfishInfoLoad(row);
         if (rhInfo != NULL)
             {
             printf("<H2>Information on %s </H2>\n", itemName);
             if (!sameString(rhInfo->zfinId, ""))
                 {
                 printf("<H3>");
                 printCustomUrl(tdb, rhInfo->zfinId, TRUE);
                 printf("</H3>\n");
                 }
             printf("<P><HR ALIGN=\"CENTER\"></P>\n<TABLE>\n");
             printf("<TR><TH ALIGN=left>Linkage group:</TH><TD>%s</TD></TR>\n", rhInfo->linkageGp);
             printf("<TR><TH ALIGN=left>Position on linkage group:</TH><TD>%d</TD></TR>\n", rhInfo->position);
             printf("<TR><TH ALIGN=left>Distance (cR):</TH><TD>%d</TD></TR>\n", rhInfo->distance);
             printf("<TR><TH ALIGN=left>Marker type:</TH><TD>%s</TD></TR>\n", rhInfo->markerType);
             printf("<TR><TH ALIGN=left>Marker source:</TH><TD>%s</TD></TR>\n", rhInfo->source);
             printf("<TR><TH ALIGN=left>Mapping institution:</TH><TD>%s</TD></TR>\n", rhInfo->mapSite);
             printf("<TR><TH ALIGN=left>Forward Primer:</TH><TD>%s</TD></TR>\n", rhInfo->leftPrimer);
             printf("<TR><TH ALIGN=left>Reverse Primer:</TH><TD>%s</TD></TR>\n", rhInfo->rightPrimer);
             printf("</TABLE>\n");
             }
         }
     }
 
 dupe = cloneString(tdb->type);
 wordCount = chopLine(dupe, words);
 if (wordCount > 0)
     {
     type = words[0];
 
     if (sameString(type, "psl"))
         {
 	char *subType = ".";
 	if (wordCount > 1)
 	    subType = words[1];
         printPslFormat(conn1, tdb, itemName, start, subType);
 	}
     }
 printTrackHtml(tdb);
 freez(&dupe);
 hFreeConn(&conn);
 hFreeConn(&conn1);
 }
 
 void doRikenRna(struct trackDb *tdb, char *item)
 /* Put up Riken RNA stuff. */
 {
 char query[512];
 struct sqlResult *sr;
 char **row;
 struct sqlConnection *conn = sqlConnect("mgsc");
 
 genericHeader(tdb, item);
 sqlSafef(query, sizeof query, "select * from rikenMrna where qName = '%s'", item);
 sr = sqlGetResult(conn, query);
 printf("<PRE><TT>\n");
 printf("#match\tmisMatches\trepMatches\tnCount\tqNumInsert\tqBaseInsert\ttNumInsert\tBaseInsert\tstrand\tqName\tqSize\tqStart\tqEnd\ttName\ttSize\ttStart\ttEnd\tblockCount\tblockSizes\tqStarts\ttStarts\n");
 while ((row = sqlNextRow(sr)) != NULL)
     {
     struct psl *psl = pslLoad(row+1);
     pslTabOut(psl, stdout);
     }
 printf("</TT></PRE>\n");
 sqlDisconnect(&conn);
 
 printTrackHtml(tdb);
 }
 
 void doYaleTars(struct trackDb *tdb, char *item, char *itemForUrl)
 /* Display information for Affy tracks*/
 
 {
 char *dupe, *type, *words[16], *chrom = NULL, *strand = NULL;
 char *item2 = NULL;
 int wordCount, end = 0;
 int start = cartInt(cart, "o");
 char query[256];
 char **row;
 struct sqlResult *sr = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 
 if (itemForUrl == NULL)
     {
     if (startsWith("TAR", item))
         {
         /* Remove TAR prefix from item */
         item2 = strchr(item, 'R');
         item2++;
         itemForUrl = item2;
         }
      else
         itemForUrl = item;
      }
 dupe = cloneString(tdb->type);
 genericHeader(tdb, item);
 wordCount = chopLine(dupe, words);
 printCustomUrl(tdb, itemForUrl, item == itemForUrl);
 
 sqlSafef(query, sizeof(query), "select tName, tEnd, strand from %s where qName='%s' and tStart=%d;", tdb->table, item, start);
 
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 
 /* load PSL into struct */
 if (row != NULL)
     {
     chrom = cloneString(row[0]);
     end = sqlUnsigned(row[1]);
     strand = cloneString(row[2]);
     }
 printPos(chrom, start, end, strand, TRUE, item);
 if (wordCount > 0)
     {
     type = words[0];
 
     if (sameString(type, "psl"))
         {
 	char *subType = ".";
 	if (wordCount > 1)
 	    subType = words[1];
         printPslFormat(conn2, tdb, item, start, subType);
 	}
     }
 printTrackHtml(tdb);
 freez(&dupe);
 hFreeConn(&conn);
 hFreeConn(&conn2);
 }
 
 void printPcrTargetMatch(struct targetDb *target, struct psl *tpsl,
 			 boolean mustGetItem)
 /* Show the non-genomic target PCR result and its genomic mapping. */
 {
 char *acc = pcrResultItemAccession(tpsl->tName);
 char *name = pcrResultItemName(tpsl->tName);
 char niceName[256];
 if (name == NULL || sameString(acc, name))
     safecpy(niceName, sizeof(niceName), acc);
 else
     safef(niceName, sizeof(niceName), "%s (%s)", name, acc);
 printf("<B>Position in %s:</B> <A HREF=\"%s?%s&db=%s&position=%s\">%s</A>"
        ":%d-%d<BR>\n",
        target->description, hgTracksName(), cartSidUrlString(cart), database,
        acc, niceName, tpsl->tStart+1, tpsl->tEnd);
 printf("<B>Size in %s:</B> %d<BR>\n", niceName,
        tpsl->tEnd - tpsl->tStart);
 if (tpsl->strand[0] == '-')
     printf("&nbsp;&nbsp;"
 	   "<EM>Warning: the match is on the reverse strand of %s</EM><BR>\n",
 	   niceName);
 
 struct psl *itemPsl = NULL, *otherPsls = NULL, *gpsl;
 int itemStart = cartInt(cart, "o");
 int itemEnd = cartInt(cart, "t");
 int rowOffset = hOffsetPastBin(database, seqName, target->pslTable);
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[2048];
 sqlSafef(query, sizeof(query), "select * from %s where qName = '%s'",
       target->pslTable, acc);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     gpsl = pslLoad(row+rowOffset);
     struct psl *pslTrimmed = pslTrimToQueryRange(gpsl, tpsl->tStart,
 						 tpsl->tEnd);
     if (sameString(gpsl->tName, seqName) &&
 	((gpsl->tStart == itemStart && gpsl->tEnd == itemEnd) ||
 	 (pslTrimmed->tStart == itemStart && pslTrimmed->tEnd == itemEnd)))
 	itemPsl = pslTrimmed;
     else
 	slAddHead(&otherPsls, pslTrimmed);
     pslFree(&gpsl);
     }
 hFreeConn(&conn);
 if (mustGetItem && itemPsl == NULL)
     errAbort("Did not find record for amplicon in %s at %s:%d-%d",
 	     niceName, seqName, itemStart, itemEnd);
 char strand[2];
 strand[1] = '\0';
 if (itemPsl != NULL)
     {
     if (itemPsl->strand[1] == '\0')
 	strand[0] = itemPsl->strand[0];
     else if (itemPsl->strand[0] != itemPsl->strand[1])
 	strand[0] = '-';
     else
 	strand[0] = '+';
     if (itemPsl != NULL)
 	printPosOnChrom(itemPsl->tName, itemPsl->tStart, itemPsl->tEnd,
 			strand, FALSE, tpsl->tName);
     }
 slSort(&otherPsls, pslCmpTarget);
 if (itemPsl != NULL && otherPsls != NULL)
     printf("<B>Other matches in genomic alignments of %s:</B><BR>\n",
 	   niceName);
 for (gpsl = otherPsls;  gpsl != NULL;  gpsl = gpsl->next)
     {
     if (gpsl->strand[1] == '\0')
 	strand[0] = gpsl->strand[0];
     else if (gpsl->strand[0] != gpsl->strand[1])
 	strand[0] = '-';
     else
 	strand[0] = '+';
     printPosOnChrom(gpsl->tName, gpsl->tStart, gpsl->tEnd, strand, FALSE,
 		    tpsl->tName);
     }
 pslFree(&itemPsl);
 pslFreeList(&otherPsls);
 }
 
 static void upperMatch(char *dna, char *primer, int size)
 /* Uppercase DNA where it matches primer. -- copied from gfPcrLib.c. */
 {
 int i;
 for (i=0; i<size; ++i)
     {
     if (dna[i] == primer[i])
         dna[i] = toupper(dna[i]);
     }
 }
 
 void printPcrSequence(struct targetDb *target, struct psl *psl,
 		      char *fPrimer, char *rPrimer)
 /* Print the amplicon sequence (as on hgPcr results page). */
 {
 int productSize = psl->tEnd - psl->tStart;
 char *ffPrimer = cloneString(fPrimer);
 char *rrPrimer = cloneString(rPrimer);
 int rPrimerSize = strlen(rPrimer);
 struct dnaSeq *seq;
 if (target != NULL)
     {
     /* Use seq+extFile if specified; otherwise just retrieve from seqFile. */
     if (isNotEmpty(target->seqTable) && isNotEmpty(target->extFileTable))
 	{
 	struct sqlConnection *conn = hAllocConn(database);
 	seq = hDnaSeqGet(database, psl->tName, target->seqTable,
 			 target->extFileTable);
 	hFreeConn(&conn);
 	char *dna = cloneStringZ(seq->dna + psl->tStart, productSize);
 	freeMem(seq->dna);
 	seq->dna = dna;
 	}
     else
 	{
 	struct twoBitFile *tbf = twoBitOpen(target->seqFile);
 	seq = twoBitReadSeqFrag(tbf, psl->tName, psl->tStart,
 				psl->tEnd);
 	twoBitClose(&tbf);
 	}
     }
 else
     seq = hChromSeq(database, psl->tName, psl->tStart, psl->tEnd);
 char *dna = seq->dna;
 tolowers(dna);
 if (psl->strand[0] == '-')
     reverseComplement(dna, productSize);
 printf("<TT><PRE>");
 /* The rest of this is loosely copied from gfPcrLib.c:outputFa(): */
 char *tNameForPos = (target != NULL ? pcrResultItemAccession(psl->tName) : psl->tName);
 printf("><A HREF=\"%s?%s&db=%s&position=%s",
        hgTracksName(), cartSidUrlString(cart), database, tNameForPos);
 if (target == NULL)
     printf(":%d-%d", psl->tStart+1, psl->tEnd);
 printf("\">%s:%d%c%d</A> %dbp %s %s\n",
        psl->tName, psl->tStart+1, psl->strand[0], psl->tEnd,
        productSize, fPrimer, rPrimer);
 
 /* Flip reverse primer to be in same direction and case as sequence, to
  * compare with sequence: */
 reverseComplement(rrPrimer, rPrimerSize);
 tolowers(rrPrimer);
 tolowers(ffPrimer);
 
 /* Capitalize where sequence and primer match, and write out sequence. */
 upperMatch(dna, ffPrimer, strlen(ffPrimer));
 upperMatch(dna + productSize - rPrimerSize, rrPrimer, rPrimerSize);
 faWriteNext(stdout, NULL, dna, productSize);
 printf("</PRE></TT>");
 }
 
 void doPcrResult(char *track, char *item)
 /* Process click on PCR of user's primers. */
 {
 struct trackDb *tdb = pcrResultFakeTdb();
 char *pslFileName, *primerFileName;
 struct targetDb *target;
 cartWebStart(cart, database, "PCR Results");
 if (! pcrResultParseCart(database, cart, &pslFileName, &primerFileName, &target))
     errAbort("PCR Result track has disappeared!");
 
 char *fPrimer, *rPrimer;
 pcrResultGetPrimers(primerFileName, &fPrimer, &rPrimer);
 printf("<H2>PCR Results (<TT>%s %s</TT>)</H2>\n", fPrimer, rPrimer);
 printf("<B>Forward primer:</B> 5' <TT>%s</TT> 3'<BR>\n", fPrimer);
 printf("<B>Reverse primer:</B> 5' <TT>%s</TT> 3'<BR>\n", rPrimer);
 if (target != NULL)
     printf("<B>Search target:</B> %s<BR>\n", target->description);
 
 struct psl *itemPsl = NULL, *otherPsls = NULL, *psl;
 if (target != NULL)
     {
     /* item (from hgTracks) is |-separated: target sequence name,
      * amplicon start offset in target sequence, and amplicon end offset. */
     char *words[3];
     int wordCount = chopByChar(cloneString(item), '|', words, ArraySize(words));
     if (wordCount != 3)
 	errAbort("doPcrResult: expected 3 |-sep'd words but got '%s'", item);
     char *targetSeqName = words[0];
     if (endsWith(targetSeqName, "__"))
 	targetSeqName[strlen(targetSeqName)-2] = '\0';
     int ampStart = atoi(words[1]), ampEnd = atoi(words[2]);
     pcrResultGetPsl(pslFileName, target, targetSeqName, seqName, ampStart, ampEnd,
 		    &itemPsl, &otherPsls);
     printPcrTargetMatch(target, itemPsl, TRUE);
     }
 else
     {
     pcrResultGetPsl(pslFileName, target, item,
 		    seqName, cartInt(cart, "o"), cartInt(cart, "t"),
 		    &itemPsl, &otherPsls);
     printPosOnChrom(itemPsl->tName, itemPsl->tStart, itemPsl->tEnd,
 		    itemPsl->strand, FALSE, NULL);
     }
 
 if (otherPsls != NULL)
     {
     puts("<HR>");
     printf("<B>Other matches for these primers:</B><BR>\n");
     for (psl = otherPsls;  psl != NULL;  psl = psl->next)
 	{
 	puts("<BR>");
 	if (target != NULL)
 	    printPcrTargetMatch(target, psl, FALSE);
 	else
 	    printPosOnChrom(psl->tName, psl->tStart, psl->tEnd,
 			    psl->strand, FALSE, NULL);
 	}
     puts("<HR>");
     }
 printPcrSequence(target, itemPsl, fPrimer, rPrimer);
 
 puts("<BR><HR>");
 printTrackHtml(tdb);
 }
 
 void doUserPsl(char *track, char *item)
 /* Process click on user-defined alignment. */
 {
 int start = cartInt(cart, "o");
 struct lineFile *lf;
 struct psl *pslList = NULL, *psl;
 char *pslName, *faName, *qName;
 enum gfType qt, tt;
 
 cartWebStart(cart, database, "BLAT Search Alignments");
 printf("<H2>BLAT Search Alignments</H2>\n");
 printf("<H3>Click on a line to see detailed letter-by-letter display</H3>");
 parseSs(item, &pslName, &faName, &qName);
 pslxFileOpen(pslName, &qt, &tt, &lf);
 while ((psl = pslNext(lf)) != NULL)
     {
     if (sameString(psl->qName, qName))
         {
 	slAddHead(&pslList, psl);
 	}
     else
         {
 	pslFree(&psl);
 	}
     }
 slReverse(&pslList);
 lineFileClose(&lf);
 printAlignments(pslList, start, "htcUserAli", "user", item);
 pslFreeList(&pslList);
 webIncludeHelpFile(USER_PSL_TRACK_NAME, TRUE);
 }
 
 void doHgGold(struct trackDb *tdb, char *fragName)
 /* Click on a fragment of golden path. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 struct sqlConnection *conn3 = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 char query2[256];
 struct sqlResult *sr2;
 char **row2;
 char query3[256];
 struct sqlResult *sr3;
 char **row3;
 struct agpFrag frag;
 struct contigAcc contigAcc;
 int start = cartInt(cart, "o");
 boolean hasBin;
 char splitTable[64];
 char *chp;
 char *accession1, *accession2, *spanner, *variation, *varEvidence,
     *contact, *remark, *comment;
 // char *evaluation;  unused variable
 char *secondAcc, *secondAccVer;
 char *tmpString;
 int first;
 
 cartWebStart(cart, database, "%s", fragName);
 hFindSplitTable(database, seqName, tdb->table, splitTable, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where frag = '%s' and chromStart = %d",
 	splitTable, fragName, start);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 agpFragStaticLoad(row+hasBin, &frag);
 
 printf("<B>Entrez nucleotide:</B><A TARGET=_blank HREF='http://www.ncbi.nlm.nih.gov/nuccore/%s'> %s</A><BR>\n", fragName, fragName);
 printf("<B>Clone Fragment ID:</B> %s<BR>\n", frag.frag);
 printf("<B>Clone Fragment Type:</B> %s<BR>\n", frag.type);
 printf("<B>Clone Bases:</B> %d-%d<BR>\n", frag.fragStart+1, frag.fragEnd);
 
 if (hTableExists(database, "contigAcc"))
     {
     sqlSafef(query2, sizeof query2, "select * from contigAcc where contig = '%s'", frag.frag);
     if ((sr2 = sqlGetResult(conn2, query2)))
         {
         row = sqlNextRow(sr2);
         if (row)
             {
             contigAccStaticLoad(row, &contigAcc);
             printf("<B>Genbank Accession: <A HREF=");
             printEntrezNucleotideUrl(stdout, contigAcc.acc);
             printf(" TARGET=_BLANK>%s</A></B><BR>\n", contigAcc.acc);
             }
         sqlFreeResult(&sr2);
         }
     }
 
 printPos(frag.chrom, frag.chromStart, frag.chromEnd, frag.strand, FALSE, NULL);
 
 if (hTableExists(database, "certificate"))
     {
     first = 1;
     again:
     tmpString = cloneString(frag.frag);
     chp = strstr(tmpString, ".");
     if (chp != NULL) *chp = '\0';
 
     if (first)
 	{
         sqlSafef(query2, sizeof query2, "select * from certificate where accession1='%s';", tmpString);
 	}
     else
 	{
         sqlSafef(query2, sizeof query2, "select * from certificate where accession2='%s';", tmpString);
 	}
     sr2 = sqlMustGetResult(conn2, query2);
     row2 = sqlNextRow(sr2);
     while (row2 != NULL)
         {
         printf("<HR>");
         accession1      = row2[0];
         accession2      = row2[1];
         spanner         = row2[2];
         // evaluation      = row2[3];  unused variable
         variation       = row2[4];
         varEvidence     = row2[5];
         contact         = row2[6];
         remark          = row2[7];
         comment         = row2[8];
 
         if (first)
             {
 	    secondAcc = accession2;
 	    }
 	else
 	    {
 	    secondAcc = accession1;
             }
 
         sqlSafef(query3, sizeof query3, "select frag from %s where frag like '%s.%c';",
                 splitTable, secondAcc, '%');
         sr3 = sqlMustGetResult(conn3, query3);
         row3 = sqlNextRow(sr3);
         if (row3 != NULL)
 	    {
             secondAccVer = row3[0];
 	    }
 	else
 	    {
 	    secondAccVer = secondAcc;
 	    }
 
 	printf("<H3>Non-standard Join Certificate: </H3>\n");
 
 	printf("The join between %s and %s is not standard due to a ", frag.frag, secondAccVer);
 	printf("sub-optimal sequence alignment between the overlapping regions of the ");
 	printf("clones.  The following details are provided by the ");
 	printf("sequencing center to support the joining of these two clones:<BR><BR>");
 
 	printf("<B>Joined with Fragment: </B> %s<BR>\n", secondAccVer);
 
 	if (strcmp(spanner, "") != 0) printf("<B>Spanner: </B> %s<BR>\n", spanner);
 	/* if (strcmp(evaluation, "") != 0) printf("<B>Evaluation: </B> %s<BR>\n", evaluation); */
 	if (strcmp(variation, "") != 0) printf("<B>Variation: </B> %s<BR>\n", variation);
 	if (strcmp(varEvidence, "")!= 0) printf("<B>Variation Evidence: </B> %s<BR>\n", varEvidence);
 	if (strcmp(remark, "") != 0) printf("<B>Remark: </B> %s<BR>\n", remark);
 	if (strcmp(comment, "") != 0) printf("<B>Comment: </B> %s<BR>\n", comment);
         if (strcmp(contact, "") != 0)
 	    printf("<B>Contact: </B> <A HREF=\"mailto:%s\">%s</A><BR>", contact, contact);
 
 	sqlFreeResult(&sr3);
 	row2 = sqlNextRow(sr2);
 	}
     sqlFreeResult(&sr2);
 
     if (first)
 	{
 	first = 0;
 	goto again;
 	}
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 hFreeConn(&conn2);
 hFreeConn(&conn3);
 printTrackHtml(tdb);
 }
 
 void doHgGap(struct trackDb *tdb, char *gapType)
 /* Print a teeny bit of info about a gap. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 struct agpGap gap;
 int start = cartInt(cart, "o");
 boolean hasBin;
 char splitTable[64];
 
 cartWebStart(cart, database, "Gap in Sequence");
 hFindSplitTable(database, seqName, tdb->table, splitTable, &hasBin);
 if (sameString(tdb->table, splitTable))
     sqlSafef(query, sizeof(query), "select * from %s where chrom = '%s' and "
           "chromStart = %d",
 	  splitTable, seqName, start);
 else
     sqlSafef(query, sizeof(query), "select * from %s where chromStart = %d",
 	  splitTable, start);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row == NULL)
     errAbort("Couldn't find gap at %s:%d", seqName, start);
 agpGapStaticLoad(row+hasBin, &gap);
 
 printf("<B>Gap Type:</B> %s<BR>\n", gap.type);
 printf("<B>Bridged:</B> %s<BR>\n", gap.bridge);
 printPos(gap.chrom, gap.chromStart, gap.chromEnd, NULL, FALSE, NULL);
 printTrackHtml(tdb);
 
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 
 void selectOneRow(struct sqlConnection *conn, char *table, char *query,
 		  struct sqlResult **retSr, char ***retRow)
 /* Do query and return one row offset by bin as needed. */
 {
 char fullTable[64];
 boolean hasBin;
 char **row;
 if (!hFindSplitTable(database, seqName, table, fullTable, &hasBin))
     errAbort("Table %s doesn't exist in database", table);
 *retSr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(*retSr)) == NULL)
     errAbort("No match to query '%s'", query);
 *retRow = row + hasBin;
 }
 
 
 void doHgContig(struct trackDb *tdb, char *ctgName)
 /* Click on a contig. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 char query[256], query2[256], ctgUrl[256];
 struct sqlResult *sr, *sr2;
 char **row;
 struct ctgPos *ctg;
 struct ctgPos2 *ctg2 = NULL;
 int cloneCount;
 struct contigAcc contigAcc;
 
 char *ncbiTerm = cgiEncode(ctgName);
 safef(ctgUrl, sizeof(ctgUrl), "%s%s", NUCCORE_SEARCH, ncbiTerm);
 
 genericHeader(tdb, ctgName);
 char *url = tdb->url;
 if (sameWord(database,"oryCun2"))
     printf("<B>Name:</B>&nbsp;%s<BR>\n", ctgName);
 else if (isNotEmpty(url))
     {
     if (sameWord(url, "none"))
 	printf("<B>Name:</B>&nbsp;%s<BR>\n", ctgName);
     else
 	printCustomUrl(tdb, ctgName, TRUE);
     }
 else
     printf("<B>Name:</B>&nbsp;<A HREF=\"%s\" TARGET=_blank>%s</A><BR>\n",
 	ctgUrl, ctgName);
 freeMem(ncbiTerm);
 sqlSafef(query, sizeof(query), "select * from %s where contig = '%s'",
 	tdb->table, ctgName);
 selectOneRow(conn, tdb->table, query, &sr, &row);
 
 if (sameString("ctgPos2", tdb->table))
     {
     ctg2 = ctgPos2Load(row);
     printf("<B>Type:</B> %s<BR>\n", ctg2->type);
     ctg = (struct ctgPos*)ctg2;
     }
 else
     ctg = ctgPosLoad(row);
 
 sqlFreeResult(&sr);
 
 if (hTableExists(database, "contigAcc"))
     {
     sqlSafef(query2, sizeof query2, "select * from contigAcc where contig = '%s'", ctgName);
     if ((sr2 = sqlGetResult(conn2, query2)))
         {
         row = sqlNextRow(sr2);
         if (row)
             {
             contigAccStaticLoad(row, &contigAcc);
             printf("<B>Genbank Accession: <A HREF=");
             printEntrezNucleotideUrl(stdout, contigAcc.acc);
             printf(" TARGET=_BLANK>%s</A></B><BR>\n", contigAcc.acc);
             }
         sqlFreeResult(&sr2);
         }
     }
 
 if (hTableExists(database, "clonePos"))
     {
     sqlSafef(query, sizeof query, "select count(*) from clonePos"
                    " where chrom = '%s' and chromEnd >= %d and chromStart <= %d",
             ctg->chrom, ctg->chromStart, ctg->chromEnd);
     cloneCount = sqlQuickNum(conn, query);
     printf("<B>Total Clones:</B> %d<BR>\n", cloneCount);
     }
 printPos(ctg->chrom, ctg->chromStart, ctg->chromEnd, NULL, TRUE, ctg->contig);
 printTrackHtml(tdb);
 
 hFreeConn(&conn);
 hFreeConn(&conn2);
 }
 
 char *cloneStageName(char *stage)
 /* Expand P/D/F. */
 {
 switch (stage[0])
     {
     case 'P':
 	return "predraft (less than 4x coverage shotgun)";
     case 'D':
 	return "draft (at least 4x coverage shotgun)";
     case 'F':
 	return "finished";
     default:
 	return "unknown";
     }
 }
 
 void doHgCover(struct trackDb *tdb, char *cloneName)
 /* Respond to click on clone. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 struct clonePos *clone;
 int fragCount;
 
 cartWebStart(cart, database, "%s", cloneName);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s'", tdb->table, cloneName);
 selectOneRow(conn, tdb->table, query, &sr, &row);
 clone = clonePosLoad(row);
 sqlFreeResult(&sr);
 
 sqlSafef(query, sizeof query,
         "select count(*) from %s_gl where end >= %d and start <= %d and frag like '%s%%'",
         clone->chrom, clone->chromStart, clone->chromEnd, clone->name);
 fragCount = sqlQuickNum(conn, query);
 
 printf("<H2>Information on <A HREF=\"");
 printEntrezNucleotideUrl(stdout, cloneName);
 printf("\" TARGET=_blank>%s</A></H2>\n", cloneName);
 printf("<B>GenBank: <A HREF=\"");
 printEntrezNucleotideUrl(stdout, cloneName);
 printf("\" TARGET=_blank>%s</A></B> <BR>\n", cloneName);
 printf("<B>Status:</B> %s<BR>\n", cloneStageName(clone->stage));
 printf("<B>Fragments:</B> %d<BR>\n", fragCount);
 printf("<B>Size:</B> %d bases<BR>\n", clone->seqSize);
 printf("<B>Chromosome:</B> %s<BR>\n", skipChr(clone->chrom));
 printf("<BR>\n");
 
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 void doHgClone(struct trackDb *tdb, char *fragName)
 /* Handle click on a clone. */
 {
 char cloneName[128];
 fragToCloneVerName(fragName, cloneName);
 doHgCover(tdb, cloneName);
 }
 
 void doBactigPos(struct trackDb *tdb, char *bactigName)
 /* Click on a bactig. */
 {
 struct bactigPos *bactig;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 char goldTable[16];
 char ctgStartStr[16];
 int ctgStart;
 
 genericHeader(tdb, bactigName);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s'", tdb->table, bactigName);
 selectOneRow(conn, tdb->table, query, &sr, &row);
 bactig = bactigPosLoad(row);
 sqlFreeResult(&sr);
 printf("<B>Name:</B> %s<BR>\n", bactigName);
 
 snprintf(goldTable, sizeof(goldTable), "%s_gold", seqName);
 
 puts("<B>First contig:</B>");
 if (hTableExists(database, goldTable))
     {
     sqlSafef(query, sizeof(query),
 	     "select chromStart from %s where frag = \"%s\"",
 	     goldTable, bactig->startContig);
     ctgStart = sqlQuickNum(conn, query);
     snprintf(ctgStartStr, sizeof(ctgStartStr), "%d", ctgStart);
     hgcAnchor("gold", bactig->startContig, ctgStartStr);
     }
 printf("%s</A><BR>\n", bactig->startContig);
 
 puts("<B>Last contig:</B>");
 if (hTableExists(database, goldTable))
     {
     sqlSafef(query, sizeof(query),
 	     "select chromStart from %s where frag = \"%s\"",
 	     goldTable, bactig->endContig);
     ctgStart = sqlQuickNum(conn, query);
     snprintf(ctgStartStr, sizeof(ctgStartStr), "%d", ctgStart);
     hgcAnchor("gold", bactig->endContig, ctgStartStr);
     }
 printf("%s</A><BR>\n", bactig->endContig);
 
 printPos(bactig->chrom, bactig->chromStart, bactig->chromEnd, NULL, FALSE,NULL);
 printTrackHtml(tdb);
 
 hFreeConn(&conn);
 }
 
 
 int showGfAlignment(struct psl *psl, bioSeq *qSeq, FILE *f,
 		    enum gfType qType, int qStart, int qEnd, char *qName)
 /* Show protein/DNA alignment or translated DNA alignment. */
 {
 int blockCount;
 int tStart = psl->tStart;
 int tEnd = psl->tEnd;
 char tName[256];
 struct dnaSeq *tSeq;
 
 /* protein psl's have a tEnd that isn't quite right */
 if ((psl->strand[1] == '+') && (qType == gftProt))
     tEnd = psl->tStarts[psl->blockCount - 1] + psl->blockSizes[psl->blockCount - 1] * 3;
 
 tSeq = hDnaFromSeq(database, seqName, tStart, tEnd, dnaLower);
 
 freez(&tSeq->name);
 tSeq->name = cloneString(psl->tName);
 safef(tName, sizeof(tName), "%s.%s", organism, psl->tName);
 if (qName == NULL)
     fprintf(f, "<H2>Alignment of %s and %s:%d-%d</H2>\n",
 	    psl->qName, psl->tName, psl->tStart+1, psl->tEnd);
 else
     fprintf(f, "<H2>Alignment of %s and %s:%d-%d</H2>\n",
 	    qName, psl->tName, psl->tStart+1, psl->tEnd);
 
 fputs("Click on links in the frame to the left to navigate through "
       "the alignment.\n", f);
 blockCount = pslShowAlignment(psl, qType == gftProt,
                               qName, qSeq, qStart, qEnd,
                               tName, tSeq, tStart, tEnd, f);
 freeDnaSeq(&tSeq);
 return blockCount;
 }
 
 struct ffAli *pslToFfAliAndSequence(struct psl *psl, struct dnaSeq *qSeq,
 				    boolean *retIsRc, struct dnaSeq **retSeq,
 				    int *retTStart)
 /* Given psl, dig up target sequence and convert to ffAli.
  * Note: if strand is -, this does a pslRc to psl! */
 {
 int tStart, tEnd, tRcAdjustedStart;
 struct dnaSeq *dnaSeq;
 
 tStart = psl->tStart - 100;
 if (tStart < 0) tStart = 0;
 if (retTStart)
     *retTStart = tStart;
 
 tEnd  = psl->tEnd + 100;
 if (tEnd > psl->tSize) tEnd = psl->tSize;
 
 dnaSeq = hDnaFromSeq(database, seqName, tStart, tEnd, dnaLower);
 freez(&dnaSeq->name);
 dnaSeq->name = cloneString(psl->tName);
 if (retSeq)
     *retSeq = dnaSeq;
 
 tRcAdjustedStart = tStart;
 
 if (psl->strand[1] == '-')
     pslRc(psl);
 
 if (psl->strand[0] == '-')
     {
     if (retIsRc)
 	*retIsRc = TRUE;
     reverseComplement(dnaSeq->dna, dnaSeq->size);
     pslRc(psl);
     tRcAdjustedStart = psl->tSize - tEnd;
     }
 return pslToFfAli(psl, qSeq, dnaSeq, tRcAdjustedStart);
 }
 
 int showPartialDnaAlignment(struct psl *wholePsl,
 			    struct dnaSeq *rnaSeq, FILE *body,
 			    int cdsS, int cdsE, boolean restrictToWindow)
 /* Show (part of) alignment for accession.  wholePsl is the whole alignment;
  * if restrictToWindow then display the part of the alignment in the current
  * browser window. */
 {
 struct dnaSeq *dnaSeq;
 int wholeTStart;
 int partTStart = wholePsl->tStart, partTEnd = wholePsl->tEnd;
 DNA *rna;
 int rnaSize;
 boolean isRc = FALSE;
 struct ffAli *wholeFfAli;
 int blockCount;
 
 /* Get RNA sequence and convert psl to ffAli.  */
 rna = rnaSeq->dna;
 rnaSize = rnaSeq->size;
 
 /* Don't forget -- this may change wholePsl! */
 wholeFfAli = pslToFfAliAndSequence(wholePsl, rnaSeq, &isRc, &dnaSeq,
 				   &wholeTStart);
 
 if (restrictToWindow)
     {
     partTStart = max(wholePsl->tStart, winStart);
     partTEnd = min(wholePsl->tEnd, winEnd);
     }
 
 /* Write body heading info. */
 fprintf(body, "<H2>Alignment of %s and %s:%d-%d</H2>\n",
 	wholePsl->qName, wholePsl->tName, partTStart+1, partTEnd);
 fprintf(body, "Click on links in the frame to the left to navigate through "
 	"the alignment.\n");
 
 if (rnaSize != wholePsl->qSize)
     {
     fprintf(body, "<p><b>Cannot display alignment. Size of rna %s is %d has changed since alignment was performed when it was %d.\n",
             wholePsl->qName, rnaSize, wholePsl->qSize);
     return 0;
     }
 
 blockCount = ffShAliPart(body, wholeFfAli, wholePsl->qName,
                          rna, rnaSize, 0,
 			 dnaSeq->name, dnaSeq->dna, dnaSeq->size,
 			 wholeTStart, 8, FALSE, isRc,
 			 FALSE, TRUE, TRUE, TRUE, TRUE,
                          cdsS, cdsE, partTStart, partTEnd);
 return blockCount;
 }
 
 void showSomeAlignment(struct psl *psl, bioSeq *oSeq,
                        enum gfType qType, int qStart, int qEnd,
                        char *qName, int cdsS, int cdsE)
 /* Display protein or DNA alignment in a frame. */
 {
 int blockCount, i;
 struct tempName indexTn, bodyTn;
 FILE *index, *body;
 
 trashDirFile(&indexTn, "index", "index", ".html");
 trashDirFile(&bodyTn, "body", "body", ".html");
 
 /* Writing body of alignment. */
 body = mustOpen(bodyTn.forCgi, "w");
 htmStartDirDepth(body, psl->qName, 2);
 if (qType == gftRna || qType == gftDna)
     blockCount = showPartialDnaAlignment(psl, oSeq, body, cdsS, cdsE, FALSE);
 else
     blockCount = showGfAlignment(psl, oSeq, body, qType, qStart, qEnd, qName);
 htmEnd(body);
 fclose(body);
 chmod(bodyTn.forCgi, 0666);
 
 /* Write index. */
 index = mustOpen(indexTn.forCgi, "w");
 if (qName == NULL)
     qName = psl->qName;
 htmStartDirDepth(index, qName, 2);
 fprintf(index, "<H3>Alignment of %s</H3>", qName);
 fprintf(index, "<A HREF=\"../%s#cDNA\" TARGET=\"body\">%s</A><BR>\n", bodyTn.forCgi, qName);
 fprintf(index, "<A HREF=\"../%s#genomic\" TARGET=\"body\">%s.%s</A><BR>\n", bodyTn.forCgi, hOrganism(database), psl->tName);
 for (i=1; i<=blockCount; ++i)
     {
     fprintf(index, "<A HREF=\"../%s#%d\" TARGET=\"body\">block%d</A><BR>\n",
 	    bodyTn.forCgi, i, i);
     }
 fprintf(index, "<A HREF=\"../%s#ali\" TARGET=\"body\">together</A><BR>\n", bodyTn.forCgi);
 htmEnd(index);
 fclose(index);
 chmod(indexTn.forCgi, 0666);
 
 /* Write (to stdout) the main html page containing just the frame info. */
 puts("<FRAMESET COLS = \"13%,87% \" >");
 printf("  <FRAME SRC=\"%s\" NAME=\"index\">\n", indexTn.forCgi);
 printf("  <FRAME SRC=\"%s\" NAME=\"body\">\n", bodyTn.forCgi);
 puts("<NOFRAMES><BODY></BODY></NOFRAMES>");
 puts("</FRAMESET>");
 puts("</HTML>\n");
 exit(0);	/* Avoid cartHtmlEnd. */
 }
 
 
 void showSomePartialDnaAlignment(struct psl *partPsl, struct psl *wholePsl,
 				 bioSeq *oSeq, char *qName, int cdsS, int cdsE)
 /* Display protein or DNA alignment in a frame. */
 {
 int blockCount, i;
 struct tempName indexTn, bodyTn;
 FILE *index, *body;
 
 trashDirFile(&indexTn, "index", "index", ".html");
 trashDirFile(&bodyTn, "body", "body", ".html");
 
 /* Writing body of alignment. */
 body = mustOpen(bodyTn.forCgi, "w");
 htmStartDirDepth(body, partPsl->qName, 2);
 blockCount = showPartialDnaAlignment(wholePsl, oSeq, body, cdsS, cdsE, TRUE);
 htmEnd(body);
 fclose(body);
 chmod(bodyTn.forCgi, 0666);
 
 /* Write index. */
 index = mustOpen(indexTn.forCgi, "w");
 if (qName == NULL)
     qName = partPsl->qName;
 htmStartDirDepth(index, qName, 2);
 fprintf(index, "<H3>Alignment of %s</H3>", qName);
 fprintf(index, "<A HREF=\"../%s#cDNA\" TARGET=\"body\">%s</A><BR>\n", bodyTn.forCgi, qName);
 if (partPsl != wholePsl)
     fprintf(index, "<A HREF=\"../%s#cDNAStart\" TARGET=\"body\">%s in browser window</A><BR>\n", bodyTn.forCgi, qName);
 fprintf(index, "<A HREF=\"../%s#genomic\" TARGET=\"body\">%s.%s</A><BR>\n", bodyTn.forCgi, hOrganism(database), partPsl->tName);
 for (i=1; i<=blockCount; ++i)
     {
     fprintf(index, "<A HREF=\"../%s#%d\" TARGET=\"body\">block%d</A><BR>\n",
 	    bodyTn.forCgi, i, i);
     }
 fprintf(index, "<A HREF=\"../%s#ali\" TARGET=\"body\">together</A><BR>\n", bodyTn.forCgi);
 fclose(index);
 chmod(indexTn.forCgi, 0666);
 
 /* Write (to stdout) the main html page containing just the frame info. */
 if (partPsl != wholePsl)
     printf("<FRAMESET COLS = \"13%%,87%% \" "
 	   "ONLOAD=\"body.location.href = '%s#cDNAStart';\">\n",
 	   bodyTn.forCgi);
 else
     puts("<FRAMESET COLS = \"13%,87% \" >");
 printf("  <FRAME SRC=\"%s\" NAME=\"index\">\n", indexTn.forCgi);
 printf("  <FRAME SRC=\"%s\" NAME=\"body\">\n", bodyTn.forCgi);
 puts("<NOFRAMES><BODY></BODY></NOFRAMES>");
 puts("</FRAMESET>");
 puts("</HTML>\n");
 exit(0);	/* Avoid cartHtmlEnd. */
 }
 
 static void getCdsStartAndStop(struct sqlConnection *conn, char *acc, char *trackTable,
 			       uint *retCdsStart, uint *retCdsEnd)
 /* Get cds start and stop, if available */
 {
 char query[256];
 if (sqlTableExists(conn, gbCdnaInfoTable))
     {
     sqlSafef(query, sizeof query, "select cds from %s where acc = '%s'", gbCdnaInfoTable, acc);
     char *cdsId = sqlQuickString(conn, query);
     if (isNotEmpty(cdsId))
 	{
         sqlSafef(query, sizeof query, "select name from %s where id = '%s'", cdsTable, cdsId);
 	char *cdsString = sqlQuickString(conn, query);
 	if (isNotEmpty(cdsString))
 	    genbankParseCds(cdsString, retCdsStart, retCdsEnd);
 	}
     }
 else
     {
     struct trackDb *tdb = hashMustFindVal(trackHash, trackTable);
     char *cdsTable = trackDbSetting(tdb, "cdsTable");
     if (isNotEmpty(cdsTable) && hTableExists(database, cdsTable))
 	{
 	sqlSafef(query, sizeof(query), "select cds from %s where id = '%s'", cdsTable, acc);
 	char *cdsString = sqlQuickString(conn, query);
 	if (isNotEmpty(cdsString))
 	    genbankParseCds(cdsString, retCdsStart, retCdsEnd);
 	}
     }
 }
 
 void htcBigPslAli(char *acc)
 /* Show alignment for accession in bigPsl file. */
 {
 struct psl *psl;
 char *aliTable;
 int start;
 unsigned int cdsStart = 0, cdsEnd = 0;
 
 
 /* Print start of HTML. */
 writeFramesetType();
 puts("<HTML>");
 aliTable = cartString(cart, "aliTable");
 if (isCustomTrack(aliTable))
     {
     struct customTrack *ct = lookupCt(aliTable);
     tdb = ct->tdb;
     }
 else
     tdb = hashFindVal(trackHash, aliTable);
 printf("<HEAD>\n<TITLE>%s vs Genomic [%s]</TITLE>\n</HEAD>\n\n", acc, aliTable);
 
 /* Get some environment vars. */
 start = cartInt(cart, "l");
 int end = cartInt(cart, "r");
 char *chrom = cartString(cart, "c");
 
 char *seq, *cdsString = NULL;
 struct lm *lm = lmInit(0);
 char *fileName = bbiNameFromSettingOrTable(tdb, NULL, tdb->table);
 struct bbiFile *bbi = bigBedFileOpen(fileName);
 struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, chrom, start, end, 0, lm);
 char *bedRow[32];
 char startBuf[16], endBuf[16];
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     bigBedIntervalToRow(bb, seqName, startBuf, endBuf, bedRow, ArraySize(bedRow));
     struct bed *bed = bedLoadN(bedRow, 12);
     if (sameString(bed->name, acc))
 	{
 	bb->next = NULL;
 	break;
 	}
     }
 if (bb == NULL)
     errAbort("item %s not found in range %s:%d-%d in bigBed %s (%s)",
              acc, chrom, start, end, tdb->table, fileName);
 unsigned seqTypeField =  bbExtraFieldIndex(bbi, "seqType");
 psl = pslFromBigPsl(seqName, bb, seqTypeField, &seq, &cdsString);
 if (cdsString)
     genbankParseCds(cdsString,  &cdsStart, &cdsEnd);
 
 
 if (seq == NULL)
     {
     printf("Sequence for %s not available.\n", psl->qName);
     return;
     }
 struct dnaSeq *rnaSeq = newDnaSeq(seq, strlen(seq), acc);
 enum gfType type = gftRna;
 if (pslIsProtein(psl))
     type = gftProt;
 showSomeAlignment(psl, rnaSeq, type, 0, rnaSeq->size, NULL, cdsStart, cdsEnd);
 }
 
 void htcBigPslAliInWindow(char *acc)
 /* Show alignment in window for accession in bigPsl file. */
 {
 struct psl *partPsl, *wholePsl;
 char *aliTable;
 int start;
 unsigned int cdsStart = 0, cdsEnd = 0;
 
 /* Print start of HTML. */
 writeFramesetType();
 puts("<HTML>");
 aliTable = cartString(cart, "aliTable");
 if (isCustomTrack(aliTable))
     {
     struct customTrack *ct = lookupCt(aliTable);
     tdb = ct->tdb;
     }
 else
     tdb = hashFindVal(trackHash, aliTable);
 printf("<HEAD>\n<TITLE>%s vs Genomic [%s]</TITLE>\n</HEAD>\n\n", acc, aliTable);
 
 /* Get some environment vars. */
 start = cartInt(cart, "l");
 int end = cartInt(cart, "r");
 char *chrom = cartString(cart, "c");
 
 char *seq, *cdsString = NULL;
 struct lm *lm = lmInit(0);
 char *fileName = bbiNameFromSettingOrTable(tdb, NULL, tdb->table);
 struct bbiFile *bbi = bigBedFileOpen(fileName);
 struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, chrom, start, end, 0, lm);
 char *bedRow[32];
 char startBuf[16], endBuf[16];
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     bigBedIntervalToRow(bb, seqName, startBuf, endBuf, bedRow, ArraySize(bedRow));
     struct bed *bed = bedLoadN(bedRow, 12);
     if (sameString(bed->name, acc))
 	{
 	bb->next = NULL;
 	break;
 	}
     }
 unsigned seqTypeField =  bbExtraFieldIndex(bbi, "seqType");
 wholePsl = pslFromBigPsl(seqName, bb, seqTypeField, &seq, &cdsString);
 
 if (seq == NULL)
     {
     printf("Sequence for %s not available.\n", wholePsl->qName);
     return;
     }
 if (cdsString)
     genbankParseCds(cdsString,  &cdsStart, &cdsEnd);
 
 if (wholePsl->tStart >= winStart && wholePsl->tEnd <= winEnd)
     partPsl = wholePsl;
 else
     partPsl = pslTrimToTargetRange(wholePsl, winStart, winEnd);
 struct dnaSeq *rnaSeq = newDnaSeq(seq, strlen(seq), acc);
 showSomePartialDnaAlignment(partPsl, wholePsl, rnaSeq,
                             NULL, cdsStart, cdsEnd);
 }
 
 static struct dnaSeq *getBaseColorSequence(char *itemName, char *table)
 /* Grab sequence using the sequence and extFile table names out of BASE_COLOR_USE_SEQUENCE. */
 {
 struct trackDb *tdb = hashMustFindVal(trackHash, table);
 char *spec = trackDbRequiredSetting(tdb, BASE_COLOR_USE_SEQUENCE);
 char *specCopy = cloneString(spec);
 
 // value is: extFile seqTbl extFileTbl
 char *words[3];
 int nwords = chopByWhite(specCopy, words, ArraySize(words));
 if ((nwords != ArraySize(words)) || !sameString(words[0], "extFile"))
     errAbort("invalid %s track setting: %s", BASE_COLOR_USE_SEQUENCE, spec);
 return hDnaSeqGet(database, itemName, words[1], words[2]);
 }
 
 void htcCdnaAli(char *acc)
 /* Show alignment for accession. */
 {
 char query[256];
 char table[64];
 char accTmp[64];
 struct sqlConnection *conn;
 struct sqlResult *sr;
 char **row;
 struct psl *psl;
 struct dnaSeq *rnaSeq;
 char *aliTable;
 int start;
 unsigned int cdsStart = 0, cdsEnd = 0;
 boolean hasBin;
 char accChopped[512] ;
 safef(accChopped, sizeof(accChopped), "%s",acc);
 chopSuffix(accChopped);
 
 /* Print start of HTML. */
 writeFramesetType();
 puts("<HTML>");
 aliTable = cartString(cart, "aliTable");
 printf("<HEAD>\n<TITLE>%s vs Genomic [%s]</TITLE>\n</HEAD>\n\n", accChopped, aliTable);
 
 /* Get some environment vars. */
 start = cartInt(cart, "o");
 
 conn = hAllocConn(database);
 getCdsStartAndStop(conn, accChopped, aliTable, &cdsStart, &cdsEnd);
 
 /* Look up alignments in database */
 hFindSplitTable(database, seqName, aliTable, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where qName like '%s%%' and tName=\"%s\" and tStart=%d",
 	table, acc, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find alignment for %s at %d", acc, start);
 psl = pslLoad(row+hasBin);
 sqlFreeResult(&sr);
 
 /* get bz rna snapshot for blastz alignments */
 if (sameString("mrnaBlastz", aliTable) || sameString("pseudoMrna", aliTable))
     {
     struct sqlConnection *conn = hAllocConn(database);
     unsigned retId = 0;
     safef(accTmp, sizeof accTmp, "bz-%s", acc);
     if (hRnaSeqAndIdx(accTmp, &rnaSeq, &retId, conn) == -1)
         rnaSeq = hRnaSeq(database, acc);
     hFreeConn(&conn);
     }
 else if (sameString("HInvGeneMrna", aliTable))
     {
     /* get RNA accession for the gene id in the alignment */
     sqlSafef(query, sizeof query, "select mrnaAcc from HInv where geneId='%s'", acc);
     rnaSeq = hRnaSeq(database, sqlQuickString(conn, query));
     }
 else if (sameString("ncbiRefSeqPsl", aliTable) || startsWith("altSeqLiftOverPsl", aliTable))
     {
     rnaSeq = getBaseColorSequence(acc, aliTable);
     }
 else
     {
     char *cdnaTable = NULL;
     struct trackDb *tdb = hashFindVal(trackHash, aliTable);
     if (tdb != NULL)
 	cdnaTable = trackDbSetting(tdb, "cdnaTable");
     if (isNotEmpty(cdnaTable) && hTableExists(database, cdnaTable))
 	rnaSeq = hGenBankGetMrna(database, acc, cdnaTable);
     else
 	rnaSeq = hRnaSeq(database, acc);
     }
 
 if (NULL == rnaSeq)
     {
 	printf("RNA sequence not found: '%s'", acc);
     }
 else
     {
     if (startsWith("xeno", aliTable))
         showSomeAlignment(psl, rnaSeq, gftDnaX, 0, rnaSeq->size, NULL, cdsStart, cdsEnd);
     else
         showSomeAlignment(psl, rnaSeq, gftDna, 0, rnaSeq->size, NULL, cdsStart, cdsEnd);
     }
 hFreeConn(&conn);
 }
 
 void htcCdnaAliInWindow(char *acc)
 /* Show part of alignment in browser window for accession. */
 {
 char query[256];
 char table[64];
 struct sqlConnection *conn;
 struct sqlResult *sr;
 char **row;
 struct psl *wholePsl, *partPsl;
 struct dnaSeq *rnaSeq;
 char *aliTable;
 int start;
 unsigned int cdsStart = 0, cdsEnd = 0;
 boolean hasBin;
 char accChopped[512] ;
 safef(accChopped, sizeof(accChopped), "%s",acc);
 chopSuffix(accChopped);
 
 /* Get some environment vars. */
 aliTable = cartString(cart, "aliTable");
 start = cartInt(cart, "o");
 
 /* Print start of HTML. */
 writeFramesetType();
 puts("<HTML>");
 printf("<HEAD>\n<TITLE>%s vs Genomic [%s]</TITLE>\n</HEAD>\n\n",
        accChopped, aliTable);
 
 conn = hAllocConn(database);
 getCdsStartAndStop(conn, accChopped, aliTable, &cdsStart, &cdsEnd);
 
 if (startsWith("user", aliTable))
     {
     char *pslName, *faName, *qName;
     struct lineFile *lf;
     bioSeq *oSeqList = NULL, *oSeq = NULL;
     struct psl *psl;
     int start;
     enum gfType tt, qt;
     boolean isProt;
     char *ss = cartOptionalString(cart, "ss");
 
     if ((ss != NULL) && !ssFilesExist(ss))
 	{
 	ss = NULL;
 	cartRemove(cart, "ss");
 	errAbort("hgBlat temporary files not found");
 	}
 
     start = cartInt(cart, "o");
 
     // itemIn is three words, the first two are the ss files and the third is the accession
     char *itemIn = cloneString(acc);
     char *words[3];
     int numWords = chopByWhite(itemIn, words, 3);
     if (numWords != 3)
         errAbort("ItemIn string doesn't have three words.");
     qName = words[2];
 
     parseSs(ss, &pslName, &faName, NULL);
     pslxFileOpen(pslName, &qt, &tt, &lf);
     isProt = (qt == gftProt);
     if (isProt)
 	errAbort("hgBlat protein alignments not supported for htcCdnaAliInWindow");
     while ((psl = pslNext(lf)) != NULL)
 	{
         if (sameString(psl->tName, seqName)
 	 && sameString(psl->qName, qName)
          && psl->tStart == start
 	    )
 	    break;
 	pslFree(&psl);
 	}
     lineFileClose(&lf);
     if (psl == NULL)
 	errAbort("Couldn't find alignment at %s:%d", seqName, start);
     oSeqList = faReadAllSeq(faName, !isProt);
     for (oSeq = oSeqList; oSeq != NULL; oSeq = oSeq->next)
 	{
 	if (sameString(oSeq->name, qName))
 	    break;
 	}
     if (oSeq == NULL)
 	errAbort("%s is in %s but not in %s. Internal error.", qName, pslName, faName);
     wholePsl = psl;
     rnaSeq = oSeq;
     }
 else
     {
     /* Look up alignments in database */
     hFindSplitTable(database, seqName, aliTable, table, &hasBin);
     sqlSafef(query, sizeof(query),
          "select * from %s where qName = '%s' and tName=\"%s\" and tStart=%d", 
          table, acc, seqName, start);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) == NULL)
 	errAbort("Couldn't find alignment for %s at %d", acc, start);
     wholePsl = pslLoad(row+hasBin);
     sqlFreeResult(&sr);
 
     if (startsWith("ucscRetroAli", aliTable) || startsWith("retroMrnaAli", aliTable) || sameString("pseudoMrna", aliTable) || startsWith("altSeqLiftOverPsl", aliTable) || startsWith("ncbiRefSeqPsl", aliTable))
 	{
         rnaSeq = getBaseColorSequence(acc, aliTable);
 	}
     else if (sameString("HInvGeneMrna", aliTable))
 	{
 	/* get RNA accession for the gene id in the alignment */
 	sqlSafef(query, sizeof(query), "select mrnaAcc from HInv where geneId='%s'",
 	      acc);
 	rnaSeq = hRnaSeq(database, sqlQuickString(conn, query));
 	}
     else
 	{
 	char *cdnaTable = NULL;
 	struct trackDb *tdb = hashFindVal(trackHash, aliTable);
 	if (tdb != NULL)
 	    cdnaTable = trackDbSetting(tdb, "cdnaTable");
 	if (isNotEmpty(cdnaTable) && hTableExists(database, cdnaTable))
 	    rnaSeq = hGenBankGetMrna(database, acc, cdnaTable);
 	else
 	    rnaSeq = hRnaSeq(database, acc);
 	}
     }
 /* Get partial psl for part of alignment in browser window: */
 if (wholePsl->tStart >= winStart && wholePsl->tEnd <= winEnd)
     partPsl = wholePsl;
 else
     partPsl = pslTrimToTargetRange(wholePsl, winStart, winEnd);
 
 if (startsWith("xeno", aliTable))
     errAbort("htcCdnaAliInWindow does not support translated alignments.");
 else
     showSomePartialDnaAlignment(partPsl, wholePsl, rnaSeq,
 				NULL, cdsStart, cdsEnd);
 hFreeConn(&conn);
 }
 
 void htcChainAli(char *item)
 /* Draw detailed alignment representation of a chain. */
 {
 struct chain *chain;
 struct psl *fatPsl, *psl = NULL;
 int id = atoi(item);
 char *track = cartString(cart, "o");
 char *type = trackTypeInfo(track);
 char *typeWords[2];
 char *otherDb = NULL, *org = NULL, *otherOrg = NULL;
 struct dnaSeq *qSeq = NULL;
 char name[128];
 
 hgBotDelay();	/* Prevent abuse. */
 
 /* Figure out other database. */
 if (chopLine(type, typeWords) < ArraySize(typeWords))
     errAbort("type line for %s is short in trackDb", track);
 otherDb = typeWords[1];
 if (! sameWord(otherDb, "seq"))
     {
     otherOrg = hOrganism(otherDb);
     }
 org = hOrganism(database);
 
 /* Load up subset of chain and convert it to part of psl
  * that just fits inside of window. */
 chain = chainLoadIdRange(database, track, seqName, winStart, winEnd, id);
 if (chain->blockList == NULL)
     {
     printf("None of chain is actually in the window");
     return;
     }
 fatPsl = chainToPsl(chain);
 
 chainFree(&chain);
 
 psl = pslTrimToTargetRange(fatPsl, winStart, winEnd);
 pslFree(&fatPsl);
 
 if (sameWord(otherDb, "seq"))
     {
     qSeq = hExtSeqPart(database, psl->qName, psl->qStart, psl->qEnd);
     safef(name, sizeof name, "%s", psl->qName);
     }
 else
     {
     qSeq = loadGenomePart(otherDb, psl->qName, psl->qStart, psl->qEnd);
     safef(name, sizeof name, "%s.%s", otherOrg, psl->qName);
     }
 writeFramesetType();
 puts("<HTML>");
 printf("<HEAD>\n<TITLE>%s %s vs %s %s </TITLE>\n</HEAD>\n\n",
        (otherOrg == NULL ? "" : otherOrg), psl->qName, org, psl->tName );
 showSomeAlignment(psl, qSeq, gftDnaX, psl->qStart, psl->qEnd, name, 0, 0);
 }
 
 void htcChainTransAli(char *item)
 /* Draw detailed alignment representation of a chain with translated protein */
 {
 struct chain *chain;
 struct psl *fatPsl, *psl = NULL;
 int id = atoi(item);
 char *track = cartString(cart, "o");
 char *type = trackTypeInfo(track);
 char *typeWords[2];
 char *otherDb = NULL, *org = NULL, *otherOrg = NULL;
 struct dnaSeq *qSeq = NULL;
 char name[128];
 int cdsStart = cgiInt("qs");
 int cdsEnd = cgiInt("qe");
 
 /* Figure out other database. */
 if (chopLine(type, typeWords) < ArraySize(typeWords))
     errAbort("type line for %s is short in trackDb", track);
 otherDb = typeWords[1];
 if (! sameWord(otherDb, "seq"))
     {
     otherOrg = hOrganism(otherDb);
     }
 org = hOrganism(database);
 
 /* Load up subset of chain and convert it to part of psl
  * that just fits inside of window. */
 chain = chainLoadIdRange(database, track, seqName, winStart, winEnd, id);
 if (chain->blockList == NULL)
     {
     printf("None of chain is actually in the window");
     return;
     }
 fatPsl = chainToPsl(chain);
 
 chainFree(&chain);
 
 psl = pslTrimToTargetRange(fatPsl, winStart, winEnd);
 pslFree(&fatPsl);
 
 if (sameWord(otherDb, "seq"))
     {
     qSeq = hExtSeq(database, psl->qName);
     safef(name, sizeof name, "%s", psl->qName);
     }
 else
     {
     qSeq = loadGenomePart(otherDb, psl->qName, psl->qStart, psl->qEnd);
     safef(name, sizeof name, "%s.%s", otherOrg, psl->qName);
     }
 writeFramesetType();
 puts("<HTML>");
 printf("<HEAD>\n<TITLE>%s %s vs %s %s </TITLE>\n</HEAD>\n\n",
        (otherOrg == NULL ? "" : otherOrg), psl->qName, org, psl->tName );
 /*showSomeAlignment(psl, qSeq, gftDnaX, psl->qStart, psl->qEnd, name, 0, 0); */
 showSomeAlignment(psl, qSeq, gftDnaX, psl->qStart, psl->qEnd, name, cdsStart, cdsEnd);
 }
 
 void htcUserAli(char *fileNames)
 /* Show alignment for accession. */
 {
 char *pslName, *faName, *qName;
 struct lineFile *lf;
 bioSeq *oSeqList = NULL, *oSeq = NULL;
 struct psl *psl;
 int start;
 enum gfType tt, qt;
 boolean isProt;
 
 /* Print start of HTML. */
 writeFramesetType();
 puts("<HTML>");
 printf("<HEAD>\n<TITLE>User Sequence vs Genomic</TITLE>\n</HEAD>\n\n");
 
 start = cartInt(cart, "o");
 parseSs(fileNames, &pslName, &faName, &qName);
 pslxFileOpen(pslName, &qt, &tt, &lf);
 isProt = (qt == gftProt);
 while ((psl = pslNext(lf)) != NULL)
     {
     if (sameString(psl->tName, seqName) && psl->tStart == start && sameString(psl->qName, qName))
         break;
     pslFree(&psl);
     }
 lineFileClose(&lf);
 if (psl == NULL)
     errAbort("Couldn't find alignment at %s:%d", seqName, start);
 oSeqList = faReadAllSeq(faName, !isProt);
 for (oSeq = oSeqList; oSeq != NULL; oSeq = oSeq->next)
     {
     if (sameString(oSeq->name, qName))
 	break;
     }
 if (oSeq == NULL)  errAbort("%s is in %s but not in %s. Internal error.", qName, pslName, faName);
 showSomeAlignment(psl, oSeq, qt, 0, oSeq->size, NULL, 0, 0);
 }
 
 void htcProteinAli(char *readName, char *table)
 /* Show protein to translated dna alignment for accession. */
 {
 struct psl *psl;
 int start;
 enum gfType qt = gftProt;
 struct sqlResult *sr;
 struct sqlConnection *conn = hAllocConn(database);
 struct dnaSeq *seq = NULL;
 char query[256], **row;
 char fullTable[64];
 boolean hasBin;
 char buffer[256];
 int addp = 0;
 char *pred = NULL;
 
 /* Print start of HTML. */
 writeFramesetType();
 puts("<HTML>");
 printf("<HEAD>\n<TITLE>Protein Sequence vs Genomic</TITLE>\n</HEAD>\n\n");
 
 addp = cartUsualInt(cart, "addp",0);
 pred = cartUsualString(cart, "pred",NULL);
 start = cartInt(cart, "o");
 hFindSplitTable(database, seqName, table, fullTable, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where qName = '%s' and tName = '%s' and tStart=%d",
 	fullTable, readName, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find alignment for %s at %d", readName, start);
 psl = pslLoad(row+hasBin);
 sqlFreeResult(&sr);
 if ((addp == 1) || (pred != NULL))
     {
     char *ptr;
 
     safef(buffer, sizeof buffer, "%s",readName);
 
     if (!(sameString(pred, "ce3.blastWBPep01")
 	    || sameString(pred, "ce9.blastSGPep01")
 	    || sameString(pred, "ce6.blastSGPep01")
 	    || sameString(pred, "ce4.blastSGPep01"))  &&
 	(ptr = strchr(buffer, '.')) != NULL)
 	{
 	*ptr = 0;
 	psl->qName = cloneString(buffer);
 	*ptr++ = 'p';
 	*ptr = 0;
 	}
     if (addp == 1)
 	seq = hPepSeq(database, buffer);
     else
 	{
 	sqlSafef(query, sizeof(query),
 	    "select seq from %s where name = '%s'", pred, psl->qName);
 	sr = sqlGetResult(conn, query);
 	if ((row = sqlNextRow(sr)) != NULL)
 	    seq = newDnaSeq(cloneString(row[0]), strlen(row[0]), psl->qName);
 	else
 	    errAbort("Cannot find sequence for '%s' in %s",psl->qName, pred);
 	sqlFreeResult(&sr);
 	}
     }
 else
     seq = hPepSeq(database, readName);
 hFreeConn(&conn);
 showSomeAlignment(psl, seq, qt, 0, seq->size, NULL, 0, 0);
 }
 
 void htcBlatXeno(char *readName, char *table)
 /* Show alignment for accession. */
 {
 struct psl *psl;
 int start;
 struct sqlResult *sr;
 struct sqlConnection *conn = hAllocConn(database);
 struct dnaSeq *seq;
 char query[256], **row;
 char fullTable[64];
 boolean hasBin;
 
 /* Print start of HTML. */
 writeFramesetType();
 puts("<HTML>");
 printf("<HEAD>\n<TITLE>Sequence %s</TITLE>\n</HEAD>\n\n", readName);
 
 start = cartInt(cart, "o");
 hFindSplitTable(database, seqName, table, fullTable, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where qName = '%s' and tName = '%s' and tStart=%d",
 	fullTable, readName, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find alignment for %s at %d", readName, start);
 psl = pslLoad(row+hasBin);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 seq = hExtSeq(database, readName);
 showSomeAlignment(psl, seq, gftDnaX, 0, seq->size, NULL, 0, 0);
 }
 
 void writeMatches(FILE *f, char *a, char *b, int count)
 /* Write a | where a and b agree, a ' ' elsewhere. */
 {
 int i;
 for (i=0; i<count; ++i)
     {
     if (a[i] == b[i])
         fputc('|', f);
     else
         fputc(' ', f);
     }
 }
 
 void fetchAndShowWaba(char *table, char *name)
 /* Fetch and display waba alignment. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int start = cartInt(cart, "o");
 struct wabAli *wa = NULL;
 int qOffset;
 char strand = '+';
 
 sqlSafef(query, sizeof query, "select * from %s where query = '%s' and chrom = '%s' and chromStart = %d",
 	table, name, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Sorry, couldn't find alignment of %s at %d of %s in database",
 	     name, start, seqName);
 wa = wabAliLoad(row);
 printf("<PRE><TT>");
 qOffset = wa->qStart;
 if (wa->strand[0] == '-')
     {
     strand = '-';
     qOffset = wa->qEnd;
     }
 xenShowAli(wa->qSym, wa->tSym, wa->hSym, wa->symCount, stdout,
 	   qOffset, wa->chromStart, strand, '+', 60);
 printf("</TT></PRE>");
 
 wabAliFree(&wa);
 hFreeConn(&conn);
 }
 
 void doHgTet(struct trackDb *tdb, char *name)
 /* Do thing with tet track. */
 {
 cartWebStart(cart, database, "Fish Alignment");
 printf("Alignment between fish sequence %s (above) and human chromosome %s (below)\n",
        name, skipChr(seqName));
 fetchAndShowWaba("waba_tet", name);
 }
 
 
 void doHgCbr(struct trackDb *tdb, char *name)
 /* Do thing with cbr track. */
 {
 cartWebStart(cart, database, "Worm Alignment");
 printf("Alignment between C briggsae sequence %s (above) and C elegans chromosome %s (below)\n",
        name, skipChr(seqName));
 fetchAndShowWaba("wabaCbr", name);
 }
 
 void doHgRepeat(struct trackDb *tdb, char *repeat)
 /* Do click on a repeat track. */
 {
 int offset = cartInt(cart, "o");
 cartWebStart(cart, database, "Repeat");
 if (offset >= 0)
     {
     struct sqlConnection *conn = hAllocConn(database);
 
     struct sqlResult *sr;
     char **row;
     struct rmskOut *ro;
     char query[256];
     char table[64];
     boolean hasBin;
     int start = cartInt(cart, "o");
 
     hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
     sqlSafef(query, sizeof query, "select * from %s where  repName = '%s' and genoName = '%s' and genoStart = %d",
 	    table, repeat, seqName, start);
     sr = sqlGetResult(conn, query);
     if (sameString(tdb->table,"rmskNew"))
         printf("<H3>CENSOR Information</H3>\n");
     else
         printf("<H3>RepeatMasker Information</H3>\n");
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	ro = rmskOutLoad(row+hasBin);
 	printf("<B>Name:</B> %s<BR>\n", ro->repName);
 	printf("<B>Family:</B> %s<BR>\n", ro->repFamily);
 	printf("<B>Class:</B> %s<BR>\n", ro->repClass);
 	printf("<B>SW Score:</B> %d<BR>\n", ro->swScore);
 	printf("<B>Divergence:</B> %3.1f%%<BR>\n", 0.1 * ro->milliDiv);
 	printf("<B>Deletions:</B>  %3.1f%%<BR>\n", 0.1 * ro->milliDel);
 	printf("<B>Insertions:</B> %3.1f%%<BR>\n", 0.1 * ro->milliIns);
 	printf("<B>Begin in repeat:</B> %d<BR>\n",
 	       (ro->strand[0] == '-' ? ro->repLeft : ro->repStart));
 	printf("<B>End in repeat:</B> %d<BR>\n", ro->repEnd);
 	printf("<B>Left in repeat:</B> %d<BR>\n",
 	       (ro->strand[0] == '-' ? -ro->repStart : -ro->repLeft));
 	printPos(seqName, ro->genoStart, ro->genoEnd, ro->strand, TRUE,
 		 ro->repName);
 	}
     hFreeConn(&conn);
     }
 else
     {
     if (sameString(repeat, "SINE"))
 	printf("This track contains the short interspersed nuclear element (SINE) class of repeats, which includes ALUs.\n");
     else if (sameString(repeat, "LINE"))
         printf("This track contains the long interspersed nuclear element (LINE) class of repeats.\n");
     else if (sameString(repeat, "LTR"))
         printf("This track contains the class of long terminal repeats (LTRs), which includes retroposons.\n");
     else
         printf("This track contains the %s class of repeats.\n", repeat);
     printf("Click on an individual repeat element within the track for more information about that item.<BR>\n");
     }
 printTrackHtml(tdb);
 }
 
 void doHgIsochore(struct trackDb *tdb, char *item)
 /* do click on isochore track. */
 {
 cartWebStart(cart, database, "Isochore Info");
 printf("<H2>Isochore Information</H2>\n");
 if (cgiVarExists("o"))
     {
     struct isochores *iso;
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     char query[256];
     int start = cartInt(cart, "o");
     sqlSafef(query, sizeof query, "select * from %s where  name = '%s' and chrom = '%s' and chromStart = %d",
 	    tdb->table, item, seqName, start);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	iso = isochoresLoad(row);
 	printf("<B>Type:</B> %s<BR>\n", iso->name);
 	printf("<B>GC Content:</B> %3.1f%%<BR>\n", 0.1*iso->gcPpt);
 	printf("<B>Chromosome:</B> %s<BR>\n", skipChr(iso->chrom));
 	printf("<B>Begin in chromosome:</B> %d<BR>\n", iso->chromStart);
 	printf("<B>End in chromosome:</B> %d<BR>\n", iso->chromEnd);
 	printf("<B>Size:</B> %d<BR>\n", iso->chromEnd - iso->chromStart);
 	printf("<BR>\n");
 	isochoresFree(&iso);
 	}
     hFreeConn(&conn);
     }
 printTrackHtml(tdb);
 }
 
 void doSimpleRepeat(struct trackDb *tdb, char *item)
 /* Print info on simple repeat. */
 {
 cartWebStart(cart, database, "Simple Repeat Info");
 printf("<H2>Simple Tandem Repeat Information</H2>\n");
 if (cgiVarExists("o"))
     {
     struct simpleRepeat *rep;
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     char query[256];
     int start = cartInt(cart, "o");
     int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
     sqlSafef(query, sizeof query, "select * from %s where  name = '%s' and chrom = '%s' and chromStart = %d",
 	    tdb->table, item, seqName, start);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	rep = simpleRepeatLoad(row+rowOffset);
 	printf("<B>Period:</B> %d<BR>\n", rep->period);
 	printf("<B>Copies:</B> %4.1f<BR>\n", rep->copyNum);
 	printf("<B>Consensus size:</B> %d<BR>\n", rep->consensusSize);
 	printf("<B>Match Percentage:</B> %d%%<BR>\n", rep->perMatch);
 	printf("<B>Insert/Delete Percentage:</B> %d%%<BR>\n", rep->perIndel);
 	printf("<B>Score:</B> %d<BR>\n", rep->score);
 	printf("<B>Entropy:</B> %4.3f<BR>\n", rep->entropy);
 	printf("<B>Sequence:</B> %s<BR>\n", rep->sequence);
 	printPos(seqName, rep->chromStart, rep->chromEnd, NULL, TRUE,
 		 rep->name);
 	printf("<BR>\n");
 	simpleRepeatFree(&rep);
 	}
     hFreeConn(&conn);
     }
 else
     {
     puts("<P>Click directly on a repeat for specific information on that repeat</P>");
     }
 printTrackHtml(tdb);
 }
 
 void hgSoftPromoter(char *track, char *item)
 /* Print info on Softberry promoter. */
 {
 cartWebStart(cart, database, "Softberry TSSW Promoter");
 printf("<H2>Softberry TSSW Promoter Prediction %s</H2>", item);
 
 if (cgiVarExists("o"))
     {
     struct softPromoter *pro;
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     char query[256];
     int start = cartInt(cart, "o");
     int rowOffset = hOffsetPastBin(database, seqName, track);
     sqlSafef(query, sizeof query, "select * from %s where  name = '%s' and chrom = '%s' and chromStart = %d",
 	    track, item, seqName, start);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	pro = softPromoterLoad(row+rowOffset);
 	bedPrintPos((struct bed *)pro, 3, NULL);
 	printf("<B>Short Name:</B> %s<BR>\n", pro->name);
 	printf("<B>Full Name:</B> %s<BR>\n", pro->origName);
 	printf("<B>Type:</B> %s<BR>\n", pro->type);
 	printf("<B>Score:</B> %f<BR>\n", pro->origScore);
 	printf("<B>Block Info:</B> %s<BR>\n", pro->blockString);
 	printf("<BR>\n");
 	htmlHorizontalLine();
 	printCappedSequence(pro->chromStart, pro->chromEnd, 100);
 	softPromoterFree(&pro);
 	htmlHorizontalLine();
 	}
     hFreeConn(&conn);
     }
 printf("<P>This track was kindly provided by Victor Solovyev (EOS Biotechnology Inc.) on behalf of ");
 printf("<A HREF=\"http://www.softberry.com\" TARGET=_blank>Softberry Inc.</A> ");
 puts("using the TSSW program. "
      "Commercial use of these predictions is restricted to viewing in "
      "this browser.  Please contact Softberry Inc. to make arrangements "
      "for further commercial access.  Further information from Softberry on"
      "this track appears below.</P>"
 
      "<P>\"Promoters were predicted by Softberry promoter prediction program TSSW in "
      "regions up to 3000 from known starts of coding regions (ATG codon) or known "
      "mapped 5'-mRNA ends. We found that limiting promoter search to  such regions "
      "drastically reduces false positive predictions. Also, we have very strong "
      "thresholds for prediction of TATA-less promoters to minimize false positive "
      "predictions. </P>"
      " "
      "<P>\"Our promoter prediction software accurately predicts about 50% promoters "
      "accurately with a small average deviation from true start site. Such accuracy "
      "makes possible experimental work with found promoter candidates. </P>"
      " "
      "<P>\"For 20 experimentally verified promoters on Chromosome 22, TSSW predicted "
      "15, placed 12 of them  within (-150,+150) region from true TSS and 6 (30% of "
      "all promoters) - within -8,+2 region from true TSS.\" </P>");
 }
 
 void doCpgIsland(struct trackDb *tdb, char *item)
 /* Print info on CpG Island. */
 {
 char *table = tdb->table;
 boolean isExt = hHasField(database, table, "obsExp");
 cartWebStart(cart, database, "CpG Island Info");
 printf("<H2>CpG Island Info</H2>\n");
 if (cgiVarExists("o"))
     {
     struct cpgIsland *island;
     struct cpgIslandExt *islandExt = NULL;
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     char query[256];
     int start = cartInt(cart, "o");
     int rowOffset = hOffsetPastBin(database, seqName, table);
     sqlSafef(query, sizeof query, "select * from %s where  name = '%s' and chrom = '%s' and chromStart = %d",
 	    table, item, seqName, start);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	if (isExt)
 	    {
 	    islandExt = cpgIslandExtLoad(row+rowOffset);
 	    island = (struct cpgIsland *)islandExt;
 	    }
 	else
 	    island = cpgIslandLoad(row+rowOffset);
 	if (! startsWith("CpG: ", island->name))
 	    printf("<B>Name:</B> %s<BR>\n", island->name);
 	bedPrintPos((struct bed *)island, 3, tdb);
 	printf("<B>Size:</B> %d<BR>\n", island->chromEnd - island->chromStart);
 	printf("<B>CpG count:</B> %d<BR>\n", island->cpgNum);
 	printf("<B>C count plus G count:</B> %d<BR>\n", island->gcNum);
 	printf("<B>Percentage CpG:</B> %1.1f%%<BR>\n", island->perCpg);
 	printf("<B>Percentage C or G:</B> %1.1f%%<BR>\n", island->perGc);
 	if (islandExt != NULL)
 	    printf("<B>Ratio of observed to expected CpG:</B> %1.2f<BR>\n",
 		   islandExt->obsExp);
 	printf("<BR>\n");
 	cpgIslandFree(&island);
 	}
     hFreeConn(&conn);
     }
 else
     {
     puts("<P>Click directly on a CpG island for specific information on that island</P>");
     }
 printTrackHtml(tdb);
 }
 
 void htcIlluminaProbesAlign(char *item)
 /* If the click came from "show alignment" on the Illumina */
 /* probes details page, show the standard alignment page. */
 {
 struct psl *psl;
 struct dnaSeq *seq;
 struct sqlResult *sr;
 struct sqlConnection *conn = hAllocConn(database);
 char query[256], **row;
 int start;
 char *pslTable = cgiUsualString("pslTable", "illuminaProbesAlign");
 char *seqTable = cgiUsualString("seqTable", "illuminaProbesSeq");
 char *probeName = item;
 char *probeString;
 int rowOffset = hOffsetPastBin(database, seqName, pslTable);
 /* Print start of HTML. */
 writeFramesetType();
 puts("<HTML>");
 printf("<HEAD>\n<TITLE>Sequence %s</TITLE>\n</HEAD>\n\n", probeName);
 start = cartInt(cart, "o");
 /* get psl */
 sqlSafef(query, sizeof(query), "select * from %s where qName = '%s' and tName = '%s' and tStart=%d",
 	pslTable, probeName, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find alignment for %s at %d", probeName, start);
 psl = pslLoad(row+rowOffset);
 sqlFreeResult(&sr);
 sqlSafef(query, sizeof(query), "select seq from %s where id = '%s'", seqTable, probeName);
 probeString = sqlNeedQuickString(conn, query);
 seq = newDnaSeq(probeString, strlen(probeString), probeName);
 hFreeConn(&conn);
 showSomeAlignment(psl, seq, gftDna, 0, seq->size, probeName, 0, 0);
 pslFree(&psl);
 freeDnaSeq(&seq);
 freeMem(probeString);
 }
 
 void doIlluminaProbes(struct trackDb *tdb, char *item)
 /* The details page of the Illumina Probes track. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 char query[256];
 int start = cartInt(cart, "o");
 genericHeader(tdb, item);
 sqlSafef(query, sizeof(query), "select * from %s where name = '%s' and chromStart = '%d'", tdb->table, item, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     struct bed *bed = bedLoad12(row+rowOffset);
     printf("<B>Probe ID:</B> %s<BR>\n", bed->name);
     printf("<B>Position:</B> "
 	   "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
 	   hgTracksPathAndSettings(), database, bed->chrom, bed->chromStart+1, bed->chromEnd);
     printf("%s:%d-%d</A><BR>\n", bed->chrom, bed->chromStart+1, bed->chromEnd);
     printf("<B>Alignment Score:</B> %d<BR>\n", bed->score);
     if ((bed->itemRgb == 1) || (bed->itemRgb == 2))
         /* The "show alignment" link. */
         {
         char other[256];
         char *pslTable = trackDbRequiredSetting(tdb, "pslTable");
 	char *seqTable = trackDbRequiredSetting(tdb, "seqTable");
 	safef(other, sizeof(other), "%d&pslTable=%s&seqTable=%s", bed->chromStart, pslTable, seqTable);
 	hgcAnchor("htcIlluminaProbesAlign", item, other);
 	printf("View Alignment</A><BR>\n");
 	}
     bedFree(&bed);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 void doSwitchDbTss(struct trackDb *tdb, char *item)
 /* Print SwitchDB TSS details. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 char query[256];
 genericHeader(tdb, item);
 sqlSafef(query, sizeof(query), "select * from %s where name = '%s'", tdb->table, item);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     struct switchDbTss tss;
     switchDbTssStaticLoad(row+rowOffset, &tss);
     printPosOnChrom(tss.chrom, tss.chromStart, tss.chromEnd, tss.strand, FALSE, item);
     printf("<B>Gene Model:</B> %s<BR>\n", tss.gmName);
     printf("<B>Gene Model Position:</B> "
        "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
        hgTracksPathAndSettings(), database, tss.chrom, tss.gmChromStart+1, tss.gmChromEnd);
     printf("%s:%d-%d</A><BR>\n", tss.chrom, tss.gmChromStart+1, tss.gmChromEnd);
     printf("<B>TSS Confidence Score:</B> %.1f<BR>\n", tss.confScore);
     printf("<B>Pseudogene TSS: </B>%s<BR>\n", (tss.isPseudo == 1) ? "Yes" : "No");
     }
 else
     {
     puts("<P>Click directly on a TSS for specific information on that TSS</P>");
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 void printLines(FILE *f, char *s, int lineSize)
 /* Print s, lineSize characters (or less) per line. */
 {
 int len = strlen(s);
 int start;
 int oneSize;
 
 for (start = 0; start < len; start += lineSize)
     {
     oneSize = len - start;
     if (oneSize > lineSize)
         oneSize = lineSize;
     mustWrite(f, s+start, oneSize);
     fputc('\n', f);
     }
 if (start != len)
     fputc('\n', f);
 }
 
 void showProteinPrediction(char *pepName, char *table)
 /* Fetch and display protein prediction. */
 {
 /* checks both gbSeq and table */
 aaSeq *seq = hGenBankGetPep(database, pepName, table);
 if (seq == NULL)
     {
     warn("Predicted peptide %s is not avaliable", pepName);
     }
 else
     {
     printf("<PRE><TT>");
     printf(">%s\n", pepName);
     printLines(stdout, seq->dna, 50);
     printf("</TT></PRE>");
     dnaSeqFree(&seq);
     }
 }
 
 boolean isGenieGeneName(char *name)
 /* Return TRUE if name is in form to be a genie name. */
 {
 char *s, *e;
 int prefixSize;
 
 e = strchr(name, '.');
 if (e == NULL)
     return FALSE;
 prefixSize = e - name;
 if (prefixSize > 3 || prefixSize == 0)
     return FALSE;
 s = e+1;
 if (!startsWith("ctg", s))
     return FALSE;
 e = strchr(name, '-');
 if (e == NULL)
     return FALSE;
 return TRUE;
 }
 
 char *hugoToGenieName(char *hugoName, char *table)
 /* Covert from hugo to genie name. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 static char buf[256], *name;
 
 sqlSafef(query, sizeof query, "select transId from %s where name = '%s'", table, hugoName);
 name = sqlQuickQuery(conn, query, buf, sizeof(buf));
 hFreeConn(&conn);
 if (name == NULL)
     errAbort("Database inconsistency: couldn't find gene name %s in knownInfo",
 	     hugoName);
 return name;
 }
 
 void displayProteinPrediction(char *pepName, char *pepSeq)
 /* display a protein prediction. */
 {
 printf("<PRE><TT>");
 printf(">%s length=%d\n", pepName,(int)strlen(pepSeq));
 printLines(stdout, pepSeq, 50);
 printf("</TT></PRE>");
 }
 
 void htcTranslatedProtein(char *pepName)
 /* Display translated protein. */
 {
 char *table = cartString(cart, "o");
 /* checks both gbSeq and table */
 aaSeq *seq = hGenBankGetPep(database, pepName, table);
 cartHtmlStart("Protein Translation");
 if (seq == NULL)
     {
     warn("Predicted peptide %s is not avaliable", pepName);
     }
 else
     {
     displayProteinPrediction(pepName, seq->dna);
     dnaSeqFree(&seq);
     }
 }
 
 static struct genePred *getGenePredForPositionSql(char *table, char *geneName)
 /* find the genePred for the current gene using an SQL table*/
 {
 struct genePred *gpList = NULL;
 char query[512];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 struct genePred *gp;
 int rowOffset = hOffsetPastBin(database, seqName, table);
 
 sqlSafef(query, sizeof(query), "select * from %s where name = \"%s\"", table, geneName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     gp = genePredLoad(row+rowOffset);
     slAddHead(&gpList, gp);
     }
 
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 return gpList;
 }
 
 struct genePred *getGenePredForPositionBigGene(struct trackDb *tdb,  char *geneName)
 /* Find the genePred to the current gene using a bigGenePred. */
 {
 char *fileName = cloneString(trackDbSetting(tdb, "bigDataUrl"));
 struct bbiFile *bbi = bigBedFileOpen(fileName);
 struct lm *lm = lmInit(0);
 struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, seqName, winStart, winEnd, 0, lm);
 struct genePred *gpList = NULL;
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     struct genePred *gp = (struct genePred *)genePredFromBigGenePred(seqName, bb); 
     if (sameString(gp->name, geneName))
 	slAddHead(&gpList, gp);
     }
 lmCleanup(&lm);
 
 return gpList;
 }
 
 static struct trackDb *getCustomTrackTdb(char *table)
 /* Find the trackDb structure for a custom track table. */
 {
 struct customTrack *ctList = getCtList();
 struct customTrack *ct = NULL;
 for (ct = ctList; ct != NULL; ct = ct->next)
     if (sameString(table, ct->tdb->track))
 	return  ct->tdb;
 return NULL;
 }
 
 static struct genePred *getGenePredForPosition(char *table, char *geneName)
 /* Build a genePred list for the given table and gene name. */
 {
 struct genePred *gpList = NULL;
 
 if (isCustomTrack(table))
     {
     struct trackDb *tdb = getCustomTrackTdb(table);
     gpList = getGenePredForPositionBigGene(tdb,  geneName);
     }
 else if (isHubTrack(table))
     {
     struct trackDb *tdb = hubConnectAddHubForTrackAndFindTdb( database, table, NULL, trackHash);
     gpList =  getGenePredForPositionBigGene(tdb, geneName);
     }
 else
     gpList =  getGenePredForPositionSql(table, geneName);
 
 return gpList;
 }
 
 void htcTranslatedPredMRna(char *geneName)
 /* Translate virtual mRNA defined by genePred to protein and display it. */
 {
 char *table = cartString(cart, "table");
 struct genePred *gp = NULL;
 char protName[256];
 char *prot = NULL;
 
 cartHtmlStart("Protein Translation from Genome");
 gp = getGenePredForPosition(table, geneName);
 
 if (gp == NULL)
     errAbort("%s not found in %s when translating to protein",
              geneName, table);
 else if (gp->cdsStart == gp->cdsEnd)
     errAbort("No CDS defined: no protein translation for %s", geneName);
 prot = getPredMRnaProtSeq(gp);
 safef(protName, sizeof(protName), "%s_prot", geneName);
 displayProteinPrediction(protName, prot);
 
 freez(&prot);
 genePredFree(&gp);
 }
 
 void htcTranslatedMRna(struct trackDb *tdb, char *acc)
 /* Translate mRNA to protein and display it. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct genbankCds cds = getCds(conn, acc);
 struct dnaSeq *mrna = hGenBankGetMrna(database, acc, NULL);
 if (mrna == NULL)
     errAbort("mRNA sequence %s not found", acc);
 if (cds.end > mrna->size)
     errAbort("CDS bounds exceed length of mRNA for %s", acc);
 
 int protBufSize = ((cds.end-cds.start)/3)+4;
 char *prot = needMem(protBufSize);
 
 mrna->dna[cds.end] = '\0';
 dnaTranslateSome(mrna->dna+cds.start, prot, protBufSize);
 
 cartHtmlStart("Protein Translation of mRNA");
 displayProteinPrediction(acc, prot);
 }
 
 void getCdsInMrna(struct genePred *gp, int *retCdsStart, int *retCdsEnd)
 /* Given a gene prediction, figure out the
  * CDS start and end in mRNA coordinates. */
 {
 int missingStart = 0, missingEnd = 0;
 int exonStart, exonEnd, exonSize, exonIx;
 int totalSize = 0;
 
 for (exonIx = 0; exonIx < gp->exonCount; ++exonIx)
     {
     exonStart = gp->exonStarts[exonIx];
     exonEnd = gp->exonEnds[exonIx];
     exonSize = exonEnd - exonStart;
     totalSize += exonSize;
     missingStart += exonSize - positiveRangeIntersection(exonStart, exonEnd, gp->cdsStart, exonEnd);
     missingEnd += exonSize - positiveRangeIntersection(exonStart, exonEnd, exonStart, gp->cdsEnd);
     }
 *retCdsStart = missingStart;
 *retCdsEnd = totalSize - missingEnd;
 }
 
 int genePredCdnaSize(struct genePred *gp)
 /* Return total size of all exons. */
 {
 int totalSize = 0;
 int exonIx;
 
 for (exonIx = 0; exonIx < gp->exonCount; ++exonIx)
     {
     totalSize += (gp->exonEnds[exonIx] - gp->exonStarts[exonIx]);
     }
 return totalSize;
 }
 
 struct dnaSeq *getCdnaSeq(struct genePred *gp)
 /* Load in cDNA sequence associated with gene prediction. */
 {
 int txStart = gp->txStart;
 struct dnaSeq *genoSeq = hDnaFromSeq(database, gp->chrom, txStart, gp->txEnd,  dnaLower);
 struct dnaSeq *cdnaSeq;
 int cdnaSize = genePredCdnaSize(gp);
 int cdnaOffset = 0, exonStart, exonSize, exonIx;
 
 AllocVar(cdnaSeq);
 cdnaSeq->dna = needMem(cdnaSize+1);
 cdnaSeq->size = cdnaSize;
 for (exonIx = 0; exonIx < gp->exonCount; ++exonIx)
     {
     exonStart = gp->exonStarts[exonIx];
     exonSize = gp->exonEnds[exonIx] - exonStart;
     memcpy(cdnaSeq->dna + cdnaOffset, genoSeq->dna + (exonStart - txStart), exonSize);
     cdnaOffset += exonSize;
     }
 assert(cdnaOffset == cdnaSeq->size);
 freeDnaSeq(&genoSeq);
 return cdnaSeq;
 }
 
 struct dnaSeq *getCdsSeq(struct genePred *gp)
 /* Load in genomic CDS sequence associated with gene prediction. */
 {
 struct dnaSeq *genoSeq = hDnaFromSeq(database, gp->chrom, gp->cdsStart, gp->cdsEnd,  dnaLower);
 struct dnaSeq *cdsSeq;
 int cdsSize = genePredCodingBases(gp);
 int cdsOffset = 0, exonStart, exonEnd, exonSize, exonIx;
 
 AllocVar(cdsSeq);
 cdsSeq->dna = needMem(cdsSize+1);
 cdsSeq->size = cdsSize;
 for (exonIx = 0; exonIx < gp->exonCount; ++exonIx)
     {
     genePredCdsExon(gp, exonIx, &exonStart, &exonEnd);
     exonSize = (exonEnd - exonStart);
     if (exonSize > 0)
         {
         memcpy(cdsSeq->dna + cdsOffset, genoSeq->dna + (exonStart - gp->cdsStart), exonSize);
         cdsOffset += exonSize;
         }
     }
 assert(cdsOffset == cdsSeq->size);
 freeDnaSeq(&genoSeq);
 if (gp->strand[0] == '-')
     reverseComplement(cdsSeq->dna, cdsSeq->size);
 return cdsSeq;
 }
 
 char *getPredMRnaProtSeq(struct genePred *gp)
 /* Get the predicted mRNA from the genome and translate it to a
  * protein. free returned string. */
 {
 struct dnaSeq *cdsDna = getCdsSeq(gp);
 int protBufSize = (cdsDna->size/3)+4;
 char *prot = needMem(protBufSize);
 int offset = 0;
 
 /* get frame offset, if available and needed */
 if (gp->exonFrames != NULL)
 {
     if (gp->strand[0] == '+' && gp->cdsStartStat != cdsComplete)
         offset = (3 - gp->exonFrames[0]) % 3;
     else if (gp->strand[0] == '-' && gp->cdsEndStat != cdsComplete)
         offset = (3 - gp->exonFrames[gp->exonCount-1]) % 3;
 }
 /* NOTE: this fix will not handle the case in which frame is shifted
  * internally or at multiple exons, as when frame-shift gaps occur in
  * an alignment of an mRNA to the genome.  Going to have to come back
  * and address that later... (acs) */
 
 dnaTranslateSome(cdsDna->dna+offset, prot, protBufSize);
 dnaSeqFree(&cdsDna);
 return prot;
 }
 
 
 
 void ncbiRefSeqSequence(char *itemName)
 {
 char *table = cartString(cart, "o");
 struct dnaSeq *rnaSeq = getBaseColorSequence(itemName, table );
 cartHtmlStart("RefSeq mRNA Sequence");
 
 printf("<PRE><TT>");
 printf(">%s\n", itemName);
 faWriteNext(stdout, NULL, rnaSeq->dna, rnaSeq->size);
 printf("</TT></PRE>");
 }
 
 
 void htcGeneMrna(char *geneName)
 /* Display cDNA predicted from genome */
 {
 char *table = cartString(cart, "o");
 cartHtmlStart("Predicted mRNA from Genome");
 struct genePred *gp, *gpList = getGenePredForPosition(table, geneName), *next;
 int cdsStart, cdsEnd;
 struct dnaSeq *seq;
 
 for(gp = gpList; gp; gp = next)
     {
     next = gp->next;
     seq = getCdnaSeq(gp);
     getCdsInMrna(gp, &cdsStart, &cdsEnd);
     toUpperN(seq->dna + cdsStart, cdsEnd - cdsStart);
     if (gp->strand[0] == '-')
 	{
         reverseComplement(seq->dna, seq->size);
 	}
     printf("<PRE><TT>");
     printf(">%s\n", geneName);
     faWriteNext(stdout, NULL, seq->dna, seq->size);
     printf("</TT></PRE>");
     genePredFree(&gp);
     freeDnaSeq(&seq);
     }
 }
 
 void htcRefMrna(char *geneName)
 /* Display mRNA associated with a refSeq gene. */
 {
 /* check both gbSeq and refMrna */
 struct dnaSeq *seq = hGenBankGetMrna(database, geneName, "refMrna");
 if (seq == NULL)
     errAbort("RefSeq mRNA sequence %s not found", geneName);
 
 cartHtmlStart("RefSeq mRNA");
 printf("<PRE><TT>");
 faWriteNext(stdout, seq->name, seq->dna, seq->size);
 printf("</TT></PRE>");
 dnaSeqFree(&seq);
 }
 
 void cartContinueRadio(char *var, char *val, char *defaultVal)
 /* Put up radio button, checking it if it matches val */
 {
 char *oldVal = cartUsualString(cart, var, defaultVal);
 cgiMakeRadioButton(var, val, sameString(oldVal, val));
 }
 
 void htcGeneInGenome(char *geneName)
 /* Put up page that lets user display genomic sequence
  * associated with gene. */
 {
 char *tbl = cgiString("o");
 
 cartWebStart(cart, database, "Genomic Sequence Near Gene");
 printf("<H2>Get Genomic Sequence Near Gene</H2>");
 
 puts("<P>"
      "Note: if you would prefer to get DNA for more than one feature of "
      "this track at a time, try the ");
 printf("<A HREF=\"%s\" TARGET=_blank>", hgTablesUrl(FALSE, tbl));
 puts("Table Browser</A> using the output format sequence.");
 
 printf("<FORM ACTION=\"%s\">\n\n", hgcName());
 cartSaveSession(cart);
 cgiMakeHiddenVar("g", "htcDnaNearGene");
 cgiContinueHiddenVar("i");
 printf("\n");
 cgiContinueHiddenVar("db");
 printf("\n");
 
 cgiContinueHiddenVar("c");
 printf("\n");
 cgiContinueHiddenVar("l");
 printf("\n");
 cgiContinueHiddenVar("r");
 printf("\n");
 cgiContinueHiddenVar("o");
 printf("\n");
 
 hgSeqOptions(cart, database, tbl);
 cgiMakeButton("submit", "submit");
 printf("</FORM>");
 }
 
 void htcGeneAlignment(char *geneName)
 /* Put up page that lets user display genomic sequence
  * associated with gene. */
 {
 cartWebStart(cart, database, "Aligned Annotated Genomic Sequence ");
 printf("<H2>Align a gene prediction to another species or the same species and view codons and translated proteins.</H2>");
 printf("<FORM ACTION=\"%s\">\n\n", hgcName());
 cartSaveSession(cart);
 cgiMakeHiddenVar("g", "htcDnaNearGene");
 cgiContinueHiddenVar("i");
 printf("\n");
 cgiContinueHiddenVar("db");
 printf("\n");
 cgiContinueHiddenVar("c");
 printf("\n");
 cgiContinueHiddenVar("l");
 printf("\n");
 cgiContinueHiddenVar("r");
 printf("\n");
 cgiContinueHiddenVar("o");
 printf("\n");
 hgSeqOptions(cart, database, cgiString("o"));
 cgiMakeButton("submit", "submit");
 printf("</FORM>");
 }
 
 void toUpperExons(int startOffset, struct dnaSeq *seq, struct genePred *gp)
 /* Upper case bits of DNA sequence that are exons according to gp. */
 {
 int s, e, size;
 int exonIx;
 int seqStart = startOffset, seqEnd = startOffset + seq->size;
 
 if (seqStart < gp->txStart)
     seqStart = gp->txStart;
 if (seqEnd > gp->txEnd)
     seqEnd = gp->txEnd;
 
 for (exonIx = 0; exonIx < gp->exonCount; ++exonIx)
     {
     s = gp->exonStarts[exonIx];
     e = gp->exonEnds[exonIx];
     if (s < seqStart) s = seqStart;
     if (e > seqEnd) e = seqEnd;
     if ((size = e - s) > 0)
 	{
 	s -= startOffset;
 	if (s < 0 ||  s + size > seq->size)
 	    errAbort("Out of range! %d-%d not in %d-%d", s, s+size, 0, size);
 	toUpperN(seq->dna + s, size);
 	}
     }
 }
 
 
 static struct bed *getBedsFromBigBedRange(struct trackDb *tdb, char *geneName)
 /* get a list of beds from a bigBed in the current range */
 {
 struct bbiFile *bbi;
 char *fileName = cloneString(trackDbSetting(tdb, "bigDataUrl"));
 bbi = bigBedFileOpen(fileName);
 struct lm *lm = lmInit(0);
 struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, seqName, winStart, winEnd, 0, lm);
 struct bed *bedList = NULL;
 char *bedRow[32];
 char startBuf[16], endBuf[16];
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     bigBedIntervalToRow(bb, seqName, startBuf, endBuf, bedRow, ArraySize(bedRow));
     struct bed *bed = bedLoadN(bedRow, 12);
     if (sameString(bed->name, geneName))
 	slAddHead(&bedList, bed);
     }
 lmCleanup(&lm);
 
 return bedList;
 }
 
 static int getSeqForBigGene(struct trackDb *tdb, char *geneName)
 /* Output sequence for a gene in a bigGenePred file. */
 {
 struct hTableInfo *hti;
 AllocVar(hti);
 hti->hasCDS = TRUE;
 hti->hasBlocks = TRUE;
 hti->rootName = tdb->table;
 
 struct bed *bedList = getBedsFromBigBedRange(tdb, geneName);
 int itemCount = hgSeqBed(database, hti, bedList);
 freez(&hti);
 bedFreeList(&bedList);
 return itemCount;
 }
 
 void htcDnaNearGene( char *geneName)
 /* Fetch DNA near a gene. */
 {
 char *table    = cartString(cart, "o");
 int itemCount;
 char *quotedItem = makeQuotedString(geneName, '\'');
 puts("<PRE>");
 struct trackDb *tdb = NULL;
 
 if (isHubTrack(table))
     {
     tdb = hubConnectAddHubForTrackAndFindTdb( database, table, NULL, trackHash);
     itemCount = getSeqForBigGene(tdb, geneName);
     }
 else if (isCustomTrack(table))
     {
     tdb = getCustomTrackTdb(table);
     itemCount = getSeqForBigGene(tdb, geneName);
     }
 else
     {
     char constraints[256];
     safef(constraints, sizeof(constraints), "name = %s", quotedItem);
     itemCount = hgSeqItemsInRange(database, table, seqName, winStart, winEnd, constraints);
     }
 if (itemCount == 0)
     printf("\n# No results returned from query.\n\n");
 puts("</PRE>");
 freeMem(quotedItem);
 }
 
 void htcTrackHtml(struct trackDb *tdb)
 /* Handle click to display track html */
 {
 cartWebStart(cart, database, "%s", tdb->shortLabel);
 printTrackHtml(tdb);
 }
 
 void doViralProt(struct trackDb *tdb, char *geneName)
 /* Handle click on known viral protein track. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 int start = cartInt(cart, "o");
 struct psl *pslList = NULL;
 
 cartWebStart(cart, database, "Viral Gene");
 printf("<H2>Viral Gene %s</H2>\n", geneName);
 printCustomUrl(tdb, geneName, TRUE);
 
 pslList = getAlignments(conn, "chr1_viralProt", geneName);
 htmlHorizontalLine();
 printf("<H3>Protein Alignments</H3>");
 printAlignments(pslList, start, "htcProteinAli", "chr1_viralProt", geneName);
 printTrackHtml(tdb);
 }
 
 void doPslAltSeq(struct trackDb *tdb, char *item)
 /* Fairly generic PSL handler -- print out some more details about the
  * alignment. */
 {
 int start = cartInt(cart, "o");
 int total = 0, i = 0;
 struct psl *pslList = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 // char *otherDb = trackDbSetting(tdb, "otherDb");
 // int altSize = hChromSize(otherDb, item);
 
 genericHeader(tdb, item);
 printCustomUrl(tdb, item, TRUE);
 
 puts("<P>");
 puts("<B>Alignment Summary:</B><BR>\n");
 // char strBuf[64];
 // sprintLongWithCommas(strBuf, altSize);
 // printf("<B>Alignment Summary: '%s' %s</B><BR>\n", item, strBuf);
 pslList = getAlignments(conn, tdb->table, item);
 printAlignments(pslList, start, "htcCdnaAli", tdb->table, item);
 
 puts("<P>");
 total = 0;
 for (i=0;  i < pslList -> blockCount;  i++)
     {
     total += pslList->blockSizes[i];
     }
 printf("%d block(s) covering %d bases<BR>\n"
        "%d matching bases<BR>\n"
        "%d mismatching bases<BR>\n"
        "%d N bases<BR>\n"
        "%d bases inserted in %s<BR>\n"
        "%d bases inserted in %s<BR>\n"
        "score: %d<BR>\n",
        pslList->blockCount, total,
        pslList->match,
        pslList->misMatch,
        pslList->nCount,
        pslList->tBaseInsert, hOrganism(database),
        pslList->qBaseInsert, item,
        pslScore(pslList));
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doPslDetailed(struct trackDb *tdb, char *item)
 /* Fairly generic PSL handler -- print out some more details about the
  * alignment. */
 {
 int start = cartInt(cart, "o");
 int total = 0, i = 0;
 struct psl *pslList = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 
 genericHeader(tdb, item);
 printCustomUrl(tdb, item, TRUE);
 
 puts("<P>");
 puts("<B>Alignment Summary:</B><BR>\n");
 pslList = getAlignments(conn, tdb->table, item);
 printAlignments(pslList, start, "htcCdnaAli", tdb->table, item);
 
 puts("<P>");
 total = 0;
 for (i=0;  i < pslList -> blockCount;  i++)
     {
     total += pslList->blockSizes[i];
     }
 printf("%d block(s) covering %d bases<BR>\n"
        "%d matching bases<BR>\n"
        "%d mismatching bases<BR>\n"
        "%d N bases<BR>\n"
        "%d bases inserted in %s<BR>\n"
        "%d bases inserted in %s<BR>\n"
        "score: %d<BR>\n",
        pslList->blockCount, total,
        pslList->match,
        pslList->misMatch,
        pslList->nCount,
        pslList->tBaseInsert, hOrganism(database),
        pslList->qBaseInsert, item,
        pslScore(pslList));
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void printEnsemblCustomUrl(struct trackDb *tdb, char *itemName, boolean encode,
     char *archive)
 /* Print Ensembl Gene URL. */
 {
 char *shortItemName;
 // char *genomeStr = ""; unused variable
 char *genomeStrEnsembl = "";
 struct sqlConnection *conn = hAllocConn(database);
 char cond_str[256], cond_str2[256], cond_str3[256];
 char *proteinID = NULL;
 char *ensPep;
 char *chp;
 char ensUrl[256];
 char *ensemblIdUrl = trackDbSettingOrDefault(tdb, "ensemblIdUrl", "http://www.ensembl.org");
 
 /* shortItemName is the name without the "." + version */
 shortItemName = cloneString(itemName);
 /* ensembl gene names are different from their usual naming scheme on ce6/ce11*/
 if (! (startsWith("ce6", database) || startsWith("ce11", database)))
     {
     chp = strstr(shortItemName, ".");
     if (chp != NULL)
 	*chp = '\0';
     }
 genomeStrEnsembl = ensOrgNameFromScientificName(scientificName);
 if (genomeStrEnsembl == NULL)
     {
     warn("Organism %s not found!", organism); fflush(stdout);
     return;
     }
 
 /* print URL that links to Ensembl transcript details */
 if (sameString(ensemblIdUrl, "http://www.ensembl.org") && archive != NULL)
     safef(ensUrl, sizeof(ensUrl), "http://%s.archive.ensembl.org/%s",
             archive, genomeStrEnsembl);
 else
     safef(ensUrl, sizeof(ensUrl), "%s/%s", ensemblIdUrl, genomeStrEnsembl);
 
 char query[512];
 char *geneName = NULL;
 if (hTableExists(database, "ensemblToGeneName"))
     {
     sqlSafef(query, sizeof(query), "select value from ensemblToGeneName where name='%s'", itemName);
     geneName = sqlQuickString(conn, query);
     }
 char *ensemblSource = NULL;
 if (hTableExists(database, "ensemblSource"))
     {
     sqlSafef(query, sizeof(query), "select source from ensemblSource where name='%s'", itemName);
     ensemblSource = sqlQuickString(conn, query);
     }
 
 sqlSafefFrag(query, sizeof(query), "name = \"%s\"", itemName);
 struct genePred *gpList = genePredReaderLoadQuery(conn, "ensGene", query);
 if (gpList && gpList->name2)
     {
     printf("<B>Ensembl Gene Link: </B>");
     if ((strlen(gpList->name2) < 1) || sameString(gpList->name2, "noXref"))
        printf("none<BR>\n");
     else
        {
        printf("<A HREF=\"%s/geneview?gene=%s\" "
 	    "target=_blank>%s</A><BR>", ensUrl, gpList->name2, gpList->name2);
        if (! (ensemblSource && differentString("protein_coding",ensemblSource)))
           {
           printf("<B>Ensembl Gene Tree: </B>");
           printf("<A HREF=\"%s/Gene/Compara_Tree?g=%s&t=%s\" "
              "target=_blank>%s</A><br>", ensUrl, gpList->name2, shortItemName, gpList->name2);
           }
        }
     }
 genePredFreeList(&gpList);
 
 printf("<B>Ensembl Transcript: </B>");
 printf("<A HREF=\"%s/transview?transcript=%s\" "
                "target=_blank>", ensUrl, shortItemName);
 printf("%s</A><br>", itemName);
 
 if (hTableExists(database, "superfamily"))
     {
     sqlSafefFrag(cond_str, sizeof(cond_str), "transcript_name='%s'", shortItemName);
 
     /* This is necessary, Ensembl kept changing their gene_xref table definition and content.*/
     proteinID = NULL;
 
     if (hTableExists(database, "ensemblXref3"))
         {
         /* use ensemblXref3 for Ensembl data release after ensembl34d */
         sqlSafefFrag(cond_str3, sizeof(cond_str3), "transcript='%s'", shortItemName);
         ensPep = sqlGetField(database, "ensemblXref3", "protein", cond_str3);
 	if (ensPep != NULL) proteinID = ensPep;
 	}
 
     if (hTableExists(database, "ensTranscript") && (proteinID == NULL))
         {
         proteinID = sqlGetField(database, "ensTranscript", "translation_name", cond_str);
         }
     else
         {
         if (hTableExists(database, "ensGeneXref"))
             {
 	    proteinID = sqlGetField(database, "ensGeneXref","translation_name", cond_str);
             }
         else if (hTableExists(database, "ensemblXref2"))
             {
             proteinID = sqlGetField(database, "ensemblXref2","translation_name", cond_str);
             }
         else
             {
             if (hTableExists(database, "ensemblXref"))
                 {
                 proteinID=sqlGetField(database, "ensemblXref","translation_name",cond_str);
                 }
             }
         }
     if (proteinID != NULL)
         {
         printf("<B>Ensembl Protein: </B>");
         printf("<A HREF=\"%s/protview?peptide=%s\" target=_blank>",
             ensUrl, proteinID);
         printf("%s</A><BR>\n", proteinID);
         }
 
 #ifdef NOT
     /* get genomeStr to be used in Superfamily URL */
     if (sameWord(organism, "human"))
         {
 	genomeStr = "hs";
 	}
     else
 	{
         if (sameWord(organism, "mouse"))
 	    {
 	    genomeStr = "mm";
 	    }
         else
             {
 	    if (sameWord(organism, "rat"))
                 {
                 genomeStr = "rn";
                 }
             else
                 {
                 if (sameWord(organism, "dog"))
                     {
                     genomeStr = "dg";
                     }
                 else
                     {
                     warn("Organism %s not found!", organism);
                     return;
                     }
                 }
             }
         }
 /* superfamily does not update with ensGene updates, stop printing an
 	invalid URL */
     sqlSafefFrag(cond_str, "name='%s'", shortItemName);
     char *ans = sqlGetField(conn, database, "superfamily", "name", cond_str);
     if (ans != NULL)
 	{
 	/* double check to make sure trackDb is also updated to be in sync with existence of supfamily table */
 	struct trackDb *tdbSf = hashFindVal(trackHash, "superfamily");
         if (tdbSf != NULL)
 	    {
             char supfamURL[512];
             printf("<B>Superfamily Link: </B>");
             safef(supfamURL, sizeof(supfamURL), "<A HREF=\"%s%s;seqid=%s\" target=_blank>",
                       tdbSf->url, genomeStr, proteinID);
             printf("%s%s</A><BR>\n", supfamURL, proteinID);
             }
         }
 #endif
     }
 if (hTableExists(database, "ensGtp") && (proteinID == NULL))
     {
     /* shortItemName removes version number but sometimes the ensGtp */
     /* table has a transcript with version number so exact match not used */
     sqlSafefFrag(cond_str2, sizeof(cond_str2), "transcript like '%s%%'", shortItemName);
     proteinID=sqlGetField(database, "ensGtp","protein",cond_str2);
     if (proteinID != NULL)
         {
 	printf("<B>Ensembl Protein: </B>");
 	printf("<A HREF=\"%s/protview?peptide=%s\" target=_blank>",
 	    ensUrl,proteinID);
 	printf("%s</A><BR>\n", proteinID);
 	}
     else
 	{
 	printf("<B>Ensembl Protein: </B>none (non-coding)<BR>\n");
 	}
     }
 if (geneName)
     {
     printf("<B>Gene Name: </B>%s<BR>\n", geneName);
     freeMem(geneName);
     }
 if (ensemblSource)
     {
     printf("<B>Ensembl Type: </B>%s<BR>\n", ensemblSource);
     freeMem(ensemblSource);
     }
 freeMem(shortItemName);
 }
 
 void printEnsemblOrVegaCustomUrl(struct trackDb *tdb, char *itemName, boolean encode, char *archive)
 /* Print Ensembl Gene URL. */
 {
 boolean isEnsembl = FALSE;
 boolean isVega = FALSE;
 boolean hasEnsGtp = FALSE;
 boolean hasVegaGtp = FALSE;
 char *shortItemName;
 char *genomeStrEnsembl = "";
 struct sqlConnection *conn = hAllocConn(database);
 char cond_str[256], cond_str2[256];
 char *geneID = NULL;
 char *proteinID = NULL;
 char *chp;
 char dbUrl[256];
 char geneType[256];
 char gtpTable[256];
 
 if (startsWith("ens", tdb->table))
    {
    isEnsembl = TRUE;
    safef(geneType, sizeof(geneType), "Ensembl");
    safef(gtpTable, sizeof(gtpTable), "ensGtp");
    if (hTableExists(database, gtpTable))
       hasEnsGtp = TRUE;
    }
 else if (startsWith("vega", tdb->table))
    {
    isVega = TRUE;
    safef(geneType, sizeof(geneType), "Vega");
    safef(gtpTable, sizeof(gtpTable), "vegaGtp");
    if (hTableExists(database, gtpTable))
       hasVegaGtp = TRUE;
    }
 /* shortItemName is the name without the "." + version */
 shortItemName = cloneString(itemName);
 /* ensembl gene names are different from their usual naming scheme on ce6/ce11*/
 if (! (startsWith("ce6", database) || startsWith("ce11", database)) )
     {
     chp = strstr(shortItemName, ".");
     if (chp != NULL)
 	*chp = '\0';
     }
 genomeStrEnsembl = ensOrgNameFromScientificName(scientificName);
 if (genomeStrEnsembl == NULL)
     {
     warn("Organism %s not found!", organism); fflush(stdout);
     return;
     }
 
 /* print URL that links to Ensembl or Vega transcript details */
 if (isEnsembl)
     {
     if (archive != NULL)
        safef(dbUrl, sizeof(dbUrl), "http://%s.archive.ensembl.org/%s",
             archive, genomeStrEnsembl);
     else
         safef(dbUrl, sizeof(dbUrl), "http://www.ensembl.org/%s", genomeStrEnsembl);
     }
 else if (isVega)
     safef(dbUrl, sizeof(dbUrl), "http://vega.sanger.ac.uk/%s", genomeStrEnsembl);
 
 boolean nonCoding = FALSE;
 char query[512];
 sqlSafefFrag(query, sizeof(query), "name = \"%s\"", itemName);
 struct genePred *gpList = genePredReaderLoadQuery(conn, tdb->table, query);
 if (gpList && (gpList->cdsStart == gpList->cdsEnd))
     nonCoding = TRUE;
 genePredFreeList(&gpList);
 /* get gene and protein IDs */
 if ((isEnsembl && hasEnsGtp) || (isVega && hasVegaGtp))
     {
     /* shortItemName removes version number but sometimes the ensGtp */
     /* table has a transcript with version number so exact match not used */
     sqlSafefFrag(cond_str, sizeof(cond_str), "transcript like '%s%%'", shortItemName);
     geneID=sqlGetField(database, gtpTable,"gene",cond_str);
     sqlSafefFrag(cond_str2, sizeof(cond_str2), "transcript like '%s%%'", shortItemName);
     proteinID=sqlGetField(database, gtpTable,"protein",cond_str2);
     }
 
 /* Print gene, transcript and protein links */
 if (geneID != NULL)
     {
     printf("<B>%s Gene: </B>", geneType);
     printf("<A HREF=\"%s/geneview?gene=%s\" "
 	    "target=_blank>%s</A><BR>", dbUrl, geneID, geneID);
     }
 printf("<B>%s Transcript: </B>", geneType);
 printf("<A HREF=\"%s/transview?transcript=%s\" "
            "target=_blank>%s</A><BR>", dbUrl, shortItemName, itemName);
 if (proteinID != NULL)
     {
     printf("<B>%s Protein: </B>", geneType);
     if (nonCoding)
         printf("none (non-coding)<BR>\n");
     else
         printf("<A HREF=\"%s/protview?peptide=%s\" "
 	      "target=_blank>%s</A><BR>", dbUrl, proteinID, proteinID);
     }
 freeMem(shortItemName);
 }
 
 void doEnsemblGene(struct trackDb *tdb, char *item, char *itemForUrl)
 /* Put up Ensembl Gene track info or Ensembl NonCoding track info. */
 {
 char *dupe, *type, *words[16];
 int wordCount;
 int start = cartInt(cart, "o");
 char condStr[256];
 char headerTitle[512];
 
 if (itemForUrl == NULL)
     itemForUrl = item;
 dupe = cloneString(tdb->type);
 
 struct trackVersion *trackVersion = getTrackVersion(database, tdb->track);
 if ((trackVersion != NULL) && !isEmpty(trackVersion->version))
     safef(headerTitle, sizeof(headerTitle), "%s - Ensembl %s", item, trackVersion->version);
 else
     safef(headerTitle, sizeof(headerTitle), "%s", item);
 
 genericHeader(tdb, headerTitle);
 wordCount = chopLine(dupe, words);
 char *archive = trackDbSetting(tdb, "ensArchive");
 if (archive == NULL)
     {
     if ((trackVersion != NULL) && !isEmpty(trackVersion->dateReference))
 	{
 	if (differentWord("current", trackVersion->dateReference))
 	    archive = cloneString(trackVersion->dateReference);
 	}
     }
 printEnsemblCustomUrl(tdb, itemForUrl, item == itemForUrl, archive);
 sqlSafefFrag(condStr, sizeof condStr, "name='%s'", item);
 
 struct sqlConnection *conn = hAllocConn(database);
 
 /* if this is a non-coding gene track, then print the biotype and
    the external ID */
 if (sameWord(tdb->table, "ensGeneNonCoding"))
     {
     char query[256];
     struct sqlResult *sr = NULL;
     char **row;
     sqlSafef(query, sizeof(query), "select biotype, extGeneId from %s where %s",
           tdb->table, condStr);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         printf("<B>Gene Type:</B> %s<BR>\n", row[0]);
         printf("<B>External Gene ID:</B> %s<BR>\n", row[1]);
         }
     sqlFreeResult(&sr);
     }
 else
     {
     /* print CCDS if this is not a non-coding gene */
     printCcdsForSrcDb(conn, item);
     printf("<BR>\n");
     }
 
 if (hTableExists(database, "ensInfo"))
     {
     struct sqlResult *sr;
     char query[256], **row;
     struct ensInfo *info = NULL;
 
     sqlSafef(query, sizeof(query),
           "select * from ensInfo where name = '%s'", item);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         info = ensInfoLoad(row);
         /* no need to print otherId field, this is the same as name 2 in
            the ensGene table and it is printed by showGenePos() */
         /* convert the status to lower case */
         tolowers(info->status);
         printf("<B>Ensembl Gene Type:</B> %s %s<BR>\n", info->status,
                 info->class);
         printf("<B>Ensembl Gene:</B> %s<BR>\n", info->geneId);
         printf("<B>Ensembl Gene Description:</B> %s<BR>\n", info->geneDesc);
         ensInfoFree(&info);
         }
     sqlFreeResult(&sr);
     }
 
 /* skip the rest if this gene is not in ensGene */
 sqlSafefFrag(condStr, sizeof condStr, "name='%s'", item);
 if (sqlGetField(database, tdb->table, "name", condStr) != NULL)
     {
     if (wordCount > 0)
         {
         type = words[0];
         if (sameString(type, "genePred"))
             {
 	    char *pepTable = NULL, *mrnaTable = NULL;
 	    if (wordCount > 1)
                 pepTable = words[1];
 	    if (wordCount > 2)
                 mrnaTable = words[2];
 	    genericGenePredClick(conn, tdb, item, start, pepTable, mrnaTable);
 	    }
         }
     }
 printTrackHtml(tdb);
 freez(&dupe);
 hFreeConn(&conn);
 }
 
 void printSuperfamilyCustomUrl(struct trackDb *tdb, char *itemName, boolean encode)
 /* Print Superfamily URL. */
 {
 char *url = tdb->url;
 if (url != NULL && url[0] != 0)
     {
     char supfamURL[1024];
     char *genomeStr;
     struct sqlConnection *conn = hAllocConn(database);
     char query[256];
     struct sqlResult *sr;
     char **row;
 
     printf("The corresponding protein %s has the following Superfamily domain(s):", itemName);
     printf("<UL>\n");
 
     sqlSafef(query, sizeof query,
             "select description from sfDescription where proteinID='%s';",
             itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     while (row != NULL)
         {
         printf("<li>%s", row[0]);
         row = sqlNextRow(sr);
         }
     sqlFreeResult(&sr);
     hFreeConn(&conn);
 
     printf("</UL>");
 
     if (sameWord(organism, "human"))
         {
         genomeStr = "hs";
 	}
     else
 	{
         if (sameWord(organism, "mouse"))
 	    {
 	    genomeStr = "mm";
 	    }
 	else
 	    {
 	    if (sameWord(organism, "rat"))
                 {
                 genomeStr = "rn";
                 }
             else
                 {
                 warn("Organism %s not found!", organism);
                 return;
 		}
 	    }
 	}
 
     printf("<B>Superfamily Link: </B>");
     safef(supfamURL, sizeof supfamURL, "<A HREF=\"%s%s;seqid=%s\" target=_blank>",
 	    url, genomeStr, itemName);
     printf("%s%s</A><BR><BR>\n", supfamURL, itemName);
     }
 }
 
 void doSuperfamily(struct trackDb *tdb, char *item, char *itemForUrl)
 /* Put up Superfamily track info. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 char *chrom, *chromStart, *chromEnd;
 char *transcript;
 
 if (itemForUrl == NULL)
     itemForUrl = item;
 
 genericHeader(tdb, item);
 
 printSuperfamilyCustomUrl(tdb, itemForUrl, item == itemForUrl);
 if (hTableExists(database, "ensGeneXref"))
     {
     sqlSafefFrag(query, sizeof query, "translation_name='%s'", item);
     transcript = sqlGetField(database, "ensGeneXref", "transcript_name", query);
 
     sqlSafef(query, sizeof query,
             "select chrom, chromStart, chromEnd from superfamily where name='%s';", transcript);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
         chrom      = row[0];
         chromStart = row[1];
         chromEnd   = row[2];
         printf("<HR>");
         printPosOnChrom(chrom, atoi(chromStart), atoi(chromEnd), NULL, TRUE, transcript);
         }
     sqlFreeResult(&sr);
     }
 if (hTableExists(database, "ensemblXref3"))
     {
     sqlSafefFrag(query, sizeof query, "protein='%s'", item);
     transcript = sqlGetField(database, "ensemblXref3", "transcript", query);
 
     sqlSafef(query, sizeof query,
             "select chrom, chromStart, chromEnd from superfamily where name='%s';", transcript);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
         chrom      = row[0];
         chromStart = row[1];
         chromEnd   = row[2];
         printf("<HR>");
         printPosOnChrom(chrom, atoi(chromStart), atoi(chromEnd), NULL, TRUE, transcript);
         }
     sqlFreeResult(&sr);
     }
 printTrackHtml(tdb);
 }
 
 void doOmimAv(struct trackDb *tdb, char *avName)
 /* Process click on an OMIM AV. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *chp;
 char *omimId, *avSubFdId;
 char *avDescStartPos, *avDescLen;
 char *omimTitle = cloneString("");
 char *geneSymbol = NULL;
 int iAvDescStartPos = 0;
 int iAvDescLen = 0;
 
 struct lineFile *lf;
 char *line;
 int lineSize;
 
 cartWebStart(cart, database, "%s (%s)", tdb->longLabel, avName);
 
 sqlSafef(query, sizeof(query), "select * from omimAv where name = '%s'", avName);
 sr = sqlGetResult(conn, query);
 
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find %s in omimAv table - database inconsistency.", avName);
 sqlFreeResult(&sr);
 
 omimId = strdup(avName);
 chp = strstr(omimId, ".");
 *chp = '\0';
 
 chp++;
 avSubFdId = chp;
 
 sqlSafef(query, sizeof(query), "select title, geneSymbol from hgFixed.omimTitle where omimId = %s", omimId);
 sr = sqlGetResult(conn, query);
 
 if ((row = sqlNextRow(sr)) != NULL)
     {
     omimTitle  = cloneString(row[0]);
     geneSymbol = cloneString(row[1]);
     }
 sqlFreeResult(&sr);
 
 printf("<H4>OMIM <A HREF=\"");
 printEntrezOMIMUrl(stdout, atoi(omimId));
 printf("\" TARGET=_blank>%s</A>: %s; %s</H4>\n", omimId, omimTitle, geneSymbol);
 
 sqlSafef(query, sizeof(query),
 "select startPos, length from omimSubField where omimId='%s' and subFieldId='%s' and fieldType='AV'",
       omimId, avSubFdId);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find %s in omimSubField table - database inconsistency.", avName);
 else
     {
     avDescStartPos = cloneString(row[0]);
     avDescLen	   = cloneString(row[1]);
     iAvDescStartPos = atoi(avDescStartPos);
     iAvDescLen      = atoi(avDescLen);
     }
 sqlFreeResult(&sr);
 
 lf = lineFileOpen("/gbdb/hg17/omim/omim.txt", TRUE);
 lineFileSeek(lf,(size_t)(iAvDescStartPos), 0);
 lineFileNext(lf, &line, &lineSize);
 printf("<h4>");
 printf(".%s %s ", avSubFdId, line);
 lineFileNext(lf, &line, &lineSize);
 printf("[%s]\n", line);
 printf("</h4>");
 
 while ((lf->lineStart + lf->bufOffsetInFile) < (iAvDescStartPos + iAvDescLen))
     {
     lineFileNext(lf, &line, &lineSize);
     printf("%s\n", line);
     }
 
 htmlHorizontalLine();
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doRgdQtl(struct trackDb *tdb, char *item)
 /* Put up RGD QTL info. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 char *otherDb = trackDbSetting(tdb, "otherDb");
 char *qtlOrg;
 if (sameString(tdb->table, "rgdQtl"))
     qtlOrg = organism;
 else if (isNotEmpty(otherDb))
     qtlOrg = hOrganism(otherDb);
 else
     qtlOrg = "";
 
 genericHeader(tdb, item);
 printf("<B>%s QTL %s: ", qtlOrg, item);
 sqlSafef(query, sizeof(query),
       "select description from %sLink where name='%s';",
       tdb->table, item);
 sr = sqlMustGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     printf("%s", row[0]);
 sqlFreeResult(&sr);
 printf("</B><BR>\n");
 
 if (isNotEmpty(tdb->url))
     {
     boolean gotId = FALSE;
     sqlSafef(query, sizeof(query), "select id from %sLink where name='%s';",
 	  tdb->table, item);
     sr = sqlMustGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
 	char *qtlId = row[0];
 	printf(gotId ? ", \n\t" : "<B>RGD QTL Report:</B> ");
         printf("<B><A HREF=\"%s%s\" target=_blank>", tdb->url, qtlId);
         printf("RGD:%s</A></B>", qtlId);
 	gotId = TRUE;
         }
     if (gotId)
 	printf("\n<BR>\n");
     sqlFreeResult(&sr);
     }
 
 int start=cartInt(cart, "o"), end=cartInt(cart, "t");
 struct bed *selectedPos=NULL, *otherPosList=NULL, *bed=NULL;
 sqlSafef(query, sizeof(query),
       "select chrom, chromStart, chromEnd from %s where name='%s' "
       "order by (chromEnd-chromStart);",
       tdb->table, item);
 sr = sqlMustGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     bed = bedLoad3(row);
     if (selectedPos == NULL && sameString(bed->chrom, seqName) &&
 	bed->chromStart == start && bed->chromEnd == end)
 	selectedPos = bed;
     else
 	slAddHead(&otherPosList, bed);
     }
 sqlFreeResult(&sr);
 if (selectedPos)
     printPosOnChrom(seqName, start, end, NULL, FALSE, item);
 
 if (otherPosList)
     printf("<BR>%s QTL %s is also mapped to these locations "
 	   "(largest genomic size first):</BR>\n", qtlOrg, item);
 for (bed = otherPosList;  bed != NULL;  bed = bed->next)
     {
     printf("<HR>");
     printPosOnChrom(bed->chrom, bed->chromStart, bed->chromEnd,
 		    NULL, FALSE, item);
     }
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 void printGadDetails(struct trackDb *tdb, char *itemName, boolean encode)
 /* Print details of a GAD entry. */
 {
 int refPrinted = 0;
 boolean showCompleteGadList;
 
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 char *chrom, *chromStart, *chromEnd;
 struct dyString *currentCgiUrl;
 char *upperDisease;
 char *diseaseClass;
 char *upperItemName;
 
 char *url = tdb->url;
 
 if (url != NULL && url[0] != 0)
     {
     showCompleteGadList = FALSE;
     if (cgiOptionalString("showAllRef") != NULL)
         {
         if (sameWord(cgiOptionalString("showAllRef"), "Y") ||
 	    sameWord(cgiOptionalString("showAllRef"), "y") )
 	    {
 	    showCompleteGadList = TRUE;
 	    }
 	}
     currentCgiUrl = cgiUrlString();
 
     printf("<H3>Gene %s: ", itemName);
     sqlSafef(query, sizeof(query), "select geneName from gadAll where geneSymbol='%s';", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)printf("%s", row[0]);
     printf("</H3>");
     sqlFreeResult(&sr);
 
     printf("<B>Genetic Association Database: ");
     printf("<A HREF=\"%s'%s'\" target=_blank>", url, itemName);
     printf("%s</B></A>\n", itemName);
 
     printf("<BR><B>CDC HuGE Published Literature:  ");
     printf("<A HREF=\"%s%s%s\" target=_blank>",
     "http://www.hugenavigator.net/HuGENavigator/searchSummary.do?firstQuery=",
            itemName,
     "&publitSearchType=now&whichContinue=firststart&check=n&dbType=publit&Mysubmit=go");
     printf("%s</B></A>\n", itemName);
 
     sqlSafef(query, sizeof(query),
           "select distinct g.omimId, o.title from gadAll g, hgFixed.omimTitle o where g.geneSymbol='%s' and g.omimId <>'.' and g.omimId=o.omimId",
           itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL) printf("<BR><B>OMIM: </B>");
     while (row != NULL)
         {
 	printf("<A HREF=\"%s%s\" target=_blank>",
 		"http://www.ncbi.nlm.nih.gov/omim/", row[0]);
 	printf("%s</B></A> %s\n", row[0], row[1]);
 	row = sqlNextRow(sr);
         }
     sqlFreeResult(&sr);
 
     /* List disease classes associated with the gene */
     sqlSafef(query, sizeof(query),
           "select distinct diseaseClass from gadAll where geneSymbol='%s' and association = 'Y' order by diseaseClass",
     itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
 
     if (row != NULL)
         {
         diseaseClass = row[0];
 	printf("<BR><B>Disease Class:  </B>");
 	printf("%s", diseaseClass);
         row = sqlNextRow(sr);
         }
 
     while (row != NULL)
         {
         diseaseClass = row[0];
 	printf(", %s", diseaseClass);
         row = sqlNextRow(sr);
 	}
     sqlFreeResult(&sr);
 
     /* List diseases associated with the gene */
     sqlSafef(query, sizeof(query),
           "select distinct broadPhen from gadAll where geneSymbol='%s' and association = 'Y' order by broadPhen;",
     itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
 
     if (row != NULL)
         {
         upperDisease = replaceChars(row[0], "'", "''");
 	touppers(upperDisease);
 	printf("<BR><B>Positive Disease Associations:  </B>");
 
 	printf("<A HREF=\"%s",
 	"http://geneticassociationdb.nih.gov/cgi-bin/tableview.cgi?table=allview&cond=upper(DISEASE)%20like%20'%25");
 	printf("%s", cgiEncode(upperDisease));
 
 	upperItemName = strdup(itemName);
 	touppers(upperItemName);
 	printf("%s%s%s\" target=_blank>", "%25'%20AND%20upper(GENE)%20%20like%20'%25", upperItemName, "%25'");
 	printf("%s</B></A>\n", row[0]);
         row = sqlNextRow(sr);
         }
 
     while (row != NULL)
         {
         upperDisease = replaceChars(row[0], "'", "''");
 	touppers(upperDisease);
 	printf(", <A HREF=\"%s%s%s%s%s\" target=_blank>",
 	"http://geneticassociationdb.nih.gov/cgi-bin/tableview.cgi?table=allview&cond=upper(DISEASE)%20like%20'%25",
 	cgiEncode(upperDisease), "%25'%20AND%20upper(GENE)%20%20like%20'%25", itemName, "%25'");
 	printf("%s</B></A>\n", row[0]);
         row = sqlNextRow(sr);
 	}
     sqlFreeResult(&sr);
 
     refPrinted = 0;
     sqlSafef(query, sizeof(query),
           "select broadPhen,reference,title,journal, pubMed, conclusion from gadAll where geneSymbol='%s' and association = 'Y' and title != '' order by broadPhen",
        itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
 
     if (row != NULL) printf("<BR><BR><B>Related Studies: </B><OL>");
     while (row != NULL)
         {
         printf("<LI><B>%s </B>", row[0]);
 
 	printf("<br>%s, %s, %s.\n", row[1], row[2], row[3]);
 	if (!sameWord(row[4], ""))
 	    {
 	    printf(" [PubMed ");
 	    printf("<A HREF=\"");
 	    printEntrezPubMedUidAbstractUrl(stdout, atoi(row[4]));
 	    printf("\" target=_blank>%s</B></A>]\n", row[4]);
 	    }
 	printf("<br><i>%s</i>\n", row[5]);
 
 	printf("</LI>\n");
         refPrinted++;
         if ((!showCompleteGadList) && (refPrinted >= 5)) break;
 	row = sqlNextRow(sr);
         }
     sqlFreeResult(&sr);
     printf("</OL>");
 
     if ((!showCompleteGadList) && (row != NULL))
         {
         printf("<B>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; more ...  </B>");
         printf("<A HREF=\"%s?showAllRef=Y&%s\">click here to view the complete list</A> ",
                hgcName(), currentCgiUrl->string);
         }
 
     sqlSafef(query, sizeof(query),
           "select chrom, chromStart, chromEnd from gad where name='%s';", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	chrom      = row[0];
         chromStart = row[1];
 	chromEnd   = row[2];
 	printf("<HR>");
 	printPosOnChrom(chrom, atoi(chromStart), atoi(chromEnd), NULL, FALSE, itemName);
         }
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 }
 
 void doGad(struct trackDb *tdb, char *item, char *itemForUrl)
 /* Put up GAD track info. */
 {
 genericHeader(tdb, item);
 printGadDetails(tdb, item, FALSE);
 printTrackHtml(tdb);
 }
 
 void printCosmicDetails(struct trackDb *tdb, char *itemName)
 /* Print details of a COSMIC entry. */
 {
 struct sqlConnection *conn  = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 char query[1024];
 char query2[1024];
 struct sqlResult *sr;
 struct sqlResult *sr2;
 char **row;
 char **row2;
 
 char *chp;
 char indent1[40] = {"&nbsp;&nbsp;&nbsp;&nbsp;"};
 char indent2[40] = {""};
 
 char *gene_name, *accession_number;
 // char $source, *cosmic_mutation_id;  unused variable
 char *mut_description, *mut_syntax_cds, *mut_syntax_aa;
 char *chromosome, *grch37_start, *grch37_stop, *mut_nt;
 char *mut_aa, *tumour_site, *mutated_samples, *examined_samples, *mut_freq;
 char *url = tdb->url;
 
 char *chrom, *chromStart, *chromEnd;
 chrom      = cartOptionalString(cart, "c");
 chromStart = cartOptionalString(cart, "o");
 chromEnd   = cartOptionalString(cart, "t");
 
 sqlSafef(query, sizeof(query),
       "select source,cosmic_mutation_id,gene_name,accession_number,mut_description,mut_syntax_cds,mut_syntax_aa,"
       "chromosome,grch37_start,grch37_stop,mut_nt,mut_aa,tumour_site,mutated_samples,examined_samples,mut_freq"
       " from cosmicRaw where cosmic_mutation_id='%s'",
       itemName);
 
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     int ii;
     boolean multipleTumorSites;
     char *indentString;
 
     ii=0;
 
     ii++; // source              = row[ii];ii++;  unused variable
     ii++; // cosmic_mutation_id  = row[ii];ii++;  unused variable
     gene_name           = row[ii];ii++;
     accession_number    = row[ii];ii++;
     mut_description     = row[ii];ii++;
     mut_syntax_cds      = row[ii];ii++;
     mut_syntax_aa       = row[ii];ii++;
 
     chromosome          = row[ii];ii++;
     grch37_start        = row[ii];ii++;
     grch37_stop         = row[ii];ii++;
     mut_nt              = row[ii];ii++;
     mut_aa              = row[ii];ii++;
     tumour_site         = row[ii];ii++;
     mutated_samples     = row[ii];ii++;
     examined_samples    = row[ii];ii++;
     mut_freq            = row[ii];ii++;
 
     // chromosome name adjustment
     if (sameString(chromosome, "23"))
 	chromosome = "X";    
     if (sameString(chromosome, "24"))
 	chromosome = "Y";    
     if (sameString(chromosome, "25"))
 	chromosome = "M";    
 
     chp = strstr(itemName, "COSM")+strlen("COSM");
     printf("<B>COSMIC ID:</B> <A HREF=\"%s%s\" TARGET=_BLANK>%s</A> (details at COSMIC site)", url, chp, chp);
 
     // Embed URL to COSMIC site per COSMICT request.
     // printf("<BR><B>Source:</B> ");
     // printf("<A HREF=\"http://cancer.sanger.ac.uk/cancergenome/projects/cosmic/\" TARGET=_BLANK>%s</A>\n", source);
 
     printf("<BR><B>Gene Name:</B> %s\n", gene_name);
     printf("<BR><B>Accession Number:</B> %s\n", accession_number);
     printf("<BR><B>Genomic Position:</B> chr%s:%s-%s", chromosome, grch37_start, grch37_stop);
     printf("<BR><B>Mutation Description:</B> %s\n", mut_description);
     printf("<BR><B>Mutation Syntax CDS:</B> %s\n", mut_syntax_cds);
     printf("<BR><B>Mutation Syntax AA:</B> %s\n", mut_syntax_aa);
     printf("<BR><B>Mutation NT:</B> %s\n", mut_nt);
     printf("<BR><B>Mutation AA:</B> %s\n", mut_aa);
 
     sqlSafef(query2, sizeof(query2),
       "select count(tumour_site) from cosmicRaw where cosmic_mutation_id='%s'", itemName);
 
     sr2 = sqlMustGetResult(conn2, query2);
     row2 = sqlNextRow(sr2);
     if ((atoi(row2[0])) > 1)
         {
 	multipleTumorSites = TRUE;
         indentString = indent1;
 	}
     else
         {
         multipleTumorSites = FALSE;
         indentString = indent2;
         }
     sqlFreeResult(&sr2);
 
     sqlSafef(query2, sizeof(query2),
       "select tumour_site,mutated_samples,examined_samples,mut_freq "
       " from cosmicRaw where cosmic_mutation_id='%s' order by tumour_site",
       itemName);
 
     sr2 = sqlMustGetResult(conn2, query2);
     row2 = sqlNextRow(sr2);
     while (row2 != NULL)
         {
         int ii;
         ii=0;
         tumour_site             = row2[ii];ii++;
         mutated_samples         = row2[ii];ii++;
         examined_samples        = row2[ii];ii++;
         mut_freq                = row2[ii];ii++;
 
         if (multipleTumorSites) printf("<BR>");
         printf("<BR><B>%sTumor Site:</B> %s\n",         indentString, tumour_site);
         printf("<BR><B>%sMutated Samples:</B> %s\n",    indentString, mutated_samples);
         printf("<BR><B>%sExamined Samples:</B> %s\n",   indentString, examined_samples);
         printf("<BR><B>%sMutation Frequency:</B> %s\n", indentString, mut_freq);
         row2 = sqlNextRow(sr2);
         }
     sqlFreeResult(&sr2);
 
     sqlSafef(query2, sizeof(query2),
       "select sum(mutated_samples) from cosmicRaw where cosmic_mutation_id='%s'",
       itemName);
 
     sr2 = sqlMustGetResult(conn2, query2);
     row2 = sqlNextRow(sr2);
     if (row2 != NULL)
         {
         printf("<BR><BR><B>Total Mutated Samples:</B> %s\n", row2[0]);
         //printf("<br>%s ", row2[0]);
         }
     sqlFreeResult(&sr2);
 
     sqlSafef(query2, sizeof(query2),
       "select sum(examined_samples) from cosmicRaw where cosmic_mutation_id='%s'",
       itemName);
     sr2 = sqlMustGetResult(conn2, query2);
     row2 = sqlNextRow(sr2);
     if (row2 != NULL)
         {
         printf("<BR><B>Total Examined Samples:</B> %s\n", row2[0]);
 	}
     sqlFreeResult(&sr2);
     sqlSafef(query2, sizeof(query2),
       "select sum(mutated_samples)*100/sum(examined_samples) from cosmicRaw where cosmic_mutation_id='%s'",
       itemName);
     sr2 = sqlMustGetResult(conn2, query2);
     row2 = sqlNextRow(sr2);
     if (row2 != NULL)
         {
         char *chp;
 	chp = strstr(row2[0], ".");
 	if ((chp != NULL) && (strlen(chp) > 3))
 	   {
 	   chp++;
 	   chp++;
 	   chp++;
 	   chp++;
 	   *chp = '\0';
 	   }
 	printf("<BR><B>Total Mutation Frequency:</B> %s%c\n", row2[0], '%');
 	//printf("<br>%s", row2[0]);
 	}
     sqlFreeResult(&sr2);
 
     }
 
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 
 printf("<HR>");
 printPosOnChrom(chrom, atoi(chromStart), atoi(chromEnd), NULL, FALSE, itemName);
 }
 
 void doCosmic(struct trackDb *tdb, char *item)
 /* Put up COSMIC track info. */
 {
 genericHeader(tdb, item);
 printCosmicDetails(tdb, item);
 printTrackHtml(tdb);
 }
 
 void printDecipherDetails(struct trackDb *tdb, char *itemName, boolean encode)
 /* Print details of a DECIPHER entry. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 struct sqlConnection *conn2 = hAllocConn(database);
 char query2[256];
 struct sqlResult *sr2;
 char **row2;
 char *strand={"+"};
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 char *chrom = cartString(cart, "c");
 
 printf("<H3>Patient %s </H3>", itemName);
 
 /* print phenotypes and other information, if available */
 if (sqlFieldIndex(conn, "decipherRaw", "phenotypes") >= 0)
     {
     sqlSafef(query, sizeof(query),
         "select phenotypes, mean_ratio, inheritance, pathogenicity, contribution "
         "from decipherRaw where id = '%s'", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if ((row != NULL) && strlen(row[0]) >= 1)
         {
         char *phenoString = replaceChars(row[0], "|", "</li>\n<li>");
         printf("<b>Phenotypes:</b>\n<ul>\n"
                "<li>%s</li>\n"
                "</ul>\n", phenoString);
         // freeMem(phenoString);
         }
     if (row != NULL)
         {
         if (isNotEmpty(row[1]))
             {
             printf("<b>Mean Ratio:</b> %s\n<br>\n", row[1]);
             }
         if (isNotEmpty(row[2]))
             {
             printf("<b>Inheritance:</b> %s\n<br>\n", row[2]);
             }
         if (isNotEmpty(row[3]))
             {
             printf("<b>Pathogenicity:</b> %s\n<br>\n", row[3]);
             }
         if (isNotEmpty(row[4]))
             {
             printf("<b>Contribution:</b> %s\n<br>\n", row[4]);
             }
         }
     sqlFreeResult(&sr);
     }
 else
     {
     sqlSafef(query, sizeof(query),
           "select distinct phenotype from decipherRaw where id ='%s' order by phenotype", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if ((row != NULL) && strlen(row[0]) >= 1)
         {
         printf("<B>Phenotype: </B><UL>");
         while (row != NULL)
             {
         printf("<LI>");
         printf("%s\n", row[0]);
         row = sqlNextRow(sr);
             }
         printf("</UL>");
         }
     sqlFreeResult(&sr);
     }
 
 /* link to Ensembl DECIPHER Patient View page */
 printf("<B>Patient View: </B>\n");
 printf("More details on patient %s at ", itemName);
 printf("<A HREF=\"%s%s\" target=_blank>",
        "https://decipher.sanger.ac.uk/patient/", itemName);
 printf("DECIPHER</A>.<BR><BR>");
 
 /* print position info */
 printPosOnChrom(chrom, start, end, strand, TRUE, itemName);
 
 /* print UCSC Genes in the reported region */
 sqlSafef(query, sizeof(query),
-      "select distinct t.name from knownCanonToDecipher t, kgXref x  where value ='%s' and x.kgId=t.name order by geneSymbol", itemName);
+      "select distinct t.name "
+      // mysql 5.7: SELECT list w/DISTINCT must include all fields in ORDER BY list (#18626)
+      ", geneSymbol "
+      "from knownCanonToDecipher t, kgXref x  where value ='%s' and x.kgId=t.name order by geneSymbol", itemName);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     printf("<BR><B>UCSC Canonical Gene(s) in this genomic region: </B><UL>");
     while (row != NULL)
         {
 	sqlSafef(query2, sizeof(query2),
         "select geneSymbol, kgId, description from kgXref where kgId ='%s'", row[0]);
 	sr2 = sqlMustGetResult(conn2, query2);
 	row2 = sqlNextRow(sr2);
 	if (row2 != NULL)
             {
 	    printf("<LI>");
             printf("<A HREF=\"%s%s\" target=_blank>","./hgGene\?hgg_chrom=none&hgg_gene=", row2[1]);
             printf("%s (%s)</A> ", row2[0], row2[1]);
 	    printf(" %s", row2[2]);
 	    }
         sqlFreeResult(&sr2);
 	row = sqlNextRow(sr);
 	}
     sqlFreeResult(&sr);
     printf("</UL>");
     }
 hFreeConn(&conn);
 hFreeConn(&conn2);
 }
 
 void doDecipher(struct trackDb *tdb, char *item, char *itemForUrl)
 /* Put up DECIPHER track info. */
 {
 genericHeader(tdb, item);
 printDecipherDetails(tdb, item, FALSE);
 printTrackHtml(tdb);
 }
 
 char *gbCdnaGetDescription(struct sqlConnection *conn, char *acc)
 /* return mrna description, or NULL if not available. freeMem result */
 {
 char query[1024];
 if (!sqlTableExists(conn, gbCdnaInfoTable))
     return NULL;
 sqlSafef(query, sizeof(query),
       "select d.name from %s g,%s d where (acc = '%s') and (g.description = d.id)", gbCdnaInfoTable, descriptionTable, acc);
 char *desc = sqlQuickString(conn, query);
 if ((desc == NULL) || sameString(desc, "n/a") || (strlen(desc) == 0))
     freez(&desc);
 return desc;
 }
 
 void printOmimGeneDetails(struct trackDb *tdb, char *itemName, boolean encode)
 /* Print details of an OMIM Gene entry. */
 {
 struct sqlConnection *conn  = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 char *url = tdb->url;
 char *kgId= NULL;
 char *title1 = NULL;
 char *title2 = NULL;
 char *geneSymbols = NULL;
 char *chrom, *chromStart, *chromEnd;
 char *kgDescription = NULL;
 char *refSeq;
 
 chrom      = cartOptionalString(cart, "c");
 chromStart = cartOptionalString(cart, "o");
 chromEnd   = cartOptionalString(cart, "t");
 
 if (url != NULL && url[0] != 0)
     {
     /* check if the entry is in morbidmap, if so remember the assoicated gene symbols */
     sqlSafef(query, sizeof(query),
           "select geneSymbols from omimMorbidMap where omimId=%s;", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	geneSymbols = cloneString(row[0]);
 	}
     sqlFreeResult(&sr);
 
     /* get corresponding KG ID */
     sqlSafef(query, sizeof(query),
 	  "select k.transcript from knownCanonical k where k.chrom='%s' and k.chromStart=%s and k.chromEnd=%s",
 	  chrom, chromStart, chromEnd);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	kgId = cloneString(row[0]);
 	}
     sqlFreeResult(&sr);
 
     /* use geneSymbols from omimMorbidMap if available */
     if (geneSymbols!= NULL)
         {
 	printf("<B>OMIM gene or syndrome:</B> %s", geneSymbols);
 	printf("<BR>\n");
 
 	/* display disorder for genes in morbidmap */
         sqlSafef(query, sizeof(query), "select description from omimMorbidMap where omimId=%s;",
               itemName);
         sr = sqlMustGetResult(conn, query);
         while ((row = sqlNextRow(sr)) != NULL)
             {
             printf("<B>Disorder:</B> %s", row[0]);
             printf("<BR>\n");
             }
         sqlFreeResult(&sr);
 	}
     else
 	{
 	/* display gene symbol(s) from omimGenemap  */
         sqlSafef(query, sizeof(query), "select geneSymbol from omimGeneMap where omimId=%s;", itemName);
         sr = sqlMustGetResult(conn, query);
         row = sqlNextRow(sr);
         if (row != NULL)
             {
             printf("<B>OMIM Gene Symbol:</B> %s", row[0]);
             printf("<BR>\n");
             sqlFreeResult(&sr);
             }
 	else
             {
             /* get gene symbol from kgXref if the entry is not in morbidmap and omim genemap */
             sqlSafef(query, sizeof(query), "select geneSymbol from kgXref where kgId='%s';", kgId);
 
             sr = sqlMustGetResult(conn, query);
             row = sqlNextRow(sr);
             if (row != NULL)
                 {
                 printf("<B>UCSC Gene Symbol:</B> %s", row[0]);
                 printf("<BR>\n");
                 }
             sqlFreeResult(&sr);
             }
 	}
     printf("<B>OMIM Database ");
     printf("<A HREF=\"%s%s\" target=_blank>", url, itemName);
     printf("%s</A></B>", itemName);
 
     sqlSafef(query, sizeof(query),
           "select title1, title2 from omimGeneMap where omimId=%s;", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	if (row[0] != NULL)
 	    {
 	    title1 = cloneString(row[0]);
             printf(": %s", title1);
 	    }
 	if (row[1] != NULL)
 	    {
 	    title2 = cloneString(row[1]);
             printf(" %s ", title2);
 	    }
 	}
     sqlFreeResult(&sr);
 
     printf("<BR>\n");
 
     if (kgId != NULL)
         {
         printf("<B>UCSC Canonical Gene ");
         printf("<A HREF=\"%s%s&hgg_chrom=none\" target=_blank>",
                "../cgi-bin/hgGene?hgg_gene=", kgId);
         printf("%s</A></B>: ", kgId);
 
         sqlSafef(query, sizeof(query), "select refseq from kgXref where kgId='%s';", kgId);
         sr = sqlMustGetResult(conn, query);
         row = sqlNextRow(sr);
         if (row != NULL)
 	    {
 	    refSeq = strdup(row[0]);
 	    kgDescription = gbCdnaGetDescription(conn2, refSeq);
 	    }
 	sqlFreeResult(&sr);
         hFreeConn(&conn2);
 
 	if (kgDescription == NULL)
 	    {
             sqlSafef(query, sizeof(query), "select description from kgXref where kgId='%s';", kgId);
             sr = sqlMustGetResult(conn, query);
             row = sqlNextRow(sr);
             if (row != NULL)
                 {
                 printf("%s", row[0]);
                 }
 
             sqlFreeResult(&sr);
             }
         else
             {
 	    printf("%s", kgDescription);
 	    }
         printf("<BR>\n");
 
 	sqlSafef(query, sizeof(query),
               "select i.transcript from knownIsoforms i, knownCanonical c where c.transcript='%s' and i.clusterId=c.clusterId and i.transcript <>'%s'",
 	      kgId, kgId);
         sr = sqlMustGetResult(conn, query);
 	if (sr != NULL)
 	    {
 	    int printedCnt;
 	    printedCnt = 0;
 	    while ((row = sqlNextRow(sr)) != NULL)
                 {
                 if (printedCnt < 1)
 		    printf("<B>Other UCSC Gene(s) in the same cluster: </B>");
 		else
 		    printf(", ");
                 printf("<A HREF=\"%s%s&hgg_chrom=none\" target=_blank>",
                        "../cgi-bin/hgGene?hgg_gene=", row[0]);
                 printf("%s</A></B>", row[0]);
                 printedCnt++;
 		}
             if (printedCnt >= 1) printf("<BR>\n");
 	    }
 	sqlFreeResult(&sr);
 	}
     }
 
 printf("<HR>");
 printPosOnChrom(chrom, atoi(chromStart), atoi(chromEnd), NULL, FALSE, itemName);
 }
 
 #include "omim.h"
 
 void printOmimGene2Details(struct trackDb *tdb, char *itemName, boolean encode)
 /* Print details of an omimGene2 entry. */
 {
 struct sqlConnection *conn  = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 char *url = tdb->url;
 char *title1 = NULL;
 char *title2 = NULL;
 char *geneSymbol = NULL;
 char *chrom, *chromStart, *chromEnd;
 
 chrom      = cartOptionalString(cart, "c");
 chromStart = cartOptionalString(cart, "o");
 chromEnd   = cartOptionalString(cart, "t");
 
 if (url != NULL && url[0] != 0)
     {
     printf("<B>OMIM: ");
     printf("<A HREF=\"%s%s\" target=_blank>", url, itemName);
     printf("%s</A></B>", itemName);
     sqlSafef(query, sizeof(query),
           "select title1, title2 from omimGeneMap where omimId=%s;", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	if (row[0] != NULL)
 	    {
 	    title1 = cloneString(row[0]);
             printf(" %s", title1);
 	    }
 	if (row[1] != NULL)
 	    {
 	    title2 = cloneString(row[1]);
             printf(" %s ", title2);
 	    }
 	}
     else
         {
 	printf("<BR>");
 	}
     sqlFreeResult(&sr);
 
     // disable NCBI link until they work it out with OMIM
     /*
     printf("<BR>\n");
     printf("<B>OMIM page at NCBI: ");
     printf("<A HREF=\"%s%s\" target=_blank>", ncbiOmimUrl, itemName);
     printf("%s</A></B>", itemName);
     */
 
     sqlSafef(query, sizeof(query),
           "select geneSymbol from omimGeneMap where omimId=%s;", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	geneSymbol = cloneString(row[0]);
 	}
     sqlFreeResult(&sr);
 
     if (geneSymbol!= NULL)
         {
 	boolean disorderShown;
 	char *phenotypeClass, *phenotypeId, *disorder;
 
 	printf("<BR><B>Gene symbol(s):</B> %s", geneSymbol);
 	printf("<BR>\n");
 
 	/* display disorder(s) */
         sqlSafef(query, sizeof(query),
 	      "select description, %s, phenotypeId from omimPhenotype where omimId=%s order by description",
 	      omimPhenotypeClassColName, itemName);
 	sr = sqlMustGetResult(conn, query);
 	disorderShown = FALSE;
         while ((row = sqlNextRow(sr)) != NULL)
             {
 	    if (!disorderShown)
                 {
                 printf("<B>Disorder(s):</B><UL>\n");
 		disorderShown = TRUE;
 		}
 	    disorder       = row[0];
             phenotypeClass = row[1];
             phenotypeId    = row[2];
             printf("<LI>%s", disorder);
             if (phenotypeId != NULL)
                 {
                 if (!sameWord(phenotypeId, "-1"))
                     {
                     printf(" (phenotype <A HREF=\"%s%s\" target=_blank>", url, phenotypeId);
                     printf("%s</A></B>", phenotypeId);
 		    // show phenotype class if available
 		    if (!sameWord(phenotypeClass, "-1")) printf(" (%s)", phenotypeClass);
 		    printf(")");
 		    }
 		else
 		    {
 		    // show phenotype class if available, even phenotypeId is not available
 		    if (!sameWord(phenotypeClass, "-1")) printf(" (%s)", phenotypeClass);
 		    }
 
 		}
 	    printf("<BR>\n");
 	    }
 	if (disorderShown) printf("</UL>\n");
         sqlFreeResult(&sr);
 	}
 
     // show RefSeq Gene link(s)
     sqlSafef(query, sizeof(query),
           "select distinct locusLinkId from %s l, omim2gene g, refGene r where l.omimId=%s and g.geneId=l.locusLinkId and g.entryType='gene' and chrom='%s' and txStart = %s and txEnd= %s",
 	  refLinkTable, itemName, chrom, chromStart, chromEnd);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
         char *geneId;
         geneId = strdup(row[0]);
         sqlFreeResult(&sr);
 
         sqlSafef(query, sizeof(query),
               "select distinct l.mrnaAcc from %s l where locusLinkId = '%s' order by mrnaAcc asc", refLinkTable, geneId);
         sr = sqlMustGetResult(conn, query);
         if (sr != NULL)
 	    {
 	    int printedCnt;
 	    printedCnt = 0;
 	    while ((row = sqlNextRow(sr)) != NULL)
                 {
                 if (printedCnt < 1)
 		    printf("<B>RefSeq Gene(s): </B>");
                 else
 		    printf(", ");
                 printf("<A HREF=\"%s%s&o=%s&t=%s\">", "../cgi-bin/hgc?g=refGene&i=",
                        row[0], chromStart, chromEnd);
                 printf("%s</A></B>", row[0]);
 	        printedCnt++;
 	        }
             if (printedCnt >= 1) printf("<BR>\n");
 	    }
         sqlFreeResult(&sr);
         }
 
     // show Related UCSC Gene links
     sqlSafef(query, sizeof(query),
           "select distinct kgId from kgXref x, %s l, omim2gene g where x.refseq = mrnaAcc and l.omimId=%s and g.omimId=l.omimId and g.entryType='gene'",
 	  refLinkTable, itemName);
     sr = sqlMustGetResult(conn, query);
     if (sr != NULL)
 	{
 	int printedCnt;
 	printedCnt = 0;
 	while ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    if (printedCnt < 1)
 		printf("<B>Related UCSC Gene(s): </B>");
 	    else
 		printf(", ");
             printf("<A HREF=\"%s%s&hgg_chrom=none\">", "../cgi-bin/hgGene?hgg_gene=", row[0]);
             printf("%s</A></B>", row[0]);
 	    printedCnt++;
 	    }
         if (printedCnt >= 1) printf("<BR>\n");
 	}
     sqlFreeResult(&sr);
 
     // show GeneReviews  link(s)
     if (sqlTableExists(conn, "geneReviewsDetail"))
         {
         sqlSafef(query, sizeof(query),
           "select distinct r.name2 from %s l, omim2gene g, refGene r where l.omimId=%s and g.geneId=l.locusLinkId and g.entryType='gene' and chrom='%s' and txStart = %s and txEnd= %s",
         refLinkTable, itemName, chrom, chromStart, chromEnd);
         sr = sqlMustGetResult(conn, query);
         if (sr != NULL)
             {
             while ((row = sqlNextRow(sr)) != NULL)
                 {
                 prGRShortRefGene(row[0]);
                 }
             }
         sqlFreeResult(&sr);
         }
 
     }
 
 printf("<HR>");
 printPosOnChrom(chrom, atoi(chromStart), atoi(chromEnd), NULL, FALSE, itemName);
 }
 
 void printOmimLocationDetails(struct trackDb *tdb, char *itemName, boolean encode)
 /* Print details of an OMIM Class 3 Gene entry. */
 {
 struct sqlConnection *conn  = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 char *url = tdb->url;
 char *kgId= NULL;
 char *title1 = NULL;
 char *title2 = NULL;
 char *geneSymbol = NULL;
 char *chrom, *chromStart, *chromEnd;
 char *kgDescription = NULL;
 char *refSeq;
 char *omimId;
 
 chrom      = cartOptionalString(cart, "c");
 chromStart = cartOptionalString(cart, "o");
 chromEnd   = cartOptionalString(cart, "t");
 
 omimId = itemName;
 
 if (url != NULL && url[0] != 0)
     {
     printf("<B>OMIM: ");
     printf("<A HREF=\"%s%s\" target=_blank>", url, itemName);
     printf("%s</A></B>", itemName);
     sqlSafef(query, sizeof(query),
           "select title1, title2 from omimGeneMap where omimId=%s;", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	if (row[0] != NULL)
 	    {
 	    title1 = cloneString(row[0]);
             printf(": %s", title1);
 	    }
 	if (row[1] != NULL)
 	    {
 	    title2 = cloneString(row[1]);
             printf(" %s ", title2);
 	    }
 	}
     sqlFreeResult(&sr);
     printf("<BR>");
 
     // disable NCBI link until they work it out with OMIM
     /*
     printf("<B>OMIM page at NCBI: ");
     printf("<A HREF=\"%s%s\" target=_blank>", ncbiOmimUrl, itemName);
     printf("%s</A></B><BR>", itemName);
     */
 
     printf("<B>Location: </B>");
     sqlSafef(query, sizeof(query),
           "select location from omimGeneMap where omimId=%s;", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	if (row[0] != NULL)
 	    {
 	    char *locStr;
 	    locStr= cloneString(row[0]);
             printf("%s\n", locStr);
 	    }
 	}
     sqlFreeResult(&sr);
 
     printf("<BR>\n");
     sqlSafef(query, sizeof(query),
           "select geneSymbol from omimGeneMap where omimId=%s;", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	geneSymbol = cloneString(row[0]);
 	}
     sqlFreeResult(&sr);
 
     sqlSafef(query, sizeof(query),"select omimId from omimPhenotype where omimId=%s\n", omimId);
     if (sqlQuickNum(conn, query) > 0)
         {
 	char *phenotypeClass, *phenotypeId, *disorder;
 
 	printf("<B>Gene symbol(s):</B> %s", geneSymbol);
 	printf("<BR>\n");
 
 	/* display disorder for genes in morbidmap */
         sqlSafef(query, sizeof(query),
 	      "select description, %s, phenotypeId from omimPhenotype where omimId=%s order by description",
 	      omimPhenotypeClassColName, itemName);
         sr = sqlMustGetResult(conn, query);
         printf("<B>Disorder(s):</B><UL>\n");
         while ((row = sqlNextRow(sr)) != NULL)
             {
 	    disorder       = row[0];
             phenotypeClass = row[1];
             phenotypeId    = row[2];
             printf("<LI>%s", disorder);
             if (phenotypeId != NULL)
                 {
                 if (!sameWord(phenotypeId, "-1"))
                     {
                     printf(" (phenotype <A HREF=\"%s%s\" target=_blank>", url, phenotypeId);
                     printf("%s</A></B>)", phenotypeId);
 		    }
 		if (!sameWord(phenotypeClass, "-1"))
 		    {
                     printf(" (%s)", phenotypeClass);
 		    }
 		}
 	    printf("<BR>\n");
 	    }
 	printf("</UL>\n");
         sqlFreeResult(&sr);
 	}
     else
 	{
 	/* display gene symbol(s) from omimGenemap  */
         sqlSafef(query, sizeof(query), "select geneSymbol from omimGeneMap where omimId=%s;", itemName);
         sr = sqlMustGetResult(conn, query);
         row = sqlNextRow(sr);
         if (row != NULL)
             {
             printf("<B>OMIM Gene Symbol:</B> %s", row[0]);
             printf("<BR>\n");
             sqlFreeResult(&sr);
             }
 	else
             {
             /* get gene symbol from kgXref if the entry is not in morbidmap and omim genemap */
             sqlSafef(query, sizeof(query), "select geneSymbol from kgXref where kgId='%s';", kgId);
 
             sr = sqlMustGetResult(conn, query);
             row = sqlNextRow(sr);
             if (row != NULL)
                 {
                 printf("<B>UCSC Gene Symbol:</B> %s", row[0]);
                 printf("<BR>\n");
                 }
             sqlFreeResult(&sr);
             }
 	}
 
     if (kgId != NULL)
         {
         printf("<B>UCSC Canonical Gene ");
         printf("<A HREF=\"%s%s&hgg_chrom=none\" target=_blank>",
                "../cgi-bin/hgGene?hgg_gene=", kgId);
         printf("%s</A></B>: ", kgId);
 
         sqlSafef(query, sizeof(query), "select refseq from kgXref where kgId='%s';", kgId);
         sr = sqlMustGetResult(conn, query);
         row = sqlNextRow(sr);
         if (row != NULL)
 	    {
 	    refSeq = strdup(row[0]);
 	    kgDescription = gbCdnaGetDescription(conn2, refSeq);
 	    }
 	sqlFreeResult(&sr);
         hFreeConn(&conn2);
 
 	if (kgDescription == NULL)
 	    {
             sqlSafef(query, sizeof(query), "select description from kgXref where kgId='%s';", kgId);
             sr = sqlMustGetResult(conn, query);
             row = sqlNextRow(sr);
             if (row != NULL)
                 {
                 printf("%s", row[0]);
                 }
 
             sqlFreeResult(&sr);
             }
         else
             {
 	    printf("%s", kgDescription);
 	    }
         printf("<BR>\n");
 
 	sqlSafef(query, sizeof(query),
 	      "select i.transcript from knownIsoforms i, knownCanonical c where c.transcript='%s' and i.clusterId=c.clusterId and i.transcript <>'%s'",
 	      kgId, kgId);
         sr = sqlMustGetResult(conn, query);
 	if (sr != NULL)
 	    {
 	    int printedCnt;
 	    printedCnt = 0;
 	    while ((row = sqlNextRow(sr)) != NULL)
                 {
 	        if (printedCnt < 1)
 		    printf("<B>Other UCSC Gene(s) in the same cluster: </B>");
 		else
 		    printf(", ");
                 printf("<A HREF=\"%s%s&hgg_chrom=none\" target=_blank>",
                        "../cgi-bin/hgGene?hgg_gene=", row[0]);
                 printf("%s</A></B>", row[0]);
                 printedCnt++;
 		}
             if (printedCnt >= 1) printf("<BR>\n");
 	    }
 	sqlFreeResult(&sr);
 	}
     }
 
 printf("<HR>");
 printPosOnChrom(chrom, atoi(chromStart), atoi(chromEnd), NULL, FALSE, itemName);
 }
 
 void doOmimLocation(struct trackDb *tdb, char *item)
 /* Put up OmimGene track info. */
 {
 genericHeader(tdb, item);
 printOmimLocationDetails(tdb, item, FALSE);
 printTrackHtml(tdb);
 }
 
 void printOmimAvSnpDetails(struct trackDb *tdb, char *itemName, boolean encode)
 /* Print details of an OMIM AvSnp entry. */
 {
 struct sqlConnection *conn  = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 char *url = tdb->url;
 char *title1 = NULL;
 char *title2 = NULL;
 char *chrom, *chromStart, *chromEnd;
 char *avId;
 char *dbSnpId;
 char *chp;
 char avString[255];
 char *avDesc = NULL;
 
 chrom      = cartOptionalString(cart, "c");
 chromStart = cartOptionalString(cart, "o");
 chromEnd   = cartOptionalString(cart, "t");
 
 avId       = strdup(itemName);
 
 chp = strstr(avId, "-");
 if (chp != NULL) *chp = '\0';
 
 safef(avString, sizeof(avString), "%s", itemName);
 
 chp = strstr(itemName, ".");
 *chp = '\0';
 
 chp = avString;
 chp = strstr(avString, ".");
 *chp = '#';
 
 if (url != NULL && url[0] != 0)
     {
     sqlSafef(query, sizeof(query),
           "select title1, title2,  format(seqNo/10000,4), v.description"
            " from omimGeneMap m, omimAv v"
           " where m.omimId=%s and m.omimId=v.omimId and v.avId='%s';", itemName, avId);
 
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	if (row[0] != NULL)
 	    {
 	    title1 = cloneString(row[0]);
 	    }
 	if (row[1] != NULL)
 	    {
 	    title2 = cloneString(row[1]);
 	    }
 	avDesc = cloneString(row[3]);
 	}
     sqlFreeResult(&sr);
 
     printf("<B>OMIM Allelic Variant: ");
     printf("<A HREF=\"%s%s\" target=_blank>", url, avString);
     printf("%s</A></B>", avId);
     printf(" %s", avDesc);
 
     printf("<BR><B>OMIM: ");
     printf("<A HREF=\"%s%s\" target=_blank>", url, itemName);
     printf("%s</A></B>", itemName);
     if (title1 != NULL) printf(": %s", title1);
     if (title2 != NULL) printf(" %s ", title2);
 
     // disable NCBI link until they work it out with OMIM
     /*
     printf("<BR>\n");
     printf("<B>OMIM page at NCBI: ");
     printf("<A HREF=\"%s%s\" target=_blank>", ncbiOmimUrl, itemName);
     printf("%s</A></B><BR>", itemName);
     */
 
     sqlSafef(query, sizeof(query),
           "select repl2 from omimAv where avId=%s;", avId);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
       	if (row[0] != NULL)
 	          printf("<BR><B>Amino Acid Replacement:</B> %s\n", row[0]);
 	      }
     sqlFreeResult(&sr);
 
     printf("<BR>\n");
 
     sqlSafef(query, sizeof(query),
           "select dbSnpId from omimAv where avId='%s'", avId);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     dbSnpId = cloneString("-");
     if (row != NULL)
       	dbSnpId = cloneString(row[0]);
     sqlFreeResult(&sr);
 
     if (!sameWord(dbSnpId, "-"))
         {
         struct slName *snpIdList, *thisSnpId;
 
         printf("<b>dbSNP/ClinVar:</b> \n");
 
         /* for each variant, print name and build a link for it if possible */
         snpIdList = slNameListFromComma(dbSnpId);
         while ((thisSnpId = slPopHead(&snpIdList)) != NULL)
             {
             if (strncmp(thisSnpId->name, "rs", 2) == 0) /* dbSnp ID */
                 printDbSnpRsUrl (thisSnpId->name, "%s", thisSnpId->name);
             else if (strncmp(thisSnpId->name, "SCV", 3) == 0) /* ClinVar ID */
                 {
                 char clinVarUrl[2048];
                 safef (clinVarUrl, sizeof(clinVarUrl), clinVarFormat, thisSnpId->name);
                 printf ("<a href=\"%s\" target=\"_blank\">%s</a>", clinVarUrl, thisSnpId->name);
                 }
             else
                 printf ("%s", thisSnpId->name);
 
             slNameFree(&thisSnpId);
 
             if (snpIdList != NULL)
                 printf (",");
             }
         printf("<br>\n");
         }
     }
 
 printf("<hr>\n");
 printPosOnChrom(chrom, atoi(chromStart), atoi(chromEnd), NULL, FALSE, itemName);
 }
 
 void doOmimAvSnp(struct trackDb *tdb, char *item)
 /* Put up OmimGene track info. */
 {
 genericHeader(tdb, item);
 printOmimAvSnpDetails(tdb, item, FALSE);
 printTrackHtml(tdb);
 }
 
 void doOmimGene2(struct trackDb *tdb, char *item)
 /* Put up OmimGene track info. */
 {
 genericHeader(tdb, item);
 printOmimGene2Details(tdb, item, FALSE);
 printTrackHtml(tdb);
 }
 
 void doOmimGene(struct trackDb *tdb, char *item)
 /* Put up OmimGene track info. */
 {
 genericHeader(tdb, item);
 printOmimGeneDetails(tdb, item, FALSE);
 printTrackHtml(tdb);
 }
 
 void printRgdSslpCustomUrl(struct trackDb *tdb, char *itemName, boolean encode)
 /* Print RGD QTL URL. */
 {
 char *url = tdb->url;
 char *sslpId;
 char *chrom, *chromStart, *chromEnd;
 
 if (url != NULL && url[0] != 0)
     {
     struct sqlConnection *conn = hAllocConn(database);
     char query[256];
     struct sqlResult *sr;
     char **row;
 
     sqlSafef(query, sizeof(query), "select id from rgdSslpLink where name='%s';", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
 	sslpId = row[0];
         printf("<H2>Rat SSLP: %s</H2>", itemName);
         printf("<B>RGD SSLP Report: ");
         printf("<A HREF=\"%s%s\" target=_blank>", url, sslpId);
         printf("RGD:%s</B></A>\n", sslpId);
         }
     sqlFreeResult(&sr);
 
     sqlSafef(query, sizeof query, "select chrom, chromStart, chromEnd from rgdSslp where name='%s';", itemName);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
         chrom      = row[0];
         chromStart = row[1];
         chromEnd   = row[2];
         printf("<HR>");
         printPosOnChrom(chrom, atoi(chromStart), atoi(chromEnd), NULL, FALSE, itemName);
         }
     sqlFreeResult(&sr);
 
     hFreeConn(&conn);
     }
 }
 
 void doVax003Vax004(struct trackDb *tdb, char *item)
 /* Put up VAX 004 info. */
 {
 char *id;
 struct sqlConnection *conn = hAllocConn(database);
 char *aliTbl = tdb->table;
 int start = cartInt(cart, "o");
 char cond_str[255], *subjId;
 
 genericHeader(tdb, item);
 
 id = item;
 printf("<H3>Sequence ID: %s", id);
 printf("</H3>\n");
 
 /* display subject ID */
 sqlSafefFrag(cond_str, sizeof cond_str, "dnaSeqId='%s'", id);
 subjId = sqlGetField(database,"gsIdXref", "subjId", cond_str);
 printf("<H3>Subject ID: ");
 printf("<A HREF=\"../cgi-bin/gsidSubj?hgs_subj=%s\">", subjId);
 printf("%s</A>\n", subjId);
 printf("</H3>");
 
 /* print alignments that track was based on */
 struct psl *pslList = getAlignments(conn, aliTbl, item);
 printf("<H3>Genomic Alignments</H3>");
 printAlignments(pslList, start, "htcCdnaAli", tdb->table, item);
 hFreeConn(&conn);
 
 printTrackHtml(tdb);
 }
 
 void doUniGene3(struct trackDb *tdb, char *item)
 /* Put up UniGene info. */
 {
 char *url = tdb->url;
 char *id;
 struct sqlConnection *conn = hAllocConn(database);
 char *aliTbl = tdb->table;
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, item);
 
 id = strstr(item, "Hs.")+strlen("Hs.");
 printf("<H3>%s UniGene: ", organism);
 printf("<A HREF=\"%s%s\" target=_blank>", url, id);
 printf("%s</B></A>\n", item);
 printf("</H3>\n");
 
 /* print alignments that track was based on */
 struct psl *pslList = getAlignments(conn, aliTbl, item);
 printf("<H3>Genomic Alignments</H3>");
 printAlignments(pslList, start, "htcCdnaAli", tdb->table, item);
 hFreeConn(&conn);
 
 printTrackHtml(tdb);
 }
 
 void doRgdSslp(struct trackDb *tdb, char *item, char *itemForUrl)
 /* Put up Superfamily track info. */
 {
 if (itemForUrl == NULL)
     itemForUrl = item;
 
 genericHeader(tdb, item);
 printRgdSslpCustomUrl(tdb, itemForUrl, item == itemForUrl);
 printTrackHtml(tdb);
 }
 
 static boolean isBDGPName(char *name)
 /* Return TRUE if name is from BDGP (matching {CG,TE,CR}0123{,4}{,-R?})  */
 {
 int len = strlen(name);
 boolean isBDGP = FALSE;
 if (startsWith("CG", name) || startsWith("TE", name) || startsWith("CR", name))
     {
     int numNum = 0;
     int i;
     for (i=2;  i < len;  i++)
 	{
 	if (isdigit(name[i]))
 	    numNum++;
 	else
 	    break;
 	}
     if ((numNum >= 4) && (numNum <= 5))
 	{
 	if (i == len)
 	    isBDGP = TRUE;
 	else if ((i == len-3) &&
 		 (name[i] == '-') && (name[i+1] == 'R') && isalpha(name[i+2]))
 	    isBDGP = TRUE;
 	}
     }
 return(isBDGP);
 }
 
 void doRgdGene(struct trackDb *tdb, char *rnaName)
 /* Process click on a RGD gene. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *sqlRnaName = rnaName;
 struct refLink *rl;
 char *rgdId;
 int start = cartInt(cart, "o");
 
 /* Make sure to escape single quotes for DB parseability */
 if (strchr(rnaName, '\''))
     sqlRnaName = replaceChars(rnaName, "'", "''");
 
 cartWebStart(cart, database, "%s", tdb->longLabel);
 
 sqlSafef(query, sizeof(query), "select * from %s where mrnaAcc = '%s'", refLinkTable, sqlRnaName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find %s in %s table - this accession may no longer be available.", rnaName, refLinkTable);
 rl = refLinkLoad(row);
 sqlFreeResult(&sr);
 printf("<H2>Gene %s</H2>\n", rl->name);
 
 sqlSafef(query, sizeof(query), "select id from rgdGeneLink where refSeq = '%s'", sqlRnaName);
 
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find %s in rgdGeneLink table - database inconsistency.", rnaName);
 rgdId = cloneString(row[0]);
 sqlFreeResult(&sr);
 
 printf("<B>RGD Gene Report: </B> <A HREF=\"");
 printf("%s%s", tdb->url, rgdId);
 printf("\" TARGET=_blank>RGD:%s</A><BR>", rgdId);
 
 printf("<B>NCBI RefSeq: </B> <A HREF=\"");
 printEntrezNucleotideUrl(stdout, rl->mrnaAcc);
 printf("\" TARGET=_blank>%s</A>", rl->mrnaAcc);
 
 /* If refSeqStatus is available, report it: */
 if (sqlTableExists(conn, refSeqStatusTable))
     {
     sqlSafef(query, sizeof(query), "select status from %s where mrnaAcc = '%s'",
 	    refSeqStatusTable, sqlRnaName);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
 	printf("&nbsp;&nbsp; Status: <B>%s</B>", row[0]);
 	}
     sqlFreeResult(&sr);
     }
 puts("<BR>");
 
 if (rl->omimId != 0)
     {
     printf("<B>OMIM:</B> <A HREF=\"");
     printEntrezOMIMUrl(stdout, rl->omimId);
     printf("\" TARGET=_blank>%d</A><BR>\n", rl->omimId);
     }
 if (rl->locusLinkId != 0)
     {
     printf("<B>Entrez Gene:</B> ");
     printf("<A HREF=\"http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=Graphics&list_uids=%d\" TARGET=_blank>",
            rl->locusLinkId);
     printf("%d</A><BR>\n", rl->locusLinkId);
     }
 
 htmlHorizontalLine();
 
 /* print alignments that track was based on */
 {
 char *aliTbl = (sameString(tdb->table, "rgdGene") ? "refSeqAli" : "xenoRGDAli");
 struct psl *pslList = getAlignments(conn, aliTbl, rl->mrnaAcc);
 printf("<H3>mRNA/Genomic Alignments</H3>");
 printAlignments(pslList, start, "htcCdnaAli", aliTbl, rl->mrnaAcc);
 }
 
 htmlHorizontalLine();
 
 geneShowPosAndLinks(rl->mrnaAcc, rl->protAcc, tdb, refPepTable, "htcTranslatedProtein",
 		    "htcRefMrna", "htcGeneInGenome", "mRNA Sequence");
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doRgdGene2(struct trackDb *tdb, char *rgdGeneId)
 /* Process click on a RGD gene. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *sqlRnaName = rgdGeneId;
 char *rgdId = NULL;
 char *chp;
 char *GeneID, *Name, *note;
 char *rgdPathwayName;
 
 /* Make sure to escape single quotes for DB parseability */
 if (strchr(rgdGeneId, '\''))
     sqlRnaName = replaceChars(rgdGeneId, "'", "''");
 
 cartWebStart(cart, database, "%s", tdb->longLabel);
 
 chp = strstr(rgdGeneId, ":");
 if (chp != NULL)
     {
     chp++;
     rgdId = strdup(chp);
     }
 else
     {
     errAbort("Couldn't find %s.", rgdGeneId);
     }
 
 sqlSafef(query, sizeof(query), "select GeneID, Name, note from rgdGeneXref where rgdGeneId = '%s'", sqlRnaName);
 
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find %s in rgdGeneXref table - database inconsistency.", rgdGeneId);
 GeneID = cloneString(row[0]);
 Name   = cloneString(row[1]);
 note   = cloneString(row[2]);
 
 sqlFreeResult(&sr);
 
 printf("<H2>Gene %s</H2>\n", Name);
 printf("<B>RGD Gene Report: </B> <A HREF=\"");
 printf("%s%s", tdb->url, rgdId);
 printf("\" TARGET=_blank>RGD:%s</A>", rgdId);
 
 printf("<BR><B>GeneID: </B> %s ", GeneID);
 printf("<BR><B>Gene Name: </B> %s ", Name);
 printf("<BR><B>Note: </B> %s ", note);
 
 sqlSafef(query, sizeof(query), "select extAC from rgdGeneXref2 where rgdGeneId = '%s' and extDB='IMAGE'", rgdGeneId);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     char *image;
     image = cloneString(row[0]);
     printf("<BR><B>IMAGE Clone: </B>");
     printf("<A HREF=\"");
     printf("%s%s", "http://www.imageconsortium.org/IQ/bin/singleCloneQuery?clone_id=", image);
     printf("\" TARGET=_blank> %s</A>", image);
     row = sqlNextRow(sr);
     while (row != NULL)
 	{
 	image = cloneString(row[0]);
 	printf(", <A HREF=\"");
 	printf("%s%s", "http://www.imageconsortium.org/IQ/bin/singleCloneQuery?clone_id=", image);
 	printf("\" TARGET=_blank>%s</A>", image);
         row = sqlNextRow(sr);
 	}
     }
 sqlFreeResult(&sr);
 
 sqlSafef(query, sizeof(query), "select extAC from rgdGeneXref2 where rgdGeneId = '%s' and extDB='MGC'", rgdGeneId);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     char *mgc;
     mgc = cloneString(row[0]);
     printf("<BR><B>MGC: </B>");
     printf("<A HREF=\"");
     printf("%s%s", "http://mgc.nci.nih.gov/Genes/CloneList?ORG=Rn&LIST=", mgc);
     printf("\" TARGET=_blank> %s</A>", mgc);
     row = sqlNextRow(sr);
     while (row != NULL)
 	{
 	mgc = cloneString(row[0]);
 	printf(", <A HREF=\"");
 	printf("%s%s", "http://mgc.nci.nih.gov/Genes/CloneList?ORG=Rn&LIST=", mgc);
 	printf("\" TARGET=_blank>%s</A>", mgc);
         row = sqlNextRow(sr);
 	}
     }
 sqlFreeResult(&sr);
 
 htmlHorizontalLine();
 printf("<H3>RGD Pathway(s)</H3>\n");
 sqlSafef(query, sizeof(query),
 "select p.rgdPathwayId, p.name from rgdGenePathway g, rgdPathway p where g.rgdGeneId = '%s' and g.rgdPathwayId=p.rgdPathwayId", rgdGeneId);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find %s in rgdGenePathway table - database inconsistency.", rgdGeneId);
 printf("<UL>");
 while (row != NULL)
     {
     rgdPathwayName = cloneString(row[1]);
     printf("<LI><B>%s</B><BR>", rgdPathwayName);
     row = sqlNextRow(sr);
     }
 sqlFreeResult(&sr);
 printf("</UL>");
 printf("<A HREF=\"");
 printf("%s%s%s", "http://rgd.mcw.edu/tools/genes/gene_ont_view.cgi?id=", rgdId, "#Pathway");
 printf("\" TARGET=_blank> %s</A> </H3>", "Click here for more RGD pathway details related to this gene...");
 
 htmlHorizontalLine();
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 char *getRefSeqCdsCompleteness(struct sqlConnection *conn, char *acc)
 /* get description of RefSeq CDS completeness or NULL if not available */
 {
 /* table mapping names to descriptions */
 static char *cmplMap[][2] =
     {
     {"Unknown", "completeness unknown"},
     {"Complete5End", "5' complete"},
     {"Complete3End", "3' complete"},
     {"FullLength", "full length"},
     {"IncompleteBothEnds", "5' and 3' incomplete"},
     {"Incomplete5End", "5' incomplete"},
     {"Incomplete3End", "3' incomplete"},
     {"Partial", "partial"},
     {NULL, NULL}
     };
 if (sqlTableExists(conn, refSeqSummaryTable))
     {
     char query[256], buf[64], *cmpl;
     int i;
     sqlSafef(query, sizeof(query),
           "select completeness from %s where mrnaAcc = '%s'",
           refSeqSummaryTable, acc);
     cmpl = sqlQuickQuery(conn, query, buf, sizeof(buf));
     if (cmpl != NULL)
         {
         for (i = 0; cmplMap[i][0] != NULL; i++)
             {
             if (sameString(cmpl, cmplMap[i][0]))
                 return cmplMap[i][1];
             }
         }
     }
 return NULL;
 }
 
 char *getRefSeqSummary(struct sqlConnection *conn, char *acc)
 /* RefSeq summary or NULL if not available; free result */
 {
 char * summary = NULL;
 if (sqlTableExists(conn, refSeqSummaryTable))
     {
     char query[256];
     sqlSafef(query, sizeof(query),
           "select summary from %s where mrnaAcc = '%s'", refSeqSummaryTable, acc);
     summary = sqlQuickString(conn, query);
     }
 return summary;
 }
 
 char *geneExtraImage(char *geneFileBase)
 /* check if there is a geneExtra image for the specified gene, if so return
  * the relative URL in a static buffer, or NULL if it doesn't exist */
 {
 static char *imgExt[] = {"png", "gif", "jpg", NULL};
 static char path[256];
 int i;
 
 for (i = 0; imgExt[i] != NULL; i++)
     {
     safef(path, sizeof(path), "../htdocs/geneExtra/%s.%s", geneFileBase, imgExt[i]);
     if (access(path, R_OK) == 0)
         {
         safef(path, sizeof(path), "../geneExtra/%s.%s", geneFileBase, imgExt[i]);
         return path;
         }
     }
 return NULL;
 }
 
 void addGeneExtra(char *geneName)
 /* create html table columns with geneExtra data, see hgdocs/geneExtra/README
  * for details */
 {
 char geneFileBase[256], *imgPath, textPath[256];
 
 /* lower-case gene name used as key */
 safef(geneFileBase, sizeof(geneFileBase), "%s", geneName);
 tolowers(geneFileBase);
 
 /* add image column, if exists */
 imgPath = geneExtraImage(geneFileBase);
 
 if (imgPath != NULL)
     printf("<td><img src=\"%s\">", imgPath);
 
 /* add text column, if exists */
 safef(textPath, sizeof(textPath), "../htdocs/geneExtra/%s.txt", geneFileBase);
 if (access(textPath, R_OK) == 0)
     {
     FILE *fh = mustOpen(textPath, "r");
     printf("<td valign=\"center\">");
     copyOpenFile(fh, stdout);
     fclose(fh);
     }
 }
 
 int gbCdnaGetVersion(struct sqlConnection *conn, char *acc)
 /* return mrna/est version, or 0 if not available */
 
 {
 int ver = 0;
 if (!sqlTableExists(conn, gbCdnaInfoTable))
     {
     warn("Genbank information not shown below, the table %s is not installed "
         "on this server. ", gbCdnaInfoTable);
     //"The information below is a shortened version of the one shown on the "
     //"<a href=\"http://genome.ucsc.edu\">UCSC site</a>", database);
     return 0;
     }
 
 if (hHasField(database, gbCdnaInfoTable, "version"))
     {
     char query[128];
     sqlSafef(query, sizeof(query),
           "select version from %s where acc = '%s'", gbCdnaInfoTable, acc);
     ver = sqlQuickNum(conn, query);
     }
 return ver;
 }
 
 static void prRefGeneXenoInfo(struct sqlConnection *conn, struct refLink *rl)
 /* print xeno refseq info, including linking to the browser, if any  */
 {
 char query[256];
 sqlSafef(query, sizeof(query), "select o.name from %s g,%s o "
       "where (g.acc = '%s') and (o.id = g.organism)",
       gbCdnaInfoTable, organismTable, rl->mrnaAcc);
 char *org = sqlQuickString(conn, query);
 if (org == NULL)
     org = cloneString("unknown");
 printf("<B>Organism:</B> %s<BR>", org);
 char *xenoDb = hDbForSciName(org);
 if ((xenoDb != NULL) && hDbIsActive(xenoDb) && hTableExists(xenoDb, "refSeqAli"))
     {
     printf("<B>UCSC browser: </B> \n");
     linkToOtherBrowserSearch(xenoDb, rl->mrnaAcc);
     printf("%s on %s (%s)</B> \n", rl->mrnaAcc, hOrganism(xenoDb), xenoDb);
     printf("</A><BR>");
     }
 freeMem(org);
 }
 
 void prRefGeneInfo(struct sqlConnection *conn, char *rnaName,
                    char *sqlRnaName, struct refLink *rl, boolean isXeno)
 /* print basic details information and links for a RefGene */
 {
 struct sqlResult *sr;
 char **row;
 char query[256];
 int ver = gbCdnaGetVersion(conn, rl->mrnaAcc);
 char *cdsCmpl = NULL;
 
 printf("<td valign=top nowrap>\n");
 if (isXeno)
     {
     if (startsWith("panTro", database))
         printf("<H2>Other RefSeq Gene %s</H2>\n", rl->name);
     else
         printf("<H2>Non-%s RefSeq Gene %s</H2>\n", organism, rl->name);
     }
 else
     printf("<H2>RefSeq Gene %s</H2>\n", rl->name);
 printf("<B>RefSeq:</B> <A HREF=\"");
 printEntrezNucleotideUrl(stdout, rl->mrnaAcc);
 if (ver > 0)
     printf("\" TARGET=_blank>%s.%d</A>", rl->mrnaAcc, ver);
 else
     printf("\" TARGET=_blank>%s</A>", rl->mrnaAcc);
 
 /* If refSeqStatus is available, report it: */
 if (sqlTableExists(conn, refSeqStatusTable))
     {
     sqlSafef(query, sizeof(query), "select status from %s where mrnaAcc = '%s'",
           refSeqStatusTable, sqlRnaName);
     char *stat = sqlQuickString(conn, query);
     if (stat != NULL)
 	printf("&nbsp;&nbsp; <B>Status: </B>%s", stat);
     }
 puts("<BR>");
 char *desc = gbCdnaGetDescription(conn, rl->mrnaAcc);
 if (desc != NULL)
     {
     printf("<B>Description:</B> ");
     htmlTextOut(desc);
     printf("<BR>\n");
     }
 
 if (isXeno)
     prRefGeneXenoInfo(conn, rl);
 else
     printCcdsForSrcDb(conn, rl->mrnaAcc);
 
 cdsCmpl = getRefSeqCdsCompleteness(conn, sqlRnaName);
 if (cdsCmpl != NULL)
     {
     printf("<B>CDS:</B> %s<BR>", cdsCmpl);
     }
 if (rl->omimId != 0)
     {
     printf("<B>OMIM:</B> <A HREF=\"");
     printEntrezOMIMUrl(stdout, rl->omimId);
     printf("\" TARGET=_blank>%d</A><BR>\n", rl->omimId);
     }
 if (rl->locusLinkId != 0)
     {
     printf("<B>Entrez Gene:</B> ");
     printf("<A HREF=\"http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=Graphics&list_uids=%d\" TARGET=_blank>",
            rl->locusLinkId);
     printf("%d</A><BR>\n", rl->locusLinkId);
 
     if ( (strstr(database, "mm") != NULL) && hTableExists(database, "MGIid"))
         {
         char *mgiID;
 	sqlSafef(query, sizeof(query), "select MGIid from MGIid where LLid = '%d';",
 		rl->locusLinkId);
 
 	sr = sqlGetResult(conn, query);
 	if ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    printf("<B>Mouse Genome Informatics:</B> ");
 	    mgiID = cloneString(row[0]);
 
 	    printf("<A HREF=\"http://www.informatics.jax.org/searches/accession_report.cgi?id=%s\" TARGET=_BLANK>%s</A><BR>\n",mgiID, mgiID);
 	    }
 	else
 	    {
 	    /* per Carol Bult from Jackson Lab 4/12/02, JAX do not always agree
 	     * with Locuslink on seq to gene association.
 	     * Thus, not finding a MGIid even if a LocusLink ID
 	     * exists is always a possibility. */
 	    }
 	sqlFreeResult(&sr);
 	}
     }
 if (!startsWith("Worm", organism))
     {
     if (startsWith("dm", database))
 	{
         /* PubMed never seems to have BDGP gene IDs... so if that's all
          * that's given for a name/product, ignore name / truncate product. */
 	char *cgp = strstr(rl->product, "CG");
 	if (cgp != NULL)
 	    {
 	    char *cgWord = firstWordInLine(cloneString(cgp));
 	    char *dashp = strchr(cgWord, '-');
 	    if (dashp != NULL)
 		*dashp = 0;
 	    if (isBDGPName(cgWord))
 		*cgp = 0;
 	    }
 	if (! isBDGPName(rl->name))
 	    medlineLinkedLine("PubMed on Gene", rl->name, rl->name);
 	if (rl->product[0] != 0)
 	    medlineProductLinkedLine("PubMed on Product", rl->product);
 	}
     else
 	{
 	medlineLinkedLine("PubMed on Gene", rl->name, rl->name);
 	if (rl->product[0] != 0)
 	    medlineProductLinkedLine("PubMed on Product", rl->product);
 	}
     printf("\n");
     printGeneCards(rl->name);
     }
 if (hTableExists(database, "jaxOrtholog"))
     {
     struct jaxOrtholog jo;
     char * sqlRlName = rl->name;
 
     /* Make sure to escape single quotes for DB parseability */
     if (strchr(rl->name, '\''))
         {
         sqlRlName = replaceChars(rl->name, "'", "''");
         }
     sqlSafef(query, sizeof(query), "select * from jaxOrtholog where humanSymbol='%s'", sqlRlName);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
 	jaxOrthologStaticLoad(row, &jo);
 	printf("<B>MGI Mouse Ortholog:</B> ");
 	printf("<A HREF=\"http://www.informatics.jax.org/searches/accession_report.cgi?id=%s\" target=_BLANK>", jo.mgiId);
 	printf("%s</A><BR>\n", jo.mouseSymbol);
 	}
     sqlFreeResult(&sr);
     }
 if (startsWith("hg", database))
     {
     printf("\n");
     printf("<B>AceView:</B> ");
     printf("<A HREF = \"http://www.ncbi.nlm.nih.gov/IEB/Research/Acembly/av.cgi?db=human&l=%s\" TARGET=_blank>",
 	   rl->name);
     printf("%s</A><BR>\n", rl->name);
     }
 prGRShortRefGene(rl->name);
 
 }
 
 void prKnownGeneInfo(struct sqlConnection *conn, char *rnaName,
                    char *sqlRnaName, struct refLink *rl)
 /* print basic details information and links for a Known Gene */
 {
 struct sqlResult *sr;
 char **row;
 char query[256];
 int ver = gbCdnaGetVersion(conn, rl->mrnaAcc);
 char *cdsCmpl = NULL;
 
 printf("<td valign=top nowrap>\n");
 
 printf("<H2>Known Gene %s</H2>\n", rl->name);
 printf("<B>KnownGene:</B> <A HREF=\"");
 printEntrezNucleotideUrl(stdout, rl->mrnaAcc);
 if (ver > 0)
     printf("\" TARGET=_blank>%s.%d</A>", rl->mrnaAcc, ver);
 else
     printf("\" TARGET=_blank>%s</A>", rl->mrnaAcc);
 fflush(stdout);
 
 puts("<BR>");
 char *desc = gbCdnaGetDescription(conn, rl->mrnaAcc);
 if (desc != NULL)
     {
     printf("<B>Description:</B> ");
     htmlTextOut(desc);
     printf("<BR>\n");
     }
 
 printCcdsForSrcDb(conn, rl->mrnaAcc);
 
 cdsCmpl = getRefSeqCdsCompleteness(conn, sqlRnaName);
 if (cdsCmpl != NULL)
     {
     printf("<B>CDS:</B> %s<BR>", cdsCmpl);
     }
 if (rl->omimId != 0)
     {
     printf("<B>OMIM:</B> <A HREF=\"");
     printEntrezOMIMUrl(stdout, rl->omimId);
     printf("\" TARGET=_blank>%d</A><BR>\n", rl->omimId);
     }
 if (rl->locusLinkId != 0)
     {
     printf("<B>Entrez Gene:</B> ");
     printf("<A HREF=\"http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=Graphics&list_uids=%d\" TARGET=_blank>",
            rl->locusLinkId);
     printf("%d</A><BR>\n", rl->locusLinkId);
 
     if ( (strstr(database, "mm") != NULL) && hTableExists(database, "MGIid"))
         {
         char *mgiID;
 	sqlSafef(query, sizeof(query), "select MGIid from MGIid where LLid = '%d';",
 		rl->locusLinkId);
 
 	sr = sqlGetResult(conn, query);
 	if ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    printf("<B>Mouse Genome Informatics:</B> ");
 	    mgiID = cloneString(row[0]);
 
 	    printf("<A HREF=\"http://www.informatics.jax.org/searches/accession_report.cgi?id=%s\" TARGET=_BLANK>%s</A><BR>\n",mgiID, mgiID);
 	    }
 	else
 	    {
 	    /* per Carol Bult from Jackson Lab 4/12/02, JAX do not always agree
 	     * with Locuslink on seq to gene association.
 	     * Thus, not finding a MGIid even if a LocusLink ID
 	     * exists is always a possibility. */
 	    }
 	sqlFreeResult(&sr);
 	}
     }
 }
 
 void doKnownGene(struct trackDb *tdb, char *rnaName)
 /* Process click on a known gene. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *kgId = cartString(cart, "i");
 char *sqlRnaName = rnaName;
 char *summary = NULL;
 struct refLink rlR;
 struct refLink *rl;
 int start = cartInt(cart, "o");
 int left = cartInt(cart, "l");
 int right = cartInt(cart, "r");
 char *chrom = cartString(cart, "c");
 /* Make sure to escape single quotes for DB parseability */
 if (strchr(rnaName, '\''))
     {
     sqlRnaName = replaceChars(rnaName, "'", "''");
     }
 /* get refLink entry */
 if (strstr(rnaName, "NM_") != NULL)
     {
     sqlSafef(query, sizeof(query), "select * from %s where mrnaAcc = '%s'", refLinkTable, sqlRnaName);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) == NULL)
         errAbort("Couldn't find %s in %s table - this accession may no longer be available.",
                  rnaName, refLinkTable);
     rl = refLinkLoad(row);
     sqlFreeResult(&sr);
     }
 else
     {
     rlR.name    = strdup(kgId);
     rlR.mrnaAcc = strdup(kgId);
     rlR.locusLinkId = 0;
     rl = &rlR;
     }
 
 cartWebStart(cart, database, "Known Gene");
 printf("<table border=0>\n<tr>\n");
 prKnownGeneInfo(conn, rnaName, sqlRnaName, rl);
 
 printf("</tr>\n</table>\n");
 
 /* optional summary text */
 summary = getRefSeqSummary(conn, kgId);
 if (summary != NULL)
     {
     htmlHorizontalLine();
     printf("<H3>Summary of %s</H3>\n", kgId);
     printf("<P>%s</P>\n", summary);
     freeMem(summary);
     }
 htmlHorizontalLine();
 
 /* print alignments that track was based on */
 {
 char *aliTbl;
 
 if (strstr(kgId, "NM_"))
     {
     aliTbl = strdup("refSeqAli");
     }
 else
     {
     aliTbl = strdup("all_mrna");
     }
 struct psl *pslList = getAlignments(conn, aliTbl, rl->mrnaAcc);
 printf("<H3>mRNA/Genomic Alignments</H3>");
 printAlignments(pslList, start, "htcCdnaAli", aliTbl, kgId);
 }
 htmlHorizontalLine();
 
 struct palInfo *palInfo = NULL;
 
 if (genbankIsRefSeqCodingMRnaAcc(rnaName))
     {
     AllocVar(palInfo);
     palInfo->chrom = chrom;
     palInfo->left = left;
     palInfo->right = right;
     palInfo->rnaName = rnaName;
     }
 
 geneShowPosAndLinksPal(rl->mrnaAcc, rl->protAcc, tdb, refPepTable, "htcTranslatedProtein",
 		    "htcRefMrna", "htcGeneInGenome", "mRNA Sequence",palInfo);
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 static struct refLink *printRefSeqInfo( struct sqlConnection *conn, struct trackDb *tdb, char *rnaName, char *version)
 {
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *sqlRnaName = rnaName;
 char *summary = NULL;
 boolean isXeno = sameString(tdb->table, "xenoRefGene");
 struct refLink *rl;
 
 /* Make sure to escape single quotes for DB parseability */
 if (strchr(rnaName, '\''))
     {
     sqlRnaName = replaceChars(rnaName, "'", "''");
     }
 /* get refLink entry */
 if (version == NULL)
     {
     sqlSafef(query, sizeof(query), "select * from %s  where mrnaAcc = '%s'", refLinkTable, sqlRnaName);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) == NULL)
 	errAbort("Couldn't find %s in %s table - this accession may no longer be available.", rnaName, refLinkTable);
     rl = refLinkLoad(row);
     sqlFreeResult(&sr);
     }
 else
     {
     sqlSafef(query, sizeof(query), "select * from %s r, %s g where mrnaAcc = '%s' and r.mrnaAcc=g.acc and g.version='%s'", refLinkTable,gbCdnaInfoTable, sqlRnaName, version);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) == NULL)
 	{
 	sqlFreeResult(&sr);
 	return NULL;
 	}
     rl = refLinkLoad(row);
     sqlFreeResult(&sr);
     }
 
 /* print the first section with info  */
 printf("<table border=0>\n<tr>\n");
 prRefGeneInfo(conn, rnaName, sqlRnaName, rl, isXeno);
 addGeneExtra(rl->name);  /* adds columns if extra info is available */
 
 printf("</tr>\n</table>\n");
 
 /* optional summary text */
 summary = getRefSeqSummary(conn, sqlRnaName);
 if (summary != NULL)
     {
     htmlHorizontalLine();
     printf("<H3>Summary of %s</H3>\n", rl->name);
     printf("<P>%s</P>\n", summary);
     freeMem(summary);
     }
 htmlHorizontalLine();
 
 return rl;
 }
 
 void doNcbiRefSeq(struct trackDb *tdb, char *itemName)
 /* Process click on a NCBI RefSeq gene. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 struct ncbiRefSeqLink *nrl;
 
 struct dyString *dy = newDyString(1024);
 dyStringPrintf(dy, "%s - %s ", tdb->longLabel, itemName);
 
 struct trackVersion *trackVersion = getTrackVersion(database, "ncbiRefSeq");
 if ((trackVersion != NULL) && !isEmpty(trackVersion->version))
     dyStringPrintf(dy, "- Release %s\n", trackVersion->version);
 
 cartWebStart(cart, database, "%s", dy->string);
 
 /* get refLink entry */
 sqlSafef(query, sizeof(query), "select * from ncbiRefSeqLink where id = '%s'", itemName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find %s in ncbiRefSeqLink table.", itemName);
 nrl = ncbiRefSeqLinkLoad(row);
 sqlFreeResult(&sr);
 
 printf("<h2>RefSeq Gene %s</h2><br>\n", nrl->name);
 printf("<b>RefSeq:</b> <a href='");
 printEntrezNucleotideUrl(stdout, nrl->id);
 printf("' target=_blank>%s</a>", nrl->id);
 printf("&nbsp;&nbsp;<b>Status: </b>%s<br>\n", nrl->status);
 printf("<b>Description:</b> %s<br>\n", nrl->product);
 if (differentWord(nrl->gbkey, ""))
     {
     printf("<b>Molecule type:</b> %s<br>\n", nrl->gbkey);
     }
 if (differentWord(nrl->pseudo, ""))
     {
     printf("<b>Pseudogene:</b> %s<br>\n", nrl->pseudo);
     }
 if (differentWord(nrl->source, ""))
     {
     printf("<b>Source:</b> %s<br>\n", nrl->source);
     }
 if (differentWord(nrl->gene_biotype, ""))
     {
     printf("<b>Biotype:</b> %s<br>\n", nrl->gene_biotype);
     }
 if (differentWord(nrl->gene_synonym, ""))
     {
     printf("<b>Synonyms:</b> %s<br>\n", nrl->gene_synonym);
     }
 if (differentWord(nrl->ncrna_class, ""))
     {
     printf("<b>ncRNA class:</b> %s<br>\n", nrl->ncrna_class);
     }
 if (differentWord(nrl->note, ""))
     {
     printf("<b>Other notes:</b> %s<br>\n", nrl->note);
     }
 if (differentWord(nrl->omimId, ""))
     {
     printf("<b>OMIM:</b> <a href='");
     printEntrezOMIMUrl(stdout, sqlSigned(nrl->omimId));
     printf("' target=_blank>%s</a><br>\n", nrl->omimId);
     }
 if (differentWord(nrl->mrnaAcc, "") && differentWord(nrl->mrnaAcc,nrl->id))
     {
     printf("<b>mRNA:</b> ");
     printf("<a href='http://www.ncbi.nlm.nih.gov/nuccore/%s' target=_blank>", nrl->mrnaAcc);
     printf("%s</a><br>\n", nrl->mrnaAcc);
     }
 if (differentWord(nrl->genbank, "") && differentWord(nrl->genbank,nrl->id))
     {
     printf("<b>Genbank:</b> ");
     printf("<a href='http://www.ncbi.nlm.nih.gov/nuccore/%s' target=_blank>", nrl->genbank);
     printf("%s</a><br>\n", nrl->genbank);
     }
 if (differentWord(nrl->protAcc, ""))
     {
     printf("<b>Protein:</b> ");
     printf("<a href='http://www.ncbi.nlm.nih.gov/protein/%s' target=_blank>", nrl->protAcc);
     printf("%s</a><br>\n", nrl->protAcc);
     }
 if (differentWord(nrl->hgnc, ""))
     {
     printf("<b>HGNC:</b> ");
     printf("<a href='http://www.genenames.org/cgi-bin/gene_symbol_report?hgnc_id=HGNC:%s' target=_blank>", nrl->hgnc);
     printf("%s</a><br>\n", nrl->hgnc);
     }
 
 if (differentWord(nrl->locusLinkId, ""))
     {
     printf("<b>Entrez Gene:</b> ");
     printf("<a href='http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=Graphics&list_uids=%s' TARGET=_blank>",
            nrl->locusLinkId);
     printf("%s</a><br>\n", nrl->locusLinkId);
     }
 
 if (differentWord(nrl->name,""))
     {
     printGeneCards(nrl->name);
     if (startsWith("hg", database))
         {
         printf("<b>AceView:</b> ");
         printf("<a href = 'http://www.ncbi.nlm.nih.gov/IEB/Research/Acembly/av.cgi?db=human&l=%s' target=_blank>",
 	   nrl->name);
         printf("%s</a><br>\n", nrl->name);
         }
     }
 if ((trackVersion != NULL) && !isEmpty(trackVersion->version))
     {
     printf("<B>Annotation Release:</B> <A href='%s' TARGET=_blank> %s <BR></A>", trackVersion->comment, trackVersion->version);
     }
 
 htmlHorizontalLine();
 if (differentWord("", nrl->description))
     {
     printf("Summary of <b>%s</b><br>\n%s<br>\n", nrl->name, nrl->description);
     htmlHorizontalLine();
     }
 
 static boolean hasSequence = TRUE;
 struct psl *pslList = getAlignments(conn, "ncbiRefSeqPsl", itemName);
 // if the itemName isn't found, it might be found as the nrl->mrnaAcc
 if (! pslList)
     pslList = getAlignments(conn, "ncbiRefSeqPsl", nrl->mrnaAcc);
 if (pslList)
     {
     char query[256];
     /* verify itemName has RNA sequence to work with */
     sqlSafef(query, sizeof(query), "select id from seqNcbiRefSeq where acc='%s' limit 1", itemName);
     char * result= sqlQuickString(conn, query);
     if (isEmpty(result))
         {
         printf ("<h4>there is NO alignment for %s</h4>\n", itemName);
         hasSequence = FALSE;
         }
     else
         {
         printf("<H3>mRNA/Genomic Alignments (%s)</H3>", itemName);
         int start = cartInt(cart, "o");
         printAlignments(pslList, start, "htcCdnaAli", "ncbiRefSeqPsl", itemName);
         }
     }
 else
     {
     printf ("<h4>there is NO alignment for %s</h4><br>\n", itemName);
     }
 
 htmlHorizontalLine();
 
 if (! ( sameString(tdb->track, "ncbiRefSeqPsl") || sameString(tdb->track, "ncbiRefSeqOther" ) ) )
     showGenePos(itemName, tdb);
 
 printf("<h3>Links to sequence:</h3>\n");
 printf("<ul>\n");
 if (differentWord("", nrl->protAcc))
     {
     puts("<li>\n");
     hgcAnchorSomewhere("htcTranslatedProtein", nrl->protAcc, "ncbiRefSeqPepTable", seqName);
     printf("Predicted Protein</a> \n");
     puts("</li>\n");
     }
 if (hasSequence)
     {
     puts("<li>\n");
     hgcAnchorSomewhere("ncbiRefSeqSequence", itemName, "ncbiRefSeqPsl", seqName);
     printf("%s</a> may be different from the genomic sequence.\n",
 	   "Predicted mRNA");
     puts("</li>\n");
     }
 puts("<LI>\n");
 hgcAnchorSomewhere("getDna", itemName, tdb->track, seqName);
 printf("Genomic Sequence</A> from assembly\n");
 puts("</LI>\n");
 
 printf("</ul>\n");
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doRefGene(struct trackDb *tdb, char *rnaName)
 /* Process click on a known RefSeq gene. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 int start = cartInt(cart, "o");
 int left = cartInt(cart, "l");
 int right = cartInt(cart, "r");
 char *chrom = cartString(cart, "c");
 
 boolean isXeno = sameString(tdb->table, "xenoRefGene");
 if (isXeno)
     cartWebStart(cart, database, "Non-%s RefSeq Gene", organism);
 else
     cartWebStart(cart, database, "RefSeq Gene");
 struct refLink *rl = printRefSeqInfo( conn, tdb, rnaName, NULL);
 
 /* print alignments that track was based on */
 char *aliTbl = (sameString(tdb->table, "refGene") ? "refSeqAli" : "xenoRefSeqAli");
 if (hTableExists(database, aliTbl))
     {
     struct psl *pslList = getAlignments(conn, aliTbl, rl->mrnaAcc);
     printf("<H3>mRNA/Genomic Alignments</H3>");
     printAlignments(pslList, start, "htcCdnaAli", aliTbl, rl->mrnaAcc);
     }
 else
     warn("Sequence alignment links not shown below, the table %s.refSeqAli is not installed " 
             "on this server", database);
 
 htmlHorizontalLine();
 
 struct palInfo *palInfo = NULL;
 
 if (genbankIsRefSeqCodingMRnaAcc(rnaName))
     {
     AllocVar(palInfo);
     palInfo->chrom = chrom;
     palInfo->left = left;
     palInfo->right = right;
     palInfo->rnaName = rnaName;
     }
 
 geneShowPosAndLinksPal(rl->mrnaAcc, rl->protAcc, tdb, refPepTable, "htcTranslatedProtein",
 		    "htcRefMrna", "htcGeneInGenome", "mRNA Sequence",palInfo);
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 char *kgIdToSpId(struct sqlConnection *conn, char* kgId)
 /* get the swissprot id for a known genes id; resulting string should be
  * freed */
 {
 char query[64];
 sqlSafef(query, sizeof(query), "select spID from kgXref where kgID='%s'", kgId);
 return sqlNeedQuickString(conn, query);
 }
 
 void doHInvGenes(struct trackDb *tdb, char *item)
 /* Process click on H-Invitational genes track. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 int start = cartInt(cart, "o");
 struct psl *pslList = NULL;
 struct HInv *hinv;
 
 /* Print non-sequence info. */
 genericHeader(tdb, item);
 
 sqlSafef(query, sizeof(query), "select * from HInv where geneId = '%s'", item);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     hinv = HInvLoad(row);
     if (hinv != NULL)
 	{
         printf("<B> Gene ID: </B> <A HREF=\"http://www.jbirc.jbic.or.jp/hinv/soup/pub_Detail.pl?acc_id=%s\" TARGET=_blank> %s <BR></A>",
                 hinv->mrnaAcc, hinv->geneId );
         printf("<B> Cluster ID: </B> <A HREF=\"http://www.jbirc.jbic.or.jp/hinv/soup/pub_Locus.pl?locus_id=%s\" TARGET=_blank> %s <BR></A>",
                 hinv->clusterId, hinv->clusterId );
         printf("<B> cDNA Accession: </B> <A HREF=\"http://getentry.ddbj.nig.ac.jp/cgi-bin/get_entry.pl?%s\" TARGET=_blank> %s <BR></A>",
                 hinv->mrnaAcc, hinv->mrnaAcc );
         }
     }
 htmlHorizontalLine();
 
 /* print alignments that track was based on */
 pslList = getAlignments(conn, "HInvGeneMrna", item);
 puts("<H3>mRNA/Genomic Alignments</H3>");
 printAlignments(pslList, start, "htcCdnaAli", "HInvGeneMrna", item);
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 char *getGi(char *ncbiFaHead)
 /* Get GI number from NCBI FA format header. */
 {
 char *s;
 static char gi[64];
 
 if (!startsWith("gi|", ncbiFaHead))
     return NULL;
 ncbiFaHead += 3;
 strncpy(gi, ncbiFaHead, sizeof(gi));
 s = strchr(gi, '|');
 if (s != NULL)
     *s = 0;
 return trimSpaces(gi);
 }
 
 void showSAM_T02(char *itemName)
 {
 char query2[256];
 struct sqlResult *sr2;
 char **row2;
 struct sqlConnection *conn2 = hAllocConn(database);
 char cond_str[256];
 char *predFN;
 char *homologID;
 char *SCOPdomain;
 char *chain;
 char goodSCOPdomain[40];
 int  first = 1;
 float  eValue;
 char *chp;
 int homologCount;
 int gotPDBFile;
 
 printf("<B>Protein Structure Analysis and Prediction by ");
 printf("<A HREF=\"http://www.soe.ucsc.edu/research/compbio/SAM_T02/sam-t02-faq.html\"");
 printf(" TARGET=_blank>SAM-T02</A></B><BR><BR>\n");
 
 printf("<B>Multiple Alignment:</B> ");
 /* printf("<A HREF=\"http://www.soe.ucsc.edu/~karplus/SARS/%s/summary.html#alignment",  */
 printf("<A HREF=\"../SARS/%s/summary.html#alignment",
        itemName);
 printf("\" TARGET=_blank>%s</A><BR>\n", itemName);
 
 printf("<B>Secondary Structure Predictions:</B> ");
 /* printf("<A HREF=\"http://www.soe.ucsc.edu/~karplus/SARS/%s/summary.html#secondary-structure",  */
 printf("<A HREF=\"../SARS/%s/summary.html#secondary-structure",
        itemName);
 printf("\" TARGET=_blank>%s</A><BR>\n", itemName);
 
 printf("<B>3D Structure Prediction (PDB file):</B> ");
 gotPDBFile = 0;
 sqlSafefFrag(cond_str, sizeof(cond_str), "proteinID='%s' and evalue <1.0e-5;", itemName);
 if (sqlGetField(database, "protHomolog", "proteinID", cond_str) != NULL)
     {
     sqlSafefFrag(cond_str, sizeof(cond_str), "proteinID='%s'", itemName);
     predFN = sqlGetField(database, "protPredFile", "predFileName", cond_str);
     if (predFN != NULL)
 	{
 	printf("<A HREF=\"../SARS/%s/", itemName);
 	/* printf("%s.t2k.undertaker-align.pdb\">%s</A><BR>\n", itemName,itemName); */
 	printf("%s\">%s</A><BR>\n", predFN,itemName);
 	gotPDBFile = 1;
 	}
     }
 if (!gotPDBFile)
     {
     printf("No high confidence level structure prediction available for this sequence.");
     printf("<BR>\n");
     }
 printf("<B>3D Structure of Close Homologs:</B> ");
 homologCount = 0;
 strcpy(goodSCOPdomain, "dummy");
 
 conn2= hAllocConn(database);
 sqlSafef(query2, sizeof(query2),
 	"select homologID,eValue,SCOPdomain,chain from sc1.protHomolog where proteinID='%s' and evalue <= 0.01;",
 	itemName);
 sr2 = sqlMustGetResult(conn2, query2);
 row2 = sqlNextRow(sr2);
 if (row2 != NULL)
     {
     while (row2 != NULL)
 	{
 	homologID = row2[0];
 	sscanf(row2[1], "%e", &eValue);
 	SCOPdomain = row2[2];
 	chp = SCOPdomain+strlen(SCOPdomain)-1;
 	while (*chp != '.') chp--;
 	*chp = '\0';
 	chain = row2[3];
         if (eValue <= 1.0e-10)
 	    strcpy(goodSCOPdomain, SCOPdomain);
 	else
 	    {
 	    if (strcmp(goodSCOPdomain,SCOPdomain) != 0)
 		goto skip;
 	    else
 		if (eValue > 0.1) goto skip;
 	    }
 	if (first)
             first = 0;
         else
             printf(", ");
 
         printf("<A HREF=\"http://www.rcsb.org/pdb/cgi/explore.cgi?job=graphics&pdbId=%s",
                homologID);
         if (strlen(chain) >= 1)
 	    printf("\"TARGET=_blank>%s(chain %s)</A>", homologID, chain);
 	else
 	    printf("\"TARGET=_blank>%s</A>", homologID);
 	homologCount++;
 
 	skip:
 	row2 = sqlNextRow(sr2);
 	}
     }
 hFreeConn(&conn2);
 sqlFreeResult(&sr2);
 if (homologCount == 0)
     printf("None<BR>\n");
 
 printf("<BR><B>Details:</B> ");
 printf("<A HREF=\"../SARS/%s/summary.html", itemName);
 printf("\" TARGET=_blank>%s</A><BR>\n", itemName);
 
 htmlHorizontalLine();
 }
 
 void showHomologies(char *geneName, char *table)
 /* Show homology info. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 boolean isFirst = TRUE, gotAny = FALSE;
 char *gi;
 struct softberryHom hom;
 
 if (sqlTableExists(conn, table))
     {
     sqlSafef(query, sizeof(query), "select * from %s where name = '%s'", table, geneName);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	softberryHomStaticLoad(row, &hom);
 	if ((gi = getGi(hom.giString)) == NULL)
 	    continue;
 	if (isFirst)
 	    {
 	    htmlHorizontalLine();
 	    printf("<H3>Protein Homologies:</H3>\n");
 	    isFirst = FALSE;
 	    gotAny = TRUE;
 	    }
 	printf("<A HREF=\"");
 	char temp[256];
 	safef(temp, sizeof(temp), "%s", gi);
 	printEntrezProteinUrl(stdout, temp);
 	printf("\" TARGET=_blank>%s</A> %s<BR>", hom.giString, hom.description);
 	}
     }
 if (gotAny)
     htmlHorizontalLine();
 hFreeConn(&conn);
 }
 
 void showPseudoHomologies(char *geneName, char *table)
 /* Show homology info. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 boolean isFirst = TRUE, gotAny = FALSE;
 struct borkPseudoHom hom;
 char *parts[10];
 int partCount;
 char *clone;
 
 if (sqlTableExists(conn, table))
     {
     sqlSafef(query, sizeof(query), "select * from %s where name = '%s'", table, geneName);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	borkPseudoHomStaticLoad(row, &hom);
 /*	if ((gi = getGi(hom.giString)) == NULL)
  *	    continue; */
 	if (isFirst)
 	    {
 	    htmlHorizontalLine();
 	    printf("<H3>Aligning Protein :</H3>\n");
 	    isFirst = FALSE;
 	    gotAny = TRUE;
 	    }
 	clone = cloneStringZ(hom.protRef,80);
 	partCount = chopString(hom.protRef, "_", parts, ArraySize(parts));
 	if (partCount > 1)
 	    {
 	    printf("<A HREF=");
 	    char temp[256];
 	    safef(temp, sizeof(temp), "%s", parts[1]);
 	    printSwissProtProteinUrl(stdout, temp);
 	    printf(" TARGET=_blank>Jump to SwissProt %s </A> " ,geneName);
 	    }
 	printf(" %s <BR><BR>Alignment Information:<BR><BR>%s<BR>", clone, hom.description);
 	}
     }
 if (gotAny)
     htmlHorizontalLine();
 hFreeConn(&conn);
 }
 
 void pseudoPrintPosHeader(struct bed *bed)
 /*    print header of pseudogene record */
 {
 printf("<p>");
 printf("<B>%s PseudoGene:</B> %s:%d-%d   %d bp<BR>\n", hOrganism(database),  bed->chrom, bed->chromStart, bed->chromEnd, bed->chromEnd-bed->chromStart);
 printf("Strand: %c",bed->strand[0]);
 printf("<p>");
 }
 
 void pseudoPrintPos(struct psl *pseudoList, struct pseudoGeneLink *pg, char *alignTable, int start, char *acc)
 /*    print details of pseudogene record */
 {
 char query[256];
 struct dyString *dy = newDyString(1024);
 char pfamDesc[128], *pdb;
 char chainTable[64];
 char chainTable_chrom[64];
 struct sqlResult *sr;
 char **row;
 struct sqlConnection *conn = hAllocConn(database);
 int first = 0;
 
 safef(chainTable,sizeof(chainTable), "selfChain");
 if (!hTableExists(database, chainTable) )
     safef(chainTable,sizeof(chainTable), "chainSelf");
 printf("<B>Description:</B> Retrogenes are processed mRNAs that are inserted back into the genome. Most are pseudogenes, and some are functional genes or anti-sense transcripts that may impede mRNA translation.<p>\n");
 printf("<B>Percent of retro that breaks net relative to Mouse : </B>%d&nbsp;%%<br>\n",pg->overlapMouse);
 printf("<B>Percent of retro that breaks net relative to Dog   : </B>%d&nbsp;%%<br>\n",pg->overlapDog);
 printf("<B>Percent of retro that breaks net relative to Macaque : </B>%d&nbsp;%%<br>\n",pg->overlapRhesus);
 printf("<B>Exons&nbsp;Inserted:</B>&nbsp;%d&nbsp;out&nbsp;of&nbsp;%d&nbsp;%d Parent Splice Sites <br>\n",pg->exonCover - pg->conservedSpliceSites, pg->exonCover,pg->exonCount);
 printf("<B>Conserved&nbsp;Splice&nbsp;Sites:</B>&nbsp;%d&nbsp;<br>\n",pg->conservedSpliceSites);
 printf("<B>PolyA&nbsp;tail:</B>&nbsp;%d As&nbsp;out&nbsp;of&nbsp;%d&nbsp;bp <B>PolyA Percent&nbsp;Id:&nbsp;</B>%5.1f&nbsp;%%\n",pg->polyA,pg->polyAlen, (float)pg->polyA*100/(float)pg->polyAlen);
 printf("&nbsp;(%d&nbsp;bp&nbsp;from&nbsp;end&nbsp;of&nbsp;retrogene)<br>\n",pg->polyAstart);
 printf("<B>Bases&nbsp;matching:</B>&nbsp;%d&nbsp;\n", pg->matches);
 printf("(%d&nbsp;%% of gene)<br>\n",pg->coverage);
 if (!sameString(pg->overName, "none"))
     printf("<B>Bases&nbsp;overlapping mRNA:</B>&nbsp;%s&nbsp;(%d&nbsp;bp)<br>\n", pg->overName, pg->maxOverlap);
 else
     printf("<B>No&nbsp;overlapping mRNA</B><br>");
 if (sameString(pg->type, "singleExon"))
     printf("<b>Overlap with Parent:&nbsp;</b>%s<p>\n",pg->type);
 else
     printf("<b>Type of RetroGene:&nbsp;</b>%s<p>\n",pg->type);
 if (pseudoList != NULL)
     {
     printf("<H4>RetroGene/Gene Alignment</H4>");
 
     printAlignments(pseudoList, start, "htcCdnaAli", alignTable, acc);
     }
 printf("<H4>Annotation for Gene locus that spawned RetroGene</H4>");
 
 printf("<ul>");
 if (!sameString(pg->refSeq,"noRefSeq"))
     {
     printf("<LI><B>RefSeq:</B> %s \n", pg->refSeq);
     linkToOtherBrowserExtra(database, pg->gChrom, pg->rStart, pg->rEnd, "refGene=pack");
     printf("%s:%d-%d \n", pg->gChrom, pg->rStart, pg->rEnd);
     printf("</A></LI>");
     }
 if (!sameString(pg->kgName,"noKg"))
     {
     printf("<LI><B>KnownGene:</B> " );
     printf("<A TARGET=\"_blank\" ");
     printf("HREF=\"../cgi-bin/hgGene?%s&%s=%s&%s=%s&%s=%s&%s=%d&%s=%d\" ",
                 cartSidUrlString(cart),
                 "db", database,
                 "hgg_gene", pg->kgName,
                 "hgg_chrom", pg->gChrom,
                 "hgg_start", pg->kStart,
                 "hgg_end", pg->kEnd);
     printf(">%s</A>  ",pg->kgName);
     linkToOtherBrowserExtra(database, pg->gChrom, pg->kStart, pg->kEnd, "knownGene=pack");
     printf("%s:%d-%d \n", pg->gChrom, pg->kStart, pg->kEnd);
     printf("</A></LI>");
     if (hTableExists(database, "knownGene"))
         {
         char *description;
         sqlSafef(query, sizeof(query),
                 "select proteinId from knownGene where name = '%s'", pg->kgName);
         description = sqlQuickString(conn, query);
         if (description != NULL)
             {
             printf("<LI><B>SwissProt ID: </B> " );
             printf("<A TARGET=\"_blank\" HREF=");
             printSwissProtProteinUrl(stdout, description);
             printf(">%s</A>",description);
             freez(&description);
             printf("</LI>" );
             }
         }
     }
 else
     {
     /* display mrna */
     printf("<LI><B>mRna:</B> %s \n", pg->name);
     linkToOtherBrowserExtra(database, pg->gChrom, pg->gStart, pg->gEnd, "mrna=pack");
     printf("%s:%d-%d \n", pg->gChrom, pg->gStart, pg->gEnd);
     printf("</A></LI>");
     }
 if (!sameString(pg->mgc,"noMgc"))
     {
     printf("<LI><B>%s Gene:</B> %s \n", mgcDbName(), pg->mgc);
     linkToOtherBrowserExtra(database, pg->gChrom, pg->mStart, pg->mEnd, "mgcGenes=pack");
     printf("%s:%d-%d \n", pg->gChrom, pg->mStart, pg->mEnd);
     printf("</A></LI>");
     }
 
 printf("</ul>");
 /* display pfam domains */
 
 printf("<p>");
 pdb = hPdbFromGdb(database);
 safef(pfamDesc, 128, "%s.pfamDesc", pdb);
 if (hTableExists(database, "knownToPfam") && hTableExists(database, pfamDesc))
     {
     sqlSafef(query, sizeof(query),
           "select description from knownToPfam kp, %s p where pfamAC = value and kp.name = '%s'",
             pfamDesc, pg->kgName);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         char *description = row[0];
         if (description == NULL)
             description = cloneString("n/a");
         printf("<B>Pfam Domain:</B> %s <p>", description);
         }
     sqlFreeResult(&sr);
     }
 
 if (hTableExists(database, "all_mrna"))
     {
     char parent[255];
     struct psl *pslList = NULL;
     char *dotPtr ;
     safef(parent, sizeof(parent), "%s",pg->name);
     /* strip off version and unique suffix when looking for parent gene*/
     dotPtr = rStringIn(".",parent) ;
     if (dotPtr != NULL)
         *dotPtr = '\0';
     pslList = loadPslRangeT("all_mrna", parent, pg->gChrom, pg->gStart, pg->gEnd);
 #ifdef NOT_USED
     char callClustal[512] = "YES";
     char inPath[512];
     safef(inPath, sizeof(inPath),
             "../pseudogene/pseudoHumanExp.%s.%s.%d.aln",
             pg->name, pg->chrom, pg->chromStart);
     /* either display a link to jalview or call it */
     if (callClustal != NULL && sameString(callClustal, "YES"))
         {
         displayJalView(inPath, "CLUSTAL", NULL);
         printf(" Display alignment of retrogene and parent mRNAs. ");
         }
 //    else
 //        {
 //        hgcAnchorJalview(pg->name,  faTn.forCgi);
 //        printf("JalView alignment of parent gene to retroGene</a>\n");
 //        }
 #endif /* NOT_USED */
 
     if (pslList != NULL)
         {
         printAlignments(pslList, pslList->tStart, "htcCdnaAli", "all_mrna", \
                 pg->name);
         htmlHorizontalLine();
         safef(chainTable_chrom,sizeof(chainTable_chrom), "%s_chainSelf",\
                 pg->chrom);
         if (hTableExists(database, chainTable_chrom) )
             {
                 /* lookup chain */
             sqlDyStringPrintf(dy,
                            "select id, score, qStart, qEnd, qStrand, qSize from %s_%s where ",
                 pg->chrom, chainTable);
             hAddBinToQuery(pg->chromStart, pg->chromEnd, dy);
             if (sameString(pg->gStrand,pg->strand))
                 sqlDyStringPrintf(dy,
                     "tEnd > %d and tStart < %d and qName = '%s' and qEnd > %d and qStart < %d and qStrand = '+' ",
                     pg->chromStart, pg->chromEnd, pg->gChrom, pg->gStart, pg->gEnd);
             else
                 {
                 sqlDyStringPrintf(dy,"tEnd > %d and tStart < %d and qName = '%s' and qEnd > %d "
                                   "and qStart < %d and qStrand = '-'",
                                pg->chromStart, pg->chromEnd, pg->gChrom,
                                hChromSize(database, pg->gChrom)-(pg->gEnd),
                                hChromSize(database, pg->gChrom)-(pg->gStart));
                 }
             dyStringAppend(dy, " order by qStart");
             sr = sqlGetResult(conn, dy->string);
             while ((row = sqlNextRow(sr)) != NULL)
                 {
                 int chainId, score;
                 unsigned int qStart, qEnd, qSize;
                 char qStrand;
                 if (first == 0)
                     {
                     printf("<H4>Gene/PseudoGene Alignment (multiple records are a result of breaks in the human Self Chaining)</H4>\n");
                     printf("Shows removed introns, frameshifts and in frame stops.\n");
                     first = 1;
                     }
                 chainId = sqlUnsigned(row[0]);
                 score = sqlUnsigned(row[1]);
                 qStart = sqlUnsigned(row[2]);
                 qEnd = sqlUnsigned(row[3]);
                 qStrand =row[4][0];
                 qSize = sqlUnsigned(row[5]);
                 if (qStrand == '-')
                     {
                     unsigned int tmp = qSize - qEnd;
                     qEnd = qSize - qStart;
                     qStart = tmp;
                     }
                 /* if (pg->chainId == 0) pg->chainId = chainId; */
                 puts("<ul><LI>\n");
                 hgcAnchorPseudoGene(pg->kgName, "knownGene", pg->chrom, "startcodon",
                                     pg->chromStart, pg->chromEnd, pg->gChrom, pg->kStart,
                                     pg->kEnd, chainId, database);
                 printf("Annotated alignment</a> using self chain.\n");
                 printf("Score: %d \n", score);
                 puts("</LI>\n");
                 printf("<ul>Raw alignment: ");
                 hgcAnchorTranslatedChain(chainId, chainTable, pg->chrom, pg->gStart, pg->gEnd);
                 printf("%s:%d-%d </A></ul> </ul>\n", pg->gChrom,qStart,qEnd);
                 }
             sqlFreeResult(&sr);
             }
         }
     }
 printf("<p>RetroGene&nbsp;Score:&nbsp;%d \n",pg->score);
 printf("Alignment&nbsp;Score:&nbsp;%d&nbsp;<br>\n",pg->axtScore);
 if (pg->posConf != 0)
     printf("AdaBoost&nbsp;Confidence:</B>&nbsp;%4.3f&nbsp;\n",pg->posConf);
 }
 
 void doPseudoPsl(struct trackDb *tdb, char *acc)
 /* Click on an pseudogene based on mrna alignment. */
 {
 char *tableName = tdb->table;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 char where[256];
 struct pseudoGeneLink *pg;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 int winStart = cartInt(cart, "l");
 int winEnd = cartInt(cart, "r");
 struct psl *pslList = NULL;
 char *chrom = cartString(cart, "c");
 char *alignTable = cgiUsualString("table", cgiString("g"));
 int rowOffset = 0;
 /* Get alignment info. */
 if (sameString(alignTable,"pseudoGeneLink2"))
     alignTable = cloneString("pseudoMrna2");
 else if (sameString(alignTable,"pseudoGeneLink3"))
     alignTable = cloneString("pseudoMrna3");
 else if (startsWith("pseudoGeneLink",alignTable))
     alignTable = cloneString("pseudoMrna");
 if (startsWith("pseudoUcsc",alignTable))
     {
     alignTable = cloneString("pseudoMrna");
     tableName = cloneString("pseudoGeneLink3");
     }
 if (hTableExists(database, alignTable) )
     {
     pslList = loadPslRangeT(alignTable, acc, chrom, winStart, winEnd);
     }
 else
     errAbort("Table %s not found.\n",alignTable);
 slSort(&pslList, pslCmpScoreDesc);
 
 /* print header */
 genericHeader(tdb, acc);
 /* Print non-sequence info. */
 cartWebStart(cart, database, "%s", acc);
 
 
 sqlSafefFrag(where, sizeof(where), "name = '%s'", acc);
 sr = hRangeQuery(conn, tableName, chrom, start, end, where, &rowOffset);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     pg = pseudoGeneLinkLoad(row+rowOffset);
     if (pg != NULL)
         {
         pseudoPrintPos(pslList, pg, alignTable, start, acc);
         }
     }
 printTrackHtml(tdb);
 
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 
 
 void doSoftberryPred(struct trackDb *tdb, char *geneName)
 /* Handle click on Softberry gene track. */
 {
 genericHeader(tdb, geneName);
 showHomologies(geneName, "softberryHom");
 if (sameWord(database, "sc1"))showSAM_T02(geneName);
 geneShowCommon(geneName, tdb, "softberryPep");
 printTrackHtml(tdb);
 }
 
 void doPseudoPred(struct trackDb *tdb, char *geneName)
 /* Handle click on Softberry gene track. */
 {
 genericHeader(tdb, geneName);
 showPseudoHomologies(geneName, "borkPseudoHom");
 geneShowCommon(geneName, tdb, "borkPseudoPep");
 printTrackHtml(tdb);
 }
 
 void doEncodePseudoPred(struct trackDb *tdb, char *geneName)
 {
 char query[256], *headerItem, *name2 = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 int start = cartInt(cart, "o");
 
 headerItem = cloneString(geneName);
 genericHeader(tdb, headerItem);
 printCustomUrl(tdb, geneName, FALSE);
 if ((sameString(tdb->table, "encodePseudogeneConsensus")) ||
          (sameString(tdb->table, "encodePseudogeneYale")))
     {
     sqlSafef(query, sizeof(query), "select name2 from %s where name = '%s'", tdb->table, geneName);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         name2 = cloneString(row[0]);
         }
     printOtherCustomUrl(tdb, name2, "url2", TRUE);
     }
 genericGenePredClick(conn, tdb, geneName, start, NULL, NULL);
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void showOrthology(char *geneName, char *table, struct sqlConnection *connMm)
 /* Show mouse Orthlogous info. */
 {
 char query[256];
 struct sqlResult *sr;
 char **row;
 boolean isFirst = TRUE, gotAny = FALSE;
 char *gi;
 struct softberryHom hom;
 
 
 if (sqlTableExists(connMm, table))
     {
     sqlSafef(query, sizeof(query), "select * from %s where name = '%s'", table, geneName);
     sr = sqlGetResult(connMm, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	softberryHomStaticLoad(row, &hom);
 	if ((gi = getGi(hom.giString)) == NULL)
 	    continue;
 	if (isFirst)
 	    {
 	    htmlHorizontalLine();
 	    printf("<H3>Protein Homologies:</H3>\n");
 	    isFirst = FALSE;
 	    gotAny = TRUE;
 	    }
 	printf("<A HREF=\"");
 	char temp[256];
 	safef(temp, sizeof(temp), "%s[gi]", gi);
 	printEntrezProteinUrl(stdout, query);
 	printf("\" TARGET=_blank>%s</A> %s<BR>", hom.giString, hom.description);
 	}
     }
 if (gotAny)
     htmlHorizontalLine();
 sqlFreeResult(&sr);
 }
 
 void doMouseOrtho(struct trackDb *tdb, char *geneName)
 /* Handle click on MouseOrtho gene track. */
 {
 struct sqlConnection *connMm = sqlConnect(mousedb);
 genericHeader(tdb, geneName);
 showOrthology(geneName, "softberryHom",connMm);
 tdb = hashFindVal(trackHash, "softberryGene");
 geneShowMouse(geneName, tdb, "softberryPep", connMm);
 printTrackHtml(tdb);
 sqlDisconnect(&connMm);
 }
 
 void showSangerExtra(char *geneName, char *extraTable)
 /* Show info from sanger22extra table if it exists. */
 {
 if (hTableExists(database, extraTable))
     {
     struct sanger22extra se;
     char query[256];
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
 
     sqlSafef(query, sizeof(query), "select * from %s where name = '%s'", extraTable, geneName);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
 	sanger22extraStaticLoad(row, &se);
 	printf("<B>Name:</B>  %s<BR>\n", se.name);
 	if (!sameString(se.name, se.locus))
 	    printf("<B>Locus:</B> %s<BR>\n", se.locus);
 	printf("<B>Description:</B> %s<BR>\n", se.description);
 	printf("<B>Gene type:</B> %s<BR>\n", se.geneType);
 	if (se.cdsType[0] != 0 && !sameString(se.geneType, se.cdsType))
 	    printf("<B>CDS type:</B> %s<BR>\n", se.cdsType);
 	}
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 }
 
 void doSangerGene(struct trackDb *tdb, char *geneName, char *pepTable, char *mrnaTable, char *extraTable)
 /* Handle click on Sanger gene track. */
 {
 genericHeader(tdb, geneName);
 showSangerExtra(geneName, extraTable);
 geneShowCommon(geneName, tdb, pepTable);
 printTrackHtml(tdb);
 }
 
 void doTrnaGenesGb(struct trackDb *tdb, char *trnaName)
 {
 struct tRNAs *trna;
 char query[512];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 int rowOffset;
 
 int start   = cartInt(cart, "o");
 int end     = cartInt(cart, "t");
 
 genericHeader(tdb,trnaName);
 
 rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 sqlSafef(query, ArraySize(query),
       "select * from %s where name = '%s' and chromStart=%d and chromEnd=%d",
 tdb->table, trnaName, start, end);
 
 sr = sqlGetResult(conn, query);
 
 /* use TABLE to align image with other info side by side */
 printf("<TABLE>");
 while ((row = sqlNextRow(sr)) != NULL)
     {
     char imgFileName[512];
     char encodedName[255];
     char *chp1, *chp2;
     int i, len;
     printf("<TR>");
     printf("<TD valign=top>");
 
     trna = tRNAsLoad(row+rowOffset);
 
     printf("<B>tRNA name: </B>%s<BR>\n",trna->name);
     printf("<B>tRNA isotype: </B> %s<BR>\n",trna->aa);
     printf("<B>tRNA anticodon: </B> %s<BR>\n",trna->ac);
     printf("<B>tRNAscan-SE score: </B> %.2f bits<BR>\n",trna->trnaScore);
     printf("<B>Intron(s): </B> %s<BR>\n",trna->intron);
     printf("<B>Genomic size: </B> %d nt<BR>\n",trna->chromEnd-trna->chromStart);
     printf("<B>Position:</B> "
        "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
        hgTracksPathAndSettings(), database, trna->chrom, trna->chromStart+1, trna->chromEnd);
     printf("%s:%d-%d</A><BR>\n", trna->chrom, trna->chromStart+1, trna->chromEnd);
     printf("<B>Strand:</B> %s<BR>\n", trna->strand);
     if (!sameString(trna->genomeUrl, ""))
         {
         printf("<BR><A HREF=\"%s\" TARGET=_blank>View summary of all genomic tRNA predictions</A><BR>\n"
 	       , trna->genomeUrl);
         printf("<BR><A HREF=\"%s\" TARGET=_blank>View complete details for this tRNA</A><BR>\n", trna->trnaUrl);
 	}
 
     if (trna->next != NULL)
       printf("<hr>\n");
 
     printf("</TD>");
 
     printf("<TD>");
 
     /* encode '?' in tRNA name into "%3F" */
     len = strlen(trna->name);
     chp1 = trna->name;
     chp2 = encodedName;
     for (i=0; i<len; i++)
         {
 	if (*chp1 == '?')
 	    {
 	    *chp2 = '%';
 	    chp2++; *chp2 = '3';
 	    chp2++; *chp2 = 'F';
 	    }
 	else
 	   {
 	   *chp2 = *chp1;
 	   }
 	chp1++;
 	chp2++;
 	}
     *chp2 = '\0';
 
     safef(imgFileName, sizeof imgFileName, "../htdocs/RNA-img/%s/%s-%s.gif", database,database,trna->name);
     if (fileExists(imgFileName))
         {
         printf(
 	"<img align=right src=\"../RNA-img/%s/%s-%s.gif\" alt='tRNA secondary structure for %s'>\n",
         database,database,encodedName,trna->name);
         }
     else
         {
         printf(
 	"<img align=right src=\"../RNA-img/%s/%s-%s.gif\" alt='tRNA secondary structure is not available for %s'>\n",
         database,database,trna->name,trna->name);
 	}
     printf("</TD>");
 
     printf("</TR>");
     }
 
 printf("</TABLE>");
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 tRNAsFree(&trna);
 }
 
 void doVegaGeneZfish(struct trackDb *tdb, char *name)
 /* Handle click on Vega gene track for zebrafish. */
 {
 struct vegaInfoZfish *vif = NULL;
 char query[256];
 struct sqlResult *sr;
 char **row;
 
 genericHeader(tdb, name);
 if (hTableExists(database, "vegaInfoZfish"))
     {
     struct sqlConnection *conn = hAllocConn(database);
 
     sqlSafef(query, sizeof(query),
 	  "select * from vegaInfoZfish where transcriptId = '%s'", name);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
 	AllocVar(vif);
 	vegaInfoZfishStaticLoad(row, vif);
 	}
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 
 printCustomUrl(tdb, name, TRUE);
 if (vif != NULL)
     {
     /* change confidence to lower case and display with method for gene type */
     tolowers(vif->confidence);
     printf("<B>VEGA Gene Type:</B> %s %s<BR>\n", vif->confidence, vif->method);
     printf("<B>VEGA Gene Name:</B> %s<BR>\n", vif->sangerName);
     if (differentString(vif->geneDesc, "NULL"))
         printf("<B>VEGA Gene Description:</B> %s<BR>\n", vif->geneDesc);
     printf("<B>VEGA Gene Id:</B> %s<BR>\n", vif->geneId);
     printf("<B>VEGA Transcript Id:</B> %s<BR>\n", name);
     printf("<B>ZFIN Id:</B> ");
     printf("<A HREF=\"http://zfin.org/cgi-bin/webdriver?MIval=aa-markerview.apg&OID=%s\" TARGET=_blank>%s</A><BR>\n", vif->zfinId, vif->zfinId);
     printf("<B>Official ZFIN Gene Symbol:</B> %s<BR>\n", vif->zfinSymbol);
     /* get information for the cloneId from */
 
     printf("<B>Clone Id:</B> \n");
     struct sqlConnection *conn2 = hAllocConn(database);
     sqlSafef(query, sizeof(query),
 	 "select cloneId from vegaToCloneId where transcriptId = '%s'", name);
     sr = sqlGetResult(conn2, query);
     if ((row = sqlNextRow(sr)) != NULL)
         printf("%s", row[0]);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         printf(" ,%s ", row[0]);
         }
     printf("<BR>\n");
     sqlFreeResult(&sr);
     hFreeConn(&conn2);
     }
 geneShowCommon(name, tdb, "vegaPep");
 printTrackHtml(tdb);
 }
 
 void doVegaGene(struct trackDb *tdb, char *item, char *itemForUrl)
 /* Handle click on Vega gene track. */
 {
 struct vegaInfo *vi = NULL;
 char versionString[256];
 char dateReference[256];
 char headerTitle[512];
 
 /* see if hgFixed.trackVersion exists */
 boolean trackVersionExists = hTableExists("hgFixed", "trackVersion");
 /* assume nothing found */
 versionString[0] = 0;
 dateReference[0] = 0;
 
 if (trackVersionExists)
     {
     char query[256];
     struct sqlConnection *conn = hAllocConn(database);
     sqlSafef(query, sizeof(query), "select version,dateReference from hgFixed.trackVersion where db = '%s' AND name = 'vegaGene' order by updateTime DESC limit 1", database);
     struct sqlResult *sr = sqlGetResult(conn, query);
     char **row;
 
     /* in case of NULL result from the table */
     versionString[0] = 0;
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	safef(versionString, sizeof(versionString), "Vega %s",
 		row[0]);
 	safef(dateReference, sizeof(dateReference), "%s",
 		row[1]);
 	}
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 
 if (itemForUrl == NULL)
     itemForUrl = item;
 
 if (versionString[0])
     safef(headerTitle, sizeof(headerTitle), "%s - %s", item, versionString);
 else
     safef(headerTitle, sizeof(headerTitle), "%s", item);
 
 genericHeader(tdb, headerTitle);
 
 if (hTableExists(database, "vegaInfo"))
     {
     char query[256];
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
 
     sqlSafef(query, sizeof(query),
 	  "select * from vegaInfo where transcriptId = '%s'", item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
 	AllocVar(vi);
 	vegaInfoStaticLoad(row, vi);
 	}
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 /* No archive for Vega */
 char *archive = NULL;
 printEnsemblOrVegaCustomUrl(tdb, itemForUrl, item == itemForUrl, archive);
 
 if (vi != NULL)
     {
     printf("<B>VEGA Gene Type:</B> %s<BR>\n", vi->method);
     printf("<B>VEGA Gene Name:</B> %s<BR>\n", vi->otterId);
     if (differentString(vi->geneDesc, "NULL"))
         printf("<B>VEGA Gene Description:</B> %s<BR>\n", vi->geneDesc);
     printf("<B>VEGA Gene Id:</B> %s<BR>\n", vi->geneId);
     printf("<B>VEGA Transcript Id:</B> %s<BR>\n", item);
     }
 geneShowCommon(item, tdb, "vegaPep");
 printTrackHtml(tdb);
 }
 
 void doBDGPGene(struct trackDb *tdb, char *geneName)
 /* Show Berkeley Drosophila Genome Project gene info. */
 {
 struct bdgpGeneInfo *bgi = NULL;
 struct flyBaseSwissProt *fbsp = NULL;
 char *geneTable = tdb->table;
 char *truncName = cloneString(geneName);
 char *ptr = strchr(truncName, '-');
 char infoTable[128];
 char pepTable[128];
 char query[512];
 
 if (ptr != NULL)
     *ptr = 0;
 safef(infoTable, sizeof(infoTable), "%sInfo", geneTable);
 
 genericHeader(tdb, geneName);
 
 if (hTableExists(database, infoTable))
     {
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     sqlSafef(query, sizeof(query),
 	  "select * from %s where bdgpName = \"%s\";",
 	  infoTable, truncName);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
 	{
 	bgi = bdgpGeneInfoLoad(row);
 	if (hTableExists(database, "flyBaseSwissProt"))
 	    {
 	    sqlSafef(query, sizeof(query),
 		  "select * from flyBaseSwissProt where flyBaseId = \"%s\"",
 		  bgi->flyBaseId);
 	    sqlFreeResult(&sr);
 	    sr = sqlGetResult(conn, query);
 	    if ((row = sqlNextRow(sr)) != NULL)
 		fbsp = flyBaseSwissProtLoad(row);
 	    }
 	}
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 if (bgi != NULL)
     {
     if (!sameString(bgi->symbol, geneName))
 	{
 	printf("<B>Gene symbol:</B> %s<BR>\n", bgi->symbol);
 	}
     if (fbsp != NULL)
 	{
 	printf("<B>SwissProt:</B> <A HREF=");
 	printSwissProtProteinUrl(stdout, fbsp->swissProtId);
 	printf(" TARGET=_BLANK>%s</A> (%s) %s<BR>\n",
 	       fbsp->swissProtId, fbsp->spSymbol, fbsp->spGeneName);
 	}
     printf("<B>FlyBase:</B> <A HREF=");
     printFlyBaseUrl(stdout, bgi->flyBaseId);
     printf(" TARGET=_BLANK>%s</A><BR>\n", bgi->flyBaseId);
     printf("<B>BDGP:</B> <A HREF=");
     printBDGPUrl(stdout, truncName);
     printf(" TARGET=_BLANK>%s</A><BR>\n", truncName);
     }
 printCustomUrl(tdb, geneName, FALSE);
 showGenePos(geneName, tdb);
 if (bgi != NULL)
     {
     if (bgi->go != NULL && bgi->go[0] != 0)
 	{
 	struct sqlConnection *goConn = sqlMayConnect("go");
 	char *goTerm = NULL;
 	char *words[10];
 	char buf[512];
 	int wordCount = chopCommas(bgi->go, words);
 	int i;
 	puts("<B>Gene Ontology terms from BDGP:</B> <BR>");
 	for (i=0;  i < wordCount && words[i][0] != 0;  i++)
 	    {
 	    if (i > 0 && sameWord(words[i], words[i-1]))
 		continue;
 	    goTerm = "";
 	    if (goConn != NULL)
 		{
 		sqlSafef(query, sizeof(query),
 		      "select name from term where acc = 'GO:%s';",
 		      words[i]);
 		goTerm = sqlQuickQuery(goConn, query, buf, sizeof(buf));
 		if (goTerm == NULL)
 		    goTerm = "";
 		}
 	    printf("&nbsp;&nbsp;&nbsp;GO:%s: %s<BR>\n",
 		   words[i], goTerm);
 	    }
 	sqlDisconnect(&goConn);
 	}
     if (bgi->cytorange != NULL && bgi->cytorange[0] != 0)
 	{
 	printf("<B>Cytorange:</B> %s<BR>", bgi->cytorange);
 	}
     }
 printf("<H3>Links to sequence:</H3>\n");
 printf("<UL>\n");
 
 safef(pepTable, sizeof(pepTable), "%sPep", geneTable);
 if (hGenBankHaveSeq(database, geneName, pepTable))
     {
     puts("<LI>\n");
     hgcAnchorSomewhere("htcTranslatedProtein", geneName, pepTable,
 		       seqName);
     printf("Predicted Protein</A> \n");
     puts("</LI>\n");
     }
 
 puts("<LI>\n");
 hgcAnchorSomewhere("htcGeneMrna", geneName, tdb->track, seqName);
 printf("%s</A> may be different from the genomic sequence.\n",
        "Predicted mRNA");
 puts("</LI>\n");
 
 puts("<LI>\n");
 hgcAnchorSomewhere("htcGeneInGenome", geneName, tdb->track, seqName);
 printf("Genomic Sequence</A> from assembly\n");
 puts("</LI>\n");
 printf("</UL>\n");
 printTrackHtml(tdb);
 }
 
 char *pepTableFromType(char *type)
 /* If type (should be from tdb->type) starts with "genePred xxxx",
  * return "xxxx" as the pepTable for this track. */
 {
 char *dupe, *words[16];
 int wordCount;
 char *pepTable = NULL;
 dupe = cloneString(type);
 wordCount = chopLine(dupe, words);
 
 if (wordCount > 1 && sameWord(words[0], "genePred") && words[1] != NULL)
     pepTable = cloneString(words[1]);
 freeMem(dupe);
 return pepTable;
 }
 
 struct bed *getBedAndPrintPos(struct trackDb *tdb, char *name, int maxN)
 /* Dig up the bed for this item just to print the position. */
 {
 struct bed *bed = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row = NULL;
 char query[256];
 char table[64];
 boolean hasBin = FALSE;
 int n = atoi(tdb->type + 4);
 int start = cgiInt("o");
 if (n < 3)
     n = 3;
 if (n > maxN)
     n = maxN;
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and chromStart = %d "
       "and name = '%s'",
       table, seqName, start, name);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     bed = bedLoadN(row+hasBin, n);
     bedPrintPos(bed, n, tdb);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 return bed;
 }
 
 void printFBLinkLine(char *label, char *id)
 /* If id is not NULL/empty, print a label and link to FlyBase. */
 {
 if (isNotEmpty(id))
     {
     printf("<B>%s:</B> <A HREF=", label);
     printFlyBaseUrl(stdout, id);
     printf(" TARGET=_BLANK>%s</A><BR>\n", id);
     }
 }
 
 void showFlyBase2004Xref(char *xrefTable, char *geneName)
 /* Show FlyBase gene info provided as of late 2004
  * (D. mel. v4.0 / D. pseud. 1.0).  Assumes xrefTable exists
  * and matches flyBase2004Xref.sql! */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 struct flyBase2004Xref *xref = NULL;
 struct flyBaseSwissProt *fbsp = NULL;
 char query[512];
 
 sqlSafef(query, sizeof(query),
       "select * from %s where name = \"%s\";", xrefTable, geneName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     xref = flyBase2004XrefLoad(row);
     if (hTableExists(database, "flyBaseSwissProt") && isNotEmpty(xref->fbgn))
 	{
 	sqlSafef(query, sizeof(query),
 	      "select * from flyBaseSwissProt where flyBaseId = \"%s\"",
 	      xref->fbgn);
 	sqlFreeResult(&sr);
 	sr = sqlGetResult(conn, query);
 	if ((row = sqlNextRow(sr)) != NULL)
 	    fbsp = flyBaseSwissProtLoad(row);
 	}
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 if (xref != NULL)
     {
     if (isNotEmpty(xref->symbol) && !sameString(xref->symbol, geneName))
 	{
 	printf("<B>Gene symbol:</B> %s<BR>\n", xref->symbol);
 	}
     if (isNotEmpty(xref->synonyms))
 	{
 	int last = strlen(xref->synonyms) - 1;
 	if (xref->synonyms[last] == ',')
 	    xref->synonyms[last] = 0;
 	printf("<B>Synonyms:</B> ");
 	htmlTextOut(xref->synonyms);
 	printf("<BR>\n");
 	}
     if (fbsp != NULL)
 	{
 	printf("<B>SwissProt:</B> <A HREF=");
 	printSwissProtProteinUrl(stdout, fbsp->swissProtId);
 	printf(" TARGET=_BLANK>%s</A> (%s) %s<BR>\n",
 	       fbsp->swissProtId, fbsp->spSymbol, fbsp->spGeneName);
 	}
     if (isNotEmpty(xref->type))
 	printf("<B>Type:</B> %s<BR>\n", xref->type);
     printFBLinkLine("FlyBase Gene", xref->fbgn);
     printFBLinkLine("FlyBase Protein", xref->fbpp);
     printFBLinkLine("FlyBase Annotation", xref->fban);
     }
 }
 
 void doFlyBaseGene(struct trackDb *tdb, char *geneName)
 /* Show FlyBase gene info. */
 {
 char *xrefTable = trackDbSettingOrDefault(tdb, "xrefTable", "flyBase2004Xref");
 genericHeader(tdb, geneName);
 
 /* Note: if we need to expand to a different xref table definition, do
  * some checking here.  For now, assume it's flyBase2004Xref-compatible: */
 if (hTableExists(database, xrefTable))
     showFlyBase2004Xref(xrefTable, geneName);
 
 printCustomUrl(tdb, geneName, FALSE);
 if (startsWith("genePred", tdb->type))
     {
     char *pepTable = pepTableFromType(tdb->type);
     showGenePos(geneName, tdb);
     printf("<H3>Links to sequence:</H3>\n");
     printf("<UL>\n");
 
     if (pepTable != NULL && hGenBankHaveSeq(database, geneName, pepTable))
 	{
 	puts("<LI>\n");
 	hgcAnchorSomewhere("htcTranslatedProtein", geneName, pepTable,
 			   seqName);
         printf("Predicted Protein</A> \n");
 	puts("</LI>\n");
 	}
     else
         errAbort("Doh, no go for %s from %s<BR>\n", geneName, pepTable);
 
     puts("<LI>\n");
     hgcAnchorSomewhere("htcGeneMrna", geneName, tdb->track, seqName);
     printf("%s</A> may be different from the genomic sequence.\n",
 	   "Predicted mRNA");
     puts("</LI>\n");
 
     puts("<LI>\n");
     hgcAnchorSomewhere("htcGeneInGenome", geneName, tdb->track, seqName);
     printf("Genomic Sequence</A> from assembly\n");
     puts("</LI>\n");
     printf("</UL>\n");
     }
 else if (startsWith("bed", tdb->type))
     {
     struct bed *bed = getBedAndPrintPos(tdb, geneName, 4);
     if (bed != NULL && bed->strand[0] != 0)
 	printf("<B>Strand:</B> %s<BR>\n", bed->strand);
     bedFree(&bed);
     }
 printTrackHtml(tdb);
 }
 
 void doBGIGene(struct trackDb *tdb, char *geneName)
 /* Show Beijing Genomics Institute gene annotation info. */
 {
 struct bgiGeneInfo *bgi = NULL;
 char *geneTable = tdb->table;
 char infoTable[128];
 char pepTable[128];
 char query[512];
 
 safef(infoTable, sizeof(infoTable), "%sInfo", geneTable);
 
 genericHeader(tdb, geneName);
 
 if (hTableExists(database, infoTable))
     {
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     sqlSafef(query, sizeof(query),
 	  "select * from %s where name = \"%s\";", infoTable, geneName);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
 	bgi = bgiGeneInfoLoad(row);
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 printCustomUrl(tdb, geneName, FALSE);
 showGenePos(geneName, tdb);
 if (bgi != NULL)
     {
     printf("<B>Annotation source:</B> %s<BR>\n", bgi->source);
     if (bgi->go != NULL && bgi->go[0] != 0 && !sameString(bgi->go, "None"))
 	{
 	struct sqlConnection *goConn = sqlMayConnect("go");
 	char *goTerm = NULL;
 	char *words[16];
 	char buf[512];
 	int wordCount = chopCommas(bgi->go, words);
 	int i;
 	puts("<B>Gene Ontology terms from BGI:</B> <BR>");
 	for (i=0;  i < wordCount && words[i][0] != 0;  i++)
 	    {
 	    if (i > 0 && sameWord(words[i], words[i-1]))
 		continue;
 	    goTerm = "";
 	    if (goConn != NULL)
 		{
 		sqlSafef(query, sizeof(query),
 		      "select name from term where acc = 'GO:%s';",
 		      words[i]);
 		goTerm = sqlQuickQuery(goConn, query, buf, sizeof(buf));
 		if (goTerm == NULL)
 		    goTerm = "";
 		}
 	    printf("&nbsp;&nbsp;&nbsp;GO:%s: %s<BR>\n",
 		   words[i], goTerm);
 	    }
 	sqlDisconnect(&goConn);
 	}
     if (bgi->ipr != NULL && bgi->ipr[0] != 0 && !sameString(bgi->ipr, "None"))
 	{
 	char *words[16];
 	int wordCount = chopByChar(bgi->ipr, ';', words, ArraySize(words));
 	int i;
 	printf("<B>Interpro terms from BGI:</B> <BR>\n");
 	for (i=0;  i < wordCount && words[i][0] != 0;  i++)
 	    {
 	    printf("&nbsp;&nbsp;&nbsp;%s<BR>\n", words[i]);
 	    }
 	}
     if (hTableExists(database, "bgiGeneSnp") && hTableExists(database, "bgiSnp"))
 	{
 	struct sqlConnection *conn = hAllocConn(database);
 	struct sqlConnection *conn2 = hAllocConn(database);
 	struct sqlResult *sr;
 	struct sqlResult *sr2;
 	struct bgiSnp snp;
 	struct bgiGeneSnp gs;
 	char **row;
 	int rowOffset = hOffsetPastBin(database, seqName, "bgiSnp");
 	boolean init = FALSE;
 	sqlSafef(query, sizeof(query),
 	      "select * from bgiGeneSnp where geneName = '%s'", geneName);
 	sr = sqlGetResult(conn, query);
 	while ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    if (! init)
 		{
 		printf("<B>BGI SNPs associated with gene %s:</B> <BR>\n",
 		       geneName);
 		init = TRUE;
 		}
 	    bgiGeneSnpStaticLoad(row, &gs);
 	    sqlSafef(query, sizeof(query),
 		  "select * from bgiSnp where name = '%s'", gs.snpName);
 	    sr2 = sqlGetResult(conn2, query);
 	    if ((row = sqlNextRow(sr2)) != NULL)
 		{
 		bgiSnpStaticLoad(row+rowOffset, &snp);
 		printf("&nbsp;&nbsp;&nbsp;<A HREF=%s&g=bgiSnp&i=%s&db=%s&c=%s&o=%d&t=%d>%s</A>: %s",
 		       hgcPathAndSettings(), gs.snpName, database,
 		       seqName, snp.chromStart, snp.chromEnd, gs.snpName,
 		       gs.geneAssoc);
 		if (gs.effect[0] != 0)
 		    printf(", %s", gs.effect);
 		if (gs.phase[0] != 0)
 		    printf(", phase %c", gs.phase[0]);
 		if (gs.siftComment[0] != 0)
 		    printf(", SIFT comment: %s", gs.siftComment);
 		puts("<BR>");
 		}
 	    sqlFreeResult(&sr2);
 	    }
 	sqlFreeResult(&sr);
 	hFreeConn(&conn);
 	hFreeConn(&conn2);
 	}
     }
 printf("<H3>Links to sequence:</H3>\n");
 printf("<UL>\n");
 
 safef(pepTable, sizeof(pepTable), "%sPep", geneTable);
 if (hGenBankHaveSeq(database, geneName, pepTable))
     {
     puts("<LI>\n");
     hgcAnchorSomewhere("htcTranslatedProtein", geneName, pepTable,
 		       seqName);
     printf("Predicted Protein</A> \n");
     puts("</LI>\n");
     }
 
 puts("<LI>\n");
 hgcAnchorSomewhere("htcGeneMrna", geneName, tdb->track, seqName);
 printf("%s</A> may be different from the genomic sequence.\n",
        "Predicted mRNA");
 puts("</LI>\n");
 
 puts("<LI>\n");
 hgcAnchorSomewhere("htcGeneInGenome", geneName, tdb->track, seqName);
 printf("Genomic Sequence</A> from assembly\n");
 puts("</LI>\n");
 printf("</UL>\n");
 printTrackHtml(tdb);
 }
 
 
 void doBGISnp(struct trackDb *tdb, char *itemName)
 /* Put up info on a Beijing Genomics Institute SNP. */
 {
 char *table = tdb->table;
 struct bgiSnp snp;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 
 genericHeader(tdb, itemName);
 
 sqlSafef(query, sizeof(query),
       "select * from %s where name = '%s'", table, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     bgiSnpStaticLoad(row+rowOffset, &snp);
     bedPrintPos((struct bed *)&snp, 3, tdb);
     printf("<B>SNP Type:</B> %s<BR>\n",
            (snp.snpType[0] == 'S') ? "Substitution" :
 	   (snp.snpType[0] == 'I') ? "Insertion" : "Deletion");
     printf("<B>SNP Sequence:</B> %s<BR>\n", snp.snpSeq);
     printf("<B>SNP in Broiler?:</B> %s<BR>\n", snp.inBroiler);
     printf("<B>SNP in Layer?:</B> %s<BR>\n", snp.inLayer);
     printf("<B>SNP in Silkie?:</B> %s<BR>\n", snp.inSilkie);
     if (hTableExists(database, "bgiGeneSnp") && hTableExists(database, "bgiGene"))
 	{
 	struct genePred *bg;
 	struct sqlConnection *conn2 = hAllocConn(database);
 	struct sqlConnection *conn3 = hAllocConn(database);
 	struct sqlResult *sr2, *sr3;
 	struct bgiGeneSnp gs;
 	sqlSafef(query, sizeof(query),
 	      "select * from bgiGeneSnp where snpName = '%s'", snp.name);
 	sr2 = sqlGetResult(conn2, query);
 	while ((row = sqlNextRow(sr2)) != NULL)
 	    {
 	    bgiGeneSnpStaticLoad(row, &gs);
 	    sqlSafef(query, sizeof(query),
 		  "select * from bgiGene where name = '%s'", gs.geneName);
 	    sr3 = sqlGetResult(conn3, query);
 	    while ((row = sqlNextRow(sr3)) != NULL)
 		{
 		bg = genePredLoad(row);
 		printf("<B>Associated gene:</B> <A HREF=%s&g=bgiGene&i=%s&c=%s&db=%s&o=%d&t=%d&l=%d&r=%d>%s</A>: %s",
 		       hgcPathAndSettings(), gs.geneName,
 		       seqName, database, bg->txStart, bg->txEnd,
 		       bg->txStart, bg->txEnd, gs.geneName, gs.geneAssoc);
 		if (gs.effect[0] != 0)
 		    printf(" %s", gs.effect);
 		if (gs.phase[0] != 0)
 		    printf(" phase %c", gs.phase[0]);
 		if (gs.siftComment[0] != 0)
 		    printf(", SIFT comment: %s", gs.siftComment);
 		puts("<BR>");
 		}
 	    sqlFreeResult(&sr3);
 	    }
 	hFreeConn(&conn3);
 	sqlFreeResult(&sr2);
 	hFreeConn(&conn2);
 	}
     printf("<B>Quality Scores:</B> %d in reference, %d in read<BR>\n",
 	   snp.qualChr, snp.qualReads);
     printf("<B>Left Primer Sequence:</B> %s<BR>\n", snp.primerL);
     printf("<B>Right Primer Sequence:</B> %s<BR>\n", snp.primerR);
     if (snp.snpType[0] != 'S')
         if (snp.questionM[0] == 'H')
 	    printf("<B>Indel Confidence</B>: High\n");
         if (snp.questionM[0] == 'L')
 	    printf("<B>Indel Confidence</B>: Low\n");
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 
 void parseChromPointPos(char *pos, char *retChrom, int *retPos)
 /* Parse out chrN:123 into chrN and 123. */
 {
 char *s, *e;
 int len;
 e = strchr(pos, ':');
 if (e == NULL)
     errAbort("No : in chromosome point position %s", pos);
 len = e - pos;
 memcpy(retChrom, pos, len);
 retChrom[len] = 0;
 s = e+1;
 *retPos = atoi(s);
 }
 
 void doGenomicDups(struct trackDb *tdb, char *dupName)
 /* Handle click on genomic dup track. */
 {
 struct genomicDups dup;
 char query[512];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char oChrom[64];
 int oStart;
 
 cartWebStart(cart, database, "Genomic Duplications");
 printf("<H2>Genomic Duplication Region</H2>\n");
 if (cgiVarExists("o"))
     {
     int start = cartInt(cart, "o");
     int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
     parseChromPointPos(dupName, oChrom, &oStart);
 
     sqlSafef(query, sizeof query, "select * from %s where chrom = '%s' and chromStart = %d "
 	    "and otherChrom = '%s' and otherStart = %d",
 	    tdb->table, seqName, start, oChrom, oStart);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)))
 	{
 	genomicDupsStaticLoad(row+rowOffset, &dup);
 	printf("<B>Region Position:</B> <A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
 	       hgTracksPathAndSettings(),
 	       database, dup.chrom, dup.chromStart, dup.chromEnd);
 	printf("%s:%d-%d</A><BR>\n", dup.chrom, dup.chromStart, dup.chromEnd);
 	printf("<B>Other Position:</B> <A HREF=\"%s&db=%s&position=%s%%3A%d-%d\" TARGET=_blank>",
 	       hgTracksName(),
 	       database, dup.otherChrom, dup.otherStart, dup.otherEnd);
 	printf("%s:%d-%d</A><BR>\n", dup.otherChrom, dup.otherStart, dup.otherEnd);
 	printf("<B>Relative orientation:</B> %s<BR>\n", dup.strand);
 	printf("<B>Percent identity:</B> %3.1f%%<BR>\n", 0.1*dup.score);
 	printf("<B>Size:</B> %d<BR>\n", dup.alignB);
 	printf("<B>Bases matching:</B> %d<BR>\n", dup.matchB);
 	printf("<B>Bases not matching:</B> %d<BR>\n", dup.mismatchB);
 	htmlHorizontalLine();
 	}
     }
 else
     {
     puts("<P>Click directly on a repeat for specific information on that repeat</P>");
     }
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void htcExtSeq(char *item)
 /* Print out DNA from some external but indexed .fa file. */
 {
 struct dnaSeq *seq;
 cartHtmlStart(item);
 seq = hExtSeq(database, item);
 printf("<PRE><TT>");
 faWriteNext(stdout, item, seq->dna, seq->size);
 printf("</TT></PRE>");
 freeDnaSeq(&seq);
 }
 
 void doBlatMouse(struct trackDb *tdb, char *itemName)
 /* Handle click on blatMouse track. */
 {
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int start = cartInt(cart, "o");
 struct psl *pslList = NULL, *psl;
 char *tiNum = strrchr(itemName, '|');
 boolean hasBin;
 char table[64];
 
 /* Print heading info including link to NCBI. */
 if (tiNum != NULL)
     ++tiNum;
 cartWebStart(cart, database, "%s", itemName);
 printf("<H1>Information on Mouse %s %s</H1>",
        (tiNum == NULL ? "Contig" : "Read"), itemName);
 
 /* Print links to NCBI and to sequence. */
 if (tiNum != NULL)
     {
     printf("Link to ");
     printf("<A HREF=\"http://www.ncbi.nlm.nih.gov/Traces/trace.cgi?val=%s\" TARGET=_blank>", tiNum);
     printf("NCBI Trace Repository for %s\n</A><BR>\n", itemName);
     }
 printf("Get ");
 printf("<A HREF=\"%s&g=htcExtSeq&c=%s&l=%d&r=%d&i=%s\">",
        hgcPathAndSettings(), seqName, winStart, winEnd, itemName);
 printf("Mouse DNA</A><BR>\n");
 
 /* Print info about mate pair. */
 if (tiNum != NULL && sqlTableExists(conn, "mouseTraceInfo"))
     {
     char buf[256];
     char *templateId;
     boolean gotMate = FALSE;
     sqlSafef(query, sizeof query, "select templateId from mouseTraceInfo where ti = '%s'", itemName);
     templateId = sqlQuickQuery(conn, query, buf, sizeof(buf));
     if (templateId != NULL)
         {
 	sqlSafef(query, sizeof query, "select ti from mouseTraceInfo where templateId = '%s'", templateId);
 	sr = sqlGetResult(conn, query);
 	while ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    char *ti = row[0];
 	    if (!sameString(ti, itemName))
 	        {
 		printf("Get ");
 		printf("<A HREF=\"%s&g=htcExtSeq&c=%s&l=%d&r=%d&i=%s\">",
 		       hgcPathAndSettings(), seqName, winStart, winEnd, ti);
 		printf("DNA for read on other end of plasmid</A><BR>\n");
 		gotMate = TRUE;
 		}
 	    }
 	sqlFreeResult(&sr);
 	}
     if (!gotMate)
 	printf("No read from other end of plasmid in database.<BR>\n");
     }
 
 /* Get alignment info and print. */
 printf("<H2>Alignments</H2>\n");
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where qName = '%s'", table, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     psl = pslLoad(row+hasBin);
     slAddHead(&pslList, psl);
     }
 sqlFreeResult(&sr);
 slReverse(&pslList);
 printAlignments(pslList, start, "htcBlatXeno", tdb->table, itemName);
 printTrackHtml(tdb);
 }
 
 boolean parseRange(char *range, char **retSeq, int *retStart, int *retEnd)
 /* Parse seq:start-end into components. */
 {
 char *s, *e;
 s = strchr(range, ':');
 if (s == NULL)
     return FALSE;
 *s++ = 0;
 e = strchr(s, '-');
 if (e == NULL)
     return FALSE;
 *e++ = 0;
 if (!isdigit(s[0]) || !isdigit(e[0]))
     return FALSE;
 *retSeq = range;
 *retStart = atoi(s);
 *retEnd = atoi(e);
 return TRUE;
 }
 
 void mustParseRange(char *range, char **retSeq, int *retStart, int *retEnd)
 /* Parse seq:start-end or die. */
 {
 if (!parseRange(range, retSeq, retStart, retEnd))
     errAbort("Malformed range %s", range);
 }
 
 struct psl *loadPslAt(char *track, char *qName, int qStart, int qEnd, char *tName, int tStart, int tEnd)
 /* Load a specific psl */
 {
 struct dyString *dy = newDyString(1024);
 struct sqlConnection *conn = hAllocConn(database);
 char table[64];
 boolean hasBin;
 struct sqlResult *sr;
 char **row;
 struct psl *psl;
 
 hFindSplitTable(database, tName, track, table, &hasBin);
 sqlDyStringPrintf(dy, "select * from %s ", table);
 sqlDyStringPrintf(dy, "where qStart = %d ", qStart);
 sqlDyStringPrintf(dy, "and qEnd = %d ", qEnd);
 sqlDyStringPrintf(dy, "and qName = '%s' ", qName);
 sqlDyStringPrintf(dy, "and tStart = %d ", tStart);
 sqlDyStringPrintf(dy, "and tEnd = %d ", tEnd);
 sqlDyStringPrintf(dy, "and tName = '%s'", tName);
 sr = sqlGetResult(conn, dy->string);
 row = sqlNextRow(sr);
 if (row == NULL)
     errAbort("Couldn't loadPslAt %s:%d-%d", tName, tStart, tEnd);
 psl = pslLoad(row + hasBin);
 sqlFreeResult(&sr);
 freeDyString(&dy);
 hFreeConn(&conn);
 return psl;
 }
 
 struct psl *loadPslFromRangePair(char *track, char *rangePair)
 /* Load a specific psl given 'qName:qStart-qEnd tName:tStart-tEnd' in rangePair. */
 {
 char *qRange, *tRange;
 char *qName, *tName;
 int qStart, qEnd, tStart, tEnd;
 qRange = nextWord(&rangePair);
 tRange = nextWord(&rangePair);
 if (tRange == NULL)
     errAbort("Expecting two ranges in loadPslFromRangePair");
 mustParseRange(qRange, &qName, &qStart, &qEnd);
 mustParseRange(tRange, &tName, &tStart, &tEnd);
 return loadPslAt(track, qName, qStart, qEnd, tName, tStart, tEnd);
 }
 
 void longXenoPsl1Given(struct trackDb *tdb, char *item,
                        char *otherOrg, char *otherChromTable,
                        char *otherDb, struct psl *psl, char *pslTableName )
 /* Put up cross-species alignment when the second species
  * sequence is in a nib file, AND psl record is given. */
 {
 char otherString[256];
 char *thisOrg = hOrganism(database);
 
 cartWebStart(cart, database, "%s", tdb->longLabel);
 printf("<B>%s position:</B> <a target=\"_blank\" href=\"%s?db=%s&position=%s%%3A%d-%d\">%s:%d-%d</a><BR>\n",
        otherOrg, hgTracksName(), otherDb, psl->qName, psl->qStart+1, psl->qEnd,
        psl->qName, psl->qStart+1, psl->qEnd);
 printf("<B>%s size:</B> %d<BR>\n", otherOrg, psl->qEnd - psl->qStart);
 printf("<B>%s position:</B> %s:%d-%d<BR>\n", thisOrg,
        psl->tName, psl->tStart+1, psl->tEnd);
 
 printf("<B>%s size:</B> %d<BR>\n", thisOrg, psl->tEnd - psl->tStart);
 printf("<B>Identical Bases:</B> %d<BR>\n", psl->match + psl->repMatch);
 printf("<B>Number of Gapless Aligning Blocks:</B> %d<BR>\n", psl->blockCount );
 printf("<B>Percent identity within gapless aligning blocks:</B> %3.1f%%<BR>\n", 0.1*(1000 - pslCalcMilliBad(psl, FALSE)));
 printf("<B>Strand:</B> %s<BR>\n",psl->strand);
 printf("<B>Browser window position:</B> %s:%d-%d<BR>\n", seqName, winStart+1, winEnd);
 printf("<B>Browser window size:</B> %d<BR>\n", winEnd - winStart);
 safef(otherString, sizeof otherString, "%d&pslTable=%s&otherOrg=%s&otherChromTable=%s&otherDb=%s", psl->tStart,
 	pslTableName, otherOrg, otherChromTable, otherDb);
 
 if (pslTrimToTargetRange(psl, winStart, winEnd) != NULL)
     {
     hgcAnchorSomewhere("htcLongXenoPsl2", item, otherString, psl->tName);
     printf("<BR>View details of parts of alignment within browser window</A>.<BR>\n");
     }
 }
 
 /*
    Multipurpose function to show alignments in details pages where applicable
 */
 void longXenoPsl1(struct trackDb *tdb, char *item,
 		  char *otherOrg, char *otherChromTable, char *otherDb)
 /* Put up cross-species alignment when the second species
  * sequence is in a nib file. */
 {
 struct psl *psl = NULL;
 char otherString[256];
 char *thisOrg = hOrganism(database);
 
 cartWebStart(cart, database, "%s", tdb->longLabel);
 psl = loadPslFromRangePair(tdb->table, item);
 printf("<B>%s position:</B> <a target=\"_blank\" href=\"%s?db=%s&position=%s%%3A%d-%d\">%s:%d-%d</a><BR>\n",
        otherOrg, hgTracksName(), otherDb, psl->qName, psl->qStart+1, psl->qEnd,
        psl->qName, psl->qStart+1, psl->qEnd);
 printf("<B>%s size:</B> %d<BR>\n", otherOrg, psl->qEnd - psl->qStart);
 printf("<B>%s position:</B> %s:%d-%d<BR>\n", thisOrg,
        psl->tName, psl->tStart+1, psl->tEnd);
 printf("<B>%s size:</B> %d<BR>\n", thisOrg,
        psl->tEnd - psl->tStart);
 printf("<B>Identical Bases:</B> %d<BR>\n", psl->match + psl->repMatch);
 printf("<B>Number of Gapless Aligning Blocks:</B> %d<BR>\n", psl->blockCount );
 printf("<B>Percent identity within gapless aligning blocks:</B> %3.1f%%<BR>\n", 0.1*(1000 - pslCalcMilliBad(psl, FALSE)));
 printf("<B>Strand:</B> %s<BR>\n",psl->strand);
 printf("<B>Browser window position:</B> %s:%d-%d<BR>\n", seqName, winStart+1, winEnd);
 printf("<B>Browser window size:</B> %d<BR>\n", winEnd - winStart);
 safef(otherString, sizeof otherString, "%d&pslTable=%s&otherOrg=%s&otherChromTable=%s&otherDb=%s", psl->tStart,
 	tdb->table, otherOrg, otherChromTable, otherDb);
 /* joni */
 if (pslTrimToTargetRange(psl, winStart, winEnd) != NULL)
     {
     hgcAnchorSomewhere("htcLongXenoPsl2", item, otherString, psl->tName);
     printf("<BR>View details of parts of alignment within browser window</A>.<BR>\n");
     }
 
 if (containsStringNoCase(otherDb, "zoo"))
     printf("<P><A HREF='%s&db=%s'>Go to the browser view of the %s</A><BR>\n",
 	   hgTracksPathAndSettings(), otherDb, otherOrg);
 printTrackHtml(tdb);
 }
 
 /* Multipurpose function to show alignments in details pages where applicable
    Show the URL from trackDb as well.
    Only used for the Chimp tracks right now. */
 void longXenoPsl1Chimp(struct trackDb *tdb, char *item,
 		       char *otherOrg, char *otherChromTable, char *otherDb)
 /* Put up cross-species alignment when the second species
  * sequence is in a nib file. */
 {
 struct psl *psl = NULL;
 char otherString[256];
 char *cgiItem = cgiEncode(item);
 char *thisOrg = hOrganism(database);
 
 cartWebStart(cart, database, "%s", tdb->longLabel);
 psl = loadPslFromRangePair(tdb->table, item);
 printf("<B>%s position:</B> %s:%d-%d<BR>\n", otherOrg,
        psl->qName, psl->qStart+1, psl->qEnd);
 printf("<B>%s size:</B> %d<BR>\n", otherOrg, psl->qEnd - psl->qStart);
 printf("<B>%s position:</B> %s:%d-%d<BR>\n", thisOrg,
        psl->tName, psl->tStart+1, psl->tEnd);
 printf("<B>%s size:</B> %d<BR>\n", thisOrg,
        psl->tEnd - psl->tStart);
 printf("<B>Identical Bases:</B> %d<BR>\n", psl->match + psl->repMatch);
 printf("<B>Number of Gapless Aligning Blocks:</B> %d<BR>\n", psl->blockCount );
 printf("<B>Percent identity within gapless aligning blocks:</B> %3.1f%%<BR>\n", 0.1*(1000 - pslCalcMilliBad(psl, FALSE)));
 printf("<B>Strand:</B> %s<BR>\n",psl->strand);
 printf("<B>Browser window position:</B> %s:%d-%d<BR>\n", seqName, winStart+1, winEnd);
 printf("<B>Browser window size:</B> %d<BR>\n", winEnd - winStart);
 safef(otherString, sizeof otherString, "%d&pslTable=%s&otherOrg=%s&otherChromTable=%s&otherDb=%s", psl->tStart,
 	tdb->table, otherOrg, otherChromTable, otherDb);
 
 printCustomUrl(tdb, item, TRUE);
 printTrackHtml(tdb);
 freez(&cgiItem);
 }
 
 void longXenoPsl1zoo2(struct trackDb *tdb, char *item,
                       char *otherOrg, char *otherChromTable)
 /* Put up cross-species alignment when the second species
  * sequence is in a nib file. */
 {
 struct psl *psl = NULL;
 char otherString[256];
 char anotherString[256];
 char *thisOrg = hOrganism(database);
 
 cartWebStart(cart, database, "%s", tdb->longLabel);
 psl = loadPslFromRangePair(tdb->table, item);
 printf("<B>%s position:</B> %s:%d-%d<BR>\n", otherOrg,
        psl->qName, psl->qStart+1, psl->qEnd);
 printf("<B>%s size:</B> %d<BR>\n", otherOrg, psl->qEnd - psl->qStart);
 printf("<B>%s position:</B> %s:%d-%d<BR>\n", thisOrg,
        psl->tName, psl->tStart+1, psl->tEnd);
 printf("<B>%s size:</B> %d<BR>\n", thisOrg,
        psl->tEnd - psl->tStart);
 printf("<B>Identical Bases:</B> %d<BR>\n", psl->match + psl->repMatch);
 printf("<B>Number of Gapless Aligning Blocks:</B> %d<BR>\n", psl->blockCount );
 printf("<B>Strand:</B> %s<BR>\n",psl->strand);
 printf("<B>Percent identity within gapless aligning blocks:</B> %3.1f%%<BR>\n", 0.1*(1000 - pslCalcMilliBad(psl, FALSE)));
 printf("<B>Browser window position:</B> %s:%d-%d<BR>\n", seqName, winStart, winEnd);
 printf("<B>Browser window size:</B> %d<BR>\n", winEnd - winStart);
 
 safef(anotherString, sizeof anotherString, "%s",otherOrg);
 toUpperN(anotherString,1);
 printf("Link to <a href=\"http://hgwdev-tcbruen.cse.ucsc.edu/cgi-bin/hgTracks?db=zoo%s1&position=chr1:%d-%d\">%s database</a><BR>\n",
        anotherString, psl->qStart, psl->qEnd, otherOrg);
 
 safef(otherString, sizeof otherString, "%d&pslTable=%s&otherOrg=%s&otherChromTable=%s", psl->tStart,
         tdb->table, otherOrg, otherChromTable);
 if (pslTrimToTargetRange(psl, winStart, winEnd) != NULL)
     {
     hgcAnchorSomewhere("htcLongXenoPsl2", item, otherString, psl->tName);
     printf("<BR>View details of parts of alignment within browser window</A>.<BR>\n");
     }
 printTrackHtml(tdb);
 }
 
 void doAlignmentOtherDb(struct trackDb *tdb, char *item)
 /* Put up cross-species alignment when the second species
  * is another db, indicated by the 3rd word of tdb->type. */
 {
 char *otherOrg;
 char *otherDb;
 char *words[8];
 char *typeLine = cloneString(tdb->type);
 int wordCount = chopLine(typeLine, words);
 if (wordCount < 3 || !(sameString(words[0], "psl") && sameString(words[1], "xeno")))
     errAbort("doAlignmentOtherDb: trackDb type must be \"psl xeno XXX\" where XXX is the name of the other database.");
 otherDb = words[2];
 otherOrg = hOrganism(otherDb);
 longXenoPsl1(tdb, item, otherOrg, "chromInfo", otherDb);
 }
 
 void doMultAlignZoo(struct trackDb *tdb, char *item, char *otherName )
 /* Put up cross-species alignment when the second species
  * sequence is in a nib file. */
 {
 char chromStr[64];
 
 /* Check to see if name is one of zoo names */
 if (!(strcmp(otherName,"human")
       && strcmp(otherName,"chimp")
       && strcmp(otherName,"baboon")
       && strcmp(otherName,"cow")
       && strcmp(otherName,"pig")
       && strcmp(otherName,"cat")
       && strcmp(otherName,"dog")
       && strcmp(otherName,"mouse")
       && strcmp(otherName,"rat")
       && strcmp(otherName,"chicken")
       && strcmp(otherName,"fugu")
       && strcmp(otherName,"tetra")
       && strcmp(otherName,"zebrafish")))
     {
     safef( chromStr, sizeof chromStr, "%sChrom" , otherName );
     longXenoPsl1zoo2(tdb, item, otherName, chromStr );
     }
 }
 
 struct chain *getChainFromRange(char *chainTable, char *chrom, int chromStart, int chromEnd)
 /* get a list of chains for a range */
 {
 char chainTable_chrom[256];
 struct dyString *dy = newDyString(128);
 struct chain *chainList = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 safef(chainTable_chrom, 256, "%s_%s",chrom, chainTable);
 
 
 if (hTableExists(database, chainTable_chrom) )
     {
     /* lookup chain if not stored */
     char **row;
     struct sqlResult *sr = NULL;
     sqlDyStringPrintf(dy, "select id, score, qStart, qEnd, qStrand, qSize from %s where ",
                    chainTable_chrom);
     hAddBinToQuery(chromStart, chromEnd, dy);
     dyStringPrintf(dy, "tEnd > %d and tStart < %d ", chromStart,chromEnd);
     dyStringAppend(dy, " order by qStart");
     sr = sqlGetResult(conn, dy->string);
 
     while ((row = sqlNextRow(sr)) != NULL)
         {
         int chainId = 0;
         unsigned int qStart, qEnd, qSize;
         struct chain *chain = NULL;
         char qStrand;
         chainId = sqlUnsigned(row[0]);
         qStart = sqlUnsigned(row[2]);
         qEnd = sqlUnsigned(row[3]);
         qStrand =row[4][0];
         qSize = sqlUnsigned(row[5]);
         if (qStrand == '-')
             {
             unsigned int tmp = qSize - qEnd;
             qEnd = qSize - qStart;
             qStart = tmp;
             }
         chain = NULL;
         if (chainId != 0)
             {
             chain = chainLoadIdRange(database, chainTable, chrom, chromStart, chromEnd, chainId);
             if (chain != NULL)
                 slAddHead(&chainList, chain);
             }
         }
     sqlFreeResult(&sr);
     }
 return chainList;
 }
 
 void htcPseudoGene(char *htcCommand, char *item)
 /* Interface for selecting & displaying alignments from axtInfo
  * for an item from a genePred table. */
 {
 struct genePred *gp = NULL;
 struct axtInfo *aiList = NULL;
 struct axt *axtList = NULL;
 struct sqlResult *sr;
 char **row;
 char trackTemp[256];
 char *track = cartString(cart, "o");
 char *chrom = cartString(cart, "c");
 char *name = cartOptionalString(cart, "i");
 char *db2 = cartString(cart, "db2");
 int tStart = cgiInt("l");
 int tEnd = cgiInt("r");
 char *qChrom = cgiOptionalString("qc");
 int chainId = cgiInt("ci");
 int qStart = cgiInt("qs");
 int qEnd = cgiInt("qe");
 char table[64];
 char query[512];
 char nibFile[512];
 char qNibFile[512];
 char qNibDir[512];
 char tNibDir[512];
 char path[512];
 boolean hasBin;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn2;
 struct hash *qChromHash = hashNew(0);
 struct cnFill *fill;
 struct chain *chain;
 struct dnaSeq *tChrom = NULL;
 
 cartWebStart(cart, database, "Alignment of %s in %s to pseudogene in %s",
 	     name, hOrganism(db2), hOrganism(database));
 conn2 = hAllocConn(db2);
 
 /* get nibFile for pseudoGene */
 sqlSafef(query, sizeof query, "select fileName from chromInfo where chrom = '%s'",  chrom);
 if (sqlQuickQuery(conn, query, nibFile, sizeof(nibFile)) == NULL)
     errAbort("Sequence %s isn't in chromInfo", chrom);
 
 /* get nibFile for Gene in other species */
 sqlSafef(query, sizeof query, "select fileName from chromInfo where chrom = '%s'" ,qChrom);
 if (sqlQuickQuery(conn2, query, qNibFile, sizeof(qNibFile)) == NULL)
     errAbort("Sequence chr1 isn't in chromInfo");
 
 /* get gp */
 if (!hFindSplitTable(db2, qChrom, track, table, &hasBin))
     errAbort("htcPseudoGene: table %s not found.\n",track);
 else if (sameString(track, "mrna"))
     {
     struct psl *psl = NULL ;
     sqlSafef(query, sizeof(query),
              "select * from %s where qName = '%s' and tName = '%s' and tStart = %d ",
              table, name, qChrom, qStart
              );
     sr = sqlGetResult(conn2, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         psl = pslLoad(row+hasBin);
         if (psl != NULL)
             gp = genePredFromPsl(psl, psl->tStart, psl->tEnd, 10);
         }
     sqlFreeResult(&sr);
     }
 else if (table != NULL)
     {
     sqlSafef(query, sizeof(query),
              "select * from %s where name = '%s' and chrom = '%s' ",
              table, name, qChrom
              );
     sr = sqlGetResult(conn2, query);
     if ((row = sqlNextRow(sr)) != NULL)
         gp = genePredLoad(row + hasBin);
     sqlFreeResult(&sr);
     }
 if (gp == NULL)
     errAbort("htcPseudoGene: Could not locate gene prediction (db=%s, table=%s, name=%s, in range %s:%d-%d) %s",
              db2, table, name, qChrom, qStart+1, qEnd, query);
 
 /* extract nib directory from nibfile */
 if (strrchr(nibFile,'/') != NULL)
     strncpy(tNibDir, nibFile, strlen(nibFile)-strlen(strrchr(nibFile,'/')));
 else
     errAbort("Cannot find nib directory for %s\n",nibFile);
 tNibDir[strlen(nibFile)-strlen(strrchr(nibFile,'/'))] = '\0';
 
 if (strrchr(qNibFile,'/') != NULL)
     strncpy(qNibDir, qNibFile, strlen(qNibFile)-strlen(strrchr(qNibFile,'/')));
 else
     errAbort("Cannot find nib directory for %s\n",qNibFile);
 qNibDir[strlen(qNibFile)-strlen(strrchr(qNibFile,'/'))] = '\0';
 
 safef(path, sizeof path, "%s/%s.nib", tNibDir, chrom);
 
 /* load chain */
 if (sameString(database,db2))
     {
     track = "selfChain";
     if (!hTableExists(database, "chr1_selfChain"))
         track = "chainSelf";
     }
 else
     {
     safef(trackTemp, sizeof trackTemp, "%sChain",hOrganism(db2));
     trackTemp[0] = tolower(trackTemp[0]);
     track = trackTemp;
     }
 if (chainId > 0 )
     {
     chain = chainDbLoad(conn, database, track, chrom, chainId);
 
     /* get list of axts for a chain */
     AllocVar(fill);
     fill->qName = cloneString(qChrom);
     fill->tSize = tEnd-tStart;
     fill->tStart = tStart;
     fill->chainId = chainId;
     fill->qSize = gp->txEnd - gp->txStart;
     fill->qStart = max(qStart, gp->txStart);
     fill->children = NULL;
     fill->next = NULL;
     fill->qStrand = chain->qStrand;
 
     tChrom = nibLoadPartMasked(NIB_MASK_MIXED, nibFile,
             fill->tStart, fill->tSize);
     axtList = netFillToAxt(fill, tChrom, hChromSize(database, chrom), qChromHash, qNibDir, chain, TRUE);
     /* make sure list is in correct order */
     if (axtList != NULL)
         if (axtList->next != NULL)
             if ((gp->strand[0] == '+' && axtList->tStart > axtList->next->tStart)
                 || (gp->strand[0] == '-' && axtList->tStart < axtList->next->tStart) )
                 slReverse(&axtList);
 
 
     /* fill in gaps between axt blocks */
     /* allows display of aligned coding regions */
     axtFillGap(&axtList,qNibDir, gp->strand[0]);
 
     if (gp->strand[0] == '-')
         axtListReverse(&axtList, database);
     if (axtList != NULL)
         if (axtList->next != NULL)
             if ((axtList->next->tStart < axtList->tStart && gp->strand[0] == '+') ||
                 (axtList->next->tStart > axtList->tStart && (gp->strand[0] == '-')))
                 slReverse(&axtList);
 
     /* output fancy formatted alignment */
     puts("<PRE><TT>");
     axtOneGeneOut(database, axtList, LINESIZE, stdout , gp, qNibFile);
     puts("</TT></PRE>");
     }
 
 axtInfoFreeList(&aiList);
 hFreeConn(&conn2);
 }
 
 void htcLongXenoPsl2(char *htcCommand, char *item)
 /* Display alignment - loading sequence from nib file. */
 {
 char *pslTable = cgiString("pslTable");
 char *otherOrg = cgiString("otherOrg");
 char *otherDb = cgiString("otherDb");
 struct psl *psl = loadPslFromRangePair(pslTable,  item);
 char *qChrom;
 char *ptr;
 char name[128];
 struct dnaSeq *qSeq = NULL;
 
 /* In hg10 tables, psl->qName can be org.chrom.  Strip it down to just
  * the chrom: */
 qChrom = psl->qName;
 if ((ptr = strchr(qChrom, '.')) != NULL)
     qChrom = ptr+1;
 
 /* Make sure that otherOrg's chrom size matches psl's qSize */
 if (hChromSize(database, qChrom) != psl->qSize)
     errAbort("Alignment's query size for %s is %d, but the size of %s in database %s is %d.  Incorrect database in trackDb.type?",
 	     qChrom, psl->qSize, qChrom, otherDb, hChromSize(otherDb, qChrom));
 
 psl = pslTrimToTargetRange(psl, winStart, winEnd);
 
 qSeq = loadGenomePart(otherDb, qChrom, psl->qStart, psl->qEnd);
 snprintf(name, sizeof(name), "%s.%s", otherOrg, qChrom);
 writeFramesetType();
 puts("<HTML>");
 printf("<HEAD>\n<TITLE>%s %dk</TITLE>\n</HEAD>\n\n", name, psl->qStart/1000);
 showSomeAlignment(psl, qSeq, gftDnaX, psl->qStart, psl->qEnd, name, 0, 0);
 }
 
 void doAlignCompGeno(struct trackDb *tdb, char *itemName, char *otherGenome)
     /* Handle click on blat or blastz track in a generic fashion */
     /* otherGenome is the text to display for genome name on details page */
 {
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 char *chrom = cartString(cart, "c");
 struct psl *pslList = NULL, *psl;
 boolean hasBin;
 char table[64];
 
 char *typeLine = cloneString(tdb->type);
 char *words[8];
 int wordCount = chopLine(typeLine, words);
 if (wordCount == 3)
     {
     if (sameString(words[0], "psl") &&
         sameString(words[1], "xeno"))
             {
             /* words[2] will contain other db */
             doAlignmentOtherDb(tdb, itemName);
             freeMem(typeLine);
             return;
             }
     }
 freeMem(typeLine);
 cartWebStart(cart, database, "%s", itemName);
 printPosOnChrom(chrom,start,end,NULL,FALSE,NULL);
 printf("<H1>Information on %s Sequence %s</H1>", otherGenome, itemName);
 
 printf("Get ");
 printf("<A HREF=\"%s&g=htcExtSeq&c=%s&l=%d&r=%d&i=%s\">",
                hgcPathAndSettings(), seqName, winStart, winEnd, itemName);
 printf("%s DNA</A><BR>\n", otherGenome);
 
 /* Get alignment info and print. */
 printf("<H2>Alignments</H2>\n");
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 
 /* if this is a non-split table then query with tName */
 if (startsWith(tdb->table, table))
     sqlSafef(query, sizeof(query), "select * from %s where qName = '%s' and tName = '%s'", table, itemName,seqName);
 else
     sqlSafef(query, sizeof(query), "select * from %s where qName = '%s'", table, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     psl = pslLoad(row+hasBin);
     slAddHead(&pslList, psl);
     }
 sqlFreeResult(&sr);
 slReverse(&pslList);
 printAlignments(pslList, start, "htcBlatXeno", tdb->table, itemName);
 printTrackHtml(tdb);
 }
 
 void doTSS(struct trackDb *tdb, char *itemName)
 /* Handle click on DBTSS track. */
 {
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row = NULL;
 int start = cartInt(cart, "o");
 struct psl *pslList = NULL, *psl = NULL;
 boolean hasBin = TRUE;
 char *table = "refFullAli"; /* Table with the pertinent PSL data */
 
 cartWebStart(cart, database, "%s", itemName);
 printf("<H1>Information on DBTSS Sequence %s</H1>", itemName);
 printf("Get ");
 printf("<A HREF=\"%s&g=htcExtSeq&c=%s&l=%d&r=%d&i=%s\">",
        hgcPathAndSettings(), seqName, winStart, winEnd, itemName);
 printf("Sequence</A><BR>\n");
 
 /* Get alignment info and print. */
 printf("<H2>Alignments</H2>\n");
 sqlSafef(query, sizeof query, "select * from %s where qName = '%s'", table, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     psl = pslLoad(row + hasBin);
     slAddHead(&pslList, psl);
     }
 
 sqlFreeResult(&sr);
 slReverse(&pslList);
 printAlignments(pslList, start, "htcCdnaAli", tdb->table, itemName);
 printTrackHtml(tdb);
 }
 
 void doEst3(char *itemName)
 /* Handle click on EST 3' end track. */
 {
 struct est3 el;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset;
 
 cartWebStart(cart, database, "EST 3' Ends");
 printf("<H2>EST 3' Ends</H2>\n");
 
 rowOffset = hOffsetPastBin(database, seqName, "est3");
 sqlSafef(query, sizeof query, "select * from est3 where chrom = '%s' and chromStart = %d",
 	seqName, start);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     est3StaticLoad(row+rowOffset, &el);
     printf("<B>EST 3' End Count:</B> %d<BR>\n", el.estCount);
     bedPrintPos((struct bed *)&el, 3, NULL);
     printf("<B>strand:</B> %s<BR>\n", el.strand);
     htmlHorizontalLine();
     }
 
 puts("<P>This track shows where clusters of EST 3' ends hit the "
      "genome.  In many cases these represent the 3' ends of genes. "
      "This data was kindly provided by Lukas Wagner and Greg Schuler "
      "at NCBI.  Additional filtering was applied by Jim Kent.</P>");
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doEncodeRna(struct trackDb *tdb, char *itemName)
 /* Handle click on encodeRna track. */
 {
 struct encodeRna rna;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset;
 struct slName *nameList, *sl;
 
 genericHeader(tdb, itemName);
 rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 sqlSafef(query, sizeof query, "select * from %s where chrom = '%s' and chromStart = %d and name = '%s'",
       tdb->table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     encodeRnaStaticLoad(row + rowOffset, &rna);
     printf("<B>name:</B> %s<BR>\n", rna.name);
     bedPrintPos((struct bed *)&rna, 3, tdb);
     printf("<B>strand:</B> %s<BR>\n", rna.strand);
     printf("<B>type:</B> %s<BR>\n", rna.type);
     printf("<B>score:</B> %2.1f<BR><BR>\n", rna.fullScore);
     printf("<B>is pseudo-gene:</B> %s<BR>\n", (rna.isPsuedo ? "yes" : "no"));
     printf("<B>is Repeatmasked:</B> %s<BR>\n", (rna.isRmasked ? "yes" : "no"));
     printf("<B>is Transcribed:</B> %s<BR>\n", (rna.isTranscribed ? "yes" : "no"));
     printf("<B>is an evoFold prediction:</B> %s<BR>\n", (rna.isPrediction ? "yes" : "no"));
     printf("<B>program predicted with:</B> %s<BR>\n", rna.source);
     printf("<BR><B>This region is transcribed in: </B>");
     nameList = slNameListFromString(rna.transcribedIn,',');
     if(nameList==NULL||sameString(nameList->name,"."))
       printf("<BR>&nbsp;&nbsp;&nbsp;&nbsp;Not transcribed\n");
     else
       for (sl=nameList;sl!=NULL;sl=sl->next)
           printf("<BR>&nbsp;&nbsp;&nbsp;&nbsp;%s\n",sl->name);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 
 void doRnaGene(struct trackDb *tdb, char *itemName)
 /* Handle click on RNA Genes track. */
 {
 struct rnaGene rna;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset;
 
 genericHeader(tdb, itemName);
 rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 sqlSafef(query, sizeof query, "select * from %s where chrom = '%s' and chromStart = %d and name = '%s'",
 	tdb->table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     rnaGeneStaticLoad(row + rowOffset, &rna);
     printf("<B>name:</B> %s<BR>\n", rna.name);
     printf("<B>type:</B> %s<BR>\n", rna.type);
     printf("<B>score:</B> %2.1f<BR>\n", rna.fullScore);
     printf("<B>is pseudo-gene:</B> %s<BR>\n", (rna.isPsuedo ? "yes" : "no"));
     printf("<B>program predicted with:</B> %s<BR>\n", rna.source);
     printf("<B>strand:</B> %s<BR>\n", rna.strand);
     bedPrintPos((struct bed *)&rna, 3, tdb);
     htmlHorizontalLine();
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doStsMarker(struct trackDb *tdb, char *marker)
 /* Respond to click on an STS marker. */
 {
 char *table = tdb->table;
 char query[256];
 char title[256];
 struct sqlConnection *conn = hAllocConn(database);
 boolean stsInfo2Exists = sqlTableExists(conn, "stsInfo2");
 boolean stsInfoExists = sqlTableExists(conn, "stsInfo");
 boolean stsMapExists = sqlTableExists(conn, "stsMap");
 struct sqlConnection *conn1 = hAllocConn(database);
 struct sqlResult *sr = NULL, *sr1 = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 struct stsMap stsRow;
 struct stsInfo *infoRow = NULL;
 struct stsInfo2 *info2Row = NULL;
 char stsid[20];
 int i;
 struct psl *pslList = NULL, *psl;
 int pslStart;
 char *sqlMarker = marker;
 boolean hasBin;
 
 /* Make sure to escpae single quotes for DB parseability */
 if (strchr(marker, '\''))
     sqlMarker = replaceChars(marker, "'", "''");
 
 /* Print out non-sequence info */
 safef(title, sizeof title, "STS Marker %s", marker);
 cartWebStart(cart, database, "%s", title);
 
 /* Find the instance of the object in the bed table */
 sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE name = '%s' "
                "AND chrom = '%s' AND chromStart = %d "
                "AND chromEnd = %d",
         table, sqlMarker, seqName, start, end);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 hasBin = hOffsetPastBin(database, seqName, table);
 if (row != NULL)
     {
     if (stsMapExists)
         stsMapStaticLoad(row+hasBin, &stsRow);
     else
         /* Load and convert from original bed format */
         {
         struct stsMarker oldStsRow;
         stsMarkerStaticLoad(row+hasBin, &oldStsRow);
 	stsMapFromStsMarker(&oldStsRow, &stsRow);
 	}
     if (stsInfo2Exists)
         {
         /* Find the instance of the object in the stsInfo2 table */
 	sqlFreeResult(&sr);
 	sqlSafef(query, sizeof query, "SELECT * FROM stsInfo2 WHERE identNo = '%d'", stsRow.identNo);
 	sr = sqlMustGetResult(conn, query);
 	row = sqlNextRow(sr);
 	if (row != NULL)
 	    {
             int i;
 	    char **cl;
 	    cl = (char **)needMem(52*sizeof(char *));
 	    for (i = 0; i < 52; ++i)
 		cl[i] = cloneString(row[i]);
 	    info2Row = stsInfo2Load(row);
 	    infoRow = stsInfoLoad(cl);
 	    freeMem(cl);
 	    }
 	}
     else if (stsInfoExists)
         {
         /* Find the instance of the object in the stsInfo table */
 	sqlFreeResult(&sr);
 	sqlSafef(query, sizeof query, "SELECT * FROM stsInfo WHERE identNo = '%d'", stsRow.identNo);
 	sr = sqlMustGetResult(conn, query);
 	row = sqlNextRow(sr);
 	if (row != NULL)
 	    infoRow = stsInfoLoad(row);
 	}
     if (((stsInfo2Exists) || (stsInfoExists)) && (row != NULL))
 	{
 	printf("<TABLE>\n");
 	printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n", seqName);
 	printf("<TR><TH ALIGN=left>Start:</TH><TD>%d</TD></TR>\n",start+1);
 	printf("<TR><TH ALIGN=left>End:</TH><TD>%d</TD></TR>\n",end);
 	printBand(seqName, start, end, TRUE);
 	printf("</TABLE>\n");
 	htmlHorizontalLine();
 
 	/* Print out marker name and links to UniSTS, Genebank, GDB */
 	if (infoRow->nameCount > 0)
 	    {
 	    printf("<TABLE>\n");
 	    printf("<TR><TH>Other names:</TH><TD>%s",infoRow->otherNames[0]);
             for (i = 1; i < infoRow->nameCount; i++)
 		printf(", %s",infoRow->otherNames[i]);
 	    printf("</TD></TR>\n</TABLE>\n");
 	    htmlHorizontalLine();
 	    }
 	printf("<TABLE>\n");
 	printf("<TR><TH ALIGN=left>UCSC STS id:</TH><TD>%d</TD></TR>\n", stsRow.identNo);
 	printf("<TR><TH ALIGN=left>UniSTS id:</TH><TD><A HREF=");
 	printUnistsUrl(stdout, infoRow->dbSTSid);
 	printf(" TARGET=_BLANK>%d</A></TD></TR>\n", infoRow->dbSTSid);
         if (infoRow->otherDbstsCount > 0)
 	    {
 	    printf("<TR><TH ALIGN=left>Related UniSTS ids:</TH>");
             for (i = 0; i < infoRow->otherDbstsCount; i++)
 		{
 		printf("<TD><A HREF=");
 		printUnistsUrl(stdout, infoRow->otherDbSTS[i]);
 		printf(" TARGET=_BLANK>%d</A></TD>", infoRow->otherDbSTS[i]);
 		}
 	    printf("</TR>\n");
             }
         if (infoRow->gbCount > 0)
 	    {
 	    printf("<TR><TH ALIGN=left>Genbank:</TH>");
             for (i = 0; i < infoRow->gbCount; i++)
 		{
 		printf("<TD><A HREF=\"");
 		printEntrezNucleotideUrl(stdout, infoRow->genbank[i]);
 		printf("\" TARGET=_BLANK>%s</A></TD>", infoRow->genbank[i]);
 		}
 	    printf("</TR>\n");
             }
         if (infoRow->gdbCount > 0)
 	    {
 	    printf("<TR><TH ALIGN=left>GDB:</TH>");
             for (i = 0; i < infoRow->gdbCount; i++)
 		{
 		printf("<TD>");
 		printf("%s</TD>", infoRow->gdb[i]);
 		}
 	    printf("</TR>\n");
             }
 	printf("<TR><TH ALIGN=left>Organism:</TH><TD>%s</TD></TR>\n",infoRow->organism);
 	printf("</TABLE>\n");
 	htmlHorizontalLine();
 	/* Print out primer information */
 	if (!sameString(infoRow->leftPrimer,""))
 	    {
 	    printf("<TABLE>\n");
 	    printf("<TR><TH ALIGN=left>Left Primer:</TH><TD>%s</TD></TR>\n",infoRow->leftPrimer);
 	    printf("<TR><TH ALIGN=left>Right Primer:</TH><TD>%s</TD></TR>\n",infoRow->rightPrimer);
 	    printf("<TR><TH ALIGN=left>Distance:</TH><TD>%s bps</TD></TR>\n",infoRow->distance);
 	    printf("</TABLE>\n");
 	    htmlHorizontalLine();
 	    }
 	/* Print out information from STS maps for this marker */
         if ((!sameString(infoRow->genethonName,""))
 	    || (!sameString(infoRow->marshfieldName,""))
 	    || (stsInfo2Exists && info2Row != NULL && (!sameString(info2Row->decodeName,""))))
 	    {
             printf("<H3>Genetic Map Positions</H3>\n");
 	    printf("<TABLE>\n");
 	    printf("<TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position</TH></TR>\n");
             if (!sameString(infoRow->genethonName,""))
 		printf("<TH ALIGN=left>Genethon:</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD></TR>\n",
 		       infoRow->genethonName, infoRow->genethonChr, infoRow->genethonPos);
 	    if (!sameString(infoRow->marshfieldName,""))
 		printf("<TH ALIGN=left>Marshfield:</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD></TR>\n",
 		       infoRow->marshfieldName, infoRow->marshfieldChr,
 		       infoRow->marshfieldPos);
 	    if ((stsInfo2Exists) && (!sameString(info2Row->decodeName,"")))
 		printf("<TH ALIGN=left>deCODE:</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD></TR>\n",
 		       info2Row->decodeName, info2Row->decodeChr,
 		       info2Row->decodePos);
 	    printf("</TABLE><P>\n");
 	    }
         if (!sameString(infoRow->wiyacName,""))
 	    {
             printf("<H3>Whitehead YAC Map Position</H3>\n");
 	    printf("<TABLE>\n");
 	    printf("<TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position</TH></TR>\n");
 	    printf("<TH ALIGN=left>WI YAC:</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD></TR>\n",
 		   infoRow->wiyacName, infoRow->wiyacChr, infoRow->wiyacPos);
 	    printf("</TABLE><P>\n");
 	    }
         if ((!sameString(infoRow->wirhName,""))
 	    || (!sameString(infoRow->gm99gb4Name,""))
 	    || (!sameString(infoRow->gm99g3Name,""))
 	    || (!sameString(infoRow->tngName,"")))
 	    {
             printf("<H3>RH Map Positions</H3>\n");
 	    printf("<TABLE>\n");
             if ((!sameString(infoRow->wirhName,""))
 		|| (!sameString(infoRow->gm99gb4Name,""))
 		|| (!sameString(infoRow->gm99g3Name,"")))
 		printf("<TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position (LOD)</TH></TR>\n");
 	    else
 		printf("<TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position</TH></TR>\n");
 	    if (!sameString(infoRow->gm99gb4Name,""))
 		printf("<TH ALIGN=left>GM99 Gb4:</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f (%.2f)</TD></TR>\n",
 		       infoRow->gm99gb4Name, infoRow->gm99gb4Chr, infoRow->gm99gb4Pos,
 		       infoRow->gm99gb4LOD);
 	    if (!sameString(infoRow->gm99g3Name,""))
 		printf("<TH ALIGN=left>GM99 G3:</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f (%.2f)</TD></TR>\n",
 		       infoRow->gm99g3Name, infoRow->gm99g3Chr, infoRow->gm99g3Pos,
 		       infoRow->gm99g3LOD);
 	    if (!sameString(infoRow->wirhName,""))
 		printf("<TH ALIGN=left>WI RH:</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f (%.2f)</TD></TR>\n",
 		       infoRow->wirhName, infoRow->wirhChr, infoRow->wirhPos,
 		       infoRow->wirhLOD);
 	    if (!sameString(infoRow->tngName,""))
 		printf("<TH ALIGN=left>Stanford TNG:</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD></TR>\n",
 		       infoRow->tngName, infoRow->tngChr, infoRow->tngPos);
 	    printf("</TABLE><P>\n");
 	    }
 	/* Print out alignment information - full sequence */
 	webNewSection("Genomic Alignments:");
         sqlSafef(query, sizeof query, "SELECT * FROM all_sts_seq WHERE qName = '%d'",
                 infoRow->identNo);
 	sr1 = sqlGetResult(conn1, query);
 	hasBin = hOffsetPastBin(database, seqName, "all_sts_seq");
 	i = 0;
 	pslStart = 0;
 	while ((row = sqlNextRow(sr1)) != NULL)
             {
 	    psl = pslLoad(row+hasBin);
 	    if ((sameString(psl->tName, seqName)) && (abs(psl->tStart - start) < 1000))
 		pslStart = psl->tStart;
 	    slAddHead(&pslList, psl);
 	    i++;
 	    }
 	slReverse(&pslList);
         if (i > 0)
 	    {
 	    printf("<H3>Full sequence:</H3>\n");
 	    safef(stsid, sizeof stsid, "%d", infoRow->identNo);
 	    printAlignments(pslList, pslStart, "htcCdnaAli", "all_sts_seq", stsid);
 	    sqlFreeResult(&sr1);
 	    htmlHorizontalLine();
 	    }
 	slFreeList(&pslList);
 	/* Print out alignment information - primers */
 	safef(stsid, sizeof stsid, "dbSTS_%d", infoRow->dbSTSid);
         sqlSafef(query, sizeof query, "SELECT * FROM all_sts_primer WHERE qName = '%s'",
                 stsid);
 	hasBin = hOffsetPastBin(database, seqName, "all_sts_primer");
 	sr1 = sqlGetResult(conn1, query);
 	i = 0;
 	pslStart = 0;
 	while ((row = sqlNextRow(sr1)) != NULL)
             {
 	    psl = pslLoad(row+hasBin);
 	    if ((sameString(psl->tName, seqName)) && (abs(psl->tStart - start) < 1000))
 		pslStart = psl->tStart;
 	    slAddHead(&pslList, psl);
 	    i++;
 	    }
 	slReverse(&pslList);
         if (i > 0)
 	    {
 	    printf("<H3>Primers:</H3>\n");
 	    printAlignments(pslList, pslStart, "htcCdnaAli", "all_sts_primer", stsid);
 	    sqlFreeResult(&sr1);
 	    }
 	slFreeList(&pslList);
 	stsInfoFree(&infoRow);
 	}
     else
 	{
 	printf("<TABLE>\n");
 	printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n", seqName);
 	printf("<TR><TH ALIGN=left>Position:</TH><TD>%d</TD></TR>\n", (stsRow.chromStart+stsRow.chromEnd)>>1);
 	printf("<TR><TH ALIGN=left>UCSC STS id:</TH><TD>%d</TD></TR>\n", stsRow.identNo);
 	if (!sameString(stsRow.ctgAcc, "-"))
 	    printf("<TR><TH ALIGN=left>Clone placed on:</TH><TD>%s</TD></TR>\n", stsRow.ctgAcc);
 	if (!sameString(stsRow.otherAcc, "-"))
 	    printf("<TR><TH ALIGN=left>Other clones hit:</TH><TD>%s</TD></TR>\n", stsRow.otherAcc);
 	if (!sameString(stsRow.genethonChrom, "0"))
 	    printf("<TR><TH ALIGN=left>Genethon:</TH><TD>chr%s</TD><TD>%.2f</TD></TR>\n", stsRow.genethonChrom, stsRow.genethonPos);
 	if (!sameString(stsRow.marshfieldChrom, "0"))
 	    printf("<TR><TH ALIGN=left>Marshfield:</TH><TD>chr%s</TD><TD>%.2f</TD></TR>\n", stsRow.marshfieldChrom, stsRow.marshfieldPos);
 	if (!sameString(stsRow.gm99Gb4Chrom, "0"))
 	    printf("<TR><TH ALIGN=left>GeneMap99 GB4:</TH><TD>chr%s</TD><TD>%.2f</TD></TR>\n", stsRow.gm99Gb4Chrom, stsRow.gm99Gb4Pos);
 	if (!sameString(stsRow.shgcG3Chrom, "0"))
 	    printf("<TR><TH ALIGN=left>GeneMap99 G3:</TH><TD>chr%s</TD><TD>%.2f</TD></TR>\n", stsRow.shgcG3Chrom, stsRow.shgcG3Pos);
 	if (!sameString(stsRow.wiYacChrom, "0"))
 	    printf("<TR><TH ALIGN=left>Whitehead YAC:</TH><TD>chr%s</TD><TD>%.2f</TD></TR>\n", stsRow.wiYacChrom, stsRow.wiYacPos);
 	if (!sameString(stsRow.wiRhChrom, "0"))
 	    printf("<TR><TH ALIGN=left>Whitehead RH:</TH><TD>chr%s</TD><TD>%.2f</TD></TR>\n", stsRow.wiRhChrom, stsRow.wiRhPos);
 	if (!sameString(stsRow.shgcTngChrom, "0"))
 	    printf("<TR><TH ALIGN=left>Stanford TNG:</TH><TD>chr%s</TD><TD>%.2f</TD></TR>\n", stsRow.shgcTngChrom, stsRow.shgcTngPos);
 	if (!sameString(stsRow.fishChrom, "0"))
             printf("<TR><TH ALIGN=left>FISH:</TH><TD>%s.%s - %s.%s</TD></TR>\n", stsRow.fishChrom,
 		   stsRow.beginBand, stsRow.fishChrom, stsRow.endBand);
 	printf("</TABLE>\n");
 	htmlHorizontalLine();
 	if (stsRow.score == 1000)
 	    printf("<H3>This is the only location found for %s</H3>\n",marker);
 	else
 	    {
 	    sqlFreeResult(&sr);
 	    printf("<H4>Other locations found for %s in the genome:</H4>\n", marker);
 	    printf("<TABLE>\n");
 	    sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE name = '%s' "
                            "AND (chrom != '%s' OR chromStart != %d OR chromEnd != %d)",
                     table, marker, seqName, start, end);
 	    sr = sqlGetResult(conn,query);
 	    hasBin = hOffsetPastBin(database, seqName, table);
 	    while ((row = sqlNextRow(sr)) != NULL)
 		{
                 if (stsMapExists)
                     stsMapStaticLoad(row+hasBin, &stsRow);
                 else
                     /* Load and convert from original bed format */
                     {
                     struct stsMarker oldStsRow;
                     stsMarkerStaticLoad(row+hasBin, &oldStsRow);
 		    stsMapFromStsMarker(&oldStsRow, &stsRow);
 		    }
 		printf("<TR><TD>%s:</TD><TD>%d</TD></TR>\n",
 		       stsRow.chrom, (stsRow.chromStart+stsRow.chromEnd)>>1);
 		}
             printf("</TABLE>\n");
 	    }
 	htmlHorizontalLine();
 	}
     }
 webNewSection("Notes:");
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 hFreeConn(&conn1);
 }
 
 void doStsMapMouse(struct trackDb *tdb, char *marker)
 /* Respond to click on an STS marker. */
 {
 char *table = tdb->table;
 char title[256];
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn1 = hAllocConn(database);
 struct sqlResult *sr = NULL, *sr1 = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 char *hgsid = cartSessionId(cart);
 struct stsMapMouse stsRow;
 struct stsInfoMouse *infoRow;
 char stsid[20];
 int i;
 struct psl *pslList = NULL, *psl;
 int pslStart;
 
 /* Print out non-sequence info */
 safef(title, sizeof title, "STS Marker %s", marker);
 cartWebStart(cart, database, "%s", title);
 
 /* Find the instance of the object in the bed table */
 sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE name = '%s' "
                "AND chrom = '%s' AND chromStart = %d "
                "AND chromEnd = %d",
         table, marker, seqName, start, end);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     stsMapMouseStaticLoad(row, &stsRow);
     /* Find the instance of the object in the stsInfo table */
     sqlFreeResult(&sr);
     sqlSafef(query, sizeof query, "SELECT * FROM stsInfoMouse WHERE identNo = '%d'", stsRow.identNo);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
 	{
 	infoRow = stsInfoMouseLoad(row);
 	printf("<TABLE>\n");
 	printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n", seqName);
 	printf("<TR><TH ALIGN=left>Start:</TH><TD>%d</TD></TR>\n",start+1);
 	printf("<TR><TH ALIGN=left>End:</TH><TD>%d</TD></TR>\n",end);
 	printf("</TABLE>\n");
 	htmlHorizontalLine();
 	printf("<TABLE>\n");
         printf("<TR><TH ALIGN=left>MGI Marker ID:</TH><TD><B>MGI:</B>");
 	printf("<A HREF = \"http://www.informatics.jax.org/searches/accession_report.cgi?id=MGI:%d\" TARGET=_blank>%d</A></TD></TR>\n", infoRow->MGIMarkerID, infoRow->MGIMarkerID);
         printf("<TR><TH ALIGN=left>MGI Probe ID:</TH><TD><B>MGI:</B>");
 	printf("<A HREF = \"http://www.informatics.jax.org/searches/accession_report.cgi?id=MGI:%d\" TARGET=_blank>%d</A></TD></TR>\n", infoRow->MGIPrimerID, infoRow->MGIPrimerID);
 	printf("</TABLE>\n");
 	htmlHorizontalLine();
 	/* Print out primer information */
 	printf("<TABLE>\n");
 	printf("<TR><TH ALIGN=left>Left Primer:</TH><TD>%s</TD></TR>\n",infoRow->primer1);
 	printf("<TR><TH ALIGN=left>Right Primer:</TH><TD>%s</TD></TR>\n",infoRow->primer2);
 	printf("<TR><TH ALIGN=left>Distance:</TH><TD>%s bps</TD></TR>\n",infoRow->distance);
 	printf("</TABLE>\n");
 	htmlHorizontalLine();
 	/* Print out information from genetic maps for this marker */
         printf("<H3>Genetic Map Position</H3>\n");
 	printf("<TABLE>\n");
 	printf("<TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position</TH></TR>\n");
         printf("<TH ALIGN=left>&nbsp</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD></TR>\n",
                infoRow->stsMarkerName, infoRow->Chr, infoRow->geneticPos);
         printf("</TABLE><P>\n");
 
         /* Print out alignment information - full sequence */
         webNewSection("Genomic Alignments:");
         safef(stsid, sizeof stsid, "%d", infoRow->MGIPrimerID);
         sqlSafef(query, sizeof query, "SELECT * FROM all_sts_primer"
                        " WHERE  qName = '%s' AND  tStart = '%d' AND tEnd = '%d'",stsid, start, end);
         sr1 = sqlGetResult(conn1, query);
         i = 0;
         pslStart = 0;
 	while ((row = sqlNextRow(sr1)) != NULL)
             {
 	    psl = pslLoad(row);
 	    if ((sameString(psl->tName, seqName)) && (abs(psl->tStart - start) < 1000))
 		pslStart = psl->tStart;
 	    slAddHead(&pslList, psl);
 	    i++;
 	    }
 	slReverse(&pslList);
         if (i > 0)
 	    {
 	    printf("<H3>Primers:</H3>\n");
 	    printAlignments(pslList, pslStart, "htcCdnaAli", "all_sts_primer", stsid);
 	    sqlFreeResult(&sr1);
 	    }
 	slFreeList(&pslList);
 	stsInfoMouseFree(&infoRow);
 	}
     htmlHorizontalLine();
 
     if (stsRow.score == 1000)
 	printf("<H3>This is the only location found for %s</H3>\n",marker);
     else
 	{
 	sqlFreeResult(&sr);
 	printf("<H4>Other locations found for %s in the genome:</H4>\n", marker);
 	printf("<TABLE>\n");
 	sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE name = '%s' "
                        "AND (chrom != '%s' OR chromStart != %d OR chromEnd != %d)",
                 table, marker, seqName, start, end);
 	sr = sqlGetResult(conn,query);
 	while ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    stsMapMouseStaticLoad(row, &stsRow);
 	    printf("<TR><TD>%s:</TD><TD><A HREF = \"../cgi-bin/hgc?hgsid=%s&o=%u&t=%d&g=stsMapMouse&i=%s&c=%s\" target=_blank>%d</A></TD></TR>\n",
 		   stsRow.chrom, hgsid, stsRow.chromStart,stsRow.chromEnd, stsRow.name, stsRow.chrom,(stsRow.chromStart+stsRow.chromEnd)>>1);
 	    }
 	printf("</TABLE>\n");
 	}
     }
 webNewSection("Notes:");
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 hFreeConn(&conn1);
 }
 
 
 
 void doStsMapMouseNew(struct trackDb *tdb, char *marker)
 /* Respond to click on an STS marker. */
 {
 char *table = tdb->table;
 char title[256];
 char query[256];
 char query1[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn1 = hAllocConn(database);
 struct sqlResult *sr = NULL, *sr1 = NULL, *sr2 = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 char *hgsid = cartSessionId(cart);
 struct stsMapMouseNew stsRow;
 struct stsInfoMouseNew *infoRow;
 char stsid[20];
 char stsPrimer[40];
 char stsClone[45];
 int i;
 struct psl *pslList = NULL, *psl;
 int pslStart;
  char sChar='%';
 
 /* Print out non-sequence info */
 
 safef(title, sizeof title, "STS Marker %s\n", marker);
 /* safef(title, sizeof title, "STS Marker <A HREF=\"http://www.informatics.jax.org/searches/marker_report.cgi?string\%%3AmousemarkerID=%s\" TARGET=_BLANK>%s</A>\n", marker, marker); */
 cartWebStart(cart, database, "%s", title);
 
 /* Find the instance of the object in the bed table */
 sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE name = '%s' "
                 "AND chrom = '%s' AND chromStart = %d "
                 "AND chromEnd = %d",
 	        table, marker, seqName, start, end);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     stsMapMouseNewStaticLoad(row, &stsRow);
     /* Find the instance of the object in the stsInfo table */
     sqlFreeResult(&sr);
     sqlSafef(query, sizeof query, "SELECT * FROM stsInfoMouseNew WHERE identNo = '%d'", stsRow.identNo);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
 	{
 	infoRow = stsInfoMouseNewLoad(row);
 	printf("<TABLE>\n");
 	printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n", seqName);
 	printf("<TR><TH ALIGN=left>Start:</TH><TD>%d</TD></TR>\n",start+1);
 	printf("<TR><TH ALIGN=left>End:</TH><TD>%d</TD></TR>\n",end);
 	printf("</TABLE>\n");
         htmlHorizontalLine();
         printf("<TABLE>\n");
         printf("<TR><TH ALIGN=left>UCSC STS Marker ID:</TH><TD>%d</TD></TR>\n", infoRow->identNo);
         if (infoRow->UiStsId != 0)
             printf("<TR><TH ALIGN=left>UniSts Marker ID:</TH><TD>"
                    "<A HREF=\"http://www.ncbi.nlm.nih.gov/genome/sts/sts.cgi?uid=%d\" "
                    "TARGET=_BLANK>%d</A></TD></TR>\n", infoRow->UiStsId, infoRow->UiStsId);
         if (infoRow->MGIId != 0)
             printf("<TR><TH ALIGN=left>MGI Marker ID:</TH><TD><B>"
                    "<A HREF=\"http://www.informatics.jax.org/searches/marker_report.cgi?"
                    "accID=MGI%c3A%d\" TARGET=_BLANK>%d</A></TD></TR>\n",
                    sChar,infoRow->MGIId,infoRow->MGIId );
         if (strcmp(infoRow->MGIName, ""))
             printf("<TR><TH ALIGN=left>MGI Marker Name:</TH><TD>%s</TD></TR>\n", infoRow->MGIName);
         printf("</TABLE>\n");
         htmlHorizontalLine();
 	/* Print out primer information */
 	printf("<TABLE>\n");
 	printf("<TR><TH ALIGN=left>Left Primer:</TH><TD>%s</TD></TR>\n",infoRow->primer1);
 	printf("<TR><TH ALIGN=left>Right Primer:</TH><TD>%s</TD></TR>\n",infoRow->primer2);
 	printf("<TR><TH ALIGN=left>Distance:</TH><TD>%s bps</TD></TR>\n",infoRow->distance);
 	printf("</TABLE>\n");
 	htmlHorizontalLine();
 	/* Print out information from genetic maps for this marker */
 	if(strcmp(infoRow->wigName, "") || strcmp(infoRow->mgiName, "") || strcmp(infoRow->rhName, ""))
 	    printf("<H3>Map Position</H3>\n<TABLE>\n");
 	if(strcmp(infoRow->wigName, ""))
             {
             printf("<TR><TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position</TH></TR>\n");
             printf("<TR><TH ALIGN=left>&nbsp</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD></TR>\n",
                    infoRow->wigName, infoRow->wigChr, infoRow->wigGeneticPos);
             }
         if (strcmp(infoRow->mgiName, ""))
             {
             printf("<TR><TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position</TH></TR>\n");
             printf("<TR><TH ALIGN=left>&nbsp</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD></TR>\n",
                    infoRow->mgiName, infoRow->mgiChr, infoRow->mgiGeneticPos);
             }
         if (strcmp(infoRow->rhName, ""))
             {
             printf("<TR><TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position</TH><TH ALIGN=left WIDTH=150>Score</TH?</TR>\n");
             printf("<TR><TH ALIGN=left>&nbsp</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD><TD WIDTH=150>%.2f</TD></TR>\n",
                    infoRow->rhName, infoRow->rhChr, infoRow->rhGeneticPos, infoRow->RHLOD);
             }
         printf("</TABLE><P>\n");
 
         /* Print out alignment information - full sequence */
         webNewSection("Genomic Alignments:");
         safef(stsid, sizeof stsid, "%d", infoRow->identNo);
 	safef(stsPrimer, sizeof stsPrimer, "%d_%s", infoRow->identNo, infoRow->name);
         safef(stsClone, sizeof stsClone, "%d_%s_clone", infoRow->identNo, infoRow->name);
 
         /* find sts in primer alignment info */
         sqlSafef(query, sizeof query, "SELECT * FROM all_sts_primer WHERE  qName = '%s' AND  tStart = '%d' "
                 "AND tEnd = '%d'",stsPrimer, start, end);
         sr1 = sqlGetResult(conn1, query);
         i = 0;
         pslStart = 0;
         while ((row = sqlNextRow(sr1)) != NULL )
             {
             psl = pslLoad(row);
             fflush(stdout);
             if ((sameString(psl->tName, seqName)) && (abs(psl->tStart - start) < 1000))
 		pslStart = psl->tStart;
 	    slAddHead(&pslList, psl);
 	    i++;
 	  }
 	slReverse(&pslList);
         if (i > 0)
 	  {
 	    printf("<H3>Primers:</H3>\n");
 	    printAlignments(pslList, pslStart, "htcCdnaAli", "all_sts_primer", stsPrimer);
 	    sqlFreeResult(&sr1);
 	  }
 	slFreeList(&pslList);
 	stsInfoMouseNewFree(&infoRow);
 
 	/* Find sts in clone sequece alignment info */
         sqlSafef(query1, sizeof query1, "SELECT * FROM all_sts_primer WHERE  qName = '%s' AND  tStart = '%d' AND tEnd = '%d'",stsClone, start, end);
 	sr2 = sqlGetResult(conn1, query1);
         i = 0;
         pslStart = 0;
         while ((row = sqlNextRow(sr2)) != NULL )
             {
             psl = pslLoad(row);
             fflush(stdout);
             if ((sameString(psl->tName, seqName)) && (abs(psl->tStart - start) < 1000))
 		pslStart = psl->tStart;
 	    slAddHead(&pslList, psl);
 	    i++;
 	  }
 	slReverse(&pslList);
         if (i > 0)
 	  {
 	    printf("<H3>Clone:</H3>\n");
 	    printAlignments(pslList, pslStart, "htcCdnaAli", "all_sts_primer", stsClone);
 	    sqlFreeResult(&sr1);
 	  }
 	slFreeList(&pslList);
 	stsInfoMouseNewFree(&infoRow);
 	}
 
 	htmlHorizontalLine();
 
 	if (stsRow.score == 1000)
 	    printf("<H3>This is the only location found for %s</H3>\n",marker);
         else
 	    {
             sqlFreeResult(&sr);
             printf("<H4>Other locations found for %s in the genome:</H4>\n", marker);
             printf("<TABLE>\n");
             sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE name = '%s' "
                            "AND (chrom != '%s' OR chromStart != %d OR chromEnd != %d)",
                            table, marker, seqName, start, end);
             sr = sqlGetResult(conn,query);
             while ((row = sqlNextRow(sr)) != NULL)
                 {
                 stsMapMouseNewStaticLoad(row, &stsRow);
                 printf("<TR><TD>%s:</TD><TD><A HREF = \"../cgi-bin/hgc?hgsid=%s&o=%u&t=%d&"
                        "g=stsMapMouseNew&i=%s&c=%s\" target=_blank>%d</A></TD></TR>\n",
                        stsRow.chrom, hgsid, stsRow.chromStart,stsRow.chromEnd, stsRow.name,
                        stsRow.chrom,(stsRow.chromStart+stsRow.chromEnd)>>1);
 		}
 	    printf("</TABLE>\n");
 	    }
     }
 webNewSection("Notes:");
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 hFreeConn(&conn1);
 }
 
 
 void doStsMapRat(struct trackDb *tdb, char *marker)
 /* Respond to click on an STS marker. */
 {
 char *table = tdb->table;
 char title[256];
 char query[256];
 char query1[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn1 = hAllocConn(database);
 struct sqlResult *sr = NULL, *sr1 = NULL, *sr2 = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 char *hgsid = cartSessionId(cart);
 struct stsMapRat stsRow;
 struct stsInfoRat *infoRow;
 char stsid[20];
 char stsPrimer[40];
 char stsClone[45];
 int i;
 struct psl *pslList = NULL, *psl;
 int pslStart;
 boolean hasBin = FALSE;
 
 /* Print out non-sequence info */
 safef(title, sizeof title, "STS Marker %s", marker);
 cartWebStart(cart, database, "%s", title);
 
 /* Find the instance of the object in the bed table */
 sqlSafefFrag(query, sizeof(query), "name = '%s'", marker);
 sr = hRangeQuery(conn, table, seqName, start, end, query, &hasBin);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     stsMapRatStaticLoad(row+hasBin, &stsRow);
     /* Find the instance of the object in the stsInfo table */
     sqlFreeResult(&sr);
     sqlSafef(query, sizeof query, "SELECT * FROM stsInfoRat WHERE identNo = '%d'", stsRow.identNo);
     sr = sqlMustGetResult(conn, query);
     row = sqlNextRow(sr);
     if (row != NULL)
 	{
 	infoRow = stsInfoRatLoad(row);
 	printf("<TABLE>\n");
 	printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n", seqName);
 	printf("<TR><TH ALIGN=left>Start:</TH><TD>%d</TD></TR>\n",start+1);
 	printf("<TR><TH ALIGN=left>End:</TH><TD>%d</TD></TR>\n",end);
 	printf("</TABLE>\n");
         htmlHorizontalLine();
         printf("<TABLE>\n");
         printf("<TR><TH ALIGN=left>UCSC STS Marker ID:</TH><TD>%d</TD></TR>\n", infoRow->identNo);
         if (infoRow->UiStsId != 0)
             printf("<TR><TH ALIGN=left>UniSts Marker ID:</TH><TD>"
                    "<A HREF=\"http://www.ncbi.nlm.nih.gov/genome/sts/sts.cgi?uid=%d\" "
                    "TARGET=_BLANK>%d</A></TD></TR>\n", infoRow->UiStsId, infoRow->UiStsId);
         if (infoRow->RGDId != 0)
             printf("<TR><TH ALIGN=left>RGD Marker ID:</TH><TD><B>"
                    "<A HREF=\"http://rgd.mcw.edu/tools/query/query.cgi?id=%d\" "
                    "TARGET=_BLANK>%d</A></TD></TR>\n", infoRow->RGDId,infoRow->RGDId );
         if (strcmp(infoRow->RGDName, ""))
             printf("<TR><TH ALIGN=left>RGD Marker Name:</TH><TD>%s</TD></TR>\n", infoRow->RGDName);
         printf("</TABLE>\n");
         htmlHorizontalLine();
 	/* Print out primer information */
 	printf("<TABLE>\n");
 	printf("<TR><TH ALIGN=left>Left Primer:</TH><TD>%s</TD></TR>\n",infoRow->primer1);
 	printf("<TR><TH ALIGN=left>Right Primer:</TH><TD>%s</TD></TR>\n",infoRow->primer2);
 	printf("<TR><TH ALIGN=left>Distance:</TH><TD>%s bps</TD></TR>\n",infoRow->distance);
 	printf("</TABLE>\n");
 	htmlHorizontalLine();
 	/* Print out information from genetic maps for this marker */
 	if(strcmp(infoRow->fhhName, "") || strcmp(infoRow->shrspName, "") || strcmp(infoRow->rhName, ""))
 	    printf("<H3>Map Position</H3>\n<TABLE>\n");
 	if(strcmp(infoRow->fhhName, ""))
 	    {
 	    printf("<TR><TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position</TH></TR>\n");
 	    printf("<TR><TH ALIGN=left>&nbsp</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD></TR>\n",
                    infoRow->fhhName, infoRow->fhhChr, infoRow->fhhGeneticPos);
 	    }
 	if(strcmp(infoRow->shrspName, ""))
 	    {
 	    printf("<TR><TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position</TH></TR>\n");
 	    printf("<TR><TH ALIGN=left>&nbsp</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD></TR>\n",
                    infoRow->shrspName, infoRow->shrspChr, infoRow->shrspGeneticPos);
 	    }
 	if(strcmp(infoRow->rhName, ""))
 	    {
 	    printf("<TR><TH>&nbsp</TH><TH ALIGN=left WIDTH=150>Name</TH><TH ALIGN=left WIDTH=150>Chromosome</TH><TH ALIGN=left WIDTH=150>Position</TH><TH ALIGN=left WIDTH=150>Score</TH?</TR>\n");
 	    printf("<TR><TH ALIGN=left>&nbsp</TH><TD WIDTH=150>%s</TD><TD WIDTH=150>%s</TD><TD WIDTH=150>%.2f</TD><TD WIDTH=150>%.2f</TD></TR>\n",
                    infoRow->rhName, infoRow->rhChr, infoRow->rhGeneticPos, infoRow->RHLOD);
 	    }
 	printf("</TABLE><P>\n");
 
 	/* Print out alignment information - full sequence */
 	webNewSection("Genomic Alignments:");
 	safef(stsid, sizeof stsid, "%d", infoRow->identNo);
 	safef(stsPrimer, sizeof stsPrimer, "%d_%s", infoRow->identNo, infoRow->name);
 	safef(stsClone, sizeof stsClone, "%d_%s_clone", infoRow->identNo, infoRow->name);
 
 	/* find sts in primer alignment info */
         sqlSafefFrag(query, sizeof(query), "qName = '%s'", stsPrimer);
 	sr1 = hRangeQuery(conn1, "all_sts_primer", seqName, start, end, query,
 			  &hasBin);
 	i = 0;
 	pslStart = 0;
 	while ((row = sqlNextRow(sr1)) != NULL )
             {
 	    psl = pslLoad(row+hasBin);
 	    fflush(stdout);
 	    if ((sameString(psl->tName, seqName)) && (abs(psl->tStart - start) < 1000))
 		pslStart = psl->tStart;
 	    slAddHead(&pslList, psl);
 	    i++;
 	    }
 	slReverse(&pslList);
         if (i > 0)
 	    {
 	    printf("<H3>Primers:</H3>\n");
 	    printAlignments(pslList, pslStart, "htcCdnaAli", "all_sts_primer", stsPrimer);
 	    sqlFreeResult(&sr1);
 	    }
 	slFreeList(&pslList);
 	stsInfoRatFree(&infoRow);
 
 	/* Find sts in clone sequece alignment info */
         sqlSafefFrag(query1, sizeof(query1), "qName = '%s'", stsClone);
 	sr2 = hRangeQuery(conn1, "all_sts_primer", seqName, start, end, query1,
 			  &hasBin);
 	i = 0;
 	pslStart = 0;
 	while ((row = sqlNextRow(sr2)) != NULL )
             {
 	    psl = pslLoad(row+hasBin);
 	    fflush(stdout);
 	    if ((sameString(psl->tName, seqName)) && (abs(psl->tStart - start) < 1000))
 		pslStart = psl->tStart;
 	    slAddHead(&pslList, psl);
 	    i++;
 	    }
 	slReverse(&pslList);
         if (i > 0)
 	    {
 	    printf("<H3>Clone:</H3>\n");
 	    printAlignments(pslList, pslStart, "htcCdnaAli", "all_sts_primer", stsClone);
 	    sqlFreeResult(&sr1);
 	    }
 	slFreeList(&pslList);
 	stsInfoRatFree(&infoRow);
 	}
 
     htmlHorizontalLine();
 
     if (stsRow.score == 1000)
 	printf("<H3>This is the only location found for %s</H3>\n",marker);
     else
 	{
 	sqlFreeResult(&sr);
 	printf("<H4>Other locations found for %s in the genome:</H4>\n", marker);
 	printf("<TABLE>\n");
 	sqlSafefFrag(query, sizeof(query), "name = '%s'", marker);
 	sr = hRangeQuery(conn, table, seqName, start, end, query, &hasBin);
 	while ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    stsMapRatStaticLoad(row+hasBin, &stsRow);
 	    printf("<TR><TD>%s:</TD><TD><A HREF = \"../cgi-bin/hgc?hgsid=%s&o=%u&t=%d&g=stsMapRat&i=%s&c=%s\" target=_blank>%d</A></TD></TR>\n",
 		   stsRow.chrom, hgsid, stsRow.chromStart,stsRow.chromEnd, stsRow.name, stsRow.chrom,(stsRow.chromStart+stsRow.chromEnd)>>1);
 	    }
 	printf("</TABLE>\n");
 	}
     }
 webNewSection("Notes:");
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 hFreeConn(&conn1);
 }
 
 void doFishClones(struct trackDb *tdb, char *clone)
 /* Handle click on the FISH clones track */
 {
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 struct fishClones *fc;
 int i;
 
 /* Print out non-sequence info */
 cartWebStart(cart, database, "%s", clone);
 
 
 /* Find the instance of the object in the bed table */
 sqlSafef(query, sizeof query, "SELECT * FROM fishClones WHERE name = '%s' "
                "AND chrom = '%s' AND chromStart = %d "
                 "AND chromEnd = %d",
         clone, seqName, start, end);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     fc = fishClonesLoad(row);
     /* Print out general sequence positional information */
     printf("<H2><A HREF=");
     printCloneDbUrl(stdout, clone);
     printf(" TARGET=_BLANK>%s</A></H2>\n", clone);
     htmlHorizontalLine();
     printf("<TABLE>\n");
     printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n", seqName);
     printf("<TR><TH ALIGN=left>Start:</TH><TD>%d</TD></TR>\n",start+1);
     printf("<TR><TH ALIGN=left>End:</TH><TD>%d</TD></TR>\n",end);
     printBand(seqName, start, end, TRUE);
     printf("</TABLE>\n");
     htmlHorizontalLine();
 
     /* Print out information about the clone */
     printf("<H4>Placement of %s on draft sequence was determined using the location of %s</H4>\n",
 	   clone, fc->placeType);
     printf("<TABLE>\n");
     if (fc->accCount > 0)
         {
 	printf("<TR><TH>Genbank Accession:</TH>");
         for (i = 0; i < fc->accCount; i++)
 	    {
 	    printf("<TD><A HREF=\"");
 	    printEntrezNucleotideUrl(stdout, fc->accNames[i]);
             printf("\" TARGET=_BLANK>%s</A></TD>", fc->accNames[i]);
 	    }
 	printf("</TR>\n");
 	}
     if (fc->stsCount > 0)
         {
 	printf("<TR><TH ALIGN=left>STS Markers within clone:</TH>");
         for (i = 0; i < fc->stsCount; i++)
 	    {
 	    printf("<TD>%s</TD>", fc->stsNames[i]);
 	    }
 	printf("</TR>\n");
         }
     if (fc->beCount > 0)
         {
 	printf("<TR><TH ALIGN=left>BAC end sequence:</TH>");
         for (i = 0; i < fc->beCount; i++)
 	    {
 	    printf("<TD><A HREF=\"");
 	    printEntrezNucleotideUrl(stdout, fc->beNames[i]);
 	    printf("\" TARGET=_BLANK>%s</A></TD>", fc->beNames[i]);
 	    }
 	printf("</TR>\n");
         }
     printf("</TABLE>\n");
 
     /* Print out FISH placement information */
     webNewSection("FISH Placements");
     /*printf("<H3>Placements of %s by FISH</H3>\n", clone);*/
     printf("<TABLE>\n");
     printf("<TR><TH ALIGN=left WIDTH=100>Lab</TH><TH>Band Position</TH></TR>\n");
     for (i = 0; i < fc->placeCount; i++)
         {
 	if (sameString(fc->bandStarts[i],fc->bandEnds[i]))
 	    {
 	    printf("<TR><TD WIDTH=100 ALIGN=left>%s</TD><TD ALIGN=center>%s</TD></TR>",
 		   fc->labs[i], fc->bandStarts[i]);
 	    }
 	else
 	    {
 	    printf("<TR><TD WIDTH=100 ALIGN=left>%s</TD><TD ALIGN=center>%s - %s</TD></TR>",
 		   fc->labs[i], fc->bandStarts[i], fc->bandEnds[i]);
 	    }
 	}
 
     }
 printf("</TABLE>\n");
 webNewSection("Notes:");
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doRecombRate(struct trackDb *tdb)
 /* Handle click on the Recombination Rate track */
 {
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 struct recombRate *rr;
 
 /* Print out non-sequence info */
 cartWebStart(cart, database, "Recombination Rates");
 
 /* Find the instance of the object in the bed table */
 sqlSafef(query, sizeof query, "SELECT * FROM recombRate WHERE "
                "chrom = '%s' AND chromStart = %d "
                "AND chromEnd = %d",
         seqName, start, end);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     rr = recombRateLoad(row);
     /* Print out general sequence positional information */
     printf("<TABLE>\n");
     printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n", seqName);
     printf("<TR><TH ALIGN=left>Start:</TH><TD>%d</TD></TR>\n",start+1);
     printf("<TR><TH ALIGN=left>End:</TH><TD>%d</TD></TR>\n",end);
     printBand(seqName, start, end, TRUE);
     printf("<TR><TH ALIGN=left>deCODE Sex-Averaged Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->decodeAvg);
     printf("<TR><TH ALIGN=left>deCODE Female Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->decodeFemale);
     printf("<TR><TH ALIGN=left>deCODE Male Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->decodeMale);
     printf("<TR><TH ALIGN=left>Marshfield Sex-Averaged Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->marshfieldAvg);
     printf("<TR><TH ALIGN=left>Marshfield Female Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->marshfieldFemale);
     printf("<TR><TH ALIGN=left>Marshfield Male Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->marshfieldMale);
     printf("<TR><TH ALIGN=left>Genethon Sex-Averaged Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->genethonAvg);
     printf("<TR><TH ALIGN=left>Genethon Female Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->genethonFemale);
     printf("<TR><TH ALIGN=left>Genethon Male Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->genethonMale);
     printf("</TABLE>\n");
     freeMem(rr);
     }
 webNewSection("Notes:");
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doRecombRateRat(struct trackDb *tdb)
 /* Handle click on the rat Recombination Rate track */
 {
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 struct recombRateRat *rr;
 
 /* Print out non-sequence info */
 cartWebStart(cart, database, "Recombination Rates");
 
 
 /* Find the instance of the object in the bed table */
 sqlSafef(query, sizeof query, "SELECT * FROM recombRateRat WHERE "
                "chrom = '%s' AND chromStart = %d "
                "AND chromEnd = %d",
         seqName, start, end);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     rr = recombRateRatLoad(row);
     /* Print out general sequence positional information */
     printf("<TABLE>\n");
     printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n", seqName);
     printf("<TR><TH ALIGN=left>Start:</TH><TD>%d</TD></TR>\n",start+1);
     printf("<TR><TH ALIGN=left>End:</TH><TD>%d</TD></TR>\n",end);
     printBand(seqName, start, end, TRUE);
     printf("<TR><TH ALIGN=left>SHRSPxBN Sex-Averaged Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->shrspAvg);
     printf("<TR><TH ALIGN=left>FHHxACI Sex-Averaged Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->fhhAvg);
     printf("</TABLE>\n");
     freeMem(rr);
     }
 webNewSection("Notes:");
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doRecombRateMouse(struct trackDb *tdb)
 /* Handle click on the mouse Recombination Rate track */
 {
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 struct recombRateMouse *rr;
 
 /* Print out non-sequence info */
 cartWebStart(cart, database, "Recombination Rates");
 
 /* Find the instance of the object in the bed table */
 sqlSafef(query, sizeof query, "SELECT * FROM recombRateMouse WHERE "
                "chrom = '%s' AND chromStart = %d "
                "AND chromEnd = %d",
         seqName, start, end);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     rr = recombRateMouseLoad(row);
     /* Print out general sequence positional information */
     printf("<TABLE>\n");
     printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n", seqName);
     printf("<TR><TH ALIGN=left>Start:</TH><TD>%d</TD></TR>\n",start+1);
     printf("<TR><TH ALIGN=left>End:</TH><TD>%d</TD></TR>\n",end);
     printBand(seqName, start, end, TRUE);
     printf("<TR><TH ALIGN=left>WI Genetic Map Sex-Averaged Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->wiAvg);
     printf("<TR><TH ALIGN=left>MGD Genetic Map Sex-Averaged Rate:</TH><TD>%3.1f cM/Mb</TD></TR>\n", rr->mgdAvg);
     printf("</TABLE>\n");
     freeMem(rr);
     }
 webNewSection("Notes:");
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doGenMapDb(struct trackDb *tdb, char *clone)
 /* Handle click on the GenMapDb clones track */
 {
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 struct genMapDb *upc;
 int size;
 
 /* Print out non-sequence info */
 cartWebStart(cart, database, "GenMapDB BAC Clones");
 
 /* Find the instance of the object in the bed table */
 sqlSafef(query, sizeof query, "SELECT * FROM genMapDb WHERE name = '%s' "
                "AND chrom = '%s' AND chromStart = %d "
                "AND chromEnd = %d",
         clone, seqName, start, end);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     upc = genMapDbLoad(row);
     /* Print out general sequence positional information */
     printf("<H2><A HREF=");
     printGenMapDbUrl(stdout, clone);
     printf(" TARGET=_BLANK>%s</A></H2>\n", clone);
     htmlHorizontalLine();
     printf("<TABLE>\n");
     printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n", seqName);
     printf("<TR><TH ALIGN=left>Start:</TH><TD>%d</TD></TR>\n",start+1);
     printf("<TR><TH ALIGN=left>End:</TH><TD>%d</TD></TR>\n",end);
     size = end - start + 1;
     printf("<TR><TH ALIGN=left>Size:</TH><TD>%d</TD></TR>\n",size);
     printBand(seqName, start, end, TRUE);
     printf("</TABLE>\n");
     htmlHorizontalLine();
 
     /* Print out information about the clone */
     printf("<H4>Placement of %s on draft sequence was determined using BAC end sequences and/or an STS marker</H4>\n",clone);
     printf("<TABLE>\n");
     if (upc->accT7)
 	{
 	printf("<TR><TH ALIGN=left>T7 end sequence:</TH>");
 	printf("<TD><A HREF=\"");
 	printEntrezNucleotideUrl(stdout, upc->accT7);
 	printf("\" TARGET=_BLANK>%s</A></TD>", upc->accT7);
         printf("<TD>%s:</TD><TD ALIGN=right>%d</TD><TD ALIGN=LEFT> - %d</TD>",
 	       seqName, upc->startT7, upc->endT7);
 	printf("</TR>\n");
 	}
     if (upc->accSP6)
 	{
 	printf("<TR><TH ALIGN=left>SP6 end sequence:</TH>");
 	printf("<TD><A HREF=\"");
 	printEntrezNucleotideUrl(stdout, upc->accSP6);
 	printf("\" TARGET=_BLANK>%s</A></TD>", upc->accSP6);
         printf("<TD>%s:</TD><TD ALIGN=right>%d</TD><TD ALIGN=LEFT> - %d</TD>",
 	       seqName, upc->startSP6, upc->endSP6);
 	printf("</TR>\n");
 	}
     if (upc->stsMarker)
 	{
 	printf("<TR><TH ALIGN=left>STS Marker:</TH>");
 	printf("<TD><A HREF=\"");
 	printEntrezUniSTSUrl(stdout, upc->stsMarker);
 	printf("\" TARGET=_BLANK>%s</A></TD>", upc->stsMarker);
         printf("<TD>%s:</TD><TD ALIGN=right>%d</TD><TD ALIGN=LEFT> - %d</TD>",
 	       seqName, upc->stsStart, upc->stsEnd);
 	printf("</TR>\n");
 	}
     printf("</TABLE>\n");
     }
 webNewSection("Notes:");
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doMouseOrthoDetail(struct trackDb *tdb, char *itemName)
 /* Handle click on mouse synteny track. */
 {
 struct mouseSyn el;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset;
 
 cartWebStart(cart, database, "Mouse Synteny");
 printf("<H2>Mouse Synteny</H2>\n");
 
 sqlSafef(query, sizeof query, "select * from %s where chrom = '%s' and chromStart = %d",
 	tdb->table, seqName, start);
 rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     htmlHorizontalLine();
     mouseSynStaticLoad(row+rowOffset, &el);
     printf("<B>mouse chromosome:</B> %s<BR>\n", el.name+6);
     printf("<B>human chromosome:</B> %s<BR>\n", skipChr(el.chrom));
     printf("<B>human starting base:</B> %d<BR>\n", el.chromStart);
     printf("<B>human ending base:</B> %d<BR>\n", el.chromEnd);
     printf("<B>size:</B> %d<BR>\n", el.chromEnd - el.chromStart);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 void doMouseSyn(struct trackDb *tdb, char *itemName)
 /* Handle click on mouse synteny track. */
 {
 struct mouseSyn el;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset;
 
 cartWebStart(cart, database, "Mouse Synteny");
 printf("<H2>Mouse Synteny</H2>\n");
 
 sqlSafef(query, sizeof query, "select * from %s where chrom = '%s' and chromStart = %d",
 	tdb->table, seqName, start);
 rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     htmlHorizontalLine();
     mouseSynStaticLoad(row+rowOffset, &el);
     printf("<B>mouse chromosome:</B> %s<BR>\n", el.name+6);
     printf("<B>human chromosome:</B> %s<BR>\n", skipChr(el.chrom));
     printf("<B>human starting base:</B> %d<BR>\n", el.chromStart);
     printf("<B>human ending base:</B> %d<BR>\n", el.chromEnd);
     printf("<B>size:</B> %d<BR>\n", el.chromEnd - el.chromStart);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 void doMouseSynWhd(struct trackDb *tdb, char *itemName)
 /* Handle click on Whitehead mouse synteny track. */
 {
 struct mouseSynWhd el;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset;
 
 cartWebStart(cart, database, "Mouse Synteny (Whitehead)");
 printf("<H2>Mouse Synteny (Whitehead)</H2>\n");
 
 sqlSafef(query, sizeof query, "select * from %s where chrom = '%s' and chromStart = %d",
 	tdb->table, seqName, start);
 rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     htmlHorizontalLine();
     mouseSynWhdStaticLoad(row+rowOffset, &el);
     printf("<B>mouse chromosome:</B> %s<BR>\n", el.name);
     printf("<B>mouse starting base:</B> %d<BR>\n", el.mouseStart+1);
     printf("<B>mouse ending base:</B> %d<BR>\n", el.mouseEnd);
     printf("<B>human chromosome:</B> %s<BR>\n", skipChr(el.chrom));
     printf("<B>human starting base:</B> %d<BR>\n", el.chromStart+1);
     printf("<B>human ending base:</B> %d<BR>\n", el.chromEnd);
     printf("<B>strand:</B> %s<BR>\n", el.strand);
     printf("<B>segment label:</B> %s<BR>\n", el.segLabel);
     printf("<B>size:</B> %d (mouse), %d (human)<BR>\n",
 	   (el.mouseEnd - el.mouseStart), (el.chromEnd - el.chromStart));
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doEnsPhusionBlast(struct trackDb *tdb, char *itemName)
 /* Handle click on Ensembl Phusion Blast synteny track. */
 {
 struct ensPhusionBlast el;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char *org = hOrganism(database);
 char *tbl = cgiUsualString("table", cgiString("g"));
 char *elname, *ptr, *xenoDb, *xenoOrg, *xenoChrom;
 char query[256];
 int rowOffset;
 
 cartWebStart(cart, database, "%s", tdb->longLabel);
 printf("<H2>%s</H2>\n", tdb->longLabel);
 
 sqlSafef(query, sizeof query, "select * from %s where chrom = '%s' and chromStart = %d",
 	tdb->table, seqName, start);
 rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     htmlHorizontalLine();
     ensPhusionBlastStaticLoad(row+rowOffset, &el);
     elname = cloneString(el.name);
     if ((ptr = strchr(elname, '.')) != NULL)
 	{
 	*ptr = 0;
 	xenoChrom = ptr+1;
 	xenoDb = elname;
 	xenoOrg = hOrganism(xenoDb);
 	}
     else
 	{
 	xenoChrom = elname;
 	xenoDb = NULL;
 	xenoOrg = "Other Organism";
 	}
     printf("<B>%s chromosome:</B> %s<BR>\n", xenoOrg, xenoChrom);
     printf("<B>%s starting base:</B> %d<BR>\n", xenoOrg, el.xenoStart+1);
     printf("<B>%s ending base:</B> %d<BR>\n", xenoOrg, el.xenoEnd);
     printf("<B>%s chromosome:</B> %s<BR>\n", org, skipChr(el.chrom));
     printf("<B>%s starting base:</B> %d<BR>\n", org, el.chromStart+1);
     printf("<B>%s ending base:</B> %d<BR>\n", org, el.chromEnd);
     printf("<B>score:</B> %d<BR>\n", el.score);
     printf("<B>strand:</B> %s<BR>\n", el.strand);
     printf("<B>size:</B> %d (%s), %d (%s)<BR>\n",
 	   (el.xenoEnd - el.xenoStart), xenoOrg,
 	   (el.chromEnd - el.chromStart), org);
     if (xenoDb != NULL)
 	{
 	printf("<A HREF=\"%s?db=%s&position=%s:%d-%d\" TARGET=_BLANK>%s Genome Browser</A> at %s:%d-%d <BR>\n",
                hgTracksName(),
 	       xenoDb, xenoChrom, el.xenoStart, el.xenoEnd,
 	       xenoOrg, xenoChrom, el.xenoStart, el.xenoEnd);
 
 	}
     printf("<A HREF=\"%s&o=%d&g=getDna&i=%s&c=%s&l=%d&r=%d&strand=%s&table=%s\">"
 	   "View DNA for this feature</A><BR>\n",  hgcPathAndSettings(),
 	   el.chromStart, cgiEncode(el.name),
 	   el.chrom, el.chromStart, el.chromEnd, el.strand, tbl);
     freez(&elname);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void printDbSnpRsUrl(char *rsId, char *labelFormat, ...)
 /* Print a link to dbSNP's report page for an rs[0-9]+ ID. */
 {
 char dbSnpUrl[2048];
 safef (dbSnpUrl, sizeof(dbSnpUrl), dbSnpFormat, rsId);
 printf ("<a href=\"%s\" target=\"_blank\">", dbSnpUrl);
 
 va_list args;
 va_start(args, labelFormat);
 vprintf(labelFormat, args);
 va_end(args);
 printf("</a>");
 }
 
 char *validateOrGetRsId(char *name, struct sqlConnection *conn)
 /* If necessary, get the rsId from the affy120K or affy10K table,
    given the affyId.  rsId is more common, affy120K is next, affy10K least.
  * returns "valid" if name is already a valid rsId,
            new rsId if it is found in the affy tables, or
            0 if no valid rsId is found */
 {
 char  *rsId = cloneString(name);
 struct affy120KDetails *a120K = NULL;
 struct affy10KDetails *a10K = NULL;
 char   query[512];
 
 if (strncmp(rsId,"rs",2)) /* is not a valid rsId, so it must be an affyId */
     {
     sqlSafef(query, sizeof(query), /* more likely to be affy120K, so check first */
 	  "select * from affy120KDetails where affyId = '%s'", name);
     a120K = affy120KDetailsLoadByQuery(conn, query);
     if (a120K != NULL) /* found affy120K record */
 	rsId = cloneString(a120K->rsId);
     affy120KDetailsFree(&a120K);
     if (strncmp(rsId,"rs",2)) /* not a valid affy120K snp, might be affy10K */
 	{
         sqlSafef(query, sizeof(query),
 	      "select * from affy10KDetails where affyId = '%s'", name);
 	a10K = affy10KDetailsLoadByQuery(conn, query);
 	if (a10K != NULL) /* found affy10K record */
 	    rsId = cloneString(a10K->rsId);
 	affy10KDetailsFree(&a10K);
 	if (strncmp(rsId,"rs",2)) /* not valid affy10K snp */
 	    return 0;
 	}
     /* not all affy snps have valid rsIds, so return if it is invalid */
     if (strncmp(rsId,"rs",2) || strlen(rsId)<4 || sameString(rsId,"rs0")) /* not a valid rsId */
 	return 0;
     }
 else
     rsId = cloneString("valid");
 return rsId;
 }
 
 char *doDbSnpRs(char *name)
 /* print additional SNP details
  * returns "valid" if name is already a valid rsId,
            new rsId if it is found in the affy tables, or
            0 if no valid rsId is found */
 {
 struct sqlConnection *hgFixed = sqlConnect("hgFixed");
 char  *rsId = validateOrGetRsId(name, hgFixed);
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char   query[512];
 struct dbSnpRs *snp = NULL;
 char  *dbOrg = cloneStringZ(database,2);
 
 toUpperN(dbOrg,1); /* capitalize first letter */
 if (rsId) /* a valid rsId exists */
     {
     if (sameString(rsId, "valid"))
 	sqlSafef(query, sizeof(query),
 	      "select * "
 	      "from   dbSnpRs%s "
 	      "where  rsId = '%s'", dbOrg, name);
     else
 	sqlSafef(query, sizeof(query),
 	      "select * "
 	      "from   dbSnpRs%s "
 	      "where  rsId = '%s'", dbOrg, rsId);
     snp = dbSnpRsLoadByQuery(hgFixed, query);
     if (snp != NULL)
 	{
 	printf("<BR>\n");
 	if(snp->avHetSE>0)
 	    {
 	    printf("<B><A HREF=\"http://www.ncbi.nlm.nih.gov/SNP/Hetfreq.html\" target=\"_blank\">");
 	    printf("Average Heterozygosity</A>:</B> %f<BR>\n",snp->avHet);
 	    printf("<B><A HREF=\"http://www.ncbi.nlm.nih.gov/SNP/Hetfreq.html\" target=\"_blank\">");
 	    printf("Standard Error of Avg. Het.</A>: </B> %f<BR>\n", snp->avHetSE);
 	    }
 	else
 	    {
 	    printf("<B><A HREF=\"http://www.ncbi.nlm.nih.gov/SNP/Hetfreq.html\" target=\"_blank\">");
 	    printf("Average Heterozygosity</A>:</B> Not Known<BR>\n");
 	    printf("<B><A HREF=\"http://www.ncbi.nlm.nih.gov/SNP/Hetfreq.html\" target=\"_blank\">");
 	    printf("Standard Error of Avg. Het.</A>: </B> Not Known<BR>\n");
             }
 //      printf("<B><A HREF=\"http://www.ncbi.nlm.nih.gov/SNP/snp_legend.cgi?legend=snpFxnColor\" "
 //             "target=\"_blank\">");
 //      printf("Functional Status</A>:</B> <span style='font-family:Courier;'>%s<BR></span>\n",
 //             snp->func);
         printf("<B>Functional Status:</B> <span style='font-family:Courier;'>%s<BR></span>\n",
                snp->func);
         printf("<B><A HREF=\"http://www.ncbi.nlm.nih.gov/SNP/snp_legend.cgi?legend=validation\" "
                "target=\"_blank\">");
         printf("Validation Status</A>:</B> <span style='font-family:Courier;'>%s<BR></span>\n",
                snp->valid);
 //      printf("<B>Validation Status:</B> <span style='font-family:Courier;'>%s<BR></span>\n",
 //             snp->valid);
         printf("<B>Allele1:          </B> <span style='font-family:Courier;'>%s<BR></span>\n",
                snp->allele1);
         printf("<B>Allele2:          </B> <span style='font-family:Courier;'>%s<BR>\n",
                snp->allele2);
         printf("<B>Sequence in Assembly</B>:&nbsp;%s<BR>\n", snp->assembly);
         printf("<B>Alternate Sequence</B>:&nbsp;&nbsp;&nbsp;%s<BR></span>\n", snp->alternate);
         }
     dbSnpRsFree(&snp);
     }
 sqlDisconnect(&hgFixed);
 if (sameString(dbOrg,"Hg"))
     {
     sqlSafef(query, sizeof(query),
 	  "select source, type from snpMap where  name = '%s'", name);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	printf("<B><A HREF=\"#source\">Variant Source</A></B>: &nbsp;%s<BR>\n",row[0]);
 	printf("<B><A HREF=\"#type\">Variant Type</A></B>: &nbsp;%s\n",row[1]);
 	}
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 return rsId;
 }
 
 void doSnpEntrezGeneLink(struct trackDb *tdb, char *name)
 /* print link to EntrezGene for this SNP */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char *table = tdb->table;
 if (hTableExists(database, "knownGene") && sqlTableExists(conn, refLinkTable) &&
     hTableExists(database, "mrnaRefseq") && hTableExists(database, table))
     {
     struct sqlResult *sr;
     char **row;
     char query[512];
 
     sqlSafef(query, sizeof(query),
 	  "select distinct        "
 	  "       rl.locusLinkID, "
 	  "       rl.name         "
 	  "from   knownGene  kg,  "
 	  "       %s         rl,  "
 	  "       %s         snp, "
 	  "       mrnaRefseq mrs  "
 	  "where  snp.chrom  = kg.chrom       "
 	  "  and  kg.name    = mrs.mrna       "
 	  "  and  mrs.refSeq = rl.mrnaAcc     "
 	  "  and  kg.txStart < snp.chromStart "
 	  "  and  kg.txEnd   > snp.chromEnd   "
 	  "  and  snp.name   = '%s'",refLinkTable, table, name);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	printf("<BR><A HREF=\"http://www.ncbi.nlm.nih.gov/SNP/snp_ref.cgi?");
 	printf("geneId=%s\" TARGET=_blank>Entrez Gene for ", row[0]);
 	printf("%s</A><BR>\n", row[1]);
 	}
     sqlFreeResult(&sr);
     }
 hFreeConn(&conn);
 }
 
 void doSnpOld(struct trackDb *tdb, char *itemName)
 /* Put up info on a SNP. */
 {
 char *snpTable = tdb->table;
 struct snp snp;
 struct snpMap snpMap;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset;
 char *printId;
 
 cartWebStart(cart, database, "Simple Nucleotide Polymorphism (SNP)");
 printf("<H2>Simple Nucleotide Polymorphism (SNP) %s</H2>\n", itemName);
 sqlSafef(query, sizeof query,
 	"select * "
 	"from   %s "
 	"where  chrom = '%s' "
 	"  and  chromStart = %d "
 	"  and  name = '%s'",
         snpTable, seqName, start, itemName);
 rowOffset = hOffsetPastBin(database, seqName, snpTable);
 sr = sqlGetResult(conn, query);
 if (sameString(snpTable,"snpMap"))
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	snpMapStaticLoad(row+rowOffset, &snpMap);
 	bedPrintPos((struct bed *)&snpMap, 3, tdb);
 	}
 else
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	snpStaticLoad(row+rowOffset, &snp);
 	bedPrintPos((struct bed *)&snp, 3, tdb);
 	}
 /* write dbSnpRs details if found. */
 printId = doDbSnpRs(itemName);
 if (printId)
     {
     puts("<BR>");
     if (sameString(printId, "valid"))
         {
 	printDbSnpRsUrl(itemName, "dbSNP link");
 	putchar('\n');
 	doSnpEntrezGeneLink(tdb, itemName);
 	}
     else
 	{
 	printDbSnpRsUrl(printId, "dbSNP link (%s)", printId);
 	putchar('\n');
 	doSnpEntrezGeneLink(tdb, printId);
 	}
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void writeSnpException(char *exceptionList, char *itemName, int rowOffset,
                        char *chrom, int chromStart, struct trackDb *tdb)
 {
 char    *tokens;
 struct   lineFile      *lf;
 struct   tokenizer     *tkz;
 struct   snpExceptions  se;
 struct   sqlConnection *conn = hAllocConn(database);
 struct   sqlResult     *sr;
 char   **row;
 char     query[256];
 char    *id;
 char    *br=" ";
 char    *noteColor="#7f0000";
 boolean  firstException=TRUE;
 boolean  multiplePositions=FALSE;
 
 if (sameString(exceptionList,"0"))
     return;
 tokens=cloneString(exceptionList);
 lf=lineFileOnString("snpExceptions", TRUE, tokens);
 tkz=tokenizerOnLineFile(lf);
 while ((id=tokenizerNext(tkz))!=NULL)
     {
     if (firstException)
 	{
         printf("<BR><B style='color:%s;'>Note(s):</B><BR>\n",noteColor);
 	firstException=FALSE;
 	}
     if (sameString(id,",")) /* is there a tokenizer that doesn't return separators? */
 	continue;
     if (sameString(id,"18")||sameString(id,"19")||sameString(id,"20"))
 	multiplePositions=TRUE;
     br=cloneString("<BR>");
     sqlSafef(query, sizeof(query), "select * from snpExceptions where exceptionId = %s", id);
     sr = sqlGetResult(conn, query);
      /* exceptionId is a primary key; at most 1 record returned */
     while ((row = sqlNextRow(sr))!=NULL)
 	{
 	snpExceptionsStaticLoad(row, &se);
         printf("&nbsp;&nbsp;&nbsp;<B style='color:%s;'>%s</B><BR>\n",
 	       noteColor,se.description);
 	}
     }
 printf("%s\n",br);
 if (multiplePositions)
     {
     struct snp snp;
     printf("<B style='color:#7f0000;'>Other Positions</B>:<BR><BR>");
     sqlSafef(query, sizeof(query), "select * from snp where name='%s'", itemName);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr))!=NULL)
 	{
 	snpStaticLoad(row+rowOffset, &snp);
 	if (differentString(chrom,snp.chrom) || chromStart!=snp.chromStart)
 	    {
 	    bedPrintPos((struct bed *)&snp, 3, tdb);
 	    printf("<BR>\n");
 	    }
 	}
     }
 }
 
 void printSnpInfo(struct snp snp)
 /* print info on a snp */
 {
 if (differentString(snp.strand,"?")) {printf("<B>Strand: </B>%s\n", snp.strand);}
 printf("<BR><B>Observed: </B>%s\n",                                 snp.observed);
 printf("<BR><B><A HREF=\"#Source\">Source</A>: </B>%s\n",           snp.source);
 printf("<BR><B><A HREF=\"#MolType\">Molecule Type</A>: </B>%s\n",   snp.molType);
 printf("<BR><B><A HREF=\"#Class\">Variant Class</A>: </B>%s\n",     snp.class);
 printf("<BR><B><A HREF=\"#Valid\">Validation Status</A>: </B>%s\n", snp.valid);
 printf("<BR><B><A HREF=\"#Func\">Function</A>: </B>%s\n",           snp.func);
 printf("<BR><B><A HREF=\"#LocType\">Location Type</A>: </B>%s\n",   snp.locType);
 if (snp.avHet>0)
     printf("<BR><B><A HREF=\"#AvHet\">Average Heterozygosity</A>: </B>%.3f +/- %.3f", snp.avHet, snp.avHetSE);
 printf("<BR>\n");
 }
 
 off_t getSnpSeqFileOffset(struct trackDb *tdb, struct snp *snp)
 /* do a lookup in snpSeq for the offset */
 {
 char *snpSeqSetting = trackDbSetting(tdb, "snpSeq");
 char snpSeqTable[128];
 char query[256];
 char **row;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 off_t offset = 0;
 
 if (isNotEmpty(snpSeqSetting))
     {
     if (hTableExists(database, snpSeqSetting))
 	safecpy(snpSeqTable, sizeof(snpSeqTable), snpSeqSetting);
     else
 	return -1;
     }
 else
     {
     safef(snpSeqTable, sizeof(snpSeqTable), "%sSeq", tdb->table);
     if (!hTableExists(database, snpSeqTable))
 	{
 	safecpy(snpSeqTable, sizeof(snpSeqTable), "snpSeq");
 	if (!hTableExists(database, snpSeqTable))
 	    return -1;
 	}
     }
 sqlSafef(query, sizeof(query), "select file_offset from %s where acc='%s'",
       snpSeqTable, snp->name);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row == NULL)
    return -1;
 offset = sqlLongLong(row[0]);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 return offset;
 }
 
 
 char *getSnpSeqFile(struct trackDb *tdb, int version)
 /* find location of snp.fa and test existence. */
 {
 char *seqFile = trackDbSetting(tdb, "snpSeqFile");
 if (isNotEmpty(seqFile))
     {
     if (fileExists(seqFile))
 	return cloneString(seqFile);
     else
 	return NULL;
     }
 char seqFileBuf[512];
 safef(seqFileBuf, sizeof(seqFileBuf), "/gbdb/%s/snp/%s.fa",
       database, tdb->table);
 if (fileExists(seqFileBuf))
     return cloneString(seqFileBuf);
 safef(seqFileBuf, sizeof(seqFileBuf), "/gbdb/%s/snp/snp%d.fa", database, version);
 if (fileExists(seqFileBuf))
     return cloneString(seqFileBuf);
 safef(seqFileBuf, sizeof(seqFileBuf), "/gbdb/%s/snp/snp.fa", database);
 if (fileExists(seqFileBuf))
     return cloneString(seqFileBuf);
 return NULL;
 }
 
 
 void printNullAlignment(int l, int r, int q)
 /* Print out a double-sided gap for unaligned insertion SNP. */
 {
 int digits = max(digitsBaseTen(r), digitsBaseTen(q));
 printf("%0*d - %0*d\n"
        "%*s"
        "  (dbSNP-annotated position was not re-aligned to "
        "observed allele code -- see adjacent alignments)\n"
        "%0*d - %0*d</B>\n\n", digits, l, digits, r,
        (digits*2 + 3), "", digits, q+1, digits, q);
 }
 
 void printOffsetAndBoldAxt(struct axt *axtIn, int lineWidth,
 			   struct axtScoreScheme *ss, int tOffset, int qOffset,
 			   int boldStart, int boldEnd,
 			   boolean tIsRc, int tSize, int qSize)
 /* Given an axt block, break it into multiple blocks for printing if
  * the bold range falls in the middle; add t & qOffset to t & q
  * coords; and print all blocks.  boldStart and boldEnd are relative to
  * the target sequence used to make axtIn (start at 0, not tOffset).
  * tIsRc means that the target sequence that was aligned to create axtIn
  * was reverse-complemented so we want to display t coords backwards;
  * that includes reversing block coords within the target seq range. */
 {
 struct axt axtBlock;
 int nullQStart = 0;
 
 /* (Defining a macro because a function would have an awful lot of arguments.)
  * First extract the portion of axtIn for this block.  If tIsRc, then
  * reverse the block's t coords within the range of (0, tSize].
  * Add t and qOffset, swap target and query so that target sequence is on top,
  * and print it out, adding bold tags before and afterwards if isBold. */
 #define doBlock(blkStart, blkEnd, isBold) \
     if (isBold) printf("<B>"); \
     if (axtGetSubsetOnT(axtIn, &axtBlock, blkStart, blkEnd, ss, isBold)) \
         { \
         if (tIsRc) \
             { \
             int tmp = axtBlock.tStart; \
             axtBlock.tStart = tSize - axtBlock.tEnd;  \
             axtBlock.tEnd = tSize - tmp; \
             } \
         axtBlock.tStart += tOffset;  axtBlock.tEnd += tOffset; \
         axtBlock.qStart += qOffset;  axtBlock.qEnd += qOffset; \
         nullQStart = axtBlock.qEnd; \
         axtSwap(&axtBlock, tSize, qSize); \
         axtPrintTraditionalExtra(&axtBlock, lineWidth, ss, stdout, \
                                  FALSE, tIsRc); \
         } \
     else if (isBold) \
         { \
         int ins = (tIsRc ? tSize - blkEnd : blkEnd) + tOffset; \
         int l = tIsRc ? ins : ins+1,  r = tIsRc ? ins+1 : ins; \
         printNullAlignment(l, r, nullQStart); \
         } \
     if (isBold) printf("</B>");
 
 /* First block: before bold range */
 doBlock(axtIn->tStart, boldStart, FALSE);
 /* Second block: bold range */
 doBlock(boldStart, boldEnd, TRUE);
 /* Third block: after bold range */
 doBlock(boldEnd, axtIn->tEnd, FALSE);
 
 #undef doBlock
 }
 
 void generateAlignment(struct dnaSeq *tSeq, struct dnaSeq *qSeq,
 		       int lineWidth, int tOffset, int qOffset,
 		       int boldStart, int boldEnd, boolean tIsRc)
 /* Use axtAffine to align tSeq to qSeq.  Print the resulting alignment.
  * tOffset and qOffset are added to the respective sets of coordinates for
  * printing.  If boldStart and boldEnd have any overlap with the aligned
  * tSeq, print that region as a separate block, in bold.  boldStart and
  * boldEnd are relative to the start of tSeq (start at 0 not tOffset).
  * tIsRc means that tSeq has been reverse-complemented so we want to
  * display t coords backwards. */
 {
 int matchScore = 100;
 int misMatchScore = 100;
 int gapOpenPenalty = 400;
 int gapExtendPenalty = 50;
 struct axtScoreScheme *ss = axtScoreSchemeSimpleDna(matchScore, misMatchScore, gapOpenPenalty, gapExtendPenalty);
 struct axt *axt = axtAffine(qSeq, tSeq, ss), *axtBlock=axt;
 
 hPrintf("<PRE>");
 if (axt == NULL)
    {
    printf("%s and %s don't align\n", tSeq->name, qSeq->name);
    return;
    }
 
 printf("<B>Alignment between genome (%s, %c strand; %d bp) and "
        "dbSNP sequence (%s; %d bp)</B>\n",
        tSeq->name, (tIsRc ? '-' : '+'), tSeq->size, qSeq->name, qSeq->size);
 for (axtBlock=axt;  axtBlock !=NULL;  axtBlock = axtBlock->next)
     {
     printf("ID (including gaps) %3.1f%%, coverage (of both) %3.1f%%\n\n",
            axtIdWithGaps(axtBlock)*100,
 	   axtCoverage(axtBlock, qSeq->size, tSeq->size)*100);
     printOffsetAndBoldAxt(axtBlock, lineWidth, ss, tOffset, qOffset,
 			  boldStart, boldEnd, tIsRc, tSeq->size, qSeq->size);
     }
 
 axtFree(&axt);
 hPrintf("</PRE>");
 }
 
 void printSnpAlignment(struct trackDb *tdb, struct snp *snp, int version)
 /* Get flanking sequences from table; align and print */
 {
 char *fileName = NULL;
 char *variation = NULL;
 
 char *line;
 struct lineFile *lf = NULL;
 int lineSize;
 static int maxFlank = 1000;
 static int lineWidth = 100;
 
 boolean gotVar = FALSE;
 boolean isNucleotide = TRUE;
 boolean leftFlankTrimmed = FALSE;
 boolean rightFlankTrimmed = FALSE;
 
 struct dyString *seqDbSnp5 = newDyString(512);
 struct dyString *seqDbSnp3 = newDyString(512);
 struct dyString *seqDbSnpTemp = newDyString(512);
 
 char *leftFlank = NULL;
 char *rightFlank = NULL;
 
 struct dnaSeq *dnaSeqDbSnp5 = NULL;
 struct dnaSeq *dnaSeqDbSnpO = NULL;
 struct dnaSeq *dnaSeqDbSnp3 = NULL;
 struct dnaSeq *seqDbSnp = NULL;
 struct dnaSeq *seqNib = NULL;
 
 int spaces = 0;
 int len5 = 0;
 int len3 = 0;
 int start = 0;
 int end = 0;
 int skipCount = 0;
 
 off_t offset = 0;
 
 fileName = getSnpSeqFile(tdb, version);
 if (!fileName)
     return;
 
 offset = getSnpSeqFileOffset(tdb, snp);
 if (offset == -1)
     return;
 
 lf = lineFileOpen(fileName, TRUE);
 lineFileSeek(lf, offset, SEEK_SET);
 /* skip the header line */
 lineFileNext(lf, &line, &lineSize);
 if (!startsWith(">rs", line))
     errAbort("Expected FASTA header, got this line:\n%s\nat offset %lld "
 	     "in file %s", line, (long long)offset, fileName);
 
 while (lineFileNext(lf, &line, &lineSize))
     {
     spaces = countChars(line, ' ');
     stripString(line, " ");
     lineSize = lineSize - spaces;
     if (sameString(line, "N"))
         isNucleotide = FALSE;
     else
         isNucleotide = isAllDna(line, lineSize);
     if (lineSize > 2 && gotVar)
         dyStringAppend(seqDbSnp3,line);
     else if (lineSize > 2 && !gotVar)
         dyStringAppend(seqDbSnp5,line);
     else if (lineSize == 2 && !isNucleotide)
         {
 	gotVar = TRUE;
 	variation = cloneString(line);
 	}
     else if (lineSize == 1)
         break;
     }
 lineFileClose(&lf);
 
 if (variation == NULL)
     {
     printf("<P>Could not parse ambiguous SNP base out of dbSNP "
 	   "sequence, so can't display re-alignment of flanking sequences.\n");
     return;
     }
 
 /* trim */
 /* axtAffine has a limit of 100,000,000 bases for query x target */
 leftFlank = dyStringCannibalize(&seqDbSnp5);
 rightFlank = dyStringCannibalize(&seqDbSnp3);
 len5 = strlen(leftFlank);
 len3 = strlen(rightFlank);
 if (len5 > maxFlank)
     {
     skipCount = len5 - maxFlank;
     leftFlank = leftFlank + skipCount;
     leftFlankTrimmed = TRUE;
     len5 = strlen(leftFlank);
     }
 if (len3 > maxFlank)
     {
     rightFlank[maxFlank] = '\0';
     rightFlankTrimmed = TRUE;
     len3 = strlen(rightFlank);
     }
 
 /* get genomic coords */
 int isRc = sameString(snp->strand, "-");
 if (isRc)
     {
     start = snp->chromStart - len3;
     end = snp->chromEnd + len5;
     }
 else
     {
     start = snp->chromStart - len5;
     end = snp->chromEnd + len3;
     }
 int genoLen3 = len3;
 int genoLen5 = len5;
 if (start < 0)
     {
     if (isRc)
 	genoLen3 += start;
     else
 	genoLen5 += start;
     start = 0;
     }
 int chromSize = hChromSize(database, snp->chrom);
 if (end > chromSize)
     {
     if (isRc)
 	genoLen5 += (chromSize - end);
     else
 	genoLen3 += (chromSize - end);
     end = chromSize;
     }
 
 /* do the lookup */
 seqNib = hChromSeqMixed(database, snp->chrom, start, end);
 if (seqNib == NULL)
     errAbort("Couldn't get genomic sequence around %s (%s:%d-%d)",
 	     snp->name, snp->chrom, start+1, end);
 if (isRc)
     reverseComplement(seqNib->dna, seqNib->size);
 char betterName[512];
 safef(betterName, sizeof(betterName), "%s %s:%d-%d",
       database, seqName, start+1, end);
 seqNib->name = cloneString(betterName);
 
 jsBeginCollapsibleSection(cart, tdb->track, "realignment",
 			  "Re-alignment of the SNP's flanking sequences to the genomic sequence",
 			  FALSE);
 printf("Note: this alignment was computed by UCSC and may not be identical to "
        "NCBI's alignment used to map the SNP.\n");
 
 printf("<PRE><B>Genomic sequence around %s (%s:%d-%d, %s strand):</B>\n",
        snp->name, snp->chrom, start+1, end,
        isRc ? "reverse complemented for -" : "+");
 int snpWidth = snp->chromEnd - snp->chromStart;
 writeSeqWithBreaks(stdout, seqNib->dna, genoLen5, lineWidth);
 printf("<B>");
 if (snp->chromEnd > snp->chromStart)
     writeSeqWithBreaks(stdout, seqNib->dna + genoLen5, snpWidth, lineWidth);
 else
     printf("-\n");
 printf("</B>");
 writeSeqWithBreaks(stdout, seqNib->dna + seqNib->size - genoLen3, genoLen3,
 		   lineWidth);
 printf("</PRE>\n");
 
 printf("\n<PRE><B>dbSNP flanking sequences and observed allele code for %s"
        ":</B>\n", snp->name);
 printf("(Uses ");
 printf("<A HREF=\"../goldenPath/help/iupac.html\"" );
 printf("TARGET=_BLANK>IUPAC ambiguity codes</A>");
 printf(")\n");
 if (leftFlankTrimmed)
     printf("Left flank trimmed to %d bases.\n", maxFlank);
 if (rightFlankTrimmed)
     printf("Right flank trimmed to %d bases.\n", maxFlank);
 dnaSeqDbSnp5 = newDnaSeq(leftFlank, len5, "dbSNP seq 5");
 dnaSeqDbSnpO = newDnaSeq(variation, strlen(variation),"dbSNP seq O");
 dnaSeqDbSnp3 = newDnaSeq(rightFlank, len3, "dbSNP seq 3");
 writeSeqWithBreaks(stdout, dnaSeqDbSnp5->dna, dnaSeqDbSnp5->size, lineWidth);
 printf("<B>");
 writeSeqWithBreaks(stdout, dnaSeqDbSnpO->dna, dnaSeqDbSnpO->size, lineWidth);
 printf("</B>");
 writeSeqWithBreaks(stdout, dnaSeqDbSnp3->dna, dnaSeqDbSnp3->size, lineWidth);
 printf("</PRE>\n");
 
 /* create seqDbSnp */
 dyStringAppend(seqDbSnpTemp, leftFlank);
 dyStringAppend(seqDbSnpTemp, variation);
 dyStringAppend(seqDbSnpTemp, rightFlank);
 seqDbSnp = newDnaSeq(seqDbSnpTemp->string, strlen(seqDbSnpTemp->string),
 		     snp->name);
 if (seqDbSnp == NULL)
     {
     warn("Couldn't get sequences");
     return;
     }
 seqDbSnp->size = strlen(seqDbSnp->dna);
 
 generateAlignment(seqNib, seqDbSnp, lineWidth, start, skipCount,
 		  genoLen5, genoLen5 + snpWidth, isRc);
 jsEndCollapsibleSection();
 }
 
 void doSnp(struct trackDb *tdb, char *itemName)
 /* Process SNP details. */
 {
 char   *snpTable = tdb->table;
 struct snp snp;
 int    start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char   query[256];
 int    rowOffset=hOffsetPastBin(database, seqName, snpTable);
 int    firstOne=1;
 char  *exception=0;
 char  *chrom="";
 int    chromStart=0;
 
 cartWebStart(cart, database, "Simple Nucleotide Polymorphism (SNP)");
 printf("<H2>Simple Nucleotide Polymorphism (SNP) %s</H2>\n", itemName);
 sqlSafef(query, sizeof(query), "select * from %s where chrom='%s' and "
       "chromStart=%d and name='%s'", snpTable, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr))!=NULL)
     {
     snpStaticLoad(row+rowOffset, &snp);
     if (firstOne)
 	{
 	exception=cloneString(snp.exception);
 	chrom = cloneString(snp.chrom);
 	chromStart = snp.chromStart;
 	bedPrintPos((struct bed *)&snp, 3, tdb);
 	printf("<BR>\n");
 	firstOne=0;
 	}
     printSnpInfo(snp);
     }
 if (startsWith("rs",itemName))
     {
     printDbSnpRsUrl(itemName, "dbSNP");
     putchar('\n');
     doSnpEntrezGeneLink(tdb, itemName);
     }
 if (hTableExists(database, "snpExceptions") && differentString(exception,"0"))
     writeSnpException(exception, itemName, rowOffset, chrom, chromStart, tdb);
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doAffy120KDetails(struct trackDb *tdb, char *name)
 /* print additional SNP details */
 {
 struct sqlConnection *conn = sqlConnect("hgFixed");
 char query[1024];
 struct affy120KDetails *snp = NULL;
 sqlSafef(query, sizeof(query),
          "select  affyId, rsId, baseA, baseB, sequenceA, sequenceB, "
 	 "        enzyme, minFreq, hetzyg, avHetSE, "
          "        NA04477, NA04479, NA04846, NA11036, NA11038, NA13056, "
          "        NA17011, NA17012, NA17013, NA17014, NA17015, NA17016, "
          "        NA17101, NA17102, NA17103, NA17104, NA17105, NA17106, "
          "        NA17201, NA17202, NA17203, NA17204, NA17205, NA17206, "
          "        NA17207, NA17208, NA17210, NA17211, NA17212, NA17213, "
          "        PD01, PD02, PD03, PD04, PD05, PD06, PD07, PD08, "
          "        PD09, PD10, PD11, PD12, PD13, PD14, PD15, PD16, "
          "        PD17, PD18, PD19, PD20, PD21, PD22, PD23, PD24  "
          "from    affy120KDetails "
          "where   affyId = %s", name);
 snp = affy120KDetailsLoadByQuery(conn, query);
 if (snp!=NULL)
     {
     printf("<BR>\n");
     printf("<B>Sample Prep Enzyme:</B> <I>%s</I><BR>\n",snp->enzyme);
     printf("<B>Minimum Allele Frequency:</B> %.3f<BR>\n",snp->minFreq);
     printf("<B>Heterozygosity:</B> %.3f<BR>\n",snp->hetzyg);
     printf("<B>Base A:          </B> <span style='font-family:Courier;'>%s</span><BR>\n",
 	   snp->baseA);
     printf("<B>Base B:          </B> <span style='font-family:Courier;'>%s</span><BR>\n",
 	   snp->baseB);
     printf("<B>Sequence of Allele A:</B>&nbsp;<span style='font-family:Courier;'>");
     printf("%s</span><BR>\n",snp->sequenceA);
     printf("<B>Sequence of Allele B:</B>&nbsp;<span style='font-family:Courier;'>");
     printf("%s</span><BR>\n",snp->sequenceB);
     if (isNotEmpty(snp->rsId))
 	{
 	puts("<BR>");
 	printDbSnpRsUrl(snp->rsId, "dbSNP link for %s", snp->rsId);
 	puts("<BR>");
 	}
     doSnpEntrezGeneLink(tdb, snp->rsId);
     printf("<BR>Genotypes:<BR>");
     printf("\n<BR><span style='font-family:Courier;'>");
     printf("NA04477:&nbsp;%s&nbsp;&nbsp;", snp->NA04477);
     printf("NA04479:&nbsp;%s&nbsp;&nbsp;", snp->NA04479);
     printf("NA04846:&nbsp;%s&nbsp;&nbsp;", snp->NA04846);
     printf("NA11036:&nbsp;%s&nbsp;&nbsp;", snp->NA11036);
     printf("NA11038:&nbsp;%s&nbsp;&nbsp;", snp->NA11038);
     printf("NA13056:&nbsp;%s&nbsp;&nbsp;", snp->NA13056);
     printf("\n<BR>NA17011:&nbsp;%s&nbsp;&nbsp;", snp->NA17011);
     printf("NA17012:&nbsp;%s&nbsp;&nbsp;", snp->NA17012);
     printf("NA17013:&nbsp;%s&nbsp;&nbsp;", snp->NA17013);
     printf("NA17014:&nbsp;%s&nbsp;&nbsp;", snp->NA17014);
     printf("NA17015:&nbsp;%s&nbsp;&nbsp;", snp->NA17015);
     printf("NA17016:&nbsp;%s&nbsp;&nbsp;", snp->NA17016);
     printf("\n<BR>NA17101:&nbsp;%s&nbsp;&nbsp;", snp->NA17101);
     printf("NA17102:&nbsp;%s&nbsp;&nbsp;", snp->NA17102);
     printf("NA17103:&nbsp;%s&nbsp;&nbsp;", snp->NA17103);
     printf("NA17104:&nbsp;%s&nbsp;&nbsp;", snp->NA17104);
     printf("NA17105:&nbsp;%s&nbsp;&nbsp;", snp->NA17105);
     printf("NA17106:&nbsp;%s&nbsp;&nbsp;", snp->NA17106);
     printf("\n<BR>NA17201:&nbsp;%s&nbsp;&nbsp;", snp->NA17201);
     printf("NA17202:&nbsp;%s&nbsp;&nbsp;", snp->NA17202);
     printf("NA17203:&nbsp;%s&nbsp;&nbsp;", snp->NA17203);
     printf("NA17204:&nbsp;%s&nbsp;&nbsp;", snp->NA17204);
     printf("NA17205:&nbsp;%s&nbsp;&nbsp;", snp->NA17205);
     printf("NA17206:&nbsp;%s&nbsp;&nbsp;", snp->NA17206);
     printf("\n<BR>NA17207:&nbsp;%s&nbsp;&nbsp;", snp->NA17207);
     printf("NA17208:&nbsp;%s&nbsp;&nbsp;", snp->NA17208);
     printf("NA17210:&nbsp;%s&nbsp;&nbsp;", snp->NA17210);
     printf("NA17211:&nbsp;%s&nbsp;&nbsp;", snp->NA17211);
     printf("NA17212:&nbsp;%s&nbsp;&nbsp;", snp->NA17212);
     printf("NA17213:&nbsp;%s&nbsp;&nbsp;", snp->NA17213);
     printf("\n<BR>PD01:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD01);
     printf("PD02:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD02);
     printf("PD03:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD03);
     printf("PD04:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD04);
     printf("PD05:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD05);
     printf("PD06:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD06);
     printf("\n<BR>PD07:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD07);
     printf("PD08:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD08);
     printf("PD09:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD09);
     printf("PD10:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD10);
     printf("PD11:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD11);
     printf("PD12:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD12);
     printf("\n<BR>PD13:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD13);
     printf("PD14:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD14);
     printf("PD15:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD15);
     printf("PD16:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD16);
     printf("PD17:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD17);
     printf("PD18:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD18);
     printf("\n<BR>PD19:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD19);
     printf("PD20:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD20);
     printf("PD21:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD21);
     printf("PD22:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD22);
     printf("PD23:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD23);
     printf("PD24:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;", snp->PD24);
     printf("\n</span>\n");
     }
 affy120KDetailsFree(&snp);
 sqlDisconnect(&conn);
 }
 
 void doCnpLocke(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct cnpLocke thisItem;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 printf("<B>NCBI Clone Registry: </B><A href=");
 printCloneDbUrl(stdout, itemName);
 printf(" target=_blank>%s</A><BR>\n", itemName);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     cnpLockeStaticLoad(row+rowOffset, &thisItem);
     bedPrintPos((struct bed *)&thisItem, 3, tdb);
     printf("<BR><B>Variation Type</B>: %s\n",thisItem.variationType);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doCnpIafrate(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct cnpIafrate cnpIafrate;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 printf("<B>NCBI Clone Registry: </B><A href=");
 printCloneDbUrl(stdout, itemName);
 printf(" target=_blank>%s</A><BR>\n", itemName);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     cnpIafrateStaticLoad(row+rowOffset, &cnpIafrate);
     bedPrintPos((struct bed *)&cnpIafrate, 3, tdb);
     printf("<BR><B>Variation Type</B>: %s\n",cnpIafrate.variationType);
     printf("<BR><B>Score</B>: %g\n",cnpIafrate.score);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doCnpIafrate2(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct cnpIafrate2 thisItem;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 printf("<B>NCBI Clone Registry: </B><A href=");
 printCloneDbUrl(stdout, itemName);
 printf(" target=_blank>%s</A><BR>\n", itemName);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     cnpIafrate2StaticLoad(row+rowOffset, &thisItem);
     bedPrintPos((struct bed *)&thisItem, 3, tdb);
     printf("<BR><B>Cohort Type</B>: %s\n",thisItem.cohortType);
     if (strstr(thisItem.cohortType, "Control"))
         {
         printf("<BR><B>Control Gain Count</B>: %d\n",thisItem.normalGain);
         printf("<BR><B>Control Loss Count</B>: %d\n",thisItem.normalLoss);
 	}
     if (strstr(thisItem.cohortType, "Patient"))
         {
         printf("<BR><B>Patient Gain Count</B>: %d\n",thisItem.patientGain);
         printf("<BR><B>Patient Loss Count</B>: %d\n",thisItem.patientLoss);
 	}
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doDelHinds2(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct delHinds2 thisItem;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     delHinds2StaticLoad(row+rowOffset, &thisItem);
     bedPrintPos((struct bed *)&thisItem, 3, tdb);
     printf("<BR><B>Frequency</B>: %3.2f%%\n",thisItem.frequency);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doDelConrad2(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct delConrad2 thisItem;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     delConrad2StaticLoad(row+rowOffset, &thisItem);
     bedPrintPos((struct bed *)&thisItem, 3, tdb);
     printf("<BR><B>HapMap individual</B>: %s\n",thisItem.offspring);
     printf("<BR><B>HapMap population</B>: %s\n",thisItem.population);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 
 void doCnpSebat(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct cnpSebat cnpSebat;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     cnpSebatStaticLoad(row+rowOffset, &cnpSebat);
     bedPrintPos((struct bed *)&cnpSebat, 3, tdb);
     printf("<BR><B>Number of probes</B>: %d\n",cnpSebat.probes);
     printf("<BR><B>Number of individuals</B>: %d\n",cnpSebat.individuals);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doCnpSebat2(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct cnpSebat2 cnpSebat2;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     cnpSebat2StaticLoad(row+rowOffset, &cnpSebat2);
     bedPrintPos((struct bed *)&cnpSebat2, 3, tdb);
     printf("<BR><B>Number of probes</B>: %d\n",cnpSebat2.probes);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 
 void printCnpSharpDetails(struct cnpSharp cnpSharp)
 {
 printf("<B>Name:               </B> %s <BR>\n",     cnpSharp.name);
 printf("<B>Variation type:     </B> %s <BR>\n",     cnpSharp.variationType);
 printf("<B>Cytoband:           </B> %s <BR>\n",     cnpSharp.cytoName);
 printf("<B>Strain:             </B> %s <BR>\n",     cnpSharp.cytoStrain);
 printf("<B>Duplication Percent:</B> %.1f %%<BR>\n", cnpSharp.dupPercent*100);
 printf("<B>Repeat Percent:     </B> %.1f %%<BR>\n", cnpSharp.repeatsPercent*100);
 printf("<B>LINE Percent:       </B> %.1f %%<BR>\n", cnpSharp.LINEpercent*100);
 printf("<B>SINE Percent:       </B> %.1f %%<BR>\n", cnpSharp.SINEpercent*100);
 printf("<B>LTR Percent:        </B> %.1f %%<BR>\n", cnpSharp.LTRpercent*100);
 printf("<B>DNA Percent:        </B> %.1f %%<BR>\n", cnpSharp.DNApercent*100);
 printf("<B>Disease Percent:    </B> %.1f %%<BR>\n", cnpSharp.diseaseSpotsPercent*100);
 }
 
 void printCnpSharpSampleData(char *itemName)
 {
 struct sqlConnection *hgFixed1 = sqlConnect("hgFixed");
 struct sqlConnection *hgFixed2 = sqlConnect("hgFixed");
 char query[256], query2[1024];
 char **row;
 struct sqlResult *sr1, *sr2;
 float sample, cutoff;
 
 printf("<BR>\n");
 sqlSafef(query, sizeof(query), "select distinct substring(sample,1,5) from cnpSharpCutoff order by sample");
 sr1 = sqlGetResult(hgFixed1, query);
 while ((row = sqlNextRow(sr1)) != NULL)
     {
     char *pop=row[0];
     printf("<table border=\"1\" cellpadding=\"0\" ><tr>");
     sqlSafef(query2, sizeof(query2),
 	  "select s1.sample, s1.gender, s1.value, c1.value, s2.value, c2.value "
 	  "from   cnpSharpSample s1, cnpSharpSample s2, cnpSharpCutoff c1, cnpSharpCutoff c2 "
 	  "where  s1.sample=s2.sample and s1.sample=c1.sample and s1.sample=c2.sample "
 	  "  and  s1.batch=1 and s2.batch=2 and c1.batch=1 and c2.batch=2 and s1.bac='%s' "
 	  "  and  s1.bac=s2.bac and s1.sample like '%s%%' order by s1.sample", itemName, pop);
     sr2 = sqlGetResult(hgFixed2, query2);
     while ((row = sqlNextRow(sr2)) != NULL)
 	{
 	if (sameString(row[1],"M")) printf("<TD width=160 bgcolor=\"#99FF99\">");
 	else                        printf("<TD width=160 bgcolor=\"#FFCCFF\">");
 	printf("%s</TD>\n",row[0]);
 	}
     printf("</TR><TR>\n");
     sqlFreeResult(&sr2);
     sr2 = sqlGetResult(hgFixed2, query2);
     while ((row = sqlNextRow(sr2)) != NULL)
 	{
 	sample = sqlFloat(row[2]);
 	cutoff = sqlFloat(row[3]);
         if (sameString(row[2],"NA"))
 	    printf("<TD width=160 >&nbsp; NA / %.3f </TD>\n",cutoff);
 	else if (sample>=cutoff)
 	    printf("<TD width=160  bgcolor=\"yellow\">&nbsp; %.3f / %.3f </TD>\n",sample,cutoff);
 	else if (sample<= 0-cutoff)
 	    printf("<TD width=160  bgcolor=\"gray\">&nbsp; %.3f / -%.3f </TD>\n",sample,cutoff);
 	else printf("<TD width=160 >&nbsp; %.3f / %.3f </TD>\n",sample,cutoff);
 	}
     printf("</TR><TR>\n");
     sqlFreeResult(&sr2);
     sr2 = sqlGetResult(hgFixed2, query2);
     while ((row = sqlNextRow(sr2)) != NULL)
 	{
 	sample = sqlFloat(row[4]);
 	cutoff = sqlFloat(row[5]);
         if (sameString(row[4],"NA"))
 	    printf("<TD width=160 >&nbsp; NA / %.3f </TD>\n",cutoff);
 	else if (sample>=cutoff)
 	    printf("<TD width=160  bgcolor=\"yellow\">&nbsp; %.3f / %.3f </TD>\n",sample,cutoff);
 	else if (sample<= 0-cutoff)
 	    printf("<TD width=160  bgcolor=\"gray\">&nbsp; %.3f / -%.3f </TD>\n",sample,cutoff);
 	else printf("<TD width=160 >&nbsp; %.3f / %.3f </TD>\n",sample,cutoff);
 	}
     sqlFreeResult(&sr2);
     printf("</tr></table>\n");
     }
 sqlFreeResult(&sr1);
 hFreeConn(&hgFixed1);
 hFreeConn(&hgFixed2);
 
 printf("<BR><B>Legend for individual values in table:</B>\n");
 printf("&nbsp;&nbsp;<table>");
 printf("<TR><TD>Title Color:</TD><TD bgcolor=\"#FFCCFF\">Female</TD><TD bgcolor=\"#99FF99\">Male</TD></TR>\n");
 printf("<TR><TD>Value Color:</TD><TD bgcolor=\"yellow\" >Above Threshold</TD><TD bgcolor=\"gray\">Below negative threshold</TD></TR>\n");
 printf("<TR><TD>Data Format:</TD><TD>Value / Threshold</TD></TR>\n");
 printf("</table>\n");
 
 }
 
 void doCnpSharp(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct cnpSharp cnpSharp;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 char variantSignal;
 char *itemCopy = cloneString(itemName);
 
 variantSignal = lastChar(itemName);
 if (variantSignal == '*')
    stripChar(itemCopy, '*');
 if (variantSignal == '?')
    stripChar(itemCopy, '?');
 if (variantSignal == '#')
    stripChar(itemCopy, '#');
 genericHeader(tdb, itemCopy);
 printf("<B>NCBI Clone Registry: </B><A href=");
 printCloneDbUrl(stdout, itemCopy);
 printf(" target=_blank>%s</A><BR>\n", itemCopy);
 if (variantSignal == '*' || variantSignal == '?' || variantSignal == '#')
     printf("<B>Note this BAC was found to be variant.   See references.</B><BR>\n");
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     cnpSharpStaticLoad(row+rowOffset, &cnpSharp);
     bedPrintPos((struct bed *)&cnpSharp, 3, tdb);
     printCnpSharpDetails(cnpSharp);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 // printCnpSharpSampleData(itemName);
 printTrackHtml(tdb);
 }
 
 
 void doCnpSharp2(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct cnpSharp2 cnpSharp2;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 printf("<B>NCBI Clone Registry: </B><A href=");
 printCloneDbUrl(stdout, itemName);
 printf(" target=_blank>%s</A><BR>\n", itemName);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     cnpSharp2StaticLoad(row+rowOffset, &cnpSharp2);
     bedPrintPos((struct bed *)&cnpSharp2, 3, tdb);
     printf("<B>Name: </B> %s <BR>\n", cnpSharp2.name);
     printf("<B>Variation type: </B> %s <BR>\n", cnpSharp2.variationType);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 // printCnpSharpSampleData(itemName);
 printTrackHtml(tdb);
 }
 
 void doDgv(struct trackDb *tdb, char *id)
 /* Details for Database of Genomic Variants (updated superset of cnp*). */
 {
 struct dgv dgv;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[512];
 int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 genericHeader(tdb, id);
 printCustomUrl(tdb, id, FALSE);
 
 sqlSafef(query, sizeof(query), "select * from %s where name = '%s' "
       "and chrom = '%s' and chromStart = %d and chromEnd = %d",
       tdb->table, id, seqName, start, end);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     dgvStaticLoad(row+rowOffset, &dgv);
     if (dgv.chromStart != dgv.thickStart ||
 	(dgv.chromEnd != dgv.thickEnd && dgv.thickEnd != dgv.chromStart))
 	{
 	printf("<B>Variant Position:</B> "
 	       "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">%s:%d-%d</A><BR>\n",
 	       hgTracksPathAndSettings(), database,
 	       dgv.chrom, dgv.thickStart+1, dgv.thickEnd,
 	       dgv.chrom, dgv.thickStart+1, dgv.thickEnd);
 	printBand(dgv.chrom, dgv.thickStart, dgv.thickEnd, FALSE);
 	printf("<B>Variant Genomic Size:</B> %d<BR>\n",
 	       dgv.thickEnd - dgv.thickStart);
 	printf("<B>Locus:</B> "
 	       "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">%s:%d-%d</A><BR>\n",
 	       hgTracksPathAndSettings(), database,
 	       dgv.chrom, dgv.chromStart+1, dgv.chromEnd,
 	       dgv.chrom, dgv.chromStart+1, dgv.chromEnd);
 	printf("<B>Locus Genomic Size:</B> %d<BR>\n",
 	       dgv.chromEnd - dgv.chromStart);
 	}
     else
 	{
 	printf("<B>Position:</B> "
 	       "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">%s:%d-%d</A><BR>\n",
 	       hgTracksPathAndSettings(), database,
 	       dgv.chrom, dgv.chromStart+1, dgv.chromEnd,
 	       dgv.chrom, dgv.chromStart+1, dgv.chromEnd);
 	printBand(dgv.chrom, dgv.chromStart, dgv.chromEnd, FALSE);
 	printf("<B>Genomic Size:</B> %d<BR>\n", dgv.chromEnd - dgv.chromStart);
 	}
     printf("<B>Variant Type:</B> %s<BR>\n", dgv.varType);
     printf("<B>Reference:</B> <A HREF=\"");
     printEntrezPubMedUidAbstractUrl(stdout, dgv.pubMedId);
     printf("\" TARGET=_BLANK>%s</A><BR>\n", dgv.reference);
     printf("<B>Method/platform:</B> %s<BR>\n", dgv.method);
     printf("<B>Sample:</B> %s<BR>\n", dgv.sample);
     if (isNotEmpty(dgv.landmark))
 	printf("<B>Landmark:</B> %s<BR>\n", dgv.landmark);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 static void maybePrintCoriellLinks(struct trackDb *tdb, char *commaSepIds)
 /* If id looks like a Coriell NA ID, print a link to Coriell, otherwise just print id. */
 {
 char *coriellUrlBase = trackDbSetting(tdb, "coriellUrlBase");
 struct slName *id, *sampleIds = slNameListFromComma(commaSepIds);
 for (id = sampleIds;  id != NULL;  id = id->next)
     {
     if (startsWith("NA", id->name) && countLeadingDigits(id->name+2) == strlen(id->name+2)
 	&& isNotEmpty(coriellUrlBase))
 	{
 	// I don't know why coriell doesn't have direct links to NA's but oh well,
 	// we can substitute 'GM' for 'NA' to get to the page...
 	char *gmId = cloneString(id->name);
 	gmId[0] = 'G';  gmId[1] = 'M';
 	printf("<A HREF=\"%s%s\" TARGET=_BLANK>%s</A>", coriellUrlBase, gmId, id->name);
 	freeMem(gmId);
 	}
     else
 	printf("%s", id->name);
     if (id->next != NULL)
 	printf(", ");
     }
 slNameFreeList(&sampleIds);
 }
 
 static void printBrowserPosLinks(char *commaSepIds)
 /* Print hgTracks links with position=id. */
 {
 struct slName *id, *sampleIds = slNameListFromComma(commaSepIds);
 for (id = sampleIds;  id != NULL;  id = id->next)
     {
     char *searchTerm = cgiEncode(trimSpaces(id->name));
     printf("<A HREF=\"%s&position=%s\">%s</A>", hgTracksPathAndSettings(), searchTerm, id->name);
     if (id->next != NULL)
 	printf(", ");
     freeMem(searchTerm);
     }
 slNameFreeList(&sampleIds);
 }
 
 void doDgvPlus(struct trackDb *tdb, char *id)
 /* Details for Database of Genomic Variants, July 2013 and later. */
 {
 struct dgvPlus dgv;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[512];
 int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 genericHeader(tdb, id);
 printCustomUrl(tdb, id, FALSE);
 
 sqlSafef(query, sizeof(query), "select * from %s where name = '%s' "
       "and chrom = '%s' and chromStart = %d and chromEnd = %d",
       tdb->table, id, seqName, start, end);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     dgvPlusStaticLoad(row+rowOffset, &dgv);
     printf("<B>Position:</B> "
 	   "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">%s:%d-%d</A><BR>\n",
 	   hgTracksPathAndSettings(), database,
 	   dgv.chrom, dgv.chromStart+1, dgv.chromEnd,
 	   dgv.chrom, dgv.chromStart+1, dgv.chromEnd);
     printBand(dgv.chrom, dgv.chromStart, dgv.chromEnd, FALSE);
     printf("<B>Genomic size:</B> %d<BR>\n", dgv.chromEnd - dgv.chromStart);
     printf("<B>Variant type:</B> %s<BR>\n", dgv.varType);
     printf("<B>Reference:</B> <A HREF=\"");
     printEntrezPubMedUidAbstractUrl(stdout, dgv.pubMedId);
     printf("\" TARGET=_BLANK>%s</A><BR>\n", dgv.reference);
     printf("<B>Method:</B> %s<BR>\n", dgv.method);
     if (isNotEmpty(dgv.platform))
 	printf("<B>Platform:</B> %s<BR>\n", dgv.platform);
     if (isNotEmpty(dgv.cohortDescription))
 	printf("<B>Sample cohort description:</B> %s<BR>\n", dgv.cohortDescription);
     if (isNotEmpty(dgv.samples))
 	{
 	printf("<B>Sample IDs:</B> ");
 	maybePrintCoriellLinks(tdb, dgv.samples);
 	printf("<BR>\n");
 	}
     printf("<B>Sample size:</B> %u<BR>\n", dgv.sampleSize);
     if (dgv.observedGains != 0 || dgv.observedLosses != 0)
 	{
 	printf("<B>Observed gains:</B> %u<BR>\n", dgv.observedGains);
 	printf("<B>Observed losses:</B> %u<BR>\n", dgv.observedLosses);
 	}
     if (isNotEmpty(dgv.mergedVariants))
 	{
 	printf("<B>Merged variants:</B> ");
 	printBrowserPosLinks(dgv.mergedVariants);
 	printf("<BR>\n");
 	}
     if (isNotEmpty(dgv.supportingVariants))
 	{
 	printf("<B>Supporting variants:</B> ");
 	printBrowserPosLinks(dgv.supportingVariants);
 	printf("<BR>\n");
 	}
     if (isNotEmpty(dgv.genes))
 	{
 	printf("<B>Genes:</B> ");
 	printBrowserPosLinks(dgv.genes);
 	printf("<BR>\n");
 	}
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 void doAffy120K(struct trackDb *tdb, char *itemName)
 /* Put up info on an Affymetrix SNP. */
 {
 char *table = tdb->table;
 struct snp snp;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset;
 
 cartWebStart(cart, database, "Single Nucleotide Polymorphism (SNP)");
 printf("<H2>Single Nucleotide Polymorphism (SNP) %s</H2>\n", itemName);
 sqlSafef(query, sizeof query, "select * "
 	       "from   affy120K "
 	       "where  chrom = '%s' "
 	       "  and  chromStart = %d "
 	       "  and  name = '%s'",
                seqName, start, itemName);
 rowOffset = hOffsetPastBin(database, seqName, table);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     snpStaticLoad(row+rowOffset, &snp);
     bedPrintPos((struct bed *)&snp, 3, tdb);
     }
 doAffy120KDetails(tdb, itemName);
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doAffy10KDetails(struct trackDb *tdb, char *name)
 /* print additional SNP details */
 {
 struct sqlConnection *conn = sqlConnect("hgFixed");
 char query[1024];
 struct affy10KDetails *snp=NULL;
 
 sqlSafef(query, sizeof(query),
          "select  affyId, rsId, tscId, baseA, baseB, "
          "sequenceA, sequenceB, enzyme "
 /** minFreq, hetzyg, and avHetSE are waiting for additional data from Affy **/
 /*	 "        , minFreq, hetzyg, avHetSE "*/
          "from    affy10KDetails "
          "where   affyId = '%s'", name);
 snp = affy10KDetailsLoadByQuery(conn, query);
 if (snp!=NULL)
     {
     printf("<BR>\n");
     printf("<B>Sample Prep Enzyme:      </B> <I>XbaI</I><BR>\n");
 /** minFreq, hetzyg, and avHetSE are waiting for additional data from Affy **/
 /*  printf("<B>Minimum Allele Frequency:</B> %.3f<BR>\n",snp->minFreq);*/
 /*  printf("<B>Heterozygosity:          </B> %.3f<BR>\n",snp->hetzyg);*/
 /*  printf("<B>Average Heterozygosity:  </B> %.3f<BR>\n",snp->avHetSE);*/
     printf("<B>Base A:                  </B> <span style='font-family:Courier;'>");
     printf("%s</span><BR>\n",snp->baseA);
     printf("<B>Base B:                  </B> <span style='font-family:Courier;'>");
     printf("%s</span><BR>\n",snp->baseB);
     printf("<B>Sequence of Allele A:    </B>&nbsp;<span style='font-family:Courier;'>");
     printf("%s</span><BR>\n",snp->sequenceA);
     printf("<B>Sequence of Allele B:    </B>&nbsp;<span style='font-family:Courier;'>");
     printf("%s</span><BR>\n",snp->sequenceB);
 
     printf("<P><A HREF=\"https://www.affymetrix.com/LinkServlet?probeset=");
     printf("%s", snp->affyId);
     printf("\" TARGET=_blank>Affymetrix NetAffx Analysis Center link for ");
     printf("%s</A></P>\n", snp->affyId);
 
     if (strncmp(snp->rsId,"unmapped",8))
 	{
 	puts("<P>");
 	printDbSnpRsUrl(snp->rsId, "dbSNP link for %s", snp->rsId);
 	puts("</P>");
 	}
     printf("<BR><A HREF=\"http://snp.cshl.org/cgi-bin/snp?name=");
     printf("%s\" TARGET=_blank>TSC link for %s</A>\n",
 	   snp->tscId, snp->tscId);
     doSnpEntrezGeneLink(tdb, snp->rsId);
     }
 /* else errAbort("<BR>Error in Query:\n%s<BR>\n",query); */
 affy10KDetailsFree(&snp);
 sqlDisconnect(&conn);
 }
 
 void doAffy10K(struct trackDb *tdb, char *itemName)
 /* Put up info on an Affymetrix SNP. */
 {
 char *table = tdb->table;
 struct snp snp;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset;
 
 cartWebStart(cart, database, "Single Nucleotide Polymorphism (SNP)");
 printf("<H2>Single Nucleotide Polymorphism (SNP) %s</H2>\n", itemName);
 sqlSafef(query, sizeof query, "select * "
 	       "from   affy10K "
 	       "where  chrom = '%s' "
 	       "  and  chromStart = %d "
 	       "  and  name = '%s'",
                seqName, start, itemName);
 rowOffset = hOffsetPastBin(database, seqName, table);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     snpStaticLoad(row+rowOffset, &snp);
     bedPrintPos((struct bed *)&snp, 3, tdb);
     }
 doAffy10KDetails(tdb, itemName);
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void printSnpOrthoSummary(struct trackDb *tdb, char *rsId, char *observed)
 /* helper function for printSnp125Info */
 {
 char *orthoTable = snp125OrthoTable(tdb, NULL);
 if (isNotEmpty(orthoTable) && hTableExists(database, orthoTable))
     {
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     char query[512];
     sqlSafef(query, sizeof(query),
           "select chimpAllele from %s where name='%s'", orthoTable, rsId);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
 	printf("<B>Summary: </B>%s>%s (chimp allele displayed first, "
 	       "then '>', then human alleles)<br>\n", row[0], observed);
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 }
 
 #define FOURBLANKCELLS "<TD></TD><TD></TD><TD></TD><TD></TD>"
 
 static char *abbreviateAllele(char *allele)
 /* If allele is >50bp then return an abbreviated version with first & last 20 bases and length;
  * otherwise just return (cloned) allele. */
 {
 int length = strlen(allele);
 if (length > 50)
     {
     struct dyString *dyAbbr = dyStringCreate("%.20s", allele);
     dyStringAppend(dyAbbr, "...");
     dyStringAppend(dyAbbr, allele+length - 20);
     dyStringPrintf(dyAbbr, " (%d bases)", length);
     return dyStringCannibalize(&dyAbbr);
     }
 return cloneString(allele);
 }
 
 void printSnpAlleleRows(struct snp125 *snp, int version)
 /* Print the UCSC ref allele (and dbSNP if it differs), as row(s) of a
  * 6-column table. */
 {
 if (sameString(snp->strand,"+") ||
     strchr(snp->refUCSC, '(')) // don't try to revComp refUCSC if it is "(N bp insertion)" etc.
     {
     printf("<TR><TD><B>Reference allele:&nbsp;</B></TD>"
 	   "<TD align=center>%s</TD>"FOURBLANKCELLS"</TR>\n", abbreviateAllele(snp->refUCSC));
     if (!sameString(snp->refUCSC, snp->refNCBI))
 	printf("<TR><TD><B>dbSnp reference allele:&nbsp;</B></TD>"
 	       "<TD align=center>%s</TD>"FOURBLANKCELLS"</TR>\n", abbreviateAllele(snp->refNCBI));
     }
 else if (sameString(snp->strand,"-"))
     {
     char *refUCSCRevComp = cloneString(snp->refUCSC);
     reverseComplement(refUCSCRevComp, strlen(refUCSCRevComp));
     printf("<TR><TD><B>Reference allele:&nbsp;</B></TD>"
 	   "<TD align=center>%s</TD>"FOURBLANKCELLS"</TR>\n", abbreviateAllele(refUCSCRevComp));
     if (version < 127 && !sameString(refUCSCRevComp, snp->refNCBI))
 	printf("<TR><TD><B>dbSnp reference allele:&nbsp;</B></TD>"
 	       "<TD align=center>%s</TD>"FOURBLANKCELLS"</TR>\n", abbreviateAllele(snp->refNCBI));
     else if (version >= 127 && !sameString(snp->refUCSC, snp->refNCBI))
 	{
 	char *refNCBIRevComp = cloneString(snp->refNCBI);
         if (! strchr(snp->refNCBI, '('))
             reverseComplement(refNCBIRevComp, strlen(refNCBIRevComp));
 	printf("<TR><TD><B>dbSnp reference allele:&nbsp;</B></TD>"
 	       "<TD align=center>%s</TD>"FOURBLANKCELLS"</TR>\n",
 	       abbreviateAllele(refNCBIRevComp));
 	}
     }
 }
 
 #define TINYPADDING 3
 void printSnpOrthoOneRow(char *orthoName, char *orthoDb,
 			 char *orthoAllele, char *orthoStrand,
 			 char *orthoChrom, int orthoStart, int orthoEnd)
 /* Print out a 6-column table row describing an orthologous allele. */
 {
 printf("<TR><TD><B>%s allele:&nbsp;</B></TD>"
        "<TD align=center>%s</TD>\n", orthoName, orthoAllele);
 if (!sameString(orthoAllele, "?"))
     {
     printf("<TD>&nbsp;&nbsp;&nbsp;<B>%s strand:&nbsp;</B></TD>"
 	   "<TD>%s</TD>\n", orthoName, orthoStrand);
     printf("<TD>&nbsp;&nbsp;&nbsp;<B>%s position:&nbsp;</B></TD>"
 	   "<TD>\n", orthoName);
     if (isNotEmpty(orthoDb))
 	linkToOtherBrowser(orthoDb, orthoChrom,
 			   orthoStart-TINYPADDING, orthoEnd+TINYPADDING);
     printf("%s:%d-%d\n", orthoChrom, orthoStart+1, orthoEnd);
     printf("%s</TD>\n",
 	   isNotEmpty(orthoDb) ? "</A>" : "");
     }
 else
     printf(FOURBLANKCELLS"\n");
 printf("</TR>\n");
 }
 
 
 void printSnpOrthoRows(struct trackDb *tdb, struct snp125 *snp)
 /* If a chimp+macaque ortho table was specified, print out the orthos
  * (if any), as rows of a 6-column table. */
 {
 int speciesCount = 0;
 char *orthoTable = snp125OrthoTable(tdb, &speciesCount);
 if (isNotEmpty(orthoTable) && hTableExists(database, orthoTable))
     {
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     char query[1024];
     if (speciesCount == 2)
 	sqlSafef(query, sizeof(query),
 	 "select chimpChrom, chimpStart, chimpEnd, chimpAllele, chimpStrand, "
 	 "macaqueChrom, macaqueStart, macaqueEnd, macaqueAllele, macaqueStrand "
 	 "from %s where chrom='%s' and bin=%d and chromStart=%d and name='%s'",
 	 orthoTable, seqName, binFromRange(snp->chromStart, snp->chromEnd),
 	 snp->chromStart, snp->name);
     else
 	sqlSafef(query, sizeof(query),
 	 "select chimpChrom, chimpStart, chimpEnd, chimpAllele, chimpStrand, "
 	 "orangChrom, orangStart, orangEnd, orangAllele, orangStrand, "
 	 "macaqueChrom, macaqueStart, macaqueEnd, macaqueAllele, macaqueStrand "
 	 "from %s where chrom='%s' and bin=%d and chromStart=%d and name='%s'",
 	 orthoTable, seqName, binFromRange(snp->chromStart, snp->chromEnd),
 	 snp->chromStart, snp->name);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
 	{
 	char *chimpChrom = row[0];
 	int chimpStart = sqlUnsigned(row[1]);
 	int chimpEnd = sqlUnsigned(row[2]);
 	char *chimpAllele = row[3];
 	char *chimpStrand = row[4];
 	char *chimpDb = trackDbSetting(tdb, "chimpDb");
 	printSnpOrthoOneRow("Chimp", chimpDb, chimpAllele, chimpStrand,
 			    chimpChrom, chimpStart, chimpEnd);
 	char *orangChrom, *orangAllele, *orangStrand, *orangDb;
 	int orangStart, orangEnd;
 	char *macaqueChrom, *macaqueAllele, *macaqueStrand, *macaqueDb;
 	int macaqueStart, macaqueEnd;
 	if (speciesCount == 2)
 	    {
 	    macaqueChrom = row[5];
 	    macaqueStart = sqlUnsigned(row[6]);
 	    macaqueEnd = sqlUnsigned(row[7]);
 	    macaqueAllele = row[8];
 	    macaqueStrand = row[9];
 	    macaqueDb = trackDbSetting(tdb, "macaqueDb");
 	    }
 	else
 	    {
 	    orangChrom = row[5];
 	    orangStart = sqlUnsigned(row[6]);
 	    orangEnd = sqlUnsigned(row[7]);
 	    orangAllele = row[8];
 	    orangStrand = row[9];
 	    orangDb = trackDbSetting(tdb, "orangDb");
 	    printSnpOrthoOneRow("Orangutan", orangDb, orangAllele, orangStrand,
 				orangChrom, orangStart, orangEnd);
 	    macaqueChrom = row[10];
 	    macaqueStart = sqlUnsigned(row[11]);
 	    macaqueEnd = sqlUnsigned(row[12]);
 	    macaqueAllele = row[13];
 	    macaqueStrand = row[14];
 	    macaqueDb = trackDbSetting(tdb, "macaqueDb");
 	    }
 	printSnpOrthoOneRow("Macaque", macaqueDb, macaqueAllele, macaqueStrand,
 			    macaqueChrom, macaqueStart, macaqueEnd);
 	sqlFreeResult(&sr);
 	hFreeConn(&conn);
 	}
     }
 }
 
 void printSnpAlleleAndOrthos(struct trackDb *tdb, struct snp125 *snp,
 			     int version)
 /* Print the UCSC ref allele (and dbSNP if it differs).  If a
  * chimp+macaque ortho table was specified, print out the orthos (if
  * any).  Wrap a table around them all so that if there are dbSNP,
  * chimp and/or macaque alleles, we can line them up nicely with the
  * reference allele. */
 {
 printf("<TABLE border=0 cellspacing=0 cellpadding=0>\n");
 printSnpAlleleRows(snp, version);
 printSnpOrthoRows(tdb, snp);
 printf("</TABLE>");
 }
 
 static char getSnpTxBase(struct genePred *gene, int exonIx, int snpStart, int offset)
 /* Find the reference assembly base that is offset bases from snpStart in the translated sequence
  * of the gene. */
 // Room for improvement: check for mRNA sequence associated with this gene, and use it if exists.
 {
 char base = 'N';
 boolean ranOffEnd = FALSE;
 int i;
 int snpPlusOffset = snpStart;
 if (offset >= 0)
     {
     int exonEnd = gene->exonEnds[exonIx];
     for (i = 0;  i < offset;  i++)
 	{
 	snpPlusOffset++;
 	if (exonEnd <= snpPlusOffset)
 	    {
 	    if (++exonIx < gene->exonCount)
 		{
 		exonEnd = gene->exonEnds[exonIx];
 		snpPlusOffset = gene->exonStarts[exonIx];
 		}
 	    else
 		ranOffEnd = TRUE;
 	    }
 	}
     }
 else
     {
     int exonStart = gene->exonStarts[exonIx];
     for (i = 0;  i > offset;  i--)
 	{
 	snpPlusOffset--;
 	if (exonStart > snpPlusOffset)
 	    {
 	    if (--exonIx >= 0)
 		{
 		exonStart = gene->exonStarts[exonIx];
 		snpPlusOffset = gene->exonEnds[exonIx] - 1;
 		}
 	    else
 		ranOffEnd = TRUE;
 	    }
 	}
     }
 if (! ranOffEnd)
     {
     struct dnaSeq *seq =
 	hDnaFromSeq(database, gene->chrom, snpPlusOffset, snpPlusOffset+1, dnaUpper);
     base = seq->dna[0];
     }
 return base;
 }
 
 char *getSymbolForGeneName(char *geneTable, char *geneId)
 /* Given a gene track and gene accession, look up the symbol if we know where to look
  * and if we find it, return a string with both symbol and acc. */
 {
 struct dyString *dy = dyStringNew(32);
 char buf[256];
 char *sym = NULL;
 if (sameString(geneTable, "knownGene") || sameString(geneTable, "refGene"))
     {
     struct sqlConnection *conn = hAllocConn(database);
     char query[256];
     query[0] = '\0';
     if (sameString(geneTable, "knownGene"))
 	sqlSafef(query, sizeof(query), "select geneSymbol from kgXref where kgID = '%s'", geneId);
     else if (sameString(geneTable, "refGene"))
 	sqlSafef(query, sizeof(query), "select name from %s where mrnaAcc = '%s'", refLinkTable, geneId);
     sym = sqlQuickQuery(conn, query, buf, sizeof(buf)-1);
     hFreeConn(&conn);
     }
 if (sym != NULL)
     dyStringPrintf(dy, "%s (%s)", sym, geneId);
 else
     dyStringAppend(dy, geneId);
 return dyStringCannibalize(&dy);
 }
 
 #define firstTwoColumnsPctS "<TR><TD>%s&nbsp;&nbsp;</TD><TD>%s&nbsp;</TD><TD>"
 
 void getSnp125RefCodonAndSnpPos(struct snp125 *snp, struct genePred *gene, int exonIx,
 				int *pSnpCodonPos, char refCodon[4], char *pRefAA)
 /* Given a single-base snp and a coding gene/exon containing it, determine the snp's position
  * in the codon and the reference codon & amino acid. */
 {
 boolean geneIsRc = sameString(gene->strand, "-");
 int snpStart = snp->chromStart, snpEnd = snp->chromEnd;
 int exonStart = gene->exonStarts[exonIx], exonEnd = gene->exonEnds[exonIx];
 int cdsStart = gene->cdsStart, cdsEnd = gene->cdsEnd;
 int exonFrame = gene->exonFrames[exonIx];
 if (exonFrame == -1)
     exonFrame = 0;
 if (cdsEnd < exonEnd) exonEnd = cdsEnd;
 if (cdsStart > exonStart) exonStart = cdsStart;
 int snpCodonPos = geneIsRc ? (2 - ((exonEnd - snpEnd) + exonFrame) % 3) :
 			     (((snpStart - exonStart) + exonFrame) % 3);
 refCodon[0] = getSnpTxBase(gene, exonIx, snpStart, -snpCodonPos);
 refCodon[1] = getSnpTxBase(gene, exonIx, snpStart, 1 - snpCodonPos);
 refCodon[2] = getSnpTxBase(gene, exonIx, snpStart, 2 - snpCodonPos);
 refCodon[3] = '\0';
 if (geneIsRc)
     {
     reverseComplement(refCodon, strlen(refCodon));
     snpCodonPos = 2 - snpCodonPos;
     }
 if (pSnpCodonPos != NULL)
     *pSnpCodonPos = snpCodonPos;
 if (pRefAA != NULL)
     {
     *pRefAA = lookupCodon(refCodon);
     if (*pRefAA == '\0') *pRefAA = '*';
     }
 }
 
 static char *highlightCodonBase(char *codon, int offset)
 /* If codon is a triplet and offset is 0 to 2, highlight the base at the offset.
  * Otherwise just return the given codon sequence unmodified.
  * Don't free the return value! */
 {
 static struct dyString *dy = NULL;
 if (dy == NULL)
     dy = dyStringNew(0);
 dyStringClear(dy);
 if (strlen(codon) != 3)
     dyStringAppend(dy, codon);
 else if (offset == 0)
     dyStringPrintf(dy, "<B>%c</B>%c%c", codon[0], codon[1], codon[2]);
 else if (offset == 1)
     dyStringPrintf(dy, "%c<B>%c</B>%c", codon[0], codon[1], codon[2]);
 else if (offset == 2)
     dyStringPrintf(dy, "%c%c<B>%c</B>", codon[0], codon[1], codon[2]);
 else
     dyStringAppend(dy, codon);
 return dy->string;
 }
 
 void printSnp125FunctionInCDS(struct snp125 *snp, char *geneTable, char *geneTrack,
 			      struct genePred *gene, int exonIx, char *geneName)
 /* Show the effect of each observed allele of snp on the given exon of gene. */
 {
 char *refAllele = cloneString(snp->refUCSC);
 boolean refIsAlpha = isalpha(refAllele[0]);
 boolean geneIsRc = sameString(gene->strand, "-"), snpIsRc = sameString(snp->strand, "-");
 if (geneIsRc && refIsAlpha)
     reverseComplement(refAllele, strlen(refAllele));
 int refAlleleSize = sameString(refAllele, "-") ? 0 : refIsAlpha ? strlen(refAllele) : -1;
 boolean refIsSingleBase = (refAlleleSize == 1 && refIsAlpha);
 int snpCodonPos = 0;
 char refCodon[4], refAA = '\0';
 if (refIsSingleBase)
     getSnp125RefCodonAndSnpPos(snp, gene, exonIx, &snpCodonPos, refCodon, &refAA);
 char *alleleStr = cloneString(snp->observed);
 char *indivAlleles[64];
 int alleleCount = chopString(alleleStr, "/", indivAlleles, ArraySize(indivAlleles));
 int j;
 for (j = 0;  j < alleleCount;  j++)
     {
     char *al = indivAlleles[j];
     boolean alIsAlpha = (isalpha(al[0]) && !sameString(al, "lengthTooLong"));
     if ((snpIsRc ^ geneIsRc) && alIsAlpha)
 	reverseComplement(al, strlen(al));
     char alBase = al[0];
     if (alBase == '\0' || sameString(al, refAllele))
 	continue;
     int alSize = sameString(al, "-") ? 0 : alIsAlpha ? strlen(al) : -1;
     if (alSize != refAlleleSize && alSize >= 0 && refAlleleSize >=0)
 	{
 
 	int diff = alSize - refAlleleSize;
 	if ((diff % 3) != 0)
 	    printf(firstTwoColumnsPctS "%s\n",
 		   geneTrack, geneName, snpMisoLinkFromFunc("frameshift"));
 	else if (diff > 0)
 	    printf(firstTwoColumnsPctS "%s (insertion of %d codon%s)\n",
 		   geneTrack, geneName, snpMisoLinkFromFunc("inframe_insertion"),
 		   (int)(diff/3), (diff > 3) ?  "s" : "");
 	else
 	    printf(firstTwoColumnsPctS "%s (deletion of %d codon%s)\n",
 		   geneTrack, geneName, snpMisoLinkFromFunc("inframe_deletion"),
 		   (int)(-diff/3), (diff < -3) ?  "s" : "");
 	}
     else if (alSize == 1 && refIsSingleBase)
 	{
 	char snpCodon[4];
 	safecpy(snpCodon, sizeof(snpCodon), refCodon);
 	snpCodon[snpCodonPos] = alBase;
 	char snpAA = lookupCodon(snpCodon);
 	if (snpAA == '\0') snpAA = '*';
 	char refCodonHtml[16], snpCodonHtml[16];
 	safecpy(refCodonHtml, sizeof(refCodonHtml), highlightCodonBase(refCodon, snpCodonPos));
 	safecpy(snpCodonHtml, sizeof(snpCodonHtml), highlightCodonBase(snpCodon, snpCodonPos));
 	if (refAA != snpAA)
 	    {
 	    if (refAA == '*')
 		printf(firstTwoColumnsPctS "%s %c (%s) --> %c (%s)\n",
 		       geneTrack, geneName, snpMisoLinkFromFunc("stop-loss"),
 		       refAA, refCodonHtml, snpAA, snpCodonHtml);
 	    else if (snpAA == '*')
 		printf(firstTwoColumnsPctS "%s %c (%s) --> %c (%s)\n",
 		       geneTrack, geneName, snpMisoLinkFromFunc("nonsense"),
 		       refAA, refCodonHtml, snpAA, snpCodonHtml);
 	    else
 		printf(firstTwoColumnsPctS "%s %c (%s) --> %c (%s)\n",
 		       geneTrack, geneName, snpMisoLinkFromFunc("missense"),
 		       refAA, refCodonHtml, snpAA, snpCodonHtml);
 	    }
 	else
 	    {
 	    if (refAA == '*')
 		printf(firstTwoColumnsPctS "%s %c (%s) --> %c (%s)\n",
 		       geneTrack, geneName, snpMisoLinkFromFunc("stop_retained_variant"),
 		       refAA, refCodonHtml, snpAA, snpCodonHtml);
 	    else
 		printf(firstTwoColumnsPctS "%s %c (%s) --> %c (%s)\n",
 		       geneTrack, geneName, snpMisoLinkFromFunc("coding-synon"),
 		       refAA, refCodonHtml, snpAA, snpCodonHtml);
 	    }
 	}
     else
 	printf(firstTwoColumnsPctS "%s %s --> %s\n",
 	       geneTrack, geneName, snpMisoLinkFromFunc("cds-synonymy-unknown"),
                abbreviateAllele(refAllele), abbreviateAllele(al));
     }
 }
 
 void printSnp125FunctionInGene(struct snp125 *snp, char *geneTable, char *geneTrack,
 			       struct genePred *gene)
 /* Given a SNP and a gene that overlaps it, say where in the gene it overlaps
  * and if in CDS, say what effect the coding alleles have. */
 {
 int snpStart = snp->chromStart, snpEnd = snp->chromEnd;
 int cdsStart = gene->cdsStart, cdsEnd = gene->cdsEnd;
 boolean geneIsRc = sameString(gene->strand, "-");
 char *geneName = getSymbolForGeneName(geneTable, gene->name);
 int i, iStart = 0, iEnd = gene->exonCount, iIncr = 1;
 if (geneIsRc)
     { iStart = gene->exonCount - 1;  iEnd = -1;  iIncr = -1; }
 for (i = iStart;  i != iEnd;  i += iIncr)
     {
     int exonStart = gene->exonStarts[i], exonEnd = gene->exonEnds[i];
     if (snpEnd > exonStart && snpStart < exonEnd)
 	{
 	if (snpEnd > cdsStart && snpStart < cdsEnd)
 	    printSnp125FunctionInCDS(snp, geneTable, geneTrack, gene, i, geneName);
 	else if (cdsEnd > cdsStart)
 	    {
 	    boolean is5Prime = ((geneIsRc && (snpStart >= cdsEnd)) ||
 				(!geneIsRc && (snpEnd < cdsStart)));
 	    printf(firstTwoColumnsPctS "%s\n", geneTrack, geneName,
 		   snpMisoLinkFromFunc((is5Prime) ? "untranslated-5" : "untranslated-3"));
 	    }
 	else
 	    printf(firstTwoColumnsPctS "%s\n", geneTrack, geneName,
 		   snpMisoLinkFromFunc("ncRNA"));
 	}
     // SO term splice_region_variant applies to first/last 3 bases of exon
     // and first/last 3-8 bases of intron
     if ((i > 0 && snpStart < exonStart+3 && snpEnd > exonStart) ||
 	(i < gene->exonCount-1 && snpStart < exonEnd && snpEnd > exonEnd-3))
 	printf(", %s", snpMisoLinkFromFunc("splice_region_variant"));
     puts("</TD></TR>");
     if (i > 0)
 	{
 	int intronStart = gene->exonEnds[i-1], intronEnd = gene->exonStarts[i];
 	if (snpEnd < intronStart || snpStart > intronEnd)
 	    continue;
 	if (snpStart < intronStart+2 && snpEnd > intronStart)
 	    printf(firstTwoColumnsPctS "%s</TD></TR>\n", geneTrack, geneName,
 		   snpMisoLinkFromFunc(geneIsRc ? "splice-3" : "splice-5"));
 	else if (snpStart < intronStart+8 && snpEnd > intronStart+2)
 	    printf(firstTwoColumnsPctS "%s, %s</TD></TR>\n", geneTrack, geneName,
 		   snpMisoLinkFromFunc("intron_variant"),
 		   snpMisoLinkFromFunc("splice_region_variant"));
 	else if (snpStart < intronEnd-8 && snpEnd > intronStart+8)
 	    printf(firstTwoColumnsPctS "%s</TD></TR>\n", geneTrack, geneName,
 		   snpMisoLinkFromFunc("intron"));
 	else if (snpStart < intronEnd-2 && snpEnd > intronEnd-8)
 	    printf(firstTwoColumnsPctS "%s, %s</TD></TR>\n", geneTrack, geneName,
 		   snpMisoLinkFromFunc("intron_variant"),
 		   snpMisoLinkFromFunc("splice_region_variant"));
 	else if (snpStart < intronEnd && snpEnd > intronEnd-2)
 	    printf(firstTwoColumnsPctS "%s</TD></TR>\n", geneTrack, geneName,
 		   snpMisoLinkFromFunc(geneIsRc ? "splice-5" : "splice-3"));
 	}
     }
 }
 
 void printSnp125NearGenes(struct sqlConnection *conn, struct snp125 *snp, char *geneTable,
 			  char *geneTrack)
 /* Search upstream and downstream of snp for neigh */
 {
 struct sqlResult *sr;
 char query[512];
 char **row;
 int snpStart = snp->chromStart, snpEnd = snp->chromEnd;
 int nearCount = 0;
 int maxDistance = 10000;
 /* query to the left: */
 sqlSafef(query, sizeof(query), "select name,txEnd,strand from %s "
       "where chrom = '%s' and txStart < %d and txEnd > %d",
       geneTable, snp->chrom, snpStart, snpStart - maxDistance);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     char *gene = row[0];
     char *geneName = getSymbolForGeneName(geneTable, gene);
     int end = sqlUnsigned(row[1]);
     char *strand = row[2];
     boolean isRc = strand[0] == '-';
     printf(firstTwoColumnsPctS "%s (%d bases %sstream)</TD></TR>\n",
 	   geneTrack, geneName, snpMisoLinkFromFunc(isRc ? "near-gene-5" : "near-gene-3"),
 	   (snpStart - end + 1), (isRc ? "up" : "down"));
     nearCount++;
     }
 sqlFreeResult(&sr);
 /* query to the right: */
 sqlSafef(query, sizeof(query), "select name,txStart,strand from %s "
       "where chrom = '%s' and txStart < %d and txEnd > %d",
       geneTable, snp->chrom, snpEnd + maxDistance, snpEnd);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     char *gene = row[0];
     char *geneName = getSymbolForGeneName(geneTable, gene);
     int start = sqlUnsigned(row[1]);
     char *strand = row[2];
     boolean isRc = strand[0] == '-';
     printf(firstTwoColumnsPctS "%s (%d bases %sstream)</TD></TR>\n",
 	   geneTrack, geneName, snpMisoLinkFromFunc(isRc ? "near-gene-3" : "near-gene-5"),
 	   (start - snpEnd + 1), (isRc ? "down" : "up"));
     nearCount++;
     }
 sqlFreeResult(&sr);
 if (nearCount == 0)
     printf("<TR><TD>%s&nbsp;&nbsp;</TD><TD></TD><TD>%s</TD></TR>", geneTrack,
            snpMisoLinkFromFunc("intergenic_variant"));
 }
 
 static struct genePred *getGPsWithFrames(struct sqlConnection *conn, char *geneTable,
 					 char *chrom, int start, int end)
 /* Given a known-to-exist genePred table name and a range, return
  * genePreds in range with exonFrames populated. */
 {
 struct genePred *gpList = NULL;
 boolean hasBin;
 struct sqlResult *sr = hRangeQuery(conn, geneTable, chrom, start, end, NULL, &hasBin);
 struct sqlConnection *conn2 = hAllocConn(database);
 boolean hasFrames = (sqlFieldIndex(conn2, geneTable, "exonFrames") == hasBin + 14);
 char **row;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     int fieldCount = hasBin + (hasFrames ? 15 : 10);
     struct genePred *gp;
     if (hasFrames)
         {
 	gp = genePredExtLoad(row+hasBin, fieldCount);
         // Some tables have an exonFrames column but it's empty...
         if (gp->exonFrames == NULL)
             genePredAddExonFrames(gp);
         }
     else
 	{
 	gp = genePredLoad(row+hasBin);
 	genePredAddExonFrames(gp);
 	}
     slAddHead(&gpList, gp);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn2);
 return gpList;
 }
 
 void printSnp125Function(struct trackDb *tdb, struct snp125 *snp)
 /* If the user has selected a gene track for functional annotation,
  * report how this SNP relates to any nearby genes. */
 {
 char varName[512];
 safef(varName, sizeof(varName), "%s_geneTrack", tdb->track);
 struct slName *geneTracks = cartOptionalSlNameList(cart, varName);
 if (geneTracks == NULL && !cartListVarExists(cart, varName))
     {
     char *defaultGeneTracks = trackDbSetting(tdb, "defaultGeneTracks");
     if (isNotEmpty(defaultGeneTracks))
 	geneTracks = slNameListFromComma(defaultGeneTracks);
     else
 	return;
     }
 struct sqlConnection *conn = hAllocConn(database);
 struct slName *gt;
 boolean first = TRUE;
 for (gt = geneTracks;  gt != NULL;  gt = gt->next)
     if (sqlTableExists(conn, gt->name))
 	{
 	if (first)
 	    {
 	    printf("<BR><B>UCSC's predicted function relative to selected gene tracks:</B>\n");
 	    printf("<TABLE border=0 cellspacing=0 cellpadding=0>\n");
 	    }
 	struct genePred *geneList = getGPsWithFrames(conn, gt->name, snp->chrom,
 						     snp->chromStart, snp->chromEnd);
 	struct genePred *gene;
 	char query[256];
 	char buf[256];
 	sqlSafef(query, sizeof(query), "select shortLabel from trackDb where tableName='%s'",
 	      gt->name);
 	char *shortLabel = sqlQuickQuery(conn, query, buf, sizeof(buf)-1);
 	if (shortLabel == NULL) shortLabel = gt->name;
 	for (gene = geneList;  gene != NULL;  gene = gene->next)
 	    printSnp125FunctionInGene(snp, gt->name, shortLabel, gene);
 	if (geneList == NULL)
 	    printSnp125NearGenes(conn, snp, gt->name, shortLabel);
 	first = FALSE;
 	}
 if (! first)
     printf("</TABLE>\n");
 hFreeConn(&conn);
 }
 
 char *dbSnpFuncFromInt(unsigned char funcCode)
 /* Translate an integer function code from NCBI into an abbreviated description.
  * Do not free return value! */
 // Might be a good idea to flesh this out with all codes, libify, and share with
 // snpNcbiToUcsc instead of partially duplicating.
 {
 switch (funcCode)
     {
     case 3:
 	return "coding-synon";
     case 8:
 	return "cds-reference";
     case 41:
 	return "nonsense";
     case 42:
 	return "missense";
     case 43:
 	return "stop-loss";
     case 44:
 	return "frameshift";
     case 45:
 	return "cds-indel";
     default:
 	{
 	static char buf[16];
 	safef(buf, sizeof(buf), "%d", funcCode);
 	return buf;
 	}
     }
 
 }
 
 void printSnp125CodingAnnotations(struct trackDb *tdb, struct snp125 *snp)
 /* If tdb specifies extra table(s) that contain protein-coding annotations,
  * show the effects of SNP on transcript coding sequences. */
 {
 char *tables = trackDbSetting(tdb, "codingAnnotations");
 if (isEmpty(tables))
     return;
 struct sqlConnection *conn = hAllocConn(database);
 struct slName *tbl, *tableList = slNameListFromString(tables, ',');
 struct dyString *query = dyStringNew(0);
 for (tbl = tableList;  tbl != NULL;  tbl = tbl->next)
     {
     if (!sqlTableExists(conn, tbl->name))
 	continue;
     char setting[512];
     safef(setting, sizeof(setting), "codingAnnoLabel_%s", tbl->name);
     char *label = trackDbSettingOrDefault(tdb, setting, NULL);
     if (label == NULL && endsWith(tbl->name, "DbSnp"))
 	label = "dbSNP";
     else
 	label = tbl->name;
     boolean hasBin = hIsBinned(database, tbl->name);
     boolean hasCoords = (sqlFieldIndex(conn, tbl->name, "chrom") != -1);
     int rowOffset = hasBin + (hasCoords ? 3 : 0);
     dyStringClear(query);
     sqlDyStringPrintf(query, "select * from %s where name = '%s'", tbl->name, snp->name);
     if (hasCoords)
 	sqlDyStringPrintf(query, " and chrom = '%s' and chromStart = %d", seqName, snp->chromStart);
     struct sqlResult *sr = sqlGetResult(conn, query->string);
     char **row;
     boolean first = TRUE;
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	if (first)
 	    {
 	    printf("<BR><B>Coding annotations by %s:</B><BR>\n", label);
 	    first = FALSE;
 	    }
 	struct snp125CodingCoordless *anno = snp125CodingCoordlessLoad(row+rowOffset);
 	int i;
 	boolean gotRef = (anno->funcCodes[0] == 8);
 	for (i = 0;  i < anno->alleleCount;  i++)
 	    {
 	    memSwapChar(anno->peptides[i], strlen(anno->peptides[i]), 'X', '*');
 	    if (anno->funcCodes[i] == 8)
 		continue;
 	    char *txName = anno->transcript;
 	    if (startsWith("NM_", anno->transcript))
 		txName = getSymbolForGeneName("refGene", anno->transcript);
 	    char *func = dbSnpFuncFromInt(anno->funcCodes[i]);
 	    printf("%s: %s ", txName, snpMisoLinkFromFunc(func));
 	    if (sameString(func, "frameshift") || sameString(func, "cds-indel"))
 		{
 		puts("<BR>");
 		continue;
 		}
 	    if (gotRef)
 		printf("%s (%s) --> ", anno->peptides[0],
 		       highlightCodonBase(anno->codons[0], anno->frame));
 	    printf("%s (%s)<BR>\n", anno->peptides[i],
 		   highlightCodonBase(anno->codons[i], anno->frame));
 	    }
 	}
     sqlFreeResult(&sr);
     }
 hFreeConn(&conn);
 }
 
 void printSnp132ExtraColumns(struct trackDb *tdb, struct snp132Ext *snp)
 /* Print columns new in snp132 */
 {
 // Skip exceptions column; handled below in writeSnpExceptionWithVersion
 printf("<TR><TD><B><A HREF=\"#Submitters\">Submitter Handles</A>&nbsp;&nbsp;</TD><TD></B>");
 int i;
 for (i=0;  i < snp->submitterCount;  i++)
     printf("%s<A HREF=\"http://www.ncbi.nlm.nih.gov/SNP/snp_viewTable.cgi?h=%s\" TARGET=_BLANK>"
 	   "%s</A>", (i > 0 ? ", " : ""), snp->submitters[i], snp->submitters[i]);
 printf("</TD></TR>\n");
 if (snp->alleleFreqCount > 0)
     {
     boolean gotNonIntN = FALSE;
     double total2NDbl = 0.0;
     for (i = 0;  i < snp->alleleFreqCount;  i++)
         total2NDbl += snp->alleleNs[i];
     int total2N = round(total2NDbl);
     printf("<TR><TD><B><A HREF=\"#AlleleFreq\">Allele Frequencies</A>&nbsp;&nbsp;</B></TD><TD>");
     for (i = 0;  i < snp->alleleFreqCount;  i++)
 	{
 	printf("%s%s: %.3f%% ", (i > 0 ? "; " : ""), snp->alleles[i], (snp->alleleFreqs[i]*100.0));
 	// alleleNs should be integers (counts of chromosomes in which allele was observed)
 	// but dbSNP extrapolates them from reported frequency and reported sample count,
 	// so sometimes they are not integers.  Present them as integers when we can, warn
 	// when we can't.
 	double f = snp->alleleFreqs[i], n = snp->alleleNs[i];
 	if (f > 0)
 	    {
 	    int roundedN = round(n);
 	    if (fabs(n - roundedN) < 0.01)
 		printf("(%d / %d)", roundedN, total2N);
 	    else
 		{
 		gotNonIntN = TRUE;
 		printf("(%.3f / %.3f)", n, total2NDbl);
 		}
 	    }
 	}
     printf("</TR></TABLE>\n");
     if (gotNonIntN)
 	printf(" <em>Note: dbSNP extrapolates allele counts from reported frequencies and "
 	       "reported 2N sample sizes (total %d); non-integer allele count may imply "
 	       "an inaccuracy in one of the reported numbers.</em><BR>\n", total2N);
     }
 else
     puts("</TABLE>");
 if (isNotEmpty(snp->bitfields) && differentString(snp->bitfields, "unknown"))
     {
     printf("<BR><B><A HREF=\"#Bitfields\">Miscellaneous properties annotated by dbSNP</A>:"
 	   "</B>\n\n");
     struct slName *bitfields = slNameListFromComma(snp->bitfields);
     if (slNameInList(bitfields, "clinically-assoc"))
 	printf("<BR> SNP is in OMIM/OMIA and/or "
 	       "at least one submitter is a Locus-Specific Database "
 	       "(&quot;clinically associated&quot;)\n");
     if (slNameInList(bitfields, "has-omim-omia"))
 	printf("<BR> SNP is in OMIM/OMIA\n");
     if (slNameInList(bitfields, "microattr-tpa"))
 	printf("<BR> SNP has a microattribution or third-party annotation\n");
     if (slNameInList(bitfields, "submitted-by-lsdb"))
 	printf("<BR> SNP was submitted by Locus-Specific Database\n");
     if (slNameInList(bitfields, "maf-5-all-pops"))
 	printf("<BR> Minor Allele Frequency is at least 5%% in all "
 	       "populations assayed\n");
     else if (slNameInList(bitfields, "maf-5-some-pop"))
 	printf("<BR> Minor Allele Frequency is at least 5%% in at least one "
 	       "population assayed\n");
     if (slNameInList(bitfields, "genotype-conflict"))
 	printf("<BR> Quality check: Different genotypes have been submitted "
 	       "for the same individual\n");
     if (slNameInList(bitfields, "rs-cluster-nonoverlapping-alleles"))
 	printf("<BR> Quality check: The reference SNP cluster contains "
 	       "submitted SNPs with non-overlapping alleles\n");
     if (slNameInList(bitfields, "observed-mismatch"))
 	printf("<BR> Quality check: The reference sequence allele at the "
 	       "mapped position is not present in the SNP allele list\n");
     puts("<BR>");
     }
 }
 
 // Defined below -- call has since moved up from doSnpWithVersion to printSnp125Info
 // so exceptions appear before our coding annotations.
 void writeSnpExceptionWithVersion(struct trackDb *tdb, struct snp132Ext *snp, int version);
 /* Print out descriptions of exceptions, if any, for this snp. */
 
 void printSnp125Info(struct trackDb *tdb, struct snp132Ext *snp, int version)
 /* print info on a snp125 */
 {
 struct snp125 *snp125 = (struct snp125 *)snp;
 printSnpOrthoSummary(tdb, snp->name, snp->observed);
 if (differentString(snp->strand,"?"))
     printf("<B>Strand: </B>%s<BR>\n", snp->strand);
 printf("<B>Observed: </B>%s<BR>\n", snp->observed);
 printSnpAlleleAndOrthos(tdb, snp125, version);
 puts("<BR><TABLE border=0 cellspacing=0 cellpadding=0>");
 if (version <= 127)
     printf("<TR><TD><B><A HREF=\"#LocType\">Location Type</A></B></TD><TD>%s</TD></TR>\n",
 	   snp->locType);
 printf("<TR><TD><B><A HREF=\"#Class\">Class</A></B></TD><TD>%s</TD></TR>\n", snp->class);
 printf("<TR><TD><B><A HREF=\"#Valid\">Validation</A></B></TD><TD>%s</TD></TR>\n", snp->valid);
 printf("<TR><TD><B><A HREF=\"#Func\">Function</A></B></TD><TD>%s</TD></TR>\n",
        snpMisoLinkFromFunc(snp->func));
 printf("<TR><TD><B><A HREF=\"#MolType\">Molecule Type</A>&nbsp;&nbsp;</B></TD><TD>%s</TD></TR>\n",
        snp->molType);
 if (snp->avHet>0)
     printf("<TR><TD><B><A HREF=\"#AvHet\">Average Heterozygosity</A>&nbsp;&nbsp;</TD>"
 	   "<TD></B>%.3f +/- %.3f</TD></TR>\n", snp->avHet, snp->avHetSE);
 printf("<TR><TD><B><A HREF=\"#Weight\">Weight</A></B></TD><TD>%d</TD></TR>\n", snp->weight);
 if (version >= 132)
     printSnp132ExtraColumns(tdb, snp);
 else
     printf("</TABLE>\n");
 printSnp125CodingAnnotations(tdb, snp125);
 writeSnpExceptionWithVersion(tdb, snp, version);
 printSnp125Function(tdb, snp125);
 }
 
 static char *getExcDescTable(struct trackDb *tdb)
 /* Look up snpExceptionDesc in tdb and provide default if not found.  Don't free return value! */
 {
 static char excDescTable[128];
 char *excDescTableSetting = trackDbSetting(tdb, "snpExceptionDesc");
 if (excDescTableSetting)
     safecpy(excDescTable, sizeof(excDescTable), excDescTableSetting);
 else
     safef(excDescTable, sizeof(excDescTable), "%sExceptionDesc", tdb->table);
 return excDescTable;
 }
 
 static boolean writeOneSnpException(char *exc, char *desc, boolean alreadyFound)
 /* Print out the description of exc, unless exc is already displayed elsewhere. */
 {
 // Don't bother reporting MultipleAlignments here -- right after this,
 // we have a whole section about the other mappings.
 if (differentString(exc, "MultipleAlignments") &&
     // Also exclude a couple that are from dbSNP not UCSC, and noted above (bitfields).
     differentString(exc, "GenotypeConflict") &&
     differentString(exc, "ClusterNonOverlappingAlleles"))
     {
     if (isEmpty(desc))
 	desc = exc;
     if (!alreadyFound)
 	printf("<BR><B>UCSC Annotations:</B><BR>\n");
     printf("%s<BR>\n", desc);
     return TRUE;
     }
 return FALSE;
 }
 
 void writeSnpExceptionFromTable(struct trackDb *tdb, char *itemName)
 /* Print out exceptions, if any, for this snp. */
 {
 char *exceptionsTableSetting = trackDbSetting(tdb, "snpExceptions");
 char exceptionsTable[128];
 if (exceptionsTableSetting)
     safecpy(exceptionsTable, sizeof(exceptionsTable), exceptionsTableSetting);
 else
     safef(exceptionsTable, sizeof(exceptionsTable), "%sExceptions", tdb->table);
 char *excDescTable = getExcDescTable(tdb);
 if (hTableExists(database, exceptionsTable) && hTableExists(database, excDescTable))
     {
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     char   query[1024];
     int    start = cartInt(cart, "o");
     sqlSafef(query, sizeof(query),
 	  "select description, %s.exception from %s, %s "
 	  "where chrom = \"%s\" and chromStart = %d and name = \"%s\" "
 	  "and %s.exception = %s.exception",
 	  excDescTable, excDescTable, exceptionsTable,
 	  seqName, start, itemName, excDescTable, exceptionsTable);
     sr = sqlGetResult(conn, query);
     boolean gotExc = FALSE;
     while ((row = sqlNextRow(sr))!=NULL)
 	gotExc |= writeOneSnpException(row[1], row[0], gotExc);
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 }
 
 static void writeSnpExceptionFromColumn(struct trackDb *tdb, struct snp132Ext *snp)
 /* Hash the contents of exception description table, and for each exception listed
  * in snp->exceptions, print out its description. */
 {
 char *excDescTable = getExcDescTable(tdb);
 if (hTableExists(database, excDescTable))
     {
     static struct hash *excDesc = NULL;
     if (excDesc == NULL)
 	{
 	excDesc = hashNew(0);
 	struct sqlConnection *conn = hAllocConn(database);
 	char query[512];
 	sqlSafef(query, sizeof(query), "select exception,description from %s", excDescTable);
 	struct sqlResult *sr = sqlGetResult(conn, query);
 	char **row;
 	while ((row = sqlNextRow(sr))!=NULL)
 	    hashAdd(excDesc, row[0], cloneString(row[1]));
 	sqlFreeResult(&sr);
 	hFreeConn(&conn);
 	}
     struct slName *excList = slNameListFromComma(snp->exceptions), *exc;
     boolean gotExc = FALSE;
     for (exc = excList;  exc != NULL;  exc = exc->next)
 	{
 	char *desc = hashFindVal(excDesc, exc->name);
 	gotExc |= writeOneSnpException(exc->name, desc, gotExc);
 	}
     }
 }
 
 void writeSnpExceptionWithVersion(struct trackDb *tdb, struct snp132Ext *snp, int version)
 /* Print out descriptions of exceptions, if any, for this snp. */
 {
 if (version >= 132)
     writeSnpExceptionFromColumn(tdb, snp);
 else
     writeSnpExceptionFromTable(tdb, snp->name);
 }
 
 struct snp *snp125ToSnp(struct snp125 *snp125)
 /* Copy over the bed6 plus observed fields. */
 {
 struct snp *snp;
 AllocVar(snp);
 snp->chrom = cloneString(snp125->chrom);
 snp->chromStart = snp125->chromStart;
 snp->chromEnd = snp125->chromEnd;
 snp->name = cloneString(snp125->name);
 snp->score = snp125->score;
 if (sameString(snp125->strand, "+"))
     snp->strand[0] = '+';
 else if (sameString(snp125->strand, "-"))
     snp->strand[0] = '-';
 else
     snp->strand[0] = '?';
 snp->strand[1] = '\0';
 snp->observed = cloneString(snp125->observed);
 return snp;
 }
 
 void checkForHgdpGeo(struct sqlConnection *conn, struct trackDb *tdb, char *itemName, int start)
 {
 char *hgdpGeoTable = "hgdpGeo"; // make this a trackDb setting
 if (!hTableExists(database, hgdpGeoTable))
     return;
 struct sqlResult *sr;
 char **row;
 char query[512];
 sqlSafef(query, sizeof(query),
       "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
       hgdpGeoTable, itemName, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     struct hgdpGeo geo;
     hgdpGeoStaticLoad(row+1, &geo);
     char title[1024];
     safef(title, sizeof(title), "Human Genome Diversity Project SNP"
 	  "<IMG name=\"hgdpImgIcon\" height=40 width=55 class='bigBlue' src=\"%s\">",
 	  hgdpPngFilePath(itemName));
     jsBeginCollapsibleSection(cart, tdb->track, "hgdpGeo", title, FALSE);
     printf("Note: These annotations are taken directly from the "
 	   "<A HREF=\"http://hgdp.uchicago.edu/\" TARGET=_BLANK>HGDP Selection Browser</A>, "
 	   "and may indicate the allele on the opposite strand from that given above.<BR>\n");
     printf("<B>Ancestral Allele:</B> %c<BR>\n", geo.ancestralAllele);
     printf("<B>Derived Allele:</B> %c<BR>\n", geo.derivedAllele);
     printf("<TABLE><TR><TD>\n");
     hgdpGeoFreqTable(&geo);
     printf("</TD><TD valign=top>\n");
     hgdpGeoImg(&geo);
     printf("</TD></TR></TABLE>\n");
     jsEndCollapsibleSection();
     }
 sqlFreeResult(&sr);
 }
 
 void checkForHapmap(struct sqlConnection *conn, struct trackDb *tdb, char *itemName)
 {
 boolean isPhaseIII = sameString(trackDbSettingOrDefault(tdb, "hapmapPhase", "II"), "III");
 boolean gotHapMap = FALSE;
 char query[512];
 if (!isPhaseIII && sqlTableExists(conn, "hapmapAllelesSummary"))
     {
     sqlSafef(query, sizeof(query),
 	  "select count(*) from hapmapAllelesSummary where name = '%s'", itemName);
     if (sqlQuickNum(conn, query) > 0)
 	gotHapMap = TRUE;
     }
 else
     {
     int i;
     for (i = 0;  hapmapPhaseIIIPops[i] != NULL;  i++)
 	{
 	char table[HDB_MAX_TABLE_STRING];
 	safef(table, sizeof(table), "hapmapSnps%s", hapmapPhaseIIIPops[i]);
 	if (sqlTableExists(conn, table))
 	    {
 	    sqlSafef(query, sizeof(query),
 		  "select count(*) from %s where name = '%s'", table, itemName);
 	    if (sqlQuickNum(conn, query) > 0)
 		{
 		gotHapMap = TRUE;
 		break;
 		}
 	    }
 	}
     }
 struct trackDb *hsTdb = hashFindVal(trackHash, "hapmapSnps");
 if (gotHapMap && hsTdb != NULL)
     {
     printf("<TR><TD colspan=2><B><A HREF=\"%s", hgTracksPathAndSettings());
     // If hapmapSnps is hidden, make it dense; if it's pack etc., leave it alone.
     if (sameString("hide", cartUsualString(cart, "hapmapSnps",
 					   trackDbSettingOrDefault(hsTdb, "visibility", "hide"))))
 	printf("&hapmapSnps=dense");
     printf("\"> HapMap SNP</A> </B></TD></TR>\n");
     }
 }
 
 static void checkForGwasCatalog(struct sqlConnection *conn, struct trackDb *tdb, char *item)
 /* If item is in gwasCatalog, add link to make the track visible. */
 {
 char *gcTable = "gwasCatalog";
 if (sqlTableExists(conn, gcTable))
     {
     char query[512];
     sqlSafef(query, sizeof(query), "select count(*) from %s where name = '%s'", gcTable, item);
     if (sqlQuickNum(conn, query) > 0)
 	{
 	struct trackDb *gcTdb = hashFindVal(trackHash, gcTable);
 	if (gcTdb != NULL)
 	    {
 	    printf("<TR><TD colspan=2>><B><A HREF=\"%s", hgTracksPathAndSettings());
 	    // If gcTable is hidden, make it dense; otherwise, leave it alone.
 	    if (sameString("hide",
 			   cartUsualString(cart, gcTable,
 					   trackDbSettingOrDefault(gcTdb, "visibility", "hide"))))
 		printf("&%s=dense", gcTable);
 	    printf("\">%s SNP</A> </B></TD></TR>\n", gcTdb->shortLabel);
 	    }
 	}
     }
 }
 
 static void printLsSnpPdb(struct sqlConnection *conn, char *pdbId, char *snpId)
 /* generate LS-SNP and chimera links for a PDB id */
 {
 char *lsSnpUrl = lsSnpPdbGetUrlPdbSnp(pdbId, snpId);
 struct tempName chimerax;
 lsSnpPdbChimeraSnpAnn(conn, pdbId, snpId, &chimerax);
 printf("<TD>%s<TD>%s<TD><A HREF=\"%s\" target=_blank>LS-SNP</A><td class=\"hgcLsSnpSep\"><A HREF=\"%s\">Chimera</A>\n",
        pdbId, lsSnpPdbChimeraGetStructType(conn, pdbId),
        lsSnpUrl, chimerax.forHtml);
 freeMem(lsSnpUrl);
 }
 
 static void printLsSnpMappings(struct sqlConnection *conn, struct slName *pdbIds,
 			       char *snpTrack, char *snpId)
 /* Print lsSnp mappings. */
 {
 jsBeginCollapsibleSection(cart, snpTrack, "lsSnp", "Mappings to PDB protein structures", FALSE);
 printf("<TABLE class=\"hgcLsSnp\">\n");
 printf("<TBODY>\n");
 int numPdbs = slCount(pdbIds);
 // limit column groups if just one row
 int numCols = (numPdbs < 3) ? numPdbs : 3;
 int iCol = 0;
 struct slName *pdbId;
 for (pdbId = pdbIds; pdbId != NULL; pdbId = pdbId->next)
     {
     if (iCol == 0)
         printf("<TR>\n");
     printLsSnpPdb(conn, pdbId->name, snpId);
     iCol++;
     if (iCol == numCols)
         {
         printf("</TR>\n");
         iCol = 0;
         }
     }
 if (iCol != 0)
     {
     // fill in last row
     for (; iCol < numCols; iCol++)
         printf("<TD colspan=4 class=\"hgcLsSnpSep\">\n");
     printf("</TR>\n");
     }
 printf("</TBODY>\n");
 printf("</TABLE>\n");
 printf("<A href=\"../goldenPath/help/chimera.html\" TARGET=_blank>Chimera help</A>\n");
 jsEndCollapsibleSection();
 }
 
 static void checkForLsSnpMappings(struct sqlConnection *conn, char *snpTrack, char *snpId)
 /* check if this SNP is mapped to any protein by LS-SNP, and if so print
 * the information. */
 {
 struct slName *pdbIds = lsSnpPdbChimeraGetSnpPdbs(conn, snpId);
 if (pdbIds != NULL)
     {
     printLsSnpMappings(conn, pdbIds, snpTrack, snpId);
     slFreeList(&pdbIds);
     }
 }
 
 void printOtherSnpMappings(char *table, char *name, int start,
 			   struct sqlConnection *conn, int rowOffset)
 /* If this SNP (from any bed4+ table) is not uniquely mapped, print the other mappings. */
 {
 char query[512];
 sqlSafef(query, sizeof(query), "select * from %s where name='%s'",
       table, name);
 struct sqlResult *sr = sqlGetResult(conn, query);
 int snpCount = 0;
 char **row;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     struct bed *snp = bedLoad3(row + rowOffset);
     if (snp->chromStart != start || differentString(snp->chrom, seqName))
 	{
 	printf("<BR>\n");
 	if (snpCount == 0)
 	    printf("<B>This SNP maps to these additional locations:</B><BR><BR>\n");
 	snpCount++;
 	bedPrintPos((struct bed *)snp, 3, tdb);
 	}
     }
 sqlFreeResult(&sr);
 }
 
 void doSnpWithVersion(struct trackDb *tdb, char *itemName, int version)
 /* Process SNP details. */
 {
 char   *table = tdb->table;
 struct snp132Ext *snp;
 struct snp *snpAlign = NULL;
 int    start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char   query[512];
 int    rowOffset=hOffsetPastBin(database, seqName, table);
 
 genericHeader(tdb, NULL);
 printf("<H2>dbSNP build %d %s</H2>\n", version, itemName);
 sqlSafef(query, sizeof(query), "select * from %s where chrom='%s' and "
       "chromStart=%d and name='%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     if (version >= 132)
 	snp = snp132ExtLoad(row+rowOffset);
     else
 	snp = (struct snp132Ext *)snp125Load(row+rowOffset);
     printCustomUrl(tdb, itemName, FALSE);
     bedPrintPos((struct bed *)snp, 3, tdb);
     snpAlign = snp125ToSnp((struct snp125 *)snp);
     printf("<BR>\n");
     printSnp125Info(tdb, snp, version);
     doSnpEntrezGeneLink(tdb, itemName);
     }
 else
     errAbort("SNP %s not found at %s base %d", itemName, seqName, start);
 sqlFreeResult(&sr);
 
 printOtherSnpMappings(table, itemName, start, conn, rowOffset);
 puts("<BR>");
 // Make table for collapsible sections:
 puts("<TABLE>");
 checkForGwasCatalog(conn, tdb, itemName);
 checkForHgdpGeo(conn, tdb, itemName, start);
 checkForHapmap(conn, tdb, itemName);
 checkForLsSnpMappings(conn, tdb->track, itemName);
 printSnpAlignment(tdb, snpAlign, version);
 puts("</TABLE>");
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 
 void doTigrGeneIndex(struct trackDb *tdb, char *item)
 /* Put up info on tigr gene index item. */
 {
 char *animal = cloneString(item);
 char *id = strchr(animal, '_');
 char buf[128];
 
 if (id == NULL)
     {
     animal = "human";
     id = item;
     }
 else
     *id++ = 0;
 if (sameString(animal, "cow"))
     animal = "cattle";
 else if (sameString(animal, "chicken"))
     animal = "g_gallus";
 else if (sameString(animal, "Dmelano"))
     animal = "drosoph";
 
 safef(buf, sizeof buf, "species=%s&tc=%s ", animal, id);
 genericClickHandler(tdb, item, buf);
 }
 
 void doJaxQTL(struct trackDb *tdb, char *item)
 /* Put up info on Quantitative Trait Locus from Jackson Lab. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char query[512];
 char **row;
 int start = cartInt(cart, "o");
 boolean isBed4 = startsWith("bed 4", tdb->type);
 boolean hasBin = hIsBinned(database, tdb->table);
 
 genericHeader(tdb, item);
 sqlSafef(query, sizeof(query),
       "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
       tdb->table, item, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     char *itemForUrl=NULL, *name=NULL, *description=NULL, *marker=NULL;
     float cMscore = 0.0;
     struct bed *bed = bedLoadN(row+hasBin, 4);
     if (isBed4)
 	{
 	char *oDb = trackDbSetting(tdb, "otherDb");
 	char *oTable = trackDbSetting(tdb, "otherDbTable");
 	itemForUrl = name = bed->name;
 	if (isNotEmpty(oDb) && isNotEmpty(oTable))
 	    {
 	    struct sqlConnection *conn2 = hAllocConn(database);
 	    char buf[1024];
 	    sqlSafef(query, sizeof(query),
 		  "select description from %s.%s where name = '%s'",
 		  oDb, oTable, name);
             description =
 		cloneString(sqlQuickQuery(conn2, query, buf, sizeof(buf)-1));
 	    sqlSafef(query, sizeof(query),
 		  "select mgiID from %s.%s where name = '%s'",
 		  oDb, oTable, name);
             itemForUrl =
 		cloneString(sqlQuickQuery(conn2, query, buf, sizeof(buf)-1));
 	    }
 	}
     else
 	{
 	struct jaxQTL *jaxQTL = jaxQTLLoad(row);
 	itemForUrl = jaxQTL->mgiID;
 	name = jaxQTL->name;
 	description = jaxQTL->description;
 	cMscore = jaxQTL->cMscore;
 	marker = jaxQTL->marker;
 	}
     printCustomUrl(tdb, itemForUrl, FALSE);
     printf("<B>QTL:</B> %s<BR>\n", name);
     if (isNotEmpty(description))
 	printf("<B>Description:</B> %s <BR>\n", description);
     if (cMscore != 0.0)
 	printf("<B>cM position of marker associated with peak LOD score:</B> "
 	       "%3.1f<BR>\n", cMscore);
     if (isNotEmpty(marker))
 	printf("<B>MIT SSLP marker with highest correlation:</B> %s<BR>",
 	       marker);
     bedPrintPos(bed, 3, tdb);
     }
 printTrackHtml(tdb);
 
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 #define GWAS_NOT_REPORTED "<em>Not reported</em>"
 #define GWAS_NONE_SIGNIFICANT "<em>None significant</em>"
 
 static char *subNrNs(char *str)
 /* The GWAS catalog has "NR" or "NS" for many values -- substitute those with something
  * more readable.  Don't free return value, it might be static. */
 {
 if (isEmpty(str) || sameString("NR", str))
     return GWAS_NOT_REPORTED;
 if (sameString("NS", str))
     return GWAS_NONE_SIGNIFICANT;
 struct dyString *dy1 = dyStringSub(str, "[NR]", "[" GWAS_NOT_REPORTED "]");
 struct dyString *dy2 = dyStringSub(dy1->string, "[NS]", "[" GWAS_NONE_SIGNIFICANT "]");
 return dyStringCannibalize(&dy2);
 }
 
 static boolean isSnpAndAllele(char *str)
 /* Return TRUE if str ~ /^rs[0-9]+-.+/ . */
 {
 if (isEmpty(str) || !startsWith("rs", str))
     return FALSE;
 char *p = str + 2;
 if (! isdigit(*p++))
     return FALSE;
 while (isdigit(*p))
     p++;
 if (*p++ != '-')
     return FALSE;
 if (*p == '\0')
     return FALSE;
 return TRUE;
 }
 
 static char *splitSnpAndAllele(char *str, char **retAllele)
 /* If str is a rsID+allele, return the rsID and if retAllele is non-null, set it
  * to the allele portion.  Don't free *retAllele.  If str is not rsID+allele,
  * return NULL. */
 {
 if (isSnpAndAllele(str))
     {
     char *rsID = cloneString(str);
     char *allele = strchr(rsID, '-');
     if (allele == NULL)
 	errAbort("splitSnpAndAllele: isSnpAllele() allowed %s", str);
     *allele++ = '\0';
     if (retAllele != NULL)
 	*retAllele = firstWordInLine(allele);
     return rsID;
     }
 else
     {
     if (retAllele != NULL)
 	*retAllele = NULL;
     return NULL;
     }
 }
 
 static char *getSnpAlleles(struct sqlConnection *conn, char *snpTable, char *snpName)
 /* Look up snpName's observed alleles in snpTable.  Returns NULL if not found. */
 {
 char query[512];
 char buf[256]; // varchar(255)
 sqlSafef(query, sizeof(query), "select observed from %s where name = '%s'", snpTable, snpName);
 return cloneString(sqlQuickQuery(conn, query, buf, sizeof(buf)-1));
 }
 
 static void gwasCatalogCheckSnpAlleles(struct trackDb *tdb, struct gwasCatalog *gc)
 /* Look up the SNP's observed alleles in the snp track and warn if they are
  * complementary (hence the risk allele is ambiguous because strand is often
  * not specified in journal articles). */
 {
 char *snpTable = trackDbSetting(tdb, "snpTable");
 if (isEmpty(snpTable))
     return;
 struct sqlConnection *conn = hAllocConn(database);
 if (sqlTableExists(conn, snpTable) && isSnpAndAllele(gc->riskAllele))
     {
     char *riskAllele = NULL, *strongSNP = splitSnpAndAllele(gc->riskAllele, &riskAllele);
     char *snpVersion = trackDbSettingOrDefault(tdb, "snpVersion", "?");
     char *dbSnpAlleles = getSnpAlleles(conn, snpTable, strongSNP);
     if (dbSnpAlleles == NULL)
 	dbSnpAlleles = "<em>not found</em>";
     boolean showBoth = differentString(strongSNP, gc->name);
     printf("<B>dbSNP build %s observed alleles for %s%s:</B> %s<BR>\n",
 	   snpVersion, (showBoth ? "Strongest SNP " : ""), strongSNP, dbSnpAlleles);
     if (stringIn("C/G", dbSnpAlleles) || stringIn("A/T", dbSnpAlleles))
 	printf("<em>Note: when SNP alleles are complementary (A/T or C/G), take care to "
 	       "determine the strand/orientation of the given risk allele from the "
 	       "original publication (above).</em><BR>\n");
     if (showBoth)
 	{
 	dbSnpAlleles = getSnpAlleles(conn, snpTable, gc->name);
 	if (dbSnpAlleles == NULL)
 	    dbSnpAlleles = "<em>not found</em>";
 	printf("<B>dbSNP build %s observed alleles for mapped SNP %s:</B> %s<BR>\n",
 	       snpVersion, gc->name, dbSnpAlleles);
 	}
     }
 hFreeConn(&conn);
 }
 
 void doGwasCatalog(struct trackDb *tdb, char *item)
 /* Show details from NHGRI's Genome-Wide Association Study catalog. */
 {
 int itemStart = cartInt(cart, "o"), itemEnd = cartInt(cart, "t");
 genericHeader(tdb, item);
 struct sqlConnection *conn = hAllocConn(database);
 struct dyString *dy = dyStringNew(512);
 sqlDyStringPrintf(dy, "select * from %s where chrom = '%s' and ", tdb->table, seqName);
 hAddBinToQuery(itemStart, itemEnd, dy);
 sqlDyStringPrintf(dy, "chromStart = %d and name = '%s'", itemStart, item);
 struct sqlResult *sr = sqlGetResult(conn, dy->string);
 int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 boolean first = TRUE;
 char **row;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (first)
 	first = FALSE;
     else
 	printf("<HR>\n");
     struct gwasCatalog *gc = gwasCatalogLoad(row+rowOffset);
     printCustomUrl(tdb, item, FALSE);
     printPos(gc->chrom, gc->chromStart, gc->chromEnd, NULL, TRUE, gc->name);
     printf("<B>Reported region:</B> %s<BR>\n", gc->region);
     printf("<B>Publication:</B> %s <em>et al.</em> "
 	   "<A HREF=\"", gc->author);
     printEntrezPubMedUidAbstractUrl(stdout, gc->pubMedID);
     printf("\" TARGET=_BLANK>%s</A>%s <em>%s.</em> %s<BR>\n",
 	   gc->title, (endsWith(gc->title, ".") ? "" : "."), gc->journal, gc->pubDate);
     printf("<B>Disease or trait:</B> %s<BR>\n", subNrNs(gc->trait));
     printf("<B>Initial sample size:</B> %s<BR>\n", subNrNs(gc->initSample));
     printf("<B>Replication sample size:</B> %s<BR>\n", subNrNs(gc->replSample));
     printf("<B>Reported gene(s):</B> %s<BR>\n", subNrNs(gc->genes));
     char *strongAllele = NULL, *strongRsID = splitSnpAndAllele(gc->riskAllele, &strongAllele);
     if (strongRsID)
 	{
 	printf("<B>Strongest SNP-Risk allele:</B> ");
 	printDbSnpRsUrl(strongRsID, "%s", strongRsID);
 	printf("-%s<BR>\n", strongAllele);
 	}
     else
 	printf("<B>Strongest SNP-Risk allele:</B> %s<BR>\n", subNrNs(gc->riskAllele));
     gwasCatalogCheckSnpAlleles(tdb, gc);
     printf("<B>Risk Allele Frequency:</B> %s<BR>\n", subNrNs(gc->riskAlFreq));
     if (isEmpty(gc->pValueDesc) || sameString(gc->pValueDesc, "NS"))
 	printf("<B>p-Value:</B> %s<BR>\n", subNrNs(gc->pValue));
     else if (gc->pValueDesc[0] == '(')
 	printf("<B>p-Value:</B> %s %s<BR>\n", gc->pValue, subNrNs(gc->pValueDesc));
     else
 	printf("<B>p-Value:</B> %s (%s)<BR>\n", gc->pValue, subNrNs(gc->pValueDesc));
     printf("<B>Odds Ratio or beta:</B> %s<BR>\n", subNrNs(gc->orOrBeta));
     printf("<B>95%% confidence interval:</B> %s<BR>\n", subNrNs(gc->ci95));
     printf("<B>Platform:</B> %s<BR>\n", subNrNs(gc->platform));
     printf("<B>Copy Number Variant (CNV)?:</B> %s<BR>\n",
 	   (gc->cnv == gwasCatalogY ? "Yes" : "No"));
     }
 sqlFreeResult(&sr);
 printTrackHtml(tdb);
 }
 
 void ncRnaPrintPos(struct bed *bed, int bedSize)
 /* Print first two fields of an ncRna entry in
  * standard format. */
 {
 char *strand = NULL;
 if (bedSize >= 4)
     printf("<B>Item:</B> %s<BR>\n", bed->name);
 if (bedSize >= 6)
    {
    strand = bed->strand;
    }
 printPos(bed->chrom, bed->chromStart, bed->chromEnd, strand, TRUE, bed->name);
 }
 
 void doNcRna(struct trackDb *tdb, char *item)
 /* Handle click in ncRna track. */
 {
 struct ncRna *ncRna;
 char table[64];
 boolean hasBin;
 struct bed *bed;
 char query[512];
 struct sqlResult *sr;
 char **row;
 struct sqlConnection *conn = hAllocConn(database);
 int bedSize;
 
 genericHeader(tdb, item);
 bedSize = 8;
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s'", table, item);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     ncRna = ncRnaLoad(row);
     printCustomUrl(tdb, item, TRUE);
     printf("<B>Type:</B> %s<BR>", ncRna->type);
     if (ncRna->extGeneId != NULL
     &&  !sameWord(ncRna->extGeneId, ""))
         {
         printf("<B>External Gene ID:</B> %s<BR>", ncRna->extGeneId);
         }
     bed = bedLoadN(row+hasBin, bedSize);
     ncRnaPrintPos(bed, bedSize);
     }
 sqlFreeResult(&sr);
 printTrackHtml(tdb);
 }
 
 void doWgRna(struct trackDb *tdb, char *item)
 /* Handle click in wgRna track. */
 {
 struct wgRna *wgRna;
 char table[64];
 boolean hasBin;
 struct bed *bed;
 char query[512];
 struct sqlResult *sr;
 char **row;
 struct sqlConnection *conn = hAllocConn(database);
 int bedSize;
 
 genericHeader(tdb, item);
 bedSize = 8;
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s'", table, item);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     wgRna = wgRnaLoad(row);
 
     /* display appropriate RNA type and URL */
     if (sameWord(wgRna->type, "HAcaBox"))
         {
         printCustomUrl(tdb, item, TRUE);
         printf("<B>RNA Type:</B> H/ACA Box snoRNA\n");
 	}
     if (sameWord(wgRna->type, "CDBox"))
         {
 	printCustomUrl(tdb, item, TRUE);
         printf("<B>RNA Type:</B> CD Box snoRNA\n");
 	}
     if (sameWord(wgRna->type, "scaRna"))
         {
 	printCustomUrl(tdb, item, TRUE);
         printf("<B>RNA Type:</B> small Cajal body-specific RNA\n");
 	}
     if (sameWord(wgRna->type, "miRna"))
         {
 	printOtherCustomUrl(tdb, item, "url2", TRUE);
 	printf("<B>RNA Type:</B> microRNA\n");
 	}
     printf("<BR>");
     bed = bedLoadN(row+hasBin, bedSize);
     bedPrintPos(bed, bedSize, tdb);
     }
 sqlFreeResult(&sr);
 printTrackHtml(tdb);
 }
 
 void doJaxQTL3(struct trackDb *tdb, char *item)
 /* Put up info on Quantitative Trait Locus from Jackson Lab. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char query[256];
 char **row;
 int start = cartInt(cart, "o");
 struct jaxQTL3 *jaxQTL;
 
 genericHeader(tdb, item);
 sqlSafef(query, sizeof query, "select * from jaxQTL3 where name = '%s' and chrom = '%s' and chromStart = %d",
         item, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     jaxQTL = jaxQTL3Load(row);
     printf("<B>Jax/MGI Link: </B>");
     printf("<a TARGET=\"_blank\" href=\"http://www.informatics.jax.org/searches/accession_report.cgi?id=%s\">%s</a><BR>\n",
            jaxQTL->mgiID, jaxQTL->mgiID);
     printf("<B>QTL:</B> %s<BR>\n", jaxQTL->name);
     printf("<B>Description:</B> %s <BR>\n", jaxQTL->description);
 
     if (!sameWord("", jaxQTL->flank1))
         {
         printf("<B>Flank Marker 1: </B>");
 	printf("<a TARGET=\"_blank\" href=\"http://www.informatics.jax.org/javawi2/servlet/WIFetch?page=searchTool&query=%s", jaxQTL->flank1);
 	printf("+&selectedQuery=Genes+and+Markers\">%s</a><BR>\n", jaxQTL->flank1);
         }
     if (!sameWord("", jaxQTL->marker))
         {
 	printf("<B>Peak Marker: </B>");
 	printf("<a TARGET=\"_blank\" href=\"http://www.informatics.jax.org/javawi2/servlet/WIFetch?page=searchTool&query=%s", jaxQTL->marker);
 	printf("+&selectedQuery=Genes+and+Markers\">%s</a><BR>\n", jaxQTL->marker);
         }
     if (!sameWord("", jaxQTL->flank2))
         {
 	printf("<B>Flank Marker 2: </B>");
 	printf("<a TARGET=\"_blank\" href=\"http://www.informatics.jax.org/javawi2/servlet/WIFetch?page=searchTool&query=%s", jaxQTL->flank2);
         printf("+&selectedQuery=Genes+and+Markers\">%s</a><BR>\n", jaxQTL->flank2);
         }
 
     /* no cMscore for current release*/
     /*printf("<B>cM position of marker associated with peak LOD score:</B> %3.1f<BR>\n",
       jaxQTL->cMscore);
     */
 
     printf("<B>Chromosome:</B> %s<BR>\n", skipChr(seqName));
     printBand(seqName, start, 0, FALSE);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doJaxAllele(struct trackDb *tdb, char *item)
 /* Show gene prediction position and other info. */
 {
 char query[512];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 boolean hasBin;
 char aliasTable[256], phenoTable[256];
 struct sqlResult *sr = NULL;
 char **row = NULL;
 boolean first = TRUE;
 
 genericHeader(tdb, item);
 safef(aliasTable, sizeof(aliasTable), "%sInfo", tdb->table);
 safef(phenoTable, sizeof(phenoTable), "jaxAllelePheno");
 sqlSafefFrag(query, sizeof(query), "name = \"%s\"", item);
 sr = hRangeQuery(conn, tdb->table, seqName, winStart, winEnd, query, &hasBin);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     struct bed *bed = bedLoadN(row+hasBin, 12);
     /* Watch out for case-insensitive matches (e.g. one allele is <sla>,
      * another is <Sla>): */
     if (! sameString(bed->name, item))
 	continue;
     if (first)
 	first = FALSE;
     else
 	printf("<BR>");
     printf("<B>MGI Representative Transcript:</B> ");
     htmTextOut(stdout, bed->name);
     puts("<BR>");
     if (hTableExists(database, aliasTable))
 	{
 	struct sqlResult *sr2 = NULL;
 	char **row2 = NULL;
 	char query2[1024];
 	sqlSafef(query2, sizeof(query2),
 	      "select mgiId,source,name from %s where name = '%s'",
 	      aliasTable, bed->name);
 	sr2 = sqlGetResult(conn2, query2);
 	while ((row2 = sqlNextRow(sr2)) != NULL)
 	    {
 	    /* Watch out for case-insensitive matches: */
 	    if (! sameString(bed->name, row2[2]))
 		continue;
 	    if (isNotEmpty(row2[0]))
 		printCustomUrl(tdb, row2[0], TRUE);
 	    printf("<B>Allele Type:</B> %s<BR>\n", row2[1]);
 	    }
 	sqlFreeResult(&sr2);
 	}
     if (hTableExists(database, phenoTable))
 	{
 	struct sqlResult *sr2 = NULL;
 	char **row2 = NULL;
 	char query2[1024];
 	struct slName *phenoList, *pheno;
 	sqlSafef(query2, sizeof(query2),
 	      "select phenotypes,allele from %s where allele = '%s'",
 	      phenoTable, bed->name);
 	sr2 = sqlGetResult(conn2, query2);
 	while ((row2 = sqlNextRow(sr2)) != NULL)
 	    {
 	    /* Watch out for case-insensitive matches: */
 	    if (! sameString(bed->name, row2[1]))
 		continue;
 	    boolean firstP = TRUE;
 	    phenoList = slNameListFromComma(row2[0]);
 	    slNameSort(&phenoList);
 	    printf("<B>Associated Phenotype(s):</B> ");
 	    for (pheno = phenoList;  pheno != NULL;  pheno = pheno->next)
 		{
 		if (firstP)
 		    firstP = FALSE;
 		else
 		    printf(", ");
 		printf("%s", pheno->name);
 		}
 	    printf("<BR>\n");
 	    }
 	sqlFreeResult(&sr2);
 	}
     printPos(bed->chrom, bed->chromStart, bed->chromEnd, bed->strand,
 	     FALSE, NULL);
     bedFree(&bed);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn2);
 hFreeConn(&conn);
 }
 
 void doJaxPhenotype(struct trackDb *tdb, char *item)
 /* Show gene prediction position and other info. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row = NULL;
 boolean hasBin;
 char query[512];
 char aliasTable[256], phenoTable[256];
 struct slName *phenoList = NULL, *pheno = NULL;
 boolean first = TRUE;
 char *selectedPheno = NULL;
 
 /* Parse out the selected phenotype passed in from hgTracks. */
 if ((selectedPheno = strstr(item, " source=")) != NULL)
     {
     *selectedPheno = '\0';
     selectedPheno += strlen(" source=");
     }
 genericHeader(tdb, item);
 safef(aliasTable, sizeof(aliasTable), "%sAlias", tdb->table);
 safef(phenoTable, sizeof(phenoTable), "jaxAllelePheno");
 sqlSafefFrag(query, sizeof(query), "name = \"%s\"", item);
 sr = hRangeQuery(conn, tdb->table, seqName, winStart, winEnd, query,
 		 &hasBin);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     struct bed *bed = bedLoadN(row+hasBin, 12);
     if (first)
 	{
 	first = FALSE;
 	printf("<B>MGI Representative Transcript:</B> ");
 	htmTextOut(stdout, bed->name);
 	puts("<BR>");
 	if (hTableExists(database, aliasTable))
 	    {
 	    struct sqlConnection *conn2 = hAllocConn(database);
 	    char query2[512];
 	    char buf[512];
 	    char *mgiId;
 	    sqlSafef(query2, sizeof(query2),
 		  "select alias from %s where name = '%s'", aliasTable, item);
 	    mgiId = sqlQuickQuery(conn2, query2, buf, sizeof(buf));
 	    if (mgiId != NULL)
 		printCustomUrl(tdb, mgiId, TRUE);
 	    hFreeConn(&conn2);
 	    }
 	printPos(bed->chrom, bed->chromStart, bed->chromEnd, bed->strand,
 		 FALSE, NULL);
 	bedFree(&bed);
 	}
     pheno = slNameNew(row[hasBin+12]);
     slAddHead(&phenoList, pheno);
     }
 sqlFreeResult(&sr);
 printf("<B>Phenotype(s) at this locus: </B> ");
 first = TRUE;
 slNameSort(&phenoList);
 for (pheno = phenoList;  pheno != NULL;  pheno = pheno->next)
     {
     if (first)
 	first = FALSE;
     else
 	printf(", ");
     if (selectedPheno && sameString(pheno->name, selectedPheno))
 	printf("<B>%s</B>", pheno->name);
     else
 	printf("%s", pheno->name);
     }
 puts("<BR>");
 if (hTableExists(database, phenoTable) && selectedPheno)
     {
     struct trackDb *alleleTdb = hMaybeTrackInfo(conn, "jaxAllele");
     struct sqlConnection *conn2 = hAllocConn(database);
     char query2[512];
     char buf[512];
     char alleleTable[256];
     safef(alleleTable, sizeof(alleleTable), "jaxAlleleInfo");
     boolean gotAllele = hTableExists(database, alleleTable);
     sqlSafef(query, sizeof(query),
 	  "select allele from %s where transcript = '%s' "
 	  "and phenotypes like '%%%s%%'",
 	  phenoTable, item, selectedPheno);
     first = TRUE;
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	char *mgiId = NULL;
 	if (first)
 	    {
 	    first = FALSE;
 	    printf("<B>Allele(s) Associated with %s Phenotype:</B> ",
 		   selectedPheno);
 	    }
 	else
 	    printf(", ");
 	if (gotAllele)
 	    {
 	    sqlSafef(query2, sizeof(query2),
 		  "select mgiID from jaxAlleleInfo where name = '%s'",
 		  row[0]);
 	    mgiId = sqlQuickQuery(conn2, query2, buf, sizeof(buf));
 	    }
 	if (mgiId && alleleTdb && alleleTdb->url)
 	    {
 	    struct dyString *dy = dyStringSub(alleleTdb->url, "$$", mgiId);
 	    printf("<A HREF=\"%s\" TARGET=_BLANK>", dy->string);
 	    dyStringFree(&dy);
 	    }
 	htmTextOut(stdout, row[0]);
 	if (mgiId && alleleTdb && alleleTdb->url)
 	    printf("</A>");
 	}
     sqlFreeResult(&sr);
     hFreeConn(&conn2);
     if (!first)
 	puts("<BR>");
     }
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doJaxAliasGenePred(struct trackDb *tdb, char *item)
 /* Show gene prediction position and other info. */
 {
 char query[512];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 struct genePred *gpList = NULL, *gp = NULL;
 boolean hasBin;
 char table[128];
 char aliasTable[256];
 boolean gotAlias = FALSE;
 
 genericHeader(tdb, item);
 safef(aliasTable, sizeof(aliasTable), "%sAlias", tdb->table);
 gotAlias = hTableExists(database, aliasTable);
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafefFrag(query, sizeof(query), "name = \"%s\"", item);
 gpList = genePredReaderLoadQuery(conn, table, query);
 for (gp = gpList; gp != NULL; gp = gp->next)
     {
     if (gotAlias)
 	{
 	char query2[1024];
 	char buf[512];
 	char *mgiId;
 	sqlSafef(query2, sizeof(query2),
 	      "select alias from %s where name = '%s'", aliasTable, item);
 	mgiId = sqlQuickQuery(conn2, query2, buf, sizeof(buf));
 	if (mgiId != NULL)
 	    printCustomUrl(tdb, mgiId, TRUE);
 	}
     printPos(gp->chrom, gp->txStart, gp->txEnd, gp->strand, FALSE, NULL);
     if (gp->next != NULL)
         printf("<br>");
     }
 printTrackHtml(tdb);
 genePredFreeList(&gpList);
 hFreeConn(&conn2);
 hFreeConn(&conn);
 }
 
 
 void doEncodeRegion(struct trackDb *tdb, char *item)
 /* Print region desription, along with generic info */
 {
 char *descr;
 char *plus = NULL;
 char buf[128];
 if ((descr = getEncodeRegionDescr(item)) != NULL)
     {
     safef(buf, sizeof(buf), "<B>Description:</B> %s<BR>\n", descr);
     plus = buf;
     }
 genericClickHandlerPlus(tdb, item, NULL, plus);
 }
 
 char *getEncodeName(char *item)
 /* the item is in the format 'ddddddd/nnn' where the first seven 'd' characters
    are the digits of the identifier, and the variable-length 'n' chatacters
    are the name of the object.  Return the name. */
 {
 char *dupe=cloneString(item);
 return dupe+8;
 }
 
 char *getEncodeId(char *item)
 /* the item is in the format 'ddddddd/nnn' where the first seven 'd' characters
    are the digits of the identifier, and the variable-length 'n' chatacters
    are the name of the object.  Return the ID portion. */
 {
 char *id = cloneString(item);
 id[7]='\0';
 return id;
 }
 
 void doEncodeErge(struct trackDb *tdb, char *item)
 /* Print ENCODE data from dbERGE II */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[1024];
 struct encodeErge *ee=NULL;
 int start = cartInt(cart, "o");
 char *newLabel = tdb->longLabel + 7; /* removes 'ENCODE ' from label */
 char *encodeName = getEncodeName(item);
 char *encodeId = getEncodeId(item);
 
 cartWebStart(cart, database, "ENCODE Region Data: %s", newLabel);
 printf("<H2>ENCODE Region <span style='text-decoration:underline;'>%s</span> Data for %s.</H2>\n",
        newLabel, encodeName);
 genericHeader(tdb, encodeName);
 
 genericBedClick(conn, tdb, item, start, 14);
 /*	reserved field has changed to itemRgb in code 2004-11-22 - Hiram */
 sqlSafef(query, sizeof(query),
 	 "select   chrom, chromStart, chromEnd, name, score, strand, "
 	 "         thickStart, thickEnd, reserved, blockCount, blockSizes, "
 	 "         chromStarts, Id, color "
 	 "from     %s "
 	 "where    name = '%s' and chromStart = %d "
 	 "order by Id ", tdb->table, item, start);
 for (ee = encodeErgeLoadByQuery(conn, query); ee!=NULL; ee=ee->next)
     {
     printf("<BR>\n");
     if (ee->Id>0)
 	{
 	printf("<BR>Additional information for <A HREF=\"http://dberge.cse.psu.edu/");
 	printf("cgi-bin/dberge_query?mode=Submit+query&disp=brow+data&pid=");
 	printf("%s\" TARGET=_blank>%s</A>\n is available from <A ", encodeId, encodeName);
 	printf("HREF=\"http://globin.cse.psu.edu/dberge/testmenu.html\" ");
 	printf("TARGET=_blank>dbERGEII</A>.\n");
 	}
     }
 printTrackHtml(tdb);
 encodeErgeFree(&ee);
 hFreeConn(&conn);
 }
 
 void doEncodeErgeHssCellLines(struct trackDb *tdb, char *item)
 /* Print ENCODE data from dbERGE II */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[1024];
 struct encodeErgeHssCellLines *ee=NULL;
 int start = cartInt(cart, "o");
 char *dupe, *words[16];
 int wordCount=0;
 char *encodeName = getEncodeName(item);
 char *encodeId = getEncodeId(item);
 int i;
 
 cartWebStart(cart, database, "ENCODE Region Data: %s", tdb->longLabel+7);
 printf("<H2>ENCODE Region <span style='text-decoration:underline;'>%s</span> Data for %s</H2>\n",
        tdb->longLabel+7, encodeName);
 genericHeader(tdb, item);
 
 dupe = cloneString(tdb->type);
 wordCount = chopLine(dupe, words);
 genericBedClick(conn, tdb, item, start, atoi(words[1]));
 /*	reserved field has changed to itemRgb in code 2004-11-22 - Hiram */
 sqlSafef(query, sizeof(query),
 	 "select   chrom, chromStart, chromEnd, name, score, strand, "
 	 "         thickStart, thickEnd, reserved, blockCount, blockSizes, "
 	 "         chromStarts, Id, color, allLines "
 	 "from     %s "
 	 "where    name = '%s' and chromStart = %d "
 	 "order by Id ", tdb->table, item, start);
 for (ee = encodeErgeHssCellLinesLoadByQuery(conn, query); ee!=NULL; ee=ee->next)
     {
     if (ee->Id>0)
 	{
 	printf("<BR><B>Cell lines:</B> ");
 	dupe = cloneString(ee->allLines);
 	wordCount = chopCommas(dupe, words);
 	for (i=0; i<wordCount-1; i++)
 	    {
 	    printf("%s, ", words[i]);
 	    }
 	printf("%s.\n",words[wordCount-1]);
 	printf("<BR><BR>Additional information for <A HREF=\"http://dberge.cse.psu.edu/");
 	printf("cgi-bin/dberge_query?mode=Submit+query&disp=brow+data&pid=");
 	printf("%s\" TARGET=_blank>%s</A>\n is available from <A ", encodeId, encodeName);
 	printf("HREF=\"http://globin.cse.psu.edu/dberge/testmenu.html\" ");
 	printf("TARGET=_blank>dbERGEII</A>.\n");
 	}
     }
 printTrackHtml(tdb);
 encodeErgeHssCellLinesFree(&ee);
 hFreeConn(&conn);
 }
 
 
 void doEncodeIndels(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct encodeIndels encodeIndel;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 boolean firstTime = TRUE;
 
 genericHeader(tdb, itemName);
 
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     encodeIndelsStaticLoad(row+rowOffset, &encodeIndel);
     if (firstTime)
         {
         printf("<B>Variant and Reference Sequences: </B><BR>\n");
         printf("<PRE><TT>%s<BR>\n", encodeIndel.variant);
         printf("%s</TT></PRE><BR>\n", encodeIndel.reference);
         bedPrintPos((struct bed *)&encodeIndel, 3, tdb);
         firstTime = FALSE;
         printf("-----------------------------------------------------<BR>\n");
         }
     printf("<B>Trace Name:</B> %s <BR>\n", encodeIndel.traceName);
     printf("<B>Trace Id:</B> ");
     printf("<A HREF=\"%s\" TARGET=_blank> %s</A> <BR>\n",
             traceUrl(encodeIndel.traceId), encodeIndel.traceId);
     printf("<B>Trace Pos:</B> %d <BR>\n", encodeIndel.tracePos);
     printf("<B>Trace Strand:</B> %s <BR>\n", encodeIndel.traceStrand);
     printf("<B>Quality Score:</B> %d <BR>\n", encodeIndel.score);
     printf("-----------------------------------------------------<BR>\n");
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doGbProtAnn(struct trackDb *tdb, char *item)
 /* Show extra info for GenBank Protein Annotations track. */
 {
 struct sqlConnection *conn  = hAllocConn(database);
 struct sqlResult *sr;
 char query[256];
 char **row;
 int start = cartInt(cart, "o");
 struct gbProtAnn *gbProtAnn;
 
 genericHeader(tdb, item);
 sqlSafef(query, sizeof query, "select * from gbProtAnn where name = '%s' and chrom = '%s' and chromStart = %d",
         item, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     gbProtAnn = gbProtAnnLoad(row);
     printCustomUrl(tdb, item, TRUE);
     printf("<B>Product:</B> %s<BR>\n", gbProtAnn->product);
     if (gbProtAnn->note[0] != 0)
 	printf("<B>Note:</B> %s <BR>\n", gbProtAnn->note);
     printf("<B>GenBank Protein: </B>");
     printf("<A HREF=\"http://www.ncbi.nlm.nih.gov/entrez/viewer.fcgi?val=%s\"",
 	    gbProtAnn->proteinId);
     printf(" TARGET=_blank>%s</A><BR>\n", gbProtAnn->proteinId);
 
     htmlHorizontalLine();
     showSAM_T02(gbProtAnn->proteinId);
 
     printPos(seqName, gbProtAnn->chromStart, gbProtAnn->chromEnd, "+", TRUE,
 	     gbProtAnn->name);
     }
 printTrackHtml(tdb);
 
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 bool matchTableOrHandler(char *word, struct trackDb *tdb)
 /* return true if word matches either the table name or the trackHandler setting of the tdb struct */
 {
 char* handler = trackDbSetting(tdb, "trackHandler");
 return (sameWord(word, tdb->table) || (handler==NULL || sameWord(word, handler)));
 }
 
 void doLinkedFeaturesSeries(char *track, char *clone, struct trackDb *tdb)
 /* Create detail page for linked features series tracks */
 {
 char query[256];
 char title[256];
 struct sqlConnection *conn = hAllocConn(database), *conn1 = hAllocConn(database);
 struct sqlResult *sr = NULL, *sr2 = NULL, *srb = NULL;
 char **row, **row1, **row2, **rowb;
 char *lfLabel = NULL;
 char *table = NULL;
 char *intName = NULL;
 char pslTable[64];
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 int length = end - start;
 int i;
 struct lfs *lfs;
 struct psl *pslList = NULL, *psl;
 boolean hasBin = hOffsetPastBin(database, seqName, track);
 
 /* Determine type */
 if (matchTableOrHandler("bacEndPairs", tdb))
     {
     safef(title, sizeof title, "Location of %s using BAC end sequences", clone);
     lfLabel = "BAC ends";
     table = track;
     }
 if (matchTableOrHandler("bacEndSingles", tdb))
      {
      safef(title, sizeof title, "Location of %s using BAC end sequences", clone);
      lfLabel = "BAC ends";
      table = track;
      }
 if (matchTableOrHandler("bacEndPairsBad", tdb))
     {
     safef(title, sizeof title, "Location of %s using BAC end sequences", clone);
     lfLabel = "BAC ends";
     table = track;
     }
 if (matchTableOrHandler("bacEndPairsLong", tdb))
     {
     safef(title, sizeof title, "Location of %s using BAC end sequences", clone);
     lfLabel = "BAC ends";
     table = track;
     }
 if (matchTableOrHandler("fosEndPairs", tdb))
     {
     safef(title, sizeof title, "Location of %s using fosmid end sequences", clone);
     lfLabel = "Fosmid ends";
     table = track;
     }
 if (matchTableOrHandler("fosEndPairsBad", tdb))
     {
     safef(title, sizeof title, "Location of %s using fosmid end sequences", clone);
     lfLabel = "Fosmid ends";
     table = track;
     }
 if (matchTableOrHandler("fosEndPairsLong", tdb))
     {
     safef(title, sizeof title, "Location of %s using fosmid end sequences", clone);
     lfLabel = "Fosmid ends";
     table = track;
     }
 if (matchTableOrHandler("earlyRep", tdb))
     {
     safef(title, sizeof title, "Location of %s using cosmid end sequences", clone);
     lfLabel = "Early Replication Cosmid Ends";
     table = track;
     }
 if (matchTableOrHandler("earlyRepBad", tdb))
     {
     safef(title, sizeof title, "Location of %s using cosmid end sequences", clone);
     lfLabel = "Early Replication Cosmid Ends";
     table = track;
     }
 if (trackDbSetting(tdb, "lfPslTable"))
     {
     safef(title, sizeof title, "Location of %s using clone end sequences", clone);
     lfLabel = "Clone ends";
     table = track;
     }
 
 /* Print out non-sequence info */
 cartWebStart(cart, database, "%s", title);
 
 /* Find the instance of the object in the bed table */
 sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE name = '%s' "
                "AND chrom = '%s' AND chromStart = %d "
                "AND chromEnd = %d",
         table, clone, seqName, start, end);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row != NULL)
     {
     lfs = lfsLoad(row+hasBin);
     if (sameString("bacEndPairs", track) || sameString("bacEndSingles", track))
 	{
         if (sameString("Zebrafish", organism) )
             {
             /* query to bacCloneXRef table to get Genbank accession */
             /* and internal Sanger name for clones */
             sqlSafef(query, sizeof query, "SELECT genbank, intName FROM bacCloneXRef WHERE name = '%s'", clone);
             srb = sqlMustGetResult(conn1, query);
             rowb = sqlNextRow(srb);
             if (rowb != NULL)
                 {
 	        printf("<H2><A HREF=");
 	        printCloneDbUrl(stdout, clone);
 	        printf(" TARGET=_BLANK>%s</A></H2>\n", clone);
                 if (rowb[0] != NULL)
                     {
                     printf("<H3>Genbank Accession: <A HREF=");
                     printEntrezNucleotideUrl(stdout, rowb[0]);
                     printf(" TARGET=_BLANK>%s</A></H3>\n", rowb[0]);
                     }
                 else
                     printf("<H3>Genbank Accession: n/a");
                 intName = cloneString(rowb[1]);
                 }
             else
                 printf("<H2>%s</H2>\n", clone);
             }
         else if (sameString("Dog", organism) ||
 	         sameString("Zebra finch", organism))
             {
             printf("<H2><A HREF=");
             printTraceUrl(stdout, "clone_id", clone);
             printf(" TARGET=_BLANK>%s</A></H2>\n", clone);
             }
 	else if (trackDbSetting(tdb, "notNCBI"))
 	    {
 	    printf("<H2>%s</H2>\n", clone);
 	    }
         else if (startsWith(tdb->track, "trace"))
             {
             printTraceUrl(stdout, "clone_id", clone);
             }
         else
             {
 	    printf("<H2><A HREF=");
 	    printCloneDbUrl(stdout, clone);
 	    printf(" TARGET=_BLANK>%s</A></H2>\n", clone);
 	    }
         }
     else if (trackDbSetting(tdb, "lfPslTable"))
         {
         printf("<H2><A HREF=");
         printCloneDbUrl(stdout, clone);
         printf(" TARGET=_BLANK>%s</A></H2>\n", clone);
 
         }
     else
 	{
 	printf("<B>%s</B>\n", clone);
 	}
 
     printf("<P><HR ALIGN=\"CENTER\"></P>\n<TABLE>\n");
     printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n",seqName);
     printf("<TR><TH ALIGN=left>Start:</TH><TD>%d</TD></TR>\n",start+1);
     printf("<TR><TH ALIGN=left>End:</TH><TD>%d</TD></TR>\n",end);
     printf("<TR><TH ALIGN=left>Length:</TH><TD>%d</TD></TR>\n",length);
     printf("<TR><TH ALIGN=left>Strand:</TH><TD>%s</TD></TR>\n", lfs->strand);
     printf("<TR><TH ALIGN=left>Score:</TH><TD>%d</TD></TR>\n", lfs->score);
 
     if ((sameString("Zebrafish", organism)) && ((sameString("bacEndPairs", track)) || (sameString("bacEndSingles", track))) )
         {
         /* print Sanger FPC name (internal name) */
         printf("<TR><TH ALIGN=left>Sanger FPC Name:</TH><TD>");
         if (intName != NULL)
             printf("%s</TD></TR>\n", intName);
         else
             printf("n/a</TD></TR>\n");
         /* print associated STS information for this BAC clone */
         //printBacStsXRef(clone);
         }
     else
         {
         printBand(seqName, start, end, TRUE);
         printf("</TABLE>\n");
         printf("<P><HR ALIGN=\"CENTER\"></P>\n");
         }
     if (lfs->score == 1000)
         {
 	printf("<H4>This is the only location found for %s</H4>\n",clone);
 	}
     else
         {
 	//printOtherLFS(clone, table, start, end);
 	}
 
     safef(title, sizeof title, "Genomic alignments of %s:", lfLabel);
     webNewSection("%s",title);
 
     for (i = 0; i < lfs->lfCount; i++)
         {
         sqlFreeResult(&sr);
         hFindSplitTable(database, seqName, lfs->pslTable, pslTable, &hasBin);
 
         if (isEmpty(pslTable) && trackDbSetting(tdb, "lfPslTable"))
             safecpy(pslTable, sizeof(pslTable), trackDbSetting(tdb, "lfPslTable"));
             
         sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE qName = '%s'",
                        pslTable, lfs->lfNames[i]);
         sr = sqlMustGetResult(conn, query);
         while ((row1 = sqlNextRow(sr)) != NULL)
             {
 	    psl = pslLoad(row1+hasBin);
             slAddHead(&pslList, psl);
             }
         slReverse(&pslList);
 
         if ((!sameString("fosEndPairs", track))
             && (!sameString("earlyRep", track))
             && (!sameString("earlyRepBad", track)))
 	    {
             if (sameWord(organism, "Zebrafish") )
                 {
                 /* query to bacEndAlias table to get Genbank accession */
                 sqlSafef(query, sizeof query, "SELECT * FROM bacEndAlias WHERE alias = '%s' ",
                         lfs->lfNames[i]);
 
                 sr2 = sqlMustGetResult(conn, query);
                 row2 = sqlNextRow(sr2);
                 if (row2 != NULL)
                     {
                     printf("<H3>%s\tAccession: <A HREF=", lfs->lfNames[i]);
                     printEntrezNucleotideUrl(stdout, row2[2]);
                     printf(" TARGET=_BLANK>%s</A></H3>\n", row2[2]);
                     }
                 else
                     {
                     printf("<B>%s</B>\n",lfs->lfNames[i]);
                     }
                 sqlFreeResult(&sr2);
                 }
             else if (sameString("Dog", organism) ||
 		     sameString("Zebra finch", organism))
                 {
                 printf("<H3><A HREF=");
                 printTraceUrl(stdout, "trace_name", lfs->lfNames[i]);
                 printf(" TARGET=_BLANK>%s</A></H3>\n",lfs->lfNames[i]);
                 }
 	    else if (trackDbSetting(tdb, "notNCBI"))
 		{
 		printf("<H3>%s</H3>\n", lfs->lfNames[i]);
 		}
 	    else if (trackDbSetting(tdb, "lfPslTable"))
 		{
                 printf("<H3><A HREF=");
                 printTraceTiUrl(stdout, lfs->lfNames[i]);
                 printf(" TARGET=_BLANK>%s</A></H3>\n",lfs->lfNames[i]);
 		}
             else
                 {
 	        printf("<H3><A HREF=");
 	        printEntrezNucleotideUrl(stdout, lfs->lfNames[i]);
 	        printf(" TARGET=_BLANK>%s</A></H3>\n",lfs->lfNames[i]);
                 }
 	    }
         else
 	    {
 	    printf("<B>%s</B>\n", lfs->lfNames[i]);
 	    }
 	printAlignments(pslList, lfs->lfStarts[i], "htcCdnaAli", lfs->pslTable, lfs->lfNames[i]);
 	htmlHorizontalLine();
 	pslFreeList(&pslList);
 	}
     }
 else
     {
     warn("Couldn't find %s in %s table", clone, table);
     }
 sqlFreeResult(&sr);
 sqlFreeResult(&sr2);
 sqlFreeResult(&srb);
 webNewSection("Notes:");
 printTrackHtml(tdb);
 hFreeConn(&conn);
 hFreeConn(&conn1);
 }
 
 void fillCghTable(int type, char *tissue, boolean bold)
 /* Get the requested records from the database and print out HTML table */
 {
 char query[256];
 char currName[64];
 int rowOffset;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 struct cgh *cghRow;
 
 if (tissue)
     sqlSafef(query, sizeof query, "type = %d AND tissue = '%s' ORDER BY name, chromStart", type, tissue);
 else
     sqlSafef(query, sizeof query, "type = %d ORDER BY name, chromStart", type);
 sr = hRangeQuery(conn, "cgh", seqName, winStart, winEnd, query, &rowOffset);
 while ((row = sqlNextRow(sr)))
     {
     cghRow = cghLoad(row);
     if (strcmp(currName,cghRow->name))
 	{
         if (bold)
 	    printf("</TR>\n<TR>\n<TH>%s</TH>\n",cghRow->name);
 	else
 	    printf("</TR>\n<TR>\n<TD>%s</TD>\n",cghRow->name);
 	strcpy(currName,cghRow->name);
 	}
     if (bold)
 	printf("<TH ALIGN=right>%.6f</TH>\n",cghRow->score);
     else
 	printf("<TD ALIGN=right>%.6f</TD>\n",cghRow->score);
     }
 sqlFreeResult(&sr);
 }
 
 
 /* Evan Eichler's stuff */
 
 void doCeleraDupPositive(struct trackDb *tdb, char *dupName)
 /* Handle click on celeraDupPositive track. */
 {
 struct celeraDupPositive dup;
 char query[512];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 int celeraVersion = 0;
 int i = 0;
 cartWebStart(cart, database, "%s", tdb->longLabel);
 
 if (sameString(database, "hg15"))
     celeraVersion = 3;
 else
     celeraVersion = 4;
 
 if (cgiVarExists("o"))
     {
     int start = cgiInt("o");
     int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 
     sqlSafef(query, sizeof(query),
 	  "select * from %s where chrom = '%s' and chromStart = %d and name= '%s'",
 	  tdb->table, seqName, start, dupName);
     sr = sqlGetResult(conn, query);
     i = 0;
     while ((row = sqlNextRow(sr)))
 	{
 	if (i > 0)
 	    htmlHorizontalLine();
 	celeraDupPositiveStaticLoad(row+rowOffset, &dup);
 	printf("<B>Duplication Name:</B> %s<BR>\n", dup.name);
 	bedPrintPos((struct bed *)(&dup), 3, tdb);
 	if (!sameString(dup.name, dup.fullName))
 	    printf("<B>Full Descriptive Name:</B> %s<BR>\n", dup.fullName);
 	if (dup.bpAlign > 0)
 	    {
 	    printf("<B>Fraction BP Match:</B> %3.4f<BR>\n", dup.fracMatch);
 	    printf("<B>Alignment Length:</B> %3.0f<BR>\n", dup.bpAlign);
 	    }
 	if (!startsWith("WSSD No.", dup.name))
 	    {
 	    printf("<A HREF=\"http://humanparalogy.gs.washington.edu"
 		   "/eichler/celera%d/cgi-bin/celera%d.pl"
 		   "?search=%s&type=pdf \" Target=%s_PDF>"
 		   "<B>Clone Read Depth Graph (PDF)</B></A><BR>",
 		   celeraVersion, celeraVersion, dup.name, dup.name);
 	    printf("<A HREF=\"http://humanparalogy.gs.washington.edu"
 		   "/eichler/celera%d/cgi-bin/celera%d.pl"
 		   "?search=%s&type=jpg \" Target=%s_JPG>"
 		   "<B>Clone Read Depth Graph (JPG)</B></A><BR>",
 		   celeraVersion, celeraVersion, dup.name, dup.name);
 	    }
 	i++;
 	}
     }
 else
     {
     puts("<P>Click directly on a duplication for information on that "
 	 "duplication.</P>");
     }
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void parseSuperDupsChromPointPos(char *pos, char *retChrom, int *retPos,
 				 int *retID)
 /* Parse out (No.)?NNNN[.,]chrN:123 into NNNN and chrN and 123. */
 {
 char *words[16];
 int wordCount = 0;
 char *sep = ",.:";
 char *origPos = pos;
 if (startsWith("No.", pos))
     pos += strlen("No.");
 pos = cloneString(pos);
 wordCount = chopString(pos, sep, words, ArraySize(words));
 if (wordCount < 2 || wordCount > 3)
     errAbort("parseSuperDupsChromPointPos: Expected something like "
 	     "(No\\.)?([0-9]+[.,])?[a-zA-Z0-9_]+:[0-9]+ but got %s", origPos);
 if (wordCount == 3)
     {
     *retID = sqlUnsigned(words[0]);
     safecpy(retChrom, 64, words[1]);
     *retPos = sqlUnsigned(words[2]);
     }
 else
     {
     *retID = -1;
     safecpy(retChrom, 64, words[0]);
     *retPos = sqlUnsigned(words[1]);
     }
 }
 
 
 void doGenomicSuperDups(struct trackDb *tdb, char *dupName)
 /* Handle click on genomic dup track. */
 {
 cartWebStart(cart, database, "%s", tdb->longLabel);
 
 if (cgiVarExists("o"))
     {
     struct genomicSuperDups dup;
     struct dyString *query = newDyString(512);
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     char oChrom[64];
     int oStart;
     int dupId;
     int rowOffset;
     int start = cgiInt("o");
     int end   = cgiInt("t");
     char *alignUrl = NULL;
     if (sameString("hg18", database))
 	alignUrl = "http://humanparalogy.gs.washington.edu/build36";
     else if (sameString("hg17", database))
 	alignUrl = "http://humanparalogy.gs.washington.edu";
     else if (sameString("hg15", database) || sameString("hg16", database))
 	alignUrl = "http://humanparalogy.gs.washington.edu/jab/der_oo33";
     rowOffset = hOffsetPastBin(database, seqName, tdb->table);
     parseSuperDupsChromPointPos(dupName, oChrom, &oStart, &dupId);
     sqlDyStringPrintf(query, "select * from %s where chrom = '%s' and ",
 		   tdb->table, seqName);
     if (rowOffset > 0)
 	hAddBinToQuery(start, end, query);
     if (dupId >= 0)
 	dyStringPrintf(query, "uid = %d and ", dupId);
     dyStringPrintf(query, "chromStart = %d and otherStart = %d",
 		   start, oStart);
     sr = sqlGetResult(conn, query->string);
     while ((row = sqlNextRow(sr)))
 	{
 	genomicSuperDupsStaticLoad(row+rowOffset, &dup);
 	bedPrintPos((struct bed *)(&dup), 4, tdb);
 	printf("<B>Other Position:</B> "
 	       "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">"
 	       "%s:%d-%d</A> &nbsp;&nbsp;&nbsp;\n",
                hgTracksPathAndSettings(), database,
 	       dup.otherChrom, dup.otherStart+1, dup.otherEnd,
 	       dup.otherChrom, dup.otherStart+1, dup.otherEnd);
 	printf("<A HREF=\"%s&o=%d&t=%d&g=getDna&i=%s&c=%s&l=%d&r=%d&strand=%s&db=%s&table=%s\">"
 	       "View DNA for other position</A><BR>\n",
 	       hgcPathAndSettings(), dup.otherStart, dup.otherEnd, "",
 	       dup.otherChrom, dup.otherStart, dup.otherEnd, dup.strand,
 	       database, tdb->track);
 	printf("<B>Other Position Relative Orientation:</B>%s<BR>\n",
 	       dup.strand);
 	if(sameString("canFam1", database))
 	{
 		printf("<B>Filter Verdict:</B> %s<BR>\n", dup.verdict);
 		printf("&nbsp;&nbsp;&nbsp;<B> testResult:</B>%s<BR>\n", dup.testResult);
 		printf("&nbsp;&nbsp;&nbsp;<B> chits:</B>%s<BR>\n", dup.chits);
 		printf("&nbsp;&nbsp;&nbsp;<B> ccov:</B>%s<BR>\n", dup.ccov);
 		printf("&nbsp;&nbsp;&nbsp;<B> posBasesHit:</B>%d<BR>\n",
 		       dup.posBasesHit);
 	}
 	if (alignUrl != NULL)
 	    printf("<A HREF=%s/%s "
 		   "TARGET=\"%s:%d-%d\">Optimal Global Alignment</A><BR>\n",
 		   alignUrl, dup.alignfile, dup.chrom,
 		   dup.chromStart, dup.chromEnd);
 	printf("<B>Alignment Length:</B> %d<BR>\n", dup.alignL);
 	printf("&nbsp;&nbsp;&nbsp;<B>Indels #:</B> %d<BR>\n", dup.indelN);
 	printf("&nbsp;&nbsp;&nbsp;<B>Indels bp:</B> %d<BR>\n", dup.indelS);
 	printf("&nbsp;&nbsp;&nbsp;<B>Aligned Bases:</B> %d<BR>\n", dup.alignB);
         printf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B>Matching bases:</B> %d<BR>\n",
 	       dup.matchB);
 	printf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B>Mismatched bases:</B> %d<BR>\n",
 	       dup.mismatchB);
 	printf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B>Transitions:</B> %d<BR>\n",
 	       dup.transitionsB);
 	printf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B>Transverions:</B> %d<BR>\n",
 	       dup.transversionsB);
 	printf("&nbsp;&nbsp;&nbsp;<B>Fraction Matching:</B> %3.4f<BR>\n",
 	       dup.fracMatch);
 	printf("&nbsp;&nbsp;&nbsp;<B>Fraction Matching with Indels:</B> %3.4f<BR>\n",
 	       dup.fracMatchIndel);
 	printf("&nbsp;&nbsp;&nbsp;<B>Jukes Cantor:</B> %3.4f<BR>\n", dup.jcK);
 	}
     dyStringFree(&query);
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 else
     puts("<P>Click directly on a repeat for specific information on that repeat</P>");
 printTrackHtml(tdb);
 }
 /* end of Evan Eichler's stuff */
 
 void doCgh(char *track, char *tissue, struct trackDb *tdb)
 /* Create detail page for comparative genomic hybridization track */
 {
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 
 /* Print out non-sequence info */
 cartWebStart(cart, database, "%s", tissue);
 
 /* Print general range info */
 printf("<H2>UCSF Comparative Genomic Hybridizations - %s</H2>\n", tissue);
 printf("<P><HR ALIGN=\"CENTER\"></P>\n<TABLE>\n");
 printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n",seqName);
 printf("<TR><TH ALIGN=left>Start window:</TH><TD>%d</TD></TR>\n",winStart);
 printf("<TR><TH ALIGN=left>End window:</TH><TD>%d</TD></TR>\n",winEnd);
 printf("</TABLE>\n");
 printf("<P><HR ALIGN=\"CENTER\"></P>\n");
 
 /* Find the names of all of the clones in this range */
 printf("<TABLE>\n");
 printf("<TR><TH>Cell Line</TH>");
 sqlSafef(query, sizeof query, "SELECT spot from cgh where chrom = '%s' AND "
                "chromStart <= '%d' AND chromEnd >= '%d' AND "
                "tissue = '%s' AND type = 3 GROUP BY spot ORDER BY chromStart",
 	seqName, winEnd, winStart, tissue);
 sr = sqlMustGetResult(conn, query);
 while ((row = sqlNextRow(sr)))
     printf("<TH>Spot %s</TH>",row[0]);
 printf("</TR>\n");
 sqlFreeResult(&sr);
 
 /* Find the relevant tissues type records in the range */
 fillCghTable(3, tissue, FALSE);
 printf("<TR><TD>&nbsp;</TD></TR>\n");
 
 /* Find the relevant tissue average records in the range */
 fillCghTable(2, tissue, TRUE);
 printf("<TR><TD>&nbsp;</TD></TR>\n");
 
 /* Find the all tissue average records in the range */
 fillCghTable(1, NULL, TRUE);
 printf("<TR><TD>&nbsp;</TD></TR>\n");
 
 printf("</TR>\n</TABLE>\n");
 hFreeConn(&conn);
 }
 
 void doMcnBreakpoints(char *track, char *name, struct trackDb *tdb)
 /* Create detail page for MCN breakpoints track */
 {
 char query[256];
 char title[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 char **row;
 struct mcnBreakpoints *mcnRecord;
 
 /* Print out non-sequence info */
 safef(title, sizeof title, "MCN Breakpoints - %s",name);
 cartWebStart(cart, database, "%s", title);
 
 /* Print general range info */
 /*printf("<H2>MCN Breakpoints - %s</H2>\n", name);
   printf("<P><HR ALIGN=\"CENTER\"></P>");*/
 printf("<TABLE>\n");
 printf("<TR><TH ALIGN=left>Chromosome:</TH><TD>%s</TD></TR>\n",seqName);
 printf("<TR><TH ALIGN=left>Begin in Chromosome:</TH><TD>%d</TD></TR>\n",start);
 printf("<TR><TH ALIGN=left>End in Chromosome:</TH><TD>%d</TD></TR>\n",end);
 printBand(seqName, start, end, TRUE);
 printf("</TABLE>\n");
 
 /* Find all of the breakpoints in this range for this name*/
 sqlSafef(query, sizeof query, "SELECT * FROM mcnBreakpoints WHERE chrom = '%s' AND "
                "chromStart = %d and chromEnd = %d AND name = '%s'",
 	seqName, start, end, name);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)))
     {
     printf("<P><HR ALIGN=\"CENTER\"></P>\n");
     mcnRecord = mcnBreakpointsLoad(row);
     printf("<TABLE>\n");
     printf("<TR><TH ALIGN=left>Case ID:</TH><TD>%s</TD></TR>", mcnRecord->caseId);
     printf("<TR><TH ALIGN=left>Breakpoint ID:</TH><TD>%s</TD></TR>", mcnRecord->bpId);
     printf("<TR><TH ALIGN=left>Trait:</TH><TD>%s</TD><TD>%s</TD></TR>", mcnRecord->trId, mcnRecord->trTxt);
     printf("<TR><TH ALIGN=left>Trait Group:</TH><TD>%s</TD><TD>%s</TD></TR>", mcnRecord->tgId, mcnRecord->tgTxt);
     printf("</TR>\n</TABLE>\n");
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void doProbeDetails(struct trackDb *tdb, char *item)
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct dnaProbe *dp = NULL;
 char query[256];
 
 genericHeader(tdb, item);
 sqlSafef(query, sizeof(query), "select * from dnaProbe where name='%s'",  item);
 dp = dnaProbeLoadByQuery(conn, query);
 if(dp != NULL)
     {
     printf("<h3>Probe details:</h3>\n");
     printf("<b>Name:</b> %s  <span style='font-size:x-small;'>"
            "[dbName genomeVersion strand coordinates]</span><br>\n",dp->name);
     printf("<b>Dna:</b> %s", dp->dna );
     printf("[<a href=\"hgBlat?type=DNA&genome=hg8&sort=&query,score&output=hyperlink&userSeq=%s\">blat (blast like alignment)</a>]<br>", dp->dna);
     printf("<b>Size:</b> %d<br>", dp->size );
     printf("<b>Chrom:</b> %s<br>", dp->chrom );
     printf("<b>ChromStart:</b> %d<br>", dp->start+1 );
     printf("<b>ChromEnd:</b> %d<br>", dp->end );
     printf("<b>Strand:</b> %s<br>", dp->strand );
     printf("<b>3' Dist:</b> %d<br>", dp->tpDist );
     printf("<b>Tm:</b> %f <span style='font-size:x-small;'>"
            "[scores over 100 are allowed]</span><br>", dp->tm );
     printf("<b>%%GC:</b> %f<br>", dp->pGC );
     printf("<b>Affy:</b> %d <span style='font-size:x-small;'>"
            "[1 passes, 0 doesn't pass Affy heuristic]</span><br>", dp->affyHeur );
     printf("<b>Sec Struct:</b> %f<br>", dp->secStruct);
     printf("<b>blatScore:</b> %d<br>", dp->blatScore );
     printf("<b>Comparison:</b> %f<br>", dp->comparison);
     }
 /* printf("<h3>Genomic Details:</h3>\n");
  * genericBedClick(conn, tdb, item, start, 1); */
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doChicken13kDetails(struct trackDb *tdb, char *item)
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct chicken13kInfo *chick = NULL;
 char query[256];
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, item);
 sqlSafef(query, sizeof(query), "select * from chicken13kInfo where id='%s'",  item);
 chick = chicken13kInfoLoadByQuery(conn, query);
 if (chick != NULL)
     {
     printf("<b>Probe name:</b> %s<br>\n", chick->id);
     printf("<b>Source:</b> %s<br>\n", chick->source);
     printf("<b>PCR Amplification code:</b> %s<br>\n", chick->pcr);
     printf("<b>Library:</b> %s<br>\n", chick->library);
     printf("<b>Source clone name:</b> %s<br>\n", chick->clone);
     printf("<b>Library:</b> %s<br>\n", chick->library);
     printf("<b>Genbank accession:</b> %s<br>\n", chick->gbkAcc);
     printf("<b>BLAT alignment:</b> %s<br>\n", chick->blat);
     printf("<b>Source annotation:</b> %s<br>\n", chick->sourceAnnot);
     printf("<b>TIGR assigned TC:</b> %s<br>\n", chick->tigrTc);
     printf("<b>TIGR TC annotation:</b> %s<br>\n", chick->tigrTcAnnot);
     printf("<b>BLAST determined annotation:</b> %s<br>\n", chick->blastAnnot);
     printf("<b>Comment:</b> %s<br>\n", chick->comment);
     }
 genericBedClick(conn, tdb, item, start, 1);
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void perlegenDetails(struct trackDb *tdb, char *item)
 {
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 char table[64];
 boolean hasBin;
 struct bed *bed;
 char query[512];
 struct sqlResult *sr;
 char **row;
 boolean firstTime = TRUE;
 int numSnpsReq = -1;
 if(tdb == NULL)
     errAbort("TrackDb entry null for perlegen, item=%s\n", item);
 
 genericHeader(tdb, item);
 printCustomUrl(tdb, item, FALSE);
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
         table, item, seqName, start);
 sr = sqlGetResult(conn, query);
 
 while ((row = sqlNextRow(sr)) != NULL)
     {
     char *name;
     /* set up for first time */
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     bed = bedLoadN(row+hasBin, 12);
 
     /* chop leading digits off name which should be in x/yyyyyy format */
     name = strstr(bed->name, "/");
     if(name == NULL)
         name = bed->name;
     else
         name++;
 
     /* determine number of SNPs required from score */
     switch(bed->score)
         {
 	case 1000:
 	    numSnpsReq = 0;
 	    break;
 	case 650:
 	    numSnpsReq = 1;
 	    break;
 	case 500:
 	    numSnpsReq = 2;
 	    break;
 	case 250:
 	    numSnpsReq = 3;
 	    break;
 	case 50:
 	    numSnpsReq = 4;
 	    break;
 	}
 
     /* finish off report ... */
     printf("<B>Block:</B> %s<BR>\n", name);
     printf("<B>Number of SNPs in block:</B> %d<BR>\n", bed->blockCount);
     printf("<B>Number of SNPs to represent block:</B> %d<BR>\n",numSnpsReq);
     printf("<B>Strand:</B> %s<BR>\n", bed->strand);
     bedPrintPos(bed, 3, tdb);
     }
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void haplotypeDetails(struct trackDb *tdb, char *item)
 {
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 char table[64];
 boolean hasBin;
 struct bed *bed;
 char query[512];
 struct sqlResult *sr;
 char **row;
 boolean firstTime = TRUE;
 if(tdb == NULL)
     errAbort("TrackDb entry null for haplotype, item=%s\n", item);
 
 genericHeader(tdb, item);
 printCustomUrl(tdb, item, TRUE);
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
         table, item, seqName, start);
 sr = sqlGetResult(conn, query);
 
 while ((row = sqlNextRow(sr)) != NULL)
     {
     /* set up for first time */
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     bed = bedLoadN(row+hasBin, 12);
 
     /* finish off report ... */
     printf("<B>Block:</B> %s<BR>\n", bed->name);
     printf("<B>Number of SNPs in block:</B> %d<BR>\n", bed->blockCount);
     /*    printf("<B>Number of SNPs to represent block:</B> %d<BR>\n",numSnpsReq);*/
     printf("<B>Strand:</B> %s<BR>\n", bed->strand);
     bedPrintPos(bed, 3, tdb);
     }
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void mitoDetails(struct trackDb *tdb, char *item)
 {
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 char table[64];
 boolean hasBin;
 struct bed *bed;
 char query[512];
 struct sqlResult *sr;
 char **row;
 boolean firstTime = TRUE;
 int numSnpsReq = -1;
 if(tdb == NULL)
     errAbort("TrackDb entry null for mitoSnps, item=%s\n", item);
 
 genericHeader(tdb, item);
 printCustomUrl(tdb, item, TRUE);
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d",
         table, item, seqName, start);
 sr = sqlGetResult(conn, query);
 
 while ((row = sqlNextRow(sr)) != NULL)
     {
     char *name;
     /* set up for first time */
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     bed = bedLoadN(row+hasBin, 12);
 
     /* chop leading digits off name which should be in xx/yyyyyy format */
     name = strstr(bed->name, "/");
     if(name == NULL)
         name = bed->name;
     else
         name++;
 
     /* determine number of SNPs required from score */
     switch(bed->score)
         {
 	case 1000:
 	    numSnpsReq = 0;
 	    break;
 	case 650:
 	    numSnpsReq = 1;
 	    break;
 	case 500:
 	    numSnpsReq = 2;
 	    break;
 	case 250:
 	    numSnpsReq = 3;
 	    break;
 	case 50:
 	    numSnpsReq = 4;
 	    break;
 	}
     /* finish off report ... */
     printf("<B>Block:</B> %s<BR>\n", name);
     printf("<B>Number of SNPs in block:</B> %d<BR>\n", bed->blockCount);
     printf("<B>Number of SNPs to represent block:</B> %d<BR>\n",numSnpsReq);
     printf("<B>Strand:</B> %s<BR>\n", bed->strand);
     bedPrintPos(bed, 3, tdb);
     }
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void ancientRDetails(struct trackDb *tdb, char *item)
 {
 struct sqlConnection *conn = hAllocConn(database);
 char table[64];
 boolean hasBin;
 struct bed *bed = NULL;
 char query[512];
 struct sqlResult *sr = NULL;
 char **row;
 boolean firstTime = TRUE;
 double ident = -1.0;
 struct ancientRref *ar = NULL;
 
 if(tdb == NULL)
     errAbort("TrackDb entry null for ancientR, item=%s\n", item);
 genericHeader(tdb, item);
 printCustomUrl(tdb, item, TRUE);
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s'",
         table, item, seqName );
 sr = sqlGetResult(conn, query);
 
 while ((row = sqlNextRow(sr)) != NULL)
     {
     char *name;
     /* set up for first time */
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     bed = bedLoadN(row+hasBin, 12);
 
     name = bed->name;
 
     /* get % identity from score */
     ident = ((bed->score + 500.0)/1500.0)*100.0;
 
     /* finish off report ... */
     printf("<h4><i>Joint Alignment</i></h4>");
     printf("<B>ID:</B> %s<BR>\n", name);
     printf("<B>Number of aligned blocks:</B> %d<BR>\n", bed->blockCount);
 
     if( ident == 50.0 )
         printf("<B>Percent identity of aligned blocks:</B> <= %g%%<BR>\n", ident);
     else
         printf("<B>Percent identity of aligned blocks:</B> %g%%<BR>\n", ident);
 
     printf("<h4><i>Human Sequence</i></h4>");
     printf("<B>Strand:</B> %s<BR>\n", bed->strand);
     bedPrintPos(bed, 3, tdb);
 
     }
 
 /* look in associated table 'ancientRref' to get human/mouse alignment*/
 sqlSafef(query, sizeof query, "select * from %sref where id = '%s'", table, item );
 sr = sqlGetResult( conn, query );
 while ((row = sqlNextRow(sr)) != NULL )
     {
     ar = ancientRrefLoad(row);
 
     printf("<h4><i>Repeat</i></h4>");
     printf("<B>Name:</B> %s<BR>\n", ar->name);
     printf("<B>Class:</B> %s<BR>\n", ar->class);
     printf("<B>Family:</B> %s<BR>\n", ar->family);
 
     /* print the aligned sequences in html on multiple rows */
     htmlHorizontalLine();
     printf("<i>human sequence on top, mouse on bottom</i><br><br>" );
     htmlPrintJointAlignment( ar->hseq, ar->mseq, 80,
 			     bed->chromStart, bed->chromEnd, bed->strand );
     }
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doGcDetails(struct trackDb *tdb, char *itemName)
 /* Show details for gc percent */
 {
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 struct gcPercent *gc;
 boolean hasBin;
 char table[64];
 
 cartWebStart(cart, database, "Percentage GC in 20,000 Base Windows (GC)");
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where chrom = '%s' and chromStart = %d and name = '%s'",
 	table, seqName, start, itemName);
 
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     gc = gcPercentLoad(row + hasBin);
     printPos(gc->chrom, gc->chromStart, gc->chromEnd, NULL, FALSE, NULL);
     printf("<B>GC Percentage:</B> %3.1f%%<BR>\n", ((float)gc->gcPpt)/10);
     gcPercentFree(&gc);
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 void chuckHtmlStart(char *title)
 /* Prints the header appropriate for the title
  * passed in. Links html to chucks stylesheet for
  * easier maintaince
  */
 {
 printf("<HTML>\n<HEAD>\n");
 // FIXME blueStyle should not be absolute to genome-test and should be called by:
 //       webIncludeResourceFile("blueStyle.css");
 printf("<LINK REL=STYLESHEET TYPE=\"text/css\" href=\"http://genome-test.cse.ucsc.edu/style/blueStyle.css\" title=\"Chuck Style\">\n");
 printf("<title>%s</title>\n</head><body bgcolor=\"#f3f3ff\">",title);
 }
 
 void chuckHtmlContactInfo()
 /* Writes out Chuck's email so people bother Chuck instead of Jim */
 {
 puts("<br><br><span style='font-size:x-small;'><i>If you have comments and/or suggestions please email "
      "<a href=\"mailto:sugnet@cse.ucsc.edu\">sugnet@cse.ucsc.edu</a>.</span>\n");
 }
 
 
 void abbr(char *s, char *fluff)
 /* Cut out fluff from s. */
 {
 int len;
 s = strstr(s, fluff);
 if (s != NULL)
     {
     len = strlen(fluff);
     strcpy(s, s+len);
     }
 }
 
 void printTableHeaderName(char *name, char *clickName, char *url)
 /* creates a table to display a name vertically,
  * basically creates a column of letters */
 {
 int i, length;
 char *header = cloneString(name);
 header = cloneString(header);
 subChar(header,'_',' ');
 length = strlen(header);
 if(url == NULL)
     url = cloneString("");
 /* printf("<b>Name:</b> %s\t<b>clickName:</b> %s\n", name,clickName); */
 if (strstr(clickName,name))
     printf("<table border=0 cellspacing=0 cellpadding=0 bgcolor='#D9E4F8'>\n");
 else
     printf("<table border=0 cellspacing=0 cellpadding=0>\n");
 for(i = 0; i < length; i++)
     {
     if (header[i] == ' ')
         printf("<tr><td align=center>&nbsp</td></tr>\n");
     else
         {
         if (strstr(clickName,name))
             printf("<tr><td align=center bgcolor='#D9E4F8'>");
         else
             printf("<tr><td align=center>");
 
         /* if we have a url, create a reference */
         if (differentString(url,""))
             printf("<a href=\"%s\" TARGET=_BLANK>%c</a>", url, header[i]);
 	else
 	    printf("%c", header[i]);
 
 	printf("</td></tr>");
 	}
     printf("\n");
     }
 printf("</table>\n");
 freez(&header);
 }
 
 struct sageExp *loadSageExps(char *tableName, struct bed  *bedist)
 /* load the sage experiment data. */
 {
 struct sqlConnection *sc = NULL;
 /* struct sqlConnection *sc = sqlConnectRemote("localhost", user, password, "hgFixed"); */
 char query[256];
 struct sageExp *seList = NULL, *se=NULL;
 char **row;
 struct sqlResult *sr = NULL;
 if(hTableExists(database, tableName))
     sc = hAllocConn(database);
 else
     sc = hAllocConn("hgFixed");
 
 sqlSafef(query, sizeof query,"select * from sageExp order by num");
 sr = sqlGetResult(sc,query);
 while((row = sqlNextRow(sr)) != NULL)
     {
     se = sageExpLoad(row);
     slAddHead(&seList,se);
     }
 sqlFreeResult(&sr);
 hFreeConn(&sc);
 slReverse(&seList);
 return seList;
 }
 
 struct sage *loadSageData(char *table, struct bed* bedList)
 /* load the sage data by constructing a query based on the qNames of the bedList */
 {
 struct sqlConnection *sc = NULL;
 struct dyString *query = newDyString(2048);
 struct sage *sgList = NULL, *sg=NULL;
 struct bed *bed=NULL;
 char **row;
 int count=0;
 struct sqlResult *sr = NULL;
 if(hTableExists(database, table))
     sc = hAllocConn(database);
 else
     sc = hAllocConn("hgFixed");
 sqlDyStringPrintf(query, "select * from sage where ");
 for(bed=bedList;bed!=NULL;bed=bed->next)
     {
     if (count++)
         {
         dyStringPrintf(query," or uni=%d ", atoi(bed->name + 3 ));
         }
     else
 	{
 	dyStringPrintf(query," uni=%d ", atoi(bed->name + 3));
 	}
     }
 sr = sqlGetResult(sc,query->string);
 while((row = sqlNextRow(sr)) != NULL)
     {
     sg = sageLoad(row);
     slAddHead(&sgList,sg);
     }
 sqlFreeResult(&sr);
 hFreeConn(&sc);
 slReverse(&sgList);
 freeDyString(&query);
 return sgList;
 }
 
 int sageBedWSListIndex(struct bed *bedList, int uni)
 /* find the index of a bed by the unigene identifier in a bed list */
 {
 struct bed *bed;
 int count =0;
 char buff[128];
 safef(buff, sizeof buff, "Hs.%d", uni);
 for(bed = bedList; bed != NULL; bed = bed->next)
     {
     if(sameString(bed->name,buff))
 	return count;
     count++;
     }
 errAbort("Didn't find the unigene tag %s",buff);
 return 0;
 }
 
 int sortSageByBedOrder(const void *e1, const void *e2)
 /* used by slSort to sort the sage experiment data using the order of the beds */
 {
 const struct sage *s1 = *((struct sage**)e1);
 const struct sage *s2 = *((struct sage**)e2);
 return(sageBedWSListIndex(sageExpList,s1->uni) - sageBedWSListIndex(sageExpList,s2->uni));
 }
 
 void printSageGraphUrl(struct sage *sgList)
 /* print out a url to a cgi script which will graph the results */
 {
 struct sage *sg = NULL;
 if (sgList == NULL)
     return;
 printf("Please click ");
 printf("<a target=_blank href=\"../cgi-bin/sageVisCGI?");
 for(sg = sgList; sg != NULL; sg = sg->next)
     {
     if(sg->next == NULL)
 	printf("u=%d", sg->uni);
     else
 	printf("u=%d&", sg->uni);
 
     }
 printf("&db=%s",database);
 printf("\">here</a>");
 printf(" to see the data as a graph.\n");
 }
 
 void printSageReference(struct sage *sgList, struct trackDb *tdb)
 {
 printf("%s", tdb->html);
 printTBSchemaLink(tdb);
 }
 
 void sagePrintTable(struct bed *bedList, char *itemName, struct trackDb *tdb)
 /* load up the sage experiment data using bed->qNames and display it as a table */
 {
 struct sageExp *seList = NULL, *se =NULL;
 struct sage *sgList=NULL, *sg=NULL;
 int featureCount;
 int count=0;
 seList=loadSageExps("sageExp",bedList);
 sgList = loadSageData("sage", bedList);
 slSort(&sgList,sortSageByBedOrder);
 
 printSageReference(sgList, tdb);
 /* temporarily disable this link until debugged and fixed.  Fan
 printSageGraphUrl(sgList);
 */
 printf("<BR>\n");
 for(sg=sgList; sg != NULL; sg = sg->next)
     {
     char buff[256];
     safef(buff, sizeof buff, "Hs.%d", sg->uni);
     }
 featureCount= slCount(sgList);
 printf("<basefont size=-1>\n");
 printf("<table cellspacing=0 style='border:1px solid black;'>\n");
 printf("<tr>\n");
 printf("<th align=center>Sage Experiment</th>\n");
 printf("<th align=center>Tissue</th>\n");
 printf("<th align=center colspan=%d valign=top>Uni-Gene Clusters<br>(<b>Median</b> [Ave &plusmn Stdev])</th>\n",featureCount);
 printf("</tr>\n<tr><td>&nbsp</td><td>&nbsp</td>\n");
 for(sg = sgList; sg != NULL; sg = sg->next)
     {
     char buff[32];
     char url[256];
     safef(buff, sizeof buff, "Hs.%d", sg->uni);
     printf("<td valign=top align=center>\n");
     safef(url, sizeof url, "http://www.ncbi.nlm.nih.gov/SAGE/SAGEcid.cgi?cid=%d&org=Hs",sg->uni);
     printTableHeaderName(buff, itemName, url);
     printf("</td>");
     }
 printf("</tr>\n");
 /* for each experiment write out the name and then all of the values */
 for(se=seList;se!=NULL;se=se->next)
     {
     char *tmp;
     tmp = strstr(se->exp,"_");
     if(++count%2)
 	printf("<tr>\n");
     else
 	printf("<tr bgcolor=\"#bababa\">\n");
     printf("<td align=left>");
     printf("%s</td>\n", tmp ? (tmp+1) : se->exp);
 
     printf("<td align=left>%s</td>\n", se->tissueType);
     for(sg=sgList; sg!=NULL; sg=sg->next)
         {
         if (sg->aves[se->num] == -1.0)
             printf("<td>N/A</td>");
         else
             printf("<td>  <b>%4.1f</b> <span style='font-size:x-small;'>[%.2f &plusmn %.2f]</span></td>\n",
 		   sg->meds[se->num],sg->aves[se->num],sg->stdevs[se->num]);
 	}
     printf("</tr>\n");
     }
 printf("</table>\n");
 }
 
 
 struct bed *bedWScoreLoadByChrom(char *table, char *chrom, int start, int end)
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 struct bed *bedWS, *bedWSList = NULL;
 char **row;
 char query[256];
 struct hTableInfo *hti = hFindTableInfo(database, seqName, table);
 if(hti == NULL)
     errAbort("Can't find table: (%s) %s", seqName, table);
 else if(hti && sameString(hti->startField, "tStart"))
     sqlSafef(query, sizeof(query),
              "select qName,tStart,tEnd from %s where tName='%s' and tStart < %u and tEnd > %u",
              table, seqName, winEnd, winStart);
 else if(hti && sameString(hti->startField, "chromStart"))
     sqlSafef(query, sizeof(query),
              "select name,chromStart,chromEnd from %s"
              " where chrom='%s' and chromStart < %u and chromEnd > %u",
              table, seqName, winEnd, winStart);
 else
     errAbort("%s doesn't have tStart or chromStart", table);
 sr = sqlGetResult(conn, query);
 while((row = sqlNextRow(sr)) != NULL)
     {
     AllocVar(bedWS);
     bedWS->name = cloneString(row[0]);
     bedWS->chromStart = sqlUnsigned(row[1]);
     bedWS->chromEnd = sqlUnsigned(row[2]);
     bedWS->chrom = cloneString(seqName);
     slAddHead(&bedWSList, bedWS);
     }
 slReverse(&bedWSList);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 return bedWSList;
 }
 
 /* Lowe Lab additions */
 void doSageDataDisp(char *tableName, char *itemName, struct trackDb *tdb)
 {
 struct bed *sgList = NULL;
 int sgCount=0;
 chuckHtmlStart("Sage Data Requested");
 printf("<h2>Sage Data for: %s %d-%d</h2>\n", seqName, winStart+1, winEnd);
 puts("<table cellpadding=0 cellspacing=0><tr><td>\n");
 
 sgList = bedWScoreLoadByChrom(tableName, seqName, winStart, winEnd);
 
 sgCount = slCount(sgList);
 if(sgCount > 50)
     printf("<hr><p>That will create too big of a table, try creating a window with less than 50 elements.<hr>\n");
 else
     {
     sageExpList = sgList;
     sagePrintTable(sgList, itemName, tdb);
     }
 printf("</td></tr></table>\n");
 /*zeroBytes(buff,64);
   safe(buff, sizeof buff, "%d", winStart);
   cgiMakeHiddenVar("winStart", buff);
   zeroBytes(buff,64);
   safef(buff, sizeof buff, "%d", winEnd);
   cgiMakeHiddenVar("winEnd", buff);
   cgiMakeHiddenVar("db",database);
   printf("<br>\n");*/
 chuckHtmlContactInfo();
 }
 
 void makeGrayShades(struct hvGfx *hvg)
 /* Make eight shades of gray in display. */
 {
 int i;
 for (i=0; i<=maxShade; ++i)
     {
     struct rgbColor rgb;
     int level = 255 - (255*i/maxShade);
     if (level < 0) level = 0;
     rgb.r = rgb.g = rgb.b = level;
     shadesOfGray[i] = hvGfxFindRgb(hvg, &rgb);
     }
 shadesOfGray[maxShade+1] = MG_RED;
 }
 
 void mgMakeColorGradient(struct memGfx *mg,
                          struct rgbColor *start, struct rgbColor *end,
                          int steps, Color *colorIxs)
 /* Make a color gradient that goes smoothly from start
  * to end colors in given number of steps.  Put indices
  * in color table in colorIxs */
 {
 double scale = 0, invScale;
 double invStep;
 int i;
 int r,g,b;
 
 steps -= 1;	/* Easier to do the calculation in an inclusive way. */
 invStep = 1.0/steps;
 for (i=0; i<=steps; ++i)
     {
     invScale = 1.0 - scale;
     r = invScale * start->r + scale * end->r;
     g = invScale * start->g + scale * end->g;
     b = invScale * start->b + scale * end->b;
     colorIxs[i] = mgFindColor(mg, r, g, b);
     scale += invStep;
     }
 }
 
 void makeRedGreenShades(struct memGfx *mg)
 /* Allocate the  shades of Red, Green and Blue */
 {
 static struct rgbColor black = {0, 0, 0};
 static struct rgbColor red = {255, 0, 0};
 mgMakeColorGradient(mg, &black, &red, maxRGBShade+1, shadesOfRed);
 exprBedColorsMade = TRUE;
 }
 
 char *altGraphXMakeImage(struct trackDb *tdb, struct altGraphX *ag)
 /* Create a drawing of splicing pattern. */
 {
 MgFont *font = mgSmallFont();
 int fontHeight = mgFontLineHeight(font);
 struct spaceSaver *ssList = NULL;
 struct hash *heightHash = NULL;
 int rowCount = 0;
 struct tempName gifTn;
 int pixWidth = atoi(cartUsualString(cart, "pix", DEFAULT_PIX_WIDTH ));
 int pixHeight = 0;
 struct hvGfx *hvg;
 int lineHeight = 0;
 double scale = 0;
 
 scale = (double)pixWidth/(ag->tEnd - ag->tStart);
 lineHeight = 2 * fontHeight +1;
 altGraphXLayout(ag, ag->tStart, ag->tEnd, scale, 100, &ssList, &heightHash, &rowCount);
 pixHeight = rowCount * lineHeight;
 trashDirFile(&gifTn, "hgc", "hgc", ".png");
 hvg = hvGfxOpenPng(pixWidth, pixHeight, gifTn.forCgi, FALSE);
 makeGrayShades(hvg);
 hvGfxSetClip(hvg, 0, 0, pixWidth, pixHeight);
 altGraphXDrawPack(ag, ssList, hvg, 0, 0, pixWidth, lineHeight, lineHeight-1,
 		  ag->tStart, ag->tEnd, scale, font, MG_BLACK, shadesOfGray, "Dummy", NULL);
 hvGfxUnclip(hvg);
 hvGfxClose(&hvg);
 printf(
        "<IMG SRC = \"%s\" BORDER=1 WIDTH=%d HEIGHT=%d><BR>\n",
        gifTn.forHtml, pixWidth, pixHeight);
 return cloneString(gifTn.forHtml);
 }
 
 char *agXStringForEdge(struct altGraphX *ag, int i)
 /* classify an edge as intron or exon */
 {
 if(ag->vTypes[ag->edgeStarts[i]] == ggSoftStart ||
    ag->vTypes[ag->edgeStarts[i]] == ggHardStart)
     return "exon";
 else if (ag->vTypes[ag->edgeStarts[i]] == ggSoftEnd ||
 	 ag->vTypes[ag->edgeStarts[i]] == ggHardEnd)
     return "intron";
 else
     return "unknown";
 }
 
 char *agXStringForType(enum ggVertexType t)
 /* convert a type to a string */
 {
 switch (t)
     {
     case ggSoftStart:
 	return "ss";
     case ggHardStart:
 	return "hs";
     case ggSoftEnd:
 	return "se";
     case ggHardEnd:
 	return "he";
     default:
 	return "NA";
     }
 }
 
 void printAltGraphXEdges(struct altGraphX *ag)
 /* Print out at table showing all of the vertexes and
    edges of an altGraphX. */
 {
 int i = 0, j = 0;
 printf("<table cellpadding=1 border=1>\n");
 printf("</table>\n");
 printf("<table cellpadding=0 cellspacing=0>\n");
 printf("<tr><th><b>Vertices</b></th><th><b>Edges</b></th></tr>\n");
 printf("<tr><td valign=top>\n");
 printf("<table cellpadding=1 border=1>\n");
 printf("<tr><th><b>Number</b></th><th><b>Type</b></th></tr>\n");
 for(i=0; i<ag->vertexCount; i++)
     {
     printf("<tr><td>%d</td><td>%s</td></tr>\n", i, agXStringForType(ag->vTypes[i]));
     }
 printf("</table>\n");
 printf("</td><td valign=top>\n");
 printf("<table cellpadding=1 border=1>\n");
 printf("<tr><th><b>Start</b></th><th><b>End</b></th><th><b>Type</b></th><th><b>Evidence</b></th></tr>\n");
 for(i=0; i<ag->edgeCount; i++)
     {
     struct evidence *e =  slElementFromIx(ag->evidence, i);
     printf("<tr><td>%d</td><td>%d</td>",           ag->edgeStarts[i], ag->edgeEnds[i]);
     printf("<td><a href=\"%s&position=%s:%d-%d&mrna=full&intronEst=full&refGene=full&altGraphX=full\">%s</a></td><td>",
            hgTracksPathAndSettings(),
            ag->tName,
            ag->vPositions[ag->edgeStarts[i]],
 	   ag->vPositions[ag->edgeEnds[i]],
 	   agXStringForEdge(ag, i));
     for(j=0; j<e->evCount; j++)
 	printf("%s, ", ag->mrnaRefs[e->mrnaIds[j]]);
     printf("</td></tr>\n");
     }
 printf("</table>\n");
 }
 
 void doAltGraphXDetails(struct trackDb *tdb, char *item)
 /* do details page for an altGraphX */
 {
 int id = atoi(item);
 char query[256];
 struct altGraphX *ag = NULL;
 struct altGraphX *orthoAg = NULL;
 char buff[128];
 struct sqlConnection *conn = hAllocConn(database);
 char *image = NULL;
 
 /* Load the altGraphX record and start page. */
 if (id != 0)
     {
     sqlSafef(query, sizeof(query),"select * from %s where id=%d", tdb->table, id);
     ag = altGraphXLoadByQuery(conn, query);
     }
 else
     {
     sqlSafef(query, sizeof(query),
              "select * from %s where tName like '%s' and tStart <= %d and tEnd >= %d",
              tdb->table, seqName, winEnd, winStart);
     ag = altGraphXLoadByQuery(conn, query);
     }
 if (ag == NULL)
     errAbort("hgc::doAltGraphXDetails() - couldn't find altGraphX with id=%d", id);
 genericHeader(tdb, ag->name);
 printPosOnChrom(ag->tName, ag->tStart, ag->tEnd, ag->strand, FALSE, NULL);
 
 /* Print a display of the Graph. */
 printf("<b>Plots of Alt-Splicing:</b>");
 printf("<center>\n");
 if(sameString(tdb->table, "altGraphXPsb2004"))
     printf("Common Splicing<br>");
 printf("Alt-Splicing drawn to scale.<br>");
 image = altGraphXMakeImage(tdb,ag);
 freez(&image);
 /* Normally just print graph with exons scaled up. For conserved
    track also display orthologous loci. */
 if(differentString(tdb->table, "altGraphXPsb2004"))
     {
     struct altGraphX *copy = altGraphXClone(ag);
     altGraphXVertPosSort(copy);
     altGraphXEnlargeExons(copy);
     printf("<br>Alt-Splicing drawn with exons enlarged.<br>\n");
     image = altGraphXMakeImage(tdb,copy);
     freez(&image);
     altGraphXFree(&copy);
     }
 else
     {
     struct sqlConnection *orthoConn = NULL;
     struct altGraphX *origAg = NULL;
     char *db2="mm3";
     sqlSafef(query, sizeof(query), "select * from altGraphX where name='%s'", ag->name);
     origAg = altGraphXLoadByQuery(conn, query);
     puts("<br><center>Human</center>\n");
     altGraphXMakeImage(tdb,origAg);
     orthoConn = hAllocConn(db2);
     sqlSafef(query, sizeof(query), "select orhtoAgName from orthoAgReport where agName='%s'", ag->name);
     sqlQuickQuery(conn, query, buff, sizeof(buff));
     sqlSafef(query, sizeof(query), "select * from altGraphX where name='%s'", buff);
     orthoAg = altGraphXLoadByQuery(orthoConn, query);
     if(differentString(orthoAg->strand, origAg->strand))
 	{
 	altGraphXReverseComplement(orthoAg);
 	puts("<br>Mouse (opposite strand)\n");
 	}
     else
 	puts("<br>Mouse\n");
     printf("<a HREF=\"%s&db=%s&position=%s:%d-%d&mrna=squish&intronEst=squish&refGene=pack&altGraphX=full\"",
 	   hgTracksName(),
 	   "mm3", orthoAg->tName, orthoAg->tStart, orthoAg->tEnd);
     printf(" ALT=\"Zoom to browser coordinates of altGraphX\">");
     printf("<span style='font-size:smaller;'>[%s.%s:%d-%d]</span></a><br><br>\n", "mm3",
 	   orthoAg->tName, orthoAg->tStart, orthoAg->tEnd);
     altGraphXMakeImage(tdb,orthoAg);
     }
 printf("<br><a HREF=\"%s&position=%s:%d-%d&mrna=full&intronEst=full&refGene=full&altGraphX=full\"",
        hgTracksPathAndSettings(), ag->tName, ag->tStart, ag->tEnd);
 printf(" ALT=\"Zoom to browser coordinates of Alt-Splice\">");
 printf("Jump to browser for %s</a><span style='font-size:smaller;'> [%s:%d-%d] </span><br><br>\n",
        ag->name, ag->tName, ag->tStart, ag->tEnd);
 if(cgiVarExists("agxPrintEdges"))
     printAltGraphXEdges(ag);
 printf("</center>\n");
 hFreeConn(&conn);
 }
 
 
 struct lineFile *openExtLineFile(unsigned int extFileId)
 /* Open line file corresponding to id in extFile table. */
 {
 char *path = hExtFileName(database, "extFile", extFileId);
 struct lineFile *lf = lineFileOpen(path, TRUE);
 freeMem(path);
 return lf;
 }
 
 void printSampleWindow( struct psl *thisPsl, int thisWinStart, int
                         thisWinEnd, char *winStr, char *otherOrg, char *otherDb,
 			char *pslTableName )
 {
 char otherString[256];
 char pslItem[1024];
 
 safef(pslItem, sizeof pslItem, "%s:%d-%d %s:%d-%d", 
     thisPsl->qName, thisPsl->qStart, thisPsl->qEnd, thisPsl->tName, thisPsl->tStart, thisPsl->tEnd );
 safef(otherString, sizeof otherString, "%d&pslTable=%s&otherOrg=%s&otherChromTable=%s&otherDb=%s", thisPsl->tStart,
 	pslTableName, otherOrg, "chromInfo" , otherDb );
 if (pslTrimToTargetRange(thisPsl, thisWinStart, thisWinEnd) != NULL)
     {
     hgcAnchorWindow("htcLongXenoPsl2", pslItem, thisWinStart,
 		    thisWinEnd, otherString, thisPsl->tName);
     printf("%s</A>\n", winStr );
     }
 }
 
 
 void firstAndLastPosition( int *thisStart, int *thisEnd, struct psl *thisPsl )
 /*return the first and last base of a psl record (not just chromStart
  * and chromEnd but the actual blocks.*/
 {
 *thisStart = thisPsl->tStarts[0];
 *thisEnd = thisPsl->tStarts[thisPsl->blockCount - 1];
 if( thisPsl->strand[1] == '-' )
     {
     *thisStart = thisPsl->tSize - *thisStart;
     *thisEnd = thisPsl->tSize - *thisEnd;
     }
 *thisEnd += thisPsl->blockSizes[thisPsl->blockCount - 1];
 }
 
 boolean sampleClickRelevant( struct sample *smp, int i, int left, int right,
 			     int humMusWinSize, int thisStart, int thisEnd )
 /* Decides if a sample is relevant for the current window and psl
  * record start and end positions */
 {
 
 if ((smp->chromStart + smp->samplePosition[i] - humMusWinSize / 2 + 1) < left
 &&  (smp->chromStart + smp->samplePosition[i] + humMusWinSize / 2    ) < left )
     return(0);
 
 if( smp->chromStart + smp->samplePosition[i] -
                                                 humMusWinSize / 2  + 1  < thisStart
 &&  (smp->chromStart + smp->samplePosition[i] + humMusWinSize / 2     ) < thisStart  )
     return(0);
 
 if ((smp->chromStart + smp->samplePosition[i] -  humMusWinSize / 2 + 1) > right
 &&  (smp->chromStart + smp->samplePosition[i] +  humMusWinSize / 2    ) > right )
     return(0);
 
 
 if( smp->chromStart + smp->samplePosition[i] -
                                                 humMusWinSize / 2 + 1  > thisEnd
     && smp->chromStart + smp->samplePosition[i] +
                                                 humMusWinSize / 2      > thisEnd  )
     return(0);
 
 return(1);
 }
 
 static double whichNum( double tmp, double min0, double max0, int n)
 /*gets range nums. from bin values*/
 {
 return( (max0 - min0)/(double)n * tmp + min0 );
 }
 
 void humMusSampleClick(struct sqlConnection *conn, struct trackDb *tdb,
 		       char *item, int start, int smpSize, char *otherOrg, char *otherDb,
 		       char *pslTableName, boolean printWindowFlag )
 /* Handle click in humMus sample (wiggle) track. */
 {
 int humMusWinSize = 50;
 int i;
 char table[64];
 boolean hasBin;
 struct sample *smp;
 char query[512];
 char tempTableName[1024];
 struct sqlResult *sr;
 char **row;
 char **pslRow;
 boolean firstTime = TRUE;
 struct psl *thisPsl;
 char str[256];
 char thisItem[256];
 char otherString[256] = "";
 struct sqlResult *pslSr;
 struct sqlConnection *conn2 = hAllocConn(database);
 int thisStart, thisEnd;
 int left = cartIntExp( cart, "l" );
 int right = cartIntExp( cart, "r" );
 char *winOn = cartUsualString( cart, "win", "F" );
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chrom = '%s'",
 	table, item, seqName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     smp = sampleLoad(row+hasBin);
     safef(tempTableName, sizeof tempTableName, "%s_%s", smp->chrom, pslTableName );
     hFindSplitTable(database, seqName, pslTableName, table, &hasBin);
     sqlSafef(query, sizeof query, "select * from %s where tName = '%s' and tEnd >= %d and tStart <= %d"
 	    , table, smp->chrom, smp->chromStart+smp->samplePosition[0]
 	    , smp->chromStart+smp->samplePosition[smp->sampleCount-1] );
 
     pslSr = sqlGetResult(conn2, query);
     if(!sameString(winOn,"T"))
 	{
 	while(( pslRow = sqlNextRow(pslSr)) != NULL )
 	    {
 	    thisPsl = pslLoad( pslRow+hasBin );
 	    firstAndLastPosition( &thisStart, &thisEnd, thisPsl );
 	    snprintf(thisItem, 256, "%s:%d-%d %s:%d-%d", thisPsl->qName,
 		     thisPsl->qStart, thisPsl->qEnd, thisPsl->tName,
 		     thisPsl->tStart, thisPsl->tEnd );
 	    longXenoPsl1Given(tdb, thisItem, otherOrg, "chromInfo",
 			      otherDb, thisPsl, pslTableName );
 	    safef(otherString, sizeof otherString, "%d&win=T", thisPsl->tStart );
 	    hgcAnchorSomewhere( tdb->track, item, otherString, thisPsl->tName );
 	    printf("View individual alignment windows\n</a>");
 	    printf("<br><br>");
 	    }
 	}
     else
 	{
 	cartSetString( cart, "win", "F" );
 	printf("<h3>Alignments Windows </h3>\n"
 	       "<b>start&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stop"
 	       "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;L-score</b><br>" );
 	while(( pslRow = sqlNextRow(pslSr)) != NULL )
 	    {
 	    thisPsl = pslLoad( pslRow+hasBin );
 	    firstAndLastPosition( &thisStart, &thisEnd, thisPsl );
 	    for( i=0; i<smp->sampleCount; i++ )
 		{
 		if( !sampleClickRelevant( smp, i, left, right, humMusWinSize,
 					  thisStart, thisEnd ) )
 		    continue;
                 snprintf( str, 256,
 			  "%d&nbsp;&nbsp;&nbsp;&nbsp;%d&nbsp;&nbsp;&nbsp;&nbsp;%g<br>",
 			  max( smp->chromStart + smp->samplePosition[i] -
 			       humMusWinSize / 2 + 1, thisStart + 1),
 			  min(smp->chromStart +  smp->samplePosition[i] +
 			      humMusWinSize / 2, thisEnd ),
 			  whichNum(smp->sampleHeight[i],0.0,8.0,1000) );
                 /* 0 to 8.0 is the fixed total L-score range for
                  * all these conservation tracks. Scores outside
                  * this range are truncated. */
 		printSampleWindow( thisPsl,
 				   smp->chromStart + smp->samplePosition[i] -
 				   humMusWinSize / 2,
 				   smp->chromStart + smp->samplePosition[i] +
 				   humMusWinSize / 2,
 				   str, otherOrg, otherDb, pslTableName );
 		}
 	    printf("<br>");
 	    }
         }
     }
 }
 
 void footPrinterSampleClick(struct sqlConnection *conn, struct trackDb *tdb,
                             char *item, int start, int smpSize)
 /* Handle click in humMus sample (wiggle) track. */
 {
 char table[64];
 boolean hasBin;
 struct sample *smp;
 char query[512];
 char tempTableName[1024];
 struct sqlResult *sr;
 char **row;
 boolean firstTime = TRUE;
 char filename[10000];
 char pslTableName[128] = "blastzBestMouse";
 int offset;
 int motifid;
 
 hFindSplitTable(database, seqName, tdb->table, table, &hasBin);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s'",
 	table, item);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     smp = sampleLoad(row+hasBin);
 
     sscanf(smp->name,"footPrinter.%d.%d",&offset,&motifid);
     safef(filename, sizeof filename, "../zoo_blanchem/new_raw2_offset%d.fa.main.html?motifID=%d", offset, motifid);
 
     safef(tempTableName, sizeof tempTableName,"%s_%s", smp->chrom, pslTableName );
     hFindSplitTable(database, seqName, pslTableName, table, &hasBin);
     sqlSafef(query, sizeof query, "select * from %s where tName = '%s' and tEnd >= %d and tStart <= %d" ,
 	    table, smp->chrom, smp->chromStart+smp->samplePosition[0],
 	    smp->chromStart+smp->samplePosition[smp->sampleCount-1] );
 
     printf("Content-Type: text/html\n\n<HTML><BODY><SCRIPT>\n");
     printf("location.replace('%s')\n",filename);
     printf("</SCRIPT> <NOSCRIPT> No JavaScript support. "
            "Click <b><a href=\"%s\">continue</a></b> for "
            "the requested GenBank report. </NOSCRIPT>\n",
            filename);
     }
 }
 
 void humMusClickHandler(struct trackDb *tdb, char *item,
         char *targetName, char *targetDb, char *targetTable, boolean printWindowFlag )
 /* Put up sample track info. */
 {
 char *words[16], *dupe = cloneString(tdb->type);
 int num;
 int wordCount;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 
 genericHeader(tdb, item);
 wordCount = chopLine(dupe, words);
 if (wordCount > 0)
     {
     num = 0;
     if (wordCount > 1)
 	num = atoi(words[1]);
     if (num < 3) num = 3;
         humMusSampleClick( conn, tdb, item, start, num, targetName, targetDb, targetTable, printWindowFlag );
     }
 printTrackHtml(tdb);
 freez(&dupe);
 hFreeConn(&conn);
 }
 
 void footPrinterClickHandler(struct trackDb *tdb, char *item )
 /* Put up generic track info. */
 {
 char *words[16], *dupe = cloneString(tdb->type);
 int num;
 int wordCount;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 
 wordCount = chopLine(dupe, words);
 if (wordCount > 0)
     {
     num = 0;
     if (wordCount > 1)
 	num = atoi(words[1]);
     if (num < 3) num = 3;
     footPrinterSampleClick(conn, tdb, item, start, num);
     }
 printTrackHtml(tdb);
 freez(&dupe);
 hFreeConn(&conn);
 }
 
 void hgCustom(char *trackId, char *fileItem)
 /* Process click on custom track. */
 {
 char *fileName, *itemName;
 struct customTrack *ctList = getCtList();
 struct customTrack *ct;
 struct bed *bed = (struct bed *)NULL;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 char *item = cartString(cart, "i");
 char *type;
 fileName = nextWord(&fileItem);
 for (ct = ctList; ct != NULL; ct = ct->next)
     if (sameString(trackId, ct->tdb->track))
 	break;
 if (ct == NULL)
     errAbort("Couldn't find '%s' in '%s'", trackId, fileName);
 type = ct->tdb->type;
 cartWebStart(cart, database, "Custom Track: %s", ct->tdb->shortLabel);
 itemName = skipLeadingSpaces(fileItem);
 printf("<H2>%s</H2>\n", ct->tdb->longLabel);
 if (sameWord(type, "array"))
     doExpRatio(ct->tdb, fileItem, ct);
 else if ( startsWith( "longTabix", type))
     doLongTabix(ct->tdb, item);
 else if (sameWord(type, "encodePeak"))
     doEncodePeak(ct->tdb, ct, fileName);
 else if (sameWord(type, "bigWig"))
     bigWigCustomClick(ct->tdb);
 else if (sameWord(type, "bigChain"))
     genericChainClick(NULL, ct->tdb, item, start, "seq");
 else if (sameWord(type, "bigPsl"))
     genericBigPslClick(NULL, ct->tdb, item, start, end);
 else if (sameWord(type, "bigMaf"))
     genericMafClick(NULL, ct->tdb, item, start);
 else if (sameWord(type, "bigBed") || sameWord(type, "bigGenePred"))
     bigBedCustomClick(ct->tdb);
 else if (sameWord(type, "bam"))
     doBamDetails(ct->tdb, itemName);
 else if (sameWord(type, "vcfTabix"))
     doVcfTabixDetails(ct->tdb, itemName);
 else if (sameWord(type, "vcf"))
     doVcfDetails(ct->tdb, itemName);
 else if (sameWord(type, "makeItems"))
     doMakeItemsDetails(ct, fileName);	// fileName is first word, which is, go figure, id
 else if (ct->wiggle)
     {
     if (ct->dbTrack)
 	{
 	struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
 	genericWiggleClick(conn, ct->tdb, fileItem, start);
 	hFreeConn(&conn);
 	}
     else
 	genericWiggleClick(NULL, ct->tdb, fileItem, start);
     /*	the NULL is for conn, don't need that for custom tracks */
     }
 else if (ct->dbTrack && startsWith("bedGraph", ct->dbTrackType))
     {
     printf("<P><A HREF=\"../cgi-bin/hgTables?db=%s&hgta_group=%s&hgta_track=%s"
            "&hgta_table=%s&position=%s:%d-%d&"
            "hgta_doSchema=describe+table+schema\" TARGET=_BLANK>"
            "View table schema</A></P>\n",
            database, ct->tdb->grp, ct->tdb->table, ct->tdb->table,
            seqName, winStart+1, winEnd);
     }
 else if (ct->dbTrack && sameString(ct->dbTrackType, "maf"))
     {
     struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
     struct sqlConnection *conn2 = hAllocConn(CUSTOM_TRASH);
     char *saveTable = ct->tdb->table;
     char *saveTrack = ct->tdb->track;
     ct->tdb->table = ct->tdb->track = ct->dbTableName;
     customMafClick(conn, conn2, ct->tdb);
     ct->tdb->table = saveTable;
     ct->tdb->track = saveTrack;
     hFreeConn(&conn2);
     hFreeConn(&conn);
     }
 else if (ct->dbTrack && sameWord(type, "bedDetail"))
     {
     doBedDetail(ct->tdb, ct, itemName);
     }
 else if (ct->dbTrack && sameWord(type, "pgSnp"))
     {
     doPgSnp(ct->tdb, itemName, ct);
     }
 else
     {
     if (ct->dbTrack)
 	{
 	char where[512];
 	int rowOffset;
 	char **row;
 	struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
 	struct sqlResult *sr = NULL;
 	int start = cartInt(cart, "o");
 	int end = cartInt(cart, "t");
 
 	if (ct->fieldCount < 4)
 	    sqlSafefFrag(where, sizeof(where), "chromStart = '%d'", start);
 	else
 	    {
 	    sqlSafefFrag(where, sizeof(where), "name = '%s'", itemName);
 	    }
 	sr = hRangeQuery(conn, ct->dbTableName, seqName, start, end,
                      where, &rowOffset);
 	while ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    bedFree(&bed);
 	    bed = bedLoadN(row+rowOffset, ct->fieldCount);
 	    }
 	sqlFreeResult(&sr);
 	hFreeConn(&conn);
 	}
     if (ct->fieldCount < 4)
 	{
 	if (! ct->dbTrack)
 	    {
 	    for (bed = ct->bedList; bed != NULL; bed = bed->next)
 		if (bed->chromStart == start && sameString(seqName, bed->chrom))
 		    break;
 	    }
 	if (bed)
 	    printPos(bed->chrom, bed->chromStart, bed->chromEnd, NULL,
 		TRUE, NULL);
 	if (ct->dbTrack)
 	    printUpdateTime(CUSTOM_TRASH, ct->tdb, ct);
 	printTrackHtml(ct->tdb);
 	return;
 	}
     else
 	{
 	if (! ct->dbTrack)
 	    {
 	    for (bed = ct->bedList; bed != NULL; bed = bed->next)
 		if (bed->chromStart == start && sameString(seqName, bed->chrom))
 		    if (bed->name == NULL || sameString(itemName, bed->name) )
 			break;
 	    }
 	}
     if (bed == NULL)
 	errAbort("Couldn't find %s@%s:%d in %s", itemName, seqName,
 		start, fileName);
     printCustomUrl(ct->tdb, itemName, TRUE);
     bedPrintPos(bed, ct->fieldCount, NULL);
     }
 printTrackUiLink(ct->tdb);
 printUpdateTime(CUSTOM_TRASH, ct->tdb, ct);
 printTrackHtml(ct->tdb);
 }
 
 void blastProtein(struct trackDb *tdb, char *itemName)
 /* Show protein to translated dna alignment for accession. */
 {
 char startBuf[64], endBuf[64];
 int start = cartInt(cart, "o");
 boolean isClicked;
 struct psl *psl = 0;
 struct sqlResult *sr = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 char query[256], **row;
 struct psl* pslList = getAlignments(conn, tdb->table, itemName);
 char *useName = itemName;
 char *acc = NULL, *prot = NULL;
 char *gene = NULL, *pos = NULL;
 char *ptr;
 char *buffer;
 char *spAcc;
 boolean isCe = FALSE;
 boolean isDm = FALSE;
 boolean isSacCer = FALSE;
 char *pred = trackDbSettingOrDefault(tdb, "pred", "NULL");
 char *blastRef = trackDbSettingOrDefault(tdb, "blastRef", "NULL");
 
 if (sameString("blastSacCer1SG", tdb->table))
     isSacCer = TRUE;
 if (startsWith("blastDm", tdb->table))
     isDm = TRUE;
 if (startsWith("blastCe", tdb->table))
     isCe = TRUE;
 buffer = needMem(strlen(itemName)+ 1);
 strcpy(buffer, itemName);
 acc = buffer;
 if (blastRef != NULL)
     {
     char *thisDb = cloneString(blastRef);
     char *table;
 
     if ((table = strchr(thisDb, '.')) != NULL)
 	{
 	*table++ = 0;
 	if (hTableExists(thisDb, table))
 	    {
 	    if (!isCe && (ptr = strchr(acc, '.')))
 		*ptr = 0;
 	    sqlSafef(query, sizeof(query), "select geneId, extra1, refPos from %s where acc = '%s'", blastRef, acc);
 	    sr = sqlGetResult(conn, query);
 	    if ((row = sqlNextRow(sr)) != NULL)
 		{
 		useName = row[0];
 		prot = row[1];
 		pos = row[2];
 		}
 	    }
         }
     }
 else if ((pos = strchr(acc, '.')) != NULL)
     {
     *pos++ = 0;
     if ((gene = strchr(pos, '.')) != NULL)
 	{
 	*gene++ = 0;
 	useName = gene;
 	if (!isDm && !isCe && ((prot = strchr(gene, '.')) != NULL))
 	    *prot++ = 0;
 	}
     }
 if (isDm == TRUE)
     cartWebStart(cart, database, "FlyBase Protein %s", useName);
 else if (isSacCer == TRUE)
     cartWebStart(cart, database, "Yeast Protein %s", useName);
 else if (isCe == TRUE)
     cartWebStart(cart, database, "C. elegans Protein %s", useName);
 else
     cartWebStart(cart, database, "Human Protein %s", useName);
 if (pos != NULL)
     {
     if (isDm == TRUE)
 	{
 	char *dmDb = cloneString(strchr(tdb->track, 'D'));
 
 	*dmDb = tolower(*dmDb);
 	*strchr(dmDb, 'F') = 0;
 
 	printf("<B>D. melanogaster position:</B>\n");
 	printf("<A TARGET=_blank HREF=\"%s?position=%s&db=%s\">",
 	    hgTracksName(), pos, dmDb);
 	}
     else if (isCe == TRUE)
 	{
 	char *assembly;
 	if (sameString("blastWBRef01", tdb->table))
 	    assembly = "ce3";
 	else if (sameString("blastCe9SG", tdb->table))
 	    assembly = "ce9";
 	else if (sameString("blastCe6SG", tdb->table))
 	    assembly = "ce6";
 	else if (sameString("blastCe4SG", tdb->table))
 	    assembly = "ce4";
 	else
 	    assembly = "ce3";
 	printf("<B>C. elegans position:</B>\n");
 	printf("<A TARGET=_blank HREF=\"%s?position=%s&db=%s\">",
 	    hgTracksName(), pos, assembly);
 	}
     else if (isSacCer == TRUE)
 	{
 	char *assembly = "sacCer1";
 	printf("<B>Yeast position:</B>\n");
 	printf("<A TARGET=_blank HREF=\"%s?position=%s&db=%s\">",
 	    hgTracksName(), pos, assembly);
 	}
     else
 	{
 	char *assembly;
 	if (sameString("blastHg16KG", tdb->table))
 	    assembly = "hg16";
 	else if (sameString("blastHg17KG", tdb->table))
 	    assembly = "hg17";
 	else
 	    assembly = "hg18";
 	printf("<B>Human position:</B>\n");
 	printf("<A TARGET=_blank HREF=\"%s?position=%s&db=%s\">",
 	    hgTracksName(), pos, assembly);
 	}
     printf("%s</A><BR>",pos);
     }
 if (acc != NULL)
     {
     if (isDm== TRUE)
 	printf("<B>FlyBase Entry:</B> <A HREF=\" %s%s", tdb->url, acc);
     else if (isSacCer== TRUE)
 	printf("<B>SGD Entry:</B> <A HREF=\" %s%s", tdb->url, acc);
     else if (isCe == TRUE)
 	printf("<B>Wormbase ORF Name:</B> <A HREF=\" %s%s", tdb->url, acc);
     else
 	{
 	printf("<B>Human mRNA:</B> <A HREF=\"");
 	printEntrezNucleotideUrl(stdout, acc);
 	}
     printf("\" TARGET=_blank>%s</A><BR>\n", acc);
     }
 if (!isDm && (prot != NULL) && !sameString("(null)", prot) && sqlTableExists(conn,"proteome.uniProtAlias"))
     {
     printf("<B>UniProtKB:</B> ");
     printf("<A HREF=");
     printSwissProtProteinUrl(stdout, prot);
 
     spAcc = uniProtFindPrimAcc(prot);
     if (spAcc == NULL)
         {
 	printf(" TARGET=_blank>%s</A></B><BR>\n", prot);
         }
     else
         {
 	printf(" TARGET=_blank>%s</A></B><BR>\n", spAcc);
         }
     }
 printf("<B>Protein length:</B> %d<BR>\n",pslList->qSize);
 
 slSort(&pslList, pslCmpMatch);
 if (slCount(pslList) > 1)
     printf("<P>The alignment you clicked on is first in the table below.<BR>\n");
 printf("<TT><PRE>");
 printf("ALIGNMENT PEPTIDE COVERAGE IDENTITY  START END EXTENT  STRAND   LINK TO BROWSER \n");
 printf("--------------------------------------------------------------------------------\n");
 for (isClicked = 1; isClicked >= 0; isClicked -= 1)
     {
     for (psl = pslList; psl != NULL; psl = psl->next)
 	{
 	if (isPslToPrintByClick(psl, start, isClicked))
 	    {
 	    printf("<A HREF=\"%s&o=%d&g=htcProteinAli&i=%s&c=%s&l=%d&r=%d&db=%s&aliTable=%s&pred=%s\">",
 		hgcPathAndSettings(), psl->tStart, psl->qName,  psl->tName,
 		psl->tStart, psl->tEnd, database,tdb->track, pred);
 	    printf("alignment</A> ");
 	    printf("<A HREF=\"%s&o=%d&g=htcGetBlastPep&i=%s&c=%s&l=%d&r=%d&db=%s&aliTable=%s\">",
 	           hgcPathAndSettings(), psl->tStart, psl->qName,  psl->tName,
                    psl->tStart, psl->tEnd, database,tdb->track);
             printf("peptide</A> ");
             printf("%5.1f%%    %5.1f%% %5d %5d %5.1f%%    %c   ",
                    100.0 * (psl->match + psl->repMatch + psl->misMatch) / psl->qSize,
                    100.0 * (psl->match + psl->repMatch) / (psl->match + psl->repMatch + psl->misMatch),
                    psl->qStart+1, psl->qEnd,
                    100.0 * (psl->qEnd - psl->qStart) / psl->qSize, psl->strand[1]);
             printf("<A HREF=\"%s&position=%s:%d-%d&db=%s&ss=%s+%s\">",
                    hgTracksPathAndSettings(),
                    psl->tName, psl->tStart + 1, psl->tEnd, database,
 		   tdb->track, itemName);
 	    sprintLongWithCommas(startBuf, psl->tStart + 1);
 	    sprintLongWithCommas(endBuf, psl->tEnd);
 	    printf("%s:%s-%s</A> <BR>",psl->tName,startBuf, endBuf);
 	    if (isClicked)
 		printf("\n");
 	    }
 	}
     }
     printf("</PRE></TT>");
     /* Add description */
     printTrackHtml(tdb);
     sqlFreeResult(&sr);
     hFreeConn(&conn);
 }
 
 static void doSgdOther(struct trackDb *tdb, char *item)
 /* Display information about other Sacchromyces Genome Database
  * other (not-coding gene) info. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct dyString *dy = dyStringNew(1024);
 if (sqlTableExists(conn, "sgdOtherDescription"))
     {
     /* Print out description and type if available. */
     struct sgdDescription sgd;
     struct sqlResult *sr;
     char query[256], **row;
     sqlSafef(query, sizeof(query),
           "select * from sgdOtherDescription where name = '%s'", item);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
 	sgdDescriptionStaticLoad(row, &sgd);
 	dyStringPrintf(dy, "<B>Description:</B> %s<BR>\n", sgd.description);
 	dyStringPrintf(dy, "<B>Type:</B> %s<BR>\n", sgd.type);
 	}
     sqlFreeResult(&sr);
     }
 hFreeConn(&conn);
 genericClickHandlerPlus(tdb, item, NULL, dy->string);
 dyStringFree(&dy);
 }
 
 
 void doPeptideAtlas(struct trackDb *tdb, char *item)
 /* PeptideAtlas item details display. Peptide details are in hgFixed.<table>Peptides */
 {
 char query[512];
 struct sqlResult *sr;
 char **row;
 
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 
 genericHeader(tdb, item);
 printCustomUrl(tdb, item, FALSE);
 struct sqlConnection *conn = hAllocConn(database);
 char peptideTable[128];
 safef(peptideTable, sizeof(peptideTable), "%sPeptides", tdb->table);
 
 // peptide info
 struct sqlConnection *connFixed= hAllocConn("hgFixed");
 if (sqlTableExists(connFixed, peptideTable))
     {
     sqlSafef(query, sizeof(query), "select * from %s where accession = '%s'", peptideTable, item);
     sr = sqlGetResult(connFixed, query);
     row = sqlNextRow(sr);
     if (row != NULL)
         {
         struct peptideAtlasPeptide *peptideInfo;
         AllocVar(peptideInfo);
         peptideAtlasPeptideStaticLoad(row, peptideInfo);
         printf("<b>Peptide sequence:</b> %s<br>\n", peptideInfo->sequence);
         printf("<b>Peptide size:</b> %d<br>\n", (int)strlen(peptideInfo->sequence));
         printf("<b>Samples where observed:</b> %d<br>\n", peptideInfo->sampleCount);
         printf("<b>Proteotypic score:</b> %.3f<br>\n", peptideInfo->proteotypicScore);
         printf("<b>Hydrophobicity:</b> %.3f<br><br>\n", peptideInfo->hydrophobicity);
         }
     sqlFreeResult(&sr);
     }
 hFreeConn(&connFixed);
 
 // peptide mappings
 sqlSafef(query, sizeof(query), "select * from %s where name = '%s' order by chrom, chromStart, chromEnd", 
         tdb->table, item);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 struct bed *bed = NULL, *beds = NULL;
 int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 while (row != NULL)
     {
     bed = bedLoadN(row + rowOffset, 12);
     if (sameString(bed->chrom, seqName) && bed->chromStart == start && bed->chromEnd == end)
         bedPrintPos(bed, 12, tdb);
     else
         slAddHead(&beds, bed);
     row = sqlNextRow(sr);
     }
 if (beds != NULL)
     {
     slSort(&beds, bedCmp);
     printf("<br><b>Other mappings of this peptide:</b> %d<br>\n", slCount(beds));
     for (bed = beds; bed != NULL; bed = bed->next)
         {
         printf("&nbsp;&nbsp;&nbsp;&nbsp;"
                "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
                hgTracksPathAndSettings(), database, bed->chrom, bed->chromStart+1, bed->chromEnd);
         printf("%s:%d-%d</A><BR>\n", bed->chrom, bed->chromStart+1, bed->chromEnd);
         }
     }
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 static void doSgdClone(struct trackDb *tdb, char *item)
 /* Display information about other Sacchromyces Genome Database
  * other (not-coding gene) info. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct dyString *dy = dyStringNew(1024);
 
 if (sqlTableExists(conn, "sgdClone"))
     {
     /* print out url with ATCC number */
     struct sgdClone sgd;
     struct sqlResult *sr;
     char query[256], **row;
     sqlSafef(query, sizeof(query),
           "select * from sgdClone where name = '%s'", item);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
 	sgdCloneStaticLoad(row+1, &sgd);
 	dyStringPrintf(dy, "<B>ATCC catalog number:</B><A HREF=\"http://www.atcc.org/ATCCAdvancedCatalogSearch/ProductDetails/tabid/452/Default.aspx?ATCCNum=%s&Template=uniqueClones\" TARGET=_blank>%s</A><BR>\n", sgd.atccName, sgd.atccName);
 	}
     sqlFreeResult(&sr);
     }
 hFreeConn(&conn);
 genericClickHandlerPlus(tdb, item,  NULL, dy->string);
 dyStringFree(&dy);
 }
 
 static void doSimpleDiff(struct trackDb *tdb, char *otherOrg)
 /* Print out simpleDiff info. */
 {
 struct simpleNucDiff snd;
 struct sqlConnection *conn = hAllocConn(database);
 char fullTable[64];
 char query[256], **row;
 struct sqlResult *sr;
 int rowOffset;
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, NULL);
 if (!hFindSplitTable(database, seqName, tdb->table, fullTable, &rowOffset))
     errAbort("No %s table in database %s", tdb->table, database);
 sqlSafef(query, sizeof(query), "select * from %s where chrom = '%s' and chromStart=%d",
     fullTable, seqName, start);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     simpleNucDiffStaticLoad(row + rowOffset, &snd);
     printf("<B>%s sequence:</B> %s<BR>\n", hOrganism(database), snd.tSeq);
     printf("<B>%s sequence:</B> %s<BR>\n", otherOrg, snd.qSeq);
     bedPrintPos((struct bed*)&snd, 3, tdb);
     printf("<BR>\n");
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 static void doVntr(struct trackDb *tdb, char *item)
 /* Perfect microsatellite repeats from VNTR program (Gerome Breen). */
 {
 struct vntr vntr;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 char extra[256];
 int rowOffset = 0;
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, item);
 genericBedClick(conn, tdb, item, start, 4);
 safef(extra, sizeof(extra), "chromStart = %d", start);
 sr = hRangeQuery(conn, tdb->table, seqName, winStart, winEnd, extra,
 		 &rowOffset);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     vntrStaticLoad(row + rowOffset, &vntr);
     printf("<B>Number of perfect repeats:</B> %.02f<BR>\n", vntr.repeatCount);
     printf("<B>Distance to last microsatellite repeat:</B> ");
     if (vntr.distanceToLast == -1)
 	printf("n/a (first in chromosome)<BR>\n");
     else
 	printf("%d<BR>\n", vntr.distanceToLast);
     printf("<B>Distance to next microsatellite repeat:</B> ");
     if (vntr.distanceToNext == -1)
 	printf("n/a (last in chromosome)<BR>\n");
     else
 	printf("%d<BR>\n", vntr.distanceToNext);
     if (isNotEmpty(vntr.forwardPrimer) &&
 	! sameString("Design_Failed", vntr.forwardPrimer))
 	{
 	printf("<B>Forward PCR primer:</B> %s<BR>\n", vntr.forwardPrimer);
 	printf("<B>Reverse PCR primer:</B> %s<BR>\n", vntr.reversePrimer);
 	printf("<B>PCR product length:</B> %s<BR>\n", vntr.pcrLength);
 	}
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 static void doZdobnovSynt(struct trackDb *tdb, char *item)
 /* Gene homology-based synteny blocks from Zdobnov, Bork et al. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 char query[256];
 int start = cartInt(cart, "o");
 char fullTable[64];
 boolean hasBin = FALSE;
 
 genericHeader(tdb, item);
 genericBedClick(conn, tdb, item, start, 4);
 hFindSplitTable(database, seqName, tdb->table, fullTable, &hasBin);
 sqlSafef(query, sizeof(query), "select * from %s where name = '%s'",
       fullTable, item);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     struct zdobnovSynt *zd = zdobnovSyntLoad(row + hasBin);
     int l = cgiInt("l");
     int r = cgiInt("r");
     int i = 0;
     puts("<B>Homologous gene names in window:</B>");
     for (i=0;  i < zd->blockCount;  i++)
 	{
 	int bStart = zd->chromStarts[i] + zd->chromStart;
 	int bEnd = bStart + zd->blockSizes[i];
 	if (bStart <= r && bEnd >= l)
 	    {
 	    printf(" %s", zd->geneNames[i]);
 	    }
 	}
     puts("");
     zdobnovSyntFree(&zd);
     }
 else
     errAbort("query returned no results: \"%s\"", query);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 
 static void doDeweySynt(struct trackDb *tdb, char *item)
 /* Gene homology-based synteny blocks from Dewey, Pachter. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int start = cartInt(cart, "o");
 char fullTable[64];
 boolean hasBin = FALSE;
 struct bed *bed = NULL;
 char query[512];
 
 genericHeader(tdb, item);
 hFindSplitTable(database, seqName, tdb->table, fullTable, &hasBin);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and chromStart = %d",
       fullTable, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     char *words[4];
     int wordCount = 0;
     bed = bedLoad6(row+hasBin);
     bedPrintPos(bed, 4, tdb);
     printf("<B>Strand:</B> %s<BR>\n", bed->strand);
     wordCount = chopByChar(bed->name, '.', words, ArraySize(words));
     if (wordCount == 3 && hDbExists(words[1]))
 	{
 	char *otherOrg = hOrganism(words[1]);
 	printf("<A TARGET=\"_blank\" HREF=\"%s?db=%s&position=%s\">",
 	       hgTracksName(), words[1], cgiEncode(words[2]));
 	printf("Open %s browser</A> at %s.<BR>\n", otherOrg, words[2]);
 	}
     bedFree(&bed);
     }
 else
     errAbort("query returned no results: \"%s\"", query);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 
 void doScaffoldEcores(struct trackDb *tdb, char *item)
 /* Creates details page and gets the scaffold co-ordinates for unmapped */
 /* genomes for display and to use to create the correct outside link URL */
 {
 char *dupe, *words[16];
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 int num;
 struct bed *bed = NULL;
 char query[512];
 struct sqlResult *sr;
 char **row;
 char *scaffoldName;
 int scaffoldStart, scaffoldEnd;
 struct dyString *itemUrl = newDyString(128), *d;
 char *old = "_";
 char *new = "";
 char *pat = "fold";
 int hasBin = 1;
 dupe = cloneString(tdb->type);
 chopLine(dupe,words);
 /* get bed size */
 num = 0;
 num = atoi(words[1]);
 
 /* get data for this item */
 sqlSafef(query, sizeof query, "select * from %s where name = '%s' and chromStart = %d", tdb->table, item, start);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     bed = bedLoadN(row+hasBin, num);
 
 genericHeader(tdb, item);
 /* convert chromosome co-ordinates to scaffold position and */
 /* make into item for URL */
 if (hScaffoldPos(database, bed->chrom, bed->chromStart, bed->chromEnd, &scaffoldName,            &scaffoldStart, &scaffoldEnd) )
    {
     scaffoldStart += 1;
    dyStringPrintf(itemUrl, "%s:%d-%d", scaffoldName, scaffoldStart,                           scaffoldEnd);
    /* remove underscore in scaffold name and change to "scafN" */
    d = dyStringSub(itemUrl->string, old, new);
    itemUrl = dyStringSub(d->string, pat, new);
    printCustomUrl(tdb, itemUrl->string, TRUE);
    }
 
 genericBedClick(conn, tdb, item, start, num);
 printTrackHtml(tdb);
 
 dyStringFree(&itemUrl);
 freez(&dupe);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 char *stripBDGPSuffix(char *name)
 /* cloneString(name), and if it ends in -R[A-Z], strip that off. */
 {
 char *stripped = cloneString(name);
 int len = strlen(stripped);
 if (stripped[len-3] == '-' &&
     stripped[len-2] == 'R' &&
     isalpha(stripped[len-1]))
     stripped[len-3] = 0;
 return(stripped);
 }
 
 static void doGencodeIntron(struct trackDb *tdb, char *item)
 /* Intron validation from ENCODE Gencode/Havana gene predictions */
 {
 struct sqlConnection *conn = hAllocConn(database);
 int start = cartInt(cart, "o");
 struct gencodeIntron *intron, *intronList = NULL;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
 
 genericHeader(tdb, item);
 sqlSafef(query, sizeof query,
         "select * from %s where name='%s' and chrom='%s' and chromStart=%d",
                 tdb->table, item, seqName, start);
 intronList = gencodeIntronLoadByQuery(conn, query, rowOffset);
 for (intron = intronList; intron != NULL; intron = intron->next)
     {
     printf("<B>Intron:</B> %s<BR>\n", intron->name);
     printf("<B>Status:</B> %s<BR>\n", intron->status);
     printf("<B>Gene:</B> %s<BR>\n", intron->geneId);
     printf("<B>Transcript:</B> %s<BR>\n", intron->transcript);
     printPos(intron->chrom, intron->chromStart,
             intron->chromEnd, intron->strand, TRUE, intron->name);
     }
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 
 static void printESPDetails(char **row, struct trackDb *tdb)
 /* Print details from a cell line subtrack table of encodeStanfordPromoters. */
 {
 struct encodeStanfordPromoters *esp = encodeStanfordPromotersLoad(row);
 bedPrintPos((struct bed *)esp, 6, tdb);
 printf("<B>Gene model ID:</B> %s<BR>\n", esp->geneModel);
 printf("<B>Gene description:</B> %s<BR>\n", esp->description);
 printf("<B>Luciferase signal A:</B> %d<BR>\n", esp->lucA);
 printf("<B>Renilla signal A:</B> %d<BR>\n", esp->renA);
 printf("<B>Luciferase signal B:</B> %d<BR>\n", esp->lucB);
 printf("<B>Renilla signal B:</B> %d<BR>\n", esp->renB);
 printf("<B>Average Luciferase/Renilla Ratio:</B> %g<BR>\n", esp->avgRatio);
 printf("<B>Normalized Luciferase/Renilla Ratio:</B> %g<BR>\n", esp->normRatio);
 printf("<B>Normalized and log2 transformed Luciferase/Renilla Ratio:</B> %g<BR>\n",
        esp->normLog2Ratio);
 }
 
 static void printESPAverageDetails(char **row, struct trackDb *tdb)
 /* Print details from the averaged subtrack table of encodeStanfordPromoters. */
 {
 struct encodeStanfordPromotersAverage *esp =
     encodeStanfordPromotersAverageLoad(row);
 bedPrintPos((struct bed *)esp, 6, tdb);
 printf("<B>Gene model ID:</B> %s<BR>\n", esp->geneModel);
 printf("<B>Gene description:</B> %s<BR>\n", esp->description);
 printf("<B>Normalized and log2 transformed Luciferase/Renilla Ratio:</B> %g<BR>\n",
        esp->normLog2Ratio);
 }
 
 void doEncodeStanfordPromoters(struct trackDb *tdb, char *item)
 /* Print ENCODE Stanford Promoters data. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row = NULL;
 int start = cartInt(cart, "o");
 char fullTable[64];
 boolean hasBin = FALSE;
 char query[1024];
 
 cartWebStart(cart, database, "%s", tdb->longLabel);
 genericHeader(tdb, item);
 hFindSplitTable(database, seqName, tdb->table, fullTable, &hasBin);
 sqlSafef(query, sizeof(query),
      "select * from %s where chrom = '%s' and chromStart = %d and name = '%s'",
       fullTable, seqName, start, item);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     if (endsWith(tdb->table, "Average"))
 	printESPAverageDetails(row+hasBin, tdb);
     else
 	printESPDetails(row+hasBin, tdb);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 void doEncodeStanfordRtPcr(struct trackDb *tdb, char *item)
 /* Print ENCODE Stanford RTPCR data. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row = NULL;
 int start = cartInt(cart, "o");
 char fullTable[64];
 boolean hasBin = FALSE;
 char query[1024];
 
 cartWebStart(cart, database, "%s", tdb->longLabel);
 genericHeader(tdb, item);
 hFindSplitTable(database, seqName, tdb->table, fullTable, &hasBin);
 sqlSafef(query, sizeof(query),
      "select * from %s where chrom = '%s' and chromStart = %d and name = '%s'",
       fullTable, seqName, start, item);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     struct bed *bed = bedLoadN(row+hasBin, 5);
     bedPrintPos(bed, 5, tdb);
     printf("<B>Primer pair ID:</B> %s<BR>\n", row[hasBin+5]);
     printf("<B>Count:</B> %s<BR>\n", row[hasBin+6]);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 void doEncodeHapMapAlleleFreq(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct encodeHapMapAlleleFreq alleleFreq;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     encodeHapMapAlleleFreqStaticLoad(row+rowOffset, &alleleFreq);
     printf("<B>Variant:</B> %s<BR>\n", alleleFreq.otherAllele);
     printf("<B>Reference:</B> %s<BR>\n", alleleFreq.refAllele);
     bedPrintPos((struct bed *)&alleleFreq, 3, tdb);
     printf("<B>Reference Allele Frequency:</B> %f <BR>\n", alleleFreq.refAlleleFreq);
     printf("<B>Other Allele Frequency:</B> %f <BR>\n", alleleFreq.otherAlleleFreq);
     printf("<B>Center:</B> %s <BR>\n", alleleFreq.center);
     printf("<B>Total count:</B> %d <BR>\n", alleleFreq.totalCount);
     printf("-----------------------------------------------------<BR>\n");
     }
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 
 void showHapmapMonomorphic(struct hapmapAllelesSummary *summaryItem)
 {
 if (summaryItem->totalAlleleCountCEU > 0)
     {
     printf("<TR>");
     printf("<TD>CEU</TD>");
     printf("<TD bgcolor = \"lightgrey\">%d (100%%)</TD>", summaryItem->totalAlleleCountCEU);
     printf("</TR>\n");
     }
 else
     printf("<TR><TD>CEU</TD><TD>not available</TD></TR>\n");
 
 if (summaryItem->totalAlleleCountCHB > 0)
     {
     printf("<TR>");
     printf("<TD>CHB</TD>");
     printf("<TD bgcolor = \"lightgrey\">%d (100%%)</TD>", summaryItem->totalAlleleCountCHB);
     printf("</TR>\n");
     }
 else
     printf("<TR><TD>CHB</TD><TD>not available</TD></TR>\n");
 
 if (summaryItem->totalAlleleCountJPT > 0)
     {
     printf("<TR>");
     printf("<TD>JPT</TD>");
     printf("<TD bgcolor = \"lightgrey\">%d (100%%)</TD>", summaryItem->totalAlleleCountJPT);
     printf("</TR>\n");
     }
 else
     printf("<TR><TD>JPT</TD><TD>not available</TD></TR>\n");
 
 if (summaryItem->totalAlleleCountYRI > 0)
     {
     printf("<TR>");
     printf("<TD>YRI</TD>");
     printf("<TD bgcolor = \"lightgrey\">%d (100%%)</TD>", summaryItem->totalAlleleCountYRI);
     printf("</TR>\n");
     }
 else
     printf("<TR><TD>YRI</TD><TD>not available</TD></TR>\n");
 }
 
 void showOneHapmapRow(char *pop, char *allele1, char *allele2, char *majorAllele,
                       int majorCount, int totalCount)
 {
 int count1 = 0;
 int count2 = 0;
 float freq1 = 0.0;
 float freq2 = 0.0;
 
 if (majorCount == 0)
     {
     printf("<TR><TD>%s</TD><TD align=center>-</TD><TD align=center>-</TD></TR>\n", pop);
     return;
     }
 
 if (sameString(allele1, majorAllele))
     {
     count1 = majorCount;
     count2 = totalCount - majorCount;
     }
 else
     {
     count2 = majorCount;
     count1 = totalCount - majorCount;
     }
 
 freq1 = 100.0 * count1 / totalCount;
 freq2 = 100.0 * count2 / totalCount;
 
 printf("<TR>");
 printf("<TD>%s</TD>", pop);
 if (count1 > count2)
     {
     printf("<TD bgcolor = \"lightgrey\" align=right>%d (%3.2f%%)</TD>", count1, freq1);
     printf("<TD align=right>%d (%3.2f%%)</TD>", count2, freq2);
     }
 else if (count1 < count2)
     {
     printf("<TD align=right>%d (%3.2f%%)</TD>", count1, freq1);
     printf("<TD bgcolor = \"lightgrey\" align=right>%d (%3.2f%%)</TD>", count2, freq2);
     }
 else
     {
     printf("<TD align=right>%d (%3.2f%%)</TD>", count1, freq1);
     printf("<TD align=right>%d (%3.2f%%)</TD>", count2, freq2);
     }
 printf("</TR>\n");
 
 }
 
 void showHapmapAverageRow(char *label, float freq1)
 {
 float freq2 = 1.0 - freq1;
 printf("<TR><TD>%s</TD>", label);
 if (freq1 > 0.5)
     {
     printf("<TD bgcolor = \"lightgrey\" align=right>(%3.2f%%)</TD>", freq1*100);
     printf("<TD align=right>(%3.2f%%)</TD>", freq2*100);
     }
 else if (freq1 < 0.5)
     {
     printf("<TD align=right>(%3.2f%%)</TD>", freq1*100);
     printf("<TD bgcolor = \"lightgrey\" align=right>(%3.2f%%)</TD>", freq2*100);
     }
 else
     {
     printf("<TD align=right>(%3.2f%%)</TD>", freq1*100);
     printf("<TD align=right>(%3.2f%%)</TD>", freq2*100);
     }
 printf("</TR>\n");
 }
 
 void doHapmapSnpsSummaryTable(struct sqlConnection *conn, struct trackDb *tdb, char *itemName,
 			      boolean showOrtho)
 /* Use the hapmapAllelesSummary table (caller checks for existence) to display allele
  * frequencies for the 4 HapMap Phase II populations. */
 {
 char *table = tdb->table;
 struct hapmapAllelesSummary *summaryItem;
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 float het = 0.0;
 
 sqlSafef(query, sizeof(query), "select * from hapmapAllelesSummary where chrom = '%s' and "
       "chromStart=%d and name = '%s'", seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 summaryItem = hapmapAllelesSummaryLoad(row+rowOffset);
 
 printf("<BR><B>Allele frequencies in each population (major allele highlighted):</B><BR>\n");
 printf("<TABLE BORDER=1>\n");
 if (differentString(summaryItem->allele2, "none"))
     {
     printf("<TR><TH>Population</TH> <TH>%s</TH> <TH>%s</TH></TR>\n", summaryItem->allele1, summaryItem->allele2);
     showOneHapmapRow("CEU", summaryItem->allele1, summaryItem->allele2, summaryItem->majorAlleleCEU,
                             summaryItem->majorAlleleCountCEU, summaryItem->totalAlleleCountCEU);
     showOneHapmapRow("CHB", summaryItem->allele1, summaryItem->allele2, summaryItem->majorAlleleCHB,
                             summaryItem->majorAlleleCountCHB, summaryItem->totalAlleleCountCHB);
     showOneHapmapRow("JPT", summaryItem->allele1, summaryItem->allele2, summaryItem->majorAlleleJPT,
                             summaryItem->majorAlleleCountJPT, summaryItem->totalAlleleCountJPT);
     showOneHapmapRow("YRI", summaryItem->allele1, summaryItem->allele2, summaryItem->majorAlleleYRI,
                             summaryItem->majorAlleleCountYRI, summaryItem->totalAlleleCountYRI);
     }
 else
     {
     printf("<TR><TH>Population</TH> <TH>%s</TH></TR>\n", summaryItem->allele1);
     showHapmapMonomorphic(summaryItem);
     }
 printf("</TABLE>\n");
 
 het = summaryItem->score / 10.0;
 printf("<BR><B>Expected Heterozygosity (from total allele frequencies):</B> %3.2f%%<BR>\n", het);
 
 
 if (showOrtho && (differentString(summaryItem->chimpAllele, "none") ||
 		  differentString(summaryItem->macaqueAllele, "none")))
     {
     printf("<BR><B>Orthologous alleles:</B><BR>\n");
     printf("<TABLE BORDER=1>\n");
     printf("<TR><TH>Species</TH> <TH>Allele</TH> <TH>Quality Score</TH></TR>\n");
     if (differentString(summaryItem->chimpAllele, "none"))
         {
         printf("<TR>");
         printf("<TD>Chimp</TD>");
         printf("<TD>%s</TD>", summaryItem->chimpAllele);
         printf("<TD>%d</TD>", summaryItem->chimpAlleleQuality);
         printf("</TR>");
 	}
     if (differentString(summaryItem->macaqueAllele, "none"))
         {
         printf("<TR>");
         printf("<TD>Macaque</TD>");
         printf("<TD>%s</TD>", summaryItem->macaqueAllele);
         printf("<TD>%d</TD>", summaryItem->macaqueAlleleQuality);
         printf("</TR>");
 	}
     printf("</TABLE>\n");
     }
 
 sqlFreeResult(&sr);
 }
 
 void doHapmapSnpsAllPops(struct sqlConnection *conn, struct trackDb *tdb, char *itemName,
 			 boolean showOrtho)
 /* Show item's SNP allele frequencies for each of the 11 HapMap Phase III
  * populations, as well as chimp and macaque if showOrtho. */
 {
 int i;
 printf("<BR><B>Allele frequencies in each population (major allele highlighted):</B><BR>\n");
 printf("<TABLE BORDER=1>\n");
 // Do a first pass to gather up alleles and counts:
 char *majorAlleles[HAP_PHASEIII_POPCOUNT];
 int majorCounts[HAP_PHASEIII_POPCOUNT], haploCounts[HAP_PHASEIII_POPCOUNT];
 int totalA1Count = 0, totalA2Count = 0, totalHaploCount = 0;
 float sumHet = 0.0;
 int sumA1A1 = 0, sumA1A2 = 0, sumA2A2 = 0;
 int popCount = 0;
 char *allele1 = NULL, *allele2 = NULL;
 for (i=0;  i < HAP_PHASEIII_POPCOUNT;  i++)
     {
     char *popCode = hapmapPhaseIIIPops[i];
     struct hapmapSnps *item = NULL;
     char table[HDB_MAX_TABLE_STRING];
     safef(table, sizeof(table), "hapmapSnps%s", popCode);
     if (sqlTableExists(conn, table))
 	{
 	char query[512];
 	sqlSafef(query, sizeof(query), "select * from %s where name = '%s' and chrom = '%s'",
 	      table, itemName, seqName);
 	struct sqlResult *sr = sqlGetResult(conn, query);
 	char **row;
 	if ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    int rowOffset = hOffsetPastBin(database, seqName, table);
 	    item = hapmapSnpsLoad(row+rowOffset);
 	    }
 	sqlFreeResult(&sr);
 	}
     majorAlleles[i] = "";
     majorCounts[i] = 0;
     haploCounts[i] = 0;
     if (item != NULL)
 	{
 	majorAlleles[i] = item->allele1;
         majorCounts[i] = 2*item->homoCount1 + item->heteroCount;
 	if (item->homoCount1 < item->homoCount2)
 	    {
 	    majorAlleles[i] = item->allele2;
 	    majorCounts[i] = 2*item->homoCount2 + item->heteroCount;
 	    }
 	haploCounts[i] = 2*(item->homoCount1 + item->homoCount2 + item->heteroCount);
 	if (allele1 == NULL)
 	    {
 	    allele1 = item->allele1;
 	    allele2 = item->allele2;
 	    }
 	else if (!sameString(allele1, item->allele1) ||
 		 (isNotEmpty(allele2) && isNotEmpty(item->allele2) &&
 		  !sameString(allele2, item->allele2)))
 	    warn("Allele order in hapmapSnps%s (%s/%s) is different from earlier table(s) (%s/%s)",
 		 popCode, item->allele1, item->allele2, allele1, allele2);
 	totalA1Count += 2*item->homoCount1 + item->heteroCount;
 	totalA2Count += 2*item->homoCount2 + item->heteroCount;
 	totalHaploCount += haploCounts[i];
 	sumHet += ((float)item->heteroCount /
 		   (item->homoCount1 + item->homoCount2 + item->heteroCount));
 	sumA1A1 += item->homoCount1;
 	sumA1A2 += item->heteroCount;
 	sumA2A2 += item->homoCount2;
 	popCount++;
 	}
     }
 printf("<TR><TH>Population</TH> <TH>%s</TH> <TH>%s</TH></TR>\n", allele1, allele2);
 for (i=0;  i < HAP_PHASEIII_POPCOUNT;  i++)
     showOneHapmapRow(hapmapPhaseIIIPops[i], allele1, allele2, majorAlleles[i],
 		     majorCounts[i], haploCounts[i]);
 showHapmapAverageRow("Average", (float)totalA1Count / totalHaploCount);
 printf("</TABLE>\n");
 
 printf("<BR><B>Average of populations' observed heterozygosities:</B> %3.2f%%<BR>\n",
        (100.0 * sumHet/popCount));
 
 if (showOrtho)
     {
     boolean showedHeader = FALSE;
     int i;
     for (i = 0;  hapmapOrthoSpecies[i] != NULL; i++)
 	{
 	char table[HDB_MAX_TABLE_STRING];
 	safef(table, sizeof(table), "hapmapAlleles%s", hapmapOrthoSpecies[i]);
 	if (sqlTableExists(conn, table))
 	    {
 	    if (!showedHeader)
 		{
 		printf("<BR><B>Orthologous alleles from reference genome assemblies:</B><BR>\n");
 		printf("<TABLE BORDER=1>\n");
 		printf("<TR><TH>Species</TH> <TH>Allele</TH> <TH>Quality Score</TH></TR>\n");
 		showedHeader = TRUE;
 		}
 	    char query[512];
 	    sqlSafef(query, sizeof(query),
 		  "select orthoAllele, score, strand from %s where name = '%s' and chrom = '%s'",
 		  table, itemName, seqName);
 	    struct sqlResult *sr = sqlGetResult(conn, query);
 	    char **row;
 	    if ((row = sqlNextRow(sr)) != NULL)
 		{
 		char *allele = row[0];
 		char *qual = row[1];
 		char *strand = row[2];
 		if (sameString("-", strand))
 		    reverseComplement(allele, strlen(allele));
 		printf("<TR><TD>%s</TD><TD>%s</TD><TD>%s</TD></TR>",
 		       hapmapOrthoSpecies[i], allele, qual);
 		}
 	    else
 		printf("<TR><TD>%s</TD><TD>N/A</TD><TD>N/A</TD></TR>", hapmapOrthoSpecies[i]);
 	    sqlFreeResult(&sr);
 	    }
 	}
     if (showedHeader)
 	printf("</TABLE>\n");
     }
 }
 
 void doHapmapSnpsSummary(struct sqlConnection *conn, struct trackDb *tdb, char *itemName,
 			 boolean showOrtho)
 /* Display per-population allele frequencies. */
 {
 boolean isPhaseIII = sameString(trackDbSettingOrDefault(tdb, "hapmapPhase", "II"), "III");
 if (!isPhaseIII && sqlTableExists(conn, "hapmapAllelesSummary"))
     doHapmapSnpsSummaryTable(conn, tdb, itemName, showOrtho);
 else
     doHapmapSnpsAllPops(conn, tdb, itemName, showOrtho);
 }
 
 void doHapmapSnps(struct trackDb *tdb, char *itemName)
 /* assume just one hapmap snp at a given location */
 {
 char *table = tdb->table;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 int majorCount = 0;
 int minorCount = 0;
 char *majorAllele = NULL;
 char *minorAllele = NULL;
 char popCode[4];
 safencpy(popCode, sizeof(popCode), table + strlen("hapmapSnps"), 3);
 popCode[3] = '\0';
 
 genericHeader(tdb, itemName);
 
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 struct hapmapSnps *item = hapmapSnpsLoad(row+rowOffset);
 printf("<B>SNP rsId:</B> ");
 printDbSnpRsUrl(itemName, "%s", itemName);
 puts("<BR>");
 printf("<B>Position:</B> <A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">%s:%d-%d</A><BR>\n",
        hgTracksPathAndSettings(), database, item->chrom, item->chromStart+1, item->chromEnd,
        item->chrom, item->chromStart+1, item->chromEnd);
 printf("<B>Strand:</B> %s<BR>\n", item->strand);
 printf("<B>Polymorphism type:</B> %s<BR>\n", item->observed);
 if (item->homoCount1 >= item->homoCount2)
     {
     majorAllele = cloneString(item->allele1);
     majorCount = item->homoCount1;
     minorCount = item->homoCount2;
     minorAllele = cloneString(item->allele2);
     }
 else
     {
     majorAllele = cloneString(item->allele2);
     majorCount = item->homoCount2;
     minorCount = item->homoCount1;
     minorAllele = cloneString(item->allele1);
     }
 
 printf("<BR><B>Genotype counts for %s:</B><BR>\n", popCode);
 printf("<TABLE BORDER=1>\n");
 printf("<TR><TD>Major allele (%s) homozygotes</TD><TD align=right>%d individuals</TD></TR>\n",
        majorAllele, majorCount);
 if (minorCount > 0)
     printf("<TR><TD>Minor allele (%s) homozygotes</TD><TD align=right>%d individuals</TD></TR>\n",
 	   minorAllele, minorCount);
 if (item->heteroCount > 0)
     printf("<TR><TD>Heterozygotes (%s)</TD><TD align=right>%d individuals</TD></TR>\n",
 	   item->observed, item->heteroCount);
 printf("<TR><TD>Total</TD><TD align=right>%d individuals</TD></TR>\n",
        majorCount + minorCount + item->heteroCount);
 printf("</TABLE>\n");
 
 sqlFreeResult(&sr);
 
 /* Show allele frequencies for all populations */
 doHapmapSnpsSummary(conn, tdb, itemName, TRUE);
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doHapmapOrthos(struct trackDb *tdb, char *itemName)
 /* could assume just one match */
 {
 char *table = tdb->table;
 struct hapmapAllelesOrtho *ortho;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 char *otherDb = NULL;
 char *otherDbName = NULL;
 
 genericHeader(tdb, itemName);
 
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     ortho = hapmapAllelesOrthoLoad(row+rowOffset);
     printf("<B>Human Position:</B> "
            "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
                   hgTracksPathAndSettings(), database, ortho->chrom, ortho->chromStart+1, ortho->chromEnd);
     printf("%s:%d-%d</A><BR>\n", ortho->chrom, ortho->chromStart+1, ortho->chromEnd);
     printf("<B>Human Strand: </B> %s\n", ortho->strand);
     printf("<BR>");
     printf("<B>Polymorphism type:</B> %s<BR>\n", ortho->observed);
 
     if (startsWith("hapmapAllelesChimp", table))
         {
         otherDb = "panTro2";
 	otherDbName = "Chimp";
 	}
     if (startsWith("hapmapAllelesMacaque", table))
         {
         otherDb = "rheMac2";
 	otherDbName = "Macaque";
 	}
 
     printf("<B>%s </B>", otherDbName);
     printf("<B>Position:</B> "
            "<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">",
                   hgTracksPathAndSettings(), otherDb, ortho->orthoChrom, ortho->orthoStart, ortho->orthoEnd);
     linkToOtherBrowser(otherDb, ortho->orthoChrom, ortho->orthoStart, ortho->orthoEnd);
     printf("%s:%d-%d</A><BR>\n", ortho->orthoChrom, ortho->orthoStart+1, ortho->orthoEnd);
 
     printf("<B>%s </B>", otherDbName);
     printf("<B>Strand:</B> %s\n", ortho->orthoStrand);
     printf("<BR>");
 
     printf("<B>%s </B>", otherDbName);
     printf("<B>Allele:</B> %s\n", ortho->orthoAllele);
     printf("<BR>");
     printf("<B>%s </B>", otherDbName);
     printf("<B>Allele Quality (0-100):</B> %d\n", ortho->score);
 
     }
 
 printf("<BR>\n");
 /* get summary data (allele frequencies) here */
 /* don't repeat ortho display */
 doHapmapSnpsSummary(conn, tdb, itemName, FALSE);
 printTrackHtml(tdb);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 
 void printSnpAllele(char *orthoDb, int snpVersion, char *rsId)
 /* check whether snpAlleles exists for a database */
 /* if found, print value */
 {
 char tableName[512];
 struct sqlConnection *conn = sqlConnect(orthoDb);
 char query[256];
 struct sqlResult *sr;
 char **row = NULL;
 
 safef(tableName, sizeof(tableName), "snp%d%sorthoAllele", snpVersion, database);
 if (!hTableExists(orthoDb, tableName))
     {
     sqlDisconnect(&conn);
     return;
     }
 
 sqlSafef(query, sizeof(query), "select allele from %s where name = '%s'", tableName, rsId);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 if (!row)
     {
     sqlDisconnect(&conn);
     return;
     }
 printf("<B>%s Allele:</B> %s<BR>\n", orthoDb, row[0]);
 sqlFreeResult(&sr);
 sqlDisconnect(&conn);
 }
 
 
 static char *fbgnFromCg(char *cgId)
 /* Given a BDGP ID, looks up its FBgn ID because FlyBase query no longer
  * supports BDGP IDs.  Returns NULL if not found.
  * Do not free the statically allocated result. */
 {
 static char result[32];  /* Ample -- FBgn ID's are 11 chars long. */
 char query[512];
 if (hTableExists(database, "flyBase2004Xref"))
     sqlSafef(query, sizeof(query),
 	  "select fbgn from flyBase2004Xref where name = '%s';", cgId);
 else if (hTableExists(database, "bdgpGeneInfo"))
     sqlSafef(query, sizeof(query),
 	  "select flyBaseId from bdgpGeneInfo where bdgpName = '%s';", cgId);
 else
     return NULL;
 struct sqlConnection *conn = hAllocConn(database);
 char *resultOrNULL =  sqlQuickQuery(conn, query, result, sizeof(result));
 hFreeConn(&conn);
 return resultOrNULL;
 }
 
 static void doPscreen(struct trackDb *tdb, char *item)
 /* P-Screen (BDGP Gene Disruption Project) P el. insertion locations/genes. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int start = cartInt(cart, "o");
 char fullTable[64];
 boolean hasBin = FALSE;
 char query[512];
 
 genericHeader(tdb, item);
 hFindSplitTable(database, seqName, tdb->table, fullTable, &hasBin);
 sqlSafef(query, sizeof(query),
      "select * from %s where chrom = '%s' and chromStart = %d and name = '%s'",
       fullTable, seqName, start, item);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     struct pscreen *psc = pscreenLoad(row+hasBin);
     int i;
     printCustomUrl(tdb, psc->name, FALSE);
     printPosOnChrom(psc->chrom, psc->chromStart, psc->chromEnd, psc->strand,
 		    FALSE, psc->name);
     if (psc->stockNumber != 0)
 	printf("<B>Stock number:</B> "
 	       "<A HREF=\"http://flystocks.bio.indiana.edu/Reports/%d.html\" "
 	       "TARGET=_BLANK>%d</A><BR>\n", psc->stockNumber,
 	       psc->stockNumber);
     for (i=0;  i < psc->geneCount;  i++)
 	{
 	char gNum[4];
 	if (psc->geneCount > 1)
 	    safef(gNum, sizeof(gNum), " %d", i+1);
 	else
 	    gNum[0] = 0;
 	if (isNotEmpty(psc->geneIds[i]))
 	    {
 	    char *idType = "FlyBase";
 	    char *fbgnId = psc->geneIds[i];
 	    if (isBDGPName(fbgnId))
 		{
 		char *stripped = stripBDGPSuffix(psc->geneIds[i]);
 		idType = "BDGP";
 		fbgnId = fbgnFromCg(stripped);
 		}
 	    if (fbgnId == NULL)
 		printf("<B>Gene%s %s ID:</B> %s<BR>\n",
 		       gNum, idType, psc->geneIds[i]);
 	    else
 		printf("<B>Gene%s %s ID:</B> "
 		       "<A HREF=\"http://flybase.net/reports/%s.html\" "
 		       "TARGET=_BLANK>%s</A><BR>\n",
 		       gNum, idType, fbgnId, psc->geneIds[i]);
 	    }
 	}
     pscreenFree(&psc);
     }
 else
     errAbort("query returned no results: \"%s\"", query);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 static void doOligoMatch(char *item)
 /* Print info about oligo match. */
 {
 char *oligo = cartUsualString(cart,
 	oligoMatchVar, cloneString(oligoMatchDefault));
 touppers(oligo);
 cartWebStart(cart, database, "Perfect Matches to Short Sequence");
 printf("<B>Sequence:</B> %s<BR>\n", oligo);
 printf("<B>Chromosome:</B> %s<BR>\n", seqName);
 printf("<B>Start:</B> %s<BR>\n", item+1);
 printf("<B>Strand:</B> %c<BR>\n", item[0]);
 webIncludeHelpFile(OLIGO_MATCH_TRACK_NAME, TRUE);
 }
 
 struct slName *cutterIsoligamers(struct cutter *myEnzyme)
 /* Find enzymes with same cut site. */
 {
 struct sqlConnection *conn;
 struct cutter *cutters = NULL;
 struct slName *ret = NULL;
 
 conn = hAllocConn("hgFixed");
 cutters = cutterLoadByQuery(conn, NOSQLINJ "select * from cutters");
 ret = findIsoligamers(myEnzyme, cutters);
 hFreeConn(&conn);
 cutterFreeList(&cutters);
 return ret;
 }
 
 void cutterPrintSite(struct cutter *enz)
 /* Print out the enzyme REBASE style. */
 {
 int i;
 for (i = 0; i < enz->size+1; i++)
     {
     if (i == enz->cut)
 	printf("^");
     else if (i == enz->cut + enz->overhang)
 	printf("v");
     if (i < enz->size)
 	printf("%c", enz->seq[i]);
     }
 }
 
 static void doCuttersEnzymeList(struct sqlConnection *conn, char *getBed, char *c, char *l, char *r)
 /* Print out list of enzymes (BED). This function will exit the program. */
 {
 struct cutter *cut = NULL;
 char query[100];
 struct dnaSeq *winDna;
 struct bed *bedList = NULL, *oneBed;
 int s, e;
 if (!c || !l || !r)
     errAbort("Bad Range");
 s = atoi(l);
 e = atoi(r);
 winDna = hDnaFromSeq(database, c, s, e, dnaUpper);
 if (sameString(getBed, "all"))
     sqlSafef(query, sizeof(query), "select * from cutters");
 else
     sqlSafef(query, sizeof(query), "select * from cutters where name=\'%s\'", getBed);
 cut = cutterLoadByQuery(conn, query);
 bedList = matchEnzymes(cut, winDna, s);
 puts("<HTML>\n<HEAD><TITLE>Enzyme Output</TITLE></HEAD>\n<BODY><PRE><TT>");
 for (oneBed = bedList; oneBed != NULL; oneBed = oneBed->next)
     {
     freeMem(oneBed->chrom);
     oneBed->chrom = cloneString(c);
     bedTabOutN(oneBed, 6, stdout);
     }
 puts("</TT></PRE>\n");
 cartFooter();
 bedFreeList(&bedList);
 cutterFreeList(&cut);
 hFreeConn(&conn);
 exit(0);
 }
 
 static void doCutters(char *item)
 /* Print info about a restriction enzyme. */
 {
 struct sqlConnection *conn;
 struct cutter *cut = NULL;
 char query[100];
 char *doGetBed = cgiOptionalString("doGetBed");
 char *c = cgiOptionalString("c");
 char *l = cgiOptionalString("l");
 char *r = cgiOptionalString("r");
 conn = hAllocConn("hgFixed");
 if (doGetBed)
     doCuttersEnzymeList(conn, doGetBed, c, l, r);
 sqlSafef(query, sizeof(query), "select * from cutters where name=\'%s\'", item);
 cut = cutterLoadByQuery(conn, query);
 cartWebStart(cart, database, "Restriction Enzymes from REBASE");
 if (cut)
     {
     char *o = cgiOptionalString("o");
     char *t = cgiOptionalString("t");
     struct slName *isoligs = cutterIsoligamers(cut);
     printf("<B>Enzyme Name:</B> %s<BR>\n", cut->name);
     /* Display position only if click came from hgTracks. */
     if (c && o && t)
         {
 	int left = atoi(o);
 	int right = atoi(t);
 	printPosOnChrom(c, left, right, NULL, FALSE, cut->name);
         }
     puts("<B>Recognition Sequence: </B>");
     cutterPrintSite(cut);
     puts("<BR>\n");
     printf("<B>Palindromic: </B>%s<BR>\n", (cut->palindromic) ? "YES" : "NO");
     if (cut->numSciz > 0)
         {
 	int i;
 	puts("<B>Isoschizomers: </B>");
 	for (i = 0; i < cut->numSciz-1; i++)
 	    printf("<A HREF=\"%s&g=%s&i=%s\">%s</A>, ", hgcPathAndSettings(), CUTTERS_TRACK_NAME, cut->scizs[i], cut->scizs[i]);
 	printf("<A HREF=\"%s&g=%s&i=%s\">%s</A><BR>\n", hgcPathAndSettings(), CUTTERS_TRACK_NAME, cut->scizs[cut->numSciz-1], cut->scizs[cut->numSciz-1]);
 	}
     if (isoligs)
 	{
 	struct slName *cur;
 	puts("<B>Isoligamers: </B>");
 	for (cur = isoligs; cur->next != NULL; cur = cur->next)
 	    printf("<A HREF=\"%s&g=%s&i=%s\">%s</A>, ", hgcPathAndSettings(), CUTTERS_TRACK_NAME, cur->name, cur->name);
 	printf("<A HREF=\"%s&g=%s&i=%s\">%s</A><BR>\n", hgcPathAndSettings(), CUTTERS_TRACK_NAME, cur->name, cur->name);
 	slFreeList(&isoligs);
 	}
     if (cut->numRefs > 0)
 	{
 	int i, count = 1;
 	char **row;
 	struct sqlResult *sr;
 	puts("<B>References:</B><BR>\n");
 	sqlSafef(query, sizeof(query), "select * from rebaseRefs");
 	sr = sqlGetResult(conn, query);
 	while ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    int refNum = atoi(row[0]);
             for (i = 0; i < cut->numRefs; i++)
 		{
 		if (refNum == cut->refs[i])
 		    printf("%d. %s<BR>\n", count++, row[1]);
 		}
 	    }
 	sqlFreeResult(&sr);
         }
     if (c && o && t)
         {
 	puts("<BR><B>Download BED of enzymes in this browser range:</B>&nbsp");
 	printf("<A HREF=\"%s&g=%s&l=%s&r=%s&c=%s&doGetBed=all\">all enzymes</A>, ", hgcPathAndSettings(), CUTTERS_TRACK_NAME, l, r, c);
 	printf("<A HREF=\"%s&g=%s&l=%s&r=%s&c=%s&doGetBed=%s\">just %s</A><BR>\n", hgcPathAndSettings(), CUTTERS_TRACK_NAME, l, r, c, cut->name, cut->name);
 	}
     }
 webIncludeHelpFile(CUTTERS_TRACK_NAME, TRUE);
 cutterFree(&cut);
 hFreeConn(&conn);
 }
 
 static void doAnoEstTcl(struct trackDb *tdb, char *item)
 /* Print info about AnoEst uniquely-clustered item. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 int start = cartInt(cart, "o");
 genericHeader(tdb, item);
 printCustomUrl(tdb, item, TRUE);
 genericBedClick(conn, tdb, item, start, 12);
 if (hTableExists(database, "anoEstExpressed"))
     {
     char query[512];
 
     sqlSafef(query, sizeof(query),
 	  "select 1 from anoEstExpressed where name = '%s'", item);
     if (sqlQuickNum(conn, query))
 	puts("<B>Expressed:</B> yes<BR>");
     else
 	puts("<B>Expressed:</B> no<BR>");
     }
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 
 void mammalPsgTableRow(char *test, char *description, float pVal, unsigned isFdrSignificant)
 /* print single row of the overview table for mammal PSG track */
 {
 char *start = "";
 char *end = "";
 
 if (isFdrSignificant)
     {
     start = "<b>";
     end = "</b>";
     }
 
 if (pVal<=1)
     {
     printf("<tr><td>%s%s%s</td><td>%s%s%s</td><td>%s%.02g%s</tr>\n",
 	   start,test,end,
 	   start,description,end,
            start,pVal,end);
     }
 }
 
 void doMammalPsg(struct trackDb *tdb, char *itemName)
 /* create details page for mammalPsg track */
 {
 struct mammalPsg *mammalPsg = NULL;
 char query[512];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char *bayesianFiguresUrl = "../images/mammalPsg";
 
 genericHeader(tdb, itemName);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s'", tdb->table, itemName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     mammalPsg = mammalPsgLoad(row);
 else
     errAbort("Can't find item '%s'", itemName);
 
 sqlFreeResult(&sr);
 
 /* first print the same thing that you would print for ordinary bed track */
 bedPrintPos((struct bed *) mammalPsg,12,tdb);
 
 /* rows showing the results of individual likelihood ratio tests */
 printf("<p><b>Likelihood ratio tests for positive selection:</b></p>\n");
 printf("<p><table border=1>\n");
 printf("<tr><th>Test</th><th>Description</th><th>P-value</th>");
 mammalPsgTableRow("A","all branches",mammalPsg->lrtAllPValue,mammalPsg->lrtAllIsFdr);
 mammalPsgTableRow("B","branch leading to primates",mammalPsg->lrtPrimateBrPValue,mammalPsg->lrtPrimateBrIsFdr);
 mammalPsgTableRow("C","primate clade",mammalPsg->lrtPrimateClPValue,mammalPsg->lrtPrimateClIsFdr);
 mammalPsgTableRow("D","branch leading to rodents",mammalPsg->lrtRodentBrPValue,mammalPsg->lrtRodentBrIsFdr);
 mammalPsgTableRow("E","rodent clade",mammalPsg->lrtRodentClPValue,mammalPsg->lrtRodentClIsFdr);
 mammalPsgTableRow("F","human branch",mammalPsg->lrtHumanPValue,mammalPsg->lrtHumanIsFdr);
 mammalPsgTableRow("G","chimp branch",mammalPsg->lrtChimpPValue,mammalPsg->lrtChimpIsFdr);
 mammalPsgTableRow("H","branch leading to hominids",mammalPsg->lrtHominidPValue,mammalPsg->lrtHominidIsFdr);
 mammalPsgTableRow("I","macaque branch",mammalPsg->lrtMacaquePValue,mammalPsg->lrtMacaqueIsFdr);
 printf("</table></p>\n");
 printf("<p>(FDR significant P-value shown in boldface)</p>\n");
 
 /* pictures showing the Bayesian analysis */
 
 if (mammalPsg->bestHist > 0)
     {
     printf("<p><b>Results of Bayesian analysis:</b><br>\n");
     printf("<table border=0 cellpadding=20><tr>\n");
     printf("<td align=center><b>Best model - posterior prob. %.2f</b><br>&nbsp;<br><img src=\"%s/model-%d-1.jpg\"></td>\n",
 	   mammalPsg->bestHistPP,bayesianFiguresUrl,mammalPsg->bestHist);
     printf("<td align=center><b>2nd best - posterior prob. %.2f</b><br>&nbsp;<br><img src=\"%s/model-%d-1.jpg\"></td>\n",
 	   mammalPsg->nextBestHistPP,bayesianFiguresUrl,mammalPsg->nextBestHist);
     printf("</tr></table></p>\n");
     }
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doDless(struct trackDb *tdb, char *itemName)
 /* create details page for DLESS */
 {
 struct dless *dless = NULL;
 char query[512];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 boolean approx;
 enum {CONS, GAIN, LOSS} elementType;
 
 genericHeader(tdb, itemName);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s'", tdb->table, itemName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     dless = dlessLoad(row);
 else
     errAbort("Can't find item '%s'", itemName);
 
 sqlFreeResult(&sr);
 
 approx = sameString(dless->condApprox, "approx");
 if (sameString(dless->type, "conserved"))
     elementType = CONS;
 else if (sameString(dless->type, "gain"))
     elementType = GAIN;
 else
     elementType = LOSS;
 
 if (elementType == CONS)
     printf("<B>Prediction:</B> conserved in all species<BR>\n");
 else
     printf("<B>Prediction:</B> %s of element on branch above node labeled \"%s\"<BR>\n",
            elementType == GAIN ? "gain" : "loss", dless->branch);
 printPos(dless->chrom, dless->chromStart, dless->chromEnd, NULL,
          FALSE, dless->name);
 printf("<B>Log-odds score:</B> %.1f bits<BR>\n", dless->score);
 
 if (elementType == CONS)
     {
     printf("<B>P-value of conservation:</B> %.2e<BR><BR>\n", dless->pConsSub);
     printf("<B>Numbers of substitutions:</B>\n<UL>\n");
     printf("<LI>Null distribution: mean = %.2f, var = %.2f, 95%% c.i. = [%d, %d]\n",
            dless->priorMeanSub, dless->priorVarSub, dless->priorMinSub,
            dless->priorMaxSub);
     printf("<LI>Posterior distribution: mean = %.2f, var = %.2f\n</UL>\n",
            dless->postMeanSub, dless->postVarSub);
     }
 else
     {
     printf("<B>P-value of conservation in subtree:</B> %.2e<BR>\n",
            dless->pConsSub);
     printf("<B>P-value of conservation in rest of tree:</B> %.2e<BR>\n",
            dless->pConsSup);
     printf("<B>P-value of conservation in subtree given total:</B> %.2e%s<BR>\n",
            dless->pConsSubCond, approx ? "*" : "");
     printf("<B>P-value of conservation in rest of tree given total:</B> %.2e%s<BR><BR>\n",
            dless->pConsSupCond, approx ? "*" : "");
     printf("<B>Numbers of substitutions in subtree beneath event</B>:\n<UL>\n");
     printf("<LI>Null distribution: mean = %.2f, var = %.2f, 95%% c.i. = [%d, %d]\n",
            dless->priorMeanSub, dless->priorVarSub, dless->priorMinSub,
            dless->priorMaxSub);
     printf("<LI>Posterior distribution: mean = %.2f, var = %.2f\n",
            dless->postMeanSub, dless->postVarSub);
     printf("</UL><B>Numbers of substitutions in rest of tree:</B>\n<UL>\n");
     printf("<LI>Null distribution: mean = %.2f, var = %.2f, 95%% c.i. = [%d, %d]\n",
            dless->priorMeanSup, dless->priorVarSup, dless->priorMinSup,
            dless->priorMaxSup);
     printf("<LI>Posterior distribution: mean = %.2f, var = %.2f\n</UL>\n",
            dless->postMeanSup, dless->postVarSup);
     if (approx)
         printf("* = Approximate p-value (usually conservative)<BR>\n");
     }
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void showSomeAlignment2(struct psl *psl, bioSeq *qSeq, enum gfType qType, int qStart,
                         int qEnd, char *entryName, char *geneName, char *geneTable, int cdsS,
                         int cdsE)
 /* Display protein or DNA alignment in a frame. */
 {
 int blockCount = 0, i = 0, j= 0, *exnStarts = NULL, *exnEnds = NULL;
 struct tempName indexTn, bodyTn;
 FILE *index, *body;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 struct genePred *gene = NULL;
 char **row, query[256];
 int tStart = psl->tStart;
 int tEnd = psl->tEnd;
 char tName[256];
 struct dnaSeq *tSeq;
 char *tables[4] = {"luGene", "refGene", "mgcGenes", "luGene2"};
 
 /* open file to write to */
 trashDirFile(&indexTn, "index", "index", ".html");
 trashDirFile(&bodyTn, "body", "body", ".html");
 body = mustOpen(bodyTn.forCgi, "w");
 
 /* get query genes struct info*/
 for(i = 0; i < 4; i++)
     {
     if(sqlTableExists(conn, tables[i]))
 	{
 	sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE name = '%s'"
 		"AND chrom = '%s' AND txStart <= %d "
 		"AND txEnd >= %d",
 		tables[i], geneName, psl->qName, qStart, qEnd);
 	sr = sqlMustGetResult(conn, query);
 	if((row = sqlNextRow(sr)) != NULL)
 	    {
 	    int hasBin = 0;
 	    if(hOffsetPastBin(database, psl->qName, tables[i]))
 		hasBin=1;
 	    gene = genePredLoad(row+hasBin);
 	    break;
 	    }
 	else
 	    sqlFreeResult(&sr);
 	}
     }
 if(i == 4)
     errAbort("Can't find query for %s in %s. This entry may no longer exist\n", geneName, geneTable);
 
 
 AllocArray(exnStarts, gene->exonCount);
 AllocArray(exnEnds, gene->exonCount);
 for(i = 0; i < gene->exonCount; i++)
     {
     if(gene->exonStarts[i] < qEnd && gene->exonEnds[i] > qStart)
 	{
 	exnStarts[j] = gene->exonStarts[i] > qStart ? gene->exonStarts[i] : qStart;
 	exnEnds[j] = gene->exonEnds[i] < qEnd ? gene->exonEnds[i] : qEnd;
 	j++;
 	}
     }
 genePredFree(&gene);
 
 /* Writing body of alignment. */
 body = mustOpen(bodyTn.forCgi, "w");
 htmStartDirDepth(body, psl->qName, 2);
 
 /* protein psl's have a tEnd that isn't quite right */
 if ((psl->strand[1] == '+') && (qType == gftProt))
     tEnd = psl->tStarts[psl->blockCount - 1] + psl->blockSizes[psl->blockCount - 1] * 3;
 
 tSeq = hDnaFromSeq(database, seqName, psl->tStart, psl->tEnd, dnaLower);
 
 freez(&tSeq->name);
 tSeq->name = cloneString(psl->tName);
 safef(tName, sizeof(tName), "%s.%s", organism, psl->tName);
 if (psl->qName == NULL)
     fprintf(body, "<H2>Alignment of %s and %s:%d-%d</H2>\n",
 	    entryName, psl->tName, psl->tStart+1, psl->tEnd);
 else
     fprintf(body, "<H2>Alignment of %s and %s:%d-%d</H2>\n",
 	    entryName, psl->tName, psl->tStart+1, psl->tEnd);
 
 fputs("Click on links in the frame to the left to navigate through "
       "the alignment.\n", body);
 
 safef(tName, sizeof(tName), "%s.%s", organism, psl->tName);
 blockCount = pslGenoShowAlignment(psl, qType == gftProt, entryName, qSeq, qStart, qEnd,
                                   tName, tSeq, tStart, tEnd, exnStarts, exnEnds, j, body);
 freez(&exnStarts);
 freez(&exnEnds);
 freeDnaSeq(&tSeq);
 
 htmEnd(body);
 fclose(body);
 chmod(bodyTn.forCgi, 0666);
 
 /* Write index. */
 index = mustOpen(indexTn.forCgi, "w");
 if (entryName == NULL)
     entryName = psl->qName;
 htmStartDirDepth(index, entryName, 2);
 fprintf(index, "<H3>Alignment of %s</H3>", entryName);
 fprintf(index, "<A HREF=\"../%s#cDNA\" TARGET=\"body\">%s</A><BR>\n", bodyTn.forCgi, entryName);
 fprintf(index, "<A HREF=\"../%s#genomic\" TARGET=\"body\">%s.%s</A><BR>\n", bodyTn.forCgi, hOrganism(database), psl->tName);
 for (i=1; i<=blockCount; ++i)
     {
     fprintf(index, "<A HREF=\"../%s#%d\" TARGET=\"body\">block%d</A><BR>\n",
 	    bodyTn.forCgi, i, i);
     }
 fprintf(index, "<A HREF=\"../%s#ali\" TARGET=\"body\">together</A><BR>\n", bodyTn.forCgi);
 fclose(index);
 chmod(indexTn.forCgi, 0666);
 
 /* Write (to stdout) the main html page containing just the frame info. */
 puts("<FRAMESET COLS = \"13%,87% \" >");
 printf("  <FRAME SRC=\"%s\" NAME=\"index\">\n", indexTn.forCgi);
 printf("  <FRAME SRC=\"%s\" NAME=\"body\">\n", bodyTn.forCgi);
 puts("<NOFRAMES><BODY></BODY></NOFRAMES>");
 puts("</FRAMESET>");
 puts("</HTML>\n");
 exit(0);	/* Avoid cartHtmlEnd. */
 }
 
 
 void potentPslAlign(char *htcCommand, char *item)
 {/* show the detail psl alignment between genome */
 char *pslTable = cgiString("pslTable");
 char *chrom = cgiString("chrom");
 int start = cgiInt("cStart");
 int end = cgiInt("cEnd");
 struct psl *psl = NULL;
 struct dnaSeq *qSeq = NULL;
 char *db = cgiString("db");
 char name[64];
 char query[256], fullTable[64];
 char **row;
 boolean hasBin;
 struct sqlResult *sr = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 
 
 hFindSplitTable(database, chrom, pslTable, fullTable, &hasBin);
 
 sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE "
         "tName = '%s' AND tStart = %d "
 	"AND tEnd = %d",
         pslTable, chrom, start, end);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if(row != NULL)
     {
     psl = pslLoad(row+hasBin);
     }
 else
     {
     errAbort("No alignment infomation\n");
     }
 qSeq = loadGenomePart(db, psl->qName, psl->qStart, psl->qEnd);
 safef(name, sizeof name, "%s in %s(%d-%d)", item,psl->qName, psl->qStart, psl->qEnd);
 writeFramesetType();
 puts("<HTML>");
 printf("<HEAD>\n<TITLE>%s %dk</TITLE>\n</HEAD>\n\n", name, psl->qStart/1000);
 showSomeAlignment2(psl, qSeq, gftDnaX, psl->qStart, psl->qEnd, name, item, "", psl->qStart, psl->qEnd);
 }
 
 void doPutaFrag(struct trackDb *tdb, char *item)
 /* display the potential pseudo and coding track */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row, table[256], query[256], *parts[6];
 struct putaInfo *info = NULL;
 int start = cartInt(cart, "o"),  end = cartInt(cart, "t");
 char *db = cgiString("db");
 char *name = cartString(cart, "i"),  *chr = cartString(cart, "c");
 char pslTable[256];
 char otherString[256];
 
 safef(table, sizeof table, "putaInfo");
 safef(pslTable, sizeof pslTable, "potentPsl");
 cartWebStart(cart, database, "Putative Coding or Pseudo Fragments");
 sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE name = '%s' "
         "AND chrom = '%s' AND chromStart = %d "
         "AND chromEnd = %d",
          table, name, chr, start, end);
 
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 
 if(row != NULL)
     {
     info = putaInfoLoad(row+1);
     }
 else
     {
     errAbort("Can't find information for %s in data base\n", name);
     }
 sqlFreeResult(&sr);
 
 char *tempName = cloneString(name);
 chopByChar(tempName, '|',parts, 4);
 
 printf("<B>%s</B> is homologous to the known gene: <A HREF=\"", name);
 printEntrezNucleotideUrl(stdout, parts[0]);
 printf("\" TARGET=_blank>%s</A><BR>\n", parts[0]);
 printf("<B>%s </B>is aligned here with score : %d<BR><BR>\n", parts[0], info->score);
 
 /* print the info about the stamper gene */
 printf("<B> %s</B><BR>\n", parts[0]);
 printf("<B>Genomic location of the mapped part of %s</B>: <A HREF=\""
        "%s?db=%s&position=%s:%d-%d\" TARGET=_blank>%s(%s):%d-%d </A> <BR>\n",
        parts[0], hgTracksName(), db, info->oChrom, info->oChromStart, info->oChromEnd,
        info->oChrom, parts[2],info->oChromStart+1, info->oChromEnd);
 printf("<B>Mapped %s Exons</B>: %d of %d. <BR> <B>Mapped %s CDS exons</B>: %d of %d <BR>\n", parts[0], info->qExons[0], info->qExons[1], parts[0], info->qExons[2], info->qExons[3]);
 
 printf("<b>Aligned %s bases</B>:%d of %d with %f identity. <BR> <B>Aligned %s CDS bases</B>:  %d of %d with %f identity.<BR><BR>\n", parts[0],info->qBases[0], info->qBases[1], info->id[0], parts[0], info->qBases[2], info->qBases[3], info->id[1]);
 
 /* print info about the stamp putative element */
 printf("<B>%s </B><BR> <B>Genomic location: </B>"
        " <A HREF=\"%s?db=%s&position=%s:%d-%d\" >%s(%s): %d - %d</A> <BR> <B> Element Structure: </B> %d putative exons and %d putative cds exons<BR><BR>\n",
        name, hgTracksName(), db, info->chrom, info->chromStart+1, info->chromEnd, info->chrom, info->strand, info->chromStart+1, info->chromEnd, info->tExons[0], info->tExons[1]);
 if(info->repeats[0] > 0)
     {
     printf("Repeats elements inserted into %s <BR>\n", name);
     }
 if(info->stop >0)
     {
     int k = 0;
     printf("Premature stops in block ");
     for(k = 0; k < info->blockCount; k++)
 	{
 	if(info->stops[k] > 0)
 	    {
 	    if(info->strand[0] == '+')
 		printf("%d ",k+1);
 	    else
 		printf("%d ", info->blockCount - k);
 	    }
 	}
     printf("<BR>\n");
     }
 
 
 /* show genome sequence */
 hgcAnchorSomewhere("htcGeneInGenome", info->name, tdb->track, seqName);
 printf("View DNA for this putative fragment</A><BR>\n");
 
 /* show the detail alignment */
 sqlSafef(query, sizeof query, "SELECT * FROM %s WHERE "
 	"tName = '%s' AND tStart = %d "
 	"AND tEnd = %d AND strand = '%c%c'",
 	pslTable, info->chrom, info->chromStart, info->chromEnd, parts[2][0], info->strand[0]);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
 if(row != NULL)
     {
     safef(otherString, sizeof otherString, "&db=%s&pslTable=%s&chrom=%s&cStart=%d&cEnd=%d&strand=%s&qStrand=%s",
 	    database, pslTable, info->chrom,info->chromStart, info->chromEnd, info->strand, parts[2]);
     hgcAnchorSomewhere("potentPsl", parts[0], otherString, info->chrom);
     printf("<BR>View details of parts of alignment </A>.</BR>\n");
     }
 sqlFreeResult(&sr);
 putaInfoFree(&info);
 hFreeConn(&conn);
 }
 
 void doInterPro(struct trackDb *tdb, char *itemName)
 {
 char condStr[255];
 char *desc;
 struct sqlConnection *conn;
 
 genericHeader(tdb, itemName);
 
 conn = hAllocConn(database);
 sqlSafefFrag(condStr, sizeof condStr, "interProId='%s'", itemName);
 desc = sqlGetField("proteome", "interProXref", "description", condStr);
 
 printf("<B>Item:</B> %s <BR>\n", itemName);
 printf("<B>Description:</B> %s <BR>\n", desc);
 printf("<B>Outside Link:</B> ");
 printf("<A HREF=");
 
 printf("http://www.ebi.ac.uk/interpro/DisplayIproEntry?ac=%s", itemName);
 printf(" Target=_blank> %s </A> <BR>\n", itemName);
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doDv(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct dvBed dvBed;
 struct dv *dv;
 struct dvXref2 *dvXref2;
 struct omimTitle *omimTitle;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr, *sr2, *sr3, *sr4;
 char **row;
 char query[256], query2[256], query3[256], query4[256];
 
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 
 printf("<B>Item:</B> %s <BR>\n", itemName);
 printf("<B>Outside Link:</B> ");
 printf("<A HREF=");
 printSwissProtVariationUrl(stdout, itemName);
 printf(" Target=_blank> %s </A> <BR>\n", itemName);
 
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     dvBedStaticLoad(row+rowOffset, &dvBed);
     bedPrintPos((struct bed *)&dvBed, 3, tdb);
     }
 sqlFreeResult(&sr);
 
 sqlSafef(query2, sizeof(query2), "select * from dv where varId = '%s' ", itemName);
 sr2 = sqlGetResult(conn, query2);
 while ((row = sqlNextRow(sr2)) != NULL)
     {
     /* not using static load */
     dv = dvLoad(row);
     printf("<B>Swiss-prot ID:</B> %s <BR>\n", dv->spID);
     printf("<B>Start:</B> %d <BR>\n", dv->start);
     printf("<B>Length:</B> %d <BR>\n", dv->len);
     printf("<B>Original:</B> %s <BR>\n", dv->orig);
     printf("<B>Variant:</B> %s <BR>\n", dv->variant);
     dvFree(&dv);
     }
 sqlFreeResult(&sr2);
 
 sqlSafef(query3, sizeof(query3), "select * from dvXref2 where varId = '%s' ", itemName);
 char *protDbName = hPdbFromGdb(database);
 struct sqlConnection *protDbConn = hAllocConn(protDbName);
 sr3 = sqlGetResult(protDbConn, query3);
 while ((row = sqlNextRow(sr3)) != NULL)
     {
     dvXref2 = dvXref2Load(row);
     if (sameString("MIM", dvXref2->extSrc))
         {
         printf("<B>OMIM:</B> ");
         printf("<A HREF=");
         printOmimUrl(stdout, dvXref2->extAcc);
         printf(" Target=_blank> %s</A> \n", dvXref2->extAcc);
 	/* nested query here */
         if (hTableExists(database, "omimTitle"))
 	    {
             sqlSafef(query4, sizeof(query4), "select * from omimTitle where omimId = '%s' ", dvXref2->extAcc);
             sr4 = sqlGetResult(conn, query4);
             while ((row = sqlNextRow(sr4)) != NULL)
                 {
 		omimTitle = omimTitleLoad(row);
 		printf("%s\n", omimTitle->title);
 		omimTitleFree(&omimTitle);
 		}
 	    }
 	    printf("<BR>\n");
 	}
     dvXref2Free(&dvXref2);
     }
 sqlFreeResult(&sr3);
 hFreeConn(&protDbConn);
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void printOregannoLink (struct oregannoLink *link)
 /* this prints a link for oreganno */
 {
 struct hash *linkInstructions = NULL;
 struct hash *thisLink = NULL;
 char *linktype, *label = NULL;
 
 hgReadRa(database, organism, rootDir, "links.ra", &linkInstructions);
 /* determine how to do link from .ra file */
 thisLink = hashFindVal(linkInstructions, link->raKey);
 if (thisLink == NULL)
     return; /* no link found */
 /* type determined by fields eg url */
 linktype = hashFindVal(thisLink, "url");
 label = hashFindVal(thisLink, "label");
 if (linktype != NULL)
     {
     char url[256];
     char *accFlag = hashFindVal(thisLink, "acc");
     if (accFlag == NULL)
         safecpy(url, sizeof(url), linktype);
     else
         {
         char *accNum = hashFindVal(thisLink, "accNum");
         if (accNum == NULL)
             safef(url, sizeof(url), linktype, link->attrAcc);
         else if (sameString(accNum, "2"))
             {
             char *val[2];
 	    char *copy = cloneString(link->attrAcc);
             if (2 == chopString(copy, ",", val, 2))
 	        safef(url, sizeof(url), linktype, val[0], val[1]);
             }
         }
     if (label == NULL)
         label = "";  /* no label */
     printf("%s - <A HREF=\"%s\" TARGET=\"_BLANK\">%s</A>\n", label, url, link->attrAcc);
     }
 }
 
 void doOreganno(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct oreganno *r = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *prevLabel = NULL;
 int i = 0, listStarted = 0;
 
 //int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 
 /* postion, band, genomic size */
 sqlSafef(query, sizeof(query),
       "select * from %s where name = '%s'", table, itemName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     r = oregannoLoad(row);
     printf("<B>ORegAnno ID:</B> %s <BR>\n", r->id);
     #if 0 // all the same as the ID for now
         printf("<B>ORegAnno name:</B> %s <BR>\n", r->name);
     #endif
     printf("<B>Strand:</B> %s<BR>\n", r->strand);
     bedPrintPos((struct bed *)r, 3, tdb);
     /* start html list for attributes */
     printf("<DL>");
     }
 sqlFreeResult(&sr);
 
 if (sameString(table, "oregannoOther"))
     {
     printf("<B>Attributes as described from other species</B><BR>\n");
     }
 /* fetch and print the attributes */
 for (i=0; i < oregannoAttrSize; i++)
     {
     int used = 0;
     char *tab;
     if (sameString(table, "oregannoOther"))
         tab = cloneString("oregannoOtherAttr");
     else
 	tab = cloneString("oregannoAttr");
     /* names are quote safe, come from oregannoUi.c */
     sqlSafef(query, sizeof(query), "select * from %s where id = '%s' and attribute = '%s'", tab, r->id, oregannoAttributes[i]);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         struct oregannoAttr attr;
         used++;
         if (used == 1)
             {
             if (!prevLabel || differentString(prevLabel, oregannoAttrLabel[i]))
                 {
                 if (listStarted == 0)
                     listStarted = 1;
                 else
                     printf("</DD>");
 
                 printf("<DT><b>%s:</b></DT><DD>\n", oregannoAttrLabel[i]);
                 freeMem(prevLabel);
                 prevLabel = cloneString(oregannoAttrLabel[i]);
                 }
             }
         oregannoAttrStaticLoad(row, &attr);
         printf("%s ", attr.attrVal);
         printf("<BR>\n");
         }
     freeMem(tab);
     if (sameString(table, "oregannoOther"))
         tab = cloneString("oregannoOtherLink");
     else
         tab = cloneString("oregannoLink");
     sqlSafef(query, sizeof(query), "select * from %s where id = '%s' and attribute = '%s'", tab, r->id, oregannoAttributes[i]);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         struct oregannoLink link;
         used++;
         if (used == 1)
             {
             if (!prevLabel || differentString(prevLabel, oregannoAttrLabel[i]))
                 {
                 if (listStarted == 0)
                     listStarted = 1;
                 else
                     printf("</DD>");
 
                 printf("<DT><b>%s:</b></DT><DD>\n", oregannoAttrLabel[i]);
                 freeMem(prevLabel);
                 prevLabel = cloneString(oregannoAttrLabel[i]);
                 }
             }
         oregannoLinkStaticLoad(row, &link);
         printOregannoLink(&link);
         printf("<BR>\n");
         }
     freeMem(tab);
     }
 if (listStarted > 0)
     printf("</DD></DL>");
 
 oregannoFree(&r);
 freeMem(prevLabel);
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doSnpArray (struct trackDb *tdb, char *itemName, char *dataSource)
 {
 char *table = tdb->table;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int start = cartInt(cart, "o");
 int end = 0;
 // char *chrom = cartString(cart, "c");
 char nibName[HDB_MAX_PATH_STRING];
 struct dnaSeq *seq;
 
 genericHeader(tdb, itemName);
 
 /* Affy uses their own identifiers */
 if (sameString(dataSource, "Affy"))
     sqlSafef(query, sizeof(query),
         "select chromEnd, strand, observed, rsId from %s where chrom = '%s' and chromStart=%d", table, seqName, start);
 else
     sqlSafef(query, sizeof(query), "select chromEnd, strand, observed from %s where chrom = '%s' and chromStart=%d", table, seqName, start);
 
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     end = sqlUnsigned(row[0]);
     printPosOnChrom(seqName, start, end, row[1], FALSE, NULL);
     printf("<B>Polymorphism:</B> %s \n", row[2]);
 
     if (end == start + 1)
         {
         hNibForChrom(database, seqName, nibName);
         seq = hFetchSeq(nibName, seqName, start, end);
 	touppers(seq->dna);
         if (sameString(row[1], "-"))
            reverseComplement(seq->dna, 1);
         printf("<BR><B>Reference allele:</B> %s \n", seq->dna);
         }
 
     if (sameString(dataSource, "Affy"))
         {
         printf("<BR><BR><A HREF=\"https://www.affymetrix.com/LinkServlet?probeset=%s\" TARGET=_blank>NetAffx</A> (log in required, registration is free)\n", itemName);
         if (regexMatch(row[3], "^rs[0-9]+$"))
             {
 	    printf("<BR>");
 	    printDbSnpRsUrl(row[3], "dbSNP (%s)", row[3]);
 	    }
 	}
     else if (regexMatch(itemName, "^rs[0-9]+$"))
         {
 	printf("<BR>");
 	printDbSnpRsUrl(itemName, "dbSNP (%s)", itemName);
 	}
     }
 sqlFreeResult(&sr);
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doSnpArray2 (struct trackDb *tdb, char *itemName, char *dataSource)
 /* doSnpArray2 is essential the same as doSnpArray except that the strand is blanked out */
 /* This is a temp solution for 3 Illumina SNP Arrays to blank out strand info for non-dbSnp entries */
 /* Should be removed once Illumina comes up with a clear defintion of their strand data */
 {
 char *table = tdb->table;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int start = cartInt(cart, "o");
 int end = 0;
 char nibName[HDB_MAX_PATH_STRING];
 struct dnaSeq *seq;
 
 genericHeader(tdb, itemName);
 /* Affy uses their own identifiers */
 if (sameString(dataSource, "Affy"))
     sqlSafef(query, sizeof(query),
         "select chromEnd, strand, observed, rsId from %s where chrom = '%s' and chromStart=%d", table, seqName, start);
 else
     sqlSafef(query, sizeof(query), "select chromEnd, strand, observed from %s where chrom = '%s' and chromStart=%d", table, seqName, start);
 
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     end = sqlUnsigned(row[0]);
 
     /* force strand info to be blank for non-dbSnp entries, per Illumina's request */
     printPosOnChrom(seqName, start, end, " ", FALSE, NULL);
     printf("<B>Polymorphism:</B> %s \n", row[2]);
 
     if (end == start + 1)
         {
         hNibForChrom(database, seqName, nibName);
         seq = hFetchSeq(nibName, seqName, start, end);
 	touppers(seq->dna);
         if (sameString(row[1], "-"))
            reverseComplement(seq->dna, 1);
         printf("<BR><B>Reference allele:</B> %s \n", seq->dna);
         }
 
     if (sameString(dataSource, "Affy"))
         {
         printf("<BR><BR><A HREF=\"https://www.affymetrix.com/LinkServlet?probeset=%s\" TARGET=_blank>NetAffx</A> (log in required, registration is free)\n", itemName);
         if (regexMatch(row[3], "^rs[0-9]+$"))
             {
             printf("<BR>");
 	    printDbSnpRsUrl(row[3], "dbSNP (%s)", row[3]);
 	    }
 	}
     else if (regexMatch(itemName, "^rs[0-9]+$"))
         {
         printf("<BR>");
 	printDbSnpRsUrl(itemName, "dbSNP (%s)", itemName);
 	}
     }
 sqlFreeResult(&sr);
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void printGvAttrCatType (int i)
 /* prints new category and type labels for attributes as needed */
 {
 /* only print name and category if different */
 if (gvPrevCat == NULL)
     {
     /* print start of both */
     /* if need to print category layer, here is where print first */
     printf("<DT><B>%s:</B></DT><DD>\n", gvAttrTypeDisplay[i]);
     gvPrevCat = cloneString(gvAttrCategory[i]);
     gvPrevType = cloneString(gvAttrTypeDisplay[i]);
     }
 else if (differentString(gvPrevCat, gvAttrCategory[i]))
     {
     /* end last, and print start of both */
     printf("</DD>");
     /* if/when add category here is where to print next */
     printf("<DT><B>%s:</B></DT><DD>\n", gvAttrTypeDisplay[i]);
     freeMem(gvPrevType);
     gvPrevType = cloneString(gvAttrTypeDisplay[i]);
     freeMem(gvPrevCat);
     gvPrevCat = cloneString(gvAttrCategory[i]);
     }
 else if (sameString(gvPrevCat, gvAttrCategory[i]) &&
         differentString(gvPrevType, gvAttrTypeDisplay[i]))
     {
     /* print new name */
     printf("</DD>");
     printf("<DT><B>%s:</B></DT><DD>\n", gvAttrTypeDisplay[i]);
     freeMem(gvPrevType);
     gvPrevType = cloneString(gvAttrTypeDisplay[i]);
     }
 /* else don't need type or category */
 }
 
 void printLinksRaLink (char *acc, char *raKey, char *displayVal)
 /* print a link with instructions in hgcData/links.ra file */
 {
 struct hash *linkInstructions = NULL;
 struct hash *thisLink = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *linktype, *label;
 char *doubleEntry = NULL;
 
 hgReadRa(database, organism, rootDir, "links.ra", &linkInstructions);
 
 /* determine how to do link from .ra file */
 thisLink = hashFindVal(linkInstructions, raKey);
 if (thisLink == NULL)
     return; /* no link found */
 /* type determined by fields: url = external, dataSql = internal, others added later? */
 /* need to print header here for some displays */
 linktype = hashFindVal(thisLink, "dataSql");
 label = hashFindVal(thisLink, "label");
 if (label == NULL)
     label = "";
 if (linktype != NULL)
     {
     sqlSafef(query, sizeof(query), linktype, acc);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         /* should this print more than 1 column, get count from ra? */
         if (row[0] != NULL)
             {
             /* print label and result */
             printf("<B>%s</B> - %s", label, row[0]);
             /* check for link */
             doubleEntry = hashFindVal(thisLink, "dataLink");
             if (doubleEntry != NULL)
                 {
                 char url[512];
                 struct hash *newLink;
                 char *accCol = NULL, *format = NULL;
                 int colNum = 1;
                 newLink = hashFindVal(linkInstructions, doubleEntry);
                 accCol = hashFindVal(thisLink, "dataLinkCol");
                 if (newLink == NULL || accCol == NULL)
                    errAbort("missing required fields in .ra file");
                 colNum = atoi(accCol);
                 format = hashFindVal(newLink, "url");
                 safef(url, sizeof(url), format, row[colNum - 1]);
                 printf(" - <A HREF=\"%s\" TARGET=_blank>%s</A>\n",
                     url, row[colNum - 1]);
                 }
             printf("<BR>\n");
             }
         }
     sqlFreeResult(&sr);
     }
 else
     {
     linktype = hashFindVal(thisLink, "url");
     if (linktype != NULL)
         {
         char url[512];
         char *encodedAcc = cgiEncode(acc);
         char *encode = hashFindVal(thisLink, "needsEncoded");
         if (encode != NULL && sameString(encode, "yes"))
             safef(url, sizeof(url), linktype, encodedAcc);
         else
             safef(url, sizeof(url), linktype, acc);
         if (displayVal == NULL || sameString(displayVal, ""))
             printf("<B>%s</B> - <A HREF=\"%s\" TARGET=_blank>%s</A><BR>\n", label, url, acc);
         else
             printf("<B>%s</B> - <A HREF=\"%s\" TARGET=_blank>%s</A><BR>\n", label, url, displayVal);
         }
     }
 hFreeConn(&conn);
 return;
 }
 
 int printProtVarLink (char *id, int i)
 {
 struct protVarLink *link = NULL;
 struct hash *linkInstructions = NULL;
 struct hash *thisLink = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *linktype, *label;
 char *doubleEntry = NULL;
 int attrCnt = 0;
 
 hgReadRa(database, organism, rootDir, "links.ra", &linkInstructions);
 sqlSafef(query, sizeof(query),
      "select * from protVarLink where id = '%s' and attrType = '%s'",
      id, gvAttrTypeKey[i]);
 /* attrType == gvAttrTypeKey should be quote safe */
 
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     struct sqlResult *sr2;
     char **row2;
 
     attrCnt++;
     link = protVarLinkLoad(row);
     /* determine how to do link from .ra file */
     thisLink = hashFindVal(linkInstructions, link->raKey);
     if (thisLink == NULL)
         continue; /* no link found */
     /* type determined by fields: url = external, dataSql = internal, others added later? */
     printGvAttrCatType(i); /* only print header if data */
     linktype = hashFindVal(thisLink, "dataSql");
     label = hashFindVal(thisLink, "label");
     if (label == NULL)
         label = "";
     if (linktype != NULL)
         {
         sqlSafef(query, sizeof(query), linktype, link->acc);
         sr2 = sqlGetResult(conn2, query);
         while ((row2 = sqlNextRow(sr2)) != NULL)
             {
             /* should this print more than 1 column, get count from ra? */
             if (row2[0] != NULL)
                 {
                 /* print label and result */
                 printf("<B>%s</B> - %s", label, row2[0]);
                 /* check for link */
                 doubleEntry = hashFindVal(thisLink, "dataLink");
                 if (doubleEntry != NULL)
                     {
                     char url[512];
                     struct hash *newLink;
                     char *accCol = NULL, *format = NULL;
                     int colNum = 1;
 	            newLink = hashFindVal(linkInstructions, doubleEntry);
                     accCol = hashFindVal(thisLink, "dataLinkCol");
                     if (newLink == NULL || accCol == NULL)
                        errAbort("missing required fields in .ra file");
                     colNum = atoi(accCol);
                     format = hashFindVal(newLink, "url");
                     safef(url, sizeof(url), format, row2[colNum - 1]);
                     printf(" - <A HREF=\"%s\" TARGET=_blank>%s</A>\n",
                         url, row2[colNum - 1]);
                     }
                 printf("<BR>\n");
                 }
             }
         sqlFreeResult(&sr2);
         }
     else
         {
         linktype = hashFindVal(thisLink, "url");
         if (linktype != NULL)
             {
             char url[1024];
             char *encodedAcc = cgiEncode(link->acc);
             char *encode = hashFindVal(thisLink, "needsEncoded");
             if (encode != NULL && sameString(encode, "yes"))
                 safef(url, sizeof(url), linktype, encodedAcc);
             else
                 safef(url, sizeof(url), linktype, link->acc);
             if (sameString(link->displayVal, ""))
                 printf("<B>%s</B> - <A HREF=\"%s\" TARGET=_blank>%s</A><BR>\n", label, url, link->acc);
             else
                 printf("<B>%s</B> - <A HREF=\"%s\" TARGET=_blank>%s</A><BR>\n", label, url, link->displayVal);
             }
         }
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 hFreeConn(&conn2);
 return attrCnt;
 }
 
 int printGvLink (char *id, int i)
 {
 struct gvLink *link = NULL;
 struct hash *linkInstructions = NULL;
 struct hash *thisLink = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlConnection *conn2 = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *linktype, *label;
 char *doubleEntry = NULL;
 int attrCnt = 0;
 
 hgReadRa(database, organism, rootDir, "links.ra", &linkInstructions);
 sqlSafef(query, sizeof(query),
      "select * from hgFixed.gvLink where id = '%s' and attrType = '%s'",
      id, gvAttrTypeKey[i]);
 /* attrType == gvAttrTypeKey should be quote safe */
 
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     struct sqlResult *sr2;
     char **row2;
 
     attrCnt++;
     link = gvLinkLoad(row);
     /* determine how to do link from .ra file */
     thisLink = hashFindVal(linkInstructions, link->raKey);
     if (thisLink == NULL)
         continue; /* no link found */
     /* type determined by fields: url = external, dataSql = internal, others added later? */
     printGvAttrCatType(i); /* only print header if data */
     linktype = hashFindVal(thisLink, "dataSql");
     label = hashFindVal(thisLink, "label");
     if (label == NULL)
         label = "";
     if (linktype != NULL)
         {
         sqlSafef(query, sizeof(query), linktype, link->acc);
         sr2 = sqlGetResult(conn2, query);
         while ((row2 = sqlNextRow(sr2)) != NULL)
             {
             /* should this print more than 1 column, get count from ra? */
             if (row2[0] != NULL)
                 {
                 /* print label and result */
                 printf("<B>%s</B> - %s", label, row2[0]);
                 /* check for link */
                 doubleEntry = hashFindVal(thisLink, "dataLink");
                 if (doubleEntry != NULL)
                     {
                     char url[512];
                     struct hash *newLink;
                     char *accCol = NULL, *format = NULL;
                     int colNum = 1;
 	            newLink = hashFindVal(linkInstructions, doubleEntry);
                     accCol = hashFindVal(thisLink, "dataLinkCol");
                     if (newLink == NULL || accCol == NULL)
                        errAbort("missing required fields in .ra file");
                     colNum = atoi(accCol);
                     format = hashFindVal(newLink, "url");
                     safef(url, sizeof(url), format, row2[colNum - 1]);
                     printf(" - <A HREF=\"%s\" TARGET=_blank>%s</A>\n",
                         url, row2[colNum - 1]);
                     }
                 printf("<BR>\n");
                 }
             }
         sqlFreeResult(&sr2);
         }
     else
         {
         linktype = hashFindVal(thisLink, "url");
         if (linktype != NULL)
             {
             char url[1024];
             char *encodedAcc = cgiEncode(link->acc);
             char *encode = hashFindVal(thisLink, "needsEncoded");
             if (encode != NULL && sameString(encode, "yes"))
                 safef(url, sizeof(url), linktype, encodedAcc);
             else
                 safef(url, sizeof(url), linktype, link->acc);
             /* bounce srcLinks through PSU first for disclaimers */
             if (sameString(link->attrType, "srcLink"))
                 {
                 char *copy = cgiEncode(url);
                 safef(url, sizeof(url), "http://phencode.bx.psu.edu/cgi-bin/phencode/link-disclaimer?src=%s&link=%s", link->raKey, copy);
                 }
             if (sameString(link->displayVal, ""))
                 printf("<B>%s</B> - <A HREF=\"%s\" TARGET=_blank>%s</A><BR>\n", label, url, link->acc);
             else
                 printf("<B>%s</B> - <A HREF=\"%s\" TARGET=_blank>%s</A><BR>\n", label, url, link->displayVal);
             }
         }
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 hFreeConn(&conn2);
 return attrCnt;
 }
 
 void doOmicia(struct trackDb *tdb, char *itemName)
 /* this prints the detail page for the Omicia track */
 {
 struct omiciaLink *link = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 
 /* print generic bed start */
 doBed6FloatScore(tdb, itemName);
 
 /* print links */
 sqlSafef(query, sizeof(query), "select * from omiciaLink where id = '%s'", itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     link = omiciaLinkLoad(row);
     printLinksRaLink(link->acc, link->raKey, link->displayVal);
     }
 sqlFreeResult(&sr);
 
 printTrackHtml(tdb);
 }
 
 void doOmiciaOld (struct trackDb *tdb, char *itemName)
 /* this prints the detail page for the Omicia OMIM track */
 {
 char *table = tdb->table;
 struct omiciaLink *link = NULL;
 struct omiciaAttr *attr = NULL;
 void *omim = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 printf("<B>Name:</B> %s<BR>\n", itemName);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart = %d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     float score;
     struct omiciaAuto *om;
     if (sameString(table, "omiciaAuto"))
         omim = omiciaAutoLoad(row);
     else
         omim = omiciaHandLoad(row);
     om = (struct omiciaAuto *)omim;
     printPos(om->chrom, om->chromStart, om->chromEnd, om->strand, TRUE, om->name);
     /* print score separately, so can divide by 100 to retrieve original */
     score = (float)om->score / 100.00;
     printf("<B>Confidence score:</B> %g<BR>\n", score);
     }
 sqlFreeResult(&sr);
 
 /* print links */
 sqlSafef(query, sizeof(query), "select * from omiciaLink where id = '%s'", itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     link = omiciaLinkLoad(row);
     printLinksRaLink(link->acc, link->raKey, link->displayVal);
     }
 sqlFreeResult(&sr);
 
 /* print attributes */
 sqlSafef(query, sizeof(query), "select * from omiciaAttr where id = '%s' order by attrType", itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     attr = omiciaAttrLoad(row);
     /* start with simple case print label and value */
     printf("<B>%s:</B> %s<BR>\n", attr->attrType, attr->attrVal);
     }
 sqlFreeResult(&sr);
 
 printTrackHtml(tdb);
 }
 
 void doProtVar (struct trackDb *tdb, char *itemName)
 /* this prints the detail page for the UniProt variation track */
 {
 char *table = tdb->table;
 struct protVarPos *mut = NULL;
 struct protVar *details = NULL;
 struct protVarAttr attr;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int hasAttr = 0;
 int i;
 int start = cartInt(cart, "o");
 
 /* official name, position, band, genomic size */
 sqlSafef(query, sizeof(query), "select * from protVar where id = '%s'", itemName);
 details = protVarLoadByQuery(conn, query);
 
 genericHeader(tdb, details->name);
 
 /* change label based on species */
 if (sameString(organism, "Human"))
     printf("<B>HGVS name:</B> %s <BR>\n", details->name);
 else
     printf("<B>Official name:</B> %s <BR>\n", details->name);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     mut = protVarPosLoad(row);
     printPos(mut->chrom, mut->chromStart, mut->chromEnd, mut->strand, TRUE, mut->name);
     }
 sqlFreeResult(&sr);
 printf("*Note the DNA retrieved by the above link is the genomic sequence.<br>");
 
 /* print location and mutation type fields */
 printf("<B>location:</B> %s<BR>\n", details->location);
 printf("<B>type:</B> %s<BR>\n", details->baseChangeType);
 /* add note here about exactness of coordinates */
 if (details->coordinateAccuracy == 0)
     {
     printf("<B>note:</B> The coordinates for this mutation are only estimated.<BR>\n");
     }
 
 printf("<DL>");
 
 /* loop through attributes (uses same lists as gv*) */
 for(i=0; i<gvAttrSize; i++)
     {
     /* check 2 attribute tables for each type */
     sqlSafef(query, sizeof(query),
         "select * from protVarAttr where id = '%s' and attrType = '%s'",
         itemName, gvAttrTypeKey[i]);
     /* attrType == gvAttrTypeKey should be quote safe */
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         hasAttr++;
         protVarAttrStaticLoad(row, &attr);
         printGvAttrCatType(i); /* only print header, if data */
         /* print value */
         printf("%s<BR>", attr.attrVal);
         }
     sqlFreeResult(&sr);
     hasAttr += printProtVarLink(itemName, i);
     }
 if (hasAttr > 0)
     printf("</DD>");
 printf("</DL>\n");
 
 protVarPosFree(&mut);
 freeMem(gvPrevCat);
 freeMem(gvPrevType);
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doGv(struct trackDb *tdb, char *itemName)
 /* this prints the detail page for the Genome variation track */
 {
 char *table = tdb->table;
 struct gvPos *mut = NULL;
 struct gv *details = NULL;
 struct gvAttr attr;
 struct gvAttrLong attrLong;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 char query[256];
 int hasAttr = 0;
 int i;
 int start = cartInt(cart, "o");
 
 /* official name, position, band, genomic size */
 sqlSafef(query, sizeof(query), "select * from hgFixed.gv where id = '%s'", itemName);
 details = gvLoadByQuery(conn, query);
 
 genericHeader(tdb, details->name);
 
 /* change label based on species */
 if (sameString(organism, "Human"))
     printf("<B>HGVS name:</B> %s <BR>\n", details->name);
 else
     printf("<B>Official name:</B> %s <BR>\n", details->name);
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     char *strand = NULL;
     mut = gvPosLoad(row);
     strand = mut->strand;
     printPos(mut->chrom, mut->chromStart, mut->chromEnd, strand, TRUE, mut->name);
     }
 sqlFreeResult(&sr);
 if (mut == NULL)
     errAbort("Couldn't find variant %s at %s %d", itemName, seqName, start);
 printf("*Note the DNA retrieved by the above link is the genomic sequence.<br>");
 
 /* fetch and print the source */
 sqlSafef(query, sizeof(query),
       "select * from hgFixed.gvSrc where srcId = '%s'", details->srcId);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     struct gvSrc *src = gvSrcLoad(row);
     printf("<B>source:</B> %s", src->lsdb);
     printf("<BR>\n");
     }
 sqlFreeResult(&sr);
 
 /* print location and mutation type fields */
 printf("<B>location:</B> %s<BR>\n", details->location);
 printf("<B>type:</B> %s<BR>\n", details->baseChangeType);
 /* add note here about exactness of coordinates */
 if (details->coordinateAccuracy == 0)
     {
     printf("<B>note:</B> The coordinates for this mutation are only estimated.<BR>\n");
     }
 
 printf("<DL>");
 
 /* loop through attributes */
 for(i=0; i<gvAttrSize; i++)
     {
     /* check all 3 attribute tables for each type */
     sqlSafef(query, sizeof(query),
         "select * from hgFixed.gvAttrLong where id = '%s' and attrType = '%s'",
 	itemName, gvAttrTypeKey[i]);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         hasAttr++;
         gvAttrLongStaticLoad(row, &attrLong);
         printGvAttrCatType(i); /* only print header, if data */
         /* print value */
         printf("%s<BR>", attrLong.attrVal);
         }
     sqlFreeResult(&sr);
     sqlSafef(query, sizeof(query),
         "select * from hgFixed.gvAttr where id = '%s' and attrType = '%s'",
         itemName, gvAttrTypeKey[i]);
     /* attrType == gvAttrTypeKey should be quote safe */
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         hasAttr++;
         gvAttrStaticLoad(row, &attr);
         printGvAttrCatType(i); /* only print header, if data */
         /* print value */
         printf("%s<BR>", attr.attrVal);
         }
     sqlFreeResult(&sr);
     hasAttr += printGvLink(itemName, i);
     }
 if (hasAttr > 0)
     printf("</DD>");
 printf("</DL>\n");
 
 /* split code from printTrackHtml */
 printTBSchemaLink(tdb);
 printDataVersion(tdb);
 printOrigAssembly(tdb);
 printUpdateTime(database, tdb, NULL);
 if (tdb->html != NULL && tdb->html[0] != 0)
     {
     htmlHorizontalLine();
     puts(tdb->html);
     }
 hPrintf("<BR>\n");
 
 gvPosFree(&mut);
 freeMem(gvPrevCat);
 freeMem(gvPrevType);
 //printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doPgSnp(struct trackDb *tdb, char *itemName, struct customTrack *ct)
 /* print detail page for personal genome track (pgSnp) */
 {
 char *table;
 struct sqlConnection *conn;
 struct sqlResult *sr;
 char **row;
 char query[256];
 if (ct == NULL)
     {
     table = tdb->table;
     conn = hAllocConn(database);
     }
 else
     {
     table = ct->dbTableName;
     conn = hAllocConn(CUSTOM_TRASH);
     //ct->tdb
     }
 
 genericHeader(tdb, itemName);
 
 sqlSafef(query, sizeof(query), "select * from %s where name = '%s' and chrom = '%s' and chromStart = %d", 
     table, itemName, seqName, cartInt(cart, "o"));
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     struct pgSnp *el = pgSnpLoad(row);
     char *all[8];
     char *freq[8];
     char *score[8];
     char *name = cloneString(el->name);
     char *fr = NULL;
     char *sc = NULL;
     char *siftTab = trackDbSetting(tdb, "pgSiftPredTab");
     char *polyTab = trackDbSetting(tdb, "pgPolyphenPredTab");
     int i = 0;
     printPos(el->chrom, el->chromStart, el->chromEnd, "+", TRUE, el->name);
     printf("Alleles are relative to forward strand of reference genome:<br>\n");
     printf("<table class=\"descTbl\">"
 	   "<tr><th>Allele</th><th>Frequency</th><th>Quality Score</th></tr>\n");
     chopByChar(name, '/', all, el->alleleCount);
     if (differentString(el->alleleFreq, ""))
         {
         fr = cloneString(el->alleleFreq);
         chopByChar(fr, ',', freq, el->alleleCount);
         }
     if (el->alleleScores != NULL)
         {
         sc = cloneString(el->alleleScores);
         chopByChar(sc, ',', score, el->alleleCount);
         }
     for (i=0; i < el->alleleCount; i++)
         {
         if (sameString(el->alleleFreq, "") || sameString(freq[i], "0"))
             freq[i] = "not available";
         if (sc == NULL || sameString(sc, ""))
             score[i] = "not available";
         printf("<tr><td>%s</td><td>%s</td><td>%s</td></tr>", all[i], freq[i], score[i]);
         }
     printf("</table>");
     printPgDbLink(database, tdb, el);
     if (siftTab != NULL)
         printPgSiftPred(database, siftTab, el);
     if (polyTab != NULL)
         printPgPolyphenPred(database, polyTab, el);
     char *genePredTable = "knownGene";
     printSeqCodDisplay(database, el, genePredTable);
     }
 sqlFreeResult(&sr);
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doPgPhenoAssoc(struct trackDb *tdb, char *itemName)
 {
 char *table = tdb->table;
 struct pgPhenoAssoc *pheno = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 struct dyString *query = dyStringNew(512);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 
 sqlDyStringPrintf(query, "select * from %s where chrom = '%s' and ",
                table, seqName);
 sqlDyStringPrintf(query, "name = '%s' and chromStart = %d", itemName, start);
 sr = sqlGetResult(conn, query->string);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     pheno = pgPhenoAssocLoad(row);
     bedPrintPos((struct bed *)pheno, 4, tdb);
     printf("Personal Genome phenotype: <a href=\"%s\">link to phenotype source</a><BR>\n", pheno->srcUrl);
     }
 printTrackHtml(tdb);
 }
 
 void doAllenBrain(struct trackDb *tdb, char *itemName)
 /* Put up page for Allen Brain Atlas. */
 {
 char *table = tdb->table;
 struct psl *pslList;
 int start = cartInt(cart, "o");
 struct sqlConnection *conn = hAllocConn(database);
 char *url, query[512];
 
 genericHeader(tdb, itemName);
 
 sqlSafef(query, sizeof(query),
         "select url from allenBrainUrl where name = '%s'", itemName);
 url = sqlQuickString(conn, query);
 printf("<H3><A HREF=\"%s\" target=_blank>", url);
 printf("Click here to open Allen Brain Atlas on this probe.</A></H3><BR>");
 
 pslList = getAlignments(conn, table, itemName);
 puts("<H3>Probe/Genome Alignments</H3>");
 printAlignments(pslList, start, "htcCdnaAli", table, itemName);
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doExaptedRepeats(struct trackDb *tdb, char *itemName)
 /* Respond to click on the exaptedRepeats track. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 struct sqlResult *sr;
 char **row;
 char *chr, *name;
 unsigned int chromStart, chromEnd;
 boolean blastzAln;
 
 cartWebStart(cart, database, "%s", itemName);
 sqlSafef(query, sizeof query, "select * from %s where name = '%s'", tdb->table, itemName);
 selectOneRow(conn, tdb->table, query, &sr, &row);
 chr = cloneString(row[0]);
 chromStart = sqlUnsigned(row[1]);
 chromEnd = sqlUnsigned(row[2]);
 name = cloneString(row[3]);
 blastzAln = (sqlUnsigned(row[4])==1);
 
 printPos(chr, chromStart, chromEnd, NULL, TRUE, name);
 printf("<B>Item:</B> %s<BR>\n", name);
 if(blastzAln){printf("<B>Alignment to the repeat consensus verified with blastz:</B> yes<BR>\n");}
 else{printf("<B>Alignment to repeat consensus verified with blastz:</B> no<BR>\n");}
 
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 printTrackHtml(tdb);
 }
 
 void doIgtc(struct trackDb *tdb, char *itemName)
 /* Details for International Gene Trap Consortium. */
 {
 char *name = cloneString(itemName);
 char *source = NULL;
 char *encodedName = cgiEncode(itemName);
 
 cgiDecode(name, name, strlen(name));
 source = strrchr(name, '_');
 if (source == NULL)
     source = "Unknown";
 else
     source++;
 
 genericHeader(tdb, name);
 printf("<B>Source:</B> %s<BR>\n", source);
 printCustomUrl(tdb, name, TRUE);
 if (startsWith("psl", tdb->type))
     {
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr = NULL;
     struct dyString *query = dyStringNew(512);
     char **row = NULL;
     int rowOffset = hOffsetPastBin(database, seqName, tdb->table);
     int start = cartInt(cart, "o");
     int end = cartInt(cart, "t");
     sqlDyStringPrintf(query, "select * from %s where tName = '%s' and ",
 		   tdb->table, seqName);
     if (rowOffset)
 	hAddBinToQuery(start, end, query);
     sqlDyStringPrintf(query, "tStart = %d and qName = '%s'", start, itemName);
     sr = sqlGetResult(conn, query->string);
     if ((row = sqlNextRow(sr)) != NULL)
 	{
 	struct psl *psl = pslLoad(row+rowOffset);
 	printPos(psl->tName, psl->tStart, psl->tEnd, psl->strand, TRUE,
 		 psl->qName);
 	if (hGenBankHaveSeq(database, itemName, NULL))
 	    {
 	    printf("<H3>%s/Genomic Alignments</H3>", name);
 	    printAlignments(psl, start, "htcCdnaAli", tdb->table,
 			    encodedName);
 	    }
 	else
 	    {
 	    printf("<B>Alignment details:</B>\n");
 	    pslDumpHtml(psl);
 	    }
 	pslFree(&psl);
 	}
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 else
     warn("Unsupported type \"%s\" for IGTC (expecting psl).", tdb->type);
 printTrackHtml(tdb);
 }
 
 void doRdmr(struct trackDb *tdb, char *item)
 /* details page for rdmr track */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[512];
 struct sqlResult *sr;
 char **row;
 int ii;
 
 char *chrom,*chromStart,*chromEnd,*fibroblast,*iPS,*absArea,*gene,*dist2gene,*relation2gene,*dist2island,*relation2island,*fdr;
 
 genericHeader(tdb, item);
 
 sqlSafef(query, sizeof(query),
 "select chrom,chromStart,chromEnd,fibroblast,iPS,absArea,gene,dist2gene,relation2gene,dist2island,relation2island,fdr from rdmrRaw where gene = '%s'",
 item);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 
     ii = 0;
 chrom       = row[ii];ii++;
 chromStart  = row[ii];ii++;
 chromEnd    = row[ii];ii++;
 fibroblast  = row[ii];ii++;
 iPS         = row[ii];ii++;
 absArea     = row[ii];ii++;
 gene        = row[ii];ii++;
     dist2gene	= row[ii];ii++;
     relation2gene = row[ii];ii++;
     dist2island	= row[ii];ii++;
     relation2island = row[ii];ii++;
     fdr		= row[ii];
 
     printf("<B>Closest Gene:</B> %s\n", gene);fflush(stdout);
     printf("<BR><B>Genomic Position:</B> %s:%s-%s", chrom, chromStart, chromEnd);
 
     printf("<BR><B>Fibroblast M value:</B> %s\n", fibroblast);
     printf("<BR><B>iPS M value:</B> %s\n", iPS);
     printf("<BR><B>Absolute area:</B> %s", absArea);
     printf("<BR><B>Distance to gene:</B> %s\n", dist2gene);
     printf("<BR><B>Relation to gene:</B> %s\n", relation2gene);
     printf("<BR><B>Distance to CGI:</B> %s\n",  dist2island);
     printf("<BR><B>Relation to CGI:</B> %s\n", relation2island);
     printf("<BR><B>False discovery rate:</B> %s\n", fdr);
 sqlFreeResult(&sr);
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 void doKomp(struct trackDb *tdb, char *item)
 /* KnockOut Mouse Project */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[512];
 struct sqlResult *sr;
 char **row;
 genericHeader(tdb, item);
 char defaultExtra[HDB_MAX_TABLE_STRING];
 safef(defaultExtra, sizeof(defaultExtra), "%sExtra", tdb->table);
 char *extraTable = trackDbSettingOrDefault(tdb, "xrefTable", defaultExtra);
 boolean gotExtra = sqlTableExists(conn, extraTable);
 if (gotExtra)
     {
     char mgiId[256];
     sqlSafef(query, sizeof(query), "select alias from %s where name = '%s'",
 	  extraTable, item);
     sqlQuickQuery(conn, query, mgiId, sizeof(mgiId));
     char *ptr = strchr(mgiId, ',');
     if (!startsWith("MGI:", mgiId) || ptr == NULL)
 	errAbort("Where is the MGI ID?: '%s'", mgiId);
     else
 	*ptr = '\0';
     // Use the MGI ID to show all centers that are working on this gene:
     sqlSafef(query, sizeof(query), "select name,alias from %s where alias like '%s,%%'",
 	  extraTable, mgiId);
     sr = sqlGetResult(conn, query);
     char lastMgiId[16];
     lastMgiId[0] = '\0';
     puts("<TABLE BORDERWIDTH=0 CELLPADDING=0>");
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	char *words[3];
 	int wordCount = chopCommas(row[1], words);
 	if (wordCount >= 3)
 	    {
 	    char *mgiId = words[0], *center = words[1], *status = words[2];
 	    if (!sameString(mgiId, lastMgiId))
 		{
 		printf("<TR><TD colspan=2>");
 		printCustomUrl(tdb, mgiId, FALSE);
 		printf("</TD></TR>\n<TR><TD colspan=2>");
 		printOtherCustomUrl(tdb, mgiId, "mgiUrl", FALSE);
 		printf("</TD></TR>\n");
 		safecpy(lastMgiId, sizeof(lastMgiId), mgiId);
 		}
 	    printf("<TR><TD><B>Center: </B>%s</TD>\n", center);
 	    ptr = strrchr(row[0], '_');
 	    if (ptr != NULL)
 		printf("<TD><B>Design ID: </B>%s</TD>\n", ptr+1);
 	    printf("<TD><B>Status: </B>%s", status);
 	    if ((ptr != NULL) && (strstr(status, "vailable") != NULL))
 		{
 		char *productStr;
 		char *chp;
 		productStr = strdup(status);
 		chp = strstr(productStr, "vailable");
 		chp--;
 		chp--;
 		*chp = '\0';
 		printf(" (<A HREF=\"http://www.knockoutmouse.org/search_results?criteria=%s\" target=_blank>",
 		       ++ptr);
 		printf("order %s)", productStr);fflush(stdout);
 		}
 	    printf("</TD></TR>\n");
 	    }
 	}
     puts("<TR><TD colspan=2>");
     sqlFreeResult(&sr);
     }
 sqlSafef(query, sizeof(query), "select chrom,chromStart,chromEnd from %s "
       "where name = '%s'", tdb->table, item);
 sr = sqlGetResult(conn, query);
 char lastChr[32];
 int lastStart = -1;
 int lastEnd = -1;
 lastChr[0] = '\0';
 while ((row = sqlNextRow(sr)) != NULL)
     {
     char *chr = row[0];
     int start = atoi(row[1]), end = atoi(row[2]);
     if (!sameString(chr, lastChr) || start != lastStart || end != lastEnd)
 	printPos(chr, start, end, NULL, TRUE, item);
     safecpy(lastChr, sizeof(lastChr), chr);
     lastStart = start;
     lastEnd = end;
     }
 sqlFreeResult(&sr);
 if (gotExtra)
     puts("</TD></TR></TABLE>");
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doHgIkmc(struct trackDb *tdb, char *item)
 /* Human Genome Map of KnockOut Mouse Project */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[512];
 struct sqlResult *sr;
 char **row;
 genericHeader(tdb, item);
 char defaultExtra[HDB_MAX_TABLE_STRING];
 safef(defaultExtra, sizeof(defaultExtra), "%sExtra", tdb->table);
 char *extraTable = trackDbSettingOrDefault(tdb, "xrefTable", defaultExtra);
 boolean gotExtra = sqlTableExists(conn, extraTable);
 if (gotExtra)
     {
     char mgiId[256];
     char *designId;
 
     sqlSafef(query, sizeof(query), "select alias from %s where name = '%s'",
 	  extraTable, item);
     sqlQuickQuery(conn, query, mgiId, sizeof(mgiId));
     char *ptr = strchr(mgiId, ',');
     if (!startsWith("MGI:", mgiId) || ptr == NULL)
 	errAbort("Where is the MGI ID?: '%s'", mgiId);
     else
 	*ptr = '\0';
     ptr++;
     designId = ptr;
     ptr = strchr(ptr, ',');
     *ptr = '\0';
 
     // Show entries with the MGI ID and design ID
     sqlSafef(query, sizeof(query), "select name,alias from %s where alias like '%s,%s%%'",
           extraTable, mgiId, designId);
     sr = sqlGetResult(conn, query);
     char lastMgiId[16];
     lastMgiId[0] = '\0';
     puts("<TABLE BORDERWIDTH=0 CELLPADDING=0>");
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	char *words[4];
 	int wordCount = chopCommas(row[1], words);
 	if (wordCount >= 3)
 	    {
 	    char *mgiId = words[0], *center = words[2], *status = words[3];
 	    if (!sameString(mgiId, lastMgiId))
 		{
 		printf("<TR><TD colspan=2>");
 		printCustomUrl(tdb, mgiId, FALSE);
 		printf("</TD></TR>\n<TR><TD colspan=2>");
 		printOtherCustomUrl(tdb, mgiId, "mgiUrl", FALSE);
 		printf("</TD></TR>\n");
 		safecpy(lastMgiId, sizeof(lastMgiId), mgiId);
 		}
 	    printf("<TR><TD><B>Center: </B>%s</TD>\n", center);
 	    ptr = strrchr(row[0], '_');
 	    if (ptr != NULL)
 		printf("<TD><B>Design ID: </B>%s</TD>\n", ptr+1);
 	    printf("<TD><B>Status: </B>%s", status);
 	    if ((ptr != NULL) && (strstr(status, "vailable") != NULL))
 		{
 		char *productStr;
 		char *chp;
 		productStr = strdup(status);
 		chp = strstr(productStr, "vailable");
 		chp--;
 		chp--;
 		*chp = '\0';
 		printf(" (<A HREF=\"http://www.komp.org/geneinfo.php?project=%s\" target=_blank>",
 		       ++ptr);
 		printf("order %s)", productStr);fflush(stdout);
 		}
 	    printf("</TD></TR>\n");
 	    }
 	}
     puts("<TR><TD colspan=2>");
     sqlFreeResult(&sr);
     }
 sqlSafef(query, sizeof(query), "select chrom,chromStart,chromEnd from %s "
       "where name = '%s'", tdb->table, item);
 sr = sqlGetResult(conn, query);
 char lastChr[32];
 int lastStart = -1;
 int lastEnd = -1;
 lastChr[0] = '\0';
 while ((row = sqlNextRow(sr)) != NULL)
     {
     char *chr = row[0];
     int start = atoi(row[1]), end = atoi(row[2]);
     if (!sameString(chr, lastChr) || start != lastStart || end != lastEnd)
 	printPos(chr, start, end, NULL, TRUE, item);
     safecpy(lastChr, sizeof(lastChr), chr);
     lastStart = start;
     lastEnd = end;
     }
 sqlFreeResult(&sr);
 if (gotExtra)
     puts("</TD></TR></TABLE>");
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 void doUCSFDemo(struct trackDb *tdb, char *item)
 {
 genericHeader(tdb, item);
 
 printf("<B>Name:</B> %s<BR>\n", item);
 
 /* this prints the detail page for the clinical information for Cancer Demo datasets */
 char *table = tdb->table;
 char *cliniTable=NULL, *key=NULL;
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 
 if (sameString(table, "CGHBreastCancerUCSF") || sameString(table, "expBreastCancerUCSF"))
     {
     cliniTable = "phenBreastTumors";
     key = "id";
 
     /* er, pr */
     printf("<BR>");
     printf("<TABLE BORDER=1>\n");
     printf("<TR><TH>ER</TH> <TH>PR</TH></TR>\n");
     sqlSafef(query, sizeof(query), "select er, pr from %s where %s = '%s' ", cliniTable, key, item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         printf("<TR>");
         printf("<TD>%s</TD>", row[0]);
         printf("<TD>%s</TD>", row[1]);
         printf("</TR>");
 	}
     printf("</TABLE>\n");
     sqlFreeResult(&sr);
 
     /* subEuc, subCor */
     printf("<BR>");
     printf("<TABLE BORDER=1>\n");
     printf("<TR><TH>subEuc</TH> <TH>subCor</TH></TR>\n");
     sqlSafef(query, sizeof(query), "select subEuc, subCor from %s where %s = '%s' ", cliniTable, key, item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         printf("<TR>");
         printf("<TD>%s</TD>", row[0]);
         printf("<TD>%s</TD>", row[1]);
         printf("</TR>");
 	}
     printf("</TABLE>\n");
     sqlFreeResult(&sr);
 
     /* subtypes */
     printf("<BR>");
     printf("<TABLE BORDER=1>\n");
     printf("<TR><TH>subtype2</TH> <TH>subtype3</TH> <TH>subtype4</TH> <TH>subtype5</TH></TR>\n");
     sqlSafef(query, sizeof(query),
           "select subtype2, subtype3, subtype4, subtype5 from %s where %s = '%s' ",
           cliniTable, key, item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         printf("<TR>");
         printf("<TD>%s</TD>", row[0]);
         printf("<TD>%s</TD>", row[1]);
         printf("<TD>%s</TD>", row[2]);
         printf("<TD>%s</TD>", row[3]);
         printf("</TR>");
 	}
     printf("</TABLE>\n");
     sqlFreeResult(&sr);
 
     /* stage, size, nodalStatus, SBRGrade */
     printf("<BR>");
     printf("<TABLE BORDER=1>\n");
     printf("<TR><TH>Stage</TH> <TH>Size</TH> <TH>Nodal status</TH> <TH>SBR Grade</TH></TR>\n");
     sqlSafef(query, sizeof(query),
         "select stage, size, nodalStatus, SBRGrade from %s where %s = '%s' ", cliniTable, key, item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         printf("<TR>");
         printf("<TD>%s</TD>", row[0]);
         printf("<TD>%s</TD>", row[1]);
         printf("<TD>%s</TD>", row[2]);
         printf("<TD>%s</TD>", row[3]);
         printf("</TR>");
 	}
     printf("</TABLE>\n");
     sqlFreeResult(&sr);
 
     /* race, familyHistory, ageDx */
     printf("<BR>");
     printf("<TABLE BORDER=1>\n");
     printf("<TR><TH>Race</TH> <TH>Family history</TH> <TH>Age of Diagnosis</TH> </TR>\n");
     sqlSafef(query, sizeof(query),
         "select race, familyHistory, ageDx from %s where %s = '%s' ", cliniTable, key, item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         printf("<TR>");
         printf("<TD>%s</TD>", row[0]);
         printf("<TD>%s</TD>", row[1]);
         printf("<TD>%s</TD>", row[2]);
         printf("</TR>");
 	}
     printf("</TABLE>\n");
     sqlFreeResult(&sr);
 
 
     /* rad, chemo, horm, erb, p53, ki67 */
     printf("<BR>");
     printf("<TABLE BORDER=1>\n");
     printf("<TR><TH>Rad</TH> <TH>Chemo</TH> <TH>Horm</TH> <TH>ERB</TH> <TH>p53</TH>");
     printf("<TH>ki67</TH></TR>\n");
     sqlSafef(query, sizeof(query),
         "select rad, chemo, horm, erb, p53, ki67 from %s where %s = '%s' ", cliniTable, key, item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         printf("<TR>");
         printf("<TD>%s</TD>", row[0]);
         printf("<TD>%s</TD>", row[1]);
         printf("<TD>%s</TD>", row[2]);
         printf("<TD>%s</TD>", row[3]);
         printf("<TD>%s</TD>", row[4]);
         printf("<TD>%s</TD>", row[5]);
         printf("</TR>");
 	}
     printf("</TABLE>\n");
     sqlFreeResult(&sr);
 
     /* T/N/M */
     printf("<BR>");
     printf("<TABLE BORDER=1>\n");
     printf("<TR><TH>T</TH> <TH>N</TH> <TH>M</TH></TR>\n");
     sqlSafef(query, sizeof(query), "select T, N, M from %s where %s = '%s' ", cliniTable, key, item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         printf("<TR>");
         printf("<TD>%s</TD>", row[0]);
         printf("<TD>%s</TD>", row[1]);
         printf("<TD>%s</TD>", row[2]);
         printf("</TR>");
 	}
     printf("</TABLE>\n");
     sqlFreeResult(&sr);
 
     /* times */
     printf("<BR><B>Times:</B><BR>\n");
     printf("<TABLE BORDER=1>\n");
     printf("<TR><TH>Type</TH> <TH>Binary</TH> <TH>Value</TH></TR>\n");
     sqlSafef(query, sizeof(query),
           "select overallBinary, overallTime, diseaseBinary, diseaseTime, "
           "allrecBinary, allrecTime, distrecBinary, distrecTime from %s where %s = '%s' ",
           cliniTable, key, item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         printf("<TR><TD>Overall</TD> <TD>%s</TD> <TD>%s</TD></TR>", row[0], row[1]);
         printf("<TR><TD>Disease</TD> <TD>%s</TD> <TD>%s</TD></TR>", row[2], row[3]);
         printf("<TR><TD>Allrec</TD> <TD>%s</TD> <TD>%s</TD></TR>", row[4], row[5]);
         printf("<TR><TD>Distrec</TD> <TD>%s</TD> <TD>%s</TD></TR>", row[6], row[7]);
 	}
     printf("</TABLE>\n");
     sqlFreeResult(&sr);
 
     /* affyChipId */
     printf("<BR>");
     sqlSafef(query, sizeof(query), "select affyChipId from %s where %s = '%s' ", cliniTable, key, item);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         printf("<B>Affy Chip ID:</B> %s\n", row[0]);
 	}
     printf("</TABLE>\n");
     sqlFreeResult(&sr);
 
     return;
     }
 else if ( sameString(table, "cnvLungBroadv2"))
     {
     cliniTable = "tspLungClinical";
     key = "tumorID";
     }
 else
     return;
 
 htmlHorizontalLine();
 
 sqlSafef(query, sizeof(query),
       "select * from %s where %s = '%s' ", cliniTable, key,item);
 
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     int numFields = sqlCountColumns(sr);
     int i;
     char *fieldName=NULL, *value=NULL;
     for (i=0; i< numFields; i++)
 	{
 	fieldName = sqlFieldName(sr);
 	value = row[i];
 	printf("%s: <B>%s</B><BR>\n", fieldName, value);
 	}
     }
 sqlFreeResult(&sr);
 //printTrackHtml(tdb);
 //hFreeConn
 }
 
 void doConsIndels(struct trackDb *tdb, char *item)
 /* Display details about items in the Indel-based Conservation track. */
 {
 struct dyString *dy = dyStringNew(1024);
 struct sqlConnection *conn = hAllocConn(database);
 struct itemConf *cf;
 char confTable[128];
 
 /* create name for confidence table containing posterior probability and
    false discovery rate (FDR). */
 safef(confTable, sizeof(confTable), "%sConf", tdb->table);
 
 if (sqlTableExists(conn, confTable))
     {
     /* print the posterior probability and FDR if available */
     struct sqlResult *sr;
     char query[256], **row;
 
     sqlSafef(query, sizeof(query),
           "select * from %s where id = '%s'", confTable, item);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
         cf = itemConfLoad(row);
         dyStringPrintf(dy, "<B>Posterior Probability:</B> %.4g<BR>\n", cf->probability);
         dyStringPrintf(dy, "<B>False Discovery Rate (FDR):</B> %.2f<BR>\n", cf->fdr);
         itemConfFree(&cf);
         }
     sqlFreeResult(&sr);
     }
 hFreeConn(&conn);
 genericClickHandlerPlus(tdb, item, NULL, dy->string);
 dyStringFree(&dy);
 }
 
 #define KIDD_EICHLER_DISC_PREFIX "kiddEichlerDisc"
 
 void printKiddEichlerNcbiLinks(struct trackDb *tdb, char *item)
 /* If we have a table that maps kiddEichler IDs to NCBI IDs, print links. */
 {
 char *ncbiAccXref = trackDbSetting(tdb, "ncbiAccXref");
 if (isNotEmpty(ncbiAccXref) && hTableExists(database, ncbiAccXref))
     {
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr;
     char **row;
     char *cloneName = cloneString(item);
     char *postUnderscore = strchr(cloneName, '_');
     char query[512];
     /* In kiddEichlerDiscG248, all clone names have a WIBR2-\w+_ prefix
      * before the G248\w+ clone name given in the files used to make this
      * table, e.g. WIBR2-1962P18_G248P85919H9,transchrm_chr4 -- skip that
      * prefix.  Then strip all kiddEichlerDisc* names' ,.* suffixes. */
     if (startsWith("WIBR2-", cloneName) && postUnderscore != NULL)
 	cloneName = postUnderscore+1;
     chopPrefixAt(cloneName, ',');
     sqlSafef(query, sizeof(query),
 	  "select cloneAcc, endF, endR from %s where name = '%s'",
 	  ncbiAccXref, cloneName);
     sr = sqlGetResult(conn, query);
     if ((row = sqlNextRow(sr)) != NULL)
 	{
 	if (isNotEmpty(row[0]))
 	    printf("<B>Clone Report and Sequence (NCBI Nucleotide): </B>"
 		   "<A HREF=\"%s\" TARGET=_BLANK>%s</A><BR>\n",
 		   getEntrezNucleotideUrl(row[0]), row[0]);
 	char *endUrlFormat = trackDbSetting(tdb, "pairedEndUrlFormat");
 	/* Truncate cloneName to get library name: ABC* are followed by _,
 	 * G248 are not. */
 	char *libId = cloneName;
 	if (startsWith("G248", libId))
 	    libId[strlen("G248")] = '\0';
 	else if (startsWith("ABC", libId))
 	    chopPrefixAt(libId, '_');
 	if (endUrlFormat && differentStringNullOk(row[1], "0"))
 	    {
 	    printf("<B>Forward End Read (NCBI Trace Archive): </B>"
 		   "<A HREF=\"");
 	    printf(endUrlFormat, libId, row[1]);
 	    printf("\" TARGET=_BLANK>%s</A><BR>\n", row[1]);
 	    }
 	if (endUrlFormat && differentStringNullOk(row[2], "0"))
 	    {
 	    printf("<B>Reverse End Read (NCBI Trace Archive): </B>"
 		   "<A HREF=\"");
 	    printf(endUrlFormat, libId, row[2]);
 	    printf("\" TARGET=_BLANK>%s</A><BR>\n", row[2]);
 	    }
 	}
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 }
 
 void doKiddEichlerDisc(struct trackDb *tdb, char *item)
 /* Discordant clone end mappings from Kidd..Eichler 2008. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 char query[512];
 struct sqlResult *sr;
 char **row;
 boolean hasBin;
 struct bed *bed;
 boolean firstTime = TRUE;
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, item);
 if (! startsWith(KIDD_EICHLER_DISC_PREFIX, tdb->table))
     errAbort("track tableName must begin with "KIDD_EICHLER_DISC_PREFIX
 	     " but instead it is %s", tdb->table);
 hasBin = hOffsetPastBin(database, seqName, tdb->table);
 /* We don't need to add bin to this because name is indexed: */
 sqlSafef(query, sizeof(query), "select * from %s where name = '%s' "
 	       "and chrom = '%s' and chromStart = %d",
 	       tdb->table, item, seqName, start);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (firstTime)
 	firstTime = FALSE;
     else
 	htmlHorizontalLine();
     bed = bedLoadN(row+hasBin, 12);
     int lastBlk = bed->blockCount - 1;
     int endForUrl = (bed->chromStart + bed->chromStarts[lastBlk] +
 		     bed->blockSizes[lastBlk]);
     char *endFudge = trackDbSetting(tdb, "endFudge");
     if (endFudge && !strstr(bed->name, "OEA"))
 	endForUrl += atoi(endFudge);
     char sampleName[16];
     safecpy(sampleName, sizeof(sampleName),
 	    tdb->table + strlen(KIDD_EICHLER_DISC_PREFIX));
     touppers(sampleName);
     char itemPlus[2048];
     safef(itemPlus, sizeof(itemPlus),
 	  "%s&o=%d&t=%d&g=%s_discordant&%s_discordant=full",
 	  cgiEncode(item), start, endForUrl, sampleName, sampleName);
     printCustomUrlWithLabel(tdb, itemPlus, item, "url", FALSE);
     printKiddEichlerNcbiLinks(tdb, bed->name);
     printf("<B>Score:</B> %d<BR>\n", bed->score);
     printPosOnChrom(bed->chrom, bed->chromStart, bed->chromEnd, bed->strand,
 		    TRUE, bed->name);
     }
 sqlFreeResult(&sr);
 printTrackHtml(tdb);
 }
 
 void doBedDetail(struct trackDb *tdb, struct customTrack *ct, char *itemName)
 /* generate the detail page for a custom track of bedDetail type */
 {
 char *table;
 struct bedDetail *r = NULL;
 struct sqlConnection *conn;
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *chrom = cartString(cart,"c");  /* don't assume name is unique */
 int start = cgiInt("o");
 int end = cgiInt("t");
 int bedPart = 4;
 if (ct == NULL)
     {
     char *words[3];
     int cnt = chopLine(cloneString(tdb->type), words);
     if (cnt > 1)
         bedPart = atoi(words[1]) - 2;
     table = tdb->table;
     conn = hAllocConn(database);
     genericHeader(tdb, itemName);
     }
 else
     {
     table = ct->dbTableName;
     conn = hAllocConn(CUSTOM_TRASH);
     bedPart = ct->fieldCount - 2;
     /* header handled by custom track handler */
     }
 
 /* postion, band, genomic size */
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and chromStart = %d and chromEnd = %d and name = '%s'", 
 	table, chrom, start, end, itemName);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) != NULL)
     {
     r = bedDetailLoadWithGaps(row, bedPart+2);
     if (isNotEmpty(r->id))
         {
         printCustomUrl(tdb, r->id, TRUE);
         printf("<br>\n");
         }
 
     bedPrintPos((struct bed*)r, bedPart, tdb);
     printf("<br>");
     if (isNotEmpty(r->id) && !sameString("qPcrPrimers", table))
         printf("<B>ID:</B> %s <BR>\n", r->id);
 
     if  (isNotEmpty(r->description))
         printf("%s <BR>\n", r->description);
     }
 sqlFreeResult(&sr);
 /* do not print this for custom tracks, they do this later */
 if (ct == NULL)
     printTrackHtml(tdb);
 
 bedDetailFree(&r);
 hFreeConn(&conn);
 }
 
 struct trackDb *tdbForTableArg()
 /* get trackDb for track passed in table arg */
 {
 char *table = cartString(cart, "table");
 struct trackDb *tdb = hashFindVal(trackHash, table);
 if (tdb == NULL)
     errAbort("no trackDb entry for %s", table);
 return tdb;
 }
 
 void doQPCRPrimers(struct trackDb *tdb, char *itemName)
 /* Put up page for QPCRPrimers. */
 {
 genericHeader(tdb, itemName);
 doBedDetail(tdb, NULL, itemName);
 } /* end of doQPCRPrimers */
 
 void doSnakeClick(struct trackDb *tdb, char *itemName)
 /* Put up page for snakes. */
 {
 struct trackDb *parentTdb = trackDbTopLevelSelfOrParent(tdb);
 char *otherSpecies = trackHubSkipHubName(tdb->table) + strlen("snake");
 char *hubName = cloneString(database);
 char otherDb[4096];
 char *qName = cartOptionalString(cart, "qName");
 int qs = atoi(cartOptionalString(cart, "qs"));
 int qe = atoi(cartOptionalString(cart, "qe"));
 int qWidth = atoi(cartOptionalString(cart, "qWidth"));
 char *qTrack = cartString(cart, "g");
 if(isHubTrack(qTrack) && ! trackHubDatabase(database))
     hubName = cloneString(qTrack);
 
 struct hash *dbAliasHash = NULL;  // create later when needed
 char * dbAliasList = trackDbSetting(tdb, "dbAliasList");
 if (dbAliasList)
     dbAliasHash = hashFromString(dbAliasList);
 
 /* current mouse strain hal file has incorrect chrom names */
 char *aliasQName = qName;
 // aliasQName = "chr1";  // temporarily make this work for the mouse hal
 
 if(trackHubDatabase(database) || isHubTrack(qTrack))
     {
     char *ptr = strchr(hubName + 4, '_');
     *ptr = 0;
     safef(otherDb, sizeof otherDb, "%s_%s", hubName, otherSpecies);
     }
 else
     {
     safef(otherDb, sizeof otherDb, "%s", otherSpecies);
     }
 
 if (dbAliasHash)
    {
    char *otherDbName = trackHubSkipHubName(otherDb);
    struct hashEl* alias = hashLookup(dbAliasHash, otherDbName);
    if (alias)
       safef(otherDb, sizeof otherDb, "%s", (char *)alias->val);
    }
 
 char headerText[256];
 safef(headerText, sizeof headerText, "reference: %s, query: %s\n", trackHubSkipHubName(database), trackHubSkipHubName(otherDb) );
 genericHeader(parentTdb, headerText);
 
 printf("<A HREF=\"hgTracks?db=%s&position=%s:%d-%d&%s_snake%s=full\" TARGET=_BLANK>%s:%d-%d</A> link to block in query assembly: <B>%s</B></A><BR>\n", otherDb, aliasQName, qs, qe, hubName, trackHubSkipHubName(database), aliasQName, qs, qe, trackHubSkipHubName(otherDb));
 
 int qCenter = (qs + qe) / 2;
 int newQs = qCenter - qWidth/2;
 int newQe = qCenter + qWidth/2;
 printf("<A HREF=\"hgTracks?db=%s&position=%s:%d-%d&%s_snake%s=full\" TARGET=\"_blank\">%s:%d-%d</A> link to same window size in query assembly: <B>%s</B></A><BR>\n", otherDb, aliasQName, newQs, newQe,hubName, trackHubSkipHubName(database), aliasQName, newQs, newQe, trackHubSkipHubName(otherDb) );
 printTrackHtml(tdb);
 } 
 
 bool vsameWords(char *a, va_list args)
 /* returns true if a is sameWord as any arg, all args must be char*  */
 {
 bool found = FALSE;
 char *b;
 while ((b = va_arg(args, char *)) != NULL)
     {
     if (sameWord(a, b))
         {
         found = TRUE;
         break;
         }
     }
 return found;
 }
 
 bool sameAltWords(char *a, char *b, ...)
 /* returns true if a is sameWord as any of the variables or b is sameWord as any of them */
 {
 va_list args;
 va_start(args, b);
 bool res = vsameWords(a, args);
 va_end(args);
 
 if (!res && (b != NULL))
     {
     va_start(args, b);
     res = vsameWords(b, args);
     va_end(args);
     }
 return res;
 }
 
 bool sameWords(char *a, ...)
 /* returns true if a is equal to any b */
 {
 va_list args;
 va_start(args, a);
 bool res = vsameWords(a, args);
 va_end(args);
 return res;
 }
 
 void printAddWbr(char *text, int distance) 
 /* a crazy hack for firefox/mozilla that is unable to break long words in tables
  * We need to add a <wbr> tag every x characters in the text to make text breakable.
  */
 {
 int i;
 i = 0;
 char *c;
 c = text;
 bool doNotBreak = FALSE;
 while (*c != 0) 
     {
     if ((*c=='&') || (*c=='<'))
        doNotBreak = TRUE;
     if (*c==';' || (*c =='>'))
        doNotBreak = FALSE;
 
     printf("%c", *c);
     if (i % distance == 0 && ! doNotBreak) 
         printf("<wbr>");
     c++;
     i++;
     }
 }
 
 void doMiddle()
 /* Generate body of HTML. */
 {
 char *track = cartString(cart, "g");
 char *item = cloneString(cartOptionalString(cart, "i"));
 char *parentWigMaf = cartOptionalString(cart, "parentWigMaf");
 struct trackDb *tdb = NULL;
 
 
 if (hIsGisaidServer())
     {
     validateGisaidUser(cart);
     }
 
 /*	database and organism are global variables used in many places	*/
 getDbAndGenome(cart, &database, &genome, NULL);
 organism = hOrganism(database);
 scientificName = hScientificName(database);
 
 initGenbankTableNames(database);
 
 dbIsFound = trackHubDatabase(database) || sqlDatabaseExists(database);
 
 // Try to deal with virt chrom position used by hgTracks.
 // Hack the cart vars to set to a non virtual chrom mode position
 if (sameString("virt", cartString(cart, "c"))
  || sameString("getDna", cartUsualString(cart, "g", "")) )
     {
     char *nvPos = cartUsualString(cart, "nonVirtPosition", NULL);
     if (nvPos)
 	{
 	// parse non-virtual position 
 	char *pos = cloneString(nvPos);
 	char *colon = strchr(pos, ':');
 	if (!colon)
 	errAbort("position has no colon");
 	char *dash = strchr(pos, '-');
 	if (!dash)
 	errAbort("position has no dash");
 	*colon = 0;
 	*dash = 0;
 	char *chromName = cloneString(pos);
 	int winStart = atol(colon+1) - 1;
 	int winEnd = atol(dash+1);
 	cartSetString(cart, "position", nvPos);
 	cartSetString(cart, "c", chromName);
 	cartSetInt(cart, "l", winStart);
 	cartSetInt(cart, "r", winEnd);
 	}
     }
 
 
 if (dbIsFound)
     seqName = hgOfficialChromName(database, cartString(cart, "c"));
 else
     seqName = cartString(cart, "c");
 
 winStart = cartUsualInt(cart, "l", 0);
 winEnd = cartUsualInt(cart, "r", 0);
 
 /* Allow faked-out c=0 l=0 r=0 (e.g. for unaligned mRNAs) but not just any
  * old bogus position: */
 if (seqName == NULL)
     {
     if (winStart != 0 || winEnd != 0)
 	webAbort("CGI variable error",
 		 "hgc: bad input variables c=%s l=%d r=%d",
 		 cartString(cart, "c"), winStart, winEnd);
     else
 	seqName = hDefaultChrom(database);
     }
 
 struct customTrack *ct = NULL;
 if (isCustomTrack(track))
     {
     struct customTrack *ctList = getCtList();
     for (ct = ctList; ct != NULL; ct = ct->next)
         if (sameString(track, ct->tdb->track))
             break;
     }
 
 if ((!isCustomTrack(track) && dbIsFound)
 ||  ((ct!= NULL) && (((ct->dbTrackType != NULL) &&  sameString(ct->dbTrackType, "maf"))|| sameString(ct->tdb->type, "bigMaf"))))
     {
     trackHash = makeTrackHashWithComposites(database, seqName, TRUE);
     if (sameString("htcBigPslAli", track) || sameString("htcBigPslAliInWindow", track) )
 	{
 	char *aliTable = cartString(cart, "aliTable");
 	if (isHubTrack(aliTable))	
 	    tdb = hubConnectAddHubForTrackAndFindTdb( database, aliTable, NULL, trackHash);
 	}
     else if (isHubTrack(track))
 	{
 	tdb = hubConnectAddHubForTrackAndFindTdb( database, track, NULL, trackHash);
 	}
     if (parentWigMaf)
         {
         int wordCount, i;
         char *words[16];
         char *typeLine;
         char *wigType = needMem(128);
         tdb = hashFindVal(trackHash, parentWigMaf);
         if (!tdb)
             errAbort("can not find trackDb entry for parentWigMaf track %s.",
                     parentWigMaf);
         typeLine = cloneString(tdb->type);
         wordCount = chopLine(typeLine, words);
         if (wordCount < 1)
             errAbort("trackDb entry for parentWigMaf track %s has corrupt type line.",
                     parentWigMaf);
         safef(wigType, 128, "wig ");
         for (i = 1; i < wordCount; ++i)
             {
             strncat(wigType, words[i], 128 - strlen(wigType));
             strncat(wigType, " ", 128 - strlen(wigType));
             }
         strncat(wigType, "\n", 128 - strlen(wigType));
         tdb->type = wigType;
         tdb->track = cloneString(track);
         tdb->table = cloneString(track);
         freeMem(typeLine);
         cartRemove(cart, "parentWigMaf");	/* ONE TIME ONLY USE !!!	*/
         }
     else
 	{
         tdb = hashFindVal(trackHash, track);
 	if (tdb == NULL)
 	    {
             if (startsWith("all_mrna", track))
 		tdb = hashFindVal(trackHash, "mrna");
                   /* Oh what a tangled web we weave. */
 	    }
 	}
     }
 
 char* handler = trackDbSetting(tdb, "trackHandler");
 
 /* Start of 1000+ line dispatch on table involving 100+ if/elses. */
 char *table = (tdb ? tdb->table : track);
 if (sameWord(table, "getDna"))
     {
     doGetDna1();
     }
 else if (sameWord(table, "htcGetDna2"))
     {
     doGetDna2();
     }
 else if (sameWord(table, "htcGetDna3"))
     {
     doGetDna3();
     }
 else if (sameWord(table, "htcGetDnaExtended1"))
     {
     doGetDnaExtended1();
     }
 else if (sameWord(table, "htcListItemsAssayed"))
     {
     doPeakClusterListItemsAssayed();
     }
 
 /* Lowe Lab Stuff */
 #ifdef LOWELAB
  else if (loweLabClick(table, item, tdb))
    {
      /* do nothing, everything handled in loweLabClick */
    }
 #endif
 else if (sameWord(table, G_DELETE_WIKI_ITEM))
     {
     doDeleteWikiItem(item, seqName, winStart, winEnd);
     }
 else if (sameWord(table, G_ADD_WIKI_COMMENTS))
     {
     doAddWikiComments(item, seqName, winStart, winEnd);
     }
 else if (sameWord(table, G_CREATE_WIKI_ITEM))
     {
     doCreateWikiItem(item, seqName, winStart, winEnd);
     }
 else if (sameString(track, "variome"))
     doVariome(item, seqName, winStart, winEnd);
 else if (sameString(track, "variome.create"))
     doCreateVariomeItem(item, seqName, winStart, winEnd);
 else if (sameString(track, "variome.delete"))
     doDeleteVariomeItem(item, seqName, winStart, winEnd);
 else if (sameString(track, "variome.addComments"))
     doAddVariomeComments(item, seqName, winStart, winEnd);
 else if (startsWith("transMapAln", table))
     transMapClickHandler(tdb, item);
 else if (startsWith("hgcTransMapCdnaAli", table))
     {
     char *aliTable = cartString(cart, "aliTable");
     char *track = hGetTrackForTable(database, aliTable);
     tdb = hashMustFindVal(trackHash, track);
     transMapShowCdnaAli(tdb, item);
     }
 else if (sameWord(table, "mrna") || sameWord(table, "mrna2") ||
 	 startsWith("all_mrna",table) ||
 	 sameWord(table, "all_est") ||
 	 sameWord(table, "celeraMrna") ||
          sameWord(table, "est") || sameWord(table, "intronEst") ||
          sameWord(table, "xenoMrna") || sameWord(table, "xenoBestMrna") ||
          startsWith("mrnaBlastz",table ) || startsWith("mrnaBad",table ) ||
          sameWord(table, "xenoBlastzMrna") || sameWord(table, "sim4") ||
          sameWord(table, "xenoEst") || sameWord(table, "psu") ||
          sameWord(table, "tightMrna") || sameWord(table, "tightEst") ||
 	 sameWord(table, "blatzHg17KG") || sameWord(table, "mapHg17KG")
          )
     {
     doHgRna(tdb, item);
     }
 else if (startsWith("pseudoMrna",table ) || startsWith("pseudoGeneLink",table )
         || sameWord("pseudoUcsc",table))
     {
     doPseudoPsl(tdb, item);
     }
 else if (startsWith("retroMrna",table ) || startsWith("retroAugust",table )|| startsWith("retroCds",table )|| startsWith("ucscRetro",table ))
     {
     retroClickHandler(tdb, item);
     }
 else if (sameString(table, "hgcRetroCdnaAli"))
     retroShowCdnaAli(item);
 else if (sameWord(table, "affyU95")
 	|| sameWord(table, "affyU133")
 	|| sameWord(table, "affyU74")
 	|| sameWord(table, "affyRAE230")
 	|| sameWord(table, "affyZebrafish")
 	|| sameWord(table, "affyGnf1h")
 	|| sameWord(table, "affyMOE430v2")
 	|| sameWord(table, "affyGnf1m") )
     {
     doAffy(tdb, item, NULL);
     }
 else if (sameWord(table, WIKI_TRACK_TABLE))
     doWikiTrack(item, seqName, winStart, winEnd);
 else if (sameWord(table, OLIGO_MATCH_TRACK_NAME))
     doOligoMatch(item);
 else if (sameWord(table, "refFullAli"))
     {
     doTSS(tdb, item);
     }
 else if (sameWord(table, "rikenMrna"))
     {
     doRikenRna(tdb, item);
     }
 else if (sameWord(table, "cgapSage"))
     {
     doCgapSage(tdb, item);
     }
 else if (sameWord(table, "ctgPos") || sameWord(table, "ctgPos2"))
     {
     doHgContig(tdb, item);
     }
 else if (sameWord(table, "clonePos"))
     {
     doHgCover(tdb, item);
     }
 else if (sameWord(table, "bactigPos"))
     {
     doBactigPos(tdb, item);
     }
 else if (sameWord(table, "hgClone"))
     {
     tdb = hashFindVal(trackHash, "clonePos");
     doHgClone(tdb, item);
     }
 else if (sameWord(table, "gold"))
     {
     doHgGold(tdb, item);
     }
 else if (sameWord(table, "gap"))
     {
     doHgGap(tdb, item);
     }
 else if (sameWord(table, "tet_waba"))
     {
     doHgTet(tdb, item);
     }
 else if (sameWord(table, "wabaCbr"))
     {
     doHgCbr(tdb, item);
     }
 else if (startsWith("rmskJoined", table))
     {
     doJRepeat(tdb, item);
     }
 else if (startsWith("rmsk", table))
     {
     doHgRepeat(tdb, item);
     }
 else if (sameWord(table, "isochores"))
     {
     doHgIsochore(tdb, item);
     }
 else if (sameWord(table, "simpleRepeat"))
     {
     doSimpleRepeat(tdb, item);
     }
 else if (startsWith("cpgIsland", table))
     {
     doCpgIsland(tdb, item);
     }
 else if (sameWord(table, "illuminaProbes"))
     {
     doIlluminaProbes(tdb, item);
     }
 else if (sameWord(table, "htcIlluminaProbesAlign"))
     {
     htcIlluminaProbesAlign(item);
     }
 else if (sameWord(table, "switchDbTss"))
     {
     doSwitchDbTss(tdb, item);
     }
 else if (sameWord(table, "omimLocation"))
     {
     doOmimLocation(tdb, item);
     }
 else if (sameWord(table, "omimAvSnp"))
     {
     doOmimAvSnp(tdb, item);
     }
 else if (sameWord(table, "omimGeneClass2"))
     {
     doOmimGene2(tdb, item);
     }
 else if (sameWord(table, "omimGene2"))
     {
     doOmimGene2(tdb, item);
     }
 else if (sameWord(table, "omimAv"))
     {
     doOmimAv(tdb, item);
     }
 else if (sameWord(table, "rgdGene"))
     {
     doRgdGene(tdb, item);
     }
 else if (sameWord(table, "rgdGene2"))
     {
     doRgdGene2(tdb, item);
     }
 else if (sameWord(table, "rgdEst"))
     {
     doHgRna(tdb, item);
     }
 else if (sameWord(table, "rgdSslp"))
     {
     doRgdSslp(tdb, item, NULL);
     }
 else if (sameWord(table, "gad"))
     {
     doGad(tdb, item, NULL);
     }
 else if (sameWord(table, "decipher"))
     {
     doDecipher(tdb, item, NULL);
     }
 else if (sameWord(table, "omimGene"))
     {
     doOmimGene(tdb, item);
     }
 else if (sameWord(table, "rgdQtl") || sameWord(table, "rgdRatQtl"))
     {
     doRgdQtl(tdb, item);
     }
 else if (sameWord(table, "superfamily"))
     {
     doSuperfamily(tdb, item, NULL);
     }
 else if (sameWord(table, "ensGene") || sameWord (table, "ensGeneNonCoding"))
     {
     doEnsemblGene(tdb, item, NULL);
     }
 else if (sameWord(table, "xenoRefGene"))
     {
     doRefGene(tdb, item);
     }
 else if (sameWord(table, "knownGene"))
     {
     doKnownGene(tdb, item);
     }
 else if (sameWord(table, "ncbiRefSeq") ||
          sameWord(table, "ncbiRefSeqPsl") ||
          sameWord(table, "ncbiRefSeqCurated") ||
          sameWord(table, "ncbiRefSeqPredicted") ||
          sameWord(table, "ncbiRefSeqOther") )
     {
     doNcbiRefSeq(tdb, item);
     }
 else if (sameWord(table, "refGene") )
     {
     doRefGene(tdb, item);
     }
 else if (sameWord(table, "ccdsGene"))
     {
     doCcdsGene(tdb, item);
     }
 else if (isNewGencodeGene(tdb))
     {
     doGencodeGene(tdb, item);
     }
 else if (sameWord(table, "mappedRefSeq"))
     /* human refseqs on chimp browser */
     {
     doRefGene(tdb, item);
     }
 else if (sameWord(table, "mgcGenes") || sameWord(table, "mgcFullMrna"))
     {
     doMgcGenes(tdb, item);
     }
 else if (sameWord(table, "orfeomeGenes") || sameWord(table, "orfeomeMrna"))
     {
     doOrfeomeGenes(tdb, item);
     }
 else if (startsWith("viralProt", table))
     {
     doViralProt(tdb, item);
     }
 else if (sameWord("otherSARS", table))
     {
     doPslDetailed(tdb, item);
     }
 else if (sameWord(table, "softberryGene"))
     {
     doSoftberryPred(tdb, item);
     }
 else if (sameWord(table, "borkPseudo"))
     {
     doPseudoPred(tdb, item);
     }
 else if (sameWord(table, "borkPseudoBig"))
     {
     doPseudoPred(tdb, item);
     }
 else if (startsWith("encodePseudogene", table))
     {
     doEncodePseudoPred(tdb,item);
     }
 else if (sameWord(table, "sanger22"))
     {
     doSangerGene(tdb, item, "sanger22pep", "sanger22mrna", "sanger22extra");
     }
 else if (sameWord(table,"tRNAs"))
     {
     doTrnaGenesGb(tdb, item);
     }
 else if (sameWord(table, "sanger20"))
     {
     doSangerGene(tdb, item, "sanger20pep", "sanger20mrna", "sanger20extra");
     }
 else if (sameWord(table, "vegaGene") || sameWord(table, "vegaPseudoGene") )
     {
     doVegaGene(tdb, item, NULL);
     }
 else if ((sameWord(table, "vegaGene") || sameWord(table, "vegaPseudoGene")) && hTableExists(database, "vegaInfoZfish"))
     {
     doVegaGeneZfish(tdb, item);
     }
 else if (sameWord(table, "genomicDups"))
     {
     doGenomicDups(tdb, item);
     }
 else if (sameWord(table, "blatMouse") || sameWord(table, "bestMouse")
 	 || sameWord(table, "blastzTest") || sameWord(table, "blastzTest2"))
     {
     doBlatMouse(tdb, item);
     }
 else if (startsWith("multAlignWebb", table))
     {
     doMultAlignZoo(tdb, item, &table[13] );
     }
 else if (sameWord(table, "exaptedRepeats"))
     {
     doExaptedRepeats(tdb, item);
     }
 /*
   Generalized code to show strict chain blastz alignments in the zoo browsers
 */
 else if (containsStringNoCase(table, "blastzStrictChain")
          && containsStringNoCase(database, "zoo"))
     {
     int len = strlen("blastzStrictChain");
     char *orgName = &table[len];
     char dbName[32] = "zoo";
     strcpy(&dbName[3], orgName);
     len = strlen(orgName);
     strcpy(&dbName[3 + len], "3");
     longXenoPsl1(tdb, item, orgName, "chromInfo", dbName);
     }
  else if (sameWord(table, "blatChimp") ||
          sameWord(table, "chimpBac") ||
          sameWord(table, "bacChimp"))
     {
     longXenoPsl1Chimp(tdb, item, "Chimpanzee", "chromInfo", database);
     }
 else if (sameWord(table, "htcLongXenoPsl2"))
     {
     htcLongXenoPsl2(table, item);
     }
 else if (sameWord(table, "htcCdnaAliInWindow"))
     {
     htcCdnaAliInWindow(item);
     }
 else if (sameWord(table, "htcPseudoGene"))
     {
     htcPseudoGene(table, item);
     }
 else if (sameWord(table, "tfbsConsSites"))
     {
     tfbsConsSites(tdb, item);
     }
 else if (sameWord(table, "tfbsCons"))
     {
     tfbsCons(tdb, item);
     }
 else if (startsWith("atom", table) && !startsWith("atomMaf", table))
     {
     doAtom(tdb, item);
     }
 else if (sameWord(table, "firstEF"))
     {
     firstEF(tdb, item);
     }
 else if ( sameWord(table, "blastHg16KG") ||  sameWord(table, "blatHg16KG" ) ||
         startsWith("blastDm",  table) || sameWord(table, "blastMm6KG") ||
         sameWord(table, "blastSacCer1SG") || sameWord(table, "blastHg17KG") ||
         startsWith("blastCe", table) || sameWord(table, "blastHg18KG") )
     {
     blastProtein(tdb, item);
     }
 else if (startsWith("altSeqLiftOverPsl", table))
     {
     doPslAltSeq(tdb, item);
     }
 else if (sameWord(table, "chimpSimpleDiff"))
     {
     doSimpleDiff(tdb, "Chimp");
     }
 /* This is a catch-all for blastz/blat tracks -- any special cases must be
  * above this point! */
 else if (startsWith("map", table) ||startsWith("blastz", table) || startsWith("blat", table) || startsWith("tblast", table) || endsWith(table, "Blastz"))
     {
     char *genome = "Unknown";
     if (startsWith("tblast", table))
         genome = &table[6];
     if (startsWith("map", table))
         genome = &table[3];
     if (startsWith("blat", table))
         genome = &table[4];
     if (startsWith("blastz", table))
         genome = &table[6];
     else if (endsWith(table,"Blastz"))
         {
         genome = table;
         *strstr(genome, "Blastz") = 0;
         }
     if (hDbExists(genome))
         {
         /* handle table that include other database name
          * in trackname; e.g. blatCe1, blatCb1, blatCi1, blatHg15, blatMm3...
          * Uses genome column from database table as display text */
         genome = hGenome(genome);
         }
     doAlignCompGeno(tdb, item, genome);
     }
 else if (sameWord(table, "rnaGene"))
     {
     doRnaGene(tdb, item);
     }
 else if (sameWord(table, "RfamSeedFolds")
 	 || sameWord(table, "RfamFullFolds")
 	 || sameWord(table, "rfamTestFolds")
 	 || sameWord(table, "evofold")
 	 || sameWord(table, "evofoldV2")
 	 || sameWord(table, "evofoldRaw")
 	 || sameWord(table, "encode_tba23EvoFold")
 	 || sameWord(table, "encodeEvoFold")
 	 || sameWord(table, "rnafold")
 	 || sameWord(table, "rnaTestFolds")
 	 || sameWord(table, "rnaTestFoldsV2")
 	 || sameWord(table, "rnaTestFoldsV3")
 	 || sameWord(table, "mcFolds")
 	 || sameWord(table, "rnaEditFolds")
 	 || sameWord(table, "altSpliceFolds")
          || stringIn(table, "rnaSecStr"))
     {
     doRnaSecStr(tdb, item);
     }
 else if (sameWord(table, "fishClones"))
     {
     doFishClones(tdb, item);
     }
 else if (sameWord(table, "stsMarker"))
     {
     doStsMarker(tdb, item);
     }
 else if (sameWord(table, "stsMapMouse"))
     {
     doStsMapMouse(tdb, item);
     }
 else if (sameWord(table, "stsMapMouseNew")) /*steal map rat code for new mouse sts table. */
     {
     doStsMapMouseNew(tdb, item);
     }
 else if(sameWord(table, "stsMapRat"))
     {
     doStsMapRat(tdb, item);
     }
 else if (sameWord(table, "stsMap"))
     {
     doStsMarker(tdb, item);
     }
 else if (sameWord(table, "rhMap"))
     {
     doZfishRHmap(tdb, item);
     }
 else if (sameWord(table, "yaleBertoneTars"))
     {
     doYaleTars(tdb, item, NULL);
     }
 else if (sameWord(table, "recombRate"))
     {
     doRecombRate(tdb);
     }
 else if (sameWord(table, "recombRateRat"))
     {
     doRecombRateRat(tdb);
     }
 else if (sameWord(table, "recombRateMouse"))
     {
     doRecombRateMouse(tdb);
     }
 else if (sameWord(table, "genMapDb"))
     {
     doGenMapDb(tdb, item);
     }
 else if (sameWord(table, "mouseSynWhd"))
     {
     doMouseSynWhd(tdb, item);
     }
 else if (sameWord(table, "ensRatMusHom"))
     {
     doEnsPhusionBlast(tdb, item);
     }
 else if (sameWord(table, "mouseSyn"))
     {
     doMouseSyn(tdb, item);
     }
 else if (sameWord(table, "mouseOrtho"))
     {
     doMouseOrtho(tdb, item);
     }
 else if (sameWord(table, USER_PSL_TRACK_NAME))
     {
     doUserPsl(table, item);
     }
 else if (sameWord(table, PCR_RESULT_TRACK_NAME))
     {
     doPcrResult(table, item);
     }
 else if (sameWord(table, "softPromoter"))
     {
     hgSoftPromoter(table, item);
     }
 else if (isCustomTrack(table))
     {
     hgCustom(table, item);
     }
 else if (sameWord(table, "snpTsc") || sameWord(table, "snpNih") || sameWord(table, "snpMap"))
     {
     doSnpOld(tdb, item);
     }
 else if (sameWord(table, "snp"))
     {
     doSnp(tdb, item);
     }
 else if (snpVersion(table) >= 125)
     {
     doSnpWithVersion(tdb, item, snpVersion(table));
     }
 else if (sameWord(table, "cnpIafrate"))
     {
     doCnpIafrate(tdb, item);
     }
 else if (sameWord(table, "cnpLocke"))
     {
     doCnpLocke(tdb, item);
     }
 else if (sameWord(table, "cnpIafrate2"))
     {
     doCnpIafrate2(tdb, item);
     }
 else if (sameWord(table, "cnpSebat"))
     {
     doCnpSebat(tdb, item);
     }
 else if (sameWord(table, "cnpSebat2"))
     {
     doCnpSebat2(tdb, item);
     }
 else if (sameWord(table, "cnpSharp"))
     {
     doCnpSharp(tdb, item);
     }
 else if (sameWord(table, "cnpSharp2"))
     {
     doCnpSharp2(tdb, item);
     }
 else if (sameWord(table, "delHinds2"))
     {
     doDelHinds2(tdb, item);
     }
 else if (sameWord(table, "delConrad2"))
     {
     doDelConrad2(tdb, item);
     }
 else if (sameWord(table, "dgv") || sameWord(table, "dgvBeta"))
     {
     doDgv(tdb, item);
     }
 else if (sameWord(table, "dgvMerged") || sameWord(table, "dgvSupporting"))
     {
     doDgvPlus(tdb, item);
     }
 else if (sameWord(table, "affy120K"))
     {
     doAffy120K(tdb, item);
     }
 else if (sameWord(table, "affy10K"))
     {
     doAffy10K(tdb, item);
     }
 else if (sameWord(table, "uniGene_2") || sameWord(table, "uniGene"))
     {
     doSageDataDisp(table, item, tdb);
     }
 else if (sameWord(table, "uniGene_3"))
     {
     doUniGene3(tdb, item);
     }
 else if (sameWord(table, "vax003") || sameWord(table, "vax004"))
     {
     doVax003Vax004(tdb, item);
     }
 else if (sameWord(table, "tigrGeneIndex"))
     {
     doTigrGeneIndex(tdb, item);
     }
 //else if ((sameWord(table, "bacEndPairs")) || (sameWord(table, "bacEndPairsBad")) || (sameWord(table, "bacEndPairsLong")) || (sameWord(table, "bacEndSingles")))
     //{
     //doLinkedFeaturesSeries(table, item, tdb);
     //}
 else if (sameAltWords(table, handler, "bacEndPairs", "bacEndPairsBad", "bacEndPairsLong", "bacEndSingles", NULL))
     {
     doLinkedFeaturesSeries(table, item, tdb);
     }
 else if ((sameWord(table, "fosEndPairs")) || (sameWord(table, "fosEndPairsBad")) || (sameWord(table, "fosEndPairsLong")))
     {
     doLinkedFeaturesSeries(table, item, tdb);
     }
  else if ((sameWord(table, "earlyRep")) || (sameWord(table, "earlyRepBad")))
     {
     doLinkedFeaturesSeries(table, item, tdb);
     }
 else if (sameWord(table, "cgh"))
     {
     doCgh(table, item, tdb);
     }
 else if (sameWord(table, "mcnBreakpoints"))
     {
     doMcnBreakpoints(table, item, tdb);
     }
 else if (sameWord(table, "htcChainAli"))
     {
     htcChainAli(item);
     }
 else if (sameWord(table, "htcChainTransAli"))
     {
     htcChainTransAli(item);
     }
 else if (sameWord(table, "htcBigPslAliInWindow"))
     {
     htcBigPslAliInWindow(item);
     }
 else if (sameWord(table, "htcBigPslAli"))
     {
     htcBigPslAli(item);
     }
 else if (sameWord(table, "htcCdnaAli"))
     {
     htcCdnaAli(item);
     }
 else if (sameWord(table, "htcUserAli"))
     {
     htcUserAli(item);
     }
 else if (sameWord(table, "htcGetBlastPep"))
     {
     doGetBlastPep(item, cartString(cart, "aliTable"));
     }
 else if (sameWord(table, "htcProteinAli"))
     {
     htcProteinAli(item, cartString(cart, "aliTable"));
     }
 else if (sameWord(table, "htcBlatXeno"))
     {
     htcBlatXeno(item, cartString(cart, "aliTable"));
     }
 else if (sameWord(table, "htcExtSeq"))
     {
     htcExtSeq(item);
     }
 else if (sameWord(table, "htcTranslatedProtein"))
     {
     htcTranslatedProtein(item);
     }
 else if (sameWord(table, "htcTranslatedPredMRna"))
     {
     htcTranslatedPredMRna(item);
     }
 else if (sameWord(table, "htcTranslatedMRna"))
     {
     htcTranslatedMRna(tdbForTableArg(), item);
     }
 else if (sameWord(table, "ncbiRefSeqSequence"))
     {
     ncbiRefSeqSequence(item);
     }
 else if (sameWord(table, "htcGeneMrna"))
     {
     htcGeneMrna(item);
     }
 else if (sameWord(table, "htcRefMrna"))
     {
     htcRefMrna(item);
     }
 else if (sameWord(table, "htcDisplayMrna"))
     {
     htcDisplayMrna(item);
     }
 else if (sameWord(table, "htcGeneInGenome"))
     {
     htcGeneInGenome(item);
     }
 else if (sameWord(table, "htcDnaNearGene"))
     {
     htcDnaNearGene( item);
     }
 else if (sameWord(table, "getMsBedAll"))
     {
     getMsBedExpDetails(tdb, item, TRUE);
     }
 else if (sameWord(table, "getMsBedRange"))
     {
     getMsBedExpDetails(tdb, item, FALSE);
     }
 else if (sameWord(table, "perlegen"))
     {
     perlegenDetails(tdb, item);
     }
 else if (sameWord(table, "haplotype"))
     {
     haplotypeDetails(tdb, item);
     }
 else if (sameWord(table, "mitoSnps"))
     {
     mitoDetails(tdb, item);
     }
 else if (sameWord(table, "loweProbes"))
     {
     doProbeDetails(tdb, item);
     }
 else if (sameWord(table, "chicken13k"))
     {
     doChicken13kDetails(tdb, item);
     }
 else if( sameWord(table, "ancientR"))
     {
     ancientRDetails(tdb, item);
     }
 else if( sameWord(table, "gcPercent"))
     {
     doGcDetails(tdb, item);
     }
 else if (sameWord(table, "htcTrackHtml"))
     {
     htcTrackHtml(tdbForTableArg());
     }
 
 /*Evan's stuff*/
 else if (sameWord(table, "genomicSuperDups"))
     {
     doGenomicSuperDups(tdb, item);
     }
 else if (sameWord(table, "celeraDupPositive"))
     {
     doCeleraDupPositive(tdb, item);
     }
 
 else if (sameWord(table, "triangle") || sameWord(table, "triangleSelf") || sameWord(table, "transfacHit") )
     {
     doTriangle(tdb, item, "dnaMotif");
     }
 else if (sameWord(table, "esRegGeneToMotif"))
     {
     doTriangle(tdb, item, "esRegMotif");
     }
 else if (startsWith("wgEncodeRegTfbsClusteredMotifs", table))
     {
     doTriangle(tdb, item, "transRegCodeMotif");
     }
 else if (sameWord(table, "transRegCode"))
     {
     doTransRegCode(tdb, item, "transRegCodeMotif");
     }
 else if (sameWord(table, "transRegCodeProbe"))
     {
     doTransRegCodeProbe(tdb, item, "transRegCode", "transRegCodeMotif",
                         "transRegCodeCondition", "growthCondition");
     }
 else if (tdb != NULL && startsWith("wgEncodeRegDnaseClustered", tdb->track))
     {
     doPeakClusters(tdb, item);
     }
 
 else if( sameWord( table, "humMusL" ) || sameWord( table, "regpotent" ))
     {
     humMusClickHandler( tdb, item, "Mouse", "mm2", "blastzBestMouse", 0);
     }
 else if( sameWord( table, "musHumL" ))
     {
     humMusClickHandler( tdb, item, "Human", "hg12", "blastzBestHuman_08_30" , 0);
     }
 else if( sameWord( table, "mm3Rn2L" ))
     {
     humMusClickHandler( tdb, item, "Rat", "rn2", "blastzBestRat", 0 );
     }
 else if( sameWord( table, "hg15Mm3L" ))
     {
     humMusClickHandler( tdb, item, "Mouse", "mm3", "blastzBestMm3", 0 );
     }
 else if( sameWord( table, "mm3Hg15L" ))
     {
     humMusClickHandler( tdb, item, "Human", "hg15", "blastzNetHuman" , 0);
     }
 else if( sameWord( table, "footPrinter" ))
     {
     footPrinterClickHandler( tdb, item );
     }
 else if (sameWord(table, "jaxQTL3"))
     {
     doJaxQTL3(tdb, item);
     }
 else if (startsWith("jaxQTL", table) || startsWith("jaxQtl", table))
     {
     doJaxQTL(tdb, item);
     }
 else if (startsWith("jaxAllele", table) || startsWith("jaxGeneTrap", table))
     {
     doJaxAllele(tdb, item);
     }
 else if (startsWith("jaxPhenotype", table))
     {
     doJaxPhenotype(tdb, item);
     }
 else if (startsWith("jaxRepTranscript", table))
     {
     doJaxAliasGenePred(tdb, item);
     }
 else if (sameWord(table, "wgRna"))
     {
     doWgRna(tdb, item);
     }
 else if (sameWord(table, "ncRna"))
     {
     doNcRna(tdb, item);
     }
 else if (sameWord(table, "gbProtAnn"))
     {
     doGbProtAnn(tdb, item);
     }
 else if (sameWord(table, "h1n1Gene"))
     {
     doH1n1Gene(tdb, item);
     }
 else if (startsWith("h1n1_", table) || startsWith("h1n1b_", table))
     {
     doH1n1Seq(tdb, item);
     }
 else if (sameWord(table, "bdgpGene") || sameWord(table, "bdgpNonCoding"))
     {
     doBDGPGene(tdb, item);
     }
 else if (sameWord(table, "flyBaseGene") || sameWord(table, "flyBaseNoncoding"))
     {
     doFlyBaseGene(tdb, item);
     }
 else if (sameWord(table, "bgiGene"))
     {
     doBGIGene(tdb, item);
     }
 else if (sameWord(table, "bgiSnp"))
     {
     doBGISnp(tdb, item);
     }
 else if (sameWord(table, "encodeRna"))
     {
     doEncodeRna(tdb, item);
     }
 else if (sameWord(table, "encodeRegions"))
     {
     doEncodeRegion(tdb, item);
     }
 else if (sameWord(table, "encodeErgeHssCellLines"))
     {
     doEncodeErgeHssCellLines(tdb, item);
     }
 else if (sameWord(table, "encodeErge5race")   || sameWord(table, "encodeErgeInVitroFoot")  || \
          sameWord(table, "encodeErgeDNAseI")  || sameWord(table, "encodeErgeMethProm")     || \
          sameWord(table, "encodeErgeExpProm") || sameWord(table, "encodeErgeStableTransf") || \
          sameWord(table, "encodeErgeBinding") || sameWord(table, "encodeErgeTransTransf")  || \
          sameWord(table, "encodeErgeSummary"))
     {
     doEncodeErge(tdb, item);
     }
 else if(sameWord(table, "HInvGeneMrna"))
     {
     doHInvGenes(tdb, item);
     }
 else if(sameWord(table, "sgdClone"))
     {
     doSgdClone(tdb, item);
     }
 else if (sameWord(table, "sgdOther"))
     {
     doSgdOther(tdb, item);
     }
 else if (sameWord(table, "vntr"))
     {
     doVntr(tdb, item);
     }
 else if (sameWord(table, "luNega") || sameWord (table, "luPosi") || sameWord (table, "mRNARemains") || sameWord(table, "pseudoUcsc2"))
     {
     doPutaFrag(tdb, item);
     }
 else if (sameWord(table, "potentPsl") || sameWord(table, "rcntPsl") )
     {
     potentPslAlign(table, item);
     }
 else if (startsWith("zdobnov", table))
     {
     doZdobnovSynt(tdb, item);
     }
 else if (startsWith("deweySynt", table))
     {
     doDeweySynt(tdb, item);
     }
 else if (startsWith("eponine", table))
     {
     doBed6FloatScore(tdb, item);
     printTrackHtml(tdb);
     }
 else if (sameWord(organism, "fugu") && startsWith("ecores", table))
     {
     doScaffoldEcores(tdb, item);
     }
 else if (startsWith("pscreen", table))
     {
     doPscreen(tdb, item);
     }
 else if (startsWith("flyreg", table))
     {
     doFlyreg(tdb, item);
     }
 /* ENCODE table */
 else if (startsWith("encodeGencodeIntron", table) &&
 	 sameString(tdb->type, "bed 6 +"))
     {
     doGencodeIntron(tdb, item);
     }
 else if (sameWord(table, "encodeIndels"))
     {
     doEncodeIndels(tdb, item);
     }
 else if (startsWith("encodeStanfordPromoters", table))
     {
     doEncodeStanfordPromoters(tdb, item);
     }
 else if (startsWith("encodeStanfordRtPcr", table))
     {
     doEncodeStanfordRtPcr(tdb, item);
     }
 else if (startsWith("encodeHapMapAlleleFreq", table))
     {
     doEncodeHapMapAlleleFreq(tdb, item);
     }
 else if (sameString("cutters", table))
     {
     doCutters(item);
     }
 else if (sameString("anoEstTcl", table))
     {
     doAnoEstTcl(tdb, item);
     }
 else if (sameString("interPro", table))
     {
     doInterPro(tdb, item);
     }
 else if (sameString("dvBed", table))
     {
     doDv(tdb, item);
     }
 else if (startsWith("hapmapSnps", table) &&
 	 (strlen(table) == 13 ||
 	  (endsWith(table, "PhaseII") && strlen(table) == 20)))
     {
     doHapmapSnps(tdb, item);
     }
 else if (startsWith("hapmapAllelesChimp", table) ||
          startsWith("hapmapAllelesMacaque", table))
     {
     doHapmapOrthos(tdb, item);
     }
 else if (sameString("snpArrayAffy250Nsp", table) ||
          sameString("snpArrayAffy250Sty", table) ||
          sameString("snpArrayAffy5", table) ||
          sameString("snpArrayAffy6", table) ||
          sameString("snpArrayAffy10", table) ||
          sameString("snpArrayAffy10v2", table) ||
          sameString("snpArrayAffy50HindIII", table) ||
          sameString("snpArrayAffy50XbaI", table))
     {
     doSnpArray(tdb, item, "Affy");
     }
 else if (sameString("snpArrayIllumina300", table) ||
          sameString("snpArrayIllumina550", table) ||
 	 sameString("snpArrayIllumina650", table) ||
 	 (sameString("snpArrayIllumina1M", table) && startsWith("rs", item) ) ||
 	 (sameString("snpArrayIlluminaHuman660W_Quad", table) && startsWith("rs", item) ) ||
 	 (sameString("snpArrayIlluminaHumanOmni1_Quad", table) && startsWith("rs", item) ) ||
 	 (sameString("snpArrayIlluminaHumanCytoSNP_12", table) && startsWith("rs", item) ) )
     {
     doSnpArray(tdb, item, "Illumina");
     }
 else if (
 	 (sameString("snpArrayIlluminaHuman660W_Quad", table) && !startsWith("rs", item) ) ||
 	 (sameString("snpArrayIlluminaHumanOmni1_Quad", table) && !startsWith("rs", item) ) ||
 	 (sameString("snpArrayIlluminaHumanCytoSNP_12", table) && !startsWith("rs", item) ) )
     {
     /* special processing for non-dbSnp entries of the 3 new Illumina arrays */
     doSnpArray2(tdb, item, "Illumina");
     }
 else if (sameString("pgVenter", table) ||
          sameString("pgWatson", table) ||
          sameString("pgYri", table)    ||
          sameString("pgCeu", table)    ||
          sameString("pgChb", table)    ||
          sameString("pgJpt", table)    ||
 	 sameString("pgSjk", table)    ||
 	 startsWith("pgYoruban", table) ||
 	 (startsWith("pgNA", table) && isdigit(table[4])) ||
          sameString("hbPgTest", table) ||
          sameString("hbPgWild", table) ||
 	 sameString("pgYh1", table) ||
          sameString("pgKb1", table) ||
          sameString("pgNb1", table) || sameString("pgNb1Indel", table) ||
          sameString("pgTk1", table) || sameString("pgTk1Indel", table) ||
          sameString("pgMd8", table) || sameString("pgMd8Indel", table) ||
          sameString("pgKb1Illum", table) ||
          sameString("pgKb1454", table) || sameString("pgKb1Indel", table) ||
          sameString("pgKb1Comb", table) ||
          sameString("pgAbtSolid", table) ||
          sameString("pgAbt", table) || sameString("pgAbt454", table) ||
          sameString("pgAbt454indels", table) ||
          sameString("pgAbtIllum", table) ||
          sameString("pgAk1", table) ||
          sameString("pgQuake", table) ||
          sameString("pgIrish", table) ||
          sameString("pgSaqqaq", table) ||
          sameString("pgSaqqaqHc", table) ||
          sameString("pgTest", table) )
     {
     doPgSnp(tdb, item, NULL);
     }
 else if (startsWith("pg", table) &&
          (endsWith(table, "PhenCode") || endsWith(table, "Snpedia") || endsWith(table, "Hgmd")) )
     {
     doPgPhenoAssoc(tdb, item);
     }
 else if (sameString("gvPos", table))
     {
     doGv(tdb, item);
     }
 else if (sameString("protVarPos", table))
     {
     doProtVar(tdb, item);
     }
 else if (sameString("oreganno", table))
     {
     doOreganno(tdb, item);
     }
 else if (sameString("oregannoOther", table))
     {
     /* Regions from another species lifted and displayed in current */
     /* same table structure, just different table name/table */
     doOreganno(tdb, item);
     }
 else if (sameString("allenBrainAli", table))
     {
     doAllenBrain(tdb, item);
     }
 else if (sameString("dless", table) || sameString("encodeDless", table))
     {
     doDless(tdb, item);
     }
 else if (sameString("mammalPsg", table))
    {
      doMammalPsg(tdb, item);
    }
 else if (sameString("igtc", table))
     {
     doIgtc(tdb, item);
     }
 else if (sameString("rdmr", table))
     {
     doRdmr(tdb, item);
     }
 else if (startsWith("komp", table) || startsWith("ikmc", table))
     {
     doKomp(tdb, item);
     }
 else if (sameString("hgIkmc", table))
     {
     doHgIkmc(tdb, item);
     }
 else if (startsWith("dbRIP", table))
     {
     dbRIP(tdb, item, NULL);
     }
 else if (sameString("omicia", table)) //Auto", table) || sameString("omiciaHand", table))
     {
     doOmicia(tdb, item);
     }
 else if ( sameString("expRatioUCSFDemo", table) || sameString("cnvLungBroadv2", table) || sameString("CGHBreastCancerUCSF", table) || sameString("expBreastCancerUCSF", table))
     {
     doUCSFDemo(tdb, item);
     }
 else if (startsWith("consIndels", table))
     {
     doConsIndels(tdb,item);
     }
 else if (startsWith(KIDD_EICHLER_DISC_PREFIX, table))
     {
     doKiddEichlerDisc(tdb, item);
     }
 else if (startsWith("hgdpGeo", table))
     {
     doHgdpGeo(tdb, item);
     }
 else if (startsWith("gwasCatalog", table))
     {
     doGwasCatalog(tdb, item);
     }
 else if (sameString("par", table))
     {
     doParDetails(tdb, item);
     }
 else if (startsWith("pubs", table))
     {
     doPubsDetails(tdb, item);
     }
 else if (tdb != NULL && startsWith("bedDetail", tdb->type))
     {
     doBedDetail(tdb, NULL, item);
     }
 else if (startsWith("numtS", table))
     {
     doNumtS(tdb, item);
     }
 else if (startsWith("cosmic", table))
     {
     doCosmic(tdb, item);
     }
 else if (sameString("geneReviews", table))
     {
     doGeneReviews(tdb, item);
     }
 else if (startsWith("qPcrPrimers", table))
     {
     doQPCRPrimers(tdb, item);
     }
 else if (sameString("lrg", table))
     {
     doLrg(tdb, item);
     }
 else if (sameString("lrgTranscriptAli", table))
     {
     doLrgTranscriptPsl(tdb, item);
     }
 else if (sameWord(table, "htcLrgCdna"))
     {
     htcLrgCdna(item);
     }
 else if (startsWith("peptideAtlas", table))
     {
     doPeptideAtlas(tdb, item);
     }
 else if (startsWith("gtexGene", table))
     {
     doGtexGeneExpr(tdb, item);
     }
 else if (startsWith("snake", trackHubSkipHubName(table)))
     {
     doSnakeClick(tdb, item);
     }
 else if (tdb != NULL && startsWithWord("vcfTabix", tdb->type))
     {
     doVcfTabixDetails(tdb, item);
     }
 else if (tdb != NULL && startsWithWord("vcf", tdb->type))
     {
     doVcfDetails(tdb, item);
     }
 else if (tdb != NULL)
     {
     genericClickHandler(tdb, item, NULL);
     }
 else
     {
     cartWebStart(cart, database, "%s", track);
     warn("Sorry, clicking there doesn't do anything yet (%s).", track);
     }
 /* End of 1000+ line dispatch on table involving 100+ if/elses. */
 
 cartHtmlEnd();
 }
 
 struct hash *orgDbHash = NULL;
 
 void initOrgDbHash()
 /* Function to initialize a hash of organism names that hash to a database ID.
  * This is used to show alignments by hashing the organism associated with the
  * track to the database name where the chromInfo is stored. For example, the
  * mousBlat track in the human browser would hash to the mm2 database. */
 {
 orgDbHash = hashNew(8);
 }
 
 void cartDoMiddle(struct cart *theCart)
 /* Save cart and do main middle handler. */
 {
 initOrgDbHash();
 cart = theCart;
 doMiddle();
 }
 
 char *excludeVars[] = {"Submit", "submit", "g", "i", "aliTable", "addp", "pred", NULL};
 
 int main(int argc, char *argv[])
 {
 long enteredMainTime = clock1000();
 pushCarefulMemHandler(LIMIT_2or6GB);
 cgiSpoof(&argc,argv);
 cartEmptyShell(cartDoMiddle, hUserCookie(), excludeVars, NULL);
 cgiExitTime("hgc", enteredMainTime);
 return 0;
 }