Example #1
0
/**
 * Parse !important
 *
 * \param c       Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx     Pointer to vector iteration context
 * \param result  Pointer to location to receive result
 * \return CSS_OK on success,
 *         CSS_INVALID if "S* ! S* important" is not at the start of the vector
 *
 * Post condition: \a *ctx is updated with the next token to process
 *                 If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_important(css_language *c,
		const parserutils_vector *vector, int *ctx,
		uint8_t *result)
{
	int orig_ctx = *ctx;
	bool match = false;
	const css_token *token;

	consumeWhitespace(vector, ctx);

	token = parserutils_vector_iterate(vector, ctx);
	if (token != NULL && tokenIsChar(token, '!')) {
		consumeWhitespace(vector, ctx);

		token = parserutils_vector_iterate(vector, ctx);
		if (token == NULL || token->type != CSS_TOKEN_IDENT) {
			*ctx = orig_ctx;
			return CSS_INVALID;
		}

		if (lwc_string_caseless_isequal(token->idata, c->strings[IMPORTANT],
				&match) == lwc_error_ok && match) {
			*result |= FLAG_IMPORTANT;
		} else {
			*ctx = orig_ctx;
			return CSS_INVALID;
		}
	} else if (token != NULL) {
		*ctx = orig_ctx;
		return CSS_INVALID;
	}

	return CSS_OK;
}
static bool consumeFrequency(const char* input, int* index, Microseconds* result) {
    const char* expectedToken;
    double val;

    if (!consumeDouble(input, index, &val)) {
        return false;
    }

    consumeWhitespace(input, index);
    switch (input[*index]) {
        case 'H':
            expectedToken = "Hz";
            break;

        case 'k':
            expectedToken = "kHz";
            val *= 1000;
            break;

        default:
            return false;
    }

    *result = Microseconds(1000000/val);

    return consumeToken(expectedToken, input, index);
}
Example #3
0
  TokenInfo getNextToken() {
    consumeWhitespace();
    TokenInfo Result;
    Result.Range.Start = currentLocation();

    if (Code.empty()) {
      Result.Kind = TokenInfo::TK_Eof;
      Result.Text = "";
      return Result;
    }

    switch (Code[0]) {
    case ',':
      Result.Kind = TokenInfo::TK_Comma;
      Result.Text = Code.substr(0, 1);
      Code = Code.drop_front();
      break;
    case '(':
      Result.Kind = TokenInfo::TK_OpenParen;
      Result.Text = Code.substr(0, 1);
      Code = Code.drop_front();
      break;
    case ')':
      Result.Kind = TokenInfo::TK_CloseParen;
      Result.Text = Code.substr(0, 1);
      Code = Code.drop_front();
      break;

    case '"':
    case '\'':
      // Parse a string literal.
      consumeStringLiteral(&Result);
      break;

    default:
      if (isAlphanumeric(Code[0])) {
        // Parse an identifier
        size_t TokenLength = 1;
        while (TokenLength < Code.size() && isAlphanumeric(Code[TokenLength]))
          ++TokenLength;
        Result.Kind = TokenInfo::TK_Ident;
        Result.Text = Code.substr(0, TokenLength);
        Code = Code.drop_front(TokenLength);
      } else {
        Result.Kind = TokenInfo::TK_InvalidChar;
        Result.Text = Code.substr(0, 1);
        Code = Code.drop_front(1);
      }
      break;
    }

    Result.Range.End = currentLocation();
    return Result;
  }
static bool consumeTime(const char* input, int* index, Microseconds* result) {
    const char* expectedToken;
    double val;

    if (!consumeDouble(input, index, &val)) {
        return false;
    }

    consumeWhitespace(input, index);
    switch (input[*index]) {
        case 'u':
            expectedToken = "us";
            break;

        case '\xB5': // Latin-1 micro
            expectedToken = "\xB5s";
            break;

        case '\xC2': // micro, utf-8
            expectedToken = "\xC2\xB5s";
            break;

        case '\xCE': // mu, utf-8
            expectedToken = "\xCE\xBCs";
            break;

        case 'm':
            expectedToken = "ms";
            val *= 1000;
            break;

        case 's':
            expectedToken = "s";
            val *= 1000000;
            break;

        default:
            return false;
    }

    *result = Microseconds(val);

    return consumeToken(expectedToken, input, index);
}
Example #5
0
/**
 * Parse a comma separated list, converting to bytecode
 *
 * \param c          Parsing context
 * \param vector     Vector of tokens to process
 * \param ctx        Pointer to vector iteration context
 * \param reserved   Callback to determine if an identifier is reserved
 * \param get_value  Callback to retrieve bytecode value for a token
 * \param style      Pointer to output style
 * \return CSS_OK      on success,
 *         CSS_INVALID if the input is invalid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *                 If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__comma_list_to_style(css_language *c,
		const parserutils_vector *vector, int *ctx,
		bool (*reserved)(css_language *c, const css_token *ident),
		css_code_t (*get_value)(css_language *c, const css_token *token, bool first),
		css_style *result)
{
	int orig_ctx = *ctx;
	int prev_ctx = orig_ctx;
	const css_token *token;
	bool first = true;
	css_error error = CSS_OK;

	token = parserutils_vector_iterate(vector, ctx);
	if (token == NULL) {
		*ctx = orig_ctx;
		return CSS_INVALID;
	}

	while (token != NULL) {
		if (token->type == CSS_TOKEN_IDENT) {
			css_code_t value = get_value(c, token, first);

			if (reserved(c, token) == false) {
				lwc_string *str = NULL;
				uint32_t snumber;

				*ctx = prev_ctx;

				error = css__ident_list_to_string(c, vector, ctx,
						reserved, &str);
				if (error != CSS_OK)
					goto cleanup;

				error = css__stylesheet_string_add(c->sheet,
						str, &snumber);
				if (error != CSS_OK)
					goto cleanup;

				error = css__stylesheet_style_append(result, 
						value);
				if (error != CSS_OK)
					goto cleanup;

				error = css__stylesheet_style_append(result,
						snumber);
				if (error != CSS_OK)
					goto cleanup;
			} else {
				error = css__stylesheet_style_append(result,
						value);
				if (error != CSS_OK)
					goto cleanup;
			}
		} else if (token->type == CSS_TOKEN_STRING) {
			css_code_t value = get_value(c, token, first);
			uint32_t snumber;

			error = css__stylesheet_string_add(c->sheet, 
					lwc_string_ref(token->idata), &snumber);
			if (error != CSS_OK)
				goto cleanup;

			error = css__stylesheet_style_append(result, value);
			if (error != CSS_OK)
				goto cleanup;

			error = css__stylesheet_style_append(result, snumber);
			if (error != CSS_OK)
				goto cleanup;
		} else {
			error = CSS_INVALID;
			goto cleanup;
		}

		consumeWhitespace(vector, ctx);

		token = parserutils_vector_peek(vector, *ctx);
		if (token != NULL && tokenIsChar(token, ',')) {
			parserutils_vector_iterate(vector, ctx);

			consumeWhitespace(vector, ctx);

			token = parserutils_vector_peek(vector, *ctx);
			if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
					token->type != CSS_TOKEN_STRING)) {
				error = CSS_INVALID;
				goto cleanup;
			}
		} else {
			break;
		}

		first = false;

		prev_ctx = *ctx;

		token = parserutils_vector_iterate(vector, ctx);
	}

cleanup:
	if (error != CSS_OK)
		*ctx = orig_ctx;

	return error;
}
Example #6
0
INSTANTIATE_READ_VALUE_FOR_TYPE(uint8_t,  false) INSTANTIATE_READ_VALUE_FOR_TYPE(uint16_t, false) INSTANTIATE_READ_VALUE_FOR_TYPE(int32_t,  false) INSTANTIATE_READ_VALUE_FOR_TYPE(uint32_t, false) INSTANTIATE_READ_VALUE_FOR_TYPE(uint64_t, true) INSTANTIATE_READ_VALUE_FOR_TYPE(uint64_t, false) INSTANTIATE_READ_VALUE_FOR_TYPE(float,    false) INSTANTIATE_READ_VALUE_FOR_TYPE(double,   false) template<typename T, bool ___2025, int baseValue> ___372 readValues( ___1399& file, size_t               ___2796, T*                   ___4299, IODescription const& ___972) { ___372 ___2039 = ___4226; REQUIRE(file.___2041()); REQUIRE(___2796>0); REQUIRE(VALID_REF(___4299)); REQUIRE(___972.___2067()); if (file.___2002()) { if (!___972.isEmpty()) ___2039 = readAndVerifyDescription(file, ___972); for (size_t ___1841 = 0; ___2039 && ___1841 < ___2796; ___1841++) { ___2039 = readAsciiValue<T, ___2025, baseValue>(file, ___4299[___1841]); ___2039 = ___2039 && consumeWhitespace(file); } } else { ___2039 = (file.fread(&___4299[0], sizeof(T), ___2796) == ___2796); } if ( !___2039 && !___972.isEmpty() ) { char formattedDescription[STRING_SIZE]; ___972.getFormattedDescription(formattedDescription, STRING_SIZE); ___1186("Error reading %" PRIu64 " %s values for %s data block.", uint64_t(___2796), ___198<T, ___2025>::typeName, formattedDescription); } ENSURE(VALID_BOOLEAN(___2039)); return ___2039; }
Example #7
0
INSTANTIATE_READ_ASCII_VALUE_PAIR(uint8_t) INSTANTIATE_READ_ASCII_VALUE_PAIR(int16_t) INSTANTIATE_READ_ASCII_VALUE_PAIR(int32_t) INSTANTIATE_READ_ASCII_VALUE_PAIR(float) INSTANTIATE_READ_ASCII_VALUE_PAIR(double) namespace { inline bool isWhitespace(___2718 const ch) { return ch == ' ' || ch == '\n' || ch == '\r'; } } namespace { ___372 consumeWhitespace(___1399& file) { REQUIRE(file.___2041()); while (!file.feof()) { int ch = file.getc(); if (!isWhitespace(ch)) { file.ungetc(ch); break; } } return ___4226; } } namespace { ___372 checkDescription( char const*          stringToCheck, IODescription const& ___972) { char formattedDescription[STRING_SIZE]; ___972.getFormattedDescription(formattedDescription, STRING_SIZE); ___372 const ___2039 = (boost::iequals(stringToCheck, formattedDescription) ? ___4226 : ___1305); ENSURE(VALID_BOOLEAN(___2039)); return ___2039; } } namespace { ___372 readAndVerifyDescription( ___1399& file, IODescription const& ___972) { REQUIRE(file.___2041()); char encounteredString[STRING_SIZE]; ___372 ___2039 = (file.fscanf(STRING_FORMAT, encounteredString) == 1); ___2039 = ___2039 && consumeWhitespace(file); if (!___2039) { char formattedDescription[STRING_SIZE]; ___972.getFormattedDescription(formattedDescription, STRING_SIZE); ___1186("Cannot read description for %s.", formattedDescription); } else { if (!checkDescription(encounteredString, ___972)) { static TagDescriptionToDescriptionMap altTagDescriptions = ALT_TAG_DESCRIPTIONS_MAP_INITIALIZER; char const* nameToCheck = ___972.___2685(); ___478(VALID_REF(nameToCheck)); while (___2039) { TagDescriptionToDescriptionMap::const_iterator altTagDescIter = altTagDescriptions.find(nameToCheck); if (altTagDescIter == altTagDescriptions.end()) { char formattedDescription[STRING_SIZE]; ___972.getFormattedDescription(formattedDescription, STRING_SIZE); ___2039 = ___1186("Mismatched descriptions: looking for '%s', found '%s' instead.", formattedDescription, encounteredString); } else { nameToCheck = altTagDescIter->second.c_str(); IODescription altDescription(nameToCheck, ___972.___4336(), ___972.zone(), ___972.___2977(), ___972.segment(), ___972.suffix()); if (checkDescription(encounteredString, altDescription)) break; } } } } ENSURE(VALID_BOOLEAN(___2039)); return ___2039; } } template<typename T, bool ___2025  > ___372 readValue( ___1399& file, T&                   ___4298, IODescription const& ___972) { REQUIRE(file.___2041()); REQUIRE(___972.___2067()); ___372 ___2039 = ___4226; if (file.___2002()) { ___2039 = ___2039 && readAndVerifyDescription(file, ___972); ___2039 = ___2039 && readAsciiValue<T, ___2025, 0>(file, ___4298); ___2039 = ___2039 && consumeWhitespace(file); } else { ___2039 = (file.fread(&___4298, sizeof(T), 1) == 1); } if (!___2039) { char formattedDescription[STRING_SIZE]; ___972.getFormattedDescription(formattedDescription, STRING_SIZE); ___1186("Cannot read %s value.", formattedDescription); } ENSURE(VALID_BOOLEAN(___2039)); return ___2039; }
void PulseStateCommand::parseFromString(const char* input, const char** error,
        unsigned* repeatDepth) {
    int index = 0;
    uint32_t val;

    consumeWhitespace(input, &index);
    if (input[index] == 0) {
        // empty line, comment, etc.
        type = noOp;
    } else if (input[index] == 'e') {
        // e.g. "end program" or "end repeat"
        if (!consumeToken("end", input, &index)) {
            *error = "unrecognized command";
            return;
        }
        consumeWhitespace(input, &index);

        if (input[index] == 'p') {
            if (!consumeToken("program", input, &index)) {
                *error = "unrecognized command";
                return;
            }
            if (*repeatDepth != 0) {
                *error = "found \"end program\" while still expecting an \"end repeat\"";
                return;
            }
            type = endProgram;
        } else if (input[index] == 'r') {
            if (!consumeToken("repeat", input, &index)) {
                *error = "unrecognized command";
                return;
            }
            if (*repeatDepth == 0) {
                *error = "found \"end repeat\" without matching \"repeat\"";
                return;
            }
            type = endRepeat;
            *repeatDepth -= 1;
        } else {
            *error = "expected \"repeat\" or \"program\"";
            return;
        }

    } else if (input[index] == 'r') {
        // e.g. "repeat 12 times:"
        if (!consumeToken("repeat", input, &index)) {
            *error = "unrecognized command";
            return;
        }
        type = repeat;

        consumeWhitespace(input, &index);
        if (!consumeUInt32(input, &index, &repeatCount)) {
            *error = "expected repeat count";
            return;
        }

        consumeWhitespace(input, &index);
        if (!consumeToken("times", input, &index)) {
            *error = "expected \"times\"";
            return;
        }

        consumeWhitespace(input, &index);
        if (!consumeToken(":", input, &index)) {
            *error = "expected \":\"";
            return;
        }
        if (*repeatDepth == maxRepeatNesting) {
            *error = "repeats nested too deeply";
            return;
        }
        *repeatDepth += 1;

    } else if (input[index] == 's') {
        // e.g. "set channel 3 to 213 us pulses at 15.1 Hz"
        if (!consumeToken("set", input, &index)) {
            *error = "unrecognized command";
            return;
        }
        type = setChannel;

        consumeWhitespace(input, &index);
        if (!consumeToken("channel", input, &index)) {
            *error = "expected \"channel\"";
            return;
        }

        consumeWhitespace(input, &index);
        if (!consumeUInt32(input, &index, &val)) {
            *error = "expected channel number";
            return;
        }
        if (val > numChannels || val == 0) {
            *error = "channel number must be between 1 and 8";
            return;
        }
        channel = val;

        consumeWhitespace(input, &index);
        if (!consumeToken("to", input, &index)) {
            *error = "expected \"to\"";
            return;
        }

        consumeWhitespace(input, &index);
        if (!consumeTime(input, &index, &onTime)) {
            *error = "expected time, e.g. \"2 s\", "
                "\"13 ms\", \"12 us\", or \"15 \u00B5s\"";
            return;
        }

        consumeWhitespace(input, &index);
        if (!consumeToken("pulses", input, &index)) {
            *error = "expected \"pulses\"";
            return;
        }

        consumeWhitespace(input, &index);

        Microseconds period;
        if (input[index] == 'a') { // e.g. "at 12 Hz"
            if (!consumeToken("at", input, &index)) {
                *error = "expected \"at\" or \"every\"";
                return;
            }

            consumeWhitespace(input, &index);
            if (!consumeFrequency(input, &index, &period)) {
                *error = "expected frequency, e.g. \"2.3 Hz\" or \"15 kHz\"";
                return;
            }
        } else { // e.g. "every 2 s"
            if (!consumeToken("every", input, &index)) {
                *error = "expected \"at\" or \"every\"";
                return;
            }

            consumeWhitespace(input, &index);
            if (!consumeTime(input, &index, &period)) {
                *error = "expected time, e.g. \"2 s\", "
                    "\"13 ms\", \"12 us\", or \"15 \u00B5s\"";
                return;
            }
        }

        if (period < onTime) {
            *error = "pulse duration longer than total period";
            return;
        }
        offTime = period - onTime;

    } else if (input[index] == 't') {
        // e.g. "turn off channel 4" or "turn on channel 1"
        if (!consumeToken("turn", input, &index)) {
            *error = "unrecognized command";
            return;
        }
        type = setChannel;

        consumeWhitespace(input, &index);
        const char* expectedToken;
        if (input[index] == 'o' && input[index + 1] == 'n') {
            expectedToken = "on";
            onTime = forever;
            offTime = 0;
        } else {
            expectedToken = "off";
            onTime = 0;
            offTime = forever;
        }
        if (!consumeToken(expectedToken, input, &index)) {
            *error = "expected \"on\" or \"off\"";
            return;
        }

        consumeWhitespace(input, &index);
        if (!consumeToken("channel", input, &index)) {
            *error = "expected \"channel\"";
            return;
        }

        consumeWhitespace(input, &index);
        if (!consumeUInt32(input, &index, &val)) {
            *error = "expected channel number";
            return;
        }
        if (val > numChannels || val == 0) {
            *error = "channel number must be between 1 and 8";
            return;
        }
        channel = val;

    } else if (input[index] == 'w') {
        // e.g. "wait 182 us"
        if (!consumeToken("wait", input, &index)) {
            *error = "unrecognized command";
            return;
        }
        type = wait;

        consumeWhitespace(input, &index);
        if (!consumeTime(input, &index, &waitTime)) {
            *error = "expected time, e.g. \"2 s\", "
                "\"13 ms\", \"12 us\", or \"15 \u00B5s\"";
            return;
        }
    } else {
        *error = "unrecognized command";
        return;
    }

    consumeWhitespace(input, &index);
    if (input[index] != 0) {
        *error = "unexpected text found after end of command";
        return;
    }

    *error = NULL;
}
Example #9
0
/**
 * Parse columns shorthand
 *
 * \param c	  Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx	  Pointer to vector iteration context
 * \param result  Pointer to location to receive resulting style
 * \return CSS_OK on success,
 *	   CSS_NOMEM on memory exhaustion,
 *	   CSS_INVALID if the input is not valid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *		   If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_columns(css_language *c,
		const parserutils_vector *vector, int *ctx,
		css_style *result)
{
	int orig_ctx = *ctx;
	int prev_ctx;
	const css_token *token;
	css_error error = CSS_OK;
	bool width = true;
	bool count = true;
	css_style *width_style;
	css_style *count_style;

	/* Firstly, handle inherit */
	token = parserutils_vector_peek(vector, *ctx);
	if (token == NULL)
		return CSS_INVALID;

	if (is_css_inherit(c, token)) {
		error = css_stylesheet_style_inherit(result,
				CSS_PROP_COLUMN_WIDTH);
		if (error != CSS_OK)
			return error;

		error = css_stylesheet_style_inherit(result,
				CSS_PROP_COLUMN_COUNT);
		if (error == CSS_OK)
			parserutils_vector_iterate(vector, ctx);

		return error;
	}

	/* Allocate for styles */
	error = css__stylesheet_style_create(c->sheet, &width_style);
	if (error != CSS_OK)
		return error;

	error = css__stylesheet_style_create(c->sheet, &count_style);
	if (error != CSS_OK) {
		css__stylesheet_style_destroy(width_style);
		return error;
	}

	/* Attempt to parse the various longhand properties */
	do {
		prev_ctx = *ctx;
		error = CSS_OK;

		if (is_css_inherit(c, token)) {
			/* Can't have another inherit */
			error = CSS_INVALID;
			goto css__parse_columns_cleanup;
		}

		/* Try each property parser in turn, but only if we
		 * haven't already got a value for this property.
		 */
		if ((width) &&
				(error = css__parse_column_width(c, vector, ctx,
						width_style)) == CSS_OK) {
			width = false;
		} else if ((count) &&
				(error = css__parse_column_count(c, vector, ctx,
						count_style)) == CSS_OK) {
			count = false;
		}

		if (error == CSS_OK) {
			consumeWhitespace(vector, ctx);

			token = parserutils_vector_peek(vector, *ctx);
		} else {
			/* Forcibly cause loop to exit */
			token = NULL;
		}
	} while (*ctx != prev_ctx && token != NULL);

	/* Set unset properties to initial values */
	if (width) {
		error = css__stylesheet_style_appendOPV(width_style,
				CSS_PROP_COLUMN_WIDTH, 0,
				COLUMN_WIDTH_AUTO);
		if (error != CSS_OK)
			goto css__parse_columns_cleanup;
	}

	if (count) {
		error = css__stylesheet_style_appendOPV(count_style,
				CSS_PROP_COLUMN_COUNT, 0,
				COLUMN_COUNT_AUTO);
		if (error != CSS_OK)
			goto css__parse_columns_cleanup;
	}

	/* Merge styles into the result */
	error = css__stylesheet_merge_style(result, width_style);
	if (error != CSS_OK)
		goto css__parse_columns_cleanup;

	error = css__stylesheet_merge_style(result, count_style);


