(root)/
gettext-0.22.4/
libtextstyle/
gnulib-local/
lib/
libcroco/
cr-tknzr.c
       1  /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
       2  
       3  /* libcroco - Library for parsing and applying CSS
       4   * Copyright (C) 2006-2019 Free Software Foundation, Inc.
       5   *
       6   * This file is not part of the GNU gettext program, but is used with
       7   * GNU gettext.
       8   *
       9   * The original copyright notice is as follows:
      10   */
      11  
      12  /*
      13   * This file is part of The Croco Library
      14   *
      15   * Copyright (C) 2003-2004 Dodji Seketeli.  All Rights Reserved.
      16   *
      17   * This program is free software; you can redistribute it and/or
      18   * modify it under the terms of version 2.1 of the GNU Lesser General Public
      19   * License as published by the Free Software Foundation.
      20   *
      21   * This program is distributed in the hope that it will be useful,
      22   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      23   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      24   * GNU General Public License for more details.
      25   *
      26   * You should have received a copy of the GNU Lesser General Public License
      27   * along with this program; if not, write to the Free Software
      28   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
      29   * USA
      30   *
      31   * Author: Dodji Seketeli
      32   */
      33  
      34  /**
      35   *@file
      36   *The definition of the #CRTknzr (tokenizer)
      37   *class.
      38   */
      39  
      40  #include <config.h>
      41  #include "string.h"
      42  #include "cr-tknzr.h"
      43  #include "cr-doc-handler.h"
      44  
      45  struct _CRTknzrPriv {
      46          /**The parser input stream of bytes*/
      47          CRInput *input;
      48  
      49          /**
      50           *A cache where tknzr_unget_token()
      51           *puts back the token. tknzr_get_next_token()
      52           *first look in this cache, and if and 
      53           *only if it's empty, fetches the next token
      54           *from the input stream.
      55           */
      56          CRToken *token_cache;
      57  
      58          /**
      59           *The position of the end of the previous token
      60           *or char fetched.
      61           */
      62          CRInputPos prev_pos;
      63  
      64          CRDocHandler *sac_handler;
      65  
      66          /**
      67           *The reference count of the current instance
      68           *of #CRTknzr. Is manipulated by cr_tknzr_ref()
      69           *and cr_tknzr_unref().
      70           */
      71          glong ref_count;
      72  };
      73  
      74  #define PRIVATE(obj) ((obj)->priv)
      75  
      76  /**
      77   *return TRUE if the character is a number ([0-9]), FALSE otherwise
      78   *@param a_char the char to test.
      79   */
      80  #define IS_NUM(a_char) (((a_char) >= '0' && (a_char) <= '9')?TRUE:FALSE)
      81  
      82  /**
      83   *Checks if 'status' equals CR_OK. If not, goto the 'error' label.
      84   *
      85   *@param status the status (of type enum CRStatus) to test.
      86   *@param is_exception if set to FALSE, the final status returned the
      87   *current function will be CR_PARSING_ERROR. If set to TRUE, the
      88   *current status will be the current value of the 'status' variable.
      89   *
      90   */
      91  #define CHECK_PARSING_STATUS(status, is_exception) \
      92  if ((status) != CR_OK) \
      93  { \
      94          if (is_exception == FALSE) \
      95          { \
      96                  status = CR_PARSING_ERROR ; \
      97          } \
      98          goto error ; \
      99  }
     100  
     101  /**
     102   *Peeks the next char from the input stream of the current tokenizer.
     103   *invokes CHECK_PARSING_STATUS on the status returned by
     104   *cr_tknzr_input_peek_char().
     105   *
     106   *@param the current instance of #CRTkzr.
     107   *@param to_char a pointer to the char where to store the
     108   *char peeked.
     109   */
     110  #define PEEK_NEXT_CHAR(a_tknzr, a_to_char) \
     111  {\
     112  status = cr_tknzr_peek_char  (a_tknzr, a_to_char) ; \
     113  CHECK_PARSING_STATUS (status, TRUE) \
     114  }
     115  
     116  /**
     117   *Reads the next char from the input stream of the current parser.
     118   *In case of error, jumps to the "error:" label located in the
     119   *function where this macro is called.
     120   *@param parser the curent instance of #CRTknzr
     121   *@param to_char a pointer to the guint32 char where to store
     122   *the character read.
     123   */
     124  #define READ_NEXT_CHAR(a_tknzr, to_char) \
     125  status = cr_tknzr_read_char (a_tknzr, to_char) ;\
     126  CHECK_PARSING_STATUS (status, TRUE)
     127  
     128  /**
     129   *Gets information about the current position in
     130   *the input of the parser.
     131   *In case of failure, this macro returns from the 
     132   *calling function and
     133   *returns a status code of type enum #CRStatus.
     134   *@param parser the current instance of #CRTknzr.
     135   *@param pos out parameter. A pointer to the position 
     136   *inside the current parser input. Must
     137   */
     138  #define RECORD_INITIAL_POS(a_tknzr, a_pos) \
     139  status = cr_input_get_cur_pos (PRIVATE  \
     140  (a_tknzr)->input, a_pos) ; \
     141  g_return_val_if_fail (status == CR_OK, status)
     142  
     143  /**
     144   *Gets the address of the current byte inside the
     145   *parser input.
     146   *@param parser the current instance of #CRTknzr.
     147   *@param addr out parameter a pointer (guchar*)
     148   *to where the address  must be put.
     149   */
     150  #define RECORD_CUR_BYTE_ADDR(a_tknzr, a_addr) \
     151  status = cr_input_get_cur_byte_addr \
     152              (PRIVATE (a_tknzr)->input, a_addr) ; \
     153  CHECK_PARSING_STATUS (status, TRUE)
     154  
     155  /**
     156   *Peeks a byte from the topmost parser input at
     157   *a given offset from the current position.
     158   *If it fails, goto the "error:" label.
     159   *
     160   *@param a_parser the current instance of #CRTknzr.
     161   *@param a_offset the offset of the byte to peek, the
     162   *current byte having the offset '0'.
     163   *@param a_byte_ptr out parameter a pointer (guchar*) to
     164   *where the peeked char is to be stored.
     165   */
     166  #define PEEK_BYTE(a_tknzr, a_offset, a_byte_ptr) \
     167  status = cr_tknzr_peek_byte (a_tknzr, \
     168                               a_offset, \
     169                               a_byte_ptr) ; \
     170  CHECK_PARSING_STATUS (status, TRUE) ;
     171  
     172  #define BYTE(a_input, a_n, a_eof) \
     173  cr_input_peek_byte2 (a_input, a_n, a_eof)
     174  
     175  /**
     176   *Reads a byte from the topmost parser input
     177   *steam.
     178   *If it fails, goto the "error" label.
     179   *@param a_parser the current instance of #CRTknzr.
     180   *@param a_byte_ptr the guchar * where to put the read char.
     181   */
     182  #define READ_NEXT_BYTE(a_tknzr, a_byte_ptr) \
     183  status = \
     184  cr_input_read_byte (PRIVATE (a_tknzr)->input, a_byte_ptr) ;\
     185  CHECK_PARSING_STATUS (status, TRUE) ;
     186  
     187  /**
     188   *Skips a given number of byte in the topmost
     189   *parser input. Don't update line and column number.
     190   *In case of error, jumps to the "error:" label
     191   *of the surrounding function.
     192   *@param a_parser the current instance of #CRTknzr.
     193   *@param a_nb_bytes the number of bytes to skip.
     194   */
     195  #define SKIP_BYTES(a_tknzr, a_nb_bytes) \
     196  status = cr_input_seek_index (PRIVATE (a_tknzr)->input, \
     197                                       CR_SEEK_CUR, a_nb_bytes) ; \
     198  CHECK_PARSING_STATUS (status, TRUE) ;
     199  
     200  /**
     201   *Skip utf8 encoded characters.
     202   *Updates line and column numbers.
     203   *@param a_parser the current instance of #CRTknzr.
     204   *@param a_nb_chars the number of chars to skip. Must be of
     205   *type glong.
     206   */
     207  #define SKIP_CHARS(a_tknzr, a_nb_chars) \
     208  { \
     209  gulong nb_chars = a_nb_chars ; \
     210  status = cr_input_consume_chars \
     211       (PRIVATE (a_tknzr)->input,0, &nb_chars) ; \
     212  CHECK_PARSING_STATUS (status, TRUE) ; \
     213  }
     214  
     215  /**
     216   *Tests the condition and if it is false, sets
     217   *status to "CR_PARSING_ERROR" and goto the 'error'
     218   *label.
     219   *@param condition the condition to test.
     220   */
     221  #define ENSURE_PARSING_COND(condition) \
     222  if (! (condition)) {status = CR_PARSING_ERROR; goto error ;}
     223  
     224  static enum CRStatus  cr_tknzr_parse_nl (CRTknzr * a_this, 
     225                                           guchar ** a_start, 
     226                                           guchar ** a_end,
     227                                           CRParsingLocation *a_location);
     228  
     229  static enum CRStatus cr_tknzr_parse_w (CRTknzr * a_this, 
     230                                         guchar ** a_start, 
     231                                         guchar ** a_end,
     232                                         CRParsingLocation *a_location) ;
     233  
     234  static enum CRStatus cr_tknzr_parse_unicode_escape (CRTknzr * a_this, 
     235                                                      guint32 * a_unicode,
     236                                                      CRParsingLocation *a_location) ;
     237  
     238  static enum CRStatus cr_tknzr_parse_escape (CRTknzr * a_this, 
     239                                              guint32 * a_esc_code,
     240                                              CRParsingLocation *a_location);
     241  
     242  static enum CRStatus cr_tknzr_parse_string (CRTknzr * a_this, 
     243                                              CRString ** a_str);
     244  
     245  static enum CRStatus cr_tknzr_parse_comment (CRTknzr * a_this, 
     246                                               CRString ** a_comment);
     247  
     248  static enum CRStatus cr_tknzr_parse_nmstart (CRTknzr * a_this, 
     249                                               guint32 * a_char, 
     250                                               CRParsingLocation *a_location);
     251  
     252  static enum CRStatus cr_tknzr_parse_num (CRTknzr * a_this,
     253                                           CRNum ** a_num);
     254  
     255  /**********************************
     256   *PRIVATE methods
     257   **********************************/
     258  
     259  /**
     260   *Parses a "w" as defined by the css spec at [4.1.1]:
     261   * w ::= [ \t\r\n\f]*
     262   *
     263   *@param a_this the current instance of #CRTknzr.
     264   *@param a_start out param. Upon successfull completion, points
     265   *to the beginning of the parsed white space, points to NULL otherwise.
     266   *Can also point to NULL is there is no white space actually.
     267   *@param a_end out param. Upon successfull completion, points
     268   *to the end of the parsed white space, points to NULL otherwise.
     269   *Can also point to NULL is there is no white space actually.
     270   */
     271  static enum CRStatus
     272  cr_tknzr_parse_w (CRTknzr * a_this, 
     273                    guchar ** a_start, 
     274                    guchar ** a_end, 
     275                    CRParsingLocation *a_location)
     276  {
     277          guint32 cur_char = 0;
     278          CRInputPos init_pos;
     279          enum CRStatus status = CR_OK;
     280  
     281          g_return_val_if_fail (a_this && PRIVATE (a_this)
     282                                && PRIVATE (a_this)->input
     283                                && a_start && a_end, 
     284                                CR_BAD_PARAM_ERROR);
     285  
     286          RECORD_INITIAL_POS (a_this, &init_pos);
     287  
     288          *a_start = NULL;
     289          *a_end = NULL;
     290  
     291          READ_NEXT_CHAR (a_this, &cur_char);
     292  
     293          if (cr_utils_is_white_space (cur_char) == FALSE) {
     294                  status = CR_PARSING_ERROR;
     295                  goto error;
     296          }
     297          if (a_location) {
     298                  cr_tknzr_get_parsing_location (a_this, 
     299                                                 a_location) ;
     300          }
     301          RECORD_CUR_BYTE_ADDR (a_this, a_start);
     302          *a_end = *a_start;
     303  
     304          for (;;) {
     305                  gboolean is_eof = FALSE;
     306  
     307                  cr_input_get_end_of_file (PRIVATE (a_this)->input, &is_eof);
     308                  if (is_eof)
     309                          break;
     310  
     311                  status = cr_tknzr_peek_char (a_this, &cur_char);
     312                  if (status == CR_END_OF_INPUT_ERROR) {
     313                          break;
     314                  } else if (status != CR_OK) {
     315                          goto error;
     316                  }
     317  
     318                  if (cr_utils_is_white_space (cur_char) == TRUE) {
     319                          READ_NEXT_CHAR (a_this, &cur_char);
     320                          RECORD_CUR_BYTE_ADDR (a_this, a_end);
     321                  } else {
     322                          break;
     323                  }
     324          }
     325  
     326          return CR_OK;
     327  
     328        error:
     329          cr_tknzr_set_cur_pos (a_this, &init_pos);
     330  
     331          return status;
     332  }
     333  
     334  /**
     335   *Parses a newline as defined in the css2 spec:
     336   * nl   ::=    \n|\r\n|\r|\f
     337   *
     338   *@param a_this the "this pointer" of the current instance of #CRTknzr.
     339   *@param a_start a pointer to the first character of the successfully 
     340   *parsed string.
     341   *@param a_end a pointer to the last character of the successfully parsed
     342   *string.
     343   *@result CR_OK uppon successfull completion, an error code otherwise.
     344   */
     345  static enum CRStatus
     346  cr_tknzr_parse_nl (CRTknzr * a_this, 
     347                     guchar ** a_start, 
     348                     guchar ** a_end, 
     349                     CRParsingLocation *a_location)
     350  {
     351          CRInputPos init_pos;
     352          guchar next_chars[2] = { 0 };
     353          enum CRStatus status = CR_PARSING_ERROR;
     354  
     355          g_return_val_if_fail (a_this && PRIVATE (a_this)
     356                                && a_start && a_end, CR_BAD_PARAM_ERROR);
     357  
     358          RECORD_INITIAL_POS (a_this, &init_pos);
     359  
     360          PEEK_BYTE (a_this, 1, &next_chars[0]);
     361          PEEK_BYTE (a_this, 2, &next_chars[1]);
     362  
     363          if ((next_chars[0] == '\r' && next_chars[1] == '\n')) {
     364                  SKIP_BYTES (a_this, 1);
     365                  if (a_location) {
     366                          cr_tknzr_get_parsing_location 
     367                                  (a_this, a_location) ;
     368                  }
     369                  SKIP_CHARS (a_this, 1);
     370  
     371                  RECORD_CUR_BYTE_ADDR (a_this, a_end);
     372  
     373                  status = CR_OK;
     374          } else if (next_chars[0] == '\n'
     375                     || next_chars[0] == '\r' || next_chars[0] == '\f') {
     376                  SKIP_CHARS (a_this, 1);
     377                  if (a_location) {
     378                          cr_tknzr_get_parsing_location 
     379                                  (a_this, a_location) ;
     380                  }
     381                  RECORD_CUR_BYTE_ADDR (a_this, a_start);
     382                  *a_end = *a_start;
     383                  status = CR_OK;
     384          } else {
     385                  status = CR_PARSING_ERROR;
     386                  goto error;
     387          }
     388          return CR_OK ;
     389  
     390   error:
     391          cr_tknzr_set_cur_pos (a_this, &init_pos) ;
     392          return status;
     393  }
     394  
     395  /**
     396   *Go ahead in the parser input, skipping all the spaces.
     397   *If the next char if not a white space, this function does nothing.
     398   *In any cases, it stops when it encounters a non white space character.
     399   *
     400   *@param a_this the current instance of #CRTknzr.
     401   *@return CR_OK upon successfull completion, an error code otherwise.
     402   */
     403  static enum CRStatus
     404  cr_tknzr_try_to_skip_spaces (CRTknzr * a_this)
     405  {
     406          enum CRStatus status = CR_ERROR;
     407          guint32 cur_char = 0;
     408  
     409          g_return_val_if_fail (a_this && PRIVATE (a_this)
     410                                && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
     411  
     412          status = cr_input_peek_char (PRIVATE (a_this)->input, &cur_char);
     413  
     414          if (status != CR_OK) {
     415                  if (status == CR_END_OF_INPUT_ERROR)
     416                          return CR_OK;
     417                  return status;
     418          }
     419  
     420          if (cr_utils_is_white_space (cur_char) == TRUE) {
     421                  gulong nb_chars = -1; /*consume all spaces */
     422  
     423                  status = cr_input_consume_white_spaces
     424                          (PRIVATE (a_this)->input, &nb_chars);
     425          }
     426  
     427          return status;
     428  }
     429  
     430  /**
     431   *Parses a "comment" as defined in the css spec at [4.1.1]:
     432   *COMMENT ::= \/\*[^*]*\*+([^/][^*]*\*+)*\/ .
     433   *This complex regexp is just to say that comments start
     434   *with the two chars '/''*' and ends with the two chars '*''/'.
     435   *It also means that comments cannot be nested.
     436   *So based on that, I've just tried to implement the parsing function
     437   *simply and in a straight forward manner.
     438   */
     439  static enum CRStatus
     440  cr_tknzr_parse_comment (CRTknzr * a_this, 
     441                          CRString ** a_comment)
     442  {
     443          enum CRStatus status = CR_OK;
     444          CRInputPos init_pos;
     445          guint32 cur_char = 0, next_char= 0;
     446          CRString *comment = NULL;
     447          CRParsingLocation loc = {0} ;
     448  
     449          g_return_val_if_fail (a_this && PRIVATE (a_this)
     450                                && PRIVATE (a_this)->input, 
     451                                CR_BAD_PARAM_ERROR);
     452  
     453          RECORD_INITIAL_POS (a_this, &init_pos);        
     454          READ_NEXT_CHAR (a_this, &cur_char) ;        
     455          ENSURE_PARSING_COND (cur_char == '/');
     456          cr_tknzr_get_parsing_location (a_this, &loc) ;
     457  
     458          READ_NEXT_CHAR (a_this, &cur_char);
     459          ENSURE_PARSING_COND (cur_char == '*');
     460          comment = cr_string_new ();
     461          for (;;) { /* [^*]* */
     462                  PEEK_NEXT_CHAR (a_this, &next_char);
     463                  if (next_char == '*')
     464                          break;
     465                  READ_NEXT_CHAR (a_this, &cur_char);
     466                  g_string_append_unichar (comment->stryng, cur_char);
     467          }
     468          /* Stop condition: next_char == '*' */
     469          for (;;) { /* \*+ */
     470                  READ_NEXT_CHAR(a_this, &cur_char);
     471                  ENSURE_PARSING_COND (cur_char == '*');
     472                  g_string_append_unichar (comment->stryng, cur_char);
     473                  PEEK_NEXT_CHAR (a_this, &next_char);
     474                  if (next_char != '*')
     475                          break;
     476          }
     477          /* Stop condition: next_char != '*' */
     478          for (;;) { /* ([^/][^*]*\*+)* */
     479                  if (next_char == '/')
     480                          break;
     481                  READ_NEXT_CHAR(a_this, &cur_char);
     482                  g_string_append_unichar (comment->stryng, cur_char);
     483                  for (;;) { /* [^*]* */
     484                          PEEK_NEXT_CHAR (a_this, &next_char);
     485                          if (next_char == '*')
     486                                  break;
     487                          READ_NEXT_CHAR (a_this, &cur_char);
     488                          g_string_append_unichar (comment->stryng, cur_char);
     489                  }
     490                  /* Stop condition: next_char = '*', no need to verify, because peek and read exit to error anyway */
     491                  for (;;) { /* \*+ */
     492                          READ_NEXT_CHAR(a_this, &cur_char);
     493                          ENSURE_PARSING_COND (cur_char == '*');
     494                          g_string_append_unichar (comment->stryng, cur_char);
     495                          PEEK_NEXT_CHAR (a_this, &next_char);
     496                          if (next_char != '*')
     497                                  break;
     498                  }
     499                  /* Continue condition: next_char != '*' */
     500          }
     501          /* Stop condition: next_char == '\/' */
     502          READ_NEXT_CHAR(a_this, &cur_char);
     503          g_string_append_unichar (comment->stryng, cur_char);
     504  
     505          if (status == CR_OK) {
     506                  cr_parsing_location_copy (&comment->location, 
     507                                            &loc) ;
     508                  *a_comment = comment;                
     509                  return CR_OK;
     510          }
     511   error:
     512  
     513          if (comment) {
     514                  cr_string_destroy (comment);
     515                  comment = NULL;
     516          }
     517  
     518          cr_tknzr_set_cur_pos (a_this, &init_pos);
     519  
     520          return status;
     521  }
     522  
     523  /**
     524   *Parses an 'unicode' escape sequence defined
     525   *in css spec at chap 4.1.1:
     526   *unicode ::= \\[0-9a-f]{1,6}[ \n\r\t\f]?
     527   *@param a_this the current instance of #CRTknzr.
     528   *@param a_start out parameter. A pointer to the start
     529   *of the unicode escape sequence. Must *NOT* be deleted by
     530   *the caller.
     531   *@param a_end out parameter. A pointer to the last character
     532   *of the unicode escape sequence. Must *NOT* be deleted by the caller.
     533   *@return CR_OK if parsing succeded, an error code otherwise.
     534   *Error code can be either CR_PARSING_ERROR if the string 
     535   *parsed just doesn't
     536   *respect the production or another error if a 
     537   *lower level error occurred.
     538   */
     539  static enum CRStatus
     540  cr_tknzr_parse_unicode_escape (CRTknzr * a_this, 
     541                                 guint32 * a_unicode,
     542                                 CRParsingLocation *a_location)
     543  {
     544          guint32 cur_char;
     545          CRInputPos init_pos;
     546          glong occur = 0;
     547          guint32 unicode = 0;
     548          guchar *tmp_char_ptr1 = NULL,
     549                  *tmp_char_ptr2 = NULL;
     550          enum CRStatus status = CR_OK;
     551  
     552          g_return_val_if_fail (a_this && PRIVATE (a_this)
     553                                && a_unicode, CR_BAD_PARAM_ERROR);
     554  
     555          /*first, let's backup the current position pointer */
     556          RECORD_INITIAL_POS (a_this, &init_pos);
     557  
     558          READ_NEXT_CHAR (a_this, &cur_char);
     559  
     560          if (cur_char != '\\') {
     561                  status = CR_PARSING_ERROR;
     562                  goto error;
     563          }
     564          if (a_location) {
     565                  cr_tknzr_get_parsing_location 
     566                          (a_this, a_location) ;
     567          }
     568          PEEK_NEXT_CHAR (a_this, &cur_char);
     569  
     570          for (occur = 0, unicode = 0; ((cur_char >= '0' && cur_char <= '9')
     571                                        || (cur_char >= 'a' && cur_char <= 'f')
     572                                        || (cur_char >= 'A' && cur_char <= 'F'))
     573               && occur < 6; occur++) {
     574                  gint cur_char_val = 0;
     575  
     576                  READ_NEXT_CHAR (a_this, &cur_char);
     577  
     578                  if ((cur_char >= '0' && cur_char <= '9')) {
     579                          cur_char_val = (cur_char - '0');
     580                  } else if ((cur_char >= 'a' && cur_char <= 'f')) {
     581                          cur_char_val = 10 + (cur_char - 'a');
     582                  } else if ((cur_char >= 'A' && cur_char <= 'F')) {
     583                          cur_char_val = 10 + (cur_char - 'A');
     584                  }
     585  
     586                  unicode = unicode * 16 + cur_char_val;
     587  
     588                  PEEK_NEXT_CHAR (a_this, &cur_char);
     589          }
     590  
     591          /* Eat a whitespace if possible. */
     592          cr_tknzr_parse_w (a_this, &tmp_char_ptr1, 
     593                            &tmp_char_ptr2, NULL);
     594          *a_unicode = unicode;
     595          return CR_OK;
     596  
     597        error:
     598          /*
     599           *restore the initial position pointer backuped at
     600           *the beginning of this function.
     601           */
     602          cr_tknzr_set_cur_pos (a_this, &init_pos);
     603  
     604          return status;
     605  }
     606  
     607  /**
     608   *parses an escape sequence as defined by the css spec:
     609   *escape ::= {unicode}|\\[ -~\200-\4177777]
     610   *@param a_this the current instance of #CRTknzr .
     611   */
     612  static enum CRStatus
     613  cr_tknzr_parse_escape (CRTknzr * a_this, guint32 * a_esc_code,
     614                         CRParsingLocation *a_location)
     615  {
     616          enum CRStatus status = CR_OK;
     617          guint32 cur_char = 0;
     618          CRInputPos init_pos;
     619          guchar next_chars[2];
     620  
     621          g_return_val_if_fail (a_this && PRIVATE (a_this)
     622                                && a_esc_code, CR_BAD_PARAM_ERROR);
     623  
     624          RECORD_INITIAL_POS (a_this, &init_pos);
     625  
     626          PEEK_BYTE (a_this, 1, &next_chars[0]);
     627          PEEK_BYTE (a_this, 2, &next_chars[1]);
     628  
     629          if (next_chars[0] != '\\') {
     630                  status = CR_PARSING_ERROR;
     631                  goto error;
     632          }
     633  
     634          if ((next_chars[1] >= '0' && next_chars[1] <= '9')
     635              || (next_chars[1] >= 'a' && next_chars[1] <= 'f')
     636              || (next_chars[1] >= 'A' && next_chars[1] <= 'F')) {
     637                  status = cr_tknzr_parse_unicode_escape (a_this, a_esc_code, 
     638                                                          a_location);
     639          } else {
     640                  /*consume the '\' char */
     641                  READ_NEXT_CHAR (a_this, &cur_char);
     642                  if (a_location) {
     643                          cr_tknzr_get_parsing_location (a_this, 
     644                                                         a_location) ;
     645                  }
     646                  /*then read the char after the '\' */
     647                  READ_NEXT_CHAR (a_this, &cur_char);
     648  
     649                  if (cur_char != ' ' && (cur_char < 200 || cur_char > 4177777)) {
     650                          status = CR_PARSING_ERROR;
     651                          goto error;
     652                  }
     653                  *a_esc_code = cur_char;
     654  
     655          }
     656          if (status == CR_OK) {
     657                  return CR_OK;
     658          }
     659   error:
     660          cr_tknzr_set_cur_pos (a_this, &init_pos);
     661          return status;
     662  }
     663  
     664  /**
     665   *Parses a string type as defined in css spec [4.1.1]:
     666   *
     667   *string ::= {string1}|{string2}
     668   *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
     669   *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
     670   *
     671   *@param a_this the current instance of #CRTknzr.
     672   *@param a_start out parameter. Upon successfull completion, 
     673   *points to the beginning of the string, points to an undefined value
     674   *otherwise.
     675   *@param a_end out parameter. Upon successfull completion, points to
     676   *the beginning of the string, points to an undefined value otherwise.
     677   *@return CR_OK upon successfull completion, an error code otherwise.
     678   */
     679  static enum CRStatus
     680  cr_tknzr_parse_string (CRTknzr * a_this, CRString ** a_str)
     681  {
     682          guint32 cur_char = 0,
     683                  delim = 0;
     684          CRInputPos init_pos;
     685          enum CRStatus status = CR_OK;
     686          CRString *str = NULL;
     687  
     688          g_return_val_if_fail (a_this && PRIVATE (a_this)
     689                                && PRIVATE (a_this)->input
     690                                && a_str, CR_BAD_PARAM_ERROR);
     691  
     692          RECORD_INITIAL_POS (a_this, &init_pos);
     693          READ_NEXT_CHAR (a_this, &cur_char);
     694  
     695          if (cur_char == '"')
     696                  delim = '"';
     697          else if (cur_char == '\'')
     698                  delim = '\'';
     699          else {
     700                  status = CR_PARSING_ERROR;
     701                  goto error;
     702          }
     703          str = cr_string_new ();
     704          if (str) {
     705                  cr_tknzr_get_parsing_location 
     706                          (a_this, &str->location) ;
     707          }
     708          for (;;) {
     709                  guchar next_chars[2] = { 0 };
     710  
     711                  PEEK_BYTE (a_this, 1, &next_chars[0]);
     712                  PEEK_BYTE (a_this, 2, &next_chars[1]);
     713  
     714                  if (next_chars[0] == '\\') {
     715                          guchar *tmp_char_ptr1 = NULL,
     716                                  *tmp_char_ptr2 = NULL;
     717                          guint32 esc_code = 0;
     718  
     719                          if (next_chars[1] == '\'' || next_chars[1] == '"') {
     720                                  g_string_append_unichar (str->stryng, 
     721                                                           next_chars[1]);
     722                                  SKIP_BYTES (a_this, 2);
     723                                  status = CR_OK;
     724                          } else {
     725                                  status = cr_tknzr_parse_escape
     726                                          (a_this, &esc_code, NULL);
     727  
     728                                  if (status == CR_OK) {
     729                                          g_string_append_unichar
     730                                                  (str->stryng, 
     731                                                   esc_code);
     732                                  }
     733                          }
     734  
     735                          if (status != CR_OK) {
     736                                  /*
     737                                   *consume the '\' char, and try to parse
     738                                   *a newline.
     739                                   */
     740                                  READ_NEXT_CHAR (a_this, &cur_char);
     741  
     742                                  status = cr_tknzr_parse_nl
     743                                          (a_this, &tmp_char_ptr1,
     744                                           &tmp_char_ptr2, NULL);
     745                          }
     746  
     747                          CHECK_PARSING_STATUS (status, FALSE);
     748                  } else if (strchr ("\t !#$%&", next_chars[0])
     749                             || (next_chars[0] >= '(' && next_chars[0] <= '~')) {
     750                          READ_NEXT_CHAR (a_this, &cur_char);
     751                          g_string_append_unichar (str->stryng, 
     752                                                   cur_char);
     753                          status = CR_OK;
     754                  }
     755  
     756                  else if (cr_utils_is_nonascii (next_chars[0])) {
     757                          READ_NEXT_CHAR (a_this, &cur_char);
     758                          g_string_append_unichar (str->stryng, cur_char);
     759                  } else if (next_chars[0] == delim) {
     760                          READ_NEXT_CHAR (a_this, &cur_char);
     761                          break;
     762                  } else {
     763                          status = CR_PARSING_ERROR;
     764                          goto error;
     765                  }
     766          }
     767  
     768          if (status == CR_OK) {
     769                  if (*a_str == NULL) {
     770                          *a_str = str;
     771                          str = NULL;
     772                  } else {
     773                          (*a_str)->stryng = g_string_append_len
     774                                  ((*a_str)->stryng,
     775                                   str->stryng->str, 
     776                                   str->stryng->len);
     777                          cr_string_destroy (str);
     778                  }
     779                  return CR_OK;
     780          }
     781  
     782   error:
     783  
     784          if (str) {
     785                  cr_string_destroy (str) ;
     786                  str = NULL;
     787          }
     788          cr_tknzr_set_cur_pos (a_this, &init_pos);
     789          return status;
     790  }
     791  
     792  /**
     793   *Parses the an nmstart as defined by the css2 spec [4.1.1]:
     794   * nmstart [a-zA-Z]|{nonascii}|{escape}
     795   *
     796   *@param a_this the current instance of #CRTknzr.
     797   *@param a_start out param. A pointer to the starting point of
     798   *the token.
     799   *@param a_end out param. A pointer to the ending point of the
     800   *token.
     801   *@param a_char out param. The actual parsed nmchar.
     802   *@return CR_OK upon successfull completion, 
     803   *an error code otherwise.
     804   */
     805  static enum CRStatus
     806  cr_tknzr_parse_nmstart (CRTknzr * a_this, 
     807                          guint32 * a_char,
     808                          CRParsingLocation *a_location)
     809  {
     810          CRInputPos init_pos;
     811          enum CRStatus status = CR_OK;
     812          guint32 cur_char = 0,
     813                  next_char = 0;
     814  
     815          g_return_val_if_fail (a_this && PRIVATE (a_this)
     816                                && PRIVATE (a_this)->input
     817                                && a_char, CR_BAD_PARAM_ERROR);
     818  
     819          RECORD_INITIAL_POS (a_this, &init_pos);
     820  
     821          PEEK_NEXT_CHAR (a_this, &next_char);
     822  
     823          if (next_char == '\\') {
     824                  status = cr_tknzr_parse_escape (a_this, a_char,
     825                                                  a_location);
     826  
     827                  if (status != CR_OK)
     828                          goto error;
     829  
     830          } else if (cr_utils_is_nonascii (next_char) == TRUE
     831                     || ((next_char >= 'a') && (next_char <= 'z'))
     832                     || ((next_char >= 'A') && (next_char <= 'Z'))
     833                  ) {
     834                  READ_NEXT_CHAR (a_this, &cur_char);
     835                  if (a_location) {
     836                          cr_tknzr_get_parsing_location (a_this, 
     837                                                         a_location) ;
     838                  }
     839                  *a_char = cur_char;
     840                  status = CR_OK;
     841          } else {
     842                  status = CR_PARSING_ERROR;
     843                  goto error;
     844          }
     845  
     846          return CR_OK;
     847  
     848   error:        
     849          cr_tknzr_set_cur_pos (a_this, &init_pos);
     850  
     851          return status;
     852  
     853  }
     854  
     855  /**
     856   *Parses an nmchar as described in the css spec at
     857   *chap 4.1.1:
     858   *nmchar ::= [a-z0-9-]|{nonascii}|{escape}
     859   *
     860   *Humm, I have added the possibility for nmchar to
     861   *contain upper case letters.
     862   *
     863   *@param a_this the current instance of #CRTknzr.
     864   *@param a_start out param. A pointer to the starting point of
     865   *the token.
     866   *@param a_end out param. A pointer to the ending point of the
     867   *token.
     868   *@param a_char out param. The actual parsed nmchar.
     869   *@return CR_OK upon successfull completion, 
     870   *an error code otherwise.
     871   */
     872  static enum CRStatus
     873  cr_tknzr_parse_nmchar (CRTknzr * a_this, guint32 * a_char,
     874                         CRParsingLocation *a_location)
     875  {
     876          guint32 cur_char = 0,
     877                  next_char = 0;
     878          enum CRStatus status = CR_OK;
     879          CRInputPos init_pos;
     880  
     881          g_return_val_if_fail (a_this && PRIVATE (a_this) && a_char,
     882                                CR_BAD_PARAM_ERROR);
     883  
     884          RECORD_INITIAL_POS (a_this, &init_pos);
     885  
     886          status = cr_input_peek_char (PRIVATE (a_this)->input, 
     887                                       &next_char) ;
     888          if (status != CR_OK)
     889                  goto error;
     890  
     891          if (next_char == '\\') {
     892                  status = cr_tknzr_parse_escape (a_this, a_char, 
     893                                                  a_location);
     894  
     895                  if (status != CR_OK)
     896                          goto error;
     897  
     898          } else if (cr_utils_is_nonascii (next_char) == TRUE
     899                     || ((next_char >= 'a') && (next_char <= 'z'))
     900                     || ((next_char >= 'A') && (next_char <= 'Z'))
     901                     || ((next_char >= '0') && (next_char <= '9'))
     902                     || (next_char == '-')
     903                     || (next_char == '_') /*'_' not allowed by the spec. */
     904                  ) {
     905                  READ_NEXT_CHAR (a_this, &cur_char);
     906                  *a_char = cur_char;
     907                  status = CR_OK;
     908                  if (a_location) {
     909                          cr_tknzr_get_parsing_location
     910                                  (a_this, a_location) ;
     911                  }
     912          } else {
     913                  status = CR_PARSING_ERROR;
     914                  goto error;
     915          }
     916          return CR_OK;
     917  
     918   error:
     919          cr_tknzr_set_cur_pos (a_this, &init_pos);
     920          return status;
     921  }
     922  
     923  /**
     924   *Parses an "ident" as defined in css spec [4.1.1]:
     925   *ident ::= {nmstart}{nmchar}*
     926   *
     927   *Actually parses it using the css3 grammar:
     928   *ident ::= -?{nmstart}{nmchar}*
     929   *@param a_this the currens instance of #CRTknzr.
     930   *
     931   *@param a_str a pointer to parsed ident. If *a_str is NULL,
     932   *this function allocates a new instance of CRString. If not, 
     933   *the function just appends the parsed string to the one passed.
     934   *In both cases it is up to the caller to free *a_str.
     935   *
     936   *@return CR_OK upon successfull completion, an error code 
     937   *otherwise.
     938   */
     939  static enum CRStatus
     940  cr_tknzr_parse_ident (CRTknzr * a_this, CRString ** a_str)
     941  {
     942          guint32 tmp_char = 0;
     943          CRString *stringue = NULL ;
     944          CRInputPos init_pos;
     945          enum CRStatus status = CR_OK;
     946          gboolean location_is_set = FALSE ;
     947  
     948          g_return_val_if_fail (a_this && PRIVATE (a_this)
     949                                && PRIVATE (a_this)->input
     950                                && a_str, CR_BAD_PARAM_ERROR);
     951  
     952          RECORD_INITIAL_POS (a_this, &init_pos);
     953          PEEK_NEXT_CHAR (a_this, &tmp_char) ;
     954          stringue = cr_string_new () ;
     955          g_return_val_if_fail (stringue, 
     956                                CR_OUT_OF_MEMORY_ERROR) ;
     957  
     958          if (tmp_char == '-') {
     959                  READ_NEXT_CHAR (a_this, &tmp_char) ;
     960                  cr_tknzr_get_parsing_location
     961                          (a_this, &stringue->location) ;
     962                  location_is_set = TRUE ;
     963                  g_string_append_unichar (stringue->stryng, 
     964                                           tmp_char) ;
     965          }
     966          status = cr_tknzr_parse_nmstart (a_this, &tmp_char, NULL);
     967          if (status != CR_OK) {
     968                  status = CR_PARSING_ERROR;
     969                  goto end ;
     970          }
     971          if (location_is_set == FALSE) {
     972                  cr_tknzr_get_parsing_location 
     973                          (a_this, &stringue->location) ;
     974                  location_is_set = TRUE ;
     975          }
     976          g_string_append_unichar (stringue->stryng, tmp_char);
     977          for (;;) {
     978                  status = cr_tknzr_parse_nmchar (a_this, 
     979                                                  &tmp_char, 
     980                                                  NULL);
     981                  if (status != CR_OK) {
     982                          status = CR_OK ;
     983                          break;
     984                  }
     985                  g_string_append_unichar (stringue->stryng, tmp_char);
     986          }
     987          if (status == CR_OK) {
     988                  if (!*a_str) {
     989                          *a_str = stringue ;
     990                  
     991                  } else {
     992                          g_string_append_len ((*a_str)->stryng, 
     993                                               stringue->stryng->str, 
     994                                               stringue->stryng->len) ;
     995                          cr_string_destroy (stringue) ;
     996                  }
     997                  stringue = NULL ;
     998          }
     999  
    1000   error:
    1001   end:
    1002          if (stringue) {
    1003                  cr_string_destroy (stringue) ;
    1004                  stringue = NULL ;
    1005          }
    1006          if (status != CR_OK ) {
    1007                  cr_tknzr_set_cur_pos (a_this, &init_pos) ;
    1008          }
    1009          return status ;
    1010  }
    1011  
    1012  
    1013  /**
    1014   *Parses a "name" as defined by css spec [4.1.1]:
    1015   *name ::= {nmchar}+
    1016   *
    1017   *@param a_this the current instance of #CRTknzr.
    1018   *
    1019   *@param a_str out parameter. A pointer to the successfully parsed
    1020   *name. If *a_str is set to NULL, this function allocates a new instance
    1021   *of CRString. If not, it just appends the parsed name to the passed *a_str.
    1022   *In both cases, it is up to the caller to free *a_str.
    1023   *
    1024   *@return CR_OK upon successfull completion, an error code otherwise.
    1025   */
    1026  static enum CRStatus
    1027  cr_tknzr_parse_name (CRTknzr * a_this, 
    1028                       CRString ** a_str)
    1029  {
    1030          guint32 tmp_char = 0;
    1031          CRInputPos init_pos;
    1032          enum CRStatus status = CR_OK;
    1033          gboolean str_needs_free = FALSE,
    1034                  is_first_nmchar=TRUE ;
    1035          glong i = 0;
    1036          CRParsingLocation loc = {0} ;
    1037  
    1038          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1039                                && PRIVATE (a_this)->input
    1040                                && a_str,
    1041                                CR_BAD_PARAM_ERROR) ;
    1042  
    1043          RECORD_INITIAL_POS (a_this, &init_pos);
    1044  
    1045          if (*a_str == NULL) {
    1046                  *a_str = cr_string_new ();
    1047                  str_needs_free = TRUE;
    1048          }
    1049          for (i = 0;; i++) {
    1050                  if (is_first_nmchar == TRUE) {
    1051                          status = cr_tknzr_parse_nmchar 
    1052                                  (a_this, &tmp_char,
    1053                                   &loc) ;
    1054                          is_first_nmchar = FALSE ;
    1055                  } else {
    1056                          status = cr_tknzr_parse_nmchar 
    1057                                  (a_this, &tmp_char, NULL) ;
    1058                  }
    1059                  if (status != CR_OK)
    1060                          break;                
    1061                  g_string_append_unichar ((*a_str)->stryng, 
    1062                                           tmp_char);
    1063          }
    1064          if (i > 0) {
    1065                  cr_parsing_location_copy 
    1066                          (&(*a_str)->location, &loc) ;
    1067                  return CR_OK;
    1068          }
    1069          if (str_needs_free == TRUE && *a_str) {
    1070                  cr_string_destroy (*a_str);
    1071                  *a_str = NULL;
    1072          }
    1073          cr_tknzr_set_cur_pos (a_this, &init_pos);
    1074          return CR_PARSING_ERROR;
    1075  }
    1076  
    1077  /**
    1078   *Parses a "hash" as defined by the css spec in [4.1.1]:
    1079   *HASH ::= #{name}
    1080   */
    1081  static enum CRStatus
    1082  cr_tknzr_parse_hash (CRTknzr * a_this, CRString ** a_str)
    1083  {
    1084          guint32 cur_char = 0;
    1085          CRInputPos init_pos;
    1086          enum CRStatus status = CR_OK;
    1087          gboolean str_needs_free = FALSE;
    1088          CRParsingLocation loc = {0} ;
    1089  
    1090          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1091                                && PRIVATE (a_this)->input,
    1092                                CR_BAD_PARAM_ERROR);
    1093  
    1094          RECORD_INITIAL_POS (a_this, &init_pos);
    1095          READ_NEXT_CHAR (a_this, &cur_char);
    1096          if (cur_char != '#') {
    1097                  status = CR_PARSING_ERROR;
    1098                  goto error;
    1099          }
    1100          if (*a_str == NULL) {
    1101                  *a_str = cr_string_new ();
    1102                  str_needs_free = TRUE;
    1103          }
    1104          cr_tknzr_get_parsing_location (a_this,
    1105                                         &loc) ;
    1106          status = cr_tknzr_parse_name (a_this, a_str);
    1107          cr_parsing_location_copy (&(*a_str)->location, &loc) ;
    1108          if (status != CR_OK) {
    1109                  goto error;
    1110          }
    1111          return CR_OK;
    1112  
    1113   error:
    1114          if (str_needs_free == TRUE && *a_str) {
    1115                  cr_string_destroy (*a_str);
    1116                  *a_str = NULL;
    1117          }
    1118  
    1119          cr_tknzr_set_cur_pos (a_this, &init_pos);
    1120          return status;
    1121  }
    1122  
    1123  /**
    1124   *Parses an uri as defined by the css spec [4.1.1]:
    1125   * URI ::= url\({w}{string}{w}\)
    1126   *         |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)
    1127   *
    1128   *@param a_this the current instance of #CRTknzr.
    1129   *@param a_str the successfully parsed url.
    1130   *@return CR_OK upon successfull completion, an error code otherwise.
    1131   */
    1132  static enum CRStatus
    1133  cr_tknzr_parse_uri (CRTknzr * a_this, 
    1134                      CRString ** a_str)
    1135  {
    1136          guint32 cur_char = 0;
    1137          CRInputPos init_pos;
    1138          enum CRStatus status = CR_PARSING_ERROR;
    1139          guchar tab[4] = { 0 }, *tmp_ptr1 = NULL, *tmp_ptr2 = NULL;
    1140          CRString *str = NULL;
    1141          CRParsingLocation location = {0} ;
    1142  
    1143          g_return_val_if_fail (a_this 
    1144                                && PRIVATE (a_this)
    1145                                && PRIVATE (a_this)->input
    1146                                && a_str, 
    1147                                CR_BAD_PARAM_ERROR);
    1148  
    1149          RECORD_INITIAL_POS (a_this, &init_pos);
    1150  
    1151          PEEK_BYTE (a_this, 1, &tab[0]);
    1152          PEEK_BYTE (a_this, 2, &tab[1]);
    1153          PEEK_BYTE (a_this, 3, &tab[2]);
    1154          PEEK_BYTE (a_this, 4, &tab[3]);
    1155  
    1156          if (tab[0] != 'u' || tab[1] != 'r' || tab[2] != 'l' || tab[3] != '(') {
    1157                  status = CR_PARSING_ERROR;
    1158                  goto error;
    1159          }
    1160          /*
    1161           *Here, we want to skip 4 bytes ('u''r''l''(').
    1162           *But we also need to keep track of the parsing location
    1163           *of the 'u'. So, we skip 1 byte, we record the parsing
    1164           *location, then we skip the 3 remaining bytes.
    1165           */
    1166          SKIP_CHARS (a_this, 1);
    1167          cr_tknzr_get_parsing_location (a_this, &location) ;
    1168          SKIP_CHARS (a_this, 3);
    1169          cr_tknzr_try_to_skip_spaces (a_this);
    1170          status = cr_tknzr_parse_string (a_this, a_str);
    1171  
    1172          if (status == CR_OK) {
    1173                  guint32 next_char = 0;
    1174                  status = cr_tknzr_parse_w (a_this, &tmp_ptr1, 
    1175                                             &tmp_ptr2, NULL);
    1176                  cr_tknzr_try_to_skip_spaces (a_this);
    1177                  PEEK_NEXT_CHAR (a_this, &next_char);
    1178                  if (next_char == ')') {
    1179                          READ_NEXT_CHAR (a_this, &cur_char);
    1180                          status = CR_OK;
    1181                  } else {
    1182                          status = CR_PARSING_ERROR;
    1183                  }
    1184          }
    1185          if (status != CR_OK) {
    1186                  str = cr_string_new ();
    1187                  for (;;) {
    1188                          guint32 next_char = 0;
    1189                          PEEK_NEXT_CHAR (a_this, &next_char);
    1190                          if (strchr ("!#$%&", next_char)
    1191                              || (next_char >= '*' && next_char <= '~')
    1192                              || (cr_utils_is_nonascii (next_char) == TRUE)) {
    1193                                  READ_NEXT_CHAR (a_this, &cur_char);
    1194                                  g_string_append_unichar 
    1195                                          (str->stryng, cur_char);
    1196                                  status = CR_OK;
    1197                          } else {
    1198                                  guint32 esc_code = 0;
    1199                                  status = cr_tknzr_parse_escape
    1200                                          (a_this, &esc_code, NULL);
    1201                                  if (status == CR_OK) {
    1202                                          g_string_append_unichar
    1203                                                  (str->stryng, 
    1204                                                   esc_code);
    1205                                  } else {
    1206                                          status = CR_OK;
    1207                                          break;
    1208                                  }
    1209                          }
    1210                  }
    1211                  cr_tknzr_try_to_skip_spaces (a_this);
    1212                  READ_NEXT_CHAR (a_this, &cur_char);
    1213                  if (cur_char == ')') {
    1214                          status = CR_OK;
    1215                  } else {
    1216                          status = CR_PARSING_ERROR;
    1217                          goto error;
    1218                  }
    1219                  if (str) {                        
    1220                          if (*a_str == NULL) {
    1221                                  *a_str = str;
    1222                                  str = NULL;
    1223                          } else {
    1224                                  g_string_append_len
    1225                                          ((*a_str)->stryng,
    1226                                           str->stryng->str,
    1227                                           str->stryng->len);
    1228                                  cr_string_destroy (str);
    1229                          }                        
    1230                  }
    1231          }
    1232  
    1233          cr_parsing_location_copy
    1234                  (&(*a_str)->location,
    1235                   &location) ;
    1236          return CR_OK ;
    1237   error:
    1238          if (str) {
    1239                  cr_string_destroy (str);
    1240                  str = NULL;
    1241          }
    1242          cr_tknzr_set_cur_pos (a_this, &init_pos);
    1243          return status;
    1244  }
    1245  
    1246  /**
    1247   *parses an RGB as defined in the css2 spec.
    1248   *rgb: rgb '('S*{num}%?S* ',' {num}#?S*,S*{num}#?S*')'
    1249   *
    1250   *@param a_this the "this pointer" of the current instance of
    1251   *@param a_rgb out parameter the parsed rgb.
    1252   *@return CR_OK upon successfull completion, an error code otherwise.
    1253   */
    1254  static enum CRStatus
    1255  cr_tknzr_parse_rgb (CRTknzr * a_this, CRRgb ** a_rgb)
    1256  {
    1257          enum CRStatus status = CR_OK;
    1258          CRInputPos init_pos;
    1259          CRNum *num = NULL;
    1260          guchar next_bytes[3] = { 0 }, cur_byte = 0;
    1261          glong red = 0,
    1262                  green = 0,
    1263                  blue = 0,
    1264                  i = 0;
    1265          gboolean is_percentage = FALSE;
    1266          CRParsingLocation location = {0} ;
    1267  
    1268          g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
    1269  
    1270          RECORD_INITIAL_POS (a_this, &init_pos);
    1271  
    1272          PEEK_BYTE (a_this, 1, &next_bytes[0]);
    1273          PEEK_BYTE (a_this, 2, &next_bytes[1]);
    1274          PEEK_BYTE (a_this, 3, &next_bytes[2]);
    1275  
    1276          if (((next_bytes[0] == 'r') || (next_bytes[0] == 'R'))
    1277              && ((next_bytes[1] == 'g') || (next_bytes[1] == 'G'))
    1278              && ((next_bytes[2] == 'b') || (next_bytes[2] == 'B'))) {
    1279                  SKIP_CHARS (a_this, 1);
    1280                  cr_tknzr_get_parsing_location (a_this, &location) ;
    1281                  SKIP_CHARS (a_this, 2);
    1282          } else {
    1283                  status = CR_PARSING_ERROR;
    1284                  goto error;
    1285          }
    1286          READ_NEXT_BYTE (a_this, &cur_byte);
    1287          ENSURE_PARSING_COND (cur_byte == '(');
    1288  
    1289          cr_tknzr_try_to_skip_spaces (a_this);
    1290          status = cr_tknzr_parse_num (a_this, &num);
    1291          ENSURE_PARSING_COND ((status == CR_OK) && (num != NULL));
    1292  
    1293          if (num->val > G_MAXLONG) {
    1294                  status = CR_PARSING_ERROR;
    1295                  goto error;
    1296          }
    1297  
    1298          red = num->val;
    1299          cr_num_destroy (num);
    1300          num = NULL;
    1301  
    1302          PEEK_BYTE (a_this, 1, &next_bytes[0]);
    1303          if (next_bytes[0] == '%') {
    1304                  SKIP_CHARS (a_this, 1);
    1305                  is_percentage = TRUE;
    1306          }
    1307          cr_tknzr_try_to_skip_spaces (a_this);
    1308  
    1309          for (i = 0; i < 2; i++) {
    1310                  READ_NEXT_BYTE (a_this, &cur_byte);
    1311                  ENSURE_PARSING_COND (cur_byte == ',');
    1312  
    1313                  cr_tknzr_try_to_skip_spaces (a_this);
    1314                  status = cr_tknzr_parse_num (a_this, &num);
    1315                  ENSURE_PARSING_COND ((status == CR_OK) && (num != NULL));
    1316  
    1317                  if (num->val > G_MAXLONG) {
    1318                          status = CR_PARSING_ERROR;
    1319                          goto error;
    1320                  }
    1321  
    1322                  PEEK_BYTE (a_this, 1, &next_bytes[0]);
    1323                  if (next_bytes[0] == '%') {
    1324                          SKIP_CHARS (a_this, 1);
    1325                          is_percentage = 1;
    1326                  }
    1327  
    1328                  if (i == 0) {
    1329                          green = num->val;
    1330                  } else if (i == 1) {
    1331                          blue = num->val;
    1332                  }
    1333  
    1334                  if (num) {
    1335                          cr_num_destroy (num);
    1336                          num = NULL;
    1337                  }
    1338                  cr_tknzr_try_to_skip_spaces (a_this);
    1339          }
    1340  
    1341          READ_NEXT_BYTE (a_this, &cur_byte);
    1342          if (*a_rgb == NULL) {
    1343                  *a_rgb = cr_rgb_new_with_vals (red, green, blue,
    1344                                                 is_percentage);
    1345  
    1346                  if (*a_rgb == NULL) {
    1347                          status = CR_ERROR;
    1348                          goto error;
    1349                  }
    1350                  status = CR_OK;
    1351          } else {
    1352                  (*a_rgb)->red = red;
    1353                  (*a_rgb)->green = green;
    1354                  (*a_rgb)->blue = blue;
    1355                  (*a_rgb)->is_percentage = is_percentage;
    1356  
    1357                  status = CR_OK;
    1358          }
    1359  
    1360          if (status == CR_OK) {
    1361                  if (a_rgb && *a_rgb) {
    1362                          cr_parsing_location_copy 
    1363                                  (&(*a_rgb)->location, 
    1364                                   &location) ;
    1365                  }
    1366                  return CR_OK;
    1367          }
    1368  
    1369   error:
    1370          if (num) {
    1371                  cr_num_destroy (num);
    1372                  num = NULL;
    1373          }
    1374  
    1375          cr_tknzr_set_cur_pos (a_this, &init_pos);
    1376          return CR_OK;
    1377  }
    1378  
    1379  /**
    1380   *Parses a atkeyword as defined by the css spec in [4.1.1]:
    1381   *ATKEYWORD ::= @{ident}
    1382   *
    1383   *@param a_this the "this pointer" of the current instance of
    1384   *#CRTknzr.
    1385   *
    1386   *@param a_str out parameter. The parsed atkeyword. If *a_str is
    1387   *set to NULL this function allocates a new instance of CRString and
    1388   *sets it to the parsed atkeyword. If not, this function just appends
    1389   *the parsed atkeyword to the end of *a_str. In both cases it is up to
    1390   *the caller to free *a_str.
    1391   *
    1392   *@return CR_OK upon successfull completion, an error code otherwise.
    1393   */
    1394  static enum CRStatus
    1395  cr_tknzr_parse_atkeyword (CRTknzr * a_this, 
    1396                            CRString ** a_str)
    1397  {
    1398          guint32 cur_char = 0;
    1399          CRInputPos init_pos;
    1400          gboolean str_needs_free = FALSE;
    1401          enum CRStatus status = CR_OK;
    1402  
    1403          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1404                                && PRIVATE (a_this)->input
    1405                                && a_str, CR_BAD_PARAM_ERROR);
    1406  
    1407          RECORD_INITIAL_POS (a_this, &init_pos);
    1408  
    1409          READ_NEXT_CHAR (a_this, &cur_char);
    1410  
    1411          if (cur_char != '@') {
    1412                  status = CR_PARSING_ERROR;
    1413                  goto error;
    1414          }
    1415  
    1416          if (*a_str == NULL) {
    1417                  *a_str = cr_string_new ();
    1418                  str_needs_free = TRUE;
    1419          }
    1420          status = cr_tknzr_parse_ident (a_this, a_str);
    1421          if (status != CR_OK) {
    1422                  goto error;
    1423          }
    1424          return CR_OK;
    1425   error:
    1426  
    1427          if (str_needs_free == TRUE && *a_str) {
    1428                  cr_string_destroy (*a_str);
    1429                  *a_str = NULL;
    1430          }
    1431          cr_tknzr_set_cur_pos (a_this, &init_pos);
    1432          return status;
    1433  }
    1434  
    1435  static enum CRStatus
    1436  cr_tknzr_parse_important (CRTknzr * a_this,
    1437                            CRParsingLocation *a_location)
    1438  {
    1439          guint32 cur_char = 0;
    1440          CRInputPos init_pos;
    1441          enum CRStatus status = CR_OK;
    1442  
    1443          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1444                                && PRIVATE (a_this)->input,
    1445                                CR_BAD_PARAM_ERROR);
    1446  
    1447          RECORD_INITIAL_POS (a_this, &init_pos);
    1448          READ_NEXT_CHAR (a_this, &cur_char);
    1449          ENSURE_PARSING_COND (cur_char == '!');
    1450          if (a_location) {
    1451                  cr_tknzr_get_parsing_location (a_this, 
    1452                                                 a_location) ;
    1453          }
    1454          cr_tknzr_try_to_skip_spaces (a_this);
    1455  
    1456          if (BYTE (PRIVATE (a_this)->input, 1, NULL) == 'i'
    1457              && BYTE (PRIVATE (a_this)->input, 2, NULL) == 'm'
    1458              && BYTE (PRIVATE (a_this)->input, 3, NULL) == 'p'
    1459              && BYTE (PRIVATE (a_this)->input, 4, NULL) == 'o'
    1460              && BYTE (PRIVATE (a_this)->input, 5, NULL) == 'r'
    1461              && BYTE (PRIVATE (a_this)->input, 6, NULL) == 't'
    1462              && BYTE (PRIVATE (a_this)->input, 7, NULL) == 'a'
    1463              && BYTE (PRIVATE (a_this)->input, 8, NULL) == 'n'
    1464              && BYTE (PRIVATE (a_this)->input, 9, NULL) == 't') {
    1465                  SKIP_BYTES (a_this, 9);
    1466                  if (a_location) {
    1467                          cr_tknzr_get_parsing_location (a_this,
    1468                                                         a_location) ;
    1469                  }
    1470                  return CR_OK;
    1471          } else {
    1472                  status = CR_PARSING_ERROR;
    1473          }
    1474  
    1475   error:
    1476          cr_tknzr_set_cur_pos (a_this, &init_pos);
    1477  
    1478          return status;
    1479  }
    1480  
    1481  /**
    1482   *Parses a num as defined in the css spec [4.1.1]:
    1483   *[0-9]+|[0-9]*\.[0-9]+
    1484   *@param a_this the current instance of #CRTknzr.
    1485   *@param a_num out parameter. The parsed number.
    1486   *@return CR_OK upon successfull completion, 
    1487   *an error code otherwise.
    1488   *
    1489   *The CSS specification says that numbers may be
    1490   *preceeded by '+' or '-' to indicate the sign.
    1491   *Technically, the "num" construction as defined
    1492   *by the tokenizer doesn't allow this, but we parse
    1493   *it here for simplicity.
    1494   */
    1495  static enum CRStatus
    1496  cr_tknzr_parse_num (CRTknzr * a_this, 
    1497                      CRNum ** a_num)
    1498  {
    1499          enum CRStatus status = CR_PARSING_ERROR;
    1500          enum CRNumType val_type = NUM_GENERIC;
    1501          gboolean parsing_dec,  /* true iff seen decimal point. */
    1502                  parsed; /* true iff the substring seen so far is a valid CSS
    1503                             number, i.e. `[0-9]+|[0-9]*\.[0-9]+'. */
    1504          guint32 cur_char = 0,
    1505                  next_char = 0;
    1506          gdouble numerator, denominator = 1;
    1507          CRInputPos init_pos;
    1508          CRParsingLocation location = {0} ;
    1509          int sign = 1;
    1510  
    1511          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1512                                && PRIVATE (a_this)->input, 
    1513                                CR_BAD_PARAM_ERROR);
    1514  
    1515          RECORD_INITIAL_POS (a_this, &init_pos);
    1516          READ_NEXT_CHAR (a_this, &cur_char);
    1517  
    1518          if (cur_char == '+' || cur_char == '-') {
    1519                  if (cur_char == '-') {
    1520                          sign = -1;
    1521                  }
    1522                  READ_NEXT_CHAR (a_this, &cur_char);
    1523          }
    1524  
    1525          if (IS_NUM (cur_char)) {
    1526                  numerator = (cur_char - '0');
    1527                  parsing_dec = FALSE;
    1528                  parsed = TRUE;
    1529          } else if (cur_char == '.') {
    1530                  numerator = 0;
    1531                  parsing_dec = TRUE;
    1532                  parsed = FALSE;
    1533          } else {
    1534                  status = CR_PARSING_ERROR;
    1535                  goto error;
    1536          }
    1537          cr_tknzr_get_parsing_location (a_this, &location) ;
    1538  
    1539          for (;;) {
    1540                  status = cr_tknzr_peek_char (a_this, &next_char);
    1541                  if (status != CR_OK) {
    1542                          if (status == CR_END_OF_INPUT_ERROR)
    1543                                  status = CR_OK;
    1544                          break;
    1545                  }
    1546                  if (next_char == '.') {
    1547                          if (parsing_dec) {
    1548                                  status = CR_PARSING_ERROR;
    1549                                  goto error;
    1550                          }
    1551  
    1552                          READ_NEXT_CHAR (a_this, &cur_char);
    1553                          parsing_dec = TRUE;
    1554                          parsed = FALSE;  /* In CSS, there must be at least
    1555                                              one digit after `.'. */
    1556                  } else if (IS_NUM (next_char)) {
    1557                          READ_NEXT_CHAR (a_this, &cur_char);
    1558                          parsed = TRUE;
    1559  
    1560                          numerator = numerator * 10 + (cur_char - '0');
    1561                          if (parsing_dec) {
    1562                                  denominator *= 10;
    1563                          }
    1564                  } else {
    1565                          break;
    1566                  }
    1567          }
    1568  
    1569          if (!parsed) {
    1570                  status = CR_PARSING_ERROR;
    1571          }
    1572  
    1573          /*
    1574           *Now, set the output param values.
    1575           */
    1576          if (status == CR_OK) {
    1577                  gdouble val = (numerator / denominator) * sign;
    1578                  if (*a_num == NULL) {
    1579                          *a_num = cr_num_new_with_val (val, val_type);
    1580  
    1581                          if (*a_num == NULL) {
    1582                                  status = CR_ERROR;
    1583                                  goto error;
    1584                          }
    1585                  } else {
    1586                          (*a_num)->val = val;
    1587                          (*a_num)->type = val_type;
    1588                  }
    1589                  cr_parsing_location_copy (&(*a_num)->location,
    1590                                            &location) ;
    1591                  return CR_OK;
    1592          }
    1593  
    1594   error:
    1595  
    1596          cr_tknzr_set_cur_pos (a_this, &init_pos);
    1597  
    1598          return status;
    1599  }
    1600  
    1601  /*********************************************
    1602   *PUBLIC methods
    1603   ********************************************/
    1604  
    1605  CRTknzr *
    1606  cr_tknzr_new (CRInput * a_input)
    1607  {
    1608          CRTknzr *result = NULL;
    1609  
    1610          result = g_try_malloc (sizeof (CRTknzr));
    1611  
    1612          if (result == NULL) {
    1613                  cr_utils_trace_info ("Out of memory");
    1614                  return NULL;
    1615          }
    1616  
    1617          memset (result, 0, sizeof (CRTknzr));
    1618  
    1619          result->priv = g_try_malloc (sizeof (CRTknzrPriv));
    1620  
    1621          if (result->priv == NULL) {
    1622                  cr_utils_trace_info ("Out of memory");
    1623  
    1624                  if (result) {
    1625                          g_free (result);
    1626                          result = NULL;
    1627                  }
    1628  
    1629                  return NULL;
    1630          }
    1631          memset (result->priv, 0, sizeof (CRTknzrPriv));
    1632          if (a_input)
    1633                  cr_tknzr_set_input (result, a_input);
    1634          return result;
    1635  }
    1636  
    1637  CRTknzr *
    1638  cr_tknzr_new_from_buf (guchar * a_buf, gulong a_len,
    1639                         enum CREncoding a_enc, 
    1640                         gboolean a_free_at_destroy)
    1641  {
    1642          CRTknzr *result = NULL;
    1643          CRInput *input = NULL;
    1644  
    1645          input = cr_input_new_from_buf (a_buf, a_len, a_enc,
    1646                                         a_free_at_destroy);
    1647  
    1648          g_return_val_if_fail (input != NULL, NULL);
    1649  
    1650          result = cr_tknzr_new (input);
    1651  
    1652          return result;
    1653  }
    1654  
    1655  CRTknzr *
    1656  cr_tknzr_new_from_uri (const guchar * a_file_uri, 
    1657                         enum CREncoding a_enc)
    1658  {
    1659          CRTknzr *result = NULL;
    1660          CRInput *input = NULL;
    1661  
    1662          input = cr_input_new_from_uri ((const gchar *) a_file_uri, a_enc);
    1663          g_return_val_if_fail (input != NULL, NULL);
    1664  
    1665          result = cr_tknzr_new (input);
    1666  
    1667          return result;
    1668  }
    1669  
    1670  void
    1671  cr_tknzr_ref (CRTknzr * a_this)
    1672  {
    1673          g_return_if_fail (a_this && PRIVATE (a_this));
    1674  
    1675          PRIVATE (a_this)->ref_count++;
    1676  }
    1677  
    1678  gboolean
    1679  cr_tknzr_unref (CRTknzr * a_this)
    1680  {
    1681          g_return_val_if_fail (a_this && PRIVATE (a_this), FALSE);
    1682  
    1683          if (PRIVATE (a_this)->ref_count > 0) {
    1684                  PRIVATE (a_this)->ref_count--;
    1685          }
    1686  
    1687          if (PRIVATE (a_this)->ref_count == 0) {
    1688                  cr_tknzr_destroy (a_this);
    1689                  return TRUE;
    1690          }
    1691  
    1692          return FALSE;
    1693  }
    1694  
    1695  enum CRStatus
    1696  cr_tknzr_set_input (CRTknzr * a_this, CRInput * a_input)
    1697  {
    1698          g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
    1699  
    1700          if (PRIVATE (a_this)->input) {
    1701                  cr_input_unref (PRIVATE (a_this)->input);
    1702          }
    1703  
    1704          PRIVATE (a_this)->input = a_input;
    1705  
    1706          cr_input_ref (PRIVATE (a_this)->input);
    1707  
    1708          return CR_OK;
    1709  }
    1710  
    1711  enum CRStatus
    1712  cr_tknzr_get_input (CRTknzr * a_this, CRInput ** a_input)
    1713  {
    1714          g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
    1715  
    1716          *a_input = PRIVATE (a_this)->input;
    1717  
    1718          return CR_OK;
    1719  }
    1720  
    1721  /*********************************
    1722   *Tokenizer input handling routines
    1723   *********************************/
    1724  
    1725  /**
    1726   *Reads the next byte from the parser input stream.
    1727   *@param a_this the "this pointer" of the current instance of
    1728   *#CRParser.
    1729   *@param a_byte out parameter the place where to store the byte
    1730   *read.
    1731   *@return CR_OK upon successfull completion, an error 
    1732   *code otherwise.
    1733   */
    1734  enum CRStatus
    1735  cr_tknzr_read_byte (CRTknzr * a_this, guchar * a_byte)
    1736  {
    1737          g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
    1738  
    1739          return cr_input_read_byte (PRIVATE (a_this)->input, a_byte);
    1740  
    1741  }
    1742  
    1743  /**
    1744   *Reads the next char from the parser input stream.
    1745   *@param a_this the current instance of #CRTknzr.
    1746   *@param a_char out parameter. The read char.
    1747   *@return CR_OK upon successfull completion, an error code
    1748   *otherwise.
    1749   */
    1750  enum CRStatus
    1751  cr_tknzr_read_char (CRTknzr * a_this, guint32 * a_char)
    1752  {
    1753          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1754                                && PRIVATE (a_this)->input
    1755                                && a_char, CR_BAD_PARAM_ERROR);
    1756  
    1757          if (PRIVATE (a_this)->token_cache) {
    1758                  cr_input_set_cur_pos (PRIVATE (a_this)->input,
    1759                                        &PRIVATE (a_this)->prev_pos);
    1760                  cr_token_destroy (PRIVATE (a_this)->token_cache);
    1761                  PRIVATE (a_this)->token_cache = NULL;
    1762          }
    1763  
    1764          return cr_input_read_char (PRIVATE (a_this)->input, a_char);
    1765  }
    1766  
    1767  /**
    1768   *Peeks a char from the parser input stream.
    1769   *To "peek a char" means reads the next char without consuming it.
    1770   *Subsequent calls to this function return the same char.
    1771   *@param a_this the current instance of #CRTknzr.
    1772   *@param a_char out parameter. The peeked char uppon successfull completion.
    1773   *@return CR_OK upon successfull completion, an error code otherwise.
    1774   */
    1775  enum CRStatus
    1776  cr_tknzr_peek_char (CRTknzr * a_this, guint32 * a_char)
    1777  {
    1778          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1779                                && PRIVATE (a_this)->input
    1780                                && a_char, CR_BAD_PARAM_ERROR);
    1781  
    1782          if (PRIVATE (a_this)->token_cache) {
    1783                  cr_input_set_cur_pos (PRIVATE (a_this)->input,
    1784                                        &PRIVATE (a_this)->prev_pos);
    1785                  cr_token_destroy (PRIVATE (a_this)->token_cache);
    1786                  PRIVATE (a_this)->token_cache = NULL;
    1787          }
    1788  
    1789          return cr_input_peek_char (PRIVATE (a_this)->input, a_char);
    1790  }
    1791  
    1792  /**
    1793   *Peeks a byte ahead at a given postion in the parser input stream.
    1794   *@param a_this the current instance of #CRTknzr.
    1795   *@param a_offset the offset of the peeked byte starting from the current
    1796   *byte in the parser input stream.
    1797   *@param a_byte out parameter. The peeked byte upon 
    1798   *successfull completion.
    1799   *@return CR_OK upon successfull completion, an error code otherwise.
    1800   */
    1801  enum CRStatus
    1802  cr_tknzr_peek_byte (CRTknzr * a_this, gulong a_offset, guchar * a_byte)
    1803  {
    1804          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1805                                && PRIVATE (a_this)->input && a_byte,
    1806                                CR_BAD_PARAM_ERROR);
    1807  
    1808          if (PRIVATE (a_this)->token_cache) {
    1809                  cr_input_set_cur_pos (PRIVATE (a_this)->input,
    1810                                        &PRIVATE (a_this)->prev_pos);
    1811                  cr_token_destroy (PRIVATE (a_this)->token_cache);
    1812                  PRIVATE (a_this)->token_cache = NULL;
    1813          }
    1814  
    1815          return cr_input_peek_byte (PRIVATE (a_this)->input,
    1816                                     CR_SEEK_CUR, a_offset, a_byte);
    1817  }
    1818  
    1819  /**
    1820   *Same as cr_tknzr_peek_byte() but this api returns the byte peeked.
    1821   *@param a_this the current instance of #CRTknzr.
    1822   *@param a_offset the offset of the peeked byte starting from the current
    1823   *byte in the parser input stream.
    1824   *@param a_eof out parameter. If not NULL, is set to TRUE if we reached end of
    1825   *file, FALE otherwise. If the caller sets it to NULL, this parameter 
    1826   *is just ignored.
    1827   *@return the peeked byte.
    1828   */
    1829  guchar
    1830  cr_tknzr_peek_byte2 (CRTknzr * a_this, gulong a_offset, gboolean * a_eof)
    1831  {
    1832          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1833                                && PRIVATE (a_this)->input, 0);
    1834  
    1835          return cr_input_peek_byte2 (PRIVATE (a_this)->input, a_offset, a_eof);
    1836  }
    1837  
    1838  /**
    1839   *Gets the number of bytes left in the topmost input stream
    1840   *associated to this parser.
    1841   *@param a_this the current instance of #CRTknzr
    1842   *@return the number of bytes left or -1 in case of error.
    1843   */
    1844  glong
    1845  cr_tknzr_get_nb_bytes_left (CRTknzr * a_this)
    1846  {
    1847          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1848                                && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
    1849  
    1850          if (PRIVATE (a_this)->token_cache) {
    1851                  cr_input_set_cur_pos (PRIVATE (a_this)->input,
    1852                                        &PRIVATE (a_this)->prev_pos);
    1853                  cr_token_destroy (PRIVATE (a_this)->token_cache);
    1854                  PRIVATE (a_this)->token_cache = NULL;
    1855          }
    1856  
    1857          return cr_input_get_nb_bytes_left (PRIVATE (a_this)->input);
    1858  }
    1859  
    1860  enum CRStatus
    1861  cr_tknzr_get_cur_pos (CRTknzr * a_this, CRInputPos * a_pos)
    1862  {
    1863          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1864                                && PRIVATE (a_this)->input
    1865                                && a_pos, CR_BAD_PARAM_ERROR);
    1866  
    1867          if (PRIVATE (a_this)->token_cache) {
    1868                  cr_input_set_cur_pos (PRIVATE (a_this)->input,
    1869                                        &PRIVATE (a_this)->prev_pos);
    1870                  cr_token_destroy (PRIVATE (a_this)->token_cache);
    1871                  PRIVATE (a_this)->token_cache = NULL;
    1872          }
    1873  
    1874          return cr_input_get_cur_pos (PRIVATE (a_this)->input, a_pos);
    1875  }
    1876  
    1877  enum CRStatus 
    1878  cr_tknzr_get_parsing_location (CRTknzr *a_this,
    1879                                 CRParsingLocation *a_loc)
    1880  {
    1881          g_return_val_if_fail (a_this 
    1882                                && PRIVATE (a_this)
    1883                                && a_loc,
    1884                                CR_BAD_PARAM_ERROR) ;
    1885  
    1886          return cr_input_get_parsing_location 
    1887                  (PRIVATE (a_this)->input, a_loc) ;
    1888  }
    1889  
    1890  enum CRStatus
    1891  cr_tknzr_get_cur_byte_addr (CRTknzr * a_this, guchar ** a_addr)
    1892  {
    1893          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1894                                && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
    1895          if (PRIVATE (a_this)->token_cache) {
    1896                  cr_input_set_cur_pos (PRIVATE (a_this)->input,
    1897                                        &PRIVATE (a_this)->prev_pos);
    1898                  cr_token_destroy (PRIVATE (a_this)->token_cache);
    1899                  PRIVATE (a_this)->token_cache = NULL;
    1900          }
    1901  
    1902          return cr_input_get_cur_byte_addr (PRIVATE (a_this)->input, a_addr);
    1903  }
    1904  
    1905  enum CRStatus
    1906  cr_tknzr_seek_index (CRTknzr * a_this, enum CRSeekPos a_origin, gint a_pos)
    1907  {
    1908          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1909                                && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
    1910  
    1911          if (PRIVATE (a_this)->token_cache) {
    1912                  cr_input_set_cur_pos (PRIVATE (a_this)->input,
    1913                                        &PRIVATE (a_this)->prev_pos);
    1914                  cr_token_destroy (PRIVATE (a_this)->token_cache);
    1915                  PRIVATE (a_this)->token_cache = NULL;
    1916          }
    1917  
    1918          return cr_input_seek_index (PRIVATE (a_this)->input, a_origin, a_pos);
    1919  }
    1920  
    1921  enum CRStatus
    1922  cr_tknzr_consume_chars (CRTknzr * a_this, guint32 a_char, glong * a_nb_char)
    1923  {
    1924  	gulong consumed = *(gulong *) a_nb_char;
    1925  	enum CRStatus status;
    1926          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1927                                && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
    1928  
    1929          if (PRIVATE (a_this)->token_cache) {
    1930                  cr_input_set_cur_pos (PRIVATE (a_this)->input,
    1931                                        &PRIVATE (a_this)->prev_pos);
    1932                  cr_token_destroy (PRIVATE (a_this)->token_cache);
    1933                  PRIVATE (a_this)->token_cache = NULL;
    1934          }
    1935  
    1936          status = cr_input_consume_chars (PRIVATE (a_this)->input,
    1937                                           a_char, &consumed);
    1938  	*a_nb_char = (glong) consumed;
    1939  	return status;
    1940  }
    1941  
    1942  enum CRStatus
    1943  cr_tknzr_set_cur_pos (CRTknzr * a_this, CRInputPos * a_pos)
    1944  {
    1945          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1946                                && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
    1947  
    1948          if (PRIVATE (a_this)->token_cache) {
    1949                  cr_token_destroy (PRIVATE (a_this)->token_cache);
    1950                  PRIVATE (a_this)->token_cache = NULL;
    1951          }
    1952  
    1953          return cr_input_set_cur_pos (PRIVATE (a_this)->input, a_pos);
    1954  }
    1955  
    1956  enum CRStatus
    1957  cr_tknzr_unget_token (CRTknzr * a_this, CRToken * a_token)
    1958  {
    1959          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1960                                && PRIVATE (a_this)->token_cache == NULL,
    1961                                CR_BAD_PARAM_ERROR);
    1962  
    1963          PRIVATE (a_this)->token_cache = a_token;
    1964  
    1965          return CR_OK;
    1966  }
    1967  
    1968  /**
    1969   *Returns the next token of the input stream.
    1970   *This method is really central. Each parsing
    1971   *method calls it.
    1972   *@param a_this the current tokenizer.
    1973   *@param a_tk out parameter. The returned token.
    1974   *for the sake of mem leak avoidance, *a_tk must
    1975   *be NULL.
    1976   *@param CR_OK upon successfull completion, an error code
    1977   *otherwise.
    1978   */
    1979  enum CRStatus
    1980  cr_tknzr_get_next_token (CRTknzr * a_this, CRToken ** a_tk)
    1981  {
    1982          enum CRStatus status = CR_OK;
    1983          CRToken *token = NULL;
    1984          CRInputPos init_pos;
    1985          guint32 next_char = 0;
    1986          guchar next_bytes[4] = { 0 };
    1987          gboolean reached_eof = FALSE;
    1988          CRInput *input = NULL;
    1989          CRString *str = NULL;
    1990          CRRgb *rgb = NULL;
    1991          CRParsingLocation location = {0} ;
    1992  
    1993          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1994                                && a_tk && *a_tk == NULL
    1995                                && PRIVATE (a_this)->input, 
    1996                                CR_BAD_PARAM_ERROR);
    1997  
    1998          if (PRIVATE (a_this)->token_cache) {
    1999                  *a_tk = PRIVATE (a_this)->token_cache;
    2000                  PRIVATE (a_this)->token_cache = NULL;
    2001                  return CR_OK;
    2002          }
    2003  
    2004          RECORD_INITIAL_POS (a_this, &init_pos);
    2005  
    2006          status = cr_input_get_end_of_file
    2007                  (PRIVATE (a_this)->input, &reached_eof);
    2008          ENSURE_PARSING_COND (status == CR_OK);
    2009  
    2010          if (reached_eof == TRUE) {
    2011                  status = CR_END_OF_INPUT_ERROR;
    2012                  goto error;
    2013          }
    2014  
    2015          input = PRIVATE (a_this)->input;
    2016  
    2017          PEEK_NEXT_CHAR (a_this, &next_char);
    2018          token = cr_token_new ();
    2019          ENSURE_PARSING_COND (token);
    2020  
    2021          switch (next_char) {
    2022          case '@':
    2023                  {
    2024                          if (BYTE (input, 2, NULL) == 'f'
    2025                              && BYTE (input, 3, NULL) == 'o'
    2026                              && BYTE (input, 4, NULL) == 'n'
    2027                              && BYTE (input, 5, NULL) == 't'
    2028                              && BYTE (input, 6, NULL) == '-'
    2029                              && BYTE (input, 7, NULL) == 'f'
    2030                              && BYTE (input, 8, NULL) == 'a'
    2031                              && BYTE (input, 9, NULL) == 'c'
    2032                              && BYTE (input, 10, NULL) == 'e') {
    2033                                  SKIP_CHARS (a_this, 1);
    2034                                  cr_tknzr_get_parsing_location 
    2035                                          (a_this, &location) ;
    2036                                  SKIP_CHARS (a_this, 9);
    2037                                  status = cr_token_set_font_face_sym (token);
    2038                                  CHECK_PARSING_STATUS (status, TRUE);
    2039                                  cr_parsing_location_copy (&token->location,
    2040                                                            &location) ;
    2041                                  goto done;
    2042                          }
    2043  
    2044                          if (BYTE (input, 2, NULL) == 'c'
    2045                              && BYTE (input, 3, NULL) == 'h'
    2046                              && BYTE (input, 4, NULL) == 'a'
    2047                              && BYTE (input, 5, NULL) == 'r'
    2048                              && BYTE (input, 6, NULL) == 's'
    2049                              && BYTE (input, 7, NULL) == 'e'
    2050                              && BYTE (input, 8, NULL) == 't') {
    2051                                  SKIP_CHARS (a_this, 1);
    2052                                  cr_tknzr_get_parsing_location
    2053                                          (a_this, &location) ;
    2054                                  SKIP_CHARS (a_this, 7);
    2055                                  status = cr_token_set_charset_sym (token);
    2056                                  CHECK_PARSING_STATUS (status, TRUE);
    2057                                  cr_parsing_location_copy (&token->location,
    2058                                                            &location) ;
    2059                                  goto done;
    2060                          }
    2061  
    2062                          if (BYTE (input, 2, NULL) == 'i'
    2063                              && BYTE (input, 3, NULL) == 'm'
    2064                              && BYTE (input, 4, NULL) == 'p'
    2065                              && BYTE (input, 5, NULL) == 'o'
    2066                              && BYTE (input, 6, NULL) == 'r'
    2067                              && BYTE (input, 7, NULL) == 't') {
    2068                                  SKIP_CHARS (a_this, 1);
    2069                                  cr_tknzr_get_parsing_location 
    2070                                          (a_this, &location) ;
    2071                                  SKIP_CHARS (a_this, 6);
    2072                                  status = cr_token_set_import_sym (token);
    2073                                  CHECK_PARSING_STATUS (status, TRUE);
    2074                                  cr_parsing_location_copy (&token->location,
    2075                                                            &location) ;
    2076                                  goto done;
    2077                          }
    2078  
    2079                          if (BYTE (input, 2, NULL) == 'm'
    2080                              && BYTE (input, 3, NULL) == 'e'
    2081                              && BYTE (input, 4, NULL) == 'd'
    2082                              && BYTE (input, 5, NULL) == 'i'
    2083                              && BYTE (input, 6, NULL) == 'a') {
    2084                                  SKIP_CHARS (a_this, 1);
    2085                                  cr_tknzr_get_parsing_location (a_this, 
    2086                                                                 &location) ;
    2087                                  SKIP_CHARS (a_this, 5);
    2088                                  status = cr_token_set_media_sym (token);
    2089                                  CHECK_PARSING_STATUS (status, TRUE);
    2090                                  cr_parsing_location_copy (&token->location, 
    2091                                                            &location) ;
    2092                                  goto done;
    2093                          }
    2094  
    2095                          if (BYTE (input, 2, NULL) == 'p'
    2096                              && BYTE (input, 3, NULL) == 'a'
    2097                              && BYTE (input, 4, NULL) == 'g'
    2098                              && BYTE (input, 5, NULL) == 'e') {
    2099                                  SKIP_CHARS (a_this, 1);
    2100                                  cr_tknzr_get_parsing_location (a_this, 
    2101                                                                 &location) ;
    2102                                  SKIP_CHARS (a_this, 4);
    2103                                  status = cr_token_set_page_sym (token);
    2104                                  CHECK_PARSING_STATUS (status, TRUE);
    2105                                  cr_parsing_location_copy (&token->location, 
    2106                                                            &location) ;
    2107                                  goto done;
    2108                          }
    2109                          status = cr_tknzr_parse_atkeyword (a_this, &str);
    2110                          if (status == CR_OK) {
    2111                                  status = cr_token_set_atkeyword (token, str);
    2112                                  CHECK_PARSING_STATUS (status, TRUE);
    2113                                  if (str) {
    2114                                          cr_parsing_location_copy (&token->location, 
    2115                                                                    &str->location) ;
    2116                                  }
    2117                                  goto done;
    2118                          }
    2119                  }
    2120                  break;
    2121  
    2122          case 'u':
    2123  
    2124                  if (BYTE (input, 2, NULL) == 'r'
    2125                      && BYTE (input, 3, NULL) == 'l'
    2126                      && BYTE (input, 4, NULL) == '(') {
    2127                          CRString *str2 = NULL;
    2128  
    2129                          status = cr_tknzr_parse_uri (a_this, &str2);
    2130                          if (status == CR_OK) {
    2131                                  status = cr_token_set_uri (token, str2);
    2132                                  CHECK_PARSING_STATUS (status, TRUE);
    2133                                  if (str2) {
    2134                                          cr_parsing_location_copy (&token->location,
    2135                                                                    &str2->location) ;
    2136                                  }
    2137                                  goto done;
    2138                          }
    2139                  } 
    2140                  goto fallback;
    2141                  break;
    2142  
    2143          case 'r':
    2144                  if (BYTE (input, 2, NULL) == 'g'
    2145                      && BYTE (input, 3, NULL) == 'b'
    2146                      && BYTE (input, 4, NULL) == '(') {
    2147                          status = cr_tknzr_parse_rgb (a_this, &rgb);
    2148                          if (status == CR_OK && rgb) {
    2149                                  status = cr_token_set_rgb (token, rgb);
    2150                                  CHECK_PARSING_STATUS (status, TRUE);
    2151                                  if (rgb) {
    2152                                          cr_parsing_location_copy (&token->location, 
    2153                                                                    &rgb->location) ;
    2154                                  }
    2155                                  rgb = NULL;
    2156                                  goto done;
    2157                          }
    2158  
    2159                  }
    2160                  goto fallback;
    2161                  break;
    2162  
    2163          case '<':
    2164                  if (BYTE (input, 2, NULL) == '!'
    2165                      && BYTE (input, 3, NULL) == '-'
    2166                      && BYTE (input, 4, NULL) == '-') {
    2167                          SKIP_CHARS (a_this, 1);
    2168                          cr_tknzr_get_parsing_location (a_this, 
    2169                                                         &location) ;
    2170                          SKIP_CHARS (a_this, 3);
    2171                          status = cr_token_set_cdo (token);
    2172                          CHECK_PARSING_STATUS (status, TRUE);
    2173                          cr_parsing_location_copy (&token->location, 
    2174                                                    &location) ;
    2175                          goto done;
    2176                  }
    2177                  break;
    2178  
    2179          case '-':
    2180                  if (BYTE (input, 2, NULL) == '-'
    2181                      && BYTE (input, 3, NULL) == '>') {
    2182                          SKIP_CHARS (a_this, 1);
    2183                          cr_tknzr_get_parsing_location (a_this, 
    2184                                                         &location) ;
    2185                          SKIP_CHARS (a_this, 2);
    2186                          status = cr_token_set_cdc (token);
    2187                          CHECK_PARSING_STATUS (status, TRUE);
    2188                          cr_parsing_location_copy (&token->location, 
    2189                                                    &location) ;
    2190                          goto done;
    2191                  } else {
    2192                          status = cr_tknzr_parse_ident
    2193                                  (a_this, &str);
    2194                          if (status == CR_OK) {
    2195                                  cr_token_set_ident
    2196                                          (token, str);
    2197                                  if (str) {
    2198                                          cr_parsing_location_copy (&token->location, 
    2199                                                                    &str->location) ;
    2200                                  }
    2201                                  goto done;
    2202                          } else {
    2203                                  goto parse_number;
    2204                          }
    2205                  }
    2206                  break;
    2207  
    2208          case '~':
    2209                  if (BYTE (input, 2, NULL) == '=') {
    2210                          SKIP_CHARS (a_this, 1);
    2211                          cr_tknzr_get_parsing_location (a_this, 
    2212                                                         &location) ;
    2213                          SKIP_CHARS (a_this, 1);
    2214                          status = cr_token_set_includes (token);
    2215                          CHECK_PARSING_STATUS (status, TRUE);
    2216                          cr_parsing_location_copy (&token->location, 
    2217                                                    &location) ;
    2218                          goto done;
    2219                  }
    2220                  break;
    2221  
    2222          case '|':
    2223                  if (BYTE (input, 2, NULL) == '=') {
    2224                          SKIP_CHARS (a_this, 1);
    2225                          cr_tknzr_get_parsing_location (a_this, 
    2226                                                         &location) ;
    2227                          SKIP_CHARS (a_this, 1);
    2228                          status = cr_token_set_dashmatch (token);
    2229                          CHECK_PARSING_STATUS (status, TRUE);
    2230                          cr_parsing_location_copy (&token->location,
    2231                                                    &location) ;
    2232                          goto done;
    2233                  }
    2234                  break;
    2235  
    2236          case '/':
    2237                  if (BYTE (input, 2, NULL) == '*') {
    2238                          status = cr_tknzr_parse_comment (a_this, &str);
    2239  
    2240                          if (status == CR_OK) {
    2241                                  status = cr_token_set_comment (token, str);
    2242                                  str = NULL;
    2243                                  CHECK_PARSING_STATUS (status, TRUE);
    2244                                  if (str) {
    2245                                          cr_parsing_location_copy (&token->location, 
    2246                                                                    &str->location) ;
    2247                                  }
    2248                                  goto done;
    2249                          }
    2250                  }
    2251                  break ;
    2252  
    2253          case ';':
    2254                  SKIP_CHARS (a_this, 1);
    2255                  cr_tknzr_get_parsing_location (a_this, 
    2256                                                 &location) ;
    2257                  status = cr_token_set_semicolon (token);
    2258                  CHECK_PARSING_STATUS (status, TRUE);
    2259                  cr_parsing_location_copy (&token->location, 
    2260                                            &location) ;
    2261                  goto done;
    2262  
    2263          case '{':
    2264                  SKIP_CHARS (a_this, 1);
    2265                  cr_tknzr_get_parsing_location (a_this, 
    2266                                                 &location) ;
    2267                  status = cr_token_set_cbo (token);
    2268                  CHECK_PARSING_STATUS (status, TRUE);
    2269                  cr_tknzr_get_parsing_location (a_this, 
    2270                                                 &location) ;
    2271                  goto done;
    2272  
    2273          case '}':
    2274                  SKIP_CHARS (a_this, 1);
    2275                  cr_tknzr_get_parsing_location (a_this, 
    2276                                                 &location) ;
    2277                  status = cr_token_set_cbc (token);
    2278                  CHECK_PARSING_STATUS (status, TRUE);
    2279                  cr_parsing_location_copy (&token->location, 
    2280                                            &location) ;
    2281                  goto done;
    2282  
    2283          case '(':
    2284                  SKIP_CHARS (a_this, 1);
    2285                  cr_tknzr_get_parsing_location (a_this, 
    2286                                                 &location) ;
    2287                  status = cr_token_set_po (token);
    2288                  CHECK_PARSING_STATUS (status, TRUE);
    2289                  cr_parsing_location_copy (&token->location, 
    2290                                            &location) ;
    2291                  goto done;
    2292  
    2293          case ')':
    2294                  SKIP_CHARS (a_this, 1);
    2295                  cr_tknzr_get_parsing_location (a_this, 
    2296                                                 &location) ;
    2297                  status = cr_token_set_pc (token);
    2298                  CHECK_PARSING_STATUS (status, TRUE);
    2299                  cr_parsing_location_copy (&token->location, 
    2300                                            &location) ;
    2301                  goto done;
    2302  
    2303          case '[':
    2304                  SKIP_CHARS (a_this, 1);
    2305                  cr_tknzr_get_parsing_location (a_this, 
    2306                                                 &location) ;
    2307                  status = cr_token_set_bo (token);
    2308                  CHECK_PARSING_STATUS (status, TRUE);
    2309                  cr_parsing_location_copy (&token->location, 
    2310                                            &location) ;
    2311                  goto done;
    2312  
    2313          case ']':
    2314                  SKIP_CHARS (a_this, 1);
    2315                  cr_tknzr_get_parsing_location (a_this, 
    2316                                                 &location) ;
    2317                  status = cr_token_set_bc (token);
    2318                  CHECK_PARSING_STATUS (status, TRUE);
    2319                  cr_parsing_location_copy (&token->location, 
    2320                                            &location) ;
    2321                  goto done;
    2322  
    2323          case ' ':
    2324          case '\t':
    2325          case '\n':
    2326          case '\f':
    2327          case '\r':
    2328                  {
    2329                          guchar *start = NULL,
    2330                                  *end = NULL;
    2331  
    2332                          status = cr_tknzr_parse_w (a_this, &start, 
    2333                                                     &end, &location);
    2334                          if (status == CR_OK) {
    2335                                  status = cr_token_set_s (token);
    2336                                  CHECK_PARSING_STATUS (status, TRUE);
    2337                                  cr_tknzr_get_parsing_location (a_this, 
    2338                                                                 &location) ;
    2339                                  goto done;
    2340                          }
    2341                  }
    2342                  break;
    2343  
    2344          case '#':
    2345                  {
    2346                          status = cr_tknzr_parse_hash (a_this, &str);
    2347                          if (status == CR_OK && str) {
    2348                                  status = cr_token_set_hash (token, str);
    2349                                  CHECK_PARSING_STATUS (status, TRUE);
    2350                                  if (str) {
    2351                                          cr_parsing_location_copy (&token->location,
    2352                                                                    &str->location) ;
    2353                                  }
    2354                                  str = NULL;
    2355                                  goto done;
    2356                          }
    2357                  }
    2358                  break;
    2359  
    2360          case '\'':
    2361          case '"':
    2362                  status = cr_tknzr_parse_string (a_this, &str);
    2363                  if (status == CR_OK && str) {
    2364                          status = cr_token_set_string (token, str);
    2365                          CHECK_PARSING_STATUS (status, TRUE);
    2366                          if (str) {
    2367                                  cr_parsing_location_copy (&token->location, 
    2368                                                            &str->location) ;
    2369                          }
    2370                          str = NULL;
    2371                          goto done;
    2372                  }
    2373                  break;
    2374  
    2375          case '!':
    2376                  status = cr_tknzr_parse_important (a_this, &location);
    2377                  if (status == CR_OK) {
    2378                          status = cr_token_set_important_sym (token);
    2379                          CHECK_PARSING_STATUS (status, TRUE);
    2380                          cr_parsing_location_copy (&token->location, 
    2381                                                    &location) ;
    2382                          goto done;
    2383                  }
    2384                  break;
    2385  
    2386          case '0':
    2387          case '1':
    2388          case '2':
    2389          case '3':
    2390          case '4':
    2391          case '5':
    2392          case '6':
    2393          case '7':
    2394          case '8':
    2395          case '9':
    2396          case '.':
    2397          case '+':
    2398          /* '-' case is handled separately above for --> comments */
    2399          parse_number:
    2400                  {
    2401                          CRNum *num = NULL;
    2402  
    2403                          status = cr_tknzr_parse_num (a_this, &num);
    2404                          if (status == CR_OK && num) {
    2405                                  next_bytes[0] = BYTE (input, 1, NULL);
    2406                                  next_bytes[1] = BYTE (input, 2, NULL);
    2407                                  next_bytes[2] = BYTE (input, 3, NULL);
    2408                                  next_bytes[3] = BYTE (input, 4, NULL);
    2409  
    2410                                  if (next_bytes[0] == 'e'
    2411                                      && next_bytes[1] == 'm') {
    2412                                          num->type = NUM_LENGTH_EM;
    2413                                          status = cr_token_set_ems (token,
    2414                                                                     num);
    2415                                          num = NULL;
    2416                                          SKIP_CHARS (a_this, 2);
    2417                                  } else if (next_bytes[0] == 'e'
    2418                                             && next_bytes[1] == 'x') {
    2419                                          num->type = NUM_LENGTH_EX;
    2420                                          status = cr_token_set_exs (token,
    2421                                                                     num);
    2422                                          num = NULL;
    2423                                          SKIP_CHARS (a_this, 2);
    2424                                  } else if (next_bytes[0] == 'p'
    2425                                             && next_bytes[1] == 'x') {
    2426                                          num->type = NUM_LENGTH_PX;
    2427                                          status = cr_token_set_length
    2428                                                  (token, num, LENGTH_PX_ET);
    2429                                          num = NULL;
    2430                                          SKIP_CHARS (a_this, 2);
    2431                                  } else if (next_bytes[0] == 'c'
    2432                                             && next_bytes[1] == 'm') {
    2433                                          num->type = NUM_LENGTH_CM;
    2434                                          status = cr_token_set_length
    2435                                                  (token, num, LENGTH_CM_ET);
    2436                                          num = NULL;
    2437                                          SKIP_CHARS (a_this, 2);
    2438                                  } else if (next_bytes[0] == 'm'
    2439                                             && next_bytes[1] == 'm') {
    2440                                          num->type = NUM_LENGTH_MM;
    2441                                          status = cr_token_set_length
    2442                                                  (token, num, LENGTH_MM_ET);
    2443                                          num = NULL;
    2444                                          SKIP_CHARS (a_this, 2);
    2445                                  } else if (next_bytes[0] == 'i'
    2446                                             && next_bytes[1] == 'n') {
    2447                                          num->type = NUM_LENGTH_IN;
    2448                                          status = cr_token_set_length
    2449                                                  (token, num, LENGTH_IN_ET);
    2450                                          num = NULL;
    2451                                          SKIP_CHARS (a_this, 2);
    2452                                  } else if (next_bytes[0] == 'p'
    2453                                             && next_bytes[1] == 't') {
    2454                                          num->type = NUM_LENGTH_PT;
    2455                                          status = cr_token_set_length
    2456                                                  (token, num, LENGTH_PT_ET);
    2457                                          num = NULL;
    2458                                          SKIP_CHARS (a_this, 2);
    2459                                  } else if (next_bytes[0] == 'p'
    2460                                             && next_bytes[1] == 'c') {
    2461                                          num->type = NUM_LENGTH_PC;
    2462                                          status = cr_token_set_length
    2463                                                  (token, num, LENGTH_PC_ET);
    2464                                          num = NULL;
    2465                                          SKIP_CHARS (a_this, 2);
    2466                                  } else if (next_bytes[0] == 'd'
    2467                                             && next_bytes[1] == 'e'
    2468                                             && next_bytes[2] == 'g') {
    2469                                          num->type = NUM_ANGLE_DEG;
    2470                                          status = cr_token_set_angle
    2471                                                  (token, num, ANGLE_DEG_ET);
    2472                                          num = NULL;
    2473                                          SKIP_CHARS (a_this, 3);
    2474                                  } else if (next_bytes[0] == 'r'
    2475                                             && next_bytes[1] == 'a'
    2476                                             && next_bytes[2] == 'd') {
    2477                                          num->type = NUM_ANGLE_RAD;
    2478                                          status = cr_token_set_angle
    2479                                                  (token, num, ANGLE_RAD_ET);
    2480                                          num = NULL;
    2481                                          SKIP_CHARS (a_this, 3);
    2482                                  } else if (next_bytes[0] == 'g'
    2483                                             && next_bytes[1] == 'r'
    2484                                             && next_bytes[2] == 'a'
    2485                                             && next_bytes[3] == 'd') {
    2486                                          num->type = NUM_ANGLE_GRAD;
    2487                                          status = cr_token_set_angle
    2488                                                  (token, num, ANGLE_GRAD_ET);
    2489                                          num = NULL;
    2490                                          SKIP_CHARS (a_this, 4);
    2491                                  } else if (next_bytes[0] == 'm'
    2492                                             && next_bytes[1] == 's') {
    2493                                          num->type = NUM_TIME_MS;
    2494                                          status = cr_token_set_time
    2495                                                  (token, num, TIME_MS_ET);
    2496                                          num = NULL;
    2497                                          SKIP_CHARS (a_this, 2);
    2498                                  } else if (next_bytes[0] == 's') {
    2499                                          num->type = NUM_TIME_S;
    2500                                          status = cr_token_set_time
    2501                                                  (token, num, TIME_S_ET);
    2502                                          num = NULL;
    2503                                          SKIP_CHARS (a_this, 1);
    2504                                  } else if (next_bytes[0] == 'H'
    2505                                             && next_bytes[1] == 'z') {
    2506                                          num->type = NUM_FREQ_HZ;
    2507                                          status = cr_token_set_freq
    2508                                                  (token, num, FREQ_HZ_ET);
    2509                                          num = NULL;
    2510                                          SKIP_CHARS (a_this, 2);
    2511                                  } else if (next_bytes[0] == 'k'
    2512                                             && next_bytes[1] == 'H'
    2513                                             && next_bytes[2] == 'z') {
    2514                                          num->type = NUM_FREQ_KHZ;
    2515                                          status = cr_token_set_freq
    2516                                                  (token, num, FREQ_KHZ_ET);
    2517                                          num = NULL;
    2518                                          SKIP_CHARS (a_this, 3);
    2519                                  } else if (next_bytes[0] == '%') {
    2520                                          num->type = NUM_PERCENTAGE;
    2521                                          status = cr_token_set_percentage
    2522                                                  (token, num);
    2523                                          num = NULL;
    2524                                          SKIP_CHARS (a_this, 1);
    2525                                  } else {
    2526                                          status = cr_tknzr_parse_ident (a_this,
    2527                                                                         &str);
    2528                                          if (status == CR_OK && str) {
    2529                                                  num->type = NUM_UNKNOWN_TYPE;
    2530                                                  status = cr_token_set_dimen
    2531                                                          (token, num, str);
    2532                                                  num = NULL;
    2533                                                  CHECK_PARSING_STATUS (status,
    2534                                                                        TRUE);
    2535                                                  str = NULL;
    2536                                          } else {
    2537                                                  status = cr_token_set_number
    2538                                                          (token, num);
    2539                                                  num = NULL;
    2540                                                  CHECK_PARSING_STATUS (status, CR_OK);
    2541                                                  str = NULL;
    2542                                          }
    2543                                  }
    2544                                  if (token && token->u.num) {
    2545                                          cr_parsing_location_copy (&token->location,
    2546                                                                    &token->u.num->location) ;
    2547                                  } else {
    2548                                          status = CR_ERROR ;
    2549                                  }
    2550                                  goto done ;
    2551                          }
    2552                  }
    2553                  break;
    2554  
    2555          default:
    2556          fallback:
    2557                  /*process the fallback cases here */
    2558  
    2559                  if (next_char == '\\'
    2560                      || (cr_utils_is_nonascii (next_bytes[0]) == TRUE)
    2561                      || ((next_char >= 'a') && (next_char <= 'z'))
    2562                      || ((next_char >= 'A') && (next_char <= 'Z'))) {
    2563                          status = cr_tknzr_parse_ident (a_this, &str);
    2564                          if (status == CR_OK && str) {
    2565                                  guint32 next_c = 0;
    2566  
    2567                                  status = cr_input_peek_char
    2568                                          (PRIVATE (a_this)->input, &next_c);
    2569  
    2570                                  if (status == CR_OK && next_c == '(') {
    2571  
    2572                                          SKIP_CHARS (a_this, 1);
    2573                                          status = cr_token_set_function
    2574                                                  (token, str);
    2575                                          CHECK_PARSING_STATUS (status, TRUE);
    2576                                          /*ownership is transfered
    2577                                           *to token by cr_token_set_function.
    2578                                           */
    2579                                          if (str) {
    2580                                                  cr_parsing_location_copy (&token->location, 
    2581                                                                            &str->location) ;
    2582                                          }
    2583                                          str = NULL;
    2584                                  } else {
    2585                                          status = cr_token_set_ident (token,
    2586                                                                       str);
    2587                                          CHECK_PARSING_STATUS (status, TRUE);
    2588                                          if (str) {
    2589                                                  cr_parsing_location_copy (&token->location, 
    2590                                                                            &str->location) ;
    2591                                          }
    2592                                          str = NULL;
    2593                                  }
    2594                                  goto done;
    2595                          } else {
    2596                                  if (str) {
    2597                                          cr_string_destroy (str);
    2598                                          str = NULL;
    2599                                  }
    2600                          }
    2601                  }
    2602                  break;
    2603          }
    2604  
    2605          READ_NEXT_CHAR (a_this, &next_char);
    2606          cr_tknzr_get_parsing_location (a_this, 
    2607                                         &location) ;
    2608          status = cr_token_set_delim (token, next_char);
    2609          CHECK_PARSING_STATUS (status, TRUE);
    2610          cr_parsing_location_copy (&token->location, 
    2611                                    &location) ;
    2612   done:
    2613  
    2614          if (status == CR_OK && token) {
    2615                  *a_tk = token;
    2616                  /*
    2617                   *store the previous position input stream pos.
    2618                   */
    2619                  memmove (&PRIVATE (a_this)->prev_pos,
    2620                           &init_pos, sizeof (CRInputPos));
    2621                  return CR_OK;
    2622          }
    2623  
    2624   error:
    2625          if (token) {
    2626                  cr_token_destroy (token);
    2627                  token = NULL;
    2628          }
    2629  
    2630          if (str) {
    2631                  cr_string_destroy (str);
    2632                  str = NULL;
    2633          }
    2634          cr_tknzr_set_cur_pos (a_this, &init_pos);
    2635          return status;
    2636  
    2637  }
    2638  
    2639  enum CRStatus
    2640  cr_tknzr_parse_token (CRTknzr * a_this, enum CRTokenType a_type,
    2641                        enum CRTokenExtraType a_et, gpointer a_res,
    2642                        gpointer a_extra_res)
    2643  {
    2644          enum CRStatus status = CR_OK;
    2645          CRToken *token = NULL;
    2646  
    2647          g_return_val_if_fail (a_this && PRIVATE (a_this)
    2648                                && PRIVATE (a_this)->input
    2649                                && a_res, CR_BAD_PARAM_ERROR);
    2650  
    2651          status = cr_tknzr_get_next_token (a_this, &token);
    2652          if (status != CR_OK)
    2653                  return status;
    2654          if (token == NULL)
    2655                  return CR_PARSING_ERROR;
    2656  
    2657          if (token->type == a_type) {
    2658                  switch (a_type) {
    2659                  case NO_TK:
    2660                  case S_TK:
    2661                  case CDO_TK:
    2662                  case CDC_TK:
    2663                  case INCLUDES_TK:
    2664                  case DASHMATCH_TK:
    2665                  case IMPORT_SYM_TK:
    2666                  case PAGE_SYM_TK:
    2667                  case MEDIA_SYM_TK:
    2668                  case FONT_FACE_SYM_TK:
    2669                  case CHARSET_SYM_TK:
    2670                  case IMPORTANT_SYM_TK:
    2671                          status = CR_OK;
    2672                          break;
    2673  
    2674                  case STRING_TK:
    2675                  case IDENT_TK:
    2676                  case HASH_TK:
    2677                  case ATKEYWORD_TK:
    2678                  case FUNCTION_TK:
    2679                  case COMMENT_TK:
    2680                  case URI_TK:
    2681                          *((CRString **) a_res) = token->u.str;
    2682                          token->u.str = NULL;
    2683                          status = CR_OK;
    2684                          break;
    2685  
    2686                  case EMS_TK:
    2687                  case EXS_TK:
    2688                  case PERCENTAGE_TK:
    2689                  case NUMBER_TK:
    2690                          *((CRNum **) a_res) = token->u.num;
    2691                          token->u.num = NULL;
    2692                          status = CR_OK;
    2693                          break;
    2694  
    2695                  case LENGTH_TK:
    2696                  case ANGLE_TK:
    2697                  case TIME_TK:
    2698                  case FREQ_TK:
    2699                          if (token->extra_type == a_et) {
    2700                                  *((CRNum **) a_res) = token->u.num;
    2701                                  token->u.num = NULL;
    2702                                  status = CR_OK;
    2703                          }
    2704                          break;
    2705  
    2706                  case DIMEN_TK:
    2707                          *((CRNum **) a_res) = token->u.num;
    2708                          if (a_extra_res == NULL) {
    2709                                  status = CR_BAD_PARAM_ERROR;
    2710                                  goto error;
    2711                          }
    2712  
    2713                          *((CRString **) a_extra_res) = token->dimen;
    2714                          token->u.num = NULL;
    2715                          token->dimen = NULL;
    2716                          status = CR_OK;
    2717                          break;
    2718  
    2719                  case DELIM_TK:
    2720                          *((guint32 *) a_res) = token->u.unichar;
    2721                          status = CR_OK;
    2722                          break;
    2723  
    2724                  case UNICODERANGE_TK:
    2725                  default:
    2726                          status = CR_PARSING_ERROR;
    2727                          break;
    2728                  }
    2729  
    2730                  cr_token_destroy (token);
    2731                  token = NULL;
    2732          } else {
    2733                  cr_tknzr_unget_token (a_this, token);
    2734                  token = NULL;
    2735                  status = CR_PARSING_ERROR;
    2736          }
    2737  
    2738          return status;
    2739  
    2740        error:
    2741  
    2742          if (token) {
    2743                  cr_tknzr_unget_token (a_this, token);
    2744                  token = NULL;
    2745          }
    2746  
    2747          return status;
    2748  }
    2749  
    2750  void
    2751  cr_tknzr_destroy (CRTknzr * a_this)
    2752  {
    2753          g_return_if_fail (a_this);
    2754  
    2755          if (PRIVATE (a_this) && PRIVATE (a_this)->input) {
    2756                  if (cr_input_unref (PRIVATE (a_this)->input)
    2757                      == TRUE) {
    2758                          PRIVATE (a_this)->input = NULL;
    2759                  }
    2760          }
    2761  
    2762          if (PRIVATE (a_this)->token_cache) {
    2763                  cr_token_destroy (PRIVATE (a_this)->token_cache);
    2764                  PRIVATE (a_this)->token_cache = NULL;
    2765          }
    2766  
    2767          if (PRIVATE (a_this)) {
    2768                  g_free (PRIVATE (a_this));
    2769                  PRIVATE (a_this) = NULL;
    2770          }
    2771  
    2772          g_free (a_this);
    2773  }