1035 lines · 26.0 KB · C
RawDownload
/* * This file is part of Hubbub. * Licensed under the MIT License, *                http://www.opensource.org/licenses/mit-license.php * * Copyright 2008 Andrew Sidwell <takkaria@netsurf-browser.org>  * Copyright 2008 John-Mark Bell <jmb@netsurf-browser.org> */ #define _GNU_SOURCE /* for strndup */#include <assert.h>#include <stdbool.h>#include <string.h> #include <libxml/HTMLparser.h>#include <libxml/HTMLtree.h>#include <libxml/debugXML.h> #include <hubbub/parser.h>#include <hubbub/tree.h> #define UNUSED(x) ((x)=(x)) /** * Error codes */typedef enum error_code {	OK,	NOMEM,	BADENCODING,	ENCODINGCHANGE} error_code; /** * Source of encoding information */typedef enum encoding_source {	ENCODING_SOURCE_HEADER,	ENCODING_SOURCE_DETECTED,	ENCODING_SOURCE_META} encoding_source; /** * Our context */typedef struct context {	hubbub_parser *parser;			/**< Underlying parser */ 	htmlDocPtr document;			/**< Document we're building */ 	const char *encoding;			/**< The charset of the input */	encoding_source enc_source;		/**< The encoding source */ #define NUM_NAMESPACES (6)	xmlNsPtr namespaces[NUM_NAMESPACES];	/**< XML namespaces */#undef NUM_NAMESPACES 	hubbub_tree_handler tree_handler;	/**< Hubbub tree callbacks */} context; /** * Mapping of namespace prefixes to URIs, indexed by hubbub_ns. */static struct {	const char *prefix;	const char *url;} namespaces[] = {	{ NULL, NULL },	{ NULL, "http://www.w3.org/1999/xhtml" },	{ "math", "http://www.w3.org/1998/Math/MathML" },	{ "svg", "http://www.w3.org/2000/svg" },	{ "xlink", "http://www.w3.org/1999/xlink" },	/** \todo Oh dear. LibXML2 refuses to create any namespace with a 	 * prefix of "xml". That sucks, royally. */	{ "xml", "http://www.w3.org/XML/1998/namespace" },	{ "xmlns", "http://www.w3.org/2000/xmlns/" }}; static inline char *c_string_from_hubbub_string(context *ctx, 		const hubbub_string *str);static void create_namespaces(context *ctx, xmlNode *root);static hubbub_error create_comment(void *ctx, const hubbub_string *data, 		void **result);static hubbub_error create_doctype(void *ctx, const hubbub_doctype *doctype,		void **result);static hubbub_error create_element(void *ctx, const hubbub_tag *tag, 		void **result);static hubbub_error create_text(void *ctx, const hubbub_string *data, 		void **result);static hubbub_error ref_node(void *ctx, void *node);static hubbub_error unref_node(void *ctx, void *node);static hubbub_error append_child(void *ctx, void *parent, void *child, 		void **result);static hubbub_error insert_before(void *ctx, void *parent, void *child, 		void *ref_child, void **result);static hubbub_error remove_child(void *ctx, void *parent, void *child, 		void **result);static hubbub_error clone_node(void *ctx, void *node, bool deep, void **result);static hubbub_error reparent_children(void *ctx, void *node, void *new_parent);static hubbub_error get_parent(void *ctx, void *node, bool element_only, 		void **result);static hubbub_error has_children(void *ctx, void *node, bool *result);static hubbub_error form_associate(void *ctx, void *form, void *node);static hubbub_error add_attributes(void *ctx, void *node,		const hubbub_attribute *attributes, uint32_t n_attributes);static hubbub_error set_quirks_mode(void *ctx, hubbub_quirks_mode mode);static hubbub_error change_encoding(void *ctx, const char *charset); /* Prototype tree handler struct */static hubbub_tree_handler tree_handler = {	create_comment,	create_doctype,	create_element,	create_text,	ref_node,	unref_node,	append_child,	insert_before,	remove_child,	clone_node,	reparent_children,	get_parent,	has_children,	form_associate,	add_attributes,	set_quirks_mode,	change_encoding,	NULL}; /****************************************************************************** * Main hubbub driver code                                                    * ******************************************************************************/static error_code create_context(const char *charset, context **ctx);static void destroy_context(context *c);static error_code parse_chunk(context *c, const uint8_t *data, size_t len);static error_code parse_completed(context *c); int main(int argc, char **argv){	error_code error;	context *c;	hubbub_parser_optparams params;	FILE *input;	uint8_t *buf;	size_t len; 	if (argc != 2) {		fprintf(stderr, "Usage: %s <input>\n", argv[0]);		return 1;	} 	/* Read input file into memory. If we wanted to, we could read into	 * a fixed-size buffer and pass each chunk to the parser sequentially.	 */	input = fopen(argv[1], "r");	if (input == NULL) {		fprintf(stderr, "Failed opening %s\n", argv[1]);		return 1;	} 	fseek(input, 0, SEEK_END);	len = ftell(input);	fseek(input, 0, SEEK_SET); 	buf = malloc(len);	if (buf == NULL) {		fclose(input);		fprintf(stderr, "No memory for buf\n");		return 1;	} 	fread(buf, 1, len, input); 	/* Create our parsing context */	error = create_context(NULL, &c);	if (error != OK) {		free(buf);		fclose(input);		fprintf(stderr, "Failed creating parsing context\n");		return 1;	} 	/* Attempt to parse the document */	error = parse_chunk(c, buf, len);	assert(error == OK || error == ENCODINGCHANGE);	if (error == ENCODINGCHANGE) {		/* During parsing, we detected that the charset of the 		 * input data was different from what was auto-detected		 * (see the change_encoding callback for more details).		 * Therefore, we must destroy the current parser and create		 * a new one using the newly-detected charset. Then we		 * reparse the data using the new parser. 		 *		 * change_encoding() will have put the new charset into		 * c->encoding.		 */		context *c2; 		error = create_context(c->encoding, &c2);		if (error != OK) {			destroy_context(c2);			free(buf);			fclose(input);			fprintf(stderr, "Failed recreating context\n");			return 1;		} 		destroy_context(c); 		c = c2; 		/* Retry the parse */		error = parse_chunk(c, buf, len);	} 	if (error != OK) {		destroy_context(c);		free(buf);		fclose(input);		fprintf(stderr, "Failed parsing document\n");		return 1;	}  	/* Tell hubbub that we've finished */	error = parse_completed(c);	if (error != OK) {		destroy_context(c);		free(buf);		fclose(input);		fprintf(stderr, "Failed parsing document\n");		return 1;	} 	/* We're done with this */	free(buf); 	/* At this point, the DOM tree can be accessed through c->document */	/* Let's dump it to stdout */	/* In a real application, we'd probably want to grab the document	 * from the parsing context, then destroy the context as it's no 	 * longer of any use */	xmlDebugDumpDocument(stdout, c->document); 	/* Clean up */	destroy_context(c); 	fclose(input); 	return 0;} /** * Create a parsing context * * \param charset  The charset the input data is in, or NULL to autodetect * \param ctx      Location to receive context * \return OK on success,  *         NOMEM on memory exhaustion,  *         BADENCODING if charset isn't supported */error_code create_context(const char *charset, context **ctx){	context *c;	hubbub_parser_optparams params;	uint32_t i;	hubbub_error error; 	c = malloc(sizeof(context));	if (c == NULL)		return NOMEM; 	c->parser = NULL;	c->encoding = charset;	c->enc_source = ENCODING_SOURCE_HEADER;	c->document = NULL; 	/* Create the parser */	error = hubbub_parser_create(c->encoding, true, &c->parser);	if (error != HUBBUB_OK) {		free(c);		if (error == HUBBUB_BADENCODING)			return BADENCODING;		else			return NOMEM;	/* Assume OOM */	} 	/* Create the root node of the document */	c->document = htmlNewDocNoDtD(NULL, NULL);	if (c->document == NULL) {		hubbub_parser_destroy(c->parser);		free(c);		return NOMEM;	}	/* Reference count of zero */	c->document->_private = (void *) 0; 	for (i = 0; 		i < sizeof(c->namespaces) / sizeof(c->namespaces[0]); i++) {		c->namespaces[i] = NULL;	} 	/* The following are both needed to make hubbub do anything. If it has 	 * no tree handler or document node registered, it won't attempt to 	 * build a tree. */ 	/* Register tree handler with hubbub */	c->tree_handler = tree_handler;	c->tree_handler.ctx = (void *) c; 	params.tree_handler = &c->tree_handler;	hubbub_parser_setopt(c->parser, HUBBUB_PARSER_TREE_HANDLER, &params); 	/* Also tell it about the document node (referencing it first) */	ref_node(c, c->document);	params.document_node = c->document;	hubbub_parser_setopt(c->parser, HUBBUB_PARSER_DOCUMENT_NODE, &params); 	*ctx = c; 	return OK;} /** * Destroy a parsing context * * \param c  Context to destroy */void destroy_context(context *c){	if (c == NULL)		return; 	if (c->parser != NULL)		hubbub_parser_destroy(c->parser); 	xmlFreeDoc(c->document); 	c->parser = NULL;	c->encoding = NULL;	c->document = NULL; 	free(c); 	return;} /** * Parse a chunk of the input document * * \param c     Parsing context * \param data  Data buffer * \param len   Length, in bytes, of data in buffer * \return OK on success, *         ENCODINGCHANGE if the encoding needs changing */error_code parse_chunk(context *c, const uint8_t *data, size_t len){	hubbub_error err; 	err = hubbub_parser_parse_chunk(c->parser, (uint8_t *) data, len);	if (err == HUBBUB_ENCODINGCHANGE)		return ENCODINGCHANGE; 	return OK;} /** * Inform that we've run out of input to parse * * \param c  Parsing context * \return OK. */error_code parse_completed(context *c){	hubbub_error error; 	error = hubbub_parser_completed(c->parser);	/** \todo error handling */ 	return OK;} /****************************************************************************** * Helper functions for tree building                                         * ******************************************************************************/ /** * Convert a hubbub string to a C string * * \param ctx  Our context * \param str  The string to convert * \return Pointer to C string, must be freed * * This is a simple utility routine, as libXML expects data to be C strings. * If we were implementing our own tree, we might store hubbub-style strings * instead (with the associated memory saving) */char *c_string_from_hubbub_string(context *ctx, const hubbub_string *str){	return strndup((const char *) str->ptr, (int) str->len);} /** * Initialise a context's XML namespaces * * \param ctx   Our context * \param root  The root node of the XML tree * * Again, this is specific to the needs of libXML. */void create_namespaces(context *ctx, xmlNode *root){	uint32_t i; 	/* Index 0 is the NULL namespace, so skip over it */	for (i = 1; i < sizeof(namespaces) / sizeof(namespaces[0]); i++) {		ctx->namespaces[i - 1] = xmlNewNs(root, 				BAD_CAST namespaces[i].url, 				BAD_CAST namespaces[i].prefix); 		/* Expect "xml" to fail here */		if (ctx->namespaces[i - 1] == NULL) {			fprintf(stderr, 				"WARNING: Failed creating namespace %s\n", 					namespaces[i].prefix);		}	}} /****************************************************************************** * Tree callbacks for hubbub                                                  * ******************************************************************************/ /** * Create a comment node * * \param ctx     Our context * \param data    The comment body * \param result  Location to receive manufactured node * \return HUBBUB_OK on success, appropriate error otherwise * * Postcondition: if successful, result's reference count must be 1. */hubbub_error create_comment(void *ctx, const hubbub_string *data, void **result){	context *c = (context *) ctx;	char *content;	xmlNodePtr n; 	content = c_string_from_hubbub_string(c, data);	if (content == NULL)		return HUBBUB_NOMEM; 	n = xmlNewDocComment(c->document, BAD_CAST content);	if (n == NULL) {		free(content);		return HUBBUB_NOMEM;	}	/* We use the _private field of libXML's xmlNode struct for the 	 * reference count. */	n->_private = (void *) (uintptr_t) 1; 	free(content); 	*result = (void *) n; 	return HUBBUB_OK;} /** * Create a doctype node * * \param ctx      Our context * \param doctype  Data for doctype node (name, public ID and system ID) * \param result   Location to receive manufactured node * \return HUBBUB_OK on success, appropriate error otherwise * * Postcondition: if successful, result's reference count must be 1. */hubbub_error create_doctype(void *ctx, const hubbub_doctype *doctype, void **result){	context *c = (context *) ctx;	char *name, *public = NULL, *system = NULL;	xmlDtdPtr n; 	name = c_string_from_hubbub_string(c, &doctype->name);	if (name == NULL)		return HUBBUB_NOMEM; 	/* May not have public ID */	if (!doctype->public_missing) {		public = c_string_from_hubbub_string(c, &doctype->public_id);		if (public == NULL) {			free(name);			return HUBBUB_NOMEM;		}	} 

Lines 1–500 of 1035.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t