Attribute nomarlization closing bug #3597
Small fixes in encoding.c
First bits of real progressive parsing,
   Daniel
diff --git a/ChangeLog b/ChangeLog
index d9dc9d2..f677325 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Wed Nov 17 18:28:06 CET 1999
+
+	* encoding.c: bug fix and typos
+	* xmlIO.[ch] parser.c: first bits toward real progressive parsing
+	* parser.c: added attribute normalization closing bug #3597
+	* test/att* result/att* SAXresult/att*: testcase for attribute
+	    normalization
+
 Mon Nov 15 18:50:56 CET 1999 Daniel Veillard <Daniel.Veillard@w3.org>
 
 	* configure.in: closing bug #3163 by adding extra flags for the
diff --git a/SAXresult/att1 b/SAXresult/att1
new file mode 100644
index 0000000..125e1b2
--- /dev/null
+++ b/SAXresult/att1
@@ -0,0 +1,5 @@
+SAX.setDocumentLocator()
+SAX.startDocument()
+SAX.startElement(doc, attr='to normalize with a space')
+SAX.endElement(doc)
+SAX.endDocument()
diff --git a/SAXresult/att2 b/SAXresult/att2
new file mode 100644
index 0000000..125e1b2
--- /dev/null
+++ b/SAXresult/att2
@@ -0,0 +1,5 @@
+SAX.setDocumentLocator()
+SAX.startDocument()
+SAX.startElement(doc, attr='to normalize with a space')
+SAX.endElement(doc)
+SAX.endDocument()
diff --git a/encoding.c b/encoding.c
index 5169cde..50deb2c 100644
--- a/encoding.c
+++ b/encoding.c
@@ -51,10 +51,10 @@
 
 /**
  * isolat1ToUTF8:
- * @out:  a pointer ot an array of bytes to store the result
- * @outlen:  the lenght of @out
- * @in:  a pointer ot an array of ISO Latin 1 chars
- * @inlen:  the lenght of @in
+ * @out:  a pointer to an array of bytes to store the result
+ * @outlen:  the length of @out
+ * @in:  a pointer to an array of ISO Latin 1 chars
+ * @inlen:  the length of @in
  *
  * Take a block of ISO Latin 1 chars in and try to convert it to an UTF-8
  * block of chars out.
@@ -86,10 +86,10 @@
 
 /**
  * UTF8Toisolat1:
- * @out:  a pointer ot an array of bytes to store the result
- * @outlen:  the lenght of @out
- * @in:  a pointer ot an array of UTF-8 chars
- * @inlen:  the lenght of @in
+ * @out:  a pointer to an array of bytes to store the result
+ * @outlen:  the length of @out
+ * @in:  a pointer to an array of UTF-8 chars
+ * @inlen:  the length of @in
  *
  * Take a block of UTF-8 chars in and try to convert it to an ISO Latin 1
  * block of chars out.
@@ -123,10 +123,10 @@
 
 /**
  * UTF16ToUTF8:
- * @out:  a pointer ot an array of bytes to store the result
- * @outlen:  the lenght of @out
- * @in:  a pointer ot an array of UTF-16 chars (array of unsigned shorts)
- * @inlen:  the lenght of @in
+ * @out:  a pointer to an array of bytes to store the result
+ * @outlen:  the length of @out
+ * @in:  a pointer to an array of UTF-16 chars (array of unsigned shorts)
+ * @inlen:  the length of @in
  *
  * Take a block of UTF-16 ushorts in and try to convert it to an UTF-8
  * block of chars out.
@@ -161,7 +161,7 @@
         else if (c < 0x10000) {  *out++= (c >> 12) | 0xE0;  bits=  6; }
         else                  {  *out++= (c >> 18) | 0xF0;  bits= 12; }
  
-        for ( ; bits < 0; bits-= 6) {
+        for ( ; bits > 0; bits-= 6) {
             if (out >= outend)  return -1;
             *out++= (c >> bits) & 0x3F;
         }
@@ -171,10 +171,10 @@
 
 /**
  * UTF8ToUTF16:
- * @out:  a pointer ot an array of shorts to store the result
- * @outlen:  the lenght of @out (number of shorts)
- * @in:  a pointer ot an array of UTF-8 chars
- * @inlen:  the lenght of @in
+ * @out:  a pointer to an array of shorts to store the result
+ * @outlen:  the length of @out (number of shorts)
+ * @in:  a pointer to an array of UTF-8 chars
+ * @inlen:  the length of @in
  *
  * Take a block of UTF-8 chars in and try to convert it to an UTF-16
  * block of chars out.
@@ -264,7 +264,7 @@
 
 /**
  * xmlParseCharEncoding:
- * @name:  the encoding name as parsed, in UTF-8 format (ASCCI actually)
+ * @name:  the encoding name as parsed, in UTF-8 format (ASCII actually)
  *
  * Conpare the string to the known encoding schemes already known. Note
  * that the comparison is case insensitive accordingly to the section
@@ -351,7 +351,7 @@
 
 /**
  * xmlNewCharEncodingHandler:
- * @name:  the encoding name, in UTF-8 format (ASCCI actually)
+ * @name:  the encoding name, in UTF-8 format (ASCII actually)
  * @input:  the xmlCharEncodingInputFunc to read that encoding
  * @output:  the xmlCharEncodingOutputFunc to write that encoding
  *
@@ -409,7 +409,7 @@
  *
  * Initialize the char encoding support, it registers the default
  * encoding supported.
- * NOTE: while public theis function usually don't need to be called
+ * NOTE: while public, this function usually doesn't need to be called
  *       in normal processing.
  */
 void
