14f04b894268dfdf9a94c8123df088e54683fa2c
kent
  Fri Dec 17 16:48:33 2021 -0800
Fixing tidy() function in strex.

diff --git src/lib/strex.c src/lib/strex.c
index 2736f3d..ba8e4d4 100644
--- src/lib/strex.c
+++ src/lib/strex.c
@@ -1964,30 +1964,44 @@
     {
     idHash = hashNew(0);
     hashAdd(prefixHash, prefix, idHash);
     }
 char *id = hashFindVal(idHash, original);
 if (id == NULL)
     {
     char symbuf[128];
     safef(symbuf, sizeof(symbuf), "%s%d", prefix, idHash->elCount + 1);
     id = lmCloneString(idHash->lm, symbuf);
     hashAdd(idHash, original, id);
     }
 return id;
 }
 
+char *finalMatchToSubstring(char *haystack,char *needle)
+/* Return the final position of needle in haystack */
+{
+char *match = NULL;
+for (;;)
+    {
+    haystack = strstr(haystack, needle);
+    if (haystack == NULL)
+        break;
+    match = haystack;
+    haystack += 1;  // Don't repeat last match
+    }
+return match;
+}
 
 static struct strexEval strexEvalCallBuiltIn(struct strexParse *p, struct strexRun *run)
 /* Handle parse tree generated by call to a built in function. */
 {
 struct strexBuiltIn *builtIn = p->val.builtIn;
 struct strexEval res;
 res.type = builtIn->returnType;
 struct lm *lm = run->lm;
 
 switch (builtIn->func)
     {
     case strexBuiltInTrim:
 	{
         struct strexEval a = strexLocalEval(p->children, run);
 	res.val.s = trimSpaces(a.val.s);
@@ -2141,49 +2155,47 @@
 	res.val.b = (strcmp(a.val.s, b.val.s) == 0);
 	break;
 	}
     case strexBuiltInTidy:
 	{
 	/* Get parameters */
         struct strexEval a = strexLocalEval(p->children, run);
         struct strexEval b = strexLocalEval(p->children->next, run);
         struct strexEval c = strexLocalEval(p->children->next->next, run);
 	char *before = a.val.s;
 	char *orig = b.val.s;
 	char *after = c.val.s;
 
 	/* Figure out start position - start of string if before string is empty or doesn't match 
 	 * otherwise right after the place where before matches. */
-	char *ourStart = NULL;
+	char *ourStart = orig;
 	if (!isEmpty(before))
 	    {
-	    ourStart = strstr(orig, before);
-	    if (ourStart != NULL) 
-		ourStart += strlen(before);
+	    char *newStart = strstr(orig, before);
+	    if (newStart != NULL)
+	        ourStart = newStart + strlen(before);
 	    }
-	if (ourStart == NULL)
-	    ourStart = orig;
 
 	/* Figure out end position */
 	char *defaultEnd = ourStart + strlen(ourStart);
-	char *ourEnd = NULL;
+	char *ourEnd = defaultEnd;
 	if (!isEmpty(after))
 	    {
-	    ourEnd = strstr(ourStart, after);
+	    char *newEnd = finalMatchToSubstring(ourStart, after);
+	    if (newEnd != NULL)
+	        ourEnd = newEnd;
 	    }
-	if (ourEnd == NULL)
-	    ourEnd = defaultEnd;
 
 	int size = ourEnd - ourStart;
 	assert(size >= 0);
 	res.val.s = lmCloneStringZ(lm, ourStart, size);
 	break;
 	}
     case strexBuiltInWarn:
         {
 	/* Figure out the message we want to convey, send it to warning handler
 	 * before returning it. */
         struct strexEval a = strexLocalEval(p->children, run);
 	char *message = a.val.s;
 	char *output = lmJoinStrings(run->lm, "WARNING: ", message);
 	if (run->warnHandler != NULL) run->warnHandler(run->symbols, output);
 	res.val.s = output;