css__parse_columns_cleanup:
	css__stylesheet_style_destroy(width_style);
	css__stylesheet_style_destroy(count_style);

	if (error != CSS_OK)
		*ctx = orig_ctx;

	return error;
}
Example #10
0
/**
 * Parse a unit specifier
 *
 * \param c             Parsing context
 * \param vector        Vector of tokens to process
 * \param ctx           Pointer to current vector iteration context
 * \param default_unit  The default unit to use if none specified
 * \param length        Pointer to location to receive length
 * \param unit          Pointer to location to receive unit
 * \return CSS_OK      on success,
 *         CSS_INVALID if the tokens do not form a valid unit
 *
 * Post condition: \a *ctx is updated with the next token to process
 *                 If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_unit_specifier(css_language *c,
		const parserutils_vector *vector, int *ctx,
		uint32_t default_unit,
		css_fixed *length, uint32_t *unit)
{
	int orig_ctx = *ctx;
	const css_token *token;
	css_fixed num;
	size_t consumed = 0;
	css_error error;

	consumeWhitespace(vector, ctx);

	token = parserutils_vector_iterate(vector, ctx);
	if (token == NULL || (token->type != CSS_TOKEN_DIMENSION &&
			token->type != CSS_TOKEN_NUMBER &&
			token->type != CSS_TOKEN_PERCENTAGE)) {
		*ctx = orig_ctx;
		return CSS_INVALID;
	}

	num = css__number_from_lwc_string(token->idata, false, &consumed);

	if (token->type == CSS_TOKEN_DIMENSION) {
		size_t len = lwc_string_length(token->idata);
		const char *data = lwc_string_data(token->idata);
		css_unit temp_unit = CSS_UNIT_PX;

		error = css__parse_unit_keyword(data + consumed, len - consumed,
				&temp_unit);
		if (error != CSS_OK) {
			*ctx = orig_ctx;
			return error;
		}

		*unit = (uint32_t) temp_unit;
	} else if (token->type == CSS_TOKEN_NUMBER) {
		/* Non-zero values are permitted in quirks mode */
		if (num != 0) {
			if (c->sheet->quirks_allowed) {
				c->sheet->quirks_used = true;
			} else {
				*ctx = orig_ctx;
				return CSS_INVALID;
			}
		}

		*unit = default_unit;

		if (c->sheet->quirks_allowed) {
			/* Also, in quirks mode, we need to cater for
			 * dimensions separated from their units by whitespace
			 * (e.g. "0 px")
			 */
			int temp_ctx = *ctx;
			css_unit temp_unit;

			consumeWhitespace(vector, &temp_ctx);

			/* Try to parse the unit keyword, ignoring errors */
			token = parserutils_vector_iterate(vector, &temp_ctx);
			if (token != NULL && token->type == CSS_TOKEN_IDENT) {
				error = css__parse_unit_keyword(
						lwc_string_data(token->idata),
						lwc_string_length(token->idata),
						&temp_unit);
				if (error == CSS_OK) {
					c->sheet->quirks_used = true;
					*ctx = temp_ctx;
					*unit = (uint32_t) temp_unit;
				}
			}
		}
	} else {
		/* Percentage -- number must be entire token data */
		if (consumed != lwc_string_length(token->idata)) {
			*ctx = orig_ctx;
			return CSS_INVALID;
		}
		*unit = UNIT_PCT;
	}

	*length = num;

	return CSS_OK;
}
Example #11
0
/**
 * Parse play-during
 *
 * \param c	  Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx	  Pointer to vector iteration context
 * \param result  Pointer to location to receive resulting style
 * \return CSS_OK on success,
 *	   CSS_NOMEM on memory exhaustion,
 *	   CSS_INVALID if the input is not valid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *		   If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_play_during(css_language *c, 
		const parserutils_vector *vector, int *ctx, 
		css_style *result)
{
	int orig_ctx = *ctx;
	css_error error;
	const css_token *token;
	uint8_t flags = 0;
	uint16_t value = 0;
	lwc_string *uri;
	bool match;
	uint32_t uri_snumber;

	/* URI [ IDENT(mix) || IDENT(repeat) ]? | IDENT(auto,none,inherit) */
	token = parserutils_vector_iterate(vector, ctx);
	if ((token == NULL) || 
	    ((token->type != CSS_TOKEN_IDENT) &&
	     (token->type != CSS_TOKEN_URI))) {
		*ctx = orig_ctx;
		return CSS_INVALID;
	}

	if (token->type == CSS_TOKEN_IDENT) {
		if ((lwc_string_caseless_isequal(
				token->idata, c->strings[INHERIT],
				&match) == lwc_error_ok && match)) {
			flags |= FLAG_INHERIT;
		} else if ((lwc_string_caseless_isequal(
				token->idata, c->strings[NONE],
				&match) == lwc_error_ok && match)) {
			value = PLAY_DURING_NONE;
		} else if ((lwc_string_caseless_isequal(
				token->idata, c->strings[AUTO],
				&match) == lwc_error_ok && match)) {
			value = PLAY_DURING_AUTO;
		} else {
			*ctx = orig_ctx;
			return CSS_INVALID;
		}
	} else {
		int modifiers;

		value = PLAY_DURING_URI;

		error = c->sheet->resolve(c->sheet->resolve_pw,
				c->sheet->url,
				token->idata, &uri);
		if (error != CSS_OK) {
			*ctx = orig_ctx;
			return error;
		}

		error = css__stylesheet_string_add(c->sheet, 
						  uri, 
						  &uri_snumber);
		if (error != CSS_OK) {
			*ctx = orig_ctx;
			return error;
		}


		for (modifiers = 0; modifiers < 2; modifiers++) {
			consumeWhitespace(vector, ctx);

			token = parserutils_vector_peek(vector, *ctx);
			if (token != NULL && token->type == CSS_TOKEN_IDENT) {
				if ((lwc_string_caseless_isequal(
						token->idata, c->strings[MIX],
						&match) == lwc_error_ok && 
						match)) {
					if ((value & PLAY_DURING_MIX) == 0)
						value |= PLAY_DURING_MIX;
					else {
						*ctx = orig_ctx;
						return CSS_INVALID;
					}
				} else if (lwc_string_caseless_isequal(
						token->idata, 
						c->strings[REPEAT],
						&match) == lwc_error_ok &&
						match) {
					if ((value & PLAY_DURING_REPEAT) == 0)
						value |= PLAY_DURING_REPEAT;
					else {
						*ctx = orig_ctx;
						return CSS_INVALID;
					}
				} else {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				parserutils_vector_iterate(vector, ctx);
			}
		}
	}

	error = css__stylesheet_style_appendOPV(result, CSS_PROP_PLAY_DURING, flags, value);
	if (error != CSS_OK) {
		*ctx = orig_ctx;
		return error;
	}

	if ((flags & FLAG_INHERIT) == false && 
	    (value & PLAY_DURING_TYPE_MASK) == PLAY_DURING_URI) {
		error = css__stylesheet_style_append(result, uri_snumber);
	}

	if (error != CSS_OK) 
		*ctx = orig_ctx;

	return error;
}
Example #12
0
/**
 * Parse border-color shorthand
 *
 * \param c	  Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx	  Pointer to vector iteration context
 * \param result  Pointer to location to receive resulting style
 * \return CSS_OK on success,
 *	   CSS_NOMEM on memory exhaustion,
 *	   CSS_INVALID if the input is not valid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *		   If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_border_color(css_language *c,
		const parserutils_vector *vector, int *ctx,
		css_style *result)
{
	int orig_ctx = *ctx;
	int prev_ctx;
	const css_token *token;
	uint16_t side_val[4];
	uint32_t side_color[4];
	uint32_t side_count = 0;
	css_error error;

	/* Firstly, handle inherit */
	token = parserutils_vector_peek(vector, *ctx);
	if (token == NULL) 
		return CSS_INVALID;
		
	if (is_css_inherit(c, token)) {
		error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_COLOR);
		if (error != CSS_OK) 
			return error;

		error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_RIGHT_COLOR);
		if (error != CSS_OK) 
			return error;		

		error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_BOTTOM_COLOR);
		if (error != CSS_OK) 
			return error;

		error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_LEFT_COLOR);
		if (error == CSS_OK) 
			parserutils_vector_iterate(vector, ctx);

		return error;
	} 

	/* Attempt to parse up to 4 colours */
	do {
		prev_ctx = *ctx;

		if ((token != NULL) && is_css_inherit(c, token)) {
			*ctx = orig_ctx;
			return CSS_INVALID;
		}

		error = css__parse_colour_specifier(c, vector, ctx, &side_val[side_count], &side_color[side_count]);
		if (error == CSS_OK) {
			side_count++;

			consumeWhitespace(vector, ctx);

			token = parserutils_vector_peek(vector, *ctx);
		} else {
			/* Forcibly cause loop to exit */
			token = NULL;
		}
	} while ((*ctx != prev_ctx) && (token != NULL) && (side_count < 4));


