f19df2f83650190a467c3b1226b3e8a23f6d48c0
braney
  Thu Sep 17 17:04:48 2026 -0700
hgc: build the chain and net details pages with htmlPrintf

The chain, snake, quickLift-chain and net click handlers assembled their
output with plain printf.  Route the values they print -- assembly and
organism names, sequence names, positions, the chain id and the quickLiftDb
setting -- through htmlPrintf instead, in the four handlers and in the five
linkToOtherBrowser* anchor helpers they share.

The anchor statements are split rather than converted whole, so that
hgTracksName(), cartSidUrlString() and the genark hubUrl stay in a plain
printf and only the values go through htmlPrintf.  Converting a whole
statement rewrites the '/' of ../cgi-bin/hgTracks as an entity on every
chain, net, maf and ortho details page; split this way the output is
unchanged for ordinary data.

Verified against the shared build: the native chainMm39, multiz100way and
quickLift "Alignment Differences" pages are byte-identical, and netMm39
differs only in the label "N's", which renders the same.

refs #20824

diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c
index c51d863f3e8..d45e5586048 100644
--- src/hg/hgc/hgc.c
+++ src/hg/hgc/hgc.c
@@ -3949,77 +3949,79 @@
 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 + hasBin);
 sqlFreeResult(&sr);
 chainDbAddBlocks(chain, track, conn);
 return chain;
 }
 
 void linkToOtherBrowserHub(char *otherDb, char *chrom, int start, int end,  char *hubUrl)
 /* Make anchor tag to open another browser window. */
 {
-printf("<A TARGET=\"_blank\" HREF=\"%s?genome=%s&position=%s%%3A%d-%d&hubUrl=%s\">",
-       hgTracksName(), otherDb, chrom, start+1, end, hubUrl);
+printf("<A TARGET=\"_blank\" HREF=\"%s?genome=", hgTracksName());
+htmlPrintf("%s&position=%s%%3A%d-%d", otherDb, chrom, start+1, end);
+printf("&hubUrl=%s\">", hubUrl);
 }
 
 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);
