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("",
- hgTracksName(), otherDb, chrom, start+1, end, hubUrl);
+printf("", hubUrl);
}
void linkToOtherBrowserExtra(char *otherDb, char *chrom, int start, int end, char *extra)
/* Make anchor tag to open another browser window. */
{
-printf("",
- hgTracksName(), otherDb, extra, chrom, start+1, end);
+printf("", otherDb, extra, chrom, start+1, end);
}
void linkToOtherBrowserSearch(char *otherDb, char *tag)
/* Make anchor tag to open another browser window. */
{
-printf("",
- hgTracksName(), otherDb, tag);
+printf("", otherDb, tag);
}
void linkToOtherBrowser(char *otherDb, char *chrom, int start, int end)
/* Make anchor tag to open another browser window. */
{
-printf("",
- hgTracksName(), otherDb, chrom, start+1, end);
+printf("", 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("",
- title, hgTracksName(), otherDb, chrom, start+1, end);
+htmlPrintf("", 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 at position corresponding to the part of chain that is in this window.
\n", trackHubSkipHubName(otherOrg));
+ htmlPrintf("Open %s browser at position corresponding to the part of chain that is in this window.
\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("%s:%d-%d link to block in query assembly: %s
\n", hubUrl, otherDb, aliasQName, qs, qe, aliasQName, qs, qe, trackHubSkipHubName(otherDb));
+ {
+ printf("%s:%d-%d link to block in query assembly: %s
\n", otherDb, aliasQName, qs, qe, aliasQName, qs, qe, trackHubSkipHubName(otherDb));
+ }
else if (otherIsActive)
- printf("%s:%d-%d link to block in query assembly: %s
\n", otherDb, aliasQName, qs, qe, aliasQName, qs, qe, trackHubSkipHubName(otherDb));
+ htmlPrintf("%s:%d-%d link to block in query assembly: %s
\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("%s:%d-%d link to same window size in query assembly: %s
\n", hubUrl,otherDb, aliasQName, newQs, newQe,aliasQName, newQs, newQe, trackHubSkipHubName(otherDb) );
+ {
+ printf("%s:%d-%d link to same window size in query assembly: %s
\n", otherDb, aliasQName, newQs, newQe,aliasQName, newQs, newQe, trackHubSkipHubName(otherDb) );
+ }
else if (otherIsActive)
- printf("%s:%d-%d link to same window size in query assembly: %s
\n", otherDb, aliasQName, newQs, newQe,aliasQName, newQs, newQe, trackHubSkipHubName(otherDb) );
+ htmlPrintf("%s:%d-%d link to same window size in query assembly: %s
\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("%s position: %s:%d-%d"
- " size: %d
\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("%s position: ", trackHubSkipHubName(thisOrg));
+printf("%s:%d-%d size: %d
\n",
+ database, chain->tName, chain->tStart+1, chain->tEnd,
+ chain->tName, chain->tStart+1, chain->tEnd, chain->tEnd-chain->tStart);
printf("Strand: %c
\n", chain->qStrand);
qChainRangePlusStrand(chain, &qs, &qe);
if (sameWord(otherDb, "seq"))
{
- printf("%s position: %s:%d-%d size: %d
\n",
+ htmlPrintf("%s position: %s:%d-%d size: %d
\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("%s position: ", otherOrg);
+ htmlPrintf("%s position: ", otherOrg);
if (otherIsActive)
- printf(" ",
- hgTracksName(), otherDb, chain->qName, qs, qe);
+ {
+ printf(" ", otherDb, chain->qName, qs, qe);
+ }
else if (hubUrl != NULL)
- printf(" ",
- hgTracksName(), otherDb, hubUrl, chain->qName, qs, qe);
- printf("%s:%d-%d", chain->qName, qs, qe);
+ {
+ printf(" ", chain->qName, qs, qe);
+ }
+ htmlPrintf("%s:%d-%d", chain->qName, qs, qe);
if (otherIsActive || hubUrl)
printf("");
printf(" size: %d
\n", chain->qEnd - chain->qStart);
}
-printf("Chain ID: %s
\n", item);
+htmlPrintf("Chain ID: %s
\n", item);
printf("Score: %1.0f\n", chain->score);
if (nullSubset)
printf("Score within browser window: N/A (no aligned bases)
\n");
else if (otherIsActive && subChain != chain)
printf(" Approximate Score within browser window: %1.0f
\n",
subSetScore);
else
printf("
\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("Normalized Score: %1.0f (aligned bases: %d)", normScore, basesAligned);
}
sqlFreeResult(&sr);
printf("
\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("
This chain comes from %s and is mapped onto %s as the browser draws it, so "
+ htmlPrintf("
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.
\n",
trackDbSetting(tdb, "quickLiftDb"), trackHubSkipHubName(database));
else
printf("
Fields above refer to entire chain or gap, not just the part inside the window.
\n");
printf("
\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("%s%s%s: ", org, space, label);
+htmlPrintf("%s%s%s: ", org, space, label);
printLongWithCommas(stdout, number);
printf("
\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("%s%s%s: ", org, space, label);
+htmlPrintf("%s%s%s: ", org, space, label);
printLongWithCommas(stdout, p);
if (q != 0)
printf(" (%3.1f%%)", 100.0 * p / q);
printf("
\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("
This net was lifted from %s, so its chains are not on this assembly "
+ htmlPrintf("
This net was lifted from %s, so its chains are not on this assembly "
"and the alignment cannot be shown here.
\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("
This track's type line names the chain track %s, which is not on "
"this assembly, so the alignment cannot be shown here.
\n",
emptyForNull(namedChainTrack));
}
htmlHorizontalLine();
}
-printf("Type: %s
\n", net->type);
+htmlPrintf("Type: %s
\n", net->type);
printf("Level: %d
\n", (net->level+1)/2);
-printf("%s position: %s:%d-%d
\n",
+htmlPrintf("%s position: %s:%d-%d
\n",
org, net->tName, net->tStart+1, net->tEnd);
-printf("%s position: %s:%d-%d
\n",
+htmlPrintf("%s position: %s:%d-%d
\n",
otherOrg, net->qName, net->qStart+1, net->qEnd);
printf("Strand: %c
\n", net->strand[0]);
printLabeledNumber(NULL, "Score", net->score);
if (net->chainId)
{
printf("Chain ID: %u
\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("%s position: %s
", trackHubSkipHubName(database), ourPos);
+htmlPrintf("%s position: %s
", 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("%s position: ", trackHubSkipHubName(otherDb));
+htmlPrintf("%s position: ", trackHubSkipHubName(otherDb));
linkToOtherBrowser(otherDb, subChain->qName, qs-1, qe);
-printf("%s
", otherPos);
+htmlPrintf("%s
", 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.
");
}
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("
The item you clicked on is shown in bold in the tables below.
\n");
if (deletions)
{
printf("
Deletions in Window:
");
printf("
\n");
- printf("
| %s Position | %s Position | Bases | Alignment |
", trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
+ htmlPrintf("
| %s Position | %s Position | Bases | Alignment |
", 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("
| %s | %s | %.*s | ", hilite, ourPos, otherPos, hr->otherBaseCount, hr->otherBases);
+ printf(" |
", hilite);
+ htmlPrintf("| %s | %s | ", ourPos, otherPos);
+ printf("%.*s | ", hr->otherBaseCount, hr->otherBases);
hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track);
printf("alignment |
");
}
printf("
");
}
if (insertions)
{
printf("
Insertions in Window:
");
printf(" \n");
- printf("
| %s Position | %s Position | Bases | Alignment |
", trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
+ htmlPrintf("
| %s Position | %s Position | Bases | Alignment |
", 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("
| %s | %s | %.*s | ", hilite, ourPos, otherPos, hr->baseCount, hr->bases);
+ printf(" |
", hilite);
+ htmlPrintf("| %s | %s | ", ourPos, otherPos);
+ printf("%.*s | ", hr->baseCount, hr->bases);
hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track);
printf("alignment |
");
}
printf("
");
}
if (doubles)
{
printf("
Double Gaps in Window:
");
printf(" \n");
- printf("
| %s Position | %s Position | # Bases in %s | #Bases in %s | Alignment |
", trackHubSkipHubName(database), trackHubSkipHubName(otherDb), trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
+ htmlPrintf("
| %s Position | %s Position | # Bases in %s | #Bases in %s | Alignment |
", 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("
| %s | %s | %d | %d | ", hilite, ourPos, otherPos, hr->baseCount, hr->otherBaseCount);
+ printf(" |
", hilite);
+ htmlPrintf("| %s | %s | ", ourPos, otherPos);
+ printf("%d | %d | ", hr->baseCount, hr->otherBaseCount);
hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track);
printf("alignment |
");
}
printf("
");
}
if (mismatches)
{
printf("
Mismatches in Window:
");
printf(" \n");
- printf("
| %s Position | %s Position | Change | Alignment |
", trackHubSkipHubName(database), trackHubSkipHubName(otherDb));
+ htmlPrintf("
| %s Position | %s Position | Change | Alignment |
", 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("
| %s | %s | %.*s -> %.*s | ", hilite, ourPos, otherPos, hr->otherBaseCount, hr->otherBases, hr->baseCount, hr->bases);
+ printf(" |
", hilite);
+ htmlPrintf("| %s | %s | ", ourPos, otherPos);
+ printf("%.*s -> %.*s | ", 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 |
");
}
printf("
");
}
}
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");