xmlreader.c revision e3c036eedde59df360f54732fc0948ffde9f3139
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#define IN_LIBXML
14#include "libxml.h"
15
16#include <string.h> /* for memset() only ! */
17
18#ifdef HAVE_CTYPE_H
19#include <ctype.h>
20#endif
21#ifdef HAVE_STDLIB_H
22#include <stdlib.h>
23#endif
24
25#include <libxml/xmlmemory.h>
26#include <libxml/xmlIO.h>
27#include <libxml/xmlreader.h>
28
29/* #define DEBUG_CALLBACKS */
30/* #define DEBUG_READER */
31
32/**
33 * TODO:
34 *
35 * macro to flag unimplemented blocks
36 */
37#define TODO 								\
38    xmlGenericError(xmlGenericErrorContext,				\
39	    "Unimplemented block at %s:%d\n",				\
40            __FILE__, __LINE__);
41
42#ifdef DEBUG_READER
43#define DUMP_READER xmlTextReaderDebug(reader);
44#else
45#define DUMP_READER
46#endif
47
48/************************************************************************
49 *									*
50 *	The parser: maps the Text Reader API on top of the existing	*
51 *		parsing routines building a tree			*
52 *									*
53 ************************************************************************/
54
55#define XML_TEXTREADER_INPUT	1
56#define XML_TEXTREADER_CTXT	2
57
58typedef enum {
59    XML_TEXTREADER_MODE_INITIAL = 0,
60    XML_TEXTREADER_MODE_INTERACTIVE = 1,
61    XML_TEXTREADER_MODE_ERROR = 2,
62    XML_TEXTREADER_MODE_EOF =3,
63    XML_TEXTREADER_MODE_CLOSED = 4,
64    XML_TEXTREADER_MODE_READING = 5
65} xmlTextReaderMode;
66
67typedef enum {
68    XML_TEXTREADER_NONE = -1,
69    XML_TEXTREADER_START= 0,
70    XML_TEXTREADER_ELEMENT= 1,
71    XML_TEXTREADER_END= 2,
72    XML_TEXTREADER_EMPTY= 3,
73    XML_TEXTREADER_BACKTRACK= 4,
74    XML_TEXTREADER_DONE= 5
75} xmlTextReaderState;
76
77struct _xmlTextReader {
78    int				mode;	/* the parsing mode */
79    int				allocs;	/* what structure were deallocated */
80    xmlTextReaderState		state;
81    xmlParserCtxtPtr		ctxt;	/* the parser context */
82    xmlSAXHandlerPtr		sax;	/* the parser SAX callbacks */
83    xmlParserInputBufferPtr	input;	/* the input */
84    startElementSAXFunc		startElement;/* initial SAX callbacks */
85    endElementSAXFunc		endElement;  /* idem */
86    charactersSAXFunc		characters;
87    cdataBlockSAXFunc		cdataBlock;
88    unsigned int 		base;	/* base of the segment in the input */
89    unsigned int 		cur;	/* current position in the input */
90    xmlNodePtr			node;	/* current node */
91    xmlNodePtr			curnode;/* current attribute node */
92    int				depth;  /* depth of the current node */
93    xmlNodePtr			faketext;/* fake xmlNs chld */
94    int				wasempty;/* was the last node empty */
95};
96
97#ifdef DEBUG_READER
98static void
99xmlTextReaderDebug(xmlTextReaderPtr reader) {
100    if ((reader == NULL) || (reader->ctxt == NULL)) {
101	fprintf(stderr, "xmlTextReader NULL\n");
102	return;
103    }
104    fprintf(stderr, "xmlTextReader: state %d depth %d ",
105	    reader->state, reader->depth);
106    if (reader->node == NULL) {
107	fprintf(stderr, "node = NULL\n");
108    } else {
109	fprintf(stderr, "node %s\n", reader->node->name);
110    }
111    fprintf(stderr, "  input: base %d, cur %d, depth %d: ",
112	    reader->base, reader->cur, reader->ctxt->nodeNr);
113    if (reader->input->buffer == NULL) {
114	fprintf(stderr, "buffer is NULL\n");
115    } else {
116#ifdef LIBXML_DEBUG_ENABLED
117	xmlDebugDumpString(stderr,
118		&reader->input->buffer->content[reader->cur]);
119#endif
120	fprintf(stderr, "\n");
121    }
122}
123#endif
124
125/**
126 * xmlTextReaderStartElement:
127 * @ctx: the user data (XML parser context)
128 * @fullname:  The element name, including namespace prefix
129 * @atts:  An array of name/value attributes pairs, NULL terminated
130 *
131 * called when an opening tag has been processed.
132 */
133static void
134xmlTextReaderStartElement(void *ctx, const xmlChar *fullname,
135	                  const xmlChar **atts) {
136    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
137    xmlParserCtxtPtr origctxt;
138    xmlTextReaderPtr reader = ctxt->_private;
139
140#ifdef DEBUG_CALLBACKS
141    printf("xmlTextReaderStartElement(%s)\n", fullname);
142#endif
143    if ((reader != NULL) && (reader->startElement != NULL)) {
144	/*
145	 * when processing an entity, the context may have been changed
146	 */
147	origctxt = reader->ctxt;
148	reader->startElement(ctx, fullname, atts);
149	if (origctxt->validate) {
150	    ctxt->valid &= xmlValidatePushElement(&origctxt->vctxt,
151				 ctxt->myDoc, ctxt->node, fullname);
152	}
153    }
154    if (reader != NULL)
155	reader->state = XML_TEXTREADER_ELEMENT;
156}
157
158/**
159 * xmlTextReaderEndElement:
160 * @ctx: the user data (XML parser context)
161 * @fullname:  The element name, including namespace prefix
162 *
163 * called when an ending tag has been processed.
164 */
165static void
166xmlTextReaderEndElement(void *ctx, const xmlChar *fullname) {
167    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
168    xmlParserCtxtPtr origctxt;
169    xmlTextReaderPtr reader = ctxt->_private;
170
171#ifdef DEBUG_CALLBACKS
172    printf("xmlTextReaderEndElement(%s)\n", fullname);
173#endif
174    if ((reader != NULL) && (reader->endElement != NULL)) {
175	xmlNodePtr node = ctxt->node;
176	/*
177	 * when processing an entity, the context may have been changed
178	 */
179	origctxt = reader->ctxt;
180
181	reader->endElement(ctx, fullname);
182
183	if (origctxt->validate) {
184	    ctxt->valid &= xmlValidatePopElement(&origctxt->vctxt,
185		                ctxt->myDoc, node, fullname);
186	}
187    }
188    if (reader != NULL) {
189	if (reader->state == XML_TEXTREADER_ELEMENT)
190	    reader->wasempty = 1;
191	else
192	    reader->wasempty = 0;
193    }
194}
195
196/**
197 * xmlTextReaderCharacters:
198 * @ctx: the user data (XML parser context)
199 * @ch:  a xmlChar string
200 * @len: the number of xmlChar
201 *
202 * receiving some chars from the parser.
203 */
204static void
205xmlTextReaderCharacters(void *ctx, const xmlChar *ch, int len)
206{
207    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
208    xmlParserCtxtPtr origctxt;
209    xmlTextReaderPtr reader = ctxt->_private;
210
211#ifdef DEBUG_CALLBACKS
212    printf("xmlTextReaderCharacters()\n");
213#endif
214    if ((reader != NULL) && (reader->characters != NULL)) {
215	reader->characters(ctx, ch, len);
216	/*
217	 * when processing an entity, the context may have been changed
218	 */
219	origctxt = reader->ctxt;
220
221	if (origctxt->validate) {
222	    ctxt->valid &= xmlValidatePushCData(&origctxt->vctxt, ch, len);
223	}
224    }
225}
226
227/**
228 * xmlTextReaderCDataBlock:
229 * @ctx: the user data (XML parser context)
230 * @value:  The pcdata content
231 * @len:  the block length
232 *
233 * called when a pcdata block has been parsed
234 */
235static void
236xmlTextReaderCDataBlock(void *ctx, const xmlChar *ch, int len)
237{
238    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
239    xmlTextReaderPtr reader = ctxt->_private;
240
241#ifdef DEBUG_CALLBACKS
242    printf("xmlTextReaderCDataBlock()\n");
243#endif
244    if ((reader != NULL) && (reader->cdataBlock != NULL)) {
245	reader->cdataBlock(ctx, ch, len);
246
247	if (ctxt->validate) {
248	    ctxt->valid &= xmlValidatePushCData(&ctxt->vctxt, ch, len);
249	}
250    }
251}
252
253/**
254 * xmlTextReaderPushData:
255 * @reader:  the xmlTextReaderPtr used
256 *
257 * Push data down the progressive parser until a significant callback
258 * got raised.
259 *
260 * Returns -1 in case of failure, 0 otherwise
261 */
262static int
263xmlTextReaderPushData(xmlTextReaderPtr reader) {
264    unsigned int cur = reader->cur;
265    xmlBufferPtr inbuf;
266    int val;
267    int oldstate;
268
269    if ((reader->input == NULL) || (reader->input->buffer == NULL))
270	return(-1);
271
272    oldstate = reader->state;
273    reader->state = XML_TEXTREADER_NONE;
274    inbuf = reader->input->buffer;
275    while (reader->state == XML_TEXTREADER_NONE) {
276	if (cur >= inbuf->use) {
277	    /*
278	     * Refill the buffer unless we are at the end of the stream
279	     */
280	    if (reader->mode != XML_TEXTREADER_MODE_EOF) {
281		val = xmlParserInputBufferRead(reader->input, 4096);
282		if (val <= 0) {
283		    reader->mode = XML_TEXTREADER_MODE_EOF;
284		    reader->state = oldstate;
285		    if ((oldstate != XML_TEXTREADER_START) ||
286			(reader->ctxt->myDoc != NULL))
287			return(val);
288		}
289	    } else
290		break;
291	}
292	if ((inbuf->content[cur] == '>') || (inbuf->content[cur] == '&')) {
293	    cur = cur + 1;
294	    val = xmlParseChunk(reader->ctxt,
295		          (const char *) &inbuf->content[reader->cur],
296			  cur - reader->cur, 0);
297	    if (val != 0)
298		return(-1);
299	    reader->cur = cur;
300	    break;
301	} else {
302	    cur = cur + 1;
303
304	    /*
305	     * One may have to force a flush at some point when parsing really
306	     * large CDATA sections
307	     */
308	    if ((cur - reader->cur > 4096) && (reader->base == 0) &&
309		(reader->mode == XML_TEXTREADER_MODE_INTERACTIVE)) {
310		cur = cur + 1;
311		val = xmlParseChunk(reader->ctxt,
312			      (const char *) &inbuf->content[reader->cur],
313			      cur - reader->cur, 0);
314		if (val != 0)
315		    return(-1);
316		reader->cur = cur;
317	    }
318	}
319    }
320    /*
321     * Discard the consumed input when needed and possible
322     */
323    if (reader->mode == XML_TEXTREADER_MODE_INTERACTIVE) {
324	if ((reader->cur >= 4096) && (reader->base == 0)) {
325	    val = xmlBufferShrink(inbuf, cur);
326	    if (val >= 0) {
327		reader->cur -= val;
328	    }
329	}
330    }
331
332    /*
333     * At the end of the stream signal that the work is done to the Push
334     * parser.
335     */
336    if (reader->mode == XML_TEXTREADER_MODE_EOF) {
337	if (reader->mode != XML_TEXTREADER_DONE) {
338	    val = xmlParseChunk(reader->ctxt,
339		    (const char *) &inbuf->content[reader->cur], 0, 1);
340	    reader->mode = XML_TEXTREADER_DONE;
341	}
342    }
343    reader->state = oldstate;
344    return(0);
345}
346
347/**
348 * xmlTextReaderRead:
349 * @reader:  the xmlTextReaderPtr used
350 *
351 *  Moves the position of the current instance to the next node in
352 *  the stream, exposing its properties.
353 *
354 *  Returns 1 if the node was read successfully, 0 if there is no more
355 *          nodes to read, or -1 in case of error
356 */
357int
358xmlTextReaderRead(xmlTextReaderPtr reader) {
359    int val, olddepth, wasempty;
360    xmlTextReaderState oldstate;
361    xmlNodePtr oldnode;
362
363    if ((reader == NULL) || (reader->ctxt == NULL))
364	return(-1);
365    if (reader->ctxt->wellFormed != 1)
366	return(-1);
367
368#ifdef DEBUG_READER
369    fprintf(stderr, "\nREAD ");
370    DUMP_READER
371#endif
372    reader->curnode = NULL;
373    if (reader->mode == XML_TEXTREADER_MODE_INITIAL) {
374	reader->mode = XML_TEXTREADER_MODE_INTERACTIVE;
375	/*
376	 * Initial state
377	 */
378	do {
379	    val = xmlTextReaderPushData(reader);
380	    if (val < 0)
381		return(-1);
382	} while ((reader->ctxt->node == NULL) &&
383		 (reader->mode != XML_TEXTREADER_MODE_EOF));
384	if (reader->ctxt->node == NULL) {
385	    if (reader->ctxt->myDoc != NULL)
386		reader->node = reader->ctxt->myDoc->children;
387	    if (reader->node == NULL)
388		return(-1);
389	} else {
390	    reader->node = reader->ctxt->nodeTab[0];
391	}
392	reader->depth = 0;
393	return(1);
394    }
395    oldstate = reader->state;
396    olddepth = reader->ctxt->nodeNr;
397    oldnode = reader->node;
398    wasempty = (((reader->wasempty == 1) && (reader->ctxt->node != NULL) &&
399	         (reader->ctxt->node->last == reader->node)) ||
400	        (reader->node != reader->ctxt->node));
401
402    /*
403     * If we are not backtracking on ancestors or examined nodes,
404     * that the parser didn't finished or that we arent at the end
405     * of stream, continue processing.
406     */
407    while (((oldstate == XML_TEXTREADER_BACKTRACK) ||
408            (reader->node->children == NULL) ||
409	    (reader->node->type == XML_ENTITY_REF_NODE) ||
410	    (reader->node->type == XML_DTD_NODE)) &&
411	   (reader->node->next == NULL) &&
412	   (reader->ctxt->nodeNr == olddepth) &&
413	   (reader->ctxt->instate != XML_PARSER_EOF)) {
414	val = xmlTextReaderPushData(reader);
415	if (val < 0)
416	    return(-1);
417	if (reader->node == NULL)
418	    return(0);
419    }
420    if (oldstate != XML_TEXTREADER_BACKTRACK) {
421	if ((reader->node->children != NULL) &&
422	    (reader->node->type != XML_ENTITY_REF_NODE) &&
423	    (reader->node->type != XML_DTD_NODE)) {
424	    reader->node = reader->node->children;
425	    reader->depth++;
426	    reader->state = XML_TEXTREADER_ELEMENT;
427	    DUMP_READER
428	    return(1);
429	}
430    }
431    if (reader->node->next != NULL) {
432	if ((oldstate == XML_TEXTREADER_ELEMENT) &&
433            (reader->node->type == XML_ELEMENT_NODE) &&
434	    (wasempty == 0)) {
435	    reader->state = XML_TEXTREADER_END;
436	    DUMP_READER
437	    return(1);
438	}
439	reader->node = reader->node->next;
440	reader->state = XML_TEXTREADER_ELEMENT;
441	DUMP_READER
442	/*
443	 * Cleanup of the old node
444	 */
445	if (oldnode->type != XML_DTD_NODE) {
446	    xmlUnlinkNode(oldnode);
447	    xmlFreeNode(oldnode);
448	}
449
450	return(1);
451    }
452    if ((oldstate == XML_TEXTREADER_ELEMENT) &&
453	(reader->node->type == XML_ELEMENT_NODE) &&
454	(wasempty == 0)) {
455	reader->state = XML_TEXTREADER_END;
456	DUMP_READER
457	return(1);
458    }
459    reader->node = reader->node->parent;
460    if ((reader->node == NULL) ||
461	(reader->node->type == XML_DOCUMENT_NODE) ||
462#ifdef LIBXML_DOCB_ENABLED
463	(reader->node->type == XML_DOCB_DOCUMENT_NODE) ||
464#endif
465	(reader->node->type == XML_HTML_DOCUMENT_NODE)) {
466	if (reader->mode != XML_TEXTREADER_DONE) {
467	    val = xmlParseChunk(reader->ctxt, "", 0, 1);
468	    reader->mode = XML_TEXTREADER_DONE;
469	}
470	reader->node = NULL;
471	reader->depth = -1;
472
473	/*
474	 * Cleanup of the old node
475	 */
476	if (oldnode->type != XML_DTD_NODE) {
477	    xmlUnlinkNode(oldnode);
478	    xmlFreeNode(oldnode);
479	}
480
481	return(0);
482    }
483    reader->depth--;
484    reader->state = XML_TEXTREADER_BACKTRACK;
485    DUMP_READER
486    return(1);
487}
488
489/**
490 * xmlTextReaderReadState:
491 * @reader:  the xmlTextReaderPtr used
492 *
493 * Gets the read state of the reader.
494 *
495 * Returns the state value, or -1 in case of error
496 */
497int
498xmlTextReaderReadState(xmlTextReaderPtr reader) {
499    if (reader == NULL)
500	return(-1);
501    return(reader->mode);
502}
503
504/**
505 * xmlTextReaderReadInnerXml:
506 * @reader:  the xmlTextReaderPtr used
507 *
508 * Reads the contents of the current node, including child nodes and markup.
509 *
510 * Returns a string containing the XML content, or NULL if the current node
511 *         is neither an element nor attribute, or has no child nodes. The
512 *         string must be deallocated by the caller.
513 */
514xmlChar *
515xmlTextReaderReadInnerXml(xmlTextReaderPtr reader) {
516    TODO
517    return(NULL);
518}
519
520/**
521 * xmlTextReaderReadOuterXml:
522 * @reader:  the xmlTextReaderPtr used
523 *
524 * Reads the contents of the current node, including child nodes and markup.
525 *
526 * Returns a string containing the XML content, or NULL if the current node
527 *         is neither an element nor attribute, or has no child nodes. The
528 *         string must be deallocated by the caller.
529 */
530xmlChar *
531xmlTextReaderReadOuterXml(xmlTextReaderPtr reader) {
532    TODO
533    return(NULL);
534}
535
536/**
537 * xmlTextReaderReadString:
538 * @reader:  the xmlTextReaderPtr used
539 *
540 * Reads the contents of an element or a text node as a string.
541 *
542 * Returns a string containing the contents of the Element or Text node,
543 *         or NULL if the reader is positioned on any other type of node.
544 *         The string must be deallocated by the caller.
545 */
546xmlChar *
547xmlTextReaderReadString(xmlTextReaderPtr reader) {
548    TODO
549    return(NULL);
550}
551
552/**
553 * xmlTextReaderReadBase64:
554 * @reader:  the xmlTextReaderPtr used
555 * @array:  a byte array to store the content.
556 * @offset:  the zero-based index into array where the method should
557 *           begin to write.
558 * @len:  the number of bytes to write.
559 *
560 * Reads and decodes the Base64 encoded contents of an element and
561 * stores the result in a byte buffer.
562 *
563 * Returns the number of bytes written to array, or zero if the current
564 *         instance is not positioned on an element or -1 in case of error.
565 */
566int
567xmlTextReaderReadBase64(xmlTextReaderPtr reader, unsigned char *array,
568	                int offset, int len) {
569    if ((reader == NULL) || (reader->ctxt == NULL))
570	return(-1);
571    if (reader->ctxt->wellFormed != 1)
572	return(-1);
573
574    if ((reader->node == NULL) || (reader->node->type == XML_ELEMENT_NODE))
575	return(0);
576    TODO
577    return(0);
578}
579
580/**
581 * xmlTextReaderReadBinHex:
582 * @reader:  the xmlTextReaderPtr used
583 * @array:  a byte array to store the content.
584 * @offset:  the zero-based index into array where the method should
585 *           begin to write.
586 * @len:  the number of bytes to write.
587 *
588 * Reads and decodes the BinHex encoded contents of an element and
589 * stores the result in a byte buffer.
590 *
591 * Returns the number of bytes written to array, or zero if the current
592 *         instance is not positioned on an element or -1 in case of error.
593 */
594int
595xmlTextReaderReadBinHex(xmlTextReaderPtr reader, unsigned char *array,
596	                int offset, int len) {
597    if ((reader == NULL) || (reader->ctxt == NULL))
598	return(-1);
599    if (reader->ctxt->wellFormed != 1)
600	return(-1);
601
602    if ((reader->node == NULL) || (reader->node->type == XML_ELEMENT_NODE))
603	return(0);
604    TODO
605    return(0);
606}
607
608/************************************************************************
609 *									*
610 *			Constructor and destructors			*
611 *									*
612 ************************************************************************/
613/**
614 * xmlNewTextReader:
615 * @input: the xmlParserInputBufferPtr used to read data
616 * @URI: the URI information for the source if available
617 *
618 * Create an xmlTextReader structure fed with @input
619 *
620 * Returns the new xmlTextReaderPtr or NULL in case of error
621 */
622xmlTextReaderPtr
623xmlNewTextReader(xmlParserInputBufferPtr input, const char *URI) {
624    xmlTextReaderPtr ret;
625    int val;
626
627    if (input == NULL)
628	return(NULL);
629    ret = xmlMalloc(sizeof(xmlTextReader));
630    if (ret == NULL) {
631        xmlGenericError(xmlGenericErrorContext,
632		"xmlNewTextReader : malloc failed\n");
633	return(NULL);
634    }
635    memset(ret, 0, sizeof(xmlTextReader));
636    ret->input = input;
637    ret->sax = (xmlSAXHandler *) xmlMalloc(sizeof(xmlSAXHandler));
638    if (ret->sax == NULL) {
639	xmlFree(ret);
640        xmlGenericError(xmlGenericErrorContext,
641		"xmlNewTextReader : malloc failed\n");
642	return(NULL);
643    }
644    memcpy(ret->sax, &xmlDefaultSAXHandler, sizeof(xmlSAXHandler));
645    ret->startElement = ret->sax->startElement;
646    ret->sax->startElement = xmlTextReaderStartElement;
647    ret->endElement = ret->sax->endElement;
648    ret->sax->endElement = xmlTextReaderEndElement;
649    ret->characters = ret->sax->characters;
650    ret->sax->characters = xmlTextReaderCharacters;
651    ret->cdataBlock = ret->sax->cdataBlock;
652    ret->sax->cdataBlock = xmlTextReaderCDataBlock;
653
654    ret->mode = XML_TEXTREADER_MODE_INITIAL;
655    ret->node = NULL;
656    ret->curnode = NULL;
657    val = xmlParserInputBufferRead(input, 4);
658    if (val >= 4) {
659	ret->ctxt = xmlCreatePushParserCtxt(ret->sax, NULL,
660			(const char *) ret->input->buffer->content, 4, URI);
661	ret->base = 0;
662	ret->cur = 4;
663    } else {
664	ret->ctxt = xmlCreatePushParserCtxt(ret->sax, NULL, NULL, 0, URI);
665	ret->base = 0;
666	ret->cur = 0;
667    }
668    ret->ctxt->_private = ret;
669    ret->ctxt->linenumbers = 1;
670    ret->allocs = XML_TEXTREADER_CTXT;
671    return(ret);
672
673}
674
675/**
676 * xmlNewTextReaderFilename:
677 * @URI: the URI of the resource to process
678 *
679 * Create an xmlTextReader structure fed with the resource at @URI
680 *
681 * Returns the new xmlTextReaderPtr or NULL in case of error
682 */
683xmlTextReaderPtr
684xmlNewTextReaderFilename(const char *URI) {
685    xmlParserInputBufferPtr input;
686    xmlTextReaderPtr ret;
687    char *directory = NULL;
688
689    input = xmlParserInputBufferCreateFilename(URI, XML_CHAR_ENCODING_NONE);
690    if (input == NULL)
691	return(NULL);
692    ret = xmlNewTextReader(input, URI);
693    if (ret == NULL) {
694	xmlFreeParserInputBuffer(input);
695	return(NULL);
696    }
697    ret->allocs |= XML_TEXTREADER_INPUT;
698    if (ret->ctxt->directory == NULL)
699        directory = xmlParserGetDirectory(URI);
700    if ((ret->ctxt->directory == NULL) && (directory != NULL))
701        ret->ctxt->directory = (char *) xmlStrdup((xmlChar *) directory);
702    if (directory != NULL)
703	xmlFree(directory);
704    return(ret);
705}
706
707/**
708 * xmlFreeTextReader:
709 * @reader:  the xmlTextReaderPtr
710 *
711 * Deallocate all the resources associated to the reader
712 */
713void
714xmlFreeTextReader(xmlTextReaderPtr reader) {
715    if (reader == NULL)
716	return;
717    if (reader->ctxt != NULL) {
718	if (reader->ctxt->myDoc != NULL) {
719	    xmlFreeDoc(reader->ctxt->myDoc);
720	    reader->ctxt->myDoc = NULL;
721	}
722	if ((reader->ctxt->vctxt.vstateTab != NULL) &&
723	    (reader->ctxt->vctxt.vstateMax > 0)){
724	    xmlFree(reader->ctxt->vctxt.vstateTab);
725	    reader->ctxt->vctxt.vstateTab = 0;
726	    reader->ctxt->vctxt.vstateMax = 0;
727	}
728	if (reader->allocs & XML_TEXTREADER_CTXT)
729	    xmlFreeParserCtxt(reader->ctxt);
730    }
731    if (reader->sax != NULL)
732	xmlFree(reader->sax);
733    if ((reader->input != NULL)  && (reader->allocs & XML_TEXTREADER_INPUT))
734	xmlFreeParserInputBuffer(reader->input);
735    if (reader->faketext != NULL) {
736	xmlFreeNode(reader->faketext);
737    }
738    xmlFree(reader);
739}
740
741/************************************************************************
742 *									*
743 *			Methods for XmlTextReader			*
744 *									*
745 ************************************************************************/
746/**
747 * xmlTextReaderClose:
748 * @reader:  the xmlTextReaderPtr used
749 *
750 * This method releases any resources allocated by the current instance
751 * changes the state to Closed and close any underlying input.
752 *
753 * Returns 0 or -1 in case of error
754 */
755int
756xmlTextReaderClose(xmlTextReaderPtr reader) {
757    if (reader == NULL)
758	return(-1);
759    reader->node = NULL;
760    reader->curnode = NULL;
761    reader->mode = XML_TEXTREADER_MODE_CLOSED;
762    if (reader->ctxt != NULL) {
763	if (reader->ctxt->myDoc != NULL) {
764	    xmlFreeDoc(reader->ctxt->myDoc);
765	    reader->ctxt->myDoc = NULL;
766	}
767	if (reader->allocs & XML_TEXTREADER_CTXT) {
768	    xmlFreeParserCtxt(reader->ctxt);
769	    reader->allocs -= XML_TEXTREADER_CTXT;
770	}
771    }
772    if (reader->sax != NULL) {
773        xmlFree(reader->sax);
774	reader->sax = NULL;
775    }
776    if ((reader->input != NULL)  && (reader->allocs & XML_TEXTREADER_INPUT)) {
777	xmlFreeParserInputBuffer(reader->input);
778	reader->allocs -= XML_TEXTREADER_INPUT;
779    }
780    return(0);
781}
782
783/**
784 * xmlTextReaderGetAttributeNo:
785 * @reader:  the xmlTextReaderPtr used
786 * @no: the zero-based index of the attribute relative to the containing element
787 *
788 * Provides the value of the attribute with the specified index relative
789 * to the containing element.
790 *
791 * Returns a string containing the value of the specified attribute, or NULL
792 *    in case of error. The string must be deallocated by the caller.
793 */
794xmlChar *
795xmlTextReaderGetAttributeNo(xmlTextReaderPtr reader, int no) {
796    xmlChar *ret;
797    int i;
798    xmlAttrPtr cur;
799    xmlNsPtr ns;
800
801    if (reader == NULL)
802	return(NULL);
803    if (reader->node == NULL)
804	return(NULL);
805    if (reader->curnode != NULL)
806	return(NULL);
807    /* TODO: handle the xmlDecl */
808    if (reader->node->type != XML_ELEMENT_NODE)
809	return(NULL);
810
811    ns = reader->node->nsDef;
812    for (i = 0;(i < no) && (ns != NULL);i++) {
813	ns = ns->next;
814    }
815    if (ns != NULL)
816	return(xmlStrdup(ns->href));
817
818    cur = reader->node->properties;
819    if (cur == NULL)
820	return(NULL);
821    for (;i < no;i++) {
822	cur = cur->next;
823	if (cur == NULL)
824	    return(NULL);
825    }
826    /* TODO walk the DTD if present */
827
828    ret = xmlNodeListGetString(reader->node->doc, cur->children, 1);
829    if (ret == NULL) return(xmlStrdup((xmlChar *)""));
830    return(ret);
831}
832
833/**
834 * xmlTextReaderGetAttribute:
835 * @reader:  the xmlTextReaderPtr used
836 * @name: the qualified name of the attribute.
837 *
838 * Provides the value of the attribute with the specified qualified name.
839 *
840 * Returns a string containing the value of the specified attribute, or NULL
841 *    in case of error. The string must be deallocated by the caller.
842 */
843xmlChar *
844xmlTextReaderGetAttribute(xmlTextReaderPtr reader, const xmlChar *name) {
845    xmlChar *prefix = NULL;
846    xmlChar *localname;
847    xmlNsPtr ns;
848    xmlChar *ret = NULL;
849
850    if ((reader == NULL) || (name == NULL))
851	return(NULL);
852    if (reader->node == NULL)
853	return(NULL);
854    if (reader->curnode != NULL)
855	return(NULL);
856
857    /* TODO: handle the xmlDecl */
858    if (reader->node->type != XML_ELEMENT_NODE)
859	return(NULL);
860
861    localname = xmlSplitQName2(name, &prefix);
862    if (localname == NULL)
863	return(xmlGetProp(reader->node, name));
864
865    ns = xmlSearchNs(reader->node->doc, reader->node, prefix);
866    if (ns != NULL)
867        ret = xmlGetNsProp(reader->node, localname, ns->href);
868
869    if (localname != NULL)
870        xmlFree(localname);
871    if (prefix != NULL)
872        xmlFree(prefix);
873    return(ret);
874}
875
876
877/**
878 * xmlTextReaderGetAttributeNs:
879 * @reader:  the xmlTextReaderPtr used
880 * @localName: the local name of the attribute.
881 * @namespaceURI: the namespace URI of the attribute.
882 *
883 * Provides the value of the specified attribute
884 *
885 * Returns a string containing the value of the specified attribute, or NULL
886 *    in case of error. The string must be deallocated by the caller.
887 */
888xmlChar *
889xmlTextReaderGetAttributeNs(xmlTextReaderPtr reader, const xmlChar *localName,
890			    const xmlChar *namespaceURI) {
891    if ((reader == NULL) || (localName == NULL))
892	return(NULL);
893    if (reader->node == NULL)
894	return(NULL);
895    if (reader->curnode != NULL)
896	return(NULL);
897
898    /* TODO: handle the xmlDecl */
899    if (reader->node->type != XML_ELEMENT_NODE)
900	return(NULL);
901
902    return(xmlGetNsProp(reader->node, localName, namespaceURI));
903}
904
905/**
906 * xmlTextReaderGetRemainder:
907 * @reader:  the xmlTextReaderPtr used
908 *
909 * Method to get the remainder of the buffered XML. this method stops the
910 * parser, set its state to End Of File and return the input stream with
911 * what is left that the parser did not use.
912 *
913 * Returns the xmlParserInputBufferPtr attached to the XML or NULL
914 *    in case of error.
915 */
916xmlParserInputBufferPtr
917xmlTextReaderGetRemainder(xmlTextReaderPtr reader) {
918    xmlParserInputBufferPtr ret = NULL;
919
920    if (reader == NULL)
921	return(NULL);
922    if (reader->node == NULL)
923	return(NULL);
924
925    reader->node = NULL;
926    reader->curnode = NULL;
927    reader->mode = XML_TEXTREADER_MODE_EOF;
928    if (reader->ctxt != NULL) {
929	if (reader->ctxt->myDoc != NULL) {
930	    xmlFreeDoc(reader->ctxt->myDoc);
931	    reader->ctxt->myDoc = NULL;
932	}
933	if (reader->allocs & XML_TEXTREADER_CTXT) {
934	    xmlFreeParserCtxt(reader->ctxt);
935	    reader->allocs -= XML_TEXTREADER_CTXT;
936	}
937    }
938    if (reader->sax != NULL) {
939        xmlFree(reader->sax);
940	reader->sax = NULL;
941    }
942    if (reader->allocs & XML_TEXTREADER_INPUT) {
943	ret = reader->input;
944	reader->allocs -= XML_TEXTREADER_INPUT;
945    } else {
946	/*
947	 * Hum, one may need to duplicate the data structure because
948	 * without reference counting the input may be freed twice:
949	 *   - by the layer which allocated it.
950	 *   - by the layer to which would have been returned to.
951	 */
952	TODO
953	return(NULL);
954    }
955    return(ret);
956}
957
958/**
959 * xmlTextReaderLookupNamespace:
960 * @reader:  the xmlTextReaderPtr used
961 * @prefix: the prefix whose namespace URI is to be resolved. To return
962 *          the default namespace, specify NULL
963 *
964 * Resolves a namespace prefix in the scope of the current element.
965 *
966 * Returns a string containing the namespace URI to which the prefix maps
967 *    or NULL in case of error. The string must be deallocated by the caller.
968 */
969xmlChar *
970xmlTextReaderLookupNamespace(xmlTextReaderPtr reader, const xmlChar *prefix) {
971    xmlNsPtr ns;
972
973    if (reader == NULL)
974	return(NULL);
975    if (reader->node == NULL)
976	return(NULL);
977
978    ns = xmlSearchNs(reader->node->doc, reader->node, prefix);
979    if (ns == NULL)
980	return(NULL);
981    return(xmlStrdup(ns->href));
982}
983
984/**
985 * xmlTextReaderMoveToAttributeNo:
986 * @reader:  the xmlTextReaderPtr used
987 * @no: the zero-based index of the attribute relative to the containing
988 *      element.
989 *
990 * Moves the position of the current instance to the attribute with
991 * the specified index relative to the containing element.
992 *
993 * Returns 1 in case of success, -1 in case of error, 0 if not found
994 */
995int
996xmlTextReaderMoveToAttributeNo(xmlTextReaderPtr reader, int no) {
997    int i;
998    xmlAttrPtr cur;
999    xmlNsPtr ns;
1000
1001    if (reader == NULL)
1002	return(-1);
1003    if (reader->node == NULL)
1004	return(-1);
1005    /* TODO: handle the xmlDecl */
1006    if (reader->node->type != XML_ELEMENT_NODE)
1007	return(-1);
1008
1009    reader->curnode = NULL;
1010
1011    ns = reader->node->nsDef;
1012    for (i = 0;(i < no) && (ns != NULL);i++) {
1013	ns = ns->next;
1014    }
1015    if (ns != NULL) {
1016	reader->curnode = (xmlNodePtr) ns;
1017	return(1);
1018    }
1019
1020    cur = reader->node->properties;
1021    if (cur == NULL)
1022	return(0);
1023    for (;i < no;i++) {
1024	cur = cur->next;
1025	if (cur == NULL)
1026	    return(0);
1027    }
1028    /* TODO walk the DTD if present */
1029
1030    reader->curnode = (xmlNodePtr) cur;
1031    return(1);
1032}
1033
1034/**
1035 * xmlTextReaderMoveToAttribute:
1036 * @reader:  the xmlTextReaderPtr used
1037 * @name: the qualified name of the attribute.
1038 *
1039 * Moves the position of the current instance to the attribute with
1040 * the specified qualified name.
1041 *
1042 * Returns 1 in case of success, -1 in case of error, 0 if not found
1043 */
1044int
1045xmlTextReaderMoveToAttribute(xmlTextReaderPtr reader, const xmlChar *name) {
1046    xmlChar *prefix = NULL;
1047    xmlChar *localname;
1048    xmlNsPtr ns;
1049    xmlAttrPtr prop;
1050
1051    if ((reader == NULL) || (name == NULL))
1052	return(-1);
1053    if (reader->node == NULL)
1054	return(-1);
1055
1056    /* TODO: handle the xmlDecl */
1057    if (reader->node->type != XML_ELEMENT_NODE)
1058	return(0);
1059
1060    localname = xmlSplitQName2(name, &prefix);
1061    if (localname == NULL) {
1062	/*
1063	 * Namespace default decl
1064	 */
1065	if (xmlStrEqual(name, BAD_CAST "xmlns")) {
1066	    ns = reader->node->nsDef;
1067	    while (ns != NULL) {
1068		if (ns->prefix == NULL) {
1069		    reader->curnode = (xmlNodePtr) ns;
1070		    return(1);
1071		}
1072		ns = ns->next;
1073	    }
1074	    return(0);
1075	}
1076
1077	prop = reader->node->properties;
1078	while (prop != NULL) {
1079	    /*
1080	     * One need to have
1081	     *   - same attribute names
1082	     *   - and the attribute carrying that namespace
1083	     */
1084	    if ((xmlStrEqual(prop->name, name)) &&
1085		((prop->ns == NULL) || (prop->ns->prefix == NULL))) {
1086		reader->curnode = (xmlNodePtr) prop;
1087		return(1);
1088	    }
1089	    prop = prop->next;
1090	}
1091	return(0);
1092    }
1093
1094    /*
1095     * Namespace default decl
1096     */
1097    if (xmlStrEqual(prefix, BAD_CAST "xmlns")) {
1098	ns = reader->node->nsDef;
1099	while (ns != NULL) {
1100	    if ((ns->prefix != NULL) && (xmlStrEqual(ns->prefix, localname))) {
1101		reader->curnode = (xmlNodePtr) ns;
1102		goto found;
1103	    }
1104	    ns = ns->next;
1105	}
1106	goto not_found;
1107    }
1108    prop = reader->node->properties;
1109    while (prop != NULL) {
1110	/*
1111	 * One need to have
1112	 *   - same attribute names
1113	 *   - and the attribute carrying that namespace
1114	 */
1115	if ((xmlStrEqual(prop->name, localname)) &&
1116	    (prop->ns != NULL) && (xmlStrEqual(prop->ns->prefix, prefix))) {
1117	    reader->curnode = (xmlNodePtr) prop;
1118	    goto found;
1119	}
1120	prop = prop->next;
1121    }
1122not_found:
1123    if (localname != NULL)
1124        xmlFree(localname);
1125    if (prefix != NULL)
1126        xmlFree(prefix);
1127    return(0);
1128
1129found:
1130    if (localname != NULL)
1131        xmlFree(localname);
1132    if (prefix != NULL)
1133        xmlFree(prefix);
1134    return(1);
1135}
1136
1137/**
1138 * xmlTextReaderMoveToAttributeNs:
1139 * @reader:  the xmlTextReaderPtr used
1140 * @localName:  the local name of the attribute.
1141 * @namespaceURI:  the namespace URI of the attribute.
1142 *
1143 * Moves the position of the current instance to the attribute with the
1144 * specified local name and namespace URI.
1145 *
1146 * Returns 1 in case of success, -1 in case of error, 0 if not found
1147 */
1148int
1149xmlTextReaderMoveToAttributeNs(xmlTextReaderPtr reader,
1150	const xmlChar *localName, const xmlChar *namespaceURI) {
1151    xmlAttrPtr prop;
1152    xmlNodePtr node;
1153
1154    if ((reader == NULL) || (localName == NULL) || (namespaceURI == NULL))
1155	return(-1);
1156    if (reader->node == NULL)
1157	return(-1);
1158    if (reader->node->type != XML_ELEMENT_NODE)
1159	return(0);
1160    node = reader->node;
1161
1162    /*
1163     * A priori reading http://www.w3.org/TR/REC-xml-names/ there is no
1164     * namespace name associated to "xmlns"
1165     */
1166    prop = node->properties;
1167    while (prop != NULL) {
1168	/*
1169	 * One need to have
1170	 *   - same attribute names
1171	 *   - and the attribute carrying that namespace
1172	 */
1173        if (xmlStrEqual(prop->name, localName) &&
1174	    ((prop->ns != NULL) &&
1175	     (xmlStrEqual(prop->ns->href, namespaceURI)))) {
1176	    reader->curnode = (xmlNodePtr) prop;
1177	    return(1);
1178        }
1179	prop = prop->next;
1180    }
1181    return(0);
1182}
1183
1184/**
1185 * xmlTextReaderMoveToFirstAttribute:
1186 * @reader:  the xmlTextReaderPtr used
1187 *
1188 * Moves the position of the current instance to the first attribute
1189 * associated with the current node.
1190 *
1191 * Returns 1 in case of success, -1 in case of error, 0 if not found
1192 */
1193int
1194xmlTextReaderMoveToFirstAttribute(xmlTextReaderPtr reader) {
1195    if (reader == NULL)
1196	return(-1);
1197    if (reader->node == NULL)
1198	return(-1);
1199    if (reader->node->type != XML_ELEMENT_NODE)
1200	return(0);
1201
1202    if (reader->node->nsDef != NULL) {
1203	reader->curnode = (xmlNodePtr) reader->node->nsDef;
1204	return(1);
1205    }
1206    if (reader->node->properties != NULL) {
1207	reader->curnode = (xmlNodePtr) reader->node->properties;
1208	return(1);
1209    }
1210    return(0);
1211}
1212
1213/**
1214 * xmlTextReaderMoveToNextAttribute:
1215 * @reader:  the xmlTextReaderPtr used
1216 *
1217 * Moves the position of the current instance to the next attribute
1218 * associated with the current node.
1219 *
1220 * Returns 1 in case of success, -1 in case of error, 0 if not found
1221 */
1222int
1223xmlTextReaderMoveToNextAttribute(xmlTextReaderPtr reader) {
1224    if (reader == NULL)
1225	return(-1);
1226    if (reader->node == NULL)
1227	return(-1);
1228    if (reader->node->type != XML_ELEMENT_NODE)
1229	return(0);
1230    if (reader->curnode == NULL)
1231	return(xmlTextReaderMoveToFirstAttribute(reader));
1232
1233    if (reader->curnode->type == XML_NAMESPACE_DECL) {
1234	xmlNsPtr ns = (xmlNsPtr) reader->curnode;
1235	if (ns->next != NULL) {
1236	    reader->curnode = (xmlNodePtr) ns->next;
1237	    return(1);
1238	}
1239	if (reader->node->properties != NULL) {
1240	    reader->curnode = (xmlNodePtr) reader->node->properties;
1241	    return(1);
1242	}
1243	return(0);
1244    } else if ((reader->curnode->type == XML_ATTRIBUTE_NODE) &&
1245	       (reader->curnode->next != NULL)) {
1246	reader->curnode = reader->curnode->next;
1247	return(1);
1248    }
1249    return(0);
1250}
1251
1252/**
1253 * xmlTextReaderMoveToElement:
1254 * @reader:  the xmlTextReaderPtr used
1255 *
1256 * Moves the position of the current instance to the node that
1257 * contains the current Attribute  node.
1258 *
1259 * Returns 1 in case of success, -1 in case of error, 0 if not moved
1260 */
1261int
1262xmlTextReaderMoveToElement(xmlTextReaderPtr reader) {
1263    if (reader == NULL)
1264	return(-1);
1265    if (reader->node == NULL)
1266	return(-1);
1267    if (reader->node->type != XML_ELEMENT_NODE)
1268	return(0);
1269    if (reader->curnode != NULL) {
1270	reader->curnode = NULL;
1271	return(1);
1272    }
1273    return(0);
1274}
1275
1276/**
1277 * xmlTextReaderReadAttributeValue:
1278 * @reader:  the xmlTextReaderPtr used
1279 *
1280 * Parses an attribute value into one or more Text and EntityReference nodes.
1281 *
1282 * Returns 1 in case of success, 0 if the reader was not positionned on an
1283 *         ttribute node or all the attribute values have been read, or -1
1284 *         in case of error.
1285 */
1286int
1287xmlTextReaderReadAttributeValue(xmlTextReaderPtr reader) {
1288    if (reader == NULL)
1289	return(-1);
1290    if (reader->node == NULL)
1291	return(-1);
1292    if (reader->curnode == NULL)
1293	return(0);
1294    if (reader->curnode->type == XML_ATTRIBUTE_NODE) {
1295	if (reader->curnode->children == NULL)
1296	    return(0);
1297	reader->curnode = reader->curnode->children;
1298    } else if (reader->curnode->type == XML_NAMESPACE_DECL) {
1299	xmlNsPtr ns = (xmlNsPtr) reader->curnode;
1300
1301	if (reader->faketext == NULL) {
1302	    reader->faketext = xmlNewDocText(reader->node->doc,
1303		                             ns->href);
1304	} else {
1305            if (reader->faketext->content != NULL)
1306		xmlFree(reader->faketext->content);
1307	    reader->faketext->content = xmlStrdup(ns->href);
1308	}
1309	reader->curnode = reader->faketext;
1310    } else {
1311	if (reader->curnode->next == NULL)
1312	    return(0);
1313	reader->curnode = reader->curnode->next;
1314    }
1315    return(1);
1316}
1317
1318/************************************************************************
1319 *									*
1320 *			Acces API to the current node			*
1321 *									*
1322 ************************************************************************/
1323/**
1324 * xmlTextReaderAttributeCount:
1325 * @reader:  the xmlTextReaderPtr used
1326 *
1327 * Provides the number of attributes of the current node
1328 *
1329 * Returns 0 i no attributes, -1 in case of error or the attribute count
1330 */
1331int
1332xmlTextReaderAttributeCount(xmlTextReaderPtr reader) {
1333    int ret;
1334    xmlAttrPtr attr;
1335    xmlNsPtr ns;
1336    xmlNodePtr node;
1337
1338    if (reader == NULL)
1339	return(-1);
1340    if (reader->node == NULL)
1341	return(0);
1342
1343    if (reader->curnode != NULL)
1344	node = reader->curnode;
1345    else
1346	node = reader->node;
1347
1348    if (node->type != XML_ELEMENT_NODE)
1349	return(0);
1350    if ((reader->state == XML_TEXTREADER_END) ||
1351	(reader->state == XML_TEXTREADER_BACKTRACK))
1352	return(0);
1353    ret = 0;
1354    attr = node->properties;
1355    while (attr != NULL) {
1356	ret++;
1357	attr = attr->next;
1358    }
1359    ns = node->nsDef;
1360    while (ns != NULL) {
1361	ret++;
1362	ns = ns->next;
1363    }
1364    return(ret);
1365}
1366
1367/**
1368 * xmlTextReaderNodeType:
1369 * @reader:  the xmlTextReaderPtr used
1370 *
1371 * Get the node type of the current node
1372 * Reference:
1373 * http://dotgnu.org/pnetlib-doc/System/Xml/XmlNodeType.html
1374 *
1375 * Returns the xmlNodeType of the current node or -1 in case of error
1376 */
1377int
1378xmlTextReaderNodeType(xmlTextReaderPtr reader) {
1379    xmlNodePtr node;
1380    if (reader == NULL)
1381	return(-1);
1382    if (reader->node == NULL)
1383	return(0);
1384    if (reader->curnode != NULL)
1385	node = reader->curnode;
1386    else
1387	node = reader->node;
1388    switch (node->type) {
1389        case XML_ELEMENT_NODE:
1390	    if ((reader->state == XML_TEXTREADER_END) ||
1391		(reader->state == XML_TEXTREADER_BACKTRACK))
1392		return(15);
1393	    return(1);
1394        case XML_NAMESPACE_DECL:
1395        case XML_ATTRIBUTE_NODE:
1396	    return(2);
1397        case XML_TEXT_NODE:
1398	    return(3); /* TODO: SignificantWhitespace == 14 Whitespace == 13 */
1399        case XML_CDATA_SECTION_NODE:
1400	    return(4);
1401        case XML_ENTITY_REF_NODE:
1402	    return(5);
1403        case XML_ENTITY_NODE:
1404	    return(6);
1405        case XML_PI_NODE:
1406	    return(7);
1407        case XML_COMMENT_NODE:
1408	    return(8);
1409        case XML_DOCUMENT_NODE:
1410        case XML_HTML_DOCUMENT_NODE:
1411#ifdef LIBXML_DOCB_ENABLED
1412        case XML_DOCB_DOCUMENT_NODE:
1413#endif
1414	    return(9);
1415        case XML_DOCUMENT_FRAG_NODE:
1416	    return(11);
1417        case XML_NOTATION_NODE:
1418	    return(12);
1419        case XML_DOCUMENT_TYPE_NODE:
1420        case XML_DTD_NODE:
1421	    return(10);
1422
1423        case XML_ELEMENT_DECL:
1424        case XML_ATTRIBUTE_DECL:
1425        case XML_ENTITY_DECL:
1426        case XML_XINCLUDE_START:
1427        case XML_XINCLUDE_END:
1428	    return(0);
1429    }
1430    return(-1);
1431}
1432
1433/**
1434 * xmlTextReaderIsEmptyElement:
1435 * @reader:  the xmlTextReaderPtr used
1436 *
1437 * Check if the current node is empty
1438 *
1439 * Returns 1 if empty, 0 if not and -1 in case of error
1440 */
1441int
1442xmlTextReaderIsEmptyElement(xmlTextReaderPtr reader) {
1443    if ((reader == NULL) || (reader->node == NULL))
1444	return(-1);
1445    if (reader->node->type != XML_ELEMENT_NODE)
1446	return(0);
1447    if (reader->curnode != NULL)
1448	return(0);
1449    if (reader->node->children != NULL)
1450	return(0);
1451    if (reader->node != reader->ctxt->node)
1452	return(1);
1453    if ((reader->ctxt->node != NULL) &&
1454	(reader->node == reader->ctxt->node->last) &&
1455	(reader->wasempty == 1))
1456	return(1);
1457    return(0);
1458}
1459
1460/**
1461 * xmlTextReaderLocalName:
1462 * @reader:  the xmlTextReaderPtr used
1463 *
1464 * The local name of the node.
1465 *
1466 * Returns the local name or NULL if not available
1467 */
1468xmlChar *
1469xmlTextReaderLocalName(xmlTextReaderPtr reader) {
1470    xmlNodePtr node;
1471    if ((reader == NULL) || (reader->node == NULL))
1472	return(NULL);
1473    if (reader->curnode != NULL)
1474	node = reader->curnode;
1475    else
1476	node = reader->node;
1477    if (node->type == XML_NAMESPACE_DECL) {
1478	xmlNsPtr ns = (xmlNsPtr) node;
1479	if (ns->prefix == NULL)
1480	    return(xmlStrdup(BAD_CAST "xmlns"));
1481	else
1482	    return(xmlStrdup(ns->prefix));
1483    }
1484    if ((node->type != XML_ELEMENT_NODE) &&
1485	(node->type != XML_ATTRIBUTE_NODE))
1486	return(xmlTextReaderName(reader));
1487    return(xmlStrdup(node->name));
1488}
1489
1490/**
1491 * xmlTextReaderName:
1492 * @reader:  the xmlTextReaderPtr used
1493 *
1494 * The qualified name of the node, equal to Prefix :LocalName.
1495 *
1496 * Returns the local name or NULL if not available
1497 */
1498xmlChar *
1499xmlTextReaderName(xmlTextReaderPtr reader) {
1500    xmlNodePtr node;
1501    xmlChar *ret;
1502
1503    if ((reader == NULL) || (reader->node == NULL))
1504	return(NULL);
1505    if (reader->curnode != NULL)
1506	node = reader->curnode;
1507    else
1508	node = reader->node;
1509    switch (node->type) {
1510        case XML_ELEMENT_NODE:
1511        case XML_ATTRIBUTE_NODE:
1512	    if ((node->ns == NULL) ||
1513		(node->ns->prefix == NULL))
1514		return(xmlStrdup(node->name));
1515
1516	    ret = xmlStrdup(node->ns->prefix);
1517	    ret = xmlStrcat(ret, BAD_CAST ":");
1518	    ret = xmlStrcat(ret, node->name);
1519	    return(ret);
1520        case XML_TEXT_NODE:
1521	    return(xmlStrdup(BAD_CAST "#text"));
1522        case XML_CDATA_SECTION_NODE:
1523	    return(xmlStrdup(BAD_CAST "#cdata-section"));
1524        case XML_ENTITY_NODE:
1525        case XML_ENTITY_REF_NODE:
1526	    return(xmlStrdup(node->name));
1527        case XML_PI_NODE:
1528	    return(xmlStrdup(node->name));
1529        case XML_COMMENT_NODE:
1530	    return(xmlStrdup(BAD_CAST "#comment"));
1531        case XML_DOCUMENT_NODE:
1532        case XML_HTML_DOCUMENT_NODE:
1533#ifdef LIBXML_DOCB_ENABLED
1534        case XML_DOCB_DOCUMENT_NODE:
1535#endif
1536	    return(xmlStrdup(BAD_CAST "#document"));
1537        case XML_DOCUMENT_FRAG_NODE:
1538	    return(xmlStrdup(BAD_CAST "#document-fragment"));
1539        case XML_NOTATION_NODE:
1540	    return(xmlStrdup(node->name));
1541        case XML_DOCUMENT_TYPE_NODE:
1542        case XML_DTD_NODE:
1543	    return(xmlStrdup(node->name));
1544        case XML_NAMESPACE_DECL: {
1545	    xmlNsPtr ns = (xmlNsPtr) node;
1546
1547	    ret = xmlStrdup(BAD_CAST "xmlns");
1548	    if (ns->prefix == NULL)
1549		return(ret);
1550	    ret = xmlStrcat(ret, BAD_CAST ":");
1551	    ret = xmlStrcat(ret, ns->prefix);
1552	    return(ret);
1553	}
1554
1555        case XML_ELEMENT_DECL:
1556        case XML_ATTRIBUTE_DECL:
1557        case XML_ENTITY_DECL:
1558        case XML_XINCLUDE_START:
1559        case XML_XINCLUDE_END:
1560	    return(NULL);
1561    }
1562    return(NULL);
1563}
1564
1565/**
1566 * xmlTextReaderPrefix:
1567 * @reader:  the xmlTextReaderPtr used
1568 *
1569 * A shorthand reference to the namespace associated with the node.
1570 *
1571 * Returns the prefix or NULL if not available
1572 */
1573xmlChar *
1574xmlTextReaderPrefix(xmlTextReaderPtr reader) {
1575    xmlNodePtr node;
1576    if ((reader == NULL) || (reader->node == NULL))
1577	return(NULL);
1578    if (reader->curnode != NULL)
1579	node = reader->curnode;
1580    else
1581	node = reader->node;
1582    if (node->type == XML_NAMESPACE_DECL) {
1583	xmlNsPtr ns = (xmlNsPtr) node;
1584	if (ns->prefix == NULL)
1585	    return(NULL);
1586	return(xmlStrdup(BAD_CAST "xmlns"));
1587    }
1588    if ((node->type != XML_ELEMENT_NODE) &&
1589	(node->type != XML_ATTRIBUTE_NODE))
1590	return(NULL);
1591    if ((node->ns != NULL) || (node->ns->prefix != NULL))
1592	return(xmlStrdup(node->ns->prefix));
1593    return(NULL);
1594}
1595
1596/**
1597 * xmlTextReaderNamespaceUri:
1598 * @reader:  the xmlTextReaderPtr used
1599 *
1600 * The URI defining the namespace associated with the node.
1601 *
1602 * Returns the namespace URI or NULL if not available
1603 */
1604xmlChar *
1605xmlTextReaderNamespaceUri(xmlTextReaderPtr reader) {
1606    xmlNodePtr node;
1607    if ((reader == NULL) || (reader->node == NULL))
1608	return(NULL);
1609    if (reader->curnode != NULL)
1610	node = reader->curnode;
1611    else
1612	node = reader->node;
1613    if (node->type == XML_NAMESPACE_DECL)
1614	return(xmlStrdup(BAD_CAST "http://www.w3.org/2000/xmlns/"));
1615    if ((node->type != XML_ELEMENT_NODE) &&
1616	(node->type != XML_ATTRIBUTE_NODE))
1617	return(NULL);
1618    if (node->ns != NULL)
1619	return(xmlStrdup(node->ns->href));
1620    return(NULL);
1621}
1622
1623/**
1624 * xmlTextReaderBaseUri:
1625 * @reader:  the xmlTextReaderPtr used
1626 *
1627 * The base URI of the node.
1628 *
1629 * Returns the base URI or NULL if not available
1630 */
1631xmlChar *
1632xmlTextReaderBaseUri(xmlTextReaderPtr reader) {
1633    if ((reader == NULL) || (reader->node == NULL))
1634	return(NULL);
1635    return(xmlNodeGetBase(NULL, reader->node));
1636}
1637
1638/**
1639 * xmlTextReaderDepth:
1640 * @reader:  the xmlTextReaderPtr used
1641 *
1642 * The depth of the node in the tree.
1643 *
1644 * Returns the depth or -1 in case of error
1645 */
1646int
1647xmlTextReaderDepth(xmlTextReaderPtr reader) {
1648    if (reader == NULL)
1649	return(-1);
1650    if (reader->node == NULL)
1651	return(0);
1652
1653    if (reader->curnode != NULL) {
1654	if ((reader->curnode->type == XML_ATTRIBUTE_NODE) ||
1655	    (reader->curnode->type == XML_NAMESPACE_DECL))
1656	    return(reader->depth + 1);
1657	return(reader->depth + 2);
1658    }
1659    return(reader->depth);
1660}
1661
1662/**
1663 * xmlTextReaderHasAttributes:
1664 * @reader:  the xmlTextReaderPtr used
1665 *
1666 * Whether the node has attributes.
1667 *
1668 * Returns 1 if true, 0 if false, and -1 in case or error
1669 */
1670int
1671xmlTextReaderHasAttributes(xmlTextReaderPtr reader) {
1672    xmlNodePtr node;
1673    if (reader == NULL)
1674	return(-1);
1675    if (reader->node == NULL)
1676	return(0);
1677    if (reader->curnode != NULL)
1678	node = reader->curnode;
1679    else
1680	node = reader->node;
1681
1682    if ((node->type == XML_ELEMENT_NODE) &&
1683	(node->properties != NULL))
1684	return(1);
1685    /* TODO: handle the xmlDecl */
1686    return(0);
1687}
1688
1689/**
1690 * xmlTextReaderHasValue:
1691 * @reader:  the xmlTextReaderPtr used
1692 *
1693 * Whether the node can have a text value.
1694 *
1695 * Returns 1 if true, 0 if false, and -1 in case or error
1696 */
1697int
1698xmlTextReaderHasValue(xmlTextReaderPtr reader) {
1699    xmlNodePtr node;
1700    if (reader == NULL)
1701	return(-1);
1702    if (reader->node == NULL)
1703	return(0);
1704    if (reader->curnode != NULL)
1705	node = reader->curnode;
1706    else
1707	node = reader->node;
1708
1709    switch (node->type) {
1710        case XML_ATTRIBUTE_NODE:
1711        case XML_TEXT_NODE:
1712        case XML_CDATA_SECTION_NODE:
1713        case XML_PI_NODE:
1714        case XML_COMMENT_NODE:
1715	    return(1);
1716	default:
1717	    return(0);
1718    }
1719    return(0);
1720}
1721
1722/**
1723 * xmlTextReaderValue:
1724 * @reader:  the xmlTextReaderPtr used
1725 *
1726 * Provides the text value of the node if present
1727 *
1728 * Returns the string or NULL if not available. The retsult must be deallocated
1729 *     with xmlFree()
1730 */
1731xmlChar *
1732xmlTextReaderValue(xmlTextReaderPtr reader) {
1733    xmlNodePtr node;
1734    if (reader == NULL)
1735	return(NULL);
1736    if (reader->node == NULL)
1737	return(NULL);
1738    if (reader->curnode != NULL)
1739	node = reader->curnode;
1740    else
1741	node = reader->node;
1742
1743    switch (node->type) {
1744        case XML_NAMESPACE_DECL:
1745	    return(xmlStrdup(((xmlNsPtr) node)->href));
1746        case XML_ATTRIBUTE_NODE:{
1747	    xmlAttrPtr attr = (xmlAttrPtr) node;
1748
1749	    if (attr->parent != NULL)
1750		return (xmlNodeListGetString
1751			(attr->parent->doc, attr->children, 1));
1752	    else
1753		return (xmlNodeListGetString(NULL, attr->children, 1));
1754	    break;
1755	}
1756        case XML_TEXT_NODE:
1757        case XML_CDATA_SECTION_NODE:
1758        case XML_PI_NODE:
1759        case XML_COMMENT_NODE:
1760            if (node->content != NULL)
1761                return (xmlStrdup(node->content));
1762	default:
1763	    return(NULL);
1764    }
1765    return(NULL);
1766}
1767
1768/**
1769 * xmlTextReaderIsDefault:
1770 * @reader:  the xmlTextReaderPtr used
1771 *
1772 * Whether an Attribute  node was generated from the default value
1773 * defined in the DTD or schema.
1774 *
1775 * Returns 0 if not defaulted, 1 if defaulted, and -1 in case of error
1776 */
1777int
1778xmlTextReaderIsDefault(xmlTextReaderPtr reader) {
1779    if (reader == NULL)
1780	return(-1);
1781    return(0);
1782}
1783
1784/**
1785 * xmlTextReaderQuoteChar:
1786 * @reader:  the xmlTextReaderPtr used
1787 *
1788 * The quotation mark character used to enclose the value of an attribute.
1789 *
1790 * Returns " or ' and -1 in case of error
1791 */
1792int
1793xmlTextReaderQuoteChar(xmlTextReaderPtr reader) {
1794    if (reader == NULL)
1795	return(-1);
1796    /* TODO maybe lookup the attribute value for " first */
1797    return((int) '"');
1798}
1799
1800/**
1801 * xmlTextReaderXmlLang:
1802 * @reader:  the xmlTextReaderPtr used
1803 *
1804 * The xml:lang scope within which the node resides.
1805 *
1806 * Returns the xml:lang value or NULL if none exists.
1807 */
1808xmlChar *
1809xmlTextReaderXmlLang(xmlTextReaderPtr reader) {
1810    if (reader == NULL)
1811	return(NULL);
1812    if (reader->node == NULL)
1813	return(NULL);
1814    return(xmlNodeGetLang(reader->node));
1815}
1816
1817/**
1818 * xmlTextReaderNormalization:
1819 * @reader:  the xmlTextReaderPtr used
1820 *
1821 * The value indicating whether to normalize white space and attribute values.
1822 * Since attribute value and end of line normalizations are a MUST in the XML
1823 * specification only the value true is accepted. The broken bahaviour of
1824 * accepting out of range character entities like &#0; is of course not
1825 * supported either.
1826 *
1827 * Returns 1 or -1 in case of error.
1828 */
1829int
1830xmlTextReaderNormalization(xmlTextReaderPtr reader) {
1831    if (reader == NULL)
1832	return(-1);
1833    return(1);
1834}
1835
1836/************************************************************************
1837 *									*
1838 *			Extensions to the base APIs			*
1839 *									*
1840 ************************************************************************/
1841
1842/**
1843 * xmlTextReaderSetParserProp:
1844 * @reader:  the xmlTextReaderPtr used
1845 * @prop:  the xmlParserProperties to set
1846 * @value:  usually 0 or 1 to (de)activate it
1847 *
1848 * Change the parser processing behaviour by changing some of its internal
1849 * properties. Note that some properties can only be changed before any
1850 * read has been done.
1851 *
1852 * Returns 0 if the call was successful, or -1 in case of error
1853 */
1854int
1855xmlTextReaderSetParserProp(xmlTextReaderPtr reader, int prop, int value) {
1856    xmlParserProperties p = (xmlParserProperties) prop;
1857    xmlParserCtxtPtr ctxt;
1858
1859    if ((reader == NULL) || (reader->ctxt == NULL))
1860	return(-1);
1861    ctxt = reader->ctxt;
1862
1863    switch (p) {
1864        case XML_PARSER_LOADDTD:
1865	    if (value != 0) {
1866		if (ctxt->loadsubset == 0) {
1867		    if (reader->mode != XML_TEXTREADER_MODE_INITIAL)
1868			return(-1);
1869		    ctxt->loadsubset = XML_DETECT_IDS;
1870		}
1871	    } else {
1872		ctxt->loadsubset = 0;
1873	    }
1874	    return(0);
1875        case XML_PARSER_DEFAULTATTRS:
1876	    if (value != 0) {
1877		ctxt->loadsubset |= XML_COMPLETE_ATTRS;
1878	    } else {
1879		if (ctxt->loadsubset & XML_COMPLETE_ATTRS)
1880		    ctxt->loadsubset -= XML_COMPLETE_ATTRS;
1881	    }
1882	    return(0);
1883        case XML_PARSER_VALIDATE:
1884	    if (value != 0) {
1885		ctxt->validate = 1;
1886	    } else {
1887		ctxt->validate = 0;
1888	    }
1889	    return(0);
1890        case XML_PARSER_SUBST_ENTITIES:
1891	    if (value != 0) {
1892		ctxt->replaceEntities = 1;
1893	    } else {
1894		ctxt->replaceEntities = 0;
1895	    }
1896	    return(0);
1897    }
1898    return(-1);
1899}
1900
1901/**
1902 * xmlTextReaderGetParserProp:
1903 * @reader:  the xmlTextReaderPtr used
1904 * @prop:  the xmlParserProperties to get
1905 *
1906 * Read the parser internal property.
1907 *
1908 * Returns the value, usually 0 or 1, or -1 in case of error.
1909 */
1910int
1911xmlTextReaderGetParserProp(xmlTextReaderPtr reader, int prop) {
1912    xmlParserProperties p = (xmlParserProperties) prop;
1913    xmlParserCtxtPtr ctxt;
1914
1915    if ((reader == NULL) || (reader->ctxt == NULL))
1916	return(-1);
1917    ctxt = reader->ctxt;
1918
1919    switch (p) {
1920        case XML_PARSER_LOADDTD:
1921	    if ((ctxt->loadsubset != 0) || (ctxt->validate != 0))
1922		return(1);
1923	    return(0);
1924        case XML_PARSER_DEFAULTATTRS:
1925	    if (ctxt->loadsubset & XML_COMPLETE_ATTRS)
1926		return(1);
1927	    return(0);
1928        case XML_PARSER_VALIDATE:
1929	    return(ctxt->validate);
1930	case XML_PARSER_SUBST_ENTITIES:
1931	    return(ctxt->replaceEntities);
1932    }
1933    return(-1);
1934}
1935
1936/**
1937 * xmlTextReaderCurrentNode:
1938 * @reader:  the xmlTextReaderPtr used
1939 *
1940 * Hacking interface allowing to get the xmlNodePtr correponding to the
1941 * current node being accessed by the xmlTextReader. This is dangerous
1942 * because the underlying node may be destroyed on the next Reads.
1943 *
1944 * Returns the xmlNodePtr or NULL in case of error.
1945 */
1946xmlNodePtr
1947xmlTextReaderCurrentNode(xmlTextReaderPtr reader) {
1948    if (reader == NULL)
1949	return(NULL);
1950
1951    if (reader->curnode != NULL)
1952	return(reader->curnode);
1953    return(reader->node);
1954}
1955
1956/**
1957 * xmlTextReaderCurrentDoc:
1958 * @reader:  the xmlTextReaderPtr used
1959 *
1960 * Hacking interface allowing to get the xmlDocPtr correponding to the
1961 * current document being accessed by the xmlTextReader. This is dangerous
1962 * because the associated node may be destroyed on the next Reads.
1963 *
1964 * Returns the xmlDocPtr or NULL in case of error.
1965 */
1966xmlDocPtr
1967xmlTextReaderCurrentDoc(xmlTextReaderPtr reader) {
1968    if ((reader == NULL) || (reader->ctxt == NULL))
1969	return(NULL);
1970
1971    return(reader->ctxt->myDoc);
1972}
1973
1974/************************************************************************
1975 *									*
1976 *			Utilities					*
1977 *									*
1978 ************************************************************************/
1979/**
1980 * xmlBase64Decode:
1981 * @in:  the input buffer
1982 * @inlen:  the size of the input (in), the size read from it (out)
1983 * @to:  the output buffer
1984 * @tolen:  the size of the output (in), the size written to (out)
1985 *
1986 * Base64 decoder, reads from @in and save in @to
1987 *
1988 * Returns 0 if all the input was consumer, 1 if the Base64 end was reached,
1989 *         2 if there wasn't enough space on the output or -1 in case of error.
1990 */
1991static int
1992xmlBase64Decode(const unsigned char *in, unsigned long *inlen,
1993	        unsigned char *to, unsigned long *tolen) {
1994    unsigned long incur;		/* current index in in[] */
1995    unsigned long inblk;		/* last block index in in[] */
1996    unsigned long outcur;		/* current index in out[] */
1997    unsigned long inmax;		/* size of in[] */
1998    unsigned long outmax;		/* size of out[] */
1999    unsigned char cur;			/* the current value read from in[] */
2000    unsigned char intmp[3], outtmp[4];	/* temporary buffers for the convert */
2001    int nbintmp;			/* number of byte in intmp[] */
2002    int is_ignore;			/* cur should be ignored */
2003    int is_end = 0;			/* the end of the base64 was found */
2004    int retval = 1;
2005    int i;
2006
2007    if ((in == NULL) || (inlen == NULL) || (to == NULL) || (tolen == NULL))
2008	return(-1);
2009
2010    incur = 0;
2011    inblk = 0;
2012    outcur = 0;
2013    inmax = *inlen;
2014    outmax = *tolen;
2015    nbintmp = 0;
2016
2017    while (1) {
2018        if (incur >= inmax)
2019            break;
2020        cur = in[incur++];
2021        is_ignore = 0;
2022        if ((cur >= 'A') && (cur <= 'Z'))
2023            cur = cur - 'A';
2024        else if ((cur >= 'a') && (cur <= 'z'))
2025            cur = cur - 'a' + 26;
2026        else if ((cur >= '0') && (cur <= '9'))
2027            cur = cur - '0' + 52;
2028        else if (cur == '+')
2029            cur = 62;
2030        else if (cur == '/')
2031            cur = 63;
2032        else if (cur == '.')
2033            cur = 0;
2034        else if (cur == '=') /*no op , end of the base64 stream */
2035            is_end = 1;
2036        else {
2037            is_ignore = 1;
2038	    if (nbintmp == 0)
2039		inblk = incur;
2040	}
2041
2042        if (!is_ignore) {
2043            int nbouttmp = 3;
2044            int is_break = 0;
2045
2046            if (is_end) {
2047                if (nbintmp == 0)
2048                    break;
2049                if ((nbintmp == 1) || (nbintmp == 2))
2050                    nbouttmp = 1;
2051                else
2052                    nbouttmp = 2;
2053                nbintmp = 3;
2054                is_break = 1;
2055            }
2056            intmp[nbintmp++] = cur;
2057	    /*
2058	     * if intmp is full, push the 4byte sequence as a 3 byte
2059	     * sequence out
2060	     */
2061            if (nbintmp == 4) {
2062                nbintmp = 0;
2063                outtmp[0] = (intmp[0] << 2) | ((intmp[1] & 0x30) >> 4);
2064                outtmp[1] =
2065                    ((intmp[1] & 0x0F) << 4) | ((intmp[2] & 0x3C) >> 2);
2066                outtmp[2] = ((intmp[2] & 0x03) << 6) | (intmp[3] & 0x3F);
2067		if (outcur + 3 >= outmax) {
2068		    retval = 2;
2069		    break;
2070		}
2071
2072                for (i = 0; i < nbouttmp; i++)
2073		    to[outcur++] = outtmp[i];
2074		inblk = incur;
2075            }
2076
2077            if (is_break) {
2078		retval = 0;
2079                break;
2080	    }
2081        }
2082    }
2083
2084    *tolen = outcur;
2085    *inlen = inblk;
2086    return (retval);
2087}
2088
2089/*
2090 * Test routine for the xmlBase64Decode function
2091 */
2092#if 0
2093int main(int argc, char **argv) {
2094    char *input = "  VW4 gcGV0        \n      aXQgdGVzdCAuCg== ";
2095    char output[100];
2096    char output2[100];
2097    char output3[100];
2098    unsigned long inlen = strlen(input);
2099    unsigned long outlen = 100;
2100    int ret;
2101    unsigned long cons, tmp, tmp2, prod;
2102
2103    /*
2104     * Direct
2105     */
2106    ret = xmlBase64Decode(input, &inlen, output, &outlen);
2107
2108    output[outlen] = 0;
2109    printf("ret: %d, inlen: %ld , outlen: %ld, output: '%s'\n", ret, inlen, outlen, output);
2110
2111    /*
2112     * output chunking
2113     */
2114    cons = 0;
2115    prod = 0;
2116    while (cons < inlen) {
2117	tmp = 5;
2118	tmp2 = inlen - cons;
2119
2120	printf("%ld %ld\n", cons, prod);
2121	ret = xmlBase64Decode(&input[cons], &tmp2, &output2[prod], &tmp);
2122	cons += tmp2;
2123	prod += tmp;
2124	printf("%ld %ld\n", cons, prod);
2125    }
2126    output2[outlen] = 0;
2127    printf("ret: %d, cons: %ld , prod: %ld, output: '%s'\n", ret, cons, prod, output2);
2128
2129    /*
2130     * input chunking
2131     */
2132    cons = 0;
2133    prod = 0;
2134    while (cons < inlen) {
2135	tmp = 100 - prod;
2136	tmp2 = inlen - cons;
2137	if (tmp2 > 5)
2138	    tmp2 = 5;
2139
2140	printf("%ld %ld\n", cons, prod);
2141	ret = xmlBase64Decode(&input[cons], &tmp2, &output3[prod], &tmp);
2142	cons += tmp2;
2143	prod += tmp;
2144	printf("%ld %ld\n", cons, prod);
2145    }
2146    output3[outlen] = 0;
2147    printf("ret: %d, cons: %ld , prod: %ld, output: '%s'\n", ret, cons, prod, output3);
2148    return(0);
2149
2150}
2151#endif
2152