#define SIDE_APPEND(OP,NUM)								\
	error = css__stylesheet_style_appendOPV(result, (OP), 0, side_val[(NUM)]);	\
	if (error != CSS_OK)								\
		break;									\
	if (side_val[(NUM)] == BORDER_COLOR_SET)					\
		error = css__stylesheet_style_append(result, side_color[(NUM)]);		\
	if (error != CSS_OK)								\
		break;

	switch (side_count) {
	case 1:
		SIDE_APPEND(CSS_PROP_BORDER_TOP_COLOR, 0);
		SIDE_APPEND(CSS_PROP_BORDER_RIGHT_COLOR, 0);
		SIDE_APPEND(CSS_PROP_BORDER_BOTTOM_COLOR, 0);
		SIDE_APPEND(CSS_PROP_BORDER_LEFT_COLOR, 0);
		break;
	case 2:
		SIDE_APPEND(CSS_PROP_BORDER_TOP_COLOR, 0);
		SIDE_APPEND(CSS_PROP_BORDER_RIGHT_COLOR, 1);
		SIDE_APPEND(CSS_PROP_BORDER_BOTTOM_COLOR, 0);
		SIDE_APPEND(CSS_PROP_BORDER_LEFT_COLOR, 1);
		break;
	case 3:
		SIDE_APPEND(CSS_PROP_BORDER_TOP_COLOR, 0);
		SIDE_APPEND(CSS_PROP_BORDER_RIGHT_COLOR, 1);
		SIDE_APPEND(CSS_PROP_BORDER_BOTTOM_COLOR, 2);
		SIDE_APPEND(CSS_PROP_BORDER_LEFT_COLOR, 1);
		break;
	case 4:
		SIDE_APPEND(CSS_PROP_BORDER_TOP_COLOR, 0);
		SIDE_APPEND(CSS_PROP_BORDER_RIGHT_COLOR, 1);
		SIDE_APPEND(CSS_PROP_BORDER_BOTTOM_COLOR, 2);
		SIDE_APPEND(CSS_PROP_BORDER_LEFT_COLOR, 3);
		break;
	default:
		error = CSS_INVALID;
		break;
	}

	if (error != CSS_OK)
		*ctx = orig_ctx;

	return error;
}
Example #13
0
/**
 * Parse border-spacing
 *
 * \param c	  Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx	  Pointer to vector iteration context
 * \param result  Pointer to location to receive resulting style
 * \return CSS_OK on success,
 *	   CSS_NOMEM on memory exhaustion,
 *	   CSS_INVALID if the input is not valid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *		   If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_border_spacing(css_language *c, 
		const parserutils_vector *vector, int *ctx, 
		css_style *result)
{
	int orig_ctx = *ctx;
	css_error error;
	const css_token *token;
	css_fixed length[2] = { 0 };
	uint32_t unit[2] = { 0 };
	bool match;

	/* length length? | IDENT(inherit) */
	token = parserutils_vector_peek(vector, *ctx);
	if (token == NULL) {
		*ctx = orig_ctx;
		return CSS_INVALID;
	}

	if (token->type == CSS_TOKEN_IDENT &&
			(lwc_string_caseless_isequal(
			token->idata, c->strings[INHERIT],
			&match) == lwc_error_ok && match)) {
		parserutils_vector_iterate(vector, ctx);
		/* inherit */
		error = css__stylesheet_style_appendOPV(result,
						       CSS_PROP_BORDER_SPACING,
						       FLAG_INHERIT,
						       0);
	} else {
		int num_lengths = 0;

		error = css__parse_unit_specifier(c, vector, ctx, UNIT_PX,
				&length[0], &unit[0]);
		if (error != CSS_OK) {
			*ctx = orig_ctx;
			return error;
		}

		if (unit[0] & UNIT_ANGLE || unit[0] & UNIT_TIME || 
				unit[0] & UNIT_FREQ || unit[0] & UNIT_PCT) {
			*ctx = orig_ctx;
			return CSS_INVALID;
		}

		num_lengths = 1;

		consumeWhitespace(vector, ctx);

		token = parserutils_vector_peek(vector, *ctx);
		if (token != NULL) {
			/* Attempt second length, ignoring errors.
			 * The core !important parser will ensure 
			 * any remaining junk is thrown out.
			 * Ctx will be preserved on error, as usual
			 */
			error = css__parse_unit_specifier(c, vector, ctx, UNIT_PX,
					&length[1], &unit[1]);
			if (error == CSS_OK) {
				if (unit[1] & UNIT_ANGLE || 
						unit[1] & UNIT_TIME ||
						unit[1] & UNIT_FREQ || 
						unit[1] & UNIT_PCT) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				num_lengths = 2;
			}
		}

		if (num_lengths == 1) {
			/* Only one length specified. Use for both axes. */
			length[1] = length[0];
			unit[1] = unit[0];
		}

		/* Lengths must not be negative */
		if (length[0] < 0 || length[1] < 0) {
			*ctx = orig_ctx;
			return CSS_INVALID;
		}

		error = css__stylesheet_style_appendOPV(result,
						       CSS_PROP_BORDER_SPACING,
						       0,
						       BORDER_SPACING_SET);

		if (error != CSS_OK) {
			*ctx = orig_ctx;
			return error;
		}

		error = css__stylesheet_style_vappend(result, 
						     4, 
						     length[0], 
						     unit[0], 
						     length[1], 
						     unit[1]);

	}

	if (error != CSS_OK) {
		*ctx = orig_ctx;
		return error;
	}

	return CSS_OK;
}
Example #14
0
/**
 * Parse text-decoration
 *
 * \param c       Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx     Pointer to vector iteration context
 * \param result  Pointer to location to receive resulting style
 * \return CSS_OK on success,
 *         CSS_NOMEM on memory exhaustion,
 *         CSS_INVALID if the input is not valid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *                 If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_text_decoration(css_language *c, 
		const parserutils_vector *vector, int *ctx, 
		css_style *result)
{
	int orig_ctx = *ctx;
	css_error error = CSS_INVALID;
	const css_token *token;
	bool match;

	/* IDENT([ underline || overline || line-through || blink ])
	 * | IDENT (none, inherit) */
	token = parserutils_vector_iterate(vector, ctx);
	if ((token == NULL) || (token->type != CSS_TOKEN_IDENT) ) {
		*ctx = orig_ctx;
		return CSS_INVALID;
	}

	if (lwc_string_caseless_isequal(token->idata,
			c->strings[INHERIT],
			&match) == lwc_error_ok && match) {
		error = css_stylesheet_style_inherit(result, CSS_PROP_TEXT_DECORATION);
	} else if (lwc_string_caseless_isequal(token->idata,
				c->strings[NONE],
				&match) == lwc_error_ok && match) {
		error = css__stylesheet_style_appendOPV(result,
				CSS_PROP_TEXT_DECORATION, 0, TEXT_DECORATION_NONE);
	} else {
		uint16_t value = 0;
		while (token != NULL) {
			if ((lwc_string_caseless_isequal(
					token->idata, c->strings[UNDERLINE],
					&match) == lwc_error_ok && match)) {
				if ((value & TEXT_DECORATION_UNDERLINE) == 0)
					value |= TEXT_DECORATION_UNDERLINE;
				else {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}
			} else if ((lwc_string_caseless_isequal(
					token->idata, c->strings[OVERLINE],
					&match) == lwc_error_ok && match)) {
				if ((value & TEXT_DECORATION_OVERLINE) == 0)
					value |= TEXT_DECORATION_OVERLINE;
				else {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}
			} else if ((lwc_string_caseless_isequal(
					token->idata, c->strings[LINE_THROUGH],
					&match) == lwc_error_ok && match)) {
				if ((value & TEXT_DECORATION_LINE_THROUGH) == 0)
					value |= TEXT_DECORATION_LINE_THROUGH;
				else {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}
			} else if ((lwc_string_caseless_isequal(
					token->idata, c->strings[BLINK],
					&match) == lwc_error_ok && match)) {
				if ((value & TEXT_DECORATION_BLINK) == 0)
					value |= TEXT_DECORATION_BLINK;
				else {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}
			} else {
				*ctx = orig_ctx;
				return CSS_INVALID;
			}

			consumeWhitespace(vector, ctx);

			token = parserutils_vector_peek(vector, *ctx);
			if (token != NULL && token->type != CSS_TOKEN_IDENT)
				break;
			token = parserutils_vector_iterate(vector, ctx);
		}
		error = css__stylesheet_style_appendOPV(result,
				CSS_PROP_TEXT_DECORATION, 0, value);
	}

	if (error != CSS_OK)
		*ctx = orig_ctx;

	return error;
}
Example #15
0
/**
 * Parse border-{top,right,bottom,left} shorthand
 *
 * \param c	  Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx	  Pointer to vector iteration context
 * \param side	  The side we're parsing for
 * \param result  Pointer to location to receive resulting style
 * \return CSS_OK on success,
 *	   CSS_NOMEM on memory exhaustion,
 *	   CSS_INVALID if the input is not valid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *		   If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_border_side(css_language *c,
		const parserutils_vector *vector, int *ctx,
		css_style *result, enum border_side_e side)
{
	int orig_ctx = *ctx;
	int prev_ctx;
	const css_token *token;
	css_error error = CSS_OK;
	bool color = true;
	bool style = true;
	bool width = true;
	css_style *color_style;
	css_style *style_style;
	css_style *width_style;

	/* Firstly, handle inherit */
	token = parserutils_vector_peek(vector, *ctx);
	if (token == NULL) 
		return CSS_INVALID;
		
	if (is_css_inherit(c, token)) {
		error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_COLOR + side);
		if (error != CSS_OK) 
			return error;

		error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_STYLE + side);
		if (error != CSS_OK) 
			return error;		

		error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_WIDTH + side);
		if (error == CSS_OK) 
			parserutils_vector_iterate(vector, ctx);

		return error;
	} 

	/* allocate styles */
	error = css__stylesheet_style_create(c->sheet, &color_style);
	if (error != CSS_OK) 
		return error;

	error = css__stylesheet_style_create(c->sheet, &style_style);
	if (error != CSS_OK) {
		css__stylesheet_style_destroy(color_style);
		return error;
	}

	error = css__stylesheet_style_create(c->sheet, &width_style);
	if (error != CSS_OK) {
		css__stylesheet_style_destroy(color_style);
		css__stylesheet_style_destroy(width_style);
		return error;
	}

	/* Attempt to parse the various longhand properties */
	do {
		prev_ctx = *ctx;
		error = CSS_OK;

		/* Ensure that we're not about to parse another inherit */
		token = parserutils_vector_peek(vector, *ctx);
		if (token != NULL && is_css_inherit(c, token)) {
			error = CSS_INVALID;
			goto css__parse_border_side_cleanup;
		}

		/* Try each property parser in turn, but only if we
		 * haven't already got a value for this property.
		 */
		if ((color) && 
		    (error = css__parse_border_side_color(c, vector, ctx, 
			     color_style, CSS_PROP_BORDER_TOP_COLOR + side)) == CSS_OK) {
			color = false;
		} else if ((style) && 
			   (error = css__parse_border_side_style(c, vector, ctx,
				    style_style, CSS_PROP_BORDER_TOP_STYLE + side)) == CSS_OK) {
			style = false;
		} else if ((width) && 
			   (error = css__parse_border_side_width(c, vector, ctx,
				    width_style, CSS_PROP_BORDER_TOP_WIDTH + side)) == CSS_OK) {
			width = false;
		} 

		if (error == CSS_OK) {
			consumeWhitespace(vector, ctx);

			token = parserutils_vector_peek(vector, *ctx);
		} else {
			/* Forcibly cause loop to exit */
			token = NULL;
		}
	} while (*ctx != prev_ctx && token != NULL);

	if (style) {
		error = css__stylesheet_style_appendOPV(style_style, 
				CSS_PROP_BORDER_TOP_STYLE + side, 0, 
				BORDER_STYLE_NONE);
		if (error != CSS_OK)
			goto css__parse_border_side_cleanup;
	}

	if (width) {
		error = css__stylesheet_style_appendOPV(width_style, 
				CSS_PROP_BORDER_TOP_WIDTH + side,
				0, BORDER_WIDTH_MEDIUM);
		if (error != CSS_OK)
			goto css__parse_border_side_cleanup;
	}

	error = css__stylesheet_merge_style(result, color_style);
	if (error != CSS_OK)
		goto css__parse_border_side_cleanup;

	error = css__stylesheet_merge_style(result, style_style);
	if (error != CSS_OK)
		goto css__parse_border_side_cleanup;

	error = css__stylesheet_merge_style(result, width_style);

