From 2eea8e30c3e5b2bdfc4abc19e2e94c0f795ceb6d Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 24 Mar 2012 14:59:13 +0000 Subject: Beginnings of an expat binding -- NOT FUNCTIONAL YET svn path=/trunk/libdom/; revision=13594 --- bindings/xml/Makefile | 18 +- bindings/xml/expat_xmlparser.c | 346 ++++++++++ bindings/xml/libxml_xmlparser.c | 1367 +++++++++++++++++++++++++++++++++++++++ bindings/xml/xmlerror.h | 2 +- bindings/xml/xmlparser.c | 1367 --------------------------------------- 5 files changed, 1729 insertions(+), 1371 deletions(-) create mode 100644 bindings/xml/expat_xmlparser.c create mode 100644 bindings/xml/libxml_xmlparser.c delete mode 100644 bindings/xml/xmlparser.c (limited to 'bindings') diff --git a/bindings/xml/Makefile b/bindings/xml/Makefile index a015193..12b4ca0 100644 --- a/bindings/xml/Makefile +++ b/bindings/xml/Makefile @@ -1,7 +1,5 @@ ifeq ($(WITH_LIBXML_BINDING),yes) - DIR_SOURCES := xmlparser.c - - DIR_INSTALL_ITEMS := /include/dom/bindings/libxml:xmlerror.h;xmlparser.h + DIR_SOURCES := libxml_xmlparser.c # LibXML2 ifneq ($(PKGCONFIG),) @@ -15,6 +13,20 @@ ifeq ($(WITH_LIBXML_BINDING),yes) # LibXML 2.6.26 has a bug in its headers that expects _POSIX_C_SOURCE to be # defined. Define it here, even though we don't need it. CFLAGS := $(CFLAGS) -D_POSIX_C_SOURCE + + DO_XML_INSTALL := yes +endif + +ifeq ($(WITH_EXPAT_BINDING),yes) + DIR_SOURCES := expat_xmlparser.c + + LDFLAGS := $(LDFLAGS) -lexpat + + DO_XML_INSTALL := yes +endif + +ifeq ($(DO_XML_INSTALL),yes) + DIR_INSTALL_ITEMS := /include/dom/bindings/libxml:xmlerror.h;xmlparser.h endif include build/makefiles/Makefile.subdir diff --git a/bindings/xml/expat_xmlparser.c b/bindings/xml/expat_xmlparser.c new file mode 100644 index 0000000..ce649d7 --- /dev/null +++ b/bindings/xml/expat_xmlparser.c @@ -0,0 +1,346 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2012 Daniel Silverstone + */ + +#include +#include +#include + +#include +#include + +#include + +#include "xmlparser.h" +#include "utils.h" + +#include + +/** + * expat XML parser object + */ +struct dom_xml_parser { + dom_msg msg; /**< Informational message function */ + void *mctx; /**< Pointer to client data */ + XML_Parser parser; /**< expat parser context */ + bool complete; /**< Indicate stream completion */ + struct dom_document *doc; /**< DOM Document we're building */ + struct dom_node *current; /**< DOM node we're currently building */ +}; + +/* Binding functions */ + +static void +expat_xmlparser_start_element_handler(void *_parser, + const XML_Char *name, + const XML_Char **atts) +{ + dom_xml_parser *parser = _parser; + dom_exception err; + dom_element *elem, *ins_elem; + dom_string *tag_name; + + fprintf(stderr, "<%s>\n", name); + + err = dom_string_create((const uint8_t *)name, + strlen(name), + &tag_name); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for tag name"); + return; + } + + err = dom_document_create_element(parser->doc, tag_name, &elem); + if (err != DOM_NO_ERR) { + dom_string_unref(tag_name); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Failed to create element '%s'", name); + return; + } + + dom_string_unref(tag_name); + + /* Add attributes to the element */ + + while (*atts) { + dom_string *key, *value; + + err = dom_string_create((const uint8_t *)(*atts), + strlen(*atts), &key); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for attribute name"); + dom_node_unref(elem); + return; + } + atts++; + err = dom_string_create((const uint8_t *)(*atts), + strlen(*atts), &value); + if (err != DOM_NO_ERR) { + dom_node_unref(elem); + dom_string_unref(key); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for attribute value"); + return; + } + atts++; + + err = dom_element_set_attribute(elem, key, value); + dom_string_unref(key); + dom_string_unref(value); + if (err != DOM_NO_ERR) { + dom_node_unref(elem); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for setting attribute"); + return; + } + } + + err = dom_node_append_child(parser->current, elem, &ins_elem); + if (err != DOM_NO_ERR) { + dom_node_unref(elem); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for appending child node"); + return; + } + + dom_node_unref(ins_elem); + + dom_node_unref(parser->current); + parser->current = (struct dom_node *)elem; /* Steal initial ref */ +} + +static void +expat_xmlparser_end_element_handler(void *_parser, + const XML_Char *name) +{ + dom_xml_parser *parser = _parser; + dom_exception err; + dom_node *parent; + + UNUSED(name); + + fprintf(stderr, "\n", name); + + err = dom_node_get_parent_node(parser->current, &parent); + + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Unable to find a parent while closing element."); + return; + } + + dom_node_unref(parser->current); + parser->current = parent; /* Takes the ref given by get_parent_node */ +} + +static void +expat_xmlparser_cdata_handler(void *_parser, + const XML_Char *s, + int len) +{ + dom_xml_parser *parser = _parser; + dom_string *data; + dom_exception err; + struct dom_cdata_section *cdata, *ins_cdata; + + err = dom_string_create((const uint8_t *)s, len, &data); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for cdata section contents"); + return; + } + + err = dom_document_create_cdata_section(parser->doc, data, &cdata); + if (err != DOM_NO_ERR) { + dom_string_unref(data); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for cdata section"); + return; + } + + /* No longer need data */ + dom_string_unref(data); + + /* Append cdata section to parent */ + err = dom_node_append_child(parser->current, (struct dom_node *) cdata, + (struct dom_node **) (void *) &ins_cdata); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) cdata); + parser->msg(DOM_MSG_ERROR, parser->mctx, + "Failed attaching cdata section"); + return; + } + + /* We're not interested in the inserted cdata section */ + if (ins_cdata != NULL) + dom_node_unref((struct dom_node *) ins_cdata); + + /* No longer interested in cdata section */ + dom_node_unref((struct dom_node *) cdata); +} + +static void +expat_xmlparser_unknown_data_handler(void *_parser, + const XML_Char *s, + int len) +{ + UNUSED(_parser); + + fprintf(stderr, "!!! %.*s !!!\n", len, s); +} +/** + * Create an XML parser instance + * + * \param enc Source charset, or NULL + * \param int_enc Desired charset of document buffer (UTF-8 or UTF-16) + * \param msg Informational message function + * \param mctx Pointer to client-specific private data + * \return Pointer to instance, or NULL on memory exhaustion + * + * int_enc is ignored due to it being made of bees. + */ +dom_xml_parser * +dom_xml_parser_create(const char *enc, const char *int_enc, + dom_msg msg, void *mctx) +{ + dom_xml_parser *parser; + dom_exception err; + + UNUSED(int_enc); + + parser = calloc(sizeof(*parser), 1); + if (parser == NULL) { + msg(DOM_MSG_CRITICAL, mctx, "No memory for parser"); + return NULL; + } + + parser->msg = msg; + parser->mctx = mctx; + + parser->parser = XML_ParserCreateNS(enc, ':'); + + if (parser->parser == NULL) { + free(parser); + msg(DOM_MSG_CRITICAL, mctx, "No memory for parser"); + return NULL; + } + + parser->complete = false; + parser->doc = NULL; + + err = dom_implementation_create_document( + DOM_IMPLEMENTATION_XML, + /* namespace */ NULL, + /* qname */ NULL, + /* doctype */ NULL, + NULL, + &parser->doc); + + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Failed creating document"); + XML_ParserFree(parser->parser); + free(parser); + return NULL; + } + + XML_SetUserData(parser->parser, parser); + + XML_SetElementHandler(parser->parser, + expat_xmlparser_start_element_handler, + expat_xmlparser_end_element_handler); + + XML_SetCharacterDataHandler(parser->parser, + expat_xmlparser_cdata_handler); + + XML_SetParamEntityParsing(parser->parser, + XML_PARAM_ENTITY_PARSING_ALWAYS); + + XML_SetDefaultHandler(parser->parser, + expat_xmlparser_unknown_data_handler); + + parser->current = dom_node_ref(parser->doc); + + return parser; +} + +/** + * Destroy an XML parser instance + * + * \param parser The parser instance to destroy + */ +void +dom_xml_parser_destroy(dom_xml_parser *parser) +{ + XML_ParserFree(parser->parser); + + free(parser); +} + +/** + * Parse a chunk of data + * + * \param parser The XML parser instance to use for parsing + * \param data Pointer to data chunk + * \param len Byte length of data chunk + * \return DOM_XML_OK on success, DOM_XML_EXTERNAL_ERR | on failure + */ +dom_xml_error +dom_xml_parser_parse_chunk(dom_xml_parser *parser, uint8_t *data, size_t len) +{ + enum XML_Status status; + + status = XML_Parse(parser->parser, (const char *)data, len, 0); + if (status != XML_STATUS_OK) { + parser->msg(DOM_MSG_ERROR, parser->mctx, + "XML_Parse failed: %d", status); + return DOM_XML_EXTERNAL_ERR | status; + } + + return DOM_XML_OK; +} + +/** + * Notify parser that datastream is empty + * + * \param parser The XML parser instance to notify + * \return DOM_XML_OK on success, DOM_XML_EXTERNAL_ERR | on failure + * + * This will force any remaining data through the parser + */ +dom_xml_error +dom_xml_parser_completed(dom_xml_parser *parser) +{ + enum XML_Status status; + + status = XML_Parse(parser->parser, "", 0, 1); + if (status != XML_STATUS_OK) { + parser->msg(DOM_MSG_ERROR, parser->mctx, + "XML_Parse failed: %d", status); + return DOM_XML_EXTERNAL_ERR | status; + } + + parser->complete = true; + + return DOM_XML_OK; + +} + +/** + * Retrieve the created DOM Document from a parser + * + * \param parser The parser instance to retrieve the document from + * \return Pointer to document, or NULL if parsing is not complete + * + * This may only be called after xml_parser_completed(). + */ +struct dom_document * +dom_xml_parser_get_document(dom_xml_parser *parser) +{ + return (parser->complete ? parser->doc : NULL); +} diff --git a/bindings/xml/libxml_xmlparser.c b/bindings/xml/libxml_xmlparser.c new file mode 100644 index 0000000..1c2442a --- /dev/null +++ b/bindings/xml/libxml_xmlparser.c @@ -0,0 +1,1367 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2007 John-Mark Bell + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include "xmlerror.h" +#include "xmlparser.h" +#include "utils.h" + +#include "core/document.h" + +static void xml_parser_start_document(void *ctx); +static void xml_parser_end_document(void *ctx); +static void xml_parser_start_element_ns(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); +static void xml_parser_end_element_ns(void *ctx, const xmlChar *localname, + const xmlChar *prefix, const xmlChar *URI); + +static dom_exception xml_parser_link_nodes(dom_xml_parser *parser, + struct dom_node *dom, xmlNodePtr xml); + +static void xml_parser_add_node(dom_xml_parser *parser, struct dom_node *parent, + xmlNodePtr child); +static void xml_parser_add_element_node(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child); +static void xml_parser_add_text_node(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child); +static void xml_parser_add_cdata_section(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child); +static void xml_parser_add_entity_reference(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child); +static void xml_parser_add_entity(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child); +static void xml_parser_add_comment(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child); +static void xml_parser_add_document_type(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child); + +static void xml_parser_internal_subset(void *ctx, const xmlChar *name, + const xmlChar *ExternalID, const xmlChar *SystemID); +static int xml_parser_is_standalone(void *ctx); +static int xml_parser_has_internal_subset(void *ctx); +static int xml_parser_has_external_subset(void *ctx); +static xmlParserInputPtr xml_parser_resolve_entity(void *ctx, + const xmlChar *publicId, const xmlChar *systemId); +static xmlEntityPtr xml_parser_get_entity(void *ctx, const xmlChar *name); +static void xml_parser_entity_decl(void *ctx, const xmlChar *name, + int type, const xmlChar *publicId, const xmlChar *systemId, + xmlChar *content); +static void xml_parser_notation_decl(void *ctx, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId); +static void xml_parser_attribute_decl(void *ctx, const xmlChar *elem, + const xmlChar *fullname, int type, int def, + const xmlChar *defaultValue, xmlEnumerationPtr tree); +static void xml_parser_element_decl(void *ctx, const xmlChar *name, + int type, xmlElementContentPtr content); +static void xml_parser_unparsed_entity_decl(void *ctx, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId, + const xmlChar *notationName); +static void xml_parser_set_document_locator(void *ctx, xmlSAXLocatorPtr loc); +static void xml_parser_reference(void *ctx, const xmlChar *name); +static void xml_parser_characters(void *ctx, const xmlChar *ch, int len); +static void xml_parser_comment(void *ctx, const xmlChar *value); +static xmlEntityPtr xml_parser_get_parameter_entity(void *ctx, + const xmlChar *name); +static void xml_parser_cdata_block(void *ctx, const xmlChar *value, int len); +static void xml_parser_external_subset(void *ctx, const xmlChar *name, + const xmlChar *ExternalID, const xmlChar *SystemID); + +/** + * libdom XML parser object + */ +struct dom_xml_parser { + xmlParserCtxtPtr xml_ctx; /**< libxml parser context */ + + struct dom_document *doc; /**< DOM Document we're building */ + + bool complete; /**< Indicate stream completion */ + + dom_string *udkey; /**< Key for DOM node user data */ + + dom_msg msg; /**< Informational message function */ + void *mctx; /**< Pointer to client data */ +}; + +/** + * SAX callback dispatch table + */ +static xmlSAXHandler sax_handler = { + .internalSubset = xml_parser_internal_subset, + .isStandalone = xml_parser_is_standalone, + .hasInternalSubset = xml_parser_has_internal_subset, + .hasExternalSubset = xml_parser_has_external_subset, + .resolveEntity = xml_parser_resolve_entity, + .getEntity = xml_parser_get_entity, + .entityDecl = xml_parser_entity_decl, + .notationDecl = xml_parser_notation_decl, + .attributeDecl = xml_parser_attribute_decl, + .elementDecl = xml_parser_element_decl, + .unparsedEntityDecl = xml_parser_unparsed_entity_decl, + .setDocumentLocator = xml_parser_set_document_locator, + .startDocument = xml_parser_start_document, + .endDocument = xml_parser_end_document, + .startElement = NULL, + .endElement = NULL, + .reference = xml_parser_reference, + .characters = xml_parser_characters, + .ignorableWhitespace = xml_parser_characters, + .processingInstruction = NULL, + .comment = xml_parser_comment, + .warning = NULL, + .error = NULL, + .fatalError = NULL, + .getParameterEntity = xml_parser_get_parameter_entity, + .cdataBlock = xml_parser_cdata_block, + .externalSubset = xml_parser_external_subset, + .initialized = XML_SAX2_MAGIC, + ._private = NULL, + .startElementNs = xml_parser_start_element_ns, + .endElementNs = xml_parser_end_element_ns, + .serror = NULL +}; + +static void *dom_xml_alloc(void *ptr, size_t len, void *pw) +{ + UNUSED(pw); + + if (ptr == NULL) + return len > 0 ? malloc(len) : NULL; + + if (len == 0) { + free(ptr); + return NULL; + } + + return realloc(ptr, len); +} + +/** + * Create an XML parser instance + * + * \param enc Source charset, or NULL + * \param int_enc Desired charset of document buffer (UTF-8 or UTF-16) + * \param msg Informational message function + * \param mctx Pointer to client-specific private data + * \return Pointer to instance, or NULL on memory exhaustion + * + * Neither ::enc nor ::int_enc are used here. + * libxml only supports a UTF-8 document buffer and forcibly setting the + * parser encoding is not yet implemented + */ +dom_xml_parser *dom_xml_parser_create(const char *enc, const char *int_enc, + dom_msg msg, void *mctx) +{ + dom_xml_parser *parser; + dom_exception err; + int ret; + + UNUSED(enc); + UNUSED(int_enc); + + parser = dom_xml_alloc(NULL, sizeof(dom_xml_parser), NULL); + if (parser == NULL) { + msg(DOM_MSG_CRITICAL, mctx, "No memory for parser"); + return NULL; + } + + parser->xml_ctx = + xmlCreatePushParserCtxt(&sax_handler, parser, "", 0, NULL); + if (parser->xml_ctx == NULL) { + dom_xml_alloc(parser, 0, NULL); + msg(DOM_MSG_CRITICAL, mctx, "Failed to create XML parser"); + return NULL; + } + + /* Set options of parsing context */ + ret = xmlCtxtUseOptions(parser->xml_ctx, XML_PARSE_DTDATTR | + XML_PARSE_DTDLOAD); + if (ret != 0) { + xmlFreeParserCtxt(parser->xml_ctx); + dom_xml_alloc(parser, 0, NULL); + msg(DOM_MSG_CRITICAL, mctx, "Failed setting parser options"); + return NULL; + } + + parser->doc = NULL; + + parser->complete = false; + + /* Create key for user data registration */ + err = dom_string_create((const uint8_t *) "__xmlnode", + SLEN("__xmlnode"), &parser->udkey); + if (err != DOM_NO_ERR) { + xmlFreeParserCtxt(parser->xml_ctx); + dom_xml_alloc(parser, 0, NULL); + msg(DOM_MSG_CRITICAL, mctx, "No memory for userdata key"); + return NULL; + } + + parser->msg = msg; + parser->mctx = mctx; + + return parser; +} + +/** + * Destroy an XML parser instance + * + * \param parser The parser instance to destroy + */ +void dom_xml_parser_destroy(dom_xml_parser *parser) +{ + dom_string_unref(parser->udkey); + + xmlFreeDoc(parser->xml_ctx->myDoc); + + xmlFreeParserCtxt(parser->xml_ctx); + + dom_xml_alloc(parser, 0, NULL); +} + +/** + * Parse a chunk of data + * + * \param parser The XML parser instance to use for parsing + * \param data Pointer to data chunk + * \param len Byte length of data chunk + * \return DOM_XML_OK on success, DOM_XML_EXTERNAL_ERR | on failure + */ +dom_xml_error dom_xml_parser_parse_chunk(dom_xml_parser *parser, + uint8_t *data, size_t len) +{ + xmlParserErrors err; + + err = xmlParseChunk(parser->xml_ctx, (char *) data, len, 0); + if (err != XML_ERR_OK) { + parser->msg(DOM_MSG_ERROR, parser->mctx, + "xmlParseChunk failed: %d", err); + return DOM_XML_EXTERNAL_ERR | err; + } + + return DOM_XML_OK; +} + +/** + * Notify parser that datastream is empty + * + * \param parser The XML parser instance to notify + * \return DOM_XML_OK on success, DOM_XML_EXTERNAL_ERR | on failure + * + * This will force any remaining data through the parser + */ +dom_xml_error dom_xml_parser_completed(dom_xml_parser *parser) +{ + xmlParserErrors err; + dom_string *name = NULL; + dom_exception derr; + + err = xmlParseChunk(parser->xml_ctx, "", 0, 1); + if (err != XML_ERR_OK) { + parser->msg(DOM_MSG_ERROR, parser->mctx, + "xmlParseChunk failed: %d", err); + return DOM_XML_EXTERNAL_ERR | err; + } + + parser->complete = true; + + /* TODO: In future, this string "id" should be extracted from the + * document schema file instead of just setting it as "id". + */ + derr = dom_string_create((const uint8_t *) "id", SLEN("id"), &name); + if (derr != DOM_NO_ERR) + return derr; + + _dom_document_set_id_name(parser->doc, name); + dom_string_unref(name); + + return DOM_XML_OK; +} + +/** + * Retrieve the created DOM Document from a parser + * + * \param parser The parser instance to retrieve the document from + * \return Pointer to document, or NULL if parsing is not complete + * + * This may only be called after xml_parser_completed(). + */ +struct dom_document *dom_xml_parser_get_document(dom_xml_parser *parser) +{ + return (parser->complete ? parser->doc : NULL); +} + +/** + * Handle a document start SAX event + * + * \param ctx The callback context + */ +void xml_parser_start_document(void *ctx) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + struct dom_document *doc; + dom_exception err; + + /* Invoke libxml2's default behaviour */ + xmlSAX2StartDocument(parser->xml_ctx); + + /* TODO: Just pass the dom_events_default_action_fetcher a NULL, + * we should pass the real function when we integrate libDOM with + * Netsurf */ + err = dom_implementation_create_document( + DOM_IMPLEMENTATION_XML, + /* namespace */ NULL, + /* qname */ NULL, + /* doctype */ NULL, + NULL, + &doc); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Failed creating document"); + return; + } + + /* Link nodes together */ + err = xml_parser_link_nodes(parser, (struct dom_node *) doc, + (xmlNodePtr) parser->xml_ctx->myDoc); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) doc); + return; + } + + /* And squirrel the document away for later use */ + parser->doc = doc; +} + +/** + * Handle a document end SAX event + * + * \param ctx The callback context + */ +void xml_parser_end_document(void *ctx) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + xmlNodePtr node; + xmlNodePtr n; + dom_exception err; + + /* Invoke libxml2's default behaviour */ + xmlSAX2EndDocument(parser->xml_ctx); + + /* If there is no document, we can't do anything */ + if (parser->doc == NULL) { + parser->msg(DOM_MSG_WARNING, parser->mctx, + "No document in end_document"); + return; + } + + /* We need to mirror any child nodes at the end of the list of + * children which occur after the last Element node in the list */ + + /* Get XML node */ + err = dom_node_get_user_data((struct dom_node *) parser->doc, + parser->udkey, (void **) (void *) &node); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_WARNING, parser->mctx, + "Failed finding XML node"); + return; + } + + /* Find last Element node, if any */ + for (n = node->last; n != NULL; n = n->prev) { + if (n->type == XML_ELEMENT_NODE) + break; + } + + if (n == NULL) { + /* No Element node found; entire list needs mirroring */ + n = node->children; + } else { + /* Found last Element; skip over it */ + n = n->next; + } + + /* Now, mirror nodes in the DOM */ + for (; n != NULL; n = n->next) { + xml_parser_add_node(parser, + (struct dom_node *) node->_private, n); + } +} + +/** + * Handle an element open SAX event + * + * \param ctx The callback context + * \param localname The local name of the element + * \param prefix The element namespace prefix + * \param URI The element namespace URI + * \param nb_namespaces The number of namespace definitions + * \param namespaces Array of nb_namespaces prefix/URI pairs + * \param nb_attributes The total number of attributes + * \param nb_defaulted The number of defaulted attributes + * \param attributes Array of nb_attributes attribute values + * + * The number of non-defaulted attributes is ::nb_attributes - ::nb_defaulted + * The defaulted attributes are at the end of the array ::attributes. + */ +void xml_parser_start_element_ns(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) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + xmlNodePtr parent = parser->xml_ctx->node; + + /* Invoke libxml2's default behaviour */ + xmlSAX2StartElementNs(parser->xml_ctx, localname, prefix, URI, + nb_namespaces, namespaces, nb_attributes, + nb_defaulted, attributes); + + /* If there is no document, we can't do anything */ + if (parser->doc == NULL) { + parser->msg(DOM_MSG_WARNING, parser->mctx, + "No document in start_element_ns"); + return; + } + + if (parent == NULL) { + /* No parent; use document */ + parent = (xmlNodePtr) parser->xml_ctx->myDoc; + } + + if (parent->type == XML_DOCUMENT_NODE || + parent->type == XML_ELEMENT_NODE) { + /* Mirror in the DOM all children of the parent node + * between the previous Element child (or the start, + * whichever is encountered first) and the Element + * just created */ + xmlNodePtr n; + + /* Find previous element node, if any */ + for (n = parser->xml_ctx->node->prev; n != NULL; + n = n->prev) { + if (n->type == XML_ELEMENT_NODE) + break; + } + + if (n == NULL) { + /* No previous Element; use parent's children */ + n = parent->children; + } else { + /* Previous Element; skip over it */ + n = n->next; + } + + /* Now, mirror nodes in the DOM */ + for (; n != parser->xml_ctx->node; n = n->next) { + xml_parser_add_node(parser, + (struct dom_node *) parent->_private, + n); + } + } + + /* Mirror the created node and its attributes in the DOM */ + xml_parser_add_node(parser, (struct dom_node *) parent->_private, + parser->xml_ctx->node); + +} + +/** + * Handle an element close SAX event + * + * \param ctx The callback context + * \param localname The local name of the element + * \param prefix The element namespace prefix + * \param URI The element namespace URI + */ +void xml_parser_end_element_ns(void *ctx, const xmlChar *localname, + const xmlChar *prefix, const xmlChar *URI) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + xmlNodePtr node = parser->xml_ctx->node; + xmlNodePtr n; + + /* Invoke libxml2's default behaviour */ + xmlSAX2EndElementNs(parser->xml_ctx, localname, prefix, URI); + + /* If there is no document, we can't do anything */ + if (parser->doc == NULL) { + parser->msg(DOM_MSG_WARNING, parser->mctx, + "No document in end_element_ns"); + return; + } + + /* If node wasn't linked, we can't do anything */ + if (node->_private == NULL) { + parser->msg(DOM_MSG_WARNING, parser->mctx, + "Node '%s' not linked", node->name); + return; + } + + /* We need to mirror any child nodes at the end of the list of + * children which occur after the last Element node in the list */ + + /* Find last Element node, if any */ + for (n = node->last; n != NULL; n = n->prev) { + if (n->type == XML_ELEMENT_NODE) + break; + } + + if (n == NULL) { + /* No Element node found; entire list needs mirroring */ + n = node->children; + } else { + /* Found last Element; skip over it */ + n = n->next; + } + + /* Now, mirror nodes in the DOM */ + for (; n != NULL; n = n->next) { + xml_parser_add_node(parser, + (struct dom_node *) node->_private, n); + } +} + +/** + * Link a DOM and XML node together + * + * \param parser The parser context + * \param dom The DOM node + * \param xml The XML node + * \return DOM_NO_ERR on success, appropriate error otherwise + */ +dom_exception xml_parser_link_nodes(dom_xml_parser *parser, + struct dom_node *dom, xmlNodePtr xml) +{ + void *prev_data; + dom_exception err; + + /* Register XML node as user data for DOM node */ + err = dom_node_set_user_data(dom, parser->udkey, xml, NULL, + &prev_data); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_ERROR, parser->mctx, + "Failed setting user data: %d", err); + return err; + } + + /* Register DOM node with the XML node */ + xml->_private = dom; + + return DOM_NO_ERR; +} + +/** + * Add a node to the DOM + * + * \param parser The parser context + * \param parent The parent DOM node + * \param child The xmlNode to mirror in the DOM as a child of parent + */ +void xml_parser_add_node(dom_xml_parser *parser, struct dom_node *parent, + xmlNodePtr child) +{ + static const char *node_types[] = { + "THIS_IS_NOT_A_NODE", + "XML_ELEMENT_NODE", + "XML_ATTRIBUTE_NODE", + "XML_TEXT_NODE", + "XML_CDATA_SECTION_NODE", + "XML_ENTITY_REF_NODE", + "XML_ENTITY_NODE", + "XML_PI_NODE", + "XML_COMMENT_NODE", + "XML_DOCUMENT_NODE", + "XML_DOCUMENT_TYPE_NODE", + "XML_DOCUMENT_FRAG_NODE", + "XML_NOTATION_NODE", + "XML_HTML_DOCUMENT_NODE", + "XML_DTD_NODE", + "XML_ELEMENT_DECL", + "XML_ATTRIBUTE_DECL", + "XML_ENTITY_DECL", + "XML_NAMESPACE_DECL", + "XML_XINCLUDE_START", + "XML_XINCLUDE_END", + "XML_DOCB_DOCUMENT_NODE" + }; + + switch (child->type) { + case XML_ELEMENT_NODE: + xml_parser_add_element_node(parser, parent, child); + break; + case XML_TEXT_NODE: + xml_parser_add_text_node(parser, parent, child); + break; + case XML_CDATA_SECTION_NODE: + xml_parser_add_cdata_section(parser, parent, child); + break; + case XML_ENTITY_REF_NODE: + xml_parser_add_entity_reference(parser, parent, child); + break; + case XML_COMMENT_NODE: + xml_parser_add_comment(parser, parent, child); + break; + case XML_DTD_NODE: + xml_parser_add_document_type(parser, parent, child); + break; + case XML_ENTITY_DECL: + xml_parser_add_entity(parser, parent, child); + break; + default: + parser->msg(DOM_MSG_NOTICE, parser->mctx, + "Unsupported node type: %s", + node_types[child->type]); + } +} + +/** + * Add an element node to the DOM + * + * \param parser The parser context + * \param parent The parent DOM node + * \param child The xmlNode to mirror in the DOM as a child of parent + */ +void xml_parser_add_element_node(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child) +{ + struct dom_element *el, *ins_el = NULL; + xmlAttrPtr a; + dom_exception err; + + /* Create the element node */ + if (child->ns == NULL) { + /* No namespace */ + dom_string *tag_name; + + /* Create tag name DOM string */ + err = dom_string_create(child->name, + strlen((const char *) child->name), &tag_name); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for tag name"); + return; + } + + /* Create element node */ + err = dom_document_create_element(parser->doc, + tag_name, &el); + if (err != DOM_NO_ERR) { + dom_string_unref(tag_name); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Failed creating element '%s'", + child->name); + return; + } + + /* No longer need tag name */ + dom_string_unref(tag_name); + } else { + /* Namespace */ + dom_string *namespace; + dom_string *qname; + size_t qnamelen = (child->ns->prefix != NULL ? + strlen((const char *) child->ns->prefix) : 0) + + (child->ns->prefix != NULL ? 1 : 0) /* ':' */ + + strlen((const char *) child->name); + uint8_t qnamebuf[qnamelen + 1 /* '\0' */]; + + /* Create namespace DOM string */ + err = dom_string_create( + child->ns->href, + strlen((const char *) child->ns->href), + &namespace); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for namespace"); + return; + } + + /* QName is "prefix:localname", + * or "localname" if there is no prefix */ + sprintf((char *) qnamebuf, "%s%s%s", + child->ns->prefix != NULL ? + (const char *) child->ns->prefix : "", + child->ns->prefix != NULL ? ":" : "", + (const char *) child->name); + + /* Create qname DOM string */ + err = dom_string_create( + qnamebuf, + qnamelen, + &qname); + if (err != DOM_NO_ERR) { + dom_string_unref(namespace); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for qname"); + return; + } + + /* Create element node */ + err = dom_document_create_element_ns(parser->doc, + namespace, qname, &el); + if (err != DOM_NO_ERR) { + dom_string_unref(namespace); + dom_string_unref(qname); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Failed creating element '%s'", + qnamebuf); + return; + } + + /* No longer need namespace / qname */ + dom_string_unref(namespace); + dom_string_unref(qname); + } + + /* Add attributes to created element */ + for (a = child->properties; a != NULL; a = a->next) { + struct dom_attr *attr, *prev_attr; + xmlNodePtr c; + + /* Create attribute node */ + if (a->ns == NULL) { + /* Attribute has no namespace */ + dom_string *name; + + /* Create attribute name DOM string */ + err = dom_string_create( + a->name, + strlen((const char *) a->name), + &name); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for attribute name"); + goto cleanup; + } + + /* Create attribute */ + err = dom_document_create_attribute(parser->doc, + name, &attr); + if (err != DOM_NO_ERR) { + dom_string_unref(name); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Failed creating attribute \ + '%s'", a->name); + goto cleanup; + } + + /* No longer need attribute name */ + dom_string_unref(name); + } else { + /* Attribute has namespace */ + dom_string *namespace; + dom_string *qname; + size_t qnamelen = (a->ns->prefix != NULL ? + strlen((const char *) a->ns->prefix) : 0) + + (a->ns->prefix != NULL ? 1 : 0) /* ':' */ + + strlen((const char *) a->name); + uint8_t qnamebuf[qnamelen + 1 /* '\0' */]; + + /* Create namespace DOM string */ + err = dom_string_create( + a->ns->href, + strlen((const char *) a->ns->href), + &namespace); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for namespace"); + return; + } + + /* QName is "prefix:localname", + * or "localname" if there is no prefix */ + sprintf((char *) qnamebuf, "%s%s%s", + a->ns->prefix != NULL ? + (const char *) a->ns->prefix : "", + a->ns->prefix != NULL ? ":" : "", + (const char *) a->name); + + /* Create qname DOM string */ + err = dom_string_create( + qnamebuf, + qnamelen, + &qname); + if (err != DOM_NO_ERR) { + dom_string_unref(namespace); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for qname"); + return; + } + + /* Create attribute */ + err = dom_document_create_attribute_ns(parser->doc, + namespace, qname, &attr); + if (err != DOM_NO_ERR) { + dom_string_unref(namespace); + dom_string_unref(qname); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Failed creating attribute \ + '%s'", qnamebuf); + return; + } + + /* No longer need namespace / qname */ + dom_string_unref(namespace); + dom_string_unref(qname); + } + + /* Clone subtree (attribute value) */ + for (c = a->children; c != NULL; c = c->next) { + xml_parser_add_node(parser, + (struct dom_node *) attr, c); + } + + /* Link nodes together */ + err = xml_parser_link_nodes(parser, + (struct dom_node *) attr, (xmlNodePtr) a); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) attr); + goto cleanup; + } + + if (a->ns == NULL) { + /* And add attribute to the element */ + err = dom_element_set_attribute_node(el, attr, + &prev_attr); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) attr); + parser->msg(DOM_MSG_ERROR, parser->mctx, + "Failed attaching attribute \ + '%s'", a->name); + goto cleanup; + } + } else { + err = dom_element_set_attribute_node_ns(el, attr, + &prev_attr); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) attr); + parser->msg(DOM_MSG_ERROR, parser->mctx, + "Failed attaching attribute \ + '%s'", a->name); + goto cleanup; + } + } + + /* We're not interested in the previous attribute (if any) */ + if (prev_attr != NULL && prev_attr != attr) + dom_node_unref((struct dom_node *) prev_attr); + + /* We're no longer interested in the attribute node */ + dom_node_unref((struct dom_node *) attr); + } + + /* Append element to parent */ + err = dom_node_append_child(parent, (struct dom_node *) el, + (struct dom_node **) (void *) &ins_el); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_ERROR, parser->mctx, + "Failed attaching element '%s'", + child->name); + goto cleanup; + } + + /* We're not interested in the inserted element */ + if (ins_el != NULL) + dom_node_unref((struct dom_node *) ins_el); + + /* Link nodes together */ + err = xml_parser_link_nodes(parser, (struct dom_node *) el, + child); + if (err != DOM_NO_ERR) { + goto cleanup; + } + + /* No longer interested in element node */ + dom_node_unref((struct dom_node *) el); + + return; + +cleanup: + /* No longer want node (any attributes attached to it + * will be cleaned up with it) */ + dom_node_unref((struct dom_node *) el); + + return; +} + +/** + * Add a text node to the DOM + * + * \param parser The parser context + * \param parent The parent DOM node + * \param child The xmlNode to mirror in the DOM as a child of parent + */ +void xml_parser_add_text_node(dom_xml_parser *parser, struct dom_node *parent, + xmlNodePtr child) +{ + struct dom_text *text, *ins_text = NULL; + dom_string *data; + dom_exception err; + + /* Create DOM string data for text node */ + err = dom_string_create(child->content, + strlen((const char *) child->content), &data); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for text node contents "); + return; + } + + /* Create text node */ + err = dom_document_create_text_node(parser->doc, data, &text); + if (err != DOM_NO_ERR) { + dom_string_unref(data); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for text node"); + return; + } + + /* No longer need data */ + dom_string_unref(data); + + /* Append text node to parent */ + err = dom_node_append_child(parent, (struct dom_node *) text, + (struct dom_node **) (void *) &ins_text); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) text); + parser->msg(DOM_MSG_ERROR, parser->mctx, + "Failed attaching text node"); + return; + } + + /* We're not interested in the inserted text node */ + if (ins_text != NULL) + dom_node_unref((struct dom_node *) ins_text); + + /* Link nodes together */ + err = xml_parser_link_nodes(parser, (struct dom_node *) text, + child); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) text); + return; + } + + /* No longer interested in text node */ + dom_node_unref((struct dom_node *) text); +} + +/** + * Add a cdata section to the DOM + * + * \param parser The parser context + * \param parent The parent DOM node + * \param child The xmlNode to mirror in the DOM as a child of parent + */ +void xml_parser_add_cdata_section(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child) +{ + struct dom_cdata_section *cdata, *ins_cdata = NULL; + dom_string *data; + dom_exception err; + + /* Create DOM string data for cdata section */ + err = dom_string_create(child->content, + strlen((const char *) child->content), &data); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for cdata section contents"); + return; + } + + /* Create cdata section */ + err = dom_document_create_cdata_section(parser->doc, data, &cdata); + if (err != DOM_NO_ERR) { + dom_string_unref(data); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for cdata section"); + return; + } + + /* No longer need data */ + dom_string_unref(data); + + /* Append cdata section to parent */ + err = dom_node_append_child(parent, (struct dom_node *) cdata, + (struct dom_node **) (void *) &ins_cdata); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) cdata); + parser->msg(DOM_MSG_ERROR, parser->mctx, + "Failed attaching cdata section"); + return; + } + + /* We're not interested in the inserted cdata section */ + if (ins_cdata != NULL) + dom_node_unref((struct dom_node *) ins_cdata); + + /* Link nodes together */ + err = xml_parser_link_nodes(parser, (struct dom_node *) cdata, + child); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) cdata); + return; + } + + /* No longer interested in cdata section */ + dom_node_unref((struct dom_node *) cdata); +} + +/** + * Add an entity reference to the DOM + * + * \param parser The parser context + * \param parent The parent DOM node + * \param child The xmlNode to mirror in the DOM as a child of parent + */ +void xml_parser_add_entity_reference(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child) +{ + struct dom_entity_reference *entity, *ins_entity = NULL; + dom_string *name; + xmlNodePtr c; + dom_exception err; + + /* Create name of entity reference */ + err = dom_string_create(child->name, + strlen((const char *) child->name), &name); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for entity reference name"); + return; + } + + /* Create text node */ + err = dom_document_create_entity_reference(parser->doc, name, + &entity); + if (err != DOM_NO_ERR) { + dom_string_unref(name); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for entity reference"); + return; + } + + /* No longer need name */ + dom_string_unref(name); + + /* Mirror subtree (reference value) */ + for (c = child->children; c != NULL; c = c->next) { + xml_parser_add_node(parser, (struct dom_node *) entity, c); + } + + /* Append entity reference to parent */ + err = dom_node_append_child(parent, (struct dom_node *) entity, + (struct dom_node **) (void *) &ins_entity); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) entity); + parser->msg(DOM_MSG_ERROR, parser->mctx, + "Failed attaching entity reference"); + return; + } + + /* We're not interested in the inserted entity reference */ + if (ins_entity != NULL) + dom_node_unref((struct dom_node *) ins_entity); + + /* Link nodes together */ + err = xml_parser_link_nodes(parser, (struct dom_node *) entity, + child); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) entity); + return; + } + + /* No longer interested in entity reference */ + dom_node_unref((struct dom_node *) entity); +} + +static void xml_parser_add_entity(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child) +{ + UNUSED(parser); + UNUSED(parent); + UNUSED(child); +} + +/** + * Add a comment to the DOM + * + * \param parser The parser context + * \param parent The parent DOM node + * \param child The xmlNode to mirror in the DOM as a child of parent + */ +void xml_parser_add_comment(dom_xml_parser *parser, struct dom_node *parent, + xmlNodePtr child) +{ + struct dom_comment *comment, *ins_comment = NULL; + dom_string *data; + dom_exception err; + + /* Create DOM string data for comment */ + err = dom_string_create(child->content, + strlen((const char *) child->content), &data); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for comment data"); + return; + } + + /* Create comment */ + err = dom_document_create_comment(parser->doc, data, &comment); + if (err != DOM_NO_ERR) { + dom_string_unref(data); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "No memory for comment node"); + return; + } + + /* No longer need data */ + dom_string_unref(data); + + /* Append comment to parent */ + err = dom_node_append_child(parent, (struct dom_node *) comment, + (struct dom_node **) (void *) &ins_comment); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) comment); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Failed attaching comment node"); + return; + } + + /* We're not interested in the inserted comment */ + if (ins_comment != NULL) + dom_node_unref((struct dom_node *) ins_comment); + + /* Link nodes together */ + err = xml_parser_link_nodes(parser, (struct dom_node *) comment, + child); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) comment); + return; + } + + /* No longer interested in comment */ + dom_node_unref((struct dom_node *) comment); +} + +/** + * Add a document type to the DOM + * + * \param parser The parser context + * \param parent The parent DOM node + * \param child The xmlNode to mirror in the DOM as a child of parent + */ +void xml_parser_add_document_type(dom_xml_parser *parser, + struct dom_node *parent, xmlNodePtr child) +{ + xmlDtdPtr dtd = (xmlDtdPtr) child; + struct dom_document_type *doctype, *ins_doctype = NULL; + const char *qname, *public_id, *system_id; + dom_exception err; + + /* Create qname for doctype */ + qname = (const char *) dtd->name; + + /* Create public ID for doctype */ + public_id = dtd->ExternalID != NULL ? + (const char *) dtd->ExternalID : ""; + + /* Create system ID for doctype */ + system_id = dtd->SystemID != NULL ? + (const char *) dtd->SystemID : ""; + + /* Create doctype */ + err = dom_implementation_create_document_type( + qname, public_id, system_id, + &doctype); + if (err != DOM_NO_ERR) { + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Failed to create document type"); + return; + } + + /* Add doctype to document */ + err = dom_node_append_child(parent, (struct dom_node *) doctype, + (struct dom_node **) (void *) &ins_doctype); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) doctype); + parser->msg(DOM_MSG_CRITICAL, parser->mctx, + "Failed attaching doctype"); + return; + } + + /* Not interested in inserted node */ + if (ins_doctype != NULL) + dom_node_unref((struct dom_node *) ins_doctype); + + /* Link nodes together */ + err = xml_parser_link_nodes(parser, (struct dom_node *) doctype, + child); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) doctype); + return; + } + + /* No longer interested in doctype */ + dom_node_unref((struct dom_node *) doctype); +} + +/* ------------------------------------------------------------------------*/ + +void xml_parser_internal_subset(void *ctx, const xmlChar *name, + const xmlChar *ExternalID, const xmlChar *SystemID) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2InternalSubset(parser->xml_ctx, name, ExternalID, SystemID); +} + +int xml_parser_is_standalone(void *ctx) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + return xmlSAX2IsStandalone(parser->xml_ctx); +} + +int xml_parser_has_internal_subset(void *ctx) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + return xmlSAX2HasInternalSubset(parser->xml_ctx); +} + +int xml_parser_has_external_subset(void *ctx) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + return xmlSAX2HasExternalSubset(parser->xml_ctx); +} + +xmlParserInputPtr xml_parser_resolve_entity(void *ctx, + const xmlChar *publicId, const xmlChar *systemId) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + return xmlSAX2ResolveEntity(parser->xml_ctx, publicId, systemId); +} + +xmlEntityPtr xml_parser_get_entity(void *ctx, const xmlChar *name) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + return xmlSAX2GetEntity(parser->xml_ctx, name); +} + +void xml_parser_entity_decl(void *ctx, const xmlChar *name, + int type, const xmlChar *publicId, const xmlChar *systemId, + xmlChar *content) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2EntityDecl(parser->xml_ctx, name, type, publicId, systemId, + content); +} + +void xml_parser_notation_decl(void *ctx, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2NotationDecl(parser->xml_ctx, name, publicId, systemId); +} + +void xml_parser_attribute_decl(void *ctx, const xmlChar *elem, + const xmlChar *fullname, int type, int def, + const xmlChar *defaultValue, xmlEnumerationPtr tree) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2AttributeDecl(parser->xml_ctx, elem, fullname, type, def, + defaultValue, tree); +} + +void xml_parser_element_decl(void *ctx, const xmlChar *name, + int type, xmlElementContentPtr content) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2ElementDecl(parser->xml_ctx, name, type, content); +} + +void xml_parser_unparsed_entity_decl(void *ctx, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId, + const xmlChar *notationName) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2UnparsedEntityDecl(parser->xml_ctx, name, publicId, + systemId, notationName); +} + +void xml_parser_set_document_locator(void *ctx, xmlSAXLocatorPtr loc) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2SetDocumentLocator(parser->xml_ctx, loc); +} + +void xml_parser_reference(void *ctx, const xmlChar *name) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2Reference(parser->xml_ctx, name); +} + +void xml_parser_characters(void *ctx, const xmlChar *ch, int len) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2Characters(parser->xml_ctx, ch, len); +} + +void xml_parser_comment(void *ctx, const xmlChar *value) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2Comment(parser->xml_ctx, value); +} + +xmlEntityPtr xml_parser_get_parameter_entity(void *ctx, const xmlChar *name) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + return xmlSAX2GetParameterEntity(parser->xml_ctx, name); +} + +void xml_parser_cdata_block(void *ctx, const xmlChar *value, int len) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2CDataBlock(parser->xml_ctx, value, len); +} + +void xml_parser_external_subset(void *ctx, const xmlChar *name, + const xmlChar *ExternalID, const xmlChar *SystemID) +{ + dom_xml_parser *parser = (dom_xml_parser *) ctx; + + xmlSAX2ExternalSubset(parser->xml_ctx, name, ExternalID, SystemID); +} diff --git a/bindings/xml/xmlerror.h b/bindings/xml/xmlerror.h index ca42f2b..51f2c8b 100644 --- a/bindings/xml/xmlerror.h +++ b/bindings/xml/xmlerror.h @@ -13,7 +13,7 @@ typedef enum { DOM_XML_NOMEM = 1, - DOM_XML_LIBXML_ERR = (1<<16), + DOM_XML_EXTERNAL_ERR = (1<<16), } dom_xml_error; #endif diff --git a/bindings/xml/xmlparser.c b/bindings/xml/xmlparser.c deleted file mode 100644 index 3ae1a3c..0000000 --- a/bindings/xml/xmlparser.c +++ /dev/null @@ -1,1367 +0,0 @@ -/* - * This file is part of libdom. - * Licensed under the MIT License, - * http://www.opensource.org/licenses/mit-license.php - * Copyright 2007 John-Mark Bell - */ - -#include -#include -#include - -#include -#include -#include - -#include - -#include - -#include "xmlerror.h" -#include "xmlparser.h" -#include "utils.h" - -#include "core/document.h" - -static void xml_parser_start_document(void *ctx); -static void xml_parser_end_document(void *ctx); -static void xml_parser_start_element_ns(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); -static void xml_parser_end_element_ns(void *ctx, const xmlChar *localname, - const xmlChar *prefix, const xmlChar *URI); - -static dom_exception xml_parser_link_nodes(dom_xml_parser *parser, - struct dom_node *dom, xmlNodePtr xml); - -static void xml_parser_add_node(dom_xml_parser *parser, struct dom_node *parent, - xmlNodePtr child); -static void xml_parser_add_element_node(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child); -static void xml_parser_add_text_node(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child); -static void xml_parser_add_cdata_section(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child); -static void xml_parser_add_entity_reference(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child); -static void xml_parser_add_entity(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child); -static void xml_parser_add_comment(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child); -static void xml_parser_add_document_type(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child); - -static void xml_parser_internal_subset(void *ctx, const xmlChar *name, - const xmlChar *ExternalID, const xmlChar *SystemID); -static int xml_parser_is_standalone(void *ctx); -static int xml_parser_has_internal_subset(void *ctx); -static int xml_parser_has_external_subset(void *ctx); -static xmlParserInputPtr xml_parser_resolve_entity(void *ctx, - const xmlChar *publicId, const xmlChar *systemId); -static xmlEntityPtr xml_parser_get_entity(void *ctx, const xmlChar *name); -static void xml_parser_entity_decl(void *ctx, const xmlChar *name, - int type, const xmlChar *publicId, const xmlChar *systemId, - xmlChar *content); -static void xml_parser_notation_decl(void *ctx, const xmlChar *name, - const xmlChar *publicId, const xmlChar *systemId); -static void xml_parser_attribute_decl(void *ctx, const xmlChar *elem, - const xmlChar *fullname, int type, int def, - const xmlChar *defaultValue, xmlEnumerationPtr tree); -static void xml_parser_element_decl(void *ctx, const xmlChar *name, - int type, xmlElementContentPtr content); -static void xml_parser_unparsed_entity_decl(void *ctx, const xmlChar *name, - const xmlChar *publicId, const xmlChar *systemId, - const xmlChar *notationName); -static void xml_parser_set_document_locator(void *ctx, xmlSAXLocatorPtr loc); -static void xml_parser_reference(void *ctx, const xmlChar *name); -static void xml_parser_characters(void *ctx, const xmlChar *ch, int len); -static void xml_parser_comment(void *ctx, const xmlChar *value); -static xmlEntityPtr xml_parser_get_parameter_entity(void *ctx, - const xmlChar *name); -static void xml_parser_cdata_block(void *ctx, const xmlChar *value, int len); -static void xml_parser_external_subset(void *ctx, const xmlChar *name, - const xmlChar *ExternalID, const xmlChar *SystemID); - -/** - * libdom XML parser object - */ -struct dom_xml_parser { - xmlParserCtxtPtr xml_ctx; /**< libxml parser context */ - - struct dom_document *doc; /**< DOM Document we're building */ - - bool complete; /**< Indicate stream completion */ - - dom_string *udkey; /**< Key for DOM node user data */ - - dom_msg msg; /**< Informational message function */ - void *mctx; /**< Pointer to client data */ -}; - -/** - * SAX callback dispatch table - */ -static xmlSAXHandler sax_handler = { - .internalSubset = xml_parser_internal_subset, - .isStandalone = xml_parser_is_standalone, - .hasInternalSubset = xml_parser_has_internal_subset, - .hasExternalSubset = xml_parser_has_external_subset, - .resolveEntity = xml_parser_resolve_entity, - .getEntity = xml_parser_get_entity, - .entityDecl = xml_parser_entity_decl, - .notationDecl = xml_parser_notation_decl, - .attributeDecl = xml_parser_attribute_decl, - .elementDecl = xml_parser_element_decl, - .unparsedEntityDecl = xml_parser_unparsed_entity_decl, - .setDocumentLocator = xml_parser_set_document_locator, - .startDocument = xml_parser_start_document, - .endDocument = xml_parser_end_document, - .startElement = NULL, - .endElement = NULL, - .reference = xml_parser_reference, - .characters = xml_parser_characters, - .ignorableWhitespace = xml_parser_characters, - .processingInstruction = NULL, - .comment = xml_parser_comment, - .warning = NULL, - .error = NULL, - .fatalError = NULL, - .getParameterEntity = xml_parser_get_parameter_entity, - .cdataBlock = xml_parser_cdata_block, - .externalSubset = xml_parser_external_subset, - .initialized = XML_SAX2_MAGIC, - ._private = NULL, - .startElementNs = xml_parser_start_element_ns, - .endElementNs = xml_parser_end_element_ns, - .serror = NULL -}; - -static void *dom_xml_alloc(void *ptr, size_t len, void *pw) -{ - UNUSED(pw); - - if (ptr == NULL) - return len > 0 ? malloc(len) : NULL; - - if (len == 0) { - free(ptr); - return NULL; - } - - return realloc(ptr, len); -} - -/** - * Create an XML parser instance - * - * \param enc Source charset, or NULL - * \param int_enc Desired charset of document buffer (UTF-8 or UTF-16) - * \param msg Informational message function - * \param mctx Pointer to client-specific private data - * \return Pointer to instance, or NULL on memory exhaustion - * - * Neither ::enc nor ::int_enc are used here. - * libxml only supports a UTF-8 document buffer and forcibly setting the - * parser encoding is not yet implemented - */ -dom_xml_parser *dom_xml_parser_create(const char *enc, const char *int_enc, - dom_msg msg, void *mctx) -{ - dom_xml_parser *parser; - dom_exception err; - int ret; - - UNUSED(enc); - UNUSED(int_enc); - - parser = dom_xml_alloc(NULL, sizeof(dom_xml_parser), NULL); - if (parser == NULL) { - msg(DOM_MSG_CRITICAL, mctx, "No memory for parser"); - return NULL; - } - - parser->xml_ctx = - xmlCreatePushParserCtxt(&sax_handler, parser, "", 0, NULL); - if (parser->xml_ctx == NULL) { - dom_xml_alloc(parser, 0, NULL); - msg(DOM_MSG_CRITICAL, mctx, "Failed to create XML parser"); - return NULL; - } - - /* Set options of parsing context */ - ret = xmlCtxtUseOptions(parser->xml_ctx, XML_PARSE_DTDATTR | - XML_PARSE_DTDLOAD); - if (ret != 0) { - xmlFreeParserCtxt(parser->xml_ctx); - dom_xml_alloc(parser, 0, NULL); - msg(DOM_MSG_CRITICAL, mctx, "Failed setting parser options"); - return NULL; - } - - parser->doc = NULL; - - parser->complete = false; - - /* Create key for user data registration */ - err = dom_string_create((const uint8_t *) "__xmlnode", - SLEN("__xmlnode"), &parser->udkey); - if (err != DOM_NO_ERR) { - xmlFreeParserCtxt(parser->xml_ctx); - dom_xml_alloc(parser, 0, NULL); - msg(DOM_MSG_CRITICAL, mctx, "No memory for userdata key"); - return NULL; - } - - parser->msg = msg; - parser->mctx = mctx; - - return parser; -} - -/** - * Destroy an XML parser instance - * - * \param parser The parser instance to destroy - */ -void dom_xml_parser_destroy(dom_xml_parser *parser) -{ - dom_string_unref(parser->udkey); - - xmlFreeDoc(parser->xml_ctx->myDoc); - - xmlFreeParserCtxt(parser->xml_ctx); - - dom_xml_alloc(parser, 0, NULL); -} - -/** - * Parse a chunk of data - * - * \param parser The XML parser instance to use for parsing - * \param data Pointer to data chunk - * \param len Byte length of data chunk - * \return DOM_XML_OK on success, DOM_XML_LIBXML_ERR | on failure - */ -dom_xml_error dom_xml_parser_parse_chunk(dom_xml_parser *parser, - uint8_t *data, size_t len) -{ - xmlParserErrors err; - - err = xmlParseChunk(parser->xml_ctx, (char *) data, len, 0); - if (err != XML_ERR_OK) { - parser->msg(DOM_MSG_ERROR, parser->mctx, - "xmlParseChunk failed: %d", err); - return DOM_XML_LIBXML_ERR | err; - } - - return DOM_XML_OK; -} - -/** - * Notify parser that datastream is empty - * - * \param parser The XML parser instance to notify - * \return DOM_XML_OK on success, DOM_XML_LIBXML_ERR | on failure - * - * This will force any remaining data through the parser - */ -dom_xml_error dom_xml_parser_completed(dom_xml_parser *parser) -{ - xmlParserErrors err; - dom_string *name = NULL; - dom_exception derr; - - err = xmlParseChunk(parser->xml_ctx, "", 0, 1); - if (err != XML_ERR_OK) { - parser->msg(DOM_MSG_ERROR, parser->mctx, - "xmlParseChunk failed: %d", err); - return DOM_XML_LIBXML_ERR | err; - } - - parser->complete = true; - - /* TODO: In future, this string "id" should be extracted from the - * document schema file instead of just setting it as "id". - */ - derr = dom_string_create((const uint8_t *) "id", SLEN("id"), &name); - if (derr != DOM_NO_ERR) - return derr; - - _dom_document_set_id_name(parser->doc, name); - dom_string_unref(name); - - return DOM_XML_OK; -} - -/** - * Retrieve the created DOM Document from a parser - * - * \param parser The parser instance to retrieve the document from - * \return Pointer to document, or NULL if parsing is not complete - * - * This may only be called after xml_parser_completed(). - */ -struct dom_document *dom_xml_parser_get_document(dom_xml_parser *parser) -{ - return (parser->complete ? parser->doc : NULL); -} - -/** - * Handle a document start SAX event - * - * \param ctx The callback context - */ -void xml_parser_start_document(void *ctx) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - struct dom_document *doc; - dom_exception err; - - /* Invoke libxml2's default behaviour */ - xmlSAX2StartDocument(parser->xml_ctx); - - /* TODO: Just pass the dom_events_default_action_fetcher a NULL, - * we should pass the real function when we integrate libDOM with - * Netsurf */ - err = dom_implementation_create_document( - DOM_IMPLEMENTATION_XML, - /* namespace */ NULL, - /* qname */ NULL, - /* doctype */ NULL, - NULL, - &doc); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "Failed creating document"); - return; - } - - /* Link nodes together */ - err = xml_parser_link_nodes(parser, (struct dom_node *) doc, - (xmlNodePtr) parser->xml_ctx->myDoc); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) doc); - return; - } - - /* And squirrel the document away for later use */ - parser->doc = doc; -} - -/** - * Handle a document end SAX event - * - * \param ctx The callback context - */ -void xml_parser_end_document(void *ctx) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - xmlNodePtr node; - xmlNodePtr n; - dom_exception err; - - /* Invoke libxml2's default behaviour */ - xmlSAX2EndDocument(parser->xml_ctx); - - /* If there is no document, we can't do anything */ - if (parser->doc == NULL) { - parser->msg(DOM_MSG_WARNING, parser->mctx, - "No document in end_document"); - return; - } - - /* We need to mirror any child nodes at the end of the list of - * children which occur after the last Element node in the list */ - - /* Get XML node */ - err = dom_node_get_user_data((struct dom_node *) parser->doc, - parser->udkey, (void **) (void *) &node); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_WARNING, parser->mctx, - "Failed finding XML node"); - return; - } - - /* Find last Element node, if any */ - for (n = node->last; n != NULL; n = n->prev) { - if (n->type == XML_ELEMENT_NODE) - break; - } - - if (n == NULL) { - /* No Element node found; entire list needs mirroring */ - n = node->children; - } else { - /* Found last Element; skip over it */ - n = n->next; - } - - /* Now, mirror nodes in the DOM */ - for (; n != NULL; n = n->next) { - xml_parser_add_node(parser, - (struct dom_node *) node->_private, n); - } -} - -/** - * Handle an element open SAX event - * - * \param ctx The callback context - * \param localname The local name of the element - * \param prefix The element namespace prefix - * \param URI The element namespace URI - * \param nb_namespaces The number of namespace definitions - * \param namespaces Array of nb_namespaces prefix/URI pairs - * \param nb_attributes The total number of attributes - * \param nb_defaulted The number of defaulted attributes - * \param attributes Array of nb_attributes attribute values - * - * The number of non-defaulted attributes is ::nb_attributes - ::nb_defaulted - * The defaulted attributes are at the end of the array ::attributes. - */ -void xml_parser_start_element_ns(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) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - xmlNodePtr parent = parser->xml_ctx->node; - - /* Invoke libxml2's default behaviour */ - xmlSAX2StartElementNs(parser->xml_ctx, localname, prefix, URI, - nb_namespaces, namespaces, nb_attributes, - nb_defaulted, attributes); - - /* If there is no document, we can't do anything */ - if (parser->doc == NULL) { - parser->msg(DOM_MSG_WARNING, parser->mctx, - "No document in start_element_ns"); - return; - } - - if (parent == NULL) { - /* No parent; use document */ - parent = (xmlNodePtr) parser->xml_ctx->myDoc; - } - - if (parent->type == XML_DOCUMENT_NODE || - parent->type == XML_ELEMENT_NODE) { - /* Mirror in the DOM all children of the parent node - * between the previous Element child (or the start, - * whichever is encountered first) and the Element - * just created */ - xmlNodePtr n; - - /* Find previous element node, if any */ - for (n = parser->xml_ctx->node->prev; n != NULL; - n = n->prev) { - if (n->type == XML_ELEMENT_NODE) - break; - } - - if (n == NULL) { - /* No previous Element; use parent's children */ - n = parent->children; - } else { - /* Previous Element; skip over it */ - n = n->next; - } - - /* Now, mirror nodes in the DOM */ - for (; n != parser->xml_ctx->node; n = n->next) { - xml_parser_add_node(parser, - (struct dom_node *) parent->_private, - n); - } - } - - /* Mirror the created node and its attributes in the DOM */ - xml_parser_add_node(parser, (struct dom_node *) parent->_private, - parser->xml_ctx->node); - -} - -/** - * Handle an element close SAX event - * - * \param ctx The callback context - * \param localname The local name of the element - * \param prefix The element namespace prefix - * \param URI The element namespace URI - */ -void xml_parser_end_element_ns(void *ctx, const xmlChar *localname, - const xmlChar *prefix, const xmlChar *URI) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - xmlNodePtr node = parser->xml_ctx->node; - xmlNodePtr n; - - /* Invoke libxml2's default behaviour */ - xmlSAX2EndElementNs(parser->xml_ctx, localname, prefix, URI); - - /* If there is no document, we can't do anything */ - if (parser->doc == NULL) { - parser->msg(DOM_MSG_WARNING, parser->mctx, - "No document in end_element_ns"); - return; - } - - /* If node wasn't linked, we can't do anything */ - if (node->_private == NULL) { - parser->msg(DOM_MSG_WARNING, parser->mctx, - "Node '%s' not linked", node->name); - return; - } - - /* We need to mirror any child nodes at the end of the list of - * children which occur after the last Element node in the list */ - - /* Find last Element node, if any */ - for (n = node->last; n != NULL; n = n->prev) { - if (n->type == XML_ELEMENT_NODE) - break; - } - - if (n == NULL) { - /* No Element node found; entire list needs mirroring */ - n = node->children; - } else { - /* Found last Element; skip over it */ - n = n->next; - } - - /* Now, mirror nodes in the DOM */ - for (; n != NULL; n = n->next) { - xml_parser_add_node(parser, - (struct dom_node *) node->_private, n); - } -} - -/** - * Link a DOM and XML node together - * - * \param parser The parser context - * \param dom The DOM node - * \param xml The XML node - * \return DOM_NO_ERR on success, appropriate error otherwise - */ -dom_exception xml_parser_link_nodes(dom_xml_parser *parser, - struct dom_node *dom, xmlNodePtr xml) -{ - void *prev_data; - dom_exception err; - - /* Register XML node as user data for DOM node */ - err = dom_node_set_user_data(dom, parser->udkey, xml, NULL, - &prev_data); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_ERROR, parser->mctx, - "Failed setting user data: %d", err); - return err; - } - - /* Register DOM node with the XML node */ - xml->_private = dom; - - return DOM_NO_ERR; -} - -/** - * Add a node to the DOM - * - * \param parser The parser context - * \param parent The parent DOM node - * \param child The xmlNode to mirror in the DOM as a child of parent - */ -void xml_parser_add_node(dom_xml_parser *parser, struct dom_node *parent, - xmlNodePtr child) -{ - static const char *node_types[] = { - "THIS_IS_NOT_A_NODE", - "XML_ELEMENT_NODE", - "XML_ATTRIBUTE_NODE", - "XML_TEXT_NODE", - "XML_CDATA_SECTION_NODE", - "XML_ENTITY_REF_NODE", - "XML_ENTITY_NODE", - "XML_PI_NODE", - "XML_COMMENT_NODE", - "XML_DOCUMENT_NODE", - "XML_DOCUMENT_TYPE_NODE", - "XML_DOCUMENT_FRAG_NODE", - "XML_NOTATION_NODE", - "XML_HTML_DOCUMENT_NODE", - "XML_DTD_NODE", - "XML_ELEMENT_DECL", - "XML_ATTRIBUTE_DECL", - "XML_ENTITY_DECL", - "XML_NAMESPACE_DECL", - "XML_XINCLUDE_START", - "XML_XINCLUDE_END", - "XML_DOCB_DOCUMENT_NODE" - }; - - switch (child->type) { - case XML_ELEMENT_NODE: - xml_parser_add_element_node(parser, parent, child); - break; - case XML_TEXT_NODE: - xml_parser_add_text_node(parser, parent, child); - break; - case XML_CDATA_SECTION_NODE: - xml_parser_add_cdata_section(parser, parent, child); - break; - case XML_ENTITY_REF_NODE: - xml_parser_add_entity_reference(parser, parent, child); - break; - case XML_COMMENT_NODE: - xml_parser_add_comment(parser, parent, child); - break; - case XML_DTD_NODE: - xml_parser_add_document_type(parser, parent, child); - break; - case XML_ENTITY_DECL: - xml_parser_add_entity(parser, parent, child); - break; - default: - parser->msg(DOM_MSG_NOTICE, parser->mctx, - "Unsupported node type: %s", - node_types[child->type]); - } -} - -/** - * Add an element node to the DOM - * - * \param parser The parser context - * \param parent The parent DOM node - * \param child The xmlNode to mirror in the DOM as a child of parent - */ -void xml_parser_add_element_node(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child) -{ - struct dom_element *el, *ins_el = NULL; - xmlAttrPtr a; - dom_exception err; - - /* Create the element node */ - if (child->ns == NULL) { - /* No namespace */ - dom_string *tag_name; - - /* Create tag name DOM string */ - err = dom_string_create(child->name, - strlen((const char *) child->name), &tag_name); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for tag name"); - return; - } - - /* Create element node */ - err = dom_document_create_element(parser->doc, - tag_name, &el); - if (err != DOM_NO_ERR) { - dom_string_unref(tag_name); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "Failed creating element '%s'", - child->name); - return; - } - - /* No longer need tag name */ - dom_string_unref(tag_name); - } else { - /* Namespace */ - dom_string *namespace; - dom_string *qname; - size_t qnamelen = (child->ns->prefix != NULL ? - strlen((const char *) child->ns->prefix) : 0) + - (child->ns->prefix != NULL ? 1 : 0) /* ':' */ + - strlen((const char *) child->name); - uint8_t qnamebuf[qnamelen + 1 /* '\0' */]; - - /* Create namespace DOM string */ - err = dom_string_create( - child->ns->href, - strlen((const char *) child->ns->href), - &namespace); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for namespace"); - return; - } - - /* QName is "prefix:localname", - * or "localname" if there is no prefix */ - sprintf((char *) qnamebuf, "%s%s%s", - child->ns->prefix != NULL ? - (const char *) child->ns->prefix : "", - child->ns->prefix != NULL ? ":" : "", - (const char *) child->name); - - /* Create qname DOM string */ - err = dom_string_create( - qnamebuf, - qnamelen, - &qname); - if (err != DOM_NO_ERR) { - dom_string_unref(namespace); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for qname"); - return; - } - - /* Create element node */ - err = dom_document_create_element_ns(parser->doc, - namespace, qname, &el); - if (err != DOM_NO_ERR) { - dom_string_unref(namespace); - dom_string_unref(qname); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "Failed creating element '%s'", - qnamebuf); - return; - } - - /* No longer need namespace / qname */ - dom_string_unref(namespace); - dom_string_unref(qname); - } - - /* Add attributes to created element */ - for (a = child->properties; a != NULL; a = a->next) { - struct dom_attr *attr, *prev_attr; - xmlNodePtr c; - - /* Create attribute node */ - if (a->ns == NULL) { - /* Attribute has no namespace */ - dom_string *name; - - /* Create attribute name DOM string */ - err = dom_string_create( - a->name, - strlen((const char *) a->name), - &name); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for attribute name"); - goto cleanup; - } - - /* Create attribute */ - err = dom_document_create_attribute(parser->doc, - name, &attr); - if (err != DOM_NO_ERR) { - dom_string_unref(name); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "Failed creating attribute \ - '%s'", a->name); - goto cleanup; - } - - /* No longer need attribute name */ - dom_string_unref(name); - } else { - /* Attribute has namespace */ - dom_string *namespace; - dom_string *qname; - size_t qnamelen = (a->ns->prefix != NULL ? - strlen((const char *) a->ns->prefix) : 0) + - (a->ns->prefix != NULL ? 1 : 0) /* ':' */ + - strlen((const char *) a->name); - uint8_t qnamebuf[qnamelen + 1 /* '\0' */]; - - /* Create namespace DOM string */ - err = dom_string_create( - a->ns->href, - strlen((const char *) a->ns->href), - &namespace); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for namespace"); - return; - } - - /* QName is "prefix:localname", - * or "localname" if there is no prefix */ - sprintf((char *) qnamebuf, "%s%s%s", - a->ns->prefix != NULL ? - (const char *) a->ns->prefix : "", - a->ns->prefix != NULL ? ":" : "", - (const char *) a->name); - - /* Create qname DOM string */ - err = dom_string_create( - qnamebuf, - qnamelen, - &qname); - if (err != DOM_NO_ERR) { - dom_string_unref(namespace); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for qname"); - return; - } - - /* Create attribute */ - err = dom_document_create_attribute_ns(parser->doc, - namespace, qname, &attr); - if (err != DOM_NO_ERR) { - dom_string_unref(namespace); - dom_string_unref(qname); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "Failed creating attribute \ - '%s'", qnamebuf); - return; - } - - /* No longer need namespace / qname */ - dom_string_unref(namespace); - dom_string_unref(qname); - } - - /* Clone subtree (attribute value) */ - for (c = a->children; c != NULL; c = c->next) { - xml_parser_add_node(parser, - (struct dom_node *) attr, c); - } - - /* Link nodes together */ - err = xml_parser_link_nodes(parser, - (struct dom_node *) attr, (xmlNodePtr) a); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) attr); - goto cleanup; - } - - if (a->ns == NULL) { - /* And add attribute to the element */ - err = dom_element_set_attribute_node(el, attr, - &prev_attr); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) attr); - parser->msg(DOM_MSG_ERROR, parser->mctx, - "Failed attaching attribute \ - '%s'", a->name); - goto cleanup; - } - } else { - err = dom_element_set_attribute_node_ns(el, attr, - &prev_attr); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) attr); - parser->msg(DOM_MSG_ERROR, parser->mctx, - "Failed attaching attribute \ - '%s'", a->name); - goto cleanup; - } - } - - /* We're not interested in the previous attribute (if any) */ - if (prev_attr != NULL && prev_attr != attr) - dom_node_unref((struct dom_node *) prev_attr); - - /* We're no longer interested in the attribute node */ - dom_node_unref((struct dom_node *) attr); - } - - /* Append element to parent */ - err = dom_node_append_child(parent, (struct dom_node *) el, - (struct dom_node **) (void *) &ins_el); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_ERROR, parser->mctx, - "Failed attaching element '%s'", - child->name); - goto cleanup; - } - - /* We're not interested in the inserted element */ - if (ins_el != NULL) - dom_node_unref((struct dom_node *) ins_el); - - /* Link nodes together */ - err = xml_parser_link_nodes(parser, (struct dom_node *) el, - child); - if (err != DOM_NO_ERR) { - goto cleanup; - } - - /* No longer interested in element node */ - dom_node_unref((struct dom_node *) el); - - return; - -cleanup: - /* No longer want node (any attributes attached to it - * will be cleaned up with it) */ - dom_node_unref((struct dom_node *) el); - - return; -} - -/** - * Add a text node to the DOM - * - * \param parser The parser context - * \param parent The parent DOM node - * \param child The xmlNode to mirror in the DOM as a child of parent - */ -void xml_parser_add_text_node(dom_xml_parser *parser, struct dom_node *parent, - xmlNodePtr child) -{ - struct dom_text *text, *ins_text = NULL; - dom_string *data; - dom_exception err; - - /* Create DOM string data for text node */ - err = dom_string_create(child->content, - strlen((const char *) child->content), &data); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for text node contents "); - return; - } - - /* Create text node */ - err = dom_document_create_text_node(parser->doc, data, &text); - if (err != DOM_NO_ERR) { - dom_string_unref(data); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for text node"); - return; - } - - /* No longer need data */ - dom_string_unref(data); - - /* Append text node to parent */ - err = dom_node_append_child(parent, (struct dom_node *) text, - (struct dom_node **) (void *) &ins_text); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) text); - parser->msg(DOM_MSG_ERROR, parser->mctx, - "Failed attaching text node"); - return; - } - - /* We're not interested in the inserted text node */ - if (ins_text != NULL) - dom_node_unref((struct dom_node *) ins_text); - - /* Link nodes together */ - err = xml_parser_link_nodes(parser, (struct dom_node *) text, - child); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) text); - return; - } - - /* No longer interested in text node */ - dom_node_unref((struct dom_node *) text); -} - -/** - * Add a cdata section to the DOM - * - * \param parser The parser context - * \param parent The parent DOM node - * \param child The xmlNode to mirror in the DOM as a child of parent - */ -void xml_parser_add_cdata_section(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child) -{ - struct dom_cdata_section *cdata, *ins_cdata = NULL; - dom_string *data; - dom_exception err; - - /* Create DOM string data for cdata section */ - err = dom_string_create(child->content, - strlen((const char *) child->content), &data); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for cdata section contents"); - return; - } - - /* Create cdata section */ - err = dom_document_create_cdata_section(parser->doc, data, &cdata); - if (err != DOM_NO_ERR) { - dom_string_unref(data); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for cdata section"); - return; - } - - /* No longer need data */ - dom_string_unref(data); - - /* Append cdata section to parent */ - err = dom_node_append_child(parent, (struct dom_node *) cdata, - (struct dom_node **) (void *) &ins_cdata); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) cdata); - parser->msg(DOM_MSG_ERROR, parser->mctx, - "Failed attaching cdata section"); - return; - } - - /* We're not interested in the inserted cdata section */ - if (ins_cdata != NULL) - dom_node_unref((struct dom_node *) ins_cdata); - - /* Link nodes together */ - err = xml_parser_link_nodes(parser, (struct dom_node *) cdata, - child); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) cdata); - return; - } - - /* No longer interested in cdata section */ - dom_node_unref((struct dom_node *) cdata); -} - -/** - * Add an entity reference to the DOM - * - * \param parser The parser context - * \param parent The parent DOM node - * \param child The xmlNode to mirror in the DOM as a child of parent - */ -void xml_parser_add_entity_reference(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child) -{ - struct dom_entity_reference *entity, *ins_entity = NULL; - dom_string *name; - xmlNodePtr c; - dom_exception err; - - /* Create name of entity reference */ - err = dom_string_create(child->name, - strlen((const char *) child->name), &name); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for entity reference name"); - return; - } - - /* Create text node */ - err = dom_document_create_entity_reference(parser->doc, name, - &entity); - if (err != DOM_NO_ERR) { - dom_string_unref(name); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for entity reference"); - return; - } - - /* No longer need name */ - dom_string_unref(name); - - /* Mirror subtree (reference value) */ - for (c = child->children; c != NULL; c = c->next) { - xml_parser_add_node(parser, (struct dom_node *) entity, c); - } - - /* Append entity reference to parent */ - err = dom_node_append_child(parent, (struct dom_node *) entity, - (struct dom_node **) (void *) &ins_entity); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) entity); - parser->msg(DOM_MSG_ERROR, parser->mctx, - "Failed attaching entity reference"); - return; - } - - /* We're not interested in the inserted entity reference */ - if (ins_entity != NULL) - dom_node_unref((struct dom_node *) ins_entity); - - /* Link nodes together */ - err = xml_parser_link_nodes(parser, (struct dom_node *) entity, - child); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) entity); - return; - } - - /* No longer interested in entity reference */ - dom_node_unref((struct dom_node *) entity); -} - -static void xml_parser_add_entity(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child) -{ - UNUSED(parser); - UNUSED(parent); - UNUSED(child); -} - -/** - * Add a comment to the DOM - * - * \param parser The parser context - * \param parent The parent DOM node - * \param child The xmlNode to mirror in the DOM as a child of parent - */ -void xml_parser_add_comment(dom_xml_parser *parser, struct dom_node *parent, - xmlNodePtr child) -{ - struct dom_comment *comment, *ins_comment = NULL; - dom_string *data; - dom_exception err; - - /* Create DOM string data for comment */ - err = dom_string_create(child->content, - strlen((const char *) child->content), &data); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for comment data"); - return; - } - - /* Create comment */ - err = dom_document_create_comment(parser->doc, data, &comment); - if (err != DOM_NO_ERR) { - dom_string_unref(data); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "No memory for comment node"); - return; - } - - /* No longer need data */ - dom_string_unref(data); - - /* Append comment to parent */ - err = dom_node_append_child(parent, (struct dom_node *) comment, - (struct dom_node **) (void *) &ins_comment); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) comment); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "Failed attaching comment node"); - return; - } - - /* We're not interested in the inserted comment */ - if (ins_comment != NULL) - dom_node_unref((struct dom_node *) ins_comment); - - /* Link nodes together */ - err = xml_parser_link_nodes(parser, (struct dom_node *) comment, - child); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) comment); - return; - } - - /* No longer interested in comment */ - dom_node_unref((struct dom_node *) comment); -} - -/** - * Add a document type to the DOM - * - * \param parser The parser context - * \param parent The parent DOM node - * \param child The xmlNode to mirror in the DOM as a child of parent - */ -void xml_parser_add_document_type(dom_xml_parser *parser, - struct dom_node *parent, xmlNodePtr child) -{ - xmlDtdPtr dtd = (xmlDtdPtr) child; - struct dom_document_type *doctype, *ins_doctype = NULL; - const char *qname, *public_id, *system_id; - dom_exception err; - - /* Create qname for doctype */ - qname = (const char *) dtd->name; - - /* Create public ID for doctype */ - public_id = dtd->ExternalID != NULL ? - (const char *) dtd->ExternalID : ""; - - /* Create system ID for doctype */ - system_id = dtd->SystemID != NULL ? - (const char *) dtd->SystemID : ""; - - /* Create doctype */ - err = dom_implementation_create_document_type( - qname, public_id, system_id, - &doctype); - if (err != DOM_NO_ERR) { - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "Failed to create document type"); - return; - } - - /* Add doctype to document */ - err = dom_node_append_child(parent, (struct dom_node *) doctype, - (struct dom_node **) (void *) &ins_doctype); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) doctype); - parser->msg(DOM_MSG_CRITICAL, parser->mctx, - "Failed attaching doctype"); - return; - } - - /* Not interested in inserted node */ - if (ins_doctype != NULL) - dom_node_unref((struct dom_node *) ins_doctype); - - /* Link nodes together */ - err = xml_parser_link_nodes(parser, (struct dom_node *) doctype, - child); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) doctype); - return; - } - - /* No longer interested in doctype */ - dom_node_unref((struct dom_node *) doctype); -} - -/* ------------------------------------------------------------------------*/ - -void xml_parser_internal_subset(void *ctx, const xmlChar *name, - const xmlChar *ExternalID, const xmlChar *SystemID) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2InternalSubset(parser->xml_ctx, name, ExternalID, SystemID); -} - -int xml_parser_is_standalone(void *ctx) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - return xmlSAX2IsStandalone(parser->xml_ctx); -} - -int xml_parser_has_internal_subset(void *ctx) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - return xmlSAX2HasInternalSubset(parser->xml_ctx); -} - -int xml_parser_has_external_subset(void *ctx) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - return xmlSAX2HasExternalSubset(parser->xml_ctx); -} - -xmlParserInputPtr xml_parser_resolve_entity(void *ctx, - const xmlChar *publicId, const xmlChar *systemId) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - return xmlSAX2ResolveEntity(parser->xml_ctx, publicId, systemId); -} - -xmlEntityPtr xml_parser_get_entity(void *ctx, const xmlChar *name) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - return xmlSAX2GetEntity(parser->xml_ctx, name); -} - -void xml_parser_entity_decl(void *ctx, const xmlChar *name, - int type, const xmlChar *publicId, const xmlChar *systemId, - xmlChar *content) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2EntityDecl(parser->xml_ctx, name, type, publicId, systemId, - content); -} - -void xml_parser_notation_decl(void *ctx, const xmlChar *name, - const xmlChar *publicId, const xmlChar *systemId) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2NotationDecl(parser->xml_ctx, name, publicId, systemId); -} - -void xml_parser_attribute_decl(void *ctx, const xmlChar *elem, - const xmlChar *fullname, int type, int def, - const xmlChar *defaultValue, xmlEnumerationPtr tree) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2AttributeDecl(parser->xml_ctx, elem, fullname, type, def, - defaultValue, tree); -} - -void xml_parser_element_decl(void *ctx, const xmlChar *name, - int type, xmlElementContentPtr content) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2ElementDecl(parser->xml_ctx, name, type, content); -} - -void xml_parser_unparsed_entity_decl(void *ctx, const xmlChar *name, - const xmlChar *publicId, const xmlChar *systemId, - const xmlChar *notationName) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2UnparsedEntityDecl(parser->xml_ctx, name, publicId, - systemId, notationName); -} - -void xml_parser_set_document_locator(void *ctx, xmlSAXLocatorPtr loc) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2SetDocumentLocator(parser->xml_ctx, loc); -} - -void xml_parser_reference(void *ctx, const xmlChar *name) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2Reference(parser->xml_ctx, name); -} - -void xml_parser_characters(void *ctx, const xmlChar *ch, int len) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2Characters(parser->xml_ctx, ch, len); -} - -void xml_parser_comment(void *ctx, const xmlChar *value) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2Comment(parser->xml_ctx, value); -} - -xmlEntityPtr xml_parser_get_parameter_entity(void *ctx, const xmlChar *name) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - return xmlSAX2GetParameterEntity(parser->xml_ctx, name); -} - -void xml_parser_cdata_block(void *ctx, const xmlChar *value, int len) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2CDataBlock(parser->xml_ctx, value, len); -} - -void xml_parser_external_subset(void *ctx, const xmlChar *name, - const xmlChar *ExternalID, const xmlChar *SystemID) -{ - dom_xml_parser *parser = (dom_xml_parser *) ctx; - - xmlSAX2ExternalSubset(parser->xml_ctx, name, ExternalID, SystemID); -} -- cgit v1.2.3