static inline struct dom_node * add_sgml_element(struct dom_stack *stack, struct dom_scanner_token *token) { struct sgml_parser *parser = get_sgml_parser(stack); struct dom_node *parent = get_dom_stack_top(stack)->node; struct dom_stack_state *state; struct sgml_parser_state *pstate; struct dom_node *node; struct sgml_node_info *node_info; node = add_dom_element(parent, &token->string); if (!node) return NULL; node_info = get_sgml_node_info(parser->info->elements, node); node->data.element.type = node_info->type; if (!push_dom_node(stack, node)) return NULL; state = get_dom_stack_top(stack); assert(node == state->node); pstate = get_sgml_parser_state(stack, state); pstate->info = node_info; return node; }
static inline void add_sgml_attribute(struct dom_stack *stack, struct dom_scanner_token *token, struct dom_scanner_token *valtoken) { struct sgml_parser *parser = get_sgml_parser(stack); struct dom_node *parent = get_dom_stack_top(stack)->node; struct dom_string *value = valtoken ? &valtoken->string : NULL; struct sgml_node_info *info; struct dom_node *node; node = add_dom_attribute(parent, &token->string, value); info = get_sgml_node_info(parser->info->attributes, node); node->data.attribute.type = info->type; node->data.attribute.id = !!(info->flags & SGML_ATTRIBUTE_IDENTIFIER); node->data.attribute.reference = !!(info->flags & SGML_ATTRIBUTE_REFERENCE); if (valtoken && valtoken->type == SGML_TOKEN_STRING) node->data.attribute.quoted = 1; if (!node || !push_dom_node(stack, node)) return; pop_dom_node(stack); }
static inline struct dom_node * add_sgml_document(struct dom_stack *stack, struct dom_string *string) { struct dom_node *node = init_dom_node(DOM_NODE_DOCUMENT, string); return node ? push_dom_node(stack, node) : NULL; }
/* Create a new parsing state by pushing a new text node containing the*/ static struct sgml_parsing_state * init_sgml_parsing_state(struct sgml_parser *parser, struct dom_string *buffer) { struct dom_stack_state *state; struct dom_node *node; node = init_dom_node(DOM_NODE_TEXT, buffer); if (!node || !push_dom_node(&parser->parsing, node)) return NULL; state = get_dom_stack_top(&parser->parsing); return get_dom_stack_state_data(parser->parsing.contexts[0], state); }
static inline void add_sgml_node(struct dom_stack *stack, enum dom_node_type type, struct dom_scanner_token *token) { struct dom_node *parent = get_dom_stack_top(stack)->node; struct dom_node *node = add_dom_node(parent, type, &token->string); if (!node) return; if (token->type == SGML_TOKEN_SPACE) node->data.text.only_space = 1; if (push_dom_node(stack, node)) pop_dom_node(stack); }
static inline struct dom_node * add_sgml_proc_instruction(struct dom_stack *stack, struct dom_scanner_token *target, struct dom_scanner_token *data) { struct dom_node *parent = get_dom_stack_top(stack)->node; struct dom_string *data_str = data ? &data->string : NULL; struct dom_node *node; node = add_dom_proc_instruction(parent, &target->string, data_str); if (!node) return NULL; switch (target->type) { case SGML_TOKEN_PROCESS_XML: node->data.proc_instruction.type = DOM_PROC_INSTRUCTION_XML; break; case SGML_TOKEN_PROCESS: default: node->data.proc_instruction.type = DOM_PROC_INSTRUCTION; } return push_dom_node(stack, node); }
/* Parse a CSS3 selector and add selector nodes to the @select struct. */ static enum dom_code parse_dom_select(struct dom_select *select, struct dom_stack *stack, struct dom_string *string) { struct dom_scanner scanner; struct dom_select_node sel; init_dom_scanner(&scanner, &dom_css_scanner_info, string, 0, 0, 1, 0, 0); memset(&sel, 0, sizeof(sel)); while (dom_scanner_has_tokens(&scanner)) { struct dom_scanner_token *token = get_dom_scanner_token(&scanner); enum dom_code code; struct dom_select_node *select_node; assert(token); if (token->type == '{' || token->type == '}' || token->type == ';' || token->type == ',') break; /* Examine the selector fragment */ switch (token->type) { case CSS_TOKEN_IDENT: sel.node.type = DOM_NODE_ELEMENT; copy_dom_string(&sel.node.string, &token->string); if (dom_scanner_token_contains(token, "*")) sel.match.element |= DOM_SELECT_ELEMENT_UNIVERSAL; break; case CSS_TOKEN_HASH: case CSS_TOKEN_HEX_COLOR: /* ID fragment */ sel.node.type = DOM_NODE_ATTRIBUTE; sel.match.attribute |= DOM_SELECT_ATTRIBUTE_ID; /* Skip the leading '#'. */ skip_dom_scanner_token_char(token); break; case '[': sel.node.type = DOM_NODE_ATTRIBUTE; code = parse_dom_select_attribute(&sel, &scanner); if (code != DOM_CODE_OK) return code; break; case '.': token = get_next_dom_scanner_token(&scanner); if (!token || token->type != CSS_TOKEN_IDENT) return DOM_CODE_SYNTAX_ERR; sel.node.type = DOM_NODE_ATTRIBUTE; sel.match.attribute |= DOM_SELECT_ATTRIBUTE_SPACE_LIST; set_dom_string(&sel.node.string, "class", -1); copy_dom_string(&sel.node.data.attribute.value, &token->string); break; case ':': code = parse_dom_select_pseudo(select, &sel, &scanner); if (code != DOM_CODE_OK) return code; break; case '>': if (get_element_relation(&sel) != DOM_SELECT_RELATION_DESCENDANT) return DOM_CODE_SYNTAX_ERR; sel.match.element |= DOM_SELECT_RELATION_DIRECT_CHILD; break; case '+': if (get_element_relation(&sel) != DOM_SELECT_RELATION_DESCENDANT) return DOM_CODE_SYNTAX_ERR; sel.match.element |= DOM_SELECT_RELATION_DIRECT_ADJACENT; break; case '~': if (get_element_relation(&sel) != DOM_SELECT_RELATION_DESCENDANT) return DOM_CODE_SYNTAX_ERR; sel.match.element |= DOM_SELECT_RELATION_INDIRECT_ADJACENT; break; default: return DOM_CODE_SYNTAX_ERR; } skip_dom_scanner_token(&scanner); if (sel.node.type == DOM_NODE_UNKNOWN) continue; select_node = mem_calloc(1, sizeof(*select_node)); copy_struct(select_node, &sel); if (!dom_stack_is_empty(stack)) { struct dom_node *node = &select_node->node; struct dom_node *parent = get_dom_stack_top(stack)->node; struct dom_node_list **list = get_dom_node_list(parent, node); int sort = (node->type == DOM_NODE_ATTRIBUTE); int index; assertm(list != NULL, "Adding node to bad parent [%d -> %d]", node->type, parent->type); index = *list && (*list)->size > 0 && sort ? get_dom_node_map_index(*list, node) : -1; if (!add_to_dom_node_list(list, node, index)) { done_dom_node(node); return DOM_CODE_ALLOC_ERR; } node->parent = parent; } else { assert(!select->selector); select->selector = select_node; } code = push_dom_node(stack, &select_node->node); if (code != DOM_CODE_OK) return code; if (select_node->node.type != DOM_NODE_ELEMENT) pop_dom_node(stack); memset(&sel, 0, sizeof(sel)); } if (select->selector) return DOM_CODE_OK; return DOM_CODE_ERR; }
/* FIXME: Instead of walking all nodes in the tree only visit those which are * of actual interest to the contexts on the stack. */ void walk_dom_nodes(struct dom_stack *stack, struct dom_node *root) { struct dom_stack_context *context; assert(root && stack); context = add_dom_stack_context(stack, NULL, &dom_stack_walk_context_info); if (!context) return; if (push_dom_node(stack, root) != DOM_CODE_OK) return; while (!dom_stack_is_empty(stack)) { struct dom_stack_state *state = get_dom_stack_top(stack); struct dom_stack_walk_state *wstate = get_dom_stack_state_data(context, state); struct dom_node_list *list = wstate->list; struct dom_node *node = state->node; switch (node->type) { case DOM_NODE_DOCUMENT: if (!list) list = node->data.document.children; break; case DOM_NODE_ELEMENT: if (!list) list = node->data.element.map; if (list == node->data.element.children) break; if (is_dom_node_list_member(list, wstate->index) && list == node->data.element.map) break; list = node->data.element.children; break; case DOM_NODE_PROCESSING_INSTRUCTION: if (!list) list = node->data.proc_instruction.map; break; case DOM_NODE_DOCUMENT_TYPE: if (!list) list = node->data.document_type.entities; if (list == node->data.document_type.notations) break; if (is_dom_node_list_member(list, wstate->index) && list == node->data.document_type.entities) break; list = node->data.document_type.notations; break; case DOM_NODE_ATTRIBUTE: case DOM_NODE_TEXT: case DOM_NODE_CDATA_SECTION: case DOM_NODE_COMMENT: case DOM_NODE_NOTATION: case DOM_NODE_DOCUMENT_FRAGMENT: case DOM_NODE_ENTITY_REFERENCE: case DOM_NODE_ENTITY: default: break; } /* Reset list state if it is a new list */ if (list != wstate->list) { wstate->list = list; wstate->index = 0; } /* If we have next child node */ if (is_dom_node_list_member(list, wstate->index)) { struct dom_node *child = list->entries[wstate->index++]; if (push_dom_node(stack, child) == DOM_CODE_OK) continue; } pop_dom_node(stack); } done_dom_stack_context(stack, context); }