9a4d8c8cdd36457a18219cfb445abf9ce4e3f843
braney
  Fri Sep 4 17:28:09 2026 -0700
hgc: make the RNA fold diagram actually appear.

RNAplot takes the name of its output file from the sequence id on the fasta
header it is given: it keeps the first 42 characters of that id and appends
"_ss.ps". We were handing it "../trash/<table>/<table>_<name>.ps" and then
asking ghostscript to read back exactly that path, which is wrong twice over.
The "_ss.ps" means the name never matched even for a short item, and the path
prefix alone is 41 characters, so RNAplot kept a single letter of the item
name and every item in a track landed on the same file. Ghostscript was then
pointed at something that had never been written, returned 1, and until the
previous commit that killed the whole details page.

There is no output-file option in RNAplot, and no way to hand our pipeline a
working directory, so the id has to carry the path and still fit. The item
name cannot be part of it: names in wuhCor1.rnaStructRangan are themselves 42
characters. So the id is now a short trash directory plus a 20 character hash
of the track and item name, which is always 37 characters, and ghostscript
reads the "_ss.ps" file RNAplot really wrote.

All 112 items of wuhCor1.rnaStructRangan, the only rnaStruct track we have,
now draw their own diagram: 112 distinct images where before there were none.

Note for whoever looks at this next: RNAplot is not part of this tree and is
not installed by it. It sits in the shared cgi-bin and in cgi-bin-beta, and
it is absent from cgi-bin-$USER, so this feature cannot be exercised from a
developer sandbox without setting rnaPlotPath in hg.conf. Worth confirming the
binary is present in the RR's cgi-bin. Where it is missing the page now says
the diagram could not be made rather than failing outright.

refs #37424

diff --git src/hg/hgc/rnaFoldClick.c src/hg/hgc/rnaFoldClick.c
index bcf9afa10a1..f8f9ed2dc52 100644
--- src/hg/hgc/rnaFoldClick.c
+++ src/hg/hgc/rnaFoldClick.c
@@ -7,30 +7,31 @@
 #include "jksql.h"
 #include "hgMaf.h"
 #include "maf.h"
 #include "cart.h"
 #include "hgc.h"
 #include "hCommon.h"
 #include "hgColors.h"
 #include "obscure.h"
 #include "customTrack.h"
 #include "htmshell.h"
 #include "rnautil.h"
 #include "rnaSecStr.h"
 #include "memalloc.h"
 #include "hgConfig.h"
 #include "pipeline.h"
+#include "md5.h"
 
 
 /* Taken from hgc.c (should probably be in hgc.h)*/
 #define RED 0xFF0000
 #define GREEN 0x00FF00
 #define BLUE 0x0000FF
 #define BLACK 0x000000
 #define CYAN 0x00FFFF
 #define GRAY 0xcccccc
 #define LTGRAY 0x999999
 #define ORANGE 0xDD6600
 #define MAGENTA 0xFF00FF
 
 #define LTPURPLE 0x9966CC
 #define SCORE_SHADES_COUNT 10
@@ -360,79 +361,96 @@
     if ((mcThis = mafMayFindCompSpecies(maf, species[i], '.')) == NULL)
 	continue;
     newOrder[mcCount++] = mcThis;
   }
 
 maf->components = NULL;
 for (i = 0; i < mcCount; i++)
   {
     newOrder[i]->next = 0;
     slAddHead(&maf->components, newOrder[i]);
   }
 
 slReverse(&maf->components);
 }
 
