/** * 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); }
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); }
/** * 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; }
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; }
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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }