1436 lines · 39.2 KB · C
RawDownload
/* * 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 <stdlib.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 dom_exception xml_parser_add_node(dom_xml_parser *parser,		struct dom_node *parent, xmlNodePtr child);static dom_exception xml_parser_add_element_node(dom_xml_parser *parser,		struct dom_node *parent, xmlNodePtr child);static dom_exception xml_parser_add_text_node(dom_xml_parser *parser,		struct dom_node *parent, xmlNodePtr child);static dom_exception xml_parser_add_cdata_section(dom_xml_parser *parser,		struct dom_node *parent, xmlNodePtr child);static dom_exception xml_parser_add_entity_reference(dom_xml_parser *parser,		struct dom_node *parent, xmlNodePtr child);static dom_exception xml_parser_add_entity(dom_xml_parser *parser,        struct dom_node *parent, xmlNodePtr child);static dom_exception xml_parser_add_comment(dom_xml_parser *parser,		struct dom_node *parent, xmlNodePtr child);static dom_exception 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 */ 	dom_string *udkey;	/**< Key for DOM node user data */ 	dom_msg msg;		/**< Informational message function */	void *mctx;		/**< Pointer to client data */ 	dom_exception err;	/**< Last DOM error, if any */}; /** * 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 \p enc nor \p 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_document **document){	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;	} 	/* 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;	} 	err = dom_implementation_create_document(			DOM_IMPLEMENTATION_XML,			/* namespace */ NULL,			/* qname */ NULL,			/* doctype */ NULL,			NULL,			NULL,			document); 	if (err != DOM_NO_ERR) {		xmlFreeParserCtxt(parser->xml_ctx);		dom_string_unref(parser->udkey);		dom_xml_alloc(parser, 0, NULL);		parser->msg(DOM_MSG_CRITICAL, parser->mctx,				"Failed creating document");		return NULL;	} 	parser->doc = (dom_document *) dom_node_ref(*document); 	parser->msg = msg;	parser->mctx = mctx; 	parser->err = DOM_NO_ERR; 	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);	dom_node_unref(parser->doc); 	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;	} 	if (parser->err != DOM_NO_ERR) {		return DOM_XML_DOM_ERR | parser->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; 	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;	} 	if (parser->err != DOM_NO_ERR) {		return DOM_XML_DOM_ERR | parser->err;	} 	return DOM_XML_OK;} /** * 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;#if LIBXML_VERSION >= 21200	const xmlError *xmlerr;#else	xmlErrorPtr xmlerr;#endif 	if (parser->err != DOM_NO_ERR)		return; 	/* Invoke libxml2's default behaviour */	xmlSAX2StartDocument(parser->xml_ctx);	xmlerr = xmlCtxtGetLastError(parser->xml_ctx);	if (xmlerr != NULL && xmlerr->level >= XML_ERR_ERROR) {		return;	} 	/* Link nodes together */	parser->err = xml_parser_link_nodes(parser,			(struct dom_node *) parser->doc,			(xmlNodePtr) parser->xml_ctx->myDoc);	if (parser->err != DOM_NO_ERR) {		parser->msg(DOM_MSG_WARNING, parser->mctx,				"Not able to link document nodes");	}} /** * 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;#if LIBXML_VERSION >= 21200	const xmlError *xmlerr;#else	xmlErrorPtr xmlerr;#endif 	if (parser->err != DOM_NO_ERR)		return; 	/* Invoke libxml2's default behaviour */	xmlSAX2EndDocument(parser->xml_ctx);	xmlerr = xmlCtxtGetLastError(parser->xml_ctx);	if (xmlerr != NULL && xmlerr->level >= XML_ERR_ERROR) {		return;	} 	/* 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 */	parser->err = dom_node_get_user_data((struct dom_node *) parser->doc,			parser->udkey, (void **) (void *) &node); 	/* The return value from dom_node_get_user_data() is always	 * DOM_NO_ERR, but the returned "node" will be NULL if no user	 * data is found. */	if (parser->err != DOM_NO_ERR || node == NULL) {		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) {		parser->err = xml_parser_add_node(parser,				(struct dom_node *) node->_private, n);		if (parser->err != DOM_NO_ERR)			return;	}} /** * 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 \p nb_attributes - \p nb_defaulted * The defaulted attributes are at the end of the array \p 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;#if LIBXML_VERSION >= 21200	const xmlError *xmlerr;#else	xmlErrorPtr xmlerr;#endif 	if (parser->err != DOM_NO_ERR)		return; 	/* Invoke libxml2's default behaviour */	xmlSAX2StartElementNs(parser->xml_ctx, localname, prefix, URI,			nb_namespaces, namespaces, nb_attributes,			nb_defaulted, attributes);	xmlerr = xmlCtxtGetLastError(parser->xml_ctx);	if (xmlerr != NULL && xmlerr->level >= XML_ERR_ERROR) {		return;	} 	/* 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) {			parser->err = xml_parser_add_node(parser,					(struct dom_node *) parent->_private,					n);			if (parser->err != DOM_NO_ERR)

Lines 1–500 of 1436.