css__parse_border_side_cleanup:
	css__stylesheet_style_destroy(color_style);
	css__stylesheet_style_destroy(style_style);
	css__stylesheet_style_destroy(width_style);

	if (error != CSS_OK)
		*ctx = orig_ctx;

	return error;
}
Example #16
0
/**
 * Parse a colour specifier
 *
 * \param c       Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx     Pointer to vector iteration context
 * \param value   Pointer to location to receive value
 * \param result  Pointer to location to receive result (AARRGGBB)
 * \return CSS_OK      on success,
 *         CSS_INVALID if the input is invalid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *                 If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_colour_specifier(css_language *c,
		const parserutils_vector *vector, int *ctx,
		uint16_t *value, uint32_t *result)
{
	int orig_ctx = *ctx;
	const css_token *token;
	bool match;
	css_error error;

	consumeWhitespace(vector, ctx);

	/* IDENT(<colour name>) | 
	 * HASH(rgb | rrggbb) |
	 * FUNCTION(rgb) [ [ NUMBER | PERCENTAGE ] ',' ] {3} ')'
	 * FUNCTION(rgba) [ [ NUMBER | PERCENTAGE ] ',' ] {4} ')'
	 * FUNCTION(hsl) ANGLE ',' PERCENTAGE ',' PERCENTAGE  ')'
	 * FUNCTION(hsla) ANGLE ',' PERCENTAGE ',' PERCENTAGE ',' NUMBER ')'
	 *
	 * For quirks, NUMBER | DIMENSION | IDENT, too
	 * I.E. "123456" -> NUMBER, "1234f0" -> DIMENSION, "f00000" -> IDENT
	 */
	token = parserutils_vector_iterate(vector, ctx);
	if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
			token->type != CSS_TOKEN_HASH &&
			token->type != CSS_TOKEN_FUNCTION)) {
		if (c->sheet->quirks_allowed == false ||
				token == NULL ||
				(token->type != CSS_TOKEN_NUMBER &&
				token->type != CSS_TOKEN_DIMENSION))
			goto invalid;
	}

	if (token->type == CSS_TOKEN_IDENT) {
		if ((lwc_string_caseless_isequal(
				token->idata, c->strings[TRANSPARENT],
				&match) == lwc_error_ok && match)) {
			*value = COLOR_TRANSPARENT;
			*result = 0; /* black transparent */
			return CSS_OK;
		} else if ((lwc_string_caseless_isequal(
				token->idata, c->strings[CURRENTCOLOR],
				&match) == lwc_error_ok && match)) {
			*value = COLOR_CURRENT_COLOR;
			*result = 0;
			return CSS_OK;
		}

		error = css__parse_named_colour(c, token->idata, result);
		if (error != CSS_OK && c->sheet->quirks_allowed) {
			error = css__parse_hash_colour(token->idata, result);
			if (error == CSS_OK)
				c->sheet->quirks_used = true;
		}

		if (error != CSS_OK)
			goto invalid;
	} else if (token->type == CSS_TOKEN_HASH) {
		error = css__parse_hash_colour(token->idata, result);
		if (error != CSS_OK)
			goto invalid;
	} else if (c->sheet->quirks_allowed &&
			token->type == CSS_TOKEN_NUMBER) {
		error = css__parse_hash_colour(token->idata, result);
		if (error == CSS_OK)
			c->sheet->quirks_used = true;
		else
			goto invalid;
	} else if (c->sheet->quirks_allowed &&
			token->type == CSS_TOKEN_DIMENSION) {
		error = css__parse_hash_colour(token->idata, result);
		if (error == CSS_OK)
			c->sheet->quirks_used = true;
		else
			goto invalid;
	} else if (token->type == CSS_TOKEN_FUNCTION) {
		uint8_t r = 0, g = 0, b = 0, a = 0xff;
		int colour_channels = 0;

		if ((lwc_string_caseless_isequal(
				token->idata, c->strings[RGB],
				&match) == lwc_error_ok && match)) {
			colour_channels = 3;
		} else if ((lwc_string_caseless_isequal(
				token->idata, c->strings[RGBA],
				&match) == lwc_error_ok && match)) {
			colour_channels = 4;
		} if ((lwc_string_caseless_isequal(
				token->idata, c->strings[HSL],
				&match) == lwc_error_ok && match)) {
			colour_channels = 5;
		} else if ((lwc_string_caseless_isequal(
				token->idata, c->strings[HSLA],
				&match) == lwc_error_ok && match)) {
			colour_channels = 6;
		}

		if (colour_channels == 3 || colour_channels == 4) {
			int i;
			css_token_type valid = CSS_TOKEN_NUMBER;
			uint8_t *components[4] = { &r, &g, &b, &a };

			for (i = 0; i < colour_channels; i++) {
				uint8_t *component;
				css_fixed num;
				size_t consumed = 0;
				int32_t intval;
				bool int_only;

				component = components[i];

				consumeWhitespace(vector, ctx);

				token = parserutils_vector_peek(vector, *ctx);
				if (token == NULL || (token->type !=
						CSS_TOKEN_NUMBER &&
						token->type !=
						CSS_TOKEN_PERCENTAGE))
					goto invalid;

				if (i == 0)
					valid = token->type;
				else if (i < 3 && token->type != valid)
					goto invalid;

				/* The alpha channel may be a float */
				if (i < 3)
					int_only = (valid == CSS_TOKEN_NUMBER);
				else
					int_only = false;

				num = css__number_from_lwc_string(token->idata,
						int_only, &consumed);
				if (consumed != lwc_string_length(token->idata))
					goto invalid;

				if (valid == CSS_TOKEN_NUMBER) {
					if (i == 3) {
						/* alpha channel */
						intval = FIXTOINT(
							FMUL(num, F_255));
					} else {
						/* colour channels */
						intval = FIXTOINT(num);
					}
				} else {
					intval = FIXTOINT(
						FDIV(FMUL(num, F_255), F_100));
				}

				if (intval > 255)
					*component = 255;
				else if (intval < 0)
					*component = 0;
				else
					*component = intval;

				parserutils_vector_iterate(vector, ctx);

				consumeWhitespace(vector, ctx);

				token = parserutils_vector_peek(vector, *ctx);
				if (token == NULL)
					goto invalid;

				if (i != (colour_channels - 1) &&
						tokenIsChar(token, ',')) {
					parserutils_vector_iterate(vector, ctx);
				} else if (i == (colour_channels - 1) &&
						tokenIsChar(token, ')')) {
					parserutils_vector_iterate(vector, ctx);
				} else {
					goto invalid;
				}
			}
		} else if (colour_channels == 5 || colour_channels == 6) {
			/* hue - saturation - lightness */
			size_t consumed = 0;
			css_fixed hue, sat, lit;
			int32_t alpha = 255;

			/* hue is a number without a unit representing an 
			 * angle (0-360) degrees  
			 */
			consumeWhitespace(vector, ctx);

			token = parserutils_vector_iterate(vector, ctx);
			if ((token == NULL) || (token->type != CSS_TOKEN_NUMBER))
				goto invalid;

			hue = css__number_from_lwc_string(token->idata, false, &consumed);
			if (consumed != lwc_string_length(token->idata))
				goto invalid; /* failed to consume the whole string as a number */

			/* Normalise hue to the range [0, 360) */
			while (hue < 0)
				hue += F_360;
			while (hue >= F_360)
				hue -= F_360;

			consumeWhitespace(vector, ctx);

			token = parserutils_vector_iterate(vector, ctx);
			if (!tokenIsChar(token, ','))
				goto invalid;


			/* saturation */
			consumeWhitespace(vector, ctx);

			token = parserutils_vector_iterate(vector, ctx);
			if ((token == NULL) || (token->type != CSS_TOKEN_PERCENTAGE))
				goto invalid;

			sat = css__number_from_lwc_string(token->idata, false, &consumed);
			if (consumed != lwc_string_length(token->idata))
				goto invalid; /* failed to consume the whole string as a number */

			/* Normalise saturation to the range [0, 100] */
			if (sat < INTTOFIX(0))
				sat = INTTOFIX(0);
			else if (sat > INTTOFIX(100))
				sat = INTTOFIX(100);

			consumeWhitespace(vector, ctx);

			token = parserutils_vector_iterate(vector, ctx);
			if (!tokenIsChar(token, ','))
				goto invalid;


			/* lightness */
			consumeWhitespace(vector, ctx);

			token = parserutils_vector_iterate(vector, ctx);
			if ((token == NULL) || (token->type != CSS_TOKEN_PERCENTAGE))
				goto invalid;

			lit = css__number_from_lwc_string(token->idata, false, &consumed);
			if (consumed != lwc_string_length(token->idata))
				goto invalid; /* failed to consume the whole string as a number */

			/* Normalise lightness to the range [0, 100] */
			if (lit < INTTOFIX(0))
				lit = INTTOFIX(0);
			else if (lit > INTTOFIX(100))
				lit = INTTOFIX(100);

			consumeWhitespace(vector, ctx);

			token = parserutils_vector_iterate(vector, ctx);

			if (colour_channels == 6) {
				/* alpha */

				if (!tokenIsChar(token, ','))
					goto invalid;
			
				consumeWhitespace(vector, ctx);

				token = parserutils_vector_iterate(vector, ctx);
				if ((token == NULL) || (token->type != CSS_TOKEN_NUMBER))
					goto invalid;

				alpha = css__number_from_lwc_string(token->idata, false, &consumed);
				if (consumed != lwc_string_length(token->idata))
					goto invalid; /* failed to consume the whole string as a number */
				
				alpha = FIXTOINT(FMUL(alpha, F_255));

				consumeWhitespace(vector, ctx);

				token = parserutils_vector_iterate(vector, ctx);

			}

			if (!tokenIsChar(token, ')'))
				goto invalid;

			/* have a valid HSV entry, convert to RGB */
			HSL_to_RGB(hue, sat, lit, &r, &g, &b);

			/* apply alpha */
			if (alpha > 255)
				a = 255;
			else if (alpha < 0)
				a = 0;
			else
				a = alpha;

		} else {
			goto invalid;
		}

		*result = (a << 24) | (r << 16) | (g << 8) | b;
	}

	*value = COLOR_SET;

	return CSS_OK;

