| /* |
| * 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 <string.h> /* for memset() only ! */ |
| #include <stdarg.h> |
| |
| #ifdef HAVE_CTYPE_H |
| #include <ctype.h> |
| #endif |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| |
| #include <libxml/xmlmemory.h> |
| #include <libxml/xmlIO.h> |
| #include <libxml/xmlreader.h> |
| #include <libxml/parserInternals.h> |
| #ifdef LIBXML_SCHEMAS_ENABLED |
| #include <libxml/relaxng.h> |
| #include <libxml/xmlschemas.h> |
| #endif |
| #include <libxml/uri.h> |
| #ifdef LIBXML_XINCLUDE_ENABLED |
| #include <libxml/xinclude.h> |
| #endif |
| #ifdef LIBXML_PATTERN_ENABLED |
| #include <libxml/pattern.h> |
| #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 <string.h> |
| #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) |
| if (id->name != NULL) |
| DICT_FREE(id->name) |
| 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; |
| attr->name = NULL; |
| 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; |
| xmlNodePtr parent; |
| xmlDictPtr dict; |
| size_t depth = 0; |
| |
| 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 (1) { |
| while ((cur->type != XML_DTD_NODE) && |
| (cur->type != XML_ENTITY_REF_NODE) && |
| (cur->children != NULL) && |
| (cur->children->parent == cur)) { |
| cur = cur->children; |
| depth += 1; |
| } |
| |
| next = cur->next; |
| parent = cur->parent; |
| |
| /* unroll to speed up freeing the document */ |
| if (cur->type != XML_DTD_NODE) { |
| |
| 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); |
| } |
| } |
| |
| if (next != NULL) { |
| cur = next; |
| } else { |
| if ((depth == 0) || (parent == NULL)) |
| break; |
| depth -= 1; |
| cur = parent; |
| cur->children = NULL; |
| } |
| } |
| } |
| |
| /** |
| * 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) { |
| 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; |
| |
| do { |
| if (node->type == XML_ENTITY_REF_NODE) { |
| 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 been raised already. |
| */ |
| if (node == oldnode) |
| break; |
| goto skip_children; |
| } |
| #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); |
| } |
| skip_children: |
| 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 aren't 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)) { |
| 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->node->doc; |
| buff = xmlBufferCreate(); |
| if (buff == NULL) |
| return NULL; |
| for (cur_node = reader->node->children; cur_node != NULL; |
| cur_node = cur_node->next) { |
| /* XXX: Why is the node copied? */ |
| 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; |
| |
| if (xmlTextReaderExpand(reader) == NULL) { |
| return NULL; |
| } |
| node = reader->node; |
| doc = node->doc; |
| /* XXX: Why is the node copied? */ |
| 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 */ |
| 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->vctxt.vstateTab != NULL) && |
| (reader->ctxt->vctxt.vstateMax > 0)){ |
| #ifdef LIBXML_REGEXP_ENABLED |
| while (reader->ctxt->vctxt.vstateNr > 0) |
| xmlValidatePopElement(&reader->ctxt->vctxt, NULL, NULL, NULL); |
| #endif /* LIBXML_REGEXP_ENABLED */ |
| xmlFree(reader->ctxt->vctxt.vstateTab); |
| reader->ctxt->vctxt.vstateTab = NULL; |
| reader->ctxt->vctxt.vstateMax = 0; |
| } |
| if (reader->ctxt->myDoc != NULL) { |
| if (reader->preserve == 0) |
| xmlTextReaderFreeDoc(reader, reader->ctxt->myDoc); |
| reader->ctxt->myDoc = NULL; |
| } |
| 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 progressed 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 positioned on an |
| * attribute 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)); |
| } |
| |
| |
| /************************************************************************ |
| * * |
| * Access 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 xmlReaderTypes 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 |