c683ecb63d721deb02fa8ab15bf66f70f1c3a326 max Sat Jul 25 18:25:00 2026 -0700 hgBlat/hgc: single-page BLAT results view with shareable alignment links Add a modern single-page BLAT results table (hgBlat.js) and a non-frameset alignment view (showSomeAlignmentModern in hgc, gated by the blatNewPage cart var). Share/reopen a result set from a durable bigPsl custom track pinned in the cart via a saved session (htcBlatAlign / loadBlatShareSessionIfAny). Factor the shared helpers into a new blatShare module (lib/blatShare.c, inc/blatShare.h). diff --git src/hg/lib/blatShare.c src/hg/lib/blatShare.c new file mode 100644 index 00000000000..1908f09aacf --- /dev/null +++ src/hg/lib/blatShare.c @@ -0,0 +1,114 @@ +/* blatShare.c - reopen durable BLAT bigPsl results. Shared by hgBlat (which rebuilds the results + * table for a shared "?u=&s=" link) and hgc (which rebuilds one base-by-base alignment for a shared + * alignment link), so both read the durable custom track through the same code and the same + * server-file security check. */ + +/* Copyright (C) 2024 The Regents of the University of California + * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ + +#include "common.h" +#include "localmem.h" +#include "psl.h" +#include "bbiFile.h" +#include "bigBed.h" +#include "bigPsl.h" +#include "cart.h" +#include "chromAlias.h" +#include "hgConfig.h" +#include "portable.h" +#include "blatShare.h" + +struct psl *pslListFromBigPslFile(char *bbFileName) +/* Read every alignment out of a bigPsl bigBed file into a psl list, in query-display order. + * Used to rebuild the Table view for a shared link from the durable custom track. */ +{ +struct bbiFile *bbi = bigBedFileOpenAlias(bbFileName, chromAliasFindAliases); +struct lm *lm = lmInit(0); +struct psl *pslList = NULL; +struct bbiChromInfo *chrom, *chromList = bbiChromList(bbi); +for (chrom = chromList; chrom != NULL; chrom = chrom->next) + { + struct bigBedInterval *bb, *ivList = bigBedIntervalQuery(bbi, chrom->name, 0, chrom->size, 0, lm); + for (bb = ivList; bb != NULL; bb = bb->next) + { + /* pslFromBigPsl ignores its seqTypeField arg (it reads seqType from the record itself). */ + struct psl *psl = pslFromBigPsl(chrom->name, bb, 0, NULL, NULL); + /* pslFromBigPsl always yields a two-char (query,target) strand with the target normalized + * to '+'; a DNA BLAT is shown with a single-char strand, so drop the redundant target '+' + * for non-protein queries to match the classic/fresh result page. */ + if (!pslIsProtein(psl) && psl->strand[1] == '+') + psl->strand[1] = 0; + slAddHead(&pslList, psl); + } + } +bbiChromInfoFreeList(&chromList); +lmCleanup(&lm); +bbiFileClose(&bbi); +return pslList; +} + +struct psl *pslFromBigPslFileMatch(char *bbFileName, char *chrom, int tStart, char *qName, + char **retSeq, char **retCds) +/* Return the single alignment in a bigPsl bigBed matching chrom:tStart and qName, together with its + * stored query sequence (retSeq) and CDS (retCds) when those out pointers are non-NULL, or NULL if + * there is no such alignment. Caller frees the returned psl (and any returned seq and cds). */ +{ +struct bbiFile *bbi = bigBedFileOpenAlias(bbFileName, chromAliasFindAliases); +struct lm *lm = lmInit(0); +struct psl *result = NULL; +if (retSeq != NULL) + *retSeq = NULL; +if (retCds != NULL) + *retCds = NULL; +/* Every record overlapping [tStart, tStart+1) is returned; keep the one that actually starts at + * tStart and carries the requested query name. */ +struct bigBedInterval *bb, *ivList = bigBedIntervalQuery(bbi, chrom, tStart, tStart + 1, 0, lm); +for (bb = ivList; bb != NULL; bb = bb->next) + { + if (bb->start != tStart) + continue; + char *seq = NULL, *cds = NULL; + struct psl *psl = pslFromBigPsl(chrom, bb, 0, &seq, &cds); + if (sameString(psl->qName, qName)) + { + if (!pslIsProtein(psl) && psl->strand[1] == '+') + psl->strand[1] = 0; + result = psl; + if (retSeq != NULL) + *retSeq = seq; + else + freeMem(seq); + if (retCds != NULL) + *retCds = cds; + else + freeMem(cds); + break; + } + pslFree(&psl); + freeMem(seq); + freeMem(cds); + } +lmCleanup(&lm); +bbiFileClose(&bbi); +return result; +} + +char *blatFindPinnedBigPsl(struct cart *cart) +/* Return a cloned path to the BLAT bigPsl bigBed that hgc's buildBigPsl pinned in the cart + * (blatLastBigBed), or NULL. Reading the pinned value is unambiguous even when the cart holds + * several BLAT custom tracks from earlier searches. A shared session (?u=&s=) can carry an arbitrary + * value here, so only ever accept a local file the server itself placed under its trash dir, or under + * the durable session-data dir (where a saved session's trash files are moved) - never a remote URL + * or an arbitrary local path. This is the trash/sessionData allow-list from isValidBigDataUrl(). */ +{ +char *f = cartOptionalString(cart, "blatLastBigBed"); +if (f == NULL) + return NULL; +char *sessionDataDir = cfgOption("sessionDataDir"); +char *sessionDataDirOld = cfgOption("sessionDataDirOld"); +if (startsWith(trashDir(), f) || + (isNotEmpty(sessionDataDir) && startsWith(sessionDataDir, f)) || + (isNotEmpty(sessionDataDirOld) && startsWith(sessionDataDirOld, f))) + return cloneString(f); +return NULL; +}