invalid:
	*ctx = orig_ctx;
	return CSS_INVALID;
}
Example #17
0
/**
 * Parse clip
 *
 * \param c       Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx     Pointer to vector iteration context
 * \param result  Pointer to location to receive resulting style
 * \return CSS_OK on success,
 *         CSS_NOMEM on memory exhaustion,
 *         CSS_INVALID if the input is not valid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *                 If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_clip(css_language *c, 
		const parserutils_vector *vector, int *ctx, 
		css_style *result)
{
	int orig_ctx = *ctx;
	css_error error;
	const css_token *token;
	int num_lengths = 0;
	css_fixed length[4] = { 0 };
	uint32_t unit[4] = { 0 };
	bool match;

	/* FUNCTION(rect) [ [ IDENT(auto) | length ] CHAR(,)? ]{3} 
	 *                [ IDENT(auto) | length ] CHAR{)} |
	 * IDENT(auto, inherit) */
	token = parserutils_vector_iterate(vector, ctx);
	if (token == NULL) {
		*ctx = orig_ctx;
		return CSS_INVALID;
	}

	if ((token->type == CSS_TOKEN_IDENT) &&
	    (lwc_string_caseless_isequal(
		    token->idata, c->strings[INHERIT],
		    &match) == lwc_error_ok && match)) {
		error = css__stylesheet_style_appendOPV(result,
						       CSS_PROP_CLIP,
						       FLAG_INHERIT,
						       0);
	} else if ((token->type == CSS_TOKEN_IDENT) &&
		   (lwc_string_caseless_isequal(
			   token->idata, c->strings[AUTO],
			   &match) == lwc_error_ok && match)) {
		error = css__stylesheet_style_appendOPV(result,
						       CSS_PROP_CLIP,
						       0,
						       CLIP_AUTO);
	} else if ((token->type == CSS_TOKEN_FUNCTION) &&
		   (lwc_string_caseless_isequal(
			   token->idata, c->strings[RECT],
			   &match) == lwc_error_ok && match)) {
		int i;
		uint16_t value = CLIP_SHAPE_RECT;

		for (i = 0; i < 4; i++) {
			consumeWhitespace(vector, ctx);

			token = parserutils_vector_peek(vector, *ctx);
			if (token == NULL) {
				*ctx = orig_ctx;
				return CSS_INVALID;
			}

			if (token->type == CSS_TOKEN_IDENT) {
				/* Slightly magical way of generating the auto 
				 * values. These are bits 3-6 of the value. */
				if ((lwc_string_caseless_isequal(
						token->idata, c->strings[AUTO],
						&match) == lwc_error_ok && 
						match))
					value |= 1 << (i + 3);
				else {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				parserutils_vector_iterate(vector, ctx);
			} else {
				error = css__parse_unit_specifier(c, vector, ctx, 
						UNIT_PX, 
						&length[num_lengths], 
						&unit[num_lengths]);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				if (unit[num_lengths] & UNIT_ANGLE || 
						unit[num_lengths] & UNIT_TIME ||
						unit[num_lengths] & UNIT_FREQ ||
						unit[num_lengths] & UNIT_PCT) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				num_lengths++;
			}

			consumeWhitespace(vector, ctx);

			/* Consume optional comma after first 3 parameters */
			if (i < 3) {
				token = parserutils_vector_peek(vector, *ctx);
				if (token == NULL) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				if (tokenIsChar(token, ','))
					parserutils_vector_iterate(vector, ctx);
			}
		}

		consumeWhitespace(vector, ctx);

		/* Finally, consume closing parenthesis */
		token = parserutils_vector_iterate(vector, ctx);
		if (token == NULL || tokenIsChar(token, ')') == false) {
			*ctx = orig_ctx;
			return CSS_INVALID;
		}

                /* output bytecode */
		error = css__stylesheet_style_appendOPV(result,
						       CSS_PROP_CLIP,
						       0,
						       value);
		if (error != CSS_OK) {
			*ctx = orig_ctx;
			return error;
		}

		for (i = 0; i < num_lengths; i++) {
			error = css__stylesheet_style_vappend(result, 
							     2, 
							     length[i], 
							     unit[i]);
			if (error != CSS_OK) 
				break;
		}


	} else {
		error = CSS_INVALID;
	}

	if (error != CSS_OK) {
		*ctx = orig_ctx;
	}

	return error;
}
Example #18
0
/**
 * Parse content
 *
 * \param c	  Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx	  Pointer to vector iteration context
 * \param result  Pointer to location to receive resulting style
 * \return CSS_OK on success,
 *	   CSS_NOMEM on memory exhaustion,
 *	   CSS_INVALID if the input is not valid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *		   If the input is invalid, then \a *ctx remains unchanged.
 */
