/* find a word in a list using '|' as separator, * optionally fold case to lower case. */ int strfind(const char *keytable, const char *str, int casefold) { char buf[128]; int c, len; const char *p; if (casefold) { pstrcpy(buf, sizeof(buf), str); str = buf; css_strtolower(buf, sizeof(buf)); } c = *str; len = strlen(str); /* need to special case the empty string */ if (len == 0) return strstr(keytable, "||") != NULL; /* initial and trailing | are optional */ /* they do not cause the empty string to match */ for (p = keytable;;) { if (!memcmp(p, str, len) && (p[len] == '|' || p[len] == '\0')) return 1; for (;;) { p = strchr(p + 1, c); if (!p) return 0; if (p[-1] == '|') break; } } }
static const char *css_attr_strlower(CSSBox *box, CSSIdent attr_id) { static char buf[200]; const char *value; value = css_attr_str(box, attr_id); if (!value) return NULL; pstrcpy(buf, sizeof(buf), value); css_strtolower(buf, sizeof(buf)); return buf; }
static int parse_tag(XMLState *s, const char *buf) { char tag[256], *q, len, eot; char attr_name[256]; char value[2048]; const char *p; CSSIdent css_tag; CSSBox *box, *box1; CSSAttribute *first_attr, **pattr, *attr; p = buf; /* ignore XML commands */ if (p[0] == '!' || p[0] == '?') return XML_STATE_TEXT; /* end of tag check */ eot = 0; if (*p == '/') { p++; eot = 1; } /* parse the tag name */ get_str(&p, tag, sizeof(tag), "/"); if (tag[0] == '\0') { /* special closing tag */ if (eot) { css_tag = CSS_ID_NIL; goto end_of_tag; } else { xml_error(s, "invalid null tag"); return XML_STATE_TEXT; } } if (s->ignore_case) css_strtolower(tag, sizeof(tag)); css_tag = css_new_ident(tag); /* XXX: should test html_syntax, but need more patches */ if (s->is_html && (css_tag == CSS_ID_style || css_tag == CSS_ID_script)) goto pretag; if (eot) goto end_of_tag; /* parse attributes */ first_attr = NULL; pattr = &first_attr; for (;;) { skip_spaces(&p); if (*p == '\0' || *p == '/') break; get_str(&p, attr_name, sizeof(attr_name), "=/"); if (s->ignore_case) css_strtolower(attr_name, sizeof(attr_name)); if (*p == '=') { int och, ch; p++; skip_spaces(&p); och = *p; /* in html, we can put non string values */ if (och != '\'' && och != '\"') { if (!s->html_syntax) xml_error(s, "string expected for attribute '%s'", attr_name); q = value; while (*p != '\0' && !strchr(" \t\n\r<>", *p)) { ch = parse_entity(&p); if ((q - value) < (int)sizeof(value) - 1) *q++ = ch; } *q = '\0'; } else { p++; q = value; while (*p != och && *p != '\0' && *p != '<') { ch = parse_entity(&p); if ((q - value) < (int)sizeof(value) - 1) *q++ = ch; } *q = '\0'; if (*p != och) { xml_error(s, "malformed string in attribute '%s'", attr_name); } else { p++; } } } else { value[0] = '\0'; } attr = box_new_attr(css_new_ident(attr_name), value); if (attr) { *pattr = attr; pattr = &attr->next; } } /* close some tags (correct HTML mistakes) */ if (s->html_syntax) { CSSBox *box1; const HTMLClosedTags *ct; ct = html_closed_tags; for (;;) { if (!ct->tag) break; if (css_tag == ct->tag) { box1 = s->box; while (box1 != NULL && css_get_enum(css_ident_str(box1->tag), ct->tag_closed) >= 0) { html_eval_tag(s, box1); box1 = box1->parent; } if (box1) { s->box = box1; } break; } ct++; } } /* create the new box and add it */ box = css_new_box(css_tag, NULL); box->attrs = first_attr; if (!s->box) { s->root_box = box; } else { css_make_child_box(s->box); css_add_box(s->box, box); } s->box = box; if ((s->flags & XML_DOCBOOK) && css_tag == CSS_ID_programlisting) { pretag: pstrcpy(s->pretag, sizeof(s->pretag), tag); s->pretaglen = strlen(s->pretag); return XML_STATE_PRETAG; } len = strlen(buf); /* end of tag. If html, check also some common mistakes. FORM is considered as self closing to avoid any content problems */ if ((len > 0 && buf[len - 1] == '/') || (s->html_syntax && (css_tag == CSS_ID_br || css_tag == CSS_ID_hr || css_tag == CSS_ID_meta || css_tag == CSS_ID_link || css_tag == CSS_ID_form || css_tag == CSS_ID_base || css_tag == CSS_ID_input || css_tag == CSS_ID_basefont || css_tag == CSS_ID_img))) { end_of_tag: box1 = s->box; if (box1) { if (s->html_syntax) { if (css_tag != CSS_ID_NIL) { /* close all non matching tags */ while (box1 != NULL && box1->tag != css_tag) { html_eval_tag(s, box1); box1 = box1->parent; } } if (!box1) { if (css_tag != CSS_ID_form) xml_error(s, "unmatched closing tag </%s>", css_ident_str(css_tag)); } else { html_eval_tag(s, box1); s->box = box1->parent; } } else { if (css_tag != CSS_ID_NIL && box1->tag != css_tag) { xml_error(s, "unmatched closing tag </%s> for <%s>", css_ident_str(css_tag), css_ident_str(box1->tag)); } else { if (s->is_html) html_eval_tag(s, box1); s->box = box1->parent; } } } } return XML_STATE_TEXT; }
static void parse_simple_selector(CSSSimpleSelector *ss, CSSParseState *b, int *ch_ptr) { char value[1024]; char tag[64]; char tag_id[64]; char attribute[64]; int ch, pclass, val; CSSStyleSheetAttributeEntry *first_attr, **last_attr; ch = *ch_ptr; /* read the tag */ first_attr = NULL; last_attr = &first_attr; tag[0] = '\0'; tag_id[0] = '\0'; pclass = 0; read_ident(b, &ch, tag, sizeof(tag)); if (b->ignore_case) css_strtolower(tag, sizeof(tag)); /* read '.class', '[xxx]', ':pseudo-class' */ for (;;) { bskip_spaces(b, &ch); if (ch == '.') { /* read the class and add it as an attribute */ ch = bgetc(b); read_ident(b, &ch, value, sizeof(value)); add_attribute(&last_attr, CSS_ID_class, CSS_ATTR_OP_EQUAL, value); } else if (ch == '#') { /* read the id */ ch = bgetc(b); read_ident(b, &ch, tag_id, sizeof(tag_id)); } else if (ch == '[') { /* read the attribute */ int op; ch = bgetc(b); read_ident(b, &ch, attribute, sizeof(attribute)); if (b->ignore_case) css_strtolower(attribute, sizeof(attribute)); switch (ch) { case '~': op = CSS_ATTR_OP_IN_LIST; ch = bgetc(b); goto get_value; case '=': op = CSS_ATTR_OP_EQUAL; goto get_value; case '|': op = CSS_ATTR_OP_IN_HLIST; ch = bgetc(b); get_value: ch = bgetc(b); if (ch == '\"' || ch == '\'') { read_string(b, &ch, value, sizeof(value)); } else { read_ident(b, &ch, value, sizeof(value)); } break; case ']': op = CSS_ATTR_OP_SET; value[0] = '\0'; break; default: dprintf("op: incorrect char '%c'\n", ch); return; /* cannot do more */ } if (ch == ']') ch = bgetc(b); add_attribute(&last_attr, css_new_ident(attribute), op, value); } else if (ch == ':') { ch = bgetc(b); read_ident(b, &ch, value, sizeof(value)); val = css_get_enum(value, "first-child,link,visited,active,hover,focus,first-line,first-letter,before,after"); if (val >= 0) pclass |= 1 << val; } else { break; } } memset(ss, 0, sizeof(CSSSimpleSelector)); if (tag[0] == '\0') { ss->tag = CSS_ID_ALL; } else { ss->tag = css_new_ident(tag); } if (tag_id[0] != '\0') { /* XXX: not fully correct, but good enough for now */ add_attribute(&last_attr, CSS_ID_id, CSS_ATTR_OP_EQUAL, value); /* we also add the id, just in case we use it in the futur */ ss->tag_id = css_new_ident(tag_id); } ss->attrs = first_attr; ss->pclasses = pclass; *ch_ptr = ch; }
/* parse the properties and return a list of properties. NOTE: 'b' is only used for error reporting */ CSSProperty *css_parse_properties(CSSParseState *b, const char *props_str) { const char *p; char property[64]; char buf[1024], buf2[64]; int property_index, type, val, nb_args, i, unit; int property_index1; CSSPropertyValue args[MAX_ARGS]; CSSProperty **last_prop, *first_prop; const CSSPropertyDef *def; val = 0; first_prop = NULL; last_prop = &first_prop; p = props_str; for (;;) { get_str(&p, property, sizeof(property), ":"); if (*p == '\0') break; if (*p == ':') p++; skip_spaces(&p); /* find the property */ def = css_properties; for (;;) { if (def >= css_properties + NB_PROPERTIES) { css_error1(b, "unsupported property '%s'", property); /* property not found skip it: find next ';' */ while (*p && *p != ';') p++; goto next; } if (!strcmp(def->name, property)) break; def++; } property_index = def - css_properties; type = def->type; nb_args = 0; for (;;) { /* get argument */ skip_spaces(&p); if (*p == ';' || *p == '\0') break; /* more than 1 argument only if wanted */ if (nb_args >= 1 && !(type & (CSS_TYPE_FOUR|CSS_TYPE_TWO|CSS_TYPE_ARGS))) break; if (nb_args >= 2 && !(type & (CSS_TYPE_FOUR|CSS_TYPE_ARGS))) break; if (nb_args >= 4 && (!type & CSS_TYPE_ARGS)) break; if (nb_args >= MAX_ARGS) break; if (*p == '\"' || *p == '\'') { /* string parsing */ /* if no string expected, continue parsing */ if (!(type & CSS_TYPE_STRING)) goto next; args[nb_args].u.str = css_parse_string(&p); unit = CSS_VALUE_STRING; goto got_val; } if (type & CSS_TYPE_ATTR) { /* attr(x) support */ if (strstart(p, "attr(", &p)) { get_str(&p, buf, sizeof(buf), ");"); if (buf[0] != '\0') { if (*p != ')') goto next; p++; if (b->ignore_case) css_strtolower(buf, sizeof(buf)); args[nb_args].u.attr_id = css_new_ident(buf); unit = CSS_VALUE_ATTR; goto got_val; } } } if (type & CSS_TYPE_COUNTER) { /* counter(x[,type]) support */ if (strstart(p, "counter(", &p)) { get_str(&p, buf, sizeof(buf), ",);"); args[nb_args].u.counter.type = CSS_LIST_STYLE_TYPE_DECIMAL; if (*p == ',') { p++; get_str(&p, buf2, sizeof(buf2), ");"); val = css_get_enum(buf2, list_style_enum); if (val >= 0) args[nb_args].u.counter.type = val; } if (*p != ')') goto next; p++; args[nb_args].u.counter.counter_id = css_new_ident(buf); unit = CSS_VALUE_COUNTER; goto got_val; } } get_str(&p, buf, sizeof(buf), ";"); unit = CSS_UNIT_NONE; if (type & CSS_TYPE_AUTO) { if (!strcmp(buf, "auto")) { val = CSS_AUTO; goto got_val; } } if (!(type & CSS_TYPE_NOINHERIT)) { if (!strcmp(buf, "inherit")) { val = CSS_INHERIT; goto got_val; } } if (type & CSS_TYPE_INTEGER) { const char *p1; val = strtol(buf, (char **)&p1, 0); if (*p1 == '\0') { unit = CSS_VALUE_INTEGER; goto got_val; } } if (type & CSS_TYPE_LENGTH) { if (!css_get_length(&val, &unit, buf)) goto got_val; } if (type & CSS_TYPE_BORDER_STYLE) { val = css_get_enum(buf, border_style_enum); if (val >= 0) goto got_val; } if (type & CSS_TYPE_LIST_STYLE) { val = css_get_enum(buf, list_style_enum); if (val >= 0) goto got_val; } if (type & CSS_TYPE_ENUM) { val = css_get_enum(buf, def->name + strlen(def->name) + 1); if (val >= 0) goto got_val; } if (type & CSS_TYPE_IDENT) { val = css_new_ident(buf); unit = CSS_VALUE_IDENT; goto got_val; } if (type & CSS_TYPE_FONT_FAMILY) { val = css_get_font_family(buf); if (val == 0) val = CSS_INHERIT; goto got_val; } if (type & CSS_TYPE_COLOR) { QEColor color; /* XXX: color parsing is not always discriminant */ if (!css_get_color(&color, buf)) { val = color; unit = CSS_VALUE_COLOR; goto got_val; } } css_error1(b, "unrecognized value '%s' for property '%s'", buf, def->name); goto next; got_val: /* specific handling may be necessary. We do them here */ switch (property_index) { case CSS_font_size: if (unit == CSS_UNIT_NONE) { if (val == 7) { /* smaller */ unit = CSS_UNIT_PERCENT; val = (CSS_LENGTH_FRAC_BASE * 10) / 12; } else if (val == 8) { /* larger */ unit = CSS_UNIT_PERCENT; val = (CSS_LENGTH_FRAC_BASE * 12) / 10; } else if (val >= 0) { unit = CSS_UNIT_IN; val = get_font_size(val); } else { goto next; } } break; case CSS_border: case CSS_border_left: case CSS_border_top: case CSS_border_right: case CSS_border_bottom: if (unit == CSS_VALUE_COLOR) { property_index1 = property_index + CSS_border_color - CSS_border; } else if (unit == CSS_UNIT_NONE) { property_index1 = property_index + CSS_border_style - CSS_border; } else { property_index1 = property_index + CSS_border_width - CSS_border; } args[0].type = unit; args[0].u.val = val; if (property_index == CSS_border) { for (i = 0; i < 4; i++) css_add_prop(&last_prop, property_index1 + 1 + i, &args[0]); } else { css_add_prop(&last_prop, property_index1, &args[0]); } /* parse next args without storing them */ continue; } args[nb_args].type = unit; if (unit != CSS_VALUE_STRING && unit != CSS_VALUE_ATTR && unit != CSS_VALUE_COUNTER) { args[nb_args].u.val = val; } nb_args++; } if (type & CSS_TYPE_SPECIAL) goto next; if (type & CSS_TYPE_FOUR) { CSSPropertyValue v1, v2, v3, v4; /* handle specifically the four args case */ v1 = args[0]; switch (nb_args) { case 1: args[1] = args[2] = args[3] = v1; break; case 2: v2 = args[1]; args[1] = args[3] = v1; args[0] = args[2] = v2; break; case 3: v2 = args[1]; v3 = args[2]; args[1] = v1; args[0] = args[2] = v2; args[3] = v3; break; case 4: default: v2 = args[1]; v3 = args[2]; v4 = args[3]; args[1] = v1; args[2] = v2; args[3] = v3; args[0] = v4; break; } for (i = 0; i < 4; i++) css_add_prop(&last_prop, property_index + 1 + i, &args[i]); } else if (type & CSS_TYPE_TWO) { if (nb_args == 1) args[1] = args[0]; for (i = 0; i < 2; i++) css_add_prop(&last_prop, property_index + 1 + i, &args[i]); } else if (type & CSS_TYPE_ARGS) { /* unbounded number of args */ css_add_prop_values(&last_prop, property_index, nb_args, args); } else { css_add_prop(&last_prop, property_index, &args[0]); } next: skip_spaces(&p); if (*p != ';') break; p++; } return first_prop; }