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

cr-parser.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  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of version 2.1 of the 
00009  * GNU Lesser General Public
00010  * License as published by the Free Software Foundation.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00020  * USA
00021  *
00022  * See COPYRIGHTS file for copyrights information.
00023  */
00024 
00025 /*
00026  *$Id: cr-parser.c,v 1.24.2.1 2004/04/18 13:07:22 dodji Exp $
00027  */
00028 
00029 /**
00030  *@file
00031  *The definition of the #CRParser class.
00032  */
00033 
00034 #include "string.h"
00035 #include "cr-parser.h"
00036 #include "cr-num.h"
00037 #include "cr-term.h"
00038 #include "cr-simple-sel.h"
00039 #include "cr-attr-sel.h"
00040 
00041 /*
00042  *Random notes: 
00043  *CSS core syntax vs CSS level 2 syntax
00044  *=====================================
00045  *
00046  *One must keep in mind
00047  *that css UA must comply with two syntax.
00048  *
00049  *1/the specific syntax that defines the css language
00050  *for a given level of specificatin (e.g css2 syntax
00051  *defined in appendix D.1 of the css2 spec)
00052  *
00053  *2/the core (general) syntax that is there to allow
00054  *UAs to parse style sheets written in levels of CSS that
00055  *didn't exist at the time the UAs were created.
00056  *
00057  *the name  of parsing functions (or methods) contained in this  file
00058  *follows the following scheme: cr_parser_parse_<production_name> (...) ;
00059  *where <production_name> is the name 
00060  *of a production of the css2 language.
00061  *When a given production is 
00062  *defined by the css2 level grammar *and* by the
00063  *css core syntax, there will be two functions to parse that production:
00064  *one will parse the production defined by the css2 level grammar and the
00065  *other will parse the production defined by the css core grammar.
00066  *The css2 level grammar related parsing function will be called:
00067  *cr_parser_parse_<production_name> (...) ;
00068  *Then css core grammar related parsing function will be called:
00069  *cr_parser_parse_<production_name>_core (...) ;
00070  *
00071  *If a production is defined only by the css core grammar, then
00072  *it will be named:
00073  *cr_parser_parse_<production_name>_core (...) ;
00074  */
00075 
00076 typedef struct _CRParserError CRParserError;
00077 
00078 /**
00079  *An abstraction of an error reported by by the
00080  *parsing routines.
00081  */
00082 struct _CRParserError {
00083         guchar *msg;
00084         enum CRStatus status;
00085         glong line;
00086         glong column;
00087         glong byte_num;
00088 };
00089 
00090 enum CRParserState {
00091         READY_STATE = 0,
00092         TRY_PARSE_CHARSET_STATE,
00093         CHARSET_PARSED_STATE,
00094         TRY_PARSE_IMPORT_STATE,
00095         IMPORT_PARSED_STATE,
00096         TRY_PARSE_RULESET_STATE,
00097         RULESET_PARSED_STATE,
00098         TRY_PARSE_MEDIA_STATE,
00099         MEDIA_PARSED_STATE,
00100         TRY_PARSE_PAGE_STATE,
00101         PAGE_PARSED_STATE,
00102         TRY_PARSE_FONT_FACE_STATE,
00103         FONT_FACE_PARSED_STATE
00104 };
00105 
00106 /**
00107  *The private attributes of
00108  *#CRParser.
00109  */
00110 struct _CRParserPriv {
00111         /**
00112          *The tokenizer
00113          */
00114         CRTknzr *tknzr;
00115 
00116         /**
00117          *The sac handlers to call
00118          *to notify the parsing of
00119          *the css2 constructions.
00120          */
00121         CRDocHandler *sac_handler;
00122 
00123         /**
00124          *A stack of errors reported
00125          *by the parsing routines.
00126          *Contains instance of #CRParserError.
00127          *This pointer is the top of the stack.
00128          */
00129         GList *err_stack;
00130 
00131         enum CRParserState state;
00132         gboolean resolve_import;
00133         gboolean is_case_sensitive;
00134         gboolean use_core_grammar;
00135 };
00136 
00137 #define PRIVATE(obj) ((obj)->priv)
00138 
00139 #define CHARS_TAB_SIZE 12
00140 
00141 /**
00142  *return TRUE if the character is a number ([0-9]), FALSE otherwise
00143  *@param a_char the char to test.
00144  */
00145 #define IS_NUM(a_char) (((a_char) >= '0' && (a_char) <= '9')?TRUE:FALSE)
00146 
00147 /**
00148  *Checks if 'status' equals CR_OK. If not, goto the 'error' label.
00149  *
00150  *@param status the status (of type enum CRStatus) to test.
00151  *@param is_exception if set to FALSE, the final status returned the
00152  *current function will be CR_PARSING_ERROR. If set to TRUE, the
00153  *current status will be the current value of the 'status' variable.
00154  *
00155  */
00156 #define CHECK_PARSING_STATUS(status, is_exception) \
00157 if ((status) != CR_OK) \
00158 { \
00159         if (is_exception == FALSE) \
00160         { \
00161                 status = CR_PARSING_ERROR ; \
00162         } \
00163         goto error ; \
00164 }
00165 
00166 /**
00167  *same as CHECK_PARSING_STATUS() but this one pushes an error
00168  *on the parser error stack when an error arises.
00169  *@param a_this the current instance of #CRParser .
00170  *@param a_status the status to check. Is of type enum #CRStatus.
00171  *@param a_is_exception in case of error, if is TRUE, the status
00172  *is set to CR_PARSING_ERROR before goto error. If is false, the
00173  *real low level status is kept and will be returned by the
00174  *upper level function that called this macro. Usally,this must
00175  *be set to FALSE.
00176  *
00177  */
00178 #define CHECK_PARSING_STATUS_ERR(a_this, a_status, a_is_exception,\
00179                                  a_err_msg, a_err_status) \
00180 if ((a_status) != CR_OK) \
00181 { \
00182         if (a_is_exception == FALSE) a_status = CR_PARSING_ERROR ; \
00183         cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \
00184         goto error ; \
00185 }
00186 
00187 /**
00188  *Peeks the next char from the input stream of the current parser
00189  *by invoking cr_tknzr_input_peek_char().
00190  *invokes CHECK_PARSING_STATUS on the status returned by
00191  *cr_tknzr_peek_char().
00192  *
00193  *@param a_this the current instance of #CRParser.
00194  *@param a_to_char a pointer to the char where to store the
00195  *char peeked.
00196  */
00197 #define PEEK_NEXT_CHAR(a_this, a_to_char) \
00198 {\
00199 enum CRStatus status ; \
00200 status = cr_tknzr_peek_char  (PRIVATE (a_this)->tknzr, a_to_char) ; \
00201 CHECK_PARSING_STATUS (status, TRUE) \
00202 }
00203 
00204 /**
00205  *Reads the next char from the input stream of the current parser.
00206  *In case of error, jumps to the "error:" label located in the
00207  *function where this macro is called.
00208  *@param a_this the curent instance of #CRParser
00209  *@param to_char a pointer to the guint32 char where to store
00210  *the character read.
00211  */
00212 #define READ_NEXT_CHAR(a_this, a_to_char) \
00213 status = cr_tknzr_read_char (PRIVATE (a_this)->tknzr, a_to_char) ; \
00214 CHECK_PARSING_STATUS (status, TRUE)
00215 
00216 /**
00217  *Gets information about the current position in
00218  *the input of the parser.
00219  *In case of failure, this macro returns from the 
00220  *calling function and
00221  *returns a status code of type enum #CRStatus.
00222  *@param a_this the current instance of #CRParser.
00223  *@param a_pos out parameter. A pointer to the position 
00224  *inside the current parser input. Must
00225  */
00226 #define RECORD_INITIAL_POS(a_this, a_pos) \
00227 status = cr_tknzr_get_cur_pos (PRIVATE \
00228 (a_this)->tknzr, a_pos) ; \
00229 g_return_val_if_fail (status == CR_OK, status)
00230 
00231 /**
00232  *Gets the address of the current byte inside the
00233  *parser input.
00234  *@param parser the current instance of #CRParser.
00235  *@param addr out parameter a pointer (guchar*)
00236  *to where the address  must be put.
00237  */
00238 #define RECORD_CUR_BYTE_ADDR(a_this, a_addr) \
00239 status = cr_tknzr_get_cur_byte_addr \
00240             (PRIVATE (a_this)->tknzr, a_addr) ; \
00241 CHECK_PARSING_STATUS (status, TRUE)
00242 
00243 /**
00244  *Peeks a byte from the topmost parser input at
00245  *a given offset from the current position.
00246  *If it fails, goto the "error:" label.
00247  *
00248  *@param a_parser the current instance of #CRParser.
00249  *@param a_offset the offset of the byte to peek, the
00250  *current byte having the offset '0'.
00251  *@param a_byte_ptr out parameter a pointer (guchar*) to
00252  *where the peeked char is to be stored.
00253  */
00254 #define PEEK_BYTE(a_parser, a_offset, a_byte_ptr) \
00255 status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr, \
00256                               a_offset, \
00257                               a_byte_ptr) ; \
00258 CHECK_PARSING_STATUS (status, TRUE) ;
00259 
00260 #define BYTE(a_parser, a_offset, a_eof) \
00261 cr_tknzr_peek_byte2 (PRIVATE (a_this)->tknzr, a_offset, a_eof)
00262 
00263 /**
00264  *Reads a byte from the topmost parser input
00265  *steam.
00266  *If it fails, goto the "error" label.
00267  *@param a_this the current instance of #CRParser.
00268  *@param a_byte_ptr the guchar * where to put the read char.
00269  */
00270 #define READ_NEXT_BYTE(a_this, a_byte_ptr) \
00271 status = cr_tknzr_read_byte (PRIVATE (a_this)->tknzr, a_byte_ptr) ; \
00272 CHECK_PARSING_STATUS (status, TRUE) ;
00273 
00274 /**
00275  *Skips a given number of byte in the topmost
00276  *parser input. Don't update line and column number.
00277  *In case of error, jumps to the "error:" label
00278  *of the surrounding function.
00279  *@param a_parser the current instance of #CRParser.
00280  *@param a_nb_bytes the number of bytes to skip.
00281  */
00282 #define SKIP_BYTES(a_this, a_nb_bytes) \
00283 status = cr_tknzr_seek_index (PRIVATE (a_this)->tknzr, \
00284                                         CR_SEEK_CUR, a_nb_bytes) ; \
00285 CHECK_PARSING_STATUS (status, TRUE) ;
00286 
00287 /**
00288  *Skip utf8 encoded characters.
00289  *Updates line and column numbers.
00290  *@param a_parser the current instance of #CRParser.
00291  *@param a_nb_chars the number of chars to skip. Must be of
00292  *type glong.
00293  */
00294 #define SKIP_CHARS(a_parser, a_nb_chars) \
00295 { \
00296 glong nb_chars = a_nb_chars ; \
00297 status = cr_tknzr_consume_chars \
00298      (PRIVATE (a_parser)->tknzr,0, &nb_chars) ; \
00299 CHECK_PARSING_STATUS (status, TRUE) ; \
00300 }
00301 
00302 /**
00303  *Tests the condition and if it is false, sets
00304  *status to "CR_PARSING_ERROR" and goto the 'error'
00305  *label.
00306  *@param condition the condition to test.
00307  */
00308 #define ENSURE_PARSING_COND(condition) \
00309 if (! (condition)) {status = CR_PARSING_ERROR; goto error ;}
00310 
00311 #define ENSURE_PARSING_COND_ERR(a_this, a_condition, \
00312                                 a_err_msg, a_err_status) \
00313 if (! (a_condition)) \
00314 { \
00315         status = CR_PARSING_ERROR; \
00316         cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \
00317         goto error ; \
00318 }
00319 
00320 #define GET_NEXT_TOKEN(a_this, a_token_ptr) \
00321 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, \
00322                                   a_token_ptr) ; \
00323 ENSURE_PARSING_COND (status == CR_OK) ;
00324 
00325 #ifdef WITH_UNICODE_ESCAPE_AND_RANGE
00326 static enum CRStatus cr_parser_parse_unicode_escape (CRParser * a_this,
00327                                                      guint32 * a_unicode);
00328 static enum CRStatus cr_parser_parse_escape (CRParser * a_this,
00329                                              guint32 * a_esc_code);
00330 
00331 static enum CRStatus cr_parser_parse_unicode_range (CRParser * a_this,
00332                                                     GString ** a_inf,
00333                                                     GString ** a_sup);
00334 #endif
00335 
00336 static enum CRStatus cr_parser_parse_stylesheet_core (CRParser * a_this);
00337 
00338 static enum CRStatus cr_parser_parse_atrule_core (CRParser * a_this);
00339 
00340 static enum CRStatus cr_parser_parse_ruleset_core (CRParser * a_this);
00341 
00342 static enum CRStatus cr_parser_parse_selector_core (CRParser * a_this);
00343 
00344 static enum CRStatus cr_parser_parse_declaration_core (CRParser * a_this);
00345 
00346 static enum CRStatus cr_parser_parse_any_core (CRParser * a_this);
00347 
00348 static enum CRStatus cr_parser_parse_block_core (CRParser * a_this);
00349 
00350 static enum CRStatus cr_parser_parse_value_core (CRParser * a_this);
00351 
00352 static enum CRStatus cr_parser_parse_string (CRParser * a_this,
00353                                              GString ** a_str);
00354 
00355 static enum CRStatus cr_parser_parse_ident (CRParser * a_this,
00356                                             GString ** a_str);
00357 
00358 static enum CRStatus cr_parser_parse_uri (CRParser * a_this,
00359                                           GString ** a_str);
00360 
00361 static enum CRStatus cr_parser_parse_function (CRParser * a_this,
00362                                                GString ** a_func_name,
00363                                                CRTerm ** a_expr);
00364 static enum CRStatus cr_parser_parse_property (CRParser * a_this,
00365                                                GString ** a_property);
00366 
00367 static enum CRStatus cr_parser_parse_attribute_selector (CRParser * a_this,
00368                                                          CRAttrSel ** a_sel);
00369 
00370 static enum CRStatus cr_parser_parse_simple_selector (CRParser * a_this,
00371                                                       CRSimpleSel ** a_sel);
00372 
00373 static enum CRStatus cr_parser_parse_simple_sels (CRParser * a_this,
00374                                                   CRSimpleSel ** a_sel);
00375 
00376 static CRParserError *cr_parser_error_new (const guchar * a_msg,
00377                                            enum CRStatus);
00378 
00379 static void cr_parser_error_set_msg (CRParserError * a_this,
00380                                      const guchar * a_msg);
00381 
00382 static void cr_parser_error_dump (CRParserError * a_this);
00383 
00384 static void cr_parser_error_set_status (CRParserError * a_this,
00385                                         enum CRStatus a_status);
00386 
00387 static void cr_parser_error_set_pos (CRParserError * a_this,
00388                                      glong a_line,
00389                                      glong a_column, glong a_byte_num);
00390 static void
00391   cr_parser_error_destroy (CRParserError * a_this);
00392 
00393 static enum CRStatus cr_parser_push_error (CRParser * a_this,
00394                                            const guchar * a_msg,
00395                                            enum CRStatus a_status);
00396 
00397 static enum CRStatus cr_parser_dump_err_stack (CRParser * a_this,
00398                                                gboolean a_clear_errs);
00399 static enum CRStatus
00400   cr_parser_clear_errors (CRParser * a_this);
00401 
00402 /*****************************
00403  *error managemet methods
00404  *****************************/
00405 
00406 /**
00407  *Constructor of #CRParserError class.
00408  *@param a_msg the brute error message.
00409  *@param a_status the error status.
00410  *@return the newly built instance of #CRParserError.
00411  */
00412 static CRParserError *
00413 cr_parser_error_new (const guchar * a_msg, enum CRStatus a_status)
00414 {
00415         CRParserError *result = NULL;
00416 
00417         result = g_try_malloc (sizeof (CRParserError));
00418 
00419         if (result == NULL) {
00420                 cr_utils_trace_info ("Out of memory");
00421                 return NULL;
00422         }
00423 
00424         memset (result, 0, sizeof (CRParserError));
00425 
00426         cr_parser_error_set_msg (result, a_msg);
00427         cr_parser_error_set_status (result, a_status);
00428 
00429         return result;
00430 }
00431 
00432 /**
00433  *Sets the message associated to this instance of #CRError.
00434  *@param a_this the current instance of #CRParserError.
00435  *@param a_msg the new message.
00436  */
00437 static void
00438 cr_parser_error_set_msg (CRParserError * a_this, const guchar * a_msg)
00439 {
00440         g_return_if_fail (a_this);
00441 
00442         if (a_this->msg) {
00443                 g_free (a_this->msg);
00444         }
00445 
00446         a_this->msg = g_strdup (a_msg);
00447 }
00448 
00449 /**
00450  *Sets the error status.
00451  *@param a_this the current instance of #CRParserError.
00452  *@param a_status the new error status.
00453  *
00454  */
00455 static void
00456 cr_parser_error_set_status (CRParserError * a_this, enum CRStatus a_status)
00457 {
00458         g_return_if_fail (a_this);
00459 
00460         a_this->status = a_status;
00461 }
00462 
00463 /**
00464  *Sets the position of the parser error.
00465  *@param a_this the current instance of #CRParserError.
00466  *@param a_line the line number.
00467  *@param a_column the column number.
00468  *@param a_byte_num the byte number.
00469  */
00470 static void
00471 cr_parser_error_set_pos (CRParserError * a_this,
00472                          glong a_line, glong a_column, glong a_byte_num)
00473 {
00474         g_return_if_fail (a_this);
00475 
00476         a_this->line = a_line;
00477         a_this->column = a_column;
00478         a_this->byte_num = a_byte_num;
00479 }
00480 
00481 static void
00482 cr_parser_error_dump (CRParserError * a_this)
00483 {
00484         g_return_if_fail (a_this);
00485 
00486         g_printerr ("parsing error: %ld:%ld:", a_this->line, a_this->column);
00487 
00488         g_printerr ("%s\n", a_this->msg);
00489 }
00490 
00491 /**
00492  *The destructor of #CRParserError.
00493  *@param a_this the current instance of #CRParserError.
00494  */
00495 static void
00496 cr_parser_error_destroy (CRParserError * a_this)
00497 {
00498         g_return_if_fail (a_this);
00499 
00500         if (a_this->msg) {
00501                 g_free (a_this->msg);
00502                 a_this->msg = NULL;
00503         }
00504 
00505         g_free (a_this);
00506 }
00507 
00508 /**
00509  *Pushes an error on the parser error stack.
00510  *@param a_this the current instance of #CRParser.
00511  *@param a_msg the error message.
00512  *@param a_status the error status.
00513  *@return CR_OK upon successfull completion, an error code otherwise.
00514  */
00515 static enum CRStatus
00516 cr_parser_push_error (CRParser * a_this,
00517                       const guchar * a_msg, enum CRStatus a_status)
00518 {
00519         enum CRStatus status = CR_OK;
00520 
00521         CRParserError *error = NULL;
00522         CRInputPos pos;
00523 
00524         g_return_val_if_fail (a_this && PRIVATE (a_this)
00525                               && a_msg, CR_BAD_PARAM_ERROR);
00526 
00527         error = cr_parser_error_new (a_msg, a_status);
00528 
00529         g_return_val_if_fail (error, CR_ERROR);
00530 
00531         RECORD_INITIAL_POS (a_this, &pos);
00532 
00533         cr_parser_error_set_pos
00534                 (error, pos.line, pos.col, pos.next_byte_index - 1);
00535 
00536         PRIVATE (a_this)->err_stack =
00537                 g_list_prepend (PRIVATE (a_this)->err_stack, error);
00538 
00539         if (PRIVATE (a_this)->err_stack == NULL)
00540                 goto error;
00541 
00542         return CR_OK;
00543 
00544       error:
00545 
00546         if (error) {
00547                 cr_parser_error_destroy (error);
00548                 error = NULL;
00549         }
00550 
00551         return status;
00552 }
00553 
00554 /**
00555  *Dumps the error stack on stdout.
00556  *@param a_this the current instance of #CRParser.
00557  *@param a_clear_errs whether to clear the error stack
00558  *after the dump or not.
00559  *@return CR_OK upon successfull completion, an error code
00560  *otherwise.
00561  */
00562 static enum CRStatus
00563 cr_parser_dump_err_stack (CRParser * a_this, gboolean a_clear_errs)
00564 {
00565         GList *cur = NULL;
00566 
00567         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
00568 
00569         if (PRIVATE (a_this)->err_stack == NULL)
00570                 return CR_OK;
00571 
00572         for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
00573                 cr_parser_error_dump ((CRParserError *) cur->data);
00574         }
00575 
00576         if (a_clear_errs == TRUE) {
00577                 cr_parser_clear_errors (a_this);
00578         }
00579 
00580         return CR_OK;
00581 }
00582 
00583 /**
00584  *Clears all the errors contained in the parser error stack.
00585  *Frees all the errors, and the stack that contains'em.
00586  *@param a_this the current instance of #CRParser.
00587  */
00588 static enum CRStatus
00589 cr_parser_clear_errors (CRParser * a_this)
00590 {
00591         GList *cur = NULL;
00592 
00593         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
00594 
00595         for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
00596                 if (cur->data) {
00597                         cr_parser_error_destroy ((CRParserError *)
00598                                                  cur->data);
00599                 }
00600         }
00601 
00602         if (PRIVATE (a_this)->err_stack) {
00603                 g_list_free (PRIVATE (a_this)->err_stack);
00604                 PRIVATE (a_this)->err_stack = NULL;
00605         }
00606 
00607         return CR_OK;
00608 }
00609 
00610 /**
00611  *Same as cr_parser_try_to_skip_spaces() but this one skips
00612  *spaces and comments.
00613  *
00614  *@param a_this the current instance of #CRParser.
00615  *@return CR_OK upon successfull completion, an error code otherwise.
00616  */
00617 enum CRStatus
00618 cr_parser_try_to_skip_spaces_and_comments (CRParser * a_this)
00619 {
00620         enum CRStatus status = CR_ERROR;
00621         CRToken *token = NULL;
00622 
00623         g_return_val_if_fail (a_this && PRIVATE (a_this)
00624                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
00625         do {
00626                 if (token) {
00627                         cr_token_destroy (token);
00628                         token = NULL;
00629                 }
00630 
00631                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
00632                                                   &token);
00633                 if (status != CR_OK)
00634                         goto error;
00635         }
00636         while ((token != NULL)
00637                && (token->type == COMMENT_TK || token->type == S_TK));
00638 
00639         cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
00640 
00641         return status;
00642 
00643       error:
00644 
00645         if (token) {
00646                 cr_token_destroy (token);
00647                 token = NULL;
00648         }
00649 
00650         return status;
00651 }
00652 
00653 /***************************************
00654  *End of Parser input handling routines
00655  ***************************************/
00656 
00657 #ifdef WITH_UNICODE_ESCAPE_AND_RANGE
00658 
00659 /**
00660  *Parses an "unicode-range" as defined in the css spec at [4.1.1]:
00661  * UNICODE-RANGE ::= U\+[0-9A-F?]{1,6}(-[0-9A-F]{1,6})?
00662  *@param a_this the current instance of #CRParser.
00663  *@param a_inf out parameter. The inferior barrier of the range.
00664  *@param a_sup out parameter. The superior barrier of the range.
00665  *@return CR_OK upon successfull completion, an error code otherwise.
00666  */
00667 static enum CRStatus
00668 cr_parser_parse_unicode_range (CRParser * a_this,
00669                                GString ** a_inf, GString ** a_sup)
00670 {
00671         enum CRStatus status = CR_OK;
00672         CRInputPos init_pos;
00673         glong i = 0;
00674         gboolean min_str_needs_free = FALSE,
00675                 sup_str_needs_free = FALSE;
00676         guint32 cur_char = 0,
00677                 next_char = 0;
00678 
00679         g_return_val_if_fail (a_this && PRIVATE (a_this)
00680                               && PRIVATE (a_this)->tknzr
00681                               && a_inf && a_sup, CR_BAD_PARAM_ERROR);
00682 
00683         RECORD_INITIAL_POS (a_this, &init_pos);
00684 
00685         READ_NEXT_CHAR (a_this, &cur_char);
00686 
00687         ENSURE_PARSING_COND_ERR
00688                 (a_this, cur_char == 'U',
00689                  "while parsing an unicode range: unicode range must start with an U",
00690                  CR_SYNTAX_ERROR);
00691 
00692         READ_NEXT_CHAR (a_this, &cur_char);
00693 
00694         ENSURE_PARSING_COND_ERR
00695                 (a_this, cur_char == '+',
00696                  "while parsing an unicode range: there must be a + after the U",
00697                  CR_SYNTAX_ERROR);
00698 
00699         if (*a_inf == NULL) {
00700                 *a_inf = g_string_new (NULL);
00701                 min_str_needs_free = TRUE;
00702         }
00703 
00704         for (i = 0; i < 6; i++) {
00705                 PEEK_NEXT_CHAR (a_this, &next_char);
00706 
00707                 if (cr_utils_is_hexa_char (next_char) == TRUE
00708                     || next_char == '?') {
00709                         READ_NEXT_CHAR (a_this, &cur_char);
00710                         g_string_append_unichar (*a_inf, cur_char);
00711                 } else {
00712                         break;
00713                 }
00714         }
00715 
00716         if (i < 1) {
00717                 status = CR_PARSING_ERROR;
00718                 cr_parser_push_error (a_this,
00719                                       "No unicode range expressed",
00720                                       CR_SYNTAX_ERROR);
00721                 goto error;
00722         }
00723 
00724         if (next_char != '-') {
00725                 return CR_OK;
00726         }
00727 
00728         READ_NEXT_CHAR (a_this, &cur_char);
00729         /*we are sure that cur_char == '-' */
00730 
00731         if (*a_sup == NULL) {
00732                 *a_sup = g_string_new (NULL);
00733                 sup_str_needs_free = TRUE;
00734         }
00735 
00736         for (i = 0; i < 6; i++) {
00737                 PEEK_NEXT_CHAR (a_this, &next_char);
00738 
00739                 if (cr_utils_is_hexa_char (next_char) == TRUE) {
00740                         READ_NEXT_CHAR (a_this, &cur_char);
00741                         if (*a_sup == NULL) {
00742                                 *a_sup = g_string_new (NULL);
00743                                 sup_str_needs_free = TRUE;
00744                         }
00745                         g_string_append_unichar (*a_sup, cur_char);
00746                 } else {
00747                         break;
00748                 }
00749         }
00750 
00751         cr_parser_clear_errors (a_this);
00752         return CR_OK;
00753 
00754       error:
00755 
00756         if (min_str_needs_free == TRUE && *a_inf) {
00757                 g_free (*a_inf);
00758                 *a_inf = NULL;
00759         }
00760 
00761         if (sup_str_needs_free == TRUE && *a_sup) {
00762                 g_free (*a_sup);
00763                 *a_sup = NULL;
00764         }
00765 
00766         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
00767 
00768         return status;
00769 }
00770 
00771 /**
00772  *Parses an 'unicode' escape sequence defined
00773  *in css spec at chap 4.1.1:
00774  *unicode ::= \\[0-9a-f]{1,6}[ \n\r\t\f]?
00775  *@param a_this the current instance of #CRParser.
00776  *@param a_start out parameter. A pointer to the start
00777  *of the unicode escape sequence. Must *NOT* be deleted by
00778  *the caller.
00779  *@param a_end out parameter. A pointer to the last character
00780  *of the unicode escape sequence. Must *NOT* be deleted by the caller.
00781  *@return CR_OK if parsing succeded, an error code otherwise.
00782  *Error code can be either CR_PARSING_ERROR if the string 
00783  *parsed just doesn't
00784  *respect the production or another error if a 
00785  *lower level error occured.
00786  */
00787 static enum CRStatus
00788 cr_parser_parse_unicode_escape (CRParser * a_this, guint32 * a_unicode)
00789 {
00790         guint32 cur_char;
00791         CRInputPos init_pos;
00792         glong occur = 0;
00793         guint32 unicode = 0;
00794         guchar *tmp_char_ptr1 = NULL,
00795                 *tmp_char_ptr2 = NULL;
00796         enum CRStatus status = CR_OK;
00797 
00798         g_return_val_if_fail (a_this && PRIVATE (a_this)
00799                               && a_unicode, CR_BAD_PARAM_ERROR);
00800 
00801         /*first, let's backup the current position pointer */
00802         RECORD_INITIAL_POS (a_this, &init_pos);
00803 
00804         READ_NEXT_CHAR (a_this, &cur_char);
00805 
00806         if (cur_char != '\\') {
00807                 status = CR_PARSING_ERROR;
00808                 goto error;
00809         }
00810 
00811         READ_NEXT_CHAR (a_this, &cur_char);
00812 
00813         for (occur = 0, unicode = 0; ((cur_char >= '0' && cur_char <= '9')
00814                                       || (cur_char >= 'a' && cur_char <= 'f')
00815                                       || (cur_char >= 'A' && cur_char <= 'F'))
00816              && occur < 6; occur++) {
00817                 gint cur_char_val = 0;
00818 
00819                 if ((cur_char >= '0' && cur_char <= '9')) {
00820                         cur_char_val = (cur_char - '0');
00821                 } else if ((cur_char >= 'a' && cur_char <= 'f')) {
00822                         cur_char_val = 10 + (cur_char - 'a');
00823                 } else if ((cur_char >= 'A' && cur_char <= 'F')) {
00824                         cur_char_val = 10 + (cur_char - 'A');
00825                 }
00826 
00827                 unicode = unicode * 10 + cur_char_val;
00828 
00829                 READ_NEXT_CHAR (a_this, &cur_char);
00830         }
00831 
00832         if (occur == 5) {
00833                 /*
00834                  *the unicode escape is 6 digit length
00835                  */
00836 
00837                 /*
00838                  *parse one space that may 
00839                  *appear just after the unicode
00840                  *escape.
00841                  */
00842                 cr_parser_parse_w (a_this, &tmp_char_ptr1, &tmp_char_ptr2);
00843                 status = CR_OK;
00844         } else {
00845                 /*
00846                  *The unicode escape is less than
00847                  *6 digit length. The character
00848                  *that comes right after the escape
00849                  *must be a white space.
00850                  */
00851                 status = cr_parser_parse_w (a_this, &tmp_char_ptr1,
00852                                             &tmp_char_ptr2);
00853 
00854                 CHECK_PARSING_STATUS_ERR
00855                         (a_this, status, FALSE,
00856                          "next char expected to be a space", CR_SYNTAX_ERROR);
00857         }
00858 
00859         if (status == CR_OK) {
00860                 *a_unicode = unicode;
00861                 cr_parser_clear_errors (a_this);
00862                 return CR_OK;
00863         }
00864 
00865       error:
00866         /*
00867          *restore the initial position pointer backuped at
00868          *the beginning of this function.
00869          */
00870         cr_tknzr_set_cur_pos (PRIVATE (a_this) tknzr, &init_pos);
00871 
00872         return status;
00873 }
00874 
00875 /**
00876  *parses an escape sequence as defined by the css spec:
00877  *escape ::= {unicode}|\\[ -~\200-\4177777]
00878  *@param a_this the current instance of #CRParser .
00879  */
00880 static enum CRStatus
00881 cr_parser_parse_escape (CRParser * a_this, guint32 * a_esc_code)
00882 {
00883         enum CRStatus status = CR_OK;
00884         guint32 cur_char = 0;
00885         CRInputPos init_pos;
00886         guchar next_chars[2];
00887 
00888         g_return_val_if_fail (a_this && PRIVATE (a_this)
00889                               && a_esc_code, CR_BAD_PARAM_ERROR);
00890 
00891         RECORD_INITIAL_POS (a_this, &init_pos);
00892 
00893         PEEK_BYTE (a_this, 1, &next_chars[0]);
00894         PEEK_BYTE (a_this, 2, &next_chars[1]);
00895 
00896         if (next_chars[0] != '\\') {
00897                 status = CR_PARSING_ERROR;
00898                 cr_parser_push_error
00899                         (a_this,
00900                          "next char expected to be a '\\'", CR_SYNTAX_ERROR);
00901 
00902                 goto error;
00903         }
00904 
00905         if ((next_chars[1] >= '0' && next_chars[1] <= '9')
00906             || (next_chars[1] >= 'a' && next_chars[1] <= 'z')
00907             || (next_chars[1] >= 'A' && next_chars[1] <= 'Z')) {
00908                 status = cr_parser_parse_unicode_escape (a_this, a_esc_code);
00909                 if (status != CR_OK
00910                     && cr_parser_errors_exist (a_this) == TRUE) {
00911                         cr_parser_clear_errors (a_this);
00912                 }
00913         } else {
00914                 /*consume the '\' char */
00915                 READ_NEXT_CHAR (a_this, &cur_char);
00916 
00917                 /*then read the char after the '\' */
00918                 READ_NEXT_CHAR (a_this, &cur_char);
00919 
00920                 if (cur_char != ' ' && (cur_char < 200 || cur_char > 4177777)) {
00921                         status = CR_PARSING_ERROR;
00922 
00923                         cr_parser_push_error
00924                                 (a_this,
00925                                  "next char expected to be a space",
00926                                  CR_SYNTAX_ERROR);
00927 
00928                         goto error;
00929                 }
00930 
00931                 *a_esc_code = cur_char;
00932 
00933         }
00934 
00935         if (status == CR_OK) {
00936                 cr_parser_clear_errors (a_this);
00937                 return CR_OK;
00938         }
00939 
00940       error:
00941 
00942         cr_tknzr_set_cur_pos (PRIVATE (a_this) tknzr, &init_pos);
00943 
00944         return status;
00945 }
00946 #endif /*WITH_UNICODE_ESCAPE_AND_RANGE */
00947 
00948 /*************************************
00949  *Non trivial terminal productions
00950  *parsing routines
00951  *************************************/
00952 
00953 /**
00954  *Parses a css stylesheet following the core css grammar.
00955  *This is mainly done for test purposes.
00956  *During the parsing, no callback is called. This is just
00957  *to validate that the stylesheet is well formed according to the
00958  *css core syntax.
00959  *stylesheet  : [ CDO | CDC | S | statement ]*;
00960  *@param a_this the current instance of #CRParser.
00961  *@return CR_OK upon successfull completion, an error code otherwise.
00962  */
00963 static enum CRStatus
00964 cr_parser_parse_stylesheet_core (CRParser * a_this)
00965 {
00966         CRToken *token = NULL;
00967         CRInputPos init_pos;
00968         enum CRStatus status = CR_ERROR;
00969 
00970         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
00971 
00972         RECORD_INITIAL_POS (a_this, &init_pos);
00973 
00974       continue_parsing:
00975 
00976         if (token) {
00977                 cr_token_destroy (token);
00978                 token = NULL;
00979         }
00980 
00981         cr_parser_try_to_skip_spaces_and_comments (a_this);
00982         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
00983         if (status == CR_END_OF_INPUT_ERROR) {
00984                 status = CR_OK;
00985                 goto done;
00986         } else if (status != CR_OK) {
00987                 goto error;
00988         }
00989 
00990         switch (token->type) {
00991 
00992         case CDO_TK:
00993         case CDC_TK:
00994                 goto continue_parsing;
00995                 break;
00996         default:
00997                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
00998                                                token);
00999                 CHECK_PARSING_STATUS (status, TRUE);
01000                 token = NULL;
01001                 status = cr_parser_parse_statement_core (a_this);
01002                 cr_parser_clear_errors (a_this);
01003                 if (status == CR_OK) {
01004                         goto continue_parsing;
01005                 } else if (status == CR_END_OF_INPUT_ERROR) {
01006                         goto done;
01007                 } else {
01008                         goto error;
01009                 }
01010         }
01011 
01012       done:
01013         if (token) {
01014                 cr_token_destroy (token);
01015                 token = NULL;
01016         }
01017 
01018         cr_parser_clear_errors (a_this);
01019         return CR_OK;
01020       error:
01021 
01022         cr_parser_push_error
01023                 (a_this, "could not recognize next production", CR_ERROR);
01024 
01025         cr_parser_dump_err_stack (a_this, TRUE);
01026 
01027         if (token) {
01028                 cr_token_destroy (token);
01029                 token = NULL;
01030         }
01031 
01032         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01033 
01034         return status;
01035 }
01036 
01037 /**
01038  *Parses an at-rule as defined by the css core grammar
01039  *in chapter 4.1 in the css2 spec.
01040  *at-rule     : ATKEYWORD S* any* [ block | ';' S* ];
01041  *@param a_this the current instance of #CRParser.
01042  *@return CR_OK upon successfull completion, an error code
01043  *otherwise.
01044  */
01045 static enum CRStatus
01046 cr_parser_parse_atrule_core (CRParser * a_this)
01047 {
01048         CRToken *token = NULL;
01049         CRInputPos init_pos;
01050         enum CRStatus status = CR_ERROR;
01051 
01052         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01053 
01054         RECORD_INITIAL_POS (a_this, &init_pos);
01055 
01056         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01057         ENSURE_PARSING_COND (status == CR_OK
01058                              && token
01059                              &&
01060                              (token->type == ATKEYWORD_TK
01061                               || token->type == IMPORT_SYM_TK
01062                               || token->type == PAGE_SYM_TK
01063                               || token->type == MEDIA_SYM_TK
01064                               || token->type == FONT_FACE_SYM_TK
01065                               || token->type == CHARSET_SYM_TK));
01066 
01067         cr_token_destroy (token);
01068         token = NULL;
01069 
01070         cr_parser_try_to_skip_spaces_and_comments (a_this);
01071 
01072         do {
01073                 status = cr_parser_parse_any_core (a_this);
01074         } while (status == CR_OK);
01075 
01076         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01077         ENSURE_PARSING_COND (status == CR_OK && token);
01078 
01079         if (token->type == CBO_TK) {
01080                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
01081                 token = NULL;
01082                 status = cr_parser_parse_block_core (a_this);
01083                 CHECK_PARSING_STATUS (status, FALSE);
01084                 goto done;
01085         } else if (token->type == SEMICOLON_TK) {
01086                 goto done;
01087         } else {
01088                 status = CR_PARSING_ERROR ;
01089                 goto error;
01090         }
01091 
01092  done:
01093         if (token) {
01094                 cr_token_destroy (token);
01095                 token = NULL;
01096         }
01097 
01098         return CR_OK;
01099 
01100  error:
01101         if (token) {
01102                 cr_token_destroy (token);
01103                 token = NULL;
01104         }
01105 
01106         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01107 
01108         return status;
01109 }
01110 
01111 /**
01112  *Parses a ruleset as defined by the css core grammar in chapter
01113  *4.1 of the css2 spec.
01114  *ruleset ::= selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*;
01115  *@param a_this the current instance of #CRParser.
01116  *@return CR_OK upon successfull completion, an error code otherwise.
01117  */
01118 static enum CRStatus
01119 cr_parser_parse_ruleset_core (CRParser * a_this)
01120 {
01121         CRToken *token = NULL;
01122         CRInputPos init_pos;
01123         enum CRStatus status = CR_ERROR;
01124 
01125         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01126         RECORD_INITIAL_POS (a_this, &init_pos);
01127 
01128         status = cr_parser_parse_selector_core (a_this);
01129 
01130         ENSURE_PARSING_COND (status == CR_OK
01131                              || status == CR_PARSING_ERROR
01132                              || status == CR_END_OF_INPUT_ERROR);
01133 
01134         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01135         ENSURE_PARSING_COND (status == CR_OK && token
01136                              && token->type == CBO_TK);
01137         cr_token_destroy (token);
01138         token = NULL;
01139 
01140         cr_parser_try_to_skip_spaces_and_comments (a_this);
01141         status = cr_parser_parse_declaration_core (a_this);
01142 
01143       parse_declaration_list:
01144         if (token) {
01145                 cr_token_destroy (token);
01146                 token = NULL;
01147         }
01148 
01149         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01150         ENSURE_PARSING_COND (status == CR_OK && token);
01151         if (token->type == CBC_TK) {
01152                 goto done;
01153         }
01154 
01155         ENSURE_PARSING_COND (status == CR_OK
01156                              && token && token->type == SEMICOLON_TK);
01157 
01158         cr_token_destroy (token);
01159         token = NULL;
01160         cr_parser_try_to_skip_spaces_and_comments (a_this);
01161         status = cr_parser_parse_declaration_core (a_this);
01162         cr_parser_clear_errors (a_this);
01163         ENSURE_PARSING_COND (status == CR_OK || status == CR_PARSING_ERROR);
01164         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01165         ENSURE_PARSING_COND (status == CR_OK && token);
01166         if (token->type == CBC_TK) {
01167                 cr_token_destroy (token);
01168                 token = NULL;
01169                 cr_parser_try_to_skip_spaces_and_comments (a_this);
01170                 goto done;
01171         } else {
01172                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
01173                                                token);
01174                 token = NULL;
01175                 goto parse_declaration_list;
01176         }
01177 
01178       done:
01179         if (token) {
01180                 cr_token_destroy (token);
01181                 token = NULL;
01182         }
01183 
01184         if (status == CR_OK) {
01185                 return CR_OK;
01186         }
01187 
01188       error:
01189         if (token) {
01190                 cr_token_destroy (token);
01191                 token = NULL;
01192         }
01193 
01194         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01195 
01196         return status;
01197 }
01198 
01199 /**
01200  *Parses a "selector" as specified by the css core 
01201  *grammar.
01202  *selector    : any+;
01203  *@param a_this the current instance of #CRParser.
01204  *@return CR_OK upon successfull completion, an error code
01205  *otherwise.
01206  */
01207 static enum CRStatus
01208 cr_parser_parse_selector_core (CRParser * a_this)
01209 {
01210         CRToken *token = NULL;
01211         CRInputPos init_pos;
01212         enum CRStatus status = CR_ERROR;
01213 
01214         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01215 
01216         RECORD_INITIAL_POS (a_this, &init_pos);
01217 
01218         status = cr_parser_parse_any_core (a_this);
01219         CHECK_PARSING_STATUS (status, FALSE);
01220 
01221         do {
01222                 status = cr_parser_parse_any_core (a_this);
01223 
01224         } while (status == CR_OK);
01225 
01226         return CR_OK;
01227 
01228       error:
01229         if (token) {
01230                 cr_token_destroy (token);
01231                 token = NULL;
01232         }
01233 
01234         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01235 
01236         return status;
01237 }
01238 
01239 /**
01240  *Parses a "block" as defined in the css core grammar
01241  *in chapter 4.1 of the css2 spec.
01242  *block ::= '{' S* [ any | block | ATKEYWORD S* | ';' ]* '}' S*;
01243  *@param a_this the current instance of #CRParser.
01244  *FIXME: code this function.
01245  */
01246 static enum CRStatus
01247 cr_parser_parse_block_core (CRParser * a_this)
01248 {
01249         CRToken *token = NULL;
01250         CRInputPos init_pos;
01251         enum CRStatus status = CR_ERROR;
01252 
01253         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01254 
01255         RECORD_INITIAL_POS (a_this, &init_pos);
01256 
01257         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01258         ENSURE_PARSING_COND (status == CR_OK && token
01259                              && token->type == CBO_TK);
01260 
01261       parse_block_content:
01262 
01263         if (token) {
01264                 cr_token_destroy (token);
01265                 token = NULL;
01266         }
01267 
01268         cr_parser_try_to_skip_spaces_and_comments (a_this);
01269 
01270         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01271         ENSURE_PARSING_COND (status == CR_OK && token);
01272 
01273         if (token->type == CBC_TK) {
01274                 cr_parser_try_to_skip_spaces_and_comments (a_this);
01275                 goto done;
01276         } else if (token->type == SEMICOLON_TK) {
01277                 goto parse_block_content;
01278         } else if (token->type == ATKEYWORD_TK) {
01279                 cr_parser_try_to_skip_spaces_and_comments (a_this);
01280                 goto parse_block_content;
01281         } else if (token->type == CBO_TK) {
01282                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
01283                 token = NULL;
01284                 status = cr_parser_parse_block_core (a_this);
01285                 CHECK_PARSING_STATUS (status, FALSE);
01286                 goto parse_block_content;
01287         } else {
01288                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
01289                 token = NULL;
01290                 status = cr_parser_parse_any_core (a_this);
01291                 CHECK_PARSING_STATUS (status, FALSE);
01292                 goto parse_block_content;
01293         }
01294 
01295       done:
01296         if (token) {
01297                 cr_token_destroy (token);
01298                 token = NULL;
01299         }
01300 
01301         if (status == CR_OK)
01302                 return CR_OK;
01303 
01304       error:
01305         if (token) {
01306                 cr_token_destroy (token);
01307                 token = NULL;
01308         }
01309 
01310         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01311 
01312         return status;
01313 }
01314 
01315 static enum CRStatus
01316 cr_parser_parse_declaration_core (CRParser * a_this)
01317 {
01318         CRToken *token = NULL;
01319         CRInputPos init_pos;
01320         enum CRStatus status = CR_ERROR;
01321         GString *prop = NULL;
01322 
01323         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01324 
01325         RECORD_INITIAL_POS (a_this, &init_pos);
01326 
01327         status = cr_parser_parse_property (a_this, &prop);
01328         CHECK_PARSING_STATUS (status, FALSE);
01329         cr_parser_clear_errors (a_this);
01330         ENSURE_PARSING_COND (status == CR_OK && prop);
01331         g_string_free (prop, TRUE);
01332         prop = NULL;
01333 
01334         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01335         ENSURE_PARSING_COND (status == CR_OK
01336                              && token
01337                              && token->type == DELIM_TK
01338                              && token->u.unichar == ':');
01339         cr_token_destroy (token);
01340         token = NULL;
01341         cr_parser_try_to_skip_spaces_and_comments (a_this);
01342         status = cr_parser_parse_value_core (a_this);
01343         CHECK_PARSING_STATUS (status, FALSE);
01344 
01345         return CR_OK;
01346 
01347       error:
01348 
01349         if (prop) {
01350                 g_string_free (prop, TRUE);
01351                 prop = NULL;
01352         }
01353 
01354         if (token) {
01355                 cr_token_destroy (token);
01356                 token = NULL;
01357         }
01358 
01359         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01360 
01361         return status;
01362 }
01363 
01364 /**
01365  *Parses a "value" production as defined by the css core grammar
01366  *in chapter 4.1.
01367  *value ::= [ any | block | ATKEYWORD S* ]+;
01368  *@param a_this the current instance of #CRParser.
01369  *@return CR_OK upon successfull completion, an error code otherwise.
01370  */
01371 static enum CRStatus
01372 cr_parser_parse_value_core (CRParser * a_this)
01373 {
01374         CRToken *token = NULL;
01375         CRInputPos init_pos;
01376         enum CRStatus status = CR_ERROR;
01377         glong ref = 0;
01378 
01379         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01380         RECORD_INITIAL_POS (a_this, &init_pos);
01381 
01382       continue_parsing:
01383 
01384         if (token) {
01385                 cr_token_destroy (token);
01386                 token = NULL;
01387         }
01388 
01389         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01390         ENSURE_PARSING_COND (status == CR_OK && token);
01391 
01392         switch (token->type) {
01393         case CBO_TK:
01394                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
01395                                                token);
01396                 token = NULL;
01397                 status = cr_parser_parse_block_core (a_this);
01398                 CHECK_PARSING_STATUS (status, FALSE);
01399                 ref++;
01400                 goto continue_parsing;
01401 
01402         case ATKEYWORD_TK:
01403                 cr_parser_try_to_skip_spaces_and_comments (a_this);
01404                 ref++;
01405                 goto continue_parsing;
01406 
01407         default:
01408                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
01409                                                token);
01410                 token = NULL;
01411                 status = cr_parser_parse_any_core (a_this);
01412                 if (status == CR_OK) {
01413                         ref++;
01414                         goto continue_parsing;
01415                 } else if (status == CR_PARSING_ERROR) {
01416                         status = CR_OK;
01417                         goto done;
01418                 } else {
01419                         goto error;
01420                 }
01421         }
01422 
01423       done:
01424         if (token) {
01425                 cr_token_destroy (token);
01426                 token = NULL;
01427         }
01428 
01429         if (status == CR_OK && ref)
01430                 return CR_OK;
01431       error:
01432         if (token) {
01433                 cr_token_destroy (token);
01434                 token = NULL;
01435         }
01436 
01437         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01438 
01439         return status;
01440 }
01441 
01442 /**
01443  *Parses an "any" as defined by the css core grammar in the
01444  *css2 spec in chapter 4.1.
01445  *any ::= [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING
01446  *        | DELIM | URI | HASH | UNICODE-RANGE | INCLUDES
01447  *        | FUNCTION | DASHMATCH | '(' any* ')' | '[' any* ']' ] S*;
01448  *
01449  *@param a_this the current instance of #CRParser.
01450  *@return CR_OK upon successfull completion, an error code otherwise.
01451  */
01452 static enum CRStatus
01453 cr_parser_parse_any_core (CRParser * a_this)
01454 {
01455         CRToken *token1 = NULL,
01456                 *token2 = NULL;
01457         CRInputPos init_pos;
01458         enum CRStatus status = CR_ERROR;
01459 
01460         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
01461 
01462         RECORD_INITIAL_POS (a_this, &init_pos);
01463 
01464         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token1);
01465 
01466         ENSURE_PARSING_COND (status == CR_OK && token1);
01467 
01468         switch (token1->type) {
01469         case IDENT_TK:
01470         case NUMBER_TK:
01471         case RGB_TK:
01472         case PERCENTAGE_TK:
01473         case DIMEN_TK:
01474         case EMS_TK:
01475         case EXS_TK:
01476         case LENGTH_TK:
01477         case ANGLE_TK:
01478         case FREQ_TK:
01479         case TIME_TK:
01480         case STRING_TK:
01481         case DELIM_TK:
01482         case URI_TK:
01483         case HASH_TK:
01484         case UNICODERANGE_TK:
01485         case INCLUDES_TK:
01486         case DASHMATCH_TK:
01487         case S_TK:
01488         case IMPORTANT_SYM_TK:
01489                 status = CR_OK;
01490                 break;
01491         case FUNCTION_TK:
01492                 /*
01493                  *this case isn't specified by the spec but it
01494                  *does happen. So we have to handle it.
01495                  *We must consider function with parameters.
01496                  *We consider parameter as being an "any*" production.
01497                  */
01498                 do {
01499                         status = cr_parser_parse_any_core (a_this);
01500                 } while (status == CR_OK);
01501 
01502                 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
01503                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
01504                                                   &token2);
01505                 ENSURE_PARSING_COND (status == CR_OK
01506                                      && token2 && token2->type == PC_TK);
01507                 break;
01508         case PO_TK:
01509                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
01510                                                   &token2);
01511                 ENSURE_PARSING_COND (status == CR_OK && token2);
01512 
01513                 if (token2->type == PC_TK) {
01514                         cr_token_destroy (token2);
01515                         token2 = NULL;
01516                         goto done;
01517                 } else {
01518                         status = cr_tknzr_unget_token
01519                                 (PRIVATE (a_this)->tknzr, token2);
01520                         token2 = NULL;
01521                 }
01522 
01523                 do {
01524                         status = cr_parser_parse_any_core (a_this);
01525                 } while (status == CR_OK);
01526 
01527                 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
01528 
01529                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
01530                                                   &token2);
01531                 ENSURE_PARSING_COND (status == CR_OK
01532                                      && token2 && token2->type == PC_TK);
01533                 status = CR_OK;
01534                 break;
01535 
01536         case BO_TK:
01537                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
01538                                                   &token2);
01539                 ENSURE_PARSING_COND (status == CR_OK && token2);
01540 
01541                 if (token2->type == BC_TK) {
01542                         cr_token_destroy (token2);
01543                         token2 = NULL;
01544                         goto done;
01545                 } else {
01546                         status = cr_tknzr_unget_token
01547                                 (PRIVATE (a_this)->tknzr, token2);
01548                         token2 = NULL;
01549                 }
01550 
01551                 do {
01552                         status = cr_parser_parse_any_core (a_this);
01553                 } while (status == CR_OK);
01554 
01555                 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
01556 
01557                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
01558                                                   &token2);
01559                 ENSURE_PARSING_COND (status == CR_OK
01560                                      && token2 && token2->type == BC_TK);
01561                 status = CR_OK;
01562                 break;
01563         default:
01564                 status = CR_PARSING_ERROR;
01565                 goto error;
01566         }
01567 
01568       done:
01569         if (token1) {
01570                 cr_token_destroy (token1);
01571                 token1 = NULL;
01572         }
01573 
01574         if (token2) {
01575                 cr_token_destroy (token2);
01576                 token2 = NULL;
01577         }
01578 
01579         return CR_OK;
01580 
01581       error:
01582 
01583         if (token1) {
01584                 cr_token_destroy (token1);
01585                 token1 = NULL;
01586         }
01587 
01588         if (token2) {
01589                 cr_token_destroy (token2);
01590                 token2 = NULL;
01591         }
01592 
01593         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01594         return status;
01595 }
01596 
01597 /**
01598  *Parses an attribute selector as defined in the css2 spec in
01599  *appendix D.1:
01600  *attrib ::= '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
01601  *            [ IDENT | STRING ] S* ]? ']'
01602  *
01603  *@param a_this the "this pointer" of the current instance of
01604  *#CRParser .
01605  *@param a_sel out parameter. The successfully parsed attribute selector.
01606  *@return CR_OK upon successfull completion, an error code otherwise.
01607  */
01608 static enum CRStatus
01609 cr_parser_parse_attribute_selector (CRParser * a_this, CRAttrSel ** a_sel)
01610 {
01611         enum CRStatus status = CR_OK;
01612         CRInputPos init_pos;
01613         CRToken *token = NULL;
01614         CRAttrSel *result = NULL;
01615 
01616         g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
01617 
01618         RECORD_INITIAL_POS (a_this, &init_pos);
01619 
01620         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01621         ENSURE_PARSING_COND (status == CR_OK && token
01622                              && token->type == BO_TK);
01623 
01624         cr_token_destroy (token);
01625         token = NULL;
01626 
01627         cr_parser_try_to_skip_spaces_and_comments (a_this);
01628 
01629         result = cr_attr_sel_new ();
01630 
01631         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01632         ENSURE_PARSING_COND (status == CR_OK
01633                              && token && token->type == IDENT_TK);
01634 
01635         result->name = token->u.str;
01636         token->u.str = NULL;
01637         cr_token_destroy (token);
01638         token = NULL;
01639 
01640         cr_parser_try_to_skip_spaces_and_comments (a_this);
01641 
01642         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01643         ENSURE_PARSING_COND (status == CR_OK && token);
01644 
01645         if (token->type == INCLUDES_TK) {
01646                 result->match_way = INCLUDES;
01647                 goto parse_right_part;
01648         } else if (token->type == DASHMATCH_TK) {
01649                 result->match_way = DASHMATCH;
01650                 goto parse_right_part;
01651         } else if (token->type == DELIM_TK && token->u.unichar == '=') {
01652                 result->match_way = EQUALS;
01653                 goto parse_right_part;
01654         } else if (token->type == BC_TK) {
01655                 result->match_way = SET;
01656                 goto done;
01657         }
01658 
01659       parse_right_part:
01660 
01661         if (token) {
01662                 cr_token_destroy (token);
01663                 token = NULL;
01664         }
01665 
01666         cr_parser_try_to_skip_spaces_and_comments (a_this);
01667 
01668         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01669 
01670         ENSURE_PARSING_COND (status == CR_OK && token);
01671 
01672         if (token->type == IDENT_TK) {
01673                 result->value = token->u.str;
01674                 token->u.str = NULL;
01675         } else if (token->type == STRING_TK) {
01676                 result->value = token->u.str;
01677                 token->u.str = NULL;
01678         } else {
01679                 status = CR_PARSING_ERROR;
01680                 goto error;
01681         }
01682 
01683         if (token) {
01684                 cr_token_destroy (token);
01685                 token = NULL;
01686         }
01687 
01688         cr_parser_try_to_skip_spaces_and_comments (a_this);
01689 
01690         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01691 
01692         ENSURE_PARSING_COND (status == CR_OK && token
01693                              && token->type == BC_TK);
01694       done:
01695         if (token) {
01696                 cr_token_destroy (token);
01697                 token = NULL;
01698         }
01699 
01700         if (*a_sel) {
01701                 status = cr_attr_sel_append_attr_sel (*a_sel, result);
01702                 CHECK_PARSING_STATUS (status, FALSE);
01703         } else {
01704                 *a_sel = result;
01705         }
01706 
01707         cr_parser_clear_errors (a_this);
01708         return CR_OK;
01709 
01710       error:
01711 
01712         if (result) {
01713                 cr_attr_sel_destroy (result);
01714                 result = NULL;
01715         }
01716 
01717         if (token) {
01718                 cr_token_destroy (token);
01719                 token = NULL;
01720         }
01721 
01722         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01723 
01724         return status;
01725 }
01726 
01727 /**
01728  *Parses a "property" as specified by the css2 spec at [4.1.1]:
01729  *property : IDENT S*;
01730  *
01731  *@param a_this the "this pointer" of the current instance of #CRParser.
01732  *@param GString a_property out parameter. The parsed property without the
01733  *trailing spaces. If *a_property is NULL, this function allocates a
01734  *new instance of GString and set it content to the parsed property.
01735  *If not, the property is just appended to a_property's previous content.
01736  *In both cases, it is up to the caller to free a_property.
01737  *@return CR_OK upon successfull completion, CR_PARSING_ERROR if the
01738  *next construction was not a "property", or an error code.
01739  */
01740 static enum CRStatus
01741 cr_parser_parse_property (CRParser * a_this, GString ** a_property)
01742 {
01743         enum CRStatus status = CR_OK;
01744         CRInputPos init_pos;
01745 
01746         g_return_val_if_fail (a_this && PRIVATE (a_this)
01747                               && PRIVATE (a_this)->tknzr
01748                               && a_property, CR_BAD_PARAM_ERROR);
01749 
01750         RECORD_INITIAL_POS (a_this, &init_pos);
01751 
01752         status = cr_parser_parse_ident (a_this, a_property);
01753         CHECK_PARSING_STATUS (status, TRUE);
01754 
01755         cr_parser_try_to_skip_spaces_and_comments (a_this);
01756 
01757         cr_parser_clear_errors (a_this);
01758         return CR_OK;
01759 
01760       error:
01761 
01762         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01763 
01764         return status;
01765 }
01766 
01767 /**
01768  *Parses a "term" as defined in the css2 spec, appendix D.1:
01769  *term ::= unary_operator? [NUMBER S* | PERCENTAGE S* | LENGTH S* | 
01770  *EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* | function ] |
01771  *STRING S* | IDENT S* | URI S* | RGB S* | UNICODERANGE S* | hexcolor
01772  *
01773  *TODO: handle parsing of 'RGB'
01774  *
01775  *@param a_term out parameter. The successfully parsed term.
01776  *@return CR_OK upon successfull completion, an error code otherwise.
01777  */
01778 enum CRStatus
01779 cr_parser_parse_term (CRParser * a_this, CRTerm ** a_term)
01780 {
01781         enum CRStatus status = CR_PARSING_ERROR;
01782         CRInputPos init_pos;
01783         CRTerm *result = NULL;
01784         CRTerm *param = NULL;
01785         CRToken *token = NULL;
01786         GString *func_name = NULL;
01787 
01788         g_return_val_if_fail (a_this && a_term, CR_BAD_PARAM_ERROR);
01789 
01790         RECORD_INITIAL_POS (a_this, &init_pos);
01791 
01792         result = cr_term_new ();
01793 
01794         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01795         if (status != CR_OK || !token)
01796                 goto error;
01797 
01798         if (token->type == DELIM_TK && token->u.unichar == '+') {
01799                 result->unary_op = PLUS_UOP;
01800         } else if (token->type == DELIM_TK && token->u.unichar == '-') {
01801                 result->unary_op = MINUS_UOP;
01802         } else if (token->type == EMS_TK
01803                    || token->type == EXS_TK
01804                    || token->type == LENGTH_TK
01805                    || token->type == ANGLE_TK
01806                    || token->type == TIME_TK
01807                    || token->type == FREQ_TK
01808                    || token->type == PERCENTAGE_TK
01809                    || token->type == NUMBER_TK) {
01810                 status = cr_term_set_number (result, token->u.num);
01811                 CHECK_PARSING_STATUS (status, TRUE);
01812                 token->u.num = NULL;
01813                 status = CR_OK;
01814         } else if (token && token->type == FUNCTION_TK) {
01815                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
01816                                                token);
01817                 token = NULL;
01818                 status = cr_parser_parse_function (a_this, &func_name,
01819                                                    &param);
01820 
01821                 if (status == CR_OK) {
01822                         status = cr_term_set_function (result, func_name,
01823                                                        param);
01824                         CHECK_PARSING_STATUS (status, TRUE);
01825                 }
01826         } else if (token && token->type == STRING_TK) {
01827                 status = cr_term_set_string (result, token->u.str);
01828                 CHECK_PARSING_STATUS (status, TRUE);
01829                 token->u.str = NULL;
01830         } else if (token && token->type == IDENT_TK) {
01831                 status = cr_term_set_ident (result, token->u.str);
01832                 CHECK_PARSING_STATUS (status, TRUE);
01833                 token->u.str = NULL;
01834         } else if (token && token->type == URI_TK) {
01835                 status = cr_term_set_uri (result, token->u.str);
01836                 CHECK_PARSING_STATUS (status, TRUE);
01837                 token->u.str = NULL;
01838         } else if (token && token->type == RGB_TK) {
01839                 status = cr_term_set_rgb (result, token->u.rgb);
01840                 CHECK_PARSING_STATUS (status, TRUE);
01841                 token->u.rgb = NULL;
01842         } else if (token && token->type == UNICODERANGE_TK) {
01843                 result->type = TERM_UNICODERANGE;
01844                 status = CR_PARSING_ERROR;
01845         } else if (token && token->type == HASH_TK) {
01846                 status = cr_term_set_hash (result, token->u.str);
01847                 CHECK_PARSING_STATUS (status, TRUE);
01848                 token->u.str = NULL;
01849         } else {
01850                 status = CR_PARSING_ERROR;
01851         }
01852 
01853         if (status != CR_OK) {
01854                 goto error;
01855         }
01856 
01857         *a_term = cr_term_append_term (*a_term, result);
01858 
01859         result = NULL;
01860 
01861         cr_parser_try_to_skip_spaces_and_comments (a_this);
01862 
01863         if (token) {
01864                 cr_token_destroy (token);
01865                 token = NULL;
01866         }
01867 
01868         cr_parser_clear_errors (a_this);
01869         return CR_OK;
01870 
01871       error:
01872 
01873         if (result) {
01874                 cr_term_destroy (result);
01875                 result = NULL;
01876         }
01877 
01878         if (token) {
01879                 cr_token_destroy (token);
01880                 token = NULL;
01881         }
01882 
01883         if (param) {
01884                 cr_term_destroy (param);
01885                 param = NULL;
01886         }
01887 
01888         if (func_name) {
01889                 g_string_free (func_name, TRUE);
01890                 func_name = NULL;
01891         }
01892 
01893         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
01894 
01895         return status;
01896 }
01897 
01898 /**
01899  *Parses a "simple_selector" as defined by the css2 spec in appendix D.1 :
01900  *element_name? [ HASH | class | attrib | pseudo ]* S*
01901  *and where pseudo is:
01902  *pseudo ::=  ':' [ IDENT | FUNCTION S* IDENT S* ')' ]
01903  *
01904  *@Param a_this the "this pointer" of the current instance of #CRParser.
01905  *@param a_sel out parameter. Is set to the successfully parsed simple
01906  *selector.
01907  *@return CR_OK upon successfull completion, an error code otherwise.
01908  */
01909 static enum CRStatus
01910 cr_parser_parse_simple_selector (CRParser * a_this, CRSimpleSel ** a_sel)
01911 {
01912         enum CRStatus status = CR_ERROR;
01913         CRInputPos init_pos;
01914         CRToken *token = NULL;
01915         CRSimpleSel *sel = NULL;
01916         CRAdditionalSel *add_sel_list = NULL;
01917         gboolean found_sel = FALSE;
01918         guint32 cur_char = 0;
01919 
01920         g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
01921 
01922         RECORD_INITIAL_POS (a_this, &init_pos);
01923 
01924         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
01925         if (status != CR_OK)
01926                 goto error;
01927 
01928         sel = cr_simple_sel_new ();
01929         ENSURE_PARSING_COND (sel);
01930 
01931         if (token && token->type == DELIM_TK && token->u.unichar == '*') {
01932                 sel->type_mask |= UNIVERSAL_SELECTOR;
01933                 sel->name = g_string_new ("*");
01934                 found_sel = TRUE;
01935         } else if (token && token->type == IDENT_TK) {
01936                 sel->name = token->u.str;
01937                 sel->type_mask |= TYPE_SELECTOR;
01938                 token->u.str = NULL;
01939                 found_sel = TRUE;
01940 
01941         } else {
01942                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
01943                                                token);
01944                 token = NULL;
01945         }
01946 
01947         if (token) {
01948                 cr_token_destroy (token);
01949                 token = NULL;
01950         }
01951 
01952         cr_parser_try_to_skip_spaces_and_comments (a_this);
01953 
01954         for (;;) {
01955                 if (token) {
01956                         cr_token_destroy (token);
01957                         token = NULL;
01958                 }
01959 
01960                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
01961                                                   &token);
01962                 if (status != CR_OK)
01963                         goto error;
01964 
01965                 if (token && token->type == HASH_TK) {
01966                         /*we parsed an attribute id */
01967                         CRAdditionalSel *add_sel = NULL;
01968 
01969                         add_sel = cr_additional_sel_new_with_type
01970                                 (ID_ADD_SELECTOR);
01971 
01972                         add_sel->content.id_name = token->u.str;
01973                         token->u.str = NULL;
01974 
01975                         add_sel_list =
01976                                 cr_additional_sel_append
01977                                 (add_sel_list, add_sel);
01978 
01979                         found_sel = TRUE;
01980                 } else if (token && (token->type == DELIM_TK)
01981                            && (token->u.unichar == '.')) {
01982                         cr_token_destroy (token);
01983                         token = NULL;
01984 
01985                         status = cr_tknzr_get_next_token
01986                                 (PRIVATE (a_this)->tknzr, &token);
01987                         if (status != CR_OK)
01988                                 goto error;
01989 
01990                         if (token && token->type == IDENT_TK) {
01991                                 CRAdditionalSel *add_sel = NULL;
01992 
01993                                 add_sel = cr_additional_sel_new_with_type
01994                                         (CLASS_ADD_SELECTOR);
01995 
01996                                 add_sel->content.class_name = token->u.str;
01997                                 token->u.str = NULL;
01998 
01999                                 add_sel_list =
02000                                         cr_additional_sel_append
02001                                         (add_sel_list, add_sel);
02002                                 found_sel = TRUE;
02003                         } else {
02004                                 status = CR_PARSING_ERROR;
02005                                 goto error;
02006                         }
02007                 } else if (token && token->type == BO_TK) {
02008                         CRAttrSel *attr_sel = NULL;
02009                         CRAdditionalSel *add_sel = NULL;
02010 
02011                         status = cr_tknzr_unget_token
02012                                 (PRIVATE (a_this)->tknzr, token);
02013                         if (status != CR_OK)
02014                                 goto error;
02015                         token = NULL;
02016 
02017                         status = cr_parser_parse_attribute_selector
02018                                 (a_this, &attr_sel);
02019                         CHECK_PARSING_STATUS (status, FALSE);
02020 
02021                         add_sel = cr_additional_sel_new_with_type
02022                                 (ATTRIBUTE_ADD_SELECTOR);
02023 
02024                         ENSURE_PARSING_COND (add_sel != NULL);
02025 
02026                         add_sel->content.attr_sel = attr_sel;
02027 
02028                         add_sel_list =
02029                                 cr_additional_sel_append
02030                                 (add_sel_list, add_sel);
02031                         found_sel = TRUE;
02032                 } else if (token && (token->type == DELIM_TK)
02033                            && (token->u.unichar == ':')) {
02034                         CRPseudo *pseudo = NULL;
02035 
02036                         /*try to parse a pseudo */
02037 
02038                         if (token) {
02039                                 cr_token_destroy (token);
02040                                 token = NULL;
02041                         }
02042 
02043                         pseudo = cr_pseudo_new ();
02044 
02045                         status = cr_tknzr_get_next_token
02046                                 (PRIVATE (a_this)->tknzr, &token);
02047 
02048                         ENSURE_PARSING_COND (status == CR_OK && token);
02049 
02050                         if (token->type == IDENT_TK) {
02051                                 pseudo->type = IDENT_PSEUDO;
02052                                 pseudo->name = token->u.str;
02053                                 token->u.str = NULL;
02054                                 found_sel = TRUE;
02055                         } else if (token->type == FUNCTION_TK) {
02056                                 pseudo->name = token->u.str;
02057                                 token->u.str = NULL;
02058                                 cr_parser_try_to_skip_spaces_and_comments
02059                                         (a_this);
02060                                 status = cr_parser_parse_ident
02061                                         (a_this, &pseudo->extra);
02062 
02063                                 ENSURE_PARSING_COND (status == CR_OK);
02064                                 READ_NEXT_CHAR (a_this, &cur_char);
02065                                 ENSURE_PARSING_COND (cur_char == ')');
02066                                 pseudo->type = FUNCTION_PSEUDO;
02067                                 found_sel = TRUE;
02068                         } else {
02069                                 status = CR_PARSING_ERROR;
02070                                 goto error;
02071                         }
02072 
02073                         if (status == CR_OK) {
02074                                 CRAdditionalSel *add_sel = NULL;
02075 
02076                                 add_sel = cr_additional_sel_new_with_type
02077                                         (PSEUDO_CLASS_ADD_SELECTOR);
02078 
02079                                 add_sel->content.pseudo = pseudo;
02080 
02081                                 add_sel_list =
02082                                         cr_additional_sel_append
02083                                         (add_sel_list, add_sel);
02084 
02085                                 status = CR_OK;
02086                         }
02087                 } else {
02088                         status = cr_tknzr_unget_token
02089                                 (PRIVATE (a_this)->tknzr, token);
02090                         token = NULL;
02091                         break;
02092                 }
02093         }
02094 
02095         if (status == CR_OK && found_sel == TRUE) {
02096                 cr_parser_try_to_skip_spaces_and_comments (a_this);
02097 
02098                 sel->add_sel = add_sel_list;
02099                 add_sel_list = NULL;
02100 
02101                 if (*a_sel == NULL) {
02102                         *a_sel = sel;
02103                 } else {
02104                         cr_simple_sel_append_simple_sel (*a_sel, sel);
02105                 }
02106 
02107                 sel = NULL;
02108 
02109                 if (token) {
02110                         cr_token_destroy (token);
02111                         token = NULL;
02112                 }
02113 
02114                 cr_parser_clear_errors (a_this);
02115                 return CR_OK;
02116         } else {
02117                 status = CR_PARSING_ERROR;
02118         }
02119 
02120       error:
02121 
02122         if (token) {
02123                 cr_token_destroy (token);
02124                 token = NULL;
02125         }
02126 
02127         if (add_sel_list) {
02128                 cr_additional_sel_destroy (add_sel_list);
02129                 add_sel_list = NULL;
02130         }
02131 
02132         if (sel) {
02133                 cr_simple_sel_destroy (sel);
02134                 sel = NULL;
02135         }
02136 
02137         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
02138 
02139         return status;
02140 
02141 }
02142 
02143 /**
02144  *Parses a "selector" as defined by the css2 spec in appendix D.1:
02145  *selector ::=  simple_selector [ combinator simple_selector ]*
02146  *
02147  *@param a_this the this pointer of the current instance of #CRParser.
02148  *@param a_start a pointer to the 
02149  *first chararcter of the successfully parsed
02150  *string.
02151  *@param a_end a pointer to the last character of the successfully parsed
02152  *string.
02153  *@return CR_OK upon successfull completion, an error code otherwise.
02154  */
02155 static enum CRStatus
02156 cr_parser_parse_simple_sels (CRParser * a_this, CRSimpleSel ** a_sel)
02157 {
02158         enum CRStatus status = CR_ERROR;
02159         CRInputPos init_pos;
02160         CRSimpleSel *sel = NULL;
02161         guint32 cur_char = 0;
02162 
02163         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
02164 
02165         RECORD_INITIAL_POS (a_this, &init_pos);
02166 
02167         status = cr_parser_parse_simple_selector (a_this, &sel);
02168         CHECK_PARSING_STATUS (status, FALSE);
02169 
02170         *a_sel = cr_simple_sel_append_simple_sel (*a_sel, sel);
02171 
02172         for (;;) {
02173                 guint32 next_char = 0;
02174                 enum Combinator comb = 0;
02175 
02176                 sel = NULL;
02177 
02178                 PEEK_NEXT_CHAR (a_this, &next_char);
02179 
02180                 if (next_char == '+') {
02181                         READ_NEXT_CHAR (a_this, &cur_char);
02182                         comb = COMB_PLUS;
02183                         cr_parser_try_to_skip_spaces_and_comments (a_this);
02184                 } else if (next_char == '>') {
02185                         READ_NEXT_CHAR (a_this, &cur_char);
02186                         comb = COMB_GT;
02187                         cr_parser_try_to_skip_spaces_and_comments (a_this);
02188                 } else {
02189                         comb = COMB_WS;
02190                 }
02191 
02192                 status = cr_parser_parse_simple_selector (a_this, &sel);
02193                 if (status != CR_OK)
02194                         break;
02195 
02196                 if (comb && sel) {
02197                         sel->combinator = comb;
02198                         comb = 0;
02199                 }
02200                 if (sel)
02201                         *a_sel = cr_simple_sel_append_simple_sel (*a_sel, sel);
02202         }
02203 
02204         cr_parser_clear_errors (a_this);
02205         return CR_OK;
02206 
02207       error:
02208 
02209         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
02210 
02211         return status;
02212 }
02213 
02214 static enum CRStatus
02215 cr_parser_parse_selector (CRParser * a_this, CRSelector ** a_selector)
02216 {
02217         enum CRStatus status = CR_OK;
02218         CRInputPos init_pos;
02219         guint32 cur_char = 0,
02220                 next_char = 0;
02221         CRSimpleSel *simple_sels = NULL;
02222         CRSelector *selector = NULL;
02223 
02224         g_return_val_if_fail (a_this && a_selector, CR_BAD_PARAM_ERROR);
02225 
02226         RECORD_INITIAL_POS (a_this, &init_pos);
02227 
02228         status = cr_parser_parse_simple_sels (a_this, &simple_sels);
02229 
02230         CHECK_PARSING_STATUS (status, FALSE);
02231 
02232         if (simple_sels) {
02233                 selector = cr_selector_append_simple_sel
02234                         (selector, simple_sels);
02235                 simple_sels = NULL;
02236         }
02237 
02238         status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, &next_char);
02239         if (status != CR_OK) {
02240                 if (status == CR_END_OF_INPUT_ERROR) {
02241                         status = CR_OK;
02242                         goto okay;
02243                 } else {
02244                         goto error;
02245                 }
02246         }
02247 
02248         if (next_char == ',') {
02249                 for (;;) {
02250                         simple_sels = NULL;
02251 
02252                         status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
02253                                                      &next_char);
02254                         if (status != CR_OK) {
02255                                 if (status == CR_END_OF_INPUT_ERROR) {
02256                                         status = CR_OK;
02257                                         break;
02258                                 } else {
02259                                         goto error;
02260                                 }
02261                         }
02262 
02263                         if (next_char != ',')
02264                                 break;
02265 
02266                         /*consume the ',' char */
02267                         READ_NEXT_CHAR (a_this, &cur_char);
02268 
02269                         cr_parser_try_to_skip_spaces_and_comments (a_this);
02270 
02271                         status = cr_parser_parse_simple_sels
02272                                 (a_this, &simple_sels);
02273 
02274                         CHECK_PARSING_STATUS (status, FALSE);
02275 
02276                         if (simple_sels) {
02277                                 selector =
02278                                         cr_selector_append_simple_sel
02279                                         (selector, simple_sels);
02280 
02281                                 simple_sels = NULL;
02282                         }
02283                 }
02284         }
02285 
02286       okay:
02287         cr_parser_try_to_skip_spaces_and_comments (a_this);
02288 
02289         if (!*a_selector) {
02290                 *a_selector = selector;
02291         } else {
02292                 *a_selector = cr_selector_append (*a_selector, selector);
02293         }
02294 
02295         selector = NULL;
02296         return CR_OK;
02297 
02298       error:
02299 
02300         if (simple_sels) {
02301                 cr_simple_sel_destroy (simple_sels);
02302                 simple_sels = NULL;
02303         }
02304 
02305         if (selector) {
02306                 cr_selector_unref (selector);
02307                 selector = NULL;
02308         }
02309 
02310         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
02311 
02312         return status;
02313 }
02314 
02315 /**
02316  *Parses a "function" as defined in css spec at appendix D.1:
02317  *function ::= FUNCTION S* expr ')' S*
02318  *FUNCTION ::= ident'('
02319  *
02320  *@param a_this the "this pointer" of the current instance of
02321  *#CRParser.
02322  *
02323  *@param a_func_name out parameter. The parsed function name
02324  *@param a_expr out parameter. The successfully parsed term.
02325  *@return CR_OK upon successfull completion, an error code otherwise.
02326  */
02327 static enum CRStatus
02328 cr_parser_parse_function (CRParser * a_this,
02329                           GString ** a_func_name, CRTerm ** a_expr)
02330 {
02331         CRInputPos init_pos;
02332         enum CRStatus status = CR_OK;
02333         CRToken *token = NULL;
02334         CRTerm *expr = NULL;
02335 
02336         g_return_val_if_fail (a_this && PRIVATE (a_this)
02337                               && a_func_name, CR_BAD_PARAM_ERROR);
02338 
02339         RECORD_INITIAL_POS (a_this, &init_pos);
02340 
02341         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
02342         if (status != CR_OK)
02343                 goto error;
02344 
02345         if (token && token->type == FUNCTION_TK) {
02346                 *a_func_name = token->u.str;
02347                 token->u.str = NULL;
02348         } else {
02349                 status = CR_PARSING_ERROR;
02350                 goto error;
02351         }
02352 
02353         cr_token_destroy (token);
02354         token = NULL;
02355 
02356         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
02357         if (status != CR_OK)
02358                 goto error;
02359 
02360         ENSURE_PARSING_COND (token && token->type == PO_TK);
02361 
02362         cr_token_destroy (token);
02363         token = NULL;
02364 
02365         status = cr_parser_parse_term (a_this, &expr);
02366 
02367         CHECK_PARSING_STATUS (status, FALSE);
02368 
02369         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
02370         if (status != CR_OK)
02371                 goto error;
02372 
02373         ENSURE_PARSING_COND (token && token->type == PC_TK);
02374 
02375         cr_token_destroy (token);
02376         token = NULL;
02377 
02378         if (expr) {
02379                 *a_expr = cr_term_append_term (*a_expr, expr);
02380                 expr = NULL;
02381         }
02382 
02383         cr_parser_clear_errors (a_this);
02384         return CR_OK;
02385 
02386       error:
02387 
02388         if (*a_func_name) {
02389                 g_string_free (*a_func_name, TRUE);
02390                 *a_func_name = NULL;
02391         }
02392 
02393         if (expr) {
02394                 cr_term_destroy (expr);
02395                 expr = NULL;
02396         }
02397 
02398         if (token) {
02399                 cr_token_destroy (token);
02400 
02401         }
02402 
02403         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
02404 
02405         return status;
02406 }
02407 
02408 /**
02409  *Parses an uri as defined by the css spec [4.1.1]:
02410  * URI ::= url\({w}{string}{w}\)
02411  *         |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)
02412  *
02413  *@param a_this the current instance of #CRParser.
02414  *@param a_str the successfully parsed url.
02415  *@return CR_OK upon successfull completion, an error code otherwise.
02416  */
02417 static enum CRStatus
02418 cr_parser_parse_uri (CRParser * a_this, GString ** a_str)
02419 {
02420 
02421         enum CRStatus status = CR_PARSING_ERROR;
02422 
02423         g_return_val_if_fail (a_this && PRIVATE (a_this)
02424                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
02425 
02426         status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
02427                                        URI_TK, NO_ET, a_str, NULL);
02428         return status;
02429 }
02430 
02431 /**
02432  *Parses a string type as defined in css spec [4.1.1]:
02433  *
02434  *string ::= {string1}|{string2}
02435  *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
02436  *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
02437  *
02438  *@param a_this the current instance of #CRParser.
02439  *@param a_start out parameter. Upon successfull completion, 
02440  *points to the beginning of the string, points to an undefined value
02441  *otherwise.
02442  *@param a_end out parameter. Upon successfull completion, points to
02443  *the beginning of the string, points to an undefined value otherwise.
02444  *@return CR_OK upon successfull completion, an error code otherwise.
02445  */
02446 static enum CRStatus
02447 cr_parser_parse_string (CRParser * a_this, GString ** a_str)
02448 {
02449         enum CRStatus status = CR_OK;
02450 
02451         g_return_val_if_fail (a_this && PRIVATE (a_this)
02452                               && PRIVATE (a_this)->tknzr
02453                               && a_str, CR_BAD_PARAM_ERROR);
02454 
02455         status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
02456                                        STRING_TK, NO_ET, a_str, NULL);
02457         return status;
02458 }
02459 
02460 /**
02461  *Parses an "ident" as defined in css spec [4.1.1]:
02462  *ident ::= {nmstart}{nmchar}*
02463  *
02464  *@param a_this the currens instance of #CRParser.
02465  *
02466  *@param a_str a pointer to parsed ident. If *a_str is NULL,
02467  *this function allocates a new instance of GString. If not, 
02468  *the function just appends the parsed string to the one passed.
02469  *In both cases it is up to the caller to free *a_str.
02470  *
02471  *@return CR_OK upon successfull completion, an error code 
02472  *otherwise.
02473  */
02474 static enum CRStatus
02475 cr_parser_parse_ident (CRParser * a_this, GString ** a_str)
02476 {
02477         enum CRStatus status = CR_OK;
02478 
02479         g_return_val_if_fail (a_this && PRIVATE (a_this)
02480                               && PRIVATE (a_this)->tknzr
02481                               && a_str, CR_BAD_PARAM_ERROR);
02482 
02483         status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
02484                                        IDENT_TK, NO_ET, a_str, NULL);
02485         return status;
02486 }
02487 
02488 /**
02489  *Parses a stylesheet as defined in the css2 spec in appendix D.1:
02490  *stylesheet ::= [ CHARSET_SYM S* STRING S* ';' ]? 
02491  *               [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
02492  *               [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]*
02493  *
02494  *TODO: Finish the code of this function. Think about splitting it into
02495  *smaller functions.
02496  *
02497  *@param a_this the "this pointer" of the current instance of #CRParser.
02498  *@param a_start out parameter. A pointer to the first character of
02499  *the successfully parsed string.
02500  *@param a_end out parameter. A pointer to the first character of
02501  *the successfully parsed string.
02502  *
02503  *@return CR_OK upon successfull completion, an error code otherwise.
02504  */
02505 static enum CRStatus
02506 cr_parser_parse_stylesheet (CRParser * a_this)
02507 {
02508         enum CRStatus status = CR_OK;
02509         CRInputPos init_pos;
02510         CRToken *token = NULL;
02511         GString *charset = NULL;
02512 
02513         g_return_val_if_fail (a_this && PRIVATE (a_this)
02514                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
02515 
02516         RECORD_INITIAL_POS (a_this, &init_pos);
02517 
02518         PRIVATE (a_this)->state = READY_STATE;
02519 
02520         if (PRIVATE (a_this)->sac_handler
02521             && PRIVATE (a_this)->sac_handler->start_document) {
02522                 PRIVATE (a_this)->sac_handler->start_document
02523                         (PRIVATE (a_this)->sac_handler);
02524         }
02525 
02526 /* parse_charset:*/
02527         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
02528 
02529         if (status == CR_END_OF_INPUT_ERROR)
02530                 goto done;
02531         CHECK_PARSING_STATUS (status, TRUE);
02532 
02533         if (token && token->type == CHARSET_SYM_TK) {
02534                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
02535                                                token);
02536                 CHECK_PARSING_STATUS (status, TRUE);
02537                 token = NULL;
02538 
02539                 status = cr_parser_parse_charset (a_this, &charset);
02540 
02541                 if (status == CR_OK && charset) {
02542                         if (PRIVATE (a_this)->sac_handler
02543                             && PRIVATE (a_this)->sac_handler->charset) {
02544                                 PRIVATE (a_this)->sac_handler->charset
02545                                         (PRIVATE (a_this)->sac_handler,
02546                                          charset);
02547                         }
02548                 } else if (status != CR_END_OF_INPUT_ERROR) {
02549                         status = cr_parser_parse_atrule_core (a_this);
02550                         CHECK_PARSING_STATUS (status, FALSE);
02551                 }
02552 
02553                 if (charset) {
02554                         g_string_free (charset, TRUE);
02555                         charset = NULL;
02556                 }
02557         } else if (token
02558                    && (token->type == S_TK || token->type == COMMENT_TK)) {
02559                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
02560                                                token);
02561                 token = NULL;
02562                 CHECK_PARSING_STATUS (status, TRUE);
02563 
02564                 cr_parser_try_to_skip_spaces_and_comments (a_this);
02565         } else if (token) {
02566                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
02567                                                token);
02568                 token = NULL;
02569                 CHECK_PARSING_STATUS (status, TRUE);
02570         }
02571 
02572 /* parse_imports:*/
02573         do {
02574                 if (token) {
02575                         cr_token_destroy (token);
02576                         token = NULL;
02577                 }
02578                 status = cr_tknzr_get_next_token
02579                         (PRIVATE (a_this)->tknzr, &token);
02580 
02581                 if (status == CR_END_OF_INPUT_ERROR)
02582                         goto done;
02583                 CHECK_PARSING_STATUS (status, TRUE);
02584         } while (token
02585                  && (token->type == S_TK
02586                      || token->type == CDO_TK || token->type == CDC_TK));
02587 
02588         if (token) {
02589                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
02590                                                token);
02591                 token = NULL;
02592         }
02593 
02594         for (;;) {
02595                 status = cr_tknzr_get_next_token
02596                         (PRIVATE (a_this)->tknzr, &token);
02597                 if (status == CR_END_OF_INPUT_ERROR)
02598                         goto done;
02599                 CHECK_PARSING_STATUS (status, TRUE);
02600 
02601                 if (token && token->type == IMPORT_SYM_TK) {
02602                         GList *media_list = NULL;
02603                         GString *import_string = NULL;
02604 
02605                         status = cr_tknzr_unget_token
02606                                 (PRIVATE (a_this)->tknzr, token);
02607                         token = NULL;
02608                         CHECK_PARSING_STATUS (status, TRUE);
02609 
02610                         status = cr_parser_parse_import (a_this,
02611                                                          &media_list,
02612                                                          &import_string);
02613 
02614                         if (status == CR_OK) {
02615                                 if (import_string
02616                                     && PRIVATE (a_this)->sac_handler
02617                                     && PRIVATE (a_this)->sac_handler->
02618                                     import_style) {
02619                                         PRIVATE (a_this)->sac_handler->
02620                                                 import_style (PRIVATE
02621                                                               (a_this)->
02622                                                               sac_handler,
02623                                                               media_list,
02624                                                               import_string,
02625                                                               NULL);
02626 
02627                                         if ((PRIVATE (a_this)->sac_handler->
02628                                              resolve_import == TRUE)) {
02629                                                 /*
02630                                                  *TODO: resolve the
02631                                                  *import rule.
02632                                                  */
02633                                         }
02634 
02635                                         if ((PRIVATE (a_this)->sac_handler->
02636                                              import_style_result)) {
02637                                                 PRIVATE (a_this)->
02638                                                         sac_handler->
02639                                                         import_style_result
02640                                                         (PRIVATE (a_this)->
02641                                                          sac_handler,
02642                                                          media_list,
02643                                                          import_string,
02644                                                          NULL, NULL);
02645                                         }
02646                                 }
02647                         } else if (status != CR_END_OF_INPUT_ERROR) {
02648                                 if (PRIVATE (a_this)->sac_handler
02649                                     && PRIVATE (a_this)->sac_handler->error) {
02650                                         PRIVATE (a_this)->sac_handler->
02651                                                 error
02652                                                 (PRIVATE (a_this)->
02653                                                  sac_handler);
02654                                 }
02655                                 status = cr_parser_parse_atrule_core (a_this);
02656                                 CHECK_PARSING_STATUS (status, TRUE) ;
02657                         } else {
02658                                 goto error ;
02659                         }
02660 
02661                         /*
02662                          *then, after calling the appropriate 
02663                          *SAC handler, free
02664                          *the media_list and import_string.
02665                          */
02666                         if (media_list) {
02667                                 GList *cur = NULL;
02668 
02669                                 /*free the medium list */
02670                                 for (cur = media_list; cur; cur = cur->next) {
02671                                         if (cur->data) {
02672                                                 g_string_free (cur->data,
02673                                                                TRUE);
02674                                         }
02675                                 }
02676 
02677                                 g_list_free (media_list);
02678                                 media_list = NULL;
02679                         }
02680 
02681                         if (import_string) {
02682                                 g_string_free (import_string, TRUE);
02683                                 import_string = NULL;
02684                         }
02685 
02686                         cr_parser_try_to_skip_spaces_and_comments (a_this);
02687                 } else if (token
02688                            && (token->type == S_TK
02689                                || token->type == CDO_TK
02690                                || token->type == CDC_TK)) {
02691                         status = cr_tknzr_unget_token
02692                                 (PRIVATE (a_this)->tknzr, token);
02693                         token = NULL;
02694 
02695                         do {
02696                                 if (token) {
02697                                         cr_token_destroy (token);
02698                                         token = NULL;
02699                                 }
02700 
02701                                 status = cr_tknzr_get_next_token
02702                                         (PRIVATE (a_this)->tknzr, &token);
02703 
02704                                 if (status == CR_END_OF_INPUT_ERROR)
02705                                         goto done;
02706                                 CHECK_PARSING_STATUS (status, TRUE);
02707                         } while (token
02708                                  && (token->type == S_TK
02709                                      || token->type == CDO_TK
02710                                      || token->type == CDC_TK));
02711                 } else {
02712                         if (token) {
02713                                 status = cr_tknzr_unget_token
02714                                         (PRIVATE (a_this)->tknzr, token);
02715                                 token = NULL;
02716                         }
02717                         goto parse_ruleset_and_others;
02718                 }
02719         }
02720 
02721       parse_ruleset_and_others:
02722 
02723         cr_parser_try_to_skip_spaces_and_comments (a_this);
02724 
02725         for (;;) {
02726                 status = cr_tknzr_get_next_token
02727                         (PRIVATE (a_this)->tknzr, &token);
02728                 if (status == CR_END_OF_INPUT_ERROR)
02729                         goto done;
02730                 CHECK_PARSING_STATUS (status, TRUE);
02731 
02732                 if (token
02733                     && (token->type == S_TK
02734                         || token->type == CDO_TK || token->type == CDC_TK)) {
02735                         status = cr_tknzr_unget_token
02736                                 (PRIVATE (a_this)->tknzr, token);
02737                         token = NULL;
02738 
02739                         do {
02740                                 if (token) {
02741                                         cr_token_destroy (token);
02742                                         token = NULL;
02743                                 }
02744 
02745                                 cr_parser_try_to_skip_spaces_and_comments
02746                                         (a_this);
02747                                 status = cr_tknzr_get_next_token
02748                                         (PRIVATE (a_this)->tknzr, &token);
02749                         } while (token
02750                                  && (token->type == S_TK
02751                                      || token->type == COMMENT_TK
02752                                      || token->type == CDO_TK
02753                                      || token->type == CDC_TK));
02754                         if (token) {
02755                                 cr_tknzr_unget_token
02756                                         (PRIVATE (a_this)->tknzr, token);
02757                                 token = NULL;
02758                         }
02759                 } else if (token
02760                            && (token->type == HASH_TK
02761                                || (token->type == DELIM_TK
02762                                    && token->u.unichar == '.')
02763                                || (token->type == DELIM_TK
02764                                    && token->u.unichar == ':')
02765                                || (token->type == DELIM_TK
02766                                    && token->u.unichar == '*')
02767                                || (token->type == BO_TK)
02768                                || token->type == IDENT_TK)) {
02769                         /*
02770                          *Try to parse a CSS2 ruleset.
02771                          *if the parsing fails, try to parse
02772                          *a css core ruleset.
02773                          */
02774                         status = cr_tknzr_unget_token
02775                                 (PRIVATE (a_this)->tknzr, token);
02776                         CHECK_PARSING_STATUS (status, TRUE);
02777                         token = NULL;
02778 
02779                         status = cr_parser_parse_ruleset (a_this);
02780 
02781                         if (status == CR_OK) {
02782                                 continue;
02783                         } else {
02784                                 if (PRIVATE (a_this)->sac_handler
02785                                     && PRIVATE (a_this)->sac_handler->error) {
02786                                         PRIVATE (a_this)->sac_handler->
02787                                                 error
02788                                                 (PRIVATE (a_this)->
02789                                                  sac_handler);
02790                                 }
02791 
02792                                 status = cr_parser_parse_ruleset_core
02793                                         (a_this);
02794 
02795                                 if (status == CR_OK) {
02796                                         continue;
02797                                 } else {
02798                                         break;
02799                                 }
02800                         }
02801                 } else if (token && token->type == MEDIA_SYM_TK) {
02802                         status = cr_tknzr_unget_token
02803                                 (PRIVATE (a_this)->tknzr, token);
02804                         CHECK_PARSING_STATUS (status, TRUE);
02805                         token = NULL;
02806 
02807                         status = cr_parser_parse_media (a_this);
02808                         if (status == CR_OK) {
02809                                 continue;
02810                         } else {
02811                                 if (PRIVATE (a_this)->sac_handler
02812                                     && PRIVATE (a_this)->sac_handler->error) {
02813                                         PRIVATE (a_this)->sac_handler->
02814                                                 error
02815                                                 (PRIVATE (a_this)->
02816                                                  sac_handler);
02817                                 }
02818 
02819                                 status = cr_parser_parse_atrule_core (a_this);
02820 
02821                                 if (status == CR_OK) {
02822                                         continue;
02823                                 } else {
02824                                         break;
02825                                 }
02826                         }
02827 
02828                 } else if (token && token->type == PAGE_SYM_TK) {
02829                         status = cr_tknzr_unget_token
02830                                 (PRIVATE (a_this)->tknzr, token);
02831                         CHECK_PARSING_STATUS (status, TRUE);
02832                         token = NULL;
02833                         status = cr_parser_parse_page (a_this);
02834 
02835                         if (status == CR_OK) {
02836                                 continue;
02837                         } else {
02838                                 if (PRIVATE (a_this)->sac_handler
02839                                     && PRIVATE (a_this)->sac_handler->error) {
02840                                         PRIVATE (a_this)->sac_handler->
02841                                                 error
02842                                                 (PRIVATE (a_this)->
02843                                                  sac_handler);
02844                                 }
02845 
02846                                 status = cr_parser_parse_atrule_core (a_this);
02847 
02848                                 if (status == CR_OK) {
02849                                         continue;
02850                                 } else {
02851                                         break;
02852                                 }
02853                         }
02854                 } else if (token && token->type == FONT_FACE_SYM_TK) {
02855                         status = cr_tknzr_unget_token
02856                                 (PRIVATE (a_this)->tknzr, token);
02857                         CHECK_PARSING_STATUS (status, TRUE);
02858                         token = NULL;
02859                         status = cr_parser_parse_font_face (a_this);
02860 
02861                         if (status == CR_OK) {
02862                                 continue;
02863                         } else {
02864                                 if (PRIVATE (a_this)->sac_handler
02865                                     && PRIVATE (a_this)->sac_handler->error) {
02866                                         PRIVATE (a_this)->sac_handler->
02867                                                 error
02868                                                 (PRIVATE (a_this)->
02869                                                  sac_handler);
02870                                 }
02871 
02872                                 status = cr_parser_parse_atrule_core (a_this);
02873 
02874                                 if (status == CR_OK) {
02875                                         continue;
02876                                 } else {
02877                                         break;
02878                                 }
02879                         }
02880                 } else {
02881                         status = cr_tknzr_unget_token
02882                                 (PRIVATE (a_this)->tknzr, token);
02883                         CHECK_PARSING_STATUS (status, TRUE);
02884                         token = NULL;
02885                         status = cr_parser_parse_statement_core (a_this);
02886 
02887                         if (status == CR_OK) {
02888                                 continue;
02889                         } else {
02890                                 break;
02891                         }
02892                 }
02893         }
02894 
02895       done:
02896         if (token) {
02897                 cr_token_destroy (token);
02898                 token = NULL;
02899         }
02900 
02901         if (status == CR_END_OF_INPUT_ERROR || status == CR_OK) {
02902 
02903                 if (PRIVATE (a_this)->sac_handler
02904                     && PRIVATE (a_this)->sac_handler->end_document) {
02905                         PRIVATE (a_this)->sac_handler->end_document
02906                                 (PRIVATE (a_this)->sac_handler);
02907                 }
02908 
02909                 return CR_OK;
02910         }
02911 
02912         cr_parser_push_error
02913                 (a_this, "could not recognize next production", CR_ERROR);
02914 
02915         if (PRIVATE (a_this)->sac_handler
02916             && PRIVATE (a_this)->sac_handler->unrecoverable_error) {
02917                 PRIVATE (a_this)->sac_handler->
02918                         unrecoverable_error (PRIVATE (a_this)->sac_handler);
02919         }
02920 
02921         cr_parser_dump_err_stack (a_this, TRUE);
02922 
02923         return status;
02924 
02925       error:
02926 
02927         if (token) {
02928                 cr_token_destroy (token);
02929                 token = NULL;
02930         }
02931 
02932         if (PRIVATE (a_this)->sac_handler
02933             && PRIVATE (a_this)->sac_handler->unrecoverable_error) {
02934                 PRIVATE (a_this)->sac_handler->
02935                         unrecoverable_error (PRIVATE (a_this)->sac_handler);
02936         }
02937 
02938         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
02939 
02940         return status;
02941 }
02942 
02943 /****************************************
02944  *Public CRParser Methods
02945  ****************************************/
02946 
02947 /**
02948  *Creates a new parser to parse data
02949  *coming the input stream given in parameter.
02950  *@param a_input the input stream of the parser.
02951  *Note that the newly created parser will ref
02952  *a_input and unref it when parsing reaches the
02953  *end of the input stream.
02954  *@return the newly created instance of #CRParser,
02955  *or NULL if an error occured.
02956  */
02957 CRParser *
02958 cr_parser_new (CRTknzr * a_tknzr)
02959 {
02960         CRParser *result = NULL;
02961         enum CRStatus status = CR_OK;
02962 
02963         result = g_malloc0 (sizeof (CRParser));
02964 
02965         PRIVATE (result) = g_malloc0 (sizeof (CRParserPriv));
02966 
02967         if (a_tknzr) {
02968                 status = cr_parser_set_tknzr (result, a_tknzr);
02969         }
02970 
02971         g_return_val_if_fail (status == CR_OK, NULL);
02972 
02973         return result;
02974 }
02975 
02976 /**
02977  *Instanciates a new parser from a memory buffer.
02978  *@param a_buf the buffer to parse.
02979  *@param a_len the length of the data in the buffer.
02980  *@param a_enc the encoding of the input buffer a_buf.
02981  *@param a_free_buf if set to TRUE, a_buf will be freed
02982  *during the destruction of the newly built instance 
02983  *of #CRParser. If set to FALSE, it is up to the caller to
02984  *eventually free it.
02985  *@return the newly built parser, or NULL if an error arises.
02986  */
02987 CRParser *
02988 cr_parser_new_from_buf (guchar * a_buf,
02989                         gulong a_len,
02990                         enum CREncoding a_enc, 
02991                         gboolean a_free_buf)
02992 {
02993         CRParser *result = NULL;
02994         CRInput *input = NULL;
02995 
02996         g_return_val_if_fail (a_buf && a_len, NULL);
02997 
02998         input = cr_input_new_from_buf (a_buf, a_len, a_enc, a_free_buf);
02999         g_return_val_if_fail (input, NULL);
03000 
03001         result = cr_parser_new_from_input (input);
03002         if (!result) {
03003                 cr_input_destroy (input);
03004                 input = NULL;
03005                 return NULL;
03006         }
03007         return result;
03008 }
03009 
03010 CRParser *
03011 cr_parser_new_from_input (CRInput * a_input)
03012 {
03013         CRParser *result = NULL;
03014         CRTknzr *tokenizer = NULL;
03015 
03016         if (a_input) {
03017                 tokenizer = cr_tknzr_new (a_input);
03018                 g_return_val_if_fail (tokenizer, NULL);
03019         }
03020 
03021         result = cr_parser_new (tokenizer);
03022         g_return_val_if_fail (result, NULL);
03023 
03024         return result;
03025 }
03026 
03027 CRParser *
03028 cr_parser_new_from_file (const guchar * a_file_uri, enum CREncoding a_enc)
03029 {
03030         CRParser *result = NULL;
03031         CRTknzr *tokenizer = NULL;
03032 
03033         tokenizer = cr_tknzr_new_from_uri (a_file_uri, a_enc);
03034         if (!tokenizer) {
03035                 cr_utils_trace_info ("Could not open input file");
03036                 return NULL;
03037         }
03038 
03039         result = cr_parser_new (tokenizer);
03040         g_return_val_if_fail (result, NULL);
03041         return result;
03042 }
03043 
03044 /**
03045  *Sets a SAC document handler to the parser.
03046  *@param a_this the "this pointer" of the current instance of #CRParser.
03047  *@param a_handler the handler to set.
03048  *@return CR_OK upon successfull completion, an error code otherwise.
03049  */
03050 enum CRStatus
03051 cr_parser_set_sac_handler (CRParser * a_this, CRDocHandler * a_handler)
03052 {
03053         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
03054 
03055         if (PRIVATE (a_this)->sac_handler) {
03056                 cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
03057         }
03058 
03059         PRIVATE (a_this)->sac_handler = a_handler;
03060         cr_doc_handler_ref (a_handler);
03061 
03062         return CR_OK;
03063 }
03064 
03065 /**
03066  *Gets the SAC document handler.
03067  *@param a_this the "this pointer" of the current instance of
03068  *#CRParser.
03069  *@param a_handler out parameter. The returned handler.
03070  *@return CR_OK upon successfull completion, an error code
03071  *otherwise.
03072  */
03073 enum CRStatus
03074 cr_parser_get_sac_handler (CRParser * a_this, CRDocHandler ** a_handler)
03075 {
03076         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
03077 
03078         *a_handler = PRIVATE (a_this)->sac_handler;
03079 
03080         return CR_OK;
03081 }
03082 
03083 /**
03084  *Sets the SAC handler associated to the current instance
03085  *of #CRParser to the default SAC handler.
03086  *@param a_this a pointer to the current instance of #CRParser.
03087  *@return CR_OK upon successfull completion, an error code otherwise.
03088  */
03089 enum CRStatus
03090 cr_parser_set_default_sac_handler (CRParser * a_this)
03091 {
03092         CRDocHandler *default_sac_handler = NULL;
03093         enum CRStatus status = CR_ERROR;
03094 
03095         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
03096 
03097         default_sac_handler = cr_doc_handler_new ();
03098 
03099         cr_doc_handler_set_default_sac_handler (default_sac_handler);
03100 
03101         status = cr_parser_set_sac_handler (a_this, default_sac_handler);
03102 
03103         if (status != CR_OK) {
03104                 cr_doc_handler_destroy (default_sac_handler);
03105                 default_sac_handler = NULL;
03106         }
03107 
03108         return status;
03109 }
03110 
03111 enum CRStatus
03112 cr_parser_set_use_core_grammar (CRParser * a_this,
03113                                 gboolean a_use_core_grammar)
03114 {
03115         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
03116 
03117         PRIVATE (a_this)->use_core_grammar = a_use_core_grammar;
03118 
03119         return CR_OK;
03120 }
03121 
03122 enum CRStatus
03123 cr_parser_get_use_core_grammar (CRParser * a_this,
03124                                 gboolean * a_use_core_grammar)
03125 {
03126         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
03127 
03128         *a_use_core_grammar = PRIVATE (a_this)->use_core_grammar;
03129 
03130         return CR_OK;
03131 }
03132 
03133 /**
03134  *Parses a the given in parameter.
03135  *@param a_this a pointer to the current instance of #CRParser.
03136  *@param a_file_uri the uri to the file to load. For the time being,
03137  *only local files are supported.
03138  *@return CR_OK upon successfull completion, an error code otherwise.
03139  */
03140 enum CRStatus
03141 cr_parser_parse_file (CRParser * a_this,
03142                       const guchar * a_file_uri, enum CREncoding a_enc)
03143 {
03144         enum CRStatus status = CR_ERROR;
03145         CRTknzr *tknzr = NULL;
03146 
03147         g_return_val_if_fail (a_this && PRIVATE (a_this)
03148                               && a_file_uri, CR_BAD_PARAM_ERROR);
03149 
03150         tknzr = cr_tknzr_new_from_uri (a_file_uri, a_enc);
03151 
03152         g_return_val_if_fail (tknzr != NULL, CR_ERROR);
03153 
03154         status = cr_parser_set_tknzr (a_this, tknzr);
03155         g_return_val_if_fail (status == CR_OK, CR_ERROR);
03156 
03157         status = cr_parser_parse (a_this);
03158 
03159         return status;
03160 }
03161 
03162 /**
03163  *Parses an expression as defined by the css2 spec in appendix
03164  *D.1:
03165  *expr: term [ operator term ]*
03166  */
03167 enum CRStatus
03168 cr_parser_parse_expr (CRParser * a_this, CRTerm ** a_expr)
03169 {
03170         enum CRStatus status = CR_ERROR;
03171         CRInputPos init_pos;
03172         CRTerm *expr = NULL,
03173                 *expr2 = NULL;
03174         guchar next_byte = 0;
03175         gulong nb_terms = 0;
03176 
03177         g_return_val_if_fail (a_this && PRIVATE (a_this)
03178                               && a_expr, CR_BAD_PARAM_ERROR);
03179 
03180         RECORD_INITIAL_POS (a_this, &init_pos);
03181 
03182         status = cr_parser_parse_term (a_this, &expr);
03183 
03184         CHECK_PARSING_STATUS (status, FALSE);
03185 
03186         for (;;) {
03187                 guchar operator = 0;
03188 
03189                 status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr,
03190                                              1, &next_byte);
03191                 if (status != CR_OK) {
03192                         if (status == CR_END_OF_INPUT_ERROR) {
03193                                 /*
03194                                    if (!nb_terms)
03195                                    {
03196                                    goto error ;
03197                                    }
03198                                  */
03199                                 status = CR_OK;
03200                                 break;
03201                         } else {
03202                                 goto error;
03203                         }
03204                 }
03205 
03206                 if (next_byte == '/' || next_byte == ',') {
03207                         READ_NEXT_BYTE (a_this, &operator);
03208                 }
03209 
03210                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03211 
03212                 status = cr_parser_parse_term (a_this, &expr2);
03213 
03214                 if (status != CR_OK || expr2 == NULL) {
03215                         status = CR_OK;
03216                         break;
03217                 }
03218 
03219                 switch (operator) {
03220                 case '/':
03221                         expr2->the_operator = DIVIDE;
03222                         break;
03223                 case ',':
03224                         expr2->the_operator = COMMA;
03225 
03226                 default:
03227                         break;
03228                 }
03229 
03230                 expr = cr_term_append_term (expr, expr2);
03231                 expr2 = NULL;
03232                 operator = 0;
03233                 nb_terms++;
03234         }
03235 
03236         if (status == CR_OK) {
03237                 *a_expr = cr_term_append_term (*a_expr, expr);
03238                 expr = NULL;
03239 
03240                 cr_parser_clear_errors (a_this);
03241                 return CR_OK;
03242         }
03243 
03244       error:
03245 
03246         if (expr) {
03247                 cr_term_destroy (expr);
03248                 expr = NULL;
03249         }
03250 
03251         if (expr2) {
03252                 cr_term_destroy (expr2);
03253                 expr2 = NULL;
03254         }
03255 
03256         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
03257 
03258         return status;
03259 }
03260 
03261 /**
03262  *Parses a declaration priority as defined by
03263  *the css2 grammar in appendix C:
03264  *prio: IMPORTANT_SYM S*
03265  *@param a_this the current instance of #CRParser.
03266  *@param a_prio a string representing the priority.
03267  *Today, only "!important" is returned as only this
03268  *priority is defined by css2.
03269  */
03270 enum CRStatus
03271 cr_parser_parse_prio (CRParser * a_this, GString ** a_prio)
03272 {
03273         enum CRStatus status = CR_ERROR;
03274         CRInputPos init_pos;
03275         CRToken *token = NULL;
03276 
03277         g_return_val_if_fail (a_this && PRIVATE (a_this)
03278                               && a_prio
03279                               && *a_prio == NULL, CR_BAD_PARAM_ERROR);
03280 
03281         RECORD_INITIAL_POS (a_this, &init_pos);
03282 
03283         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
03284         if (status == CR_END_OF_INPUT_ERROR) {
03285                 goto error;
03286         }
03287         ENSURE_PARSING_COND (status == CR_OK
03288                              && token && token->type == IMPORTANT_SYM_TK);
03289 
03290         cr_parser_try_to_skip_spaces_and_comments (a_this);
03291         *a_prio = g_string_new ("!important");
03292         cr_token_destroy (token);
03293         token = NULL;
03294         return CR_OK;
03295 
03296       error:
03297         if (token) {
03298                 cr_token_destroy (token);
03299                 token = NULL;
03300         }
03301         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
03302 
03303         return status;
03304 }
03305 
03306 /**
03307  *TODO: return the parsed priority, so that
03308  *upper layers can take benefit from it.
03309  *Parses a "declaration" as defined by the css2 spec in appendix D.1:
03310  *declaration ::= [property ':' S* expr prio?]?
03311  *
03312  *@param a_this the "this pointer" of the current instance of #CRParser.
03313  *@param a_property the successfully parsed property. The caller
03314  * *must* free the returned pointer.
03315  *@param a_expr the expression that represents the attribute value.
03316  *The caller *must* free the returned pointer.
03317  *@return CR_OK upon successfull completion, an error code otherwise.
03318  */
03319 enum CRStatus
03320 cr_parser_parse_declaration (CRParser * a_this,
03321                              GString ** a_property,
03322                              CRTerm ** a_expr, gboolean * a_important)
03323 {
03324         enum CRStatus status = CR_ERROR;
03325         CRInputPos init_pos;
03326         guint32 cur_char = 0;
03327         CRTerm *expr = NULL;
03328         GString *prio = NULL;
03329 
03330         g_return_val_if_fail (a_this && PRIVATE (a_this)
03331                               && a_property && a_expr
03332                               && a_important, CR_BAD_PARAM_ERROR);
03333 
03334         RECORD_INITIAL_POS (a_this, &init_pos);
03335 
03336         status = cr_parser_parse_property (a_this, a_property);
03337 
03338         if (status == CR_END_OF_INPUT_ERROR)
03339                 goto error;
03340 
03341         CHECK_PARSING_STATUS_ERR
03342                 (a_this, status, FALSE,
03343                  "while parsing declaration: next property is malformed",
03344                  CR_SYNTAX_ERROR);
03345 
03346         READ_NEXT_CHAR (a_this, &cur_char);
03347 
03348         if (cur_char != ':') {
03349                 status = CR_PARSING_ERROR;
03350                 cr_parser_push_error
03351                         (a_this,
03352                          "while parsing declaration: this char must be ':'",
03353                          CR_SYNTAX_ERROR);
03354                 goto error;
03355         }
03356 
03357         cr_parser_try_to_skip_spaces_and_comments (a_this);
03358 
03359         status = cr_parser_parse_expr (a_this, &expr);
03360 
03361         CHECK_PARSING_STATUS_ERR
03362                 (a_this, status, FALSE,
03363                  "while parsing declaration: next expression is malformed",
03364                  CR_SYNTAX_ERROR);
03365 
03366         cr_parser_try_to_skip_spaces_and_comments (a_this);
03367         status = cr_parser_parse_prio (a_this, &prio);
03368         if (prio) {
03369                 g_string_free (prio, TRUE);
03370                 prio = NULL;
03371                 *a_important = TRUE;
03372         } else {
03373                 *a_important = FALSE;
03374         }
03375         if (*a_expr) {
03376                 cr_term_append_term (*a_expr, expr);
03377                 expr = NULL;
03378         } else {
03379                 *a_expr = expr;
03380                 expr = NULL;
03381         }
03382 
03383         cr_parser_clear_errors (a_this);
03384         return CR_OK;
03385 
03386       error:
03387 
03388         if (expr) {
03389                 cr_term_destroy (expr);
03390                 expr = NULL;
03391         }
03392 
03393         if (*a_property) {
03394                 g_string_free (*a_property, TRUE);
03395                 *a_property = NULL;
03396         }
03397 
03398         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
03399 
03400         return status;
03401 }
03402 
03403 /**
03404  *Parses a statement as defined by the css core grammar in
03405  *chapter 4.1 of the css2 spec.
03406  *statement   : ruleset | at-rule;
03407  *@param a_this the current instance of #CRParser.
03408  *@return CR_OK upon successfull completion, an error code otherwise.
03409  */
03410 enum CRStatus
03411 cr_parser_parse_statement_core (CRParser * a_this)
03412 {
03413         CRToken *token = NULL;
03414         CRInputPos init_pos;
03415         enum CRStatus status = CR_ERROR;
03416 
03417         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
03418 
03419         RECORD_INITIAL_POS (a_this, &init_pos);
03420 
03421         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
03422 
03423         ENSURE_PARSING_COND (status == CR_OK && token);
03424 
03425         switch (token->type) {
03426         case ATKEYWORD_TK:
03427         case IMPORT_SYM_TK:
03428         case PAGE_SYM_TK:
03429         case MEDIA_SYM_TK:
03430         case FONT_FACE_SYM_TK:
03431         case CHARSET_SYM_TK:
03432                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
03433                 token = NULL;
03434                 status = cr_parser_parse_atrule_core (a_this);
03435                 CHECK_PARSING_STATUS (status, TRUE);
03436                 break;
03437 
03438         default:
03439                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
03440                 token = NULL;
03441                 status = cr_parser_parse_ruleset_core (a_this);
03442                 cr_parser_clear_errors (a_this);
03443                 CHECK_PARSING_STATUS (status, TRUE);
03444         }
03445 
03446         return CR_OK;
03447 
03448       error:
03449         if (token) {
03450                 cr_token_destroy (token);
03451                 token = NULL;
03452         }
03453 
03454         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
03455 
03456         return status;
03457 }
03458 
03459 /**
03460  *Parses a "ruleset" as defined in the css2 spec at appendix D.1.
03461  *ruleset ::= selector [ ',' S* selector ]* 
03462  *'{' S* declaration? [ ';' S* declaration? ]* '}' S*;
03463  *
03464  *This methods calls the the SAC handler on the relevant SAC handler
03465  *callbacks whenever it encounters some specific constructions.
03466  *See the documentation of #CRDocHandler (the SAC handler) to know
03467  *when which SAC handler is called.
03468  *@param a_this the "this pointer" of the current instance of #CRParser.
03469  *@return CR_OK upon successfull completion, an error code otherwise.
03470  */
03471 enum CRStatus
03472 cr_parser_parse_ruleset (CRParser * a_this)
03473 {
03474         enum CRStatus status = CR_OK;
03475         CRInputPos init_pos;
03476         guint32 cur_char = 0,
03477                 next_char = 0;
03478         GString *property = NULL;
03479         CRTerm *expr = NULL;
03480         CRSimpleSel *simple_sels = NULL;
03481         CRSelector *selector = NULL;
03482         gboolean start_selector = FALSE,
03483                 is_important = FALSE;
03484 
03485         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
03486 
03487         RECORD_INITIAL_POS (a_this, &init_pos);
03488 
03489         status = cr_parser_parse_selector (a_this, &selector);
03490         CHECK_PARSING_STATUS (status, FALSE);
03491 
03492         READ_NEXT_CHAR (a_this, &cur_char);
03493 
03494         ENSURE_PARSING_COND_ERR
03495                 (a_this, cur_char == '{',
03496                  "while parsing rulset: current char should be '{'",
03497                  CR_SYNTAX_ERROR);
03498 
03499         if (PRIVATE (a_this)->sac_handler
03500             && PRIVATE (a_this)->sac_handler->start_selector) {
03501                 /*
03502                  *the selector if ref counted so that the parser's user
03503                  *can choose to keep it.
03504                  */
03505                 if (selector) {
03506                         cr_selector_ref (selector);
03507                 }
03508 
03509                 PRIVATE (a_this)->sac_handler->start_selector
03510                         (PRIVATE (a_this)->sac_handler, selector);
03511                 start_selector = TRUE;
03512         }
03513 
03514         cr_parser_try_to_skip_spaces_and_comments (a_this);
03515 
03516         PRIVATE (a_this)->state = TRY_PARSE_RULESET_STATE;
03517 
03518         status = cr_parser_parse_declaration (a_this, &property,
03519                                               &expr, &is_important);        
03520         if (expr) {
03521                 cr_term_ref (expr);
03522         }
03523         if (status == CR_OK
03524             && PRIVATE (a_this)->sac_handler
03525             && PRIVATE (a_this)->sac_handler->property) {
03526                 PRIVATE (a_this)->sac_handler->property
03527                         (PRIVATE (a_this)->sac_handler, property, expr,
03528                          is_important);
03529         }        
03530         if (status == CR_OK) {
03531                 /*
03532                  *free the allocated
03533                  *'property' and 'term' before parsing
03534                  *next declarations.
03535                  */
03536                 if (property) {
03537                         g_string_free (property, TRUE);
03538                         property = NULL;
03539                 }
03540                 if (expr) {
03541                         cr_term_unref (expr);
03542                         expr = NULL;
03543                 }
03544         } else {/*status != CR_OK*/                
03545                 guint32 c = 0 ;
03546                 /*
03547                  *test if we have reached '}', which
03548                  *would mean that we are parsing an empty ruleset (eg. x{ })
03549                  *In that case, goto end_of_ruleset.
03550                  */
03551                 status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, &c) ;
03552                 if (status == CR_OK && c == '}') {
03553                         status = CR_OK ;
03554                         goto end_of_ruleset ;
03555                 }
03556         }
03557         CHECK_PARSING_STATUS_ERR
03558                 (a_this, status, FALSE,
03559                  "while parsing ruleset: next construction should be a declaration",
03560                  CR_SYNTAX_ERROR);
03561 
03562         for (;;) {
03563                 PEEK_NEXT_CHAR (a_this, &next_char);
03564                 if (next_char != ';')
03565                         break;
03566 
03567                 /*consume the ';' char */
03568                 READ_NEXT_CHAR (a_this, &cur_char);
03569 
03570                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03571 
03572                 status = cr_parser_parse_declaration (a_this, &property,
03573                                                       &expr, &is_important);
03574 
03575                 if (expr) {
03576                         cr_term_ref (expr);
03577                 }
03578                 if (status == CR_OK
03579                     && PRIVATE (a_this)->sac_handler
03580                     && PRIVATE (a_this)->sac_handler->property) {
03581                         PRIVATE (a_this)->sac_handler->property
03582                                 (PRIVATE (a_this)->sac_handler,
03583                                  property, expr, is_important);
03584                 }
03585                 if (property) {
03586                         g_string_free (property, TRUE);
03587                         property = NULL;
03588                 }
03589                 if (expr) {
03590                         cr_term_unref (expr);
03591                         expr = NULL;
03592                 }
03593         }
03594 
03595  end_of_ruleset:
03596         cr_parser_try_to_skip_spaces_and_comments (a_this);
03597         READ_NEXT_CHAR (a_this, &cur_char);
03598         ENSURE_PARSING_COND_ERR
03599                 (a_this, cur_char == '}',
03600                  "while parsing rulset: current char must be a '}'",
03601                  CR_SYNTAX_ERROR);
03602 
03603         if (PRIVATE (a_this)->sac_handler
03604             && PRIVATE (a_this)->sac_handler->end_selector) {
03605                 PRIVATE (a_this)->sac_handler->end_selector
03606                         (PRIVATE (a_this)->sac_handler, selector);
03607                 start_selector = FALSE;
03608         }
03609 
03610         if (expr) {
03611                 cr_term_unref (expr);
03612                 expr = NULL;
03613         }
03614 
03615         if (simple_sels) {
03616                 cr_simple_sel_destroy (simple_sels);
03617                 simple_sels = NULL;
03618         }
03619 
03620         if (selector) {
03621                 cr_selector_unref (selector);
03622                 selector = NULL;
03623         }
03624 
03625         cr_parser_clear_errors (a_this);
03626         PRIVATE (a_this)->state = RULESET_PARSED_STATE;
03627 
03628         return CR_OK;
03629 
03630  error:
03631         if (start_selector == TRUE
03632             && PRIVATE (a_this)->sac_handler
03633             && PRIVATE (a_this)->sac_handler->error) {
03634                 PRIVATE (a_this)->sac_handler->error
03635                         (PRIVATE (a_this)->sac_handler);
03636         }
03637         if (expr) {
03638                 cr_term_unref (expr);
03639                 expr = NULL;
03640         }
03641         if (simple_sels) {
03642                 cr_simple_sel_destroy (simple_sels);
03643                 simple_sels = NULL;
03644         }
03645         if (property) {
03646                 g_string_free (property, TRUE);
03647         }
03648         if (selector) {
03649                 cr_selector_unref (selector);
03650                 selector = NULL;
03651         }
03652 
03653         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
03654 
03655         return status;
03656 }
03657 
03658 /**
03659  *Parses an 'import' declaration as defined in the css2 spec
03660  *in appendix D.1:
03661  *
03662  *import ::= 
03663  *@import [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S*
03664  *
03665  *@param a_this the "this pointer" of the current instance 
03666  *of #CRParser.
03667  *
03668  *@param a_medium_list out parameter. A linked list of 
03669  *GString (see the doc of glib-2). 
03670  *Each GString is a string that contains
03671  *a 'medium' declaration part of the successfully 
03672  *parsed 'import' declaration.
03673  *
03674  *@param a_import_string out parameter. 
03675  *A string that contains the 'import 
03676  *string". The import string can be either an uri (if it starts with
03677  *the substring "uri(") or a any other css2 string. Note that
03678  * *a_import_string must be initially set to NULL or else, this function
03679  *will return CR_BAD_PARAM_ERROR.
03680  *
03681  *@return CR_OK upon sucessfull completion, an error code otherwise.
03682  */
03683 enum CRStatus
03684 cr_parser_parse_import (CRParser * a_this,
03685                         GList ** a_media_list, GString ** a_import_string)
03686 {
03687         enum CRStatus status = CR_OK;
03688         CRInputPos init_pos;
03689         guint32 cur_char = 0,
03690                 next_char = 0;
03691         GString *medium = NULL;
03692 
03693         g_return_val_if_fail (a_this
03694                               && a_import_string
03695                               && (*a_import_string == NULL),
03696                               CR_BAD_PARAM_ERROR);
03697 
03698         RECORD_INITIAL_POS (a_this, &init_pos);
03699 
03700         if (BYTE (a_this, 1, NULL) == '@'
03701             && BYTE (a_this, 2, NULL) == 'i'
03702             && BYTE (a_this, 3, NULL) == 'm'
03703             && BYTE (a_this, 4, NULL) == 'p'
03704             && BYTE (a_this, 5, NULL) == 'o'
03705             && BYTE (a_this, 6, NULL) == 'r'
03706             && BYTE (a_this, 7, NULL) == 't') {
03707                 SKIP_CHARS (a_this, 7);
03708                 status = CR_OK;
03709         } else {
03710                 status = CR_PARSING_ERROR;
03711                 goto error;
03712         }
03713 
03714         cr_parser_try_to_skip_spaces_and_comments (a_this);
03715 
03716         PRIVATE (a_this)->state = TRY_PARSE_IMPORT_STATE;
03717 
03718         PEEK_NEXT_CHAR (a_this, &next_char);
03719 
03720         if (next_char == '"' || next_char == '\'') {
03721                 status = cr_parser_parse_string (a_this, a_import_string);
03722 
03723                 CHECK_PARSING_STATUS (status, FALSE);
03724         } else {
03725                 status = cr_parser_parse_uri (a_this, a_import_string);
03726 
03727                 CHECK_PARSING_STATUS (status, FALSE);
03728         }
03729 
03730         cr_parser_try_to_skip_spaces_and_comments (a_this);
03731 
03732         status = cr_parser_parse_ident (a_this, &medium);
03733 
03734         if (status == CR_OK && medium) {
03735                 *a_media_list = g_list_append (*a_media_list, medium);
03736                 medium = NULL;
03737         }
03738 
03739         cr_parser_try_to_skip_spaces_and_comments (a_this);
03740 
03741         for (; status == CR_OK;) {
03742                 if ((status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
03743                                                   &next_char)) != CR_OK) {
03744                         if (status == CR_END_OF_INPUT_ERROR) {
03745                                 status = CR_OK;
03746                                 goto okay;
03747                         }
03748                         goto error;
03749                 }
03750 
03751                 if (next_char == ',') {
03752                         READ_NEXT_CHAR (a_this, &cur_char);
03753                 } else {
03754                         break;
03755                 }
03756 
03757                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03758 
03759                 status = cr_parser_parse_ident (a_this, &medium);
03760 
03761                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03762 
03763                 if ((status == CR_OK) && medium) {
03764                         *a_media_list = g_list_append (*a_media_list, medium);
03765 
03766                         medium = NULL;
03767                 }
03768 
03769                 CHECK_PARSING_STATUS (status, FALSE);
03770                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03771         }
03772         cr_parser_try_to_skip_spaces_and_comments (a_this);
03773         READ_NEXT_CHAR (a_this, &cur_char);
03774         ENSURE_PARSING_COND (cur_char == ';');
03775         cr_parser_try_to_skip_spaces_and_comments (a_this);
03776       okay:
03777         cr_parser_clear_errors (a_this);
03778         PRIVATE (a_this)->state = IMPORT_PARSED_STATE;
03779 
03780         return CR_OK;
03781 
03782       error:
03783 
03784         if (*a_media_list) {
03785                 GList *cur = NULL;
03786 
03787                 /*
03788                  *free each element of *a_media_list.
03789                  *Note that each element of *a_medium list *must*
03790                  *be a GString* or else, the code that is coming next 
03791                  *will corrupt the memory and lead to hard to debug
03792                  *random crashes.
03793                  *This is where C++ and its compile time
03794                  *type checking mecanism (through STL containers) would
03795                  *have prevented us to go through this hassle.
03796                  */
03797                 for (cur = *a_media_list; cur; cur = cur->next) {
03798                         if (cur->data) {
03799                                 g_string_free (cur->data, TRUE);
03800                         }
03801                 }
03802 
03803                 g_list_free (*a_media_list);
03804                 *a_media_list = NULL;
03805         }
03806 
03807         if (*a_import_string) {
03808                 g_string_free (*a_import_string, TRUE);
03809                 *a_import_string = NULL;
03810         }
03811 
03812         if (medium) {
03813                 g_string_free (medium, TRUE);
03814                 medium = NULL;
03815         }
03816 
03817         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
03818 
03819         return status;
03820 }
03821 
03822 /**
03823  *Parses a 'media' declaration as specified in the css2 spec at
03824  *appendix D.1:
03825  *
03826  *media ::= @media S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S*
03827  *
03828  *Note that this function calls the required sac handlers during the parsing
03829  *to notify media productions. See #CRDocHandler to know the callback called
03830  *during @media parsing.
03831  *@param a_this the "this pointer" of the current instance of #CRParser.
03832  *@return CR_OK upon successfull completion, an error code otherwise.
03833  */
03834 enum CRStatus
03835 cr_parser_parse_media (CRParser * a_this)
03836 {
03837         enum CRStatus status = CR_OK;
03838         CRInputPos init_pos;
03839         CRToken *token = NULL;
03840         guint32 next_char = 0,
03841                 cur_char = 0;
03842         GString *medium = NULL;
03843         GList *media_list = NULL;
03844 
03845         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
03846 
03847         RECORD_INITIAL_POS (a_this, &init_pos);
03848 
03849         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
03850         ENSURE_PARSING_COND (status == CR_OK
03851                              && token && token->type == MEDIA_SYM_TK);
03852 
03853         cr_token_destroy (token);
03854         token = NULL;
03855 
03856         cr_parser_try_to_skip_spaces_and_comments (a_this);
03857 
03858         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
03859         ENSURE_PARSING_COND (status == CR_OK
03860                              && token && token->type == IDENT_TK);
03861 
03862         medium = token->u.str;
03863         token->u.str = NULL;
03864         cr_token_destroy (token);
03865         token = NULL;
03866 
03867         if (medium) {
03868                 media_list = g_list_append (media_list, medium);
03869                 medium = NULL;
03870         }
03871 
03872         for (; status == CR_OK;) {
03873                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03874                 PEEK_NEXT_CHAR (a_this, &next_char);
03875 
03876                 if (next_char == ',') {
03877                         READ_NEXT_CHAR (a_this, &cur_char);
03878                 } else {
03879                         break;
03880                 }
03881 
03882                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03883 
03884                 status = cr_parser_parse_ident (a_this, &medium);
03885 
03886                 CHECK_PARSING_STATUS (status, FALSE);
03887 
03888                 if (medium) {
03889                         media_list = g_list_append (media_list, medium);
03890                         medium = NULL;
03891                 }
03892         }
03893 
03894         READ_NEXT_CHAR (a_this, &cur_char);
03895 
03896         ENSURE_PARSING_COND (cur_char == '{');
03897 
03898         /*
03899          *call the SAC handler api here.
03900          */
03901         if (PRIVATE (a_this)->sac_handler
03902             && PRIVATE (a_this)->sac_handler->start_media) {
03903                 PRIVATE (a_this)->sac_handler->start_media
03904                         (PRIVATE (a_this)->sac_handler, media_list);
03905         }
03906 
03907         cr_parser_try_to_skip_spaces_and_comments (a_this);
03908 
03909         PRIVATE (a_this)->state = TRY_PARSE_MEDIA_STATE;
03910 
03911         for (; status == CR_OK;) {
03912                 status = cr_parser_parse_ruleset (a_this);
03913                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03914         }
03915 
03916         READ_NEXT_CHAR (a_this, &cur_char);
03917 
03918         ENSURE_PARSING_COND (cur_char == '}');
03919 
03920         /*
03921          *call the right SAC handler api here.
03922          */
03923         if (PRIVATE (a_this)->sac_handler
03924             && PRIVATE (a_this)->sac_handler->end_media) {
03925                 PRIVATE (a_this)->sac_handler->end_media
03926                         (PRIVATE (a_this)->sac_handler, media_list);
03927         }
03928 
03929         cr_parser_try_to_skip_spaces_and_comments (a_this);
03930 
03931         /*
03932          *Then, free the data structures passed to
03933          *the last call to the SAC handler.
03934          */
03935         if (medium) {
03936                 g_string_free (medium, TRUE);
03937                 medium = NULL;
03938         }
03939 
03940         if (media_list) {
03941                 GList *cur = NULL;
03942 
03943                 for (cur = media_list; cur; cur = cur->next) {
03944                         g_string_free (cur->data, TRUE);
03945                 }
03946 
03947                 g_list_free (media_list);
03948                 media_list = NULL;
03949         }
03950 
03951         cr_parser_clear_errors (a_this);
03952         PRIVATE (a_this)->state = MEDIA_PARSED_STATE;
03953 
03954         return CR_OK;
03955 
03956       error:
03957 
03958         if (token) {
03959                 cr_token_destroy (token);
03960                 token = NULL;
03961         }
03962 
03963         if (medium) {
03964                 g_string_free (medium, TRUE);
03965                 medium = NULL;
03966         }
03967 
03968         if (media_list) {
03969                 GList *cur = NULL;
03970 
03971                 for (cur = media_list; cur; cur = cur->next) {
03972                         g_string_free (cur->data, TRUE);
03973                 }
03974 
03975                 g_list_free (media_list);
03976                 media_list = NULL;
03977         }
03978 
03979         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
03980 
03981         return status;
03982 }
03983 
03984 /**
03985  *Parses '@page' rule as specified in the css2 spec in appendix D.1:
03986  *page ::= PAGE_SYM S* IDENT? pseudo_page? S* 
03987  *'{' S* declaration [ ';' S* declaration ]* '}' S*
03988  *
03989  *This function also calls the relevant SAC handlers whenever it
03990  *encounters a construction that must 
03991  *be reported to the calling application.
03992  *@param a_this the "this pointer" of the current instance of #CRParser.
03993  *@return CR_OK upon successfull completion, an error code otherwise.
03994  */
03995 enum CRStatus
03996 cr_parser_parse_page (CRParser * a_this)
03997 {
03998         enum CRStatus status = CR_OK;
03999         CRInputPos init_pos;
04000         CRToken *token = NULL;
04001         CRTerm *css_expression = NULL;
04002         GString *page_selector = NULL,
04003                 *page_pseudo_class = NULL,
04004                 *property = NULL;
04005         gboolean important = TRUE;
04006 
04007         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
04008 
04009         RECORD_INITIAL_POS (a_this, &init_pos);
04010 
04011         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04012         ENSURE_PARSING_COND (status == CR_OK
04013                              && token && token->type == PAGE_SYM_TK);
04014 
04015         cr_token_destroy (token);
04016         token = NULL;
04017 
04018         cr_parser_try_to_skip_spaces_and_comments (a_this);
04019 
04020         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04021         ENSURE_PARSING_COND (status == CR_OK && token);
04022 
04023         if (token->type == IDENT_TK) {
04024                 page_selector = token->u.str;
04025                 token->u.str = NULL;
04026                 cr_token_destroy (token);
04027                 token = NULL;
04028         } else {
04029                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
04030                 token = NULL;
04031         }
04032 
04033         /* 
04034          *try to parse pseudo_page
04035          */
04036         cr_parser_try_to_skip_spaces_and_comments (a_this);
04037         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04038         ENSURE_PARSING_COND (status == CR_OK && token);
04039 
04040         if (token->type == DELIM_TK && token->u.unichar == ':') {
04041                 cr_token_destroy (token);
04042                 token = NULL;
04043                 status = cr_parser_parse_ident (a_this, &page_pseudo_class);
04044                 CHECK_PARSING_STATUS (status, FALSE);
04045         } else {
04046                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
04047                 token = NULL;
04048         }
04049 
04050         /*
04051          *parse_block
04052          *
04053          */
04054         cr_parser_try_to_skip_spaces_and_comments (a_this);
04055 
04056         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04057 
04058         ENSURE_PARSING_COND (status == CR_OK && token
04059                              && token->type == CBO_TK);
04060 
04061         cr_token_destroy (token);
04062         token = NULL;
04063 
04064         /*
04065          *Call the appropriate SAC handler here.
04066          */
04067         if (PRIVATE (a_this)->sac_handler
04068             && PRIVATE (a_this)->sac_handler->start_page) {
04069                 PRIVATE (a_this)->sac_handler->start_page
04070                         (PRIVATE (a_this)->sac_handler,
04071                          page_selector, page_pseudo_class);
04072         }
04073         cr_parser_try_to_skip_spaces_and_comments (a_this);
04074 
04075         PRIVATE (a_this)->state = TRY_PARSE_PAGE_STATE;
04076 
04077         status = cr_parser_parse_declaration (a_this, &property,
04078                                               &css_expression, &important);
04079         ENSURE_PARSING_COND (status == CR_OK);
04080 
04081         /*
04082          *call the relevant SAC handler here...
04083          */
04084         if (PRIVATE (a_this)->sac_handler
04085             && PRIVATE (a_this)->sac_handler->property) {
04086                 if (css_expression)
04087                         cr_term_ref (css_expression);
04088 
04089                 PRIVATE (a_this)->sac_handler->property
04090                         (PRIVATE (a_this)->sac_handler,
04091                          property, css_expression, important);
04092         }
04093         /*
04094          *... and free the data structure passed to that last
04095          *SAC handler.
04096          */
04097         if (property) {
04098                 g_string_free (property, TRUE);
04099                 property = NULL;
04100         }
04101         if (css_expression) {
04102                 cr_term_unref (css_expression);
04103                 css_expression = NULL;
04104         }
04105 
04106         for (;;) {
04107                 /*parse the other ';' separated declarations */
04108                 if (token) {
04109                         cr_token_destroy (token);
04110                         token = NULL;
04111                 }
04112                 status = cr_tknzr_get_next_token
04113                         (PRIVATE (a_this)->tknzr, &token);
04114 
04115                 ENSURE_PARSING_COND (status == CR_OK && token);
04116 
04117                 if (token->type != SEMICOLON_TK)
04118                         break;
04119 
04120                 cr_token_destroy (token);
04121                 token = NULL;
04122                 cr_parser_try_to_skip_spaces_and_comments (a_this);
04123 
04124                 status = cr_parser_parse_declaration (a_this, &property,
04125                                                       &css_expression,
04126                                                       &important);
04127                 CHECK_PARSING_STATUS (status, FALSE);
04128 
04129                 /*
04130                  *call the relevant SAC handler here...
04131                  */
04132                 if (PRIVATE (a_this)->sac_handler
04133                     && PRIVATE (a_this)->sac_handler->property) {
04134                         cr_term_ref (css_expression);
04135                         PRIVATE (a_this)->sac_handler->property
04136                                 (PRIVATE (a_this)->sac_handler,
04137                                  property, css_expression, important);
04138                 }
04139                 /*
04140                  *... and free the data structure passed to that last
04141                  *SAC handler.
04142                  */
04143                 if (property) {
04144                         g_string_free (property, TRUE);
04145                         property = NULL;
04146                 }
04147                 if (css_expression) {
04148                         cr_term_unref (css_expression);
04149                         css_expression = NULL;
04150                 }
04151         }
04152         ENSURE_PARSING_COND (status == CR_OK && token
04153                              && token->type == CBC_TK);
04154         cr_token_destroy (token);
04155         token = NULL;
04156 
04157         /*
04158          *call the relevant SAC handler here.
04159          */
04160         if (PRIVATE (a_this)->sac_handler
04161             && PRIVATE (a_this)->sac_handler->end_page) {
04162                 PRIVATE (a_this)->sac_handler->end_page
04163                         (PRIVATE (a_this)->sac_handler,
04164                          page_selector, page_pseudo_class);
04165         }
04166 
04167         if (page_selector) {
04168                 g_string_free (page_selector, TRUE);
04169                 page_selector = NULL;
04170         }
04171 
04172         if (page_pseudo_class) {
04173                 g_string_free (page_pseudo_class, TRUE);
04174                 page_pseudo_class = NULL;
04175         }
04176 
04177         cr_parser_try_to_skip_spaces_and_comments (a_this);
04178 
04179         /*here goes the former implem of this function ... */
04180 
04181         cr_parser_clear_errors (a_this);
04182         PRIVATE (a_this)->state = PAGE_PARSED_STATE;
04183 
04184         return CR_OK;
04185 
04186       error:
04187         if (token) {
04188                 cr_token_destroy (token);
04189                 token = NULL;
04190         }
04191         if (page_selector) {
04192                 g_string_free (page_selector, TRUE);
04193                 page_selector = NULL;
04194         }
04195         if (page_pseudo_class) {
04196                 g_string_free (page_pseudo_class, TRUE);
04197                 page_pseudo_class = NULL;
04198         }
04199         if (property) {
04200                 g_string_free (property, TRUE);
04201                 property = NULL;
04202         }
04203         if (css_expression) {
04204                 cr_term_destroy (css_expression);
04205                 css_expression = NULL;
04206         }
04207         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
04208         return status;
04209 }
04210 
04211 /**
04212  *Parses a charset declaration as defined implictly by the css2 spec in
04213  *appendix D.1:
04214  *charset ::= CHARSET_SYM S* STRING S* ';'
04215  *
04216  *@param a_this the "this pointer" of the current instance of #CRParser.
04217  *@param a_value out parameter. The actual parsed value of the charset 
04218  *declararation. Note that for safety check reasons, *a_value must be
04219  *set to NULL.
04220  *@return CR_OK upon successfull completion, an error code otherwise.
04221  */
04222 enum CRStatus
04223 cr_parser_parse_charset (CRParser * a_this, GString ** a_value)
04224 {
04225         enum CRStatus status = CR_OK;
04226         CRInputPos init_pos;
04227         CRToken *token = NULL;
04228         GString *charset_str = NULL;
04229 
04230         g_return_val_if_fail (a_this && a_value
04231                               && (*a_value == NULL), CR_BAD_PARAM_ERROR);
04232 
04233         RECORD_INITIAL_POS (a_this, &init_pos);
04234 
04235         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04236 
04237         ENSURE_PARSING_COND (status == CR_OK
04238                              && token && token->type == CHARSET_SYM_TK);
04239 
04240         cr_token_destroy (token);
04241         token = NULL;
04242 
04243         PRIVATE (a_this)->state = TRY_PARSE_CHARSET_STATE;
04244 
04245         cr_parser_try_to_skip_spaces_and_comments (a_this);
04246 
04247         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04248         ENSURE_PARSING_COND (status == CR_OK
04249                              && token && token->type == STRING_TK);
04250         charset_str = token->u.str;
04251         token->u.str = NULL;
04252         cr_token_destroy (token);
04253         token = NULL;
04254 
04255         cr_parser_try_to_skip_spaces_and_comments (a_this);
04256 
04257         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04258 
04259         ENSURE_PARSING_COND (status == CR_OK
04260                              && token && token->type == SEMICOLON_TK);
04261         cr_token_destroy (token);
04262         token = NULL;
04263 
04264         if (charset_str) {
04265                 *a_value = charset_str;
04266                 charset_str = NULL;
04267         }
04268 
04269         PRIVATE (a_this)->state = CHARSET_PARSED_STATE;
04270         return CR_OK;
04271 
04272       error:
04273 
04274         if (token) {
04275                 cr_token_destroy (token);
04276                 token = NULL;
04277         }
04278 
04279         if (*a_value) {
04280                 g_string_free (*a_value, TRUE);
04281                 *a_value = NULL;
04282         }
04283 
04284         if (charset_str) {
04285                 g_string_free (charset_str, TRUE);
04286                 charset_str = NULL;
04287         }
04288 
04289         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
04290 
04291         return status;
04292 }
04293 
04294 /**
04295  *Parses the "@font-face" rule specified in the css1 spec in
04296  *appendix D.1:
04297  *
04298  *font_face ::= FONT_FACE_SYM S* 
04299  *'{' S* declaration [ ';' S* declaration ]* '}' S*
04300  *
04301  *This function will call SAC handlers whenever it is necessary.
04302  *@return CR_OK upon successfull completion, an error code otherwise.
04303  */
04304 enum CRStatus
04305 cr_parser_parse_font_face (CRParser * a_this)
04306 {
04307         enum CRStatus status = CR_ERROR;
04308         CRInputPos init_pos;
04309         GString *property = NULL;
04310         CRTerm *css_expression = NULL;
04311         CRToken *token = NULL;
04312         gboolean important = FALSE;
04313         guint32 next_char = 0,
04314                 cur_char = 0;
04315 
04316         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
04317 
04318         RECORD_INITIAL_POS (a_this, &init_pos);
04319 
04320         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04321         ENSURE_PARSING_COND (status == CR_OK
04322                              && token && token->type == FONT_FACE_SYM_TK);
04323 
04324         cr_parser_try_to_skip_spaces_and_comments (a_this);
04325         if (token) {
04326                 cr_token_destroy (token);
04327                 token = NULL;
04328         }
04329         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04330         ENSURE_PARSING_COND (status == CR_OK && token
04331                              && token->type == CBO_TK);
04332         if (token) {
04333                 cr_token_destroy (token);
04334                 token = NULL;
04335         }
04336         /*
04337          *here, call the relevant SAC handler.
04338          */
04339         if (PRIVATE (a_this)->sac_handler
04340             && PRIVATE (a_this)->sac_handler->start_font_face) {
04341                 PRIVATE (a_this)->sac_handler->start_font_face
04342                         (PRIVATE (a_this)->sac_handler);
04343         }
04344         PRIVATE (a_this)->state = TRY_PARSE_FONT_FACE_STATE;
04345         /*
04346          *and resume the parsing.
04347          */
04348         cr_parser_try_to_skip_spaces_and_comments (a_this);
04349         status = cr_parser_parse_declaration (a_this, &property,
04350                                               &css_expression, &important);
04351         if (status == CR_OK) {
04352                 /*
04353                  *here, call the relevant SAC handler.
04354                  */
04355                 cr_term_ref (css_expression);
04356                 if (PRIVATE (a_this)->sac_handler &&
04357                     PRIVATE (a_this)->sac_handler->property) {
04358                         PRIVATE (a_this)->sac_handler->property
04359                                 (PRIVATE (a_this)->sac_handler,
04360                                  property, css_expression, important);
04361                 }
04362                 ENSURE_PARSING_COND (css_expression && property);
04363         }
04364         /*free the data structures allocated during last parsing. */
04365         if (property) {
04366                 g_string_free (property, TRUE);
04367                 property = NULL;
04368         }
04369         if (css_expression) {
04370                 cr_term_unref (css_expression);
04371                 css_expression = NULL;
04372         }
04373         for (;;) {
04374                 PEEK_NEXT_CHAR (a_this, &next_char);
04375                 if (next_char == ';') {
04376                         READ_NEXT_CHAR (a_this, &cur_char);
04377                 } else {
04378                         break;
04379                 }
04380                 cr_parser_try_to_skip_spaces_and_comments (a_this);
04381                 status = cr_parser_parse_declaration (a_this, &property,
04382                                                       &css_expression,
04383                                                       &important);
04384                 if (status != CR_OK)
04385                         break;
04386                 /*
04387                  *here, call the relevant SAC handler.
04388                  */
04389                 cr_term_ref (css_expression);
04390                 if (PRIVATE (a_this)->sac_handler->property) {
04391                         PRIVATE (a_this)->sac_handler->property
04392                                 (PRIVATE (a_this)->sac_handler,
04393                                  property, css_expression, important);
04394                 }
04395                 /*
04396                  *Then, free the data structures allocated during 
04397                  *last parsing.
04398                  */
04399                 if (property) {
04400                         g_string_free (property, TRUE);
04401                         property = NULL;
04402                 }
04403                 if (css_expression) {
04404                         cr_term_unref (css_expression);
04405                         css_expression = NULL;
04406                 }
04407         }
04408         cr_parser_try_to_skip_spaces_and_comments (a_this);
04409         READ_NEXT_CHAR (a_this, &cur_char);
04410         ENSURE_PARSING_COND (cur_char == '}');
04411         /*
04412          *here, call the relevant SAC handler.
04413          */
04414         if (PRIVATE (a_this)->sac_handler->end_font_face) {
04415                 PRIVATE (a_this)->sac_handler->end_font_face
04416                         (PRIVATE (a_this)->sac_handler);
04417         }
04418         cr_parser_try_to_skip_spaces_and_comments (a_this);
04419 
04420         if (token) {
04421                 cr_token_destroy (token);
04422                 token = NULL;
04423         }
04424         cr_parser_clear_errors (a_this);
04425         PRIVATE (a_this)->state = FONT_FACE_PARSED_STATE;
04426         return CR_OK;
04427 
04428       error:
04429         if (token) {
04430                 cr_token_destroy (token);
04431                 token = NULL;
04432         }
04433         if (property) {
04434                 g_string_free (property, TRUE);
04435                 property = NULL;
04436         }
04437         if (css_expression) {
04438                 cr_term_destroy (css_expression);
04439                 css_expression = NULL;
04440         }
04441         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
04442         return status;
04443 }
04444 
04445 /**
04446  *Parses the data that comes from the
04447  *input previously associated to the current instance of
04448  *#CRParser.
04449  *@param a_this the current instance of #CRParser.
04450  *@return CR_OK ;
04451  */
04452 enum CRStatus
04453 cr_parser_parse (CRParser * a_this)
04454 {
04455         enum CRStatus status = CR_ERROR;
04456 
04457         g_return_val_if_fail (a_this && PRIVATE (a_this)
04458                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
04459 
04460         if (PRIVATE (a_this)->use_core_grammar == FALSE) {
04461                 status = cr_parser_parse_stylesheet (a_this);
04462         } else {
04463                 status = cr_parser_parse_stylesheet_core (a_this);
04464         }
04465 
04466         return status;
04467 }
04468 
04469 enum CRStatus
04470 cr_parser_set_tknzr (CRParser * a_this, CRTknzr * a_tknzr)
04471 {
04472         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
04473 
04474         if (PRIVATE (a_this)->tknzr) {
04475                 cr_tknzr_unref (PRIVATE (a_this)->tknzr);
04476         }
04477 
04478         PRIVATE (a_this)->tknzr = a_tknzr;
04479 
04480         if (a_tknzr)
04481                 cr_tknzr_ref (a_tknzr);
04482 
04483         return CR_OK;
04484 }
04485 
04486 /**
04487  *Getter of the parser's underlying tokenizer
04488  *@param a_this the current instance of #CRParser
04489  *@param a_tknzr out parameter. The returned tokenizer
04490  *@return CR_OK upon succesful completion, an error code
04491  *otherwise
04492  */
04493 enum CRStatus
04494 cr_parser_get_tknzr (CRParser * a_this, CRTknzr ** a_tknzr)
04495 {
04496         g_return_val_if_fail (a_this && PRIVATE (a_this)
04497                               && a_tknzr, CR_BAD_PARAM_ERROR);
04498 
04499         *a_tknzr = PRIVATE (a_this)->tknzr;
04500         return CR_OK;
04501 }
04502 
04503 /**
04504  *Parses a stylesheet from a buffer
04505  *@param a_this the current instance of #CRparser
04506  *@param a_buf the input buffer
04507  *@param a_len the length of the input buffer
04508  *@param a_enc the encoding of the buffer
04509  *@return CR_OK upon successful completion, an error code otherwise.
04510  */
04511 enum CRStatus
04512 cr_parser_parse_buf (CRParser * a_this,
04513                      const guchar * a_buf,
04514                      gulong a_len, enum CREncoding a_enc)
04515 {
04516         enum CRStatus status = CR_ERROR;
04517         CRTknzr *tknzr = NULL;
04518 
04519         g_return_val_if_fail (a_this && PRIVATE (a_this)
04520                               && a_buf, CR_BAD_PARAM_ERROR);
04521 
04522         tknzr = cr_tknzr_new_from_buf ((guchar*)a_buf, a_len, a_enc, FALSE);
04523 
04524         g_return_val_if_fail (tknzr != NULL, CR_ERROR);
04525 
04526         status = cr_parser_set_tknzr (a_this, tknzr);
04527         g_return_val_if_fail (status == CR_OK, CR_ERROR);
04528 
04529         status = cr_parser_parse (a_this);
04530 
04531         return status;
04532 }
04533 
04534 /**
04535  *Destroys the current instance
04536  *of #CRParser.
04537  *@param a_this the current instance of #CRParser to
04538  *destroy.
04539  */
04540 void
04541 cr_parser_destroy (CRParser * a_this)
04542 {
04543         g_return_if_fail (a_this && PRIVATE (a_this));
04544 
04545         if (PRIVATE (a_this)->tknzr) {
04546                 if (cr_tknzr_unref (PRIVATE (a_this)->tknzr) == TRUE)
04547                         PRIVATE (a_this)->tknzr = NULL;
04548         }
04549 
04550         if (PRIVATE (a_this)->sac_handler) {
04551                 cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
04552                 PRIVATE (a_this)->sac_handler = NULL;
04553         }
04554 
04555         if (PRIVATE (a_this)->err_stack) {
04556                 cr_parser_clear_errors (a_this);
04557                 PRIVATE (a_this)->err_stack = NULL;
04558         }
04559 
04560         if (PRIVATE (a_this)) {
04561                 g_free (PRIVATE (a_this));
04562                 PRIVATE (a_this) = NULL;
04563         }
04564 
04565         if (a_this) {
04566                 g_free (a_this);
04567                 a_this = NULL;  /*useless. Just for the sake of coherence */
04568         }
04569 }

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