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

cr-parser.c

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

Generated on Sat Mar 20 02:38:43 2004 for Libcroco by doxygen 1.3.5