Code

moving trunk for module inkscape
[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 = NULL;
414         result = g_try_malloc (sizeof (CRParserError));
416         if (result == NULL) {
417                 cr_utils_trace_info ("Out of memory");
418                 return NULL;
419         }
421         memset (result, 0, sizeof (CRParserError));
423         cr_parser_error_set_msg (result, a_msg);
424         cr_parser_error_set_status (result, a_status);
426         return result;
429 /**
430  *Sets the message associated to this instance of #CRError.
431  *@param a_this the current instance of #CRParserError.
432  *@param a_msg the new message.
433  */
434 static void
435 cr_parser_error_set_msg (CRParserError * a_this, const guchar * a_msg)
437         g_return_if_fail (a_this);
439         if (a_this->msg) {
440                 g_free (a_this->msg);
441         }
443         a_this->msg = g_strdup (a_msg);
446 /**
447  *Sets the error status.
448  *@param a_this the current instance of #CRParserError.
449  *@param a_status the new error status.
450  *
451  */
452 static void
453 cr_parser_error_set_status (CRParserError * a_this, enum CRStatus a_status)
455         g_return_if_fail (a_this);
457         a_this->status = a_status;
460 /**
461  *Sets the position of the parser error.
462  *@param a_this the current instance of #CRParserError.
463  *@param a_line the line number.
464  *@param a_column the column number.
465  *@param a_byte_num the byte number.
466  */
467 static void
468 cr_parser_error_set_pos (CRParserError * a_this,
469                          glong a_line, glong a_column, glong a_byte_num)
471         g_return_if_fail (a_this);
473         a_this->line = a_line;
474         a_this->column = a_column;
475         a_this->byte_num = a_byte_num;
478 static void
479 cr_parser_error_dump (CRParserError * a_this)
481         g_return_if_fail (a_this);
483         g_printerr ("parsing error: %ld:%ld:", a_this->line, a_this->column);
485         g_printerr ("%s\n", a_this->msg);
488 /**
489  *The destructor of #CRParserError.
490  *@param a_this the current instance of #CRParserError.
491  */
492 static void
493 cr_parser_error_destroy (CRParserError * a_this)
495         g_return_if_fail (a_this);
497         if (a_this->msg) {
498                 g_free (a_this->msg);
499                 a_this->msg = NULL;
500         }
502         g_free (a_this);
505 /**
506  *Pushes an error on the parser error stack.
507  *@param a_this the current instance of #CRParser.
508  *@param a_msg the error message.
509  *@param a_status the error status.
510  *@return CR_OK upon successful completion, an error code otherwise.
511  */
512 static enum CRStatus
513 cr_parser_push_error (CRParser * a_this,
514                       const guchar * a_msg, enum CRStatus a_status)
516         enum CRStatus status = CR_OK;
518         CRParserError *error = NULL;
519         CRInputPos pos;
521         g_return_val_if_fail (a_this && PRIVATE (a_this)
522                               && a_msg, CR_BAD_PARAM_ERROR);
524         error = cr_parser_error_new (a_msg, a_status);
526         g_return_val_if_fail (error, CR_ERROR);
528         RECORD_INITIAL_POS (a_this, &pos);
530         cr_parser_error_set_pos
531                 (error, pos.line, pos.col, pos.next_byte_index - 1);
533         PRIVATE (a_this)->err_stack =
534                 g_list_prepend (PRIVATE (a_this)->err_stack, error);
536         if (PRIVATE (a_this)->err_stack == NULL)
537                 goto error;
539         return CR_OK;
541       error:
543         if (error) {
544                 cr_parser_error_destroy (error);
545                 error = NULL;
546         }
548         return status;
551 /**
552  *Dumps the error stack using g_printerr.
553  *@param a_this the current instance of #CRParser.
554  *@param a_clear_errs whether to clear the error stack
555  *after the dump or not.
556  *@return CR_OK upon successfull completion, an error code
557  *otherwise.
558  */
559 static enum CRStatus
560 cr_parser_dump_err_stack (CRParser * a_this, gboolean a_clear_errs)
562         GList *cur = NULL;
564         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
566         if (PRIVATE (a_this)->err_stack == NULL)
567                 return CR_OK;
569         for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
570                 cr_parser_error_dump ((CRParserError *) cur->data);
571         }
573         if (a_clear_errs == TRUE) {
574                 cr_parser_clear_errors (a_this);
575         }
577         return CR_OK;
580 /**
581  *Clears all the errors contained in the parser error stack.
582  *Frees all the errors, and the stack that contains'em.
583  *@param a_this the current instance of #CRParser.
584  */
585 static enum CRStatus
586 cr_parser_clear_errors (CRParser * a_this)
588         GList *cur = NULL;
590         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
592         for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
593                 if (cur->data) {
594                         cr_parser_error_destroy ((CRParserError *)
595                                                  cur->data);
596                 }
597         }
599         if (PRIVATE (a_this)->err_stack) {
600                 g_list_free (PRIVATE (a_this)->err_stack);
601                 PRIVATE (a_this)->err_stack = NULL;
602         }
604         return CR_OK;
607 /**
608  *Same as cr_parser_try_to_skip_spaces() but this one skips
609  *spaces and comments.
610  *
611  *@param a_this the current instance of #CRParser.
612  *@return CR_OK upon successfull completion, an error code otherwise.
613  */
614 enum CRStatus
615 cr_parser_try_to_skip_spaces_and_comments (CRParser * a_this)
617         enum CRStatus status = CR_ERROR;
618         CRToken *token = NULL;
620         g_return_val_if_fail (a_this && PRIVATE (a_this)
621                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
622         do {
623                 if (token) {
624                         cr_token_destroy (token);
625                         token = NULL;
626                 }
628                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
629                                                   &token);
630                 if (status != CR_OK)
631                         goto error;
632         }
633         while ((token != NULL)
634                && (token->type == COMMENT_TK || token->type == S_TK));
636         cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
638         return status;
640       error:
642         if (token) {
643                 cr_token_destroy (token);
644                 token = NULL;
645         }
647         return status;
650 /***************************************
651  *End of Parser input handling routines
652  ***************************************/
655 /*************************************
656  *Non trivial terminal productions
657  *parsing routines
658  *************************************/
660 /**
661  *Parses a css stylesheet following the core css grammar.
662  *This is mainly done for test purposes.
663  *During the parsing, no callback is called. This is just
664  *to validate that the stylesheet is well formed according to the
665  *css core syntax.
666  *stylesheet  : [ CDO | CDC | S | statement ]*;
667  *@param a_this the current instance of #CRParser.
668  *@return CR_OK upon successful completion, an error code otherwise.
669  */
670 static enum CRStatus
671 cr_parser_parse_stylesheet_core (CRParser * a_this)
673         CRToken *token = NULL;
674         CRInputPos init_pos;
675         enum CRStatus status = CR_ERROR;
677         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
679         RECORD_INITIAL_POS (a_this, &init_pos);
681  continue_parsing:
683         if (token) {
684                 cr_token_destroy (token);
685                 token = NULL;
686         }
688         cr_parser_try_to_skip_spaces_and_comments (a_this);
689         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
690         if (status == CR_END_OF_INPUT_ERROR) {
691                 status = CR_OK;
692                 goto done;
693         } else if (status != CR_OK) {
694                 goto error;
695         }
697         switch (token->type) {
699         case CDO_TK:
700         case CDC_TK:
701                 goto continue_parsing;
702                 break;
703         default:
704                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
705                                                token);
706                 CHECK_PARSING_STATUS (status, TRUE);
707                 token = NULL;
708                 status = cr_parser_parse_statement_core (a_this);
709                 cr_parser_clear_errors (a_this);
710                 if (status == CR_OK) {
711                         goto continue_parsing;
712                 } else if (status == CR_END_OF_INPUT_ERROR) {
713                         goto done;
714                 } else {
715                         goto error;
716                 }
717         }
719  done:
720         if (token) {
721                 cr_token_destroy (token);
722                 token = NULL;
723         }
725         cr_parser_clear_errors (a_this);
726         return CR_OK;
728  error:
729         cr_parser_push_error
730                 (a_this, "could not recognize next production", CR_ERROR);
732         cr_parser_dump_err_stack (a_this, TRUE);
734         if (token) {
735                 cr_token_destroy (token);
736                 token = NULL;
737         }
739         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
741         return status;
744 /**
745  *Parses an at-rule as defined by the css core grammar
746  *in chapter 4.1 in the css2 spec.
747  *at-rule     : ATKEYWORD S* any* [ block | ';' S* ];
748  *@param a_this the current instance of #CRParser.
749  *@return CR_OK upon successfull completion, an error code
750  *otherwise.
751  */
752 static enum CRStatus
753 cr_parser_parse_atrule_core (CRParser * a_this)
755         CRToken *token = NULL;
756         CRInputPos init_pos;
757         enum CRStatus status = CR_ERROR;
759         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
761         RECORD_INITIAL_POS (a_this, &init_pos);
763         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
764                                           &token);
765         ENSURE_PARSING_COND (status == CR_OK
766                              && token
767                              &&
768                              (token->type == ATKEYWORD_TK
769                               || token->type == IMPORT_SYM_TK
770                               || token->type == PAGE_SYM_TK
771                               || token->type == MEDIA_SYM_TK
772                               || token->type == FONT_FACE_SYM_TK
773                               || token->type == CHARSET_SYM_TK));
775         cr_token_destroy (token);
776         token = NULL;
778         cr_parser_try_to_skip_spaces_and_comments (a_this);
780         do {
781                 status = cr_parser_parse_any_core (a_this);
782         } while (status == CR_OK);
784         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
785                                           &token);
786         ENSURE_PARSING_COND (status == CR_OK && token);
788         if (token->type == CBO_TK) {
789                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, 
790                                       token);
791                 token = NULL;
792                 status = cr_parser_parse_block_core (a_this);
793                 CHECK_PARSING_STATUS (status,
794                                       FALSE);
795                 goto done;
796         } else if (token->type == SEMICOLON_TK) {
797                 goto done;
798         } else {
799                 status = CR_PARSING_ERROR ;
800                 goto error;
801         }
803  done:
804         if (token) {
805                 cr_token_destroy (token);
806                 token = NULL;
807         }
808         return CR_OK;
810  error:
811         if (token) {
812                 cr_token_destroy (token);
813                 token = NULL;
814         }
815         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr,
816                               &init_pos);
817         return status;
820 /**
821  *Parses a ruleset as defined by the css core grammar in chapter
822  *4.1 of the css2 spec.
823  *ruleset ::= selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*;
824  *@param a_this the current instance of #CRParser.
825  *@return CR_OK upon successfull completion, an error code otherwise.
826  */
827 static enum CRStatus
828 cr_parser_parse_ruleset_core (CRParser * a_this)
830         CRToken *token = NULL;
831         CRInputPos init_pos;
832         enum CRStatus status = CR_ERROR;
834         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
835         RECORD_INITIAL_POS (a_this, &init_pos);
837         status = cr_parser_parse_selector_core (a_this);
839         ENSURE_PARSING_COND (status == CR_OK
840                              || status == CR_PARSING_ERROR
841                              || status == CR_END_OF_INPUT_ERROR);
843         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
844         ENSURE_PARSING_COND (status == CR_OK && token
845                              && token->type == CBO_TK);
846         cr_token_destroy (token);
847         token = NULL;
849         cr_parser_try_to_skip_spaces_and_comments (a_this);
850         status = cr_parser_parse_declaration_core (a_this);
852       parse_declaration_list:
853         if (token) {
854                 cr_token_destroy (token);
855                 token = NULL;
856         }
858         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
859         ENSURE_PARSING_COND (status == CR_OK && token);
860         if (token->type == CBC_TK) {
861                 goto done;
862         }
864         ENSURE_PARSING_COND (status == CR_OK
865                              && token && token->type == SEMICOLON_TK);
867         cr_token_destroy (token);
868         token = NULL;
869         cr_parser_try_to_skip_spaces_and_comments (a_this);
870         status = cr_parser_parse_declaration_core (a_this);
871         cr_parser_clear_errors (a_this);
872         ENSURE_PARSING_COND (status == CR_OK || status == CR_PARSING_ERROR);
873         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
874         ENSURE_PARSING_COND (status == CR_OK && token);
875         if (token->type == CBC_TK) {
876                 cr_token_destroy (token);
877                 token = NULL;
878                 cr_parser_try_to_skip_spaces_and_comments (a_this);
879                 goto done;
880         } else {
881                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
882                                                token);
883                 token = NULL;
884                 goto parse_declaration_list;
885         }
887       done:
888         if (token) {
889                 cr_token_destroy (token);
890                 token = NULL;
891         }
893         if (status == CR_OK) {
894                 return CR_OK;
895         }
897       error:
898         if (token) {
899                 cr_token_destroy (token);
900                 token = NULL;
901         }
903         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
905         return status;
908 /**
909  *Parses a "selector" as specified by the css core 
910  *grammar.
911  *selector    : any+;
912  *@param a_this the current instance of #CRParser.
913  *@return CR_OK upon successfull completion, an error code
914  *otherwise.
915  */
916 static enum CRStatus
917 cr_parser_parse_selector_core (CRParser * a_this)
919         CRToken *token = NULL;
920         CRInputPos init_pos;
921         enum CRStatus status = CR_ERROR;
923         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
925         RECORD_INITIAL_POS (a_this, &init_pos);
927         status = cr_parser_parse_any_core (a_this);
928         CHECK_PARSING_STATUS (status, FALSE);
930         do {
931                 status = cr_parser_parse_any_core (a_this);
933         } while (status == CR_OK);
935         return CR_OK;
937  error:
938         if (token) {
939                 cr_token_destroy (token);
940                 token = NULL;
941         }
943         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
945         return status;
948 /**
949  *Parses a "block" as defined in the css core grammar
950  *in chapter 4.1 of the css2 spec.
951  *block ::= '{' S* [ any | block | ATKEYWORD S* | ';' ]* '}' S*;
952  *@param a_this the current instance of #CRParser.
953  *FIXME: code this function.
954  */
955 static enum CRStatus
956 cr_parser_parse_block_core (CRParser * a_this)
958         CRToken *token = NULL;
959         CRInputPos init_pos;
960         enum CRStatus status = CR_ERROR;
962         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
964         RECORD_INITIAL_POS (a_this, &init_pos);
966         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
967         ENSURE_PARSING_COND (status == CR_OK && token
968                              && token->type == CBO_TK);
970       parse_block_content:
972         if (token) {
973                 cr_token_destroy (token);
974                 token = NULL;
975         }
977         cr_parser_try_to_skip_spaces_and_comments (a_this);
979         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
980         ENSURE_PARSING_COND (status == CR_OK && token);
982         if (token->type == CBC_TK) {
983                 cr_parser_try_to_skip_spaces_and_comments (a_this);
984                 goto done;
985         } else if (token->type == SEMICOLON_TK) {
986                 goto parse_block_content;
987         } else if (token->type == ATKEYWORD_TK) {
988                 cr_parser_try_to_skip_spaces_and_comments (a_this);
989                 goto parse_block_content;
990         } else if (token->type == CBO_TK) {
991                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
992                 token = NULL;
993                 status = cr_parser_parse_block_core (a_this);
994                 CHECK_PARSING_STATUS (status, FALSE);
995                 goto parse_block_content;
996         } else {
997                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
998                 token = NULL;
999                 status = cr_parser_parse_any_core (a_this);
1000                 CHECK_PARSING_STATUS (status, FALSE);
1001                 goto parse_block_content;
1002         }
1004       done:
1005         if (token) {
1006                 cr_token_destroy (token);
1007                 token = NULL;
1008         }
1010         if (status == CR_OK)
1011                 return CR_OK;
1013       error:
1014         if (token) {
1015                 cr_token_destroy (token);
1016                 token = NULL;
1017         }
1019         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1021         return status;
1024 static enum CRStatus
1025 cr_parser_parse_declaration_core (CRParser * a_this)
1027         CRToken *token = NULL;
1028         CRInputPos init_pos;
1029         enum CRStatus status = CR_ERROR;
1030         CRString *prop = NULL;
1032         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1034         RECORD_INITIAL_POS (a_this, &init_pos);
1036         status = cr_parser_parse_property (a_this, &prop);
1037         CHECK_PARSING_STATUS (status, FALSE);
1038         cr_parser_clear_errors (a_this);
1039         ENSURE_PARSING_COND (status == CR_OK && prop);
1040         cr_string_destroy (prop);
1041         prop = NULL;
1043         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1044         ENSURE_PARSING_COND (status == CR_OK
1045                              && token
1046                              && token->type == DELIM_TK
1047                              && token->u.unichar == ':');
1048         cr_token_destroy (token);
1049         token = NULL;
1050         cr_parser_try_to_skip_spaces_and_comments (a_this);
1051         status = cr_parser_parse_value_core (a_this);
1052         CHECK_PARSING_STATUS (status, FALSE);
1054         return CR_OK;
1056       error:
1058         if (prop) {
1059                 cr_string_destroy (prop);
1060                 prop = NULL;
1061         }
1063         if (token) {
1064                 cr_token_destroy (token);
1065                 token = NULL;
1066         }
1068         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1070         return status;
1073 /**
1074  *Parses a "value" production as defined by the css core grammar
1075  *in chapter 4.1.
1076  *value ::= [ any | block | ATKEYWORD S* ]+;
1077  *@param a_this the current instance of #CRParser.
1078  *@return CR_OK upon successfull completion, an error code otherwise.
1079  */
1080 static enum CRStatus
1081 cr_parser_parse_value_core (CRParser * a_this)
1083         CRToken *token = NULL;
1084         CRInputPos init_pos;
1085         enum CRStatus status = CR_ERROR;
1086         glong ref = 0;
1088         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1089         RECORD_INITIAL_POS (a_this, &init_pos);
1091       continue_parsing:
1093         if (token) {
1094                 cr_token_destroy (token);
1095                 token = NULL;
1096         }
1098         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1099         ENSURE_PARSING_COND (status == CR_OK && token);
1101         switch (token->type) {
1102         case CBO_TK:
1103                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
1104                                                token);
1105                 token = NULL;
1106                 status = cr_parser_parse_block_core (a_this);
1107                 CHECK_PARSING_STATUS (status, FALSE);
1108                 ref++;
1109                 goto continue_parsing;
1111         case ATKEYWORD_TK:
1112                 cr_parser_try_to_skip_spaces_and_comments (a_this);
1113                 ref++;
1114                 goto continue_parsing;
1116         default:
1117                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
1118                                                token);
1119                 token = NULL;
1120                 status = cr_parser_parse_any_core (a_this);
1121                 if (status == CR_OK) {
1122                         ref++;
1123                         goto continue_parsing;
1124                 } else if (status == CR_PARSING_ERROR) {
1125                         status = CR_OK;
1126                         goto done;
1127                 } else {
1128                         goto error;
1129                 }
1130         }
1132       done:
1133         if (token) {
1134                 cr_token_destroy (token);
1135                 token = NULL;
1136         }
1138         if (status == CR_OK && ref)
1139                 return CR_OK;
1140       error:
1141         if (token) {
1142                 cr_token_destroy (token);
1143                 token = NULL;
1144         }
1146         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1148         return status;
1151 /**
1152  *Parses an "any" as defined by the css core grammar in the
1153  *css2 spec in chapter 4.1.
1154  *any ::= [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING
1155  *        | DELIM | URI | HASH | UNICODE-RANGE | INCLUDES
1156  *        | FUNCTION | DASHMATCH | '(' any* ')' | '[' any* ']' ] S*;
1157  *
1158  *@param a_this the current instance of #CRParser.
1159  *@return CR_OK upon successfull completion, an error code otherwise.
1160  */
1161 static enum CRStatus
1162 cr_parser_parse_any_core (CRParser * a_this)
1164         CRToken *token1 = NULL,
1165                 *token2 = NULL;
1166         CRInputPos init_pos;
1167         enum CRStatus status = CR_ERROR;
1169         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
1171         RECORD_INITIAL_POS (a_this, &init_pos);
1173         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token1);
1175         ENSURE_PARSING_COND (status == CR_OK && token1);
1177         switch (token1->type) {
1178         case IDENT_TK:
1179         case NUMBER_TK:
1180         case RGB_TK:
1181         case PERCENTAGE_TK:
1182         case DIMEN_TK:
1183         case EMS_TK:
1184         case EXS_TK:
1185         case LENGTH_TK:
1186         case ANGLE_TK:
1187         case FREQ_TK:
1188         case TIME_TK:
1189         case STRING_TK:
1190         case DELIM_TK:
1191         case URI_TK:
1192         case HASH_TK:
1193         case UNICODERANGE_TK:
1194         case INCLUDES_TK:
1195         case DASHMATCH_TK:
1196         case S_TK:
1197         case COMMENT_TK:
1198         case IMPORTANT_SYM_TK:
1199                 status = CR_OK;
1200                 break;
1201         case FUNCTION_TK:
1202                 /*
1203                  *this case isn't specified by the spec but it
1204                  *does happen. So we have to handle it.
1205                  *We must consider function with parameters.
1206                  *We consider parameter as being an "any*" production.
1207                  */
1208                 do {
1209                         status = cr_parser_parse_any_core (a_this);
1210                 } while (status == CR_OK);
1212                 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
1213                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1214                                                   &token2);
1215                 ENSURE_PARSING_COND (status == CR_OK
1216                                      && token2 && token2->type == PC_TK);
1217                 break;
1218         case PO_TK:
1219                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1220                                                   &token2);
1221                 ENSURE_PARSING_COND (status == CR_OK && token2);
1223                 if (token2->type == PC_TK) {
1224                         cr_token_destroy (token2);
1225                         token2 = NULL;
1226                         goto done;
1227                 } else {
1228                         status = cr_tknzr_unget_token
1229                                 (PRIVATE (a_this)->tknzr, token2);
1230                         token2 = NULL;
1231                 }
1233                 do {
1234                         status = cr_parser_parse_any_core (a_this);
1235                 } while (status == CR_OK);
1237                 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
1239                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1240                                                   &token2);
1241                 ENSURE_PARSING_COND (status == CR_OK
1242                                      && token2 && token2->type == PC_TK);
1243                 status = CR_OK;
1244                 break;
1246         case BO_TK:
1247                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1248                                                   &token2);
1249                 ENSURE_PARSING_COND (status == CR_OK && token2);
1251                 if (token2->type == BC_TK) {
1252                         cr_token_destroy (token2);
1253                         token2 = NULL;
1254                         goto done;
1255                 } else {
1256                         status = cr_tknzr_unget_token
1257                                 (PRIVATE (a_this)->tknzr, token2);
1258                         token2 = NULL;
1259                 }
1261                 do {
1262                         status = cr_parser_parse_any_core (a_this);
1263                 } while (status == CR_OK);
1265                 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
1267                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1268                                                   &token2);
1269                 ENSURE_PARSING_COND (status == CR_OK
1270                                      && token2 && token2->type == BC_TK);
1271                 status = CR_OK;
1272                 break;
1273         default:
1274                 status = CR_PARSING_ERROR;
1275                 goto error;
1276         }
1278       done:
1279         if (token1) {
1280                 cr_token_destroy (token1);
1281                 token1 = NULL;
1282         }
1284         if (token2) {
1285                 cr_token_destroy (token2);
1286                 token2 = NULL;
1287         }
1289         return CR_OK;
1291       error:
1293         if (token1) {
1294                 cr_token_destroy (token1);
1295                 token1 = NULL;
1296         }
1298         if (token2) {
1299                 cr_token_destroy (token2);
1300                 token2 = NULL;
1301         }
1303         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1304         return status;
1307 /**
1308  *Parses an attribute selector as defined in the css2 spec in
1309  *appendix D.1:
1310  *attrib ::= '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
1311  *            [ IDENT | STRING ] S* ]? ']'
1312  *
1313  *@param a_this the "this pointer" of the current instance of
1314  *#CRParser .
1315  *@param a_sel out parameter. The successfully parsed attribute selector.
1316  *@return CR_OK upon successfull completion, an error code otherwise.
1317  */
1318 static enum CRStatus
1319 cr_parser_parse_attribute_selector (CRParser * a_this, 
1320                                     CRAttrSel ** a_sel)
1322         enum CRStatus status = CR_OK;
1323         CRInputPos init_pos;
1324         CRToken *token = NULL;
1325         CRAttrSel *result = NULL;
1326         CRParsingLocation location = {0,0,0} ;
1328         g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
1330         RECORD_INITIAL_POS (a_this, &init_pos);
1332         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1333         ENSURE_PARSING_COND (status == CR_OK && token
1334                              && token->type == BO_TK);
1335         cr_parsing_location_copy
1336                 (&location, &token->location) ;
1337         cr_token_destroy (token);
1338         token = NULL;
1340         cr_parser_try_to_skip_spaces_and_comments (a_this);
1342         result = cr_attr_sel_new ();
1343         if (!result) {
1344                 cr_utils_trace_info ("result failed")  ;
1345                 status = CR_OUT_OF_MEMORY_ERROR ;
1346                 goto error ;
1347         }
1348         cr_parsing_location_copy (&result->location,
1349                                   &location) ;
1350         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1351         ENSURE_PARSING_COND (status == CR_OK
1352                              && token && token->type == IDENT_TK);
1354         result->name = token->u.str;
1355         token->u.str = NULL;
1356         cr_token_destroy (token);
1357         token = NULL;
1359         cr_parser_try_to_skip_spaces_and_comments (a_this);
1361         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1362         ENSURE_PARSING_COND (status == CR_OK && token);
1364         if (token->type == INCLUDES_TK) {
1365                 result->match_way = INCLUDES;
1366                 goto parse_right_part;
1367         } else if (token->type == DASHMATCH_TK) {
1368                 result->match_way = DASHMATCH;
1369                 goto parse_right_part;
1370         } else if (token->type == DELIM_TK && token->u.unichar == '=') {
1371                 result->match_way = EQUALS;
1372                 goto parse_right_part;
1373         } else if (token->type == BC_TK) {
1374                 result->match_way = SET;
1375                 goto done;
1376         }
1378  parse_right_part:
1380         if (token) {
1381                 cr_token_destroy (token);
1382                 token = NULL;
1383         }
1385         cr_parser_try_to_skip_spaces_and_comments (a_this);
1387         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1388         ENSURE_PARSING_COND (status == CR_OK && token);
1389         
1390         if (token->type == IDENT_TK) {
1391                 result->value = token->u.str;
1392                 token->u.str = NULL;
1393         } else if (token->type == STRING_TK) {
1394                 result->value = token->u.str;
1395                 token->u.str = NULL;
1396         } else {
1397                 status = CR_PARSING_ERROR;
1398                 goto error;
1399         }
1401         if (token) {
1402                 cr_token_destroy (token);
1403                 token = NULL;
1404         }
1406         cr_parser_try_to_skip_spaces_and_comments (a_this);
1408         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1410         ENSURE_PARSING_COND (status == CR_OK && token
1411                              && token->type == BC_TK);
1412  done:
1413         if (token) {
1414                 cr_token_destroy (token);
1415                 token = NULL;
1416         }
1418         if (*a_sel) {
1419                 status = cr_attr_sel_append_attr_sel (*a_sel, result);
1420                 CHECK_PARSING_STATUS (status, FALSE);
1421         } else {
1422                 *a_sel = result;
1423         }
1425         cr_parser_clear_errors (a_this);
1426         return CR_OK;
1428  error:
1430         if (result) {
1431                 cr_attr_sel_destroy (result);
1432                 result = NULL;
1433         }
1435         if (token) {
1436                 cr_token_destroy (token);
1437                 token = NULL;
1438         }
1440         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1442         return status;
1445 /**
1446  *Parses a "property" as specified by the css2 spec at [4.1.1]:
1447  *property : IDENT S*;
1448  *
1449  *@param a_this the "this pointer" of the current instance of #CRParser.
1450  *@param GString a_property out parameter. The parsed property without the
1451  *trailing spaces. If *a_property is NULL, this function allocates a
1452  *new instance of GString and set it content to the parsed property.
1453  *If not, the property is just appended to a_property's previous content.
1454  *In both cases, it is up to the caller to free a_property.
1455  *@return CR_OK upon successfull completion, CR_PARSING_ERROR if the
1456  *next construction was not a "property", or an error code.
1457  */
1458 static enum CRStatus
1459 cr_parser_parse_property (CRParser * a_this, 
1460                           CRString ** a_property)
1462         enum CRStatus status = CR_OK;
1463         CRInputPos init_pos;
1465         g_return_val_if_fail (a_this && PRIVATE (a_this)
1466                               && PRIVATE (a_this)->tknzr
1467                               && a_property, 
1468                               CR_BAD_PARAM_ERROR);
1470         RECORD_INITIAL_POS (a_this, &init_pos);
1472         status = cr_parser_parse_ident (a_this, a_property);
1473         CHECK_PARSING_STATUS (status, TRUE);
1474         
1475         cr_parser_try_to_skip_spaces_and_comments (a_this);
1477         cr_parser_clear_errors (a_this);
1478         return CR_OK;
1480       error:
1482         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1484         return status;
1487 /**
1488  *Parses a "term" as defined in the css2 spec, appendix D.1:
1489  *term ::= unary_operator? [NUMBER S* | PERCENTAGE S* | LENGTH S* | 
1490  *EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* | function ] |
1491  *STRING S* | IDENT S* | URI S* | RGB S* | UNICODERANGE S* | hexcolor
1492  *
1493  *TODO: handle parsing of 'RGB'
1494  *
1495  *@param a_term out parameter. The successfully parsed term.
1496  *@return CR_OK upon successfull completion, an error code otherwise.
1497  */
1498 enum CRStatus
1499 cr_parser_parse_term (CRParser * a_this, CRTerm ** a_term)
1501         enum CRStatus status = CR_PARSING_ERROR;
1502         CRInputPos init_pos;
1503         CRTerm *result = NULL;
1504         CRTerm *param = NULL;
1505         CRToken *token = NULL;
1506         CRString *func_name = NULL;
1507         CRParsingLocation location = {0,0,0} ;
1509         g_return_val_if_fail (a_this && a_term, CR_BAD_PARAM_ERROR);
1511         RECORD_INITIAL_POS (a_this, &init_pos);
1513         result = cr_term_new ();
1515         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
1516                                           &token);
1517         if (status != CR_OK || !token)
1518                 goto error;
1520         cr_parsing_location_copy (&location, &token->location) ;
1521         if (token->type == DELIM_TK && token->u.unichar == '+') {
1522                 result->unary_op = PLUS_UOP;
1523                 cr_token_destroy (token) ;
1524                 token = NULL ;
1525                 cr_parser_try_to_skip_spaces_and_comments (a_this);
1526                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
1527                                                   &token);
1528                 if (status != CR_OK || !token)
1529                         goto error;
1530         } else if (token->type == DELIM_TK && token->u.unichar == '-') {
1531                 result->unary_op = MINUS_UOP;
1532                 cr_token_destroy (token) ;
1533                 token = NULL ;
1534                 cr_parser_try_to_skip_spaces_and_comments (a_this);
1535                 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
1536                                                   &token);
1537                 if (status != CR_OK || !token)
1538                         goto error;
1539         }
1541         if (token->type == EMS_TK
1542             || token->type == EXS_TK
1543             || token->type == LENGTH_TK
1544             || token->type == ANGLE_TK
1545             || token->type == TIME_TK
1546             || token->type == FREQ_TK
1547             || token->type == PERCENTAGE_TK
1548             || token->type == NUMBER_TK) {
1549                 status = cr_term_set_number (result, token->u.num);
1550                 CHECK_PARSING_STATUS (status, TRUE);
1551                 token->u.num = NULL;
1552                 status = CR_OK;
1553         } else if (token && token->type == FUNCTION_TK) {
1554                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
1555                                                token);
1556                 token = NULL;
1557                 status = cr_parser_parse_function (a_this, &func_name,
1558                                                    &param);
1560                 if (status == CR_OK) {
1561                         status = cr_term_set_function (result,
1562                                                        func_name,
1563                                                        param);
1564                         CHECK_PARSING_STATUS (status, TRUE);
1565                 }
1566         } else if (token && token->type == STRING_TK) {
1567                 status = cr_term_set_string (result, 
1568                                              token->u.str);
1569                 CHECK_PARSING_STATUS (status, TRUE);
1570                 token->u.str = NULL;
1571         } else if (token && token->type == IDENT_TK) {
1572                 status = cr_term_set_ident (result, token->u.str);
1573                 CHECK_PARSING_STATUS (status, TRUE);
1574                 token->u.str = NULL;
1575         } else if (token && token->type == URI_TK) {
1576                 status = cr_term_set_uri (result, token->u.str);
1577                 CHECK_PARSING_STATUS (status, TRUE);
1578                 token->u.str = NULL;
1579         } else if (token && token->type == RGB_TK) {
1580                 status = cr_term_set_rgb (result, token->u.rgb);
1581                 CHECK_PARSING_STATUS (status, TRUE);
1582                 token->u.rgb = NULL;
1583         } else if (token && token->type == UNICODERANGE_TK) {
1584                 result->type = TERM_UNICODERANGE;
1585                 status = CR_PARSING_ERROR;
1586         } else if (token && token->type == HASH_TK) {
1587                 status = cr_term_set_hash (result, token->u.str);
1588                 CHECK_PARSING_STATUS (status, TRUE);
1589                 token->u.str = NULL;
1590         } else {
1591                 status = CR_PARSING_ERROR;
1592         }
1594         if (status != CR_OK) {
1595                 goto error;
1596         }
1597         cr_parsing_location_copy (&result->location,
1598                                   &location) ;
1599         *a_term = cr_term_append_term (*a_term, result);
1601         result = NULL;
1603         cr_parser_try_to_skip_spaces_and_comments (a_this);
1605         if (token) {
1606                 cr_token_destroy (token);
1607                 token = NULL;
1608         }
1610         cr_parser_clear_errors (a_this);
1611         return CR_OK;
1613  error:
1615         if (result) {
1616                 cr_term_destroy (result);
1617                 result = NULL;
1618         }
1620         if (token) {
1621                 cr_token_destroy (token);
1622                 token = NULL;
1623         }
1625         if (param) {
1626                 cr_term_destroy (param);
1627                 param = NULL;
1628         }
1630         if (func_name) {
1631                 cr_string_destroy (func_name);
1632                 func_name = NULL;
1633         }
1635         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1637         return status;
1640 /**
1641  *Parses a "simple_selector" as defined by the css2 spec in appendix D.1 :
1642  *element_name? [ HASH | class | attrib | pseudo ]* S*
1643  *and where pseudo is:
1644  *pseudo ::=  ':' [ IDENT | FUNCTION S* IDENT S* ')' ]
1645  *
1646  *@Param a_this the "this pointer" of the current instance of #CRParser.
1647  *@param a_sel out parameter. Is set to the successfully parsed simple
1648  *selector.
1649  *@return CR_OK upon successfull completion, an error code otherwise.
1650  */
1651 static enum CRStatus
1652 cr_parser_parse_simple_selector (CRParser * a_this, CRSimpleSel ** a_sel)
1654         enum CRStatus status = CR_ERROR;
1655         CRInputPos init_pos;
1656         CRToken *token = NULL;
1657         CRSimpleSel *sel = NULL;
1658         CRAdditionalSel *add_sel_list = NULL;
1659         gboolean found_sel = FALSE;
1660         guint32 cur_char = 0;
1662         g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
1664         RECORD_INITIAL_POS (a_this, &init_pos);
1666         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1667         if (status != CR_OK)
1668                 goto error;
1670         sel = cr_simple_sel_new ();
1671         ENSURE_PARSING_COND (sel);
1673         cr_parsing_location_copy 
1674                 (&sel->location, 
1675                  &token->location) ;
1677         if (token && token->type == DELIM_TK 
1678             && token->u.unichar == '*') {
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                 sel->type_mask |= TYPE_SELECTOR;
1685                 token->u.str = NULL;
1686                 found_sel = TRUE;
1687         } else {
1688                 status = cr_tknzr_unget_token 
1689                         (PRIVATE (a_this)->tknzr,
1690                          token);
1691                 token = NULL;
1692         }
1694         if (token) {
1695                 cr_token_destroy (token);
1696                 token = NULL;
1697         }
1699         cr_parser_try_to_skip_spaces_and_comments (a_this);
1701         for (;;) {
1702                 if (token) {
1703                         cr_token_destroy (token);
1704                         token = NULL;
1705                 }
1707                 status = cr_tknzr_get_next_token 
1708                         (PRIVATE (a_this)->tknzr,
1709                          &token);
1710                 if (status != CR_OK)
1711                         goto error;
1713                 if (token && token->type == HASH_TK) {
1714                         /*we parsed an attribute id */
1715                         CRAdditionalSel *add_sel = NULL;
1717                         add_sel = cr_additional_sel_new_with_type
1718                                 (ID_ADD_SELECTOR);
1720                         add_sel->content.id_name = token->u.str;
1721                         token->u.str = NULL;
1723                         cr_parsing_location_copy 
1724                                 (&add_sel->location,
1725                                  &token->location) ;
1726                         add_sel_list =
1727                                 cr_additional_sel_append
1728                                 (add_sel_list, add_sel);                        
1729                         found_sel = TRUE;
1730                 } else if (token && (token->type == DELIM_TK)
1731                            && (token->u.unichar == '.')) {
1732                         cr_token_destroy (token);
1733                         token = NULL;
1735                         status = cr_tknzr_get_next_token
1736                                 (PRIVATE (a_this)->tknzr, &token);
1737                         if (status != CR_OK)
1738                                 goto error;
1740                         if (token && token->type == IDENT_TK) {
1741                                 CRAdditionalSel *add_sel = NULL;
1743                                 add_sel = cr_additional_sel_new_with_type
1744                                         (CLASS_ADD_SELECTOR);
1746                                 add_sel->content.class_name = token->u.str;
1747                                 token->u.str = NULL;
1749                                 add_sel_list =
1750                                         cr_additional_sel_append
1751                                         (add_sel_list, add_sel);
1752                                 found_sel = TRUE;
1754                                 cr_parsing_location_copy 
1755                                         (&add_sel->location, 
1756                                          & token->location) ;
1757                         } else {
1758                                 status = CR_PARSING_ERROR;
1759                                 goto error;
1760                         }
1761                 } else if (token && token->type == BO_TK) {
1762                         CRAttrSel *attr_sel = NULL;
1763                         CRAdditionalSel *add_sel = NULL;
1765                         status = cr_tknzr_unget_token
1766                                 (PRIVATE (a_this)->tknzr, token);
1767                         if (status != CR_OK)
1768                                 goto error;
1769                         token = NULL;
1771                         status = cr_parser_parse_attribute_selector
1772                                 (a_this, &attr_sel);
1773                         CHECK_PARSING_STATUS (status, FALSE);
1775                         add_sel = cr_additional_sel_new_with_type
1776                                 (ATTRIBUTE_ADD_SELECTOR);
1778                         ENSURE_PARSING_COND (add_sel != NULL);
1780                         add_sel->content.attr_sel = attr_sel;
1782                         add_sel_list =
1783                                 cr_additional_sel_append
1784                                 (add_sel_list, add_sel);
1785                         found_sel = TRUE;
1786                         cr_parsing_location_copy 
1787                                 (&add_sel->location,
1788                                  &attr_sel->location) ;
1789                 } else if (token && (token->type == DELIM_TK)
1790                            && (token->u.unichar == ':')) {
1791                         CRPseudo *pseudo = NULL;
1793                         /*try to parse a pseudo */
1795                         if (token) {
1796                                 cr_token_destroy (token);
1797                                 token = NULL;
1798                         }
1800                         pseudo = cr_pseudo_new ();
1802                         status = cr_tknzr_get_next_token
1803                                 (PRIVATE (a_this)->tknzr, &token);
1804                         ENSURE_PARSING_COND (status == CR_OK && token);
1806                         cr_parsing_location_copy 
1807                                 (&pseudo->location, 
1808                                  &token->location) ;
1810                         if (token->type == IDENT_TK) {
1811                                 pseudo->type = IDENT_PSEUDO;
1812                                 pseudo->name = token->u.str;
1813                                 token->u.str = NULL;
1814                                 found_sel = TRUE;
1815                         } else if (token->type == FUNCTION_TK) {
1816                                 pseudo->name = token->u.str;
1817                                 token->u.str = NULL;
1818                                 cr_parser_try_to_skip_spaces_and_comments
1819                                         (a_this);
1820                                 status = cr_parser_parse_ident
1821                                         (a_this, &pseudo->extra);
1823                                 ENSURE_PARSING_COND (status == CR_OK);
1824                                 READ_NEXT_CHAR (a_this, &cur_char);
1825                                 ENSURE_PARSING_COND (cur_char == ')');
1826                                 pseudo->type = FUNCTION_PSEUDO;
1827                                 found_sel = TRUE;
1828                         } else {
1829                                 status = CR_PARSING_ERROR;
1830                                 goto error;
1831                         }
1833                         if (status == CR_OK) {
1834                                 CRAdditionalSel *add_sel = NULL;
1836                                 add_sel = cr_additional_sel_new_with_type
1837                                         (PSEUDO_CLASS_ADD_SELECTOR);
1839                                 add_sel->content.pseudo = pseudo;
1840                                 cr_parsing_location_copy 
1841                                         (&add_sel->location, 
1842                                          &pseudo->location) ;
1843                                 add_sel_list =
1844                                         cr_additional_sel_append
1845                                         (add_sel_list, add_sel);
1846                                 status = CR_OK;
1847                         }
1848                 } else {
1849                         status = cr_tknzr_unget_token
1850                                 (PRIVATE (a_this)->tknzr, token);
1851                         token = NULL;
1852                         break;
1853                 }
1854         }
1856         if (status == CR_OK && found_sel == TRUE) {
1857                 cr_parser_try_to_skip_spaces_and_comments (a_this);
1859                 sel->add_sel = add_sel_list;
1860                 add_sel_list = NULL;
1861                 
1862                 if (*a_sel == NULL) {
1863                         *a_sel = sel;
1864                 } else {
1865                         cr_simple_sel_append_simple_sel (*a_sel, sel);
1866                 }
1868                 sel = NULL;
1870                 if (token) {
1871                         cr_token_destroy (token);
1872                         token = NULL;
1873                 }
1875                 cr_parser_clear_errors (a_this);
1876                 return CR_OK;
1877         } else {
1878                 status = CR_PARSING_ERROR;
1879         }
1881  error:
1883         if (token) {
1884                 cr_token_destroy (token);
1885                 token = NULL;
1886         }
1888         if (add_sel_list) {
1889                 cr_additional_sel_destroy (add_sel_list);
1890                 add_sel_list = NULL;
1891         }
1893         if (sel) {
1894                 cr_simple_sel_destroy (sel);
1895                 sel = NULL;
1896         }
1898         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1900         return status;
1904 /**
1905  *Parses a "selector" as defined by the css2 spec in appendix D.1:
1906  *selector ::=  simple_selector [ combinator simple_selector ]*
1907  *
1908  *@param a_this the this pointer of the current instance of #CRParser.
1909  *@param a_start a pointer to the 
1910  *first chararcter of the successfully parsed
1911  *string.
1912  *@param a_end a pointer to the last character of the successfully parsed
1913  *string.
1914  *@return CR_OK upon successfull completion, an error code otherwise.
1915  */
1916 static enum CRStatus
1917 cr_parser_parse_simple_sels (CRParser * a_this, 
1918                              CRSimpleSel ** a_sel)
1920         enum CRStatus status = CR_ERROR;
1921         CRInputPos init_pos;
1922         CRSimpleSel *sel = NULL;
1923         guint32 cur_char = 0;
1925         g_return_val_if_fail (a_this                               
1926                               && PRIVATE (a_this)
1927                               && a_sel,
1928                               CR_BAD_PARAM_ERROR);
1930         RECORD_INITIAL_POS (a_this, &init_pos);
1932         status = cr_parser_parse_simple_selector (a_this, &sel);
1933         CHECK_PARSING_STATUS (status, FALSE);
1935         *a_sel = cr_simple_sel_append_simple_sel (*a_sel, sel);
1937         for (;;) {
1938                 guint32 next_char = 0;
1939                 enum Combinator comb = 0;
1941                 sel = NULL;
1943                 PEEK_NEXT_CHAR (a_this, &next_char);
1945                 if (next_char == '+') {
1946                         READ_NEXT_CHAR (a_this, &cur_char);
1947                         comb = COMB_PLUS;
1948                         cr_parser_try_to_skip_spaces_and_comments (a_this);
1949                 } else if (next_char == '>') {
1950                         READ_NEXT_CHAR (a_this, &cur_char);
1951                         comb = COMB_GT;
1952                         cr_parser_try_to_skip_spaces_and_comments (a_this);
1953                 } else {
1954                         comb = COMB_WS;
1955                 }
1957                 status = cr_parser_parse_simple_selector (a_this, &sel);
1958                 if (status != CR_OK)
1959                         break;
1961                 if (comb && sel) {
1962                         sel->combinator = comb;
1963                         comb = 0;
1964                 }
1965                 if (sel) {
1966                         *a_sel = cr_simple_sel_append_simple_sel (*a_sel, 
1967                                                                   sel) ;
1968                 }
1969         }
1970         cr_parser_clear_errors (a_this);
1971         return CR_OK;
1973  error:
1975         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1977         return status;
1980 /**
1981  *Parses a comma separated list of selectors.
1982  *@param a_this the current instance of #CRParser.
1983  *@param a_selector the parsed list of comma separated
1984  *selectors.
1985  *@return CR_OK upon successful completion, an error
1986  *code otherwise.
1987  */
1988 static enum CRStatus
1989 cr_parser_parse_selector (CRParser * a_this, 
1990                           CRSelector ** a_selector)
1992         enum CRStatus status = CR_OK;
1993         CRInputPos init_pos;
1994         guint32 cur_char = 0,
1995                 next_char = 0;
1996         CRSimpleSel *simple_sels = NULL;
1997         CRSelector *selector = NULL;
1999         g_return_val_if_fail (a_this && a_selector, CR_BAD_PARAM_ERROR);
2001         RECORD_INITIAL_POS (a_this, &init_pos);
2003         status = cr_parser_parse_simple_sels (a_this, &simple_sels);
2004         CHECK_PARSING_STATUS (status, FALSE);
2006         if (simple_sels) {
2007                 selector = cr_selector_append_simple_sel
2008                         (selector, simple_sels);
2009                 if (selector) {
2010                         cr_parsing_location_copy
2011                                 (&selector->location,
2012                                  &simple_sels->location) ;
2013                 }
2014                 simple_sels = NULL;
2015         } else {
2016                 status = CR_PARSING_ERROR ;
2017                 goto error ;
2018         }
2020         status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
2021                                      &next_char);
2022         if (status != CR_OK) {
2023                 if (status == CR_END_OF_INPUT_ERROR) {
2024                         status = CR_OK;
2025                         goto okay;
2026                 } else {
2027                         goto error;
2028                 }
2029         }
2031         if (next_char == ',') {
2032                 for (;;) {
2033                         simple_sels = NULL;
2035                         status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
2036                                                      &next_char);
2037                         if (status != CR_OK) {
2038                                 if (status == CR_END_OF_INPUT_ERROR) {
2039                                         status = CR_OK;
2040                                         break;
2041                                 } else {
2042                                         goto error;
2043                                 }
2044                         }
2046                         if (next_char != ',')
2047                                 break;
2049                         /*consume the ',' char */
2050                         READ_NEXT_CHAR (a_this, &cur_char);
2052                         cr_parser_try_to_skip_spaces_and_comments (a_this);
2054                         status = cr_parser_parse_simple_sels
2055                                 (a_this, &simple_sels);
2057                         CHECK_PARSING_STATUS (status, FALSE);
2059                         if (simple_sels) {
2060                                 selector =
2061                                         cr_selector_append_simple_sel
2062                                         (selector, simple_sels);
2064                                 simple_sels = NULL;
2065                         }
2066                 }
2067         }
2069       okay:
2070         cr_parser_try_to_skip_spaces_and_comments (a_this);
2072         if (!*a_selector) {
2073                 *a_selector = selector;
2074         } else {
2075                 *a_selector = cr_selector_append (*a_selector, selector);
2076         }
2078         selector = NULL;
2079         return CR_OK;
2081       error:
2083         if (simple_sels) {
2084                 cr_simple_sel_destroy (simple_sels);
2085                 simple_sels = NULL;
2086         }
2088         if (selector) {
2089                 cr_selector_unref (selector);
2090                 selector = NULL;
2091         }
2093         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
2095         return status;
2098 /**
2099  *Parses a "function" as defined in css spec at appendix D.1:
2100  *function ::= FUNCTION S* expr ')' S*
2101  *FUNCTION ::= ident'('
2102  *
2103  *@param a_this the "this pointer" of the current instance of
2104  *#CRParser.
2105  *
2106  *@param a_func_name out parameter. The parsed function name
2107  *@param a_expr out parameter. The successfully parsed term.
2108  *@return CR_OK upon successfull completion, an error code otherwise.
2109  */
2110 static enum CRStatus
2111 cr_parser_parse_function (CRParser * a_this,
2112                           CRString ** a_func_name,
2113                           CRTerm ** a_expr)
2115         CRInputPos init_pos;
2116         enum CRStatus status = CR_OK;
2117         CRToken *token = NULL;
2118         CRTerm *expr = NULL;
2120         g_return_val_if_fail (a_this && PRIVATE (a_this)
2121                               && a_func_name,
2122                               CR_BAD_PARAM_ERROR);
2124         RECORD_INITIAL_POS (a_this, &init_pos);
2126         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
2127         if (status != CR_OK)
2128                 goto error;
2130         if (token && token->type == FUNCTION_TK) {
2131                 *a_func_name = token->u.str;
2132                 token->u.str = NULL;
2133         } else {
2134                 status = CR_PARSING_ERROR;
2135                 goto error;
2136         }
2137         cr_token_destroy (token);
2138         token = NULL;
2139         
2140         cr_parser_try_to_skip_spaces_and_comments (a_this) ;
2142         status = cr_parser_parse_expr (a_this, &expr);
2144         CHECK_PARSING_STATUS (status, FALSE);
2146         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
2147         if (status != CR_OK)
2148                 goto error;
2150         ENSURE_PARSING_COND (token && token->type == PC_TK);
2152         cr_token_destroy (token);
2153         token = NULL;
2155         if (expr) {
2156                 *a_expr = cr_term_append_term (*a_expr, expr);
2157                 expr = NULL;
2158         }
2160         cr_parser_clear_errors (a_this);
2161         return CR_OK;
2163       error:
2165         if (*a_func_name) {
2166                 cr_string_destroy (*a_func_name);
2167                 *a_func_name = NULL;
2168         }
2170         if (expr) {
2171                 cr_term_destroy (expr);
2172                 expr = NULL;
2173         }
2175         if (token) {
2176                 cr_token_destroy (token);
2178         }
2180         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
2182         return status;
2185 /**
2186  *Parses an uri as defined by the css spec [4.1.1]:
2187  * URI ::= url\({w}{string}{w}\)
2188  *         |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)
2189  *
2190  *@param a_this the current instance of #CRParser.
2191  *@param a_str the successfully parsed url.
2192  *@return CR_OK upon successfull completion, an error code otherwise.
2193  */
2194 static enum CRStatus
2195 cr_parser_parse_uri (CRParser * a_this, CRString ** a_str)
2198         enum CRStatus status = CR_PARSING_ERROR;
2200         g_return_val_if_fail (a_this && PRIVATE (a_this)
2201                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
2203         status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
2204                                        URI_TK, NO_ET, a_str, NULL);
2205         return status;
2208 /**
2209  *Parses a string type as defined in css spec [4.1.1]:
2210  *
2211  *string ::= {string1}|{string2}
2212  *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
2213  *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
2214  *
2215  *@param a_this the current instance of #CRParser.
2216  *@param a_start out parameter. Upon successfull completion, 
2217  *points to the beginning of the string, points to an undefined value
2218  *otherwise.
2219  *@param a_end out parameter. Upon successfull completion, points to
2220  *the beginning of the string, points to an undefined value otherwise.
2221  *@return CR_OK upon successfull completion, an error code otherwise.
2222  */
2223 static enum CRStatus
2224 cr_parser_parse_string (CRParser * a_this, CRString ** a_str)
2226         enum CRStatus status = CR_OK;
2228         g_return_val_if_fail (a_this && PRIVATE (a_this)
2229                               && PRIVATE (a_this)->tknzr
2230                               && a_str, CR_BAD_PARAM_ERROR);
2232         status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
2233                                        STRING_TK, NO_ET, a_str, NULL);
2234         return status;
2237 /**
2238  *Parses an "ident" as defined in css spec [4.1.1]:
2239  *ident ::= {nmstart}{nmchar}*
2240  *
2241  *@param a_this the currens instance of #CRParser.
2242  *
2243  *@param a_str a pointer to parsed ident. If *a_str is NULL,
2244  *this function allocates a new instance of #CRString. If not, 
2245  *the function just appends the parsed string to the one passed.
2246  *In both cases it is up to the caller to free *a_str.
2247  *
2248  *@return CR_OK upon successfull completion, an error code 
2249  *otherwise.
2250  */
2251 static enum CRStatus
2252 cr_parser_parse_ident (CRParser * a_this, CRString ** a_str)
2254         enum CRStatus status = CR_OK;
2256         g_return_val_if_fail (a_this && PRIVATE (a_this)
2257                               && PRIVATE (a_this)->tknzr
2258                               && a_str, CR_BAD_PARAM_ERROR);
2260         status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
2261                                        IDENT_TK, NO_ET, a_str, NULL);
2262         return status;
2265 /**
2266  *the next rule is ignored as well. This seems to be a bug
2267  *Parses a stylesheet as defined in the css2 spec in appendix D.1:
2268  *stylesheet ::= [ CHARSET_SYM S* STRING S* ';' ]? 
2269  *               [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
2270  *               [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]*
2271  *
2272  *TODO: Finish the code of this function. Think about splitting it into
2273  *smaller functions.
2274  *
2275  *@param a_this the "this pointer" of the current instance of #CRParser.
2276  *@param a_start out parameter. A pointer to the first character of
2277  *the successfully parsed string.
2278  *@param a_end out parameter. A pointer to the first character of
2279  *the successfully parsed string.
2280  *
2281  *@return CR_OK upon successfull completion, an error code otherwise.
2282  */
2283 static enum CRStatus
2284 cr_parser_parse_stylesheet (CRParser * a_this)
2286         enum CRStatus status = CR_OK;
2287         CRInputPos init_pos;
2288         CRToken *token = NULL;
2289         CRString *charset = NULL;
2291         g_return_val_if_fail (a_this && PRIVATE (a_this)
2292                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
2294         RECORD_INITIAL_POS (a_this, &init_pos);
2296         PRIVATE (a_this)->state = READY_STATE;
2298         if (PRIVATE (a_this)->sac_handler
2299             && PRIVATE (a_this)->sac_handler->start_document) {
2300                 PRIVATE (a_this)->sac_handler->start_document
2301                         (PRIVATE (a_this)->sac_handler);
2302         }
2304  parse_charset:
2305         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
2307         if (status == CR_END_OF_INPUT_ERROR)
2308                 goto done;
2309         CHECK_PARSING_STATUS (status, TRUE);
2311         if (token && token->type == CHARSET_SYM_TK) {
2312                 CRParsingLocation location = {0,0,0} ;
2313                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2314                                                token);
2315                 CHECK_PARSING_STATUS (status, TRUE);
2316                 token = NULL;
2318                 status = cr_parser_parse_charset (a_this, 
2319                                                   &charset,
2320                                                   &location);
2322                 if (status == CR_OK && charset) {
2323                         if (PRIVATE (a_this)->sac_handler
2324                             && PRIVATE (a_this)->sac_handler->charset) {
2325                                 PRIVATE (a_this)->sac_handler->charset
2326                                         (PRIVATE (a_this)->sac_handler,
2327                                          charset, &location);
2328                         }
2329                 } else if (status != CR_END_OF_INPUT_ERROR) {
2330                         status = cr_parser_parse_atrule_core (a_this);
2331                         CHECK_PARSING_STATUS (status, FALSE);
2332                 }
2334                 if (charset) {
2335                         cr_string_destroy (charset);
2336                         charset = NULL;
2337                 }
2338         } else if (token
2339                    && (token->type == S_TK 
2340                        || token->type == COMMENT_TK)) {
2341                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2342                                                token);
2343                 token = NULL;
2344                 CHECK_PARSING_STATUS (status, TRUE);
2346                 cr_parser_try_to_skip_spaces_and_comments (a_this);
2347                 goto parse_charset ;
2348         } else if (token) {
2349                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2350                                                token);
2351                 token = NULL;
2352                 CHECK_PARSING_STATUS (status, TRUE);
2353         }
2355 /* parse_imports:*/
2356         do {
2357                 if (token) {
2358                         cr_token_destroy (token);
2359                         token = NULL;
2360                 }
2361                 cr_parser_try_to_skip_spaces_and_comments (a_this) ;
2362                 status = cr_tknzr_get_next_token
2363                         (PRIVATE (a_this)->tknzr, &token);
2365                 if (status == CR_END_OF_INPUT_ERROR)
2366                         goto done;
2367                 CHECK_PARSING_STATUS (status, TRUE);
2368         } while (token
2369                  && (token->type == S_TK
2370                      || token->type == CDO_TK || token->type == CDC_TK));
2372         if (token) {
2373                 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2374                                                token);
2375                 token = NULL;
2376         }
2378         for (;;) {
2379                 status = cr_tknzr_get_next_token
2380                         (PRIVATE (a_this)->tknzr, &token);
2381                 if (status == CR_END_OF_INPUT_ERROR)
2382                         goto done;
2383                 CHECK_PARSING_STATUS (status, TRUE);
2385                 if (token && token->type == IMPORT_SYM_TK) {
2386                         GList *media_list = NULL;
2387                         CRString *import_string = NULL;
2388                         CRParsingLocation location = {0,0,0} ;
2390                         status = cr_tknzr_unget_token
2391                                 (PRIVATE (a_this)->tknzr, token);
2392                         token = NULL;
2393                         CHECK_PARSING_STATUS (status, TRUE);
2395                         status = cr_parser_parse_import (a_this,
2396                                                          &media_list,
2397                                                          &import_string,
2398                                                          &location);
2399                         if (status == CR_OK) {
2400                                 if (import_string
2401                                     && PRIVATE (a_this)->sac_handler
2402                                     && PRIVATE (a_this)->sac_handler->import_style) {
2403                                         PRIVATE (a_this)->sac_handler->import_style 
2404                                                 (PRIVATE(a_this)->sac_handler,
2405                                                  media_list,
2406                                                  import_string,
2407                                                  NULL, &location) ;
2409                                         if ((PRIVATE (a_this)->sac_handler->resolve_import == TRUE)) {
2410                                                 /*
2411                                                  *TODO: resolve the
2412                                                  *import rule.
2413                                                  */
2414                                         }
2416                                         if ((PRIVATE (a_this)->sac_handler->import_style_result)) {
2417                                                 PRIVATE (a_this)->sac_handler->import_style_result
2418                                                         (PRIVATE (a_this)->sac_handler,
2419                                                          media_list, import_string,
2420                                                          NULL, NULL);
2421                                         }
2422                                 }
2423                         } else if (status != CR_END_OF_INPUT_ERROR) {
2424                                 if (PRIVATE (a_this)->sac_handler
2425                                     && PRIVATE (a_this)->sac_handler->error) {
2426                                         PRIVATE (a_this)->sac_handler->error
2427                                                 (PRIVATE (a_this)->sac_handler);
2428                                 }
2429                                 status = cr_parser_parse_atrule_core (a_this);
2430                                 CHECK_PARSING_STATUS (status, TRUE) ;
2431                         } else {
2432                                 goto error ;
2433                         }
2435                         /*
2436                          *then, after calling the appropriate 
2437                          *SAC handler, free
2438                          *the media_list and import_string.
2439                          */
2440                         if (media_list) {
2441                                 GList *cur = NULL;
2443                                 /*free the medium list */
2444                                 for (cur = media_list; cur; cur = cur->next) {
2445                                         if (cur->data) {
2446                                                 cr_string_destroy (cur->data);
2447                                         }
2448                                 }
2450                                 g_list_free (media_list);
2451                                 media_list = NULL;
2452                         }
2454                         if (import_string) {
2455                                 cr_string_destroy (import_string);
2456                                 import_string = NULL;
2457                         }
2459                         cr_parser_try_to_skip_spaces_and_comments (a_this);
2460                 } else if (token
2461                            && (token->type == S_TK
2462                                || token->type == CDO_TK
2463                                || token->type == CDC_TK)) {
2464                         status = cr_tknzr_unget_token
2465                                 (PRIVATE (a_this)->tknzr, token);
2466                         token = NULL;
2468                         do {
2469                                 if (token) {
2470                                         cr_token_destroy (token);
2471                                         token = NULL;
2472                                 }
2474                                 status = cr_tknzr_get_next_token
2475                                         (PRIVATE (a_this)->tknzr, &token);
2477                                 if (status == CR_END_OF_INPUT_ERROR)
2478                                         goto done;
2479                                 CHECK_PARSING_STATUS (status, TRUE);
2480                         } while (token
2481                                  && (token->type == S_TK
2482                                      || token->type == CDO_TK
2483                                      || token->type == CDC_TK));
2484                 } else {
2485                         if (token) {
2486                                 status = cr_tknzr_unget_token
2487                                         (PRIVATE (a_this)->tknzr, token);
2488                                 token = NULL;
2489                         }
2490                         goto parse_ruleset_and_others;
2491                 }
2492         }
2494  parse_ruleset_and_others:
2496         cr_parser_try_to_skip_spaces_and_comments (a_this);
2498         for (;;) {
2499                 status = cr_tknzr_get_next_token
2500                         (PRIVATE (a_this)->tknzr, &token);
2501                 if (status == CR_END_OF_INPUT_ERROR)
2502                         goto done;
2503                 CHECK_PARSING_STATUS (status, TRUE);
2505                 if (token
2506                     && (token->type == S_TK
2507                         || token->type == CDO_TK || token->type == CDC_TK)) {
2508                         status = cr_tknzr_unget_token
2509                                 (PRIVATE (a_this)->tknzr, token);
2510                         token = NULL;
2512                         do {
2513                                 if (token) {
2514                                         cr_token_destroy (token);
2515                                         token = NULL;
2516                                 }
2518                                 cr_parser_try_to_skip_spaces_and_comments
2519                                         (a_this);
2520                                 status = cr_tknzr_get_next_token
2521                                         (PRIVATE (a_this)->tknzr, &token);
2522                         } while (token
2523                                  && (token->type == S_TK
2524                                      || token->type == COMMENT_TK
2525                                      || token->type == CDO_TK
2526                                      || token->type == CDC_TK));
2527                         if (token) {
2528                                 cr_tknzr_unget_token
2529                                         (PRIVATE (a_this)->tknzr, token);
2530                                 token = NULL;
2531                         }
2532                 } else if (token
2533                            && (token->type == HASH_TK
2534                                || (token->type == DELIM_TK
2535                                    && token->u.unichar == '.')
2536                                || (token->type == DELIM_TK
2537                                    && token->u.unichar == ':')
2538                                || (token->type == DELIM_TK
2539                                    && token->u.unichar == '*')
2540                                || (token->type == BO_TK)
2541                                || token->type == IDENT_TK)) {
2542                         /*
2543                          *Try to parse a CSS2 ruleset.
2544                          *if the parsing fails, try to parse
2545                          *a css core ruleset.
2546                          */
2547                         status = cr_tknzr_unget_token
2548                                 (PRIVATE (a_this)->tknzr, token);
2549                         CHECK_PARSING_STATUS (status, TRUE);
2550                         token = NULL;
2552                         status = cr_parser_parse_ruleset (a_this);
2554                         if (status == CR_OK) {
2555                                 continue;
2556                         } else {
2557                                 if (PRIVATE (a_this)->sac_handler
2558                                     && PRIVATE (a_this)->sac_handler->error) {
2559                                         PRIVATE (a_this)->sac_handler->
2560                                                 error
2561                                                 (PRIVATE (a_this)->
2562                                                  sac_handler);
2563                                 }
2565                                 status = cr_parser_parse_ruleset_core
2566                                         (a_this);
2568                                 if (status == CR_OK) {
2569                                         continue;
2570                                 } else {
2571                                         break;
2572                                 }
2573                         }
2574                 } else if (token && token->type == MEDIA_SYM_TK) {
2575                         status = cr_tknzr_unget_token
2576                                 (PRIVATE (a_this)->tknzr, token);
2577                         CHECK_PARSING_STATUS (status, TRUE);
2578                         token = NULL;
2580                         status = cr_parser_parse_media (a_this);
2581                         if (status == CR_OK) {
2582                                 continue;
2583                         } else {
2584                                 if (PRIVATE (a_this)->sac_handler
2585                                     && PRIVATE (a_this)->sac_handler->error) {
2586                                         PRIVATE (a_this)->sac_handler->
2587                                                 error
2588                                                 (PRIVATE (a_this)->
2589                                                  sac_handler);
2590                                 }
2592                                 status = cr_parser_parse_atrule_core (a_this);
2594                                 if (status == CR_OK) {
2595                                         continue;
2596                                 } else {
2597                                         break;
2598                                 }
2599                         }
2601                 } else if (token && token->type == PAGE_SYM_TK) {
2602                         status = cr_tknzr_unget_token
2603                                 (PRIVATE (a_this)->tknzr, token);
2604                         CHECK_PARSING_STATUS (status, TRUE);
2605                         token = NULL;
2606                         status = cr_parser_parse_page (a_this);
2608                         if (status == CR_OK) {
2609                                 continue;
2610                         } else {
2611                                 if (PRIVATE (a_this)->sac_handler
2612                                     && PRIVATE (a_this)->sac_handler->error) {
2613                                         PRIVATE (a_this)->sac_handler->
2614                                                 error
2615                                                 (PRIVATE (a_this)->
2616                                                  sac_handler);
2617                                 }
2619                                 status = cr_parser_parse_atrule_core (a_this);
2621                                 if (status == CR_OK) {
2622                                         continue;
2623                                 } else {
2624                                         break;
2625                                 }
2626                         }
2627                 } else if (token && token->type == FONT_FACE_SYM_TK) {
2628                         status = cr_tknzr_unget_token
2629                                 (PRIVATE (a_this)->tknzr, token);
2630                         CHECK_PARSING_STATUS (status, TRUE);
2631                         token = NULL;
2632                         status = cr_parser_parse_font_face (a_this);
2634                         if (status == CR_OK) {
2635                                 continue;
2636                         } else {
2637                                 if (PRIVATE (a_this)->sac_handler
2638                                     && PRIVATE (a_this)->sac_handler->error) {
2639                                         PRIVATE (a_this)->sac_handler->
2640                                                 error
2641                                                 (PRIVATE (a_this)->
2642                                                  sac_handler);
2643                                 }
2645                                 status = cr_parser_parse_atrule_core (a_this);
2647                                 if (status == CR_OK) {
2648                                         continue;
2649                                 } else {
2650                                         break;
2651                                 }
2652                         }
2653                 } else {
2654                         status = cr_tknzr_unget_token
2655                                 (PRIVATE (a_this)->tknzr, token);
2656                         CHECK_PARSING_STATUS (status, TRUE);
2657                         token = NULL;
2658                         status = cr_parser_parse_statement_core (a_this);
2660                         if (status == CR_OK) {
2661                                 continue;
2662                         } else {
2663                                 break;
2664                         }
2665                 }
2666         }
2668       done:
2669         if (token) {
2670                 cr_token_destroy (token);
2671                 token = NULL;
2672         }
2674         if (status == CR_END_OF_INPUT_ERROR || status == CR_OK) {
2676                 if (PRIVATE (a_this)->sac_handler
2677                     && PRIVATE (a_this)->sac_handler->end_document) {
2678                         PRIVATE (a_this)->sac_handler->end_document
2679                                 (PRIVATE (a_this)->sac_handler);
2680                 }
2682                 return CR_OK;
2683         }
2685         cr_parser_push_error
2686                 (a_this, "could not recognize next production", CR_ERROR);
2688         if (PRIVATE (a_this)->sac_handler
2689             && PRIVATE (a_this)->sac_handler->unrecoverable_error) {
2690                 PRIVATE (a_this)->sac_handler->
2691                         unrecoverable_error (PRIVATE (a_this)->sac_handler);
2692         }
2694         cr_parser_dump_err_stack (a_this, TRUE);
2696         return status;
2698       error:
2700         if (token) {
2701                 cr_token_destroy (token);
2702                 token = NULL;
2703         }
2705         if (PRIVATE (a_this)->sac_handler
2706             && PRIVATE (a_this)->sac_handler->unrecoverable_error) {
2707                 PRIVATE (a_this)->sac_handler->
2708                         unrecoverable_error (PRIVATE (a_this)->sac_handler);
2709         }
2711         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
2713         return status;
2716 /****************************************
2717  *Public CRParser Methods
2718  ****************************************/
2720 /**
2721  *Creates a new parser to parse data
2722  *coming the input stream given in parameter.
2723  *@param a_input the input stream of the parser.
2724  *Note that the newly created parser will ref
2725  *a_input and unref it when parsing reaches the
2726  *end of the input stream.
2727  *@return the newly created instance of #CRParser,
2728  *or NULL if an error occured.
2729  */
2730 CRParser *
2731 cr_parser_new (CRTknzr * a_tknzr)
2733         CRParser *result = NULL;
2734         enum CRStatus status = CR_OK;
2736         result = g_malloc0 (sizeof (CRParser));
2738         PRIVATE (result) = g_malloc0 (sizeof (CRParserPriv));
2740         if (a_tknzr) {
2741                 status = cr_parser_set_tknzr (result, a_tknzr);
2742         }
2744         g_return_val_if_fail (status == CR_OK, NULL);
2746         return result;
2749 /**
2750  *Instanciates a new parser from a memory buffer.
2751  *@param a_buf the buffer to parse.
2752  *@param a_len the length of the data in the buffer.
2753  *@param a_enc the encoding of the input buffer a_buf.
2754  *@param a_free_buf if set to TRUE, a_buf will be freed
2755  *during the destruction of the newly built instance 
2756  *of #CRParser. If set to FALSE, it is up to the caller to
2757  *eventually free it.
2758  *@return the newly built parser, or NULL if an error arises.
2759  */
2760 CRParser *
2761 cr_parser_new_from_buf (guchar * a_buf,
2762                         gulong a_len,
2763                         enum CREncoding a_enc, 
2764                         gboolean a_free_buf)
2766         CRParser *result = NULL;
2767         CRInput *input = NULL;
2769         g_return_val_if_fail (a_buf, NULL);
2771         input = cr_input_new_from_buf (a_buf, a_len, a_enc, a_free_buf);
2772         g_return_val_if_fail (input, NULL);
2774         result = cr_parser_new_from_input (input);
2775         if (!result) {
2776                 cr_input_destroy (input);
2777                 input = NULL;
2778                 return NULL;
2779         }
2780         return result;
2783 CRParser *
2784 cr_parser_new_from_input (CRInput * a_input)
2786         CRParser *result = NULL;
2787         CRTknzr *tokenizer = NULL;
2789         if (a_input) {
2790                 tokenizer = cr_tknzr_new (a_input);
2791                 g_return_val_if_fail (tokenizer, NULL);
2792         }
2794         result = cr_parser_new (tokenizer);
2795         g_return_val_if_fail (result, NULL);
2797         return result;
2800 CRParser *
2801 cr_parser_new_from_file (const guchar * a_file_uri, enum CREncoding a_enc)
2803         CRParser *result = NULL;
2804         CRTknzr *tokenizer = NULL;
2806         tokenizer = cr_tknzr_new_from_uri (a_file_uri, a_enc);
2807         if (!tokenizer) {
2808                 cr_utils_trace_info ("Could not open input file");
2809                 return NULL;
2810         }
2812         result = cr_parser_new (tokenizer);
2813         g_return_val_if_fail (result, NULL);
2814         return result;
2817 /**
2818  *Sets a SAC document handler to the parser.
2819  *@param a_this the "this pointer" of the current instance of #CRParser.
2820  *@param a_handler the handler to set.
2821  *@return CR_OK upon successfull completion, an error code otherwise.
2822  */
2823 enum CRStatus
2824 cr_parser_set_sac_handler (CRParser * a_this, CRDocHandler * a_handler)
2826         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
2828         if (PRIVATE (a_this)->sac_handler) {
2829                 cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
2830         }
2832         PRIVATE (a_this)->sac_handler = a_handler;
2833         cr_doc_handler_ref (a_handler);
2835         return CR_OK;
2838 /**
2839  *Gets the SAC document handler.
2840  *@param a_this the "this pointer" of the current instance of
2841  *#CRParser.
2842  *@param a_handler out parameter. The returned handler.
2843  *@return CR_OK upon successfull completion, an error code
2844  *otherwise.
2845  */
2846 enum CRStatus
2847 cr_parser_get_sac_handler (CRParser * a_this, CRDocHandler ** a_handler)
2849         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
2851         *a_handler = PRIVATE (a_this)->sac_handler;
2853         return CR_OK;
2856 /**
2857  *Sets the SAC handler associated to the current instance
2858  *of #CRParser to the default SAC handler.
2859  *@param a_this a pointer to the current instance of #CRParser.
2860  *@return CR_OK upon successfull completion, an error code otherwise.
2861  */
2862 enum CRStatus
2863 cr_parser_set_default_sac_handler (CRParser * a_this)
2865         CRDocHandler *default_sac_handler = NULL;
2866         enum CRStatus status = CR_ERROR;
2868         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
2870         default_sac_handler = cr_doc_handler_new ();
2872         cr_doc_handler_set_default_sac_handler (default_sac_handler);
2874         status = cr_parser_set_sac_handler (a_this, default_sac_handler);
2876         if (status != CR_OK) {
2877                 cr_doc_handler_destroy (default_sac_handler);
2878                 default_sac_handler = NULL;
2879         }
2881         return status;
2884 enum CRStatus
2885 cr_parser_set_use_core_grammar (CRParser * a_this,
2886                                 gboolean a_use_core_grammar)
2888         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
2890         PRIVATE (a_this)->use_core_grammar = a_use_core_grammar;
2892         return CR_OK;
2895 enum CRStatus
2896 cr_parser_get_use_core_grammar (CRParser * a_this,
2897                                 gboolean * a_use_core_grammar)
2899         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
2901         *a_use_core_grammar = PRIVATE (a_this)->use_core_grammar;
2903         return CR_OK;
2906 /**
2907  *Parses a the given in parameter.
2908  *@param a_this a pointer to the current instance of #CRParser.
2909  *@param a_file_uri the uri to the file to load. For the time being,
2910  *only local files are supported.
2911  *@return CR_OK upon successfull completion, an error code otherwise.
2912  */
2913 enum CRStatus
2914 cr_parser_parse_file (CRParser * a_this,
2915                       const guchar * a_file_uri, enum CREncoding a_enc)
2917         enum CRStatus status = CR_ERROR;
2918         CRTknzr *tknzr = NULL;
2920         g_return_val_if_fail (a_this && PRIVATE (a_this)
2921                               && a_file_uri, CR_BAD_PARAM_ERROR);
2923         tknzr = cr_tknzr_new_from_uri (a_file_uri, a_enc);
2925         g_return_val_if_fail (tknzr != NULL, CR_ERROR);
2927         status = cr_parser_set_tknzr (a_this, tknzr);
2928         g_return_val_if_fail (status == CR_OK, CR_ERROR);
2930         status = cr_parser_parse (a_this);
2932         return status;
2935 /**
2936  *Parses an expression as defined by the css2 spec in appendix
2937  *D.1:
2938  *expr: term [ operator term ]*
2939  */
2940 enum CRStatus
2941 cr_parser_parse_expr (CRParser * a_this, CRTerm ** a_expr)
2943         enum CRStatus status = CR_ERROR;
2944         CRInputPos init_pos;
2945         CRTerm *expr = NULL,
2946                 *expr2 = NULL;
2947         guchar next_byte = 0;
2948         gulong nb_terms = 0;
2950         g_return_val_if_fail (a_this && PRIVATE (a_this)
2951                               && a_expr, CR_BAD_PARAM_ERROR);
2953         RECORD_INITIAL_POS (a_this, &init_pos);
2955         status = cr_parser_parse_term (a_this, &expr);
2957         CHECK_PARSING_STATUS (status, FALSE);
2959         for (;;) {
2960                 guchar operator = 0;
2962                 status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr,
2963                                              1, &next_byte);
2964                 if (status != CR_OK) {
2965                         if (status == CR_END_OF_INPUT_ERROR) {
2966                                 /*
2967                                    if (!nb_terms)
2968                                    {
2969                                    goto error ;
2970                                    }
2971                                  */
2972                                 status = CR_OK;
2973                                 break;
2974                         } else {
2975                                 goto error;
2976                         }
2977                 }
2979                 if (next_byte == '/' || next_byte == ',') {
2980                         READ_NEXT_BYTE (a_this, &operator);
2981                 }
2983                 cr_parser_try_to_skip_spaces_and_comments (a_this);
2985                 status = cr_parser_parse_term (a_this, &expr2);
2987                 if (status != CR_OK || expr2 == NULL) {
2988                         status = CR_OK;
2989                         break;
2990                 }
2992                 switch (operator) {
2993                 case '/':
2994                         expr2->the_operator = DIVIDE;
2995                         break;
2996                 case ',':
2997                         expr2->the_operator = COMMA;
2999                 default:
3000                         break;
3001                 }
3003                 expr = cr_term_append_term (expr, expr2);
3004                 expr2 = NULL;
3005                 operator = 0;
3006                 nb_terms++;
3007         }
3009         if (status == CR_OK) {
3010                 *a_expr = cr_term_append_term (*a_expr, expr);
3011                 expr = NULL;
3013                 cr_parser_clear_errors (a_this);
3014                 return CR_OK;
3015         }
3017       error:
3019         if (expr) {
3020                 cr_term_destroy (expr);
3021                 expr = NULL;
3022         }
3024         if (expr2) {
3025                 cr_term_destroy (expr2);
3026                 expr2 = NULL;
3027         }
3029         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3031         return status;
3034 /**
3035  *Parses a declaration priority as defined by
3036  *the css2 grammar in appendix C:
3037  *prio: IMPORTANT_SYM S*
3038  *@param a_this the current instance of #CRParser.
3039  *@param a_prio a string representing the priority.
3040  *Today, only "!important" is returned as only this
3041  *priority is defined by css2.
3042  */
3043 enum CRStatus
3044 cr_parser_parse_prio (CRParser * a_this, CRString ** a_prio)
3046         enum CRStatus status = CR_ERROR;
3047         CRInputPos init_pos;
3048         CRToken *token = NULL;
3050         g_return_val_if_fail (a_this && PRIVATE (a_this)
3051                               && a_prio
3052                               && *a_prio == NULL, CR_BAD_PARAM_ERROR);
3054         RECORD_INITIAL_POS (a_this, &init_pos);
3056         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3057         if (status == CR_END_OF_INPUT_ERROR) {
3058                 goto error;
3059         }
3060         ENSURE_PARSING_COND (status == CR_OK
3061                              && token && token->type == IMPORTANT_SYM_TK);
3063         cr_parser_try_to_skip_spaces_and_comments (a_this);
3064         *a_prio = cr_string_new_from_string ("!important");
3065         cr_token_destroy (token);
3066         token = NULL;
3067         return CR_OK;
3069       error:
3070         if (token) {
3071                 cr_token_destroy (token);
3072                 token = NULL;
3073         }
3074         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3076         return status;
3079 /**
3080  *TODO: return the parsed priority, so that
3081  *upper layers can take benefit from it.
3082  *Parses a "declaration" as defined by the css2 spec in appendix D.1:
3083  *declaration ::= [property ':' S* expr prio?]?
3084  *
3085  *@param a_this the "this pointer" of the current instance of #CRParser.
3086  *@param a_property the successfully parsed property. The caller
3087  * *must* free the returned pointer.
3088  *@param a_expr the expression that represents the attribute value.
3089  *The caller *must* free the returned pointer.
3090  *@return CR_OK upon successfull completion, an error code otherwise.
3091  */
3092 enum CRStatus
3093 cr_parser_parse_declaration (CRParser * a_this,
3094                              CRString ** a_property,
3095                              CRTerm ** a_expr, gboolean * a_important)
3097         enum CRStatus status = CR_ERROR;
3098         CRInputPos init_pos;
3099         guint32 cur_char = 0;
3100         CRTerm *expr = NULL;
3101         CRString *prio = NULL;
3103         g_return_val_if_fail (a_this && PRIVATE (a_this)
3104                               && a_property && a_expr
3105                               && a_important, CR_BAD_PARAM_ERROR);
3107         RECORD_INITIAL_POS (a_this, &init_pos);
3109         status = cr_parser_parse_property (a_this, a_property);
3111         if (status == CR_END_OF_INPUT_ERROR)
3112                 goto error;
3114         CHECK_PARSING_STATUS_ERR
3115                 (a_this, status, FALSE,
3116                  "while parsing declaration: next property is malformed",
3117                  CR_SYNTAX_ERROR);
3119         READ_NEXT_CHAR (a_this, &cur_char);
3121         if (cur_char != ':') {
3122                 status = CR_PARSING_ERROR;
3123                 cr_parser_push_error
3124                         (a_this,
3125                          "while parsing declaration: this char must be ':'",
3126                          CR_SYNTAX_ERROR);
3127                 goto error;
3128         }
3130         cr_parser_try_to_skip_spaces_and_comments (a_this);
3132         status = cr_parser_parse_expr (a_this, &expr);
3134         CHECK_PARSING_STATUS_ERR
3135                 (a_this, status, FALSE,
3136                  "while parsing declaration: next expression is malformed",
3137                  CR_SYNTAX_ERROR);
3139         cr_parser_try_to_skip_spaces_and_comments (a_this);
3140         status = cr_parser_parse_prio (a_this, &prio);
3141         if (prio) {
3142                 cr_string_destroy (prio);
3143                 prio = NULL;
3144                 *a_important = TRUE;
3145         } else {
3146                 *a_important = FALSE;
3147         }
3148         if (*a_expr) {
3149                 cr_term_append_term (*a_expr, expr);
3150                 expr = NULL;
3151         } else {
3152                 *a_expr = expr;
3153                 expr = NULL;
3154         }
3156         cr_parser_clear_errors (a_this);
3157         return CR_OK;
3159       error:
3161         if (expr) {
3162                 cr_term_destroy (expr);
3163                 expr = NULL;
3164         }
3166         if (*a_property) {
3167                 cr_string_destroy (*a_property);
3168                 *a_property = NULL;
3169         }
3171         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3173         return status;
3176 /**
3177  *Parses a statement as defined by the css core grammar in
3178  *chapter 4.1 of the css2 spec.
3179  *statement   : ruleset | at-rule;
3180  *@param a_this the current instance of #CRParser.
3181  *@return CR_OK upon successfull completion, an error code otherwise.
3182  */
3183 enum CRStatus
3184 cr_parser_parse_statement_core (CRParser * a_this)
3186         CRToken *token = NULL;
3187         CRInputPos init_pos;
3188         enum CRStatus status = CR_ERROR;
3190         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
3192         RECORD_INITIAL_POS (a_this, &init_pos);
3194         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3196         ENSURE_PARSING_COND (status == CR_OK && token);
3198         switch (token->type) {
3199         case ATKEYWORD_TK:
3200         case IMPORT_SYM_TK:
3201         case PAGE_SYM_TK:
3202         case MEDIA_SYM_TK:
3203         case FONT_FACE_SYM_TK:
3204         case CHARSET_SYM_TK:
3205                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3206                 token = NULL;
3207                 status = cr_parser_parse_atrule_core (a_this);
3208                 CHECK_PARSING_STATUS (status, TRUE);
3209                 break;
3211         default:
3212                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3213                 token = NULL;
3214                 status = cr_parser_parse_ruleset_core (a_this);
3215                 cr_parser_clear_errors (a_this);
3216                 CHECK_PARSING_STATUS (status, TRUE);
3217         }
3219         return CR_OK;
3221       error:
3222         if (token) {
3223                 cr_token_destroy (token);
3224                 token = NULL;
3225         }
3227         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3229         return status;
3232 /**
3233  *Parses a "ruleset" as defined in the css2 spec at appendix D.1.
3234  *ruleset ::= selector [ ',' S* selector ]* 
3235  *'{' S* declaration? [ ';' S* declaration? ]* '}' S*;
3236  *
3237  *This methods calls the the SAC handler on the relevant SAC handler
3238  *callbacks whenever it encounters some specific constructions.
3239  *See the documentation of #CRDocHandler (the SAC handler) to know
3240  *when which SAC handler is called.
3241  *@param a_this the "this pointer" of the current instance of #CRParser.
3242  *@return CR_OK upon successfull completion, an error code otherwise.
3243  */
3244 enum CRStatus
3245 cr_parser_parse_ruleset (CRParser * a_this)
3247         enum CRStatus status = CR_OK;
3248         CRInputPos init_pos;
3249         guint32 cur_char = 0,
3250                 next_char = 0;
3251         CRString *property = NULL;
3252         CRTerm *expr = NULL;
3253         CRSimpleSel *simple_sels = NULL;
3254         CRSelector *selector = NULL;
3255         gboolean start_selector = FALSE,
3256                 is_important = FALSE;
3258         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
3260         RECORD_INITIAL_POS (a_this, &init_pos);
3262         status = cr_parser_parse_selector (a_this, &selector);
3263         CHECK_PARSING_STATUS (status, FALSE);
3265         READ_NEXT_CHAR (a_this, &cur_char);
3267         ENSURE_PARSING_COND_ERR
3268                 (a_this, cur_char == '{',
3269                  "while parsing rulset: current char should be '{'",
3270                  CR_SYNTAX_ERROR);
3272         if (PRIVATE (a_this)->sac_handler
3273             && PRIVATE (a_this)->sac_handler->start_selector) {
3274                 /*
3275                  *the selector is ref counted so that the parser's user
3276                  *can choose to keep it.
3277                  */
3278                 if (selector) {
3279                         cr_selector_ref (selector);
3280                 }
3282                 PRIVATE (a_this)->sac_handler->start_selector
3283                         (PRIVATE (a_this)->sac_handler, selector);
3284                 start_selector = TRUE;
3285         }
3287         cr_parser_try_to_skip_spaces_and_comments (a_this);
3289         PRIVATE (a_this)->state = TRY_PARSE_RULESET_STATE;
3291         status = cr_parser_parse_declaration (a_this, &property,
3292                                               &expr,
3293                                               &is_important);
3294         if (expr) {
3295                 cr_term_ref (expr);
3296         }
3297         if (status == CR_OK
3298             && PRIVATE (a_this)->sac_handler
3299             && PRIVATE (a_this)->sac_handler->property) {
3300                 PRIVATE (a_this)->sac_handler->property
3301                         (PRIVATE (a_this)->sac_handler, property, expr,
3302                          is_important);
3303         }        
3304         if (status == CR_OK) {
3305                 /*
3306                  *free the allocated
3307                  *'property' and 'term' before parsing
3308                  *next declarations.
3309                  */
3310                 if (property) {
3311                         cr_string_destroy (property);
3312                         property = NULL;
3313                 }
3314                 if (expr) {
3315                         cr_term_unref (expr);
3316                         expr = NULL;
3317                 }
3318         } else {/*status != CR_OK*/                
3319                 guint32 c = 0 ;
3320                 /*
3321                  *test if we have reached '}', which
3322                  *would mean that we are parsing an empty ruleset (eg. x{ })
3323                  *In that case, goto end_of_ruleset.
3324                  */
3325                 status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, &c) ;
3326                 if (status == CR_OK && c == '}') {
3327                         status = CR_OK ;
3328                         goto end_of_ruleset ;
3329                 }
3330         }
3331         CHECK_PARSING_STATUS_ERR
3332                 (a_this, status, FALSE,
3333                  "while parsing ruleset: next construction should be a declaration",
3334                  CR_SYNTAX_ERROR);
3336         for (;;) {
3337                 PEEK_NEXT_CHAR (a_this, &next_char);
3338                 if (next_char != ';')
3339                         break;
3341                 /*consume the ';' char */
3342                 READ_NEXT_CHAR (a_this, &cur_char);
3344                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3346                 status = cr_parser_parse_declaration (a_this, &property,
3347                                                       &expr, &is_important);
3349                 if (expr) {
3350                         cr_term_ref (expr);
3351                 }
3352                 if (status == CR_OK
3353                     && PRIVATE (a_this)->sac_handler
3354                     && PRIVATE (a_this)->sac_handler->property) {
3355                         PRIVATE (a_this)->sac_handler->property
3356                                 (PRIVATE (a_this)->sac_handler,
3357                                  property, expr, is_important);
3358                 }
3359                 if (property) {
3360                         cr_string_destroy (property);
3361                         property = NULL;
3362                 }
3363                 if (expr) {
3364                         cr_term_unref (expr);
3365                         expr = NULL;
3366                 }
3367         }
3369  end_of_ruleset:
3370         cr_parser_try_to_skip_spaces_and_comments (a_this);
3371         READ_NEXT_CHAR (a_this, &cur_char);
3372         ENSURE_PARSING_COND_ERR
3373                 (a_this, cur_char == '}',
3374                  "while parsing rulset: current char must be a '}'",
3375                  CR_SYNTAX_ERROR);
3377         if (PRIVATE (a_this)->sac_handler
3378             && PRIVATE (a_this)->sac_handler->end_selector) {
3379                 PRIVATE (a_this)->sac_handler->end_selector
3380                         (PRIVATE (a_this)->sac_handler, selector);
3381                 start_selector = FALSE;
3382         }
3384         if (expr) {
3385                 cr_term_unref (expr);
3386                 expr = NULL;
3387         }
3389         if (simple_sels) {
3390                 cr_simple_sel_destroy (simple_sels);
3391                 simple_sels = NULL;
3392         }
3394         if (selector) {
3395                 cr_selector_unref (selector);
3396                 selector = NULL;
3397         }
3399         cr_parser_clear_errors (a_this);
3400         PRIVATE (a_this)->state = RULESET_PARSED_STATE;
3402         return CR_OK;
3404  error:
3405         if (start_selector == TRUE
3406             && PRIVATE (a_this)->sac_handler
3407             && PRIVATE (a_this)->sac_handler->error) {
3408                 PRIVATE (a_this)->sac_handler->error
3409                         (PRIVATE (a_this)->sac_handler);
3410         }
3411         if (expr) {
3412                 cr_term_unref (expr);
3413                 expr = NULL;
3414         }
3415         if (simple_sels) {
3416                 cr_simple_sel_destroy (simple_sels);
3417                 simple_sels = NULL;
3418         }
3419         if (property) {
3420                 cr_string_destroy (property);
3421         }
3422         if (selector) {
3423                 cr_selector_unref (selector);
3424                 selector = NULL;
3425         }
3427         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3429         return status;
3432 /**
3433  *Parses an 'import' declaration as defined in the css2 spec
3434  *in appendix D.1:
3435  *
3436  *import ::=
3437  *@import [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S*
3438  *
3439  *@param a_this the "this pointer" of the current instance 
3440  *of #CRParser.
3441  *
3442  *@param a_medium_list out parameter. A linked list of 
3443  *#CRString
3444  *Each CRString is a string that contains
3445  *a 'medium' declaration part of the successfully 
3446  *parsed 'import' declaration.
3447  *
3448  *@param a_import_string out parameter. 
3449  *A string that contains the 'import 
3450  *string". The import string can be either an uri (if it starts with
3451  *the substring "uri(") or a any other css2 string. Note that
3452  * *a_import_string must be initially set to NULL or else, this function
3453  *will return CR_BAD_PARAM_ERROR.
3454  *
3455  *@return CR_OK upon sucessfull completion, an error code otherwise.
3456  */
3457 enum CRStatus
3458 cr_parser_parse_import (CRParser * a_this,
3459                         GList ** a_media_list,
3460                         CRString ** a_import_string,
3461                         CRParsingLocation *a_location)
3463         enum CRStatus status = CR_OK;
3464         CRInputPos init_pos;
3465         guint32 cur_char = 0,
3466                 next_char = 0;
3467         CRString *medium = NULL;
3469         g_return_val_if_fail (a_this
3470                               && a_import_string
3471                               && (*a_import_string == NULL),
3472                               CR_BAD_PARAM_ERROR);
3474         RECORD_INITIAL_POS (a_this, &init_pos);
3476         if (BYTE (a_this, 1, NULL) == '@'
3477             && BYTE (a_this, 2, NULL) == 'i'
3478             && BYTE (a_this, 3, NULL) == 'm'
3479             && BYTE (a_this, 4, NULL) == 'p'
3480             && BYTE (a_this, 5, NULL) == 'o'
3481             && BYTE (a_this, 6, NULL) == 'r'
3482             && BYTE (a_this, 7, NULL) == 't') {
3483                 SKIP_CHARS (a_this, 1);
3484                 if (a_location) {
3485                         cr_parser_get_parsing_location 
3486                                 (a_this, a_location) ;
3487                 }
3488                 SKIP_CHARS (a_this, 6);
3489                 status = CR_OK;
3490         } else {
3491                 status = CR_PARSING_ERROR;
3492                 goto error;
3493         }
3495         cr_parser_try_to_skip_spaces_and_comments (a_this);
3497         PRIVATE (a_this)->state = TRY_PARSE_IMPORT_STATE;
3499         PEEK_NEXT_CHAR (a_this, &next_char);
3501         if (next_char == '"' || next_char == '\'') {
3502                 status = cr_parser_parse_string (a_this, a_import_string);
3504                 CHECK_PARSING_STATUS (status, FALSE);
3505         } else {
3506                 status = cr_parser_parse_uri (a_this, a_import_string);
3508                 CHECK_PARSING_STATUS (status, FALSE);
3509         }
3511         cr_parser_try_to_skip_spaces_and_comments (a_this);
3513         status = cr_parser_parse_ident (a_this, &medium);
3515         if (status == CR_OK && medium) {
3516                 *a_media_list = g_list_append (*a_media_list, medium);
3517                 medium = NULL;
3518         }
3520         cr_parser_try_to_skip_spaces_and_comments (a_this);
3522         for (; status == CR_OK;) {
3523                 if ((status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
3524                                                   &next_char)) != CR_OK) {
3525                         if (status == CR_END_OF_INPUT_ERROR) {
3526                                 status = CR_OK;
3527                                 goto okay;
3528                         }
3529                         goto error;
3530                 }
3532                 if (next_char == ',') {
3533                         READ_NEXT_CHAR (a_this, &cur_char);
3534                 } else {
3535                         break;
3536                 }
3538                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3540                 status = cr_parser_parse_ident (a_this, &medium);
3542                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3544                 if ((status == CR_OK) && medium) {
3545                         *a_media_list = g_list_append (*a_media_list, medium);
3547                         medium = NULL;
3548                 }
3550                 CHECK_PARSING_STATUS (status, FALSE);
3551                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3552         }
3553         cr_parser_try_to_skip_spaces_and_comments (a_this);
3554         READ_NEXT_CHAR (a_this, &cur_char);
3555         ENSURE_PARSING_COND (cur_char == ';');
3556         cr_parser_try_to_skip_spaces_and_comments (a_this);
3557       okay:
3558         cr_parser_clear_errors (a_this);
3559         PRIVATE (a_this)->state = IMPORT_PARSED_STATE;
3561         return CR_OK;
3563       error:
3565         if (*a_media_list) {
3566                 GList *cur = NULL;
3568                 /*
3569                  *free each element of *a_media_list.
3570                  *Note that each element of *a_medium list *must*
3571                  *be a GString* or else, the code that is coming next 
3572                  *will corrupt the memory and lead to hard to debug
3573                  *random crashes.
3574                  *This is where C++ and its compile time
3575                  *type checking mecanism (through STL containers) would
3576                  *have prevented us to go through this hassle.
3577                  */
3578                 for (cur = *a_media_list; cur; cur = cur->next) {
3579                         if (cur->data) {
3580                                 cr_string_destroy (cur->data);
3581                         }
3582                 }
3584                 g_list_free (*a_media_list);
3585                 *a_media_list = NULL;
3586         }
3588         if (*a_import_string) {
3589                 cr_string_destroy (*a_import_string);
3590                 *a_import_string = NULL;
3591         }
3593         if (medium) {
3594                 cr_string_destroy (medium);
3595                 medium = NULL;
3596         }
3598         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3600         return status;
3603 /**
3604  *Parses a 'media' declaration as specified in the css2 spec at
3605  *appendix D.1:
3606  *
3607  *media ::= @media S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S*
3608  *
3609  *Note that this function calls the required sac handlers during the parsing
3610  *to notify media productions. See #CRDocHandler to know the callback called
3611  *during @media parsing.
3612  *@param a_this the "this pointer" of the current instance of #CRParser.
3613  *@return CR_OK upon successfull completion, an error code otherwise.
3614  */
3615 enum CRStatus
3616 cr_parser_parse_media (CRParser * a_this)
3618         enum CRStatus status = CR_OK;
3619         CRInputPos init_pos;
3620         CRToken *token = NULL;
3621         guint32 next_char = 0,
3622                 cur_char = 0;
3623         CRString *medium = NULL;
3624         GList *media_list = NULL;
3625         CRParsingLocation location = {0,0,0} ;
3627         g_return_val_if_fail (a_this 
3628                               && PRIVATE (a_this), 
3629                               CR_BAD_PARAM_ERROR);
3631         RECORD_INITIAL_POS (a_this, &init_pos);
3633         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
3634                                           &token);
3635         ENSURE_PARSING_COND (status == CR_OK
3636                              && token 
3637                              && token->type == MEDIA_SYM_TK);
3638         cr_parsing_location_copy (&location, &token->location) ;
3639         cr_token_destroy (token);
3640         token = NULL;
3642         cr_parser_try_to_skip_spaces_and_comments (a_this);
3644         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3645         ENSURE_PARSING_COND (status == CR_OK
3646                              && token && token->type == IDENT_TK);
3648         medium = token->u.str;
3649         token->u.str = NULL;
3650         cr_token_destroy (token);
3651         token = NULL;
3653         if (medium) {
3654                 media_list = g_list_append (media_list, medium);
3655                 medium = NULL;
3656         }
3658         for (; status == CR_OK;) {
3659                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3660                 PEEK_NEXT_CHAR (a_this, &next_char);
3662                 if (next_char == ',') {
3663                         READ_NEXT_CHAR (a_this, &cur_char);
3664                 } else {
3665                         break;
3666                 }
3668                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3670                 status = cr_parser_parse_ident (a_this, &medium);
3672                 CHECK_PARSING_STATUS (status, FALSE);
3674                 if (medium) {
3675                         media_list = g_list_append (media_list, medium);
3676                         medium = NULL;
3677                 }
3678         }
3680         READ_NEXT_CHAR (a_this, &cur_char);
3682         ENSURE_PARSING_COND (cur_char == '{');
3684         /*
3685          *call the SAC handler api here.
3686          */
3687         if (PRIVATE (a_this)->sac_handler
3688             && PRIVATE (a_this)->sac_handler->start_media) {
3689                 PRIVATE (a_this)->sac_handler->start_media
3690                         (PRIVATE (a_this)->sac_handler, media_list,
3691                          &location);
3692         }
3694         cr_parser_try_to_skip_spaces_and_comments (a_this);
3696         PRIVATE (a_this)->state = TRY_PARSE_MEDIA_STATE;
3698         for (; status == CR_OK;) {
3699                 status = cr_parser_parse_ruleset (a_this);
3700                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3701         }
3703         READ_NEXT_CHAR (a_this, &cur_char);
3705         ENSURE_PARSING_COND (cur_char == '}');
3707         /*
3708          *call the right SAC handler api here.
3709          */
3710         if (PRIVATE (a_this)->sac_handler
3711             && PRIVATE (a_this)->sac_handler->end_media) {
3712                 PRIVATE (a_this)->sac_handler->end_media
3713                         (PRIVATE (a_this)->sac_handler, media_list);
3714         }
3716         cr_parser_try_to_skip_spaces_and_comments (a_this);
3718         /*
3719          *Then, free the data structures passed to
3720          *the last call to the SAC handler.
3721          */
3722         if (medium) {
3723                 cr_string_destroy (medium);
3724                 medium = NULL;
3725         }
3727         if (media_list) {
3728                 GList *cur = NULL;
3730                 for (cur = media_list; cur; cur = cur->next) {
3731                         cr_string_destroy (cur->data);
3732                 }
3734                 g_list_free (media_list);
3735                 media_list = NULL;
3736         }
3738         cr_parser_clear_errors (a_this);
3739         PRIVATE (a_this)->state = MEDIA_PARSED_STATE;
3741         return CR_OK;
3743       error:
3745         if (token) {
3746                 cr_token_destroy (token);
3747                 token = NULL;
3748         }
3750         if (medium) {
3751                 cr_string_destroy (medium);
3752                 medium = NULL;
3753         }
3755         if (media_list) {
3756                 GList *cur = NULL;
3758                 for (cur = media_list; cur; cur = cur->next) {
3759                         cr_string_destroy (cur->data);
3760                 }
3762                 g_list_free (media_list);
3763                 media_list = NULL;
3764         }
3766         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3768         return status;
3771 /**
3772  *Parses '@page' rule as specified in the css2 spec in appendix D.1:
3773  *page ::= PAGE_SYM S* IDENT? pseudo_page? S* 
3774  *'{' S* declaration [ ';' S* declaration ]* '}' S*
3775  *
3776  *This function also calls the relevant SAC handlers whenever it
3777  *encounters a construction that must 
3778  *be reported to the calling application.
3779  *@param a_this the "this pointer" of the current instance of #CRParser.
3780  *@return CR_OK upon successfull completion, an error code otherwise.
3781  */
3782 enum CRStatus
3783 cr_parser_parse_page (CRParser * a_this)
3785         enum CRStatus status = CR_OK;
3786         CRInputPos init_pos;
3787         CRToken *token = NULL;
3788         CRTerm *css_expression = NULL;
3789         CRString *page_selector = NULL,
3790                 *page_pseudo_class = NULL,
3791                 *property = NULL;
3792         gboolean important = TRUE;
3793         CRParsingLocation location = {0,0,0} ;
3795         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
3797         RECORD_INITIAL_POS (a_this, &init_pos);
3799         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
3800                                           &token) ;
3801         ENSURE_PARSING_COND (status == CR_OK
3802                              && token 
3803                              && token->type == PAGE_SYM_TK);
3805         cr_parsing_location_copy (&location, &token->location) ;
3806         cr_token_destroy (token);
3807         token = NULL;
3809         cr_parser_try_to_skip_spaces_and_comments (a_this);
3811         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3812         ENSURE_PARSING_COND (status == CR_OK && token);
3814         if (token->type == IDENT_TK) {
3815                 page_selector = token->u.str;
3816                 token->u.str = NULL;
3817                 cr_token_destroy (token);
3818                 token = NULL;
3819         } else {
3820                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3821                 token = NULL;
3822         }
3824         /* 
3825          *try to parse pseudo_page
3826          */
3827         cr_parser_try_to_skip_spaces_and_comments (a_this);
3828         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3829         ENSURE_PARSING_COND (status == CR_OK && token);
3831         if (token->type == DELIM_TK && token->u.unichar == ':') {
3832                 cr_token_destroy (token);
3833                 token = NULL;
3834                 status = cr_parser_parse_ident (a_this, &page_pseudo_class);
3835                 CHECK_PARSING_STATUS (status, FALSE);
3836         } else {
3837                 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3838                 token = NULL;
3839         }
3841         /*
3842          *parse_block
3843          *
3844          */
3845         cr_parser_try_to_skip_spaces_and_comments (a_this);
3847         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3849         ENSURE_PARSING_COND (status == CR_OK && token
3850                              && token->type == CBO_TK);
3852         cr_token_destroy (token);
3853         token = NULL;
3855         /*
3856          *Call the appropriate SAC handler here.
3857          */
3858         if (PRIVATE (a_this)->sac_handler
3859             && PRIVATE (a_this)->sac_handler->start_page) {
3860                 PRIVATE (a_this)->sac_handler->start_page
3861                         (PRIVATE (a_this)->sac_handler,
3862                          page_selector, page_pseudo_class,
3863                          &location);
3864         }
3865         cr_parser_try_to_skip_spaces_and_comments (a_this);
3867         PRIVATE (a_this)->state = TRY_PARSE_PAGE_STATE;
3869         status = cr_parser_parse_declaration (a_this, &property,
3870                                               &css_expression, 
3871                                               &important);
3872         ENSURE_PARSING_COND (status == CR_OK);
3874         /*
3875          *call the relevant SAC handler here...
3876          */
3877         if (PRIVATE (a_this)->sac_handler
3878             && PRIVATE (a_this)->sac_handler->property) {
3879                 if (css_expression)
3880                         cr_term_ref (css_expression);
3882                 PRIVATE (a_this)->sac_handler->property
3883                         (PRIVATE (a_this)->sac_handler,
3884                          property, css_expression, important);
3885         }
3886         /*
3887          *... and free the data structure passed to that last
3888          *SAC handler.
3889          */
3890         if (property) {
3891                 cr_string_destroy (property);
3892                 property = NULL;
3893         }
3894         if (css_expression) {
3895                 cr_term_unref (css_expression);
3896                 css_expression = NULL;
3897         }
3899         for (;;) {
3900                 /*parse the other ';' separated declarations */
3901                 if (token) {
3902                         cr_token_destroy (token);
3903                         token = NULL;
3904                 }
3905                 status = cr_tknzr_get_next_token
3906                         (PRIVATE (a_this)->tknzr, &token);
3908                 ENSURE_PARSING_COND (status == CR_OK && token);
3910                 if (token->type != SEMICOLON_TK) {
3911                         cr_tknzr_unget_token
3912                                 (PRIVATE (a_this)->tknzr,
3913                                  token);
3914                         token = NULL ;
3915                         break;
3916                 }
3918                 cr_token_destroy (token);
3919                 token = NULL;
3920                 cr_parser_try_to_skip_spaces_and_comments (a_this);
3922                 status = cr_parser_parse_declaration (a_this, &property,
3923                                                       &css_expression,
3924                                                       &important);
3925                 if (status != CR_OK)
3926                         break ;
3928                 /*
3929                  *call the relevant SAC handler here...
3930                  */
3931                 if (PRIVATE (a_this)->sac_handler
3932                     && PRIVATE (a_this)->sac_handler->property) {
3933                         cr_term_ref (css_expression);
3934                         PRIVATE (a_this)->sac_handler->property
3935                                 (PRIVATE (a_this)->sac_handler,
3936                                  property, css_expression, important);
3937                 }
3938                 /*
3939                  *... and free the data structure passed to that last
3940                  *SAC handler.
3941                  */
3942                 if (property) {
3943                         cr_string_destroy (property);
3944                         property = NULL;
3945                 }
3946                 if (css_expression) {
3947                         cr_term_unref (css_expression);
3948                         css_expression = NULL;
3949                 }
3950         }
3951         cr_parser_try_to_skip_spaces_and_comments 
3952                 (a_this) ;
3953         if (token) {
3954                 cr_token_destroy (token) ;
3955                 token = NULL ;
3956         }
3958         status = cr_tknzr_get_next_token
3959                         (PRIVATE (a_this)->tknzr, &token);
3960         ENSURE_PARSING_COND (status == CR_OK 
3961                              && token 
3962                              && token->type == CBC_TK) ;
3963         cr_token_destroy (token) ;
3964         token = NULL ;
3965         /*
3966          *call the relevant SAC handler here.
3967          */
3968         if (PRIVATE (a_this)->sac_handler
3969             && PRIVATE (a_this)->sac_handler->end_page) {
3970                 PRIVATE (a_this)->sac_handler->end_page
3971                         (PRIVATE (a_this)->sac_handler,
3972                          page_selector, page_pseudo_class);
3973         }
3975         if (page_selector) {
3976                 cr_string_destroy (page_selector);
3977                 page_selector = NULL;
3978         }
3980         if (page_pseudo_class) {
3981                 cr_string_destroy (page_pseudo_class);
3982                 page_pseudo_class = NULL;
3983         }
3985         cr_parser_try_to_skip_spaces_and_comments (a_this);
3987         /*here goes the former implem of this function ... */
3989         cr_parser_clear_errors (a_this);
3990         PRIVATE (a_this)->state = PAGE_PARSED_STATE;
3992         return CR_OK;
3994  error:
3995         if (token) {
3996                 cr_token_destroy (token);
3997                 token = NULL;
3998         }
3999         if (page_selector) {
4000                 cr_string_destroy (page_selector);
4001                 page_selector = NULL;
4002         }
4003         if (page_pseudo_class) {
4004                 cr_string_destroy (page_pseudo_class);
4005                 page_pseudo_class = NULL;
4006         }
4007         if (property) {
4008                 cr_string_destroy (property);
4009                 property = NULL;
4010         }
4011         if (css_expression) {
4012                 cr_term_destroy (css_expression);
4013                 css_expression = NULL;
4014         }
4015         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
4016         return status;
4019 /**
4020  *Parses a charset declaration as defined implictly by the css2 spec in
4021  *appendix D.1:
4022  *charset ::= CHARSET_SYM S* STRING S* ';'
4023  *
4024  *@param a_this the "this pointer" of the current instance of #CRParser.
4025  *@param a_value out parameter. The actual parsed value of the charset 
4026  *declararation. Note that for safety check reasons, *a_value must be
4027  *set to NULL.
4028  *@param a_charset_sym_location the parsing location of
4029  *@return CR_OK upon successfull completion, an error code otherwise.
4030  */
4031 enum CRStatus
4032 cr_parser_parse_charset (CRParser * a_this, CRString ** a_value,
4033                          CRParsingLocation *a_charset_sym_location)
4035         enum CRStatus status = CR_OK;
4036         CRInputPos init_pos;
4037         CRToken *token = NULL;
4038         CRString *charset_str = NULL;
4040         g_return_val_if_fail (a_this && a_value
4041                               && (*a_value == NULL), 
4042                               CR_BAD_PARAM_ERROR);
4044         RECORD_INITIAL_POS (a_this, &init_pos);
4046         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4048         ENSURE_PARSING_COND (status == CR_OK
4049                              && token && token->type == CHARSET_SYM_TK);
4050         if (a_charset_sym_location) {
4051                 cr_parsing_location_copy (a_charset_sym_location, 
4052                                           &token->location) ;
4053         }
4054         cr_token_destroy (token);
4055         token = NULL;
4057         PRIVATE (a_this)->state = TRY_PARSE_CHARSET_STATE;
4059         cr_parser_try_to_skip_spaces_and_comments (a_this);
4061         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4062         ENSURE_PARSING_COND (status == CR_OK
4063                              && token && token->type == STRING_TK);
4064         charset_str = token->u.str;
4065         token->u.str = NULL;
4066         cr_token_destroy (token);
4067         token = NULL;
4069         cr_parser_try_to_skip_spaces_and_comments (a_this);
4071         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4073         ENSURE_PARSING_COND (status == CR_OK
4074                              && token && token->type == SEMICOLON_TK);
4075         cr_token_destroy (token);
4076         token = NULL;
4078         if (charset_str) {
4079                 *a_value = charset_str;
4080                 charset_str = NULL;
4081         }
4083         PRIVATE (a_this)->state = CHARSET_PARSED_STATE;
4084         return CR_OK;
4086  error:
4088         if (token) {
4089                 cr_token_destroy (token);
4090                 token = NULL;
4091         }
4093         if (*a_value) {
4094                 cr_string_destroy (*a_value);
4095                 *a_value = NULL;
4096         }
4098         if (charset_str) {
4099                 cr_string_destroy (charset_str);
4100                 charset_str = NULL;
4101         }
4103         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
4105         return status;
4108 /**
4109  *Parses the "@font-face" rule specified in the css1 spec in
4110  *appendix D.1:
4111  *
4112  *font_face ::= FONT_FACE_SYM S* 
4113  *'{' S* declaration [ ';' S* declaration ]* '}' S*
4114  *
4115  *This function will call SAC handlers whenever it is necessary.
4116  *@return CR_OK upon successfull completion, an error code otherwise.
4117  */
4118 enum CRStatus
4119 cr_parser_parse_font_face (CRParser * a_this)
4121         enum CRStatus status = CR_ERROR;
4122         CRInputPos init_pos;
4123         CRString *property = NULL;
4124         CRTerm *css_expression = NULL;
4125         CRToken *token = NULL;
4126         gboolean important = FALSE;
4127         guint32 next_char = 0,
4128                 cur_char = 0;
4129         CRParsingLocation location = {0,0,0} ;
4131         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
4133         RECORD_INITIAL_POS (a_this, &init_pos);
4135         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4136         ENSURE_PARSING_COND (status == CR_OK
4137                              && token 
4138                              && token->type == FONT_FACE_SYM_TK);
4140         cr_parser_try_to_skip_spaces_and_comments (a_this);
4141         if (token) {
4142                 cr_parsing_location_copy (&location, 
4143                                           &token->location) ;
4144                 cr_token_destroy (token);
4145                 token = NULL;
4146         }
4147         status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, 
4148                                           &token);
4149         ENSURE_PARSING_COND (status == CR_OK && token
4150                              && token->type == CBO_TK);
4151         if (token) {
4152                 cr_token_destroy (token);
4153                 token = NULL;
4154         }
4155         /*
4156          *here, call the relevant SAC handler.
4157          */
4158         if (PRIVATE (a_this)->sac_handler
4159             && PRIVATE (a_this)->sac_handler->start_font_face) {
4160                 PRIVATE (a_this)->sac_handler->start_font_face
4161                         (PRIVATE (a_this)->sac_handler, &location);
4162         }
4163         PRIVATE (a_this)->state = TRY_PARSE_FONT_FACE_STATE;
4164         /*
4165          *and resume the parsing.
4166          */
4167         cr_parser_try_to_skip_spaces_and_comments (a_this);
4168         status = cr_parser_parse_declaration (a_this, &property,
4169                                               &css_expression, &important);
4170         if (status == CR_OK) {
4171                 /*
4172                  *here, call the relevant SAC handler.
4173                  */
4174                 cr_term_ref (css_expression);
4175                 if (PRIVATE (a_this)->sac_handler &&
4176                     PRIVATE (a_this)->sac_handler->property) {
4177                         PRIVATE (a_this)->sac_handler->property
4178                                 (PRIVATE (a_this)->sac_handler,
4179                                  property, css_expression, important);
4180                 }
4181                 ENSURE_PARSING_COND (css_expression && property);
4182         }
4183         /*free the data structures allocated during last parsing. */
4184         if (property) {
4185                 cr_string_destroy (property);
4186                 property = NULL;
4187         }
4188         if (css_expression) {
4189                 cr_term_unref (css_expression);
4190                 css_expression = NULL;
4191         }
4192         for (;;) {
4193                 PEEK_NEXT_CHAR (a_this, &next_char);
4194                 if (next_char == ';') {
4195                         READ_NEXT_CHAR (a_this, &cur_char);
4196                 } else {
4197                         break;
4198                 }
4199                 cr_parser_try_to_skip_spaces_and_comments (a_this);
4200                 status = cr_parser_parse_declaration (a_this, 
4201                                                       &property,
4202                                                       &css_expression,
4203                                                       &important);
4204                 if (status != CR_OK)
4205                         break;
4206                 /*
4207                  *here, call the relevant SAC handler.
4208                  */
4209                 cr_term_ref (css_expression);
4210                 if (PRIVATE (a_this)->sac_handler->property) {
4211                         PRIVATE (a_this)->sac_handler->property
4212                                 (PRIVATE (a_this)->sac_handler,
4213                                  property, css_expression, important);
4214                 }
4215                 /*
4216                  *Then, free the data structures allocated during 
4217                  *last parsing.
4218                  */
4219                 if (property) {
4220                         cr_string_destroy (property);
4221                         property = NULL;
4222                 }
4223                 if (css_expression) {
4224                         cr_term_unref (css_expression);
4225                         css_expression = NULL;
4226                 }
4227         }
4228         cr_parser_try_to_skip_spaces_and_comments (a_this);
4229         READ_NEXT_CHAR (a_this, &cur_char);
4230         ENSURE_PARSING_COND (cur_char == '}');
4231         /*
4232          *here, call the relevant SAC handler.
4233          */
4234         if (PRIVATE (a_this)->sac_handler->end_font_face) {
4235                 PRIVATE (a_this)->sac_handler->end_font_face
4236                         (PRIVATE (a_this)->sac_handler);
4237         }
4238         cr_parser_try_to_skip_spaces_and_comments (a_this);
4240         if (token) {
4241                 cr_token_destroy (token);
4242                 token = NULL;
4243         }
4244         cr_parser_clear_errors (a_this);
4245         PRIVATE (a_this)->state = FONT_FACE_PARSED_STATE;
4246         return CR_OK;
4248       error:
4249         if (token) {
4250                 cr_token_destroy (token);
4251                 token = NULL;
4252         }
4253         if (property) {
4254                 cr_string_destroy (property);
4255                 property = NULL;
4256         }
4257         if (css_expression) {
4258                 cr_term_destroy (css_expression);
4259                 css_expression = NULL;
4260         }
4261         cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
4262         return status;
4265 /**
4266  *Parses the data that comes from the
4267  *input previously associated to the current instance of
4268  *#CRParser.
4269  *@param a_this the current instance of #CRParser.
4270  *@return CR_OK ;
4271  */
4272 enum CRStatus
4273 cr_parser_parse (CRParser * a_this)
4275         enum CRStatus status = CR_ERROR;
4277         g_return_val_if_fail (a_this && PRIVATE (a_this)
4278                               && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
4280         if (PRIVATE (a_this)->use_core_grammar == FALSE) {
4281                 status = cr_parser_parse_stylesheet (a_this);
4282         } else {
4283                 status = cr_parser_parse_stylesheet_core (a_this);
4284         }
4286         return status;
4289 enum CRStatus
4290 cr_parser_set_tknzr (CRParser * a_this, CRTknzr * a_tknzr)
4292         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
4294         if (PRIVATE (a_this)->tknzr) {
4295                 cr_tknzr_unref (PRIVATE (a_this)->tknzr);
4296         }
4298         PRIVATE (a_this)->tknzr = a_tknzr;
4300         if (a_tknzr)
4301                 cr_tknzr_ref (a_tknzr);
4303         return CR_OK;
4306 /**
4307  *Getter of the parser's underlying tokenizer
4308  *@param a_this the current instance of #CRParser
4309  *@param a_tknzr out parameter. The returned tokenizer
4310  *@return CR_OK upon succesful completion, an error code
4311  *otherwise
4312  */
4313 enum CRStatus
4314 cr_parser_get_tknzr (CRParser * a_this, CRTknzr ** a_tknzr)
4316         g_return_val_if_fail (a_this && PRIVATE (a_this)
4317                               && a_tknzr, CR_BAD_PARAM_ERROR);
4319         *a_tknzr = PRIVATE (a_this)->tknzr;
4320         return CR_OK;
4323 /**
4324  *Gets the current parsing location.
4325  *@param a_this the current instance of #CRParser
4326  *@param a_loc the parsing location to get.
4327  *@return CR_OK upon succesful completion, an error code
4328  *otherwise.
4329  */
4330 enum CRStatus 
4331 cr_parser_get_parsing_location (CRParser *a_this, 
4332                                 CRParsingLocation *a_loc)
4334         g_return_val_if_fail (a_this 
4335                               && PRIVATE (a_this)
4336                               && a_loc, CR_BAD_PARAM_ERROR) ;
4338         return cr_tknzr_get_parsing_location 
4339                 (PRIVATE (a_this)->tknzr, a_loc) ;
4342 /**
4343  *Parses a stylesheet from a buffer
4344  *@param a_this the current instance of #CRparser
4345  *@param a_buf the input buffer
4346  *@param a_len the length of the input buffer
4347  *@param a_enc the encoding of the buffer
4348  *@return CR_OK upon successful completion, an error code otherwise.
4349  */
4350 enum CRStatus
4351 cr_parser_parse_buf (CRParser * a_this,
4352                      const guchar * a_buf,
4353                      gulong a_len, enum CREncoding a_enc)
4355         enum CRStatus status = CR_ERROR;
4356         CRTknzr *tknzr = NULL;
4358         g_return_val_if_fail (a_this && PRIVATE (a_this)
4359                               && a_buf, CR_BAD_PARAM_ERROR);
4361         tknzr = cr_tknzr_new_from_buf ((guchar*)a_buf, a_len, a_enc, FALSE);
4363         g_return_val_if_fail (tknzr != NULL, CR_ERROR);
4365         status = cr_parser_set_tknzr (a_this, tknzr);
4366         g_return_val_if_fail (status == CR_OK, CR_ERROR);
4368         status = cr_parser_parse (a_this);
4370         return status;
4373 /**
4374  *Destroys the current instance
4375  *of #CRParser.
4376  *@param a_this the current instance of #CRParser to
4377  *destroy.
4378  */
4379 void
4380 cr_parser_destroy (CRParser * a_this)
4382         g_return_if_fail (a_this && PRIVATE (a_this));
4384         if (PRIVATE (a_this)->tknzr) {
4385                 if (cr_tknzr_unref (PRIVATE (a_this)->tknzr) == TRUE)
4386                         PRIVATE (a_this)->tknzr = NULL;
4387         }
4389         if (PRIVATE (a_this)->sac_handler) {
4390                 cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
4391                 PRIVATE (a_this)->sac_handler = NULL;
4392         }
4394         if (PRIVATE (a_this)->err_stack) {
4395                 cr_parser_clear_errors (a_this);
4396                 PRIVATE (a_this)->err_stack = NULL;
4397         }
4399         if (PRIVATE (a_this)) {
4400                 g_free (PRIVATE (a_this));
4401                 PRIVATE (a_this) = NULL;
4402         }
4404         if (a_this) {
4405                 g_free (a_this);
4406                 a_this = NULL;  /*useless. Just for the sake of coherence */
4407         }