/** * Read the next rule from the input data * @param data the CSS data to read from * @param offset VAR param of the offset into data to read from * @return the rule or NULL */ static css_rule *get_css_rule( const char *data, int *offset ) { int pos = *offset; int state = 0; int bodyStart=-1,bodyEnd=-1; css_rule *rule = NULL; while ( state >= 0 && data[pos] != 0 ) { switch ( state ) { case 0: // collecting selector if ( data[pos] == '{' ) { bodyStart = pos; state = 1; } pos++; break; case 1: // collecting body if ( data[pos] == '}' ) { bodyEnd = pos; state = -1; } pos++; break; } } if ( (bodyStart == -1 || bodyEnd == -1)&&state!=0 ) { warning("failed to find css rule body", 0 ); return NULL; } else { rule = css_rule_create(); if ( rule != NULL ) { int res = get_selectors( data, *offset, bodyStart-(*offset), rule ); if ( res ) res = get_properties( &data[bodyStart+1], (bodyEnd-bodyStart), rule ); } // update pos even if rule-reading fails *offset = pos; if ( css_rule_valid(rule) ) return rule; else if ( rule != NULL ) css_rule_dispose( rule ); return NULL; } }
/* TODO: add more error checks, cut corner cases */ int css_parse(css_parser *state, const char *data, int len) { #define CHANGE_BUFFER(BUFF) \ state->tobuf = (BUFF); \ *(state->tobuf) = 0; \ state->tobuf_space = sizeof((BUFF)) #define OUTER_SPACE 0 /* whitespace outside the ___sel{}___ */ #define INNER_SPACE 9 /* whitespace inside the {___prop:val;___} */ #define MIDDLE_SPACE 8 /* whitespace between prop:____val */ #define COMMENT 1 #define SUBSTRING 5 #define SELECTORS 3 #define PROPERTY 6 #define VALUE 7 int i, j; #if 0 printf("\n");//debug printf("BUF2>> %s[end]\n", state->selector_buffer); printf("BUF3>> %s[end]\n", state->property_buffer); printf("BUF4>> %s[end]\n", state->value_buffer); #endif for (i = 0; i < len; i++) { char c = data[i]; //printf("[%d] -- %c\n", sector, c); //if ( c == '\n' || c == '\r' || c == '\t') continue; if (isspace(c)) { if (state->sector == OUTER_SPACE) continue; /* ignore outer whitespace */ if (state->sector == PROPERTY) continue; /* ignore whitespace in property names -- note, not clean, might lead to errors */ if (state->sector == INNER_SPACE) continue; /* ignore whitespace after property values */ if (state->sector == MIDDLE_SPACE) continue; /* ignore whitespace inside rule blocks? */ } if (c == '/' && state->sector == COMMENT) { /* end of comment */ /* pop sector */ state->sector = state->prev_sector; state->prev_sector = 0; continue; } if (state->sector == COMMENT) { /* inside a comment, ignore */ continue; } if (c == '/') { /* start of comment */ /* push sector */ state->prev_sector = state->sector; state->sector = COMMENT; continue; } if (c == 0) break; /* not readable */ if (c == ',') { /* end of selector? */ if (state->sector == SELECTORS) { //printf("Got selector %d: `%s`\n", num_selectors, buf2); state->selectors[state->num_selectors++] = css_target_parse(state->selector_buffer); CHANGE_BUFFER(state->selector_buffer); state->sector = OUTER_SPACE; continue; } } if (c == '{') { /* start of rule block? */ if (state->sector == SELECTORS) { //printf("Got last selector: `%s`\n", buf2); state->selectors[state->num_selectors++] = css_target_parse(state->selector_buffer); CHANGE_BUFFER(state->selector_buffer); } /* now, create empty rules for each selector */ for (j = 0; j < state->num_selectors; j++) { state->rules[j] = css_rule_create(state->selectors[j], NULL, NULL); state->num_rules++; if (!state->style) state->style = state->rules[j]; else css_rule_add_next(state->style, state->rules[j]); } /* and reset "pending" selectors */ state->num_selectors = 0; state->sector = INNER_SPACE; continue; } /* if (sector == SELECTORS) { // inside selectors, condense all spaces if (isspace(c)) { extra_space = 1; continue; } else if (extra_space) { *tobuf++ = ' '; extra_space = 0; } } */ if (c == ':' && state->sector == PROPERTY) { /* end of property name */ state->sector = MIDDLE_SPACE; continue; } if (c == ';' && state->sector == VALUE) { /* end of property value */ /* add prop/value to all pending rules */ for (j = 0; j < state->num_rules; j++) css_rule_add_one(state->rules[j], state->property_buffer, strtrim(state->value_buffer)); //printf("Added `%s=%s` to %d rule(s)\n", buf3, buf4, num_rules); state->sector = INNER_SPACE; continue; } if (c == '}') { if (state->sector == VALUE) { /* add prop/value to all pending rules */ for (j = 0; j < state->num_rules; j++) css_rule_add_one(state->rules[j], state->property_buffer, strtrim(state->value_buffer)); //printf("Added `%s=%s` to %d rule(s)\n", buf3, buf4, num_rules); } else if (state->sector != INNER_SPACE) { fprintf(stderr, "Unexpected '}', expected value or ';'\n"); return 1; } /* reset rules */ state->num_rules = 0; state->sector = OUTER_SPACE; continue; } if (!isspace(c)) { if (state->sector == INNER_SPACE) { /* start of property name */ state->sector = PROPERTY; CHANGE_BUFFER(state->property_buffer); /* select "property" buffer for output */ } if (state->sector == MIDDLE_SPACE) { /* start of value */ state->sector = VALUE; CHANGE_BUFFER(state->value_buffer); /* select "value" buffer for output */ } } if (c == '"') { /* push/pop in-string */ if (state->sector == SUBSTRING) { state->sector = state->prev_sector; state->prev_sector = 0; } else { state->prev_sector = state->sector; state->sector = SUBSTRING; } continue; } if (isalpha(c) || c == '*' || c == '.' || c == '#') { if (state->sector == OUTER_SPACE) { /* start of selector(s) */ state->sector = SELECTORS; CHANGE_BUFFER(state->selector_buffer); /* select "selectors" buffer for output */ } } /* Buffer overflow */ if (state->tobuf_space < 2) { fprintf(stderr, "No buffer space to store selector/property/value, ABORTING parsing.\n"); return 1; } //printf("Saving `%c`, space left: %d\n", c, tobuf_space); /* Save character to either buffer */ *state->tobuf++ = c; *state->tobuf = 0; state->tobuf_space--; } return 0; }