diff options
author | Daniel Silverstone <dsilvers@netsurf-browser.org> | 2012-03-24 14:59:13 +0000 |
---|---|---|
committer | Daniel Silverstone <dsilvers@netsurf-browser.org> | 2012-03-24 14:59:13 +0000 |
commit | 2eea8e30c3e5b2bdfc4abc19e2e94c0f795ceb6d (patch) | |
tree | 75e03edda4783902370da9d26146a9e6d8c81f05 /bindings/xml/libxml_xmlparser.c | |
parent | 03ae3655cd0dd14156f54ac008cf1aa8eb8a39a8 (diff) | |
download | libdom-2eea8e30c3e5b2bdfc4abc19e2e94c0f795ceb6d.tar.gz libdom-2eea8e30c3e5b2bdfc4abc19e2e94c0f795ceb6d.tar.bz2 |
Beginnings of an expat binding -- NOT FUNCTIONAL YET
svn path=/trunk/libdom/; revision=13594
Diffstat (limited to 'bindings/xml/libxml_xmlparser.c')
-rw-r--r-- | bindings/xml/libxml_xmlparser.c | 1367 |
1 files changed, 1367 insertions, 0 deletions
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 <jmb@netsurf-browser.org> + */ + +#include <stdbool.h> +#include <string.h> +#include <assert.h> + +#include <libxml/parser.h> +#include <libxml/SAX2.h> +#include <libxml/xmlerror.h> + +#include <dom/dom.h> + +#include <libwapcaplet/libwapcaplet.h> + +#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 | <libxml error> 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 | <libxml error> 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); +} |