b4538b995c8b87f8663861aa9cde4b8c68e83eb1 braney Tue Apr 7 12:52:56 2026 -0700 Fix Get DNA position text box being ignored when table is specified, and accept single-base positions. refs #10316, #15336, #37325 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c index 4d2d8457048..84614360b20 100644 --- src/hg/hgc/hgc.c +++ src/hg/hgc/hgc.c @@ -5533,49 +5533,66 @@ { struct trackDb *tdbList = hTrackDb(database); struct trackDb *ctdbList = tdbForCustomTracks(); struct trackDb *utdbList = tdbForUserPsl(); struct grp *pGrpList = NULL; struct trackDb *hubList = hubCollectTracks(database, &pGrpList); ctdbList = slCat(ctdbList, tdbList); ctdbList = slCat(ctdbList, hubList); tdbList = slCat(utdbList, ctdbList); return tdbList; } +static boolean parseDnaPos(char *db, char *pos, char **retChrom, int *retStart, int *retEnd) +/* Parse a position string that may be chrom:start-end or just chrom:pos (single base). */ +{ +if (hgParseChromRange(db, pos, retChrom, retStart, retEnd)) + return TRUE; +/* check for single position like chrX:67774500 (colon but no dash) */ +char *colon = strchr(pos, ':'); +if (colon != NULL && strchr(colon, '-') == NULL) + { + struct dyString *dy = dyStringCreate("%s-%s", pos, colon + 1); + boolean result = hgParseChromRange(db, dy->string, retChrom, retStart, retEnd); + dyStringFree(&dy); + return result; + } +return FALSE; +} + void doGetDnaExtended1() /* Do extended case/color get DNA options. */ { 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; struct trackDb *tdbList = loadTracks(); cartWebStart(cart, database, "Extended DNA Case/Color"); if (NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos")))) - hgParseChromRange(database, pos, &seqName, &winStart, &winEnd); + parseDnaPos(database, pos, &seqName, &winStart, &winEnd); if (winEnd - winStart > 5000000) { printf("Please zoom in to 5 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 " ""hide" display mode are not shown in the grid below.</B> <P>"); if (cgiBooleanDefined("hgSeq.maskRepeats")) @@ -5833,54 +5850,58 @@ 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"); puts("<PRE>"); if (tbl[0] == 0) { itemCount = 1; if ( NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos"))) && - hgParseChromRange((dbIsFound ? database : NULL), pos, &chrom, &start, &end)) + parseDnaPos((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[HDB_MAX_TABLE_STRING]; char parsedChrom[HDB_MAX_CHROM_STRING]; /* use the values from the dnaPos dialog box */ if (!( NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos"))) && - hgParseChromRange(database, pos, &chrom, &start, &end))) + parseDnaPos(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"); } + else + { + seqName = chrom; + } /* 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; @@ -6189,31 +6210,31 @@ 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 = loadTracks(); 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); + parseDnaPos(database, pos, &seqName, &winStart, &winEnd); winSize = winEnd - winStart; uBits = bitAlloc(winSize); /* Underline bits. */ iBits = bitAlloc(winSize); /* Italic bits. */ bBits = bitAlloc(winSize); /* Bold bits. */ 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);