fd81dd0a7889fb72292a9e74cccede7f5255faef
galt
  Sat Feb 25 17:49:20 2017 -0800
adding SVG self-closing tag capability to htmlCheck.

diff --git src/lib/htmlPage.c src/lib/htmlPage.c
index a4a300a..e496736 100644
--- src/lib/htmlPage.c
+++ src/lib/htmlPage.c
@@ -478,30 +478,43 @@
 }
 
 char *htmlTagAttributeNeeded(struct htmlPage *page, struct htmlTag *tag, char *name)
 /* Return named tag attribute.  Complain and return "n/a" if it
  * doesn't exist. */
 {
 char *val = htmlTagAttributeVal(page, tag, name, NULL);
 if (val == NULL)
     {
     tagWarn(page, tag, "Missing %s attribute", name);
     val = "n/a";
     }
 return val;
 }
 
+boolean isSelfClosingTag(struct htmlTag *tag)
+/* Return strue if last attributes' name is "/" 
+ * Self-closing tags are used with html5 and SGV */
+{
+struct htmlAttribute *att = tag->attributes;
+if (!att)
+    return FALSE;
+while (att->next) att = att->next;
+if (sameString(att->name,"/"))
+    return TRUE;
+return FALSE;
+}
+
 static struct htmlTag *htmlTagScan(char *html, char *dupe)
 /* Scan HTML for tags and return a list of them. 
  * Html is the text to scan, and dupe is a copy of it
  * which this routine will insert 0's in in the course of
  * parsing.*/
 {
 char *s = dupe, c, *e, *tagName;
 struct htmlTag *tagList = NULL, *tag;
 struct htmlAttribute *att;
 int pos;
 
 for (;;)
     {
     c = *s++;
     if (c == 0)
@@ -1677,30 +1690,42 @@
 "BASE",
 "BR",
 "COL",
 "COMMAND",
 "EMBED",
 "FRAME",  // not in html5
 "HR",
 "IMG",
 "INPUT",
 "LINK",
 "META",
 "PARAM",
 "SOURCE"
 };
 
+static char *selfClosers[] =
+/* Tags which can be optionally self-closing in html5 or SVG. */
+{
+"CIRCLE",   // SVG
+"ELLIPSE",  // SVG
+"LINE",     // SVG
+"PATH",     // SVG
+"POLYGON",  // SVG
+"POLYLINE", // SVG
+"RECT"      // SVG
+};
+
 static struct htmlTag *validateBody(struct htmlPage *page, struct htmlTag *startTag)
 /* Go through tags from current position (just past <BODY>)
  * up to and including </BODY> and check some things. */
 {
 struct htmlTag *tag, *endTag = NULL;
 
 /* First search for end tag. */
 for (tag = startTag; tag != NULL; tag = tag->next)
     {
     if (sameWord(tag->name, "/BODY"))
         {
 	endTag = tag;
 	break;
 	}
     }
@@ -1884,57 +1909,66 @@
     if (tag == NULL || !sameWord(tag->name, "/HTML"))
 	errAbort("Missing </HTML>");
     validateCgiUrls(page);
     }
 }
 
 void htmlPageStrictTagNestCheck(struct htmlPage *page)
 /* Do strict tag nesting check.  Aborts if there is a problem. */
 {
 struct htmlTag *tag;
 /* To simplify things upper case all tag names. */
 for (tag = page->tags; tag != NULL; tag = tag->next)
     touppers(tag->name);
 
 /* Add singleton tags to hash. */
-struct hash *hash = hashNew(8);
+struct hash *singleTonHash = hashNew(8);
 int i;
-int nesterCount=ArraySize(singleTons);
-for (i=0; i<nesterCount; ++i)
-    hashAdd(hash, singleTons[i], NULL);
+int count=ArraySize(singleTons);
+for (i=0; i<count; ++i)
+    hashAdd(singleTonHash, singleTons[i], NULL);
+
+/* Add selfCloser tags to hash. */
+struct hash *selfCloserHash = hashNew(8);
+count=ArraySize(selfClosers);
+for (i=0; i<count; ++i)
+    hashAdd(selfCloserHash, selfClosers[i], NULL);
 
 struct slName *tagStack = NULL;
 for (tag = page->tags; tag != NULL; tag = tag->next)
     {
     if (startsWith("/", tag->name))
 	{
-	if (hashLookup(hash, tag->name+1))
+	if (hashLookup(singleTonHash, tag->name+1))
 	    tagAbort(page, tag, "Tag %s closing tag not allowed for singleton tags.", tag->name);
 	if (!sameString("P", tag->name+1))
 	    {
 	    if (!tagStack)
 		tagAbort(page, tag, "No tags still left on stack. Closing tag %s has no corresponding open tag.", tag->name);
 	    struct slName *top = slPopHead(&tagStack);
 	    // flush LI tags still on stack when /UL or /OL encountered
 	    // since the missing /LI tags are usually tolerated. 
 	    while ((sameString(tag->name, "/UL") || sameString(tag->name, "/OL")) && sameString(top->name,"LI"))
 		{
 		tagWarn(page, tag, "Closing tag %s found. LI tag on stack. Missing /LI tag. Please fix. Continuing.", tag->name);
 		top = slPopHead(&tagStack);
 		}
 	    if (!sameString(top->name,tag->name+1))
 		{
 		tagAbort(page, tag, "Closing tag %s found, tag %s at top of stack.", tag->name, top->name);
 		}
 	    }
 	}
     else
 	{
-	if (!hashLookup(hash, tag->name) && !sameString("P", tag->name))
+	if (
+	    ! hashLookup(singleTonHash, tag->name) 
+	 && !(hashLookup(selfCloserHash, tag->name) && isSelfClosingTag(tag))
+         && ! sameString("P", tag->name))
 	    {
 	    slAddHead(&tagStack, slNameNew(tag->name));
 	    }	    
 	}	    
     }
 if (tagStack)
     errAbort("Some tags still left on stack. Open tag %s missing its closing tag.", tagStack->name);
 }