/* * This file is part of LibCSS. * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2009 John-Mark Bell */ #include #include "bytecode/bytecode.h" #include "bytecode/opcodes.h" #include "parse/properties/properties.h" #include "parse/properties/utils.h" static css_error parse_cue_common(css_language *c, const parserutils_vector *vector, int *ctx, uint16_t op, css_style **result); /** * Parse azimuth * * \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_azimuth(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; css_fixed length = 0; uint32_t unit = 0; uint32_t required_size; /* angle | [ IDENT(left-side, far-left, left, center-left, center, * center-right, right, far-right, right-side) || * IDENT(behind) * ] * | IDENT(leftwards, rightwards, inherit) */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[INHERIT]) { parserutils_vector_iterate(vector, ctx); flags = FLAG_INHERIT; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[LEFTWARDS]) { parserutils_vector_iterate(vector, ctx); value = AZIMUTH_LEFTWARDS; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[RIGHTWARDS]) { parserutils_vector_iterate(vector, ctx); value = AZIMUTH_RIGHTWARDS; } else if (token->type == CSS_TOKEN_IDENT) { parserutils_vector_iterate(vector, ctx); /* Now, we may have one of the other keywords or behind, * potentially followed by behind or other keyword, * respectively */ if (token->ilower == c->strings[LEFT_SIDE]) { value = AZIMUTH_LEFT_SIDE; } else if (token->ilower == c->strings[FAR_LEFT]) { value = AZIMUTH_FAR_LEFT; } else if (token->ilower == c->strings[LEFT]) { value = AZIMUTH_LEFT; } else if (token->ilower == c->strings[CENTER_LEFT]) { value = AZIMUTH_CENTER_LEFT; } else if (token->ilower == c->strings[CENTER]) { value = AZIMUTH_CENTER; } else if (token->ilower == c->strings[CENTER_RIGHT]) { value = AZIMUTH_CENTER_RIGHT; } else if (token->ilower == c->strings[RIGHT]) { value = AZIMUTH_RIGHT; } else if (token->ilower == c->strings[FAR_RIGHT]) { value = AZIMUTH_FAR_RIGHT; } else if (token->ilower == c->strings[RIGHT_SIDE]) { value = AZIMUTH_RIGHT_SIDE; } else if (token->ilower == c->strings[BEHIND]) { value = AZIMUTH_BEHIND; } else { *ctx = orig_ctx; return CSS_INVALID; } consumeWhitespace(vector, ctx); /* Get potential following token */ token = parserutils_vector_peek(vector, *ctx); if (token != NULL && token->type == CSS_TOKEN_IDENT && value == AZIMUTH_BEHIND) { parserutils_vector_iterate(vector, ctx); if (token->ilower == c->strings[LEFT_SIDE]) { value |= AZIMUTH_LEFT_SIDE; } else if (token->ilower == c->strings[FAR_LEFT]) { value |= AZIMUTH_FAR_LEFT; } else if (token->ilower == c->strings[LEFT]) { value |= AZIMUTH_LEFT; } else if (token->ilower == c->strings[CENTER-LEFT]) { value |= AZIMUTH_CENTER_LEFT; } else if (token->ilower == c->strings[CENTER]) { value |= AZIMUTH_CENTER; } else if (token->ilower == c->strings[CENTER-RIGHT]) { value |= AZIMUTH_CENTER_RIGHT; } else if (token->ilower == c->strings[RIGHT]) { value |= AZIMUTH_RIGHT; } else if (token->ilower == c->strings[FAR_RIGHT]) { value |= AZIMUTH_FAR_RIGHT; } else if (token->ilower == c->strings[RIGHT_SIDE]) { value |= AZIMUTH_RIGHT_SIDE; } else { *ctx = orig_ctx; return CSS_INVALID; } } else if (token != NULL && token->type == CSS_TOKEN_IDENT && value != AZIMUTH_BEHIND) { parserutils_vector_iterate(vector, ctx); if (token->ilower == c->strings[BEHIND]) { value |= AZIMUTH_BEHIND; } else { *ctx = orig_ctx; return CSS_INVALID; } } else if ((token == NULL || token->type != CSS_TOKEN_IDENT) && value == AZIMUTH_BEHIND) { value |= AZIMUTH_CENTER; } } else { error = parse_unit_specifier(c, vector, ctx, UNIT_DEG, &length, &unit); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if ((unit & UNIT_ANGLE) == false) { *ctx = orig_ctx; return CSS_INVALID; } /* Valid angles lie between -360 and 360 degrees */ if (unit == UNIT_DEG) { if (length < FMULI(F_360, -1) || length > F_360) { *ctx = orig_ctx; return CSS_INVALID; } } else if (unit == UNIT_GRAD) { if (length < FMULI(F_400, -1) || length > F_400) { *ctx = orig_ctx; return CSS_INVALID; } } else if (unit == UNIT_RAD) { if (length < FMULI(F_2PI, -1) || length > F_2PI) { *ctx = orig_ctx; return CSS_INVALID; } } value = AZIMUTH_ANGLE; } opv = buildOPV(CSS_PROP_AZIMUTH, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == AZIMUTH_ANGLE) required_size += sizeof(length) + sizeof(unit); /* 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 == AZIMUTH_ANGLE) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &length, sizeof(length)); memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv) + sizeof(length), &unit, sizeof(unit)); } return CSS_OK; } /** * Parse cue-after * * \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_cue_after(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { return parse_cue_common(c, vector, ctx, CSS_PROP_CUE_AFTER, result); } /** * Parse cue-before * * \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_cue_before(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { return parse_cue_common(c, vector, ctx, CSS_PROP_CUE_BEFORE, result); } /** * Parse elevation * * \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_elevation(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; css_fixed length = 0; uint32_t unit = 0; uint32_t required_size; /* angle | IDENT(below, level, above, higher, lower, inherit) */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[INHERIT]) { parserutils_vector_iterate(vector, ctx); flags = FLAG_INHERIT; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[BELOW]) { parserutils_vector_iterate(vector, ctx); value = ELEVATION_BELOW; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[LEVEL]) { parserutils_vector_iterate(vector, ctx); value = ELEVATION_LEVEL; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[ABOVE]) { parserutils_vector_iterate(vector, ctx); value = ELEVATION_ABOVE; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[HIGHER]) { parserutils_vector_iterate(vector, ctx); value = ELEVATION_HIGHER; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[LOWER]) { parserutils_vector_iterate(vector, ctx); value = ELEVATION_LOWER; } else { error = parse_unit_specifier(c, vector, ctx, UNIT_DEG, &length, &unit); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if ((unit & UNIT_ANGLE) == false) { *ctx = orig_ctx; return CSS_INVALID; } /* Valid angles lie between -90 and 90 degrees */ if (unit == UNIT_DEG) { if (length < FMULI(F_90, -1) || length > F_90) { *ctx = orig_ctx; return CSS_INVALID; } } else if (unit == UNIT_GRAD) { if (length < FMULI(F_100, -1) || length > F_100) { *ctx = orig_ctx; return CSS_INVALID; } } else if (unit == UNIT_RAD) { if (length < FMULI(F_PI_2, -1) || length > F_PI_2) { *ctx = orig_ctx; return CSS_INVALID; } } value = ELEVATION_ANGLE; } opv = buildOPV(CSS_PROP_ELEVATION, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == ELEVATION_ANGLE) required_size += sizeof(length) + sizeof(unit); /* 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 == ELEVATION_ANGLE) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &length, sizeof(length)); memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv) + sizeof(length), &unit, sizeof(unit)); } return CSS_OK; } /** * Parse pause-after * * \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_pause_after(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; css_fixed length = 0; uint32_t unit = 0; uint32_t required_size; /* time | percentage | IDENT(inherit) */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[INHERIT]) { parserutils_vector_iterate(vector, ctx); flags = FLAG_INHERIT; } else { error = parse_unit_specifier(c, vector, ctx, UNIT_S, &length, &unit); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if ((unit & UNIT_TIME) == false && (unit & UNIT_PCT) == false) { *ctx = orig_ctx; return CSS_INVALID; } /* Negative values are illegal */ if (length < 0) { *ctx = orig_ctx; return CSS_INVALID; } value = PAUSE_AFTER_SET; } opv = buildOPV(CSS_PROP_PAUSE_AFTER, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == PAUSE_AFTER_SET) required_size += sizeof(length) + sizeof(unit); /* 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 == PAUSE_AFTER_SET) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &length, sizeof(length)); memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv) + sizeof(length), &unit, sizeof(unit)); } return CSS_OK; } /** * Parse pause-before * * \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_pause_before(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; css_fixed length = 0; uint32_t unit = 0; uint32_t required_size; /* time | percentage | IDENT(inherit) */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[INHERIT]) { parserutils_vector_iterate(vector, ctx); flags = FLAG_INHERIT; } else { error = parse_unit_specifier(c, vector, ctx, UNIT_S, &length, &unit); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if ((unit & UNIT_TIME) == false && (unit & UNIT_PCT) == false) { *ctx = orig_ctx; return CSS_INVALID; } /* Negative values are illegal */ if (length < 0) { *ctx = orig_ctx; return CSS_INVALID; } value = PAUSE_BEFORE_SET; } opv = buildOPV(CSS_PROP_PAUSE_BEFORE, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == PAUSE_BEFORE_SET) required_size += sizeof(length) + sizeof(unit); /* 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 == PAUSE_BEFORE_SET) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &length, sizeof(length)); memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv) + sizeof(length), &unit, sizeof(unit)); } return CSS_OK; } /** * Parse pitch-range * * \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_pitch_range(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; css_fixed num = 0; uint32_t required_size; /* number | IDENT (inherit) */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_NUMBER)) { *ctx = orig_ctx; return CSS_INVALID; } if (token->ilower == c->strings[INHERIT]) { flags |= FLAG_INHERIT; } else if (token->type == CSS_TOKEN_NUMBER) { size_t consumed = 0; num = number_from_lwc_string(token->ilower, false, &consumed); /* Invalid if there are trailing characters */ if (consumed != lwc_string_length(token->ilower)) { *ctx = orig_ctx; return CSS_INVALID; } /* Must be between 0 and 100 */ if (num < 0 || num > F_100) { *ctx = orig_ctx; return CSS_INVALID; } value = PITCH_RANGE_SET; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_PITCH_RANGE, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == PITCH_RANGE_SET) required_size += sizeof(num); /* 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 == PITCH_RANGE_SET) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &num, sizeof(num)); } return CSS_OK; } /** * Parse pitch * * \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_pitch(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; css_fixed length = 0; uint32_t unit = 0; uint32_t required_size; /* frequency | IDENT(x-low, low, medium, high, x-high, inherit) */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[INHERIT]) { parserutils_vector_iterate(vector, ctx); flags = FLAG_INHERIT; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[X_LOW]) { parserutils_vector_iterate(vector, ctx); value = PITCH_X_LOW; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[LOW]) { parserutils_vector_iterate(vector, ctx); value = PITCH_LOW; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[MEDIUM]) { parserutils_vector_iterate(vector, ctx); value = PITCH_MEDIUM; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[HIGH]) { parserutils_vector_iterate(vector, ctx); value = PITCH_HIGH; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[X_HIGH]) { parserutils_vector_iterate(vector, ctx); value = PITCH_X_HIGH; } else { error = parse_unit_specifier(c, vector, ctx, UNIT_HZ, &length, &unit); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if ((unit & UNIT_FREQ) == false) { *ctx = orig_ctx; return CSS_INVALID; } /* Negative values are invalid */ if (length < 0) { *ctx = orig_ctx; return CSS_INVALID; } value = PITCH_FREQUENCY; } opv = buildOPV(CSS_PROP_PITCH, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == PITCH_FREQUENCY) required_size += sizeof(length) + sizeof(unit); /* 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 == PITCH_FREQUENCY) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &length, sizeof(length)); memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv) + sizeof(length), &unit, sizeof(unit)); } 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 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; uint32_t opv; uint32_t required_size; lwc_string *uri; /* 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 (token->ilower == c->strings[INHERIT]) { flags |= FLAG_INHERIT; } else if (token->ilower == c->strings[NONE]) { value = PLAY_DURING_NONE; } else if (token->ilower == c->strings[AUTO]) { value = PLAY_DURING_AUTO; } else { *ctx = orig_ctx; return CSS_INVALID; } } else { int modifiers; value = PLAY_DURING_URI; uri = token->idata; for (modifiers = 0; modifiers < 2; modifiers++) { consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token != NULL && token->type == CSS_TOKEN_IDENT) { if (token->ilower == c->strings[MIX]) { if ((value & PLAY_DURING_MIX) == 0) value |= PLAY_DURING_MIX; else { *ctx = orig_ctx; return CSS_INVALID; } } else if (token->ilower == c->strings[REPEAT]) { 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); } } } opv = buildOPV(CSS_PROP_PLAY_DURING, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && (value & PLAY_DURING_TYPE_MASK) == PLAY_DURING_URI) required_size += sizeof(lwc_string *); /* 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 & PLAY_DURING_TYPE_MASK) == PLAY_DURING_URI) { lwc_context_string_ref(c->sheet->dictionary, uri); memcpy((uint8_t *) (*result)->bytecode + sizeof(opv), &uri, sizeof(lwc_string *)); } return CSS_OK; } /** * Parse richness * * \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_richness(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; css_fixed num = 0; uint32_t required_size; /* number | IDENT (inherit) */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_NUMBER)) { *ctx = orig_ctx; return CSS_INVALID; } if (token->ilower == c->strings[INHERIT]) { flags |= FLAG_INHERIT; } else if (token->type == CSS_TOKEN_NUMBER) { size_t consumed = 0; num = number_from_lwc_string(token->ilower, false, &consumed); /* Invalid if there are trailing characters */ if (consumed != lwc_string_length(token->ilower)) { *ctx = orig_ctx; return CSS_INVALID; } /* Must be between 0 and 100 */ if (num < 0 || num > F_100) { *ctx = orig_ctx; return CSS_INVALID; } value = RICHNESS_SET; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_RICHNESS, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == RICHNESS_SET) required_size += sizeof(num); /* 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 == RICHNESS_SET) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &num, sizeof(num)); } return CSS_OK; } /** * Parse speak-header * * \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_speak_header(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { int orig_ctx = *ctx; css_error error; const css_token *ident; uint8_t flags = 0; uint16_t value = 0; uint32_t opv; /* IDENT (once, always, inherit) */ ident = parserutils_vector_iterate(vector, ctx); if (ident == NULL || ident->type != CSS_TOKEN_IDENT) { *ctx = orig_ctx; return CSS_INVALID; } if (ident->ilower == c->strings[INHERIT]) { flags |= FLAG_INHERIT; } else if (ident->ilower == c->strings[ONCE]) { value = SPEAK_HEADER_ONCE; } else if (ident->ilower == c->strings[ALWAYS]) { value = SPEAK_HEADER_ALWAYS; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_SPEAK_HEADER, flags, value); /* Allocate result */ error = css_stylesheet_style_create(c->sheet, sizeof(opv), result); if (error != CSS_OK) { *ctx = orig_ctx; return error; } /* Copy the bytecode to it */ memcpy((*result)->bytecode, &opv, sizeof(opv)); return CSS_OK; } /** * Parse speak-numeral * * \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_speak_numeral(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { int orig_ctx = *ctx; css_error error; const css_token *ident; uint8_t flags = 0; uint16_t value = 0; uint32_t opv; /* IDENT (digits, continuous, inherit) */ ident = parserutils_vector_iterate(vector, ctx); if (ident == NULL || ident->type != CSS_TOKEN_IDENT) { *ctx = orig_ctx; return CSS_INVALID; } if (ident->ilower == c->strings[INHERIT]) { flags |= FLAG_INHERIT; } else if (ident->ilower == c->strings[DIGITS]) { value = SPEAK_NUMERAL_DIGITS; } else if (ident->ilower == c->strings[CONTINUOUS]) { value = SPEAK_NUMERAL_CONTINUOUS; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_SPEAK_NUMERAL, flags, value); /* Allocate result */ error = css_stylesheet_style_create(c->sheet, sizeof(opv), result); if (error != CSS_OK) { *ctx = orig_ctx; return error; } /* Copy the bytecode to it */ memcpy((*result)->bytecode, &opv, sizeof(opv)); return CSS_OK; } /** * Parse speak-punctuation * * \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_speak_punctuation(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { int orig_ctx = *ctx; css_error error; const css_token *ident; uint8_t flags = 0; uint16_t value = 0; uint32_t opv; /* IDENT (code, none, inherit) */ ident = parserutils_vector_iterate(vector, ctx); if (ident == NULL || ident->type != CSS_TOKEN_IDENT) { *ctx = orig_ctx; return CSS_INVALID; } if (ident->ilower == c->strings[INHERIT]) { flags |= FLAG_INHERIT; } else if (ident->ilower == c->strings[CODE]) { value = SPEAK_PUNCTUATION_CODE; } else if (ident->ilower == c->strings[NONE]) { value = SPEAK_PUNCTUATION_NONE; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_SPEAK_PUNCTUATION, flags, value); /* Allocate result */ error = css_stylesheet_style_create(c->sheet, sizeof(opv), result); if (error != CSS_OK) { *ctx = orig_ctx; return error; } /* Copy the bytecode to it */ memcpy((*result)->bytecode, &opv, sizeof(opv)); return CSS_OK; } /** * Parse speak * * \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_speak(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { int orig_ctx = *ctx; css_error error; const css_token *ident; uint8_t flags = 0; uint16_t value = 0; uint32_t opv; /* IDENT (normal, none, spell-out, inherit) */ ident = parserutils_vector_iterate(vector, ctx); if (ident == NULL || ident->type != CSS_TOKEN_IDENT) { *ctx = orig_ctx; return CSS_INVALID; } if (ident->ilower == c->strings[INHERIT]) { flags |= FLAG_INHERIT; } else if (ident->ilower == c->strings[NORMAL]) { value = SPEAK_NORMAL; } else if (ident->ilower == c->strings[NONE]) { value = SPEAK_NONE; } else if (ident->ilower == c->strings[SPELL_OUT]) { value = SPEAK_SPELL_OUT; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_SPEAK, flags, value); /* Allocate result */ error = css_stylesheet_style_create(c->sheet, sizeof(opv), result); if (error != CSS_OK) { *ctx = orig_ctx; return error; } /* Copy the bytecode to it */ memcpy((*result)->bytecode, &opv, sizeof(opv)); return CSS_OK; } /** * Parse speech-rate * * \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_speech_rate(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; css_fixed num = 0; uint32_t required_size; /* number | IDENT (x-slow, slow, medium, fast, x-fast, faster, slower, * inherit) */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_NUMBER)) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[INHERIT]) { flags |= FLAG_INHERIT; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[X_SLOW]) { value = SPEECH_RATE_X_SLOW; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[SLOW]) { value = SPEECH_RATE_SLOW; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[MEDIUM]) { value = SPEECH_RATE_MEDIUM; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[FAST]) { value = SPEECH_RATE_FAST; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[X_FAST]) { value = SPEECH_RATE_X_FAST; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[FASTER]) { value = SPEECH_RATE_FASTER; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[SLOWER]) { value = SPEECH_RATE_SLOWER; } else if (token->type == CSS_TOKEN_NUMBER) { size_t consumed = 0; num = number_from_lwc_string(token->ilower, false, &consumed); /* Invalid if there are trailing characters */ if (consumed != lwc_string_length(token->ilower)) { *ctx = orig_ctx; return CSS_INVALID; } /* Make negative values invalid */ if (num < 0) { *ctx = orig_ctx; return CSS_INVALID; } value = SPEECH_RATE_SET; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_SPEECH_RATE, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == SPEECH_RATE_SET) required_size += sizeof(num); /* 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 == SPEECH_RATE_SET) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &num, sizeof(num)); } return CSS_OK; } /** * Parse stress * * \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_stress(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; css_fixed num = 0; uint32_t required_size; /* number | IDENT (inherit) */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_NUMBER)) { *ctx = orig_ctx; return CSS_INVALID; } if (token->ilower == c->strings[INHERIT]) { flags |= FLAG_INHERIT; } else if (token->type == CSS_TOKEN_NUMBER) { size_t consumed = 0; num = number_from_lwc_string(token->ilower, false, &consumed); /* Invalid if there are trailing characters */ if (consumed != lwc_string_length(token->ilower)) { *ctx = orig_ctx; return CSS_INVALID; } if (num < 0 || num > INTTOFIX(100)) { *ctx = orig_ctx; return CSS_INVALID; } value = STRESS_SET; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_STRESS, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == STRESS_SET) required_size += sizeof(num); /* 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 == STRESS_SET) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &num, sizeof(num)); } return CSS_OK; } /** * Determine if a given voice-family ident is reserved * * \param c Parsing context * \param ident IDENT to consider * \return True if IDENT is reserved, false otherwise */ static bool voice_family_reserved(css_language *c, const css_token *ident) { return ident->ilower == c->strings[MALE] || ident->ilower == c->strings[FEMALE] || ident->ilower == c->strings[CHILD]; } /** * Convert a voice-family token into a bytecode value * * \param c Parsing context * \param token Token to consider * \return Bytecode value */ static uint16_t voice_family_value(css_language *c, const css_token *token) { uint16_t value; if (token->type == CSS_TOKEN_IDENT) { if (token->ilower == c->strings[MALE]) value = VOICE_FAMILY_MALE; else if (token->ilower == c->strings[FEMALE]) value = VOICE_FAMILY_FEMALE; else if (token->ilower == c->strings[CHILD]) value = VOICE_FAMILY_CHILD; else value = VOICE_FAMILY_IDENT_LIST; } else { value = VOICE_FAMILY_STRING; } return value; } /** * Parse voice-family * * \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_voice_family(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; uint32_t required_size = sizeof(opv); int temp_ctx = *ctx; uint8_t *ptr; /* [ IDENT+ | STRING ] [ ',' [ IDENT+ | STRING ] ]* | IDENT(inherit) * * In the case of IDENT+, any whitespace between tokens is collapsed to * a single space */ /* Pass 1: validate input and calculate space */ token = parserutils_vector_iterate(vector, &temp_ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_STRING)) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[INHERIT]) { flags = FLAG_INHERIT; } else { uint32_t list_size; value = voice_family_value(c, token); error = comma_list_length(c, vector, &temp_ctx, token, voice_family_reserved, &list_size); if (error != CSS_OK) { *ctx = orig_ctx; return error; } required_size += list_size; } opv = buildOPV(CSS_PROP_VOICE_FAMILY, flags, value); /* Allocate result */ error = css_stylesheet_style_create(c->sheet, required_size, result); if (error != CSS_OK) { *ctx = orig_ctx; return error; } /* Copy OPV to bytecode */ ptr = (*result)->bytecode; memcpy(ptr, &opv, sizeof(opv)); ptr += sizeof(opv); /* Pass 2: populate bytecode */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_STRING)) { css_stylesheet_style_destroy(c->sheet, *result); *result = NULL; *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[INHERIT]) { /* Nothing to do */ } else { error = comma_list_to_bytecode(c, vector, ctx, token, voice_family_reserved, voice_family_value, &ptr); if (error != CSS_OK) { css_stylesheet_style_destroy(c->sheet, *result); *result = NULL; *ctx = orig_ctx; return error; } /* Write terminator */ opv = VOICE_FAMILY_END; memcpy(ptr, &opv, sizeof(opv)); ptr += sizeof(opv); } return CSS_OK; } /** * Parse volume * * \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_volume(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; css_fixed length = 0; uint32_t unit = 0; uint32_t required_size; /* number | percentage | IDENT(silent, x-soft, soft, medium, loud, * x-loud, inherit) */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[INHERIT]) { parserutils_vector_iterate(vector, ctx); flags = FLAG_INHERIT; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[SILENT]) { parserutils_vector_iterate(vector, ctx); value = VOLUME_SILENT; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[X_SOFT]) { parserutils_vector_iterate(vector, ctx); value = VOLUME_X_SOFT; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[SOFT]) { parserutils_vector_iterate(vector, ctx); value = VOLUME_SOFT; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[MEDIUM]) { parserutils_vector_iterate(vector, ctx); value = VOLUME_MEDIUM; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[LOUD]) { parserutils_vector_iterate(vector, ctx); value = VOLUME_LOUD; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[X_LOUD]) { parserutils_vector_iterate(vector, ctx); value = VOLUME_X_LOUD; } else if (token->type == CSS_TOKEN_NUMBER) { size_t consumed = 0; length = number_from_lwc_string(token->ilower, false, &consumed); if (consumed != lwc_string_length(token->ilower)) { *ctx = orig_ctx; return CSS_INVALID; } /* Must be between 0 and 100 */ if (length < 0 || length > F_100) { *ctx = orig_ctx; return CSS_INVALID; } parserutils_vector_iterate(vector, ctx); value = VOLUME_NUMBER; } else { /* Yes, really UNIT_PX -- percentages MUST have a % sign */ error = parse_unit_specifier(c, vector, ctx, UNIT_PX, &length, &unit); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if ((unit & UNIT_PCT) == false) { *ctx = orig_ctx; return CSS_INVALID; } /* Must be positive */ if (length < 0) { *ctx = orig_ctx; return CSS_INVALID; } value = VOLUME_DIMENSION; } opv = buildOPV(CSS_PROP_VOLUME, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == VOLUME_NUMBER) required_size += sizeof(length); else if ((flags & FLAG_INHERIT) == false && value == VOLUME_DIMENSION) required_size += sizeof(length) + sizeof(unit); /* 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 == VOLUME_NUMBER || value == VOLUME_DIMENSION)) memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &length, sizeof(length)); if ((flags & FLAG_INHERIT) == false && value == VOLUME_DIMENSION) memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv) + sizeof(length), &unit, sizeof(unit)); return CSS_OK; } /** * Common parser for cue-after and cue-before * * \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_cue_common(css_language *c, const parserutils_vector *vector, int *ctx, uint16_t op, 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; uint32_t required_size; /* URI | IDENT (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 && token->ilower == c->strings[INHERIT]) { flags |= FLAG_INHERIT; } else if (token->type == CSS_TOKEN_IDENT && token->ilower == c->strings[NONE]) { value = CUE_AFTER_NONE; } else if (token->type == CSS_TOKEN_URI) { value = CUE_AFTER_URI; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(op, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == CUE_AFTER_URI) required_size += sizeof(lwc_string *); /* 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 == CUE_AFTER_URI) { lwc_context_string_ref(c->sheet->dictionary, token->idata); memcpy((uint8_t *) (*result)->bytecode + sizeof(opv), &token->idata, sizeof(lwc_string *)); } return CSS_OK; }