/* * xmlreader.c: implements the xmlTextReader streaming node API * * NOTE: * XmlTextReader.Normalization Property won't be supported, since * it makes the parser non compliant to the XML recommendation * * See Copyright for the status of this software. * * daniel@veillard.com */ /* * TODOs: * - XML Schemas validation */ #define IN_LIBXML #include "libxml.h" #ifdef LIBXML_READER_ENABLED #include /* for memset() only ! */ #include #ifdef HAVE_CTYPE_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #include #include #include #include #ifdef LIBXML_SCHEMAS_ENABLED #include #include #endif #include #ifdef LIBXML_XINCLUDE_ENABLED #include #endif #ifdef LIBXML_PATTERN_ENABLED #include #endif #include "buf.h" #define MAX_ERR_MSG_SIZE 64000 /* * The following VA_COPY was coded following an example in * the Samba project. It may not be sufficient for some * esoteric implementations of va_list but (hopefully) will * be sufficient for libxml2. */ #ifndef VA_COPY #ifdef HAVE_VA_COPY #define VA_COPY(dest, src) va_copy(dest, src) #else #ifdef HAVE___VA_COPY #define VA_COPY(dest,src) __va_copy(dest, src) #else #ifndef VA_LIST_IS_ARRAY #define VA_COPY(dest,src) (dest) = (src) #else #include #define VA_COPY(dest,src) memcpy((char *)(dest),(char *)(src),sizeof(va_list)) #endif #endif #endif #endif /* #define DEBUG_CALLBACKS */ /* #define DEBUG_READER */ /** * TODO: * * macro to flag unimplemented blocks */ #define TODO \ xmlGenericError(xmlGenericErrorContext, \ "Unimplemented block at %s:%d\n", \ __FILE__, __LINE__); #ifdef DEBUG_READER #define DUMP_READER xmlTextReaderDebug(reader); #else #define DUMP_READER #endif #define CHUNK_SIZE 512 /************************************************************************ * * * The parser: maps the Text Reader API on top of the existing * * parsing routines building a tree * * * ************************************************************************/ #define XML_TEXTREADER_INPUT 1 #define XML_TEXTREADER_CTXT 2 typedef enum { XML_TEXTREADER_NONE = -1, XML_TEXTREADER_START= 0, XML_TEXTREADER_ELEMENT= 1, XML_TEXTREADER_END= 2, XML_TEXTREADER_EMPTY= 3, XML_TEXTREADER_BACKTRACK= 4, XML_TEXTREADER_DONE= 5, XML_TEXTREADER_ERROR= 6 } xmlTextReaderState; typedef enum { XML_TEXTREADER_NOT_VALIDATE = 0, XML_TEXTREADER_VALIDATE_DTD = 1, XML_TEXTREADER_VALIDATE_RNG = 2, XML_TEXTREADER_VALIDATE_XSD = 4 } xmlTextReaderValidate; struct _xmlTextReader { int mode; /* the parsing mode */ xmlDocPtr doc; /* when walking an existing doc */ xmlTextReaderValidate validate;/* is there any validation */ int allocs; /* what structure were deallocated */ xmlTextReaderState state; xmlParserCtxtPtr ctxt; /* the parser context */ xmlSAXHandlerPtr sax; /* the parser SAX callbacks */ xmlParserInputBufferPtr input; /* the input */ startElementSAXFunc startElement;/* initial SAX callbacks */ endElementSAXFunc endElement; /* idem */ startElementNsSAX2Func startElementNs;/* idem */ endElementNsSAX2Func endElementNs; /* idem */ charactersSAXFunc characters; cdataBlockSAXFunc cdataBlock; unsigned int base; /* base of the segment in the input */ unsigned int cur; /* current position in the input */ xmlNodePtr node; /* current node */ xmlNodePtr curnode;/* current attribute node */ int depth; /* depth of the current node */ xmlNodePtr faketext;/* fake xmlNs chld */ int preserve;/* preserve the resulting document */ xmlBufPtr buffer; /* used to return const xmlChar * */ xmlDictPtr dict; /* the context dictionary */ /* entity stack when traversing entities content */ xmlNodePtr ent; /* Current Entity Ref Node */ int entNr; /* Depth of the entities stack */ int entMax; /* Max depth of the entities stack */ xmlNodePtr *entTab; /* array of entities */ /* error handling */ xmlTextReaderErrorFunc errorFunc; /* callback function */ void *errorFuncArg; /* callback function user argument */ #ifdef LIBXML_SCHEMAS_ENABLED /* Handling of RelaxNG validation */ xmlRelaxNGPtr rngSchemas; /* The Relax NG schemas */ xmlRelaxNGValidCtxtPtr rngValidCtxt;/* The Relax NG validation context */ int rngPreserveCtxt; /* 1 if the context was provided by the user */ int rngValidErrors;/* The number of errors detected */ xmlNodePtr rngFullNode; /* the node if RNG not progressive */ /* Handling of Schemas validation */ xmlSchemaPtr xsdSchemas; /* The Schemas schemas */ xmlSchemaValidCtxtPtr xsdValidCtxt;/* The Schemas validation context */ int xsdPreserveCtxt; /* 1 if the context was provided by the user */ int xsdValidErrors;/* The number of errors detected */ xmlSchemaSAXPlugPtr xsdPlug; /* the schemas plug in SAX pipeline */ #endif #ifdef LIBXML_XINCLUDE_ENABLED /* Handling of XInclude processing */ int xinclude; /* is xinclude asked for */ const xmlChar * xinclude_name; /* the xinclude name from dict */ xmlXIncludeCtxtPtr xincctxt; /* the xinclude context */ int in_xinclude; /* counts for xinclude */ #endif #ifdef LIBXML_PATTERN_ENABLED int patternNr; /* number of preserve patterns */ int patternMax; /* max preserve patterns */ xmlPatternPtr *patternTab; /* array of preserve patterns */ #endif int preserves; /* level of preserves */ int parserFlags; /* the set of options set */ /* Structured error handling */ xmlStructuredErrorFunc sErrorFunc; /* callback function */ }; #define NODE_IS_EMPTY 0x1 #define NODE_IS_PRESERVED 0x2 #define NODE_IS_SPRESERVED 0x4 /** * CONSTSTR: * * Macro used to return an interned string */ #define CONSTSTR(str) xmlDictLookup(reader->dict, (str), -1) #define CONSTQSTR(p, str) xmlDictQLookup(reader->dict, (p), (str)) static int xmlTextReaderReadTree(xmlTextReaderPtr reader); static int xmlTextReaderNextTree(xmlTextReaderPtr reader); /************************************************************************ * * * Our own version of the freeing routines as we recycle nodes * * * ************************************************************************/ /** * DICT_FREE: * @str: a string * * Free a string if it is not owned by the "dict" dictionary in the * current scope */ #define DICT_FREE(str) \ if ((str) && ((!dict) || \ (xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \ xmlFree((char *)(str)); static void xmlTextReaderFreeNode(xmlTextReaderPtr reader, xmlNodePtr cur); static void xmlTextReaderFreeNodeList(xmlTextReaderPtr reader, xmlNodePtr cur); /** * xmlFreeID: * @not: A id * * Deallocate the memory used by an id definition */ static void xmlFreeID(xmlIDPtr id) { xmlDictPtr dict = NULL; if (id == NULL) return; if (id->doc != NULL) dict = id->doc->dict; if (id->value != NULL) DICT_FREE(id->value) xmlFree(id); } /** * xmlTextReaderRemoveID: * @doc: the document * @attr: the attribute * * Remove the given attribute from the ID table maintained internally. * * Returns -1 if the lookup failed and 0 otherwise */ static int xmlTextReaderRemoveID(xmlDocPtr doc, xmlAttrPtr attr) { xmlIDTablePtr table; xmlIDPtr id; xmlChar *ID; if (doc == NULL) return(-1); if (attr == NULL) return(-1); table = (xmlIDTablePtr) doc->ids; if (table == NULL) return(-1); ID = xmlNodeListGetString(doc, attr->children, 1); if (ID == NULL) return(-1); id = xmlHashLookup(table, ID); xmlFree(ID); if (id == NULL || id->attr != attr) { return(-1); } id->name = attr->name; id->attr = NULL; return(0); } /** * xmlTextReaderFreeProp: * @reader: the xmlTextReaderPtr used * @cur: the node * * Free a node. */ static void xmlTextReaderFreeProp(xmlTextReaderPtr reader, xmlAttrPtr cur) { xmlDictPtr dict; if ((reader != NULL) && (reader->ctxt != NULL)) dict = reader->ctxt->dict; else dict = NULL; if (cur == NULL) return; if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue)) xmlDeregisterNodeDefaultValue((xmlNodePtr) cur); /* Check for ID removal -> leading to invalid references ! */ if ((cur->parent != NULL) && (cur->parent->doc != NULL) && ((cur->parent->doc->intSubset != NULL) || (cur->parent->doc->extSubset != NULL))) { if (xmlIsID(cur->parent->doc, cur->parent, cur)) xmlTextReaderRemoveID(cur->parent->doc, cur); } if (cur->children != NULL) xmlTextReaderFreeNodeList(reader, cur->children); DICT_FREE(cur->name); if ((reader != NULL) && (reader->ctxt != NULL) && (reader->ctxt->freeAttrsNr < 100)) { cur->next = reader->ctxt->freeAttrs; reader->ctxt->freeAttrs = cur; reader->ctxt->freeAttrsNr++; } else { xmlFree(cur); } } /** * xmlTextReaderFreePropList: * @reader: the xmlTextReaderPtr used * @cur: the first property in the list * * Free a property and all its siblings, all the children are freed too. */ static void xmlTextReaderFreePropList(xmlTextReaderPtr reader, xmlAttrPtr cur) { xmlAttrPtr next; while (cur != NULL) { next = cur->next; xmlTextReaderFreeProp(reader, cur); cur = next; } } /** * xmlTextReaderFreeNodeList: * @reader: the xmlTextReaderPtr used * @cur: the first node in the list * * Free a node and all its siblings, this is a recursive behaviour, all * the children are freed too. */ static void xmlTextReaderFreeNodeList(xmlTextReaderPtr reader, xmlNodePtr cur) { xmlNodePtr next; xmlDictPtr dict; if ((reader != NULL) && (reader->ctxt != NULL)) dict = reader->ctxt->dict; else dict = NULL; if (cur == NULL) return; if (cur->type == XML_NAMESPACE_DECL) { xmlFreeNsList((xmlNsPtr) cur); return; } if ((cur->type == XML_DOCUMENT_NODE) || (cur->type == XML_HTML_DOCUMENT_NODE)) { xmlFreeDoc((xmlDocPtr) cur); return; } while (cur != NULL) { next = cur->next; /* unroll to speed up freeing the document */ if (cur->type != XML_DTD_NODE) { if ((cur->children != NULL) && (cur->type != XML_ENTITY_REF_NODE)) { if (cur->children->parent == cur) xmlTextReaderFreeNodeList(reader, cur->children); cur->children = NULL; } if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue)) xmlDeregisterNodeDefaultValue(cur); if (((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_XINCLUDE_START) || (cur->type == XML_XINCLUDE_END)) && (cur->properties != NULL)) xmlTextReaderFreePropList(reader, cur->properties); if ((cur->content != (xmlChar *) &(cur->properties)) && (cur->type != XML_ELEMENT_NODE) && (cur->type != XML_XINCLUDE_START) && (cur->type != XML_XINCLUDE_END) && (cur->type != XML_ENTITY_REF_NODE)) { DICT_FREE(cur->content); } if (((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_XINCLUDE_START) || (cur->type == XML_XINCLUDE_END)) && (cur->nsDef != NULL)) xmlFreeNsList(cur->nsDef); /* * we don't free element names here they are interned now */ if ((cur->type != XML_TEXT_NODE) && (cur->type != XML_COMMENT_NODE)) DICT_FREE(cur->name); if (((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_TEXT_NODE)) && (reader != NULL) && (reader->ctxt != NULL) && (reader->ctxt->freeElemsNr < 100)) { cur->next = reader->ctxt->freeElems; reader->ctxt->freeElems = cur; reader->ctxt->freeElemsNr++; } else { xmlFree(cur); } } cur = next; } } /** * xmlTextReaderFreeNode: * @reader: the xmlTextReaderPtr used * @cur: the node * * Free a node, this is a recursive behaviour, all the children are freed too. * This doesn't unlink the child from the list, use xmlUnlinkNode() first. */ static void xmlTextReaderFreeNode(xmlTextReaderPtr reader, xmlNodePtr cur) { xmlDictPtr dict; if ((reader != NULL) && (reader->ctxt != NULL)) dict = reader->ctxt->dict; else dict = NULL; if (cur->type == XML_DTD_NODE) { xmlFreeDtd((xmlDtdPtr) cur); return; } if (cur->type == XML_NAMESPACE_DECL) { xmlFreeNs((xmlNsPtr) cur); return; } if (cur->type == XML_ATTRIBUTE_NODE) { xmlTextReaderFreeProp(reader, (xmlAttrPtr) cur); return; } if ((cur->children != NULL) && (cur->type != XML_ENTITY_REF_NODE)) { if (cur->children->parent == cur) xmlTextReaderFreeNodeList(reader, cur->children); cur->children = NULL; } if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue)) xmlDeregisterNodeDefaultValue(cur); if (((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_XINCLUDE_START) || (cur->type == XML_XINCLUDE_END)) && (cur->properties != NULL)) xmlTextReaderFreePropList(reader, cur->properties); if ((cur->content != (xmlChar *) &(cur->properties)) && (cur->type != XML_ELEMENT_NODE) && (cur->type != XML_XINCLUDE_START) && (cur->type != XML_XINCLUDE_END) && (cur->type != XML_ENTITY_REF_NODE)) { DICT_FREE(cur->content); } if (((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_XINCLUDE_START) || (cur->type == XML_XINCLUDE_END)) && (cur->nsDef != NULL)) xmlFreeNsList(cur->nsDef); /* * we don't free names here they are interned now */ if ((cur->type != XML_TEXT_NODE) && (cur->type != XML_COMMENT_NODE)) DICT_FREE(cur->name); if (((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_TEXT_NODE)) && (reader != NULL) && (reader->ctxt != NULL) && (reader->ctxt->freeElemsNr < 100)) { cur->next = reader->ctxt->freeElems; reader->ctxt->freeElems = cur; reader->ctxt->freeElemsNr++; } else { xmlFree(cur); } } static void xmlTextReaderFreeIDTableEntry(void *id, const xmlChar *name ATTRIBUTE_UNUSED) { xmlFreeID((xmlIDPtr) id); } /** * xmlTextReaderFreeIDTable: * @table: An id table * * Deallocate the memory used by an ID hash table. */ static void xmlTextReaderFreeIDTable(xmlIDTablePtr table) { xmlHashFree(table, xmlTextReaderFreeIDTableEntry); } /** * xmlTextReaderFreeDoc: * @reader: the xmlTextReaderPtr used * @cur: pointer to the document * * Free up all the structures used by a document, tree included. */ static void xmlTextReaderFreeDoc(xmlTextReaderPtr reader, xmlDocPtr cur) { xmlDtdPtr extSubset, intSubset; if (cur == NULL) return; if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue)) xmlDeregisterNodeDefaultValue((xmlNodePtr) cur); /* * Do this before freeing the children list to avoid ID lookups */ if (cur->ids != NULL) xmlTextReaderFreeIDTable((xmlIDTablePtr) cur->ids); cur->ids = NULL; if (cur->refs != NULL) xmlFreeRefTable((xmlRefTablePtr) cur->refs); cur->refs = NULL; extSubset = cur->extSubset; intSubset = cur->intSubset; if (intSubset == extSubset) extSubset = NULL; if (extSubset != NULL) { xmlUnlinkNode((xmlNodePtr) cur->extSubset); cur->extSubset = NULL; xmlFreeDtd(extSubset); } if (intSubset != NULL) { xmlUnlinkNode((xmlNodePtr) cur->intSubset); cur->intSubset = NULL; xmlFreeDtd(intSubset); } if (cur->children != NULL) xmlTextReaderFreeNodeList(reader, cur->children); if (cur->version != NULL) xmlFree((char *) cur->version); if (cur->name != NULL) xmlFree((char *) cur->name); if (cur->encoding != NULL) xmlFree((char *) cur->encoding); if (cur->oldNs != NULL) xmlFreeNsList(cur->oldNs); if (cur->URL != NULL) xmlFree((char *) cur->URL); if (cur->dict != NULL) xmlDictFree(cur->dict); xmlFree(cur); } /************************************************************************ * * * The reader core parser * * * ************************************************************************/ #ifdef DEBUG_READER static void xmlTextReaderDebug(xmlTextReaderPtr reader) { if ((reader == NULL) || (reader->ctxt == NULL)) { fprintf(stderr, "xmlTextReader NULL\n"); return; } fprintf(stderr, "xmlTextReader: state %d depth %d ", reader->state, reader->depth); if (reader->node == NULL) { fprintf(stderr, "node = NULL\n"); } else { fprintf(stderr, "node %s\n", reader->node->name); } fprintf(stderr, " input: base %d, cur %d, depth %d: ", reader->base, reader->cur, reader->ctxt->nodeNr); if (reader->input->buffer == NULL) { fprintf(stderr, "buffer is NULL\n"); } else { #ifdef LIBXML_DEBUG_ENABLED xmlDebugDumpString(stderr, &reader->input->buffer->content[reader->cur]); #endif fprintf(stderr, "\n"); } } #endif /** * xmlTextReaderEntPush: * @reader: the xmlTextReaderPtr used * @value: the entity reference node * * Pushes a new entity reference node on top of the entities stack * * Returns 0 in case of error, the index in the stack otherwise */ static int xmlTextReaderEntPush(xmlTextReaderPtr reader, xmlNodePtr value) { if (reader->entMax <= 0) { reader->entMax = 10; reader->entTab = (xmlNodePtr *) xmlMalloc(reader->entMax * sizeof(reader->entTab[0])); if (reader->entTab == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlMalloc failed !\n"); return (0); } } if (reader->entNr >= reader->entMax) { reader->entMax *= 2; reader->entTab = (xmlNodePtr *) xmlRealloc(reader->entTab, reader->entMax * sizeof(reader->entTab[0])); if (reader->entTab == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlRealloc failed !\n"); return (0); } } reader->entTab[reader->entNr] = value; reader->ent = value; return (reader->entNr++); } /** * xmlTextReaderEntPop: * @reader: the xmlTextReaderPtr used * * Pops the top element entity from the entities stack * * Returns the entity just removed */ static xmlNodePtr xmlTextReaderEntPop(xmlTextReaderPtr reader) { xmlNodePtr ret; if (reader->entNr <= 0) return (NULL); reader->entNr--; if (reader->entNr > 0) reader->ent = reader->entTab[reader->entNr - 1]; else reader->ent = NULL; ret = reader->entTab[reader->entNr]; reader->entTab[reader->entNr] = NULL; return (ret); } /** * xmlTextReaderStartElement: * @ctx: the user data (XML parser context) * @fullname: The element name, including namespace prefix * @atts: An array of name/value attributes pairs, NULL terminated * * called when an opening tag has been processed. */ static void xmlTextReaderStartElement(void *ctx, const xmlChar *fullname, const xmlChar **atts) { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlTextReaderPtr reader = ctxt->_private; #ifdef DEBUG_CALLBACKS printf("xmlTextReaderStartElement(%s)\n", fullname); #endif if ((reader != NULL) && (reader->startElement != NULL)) { reader->startElement(ctx, fullname, atts); if ((ctxt->node != NULL) && (ctxt->input != NULL) && (ctxt->input->cur != NULL) && (ctxt->input->cur[0] == '/') && (ctxt->input->cur[1] == '>')) ctxt->node->extra = NODE_IS_EMPTY; } if (reader != NULL) reader->state = XML_TEXTREADER_ELEMENT; } /** * xmlTextReaderEndElement: * @ctx: the user data (XML parser context) * @fullname: The element name, including namespace prefix * * called when an ending tag has been processed. */ static void xmlTextReaderEndElement(void *ctx, const xmlChar *fullname) { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlTextReaderPtr reader = ctxt->_private; #ifdef DEBUG_CALLBACKS printf("xmlTextReaderEndElement(%s)\n", fullname); #endif if ((reader != NULL) && (reader->endElement != NULL)) { reader->endElement(ctx, fullname); } } /** * xmlTextReaderStartElementNs: * @ctx: the user data (XML parser context) * @localname: the local name of the element * @prefix: the element namespace prefix if available * @URI: the element namespace name if available * @nb_namespaces: number of namespace definitions on that node * @namespaces: pointer to the array of prefix/URI pairs namespace definitions * @nb_attributes: the number of attributes on that node * nb_defaulted: the number of defaulted attributes. * @attributes: pointer to the array of (localname/prefix/URI/value/end) * attribute values. * * called when an opening tag has been processed. */ static void xmlTextReaderStartElementNs(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes) { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlTextReaderPtr reader = ctxt->_private; #ifdef DEBUG_CALLBACKS printf("xmlTextReaderStartElementNs(%s)\n", localname); #endif if ((reader != NULL) && (reader->startElementNs != NULL)) { reader->startElementNs(ctx, localname, prefix, URI, nb_namespaces, namespaces, nb_attributes, nb_defaulted, attributes); if ((ctxt->node != NULL) && (ctxt->input != NULL) && (ctxt->input->cur != NULL) && (ctxt->input->cur[0] == '/') && (ctxt->input->cur[1] == '>')) ctxt->node->extra = NODE_IS_EMPTY; } if (reader != NULL) reader->state = XML_TEXTREADER_ELEMENT; } /** * xmlTextReaderEndElementNs: * @ctx: the user data (XML parser context) * @localname: the local name of the element * @prefix: the element namespace prefix if available * @URI: the element namespace name if available * * called when an ending tag has been processed. */ static void xmlTextReaderEndElementNs(void *ctx, const xmlChar * localname, const xmlChar * prefix, const xmlChar * URI) { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlTextReaderPtr reader = ctxt->_private; #ifdef DEBUG_CALLBACKS printf("xmlTextReaderEndElementNs(%s)\n", localname); #endif if ((reader != NULL) && (reader->endElementNs != NULL)) { reader->endElementNs(ctx, localname, prefix, URI); } } /** * xmlTextReaderCharacters: * @ctx: the user data (XML parser context) * @ch: a xmlChar string * @len: the number of xmlChar * * receiving some chars from the parser. */ static void xmlTextReaderCharacters(void *ctx, const xmlChar *ch, int len) { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlTextReaderPtr reader = ctxt->_private; #ifdef DEBUG_CALLBACKS printf("xmlTextReaderCharacters()\n"); #endif if ((reader != NULL) && (reader->characters != NULL)) { reader->characters(ctx, ch, len); } } /** * xmlTextReaderCDataBlock: * @ctx: the user data (XML parser context) * @value: The pcdata content * @len: the block length * * called when a pcdata block has been parsed */ static void xmlTextReaderCDataBlock(void *ctx, const xmlChar *ch, int len) { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlTextReaderPtr reader = ctxt->_private; #ifdef DEBUG_CALLBACKS printf("xmlTextReaderCDataBlock()\n"); #endif if ((reader != NULL) && (reader->cdataBlock != NULL)) { reader->cdataBlock(ctx, ch, len); } } /** * xmlTextReaderPushData: * @reader: the xmlTextReaderPtr used * * Push data down the progressive parser until a significant callback * got raised. * * Returns -1 in case of failure, 0 otherwise */ static int xmlTextReaderPushData(xmlTextReaderPtr reader) { xmlBufPtr inbuf; int val, s; xmlTextReaderState oldstate; int alloc; if ((reader->input == NULL) || (reader->input->buffer == NULL)) return(-1); oldstate = reader->state; reader->state = XML_TEXTREADER_NONE; inbuf = reader->input->buffer; alloc = xmlBufGetAllocationScheme(inbuf); while (reader->state == XML_TEXTREADER_NONE) { if (xmlBufUse(inbuf) < reader->cur + CHUNK_SIZE) { /* * Refill the buffer unless we are at the end of the stream */ if (reader->mode != XML_TEXTREADER_MODE_EOF) { val = xmlParserInputBufferRead(reader->input, 4096); if ((val == 0) && (alloc == XML_BUFFER_ALLOC_IMMUTABLE)) { if (xmlBufUse(inbuf) == reader->cur) { reader->mode = XML_TEXTREADER_MODE_EOF; reader->state = oldstate; } } else if (val < 0) { reader->mode = XML_TEXTREADER_MODE_EOF; reader->state = oldstate; if ((oldstate != XML_TEXTREADER_START) || (reader->ctxt->myDoc != NULL)) return(val); } else if (val == 0) { /* mark the end of the stream and process the remains */ reader->mode = XML_TEXTREADER_MODE_EOF; break; } } else break; } /* * parse by block of CHUNK_SIZE bytes, various tests show that * it's the best tradeoff at least on a 1.2GH Duron */ if (xmlBufUse(inbuf) >= reader->cur + CHUNK_SIZE) { val = xmlParseChunk(reader->ctxt, (const char *) xmlBufContent(inbuf) + reader->cur, CHUNK_SIZE, 0); reader->cur += CHUNK_SIZE; if (val != 0) reader->ctxt->wellFormed = 0; if (reader->ctxt->wellFormed == 0) break; } else { s = xmlBufUse(inbuf) - reader->cur; val = xmlParseChunk(reader->ctxt, (const char *) xmlBufContent(inbuf) + reader->cur, s, 0); reader->cur += s; if (val != 0) reader->ctxt->wellFormed = 0; break; } } /* * Discard the consumed input when needed and possible */ if (reader->mode == XML_TEXTREADER_MODE_INTERACTIVE) { if (alloc != XML_BUFFER_ALLOC_IMMUTABLE) { if ((reader->cur >= 4096) && (xmlBufUse(inbuf) - reader->cur <= CHUNK_SIZE)) { val = xmlBufShrink(inbuf, reader->cur); if (val >= 0) { reader->cur -= val; } } } } /* * At the end of the stream signal that the work is done to the Push * parser. */ else if (reader->mode == XML_TEXTREADER_MODE_EOF) { if (reader->state != XML_TEXTREADER_DONE) { s = xmlBufUse(inbuf) - reader->cur; val = xmlParseChunk(reader->ctxt, (const char *) xmlBufContent(inbuf) + reader->cur, s, 1); reader->cur = xmlBufUse(inbuf); reader->state = XML_TEXTREADER_DONE; if (val != 0) { if (reader->ctxt->wellFormed) reader->ctxt->wellFormed = 0; else return(-1); } } } reader->state = oldstate; if (reader->ctxt->wellFormed == 0) { reader->mode = XML_TEXTREADER_MODE_EOF; return(-1); } return(0); } #ifdef LIBXML_REGEXP_ENABLED /** * xmlTextReaderValidatePush: * @reader: the xmlTextReaderPtr used * * Push the current node for validation */ static void xmlTextReaderValidatePush(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) { xmlNodePtr node = reader->node; #ifdef LIBXML_VALID_ENABLED if ((reader->validate == XML_TEXTREADER_VALIDATE_DTD) && (reader->ctxt != NULL) && (reader->ctxt->validate == 1)) { if ((node->ns == NULL) || (node->ns->prefix == NULL)) { reader->ctxt->valid &= xmlValidatePushElement(&reader->ctxt->vctxt, reader->ctxt->myDoc, node, node->name); } else { /* TODO use the BuildQName interface */ xmlChar *qname; qname = xmlStrdup(node->ns->prefix); qname = xmlStrcat(qname, BAD_CAST ":"); qname = xmlStrcat(qname, node->name); reader->ctxt->valid &= xmlValidatePushElement(&reader->ctxt->vctxt, reader->ctxt->myDoc, node, qname); if (qname != NULL) xmlFree(qname); } } #endif /* LIBXML_VALID_ENABLED */ #ifdef LIBXML_SCHEMAS_ENABLED if ((reader->validate == XML_TEXTREADER_VALIDATE_RNG) && (reader->rngValidCtxt != NULL)) { int ret; if (reader->rngFullNode != NULL) return; ret = xmlRelaxNGValidatePushElement(reader->rngValidCtxt, reader->ctxt->myDoc, node); if (ret == 0) { /* * this element requires a full tree */ node = xmlTextReaderExpand(reader); if (node == NULL) { printf("Expand failed !\n"); ret = -1; } else { ret = xmlRelaxNGValidateFullElement(reader->rngValidCtxt, reader->ctxt->myDoc, node); reader->rngFullNode = node; } } if (ret != 1) reader->rngValidErrors++; } #endif } /** * xmlTextReaderValidateCData: * @reader: the xmlTextReaderPtr used * @data: pointer to the CData * @len: length of the CData block in bytes. * * Push some CData for validation */ static void xmlTextReaderValidateCData(xmlTextReaderPtr reader, const xmlChar *data, int len) { #ifdef LIBXML_VALID_ENABLED if ((reader->validate == XML_TEXTREADER_VALIDATE_DTD) && (reader->ctxt != NULL) && (reader->ctxt->validate == 1)) { reader->ctxt->valid &= xmlValidatePushCData(&reader->ctxt->vctxt, data, len); } #endif /* LIBXML_VALID_ENABLED */ #ifdef LIBXML_SCHEMAS_ENABLED if ((reader->validate == XML_TEXTREADER_VALIDATE_RNG) && (reader->rngValidCtxt != NULL)) { int ret; if (reader->rngFullNode != NULL) return; ret = xmlRelaxNGValidatePushCData(reader->rngValidCtxt, data, len); if (ret != 1) reader->rngValidErrors++; } #endif } /** * xmlTextReaderValidatePop: * @reader: the xmlTextReaderPtr used * * Pop the current node from validation */ static void xmlTextReaderValidatePop(xmlTextReaderPtr reader) { xmlNodePtr node = reader->node; #ifdef LIBXML_VALID_ENABLED if ((reader->validate == XML_TEXTREADER_VALIDATE_DTD) && (reader->ctxt != NULL) && (reader->ctxt->validate == 1)) { if ((node->ns == NULL) || (node->ns->prefix == NULL)) { reader->ctxt->valid &= xmlValidatePopElement(&reader->ctxt->vctxt, reader->ctxt->myDoc, node, node->name); } else { /* TODO use the BuildQName interface */ xmlChar *qname; qname = xmlStrdup(node->ns->prefix); qname = xmlStrcat(qname, BAD_CAST ":"); qname = xmlStrcat(qname, node->name); reader->ctxt->valid &= xmlValidatePopElement(&reader->ctxt->vctxt, reader->ctxt->myDoc, node, qname); if (qname != NULL) xmlFree(qname); } } #endif /* LIBXML_VALID_ENABLED */ #ifdef LIBXML_SCHEMAS_ENABLED if ((reader->validate == XML_TEXTREADER_VALIDATE_RNG) && (reader->rngValidCtxt != NULL)) { int ret; if (reader->rngFullNode != NULL) { if (node == reader->rngFullNode) reader->rngFullNode = NULL; return; } ret = xmlRelaxNGValidatePopElement(reader->rngValidCtxt, reader->ctxt->myDoc, node); if (ret != 1) reader->rngValidErrors++; } #endif } /** * xmlTextReaderValidateEntity: * @reader: the xmlTextReaderPtr used * * Handle the validation when an entity reference is encountered and * entity substitution is not activated. As a result the parser interface * must walk through the entity and do the validation calls */ static void xmlTextReaderValidateEntity(xmlTextReaderPtr reader) { xmlNodePtr oldnode = reader->node; xmlNodePtr node = reader->node; xmlParserCtxtPtr ctxt = reader->ctxt; do { if (node->type == XML_ENTITY_REF_NODE) { /* * Case where the underlying tree is not availble, lookup the entity * and walk it. */ if ((node->children == NULL) && (ctxt->sax != NULL) && (ctxt->sax->getEntity != NULL)) { node->children = (xmlNodePtr) ctxt->sax->getEntity(ctxt, node->name); } if ((node->children != NULL) && (node->children->type == XML_ENTITY_DECL) && (node->children->children != NULL)) { xmlTextReaderEntPush(reader, node); node = node->children->children; continue; } else { /* * The error has probably be raised already. */ if (node == oldnode) break; node = node->next; } #ifdef LIBXML_REGEXP_ENABLED } else if (node->type == XML_ELEMENT_NODE) { reader->node = node; xmlTextReaderValidatePush(reader); } else if ((node->type == XML_TEXT_NODE) || (node->type == XML_CDATA_SECTION_NODE)) { xmlTextReaderValidateCData(reader, node->content, xmlStrlen(node->content)); #endif } /* * go to next node */ if (node->children != NULL) { node = node->children; continue; } else if (node->type == XML_ELEMENT_NODE) { xmlTextReaderValidatePop(reader); } if (node->next != NULL) { node = node->next; continue; } do { node = node->parent; if (node->type == XML_ELEMENT_NODE) { xmlNodePtr tmp; if (reader->entNr == 0) { while ((tmp = node->last) != NULL) { if ((tmp->extra & NODE_IS_PRESERVED) == 0) { xmlUnlinkNode(tmp); xmlTextReaderFreeNode(reader, tmp); } else break; } } reader->node = node; xmlTextReaderValidatePop(reader); } if ((node->type == XML_ENTITY_DECL) && (reader->ent != NULL) && (reader->ent->children == node)) { node = xmlTextReaderEntPop(reader); } if (node == oldnode) break; if (node->next != NULL) { node = node->next; break; } } while ((node != NULL) && (node != oldnode)); } while ((node != NULL) && (node != oldnode)); reader->node = oldnode; } #endif /* LIBXML_REGEXP_ENABLED */ /** * xmlTextReaderGetSuccessor: * @cur: the current node * * Get the successor of a node if available. * * Returns the successor node or NULL */ static xmlNodePtr xmlTextReaderGetSuccessor(xmlNodePtr cur) { if (cur == NULL) return(NULL) ; /* ERROR */ if (cur->next != NULL) return(cur->next) ; do { cur = cur->parent; if (cur == NULL) break; if (cur->next != NULL) return(cur->next); } while (cur != NULL); return(cur); } /** * xmlTextReaderDoExpand: * @reader: the xmlTextReaderPtr used * * Makes sure that the current node is fully read as well as all its * descendant. It means the full DOM subtree must be available at the * end of the call. * * Returns 1 if the node was expanded successfully, 0 if there is no more * nodes to read, or -1 in case of error */ static int xmlTextReaderDoExpand(xmlTextReaderPtr reader) { int val; if ((reader == NULL) || (reader->node == NULL) || (reader->ctxt == NULL)) return(-1); do { if (reader->ctxt->instate == XML_PARSER_EOF) return(1); if (xmlTextReaderGetSuccessor(reader->node) != NULL) return(1); if (reader->ctxt->nodeNr < reader->depth) return(1); if (reader->mode == XML_TEXTREADER_MODE_EOF) return(1); val = xmlTextReaderPushData(reader); if (val < 0){ reader->mode = XML_TEXTREADER_MODE_ERROR; return(-1); } } while(reader->mode != XML_TEXTREADER_MODE_EOF); return(1); } /** * xmlTextReaderCollectSiblings: * @node: the first child * * Traverse depth-first through all sibling nodes and their children * nodes and concatenate their content. This is an auxiliary function * to xmlTextReaderReadString. * * Returns a string containing the content, or NULL in case of error. */ static xmlChar * xmlTextReaderCollectSiblings(xmlNodePtr node) { xmlBufferPtr buffer; xmlChar *ret; if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) return(NULL); buffer = xmlBufferCreate(); if (buffer == NULL) return NULL; for ( ; node != NULL; node = node->next) { switch (node->type) { case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: xmlBufferCat(buffer, node->content); break; case XML_ELEMENT_NODE: { xmlChar *tmp; tmp = xmlTextReaderCollectSiblings(node->children); xmlBufferCat(buffer, tmp); xmlFree(tmp); break; } default: break; } } ret = buffer->content; buffer->content = NULL; xmlBufferFree(buffer); return(ret); } /** * xmlTextReaderRead: * @reader: the xmlTextReaderPtr used * * Moves the position of the current instance to the next node in * the stream, exposing its properties. * * Returns 1 if the node was read successfully, 0 if there is no more * nodes to read, or -1 in case of error */ int xmlTextReaderRead(xmlTextReaderPtr reader) { int val, olddepth = 0; xmlTextReaderState oldstate = XML_TEXTREADER_START; xmlNodePtr oldnode = NULL; if (reader == NULL) return(-1); reader->curnode = NULL; if (reader->doc != NULL) return(xmlTextReaderReadTree(reader)); if (reader->ctxt == NULL) return(-1); #ifdef DEBUG_READER fprintf(stderr, "\nREAD "); DUMP_READER #endif if (reader->mode == XML_TEXTREADER_MODE_INITIAL) { reader->mode = XML_TEXTREADER_MODE_INTERACTIVE; /* * Initial state */ do { val = xmlTextReaderPushData(reader); if (val < 0){ reader->mode = XML_TEXTREADER_MODE_ERROR; reader->state = XML_TEXTREADER_ERROR; return(-1); } } while ((reader->ctxt->node == NULL) && ((reader->mode != XML_TEXTREADER_MODE_EOF) && (reader->state != XML_TEXTREADER_DONE))); if (reader->ctxt->node == NULL) { if (reader->ctxt->myDoc != NULL) { reader->node = reader->ctxt->myDoc->children; } if (reader->node == NULL){ reader->mode = XML_TEXTREADER_MODE_ERROR; reader->state = XML_TEXTREADER_ERROR; return(-1); } reader->state = XML_TEXTREADER_ELEMENT; } else { if (reader->ctxt->myDoc != NULL) { reader->node = reader->ctxt->myDoc->children; } if (reader->node == NULL) reader->node = reader->ctxt->nodeTab[0]; reader->state = XML_TEXTREADER_ELEMENT; } reader->depth = 0; reader->ctxt->parseMode = XML_PARSE_READER; goto node_found; } oldstate = reader->state; olddepth = reader->ctxt->nodeNr; oldnode = reader->node; get_next_node: if (reader->node == NULL) { if (reader->mode == XML_TEXTREADER_MODE_EOF) return(0); else return(-1); } /* * If we are not backtracking on ancestors or examined nodes, * that the parser didn't finished or that we arent at the end * of stream, continue processing. */ while ((reader->node != NULL) && (reader->node->next == NULL) && (reader->ctxt->nodeNr == olddepth) && ((oldstate == XML_TEXTREADER_BACKTRACK) || (reader->node->children == NULL) || (reader->node->type == XML_ENTITY_REF_NODE) || ((reader->node->children != NULL) && (reader->node->children->type == XML_TEXT_NODE) && (reader->node->children->next == NULL)) || (reader->node->type == XML_DTD_NODE) || (reader->node->type == XML_DOCUMENT_NODE) || (reader->node->type == XML_HTML_DOCUMENT_NODE)) && ((reader->ctxt->node == NULL) || (reader->ctxt->node == reader->node) || (reader->ctxt->node == reader->node->parent)) && (reader->ctxt->instate != XML_PARSER_EOF)) { val = xmlTextReaderPushData(reader); if (val < 0){ reader->mode = XML_TEXTREADER_MODE_ERROR; reader->state = XML_TEXTREADER_ERROR; return(-1); } if (reader->node == NULL) goto node_end; } if (oldstate != XML_TEXTREADER_BACKTRACK) { if ((reader->node->children != NULL) && (reader->node->type != XML_ENTITY_REF_NODE) && (reader->node->type != XML_XINCLUDE_START) && (reader->node->type != XML_DTD_NODE)) { reader->node = reader->node->children; reader->depth++; reader->state = XML_TEXTREADER_ELEMENT; goto node_found; } } if (reader->node->next != NULL) { if ((oldstate == XML_TEXTREADER_ELEMENT) && (reader->node->type == XML_ELEMENT_NODE) && (reader->node->children == NULL) && ((reader->node->extra & NODE_IS_EMPTY) == 0) #ifdef LIBXML_XINCLUDE_ENABLED && (reader->in_xinclude <= 0) #endif ) { reader->state = XML_TEXTREADER_END; goto node_found; } #ifdef LIBXML_REGEXP_ENABLED if ((reader->validate) && (reader->node->type == XML_ELEMENT_NODE)) xmlTextReaderValidatePop(reader); #endif /* LIBXML_REGEXP_ENABLED */ if ((reader->preserves > 0) && (reader->node->extra & NODE_IS_SPRESERVED)) reader->preserves--; reader->node = reader->node->next; reader->state = XML_TEXTREADER_ELEMENT; /* * Cleanup of the old node */ if ((reader->preserves == 0) && #ifdef LIBXML_XINCLUDE_ENABLED (reader->in_xinclude == 0) && #endif (reader->entNr == 0) && (reader->node->prev != NULL) && (reader->node->prev->type != XML_DTD_NODE)) { xmlNodePtr tmp = reader->node->prev; if ((tmp->extra & NODE_IS_PRESERVED) == 0) { xmlUnlinkNode(tmp); xmlTextReaderFreeNode(reader, tmp); } } goto node_found; } if ((oldstate == XML_TEXTREADER_ELEMENT) && (reader->node->type == XML_ELEMENT_NODE) && (reader->node->children == NULL) && ((reader->node->extra & NODE_IS_EMPTY) == 0)) {; reader->state = XML_TEXTREADER_END; goto node_found; } #ifdef LIBXML_REGEXP_ENABLED if ((reader->validate != XML_TEXTREADER_NOT_VALIDATE) && (reader->node->type == XML_ELEMENT_NODE)) xmlTextReaderValidatePop(reader); #endif /* LIBXML_REGEXP_ENABLED */ if ((reader->preserves > 0) && (reader->node->extra & NODE_IS_SPRESERVED)) reader->preserves--; reader->node = reader->node->parent; if ((reader->node == NULL) || (reader->node->type == XML_DOCUMENT_NODE) || #ifdef LIBXML_DOCB_ENABLED (reader->node->type == XML_DOCB_DOCUMENT_NODE) || #endif (reader->node->type == XML_HTML_DOCUMENT_NODE)) { if (reader->mode != XML_TEXTREADER_MODE_EOF) { val = xmlParseChunk(reader->ctxt, "", 0, 1); reader->state = XML_TEXTREADER_DONE; if (val != 0) return(-1); } reader->node = NULL; reader->depth = -1; /* * Cleanup of the old node */ if ((oldnode != NULL) && (reader->preserves == 0) && #ifdef LIBXML_XINCLUDE_ENABLED (reader->in_xinclude == 0) && #endif (reader->entNr == 0) && (oldnode->type != XML_DTD_NODE) && ((oldnode->extra & NODE_IS_PRESERVED) == 0)) { xmlUnlinkNode(oldnode); xmlTextReaderFreeNode(reader, oldnode); } goto node_end; } if ((reader->preserves == 0) && #ifdef LIBXML_XINCLUDE_ENABLED (reader->in_xinclude == 0) && #endif (reader->entNr == 0) && (reader->node->last != NULL) && ((reader->node->last->extra & NODE_IS_PRESERVED) == 0)) { xmlNodePtr tmp = reader->node->last; xmlUnlinkNode(tmp); xmlTextReaderFreeNode(reader, tmp); } reader->depth--; reader->state = XML_TEXTREADER_BACKTRACK; node_found: DUMP_READER /* * If we are in the middle of a piece of CDATA make sure it's finished */ if ((reader->node != NULL) && (reader->node->next == NULL) && ((reader->node->type == XML_TEXT_NODE) || (reader->node->type == XML_CDATA_SECTION_NODE))) { if (xmlTextReaderExpand(reader) == NULL) return -1; } #ifdef LIBXML_XINCLUDE_ENABLED /* * Handle XInclude if asked for */ if ((reader->xinclude) && (reader->node != NULL) && (reader->node->type == XML_ELEMENT_NODE) && (reader->node->ns != NULL) && ((xmlStrEqual(reader->node->ns->href, XINCLUDE_NS)) || (xmlStrEqual(reader->node->ns->href, XINCLUDE_OLD_NS)))) { if (reader->xincctxt == NULL) { reader->xincctxt = xmlXIncludeNewContext(reader->ctxt->myDoc); xmlXIncludeSetFlags(reader->xincctxt, reader->parserFlags & (~XML_PARSE_NOXINCNODE)); } /* * expand that node and process it */ if (xmlTextReaderExpand(reader) == NULL) return -1; xmlXIncludeProcessNode(reader->xincctxt, reader->node); } if ((reader->node != NULL) && (reader->node->type == XML_XINCLUDE_START)) { reader->in_xinclude++; goto get_next_node; } if ((reader->node != NULL) && (reader->node->type == XML_XINCLUDE_END)) { reader->in_xinclude--; goto get_next_node; } #endif /* * Handle entities enter and exit when in entity replacement mode */ if ((reader->node != NULL) && (reader->node->type == XML_ENTITY_REF_NODE) && (reader->ctxt != NULL) && (reader->ctxt->replaceEntities == 1)) { /* * Case where the underlying tree is not availble, lookup the entity * and walk it. */ if ((reader->node->children == NULL) && (reader->ctxt->sax != NULL) && (reader->ctxt->sax->getEntity != NULL)) { reader->node->children = (xmlNodePtr) reader->ctxt->sax->getEntity(reader->ctxt, reader->node->name); } if ((reader->node->children != NULL) && (reader->node->children->type == XML_ENTITY_DECL) && (reader->node->children->children != NULL)) { xmlTextReaderEntPush(reader, reader->node); reader->node = reader->node->children->children; } #ifdef LIBXML_REGEXP_ENABLED } else if ((reader->node != NULL) && (reader->node->type == XML_ENTITY_REF_NODE) && (reader->ctxt != NULL) && (reader->validate)) { xmlTextReaderValidateEntity(reader); #endif /* LIBXML_REGEXP_ENABLED */ } if ((reader->node != NULL) && (reader->node->type == XML_ENTITY_DECL) && (reader->ent != NULL) && (reader->ent->children == reader->node)) { reader->node = xmlTextReaderEntPop(reader); reader->depth++; goto get_next_node; } #ifdef LIBXML_REGEXP_ENABLED if ((reader->validate != XML_TEXTREADER_NOT_VALIDATE) && (reader->node != NULL)) { xmlNodePtr node = reader->node; if ((node->type == XML_ELEMENT_NODE) && ((reader->state != XML_TEXTREADER_END) && (reader->state != XML_TEXTREADER_BACKTRACK))) { xmlTextReaderValidatePush(reader); } else if ((node->type == XML_TEXT_NODE) || (node->type == XML_CDATA_SECTION_NODE)) { xmlTextReaderValidateCData(reader, node->content, xmlStrlen(node->content)); } } #endif /* LIBXML_REGEXP_ENABLED */ #ifdef LIBXML_PATTERN_ENABLED if ((reader->patternNr > 0) && (reader->state != XML_TEXTREADER_END) && (reader->state != XML_TEXTREADER_BACKTRACK)) { int i; for (i = 0;i < reader->patternNr;i++) { if (xmlPatternMatch(reader->patternTab[i], reader->node) == 1) { xmlTextReaderPreserve(reader); break; } } } #endif /* LIBXML_PATTERN_ENABLED */ #ifdef LIBXML_SCHEMAS_ENABLED if ((reader->validate == XML_TEXTREADER_VALIDATE_XSD) && (reader->xsdValidErrors == 0) && (reader->xsdValidCtxt != NULL)) { reader->xsdValidErrors = !xmlSchemaIsValid(reader->xsdValidCtxt); } #endif /* LIBXML_PATTERN_ENABLED */ return(1); node_end: reader->state = XML_TEXTREADER_DONE; return(0); } /** * xmlTextReaderReadState: * @reader: the xmlTextReaderPtr used * * Gets the read state of the reader. * * Returns the state value, or -1 in case of error */ int xmlTextReaderReadState(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); return(reader->mode); } /** * xmlTextReaderExpand: * @reader: the xmlTextReaderPtr used * * Reads the contents of the current node and the full subtree. It then makes * the subtree available until the next xmlTextReaderRead() call * * Returns a node pointer valid until the next xmlTextReaderRead() call * or NULL in case of error. */ xmlNodePtr xmlTextReaderExpand(xmlTextReaderPtr reader) { if ((reader == NULL) || (reader->node == NULL)) return(NULL); if (reader->doc != NULL) return(reader->node); if (reader->ctxt == NULL) return(NULL); if (xmlTextReaderDoExpand(reader) < 0) return(NULL); return(reader->node); } /** * xmlTextReaderNext: * @reader: the xmlTextReaderPtr used * * Skip to the node following the current one in document order while * avoiding the subtree if any. * * Returns 1 if the node was read successfully, 0 if there is no more * nodes to read, or -1 in case of error */ int xmlTextReaderNext(xmlTextReaderPtr reader) { int ret; xmlNodePtr cur; if (reader == NULL) return(-1); if (reader->doc != NULL) return(xmlTextReaderNextTree(reader)); cur = reader->node; if ((cur == NULL) || (cur->type != XML_ELEMENT_NODE)) return(xmlTextReaderRead(reader)); if (reader->state == XML_TEXTREADER_END || reader->state == XML_TEXTREADER_BACKTRACK) return(xmlTextReaderRead(reader)); if (cur->extra & NODE_IS_EMPTY) return(xmlTextReaderRead(reader)); do { ret = xmlTextReaderRead(reader); if (ret != 1) return(ret); } while (reader->node != cur); return(xmlTextReaderRead(reader)); } #ifdef LIBXML_WRITER_ENABLED /** * xmlTextReaderReadInnerXml: * @reader: the xmlTextReaderPtr used * * Reads the contents of the current node, including child nodes and markup. * * Returns a string containing the XML content, or NULL if the current node * is neither an element nor attribute, or has no child nodes. The * string must be deallocated by the caller. */ xmlChar * xmlTextReaderReadInnerXml(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) { xmlChar *resbuf; xmlNodePtr node, cur_node; xmlBufferPtr buff, buff2; xmlDocPtr doc; if (xmlTextReaderExpand(reader) == NULL) { return NULL; } doc = reader->doc; buff = xmlBufferCreate(); for (cur_node = reader->node->children; cur_node != NULL; cur_node = cur_node->next) { node = xmlDocCopyNode(cur_node, doc, 1); buff2 = xmlBufferCreate(); if (xmlNodeDump(buff2, doc, node, 0, 0) == -1) { xmlFreeNode(node); xmlBufferFree(buff2); xmlBufferFree(buff); return NULL; } xmlBufferCat(buff, buff2->content); xmlFreeNode(node); xmlBufferFree(buff2); } resbuf = buff->content; buff->content = NULL; xmlBufferFree(buff); return resbuf; } #endif #ifdef LIBXML_WRITER_ENABLED /** * xmlTextReaderReadOuterXml: * @reader: the xmlTextReaderPtr used * * Reads the contents of the current node, including child nodes and markup. * * Returns a string containing the node and any XML content, or NULL if the * current node cannot be serialized. The string must be deallocated * by the caller. */ xmlChar * xmlTextReaderReadOuterXml(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) { xmlChar *resbuf; xmlNodePtr node; xmlBufferPtr buff; xmlDocPtr doc; node = reader->node; doc = reader->doc; if (xmlTextReaderExpand(reader) == NULL) { return NULL; } if (node->type == XML_DTD_NODE) { node = (xmlNodePtr) xmlCopyDtd((xmlDtdPtr) node); } else { node = xmlDocCopyNode(node, doc, 1); } buff = xmlBufferCreate(); if (xmlNodeDump(buff, doc, node, 0, 0) == -1) { xmlFreeNode(node); xmlBufferFree(buff); return NULL; } resbuf = buff->content; buff->content = NULL; xmlFreeNode(node); xmlBufferFree(buff); return resbuf; } #endif /** * xmlTextReaderReadString: * @reader: the xmlTextReaderPtr used * * Reads the contents of an element or a text node as a string. * * Returns a string containing the contents of the Element or Text node, * or NULL if the reader is positioned on any other type of node. * The string must be deallocated by the caller. */ xmlChar * xmlTextReaderReadString(xmlTextReaderPtr reader) { xmlNodePtr node; if ((reader == NULL) || (reader->node == NULL)) return(NULL); node = (reader->curnode != NULL) ? reader->curnode : reader->node; switch (node->type) { case XML_TEXT_NODE: if (node->content != NULL) return(xmlStrdup(node->content)); break; case XML_ELEMENT_NODE: if (xmlTextReaderDoExpand(reader) != -1) { return xmlTextReaderCollectSiblings(node->children); } break; case XML_ATTRIBUTE_NODE: TODO break; default: break; } return(NULL); } #if 0 /** * xmlTextReaderReadBase64: * @reader: the xmlTextReaderPtr used * @array: a byte array to store the content. * @offset: the zero-based index into array where the method should * begin to write. * @len: the number of bytes to write. * * Reads and decodes the Base64 encoded contents of an element and * stores the result in a byte buffer. * * Returns the number of bytes written to array, or zero if the current * instance is not positioned on an element or -1 in case of error. */ int xmlTextReaderReadBase64(xmlTextReaderPtr reader, unsigned char *array ATTRIBUTE_UNUSED, int offset ATTRIBUTE_UNUSED, int len ATTRIBUTE_UNUSED) { if ((reader == NULL) || (reader->ctxt == NULL)) return(-1); if (reader->ctxt->wellFormed != 1) return(-1); if ((reader->node == NULL) || (reader->node->type == XML_ELEMENT_NODE)) return(0); TODO return(0); } /** * xmlTextReaderReadBinHex: * @reader: the xmlTextReaderPtr used * @array: a byte array to store the content. * @offset: the zero-based index into array where the method should * begin to write. * @len: the number of bytes to write. * * Reads and decodes the BinHex encoded contents of an element and * stores the result in a byte buffer. * * Returns the number of bytes written to array, or zero if the current * instance is not positioned on an element or -1 in case of error. */ int xmlTextReaderReadBinHex(xmlTextReaderPtr reader, unsigned char *array ATTRIBUTE_UNUSED, int offset ATTRIBUTE_UNUSED, int len ATTRIBUTE_UNUSED) { if ((reader == NULL) || (reader->ctxt == NULL)) return(-1); if (reader->ctxt->wellFormed != 1) return(-1); if ((reader->node == NULL) || (reader->node->type == XML_ELEMENT_NODE)) return(0); TODO return(0); } #endif /************************************************************************ * * * Operating on a preparsed tree * * * ************************************************************************/ static int xmlTextReaderNextTree(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); if (reader->state == XML_TEXTREADER_END) return(0); if (reader->node == NULL) { if (reader->doc->children == NULL) { reader->state = XML_TEXTREADER_END; return(0); } reader->node = reader->doc->children; reader->state = XML_TEXTREADER_START; return(1); } if (reader->state != XML_TEXTREADER_BACKTRACK) { /* Here removed traversal to child, because we want to skip the subtree, replace with traversal to sibling to skip subtree */ if (reader->node->next != 0) { /* Move to sibling if present,skipping sub-tree */ reader->node = reader->node->next; reader->state = XML_TEXTREADER_START; return(1); } /* if reader->node->next is NULL mean no subtree for current node, so need to move to sibling of parent node if present */ if ((reader->node->type == XML_ELEMENT_NODE) || (reader->node->type == XML_ATTRIBUTE_NODE)) { reader->state = XML_TEXTREADER_BACKTRACK; /* This will move to parent if present */ xmlTextReaderRead(reader); } } if (reader->node->next != 0) { reader->node = reader->node->next; reader->state = XML_TEXTREADER_START; return(1); } if (reader->node->parent != 0) { if (reader->node->parent->type == XML_DOCUMENT_NODE) { reader->state = XML_TEXTREADER_END; return(0); } reader->node = reader->node->parent; reader->depth--; reader->state = XML_TEXTREADER_BACKTRACK; /* Repeat process to move to sibling of parent node if present */ xmlTextReaderNextTree(reader); } reader->state = XML_TEXTREADER_END; return(1); } /** * xmlTextReaderReadTree: * @reader: the xmlTextReaderPtr used * * Moves the position of the current instance to the next node in * the stream, exposing its properties. * * Returns 1 if the node was read successfully, 0 if there is no more * nodes to read, or -1 in case of error */ static int xmlTextReaderReadTree(xmlTextReaderPtr reader) { if (reader->state == XML_TEXTREADER_END) return(0); next_node: if (reader->node == NULL) { if (reader->doc->children == NULL) { reader->state = XML_TEXTREADER_END; return(0); } reader->node = reader->doc->children; reader->state = XML_TEXTREADER_START; goto found_node; } if ((reader->state != XML_TEXTREADER_BACKTRACK) && (reader->node->type != XML_DTD_NODE) && (reader->node->type != XML_XINCLUDE_START) && (reader->node->type != XML_ENTITY_REF_NODE)) { if (reader->node->children != NULL) { reader->node = reader->node->children; reader->depth++; reader->state = XML_TEXTREADER_START; goto found_node; } if (reader->node->type == XML_ATTRIBUTE_NODE) { reader->state = XML_TEXTREADER_BACKTRACK; goto found_node; } } if (reader->node->next != NULL) { reader->node = reader->node->next; reader->state = XML_TEXTREADER_START; goto found_node; } if (reader->node->parent != NULL) { if ((reader->node->parent->type == XML_DOCUMENT_NODE) || (reader->node->parent->type == XML_HTML_DOCUMENT_NODE)) { reader->state = XML_TEXTREADER_END; return(0); } reader->node = reader->node->parent; reader->depth--; reader->state = XML_TEXTREADER_BACKTRACK; goto found_node; } reader->state = XML_TEXTREADER_END; found_node: if ((reader->node->type == XML_XINCLUDE_START) || (reader->node->type == XML_XINCLUDE_END)) goto next_node; return(1); } /** * xmlTextReaderNextSibling: * @reader: the xmlTextReaderPtr used * * Skip to the node following the current one in document order while * avoiding the subtree if any. * Currently implemented only for Readers built on a document * * Returns 1 if the node was read successfully, 0 if there is no more * nodes to read, or -1 in case of error */ int xmlTextReaderNextSibling(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); if (reader->doc == NULL) { /* TODO */ return(-1); } if (reader->state == XML_TEXTREADER_END) return(0); if (reader->node == NULL) return(xmlTextReaderNextTree(reader)); if (reader->node->next != NULL) { reader->node = reader->node->next; reader->state = XML_TEXTREADER_START; return(1); } return(0); } /************************************************************************ * * * Constructor and destructors * * * ************************************************************************/ /** * xmlNewTextReader: * @input: the xmlParserInputBufferPtr used to read data * @URI: the URI information for the source if available * * Create an xmlTextReader structure fed with @input * * Returns the new xmlTextReaderPtr or NULL in case of error */ xmlTextReaderPtr xmlNewTextReader(xmlParserInputBufferPtr input, const char *URI) { xmlTextReaderPtr ret; if (input == NULL) return(NULL); ret = xmlMalloc(sizeof(xmlTextReader)); if (ret == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlNewTextReader : malloc failed\n"); return(NULL); } memset(ret, 0, sizeof(xmlTextReader)); ret->doc = NULL; ret->entTab = NULL; ret->entMax = 0; ret->entNr = 0; ret->input = input; ret->buffer = xmlBufCreateSize(100); if (ret->buffer == NULL) { xmlFree(ret); xmlGenericError(xmlGenericErrorContext, "xmlNewTextReader : malloc failed\n"); return(NULL); } /* no operation on a reader should require a huge buffer */ xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_BOUNDED); ret->sax = (xmlSAXHandler *) xmlMalloc(sizeof(xmlSAXHandler)); if (ret->sax == NULL) { xmlBufFree(ret->buffer); xmlFree(ret); xmlGenericError(xmlGenericErrorContext, "xmlNewTextReader : malloc failed\n"); return(NULL); } xmlSAXVersion(ret->sax, 2); ret->startElement = ret->sax->startElement; ret->sax->startElement = xmlTextReaderStartElement; ret->endElement = ret->sax->endElement; ret->sax->endElement = xmlTextReaderEndElement; #ifdef LIBXML_SAX1_ENABLED if (ret->sax->initialized == XML_SAX2_MAGIC) { #endif /* LIBXML_SAX1_ENABLED */ ret->startElementNs = ret->sax->startElementNs; ret->sax->startElementNs = xmlTextReaderStartElementNs; ret->endElementNs = ret->sax->endElementNs; ret->sax->endElementNs = xmlTextReaderEndElementNs; #ifdef LIBXML_SAX1_ENABLED } else { ret->startElementNs = NULL; ret->endElementNs = NULL; } #endif /* LIBXML_SAX1_ENABLED */ ret->characters = ret->sax->characters; ret->sax->characters = xmlTextReaderCharacters; ret->sax->ignorableWhitespace = xmlTextReaderCharacters; ret->cdataBlock = ret->sax->cdataBlock; ret->sax->cdataBlock = xmlTextReaderCDataBlock; ret->mode = XML_TEXTREADER_MODE_INITIAL; ret->node = NULL; ret->curnode = NULL; if (xmlBufUse(ret->input->buffer) < 4) { xmlParserInputBufferRead(input, 4); } if (xmlBufUse(ret->input->buffer) >= 4) { ret->ctxt = xmlCreatePushParserCtxt(ret->sax, NULL, (const char *) xmlBufContent(ret->input->buffer), 4, URI); ret->base = 0; ret->cur = 4; } else { ret->ctxt = xmlCreatePushParserCtxt(ret->sax, NULL, NULL, 0, URI); ret->base = 0; ret->cur = 0; } if (ret->ctxt == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlNewTextReader : malloc failed\n"); xmlBufFree(ret->buffer); xmlFree(ret->sax); xmlFree(ret); return(NULL); } ret->ctxt->parseMode = XML_PARSE_READER; ret->ctxt->_private = ret; ret->ctxt->linenumbers = 1; ret->ctxt->dictNames = 1; ret->allocs = XML_TEXTREADER_CTXT; /* * use the parser dictionary to allocate all elements and attributes names */ ret->ctxt->docdict = 1; ret->dict = ret->ctxt->dict; #ifdef LIBXML_XINCLUDE_ENABLED ret->xinclude = 0; #endif #ifdef LIBXML_PATTERN_ENABLED ret->patternMax = 0; ret->patternTab = NULL; #endif return(ret); } /** * xmlNewTextReaderFilename: * @URI: the URI of the resource to process * * Create an xmlTextReader structure fed with the resource at @URI * * Returns the new xmlTextReaderPtr or NULL in case of error */ xmlTextReaderPtr xmlNewTextReaderFilename(const char *URI) { xmlParserInputBufferPtr input; xmlTextReaderPtr ret; char *directory = NULL; input = xmlParserInputBufferCreateFilename(URI, XML_CHAR_ENCODING_NONE); if (input == NULL) return(NULL); ret = xmlNewTextReader(input, URI); if (ret == NULL) { xmlFreeParserInputBuffer(input); return(NULL); } ret->allocs |= XML_TEXTREADER_INPUT; if (ret->ctxt->directory == NULL) directory = xmlParserGetDirectory(URI); if ((ret->ctxt->directory == NULL) && (directory != NULL)) ret->ctxt->directory = (char *) xmlStrdup((xmlChar *) directory); if (directory != NULL) xmlFree(directory); return(ret); } /** * xmlFreeTextReader: * @reader: the xmlTextReaderPtr * * Deallocate all the resources associated to the reader */ void xmlFreeTextReader(xmlTextReaderPtr reader) { if (reader == NULL) return; #ifdef LIBXML_SCHEMAS_ENABLED if (reader->rngSchemas != NULL) { xmlRelaxNGFree(reader->rngSchemas); reader->rngSchemas = NULL; } if (reader->rngValidCtxt != NULL) { if (! reader->rngPreserveCtxt) xmlRelaxNGFreeValidCtxt(reader->rngValidCtxt); reader->rngValidCtxt = NULL; } if (reader->xsdPlug != NULL) { xmlSchemaSAXUnplug(reader->xsdPlug); reader->xsdPlug = NULL; } if (reader->xsdValidCtxt != NULL) { if (! reader->xsdPreserveCtxt) xmlSchemaFreeValidCtxt(reader->xsdValidCtxt); reader->xsdValidCtxt = NULL; } if (reader->xsdSchemas != NULL) { xmlSchemaFree(reader->xsdSchemas); reader->xsdSchemas = NULL; } #endif #ifdef LIBXML_XINCLUDE_ENABLED if (reader->xincctxt != NULL) xmlXIncludeFreeContext(reader->xincctxt); #endif #ifdef LIBXML_PATTERN_ENABLED if (reader->patternTab != NULL) { int i; for (i = 0;i < reader->patternNr;i++) { if (reader->patternTab[i] != NULL) xmlFreePattern(reader->patternTab[i]); } xmlFree(reader->patternTab); } #endif if (reader->faketext != NULL) { xmlFreeNode(reader->faketext); } if (reader->ctxt != NULL) { if (reader->dict == reader->ctxt->dict) reader->dict = NULL; if (reader->ctxt->myDoc != NULL) { if (reader->preserve == 0) xmlTextReaderFreeDoc(reader, reader->ctxt->myDoc); reader->ctxt->myDoc = NULL; } if ((reader->ctxt->vctxt.vstateTab != NULL) && (reader->ctxt->vctxt.vstateMax > 0)){ xmlFree(reader->ctxt->vctxt.vstateTab); reader->ctxt->vctxt.vstateTab = NULL; reader->ctxt->vctxt.vstateMax = 0; } if (reader->allocs & XML_TEXTREADER_CTXT) xmlFreeParserCtxt(reader->ctxt); } if (reader->sax != NULL) xmlFree(reader->sax); if ((reader->input != NULL) && (reader->allocs & XML_TEXTREADER_INPUT)) xmlFreeParserInputBuffer(reader->input); if (reader->buffer != NULL) xmlBufFree(reader->buffer); if (reader->entTab != NULL) xmlFree(reader->entTab); if (reader->dict != NULL) xmlDictFree(reader->dict); xmlFree(reader); } /************************************************************************ * * * Methods for XmlTextReader * * * ************************************************************************/ /** * xmlTextReaderClose: * @reader: the xmlTextReaderPtr used * * This method releases any resources allocated by the current instance * changes the state to Closed and close any underlying input. * * Returns 0 or -1 in case of error */ int xmlTextReaderClose(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); reader->node = NULL; reader->curnode = NULL; reader->mode = XML_TEXTREADER_MODE_CLOSED; if (reader->ctxt != NULL) { xmlStopParser(reader->ctxt); if (reader->ctxt->myDoc != NULL) { if (reader->preserve == 0) xmlTextReaderFreeDoc(reader, reader->ctxt->myDoc); reader->ctxt->myDoc = NULL; } } if ((reader->input != NULL) && (reader->allocs & XML_TEXTREADER_INPUT)) { xmlFreeParserInputBuffer(reader->input); reader->allocs -= XML_TEXTREADER_INPUT; } return(0); } /** * xmlTextReaderGetAttributeNo: * @reader: the xmlTextReaderPtr used * @no: the zero-based index of the attribute relative to the containing element * * Provides the value of the attribute with the specified index relative * to the containing element. * * Returns a string containing the value of the specified attribute, or NULL * in case of error. The string must be deallocated by the caller. */ xmlChar * xmlTextReaderGetAttributeNo(xmlTextReaderPtr reader, int no) { xmlChar *ret; int i; xmlAttrPtr cur; xmlNsPtr ns; if (reader == NULL) return(NULL); if (reader->node == NULL) return(NULL); if (reader->curnode != NULL) return(NULL); /* TODO: handle the xmlDecl */ if (reader->node->type != XML_ELEMENT_NODE) return(NULL); ns = reader->node->nsDef; for (i = 0;(i < no) && (ns != NULL);i++) { ns = ns->next; } if (ns != NULL) return(xmlStrdup(ns->href)); cur = reader->node->properties; if (cur == NULL) return(NULL); for (;i < no;i++) { cur = cur->next; if (cur == NULL) return(NULL); } /* TODO walk the DTD if present */ ret = xmlNodeListGetString(reader->node->doc, cur->children, 1); if (ret == NULL) return(xmlStrdup((xmlChar *)"")); return(ret); } /** * xmlTextReaderGetAttribute: * @reader: the xmlTextReaderPtr used * @name: the qualified name of the attribute. * * Provides the value of the attribute with the specified qualified name. * * Returns a string containing the value of the specified attribute, or NULL * in case of error. The string must be deallocated by the caller. */ xmlChar * xmlTextReaderGetAttribute(xmlTextReaderPtr reader, const xmlChar *name) { xmlChar *prefix = NULL; xmlChar *localname; xmlNsPtr ns; xmlChar *ret = NULL; if ((reader == NULL) || (name == NULL)) return(NULL); if (reader->node == NULL) return(NULL); if (reader->curnode != NULL) return(NULL); /* TODO: handle the xmlDecl */ if (reader->node->type != XML_ELEMENT_NODE) return(NULL); localname = xmlSplitQName2(name, &prefix); if (localname == NULL) { /* * Namespace default decl */ if (xmlStrEqual(name, BAD_CAST "xmlns")) { ns = reader->node->nsDef; while (ns != NULL) { if (ns->prefix == NULL) { return(xmlStrdup(ns->href)); } ns = ns->next; } return NULL; } return(xmlGetNoNsProp(reader->node, name)); } /* * Namespace default decl */ if (xmlStrEqual(prefix, BAD_CAST "xmlns")) { ns = reader->node->nsDef; while (ns != NULL) { if ((ns->prefix != NULL) && (xmlStrEqual(ns->prefix, localname))) { ret = xmlStrdup(ns->href); break; } ns = ns->next; } } else { ns = xmlSearchNs(reader->node->doc, reader->node, prefix); if (ns != NULL) ret = xmlGetNsProp(reader->node, localname, ns->href); } xmlFree(localname); if (prefix != NULL) xmlFree(prefix); return(ret); } /** * xmlTextReaderGetAttributeNs: * @reader: the xmlTextReaderPtr used * @localName: the local name of the attribute. * @namespaceURI: the namespace URI of the attribute. * * Provides the value of the specified attribute * * Returns a string containing the value of the specified attribute, or NULL * in case of error. The string must be deallocated by the caller. */ xmlChar * xmlTextReaderGetAttributeNs(xmlTextReaderPtr reader, const xmlChar *localName, const xmlChar *namespaceURI) { xmlChar *prefix = NULL; xmlNsPtr ns; if ((reader == NULL) || (localName == NULL)) return(NULL); if (reader->node == NULL) return(NULL); if (reader->curnode != NULL) return(NULL); /* TODO: handle the xmlDecl */ if (reader->node->type != XML_ELEMENT_NODE) return(NULL); if (xmlStrEqual(namespaceURI, BAD_CAST "http://www.w3.org/2000/xmlns/")) { if (! xmlStrEqual(localName, BAD_CAST "xmlns")) { prefix = BAD_CAST localName; } ns = reader->node->nsDef; while (ns != NULL) { if ((prefix == NULL && ns->prefix == NULL) || ((ns->prefix != NULL) && (xmlStrEqual(ns->prefix, localName)))) { return xmlStrdup(ns->href); } ns = ns->next; } return NULL; } return(xmlGetNsProp(reader->node, localName, namespaceURI)); } /** * xmlTextReaderGetRemainder: * @reader: the xmlTextReaderPtr used * * Method to get the remainder of the buffered XML. this method stops the * parser, set its state to End Of File and return the input stream with * what is left that the parser did not use. * * The implementation is not good, the parser certainly procgressed past * what's left in reader->input, and there is an allocation problem. Best * would be to rewrite it differently. * * Returns the xmlParserInputBufferPtr attached to the XML or NULL * in case of error. */ xmlParserInputBufferPtr xmlTextReaderGetRemainder(xmlTextReaderPtr reader) { xmlParserInputBufferPtr ret = NULL; if (reader == NULL) return(NULL); if (reader->node == NULL) return(NULL); reader->node = NULL; reader->curnode = NULL; reader->mode = XML_TEXTREADER_MODE_EOF; if (reader->ctxt != NULL) { xmlStopParser(reader->ctxt); if (reader->ctxt->myDoc != NULL) { if (reader->preserve == 0) xmlTextReaderFreeDoc(reader, reader->ctxt->myDoc); reader->ctxt->myDoc = NULL; } } if (reader->allocs & XML_TEXTREADER_INPUT) { ret = reader->input; reader->input = NULL; reader->allocs -= XML_TEXTREADER_INPUT; } else { /* * Hum, one may need to duplicate the data structure because * without reference counting the input may be freed twice: * - by the layer which allocated it. * - by the layer to which would have been returned to. */ TODO return(NULL); } return(ret); } /** * xmlTextReaderLookupNamespace: * @reader: the xmlTextReaderPtr used * @prefix: the prefix whose namespace URI is to be resolved. To return * the default namespace, specify NULL * * Resolves a namespace prefix in the scope of the current element. * * Returns a string containing the namespace URI to which the prefix maps * or NULL in case of error. The string must be deallocated by the caller. */ xmlChar * xmlTextReaderLookupNamespace(xmlTextReaderPtr reader, const xmlChar *prefix) { xmlNsPtr ns; if (reader == NULL) return(NULL); if (reader->node == NULL) return(NULL); ns = xmlSearchNs(reader->node->doc, reader->node, prefix); if (ns == NULL) return(NULL); return(xmlStrdup(ns->href)); } /** * xmlTextReaderMoveToAttributeNo: * @reader: the xmlTextReaderPtr used * @no: the zero-based index of the attribute relative to the containing * element. * * Moves the position of the current instance to the attribute with * the specified index relative to the containing element. * * Returns 1 in case of success, -1 in case of error, 0 if not found */ int xmlTextReaderMoveToAttributeNo(xmlTextReaderPtr reader, int no) { int i; xmlAttrPtr cur; xmlNsPtr ns; if (reader == NULL) return(-1); if (reader->node == NULL) return(-1); /* TODO: handle the xmlDecl */ if (reader->node->type != XML_ELEMENT_NODE) return(-1); reader->curnode = NULL; ns = reader->node->nsDef; for (i = 0;(i < no) && (ns != NULL);i++) { ns = ns->next; } if (ns != NULL) { reader->curnode = (xmlNodePtr) ns; return(1); } cur = reader->node->properties; if (cur == NULL) return(0); for (;i < no;i++) { cur = cur->next; if (cur == NULL) return(0); } /* TODO walk the DTD if present */ reader->curnode = (xmlNodePtr) cur; return(1); } /** * xmlTextReaderMoveToAttribute: * @reader: the xmlTextReaderPtr used * @name: the qualified name of the attribute. * * Moves the position of the current instance to the attribute with * the specified qualified name. * * Returns 1 in case of success, -1 in case of error, 0 if not found */ int xmlTextReaderMoveToAttribute(xmlTextReaderPtr reader, const xmlChar *name) { xmlChar *prefix = NULL; xmlChar *localname; xmlNsPtr ns; xmlAttrPtr prop; if ((reader == NULL) || (name == NULL)) return(-1); if (reader->node == NULL) return(-1); /* TODO: handle the xmlDecl */ if (reader->node->type != XML_ELEMENT_NODE) return(0); localname = xmlSplitQName2(name, &prefix); if (localname == NULL) { /* * Namespace default decl */ if (xmlStrEqual(name, BAD_CAST "xmlns")) { ns = reader->node->nsDef; while (ns != NULL) { if (ns->prefix == NULL) { reader->curnode = (xmlNodePtr) ns; return(1); } ns = ns->next; } return(0); } prop = reader->node->properties; while (prop != NULL) { /* * One need to have * - same attribute names * - and the attribute carrying that namespace */ if ((xmlStrEqual(prop->name, name)) && ((prop->ns == NULL) || (prop->ns->prefix == NULL))) { reader->curnode = (xmlNodePtr) prop; return(1); } prop = prop->next; } return(0); } /* * Namespace default decl */ if (xmlStrEqual(prefix, BAD_CAST "xmlns")) { ns = reader->node->nsDef; while (ns != NULL) { if ((ns->prefix != NULL) && (xmlStrEqual(ns->prefix, localname))) { reader->curnode = (xmlNodePtr) ns; goto found; } ns = ns->next; } goto not_found; } prop = reader->node->properties; while (prop != NULL) { /* * One need to have * - same attribute names * - and the attribute carrying that namespace */ if ((xmlStrEqual(prop->name, localname)) && (prop->ns != NULL) && (xmlStrEqual(prop->ns->prefix, prefix))) { reader->curnode = (xmlNodePtr) prop; goto found; } prop = prop->next; } not_found: if (localname != NULL) xmlFree(localname); if (prefix != NULL) xmlFree(prefix); return(0); found: if (localname != NULL) xmlFree(localname); if (prefix != NULL) xmlFree(prefix); return(1); } /** * xmlTextReaderMoveToAttributeNs: * @reader: the xmlTextReaderPtr used * @localName: the local name of the attribute. * @namespaceURI: the namespace URI of the attribute. * * Moves the position of the current instance to the attribute with the * specified local name and namespace URI. * * Returns 1 in case of success, -1 in case of error, 0 if not found */ int xmlTextReaderMoveToAttributeNs(xmlTextReaderPtr reader, const xmlChar *localName, const xmlChar *namespaceURI) { xmlAttrPtr prop; xmlNodePtr node; xmlNsPtr ns; xmlChar *prefix = NULL; if ((reader == NULL) || (localName == NULL) || (namespaceURI == NULL)) return(-1); if (reader->node == NULL) return(-1); if (reader->node->type != XML_ELEMENT_NODE) return(0); node = reader->node; if (xmlStrEqual(namespaceURI, BAD_CAST "http://www.w3.org/2000/xmlns/")) { if (! xmlStrEqual(localName, BAD_CAST "xmlns")) { prefix = BAD_CAST localName; } ns = reader->node->nsDef; while (ns != NULL) { if ((prefix == NULL && ns->prefix == NULL) || ((ns->prefix != NULL) && (xmlStrEqual(ns->prefix, localName)))) { reader->curnode = (xmlNodePtr) ns; return(1); } ns = ns->next; } return(0); } prop = node->properties; while (prop != NULL) { /* * One need to have * - same attribute names * - and the attribute carrying that namespace */ if (xmlStrEqual(prop->name, localName) && ((prop->ns != NULL) && (xmlStrEqual(prop->ns->href, namespaceURI)))) { reader->curnode = (xmlNodePtr) prop; return(1); } prop = prop->next; } return(0); } /** * xmlTextReaderMoveToFirstAttribute: * @reader: the xmlTextReaderPtr used * * Moves the position of the current instance to the first attribute * associated with the current node. * * Returns 1 in case of success, -1 in case of error, 0 if not found */ int xmlTextReaderMoveToFirstAttribute(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); if (reader->node == NULL) return(-1); if (reader->node->type != XML_ELEMENT_NODE) return(0); if (reader->node->nsDef != NULL) { reader->curnode = (xmlNodePtr) reader->node->nsDef; return(1); } if (reader->node->properties != NULL) { reader->curnode = (xmlNodePtr) reader->node->properties; return(1); } return(0); } /** * xmlTextReaderMoveToNextAttribute: * @reader: the xmlTextReaderPtr used * * Moves the position of the current instance to the next attribute * associated with the current node. * * Returns 1 in case of success, -1 in case of error, 0 if not found */ int xmlTextReaderMoveToNextAttribute(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); if (reader->node == NULL) return(-1); if (reader->node->type != XML_ELEMENT_NODE) return(0); if (reader->curnode == NULL) return(xmlTextReaderMoveToFirstAttribute(reader)); if (reader->curnode->type == XML_NAMESPACE_DECL) { xmlNsPtr ns = (xmlNsPtr) reader->curnode; if (ns->next != NULL) { reader->curnode = (xmlNodePtr) ns->next; return(1); } if (reader->node->properties != NULL) { reader->curnode = (xmlNodePtr) reader->node->properties; return(1); } return(0); } else if ((reader->curnode->type == XML_ATTRIBUTE_NODE) && (reader->curnode->next != NULL)) { reader->curnode = reader->curnode->next; return(1); } return(0); } /** * xmlTextReaderMoveToElement: * @reader: the xmlTextReaderPtr used * * Moves the position of the current instance to the node that * contains the current Attribute node. * * Returns 1 in case of success, -1 in case of error, 0 if not moved */ int xmlTextReaderMoveToElement(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); if (reader->node == NULL) return(-1); if (reader->node->type != XML_ELEMENT_NODE) return(0); if (reader->curnode != NULL) { reader->curnode = NULL; return(1); } return(0); } /** * xmlTextReaderReadAttributeValue: * @reader: the xmlTextReaderPtr used * * Parses an attribute value into one or more Text and EntityReference nodes. * * Returns 1 in case of success, 0 if the reader was not positionned on an * ttribute node or all the attribute values have been read, or -1 * in case of error. */ int xmlTextReaderReadAttributeValue(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); if (reader->node == NULL) return(-1); if (reader->curnode == NULL) return(0); if (reader->curnode->type == XML_ATTRIBUTE_NODE) { if (reader->curnode->children == NULL) return(0); reader->curnode = reader->curnode->children; } else if (reader->curnode->type == XML_NAMESPACE_DECL) { xmlNsPtr ns = (xmlNsPtr) reader->curnode; if (reader->faketext == NULL) { reader->faketext = xmlNewDocText(reader->node->doc, ns->href); } else { if ((reader->faketext->content != NULL) && (reader->faketext->content != (xmlChar *) &(reader->faketext->properties))) xmlFree(reader->faketext->content); reader->faketext->content = xmlStrdup(ns->href); } reader->curnode = reader->faketext; } else { if (reader->curnode->next == NULL) return(0); reader->curnode = reader->curnode->next; } return(1); } /** * xmlTextReaderConstEncoding: * @reader: the xmlTextReaderPtr used * * Determine the encoding of the document being read. * * Returns a string containing the encoding of the document or NULL in * case of error. The string is deallocated with the reader. */ const xmlChar * xmlTextReaderConstEncoding(xmlTextReaderPtr reader) { xmlDocPtr doc = NULL; if (reader == NULL) return(NULL); if (reader->doc != NULL) doc = reader->doc; else if (reader->ctxt != NULL) doc = reader->ctxt->myDoc; if (doc == NULL) return(NULL); if (doc->encoding == NULL) return(NULL); else return(CONSTSTR(doc->encoding)); } /************************************************************************ * * * Acces API to the current node * * * ************************************************************************/ /** * xmlTextReaderAttributeCount: * @reader: the xmlTextReaderPtr used * * Provides the number of attributes of the current node * * Returns 0 i no attributes, -1 in case of error or the attribute count */ int xmlTextReaderAttributeCount(xmlTextReaderPtr reader) { int ret; xmlAttrPtr attr; xmlNsPtr ns; xmlNodePtr node; if (reader == NULL) return(-1); if (reader->node == NULL) return(0); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; if (node->type != XML_ELEMENT_NODE) return(0); if ((reader->state == XML_TEXTREADER_END) || (reader->state == XML_TEXTREADER_BACKTRACK)) return(0); ret = 0; attr = node->properties; while (attr != NULL) { ret++; attr = attr->next; } ns = node->nsDef; while (ns != NULL) { ret++; ns = ns->next; } return(ret); } /** * xmlTextReaderNodeType: * @reader: the xmlTextReaderPtr used * * Get the node type of the current node * Reference: * http://www.gnu.org/software/dotgnu/pnetlib-doc/System/Xml/XmlNodeType.html * * Returns the xmlNodeType of the current node or -1 in case of error */ int xmlTextReaderNodeType(xmlTextReaderPtr reader) { xmlNodePtr node; if (reader == NULL) return(-1); if (reader->node == NULL) return(XML_READER_TYPE_NONE); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; switch (node->type) { case XML_ELEMENT_NODE: if ((reader->state == XML_TEXTREADER_END) || (reader->state == XML_TEXTREADER_BACKTRACK)) return(XML_READER_TYPE_END_ELEMENT); return(XML_READER_TYPE_ELEMENT); case XML_NAMESPACE_DECL: case XML_ATTRIBUTE_NODE: return(XML_READER_TYPE_ATTRIBUTE); case XML_TEXT_NODE: if (xmlIsBlankNode(reader->node)) { if (xmlNodeGetSpacePreserve(reader->node)) return(XML_READER_TYPE_SIGNIFICANT_WHITESPACE); else return(XML_READER_TYPE_WHITESPACE); } else { return(XML_READER_TYPE_TEXT); } case XML_CDATA_SECTION_NODE: return(XML_READER_TYPE_CDATA); case XML_ENTITY_REF_NODE: return(XML_READER_TYPE_ENTITY_REFERENCE); case XML_ENTITY_NODE: return(XML_READER_TYPE_ENTITY); case XML_PI_NODE: return(XML_READER_TYPE_PROCESSING_INSTRUCTION); case XML_COMMENT_NODE: return(XML_READER_TYPE_COMMENT); case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: #ifdef LIBXML_DOCB_ENABLED case XML_DOCB_DOCUMENT_NODE: #endif return(XML_READER_TYPE_DOCUMENT); case XML_DOCUMENT_FRAG_NODE: return(XML_READER_TYPE_DOCUMENT_FRAGMENT); case XML_NOTATION_NODE: return(XML_READER_TYPE_NOTATION); case XML_DOCUMENT_TYPE_NODE: case XML_DTD_NODE: return(XML_READER_TYPE_DOCUMENT_TYPE); case XML_ELEMENT_DECL: case XML_ATTRIBUTE_DECL: case XML_ENTITY_DECL: case XML_XINCLUDE_START: case XML_XINCLUDE_END: return(XML_READER_TYPE_NONE); } return(-1); } /** * xmlTextReaderIsEmptyElement: * @reader: the xmlTextReaderPtr used * * Check if the current node is empty * * Returns 1 if empty, 0 if not and -1 in case of error */ int xmlTextReaderIsEmptyElement(xmlTextReaderPtr reader) { if ((reader == NULL) || (reader->node == NULL)) return(-1); if (reader->node->type != XML_ELEMENT_NODE) return(0); if (reader->curnode != NULL) return(0); if (reader->node->children != NULL) return(0); if (reader->state == XML_TEXTREADER_END) return(0); if (reader->doc != NULL) return(1); #ifdef LIBXML_XINCLUDE_ENABLED if (reader->in_xinclude > 0) return(1); #endif return((reader->node->extra & NODE_IS_EMPTY) != 0); } /** * xmlTextReaderLocalName: * @reader: the xmlTextReaderPtr used * * The local name of the node. * * Returns the local name or NULL if not available, * if non NULL it need to be freed by the caller. */ xmlChar * xmlTextReaderLocalName(xmlTextReaderPtr reader) { xmlNodePtr node; if ((reader == NULL) || (reader->node == NULL)) return(NULL); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; if (node->type == XML_NAMESPACE_DECL) { xmlNsPtr ns = (xmlNsPtr) node; if (ns->prefix == NULL) return(xmlStrdup(BAD_CAST "xmlns")); else return(xmlStrdup(ns->prefix)); } if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(xmlTextReaderName(reader)); return(xmlStrdup(node->name)); } /** * xmlTextReaderConstLocalName: * @reader: the xmlTextReaderPtr used * * The local name of the node. * * Returns the local name or NULL if not available, the * string will be deallocated with the reader. */ const xmlChar * xmlTextReaderConstLocalName(xmlTextReaderPtr reader) { xmlNodePtr node; if ((reader == NULL) || (reader->node == NULL)) return(NULL); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; if (node->type == XML_NAMESPACE_DECL) { xmlNsPtr ns = (xmlNsPtr) node; if (ns->prefix == NULL) return(CONSTSTR(BAD_CAST "xmlns")); else return(ns->prefix); } if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(xmlTextReaderConstName(reader)); return(node->name); } /** * xmlTextReaderName: * @reader: the xmlTextReaderPtr used * * The qualified name of the node, equal to Prefix :LocalName. * * Returns the local name or NULL if not available, * if non NULL it need to be freed by the caller. */ xmlChar * xmlTextReaderName(xmlTextReaderPtr reader) { xmlNodePtr node; xmlChar *ret; if ((reader == NULL) || (reader->node == NULL)) return(NULL); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; switch (node->type) { case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: if ((node->ns == NULL) || (node->ns->prefix == NULL)) return(xmlStrdup(node->name)); ret = xmlStrdup(node->ns->prefix); ret = xmlStrcat(ret, BAD_CAST ":"); ret = xmlStrcat(ret, node->name); return(ret); case XML_TEXT_NODE: return(xmlStrdup(BAD_CAST "#text")); case XML_CDATA_SECTION_NODE: return(xmlStrdup(BAD_CAST "#cdata-section")); case XML_ENTITY_NODE: case XML_ENTITY_REF_NODE: return(xmlStrdup(node->name)); case XML_PI_NODE: return(xmlStrdup(node->name)); case XML_COMMENT_NODE: return(xmlStrdup(BAD_CAST "#comment")); case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: #ifdef LIBXML_DOCB_ENABLED case XML_DOCB_DOCUMENT_NODE: #endif return(xmlStrdup(BAD_CAST "#document")); case XML_DOCUMENT_FRAG_NODE: return(xmlStrdup(BAD_CAST "#document-fragment")); case XML_NOTATION_NODE: return(xmlStrdup(node->name)); case XML_DOCUMENT_TYPE_NODE: case XML_DTD_NODE: return(xmlStrdup(node->name)); case XML_NAMESPACE_DECL: { xmlNsPtr ns = (xmlNsPtr) node; ret = xmlStrdup(BAD_CAST "xmlns"); if (ns->prefix == NULL) return(ret); ret = xmlStrcat(ret, BAD_CAST ":"); ret = xmlStrcat(ret, ns->prefix); return(ret); } case XML_ELEMENT_DECL: case XML_ATTRIBUTE_DECL: case XML_ENTITY_DECL: case XML_XINCLUDE_START: case XML_XINCLUDE_END: return(NULL); } return(NULL); } /** * xmlTextReaderConstName: * @reader: the xmlTextReaderPtr used * * The qualified name of the node, equal to Prefix :LocalName. * * Returns the local name or NULL if not available, the string is * deallocated with the reader. */ const xmlChar * xmlTextReaderConstName(xmlTextReaderPtr reader) { xmlNodePtr node; if ((reader == NULL) || (reader->node == NULL)) return(NULL); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; switch (node->type) { case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: if ((node->ns == NULL) || (node->ns->prefix == NULL)) return(node->name); return(CONSTQSTR(node->ns->prefix, node->name)); case XML_TEXT_NODE: return(CONSTSTR(BAD_CAST "#text")); case XML_CDATA_SECTION_NODE: return(CONSTSTR(BAD_CAST "#cdata-section")); case XML_ENTITY_NODE: case XML_ENTITY_REF_NODE: return(CONSTSTR(node->name)); case XML_PI_NODE: return(CONSTSTR(node->name)); case XML_COMMENT_NODE: return(CONSTSTR(BAD_CAST "#comment")); case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: #ifdef LIBXML_DOCB_ENABLED case XML_DOCB_DOCUMENT_NODE: #endif return(CONSTSTR(BAD_CAST "#document")); case XML_DOCUMENT_FRAG_NODE: return(CONSTSTR(BAD_CAST "#document-fragment")); case XML_NOTATION_NODE: return(CONSTSTR(node->name)); case XML_DOCUMENT_TYPE_NODE: case XML_DTD_NODE: return(CONSTSTR(node->name)); case XML_NAMESPACE_DECL: { xmlNsPtr ns = (xmlNsPtr) node; if (ns->prefix == NULL) return(CONSTSTR(BAD_CAST "xmlns")); return(CONSTQSTR(BAD_CAST "xmlns", ns->prefix)); } case XML_ELEMENT_DECL: case XML_ATTRIBUTE_DECL: case XML_ENTITY_DECL: case XML_XINCLUDE_START: case XML_XINCLUDE_END: return(NULL); } return(NULL); } /** * xmlTextReaderPrefix: * @reader: the xmlTextReaderPtr used * * A shorthand reference to the namespace associated with the node. * * Returns the prefix or NULL if not available, * if non NULL it need to be freed by the caller. */ xmlChar * xmlTextReaderPrefix(xmlTextReaderPtr reader) { xmlNodePtr node; if ((reader == NULL) || (reader->node == NULL)) return(NULL); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; if (node->type == XML_NAMESPACE_DECL) { xmlNsPtr ns = (xmlNsPtr) node; if (ns->prefix == NULL) return(NULL); return(xmlStrdup(BAD_CAST "xmlns")); } if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(NULL); if ((node->ns != NULL) && (node->ns->prefix != NULL)) return(xmlStrdup(node->ns->prefix)); return(NULL); } /** * xmlTextReaderConstPrefix: * @reader: the xmlTextReaderPtr used * * A shorthand reference to the namespace associated with the node. * * Returns the prefix or NULL if not available, the string is deallocated * with the reader. */ const xmlChar * xmlTextReaderConstPrefix(xmlTextReaderPtr reader) { xmlNodePtr node; if ((reader == NULL) || (reader->node == NULL)) return(NULL); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; if (node->type == XML_NAMESPACE_DECL) { xmlNsPtr ns = (xmlNsPtr) node; if (ns->prefix == NULL) return(NULL); return(CONSTSTR(BAD_CAST "xmlns")); } if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(NULL); if ((node->ns != NULL) && (node->ns->prefix != NULL)) return(CONSTSTR(node->ns->prefix)); return(NULL); } /** * xmlTextReaderNamespaceUri: * @reader: the xmlTextReaderPtr used * * The URI defining the namespace associated with the node. * * Returns the namespace URI or NULL if not available, * if non NULL it need to be freed by the caller. */ xmlChar * xmlTextReaderNamespaceUri(xmlTextReaderPtr reader) { xmlNodePtr node; if ((reader == NULL) || (reader->node == NULL)) return(NULL); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; if (node->type == XML_NAMESPACE_DECL) return(xmlStrdup(BAD_CAST "http://www.w3.org/2000/xmlns/")); if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(NULL); if (node->ns != NULL) return(xmlStrdup(node->ns->href)); return(NULL); } /** * xmlTextReaderConstNamespaceUri: * @reader: the xmlTextReaderPtr used * * The URI defining the namespace associated with the node. * * Returns the namespace URI or NULL if not available, the string * will be deallocated with the reader */ const xmlChar * xmlTextReaderConstNamespaceUri(xmlTextReaderPtr reader) { xmlNodePtr node; if ((reader == NULL) || (reader->node == NULL)) return(NULL); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; if (node->type == XML_NAMESPACE_DECL) return(CONSTSTR(BAD_CAST "http://www.w3.org/2000/xmlns/")); if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(NULL); if (node->ns != NULL) return(CONSTSTR(node->ns->href)); return(NULL); } /** * xmlTextReaderBaseUri: * @reader: the xmlTextReaderPtr used * * The base URI of the node. * * Returns the base URI or NULL if not available, * if non NULL it need to be freed by the caller. */ xmlChar * xmlTextReaderBaseUri(xmlTextReaderPtr reader) { if ((reader == NULL) || (reader->node == NULL)) return(NULL); return(xmlNodeGetBase(NULL, reader->node)); } /** * xmlTextReaderConstBaseUri: * @reader: the xmlTextReaderPtr used * * The base URI of the node. * * Returns the base URI or NULL if not available, the string * will be deallocated with the reader */ const xmlChar * xmlTextReaderConstBaseUri(xmlTextReaderPtr reader) { xmlChar *tmp; const xmlChar *ret; if ((reader == NULL) || (reader->node == NULL)) return(NULL); tmp = xmlNodeGetBase(NULL, reader->node); if (tmp == NULL) return(NULL); ret = CONSTSTR(tmp); xmlFree(tmp); return(ret); } /** * xmlTextReaderDepth: * @reader: the xmlTextReaderPtr used * * The depth of the node in the tree. * * Returns the depth or -1 in case of error */ int xmlTextReaderDepth(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); if (reader->node == NULL) return(0); if (reader->curnode != NULL) { if ((reader->curnode->type == XML_ATTRIBUTE_NODE) || (reader->curnode->type == XML_NAMESPACE_DECL)) return(reader->depth + 1); return(reader->depth + 2); } return(reader->depth); } /** * xmlTextReaderHasAttributes: * @reader: the xmlTextReaderPtr used * * Whether the node has attributes. * * Returns 1 if true, 0 if false, and -1 in case or error */ int xmlTextReaderHasAttributes(xmlTextReaderPtr reader) { xmlNodePtr node; if (reader == NULL) return(-1); if (reader->node == NULL) return(0); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; if ((node->type == XML_ELEMENT_NODE) && ((node->properties != NULL) || (node->nsDef != NULL))) return(1); /* TODO: handle the xmlDecl */ return(0); } /** * xmlTextReaderHasValue: * @reader: the xmlTextReaderPtr used * * Whether the node can have a text value. * * Returns 1 if true, 0 if false, and -1 in case or error */ int xmlTextReaderHasValue(xmlTextReaderPtr reader) { xmlNodePtr node; if (reader == NULL) return(-1); if (reader->node == NULL) return(0); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; switch (node->type) { case XML_ATTRIBUTE_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: case XML_NAMESPACE_DECL: return(1); default: break; } return(0); } /** * xmlTextReaderValue: * @reader: the xmlTextReaderPtr used * * Provides the text value of the node if present * * Returns the string or NULL if not available. The result must be deallocated * with xmlFree() */ xmlChar * xmlTextReaderValue(xmlTextReaderPtr reader) { xmlNodePtr node; if (reader == NULL) return(NULL); if (reader->node == NULL) return(NULL); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; switch (node->type) { case XML_NAMESPACE_DECL: return(xmlStrdup(((xmlNsPtr) node)->href)); case XML_ATTRIBUTE_NODE:{ xmlAttrPtr attr = (xmlAttrPtr) node; if (attr->parent != NULL) return (xmlNodeListGetString (attr->parent->doc, attr->children, 1)); else return (xmlNodeListGetString(NULL, attr->children, 1)); break; } case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: if (node->content != NULL) return (xmlStrdup(node->content)); default: break; } return(NULL); } /** * xmlTextReaderConstValue: * @reader: the xmlTextReaderPtr used * * Provides the text value of the node if present * * Returns the string or NULL if not available. The result will be * deallocated on the next Read() operation. */ const xmlChar * xmlTextReaderConstValue(xmlTextReaderPtr reader) { xmlNodePtr node; if (reader == NULL) return(NULL); if (reader->node == NULL) return(NULL); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; switch (node->type) { case XML_NAMESPACE_DECL: return(((xmlNsPtr) node)->href); case XML_ATTRIBUTE_NODE:{ xmlAttrPtr attr = (xmlAttrPtr) node; const xmlChar *ret; if ((attr->children != NULL) && (attr->children->type == XML_TEXT_NODE) && (attr->children->next == NULL)) return(attr->children->content); else { if (reader->buffer == NULL) { reader->buffer = xmlBufCreateSize(100); if (reader->buffer == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlTextReaderSetup : malloc failed\n"); return (NULL); } xmlBufSetAllocationScheme(reader->buffer, XML_BUFFER_ALLOC_BOUNDED); } else xmlBufEmpty(reader->buffer); xmlBufGetNodeContent(reader->buffer, node); ret = xmlBufContent(reader->buffer); if (ret == NULL) { /* error on the buffer best to reallocate */ xmlBufFree(reader->buffer); reader->buffer = xmlBufCreateSize(100); xmlBufSetAllocationScheme(reader->buffer, XML_BUFFER_ALLOC_BOUNDED); ret = BAD_CAST ""; } return(ret); } break; } case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: return(node->content); default: break; } return(NULL); } /** * xmlTextReaderIsDefault: * @reader: the xmlTextReaderPtr used * * Whether an Attribute node was generated from the default value * defined in the DTD or schema. * * Returns 0 if not defaulted, 1 if defaulted, and -1 in case of error */ int xmlTextReaderIsDefault(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); return(0); } /** * xmlTextReaderQuoteChar: * @reader: the xmlTextReaderPtr used * * The quotation mark character used to enclose the value of an attribute. * * Returns " or ' and -1 in case of error */ int xmlTextReaderQuoteChar(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); /* TODO maybe lookup the attribute value for " first */ return((int) '"'); } /** * xmlTextReaderXmlLang: * @reader: the xmlTextReaderPtr used * * The xml:lang scope within which the node resides. * * Returns the xml:lang value or NULL if none exists., * if non NULL it need to be freed by the caller. */ xmlChar * xmlTextReaderXmlLang(xmlTextReaderPtr reader) { if (reader == NULL) return(NULL); if (reader->node == NULL) return(NULL); return(xmlNodeGetLang(reader->node)); } /** * xmlTextReaderConstXmlLang: * @reader: the xmlTextReaderPtr used * * The xml:lang scope within which the node resides. * * Returns the xml:lang value or NULL if none exists. */ const xmlChar * xmlTextReaderConstXmlLang(xmlTextReaderPtr reader) { xmlChar *tmp; const xmlChar *ret; if (reader == NULL) return(NULL); if (reader->node == NULL) return(NULL); tmp = xmlNodeGetLang(reader->node); if (tmp == NULL) return(NULL); ret = CONSTSTR(tmp); xmlFree(tmp); return(ret); } /** * xmlTextReaderConstString: * @reader: the xmlTextReaderPtr used * @str: the string to intern. * * Get an interned string from the reader, allows for example to * speedup string name comparisons * * Returns an interned copy of the string or NULL in case of error. The * string will be deallocated with the reader. */ const xmlChar * xmlTextReaderConstString(xmlTextReaderPtr reader, const xmlChar *str) { if (reader == NULL) return(NULL); return(CONSTSTR(str)); } /** * xmlTextReaderNormalization: * @reader: the xmlTextReaderPtr used * * The value indicating whether to normalize white space and attribute values. * Since attribute value and end of line normalizations are a MUST in the XML * specification only the value true is accepted. The broken bahaviour of * accepting out of range character entities like � is of course not * supported either. * * Returns 1 or -1 in case of error. */ int xmlTextReaderNormalization(xmlTextReaderPtr reader) { if (reader == NULL) return(-1); return(1); } /************************************************************************ * * * Extensions to the base APIs * * * ************************************************************************/ /** * xmlTextReaderSetParserProp: * @reader: the xmlTextReaderPtr used * @prop: the xmlParserProperties to set * @value: usually 0 or 1 to (de)activate it * * Change the parser processing behaviour by changing some of its internal * properties. Note that some properties can only be changed before any * read has been done. * * Returns 0 if the call was successful, or -1 in case of error */ int xmlTextReaderSetParserProp(xmlTextReaderPtr reader, int prop, int value) { xmlParserProperties p = (xmlParserProperties) prop; xmlParserCtxtPtr ctxt; if ((reader == NULL) || (reader->ctxt == NULL)) return(-1); ctxt = reader->ctxt; switch (p) { case XML_PARSER_LOADDTD: if (value != 0) { if (ctxt->loadsubset == 0) { if (reader->mode != XML_TEXTREADER_MODE_INITIAL) return(-1); ctxt->loadsubset = XML_DETECT_IDS; } } else { ctxt->loadsubset = 0; } return(0); case XML_PARSER_DEFAULTATTRS: if (value != 0) { ctxt->loadsubset |= XML_COMPLETE_ATTRS; } else { if (ctxt->loadsubset & XML_COMPLETE_ATTRS) ctxt->loadsubset -= XML_COMPLETE_ATTRS; } return(0); case XML_PARSER_VALIDATE: if (value != 0) { ctxt->validate = 1; reader->validate = XML_TEXTREADER_VALIDATE_DTD; } else { ctxt->validate = 0; } return(0); case XML_PARSER_SUBST_ENTITIES: if (value != 0) { ctxt->replaceEntities = 1; } else { ctxt->replaceEntities = 0; } return(0); } return(-1); } /** * xmlTextReaderGetParserProp: * @reader: the xmlTextReaderPtr used * @prop: the xmlParserProperties to get * * Read the parser internal property. * * Returns the value, usually 0 or 1, or -1 in case of error. */ int xmlTextReaderGetParserProp(xmlTextReaderPtr reader, int prop) { xmlParserProperties p = (xmlParserProperties) prop; xmlParserCtxtPtr ctxt; if ((reader == NULL) || (reader->ctxt == NULL)) return(-1); ctxt = reader->ctxt; switch (p) { case XML_PARSER_LOADDTD: if ((ctxt->loadsubset != 0) || (ctxt->validate != 0)) return(1); return(0); case XML_PARSER_DEFAULTATTRS: if (ctxt->loadsubset & XML_COMPLETE_ATTRS) return(1); return(0); case XML_PARSER_VALIDATE: return(reader->validate); case XML_PARSER_SUBST_ENTITIES: return(ctxt->replaceEntities); } return(-1); } /** * xmlTextReaderGetParserLineNumber: * @reader: the user data (XML reader context) * * Provide the line number of the current parsing point. * * Returns an int or 0 if not available */ int xmlTextReaderGetParserLineNumber(xmlTextReaderPtr reader) { if ((reader == NULL) || (reader->ctxt == NULL) || (reader->ctxt->input == NULL)) { return (0); } return (reader->ctxt->input->line); } /** * xmlTextReaderGetParserColumnNumber: * @reader: the user data (XML reader context) * * Provide the column number of the current parsing point. * * Returns an int or 0 if not available */ int xmlTextReaderGetParserColumnNumber(xmlTextReaderPtr reader) { if ((reader == NULL) || (reader->ctxt == NULL) || (reader->ctxt->input == NULL)) { return (0); } return (reader->ctxt->input->col); } /** * xmlTextReaderCurrentNode: * @reader: the xmlTextReaderPtr used * * Hacking interface allowing to get the xmlNodePtr correponding to the * current node being accessed by the xmlTextReader. This is dangerous * because the underlying node may be destroyed on the next Reads. * * Returns the xmlNodePtr or NULL in case of error. */ xmlNodePtr xmlTextReaderCurrentNode(xmlTextReaderPtr reader) { if (reader == NULL) return(NULL); if (reader->curnode != NULL) return(reader->curnode); return(reader->node); } /** * xmlTextReaderPreserve: * @reader: the xmlTextReaderPtr used * * This tells the XML Reader to preserve the current node. * The caller must also use xmlTextReaderCurrentDoc() to * keep an handle on the resulting document once parsing has finished * * Returns the xmlNodePtr or NULL in case of error. */ xmlNodePtr xmlTextReaderPreserve(xmlTextReaderPtr reader) { xmlNodePtr cur, parent; if (reader == NULL) return(NULL); if (reader->curnode != NULL) cur = reader->curnode; else cur = reader->node; if (cur == NULL) return(NULL); if ((cur->type != XML_DOCUMENT_NODE) && (cur->type != XML_DTD_NODE)) { cur->extra |= NODE_IS_PRESERVED; cur->extra |= NODE_IS_SPRESERVED; } reader->preserves++; parent = cur->parent;; while (parent != NULL) { if (parent->type == XML_ELEMENT_NODE) parent->extra |= NODE_IS_PRESERVED; parent = parent->parent; } return(cur); } #ifdef LIBXML_PATTERN_ENABLED /** * xmlTextReaderPreservePattern: * @reader: the xmlTextReaderPtr used * @pattern: an XPath subset pattern * @namespaces: the prefix definitions, array of [URI, prefix] or NULL * * This tells the XML Reader to preserve all nodes matched by the * pattern. The caller must also use xmlTextReaderCurrentDoc() to * keep an handle on the resulting document once parsing has finished * * Returns a non-negative number in case of success and -1 in case of error */ int xmlTextReaderPreservePattern(xmlTextReaderPtr reader, const xmlChar *pattern, const xmlChar **namespaces) { xmlPatternPtr comp; if ((reader == NULL) || (pattern == NULL)) return(-1); comp = xmlPatterncompile(pattern, reader->dict, 0, namespaces); if (comp == NULL) return(-1); if (reader->patternMax <= 0) { reader->patternMax = 4; reader->patternTab = (xmlPatternPtr *) xmlMalloc(reader->patternMax * sizeof(reader->patternTab[0])); if (reader->patternTab == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlMalloc failed !\n"); return (-1); } } if (reader->patternNr >= reader->patternMax) { xmlPatternPtr *tmp; reader->patternMax *= 2; tmp = (xmlPatternPtr *) xmlRealloc(reader->patternTab, reader->patternMax * sizeof(reader->patternTab[0])); if (tmp == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlRealloc failed !\n"); reader->patternMax /= 2; return (-1); } reader->patternTab = tmp; } reader->patternTab[reader->patternNr] = comp; return(reader->patternNr++); } #endif /** * xmlTextReaderCurrentDoc: * @reader: the xmlTextReaderPtr used * * Hacking interface allowing to get the xmlDocPtr correponding to the * current document being accessed by the xmlTextReader. * NOTE: as a result of this call, the reader will not destroy the * associated XML document and calling xmlFreeDoc() on the result * is needed once the reader parsing has finished. * * Returns the xmlDocPtr or NULL in case of error. */ xmlDocPtr xmlTextReaderCurrentDoc(xmlTextReaderPtr reader) { if (reader == NULL) return(NULL); if (reader->doc != NULL) return(reader->doc); if ((reader->ctxt == NULL) || (reader->ctxt->myDoc == NULL)) return(NULL); reader->preserve = 1; return(reader->ctxt->myDoc); } #ifdef LIBXML_SCHEMAS_ENABLED static char *xmlTextReaderBuildMessage(const char *msg, va_list ap) LIBXML_ATTR_FORMAT(1,0); static void XMLCDECL xmlTextReaderValidityError(void *ctxt, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3); static void XMLCDECL xmlTextReaderValidityWarning(void *ctxt, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3); static void XMLCDECL xmlTextReaderValidityErrorRelay(void *ctx, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3); static void XMLCDECL xmlTextReaderValidityWarningRelay(void *ctx, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3); static void XMLCDECL xmlTextReaderValidityErrorRelay(void *ctx, const char *msg, ...) { xmlTextReaderPtr reader = (xmlTextReaderPtr) ctx; char *str; va_list ap; va_start(ap, msg); str = xmlTextReaderBuildMessage(msg, ap); if (!reader->errorFunc) { xmlTextReaderValidityError(ctx, "%s", str); } else { reader->errorFunc(reader->errorFuncArg, str, XML_PARSER_SEVERITY_VALIDITY_ERROR, NULL /* locator */ ); } if (str != NULL) xmlFree(str); va_end(ap); } static void XMLCDECL xmlTextReaderValidityWarningRelay(void *ctx, const char *msg, ...) { xmlTextReaderPtr reader = (xmlTextReaderPtr) ctx; char *str; va_list ap; va_start(ap, msg); str = xmlTextReaderBuildMessage(msg, ap); if (!reader->errorFunc) { xmlTextReaderValidityWarning(ctx, "%s", str); } else { reader->errorFunc(reader->errorFuncArg, str, XML_PARSER_SEVERITY_VALIDITY_WARNING, NULL /* locator */ ); } if (str != NULL) xmlFree(str); va_end(ap); } static void xmlTextReaderStructuredError(void *ctxt, xmlErrorPtr error); static void xmlTextReaderValidityStructuredRelay(void *userData, xmlErrorPtr error) { xmlTextReaderPtr reader = (xmlTextReaderPtr) userData; if (reader->sErrorFunc) { reader->sErrorFunc(reader->errorFuncArg, error); } else { xmlTextReaderStructuredError(reader, error); } } /** * xmlTextReaderRelaxNGSetSchema: * @reader: the xmlTextReaderPtr used * @schema: a precompiled RelaxNG schema * * Use RelaxNG to validate the document as it is processed. * Activation is only possible before the first Read(). * if @schema is NULL, then RelaxNG validation is desactivated. @ The @schema should not be freed until the reader is deallocated * or its use has been deactivated. * * Returns 0 in case the RelaxNG validation could be (des)activated and * -1 in case of error. */ int xmlTextReaderRelaxNGSetSchema(xmlTextReaderPtr reader, xmlRelaxNGPtr schema) { if (reader == NULL) return(-1); if (schema == NULL) { if (reader->rngSchemas != NULL) { xmlRelaxNGFree(reader->rngSchemas); reader->rngSchemas = NULL; } if (reader->rngValidCtxt != NULL) { if (! reader->rngPreserveCtxt) xmlRelaxNGFreeValidCtxt(reader->rngValidCtxt); reader->rngValidCtxt = NULL; } reader->rngPreserveCtxt = 0; return(0); } if (reader->mode != XML_TEXTREADER_MODE_INITIAL) return(-1); if (reader->rngSchemas != NULL) { xmlRelaxNGFree(reader->rngSchemas); reader->rngSchemas = NULL; } if (reader->rngValidCtxt != NULL) { if (! reader->rngPreserveCtxt) xmlRelaxNGFreeValidCtxt(reader->rngValidCtxt); reader->rngValidCtxt = NULL; } reader->rngPreserveCtxt = 0; reader->rngValidCtxt = xmlRelaxNGNewValidCtxt(schema); if (reader->rngValidCtxt == NULL) return(-1); if (reader->errorFunc != NULL) { xmlRelaxNGSetValidErrors(reader->rngValidCtxt, xmlTextReaderValidityErrorRelay, xmlTextReaderValidityWarningRelay, reader); } if (reader->sErrorFunc != NULL) { xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, xmlTextReaderValidityStructuredRelay, reader); } reader->rngValidErrors = 0; reader->rngFullNode = NULL; reader->validate = XML_TEXTREADER_VALIDATE_RNG; return(0); } /** * xmlTextReaderLocator: * @ctx: the xmlTextReaderPtr used * @file: returned file information * @line: returned line information * * Internal locator function for the readers * * Returns 0 in case the Schema validation could be (des)activated and * -1 in case of error. */ static int xmlTextReaderLocator(void *ctx, const char **file, unsigned long *line) { xmlTextReaderPtr reader; if ((ctx == NULL) || ((file == NULL) && (line == NULL))) return(-1); if (file != NULL) *file = NULL; if (line != NULL) *line = 0; reader = (xmlTextReaderPtr) ctx; if ((reader->ctxt != NULL) && (reader->ctxt->input != NULL)) { if (file != NULL) *file = reader->ctxt->input->filename; if (line != NULL) *line = reader->ctxt->input->line; return(0); } if (reader->node != NULL) { long res; int ret = 0; if (line != NULL) { res = xmlGetLineNo(reader->node); if (res > 0) *line = (unsigned long) res; else ret = -1; } if (file != NULL) { xmlDocPtr doc = reader->node->doc; if ((doc != NULL) && (doc->URL != NULL)) *file = (const char *) doc->URL; else ret = -1; } return(ret); } return(-1); } /** * xmlTextReaderSetSchema: * @reader: the xmlTextReaderPtr used * @schema: a precompiled Schema schema * * Use XSD Schema to validate the document as it is processed. * Activation is only possible before the first Read(). * if @schema is NULL, then Schema validation is desactivated. @ The @schema should not be freed until the reader is deallocated * or its use has been deactivated. * * Returns 0 in case the Schema validation could be (des)activated and * -1 in case of error. */ int xmlTextReaderSetSchema(xmlTextReaderPtr reader, xmlSchemaPtr schema) { if (reader == NULL) return(-1); if (schema == NULL) { if (reader->xsdPlug != NULL) { xmlSchemaSAXUnplug(reader->xsdPlug); reader->xsdPlug = NULL; } if (reader->xsdValidCtxt != NULL) { if (! reader->xsdPreserveCtxt) xmlSchemaFreeValidCtxt(reader->xsdValidCtxt); reader->xsdValidCtxt = NULL; } reader->xsdPreserveCtxt = 0; if (reader->xsdSchemas != NULL) { xmlSchemaFree(reader->xsdSchemas); reader->xsdSchemas = NULL; } return(0); } if (reader->mode != XML_TEXTREADER_MODE_INITIAL) return(-1); if (reader->xsdPlug != NULL) { xmlSchemaSAXUnplug(reader->xsdPlug); reader->xsdPlug = NULL; } if (reader->xsdValidCtxt != NULL) { if (! reader->xsdPreserveCtxt) xmlSchemaFreeValidCtxt(reader->xsdValidCtxt); reader->xsdValidCtxt = NULL; } reader->xsdPreserveCtxt = 0; if (reader->xsdSchemas != NULL) { xmlSchemaFree(reader->xsdSchemas); reader->xsdSchemas = NULL; } reader->xsdValidCtxt = xmlSchemaNewValidCtxt(schema); if (reader->xsdValidCtxt == NULL) { xmlSchemaFree(reader->xsdSchemas); reader->xsdSchemas = NULL; return(-1); } reader->xsdPlug = xmlSchemaSAXPlug(reader->xsdValidCtxt, &(reader->ctxt->sax), &(reader->ctxt->userData)); if (reader->xsdPlug == NULL) { xmlSchemaFree(reader->xsdSchemas); reader->xsdSchemas = NULL; xmlSchemaFreeValidCtxt(reader->xsdValidCtxt); reader->xsdValidCtxt = NULL; return(-1); } xmlSchemaValidateSetLocator(reader->xsdValidCtxt, xmlTextReaderLocator, (void *) reader); if (reader->errorFunc != NULL) { xmlSchemaSetValidErrors(reader->xsdValidCtxt, xmlTextReaderValidityErrorRelay, xmlTextReaderValidityWarningRelay, reader); } if (reader->sErrorFunc != NULL) { xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, xmlTextReaderValidityStructuredRelay, reader); } reader->xsdValidErrors = 0; reader->validate = XML_TEXTREADER_VALIDATE_XSD; return(0); } /** * xmlTextReaderRelaxNGValidateInternal: * @reader: the xmlTextReaderPtr used * @rng: the path to a RelaxNG schema or NULL * @ctxt: the RelaxNG schema validation context or NULL * @options: options (not yet used) * * Use RelaxNG to validate the document as it is processed. * Activation is only possible before the first Read(). * If both @rng and @ctxt are NULL, then RelaxNG validation is deactivated. * * Returns 0 in case the RelaxNG validation could be (de)activated and * -1 in case of error. */ static int xmlTextReaderRelaxNGValidateInternal(xmlTextReaderPtr reader, const char *rng, xmlRelaxNGValidCtxtPtr ctxt, int options ATTRIBUTE_UNUSED) { if (reader == NULL) return(-1); if ((rng != NULL) && (ctxt != NULL)) return (-1); if (((rng != NULL) || (ctxt != NULL)) && ((reader->mode != XML_TEXTREADER_MODE_INITIAL) || (reader->ctxt == NULL))) return(-1); /* Cleanup previous validation stuff. */ if (reader->rngValidCtxt != NULL) { if ( !reader->rngPreserveCtxt) xmlRelaxNGFreeValidCtxt(reader->rngValidCtxt); reader->rngValidCtxt = NULL; } reader->rngPreserveCtxt = 0; if (reader->rngSchemas != NULL) { xmlRelaxNGFree(reader->rngSchemas); reader->rngSchemas = NULL; } if ((rng == NULL) && (ctxt == NULL)) { /* We just want to deactivate the validation, so get out. */ return(0); } if (rng != NULL) { xmlRelaxNGParserCtxtPtr pctxt; /* Parse the schema and create validation environment. */ pctxt = xmlRelaxNGNewParserCtxt(rng); if (reader->errorFunc != NULL) { xmlRelaxNGSetParserErrors(pctxt, xmlTextReaderValidityErrorRelay, xmlTextReaderValidityWarningRelay, reader); } if (reader->sErrorFunc != NULL) { xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, xmlTextReaderValidityStructuredRelay, reader); } reader->rngSchemas = xmlRelaxNGParse(pctxt); xmlRelaxNGFreeParserCtxt(pctxt); if (reader->rngSchemas == NULL) return(-1); reader->rngValidCtxt = xmlRelaxNGNewValidCtxt(reader->rngSchemas); if (reader->rngValidCtxt == NULL) { xmlRelaxNGFree(reader->rngSchemas); reader->rngSchemas = NULL; return(-1); } } else { /* Use the given validation context. */ reader->rngValidCtxt = ctxt; reader->rngPreserveCtxt = 1; } /* * Redirect the validation context's error channels to use * the reader channels. * TODO: In case the user provides the validation context we * could make this redirection optional. */ if (reader->errorFunc != NULL) { xmlRelaxNGSetValidErrors(reader->rngValidCtxt, xmlTextReaderValidityErrorRelay, xmlTextReaderValidityWarningRelay, reader); } if (reader->sErrorFunc != NULL) { xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, xmlTextReaderValidityStructuredRelay, reader); } reader->rngValidErrors = 0; reader->rngFullNode = NULL; reader->validate = XML_TEXTREADER_VALIDATE_RNG; return(0); } /** * xmlTextReaderSchemaValidateInternal: * @reader: the xmlTextReaderPtr used * @xsd: the path to a W3C XSD schema or NULL * @ctxt: the XML Schema validation context or NULL * @options: options (not used yet) * * Validate the document as it is processed using XML Schema. * Activation is only possible before the first Read(). * If both @xsd and @ctxt are NULL then XML Schema validation is deactivated. * * Returns 0 in case the schemas validation could be (de)activated and * -1 in case of error. */ static int xmlTextReaderSchemaValidateInternal(xmlTextReaderPtr reader, const char *xsd, xmlSchemaValidCtxtPtr ctxt, int options ATTRIBUTE_UNUSED) { if (reader == NULL) return(-1); if ((xsd != NULL) && (ctxt != NULL)) return(-1); if (((xsd != NULL) || (ctxt != NULL)) && ((reader->mode != XML_TEXTREADER_MODE_INITIAL) || (reader->ctxt == NULL))) return(-1); /* Cleanup previous validation stuff. */ if (reader->xsdPlug != NULL) { xmlSchemaSAXUnplug(reader->xsdPlug); reader->xsdPlug = NULL; } if (reader->xsdValidCtxt != NULL) { if (! reader->xsdPreserveCtxt) xmlSchemaFreeValidCtxt(reader->xsdValidCtxt); reader->xsdValidCtxt = NULL; } reader->xsdPreserveCtxt = 0; if (reader->xsdSchemas != NULL) { xmlSchemaFree(reader->xsdSchemas); reader->xsdSchemas = NULL; } if ((xsd == NULL) && (ctxt == NULL)) { /* We just want to deactivate the validation, so get out. */ return(0); } if (xsd != NULL) { xmlSchemaParserCtxtPtr pctxt; /* Parse the schema and create validation environment. */ pctxt = xmlSchemaNewParserCtxt(xsd); if (reader->errorFunc != NULL) { xmlSchemaSetParserErrors(pctxt, xmlTextReaderValidityErrorRelay, xmlTextReaderValidityWarningRelay, reader); } reader->xsdSchemas = xmlSchemaParse(pctxt); xmlSchemaFreeParserCtxt(pctxt); if (reader->xsdSchemas == NULL) return(-1); reader->xsdValidCtxt = xmlSchemaNewValidCtxt(reader->xsdSchemas); if (reader->xsdValidCtxt == NULL) { xmlSchemaFree(reader->xsdSchemas); reader->xsdSchemas = NULL; return(-1); } reader->xsdPlug = xmlSchemaSAXPlug(reader->xsdValidCtxt, &(reader->ctxt->sax), &(reader->ctxt->userData)); if (reader->xsdPlug == NULL) { xmlSchemaFree(reader->xsdSchemas); reader->xsdSchemas = NULL; xmlSchemaFreeValidCtxt(reader->xsdValidCtxt); reader->xsdValidCtxt = NULL; return(-1); } } else { /* Use the given validation context. */ reader->xsdValidCtxt = ctxt; reader->xsdPreserveCtxt = 1; reader->xsdPlug = xmlSchemaSAXPlug(reader->xsdValidCtxt, &(reader->ctxt->sax), &(reader->ctxt->userData)); if (reader->xsdPlug == NULL) { reader->xsdValidCtxt = NULL; reader->xsdPreserveCtxt = 0; return(-1); } } xmlSchemaValidateSetLocator(reader->xsdValidCtxt, xmlTextReaderLocator, (void *) reader); /* * Redirect the validation context's error channels to use * the reader channels. * TODO: In case the user provides the validation context we * could make this redirection optional. */ if (reader->errorFunc != NULL) { xmlSchemaSetValidErrors(reader->xsdValidCtxt, xmlTextReaderValidityErrorRelay, xmlTextReaderValidityWarningRelay, reader); } if (reader->sErrorFunc != NULL) { xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, xmlTextReaderValidityStructuredRelay, reader); } reader->xsdValidErrors = 0; reader->validate = XML_TEXTREADER_VALIDATE_XSD; return(0); } /** * xmlTextReaderSchemaValidateCtxt: * @reader: the xmlTextReaderPtr used * @ctxt: the XML Schema validation context or NULL * @options: options (not used yet) * * Use W3C XSD schema context to validate the document as it is processed. * Activation is only possible before the first Read(). * If @ctxt is NULL, then XML Schema validation is deactivated. * * Returns 0 in case the schemas validation could be (de)activated and * -1 in case of error. */ int xmlTextReaderSchemaValidateCtxt(xmlTextReaderPtr reader, xmlSchemaValidCtxtPtr ctxt, int options) { return(xmlTextReaderSchemaValidateInternal(reader, NULL, ctxt, options)); } /** * xmlTextReaderSchemaValidate: * @reader: the xmlTextReaderPtr used * @xsd: the path to a W3C XSD schema or NULL * * Use W3C XSD schema to validate the document as it is processed. * Activation is only possible before the first Read(). * If @xsd is NULL, then XML Schema validation is deactivated. * * Returns 0 in case the schemas validation could be (de)activated and * -1 in case of error. */ int xmlTextReaderSchemaValidate(xmlTextReaderPtr reader, const char *xsd) { return(xmlTextReaderSchemaValidateInternal(reader, xsd, NULL, 0)); } /** * xmlTextReaderRelaxNGValidateCtxt: * @reader: the xmlTextReaderPtr used * @ctxt: the RelaxNG schema validation context or NULL * @options: options (not used yet) * * Use RelaxNG schema context to validate the document as it is processed. * Activation is only possible before the first Read(). * If @ctxt is NULL, then RelaxNG schema validation is deactivated. * * Returns 0 in case the schemas validation could be (de)activated and * -1 in case of error. */ int xmlTextReaderRelaxNGValidateCtxt(xmlTextReaderPtr reader, xmlRelaxNGValidCtxtPtr ctxt, int options) { return(xmlTextReaderRelaxNGValidateInternal(reader, NULL, ctxt, options)); } /** * xmlTextReaderRelaxNGValidate: * @reader: the xmlTextReaderPtr used * @rng: the path to a RelaxNG schema or NULL * * Use RelaxNG schema to validate the document as it is processed. * Activation is only possible before the first Read(). * If @rng is NULL, then RelaxNG schema validation is deactivated. * * Returns 0 in case the schemas validation could be (de)activated and * -1 in case of error. */ int xmlTextReaderRelaxNGValidate(xmlTextReaderPtr reader, const char *rng) { return(xmlTextReaderRelaxNGValidateInternal(reader, rng, NULL, 0)); } #endif /** * xmlTextReaderIsNamespaceDecl: * @reader: the xmlTextReaderPtr used * * Determine whether the current node is a namespace declaration * rather than a regular attribute. * * Returns 1 if the current node is a namespace declaration, 0 if it * is a regular attribute or other type of node, or -1 in case of * error. */ int xmlTextReaderIsNamespaceDecl(xmlTextReaderPtr reader) { xmlNodePtr node; if (reader == NULL) return(-1); if (reader->node == NULL) return(-1); if (reader->curnode != NULL) node = reader->curnode; else node = reader->node; if (XML_NAMESPACE_DECL == node->type) return(1); else return(0); } /** * xmlTextReaderConstXmlVersion: * @reader: the xmlTextReaderPtr used * * Determine the XML version of the document being read. * * Returns a string containing the XML version of the document or NULL * in case of error. The string is deallocated with the reader. */ const xmlChar * xmlTextReaderConstXmlVersion(xmlTextReaderPtr reader) { xmlDocPtr doc = NULL; if (reader == NULL) return(NULL); if (reader->doc != NULL) doc = reader->doc; else if (reader->ctxt != NULL) doc = reader->ctxt->myDoc; if (doc == NULL) return(NULL); if (doc->version == NULL) return(NULL); else return(CONSTSTR(doc->version)); } /** * xmlTextReaderStandalone: * @reader: the xmlTextReaderPtr used * * Determine the standalone status of the document being read. * * Returns 1 if the document was declared to be standalone, 0 if it * was declared to be not standalone, or -1 if the document did not * specify its standalone status or in case of error. */ int xmlTextReaderStandalone(xmlTextReaderPtr reader) { xmlDocPtr doc = NULL; if (reader == NULL) return(-1); if (reader->doc != NULL) doc = reader->doc; else if (reader->ctxt != NULL) doc = reader->ctxt->myDoc; if (doc == NULL) return(-1); return(doc->standalone); } /************************************************************************ * * * Error Handling Extensions * * * ************************************************************************/ /* helper to build a xmlMalloc'ed string from a format and va_list */ static char * xmlTextReaderBuildMessage(const char *msg, va_list ap) { int size = 0; int chars; char *larger; char *str = NULL; va_list aq; while (1) { VA_COPY(aq, ap); chars = vsnprintf(str, size, msg, aq); va_end(aq); if (chars < 0) { xmlGenericError(xmlGenericErrorContext, "vsnprintf failed !\n"); if (str) xmlFree(str); return NULL; } if ((chars < size) || (size == MAX_ERR_MSG_SIZE)) break; if (chars < MAX_ERR_MSG_SIZE) size = chars + 1; else size = MAX_ERR_MSG_SIZE; if ((larger = (char *) xmlRealloc(str, size)) == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlRealloc failed !\n"); if (str) xmlFree(str); return NULL; } str = larger; } return str; } /** * xmlTextReaderLocatorLineNumber: * @locator: the xmlTextReaderLocatorPtr used * * Obtain the line number for the given locator. * * Returns the line number or -1 in case of error. */ int xmlTextReaderLocatorLineNumber(xmlTextReaderLocatorPtr locator) { /* we know that locator is a xmlParserCtxtPtr */ xmlParserCtxtPtr ctx = (xmlParserCtxtPtr)locator; int ret = -1; if (locator == NULL) return(-1); if (ctx->node != NULL) { ret = xmlGetLineNo(ctx->node); } else { /* inspired from error.c */ xmlParserInputPtr input; input = ctx->input; if ((input->filename == NULL) && (ctx->inputNr > 1)) input = ctx->inputTab[ctx->inputNr - 2]; if (input != NULL) { ret = input->line; } else { ret = -1; } } return ret; } /** * xmlTextReaderLocatorBaseURI: * @locator: the xmlTextReaderLocatorPtr used * * Obtain the base URI for the given locator. * * Returns the base URI or NULL in case of error, * if non NULL it need to be freed by the caller. */ xmlChar * xmlTextReaderLocatorBaseURI(xmlTextReaderLocatorPtr locator) { /* we know that locator is a xmlParserCtxtPtr */ xmlParserCtxtPtr ctx = (xmlParserCtxtPtr)locator; xmlChar *ret = NULL; if (locator == NULL) return(NULL); if (ctx->node != NULL) { ret = xmlNodeGetBase(NULL,ctx->node); } else { /* inspired from error.c */ xmlParserInputPtr input; input = ctx->input; if ((input->filename == NULL) && (ctx->inputNr > 1)) input = ctx->inputTab[ctx->inputNr - 2]; if (input != NULL) { ret = xmlStrdup(BAD_CAST input->filename); } else { ret = NULL; } } return ret; } static void xmlTextReaderGenericError(void *ctxt, xmlParserSeverities severity, char *str) { xmlParserCtxtPtr ctx = (xmlParserCtxtPtr) ctxt; xmlTextReaderPtr reader = (xmlTextReaderPtr) ctx->_private; if (str != NULL) { if (reader->errorFunc) reader->errorFunc(reader->errorFuncArg, str, severity, (xmlTextReaderLocatorPtr) ctx); xmlFree(str); } } static void xmlTextReaderStructuredError(void *ctxt, xmlErrorPtr error) { xmlParserCtxtPtr ctx = (xmlParserCtxtPtr) ctxt; xmlTextReaderPtr reader = (xmlTextReaderPtr) ctx->_private; if (error && reader->sErrorFunc) { reader->sErrorFunc(reader->errorFuncArg, (xmlErrorPtr) error); } } static void XMLCDECL LIBXML_ATTR_FORMAT(2,3) xmlTextReaderError(void *ctxt, const char *msg, ...) { va_list ap; va_start(ap, msg); xmlTextReaderGenericError(ctxt, XML_PARSER_SEVERITY_ERROR, xmlTextReaderBuildMessage(msg, ap)); va_end(ap); } static void XMLCDECL LIBXML_ATTR_FORMAT(2,3) xmlTextReaderWarning(void *ctxt, const char *msg, ...) { va_list ap; va_start(ap, msg); xmlTextReaderGenericError(ctxt, XML_PARSER_SEVERITY_WARNING, xmlTextReaderBuildMessage(msg, ap)); va_end(ap); } static void XMLCDECL xmlTextReaderValidityError(void *ctxt, const char *msg, ...) { va_list ap; int len = xmlStrlen((const xmlChar *) msg); if ((len > 1) && (msg[len - 2] != ':')) { /* * some callbacks only report locator information: * skip them (mimicking behaviour in error.c) */ va_start(ap, msg); xmlTextReaderGenericError(ctxt, XML_PARSER_SEVERITY_VALIDITY_ERROR, xmlTextReaderBuildMessage(msg, ap)); va_end(ap); } } static void XMLCDECL xmlTextReaderValidityWarning(void *ctxt, const char *msg, ...) { va_list ap; int len = xmlStrlen((const xmlChar *) msg); if ((len != 0) && (msg[len - 1] != ':')) { /* * some callbacks only report locator information: * skip them (mimicking behaviour in error.c) */ va_start(ap, msg); xmlTextReaderGenericError(ctxt, XML_PARSER_SEVERITY_VALIDITY_WARNING, xmlTextReaderBuildMessage(msg, ap)); va_end(ap); } } /** * xmlTextReaderSetErrorHandler: * @reader: the xmlTextReaderPtr used * @f: the callback function to call on error and warnings * @arg: a user argument to pass to the callback function * * Register a callback function that will be called on error and warnings. * * If @f is NULL, the default error and warning handlers are restored. */ void xmlTextReaderSetErrorHandler(xmlTextReaderPtr reader, xmlTextReaderErrorFunc f, void *arg) { if (f != NULL) { reader->ctxt->sax->error = xmlTextReaderError; reader->ctxt->sax->serror = NULL; reader->ctxt->vctxt.error = xmlTextReaderValidityError; reader->ctxt->sax->warning = xmlTextReaderWarning; reader->ctxt->vctxt.warning = xmlTextReaderValidityWarning; reader->errorFunc = f; reader->sErrorFunc = NULL; reader->errorFuncArg = arg; #ifdef LIBXML_SCHEMAS_ENABLED if (reader->rngValidCtxt) { xmlRelaxNGSetValidErrors(reader->rngValidCtxt, xmlTextReaderValidityErrorRelay, xmlTextReaderValidityWarningRelay, reader); xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, NULL, reader); } if (reader->xsdValidCtxt) { xmlSchemaSetValidErrors(reader->xsdValidCtxt, xmlTextReaderValidityErrorRelay, xmlTextReaderValidityWarningRelay, reader); xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, NULL, reader); } #endif } else { /* restore defaults */ reader->ctxt->sax->error = xmlParserError; reader->ctxt->vctxt.error = xmlParserValidityError; reader->ctxt->sax->warning = xmlParserWarning; reader->ctxt->vctxt.warning = xmlParserValidityWarning; reader->errorFunc = NULL; reader->sErrorFunc = NULL; reader->errorFuncArg = NULL; #ifdef LIBXML_SCHEMAS_ENABLED if (reader->rngValidCtxt) { xmlRelaxNGSetValidErrors(reader->rngValidCtxt, NULL, NULL, reader); xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, NULL, reader); } if (reader->xsdValidCtxt) { xmlSchemaSetValidErrors(reader->xsdValidCtxt, NULL, NULL, reader); xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, NULL, reader); } #endif } } /** * xmlTextReaderSetStructuredErrorHandler: * @reader: the xmlTextReaderPtr used * @f: the callback function to call on error and warnings * @arg: a user argument to pass to the callback function * * Register a callback function that will be called on error and warnings. * * If @f is NULL, the default error and warning handlers are restored. */ void xmlTextReaderSetStructuredErrorHandler(xmlTextReaderPtr reader, xmlStructuredErrorFunc f, void *arg) { if (f != NULL) { reader->ctxt->sax->error = NULL; reader->ctxt->sax->serror = xmlTextReaderStructuredError; reader->ctxt->vctxt.error = xmlTextReaderValidityError; reader->ctxt->sax->warning = xmlTextReaderWarning; reader->ctxt->vctxt.warning = xmlTextReaderValidityWarning; reader->sErrorFunc = f; reader->errorFunc = NULL; reader->errorFuncArg = arg; #ifdef LIBXML_SCHEMAS_ENABLED if (reader->rngValidCtxt) { xmlRelaxNGSetValidErrors(reader->rngValidCtxt, NULL, NULL, reader); xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, xmlTextReaderValidityStructuredRelay, reader); } if (reader->xsdValidCtxt) { xmlSchemaSetValidErrors(reader->xsdValidCtxt, NULL, NULL, reader); xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, xmlTextReaderValidityStructuredRelay, reader); } #endif } else { /* restore defaults */ reader->ctxt->sax->error = xmlParserError; reader->ctxt->sax->serror = NULL; reader->ctxt->vctxt.error = xmlParserValidityError; reader->ctxt->sax->warning = xmlParserWarning; reader->ctxt->vctxt.warning = xmlParserValidityWarning; reader->errorFunc = NULL; reader->sErrorFunc = NULL; reader->errorFuncArg = NULL; #ifdef LIBXML_SCHEMAS_ENABLED if (reader->rngValidCtxt) { xmlRelaxNGSetValidErrors(reader->rngValidCtxt, NULL, NULL, reader); xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, NULL, reader); } if (reader->xsdValidCtxt) { xmlSchemaSetValidErrors(reader->xsdValidCtxt, NULL, NULL, reader); xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, NULL, reader); } #endif } } /** * xmlTextReaderIsValid: * @reader: the xmlTextReaderPtr used * * Retrieve the validity status from the parser context * * Returns the flag value 1 if valid, 0 if no, and -1 in case of error */ int xmlTextReaderIsValid(xmlTextReaderPtr reader) { if (reader == NULL) return (-1); #ifdef LIBXML_SCHEMAS_ENABLED if (reader->validate == XML_TEXTREADER_VALIDATE_RNG) return (reader->rngValidErrors == 0); if (reader->validate == XML_TEXTREADER_VALIDATE_XSD) return (reader->xsdValidErrors == 0); #endif if ((reader->ctxt != NULL) && (reader->ctxt->validate == 1)) return (reader->ctxt->valid); return (0); } /** * xmlTextReaderGetErrorHandler: * @reader: the xmlTextReaderPtr used * @f: the callback function or NULL is no callback has been registered * @arg: a user argument * * Retrieve the error callback function and user argument. */ void xmlTextReaderGetErrorHandler(xmlTextReaderPtr reader, xmlTextReaderErrorFunc * f, void **arg) { if (f != NULL) *f = reader->errorFunc; if (arg != NULL) *arg = reader->errorFuncArg; } /************************************************************************ * * * New set (2.6.0) of simpler and more flexible APIs * * * ************************************************************************/ /** * xmlTextReaderSetup: * @reader: an XML reader * @input: xmlParserInputBufferPtr used to feed the reader, will * be destroyed with it. * @URL: the base URL to use for the document * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * Setup an XML reader with new options * * Returns 0 in case of success and -1 in case of error. */ int xmlTextReaderSetup(xmlTextReaderPtr reader, xmlParserInputBufferPtr input, const char *URL, const char *encoding, int options) { if (reader == NULL) { if (input != NULL) xmlFreeParserInputBuffer(input); return (-1); } /* * we force the generation of compact text nodes on the reader * since usr applications should never modify the tree */ options |= XML_PARSE_COMPACT; reader->doc = NULL; reader->entNr = 0; reader->parserFlags = options; reader->validate = XML_TEXTREADER_NOT_VALIDATE; if ((input != NULL) && (reader->input != NULL) && (reader->allocs & XML_TEXTREADER_INPUT)) { xmlFreeParserInputBuffer(reader->input); reader->input = NULL; reader->allocs -= XML_TEXTREADER_INPUT; } if (input != NULL) { reader->input = input; reader->allocs |= XML_TEXTREADER_INPUT; } if (reader->buffer == NULL) reader->buffer = xmlBufCreateSize(100); if (reader->buffer == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlTextReaderSetup : malloc failed\n"); return (-1); } /* no operation on a reader should require a huge buffer */ xmlBufSetAllocationScheme(reader->buffer, XML_BUFFER_ALLOC_BOUNDED); if (reader->sax == NULL) reader->sax = (xmlSAXHandler *) xmlMalloc(sizeof(xmlSAXHandler)); if (reader->sax == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlTextReaderSetup : malloc failed\n"); return (-1); } xmlSAXVersion(reader->sax, 2); reader->startElement = reader->sax->startElement; reader->sax->startElement = xmlTextReaderStartElement; reader->endElement = reader->sax->endElement; reader->sax->endElement = xmlTextReaderEndElement; #ifdef LIBXML_SAX1_ENABLED if (reader->sax->initialized == XML_SAX2_MAGIC) { #endif /* LIBXML_SAX1_ENABLED */ reader->startElementNs = reader->sax->startElementNs; reader->sax->startElementNs = xmlTextReaderStartElementNs; reader->endElementNs = reader->sax->endElementNs; reader->sax->endElementNs = xmlTextReaderEndElementNs; #ifdef LIBXML_SAX1_ENABLED } else { reader->startElementNs = NULL; reader->endElementNs = NULL; } #endif /* LIBXML_SAX1_ENABLED */ reader->characters = reader->sax->characters; reader->sax->characters = xmlTextReaderCharacters; reader->sax->ignorableWhitespace = xmlTextReaderCharacters; reader->cdataBlock = reader->sax->cdataBlock; reader->sax->cdataBlock = xmlTextReaderCDataBlock; reader->mode = XML_TEXTREADER_MODE_INITIAL; reader->node = NULL; reader->curnode = NULL; if (input != NULL) { if (xmlBufUse(reader->input->buffer) < 4) { xmlParserInputBufferRead(input, 4); } if (reader->ctxt == NULL) { if (xmlBufUse(reader->input->buffer) >= 4) { reader->ctxt = xmlCreatePushParserCtxt(reader->sax, NULL, (const char *) xmlBufContent(reader->input->buffer), 4, URL); reader->base = 0; reader->cur = 4; } else { reader->ctxt = xmlCreatePushParserCtxt(reader->sax, NULL, NULL, 0, URL); reader->base = 0; reader->cur = 0; } } else { xmlParserInputPtr inputStream; xmlParserInputBufferPtr buf; xmlCharEncoding enc = XML_CHAR_ENCODING_NONE; xmlCtxtReset(reader->ctxt); buf = xmlAllocParserInputBuffer(enc); if (buf == NULL) return(-1); inputStream = xmlNewInputStream(reader->ctxt); if (inputStream == NULL) { xmlFreeParserInputBuffer(buf); return(-1); } if (URL == NULL) inputStream->filename = NULL; else inputStream->filename = (char *) xmlCanonicPath((const xmlChar *) URL); inputStream->buf = buf; xmlBufResetInput(buf->buffer, inputStream); inputPush(reader->ctxt, inputStream); reader->cur = 0; } if (reader->ctxt == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlTextReaderSetup : malloc failed\n"); return (-1); } } if (reader->dict != NULL) { if (reader->ctxt->dict != NULL) { if (reader->dict != reader->ctxt->dict) { xmlDictFree(reader->dict); reader->dict = reader->ctxt->dict; } } else { reader->ctxt->dict = reader->dict; } } else { if (reader->ctxt->dict == NULL) reader->ctxt->dict = xmlDictCreate(); reader->dict = reader->ctxt->dict; } reader->ctxt->_private = reader; reader->ctxt->linenumbers = 1; reader->ctxt->dictNames = 1; /* * use the parser dictionary to allocate all elements and attributes names */ reader->ctxt->docdict = 1; reader->ctxt->parseMode = XML_PARSE_READER; #ifdef LIBXML_XINCLUDE_ENABLED if (reader->xincctxt != NULL) { xmlXIncludeFreeContext(reader->xincctxt); reader->xincctxt = NULL; } if (options & XML_PARSE_XINCLUDE) { reader->xinclude = 1; reader->xinclude_name = xmlDictLookup(reader->dict, XINCLUDE_NODE, -1); options -= XML_PARSE_XINCLUDE; } else reader->xinclude = 0; reader->in_xinclude = 0; #endif #ifdef LIBXML_PATTERN_ENABLED if (reader->patternTab == NULL) { reader->patternNr = 0; reader->patternMax = 0; } while (reader->patternNr > 0) { reader->patternNr--; if (reader->patternTab[reader->patternNr] != NULL) { xmlFreePattern(reader->patternTab[reader->patternNr]); reader->patternTab[reader->patternNr] = NULL; } } #endif if (options & XML_PARSE_DTDVALID) reader->validate = XML_TEXTREADER_VALIDATE_DTD; xmlCtxtUseOptions(reader->ctxt, options); if (encoding != NULL) { xmlCharEncodingHandlerPtr hdlr; hdlr = xmlFindCharEncodingHandler(encoding); if (hdlr != NULL) xmlSwitchToEncoding(reader->ctxt, hdlr); } if ((URL != NULL) && (reader->ctxt->input != NULL) && (reader->ctxt->input->filename == NULL)) reader->ctxt->input->filename = (char *) xmlStrdup((const xmlChar *) URL); reader->doc = NULL; return (0); } /** * xmlTextReaderByteConsumed: * @reader: an XML reader * * This function provides the current index of the parser used * by the reader, relative to the start of the current entity. * This function actually just wraps a call to xmlBytesConsumed() * for the parser context associated with the reader. * See xmlBytesConsumed() for more information. * * Returns the index in bytes from the beginning of the entity or -1 * in case the index could not be computed. */ long xmlTextReaderByteConsumed(xmlTextReaderPtr reader) { if ((reader == NULL) || (reader->ctxt == NULL)) return(-1); return(xmlByteConsumed(reader->ctxt)); } /** * xmlReaderWalker: * @doc: a preparsed document * * Create an xmltextReader for a preparsed document. * * Returns the new reader or NULL in case of error. */ xmlTextReaderPtr xmlReaderWalker(xmlDocPtr doc) { xmlTextReaderPtr ret; if (doc == NULL) return(NULL); ret = xmlMalloc(sizeof(xmlTextReader)); if (ret == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlNewTextReader : malloc failed\n"); return(NULL); } memset(ret, 0, sizeof(xmlTextReader)); ret->entNr = 0; ret->input = NULL; ret->mode = XML_TEXTREADER_MODE_INITIAL; ret->node = NULL; ret->curnode = NULL; ret->base = 0; ret->cur = 0; ret->allocs = XML_TEXTREADER_CTXT; ret->doc = doc; ret->state = XML_TEXTREADER_START; ret->dict = xmlDictCreate(); return(ret); } /** * xmlReaderForDoc: * @cur: a pointer to a zero terminated string * @URL: the base URL to use for the document * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * Create an xmltextReader for an XML in-memory document. * The parsing flags @options are a combination of xmlParserOption. * * Returns the new reader or NULL in case of error. */ xmlTextReaderPtr xmlReaderForDoc(const xmlChar * cur, const char *URL, const char *encoding, int options) { int len; if (cur == NULL) return (NULL); len = xmlStrlen(cur); return (xmlReaderForMemory ((const char *) cur, len, URL, encoding, options)); } /** * xmlReaderForFile: * @filename: a file or URL * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * parse an XML file from the filesystem or the network. * The parsing flags @options are a combination of xmlParserOption. * * Returns the new reader or NULL in case of error. */ xmlTextReaderPtr xmlReaderForFile(const char *filename, const char *encoding, int options) { xmlTextReaderPtr reader; reader = xmlNewTextReaderFilename(filename); if (reader == NULL) return (NULL); xmlTextReaderSetup(reader, NULL, NULL, encoding, options); return (reader); } /** * xmlReaderForMemory: * @buffer: a pointer to a char array * @size: the size of the array * @URL: the base URL to use for the document * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * Create an xmltextReader for an XML in-memory document. * The parsing flags @options are a combination of xmlParserOption. * * Returns the new reader or NULL in case of error. */ xmlTextReaderPtr xmlReaderForMemory(const char *buffer, int size, const char *URL, const char *encoding, int options) { xmlTextReaderPtr reader; xmlParserInputBufferPtr buf; buf = xmlParserInputBufferCreateStatic(buffer, size, XML_CHAR_ENCODING_NONE); if (buf == NULL) { return (NULL); } reader = xmlNewTextReader(buf, URL); if (reader == NULL) { xmlFreeParserInputBuffer(buf); return (NULL); } reader->allocs |= XML_TEXTREADER_INPUT; xmlTextReaderSetup(reader, NULL, URL, encoding, options); return (reader); } /** * xmlReaderForFd: * @fd: an open file descriptor * @URL: the base URL to use for the document * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * Create an xmltextReader for an XML from a file descriptor. * The parsing flags @options are a combination of xmlParserOption. * NOTE that the file descriptor will not be closed when the * reader is closed or reset. * * Returns the new reader or NULL in case of error. */ xmlTextReaderPtr xmlReaderForFd(int fd, const char *URL, const char *encoding, int options) { xmlTextReaderPtr reader; xmlParserInputBufferPtr input; if (fd < 0) return (NULL); input = xmlParserInputBufferCreateFd(fd, XML_CHAR_ENCODING_NONE); if (input == NULL) return (NULL); input->closecallback = NULL; reader = xmlNewTextReader(input, URL); if (reader == NULL) { xmlFreeParserInputBuffer(input); return (NULL); } reader->allocs |= XML_TEXTREADER_INPUT; xmlTextReaderSetup(reader, NULL, URL, encoding, options); return (reader); } /** * xmlReaderForIO: * @ioread: an I/O read function * @ioclose: an I/O close function * @ioctx: an I/O handler * @URL: the base URL to use for the document * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * Create an xmltextReader for an XML document from I/O functions and source. * The parsing flags @options are a combination of xmlParserOption. * * Returns the new reader or NULL in case of error. */ xmlTextReaderPtr xmlReaderForIO(xmlInputReadCallback ioread, xmlInputCloseCallback ioclose, void *ioctx, const char *URL, const char *encoding, int options) { xmlTextReaderPtr reader; xmlParserInputBufferPtr input; if (ioread == NULL) return (NULL); input = xmlParserInputBufferCreateIO(ioread, ioclose, ioctx, XML_CHAR_ENCODING_NONE); if (input == NULL) { if (ioclose != NULL) ioclose(ioctx); return (NULL); } reader = xmlNewTextReader(input, URL); if (reader == NULL) { xmlFreeParserInputBuffer(input); return (NULL); } reader->allocs |= XML_TEXTREADER_INPUT; xmlTextReaderSetup(reader, NULL, URL, encoding, options); return (reader); } /** * xmlReaderNewWalker: * @reader: an XML reader * @doc: a preparsed document * * Setup an xmltextReader to parse a preparsed XML document. * This reuses the existing @reader xmlTextReader. * * Returns 0 in case of success and -1 in case of error */ int xmlReaderNewWalker(xmlTextReaderPtr reader, xmlDocPtr doc) { if (doc == NULL) return (-1); if (reader == NULL) return (-1); if (reader->input != NULL) { xmlFreeParserInputBuffer(reader->input); } if (reader->ctxt != NULL) { xmlCtxtReset(reader->ctxt); } reader->entNr = 0; reader->input = NULL; reader->mode = XML_TEXTREADER_MODE_INITIAL; reader->node = NULL; reader->curnode = NULL; reader->base = 0; reader->cur = 0; reader->allocs = XML_TEXTREADER_CTXT; reader->doc = doc; reader->state = XML_TEXTREADER_START; if (reader->dict == NULL) { if ((reader->ctxt != NULL) && (reader->ctxt->dict != NULL)) reader->dict = reader->ctxt->dict; else reader->dict = xmlDictCreate(); } return(0); } /** * xmlReaderNewDoc: * @reader: an XML reader * @cur: a pointer to a zero terminated string * @URL: the base URL to use for the document * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * Setup an xmltextReader to parse an XML in-memory document. * The parsing flags @options are a combination of xmlParserOption. * This reuses the existing @reader xmlTextReader. * * Returns 0 in case of success and -1 in case of error */ int xmlReaderNewDoc(xmlTextReaderPtr reader, const xmlChar * cur, const char *URL, const char *encoding, int options) { int len; if (cur == NULL) return (-1); if (reader == NULL) return (-1); len = xmlStrlen(cur); return (xmlReaderNewMemory(reader, (const char *)cur, len, URL, encoding, options)); } /** * xmlReaderNewFile: * @reader: an XML reader * @filename: a file or URL * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * parse an XML file from the filesystem or the network. * The parsing flags @options are a combination of xmlParserOption. * This reuses the existing @reader xmlTextReader. * * Returns 0 in case of success and -1 in case of error */ int xmlReaderNewFile(xmlTextReaderPtr reader, const char *filename, const char *encoding, int options) { xmlParserInputBufferPtr input; if (filename == NULL) return (-1); if (reader == NULL) return (-1); input = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE); if (input == NULL) return (-1); return (xmlTextReaderSetup(reader, input, filename, encoding, options)); } /** * xmlReaderNewMemory: * @reader: an XML reader * @buffer: a pointer to a char array * @size: the size of the array * @URL: the base URL to use for the document * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * Setup an xmltextReader to parse an XML in-memory document. * The parsing flags @options are a combination of xmlParserOption. * This reuses the existing @reader xmlTextReader. * * Returns 0 in case of success and -1 in case of error */ int xmlReaderNewMemory(xmlTextReaderPtr reader, const char *buffer, int size, const char *URL, const char *encoding, int options) { xmlParserInputBufferPtr input; if (reader == NULL) return (-1); if (buffer == NULL) return (-1); input = xmlParserInputBufferCreateStatic(buffer, size, XML_CHAR_ENCODING_NONE); if (input == NULL) { return (-1); } return (xmlTextReaderSetup(reader, input, URL, encoding, options)); } /** * xmlReaderNewFd: * @reader: an XML reader * @fd: an open file descriptor * @URL: the base URL to use for the document * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * Setup an xmltextReader to parse an XML from a file descriptor. * NOTE that the file descriptor will not be closed when the * reader is closed or reset. * The parsing flags @options are a combination of xmlParserOption. * This reuses the existing @reader xmlTextReader. * * Returns 0 in case of success and -1 in case of error */ int xmlReaderNewFd(xmlTextReaderPtr reader, int fd, const char *URL, const char *encoding, int options) { xmlParserInputBufferPtr input; if (fd < 0) return (-1); if (reader == NULL) return (-1); input = xmlParserInputBufferCreateFd(fd, XML_CHAR_ENCODING_NONE); if (input == NULL) return (-1); input->closecallback = NULL; return (xmlTextReaderSetup(reader, input, URL, encoding, options)); } /** * xmlReaderNewIO: * @reader: an XML reader * @ioread: an I/O read function * @ioclose: an I/O close function * @ioctx: an I/O handler * @URL: the base URL to use for the document * @encoding: the document encoding, or NULL * @options: a combination of xmlParserOption * * Setup an xmltextReader to parse an XML document from I/O functions * and source. * The parsing flags @options are a combination of xmlParserOption. * This reuses the existing @reader xmlTextReader. * * Returns 0 in case of success and -1 in case of error */ int xmlReaderNewIO(xmlTextReaderPtr reader, xmlInputReadCallback ioread, xmlInputCloseCallback ioclose, void *ioctx, const char *URL, const char *encoding, int options) { xmlParserInputBufferPtr input; if (ioread == NULL) return (-1); if (reader == NULL) return (-1); input = xmlParserInputBufferCreateIO(ioread, ioclose, ioctx, XML_CHAR_ENCODING_NONE); if (input == NULL) { if (ioclose != NULL) ioclose(ioctx); return (-1); } return (xmlTextReaderSetup(reader, input, URL, encoding, options)); } /************************************************************************ * * * Utilities * * * ************************************************************************/ #ifdef NOT_USED_YET /** * xmlBase64Decode: * @in: the input buffer * @inlen: the size of the input (in), the size read from it (out) * @to: the output buffer * @tolen: the size of the output (in), the size written to (out) * * Base64 decoder, reads from @in and save in @to * TODO: tell jody when this is actually exported * * Returns 0 if all the input was consumer, 1 if the Base64 end was reached, * 2 if there wasn't enough space on the output or -1 in case of error. */ static int xmlBase64Decode(const unsigned char *in, unsigned long *inlen, unsigned char *to, unsigned long *tolen) { unsigned long incur; /* current index in in[] */ unsigned long inblk; /* last block index in in[] */ unsigned long outcur; /* current index in out[] */ unsigned long inmax; /* size of in[] */ unsigned long outmax; /* size of out[] */ unsigned char cur; /* the current value read from in[] */ unsigned char intmp[4], outtmp[4]; /* temporary buffers for the convert */ int nbintmp; /* number of byte in intmp[] */ int is_ignore; /* cur should be ignored */ int is_end = 0; /* the end of the base64 was found */ int retval = 1; int i; if ((in == NULL) || (inlen == NULL) || (to == NULL) || (tolen == NULL)) return (-1); incur = 0; inblk = 0; outcur = 0; inmax = *inlen; outmax = *tolen; nbintmp = 0; while (1) { if (incur >= inmax) break; cur = in[incur++]; is_ignore = 0; if ((cur >= 'A') && (cur <= 'Z')) cur = cur - 'A'; else if ((cur >= 'a') && (cur <= 'z')) cur = cur - 'a' + 26; else if ((cur >= '0') && (cur <= '9')) cur = cur - '0' + 52; else if (cur == '+') cur = 62; else if (cur == '/') cur = 63; else if (cur == '.') cur = 0; else if (cur == '=') /*no op , end of the base64 stream */ is_end = 1; else { is_ignore = 1; if (nbintmp == 0) inblk = incur; } if (!is_ignore) { int nbouttmp = 3; int is_break = 0; if (is_end) { if (nbintmp == 0) break; if ((nbintmp == 1) || (nbintmp == 2)) nbouttmp = 1; else nbouttmp = 2; nbintmp = 3; is_break = 1; } intmp[nbintmp++] = cur; /* * if intmp is full, push the 4byte sequence as a 3 byte * sequence out */ if (nbintmp == 4) { nbintmp = 0; outtmp[0] = (intmp[0] << 2) | ((intmp[1] & 0x30) >> 4); outtmp[1] = ((intmp[1] & 0x0F) << 4) | ((intmp[2] & 0x3C) >> 2); outtmp[2] = ((intmp[2] & 0x03) << 6) | (intmp[3] & 0x3F); if (outcur + 3 >= outmax) { retval = 2; break; } for (i = 0; i < nbouttmp; i++) to[outcur++] = outtmp[i]; inblk = incur; } if (is_break) { retval = 0; break; } } } *tolen = outcur; *inlen = inblk; return (retval); } /* * Test routine for the xmlBase64Decode function */ #if 0 int main(int argc, char **argv) { char *input = " VW4 gcGV0 \n aXQgdGVzdCAuCg== "; char output[100]; char output2[100]; char output3[100]; unsigned long inlen = strlen(input); unsigned long outlen = 100; int ret; unsigned long cons, tmp, tmp2, prod; /* * Direct */ ret = xmlBase64Decode(input, &inlen, output, &outlen); output[outlen] = 0; printf("ret: %d, inlen: %ld , outlen: %ld, output: '%s'\n", ret, inlen, outlen, output)indent: Standard input:179: Error:Unmatched #endif ; /* * output chunking */ cons = 0; prod = 0; while (cons < inlen) { tmp = 5; tmp2 = inlen - cons; printf("%ld %ld\n", cons, prod); ret = xmlBase64Decode(&input[cons], &tmp2, &output2[prod], &tmp); cons += tmp2; prod += tmp; printf("%ld %ld\n", cons, prod); } output2[outlen] = 0; printf("ret: %d, cons: %ld , prod: %ld, output: '%s'\n", ret, cons, prod, output2); /* * input chunking */ cons = 0; prod = 0; while (cons < inlen) { tmp = 100 - prod; tmp2 = inlen - cons; if (tmp2 > 5) tmp2 = 5; printf("%ld %ld\n", cons, prod); ret = xmlBase64Decode(&input[cons], &tmp2, &output3[prod], &tmp); cons += tmp2; prod += tmp; printf("%ld %ld\n", cons, prod); } output3[outlen] = 0; printf("ret: %d, cons: %ld , prod: %ld, output: '%s'\n", ret, cons, prod, output3); return (0); } #endif #endif /* NOT_USED_YET */ #define bottom_xmlreader #include "elfgcchack.h" #endif /* LIBXML_READER_ENABLED */