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

Generated on Fri Oct 29 08:29:12 2004 for Libcroco by  doxygen 1.3.9.1