Libcroco
cr-tknzr.c
Go to the documentation of this file.
00001 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
00002 
00003 /*
00004  * This file is part of The Croco Library
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of version 2.1 of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00018  * USA
00019  *
00020  * Author: Dodji Seketeli
00021  * See the COPYRIGHTS file for copyrights information.
00022  */
00023 
00024 /**
00025  *@file
00026  *The definition of the #CRTknzr (tokenizer)
00027  *class.
00028  */
00029 
00030 #include "string.h"
00031 #include "cr-tknzr.h"
00032 #include "cr-doc-handler.h"
00033 
00034 struct _CRTknzrPriv {
00035         /**The parser input stream of bytes*/
00036         CRInput *input;
00037 
00038         /**
00039          *A cache where tknzr_unget_token()
00040          *puts back the token. tknzr_get_next_token()
00041          *first look in this cache, and if and 
00042          *only if it's empty, fetches the next token
00043          *from the input stream.
00044          */
00045         CRToken *token_cache;
00046 
00047         /**
00048          *The position of the end of the previous token
00049          *or char fetched.
00050          */
00051         CRInputPos prev_pos;
00052 
00053         CRDocHandler *sac_handler;
00054 
00055         /**
00056          *The reference count of the current instance
00057          *of #CRTknzr. Is manipulated by cr_tknzr_ref()
00058          *and cr_tknzr_unref().
00059          */
00060         glong ref_count;
00061 };
00062 
00063 #define PRIVATE(obj) ((obj)->priv)
00064 
00065 /**
00066  *return TRUE if the character is a number ([0-9]), FALSE otherwise
00067  *@param a_char the char to test.
00068  */
00069 #define IS_NUM(a_char) (((a_char) >= '0' && (a_char) <= '9')?TRUE:FALSE)
00070 
00071 /**
00072  *Checks if 'status' equals CR_OK. If not, goto the 'error' label.
00073  *
00074  *@param status the status (of type enum CRStatus) to test.
00075  *@param is_exception if set to FALSE, the final status returned the
00076  *current function will be CR_PARSING_ERROR. If set to TRUE, the
00077  *current status will be the current value of the 'status' variable.
00078  *
00079  */
00080 #define CHECK_PARSING_STATUS(status, is_exception) \
00081 if ((status) != CR_OK) \
00082 { \
00083         if (is_exception == FALSE) \
00084         { \
00085                 status = CR_PARSING_ERROR ; \
00086         } \
00087         goto error ; \
00088 }
00089 
00090 /**
00091  *Peeks the next char from the input stream of the current tokenizer.
00092  *invokes CHECK_PARSING_STATUS on the status returned by
00093  *cr_tknzr_input_peek_char().
00094  *
00095  *@param the current instance of #CRTkzr.
00096  *@param to_char a pointer to the char where to store the
00097  *char peeked.
00098  */
00099 #define PEEK_NEXT_CHAR(a_tknzr, a_to_char) \
00100 {\
00101 status = cr_tknzr_peek_char  (a_tknzr, a_to_char) ; \
00102 CHECK_PARSING_STATUS (status, TRUE) \
00103 }
00104 
00105 /**
00106  *Reads the next char from the input stream of the current parser.
00107  *In case of error, jumps to the "error:" label located in the
00108  *function where this macro is called.
00109  *@param parser the curent instance of #CRTknzr
00110  *@param to_char a pointer to the guint32 char where to store
00111  *the character read.
00112  */
00113 #define READ_NEXT_CHAR(a_tknzr, to_char) \
00114 status = cr_tknzr_read_char (a_tknzr, to_char) ;\
00115 CHECK_PARSING_STATUS (status, TRUE)
00116 
00117 /**
00118  *Gets information about the current position in
00119  *the input of the parser.
00120  *In case of failure, this macro returns from the 
00121  *calling function and
00122  *returns a status code of type enum #CRStatus.
00123  *@param parser the current instance of #CRTknzr.
00124  *@param pos out parameter. A pointer to the position 
00125  *inside the current parser input. Must
00126  */
00127 #define RECORD_INITIAL_POS(a_tknzr, a_pos) \
00128 status = cr_input_get_cur_pos (PRIVATE  \
00129 (a_tknzr)->input, a_pos) ; \
00130 g_return_val_if_fail (status == CR_OK, status)
00131 
00132 /**
00133  *Gets the address of the current byte inside the
00134  *parser input.
00135  *@param parser the current instance of #CRTknzr.
00136  *@param addr out parameter a pointer (guchar*)
00137  *to where the address  must be put.
00138  */
00139 #define RECORD_CUR_BYTE_ADDR(a_tknzr, a_addr) \
00140 status = cr_input_get_cur_byte_addr \
00141             (PRIVATE (a_tknzr)->input, a_addr) ; \
00142 CHECK_PARSING_STATUS (status, TRUE)
00143 
00144 /**
00145  *Peeks a byte from the topmost parser input at
00146  *a given offset from the current position.
00147  *If it fails, goto the "error:" label.
00148  *
00149  *@param a_parser the current instance of #CRTknzr.
00150  *@param a_offset the offset of the byte to peek, the
00151  *current byte having the offset '0'.
00152  *@param a_byte_ptr out parameter a pointer (guchar*) to
00153  *where the peeked char is to be stored.
00154  */
00155 #define PEEK_BYTE(a_tknzr, a_offset, a_byte_ptr) \
00156 status = cr_tknzr_peek_byte (a_tknzr, \
00157                              a_offset, \
00158                              a_byte_ptr) ; \
00159 CHECK_PARSING_STATUS (status, TRUE) ;
00160 
00161 #define BYTE(a_input, a_n, a_eof) \
00162 cr_input_peek_byte2 (a_input, a_n, a_eof)
00163 
00164 /**
00165  *Reads a byte from the topmost parser input
00166  *steam.
00167  *If it fails, goto the "error" label.
00168  *@param a_parser the current instance of #CRTknzr.
00169  *@param a_byte_ptr the guchar * where to put the read char.
00170  */
00171 #define READ_NEXT_BYTE(a_tknzr, a_byte_ptr) \
00172 status = \
00173 cr_input_read_byte (PRIVATE (a_tknzr)->input, a_byte_ptr) ;\
00174 CHECK_PARSING_STATUS (status, TRUE) ;
00175 
00176 /**
00177  *Skips a given number of byte in the topmost
00178  *parser input. Don't update line and column number.
00179  *In case of error, jumps to the "error:" label
00180  *of the surrounding function.
00181  *@param a_parser the current instance of #CRTknzr.
00182  *@param a_nb_bytes the number of bytes to skip.
00183  */
00184 #define SKIP_BYTES(a_tknzr, a_nb_bytes) \
00185 status = cr_input_seek_index (PRIVATE (a_tknzr)->input, \
00186                                      CR_SEEK_CUR, a_nb_bytes) ; \
00187 CHECK_PARSING_STATUS (status, TRUE) ;
00188 
00189 /**
00190  *Skip utf8 encoded characters.
00191  *Updates line and column numbers.
00192  *@param a_parser the current instance of #CRTknzr.
00193  *@param a_nb_chars the number of chars to skip. Must be of
00194  *type glong.
00195  */
00196 #define SKIP_CHARS(a_tknzr, a_nb_chars) \
00197 { \
00198 glong nb_chars = a_nb_chars ; \
00199 status = cr_input_consume_chars \
00200      (PRIVATE (a_tknzr)->input,0, &nb_chars) ; \
00201 CHECK_PARSING_STATUS (status, TRUE) ; \
00202 }
00203 
00204 /**
00205  *Tests the condition and if it is false, sets
00206  *status to "CR_PARSING_ERROR" and goto the 'error'
00207  *label.
00208  *@param condition the condition to test.
00209  */
00210 #define ENSURE_PARSING_COND(condition) \
00211 if (! (condition)) {status = CR_PARSING_ERROR; goto error ;}
00212 
00213 static enum CRStatus  cr_tknzr_parse_nl (CRTknzr * a_this, 
00214                                          guchar ** a_start, 
00215                                          guchar ** a_end,
00216                                          CRParsingLocation *a_location);
00217 
00218 static enum CRStatus cr_tknzr_parse_w (CRTknzr * a_this, 
00219                                        guchar ** a_start, 
00220                                        guchar ** a_end,
00221                                        CRParsingLocation *a_location) ;
00222 
00223 static enum CRStatus cr_tknzr_parse_unicode_escape (CRTknzr * a_this, 
00224                                                     guint32 * a_unicode,
00225                                                     CRParsingLocation *a_location) ;
00226 
00227 static enum CRStatus cr_tknzr_parse_escape (CRTknzr * a_this, 
00228                                             guint32 * a_esc_code,
00229                                             CRParsingLocation *a_location);
00230 
00231 static enum CRStatus cr_tknzr_parse_string (CRTknzr * a_this, 
00232                                             CRString ** a_str);
00233 
00234 static enum CRStatus cr_tknzr_parse_comment (CRTknzr * a_this, 
00235                                              CRString ** a_comment);
00236 
00237 static enum CRStatus cr_tknzr_parse_nmstart (CRTknzr * a_this, 
00238                                              guint32 * a_char, 
00239                                              CRParsingLocation *a_location);
00240 
00241 static enum CRStatus cr_tknzr_parse_num (CRTknzr * a_this,
00242                                          CRNum ** a_num);
00243 
00244 /**********************************
00245  *PRIVATE methods
00246  **********************************/
00247 
00248 /**
00249  *Parses a "w" as defined by the css spec at [4.1.1]:
00250  * w ::= [ \t\r\n\f]*
00251  *
00252  *@param a_this the current instance of #CRTknzr.
00253  *@param a_start out param. Upon successfull completion, points
00254  *to the beginning of the parsed white space, points to NULL otherwise.
00255  *Can also point to NULL is there is no white space actually.
00256  *@param a_end out param. Upon successfull completion, points
00257  *to the end of the parsed white space, points to NULL otherwise.
00258  *Can also point to NULL is there is no white space actually.
00259  */
00260 static enum CRStatus
00261 cr_tknzr_parse_w (CRTknzr * a_this, 
00262                   guchar ** a_start, 
00263                   guchar ** a_end, 
00264                   CRParsingLocation *a_location)
00265 {
00266         guint32 cur_char = 0;
00267         CRInputPos init_pos;
00268         enum CRStatus status = CR_OK;
00269 
00270         g_return_val_if_fail (a_this && PRIVATE (a_this)
00271                               && PRIVATE (a_this)->input
00272                               && a_start && a_end, 
00273                               CR_BAD_PARAM_ERROR);
00274 
00275         RECORD_INITIAL_POS (a_this, &init_pos);
00276 
00277         *a_start = NULL;
00278         *a_end = NULL;
00279 
00280         READ_NEXT_CHAR (a_this, &cur_char);
00281 
00282         if (cr_utils_is_white_space (cur_char) == FALSE) {
00283                 status = CR_PARSING_ERROR;
00284                 goto error;
00285         }
00286         if (a_location) {
00287                 cr_tknzr_get_parsing_location (a_this, 
00288                                                a_location) ;
00289         }
00290         RECORD_CUR_BYTE_ADDR (a_this, a_start);
00291         *a_end = *a_start;
00292 
00293         for (;;) {
00294                 gboolean is_eof = FALSE;
00295 
00296                 cr_input_get_end_of_file (PRIVATE (a_this)->input, &is_eof);
00297                 if (is_eof)
00298                         break;
00299 
00300                 status = cr_tknzr_peek_char (a_this, &cur_char);
00301                 if (status == CR_END_OF_INPUT_ERROR) {
00302                         status = CR_OK;
00303                         break;
00304                 } else if (status != CR_OK) {
00305                         goto error;
00306                 }
00307 
00308                 if (cr_utils_is_white_space (cur_char) == TRUE) {
00309                         READ_NEXT_CHAR (a_this, &cur_char);
00310                         RECORD_CUR_BYTE_ADDR (a_this, a_end);
00311                 } else {
00312                         break;
00313                 }
00314         }
00315 
00316         return CR_OK;
00317 
00318       error:
00319         cr_tknzr_set_cur_pos (a_this, &init_pos);
00320 
00321         return status;
00322 }
00323 
00324 /**
00325  *Parses a newline as defined in the css2 spec:
00326  * nl   ::=    \n|\r\n|\r|\f
00327  *
00328  *@param a_this the "this pointer" of the current instance of #CRTknzr.
00329  *@param a_start a pointer to the first character of the successfully 
00330  *parsed string.
00331  *@param a_end a pointer to the last character of the successfully parsed
00332  *string.
00333  *@result CR_OK uppon successfull completion, an error code otherwise.
00334  */
00335 static enum CRStatus
00336 cr_tknzr_parse_nl (CRTknzr * a_this, 
00337                    guchar ** a_start, 
00338                    guchar ** a_end, 
00339                    CRParsingLocation *a_location)
00340 {
00341         CRInputPos init_pos;
00342         guchar next_chars[2] = { 0 };
00343         enum CRStatus status = CR_PARSING_ERROR;
00344 
00345         g_return_val_if_fail (a_this && PRIVATE (a_this)
00346                               && a_start && a_end, CR_BAD_PARAM_ERROR);
00347 
00348         RECORD_INITIAL_POS (a_this, &init_pos);
00349 
00350         PEEK_BYTE (a_this, 1, &next_chars[0]);
00351         PEEK_BYTE (a_this, 2, &next_chars[1]);
00352 
00353         if ((next_chars[0] == '\r' && next_chars[1] == '\n')) {
00354                 SKIP_BYTES (a_this, 1);
00355                 if (a_location) {
00356                         cr_tknzr_get_parsing_location 
00357                                 (a_this, a_location) ;
00358                 }
00359                 SKIP_CHARS (a_this, 1);
00360 
00361                 RECORD_CUR_BYTE_ADDR (a_this, a_end);
00362 
00363                 status = CR_OK;
00364         } else if (next_chars[0] == '\n'
00365                    || next_chars[0] == '\r' || next_chars[0] == '\f') {
00366                 SKIP_CHARS (a_this, 1);
00367                 if (a_location) {
00368                         cr_tknzr_get_parsing_location 
00369                                 (a_this, a_location) ;
00370                 }
00371                 RECORD_CUR_BYTE_ADDR (a_this, a_start);
00372                 *a_end = *a_start;
00373                 status = CR_OK;
00374         } else {
00375                 status = CR_PARSING_ERROR;
00376                 goto error;
00377         }
00378         return CR_OK ;
00379 
00380  error:
00381         cr_tknzr_set_cur_pos (a_this, &init_pos) ;
00382         return status;
00383 }
00384 
00385 /**
00386  *Go ahead in the parser input, skipping all the spaces.
00387  *If the next char if not a white space, this function does nothing.
00388  *In any cases, it stops when it encounters a non white space character.
00389  *
00390  *@param a_this the current instance of #CRTknzr.
00391  *@return CR_OK upon successfull completion, an error code otherwise.
00392  */
00393 static enum CRStatus
00394 cr_tknzr_try_to_skip_spaces (CRTknzr * a_this)
00395 {
00396         enum CRStatus status = CR_ERROR;
00397         guint32 cur_char = 0;
00398 
00399         g_return_val_if_fail (a_this && PRIVATE (a_this)
00400                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
00401 
00402         status = cr_input_peek_char (PRIVATE (a_this)->input, &cur_char);
00403 
00404         if (status != CR_OK) {
00405                 if (status == CR_END_OF_INPUT_ERROR)
00406                         return CR_OK;
00407                 return status;
00408         }
00409 
00410         if (cr_utils_is_white_space (cur_char) == TRUE) {
00411                 glong nb_chars = -1; /*consume all spaces */
00412 
00413                 status = cr_input_consume_white_spaces
00414                         (PRIVATE (a_this)->input, &nb_chars);
00415         }
00416 
00417         return status;
00418 }
00419 
00420 /**
00421  *Parses a "comment" as defined in the css spec at [4.1.1]:
00422  *COMMENT ::= \/\*[^*]*\*+([^/][^*]*\*+)*\/ .
00423  *This complex regexp is just to say that comments start
00424  *with the two chars '/''*' and ends with the two chars '*''/'.
00425  *It also means that comments cannot be nested.
00426  *So based on that, I've just tried to implement the parsing function
00427  *simply and in a straight forward manner.
00428  */
00429 static enum CRStatus
00430 cr_tknzr_parse_comment (CRTknzr * a_this, 
00431                         CRString ** a_comment)
00432 {
00433         enum CRStatus status = CR_OK;
00434         CRInputPos init_pos;
00435         guint32 cur_char = 0, next_char= 0;
00436         CRString *comment = NULL;
00437         CRParsingLocation loc = {0} ;
00438 
00439         g_return_val_if_fail (a_this && PRIVATE (a_this)
00440                               && PRIVATE (a_this)->input, 
00441                               CR_BAD_PARAM_ERROR);
00442 
00443         RECORD_INITIAL_POS (a_this, &init_pos);        
00444         READ_NEXT_CHAR (a_this, &cur_char) ;        
00445         ENSURE_PARSING_COND (cur_char == '/');
00446         cr_tknzr_get_parsing_location (a_this, &loc) ;
00447 
00448         READ_NEXT_CHAR (a_this, &cur_char);
00449         ENSURE_PARSING_COND (cur_char == '*');
00450         comment = cr_string_new ();
00451         for (;;) { /* [^*]* */
00452                 PEEK_NEXT_CHAR (a_this, &next_char);
00453                 if (next_char == '*')
00454                         break;
00455                 READ_NEXT_CHAR (a_this, &cur_char);
00456                 g_string_append_unichar (comment->stryng, cur_char);
00457         }
00458         /* Stop condition: next_char == '*' */
00459         for (;;) { /* \*+ */
00460                 READ_NEXT_CHAR(a_this, &cur_char);
00461                 ENSURE_PARSING_COND (cur_char == '*');
00462                 g_string_append_unichar (comment->stryng, cur_char);
00463                 PEEK_NEXT_CHAR (a_this, &next_char);
00464                 if (next_char != '*')
00465                         break;
00466         }
00467         /* Stop condition: next_char != '*' */
00468         for (;;) { /* ([^/][^*]*\*+)* */
00469                 if (next_char == '/')
00470                         break;
00471                 READ_NEXT_CHAR(a_this, &cur_char);
00472                 g_string_append_unichar (comment->stryng, cur_char);
00473                 for (;;) { /* [^*]* */
00474                         PEEK_NEXT_CHAR (a_this, &next_char);
00475                         if (next_char == '*')
00476                                 break;
00477                         READ_NEXT_CHAR (a_this, &cur_char);
00478                         g_string_append_unichar (comment->stryng, cur_char);
00479                 }
00480                 /* Stop condition: next_char = '*', no need to verify, because peek and read exit to error anyway */
00481                 for (;;) { /* \*+ */
00482                         READ_NEXT_CHAR(a_this, &cur_char);
00483                         ENSURE_PARSING_COND (cur_char == '*');
00484                         g_string_append_unichar (comment->stryng, cur_char);
00485                         PEEK_NEXT_CHAR (a_this, &next_char);
00486                         if (next_char != '*')
00487                                 break;
00488                 }
00489                 /* Continue condition: next_char != '*' */
00490         }
00491         /* Stop condition: next_char == '\/' */
00492         READ_NEXT_CHAR(a_this, &cur_char);
00493         g_string_append_unichar (comment->stryng, cur_char);
00494 
00495         if (status == CR_OK) {
00496                 cr_parsing_location_copy (&comment->location, 
00497                                           &loc) ;
00498                 *a_comment = comment;                
00499                 return CR_OK;
00500         }
00501  error:
00502 
00503         if (comment) {
00504                 cr_string_destroy (comment);
00505                 comment = NULL;
00506         }
00507 
00508         cr_tknzr_set_cur_pos (a_this, &init_pos);
00509 
00510         return status;
00511 }
00512 
00513 /**
00514  *Parses an 'unicode' escape sequence defined
00515  *in css spec at chap 4.1.1:
00516  *unicode ::= \\[0-9a-f]{1,6}[ \n\r\t\f]?
00517  *@param a_this the current instance of #CRTknzr.
00518  *@param a_start out parameter. A pointer to the start
00519  *of the unicode escape sequence. Must *NOT* be deleted by
00520  *the caller.
00521  *@param a_end out parameter. A pointer to the last character
00522  *of the unicode escape sequence. Must *NOT* be deleted by the caller.
00523  *@return CR_OK if parsing succeded, an error code otherwise.
00524  *Error code can be either CR_PARSING_ERROR if the string 
00525  *parsed just doesn't
00526  *respect the production or another error if a 
00527  *lower level error occured.
00528  */
00529 static enum CRStatus
00530 cr_tknzr_parse_unicode_escape (CRTknzr * a_this, 
00531                                guint32 * a_unicode,
00532                                CRParsingLocation *a_location)
00533 {
00534         guint32 cur_char;
00535         CRInputPos init_pos;
00536         glong occur = 0;
00537         guint32 unicode = 0;
00538         guchar *tmp_char_ptr1 = NULL,
00539                 *tmp_char_ptr2 = NULL;
00540         enum CRStatus status = CR_OK;
00541 
00542         g_return_val_if_fail (a_this && PRIVATE (a_this)
00543                               && a_unicode, CR_BAD_PARAM_ERROR);
00544 
00545         /*first, let's backup the current position pointer */
00546         RECORD_INITIAL_POS (a_this, &init_pos);
00547 
00548         READ_NEXT_CHAR (a_this, &cur_char);
00549 
00550         if (cur_char != '\\') {
00551                 status = CR_PARSING_ERROR;
00552                 goto error;
00553         }
00554         if (a_location) {
00555                 cr_tknzr_get_parsing_location 
00556                         (a_this, a_location) ;
00557         }
00558         PEEK_NEXT_CHAR (a_this, &cur_char);
00559 
00560         for (occur = 0, unicode = 0; ((cur_char >= '0' && cur_char <= '9')
00561                                       || (cur_char >= 'a' && cur_char <= 'f')
00562                                       || (cur_char >= 'A' && cur_char <= 'F'))
00563              && occur < 6; occur++) {
00564                 gint cur_char_val = 0;
00565 
00566                 READ_NEXT_CHAR (a_this, &cur_char);
00567 
00568                 if ((cur_char >= '0' && cur_char <= '9')) {
00569                         cur_char_val = (cur_char - '0');
00570                 } else if ((cur_char >= 'a' && cur_char <= 'f')) {
00571                         cur_char_val = 10 + (cur_char - 'a');
00572                 } else if ((cur_char >= 'A' && cur_char <= 'F')) {
00573                         cur_char_val = 10 + (cur_char - 'A');
00574                 }
00575 
00576                 unicode = unicode * 16 + cur_char_val;
00577 
00578                 PEEK_NEXT_CHAR (a_this, &cur_char);
00579         }
00580 
00581         /* Eat a whitespace if possible. */
00582         cr_tknzr_parse_w (a_this, &tmp_char_ptr1, 
00583                           &tmp_char_ptr2, NULL);
00584         *a_unicode = unicode;
00585         return CR_OK;
00586 
00587       error:
00588         /*
00589          *restore the initial position pointer backuped at
00590          *the beginning of this function.
00591          */
00592         cr_tknzr_set_cur_pos (a_this, &init_pos);
00593 
00594         return status;
00595 }
00596 
00597 /**
00598  *parses an escape sequence as defined by the css spec:
00599  *escape ::= {unicode}|\\[ -~\200-\4177777]
00600  *@param a_this the current instance of #CRTknzr .
00601  */
00602 static enum CRStatus
00603 cr_tknzr_parse_escape (CRTknzr * a_this, guint32 * a_esc_code,
00604                        CRParsingLocation *a_location)
00605 {
00606         enum CRStatus status = CR_OK;
00607         guint32 cur_char = 0;
00608         CRInputPos init_pos;
00609         guchar next_chars[2];
00610 
00611         g_return_val_if_fail (a_this && PRIVATE (a_this)
00612                               && a_esc_code, CR_BAD_PARAM_ERROR);
00613 
00614         RECORD_INITIAL_POS (a_this, &init_pos);
00615 
00616         PEEK_BYTE (a_this, 1, &next_chars[0]);
00617         PEEK_BYTE (a_this, 2, &next_chars[1]);
00618 
00619         if (next_chars[0] != '\\') {
00620                 status = CR_PARSING_ERROR;
00621                 goto error;
00622         }
00623 
00624         if ((next_chars[1] >= '0' && next_chars[1] <= '9')
00625             || (next_chars[1] >= 'a' && next_chars[1] <= 'f')
00626             || (next_chars[1] >= 'A' && next_chars[1] <= 'F')) {
00627                 status = cr_tknzr_parse_unicode_escape (a_this, a_esc_code, 
00628                                                         a_location);
00629         } else {
00630                 /*consume the '\' char */
00631                 READ_NEXT_CHAR (a_this, &cur_char);
00632                 if (a_location) {
00633                         cr_tknzr_get_parsing_location (a_this, 
00634                                                        a_location) ;
00635                 }
00636                 /*then read the char after the '\' */
00637                 READ_NEXT_CHAR (a_this, &cur_char);
00638 
00639                 if (cur_char != ' ' && (cur_char < 200 || cur_char > 4177777)) {
00640                         status = CR_PARSING_ERROR;
00641                         goto error;
00642                 }
00643                 *a_esc_code = cur_char;
00644 
00645         }
00646         if (status == CR_OK) {
00647                 return CR_OK;
00648         }
00649  error:
00650         cr_tknzr_set_cur_pos (a_this, &init_pos);
00651         return status;
00652 }
00653 
00654 /**
00655  *Parses a string type as defined in css spec [4.1.1]:
00656  *
00657  *string ::= {string1}|{string2}
00658  *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
00659  *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
00660  *
00661  *@param a_this the current instance of #CRTknzr.
00662  *@param a_start out parameter. Upon successfull completion, 
00663  *points to the beginning of the string, points to an undefined value
00664  *otherwise.
00665  *@param a_end out parameter. Upon successfull completion, points to
00666  *the beginning of the string, points to an undefined value otherwise.
00667  *@return CR_OK upon successfull completion, an error code otherwise.
00668  */
00669 static enum CRStatus
00670 cr_tknzr_parse_string (CRTknzr * a_this, CRString ** a_str)
00671 {
00672         guint32 cur_char = 0,
00673                 delim = 0;
00674         CRInputPos init_pos;
00675         enum CRStatus status = CR_OK;
00676         CRString *str = NULL;
00677 
00678         g_return_val_if_fail (a_this && PRIVATE (a_this)
00679                               && PRIVATE (a_this)->input
00680                               && a_str, CR_BAD_PARAM_ERROR);
00681 
00682         RECORD_INITIAL_POS (a_this, &init_pos);
00683         READ_NEXT_CHAR (a_this, &cur_char);
00684 
00685         if (cur_char == '"')
00686                 delim = '"';
00687         else if (cur_char == '\'')
00688                 delim = '\'';
00689         else {
00690                 status = CR_PARSING_ERROR;
00691                 goto error;
00692         }
00693         str = cr_string_new ();
00694         if (str) {
00695                 cr_tknzr_get_parsing_location 
00696                         (a_this, &str->location) ;
00697         }
00698         for (;;) {
00699                 guchar next_chars[2] = { 0 };
00700 
00701                 PEEK_BYTE (a_this, 1, &next_chars[0]);
00702                 PEEK_BYTE (a_this, 2, &next_chars[1]);
00703 
00704                 if (next_chars[0] == '\\') {
00705                         guchar *tmp_char_ptr1 = NULL,
00706                                 *tmp_char_ptr2 = NULL;
00707                         guint32 esc_code = 0;
00708 
00709                         if (next_chars[1] == '\'' || next_chars[1] == '"') {
00710                                 g_string_append_unichar (str->stryng, 
00711                                                          next_chars[1]);
00712                                 SKIP_BYTES (a_this, 2);
00713                                 status = CR_OK;
00714                         } else {
00715                                 status = cr_tknzr_parse_escape
00716                                         (a_this, &esc_code, NULL);
00717 
00718                                 if (status == CR_OK) {
00719                                         g_string_append_unichar
00720                                                 (str->stryng, 
00721                                                  esc_code);
00722                                 }
00723                         }
00724 
00725                         if (status != CR_OK) {
00726                                 /*
00727                                  *consume the '\' char, and try to parse
00728                                  *a newline.
00729                                  */
00730                                 READ_NEXT_CHAR (a_this, &cur_char);
00731 
00732                                 status = cr_tknzr_parse_nl
00733                                         (a_this, &tmp_char_ptr1,
00734                                          &tmp_char_ptr2, NULL);
00735                         }
00736 
00737                         CHECK_PARSING_STATUS (status, FALSE);
00738                 } else if (strchr ("\t !#$%&", next_chars[0])
00739                            || (next_chars[0] >= '(' && next_chars[0] <= '~')) {
00740                         READ_NEXT_CHAR (a_this, &cur_char);
00741                         g_string_append_unichar (str->stryng, 
00742                                                  cur_char);
00743                         status = CR_OK;
00744                 }
00745 
00746                 else if (cr_utils_is_nonascii (next_chars[0])) {
00747                         READ_NEXT_CHAR (a_this, &cur_char);
00748                         g_string_append_unichar (str->stryng, cur_char);
00749                 } else if (next_chars[0] == delim) {
00750                         READ_NEXT_CHAR (a_this, &cur_char);
00751                         break;
00752                 } else {
00753                         status = CR_PARSING_ERROR;
00754                         goto error;
00755                 }
00756         }
00757 
00758         if (status == CR_OK) {
00759                 if (*a_str == NULL) {
00760                         *a_str = str;
00761                         str = NULL;
00762                 } else {
00763                         (*a_str)->stryng = g_string_append_len
00764                                 ((*a_str)->stryng,
00765                                  str->stryng->str, 
00766                                  str->stryng->len);
00767                         cr_string_destroy (str);
00768                 }
00769                 return CR_OK;
00770         }
00771 
00772  error:
00773 
00774         if (str) {
00775                 cr_string_destroy (str) ;
00776                 str = NULL;
00777         }
00778         cr_tknzr_set_cur_pos (a_this, &init_pos);
00779         return status;
00780 }
00781 
00782 /**
00783  *Parses the an nmstart as defined by the css2 spec [4.1.1]:
00784  * nmstart [a-zA-Z]|{nonascii}|{escape}
00785  *
00786  *@param a_this the current instance of #CRTknzr.
00787  *@param a_start out param. A pointer to the starting point of
00788  *the token.
00789  *@param a_end out param. A pointer to the ending point of the
00790  *token.
00791  *@param a_char out param. The actual parsed nmchar.
00792  *@return CR_OK upon successfull completion, 
00793  *an error code otherwise.
00794  */
00795 static enum CRStatus
00796 cr_tknzr_parse_nmstart (CRTknzr * a_this, 
00797                         guint32 * a_char,
00798                         CRParsingLocation *a_location)
00799 {
00800         CRInputPos init_pos;
00801         enum CRStatus status = CR_OK;
00802         guint32 cur_char = 0,
00803                 next_char = 0;
00804 
00805         g_return_val_if_fail (a_this && PRIVATE (a_this)
00806                               && PRIVATE (a_this)->input
00807                               && a_char, CR_BAD_PARAM_ERROR);
00808 
00809         RECORD_INITIAL_POS (a_this, &init_pos);
00810 
00811         PEEK_NEXT_CHAR (a_this, &next_char);
00812 
00813         if (next_char == '\\') {
00814                 status = cr_tknzr_parse_escape (a_this, a_char,
00815                                                 a_location);
00816 
00817                 if (status != CR_OK)
00818                         goto error;
00819 
00820         } else if (cr_utils_is_nonascii (next_char) == TRUE
00821                    || ((next_char >= 'a') && (next_char <= 'z'))
00822                    || ((next_char >= 'A') && (next_char <= 'Z'))
00823                 ) {
00824                 READ_NEXT_CHAR (a_this, &cur_char);
00825                 if (a_location) {
00826                         cr_tknzr_get_parsing_location (a_this, 
00827                                                        a_location) ;
00828                 }
00829                 *a_char = cur_char;
00830                 status = CR_OK;
00831         } else {
00832                 status = CR_PARSING_ERROR;
00833                 goto error;
00834         }
00835 
00836         return CR_OK;
00837 
00838  error:        
00839         cr_tknzr_set_cur_pos (a_this, &init_pos);
00840 
00841         return status;
00842 
00843 }
00844 
00845 /**
00846  *Parses an nmchar as described in the css spec at
00847  *chap 4.1.1:
00848  *nmchar ::= [a-z0-9-]|{nonascii}|{escape}
00849  *
00850  *Humm, I have added the possibility for nmchar to
00851  *contain upper case letters.
00852  *
00853  *@param a_this the current instance of #CRTknzr.
00854  *@param a_start out param. A pointer to the starting point of
00855  *the token.
00856  *@param a_end out param. A pointer to the ending point of the
00857  *token.
00858  *@param a_char out param. The actual parsed nmchar.
00859  *@return CR_OK upon successfull completion, 
00860  *an error code otherwise.
00861  */
00862 static enum CRStatus
00863 cr_tknzr_parse_nmchar (CRTknzr * a_this, guint32 * a_char,
00864                        CRParsingLocation *a_location)
00865 {
00866         guint32 cur_char = 0,
00867                 next_char = 0;
00868         enum CRStatus status = CR_OK;
00869         CRInputPos init_pos;
00870 
00871         g_return_val_if_fail (a_this && PRIVATE (a_this) && a_char,
00872                               CR_BAD_PARAM_ERROR);
00873 
00874         RECORD_INITIAL_POS (a_this, &init_pos);
00875 
00876         status = cr_input_peek_char (PRIVATE (a_this)->input, 
00877                                      &next_char) ;
00878         if (status != CR_OK)
00879                 goto error;
00880 
00881         if (next_char == '\\') {
00882                 status = cr_tknzr_parse_escape (a_this, a_char, 
00883                                                 a_location);
00884 
00885                 if (status != CR_OK)
00886                         goto error;
00887 
00888         } else if (cr_utils_is_nonascii (next_char) == TRUE
00889                    || ((next_char >= 'a') && (next_char <= 'z'))
00890                    || ((next_char >= 'A') && (next_char <= 'Z'))
00891                    || ((next_char >= '0') && (next_char <= '9'))
00892                    || (next_char == '-')
00893                    || (next_char == '_') /*'_' not allowed by the spec. */
00894                 ) {
00895                 READ_NEXT_CHAR (a_this, &cur_char);
00896                 *a_char = cur_char;
00897                 status = CR_OK;
00898                 if (a_location) {
00899                         cr_tknzr_get_parsing_location
00900                                 (a_this, a_location) ;
00901                 }
00902         } else {
00903                 status = CR_PARSING_ERROR;
00904                 goto error;
00905         }
00906         return CR_OK;
00907 
00908  error:
00909         cr_tknzr_set_cur_pos (a_this, &init_pos);
00910         return status;
00911 }
00912 
00913 /**
00914  *Parses an "ident" as defined in css spec [4.1.1]:
00915  *ident ::= {nmstart}{nmchar}*
00916  *
00917  *Actually parses it using the css3 grammar:
00918  *ident ::= -?{nmstart}{nmchar}*
00919  *@param a_this the currens instance of #CRTknzr.
00920  *
00921  *@param a_str a pointer to parsed ident. If *a_str is NULL,
00922  *this function allocates a new instance of CRString. If not, 
00923  *the function just appends the parsed string to the one passed.
00924  *In both cases it is up to the caller to free *a_str.
00925  *
00926  *@return CR_OK upon successfull completion, an error code 
00927  *otherwise.
00928  */
00929 static enum CRStatus
00930 cr_tknzr_parse_ident (CRTknzr * a_this, CRString ** a_str)
00931 {
00932         guint32 tmp_char = 0;
00933         CRString *stringue = NULL ;
00934         CRInputPos init_pos;
00935         enum CRStatus status = CR_OK;
00936         gboolean location_is_set = FALSE ;
00937 
00938         g_return_val_if_fail (a_this && PRIVATE (a_this)
00939                               && PRIVATE (a_this)->input
00940                               && a_str, CR_BAD_PARAM_ERROR);
00941 
00942         RECORD_INITIAL_POS (a_this, &init_pos);
00943         PEEK_NEXT_CHAR (a_this, &tmp_char) ;
00944         stringue = cr_string_new () ;
00945         g_return_val_if_fail (stringue, 
00946                               CR_OUT_OF_MEMORY_ERROR) ;
00947 
00948         if (tmp_char == '-') {
00949                 READ_NEXT_CHAR (a_this, &tmp_char) ;
00950                 cr_tknzr_get_parsing_location
00951                         (a_this, &stringue->location) ;
00952                 location_is_set = TRUE ;
00953                 g_string_append_unichar (stringue->stryng, 
00954                                          tmp_char) ;
00955         }
00956         status = cr_tknzr_parse_nmstart (a_this, &tmp_char, NULL);
00957         if (status != CR_OK) {
00958                 status = CR_PARSING_ERROR;
00959                 goto end ;
00960         }
00961         if (location_is_set == FALSE) {
00962                 cr_tknzr_get_parsing_location 
00963                         (a_this, &stringue->location) ;
00964                 location_is_set = TRUE ;
00965         }
00966         g_string_append_unichar (stringue->stryng, tmp_char);
00967         for (;;) {
00968                 status = cr_tknzr_parse_nmchar (a_this, 
00969                                                 &tmp_char, 
00970                                                 NULL);
00971                 if (status != CR_OK) {
00972                         status = CR_OK ;
00973                         break;
00974                 }
00975                 g_string_append_unichar (stringue->stryng, tmp_char);
00976         }
00977         if (status == CR_OK) {
00978                 if (!*a_str) {
00979                         *a_str = stringue ;
00980                 
00981                 } else {
00982                         g_string_append_len ((*a_str)->stryng, 
00983                                              stringue->stryng->str, 
00984                                              stringue->stryng->len) ;
00985                         cr_string_destroy (stringue) ;
00986                 }
00987                 stringue = NULL ;
00988         }
00989 
00990  error:
00991  end:
00992         if (stringue) {
00993                 cr_string_destroy (stringue) ;
00994                 stringue = NULL ;
00995         }
00996         if (status != CR_OK ) {
00997                 cr_tknzr_set_cur_pos (a_this, &init_pos) ;
00998         }
00999         return status ;
01000 }
01001 
01002 
01003 /**
01004  *Parses a "name" as defined by css spec [4.1.1]:
01005  *name ::= {nmchar}+
01006  *
01007  *@param a_this the current instance of #CRTknzr.
01008  *
01009  *@param a_str out parameter. A pointer to the successfully parsed
01010  *name. If *a_str is set to NULL, this function allocates a new instance
01011  *of CRString. If not, it just appends the parsed name to the passed *a_str.
01012  *In both cases, it is up to the caller to free *a_str.
01013  *
01014  *@return CR_OK upon successfull completion, an error code otherwise.
01015  */
01016 static enum CRStatus
01017 cr_tknzr_parse_name (CRTknzr * a_this, 
01018                      CRString ** a_str)
01019 {
01020         guint32 tmp_char = 0;
01021         CRInputPos init_pos;
01022         enum CRStatus status = CR_OK;
01023         gboolean str_needs_free = FALSE,
01024                 is_first_nmchar=TRUE ;
01025         glong i = 0;
01026         CRParsingLocation loc = {0} ;
01027 
01028         g_return_val_if_fail (a_this && PRIVATE (a_this)
01029                               && PRIVATE (a_this)->input
01030                               && a_str,
01031                               CR_BAD_PARAM_ERROR) ;
01032 
01033         RECORD_INITIAL_POS (a_this, &init_pos);
01034 
01035         if (*a_str == NULL) {
01036                 *a_str = cr_string_new ();
01037                 str_needs_free = TRUE;
01038         }
01039         for (i = 0;; i++) {
01040                 if (is_first_nmchar == TRUE) {
01041                         status = cr_tknzr_parse_nmchar 
01042                                 (a_this, &tmp_char,
01043                                  &loc) ;
01044                         is_first_nmchar = FALSE ;
01045                 } else {
01046                         status = cr_tknzr_parse_nmchar 
01047                                 (a_this, &tmp_char, NULL) ;
01048                 }
01049                 if (status != CR_OK)
01050                         break;                
01051                 g_string_append_unichar ((*a_str)->stryng, 
01052                                          tmp_char);
01053         }
01054         if (i > 0) {
01055                 cr_parsing_location_copy 
01056                         (&(*a_str)->location, &loc) ;
01057                 return CR_OK;
01058         }
01059         if (str_needs_free == TRUE && *a_str) {
01060                 cr_string_destroy (*a_str);
01061                 *a_str = NULL;
01062         }
01063         cr_tknzr_set_cur_pos (a_this, &init_pos);
01064         return CR_PARSING_ERROR;
01065 }
01066 
01067 /**
01068  *Parses a "hash" as defined by the css spec in [4.1.1]:
01069  *HASH ::= #{name}
01070  */
01071 static enum CRStatus
01072 cr_tknzr_parse_hash (CRTknzr * a_this, CRString ** a_str)
01073 {
01074         guint32 cur_char = 0;
01075         CRInputPos init_pos;
01076         enum CRStatus status = CR_OK;
01077         gboolean str_needs_free = FALSE;
01078         CRParsingLocation loc = {0} ;
01079 
01080         g_return_val_if_fail (a_this && PRIVATE (a_this)
01081                               && PRIVATE (a_this)->input,
01082                               CR_BAD_PARAM_ERROR);
01083 
01084         RECORD_INITIAL_POS (a_this, &init_pos);
01085         READ_NEXT_CHAR (a_this, &cur_char);
01086         if (cur_char != '#') {
01087                 status = CR_PARSING_ERROR;
01088                 goto error;
01089         }
01090         if (*a_str == NULL) {
01091                 *a_str = cr_string_new ();
01092                 str_needs_free = TRUE;
01093         }
01094         cr_tknzr_get_parsing_location (a_this,
01095                                        &loc) ;
01096         status = cr_tknzr_parse_name (a_this, a_str);
01097         cr_parsing_location_copy (&(*a_str)->location, &loc) ;
01098         if (status != CR_OK) {
01099                 goto error;
01100         }
01101         return CR_OK;
01102 
01103  error:
01104         if (str_needs_free == TRUE && *a_str) {
01105                 cr_string_destroy (*a_str);
01106                 *a_str = NULL;
01107         }
01108 
01109         cr_tknzr_set_cur_pos (a_this, &init_pos);
01110         return status;
01111 }
01112 
01113 /**
01114  *Parses an uri as defined by the css spec [4.1.1]:
01115  * URI ::= url\({w}{string}{w}\)
01116  *         |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)
01117  *
01118  *@param a_this the current instance of #CRTknzr.
01119  *@param a_str the successfully parsed url.
01120  *@return CR_OK upon successfull completion, an error code otherwise.
01121  */
01122 static enum CRStatus
01123 cr_tknzr_parse_uri (CRTknzr * a_this, 
01124                     CRString ** a_str)
01125 {
01126         guint32 cur_char = 0;
01127         CRInputPos init_pos;
01128         enum CRStatus status = CR_PARSING_ERROR;
01129         guchar tab[4] = { 0 }, *tmp_ptr1 = NULL, *tmp_ptr2 = NULL;
01130         CRString *str = NULL;
01131         CRParsingLocation location = {0} ;
01132 
01133         g_return_val_if_fail (a_this 
01134                               && PRIVATE (a_this)
01135                               && PRIVATE (a_this)->input
01136                               && a_str, 
01137                               CR_BAD_PARAM_ERROR);
01138 
01139         RECORD_INITIAL_POS (a_this, &init_pos);
01140 
01141         PEEK_BYTE (a_this, 1, &tab[0]);
01142         PEEK_BYTE (a_this, 2, &tab[1]);
01143         PEEK_BYTE (a_this, 3, &tab[2]);
01144         PEEK_BYTE (a_this, 4, &tab[3]);
01145 
01146         if (tab[0] != 'u' || tab[1] != 'r' || tab[2] != 'l' || tab[3] != '(') {
01147                 status = CR_PARSING_ERROR;
01148                 goto error;
01149         }
01150         /*
01151          *Here, we want to skip 4 bytes ('u''r''l''(').
01152          *But we also need to keep track of the parsing location
01153          *of the 'u'. So, we skip 1 byte, we record the parsing
01154          *location, then we skip the 3 remaining bytes.
01155          */
01156         SKIP_CHARS (a_this, 1);
01157         cr_tknzr_get_parsing_location (a_this, &location) ;
01158         SKIP_CHARS (a_this, 3);
01159         cr_tknzr_try_to_skip_spaces (a_this);
01160         status = cr_tknzr_parse_string (a_this, a_str);
01161 
01162         if (status == CR_OK) {
01163                 guint32 next_char = 0;
01164                 status = cr_tknzr_parse_w (a_this, &tmp_ptr1, 
01165                                            &tmp_ptr2, NULL);
01166                 cr_tknzr_try_to_skip_spaces (a_this);
01167                 PEEK_NEXT_CHAR (a_this, &next_char);
01168                 if (next_char == ')') {
01169                         READ_NEXT_CHAR (a_this, &cur_char);
01170                         status = CR_OK;
01171                 } else {
01172                         status = CR_PARSING_ERROR;
01173                 }
01174         }
01175         if (status != CR_OK) {
01176                 str = cr_string_new ();
01177                 for (;;) {
01178                         guint32 next_char = 0;
01179                         PEEK_NEXT_CHAR (a_this, &next_char);
01180                         if (strchr ("!#$%&", next_char)
01181                             || (next_char >= '*' && next_char <= '~')
01182                             || (cr_utils_is_nonascii (next_char) == TRUE)) {
01183                                 READ_NEXT_CHAR (a_this, &cur_char);
01184                                 g_string_append_unichar 
01185                                         (str->stryng, cur_char);
01186                                 status = CR_OK;
01187                         } else {
01188                                 guint32 esc_code = 0;
01189                                 status = cr_tknzr_parse_escape
01190                                         (a_this, &esc_code, NULL);
01191                                 if (status == CR_OK) {
01192                                         g_string_append_unichar
01193                                                 (str->stryng, 
01194                                                  esc_code);
01195                                 } else {
01196                                         status = CR_OK;
01197                                         break;
01198                                 }
01199                         }
01200                 }
01201                 cr_tknzr_try_to_skip_spaces (a_this);
01202                 READ_NEXT_CHAR (a_this, &cur_char);
01203                 if (cur_char == ')') {
01204                         status = CR_OK;
01205                 } else {
01206                         status = CR_PARSING_ERROR;
01207                         goto error;
01208                 }
01209                 if (str) {                        
01210                         if (*a_str == NULL) {
01211                                 *a_str = str;
01212                                 str = NULL;
01213                         } else {
01214                                 g_string_append_len
01215                                         ((*a_str)->stryng,
01216                                          str->stryng->str,
01217                                          str->stryng->len);
01218                                 cr_string_destroy (str);
01219                         }                        
01220                 }
01221         }
01222 
01223         cr_parsing_location_copy
01224                 (&(*a_str)->location,
01225                  &location) ;
01226         return CR_OK ;
01227  error:
01228         if (str) {
01229                 cr_string_destroy (str);
01230                 str = NULL;
01231         }
01232         cr_tknzr_set_cur_pos (a_this, &init_pos);
01233         return status;
01234 }
01235 
01236 /**
01237  *parses an RGB as defined in the css2 spec.
01238  *rgb: rgb '('S*{num}%?S* ',' {num}#?S*,S*{num}#?S*')'
01239  *
01240  *@param a_this the "this pointer" of the current instance of
01241  *@param a_rgb out parameter the parsed rgb.
01242  *@return CR_OK upon successfull completion, an error code otherwise.
01243  */
01244 static enum CRStatus
01245 cr_tknzr_parse_rgb (CRTknzr * a_this, CRRgb ** a_rgb)
01246 {
01247         enum CRStatus status = CR_OK;
01248         CRInputPos init_pos;
01249         CRNum *num = NULL;
01250         guchar next_bytes[3] = { 0 }, cur_byte = 0;
01251         glong red = 0,
01252                 green = 0,
01253                 blue = 0,
01254                 i = 0;
01255         gboolean is_percentage = FALSE;
01256         CRParsingLocation location = {0} ;
01257 
01258         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01259 
01260         RECORD_INITIAL_POS (a_this, &init_pos);
01261 
01262         PEEK_BYTE (a_this, 1, &next_bytes[0]);
01263         PEEK_BYTE (a_this, 2, &next_bytes[1]);
01264         PEEK_BYTE (a_this, 3, &next_bytes[2]);
01265 
01266         if (((next_bytes[0] == 'r') || (next_bytes[0] == 'R'))
01267             && ((next_bytes[1] == 'g') || (next_bytes[1] == 'G'))
01268             && ((next_bytes[2] == 'b') || (next_bytes[2] == 'B'))) {
01269                 SKIP_CHARS (a_this, 1);
01270                 cr_tknzr_get_parsing_location (a_this, &location) ;
01271                 SKIP_CHARS (a_this, 2);
01272         } else {
01273                 status = CR_PARSING_ERROR;
01274                 goto error;
01275         }
01276         READ_NEXT_BYTE (a_this, &cur_byte);
01277         ENSURE_PARSING_COND (cur_byte == '(');
01278 
01279         cr_tknzr_try_to_skip_spaces (a_this);
01280         status = cr_tknzr_parse_num (a_this, &num);
01281         ENSURE_PARSING_COND ((status == CR_OK) && (num != NULL));
01282 
01283         red = num->val;
01284         cr_num_destroy (num);
01285         num = NULL;
01286 
01287         PEEK_BYTE (a_this, 1, &next_bytes[0]);
01288         if (next_bytes[0] == '%') {
01289                 SKIP_CHARS (a_this, 1);
01290                 is_percentage = TRUE;
01291         }
01292         cr_tknzr_try_to_skip_spaces (a_this);
01293 
01294         for (i = 0; i < 2; i++) {
01295                 READ_NEXT_BYTE (a_this, &cur_byte);
01296                 ENSURE_PARSING_COND (cur_byte == ',');
01297 
01298                 cr_tknzr_try_to_skip_spaces (a_this);
01299                 status = cr_tknzr_parse_num (a_this, &num);
01300                 ENSURE_PARSING_COND ((status == CR_OK) && (num != NULL));
01301 
01302                 PEEK_BYTE (a_this, 1, &next_bytes[0]);
01303                 if (next_bytes[0] == '%') {
01304                         SKIP_CHARS (a_this, 1);
01305                         is_percentage = 1;
01306                 }
01307 
01308                 if (i == 0) {
01309                         green = num->val;
01310                 } else if (i == 1) {
01311                         blue = num->val;
01312                 }
01313 
01314                 if (num) {
01315                         cr_num_destroy (num);
01316                         num = NULL;
01317                 }
01318                 cr_tknzr_try_to_skip_spaces (a_this);
01319         }
01320 
01321         READ_NEXT_BYTE (a_this, &cur_byte);
01322         if (*a_rgb == NULL) {
01323                 *a_rgb = cr_rgb_new_with_vals (red, green, blue,
01324                                                is_percentage);
01325 
01326                 if (*a_rgb == NULL) {
01327                         status = CR_ERROR;
01328                         goto error;
01329                 }
01330                 status = CR_OK;
01331         } else {
01332                 (*a_rgb)->red = red;
01333                 (*a_rgb)->green = green;
01334                 (*a_rgb)->blue = blue;
01335                 (*a_rgb)->is_percentage = is_percentage;
01336 
01337                 status = CR_OK;
01338         }
01339 
01340         if (status == CR_OK) {
01341                 if (a_rgb && *a_rgb) {
01342                         cr_parsing_location_copy 
01343                                 (&(*a_rgb)->location, 
01344                                  &location) ;
01345                 }
01346                 return CR_OK;
01347         }
01348 
01349  error:
01350         if (num) {
01351                 cr_num_destroy (num);
01352                 num = NULL;
01353         }
01354 
01355         cr_tknzr_set_cur_pos (a_this, &init_pos);
01356         return CR_OK;
01357 }
01358 
01359 /**
01360  *Parses a atkeyword as defined by the css spec in [4.1.1]:
01361  *ATKEYWORD ::= @{ident}
01362  *
01363  *@param a_this the "this pointer" of the current instance of
01364  *#CRTknzr.
01365  *
01366  *@param a_str out parameter. The parsed atkeyword. If *a_str is
01367  *set to NULL this function allocates a new instance of CRString and
01368  *sets it to the parsed atkeyword. If not, this function just appends
01369  *the parsed atkeyword to the end of *a_str. In both cases it is up to
01370  *the caller to free *a_str.
01371  *
01372  *@return CR_OK upon successfull completion, an error code otherwise.
01373  */
01374 static enum CRStatus
01375 cr_tknzr_parse_atkeyword (CRTknzr * a_this, 
01376                           CRString ** a_str)
01377 {
01378         guint32 cur_char = 0;
01379         CRInputPos init_pos;
01380         gboolean str_needs_free = FALSE;
01381         enum CRStatus status = CR_OK;
01382 
01383         g_return_val_if_fail (a_this && PRIVATE (a_this)
01384                               && PRIVATE (a_this)->input
01385                               && a_str, CR_BAD_PARAM_ERROR);
01386 
01387         RECORD_INITIAL_POS (a_this, &init_pos);
01388 
01389         READ_NEXT_CHAR (a_this, &cur_char);
01390 
01391         if (cur_char != '@') {
01392                 status = CR_PARSING_ERROR;
01393                 goto error;
01394         }
01395 
01396         if (*a_str == NULL) {
01397                 *a_str = cr_string_new ();
01398                 str_needs_free = TRUE;
01399         }
01400         status = cr_tknzr_parse_ident (a_this, a_str);
01401         if (status != CR_OK) {
01402                 goto error;
01403         }
01404         return CR_OK;
01405  error:
01406 
01407         if (str_needs_free == TRUE && *a_str) {
01408                 cr_string_destroy (*a_str);
01409                 *a_str = NULL;
01410         }
01411         cr_tknzr_set_cur_pos (a_this, &init_pos);
01412         return status;
01413 }
01414 
01415 static enum CRStatus
01416 cr_tknzr_parse_important (CRTknzr * a_this,
01417                           CRParsingLocation *a_location)
01418 {
01419         guint32 cur_char = 0;
01420         CRInputPos init_pos;
01421         enum CRStatus status = CR_OK;
01422 
01423         g_return_val_if_fail (a_this && PRIVATE (a_this)
01424                               && PRIVATE (a_this)->input,
01425                               CR_BAD_PARAM_ERROR);
01426 
01427         RECORD_INITIAL_POS (a_this, &init_pos);
01428         READ_NEXT_CHAR (a_this, &cur_char);
01429         ENSURE_PARSING_COND (cur_char == '!');
01430         if (a_location) {
01431                 cr_tknzr_get_parsing_location (a_this, 
01432                                                a_location) ;
01433         }
01434         cr_tknzr_try_to_skip_spaces (a_this);
01435 
01436         if (BYTE (PRIVATE (a_this)->input, 1, NULL) == 'i'
01437             && BYTE (PRIVATE (a_this)->input, 2, NULL) == 'm'
01438             && BYTE (PRIVATE (a_this)->input, 3, NULL) == 'p'
01439             && BYTE (PRIVATE (a_this)->input, 4, NULL) == 'o'
01440             && BYTE (PRIVATE (a_this)->input, 5, NULL) == 'r'
01441             && BYTE (PRIVATE (a_this)->input, 6, NULL) == 't'
01442             && BYTE (PRIVATE (a_this)->input, 7, NULL) == 'a'
01443             && BYTE (PRIVATE (a_this)->input, 8, NULL) == 'n'
01444             && BYTE (PRIVATE (a_this)->input, 9, NULL) == 't') {
01445                 SKIP_BYTES (a_this, 9);
01446                 if (a_location) {
01447                         cr_tknzr_get_parsing_location (a_this,
01448                                                        a_location) ;
01449                 }
01450                 return CR_OK;
01451         } else {
01452                 status = CR_PARSING_ERROR;
01453         }
01454 
01455  error:
01456         cr_tknzr_set_cur_pos (a_this, &init_pos);
01457 
01458         return status;
01459 }
01460 
01461 /**
01462  *Parses a num as defined in the css spec [4.1.1]:
01463  *[0-9]+|[0-9]*\.[0-9]+
01464  *@param a_this the current instance of #CRTknzr.
01465  *@param a_num out parameter. The parsed number.
01466  *@return CR_OK upon successfull completion, 
01467  *an error code otherwise.
01468  *
01469  *The CSS specification says that numbers may be
01470  *preceeded by '+' or '-' to indicate the sign.
01471  *Technically, the "num" construction as defined
01472  *by the tokenizer doesn't allow this, but we parse
01473  *it here for simplicity.
01474  */
01475 static enum CRStatus
01476 cr_tknzr_parse_num (CRTknzr * a_this, 
01477                     CRNum ** a_num)
01478 {
01479         enum CRStatus status = CR_PARSING_ERROR;
01480         enum CRNumType val_type = NUM_GENERIC;
01481         gboolean parsing_dec,  /* true iff seen decimal point. */
01482                 parsed; /* true iff the substring seen so far is a valid CSS
01483                            number, i.e. `[0-9]+|[0-9]*\.[0-9]+'. */
01484         guint32 cur_char = 0,
01485                 next_char = 0;
01486         gdouble numerator, denominator = 1;
01487         CRInputPos init_pos;
01488         CRParsingLocation location = {0} ;
01489         int sign = 1;
01490 
01491         g_return_val_if_fail (a_this && PRIVATE (a_this)
01492                               && PRIVATE (a_this)->input, 
01493                               CR_BAD_PARAM_ERROR);
01494 
01495         RECORD_INITIAL_POS (a_this, &init_pos);
01496         READ_NEXT_CHAR (a_this, &cur_char);
01497 
01498         if (cur_char == '+' || cur_char == '-') {
01499                 if (cur_char == '-') {
01500                         sign = -1;
01501                 }
01502                 READ_NEXT_CHAR (a_this, &cur_char);
01503         }
01504 
01505         if (IS_NUM (cur_char)) {
01506                 numerator = (cur_char - '0');
01507                 parsing_dec = FALSE;
01508                 parsed = TRUE;
01509         } else if (cur_char == '.') {
01510                 numerator = 0;
01511                 parsing_dec = TRUE;
01512                 parsed = FALSE;
01513         } else {
01514                 status = CR_PARSING_ERROR;
01515                 goto error;
01516         }
01517         cr_tknzr_get_parsing_location (a_this, &location) ;
01518 
01519         for (;;) {
01520                 status = cr_tknzr_peek_char (a_this, &next_char);
01521                 if (status != CR_OK) {
01522                         if (status == CR_END_OF_INPUT_ERROR)
01523                                 status = CR_OK;
01524                         break;
01525                 }
01526                 if (next_char == '.') {
01527                         if (parsing_dec) {
01528                                 status = CR_PARSING_ERROR;
01529                                 goto error;
01530                         }
01531 
01532                         READ_NEXT_CHAR (a_this, &cur_char);
01533                         parsing_dec = TRUE;
01534                         parsed = FALSE;  /* In CSS, there must be at least
01535                                             one digit after `.'. */
01536                 } else if (IS_NUM (next_char)) {
01537                         READ_NEXT_CHAR (a_this, &cur_char);
01538                         parsed = TRUE;
01539 
01540                         numerator = numerator * 10 + (cur_char - '0');
01541                         if (parsing_dec) {
01542                                 denominator *= 10;
01543                         }
01544                 } else {
01545                         break;
01546                 }
01547         }
01548 
01549         if (!parsed) {
01550                 status = CR_PARSING_ERROR;
01551         }
01552 
01553         /*
01554          *Now, set the output param values.
01555          */
01556         if (status == CR_OK) {
01557                 gdouble val = (numerator / denominator) * sign;
01558                 if (*a_num == NULL) {
01559                         *a_num = cr_num_new_with_val (val, val_type);
01560 
01561                         if (*a_num == NULL) {
01562                                 status = CR_ERROR;
01563                                 goto error;
01564                         }
01565                 } else {
01566                         (*a_num)->val = val;
01567                         (*a_num)->type = val_type;
01568                 }
01569                 cr_parsing_location_copy (&(*a_num)->location,
01570                                           &location) ;
01571                 return CR_OK;
01572         }
01573 
01574  error:
01575 
01576         cr_tknzr_set_cur_pos (a_this, &init_pos);
01577 
01578         return status;
01579 }
01580 
01581 /*********************************************
01582  *PUBLIC methods
01583  ********************************************/
01584 
01585 CRTknzr *
01586 cr_tknzr_new (CRInput * a_input)
01587 {
01588         CRTknzr *result = NULL;
01589 
01590         result = g_try_malloc (sizeof (CRTknzr));
01591 
01592         if (result == NULL) {
01593                 cr_utils_trace_info ("Out of memory");
01594                 return NULL;
01595         }
01596 
01597         memset (result, 0, sizeof (CRTknzr));
01598 
01599         result->priv = g_try_malloc (sizeof (CRTknzrPriv));
01600 
01601         if (result->priv == NULL) {
01602                 cr_utils_trace_info ("Out of memory");
01603 
01604                 if (result) {
01605                         g_free (result);
01606                         result = NULL;
01607                 }
01608 
01609                 return NULL;
01610         }
01611         memset (result->priv, 0, sizeof (CRTknzrPriv));
01612         if (a_input)
01613                 cr_tknzr_set_input (result, a_input);
01614         return result;
01615 }
01616 
01617 CRTknzr *
01618 cr_tknzr_new_from_buf (guchar * a_buf, gulong a_len,
01619                        enum CREncoding a_enc, 
01620                        gboolean a_free_at_destroy)
01621 {
01622         CRTknzr *result = NULL;
01623         CRInput *input = NULL;
01624 
01625         input = cr_input_new_from_buf (a_buf, a_len, a_enc,
01626                                        a_free_at_destroy);
01627 
01628         g_return_val_if_fail (input != NULL, NULL);
01629 
01630         result = cr_tknzr_new (input);
01631 
01632         return result;
01633 }
01634 
01635 CRTknzr *
01636 cr_tknzr_new_from_uri (const guchar * a_file_uri, 
01637                        enum CREncoding a_enc)
01638 {
01639         CRTknzr *result = NULL;
01640         CRInput *input = NULL;
01641 
01642         input = cr_input_new_from_uri (a_file_uri, a_enc);
01643         g_return_val_if_fail (input != NULL, NULL);
01644 
01645         result = cr_tknzr_new (input);
01646 
01647         return result;
01648 }
01649 
01650 void
01651 cr_tknzr_ref (CRTknzr * a_this)
01652 {
01653         g_return_if_fail (a_this && PRIVATE (a_this));
01654 
01655         PRIVATE (a_this)->ref_count++;
01656 }
01657 
01658 gboolean
01659 cr_tknzr_unref (CRTknzr * a_this)
01660 {
01661         g_return_val_if_fail (a_this && PRIVATE (a_this), FALSE);
01662 
01663         if (PRIVATE (a_this)->ref_count > 0) {
01664                 PRIVATE (a_this)->ref_count--;
01665         }
01666 
01667         if (PRIVATE (a_this)->ref_count == 0) {
01668                 cr_tknzr_destroy (a_this);
01669                 return TRUE;
01670         }
01671 
01672         return FALSE;
01673 }
01674 
01675 enum CRStatus
01676 cr_tknzr_set_input (CRTknzr * a_this, CRInput * a_input)
01677 {
01678         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01679 
01680         if (PRIVATE (a_this)->input) {
01681                 cr_input_unref (PRIVATE (a_this)->input);
01682         }
01683 
01684         PRIVATE (a_this)->input = a_input;
01685 
01686         cr_input_ref (PRIVATE (a_this)->input);
01687 
01688         return CR_OK;
01689 }
01690 
01691 enum CRStatus
01692 cr_tknzr_get_input (CRTknzr * a_this, CRInput ** a_input)
01693 {
01694         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01695 
01696         *a_input = PRIVATE (a_this)->input;
01697 
01698         return CR_OK;
01699 }
01700 
01701 /*********************************
01702  *Tokenizer input handling routines
01703  *********************************/
01704 
01705 /**
01706  *Reads the next byte from the parser input stream.
01707  *@param a_this the "this pointer" of the current instance of
01708  *#CRParser.
01709  *@param a_byte out parameter the place where to store the byte
01710  *read.
01711  *@return CR_OK upon successfull completion, an error 
01712  *code otherwise.
01713  */
01714 enum CRStatus
01715 cr_tknzr_read_byte (CRTknzr * a_this, guchar * a_byte)
01716 {
01717         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01718 
01719         return cr_input_read_byte (PRIVATE (a_this)->input, a_byte);
01720 
01721 }
01722 
01723 /**
01724  *Reads the next char from the parser input stream.
01725  *@param a_this the current instance of #CRTknzr.
01726  *@param a_char out parameter. The read char.
01727  *@return CR_OK upon successfull completion, an error code
01728  *otherwise.
01729  */
01730 enum CRStatus
01731 cr_tknzr_read_char (CRTknzr * a_this, guint32 * a_char)
01732 {
01733         g_return_val_if_fail (a_this && PRIVATE (a_this)
01734                               && PRIVATE (a_this)->input
01735                               && a_char, CR_BAD_PARAM_ERROR);
01736 
01737         if (PRIVATE (a_this)->token_cache) {
01738                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
01739                                       &PRIVATE (a_this)->prev_pos);
01740                 cr_token_destroy (PRIVATE (a_this)->token_cache);
01741                 PRIVATE (a_this)->token_cache = NULL;
01742         }
01743 
01744         return cr_input_read_char (PRIVATE (a_this)->input, a_char);
01745 }
01746 
01747 /**
01748  *Peeks a char from the parser input stream.
01749  *To "peek a char" means reads the next char without consuming it.
01750  *Subsequent calls to this function return the same char.
01751  *@param a_this the current instance of #CRTknzr.
01752  *@param a_char out parameter. The peeked char uppon successfull completion.
01753  *@return CR_OK upon successfull completion, an error code otherwise.
01754  */
01755 enum CRStatus
01756 cr_tknzr_peek_char (CRTknzr * a_this, guint32 * a_char)
01757 {
01758         g_return_val_if_fail (a_this && PRIVATE (a_this)
01759                               && PRIVATE (a_this)->input
01760                               && a_char, CR_BAD_PARAM_ERROR);
01761 
01762         if (PRIVATE (a_this)->token_cache) {
01763                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
01764                                       &PRIVATE (a_this)->prev_pos);
01765                 cr_token_destroy (PRIVATE (a_this)->token_cache);
01766                 PRIVATE (a_this)->token_cache = NULL;
01767         }
01768 
01769         return cr_input_peek_char (PRIVATE (a_this)->input, a_char);
01770 }
01771 
01772 /**
01773  *Peeks a byte ahead at a given postion in the parser input stream.
01774  *@param a_this the current instance of #CRTknzr.
01775  *@param a_offset the offset of the peeked byte starting from the current
01776  *byte in the parser input stream.
01777  *@param a_byte out parameter. The peeked byte upon 
01778  *successfull completion.
01779  *@return CR_OK upon successfull completion, an error code otherwise.
01780  */
01781 enum CRStatus
01782 cr_tknzr_peek_byte (CRTknzr * a_this, gulong a_offset, guchar * a_byte)
01783 {
01784         g_return_val_if_fail (a_this && PRIVATE (a_this)
01785                               && PRIVATE (a_this)->input && a_byte,
01786                               CR_BAD_PARAM_ERROR);
01787 
01788         if (PRIVATE (a_this)->token_cache) {
01789                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
01790                                       &PRIVATE (a_this)->prev_pos);
01791                 cr_token_destroy (PRIVATE (a_this)->token_cache);
01792                 PRIVATE (a_this)->token_cache = NULL;
01793         }
01794 
01795         return cr_input_peek_byte (PRIVATE (a_this)->input,
01796                                    CR_SEEK_CUR, a_offset, a_byte);
01797 }
01798 
01799 /**
01800  *Same as cr_tknzr_peek_byte() but this api returns the byte peeked.
01801  *@param a_this the current instance of #CRTknzr.
01802  *@param a_offset the offset of the peeked byte starting from the current
01803  *byte in the parser input stream.
01804  *@param a_eof out parameter. If not NULL, is set to TRUE if we reached end of
01805  *file, FALE otherwise. If the caller sets it to NULL, this parameter 
01806  *is just ignored.
01807  *@return the peeked byte.
01808  */
01809 guchar
01810 cr_tknzr_peek_byte2 (CRTknzr * a_this, gulong a_offset, gboolean * a_eof)
01811 {
01812         g_return_val_if_fail (a_this && PRIVATE (a_this)
01813                               && PRIVATE (a_this)->input, 0);
01814 
01815         return cr_input_peek_byte2 (PRIVATE (a_this)->input, a_offset, a_eof);
01816 }
01817 
01818 /**
01819  *Gets the number of bytes left in the topmost input stream
01820  *associated to this parser.
01821  *@param a_this the current instance of #CRTknzr
01822  *@return the number of bytes left or -1 in case of error.
01823  */
01824 glong
01825 cr_tknzr_get_nb_bytes_left (CRTknzr * a_this)
01826 {
01827         g_return_val_if_fail (a_this && PRIVATE (a_this)
01828                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
01829 
01830         if (PRIVATE (a_this)->token_cache) {
01831                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
01832                                       &PRIVATE (a_this)->prev_pos);
01833                 cr_token_destroy (PRIVATE (a_this)->token_cache);
01834                 PRIVATE (a_this)->token_cache = NULL;
01835         }
01836 
01837         return cr_input_get_nb_bytes_left (PRIVATE (a_this)->input);
01838 }
01839 
01840 enum CRStatus
01841 cr_tknzr_get_cur_pos (CRTknzr * a_this, CRInputPos * a_pos)
01842 {
01843         g_return_val_if_fail (a_this && PRIVATE (a_this)
01844                               && PRIVATE (a_this)->input
01845                               && a_pos, CR_BAD_PARAM_ERROR);
01846 
01847         if (PRIVATE (a_this)->token_cache) {
01848                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
01849                                       &PRIVATE (a_this)->prev_pos);
01850                 cr_token_destroy (PRIVATE (a_this)->token_cache);
01851                 PRIVATE (a_this)->token_cache = NULL;
01852         }
01853 
01854         return cr_input_get_cur_pos (PRIVATE (a_this)->input, a_pos);
01855 }
01856 
01857 enum CRStatus 
01858 cr_tknzr_get_parsing_location (CRTknzr *a_this,
01859                                CRParsingLocation *a_loc)
01860 {
01861         g_return_val_if_fail (a_this 
01862                               && PRIVATE (a_this)
01863                               && a_loc,
01864                               CR_BAD_PARAM_ERROR) ;
01865 
01866         return cr_input_get_parsing_location 
01867                 (PRIVATE (a_this)->input, a_loc) ;
01868 }
01869 
01870 enum CRStatus
01871 cr_tknzr_get_cur_byte_addr (CRTknzr * a_this, guchar ** a_addr)
01872 {
01873         g_return_val_if_fail (a_this && PRIVATE (a_this)
01874                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
01875         if (PRIVATE (a_this)->token_cache) {
01876                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
01877                                       &PRIVATE (a_this)->prev_pos);
01878                 cr_token_destroy (PRIVATE (a_this)->token_cache);
01879                 PRIVATE (a_this)->token_cache = NULL;
01880         }
01881 
01882         return cr_input_get_cur_byte_addr (PRIVATE (a_this)->input, a_addr);
01883 }
01884 
01885 enum CRStatus
01886 cr_tknzr_seek_index (CRTknzr * a_this, enum CRSeekPos a_origin, gint a_pos)
01887 {
01888         g_return_val_if_fail (a_this && PRIVATE (a_this)
01889                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
01890 
01891         if (PRIVATE (a_this)->token_cache) {
01892                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
01893                                       &PRIVATE (a_this)->prev_pos);
01894                 cr_token_destroy (PRIVATE (a_this)->token_cache);
01895                 PRIVATE (a_this)->token_cache = NULL;
01896         }
01897 
01898         return cr_input_seek_index (PRIVATE (a_this)->input, a_origin, a_pos);
01899 }
01900 
01901 enum CRStatus
01902 cr_tknzr_consume_chars (CRTknzr * a_this, guint32 a_char, glong * a_nb_char)
01903 {
01904         g_return_val_if_fail (a_this && PRIVATE (a_this)
01905                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
01906 
01907         if (PRIVATE (a_this)->token_cache) {
01908                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
01909                                       &PRIVATE (a_this)->prev_pos);
01910                 cr_token_destroy (PRIVATE (a_this)->token_cache);
01911                 PRIVATE (a_this)->token_cache = NULL;
01912         }
01913 
01914         return cr_input_consume_chars (PRIVATE (a_this)->input,
01915                                        a_char, a_nb_char);
01916 }
01917 
01918 enum CRStatus
01919 cr_tknzr_set_cur_pos (CRTknzr * a_this, CRInputPos * a_pos)
01920 {
01921         g_return_val_if_fail (a_this && PRIVATE (a_this)
01922                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
01923 
01924         if (PRIVATE (a_this)->token_cache) {
01925                 cr_token_destroy (PRIVATE (a_this)->token_cache);
01926                 PRIVATE (a_this)->token_cache = NULL;
01927         }
01928 
01929         return cr_input_set_cur_pos (PRIVATE (a_this)->input, a_pos);
01930 }
01931 
01932 enum CRStatus
01933 cr_tknzr_unget_token (CRTknzr * a_this, CRToken * a_token)
01934 {
01935         g_return_val_if_fail (a_this && PRIVATE (a_this)
01936                               && PRIVATE (a_this)->token_cache == NULL,
01937                               CR_BAD_PARAM_ERROR);
01938 
01939         PRIVATE (a_this)->token_cache = a_token;
01940 
01941         return CR_OK;
01942 }
01943 
01944 /**
01945  *Returns the next token of the input stream.
01946  *This method is really central. Each parsing
01947  *method calls it.
01948  *@param a_this the current tokenizer.
01949  *@param a_tk out parameter. The returned token.
01950  *for the sake of mem leak avoidance, *a_tk must
01951  *be NULL.
01952  *@param CR_OK upon successfull completion, an error code
01953  *otherwise.
01954  */
01955 enum CRStatus
01956 cr_tknzr_get_next_token (CRTknzr * a_this, CRToken ** a_tk)
01957 {
01958         enum CRStatus status = CR_OK;
01959         CRToken *token = NULL;
01960         CRInputPos init_pos;
01961         guint32 next_char = 0;
01962         guchar next_bytes[4] = { 0 };
01963         gboolean reached_eof = FALSE;
01964         CRInput *input = NULL;
01965         CRString *str = NULL;
01966         CRRgb *rgb = NULL;
01967         CRParsingLocation location = {0} ;
01968 
01969         g_return_val_if_fail (a_this && PRIVATE (a_this)
01970                               && a_tk && *a_tk == NULL
01971                               && PRIVATE (a_this)->input, 
01972                               CR_BAD_PARAM_ERROR);
01973 
01974         if (PRIVATE (a_this)->token_cache) {
01975                 *a_tk = PRIVATE (a_this)->token_cache;
01976                 PRIVATE (a_this)->token_cache = NULL;
01977                 return CR_OK;
01978         }
01979 
01980         RECORD_INITIAL_POS (a_this, &init_pos);
01981 
01982         status = cr_input_get_end_of_file
01983                 (PRIVATE (a_this)->input, &reached_eof);
01984         ENSURE_PARSING_COND (status == CR_OK);
01985 
01986         if (reached_eof == TRUE) {
01987                 status = CR_END_OF_INPUT_ERROR;
01988                 goto error;
01989         }
01990 
01991         input = PRIVATE (a_this)->input;
01992 
01993         PEEK_NEXT_CHAR (a_this, &next_char);
01994         token = cr_token_new ();
01995         ENSURE_PARSING_COND (token);
01996 
01997         switch (next_char) {
01998         case '@':
01999                 {
02000                         if (BYTE (input, 2, NULL) == 'f'
02001                             && BYTE (input, 3, NULL) == 'o'
02002                             && BYTE (input, 4, NULL) == 'n'
02003                             && BYTE (input, 5, NULL) == 't'
02004                             && BYTE (input, 6, NULL) == '-'
02005                             && BYTE (input, 7, NULL) == 'f'
02006                             && BYTE (input, 8, NULL) == 'a'
02007                             && BYTE (input, 9, NULL) == 'c'
02008                             && BYTE (input, 10, NULL) == 'e') {
02009                                 SKIP_CHARS (a_this, 1);
02010                                 cr_tknzr_get_parsing_location 
02011                                         (a_this, &location) ;
02012                                 SKIP_CHARS (a_this, 9);
02013                                 status = cr_token_set_font_face_sym (token);
02014                                 CHECK_PARSING_STATUS (status, TRUE);
02015                                 cr_parsing_location_copy (&token->location,
02016                                                           &location) ;
02017                                 goto done;
02018                         }
02019 
02020                         if (BYTE (input, 2, NULL) == 'c'
02021                             && BYTE (input, 3, NULL) == 'h'
02022                             && BYTE (input, 4, NULL) == 'a'
02023                             && BYTE (input, 5, NULL) == 'r'
02024                             && BYTE (input, 6, NULL) == 's'
02025                             && BYTE (input, 7, NULL) == 'e'
02026                             && BYTE (input, 8, NULL) == 't') {
02027                                 SKIP_CHARS (a_this, 1);
02028                                 cr_tknzr_get_parsing_location
02029                                         (a_this, &location) ;
02030                                 SKIP_CHARS (a_this, 7);
02031                                 status = cr_token_set_charset_sym (token);
02032                                 CHECK_PARSING_STATUS (status, TRUE);
02033                                 cr_parsing_location_copy (&token->location,
02034                                                           &location) ;
02035                                 goto done;
02036                         }
02037 
02038                         if (BYTE (input, 2, NULL) == 'i'
02039                             && BYTE (input, 3, NULL) == 'm'
02040                             && BYTE (input, 4, NULL) == 'p'
02041                             && BYTE (input, 5, NULL) == 'o'
02042                             && BYTE (input, 6, NULL) == 'r'
02043                             && BYTE (input, 7, NULL) == 't') {
02044                                 SKIP_CHARS (a_this, 1);
02045                                 cr_tknzr_get_parsing_location 
02046                                         (a_this, &location) ;
02047                                 SKIP_CHARS (a_this, 6);
02048                                 status = cr_token_set_import_sym (token);
02049                                 CHECK_PARSING_STATUS (status, TRUE);
02050                                 cr_parsing_location_copy (&token->location,
02051                                                           &location) ;
02052                                 goto done;
02053                         }
02054 
02055                         if (BYTE (input, 2, NULL) == 'm'
02056                             && BYTE (input, 3, NULL) == 'e'
02057                             && BYTE (input, 4, NULL) == 'd'
02058                             && BYTE (input, 5, NULL) == 'i'
02059                             && BYTE (input, 6, NULL) == 'a') {
02060                                 SKIP_CHARS (a_this, 1);
02061                                 cr_tknzr_get_parsing_location (a_this, 
02062                                                                &location) ;
02063                                 SKIP_CHARS (a_this, 5);
02064                                 status = cr_token_set_media_sym (token);
02065                                 CHECK_PARSING_STATUS (status, TRUE);
02066                                 cr_parsing_location_copy (&token->location, 
02067                                                           &location) ;
02068                                 goto done;
02069                         }
02070 
02071                         if (BYTE (input, 2, NULL) == 'p'
02072                             && BYTE (input, 3, NULL) == 'a'
02073                             && BYTE (input, 4, NULL) == 'g'
02074                             && BYTE (input, 5, NULL) == 'e') {
02075                                 SKIP_CHARS (a_this, 1);
02076                                 cr_tknzr_get_parsing_location (a_this, 
02077                                                                &location) ;
02078                                 SKIP_CHARS (a_this, 4);
02079                                 status = cr_token_set_page_sym (token);
02080                                 CHECK_PARSING_STATUS (status, TRUE);
02081                                 cr_parsing_location_copy (&token->location, 
02082                                                           &location) ;
02083                                 goto done;
02084                         }
02085                         status = cr_tknzr_parse_atkeyword (a_this, &str);
02086                         if (status == CR_OK) {
02087                                 status = cr_token_set_atkeyword (token, str);
02088                                 CHECK_PARSING_STATUS (status, TRUE);
02089                                 if (str) {
02090                                         cr_parsing_location_copy (&token->location, 
02091                                                                   &str->location) ;
02092                                 }
02093                                 goto done;
02094                         }
02095                 }
02096                 break;
02097 
02098         case 'u':
02099 
02100                 if (BYTE (input, 2, NULL) == 'r'
02101                     && BYTE (input, 3, NULL) == 'l'
02102                     && BYTE (input, 4, NULL) == '(') {
02103                         CRString *str = NULL;
02104 
02105                         status = cr_tknzr_parse_uri (a_this, &str);
02106                         if (status == CR_OK) {
02107                                 status = cr_token_set_uri (token, str);
02108                                 CHECK_PARSING_STATUS (status, TRUE);
02109                                 if (str) {
02110                                         cr_parsing_location_copy (&token->location,
02111                                                                   &str->location) ;
02112                                 }
02113                                 goto done;
02114                         }
02115                 } 
02116                 goto fallback;
02117                 break;
02118 
02119         case 'r':
02120                 if (BYTE (input, 2, NULL) == 'g'
02121                     && BYTE (input, 3, NULL) == 'b'
02122                     && BYTE (input, 4, NULL) == '(') {
02123                         status = cr_tknzr_parse_rgb (a_this, &rgb);
02124                         if (status == CR_OK && rgb) {
02125                                 status = cr_token_set_rgb (token, rgb);
02126                                 CHECK_PARSING_STATUS (status, TRUE);
02127                                 if (rgb) {
02128                                         cr_parsing_location_copy (&token->location, 
02129                                                                   &rgb->location) ;
02130                                 }
02131                                 rgb = NULL;
02132                                 goto done;
02133                         }
02134 
02135                 }
02136                 goto fallback;
02137                 break;
02138 
02139         case '<':
02140                 if (BYTE (input, 2, NULL) == '!'
02141                     && BYTE (input, 3, NULL) == '-'
02142                     && BYTE (input, 4, NULL) == '-') {
02143                         SKIP_CHARS (a_this, 1);
02144                         cr_tknzr_get_parsing_location (a_this, 
02145                                                        &location) ;
02146                         SKIP_CHARS (a_this, 3);
02147                         status = cr_token_set_cdo (token);
02148                         CHECK_PARSING_STATUS (status, TRUE);
02149                         cr_parsing_location_copy (&token->location, 
02150                                                   &location) ;
02151                         goto done;
02152                 }
02153                 break;
02154 
02155         case '-':
02156                 if (BYTE (input, 2, NULL) == '-'
02157                     && BYTE (input, 3, NULL) == '>') {
02158                         SKIP_CHARS (a_this, 1);
02159                         cr_tknzr_get_parsing_location (a_this, 
02160                                                        &location) ;
02161                         SKIP_CHARS (a_this, 2);
02162                         status = cr_token_set_cdc (token);
02163                         CHECK_PARSING_STATUS (status, TRUE);
02164                         cr_parsing_location_copy (&token->location, 
02165                                                   &location) ;
02166                         goto done;
02167                 } else {
02168                         status = cr_tknzr_parse_ident
02169                                 (a_this, &str);
02170                         if (status == CR_OK) {
02171                                 cr_token_set_ident
02172                                         (token, str);
02173                                 if (str) {
02174                                         cr_parsing_location_copy (&token->location, 
02175                                                                   &str->location) ;
02176                                 }
02177                                 goto done;
02178                         } else {
02179                                 goto parse_number;
02180                         }
02181                 }
02182                 break;
02183 
02184         case '~':
02185                 if (BYTE (input, 2, NULL) == '=') {
02186                         SKIP_CHARS (a_this, 1);
02187                         cr_tknzr_get_parsing_location (a_this, 
02188                                                        &location) ;
02189                         SKIP_CHARS (a_this, 1);
02190                         status = cr_token_set_includes (token);
02191                         CHECK_PARSING_STATUS (status, TRUE);
02192                         cr_parsing_location_copy (&token->location, 
02193                                                   &location) ;
02194                         goto done;
02195                 }
02196                 break;
02197 
02198         case '|':
02199                 if (BYTE (input, 2, NULL) == '=') {
02200                         SKIP_CHARS (a_this, 1);
02201                         cr_tknzr_get_parsing_location (a_this, 
02202                                                        &location) ;
02203                         SKIP_CHARS (a_this, 1);
02204                         status = cr_token_set_dashmatch (token);
02205                         CHECK_PARSING_STATUS (status, TRUE);
02206                         cr_parsing_location_copy (&token->location,
02207                                                   &location) ;
02208                         goto done;
02209                 }
02210                 break;
02211 
02212         case '/':
02213                 if (BYTE (input, 2, NULL) == '*') {
02214                         status = cr_tknzr_parse_comment (a_this, &str);
02215 
02216                         if (status == CR_OK) {
02217                                 status = cr_token_set_comment (token, str);
02218                                 str = NULL;
02219                                 CHECK_PARSING_STATUS (status, TRUE);
02220                                 if (str) {
02221                                         cr_parsing_location_copy (&token->location, 
02222                                                                   &str->location) ;
02223                                 }
02224                                 goto done;
02225                         }
02226                 }
02227                 break ;
02228 
02229         case ';':
02230                 SKIP_CHARS (a_this, 1);
02231                 cr_tknzr_get_parsing_location (a_this, 
02232                                                &location) ;
02233                 status = cr_token_set_semicolon (token);
02234                 CHECK_PARSING_STATUS (status, TRUE);
02235                 cr_parsing_location_copy (&token->location, 
02236                                           &location) ;
02237                 goto done;
02238 
02239         case '{':
02240                 SKIP_CHARS (a_this, 1);
02241                 cr_tknzr_get_parsing_location (a_this, 
02242                                                &location) ;
02243                 status = cr_token_set_cbo (token);
02244                 CHECK_PARSING_STATUS (status, TRUE);
02245                 cr_tknzr_get_parsing_location (a_this, 
02246                                                &location) ;
02247                 goto done;
02248 
02249         case '}':
02250                 SKIP_CHARS (a_this, 1);
02251                 cr_tknzr_get_parsing_location (a_this, 
02252                                                &location) ;
02253                 status = cr_token_set_cbc (token);
02254                 CHECK_PARSING_STATUS (status, TRUE);
02255                 cr_parsing_location_copy (&token->location, 
02256                                           &location) ;
02257                 goto done;
02258 
02259         case '(':
02260                 SKIP_CHARS (a_this, 1);
02261                 cr_tknzr_get_parsing_location (a_this, 
02262                                                &location) ;
02263                 status = cr_token_set_po (token);
02264                 CHECK_PARSING_STATUS (status, TRUE);
02265                 cr_parsing_location_copy (&token->location, 
02266                                           &location) ;
02267                 goto done;
02268 
02269         case ')':
02270                 SKIP_CHARS (a_this, 1);
02271                 cr_tknzr_get_parsing_location (a_this, 
02272                                                &location) ;
02273                 status = cr_token_set_pc (token);
02274                 CHECK_PARSING_STATUS (status, TRUE);
02275                 cr_parsing_location_copy (&token->location, 
02276                                           &location) ;
02277                 goto done;
02278 
02279         case '[':
02280                 SKIP_CHARS (a_this, 1);
02281                 cr_tknzr_get_parsing_location (a_this, 
02282                                                &location) ;
02283                 status = cr_token_set_bo (token);
02284                 CHECK_PARSING_STATUS (status, TRUE);
02285                 cr_parsing_location_copy (&token->location, 
02286                                           &location) ;
02287                 goto done;
02288 
02289         case ']':
02290                 SKIP_CHARS (a_this, 1);
02291                 cr_tknzr_get_parsing_location (a_this, 
02292                                                &location) ;
02293                 status = cr_token_set_bc (token);
02294                 CHECK_PARSING_STATUS (status, TRUE);
02295                 cr_parsing_location_copy (&token->location, 
02296                                           &location) ;
02297                 goto done;
02298 
02299         case ' ':
02300         case '\t':
02301         case '\n':
02302         case '\f':
02303         case '\r':
02304                 {
02305                         guchar *start = NULL,
02306                                 *end = NULL;
02307 
02308                         status = cr_tknzr_parse_w (a_this, &start, 
02309                                                    &end, &location);
02310                         if (status == CR_OK) {
02311                                 status = cr_token_set_s (token);
02312                                 CHECK_PARSING_STATUS (status, TRUE);
02313                                 cr_tknzr_get_parsing_location (a_this, 
02314                                                                &location) ;
02315                                 goto done;
02316                         }
02317                 }
02318                 break;
02319 
02320         case '#':
02321                 {
02322                         status = cr_tknzr_parse_hash (a_this, &str);
02323                         if (status == CR_OK && str) {
02324                                 status = cr_token_set_hash (token, str);
02325                                 CHECK_PARSING_STATUS (status, TRUE);
02326                                 if (str) {
02327                                         cr_parsing_location_copy (&token->location,
02328                                                                   &str->location) ;
02329                                 }
02330                                 str = NULL;
02331                                 goto done;
02332                         }
02333                 }
02334                 break;
02335 
02336         case '\'':
02337         case '"':
02338                 status = cr_tknzr_parse_string (a_this, &str);
02339                 if (status == CR_OK && str) {
02340                         status = cr_token_set_string (token, str);
02341                         CHECK_PARSING_STATUS (status, TRUE);
02342                         if (str) {
02343                                 cr_parsing_location_copy (&token->location, 
02344                                                           &str->location) ;
02345                         }
02346                         str = NULL;
02347                         goto done;
02348                 }
02349                 break;
02350 
02351         case '!':
02352                 status = cr_tknzr_parse_important (a_this, &location);
02353                 if (status == CR_OK) {
02354                         status = cr_token_set_important_sym (token);
02355                         CHECK_PARSING_STATUS (status, TRUE);
02356                         cr_parsing_location_copy (&token->location, 
02357                                                   &location) ;
02358                         goto done;
02359                 }
02360                 break;
02361 
02362         case '0':
02363         case '1':
02364         case '2':
02365         case '3':
02366         case '4':
02367         case '5':
02368         case '6':
02369         case '7':
02370         case '8':
02371         case '9':
02372         case '.':
02373         case '+':
02374         /* '-' case is handled separately above for --> comments */
02375         parse_number:
02376                 {
02377                         CRNum *num = NULL;
02378 
02379                         status = cr_tknzr_parse_num (a_this, &num);
02380                         if (status == CR_OK && num) {
02381                                 next_bytes[0] = BYTE (input, 1, NULL);
02382                                 next_bytes[1] = BYTE (input, 2, NULL);
02383                                 next_bytes[2] = BYTE (input, 3, NULL);
02384                                 next_bytes[3] = BYTE (input, 3, NULL);
02385 
02386                                 if (next_bytes[0] == 'e'
02387                                     && next_bytes[1] == 'm') {
02388                                         num->type = NUM_LENGTH_EM;
02389                                         status = cr_token_set_ems (token,
02390                                                                    num);
02391                                         num = NULL;
02392                                         SKIP_CHARS (a_this, 2);
02393                                 } else if (next_bytes[0] == 'e'
02394                                            && next_bytes[1] == 'x') {
02395                                         num->type = NUM_LENGTH_EX;
02396                                         status = cr_token_set_exs (token,
02397                                                                    num);
02398                                         num = NULL;
02399                                         SKIP_CHARS (a_this, 2);
02400                                 } else if (next_bytes[0] == 'p'
02401                                            && next_bytes[1] == 'x') {
02402                                         num->type = NUM_LENGTH_PX;
02403                                         status = cr_token_set_length
02404                                                 (token, num, LENGTH_PX_ET);
02405                                         num = NULL;
02406                                         SKIP_CHARS (a_this, 2);
02407                                 } else if (next_bytes[0] == 'c'
02408                                            && next_bytes[1] == 'm') {
02409                                         num->type = NUM_LENGTH_CM;
02410                                         status = cr_token_set_length
02411                                                 (token, num, LENGTH_CM_ET);
02412                                         num = NULL;
02413                                         SKIP_CHARS (a_this, 2);
02414                                 } else if (next_bytes[0] == 'm'
02415                                            && next_bytes[1] == 'm') {
02416                                         num->type = NUM_LENGTH_MM;
02417                                         status = cr_token_set_length
02418                                                 (token, num, LENGTH_MM_ET);
02419                                         num = NULL;
02420                                         SKIP_CHARS (a_this, 2);
02421                                 } else if (next_bytes[0] == 'i'
02422                                            && next_bytes[1] == 'n') {
02423                                         num->type = NUM_LENGTH_IN;
02424                                         status = cr_token_set_length
02425                                                 (token, num, LENGTH_IN_ET);
02426                                         num = NULL;
02427                                         SKIP_CHARS (a_this, 2);
02428                                 } else if (next_bytes[0] == 'p'
02429                                            && next_bytes[1] == 't') {
02430                                         num->type = NUM_LENGTH_PT;
02431                                         status = cr_token_set_length
02432                                                 (token, num, LENGTH_PT_ET);
02433                                         num = NULL;
02434                                         SKIP_CHARS (a_this, 2);
02435                                 } else if (next_bytes[0] == 'p'
02436                                            && next_bytes[1] == 'c') {
02437                                         num->type = NUM_LENGTH_PC;
02438                                         status = cr_token_set_length
02439                                                 (token, num, LENGTH_PC_ET);
02440                                         num = NULL;
02441                                         SKIP_CHARS (a_this, 2);
02442                                 } else if (next_bytes[0] == 'd'
02443                                            && next_bytes[1] == 'e'
02444                                            && next_bytes[2] == 'g') {
02445                                         num->type = NUM_ANGLE_DEG;
02446                                         status = cr_token_set_angle
02447                                                 (token, num, ANGLE_DEG_ET);
02448                                         num = NULL;
02449                                         SKIP_CHARS (a_this, 3);
02450                                 } else if (next_bytes[0] == 'r'
02451                                            && next_bytes[1] == 'a'
02452                                            && next_bytes[2] == 'd') {
02453                                         num->type = NUM_ANGLE_RAD;
02454                                         status = cr_token_set_angle
02455                                                 (token, num, ANGLE_RAD_ET);
02456                                         num = NULL;
02457                                         SKIP_CHARS (a_this, 3);
02458                                 } else if (next_bytes[0] == 'g'
02459                                            && next_bytes[1] == 'r'
02460                                            && next_bytes[2] == 'a'
02461                                            && next_bytes[3] == 'd') {
02462                                         num->type = NUM_ANGLE_GRAD;
02463                                         status = cr_token_set_angle
02464                                                 (token, num, ANGLE_GRAD_ET);
02465                                         num = NULL;
02466                                         SKIP_CHARS (a_this, 4);
02467                                 } else if (next_bytes[0] == 'm'
02468                                            && next_bytes[1] == 's') {
02469                                         num->type = NUM_TIME_MS;
02470                                         status = cr_token_set_time
02471                                                 (token, num, TIME_MS_ET);
02472                                         num = NULL;
02473                                         SKIP_CHARS (a_this, 2);
02474                                 } else if (next_bytes[0] == 's') {
02475                                         num->type = NUM_TIME_S;
02476                                         status = cr_token_set_time
02477                                                 (token, num, TIME_S_ET);
02478                                         num = NULL;
02479                                         SKIP_CHARS (a_this, 1);
02480                                 } else if (next_bytes[0] == 'H'
02481                                            && next_bytes[1] == 'z') {
02482                                         num->type = NUM_FREQ_HZ;
02483                                         status = cr_token_set_freq
02484                                                 (token, num, FREQ_HZ_ET);
02485                                         num = NULL;
02486                                         SKIP_CHARS (a_this, 2);
02487                                 } else if (next_bytes[0] == 'k'
02488                                            && next_bytes[1] == 'H'
02489                                            && next_bytes[2] == 'z') {
02490                                         num->type = NUM_FREQ_KHZ;
02491                                         status = cr_token_set_freq
02492                                                 (token, num, FREQ_KHZ_ET);
02493                                         num = NULL;
02494                                         SKIP_CHARS (a_this, 3);
02495                                 } else if (next_bytes[0] == '%') {
02496                                         num->type = NUM_PERCENTAGE;
02497                                         status = cr_token_set_percentage
02498                                                 (token, num);
02499                                         num = NULL;
02500                                         SKIP_CHARS (a_this, 1);
02501                                 } else {
02502                                         status = cr_tknzr_parse_ident (a_this,
02503                                                                        &str);
02504                                         if (status == CR_OK && str) {
02505                                                 num->type = NUM_UNKNOWN_TYPE;
02506                                                 status = cr_token_set_dimen
02507                                                         (token, num, str);
02508                                                 num = NULL;
02509                                                 CHECK_PARSING_STATUS (status,
02510                                                                       TRUE);
02511                                                 str = NULL;
02512                                         } else {
02513                                                 status = cr_token_set_number
02514                                                         (token, num);
02515                                                 num = NULL;
02516                                                 CHECK_PARSING_STATUS (status, CR_OK);
02517                                                 str = NULL;
02518                                         }
02519                                 }
02520                                 if (token && token->u.num) {
02521                                         cr_parsing_location_copy (&token->location,
02522                                                                   &token->u.num->location) ;
02523                                 } else {
02524                                         status = CR_ERROR ;
02525                                 }
02526                                 goto done ;
02527                         }
02528                 }
02529                 break;
02530 
02531         default:
02532         fallback:
02533                 /*process the fallback cases here */
02534 
02535                 if (next_char == '\\'
02536                     || (cr_utils_is_nonascii (next_bytes[0]) == TRUE)
02537                     || ((next_char >= 'a') && (next_char <= 'z'))
02538                     || ((next_char >= 'A') && (next_char <= 'Z'))) {
02539                         status = cr_tknzr_parse_ident (a_this, &str);
02540                         if (status == CR_OK && str) {
02541                                 guint32 next_c = 0;
02542 
02543                                 status = cr_input_peek_char
02544                                         (PRIVATE (a_this)->input, &next_c);
02545 
02546                                 if (status == CR_OK && next_c == '(') {
02547 
02548                                         SKIP_CHARS (a_this, 1);
02549                                         status = cr_token_set_function
02550                                                 (token, str);
02551                                         CHECK_PARSING_STATUS (status, TRUE);
02552                                         /*ownership is transfered
02553                                          *to token by cr_token_set_function.
02554                                          */
02555                                         if (str) {
02556                                                 cr_parsing_location_copy (&token->location, 
02557                                                                           &str->location) ;
02558                                         }
02559                                         str = NULL;
02560                                 } else {
02561                                         status = cr_token_set_ident (token,
02562                                                                      str);
02563                                         CHECK_PARSING_STATUS (status, TRUE);
02564                                         if (str) {
02565                                                 cr_parsing_location_copy (&token->location, 
02566                                                                           &str->location) ;
02567                                         }
02568                                         str = NULL;
02569                                 }
02570                                 goto done;
02571                         } else {
02572                                 if (str) {
02573                                         cr_string_destroy (str);
02574                                         str = NULL;
02575                                 }
02576                         }
02577                 }
02578                 break;
02579         }
02580 
02581         READ_NEXT_CHAR (a_this, &next_char);
02582         cr_tknzr_get_parsing_location (a_this, 
02583                                        &location) ;
02584         status = cr_token_set_delim (token, next_char);
02585         CHECK_PARSING_STATUS (status, TRUE);
02586         cr_parsing_location_copy (&token->location, 
02587                                   &location) ;
02588  done:
02589 
02590         if (status == CR_OK && token) {
02591                 *a_tk = token;
02592                 /*
02593                  *store the previous position input stream pos.
02594                  */
02595                 memmove (&PRIVATE (a_this)->prev_pos,
02596                          &init_pos, sizeof (CRInputPos));
02597                 return CR_OK;
02598         }
02599 
02600  error:
02601         if (token) {
02602                 cr_token_destroy (token);
02603                 token = NULL;
02604         }
02605 
02606         if (str) {
02607                 cr_string_destroy (str);
02608                 str = NULL;
02609         }
02610         cr_tknzr_set_cur_pos (a_this, &init_pos);
02611         return status;
02612 
02613 }
02614 
02615 enum CRStatus
02616 cr_tknzr_parse_token (CRTknzr * a_this, enum CRTokenType a_type,
02617                       enum CRTokenExtraType a_et, gpointer a_res,
02618                       gpointer a_extra_res)
02619 {
02620         enum CRStatus status = CR_OK;
02621         CRToken *token = NULL;
02622 
02623         g_return_val_if_fail (a_this && PRIVATE (a_this)
02624                               && PRIVATE (a_this)->input
02625                               && a_res, CR_BAD_PARAM_ERROR);
02626 
02627         status = cr_tknzr_get_next_token (a_this, &token);
02628         if (status != CR_OK)
02629                 return status;
02630         if (token == NULL)
02631                 return CR_PARSING_ERROR;
02632 
02633         if (token->type == a_type) {
02634                 switch (a_type) {
02635                 case NO_TK:
02636                 case S_TK:
02637                 case CDO_TK:
02638                 case CDC_TK:
02639                 case INCLUDES_TK:
02640                 case DASHMATCH_TK:
02641                 case IMPORT_SYM_TK:
02642                 case PAGE_SYM_TK:
02643                 case MEDIA_SYM_TK:
02644                 case FONT_FACE_SYM_TK:
02645                 case CHARSET_SYM_TK:
02646                 case IMPORTANT_SYM_TK:
02647                         status = CR_OK;
02648                         break;
02649 
02650                 case STRING_TK:
02651                 case IDENT_TK:
02652                 case HASH_TK:
02653                 case ATKEYWORD_TK:
02654                 case FUNCTION_TK:
02655                 case COMMENT_TK:
02656                 case URI_TK:
02657                         *((CRString **) a_res) = token->u.str;
02658                         token->u.str = NULL;
02659                         status = CR_OK;
02660                         break;
02661 
02662                 case EMS_TK:
02663                 case EXS_TK:
02664                 case PERCENTAGE_TK:
02665                 case NUMBER_TK:
02666                         *((CRNum **) a_res) = token->u.num;
02667                         token->u.num = NULL;
02668                         status = CR_OK;
02669                         break;
02670 
02671                 case LENGTH_TK:
02672                 case ANGLE_TK:
02673                 case TIME_TK:
02674                 case FREQ_TK:
02675                         if (token->extra_type == a_et) {
02676                                 *((CRNum **) a_res) = token->u.num;
02677                                 token->u.num = NULL;
02678                                 status = CR_OK;
02679                         }
02680                         break;
02681 
02682                 case DIMEN_TK:
02683                         *((CRNum **) a_res) = token->u.num;
02684                         if (a_extra_res == NULL) {
02685                                 status = CR_BAD_PARAM_ERROR;
02686                                 goto error;
02687                         }
02688 
02689                         *((CRString **) a_extra_res) = token->dimen;
02690                         token->u.num = NULL;
02691                         token->dimen = NULL;
02692                         status = CR_OK;
02693                         break;
02694 
02695                 case DELIM_TK:
02696                         *((guint32 *) a_res) = token->u.unichar;
02697                         status = CR_OK;
02698                         break;
02699 
02700                 case UNICODERANGE_TK:
02701                 default:
02702                         status = CR_PARSING_ERROR;
02703                         break;
02704                 }
02705 
02706                 cr_token_destroy (token);
02707                 token = NULL;
02708         } else {
02709                 cr_tknzr_unget_token (a_this, token);
02710                 token = NULL;
02711                 status = CR_PARSING_ERROR;
02712         }
02713 
02714         return status;
02715 
02716       error:
02717 
02718         if (token) {
02719                 cr_tknzr_unget_token (a_this, token);
02720                 token = NULL;
02721         }
02722 
02723         return status;
02724 }
02725 
02726 void
02727 cr_tknzr_destroy (CRTknzr * a_this)
02728 {
02729         g_return_if_fail (a_this);
02730 
02731         if (PRIVATE (a_this) && PRIVATE (a_this)->input) {
02732                 if (cr_input_unref (PRIVATE (a_this)->input)
02733                     == TRUE) {
02734                         PRIVATE (a_this)->input = NULL;
02735                 }
02736         }
02737 
02738         if (PRIVATE (a_this)->token_cache) {
02739                 cr_token_destroy (PRIVATE (a_this)->token_cache);
02740                 PRIVATE (a_this)->token_cache = NULL;
02741         }
02742 
02743         if (PRIVATE (a_this)) {
02744                 g_free (PRIVATE (a_this));
02745                 PRIVATE (a_this) = NULL;
02746         }
02747 
02748         g_free (a_this);
02749 }