diff --git a/include/libxml/xmlIO.h b/include/libxml/xmlIO.h
index a99ab23..58baeb0 100644
--- a/include/libxml/xmlIO.h
+++ b/include/libxml/xmlIO.h
@@ -50,6 +50,9 @@
 						 int len);
 int	xmlParserInputBufferGrow		(xmlParserInputBufferPtr in,
 						 int len);
+int	xmlParserInputBufferPush		(xmlParserInputBufferPtr in,
+						 int len,
+						 char *buf);
 void	xmlFreeParserInputBuffer		(xmlParserInputBufferPtr in);
 char *	xmlParserGetDirectory			(const char *filename);
 
diff --git a/parser.c b/parser.c
index 207e6496..3491efb 100644
--- a/parser.c
+++ b/parser.c
@@ -2497,58 +2497,146 @@
  * [10] AttValue ::= '"' ([^<&"] | Reference)* '"' |
  *                   "'" ([^<&'] | Reference)* "'"
  *
- * Returns the AttValue parsed or NULL.
+ * 3.3.3 Attribute-Value Normalization:
+ * Before the value of an attribute is passed to the application or
+ * checked for validity, the XML processor must normalize it as follows: 
+ * - a character reference is processed by appending the referenced
+ *   character to the attribute value
+ * - an entity reference is processed by recursively processing the
+ *   replacement text of the entity 
+ * - a whitespace character (#x20, #xD, #xA, #x9) is processed by
+ *   appending #x20 to the normalized value, except that only a single
+ *   #x20 is appended for a "#xD#xA" sequence that is part of an external
+ *   parsed entity or the literal entity value of an internal parsed entity 
+ * - other characters are processed by appending them to the normalized value 
+ *
+ * Returns the AttValue parsed or NULL. The value has to be freed by the caller.
  */
 
 xmlChar *
 xmlParseAttValue(xmlParserCtxtPtr ctxt) {
-    xmlChar *ret = NULL;
+    xmlChar limit = 0;
+    xmlChar *buffer = NULL;
+    int buffer_size = 0;
+    xmlChar *out = NULL;
+
+    xmlChar *current = NULL;
+    xmlEntityPtr ent;
+    xmlChar cur;
+    int blank = 0;
+
 
     SHRINK;
     if (CUR == '"') {
 	ctxt->instate = XML_PARSER_ATTRIBUTE_VALUE;
+	limit = '"';
         NEXT;
-	ret = xmlDecodeEntities(ctxt, -1, XML_SUBSTITUTE_REF, '"', '<', 0);
-	if (CUR == '<') {
-	    if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
-		ctxt->sax->error(ctxt->userData,
-		   "Unescaped '<' not allowed in attributes values\n");
-	    ctxt->errNo = XML_ERR_LT_IN_ATTRIBUTE;
-	    ctxt->wellFormed = 0;
-	}
-        if (CUR != '"') {
-	    if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
-		ctxt->sax->error(ctxt->userData, "AttValue: ' expected\n");
-	    ctxt->errNo = XML_ERR_ATTRIBUTE_NOT_FINISHED;
-	    ctxt->wellFormed = 0;
-	} else
-	    NEXT;
     } else if (CUR == '\'') {
+	limit = '\'';
 	ctxt->instate = XML_PARSER_ATTRIBUTE_VALUE;
         NEXT;
-	ret = xmlDecodeEntities(ctxt, -1, XML_SUBSTITUTE_REF, '\'', '<', 0);
-	if (CUR == '<') {
-	    if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
-		ctxt->sax->error(ctxt->userData,
-		   "Unescaped '<' not allowed in attributes values\n");
-	    ctxt->errNo = XML_ERR_LT_IN_ATTRIBUTE;
-	    ctxt->wellFormed = 0;
-	}
-        if (CUR != '\'') {
-	    if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
-		ctxt->sax->error(ctxt->userData, "AttValue: ' expected\n");
-	    ctxt->errNo = XML_ERR_ATTRIBUTE_NOT_FINISHED;
-	    ctxt->wellFormed = 0;
-	} else
-	    NEXT;
     } else {
 	ctxt->errNo = XML_ERR_ATTRIBUTE_NOT_STARTED;
 	if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
 	    ctxt->sax->error(ctxt->userData, "AttValue: \" or ' expected\n");
 	ctxt->wellFormed = 0;
+	return(NULL);
     }
     
-    return(ret);
+    /*
+     * allocate a translation buffer.
+     */
+    buffer_size = 100;
+    buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
+    if (buffer == NULL) {
+	perror("xmlParseAttValue: malloc failed");
+	return(NULL);
+    }
+    out = buffer;
+
+    /*
+     * Ok loop until we reach one of the ending char or a size limit.
+     */
+    cur = CUR;
+    while ((cur != limit) && (cur != '<')) {
+
+	if (cur == 0) break;
+        if ((cur == '&') && (NXT(1) == '#')) {
+	    int val = xmlParseCharRef(ctxt);
+	    *out++ = val;
+	    blank = 0;
+	} else if (cur == '&') {
+	    ent = xmlParseEntityRef(ctxt);
+	    if ((ent != NULL) && 
+		(ctxt->replaceEntities != 0)) {
+		current = ent->content;
+		while (*current != 0) {
+		    *out++ = *current++;
+		    if (out - buffer > buffer_size - 10) {
+			int index = out - buffer;
+
+			growBuffer(buffer);
+			out = &buffer[index];
+		    }
+		}
+	    } else if (ent != NULL) {
+		int i = xmlStrlen(ent->name);
+		const xmlChar *cur = ent->name;
+
+		*out++ = '&';
+		if (out - buffer > buffer_size - i - 10) {
+		    int index = out - buffer;
+
+		    growBuffer(buffer);
+		    out = &buffer[index];
+		}
+		for (;i > 0;i--)
+		    *out++ = *cur++;
+		*out++ = ';';
+	    }
+	    blank = 0;
+	} else {
+	    /*  invalid for UTF-8 , use COPY(out); !!!!!! */
+	    if ((cur == 0x20) || (cur == 0xD) || (cur == 0xA) || (cur == 0x9)) {
+	        if (!blank) {
+		    *out++ = 0x20;
+		    if (out - buffer > buffer_size - 10) {
+		      int index = out - buffer;
+		      
+		      growBuffer(buffer);
+		      out = &buffer[index];
+		    }
+		}
+		blank = 1;
+	    } else {
+		*out++ = cur;
+		if (out - buffer > buffer_size - 10) {
+		  int index = out - buffer;
+		  
+		  growBuffer(buffer);
+		  out = &buffer[index];
+		}
+		blank = 0;
+	    }
+	    NEXT;
+	}
+	cur = CUR;
+    }
+    *out++ = 0;
+    if (CUR == '<') {
+	if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+	    ctxt->sax->error(ctxt->userData,
+	       "Unescaped '<' not allowed in attributes values\n");
+	ctxt->errNo = XML_ERR_LT_IN_ATTRIBUTE;
+	ctxt->wellFormed = 0;
+    } else if (CUR != limit) {
+	if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+	    ctxt->sax->error(ctxt->userData, "AttValue: ' expected\n");
+	ctxt->errNo = XML_ERR_ATTRIBUTE_NOT_FINISHED;
+	ctxt->wellFormed = 0;
+    } else
+	NEXT;
+    return(buffer);
 }
 
 /**
@@ -4962,7 +5050,7 @@
  *
  * [NS 10] EmptyElement ::= '<' QName (S Attribute)* S? '/>'
  *
- * Returns the element name parsed
+ * Returne the element name parsed
  */
 
 xmlChar *
@@ -5988,6 +6076,80 @@
 
 /************************************************************************
  *									*
+ * 		Progressive parsing interfaces				*
+ *									*
+ ************************************************************************/
+
+/**
+ * xmlParseLookupSequence:
+ * @ctxt:  an XML parser context
+ * @first:  the first char to lookup
+ * @next:  the next char to lookup
+ *
+ * Try to find if a sequence (first, next) or  just (first) if next
+ * is zero is available in the input stream.
+ * Since XML-1.0 is an LALR(2) grammar a sequence of 2 char should be
+ * enought. If this doesn't prove true this function call may change.
+ *
+ * Returns 1 if the full sequence is available, 0 otherwise.
+ */
+int
+xmlParseLookupSequence(xmlParserCtxtPtr ctxt, xmlChar first, xmlChar next) {
+    return(0);
+}
+
+/**
+ * xmlParseTry:
+ * @ctxt:  an XML parser context
+ *
+ * Try to progress on parsing
+ *
+ * Returns zero if no parsing was possible
+ */
+int
+xmlParseTry(xmlParserCtxtPtr ctxt) {
+    int ret = 0;
+
+    while (1) {
+        switch (ctxt->instate) {
+            case XML_PARSER_EOF:
+	        return(0);
+            case XML_PARSER_PROLOG:
+            case XML_PARSER_CONTENT:
+            case XML_PARSER_ENTITY_DECL:
+            case XML_PARSER_ENTITY_VALUE:
+            case XML_PARSER_ATTRIBUTE_VALUE:
+            case XML_PARSER_DTD:
+            case XML_PARSER_EPILOG:
+            case XML_PARSER_COMMENT:
+            case XML_PARSER_CDATA_SECTION:
+	}
+    }
+    return(ret);
+}
+
+/**
+ * xmlParseChunk:
+ * @ctxt:  an XML parser context
+ * @chunk:  an char array
+ * @size:  the size in byte of the chunk
+ * @terminate:  last chunk indicator
+ *
+ * Parse a Chunk of memory
+ *
+ * Returns zero if no error, the xmlParserErrors otherwise.
+ */
+xmlParserErrors
+xmlParseChunk(xmlParserCtxtPtr ctxt, const char *chunk, int size,
+              int terminate) {
+    if ((size > 0) && (chunk != NULL)) {	      
+	xmlParserInputBufferPush(ctxt->input, size, chunk);	      
+    }
+    return((xmlParserErrors) ctxt->errNo);	      
+}
+
+/************************************************************************
+ *									*
  * 		I/O front end functions to the parser			*
  *									*
  ************************************************************************/
diff --git a/result/SVG/flower2.xml b/result/SVG/flower2.xml
index f8970db..b34513d 100644
--- a/result/SVG/flower2.xml
+++ b/result/SVG/flower2.xml
@@ -2,42 +2,9 @@
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG April 1999//EN" "http://www.w3.org/Graphics/SVG/svg-19990412.dtd">
 <svg width="800px" height="800px">
   <desc>This sample SVG file draws a flower</desc>
-  <g style="transform: matrix(1 0 0 -1 -25.88 798.60);

-     stroke: #000; stroke-width: 1">
-    <path style="fill: #1A5466" d="m 242.33 684.19

-          C 346.44 757.48 271.45 647.38 213.17 641.37

-          C 271.45 647.38 383.43 575.21 256.71 613.30

-          C 383.43 575.21 251.04 565.90 205.23 609.68

-          C 251.04 565.90 265.13 432.88 210.71 557.95

-          C 265.13 432.88 175.04 531.37 175.67 596.26

-          C 175.04 531.37 80.63  437.67 138.96 559.82

-          C  80.63 437.67 100.67 569.80 146.75 611.20

-          C 100.67 569.80 -31.14 585.98 95.49  617.49

-          C -31.14 585.98 83.94  652.25 140.24 643.26

-          C 83.94  652.25 13.98  766.12 113.04 687.55

-          C 13.98  766.12 137.45 716.63 161.05 668.30

-          C 137.45 716.63 182.02 842.45 178.39 717.23

-          C 182.02 842.45 220.90 714.46 193.51 667.46

-          C 220.90 714.46 346.44 757.48 242.33 684.19 z"/>
-    <path style="fill: #34AACD" d="M 235.33 691.19

-          C 339.44 764.48 264.45 654.38 206.17 648.37

-          C 264.45 654.38 376.43 582.21 249.71 620.30

-          C 376.43 582.21 244.04 572.90 198.23 616.68

-          C 244.04 572.90 258.13 439.88 203.71 564.95

-          C 258.13 439.88 168.04 538.37 168.67 603.26

-          C 168.04 538.37 73.63  444.67 131.96 566.82

-          C 73.63  444.67 93.67  576.80 139.75 618.20

-          C 93.67  576.80 -38.14 592.98  88.49 624.49

-          C -38.14 592.98 76.94  659.25 133.24 650.26

-          C 76.94  659.25 6.98   773.12 106.04 694.55

-          C 6.98   773.12 130.45 723.63 154.05 675.30

-          C 130.45 723.63 175.02 849.45 171.39 724.23

-          C 175.02 849.45 213.90 721.46 186.51 674.46

-          C 213.90 721.46 339.44 764.48 235.33 691.19 z"/>
-    <path style="fill: #F881BF" d="M 199.44 634.43

-          C 199.44 622.16 189.19 612.21 176.54 612.21

-          C 163.89 612.21 153.63 622.16 153.63 634.43

-          C 153.63 646.71 163.89 656.66 176.54 656.66

-          C 189.19 656.66 199.44 646.71 199.44 634.43 z"/>
+  <g style="transform: matrix(1 0 0 -1 -25.88 798.60); stroke: #000; stroke-width: 1">
+    <path style="fill: #1A5466" d="m 242.33 684.19 C 346.44 757.48 271.45 647.38 213.17 641.37 C 271.45 647.38 383.43 575.21 256.71 613.30 C 383.43 575.21 251.04 565.90 205.23 609.68 C 251.04 565.90 265.13 432.88 210.71 557.95 C 265.13 432.88 175.04 531.37 175.67 596.26 C 175.04 531.37 80.63 437.67 138.96 559.82 C 80.63 437.67 100.67 569.80 146.75 611.20 C 100.67 569.80 -31.14 585.98 95.49 617.49 C -31.14 585.98 83.94 652.25 140.24 643.26 C 83.94 652.25 13.98 766.12 113.04 687.55 C 13.98 766.12 137.45 716.63 161.05 668.30 C 137.45 716.63 182.02 842.45 178.39 717.23 C 182.02 842.45 220.90 714.46 193.51 667.46 C 220.90 714.46 346.44 757.48 242.33 684.19 z"/>
+    <path style="fill: #34AACD" d="M 235.33 691.19 C 339.44 764.48 264.45 654.38 206.17 648.37 C 264.45 654.38 376.43 582.21 249.71 620.30 C 376.43 582.21 244.04 572.90 198.23 616.68 C 244.04 572.90 258.13 439.88 203.71 564.95 C 258.13 439.88 168.04 538.37 168.67 603.26 C 168.04 538.37 73.63 444.67 131.96 566.82 C 73.63 444.67 93.67 576.80 139.75 618.20 C 93.67 576.80 -38.14 592.98 88.49 624.49 C -38.14 592.98 76.94 659.25 133.24 650.26 C 76.94 659.25 6.98 773.12 106.04 694.55 C 6.98 773.12 130.45 723.63 154.05 675.30 C 130.45 723.63 175.02 849.45 171.39 724.23 C 175.02 849.45 213.90 721.46 186.51 674.46 C 213.90 721.46 339.44 764.48 235.33 691.19 z"/>
+    <path style="fill: #F881BF" d="M 199.44 634.43 C 199.44 622.16 189.19 612.21 176.54 612.21 C 163.89 612.21 153.63 622.16 153.63 634.43 C 153.63 646.71 163.89 656.66 176.54 656.66 C 189.19 656.66 199.44 646.71 199.44 634.43 z"/>
   </g>
 </svg>
diff --git a/result/SVG/toap02.xml b/result/SVG/toap02.xml
index c62aa04..0a90368 100644
--- a/result/SVG/toap02.xml
+++ b/result/SVG/toap02.xml
@@ -3,7 +3,7 @@
 <svg width="4in" height="3in">
   <defs>
     <symbol id="Triangle1" min-x="0" min-y="0" max-x="300" max-y="200">
-      <path d="M 50 0 L  50 200 L 250 0 z"/>
+      <path d="M 50 0 L 50 200 L 250 0 z"/>
     </symbol>
     <symbol id="Triangle2" min-x="0" min-y="0" max-x="300" max-y="200">
       <path d="M 50 0 L 250 200 L 250 0 z"/>
diff --git a/result/att1 b/result/att1
new file mode 100644
index 0000000..d3ed2ad
--- /dev/null
+++ b/result/att1
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<doc attr="to normalize with a space"/>
diff --git a/result/att2 b/result/att2
new file mode 100644
index 0000000..d3ed2ad
--- /dev/null
+++ b/result/att2
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<doc attr="to normalize with a space"/>
diff --git a/result/valid/REC-xml-19980210.xml b/result/valid/REC-xml-19980210.xml
index 2ff5512..7f70749 100644
--- a/result/valid/REC-xml-19980210.xml
+++ b/result/valid/REC-xml-19980210.xml
@@ -1674,8 +1674,7 @@
 <p>The XML processor must normalize attribute values before
 passing them to the application, as described in 
 <specref ref="AVNormalize"/>.</p>-->
-          <p><termdef id="dt-enumerated" term="Enumerated Attribute
-Values"><term>Enumerated attributes</term> can take one 
+          <p><termdef id="dt-enumerated" term="Enumerated Attribute Values"><term>Enumerated attributes</term> can take one 
 of a list of values provided in the declaration</termdef>. There are two
 kinds of enumerated types:
 <scrap lang="ebnf"><head>Enumerated Attribute Types</head><prod id="NT-EnumeratedType"><lhs>EnumeratedType</lhs><rhs><nt def="NT-NotationType">NotationType</nt> 
diff --git a/test/att1 b/test/att1
new file mode 100644
index 0000000..609e5cc
--- /dev/null
+++ b/test/att1
@@ -0,0 +1,2 @@
+<doc attr="to normalize
+with a    space"/>
diff --git a/test/att2 b/test/att2
new file mode 100644
index 0000000..e630ff5
--- /dev/null
+++ b/test/att2
Binary files differ
diff --git a/xmlIO.c b/xmlIO.c
index 668caab..ea14efe 100644
--- a/xmlIO.c
+++ b/xmlIO.c
@@ -250,12 +250,62 @@
 }
 
 /**
+ * xmlParserInputBufferPush:
+ * @in:  a buffered parser input
+ * @buf:  an char array
+ * @len:  the size in bytes of the array.
+ *
+ * Push the content of the arry in the input buffer
+ * This routine handle the I18N transcoding to internal UTF-8
+ * This is used when operating the parser in progressive (push) mode.
+ *
+ * Returns the number of chars read and stored in the buffer, or -1
+ *         in case of error.
+ */
+int
+xmlParserInputBufferPush(xmlParserInputBufferPtr in, int len, char *buf) {
+    char *buffer = NULL;
+    int nbchars = 0;
+
+    if (len < 0) return(0);
+    if (in->encoder != NULL) {
+        xmlChar *buf;
+
+	buf = (xmlChar *) xmlMalloc((len + 1) * 2 * sizeof(xmlChar));
+	if (buf == NULL) {
+	    fprintf(stderr, "xmlParserInputBufferGrow : out of memory !\n");
+	    xmlFree(buffer);
+	    return(-1);
+	}
+	nbchars = in->encoder->input(buf, (len + 1) * 2 * sizeof(xmlChar),
+	                             BAD_CAST buffer, len);
+	/*
+	 * TODO : we really need to have something atomic or the 
+	 *        encoder must report the number of bytes read
+	 */
+        buf[nbchars] = 0;
+        xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars);
+	xmlFree(buf);
+    } else {
+	nbchars = len;
+        buffer[nbchars] = 0;
+        xmlBufferAdd(in->buffer, (xmlChar *) buffer, nbchars);
+    }
+#ifdef DEBUG_INPUT
+    fprintf(stderr, "I/O: pushed %d chars, buffer %d/%d\n",
+            nbchars, in->buffer->use, in->buffer->size);
+#endif
+    return(nbchars);
+}
+
+/**
  * xmlParserInputBufferGrow:
  * @in:  a buffered parser input
  * @len:  indicative value of the amount of chars to read
  *
  * Grow up the content of the input buffer, the old data are preserved
  * This routine handle the I18N transcoding to internal UTF-8
+ * This routine is used when operating the parser in normal (pull) mode
  * TODO: one should be able to remove one extra copy
  *
  * Returns the number of chars read and stored in the buffer, or -1
diff --git a/xmlIO.h b/xmlIO.h
index a99ab23..58baeb0 100644
--- a/xmlIO.h
+++ b/xmlIO.h
@@ -50,6 +50,9 @@
 						 int len);
 int	xmlParserInputBufferGrow		(xmlParserInputBufferPtr in,
 						 int len);
+int	xmlParserInputBufferPush		(xmlParserInputBufferPtr in,
+						 int len,
+						 char *buf);
 void	xmlFreeParserInputBuffer		(xmlParserInputBufferPtr in);
 char *	xmlParserGetDirectory			(const char *filename);