99e615846246a19fdb5d01fcea5dce49179dd2ba
braney
  Tue Sep 22 10:52:28 2026 -0700
htmlPage: parse valueless tag attributes such as SELECTED

The attribute name scanner read everything up to the next equals sign, so
<OPTION SELECTED value='any'> became a single attribute named "SELECTED
value".  A lookup for SELECTED then found nothing and the SELECT fell back
to its first option.  The same happened to CHECKED on a checkbox, and to the
src in <script async src=...>.  A name now ends at white space as well, with
a look-ahead so that spaces around the equals sign still work.

This is what put 21 soft errors in every hgNearTest robot run since v495.

refs #38413

diff --git src/lib/htmlPage.c src/lib/htmlPage.c
index d1f28e491b3..6a8ec4e1414 100644
--- src/lib/htmlPage.c
+++ src/lib/htmlPage.c
@@ -568,41 +568,54 @@
 	    for (;;)
 		{
 		char *name, *val;
 		boolean gotEnd = FALSE;
 
 		/* Check for end tag. */
 		s = skipLeadingSpaces(s);
 		if (s[0] == '>' || s[0] == 0)
 		    {
 		    tag->end = html + (s - dupe);
 		    if (s[0] == '>')
 			tag->end += 1;
 		    break;
 		    }
 
-		/* Get name - everything up to equals. */
+		/* Get name - everything up to equals, white space or tag end.
+		 * A name that ends at white space belongs to a valueless
+		 * attribute such as the SELECTED in <OPTION SELECTED value='x'>,
+		 * unless the equals is merely separated from it by spaces. */
 		e = s;
 		for (;;)
 		    {
 		    c = *e;
 		    if (c == '=')
 		        break;
 		    else if (c == '>')
 		        break;
 		    else if (c == 0)
 		        break;
+		    else if (isspace(c))
+		        {
+			char *afterSpaces = skipLeadingSpaces(e);
+			if (*afterSpaces == '=')
+			    {
+			    e = afterSpaces;
+			    c = *e;
+			    }
+			break;
+			}
 		    e += 1;
 		    }
 		if (c == 0)
 		    {
 		    warn("End of file in tag");
 		    break;
 		    }
 		name = s;
 		*e++ = 0;
 		eraseTrailingSpaces(name);
 		if (c == '>')
 		    {
 		    val = "";
 		    gotEnd = TRUE;
 		    tag->end = html + (e - dupe);