css_error css__parse_content(css_language *c,
		const parserutils_vector *vector, int *ctx,
		css_style *result)
{
	int orig_ctx = *ctx;
	css_error error;
	const css_token *token;
	bool match;

	/* IDENT(normal, none, inherit) | [ ... ]+ */
	token = parserutils_vector_iterate(vector, ctx);
	if (token == NULL) {
		*ctx = orig_ctx;
		return CSS_INVALID;
	}


	if ((token->type == CSS_TOKEN_IDENT) &&
	    (lwc_string_caseless_isequal(token->idata,
					 c->strings[INHERIT],
					 &match) == lwc_error_ok && match)) {
		error = css_stylesheet_style_inherit(result, CSS_PROP_CONTENT);
	} else if ((token->type == CSS_TOKEN_IDENT) &&
		   (lwc_string_caseless_isequal(token->idata,
						c->strings[NORMAL],
						&match) == lwc_error_ok && match)) {
		error = css__stylesheet_style_appendOPV(result, CSS_PROP_CONTENT, 0, CONTENT_NORMAL);
	} else if ((token->type == CSS_TOKEN_IDENT) &&
		   (lwc_string_caseless_isequal(token->idata,
						c->strings[NONE],
						&match) == lwc_error_ok && match)) {
		error = css__stylesheet_style_appendOPV(result, CSS_PROP_CONTENT, 0, CONTENT_NONE);
	} else {

/* Macro to output the value marker, awkward because we need to check
 * first to determine how the value is constructed.
 */
#define CSS_APPEND(CSSVAL) css__stylesheet_style_append(result, first?buildOPV(CSS_PROP_CONTENT, 0, CSSVAL):CSSVAL)

		bool first = true;
		int prev_ctx = orig_ctx;

		/* [
		 *   IDENT(open-quote, close-quote, no-open-quote,
		 *         no-close-quote) |
		 *   STRING |
		 *   URI |
		 *   FUNCTION(attr) IDENT ')' |
		 *   FUNCTION(counter) IDENT IDENT? ')' |
		 *   FUNCTION(counters) IDENT STRING IDENT? ')'
		 * ]+
		 */

		while (token != NULL) {
			if ((token->type == CSS_TOKEN_IDENT) &&
			    (lwc_string_caseless_isequal(
				    token->idata, c->strings[OPEN_QUOTE],
				    &match) == lwc_error_ok && match)) {

				error = CSS_APPEND(CONTENT_OPEN_QUOTE);

			} else if (token->type == CSS_TOKEN_IDENT &&
				   (lwc_string_caseless_isequal(
					   token->idata, c->strings[CLOSE_QUOTE],
					   &match) == lwc_error_ok && match)) {

				error = CSS_APPEND(CONTENT_CLOSE_QUOTE);
			} else if (token->type == CSS_TOKEN_IDENT &&
				   (lwc_string_caseless_isequal(
					   token->idata, c->strings[NO_OPEN_QUOTE],
					   &match) == lwc_error_ok && match)) {
				error = CSS_APPEND(CONTENT_NO_OPEN_QUOTE);
			} else if (token->type == CSS_TOKEN_IDENT &&
				   (lwc_string_caseless_isequal(
					   token->idata, c->strings[NO_CLOSE_QUOTE],
					   &match) == lwc_error_ok && match)) {
				error = CSS_APPEND(CONTENT_NO_CLOSE_QUOTE);
			} else if (token->type == CSS_TOKEN_STRING) {
				uint32_t snumber;

				error = css__stylesheet_string_add(c->sheet, lwc_string_ref(token->idata), &snumber);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = CSS_APPEND(CONTENT_STRING);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = css__stylesheet_style_append(result, snumber);
			} else if (token->type == CSS_TOKEN_URI) {
				lwc_string *uri;
				uint32_t uri_snumber;

				error = c->sheet->resolve(c->sheet->resolve_pw,
							  c->sheet->url,
							  token->idata, 
							  &uri);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = css__stylesheet_string_add(c->sheet, 
								  uri, 
								  &uri_snumber);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = CSS_APPEND(CONTENT_URI);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = css__stylesheet_style_append(result, uri_snumber);
			} else if (token->type == CSS_TOKEN_FUNCTION &&
				   (lwc_string_caseless_isequal(
					   token->idata, c->strings[ATTR],
					   &match) == lwc_error_ok && match)) {
				uint32_t snumber;

				consumeWhitespace(vector, ctx);

				/* Expect IDENT */
				token = parserutils_vector_iterate(vector, ctx);
				if (token == NULL || token->type != CSS_TOKEN_IDENT) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				error = css__stylesheet_string_add(c->sheet, lwc_string_ref(token->idata), &snumber);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = CSS_APPEND(CONTENT_ATTR);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = css__stylesheet_style_append(result, snumber);

				consumeWhitespace(vector, ctx);

				/* Expect ')' */
				token = parserutils_vector_iterate(vector, ctx);
				if (token == NULL || tokenIsChar(token, ')') == false) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}
			} else if (token->type == CSS_TOKEN_FUNCTION &&
				   (lwc_string_caseless_isequal(
					   token->idata, c->strings[COUNTER],
					   &match) == lwc_error_ok && match)) {
				lwc_string *name;
				uint32_t snumber;
				uint32_t opv;

				opv = CONTENT_COUNTER;

				consumeWhitespace(vector, ctx);

				/* Expect IDENT */
				token = parserutils_vector_iterate(vector, ctx);
				if (token == NULL || token->type != CSS_TOKEN_IDENT) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				name = token->idata;

				consumeWhitespace(vector, ctx);

				/* Possible ',' */
				token = parserutils_vector_peek(vector, *ctx);
				if (token == NULL ||
				    (tokenIsChar(token, ',') == false &&
				     tokenIsChar(token, ')') == false)) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				if (tokenIsChar(token, ',')) {
					uint16_t v;

					parserutils_vector_iterate(vector, ctx);

					consumeWhitespace(vector, ctx);

					/* Expect IDENT */
					token = parserutils_vector_peek(vector, *ctx);
					if (token == NULL || token->type !=
					    CSS_TOKEN_IDENT) {
						*ctx = orig_ctx;
						return CSS_INVALID;
					}

					error = css__parse_list_style_type_value(c, token, &v);
					if (error != CSS_OK) {
						*ctx = orig_ctx;
						return error;
					}

					opv |= v << CONTENT_COUNTER_STYLE_SHIFT;

					parserutils_vector_iterate(vector, ctx);

					consumeWhitespace(vector, ctx);
				} else {
					opv |= LIST_STYLE_TYPE_DECIMAL <<
						CONTENT_COUNTER_STYLE_SHIFT;
				}

				/* Expect ')' */
				token = parserutils_vector_iterate(vector, ctx);
				if (token == NULL || tokenIsChar(token,	')') == false) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}


				error = css__stylesheet_string_add(c->sheet, lwc_string_ref(name), &snumber);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = CSS_APPEND(opv);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = css__stylesheet_style_append(result, snumber);
			} else if (token->type == CSS_TOKEN_FUNCTION &&
				   (lwc_string_caseless_isequal(
					   token->idata, c->strings[COUNTERS],
					   &match) == lwc_error_ok && match)) {
				lwc_string *name;
				lwc_string *sep;
				uint32_t name_snumber;
				uint32_t sep_snumber;
				uint32_t opv;

				opv = CONTENT_COUNTERS;

				consumeWhitespace(vector, ctx);

				/* Expect IDENT */
				token = parserutils_vector_iterate(vector, ctx);
				if (token == NULL || token->type != CSS_TOKEN_IDENT) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				name = token->idata;

				consumeWhitespace(vector, ctx);

				/* Expect ',' */
				token = parserutils_vector_iterate(vector, ctx);
				if (token == NULL || tokenIsChar(token, ',') == false) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				consumeWhitespace(vector, ctx);

				/* Expect STRING */
				token = parserutils_vector_iterate(vector, ctx);
				if (token == NULL || token->type != CSS_TOKEN_STRING) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				sep = token->idata;

				consumeWhitespace(vector, ctx);

				/* Possible ',' */
				token = parserutils_vector_peek(vector, *ctx);
				if (token == NULL ||
				    (tokenIsChar(token, ',') == false &&
				     tokenIsChar(token, ')') == false)) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				if (tokenIsChar(token, ',')) {
					uint16_t v;

					parserutils_vector_iterate(vector, ctx);

					consumeWhitespace(vector, ctx);

					/* Expect IDENT */
					token = parserutils_vector_peek(vector, *ctx);
					if (token == NULL || token->type !=
					    CSS_TOKEN_IDENT) {
						*ctx = orig_ctx;
						return CSS_INVALID;
					}

					error = css__parse_list_style_type_value(c,
									    token, &v);
					if (error != CSS_OK) {
						*ctx = orig_ctx;
						return error;
					}

					opv |= v << CONTENT_COUNTERS_STYLE_SHIFT;

					parserutils_vector_iterate(vector, ctx);

					consumeWhitespace(vector, ctx);
				} else {
					opv |= LIST_STYLE_TYPE_DECIMAL <<
						CONTENT_COUNTERS_STYLE_SHIFT;
				}

				/* Expect ')' */
				token = parserutils_vector_iterate(vector, ctx);
				if (token == NULL || tokenIsChar(token, ')') == false) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}


				error = css__stylesheet_string_add(c->sheet, lwc_string_ref(name), &name_snumber);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = css__stylesheet_string_add(c->sheet, lwc_string_ref(sep), &sep_snumber);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = CSS_APPEND(opv);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = css__stylesheet_style_append(result, name_snumber);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				error = css__stylesheet_style_append(result, sep_snumber);
			} else if (first) {
				/* Invalid if this is the first iteration */
				error = CSS_INVALID;
			} else {
				/* Give up, ensuring current token is reprocessed */
				*ctx = prev_ctx;
				error = CSS_OK;
				break;
			}

			/* if there was an error bail */
			if (error != CSS_OK) {
				*ctx = orig_ctx;
				return error;
			}

			first = false;

			consumeWhitespace(vector, ctx);

			prev_ctx = *ctx;
			token = parserutils_vector_iterate(vector, ctx);
		} /* while */

		/* Write list terminator */
		css__stylesheet_style_append(result, CONTENT_NORMAL);
	}

	if (error != CSS_OK)
		*ctx = orig_ctx;

	return error;
}
Example #19
0
/**
 * Parse clip
 *
 * \param c       Parsing context
 * \param vector  Vector of tokens to process
 * \param ctx     Pointer to vector iteration context
 * \param result  Pointer to location to receive resulting style
 * \return CSS_OK on success,
 *         CSS_NOMEM on memory exhaustion,
 *         CSS_INVALID if the input is not valid
 *
 * Post condition: \a *ctx is updated with the next token to process
 *                 If the input is invalid, then \a *ctx remains unchanged.
 */
