commit 228eda2f77bb9a7cd86e9fc6a5120865980b0875
Author: Eugene Pimenov <libc@mac.com>
Date:   Fri Feb 12 07:58:12 2010 +0300

    Make html parser non-recursive.
    
    * HTMLParser.c: make htmlParseElement non-recursive.
    * include/libxml/parser.h: add the nodeInfo stack
    * parserInternals.c: free nodeInfo stack in xmlFreeParserCtxt

diff --git a/HTMLparser.c b/HTMLparser.c
index 3d4831c..b1b17b9 100644
--- a/HTMLparser.c
+++ b/HTMLparser.c
@@ -209,6 +209,59 @@ htmlnamePop(htmlParserCtxtPtr ctxt)
     return (ret);
 }
 
+/**
+ * htmlNodeInfoPush:
+ * @ctxt:  an HTML parser context
+ * @value:  the node info
+ *
+ * Pushes a new element name on top of the node info stack
+ *
+ * Returns 0 in case of error, the index in the stack otherwise
+ */
+static int
+htmlNodeInfoPush(htmlParserCtxtPtr ctxt, htmlParserNodeInfo *value)
+{
+    if (ctxt->nodeInfoNr >= ctxt->nodeInfoMax) {
+        if (ctxt->nodeInfoMax == 0)
+                ctxt->nodeInfoMax = 5;
+        ctxt->nodeInfoMax *= 2;
+        ctxt->nodeInfoTab = (htmlParserNodeInfo *)
+                         xmlRealloc((htmlParserNodeInfo *)ctxt->nodeInfoTab,
+                                    ctxt->nodeInfoMax *
+                                    sizeof(ctxt->nodeInfoTab[0]));
+        if (ctxt->nodeInfoTab == NULL) {
+            htmlErrMemory(ctxt, NULL);
+            return (0);
+        }
+    }
+    ctxt->nodeInfoTab[ctxt->nodeInfoNr] = *value;
+    ctxt->nodeInfo = &ctxt->nodeInfoTab[ctxt->nodeInfoNr];
+    return (ctxt->nodeInfoNr++);
+}
+
+/**
+ * htmlNodeInfoPop:
+ * @ctxt:  an HTML parser context
+ *
+ * Pops the top element name from the node info stack
+ *
+ * Returns 0 in case of error, the pointer to NodeInfo otherwise
+ */
+static htmlParserNodeInfo *
+htmlNodeInfoPop(htmlParserCtxtPtr ctxt)
+{
+    if (ctxt->nodeInfoNr <= 0)
+        return (NULL);
+    ctxt->nodeInfoNr--;
+    if (ctxt->nodeInfoNr < 0)
+        return (NULL);
+    if (ctxt->nodeInfoNr > 0)
+        ctxt->nodeInfo = &ctxt->nodeInfoTab[ctxt->nodeInfoNr - 1];
+    else
+        ctxt->nodeInfo = NULL;
+    return &ctxt->nodeInfoTab[ctxt->nodeInfoNr];
+}
+
 /*
  * Macros for accessing the content. Those should be used only by the parser,
  * and not exported.
@@ -3922,6 +3975,24 @@ htmlParseReference(htmlParserCtxtPtr ctxt) {
     }
 }
 
+
+static void htmlParserFinishElementParsing(htmlParserCtxtPtr ctxt) {
+    /*
+     * Capture end position and add node
+     */
+    if ( ctxt->node != NULL && ctxt->record_info ) {
+       ctxt->nodeInfo->end_pos = ctxt->input->consumed +
+                                (CUR_PTR - ctxt->input->base);
+       ctxt->nodeInfo->end_line = ctxt->input->line;
+       ctxt->nodeInfo->node = ctxt->node;
+       xmlParserAddNodeInfo(ctxt, ctxt->nodeInfo);
+       htmlNodeInfoPop(ctxt);
+    }
+    if (!IS_CHAR_CH(CUR)) {
+       htmlAutoCloseOnEnd(ctxt);
+    }
+}
+
 /**
  * htmlParseContent:
  * @ctxt:  an HTML parser context
@@ -3953,7 +4024,9 @@ htmlParseContent(htmlParserCtxtPtr ctxt) {
 		((currentNode != NULL) || (ctxt->nameNr == 0))) {
 		if (currentNode != NULL)
 		    xmlFree(currentNode);
-		return;
+
+	        currentNode = xmlStrdup(ctxt->name);
+	        depth = ctxt->nameNr;
 	    }
 	    continue; /* while */
         }
@@ -3967,12 +4040,16 @@ htmlParseContent(htmlParserCtxtPtr ctxt) {
 			 "htmlParseStartTag: invalid element name\n",
 			 NULL, NULL);
 	        /* Dump the bogus tag like browsers do */
-        while ((IS_CHAR_CH(CUR)) && (CUR != '>'))
+	        while ((IS_CHAR_CH(CUR)) && (CUR != '>'))
 	            NEXT;
 
+	        htmlParserFinishElementParsing(ctxt);
 	        if (currentNode != NULL)
 	            xmlFree(currentNode);
