Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of LibCSS.
  3.  * Licensed under the MIT License,
  4.  *                http://www.opensource.org/licenses/mit-license.php
  5.  * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org>
  6.  */
  7.  
  8. #include <assert.h>
  9. #include <string.h>
  10.  
  11. #include "stylesheet.h"
  12. #include "bytecode/bytecode.h"
  13. #include "bytecode/opcodes.h"
  14. #include "parse/properties/properties.h"
  15. #include "parse/properties/utils.h"
  16. #include "utils/parserutilserror.h"
  17.  
  18.  
  19. /**
  20.  * Parse list-style-type value
  21.  *
  22.  * \param c      Parsing context
  23.  * \param ident  Identifier to consider
  24.  * \param value  Pointer to location to receive value
  25.  * \return CSS_OK on success,
  26.  *         CSS_INVALID if the input is not valid
  27.  */
  28. css_error css__parse_list_style_type_value(css_language *c, const css_token *ident,
  29.                 uint16_t *value)
  30. {
  31.         bool match;
  32.  
  33.         /* IDENT (disc, circle, square, decimal, decimal-leading-zero,
  34.          *        lower-roman, upper-roman, lower-greek, lower-latin,
  35.          *        upper-latin, armenian, georgian, lower-alpha, upper-alpha,
  36.          *        none)
  37.          */
  38.         if ((lwc_string_caseless_isequal(
  39.                         ident->idata, c->strings[DISC],
  40.                         &match) == lwc_error_ok && match)) {
  41.                 *value = LIST_STYLE_TYPE_DISC;
  42.         } else if ((lwc_string_caseless_isequal(
  43.                         ident->idata, c->strings[CIRCLE],
  44.                         &match) == lwc_error_ok && match)) {
  45.                 *value = LIST_STYLE_TYPE_CIRCLE;
  46.         } else if ((lwc_string_caseless_isequal(
  47.                         ident->idata, c->strings[SQUARE],
  48.                         &match) == lwc_error_ok && match)) {
  49.                 *value = LIST_STYLE_TYPE_SQUARE;
  50.         } else if ((lwc_string_caseless_isequal(
  51.                         ident->idata, c->strings[DECIMAL],
  52.                         &match) == lwc_error_ok && match)) {
  53.                 *value = LIST_STYLE_TYPE_DECIMAL;
  54.         } else if ((lwc_string_caseless_isequal(
  55.                         ident->idata, c->strings[DECIMAL_LEADING_ZERO],
  56.                         &match) == lwc_error_ok && match)) {
  57.                 *value = LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO;
  58.         } else if ((lwc_string_caseless_isequal(
  59.                         ident->idata, c->strings[LOWER_ROMAN],
  60.                         &match) == lwc_error_ok && match)) {
  61.                 *value = LIST_STYLE_TYPE_LOWER_ROMAN;
  62.         } else if ((lwc_string_caseless_isequal(
  63.                         ident->idata, c->strings[UPPER_ROMAN],
  64.                         &match) == lwc_error_ok && match)) {
  65.                 *value = LIST_STYLE_TYPE_UPPER_ROMAN;
  66.         } else if ((lwc_string_caseless_isequal(
  67.                         ident->idata, c->strings[LOWER_GREEK],
  68.                         &match) == lwc_error_ok && match)) {
  69.                 *value = LIST_STYLE_TYPE_LOWER_GREEK;
  70.         } else if ((lwc_string_caseless_isequal(
  71.                         ident->idata, c->strings[LOWER_LATIN],
  72.                         &match) == lwc_error_ok && match)) {
  73.                 *value = LIST_STYLE_TYPE_LOWER_LATIN;
  74.         } else if ((lwc_string_caseless_isequal(
  75.                         ident->idata, c->strings[UPPER_LATIN],
  76.                         &match) == lwc_error_ok && match)) {
  77.                 *value = LIST_STYLE_TYPE_UPPER_LATIN;
  78.         } else if ((lwc_string_caseless_isequal(
  79.                         ident->idata, c->strings[ARMENIAN],
  80.                         &match) == lwc_error_ok && match)) {
  81.                 *value = LIST_STYLE_TYPE_ARMENIAN;
  82.         } else if ((lwc_string_caseless_isequal(
  83.                         ident->idata, c->strings[GEORGIAN],
  84.                         &match) == lwc_error_ok && match)) {
  85.                 *value = LIST_STYLE_TYPE_GEORGIAN;
  86.         } else if ((lwc_string_caseless_isequal(
  87.                         ident->idata, c->strings[LOWER_ALPHA],
  88.                         &match) == lwc_error_ok && match)) {
  89.                 *value = LIST_STYLE_TYPE_LOWER_ALPHA;
  90.         } else if ((lwc_string_caseless_isequal(
  91.                         ident->idata, c->strings[UPPER_ALPHA],
  92.                         &match) == lwc_error_ok && match)) {
  93.                 *value = LIST_STYLE_TYPE_UPPER_ALPHA;
  94.         } else if ((lwc_string_caseless_isequal(
  95.                         ident->idata, c->strings[NONE],
  96.                         &match) == lwc_error_ok && match)) {
  97.                 *value = LIST_STYLE_TYPE_NONE;
  98.         } else
  99.                 return CSS_INVALID;
  100.  
  101.         return CSS_OK;
  102. }
  103.  
  104.  
  105.  
  106. /**
  107.  * Parse border-{top,right,bottom,left} shorthand
  108.  *
  109.  * \param c       Parsing context
  110.  * \param vector  Vector of tokens to process
  111.  * \param ctx     Pointer to vector iteration context
  112.  * \param side    The side we're parsing for
  113.  * \param result  Pointer to location to receive resulting style
  114.  * \return CSS_OK on success,
  115.  *         CSS_NOMEM on memory exhaustion,
  116.  *         CSS_INVALID if the input is not valid
  117.  *
  118.  * Post condition: \a *ctx is updated with the next token to process
  119.  *                 If the input is invalid, then \a *ctx remains unchanged.
  120.  */
  121. css_error css__parse_border_side(css_language *c,
  122.                 const parserutils_vector *vector, int *ctx,
  123.                 css_style *result, enum border_side_e side)
  124. {
  125.         int orig_ctx = *ctx;
  126.         int prev_ctx;
  127.         const css_token *token;
  128.         css_error error = CSS_OK;
  129.         bool color = true;
  130.         bool style = true;
  131.         bool width = true;
  132.         css_style *color_style;
  133.         css_style *style_style;
  134.         css_style *width_style;
  135.  
  136.         /* Firstly, handle inherit */
  137.         token = parserutils_vector_peek(vector, *ctx);
  138.         if (token == NULL)
  139.                 return CSS_INVALID;
  140.                
  141.         if (is_css_inherit(c, token)) {
  142.                 error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_COLOR + side);
  143.                 if (error != CSS_OK)
  144.                         return error;
  145.  
  146.                 error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_STYLE + side);
  147.                 if (error != CSS_OK)
  148.                         return error;          
  149.  
  150.                 error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_WIDTH + side);
  151.                 if (error == CSS_OK)
  152.                         parserutils_vector_iterate(vector, ctx);
  153.  
  154.                 return error;
  155.         }
  156.  
  157.         /* allocate styles */
  158.         error = css__stylesheet_style_create(c->sheet, &color_style);
  159.         if (error != CSS_OK)
  160.                 return error;
  161.  
  162.         error = css__stylesheet_style_create(c->sheet, &style_style);
  163.         if (error != CSS_OK) {
  164.                 css__stylesheet_style_destroy(color_style);
  165.                 return error;
  166.         }
  167.  
  168.         error = css__stylesheet_style_create(c->sheet, &width_style);
  169.         if (error != CSS_OK) {
  170.                 css__stylesheet_style_destroy(color_style);
  171.                 css__stylesheet_style_destroy(width_style);
  172.                 return error;
  173.         }
  174.  
  175.         /* Attempt to parse the various longhand properties */
  176.         do {
  177.                 prev_ctx = *ctx;
  178.                 error = CSS_OK;
  179.  
  180.                 /* Ensure that we're not about to parse another inherit */
  181.                 token = parserutils_vector_peek(vector, *ctx);
  182.                 if (token != NULL && is_css_inherit(c, token)) {
  183.                         error = CSS_INVALID;
  184.                         goto css__parse_border_side_cleanup;
  185.                 }
  186.  
  187.                 /* Try each property parser in turn, but only if we
  188.                  * haven't already got a value for this property.
  189.                  */
  190.                 if ((color) &&
  191.                     (error = css__parse_border_side_color(c, vector, ctx,
  192.                              color_style, CSS_PROP_BORDER_TOP_COLOR + side)) == CSS_OK) {
  193.                         color = false;
  194.                 } else if ((style) &&
  195.                            (error = css__parse_border_side_style(c, vector, ctx,
  196.                                     style_style, CSS_PROP_BORDER_TOP_STYLE + side)) == CSS_OK) {
  197.                         style = false;
  198.                 } else if ((width) &&
  199.                            (error = css__parse_border_side_width(c, vector, ctx,
  200.                                     width_style, CSS_PROP_BORDER_TOP_WIDTH + side)) == CSS_OK) {
  201.                         width = false;
  202.                 }
  203.  
  204.                 if (error == CSS_OK) {
  205.                         consumeWhitespace(vector, ctx);
  206.  
  207.                         token = parserutils_vector_peek(vector, *ctx);
  208.                 } else {
  209.                         /* Forcibly cause loop to exit */
  210.                         token = NULL;
  211.                 }
  212.         } while (*ctx != prev_ctx && token != NULL);
  213.  
  214.         if (style) {
  215.                 error = css__stylesheet_style_appendOPV(style_style,
  216.                                 CSS_PROP_BORDER_TOP_STYLE + side, 0,
  217.                                 BORDER_STYLE_NONE);
  218.                 if (error != CSS_OK)
  219.                         goto css__parse_border_side_cleanup;
  220.         }
  221.  
  222.         if (width) {
  223.                 error = css__stylesheet_style_appendOPV(width_style,
  224.                                 CSS_PROP_BORDER_TOP_WIDTH + side,
  225.                                 0, BORDER_WIDTH_MEDIUM);
  226.                 if (error != CSS_OK)
  227.                         goto css__parse_border_side_cleanup;
  228.         }
  229.  
  230.         error = css__stylesheet_merge_style(result, color_style);
  231.         if (error != CSS_OK)
  232.                 goto css__parse_border_side_cleanup;
  233.  
  234.         error = css__stylesheet_merge_style(result, style_style);
  235.         if (error != CSS_OK)
  236.                 goto css__parse_border_side_cleanup;
  237.  
  238.         error = css__stylesheet_merge_style(result, width_style);
  239.  
  240. css__parse_border_side_cleanup:
  241.         css__stylesheet_style_destroy(color_style);
  242.         css__stylesheet_style_destroy(style_style);
  243.         css__stylesheet_style_destroy(width_style);
  244.  
  245.         if (error != CSS_OK)
  246.                 *ctx = orig_ctx;
  247.  
  248.         return error;
  249. }
  250.  
  251.  
  252. /**
  253.  * Convert Hue Saturation Lightness value to RGB.
  254.  *
  255.  * \param hue Hue in degrees 0..360
  256.  * \param sat Saturation value in percent 0..100
  257.  * \param lit Lightness value in percent 0..100
  258.  * \param r red component
  259.  * \param g green component
  260.  * \param b blue component
  261.  */
  262. static void HSL_to_RGB(css_fixed hue, css_fixed sat, css_fixed lit, uint8_t *r, uint8_t *g, uint8_t *b)
  263. {
  264.         css_fixed min_rgb, max_rgb, chroma;
  265.         css_fixed relative_hue, scaled_hue, mid1, mid2;
  266.         int sextant;
  267.  
  268. #define ORGB(R, G, B) \
  269.         *r = FIXTOINT(FDIV(FMUL((R), F_255), F_100)); \
  270.         *g = FIXTOINT(FDIV(FMUL((G), F_255), F_100)); \
  271.         *b = FIXTOINT(FDIV(FMUL((B), F_255), F_100))
  272.  
  273.         /* If saturation is zero there is no hue and r = g = b = lit */
  274.         if (sat == INTTOFIX(0)) {
  275.                 ORGB(lit, lit, lit);
  276.                 return;
  277.         }
  278.  
  279.         /* Compute max(r,g,b) */
  280.         if (lit <= INTTOFIX(50)) {
  281.                 max_rgb = FDIV(FMUL(lit, FADD(sat, F_100)), F_100);
  282.         } else {
  283.                 max_rgb = FDIV(FSUB(FMUL(FADD(lit, sat), F_100), FMUL(lit, sat)), F_100);
  284.         }
  285.  
  286.         /* Compute min(r,g,b) */
  287.         min_rgb = FSUB(FMUL(lit, INTTOFIX(2)), max_rgb);
  288.  
  289.         /* We know that the value of at least one of the components is
  290.          * max(r,g,b) and that the value of at least one of the other
  291.          * components is min(r,g,b).
  292.          *
  293.          * We can determine which components have these values by
  294.          * considering which the sextant of the hexcone the hue lies
  295.          * in:
  296.          *
  297.          * Sextant:     max(r,g,b):     min(r,g,b):
  298.          *
  299.          * 0            r               b
  300.          * 1            g               b
  301.          * 2            g               r
  302.          * 3            b               r
  303.          * 4            b               g
  304.          * 5            r               g
  305.          *
  306.          * Thus, we need only compute the value of the third component
  307.          */
  308.  
  309.         /* Chroma is the difference between min and max */
  310.         chroma = FSUB(max_rgb, min_rgb);
  311.  
  312.         /* Compute which sextant the hue lies in (truncates result) */
  313.         hue = FDIV(FMUL(hue, INTTOFIX(6)), F_360);
  314.         sextant = FIXTOINT(hue);
  315.  
  316.         /* Compute offset of hue from start of sextant */
  317.         relative_hue = FSUB(hue, INTTOFIX(sextant));
  318.  
  319.         /* Scale offset by chroma */
  320.         scaled_hue = FMUL(relative_hue, chroma);
  321.  
  322.         /* Compute potential values of the third colour component */
  323.         mid1 = FADD(min_rgb, scaled_hue);
  324.         mid2 = FSUB(max_rgb, scaled_hue);
  325.  
  326.         /* Populate result */
  327.         switch (sextant) {
  328.         case 0: ORGB(max_rgb,   mid1,      min_rgb); break;
  329.         case 1: ORGB(mid2,      max_rgb,   min_rgb); break;
  330.         case 2: ORGB(min_rgb,   max_rgb,   mid1); break;
  331.         case 3: ORGB(min_rgb,   mid2,      max_rgb); break;
  332.         case 4: ORGB(mid1,      min_rgb,   max_rgb); break;
  333.         case 5: ORGB(max_rgb,   min_rgb,   mid2); break;
  334.         }
  335.  
  336. #undef ORGB
  337. }
  338.  
  339. /**
  340.  * Parse a colour specifier
  341.  *
  342.  * \param c       Parsing context
  343.  * \param vector  Vector of tokens to process
  344.  * \param ctx     Pointer to vector iteration context
  345.  * \param value   Pointer to location to receive value
  346.  * \param result  Pointer to location to receive result (AARRGGBB)
  347.  * \return CSS_OK      on success,
  348.  *         CSS_INVALID if the input is invalid
  349.  *
  350.  * Post condition: \a *ctx is updated with the next token to process
  351.  *                 If the input is invalid, then \a *ctx remains unchanged.
  352.  */
  353. css_error css__parse_colour_specifier(css_language *c,
  354.                 const parserutils_vector *vector, int *ctx,
  355.                 uint16_t *value, uint32_t *result)
  356. {
  357.         int orig_ctx = *ctx;
  358.         const css_token *token;
  359.         bool match;
  360.         css_error error;
  361.  
  362.         consumeWhitespace(vector, ctx);
  363.  
  364.         /* IDENT(<colour name>) |
  365.          * HASH(rgb | rrggbb) |
  366.          * FUNCTION(rgb) [ [ NUMBER | PERCENTAGE ] ',' ] {3} ')'
  367.          * FUNCTION(rgba) [ [ NUMBER | PERCENTAGE ] ',' ] {4} ')'
  368.          * FUNCTION(hsl) ANGLE ',' PERCENTAGE ',' PERCENTAGE  ')'
  369.          * FUNCTION(hsla) ANGLE ',' PERCENTAGE ',' PERCENTAGE ',' NUMBER ')'
  370.          *
  371.          * For quirks, NUMBER | DIMENSION | IDENT, too
  372.          * I.E. "123456" -> NUMBER, "1234f0" -> DIMENSION, "f00000" -> IDENT
  373.          */
  374.         token = parserutils_vector_iterate(vector, ctx);
  375.         if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
  376.                         token->type != CSS_TOKEN_HASH &&
  377.                         token->type != CSS_TOKEN_FUNCTION)) {
  378.                 if (c->sheet->quirks_allowed == false ||
  379.                                 token == NULL ||
  380.                                 (token->type != CSS_TOKEN_NUMBER &&
  381.                                 token->type != CSS_TOKEN_DIMENSION))
  382.                         goto invalid;
  383.         }
  384.  
  385.         if (token->type == CSS_TOKEN_IDENT) {
  386.                 if ((lwc_string_caseless_isequal(
  387.                                 token->idata, c->strings[TRANSPARENT],
  388.                                 &match) == lwc_error_ok && match)) {
  389.                         *value = COLOR_TRANSPARENT;
  390.                         *result = 0; /* black transparent */
  391.                         return CSS_OK;
  392.                 } else if ((lwc_string_caseless_isequal(
  393.                                 token->idata, c->strings[CURRENTCOLOR],
  394.                                 &match) == lwc_error_ok && match)) {
  395.                         *value = COLOR_CURRENT_COLOR;
  396.                         *result = 0;
  397.                         return CSS_OK;
  398.                 }
  399.  
  400.                 error = css__parse_named_colour(c, token->idata, result);
  401.                 if (error != CSS_OK && c->sheet->quirks_allowed) {
  402.                         error = css__parse_hash_colour(token->idata, result);
  403.                         if (error == CSS_OK)
  404.                                 c->sheet->quirks_used = true;
  405.                 }
  406.  
  407.                 if (error != CSS_OK)
  408.                         goto invalid;
  409.         } else if (token->type == CSS_TOKEN_HASH) {
  410.                 error = css__parse_hash_colour(token->idata, result);
  411.                 if (error != CSS_OK)
  412.                         goto invalid;
  413.         } else if (c->sheet->quirks_allowed &&
  414.                         token->type == CSS_TOKEN_NUMBER) {
  415.                 error = css__parse_hash_colour(token->idata, result);
  416.                 if (error == CSS_OK)
  417.                         c->sheet->quirks_used = true;
  418.                 else
  419.                         goto invalid;
  420.         } else if (c->sheet->quirks_allowed &&
  421.                         token->type == CSS_TOKEN_DIMENSION) {
  422.                 error = css__parse_hash_colour(token->idata, result);
  423.                 if (error == CSS_OK)
  424.                         c->sheet->quirks_used = true;
  425.                 else
  426.                         goto invalid;
  427.         } else if (token->type == CSS_TOKEN_FUNCTION) {
  428.                 uint8_t r = 0, g = 0, b = 0, a = 0xff;
  429.                 int colour_channels = 0;
  430.  
  431.                 if ((lwc_string_caseless_isequal(
  432.                                 token->idata, c->strings[RGB],
  433.                                 &match) == lwc_error_ok && match)) {
  434.                         colour_channels = 3;
  435.                 } else if ((lwc_string_caseless_isequal(
  436.                                 token->idata, c->strings[RGBA],
  437.                                 &match) == lwc_error_ok && match)) {
  438.                         colour_channels = 4;
  439.                 } if ((lwc_string_caseless_isequal(
  440.                                 token->idata, c->strings[HSL],
  441.                                 &match) == lwc_error_ok && match)) {
  442.                         colour_channels = 5;
  443.                 } else if ((lwc_string_caseless_isequal(
  444.                                 token->idata, c->strings[HSLA],
  445.                                 &match) == lwc_error_ok && match)) {
  446.                         colour_channels = 6;
  447.                 }
  448.  
  449.                 if (colour_channels == 3 || colour_channels == 4) {
  450.                         int i;
  451.                         css_token_type valid = CSS_TOKEN_NUMBER;
  452.                         uint8_t *components[4] = { &r, &g, &b, &a };
  453.  
  454.                         for (i = 0; i < colour_channels; i++) {
  455.                                 uint8_t *component;
  456.                                 css_fixed num;
  457.                                 size_t consumed = 0;
  458.                                 int32_t intval;
  459.                                 bool int_only;
  460.  
  461.                                 component = components[i];
  462.  
  463.                                 consumeWhitespace(vector, ctx);
  464.  
  465.                                 token = parserutils_vector_peek(vector, *ctx);
  466.                                 if (token == NULL || (token->type !=
  467.                                                 CSS_TOKEN_NUMBER &&
  468.                                                 token->type !=
  469.                                                 CSS_TOKEN_PERCENTAGE))
  470.                                         goto invalid;
  471.  
  472.                                 if (i == 0)
  473.                                         valid = token->type;
  474.                                 else if (i < 3 && token->type != valid)
  475.                                         goto invalid;
  476.  
  477.                                 /* The alpha channel may be a float */
  478.                                 if (i < 3)
  479.                                         int_only = (valid == CSS_TOKEN_NUMBER);
  480.                                 else
  481.                                         int_only = false;
  482.  
  483.                                 num = css__number_from_lwc_string(token->idata,
  484.                                                 int_only, &consumed);
  485.                                 if (consumed != lwc_string_length(token->idata))
  486.                                         goto invalid;
  487.  
  488.                                 if (valid == CSS_TOKEN_NUMBER) {
  489.                                         if (i == 3) {
  490.                                                 /* alpha channel */
  491.                                                 intval = FIXTOINT(
  492.                                                         FMUL(num, F_255));
  493.                                         } else {
  494.                                                 /* colour channels */
  495.                                                 intval = FIXTOINT(num);
  496.                                         }
  497.                                 } else {
  498.                                         intval = FIXTOINT(
  499.                                                 FDIV(FMUL(num, F_255), F_100));
  500.                                 }
  501.  
  502.                                 if (intval > 255)
  503.                                         *component = 255;
  504.                                 else if (intval < 0)
  505.                                         *component = 0;
  506.                                 else
  507.                                         *component = intval;
  508.  
  509.                                 parserutils_vector_iterate(vector, ctx);
  510.  
  511.                                 consumeWhitespace(vector, ctx);
  512.  
  513.                                 token = parserutils_vector_peek(vector, *ctx);
  514.                                 if (token == NULL)
  515.                                         goto invalid;
  516.  
  517.                                 if (i != (colour_channels - 1) &&
  518.                                                 tokenIsChar(token, ',')) {
  519.                                         parserutils_vector_iterate(vector, ctx);
  520.                                 } else if (i == (colour_channels - 1) &&
  521.                                                 tokenIsChar(token, ')')) {
  522.                                         parserutils_vector_iterate(vector, ctx);
  523.                                 } else {
  524.                                         goto invalid;
  525.                                 }
  526.                         }
  527.                 } else if (colour_channels == 5 || colour_channels == 6) {
  528.                         /* hue - saturation - lightness */
  529.                         size_t consumed = 0;
  530.                         css_fixed hue, sat, lit;
  531.                         int32_t alpha = 255;
  532.  
  533.                         /* hue is a number without a unit representing an
  534.                          * angle (0-360) degrees  
  535.                          */
  536.                         consumeWhitespace(vector, ctx);
  537.  
  538.                         token = parserutils_vector_iterate(vector, ctx);
  539.                         if ((token == NULL) || (token->type != CSS_TOKEN_NUMBER))
  540.                                 goto invalid;
  541.  
  542.                         hue = css__number_from_lwc_string(token->idata, false, &consumed);
  543.                         if (consumed != lwc_string_length(token->idata))
  544.                                 goto invalid; /* failed to consume the whole string as a number */
  545.  
  546.                         /* Normalise hue to the range [0, 360) */
  547.                         while (hue < 0)
  548.                                 hue += F_360;
  549.                         while (hue >= F_360)
  550.                                 hue -= F_360;
  551.  
  552.                         consumeWhitespace(vector, ctx);
  553.  
  554.                         token = parserutils_vector_iterate(vector, ctx);
  555.                         if (!tokenIsChar(token, ','))
  556.                                 goto invalid;
  557.  
  558.  
  559.                         /* saturation */
  560.                         consumeWhitespace(vector, ctx);
  561.  
  562.                         token = parserutils_vector_iterate(vector, ctx);
  563.                         if ((token == NULL) || (token->type != CSS_TOKEN_PERCENTAGE))
  564.                                 goto invalid;
  565.  
  566.                         sat = css__number_from_lwc_string(token->idata, false, &consumed);
  567.                         if (consumed != lwc_string_length(token->idata))
  568.                                 goto invalid; /* failed to consume the whole string as a number */
  569.  
  570.                         /* Normalise saturation to the range [0, 100] */
  571.                         if (sat < INTTOFIX(0))
  572.                                 sat = INTTOFIX(0);
  573.                         else if (sat > INTTOFIX(100))
  574.                                 sat = INTTOFIX(100);
  575.  
  576.                         consumeWhitespace(vector, ctx);
  577.  
  578.                         token = parserutils_vector_iterate(vector, ctx);
  579.                         if (!tokenIsChar(token, ','))
  580.                                 goto invalid;
  581.  
  582.  
  583.                         /* lightness */
  584.                         consumeWhitespace(vector, ctx);
  585.  
  586.                         token = parserutils_vector_iterate(vector, ctx);
  587.                         if ((token == NULL) || (token->type != CSS_TOKEN_PERCENTAGE))
  588.                                 goto invalid;
  589.  
  590.                         lit = css__number_from_lwc_string(token->idata, false, &consumed);
  591.                         if (consumed != lwc_string_length(token->idata))
  592.                                 goto invalid; /* failed to consume the whole string as a number */
  593.  
  594.                         /* Normalise lightness to the range [0, 100] */
  595.                         if (lit < INTTOFIX(0))
  596.                                 lit = INTTOFIX(0);
  597.                         else if (lit > INTTOFIX(100))
  598.                                 lit = INTTOFIX(100);
  599.  
  600.                         consumeWhitespace(vector, ctx);
  601.  
  602.                         token = parserutils_vector_iterate(vector, ctx);
  603.  
  604.                         if (colour_channels == 6) {
  605.                                 /* alpha */
  606.  
  607.                                 if (!tokenIsChar(token, ','))
  608.                                         goto invalid;
  609.                        
  610.                                 consumeWhitespace(vector, ctx);
  611.  
  612.                                 token = parserutils_vector_iterate(vector, ctx);
  613.                                 if ((token == NULL) || (token->type != CSS_TOKEN_NUMBER))
  614.                                         goto invalid;
  615.  
  616.                                 alpha = css__number_from_lwc_string(token->idata, false, &consumed);
  617.                                 if (consumed != lwc_string_length(token->idata))
  618.                                         goto invalid; /* failed to consume the whole string as a number */
  619.                                
  620.                                 alpha = FIXTOINT(FMUL(alpha, F_255));
  621.  
  622.                                 consumeWhitespace(vector, ctx);
  623.  
  624.                                 token = parserutils_vector_iterate(vector, ctx);
  625.  
  626.                         }
  627.  
  628.                         if (!tokenIsChar(token, ')'))
  629.                                 goto invalid;
  630.  
  631.                         /* have a valid HSV entry, convert to RGB */
  632.                         HSL_to_RGB(hue, sat, lit, &r, &g, &b);
  633.  
  634.                         /* apply alpha */
  635.                         if (alpha > 255)
  636.                                 a = 255;
  637.                         else if (alpha < 0)
  638.                                 a = 0;
  639.                         else
  640.                                 a = alpha;
  641.  
  642.                 } else {
  643.                         goto invalid;
  644.                 }
  645.  
  646.                 *result = (a << 24) | (r << 16) | (g << 8) | b;
  647.         }
  648.  
  649.         *value = COLOR_SET;
  650.  
  651.         return CSS_OK;
  652.  
  653. invalid:
  654.         *ctx = orig_ctx;
  655.         return CSS_INVALID;
  656. }
  657.  
  658. /**
  659.  * Parse a named colour
  660.  *
  661.  * \param c       Parsing context
  662.  * \param data    Colour name string
  663.  * \param result  Pointer to location to receive result
  664.  * \return CSS_OK      on success,
  665.  *         CSS_INVALID if the colour name is unknown
  666.  */
  667. css_error css__parse_named_colour(css_language *c, lwc_string *data,
  668.                 uint32_t *result)
  669. {
  670.         static const uint32_t colourmap[LAST_COLOUR + 1 - FIRST_COLOUR] = {
  671.                 0xfff0f8ff, /* ALICEBLUE */
  672.                 0xfffaebd7, /* ANTIQUEWHITE */
  673.                 0xff00ffff, /* AQUA */
  674.                 0xff7fffd4, /* AQUAMARINE */
  675.                 0xfff0ffff, /* AZURE */
  676.                 0xfff5f5dc, /* BEIGE */
  677.                 0xffffe4c4, /* BISQUE */
  678.                 0xff000000, /* BLACK */
  679.                 0xffffebcd, /* BLANCHEDALMOND */
  680.                 0xff0000ff, /* BLUE */
  681.                 0xff8a2be2, /* BLUEVIOLET */
  682.                 0xffa52a2a, /* BROWN */
  683.                 0xffdeb887, /* BURLYWOOD */
  684.                 0xff5f9ea0, /* CADETBLUE */
  685.                 0xff7fff00, /* CHARTREUSE */
  686.                 0xffd2691e, /* CHOCOLATE */
  687.                 0xffff7f50, /* CORAL */
  688.                 0xff6495ed, /* CORNFLOWERBLUE */
  689.                 0xfffff8dc, /* CORNSILK */
  690.                 0xffdc143c, /* CRIMSON */
  691.                 0xff00ffff, /* CYAN */
  692.                 0xff00008b, /* DARKBLUE */
  693.                 0xff008b8b, /* DARKCYAN */
  694.                 0xffb8860b, /* DARKGOLDENROD */
  695.                 0xffa9a9a9, /* DARKGRAY */
  696.                 0xff006400, /* DARKGREEN */
  697.                 0xffa9a9a9, /* DARKGREY */
  698.                 0xffbdb76b, /* DARKKHAKI */
  699.                 0xff8b008b, /* DARKMAGENTA */
  700.                 0xff556b2f, /* DARKOLIVEGREEN */
  701.                 0xffff8c00, /* DARKORANGE */
  702.                 0xff9932cc, /* DARKORCHID */
  703.                 0xff8b0000, /* DARKRED */
  704.                 0xffe9967a, /* DARKSALMON */
  705.                 0xff8fbc8f, /* DARKSEAGREEN */
  706.                 0xff483d8b, /* DARKSLATEBLUE */
  707.                 0xff2f4f4f, /* DARKSLATEGRAY */
  708.                 0xff2f4f4f, /* DARKSLATEGREY */
  709.                 0xff00ced1, /* DARKTURQUOISE */
  710.                 0xff9400d3, /* DARKVIOLET */
  711.                 0xffff1493, /* DEEPPINK */
  712.                 0xff00bfff, /* DEEPSKYBLUE */
  713.                 0xff696969, /* DIMGRAY */
  714.                 0xff696969, /* DIMGREY */
  715.                 0xff1e90ff, /* DODGERBLUE */
  716.                 0xffd19275, /* FELDSPAR */
  717.                 0xffb22222, /* FIREBRICK */
  718.                 0xfffffaf0, /* FLORALWHITE */
  719.                 0xff228b22, /* FORESTGREEN */
  720.                 0xffff00ff, /* FUCHSIA */
  721.                 0xffdcdcdc, /* GAINSBORO */
  722.                 0xfff8f8ff, /* GHOSTWHITE */
  723.                 0xffffd700, /* GOLD */
  724.                 0xffdaa520, /* GOLDENROD */
  725.                 0xff808080, /* GRAY */
  726.                 0xff008000, /* GREEN */
  727.                 0xffadff2f, /* GREENYELLOW */
  728.                 0xff808080, /* GREY */
  729.                 0xfff0fff0, /* HONEYDEW */
  730.                 0xffff69b4, /* HOTPINK */
  731.                 0xffcd5c5c, /* INDIANRED */
  732.                 0xff4b0082, /* INDIGO */
  733.                 0xfffffff0, /* IVORY */
  734.                 0xfff0e68c, /* KHAKI */
  735.                 0xffe6e6fa, /* LAVENDER */
  736.                 0xfffff0f5, /* LAVENDERBLUSH */
  737.                 0xff7cfc00, /* LAWNGREEN */
  738.                 0xfffffacd, /* LEMONCHIFFON */
  739.                 0xffadd8e6, /* LIGHTBLUE */
  740.                 0xfff08080, /* LIGHTCORAL */
  741.                 0xffe0ffff, /* LIGHTCYAN */
  742.                 0xfffafad2, /* LIGHTGOLDENRODYELLOW */
  743.                 0xffd3d3d3, /* LIGHTGRAY */
  744.                 0xff90ee90, /* LIGHTGREEN */
  745.                 0xffd3d3d3, /* LIGHTGREY */
  746.                 0xffffb6c1, /* LIGHTPINK */
  747.                 0xffffa07a, /* LIGHTSALMON */
  748.                 0xff20b2aa, /* LIGHTSEAGREEN */
  749.                 0xff87cefa, /* LIGHTSKYBLUE */
  750.                 0xff8470ff, /* LIGHTSLATEBLUE */
  751.                 0xff778899, /* LIGHTSLATEGRAY */
  752.                 0xff778899, /* LIGHTSLATEGREY */
  753.                 0xffb0c4de, /* LIGHTSTEELBLUE */
  754.                 0xffffffe0, /* LIGHTYELLOW */
  755.                 0xff00ff00, /* LIME */
  756.                 0xff32cd32, /* LIMEGREEN */
  757.                 0xfffaf0e6, /* LINEN */
  758.                 0xffff00ff, /* MAGENTA */
  759.                 0xff800000, /* MAROON */
  760.                 0xff66cdaa, /* MEDIUMAQUAMARINE */
  761.                 0xff0000cd, /* MEDIUMBLUE */
  762.                 0xffba55d3, /* MEDIUMORCHID */
  763.                 0xff9370db, /* MEDIUMPURPLE */
  764.                 0xff3cb371, /* MEDIUMSEAGREEN */
  765.                 0xff7b68ee, /* MEDIUMSLATEBLUE */
  766.                 0xff00fa9a, /* MEDIUMSPRINGGREEN */
  767.                 0xff48d1cc, /* MEDIUMTURQUOISE */
  768.                 0xffc71585, /* MEDIUMVIOLETRED */
  769.                 0xff191970, /* MIDNIGHTBLUE */
  770.                 0xfff5fffa, /* MINTCREAM */
  771.                 0xffffe4e1, /* MISTYROSE */
  772.                 0xffffe4b5, /* MOCCASIN */
  773.                 0xffffdead, /* NAVAJOWHITE */
  774.                 0xff000080, /* NAVY */
  775.                 0xfffdf5e6, /* OLDLACE */
  776.                 0xff808000, /* OLIVE */
  777.                 0xff6b8e23, /* OLIVEDRAB */
  778.                 0xffffa500, /* ORANGE */
  779.                 0xffff4500, /* ORANGERED */
  780.                 0xffda70d6, /* ORCHID */
  781.                 0xffeee8aa, /* PALEGOLDENROD */
  782.                 0xff98fb98, /* PALEGREEN */
  783.                 0xffafeeee, /* PALETURQUOISE */
  784.                 0xffdb7093, /* PALEVIOLETRED */
  785.                 0xffffefd5, /* PAPAYAWHIP */
  786.                 0xffffdab9, /* PEACHPUFF */
  787.                 0xffcd853f, /* PERU */
  788.                 0xffffc0cb, /* PINK */
  789.                 0xffdda0dd, /* PLUM */
  790.                 0xffb0e0e6, /* POWDERBLUE */
  791.                 0xff800080, /* PURPLE */
  792.                 0xffff0000, /* RED */
  793.                 0xffbc8f8f, /* ROSYBROWN */
  794.                 0xff4169e1, /* ROYALBLUE */
  795.                 0xff8b4513, /* SADDLEBROWN */
  796.                 0xfffa8072, /* SALMON */
  797.                 0xfff4a460, /* SANDYBROWN */
  798.                 0xff2e8b57, /* SEAGREEN */
  799.                 0xfffff5ee, /* SEASHELL */
  800.                 0xffa0522d, /* SIENNA */
  801.                 0xffc0c0c0, /* SILVER */
  802.                 0xff87ceeb, /* SKYBLUE */
  803.                 0xff6a5acd, /* SLATEBLUE */
  804.                 0xff708090, /* SLATEGRAY */
  805.                 0xff708090, /* SLATEGREY */
  806.                 0xfffffafa, /* SNOW */
  807.                 0xff00ff7f, /* SPRINGGREEN */
  808.                 0xff4682b4, /* STEELBLUE */
  809.                 0xffd2b48c, /* TAN */
  810.                 0xff008080, /* TEAL */
  811.                 0xffd8bfd8, /* THISTLE */
  812.                 0xffff6347, /* TOMATO */
  813.                 0xff40e0d0, /* TURQUOISE */
  814.                 0xffee82ee, /* VIOLET */
  815.                 0xffd02090, /* VIOLETRED */
  816.                 0xfff5deb3, /* WHEAT */
  817.                 0xffffffff, /* WHITE */
  818.                 0xfff5f5f5, /* WHITESMOKE */
  819.                 0xffffff00, /* YELLOW */
  820.                 0xff9acd32  /* YELLOWGREEN */
  821.         };
  822.         int i;
  823.         bool match;
  824.  
  825.         for (i = FIRST_COLOUR; i <= LAST_COLOUR; i++) {
  826.                 if (lwc_string_caseless_isequal(data, c->strings[i],
  827.                                 &match) == lwc_error_ok && match)
  828.                         break;
  829.         }
  830.  
  831.         if (i <= LAST_COLOUR) {
  832.                 /* Known named colour */
  833.                 *result = colourmap[i - FIRST_COLOUR];
  834.                 return CSS_OK;
  835.         }
  836.  
  837.         /* We don't know this colour name; ask the client */
  838.         if (c->sheet->color != NULL)
  839.                 return c->sheet->color(c->sheet->color_pw, data, result);
  840.  
  841.         /* Invalid colour name */
  842.         return CSS_INVALID;
  843. }
  844.  
  845. /**
  846.  * Parse a hash colour (#rgb or #rrggbb)
  847.  *
  848.  * \param data    Pointer to colour string
  849.  * \param result  Pointer to location to receive result (AARRGGBB)
  850.  * \return CSS_OK      on success,
  851.  *         CSS_INVALID if the input is invalid
  852.  */
  853. css_error css__parse_hash_colour(lwc_string *data, uint32_t *result)
  854. {
  855.         uint8_t r = 0, g = 0, b = 0, a = 0xff;
  856.         size_t len = lwc_string_length(data);
  857.         const char *input = lwc_string_data(data);
  858.  
  859.         if (len == 3 && isHex(input[0]) && isHex(input[1]) &&
  860.                         isHex(input[2])) {
  861.                 r = charToHex(input[0]);
  862.                 g = charToHex(input[1]);
  863.                 b = charToHex(input[2]);
  864.  
  865.                 r |= (r << 4);
  866.                 g |= (g << 4);
  867.                 b |= (b << 4);
  868.         } else if (len == 6 && isHex(input[0]) && isHex(input[1]) &&
  869.                         isHex(input[2]) && isHex(input[3]) &&
  870.                         isHex(input[4]) && isHex(input[5])) {
  871.                 r = (charToHex(input[0]) << 4);
  872.                 r |= charToHex(input[1]);
  873.                 g = (charToHex(input[2]) << 4);
  874.                 g |= charToHex(input[3]);
  875.                 b = (charToHex(input[4]) << 4);
  876.                 b |= charToHex(input[5]);
  877.         } else
  878.                 return CSS_INVALID;
  879.  
  880.         *result = (a << 24) | (r << 16) | (g << 8) | b;
  881.  
  882.         return CSS_OK;
  883. }
  884.  
  885. /**
  886.  * Parse a unit specifier
  887.  *
  888.  * \param c             Parsing context
  889.  * \param vector        Vector of tokens to process
  890.  * \param ctx           Pointer to current vector iteration context
  891.  * \param default_unit  The default unit to use if none specified
  892.  * \param length        Pointer to location to receive length
  893.  * \param unit          Pointer to location to receive unit
  894.  * \return CSS_OK      on success,
  895.  *         CSS_INVALID if the tokens do not form a valid unit
  896.  *
  897.  * Post condition: \a *ctx is updated with the next token to process
  898.  *                 If the input is invalid, then \a *ctx remains unchanged.
  899.  */
  900. css_error css__parse_unit_specifier(css_language *c,
  901.                 const parserutils_vector *vector, int *ctx,
  902.                 uint32_t default_unit,
  903.                 css_fixed *length, uint32_t *unit)
  904. {
  905.         int orig_ctx = *ctx;
  906.         const css_token *token;
  907.         css_fixed num;
  908.         size_t consumed = 0;
  909.         css_error error;
  910.  
  911.         consumeWhitespace(vector, ctx);
  912.  
  913.         token = parserutils_vector_iterate(vector, ctx);
  914.         if (token == NULL || (token->type != CSS_TOKEN_DIMENSION &&
  915.                         token->type != CSS_TOKEN_NUMBER &&
  916.                         token->type != CSS_TOKEN_PERCENTAGE)) {
  917.                 *ctx = orig_ctx;
  918.                 return CSS_INVALID;
  919.         }
  920.  
  921.         num = css__number_from_lwc_string(token->idata, false, &consumed);
  922.  
  923.         if (token->type == CSS_TOKEN_DIMENSION) {
  924.                 size_t len = lwc_string_length(token->idata);
  925.                 const char *data = lwc_string_data(token->idata);
  926.                 css_unit temp_unit = CSS_UNIT_PX;
  927.  
  928.                 error = css__parse_unit_keyword(data + consumed, len - consumed,
  929.                                 &temp_unit);
  930.                 if (error != CSS_OK) {
  931.                         *ctx = orig_ctx;
  932.                         return error;
  933.                 }
  934.  
  935.                 *unit = (uint32_t) temp_unit;
  936.         } else if (token->type == CSS_TOKEN_NUMBER) {
  937.                 /* Non-zero values are permitted in quirks mode */
  938.                 if (num != 0) {
  939.                         if (c->sheet->quirks_allowed) {
  940.                                 c->sheet->quirks_used = true;
  941.                         } else {
  942.                                 *ctx = orig_ctx;
  943.                                 return CSS_INVALID;
  944.                         }
  945.                 }
  946.  
  947.                 *unit = default_unit;
  948.  
  949.                 if (c->sheet->quirks_allowed) {
  950.                         /* Also, in quirks mode, we need to cater for
  951.                          * dimensions separated from their units by whitespace
  952.                          * (e.g. "0 px")
  953.                          */
  954.                         int temp_ctx = *ctx;
  955.                         css_unit temp_unit;
  956.  
  957.                         consumeWhitespace(vector, &temp_ctx);
  958.  
  959.                         /* Try to parse the unit keyword, ignoring errors */
  960.                         token = parserutils_vector_iterate(vector, &temp_ctx);
  961.                         if (token != NULL && token->type == CSS_TOKEN_IDENT) {
  962.                                 error = css__parse_unit_keyword(
  963.                                                 lwc_string_data(token->idata),
  964.                                                 lwc_string_length(token->idata),
  965.                                                 &temp_unit);
  966.                                 if (error == CSS_OK) {
  967.                                         c->sheet->quirks_used = true;
  968.                                         *ctx = temp_ctx;
  969.                                         *unit = (uint32_t) temp_unit;
  970.                                 }
  971.                         }
  972.                 }
  973.         } else {
  974.                 /* Percentage -- number must be entire token data */
  975.                 if (consumed != lwc_string_length(token->idata)) {
  976.                         *ctx = orig_ctx;
  977.                         return CSS_INVALID;
  978.                 }
  979.                 *unit = UNIT_PCT;
  980.         }
  981.  
  982.         *length = num;
  983.  
  984.         return CSS_OK;
  985. }
  986.  
  987. /**
  988.  * Parse a unit keyword
  989.  *
  990.  * \param ptr   Pointer to keyword string
  991.  * \param len   Length, in bytes, of string
  992.  * \param unit  Pointer to location to receive computed unit
  993.  * \return CSS_OK      on success,
  994.  *         CSS_INVALID on encountering an unknown keyword
  995.  */
  996. css_error css__parse_unit_keyword(const char *ptr, size_t len, uint32_t *unit)
  997. {
  998.         if (len == 4) {
  999.                 if (strncasecmp(ptr, "grad", 4) == 0)
  1000.                         *unit = UNIT_GRAD;
  1001.                 else
  1002.                         return CSS_INVALID;
  1003.         } else if (len == 3) {
  1004.                 if (strncasecmp(ptr, "kHz", 3) == 0)
  1005.                         *unit = UNIT_KHZ;
  1006.                 else if (strncasecmp(ptr, "deg", 3) == 0)
  1007.                         *unit = UNIT_DEG;
  1008.                 else if (strncasecmp(ptr, "rad", 3) == 0)
  1009.                         *unit = UNIT_RAD;
  1010.                 else
  1011.                         return CSS_INVALID;
  1012.         } else if (len == 2) {
  1013.                 if (strncasecmp(ptr, "Hz", 2) == 0)
  1014.                         *unit = UNIT_HZ;
  1015.                 else if (strncasecmp(ptr, "ms", 2) == 0)
  1016.                         *unit = UNIT_MS;
  1017.                 else if (strncasecmp(ptr, "px", 2) == 0)
  1018.                         *unit = UNIT_PX;
  1019.                 else if (strncasecmp(ptr, "ex", 2) == 0)
  1020.                         *unit = UNIT_EX;
  1021.                 else if (strncasecmp(ptr, "em", 2) == 0)
  1022.                         *unit = UNIT_EM;
  1023.                 else if (strncasecmp(ptr, "in", 2) == 0)
  1024.                         *unit = UNIT_IN;
  1025.                 else if (strncasecmp(ptr, "cm", 2) == 0)
  1026.                         *unit = UNIT_CM;
  1027.                 else if (strncasecmp(ptr, "mm", 2) == 0)
  1028.                         *unit = UNIT_MM;
  1029.                 else if (strncasecmp(ptr, "pt", 2) == 0)
  1030.                         *unit = UNIT_PT;
  1031.                 else if (strncasecmp(ptr, "pc", 2) == 0)
  1032.                         *unit = UNIT_PC;
  1033.                 else
  1034.                         return CSS_INVALID;
  1035.         } else if (len == 1) {
  1036.                 if (strncasecmp(ptr, "s", 1) == 0)
  1037.                         *unit = UNIT_S;
  1038.                 else
  1039.                         return CSS_INVALID;
  1040.         } else
  1041.                 return CSS_INVALID;
  1042.  
  1043.         return CSS_OK;
  1044. }
  1045.  
  1046. /**
  1047.  * Create a string from a list of IDENT/S tokens if the next token is IDENT
  1048.  * or references the next token's string if it is a STRING
  1049.  *
  1050.  * \param c          Parsing context
  1051.  * \param vector     Vector containing tokens
  1052.  * \param ctx        Vector iteration context
  1053.  * \param reserved   Callback to determine if an identifier is reserved
  1054.  * \param result     Pointer to location to receive resulting string
  1055.  * \return CSS_OK on success, appropriate error otherwise.
  1056.  *
  1057.  * Post condition: \a *ctx is updated with the next token to process
  1058.  *                 If the input is invalid, then \a *ctx remains unchanged.
  1059.  *
  1060.  *                 The resulting string's reference is passed to the caller
  1061.  */
  1062. css_error css__ident_list_or_string_to_string(css_language *c,
  1063.                 const parserutils_vector *vector, int *ctx,
  1064.                 bool (*reserved)(css_language *c, const css_token *ident),
  1065.                 lwc_string **result)
  1066. {
  1067.         const css_token *token;
  1068.        
  1069.         token = parserutils_vector_peek(vector, *ctx);
  1070.         if (token == NULL)
  1071.                 return CSS_INVALID;
  1072.        
  1073.         if (token->type == CSS_TOKEN_STRING) {
  1074.                 token = parserutils_vector_iterate(vector, ctx);
  1075.                 *result = lwc_string_ref(token->idata);
  1076.                 return CSS_OK;
  1077.         } else  if(token->type == CSS_TOKEN_IDENT) {
  1078.                 return css__ident_list_to_string(c, vector, ctx, reserved,
  1079.                                 result);
  1080.         }
  1081.        
  1082.         return CSS_INVALID;
  1083. }
  1084.  
  1085. /**
  1086.  * Create a string from a list of IDENT/S tokens
  1087.  *
  1088.  * \param c          Parsing context
  1089.  * \param vector     Vector containing tokens
  1090.  * \param ctx        Vector iteration context
  1091.  * \param reserved   Callback to determine if an identifier is reserved
  1092.  * \param result     Pointer to location to receive resulting string
  1093.  * \return CSS_OK on success, appropriate error otherwise.
  1094.  *
  1095.  * Post condition: \a *ctx is updated with the next token to process
  1096.  *                 If the input is invalid, then \a *ctx remains unchanged.
  1097.  *
  1098.  *                 The resulting string's reference is passed to the caller
  1099.  */
  1100. css_error css__ident_list_to_string(css_language *c,
  1101.                 const parserutils_vector *vector, int *ctx,
  1102.                 bool (*reserved)(css_language *c, const css_token *ident),
  1103.                 lwc_string **result)
  1104. {
  1105.         int orig_ctx = *ctx;
  1106.         const css_token *token;
  1107.         css_error error = CSS_OK;
  1108.         parserutils_buffer *buffer;
  1109.         parserutils_error perror;
  1110.         lwc_string *interned;
  1111.         lwc_error lerror;
  1112.  
  1113.         perror = parserutils_buffer_create((parserutils_alloc) c->alloc,
  1114.                         c->pw, &buffer);
  1115.         if (perror != PARSERUTILS_OK)
  1116.                 return css_error_from_parserutils_error(perror);
  1117.  
  1118.         /* We know this token exists, and is an IDENT */
  1119.         token = parserutils_vector_iterate(vector, ctx);
  1120.  
  1121.         /* Consume all subsequent IDENT or S tokens */ 
  1122.         while (token != NULL && (token->type == CSS_TOKEN_IDENT ||
  1123.                         token->type == CSS_TOKEN_S)) {
  1124.                 if (token->type == CSS_TOKEN_IDENT) {
  1125.                         /* IDENT -- if reserved, reject style */
  1126.                         if (reserved != NULL && reserved(c, token)) {
  1127.                                 error = CSS_INVALID;
  1128.                                 goto cleanup;
  1129.                         }
  1130.  
  1131.                         perror = parserutils_buffer_append(buffer,
  1132.                                         (const uint8_t *) lwc_string_data(token->idata),
  1133.                                         lwc_string_length(token->idata));
  1134.                 } else {
  1135.                         /* S */
  1136.                         perror = parserutils_buffer_append(buffer,
  1137.                                         (const uint8_t *) " ", 1);
  1138.                 }
  1139.  
  1140.                 if (perror != PARSERUTILS_OK) {
  1141.                         error = css_error_from_parserutils_error(perror);
  1142.                         goto cleanup;
  1143.                 }
  1144.  
  1145.                 token = parserutils_vector_iterate(vector, ctx);
  1146.         }
  1147.  
  1148.         /* Rewind context by one step if we consumed an unacceptable token */
  1149.         if (token != NULL)
  1150.                 *ctx = *ctx - 1;
  1151.  
  1152.         /* Strip trailing whitespace */
  1153.         while (buffer->length > 0 && buffer->data[buffer->length - 1] == ' ')
  1154.                 buffer->length--;
  1155.  
  1156.         /* Intern the buffer contents */
  1157.         lerror = lwc_intern_string((char *) buffer->data, buffer->length, &interned);
  1158.         if (lerror != lwc_error_ok) {
  1159.                 error = css_error_from_lwc_error(lerror);
  1160.                 goto cleanup;
  1161.         }
  1162.  
  1163.         *result = interned;
  1164.  
  1165. cleanup:
  1166.         parserutils_buffer_destroy(buffer);
  1167.  
  1168.         if (error != CSS_OK)
  1169.                 *ctx = orig_ctx;
  1170.  
  1171.         return error;
  1172. }
  1173.  
  1174. /**
  1175.  * Parse a comma separated list, converting to bytecode
  1176.  *
  1177.  * \param c          Parsing context
  1178.  * \param vector     Vector of tokens to process
  1179.  * \param ctx        Pointer to vector iteration context
  1180.  * \param reserved   Callback to determine if an identifier is reserved
  1181.  * \param get_value  Callback to retrieve bytecode value for a token
  1182.  * \param style      Pointer to output style
  1183.  * \return CSS_OK      on success,
  1184.  *         CSS_INVALID if the input is invalid
  1185.  *
  1186.  * Post condition: \a *ctx is updated with the next token to process
  1187.  *                 If the input is invalid, then \a *ctx remains unchanged.
  1188.  */
  1189. css_error css__comma_list_to_style(css_language *c,
  1190.                 const parserutils_vector *vector, int *ctx,
  1191.                 bool (*reserved)(css_language *c, const css_token *ident),
  1192.                 css_code_t (*get_value)(css_language *c, const css_token *token, bool first),
  1193.                 css_style *result)
  1194. {
  1195.         int orig_ctx = *ctx;
  1196.         int prev_ctx = orig_ctx;
  1197.         const css_token *token;
  1198.         bool first = true;
  1199.         css_error error = CSS_OK;
  1200.  
  1201.         token = parserutils_vector_iterate(vector, ctx);
  1202.         if (token == NULL) {
  1203.                 *ctx = orig_ctx;
  1204.                 return CSS_INVALID;
  1205.         }
  1206.  
  1207.         while (token != NULL) {
  1208.                 if (token->type == CSS_TOKEN_IDENT) {
  1209.                         css_code_t value = get_value(c, token, first);
  1210.  
  1211.                         if (reserved(c, token) == false) {
  1212.                                 lwc_string *str = NULL;
  1213.                                 uint32_t snumber;
  1214.  
  1215.                                 *ctx = prev_ctx;
  1216.  
  1217.                                 error = css__ident_list_to_string(c, vector, ctx,
  1218.                                                 reserved, &str);
  1219.                                 if (error != CSS_OK)
  1220.                                         goto cleanup;
  1221.  
  1222.                                 error = css__stylesheet_string_add(c->sheet,
  1223.                                                 str, &snumber);
  1224.                                 if (error != CSS_OK)
  1225.                                         goto cleanup;
  1226.  
  1227.                                 error = css__stylesheet_style_append(result,
  1228.                                                 value);
  1229.                                 if (error != CSS_OK)
  1230.                                         goto cleanup;
  1231.  
  1232.                                 error = css__stylesheet_style_append(result,
  1233.                                                 snumber);
  1234.                                 if (error != CSS_OK)
  1235.                                         goto cleanup;
  1236.                         } else {
  1237.                                 error = css__stylesheet_style_append(result,
  1238.                                                 value);
  1239.                                 if (error != CSS_OK)
  1240.                                         goto cleanup;
  1241.                         }
  1242.                 } else if (token->type == CSS_TOKEN_STRING) {
  1243.                         css_code_t value = get_value(c, token, first);
  1244.                         uint32_t snumber;
  1245.  
  1246.                         error = css__stylesheet_string_add(c->sheet,
  1247.                                         lwc_string_ref(token->idata), &snumber);
  1248.                         if (error != CSS_OK)
  1249.                                 goto cleanup;
  1250.  
  1251.                         error = css__stylesheet_style_append(result, value);
  1252.                         if (error != CSS_OK)
  1253.                                 goto cleanup;
  1254.  
  1255.                         error = css__stylesheet_style_append(result, snumber);
  1256.                         if (error != CSS_OK)
  1257.                                 goto cleanup;
  1258.                 } else {
  1259.                         error = CSS_INVALID;
  1260.                         goto cleanup;
  1261.                 }
  1262.  
  1263.                 consumeWhitespace(vector, ctx);
  1264.  
  1265.                 token = parserutils_vector_peek(vector, *ctx);
  1266.                 if (token != NULL && tokenIsChar(token, ',')) {
  1267.                         parserutils_vector_iterate(vector, ctx);
  1268.  
  1269.                         consumeWhitespace(vector, ctx);
  1270.  
  1271.                         token = parserutils_vector_peek(vector, *ctx);
  1272.                         if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
  1273.                                         token->type != CSS_TOKEN_STRING)) {
  1274.                                 error = CSS_INVALID;
  1275.                                 goto cleanup;
  1276.                         }
  1277.                 } else {
  1278.                         break;
  1279.                 }
  1280.  
  1281.                 first = false;
  1282.  
  1283.                 prev_ctx = *ctx;
  1284.  
  1285.                 token = parserutils_vector_iterate(vector, ctx);
  1286.         }
  1287.  
  1288. cleanup:
  1289.         if (error != CSS_OK)
  1290.                 *ctx = orig_ctx;
  1291.  
  1292.         return error;
  1293. }
  1294.  
  1295.