xmlreader.c revision 9e07710b95e620a914fcf69033d06f712ba387d9
1/*
2 * xmlreader.c: implements the xmlTextReader streaming node API
3 *
4 * NOTE:
5 *   XmlTextReader.Normalization Property won't be supported, since
6 *     it makes the parser non compliant to the XML recommendation
7 *
8 * See Copyright for the status of this software.
9 *
10 * daniel@veillard.com
11 */
12
13/*
14 * TODOs:
15 *   - provide an API to expand part of the tree
16 *   - provide an API to preserve part of the tree
17 *   - Streaming XInclude support
18 *   - validation against a provided DTD
19 *   - XML Schemas validation
20 *   - setting(s) for NoBlanks
21 *   - performances and tuning ...
22 */
23#define IN_LIBXML
24#include "libxml.h"
25
26#include <string.h> /* for memset() only ! */
27#include <stdarg.h>
28
29#ifdef HAVE_CTYPE_H
30#include <ctype.h>
31#endif
32#ifdef HAVE_STDLIB_H
33#include <stdlib.h>
34#endif
35
36#include <libxml/xmlmemory.h>
37#include <libxml/xmlIO.h>
38#include <libxml/xmlreader.h>
39
40/* #define DEBUG_CALLBACKS */
41/* #define DEBUG_READER */
42
43/**
44 * TODO:
45 *
46 * macro to flag unimplemented blocks
47 */
48#define TODO 								\
49    xmlGenericError(xmlGenericErrorContext,				\
50	    "Unimplemented block at %s:%d\n",				\
51            __FILE__, __LINE__);
52
53#ifdef DEBUG_READER
54#define DUMP_READER xmlTextReaderDebug(reader);
55#else
56#define DUMP_READER
57#endif
58
59/************************************************************************
60 *									*
61 *	The parser: maps the Text Reader API on top of the existing	*
62 *		parsing routines building a tree			*
63 *									*
64 ************************************************************************/
65
66#define XML_TEXTREADER_INPUT	1
67#define XML_TEXTREADER_CTXT	2
68
69typedef enum {
70    XML_TEXTREADER_MODE_INITIAL = 0,
71    XML_TEXTREADER_MODE_INTERACTIVE = 1,
72    XML_TEXTREADER_MODE_ERROR = 2,
73    XML_TEXTREADER_MODE_EOF =3,
74    XML_TEXTREADER_MODE_CLOSED = 4,
75    XML_TEXTREADER_MODE_READING = 5
76} xmlTextReaderMode;
77
78typedef enum {
79    XML_TEXTREADER_NONE = -1,
80    XML_TEXTREADER_START= 0,
81    XML_TEXTREADER_ELEMENT= 1,
82    XML_TEXTREADER_END= 2,
83    XML_TEXTREADER_EMPTY= 3,
84    XML_TEXTREADER_BACKTRACK= 4,
85    XML_TEXTREADER_DONE= 5
86} xmlTextReaderState;
87
88struct _xmlTextReader {
89    int				mode;	/* the parsing mode */
90    int				allocs;	/* what structure were deallocated */
91    xmlTextReaderState		state;
92    xmlParserCtxtPtr		ctxt;	/* the parser context */
93    xmlSAXHandlerPtr		sax;	/* the parser SAX callbacks */
94    xmlParserInputBufferPtr	input;	/* the input */
95    startElementSAXFunc		startElement;/* initial SAX callbacks */
96    endElementSAXFunc		endElement;  /* idem */
97    charactersSAXFunc		characters;
98    cdataBlockSAXFunc		cdataBlock;
99    unsigned int 		base;	/* base of the segment in the input */
100    unsigned int 		cur;	/* current position in the input */
101    xmlNodePtr			node;	/* current node */
102    xmlNodePtr			curnode;/* current attribute node */
103    int				depth;  /* depth of the current node */
104    xmlNodePtr			faketext;/* fake xmlNs chld */
105
106    /* entity stack when traversing entities content */
107    xmlNodePtr         ent;          /* Current Entity Ref Node */
108    int                entNr;        /* Depth of the entities stack */
109    int                entMax;       /* Max depth of the entities stack */
110    xmlNodePtr        *entTab;       /* array of entities */
111
112    /* error handling */
113    xmlTextReaderErrorFunc errorFunc;    /* callback function */
114    void                  *errorFuncArg; /* callback function user argument */
115};
116
117static const char *xmlTextReaderIsEmpty = "This element is empty";
118
119#ifdef DEBUG_READER
120static void
121xmlTextReaderDebug(xmlTextReaderPtr reader) {
122    if ((reader == NULL) || (reader->ctxt == NULL)) {
123	fprintf(stderr, "xmlTextReader NULL\n");
124	return;
125    }
126    fprintf(stderr, "xmlTextReader: state %d depth %d ",
127	    reader->state, reader->depth);
128    if (reader->node == NULL) {
129	fprintf(stderr, "node = NULL\n");
130    } else {
131	fprintf(stderr, "node %s\n", reader->node->name);
132    }
133    fprintf(stderr, "  input: base %d, cur %d, depth %d: ",
134	    reader->base, reader->cur, reader->ctxt->nodeNr);
135    if (reader->input->buffer == NULL) {
136	fprintf(stderr, "buffer is NULL\n");
137    } else {
138#ifdef LIBXML_DEBUG_ENABLED
139	xmlDebugDumpString(stderr,
140		&reader->input->buffer->content[reader->cur]);
141#endif
142	fprintf(stderr, "\n");
143    }
144}
145#endif
146
147/**
148 * xmlTextReaderEntPush:
149 * @reader:  the xmlTextReaderPtr used
150 * @value:  the entity reference node
151 *
152 * Pushes a new entity reference node on top of the entities stack
153 *
154 * Returns 0 in case of error, the index in the stack otherwise
155 */
156static int
157xmlTextReaderEntPush(xmlTextReaderPtr reader, xmlNodePtr value)
158{
159    if (reader->entMax <= 0) {
160	reader->entMax = 10;
161	reader->entTab = (xmlNodePtr *) xmlMalloc(reader->entMax *
162		                                  sizeof(reader->entTab[0]));
163        if (reader->entTab == NULL) {
164            xmlGenericError(xmlGenericErrorContext, "xmlMalloc failed !\n");
165            return (0);
166        }
167    }
168    if (reader->entNr >= reader->entMax) {
169        reader->entMax *= 2;
170        reader->entTab =
171            (xmlNodePtr *) xmlRealloc(reader->entTab,
172                                      reader->entMax *
173                                      sizeof(reader->entTab[0]));
174        if (reader->entTab == NULL) {
175            xmlGenericError(xmlGenericErrorContext, "xmlRealloc failed !\n");
176            return (0);
177        }
178    }
179    reader->entTab[reader->entNr] = value;
180    reader->ent = value;
181    return (reader->entNr++);
182}
183
184/**
185 * xmlTextReaderEntPop:
186 * @reader:  the xmlTextReaderPtr used
187 *
188 * Pops the top element entity from the entities stack
189 *
190 * Returns the entity just removed
191 */
192static xmlNodePtr
193xmlTextReaderEntPop(xmlTextReaderPtr reader)
194{
195    xmlNodePtr ret;
196
197    if (reader->entNr <= 0)
198        return (0);
199    reader->entNr--;
200    if (reader->entNr > 0)
201        reader->ent = reader->entTab[reader->entNr - 1];
202    else
203        reader->ent = NULL;
204    ret = reader->entTab[reader->entNr];
205    reader->entTab[reader->entNr] = 0;
206    return (ret);
207}
208
209/**
210 * xmlTextReaderStartElement:
211 * @ctx: the user data (XML parser context)
212 * @fullname:  The element name, including namespace prefix
213 * @atts:  An array of name/value attributes pairs, NULL terminated
214 *
215 * called when an opening tag has been processed.
216 */
217static void
218xmlTextReaderStartElement(void *ctx, const xmlChar *fullname,
219	                  const xmlChar **atts) {
220    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
221    xmlParserCtxtPtr origctxt;
222    xmlTextReaderPtr reader = ctxt->_private;
223
224#ifdef DEBUG_CALLBACKS
225    printf("xmlTextReaderStartElement(%s)\n", fullname);
226#endif
227    if ((reader != NULL) && (reader->startElement != NULL)) {
228	/*
229	 * when processing an entity, the context may have been changed
230	 */
231	origctxt = reader->ctxt;
232	reader->startElement(ctx, fullname, atts);
233	if ((ctxt->node != NULL) && (ctxt->input != NULL) &&
234	    (ctxt->input->cur != NULL) && (ctxt->input->cur[0] == '/') &&
235	    (ctxt->input->cur[1] == '>'))
236	    ctxt->node->_private = (void *) xmlTextReaderIsEmpty;
237    }
238    if (reader != NULL)
239	reader->state = XML_TEXTREADER_ELEMENT;
240}
241
242/**
243 * xmlTextReaderEndElement:
244 * @ctx: the user data (XML parser context)
245 * @fullname:  The element name, including namespace prefix
246 *
247 * called when an ending tag has been processed.
248 */
249static void
250xmlTextReaderEndElement(void *ctx, const xmlChar *fullname) {
251    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
252    xmlParserCtxtPtr origctxt;
253    xmlTextReaderPtr reader = ctxt->_private;
254
255#ifdef DEBUG_CALLBACKS
256    printf("xmlTextReaderEndElement(%s)\n", fullname);
257#endif
258    if ((reader != NULL) && (reader->endElement != NULL)) {
259	/*
260	 * when processing an entity, the context may have been changed
261	 */
262	origctxt = reader->ctxt;
263
264	reader->endElement(ctx, fullname);
265    }
266}
267
268/**
269 * xmlTextReaderCharacters:
270 * @ctx: the user data (XML parser context)
271 * @ch:  a xmlChar string
272 * @len: the number of xmlChar
273 *
274 * receiving some chars from the parser.
275 */
276static void
277xmlTextReaderCharacters(void *ctx, const xmlChar *ch, int len)
278{
279    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
280    xmlParserCtxtPtr origctxt;
281    xmlTextReaderPtr reader = ctxt->_private;
282
283#ifdef DEBUG_CALLBACKS
284    printf("xmlTextReaderCharacters()\n");
285#endif
286    if ((reader != NULL) && (reader->characters != NULL)) {
287	reader->characters(ctx, ch, len);
288	/*
289	 * when processing an entity, the context may have been changed
290	 */
291	origctxt = reader->ctxt;
292    }
293}
294
295/**
296 * xmlTextReaderCDataBlock:
297 * @ctx: the user data (XML parser context)
298 * @value:  The pcdata content
299 * @len:  the block length
300 *
301 * called when a pcdata block has been parsed
302 */
303static void
304xmlTextReaderCDataBlock(void *ctx, const xmlChar *ch, int len)
305{
306    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
307    xmlTextReaderPtr reader = ctxt->_private;
308
309#ifdef DEBUG_CALLBACKS
310    printf("xmlTextReaderCDataBlock()\n");
311#endif
312    if ((reader != NULL) && (reader->cdataBlock != NULL)) {
313	reader->cdataBlock(ctx, ch, len);
314    }
315}
316
317/**
318 * xmlTextReaderPushData:
319 * @reader:  the xmlTextReaderPtr used
320 *
321 * Push data down the progressive parser until a significant callback
322 * got raised.
323 *
324 * Returns -1 in case of failure, 0 otherwise
325 */
326static int
327xmlTextReaderPushData(xmlTextReaderPtr reader) {
328    unsigned int cur = reader->cur;
329    xmlBufferPtr inbuf;
330    int val;
331    int oldstate;
332
333    if ((reader->input == NULL) || (reader->input->buffer == NULL))
334	return(-1);
335
336    oldstate = reader->state;
337    reader->state = XML_TEXTREADER_NONE;
338    inbuf = reader->input->buffer;
339    while (reader->state == XML_TEXTREADER_NONE) {
340	if (cur >= inbuf->use) {
341	    /*
342	     * Refill the buffer unless we are at the end of the stream
343	     */
344	    if (reader->mode != XML_TEXTREADER_MODE_EOF) {
345		val = xmlParserInputBufferRead(reader->input, 4096);
346		if (val <= 0) {
347		    reader->mode = XML_TEXTREADER_MODE_EOF;
348		    reader->state = oldstate;
349		    if ((oldstate != XML_TEXTREADER_START) ||
350			(reader->ctxt->myDoc != NULL))
351			return(val);
352		}
353	    } else
354		break;
355	}
356	/*
357	 * parse by block of 512 bytes
358	 */
359	if ((cur >= reader->cur + 512) || (cur >= inbuf->use)) {
360	    if (cur < inbuf->use)
361		cur = cur + 1;
362	    val = xmlParseChunk(reader->ctxt,
363		          (const char *) &inbuf->content[reader->cur],
364			  cur - reader->cur, 0);
365	    if (val != 0)
366		return(-1);
367	    reader->cur = cur;
368	    break;
369	} else {
370	    cur = cur + 1;
371
372	    /*
373	     * One may have to force a flush at some point when parsing really
374	     * large CDATA sections
375	     */
376	    if ((cur - reader->cur > 4096) && (reader->base == 0) &&
377		(reader->mode == XML_TEXTREADER_MODE_INTERACTIVE)) {
378		cur = cur + 1;
379		val = xmlParseChunk(reader->ctxt,
380			      (const char *) &inbuf->content[reader->cur],
381			      cur - reader->cur, 0);
382		if (val != 0)
383		    return(-1);
384		reader->cur = cur;
385	    }
386	}
387    }
388    /*
389     * Discard the consumed input when needed and possible
390     */
391    if (reader->mode == XML_TEXTREADER_MODE_INTERACTIVE) {
392	if ((reader->cur >= 4096) && (reader->base == 0)) {
393	    val = xmlBufferShrink(inbuf, cur);
394	    if (val >= 0) {
395		reader->cur -= val;
396	    }
397	}
398    }
399
400    /*
401     * At the end of the stream signal that the work is done to the Push
402     * parser.
403     */
404    if (reader->mode == XML_TEXTREADER_MODE_EOF) {
405	if (reader->mode != XML_TEXTREADER_DONE) {
406	    val = xmlParseChunk(reader->ctxt,
407		    (const char *) &inbuf->content[reader->cur],
408		    cur - reader->cur, 1);
409	    reader->cur = cur;
410	    reader->mode = XML_TEXTREADER_DONE;
411	}
412    }
413    reader->state = oldstate;
414    return(0);
415}
416
417/**
418 * xmlTextReaderValidatePush:
419 * @reader:  the xmlTextReaderPtr used
420 *
421 * Push the current node for validation
422 */
423static void
424xmlTextReaderValidatePush(xmlTextReaderPtr reader) {
425#ifdef LIBXML_REGEXP_ENABLED
426    xmlNodePtr node = reader->node;
427
428    if ((node->ns == NULL) || (node->ns->prefix == NULL)) {
429	reader->ctxt->valid &= xmlValidatePushElement(&reader->ctxt->vctxt,
430				reader->ctxt->myDoc, node, node->name);
431    } else {
432	xmlChar *qname;
433
434	qname = xmlStrdup(node->ns->prefix);
435	qname = xmlStrcat(qname, BAD_CAST ":");
436	qname = xmlStrcat(qname, node->name);
437	reader->ctxt->valid &= xmlValidatePushElement(&reader->ctxt->vctxt,
438				reader->ctxt->myDoc, node, qname);
439	if (qname != NULL)
440	    xmlFree(qname);
441    }
442#endif /* LIBXML_REGEXP_ENABLED */
443}
444/**
445 * xmlTextReaderValidatePop:
446 * @reader:  the xmlTextReaderPtr used
447 *
448 * Pop the current node from validation
449 */
450static void
451xmlTextReaderValidatePop(xmlTextReaderPtr reader) {
452#ifdef LIBXML_REGEXP_ENABLED
453    xmlNodePtr node = reader->node;
454
455    if ((node->ns == NULL) || (node->ns->prefix == NULL)) {
456	reader->ctxt->valid &= xmlValidatePopElement(&reader->ctxt->vctxt,
457				reader->ctxt->myDoc, node, node->name);
458    } else {
459	xmlChar *qname;
460
461	qname = xmlStrdup(node->ns->prefix);
462	qname = xmlStrcat(qname, BAD_CAST ":");
463	qname = xmlStrcat(qname, node->name);
464	reader->ctxt->valid &= xmlValidatePopElement(&reader->ctxt->vctxt,
465				reader->ctxt->myDoc, node, qname);
466	if (qname != NULL)
467	    xmlFree(qname);
468    }
469#endif /* LIBXML_REGEXP_ENABLED */
470}
471/**
472 * xmlTextReaderValidateEntity:
473 * @reader:  the xmlTextReaderPtr used
474 *
475 * Handle the validation when an entity reference is encountered and
476 * entity substitution is not activated. As a result the parser interface
477 * must walk through the entity and do the validation calls
478 */
479static void
480xmlTextReaderValidateEntity(xmlTextReaderPtr reader) {
481#ifdef LIBXML_REGEXP_ENABLED
482    xmlNodePtr oldnode = reader->node;
483    xmlNodePtr node = reader->node;
484    xmlParserCtxtPtr ctxt = reader->ctxt;
485
486    do {
487	if (node->type == XML_ENTITY_REF_NODE) {
488	    /*
489	     * Case where the underlying tree is not availble, lookup the entity
490	     * and walk it.
491	     */
492	    if ((node->children == NULL) && (ctxt->sax != NULL) &&
493		(ctxt->sax->getEntity != NULL)) {
494		node->children = (xmlNodePtr)
495		    ctxt->sax->getEntity(ctxt, node->name);
496	    }
497
498	    if ((node->children != NULL) &&
499		(node->children->type == XML_ENTITY_DECL) &&
500		(node->children->children != NULL)) {
501		xmlTextReaderEntPush(reader, node);
502		node = node->children->children;
503		continue;
504	    } else {
505		/*
506		 * The error has probably be raised already.
507		 */
508		if (node == oldnode)
509		    break;
510		node = node->next;
511	    }
512	} else if (node->type == XML_ELEMENT_NODE) {
513	    reader->node = node;
514	    xmlTextReaderValidatePush(reader);
515	} else if ((node->type == XML_TEXT_NODE) ||
516		   (node->type == XML_CDATA_SECTION_NODE)) {
517	    ctxt->valid &= xmlValidatePushCData(&ctxt->vctxt,
518			      node->content, xmlStrlen(node->content));
519	}
520
521	/*
522	 * go to next node
523	 */
524	if (node->children != NULL) {
525	    node = node->children;
526	    continue;
527	} else if (node->type == XML_ELEMENT_NODE) {
528	    xmlTextReaderValidatePop(reader);
529	}
530	if (node->next != NULL) {
531	    node = node->next;
532	    continue;
533	}
534	do {
535	    node = node->parent;
536	    if (node->type == XML_ELEMENT_NODE) {
537		reader->node = node;
538		xmlTextReaderValidatePop(reader);
539	    }
540	    if ((node->type == XML_ENTITY_DECL) &&
541		(reader->ent != NULL) && (reader->ent->children == node)) {
542		node = xmlTextReaderEntPop(reader);
543	    }
544	    if (node == oldnode)
545		break;
546	    if (node->next != NULL) {
547		node = node->next;
548		break;
549	    }
550	} while ((node != NULL) && (node != oldnode));
551    } while ((node != NULL) && (node != oldnode));
552    reader->node = oldnode;
553#endif /* LIBXML_REGEXP_ENABLED */
554}
555
556
557/**
558 * xmlTextReaderRead:
559 * @reader:  the xmlTextReaderPtr used
560 *
561 *  Moves the position of the current instance to the next node in
562 *  the stream, exposing its properties.
563 *
564 *  Returns 1 if the node was read successfully, 0 if there is no more
565 *          nodes to read, or -1 in case of error
566 */
567int
568xmlTextReaderRead(xmlTextReaderPtr reader) {
569    int val, olddepth = 0;
570    xmlTextReaderState oldstate = 0;
571    xmlNodePtr oldnode = NULL;
572
573    if ((reader == NULL) || (reader->ctxt == NULL))
574	return(-1);
575    if (reader->ctxt->wellFormed != 1)
576	return(-1);
577
578#ifdef DEBUG_READER
579    fprintf(stderr, "\nREAD ");
580    DUMP_READER
581#endif
582    reader->curnode = NULL;
583    if (reader->mode == XML_TEXTREADER_MODE_INITIAL) {
584	reader->mode = XML_TEXTREADER_MODE_INTERACTIVE;
585	/*
586	 * Initial state
587	 */
588	do {
589	    val = xmlTextReaderPushData(reader);
590	    if (val < 0)
591		return(-1);
592	} while ((reader->ctxt->node == NULL) &&
593		 ((reader->mode != XML_TEXTREADER_MODE_EOF) &&
594		  (reader->mode != XML_TEXTREADER_DONE)));
595	if (reader->ctxt->node == NULL) {
596	    if (reader->ctxt->myDoc != NULL) {
597		reader->node = reader->ctxt->myDoc->children;
598	    }
599	    if (reader->node == NULL)
600		return(-1);
601	    reader->state = XML_TEXTREADER_ELEMENT;
602	} else {
603	    if (reader->ctxt->myDoc != NULL) {
604		reader->node = reader->ctxt->myDoc->children;
605	    }
606	    if (reader->node == NULL)
607		reader->node = reader->ctxt->nodeTab[0];
608	    reader->state = XML_TEXTREADER_ELEMENT;
609	}
610	reader->depth = 0;
611	goto node_found;
612    }
613    oldstate = reader->state;
614    olddepth = reader->ctxt->nodeNr;
615    oldnode = reader->node;
616
617get_next_node:
618    /*
619     * If we are not backtracking on ancestors or examined nodes,
620     * that the parser didn't finished or that we arent at the end
621     * of stream, continue processing.
622     */
623    while (((oldstate == XML_TEXTREADER_BACKTRACK) ||
624            (reader->node->children == NULL) ||
625	    (reader->node->type == XML_ENTITY_REF_NODE) ||
626	    (reader->node->type == XML_DTD_NODE) ||
627	    (reader->node->type == XML_DOCUMENT_NODE) ||
628	    (reader->node->type == XML_HTML_DOCUMENT_NODE)) &&
629	   (reader->node->next == NULL) &&
630	   ((reader->ctxt->node == NULL) ||
631	    (reader->ctxt->node == reader->node) ||
632	    (reader->ctxt->node == reader->node->parent)) &&
633	   (reader->ctxt->nodeNr == olddepth) &&
634	   (reader->ctxt->instate != XML_PARSER_EOF)) {
635	val = xmlTextReaderPushData(reader);
636	if (val < 0)
637	    return(-1);
638	if (reader->node == NULL)
639	    goto node_end;
640    }
641    /*
642     * If we are in the middle of a piece of CDATA make sure it's finished
643     * Maybe calling a function checking that a non-character() callback was
644     * received would be cleaner for the loop exit.
645     */
646    if ((oldstate == XML_TEXTREADER_ELEMENT) &&
647	(reader->ctxt->instate == XML_PARSER_CDATA_SECTION)) {
648	while ((reader->ctxt->instate == XML_PARSER_CDATA_SECTION) &&
649	       (((reader->node->content == NULL) &&
650		 (reader->node->next != NULL) &&
651		 (reader->node->next->type == XML_CDATA_SECTION_NODE) &&
652		 (reader->node->next->next == NULL) &&
653		 (reader->node->parent->next == NULL)) ||
654	        ((reader->node->children != NULL) &&
655		 (reader->node->children->type == XML_CDATA_SECTION_NODE) &&
656		 (reader->node->children->next == NULL) &&
657		 (reader->node->children->next == NULL)))) {
658	    val = xmlTextReaderPushData(reader);
659	    if (val < 0)
660		return(-1);
661	}
662    }
663    if ((oldstate == XML_TEXTREADER_ELEMENT) &&
664	(reader->ctxt->instate == XML_PARSER_CONTENT)) {
665	while ((reader->ctxt->instate == XML_PARSER_CONTENT) &&
666	       (((reader->node->content == NULL) &&
667		 (reader->node->next != NULL) &&
668		 (reader->node->next->type == XML_TEXT_NODE) &&
669		 (reader->node->next->next == NULL) &&
670		 (reader->node->parent->next == NULL)) ||
671	        ((reader->node->children != NULL) &&
672		 (reader->node->children->type == XML_TEXT_NODE) &&
673		 (reader->node->children->next == NULL) &&
674		 (reader->node->children->next == NULL)))) {
675	    val = xmlTextReaderPushData(reader);
676	    if (val < 0)
677		return(-1);
678	}
679    }
680    if (oldstate != XML_TEXTREADER_BACKTRACK) {
681	if ((reader->node->children != NULL) &&
682	    (reader->node->type != XML_ENTITY_REF_NODE) &&
683	    (reader->node->type != XML_DTD_NODE)) {
684	    reader->node = reader->node->children;
685	    reader->depth++;
686	    reader->state = XML_TEXTREADER_ELEMENT;
687	    goto node_found;
688	}
689    }
690    if (reader->node->next != NULL) {
691	if ((oldstate == XML_TEXTREADER_ELEMENT) &&
692            (reader->node->type == XML_ELEMENT_NODE) &&
693	    (reader->node->children == NULL) &&
694	    (reader->node->_private != (void *)xmlTextReaderIsEmpty)) {
695	    reader->state = XML_TEXTREADER_END;
696	    goto node_found;
697	}
698	if ((reader->ctxt->validate) &&
699	    (reader->node->type == XML_ELEMENT_NODE))
700	    xmlTextReaderValidatePop(reader);
701	reader->node = reader->node->next;
702	reader->state = XML_TEXTREADER_ELEMENT;
703
704	/*
705	 * Cleanup of the old node
706	 */
707	if ((reader->node->prev != NULL) &&
708            (reader->node->prev->type != XML_DTD_NODE)) {
709	    xmlNodePtr tmp = reader->node->prev;
710	    xmlUnlinkNode(tmp);
711	    xmlFreeNode(tmp);
712	}
713
714	goto node_found;
715    }
716    if ((oldstate == XML_TEXTREADER_ELEMENT) &&
717	(reader->node->type == XML_ELEMENT_NODE) &&
718	(reader->node->children == NULL) &&
719	(reader->node->_private != (void *)xmlTextReaderIsEmpty)) {
720	reader->state = XML_TEXTREADER_END;
721	goto node_found;
722    }
723    if ((reader->ctxt->validate) && (reader->node->type == XML_ELEMENT_NODE))
724	xmlTextReaderValidatePop(reader);
725    reader->node = reader->node->parent;
726    if ((reader->node == NULL) ||
727	(reader->node->type == XML_DOCUMENT_NODE) ||
728#ifdef LIBXML_DOCB_ENABLED
729	(reader->node->type == XML_DOCB_DOCUMENT_NODE) ||
730#endif
731	(reader->node->type == XML_HTML_DOCUMENT_NODE)) {
732	if (reader->mode != XML_TEXTREADER_DONE) {
733	    val = xmlParseChunk(reader->ctxt, "", 0, 1);
734	    reader->mode = XML_TEXTREADER_DONE;
735	}
736	reader->node = NULL;
737	reader->depth = -1;
738
739	/*
740	 * Cleanup of the old node
741	 */
742	if (oldnode->type != XML_DTD_NODE) {
743	    xmlUnlinkNode(oldnode);
744	    xmlFreeNode(oldnode);
745	}
746
747	goto node_end;
748    }
749    reader->depth--;
750    reader->state = XML_TEXTREADER_BACKTRACK;
751
752node_found:
753    DUMP_READER
754
755    /*
756     * Handle entities enter and exit when in entity replacement mode
757     */
758    if ((reader->node != NULL) &&
759	(reader->node->type == XML_ENTITY_REF_NODE) &&
760	(reader->ctxt != NULL) && (reader->ctxt->replaceEntities == 1)) {
761	/*
762	 * Case where the underlying tree is not availble, lookup the entity
763	 * and walk it.
764	 */
765	if ((reader->node->children == NULL) && (reader->ctxt->sax != NULL) &&
766	    (reader->ctxt->sax->getEntity != NULL)) {
767	    reader->node->children = (xmlNodePtr)
768		reader->ctxt->sax->getEntity(reader->ctxt, reader->node->name);
769	}
770
771	if ((reader->node->children != NULL) &&
772	    (reader->node->children->type == XML_ENTITY_DECL) &&
773	    (reader->node->children->children != NULL)) {
774	    xmlTextReaderEntPush(reader, reader->node);
775	    reader->node = reader->node->children->children;
776	}
777    } else if ((reader->node != NULL) &&
778	       (reader->node->type == XML_ENTITY_REF_NODE) &&
779	       (reader->ctxt != NULL) && (reader->ctxt->validate == 1)) {
780	xmlTextReaderValidateEntity(reader);
781    }
782    if ((reader->node != NULL) &&
783	(reader->node->type == XML_ENTITY_DECL) &&
784	(reader->ent != NULL) && (reader->ent->children == reader->node)) {
785	reader->node = xmlTextReaderEntPop(reader);
786	reader->depth++;
787        goto get_next_node;
788    }
789#ifdef LIBXML_REGEXP_ENABLED
790    if ((reader->ctxt->validate) && (reader->node != NULL)) {
791	xmlNodePtr node = reader->node;
792	xmlParserCtxtPtr ctxt = reader->ctxt;
793
794	if ((node->type == XML_ELEMENT_NODE) &&
795            ((reader->state != XML_TEXTREADER_END) &&
796	     (reader->state != XML_TEXTREADER_BACKTRACK))) {
797	    xmlTextReaderValidatePush(reader);
798	} else if ((node->type == XML_TEXT_NODE) ||
799		   (node->type == XML_CDATA_SECTION_NODE)) {
800	    ctxt->valid &= xmlValidatePushCData(&ctxt->vctxt,
801			      node->content, xmlStrlen(node->content));
802	}
803    }
804#endif /* LIBXML_REGEXP_ENABLED */
805    return(1);
806node_end:
807    return(0);
808}
809
810/**
811 * xmlTextReaderReadState:
812 * @reader:  the xmlTextReaderPtr used
813 *
814 * Gets the read state of the reader.
815 *
816 * Returns the state value, or -1 in case of error
817 */
818int
819xmlTextReaderReadState(xmlTextReaderPtr reader) {
820    if (reader == NULL)
821	return(-1);
822    return(reader->mode);
823}
824
825/**
826 * xmlTextReaderReadInnerXml:
827 * @reader:  the xmlTextReaderPtr used
828 *
829 * Reads the contents of the current node, including child nodes and markup.
830 *
831 * Returns a string containing the XML content, or NULL if the current node
832 *         is neither an element nor attribute, or has no child nodes. The
833 *         string must be deallocated by the caller.
834 */
835xmlChar *
836xmlTextReaderReadInnerXml(xmlTextReaderPtr reader) {
837    TODO
838    return(NULL);
839}
840
841/**
842 * xmlTextReaderReadOuterXml:
843 * @reader:  the xmlTextReaderPtr used
844 *
845 * Reads the contents of the current node, including child nodes and markup.
846 *
847 * Returns a string containing the XML content, or NULL if the current node
848 *         is neither an element nor attribute, or has no child nodes. The
849 *         string must be deallocated by the caller.
850 */
851xmlChar *
852xmlTextReaderReadOuterXml(xmlTextReaderPtr reader) {
853    TODO
854    return(NULL);
855}
856
857/**
858 * xmlTextReaderReadString:
859 * @reader:  the xmlTextReaderPtr used
860 *
861 * Reads the contents of an element or a text node as a string.
862 *
863 * Returns a string containing the contents of the Element or Text node,
864 *         or NULL if the reader is positioned on any other type of node.
865 *         The string must be deallocated by the caller.
866 */
867xmlChar *
868xmlTextReaderReadString(xmlTextReaderPtr reader) {
869    TODO
870    return(NULL);
871}
872
873/**
874 * xmlTextReaderReadBase64:
875 * @reader:  the xmlTextReaderPtr used
876 * @array:  a byte array to store the content.
877 * @offset:  the zero-based index into array where the method should
878 *           begin to write.
879 * @len:  the number of bytes to write.
880 *
881 * Reads and decodes the Base64 encoded contents of an element and
882 * stores the result in a byte buffer.
883 *
884 * Returns the number of bytes written to array, or zero if the current
885 *         instance is not positioned on an element or -1 in case of error.
886 */
887int
888xmlTextReaderReadBase64(xmlTextReaderPtr reader, unsigned char *array,
889	                int offset, int len) {
890    if ((reader == NULL) || (reader->ctxt == NULL))
891	return(-1);
892    if (reader->ctxt->wellFormed != 1)
893	return(-1);
894
895    if ((reader->node == NULL) || (reader->node->type == XML_ELEMENT_NODE))
896	return(0);
897    TODO
898    return(0);
899}
900
901/**
902 * xmlTextReaderReadBinHex:
903 * @reader:  the xmlTextReaderPtr used
904 * @array:  a byte array to store the content.
905 * @offset:  the zero-based index into array where the method should
906 *           begin to write.
907 * @len:  the number of bytes to write.
908 *
909 * Reads and decodes the BinHex encoded contents of an element and
910 * stores the result in a byte buffer.
911 *
912 * Returns the number of bytes written to array, or zero if the current
913 *         instance is not positioned on an element or -1 in case of error.
914 */
915int
916xmlTextReaderReadBinHex(xmlTextReaderPtr reader, unsigned char *array,
917	                int offset, int len) {
918    if ((reader == NULL) || (reader->ctxt == NULL))
919	return(-1);
920    if (reader->ctxt->wellFormed != 1)
921	return(-1);
922
923    if ((reader->node == NULL) || (reader->node->type == XML_ELEMENT_NODE))
924	return(0);
925    TODO
926    return(0);
927}
928
929/************************************************************************
930 *									*
931 *			Constructor and destructors			*
932 *									*
933 ************************************************************************/
934/**
935 * xmlNewTextReader:
936 * @input: the xmlParserInputBufferPtr used to read data
937 * @URI: the URI information for the source if available
938 *
939 * Create an xmlTextReader structure fed with @input
940 *
941 * Returns the new xmlTextReaderPtr or NULL in case of error
942 */
943xmlTextReaderPtr
944xmlNewTextReader(xmlParserInputBufferPtr input, const char *URI) {
945    xmlTextReaderPtr ret;
946    int val;
947
948    if (input == NULL)
949	return(NULL);
950    ret = xmlMalloc(sizeof(xmlTextReader));
951    if (ret == NULL) {
952        xmlGenericError(xmlGenericErrorContext,
953		"xmlNewTextReader : malloc failed\n");
954	return(NULL);
955    }
956    memset(ret, 0, sizeof(xmlTextReader));
957    ret->entTab = NULL;
958    ret->entMax = 0;
959    ret->entNr = 0;
960    ret->input = input;
961    ret->sax = (xmlSAXHandler *) xmlMalloc(sizeof(xmlSAXHandler));
962    if (ret->sax == NULL) {
963	xmlFree(ret);
964        xmlGenericError(xmlGenericErrorContext,
965		"xmlNewTextReader : malloc failed\n");
966	return(NULL);
967    }
968    memcpy(ret->sax, &xmlDefaultSAXHandler, sizeof(xmlSAXHandler));
969    ret->startElement = ret->sax->startElement;
970    ret->sax->startElement = xmlTextReaderStartElement;
971    ret->endElement = ret->sax->endElement;
972    ret->sax->endElement = xmlTextReaderEndElement;
973    ret->characters = ret->sax->characters;
974    ret->sax->characters = xmlTextReaderCharacters;
975    ret->cdataBlock = ret->sax->cdataBlock;
976    ret->sax->cdataBlock = xmlTextReaderCDataBlock;
977
978    ret->mode = XML_TEXTREADER_MODE_INITIAL;
979    ret->node = NULL;
980    ret->curnode = NULL;
981    val = xmlParserInputBufferRead(input, 4);
982    if (val >= 4) {
983	ret->ctxt = xmlCreatePushParserCtxt(ret->sax, NULL,
984			(const char *) ret->input->buffer->content, 4, URI);
985	ret->base = 0;
986	ret->cur = 4;
987    } else {
988	ret->ctxt = xmlCreatePushParserCtxt(ret->sax, NULL, NULL, 0, URI);
989	ret->base = 0;
990	ret->cur = 0;
991    }
992    ret->ctxt->_private = ret;
993    ret->ctxt->linenumbers = 1;
994    ret->allocs = XML_TEXTREADER_CTXT;
995    return(ret);
996
997}
998
999/**
1000 * xmlNewTextReaderFilename:
1001 * @URI: the URI of the resource to process
1002 *
1003 * Create an xmlTextReader structure fed with the resource at @URI
1004 *
1005 * Returns the new xmlTextReaderPtr or NULL in case of error
1006 */
1007xmlTextReaderPtr
1008xmlNewTextReaderFilename(const char *URI) {
1009    xmlParserInputBufferPtr input;
1010    xmlTextReaderPtr ret;
1011    char *directory = NULL;
1012
1013    input = xmlParserInputBufferCreateFilename(URI, XML_CHAR_ENCODING_NONE);
1014    if (input == NULL)
1015	return(NULL);
1016    ret = xmlNewTextReader(input, URI);
1017    if (ret == NULL) {
1018	xmlFreeParserInputBuffer(input);
1019	return(NULL);
1020    }
1021    ret->allocs |= XML_TEXTREADER_INPUT;
1022    if (ret->ctxt->directory == NULL)
1023        directory = xmlParserGetDirectory(URI);
1024    if ((ret->ctxt->directory == NULL) && (directory != NULL))
1025        ret->ctxt->directory = (char *) xmlStrdup((xmlChar *) directory);
1026    if (directory != NULL)
1027	xmlFree(directory);
1028    return(ret);
1029}
1030
1031/**
1032 * xmlFreeTextReader:
1033 * @reader:  the xmlTextReaderPtr
1034 *
1035 * Deallocate all the resources associated to the reader
1036 */
1037void
1038xmlFreeTextReader(xmlTextReaderPtr reader) {
1039    if (reader == NULL)
1040	return;
1041    if (reader->ctxt != NULL) {
1042	if (reader->ctxt->myDoc != NULL) {
1043	    xmlFreeDoc(reader->ctxt->myDoc);
1044	    reader->ctxt->myDoc = NULL;
1045	}
1046	if ((reader->ctxt->vctxt.vstateTab != NULL) &&
1047	    (reader->ctxt->vctxt.vstateMax > 0)){
1048	    xmlFree(reader->ctxt->vctxt.vstateTab);
1049	    reader->ctxt->vctxt.vstateTab = 0;
1050	    reader->ctxt->vctxt.vstateMax = 0;
1051	}
1052	if (reader->allocs & XML_TEXTREADER_CTXT)
1053	    xmlFreeParserCtxt(reader->ctxt);
1054    }
1055    if (reader->sax != NULL)
1056	xmlFree(reader->sax);
1057    if ((reader->input != NULL)  && (reader->allocs & XML_TEXTREADER_INPUT))
1058	xmlFreeParserInputBuffer(reader->input);
1059    if (reader->faketext != NULL) {
1060	xmlFreeNode(reader->faketext);
1061    }
1062    if (reader->entTab != NULL)
1063	xmlFree(reader->entTab);
1064    xmlFree(reader);
1065}
1066
1067/************************************************************************
1068 *									*
1069 *			Methods for XmlTextReader			*
1070 *									*
1071 ************************************************************************/
1072/**
1073 * xmlTextReaderClose:
1074 * @reader:  the xmlTextReaderPtr used
1075 *
1076 * This method releases any resources allocated by the current instance
1077 * changes the state to Closed and close any underlying input.
1078 *
1079 * Returns 0 or -1 in case of error
1080 */
1081int
1082xmlTextReaderClose(xmlTextReaderPtr reader) {
1083    if (reader == NULL)
1084	return(-1);
1085    reader->node = NULL;
1086    reader->curnode = NULL;
1087    reader->mode = XML_TEXTREADER_MODE_CLOSED;
1088    if (reader->ctxt != NULL) {
1089	if (reader->ctxt->myDoc != NULL) {
1090	    xmlFreeDoc(reader->ctxt->myDoc);
1091	    reader->ctxt->myDoc = NULL;
1092	}
1093	if (reader->allocs & XML_TEXTREADER_CTXT) {
1094	    xmlFreeParserCtxt(reader->ctxt);
1095	    reader->allocs -= XML_TEXTREADER_CTXT;
1096	}
1097    }
1098    if (reader->sax != NULL) {
1099        xmlFree(reader->sax);
1100	reader->sax = NULL;
1101    }
1102    if ((reader->input != NULL)  && (reader->allocs & XML_TEXTREADER_INPUT)) {
1103	xmlFreeParserInputBuffer(reader->input);
1104	reader->allocs -= XML_TEXTREADER_INPUT;
1105    }
1106    return(0);
1107}
1108
1109/**
1110 * xmlTextReaderGetAttributeNo:
1111 * @reader:  the xmlTextReaderPtr used
1112 * @no: the zero-based index of the attribute relative to the containing element
1113 *
1114 * Provides the value of the attribute with the specified index relative
1115 * to the containing element.
1116 *
1117 * Returns a string containing the value of the specified attribute, or NULL
1118 *    in case of error. The string must be deallocated by the caller.
1119 */
1120xmlChar *
1121xmlTextReaderGetAttributeNo(xmlTextReaderPtr reader, int no) {
1122    xmlChar *ret;
1123    int i;
1124    xmlAttrPtr cur;
1125    xmlNsPtr ns;
1126
1127    if (reader == NULL)
1128	return(NULL);
1129    if (reader->node == NULL)
1130	return(NULL);
1131    if (reader->curnode != NULL)
1132	return(NULL);
1133    /* TODO: handle the xmlDecl */
1134    if (reader->node->type != XML_ELEMENT_NODE)
1135	return(NULL);
1136
1137    ns = reader->node->nsDef;
1138    for (i = 0;(i < no) && (ns != NULL);i++) {
1139	ns = ns->next;
1140    }
1141    if (ns != NULL)
1142	return(xmlStrdup(ns->href));
1143
1144    cur = reader->node->properties;
1145    if (cur == NULL)
1146	return(NULL);
1147    for (;i < no;i++) {
1148	cur = cur->next;
1149	if (cur == NULL)
1150	    return(NULL);
1151    }
1152    /* TODO walk the DTD if present */
1153
1154    ret = xmlNodeListGetString(reader->node->doc, cur->children, 1);
1155    if (ret == NULL) return(xmlStrdup((xmlChar *)""));
1156    return(ret);
1157}
1158
1159/**
1160 * xmlTextReaderGetAttribute:
1161 * @reader:  the xmlTextReaderPtr used
1162 * @name: the qualified name of the attribute.
1163 *
1164 * Provides the value of the attribute with the specified qualified name.
1165 *
1166 * Returns a string containing the value of the specified attribute, or NULL
1167 *    in case of error. The string must be deallocated by the caller.
1168 */
1169xmlChar *
1170xmlTextReaderGetAttribute(xmlTextReaderPtr reader, const xmlChar *name) {
1171    xmlChar *prefix = NULL;
1172    xmlChar *localname;
1173    xmlNsPtr ns;
1174    xmlChar *ret = NULL;
1175
1176    if ((reader == NULL) || (name == NULL))
1177	return(NULL);
1178    if (reader->node == NULL)
1179	return(NULL);
1180    if (reader->curnode != NULL)
1181	return(NULL);
1182
1183    /* TODO: handle the xmlDecl */
1184    if (reader->node->type != XML_ELEMENT_NODE)
1185	return(NULL);
1186
1187    localname = xmlSplitQName2(name, &prefix);
1188    if (localname == NULL)
1189	return(xmlGetProp(reader->node, name));
1190
1191    ns = xmlSearchNs(reader->node->doc, reader->node, prefix);
1192    if (ns != NULL)
1193        ret = xmlGetNsProp(reader->node, localname, ns->href);
1194
1195    if (localname != NULL)
1196        xmlFree(localname);
1197    if (prefix != NULL)
1198        xmlFree(prefix);
1199    return(ret);
1200}
1201
1202
1203/**
1204 * xmlTextReaderGetAttributeNs:
1205 * @reader:  the xmlTextReaderPtr used
1206 * @localName: the local name of the attribute.
1207 * @namespaceURI: the namespace URI of the attribute.
1208 *
1209 * Provides the value of the specified attribute
1210 *
1211 * Returns a string containing the value of the specified attribute, or NULL
1212 *    in case of error. The string must be deallocated by the caller.
1213 */
1214xmlChar *
1215xmlTextReaderGetAttributeNs(xmlTextReaderPtr reader, const xmlChar *localName,
1216			    const xmlChar *namespaceURI) {
1217    if ((reader == NULL) || (localName == NULL))
1218	return(NULL);
1219    if (reader->node == NULL)
1220	return(NULL);
1221    if (reader->curnode != NULL)
1222	return(NULL);
1223
1224    /* TODO: handle the xmlDecl */
1225    if (reader->node->type != XML_ELEMENT_NODE)
1226	return(NULL);
1227
1228    return(xmlGetNsProp(reader->node, localName, namespaceURI));
1229}
1230
1231/**
1232 * xmlTextReaderGetRemainder:
1233 * @reader:  the xmlTextReaderPtr used
1234 *
1235 * Method to get the remainder of the buffered XML. this method stops the
1236 * parser, set its state to End Of File and return the input stream with
1237 * what is left that the parser did not use.
1238 *
1239 * Returns the xmlParserInputBufferPtr attached to the XML or NULL
1240 *    in case of error.
1241 */
1242xmlParserInputBufferPtr
1243xmlTextReaderGetRemainder(xmlTextReaderPtr reader) {
1244    xmlParserInputBufferPtr ret = NULL;
1245
1246    if (reader == NULL)
1247	return(NULL);
1248    if (reader->node == NULL)
1249	return(NULL);
1250
1251    reader->node = NULL;
1252    reader->curnode = NULL;
1253    reader->mode = XML_TEXTREADER_MODE_EOF;
1254    if (reader->ctxt != NULL) {
1255	if (reader->ctxt->myDoc != NULL) {
1256	    xmlFreeDoc(reader->ctxt->myDoc);
1257	    reader->ctxt->myDoc = NULL;
1258	}
1259	if (reader->allocs & XML_TEXTREADER_CTXT) {
1260	    xmlFreeParserCtxt(reader->ctxt);
1261	    reader->allocs -= XML_TEXTREADER_CTXT;
1262	}
1263    }
1264    if (reader->sax != NULL) {
1265        xmlFree(reader->sax);
1266	reader->sax = NULL;
1267    }
1268    if (reader->allocs & XML_TEXTREADER_INPUT) {
1269	ret = reader->input;
1270	reader->allocs -= XML_TEXTREADER_INPUT;
1271    } else {
1272	/*
1273	 * Hum, one may need to duplicate the data structure because
1274	 * without reference counting the input may be freed twice:
1275	 *   - by the layer which allocated it.
1276	 *   - by the layer to which would have been returned to.
1277	 */
1278	TODO
1279	return(NULL);
1280    }
1281    return(ret);
1282}
1283
1284/**
1285 * xmlTextReaderLookupNamespace:
1286 * @reader:  the xmlTextReaderPtr used
1287 * @prefix: the prefix whose namespace URI is to be resolved. To return
1288 *          the default namespace, specify NULL
1289 *
1290 * Resolves a namespace prefix in the scope of the current element.
1291 *
1292 * Returns a string containing the namespace URI to which the prefix maps
1293 *    or NULL in case of error. The string must be deallocated by the caller.
1294 */
1295xmlChar *
1296xmlTextReaderLookupNamespace(xmlTextReaderPtr reader, const xmlChar *prefix) {
1297    xmlNsPtr ns;
1298
1299    if (reader == NULL)
1300	return(NULL);
1301    if (reader->node == NULL)
1302	return(NULL);
1303
1304    ns = xmlSearchNs(reader->node->doc, reader->node, prefix);
1305    if (ns == NULL)
1306	return(NULL);
1307    return(xmlStrdup(ns->href));
1308}
1309
1310/**
1311 * xmlTextReaderMoveToAttributeNo:
1312 * @reader:  the xmlTextReaderPtr used
1313 * @no: the zero-based index of the attribute relative to the containing
1314 *      element.
1315 *
1316 * Moves the position of the current instance to the attribute with
1317 * the specified index relative to the containing element.
1318 *
1319 * Returns 1 in case of success, -1 in case of error, 0 if not found
1320 */
1321int
1322xmlTextReaderMoveToAttributeNo(xmlTextReaderPtr reader, int no) {
1323    int i;
1324    xmlAttrPtr cur;
1325    xmlNsPtr ns;
1326
1327    if (reader == NULL)
1328	return(-1);
1329    if (reader->node == NULL)
1330	return(-1);
1331    /* TODO: handle the xmlDecl */
1332    if (reader->node->type != XML_ELEMENT_NODE)
1333	return(-1);
1334
1335    reader->curnode = NULL;
1336
1337    ns = reader->node->nsDef;
1338    for (i = 0;(i < no) && (ns != NULL);i++) {
1339	ns = ns->next;
1340    }
1341    if (ns != NULL) {
1342	reader->curnode = (xmlNodePtr) ns;
1343	return(1);
1344    }
1345
1346    cur = reader->node->properties;
1347    if (cur == NULL)
1348	return(0);
1349    for (;i < no;i++) {
1350	cur = cur->next;
1351	if (cur == NULL)
1352	    return(0);
1353    }
1354    /* TODO walk the DTD if present */
1355
1356    reader->curnode = (xmlNodePtr) cur;
1357    return(1);
1358}
1359
1360/**
1361 * xmlTextReaderMoveToAttribute:
1362 * @reader:  the xmlTextReaderPtr used
1363 * @name: the qualified name of the attribute.
1364 *
1365 * Moves the position of the current instance to the attribute with
1366 * the specified qualified name.
1367 *
1368 * Returns 1 in case of success, -1 in case of error, 0 if not found
1369 */
1370int
1371xmlTextReaderMoveToAttribute(xmlTextReaderPtr reader, const xmlChar *name) {
1372    xmlChar *prefix = NULL;
1373    xmlChar *localname;
1374    xmlNsPtr ns;
1375    xmlAttrPtr prop;
1376
1377    if ((reader == NULL) || (name == NULL))
1378	return(-1);
1379    if (reader->node == NULL)
1380	return(-1);
1381
1382    /* TODO: handle the xmlDecl */
1383    if (reader->node->type != XML_ELEMENT_NODE)
1384	return(0);
1385
1386    localname = xmlSplitQName2(name, &prefix);
1387    if (localname == NULL) {
1388	/*
1389	 * Namespace default decl
1390	 */
1391	if (xmlStrEqual(name, BAD_CAST "xmlns")) {
1392	    ns = reader->node->nsDef;
1393	    while (ns != NULL) {
1394		if (ns->prefix == NULL) {
1395		    reader->curnode = (xmlNodePtr) ns;
1396		    return(1);
1397		}
1398		ns = ns->next;
1399	    }
1400	    return(0);
1401	}
1402
1403	prop = reader->node->properties;
1404	while (prop != NULL) {
1405	    /*
1406	     * One need to have
1407	     *   - same attribute names
1408	     *   - and the attribute carrying that namespace
1409	     */
1410	    if ((xmlStrEqual(prop->name, name)) &&
1411		((prop->ns == NULL) || (prop->ns->prefix == NULL))) {
1412		reader->curnode = (xmlNodePtr) prop;
1413		return(1);
1414	    }
1415	    prop = prop->next;
1416	}
1417	return(0);
1418    }
1419
1420    /*
1421     * Namespace default decl
1422     */
1423    if (xmlStrEqual(prefix, BAD_CAST "xmlns")) {
1424	ns = reader->node->nsDef;
1425	while (ns != NULL) {
1426	    if ((ns->prefix != NULL) && (xmlStrEqual(ns->prefix, localname))) {
1427		reader->curnode = (xmlNodePtr) ns;
1428		goto found;
1429	    }
1430	    ns = ns->next;
1431	}
1432	goto not_found;
1433    }
1434    prop = reader->node->properties;
1435    while (prop != NULL) {
1436	/*
1437	 * One need to have
1438	 *   - same attribute names
1439	 *   - and the attribute carrying that namespace
1440	 */
1441	if ((xmlStrEqual(prop->name, localname)) &&
1442	    (prop->ns != NULL) && (xmlStrEqual(prop->ns->prefix, prefix))) {
1443	    reader->curnode = (xmlNodePtr) prop;
1444	    goto found;
1445	}
1446	prop = prop->next;
1447    }
1448not_found:
1449    if (localname != NULL)
1450        xmlFree(localname);
1451    if (prefix != NULL)
1452        xmlFree(prefix);
1453    return(0);
1454
1455found:
1456    if (localname != NULL)
1457        xmlFree(localname);
1458    if (prefix != NULL)
1459        xmlFree(prefix);
1460    return(1);
1461}
1462
1463/**
1464 * xmlTextReaderMoveToAttributeNs:
1465 * @reader:  the xmlTextReaderPtr used
1466 * @localName:  the local name of the attribute.
1467 * @namespaceURI:  the namespace URI of the attribute.
1468 *
1469 * Moves the position of the current instance to the attribute with the
1470 * specified local name and namespace URI.
1471 *
1472 * Returns 1 in case of success, -1 in case of error, 0 if not found
1473 */
1474int
1475xmlTextReaderMoveToAttributeNs(xmlTextReaderPtr reader,
1476	const xmlChar *localName, const xmlChar *namespaceURI) {
1477    xmlAttrPtr prop;
1478    xmlNodePtr node;
1479
1480    if ((reader == NULL) || (localName == NULL) || (namespaceURI == NULL))
1481	return(-1);
1482    if (reader->node == NULL)
1483	return(-1);
1484    if (reader->node->type != XML_ELEMENT_NODE)
1485	return(0);
1486    node = reader->node;
1487
1488    /*
1489     * A priori reading http://www.w3.org/TR/REC-xml-names/ there is no
1490     * namespace name associated to "xmlns"
1491     */
1492    prop = node->properties;
1493    while (prop != NULL) {
1494	/*
1495	 * One need to have
1496	 *   - same attribute names
1497	 *   - and the attribute carrying that namespace
1498	 */
1499        if (xmlStrEqual(prop->name, localName) &&
1500	    ((prop->ns != NULL) &&
1501	     (xmlStrEqual(prop->ns->href, namespaceURI)))) {
1502	    reader->curnode = (xmlNodePtr) prop;
1503	    return(1);
1504        }
1505	prop = prop->next;
1506    }
1507    return(0);
1508}
1509
1510/**
1511 * xmlTextReaderMoveToFirstAttribute:
1512 * @reader:  the xmlTextReaderPtr used
1513 *
1514 * Moves the position of the current instance to the first attribute
1515 * associated with the current node.
1516 *
1517 * Returns 1 in case of success, -1 in case of error, 0 if not found
1518 */
1519int
1520xmlTextReaderMoveToFirstAttribute(xmlTextReaderPtr reader) {
1521    if (reader == NULL)
1522	return(-1);
1523    if (reader->node == NULL)
1524	return(-1);
1525    if (reader->node->type != XML_ELEMENT_NODE)
1526	return(0);
1527
1528    if (reader->node->nsDef != NULL) {
1529	reader->curnode = (xmlNodePtr) reader->node->nsDef;
1530	return(1);
1531    }
1532    if (reader->node->properties != NULL) {
1533	reader->curnode = (xmlNodePtr) reader->node->properties;
1534	return(1);
1535    }
1536    return(0);
1537}
1538
1539/**
1540 * xmlTextReaderMoveToNextAttribute:
1541 * @reader:  the xmlTextReaderPtr used
1542 *
1543 * Moves the position of the current instance to the next attribute
1544 * associated with the current node.
1545 *
1546 * Returns 1 in case of success, -1 in case of error, 0 if not found
1547 */
1548int
1549xmlTextReaderMoveToNextAttribute(xmlTextReaderPtr reader) {
1550    if (reader == NULL)
1551	return(-1);
1552    if (reader->node == NULL)
1553	return(-1);
1554    if (reader->node->type != XML_ELEMENT_NODE)
1555	return(0);
1556    if (reader->curnode == NULL)
1557	return(xmlTextReaderMoveToFirstAttribute(reader));
1558
1559    if (reader->curnode->type == XML_NAMESPACE_DECL) {
1560	xmlNsPtr ns = (xmlNsPtr) reader->curnode;
1561	if (ns->next != NULL) {
1562	    reader->curnode = (xmlNodePtr) ns->next;
1563	    return(1);
1564	}
1565	if (reader->node->properties != NULL) {
1566	    reader->curnode = (xmlNodePtr) reader->node->properties;
1567	    return(1);
1568	}
1569	return(0);
1570    } else if ((reader->curnode->type == XML_ATTRIBUTE_NODE) &&
1571	       (reader->curnode->next != NULL)) {
1572	reader->curnode = reader->curnode->next;
1573	return(1);
1574    }
1575    return(0);
1576}
1577
1578/**
1579 * xmlTextReaderMoveToElement:
1580 * @reader:  the xmlTextReaderPtr used
1581 *
1582 * Moves the position of the current instance to the node that
1583 * contains the current Attribute  node.
1584 *
1585 * Returns 1 in case of success, -1 in case of error, 0 if not moved
1586 */
1587int
1588xmlTextReaderMoveToElement(xmlTextReaderPtr reader) {
1589    if (reader == NULL)
1590	return(-1);
1591    if (reader->node == NULL)
1592	return(-1);
1593    if (reader->node->type != XML_ELEMENT_NODE)
1594	return(0);
1595    if (reader->curnode != NULL) {
1596	reader->curnode = NULL;
1597	return(1);
1598    }
1599    return(0);
1600}
1601
1602/**
1603 * xmlTextReaderReadAttributeValue:
1604 * @reader:  the xmlTextReaderPtr used
1605 *
1606 * Parses an attribute value into one or more Text and EntityReference nodes.
1607 *
1608 * Returns 1 in case of success, 0 if the reader was not positionned on an
1609 *         ttribute node or all the attribute values have been read, or -1
1610 *         in case of error.
1611 */
1612int
1613xmlTextReaderReadAttributeValue(xmlTextReaderPtr reader) {
1614    if (reader == NULL)
1615	return(-1);
1616    if (reader->node == NULL)
1617	return(-1);
1618    if (reader->curnode == NULL)
1619	return(0);
1620    if (reader->curnode->type == XML_ATTRIBUTE_NODE) {
1621	if (reader->curnode->children == NULL)
1622	    return(0);
1623	reader->curnode = reader->curnode->children;
1624    } else if (reader->curnode->type == XML_NAMESPACE_DECL) {
1625	xmlNsPtr ns = (xmlNsPtr) reader->curnode;
1626
1627	if (reader->faketext == NULL) {
1628	    reader->faketext = xmlNewDocText(reader->node->doc,
1629		                             ns->href);
1630	} else {
1631            if (reader->faketext->content != NULL)
1632		xmlFree(reader->faketext->content);
1633	    reader->faketext->content = xmlStrdup(ns->href);
1634	}
1635	reader->curnode = reader->faketext;
1636    } else {
1637	if (reader->curnode->next == NULL)
1638	    return(0);
1639	reader->curnode = reader->curnode->next;
1640    }
1641    return(1);
1642}
1643
1644/************************************************************************
1645 *									*
1646 *			Acces API to the current node			*
1647 *									*
1648 ************************************************************************/
1649/**
1650 * xmlTextReaderAttributeCount:
1651 * @reader:  the xmlTextReaderPtr used
1652 *
1653 * Provides the number of attributes of the current node
1654 *
1655 * Returns 0 i no attributes, -1 in case of error or the attribute count
1656 */
1657int
1658xmlTextReaderAttributeCount(xmlTextReaderPtr reader) {
1659    int ret;
1660    xmlAttrPtr attr;
1661    xmlNsPtr ns;
1662    xmlNodePtr node;
1663
1664    if (reader == NULL)
1665	return(-1);
1666    if (reader->node == NULL)
1667	return(0);
1668
1669    if (reader->curnode != NULL)
1670	node = reader->curnode;
1671    else
1672	node = reader->node;
1673
1674    if (node->type != XML_ELEMENT_NODE)
1675	return(0);
1676    if ((reader->state == XML_TEXTREADER_END) ||
1677	(reader->state == XML_TEXTREADER_BACKTRACK))
1678	return(0);
1679    ret = 0;
1680    attr = node->properties;
1681    while (attr != NULL) {
1682	ret++;
1683	attr = attr->next;
1684    }
1685    ns = node->nsDef;
1686    while (ns != NULL) {
1687	ret++;
1688	ns = ns->next;
1689    }
1690    return(ret);
1691}
1692
1693/**
1694 * xmlTextReaderNodeType:
1695 * @reader:  the xmlTextReaderPtr used
1696 *
1697 * Get the node type of the current node
1698 * Reference:
1699 * http://dotgnu.org/pnetlib-doc/System/Xml/XmlNodeType.html
1700 *
1701 * Returns the xmlNodeType of the current node or -1 in case of error
1702 */
1703int
1704xmlTextReaderNodeType(xmlTextReaderPtr reader) {
1705    xmlNodePtr node;
1706    if (reader == NULL)
1707	return(-1);
1708    if (reader->node == NULL)
1709	return(0);
1710    if (reader->curnode != NULL)
1711	node = reader->curnode;
1712    else
1713	node = reader->node;
1714    switch (node->type) {
1715        case XML_ELEMENT_NODE:
1716	    if ((reader->state == XML_TEXTREADER_END) ||
1717		(reader->state == XML_TEXTREADER_BACKTRACK))
1718		return(15);
1719	    return(1);
1720        case XML_NAMESPACE_DECL:
1721        case XML_ATTRIBUTE_NODE:
1722	    return(2);
1723        case XML_TEXT_NODE:
1724	    return(3); /* TODO: SignificantWhitespace == 14 Whitespace == 13 */
1725        case XML_CDATA_SECTION_NODE:
1726	    return(4);
1727        case XML_ENTITY_REF_NODE:
1728	    return(5);
1729        case XML_ENTITY_NODE:
1730	    return(6);
1731        case XML_PI_NODE:
1732	    return(7);
1733        case XML_COMMENT_NODE:
1734	    return(8);
1735        case XML_DOCUMENT_NODE:
1736        case XML_HTML_DOCUMENT_NODE:
1737#ifdef LIBXML_DOCB_ENABLED
1738        case XML_DOCB_DOCUMENT_NODE:
1739#endif
1740	    return(9);
1741        case XML_DOCUMENT_FRAG_NODE:
1742	    return(11);
1743        case XML_NOTATION_NODE:
1744	    return(12);
1745        case XML_DOCUMENT_TYPE_NODE:
1746        case XML_DTD_NODE:
1747	    return(10);
1748
1749        case XML_ELEMENT_DECL:
1750        case XML_ATTRIBUTE_DECL:
1751        case XML_ENTITY_DECL:
1752        case XML_XINCLUDE_START:
1753        case XML_XINCLUDE_END:
1754	    return(0);
1755    }
1756    return(-1);
1757}
1758
1759/**
1760 * xmlTextReaderIsEmptyElement:
1761 * @reader:  the xmlTextReaderPtr used
1762 *
1763 * Check if the current node is empty
1764 *
1765 * Returns 1 if empty, 0 if not and -1 in case of error
1766 */
1767int
1768xmlTextReaderIsEmptyElement(xmlTextReaderPtr reader) {
1769    if ((reader == NULL) || (reader->node == NULL))
1770	return(-1);
1771    if (reader->node->type != XML_ELEMENT_NODE)
1772	return(0);
1773    if (reader->curnode != NULL)
1774	return(0);
1775    if (reader->node->children != NULL)
1776	return(0);
1777    if (reader->state == XML_TEXTREADER_END)
1778	return(0);
1779    return(reader->node->_private == (void *)xmlTextReaderIsEmpty);
1780}
1781
1782/**
1783 * xmlTextReaderLocalName:
1784 * @reader:  the xmlTextReaderPtr used
1785 *
1786 * The local name of the node.
1787 *
1788 * Returns the local name or NULL if not available
1789 */
1790xmlChar *
1791xmlTextReaderLocalName(xmlTextReaderPtr reader) {
1792    xmlNodePtr node;
1793    if ((reader == NULL) || (reader->node == NULL))
1794	return(NULL);
1795    if (reader->curnode != NULL)
1796	node = reader->curnode;
1797    else
1798	node = reader->node;
1799    if (node->type == XML_NAMESPACE_DECL) {
1800	xmlNsPtr ns = (xmlNsPtr) node;
1801	if (ns->prefix == NULL)
1802	    return(xmlStrdup(BAD_CAST "xmlns"));
1803	else
1804	    return(xmlStrdup(ns->prefix));
1805    }
1806    if ((node->type != XML_ELEMENT_NODE) &&
1807	(node->type != XML_ATTRIBUTE_NODE))
1808	return(xmlTextReaderName(reader));
1809    return(xmlStrdup(node->name));
1810}
1811
1812/**
1813 * xmlTextReaderName:
1814 * @reader:  the xmlTextReaderPtr used
1815 *
1816 * The qualified name of the node, equal to Prefix :LocalName.
1817 *
1818 * Returns the local name or NULL if not available
1819 */
1820xmlChar *
1821xmlTextReaderName(xmlTextReaderPtr reader) {
1822    xmlNodePtr node;
1823    xmlChar *ret;
1824
1825    if ((reader == NULL) || (reader->node == NULL))
1826	return(NULL);
1827    if (reader->curnode != NULL)
1828	node = reader->curnode;
1829    else
1830	node = reader->node;
1831    switch (node->type) {
1832        case XML_ELEMENT_NODE:
1833        case XML_ATTRIBUTE_NODE:
1834	    if ((node->ns == NULL) ||
1835		(node->ns->prefix == NULL))
1836		return(xmlStrdup(node->name));
1837
1838	    ret = xmlStrdup(node->ns->prefix);
1839	    ret = xmlStrcat(ret, BAD_CAST ":");
1840	    ret = xmlStrcat(ret, node->name);
1841	    return(ret);
1842        case XML_TEXT_NODE:
1843	    return(xmlStrdup(BAD_CAST "#text"));
1844        case XML_CDATA_SECTION_NODE:
1845	    return(xmlStrdup(BAD_CAST "#cdata-section"));
1846        case XML_ENTITY_NODE:
1847        case XML_ENTITY_REF_NODE:
1848	    return(xmlStrdup(node->name));
1849        case XML_PI_NODE:
1850	    return(xmlStrdup(node->name));
1851        case XML_COMMENT_NODE:
1852	    return(xmlStrdup(BAD_CAST "#comment"));
1853        case XML_DOCUMENT_NODE:
1854        case XML_HTML_DOCUMENT_NODE:
1855#ifdef LIBXML_DOCB_ENABLED
1856        case XML_DOCB_DOCUMENT_NODE:
1857#endif
1858	    return(xmlStrdup(BAD_CAST "#document"));
1859        case XML_DOCUMENT_FRAG_NODE:
1860	    return(xmlStrdup(BAD_CAST "#document-fragment"));
1861        case XML_NOTATION_NODE:
1862	    return(xmlStrdup(node->name));
1863        case XML_DOCUMENT_TYPE_NODE:
1864        case XML_DTD_NODE:
1865	    return(xmlStrdup(node->name));
1866        case XML_NAMESPACE_DECL: {
1867	    xmlNsPtr ns = (xmlNsPtr) node;
1868
1869	    ret = xmlStrdup(BAD_CAST "xmlns");
1870	    if (ns->prefix == NULL)
1871		return(ret);
1872	    ret = xmlStrcat(ret, BAD_CAST ":");
1873	    ret = xmlStrcat(ret, ns->prefix);
1874	    return(ret);
1875	}
1876
1877        case XML_ELEMENT_DECL:
1878        case XML_ATTRIBUTE_DECL:
1879        case XML_ENTITY_DECL:
1880        case XML_XINCLUDE_START:
1881        case XML_XINCLUDE_END:
1882	    return(NULL);
1883    }
1884    return(NULL);
1885}
1886
1887/**
1888 * xmlTextReaderPrefix:
1889 * @reader:  the xmlTextReaderPtr used
1890 *
1891 * A shorthand reference to the namespace associated with the node.
1892 *
1893 * Returns the prefix or NULL if not available
1894 */
1895xmlChar *
1896xmlTextReaderPrefix(xmlTextReaderPtr reader) {
1897    xmlNodePtr node;
1898    if ((reader == NULL) || (reader->node == NULL))
1899	return(NULL);
1900    if (reader->curnode != NULL)
1901	node = reader->curnode;
1902    else
1903	node = reader->node;
1904    if (node->type == XML_NAMESPACE_DECL) {
1905	xmlNsPtr ns = (xmlNsPtr) node;
1906	if (ns->prefix == NULL)
1907	    return(NULL);
1908	return(xmlStrdup(BAD_CAST "xmlns"));
1909    }
1910    if ((node->type != XML_ELEMENT_NODE) &&
1911	(node->type != XML_ATTRIBUTE_NODE))
1912	return(NULL);
1913    if ((node->ns != NULL) && (node->ns->prefix != NULL))
1914	return(xmlStrdup(node->ns->prefix));
1915    return(NULL);
1916}
1917
1918/**
1919 * xmlTextReaderNamespaceUri:
1920 * @reader:  the xmlTextReaderPtr used
1921 *
1922 * The URI defining the namespace associated with the node.
1923 *
1924 * Returns the namespace URI or NULL if not available
1925 */
1926xmlChar *
1927xmlTextReaderNamespaceUri(xmlTextReaderPtr reader) {
1928    xmlNodePtr node;
1929    if ((reader == NULL) || (reader->node == NULL))
1930	return(NULL);
1931    if (reader->curnode != NULL)
1932	node = reader->curnode;
1933    else
1934	node = reader->node;
1935    if (node->type == XML_NAMESPACE_DECL)
1936	return(xmlStrdup(BAD_CAST "http://www.w3.org/2000/xmlns/"));
1937    if ((node->type != XML_ELEMENT_NODE) &&
1938	(node->type != XML_ATTRIBUTE_NODE))
1939	return(NULL);
1940    if (node->ns != NULL)
1941	return(xmlStrdup(node->ns->href));
1942    return(NULL);
1943}
1944
1945/**
1946 * xmlTextReaderBaseUri:
1947 * @reader:  the xmlTextReaderPtr used
1948 *
1949 * The base URI of the node.
1950 *
1951 * Returns the base URI or NULL if not available
1952 */
1953xmlChar *
1954xmlTextReaderBaseUri(xmlTextReaderPtr reader) {
1955    if ((reader == NULL) || (reader->node == NULL))
1956	return(NULL);
1957    return(xmlNodeGetBase(NULL, reader->node));
1958}
1959
1960/**
1961 * xmlTextReaderDepth:
1962 * @reader:  the xmlTextReaderPtr used
1963 *
1964 * The depth of the node in the tree.
1965 *
1966 * Returns the depth or -1 in case of error
1967 */
1968int
1969xmlTextReaderDepth(xmlTextReaderPtr reader) {
1970    if (reader == NULL)
1971	return(-1);
1972    if (reader->node == NULL)
1973	return(0);
1974
1975    if (reader->curnode != NULL) {
1976	if ((reader->curnode->type == XML_ATTRIBUTE_NODE) ||
1977	    (reader->curnode->type == XML_NAMESPACE_DECL))
1978	    return(reader->depth + 1);
1979	return(reader->depth + 2);
1980    }
1981    return(reader->depth);
1982}
1983
1984/**
1985 * xmlTextReaderHasAttributes:
1986 * @reader:  the xmlTextReaderPtr used
1987 *
1988 * Whether the node has attributes.
1989 *
1990 * Returns 1 if true, 0 if false, and -1 in case or error
1991 */
1992int
1993xmlTextReaderHasAttributes(xmlTextReaderPtr reader) {
1994    xmlNodePtr node;
1995    if (reader == NULL)
1996	return(-1);
1997    if (reader->node == NULL)
1998	return(0);
1999    if (reader->curnode != NULL)
2000	node = reader->curnode;
2001    else
2002	node = reader->node;
2003
2004    if ((node->type == XML_ELEMENT_NODE) &&
2005	(node->properties != NULL))
2006	return(1);
2007    /* TODO: handle the xmlDecl */
2008    return(0);
2009}
2010
2011/**
2012 * xmlTextReaderHasValue:
2013 * @reader:  the xmlTextReaderPtr used
2014 *
2015 * Whether the node can have a text value.
2016 *
2017 * Returns 1 if true, 0 if false, and -1 in case or error
2018 */
2019int
2020xmlTextReaderHasValue(xmlTextReaderPtr reader) {
2021    xmlNodePtr node;
2022    if (reader == NULL)
2023	return(-1);
2024    if (reader->node == NULL)
2025	return(0);
2026    if (reader->curnode != NULL)
2027	node = reader->curnode;
2028    else
2029	node = reader->node;
2030
2031    switch (node->type) {
2032        case XML_ATTRIBUTE_NODE:
2033        case XML_TEXT_NODE:
2034        case XML_CDATA_SECTION_NODE:
2035        case XML_PI_NODE:
2036        case XML_COMMENT_NODE:
2037        case XML_NAMESPACE_DECL:
2038	    return(1);
2039	default:
2040	    break;
2041    }
2042    return(0);
2043}
2044
2045/**
2046 * xmlTextReaderValue:
2047 * @reader:  the xmlTextReaderPtr used
2048 *
2049 * Provides the text value of the node if present
2050 *
2051 * Returns the string or NULL if not available. The retsult must be deallocated
2052 *     with xmlFree()
2053 */
2054xmlChar *
2055xmlTextReaderValue(xmlTextReaderPtr reader) {
2056    xmlNodePtr node;
2057    if (reader == NULL)
2058	return(NULL);
2059    if (reader->node == NULL)
2060	return(NULL);
2061    if (reader->curnode != NULL)
2062	node = reader->curnode;
2063    else
2064	node = reader->node;
2065
2066    switch (node->type) {
2067        case XML_NAMESPACE_DECL:
2068	    return(xmlStrdup(((xmlNsPtr) node)->href));
2069        case XML_ATTRIBUTE_NODE:{
2070	    xmlAttrPtr attr = (xmlAttrPtr) node;
2071
2072	    if (attr->parent != NULL)
2073		return (xmlNodeListGetString
2074			(attr->parent->doc, attr->children, 1));
2075	    else
2076		return (xmlNodeListGetString(NULL, attr->children, 1));
2077	    break;
2078	}
2079        case XML_TEXT_NODE:
2080        case XML_CDATA_SECTION_NODE:
2081        case XML_PI_NODE:
2082        case XML_COMMENT_NODE:
2083            if (node->content != NULL)
2084                return (xmlStrdup(node->content));
2085	default:
2086	    break;
2087    }
2088    return(NULL);
2089}
2090
2091/**
2092 * xmlTextReaderIsDefault:
2093 * @reader:  the xmlTextReaderPtr used
2094 *
2095 * Whether an Attribute  node was generated from the default value
2096 * defined in the DTD or schema.
2097 *
2098 * Returns 0 if not defaulted, 1 if defaulted, and -1 in case of error
2099 */
2100int
2101xmlTextReaderIsDefault(xmlTextReaderPtr reader) {
2102    if (reader == NULL)
2103	return(-1);
2104    return(0);
2105}
2106
2107/**
2108 * xmlTextReaderQuoteChar:
2109 * @reader:  the xmlTextReaderPtr used
2110 *
2111 * The quotation mark character used to enclose the value of an attribute.
2112 *
2113 * Returns " or ' and -1 in case of error
2114 */
2115int
2116xmlTextReaderQuoteChar(xmlTextReaderPtr reader) {
2117    if (reader == NULL)
2118	return(-1);
2119    /* TODO maybe lookup the attribute value for " first */
2120    return((int) '"');
2121}
2122
2123/**
2124 * xmlTextReaderXmlLang:
2125 * @reader:  the xmlTextReaderPtr used
2126 *
2127 * The xml:lang scope within which the node resides.
2128 *
2129 * Returns the xml:lang value or NULL if none exists.
2130 */
2131xmlChar *
2132xmlTextReaderXmlLang(xmlTextReaderPtr reader) {
2133    if (reader == NULL)
2134	return(NULL);
2135    if (reader->node == NULL)
2136	return(NULL);
2137    return(xmlNodeGetLang(reader->node));
2138}
2139
2140/**
2141 * xmlTextReaderNormalization:
2142 * @reader:  the xmlTextReaderPtr used
2143 *
2144 * The value indicating whether to normalize white space and attribute values.
2145 * Since attribute value and end of line normalizations are a MUST in the XML
2146 * specification only the value true is accepted. The broken bahaviour of
2147 * accepting out of range character entities like &#0; is of course not
2148 * supported either.
2149 *
2150 * Returns 1 or -1 in case of error.
2151 */
2152int
2153xmlTextReaderNormalization(xmlTextReaderPtr reader) {
2154    if (reader == NULL)
2155	return(-1);
2156    return(1);
2157}
2158
2159/************************************************************************
2160 *									*
2161 *			Extensions to the base APIs			*
2162 *									*
2163 ************************************************************************/
2164
2165/**
2166 * xmlTextReaderSetParserProp:
2167 * @reader:  the xmlTextReaderPtr used
2168 * @prop:  the xmlParserProperties to set
2169 * @value:  usually 0 or 1 to (de)activate it
2170 *
2171 * Change the parser processing behaviour by changing some of its internal
2172 * properties. Note that some properties can only be changed before any
2173 * read has been done.
2174 *
2175 * Returns 0 if the call was successful, or -1 in case of error
2176 */
2177int
2178xmlTextReaderSetParserProp(xmlTextReaderPtr reader, int prop, int value) {
2179    xmlParserProperties p = (xmlParserProperties) prop;
2180    xmlParserCtxtPtr ctxt;
2181
2182    if ((reader == NULL) || (reader->ctxt == NULL))
2183	return(-1);
2184    ctxt = reader->ctxt;
2185
2186    switch (p) {
2187        case XML_PARSER_LOADDTD:
2188	    if (value != 0) {
2189		if (ctxt->loadsubset == 0) {
2190		    if (reader->mode != XML_TEXTREADER_MODE_INITIAL)
2191			return(-1);
2192		    ctxt->loadsubset = XML_DETECT_IDS;
2193		}
2194	    } else {
2195		ctxt->loadsubset = 0;
2196	    }
2197	    return(0);
2198        case XML_PARSER_DEFAULTATTRS:
2199	    if (value != 0) {
2200		ctxt->loadsubset |= XML_COMPLETE_ATTRS;
2201	    } else {
2202		if (ctxt->loadsubset & XML_COMPLETE_ATTRS)
2203		    ctxt->loadsubset -= XML_COMPLETE_ATTRS;
2204	    }
2205	    return(0);
2206        case XML_PARSER_VALIDATE:
2207	    if (value != 0) {
2208		ctxt->validate = 1;
2209	    } else {
2210		ctxt->validate = 0;
2211	    }
2212	    return(0);
2213        case XML_PARSER_SUBST_ENTITIES:
2214	    if (value != 0) {
2215		ctxt->replaceEntities = 1;
2216	    } else {
2217		ctxt->replaceEntities = 0;
2218	    }
2219	    return(0);
2220    }
2221    return(-1);
2222}
2223
2224/**
2225 * xmlTextReaderGetParserProp:
2226 * @reader:  the xmlTextReaderPtr used
2227 * @prop:  the xmlParserProperties to get
2228 *
2229 * Read the parser internal property.
2230 *
2231 * Returns the value, usually 0 or 1, or -1 in case of error.
2232 */
2233int
2234xmlTextReaderGetParserProp(xmlTextReaderPtr reader, int prop) {
2235    xmlParserProperties p = (xmlParserProperties) prop;
2236    xmlParserCtxtPtr ctxt;
2237
2238    if ((reader == NULL) || (reader->ctxt == NULL))
2239	return(-1);
2240    ctxt = reader->ctxt;
2241
2242    switch (p) {
2243        case XML_PARSER_LOADDTD:
2244	    if ((ctxt->loadsubset != 0) || (ctxt->validate != 0))
2245		return(1);
2246	    return(0);
2247        case XML_PARSER_DEFAULTATTRS:
2248	    if (ctxt->loadsubset & XML_COMPLETE_ATTRS)
2249		return(1);
2250	    return(0);
2251        case XML_PARSER_VALIDATE:
2252	    return(ctxt->validate);
2253	case XML_PARSER_SUBST_ENTITIES:
2254	    return(ctxt->replaceEntities);
2255    }
2256    return(-1);
2257}
2258
2259/**
2260 * xmlTextReaderCurrentNode:
2261 * @reader:  the xmlTextReaderPtr used
2262 *
2263 * Hacking interface allowing to get the xmlNodePtr correponding to the
2264 * current node being accessed by the xmlTextReader. This is dangerous
2265 * because the underlying node may be destroyed on the next Reads.
2266 *
2267 * Returns the xmlNodePtr or NULL in case of error.
2268 */
2269xmlNodePtr
2270xmlTextReaderCurrentNode(xmlTextReaderPtr reader) {
2271    if (reader == NULL)
2272	return(NULL);
2273
2274    if (reader->curnode != NULL)
2275	return(reader->curnode);
2276    return(reader->node);
2277}
2278
2279/**
2280 * xmlTextReaderCurrentDoc:
2281 * @reader:  the xmlTextReaderPtr used
2282 *
2283 * Hacking interface allowing to get the xmlDocPtr correponding to the
2284 * current document being accessed by the xmlTextReader. This is dangerous
2285 * because the associated node may be destroyed on the next Reads.
2286 *
2287 * Returns the xmlDocPtr or NULL in case of error.
2288 */
2289xmlDocPtr
2290xmlTextReaderCurrentDoc(xmlTextReaderPtr reader) {
2291    if ((reader == NULL) || (reader->ctxt == NULL))
2292	return(NULL);
2293
2294    return(reader->ctxt->myDoc);
2295}
2296
2297/************************************************************************
2298 *									*
2299 *			Error Handling Extensions                       *
2300 *									*
2301 ************************************************************************/
2302
2303/* helper to build a xmlMalloc'ed string from a format and va_list */
2304static char *
2305xmlTextReaderBuildMessage(const char *msg, va_list ap) {
2306    int size;
2307    int chars;
2308    char *larger;
2309    char *str;
2310
2311    str = (char *) xmlMalloc(150);
2312    if (str == NULL) {
2313	xmlGenericError(xmlGenericErrorContext, "xmlMalloc failed !\n");
2314        return NULL;
2315    }
2316
2317    size = 150;
2318
2319    while (1) {
2320        chars = vsnprintf(str, size, msg, ap);
2321        if ((chars > -1) && (chars < size))
2322            break;
2323        if (chars > -1)
2324            size += chars + 1;
2325        else
2326            size += 100;
2327        if ((larger = (char *) xmlRealloc(str, size)) == NULL) {
2328	    xmlGenericError(xmlGenericErrorContext, "xmlRealloc failed !\n");
2329            xmlFree(str);
2330            return NULL;
2331        }
2332        str = larger;
2333    }
2334
2335    return str;
2336}
2337
2338/**
2339 * xmlTextReaderLocatorLineNumber:
2340 * @locator: the xmlTextReaderLocatorPtr used
2341 *
2342 * Obtain the line number for the given locator.
2343 *
2344 * Returns the line number or -1 in case of error.
2345 */
2346int
2347xmlTextReaderLocatorLineNumber(xmlTextReaderLocatorPtr locator) {
2348    /* we know that locator is a xmlParserCtxtPtr */
2349    xmlParserCtxtPtr ctx = (xmlParserCtxtPtr)locator;
2350    int ret = -1;
2351
2352    if (ctx->node != NULL) {
2353	ret = xmlGetLineNo(ctx->node);
2354    }
2355    else {
2356	/* inspired from error.c */
2357	xmlParserInputPtr input;
2358	input = ctx->input;
2359	if ((input->filename == NULL) && (ctx->inputNr > 1))
2360	    input = ctx->inputTab[ctx->inputNr - 2];
2361	if (input != NULL) {
2362	    ret = input->line;
2363	}
2364	else {
2365	    ret = -1;
2366	}
2367    }
2368
2369    return ret;
2370}
2371
2372/**
2373 * xmlTextReaderLocatorBaseURI:
2374 * @locator: the xmlTextReaderLocatorPtr used
2375 *
2376 * Obtain the base URI for the given locator.
2377 *
2378 * Returns the base URI or NULL in case of error.
2379 */
2380xmlChar *
2381xmlTextReaderLocatorBaseURI(xmlTextReaderLocatorPtr locator) {
2382    /* we know that locator is a xmlParserCtxtPtr */
2383    xmlParserCtxtPtr ctx = (xmlParserCtxtPtr)locator;
2384    xmlChar *ret = NULL;
2385
2386    if (ctx->node != NULL) {
2387	ret = xmlNodeGetBase(NULL,ctx->node);
2388    }
2389    else {
2390	/* inspired from error.c */
2391	xmlParserInputPtr input;
2392	input = ctx->input;
2393	if ((input->filename == NULL) && (ctx->inputNr > 1))
2394	    input = ctx->inputTab[ctx->inputNr - 2];
2395	if (input != NULL) {
2396	    ret = xmlStrdup(BAD_CAST input->filename);
2397	}
2398	else {
2399	    ret = NULL;
2400	}
2401    }
2402
2403    return ret;
2404}
2405
2406static void
2407xmlTextReaderGenericError(void *ctxt, int severity, char *str) {
2408    xmlParserCtxtPtr ctx = (xmlParserCtxtPtr)ctxt;
2409    xmlTextReaderPtr reader = (xmlTextReaderPtr)ctx->_private;
2410
2411    if (str != NULL) {
2412	reader->errorFunc(reader->errorFuncArg,
2413			  str,
2414			  severity,
2415			  (xmlTextReaderLocatorPtr)ctx);
2416	xmlFree(str);
2417    }
2418}
2419
2420static void
2421xmlTextReaderError(void *ctxt, const char *msg, ...) {
2422    va_list ap;
2423
2424    va_start(ap,msg);
2425    xmlTextReaderGenericError(ctxt,
2426                              XML_PARSER_SEVERITY_ERROR,
2427	                      xmlTextReaderBuildMessage(msg,ap));
2428    va_end(ap);
2429
2430}
2431
2432static void
2433xmlTextReaderWarning(void *ctxt, const char *msg, ...) {
2434    va_list ap;
2435
2436    va_start(ap,msg);
2437    xmlTextReaderGenericError(ctxt,
2438                              XML_PARSER_SEVERITY_WARNING,
2439	                      xmlTextReaderBuildMessage(msg,ap));
2440    va_end(ap);
2441}
2442
2443static void
2444xmlTextReaderValidityError(void *ctxt, const char *msg, ...) {
2445    va_list ap;
2446    int len = xmlStrlen((const xmlChar *) msg);
2447
2448    if ((len > 1) && (msg[len - 2] != ':')) {
2449	/*
2450	 * some callbacks only report locator information:
2451	 * skip them (mimicking behaviour in error.c)
2452	 */
2453	va_start(ap,msg);
2454	xmlTextReaderGenericError(ctxt,
2455				  XML_PARSER_SEVERITY_VALIDITY_ERROR,
2456				  xmlTextReaderBuildMessage(msg,ap));
2457	va_end(ap);
2458    }
2459}
2460
2461static void
2462xmlTextReaderValidityWarning(void *ctxt, const char *msg, ...) {
2463    va_list ap;
2464    int len = xmlStrlen((const xmlChar *) msg);
2465
2466    if ((len != 0) && (msg[len - 1] != ':')) {
2467	/*
2468	 * some callbacks only report locator information:
2469	 * skip them (mimicking behaviour in error.c)
2470	 */
2471	va_start(ap,msg);
2472	xmlTextReaderGenericError(ctxt,
2473				  XML_PARSER_SEVERITY_VALIDITY_WARNING,
2474				  xmlTextReaderBuildMessage(msg,ap));
2475	va_end(ap);
2476    }
2477}
2478
2479/**
2480 * xmlTextReaderSetErrorHandler:
2481 * @reader:  the xmlTextReaderPtr used
2482 * @f:	the callback function to call on error and warnings
2483 * @arg:    a user argument to pass to the callback function
2484 *
2485 * Register a callback function that will be called on error and warnings.
2486 *
2487 * If @f is NULL, the default error and warning handlers are restored.
2488 */
2489void
2490xmlTextReaderSetErrorHandler(xmlTextReaderPtr reader,
2491			     xmlTextReaderErrorFunc f,
2492			     void *arg) {
2493    if (f != NULL) {
2494	reader->ctxt->sax->error = xmlTextReaderError;
2495	reader->ctxt->vctxt.error = xmlTextReaderValidityError;
2496	reader->ctxt->sax->warning = xmlTextReaderWarning;
2497	reader->ctxt->vctxt.warning = xmlTextReaderValidityWarning;
2498	reader->errorFunc = f;
2499	reader->errorFuncArg = arg;
2500    }
2501    else {
2502	/* restore defaults */
2503	reader->ctxt->sax->error = xmlParserError;
2504	reader->ctxt->vctxt.error = xmlParserValidityError;
2505	reader->ctxt->sax->warning = xmlParserWarning;
2506	reader->ctxt->vctxt.warning = xmlParserValidityWarning;
2507	reader->errorFunc = NULL;
2508	reader->errorFuncArg = NULL;
2509    }
2510}
2511
2512/**
2513 * xmlTextReaderGetErrorHandler:
2514 * @reader:  the xmlTextReaderPtr used
2515 * @f:	the callback function or NULL is no callback has been registered
2516 * @arg:    a user argument
2517 *
2518 * Retrieve the error callback function and user argument.
2519 */
2520void
2521xmlTextReaderGetErrorHandler(xmlTextReaderPtr reader,
2522			     xmlTextReaderErrorFunc *f,
2523			     void **arg) {
2524    *f = reader->errorFunc;
2525    *arg = reader->errorFuncArg;
2526}
2527
2528/************************************************************************
2529 *									*
2530 *			Utilities					*
2531 *									*
2532 ************************************************************************/
2533/**
2534 * xmlBase64Decode:
2535 * @in:  the input buffer
2536 * @inlen:  the size of the input (in), the size read from it (out)
2537 * @to:  the output buffer
2538 * @tolen:  the size of the output (in), the size written to (out)
2539 *
2540 * Base64 decoder, reads from @in and save in @to
2541 * TODO: tell jody when this is actually exported
2542 *
2543 * Returns 0 if all the input was consumer, 1 if the Base64 end was reached,
2544 *         2 if there wasn't enough space on the output or -1 in case of error.
2545 */
2546static int
2547xmlBase64Decode(const unsigned char *in, unsigned long *inlen,
2548	        unsigned char *to, unsigned long *tolen) {
2549    unsigned long incur;		/* current index in in[] */
2550    unsigned long inblk;		/* last block index in in[] */
2551    unsigned long outcur;		/* current index in out[] */
2552    unsigned long inmax;		/* size of in[] */
2553    unsigned long outmax;		/* size of out[] */
2554    unsigned char cur;			/* the current value read from in[] */
2555    unsigned char intmp[3], outtmp[4];	/* temporary buffers for the convert */
2556    int nbintmp;			/* number of byte in intmp[] */
2557    int is_ignore;			/* cur should be ignored */
2558    int is_end = 0;			/* the end of the base64 was found */
2559    int retval = 1;
2560    int i;
2561
2562    if ((in == NULL) || (inlen == NULL) || (to == NULL) || (tolen == NULL))
2563	return(-1);
2564
2565    incur = 0;
2566    inblk = 0;
2567    outcur = 0;
2568    inmax = *inlen;
2569    outmax = *tolen;
2570    nbintmp = 0;
2571
2572    while (1) {
2573        if (incur >= inmax)
2574            break;
2575        cur = in[incur++];
2576        is_ignore = 0;
2577        if ((cur >= 'A') && (cur <= 'Z'))
2578            cur = cur - 'A';
2579        else if ((cur >= 'a') && (cur <= 'z'))
2580            cur = cur - 'a' + 26;
2581        else if ((cur >= '0') && (cur <= '9'))
2582            cur = cur - '0' + 52;
2583        else if (cur == '+')
2584            cur = 62;
2585        else if (cur == '/')
2586            cur = 63;
2587        else if (cur == '.')
2588            cur = 0;
2589        else if (cur == '=') /*no op , end of the base64 stream */
2590            is_end = 1;
2591        else {
2592            is_ignore = 1;
2593	    if (nbintmp == 0)
2594		inblk = incur;
2595	}
2596
2597        if (!is_ignore) {
2598            int nbouttmp = 3;
2599            int is_break = 0;
2600
2601            if (is_end) {
2602                if (nbintmp == 0)
2603                    break;
2604                if ((nbintmp == 1) || (nbintmp == 2))
2605                    nbouttmp = 1;
2606                else
2607                    nbouttmp = 2;
2608                nbintmp = 3;
2609                is_break = 1;
2610            }
2611            intmp[nbintmp++] = cur;
2612	    /*
2613	     * if intmp is full, push the 4byte sequence as a 3 byte
2614	     * sequence out
2615	     */
2616            if (nbintmp == 4) {
2617                nbintmp = 0;
2618                outtmp[0] = (intmp[0] << 2) | ((intmp[1] & 0x30) >> 4);
2619                outtmp[1] =
2620                    ((intmp[1] & 0x0F) << 4) | ((intmp[2] & 0x3C) >> 2);
2621                outtmp[2] = ((intmp[2] & 0x03) << 6) | (intmp[3] & 0x3F);
2622		if (outcur + 3 >= outmax) {
2623		    retval = 2;
2624		    break;
2625		}
2626
2627                for (i = 0; i < nbouttmp; i++)
2628		    to[outcur++] = outtmp[i];
2629		inblk = incur;
2630            }
2631
2632            if (is_break) {
2633		retval = 0;
2634                break;
2635	    }
2636        }
2637    }
2638
2639    *tolen = outcur;
2640    *inlen = inblk;
2641    return (retval);
2642}
2643
2644/*
2645 * Test routine for the xmlBase64Decode function
2646 */
2647#if 0
2648int main(int argc, char **argv) {
2649    char *input = "  VW4 gcGV0        \n      aXQgdGVzdCAuCg== ";
2650    char output[100];
2651    char output2[100];
2652    char output3[100];
2653    unsigned long inlen = strlen(input);
2654    unsigned long outlen = 100;
2655    int ret;
2656    unsigned long cons, tmp, tmp2, prod;
2657
2658    /*
2659     * Direct
2660     */
2661    ret = xmlBase64Decode(input, &inlen, output, &outlen);
2662
2663    output[outlen] = 0;
2664    printf("ret: %d, inlen: %ld , outlen: %ld, output: '%s'\n", ret, inlen, outlen, output);
2665
2666    /*
2667     * output chunking
2668     */
2669    cons = 0;
2670    prod = 0;
2671    while (cons < inlen) {
2672	tmp = 5;
2673	tmp2 = inlen - cons;
2674
2675	printf("%ld %ld\n", cons, prod);
2676	ret = xmlBase64Decode(&input[cons], &tmp2, &output2[prod], &tmp);
2677	cons += tmp2;
2678	prod += tmp;
2679	printf("%ld %ld\n", cons, prod);
2680    }
2681    output2[outlen] = 0;
2682    printf("ret: %d, cons: %ld , prod: %ld, output: '%s'\n", ret, cons, prod, output2);
2683
2684    /*
2685     * input chunking
2686     */
2687    cons = 0;
2688    prod = 0;
2689    while (cons < inlen) {
2690	tmp = 100 - prod;
2691	tmp2 = inlen - cons;
2692	if (tmp2 > 5)
2693	    tmp2 = 5;
2694
2695	printf("%ld %ld\n", cons, prod);
2696	ret = xmlBase64Decode(&input[cons], &tmp2, &output3[prod], &tmp);
2697	cons += tmp2;
2698	prod += tmp;
2699	printf("%ld %ld\n", cons, prod);
2700    }
2701    output3[outlen] = 0;
2702    printf("ret: %d, cons: %ld , prod: %ld, output: '%s'\n", ret, cons, prod, output3);
2703    return(0);
2704
2705}
2706#endif
2707