-	        return;
+
+	        currentNode = xmlStrdup(ctxt->name);
+	        depth = ctxt->nameNr;
+	        continue;
 	    }
 
 	    if (ctxt->name != NULL) {
@@ -3990,8 +4067,12 @@ htmlParseContent(htmlParserCtxtPtr ctxt) {
         if ((ctxt->nameNr > 0) && (depth >= ctxt->nameNr) &&
 	    (!xmlStrEqual(currentNode, ctxt->name)))
 	     {
+	    htmlParserFinishElementParsing(ctxt);
 	    if (currentNode != NULL) xmlFree(currentNode);
-	    return;
+
+	    currentNode = xmlStrdup(ctxt->name);
+	    depth = ctxt->nameNr;
+	    continue;
 	}
 
 	if ((CUR != 0) && ((xmlStrEqual(currentNode, BAD_CAST"script")) ||
@@ -4035,6 +4116,10 @@ htmlParseContent(htmlParserCtxtPtr ctxt) {
 	     */
 	    else if (CUR == '<') {
 		htmlParseElement(ctxt);
+		if (currentNode != NULL) xmlFree(currentNode);
+
+		currentNode = xmlStrdup(ctxt->name);
+		depth = ctxt->nameNr;
 	    }
 
 	    /*
@@ -4087,11 +4172,12 @@ __htmlParseContent(void *ctxt) {
 	htmlParseContent((htmlParserCtxtPtr) ctxt);
 }
 
+
 /**
  * htmlParseElement:
  * @ctxt:  an HTML parser context
  *
- * parse an HTML element, this is highly recursive
+ * parse an HTML element
  *
  * [39] element ::= EmptyElemTag | STag content ETag
  *
@@ -4101,7 +4187,6 @@ __htmlParseContent(void *ctxt) {
 void
 htmlParseElement(htmlParserCtxtPtr ctxt) {
     const xmlChar *name;
-    xmlChar *currentNode = NULL;
     const htmlElemDesc * info;
     htmlParserNodeInfo node_info;
     int failed;
@@ -4166,16 +4251,9 @@ htmlParseElement(htmlParserCtxtPtr ctxt) {
 	    htmlnamePop(ctxt);
 	}
 
-	/*
-	 * Capture end position and add node
-	 */
-	if (ctxt->record_info) {
-	   node_info.end_pos = ctxt->input->consumed +
-			      (CUR_PTR - ctxt->input->base);
-	   node_info.end_line = ctxt->input->line;
-	   node_info.node = ctxt->node;
-	   xmlParserAddNodeInfo(ctxt, &node_info);
-	}
+        if (ctxt->record_info)
+            htmlNodeInfoPush(ctxt, &node_info);
+        htmlParserFinishElementParsing(ctxt);
 	return;
     }
 
@@ -4189,34 +4267,8 @@ htmlParseElement(htmlParserCtxtPtr ctxt) {
 	return;
     }
 
-    /*
-     * Parse the content of the element:
-     */
-    currentNode = xmlStrdup(ctxt->name);
-    depth = ctxt->nameNr;
-    while (IS_CHAR_CH(CUR)) {
-	oldptr = ctxt->input->cur;
-	htmlParseContent(ctxt);
-	if (oldptr==ctxt->input->cur) break;
-	if (ctxt->nameNr < depth) break;
-    }
-
-    /*
-     * Capture end position and add node
-     */
-    if ( currentNode != NULL && ctxt->record_info ) {
-       node_info.end_pos = ctxt->input->consumed +
-                          (CUR_PTR - ctxt->input->base);
-       node_info.end_line = ctxt->input->line;
-       node_info.node = ctxt->node;
-       xmlParserAddNodeInfo(ctxt, &node_info);
-    }
-    if (!IS_CHAR_CH(CUR)) {
-	htmlAutoCloseOnEnd(ctxt);
-    }
-
-    if (currentNode != NULL)
-	xmlFree(currentNode);
+    if (ctxt->record_info)
+        htmlNodeInfoPush(ctxt, &node_info);
 }
 
 /**
@@ -4440,6 +4492,10 @@ htmlInitParserCtxt(htmlParserCtxtPtr ctxt)
     ctxt->nameMax = 10;
     ctxt->name = NULL;
 
+    ctxt->nodeInfoTab = NULL;
+    ctxt->nodeInfoNr  = 0;
+    ctxt->nodeInfoMax = 0;
+
     if (sax == NULL) ctxt->sax = (xmlSAXHandlerPtr) &htmlDefaultSAXHandler;
     else {
         ctxt->sax = sax;
diff --git a/include/libxml/parser.h b/include/libxml/parser.h
index 148ee03..b9ccaca 100644
--- a/include/libxml/parser.h
+++ b/include/libxml/parser.h
@@ -209,6 +209,10 @@ struct _xmlParserCtxt {
 
     int record_info;                  /* Whether node info should be kept */
     xmlParserNodeInfoSeq node_seq;    /* info about each node parsed */
+    xmlParserNodeInfo *nodeInfo;      /* Current NodeInfo */
+    int                nodeInfoNr;    /* Depth of the parsing stack */
+    int                nodeInfoMax;   /* Max depth of the parsing stack */
+    xmlParserNodeInfo *nodeInfoTab;   /* array of nodeInfos */
 
     int errNo;                        /* error code */
 
diff --git a/parserInternals.c b/parserInternals.c
index ff20435..2404ddf 100644
--- a/parserInternals.c
+++ b/parserInternals.c
@@ -1782,6 +1782,7 @@ xmlFreeParserCtxt(xmlParserCtxtPtr ctxt)
     if (ctxt->spaceTab != NULL) xmlFree(ctxt->spaceTab);
     if (ctxt->nameTab != NULL) xmlFree((xmlChar * *)ctxt->nameTab);
     if (ctxt->nodeTab != NULL) xmlFree(ctxt->nodeTab);
+    if (ctxt->nodeInfoTab != NULL) xmlFree(ctxt->nodeInfoTab);
     if (ctxt->inputTab != NULL) xmlFree(ctxt->inputTab);
     if (ctxt->version != NULL) xmlFree((char *) ctxt->version);
     if (ctxt->encoding != NULL) xmlFree((char *) ctxt->encoding);
