From d7a4adf48149e0d6d1ae0cc79c1a6d73aa02dd3f Mon Sep 17 00:00:00 2001 From: James Bursa Date: Sat, 1 May 2004 17:48:38 +0000 Subject: [project @ 2004-05-01 17:48:38 by bursa] CSS parsing improvements: new tokeniser using re2c, improve memory-exhaustion behaviour, plug leaks, reduce memory usage, clean up code, add source documention. svn path=/import/netsurf/; revision=806 --- css/css.c | 1093 +++++++++++++++++++++++++++++++++++++++++---------------- css/css.h | 110 +++--- css/makeenum | 7 +- css/parser.y | 260 +++++++++----- css/ruleset.c | 352 +++++++++++-------- css/scanner.l | 145 ++++---- 6 files changed, 1338 insertions(+), 629 deletions(-) (limited to 'css') diff --git a/css/css.c b/css/css.c index 9745ed281..72bc32d40 100644 --- a/css/css.c +++ b/css/css.c @@ -5,6 +5,72 @@ * Copyright 2004 James Bursa */ +/** \file + * CSS handling (implementation). + * + * See CSS 2.1 chapter 5 for the terms used here. + * + * CSS style sheets are stored as a hash table mapping selectors to styles. + * Selectors are hashed by the type selector of the last simple + * selector in the selector. The universal selector is hashed to + * chain 0. + * + * A simple selector is a struct css_selector with type + * CSS_SELECTOR_ELEMENT. The data field is the type selector, or 0 for + * the universal selector. Any attribute selectors, ID + * selectors, or pseudo-classes form a linked list of + * css_selector hanging from detail. + * + * A selector is a linked list by the combiner field of these simple + * selectors, in reverse order that they appear in the concrete syntax. The last + * simple selector is first, then the previous one is linked at combiner and has + * relationship comb to the last, etc. + * + * Selectors are then linked in each hash chain by next, in order of increasing + * specificity. + * + * As an example, the stylesheet + * \code + * th { [1] } + * div#id1 > h4.class1 { [2] } + * center * { [3] } \endcode + * + * would result in a struct css_stylesheet (content::data.css.css) like this + * \dot + * digraph example { + * node [shape=record, fontname=Helvetica, fontsize=9]; + * edge [fontname=Helvetica, fontsize=9]; + * css -> n0 [label="rule[0]"]; + * css -> n2 [label="rule[29]"]; + * + * n0 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata 0\lcomb CSS_COMB_ANCESTOR\lspecificity 2\l"]; + * n0 -> n1 [label="combiner"]; + * n0 -> n0style [label="style"]; n0style [label="[3]"]; + * + * n1 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"center\"\lcomb CSS_COMB_NONE\lspecificity 1\l"]; + * + * n2 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"th\"\lcomb CSS_COMB_NONE\lspecificity 1\l"]; + * n2 -> n3 [label="next"]; + * n2 -> n2style [label="style"]; n2style [label="[1]"]; + * + * n3 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"h4\"\lcomb CSS_COMB_PARENT\lspecificity 0x10102\l"]; + * n3 -> n4 [label="detail"]; + * n3 -> n5 [label="combiner"]; + * n3 -> n3style [label="style"]; n3style [label="[2]"]; + * + * n4 [label="css_selector\ntype CSS_SELECTOR_CLASS\ldata \"class1\"\lcomb CSS_COMB_NONE\lspecificity 0x100\l"]; + * + * n5 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"div\"\lcomb CSS_COMB_NONE\lspecificity 0x10001\l"]; + * n5 -> n6 [label="detail"]; + * + * n6 [label="css_selector\ntype CSS_SELECTOR_ID\ldata \"#id1\"\lcomb CSS_COMB_NONE\lspecificity 0x10000\l"]; + * } + * \enddot + * + * (any fields not shown are 0). In this example the first two rules happen to + * have hashed to the same value. + */ + #include #include #include @@ -24,19 +90,17 @@ #include "netsurf/utils/url.h" #include "netsurf/utils/utils.h" -/** - * internal structures - */ - -struct decl { - unsigned long score; - struct rule * rule; -}; static void css_atimport_callback(content_msg msg, struct content *css, void *p1, void *p2, const char *error); -static bool css_match_rule(struct css_node *rule, xmlNode *element); - +static bool css_match_rule(struct css_selector *rule, xmlNode *element); +static bool css_match_detail(const struct css_selector *detail, + xmlNode *element); +static void css_dump_length(const struct css_length * const length); +static void css_dump_selector(const struct css_selector *r); + +/** Default style for a document. These are the 'Initial values' from the + * spec. */ const struct css_style css_base_style = { 0xffffff, { { 0x000000, { CSS_BORDER_WIDTH_LENGTH, @@ -76,6 +140,7 @@ const struct css_style css_base_style = { CSS_WHITE_SPACE_NORMAL }; +/** Style with no values set. */ const struct css_style css_empty_style = { CSS_COLOR_INHERIT, { { CSS_COLOR_INHERIT, { CSS_BORDER_WIDTH_INHERIT, @@ -115,6 +180,8 @@ const struct css_style css_empty_style = { CSS_WHITE_SPACE_INHERIT }; +/** Default style for an element. These should be INHERIT if 'Inherited' is yes, + * and the 'Initial value' otherwise. */ const struct css_style css_blank_style = { TRANSPARENT, { { 0x000000, { CSS_BORDER_WIDTH_LENGTH, @@ -155,27 +222,27 @@ const struct css_style css_blank_style = { }; +/** + * Convert a CONTENT_CSS for use. + */ int css_convert(struct content *c, unsigned int width, unsigned int height) { - char *source_data; + unsigned char *source_data; + unsigned char *current, *end, *token_text; unsigned int i; int token; - int error; void *parser; - struct parse_params param = {0, c, 0, false}; - yyscan_t lexer; - YY_BUFFER_STATE buffer; + struct css_parser_params param = {false, c, 0, false, false}; + struct css_parser_token token_data; c->data.css.css = malloc(sizeof *c->data.css.css); parser = css_parser_Alloc(malloc); - error = css_lex_init(&lexer); - source_data = realloc(c->source_data, c->source_size + 2); + source_data = realloc(c->source_data, c->source_size + 10); - if (!c->data.css.css || !parser || error || !source_data) { + if (!c->data.css.css || !parser || !source_data) { free(c->data.css.css); - free(parser); - css_lex_destroy(lexer); + css_parser_Free(parser, free); return 1; } @@ -187,27 +254,33 @@ int css_convert(struct content *c, unsigned int width, unsigned int height) c->active = 0; c->source_data = source_data; - source_data[c->source_size] = 0; - source_data[c->source_size + 1] = 0; - /** \todo handle errors from the lexer (YY_FATAL_ERROR etc.) */ - buffer = css__scan_buffer(source_data, c->source_size + 2, lexer); - assert(buffer); - while ((token = css_lex(lexer))) { - css_parser_(parser, token, - xstrdup(css_get_text(lexer)), - ¶m); + for (i = 0; i != 10; i++) + source_data[c->source_size + i] = 0; + + current = source_data; + end = source_data + c->source_size; + while (current < end && (token = css_tokenise(¤t, end + 10, + &token_text))) { + token_data.text = token_text; + token_data.length = current - token_text; + printf("%i \"%.*s\"\n", token, token_data.length, + token_data.text); + css_parser_(parser, token, token_data, ¶m); if (param.syntax_error) { - int line = css_get_lineno(lexer); - LOG(("syntax error near line %i", line)); + LOG(("syntax error near offset %i", + token_text - source_data)); param.syntax_error = false; + } else if (param.memory_error) { + LOG(("out of memory")); + break; } } - css__delete_buffer(buffer, lexer); - css_parser_(parser, 0, 0, ¶m); + css_parser_(parser, 0, token_data, ¶m); css_parser_Free(parser, free); - css_lex_destroy(lexer); + if (param.memory_error) + return 1; /*css_dump_stylesheet(c->data.css.css);*/ @@ -251,17 +324,21 @@ void css_revive(struct content *c, unsigned int width, unsigned int height) } +/** + * Destroy a CONTENT_CSS and free all resources it owns. + */ + void css_destroy(struct content *c) { unsigned int i; - struct css_node *r; + struct css_selector *r; for (i = 0; i != HASH_SIZE; i++) { for (r = c->data.css.css->rule[i]; r != 0; r = r->next) - xfree(r->style); - css_free_node(c->data.css.css->rule[i]); + free(r->style); + css_free_selector(c->data.css.css->rule[i]); } - xfree(c->data.css.css); + free(c->data.css.css); /* imported stylesheets */ for (i = 0; i != c->data.css.import_count; i++) @@ -270,24 +347,32 @@ void css_destroy(struct content *c) content_remove_user(c->data.css.import_content[i], css_atimport_callback, c, (void*)i); } - xfree(c->data.css.import_url); - xfree(c->data.css.import_content); + free(c->data.css.import_url); + free(c->data.css.import_content); } /** - * parser support functions + * Create a new struct css_node. + * + * \param type type of node + * \param data string for data, not copied + * \param data_length length of data + * \return allocated node, or 0 if memory exhausted + * + * Used by the parser. */ -struct css_node * css_new_node(css_node_type type, char *data, - struct css_node *left, struct css_node *right) +struct css_node * css_new_node(css_node_type type, + const char *data, unsigned int data_length) { - struct css_node *node = xcalloc(1, sizeof(*node)); + struct css_node *node = malloc(sizeof *node); + if (!node) + return 0; node->type = type; node->data = data; - node->data2 = 0; - node->left = left; - node->right = right; + node->data_length = data_length; + node->value = 0; node->next = 0; node->comb = CSS_COMB_NONE; node->style = 0; @@ -295,65 +380,144 @@ struct css_node * css_new_node(css_node_type type, char *data, return node; } + +/** + * Free a struct css_node recursively. + * + * \param node css_node to free + * + * Used by the parser. + */ + void css_free_node(struct css_node *node) { - if (node == 0) + if (!node) return; - if (node->left != 0) - css_free_node(node->left); - if (node->right != 0) - css_free_node(node->right); - if (node->next != 0) + if (node->value) + css_free_node(node->value); + if (node->next) css_free_node(node->next); - if (node->data != 0) - free(node->data); free(node); } -char *css_unquote(char *s) + +/** + * Create a new struct css_selector. + * + * \param type type of selector + * \param data string for data, not copied + * \param data_length length of data + * \return allocated selector, or 0 if memory exhausted + */ + +struct css_selector * css_new_selector(css_selector_type type, + const char *data, unsigned int data_length) { - unsigned int len = strlen(s); - memmove(s, s + 1, len); - s[len - 2] = 0; - return s; + struct css_selector *node = malloc(sizeof *node); + if (!node) + return 0; + node->type = type; + node->data = data; + node->data_length = data_length; + node->data2 = 0; + node->detail = 0; + node->combiner = 0; + node->next = 0; + node->comb = CSS_COMB_NONE; + node->style = 0; + node->specificity = 0; + return node; } +/** + * Free a struct css_selector recursively. + * + * \param node css_selector to free + */ + +void css_free_selector(struct css_selector *node) +{ + if (!node) + return; + if (node->detail) + css_free_selector(node->detail); + if (node->combiner) + css_free_selector(node->combiner); + if (node->next) + css_free_selector(node->next); + free(node); +} + + +/** + * Process an @import rule. + */ + void css_atimport(struct content *c, struct css_node *node) { - char *s, *url, *url1; - int string = 0, screen = 1; + const char *s; + char *t, *url, *url1; + bool string = false, screen = true; unsigned int i; + char **import_url; + struct content **import_content; LOG(("@import rule")); + import_url = realloc(c->data.css.import_url, + (c->data.css.import_count + 1) * + sizeof(*c->data.css.import_url)); + if (!import_url) { + /** \todo report to user */ + return; + } + c->data.css.import_url = import_url; + + import_content = realloc(c->data.css.import_content, + (c->data.css.import_count + 1) * + sizeof(*c->data.css.import_content)); + if (!import_content) { + /** \todo report to user */ + return; + } + c->data.css.import_content = import_content; + /* uri(...) or "..." */ switch (node->type) { case CSS_NODE_URI: - LOG(("URI '%s'", node->data)); + LOG(("URI '%.*s'", node->data_length, node->data)); for (s = node->data + 4; *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' || *s == '\f'; s++) ; if (*s == '\'' || *s == '"') { - string = 1; + string = true; s++; } - url = xstrdup(s); - for (s = url + strlen(url) - 2; - *s == ' ' || *s == '\t' || *s == '\r' || - *s == '\n' || *s == '\f'; - s--) + url = strndup(s, node->data_length - (s - node->data)); + if (!url) { + /** \todo report to user */ + return; + } + for (t = url + strlen(url) - 2; + *t == ' ' || *t == '\t' || *t == '\r' || + *t == '\n' || *t == '\f'; + t--) ; if (string) - *s = 0; + *t = 0; else - *(s + 1) = 0; + *(t + 1) = 0; break; case CSS_NODE_STRING: - LOG(("STRING '%s'", node->data)); - url = xstrdup(node->data); + LOG(("STRING '%.*s'", node->data_length, node->data)); + url = strndup(node->data, node->data_length); + if (!url) { + /** \todo report to user */ + return; + } break; default: return; @@ -361,14 +525,17 @@ void css_atimport(struct content *c, struct css_node *node) /* media not specified, 'screen', or 'all' */ for (node = node->next; node != 0; node = node->next) { - screen = 0; + screen = false; if (node->type != CSS_NODE_IDENT) { free(url); return; } LOG(("medium '%s'", node->data)); - if (strcmp(node->data, "screen") == 0 || strcmp(node->data, "all") == 0) { - screen = 1; + if ((node->data_length == 6 && + strncmp(node->data, "screen", 6) == 0) || + (node->data_length == 3 && + strncmp(node->data, "all", 3) == 0)) { + screen = true; break; } node = node->next; @@ -390,11 +557,6 @@ void css_atimport(struct content *c, struct css_node *node) /* start the fetch */ c->data.css.import_count++; - c->data.css.import_url = xrealloc(c->data.css.import_url, - c->data.css.import_count * sizeof(*c->data.css.import_url)); - c->data.css.import_content = xrealloc(c->data.css.import_content, - c->data.css.import_count * sizeof(*c->data.css.import_content)); - i = c->data.css.import_count - 1; c->data.css.import_url[i] = url1; c->data.css.import_content[i] = fetchcache( @@ -415,6 +577,10 @@ void css_atimport(struct content *c, struct css_node *node) } +/** + * Fetchcache callback for imported stylesheets. + */ + void css_atimport_callback(content_msg msg, struct content *css, void *p1, void *p2, const char *error) { @@ -451,7 +617,12 @@ void css_atimport_callback(content_msg msg, struct content *css, case CONTENT_MSG_REDIRECT: c->active--; free(c->data.css.import_url[i]); - c->data.css.import_url[i] = xstrdup(error); + c->data.css.import_url[i] = strdup(error); + if (!c->data.css.import_url[i]) { + /** \todo report to user */ + c->error = 1; + return; + } c->data.css.import_content[i] = fetchcache( c->data.css.import_url[i], c->url, css_atimport_callback, c, (void*)i, css->width, css->height, true @@ -475,13 +646,19 @@ void css_atimport_callback(content_msg msg, struct content *css, /** * Find the style which applies to an element. + * + * \param css content of type CONTENT_CSS + * \param element element in xml tree to match + * \param style style to update + * + * The style is updated with any rules that match the element. */ void css_get_style(struct content *css, xmlNode *element, struct css_style *style) { struct css_stylesheet *stylesheet = css->data.css.css; - struct css_node *rule; + struct css_selector *rule; unsigned int hash, i; /* imported stylesheets */ @@ -491,7 +668,8 @@ void css_get_style(struct content *css, xmlNode *element, element, style); /* match rules which end with the same element */ - hash = css_hash((char *) element->name); + hash = css_hash((const char *) element->name, + strlen((const char *) element->name)); for (rule = stylesheet->rule[hash]; rule; rule = rule->next) if (css_match_rule(rule, element)) css_merge(style, rule->style); @@ -507,110 +685,32 @@ void css_get_style(struct content *css, xmlNode *element, * Determine if a rule applies to an element. */ -bool css_match_rule(struct css_node *rule, xmlNode *element) +bool css_match_rule(struct css_selector *rule, xmlNode *element) { - bool match; - char *s, *word, *space; - unsigned int i; - struct css_node *detail; + struct css_selector *detail; xmlNode *anc, *prev; assert(element->type == XML_ELEMENT_NODE); - if (rule->data && strcasecmp(rule->data, (char *) element->name) != 0) + if (rule->data && (rule->data_length != + strlen((const char *) element->name) || + strncasecmp(rule->data, (const char *) element->name, + rule->data_length) != 0)) return false; - for (detail = rule->left; detail; detail = detail->next) { - s = 0; - match = false; - switch (detail->type) { - case CSS_NODE_ID: - s = (char *) xmlGetProp(element, (const xmlChar *) "id"); - if (s && strcasecmp(detail->data + 1, s) == 0) - match = true; - break; - - case CSS_NODE_CLASS: - s = (char *) xmlGetProp(element, (const xmlChar *) "class"); - if (s) { - word = s; - do { - space = strchr(word, ' '); - if (space) - *space = 0; - if (strcasecmp(word, detail->data) == 0) { - match = true; - break; - } - word = space + 1; - } while (space); - } - break; - - case CSS_NODE_ATTRIB: - /* matches if an attribute is present */ - s = (char *) xmlGetProp(element, (const xmlChar *) detail->data); - if (s) - match = true; - break; - - case CSS_NODE_ATTRIB_EQ: - /* matches if an attribute has a certain value */ - s = (char *) xmlGetProp(element, (const xmlChar *) detail->data); - if (s && strcasecmp(detail->data2, s) == 0) - match = true; - break; - - case CSS_NODE_ATTRIB_INC: - /* matches if one of the space separated words - * in the attribute is equal */ - s = (char *) xmlGetProp(element, (const xmlChar *) detail->data); - if (s) { - word = s; - do { - space = strchr(word, ' '); - if (space) - *space = 0; - if (strcasecmp(word, detail->data2) == 0) { - match = true; - break; - } - word = space + 1; - } while (space); - } - break; - - case CSS_NODE_ATTRIB_DM: - /* matches if a prefix up to a hyphen matches */ - s = (char *) xmlGetProp(element, (const xmlChar *) detail->data); - if (s) { - i = strlen(detail->data2); - if (strncasecmp(detail->data2, s, i) == 0 && - (s[i] == '-' || s[i] == 0)) - match = true; - } - break; - - case CSS_NODE_PSEUDO: - break; - - default: - assert(0); - } - if (s) - xmlFree(s); - if (!match) + for (detail = rule->detail; detail; detail = detail->next) { + if (!css_match_detail(detail, element)) return false; } - if (!rule->right) + if (!rule->combiner) return true; switch (rule->comb) { case CSS_COMB_ANCESTOR: for (anc = element->parent; anc; anc = anc->parent) if (anc->type == XML_ELEMENT_NODE && - css_match_rule(rule->right, anc)) + css_match_rule(rule->combiner, anc)) return true; break; @@ -621,7 +721,7 @@ bool css_match_rule(struct css_node *rule, xmlNode *element) ; if (!prev) return false; - return css_match_rule(rule->right, prev); + return css_match_rule(rule->combiner, prev); break; case CSS_COMB_PARENT: @@ -631,7 +731,7 @@ bool css_match_rule(struct css_node *rule, xmlNode *element) ; if (!anc) return false; - return css_match_rule(rule->right, anc); + return css_match_rule(rule->combiner, anc); break; default: @@ -642,173 +742,468 @@ bool css_match_rule(struct css_node *rule, xmlNode *element) } +/** + * Determine if a selector detail matches an element. + * + * \param detail a css_selector of type other than CSS_SELECTOR_ELEMENT + * \param element element in xml tree to match + * \return true if the selector matches the element + */ + +bool css_match_detail(const struct css_selector *detail, + xmlNode *element) +{ + bool match = false; + char *s = 0; + char *space, *word; + unsigned int length; + + switch (detail->type) { + case CSS_SELECTOR_ID: + s = (char *) xmlGetProp(element, + (const xmlChar *) "id"); + if (s && strlen(s) == detail->data_length && + strncasecmp(detail->data, s, + detail->data_length) == 0) + match = true; + break; + + case CSS_SELECTOR_CLASS: + s = (char *) xmlGetProp(element, + (const xmlChar *) "class"); + if (!s) + break; + word = s; + do { + space = strchr(word, ' '); + if (space) + length = space - word; + else + length = strlen(word); + if (length == detail->data_length && + strncasecmp(word, detail->data, + length) == 0) { + match = true; + break; + } + word = space + 1; + } while (space); + break; + + case CSS_SELECTOR_ATTRIB: + /* matches if an attribute is present */ + word = strndup(detail->data, detail->data_length); + if (!word) { + /** \todo report to user */ + return false; + } + s = (char *) xmlGetProp(element, + (const xmlChar *) word); + free(word); + if (s) + match = true; + break; + + case CSS_SELECTOR_ATTRIB_EQ: + /* matches if an attribute has a certain value*/ + word = strndup(detail->data, detail->data_length); + if (!word) { + /** \todo report to user */ + return false; + } + s = (char *) xmlGetProp(element, + (const xmlChar *) word); + free(word); + if (s && strlen(s) == detail->data2_length && + strncasecmp(detail->data2, s, + detail->data2_length) == 0) + match = true; + break; + + case CSS_SELECTOR_ATTRIB_INC: + /* matches if one of the space separated words + * in the attribute is equal */ + word = strndup(detail->data, + detail->data_length); + if (!word) { + /** \todo report to user */ + return false; + } + s = (char *) xmlGetProp(element, + (const xmlChar *) word); + free(word); + if (!s) + break; + word = s; + do { + space = strchr(word, ' '); + if (space) + length = space - word; + else + length = strlen(word); + if (length == detail->data2_length && + strncasecmp(word, detail->data2, + length) == 0) { + match = true; + break; + } + word = space + 1; + } while (space); + break; + + case CSS_SELECTOR_ATTRIB_DM: + /* matches if a prefix up to a hyphen matches */ + word = strndup(detail->data, + detail->data_length); + if (!word) { + /** \todo report to user */ + return false; + } + s = (char *) xmlGetProp(element, + (const xmlChar *) word); + free(word); + if (!s) + break; + length = detail->data2_length; + if (strncasecmp(detail->data2, s, length) == 0 && + (s[length] == '-' || s[length] == 0)) + match = true; + break; + + case CSS_SELECTOR_PSEUDO: + break; + + default: + assert(0); + } + + if (s) + xmlFree(s); + + return match; +} + + +/** + * Parse a stand-alone CSS property list. + * + * \param style css_style to update + * \param str property list, as found in HTML style attributes + */ + void css_parse_property_list(struct css_style * style, char * str) { - yyscan_t lexer; - void *parser; - YY_BUFFER_STATE buffer; + unsigned char *source_data; + unsigned char *current, *end, *token_text; + size_t length; + unsigned int i; int token; - struct parse_params param = {1, 0, 0, false}; + void *parser; + struct css_parser_params param = {true, 0, 0, false, false}; + struct css_parser_token token_data; + const struct css_parser_token token_start = { "{", 1 }; + const struct css_parser_token token_end = { "}", 1 }; - css_lex_init(&lexer); - parser = css_parser_Alloc((void*)malloc); - css_parser_(parser, LBRACE, xstrdup("{"), ¶m); + length = strlen(str); - buffer = css__scan_string(str, lexer); - while ((token = css_lex(lexer))) { - css_parser_(parser, token, - xstrdup(css_get_text(lexer)), - ¶m); + parser = css_parser_Alloc(malloc); + source_data = malloc(length + 10); + + if (!parser || !source_data) { + free(parser); + css_parser_Free(parser, free); + return; } - css__delete_buffer(buffer, lexer); - css_parser_(parser, RBRACE, xstrdup("}"), ¶m); - css_parser_(parser, 0, 0, ¶m); + + strcpy(source_data, str); + for (i = 0; i != 10; i++) + source_data[length + i] = 0; + + css_parser_(parser, LBRACE, token_start, ¶m); + + current = source_data; + end = source_data + strlen(str); + while (current < end && (token = css_tokenise(¤t, end + 10, + &token_text))) { + token_data.text = token_text; + token_data.length = current - token_text; + css_parser_(parser, token, token_data, ¶m); + if (param.syntax_error) { + LOG(("syntax error near offset %i", + token_text - source_data)); + param.syntax_error = false; + } else if (param.memory_error) { + LOG(("out of memory")); + break; + } + } + css_parser_(parser, RBRACE, token_end, ¶m); + css_parser_(parser, 0, token_data, ¶m); css_parser_Free(parser, free); - css_lex_destroy(lexer); + + if (param.memory_error) { + css_free_node(param.declaration); + return; + } css_add_declarations(style, param.declaration); css_free_node(param.declaration); -} + free(source_data); +} /** - * dump a style + * Dump a css_style to stderr in CSS-like syntax. */ -static void dump_length(const struct css_length * const length) -{ - fprintf(stderr, "%g%s", length->value, - css_unit_name[length->unit]); -} - void css_dump_style(const struct css_style * const style) { unsigned int i; fprintf(stderr, "{ "); - fprintf(stderr, "background-color: #%lx; ", style->background_color); - fprintf(stderr, "clear: %s; ", css_clear_name[style->clear]); - fprintf(stderr, "color: #%lx; ", style->color); - fprintf(stderr, "cursor: %s", css_cursor_name[style->cursor]); - fprintf(stderr, "display: %s; ", css_display_name[style->display]); - fprintf(stderr, "float: %s; ", css_float_name[style->float_]); - fprintf(stderr, "font: %s %s ", css_font_style_name[style->font_style], - css_font_weight_name[style->font_weight]); - switch (style->font_size.size) { - case CSS_FONT_SIZE_ABSOLUTE: fprintf(stderr, "[%g]", style->font_size.value.absolute); break; - case CSS_FONT_SIZE_LENGTH: dump_length(&style->font_size.value.length); break; - case CSS_FONT_SIZE_PERCENT: fprintf(stderr, "%g%%", style->font_size.value.percent); break; - case CSS_FONT_SIZE_INHERIT: fprintf(stderr, "inherit"); break; - default: fprintf(stderr, "UNKNOWN"); break; + +#define DUMP_COLOR(z, s) \ + if (style->z != css_empty_style.z) { \ + if (style->z == TRANSPARENT) \ + fprintf(stderr, s ": transparent; "); \ + else if (style->z == CSS_COLOR_NONE) \ + fprintf(stderr, s ": none; "); \ + else \ + fprintf(stderr, s ": #%.6lx; ", style->z); \ } - fprintf(stderr, "/"); - switch (style->line_height.size) { - case CSS_LINE_HEIGHT_ABSOLUTE: fprintf(stderr, "[%g]", style->line_height.value.absolute); break; - case CSS_LINE_HEIGHT_LENGTH: dump_length(&style->line_height.value.length); break; - case CSS_LINE_HEIGHT_PERCENT: fprintf(stderr, "%g%%", style->line_height.value.percent); break; - case CSS_LINE_HEIGHT_INHERIT: fprintf(stderr, "inherit"); break; - default: fprintf(stderr, "UNKNOWN"); break; + +#define DUMP_KEYWORD(z, s, n) \ + if (style->z != css_empty_style.z) \ + fprintf(stderr, s ": %s; ", n[style->z]); + + DUMP_COLOR(background_color, "background-color"); + DUMP_KEYWORD(clear, "clear", css_clear_name); + DUMP_COLOR(color, "color"); + DUMP_KEYWORD(cursor, "cursor", css_cursor_name); + DUMP_KEYWORD(display, "display", css_display_name); + DUMP_KEYWORD(float_, "float", css_float_name); + + if (style->font_style != css_empty_style.font_style || + style->font_weight != css_empty_style.font_weight || + style->font_size.size != + css_empty_style.font_size.size || + style->line_height.size != + css_empty_style.line_height.size || + style->font_family != css_empty_style.font_family || + style->font_variant != css_empty_style.font_variant) { + fprintf(stderr, "font: %s %s ", + css_font_style_name[style->font_style], + css_font_weight_name[style->font_weight]); + switch (style->font_size.size) { + case CSS_FONT_SIZE_ABSOLUTE: + fprintf(stderr, "[%g]", + style->font_size.value.absolute); + break; + case CSS_FONT_SIZE_LENGTH: + css_dump_length(&style->font_size.value.length); + break; + case CSS_FONT_SIZE_PERCENT: + fprintf(stderr, "%g%%", + style->font_size.value.percent); + break; + case CSS_FONT_SIZE_INHERIT: + fprintf(stderr, "inherit"); + break; + default: + fprintf(stderr, "UNKNOWN"); + break; + } + fprintf(stderr, "/"); + switch (style->line_height.size) { + case CSS_LINE_HEIGHT_ABSOLUTE: + fprintf(stderr, "[%g]", + style->line_height.value.absolute); + break; + case CSS_LINE_HEIGHT_LENGTH: + css_dump_length(&style->line_height.value.length); + break; + case CSS_LINE_HEIGHT_PERCENT: + fprintf(stderr, "%g%%", + style->line_height.value.percent); + break; + case CSS_LINE_HEIGHT_INHERIT: + fprintf(stderr, "inherit"); + break; + default: + fprintf(stderr, "UNKNOWN"); + break; + } + fprintf(stderr, " %s", + css_font_family_name[style->font_family]); + fprintf(stderr, " %s", + css_font_variant_name[style->font_variant]); + fprintf(stderr, "; "); } - fprintf(stderr, " %s", css_font_family_name[style->font_family]); - fprintf(stderr, " %s", css_font_variant_name[style->font_variant]); - fprintf(stderr, "; "); - fprintf(stderr, "height: "); - switch (style->height.height) { - case CSS_HEIGHT_AUTO: fprintf(stderr, "auto"); break; - case CSS_HEIGHT_LENGTH: dump_length(&style->height.length); break; - default: fprintf(stderr, "UNKNOWN"); break; + + if (style->height.height != css_empty_style.height.height) { + fprintf(stderr, "height: "); + switch (style->height.height) { + case CSS_HEIGHT_AUTO: + fprintf(stderr, "auto"); break; + case CSS_HEIGHT_LENGTH: + css_dump_length(&style->height.length); break; + default: + fprintf(stderr, "UNKNOWN"); break; + } + fprintf(stderr, "; "); } - fprintf(stderr, "; "); - fprintf(stderr, "margin:"); - for (i = 0; i != 4; i++) { - switch (style->margin[i].margin) { - case CSS_MARGIN_INHERIT: fprintf(stderr, " inherit"); break; - case CSS_MARGIN_LENGTH: fprintf(stderr, " "); - dump_length(&style->margin[i].value.length); break; - case CSS_MARGIN_PERCENT: fprintf(stderr, " %g%%", style->margin[i].value.percent); - case CSS_MARGIN_AUTO: fprintf(stderr, " auto"); break; - default: fprintf(stderr, "UNKNOWN"); break; + + if (style->margin[0].margin != css_empty_style.margin[0].margin || + style->margin[1].margin != css_empty_style.margin[1].margin || + style->margin[2].margin != css_empty_style.margin[2].margin || + style->margin[3].margin != css_empty_style.margin[3].margin) { + fprintf(stderr, "margin:"); + for (i = 0; i != 4; i++) { + switch (style->margin[i].margin) { + case CSS_MARGIN_INHERIT: + fprintf(stderr, " inherit"); + break; + case CSS_MARGIN_LENGTH: + fprintf(stderr, " "); + css_dump_length(&style->margin[i].value.length); + break; + case CSS_MARGIN_PERCENT: + fprintf(stderr, " %g%%", + style->margin[i].value.percent); + break; + case CSS_MARGIN_AUTO: + fprintf(stderr, " auto"); + break; + default: + fprintf(stderr, "UNKNOWN"); + break; + } } + fprintf(stderr, "; "); } - fprintf(stderr, "; "); - fprintf(stderr, "padding:"); - for (i = 0; i != 4; i++) { - switch (style->padding[i].padding) { - case CSS_PADDING_INHERIT: fprintf(stderr, " inherit"); break; - case CSS_PADDING_LENGTH: fprintf(stderr, " "); - dump_length(&style->padding[i].value.length); break; - case CSS_PADDING_PERCENT: fprintf(stderr, " %g%%", style->padding[i].value.percent); - default: fprintf(stderr, "UNKNOWN"); break; + + if (style->padding[0].padding != css_empty_style.padding[0].padding || + style->padding[1].padding != css_empty_style.padding[1].padding || + style->padding[2].padding != css_empty_style.padding[2].padding || + style->padding[3].padding != css_empty_style.padding[3].padding) { + fprintf(stderr, "padding:"); + for (i = 0; i != 4; i++) { + switch (style->padding[i].padding) { + case CSS_PADDING_INHERIT: + fprintf(stderr, " inherit"); + break; + case CSS_PADDING_LENGTH: + fprintf(stderr, " "); + css_dump_length(&style->padding[i].value.length); + break; + case CSS_PADDING_PERCENT: + fprintf(stderr, " %g%%", + style->padding[i].value.percent); + break; + default: + fprintf(stderr, "UNKNOWN"); + break; + } } + fprintf(stderr, "; "); } - fprintf(stderr, "; "); - fprintf(stderr, "text-align: %s; ", css_text_align_name[style->text_align]); - fprintf(stderr, "text-decoration:"); - switch (style->text_decoration) { - case CSS_TEXT_DECORATION_NONE: fprintf(stderr, " none"); break; - case CSS_TEXT_DECORATION_INHERIT: fprintf(stderr, " inherit"); break; - default: - if (style->text_decoration & CSS_TEXT_DECORATION_UNDERLINE) - fprintf(stderr, " underline"); - if (style->text_decoration & CSS_TEXT_DECORATION_OVERLINE) - fprintf(stderr, " overline"); - if (style->text_decoration & CSS_TEXT_DECORATION_LINE_THROUGH) - fprintf(stderr, " line-through"); - if (style->text_decoration & CSS_TEXT_DECORATION_BLINK) - fprintf(stderr, " blink"); + + DUMP_KEYWORD(text_align, "text-align", css_text_align_name); + + if (style->text_decoration != css_empty_style.text_decoration) { + fprintf(stderr, "text-decoration:"); + if (style->text_decoration == CSS_TEXT_DECORATION_NONE) + fprintf(stderr, " none"); + if (style->text_decoration == CSS_TEXT_DECORATION_INHERIT) + fprintf(stderr, " inherit"); + if (style->text_decoration & CSS_TEXT_DECORATION_UNDERLINE) + fprintf(stderr, " underline"); + if (style->text_decoration & CSS_TEXT_DECORATION_OVERLINE) + fprintf(stderr, " overline"); + if (style->text_decoration & CSS_TEXT_DECORATION_LINE_THROUGH) + fprintf(stderr, " line-through"); + if (style->text_decoration & CSS_TEXT_DECORATION_BLINK) + fprintf(stderr, " blink"); + fprintf(stderr, "; "); } - fprintf(stderr, "; "); - fprintf(stderr, "text-indent: "); - switch (style->text_indent.size) { - case CSS_TEXT_INDENT_LENGTH: dump_length(&style->text_indent.value.length); break; - case CSS_TEXT_INDENT_PERCENT: fprintf(stderr, "%g%%", style->text_indent.value.percent); break; - case CSS_TEXT_INDENT_INHERIT: fprintf(stderr, "inherit"); break; - default: fprintf(stderr, "UNKNOWN"); break; + + if (style->text_indent.size != css_empty_style.text_indent.size) { + fprintf(stderr, "text-indent: "); + switch (style->text_indent.size) { + case CSS_TEXT_INDENT_LENGTH: + css_dump_length(&style->text_indent.value.length); + break; + case CSS_TEXT_INDENT_PERCENT: + fprintf(stderr, "%g%%", + style->text_indent.value.percent); + break; + case CSS_TEXT_INDENT_INHERIT: + fprintf(stderr, "inherit"); + break; + default: + fprintf(stderr, "UNKNOWN"); + break; + } + fprintf(stderr, "; "); } - fprintf(stderr, "; "); - fprintf(stderr, "text-transform: %s; ", css_text_transform_name[style->text_transform]); - fprintf(stderr, "visibility: %s; ", css_visibility_name[style->visibility]); - fprintf(stderr, "width: "); - switch (style->width.width) { - case CSS_WIDTH_INHERIT: fprintf(stderr, "inherit"); break; - case CSS_WIDTH_AUTO: fprintf(stderr, "auto"); break; - case CSS_WIDTH_LENGTH: dump_length(&style->width.value.length); break; - case CSS_WIDTH_PERCENT: fprintf(stderr, "%g%%", style->width.value.percent); break; - default: fprintf(stderr, "UNKNOWN"); break; + + DUMP_KEYWORD(text_transform, "text-transform", css_text_transform_name); + DUMP_KEYWORD(visibility, "visibility", css_visibility_name); + + if (style->width.width != css_empty_style.width.width) { + fprintf(stderr, "width: "); + switch (style->width.width) { + case CSS_WIDTH_INHERIT: + fprintf(stderr, "inherit"); + break; + case CSS_WIDTH_AUTO: + fprintf(stderr, "auto"); + break; + case CSS_WIDTH_LENGTH: + css_dump_length(&style->width.value.length); + break; + case CSS_WIDTH_PERCENT: + fprintf(stderr, "%g%%", + style->width.value.percent); + break; + default: + fprintf(stderr, "UNKNOWN"); + break; + } + fprintf(stderr, "; "); } - fprintf(stderr, "; "); - fprintf(stderr, "white-space: %s; ", css_white_space_name[style->white_space]); + + DUMP_KEYWORD(white_space, "white-space", css_white_space_name); + fprintf(stderr, "}"); } +/** + * Dump a css_length to stderr. + */ + +void css_dump_length(const struct css_length * const length) +{ + fprintf(stderr, "%g%s", length->value, css_unit_name[length->unit]); +} + + +/** + * Dump a complete css_stylesheet to stderr in CSS syntax. + */ + void css_dump_stylesheet(const struct css_stylesheet * stylesheet) { unsigned int i; - struct css_node *r, *n, *m; + struct css_selector *r; for (i = 0; i != HASH_SIZE; i++) { /*fprintf(stderr, "hash %i:\n", i);*/ for (r = stylesheet->rule[i]; r != 0; r = r->next) { - for (n = r; n != 0; n = n->right) { - if (n->data != 0) - fprintf(stderr, "%s", n->data); - for (m = n->left; m != 0; m = m->next) { - switch (m->type) { - case CSS_NODE_ID: fprintf(stderr, "%s", m->data); break; - case CSS_NODE_CLASS: fprintf(stderr, ".%s", m->data); break; - case CSS_NODE_ATTRIB: fprintf(stderr, "[%s]", m->data); break; - case CSS_NODE_ATTRIB_EQ: fprintf(stderr, "[%s=%s]", m->data, m->data2); break; - case CSS_NODE_ATTRIB_INC: fprintf(stderr, "[%s~=%s]", m->data, m->data2); break; - case CSS_NODE_ATTRIB_DM: fprintf(stderr, "[%s|=%s]", m->data, m->data2); break; - case CSS_NODE_PSEUDO: fprintf(stderr, ":%s", m->data); break; - default: fprintf(stderr, "unexpected node"); - } - } - fprintf(stderr, " "); - } - fprintf(stderr, "%lx ", r->specificity); + css_dump_selector(r); + fprintf(stderr, " <%lx> ", r->specificity); css_dump_style(r->style); fprintf(stderr, "\n"); } @@ -817,10 +1212,81 @@ void css_dump_stylesheet(const struct css_stylesheet * stylesheet) /** - * cascade styles + * Dump a css_selector to stderr in CSS syntax. + */ + +void css_dump_selector(const struct css_selector *r) +{ + struct css_selector *m; + + if (r->combiner) + css_dump_selector(r->combiner); + + switch (r->comb) { + case CSS_COMB_NONE: break; + case CSS_COMB_ANCESTOR: fprintf(stderr, " "); break; + case CSS_COMB_PARENT: fprintf(stderr, " > "); break; + case CSS_COMB_PRECEDED: fprintf(stderr, " + "); break; + } + + if (r->data) + fprintf(stderr, "%.*s", r->data_length, r->data); + else + fprintf(stderr, "*"); + + for (m = r->detail; m; m = m->next) { + switch (m->type) { + case CSS_SELECTOR_ID: + fprintf(stderr, "#%.*s", + m->data_length, m->data); + break; + case CSS_SELECTOR_CLASS: + fprintf(stderr, ".%.*s", + m->data_length, m->data); + break; + case CSS_SELECTOR_ATTRIB: + fprintf(stderr, "[%.*s]", + m->data_length, m->data); + break; + case CSS_SELECTOR_ATTRIB_EQ: + fprintf(stderr, "[%.*s=%.*s]", + m->data_length, m->data, + m->data2_length, m->data2); + break; + case CSS_SELECTOR_ATTRIB_INC: + fprintf(stderr, "[%.*s~=%.*s]", + m->data_length, m->data, + m->data2_length, m->data2); + break; + case CSS_SELECTOR_ATTRIB_DM: + fprintf(stderr, "[%.*s|=%.*s]", + m->data_length, m->data, + m->data2_length, m->data2); + break; + case CSS_SELECTOR_PSEUDO: + fprintf(stderr, ":%.*s", + m->data_length, m->data); + break; + default: + fprintf(stderr, "(unexpected detail)"); + } + } +} + + +/** + * Cascade styles. + * + * \param style css_style to modify + * \param apply css_style to cascade onto style + * + * Attributes which have the value 'inherit' in apply are unchanged in style. + * Other attributes are copied to style, calculating percentages relative to + * style where applicable. */ -void css_cascade(struct css_style * const style, const struct css_style * const apply) +void css_cascade(struct css_style * const style, + const struct css_style * const apply) { unsigned int i; float f; @@ -923,7 +1389,18 @@ void css_cascade(struct css_style * const style, const struct css_style * const } -void css_merge(struct css_style * const style, const struct css_style * const apply) +/** + * Merge styles. + * + * \param style css_style to modify + * \param apply css_style to merge onto style + * + * Attributes which have the value 'inherit' in apply are unchanged in style. + * Other attributes are copied to style, overwriting it. + */ + +void css_merge(struct css_style * const style, + const struct css_style * const apply) { unsigned int i; @@ -985,14 +1462,20 @@ void css_merge(struct css_style * const style, const struct css_style * const ap } +/** + * Calculate a hash for an element name. + * + * The hash is case-insensitive. + */ -unsigned int css_hash(const char *s) +unsigned int css_hash(const char *s, int length) { + int i; unsigned int z = 0; if (s == 0) return 0; - for (; *s != 0; s++) - z += *s & 0x1f; /* lower 5 bits, case insensitive */ + for (i = 0; i != length; i++) + z += s[i] & 0x1f; /* lower 5 bits, case insensitive */ return (z % (HASH_SIZE - 1)) + 1; } diff --git a/css/css.h b/css/css.h index 344fa4b83..f00e7121e 100644 --- a/css/css.h +++ b/css/css.h @@ -27,9 +27,6 @@ #include "libxml/HTMLparser.h" #include "css_enum.h" -/* - * structures and typedefs - */ typedef unsigned long colour; /* 0xbbggrr */ #define TRANSPARENT 0x1000000 @@ -180,8 +177,43 @@ extern const struct css_style css_blank_style; #ifdef CSS_INTERNALS +/** Type of a css_selector. */ +typedef enum { + CSS_SELECTOR_ELEMENT, + CSS_SELECTOR_ID, + CSS_SELECTOR_CLASS, + CSS_SELECTOR_ATTRIB, + CSS_SELECTOR_ATTRIB_EQ, + CSS_SELECTOR_ATTRIB_INC, + CSS_SELECTOR_ATTRIB_DM, + CSS_SELECTOR_PSEUDO, +} css_selector_type; + +/** Relationship to combiner in a css_selector. */ +typedef enum { + CSS_COMB_NONE, + CSS_COMB_ANCESTOR, + CSS_COMB_PARENT, + CSS_COMB_PRECEDED, +} css_combinator; + +/** Representation of a CSS selector. */ +struct css_selector { + css_selector_type type; + const char *data; + unsigned int data_length; + const char *data2; + unsigned int data2_length; + struct css_selector *detail; + struct css_selector *combiner; + struct css_selector *next; + css_combinator comb; + struct css_style *style; + unsigned long specificity; +}; + +/** Type of a css_node. */ typedef enum { - CSS_NODE_BLOCK, CSS_NODE_DECLARATION, CSS_NODE_IDENT, CSS_NODE_NUMBER, @@ -202,55 +234,45 @@ typedef enum { CSS_NODE_GT, CSS_NODE_PAREN, CSS_NODE_BRAC, - CSS_NODE_SELECTOR, - CSS_NODE_ID, - CSS_NODE_CLASS, - CSS_NODE_ATTRIB, - CSS_NODE_ATTRIB_EQ, - CSS_NODE_ATTRIB_INC, - CSS_NODE_ATTRIB_DM, - CSS_NODE_PSEUDO, } css_node_type; -typedef enum { - CSS_COMB_NONE, - CSS_COMB_ANCESTOR, - CSS_COMB_PARENT, - CSS_COMB_PRECEDED, -} css_combinator; - +/** A node in a CSS parse tree. */ struct css_node { css_node_type type; - char *data; - char *data2; - struct css_node *left; - struct css_node *right; + const char *data; + unsigned int data_length; + struct css_node *value; struct css_node *next; css_combinator comb; struct css_style *style; unsigned long specificity; }; -#include "netsurf/css/scanner.h" #define HASH_SIZE (47 + 1) +/** Representation of a CSS 2 style sheet. */ struct css_stylesheet { - struct css_node *rule[HASH_SIZE]; + struct css_selector *rule[HASH_SIZE]; }; -struct parse_params { - int ruleset_only; +/** Parameters to and results from the CSS parser. */ +struct css_parser_params { + bool ruleset_only; struct content *stylesheet; struct css_node *declaration; bool syntax_error; + bool memory_error; +}; + +/** Token type for the CSS parser. */ +struct css_parser_token { + const char *text; + unsigned int length; }; #endif -/* - * interface - */ struct content; @@ -260,28 +282,36 @@ void css_destroy(struct content *c); #ifdef CSS_INTERNALS -struct css_node * css_new_node(css_node_type type, char *data, - struct css_node *left, struct css_node *right); +struct css_node * css_new_node(css_node_type type, + const char *data, unsigned int data_length); void css_free_node(struct css_node *node); -char *css_unquote(char *s); +struct css_selector * css_new_selector(css_selector_type type, + const char *data, unsigned int data_length); +void css_free_selector(struct css_selector *node); void css_atimport(struct content *c, struct css_node *node); void css_add_ruleset(struct content *c, - struct css_node *selector, + struct css_selector *selector, + struct css_node *declaration); +void css_add_declarations(struct css_style *style, struct css_node *declaration); -void css_add_declarations(struct css_style *style, struct css_node *declaration); -unsigned int css_hash(const char *s); +unsigned int css_hash(const char *s, int length); + +int css_tokenise(unsigned char **buffer, unsigned char *end, + unsigned char **token_text); void css_parser_Trace(FILE *TraceFILE, char *zTracePrompt); void *css_parser_Alloc(void *(*mallocProc)(/*size_t*/int)); void css_parser_Free(void *p, void (*freeProc)(void*)); -void css_parser_(void *yyp, int yymajor, char* yyminor, - struct parse_params *param); +void css_parser_(void *yyp, int yymajor, struct css_parser_token yyminor, + struct css_parser_params *param); #endif void css_get_style(struct content *c, xmlNode *n, struct css_style * style); -void css_cascade(struct css_style * const style, const struct css_style * const apply); -void css_merge(struct css_style * const style, const struct css_style * const apply); +void css_cascade(struct css_style * const style, + const struct css_style * const apply); +void css_merge(struct css_style * const style, + const struct css_style * const apply); void css_parse_property_list(struct css_style * style, char * str); colour named_colour(const char *name); void css_dump_style(const struct css_style * const style); diff --git a/css/makeenum b/css/makeenum index 4950cc14d..004313d93 100755 --- a/css/makeenum +++ b/css/makeenum @@ -28,17 +28,18 @@ while (<>) { print H join ",\n ${uc_name}_", @uc_enum; print H ",\n ${uc_name}_UNKNOWN\n"; print H "} $name;\n"; - print H "$name ${name}_parse(const char * const s);\n\n"; + print H "$name ${name}_parse(const char * const s, int length);\n\n"; print C "/**\n * $name\n */\n\n"; print C "const char * const ${name}_name[] = {\n \""; print C join "\",\n \"", @enum; print C "\"\n};\n\n"; - print C "$name ${name}_parse(const char * const s)\n{\n"; + print C "$name ${name}_parse(const char * const s, int length)\n{\n"; foreach $x (@enum) { $ux = uc $x; $ux =~ s/-/_/g; - print C " if (strcasecmp(s, \"$x\") == 0) return ${uc_name}_$ux;\n"; + $len = length $x; + print C " if (length == $len && strncasecmp(s, \"$x\", $len) == 0) return ${uc_name}_$ux;\n"; } print C " return ${uc_name}_UNKNOWN;\n}\n\n"; } diff --git a/css/parser.y b/css/parser.y index b373ad585..81a2772c1 100644 --- a/css/parser.y +++ b/css/parser.y @@ -2,12 +2,12 @@ * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license - * Copyright 2003 James Bursa + * Copyright 2004 James Bursa */ -/* +/** \file -CSS parser using the lemon parser generator +CSS parser using the lemon parser generator. see CSS2 Specification, chapter 4 http://www.w3.org/TR/REC-CSS2/syndata.html, @@ -40,9 +40,10 @@ statement ::= at_rule. at_rule ::= ATKEYWORD any_list block. at_rule ::= ATKEYWORD(A) any_list(B) SEMI. - { if (strcasecmp(A, "@import") == 0) + { if ((A.length == 7) && (strncasecmp(A.text, "@import", 7) == 0) + && B) css_atimport(param->stylesheet, B); - free(A); css_free_node(B); } + css_free_node(B); } block ::= LBRACE block_body RBRACE. block_body ::= . @@ -52,7 +53,10 @@ block_body ::= block_body ATKEYWORD. block_body ::= block_body SEMI. ruleset ::= selector_list(A) LBRACE declaration_list(B) RBRACE. - { css_add_ruleset(param->stylesheet, A, B); + { if (A && B) + css_add_ruleset(param->stylesheet, A, B); + else + css_free_selector(A); css_free_node(B); } ruleset ::= LBRACE declaration_list(A) RBRACE. /* this form of ruleset not used in CSS2 @@ -65,13 +69,28 @@ ruleset ::= any_list_1(A) LBRACE declaration_list(B) RBRACE. selector_list(A) ::= selector(B). { A = B; } selector_list(A) ::= selector_list(B) COMMA selector(C). - { C->next = B; A = C; } + { if (B && C) { + C->next = B; + A = C; + } else { + css_free_selector(B); + css_free_selector(C); + A = 0; + } } selector(A) ::= simple_selector(B). { A = B; } selector(A) ::= selector(B) css_combinator(C) simple_selector(D). - { D->right = B; D->comb = C; A = D; - A->specificity += B->specificity; } + { if (B && D) { + D->combiner = B; + D->comb = C; + D->specificity += B->specificity; + A = D; + } else { + css_free_selector(B); + css_free_selector(D); + A = 0; + } } css_combinator(A) ::= . { A = CSS_COMB_ANCESTOR; } @@ -81,64 +100,106 @@ css_combinator(A) ::= GT. { A = CSS_COMB_PARENT; } simple_selector(A) ::= element_name(B) detail_list(C). - { A = css_new_node(CSS_NODE_SELECTOR, B, C, 0); - A->specificity = 1 + C->specificity; } + { if (C && (A = css_new_selector(CSS_SELECTOR_ELEMENT, + B.text, B.length))) { + A->detail = C; + A->specificity = 1 + C->specificity; + } else { + param->memory_error = true; + css_free_selector(C); + A = 0; + } } simple_selector(A) ::= element_name(B). - { A = css_new_node(CSS_NODE_SELECTOR, B, 0, 0); - A->specificity = 1; } + { if ((A = css_new_selector(CSS_SELECTOR_ELEMENT, + B.text, B.length))) + A->specificity = 1; + else + param->memory_error = true; + } simple_selector(A) ::= detail_list(C). - { A = css_new_node(CSS_NODE_SELECTOR, 0, C, 0); - A->specificity = C->specificity; } + { if (C && (A = css_new_selector(CSS_SELECTOR_ELEMENT, 0, 0))) { + A->detail = C; + A->specificity = C->specificity; + } else { + param->memory_error = true; + css_free_selector(C); + A = 0; + } } element_name(A) ::= IDENT(B). { A = B; } element_name(A) ::= ASTERISK. - { A = 0; } + { A.text = 0; } detail_list(A) ::= detail(B). { A = B; } detail_list(A) ::= detail(B) detail_list(C). - { A = B; A->specificity += C->specificity; A->next = C; } + { if (B && C) { + B->specificity += C->specificity; + B->next = C; + A = B; + } else { + css_free_selector(B); + css_free_selector(C); + A = 0; + } } detail(A) ::= HASH(B). - { A = css_new_node(CSS_NODE_ID, B, 0, 0); - A->specificity = 0x10000; } + { A = css_new_selector(CSS_SELECTOR_ID, B.text+1, B.length-1); + if (A) A->specificity = 0x10000; + else param->memory_error = true; } detail(A) ::= DOT IDENT(B). - { A = css_new_node(CSS_NODE_CLASS, B, 0, 0); - A->specificity = 0x100; } + { A = css_new_selector(CSS_SELECTOR_CLASS, B.text, B.length); + if (A) A->specificity = 0x100; + else param->memory_error = true; } detail(A) ::= LBRAC IDENT(B) RBRAC. - { A = css_new_node(CSS_NODE_ATTRIB, B, 0, 0); - A->specificity = 0x100; } + { A = css_new_selector(CSS_SELECTOR_ATTRIB, B.text, B.length); + if (A) A->specificity = 0x100; + else param->memory_error = true; } detail(A) ::= LBRAC IDENT(B) EQUALS IDENT(C) RBRAC. - { A = css_new_node(CSS_NODE_ATTRIB_EQ, B, 0, 0); A->data2 = C; - A->specificity = 0x100; } + { A = css_new_selector(CSS_SELECTOR_ATTRIB_EQ, B.text, B.length); + if (A) { A->data2 = C.text; A->data2_length = C.length; + A->specificity = 0x100; } + else param->memory_error = true; } detail(A) ::= LBRAC IDENT(B) EQUALS STRING(C) RBRAC. - { A = css_new_node(CSS_NODE_ATTRIB_EQ, B, 0, 0); A->data2 = css_unquote(C); - A->specificity = 0x100; } + { A = css_new_selector(CSS_SELECTOR_ATTRIB_EQ, B.text, B.length); + if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2; + A->specificity = 0x100; } + else param->memory_error = true; } detail(A) ::= LBRAC IDENT(B) INCLUDES IDENT(C) RBRAC. - { A = css_new_node(CSS_NODE_ATTRIB_INC, B, 0, 0); A->data2 = C; - A->specificity = 0x100; } + { A = css_new_selector(CSS_SELECTOR_ATTRIB_INC, B.text, B.length); + if (A) { A->data2 = C.text; A->data2_length = C.length; + A->specificity = 0x100; } + else param->memory_error = true; } detail(A) ::= LBRAC IDENT(B) INCLUDES STRING(C) RBRAC. - { A = css_new_node(CSS_NODE_ATTRIB_INC, B, 0, 0); A->data2 = css_unquote(C); - A->specificity = 0x100; } + { A = css_new_selector(CSS_SELECTOR_ATTRIB_INC, B.text, B.length); + if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2; + A->specificity = 0x100; } + else param->memory_error = true; } detail(A) ::= LBRAC IDENT(B) DASHMATCH IDENT(C) RBRAC. - { A = css_new_node(CSS_NODE_ATTRIB_DM, B, 0, 0); A->data2 = C; - A->specificity = 0x100; } + { A = css_new_selector(CSS_SELECTOR_ATTRIB_DM, B.text, B.length); + if (A) { A->data2 = C.text; A->data2_length = C.length; + A->specificity = 0x100; } + else param->memory_error = true; } detail(A) ::= LBRAC IDENT(B) DASHMATCH STRING(C) RBRAC. - { A = css_new_node(CSS_NODE_ATTRIB_DM, B, 0, 0); A->data2 = css_unquote(C); - A->specificity = 0x100; } + { A = css_new_selector(CSS_SELECTOR_ATTRIB_DM, B.text, B.length); + if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2; + A->specificity = 0x100; } + else param->memory_error = true; } detail(A) ::= COLON IDENT(B). - { if (strcasecmp(B, "link") == 0) { - A = css_new_node(CSS_NODE_ATTRIB, xstrdup("href"), 0, 0); - A->specificity = 0x100; - free(B); + { if (B.length == 4 && strncasecmp(B.text, "link", 4) == 0) { + A = css_new_selector(CSS_SELECTOR_ATTRIB, "href", 4); + if (A) A->specificity = 0x100; + else param->memory_error = true; } else { - A = css_new_node(CSS_NODE_PSEUDO, B, 0, 0); - A->specificity = 0x100; + A = css_new_selector(CSS_SELECTOR_PSEUDO, B.text, B.length); + if (A) A->specificity = 0x100; + else param->memory_error = true; } } detail(A) ::= COLON FUNCTION(B) IDENT RPAREN. - { A = css_new_node(CSS_NODE_PSEUDO, B, 0, 0); - A->specificity = 0x100; } + { A = css_new_selector(CSS_SELECTOR_PSEUDO, B.text, B.length); + if (A) A->specificity = 0x100; + else param->memory_error = true; } declaration_list(A) ::= . { A = 0; } @@ -150,7 +211,14 @@ declaration_list(A) ::= declaration(B) SEMI declaration_list(C). { if (B) { B->next = C; A = B; } else { A = C; } } declaration(A) ::= property(B) COLON value(C). - { A = css_new_node(CSS_NODE_DECLARATION, B, C, 0); } + { if (C && (A = css_new_node(CSS_NODE_DECLARATION, + B.text, B.length))) + A->value = C; + else { + param->memory_error = true; + css_free_node(C); + A = 0; + } } declaration(A) ::= any_list_1(B). /* malformed declaration: ignore */ { A = 0; css_free_node(B); } @@ -160,7 +228,8 @@ property(A) ::= IDENT(B). value(A) ::= any(B). { A = B; } value(A) ::= any(B) value(C). - { B->next = C; A = B; } + { if (B && C) { B->next = C; A = B; } + else { css_free_node(B); css_free_node(C); A = 0; } } value(A) ::= value(B) block. { A = B; } value(A) ::= value(B) ATKEYWORD. @@ -170,70 +239,100 @@ value(A) ::= value(B) ATKEYWORD. any_list(A) ::= . { A = 0; } any_list(A) ::= any(B) any_list(C). - { B->next = C; A = B; } + { if (B) { B->next = C; A = B; } + else { css_free_node(B); css_free_node(C); A = 0; } } any_list_1(A) ::= any(B) any_list(C). - { B->next = C; A = B; } + { if (B) { B->next = C; A = B; } + else { css_free_node(B); css_free_node(C); A = 0; } } any(A) ::= IDENT(B). - { A = css_new_node(CSS_NODE_IDENT, B, 0, 0); } + { A = css_new_node(CSS_NODE_IDENT, B.text, B.length); + if (!A) param->memory_error = true; } any(A) ::= NUMBER(B). - { A = css_new_node(CSS_NODE_NUMBER, B, 0, 0); } + { A = css_new_node(CSS_NODE_NUMBER, B.text, B.length); + if (!A) param->memory_error = true; } any(A) ::= PERCENTAGE(B). - { A = css_new_node(CSS_NODE_PERCENTAGE, B, 0, 0); } + { A = css_new_node(CSS_NODE_PERCENTAGE, B.text, B.length); + if (!A) param->memory_error = true; } any(A) ::= DIMENSION(B). - { A = css_new_node(CSS_NODE_DIMENSION, B, 0, 0); } + { A = css_new_node(CSS_NODE_DIMENSION, B.text, B.length); + if (!A) param->memory_error = true; } any(A) ::= STRING(B). - { A = css_new_node(CSS_NODE_STRING, css_unquote(B), 0, 0); } + { A = css_new_node(CSS_NODE_STRING, B.text + 1, B.length - 2); + if (!A) param->memory_error = true; } any(A) ::= DELIM(B). - { A = css_new_node(CSS_NODE_DELIM, B, 0, 0); } + { A = css_new_node(CSS_NODE_DELIM, B.text, B.length); + if (!A) param->memory_error = true; } any(A) ::= URI(B). - { A = css_new_node(CSS_NODE_URI, B, 0, 0); } + { A = css_new_node(CSS_NODE_URI, B.text, B.length); + if (!A) param->memory_error = true; } any(A) ::= HASH(B). - { A = css_new_node(CSS_NODE_HASH, B, 0, 0); } + { A = css_new_node(CSS_NODE_HASH, B.text, B.length); + if (!A) param->memory_error = true; } any(A) ::= UNICODE_RANGE(B). - { A = css_new_node(CSS_NODE_UNICODE_RANGE, B, 0, 0); } + { A = css_new_node(CSS_NODE_UNICODE_RANGE, B.text, B.length); + if (!A) param->memory_error = true; } any(A) ::= INCLUDES. - { A = css_new_node(CSS_NODE_INCLUDES, 0, 0, 0); } + { A = css_new_node(CSS_NODE_INCLUDES, 0, 0); + if (!A) param->memory_error = true; } any(A) ::= FUNCTION(B). - { A = css_new_node(CSS_NODE_FUNCTION, B, 0, 0); } + { A = css_new_node(CSS_NODE_FUNCTION, B.text, B.length); + if (!A) param->memory_error = true; } any(A) ::= DASHMATCH. - { A = css_new_node(CSS_NODE_DASHMATCH, 0, 0, 0); } + { A = css_new_node(CSS_NODE_DASHMATCH, 0, 0); + if (!A) param->memory_error = true; } any(A) ::= COLON. - { A = css_new_node(CSS_NODE_COLON, 0, 0, 0); } + { A = css_new_node(CSS_NODE_COLON, 0, 0); + if (!A) param->memory_error = true; } any(A) ::= COMMA. - { A = css_new_node(CSS_NODE_COMMA, 0, 0, 0); } + { A = css_new_node(CSS_NODE_COMMA, 0, 0); + if (!A) param->memory_error = true; } any(A) ::= DOT. - { A = css_new_node(CSS_NODE_DOT, 0, 0, 0); } + { A = css_new_node(CSS_NODE_DOT, 0, 0); + if (!A) param->memory_error = true; } any(A) ::= PLUS. - { A = css_new_node(CSS_NODE_PLUS, 0, 0, 0); } + { A = css_new_node(CSS_NODE_PLUS, 0, 0); + if (!A) param->memory_error = true; } any(A) ::= GT. - { A = css_new_node(CSS_NODE_GT, 0, 0, 0); } + { A = css_new_node(CSS_NODE_GT, 0, 0); + if (!A) param->memory_error = true; } any(A) ::= LPAREN any_list(B) RPAREN. - { A = css_new_node(CSS_NODE_PAREN, 0, B, 0); } + { if ((A = css_new_node(CSS_NODE_PAREN, 0, 0))) + A->value = B; + else { + param->memory_error = true; + css_free_node(B); + A = 0; + } } any(A) ::= LBRAC any_list(B) RBRAC. - { A = css_new_node(CSS_NODE_BRAC, 0, B, 0); } + { if ((A = css_new_node(CSS_NODE_BRAC, 0, 0))) + A->value = B; + else { + param->memory_error = true; + css_free_node(B); + A = 0; + } } any(A) ::= ASTERISK(B). - { A = css_new_node(CSS_NODE_DELIM, B, 0, 0); } + { A = css_new_node(CSS_NODE_DELIM, B.text, B.length); + if (!A) param->memory_error = true; } /* lemon directives */ -%extra_argument { struct parse_params *param } +%extra_argument { struct css_parser_params *param } %include { #define CSS_INTERNALS -#include "netsurf/css/scanner.h" #include "netsurf/css/css.h" #include "netsurf/utils/utils.h" } %name css_parser_ -%token_type { char* } -%token_destructor { xfree($$); } +%token_type { struct css_parser_token } -%type selector_list { struct css_node * } -%type selector { struct css_node * } +%type selector_list { struct css_selector * } +%type selector { struct css_selector * } %type css_combinator { css_combinator } -%type simple_selector { struct css_node * } -%type detail_list { struct css_node * } -%type detail { struct css_node * } +%type simple_selector { struct css_selector * } +%type detail_list { struct css_selector * } +%type detail { struct css_selector * } %type declaration_list { struct css_node * } %type declaration { struct css_node * } %type value { struct css_node * } @@ -241,10 +340,11 @@ any(A) ::= ASTERISK(B). %type any_list_1 { struct css_node * } %type any { struct css_node * } -%destructor selector_list { css_free_node($$); } -%destructor selector { css_free_node($$); } -%destructor simple_selector { css_free_node($$); } -%destructor detail_list { css_free_node($$); } +%destructor selector_list { css_free_selector($$); } +%destructor selector { css_free_selector($$); } +%destructor simple_selector { css_free_selector($$); } +%destructor detail_list { css_free_selector($$); } +%destructor detail { css_free_selector($$); } %destructor declaration_list { css_free_node($$); } %destructor declaration { css_free_node($$); } %destructor value { css_free_node($$); } diff --git a/css/ruleset.c b/css/ruleset.c index d898dbcab..e2e0d03bd 100644 --- a/css/ruleset.c +++ b/css/ruleset.c @@ -5,6 +5,14 @@ * Copyright 2004 James Bursa */ +/** \file + * CSS ruleset parsing. + * + * This file implements the last stage of CSS parsing. It converts trees of + * struct css_node produced by the parser into struct style, and adds them to a + * stylesheet. + */ + #include #include #include @@ -18,23 +26,9 @@ #include "netsurf/utils/utils.h" -struct property_entry { - const char name[20]; - void (*parse) (struct css_style * const s, const struct css_node * const v); -}; - -struct colour_entry { - const char name[12]; - colour col; -}; - -struct font_size_entry { - const char name[10]; - float size; -}; - -static int compare_selectors(const struct css_node *n0, const struct css_node *n1); +static bool css_compare_selectors(const struct css_selector *n0, + const struct css_selector *n1); static int parse_length(struct css_length * const length, const struct css_node * const v, bool non_negative); static colour parse_colour(const struct css_node * const v); @@ -102,11 +96,19 @@ static void parse_text_transform(struct css_style * const s, const struct css_no static void parse_visibility(struct css_style * const s, const struct css_node * const v); static void parse_width(struct css_style * const s, const struct css_node * const v); static void parse_white_space(struct css_style * const s, const struct css_node * const v); -static css_text_decoration css_text_decoration_parse(const char * const s); +static css_text_decoration css_text_decoration_parse(const char * const s, + int length); + +/** An entry in css_property_table. */ +struct css_property_entry { + const char name[20]; + void (*parse) (struct css_style * const s, + const struct css_node * const v); +}; -/* table of property parsers: MUST be sorted by property name */ -static const struct property_entry property_table[] = { +/** Table of property parsers. MUST be sorted by property name. */ +static const struct css_property_entry css_property_table[] = { { "background", parse_background }, { "background-color", parse_background_color }, { "border", parse_border }, @@ -161,9 +163,16 @@ static const struct property_entry property_table[] = { { "width", parse_width }, }; -/* table of standard colour names: MUST be sorted by colour name - * note: colour is 0xbbggrr */ -static const struct colour_entry colour_table[] = { + +/** An entry in css_colour_table. */ +struct css_colour_entry { + const char name[12]; + colour col; +}; + +/* Table of standard colour names. MUST be sorted by colour name. + * Note: colour is 0xbbggrr. */ +static const struct css_colour_entry css_colour_table[] = { { "aqua", 0xffff00 }, { "black", 0x000000 }, { "blue", 0xff0000 }, @@ -184,9 +193,16 @@ static const struct colour_entry colour_table[] = { { "yellow", 0x00ffff }, }; -/* table of font sizes: MUST be sorted by name */ + +/** An entry in css_font_size_table. */ +struct css_font_size_entry { + const char name[10]; + float size; +}; + +/** Table of font sizes. MUST be sorted by name. */ #define SIZE_FACTOR 1.2 -static const struct font_size_entry font_size_table[] = { +static const struct css_font_size_entry css_font_size_table[] = { { "large", 1.0 * SIZE_FACTOR }, { "medium", 1.0 }, { "small", 1.0 / SIZE_FACTOR }, @@ -198,42 +214,26 @@ static const struct font_size_entry font_size_table[] = { /** - * css_add_ruleset -- add a ruleset to a stylesheet + * Add a ruleset to a stylesheet. */ void css_add_ruleset(struct content *c, - struct css_node *selector, + struct css_selector *selector, struct css_node *declaration) { bool found; struct css_stylesheet *stylesheet = c->data.css.css; - struct css_node *n, *sel, *next_sel, *prev; + struct css_selector *n, *sel, *next_sel, *prev; struct css_style *style; unsigned int hash; for (sel = selector; sel != 0; sel = next_sel) { next_sel = sel->next; - /*LOG(("+++")); - for (n = sel; n != 0; n = n->right) { - struct css_node *m; - if (n->data != 0) - fprintf(stderr, "%s", n->data); - for (m = n->left; m != 0; m = m->next) { - switch (m->type) { - case CSS_NODE_ID: fprintf(stderr, "%s", m->data); break; - case CSS_NODE_CLASS: fprintf(stderr, ".%s", m->data); break; - default: fprintf(stderr, "unexpected node"); - } - } - fprintf(stderr, " "); - } - fprintf(stderr, "\n");*/ - /* check if this selector is already present */ found = false; prev = 0; - hash = css_hash(sel->data); + hash = css_hash(sel->data, sel->data_length); /* selectors are ordered by specificity in the hash chain */ for (n = stylesheet->rule[hash]; n && n->specificity < sel->specificity; @@ -242,7 +242,7 @@ void css_add_ruleset(struct content *c, for ( ; n && n->specificity == sel->specificity; n = n->next) { prev = n; - if (compare_selectors(sel, n)) { + if (css_compare_selectors(sel, n)) { found = true; break; } @@ -250,7 +250,12 @@ void css_add_ruleset(struct content *c, if (!found) { /* not present: construct a new struct css_style */ LOG(("constructing new style")); - style = xcalloc(1, sizeof(*style)); + style = malloc(sizeof *style); + if (!style) { + /** \todo report to user */ + css_free_selector(sel); + return; + } memcpy(style, &css_empty_style, sizeof(*style)); sel->style = style; sel->next = n; @@ -264,7 +269,7 @@ void css_add_ruleset(struct content *c, LOG(("augumenting existing style")); style = n->style; sel->next = 0; - css_free_node(sel); + css_free_selector(sel); } /* fill in the declarations */ @@ -273,69 +278,91 @@ void css_add_ruleset(struct content *c, } +/** + * Add declarations to a style. + */ + void css_add_declarations(struct css_style *style, struct css_node *declaration) { + char name[20]; struct css_node *n; for (n = declaration; n != 0; n = n->next) { - struct property_entry *p; - assert(n->type == CSS_NODE_DECLARATION && n->data != 0 && n->left != 0); - p = bsearch(n->data, property_table, - sizeof(property_table) / sizeof(property_table[0]), - sizeof(property_table[0]), (void*)strcasecmp); + struct css_property_entry *p; + assert(n->type == CSS_NODE_DECLARATION && n->data && n->value); + if (19 < n->data_length) + continue; + strncpy(name, n->data, n->data_length); + name[n->data_length] = 0; + p = bsearch(name, css_property_table, + sizeof css_property_table / + sizeof css_property_table[0], + sizeof css_property_table[0], + (int (*)(const void *, const void *)) + strcasecmp); if (p == 0) continue; - p->parse(style, n->left); + p->parse(style, n->value); } } -int compare_selectors(const struct css_node *n0, const struct css_node *n1) +/** + * Compare two css_selectors. + */ + +bool css_compare_selectors(const struct css_selector *n0, + const struct css_selector *n1) { - struct css_node *m0, *m1; + struct css_selector *m0, *m1; unsigned int count0 = 0, count1 = 0; /* compare element name */ if (!((n0->data == 0 && n1->data == 0) || - (n0->data != 0 && n1->data != 0 && strcmp(n0->data, n1->data) == 0))) - return 0; + (n0->data != 0 && n1->data != 0 && + n0->data_length == n1->data_length && + strncmp(n0->data, n1->data, n0->data_length) == 0))) + return false; if (n0->comb != n1->comb) - return 0; + return false; /* compare classes and ids */ - for (m0 = n0->left; m0 != 0; m0 = m0->next) + for (m0 = n0->detail; m0 != 0; m0 = m0->next) count0++; - for (m1 = n1->left; m1 != 0; m1 = m1->next) + for (m1 = n1->detail; m1 != 0; m1 = m1->next) count1++; if (count0 != count1) - return 0; - for (m0 = n0->left; m0 != 0; m0 = m0->next) { - int found = 0; - for (m1 = n1->left; m1 != 0; m1 = m1->next) { + return false; + for (m0 = n0->detail; m0 != 0; m0 = m0->next) { + bool found = false; + for (m1 = n1->detail; m1 != 0; m1 = m1->next) { /* TODO: should this be case sensitive for IDs? */ if (m0->type == m1->type && - strcasecmp(m0->data, m1->data) == 0 && + m0->data_length == m1->data_length && + strncasecmp(m0->data, m1->data, + m0->data_length) == 0 && ((m0->data2 == 0 && m1->data2 == 0) || - strcasecmp(m0->data2, m1->data2) == 0)) { - found = 1; + (m0->data2_length == m1->data2_length && + strncasecmp(m0->data2, m1->data2, + m0->data2_length) == 0))) { + found = true; break; } } if (!found) - return 0; + return false; } /* compare ancestors */ if (n0->comb == CSS_COMB_NONE) - return 1; + return true; - return compare_selectors(n0->right, n1->right); + return css_compare_selectors(n0->combiner, n1->combiner); } - -/** - * property parsers +/* + * Property parsers. */ /* TODO: consider CSS_NODE_NUMBER whenever a value may be '0' */ @@ -345,6 +372,7 @@ int parse_length(struct css_length * const length, { css_unit u; float value; + int num_length; if (v->type == CSS_NODE_NUMBER && atof(v->data) == 0) { length->unit = CSS_UNIT_EM; length->value = 0; @@ -352,7 +380,8 @@ int parse_length(struct css_length * const length, } if (v->type != CSS_NODE_DIMENSION) return 1; - u = css_unit_parse(v->data + strspn(v->data, "0123456789+-.")); + num_length = strspn(v->data, "0123456789+-."); + u = css_unit_parse(v->data + num_length, v->data_length - num_length); if (u == CSS_UNIT_UNKNOWN) return 1; value = atof(v->data); @@ -366,12 +395,13 @@ int parse_length(struct css_length * const length, colour named_colour(const char *name) { - struct colour_entry *col; + struct css_colour_entry *col; unsigned int r, g, b; - col = bsearch(name, colour_table, - sizeof(colour_table) / sizeof(colour_table[0]), - sizeof(colour_table[0]), (void*)strcasecmp); + col = bsearch(name, css_colour_table, + sizeof css_colour_table / sizeof css_colour_table[0], + sizeof css_colour_table[0], + (int (*)(const void *, const void *)) strcasecmp); if (col == 0) { /* A common error is the omission of the '#' from the * start of a colour specified in #rrggbb format. @@ -391,17 +421,16 @@ colour named_colour(const char *name) colour parse_colour(const struct css_node * const v) { colour c = CSS_COLOR_NONE; - int len; unsigned int r, g, b; - struct colour_entry *col; + struct css_colour_entry *col; + char colour_name[12]; switch (v->type) { case CSS_NODE_HASH: - len = strlen(v->data); - if (len == 4) { + if (v->data_length == 4) { if (sscanf(v->data + 1, "%1x%1x%1x", &r, &g, &b) == 3) c = (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r; - } else if (len == 7) { + } else if (v->data_length == 7) { if (sscanf(v->data + 1, "%2x%2x%2x", &r, &g, &b) == 3) c = (b << 16) | (g << 8) | r; } @@ -412,9 +441,16 @@ colour parse_colour(const struct css_node * const v) break; case CSS_NODE_IDENT: - col = bsearch(v->data, colour_table, - sizeof(colour_table) / sizeof(colour_table[0]), - sizeof(colour_table[0]), (void*)strcasecmp); + if (11 < v->data_length) + break; + strncpy(colour_name, v->data, v->data_length); + colour_name[v->data_length] = 0; + col = bsearch(colour_name, css_colour_table, + sizeof css_colour_table / + sizeof css_colour_table[0], + sizeof css_colour_table[0], + (int (*)(const void *, const void *)) + strcasecmp); if (col != 0) c = col->col; break; @@ -460,10 +496,14 @@ void parse_border_width(struct css_style * const s, for (w = v; w; w = w->next, count++) if (!((w->type == CSS_NODE_IDENT && ( - (strcasecmp(w->data, "inherit") == 0) || - (strcasecmp(w->data, "thin") == 0) || - (strcasecmp(w->data, "medium") == 0) || - (strcasecmp(w->data, "thick") == 0))) || + (w->data_length == 7 && + strncasecmp(w->data, "inherit", 7) == 0) || + (w->data_length == 4 && + strncasecmp(w->data, "thin", 4) == 0) || + (w->data_length == 6 && + strncasecmp(w->data, "medium", 6) == 0) || + (w->data_length == 5 && + strncasecmp(w->data, "thick", 5) == 0))) || (w->type == CSS_NODE_DIMENSION) || (w->type == CSS_NODE_NUMBER))) return; @@ -521,17 +561,21 @@ void parse_border_width_side(struct css_style * const s, const struct css_node * const v, unsigned int i) { if (v->type == CSS_NODE_IDENT) { - if (strcasecmp(v->data, "inherit") == 0) + if (v->data_length == 7 && + strncasecmp(v->data, "inherit", 7) == 0) s->border[i].width.width = CSS_BORDER_WIDTH_INHERIT; - else if (strcasecmp(v->data, "thin") == 0) { + else if (v->data_length == 4 && + strncasecmp(v->data, "thin", 4) == 0) { s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; s->border[i].width.value.value = 1; s->border[i].width.value.unit = CSS_UNIT_PX; - } else if (strcasecmp(v->data, "medium") == 0) { + } else if (v->data_length == 6 && + strncasecmp(v->data, "medium", 6) == 0) { s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; s->border[i].width.value.value = 2; s->border[i].width.value.unit = CSS_UNIT_PX; - } else if (strcasecmp(v->data, "thick") == 0) { + } else if (v->data_length == 5 && + strncasecmp(v->data, "thick", 5) == 0) { s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; s->border[i].width.value.value = 4; s->border[i].width.value.unit = CSS_UNIT_PX; @@ -673,7 +717,7 @@ PARSE_BORDER_STYLE(left, LEFT) void parse_border_style_side(struct css_style * const s, const struct css_node * const v, unsigned int i) { - css_border_style z = css_border_style_parse(v->data); + css_border_style z = css_border_style_parse(v->data, v->data_length); if (z != CSS_BORDER_STYLE_UNKNOWN) s->border[i].style = z; } @@ -706,7 +750,8 @@ void parse_border_side(struct css_style * const s, css_border_style z; if (!v->next && v->type == CSS_NODE_IDENT && - strcasecmp(v->data, "inherit") == 0) { + v->data_length == 7 && + strncasecmp(v->data, "inherit", 7) == 0) { s->border[i].color = CSS_COLOR_INHERIT; s->border[i].width.width = CSS_BORDER_WIDTH_INHERIT; s->border[i].style = CSS_BORDER_STYLE_INHERIT; @@ -721,7 +766,7 @@ void parse_border_side(struct css_style * const s, } if (v->type == CSS_NODE_IDENT) { - z = css_border_style_parse(v->data); + z = css_border_style_parse(v->data, v->data_length); if (z != CSS_BORDER_STYLE_UNKNOWN) { s->border[i].style = z; continue; @@ -737,7 +782,7 @@ void parse_clear(struct css_style * const s, const struct css_node * const v) css_clear z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; - z = css_clear_parse(v->data); + z = css_clear_parse(v->data, v->data_length); if (z != CSS_CLEAR_UNKNOWN) s->clear = z; } @@ -755,7 +800,7 @@ void parse_cursor(struct css_style * const s, const struct css_node * v) for (; v; v = v->next) { switch (v->type) { case CSS_NODE_IDENT: - z = css_cursor_parse(v->data); + z = css_cursor_parse(v->data, v->data_length); if (z != CSS_CURSOR_UNKNOWN) { s->cursor = z; return; @@ -772,7 +817,7 @@ void parse_display(struct css_style * const s, const struct css_node * const v) css_display z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; - z = css_display_parse(v->data); + z = css_display_parse(v->data, v->data_length); if (z != CSS_DISPLAY_UNKNOWN) s->display = z; } @@ -782,7 +827,7 @@ void parse_float(struct css_style * const s, const struct css_node * const v) css_float z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; - z = css_float_parse(v->data); + z = css_float_parse(v->data, v->data_length); if (z != CSS_FLOAT_UNKNOWN) s->float_ = z; } @@ -802,23 +847,27 @@ void parse_font(struct css_style * const s, const struct css_node * v) switch (v->type) { case CSS_NODE_IDENT: /* font-family */ - ff = css_font_family_parse(v->data); + ff = css_font_family_parse(v->data, + v->data_length); if (ff != CSS_FONT_FAMILY_UNKNOWN) { s->font_family = ff; break; } /* font-style, font-variant, or font-weight */ - fs = css_font_style_parse(v->data); + fs = css_font_style_parse(v->data, + v->data_length); if (fs != CSS_FONT_STYLE_UNKNOWN) { s->font_style = fs; break; } - fv = css_font_variant_parse(v->data); + fv = css_font_variant_parse(v->data, + v->data_length); if (fv != CSS_FONT_VARIANT_UNKNOWN) { s->font_variant = fv; break; } - fw = css_font_weight_parse(v->data); + fw = css_font_weight_parse(v->data, + v->data_length); if (fw != CSS_FONT_WEIGHT_UNKNOWN) { s->font_weight = fw; break; @@ -828,7 +877,7 @@ void parse_font(struct css_style * const s, const struct css_node * v) parse_font_size(s, v); break; case CSS_NODE_DELIM: - if (v->data[0] == '/' && v->data[1] == 0 && + if (v->data[0] == '/' && v->data_length == 1 && v->next) { v = v->next; parse_line_height(s, v); @@ -846,7 +895,8 @@ void parse_font_family(struct css_style * const s, const struct css_node * v) for (; v; v = v->next) { switch (v->type) { case CSS_NODE_IDENT: - z = css_font_family_parse(v->data); + z = css_font_family_parse(v->data, + v->data_length); if (z != CSS_FONT_FAMILY_UNKNOWN) { s->font_family = z; return; @@ -860,21 +910,31 @@ void parse_font_family(struct css_style * const s, const struct css_node * v) void parse_font_size(struct css_style * const s, const struct css_node * const v) { - struct font_size_entry *fs; + char font_size_name[10]; + struct css_font_size_entry *fs; switch (v->type) { case CSS_NODE_IDENT: - fs = bsearch(v->data, font_size_table, - sizeof(font_size_table) / sizeof(font_size_table[0]), - sizeof(font_size_table[0]), (void*)strcasecmp); + if (9 < v->data_length) + break; + strncpy(font_size_name, v->data, v->data_length); + font_size_name[v->data_length] = 0; + fs = bsearch(font_size_name, css_font_size_table, + sizeof css_font_size_table / + sizeof css_font_size_table[0], + sizeof css_font_size_table[0], + (int (*)(const void *, const void *)) + strcasecmp); if (fs != 0) { s->font_size.size = CSS_FONT_SIZE_LENGTH; s->font_size.value.length.unit = CSS_UNIT_PT; s->font_size.value.length.value = fs->size * option_font_size / 10; - } else if (strcasecmp(v->data, "larger") == 0) { + } else if (v->data_length == 6 && + strncasecmp(v->data, "larger", 6) == 0) { s->font_size.size = CSS_FONT_SIZE_PERCENT; s->font_size.value.percent = SIZE_FACTOR * 100; - } else if (strcmp(v->data, "smaller") == 0) { + } else if (v->data_length == 7 && + strncasecmp(v->data, "smaller", 7) == 0) { s->font_size.size = CSS_FONT_SIZE_PERCENT; s->font_size.value.percent = 1 / SIZE_FACTOR * 100; } @@ -900,7 +960,7 @@ void parse_font_style(struct css_style * const s, const struct css_node * const css_font_style z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; - z = css_font_style_parse(v->data); + z = css_font_style_parse(v->data, v->data_length); if (z != CSS_FONT_STYLE_UNKNOWN) s->font_style = z; } @@ -910,7 +970,7 @@ void parse_font_variant(struct css_style * const s, const struct css_node * cons css_font_variant z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; - z = css_font_variant_parse(v->data); + z = css_font_variant_parse(v->data, v->data_length); if (z != CSS_FONT_VARIANT_UNKNOWN) s->font_variant = z; } @@ -920,14 +980,15 @@ void parse_font_weight(struct css_style * const s, const struct css_node * const css_font_weight z; if ((v->type != CSS_NODE_IDENT && v->type != CSS_NODE_NUMBER) || v->next != 0) return; - z = css_font_weight_parse(v->data); + z = css_font_weight_parse(v->data, v->data_length); if (z != CSS_FONT_WEIGHT_UNKNOWN) s->font_weight = z; } void parse_height(struct css_style * const s, const struct css_node * const v) { - if (v->type == CSS_NODE_IDENT && strcasecmp(v->data, "auto") == 0) + if (v->type == CSS_NODE_IDENT && v->data_length == 4 && + strncasecmp(v->data, "auto", 4) == 0) s->height.height = CSS_HEIGHT_AUTO; else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && parse_length(&s->height.length, v, true) == 0) @@ -936,7 +997,8 @@ void parse_height(struct css_style * const s, const struct css_node * const v) void parse_line_height(struct css_style * const s, const struct css_node * const v) { - if (v->type == CSS_NODE_IDENT && strcasecmp(v->data, "normal") == 0) { + if (v->type == CSS_NODE_IDENT && v->data_length == 6 && + strncasecmp(v->data, "normal", 6) == 0) { s->line_height.size = CSS_LINE_HEIGHT_ABSOLUTE; s->line_height.value.absolute = 1.3; } else if (v->type == CSS_NODE_PERCENTAGE) { @@ -958,8 +1020,10 @@ void parse_margin(struct css_style * const s, const struct css_node * const v) for (w = v; w; w = w->next, count++) if (!((w->type == CSS_NODE_IDENT && ( - (strcasecmp(w->data, "inherit") == 0) || - (strcasecmp(w->data, "auto") == 0))) || + (w->data_length == 7 && + strncasecmp(w->data, "inherit", 7) == 0) || + (w->data_length == 4 && + strncasecmp(w->data, "auto", 4) == 0))) || (w->type == CSS_NODE_PERCENTAGE) || (w->type == CSS_NODE_DIMENSION) || (w->type == CSS_NODE_NUMBER))) @@ -1017,9 +1081,11 @@ PARSE_MARGIN_(left, LEFT) void parse_margin_side(struct css_style * const s, const struct css_node * const v, unsigned int i) { - if (v->type == CSS_NODE_IDENT && strcasecmp(v->data, "inherit") == 0) + if (v->type == CSS_NODE_IDENT && v->data_length == 7 && + strncasecmp(v->data, "inherit", 7) == 0) s->margin[i].margin = CSS_MARGIN_INHERIT; - else if (v->type == CSS_NODE_IDENT && strcasecmp(v->data, "auto") == 0) + else if (v->type == CSS_NODE_IDENT && v->data_length == 4 && + strncasecmp(v->data, "auto", 4) == 0) s->margin[i].margin = CSS_MARGIN_AUTO; else if (v->type == CSS_NODE_PERCENTAGE) { s->margin[i].margin = CSS_MARGIN_PERCENT; @@ -1036,7 +1102,8 @@ void parse_padding(struct css_style * const s, const struct css_node * const v) const struct css_node *w; for (w = v; w; w = w->next, count++) - if (!((w->type == CSS_NODE_IDENT && strcasecmp(w->data, "inherit") == 0) || + if (!((w->type == CSS_NODE_IDENT && w->data_length == 7 && + strncasecmp(w->data, "inherit", 7) == 0) || (w->type == CSS_NODE_PERCENTAGE) || (w->type == CSS_NODE_DIMENSION) || (w->type == CSS_NODE_NUMBER))) @@ -1094,7 +1161,8 @@ PARSE_PADDING_(left, LEFT) void parse_padding_side(struct css_style * const s, const struct css_node * const v, unsigned int i) { - if (v->type == CSS_NODE_IDENT && strcasecmp(v->data, "inherit") == 0) + if (v->type == CSS_NODE_IDENT && v->data_length == 7 && + strncasecmp(v->data, "inherit", 7) == 0) s->padding[i].padding = CSS_PADDING_INHERIT; else if (v->type == CSS_NODE_PERCENTAGE) { s->padding[i].padding = CSS_PADDING_PERCENT; @@ -1110,7 +1178,7 @@ void parse_text_align(struct css_style * const s, const struct css_node * const css_text_align z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; - z = css_text_align_parse(v->data); + z = css_text_align_parse(v->data, v->data_length); if (z != CSS_TEXT_ALIGN_UNKNOWN) s->text_align = z; } @@ -1134,7 +1202,7 @@ void parse_text_decoration(struct css_style * const s, const struct css_node * c css_text_decoration z; if (v->type != CSS_NODE_IDENT) return; - z = css_text_decoration_parse(v->data); + z = css_text_decoration_parse(v->data, v->data_length); if (z == CSS_TEXT_DECORATION_INHERIT || z == CSS_TEXT_DECORATION_NONE) { if (v->next != 0) return; @@ -1143,7 +1211,7 @@ void parse_text_decoration(struct css_style * const s, const struct css_node * c if (z != CSS_TEXT_DECORATION_UNKNOWN) s->text_decoration |= z; for (temp = v->next; temp; temp = temp->next) { - z = css_text_decoration_parse(temp->data); + z = css_text_decoration_parse(temp->data, temp->data_length); if (z != CSS_TEXT_DECORATION_UNKNOWN) s->text_decoration |= z; } @@ -1154,7 +1222,7 @@ void parse_text_transform(struct css_style * const s, const struct css_node * co css_text_transform z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; - z = css_text_transform_parse(v->data); + z = css_text_transform_parse(v->data, v->data_length); if (z != CSS_TEXT_TRANSFORM_UNKNOWN) s->text_transform = z; } @@ -1164,14 +1232,15 @@ void parse_visibility(struct css_style * const s, const struct css_node * const css_visibility z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; - z = css_visibility_parse(v->data); + z = css_visibility_parse(v->data, v->data_length); if (z != CSS_VISIBILITY_UNKNOWN) s->visibility = z; } void parse_width(struct css_style * const s, const struct css_node * const v) { - if (v->type == CSS_NODE_IDENT && strcasecmp(v->data, "auto") == 0) + if (v->type == CSS_NODE_IDENT && v->data_length == 4 && + strncasecmp(v->data, "auto", 4) == 0) s->width.width = CSS_WIDTH_AUTO; else if (v->type == CSS_NODE_PERCENTAGE) { s->width.width = CSS_WIDTH_PERCENT; @@ -1186,19 +1255,26 @@ void parse_white_space(struct css_style * const s, const struct css_node * const css_white_space z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; - z = css_white_space_parse(v->data); + z = css_white_space_parse(v->data, v->data_length); if (z != CSS_WHITE_SPACE_UNKNOWN) s->white_space = z; } -css_text_decoration css_text_decoration_parse(const char * const s) +css_text_decoration css_text_decoration_parse(const char * const s, + int length) { - if (strcasecmp(s, "inherit") == 0) return CSS_TEXT_DECORATION_INHERIT; - if (strcasecmp(s, "none") == 0) return CSS_TEXT_DECORATION_NONE; - if (strcasecmp(s, "blink") == 0) return CSS_TEXT_DECORATION_BLINK; - if (strcasecmp(s, "line-through") == 0) return CSS_TEXT_DECORATION_LINE_THROUGH; - if (strcasecmp(s, "overline") == 0) return CSS_TEXT_DECORATION_OVERLINE; - if (strcasecmp(s, "underline") == 0) return CSS_TEXT_DECORATION_UNDERLINE; + if (length == 7 && strncasecmp(s, "inherit", 7) == 0) + return CSS_TEXT_DECORATION_INHERIT; + if (length == 4 && strncasecmp(s, "none", 4) == 0) + return CSS_TEXT_DECORATION_NONE; + if (length == 5 && strncasecmp(s, "blink", 5) == 0) + return CSS_TEXT_DECORATION_BLINK; + if (length == 12 && strncasecmp(s, "line-through", 12) == 0) + return CSS_TEXT_DECORATION_LINE_THROUGH; + if (length == 8 && strncasecmp(s, "overline", 8) == 0) + return CSS_TEXT_DECORATION_OVERLINE; + if (length == 9 && strncasecmp(s, "underline", 9) == 0) + return CSS_TEXT_DECORATION_UNDERLINE; return CSS_TEXT_DECORATION_UNKNOWN; } diff --git a/css/scanner.l b/css/scanner.l index 1f8cc1d92..36347b3a8 100644 --- a/css/scanner.l +++ b/css/scanner.l @@ -2,78 +2,97 @@ * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license - * Copyright 2003 James Bursa + * Copyright 2004 James Bursa */ -%{ -#include "parser.h" -%} +/** \file + * CSS tokeniser using re2c. + * + * see CSS2 Specification, chapter 4 + * http://www.w3.org/TR/REC-CSS2/syndata.html, + * and errata + * http://www.w3.org/Style/css2-updates/REC-CSS2-19980512-errata + */ + +#include +#define CSS_INTERNALS +#include "netsurf/css/css.h" +#include "netsurf/css/parser.h" + +#define YYCTYPE unsigned char +#define YYCURSOR (*buffer) +#define YYLIMIT end +#define YYMARKER marker +#define YYFILL(n) { return 0; } -%option 8bit -%option batch -%option case-insensitive -%option header-file="scanner.h" -%option outfile="scanner.c" -%option prefix="css_" -%option reentrant -%option never-interactive -%option noyywrap -%option yylineno -/* see CSS2 Specification, chapter 4 - http://www.w3.org/TR/REC-CSS2/syndata.html, - and errata - http://www.w3.org/Style/css2-updates/REC-CSS2-19980512-errata */ +/** + * Identify a CSS source token. + * + * \param buffer source to tokenise, updated to new position + * \param end end of source + * \param token_text updated to start of recognized token + * \return token number + */ + +int css_tokenise(unsigned char **buffer, unsigned char *end, + unsigned char **token_text) +{ + unsigned char *marker; -ident {nmstart}{nmchar}* -name {nmchar}+ -nmstart [a-zA-Z_]|{nonascii}|{escape} -nonascii [\200-\377] -unicode \\[0-9a-f]{1,6}[ \n\r\t\f]? -escape {unicode}|\\[ -~\200-\377] -nmchar [-a-zA-Z0-9_]|{nonascii}|{escape} -num [+-]?[0-9]+|[0-9]*\.[0-9]+ -string {string1}|{string2} -string1 \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\" -string2 \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\' -nl \n|\r\n|\r|\f -w [ \t\r\n\f]* +start: + *token_text = YYCURSOR; -%% +/*!re2c +nonascii = [\200-\377]; +unicode = "\\" [0-9a-f]+ [ \n\r\t\f]?; +escape = unicode | "\\" [ -~\200-\377]; +nmchar = [-a-zA-Z0-9_] | nonascii | escape; +nmstart = [a-zA-Z_] | nonascii | escape; +ident = nmstart nmchar*; +name = nmchar+; +num = [+-]? [0-9]+ | [0-9]* "." [0-9]+; +nl = "\n" | "\r\n" | "\r" | "\f"; +string1 = "\"" ([\t !#$%&(-~] | "\\" nl | "'" | nonascii | escape)* "\""; +string2 = "'" ([\t !#$%&(-~] | "\\" nl | "\""| nonascii | escape)* "'"; +string = string1 | string2; +w = [ \t\r\n\f]*; +any = [\000-\377]; -{ident} { return IDENT; } -@{ident} { return ATKEYWORD; } -{string} { return STRING; } -#{name} { return HASH; } -{num} { return NUMBER; } -{num}% { return PERCENTAGE; } -{num}{ident} { return DIMENSION; } -url\({w}{string}{w}\)|url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\) { - return URI; } -U\+[0-9A-F?]{1,6}(-[0-9A-F]{1,6})? { - return UNICODE_RANGE; } -"" /* ignore CDC */ -; { return SEMI; } -\{ { return LBRACE; } -\} { return RBRACE; } -\( { return LPAREN; } -\) { return RPAREN; } -\[ { return LBRAC; } -\] { return RBRAC; } -[ \t\r\n\f]+ /* ignore whitespace */ -\/\*[^*]*\*+([^/][^*]*\*+)*\/ /* ignore comments */ -{ident}\( { return FUNCTION; } -= { return EQUALS; } -~= { return INCLUDES; } +ident { return IDENT; } +"@" ident { return ATKEYWORD; } +string { return STRING; } +"#" name { return HASH; } +num { return NUMBER; } +num "%" { return PERCENTAGE; } +num ident { return DIMENSION; } +"url(" w string w ")" | "url(" w ([!#$%&*-~]|nonascii|escape)* w ")" + { return URI; } +"U+" [0-9A-F?]+ ("-" [0-9A-F]+ )? + { return UNICODE_RANGE; } +"" { goto start; /* ignore CDC */ } +";" { return SEMI; } +"{" { return LBRACE; } +"}" { return RBRACE; } +"(" { return LPAREN; } +")" { return RPAREN; } +"[" { return LBRAC; } +"]" { return RBRAC; } +[ \t\r\n\f]+ { goto start; /* ignore whitespace */ } +"/*" (any\[*])* "*"+ ((any\[/]) (any\[*])* "*"+)* "/" + { goto start; /* ignore comments */ } +ident "(" { return FUNCTION; } +"=" { return EQUALS; } +"~=" { return INCLUDES; } "|=" { return DASHMATCH; } -: { return COLON; } -, { return COMMA; } +":" { return COLON; } +"," { return COMMA; } "+" { return PLUS; } -> { return GT; } +">" { return GT; } "." { return DOT; } "*" { return ASTERISK; } -. { return DELIM; } - -%% +any { return DELIM; } +*/ +} -- cgit v1.2.3