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

Generated on Wed Mar 15 18:23:54 2006 for Libcroco by  doxygen 1.4.6