/* * 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, ¶ms); /* 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, ¶ms); *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. Load more
/* * 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, ¶ms); /* 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, ¶ms); *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; } }