css_error parse_clip(css_language *c, 
		const parserutils_vector *vector, int *ctx, 
		css_style **result)
{
	int orig_ctx = *ctx;
	css_error error;
	const css_token *token;
	uint8_t flags = 0;
	uint16_t value = 0;
	uint32_t opv;
	int num_lengths = 0;
	css_fixed length[4] = { 0 };
	uint32_t unit[4] = { 0 };
	uint32_t required_size;
	bool match;

	/* FUNCTION(rect) [ [ IDENT(auto) | length ] CHAR(,)? ]{3} 
	 *                [ IDENT(auto) | length ] CHAR{)} |
	 * IDENT(auto, inherit) */
	token = parserutils_vector_iterate(vector, ctx);
	if (token == NULL) {
		*ctx = orig_ctx;
		return CSS_INVALID;
	}

	if (token->type == CSS_TOKEN_IDENT &&
			(lwc_string_caseless_isequal(
			token->idata, c->strings[INHERIT],
			&match) == lwc_error_ok && match)) {
		flags = FLAG_INHERIT;
	} else if (token->type == CSS_TOKEN_IDENT &&
			(lwc_string_caseless_isequal(
			token->idata, c->strings[AUTO],
			&match) == lwc_error_ok && match)) {
		value = CLIP_AUTO;
	} else if (token->type == CSS_TOKEN_FUNCTION &&
			(lwc_string_caseless_isequal(
			token->idata, c->strings[RECT],
			&match) == lwc_error_ok && match)) {
		int i;
		value = CLIP_SHAPE_RECT;

		for (i = 0; i < 4; i++) {
			consumeWhitespace(vector, ctx);

			token = parserutils_vector_peek(vector, *ctx);
			if (token == NULL) {
				*ctx = orig_ctx;
				return CSS_INVALID;
			}

			if (token->type == CSS_TOKEN_IDENT) {
				/* Slightly magical way of generating the auto 
				 * values. These are bits 3-6 of the value. */
				if ((lwc_string_caseless_isequal(
						token->idata, c->strings[AUTO],
						&match) == lwc_error_ok && 
						match))
					value |= 1 << (i + 3);
				else {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				parserutils_vector_iterate(vector, ctx);
			} else {
				error = parse_unit_specifier(c, vector, ctx, 
						UNIT_PX, 
						&length[num_lengths], 
						&unit[num_lengths]);
				if (error != CSS_OK) {
					*ctx = orig_ctx;
					return error;
				}

				if (unit[num_lengths] & UNIT_ANGLE || 
						unit[num_lengths] & UNIT_TIME ||
						unit[num_lengths] & UNIT_FREQ ||
						unit[num_lengths] & UNIT_PCT) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				num_lengths++;
			}

			consumeWhitespace(vector, ctx);

			/* Consume optional comma after first 3 parameters */
			if (i < 3) {
				token = parserutils_vector_peek(vector, *ctx);
				if (token == NULL) {
					*ctx = orig_ctx;
					return CSS_INVALID;
				}

				if (tokenIsChar(token, ','))
					parserutils_vector_iterate(vector, ctx);
			}
		}

		consumeWhitespace(vector, ctx);

		/* Finally, consume closing parenthesis */
		token = parserutils_vector_iterate(vector, ctx);
		if (token == NULL || tokenIsChar(token, ')') == false) {
			*ctx = orig_ctx;
			return CSS_INVALID;
		}
	} else {
		*ctx = orig_ctx;
		return CSS_INVALID;
	}

	opv = buildOPV(CSS_PROP_CLIP, flags, value);

	required_size = sizeof(opv);
	if ((flags & FLAG_INHERIT) == false && 
			(value & CLIP_SHAPE_MASK) == CLIP_SHAPE_RECT) {
		required_size += 
			num_lengths * (sizeof(length[0]) + sizeof(unit[0]));
	}

	/* Allocate result */
	error = css_stylesheet_style_create(c->sheet, required_size, result);
	if (error != CSS_OK) {
		*ctx = orig_ctx;
		return error;
	}

	/* Copy the bytecode to it */
	memcpy((*result)->bytecode, &opv, sizeof(opv));
	if ((flags & FLAG_INHERIT) == false && 
			(value & CLIP_SHAPE_MASK) == CLIP_SHAPE_RECT) {
		int i;
		uint8_t *ptr = ((uint8_t *) (*result)->bytecode) + sizeof(opv);

		for (i = 0; i < num_lengths; i++) {
			memcpy(ptr, &length[i], sizeof(length[i]));
			ptr += sizeof(length[i]);
			memcpy(ptr, &unit[i], sizeof(unit[i]));
			ptr += sizeof(unit[i]);
		}
	}

	return CSS_OK;
}