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