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