Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
static void html_eval_tag(XMLState *s, CSSBox *box)
{
    const char *value;
    CSSProperty *first_prop, **last_prop;
    QEColor color;
    int width, height, val, type;
    int border, padding;
    CSSPropertyValue arg;
    CSSPropertyValue args[2];

    first_prop = NULL;
    last_prop = &first_prop;
    switch (box->tag) {
    case CSS_ID_img:
    parse_img:
        box->content_type = CSS_CONTENT_TYPE_IMAGE;
        box->u.image.content_alt = NULL;
        /* set alt content */
        value = css_attr_str(box, CSS_ID_alt);
        if (!value) {
            /* if no alt, display the name of the image */
            value = css_attr_str(box, CSS_ID_src);
            if (value)
                value = basename(value);
        }
        if (value && value[0] != '\0') {
            arg.type = CSS_VALUE_STRING;
            arg.u.str = strdup(value);
            css_add_prop(&last_prop, CSS_content_alt, &arg);
        }
        
        width = css_attr_int(box, CSS_ID_width, 0);
        if (width <= 0)
            width = DEFAULT_IMG_WIDTH;
        height = css_attr_int(box, CSS_ID_height, 0);
        if (height <= 0)
            height = DEFAULT_IMG_HEIGHT;

        css_add_prop_unit(&last_prop, CSS_width, 
                          CSS_UNIT_PIXEL, width);
        css_add_prop_unit(&last_prop, CSS_height, 
                          CSS_UNIT_PIXEL, height);

        /* border */
        val = css_attr_int(box, CSS_ID_border, -1);
        if (val >= 0) {
            css_add_prop_unit(&last_prop, CSS_border_left_width,
                              CSS_UNIT_PIXEL, val);
            css_add_prop_unit(&last_prop, CSS_border_right_width,
                              CSS_UNIT_PIXEL, val);
            css_add_prop_unit(&last_prop, CSS_border_top_width,
                              CSS_UNIT_PIXEL, val);
            css_add_prop_unit(&last_prop, CSS_border_bottom_width,
                              CSS_UNIT_PIXEL, val);

            css_add_prop_int(&last_prop, CSS_border_left_style,
                             CSS_BORDER_STYLE_SOLID);
            css_add_prop_int(&last_prop, CSS_border_right_style,
                             CSS_BORDER_STYLE_SOLID);
            css_add_prop_int(&last_prop, CSS_border_top_style,
                             CSS_BORDER_STYLE_SOLID);
            css_add_prop_int(&last_prop, CSS_border_bottom_style,
                             CSS_BORDER_STYLE_SOLID);
        }
        /* margins */
        val = css_attr_int(box, CSS_ID_hspace, -1);
        if (val >= 0) {
            css_add_prop_unit(&last_prop, CSS_margin_left,
                              CSS_UNIT_PIXEL, val);
            css_add_prop_unit(&last_prop, CSS_margin_right,
                              CSS_UNIT_PIXEL, val);
        }
        val = css_attr_int(box, CSS_ID_vspace, -1);
        if (val >= 0) {
            css_add_prop_unit(&last_prop, CSS_margin_top, 
                              CSS_UNIT_PIXEL, val);
            css_add_prop_unit(&last_prop, CSS_margin_bottom,
                              CSS_UNIT_PIXEL, val);
        }
        break;
    case CSS_ID_body:
        value = css_attr_str(box, CSS_ID_text);
        if (value && !css_get_color(&color, value)) {
            css_add_prop_int(&last_prop, CSS_color, color);
        }
        /* we handle link by adding a new stylesheet entry */
        value = css_attr_str(box, CSS_ID_link);
        if (value && !css_get_color(&color, value)) {
            CSSStyleSheetEntry *e;
            CSSSimpleSelector ss1, *ss = &ss1;
            CSSProperty **last_prop;
            CSSStyleSheetAttributeEntry **plast_attr;

            /* specific to <a href="xxx"> tag */
            memset(ss, 0, sizeof(CSSSimpleSelector));
            ss->tag = CSS_ID_a;
            plast_attr = &ss->attrs;
            add_attribute(&plast_attr, CSS_ID_href, CSS_ATTR_OP_SET, "");
                          
            e = add_style_entry(s->style_sheet, ss, CSS_MEDIA_ALL);

            /* add color property */
            last_prop = &e->props;
            css_add_prop_int(&last_prop, CSS_color, color);
        }
        break;
    case CSS_ID_font:
    case CSS_ID_basefont:
        /* size */
        value = css_attr_str(box, CSS_ID_size);
        if (value) {
            val = strtol(value, NULL, 10);
            if (value[0] == '+' || value[0] == '-') {
                /* relative size */
                val += s->base_font;
            }
            if (val < 1)
                val = 1;
            else if (val > 7)
                val = 7;
            if (box->tag == CSS_ID_basefont)
                s->base_font = val;
            /* XXX: incorrect for basefont */
            css_add_prop_unit(&last_prop, CSS_font_size, 
                              CSS_UNIT_IN, get_font_size(val - 1));
        }
            
        /* color */
        value = css_attr_str(box, CSS_ID_color);
        if (value && !css_get_color(&color, value)) {
            css_add_prop_int(&last_prop, CSS_color, color);
        }
        break;
    case CSS_ID_br:
        value = css_attr_strlower(box, CSS_ID_clear);
        if (value) {
            val = css_get_enum(value, "none,left,right,all");
            if (val >= 0) {
                css_add_prop_int(&last_prop, CSS_clear, val + CSS_CLEAR_NONE);
            }
        }
        break;
    case CSS_ID_table:
        val = css_attr_int(box, CSS_ID_width, -1);
        if (val >= 0) {
            css_add_prop_unit(&last_prop, CSS_width,
                              CSS_UNIT_PIXEL, val);
        }
        border = css_attr_int(box, CSS_ID_border, -1);
        if (border >= 0) {
            css_add_prop_unit(&last_prop, CSS_border_left_width, 
                              CSS_UNIT_PIXEL, border);
            css_add_prop_unit(&last_prop, CSS_border_right_width,
                              CSS_UNIT_PIXEL, border);
            css_add_prop_unit(&last_prop, CSS_border_top_width,
                              CSS_UNIT_PIXEL, border);
            css_add_prop_unit(&last_prop, CSS_border_bottom_width,
                              CSS_UNIT_PIXEL, border);
            css_add_prop_int(&last_prop, CSS_border_left_style,
                             CSS_BORDER_STYLE_GROOVE);
            css_add_prop_int(&last_prop, CSS_border_right_style,
                             CSS_BORDER_STYLE_GROOVE);
            css_add_prop_int(&last_prop, CSS_border_top_style,
                             CSS_BORDER_STYLE_GROOVE);
            css_add_prop_int(&last_prop, CSS_border_bottom_style,
                             CSS_BORDER_STYLE_GROOVE);
            /* cell have a border of 1 pixel width */
            if (border > 1)
                border = 1;
        }
        val = css_attr_int(box, CSS_ID_cellspacing, -1);
        if (val >= 0) {
            css_add_prop_unit(&last_prop, CSS_border_spacing_horizontal, 
                              CSS_UNIT_PIXEL, val);
            css_add_prop_unit(&last_prop, CSS_border_spacing_vertical,
                              CSS_UNIT_PIXEL, val);
        }
        padding = css_attr_int(box, CSS_ID_cellpadding, -1);
        /* apply border styles to each cell (cannot be done exactly by
           CSS) */
        if (border >= 1 || padding >= 1)
            html_table_borders(box, border, padding);
        break;

    case CSS_ID_col:
    case CSS_ID_colgroup:
        val = css_attr_int(box, CSS_ID_width, -1);
        if (val >= 0) {
            css_add_prop_unit(&last_prop, CSS_width, CSS_UNIT_PIXEL, val);
        }
        break;
    case CSS_ID_td:
        val = css_attr_int(box, CSS_ID_width, -1);
        if (val >= 0) {
            css_add_prop_unit(&last_prop, CSS_width, CSS_UNIT_PIXEL, val);
        }
        val = css_attr_int(box, CSS_ID_height, -1);
        if (val >= 0) {
            css_add_prop_unit(&last_prop, CSS_height, CSS_UNIT_PIXEL, val);
        }
        break;

    case CSS_ID_ol:
    case CSS_ID_li:
        /* NOTE: case is important */
        /* XXX: currently cannot propagate for LI tag */
        value = css_attr_str(box, CSS_ID_type);
        if (value) {
            val = css_get_enum(value, "1,a,A,i,I");
            if (val >= 0) {
                val += CSS_LIST_STYLE_TYPE_DECIMAL;
                css_add_prop_int(&last_prop, CSS_list_style_type, val);
            }
        }
        /* XXX: add value, but needs a css extension */
        if (box->tag == CSS_ID_ol) 
            val = CSS_ID_start;
        else
            val = CSS_ID_value;
        /* NOTE: only works with digits */
        val = css_attr_int(box, val, 0);
        if (val > 0) {
            args[0].type = CSS_VALUE_IDENT;
            args[0].u.val = CSS_ID_list_item;
            args[1].type = CSS_VALUE_INTEGER;
            args[1].u.val = val - 1; /* currently needs minus 1 */
            css_add_prop_values(&last_prop, CSS_counter_reset, 2, args);
        }
        break;
        /* controls */
    case CSS_ID_button:
        type = CSS_ID_submit;
        value = css_attr_strlower(box, CSS_ID_type);
        if (value)
            type = css_new_ident(value);
        if (type != CSS_ID_button && type != CSS_ID_reset)
            type = CSS_ID_submit;
        goto parse_input;
    case CSS_ID_input:
        type = CSS_ID_text;
        value = css_attr_strlower(box, CSS_ID_type);
        if (value) {
            type = css_new_ident(value);
        } else {
            CSSAttribute *attr;
            /* NOTE: we add an attribute for css rules */
            attr = box_new_attr(CSS_ID_type, "text");
            if (attr) {
                attr->next = box->attrs;
                box->attrs = attr;
            }
        }
            
    parse_input:
        if (type == CSS_ID_image)
            goto parse_img;
        if (type == CSS_ID_button ||
            type == CSS_ID_reset ||
            type == CSS_ID_submit ||
            type == CSS_ID_text ||
            type == CSS_ID_password ||
            type == CSS_ID_file) {
            /* put text inside the box (XXX: use attr() in content
               attribute ? */
            value = css_attr_str(box, CSS_ID_value);
            if (value) {
                css_set_text_string(box, value);
            }
        }
        /* size */
        if (type == CSS_ID_text ||
            type == CSS_ID_password) {
            val = css_attr_int(box, CSS_ID_size, 10);
            css_add_prop_unit(&last_prop, CSS_width,
                              CSS_UNIT_EM, val << CSS_LENGTH_FRAC_BITS);
        }
        break;
    case CSS_ID_textarea:
        val = css_attr_int(box, CSS_ID_cols, 10);
        if (val < 1)
            val = 1;
        css_add_prop_unit(&last_prop, CSS_width, 
                          CSS_UNIT_EM, val << CSS_LENGTH_FRAC_BITS);

        val = css_attr_int(box, CSS_ID_rows, 1);
        if (val < 1)
            val = 1;
        css_add_prop_unit(&last_prop, CSS_height, 
                          CSS_UNIT_EM, val << CSS_LENGTH_FRAC_BITS);
        break;
    case CSS_ID_select:
        val = css_attr_int(box, CSS_ID_size, 1);
        if (val < 1)
            val = 1;
        css_add_prop_unit(&last_prop, CSS_height, 
                          CSS_UNIT_EM, val << CSS_LENGTH_FRAC_BITS);
        break;
    default:
        break;
    }

    /* generic attributes */
    value = css_attr_str(box, CSS_ID_bgcolor);
    if (value && !css_get_color(&color, value)) {
        css_add_prop_int(&last_prop, CSS_background_color, color);
    }
    value = css_attr_strlower(box, CSS_ID_align);
    if (value) {
        switch (box->tag) {
        case CSS_ID_caption:
            /* use caption-side property for captions */
            val = css_get_enum(value, "top,bottom,left,right");
            if (val >= 0) {
                css_add_prop_int(&last_prop, CSS_caption_side, val);
            }
            break;
        case CSS_ID_img:
            /* floating images */
            val = css_get_enum(value, "left,right");
            if (val >= 0) {
                css_add_prop_int(&last_prop, CSS_float, val + CSS_FLOAT_LEFT);
            }
            break;
        case CSS_ID_table:
            val = css_get_enum(value, "left,right,center");
            if (val == CSS_TEXT_ALIGN_LEFT || val == CSS_TEXT_ALIGN_RIGHT) {
                css_add_prop_int(&last_prop, CSS_float, val + CSS_FLOAT_LEFT);
            } else if (val == CSS_TEXT_ALIGN_CENTER) {
                css_add_prop_int(&last_prop, CSS_margin_left, CSS_AUTO);
                css_add_prop_int(&last_prop, CSS_margin_right, CSS_AUTO);
            }
            break;
        default:
            val = css_get_enum(value, "left,right,center");
            if (val >= 0) {
                css_add_prop_int(&last_prop, CSS_text_align, val);
            }
            break;
        }
    }
    value = css_attr_strlower(box, CSS_ID_valign);
    if (value) {
        val = css_get_enum(value, "baseline,,,top,,middle,bottom");
        if (val >= 0) {
            css_add_prop_int(&last_prop, CSS_vertical_align, val);
        }
    }

    val = css_attr_int(box, CSS_ID_colspan, 1);
    if (val > 1) {
        css_add_prop_unit(&last_prop, CSS_column_span, CSS_VALUE_INTEGER, val);
    }
    val = css_attr_int(box, CSS_ID_rowspan, 1);
    if (val > 1) {
        css_add_prop_unit(&last_prop, CSS_row_span, CSS_VALUE_INTEGER, val);
    }

    value = css_attr_str(box, CSS_ID_style);
    if (value) {
        CSSParseState b1, *b = &b1;
        b->ptr = NULL;
        b->line_num = s->line_num; /* XXX: slightly incorrect */
        b->filename = s->filename;
        b->ignore_case = s->ignore_case;
        *last_prop = css_parse_properties(b, value);
    }
    box->properties = first_prop;
}
Ejemplo n.º 3
0
/* flags: only XML_IGNORE_CASE is handled */
void css_parse_style_sheet(CSSStyleSheet *s, CSSParseState *b)
{
    char value[1024];
    char tag[64];
    char tag_id[64];
    char *q;
    int ch, media, val, last_tree_op, i;
    CSSStyleSheetEntry *e, **first_eprops;
    CSSSimpleSelector ss2, *ss = &ss2, *last_ss, *ss1;
    CSSProperty *props;

    ch = bgetc(b);
    media = CSS_MEDIA_ALL;
    for (;;) {
redo:
        first_eprops = s->plast_entry;
        bskip_spaces(b, &ch);
        if (ch == EOF)
            break;
        /* eat inserted HTML comments for compatible STYLE tag parsing */
        if (ch == '<') {
            beat(b, &ch, "<!--");
            goto redo;
        } else if (ch == '-') {
            beat(b, &ch, "-->");
            goto redo;
        }

        /* handle '@media { ... }' */
        if (ch == '@') {
            ch = bgetc(b);
            read_ident(b, &ch, tag, sizeof(tag));
            switch (css_get_enum(tag, "media,page")) {
            case 0:
                /* @media */
                media = 0;
                for (;;) {
                    bskip_spaces(b, &ch);
                    read_ident(b, &ch, tag, sizeof(tag));
                    val = css_get_enum(tag, "tty,screen,print,tv,speech,all");
                    if (val < 0 || val == 5)
                        media = CSS_MEDIA_ALL;
                    else
                        media |= (1 << val);
                    bskip_spaces(b, &ch);
                    if (ch == ',') {
                        ch = bgetc(b);
                    } else if (ch == '{' || ch == EOF) {
                        ch = bgetc(b);
                        break;
                    }
                }
                goto redo;
            case 1:
                /* @page */
                bskip_spaces(b, &ch);
                if (ch != '{') {
                    read_ident(b, &ch, tag_id, sizeof(tag_id));
                    bskip_spaces(b, &ch);
                }
                memset(ss, 0, sizeof(CSSSimpleSelector));
                ss->tag = css_new_ident("@page");
                if (tag_id[0] != '\0')
                    ss->tag_id = css_new_ident(tag_id);
                add_style_entry(s, ss, media);
                goto parse_props;
            default:
                css_error1(b, "unrecognized css directive '@%s'", tag);
                break;
            }
        } else if (ch == '}') {
            /* XXX: end of media, should unstack */
            ch = bgetc(b);
            goto redo;
        }

        /* parse a selector list */
        for (;;) {
            /* parse simple selectors with operations */
            last_ss = NULL;
            last_tree_op = CSS_TREE_OP_NONE;
            for (;;) {
                int tree_op;
                bskip_spaces(b, &ch);
                parse_simple_selector(ss, b, &ch);
                bskip_spaces(b, &ch);
                ss->tree_op = last_tree_op;
                ss->next = last_ss;
                if (ch == '+') {
                    tree_op = CSS_TREE_OP_PRECEEDED;
                    ch = bgetc(b);
                    goto add_tree;
                } else if (ch == '>') {
                    tree_op = CSS_TREE_OP_CHILD;
                    ch = bgetc(b);
                    goto add_tree;
                } else if (isalpha(ch)) {
                    tree_op = CSS_TREE_OP_DESCENDANT;
add_tree:
                    ss1 = malloc(sizeof(CSSSimpleSelector));
                    if (ss1) {
                        memcpy(ss1, ss, sizeof(CSSSimpleSelector));
                        last_ss = ss1;
                    }
                    last_tree_op = tree_op;
                } else {
                    /* other char: exit */
                    break;
                }
            }
            add_style_entry(s, ss, media);

            /* get next selector, if present */
            if (ch != ',')
                break;
            ch = bgetc(b);
        }
parse_props:
        /* expect start of properties */
        if (ch != '{')
            break;
        ch = bgetc(b);

        q = value;
        while (ch != '}' && ch != EOF) {
            if ((q - value) < (int)sizeof(value) - 1)
                *q++ = ch;
            ch = bgetc(b);
        }
        *q = '\0';

        if (ch == '}')
            ch = bgetc(b);

        /* the properties are extracted, now add them to each tag */
        /* XXX: should locate entries first, then add, to avoid adding
           duplicate entries */
        /* XXX: should put font properties first to avoid em/ex units
           problems, but it would still not be sufficient. */
        props = css_parse_properties(b, value);
        i = 0;
        for (e = *first_eprops; e != NULL; e = e->next) {
            if (i == 0)
                e->props = props;
            else
                e->props = dup_properties(props);
            i++;
        }
    }
#ifdef DEBUG
    css_dump_style_sheet(s);
#endif
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
/* return a length and its unit. some normalization is done to limit
   the number of units. Return non zero if error. */
static int css_get_length(int *length_ptr, int *unit_ptr, const char *p)
{
    int num, len, unit;
    const char *p1;
    char buf[32];
    float f;

    p1 = p;
    if (*p == '+' || *p == '-')
        p++;
    while (isdigit((unsigned char)*p))
        p++;
    if (*p == '.') {
        p++;
        while (isdigit((unsigned char)*p))
            p++;
    }
    len = p - p1;
    if (len == 0)
        return -1;
    if (len > (int)sizeof(buf) - 1)
        len = (int)sizeof(buf) - 1;
    memcpy(buf, p1, len);
    buf[len] = '\0';
    f = strtod(buf, NULL);
    unit = css_get_enum(p, "px,%,ex,em,mm,in,cm,pt,pc");
    if (unit < 0) {
        /* only 0 is valid without unit */
        if (f != 0.0 || *p != '\0')
            return -1;
        num = 0;
        unit = CSS_UNIT_NONE;
    } else {
        unit++;
        if (f < 0 && unit != CSS_UNIT_PERCENT)
            return -1;
        switch (unit) {
        case CSS_UNIT_PIXEL:
            num = (int)(f);
            break;
        case CSS_UNIT_PERCENT:
            if (f < 0) {
                if (f >= 100)
                    return -1;
                f = 100 - f;
            }
            num = (int)(f * (0.01 * CSS_LENGTH_FRAC_BASE));
            break;
        case CSS_UNIT_EX:
        case CSS_UNIT_EM:
            num = (int)(f * CSS_LENGTH_FRAC_BASE);
            break;
        case CSS_UNIT_IN:
phys_unit:
            unit = CSS_UNIT_IN;
            num = (int)(f * CSS_LENGTH_FRAC_BASE);
            break;
        case CSS_UNIT_MM:
            f = f / 25.4;
            goto phys_unit;
        case CSS_UNIT_CM:
            f = f / 2.54;
            goto phys_unit;
        case CSS_UNIT_PT:
            f = f / 72.0;
            goto phys_unit;
        case CSS_UNIT_PC:
            f = f * 12.0 / 72.0;
            goto phys_unit;
        default:
            return -1;
        }
    }
    *length_ptr = num;
    *unit_ptr = unit;
    return 0;
}
Ejemplo n.º 6
0
/* 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;
}