db7b859addb8e2930d0883f553fb465177459bd4
galt
  Fri Nov 21 12:58:26 2014 -0800
Fixes #14409. Instead of showing only 1 example missing identifier, show up to the first 20.
diff --git src/hg/hgTables/identifiers.c src/hg/hgTables/identifiers.c
index 6c92685..d29cf5e 100644
--- src/hg/hgTables/identifiers.c
+++ src/hg/hgTables/identifiers.c
@@ -368,58 +368,60 @@
 /* Process submit in paste identifiers page. */
 {
 char *idText = trimSpaces(cartString(cart, hgtaPastedIdentifiers));
 htmlOpen("Table Browser (Input Identifiers)");
 if (isNotEmpty(idText))
     {
     /* Write terms to temp file, checking whether they have matches, and
      * save temp file name. */
     boolean saveIdText = (strlen(idText) < MAX_IDTEXT);
     char *idTextForLf = saveIdText ? cloneString(idText) : idText;
     struct lineFile *lf = lineFileOnString("idText", TRUE, idTextForLf);
     char *line, *word;
     struct tempName tn;
     FILE *f;
     int totalTerms = 0, foundTerms = 0;
-    char *exampleMiss = NULL;
+    struct slName* missingTerms = NULL;
+    struct dyString *exampleMissingIds = dyStringNew(256);
     char *actualDb = database;
     if (sameWord(curTable, WIKI_TRACK_TABLE))
 	actualDb = wikiDbName();
     struct hTableInfo *hti = maybeGetHti(actualDb, curTable, conn);
     char *idField = getIdField(actualDb, curTrack, curTable, hti);
     if (idField == NULL)
 	{
 	warn("Sorry, I can't tell which field of table %s to treat as the "
 	     "identifier field.", curTable);
 	webNewSection("Table Browser");
 	cartRemove(cart, hgtaIdentifierDb);
 	cartRemove(cart, hgtaIdentifierTable);
 	cartRemove(cart, hgtaIdentifierFile);
 	mainPageAfterOpen(conn);
 	htmlClose();
 	return;
 	}
     struct slName *allTerms = NULL, *term;
     while (lineFileNext(lf, &line, NULL))
 	{
 	while ((word = nextWord(&line)) != NULL)
 	    {
 	    term = slNameNew(word);
 	    slAddHead(&allTerms, term);
 	    totalTerms++;
 	    }
 	}
+    slReverse(&allTerms);
     lineFileClose(&lf);
     char *extraWhere = NULL;
     int maxIdsInWhere = cartUsualInt(cart, "hgt_maxIdsInWhere", DEFAULT_MAX_IDS_IN_WHERE);
     if (totalTerms > 0 && totalTerms <= maxIdsInWhere)
 	extraWhere = slNameToInExpression(idField, allTerms);
 
     struct lm *lm = lmInit(0);
     struct hash *matchHash = getAllPossibleIds(conn, lm, idField, extraWhere);
     trashDirFile(&tn, "hgtData", "identifiers", ".key");
     f = mustOpen(tn.forCgi, "w");
     for (term = allTerms;  term != NULL;  term = term->next)
 	{
 	struct slName *matchList = NULL, *match;
 	if (matchHash == NULL)
 	    {
@@ -439,58 +441,74 @@
 		    {
 		    match = slNameNew((char *)hel->val);
 		    slAddHead(&matchList, match);
 		    }
 		}
 	    }
 	if (matchList != NULL)
 	    {
 	    foundTerms++;
 	    for (match = matchList;  match != NULL;  match = match->next)
 		{
 		mustWrite(f, match->name, strlen(match->name));
 		mustWrite(f, "\n", 1);
 		}
 	    }
-	else if (exampleMiss == NULL)
+	else 
 	    {
-	    exampleMiss = cloneString(term->name);
+	    slAddHead(&missingTerms, slNameNew(term->name));
 	    }
 	}
+    slReverse(&missingTerms);
     carefulClose(&f);
     cartSetString(cart, hgtaIdentifierDb, database);
     cartSetString(cart, hgtaIdentifierTable, curTable);
     cartSetString(cart, hgtaIdentifierFile, tn.forCgi);
     if (saveIdText)
 	freez(&idTextForLf);
     else
 	cartRemove(cart, hgtaPastedIdentifiers);
-    if (foundTerms < totalTerms)
+    int missingCount = totalTerms - foundTerms;
+    if (missingCount > 0)
 	{
 	char *xrefTable, *aliasField;
 	getXrefInfo(conn, &xrefTable, NULL, &aliasField);
 	boolean xrefIsSame = xrefTable && sameString(curTable, xrefTable);
-	warn("Note: %d of the %d given identifiers (e.g. %s) have no match in "
+	int exampleCount = 0;
+	for (term = missingTerms;  term != NULL;  term = term->next)
+	    {
+	    dyStringPrintf(exampleMissingIds, "%s\n", term->name);
+	    ++exampleCount;
+	    if (exampleCount >= 20)
+		break;
+	    }
+	warn("Note: %d of the %d given identifiers have no match in "
 	     "table %s, field %s%s%s%s%s.  "
 	     "Try the \"describe table schema\" button for more "
-	     "information about the table and field.",
+	     "information about the table and field.\n"
+	     "%d %smissing identifier(s):\n"
+	     "%s\n",
 	     (totalTerms - foundTerms), totalTerms,
-	     exampleMiss, curTable, idField,
+	     curTable, idField,
 	     (xrefTable ? (xrefIsSame ? "" : " or in alias table ") : ""),
 	     (xrefTable ? (xrefIsSame ? "" : xrefTable) : ""),
 	     (xrefTable ? (xrefIsSame ? " or in field " : ", field ") : ""),
-	     (xrefTable ? aliasField : ""));
+	     (xrefTable ? aliasField : ""),
+	     exampleCount,
+	     exampleCount < missingCount ? "example " : "",
+	     dyStringCannibalize(&exampleMissingIds)
+	    );
 	webNewSection("Table Browser");
 	}
     lmCleanup(&lm);
     hashFree(&matchHash);
     }
 else
     {
     cartRemove(cart, hgtaIdentifierFile);
     }
 mainPageAfterOpen(conn);
 htmlClose();
 }
 
 char *identifierFileName()
 /* File name identifiers are in, or NULL if not for curTable or no such file. */