981b68955aada90b686af9ad7699e542c0ed24ff
kate
  Tue Apr 13 12:22:00 2021 -0700
Extend #insert support in hgTrackDb to support variable substitution in filename.  Prompted by single cell composite tracks, so subtracks can share a description page with subtrack-specific sections inserted. refs #27365

diff --git src/hg/makeDb/hgTrackDb/hgTrackDb.c src/hg/makeDb/hgTrackDb/hgTrackDb.c
index b98e6cf..27113e8 100644
--- src/hg/makeDb/hgTrackDb/hgTrackDb.c
+++ src/hg/makeDb/hgTrackDb/hgTrackDb.c
@@ -376,89 +376,96 @@
 // This regular expression matches an HTML comment that contains a
 // server-side-include-like syntax that tells us to replace the HTML
 // comment with the contents of some other HTML file (relative path)
 // [which itself may include such HTML comments].
 // This regex has substrs:
 // * substrs[0] is the entire match
 // * substrs[1] is an optional '#if (db==xxxYyy1)' before #insert
 // * substrs[2] is the database from the #if, or empty if substrs[1] is empty
 // * substrs[3] is the relative path to pull in.
 const static char *insertHtmlRegex =
     "<!--"
     "([[:space:]]*#if[[:space:]]*\\(db[[:space:]]*==[[:space:]]*([[:alnum:]]+)[[:space:]]*\\))?"
     "[[:space:]]*#insert[[:space:]]+file[[:space:]]*=[[:space:]]*"
     "\"([^/][^\"]+)\"[[:space:]]*-->";
 
-static char *readHtmlRecursive(char *fileName, char *database)
+static char *readHtmlRecursive(char *fileName, char *database, struct trackDb *tdb)
 /* Slurp in an html file.  Wherever it contains insertHtmlRegex, recursively slurp that in
  * and replace insertHtmlRegex with the contents. */
 {
 char *html;
 readInGulp(fileName, &html, NULL);
 if (isEmpty(html))
     return html;
 regmatch_t substrs[4];
 while (regexMatchSubstr(html, insertHtmlRegex, substrs, ArraySize(substrs)))
     {
     struct dyString *dy = dyStringNew(0);
     // All text before the regex match:
     dyStringAppendN(dy, html, substrs[0].rm_so);
     // Is there an #if before the #insert ?
     boolean doInsert = TRUE;
     if (substrs[1].rm_so != -1 &&
 	(! sameStringN(database, html+substrs[2].rm_so, (substrs[2].rm_eo - substrs[2].rm_so))))
 	doInsert = FALSE;
     if (doInsert)
 	{
 	// Recursively pull in inserted file contents from relative path, replacing regex match:
 	char dir[PATH_LEN];
 	splitPath(fileName, dir, NULL, NULL);
 	char insertFileName[PATH_LEN+FILENAME_LEN];
 	safecpy(insertFileName, sizeof(insertFileName), dir);
 	safencat(insertFileName, sizeof(insertFileName), html+substrs[3].rm_so,
 		 (substrs[3].rm_eo - substrs[3].rm_so));
+        char *varSubFilename = hVarSubst("readHtmlRecursive: var substitution error",
+                                                tdb, database, insertFileName);
+        if (varSubFilename)
+            safecpy(insertFileName, sizeof(insertFileName), varSubFilename);
+        if (differentString(fileName, insertFileName)) // protect against infinite loop
+            {
             if (!fileExists(insertFileName))
                 errAbort("readHtmlRecursive: relative path '%s' (#insert'ed in %s) not found",
                          insertFileName, fileName);
-	char *insertedText = readHtmlRecursive(insertFileName, database);
+            char *insertedText = readHtmlRecursive(insertFileName, database, tdb);
             dyStringAppend(dy, insertedText);
             freez(&insertedText);
             }
+	}
     // All text after the regex match:
     dyStringAppend(dy, html+substrs[0].rm_eo);
     freez(&html);
     html = dyStringCannibalize(&dy);
     }
 return html;
 }
 
 static void layerOnHtml(char *dirName, struct trackDb *tdbList, char *database)
 /* Read in track HTML call bottom-up. */
 {
 char fileName[512];
 struct trackDb *td;
 for (td = tdbList; td != NULL; td = td->next)
     {
     if (isEmpty(td->html))
         {
         char *htmlName = trackDbSetting(td, "html");
         if (htmlName == NULL)
             htmlName = td->track;
 	safef(fileName, sizeof(fileName), "%s/%s.html", dirName, htmlName);
 	if (fileExists(fileName))
             {
-	    td->html = readHtmlRecursive(fileName, database);
+	    td->html = readHtmlRecursive(fileName, database, td);
             // Check for note ASCII characters at higher levels of verboseness.
             // Normally, these are acceptable ISO-8859-1 characters
             if  ((verboseLevel() >= 2) && hasNonAsciiChars(td->html))
                 verbose(2, "Note: non-printing or non-ASCII characters in %s\n", fileName);
             }
         }
     }
 }
 
 static char *subsituteVariables(struct hashEl *el, char *database)
 /* substitute variables where supported */
 {
 char* val = (char*)el->val;
 char *name = el->name;
 /* Only some attribute support variable substitution, at least for now