Libcroco
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 pnc_status ; \
00201 pnc_status = cr_tknzr_peek_char  (PRIVATE (a_this)->tknzr, a_to_char) ; \
00202 CHECK_PARSING_STATUS (pnc_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 = (guchar *) g_strdup ((const gchar *) 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, (const guchar *) "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, (const guchar *) "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 occurred.
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 const * 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                  (const guchar *) "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                          (const guchar *) "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                  (const guchar *) "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         CRParsingLocation end_parsing_location;
03340 
03341         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
03342 
03343         RECORD_INITIAL_POS (a_this, &init_pos);
03344 
03345         status = cr_parser_parse_selector (a_this, &selector);
03346         CHECK_PARSING_STATUS (status, FALSE);
03347 
03348         READ_NEXT_CHAR (a_this, &cur_char);
03349 
03350         ENSURE_PARSING_COND_ERR
03351                 (a_this, cur_char == '{',
03352                  (const guchar *) "while parsing rulset: current char should be '{'",
03353                  CR_SYNTAX_ERROR);
03354 
03355         if (PRIVATE (a_this)->sac_handler
03356             && PRIVATE (a_this)->sac_handler->start_selector) {
03357                 /*
03358                  *the selector is ref counted so that the parser's user
03359                  *can choose to keep it.
03360                  */
03361                 if (selector) {
03362                         cr_selector_ref (selector);
03363                 }
03364 
03365                 PRIVATE (a_this)->sac_handler->start_selector
03366                         (PRIVATE (a_this)->sac_handler, selector);
03367                 start_selector = TRUE;
03368         }
03369 
03370         cr_parser_try_to_skip_spaces_and_comments (a_this);
03371 
03372         PRIVATE (a_this)->state = TRY_PARSE_RULESET_STATE;
03373 
03374         status = cr_parser_parse_declaration (a_this, &property,
03375                                               &expr,
03376                                               &is_important);
03377         if (expr) {
03378                 cr_term_ref (expr);
03379         }
03380         if (status == CR_OK
03381             && PRIVATE (a_this)->sac_handler
03382             && PRIVATE (a_this)->sac_handler->property) {
03383                 PRIVATE (a_this)->sac_handler->property
03384                         (PRIVATE (a_this)->sac_handler, property, expr,
03385                          is_important);
03386         }        
03387         if (status == CR_OK) {
03388                 /*
03389                  *free the allocated
03390                  *'property' and 'term' before parsing
03391                  *next declarations.
03392                  */
03393                 if (property) {
03394                         cr_string_destroy (property);
03395                         property = NULL;
03396                 }
03397                 if (expr) {
03398                         cr_term_unref (expr);
03399                         expr = NULL;
03400                 }
03401         } else {/*status != CR_OK*/                
03402                 guint32 c = 0 ;
03403                 /*
03404                  *test if we have reached '}', which
03405                  *would mean that we are parsing an empty ruleset (eg. x{ })
03406                  *In that case, goto end_of_ruleset.
03407                  */
03408                 status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, &c) ;
03409                 if (status == CR_OK && c == '}') {
03410                         status = CR_OK ;
03411                         goto end_of_ruleset ;
03412                 }
03413         }
03414         CHECK_PARSING_STATUS_ERR
03415                 (a_this, status, FALSE,
03416                  (const guchar *) "while parsing ruleset: next construction should be a declaration",
03417                  CR_SYNTAX_ERROR);
03418 
03419         for (;;) {
03420                 PEEK_NEXT_CHAR (a_this, &next_char);
03421                 if (next_char != ';')
03422                         break;
03423 
03424                 /*consume the ';' char */
03425                 READ_NEXT_CHAR (a_this, &cur_char);
03426 
03427                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03428 
03429                 status = cr_parser_parse_declaration (a_this, &property,
03430                                                       &expr, &is_important);
03431 
03432                 if (expr) {
03433                         cr_term_ref (expr);
03434                 }
03435                 if (status == CR_OK
03436                     && PRIVATE (a_this)->sac_handler
03437                     && PRIVATE (a_this)->sac_handler->property) {
03438                         PRIVATE (a_this)->sac_handler->property
03439                                 (PRIVATE (a_this)->sac_handler,
03440                                  property, expr, is_important);
03441                 }
03442                 if (property) {
03443                         cr_string_destroy (property);
03444                         property = NULL;
03445                 }
03446                 if (expr) {
03447                         cr_term_unref (expr);
03448                         expr = NULL;
03449                 }
03450         }
03451 
03452  end_of_ruleset:
03453         cr_parser_try_to_skip_spaces_and_comments (a_this);
03454         cr_parser_get_parsing_location (a_this, &end_parsing_location);
03455         READ_NEXT_CHAR (a_this, &cur_char);
03456         ENSURE_PARSING_COND_ERR
03457                 (a_this, cur_char == '}',
03458                  (const guchar *) "while parsing rulset: current char must be a '}'",
03459                  CR_SYNTAX_ERROR);
03460 
03461         selector->location = end_parsing_location;
03462         if (PRIVATE (a_this)->sac_handler
03463             && PRIVATE (a_this)->sac_handler->end_selector) {
03464                 PRIVATE (a_this)->sac_handler->end_selector
03465                         (PRIVATE (a_this)->sac_handler, selector);
03466                 start_selector = FALSE;
03467         }
03468 
03469         if (expr) {
03470                 cr_term_unref (expr);
03471                 expr = NULL;
03472         }
03473 
03474         if (simple_sels) {
03475                 cr_simple_sel_destroy (simple_sels);
03476                 simple_sels = NULL;
03477         }
03478 
03479         if (selector) {
03480                 cr_selector_unref (selector);
03481                 selector = NULL;
03482         }
03483 
03484         cr_parser_clear_errors (a_this);
03485         PRIVATE (a_this)->state = RULESET_PARSED_STATE;
03486 
03487         return CR_OK;
03488 
03489  error:
03490         if (start_selector == TRUE
03491             && PRIVATE (a_this)->sac_handler
03492             && PRIVATE (a_this)->sac_handler->error) {
03493                 PRIVATE (a_this)->sac_handler->error
03494                         (PRIVATE (a_this)->sac_handler);
03495         }
03496         if (expr) {
03497                 cr_term_unref (expr);
03498                 expr = NULL;
03499         }
03500         if (simple_sels) {
03501                 cr_simple_sel_destroy (simple_sels);
03502                 simple_sels = NULL;
03503         }
03504         if (property) {
03505                 cr_string_destroy (property);
03506         }
03507         if (selector) {
03508                 cr_selector_unref (selector);
03509                 selector = NULL;
03510         }
03511 
03512         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
03513 
03514         return status;
03515 }
03516 
03517 /**
03518  * cr_parser_parse_import:
03519  *@a_this: the "this pointer" of the current instance 
03520  *of #CRParser.
03521  *@a_media_list: out parameter. A linked list of 
03522  *#CRString
03523  *Each CRString is a string that contains
03524  *a 'medium' declaration part of the successfully 
03525  *parsed 'import' declaration.
03526  *@a_import_string: out parameter. 
03527  *A string that contains the 'import 
03528  *string". The import string can be either an uri (if it starts with
03529  *the substring "uri(") or a any other css2 string. Note that
03530  * *a_import_string must be initially set to NULL or else, this function
03531  *will return CR_BAD_PARAM_ERROR.
03532  *@a_location: the location (line, column) where the import has been parsed
03533  *
03534  *Parses an 'import' declaration as defined in the css2 spec
03535  *in appendix D.1:
03536  *
03537  *import ::=
03538  *\@import [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S*
03539  *
03540  *Returns CR_OK upon sucessfull completion, an error code otherwise.
03541  */
03542 enum CRStatus
03543 cr_parser_parse_import (CRParser * a_this,
03544                         GList ** a_media_list,
03545                         CRString ** a_import_string,
03546                         CRParsingLocation *a_location)
03547 {
03548         enum CRStatus status = CR_OK;
03549         CRInputPos init_pos;
03550         guint32 cur_char = 0,
03551                 next_char = 0;
03552         CRString *medium = NULL;
03553 
03554         g_return_val_if_fail (a_this
03555                               && a_import_string
03556                               && (*a_import_string == NULL),
03557                               CR_BAD_PARAM_ERROR);
03558 
03559         RECORD_INITIAL_POS (a_this, &init_pos);
03560 
03561         if (BYTE (a_this, 1, NULL) == '@'
03562             && BYTE (a_this, 2, NULL) == 'i'
03563             && BYTE (a_this, 3, NULL) == 'm'
03564             && BYTE (a_this, 4, NULL) == 'p'
03565             && BYTE (a_this, 5, NULL) == 'o'
03566             && BYTE (a_this, 6, NULL) == 'r'
03567             && BYTE (a_this, 7, NULL) == 't') {
03568                 SKIP_CHARS (a_this, 1);
03569                 if (a_location) {
03570                         cr_parser_get_parsing_location 
03571                                 (a_this, a_location) ;
03572                 }
03573                 SKIP_CHARS (a_this, 6);
03574                 status = CR_OK;
03575         } else {
03576                 status = CR_PARSING_ERROR;
03577                 goto error;
03578         }
03579 
03580         cr_parser_try_to_skip_spaces_and_comments (a_this);
03581 
03582         PRIVATE (a_this)->state = TRY_PARSE_IMPORT_STATE;
03583 
03584         PEEK_NEXT_CHAR (a_this, &next_char);
03585 
03586         if (next_char == '"' || next_char == '\'') {
03587                 status = cr_parser_parse_string (a_this, a_import_string);
03588 
03589                 CHECK_PARSING_STATUS (status, FALSE);
03590         } else {
03591                 status = cr_parser_parse_uri (a_this, a_import_string);
03592 
03593                 CHECK_PARSING_STATUS (status, FALSE);
03594         }
03595 
03596         cr_parser_try_to_skip_spaces_and_comments (a_this);
03597 
03598         status = cr_parser_parse_ident (a_this, &medium);
03599 
03600         if (status == CR_OK && medium) {
03601                 *a_media_list = g_list_append (*a_media_list, medium);
03602                 medium = NULL;
03603         }
03604 
03605         cr_parser_try_to_skip_spaces_and_comments (a_this);
03606 
03607         for (; status == CR_OK;) {
03608                 if ((status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
03609                                                   &next_char)) != CR_OK) {
03610                         if (status == CR_END_OF_INPUT_ERROR) {
03611                                 status = CR_OK;
03612                                 goto okay;
03613                         }
03614                         goto error;
03615                 }
03616 
03617                 if (next_char == ',') {
03618                         READ_NEXT_CHAR (a_this, &cur_char);
03619                 } else {
03620                         break;
03621                 }
03622 
03623                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03624 
03625                 status = cr_parser_parse_ident (a_this, &medium);
03626 
03627                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03628 
03629                 if ((status == CR_OK) && medium) {
03630                         *a_media_list = g_list_append (*a_media_list, medium);
03631 
03632                         medium = NULL;
03633                 }
03634 
03635                 CHECK_PARSING_STATUS (status, FALSE);
03636                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03637         }
03638         cr_parser_try_to_skip_spaces_and_comments (a_this);
03639         READ_NEXT_CHAR (a_this, &cur_char);
03640         ENSURE_PARSING_COND (cur_char == ';');
03641         cr_parser_try_to_skip_spaces_and_comments (a_this);
03642       okay:
03643         cr_parser_clear_errors (a_this);
03644         PRIVATE (a_this)->state = IMPORT_PARSED_STATE;
03645 
03646         return CR_OK;
03647 
03648       error:
03649 
03650         if (*a_media_list) {
03651                 GList *cur = NULL;
03652 
03653                 /*
03654                  *free each element of *a_media_list.
03655                  *Note that each element of *a_medium list *must*
03656                  *be a GString* or else, the code that is coming next 
03657                  *will corrupt the memory and lead to hard to debug
03658                  *random crashes.
03659                  *This is where C++ and its compile time
03660                  *type checking mecanism (through STL containers) would
03661                  *have prevented us to go through this hassle.
03662                  */
03663                 for (cur = *a_media_list; cur; cur = cur->next) {
03664                         if (cur->data) {
03665                                 cr_string_destroy (cur->data);
03666                         }
03667                 }
03668 
03669                 g_list_free (*a_media_list);
03670                 *a_media_list = NULL;
03671         }
03672 
03673         if (*a_import_string) {
03674                 cr_string_destroy (*a_import_string);
03675                 *a_import_string = NULL;
03676         }
03677 
03678         if (medium) {
03679                 cr_string_destroy (medium);
03680                 medium = NULL;
03681         }
03682 
03683         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
03684 
03685         return status;
03686 }
03687 
03688 /**
03689  * cr_parser_parse_media:
03690  *@a_this: the "this pointer" of the current instance of #CRParser.
03691  *
03692  *Parses a 'media' declaration as specified in the css2 spec at
03693  *appendix D.1:
03694  *
03695  *media ::= \@media S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S*
03696  *
03697  *Note that this function calls the required sac handlers during the parsing
03698  *to notify media productions. See #CRDocHandler to know the callback called
03699  *during \@media parsing.
03700  *
03701  *Returns CR_OK upon successfull completion, an error code otherwise.
03702  */
03703 enum CRStatus
03704 cr_parser_parse_media (CRParser * a_this)
03705 {
03706         enum CRStatus status = CR_OK;
03707         CRInputPos init_pos;
03708         CRToken *token = NULL;
03709         guint32 next_char = 0,
03710                 cur_char = 0;
03711         CRString *medium = NULL;
03712         GList *media_list = NULL;
03713         CRParsingLocation location = {0} ;
03714 
03715         g_return_val_if_fail (a_this 
03716                               && PRIVATE (a_this), 
03717                               CR_BAD_PARAM_ERROR);
03718 
03719         RECORD_INITIAL_POS (a_this, &init_pos);
03720 
03721         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
03722                                           &token);
03723         ENSURE_PARSING_COND (status == CR_OK
03724                              && token 
03725                              && token->type == MEDIA_SYM_TK);
03726         cr_parsing_location_copy (&location, &token->location) ;
03727         cr_token_destroy (token);
03728         token = NULL;
03729 
03730         cr_parser_try_to_skip_spaces_and_comments (a_this);
03731 
03732         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
03733         ENSURE_PARSING_COND (status == CR_OK
03734                              && token && token->type == IDENT_TK);
03735 
03736         medium = token->u.str;
03737         token->u.str = NULL;
03738         cr_token_destroy (token);
03739         token = NULL;
03740 
03741         if (medium) {
03742                 media_list = g_list_append (media_list, medium);
03743                 medium = NULL;
03744         }
03745 
03746         for (; status == CR_OK;) {
03747                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03748                 PEEK_NEXT_CHAR (a_this, &next_char);
03749 
03750                 if (next_char == ',') {
03751                         READ_NEXT_CHAR (a_this, &cur_char);
03752                 } else {
03753                         break;
03754                 }
03755 
03756                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03757 
03758                 status = cr_parser_parse_ident (a_this, &medium);
03759 
03760                 CHECK_PARSING_STATUS (status, FALSE);
03761 
03762                 if (medium) {
03763                         media_list = g_list_append (media_list, medium);
03764                         medium = NULL;
03765                 }
03766         }
03767 
03768         READ_NEXT_CHAR (a_this, &cur_char);
03769 
03770         ENSURE_PARSING_COND (cur_char == '{');
03771 
03772         /*
03773          *call the SAC handler api here.
03774          */
03775         if (PRIVATE (a_this)->sac_handler
03776             && PRIVATE (a_this)->sac_handler->start_media) {
03777                 PRIVATE (a_this)->sac_handler->start_media
03778                         (PRIVATE (a_this)->sac_handler, media_list,
03779                          &location);
03780         }
03781 
03782         cr_parser_try_to_skip_spaces_and_comments (a_this);
03783 
03784         PRIVATE (a_this)->state = TRY_PARSE_MEDIA_STATE;
03785 
03786         for (; status == CR_OK;) {
03787                 status = cr_parser_parse_ruleset (a_this);
03788                 cr_parser_try_to_skip_spaces_and_comments (a_this);
03789         }
03790 
03791         READ_NEXT_CHAR (a_this, &cur_char);
03792 
03793         ENSURE_PARSING_COND (cur_char == '}');
03794 
03795         /*
03796          *call the right SAC handler api here.
03797          */
03798         if (PRIVATE (a_this)->sac_handler
03799             && PRIVATE (a_this)->sac_handler->end_media) {
03800                 PRIVATE (a_this)->sac_handler->end_media
03801                         (PRIVATE (a_this)->sac_handler, media_list);
03802         }
03803 
03804         cr_parser_try_to_skip_spaces_and_comments (a_this);
03805 
03806         /*
03807          *Then, free the data structures passed to
03808          *the last call to the SAC handler.
03809          */
03810         if (medium) {
03811                 cr_string_destroy (medium);
03812                 medium = NULL;
03813         }
03814 
03815         if (media_list) {
03816                 GList *cur = NULL;
03817 
03818                 for (cur = media_list; cur; cur = cur->next) {
03819                         cr_string_destroy (cur->data);
03820                 }
03821 
03822                 g_list_free (media_list);
03823                 media_list = NULL;
03824         }
03825 
03826         cr_parser_clear_errors (a_this);
03827         PRIVATE (a_this)->state = MEDIA_PARSED_STATE;
03828 
03829         return CR_OK;
03830 
03831       error:
03832 
03833         if (token) {
03834                 cr_token_destroy (token);
03835                 token = NULL;
03836         }
03837 
03838         if (medium) {
03839                 cr_string_destroy (medium);
03840                 medium = NULL;
03841         }
03842 
03843         if (media_list) {
03844                 GList *cur = NULL;
03845 
03846                 for (cur = media_list; cur; cur = cur->next) {
03847                         cr_string_destroy (cur->data);
03848                 }
03849 
03850                 g_list_free (media_list);
03851                 media_list = NULL;
03852         }
03853 
03854         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
03855 
03856         return status;
03857 }
03858 
03859 /**
03860  * cr_parser_parse_page:
03861  *@a_this: the "this pointer" of the current instance of #CRParser.
03862  *
03863  *Parses '\@page' rule as specified in the css2 spec in appendix D.1:
03864  *page ::= PAGE_SYM S* IDENT? pseudo_page? S* 
03865  *'{' S* declaration [ ';' S* declaration ]* '}' S*
03866  *
03867  *This function also calls the relevant SAC handlers whenever it
03868  *encounters a construction that must 
03869  *be reported to the calling application.
03870  *
03871  *Returns CR_OK upon successfull completion, an error code otherwise.
03872  */
03873 enum CRStatus
03874 cr_parser_parse_page (CRParser * a_this)
03875 {
03876         enum CRStatus status = CR_OK;
03877         CRInputPos init_pos;
03878         CRToken *token = NULL;
03879         CRTerm *css_expression = NULL;
03880         CRString *page_selector = NULL,
03881                 *page_pseudo_class = NULL,
03882                 *property = NULL;
03883         gboolean important = TRUE;
03884         CRParsingLocation location = {0} ;
03885 
03886         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
03887 
03888         RECORD_INITIAL_POS (a_this, &init_pos);
03889 
03890         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
03891                                           &token) ;
03892         ENSURE_PARSING_COND (status == CR_OK
03893                              && token 
03894                              && token->type == PAGE_SYM_TK);
03895 
03896         cr_parsing_location_copy (&location, &token->location) ;
03897         cr_token_destroy (token);
03898         token = NULL;
03899 
03900         cr_parser_try_to_skip_spaces_and_comments (a_this);
03901 
03902         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
03903         ENSURE_PARSING_COND (status == CR_OK && token);
03904 
03905         if (token->type == IDENT_TK) {
03906                 page_selector = token->u.str;
03907                 token->u.str = NULL;
03908                 cr_token_destroy (token);
03909                 token = NULL;
03910         } else {
03911                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
03912                 token = NULL;
03913         }
03914 
03915         /* 
03916          *try to parse pseudo_page
03917          */
03918         cr_parser_try_to_skip_spaces_and_comments (a_this);
03919         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
03920         ENSURE_PARSING_COND (status == CR_OK && token);
03921 
03922         if (token->type == DELIM_TK && token->u.unichar == ':') {
03923                 cr_token_destroy (token);
03924                 token = NULL;
03925                 status = cr_parser_parse_ident (a_this, &page_pseudo_class);
03926                 CHECK_PARSING_STATUS (status, FALSE);
03927         } else {
03928                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
03929                 token = NULL;
03930         }
03931 
03932         /*
03933          *parse_block
03934          *
03935          */
03936         cr_parser_try_to_skip_spaces_and_comments (a_this);
03937 
03938         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
03939 
03940         ENSURE_PARSING_COND (status == CR_OK && token
03941                              && token->type == CBO_TK);
03942 
03943         cr_token_destroy (token);
03944         token = NULL;
03945 
03946         /*
03947          *Call the appropriate SAC handler here.
03948          */
03949         if (PRIVATE (a_this)->sac_handler
03950             && PRIVATE (a_this)->sac_handler->start_page) {
03951                 PRIVATE (a_this)->sac_handler->start_page
03952                         (PRIVATE (a_this)->sac_handler,
03953                          page_selector, page_pseudo_class,
03954                          &location);
03955         }
03956         cr_parser_try_to_skip_spaces_and_comments (a_this);
03957 
03958         PRIVATE (a_this)->state = TRY_PARSE_PAGE_STATE;
03959 
03960         status = cr_parser_parse_declaration (a_this, &property,
03961                                               &css_expression, 
03962                                               &important);
03963         ENSURE_PARSING_COND (status == CR_OK);
03964 
03965         /*
03966          *call the relevant SAC handler here...
03967          */
03968         if (PRIVATE (a_this)->sac_handler
03969             && PRIVATE (a_this)->sac_handler->property) {
03970                 if (css_expression)
03971                         cr_term_ref (css_expression);
03972 
03973                 PRIVATE (a_this)->sac_handler->property
03974                         (PRIVATE (a_this)->sac_handler,
03975                          property, css_expression, important);
03976         }
03977         /*
03978          *... and free the data structure passed to that last
03979          *SAC handler.
03980          */
03981         if (property) {
03982                 cr_string_destroy (property);
03983                 property = NULL;
03984         }
03985         if (css_expression) {
03986                 cr_term_unref (css_expression);
03987                 css_expression = NULL;
03988         }
03989 
03990         for (;;) {
03991                 /*parse the other ';' separated declarations */
03992                 if (token) {
03993                         cr_token_destroy (token);
03994                         token = NULL;
03995                 }
03996                 status = cr_tknzr_get_next_token
03997                         (PRIVATE (a_this)->tknzr, &token);
03998 
03999                 ENSURE_PARSING_COND (status == CR_OK && token);
04000 
04001                 if (token->type != SEMICOLON_TK) {
04002                         cr_tknzr_unget_token
04003                                 (PRIVATE (a_this)->tknzr,
04004                                  token);
04005                         token = NULL ;
04006                         break;
04007                 }
04008 
04009                 cr_token_destroy (token);
04010                 token = NULL;
04011                 cr_parser_try_to_skip_spaces_and_comments (a_this);
04012 
04013                 status = cr_parser_parse_declaration (a_this, &property,
04014                                                       &css_expression,
04015                                                       &important);
04016                 if (status != CR_OK)
04017                         break ;
04018 
04019                 /*
04020                  *call the relevant SAC handler here...
04021                  */
04022                 if (PRIVATE (a_this)->sac_handler
04023                     && PRIVATE (a_this)->sac_handler->property) {
04024                         cr_term_ref (css_expression);
04025                         PRIVATE (a_this)->sac_handler->property
04026                                 (PRIVATE (a_this)->sac_handler,
04027                                  property, css_expression, important);
04028                 }
04029                 /*
04030                  *... and free the data structure passed to that last
04031                  *SAC handler.
04032                  */
04033                 if (property) {
04034                         cr_string_destroy (property);
04035                         property = NULL;
04036                 }
04037                 if (css_expression) {
04038                         cr_term_unref (css_expression);
04039                         css_expression = NULL;
04040                 }
04041         }
04042         cr_parser_try_to_skip_spaces_and_comments 
04043                 (a_this) ;
04044         if (token) {
04045                 cr_token_destroy (token) ;
04046                 token = NULL ;
04047         }
04048 
04049         status = cr_tknzr_get_next_token
04050                         (PRIVATE (a_this)->tknzr, &token);
04051         ENSURE_PARSING_COND (status == CR_OK 
04052                              && token 
04053                              && token->type == CBC_TK) ;
04054         cr_token_destroy (token) ;
04055         token = NULL ;
04056         /*
04057          *call the relevant SAC handler here.
04058          */
04059         if (PRIVATE (a_this)->sac_handler
04060             && PRIVATE (a_this)->sac_handler->end_page) {
04061                 PRIVATE (a_this)->sac_handler->end_page
04062                         (PRIVATE (a_this)->sac_handler,
04063                          page_selector, page_pseudo_class);
04064         }
04065 
04066         if (page_selector) {
04067                 cr_string_destroy (page_selector);
04068                 page_selector = NULL;
04069         }
04070 
04071         if (page_pseudo_class) {
04072                 cr_string_destroy (page_pseudo_class);
04073                 page_pseudo_class = NULL;
04074         }
04075 
04076         cr_parser_try_to_skip_spaces_and_comments (a_this);
04077 
04078         /*here goes the former implem of this function ... */
04079 
04080         cr_parser_clear_errors (a_this);
04081         PRIVATE (a_this)->state = PAGE_PARSED_STATE;
04082 
04083         return CR_OK;
04084 
04085  error:
04086         if (token) {
04087                 cr_token_destroy (token);
04088                 token = NULL;
04089         }
04090         if (page_selector) {
04091                 cr_string_destroy (page_selector);
04092                 page_selector = NULL;
04093         }
04094         if (page_pseudo_class) {
04095                 cr_string_destroy (page_pseudo_class);
04096                 page_pseudo_class = NULL;
04097         }
04098         if (property) {
04099                 cr_string_destroy (property);
04100                 property = NULL;
04101         }
04102         if (css_expression) {
04103                 cr_term_destroy (css_expression);
04104                 css_expression = NULL;
04105         }
04106         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
04107         return status;
04108 }
04109 
04110 /**
04111  * cr_parser_parse_charset:
04112  *@a_this: the "this pointer" of the current instance of #CRParser.
04113  *@a_value: out parameter. The actual parsed value of the charset 
04114  *declararation. Note that for safety check reasons, *a_value must be
04115  *set to NULL.
04116  *@a_charset_sym_location: the parsing location of the charset rule
04117  *
04118  *Parses a charset declaration as defined implictly by the css2 spec in
04119  *appendix D.1:
04120  *charset ::= CHARSET_SYM S* STRING S* ';'
04121  *
04122  *Returns CR_OK upon successfull completion, an error code otherwise.
04123  */
04124 enum CRStatus
04125 cr_parser_parse_charset (CRParser * a_this, CRString ** a_value,
04126                          CRParsingLocation *a_charset_sym_location)
04127 {
04128         enum CRStatus status = CR_OK;
04129         CRInputPos init_pos;
04130         CRToken *token = NULL;
04131         CRString *charset_str = NULL;
04132 
04133         g_return_val_if_fail (a_this && a_value
04134                               && (*a_value == NULL), 
04135                               CR_BAD_PARAM_ERROR);
04136 
04137         RECORD_INITIAL_POS (a_this, &init_pos);
04138 
04139         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04140 
04141         ENSURE_PARSING_COND (status == CR_OK
04142                              && token && token->type == CHARSET_SYM_TK);
04143         if (a_charset_sym_location) {
04144                 cr_parsing_location_copy (a_charset_sym_location, 
04145                                           &token->location) ;
04146         }
04147         cr_token_destroy (token);
04148         token = NULL;
04149 
04150         PRIVATE (a_this)->state = TRY_PARSE_CHARSET_STATE;
04151 
04152         cr_parser_try_to_skip_spaces_and_comments (a_this);
04153 
04154         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04155         ENSURE_PARSING_COND (status == CR_OK
04156                              && token && token->type == STRING_TK);
04157         charset_str = token->u.str;
04158         token->u.str = NULL;
04159         cr_token_destroy (token);
04160         token = NULL;
04161 
04162         cr_parser_try_to_skip_spaces_and_comments (a_this);
04163 
04164         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04165 
04166         ENSURE_PARSING_COND (status == CR_OK
04167                              && token && token->type == SEMICOLON_TK);
04168         cr_token_destroy (token);
04169         token = NULL;
04170 
04171         if (charset_str) {
04172                 *a_value = charset_str;
04173                 charset_str = NULL;
04174         }
04175 
04176         PRIVATE (a_this)->state = CHARSET_PARSED_STATE;
04177         return CR_OK;
04178 
04179  error:
04180 
04181         if (token) {
04182                 cr_token_destroy (token);
04183                 token = NULL;
04184         }
04185 
04186         if (*a_value) {
04187                 cr_string_destroy (*a_value);
04188                 *a_value = NULL;
04189         }
04190 
04191         if (charset_str) {
04192                 cr_string_destroy (charset_str);
04193                 charset_str = NULL;
04194         }
04195 
04196         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
04197 
04198         return status;
04199 }
04200 
04201 /**
04202  * cr_parser_parse_font_face:
04203  *@a_this: the current instance of #CRParser.
04204  *
04205  *Parses the "\@font-face" rule specified in the css1 spec in
04206  *appendix D.1:
04207  *
04208  *font_face ::= FONT_FACE_SYM S* 
04209  *'{' S* declaration [ ';' S* declaration ]* '}' S*
04210  *
04211  *This function will call SAC handlers whenever it is necessary.
04212  *
04213  *Returns CR_OK upon successfull completion, an error code otherwise.
04214  */
04215 enum CRStatus
04216 cr_parser_parse_font_face (CRParser * a_this)
04217 {
04218         enum CRStatus status = CR_ERROR;
04219         CRInputPos init_pos;
04220         CRString *property = NULL;
04221         CRTerm *css_expression = NULL;
04222         CRToken *token = NULL;
04223         gboolean important = FALSE;
04224         guint32 next_char = 0,
04225                 cur_char = 0;
04226         CRParsingLocation location = {0} ;
04227 
04228         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
04229 
04230         RECORD_INITIAL_POS (a_this, &init_pos);
04231 
04232         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
04233         ENSURE_PARSING_COND (status == CR_OK
04234                              && token 
04235                              && token->type == FONT_FACE_SYM_TK);
04236 
04237         cr_parser_try_to_skip_spaces_and_comments (a_this);
04238         if (token) {
04239                 cr_parsing_location_copy (&location, 
04240                                           &token->location) ;
04241                 cr_token_destroy (token);
04242                 token = NULL;
04243         }
04244         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
04245                                           &token);
04246         ENSURE_PARSING_COND (status == CR_OK && token
04247                              && token->type == CBO_TK);
04248         if (token) {
04249                 cr_token_destroy (token);
04250                 token = NULL;
04251         }
04252         /*
04253          *here, call the relevant SAC handler.
04254          */
04255         if (PRIVATE (a_this)->sac_handler
04256             && PRIVATE (a_this)->sac_handler->start_font_face) {
04257                 PRIVATE (a_this)->sac_handler->start_font_face
04258                         (PRIVATE (a_this)->sac_handler, &location);
04259         }
04260         PRIVATE (a_this)->state = TRY_PARSE_FONT_FACE_STATE;
04261         /*
04262          *and resume the parsing.
04263          */
04264         cr_parser_try_to_skip_spaces_and_comments (a_this);
04265         status = cr_parser_parse_declaration (a_this, &property,
04266                                               &css_expression, &important);
04267         if (status == CR_OK) {
04268                 /*
04269                  *here, call the relevant SAC handler.
04270                  */
04271                 cr_term_ref (css_expression);
04272                 if (PRIVATE (a_this)->sac_handler &&
04273                     PRIVATE (a_this)->sac_handler->property) {
04274                         PRIVATE (a_this)->sac_handler->property
04275                                 (PRIVATE (a_this)->sac_handler,
04276                                  property, css_expression, important);
04277                 }
04278                 ENSURE_PARSING_COND (css_expression && property);
04279         }
04280         /*free the data structures allocated during last parsing. */
04281         if (property) {
04282                 cr_string_destroy (property);
04283                 property = NULL;
04284         }
04285         if (css_expression) {
04286                 cr_term_unref (css_expression);
04287                 css_expression = NULL;
04288         }
04289         for (;;) {
04290                 PEEK_NEXT_CHAR (a_this, &next_char);
04291                 if (next_char == ';') {
04292                         READ_NEXT_CHAR (a_this, &cur_char);
04293                 } else {
04294                         break;
04295                 }
04296                 cr_parser_try_to_skip_spaces_and_comments (a_this);
04297                 status = cr_parser_parse_declaration (a_this, 
04298                                                       &property,
04299                                                       &css_expression,
04300                                                       &important);
04301                 if (status != CR_OK)
04302                         break;
04303                 /*
04304                  *here, call the relevant SAC handler.
04305                  */
04306                 cr_term_ref (css_expression);
04307                 if (PRIVATE (a_this)->sac_handler->property) {
04308                         PRIVATE (a_this)->sac_handler->property
04309                                 (PRIVATE (a_this)->sac_handler,
04310                                  property, css_expression, important);
04311                 }
04312                 /*
04313                  *Then, free the data structures allocated during 
04314                  *last parsing.
04315                  */
04316                 if (property) {
04317                         cr_string_destroy (property);
04318                         property = NULL;
04319                 }
04320                 if (css_expression) {
04321                         cr_term_unref (css_expression);
04322                         css_expression = NULL;
04323                 }
04324         }
04325         cr_parser_try_to_skip_spaces_and_comments (a_this);
04326         READ_NEXT_CHAR (a_this, &cur_char);
04327         ENSURE_PARSING_COND (cur_char == '}');
04328         /*
04329          *here, call the relevant SAC handler.
04330          */
04331         if (PRIVATE (a_this)->sac_handler->end_font_face) {
04332                 PRIVATE (a_this)->sac_handler->end_font_face
04333                         (PRIVATE (a_this)->sac_handler);
04334         }
04335         cr_parser_try_to_skip_spaces_and_comments (a_this);
04336 
04337         if (token) {
04338                 cr_token_destroy (token);
04339                 token = NULL;
04340         }
04341         cr_parser_clear_errors (a_this);
04342         PRIVATE (a_this)->state = FONT_FACE_PARSED_STATE;
04343         return CR_OK;
04344 
04345       error:
04346         if (token) {
04347                 cr_token_destroy (token);
04348                 token = NULL;
04349         }
04350         if (property) {
04351                 cr_string_destroy (property);
04352                 property = NULL;
04353         }
04354         if (css_expression) {
04355                 cr_term_destroy (css_expression);
04356                 css_expression = NULL;
04357         }
04358         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
04359         return status;
04360 }
04361 
04362 /**
04363  * cr_parser_parse:
04364  *@a_this: the current instance of #CRParser.
04365  *
04366  *Parses the data that comes from the
04367  *input previously associated to the current instance of
04368  *#CRParser.
04369  *
04370  *Returns CR_OK upon succesful completion, an error code otherwise.
04371  */
04372 enum CRStatus
04373 cr_parser_parse (CRParser * a_this)
04374 {
04375         enum CRStatus status = CR_ERROR;
04376 
04377         g_return_val_if_fail (a_this && PRIVATE (a_this)
04378                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
04379 
04380         if (PRIVATE (a_this)->use_core_grammar == FALSE) {
04381                 status = cr_parser_parse_stylesheet (a_this);
04382         } else {
04383                 status = cr_parser_parse_stylesheet_core (a_this);
04384         }
04385 
04386         return status;
04387 }
04388 
04389 /**
04390  * cr_parser_set_tknzr:
04391  * @a_this: the current instance of #CRParser;
04392  * @a_tknzr: the new tokenizer.
04393  *
04394  * Returns CR_OK upon successful completion, an error code otherwise.
04395  */
04396 enum CRStatus
04397 cr_parser_set_tknzr (CRParser * a_this, CRTknzr * a_tknzr)
04398 {
04399         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
04400 
04401         if (PRIVATE (a_this)->tknzr) {
04402                 cr_tknzr_unref (PRIVATE (a_this)->tknzr);
04403         }
04404 
04405         PRIVATE (a_this)->tknzr = a_tknzr;
04406 
04407         if (a_tknzr)
04408                 cr_tknzr_ref (a_tknzr);
04409 
04410         return CR_OK;
04411 }
04412 
04413 /**
04414  * cr_parser_get_tknzr:
04415  *@a_this: the current instance of #CRParser
04416  *@a_tknzr: out parameter. The returned tokenizer
04417  *
04418  *Getter of the parser's underlying tokenizer
04419  * 
04420  *Returns CR_OK upon succesful completion, an error code
04421  *otherwise
04422  */
04423 enum CRStatus
04424 cr_parser_get_tknzr (CRParser * a_this, CRTknzr ** a_tknzr)
04425 {
04426         g_return_val_if_fail (a_this && PRIVATE (a_this)
04427                               && a_tknzr, CR_BAD_PARAM_ERROR);
04428 
04429         *a_tknzr = PRIVATE (a_this)->tknzr;
04430         return CR_OK;
04431 }
04432 
04433 /**
04434  * cr_parser_get_parsing_location:
04435  *@a_this: the current instance of #CRParser
04436  *@a_loc: the parsing location to get.
04437  *
04438  *Gets the current parsing location.
04439  *
04440  *Returns CR_OK upon succesful completion, an error code
04441  *otherwise.
04442  */
04443 enum CRStatus 
04444 cr_parser_get_parsing_location (CRParser const *a_this,
04445                                 CRParsingLocation *a_loc)
04446 {
04447         g_return_val_if_fail (a_this 
04448                               && PRIVATE (a_this)
04449                               && a_loc, CR_BAD_PARAM_ERROR) ;
04450 
04451         return cr_tknzr_get_parsing_location 
04452                 (PRIVATE (a_this)->tknzr, a_loc) ;
04453 }
04454 
04455 /**
04456  * cr_parser_parse_buf:
04457  *@a_this: the current instance of #CRparser
04458  *@a_buf: the input buffer
04459  *@a_len: the length of the input buffer
04460  *@a_enc: the encoding of the buffer
04461  *
04462  *Parses a stylesheet from a buffer
04463  *
04464  *Returns CR_OK upon successful completion, an error code otherwise.
04465  */
04466 enum CRStatus
04467 cr_parser_parse_buf (CRParser * a_this,
04468                      const guchar * a_buf,
04469                      gulong a_len, enum CREncoding a_enc)
04470 {
04471         enum CRStatus status = CR_ERROR;
04472         CRTknzr *tknzr = NULL;
04473 
04474         g_return_val_if_fail (a_this && PRIVATE (a_this)
04475                               && a_buf, CR_BAD_PARAM_ERROR);
04476 
04477         tknzr = cr_tknzr_new_from_buf ((guchar*)a_buf, a_len, a_enc, FALSE);
04478 
04479         g_return_val_if_fail (tknzr != NULL, CR_ERROR);
04480 
04481         status = cr_parser_set_tknzr (a_this, tknzr);
04482         g_return_val_if_fail (status == CR_OK, CR_ERROR);
04483 
04484         status = cr_parser_parse (a_this);
04485 
04486         return status;
04487 }
04488 
04489 /**
04490  * cr_parser_destroy:
04491  *@a_this: the current instance of #CRParser to
04492  *destroy.
04493  *
04494  *Destroys the current instance
04495  *of #CRParser.
04496  */
04497 void
04498 cr_parser_destroy (CRParser * a_this)
04499 {
04500         g_return_if_fail (a_this && PRIVATE (a_this));
04501 
04502         if (PRIVATE (a_this)->tknzr) {
04503                 if (cr_tknzr_unref (PRIVATE (a_this)->tknzr) == TRUE)
04504                         PRIVATE (a_this)->tknzr = NULL;
04505         }
04506 
04507         if (PRIVATE (a_this)->sac_handler) {
04508                 cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
04509                 PRIVATE (a_this)->sac_handler = NULL;
04510         }
04511 
04512         if (PRIVATE (a_this)->err_stack) {
04513                 cr_parser_clear_errors (a_this);
04514                 PRIVATE (a_this)->err_stack = NULL;
04515         }
04516 
04517         if (PRIVATE (a_this)) {
04518                 g_free (PRIVATE (a_this));
04519                 PRIVATE (a_this) = NULL;
04520         }
04521 
04522         if (a_this) {
04523                 g_free (a_this);
04524                 a_this = NULL;  /*useless. Just for the sake of coherence */
04525         }
04526 }