diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/HtmlTree.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/src/HtmlTree.c b/src/HtmlTree.c new file mode 100644 index 0000000..f4deb88 --- /dev/null +++ b/src/HtmlTree.c @@ -0,0 +1,171 @@ +#include "../include/HtmlTree.h" +#include <stdlib.h> + +static void html_node_deinit(HtmlNode *self); + +static void html_node_init(HtmlNode *self) { + self->name_or_value.data = NULL; + self->name_or_value.size = 0; + self->parent = NULL; + self->first_child = NULL; + self->last_child = NULL; + self->first_attr = NULL; + self->node_type = HTML_NODE_NODE; +} + +static HtmlNode* html_node_create(HtmlStringView name_or_value, HtmlNode *parent, HtmlNodeType node_type) { + HtmlNode *new_node = malloc(sizeof(HtmlNode)); + if(!new_node) + return NULL; + + new_node->name_or_value = name_or_value; + new_node->parent = parent; + new_node->first_child = NULL; + new_node->last_child = NULL; + new_node->first_attr = NULL; + new_node->node_type = node_type; + + if(parent) { + HtmlNodeChild *node_child = malloc(sizeof(HtmlNodeChild)); + if(!node_child) { + free(new_node); + return NULL; + } + + node_child->node = new_node; + node_child->next = NULL; + + if(!parent->first_child) { + parent->first_child = node_child; + parent->last_child = node_child; + } else { + parent->last_child->next = node_child; + parent->last_child = node_child; + } + } + + return new_node; +} + +static void html_node_child_deinit(HtmlNodeChild *self) { + html_node_deinit(self->node); + free(self->node); + self->node = NULL; + + if(self->next) { + html_node_child_deinit(self->next); + free(self->next); + self->next = NULL; + } +} + +static void html_attribute_deinit(HtmlAttribute *self) { + if(self->next) { + html_attribute_deinit(self->next); + free(self->next); + self->next = NULL; + } +} + +void html_node_deinit(HtmlNode *self) { + self->name_or_value.data = NULL; + self->name_or_value.size = 0; + self->parent = NULL; + + if(self->first_child) { + html_node_child_deinit(self->first_child); + free(self->first_child); + self->first_child = NULL; + self->last_child = NULL; + } + + if(self->first_attr) { + html_attribute_deinit(self->first_attr); + free(self->first_attr); + self->first_attr = NULL; + } +} + +typedef struct { + HtmlNode *current_node; + HtmlAttribute *current_node_last_attribute; +} ParseUserdata; + +static int parse_callback(HtmlParser *html_parser, HtmlParseType parse_type, void *userdata) { + ParseUserdata *parse_userdata = userdata; + + switch(parse_type) { + case HTML_PARSE_TAG_START: { + HtmlNode *new_node = html_node_create(html_parser->tag_name, parse_userdata->current_node, HTML_NODE_NODE); + if(!new_node) + return 1; + + parse_userdata->current_node = new_node; + parse_userdata->current_node_last_attribute = NULL; + break; + } + case HTML_PARSE_TAG_END: { + HtmlNode *parent_node = parse_userdata->current_node->parent; + if(parent_node) { + parse_userdata->current_node = parent_node; + parse_userdata->current_node_last_attribute = NULL; + } + break; + } + case HTML_PARSE_ATTRIBUTE: { + HtmlAttribute *new_attr = malloc(sizeof(HtmlAttribute)); + if(!new_attr) + return 1; + + new_attr->key = html_parser->attribute_key; + new_attr->value = html_parser->attribute_value; + new_attr->next = NULL; + + if(parse_userdata->current_node_last_attribute) + parse_userdata->current_node_last_attribute->next = new_attr; + else + parse_userdata->current_node->first_attr = new_attr; + + parse_userdata->current_node_last_attribute = new_attr; + break; + } + case HTML_PARSE_TEXT: + case HTML_PARSE_JAVASCRIPT_CODE: { + HtmlNode *new_node = html_node_create(html_parser->text_stripped, parse_userdata->current_node, parse_type == HTML_PARSE_TEXT ? HTML_NODE_TEXT : HTML_NODE_JS); + if(!new_node) + return 1; + + parse_userdata->current_node_last_attribute = NULL; + break; + } + } + + return 0; +} + +int html_parse_to_tree(HtmlTree *self, const char *html_source, size_t len) { + int result; + ParseUserdata parse_userdata; + html_node_init(&self->root_node); + + parse_userdata.current_node = &self->root_node; + parse_userdata.current_node_last_attribute = NULL; + result = html_parser_parse(html_source, len, parse_callback, &parse_userdata); + + if(result != 0) + html_tree_deinit(self); + return result; +} + +void html_tree_deinit(HtmlTree *self) { + html_node_deinit(&self->root_node); +} + +HtmlAttribute* html_node_get_attribute_by_name(HtmlNode *self, HtmlStringView name) { + HtmlAttribute *attr = self->first_attr; + for(; attr; attr = attr->next) { + if(html_string_view_equals_case_insensitive(&attr->key, &name)) + return attr; + } + return NULL; +} |