3b42c8080b54ad87a6c37efbb3a78a0781b7e8ac
angie
  Fri Jan 11 10:35:21 2019 -0800
Fixing unencoded HTML characters caught by Daniel's testing.  Also, when we quit, there might be thousands of errors left in the file, so say 'At least N errors...'.  refs #22638

diff --git src/hg/lib/cart.c src/hg/lib/cart.c
index 1779f82..c1910f3 100644
--- src/hg/lib/cart.c
+++ src/hg/lib/cart.c
@@ -788,55 +788,57 @@
              errorSum > CART_LOAD_TOO_MANY_ERRORS &&
              stats->validCount < CART_LOAD_ENOUGH_VALID) ||
             errorSum > CART_LOAD_WAY_TOO_MANY_ERRORS);
     }
 return FALSE;
 }
 
 #define CART_VAR_MAX_LENGTH 1024
 #define CART_VAL_MAX_LENGTH (64 * 1024)
 
 static void vsReport(struct validityStats *stats, struct dyString *dyMessage)
 /* Append summary/explanation to dyMessage.   */
 {
 if (stats && dyMessage)
     {
+    boolean quitting = vsTooManyErrors(stats);
+    char *atLeast = (quitting ? "At least " : "");
     dyStringPrintf(dyMessage, "<br>%d valid settings found.  ", stats->validCount);
     if (stats->binaryCount || stats->weirdCharsCount || stats->dataCount ||
         stats->varTooLongCount || stats->valTooLongCount)
         dyStringPrintf(dyMessage, "<b>Note: invalid settings were found and omitted.</b>  ");
     if (stats->binaryCount)
-        dyStringPrintf(dyMessage, "%d setting names contained binary data.  ",
-                       stats->binaryCount);
+        dyStringPrintf(dyMessage, "%s%d setting names contained binary data.  ",
+                       atLeast, stats->binaryCount);
     if (stats->weirdCharsCount)
         dyStringPrintf(dyMessage,
-                       "%d setting names contained unexpected characters, for example '%s'.  ",
-                       stats->weirdCharsCount, stats->weirdCharsExample);
+                       "%s%d setting names contained unexpected characters, for example '%s'.  ",
+                       atLeast, stats->weirdCharsCount, htmlEncode(stats->weirdCharsExample));
     if (stats->dataCount)
-        dyStringPrintf(dyMessage, "%d lines appeared to be custom track data, for example "
+        dyStringPrintf(dyMessage, "%s%d lines appeared to be custom track data, for example "
                        "a line begins with '%s'.  ",
-                       stats->dataCount, stats->dataExample);
+                       atLeast, stats->dataCount, stats->dataExample);
     if (stats->varTooLongCount)
-        dyStringPrintf(dyMessage, "%d setting names were too long (up to %d).  ",
-                       stats->varTooLongCount, stats->varTooLongLength);
+        dyStringPrintf(dyMessage, "%s%d setting names were too long (up to %d).  ",
+                       atLeast, stats->varTooLongCount, stats->varTooLongLength);
     if (stats->valTooLongCount)
-        dyStringPrintf(dyMessage, "%d setting values were too long (up to %d).  ",
-                       stats->valTooLongCount, stats->valTooLongLength);
-    }
-if (vsTooManyErrors(stats))
+        dyStringPrintf(dyMessage, "%s%d setting values were too long (up to %d).  ",
+                       atLeast, stats->valTooLongCount, stats->valTooLongLength);
+    if (quitting)
         dyStringPrintf(dyMessage, "Encountered too many errors -- quitting.  ");
     }
+}
 
 // Our timestamp vars (_, hgt_) are an exception to the usual cart var naming patterns:
 #define CART_VAR_TIMESTAMP "^([a-z]+)?_$"
 // Legitimate cart vars look like this (but so do some not-vars, so we filter further below):
 #define CART_VAR_VALID_CHARACTERS "^[A-Za-z]([A-Za-z0-9._:-]*[A-Za-z0-9]+)?$"
 
 // These are "cart variables" that are actually custom track data:
 static char *cartVarBlackList[] = { "X",
                                     "Y",
                                     "MT",
                                     "fixedStep",
                                     "variableStep",
                                    };
 
 // Prefixes of "cart variables" from data files that have caused trouble in the past: