Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

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

Generated on Tue Apr 27 13:46:36 2004 for Libcroco by doxygen 1.3.6