d040206b07aef75170ce11c82b387cba22eeceff
galt
  Fri Aug 22 00:06:30 2025 -0700
Extend custom file extensions to support upper and lower case url-encoded versions of them, so that can support URLs like GEO makes which have been unnecessarily url-encoded. fixes #36074 and closes #36071

diff --git src/hg/lib/customTrack.c src/hg/lib/customTrack.c
index 6e287e05f2d..1dae331d906 100644
--- src/hg/lib/customTrack.c
+++ src/hg/lib/customTrack.c
@@ -653,42 +653,61 @@
         fileName, (unsigned long) cF, (unsigned long) strlen(cF));
     }
 return cloneString(buf);
 }
 
 char* customTrackTypeFromBigFile(char *url)
 /* return most likely type for a big file name or NULL,
  * has to be freed. */
 {
 // pull out file part from the URL, strip off the query part after "?"
 char fileName[2000];
 safecpy(fileName, sizeof(fileName), url);
 chopSuffixAt(fileName, '?');
 
 // based on udc cache dir analysis by hiram in rm #12813
-if (endsWith(fileName, ".bb") || endsWith(fileName, ".bigBed") || endsWith(fileName, ".bigbed"))
+if (endsWith(fileName, ".bb") || endsWith(fileName, ".bigBed") || endsWith(fileName, ".bigbed")
+ || endsWith(fileName, "%2Ebb") || endsWith(fileName, "%2EbigBed") || endsWith(fileName, "%2Ebigbed")
+ || endsWith(fileName, "%2ebb") || endsWith(fileName, "%2ebigBed") || endsWith(fileName, "%2ebigbed"))
     return cloneString("bigBed");
-if (endsWith(fileName, ".bw") || endsWith(fileName, ".bigWig") ||  
-            endsWith(fileName, ".bigwig") || endsWith(fileName, ".bwig"))
+
+if (endsWith(fileName, ".bw") || endsWith(fileName, ".bigWig") 
+ || endsWith(fileName, "%2Ebw") || endsWith(fileName, "%2EbigWig")
+ || endsWith(fileName, "%2ebw") || endsWith(fileName, "%2ebigWig")
+         || endsWith(fileName, ".bigwig") || endsWith(fileName, ".bwig")
+         || endsWith(fileName, "%2Ebigwig") || endsWith(fileName, "%2Ebwig")
+         || endsWith(fileName, "%2ebigwig") || endsWith(fileName, "%2ebwig"))
     return cloneString("bigWig");
-if (endsWith(fileName, ".inter.bb") || endsWith(fileName, ".inter.bigBed"))
+
+if (endsWith(fileName, ".inter.bb") || endsWith(fileName, ".inter.bigBed")
+ || endsWith(fileName, "%2Einter%2Ebb") || endsWith(fileName, "%2Einter%2EbigBed")
+ || endsWith(fileName, "%2einter%2ebb") || endsWith(fileName, "%2Einter%2ebigBed"))
    return cloneString("bigInteract");
-if (endsWith(fileName, ".bam") || endsWith(fileName, ".cram"))
+
+if (endsWith(fileName, ".bam") || endsWith(fileName, ".cram")
+ || endsWith(fileName, "%2Ebam") || endsWith(fileName, "%2Ecram")
+ || endsWith(fileName, "%2ebam") || endsWith(fileName, "%2ecram"))
     return cloneString("bam");
-if (endsWith(fileName, ".vcf.gz"))
+
+if (endsWith(fileName, ".vcf.gz")
+ || endsWith(fileName, "%2Evcf%2Egz")
+ || endsWith(fileName, "%2evcf%2egz"))
     return cloneString("vcfTabix");
-if (endsWith(fileName, ".vcf"))
+
+if (endsWith(fileName, ".vcf")
+ || endsWith(fileName, "%2Evcf")
+ || endsWith(fileName, "%2evcf"))
     return cloneString("vcf");
 return NULL;
 }
 
 boolean customTrackIsBigData(char *fileName)
 /* Return TRUE if fileName has a suffix that we recognize as a bigDataUrl track type. */
 {
 char *fileNameDecoded = cloneString(fileName);
 cgiDecode(fileName, fileNameDecoded, strlen(fileName));
 
 boolean result;
 char *type = customTrackTypeFromBigFile(fileNameDecoded);
 // exclude plain VCF (as opposed to vcfTabix) from bigData treatment
 result = (type != NULL && differentString(type, "vcf"));