+printf("<A TARGET=\"_blank\" HREF=\"%s?db=", hgTracksName());
+htmlPrintf("%s&%s&position=%s%%3A%d-%d\">", 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);
+printf("<A TARGET=\"_blank\" HREF=\"%s?db=", hgTracksName());
+htmlPrintf("%s&ct=&position=%s\">", 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);
+printf("<A TARGET=\"_blank\" HREF=\"%s?db=", hgTracksName());
+htmlPrintf("%s&ct=&position=%s%%3A%d-%d\">", 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);
+htmlPrintf("<A TARGET=\"_blank\" TITLE=\"%s\" HREF=\"", title);
+printf("%s?db=", hgTracksName());
+htmlPrintf("%s&ct=&position=%s%%3A%d-%d\">", otherDb, chrom, start+1, end);
 }
 
 void chainToOtherBrowser(struct chain *chain, char *otherDb, char *otherOrg, char *hubUrl)
 /* 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);
     if (hubUrl)
         linkToOtherBrowserHub(otherDb, subChain->qName, qs-1, qe, hubUrl);
     else
         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", trackHubSkipHubName(otherOrg));
+    htmlPrintf("Open %s browser</A> at position corresponding to the part of chain that is in this window.<BR>\n", trackHubSkipHubName(otherOrg));
     }
 chainFree(&toFree);
 }
 
 struct dnaSeq *otherChromSeq(struct twoBitFile *otherTbf, char *otherDb, 
     char *otherChrom, int otherStart, int otherEnd)
 /* This fetches sequence from tdb->otherTwoBitUrl if available,  if not
  * tries to find it via otherDb */
 {
 if (otherTbf != NULL)
     {
     return twoBitReadSeqFrag(otherTbf, otherChrom, otherStart, otherEnd);
     }
 else
     {
@@ -4038,41 +4040,47 @@
 
 char *aliasQName = qName;
 
 boolean otherIsActive = FALSE;
 char *hubUrl = NULL;
 if (hDbIsActive(otherDb))
    otherIsActive = TRUE;
 else 
     hubUrl = genarkUrl(otherDb); // may be NULL
 
 char headerText[256];
 safef(headerText, sizeof headerText, "reference: %s, query: %s\n", trackHubSkipHubName(database), trackHubSkipHubName(otherDb) );
 genericHeader(parentTdb, headerText);
 
 if (hubUrl != NULL)
-    printf("<A HREF=\"hgTracks?hubUrl=%s&genome=%s&position=%s:%d-%d\" TARGET=_BLANK>%s:%d-%d</A> link to block in query assembly: <B>%s</B></A><BR>\n", hubUrl, otherDb, aliasQName,  qs, qe,   aliasQName, qs, qe, trackHubSkipHubName(otherDb));
+    {
+    printf("<A HREF=\"hgTracks?hubUrl=%s&genome=", hubUrl);
+    htmlPrintf("%s&position=%s:%d-%d\" TARGET=_BLANK>%s:%d-%d</A> link to block in query assembly: <B>%s</B></A><BR>\n", otherDb, aliasQName,  qs, qe,   aliasQName, qs, qe, trackHubSkipHubName(otherDb));
+    }
 else if (otherIsActive)
-    printf("<A HREF=\"hgTracks?db=%s&position=%s:%d-%d\" TARGET=_BLANK>%s:%d-%d</A> link to block in query assembly: <B>%s</B></A><BR>\n", otherDb, aliasQName, qs, qe, aliasQName, qs, qe, trackHubSkipHubName(otherDb));
+    htmlPrintf("<A HREF=\"hgTracks?db=%s&position=%s:%d-%d\" TARGET=_BLANK>%s:%d-%d</A> link to block in query assembly: <B>%s</B></A><BR>\n", otherDb, aliasQName, qs, qe, aliasQName, qs, qe, trackHubSkipHubName(otherDb));
 
 int qCenter = (qs + qe) / 2;
 int newQs = qCenter - qWidth/2;
 int newQe = qCenter + qWidth/2;
 if (hubUrl != NULL)
-   printf("<A HREF=\"hgTracks?hubUrl=%s&genome=%s&position=%s:%d-%d\" TARGET=\"_blank\">%s:%d-%d</A> link to same window size in query assembly: <B>%s</B></A><BR>\n", hubUrl,otherDb, aliasQName, newQs, newQe,aliasQName, newQs, newQe, trackHubSkipHubName(otherDb) );
+   {
+   printf("<A HREF=\"hgTracks?hubUrl=%s&genome=", hubUrl);
+   htmlPrintf("%s&position=%s:%d-%d\" TARGET=\"_blank\">%s:%d-%d</A> link to same window size in query assembly: <B>%s</B></A><BR>\n", otherDb, aliasQName, newQs, newQe,aliasQName, newQs, newQe, trackHubSkipHubName(otherDb) );
+   }
 else if (otherIsActive)
-    printf("<A HREF=\"hgTracks?db=%s&position=%s:%d-%d\" TARGET=\"_blank\">%s:%d-%d</A> link to same window size in query assembly: <B>%s</B></A><BR>\n", otherDb, aliasQName, newQs, newQe,aliasQName, newQs, newQe, trackHubSkipHubName(otherDb) );
+    htmlPrintf("<A HREF=\"hgTracks?db=%s&position=%s:%d-%d\" TARGET=\"_blank\">%s:%d-%d</A> link to same window size in query assembly: <B>%s</B></A><BR>\n", otherDb, aliasQName, newQs, newQe,aliasQName, newQs, newQe, trackHubSkipHubName(otherDb) );
 printTrackHtml(tdb);
 } 
 
 void genericChainClick(struct sqlConnection *conn, struct trackDb *tdb,
                        char *item, int start, char *otherDb)
 /* Handle click in chain track, at least the basics. */
 {
 boolean doSnake = cartOrTdbBoolean(cart, tdb, "doSnake" , FALSE) && cfgOptionBooleanDefault("canSnake", TRUE);
 
 if (doSnake)
     return doSnakeChainClick(tdb, item, otherDb);
 
 struct twoBitFile *otherTbf = getOtherTwoBitUrl(tdb);
 char *thisOrg = hOrganism(database);
 char *otherOrg = NULL;
@@ -4145,59 +4153,65 @@
                         atoi(words[0]));
             }
         }
 
     if (subChain->qStrand == '-')
         reverseIntRange(&qStart, &qEnd, subChain->qSize);
     qSeq = otherChromSeq(otherTbf, 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",
-       trackHubSkipHubName(thisOrg), hgTracksName(), cartSidUrlString(cart), database,
-       chain->tName, chain->tStart+1, chain->tEnd, chain->tName, chain->tStart+1, chain->tEnd,
-       chain->tEnd-chain->tStart);
+htmlPrintf("<B>%s position:</B> ", trackHubSkipHubName(thisOrg));
+printf("<A HREF=\"%s?%s&db=", hgTracksName(), cartSidUrlString(cart));
+htmlPrintf("%s&position=%s:%d-%d\">%s:%d-%d</A>  size: %d <BR>\n",
+           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",
+    htmlPrintf("<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);
+    htmlPrintf("<B>%s position: </B>", otherOrg);
     if (otherIsActive)
-        printf(" <A target=\"_blank\" href=\"%s?db=%s&position=%s%%3A%d-%d\">",
-               hgTracksName(), otherDb, chain->qName, qs, qe);
+        {
+        printf(" <A target=\"_blank\" href=\"%s?db=", hgTracksName());
+        htmlPrintf("%s&position=%s%%3A%d-%d\">", otherDb, chain->qName, qs, qe);
+        }
     else if (hubUrl != NULL)
-        printf(" <A target=\"_blank\" href=\"%s?genome=%s&hubUrl=%s&position=%s%%3A%d-%d\">",
-               hgTracksName(), otherDb, hubUrl, chain->qName, qs, qe);
-    printf("%s:%d-%d", chain->qName, qs, qe);
+        {
+        printf(" <A target=\"_blank\" href=\"%s?genome=", hgTracksName());
+        htmlPrintf("%s", otherDb);
+        printf("&hubUrl=%s&position=", hubUrl);
+        htmlPrintf("%s%%3A%d-%d\">", chain->qName, qs, qe);
+        }
+    htmlPrintf("%s:%d-%d", chain->qName, qs, qe);
     if (otherIsActive || hubUrl)
         printf("</A>");
     printf(" size: %d<BR>\n", chain->qEnd - chain->qStart);
     }
-printf("<B>Chain ID:</B> %s<BR>\n", item);
+htmlPrintf("<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 (otherIsActive && subChain != chain)
     printf("<B>&nbsp;&nbsp;Approximate Score within browser window:</B> %1.0f<BR>\n",
 	   subSetScore);
 else
     printf("<BR>\n");
 
 boolean normScoreAvailable = chainDbNormScoreAvailable(tdb);
 
 // The normalized score lives in the chain table, so for a quickLifted track it has to be
 // read from the assembly the chain came from.  Against the assembly on screen the table
 // name either does not resolve, or resolves to a same-named table there and returns
@@ -4227,31 +4241,31 @@
     if ((row = sqlNextRow(sr)) != NULL)
         {
         double normScore = atof(row[0]);
         int basesAligned = chain->score / normScore;
 	printf("<B>Normalized Score:</B> %1.0f (aligned bases: %d)", normScore, basesAligned);
         }
     sqlFreeResult(&sr);
     printf("<BR>\n");
     }
 if (lifted)
     hFreeConn(&normConn);
 
 if (quickLiftIsLifted(tdb) && !quickLiftIsOwnChainTrack(tdb))
     // A lifted chain is only worked out over the window being viewed, so the whole chain's
     // extent is not knowable here and the usual sentence would be wrong.
-    printf("<BR>This chain comes from %s and is mapped onto %s as the browser draws it, so "
+    htmlPrintf("<BR>This chain comes from %s and is mapped onto %s as the browser draws it, so "
                "the fields above describe the part of it around the window rather than the "
                "whole chain.<BR>\n",
                trackDbSetting(tdb, "quickLiftDb"), trackHubSkipHubName(database));
 else
     printf("<BR>Fields above refer to entire chain or gap, not just the part inside the window.<BR>\n");
 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 (otherTbf != NULL || 
@@ -4325,42 +4339,42 @@
 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);
+htmlPrintf("<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);
+htmlPrintf("<B>%s%s%s:</B> ", org, space, label);
 printLongWithCommas(stdout, p);
 if (q != 0)
     printf(" (%3.1f%%)", 100.0 * p / q);
 printf("<BR>\n");
 }
 
 static struct netAlign *bigNetLoadOne(struct trackDb *tdb, char *chrom, int start,
                                       unsigned level, boolean *retClipped)
 /* Load the record from a bigNet file at the given level that covers start.
  * Returns NULL if there isn't one.  Sets *retClipped when a quickLifted row would
  * only lift with its ends pulled in, so the extent reported is the visible part
  * rather than the whole item. */
 {
 char *fileName = hReplaceGbdb(trackDbSetting(tdb, "bigDataUrl"));
 char *quickLiftFile = trackDbSetting(tdb, "quickLiftUrl");
@@ -4574,49 +4588,49 @@
     if (chain != NULL)
         {
          /* print link to browser for otherDb only if otherDb is active */
         if (hDbIsActive(otherDb))
 	    chainToOtherBrowser(chain, otherDb, otherOrgBrowser, NULL);
 	chainFree(&chain);
 	}
     htmlHorizontalLine();
     }
 else if (net->chainId != 0)
     {
     /* Only an isBig track with no chain track to follow gets here. */
     if (isLifted)
         {
         char *sourceDb = trackDbSetting(tdb, "quickLiftDb");
-        printf("<BR>This net was lifted from %s, so its chains are not on this assembly "
+        htmlPrintf("<BR>This net was lifted from %s, so its chains are not on this assembly "
                    "and the alignment cannot be shown here.<BR>\n",
                    isEmpty(sourceDb) ? "another assembly" : sourceDb);
         }
     else
         {
         /* A hub whose type line names a chain track this assembly does not have.  Say so
          * rather than dropping the whole section without a word. */
         htmlPrintf("<BR>This track's type line names the chain track %s, which is not on "
                    "this assembly, so the alignment cannot be shown here.<BR>\n",
                    emptyForNull(namedChainTrack));
         }
     htmlHorizontalLine();
     }
-printf("<B>Type:</B> %s<BR>\n", net->type);
+htmlPrintf("<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",
+htmlPrintf("<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",
+htmlPrintf("<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)
@@ -5194,39 +5208,39 @@
             otherIsActive = TRUE;
             otherDb = genome->name;
             }
         }
     }
 
 chainSubsetOnT(chain, winStart, winEnd, &subChain, &toFree);
 
 char position[128];
 char *ourPos, *otherPos;
 int seqStart = cartInt(cart, "l");
 int seqEnd =   cartInt(cart, "r");
 char *chromName = cartString(cart, "c");
 safef(position, 128, "%s:%d-%d", chromName, seqStart, seqEnd);
 ourPos = cloneString(addCommasToPos(database, position));
-printf("<B>%s position:</B> %s<BR>", trackHubSkipHubName(database), ourPos);
+htmlPrintf("<B>%s position:</B> %s<BR>", trackHubSkipHubName(database), ourPos);
 
 int qs,qe;
 qChainRangePlusStrand(subChain, &qs, &qe);
 safef(position, 128, "%s:%d-%d", subChain->qName, qs-1, qe);
 otherPos = cloneString(addCommasToPos(otherDb, position));
-printf("<B>%s position: </B>", trackHubSkipHubName(otherDb));
+htmlPrintf("<B>%s position: </B>", trackHubSkipHubName(otherDb));
 linkToOtherBrowser(otherDb, subChain->qName, qs-1, qe);
-printf("%s</A><BR><BR>",  otherPos);
+htmlPrintf("%s</A><BR><BR>",  otherPos);
 chainWinSize = min(winEnd-winStart, chain->tEnd - chain->tStart);
 
 if (otherTbf != NULL || 
     (!startsWith("big", tdb->type) && sqlDatabaseExists(otherDb) 
      && chromSeqFileExists(otherDb, chain->qName)))
     {
     if (chainWinSize < 1000000)
         {
         printf("View ");
         hgcAnchorSomewhere("htcChainAli", item, tdb->track, chain->tName);
         printf("DNA sequence alignment of whole window.</A><BR><BR>");
         }
     else
         {
         printf("Zoom so that browser window covers 1,000,000 bases or less "
@@ -5259,115 +5273,123 @@
         case QUICKTYPE_MISMATCH:
             mismatches++;
             break;
         }
     }
 
 // If the click came from a specific difference (not one of the "identical"
 // regions), the matching row is shown in bold in the tables below.
 if (clickStart >= 0)
     printf("<BR>The item you clicked on is shown in <B>bold</B> in the tables below.<BR>\n");
 
 if (deletions)
     {
     printf("<BR><B>Deletions in Window:</B><BR>");
     printf("<TABLE border=\"1\"> <TR>\n");
-    printf("<TR><TD>%s Position</TD><TD>%s Position</TD><TD>Bases</TD><TD>Alignment</TD><TR>", trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
+    htmlPrintf("<TR><TD>%s Position</TD><TD>%s Position</TD><TD>Bases</TD><TD>Alignment</TD><TR>", trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
     for(hr = regions; hr; hr = hr->next)
         {
         if (hr->type != QUICKTYPE_DEL)
             continue;
 
         char *ourPos, *otherPos;
         snprintf(position, 128, "%s:%ld-%ld", hr->chrom, hr->chromStart, hr->chromEnd);
         ourPos = cloneString(addCommasToPos(database, position));
         snprintf(position, 128, "%s:%ld-%ld", hr->oChrom, hr->oChromStart, hr->oChromEnd);
         otherPos = cloneString(addCommasToPos(database, position));
         char *hilite = (clickStart >= 0 && hr->chromStart == clickStart && hr->chromEnd == clickEnd) ? " style='font-weight:bold'" : "";
-        printf("<TR%s><TD>%s</TD><TD>%s</TD><TD>%.*s</TD><TD>",   hilite, ourPos, otherPos, hr->otherBaseCount, hr->otherBases);
+        printf("<TR%s>", hilite);
+        htmlPrintf("<TD>%s</TD><TD>%s</TD>", ourPos, otherPos);
+        printf("<TD>%.*s</TD><TD>", hr->otherBaseCount, hr->otherBases);
         hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track);
             printf("alignment</A></TD></TR>");
 
         }
     printf("</TABLE>");
     }
 
 if (insertions)
     {
     printf("<BR><B>Insertions in Window:</B><BR>");
     printf("<TABLE border=\"1\"> <TR>\n");
-    printf("<TR><TD>%s Position</TD><TD>%s Position</TD><TD>Bases</TD><TD>Alignment</TD><TR>", trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
+    htmlPrintf("<TR><TD>%s Position</TD><TD>%s Position</TD><TD>Bases</TD><TD>Alignment</TD><TR>", trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
     for(hr = regions; hr; hr = hr->next)
         {
         if (hr->type != QUICKTYPE_INSERT)
             continue;
 
         char *ourPos, *otherPos;
         snprintf(position, 128, "%s:%ld-%ld", hr->chrom, hr->chromStart, hr->chromEnd);
         ourPos = cloneString(addCommasToPos(database, position));
         snprintf(position, 128, "%s:%ld-%ld", hr->oChrom, hr->oChromStart, hr->oChromEnd);
         otherPos = cloneString(addCommasToPos(database, position));
         char *hilite = (clickStart >= 0 && hr->chromStart == clickStart && hr->chromEnd == clickEnd) ? " style='font-weight:bold'" : "";
-        printf("<TR%s><TD>%s</TD><TD>%s</TD><TD>%.*s</TD><TD>",   hilite, ourPos, otherPos, hr->baseCount, hr->bases);
+        printf("<TR%s>", hilite);
+        htmlPrintf("<TD>%s</TD><TD>%s</TD>", ourPos, otherPos);
+        printf("<TD>%.*s</TD><TD>", hr->baseCount, hr->bases);
         hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track);
             printf("alignment</A></TD></TR>");
 
         }
     printf("</TABLE>");
     }
 
 if (doubles)
     {
     printf("<BR><B>Double Gaps in Window:</B><BR>");
     printf("<TABLE border=\"1\"> <TR>\n");
-    printf("<TR><TD>%s Position</TD><TD>%s Position</TD><TD># Bases in %s</TD><TD>#Bases in %s</TD><TD>Alignment</TD><TR>", trackHubSkipHubName(database), trackHubSkipHubName(otherDb), trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
+    htmlPrintf("<TR><TD>%s Position</TD><TD>%s Position</TD><TD># Bases in %s</TD><TD>#Bases in %s</TD><TD>Alignment</TD><TR>", trackHubSkipHubName(database), trackHubSkipHubName(otherDb), trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
     for(hr = regions; hr; hr = hr->next)
         {
         if (hr->type != QUICKTYPE_DOUBLE)
             continue;
 
         char *ourPos, *otherPos;
         snprintf(position, 128, "%s:%ld-%ld", hr->chrom, hr->chromStart, hr->chromEnd);
         ourPos = cloneString(addCommasToPos(database, position));
         snprintf(position, 128, "%s:%ld-%ld", hr->oChrom, hr->oChromStart, hr->oChromEnd);
         otherPos = cloneString(addCommasToPos(database, position));
         char *hilite = (clickStart >= 0 && hr->chromStart == clickStart && hr->chromEnd == clickEnd) ? " style='font-weight:bold'" : "";
-        printf("<TR%s><TD>%s</TD><TD>%s</TD><TD>%d</TD><TD>%d</TD><TD>",   hilite, ourPos, otherPos, hr->baseCount, hr->otherBaseCount);
+        printf("<TR%s>", hilite);
+        htmlPrintf("<TD>%s</TD><TD>%s</TD>", ourPos, otherPos);
+        printf("<TD>%d</TD><TD>%d</TD><TD>", hr->baseCount, hr->otherBaseCount);
         hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track);
             printf("alignment</A></TD></TR>");
 
         }
     printf("</TABLE>");
     }
 
 if (mismatches)
     {
     printf("<BR><B>Mismatches in Window:</B><BR>");
     printf("<TABLE border=\"1\"> <TR>\n");
-    printf("<TR><TD>%s Position</TD><TD>%s Position</TD><TD>Change</TD><TD>Alignment</TD><TR>", trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
+    htmlPrintf("<TR><TD>%s Position</TD><TD>%s Position</TD><TD>Change</TD><TD>Alignment</TD><TR>", trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
     for(hr = regions; hr; hr = hr->next)
         {
         if (hr->type != QUICKTYPE_MISMATCH)
             continue;
 
         char *ourPos, *otherPos;
         snprintf(position, 128, "%s:%ld-%ld", hr->chrom, hr->chromStart, hr->chromEnd);
         ourPos = cloneString(addCommasToPos(database, position));
         snprintf(position, 128, "%s:%ld-%ld", hr->oChrom, hr->oChromStart, hr->oChromEnd);
         otherPos = cloneString(addCommasToPos(database, position));
         char *hilite = (clickStart >= 0 && hr->chromStart == clickStart && hr->chromEnd == clickEnd) ? " style='font-weight:bold'" : "";
-        printf("<TR%s><TD>%s</TD><TD>%s</TD><TD>%.*s -> %.*s</TD><TD>",   hilite, ourPos, otherPos, hr->otherBaseCount, hr->otherBases, hr->baseCount, hr->bases);
+        printf("<TR%s>", hilite);
+        htmlPrintf("<TD>%s</TD><TD>%s</TD>", ourPos, otherPos);
+        printf("<TD>%.*s -> %.*s</TD><TD>", hr->otherBaseCount, hr->otherBases, hr->baseCount, hr->bases);
         hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track);
             printf("alignment</A></TD></TR>");
 
         }
     printf("</TABLE>");
     }
 }
 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");