/**
 * 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;
	}
}
Exemplo n.º 2
0
/* 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;
}