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 2008 John-Mark Bell <jmb@netsurf-browser.org>
  6.  */
  7.  
  8. /** \file CSS lexer
  9.  *
  10.  * See docs/Tokens for the production rules used by this lexer.
  11.  *
  12.  * See docs/Lexer for the inferred first characters for each token.
  13.  *
  14.  * See also CSS3 Syntax module and CSS2.1 $4.1.1 + errata
  15.  *
  16.  * The lexer assumes that all invalid Unicode codepoints have been converted
  17.  * to U+FFFD by the input stream.
  18.  *
  19.  * The lexer comprises a state machine, the top-level of which is derived from
  20.  * the First sets in docs/Lexer. Each top-level state may contain a number of
  21.  * sub states. These enable restarting of the parser.
  22.  */
  23.  
  24. #include <assert.h>
  25. #include <stdarg.h>
  26. #include <stdbool.h>
  27. #include <stdint.h>
  28.  
  29. #include <parserutils/charset/utf8.h>
  30. #include <parserutils/input/inputstream.h>
  31. #include <parserutils/utils/buffer.h>
  32.  
  33. #include <libcss/errors.h>
  34.  
  35. #include "lex/lex.h"
  36. #include "utils/parserutilserror.h"
  37. #include "utils/utils.h"
  38.  
  39. /** \todo Optimisation -- we're currently revisiting a bunch of input
  40.  *        characters (Currently, we're calling parserutils_inputstream_peek
  41.  *        about 1.5x the number of characters in the input stream). Ideally,
  42.  *        we'll visit each character in the input exactly once. In reality,
  43.  *        the upper bound is twice, due to the need, in some cases, to read
  44.  *        one character beyond the end of a token's input to detect the end
  45.  *        of the token. Resumability adds a little overhead here, unless
  46.  *        we're somewhat more clever when it comes to having support for
  47.  *        restarting mid-escape sequence. Currently, we rewind back to the
  48.  *        start of the sequence and process the whole thing again.
  49.  */
  50.  
  51. enum {
  52.         sSTART          =  0,
  53.         sATKEYWORD      =  1,
  54.         sSTRING         =  2,
  55.         sHASH           =  3,
  56.         sNUMBER         =  4,
  57.         sCDO            =  5,
  58.         sCDC            =  6,
  59.         sS              =  7,
  60.         sCOMMENT        =  8,
  61.         sMATCH          =  9,
  62.         sURI            = 10,
  63.         sIDENT          = 11,
  64.         sESCAPEDIDENT   = 12,
  65.         sURL            = 13,
  66.         sUCR            = 14
  67. };
  68.  
  69. /**
  70.  * CSS lexer object
  71.  */
  72. struct css_lexer
  73. {
  74.         parserutils_inputstream *input; /**< Inputstream containing CSS */
  75.  
  76.         size_t bytesReadForToken;       /**< Total bytes read from the
  77.                                          * inputstream for the current token */
  78.  
  79.         css_token token;                /**< The current token */
  80.  
  81.         bool escapeSeen;                /**< Whether an escape sequence has
  82.                                          * been seen while processing the input
  83.                                          * for the current token */
  84.         parserutils_buffer *unescapedTokenData; /**< Buffer containing
  85.                                                  * unescaped token data
  86.                                                  * (used iff escapeSeen == true)
  87.                                                  */
  88.  
  89.         unsigned int state    : 4,      /**< Current state */
  90.                      substate : 4;      /**< Current substate */
  91.  
  92.         struct {
  93.                 uint8_t first;          /**< First character read for token */
  94.                 size_t origBytes;       /**< Storage of current number of
  95.                                          * bytes read, for rewinding */
  96.                 bool lastWasStar;       /**< Whether the previous character
  97.                                          * was an asterisk */
  98.                 bool lastWasCR;         /**< Whether the previous character
  99.                                          * was CR */
  100.                 size_t bytesForURL;     /**< Input bytes read for "url(", for
  101.                                          * rewinding */
  102.                 size_t dataLenForURL;   /**< Output length for "url(", for
  103.                                          * rewinding */
  104.                 int hexCount;           /**< Counter for reading hex digits */
  105.         } context;                      /**< Context for the current state */
  106.  
  107.         bool emit_comments;             /**< Whether to emit comment tokens */
  108.  
  109.         uint32_t currentCol;            /**< Current column in source */
  110.         uint32_t currentLine;           /**< Current line in source */
  111.  
  112.         css_allocator_fn alloc;         /**< Memory (de)allocation function */
  113.         void *pw;                       /**< Pointer to client-specific data */
  114. };
  115.  
  116. #define APPEND(lexer, data, len)                                        \
  117. do {                                                                    \
  118.         css_error error;                                                \
  119.         error = appendToTokenData((lexer),                              \
  120.                         (const uint8_t *) (data), (len));               \
  121.         if (error != CSS_OK)                                            \
  122.                 return error;                                           \
  123.         (lexer)->bytesReadForToken += (len);                            \
  124.         (lexer)->currentCol += (len);                                   \
  125. } while(0)                                                              \
  126.  
  127. static css_error appendToTokenData(css_lexer *lexer,
  128.                 const uint8_t *data, size_t len);
  129. static css_error emitToken(css_lexer *lexer, css_token_type type,
  130.                 css_token **token);
  131.  
  132. static css_error AtKeyword(css_lexer *lexer, css_token **token);
  133. static css_error CDCOrIdentOrFunctionOrNPD(css_lexer *lexer,
  134.                 css_token **token);
  135. static css_error CDO(css_lexer *lexer, css_token **token);
  136. static css_error Comment(css_lexer *lexer, css_token **token);
  137. static css_error EscapedIdentOrFunction(css_lexer *lexer,
  138.                 css_token **token);
  139. static css_error Hash(css_lexer *lexer, css_token **token);
  140. static css_error IdentOrFunction(css_lexer *lexer,
  141.                 css_token **token);
  142. static css_error Match(css_lexer *lexer, css_token **token);
  143. static css_error NumberOrPercentageOrDimension(css_lexer *lexer,
  144.                 css_token **token);
  145. static css_error S(css_lexer *lexer, css_token **token);
  146. static css_error Start(css_lexer *lexer, css_token **token);
  147. static css_error String(css_lexer *lexer, css_token **token);
  148. static css_error URIOrUnicodeRangeOrIdentOrFunction(
  149.                 css_lexer *lexer, css_token **token);
  150. static css_error URI(css_lexer *lexer, css_token **token);
  151. static css_error UnicodeRange(css_lexer *lexer, css_token **token);
  152.  
  153. static css_error consumeDigits(css_lexer *lexer);
  154. static css_error consumeEscape(css_lexer *lexer, bool nl);
  155. static css_error consumeNMChars(css_lexer *lexer);
  156. static css_error consumeString(css_lexer *lexer);
  157. static css_error consumeStringChars(css_lexer *lexer);
  158. static css_error consumeUnicode(css_lexer *lexer, uint32_t ucs);
  159. static css_error consumeURLChars(css_lexer *lexer);
  160. static css_error consumeWChars(css_lexer *lexer);
  161.  
  162. static inline bool startNMChar(uint8_t c);
  163. static inline bool startNMStart(uint8_t c);
  164. static inline bool startStringChar(uint8_t c);
  165. static inline bool startURLChar(uint8_t c);
  166. static inline bool isSpace(uint8_t c);
  167.  
  168. /**
  169.  * Create a lexer instance
  170.  *
  171.  * \param input  The inputstream to read from
  172.  * \param alloc  Memory (de)allocation function
  173.  * \param pw     Pointer to client-specific private data
  174.  * \param lexer  Pointer to location to receive lexer instance
  175.  * \return CSS_OK on success,
  176.  *         CSS_BADPARM on bad parameters,
  177.  *         CSS_NOMEM on memory exhaustion
  178.  */
  179. css_error css__lexer_create(parserutils_inputstream *input,
  180.                 css_allocator_fn alloc, void *pw, css_lexer **lexer)
  181. {
  182.         css_lexer *lex;
  183.  
  184.         if (input == NULL || alloc == NULL || lexer == NULL)
  185.                 return CSS_BADPARM;
  186.  
  187.         lex = alloc(NULL, sizeof(css_lexer), pw);
  188.         if (lex == NULL)
  189.                 return CSS_NOMEM;
  190.  
  191.         lex->input = input;
  192.         lex->bytesReadForToken = 0;
  193.         lex->token.type = CSS_TOKEN_EOF;
  194.         lex->token.data.data = NULL;
  195.         lex->token.data.len = 0;
  196.         lex->escapeSeen = false;
  197.         lex->unescapedTokenData = NULL;
  198.         lex->state = sSTART;
  199.         lex->substate = 0;
  200.         lex->emit_comments = false;
  201.         lex->currentCol = 1;
  202.         lex->currentLine = 1;
  203.         lex->alloc = alloc;
  204.         lex->pw = pw;
  205.  
  206.         *lexer = lex;
  207.  
  208.         return CSS_OK;
  209. }
  210.  
  211. /**
  212.  * Destroy a lexer instance
  213.  *
  214.  * \param lexer  The instance to destroy
  215.  * \return CSS_OK on success, appropriate error otherwise
  216.  */
  217. css_error css__lexer_destroy(css_lexer *lexer)
  218. {
  219.         if (lexer == NULL)
  220.                 return CSS_BADPARM;
  221.  
  222.         if (lexer->unescapedTokenData != NULL)
  223.                 parserutils_buffer_destroy(lexer->unescapedTokenData);
  224.  
  225.         lexer->alloc(lexer, 0, lexer->pw);
  226.  
  227.         return CSS_OK;
  228. }
  229.  
  230. /**
  231.  * Configure a lexer instance
  232.  *
  233.  * \param lexer   The lexer to configure
  234.  * \param type    The option type to modify
  235.  * \param params  Option-specific parameters
  236.  * \return CSS_OK on success, appropriate error otherwise
  237.  */
  238. css_error css__lexer_setopt(css_lexer *lexer, css_lexer_opttype type,
  239.                 css_lexer_optparams *params)
  240. {
  241.         if (lexer == NULL || params == NULL)
  242.                 return CSS_BADPARM;
  243.  
  244.         switch (type) {
  245.         case CSS_LEXER_EMIT_COMMENTS:
  246.                 lexer->emit_comments = params->emit_comments;
  247.                 break;
  248.         default:
  249.                 return CSS_BADPARM;
  250.         }
  251.  
  252.         return CSS_OK;
  253. }
  254.  
  255. /**
  256.  * Retrieve a token from a lexer
  257.  *
  258.  * \param lexer  The lexer instance to read from
  259.  * \param token  Pointer to location to receive pointer to token
  260.  * \return CSS_OK on success, appropriate error otherwise
  261.  *
  262.  * The returned token object is owned by the lexer. However, the client is
  263.  * permitted to modify the data members of the token. The token must not be
  264.  * freed by the client (it may not have been allocated in the first place),
  265.  * nor may any of the pointers contained within it. The client may, if they
  266.  * wish, overwrite any data member of the returned token object -- the lexer
  267.  * does not depend on these remaining constant. This allows the client code
  268.  * to efficiently implement a push-back buffer with interned string data.
  269.  */
  270. css_error css__lexer_get_token(css_lexer *lexer, css_token **token)
  271. {
  272.         css_error error;
  273.  
  274.         if (lexer == NULL || token == NULL)
  275.                 return CSS_BADPARM;
  276.  
  277.         switch (lexer->state)
  278.         {
  279.         case sSTART:
  280.         start:
  281.                 return Start(lexer, token);
  282.         case sATKEYWORD:
  283.                 return AtKeyword(lexer, token);
  284.         case sSTRING:
  285.                 return String(lexer, token);
  286.         case sHASH:
  287.                 return Hash(lexer, token);
  288.         case sNUMBER:
  289.                 return NumberOrPercentageOrDimension(lexer, token);
  290.         case sCDO:
  291.                 return CDO(lexer, token);
  292.         case sCDC:
  293.                 return CDCOrIdentOrFunctionOrNPD(lexer, token);
  294.         case sS:
  295.                 return S(lexer, token);
  296.         case sCOMMENT:
  297.                 error = Comment(lexer, token);
  298.                 if (!lexer->emit_comments && error == CSS_OK &&
  299.                                 (*token)->type == CSS_TOKEN_COMMENT)
  300.                         goto start;
  301.                 return error;
  302.         case sMATCH:
  303.                 return Match(lexer, token);
  304.         case sURI:
  305.                 return URI(lexer, token);
  306.         case sIDENT:
  307.                 return IdentOrFunction(lexer, token);
  308.         case sESCAPEDIDENT:
  309.                 return EscapedIdentOrFunction(lexer, token);
  310.         case sURL:
  311.                 return URI(lexer, token);
  312.         case sUCR:
  313.                 return UnicodeRange(lexer, token);
  314.         }
  315.  
  316.         /* Should never be reached */
  317.         assert(0);
  318.  
  319.         return CSS_OK;
  320. }
  321.  
  322. /******************************************************************************
  323.  * Utility routines                                                           *
  324.  ******************************************************************************/
  325.  
  326. /**
  327.  * Append some data to the current token
  328.  *
  329.  * \param lexer  The lexer instance
  330.  * \param data   Pointer to data to append
  331.  * \param len    Length, in bytes, of data
  332.  * \return CSS_OK on success, appropriate error otherwise
  333.  *
  334.  * This should not be called directly without good reason. Use the APPEND()
  335.  * macro instead.
  336.  */
  337. css_error appendToTokenData(css_lexer *lexer, const uint8_t *data, size_t len)
  338. {
  339.         css_token *token = &lexer->token;
  340.  
  341.         if (lexer->escapeSeen) {
  342.                 css_error error = css_error_from_parserutils_error(
  343.                                 parserutils_buffer_append(
  344.                                         lexer->unescapedTokenData, data, len));
  345.                 if (error != CSS_OK)
  346.                         return error;
  347.         }
  348.  
  349.         token->data.len += len;
  350.  
  351.         return CSS_OK;
  352. }
  353.  
  354. /**
  355.  * Prepare a token for consumption and emit it to the client
  356.  *
  357.  * \param lexer  The lexer instance
  358.  * \param type   The type of token to emit
  359.  * \param token  Pointer to location to receive pointer to token
  360.  * \return CSS_OK on success, appropriate error otherwise
  361.  */
  362. css_error emitToken(css_lexer *lexer, css_token_type type,
  363.                 css_token **token)
  364. {
  365.         css_token *t = &lexer->token;
  366.  
  367.         t->type = type;
  368.  
  369.         /* Calculate token data start pointer. We have to do this here as
  370.          * the inputstream's buffer may have moved under us. */
  371.         if (lexer->escapeSeen) {
  372.                 t->data.data = lexer->unescapedTokenData->data;
  373.         } else {
  374.                 size_t clen;
  375.                 const uint8_t *data;
  376.                 parserutils_error error;
  377.  
  378.                 error = parserutils_inputstream_peek(lexer->input, 0,
  379.                                 &data, &clen);
  380.  
  381. #ifndef NDEBUG
  382.                 assert(type == CSS_TOKEN_EOF || error == PARSERUTILS_OK);
  383. #else
  384.                 (void) error;
  385. #endif
  386.  
  387.                 t->data.data = (type == CSS_TOKEN_EOF) ? NULL : (uint8_t *) data;
  388.         }
  389.  
  390.         switch (type) {
  391.         case CSS_TOKEN_ATKEYWORD:
  392.                 /* Strip the '@' from the front */
  393.                 t->data.data += 1;
  394.                 t->data.len -= 1;
  395.                 break;
  396.         case CSS_TOKEN_STRING:
  397.                 /* Strip the leading quote */
  398.                 t->data.data += 1;
  399.                 t->data.len -= 1;
  400.  
  401.                 /* Strip the trailing quote, iff it exists (may have hit EOF) */
  402.                 if (t->data.len > 0 && (t->data.data[t->data.len - 1] == '"' ||
  403.                                 t->data.data[t->data.len - 1] == '\'')) {
  404.                         t->data.len -= 1;
  405.                 }
  406.                 break;
  407.         case CSS_TOKEN_INVALID_STRING:
  408.                 /* Strip the leading quote */
  409.                 t->data.data += 1;
  410.                 t->data.len -= 1;
  411.                 break;
  412.         case CSS_TOKEN_HASH:
  413.                 /* Strip the '#' from the front */
  414.                 t->data.data += 1;
  415.                 t->data.len -= 1;
  416.                 break;
  417.         case CSS_TOKEN_PERCENTAGE:
  418.                 /* Strip the '%' from the end */
  419.                 t->data.len -= 1;
  420.                 break;
  421.         case CSS_TOKEN_DIMENSION:
  422.                 break;
  423.         case CSS_TOKEN_URI:
  424.                 /* Strip the "url(" from the start */
  425.                 t->data.data += SLEN("url(");
  426.                 t->data.len -= SLEN("url(");
  427.  
  428.                 /* Strip any leading whitespace */
  429.                 while (isSpace(t->data.data[0])) {
  430.                         t->data.data++;
  431.                         t->data.len--;
  432.                 }
  433.  
  434.                 /* Strip any leading quote */
  435.                 if (t->data.data[0] == '"' || t->data.data[0] == '\'') {
  436.                         t->data.data += 1;
  437.                         t->data.len -= 1;
  438.                 }
  439.  
  440.                 /* Strip the trailing ')' */
  441.                 t->data.len -= 1;
  442.  
  443.                 /* Strip any trailing whitespace */
  444.                 while (t->data.len > 0 &&
  445.                                 isSpace(t->data.data[t->data.len - 1])) {
  446.                         t->data.len--;
  447.                 }
  448.  
  449.                 /* Strip any trailing quote */
  450.                 if (t->data.len > 0 && (t->data.data[t->data.len - 1] == '"' ||
  451.                                 t->data.data[t->data.len - 1] == '\'')) {
  452.                         t->data.len -= 1;
  453.                 }
  454.                 break;
  455.         case CSS_TOKEN_UNICODE_RANGE:
  456.                 /* Remove "U+" from the start */
  457.                 t->data.data += SLEN("U+");
  458.                 t->data.len -= SLEN("U+");
  459.                 break;
  460.         case CSS_TOKEN_COMMENT:
  461.                 /* Strip the leading '/' and '*' */
  462.                 t->data.data += SLEN("/*");
  463.                 t->data.len -= SLEN("/*");
  464.  
  465.                 /* Strip the trailing '*' and '/' */
  466.                 t->data.len -= SLEN("*/");
  467.                 break;
  468.         case CSS_TOKEN_FUNCTION:
  469.                 /* Strip the trailing '(' */
  470.                 t->data.len -= 1;
  471.                 break;
  472.         default:
  473.                 break;
  474.         }
  475.  
  476.         *token = t;
  477.  
  478.         /* Reset the lexer's state */
  479.         lexer->state = sSTART;
  480.         lexer->substate = 0;
  481.  
  482.         return CSS_OK;
  483. }
  484.  
  485. /******************************************************************************
  486.  * State machine components                                                   *
  487.  ******************************************************************************/
  488.  
  489. css_error AtKeyword(css_lexer *lexer, css_token **token)
  490. {
  491.         const uint8_t *cptr;
  492.         uint8_t c;
  493.         size_t clen;
  494.         css_error error;
  495.         parserutils_error perror;
  496.         enum { Initial = 0, Escape = 1, NMChar = 2 };
  497.  
  498.         /* ATKEYWORD = '@' ident
  499.          *
  500.          * The '@' has been consumed.
  501.          */
  502.  
  503.         switch (lexer->substate) {
  504.         case Initial:
  505.                 perror = parserutils_inputstream_peek(lexer->input,
  506.                                 lexer->bytesReadForToken, &cptr, &clen);
  507.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  508.                         return css_error_from_parserutils_error(perror);
  509.  
  510.                 if (perror == PARSERUTILS_EOF)
  511.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  512.  
  513.                 c = *cptr;
  514.  
  515.                 if (!startNMStart(c))
  516.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  517.  
  518.                 if (c != '\\') {
  519.                         APPEND(lexer, cptr, clen);
  520.                 } else {
  521.                         lexer->bytesReadForToken += clen;
  522.                         goto escape;
  523.                 }
  524.  
  525.                 /* Fall through */
  526.         case NMChar:
  527.         nmchar:
  528.                 lexer->substate = NMChar;
  529.                 error = consumeNMChars(lexer);
  530.                 if (error != CSS_OK)
  531.                         return error;
  532.                 break;
  533.  
  534.         case Escape:
  535.         escape:
  536.                 lexer->substate = Escape;
  537.                 error = consumeEscape(lexer, false);
  538.                 if (error != CSS_OK) {
  539.                         if (error == CSS_EOF || error == CSS_INVALID) {
  540.                                 /* Rewind the '\\' */
  541.                                 lexer->bytesReadForToken -= 1;
  542.  
  543.                                 return emitToken(lexer, CSS_TOKEN_CHAR, token);
  544.                         }
  545.  
  546.                         return error;
  547.                 }
  548.  
  549.                 goto nmchar;
  550.         }
  551.  
  552.         return emitToken(lexer, CSS_TOKEN_ATKEYWORD, token);
  553. }
  554.  
  555. css_error CDCOrIdentOrFunctionOrNPD(css_lexer *lexer, css_token **token)
  556. {
  557.         css_token *t = &lexer->token;
  558.         const uint8_t *cptr;
  559.         uint8_t c;
  560.         size_t clen;
  561.         css_error error;
  562.         parserutils_error perror;
  563.         enum { Initial = 0, Escape = 1, Gt = 2 };
  564.  
  565.         /* CDC = "-->"
  566.          * IDENT = [-]? nmstart nmchar*
  567.          * FUNCTION = [-]? nmstart nmchar* '('
  568.          * NUMBER = num = [-+]? ([0-9]+ | [0-9]* '.' [0-9]+)
  569.          * PERCENTAGE = num '%'
  570.          * DIMENSION = num ident
  571.          *
  572.          * The first dash has been consumed. Thus, we must consume the next
  573.          * character in the stream. If it's a dash, then we're dealing with
  574.          * CDC. If it's a digit or dot, then we're dealing with NPD.
  575.          * Otherwise, we're dealing with IDENT/FUNCTION.
  576.          */
  577.  
  578.         switch (lexer->substate) {
  579.         case Initial:
  580.                 perror = parserutils_inputstream_peek(lexer->input,
  581.                                 lexer->bytesReadForToken, &cptr, &clen);
  582.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  583.                         return css_error_from_parserutils_error(perror);
  584.  
  585.                 if (perror == PARSERUTILS_EOF) {
  586.                         /* We can only match char with what we've read so far */
  587.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  588.                 }
  589.  
  590.                 c = *cptr;
  591.  
  592.                 if (isDigit(c) || c == '.') {
  593.                         /* NPD */
  594.                         APPEND(lexer, cptr, clen);
  595.                         lexer->state = sNUMBER;
  596.                         lexer->substate = 0;
  597.                         /* Abuse "first" to store first non-sign character */
  598.                         lexer->context.first = c;
  599.                         return NumberOrPercentageOrDimension(lexer, token);
  600.                 }
  601.  
  602.                 if (c != '-' && !startNMStart(c)) {
  603.                         /* Can only be CHAR */
  604.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  605.                 }
  606.  
  607.  
  608.                 if (c != '\\') {
  609.                         APPEND(lexer, cptr, clen);
  610.                 }
  611.  
  612.                 if (c != '-') {
  613.                         if (c == '\\') {
  614.                                 lexer->bytesReadForToken += clen;
  615.                                 goto escape;
  616.                         }
  617.  
  618.                         lexer->state = sIDENT;
  619.                         lexer->substate = 0;
  620.                         return IdentOrFunction(lexer, token);
  621.                 }
  622.  
  623.                 /* Fall through */
  624.         case Gt:
  625.                 lexer->substate = Gt;
  626.  
  627.                 /* Ok, so we're dealing with CDC. Expect a '>' */
  628.                 perror = parserutils_inputstream_peek(lexer->input,
  629.                                 lexer->bytesReadForToken, &cptr, &clen);
  630.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  631.                         return css_error_from_parserutils_error(perror);
  632.  
  633.                 if (perror == PARSERUTILS_EOF) {
  634.                         /* CHAR is the only match here */
  635.                         /* Remove the '-' we read above */
  636.                         lexer->bytesReadForToken -= 1;
  637.                         t->data.len -= 1;
  638.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  639.                 }
  640.  
  641.                 c = *cptr;
  642.  
  643.                 if (c == '>') {
  644.                         APPEND(lexer, cptr, clen);
  645.  
  646.                         t->type = CSS_TOKEN_CDC;
  647.                 } else {
  648.                         /* Remove the '-' we read above */
  649.                         lexer->bytesReadForToken -= 1;
  650.                         t->data.len -= 1;
  651.                         t->type = CSS_TOKEN_CHAR;
  652.                 }
  653.                 break;
  654.  
  655.         case Escape:
  656.         escape:
  657.                 lexer->substate = Escape;
  658.                 error = consumeEscape(lexer, false);
  659.                 if (error != CSS_OK) {
  660.                         if (error == CSS_EOF || error == CSS_INVALID) {
  661.                                 /* Rewind the '\\' */
  662.                                 lexer->bytesReadForToken -= 1;
  663.  
  664.                                 return emitToken(lexer, CSS_TOKEN_CHAR, token);
  665.                         }
  666.  
  667.                         return error;
  668.                 }
  669.  
  670.                 lexer->state = sIDENT;
  671.                 lexer->substate = 0;
  672.                 return IdentOrFunction(lexer, token);
  673.         }
  674.  
  675.         return emitToken(lexer, t->type, token);
  676. }
  677.  
  678. css_error CDO(css_lexer *lexer, css_token **token)
  679. {
  680.         css_token *t = &lexer->token;
  681.         const uint8_t *cptr;
  682.         uint8_t c;
  683.         size_t clen;
  684.         parserutils_error perror;
  685.         enum { Initial = 0, Dash1 = 1, Dash2 = 2 };
  686.  
  687.         /* CDO = "<!--"
  688.          *
  689.          * The '<' has been consumed
  690.          */
  691.  
  692.         switch (lexer->substate) {
  693.         case Initial:
  694.                 /* Expect '!' */
  695.                 perror = parserutils_inputstream_peek(lexer->input,
  696.                                 lexer->bytesReadForToken, &cptr, &clen);
  697.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  698.                         return css_error_from_parserutils_error(perror);
  699.  
  700.                 if (perror == PARSERUTILS_EOF) {
  701.                         /* CHAR is the only match here */
  702.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  703.                 }
  704.  
  705.                 c = *cptr;
  706.  
  707.                 if (c == '!') {
  708.                         APPEND(lexer, cptr, clen);
  709.                 } else {
  710.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  711.                 }
  712.  
  713.                 /* Fall Through */
  714.         case Dash1:
  715.                 lexer->substate = Dash1;
  716.  
  717.                 /* Expect '-' */
  718.                 perror = parserutils_inputstream_peek(lexer->input,
  719.                                 lexer->bytesReadForToken, &cptr, &clen);
  720.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  721.                         return css_error_from_parserutils_error(perror);
  722.  
  723.                 if (perror == PARSERUTILS_EOF) {
  724.                         /* CHAR is the only match here */
  725.                         /* Remove the '!' we read above */
  726.                         lexer->bytesReadForToken -= 1;
  727.                         t->data.len -= 1;
  728.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  729.                 }
  730.  
  731.                 c = *cptr;
  732.  
  733.                 if (c == '-') {
  734.                         APPEND(lexer, cptr, clen);
  735.                 } else {
  736.                         /* Remove the '!' we read above */
  737.                         lexer->bytesReadForToken -= 1;
  738.                         t->data.len -= 1;
  739.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  740.                 }
  741.  
  742.                 /* Fall through */
  743.         case Dash2:
  744.                 lexer->substate = Dash2;
  745.  
  746.                 /* Expect '-' */
  747.                 perror = parserutils_inputstream_peek(lexer->input,
  748.                                 lexer->bytesReadForToken, &cptr, &clen);
  749.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  750.                         return css_error_from_parserutils_error(perror);
  751.  
  752.                 if (perror == PARSERUTILS_EOF) {
  753.                         /* CHAR is the only match here */
  754.                         /* Remove the '-' and the '!' we read above */
  755.                         lexer->bytesReadForToken -= 2;
  756.                         t->data.len -= 2;
  757.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  758.                 }
  759.  
  760.                 c = *cptr;
  761.  
  762.                 if (c == '-') {
  763.                         APPEND(lexer, cptr, clen);
  764.                 } else {
  765.                         /* Remove the '-' and the '!' we read above */
  766.                         lexer->bytesReadForToken -= 2;
  767.                         t->data.len -= 2;
  768.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  769.                 }
  770.         }
  771.  
  772.         return emitToken(lexer, CSS_TOKEN_CDO, token);
  773. }
  774.  
  775. css_error Comment(css_lexer *lexer, css_token **token)
  776. {
  777.         const uint8_t *cptr;
  778.         uint8_t c;
  779.         size_t clen;
  780.         parserutils_error perror;
  781.         enum { Initial = 0, InComment = 1 };
  782.  
  783.         /* COMMENT = '/' '*' [^*]* '*'+ ([^/] [^*]* '*'+)* '/'
  784.          *
  785.          * The '/' has been consumed.
  786.          */
  787.         switch (lexer->substate) {
  788.         case Initial:
  789.                 perror = parserutils_inputstream_peek(lexer->input,
  790.                                 lexer->bytesReadForToken, &cptr, &clen);
  791.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  792.                         return css_error_from_parserutils_error(perror);
  793.  
  794.                 if (perror == PARSERUTILS_EOF)
  795.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  796.  
  797.                 c = *cptr;
  798.  
  799.                 if (c != '*')
  800.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  801.  
  802.                 APPEND(lexer, cptr, clen);
  803.        
  804.                 /* Fall through */
  805.         case InComment:
  806.                 lexer->substate = InComment;
  807.  
  808.                 while (1) {
  809.                         perror = parserutils_inputstream_peek(lexer->input,
  810.                                         lexer->bytesReadForToken, &cptr, &clen);
  811.                         if (perror != PARSERUTILS_OK &&
  812.                                         perror != PARSERUTILS_EOF)
  813.                                 return css_error_from_parserutils_error(perror);
  814.  
  815.                         if (perror == PARSERUTILS_EOF) {
  816.                                 /* As per unterminated strings,
  817.                                  * we ignore unterminated comments. */
  818.                                 return emitToken(lexer, CSS_TOKEN_EOF, token);
  819.                         }
  820.  
  821.                         c = *cptr;
  822.  
  823.                         APPEND(lexer, cptr, clen);
  824.  
  825.                         if (lexer->context.lastWasStar && c == '/')
  826.                                 break;
  827.  
  828.                         lexer->context.lastWasStar = (c == '*');
  829.  
  830.                         if (c == '\n' || c == '\f') {
  831.                                 lexer->currentCol = 1;
  832.                                 lexer->currentLine++;
  833.                         }
  834.  
  835.                         if (lexer->context.lastWasCR && c != '\n') {
  836.                                 lexer->currentCol = 1;
  837.                                 lexer->currentLine++;
  838.                         }
  839.                         lexer->context.lastWasCR = (c == '\r');
  840.                 }
  841.         }
  842.  
  843.         return emitToken(lexer, CSS_TOKEN_COMMENT, token);
  844. }
  845.  
  846. css_error EscapedIdentOrFunction(css_lexer *lexer, css_token **token)
  847. {
  848.         css_error error;
  849.  
  850.         /* IDENT = ident = [-]? nmstart nmchar*
  851.          * FUNCTION = ident '(' = [-]? nmstart nmchar* '('
  852.          *
  853.          * In this case, nmstart is an escape sequence and no '-' is present.
  854.          *
  855.          * The '\\' has been consumed.
  856.          */
  857.  
  858.         error = consumeEscape(lexer, false);
  859.         if (error != CSS_OK) {
  860.                 if (error == CSS_EOF || error == CSS_INVALID) {
  861.                         /* The '\\' is a token of its own */
  862.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  863.                 }
  864.  
  865.                 return error;
  866.         }
  867.  
  868.         lexer->state = sIDENT;
  869.         lexer->substate = 0;
  870.         return IdentOrFunction(lexer, token);
  871. }
  872.  
  873. css_error Hash(css_lexer *lexer, css_token **token)
  874. {      
  875.         css_error error;
  876.        
  877.         /* HASH = '#' name  = '#' nmchar+
  878.          *
  879.          * The '#' has been consumed.
  880.          */
  881.  
  882.         error = consumeNMChars(lexer);
  883.         if (error != CSS_OK)
  884.                 return error;
  885.  
  886.         /* Require at least one NMChar otherwise, we're just a raw '#' */
  887.         if (lexer->bytesReadForToken - lexer->context.origBytes > 0)
  888.                 return emitToken(lexer, CSS_TOKEN_HASH, token);
  889.  
  890.         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  891. }
  892.  
  893. css_error IdentOrFunction(css_lexer *lexer, css_token **token)
  894. {
  895.         css_token *t = &lexer->token;
  896.         const uint8_t *cptr;
  897.         uint8_t c;
  898.         size_t clen;
  899.         css_error error;
  900.         parserutils_error perror;
  901.         enum { Initial = 0, Bracket = 1 };
  902.  
  903.         /* IDENT = ident = [-]? nmstart nmchar*
  904.          * FUNCTION = ident '(' = [-]? nmstart nmchar* '('
  905.          *
  906.          * The optional dash and nmstart are already consumed
  907.          */
  908.  
  909.         switch (lexer->substate) {
  910.         case Initial:
  911.                 /* Consume all subsequent nmchars (if any exist) */
  912.                 error = consumeNMChars(lexer);
  913.                 if (error != CSS_OK)
  914.                         return error;
  915.  
  916.                 /* Fall through */
  917.         case Bracket:
  918.                 lexer->substate = Bracket;
  919.  
  920.                 perror = parserutils_inputstream_peek(lexer->input,
  921.                                 lexer->bytesReadForToken, &cptr, &clen);
  922.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  923.                         return css_error_from_parserutils_error(perror);
  924.  
  925.                 if (perror == PARSERUTILS_EOF) {
  926.                         /* IDENT, rather than CHAR */
  927.                         return emitToken(lexer, CSS_TOKEN_IDENT, token);
  928.                 }
  929.  
  930.                 c = *cptr;
  931.  
  932.                 if (c == '(') {
  933.                         APPEND(lexer, cptr, clen);
  934.  
  935.                         t->type = CSS_TOKEN_FUNCTION;
  936.                 } else {
  937.                         t->type = CSS_TOKEN_IDENT;
  938.                 }
  939.         }
  940.  
  941.         return emitToken(lexer, t->type, token);
  942. }
  943.  
  944. css_error Match(css_lexer *lexer, css_token **token)
  945. {
  946.         const uint8_t *cptr;
  947.         uint8_t c;
  948.         size_t clen;
  949.         parserutils_error perror;
  950.         css_token_type type = CSS_TOKEN_EOF; /* GCC's braindead */
  951.  
  952.         /* INCLUDES       = "~="
  953.          * DASHMATCH      = "|="
  954.          * PREFIXMATCH    = "^="
  955.          * SUFFIXMATCH    = "$="
  956.          * SUBSTRINGMATCH = "*="
  957.          *
  958.          * The first character has been consumed.
  959.          */
  960.  
  961.         perror = parserutils_inputstream_peek(lexer->input,
  962.                         lexer->bytesReadForToken, &cptr, &clen);
  963.         if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  964.                 return css_error_from_parserutils_error(perror);
  965.  
  966.         if (perror == PARSERUTILS_EOF)
  967.                 return emitToken(lexer, CSS_TOKEN_CHAR, token);
  968.  
  969.         c = *cptr;
  970.  
  971.         if (c != '=')
  972.                 return emitToken(lexer, CSS_TOKEN_CHAR, token);
  973.  
  974.         APPEND(lexer, cptr, clen);
  975.  
  976.         switch (lexer->context.first) {
  977.         case '~':
  978.                 type = CSS_TOKEN_INCLUDES;
  979.                 break;
  980.         case '|':
  981.                 type = CSS_TOKEN_DASHMATCH;
  982.                 break;
  983.         case '^':
  984.                 type = CSS_TOKEN_PREFIXMATCH;
  985.                 break;
  986.         case '$':
  987.                 type = CSS_TOKEN_SUFFIXMATCH;
  988.                 break;
  989.         case '*':
  990.                 type = CSS_TOKEN_SUBSTRINGMATCH;
  991.                 break;
  992.         default:
  993.                 assert(0);
  994.         }
  995.  
  996.         return emitToken(lexer, type, token);
  997. }
  998.  
  999. css_error NumberOrPercentageOrDimension(css_lexer *lexer, css_token **token)
  1000. {
  1001.         css_token *t = &lexer->token;
  1002.         const uint8_t *cptr;
  1003.         uint8_t c;
  1004.         size_t clen;
  1005.         css_error error;
  1006.         parserutils_error perror;
  1007.         enum { Initial = 0, Dot = 1, MoreDigits = 2,
  1008.                 Suffix = 3, NMChars = 4, Escape = 5 };
  1009.  
  1010.         /* NUMBER = num = [-+]? ([0-9]+ | [0-9]* '.' [0-9]+)
  1011.          * PERCENTAGE = num '%'
  1012.          * DIMENSION = num ident
  1013.          *
  1014.          * The sign, or sign and first digit or dot,
  1015.          * or first digit, or '.' has been consumed.
  1016.          */
  1017.  
  1018.         switch (lexer->substate) {
  1019.         case Initial:
  1020.                 error = consumeDigits(lexer);
  1021.                 if (error != CSS_OK)
  1022.                         return error;
  1023.  
  1024.                 /* Fall through */
  1025.         case Dot:
  1026.                 lexer->substate = Dot;
  1027.  
  1028.                 perror = parserutils_inputstream_peek(lexer->input,
  1029.                                 lexer->bytesReadForToken, &cptr, &clen);
  1030.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1031.                         return css_error_from_parserutils_error(perror);
  1032.  
  1033.                 if (perror == PARSERUTILS_EOF) {
  1034.                         if (t->data.len == 1 && (lexer->context.first == '.' ||
  1035.                                         lexer->context.first == '+'))
  1036.                                 return emitToken(lexer, CSS_TOKEN_CHAR, token);
  1037.                         else
  1038.                                 return emitToken(lexer, CSS_TOKEN_NUMBER,
  1039.                                                 token);
  1040.                 }
  1041.  
  1042.                 c = *cptr;
  1043.  
  1044.                 /* Bail if we've not got a '.' or we've seen one already */
  1045.                 if (c != '.' || lexer->context.first == '.')
  1046.                         goto suffix;
  1047.  
  1048.                 /* Save the token length up to the end of the digits */
  1049.                 lexer->context.origBytes = lexer->bytesReadForToken;
  1050.  
  1051.                 /* Append the '.' to the token */
  1052.                 APPEND(lexer, cptr, clen);
  1053.  
  1054.                 /* Fall through */
  1055.         case MoreDigits:
  1056.                 lexer->substate = MoreDigits;
  1057.  
  1058.                 error = consumeDigits(lexer);
  1059.                 if (error != CSS_OK)
  1060.                         return error;
  1061.  
  1062.                 if (lexer->bytesReadForToken - lexer->context.origBytes == 1) {
  1063.                         /* No digits after dot => dot isn't part of number */
  1064.                         lexer->bytesReadForToken -= 1;
  1065.                         t->data.len -= 1;
  1066.                 }
  1067.  
  1068.                 /* Fall through */
  1069.         case Suffix:
  1070.         suffix:
  1071.                 lexer->substate = Suffix;
  1072.  
  1073.                 perror = parserutils_inputstream_peek(lexer->input,
  1074.                                 lexer->bytesReadForToken, &cptr, &clen);
  1075.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1076.                         return css_error_from_parserutils_error(perror);
  1077.  
  1078.                 if (perror == PARSERUTILS_EOF) {
  1079.                         if (t->data.len == 1 && (lexer->context.first == '.' ||
  1080.                                         lexer->context.first == '+'))
  1081.                                 return emitToken(lexer, CSS_TOKEN_CHAR, token);
  1082.                         else
  1083.                                 return emitToken(lexer, CSS_TOKEN_NUMBER,
  1084.                                                 token);
  1085.                 }
  1086.  
  1087.                 c = *cptr;
  1088.  
  1089.                 /* A solitary '.' or '+' is a CHAR, not numeric */
  1090.                 if (t->data.len == 1 && (lexer->context.first == '.' ||
  1091.                                 lexer->context.first == '+'))
  1092.                         return emitToken(lexer, CSS_TOKEN_CHAR, token);
  1093.  
  1094.                 if (c == '%') {
  1095.                         APPEND(lexer, cptr, clen);
  1096.  
  1097.                         return emitToken(lexer, CSS_TOKEN_PERCENTAGE, token);
  1098.                 } else if (!startNMStart(c)) {
  1099.                         return emitToken(lexer, CSS_TOKEN_NUMBER, token);
  1100.                 }
  1101.  
  1102.                 if (c != '\\') {
  1103.                         APPEND(lexer, cptr, clen);
  1104.                 } else {
  1105.                         lexer->bytesReadForToken += clen;
  1106.                         goto escape;
  1107.                 }
  1108.  
  1109.                 /* Fall through */
  1110.         case NMChars:
  1111.         nmchars:
  1112.                 lexer->substate = NMChars;
  1113.  
  1114.                 error = consumeNMChars(lexer);
  1115.                 if (error != CSS_OK)
  1116.                         return error;
  1117.  
  1118.                 break;
  1119.         case Escape:
  1120.         escape:
  1121.                 lexer->substate = Escape;
  1122.  
  1123.                 error = consumeEscape(lexer, false);
  1124.                 if (error != CSS_OK) {
  1125.                         if (error == CSS_EOF || error == CSS_INVALID) {
  1126.                                 /* Rewind the '\\' */
  1127.                                 lexer->bytesReadForToken -= 1;
  1128.  
  1129.                                 /* This can only be a number */
  1130.                                 return emitToken(lexer,
  1131.                                                 CSS_TOKEN_NUMBER, token);
  1132.                         }
  1133.  
  1134.                         return error;
  1135.                 }
  1136.  
  1137.                 goto nmchars;
  1138.         }
  1139.  
  1140.         return emitToken(lexer, CSS_TOKEN_DIMENSION, token);
  1141. }
  1142.  
  1143. css_error S(css_lexer *lexer, css_token **token)
  1144. {
  1145.         css_error error;
  1146.  
  1147.         /* S = wc*
  1148.          *
  1149.          * The first whitespace character has been consumed.
  1150.          */
  1151.  
  1152.         error = consumeWChars(lexer);
  1153.         if (error != CSS_OK)
  1154.                 return error;
  1155.  
  1156.         return emitToken(lexer, CSS_TOKEN_S, token);
  1157. }
  1158.  
  1159. css_error Start(css_lexer *lexer, css_token **token)
  1160. {
  1161.         css_token *t = &lexer->token;
  1162.         const uint8_t *cptr;
  1163.         uint8_t c;
  1164.         size_t clen;
  1165.         css_error error;
  1166.         parserutils_error perror;
  1167.  
  1168. start:
  1169.  
  1170.         /* Advance past the input read for the previous token */
  1171.         if (lexer->bytesReadForToken > 0) {
  1172.                 parserutils_inputstream_advance(
  1173.                                 lexer->input, lexer->bytesReadForToken);
  1174.                 lexer->bytesReadForToken = 0;
  1175.         }
  1176.  
  1177.         /* Reset in preparation for the next token */
  1178.         t->type = CSS_TOKEN_EOF;
  1179.         t->data.data = NULL;
  1180.         t->data.len = 0;
  1181.         t->idata = NULL;
  1182.         t->col = lexer->currentCol;
  1183.         t->line = lexer->currentLine;
  1184.         lexer->escapeSeen = false;
  1185.         if (lexer->unescapedTokenData != NULL)
  1186.                 lexer->unescapedTokenData->length = 0;
  1187.  
  1188.         perror = parserutils_inputstream_peek(lexer->input, 0, &cptr, &clen);
  1189.         if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1190.                 return css_error_from_parserutils_error(perror);
  1191.  
  1192.         if (perror == PARSERUTILS_EOF)
  1193.                 return emitToken(lexer, CSS_TOKEN_EOF, token);
  1194.  
  1195.         APPEND(lexer, cptr, clen);
  1196.  
  1197.         c = *cptr;
  1198.  
  1199.         if (clen > 1 || c >= 0x80) {
  1200.                 lexer->state = sIDENT;
  1201.                 lexer->substate = 0;
  1202.                 return IdentOrFunction(lexer, token);
  1203.         }
  1204.  
  1205.         switch (c) {
  1206.         case '@':
  1207.                 lexer->state = sATKEYWORD;
  1208.                 lexer->substate = 0;
  1209.                 return AtKeyword(lexer, token);
  1210.         case '"': case '\'':
  1211.                 lexer->state = sSTRING;
  1212.                 lexer->substate = 0;
  1213.                 lexer->context.first = c;
  1214.                 return String(lexer, token);
  1215.         case '#':
  1216.                 lexer->state = sHASH;
  1217.                 lexer->substate = 0;
  1218.                 lexer->context.origBytes = lexer->bytesReadForToken;
  1219.                 return Hash(lexer, token);
  1220.         case '0': case '1': case '2': case '3': case '4':
  1221.         case '5': case '6': case '7': case '8': case '9':
  1222.         case '.': case '+':
  1223.                 lexer->state = sNUMBER;
  1224.                 lexer->substate = 0;
  1225.                 lexer->context.first = c;
  1226.                 return NumberOrPercentageOrDimension(lexer, token);
  1227.         case '<':
  1228.                 lexer->state = sCDO;
  1229.                 lexer->substate = 0;
  1230.                 return CDO(lexer, token);
  1231.         case '-':
  1232.                 lexer->state = sCDC;
  1233.                 lexer->substate = 0;
  1234.                 return CDCOrIdentOrFunctionOrNPD(lexer, token);
  1235.         case ' ': case '\t': case '\r': case '\n': case '\f':
  1236.                 lexer->state = sS;
  1237.                 lexer->substate = 0;
  1238.                 if (c == '\n' || c == '\f') {
  1239.                         lexer->currentCol = 1;
  1240.                         lexer->currentLine++;
  1241.                 }
  1242.                 lexer->context.lastWasCR = (c == '\r');
  1243.                 return S(lexer, token);
  1244.         case '/':
  1245.                 lexer->state = sCOMMENT;
  1246.                 lexer->substate = 0;
  1247.                 lexer->context.lastWasStar = false;
  1248.                 lexer->context.lastWasCR = false;
  1249.                 error = Comment(lexer, token);
  1250.                 if (!lexer->emit_comments && error == CSS_OK &&
  1251.                                 (*token)->type == CSS_TOKEN_COMMENT)
  1252.                         goto start;
  1253.                 return error;
  1254.         case '~': case '|': case '^': case '$': case '*':
  1255.                 lexer->state = sMATCH;
  1256.                 lexer->substate = 0;
  1257.                 lexer->context.first = c;
  1258.                 return Match(lexer, token);
  1259.         case 'u': case 'U':
  1260.                 lexer->state = sURI;
  1261.                 lexer->substate = 0;
  1262.                 return URIOrUnicodeRangeOrIdentOrFunction(lexer, token);
  1263.         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  1264.         case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
  1265.         case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
  1266.         case 's': case 't': /*  'u'*/ case 'v': case 'w': case 'x':
  1267.         case 'y': case 'z':
  1268.         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  1269.         case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
  1270.         case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
  1271.         case 'S': case 'T': /*  'U'*/ case 'V': case 'W': case 'X':
  1272.         case 'Y': case 'Z':
  1273.         case '_':
  1274.                 lexer->state = sIDENT;
  1275.                 lexer->substate = 0;
  1276.                 return IdentOrFunction(lexer, token);
  1277.         case '\\':
  1278.                 lexer->state = sESCAPEDIDENT;
  1279.                 lexer->substate = 0;
  1280.                 return EscapedIdentOrFunction(lexer, token);
  1281.         default:
  1282.                 return emitToken(lexer, CSS_TOKEN_CHAR, token);
  1283.         }
  1284. }
  1285.  
  1286. css_error String(css_lexer *lexer, css_token **token)
  1287. {
  1288.         css_error error;
  1289.  
  1290.         /* STRING = string
  1291.          *
  1292.          * The open quote has been consumed.
  1293.          */
  1294.  
  1295.         error = consumeString(lexer);
  1296.         if (error != CSS_OK && error != CSS_EOF && error != CSS_INVALID)
  1297.                 return error;
  1298.  
  1299.         /* EOF will be reprocessed in Start() */
  1300.         return emitToken(lexer,
  1301.                         error == CSS_INVALID ? CSS_TOKEN_INVALID_STRING
  1302.                                              : CSS_TOKEN_STRING,
  1303.                         token);
  1304. }
  1305.  
  1306. css_error URIOrUnicodeRangeOrIdentOrFunction(css_lexer *lexer,
  1307.                 css_token **token)
  1308. {
  1309.         const uint8_t *cptr;
  1310.         uint8_t c;
  1311.         size_t clen;
  1312.         parserutils_error perror;
  1313.  
  1314.         /* URI = "url(" w (string | urlchar*) w ')'
  1315.          * UNICODE-RANGE = [Uu] '+' [0-9a-fA-F?]{1,6}(-[0-9a-fA-F]{1,6})?
  1316.          * IDENT = ident = [-]? nmstart nmchar*
  1317.          * FUNCTION = ident '(' = [-]? nmstart nmchar* '('
  1318.          *
  1319.          * The 'u' (or 'U') has been consumed.
  1320.          */
  1321.  
  1322.         perror = parserutils_inputstream_peek(lexer->input,
  1323.                         lexer->bytesReadForToken, &cptr, &clen);
  1324.         if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1325.                 return css_error_from_parserutils_error(perror);
  1326.  
  1327.         if (perror == PARSERUTILS_EOF) {
  1328.                 /* IDENT, rather than CHAR */
  1329.                 return emitToken(lexer, CSS_TOKEN_IDENT, token);
  1330.         }
  1331.  
  1332.         c = *cptr;
  1333.  
  1334.         if (c == 'r' || c == 'R') {
  1335.                 APPEND(lexer, cptr, clen);
  1336.  
  1337.                 lexer->state = sURL;
  1338.                 lexer->substate = 0;
  1339.                 return URI(lexer, token);
  1340.         } else if (c == '+') {
  1341.                 APPEND(lexer, cptr, clen);
  1342.  
  1343.                 lexer->state = sUCR;
  1344.                 lexer->substate = 0;
  1345.                 lexer->context.hexCount = 0;
  1346.                 return UnicodeRange(lexer, token);
  1347.         }
  1348.  
  1349.         /* Can only be IDENT or FUNCTION. Reprocess current character */
  1350.         lexer->state = sIDENT;
  1351.         lexer->substate = 0;
  1352.         return IdentOrFunction(lexer, token);
  1353. }
  1354.  
  1355. css_error URI(css_lexer *lexer, css_token **token)
  1356. {
  1357.         const uint8_t *cptr;
  1358.         uint8_t c;
  1359.         size_t clen;
  1360.         css_error error;
  1361.         parserutils_error perror;
  1362.         enum { Initial = 0, LParen = 1, W1 = 2, Quote = 3,
  1363.                 URL = 4, W2 = 5, RParen = 6, String = 7 };
  1364.  
  1365.         /* URI = "url(" w (string | urlchar*) w ')'
  1366.          *
  1367.          * 'u' and 'r' have been consumed.
  1368.          */
  1369.  
  1370.         switch (lexer->substate) {
  1371.         case Initial:
  1372.                 perror = parserutils_inputstream_peek(lexer->input,
  1373.                                 lexer->bytesReadForToken, &cptr, &clen);
  1374.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1375.                         return css_error_from_parserutils_error(perror);
  1376.  
  1377.                 if (perror == PARSERUTILS_EOF) {
  1378.                         /* IDENT */
  1379.                         return emitToken(lexer, CSS_TOKEN_IDENT, token);
  1380.                 }
  1381.  
  1382.                 c = *cptr;
  1383.  
  1384.                 if (c == 'l' || c == 'L') {
  1385.                         APPEND(lexer, cptr, clen);
  1386.                 } else {
  1387.                         lexer->state = sIDENT;
  1388.                         lexer->substate = 0;
  1389.                         return IdentOrFunction(lexer, token);
  1390.                 }
  1391.  
  1392.                 /* Fall through */
  1393.         case LParen:
  1394.                 lexer->substate = LParen;
  1395.  
  1396.                 perror = parserutils_inputstream_peek(lexer->input,
  1397.                                 lexer->bytesReadForToken, &cptr, &clen);
  1398.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1399.                         return css_error_from_parserutils_error(perror);
  1400.  
  1401.                 if (perror == PARSERUTILS_EOF)
  1402.                         return emitToken(lexer, CSS_TOKEN_IDENT, token);
  1403.  
  1404.                 c = *cptr;
  1405.  
  1406.                 if (c == '(') {
  1407.                         APPEND(lexer, cptr, clen);
  1408.                 } else {
  1409.                         lexer->state = sIDENT;
  1410.                         lexer->substate = 0;
  1411.                         return IdentOrFunction(lexer, token);
  1412.                 }
  1413.  
  1414.                 /* Save the number of input bytes read for "url(" */
  1415.                 lexer->context.bytesForURL = lexer->bytesReadForToken;
  1416.                 /* And the length of the token data at the same point */
  1417.                 lexer->context.dataLenForURL = lexer->token.data.len;
  1418.  
  1419.                 lexer->context.lastWasCR = false;
  1420.  
  1421.                 /* Fall through */
  1422.         case W1:
  1423.                 lexer->substate = W1;
  1424.  
  1425.                 error = consumeWChars(lexer);
  1426.                 if (error != CSS_OK)
  1427.                         return error;
  1428.  
  1429.                 /* Fall through */
  1430.         case Quote:
  1431.                 lexer->substate = Quote;
  1432.  
  1433.                 perror = parserutils_inputstream_peek(lexer->input,
  1434.                                 lexer->bytesReadForToken, &cptr, &clen);
  1435.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1436.                         return css_error_from_parserutils_error(perror);
  1437.  
  1438.                 if (perror == PARSERUTILS_EOF) {
  1439.                         /* Rewind to "url(" */
  1440.                         lexer->bytesReadForToken = lexer->context.bytesForURL;
  1441.                         lexer->token.data.len = lexer->context.dataLenForURL;
  1442.                         return emitToken(lexer, CSS_TOKEN_FUNCTION, token);
  1443.                 }
  1444.  
  1445.                 c = *cptr;
  1446.  
  1447.                 if (c == '"' || c == '\'') {
  1448.                         APPEND(lexer, cptr, clen);
  1449.  
  1450.                         lexer->context.first = c;
  1451.  
  1452.                         goto string;
  1453.                 }
  1454.  
  1455.                 /* Potential minor optimisation: If string is more common,
  1456.                  * then fall through to that state and branch for the URL
  1457.                  * state. Need to investigate a reasonably large corpus of
  1458.                  * real-world data to determine if this is worthwhile. */
  1459.  
  1460.                 /* Fall through */
  1461.         case URL:
  1462.                 lexer->substate = URL;
  1463.  
  1464.                 error = consumeURLChars(lexer);
  1465.                 if (error != CSS_OK)
  1466.                         return error;
  1467.  
  1468.                 lexer->context.lastWasCR = false;
  1469.  
  1470.                 /* Fall through */
  1471.         case W2:
  1472.         w2:
  1473.                 lexer->substate = W2;
  1474.  
  1475.                 error = consumeWChars(lexer);
  1476.                 if (error != CSS_OK)
  1477.                         return error;
  1478.  
  1479.                 /* Fall through */
  1480.         case RParen:
  1481.                 lexer->substate = RParen;
  1482.  
  1483.                 perror = parserutils_inputstream_peek(lexer->input,
  1484.                                 lexer->bytesReadForToken, &cptr, &clen);
  1485.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1486.                         return css_error_from_parserutils_error(perror);
  1487.  
  1488.                 if (perror == PARSERUTILS_EOF) {
  1489.                         /* Rewind to "url(" */
  1490.                         lexer->bytesReadForToken = lexer->context.bytesForURL;
  1491.                         lexer->token.data.len = lexer->context.dataLenForURL;
  1492.                         return emitToken(lexer, CSS_TOKEN_FUNCTION, token);
  1493.                 }
  1494.  
  1495.                 c = *cptr;
  1496.  
  1497.                 if (c != ')') {
  1498.                         /* Rewind to "url(" */
  1499.                         lexer->bytesReadForToken = lexer->context.bytesForURL;
  1500.                         lexer->token.data.len = lexer->context.dataLenForURL;
  1501.                         return emitToken(lexer, CSS_TOKEN_FUNCTION, token);
  1502.                 }
  1503.  
  1504.                 APPEND(lexer, cptr, clen);
  1505.                 break;
  1506.         case String:
  1507.         string:
  1508.                 lexer->substate = String;
  1509.  
  1510.                 error = consumeString(lexer);
  1511.                 if (error == CSS_INVALID) {
  1512.                         /* Rewind to "url(" */
  1513.                         lexer->bytesReadForToken = lexer->context.bytesForURL;
  1514.                         lexer->token.data.len = lexer->context.dataLenForURL;
  1515.                         return emitToken(lexer, CSS_TOKEN_FUNCTION, token);
  1516.                 } else if (error != CSS_OK && error != CSS_EOF) {
  1517.                         return error;
  1518.                 }
  1519.  
  1520.                 /* EOF gets handled in RParen */
  1521.  
  1522.                 lexer->context.lastWasCR = false;
  1523.  
  1524.                 goto w2;
  1525.         }
  1526.  
  1527.         return emitToken(lexer, CSS_TOKEN_URI, token);
  1528. }
  1529.  
  1530. css_error UnicodeRange(css_lexer *lexer, css_token **token)
  1531. {
  1532.         css_token *t = &lexer->token;
  1533.         const uint8_t *cptr;
  1534.         uint8_t c = 0; /* GCC: shush */
  1535.         size_t clen;
  1536.         parserutils_error perror;
  1537.         enum { Initial = 0, MoreDigits = 1 };
  1538.  
  1539.         /* UNICODE-RANGE = [Uu] '+' [0-9a-fA-F?]{1,6}(-[0-9a-fA-F]{1,6})?
  1540.          *
  1541.          * "U+" has been consumed.
  1542.          */
  1543.  
  1544.         switch (lexer->substate) {
  1545.         case Initial:
  1546.                 /* Attempt to consume 6 hex digits (or question marks) */
  1547.                 for (; lexer->context.hexCount < 6; lexer->context.hexCount++) {
  1548.                         perror = parserutils_inputstream_peek(lexer->input,
  1549.                                         lexer->bytesReadForToken, &cptr, &clen);
  1550.                         if (perror != PARSERUTILS_OK &&
  1551.                                         perror != PARSERUTILS_EOF)
  1552.                                 return css_error_from_parserutils_error(perror);
  1553.  
  1554.                         if (perror == PARSERUTILS_EOF) {
  1555.                                 if (lexer->context.hexCount == 0) {
  1556.                                         /* Remove '+' */
  1557.                                         lexer->bytesReadForToken -= 1;
  1558.                                         t->data.len -= 1;
  1559.                                         /* u == IDENT */
  1560.                                         return emitToken(lexer,
  1561.                                                         CSS_TOKEN_IDENT, token);
  1562.                                 } else {
  1563.                                         return emitToken(lexer,
  1564.                                                 CSS_TOKEN_UNICODE_RANGE, token);
  1565.                                 }
  1566.                         }
  1567.  
  1568.                         c = *cptr;
  1569.  
  1570.                         if (isHex(c) || c == '?') {
  1571.                                 APPEND(lexer, cptr, clen);
  1572.                         } else {
  1573.                                 break;
  1574.                         }
  1575.                 }
  1576.  
  1577.                 if (lexer->context.hexCount == 0) {
  1578.                         /* We didn't consume any valid Unicode Range digits */
  1579.                         /* Remove the '+' */
  1580.                         lexer->bytesReadForToken -= 1;
  1581.                         t->data.len -= 1;
  1582.                         /* 'u' == IDENT */
  1583.                         return emitToken(lexer, CSS_TOKEN_IDENT, token);
  1584.                 }
  1585.  
  1586.                 if (lexer->context.hexCount == 6) {
  1587.                         /* Consumed 6 valid characters. Look for '-' */
  1588.                         perror = parserutils_inputstream_peek(lexer->input,
  1589.                                         lexer->bytesReadForToken, &cptr, &clen);
  1590.                         if (perror != PARSERUTILS_OK &&
  1591.                                         perror != PARSERUTILS_EOF)
  1592.                                 return css_error_from_parserutils_error(perror);
  1593.  
  1594.                         if (perror == PARSERUTILS_EOF)
  1595.                                 return emitToken(lexer,
  1596.                                                 CSS_TOKEN_UNICODE_RANGE, token);
  1597.  
  1598.                         c = *cptr;
  1599.                 }
  1600.  
  1601.                 /* If we've got a '-', then we may have a
  1602.                  * second range component */
  1603.                 if (c != '-') {
  1604.                         /* Reached the end of the range */
  1605.                         return emitToken(lexer, CSS_TOKEN_UNICODE_RANGE, token);
  1606.                 }
  1607.  
  1608.                 APPEND(lexer, cptr, clen);
  1609.  
  1610.                 /* Reset count for next set of digits */
  1611.                 lexer->context.hexCount = 0;
  1612.  
  1613.                 /* Fall through */
  1614.         case MoreDigits:
  1615.                 lexer->substate = MoreDigits;
  1616.  
  1617.                 /* Consume up to 6 hex digits */
  1618.                 for (; lexer->context.hexCount < 6; lexer->context.hexCount++) {
  1619.                         perror = parserutils_inputstream_peek(lexer->input,
  1620.                                         lexer->bytesReadForToken, &cptr, &clen);
  1621.                         if (perror != PARSERUTILS_OK &&
  1622.                                         perror != PARSERUTILS_EOF)
  1623.                                 return css_error_from_parserutils_error(perror);
  1624.  
  1625.                         if (perror == PARSERUTILS_EOF) {
  1626.                                 if (lexer->context.hexCount == 0) {
  1627.                                         /* Remove '-' */
  1628.                                         lexer->bytesReadForToken -= 1;
  1629.                                         t->data.len -= 1;
  1630.                                 }
  1631.  
  1632.                                 return emitToken(lexer,
  1633.                                                 CSS_TOKEN_UNICODE_RANGE, token);
  1634.                         }
  1635.  
  1636.                         c = *cptr;
  1637.  
  1638.                         if (isHex(c)) {
  1639.                                 APPEND(lexer, cptr, clen);
  1640.                         } else {
  1641.                                 break;
  1642.                         }
  1643.                 }
  1644.  
  1645.                 if (lexer->context.hexCount == 0) {
  1646.                         /* No hex digits consumed. Remove '-' */
  1647.                         lexer->bytesReadForToken -= 1;
  1648.                         t->data.len -= 1;
  1649.                 }
  1650.         }
  1651.  
  1652.         return emitToken(lexer, CSS_TOKEN_UNICODE_RANGE, token);
  1653. }
  1654.  
  1655. /******************************************************************************
  1656.  * Input consumers                                                            *
  1657.  ******************************************************************************/
  1658.  
  1659. css_error consumeDigits(css_lexer *lexer)
  1660. {
  1661.         const uint8_t *cptr;
  1662.         uint8_t c;
  1663.         size_t clen;
  1664.         parserutils_error perror;
  1665.  
  1666.         /* digit = [0-9] */
  1667.  
  1668.         /* Consume all digits */
  1669.         do {
  1670.                 perror = parserutils_inputstream_peek(lexer->input,
  1671.                                 lexer->bytesReadForToken, &cptr, &clen);
  1672.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1673.                         return css_error_from_parserutils_error(perror);
  1674.  
  1675.                 if (perror == PARSERUTILS_EOF)
  1676.                         return CSS_OK;
  1677.  
  1678.                 c = *cptr;
  1679.  
  1680.                 if (isDigit(c)) {
  1681.                         APPEND(lexer, cptr, clen);
  1682.                 }
  1683.         } while (isDigit(c));
  1684.  
  1685.         return CSS_OK;
  1686. }
  1687.  
  1688. css_error consumeEscape(css_lexer *lexer, bool nl)
  1689. {
  1690.         const uint8_t *cptr, *sdata;
  1691.         uint8_t c;
  1692.         size_t clen, slen;
  1693.         css_error error;
  1694.         parserutils_error perror;
  1695.  
  1696.         /* escape = unicode | '\' [^\n\r\f0-9a-fA-F]
  1697.          *
  1698.          * The '\' has been consumed.
  1699.          */
  1700.  
  1701.         perror = parserutils_inputstream_peek(lexer->input,
  1702.                         lexer->bytesReadForToken, &cptr, &clen);
  1703.         if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1704.                 return css_error_from_parserutils_error(perror);
  1705.  
  1706.         if (perror == PARSERUTILS_EOF)
  1707.                 return CSS_EOF;
  1708.  
  1709.         c = *cptr;
  1710.  
  1711.         if (!nl && (c == '\n' || c == '\r' || c == '\f')) {
  1712.                 /* These are not permitted */
  1713.                 return CSS_INVALID;
  1714.         }
  1715.  
  1716.         /* Create unescaped buffer, if it doesn't already exist */
  1717.         if (lexer->unescapedTokenData == NULL) {
  1718.                 perror = parserutils_buffer_create(lexer->alloc, lexer->pw,
  1719.                                 &lexer->unescapedTokenData);
  1720.                 if (perror != PARSERUTILS_OK)
  1721.                         return css_error_from_parserutils_error(perror);
  1722.         }
  1723.  
  1724.         /* If this is the first escaped character we've seen for this token,
  1725.          * we must copy the characters we've read to the unescaped buffer */
  1726.         if (!lexer->escapeSeen) {
  1727.                 if (lexer->bytesReadForToken > 1) {
  1728.                         perror = parserutils_inputstream_peek(
  1729.                                         lexer->input, 0, &sdata, &slen);
  1730.  
  1731.                         assert(perror == PARSERUTILS_OK);
  1732.  
  1733.                         /* -1 to skip '\\' */
  1734.                         perror = parserutils_buffer_append(
  1735.                                         lexer->unescapedTokenData,
  1736.                                         sdata, lexer->bytesReadForToken - 1);
  1737.                         if (perror != PARSERUTILS_OK)
  1738.                                 return css_error_from_parserutils_error(perror);
  1739.                 }
  1740.  
  1741.                 lexer->token.data.len = lexer->bytesReadForToken - 1;
  1742.                 lexer->escapeSeen = true;
  1743.         }
  1744.  
  1745.         if (isHex(c)) {
  1746.                 lexer->bytesReadForToken += clen;
  1747.  
  1748.                 error = consumeUnicode(lexer, charToHex(c));
  1749.                 if (error != CSS_OK) {
  1750.                         /* Rewind for next time */
  1751.                         lexer->bytesReadForToken -= clen;
  1752.                 }
  1753.  
  1754.                 return error;
  1755.         }
  1756.  
  1757.         /* If we're handling escaped newlines, convert CR(LF)? to LF */
  1758.         if (nl && c == '\r') {
  1759.                 perror = parserutils_inputstream_peek(lexer->input,
  1760.                                 lexer->bytesReadForToken + clen, &cptr, &clen);
  1761.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1762.                         return css_error_from_parserutils_error(perror);
  1763.  
  1764.                 if (perror == PARSERUTILS_EOF) {
  1765.                         c = '\n';
  1766.                         APPEND(lexer, &c, 1);
  1767.  
  1768.                         lexer->currentCol = 1;
  1769.                         lexer->currentLine++;
  1770.  
  1771.                         return CSS_OK;
  1772.                 }
  1773.  
  1774.                 c = *cptr;
  1775.  
  1776.                 if (c == '\n') {
  1777.                         APPEND(lexer, &c, 1);
  1778.                         /* And skip the '\r' in the input */
  1779.                         lexer->bytesReadForToken += clen;
  1780.  
  1781.                         lexer->currentCol = 1;
  1782.                         lexer->currentLine++;
  1783.  
  1784.                         return CSS_OK;
  1785.                 }
  1786.         } else if (nl && (c == '\n' || c == '\f')) {
  1787.                 /* APPEND will increment this appropriately */
  1788.                 lexer->currentCol = 0;
  1789.                 lexer->currentLine++;
  1790.         } else if (c != '\n' && c != '\r' && c != '\f') {
  1791.                 lexer->currentCol++;
  1792.         }
  1793.  
  1794.         /* Append the unescaped character */
  1795.         APPEND(lexer, cptr, clen);
  1796.  
  1797.         return CSS_OK;
  1798. }
  1799.  
  1800. css_error consumeNMChars(css_lexer *lexer)
  1801. {
  1802.         const uint8_t *cptr;
  1803.         uint8_t c;
  1804.         size_t clen;
  1805.         css_error error;
  1806.         parserutils_error perror;
  1807.  
  1808.         /* nmchar = [a-zA-Z] | '-' | '_' | nonascii | escape */
  1809.  
  1810.         do {
  1811.                 perror = parserutils_inputstream_peek(lexer->input,
  1812.                                 lexer->bytesReadForToken, &cptr, &clen);
  1813.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1814.                         return css_error_from_parserutils_error(perror);
  1815.  
  1816.                 if (perror == PARSERUTILS_EOF)
  1817.                         return CSS_OK;
  1818.  
  1819.                 c = *cptr;
  1820.  
  1821.                 if (startNMChar(c) && c != '\\') {
  1822.                         APPEND(lexer, cptr, clen);
  1823.                 }
  1824.  
  1825.                 if (c == '\\') {
  1826.                         lexer->bytesReadForToken += clen;
  1827.  
  1828.                         error = consumeEscape(lexer, false);
  1829.                         if (error != CSS_OK) {
  1830.                                 /* Rewind '\\', so we do the
  1831.                                  * right thing next time */
  1832.                                 lexer->bytesReadForToken -= clen;
  1833.  
  1834.                                 /* Convert either EOF or INVALID into OK.
  1835.                                  * This will cause the caller to believe that
  1836.                                  * all NMChars in the sequence have been
  1837.                                  * processed (and thus proceed to the next
  1838.                                  * state). Eventually, the '\\' will be output
  1839.                                  * as a CHAR. */
  1840.                                 if (error == CSS_EOF || error == CSS_INVALID)
  1841.                                         return CSS_OK;
  1842.  
  1843.                                 return error;
  1844.                         }
  1845.                 }
  1846.         } while (startNMChar(c));
  1847.  
  1848.         return CSS_OK;
  1849. }
  1850.  
  1851. css_error consumeString(css_lexer *lexer)
  1852. {
  1853.         const uint8_t *cptr;
  1854.         uint8_t c;
  1855.         size_t clen;
  1856.         uint8_t quote = lexer->context.first;
  1857.         uint8_t permittedquote = (quote == '"') ? '\'' : '"';
  1858.         css_error error;
  1859.         parserutils_error perror;
  1860.  
  1861.         /* string = '"' (stringchar | "'")* '"' | "'" (stringchar | '"')* "'"
  1862.          *
  1863.          * The open quote has been consumed.
  1864.          */
  1865.  
  1866.         do {
  1867.                 perror = parserutils_inputstream_peek(lexer->input,
  1868.                                 lexer->bytesReadForToken, &cptr, &clen);
  1869.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1870.                         return css_error_from_parserutils_error(perror);
  1871.  
  1872.                 if (perror == PARSERUTILS_EOF)
  1873.                         return CSS_EOF;
  1874.  
  1875.                 c = *cptr;
  1876.  
  1877.                 if (c == permittedquote) {
  1878.                         APPEND(lexer, cptr, clen);
  1879.                 } else if (startStringChar(c)) {
  1880.                         error = consumeStringChars(lexer);
  1881.                         if (error != CSS_OK)
  1882.                                 return error;
  1883.                 } else if (c != quote) {
  1884.                         /* Invalid character in string */
  1885.                         return CSS_INVALID;
  1886.                 }
  1887.         } while(c != quote);
  1888.  
  1889.         /* Append closing quote to token data */
  1890.         APPEND(lexer, cptr, clen);
  1891.  
  1892.         return CSS_OK;
  1893. }
  1894.  
  1895. css_error consumeStringChars(css_lexer *lexer)
  1896. {
  1897.         const uint8_t *cptr;
  1898.         uint8_t c;
  1899.         size_t clen;
  1900.         css_error error;
  1901.         parserutils_error perror;
  1902.  
  1903.         /* stringchar = urlchar | ' ' | ')' | '\' nl */
  1904.  
  1905.         do {
  1906.                 perror = parserutils_inputstream_peek(lexer->input,
  1907.                                 lexer->bytesReadForToken, &cptr, &clen);
  1908.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  1909.                         return css_error_from_parserutils_error(perror);
  1910.  
  1911.                 if (perror == PARSERUTILS_EOF)
  1912.                         return CSS_OK;
  1913.  
  1914.                 c = *cptr;
  1915.  
  1916.                 if (startStringChar(c) && c != '\\') {
  1917.                         APPEND(lexer, cptr, clen);
  1918.                 }
  1919.  
  1920.                 if (c == '\\') {
  1921.                         lexer->bytesReadForToken += clen;
  1922.  
  1923.                         error = consumeEscape(lexer, true);
  1924.                         if (error != CSS_OK) {
  1925.                                 /* Rewind '\\', so we do the
  1926.                                  * right thing next time. */
  1927.                                 lexer->bytesReadForToken -= clen;
  1928.  
  1929.                                 /* Convert EOF to OK. This causes the caller
  1930.                                  * to believe that all StringChars have been
  1931.                                  * processed. Eventually, the '\\' will be
  1932.                                  * output as a CHAR. */
  1933.                                 if (error == CSS_EOF)
  1934.                                         return CSS_OK;
  1935.  
  1936.                                 return error;
  1937.                         }
  1938.                 }
  1939.         } while (startStringChar(c));
  1940.  
  1941.         return CSS_OK;
  1942.  
  1943. }
  1944.  
  1945. css_error consumeUnicode(css_lexer *lexer, uint32_t ucs)
  1946. {
  1947.         const uint8_t *cptr;
  1948.         uint8_t c = 0;
  1949.         size_t clen;
  1950.         uint8_t utf8[6];
  1951.         uint8_t *utf8data = utf8;
  1952.         size_t utf8len = sizeof(utf8);
  1953.         size_t bytesReadInit = lexer->bytesReadForToken;
  1954.         int count;
  1955.         css_error error;
  1956.         parserutils_error perror;
  1957.  
  1958.         /* unicode = '\' [0-9a-fA-F]{1,6} wc?
  1959.          *
  1960.          * The '\' and the first digit have been consumed.
  1961.          */
  1962.  
  1963.         /* Attempt to consume a further five hex digits */
  1964.         for (count = 0; count < 5; count++) {
  1965.                 perror = parserutils_inputstream_peek(lexer->input,
  1966.                                 lexer->bytesReadForToken, &cptr, &clen);
  1967.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF) {
  1968.                         /* Rewind what we've read */
  1969.                         lexer->bytesReadForToken = bytesReadInit;
  1970.                         return css_error_from_parserutils_error(perror);
  1971.                 }
  1972.  
  1973.                 if (perror == PARSERUTILS_EOF)
  1974.                         break;
  1975.  
  1976.                 c = *cptr;
  1977.  
  1978.                 if (isHex(c)) {
  1979.                         lexer->bytesReadForToken += clen;
  1980.  
  1981.                         ucs = (ucs << 4) | charToHex(c);
  1982.                 } else {
  1983.                         break;
  1984.                 }
  1985.         }
  1986.  
  1987.         /* Sanitise UCS4 character */
  1988.         if (ucs > 0x10FFFF || ucs <= 0x0008 || ucs == 0x000B ||
  1989.                         (0x000E <= ucs && ucs <= 0x001F) ||
  1990.                         (0x007F <= ucs && ucs <= 0x009F) ||
  1991.                         (0xD800 <= ucs && ucs <= 0xDFFF) ||
  1992.                         (0xFDD0 <= ucs && ucs <= 0xFDEF) ||
  1993.                         (ucs & 0xFFFE) == 0xFFFE) {
  1994.                 ucs = 0xFFFD;
  1995.         } else if (ucs == 0x000D) {
  1996.                 ucs = 0x000A;
  1997.         }
  1998.  
  1999.         /* Convert our UCS4 character to UTF-8 */
  2000.         perror = parserutils_charset_utf8_from_ucs4(ucs, &utf8data, &utf8len);
  2001.         assert(perror == PARSERUTILS_OK);
  2002.  
  2003.         /* Attempt to read a trailing whitespace character */
  2004.         perror = parserutils_inputstream_peek(lexer->input,
  2005.                         lexer->bytesReadForToken, &cptr, &clen);
  2006.         if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF) {
  2007.                 /* Rewind what we've read */
  2008.                 lexer->bytesReadForToken = bytesReadInit;
  2009.                 return css_error_from_parserutils_error(perror);
  2010.         }
  2011.  
  2012.         if (perror == PARSERUTILS_OK && *cptr == '\r') {
  2013.                 /* Potential CRLF */
  2014.                 const uint8_t *pCR = cptr;
  2015.  
  2016.                 perror = parserutils_inputstream_peek(lexer->input,
  2017.                                 lexer->bytesReadForToken + 1, &cptr, &clen);
  2018.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF) {
  2019.                         /* Rewind what we've read */
  2020.                         lexer->bytesReadForToken = bytesReadInit;
  2021.                         return css_error_from_parserutils_error(perror);
  2022.                 }
  2023.  
  2024.                 if (perror == PARSERUTILS_OK && *cptr == '\n') {
  2025.                         /* CRLF -- account for CR */
  2026.                         lexer->bytesReadForToken += 1;
  2027.                 } else {
  2028.                         /* Stray CR -- restore for later */
  2029.                         cptr = pCR;
  2030.                         clen = 1;
  2031.                         perror = PARSERUTILS_OK;
  2032.                 }
  2033.         }
  2034.  
  2035.         /* Append char. to the token data (unescaped buffer already set up) */
  2036.         /* We can't use the APPEND() macro here as we want to rewind correctly
  2037.          * on error. Additionally, lexer->bytesReadForToken has already been
  2038.          * advanced */
  2039.         error = appendToTokenData(lexer, (const uint8_t *) utf8,
  2040.                         sizeof(utf8) - utf8len);
  2041.         if (error != CSS_OK) {
  2042.                 /* Rewind what we've read */
  2043.                 lexer->bytesReadForToken = bytesReadInit;
  2044.                 return error;
  2045.         }
  2046.  
  2047.         /* Deal with the whitespace character */
  2048.         if (perror == PARSERUTILS_EOF)
  2049.                 return CSS_OK;
  2050.  
  2051.         if (isSpace(*cptr)) {
  2052.                 lexer->bytesReadForToken += clen;
  2053.         }
  2054.  
  2055.         /* Fixup cursor position */
  2056.         if (*cptr == '\r' || *cptr == '\n' || *cptr == '\f') {
  2057.                 lexer->currentCol = 1;
  2058.                 lexer->currentLine++;
  2059.         } else {
  2060.                 /* +2 for '\' and first digit */
  2061.                 lexer->currentCol += lexer->bytesReadForToken -
  2062.                                 bytesReadInit + 2;
  2063.         }
  2064.  
  2065.         return CSS_OK;
  2066. }
  2067.  
  2068. css_error consumeURLChars(css_lexer *lexer)
  2069. {
  2070.         const uint8_t *cptr;
  2071.         uint8_t c;
  2072.         size_t clen;
  2073.         css_error error;
  2074.         parserutils_error perror;
  2075.  
  2076.         /* urlchar = [\t!#-&(*-~] | nonascii | escape */
  2077.  
  2078.         do {
  2079.                 perror = parserutils_inputstream_peek(lexer->input,
  2080.                                 lexer->bytesReadForToken, &cptr, &clen);
  2081.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  2082.                         return css_error_from_parserutils_error(perror);
  2083.  
  2084.                 if (perror == PARSERUTILS_EOF)
  2085.                         return CSS_OK;
  2086.  
  2087.                 c = *cptr;
  2088.  
  2089.                 if (startURLChar(c) && c != '\\') {
  2090.                         APPEND(lexer, cptr, clen);
  2091.                 }
  2092.  
  2093.                 if (c == '\\') {
  2094.                         lexer->bytesReadForToken += clen;
  2095.  
  2096.                         error = consumeEscape(lexer, false);
  2097.                         if (error != CSS_OK) {
  2098.                                 /* Rewind '\\', so we do the
  2099.                                  * right thing next time */
  2100.                                 lexer->bytesReadForToken -= clen;
  2101.  
  2102.                                 /* Convert either EOF or INVALID into OK.
  2103.                                  * This will cause the caller to believe that
  2104.                                  * all URLChars in the sequence have been
  2105.                                  * processed (and thus proceed to the next
  2106.                                  * state). Eventually, the '\\' will be output
  2107.                                  * as a CHAR. */
  2108.                                 if (error == CSS_EOF || error == CSS_INVALID)
  2109.                                         return CSS_OK;
  2110.  
  2111.                                 return error;
  2112.                         }
  2113.                 }
  2114.         } while (startURLChar(c));
  2115.  
  2116.         return CSS_OK;
  2117. }
  2118.  
  2119. css_error consumeWChars(css_lexer *lexer)
  2120. {
  2121.         const uint8_t *cptr;
  2122.         uint8_t c;
  2123.         size_t clen;
  2124.         parserutils_error perror;
  2125.  
  2126.         do {
  2127.                 perror = parserutils_inputstream_peek(lexer->input,
  2128.                                 lexer->bytesReadForToken, &cptr, &clen);
  2129.                 if (perror != PARSERUTILS_OK && perror != PARSERUTILS_EOF)
  2130.                         return css_error_from_parserutils_error(perror);
  2131.  
  2132.                 if (perror == PARSERUTILS_EOF)
  2133.                         return CSS_OK;
  2134.  
  2135.                 c = *cptr;
  2136.  
  2137.                 if (isSpace(c)) {
  2138.                         APPEND(lexer, cptr, clen);
  2139.                 }
  2140.  
  2141.                 if (c == '\n' || c == '\f') {
  2142.                         lexer->currentCol = 1;
  2143.                         lexer->currentLine++;
  2144.                 }
  2145.  
  2146.                 if (lexer->context.lastWasCR && c != '\n') {
  2147.                         lexer->currentCol = 1;
  2148.                         lexer->currentLine++;
  2149.                 }
  2150.                 lexer->context.lastWasCR = (c == '\r');
  2151.         } while (isSpace(c));
  2152.  
  2153.         if (lexer->context.lastWasCR) {
  2154.                 lexer->currentCol = 1;
  2155.                 lexer->currentLine++;
  2156.         }
  2157.  
  2158.         return CSS_OK;
  2159. }
  2160.  
  2161. /******************************************************************************
  2162.  * More utility routines                                                      *
  2163.  ******************************************************************************/
  2164.  
  2165. bool startNMChar(uint8_t c)
  2166. {
  2167.         return c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
  2168.                 ('0' <= c && c <= '9') || c == '-' || c >= 0x80 || c == '\\';
  2169. }
  2170.  
  2171. bool startNMStart(uint8_t c)
  2172. {
  2173.         return c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
  2174.                 c >= 0x80 || c == '\\';
  2175. }
  2176.  
  2177. bool startStringChar(uint8_t c)
  2178. {
  2179.         return startURLChar(c) || c == ' ' || c == ')';
  2180. }
  2181.  
  2182. bool startURLChar(uint8_t c)
  2183. {
  2184.         return c == '\t' || c == '!' || ('#' <= c && c <= '&') || c == '(' ||
  2185.                 ('*' <= c && c <= '~') || c >= 0x80 || c == '\\';
  2186. }
  2187.  
  2188. bool isSpace(uint8_t c)
  2189. {
  2190.         return c == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t';
  2191. }
  2192.  
  2193.