Code

emf import : recalculate text alignment for rotated text (Bug 341847)
[inkscape.git] / src / libcroco / cr-parser.c
1 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
3 /*
4  * This file is part of The Croco Library
5  *
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the 
9  * GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20  * USA
21  *
22  * Author: Dodji Seketeli
23  * See COPYRIGHTS file for copyrights information.
24  */
26 /**
27  *@file
28  *The definition of the #CRParser class.
29  */
31 #include "string.h"
32 #include "cr-parser.h"
33 #include "cr-num.h"
34 #include "cr-term.h"
35 #include "cr-simple-sel.h"
36 #include "cr-attr-sel.h"
38 /*
39  *Random notes: 
40  *CSS core syntax vs CSS level 2 syntax
41  *=====================================
42  *
43  *One must keep in mind
44  *that css UA must comply with two syntax.
45  *
46  *1/the specific syntax that defines the css language
47  *for a given level of specificatin (e.g css2 syntax
48  *defined in appendix D.1 of the css2 spec)
49  *
50  *2/the core (general) syntax that is there to allow
51  *UAs to parse style sheets written in levels of CSS that
52  *didn't exist at the time the UAs were created.
53  *
54  *the name  of parsing functions (or methods) contained in this  file
55  *follows the following scheme: cr_parser_parse_<production_name> (...) ;
56  *where <production_name> is the name 
57  *of a production of the css2 language.
58  *When a given production is 
59  *defined by the css2 level grammar *and* by the
60  *css core syntax, there will be two functions to parse that production:
61  *one will parse the production defined by the css2 level grammar and the
62  *other will parse the production defined by the css core grammar.
63  *The css2 level grammar related parsing function will be called:
64  *cr_parser_parse_<production_name> (...) ;
65  *Then css core grammar related parsing function will be called:
66  *cr_parser_parse_<production_name>_core (...) ;
67  *
68  *If a production is defined only by the css core grammar, then
69  *it will be named:
70  *cr_parser_parse_<production_name>_core (...) ;
71  */
73 typedef struct _CRParserError CRParserError;
75 /**
76  *An abstraction of an error reported by by the
77  *parsing routines.
78  */
79 struct _CRParserError {
80         guchar *msg;
81         enum CRStatus status;
82         glong line;
83         glong column;
84         glong byte_num;
85 };
87 enum CRParserState {
88         READY_STATE = 0,
89         TRY_PARSE_CHARSET_STATE,
90         CHARSET_PARSED_STATE,
91         TRY_PARSE_IMPORT_STATE,
92         IMPORT_PARSED_STATE,
93         TRY_PARSE_RULESET_STATE,
94         RULESET_PARSED_STATE,
95         TRY_PARSE_MEDIA_STATE,
96         MEDIA_PARSED_STATE,
97         TRY_PARSE_PAGE_STATE,
98         PAGE_PARSED_STATE,
99         TRY_PARSE_FONT_FACE_STATE,
100         FONT_FACE_PARSED_STATE
101 } ;
103 /**
104  *The private attributes of
105  *#CRParser.
106  */
107 struct _CRParserPriv {
108         /**
109          *The tokenizer
110          */
111         CRTknzr *tknzr;
113         /**
114          *The sac handlers to call
115          *to notify the parsing of
116          *the css2 constructions.
117          */
118         CRDocHandler *sac_handler;
120         /**
121          *A stack of errors reported
122          *by the parsing routines.
123          *Contains instance of #CRParserError.
124          *This pointer is the top of the stack.
125          */
126         GList *err_stack;
128         enum CRParserState state;
129         gboolean resolve_import;
130         gboolean is_case_sensitive;
131         gboolean use_core_grammar;
132 };
134 #define PRIVATE(obj) ((obj)->priv)
136 #define CHARS_TAB_SIZE 12
138 /**
139  *return TRUE if the character is a number ([0-9]), FALSE otherwise
140  *@param a_char the char to test.
141  */
142 #define IS_NUM(a_char) (((a_char) >= '0' && (a_char) <= '9')?TRUE:FALSE)
144 /**
145  *Checks if 'status' equals CR_OK. If not, goto the 'error' label.
146  *
147  *@param status the status (of type enum CRStatus) to test.
148  *@param is_exception if set to FALSE, the final status returned 
149  *by the current function will be CR_PARSING_ERROR. If set to TRUE, the
150  *current status will be the current value of the 'status' variable.
151  *
152  */
153 #define CHECK_PARSING_STATUS(status, is_exception) \
154 if ((status) != CR_OK) \
155 { \
156         if (is_exception == FALSE) \
157         { \
158                 status = CR_PARSING_ERROR ; \
159         } \
160         goto error ; \
163 /**
164  *same as CHECK_PARSING_STATUS() but this one pushes an error
165  *on the parser error stack when an error arises.
166  *@param a_this the current instance of #CRParser .
167  *@param a_status the status to check. Is of type enum #CRStatus.
168  *@param a_is_exception in case of error, if is TRUE, the status
169  *is set to CR_PARSING_ERROR before goto error. If is false, the
170  *real low level status is kept and will be returned by the
171  *upper level function that called this macro. Usally,this must
172  *be set to FALSE.
173  *
174  */
175 #define CHECK_PARSING_STATUS_ERR(a_this, a_status, a_is_exception,\
176                                  a_err_msg, a_err_status) \
177 if ((a_status) != CR_OK) \
178 { \
179         if (a_is_exception == FALSE) a_status = CR_PARSING_ERROR ; \
180         cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \
181         goto error ; \
184 /**
185  *Peeks the next char from the input stream of the current parser
186  *by invoking cr_tknzr_input_peek_char().
187  *invokes CHECK_PARSING_STATUS on the status returned by
188  *cr_tknzr_peek_char().
189  *
190  *@param a_this the current instance of #CRParser.
191  *@param a_to_char a pointer to the char where to store the
192  *char peeked.
193  */
194 #define PEEK_NEXT_CHAR(a_this, a_to_char) \
195 {\
196 enum CRStatus status ; \
197 status = cr_tknzr_peek_char  (PRIVATE (a_this)->tknzr, a_to_char) ; \
198 CHECK_PARSING_STATUS (status, TRUE) \
201 /**
202  *Reads the next char from the input stream of the current parser.
203  *In case of error, jumps to the "error:" label located in the
204  *function where this macro is called.
205  *@param a_this the curent instance of #CRParser
206  *@param to_char a pointer to the guint32 char where to store
207  *the character read.
208  */
209 #define READ_NEXT_CHAR(a_this, a_to_char) \
210 status = cr_tknzr_read_char (PRIVATE (a_this)->tknzr, a_to_char) ; \
211 CHECK_PARSING_STATUS (status, TRUE)
213 /**
214  *Gets information about the current position in
215  *the input of the parser.
216  *In case of failure, this macro returns from the 
217  *calling function and
218  *returns a status code of type enum #CRStatus.
219  *@param a_this the current instance of #CRParser.
220  *@param a_pos out parameter. A pointer to the position 
221  *inside the current parser input. Must
222  */
223 #define RECORD_INITIAL_POS(a_this, a_pos) \
224 status = cr_tknzr_get_cur_pos (PRIVATE \
225 (a_this)->tknzr, a_pos) ; \
226 g_return_val_if_fail (status == CR_OK, status)
228 /**
229  *Gets the address of the current byte inside the
230  *parser input.
231  *@param parser the current instance of #CRParser.
232  *@param addr out parameter a pointer (guchar*)
233  *to where the address  must be put.
234  */
235 #define RECORD_CUR_BYTE_ADDR(a_this, a_addr) \
236 status = cr_tknzr_get_cur_byte_addr \
237             (PRIVATE (a_this)->tknzr, a_addr) ; \
238 CHECK_PARSING_STATUS (status, TRUE)
240 /**
241  *Peeks a byte from the topmost parser input at
242  *a given offset from the current position.
243  *If it fails, goto the "error:" label.
244  *
245  *@param a_parser the current instance of #CRParser.
246  *@param a_offset the offset of the byte to peek, the
247  *current byte having the offset '0'.
248  *@param a_byte_ptr out parameter a pointer (guchar*) to
249  *where the peeked char is to be stored.
250  */
251 #define PEEK_BYTE(a_parser, a_offset, a_byte_ptr) \
252 status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr, \
253                               a_offset, \
254                               a_byte_ptr) ; \
255 CHECK_PARSING_STATUS (status, TRUE) ;
257 #define BYTE(a_parser, a_offset, a_eof) \
258 cr_tknzr_peek_byte2 (PRIVATE (a_this)->tknzr, a_offset, a_eof)
260 /**
261  *Reads a byte from the topmost parser input
262  *steam.
263  *If it fails, goto the "error" label.
264  *@param a_this the current instance of #CRParser.
265  *@param a_byte_ptr the guchar * where to put the read char.
266  */
267 #define READ_NEXT_BYTE(a_this, a_byte_ptr) \
268 status = cr_tknzr_read_byte (PRIVATE (a_this)->tknzr, a_byte_ptr) ; \
269 CHECK_PARSING_STATUS (status, TRUE) ;
271 /**
272  *Skips a given number of byte in the topmost
273  *parser input. Don't update line and column number.
274  *In case of error, jumps to the "error:" label
275  *of the surrounding function.
276  *@param a_parser the current instance of #CRParser.
277  *@param a_nb_bytes the number of bytes to skip.
278  */
279 #define SKIP_BYTES(a_this, a_nb_bytes) \
280 status = cr_tknzr_seek_index (PRIVATE (a_this)->tknzr, \
281                                         CR_SEEK_CUR, a_nb_bytes) ; \
282 CHECK_PARSING_STATUS (status, TRUE) ;
284 /**
285  *Skip utf8 encoded characters.
286  *Updates line and column numbers.
287  *@param a_parser the current instance of #CRParser.
288  *@param a_nb_chars the number of chars to skip. Must be of
289  *type glong.
290  */
291 #define SKIP_CHARS(a_parser, a_nb_chars) \
292 { \
293 glong nb_chars = a_nb_chars ; \
294 status = cr_tknzr_consume_chars \
295      (PRIVATE (a_parser)->tknzr,0, &nb_chars) ; \
296 CHECK_PARSING_STATUS (status, TRUE) ; \
299 /**
300  *Tests the condition and if it is false, sets
301  *status to "CR_PARSING_ERROR" and goto the 'error'
302  *label.
303  *@param condition the condition to test.
304  */
305 #define ENSURE_PARSING_COND(condition) \
306 if (! (condition)) {status = CR_PARSING_ERROR; goto error ;}
308 #define ENSURE_PARSING_COND_ERR(a_this, a_condition, \
309                                 a_err_msg, a_err_status) \
310 if (! (a_condition)) \
311 { \
312         status = CR_PARSING_ERROR; \
313         cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \
314         goto error ; \
317 #define GET_NEXT_TOKEN(a_this, a_token_ptr) \
318 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, \
319                                   a_token_ptr) ; \
320 ENSURE_PARSING_COND (status == CR_OK) ;
322 #ifdef WITH_UNICODE_ESCAPE_AND_RANGE
323 static enum CRStatus cr_parser_parse_unicode_escape (CRParser * a_this,
324                                                      guint32 * a_unicode);
325 static enum CRStatus cr_parser_parse_escape (CRParser * a_this,
326                                              guint32 * a_esc_code);
328 static enum CRStatus cr_parser_parse_unicode_range (CRParser * a_this,
329                                                     CRString ** a_inf,
330                                                     CRString ** a_sup);
331 #endif
333 static enum CRStatus cr_parser_parse_stylesheet_core (CRParser * a_this);
335 static enum CRStatus cr_parser_parse_atrule_core (CRParser * a_this);
337 static enum CRStatus cr_parser_parse_ruleset_core (CRParser * a_this);
339 static enum CRStatus cr_parser_parse_selector_core (CRParser * a_this);
341 static enum CRStatus cr_parser_parse_declaration_core (CRParser * a_this);
343 static enum CRStatus cr_parser_parse_any_core (CRParser * a_this);
345 static enum CRStatus cr_parser_parse_block_core (CRParser * a_this);
347 static enum CRStatus cr_parser_parse_value_core (CRParser * a_this);
349 static enum CRStatus cr_parser_parse_string (CRParser * a_this,
350                                              CRString ** a_str);
352 static enum CRStatus cr_parser_parse_ident (CRParser * a_this,
353                                             CRString ** a_str);
355 static enum CRStatus cr_parser_parse_uri (CRParser * a_this,
356                                           CRString ** a_str);
358 static enum CRStatus cr_parser_parse_function (CRParser * a_this,
359                                                CRString ** a_func_name,
360                                                CRTerm ** a_expr);
361 static enum CRStatus cr_parser_parse_property (CRParser * a_this,
362                                                CRString ** a_property);
364 static enum CRStatus cr_parser_parse_attribute_selector (CRParser * a_this,
365                                                          CRAttrSel ** a_sel);
367 static enum CRStatus cr_parser_parse_simple_selector (CRParser * a_this,
368                                                       CRSimpleSel ** a_sel);
370 static enum CRStatus cr_parser_parse_simple_sels (CRParser * a_this,
371                                                   CRSimpleSel ** a_sel);
373 static CRParserError *cr_parser_error_new (const guchar * a_msg,
374                                            enum CRStatus);
376 static void cr_parser_error_set_msg (CRParserError * a_this,
377                                      const guchar * a_msg);
379 static void cr_parser_error_dump (CRParserError * a_this);
381 static void cr_parser_error_set_status (CRParserError * a_this,
382                                         enum CRStatus a_status);
384 static void cr_parser_error_set_pos (CRParserError * a_this,
385                                      glong a_line,
386                                      glong a_column, glong a_byte_num);
387 static void
388   cr_parser_error_destroy (CRParserError * a_this);
390 static enum CRStatus cr_parser_push_error (CRParser * a_this,
391                                            const guchar * a_msg,
392                                            enum CRStatus a_status);
394 static enum CRStatus cr_parser_dump_err_stack (CRParser * a_this,
395                                                gboolean a_clear_errs);
396 static enum CRStatus
397   cr_parser_clear_errors (CRParser * a_this);
399 /*****************************
400  *error managemet methods
401  *****************************/
403 /**
404  *Constructor of #CRParserError class.
405  *@param a_msg the brute error message.
406  *@param a_status the error status.
407  *@return the newly built instance of #CRParserError.
408  */
409 static CRParserError *
410 cr_parser_error_new (const guchar * a_msg, enum CRStatus a_status)
412         CRParserError *result = (CRParserError *)g_try_malloc (sizeof (CRParserError));
414         if (result == NULL) {
415                 cr_utils_trace_info ("Out of memory");
416                 return NULL;
417         }
419         memset (result, 0, sizeof (CRParserError));
421         cr_parser_error_set_msg (result, a_msg);
422         cr_parser_error_set_status (result, a_status);
424         return result;
427 /**
428  *Sets the message associated to this instance of #CRError.
429  *@param a_this the current instance of #CRParserError.
430  *@param a_msg the new message.
431  */
432 static void
433 cr_parser_error_set_msg (CRParserError * a_this, const guchar * a_msg)
435         g_return_if_fail (a_this);
437         if (a_this->msg) {
438                 g_free (a_this->msg);
439         }
441         a_this->msg = (guchar *)g_strdup ((gchar *)a_msg);
444 /**
445  *Sets the error status.
446  *@param a_this the current instance of #CRParserError.
447  *@param a_status the new error status.
448  *
449  */
450 static void
451 cr_parser_error_set_status (CRParserError * a_this, enum CRStatus a_status)
453         g_return_if_fail (a_this);
455         a_this->status = a_status;
458 /**
459  *Sets the position of the parser error.
460  *@param a_this the current instance of #CRParserError.
461  *@param a_line the line number.
462  *@param a_column the column number.
463  *@param a_byte_num the byte number.
464  */
465 static void
466 cr_parser_error_set_pos (CRParserError * a_this,
467                          glong a_line, glong a_column, glong a_byte_num)
469         g_return_if_fail (a_this);
471         a_this->line = a_line;
472         a_this->column = a_column;
473         a_this->byte_num = a_byte_num;
476 static void
477 cr_parser_error_dump (CRParserError * a_this)
479         g_return_if_fail (a_this);
481         g_printerr ("parsing error: %ld:%ld:", a_this->line, a_this->column);
483         g_printerr ("%s\n", a_this->msg);
486 /**
487  *The destructor of #CRParserError.
488  *@param a_this the current instance of #CRParserError.
489  */
490 static void
491 cr_parser_error_destroy (CRParserError * a_this)
493         g_return_if_fail (a_this);
495         if (a_this->msg) {
496                 g_free (a_this->msg);
497                 a_this->msg = NULL;
498         }
500         g_free (a_this);
503 /**
504  *Pushes an error on the parser error stack.
505  *@param a_this the current instance of #CRParser.
506  *@param a_msg the error message.
507  *@param a_status the error status.
508  *@return CR_OK upon successful completion, an error code otherwise.
509  */
510 static enum CRStatus
511 cr_parser_push_error (CRParser * a_this,
512                       const guchar * a_msg, enum CRStatus a_status)
514         enum CRStatus status = CR_OK;
516         CRParserError *error = NULL;
517         CRInputPos pos;
519         g_return_val_if_fail (a_this && PRIVATE (a_this)
520                               && a_msg, CR_BAD_PARAM_ERROR);
522         error = cr_parser_error_new (a_msg, a_status);
524         g_return_val_if_fail (error, CR_ERROR);
526         RECORD_INITIAL_POS (a_this, &pos);
528         cr_parser_error_set_pos
529                 (error, pos.line, pos.col, pos.next_byte_index - 1);
531         PRIVATE (a_this)->err_stack =
532                 g_list_prepend (PRIVATE (a_this)->err_stack, error);
534         if (PRIVATE (a_this)->err_stack == NULL)
535                 goto error;
537         return CR_OK;
539       error:
541         if (error) {
542                 cr_parser_error_destroy (error);
543                 error = NULL;
544         }
546         return status;
549 /**
550  *Dumps the error stack using g_printerr.
551  *@param a_this the current instance of #CRParser.
552  *@param a_clear_errs whether to clear the error stack
553  *after the dump or not.
554  *@return CR_OK upon successfull completion, an error code
555  *otherwise.
556  */
557 static enum CRStatus
558 cr_parser_dump_err_stack (CRParser * a_this, gboolean a_clear_errs)
560         GList *cur = NULL;
562         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
564         if (PRIVATE (a_this)->err_stack == NULL)
565                 return CR_OK;
567         for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
568                 cr_parser_error_dump ((CRParserError *) cur->data);
569         }
571         if (a_clear_errs == TRUE) {
572                 cr_parser_clear_errors (a_this);
573         }
575         return CR_OK;
578 /**
579  *Clears all the errors contained in the parser error stack.
580  *Frees all the errors, and the stack that contains'em.
581  *@param a_this the current instance of #CRParser.
582  */
583 static enum CRStatus
584 cr_parser_clear_errors (CRParser * a_this)
586         GList *cur = NULL;
588         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
590         for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
591                 if (cur->data) {
592                         cr_parser_error_destroy ((CRParserError *)
593                                                  cur->data);
594                 }
595         }
597         if (PRIVATE (a_this)->err_stack) {
598                 g_list_free (PRIVATE (a_this)->err_stack);
599                 PRIVATE (a_this)->err_stack = NULL;
600         }
602         return CR_OK;
605 /**
606  *Same as cr_parser_try_to_skip_spaces() but this one skips
607  *spaces and comments.
608  *
609  *@param a_this the current instance of #CRParser.
610  *@return CR_OK upon successfull completion, an error code otherwise.
611  */
612 enum CRStatus
613 cr_parser_try_to_skip_spaces_and_comments (CRParser * a_this)
615         enum CRStatus status = CR_ERROR;
616         CRToken *token = NULL;
618         g_return_val_if_fail (a_this && PRIVATE (a_this)
619                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
620         do {
621                 if (token) {
622                         cr_token_destroy (token);
623                         token = NULL;
624                 }
626                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
627                                                   &token);
628                 if (status != CR_OK)
629                         goto error;
630         }
631         while ((token != NULL)
632                && (token->type == COMMENT_TK || token->type == S_TK));
634         cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
636         return status;
638       error:
640         if (token) {
641                 cr_token_destroy (token);
642                 token = NULL;
643         }
645         return status;
648 /***************************************
649  *End of Parser input handling routines
650  ***************************************/
653 /*************************************
654  *Non trivial terminal productions
655  *parsing routines
656  *************************************/
658 /**
659  *Parses a css stylesheet following the core css grammar.
660  *This is mainly done for test purposes.
661  *During the parsing, no callback is called. This is just
662  *to validate that the stylesheet is well formed according to the
663  *css core syntax.
664  *stylesheet  : [ CDO | CDC | S | statement ]*;
665  *@param a_this the current instance of #CRParser.
666  *@return CR_OK upon successful completion, an error code otherwise.
667  */
668 static enum CRStatus
669 cr_parser_parse_stylesheet_core (CRParser * a_this)
671         CRToken *token = NULL;
672         CRInputPos init_pos;
673         enum CRStatus status = CR_ERROR;
675         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
677         RECORD_INITIAL_POS (a_this, &init_pos);
679  continue_parsing:
681         if (token) {
682                 cr_token_destroy (token);
683                 token = NULL;
684         }
686         cr_parser_try_to_skip_spaces_and_comments (a_this);
687         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
688         if (status == CR_END_OF_INPUT_ERROR) {
689                 status = CR_OK;
690                 goto done;
691         } else if (status != CR_OK) {
692                 goto error;
693         }
695         switch (token->type) {
697         case CDO_TK:
698         case CDC_TK:
699                 goto continue_parsing;
700                 break;
701         default:
702                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
703                                                token);
704                 CHECK_PARSING_STATUS (status, TRUE);
705                 token = NULL;
706                 status = cr_parser_parse_statement_core (a_this);
707                 cr_parser_clear_errors (a_this);
708                 if (status == CR_OK) {
709                         goto continue_parsing;
710                 } else if (status == CR_END_OF_INPUT_ERROR) {
711                         goto done;
712                 } else {
713                         goto error;
714                 }
715         }
717  done:
718         if (token) {
719                 cr_token_destroy (token);
720                 token = NULL;
721         }
723         cr_parser_clear_errors (a_this);
724         return CR_OK;
726  error:
727         cr_parser_push_error
728                 (a_this, (guchar *)"could not recognize next production", CR_ERROR);
730         cr_parser_dump_err_stack (a_this, TRUE);
732         if (token) {
733                 cr_token_destroy (token);
734                 token = NULL;
735         }
737         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
739         return status;
742 /**
743  *Parses an at-rule as defined by the css core grammar
744  *in chapter 4.1 in the css2 spec.
745  *at-rule     : ATKEYWORD S* any* [ block | ';' S* ];
746  *@param a_this the current instance of #CRParser.
747  *@return CR_OK upon successfull completion, an error code
748  *otherwise.
749  */
750 static enum CRStatus
751 cr_parser_parse_atrule_core (CRParser * a_this)
753         CRToken *token = NULL;
754         CRInputPos init_pos;
755         enum CRStatus status = CR_ERROR;
757         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
759         RECORD_INITIAL_POS (a_this, &init_pos);
761         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
762                                           &token);
763         ENSURE_PARSING_COND (status == CR_OK
764                              && token
765                              &&
766                              (token->type == ATKEYWORD_TK
767                               || token->type == IMPORT_SYM_TK
768                               || token->type == PAGE_SYM_TK
769                               || token->type == MEDIA_SYM_TK
770                               || token->type == FONT_FACE_SYM_TK
771                               || token->type == CHARSET_SYM_TK));
773         cr_token_destroy (token);
774         token = NULL;
776         cr_parser_try_to_skip_spaces_and_comments (a_this);
778         do {
779                 status = cr_parser_parse_any_core (a_this);
780         } while (status == CR_OK);
782         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
783                                           &token);
784         ENSURE_PARSING_COND (status == CR_OK && token);
786         if (token->type == CBO_TK) {
787                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, 
788                                       token);
789                 token = NULL;
790                 status = cr_parser_parse_block_core (a_this);
791                 CHECK_PARSING_STATUS (status,
792                                       FALSE);
793                 goto done;
794         } else if (token->type == SEMICOLON_TK) {
795                 goto done;
796         } else {
797                 status = CR_PARSING_ERROR ;
798                 goto error;
799         }
801  done:
802         if (token) {
803                 cr_token_destroy (token);
804                 token = NULL;
805         }
806         return CR_OK;
808  error:
809         if (token) {
810                 cr_token_destroy (token);
811                 token = NULL;
812         }
813         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr,
814                               &init_pos);
815         return status;
818 /**
819  *Parses a ruleset as defined by the css core grammar in chapter
820  *4.1 of the css2 spec.
821  *ruleset ::= selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*;
822  *@param a_this the current instance of #CRParser.
823  *@return CR_OK upon successfull completion, an error code otherwise.
824  */
825 static enum CRStatus
826 cr_parser_parse_ruleset_core (CRParser * a_this)
828         CRToken *token = NULL;
829         CRInputPos init_pos;
830         enum CRStatus status = CR_ERROR;
832         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
833         RECORD_INITIAL_POS (a_this, &init_pos);
835         status = cr_parser_parse_selector_core (a_this);
837         ENSURE_PARSING_COND (status == CR_OK
838                              || status == CR_PARSING_ERROR
839                              || status == CR_END_OF_INPUT_ERROR);
841         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
842         ENSURE_PARSING_COND (status == CR_OK && token
843                              && token->type == CBO_TK);
844         cr_token_destroy (token);
845         token = NULL;
847         cr_parser_try_to_skip_spaces_and_comments (a_this);
848         status = cr_parser_parse_declaration_core (a_this);
850       parse_declaration_list:
851         if (token) {
852                 cr_token_destroy (token);
853                 token = NULL;
854         }
856         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
857         ENSURE_PARSING_COND (status == CR_OK && token);
858         if (token->type == CBC_TK) {
859                 goto done;
860         }
862         ENSURE_PARSING_COND (status == CR_OK
863                              && token && token->type == SEMICOLON_TK);
865         cr_token_destroy (token);
866         token = NULL;
867         cr_parser_try_to_skip_spaces_and_comments (a_this);
868         status = cr_parser_parse_declaration_core (a_this);
869         cr_parser_clear_errors (a_this);
870         ENSURE_PARSING_COND (status == CR_OK || status == CR_PARSING_ERROR);
871         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
872         ENSURE_PARSING_COND (status == CR_OK && token);
873         if (token->type == CBC_TK) {
874                 cr_token_destroy (token);
875                 token = NULL;
876                 cr_parser_try_to_skip_spaces_and_comments (a_this);
877                 goto done;
878         } else {
879                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
880                                                token);
881                 token = NULL;
882                 goto parse_declaration_list;
883         }
885       done:
886         if (token) {
887                 cr_token_destroy (token);
888                 token = NULL;
889         }
891         if (status == CR_OK) {
892                 return CR_OK;
893         }
895       error:
896         if (token) {
897                 cr_token_destroy (token);
898                 token = NULL;
899         }
901         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
903         return status;
906 /**
907  *Parses a "selector" as specified by the css core 
908  *grammar.
909  *selector    : any+;
910  *@param a_this the current instance of #CRParser.
911  *@return CR_OK upon successfull completion, an error code
912  *otherwise.
913  */
914 static enum CRStatus
915 cr_parser_parse_selector_core (CRParser * a_this)
917         CRToken *token = NULL;
918         CRInputPos init_pos;
919         enum CRStatus status = CR_ERROR;
921         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
923         RECORD_INITIAL_POS (a_this, &init_pos);
925         status = cr_parser_parse_any_core (a_this);
926         CHECK_PARSING_STATUS (status, FALSE);
928         do {
929                 status = cr_parser_parse_any_core (a_this);
931         } while (status == CR_OK);
933         return CR_OK;
935  error:
936         if (token) {
937                 cr_token_destroy (token);
938                 token = NULL;
939         }
941         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
943         return status;
946 /**
947  *Parses a "block" as defined in the css core grammar
948  *in chapter 4.1 of the css2 spec.
949  *block ::= '{' S* [ any | block | ATKEYWORD S* | ';' ]* '}' S*;
950  *@param a_this the current instance of #CRParser.
951  *FIXME: code this function.
952  */
953 static enum CRStatus
954 cr_parser_parse_block_core (CRParser * a_this)
956         CRToken *token = NULL;
957         CRInputPos init_pos;
958         enum CRStatus status = CR_ERROR;
960         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
962         RECORD_INITIAL_POS (a_this, &init_pos);
964         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
965         ENSURE_PARSING_COND (status == CR_OK && token
966                              && token->type == CBO_TK);
968       parse_block_content:
970         if (token) {
971                 cr_token_destroy (token);
972                 token = NULL;
973         }
975         cr_parser_try_to_skip_spaces_and_comments (a_this);
977         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
978         ENSURE_PARSING_COND (status == CR_OK && token);
980         if (token->type == CBC_TK) {
981                 cr_parser_try_to_skip_spaces_and_comments (a_this);
982                 goto done;
983         } else if (token->type == SEMICOLON_TK) {
984                 goto parse_block_content;
985         } else if (token->type == ATKEYWORD_TK) {
986                 cr_parser_try_to_skip_spaces_and_comments (a_this);
987                 goto parse_block_content;
988         } else if (token->type == CBO_TK) {
989                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
990                 token = NULL;
991                 status = cr_parser_parse_block_core (a_this);
992                 CHECK_PARSING_STATUS (status, FALSE);
993                 goto parse_block_content;
994         } else {
995                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
996                 token = NULL;
997                 status = cr_parser_parse_any_core (a_this);
998                 CHECK_PARSING_STATUS (status, FALSE);
999                 goto parse_block_content;
1000         }
1002       done:
1003         if (token) {
1004                 cr_token_destroy (token);
1005                 token = NULL;
1006         }
1008         if (status == CR_OK)
1009                 return CR_OK;
1011       error:
1012         if (token) {
1013                 cr_token_destroy (token);
1014                 token = NULL;
1015         }
1017         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1019         return status;
1022 static enum CRStatus
1023 cr_parser_parse_declaration_core (CRParser * a_this)
1025         CRToken *token = NULL;
1026         CRInputPos init_pos;
1027         enum CRStatus status = CR_ERROR;
1028         CRString *prop = NULL;
1030         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1032         RECORD_INITIAL_POS (a_this, &init_pos);
1034         status = cr_parser_parse_property (a_this, &prop);
1035         CHECK_PARSING_STATUS (status, FALSE);
1036         cr_parser_clear_errors (a_this);
1037         ENSURE_PARSING_COND (status == CR_OK && prop);
1038         cr_string_destroy (prop);
1039         prop = NULL;
1041         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1042         ENSURE_PARSING_COND (status == CR_OK
1043                              && token
1044                              && token->type == DELIM_TK
1045                              && token->u.unichar == ':');
1046         cr_token_destroy (token);
1047         token = NULL;
1048         cr_parser_try_to_skip_spaces_and_comments (a_this);
1049         status = cr_parser_parse_value_core (a_this);
1050         CHECK_PARSING_STATUS (status, FALSE);
1052         return CR_OK;
1054       error:
1056         if (prop) {
1057                 cr_string_destroy (prop);
1058                 prop = NULL;
1059         }
1061         if (token) {
1062                 cr_token_destroy (token);
1063                 token = NULL;
1064         }
1066         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1068         return status;
1071 /**
1072  *Parses a "value" production as defined by the css core grammar
1073  *in chapter 4.1.
1074  *value ::= [ any | block | ATKEYWORD S* ]+;
1075  *@param a_this the current instance of #CRParser.
1076  *@return CR_OK upon successfull completion, an error code otherwise.
1077  */
1078 static enum CRStatus
1079 cr_parser_parse_value_core (CRParser * a_this)
1081         CRToken *token = NULL;
1082         CRInputPos init_pos;
1083         enum CRStatus status = CR_ERROR;
1084         glong ref = 0;
1086         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1087         RECORD_INITIAL_POS (a_this, &init_pos);
1089       continue_parsing:
1091         if (token) {
1092                 cr_token_destroy (token);
1093                 token = NULL;
1094         }
1096         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1097         ENSURE_PARSING_COND (status == CR_OK && token);
1099         switch (token->type) {
1100         case CBO_TK:
1101                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
1102                                                token);
1103                 token = NULL;
1104                 status = cr_parser_parse_block_core (a_this);
1105                 CHECK_PARSING_STATUS (status, FALSE);
1106                 ref++;
1107                 goto continue_parsing;
1109         case ATKEYWORD_TK:
1110                 cr_parser_try_to_skip_spaces_and_comments (a_this);
1111                 ref++;
1112                 goto continue_parsing;
1114         default:
1115                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
1116                                                token);
1117                 token = NULL;
1118                 status = cr_parser_parse_any_core (a_this);
1119                 if (status == CR_OK) {
1120                         ref++;
1121                         goto continue_parsing;
1122                 } else if (status == CR_PARSING_ERROR) {
1123                         status = CR_OK;
1124                         goto done;
1125                 } else {
1126                         goto error;
1127                 }
1128         }
1130       done:
1131         if (token) {
1132                 cr_token_destroy (token);
1133                 token = NULL;
1134         }
1136         if (status == CR_OK && ref)
1137                 return CR_OK;
1138       error:
1139         if (token) {
1140                 cr_token_destroy (token);
1141                 token = NULL;
1142         }
1144         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1146         return status;
1149 /**
1150  *Parses an "any" as defined by the css core grammar in the
1151  *css2 spec in chapter 4.1.
1152  *any ::= [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING
1153  *        | DELIM | URI | HASH | UNICODE-RANGE | INCLUDES
1154  *        | FUNCTION | DASHMATCH | '(' any* ')' | '[' any* ']' ] S*;
1155  *
1156  *@param a_this the current instance of #CRParser.
1157  *@return CR_OK upon successfull completion, an error code otherwise.
1158  */
1159 static enum CRStatus
1160 cr_parser_parse_any_core (CRParser * a_this)
1162         CRToken *token1 = NULL,
1163                 *token2 = NULL;
1164         CRInputPos init_pos;
1165         enum CRStatus status = CR_ERROR;
1167         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
1169         RECORD_INITIAL_POS (a_this, &init_pos);
1171         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token1);
1173         ENSURE_PARSING_COND (status == CR_OK && token1);
1175         switch (token1->type) {
1176         case IDENT_TK:
1177         case NUMBER_TK:
1178         case RGB_TK:
1179         case PERCENTAGE_TK:
1180         case DIMEN_TK:
1181         case EMS_TK:
1182         case EXS_TK:
1183         case LENGTH_TK:
1184         case ANGLE_TK:
1185         case FREQ_TK:
1186         case TIME_TK:
1187         case STRING_TK:
1188         case DELIM_TK:
1189         case URI_TK:
1190         case HASH_TK:
1191         case UNICODERANGE_TK:
1192         case INCLUDES_TK:
1193         case DASHMATCH_TK:
1194         case S_TK:
1195         case COMMENT_TK:
1196         case IMPORTANT_SYM_TK:
1197                 status = CR_OK;
1198                 break;
1199         case FUNCTION_TK:
1200                 /*
1201                  *this case isn't specified by the spec but it
1202                  *does happen. So we have to handle it.
1203                  *We must consider function with parameters.
1204                  *We consider parameter as being an "any*" production.
1205                  */
1206                 do {
1207                         status = cr_parser_parse_any_core (a_this);
1208                 } while (status == CR_OK);
1210                 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
1211                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1212                                                   &token2);
1213                 ENSURE_PARSING_COND (status == CR_OK
1214                                      && token2 && token2->type == PC_TK);
1215                 break;
1216         case PO_TK:
1217                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1218                                                   &token2);
1219                 ENSURE_PARSING_COND (status == CR_OK && token2);
1221                 if (token2->type == PC_TK) {
1222                         cr_token_destroy (token2);
1223                         token2 = NULL;
1224                         goto done;
1225                 } else {
1226                         status = cr_tknzr_unget_token
1227                                 (PRIVATE (a_this)->tknzr, token2);
1228                         token2 = NULL;
1229                 }
1231                 do {
1232                         status = cr_parser_parse_any_core (a_this);
1233                 } while (status == CR_OK);
1235                 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
1237                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1238                                                   &token2);
1239                 ENSURE_PARSING_COND (status == CR_OK
1240                                      && token2 && token2->type == PC_TK);
1241                 status = CR_OK;
1242                 break;
1244         case BO_TK:
1245                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1246                                                   &token2);
1247                 ENSURE_PARSING_COND (status == CR_OK && token2);
1249                 if (token2->type == BC_TK) {
1250                         cr_token_destroy (token2);
1251                         token2 = NULL;
1252                         goto done;
1253                 } else {
1254                         status = cr_tknzr_unget_token
1255                                 (PRIVATE (a_this)->tknzr, token2);
1256                         token2 = NULL;
1257                 }
1259                 do {
1260                         status = cr_parser_parse_any_core (a_this);
1261                 } while (status == CR_OK);
1263                 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
1265                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1266                                                   &token2);
1267                 ENSURE_PARSING_COND (status == CR_OK
1268                                      && token2 && token2->type == BC_TK);
1269                 status = CR_OK;
1270                 break;
1271         default:
1272                 status = CR_PARSING_ERROR;
1273                 goto error;
1274         }
1276       done:
1277         if (token1) {
1278                 cr_token_destroy (token1);
1279                 token1 = NULL;
1280         }
1282         if (token2) {
1283                 cr_token_destroy (token2);
1284                 token2 = NULL;
1285         }
1287         return CR_OK;
1289       error:
1291         if (token1) {
1292                 cr_token_destroy (token1);
1293                 token1 = NULL;
1294         }
1296         if (token2) {
1297                 cr_token_destroy (token2);
1298                 token2 = NULL;
1299         }
1301         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1302         return status;
1305 /**
1306  *Parses an attribute selector as defined in the css2 spec in
1307  *appendix D.1:
1308  *attrib ::= '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
1309  *            [ IDENT | STRING ] S* ]? ']'
1310  *
1311  *@param a_this the "this pointer" of the current instance of
1312  *#CRParser .
1313  *@param a_sel out parameter. The successfully parsed attribute selector.
1314  *@return CR_OK upon successfull completion, an error code otherwise.
1315  */
1316 static enum CRStatus
1317 cr_parser_parse_attribute_selector (CRParser * a_this, 
1318                                     CRAttrSel ** a_sel)
1320         enum CRStatus status = CR_OK;
1321         CRInputPos init_pos;
1322         CRToken *token = NULL;
1323         CRAttrSel *result = NULL;
1324         CRParsingLocation location = {0,0,0} ;
1326         g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
1328         RECORD_INITIAL_POS (a_this, &init_pos);
1330         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1331         ENSURE_PARSING_COND (status == CR_OK && token
1332                              && token->type == BO_TK);
1333         cr_parsing_location_copy
1334                 (&location, &token->location) ;
1335         cr_token_destroy (token);
1336         token = NULL;
1338         cr_parser_try_to_skip_spaces_and_comments (a_this);
1340         result = cr_attr_sel_new ();
1341         if (!result) {
1342                 cr_utils_trace_info ("result failed")  ;
1343                 status = CR_OUT_OF_MEMORY_ERROR ;
1344                 goto error ;
1345         }
1346         cr_parsing_location_copy (&result->location,
1347                                   &location) ;
1348         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1349         ENSURE_PARSING_COND (status == CR_OK
1350                              && token && token->type == IDENT_TK);
1352         result->name = token->u.str;
1353         token->u.str = NULL;
1354         cr_token_destroy (token);
1355         token = NULL;
1357         cr_parser_try_to_skip_spaces_and_comments (a_this);
1359         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1360         ENSURE_PARSING_COND (status == CR_OK && token);
1362         if (token->type == INCLUDES_TK) {
1363                 result->match_way = INCLUDES;
1364                 goto parse_right_part;
1365         } else if (token->type == DASHMATCH_TK) {
1366                 result->match_way = DASHMATCH;
1367                 goto parse_right_part;
1368         } else if (token->type == DELIM_TK && token->u.unichar == '=') {
1369                 result->match_way = EQUALS;
1370                 goto parse_right_part;
1371         } else if (token->type == BC_TK) {
1372                 result->match_way = SET;
1373                 goto done;
1374         }
1376  parse_right_part:
1378         if (token) {
1379                 cr_token_destroy (token);
1380                 token = NULL;
1381         }
1383         cr_parser_try_to_skip_spaces_and_comments (a_this);
1385         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1386         ENSURE_PARSING_COND (status == CR_OK && token);
1387         
1388         if (token->type == IDENT_TK) {
1389                 result->value = token->u.str;
1390                 token->u.str = NULL;
1391         } else if (token->type == STRING_TK) {
1392                 result->value = token->u.str;
1393                 token->u.str = NULL;
1394         } else {
1395                 status = CR_PARSING_ERROR;
1396                 goto error;
1397         }
1399         if (token) {
1400                 cr_token_destroy (token);
1401                 token = NULL;
1402         }
1404         cr_parser_try_to_skip_spaces_and_comments (a_this);
1406         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1408         ENSURE_PARSING_COND (status == CR_OK && token
1409                              && token->type == BC_TK);
1410  done:
1411         if (token) {
1412                 cr_token_destroy (token);
1413                 token = NULL;
1414         }
1416         if (*a_sel) {
1417                 status = cr_attr_sel_append_attr_sel (*a_sel, result);
1418                 CHECK_PARSING_STATUS (status, FALSE);
1419         } else {
1420                 *a_sel = result;
1421         }
1423         cr_parser_clear_errors (a_this);
1424         return CR_OK;
1426  error:
1428         if (result) {
1429                 cr_attr_sel_destroy (result);
1430                 result = NULL;
1431         }
1433         if (token) {
1434                 cr_token_destroy (token);
1435                 token = NULL;
1436         }
1438         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1440         return status;
1443 /**
1444  *Parses a "property" as specified by the css2 spec at [4.1.1]:
1445  *property : IDENT S*;
1446  *
1447  *@param a_this the "this pointer" of the current instance of #CRParser.
1448  *@param GString a_property out parameter. The parsed property without the
1449  *trailing spaces. If *a_property is NULL, this function allocates a
1450  *new instance of GString and set it content to the parsed property.
1451  *If not, the property is just appended to a_property's previous content.
1452  *In both cases, it is up to the caller to free a_property.
1453  *@return CR_OK upon successfull completion, CR_PARSING_ERROR if the
1454  *next construction was not a "property", or an error code.
1455  */
1456 static enum CRStatus
1457 cr_parser_parse_property (CRParser * a_this, 
1458                           CRString ** a_property)
1460         enum CRStatus status = CR_OK;
1461         CRInputPos init_pos;
1463         g_return_val_if_fail (a_this && PRIVATE (a_this)
1464                               && PRIVATE (a_this)->tknzr
1465                               && a_property, 
1466                               CR_BAD_PARAM_ERROR);
1468         RECORD_INITIAL_POS (a_this, &init_pos);
1470         status = cr_parser_parse_ident (a_this, a_property);
1471         CHECK_PARSING_STATUS (status, TRUE);
1472         
1473         cr_parser_try_to_skip_spaces_and_comments (a_this);
1475         cr_parser_clear_errors (a_this);
1476         return CR_OK;
1478       error:
1480         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1482         return status;
1485 /**
1486  *Parses a "term" as defined in the css2 spec, appendix D.1:
1487  *term ::= unary_operator? [NUMBER S* | PERCENTAGE S* | LENGTH S* | 
1488  *EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* | function ] |
1489  *STRING S* | IDENT S* | URI S* | RGB S* | UNICODERANGE S* | hexcolor
1490  *
1491  *TODO: handle parsing of 'RGB'
1492  *
1493  *@param a_term out parameter. The successfully parsed term.
1494  *@return CR_OK upon successfull completion, an error code otherwise.
1495  */
1496 enum CRStatus
1497 cr_parser_parse_term (CRParser * a_this, CRTerm ** a_term)
1499         enum CRStatus status = CR_PARSING_ERROR;
1500         CRInputPos init_pos;
1501         CRTerm *result = NULL;
1502         CRTerm *param = NULL;
1503         CRToken *token = NULL;
1504         CRString *func_name = NULL;
1505         CRParsingLocation location = {0,0,0} ;
1507         g_return_val_if_fail (a_this && a_term, CR_BAD_PARAM_ERROR);
1509         RECORD_INITIAL_POS (a_this, &init_pos);
1511         result = cr_term_new ();
1513         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
1514                                           &token);
1515         if (status != CR_OK || !token)
1516                 goto error;
1518         cr_parsing_location_copy (&location, &token->location) ;
1519         if (token->type == DELIM_TK && token->u.unichar == '+') {
1520                 result->unary_op = PLUS_UOP;
1521                 cr_token_destroy (token) ;
1522                 token = NULL ;
1523                 cr_parser_try_to_skip_spaces_and_comments (a_this);
1524                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
1525                                                   &token);
1526                 if (status != CR_OK || !token)
1527                         goto error;
1528         } else if (token->type == DELIM_TK && token->u.unichar == '-') {
1529                 result->unary_op = MINUS_UOP;
1530                 cr_token_destroy (token) ;
1531                 token = NULL ;
1532                 cr_parser_try_to_skip_spaces_and_comments (a_this);
1533                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
1534                                                   &token);
1535                 if (status != CR_OK || !token)
1536                         goto error;
1537         }
1539         if (token->type == EMS_TK
1540             || token->type == EXS_TK
1541             || token->type == LENGTH_TK
1542             || token->type == ANGLE_TK
1543             || token->type == TIME_TK
1544             || token->type == FREQ_TK
1545             || token->type == PERCENTAGE_TK
1546             || token->type == NUMBER_TK) {
1547                 status = cr_term_set_number (result, token->u.num);
1548                 CHECK_PARSING_STATUS (status, TRUE);
1549                 token->u.num = NULL;
1550                 status = CR_OK;
1551         } else if (token && token->type == FUNCTION_TK) {
1552                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
1553                                                token);
1554                 token = NULL;
1555                 status = cr_parser_parse_function (a_this, &func_name,
1556                                                    &param);
1558                 if (status == CR_OK) {
1559                         status = cr_term_set_function (result,
1560                                                        func_name,
1561                                                        param);
1562                         CHECK_PARSING_STATUS (status, TRUE);
1563                 }
1564         } else if (token && token->type == STRING_TK) {
1565                 status = cr_term_set_string (result, 
1566                                              token->u.str);
1567                 CHECK_PARSING_STATUS (status, TRUE);
1568                 token->u.str = NULL;
1569         } else if (token && token->type == IDENT_TK) {
1570                 status = cr_term_set_ident (result, token->u.str);
1571                 CHECK_PARSING_STATUS (status, TRUE);
1572                 token->u.str = NULL;
1573         } else if (token && token->type == URI_TK) {
1574                 status = cr_term_set_uri (result, token->u.str);
1575                 CHECK_PARSING_STATUS (status, TRUE);
1576                 token->u.str = NULL;
1577         } else if (token && token->type == RGB_TK) {
1578                 status = cr_term_set_rgb (result, token->u.rgb);
1579                 CHECK_PARSING_STATUS (status, TRUE);
1580                 token->u.rgb = NULL;
1581         } else if (token && token->type == UNICODERANGE_TK) {
1582                 result->type = TERM_UNICODERANGE;
1583                 status = CR_PARSING_ERROR;
1584         } else if (token && token->type == HASH_TK) {
1585                 status = cr_term_set_hash (result, token->u.str);
1586                 CHECK_PARSING_STATUS (status, TRUE);
1587                 token->u.str = NULL;
1588         } else {
1589                 status = CR_PARSING_ERROR;
1590         }
1592         if (status != CR_OK) {
1593                 goto error;
1594         }
1595         cr_parsing_location_copy (&result->location,
1596                                   &location) ;
1597         *a_term = cr_term_append_term (*a_term, result);
1599         result = NULL;
1601         cr_parser_try_to_skip_spaces_and_comments (a_this);
1603         if (token) {
1604                 cr_token_destroy (token);
1605                 token = NULL;
1606         }
1608         cr_parser_clear_errors (a_this);
1609         return CR_OK;
1611  error:
1613         if (result) {
1614                 cr_term_destroy (result);
1615                 result = NULL;
1616         }
1618         if (token) {
1619                 cr_token_destroy (token);
1620                 token = NULL;
1621         }
1623         if (param) {
1624                 cr_term_destroy (param);
1625                 param = NULL;
1626         }
1628         if (func_name) {
1629                 cr_string_destroy (func_name);
1630                 func_name = NULL;
1631         }
1633         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1635         return status;
1638 /**
1639  *Parses a "simple_selector" as defined by the css2 spec in appendix D.1 :
1640  *element_name? [ HASH | class | attrib | pseudo ]* S*
1641  *and where pseudo is:
1642  *pseudo ::=  ':' [ IDENT | FUNCTION S* IDENT S* ')' ]
1643  *
1644  *@Param a_this the "this pointer" of the current instance of #CRParser.
1645  *@param a_sel out parameter. Is set to the successfully parsed simple
1646  *selector.
1647  *@return CR_OK upon successfull completion, an error code otherwise.
1648  */
1649 static enum CRStatus
1650 cr_parser_parse_simple_selector (CRParser * a_this, CRSimpleSel ** a_sel)
1652         enum CRStatus status = CR_ERROR;
1653         CRInputPos init_pos;
1654         CRToken *token = NULL;
1655         CRSimpleSel *sel = NULL;
1656         CRAdditionalSel *add_sel_list = NULL;
1657         gboolean found_sel = FALSE;
1658         guint32 cur_char = 0;
1660         g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
1662         RECORD_INITIAL_POS (a_this, &init_pos);
1664         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1665         if (status != CR_OK)
1666                 goto error;
1668         sel = cr_simple_sel_new ();
1669         ENSURE_PARSING_COND (sel);
1671         cr_parsing_location_copy 
1672                 (&sel->location, 
1673                  &token->location) ;
1675         if (token && token->type == DELIM_TK 
1676             && token->u.unichar == '*') {
1677                 int comb = (int)sel->type_mask | (int) UNIVERSAL_SELECTOR;
1678                 sel->type_mask = (enum SimpleSelectorType)comb;
1679                 //sel->type_mask |= UNIVERSAL_SELECTOR;
1680                 sel->name = cr_string_new_from_string ("*");
1681                 found_sel = TRUE;
1682         } else if (token && token->type == IDENT_TK) {
1683                 sel->name = token->u.str;
1684                 int comb = (int)sel->type_mask | (int) TYPE_SELECTOR;
1685                 sel->type_mask = (enum SimpleSelectorType)comb;
1686                 //sel->type_mask |= TYPE_SELECTOR;
1687                 token->u.str = NULL;
1688                 found_sel = TRUE;
1689         } else {
1690                 status = cr_tknzr_unget_token 
1691                         (PRIVATE (a_this)->tknzr,
1692                          token);
1693                 token = NULL;
1694         }
1696         if (token) {
1697                 cr_token_destroy (token);
1698                 token = NULL;
1699         }
1701         cr_parser_try_to_skip_spaces_and_comments (a_this);
1703         for (;;) {
1704                 if (token) {
1705                         cr_token_destroy (token);
1706                         token = NULL;
1707                 }
1709                 status = cr_tknzr_get_next_token 
1710                         (PRIVATE (a_this)->tknzr,
1711                          &token);
1712                 if (status != CR_OK)
1713                         goto error;
1715                 if (token && token->type == HASH_TK) {
1716                         /*we parsed an attribute id */
1717                         CRAdditionalSel *add_sel = NULL;
1719                         add_sel = cr_additional_sel_new_with_type
1720                                 (ID_ADD_SELECTOR);
1722                         add_sel->content.id_name = token->u.str;
1723                         token->u.str = NULL;
1725                         cr_parsing_location_copy 
1726                                 (&add_sel->location,
1727                                  &token->location) ;
1728                         add_sel_list =
1729                                 cr_additional_sel_append
1730                                 (add_sel_list, add_sel);                        
1731                         found_sel = TRUE;
1732                 } else if (token && (token->type == DELIM_TK)
1733                            && (token->u.unichar == '.')) {
1734                         cr_token_destroy (token);
1735                         token = NULL;
1737                         status = cr_tknzr_get_next_token
1738                                 (PRIVATE (a_this)->tknzr, &token);
1739                         if (status != CR_OK)
1740                                 goto error;
1742                         if (token && token->type == IDENT_TK) {
1743                                 CRAdditionalSel *add_sel = NULL;
1745                                 add_sel = cr_additional_sel_new_with_type
1746                                         (CLASS_ADD_SELECTOR);
1748                                 add_sel->content.class_name = token->u.str;
1749                                 token->u.str = NULL;
1751                                 add_sel_list =
1752                                         cr_additional_sel_append
1753                                         (add_sel_list, add_sel);
1754                                 found_sel = TRUE;
1756                                 cr_parsing_location_copy 
1757                                         (&add_sel->location, 
1758                                          & token->location) ;
1759                         } else {
1760                                 status = CR_PARSING_ERROR;
1761                                 goto error;
1762                         }
1763                 } else if (token && token->type == BO_TK) {
1764                         CRAttrSel *attr_sel = NULL;
1765                         CRAdditionalSel *add_sel = NULL;
1767                         status = cr_tknzr_unget_token
1768                                 (PRIVATE (a_this)->tknzr, token);
1769                         if (status != CR_OK)
1770                                 goto error;
1771                         token = NULL;
1773                         status = cr_parser_parse_attribute_selector
1774                                 (a_this, &attr_sel);
1775                         CHECK_PARSING_STATUS (status, FALSE);
1777                         add_sel = cr_additional_sel_new_with_type
1778                                 (ATTRIBUTE_ADD_SELECTOR);
1780                         ENSURE_PARSING_COND (add_sel != NULL);
1782                         add_sel->content.attr_sel = attr_sel;
1784                         add_sel_list =
1785                                 cr_additional_sel_append
1786                                 (add_sel_list, add_sel);
1787                         found_sel = TRUE;
1788                         cr_parsing_location_copy 
1789                                 (&add_sel->location,
1790                                  &attr_sel->location) ;
1791                 } else if (token && (token->type == DELIM_TK)
1792                            && (token->u.unichar == ':')) {
1793                         CRPseudo *pseudo = NULL;
1795                         /*try to parse a pseudo */
1797                         if (token) {
1798                                 cr_token_destroy (token);
1799                                 token = NULL;
1800                         }
1802                         pseudo = cr_pseudo_new ();
1804                         status = cr_tknzr_get_next_token
1805                                 (PRIVATE (a_this)->tknzr, &token);
1806                         ENSURE_PARSING_COND (status == CR_OK && token);
1808                         cr_parsing_location_copy 
1809                                 (&pseudo->location, 
1810                                  &token->location) ;
1812                         if (token->type == IDENT_TK) {
1813                                 pseudo->type = IDENT_PSEUDO;
1814                                 pseudo->name = token->u.str;
1815                                 token->u.str = NULL;
1816                                 found_sel = TRUE;
1817                         } else if (token->type == FUNCTION_TK) {
1818                                 pseudo->name = token->u.str;
1819                                 token->u.str = NULL;
1820                                 cr_parser_try_to_skip_spaces_and_comments
1821                                         (a_this);
1822                                 status = cr_parser_parse_ident
1823                                         (a_this, &pseudo->extra);
1825                                 ENSURE_PARSING_COND (status == CR_OK);
1826                                 READ_NEXT_CHAR (a_this, &cur_char);
1827                                 ENSURE_PARSING_COND (cur_char == ')');
1828                                 pseudo->type = FUNCTION_PSEUDO;
1829                                 found_sel = TRUE;
1830                         } else {
1831                                 status = CR_PARSING_ERROR;
1832                                 goto error;
1833                         }
1835                         if (status == CR_OK) {
1836                                 CRAdditionalSel *add_sel = NULL;
1838                                 add_sel = cr_additional_sel_new_with_type
1839                                         (PSEUDO_CLASS_ADD_SELECTOR);
1841                                 add_sel->content.pseudo = pseudo;
1842                                 cr_parsing_location_copy 
1843                                         (&add_sel->location, 
1844                                          &pseudo->location) ;
1845                                 add_sel_list =
1846                                         cr_additional_sel_append
1847                                         (add_sel_list, add_sel);
1848                                 status = CR_OK;
1849                         }
1850                 } else {
1851                         status = cr_tknzr_unget_token
1852                                 (PRIVATE (a_this)->tknzr, token);
1853                         token = NULL;
1854                         break;
1855                 }
1856         }
1858         if (status == CR_OK && found_sel == TRUE) {
1859                 cr_parser_try_to_skip_spaces_and_comments (a_this);
1861                 sel->add_sel = add_sel_list;
1862                 add_sel_list = NULL;
1863                 
1864                 if (*a_sel == NULL) {
1865                         *a_sel = sel;
1866                 } else {
1867                         cr_simple_sel_append_simple_sel (*a_sel, sel);
1868                 }
1870                 sel = NULL;
1872                 if (token) {
1873                         cr_token_destroy (token);
1874                         token = NULL;
1875                 }
1877                 cr_parser_clear_errors (a_this);
1878                 return CR_OK;
1879         } else {
1880                 status = CR_PARSING_ERROR;
1881         }
1883  error:
1885         if (token) {
1886                 cr_token_destroy (token);
1887                 token = NULL;
1888         }
1890         if (add_sel_list) {
1891                 cr_additional_sel_destroy (add_sel_list);
1892                 add_sel_list = NULL;
1893         }
1895         if (sel) {
1896                 cr_simple_sel_destroy (sel);
1897                 sel = NULL;
1898         }
1900         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1902         return status;
1906 /**
1907  *Parses a "selector" as defined by the css2 spec in appendix D.1:
1908  *selector ::=  simple_selector [ combinator simple_selector ]*
1909  *
1910  *@param a_this the this pointer of the current instance of #CRParser.
1911  *@param a_start a pointer to the 
1912  *first chararcter of the successfully parsed
1913  *string.
1914  *@param a_end a pointer to the last character of the successfully parsed
1915  *string.
1916  *@return CR_OK upon successfull completion, an error code otherwise.
1917  */
1918 static enum CRStatus
1919 cr_parser_parse_simple_sels (CRParser * a_this, 
1920                              CRSimpleSel ** a_sel)
1922         enum CRStatus status = CR_ERROR;
1923         CRInputPos init_pos;
1924         CRSimpleSel *sel = NULL;
1925         guint32 cur_char = 0;
1927         g_return_val_if_fail (a_this                               
1928                               && PRIVATE (a_this)
1929                               && a_sel,
1930                               CR_BAD_PARAM_ERROR);
1932         RECORD_INITIAL_POS (a_this, &init_pos);
1934         status = cr_parser_parse_simple_selector (a_this, &sel);
1935         CHECK_PARSING_STATUS (status, FALSE);
1937         *a_sel = cr_simple_sel_append_simple_sel (*a_sel, sel);
1939         for (;;) {
1940                 guint32 next_char = 0;
1941                 int comb = 0;
1943                 sel = NULL;
1945                 PEEK_NEXT_CHAR (a_this, &next_char);
1947                 if (next_char == '+') {
1948                         READ_NEXT_CHAR (a_this, &cur_char);
1949                         comb = COMB_PLUS;
1950                         cr_parser_try_to_skip_spaces_and_comments (a_this);
1951                 } else if (next_char == '>') {
1952                         READ_NEXT_CHAR (a_this, &cur_char);
1953                         comb = COMB_GT;
1954                         cr_parser_try_to_skip_spaces_and_comments (a_this);
1955                 } else {
1956                         comb = COMB_WS;
1957                 }
1959                 status = cr_parser_parse_simple_selector (a_this, &sel);
1960                 if (status != CR_OK)
1961                         break;
1963                 if (comb && sel) {
1964                         sel->combinator = (enum Combinator)comb;
1965                         comb = 0;
1966                 }
1967                 if (sel) {
1968                         *a_sel = cr_simple_sel_append_simple_sel (*a_sel, 
1969                                                                   sel) ;
1970                 }
1971         }
1972         cr_parser_clear_errors (a_this);
1973         return CR_OK;
1975  error:
1977         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1979         return status;
1982 /**
1983  *Parses a comma separated list of selectors.
1984  *@param a_this the current instance of #CRParser.
1985  *@param a_selector the parsed list of comma separated
1986  *selectors.
1987  *@return CR_OK upon successful completion, an error
1988  *code otherwise.
1989  */
1990 static enum CRStatus
1991 cr_parser_parse_selector (CRParser * a_this, 
1992                           CRSelector ** a_selector)
1994         enum CRStatus status = CR_OK;
1995         CRInputPos init_pos;
1996         guint32 cur_char = 0,
1997                 next_char = 0;
1998         CRSimpleSel *simple_sels = NULL;
1999         CRSelector *selector = NULL;
2001         g_return_val_if_fail (a_this && a_selector, CR_BAD_PARAM_ERROR);
2003         RECORD_INITIAL_POS (a_this, &init_pos);
2005         status = cr_parser_parse_simple_sels (a_this, &simple_sels);
2006         CHECK_PARSING_STATUS (status, FALSE);
2008         if (simple_sels) {
2009                 selector = cr_selector_append_simple_sel
2010                         (selector, simple_sels);
2011                 if (selector) {
2012                         cr_parsing_location_copy
2013                                 (&selector->location,
2014                                  &simple_sels->location) ;
2015                 }
2016                 simple_sels = NULL;
2017         } else {
2018                 status = CR_PARSING_ERROR ;
2019                 goto error ;
2020         }
2022         status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
2023                                      &next_char);
2024         if (status != CR_OK) {
2025                 if (status == CR_END_OF_INPUT_ERROR) {
2026                         status = CR_OK;
2027                         goto okay;
2028                 } else {
2029                         goto error;
2030                 }
2031         }
2033         if (next_char == ',') {
2034                 for (;;) {
2035                         simple_sels = NULL;
2037                         status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
2038                                                      &next_char);
2039                         if (status != CR_OK) {
2040                                 if (status == CR_END_OF_INPUT_ERROR) {
2041                                         status = CR_OK;
2042                                         break;
2043                                 } else {
2044                                         goto error;
2045                                 }
2046                         }
2048                         if (next_char != ',')
2049                                 break;
2051                         /*consume the ',' char */
2052                         READ_NEXT_CHAR (a_this, &cur_char);
2054                         cr_parser_try_to_skip_spaces_and_comments (a_this);
2056                         status = cr_parser_parse_simple_sels
2057                                 (a_this, &simple_sels);
2059                         CHECK_PARSING_STATUS (status, FALSE);
2061                         if (simple_sels) {
2062                                 selector =
2063                                         cr_selector_append_simple_sel
2064                                         (selector, simple_sels);
2066                                 simple_sels = NULL;
2067                         }
2068                 }
2069         }
2071       okay:
2072         cr_parser_try_to_skip_spaces_and_comments (a_this);
2074         if (!*a_selector) {
2075                 *a_selector = selector;
2076         } else {
2077                 *a_selector = cr_selector_append (*a_selector, selector);
2078         }
2080         selector = NULL;
2081         return CR_OK;
2083       error:
2085         if (simple_sels) {
2086                 cr_simple_sel_destroy (simple_sels);
2087                 simple_sels = NULL;
2088         }
2090         if (selector) {
2091                 cr_selector_unref (selector);
2092                 selector = NULL;
2093         }
2095         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
2097         return status;
2100 /**
2101  *Parses a "function" as defined in css spec at appendix D.1:
2102  *function ::= FUNCTION S* expr ')' S*
2103  *FUNCTION ::= ident'('
2104  *
2105  *@param a_this the "this pointer" of the current instance of
2106  *#CRParser.
2107  *
2108  *@param a_func_name out parameter. The parsed function name
2109  *@param a_expr out parameter. The successfully parsed term.
2110  *@return CR_OK upon successfull completion, an error code otherwise.
2111  */
2112 static enum CRStatus
2113 cr_parser_parse_function (CRParser * a_this,
2114                           CRString ** a_func_name,
2115                           CRTerm ** a_expr)
2117         CRInputPos init_pos;
2118         enum CRStatus status = CR_OK;
2119         CRToken *token = NULL;
2120         CRTerm *expr = NULL;
2122         g_return_val_if_fail (a_this && PRIVATE (a_this)
2123                               && a_func_name,
2124                               CR_BAD_PARAM_ERROR);
2126         RECORD_INITIAL_POS (a_this, &init_pos);
2128         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
2129         if (status != CR_OK)
2130                 goto error;
2132         if (token && token->type == FUNCTION_TK) {
2133                 *a_func_name = token->u.str;
2134                 token->u.str = NULL;
2135         } else {
2136                 status = CR_PARSING_ERROR;
2137                 goto error;
2138         }
2139         cr_token_destroy (token);
2140         token = NULL;
2141         
2142         cr_parser_try_to_skip_spaces_and_comments (a_this) ;
2144         status = cr_parser_parse_expr (a_this, &expr);
2146         CHECK_PARSING_STATUS (status, FALSE);
2148         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
2149         if (status != CR_OK)
2150                 goto error;
2152         ENSURE_PARSING_COND (token && token->type == PC_TK);
2154         cr_token_destroy (token);
2155         token = NULL;
2157         if (expr) {
2158                 *a_expr = cr_term_append_term (*a_expr, expr);
2159                 expr = NULL;
2160         }
2162         cr_parser_clear_errors (a_this);
2163         return CR_OK;
2165       error:
2167         if (*a_func_name) {
2168                 cr_string_destroy (*a_func_name);
2169                 *a_func_name = NULL;
2170         }
2172         if (expr) {
2173                 cr_term_destroy (expr);
2174                 expr = NULL;
2175         }
2177         if (token) {
2178                 cr_token_destroy (token);
2180         }
2182         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
2184         return status;
2187 /**
2188  *Parses an uri as defined by the css spec [4.1.1]:
2189  * URI ::= url\({w}{string}{w}\)
2190  *         |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)
2191  *
2192  *@param a_this the current instance of #CRParser.
2193  *@param a_str the successfully parsed url.
2194  *@return CR_OK upon successfull completion, an error code otherwise.
2195  */
2196 static enum CRStatus
2197 cr_parser_parse_uri (CRParser * a_this, CRString ** a_str)
2200         enum CRStatus status = CR_PARSING_ERROR;
2202         g_return_val_if_fail (a_this && PRIVATE (a_this)
2203                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
2205         status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
2206                                        URI_TK, NO_ET, a_str, NULL);
2207         return status;
2210 /**
2211  *Parses a string type as defined in css spec [4.1.1]:
2212  *
2213  *string ::= {string1}|{string2}
2214  *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
2215  *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
2216  *
2217  *@param a_this the current instance of #CRParser.
2218  *@param a_start out parameter. Upon successfull completion, 
2219  *points to the beginning of the string, points to an undefined value
2220  *otherwise.
2221  *@param a_end out parameter. Upon successfull completion, points to
2222  *the beginning of the string, points to an undefined value otherwise.
2223  *@return CR_OK upon successfull completion, an error code otherwise.
2224  */
2225 static enum CRStatus
2226 cr_parser_parse_string (CRParser * a_this, CRString ** a_str)
2228         enum CRStatus status = CR_OK;
2230         g_return_val_if_fail (a_this && PRIVATE (a_this)
2231                               && PRIVATE (a_this)->tknzr
2232                               && a_str, CR_BAD_PARAM_ERROR);
2234         status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
2235                                        STRING_TK, NO_ET, a_str, NULL);
2236         return status;
2239 /**
2240  *Parses an "ident" as defined in css spec [4.1.1]:
2241  *ident ::= {nmstart}{nmchar}*
2242  *
2243  *@param a_this the currens instance of #CRParser.
2244  *
2245  *@param a_str a pointer to parsed ident. If *a_str is NULL,
2246  *this function allocates a new instance of #CRString. If not, 
2247  *the function just appends the parsed string to the one passed.
2248  *In both cases it is up to the caller to free *a_str.
2249  *
2250  *@return CR_OK upon successfull completion, an error code 
2251  *otherwise.
2252  */
2253 static enum CRStatus
2254 cr_parser_parse_ident (CRParser * a_this, CRString ** a_str)
2256         enum CRStatus status = CR_OK;
2258         g_return_val_if_fail (a_this && PRIVATE (a_this)
2259                               && PRIVATE (a_this)->tknzr
2260                               && a_str, CR_BAD_PARAM_ERROR);
2262         status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
2263                                        IDENT_TK, NO_ET, a_str, NULL);
2264         return status;
2267 /**
2268  *the next rule is ignored as well. This seems to be a bug
2269  *Parses a stylesheet as defined in the css2 spec in appendix D.1:
2270  *stylesheet ::= [ CHARSET_SYM S* STRING S* ';' ]? 
2271  *               [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
2272  *               [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]*
2273  *
2274  *TODO: Finish the code of this function. Think about splitting it into
2275  *smaller functions.
2276  *
2277  *@param a_this the "this pointer" of the current instance of #CRParser.
2278  *@param a_start out parameter. A pointer to the first character of
2279  *the successfully parsed string.
2280  *@param a_end out parameter. A pointer to the first character of
2281  *the successfully parsed string.
2282  *
2283  *@return CR_OK upon successfull completion, an error code otherwise.
2284  */
2285 static enum CRStatus
2286 cr_parser_parse_stylesheet (CRParser * a_this)
2288         enum CRStatus status = CR_OK;
2289         CRInputPos init_pos;
2290         CRToken *token = NULL;
2291         CRString *charset = NULL;
2293         g_return_val_if_fail (a_this && PRIVATE (a_this)
2294                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
2296         RECORD_INITIAL_POS (a_this, &init_pos);
2298         PRIVATE (a_this)->state = READY_STATE;
2300         if (PRIVATE (a_this)->sac_handler
2301             && PRIVATE (a_this)->sac_handler->start_document) {
2302                 PRIVATE (a_this)->sac_handler->start_document
2303                         (PRIVATE (a_this)->sac_handler);
2304         }
2306  parse_charset:
2307         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
2309         if (status == CR_END_OF_INPUT_ERROR)
2310                 goto done;
2311         CHECK_PARSING_STATUS (status, TRUE);
2313         if (token && token->type == CHARSET_SYM_TK) {
2314                 CRParsingLocation location = {0,0,0} ;
2315                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2316                                                token);
2317                 CHECK_PARSING_STATUS (status, TRUE);
2318                 token = NULL;
2320                 status = cr_parser_parse_charset (a_this, 
2321                                                   &charset,
2322                                                   &location);
2324                 if (status == CR_OK && charset) {
2325                         if (PRIVATE (a_this)->sac_handler
2326                             && PRIVATE (a_this)->sac_handler->charset) {
2327                                 PRIVATE (a_this)->sac_handler->charset
2328                                         (PRIVATE (a_this)->sac_handler,
2329                                          charset, &location);
2330                         }
2331                 } else if (status != CR_END_OF_INPUT_ERROR) {
2332                         status = cr_parser_parse_atrule_core (a_this);
2333                         CHECK_PARSING_STATUS (status, FALSE);
2334                 }
2336                 if (charset) {
2337                         cr_string_destroy (charset);
2338                         charset = NULL;
2339                 }
2340         } else if (token
2341                    && (token->type == S_TK 
2342                        || token->type == COMMENT_TK)) {
2343                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2344                                                token);
2345                 token = NULL;
2346                 CHECK_PARSING_STATUS (status, TRUE);
2348                 cr_parser_try_to_skip_spaces_and_comments (a_this);
2349                 goto parse_charset ;
2350         } else if (token) {
2351                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2352                                                token);
2353                 token = NULL;
2354                 CHECK_PARSING_STATUS (status, TRUE);
2355         }
2357 /* parse_imports:*/
2358         do {
2359                 if (token) {
2360                         cr_token_destroy (token);
2361                         token = NULL;
2362                 }
2363                 cr_parser_try_to_skip_spaces_and_comments (a_this) ;
2364                 status = cr_tknzr_get_next_token
2365                         (PRIVATE (a_this)->tknzr, &token);
2367                 if (status == CR_END_OF_INPUT_ERROR)
2368                         goto done;
2369                 CHECK_PARSING_STATUS (status, TRUE);
2370         } while (token
2371                  && (token->type == S_TK
2372                      || token->type == CDO_TK || token->type == CDC_TK));
2374         if (token) {
2375                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2376                                                token);
2377                 token = NULL;
2378         }
2380         for (;;) {
2381                 status = cr_tknzr_get_next_token
2382                         (PRIVATE (a_this)->tknzr, &token);
2383                 if (status == CR_END_OF_INPUT_ERROR)
2384                         goto done;
2385                 CHECK_PARSING_STATUS (status, TRUE);
2387                 if (token && token->type == IMPORT_SYM_TK) {
2388                         GList *media_list = NULL;
2389                         CRString *import_string = NULL;
2390                         CRParsingLocation location = {0,0,0} ;
2392                         status = cr_tknzr_unget_token
2393                                 (PRIVATE (a_this)->tknzr, token);
2394                         token = NULL;
2395                         CHECK_PARSING_STATUS (status, TRUE);
2397                         status = cr_parser_parse_import (a_this,
2398                                                          &media_list,
2399                                                          &import_string,
2400                                                          &location);
2401                         if (status == CR_OK) {
2402                                 if (import_string
2403                                     && PRIVATE (a_this)->sac_handler
2404                                     && PRIVATE (a_this)->sac_handler->import_style) {
2405                                         PRIVATE (a_this)->sac_handler->import_style 
2406                                                 (PRIVATE(a_this)->sac_handler,
2407                                                  media_list,
2408                                                  import_string,
2409                                                  NULL, &location) ;
2411                                         if ((PRIVATE (a_this)->sac_handler->resolve_import == TRUE)) {
2412                                                 /*
2413                                                  *TODO: resolve the
2414                                                  *import rule.
2415                                                  */
2416                                         }
2418                                         if ((PRIVATE (a_this)->sac_handler->import_style_result)) {
2419                                                 PRIVATE (a_this)->sac_handler->import_style_result
2420                                                         (PRIVATE (a_this)->sac_handler,
2421                                                          media_list, import_string,
2422                                                          NULL, NULL);
2423                                         }
2424                                 }
2425                         } else if (status != CR_END_OF_INPUT_ERROR) {
2426                                 if (PRIVATE (a_this)->sac_handler
2427                                     && PRIVATE (a_this)->sac_handler->error) {
2428                                         PRIVATE (a_this)->sac_handler->error
2429                                                 (PRIVATE (a_this)->sac_handler);
2430                                 }
2431                                 status = cr_parser_parse_atrule_core (a_this);
2432                                 CHECK_PARSING_STATUS (status, TRUE) ;
2433                         } else {
2434                                 goto error ;
2435                         }
2437                         /*
2438                          *then, after calling the appropriate 
2439                          *SAC handler, free
2440                          *the media_list and import_string.
2441                          */
2442                         if (media_list) {
2443                                 GList *cur = NULL;
2445                                 /*free the medium list */
2446                                 for (cur = media_list; cur; cur = cur->next) {
2447                                         if (cur->data) {
2448                                                 cr_string_destroy ((CRString *)cur->data);
2449                                         }
2450                                 }
2452                                 g_list_free (media_list);
2453                                 media_list = NULL;
2454                         }
2456                         if (import_string) {
2457                                 cr_string_destroy (import_string);
2458                                 import_string = NULL;
2459                         }
2461                         cr_parser_try_to_skip_spaces_and_comments (a_this);
2462                 } else if (token
2463                            && (token->type == S_TK
2464                                || token->type == CDO_TK
2465                                || token->type == CDC_TK)) {
2466                         status = cr_tknzr_unget_token
2467                                 (PRIVATE (a_this)->tknzr, token);
2468                         token = NULL;
2470                         do {
2471                                 if (token) {
2472                                         cr_token_destroy (token);
2473                                         token = NULL;
2474                                 }
2476                                 status = cr_tknzr_get_next_token
2477                                         (PRIVATE (a_this)->tknzr, &token);
2479                                 if (status == CR_END_OF_INPUT_ERROR)
2480                                         goto done;
2481                                 CHECK_PARSING_STATUS (status, TRUE);
2482                         } while (token
2483                                  && (token->type == S_TK
2484                                      || token->type == CDO_TK
2485                                      || token->type == CDC_TK));
2486                 } else {
2487                         if (token) {
2488                                 status = cr_tknzr_unget_token
2489                                         (PRIVATE (a_this)->tknzr, token);
2490                                 token = NULL;
2491                         }
2492                         goto parse_ruleset_and_others;
2493                 }
2494         }
2496  parse_ruleset_and_others:
2498         cr_parser_try_to_skip_spaces_and_comments (a_this);
2500         for (;;) {
2501                 status = cr_tknzr_get_next_token
2502                         (PRIVATE (a_this)->tknzr, &token);
2503                 if (status == CR_END_OF_INPUT_ERROR)
2504                         goto done;
2505                 CHECK_PARSING_STATUS (status, TRUE);
2507                 if (token
2508                     && (token->type == S_TK
2509                         || token->type == CDO_TK || token->type == CDC_TK)) {
2510                         status = cr_tknzr_unget_token
2511                                 (PRIVATE (a_this)->tknzr, token);
2512                         token = NULL;
2514                         do {
2515                                 if (token) {
2516                                         cr_token_destroy (token);
2517                                         token = NULL;
2518                                 }
2520                                 cr_parser_try_to_skip_spaces_and_comments
2521                                         (a_this);
2522                                 status = cr_tknzr_get_next_token
2523                                         (PRIVATE (a_this)->tknzr, &token);
2524                         } while (token
2525                                  && (token->type == S_TK
2526                                      || token->type == COMMENT_TK
2527                                      || token->type == CDO_TK
2528                                      || token->type == CDC_TK));
2529                         if (token) {
2530                                 cr_tknzr_unget_token
2531                                         (PRIVATE (a_this)->tknzr, token);
2532                                 token = NULL;
2533                         }
2534                 } else if (token
2535                            && (token->type == HASH_TK
2536                                || (token->type == DELIM_TK
2537                                    && token->u.unichar == '.')
2538                                || (token->type == DELIM_TK
2539                                    && token->u.unichar == ':')
2540                                || (token->type == DELIM_TK
2541                                    && token->u.unichar == '*')
2542                                || (token->type == BO_TK)
2543                                || token->type == IDENT_TK)) {
2544                         /*
2545                          *Try to parse a CSS2 ruleset.
2546                          *if the parsing fails, try to parse
2547                          *a css core ruleset.
2548                          */
2549                         status = cr_tknzr_unget_token
2550                                 (PRIVATE (a_this)->tknzr, token);
2551                         CHECK_PARSING_STATUS (status, TRUE);
2552                         token = NULL;
2554                         status = cr_parser_parse_ruleset (a_this);
2556                         if (status == CR_OK) {
2557                                 continue;
2558                         } else {
2559                                 if (PRIVATE (a_this)->sac_handler
2560                                     && PRIVATE (a_this)->sac_handler->error) {
2561                                         PRIVATE (a_this)->sac_handler->
2562                                                 error
2563                                                 (PRIVATE (a_this)->
2564                                                  sac_handler);
2565                                 }
2567                                 status = cr_parser_parse_ruleset_core
2568                                         (a_this);
2570                                 if (status == CR_OK) {
2571                                         continue;
2572                                 } else {
2573                                         break;
2574                                 }
2575                         }
2576                 } else if (token && token->type == MEDIA_SYM_TK) {
2577                         status = cr_tknzr_unget_token
2578                                 (PRIVATE (a_this)->tknzr, token);
2579                         CHECK_PARSING_STATUS (status, TRUE);
2580                         token = NULL;
2582                         status = cr_parser_parse_media (a_this);
2583                         if (status == CR_OK) {
2584                                 continue;
2585                         } else {
2586                                 if (PRIVATE (a_this)->sac_handler
2587                                     && PRIVATE (a_this)->sac_handler->error) {
2588                                         PRIVATE (a_this)->sac_handler->
2589                                                 error
2590                                                 (PRIVATE (a_this)->
2591                                                  sac_handler);
2592                                 }
2594                                 status = cr_parser_parse_atrule_core (a_this);
2596                                 if (status == CR_OK) {
2597                                         continue;
2598                                 } else {
2599                                         break;
2600                                 }
2601                         }
2603                 } else if (token && token->type == PAGE_SYM_TK) {
2604                         status = cr_tknzr_unget_token
2605                                 (PRIVATE (a_this)->tknzr, token);
2606                         CHECK_PARSING_STATUS (status, TRUE);
2607                         token = NULL;
2608                         status = cr_parser_parse_page (a_this);
2610                         if (status == CR_OK) {
2611                                 continue;
2612                         } else {
2613                                 if (PRIVATE (a_this)->sac_handler
2614                                     && PRIVATE (a_this)->sac_handler->error) {
2615                                         PRIVATE (a_this)->sac_handler->
2616                                                 error
2617                                                 (PRIVATE (a_this)->
2618                                                  sac_handler);
2619                                 }
2621                                 status = cr_parser_parse_atrule_core (a_this);
2623                                 if (status == CR_OK) {
2624                                         continue;
2625                                 } else {
2626                                         break;
2627                                 }
2628                         }
2629                 } else if (token && token->type == FONT_FACE_SYM_TK) {
2630                         status = cr_tknzr_unget_token
2631                                 (PRIVATE (a_this)->tknzr, token);
2632                         CHECK_PARSING_STATUS (status, TRUE);
2633                         token = NULL;
2634                         status = cr_parser_parse_font_face (a_this);
2636                         if (status == CR_OK) {
2637                                 continue;
2638                         } else {
2639                                 if (PRIVATE (a_this)->sac_handler
2640                                     && PRIVATE (a_this)->sac_handler->error) {
2641                                         PRIVATE (a_this)->sac_handler->
2642                                                 error
2643                                                 (PRIVATE (a_this)->
2644                                                  sac_handler);
2645                                 }
2647                                 status = cr_parser_parse_atrule_core (a_this);
2649                                 if (status == CR_OK) {
2650                                         continue;
2651                                 } else {
2652                                         break;
2653                                 }
2654                         }
2655                 } else {
2656                         status = cr_tknzr_unget_token
2657                                 (PRIVATE (a_this)->tknzr, token);
2658                         CHECK_PARSING_STATUS (status, TRUE);
2659                         token = NULL;
2660                         status = cr_parser_parse_statement_core (a_this);
2662                         if (status == CR_OK) {
2663                                 continue;
2664                         } else {
2665                                 break;
2666                         }
2667                 }
2668         }
2670       done:
2671         if (token) {
2672                 cr_token_destroy (token);
2673                 token = NULL;
2674         }
2676         if (status == CR_END_OF_INPUT_ERROR || status == CR_OK) {
2678                 if (PRIVATE (a_this)->sac_handler
2679                     && PRIVATE (a_this)->sac_handler->end_document) {
2680                         PRIVATE (a_this)->sac_handler->end_document
2681                                 (PRIVATE (a_this)->sac_handler);
2682                 }
2684                 return CR_OK;
2685         }
2687         cr_parser_push_error
2688                 (a_this, (guchar *)"could not recognize next production", CR_ERROR);
2690         if (PRIVATE (a_this)->sac_handler
2691             && PRIVATE (a_this)->sac_handler->unrecoverable_error) {
2692                 PRIVATE (a_this)->sac_handler->
2693                         unrecoverable_error (PRIVATE (a_this)->sac_handler);
2694         }
2696         cr_parser_dump_err_stack (a_this, TRUE);
2698         return status;
2700       error:
2702         if (token) {
2703                 cr_token_destroy (token);
2704                 token = NULL;
2705         }
2707         if (PRIVATE (a_this)->sac_handler
2708             && PRIVATE (a_this)->sac_handler->unrecoverable_error) {
2709                 PRIVATE (a_this)->sac_handler->
2710                         unrecoverable_error (PRIVATE (a_this)->sac_handler);
2711         }
2713         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
2715         return status;
2718 /****************************************
2719  *Public CRParser Methods
2720  ****************************************/
2722 /**
2723  *Creates a new parser to parse data
2724  *coming the input stream given in parameter.
2725  *@param a_input the input stream of the parser.
2726  *Note that the newly created parser will ref
2727  *a_input and unref it when parsing reaches the
2728  *end of the input stream.
2729  *@return the newly created instance of #CRParser,
2730  *or NULL if an error occured.
2731  */
2732 CRParser *
2733 cr_parser_new (CRTknzr * a_tknzr)
2735         enum CRStatus status = CR_OK;
2737         CRParser *result = (CRParser *)g_malloc0 (sizeof (CRParser));
2739         PRIVATE (result) = (CRParserPriv *)g_malloc0 (sizeof (CRParserPriv));
2741         if (a_tknzr) {
2742                 status = cr_parser_set_tknzr (result, a_tknzr);
2743         }
2745         g_return_val_if_fail (status == CR_OK, NULL);
2747         return result;
2750 /**
2751  *Instanciates a new parser from a memory buffer.
2752  *@param a_buf the buffer to parse.
2753  *@param a_len the length of the data in the buffer.
2754  *@param a_enc the encoding of the input buffer a_buf.
2755  *@param a_free_buf if set to TRUE, a_buf will be freed
2756  *during the destruction of the newly built instance 
2757  *of #CRParser. If set to FALSE, it is up to the caller to
2758  *eventually free it.
2759  *@return the newly built parser, or NULL if an error arises.
2760  */
2761 CRParser *
2762 cr_parser_new_from_buf (guchar * a_buf,
2763                         gulong a_len,
2764                         enum CREncoding a_enc, 
2765                         gboolean a_free_buf)
2767         CRParser *result = NULL;
2768         CRInput *input = NULL;
2770         g_return_val_if_fail (a_buf, NULL);
2772         input = cr_input_new_from_buf (a_buf, a_len, a_enc, a_free_buf);
2773         g_return_val_if_fail (input, NULL);
2775         result = cr_parser_new_from_input (input);
2776         if (!result) {
2777                 cr_input_destroy (input);
2778                 input = NULL;
2779                 return NULL;
2780         }
2781         return result;
2784 CRParser *
2785 cr_parser_new_from_input (CRInput * a_input)
2787         CRParser *result = NULL;
2788         CRTknzr *tokenizer = NULL;
2790         if (a_input) {
2791                 tokenizer = cr_tknzr_new (a_input);
2792                 g_return_val_if_fail (tokenizer, NULL);
2793         }
2795         result = cr_parser_new (tokenizer);
2796         g_return_val_if_fail (result, NULL);
2798         return result;
2801 CRParser *
2802 cr_parser_new_from_file (const guchar * a_file_uri, enum CREncoding a_enc)
2804         CRParser *result = NULL;
2805         CRTknzr *tokenizer = NULL;
2807         tokenizer = cr_tknzr_new_from_uri (a_file_uri, a_enc);
2808         if (!tokenizer) {
2809                 cr_utils_trace_info ("Could not open input file");
2810                 return NULL;
2811         }
2813         result = cr_parser_new (tokenizer);
2814         g_return_val_if_fail (result, NULL);
2815         return result;
2818 /**
2819  *Sets a SAC document handler to the parser.
2820  *@param a_this the "this pointer" of the current instance of #CRParser.
2821  *@param a_handler the handler to set.
2822  *@return CR_OK upon successfull completion, an error code otherwise.
2823  */
2824 enum CRStatus
2825 cr_parser_set_sac_handler (CRParser * a_this, CRDocHandler * a_handler)
2827         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
2829         if (PRIVATE (a_this)->sac_handler) {
2830                 cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
2831         }
2833         PRIVATE (a_this)->sac_handler = a_handler;
2834         cr_doc_handler_ref (a_handler);
2836         return CR_OK;
2839 /**
2840  *Gets the SAC document handler.
2841  *@param a_this the "this pointer" of the current instance of
2842  *#CRParser.
2843  *@param a_handler out parameter. The returned handler.
2844  *@return CR_OK upon successfull completion, an error code
2845  *otherwise.
2846  */
2847 enum CRStatus
2848 cr_parser_get_sac_handler (CRParser * a_this, CRDocHandler ** a_handler)
2850         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
2852         *a_handler = PRIVATE (a_this)->sac_handler;
2854         return CR_OK;
2857 /**
2858  *Sets the SAC handler associated to the current instance
2859  *of #CRParser to the default SAC handler.
2860  *@param a_this a pointer to the current instance of #CRParser.
2861  *@return CR_OK upon successfull completion, an error code otherwise.
2862  */
2863 enum CRStatus
2864 cr_parser_set_default_sac_handler (CRParser * a_this)
2866         CRDocHandler *default_sac_handler = NULL;
2867         enum CRStatus status = CR_ERROR;
2869         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
2871         default_sac_handler = cr_doc_handler_new ();
2873         cr_doc_handler_set_default_sac_handler (default_sac_handler);
2875         status = cr_parser_set_sac_handler (a_this, default_sac_handler);
2877         if (status != CR_OK) {
2878                 cr_doc_handler_destroy (default_sac_handler);
2879                 default_sac_handler = NULL;
2880         }
2882         return status;
2885 enum CRStatus
2886 cr_parser_set_use_core_grammar (CRParser * a_this,
2887                                 gboolean a_use_core_grammar)
2889         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
2891         PRIVATE (a_this)->use_core_grammar = a_use_core_grammar;
2893         return CR_OK;
2896 enum CRStatus
2897 cr_parser_get_use_core_grammar (CRParser * a_this,
2898                                 gboolean * a_use_core_grammar)
2900         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
2902         *a_use_core_grammar = PRIVATE (a_this)->use_core_grammar;
2904         return CR_OK;
2907 /**
2908  *Parses a the given in parameter.
2909  *@param a_this a pointer to the current instance of #CRParser.
2910  *@param a_file_uri the uri to the file to load. For the time being,
2911  *only local files are supported.
2912  *@return CR_OK upon successfull completion, an error code otherwise.
2913  */
2914 enum CRStatus
2915 cr_parser_parse_file (CRParser * a_this,
2916                       const guchar * a_file_uri, enum CREncoding a_enc)
2918         enum CRStatus status = CR_ERROR;
2919         CRTknzr *tknzr = NULL;
2921         g_return_val_if_fail (a_this && PRIVATE (a_this)
2922                               && a_file_uri, CR_BAD_PARAM_ERROR);
2924         tknzr = cr_tknzr_new_from_uri (a_file_uri, a_enc);
2926         g_return_val_if_fail (tknzr != NULL, CR_ERROR);
2928         status = cr_parser_set_tknzr (a_this, tknzr);
2929         g_return_val_if_fail (status == CR_OK, CR_ERROR);
2931         status = cr_parser_parse (a_this);
2933         return status;
2936 /**
2937  *Parses an expression as defined by the css2 spec in appendix
2938  *D.1:
2939  *expr: term [ operator term ]*
2940  */
2941 enum CRStatus
2942 cr_parser_parse_expr (CRParser * a_this, CRTerm ** a_expr)
2944         enum CRStatus status = CR_ERROR;
2945         CRInputPos init_pos;
2946         CRTerm *expr = NULL,
2947                 *expr2 = NULL;
2948         guchar next_byte = 0;
2949         gulong nb_terms = 0;
2951         g_return_val_if_fail (a_this && PRIVATE (a_this)
2952                               && a_expr, CR_BAD_PARAM_ERROR);
2954         RECORD_INITIAL_POS (a_this, &init_pos);
2956         status = cr_parser_parse_term (a_this, &expr);
2958         CHECK_PARSING_STATUS (status, FALSE);
2960         for (;;) {
2961                 guchar operatr = 0;
2963                 status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr,
2964                                              1, &next_byte);
2965                 if (status != CR_OK) {
2966                         if (status == CR_END_OF_INPUT_ERROR) {
2967                                 /*
2968                                    if (!nb_terms)
2969                                    {
2970                                    goto error ;
2971                                    }
2972                                  */
2973                                 status = CR_OK;
2974                                 break;
2975                         } else {
2976                                 goto error;
2977                         }
2978                 }
2980                 if (next_byte == '/' || next_byte == ',') {
2981                         READ_NEXT_BYTE (a_this, &operatr);
2982                 }
2984                 cr_parser_try_to_skip_spaces_and_comments (a_this);
2986                 status = cr_parser_parse_term (a_this, &expr2);
2988                 if (status != CR_OK || expr2 == NULL) {
2989                         status = CR_OK;
2990                         break;
2991                 }
2993                 switch (operatr) {
2994                 case '/':
2995                         expr2->the_operator = DIVIDE;
2996                         break;
2997                 case ',':
2998                         expr2->the_operator = COMMA;
3000                 default:
3001                         break;
3002                 }
3004                 expr = cr_term_append_term (expr, expr2);
3005                 expr2 = NULL;
3006                 operatr = 0;
3007                 nb_terms++;
3008         }
3010         if (status == CR_OK) {
3011                 *a_expr = cr_term_append_term (*a_expr, expr);
3012                 expr = NULL;
3014                 cr_parser_clear_errors (a_this);
3015                 return CR_OK;
3016         }
3018       error:
3020         if (expr) {
3021                 cr_term_destroy (expr);
3022                 expr = NULL;
3023         }
3025         if (expr2) {
3026                 cr_term_destroy (expr2);
3027                 expr2 = NULL;
3028         }
3030         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3032         return status;
3035 /**
3036  *Parses a declaration priority as defined by
3037  *the css2 grammar in appendix C:
3038  *prio: IMPORTANT_SYM S*
3039  *@param a_this the current instance of #CRParser.
3040  *@param a_prio a string representing the priority.
3041  *Today, only "!important" is returned as only this
3042  *priority is defined by css2.
3043  */
3044 enum CRStatus
3045 cr_parser_parse_prio (CRParser * a_this, CRString ** a_prio)
3047         enum CRStatus status = CR_ERROR;
3048         CRInputPos init_pos;
3049         CRToken *token = NULL;
3051         g_return_val_if_fail (a_this && PRIVATE (a_this)
3052                               && a_prio
3053                               && *a_prio == NULL, CR_BAD_PARAM_ERROR);
3055         RECORD_INITIAL_POS (a_this, &init_pos);
3057         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3058         if (status == CR_END_OF_INPUT_ERROR) {
3059                 goto error;
3060         }
3061         ENSURE_PARSING_COND (status == CR_OK
3062                              && token && token->type == IMPORTANT_SYM_TK);
3064         cr_parser_try_to_skip_spaces_and_comments (a_this);
3065         *a_prio = cr_string_new_from_string ("!important");
3066         cr_token_destroy (token);
3067         token = NULL;
3068         return CR_OK;
3070       error:
3071         if (token) {
3072                 cr_token_destroy (token);
3073                 token = NULL;
3074         }
3075         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3077         return status;
3080 /**
3081  *TODO: return the parsed priority, so that
3082  *upper layers can take benefit from it.
3083  *Parses a "declaration" as defined by the css2 spec in appendix D.1:
3084  *declaration ::= [property ':' S* expr prio?]?
3085  *
3086  *@param a_this the "this pointer" of the current instance of #CRParser.
3087  *@param a_property the successfully parsed property. The caller
3088  * *must* free the returned pointer.
3089  *@param a_expr the expression that represents the attribute value.
3090  *The caller *must* free the returned pointer.
3091  *@return CR_OK upon successfull completion, an error code otherwise.
3092  */
3093 enum CRStatus
3094 cr_parser_parse_declaration (CRParser * a_this,
3095                              CRString ** a_property,
3096                              CRTerm ** a_expr, gboolean * a_important)
3098         enum CRStatus status = CR_ERROR;
3099         CRInputPos init_pos;
3100         guint32 cur_char = 0;
3101         CRTerm *expr = NULL;
3102         CRString *prio = NULL;
3104         g_return_val_if_fail (a_this && PRIVATE (a_this)
3105                               && a_property && a_expr
3106                               && a_important, CR_BAD_PARAM_ERROR);
3108         RECORD_INITIAL_POS (a_this, &init_pos);
3110         status = cr_parser_parse_property (a_this, a_property);
3112         if (status == CR_END_OF_INPUT_ERROR)
3113                 goto error;
3115         CHECK_PARSING_STATUS_ERR
3116                 (a_this, status, FALSE,
3117                  (guchar *)"while parsing declaration: next property is malformed",
3118                  CR_SYNTAX_ERROR);
3120         READ_NEXT_CHAR (a_this, &cur_char);
3122         if (cur_char != ':') {
3123                 status = CR_PARSING_ERROR;
3124                 cr_parser_push_error
3125                         (a_this,
3126                          (guchar *)"while parsing declaration: this char must be ':'",
3127                          CR_SYNTAX_ERROR);
3128                 goto error;
3129         }
3131         cr_parser_try_to_skip_spaces_and_comments (a_this);
3133         status = cr_parser_parse_expr (a_this, &expr);
3135         CHECK_PARSING_STATUS_ERR
3136                 (a_this, status, FALSE,
3137                  (guchar *)"while parsing declaration: next expression is malformed",
3138                  CR_SYNTAX_ERROR);
3140         cr_parser_try_to_skip_spaces_and_comments (a_this);
3141         status = cr_parser_parse_prio (a_this, &prio);
3142         if (prio) {
3143                 cr_string_destroy (prio);
3144                 prio = NULL;
3145                 *a_important = TRUE;
3146         } else {
3147                 *a_important = FALSE;
3148         }
3149         if (*a_expr) {
3150                 cr_term_append_term (*a_expr, expr);
3151                 expr = NULL;
3152         } else {
3153                 *a_expr = expr;
3154                 expr = NULL;
3155         }
3157         cr_parser_clear_errors (a_this);
3158         return CR_OK;
3160       error:
3162         if (expr) {
3163                 cr_term_destroy (expr);
3164                 expr = NULL;
3165         }
3167         if (*a_property) {
3168                 cr_string_destroy (*a_property);
3169                 *a_property = NULL;
3170         }
3172         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3174         return status;
3177 /**
3178  *Parses a statement as defined by the css core grammar in
3179  *chapter 4.1 of the css2 spec.
3180  *statement   : ruleset | at-rule;
3181  *@param a_this the current instance of #CRParser.
3182  *@return CR_OK upon successfull completion, an error code otherwise.
3183  */
3184 enum CRStatus
3185 cr_parser_parse_statement_core (CRParser * a_this)
3187         CRToken *token = NULL;
3188         CRInputPos init_pos;
3189         enum CRStatus status = CR_ERROR;
3191         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
3193         RECORD_INITIAL_POS (a_this, &init_pos);
3195         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3197         ENSURE_PARSING_COND (status == CR_OK && token);
3199         switch (token->type) {
3200         case ATKEYWORD_TK:
3201         case IMPORT_SYM_TK:
3202         case PAGE_SYM_TK:
3203         case MEDIA_SYM_TK:
3204         case FONT_FACE_SYM_TK:
3205         case CHARSET_SYM_TK:
3206                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3207                 token = NULL;
3208                 status = cr_parser_parse_atrule_core (a_this);
3209                 CHECK_PARSING_STATUS (status, TRUE);
3210                 break;
3212         default:
3213                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3214                 token = NULL;
3215                 status = cr_parser_parse_ruleset_core (a_this);
3216                 cr_parser_clear_errors (a_this);
3217                 CHECK_PARSING_STATUS (status, TRUE);
3218         }
3220         return CR_OK;
3222       error:
3223         if (token) {
3224                 cr_token_destroy (token);
3225                 token = NULL;
3226         }
3228         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3230         return status;
3233 /**
3234  *Parses a "ruleset" as defined in the css2 spec at appendix D.1.
3235  *ruleset ::= selector [ ',' S* selector ]* 
3236  *'{' S* declaration? [ ';' S* declaration? ]* '}' S*;
3237  *
3238  *This methods calls the the SAC handler on the relevant SAC handler
3239  *callbacks whenever it encounters some specific constructions.
3240  *See the documentation of #CRDocHandler (the SAC handler) to know
3241  *when which SAC handler is called.
3242  *@param a_this the "this pointer" of the current instance of #CRParser.
3243  *@return CR_OK upon successfull completion, an error code otherwise.
3244  */
3245 enum CRStatus
3246 cr_parser_parse_ruleset (CRParser * a_this)
3248         enum CRStatus status = CR_OK;
3249         CRInputPos init_pos;
3250         guint32 cur_char = 0,
3251                 next_char = 0;
3252         CRString *property = NULL;
3253         CRTerm *expr = NULL;
3254         CRSimpleSel *simple_sels = NULL;
3255         CRSelector *selector = NULL;
3256         gboolean start_selector = FALSE,
3257                 is_important = FALSE;
3259         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
3261         RECORD_INITIAL_POS (a_this, &init_pos);
3263         status = cr_parser_parse_selector (a_this, &selector);
3264         CHECK_PARSING_STATUS (status, FALSE);
3266         READ_NEXT_CHAR (a_this, &cur_char);
3268         ENSURE_PARSING_COND_ERR
3269                 (a_this, cur_char == '{',
3270                  (guchar *)"while parsing rulset: current char should be '{'",
3271                  CR_SYNTAX_ERROR);
3273         if (PRIVATE (a_this)->sac_handler
3274             && PRIVATE (a_this)->sac_handler->start_selector) {
3275                 /*
3276                  *the selector is ref counted so that the parser's user
3277                  *can choose to keep it.
3278                  */
3279                 if (selector) {
3280                         cr_selector_ref (selector);
3281                 }
3283                 PRIVATE (a_this)->sac_handler->start_selector
3284                         (PRIVATE (a_this)->sac_handler, selector);
3285                 start_selector = TRUE;
3286         }
3288         cr_parser_try_to_skip_spaces_and_comments (a_this);
3290         PRIVATE (a_this)->state = TRY_PARSE_RULESET_STATE;
3292         status = cr_parser_parse_declaration (a_this, &property,
3293                                               &expr,
3294                                               &is_important);
3295         if (expr) {
3296                 cr_term_ref (expr);
3297         }
3298         if (status == CR_OK
3299             && PRIVATE (a_this)->sac_handler
3300             && PRIVATE (a_this)->sac_handler->property) {
3301                 PRIVATE (a_this)->sac_handler->property
3302                         (PRIVATE (a_this)->sac_handler, property, expr,
3303                          is_important);
3304         }        
3305         if (status == CR_OK) {
3306                 /*
3307                  *free the allocated
3308                  *'property' and 'term' before parsing
3309                  *next declarations.
3310                  */
3311                 if (property) {
3312                         cr_string_destroy (property);
3313                         property = NULL;
3314                 }
3315                 if (expr) {
3316                         cr_term_unref (expr);
3317                         expr = NULL;
3318                 }
3319         } else {/*status != CR_OK*/                
3320                 guint32 c = 0 ;
3321                 /*
3322                  *test if we have reached '}', which
3323                  *would mean that we are parsing an empty ruleset (eg. x{ })
3324                  *In that case, goto end_of_ruleset.
3325                  */
3326                 status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, &c) ;
3327                 if (status == CR_OK && c == '}') {
3328                         status = CR_OK ;
3329                         goto end_of_ruleset ;
3330                 }
3331         }
3332         CHECK_PARSING_STATUS_ERR
3333                 (a_this, status, FALSE,
3334                  (guchar *)"while parsing ruleset: next construction should be a declaration",
3335                  CR_SYNTAX_ERROR);
3337         for (;;) {
3338                 PEEK_NEXT_CHAR (a_this, &next_char);
3339                 if (next_char != ';')
3340                         break;
3342                 /*consume the ';' char */
3343                 READ_NEXT_CHAR (a_this, &cur_char);
3345                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3347                 status = cr_parser_parse_declaration (a_this, &property,
3348                                                       &expr, &is_important);
3350                 if (expr) {
3351                         cr_term_ref (expr);
3352                 }
3353                 if (status == CR_OK
3354                     && PRIVATE (a_this)->sac_handler
3355                     && PRIVATE (a_this)->sac_handler->property) {
3356                         PRIVATE (a_this)->sac_handler->property
3357                                 (PRIVATE (a_this)->sac_handler,
3358                                  property, expr, is_important);
3359                 }
3360                 if (property) {
3361                         cr_string_destroy (property);
3362                         property = NULL;
3363                 }
3364                 if (expr) {
3365                         cr_term_unref (expr);
3366                         expr = NULL;
3367                 }
3368         }
3370  end_of_ruleset:
3371         cr_parser_try_to_skip_spaces_and_comments (a_this);
3372         READ_NEXT_CHAR (a_this, &cur_char);
3373         ENSURE_PARSING_COND_ERR
3374                 (a_this, cur_char == '}',
3375                  (guchar *)"while parsing rulset: current char must be a '}'",
3376                  CR_SYNTAX_ERROR);
3378         if (PRIVATE (a_this)->sac_handler
3379             && PRIVATE (a_this)->sac_handler->end_selector) {
3380                 PRIVATE (a_this)->sac_handler->end_selector
3381                         (PRIVATE (a_this)->sac_handler, selector);
3382                 start_selector = FALSE;
3383         }
3385         if (expr) {
3386                 cr_term_unref (expr);
3387                 expr = NULL;
3388         }
3390         if (simple_sels) {
3391                 cr_simple_sel_destroy (simple_sels);
3392                 simple_sels = NULL;
3393         }
3395         if (selector) {
3396                 cr_selector_unref (selector);
3397                 selector = NULL;
3398         }
3400         cr_parser_clear_errors (a_this);
3401         PRIVATE (a_this)->state = RULESET_PARSED_STATE;
3403         return CR_OK;
3405  error:
3406         if (start_selector == TRUE
3407             && PRIVATE (a_this)->sac_handler
3408             && PRIVATE (a_this)->sac_handler->error) {
3409                 PRIVATE (a_this)->sac_handler->error
3410                         (PRIVATE (a_this)->sac_handler);
3411         }
3412         if (expr) {
3413                 cr_term_unref (expr);
3414                 expr = NULL;
3415         }
3416         if (simple_sels) {
3417                 cr_simple_sel_destroy (simple_sels);
3418                 simple_sels = NULL;
3419         }
3420         if (property) {
3421                 cr_string_destroy (property);
3422         }
3423         if (selector) {
3424                 cr_selector_unref (selector);
3425                 selector = NULL;
3426         }
3428         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3430         return status;
3433 /**
3434  *Parses an 'import' declaration as defined in the css2 spec
3435  *in appendix D.1:
3436  *
3437  *import ::=
3438  *@import [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S*
3439  *
3440  *@param a_this the "this pointer" of the current instance 
3441  *of #CRParser.
3442  *
3443  *@param a_medium_list out parameter. A linked list of 
3444  *#CRString
3445  *Each CRString is a string that contains
3446  *a 'medium' declaration part of the successfully 
3447  *parsed 'import' declaration.
3448  *
3449  *@param a_import_string out parameter. 
3450  *A string that contains the 'import 
3451  *string". The import string can be either an uri (if it starts with
3452  *the substring "uri(") or a any other css2 string. Note that
3453  * *a_import_string must be initially set to NULL or else, this function
3454  *will return CR_BAD_PARAM_ERROR.
3455  *
3456  *@return CR_OK upon sucessfull completion, an error code otherwise.
3457  */
3458 enum CRStatus
3459 cr_parser_parse_import (CRParser * a_this,
3460                         GList ** a_media_list,
3461                         CRString ** a_import_string,
3462                         CRParsingLocation *a_location)
3464         enum CRStatus status = CR_OK;
3465         CRInputPos init_pos;
3466         guint32 cur_char = 0,
3467                 next_char = 0;
3468         CRString *medium = NULL;
3470         g_return_val_if_fail (a_this
3471                               && a_import_string
3472                               && (*a_import_string == NULL),
3473                               CR_BAD_PARAM_ERROR);
3475         RECORD_INITIAL_POS (a_this, &init_pos);
3477         if (BYTE (a_this, 1, NULL) == '@'
3478             && BYTE (a_this, 2, NULL) == 'i'
3479             && BYTE (a_this, 3, NULL) == 'm'
3480             && BYTE (a_this, 4, NULL) == 'p'
3481             && BYTE (a_this, 5, NULL) == 'o'
3482             && BYTE (a_this, 6, NULL) == 'r'
3483             && BYTE (a_this, 7, NULL) == 't') {
3484                 SKIP_CHARS (a_this, 1);
3485                 if (a_location) {
3486                         cr_parser_get_parsing_location 
3487                                 (a_this, a_location) ;
3488                 }
3489                 SKIP_CHARS (a_this, 6);
3490                 status = CR_OK;
3491         } else {
3492                 status = CR_PARSING_ERROR;
3493                 goto error;
3494         }
3496         cr_parser_try_to_skip_spaces_and_comments (a_this);
3498         PRIVATE (a_this)->state = TRY_PARSE_IMPORT_STATE;
3500         PEEK_NEXT_CHAR (a_this, &next_char);
3502         if (next_char == '"' || next_char == '\'') {
3503                 status = cr_parser_parse_string (a_this, a_import_string);
3505                 CHECK_PARSING_STATUS (status, FALSE);
3506         } else {
3507                 status = cr_parser_parse_uri (a_this, a_import_string);
3509                 CHECK_PARSING_STATUS (status, FALSE);
3510         }
3512         cr_parser_try_to_skip_spaces_and_comments (a_this);
3514         status = cr_parser_parse_ident (a_this, &medium);
3516         if (status == CR_OK && medium) {
3517                 *a_media_list = g_list_append (*a_media_list, medium);
3518                 medium = NULL;
3519         }
3521         cr_parser_try_to_skip_spaces_and_comments (a_this);
3523         for (; status == CR_OK;) {
3524                 if ((status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
3525                                                   &next_char)) != CR_OK) {
3526                         if (status == CR_END_OF_INPUT_ERROR) {
3527                                 status = CR_OK;
3528                                 goto okay;
3529                         }
3530                         goto error;
3531                 }
3533                 if (next_char == ',') {
3534                         READ_NEXT_CHAR (a_this, &cur_char);
3535                 } else {
3536                         break;
3537                 }
3539                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3541                 status = cr_parser_parse_ident (a_this, &medium);
3543                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3545                 if ((status == CR_OK) && medium) {
3546                         *a_media_list = g_list_append (*a_media_list, medium);
3548                         medium = NULL;
3549                 }
3551                 CHECK_PARSING_STATUS (status, FALSE);
3552                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3553         }
3554         cr_parser_try_to_skip_spaces_and_comments (a_this);
3555         READ_NEXT_CHAR (a_this, &cur_char);
3556         ENSURE_PARSING_COND (cur_char == ';');
3557         cr_parser_try_to_skip_spaces_and_comments (a_this);
3558       okay:
3559         cr_parser_clear_errors (a_this);
3560         PRIVATE (a_this)->state = IMPORT_PARSED_STATE;
3562         return CR_OK;
3564       error:
3566         if (*a_media_list) {
3567                 GList *cur = NULL;
3569                 /*
3570                  *free each element of *a_media_list.
3571                  *Note that each element of *a_medium list *must*
3572                  *be a GString* or else, the code that is coming next 
3573                  *will corrupt the memory and lead to hard to debug
3574                  *random crashes.
3575                  *This is where C++ and its compile time
3576                  *type checking mecanism (through STL containers) would
3577                  *have prevented us to go through this hassle.
3578                  */
3579                 for (cur = *a_media_list; cur; cur = cur->next) {
3580                         if (cur->data) {
3581                                 cr_string_destroy ((CRString *)cur->data);
3582                         }
3583                 }
3585                 g_list_free (*a_media_list);
3586                 *a_media_list = NULL;
3587         }
3589         if (*a_import_string) {
3590                 cr_string_destroy (*a_import_string);
3591                 *a_import_string = NULL;
3592         }
3594         if (medium) {
3595                 cr_string_destroy (medium);
3596                 medium = NULL;
3597         }
3599         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3601         return status;
3604 /**
3605  *Parses a 'media' declaration as specified in the css2 spec at
3606  *appendix D.1:
3607  *
3608  *media ::= @media S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S*
3609  *
3610  *Note that this function calls the required sac handlers during the parsing
3611  *to notify media productions. See #CRDocHandler to know the callback called
3612  *during @media parsing.
3613  *@param a_this the "this pointer" of the current instance of #CRParser.
3614  *@return CR_OK upon successfull completion, an error code otherwise.
3615  */
3616 enum CRStatus
3617 cr_parser_parse_media (CRParser * a_this)
3619         enum CRStatus status = CR_OK;
3620         CRInputPos init_pos;
3621         CRToken *token = NULL;
3622         guint32 next_char = 0,
3623                 cur_char = 0;
3624         CRString *medium = NULL;
3625         GList *media_list = NULL;
3626         CRParsingLocation location = {0,0,0} ;
3628         g_return_val_if_fail (a_this 
3629                               && PRIVATE (a_this), 
3630                               CR_BAD_PARAM_ERROR);
3632         RECORD_INITIAL_POS (a_this, &init_pos);
3634         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
3635                                           &token);
3636         ENSURE_PARSING_COND (status == CR_OK
3637                              && token 
3638                              && token->type == MEDIA_SYM_TK);
3639         cr_parsing_location_copy (&location, &token->location) ;
3640         cr_token_destroy (token);
3641         token = NULL;
3643         cr_parser_try_to_skip_spaces_and_comments (a_this);
3645         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3646         ENSURE_PARSING_COND (status == CR_OK
3647                              && token && token->type == IDENT_TK);
3649         medium = token->u.str;
3650         token->u.str = NULL;
3651         cr_token_destroy (token);
3652         token = NULL;
3654         if (medium) {
3655                 media_list = g_list_append (media_list, medium);
3656                 medium = NULL;
3657         }
3659         for (; status == CR_OK;) {
3660                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3661                 PEEK_NEXT_CHAR (a_this, &next_char);
3663                 if (next_char == ',') {
3664                         READ_NEXT_CHAR (a_this, &cur_char);
3665                 } else {
3666                         break;
3667                 }
3669                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3671                 status = cr_parser_parse_ident (a_this, &medium);
3673                 CHECK_PARSING_STATUS (status, FALSE);
3675                 if (medium) {
3676                         media_list = g_list_append (media_list, medium);
3677                         medium = NULL;
3678                 }
3679         }
3681         READ_NEXT_CHAR (a_this, &cur_char);
3683         ENSURE_PARSING_COND (cur_char == '{');
3685         /*
3686          *call the SAC handler api here.
3687          */
3688         if (PRIVATE (a_this)->sac_handler
3689             && PRIVATE (a_this)->sac_handler->start_media) {
3690                 PRIVATE (a_this)->sac_handler->start_media
3691                         (PRIVATE (a_this)->sac_handler, media_list,
3692                          &location);
3693         }
3695         cr_parser_try_to_skip_spaces_and_comments (a_this);
3697         PRIVATE (a_this)->state = TRY_PARSE_MEDIA_STATE;
3699         for (; status == CR_OK;) {
3700                 status = cr_parser_parse_ruleset (a_this);
3701                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3702         }
3704         READ_NEXT_CHAR (a_this, &cur_char);
3706         ENSURE_PARSING_COND (cur_char == '}');
3708         /*
3709          *call the right SAC handler api here.
3710          */
3711         if (PRIVATE (a_this)->sac_handler
3712             && PRIVATE (a_this)->sac_handler->end_media) {
3713                 PRIVATE (a_this)->sac_handler->end_media
3714                         (PRIVATE (a_this)->sac_handler, media_list);
3715         }
3717         cr_parser_try_to_skip_spaces_and_comments (a_this);
3719         /*
3720          *Then, free the data structures passed to
3721          *the last call to the SAC handler.
3722          */
3723         if (medium) {
3724                 cr_string_destroy (medium);
3725                 medium = NULL;
3726         }
3728         if (media_list) {
3729                 GList *cur = NULL;
3731                 for (cur = media_list; cur; cur = cur->next) {
3732                         cr_string_destroy ((CRString *)cur->data);
3733                 }
3735                 g_list_free (media_list);
3736                 media_list = NULL;
3737         }
3739         cr_parser_clear_errors (a_this);
3740         PRIVATE (a_this)->state = MEDIA_PARSED_STATE;
3742         return CR_OK;
3744       error:
3746         if (token) {
3747                 cr_token_destroy (token);
3748                 token = NULL;
3749         }
3751         if (medium) {
3752                 cr_string_destroy (medium);
3753                 medium = NULL;
3754         }
3756         if (media_list) {
3757                 GList *cur = NULL;
3759                 for (cur = media_list; cur; cur = cur->next) {
3760                         cr_string_destroy ((CRString *)cur->data);
3761                 }
3763                 g_list_free (media_list);
3764                 media_list = NULL;
3765         }
3767         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3769         return status;
3772 /**
3773  *Parses '@page' rule as specified in the css2 spec in appendix D.1:
3774  *page ::= PAGE_SYM S* IDENT? pseudo_page? S* 
3775  *'{' S* declaration [ ';' S* declaration ]* '}' S*
3776  *
3777  *This function also calls the relevant SAC handlers whenever it
3778  *encounters a construction that must 
3779  *be reported to the calling application.
3780  *@param a_this the "this pointer" of the current instance of #CRParser.
3781  *@return CR_OK upon successfull completion, an error code otherwise.
3782  */
3783 enum CRStatus
3784 cr_parser_parse_page (CRParser * a_this)
3786         enum CRStatus status = CR_OK;
3787         CRInputPos init_pos;
3788         CRToken *token = NULL;
3789         CRTerm *css_expression = NULL;
3790         CRString *page_selector = NULL,
3791                 *page_pseudo_class = NULL,
3792                 *property = NULL;
3793         gboolean important = TRUE;
3794         CRParsingLocation location = {0,0,0} ;
3796         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
3798         RECORD_INITIAL_POS (a_this, &init_pos);
3800         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
3801                                           &token) ;
3802         ENSURE_PARSING_COND (status == CR_OK
3803                              && token 
3804                              && token->type == PAGE_SYM_TK);
3806         cr_parsing_location_copy (&location, &token->location) ;
3807         cr_token_destroy (token);
3808         token = NULL;
3810         cr_parser_try_to_skip_spaces_and_comments (a_this);
3812         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3813         ENSURE_PARSING_COND (status == CR_OK && token);
3815         if (token->type == IDENT_TK) {
3816                 page_selector = token->u.str;
3817                 token->u.str = NULL;
3818                 cr_token_destroy (token);
3819                 token = NULL;
3820         } else {
3821                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3822                 token = NULL;
3823         }
3825         /* 
3826          *try to parse pseudo_page
3827          */
3828         cr_parser_try_to_skip_spaces_and_comments (a_this);
3829         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3830         ENSURE_PARSING_COND (status == CR_OK && token);
3832         if (token->type == DELIM_TK && token->u.unichar == ':') {
3833                 cr_token_destroy (token);
3834                 token = NULL;
3835                 status = cr_parser_parse_ident (a_this, &page_pseudo_class);
3836                 CHECK_PARSING_STATUS (status, FALSE);
3837         } else {
3838                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3839                 token = NULL;
3840         }
3842         /*
3843          *parse_block
3844          *
3845          */
3846         cr_parser_try_to_skip_spaces_and_comments (a_this);
3848         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3850         ENSURE_PARSING_COND (status == CR_OK && token
3851                              && token->type == CBO_TK);
3853         cr_token_destroy (token);
3854         token = NULL;
3856         /*
3857          *Call the appropriate SAC handler here.
3858          */
3859         if (PRIVATE (a_this)->sac_handler
3860             && PRIVATE (a_this)->sac_handler->start_page) {
3861                 PRIVATE (a_this)->sac_handler->start_page
3862                         (PRIVATE (a_this)->sac_handler,
3863                          page_selector, page_pseudo_class,
3864                          &location);
3865         }
3866         cr_parser_try_to_skip_spaces_and_comments (a_this);
3868         PRIVATE (a_this)->state = TRY_PARSE_PAGE_STATE;
3870         status = cr_parser_parse_declaration (a_this, &property,
3871                                               &css_expression, 
3872                                               &important);
3873         ENSURE_PARSING_COND (status == CR_OK);
3875         /*
3876          *call the relevant SAC handler here...
3877          */
3878         if (PRIVATE (a_this)->sac_handler
3879             && PRIVATE (a_this)->sac_handler->property) {
3880                 if (css_expression)
3881                         cr_term_ref (css_expression);
3883                 PRIVATE (a_this)->sac_handler->property
3884                         (PRIVATE (a_this)->sac_handler,
3885                          property, css_expression, important);
3886         }
3887         /*
3888          *... and free the data structure passed to that last
3889          *SAC handler.
3890          */
3891         if (property) {
3892                 cr_string_destroy (property);
3893                 property = NULL;
3894         }
3895         if (css_expression) {
3896                 cr_term_unref (css_expression);
3897                 css_expression = NULL;
3898         }
3900         for (;;) {
3901                 /*parse the other ';' separated declarations */
3902                 if (token) {
3903                         cr_token_destroy (token);
3904                         token = NULL;
3905                 }
3906                 status = cr_tknzr_get_next_token
3907                         (PRIVATE (a_this)->tknzr, &token);
3909                 ENSURE_PARSING_COND (status == CR_OK && token);
3911                 if (token->type != SEMICOLON_TK) {
3912                         cr_tknzr_unget_token
3913                                 (PRIVATE (a_this)->tknzr,
3914                                  token);
3915                         token = NULL ;
3916                         break;
3917                 }
3919                 cr_token_destroy (token);
3920                 token = NULL;
3921                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3923                 status = cr_parser_parse_declaration (a_this, &property,
3924                                                       &css_expression,
3925                                                       &important);
3926                 if (status != CR_OK)
3927                         break ;
3929                 /*
3930                  *call the relevant SAC handler here...
3931                  */
3932                 if (PRIVATE (a_this)->sac_handler
3933                     && PRIVATE (a_this)->sac_handler->property) {
3934                         cr_term_ref (css_expression);
3935                         PRIVATE (a_this)->sac_handler->property
3936                                 (PRIVATE (a_this)->sac_handler,
3937                                  property, css_expression, important);
3938                 }
3939                 /*
3940                  *... and free the data structure passed to that last
3941                  *SAC handler.
3942                  */
3943                 if (property) {
3944                         cr_string_destroy (property);
3945                         property = NULL;
3946                 }
3947                 if (css_expression) {
3948                         cr_term_unref (css_expression);
3949                         css_expression = NULL;
3950                 }
3951         }
3952         cr_parser_try_to_skip_spaces_and_comments 
3953                 (a_this) ;
3954         if (token) {
3955                 cr_token_destroy (token) ;
3956                 token = NULL ;
3957         }
3959         status = cr_tknzr_get_next_token
3960                         (PRIVATE (a_this)->tknzr, &token);
3961         ENSURE_PARSING_COND (status == CR_OK 
3962                              && token 
3963                              && token->type == CBC_TK) ;
3964         cr_token_destroy (token) ;
3965         token = NULL ;
3966         /*
3967          *call the relevant SAC handler here.
3968          */
3969         if (PRIVATE (a_this)->sac_handler
3970             && PRIVATE (a_this)->sac_handler->end_page) {
3971                 PRIVATE (a_this)->sac_handler->end_page
3972                         (PRIVATE (a_this)->sac_handler,
3973                          page_selector, page_pseudo_class);
3974         }
3976         if (page_selector) {
3977                 cr_string_destroy (page_selector);
3978                 page_selector = NULL;
3979         }
3981         if (page_pseudo_class) {
3982                 cr_string_destroy (page_pseudo_class);
3983                 page_pseudo_class = NULL;
3984         }
3986         cr_parser_try_to_skip_spaces_and_comments (a_this);
3988         /*here goes the former implem of this function ... */
3990         cr_parser_clear_errors (a_this);
3991         PRIVATE (a_this)->state = PAGE_PARSED_STATE;
3993         return CR_OK;
3995  error:
3996         if (token) {
3997                 cr_token_destroy (token);
3998                 token = NULL;
3999         }
4000         if (page_selector) {
4001                 cr_string_destroy (page_selector);
4002                 page_selector = NULL;
4003         }
4004         if (page_pseudo_class) {
4005                 cr_string_destroy (page_pseudo_class);
4006                 page_pseudo_class = NULL;
4007         }
4008         if (property) {
4009                 cr_string_destroy (property);
4010                 property = NULL;
4011         }
4012         if (css_expression) {
4013                 cr_term_destroy (css_expression);
4014                 css_expression = NULL;
4015         }
4016         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
4017         return status;
4020 /**
4021  *Parses a charset declaration as defined implictly by the css2 spec in
4022  *appendix D.1:
4023  *charset ::= CHARSET_SYM S* STRING S* ';'
4024  *
4025  *@param a_this the "this pointer" of the current instance of #CRParser.
4026  *@param a_value out parameter. The actual parsed value of the charset 
4027  *declararation. Note that for safety check reasons, *a_value must be
4028  *set to NULL.
4029  *@param a_charset_sym_location the parsing location of
4030  *@return CR_OK upon successfull completion, an error code otherwise.
4031  */
4032 enum CRStatus
4033 cr_parser_parse_charset (CRParser * a_this, CRString ** a_value,
4034                          CRParsingLocation *a_charset_sym_location)
4036         enum CRStatus status = CR_OK;
4037         CRInputPos init_pos;
4038         CRToken *token = NULL;
4039         CRString *charset_str = NULL;
4041         g_return_val_if_fail (a_this && a_value
4042                               && (*a_value == NULL), 
4043                               CR_BAD_PARAM_ERROR);
4045         RECORD_INITIAL_POS (a_this, &init_pos);
4047         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4049         ENSURE_PARSING_COND (status == CR_OK
4050                              && token && token->type == CHARSET_SYM_TK);
4051         if (a_charset_sym_location) {
4052                 cr_parsing_location_copy (a_charset_sym_location, 
4053                                           &token->location) ;
4054         }
4055         cr_token_destroy (token);
4056         token = NULL;
4058         PRIVATE (a_this)->state = TRY_PARSE_CHARSET_STATE;
4060         cr_parser_try_to_skip_spaces_and_comments (a_this);
4062         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4063         ENSURE_PARSING_COND (status == CR_OK
4064                              && token && token->type == STRING_TK);
4065         charset_str = token->u.str;
4066         token->u.str = NULL;
4067         cr_token_destroy (token);
4068         token = NULL;
4070         cr_parser_try_to_skip_spaces_and_comments (a_this);
4072         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4074         ENSURE_PARSING_COND (status == CR_OK
4075                              && token && token->type == SEMICOLON_TK);
4076         cr_token_destroy (token);
4077         token = NULL;
4079         if (charset_str) {
4080                 *a_value = charset_str;
4081                 charset_str = NULL;
4082         }
4084         PRIVATE (a_this)->state = CHARSET_PARSED_STATE;
4085         return CR_OK;
4087  error:
4089         if (token) {
4090                 cr_token_destroy (token);
4091                 token = NULL;
4092         }
4094         if (*a_value) {
4095                 cr_string_destroy (*a_value);
4096                 *a_value = NULL;
4097         }
4099         if (charset_str) {
4100                 cr_string_destroy (charset_str);
4101                 charset_str = NULL;
4102         }
4104         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
4106         return status;
4109 /**
4110  *Parses the "@font-face" rule specified in the css1 spec in
4111  *appendix D.1:
4112  *
4113  *font_face ::= FONT_FACE_SYM S* 
4114  *'{' S* declaration [ ';' S* declaration ]* '}' S*
4115  *
4116  *This function will call SAC handlers whenever it is necessary.
4117  *@return CR_OK upon successfull completion, an error code otherwise.
4118  */
4119 enum CRStatus
4120 cr_parser_parse_font_face (CRParser * a_this)
4122         enum CRStatus status = CR_ERROR;
4123         CRInputPos init_pos;
4124         CRString *property = NULL;
4125         CRTerm *css_expression = NULL;
4126         CRToken *token = NULL;
4127         gboolean important = FALSE;
4128         guint32 next_char = 0,
4129                 cur_char = 0;
4130         CRParsingLocation location = {0,0,0} ;
4132         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
4134         RECORD_INITIAL_POS (a_this, &init_pos);
4136         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4137         ENSURE_PARSING_COND (status == CR_OK
4138                              && token 
4139                              && token->type == FONT_FACE_SYM_TK);
4141         cr_parser_try_to_skip_spaces_and_comments (a_this);
4142         if (token) {
4143                 cr_parsing_location_copy (&location, 
4144                                           &token->location) ;
4145                 cr_token_destroy (token);
4146                 token = NULL;
4147         }
4148         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
4149                                           &token);
4150         ENSURE_PARSING_COND (status == CR_OK && token
4151                              && token->type == CBO_TK);
4152         if (token) {
4153                 cr_token_destroy (token);
4154                 token = NULL;
4155         }
4156         /*
4157          *here, call the relevant SAC handler.
4158          */
4159         if (PRIVATE (a_this)->sac_handler
4160             && PRIVATE (a_this)->sac_handler->start_font_face) {
4161                 PRIVATE (a_this)->sac_handler->start_font_face
4162                         (PRIVATE (a_this)->sac_handler, &location);
4163         }
4164         PRIVATE (a_this)->state = TRY_PARSE_FONT_FACE_STATE;
4165         /*
4166          *and resume the parsing.
4167          */
4168         cr_parser_try_to_skip_spaces_and_comments (a_this);
4169         status = cr_parser_parse_declaration (a_this, &property,
4170                                               &css_expression, &important);
4171         if (status == CR_OK) {
4172                 /*
4173                  *here, call the relevant SAC handler.
4174                  */
4175                 cr_term_ref (css_expression);
4176                 if (PRIVATE (a_this)->sac_handler &&
4177                     PRIVATE (a_this)->sac_handler->property) {
4178                         PRIVATE (a_this)->sac_handler->property
4179                                 (PRIVATE (a_this)->sac_handler,
4180                                  property, css_expression, important);
4181                 }
4182                 ENSURE_PARSING_COND (css_expression && property);
4183         }
4184         /*free the data structures allocated during last parsing. */
4185         if (property) {
4186                 cr_string_destroy (property);
4187                 property = NULL;
4188         }
4189         if (css_expression) {
4190                 cr_term_unref (css_expression);
4191                 css_expression = NULL;
4192         }
4193         for (;;) {
4194                 PEEK_NEXT_CHAR (a_this, &next_char);
4195                 if (next_char == ';') {
4196                         READ_NEXT_CHAR (a_this, &cur_char);
4197                 } else {
4198                         break;
4199                 }
4200                 cr_parser_try_to_skip_spaces_and_comments (a_this);
4201                 status = cr_parser_parse_declaration (a_this, 
4202                                                       &property,
4203                                                       &css_expression,
4204                                                       &important);
4205                 if (status != CR_OK)
4206                         break;
4207                 /*
4208                  *here, call the relevant SAC handler.
4209                  */
4210                 cr_term_ref (css_expression);
4211                 if (PRIVATE (a_this)->sac_handler->property) {
4212                         PRIVATE (a_this)->sac_handler->property
4213                                 (PRIVATE (a_this)->sac_handler,
4214                                  property, css_expression, important);
4215                 }
4216                 /*
4217                  *Then, free the data structures allocated during 
4218                  *last parsing.
4219                  */
4220                 if (property) {
4221                         cr_string_destroy (property);
4222                         property = NULL;
4223                 }
4224                 if (css_expression) {
4225                         cr_term_unref (css_expression);
4226                         css_expression = NULL;
4227                 }
4228         }
4229         cr_parser_try_to_skip_spaces_and_comments (a_this);
4230         READ_NEXT_CHAR (a_this, &cur_char);
4231         ENSURE_PARSING_COND (cur_char == '}');
4232         /*
4233          *here, call the relevant SAC handler.
4234          */
4235         if (PRIVATE (a_this)->sac_handler->end_font_face) {
4236                 PRIVATE (a_this)->sac_handler->end_font_face
4237                         (PRIVATE (a_this)->sac_handler);
4238         }
4239         cr_parser_try_to_skip_spaces_and_comments (a_this);
4241         if (token) {
4242                 cr_token_destroy (token);
4243                 token = NULL;
4244         }
4245         cr_parser_clear_errors (a_this);
4246         PRIVATE (a_this)->state = FONT_FACE_PARSED_STATE;
4247         return CR_OK;
4249       error:
4250         if (token) {
4251                 cr_token_destroy (token);
4252                 token = NULL;
4253         }
4254         if (property) {
4255                 cr_string_destroy (property);
4256                 property = NULL;
4257         }
4258         if (css_expression) {
4259                 cr_term_destroy (css_expression);
4260                 css_expression = NULL;
4261         }
4262         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
4263         return status;
4266 /**
4267  *Parses the data that comes from the
4268  *input previously associated to the current instance of
4269  *#CRParser.
4270  *@param a_this the current instance of #CRParser.
4271  *@return CR_OK ;
4272  */
4273 enum CRStatus
4274 cr_parser_parse (CRParser * a_this)
4276         enum CRStatus status = CR_ERROR;
4278         g_return_val_if_fail (a_this && PRIVATE (a_this)
4279                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
4281         if (PRIVATE (a_this)->use_core_grammar == FALSE) {
4282                 status = cr_parser_parse_stylesheet (a_this);
4283         } else {
4284                 status = cr_parser_parse_stylesheet_core (a_this);
4285         }
4287         return status;
4290 enum CRStatus
4291 cr_parser_set_tknzr (CRParser * a_this, CRTknzr * a_tknzr)
4293         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
4295         if (PRIVATE (a_this)->tknzr) {
4296                 cr_tknzr_unref (PRIVATE (a_this)->tknzr);
4297         }
4299         PRIVATE (a_this)->tknzr = a_tknzr;
4301         if (a_tknzr)
4302                 cr_tknzr_ref (a_tknzr);
4304         return CR_OK;
4307 /**
4308  *Getter of the parser's underlying tokenizer
4309  *@param a_this the current instance of #CRParser
4310  *@param a_tknzr out parameter. The returned tokenizer
4311  *@return CR_OK upon succesful completion, an error code
4312  *otherwise
4313  */
4314 enum CRStatus
4315 cr_parser_get_tknzr (CRParser * a_this, CRTknzr ** a_tknzr)
4317         g_return_val_if_fail (a_this && PRIVATE (a_this)
4318                               && a_tknzr, CR_BAD_PARAM_ERROR);
4320         *a_tknzr = PRIVATE (a_this)->tknzr;
4321         return CR_OK;
4324 /**
4325  *Gets the current parsing location.
4326  *@param a_this the current instance of #CRParser
4327  *@param a_loc the parsing location to get.
4328  *@return CR_OK upon succesful completion, an error code
4329  *otherwise.
4330  */
4331 enum CRStatus 
4332 cr_parser_get_parsing_location (CRParser *a_this, 
4333                                 CRParsingLocation *a_loc)
4335         g_return_val_if_fail (a_this 
4336                               && PRIVATE (a_this)
4337                               && a_loc, CR_BAD_PARAM_ERROR) ;
4339         return cr_tknzr_get_parsing_location 
4340                 (PRIVATE (a_this)->tknzr, a_loc) ;
4343 /**
4344  *Parses a stylesheet from a buffer
4345  *@param a_this the current instance of #CRparser
4346  *@param a_buf the input buffer
4347  *@param a_len the length of the input buffer
4348  *@param a_enc the encoding of the buffer
4349  *@return CR_OK upon successful completion, an error code otherwise.
4350  */
4351 enum CRStatus
4352 cr_parser_parse_buf (CRParser * a_this,
4353                      const guchar * a_buf,
4354                      gulong a_len, enum CREncoding a_enc)
4356         enum CRStatus status = CR_ERROR;
4357         CRTknzr *tknzr = NULL;
4359         g_return_val_if_fail (a_this && PRIVATE (a_this)
4360                               && a_buf, CR_BAD_PARAM_ERROR);
4362         tknzr = cr_tknzr_new_from_buf ((guchar*)a_buf, a_len, a_enc, FALSE);
4364         g_return_val_if_fail (tknzr != NULL, CR_ERROR);
4366         status = cr_parser_set_tknzr (a_this, tknzr);
4367         g_return_val_if_fail (status == CR_OK, CR_ERROR);
4369         status = cr_parser_parse (a_this);
4371         return status;
4374 /**
4375  *Destroys the current instance
4376  *of #CRParser.
4377  *@param a_this the current instance of #CRParser to
4378  *destroy.
4379  */
4380 void
4381 cr_parser_destroy (CRParser * a_this)
4383         g_return_if_fail (a_this && PRIVATE (a_this));
4385         if (PRIVATE (a_this)->tknzr) {
4386                 if (cr_tknzr_unref (PRIVATE (a_this)->tknzr) == TRUE)
4387                         PRIVATE (a_this)->tknzr = NULL;
4388         }
4390         if (PRIVATE (a_this)->sac_handler) {
4391                 cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
4392                 PRIVATE (a_this)->sac_handler = NULL;
4393         }
4395         if (PRIVATE (a_this)->err_stack) {
4396                 cr_parser_clear_errors (a_this);
4397                 PRIVATE (a_this)->err_stack = NULL;
4398         }
4400         if (PRIVATE (a_this)) {
4401                 g_free (PRIVATE (a_this));
4402                 PRIVATE (a_this) = NULL;
4403         }
4405         if (a_this) {
4406                 g_free (a_this);
4407                 a_this = NULL;  /*useless. Just for the sake of coherence */
4408         }