+/* RNAplot truncates the sequence id it is given to 42 characters, and the id has to
+ * carry the output path, so both of these are kept deliberately short: 9 for
+ * "../trash/", 8 for the directory and its slash, and 20 for the hash is 37. */
+#define RNA_FOLD_TRASH_DIR "rnaFold"
+#define RNA_FOLD_HASH_LEN  20
+
 void htmlPrintSecStr(FILE *f, char *table, struct rnaSecStr *item, int start)
 /* Print out the details for an rnaStruct* table. */
 {
 // grab the sequence
 struct dnaSeq *seq = hChromSeq(database, item->chrom, item->chromStart, item->chromEnd);
 touppers(seq->dna);
 if (item->strand[0] == '-')
     reverseComplement(seq->dna, seq->size);
 toRna(seq->dna);
 
 // make sure the dna is not longer than the paren string
 seq->dna[strlen(item->secStr)] = 0;
 
 char *rnaPlotPath = cfgOptionDefault("rnaPlotPath", "../cgi-bin/RNAplot");
-mkdirTrashDirectory(table);
+mkdirTrashDirectory(RNA_FOLD_TRASH_DIR);
+
+/* RNAplot takes the name of its output file from the sequence id on the fasta header
+ * written below: it keeps the first 42 characters of that id and appends "_ss.ps".
+ * So the id has to be a relative path that survives the truncation intact and is
+ * still unique per item.  There is no room to spell the item out - names in
+ * wuhCor1.rnaStructRangan reach 42 characters on their own - so hash the table and
+ * item name into a fixed-width base.  The id below is always 37 characters.
+ * refs #37424 */
+char idText[PATH_LEN];
+safef(idText, sizeof idText, "%s/%s", table, item->name);
+char *idHash = md5HexForString(idText);
+idHash[RNA_FOLD_HASH_LEN] = '\0';
+
+char psRoot[PATH_LEN];
+safef(psRoot, sizeof psRoot, "../trash/%s/%s", RNA_FOLD_TRASH_DIR, idHash);
+freeMem(idHash);
 
-char psName[512];
-safef(psName, sizeof(psName), "../trash/%s/%s_%s.ps", table, table, item->name);
 char *plotCmd[] = {rnaPlotPath, NULL};
 struct pipeline *plStruct = pipelineOpen1(plotCmd, pipelineWrite | pipelineNoAbort, "/dev/null", NULL, 0);
 FILE *of = pipelineFile(plStruct);
 if (of != NULL)
     {
-    fprintf(of, ">%s\n", psName);        /* This tells where to put file. */
+    fprintf(of, ">%s\n", psRoot);        /* This tells where to put file. */
     fprintf(of, "%s\n%s\n", seq->dna, item->secStr);
     }
 pipelineClose(&plStruct);
 
-char pngName[256];
-char *rootName = cloneString(psName);
- 
-chopSuffix(rootName);
-safef(pngName, sizeof(pngName), "%s.png", rootName);
+/* RNAplot appended "_ss.ps" to the id, so that, not psRoot, is the file gs reads. */
+char psName[PATH_LEN];
+safef(psName, sizeof psName, "%s_ss.ps", psRoot);
+char pngName[PATH_LEN];
+safef(pngName, sizeof pngName, "%s.png", psRoot);
 
 char outputBuf[1024];
 safef(outputBuf, sizeof outputBuf, "-sOutputFile=%s", pngName);
 char *pipeCmd[] = {"gs", "-sDEVICE=png16m", outputBuf, "-dBATCH","-dNOPAUSE","-q", psName, NULL};
 struct pipeline *pl = pipelineOpen1(pipeCmd, pipelineWrite | pipelineNoAbort, "/dev/null", NULL, 0);
 int sysRet = pipelineWait(pl);
 
 printf("<a target=blank href='http://pseudoviewer.inha.ac.kr/WSPV_quickSender.asp?seq=%s&str=%s&start=%d'>Display on PseudoViewer</a><BR>", seq->dna, item->secStr, start);
 htmlHorizontalLine();
 if (sysRet != 0)
     {
-    /* Always fires today: RNAplot truncates the sequence id we hand it to 42
-     * characters and appends "_ss.ps", and psName's directory prefix is already 41
-     * characters, so RNAplot writes rnaStructRangan_<one letter>_ss.ps and gs is
-     * pointed at a file that was never created.  Drop just the diagram; the rest of
-     * the page is still worth showing.  refs #37424 */
+    /* RNAplot wrote no PostScript file, or gs could not read it.  Drop just the
+     * diagram; the rest of the page is still worth showing.  refs #37424 */
     printf("RNAFold diagram could not be made.<BR>");
     warn("Could not make the RNA fold diagram: system call returned %d for:\n  %s",
         sysRet, pipelineDesc(pl));
     }
 else
     {
     printf("RNAFold diagram:<BR>");
     printf("<IMG SRC='%s' border = '2'>", pngName);
     }
 }
 
 void htmlPrintSecStrEvofoldDrawing(FILE *f, struct rnaSecStr *item)
 {
 char fileName[512];
 struct dnaSeq *seq;