• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gnulib-local/lib/libcroco/
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 2.1 of the
9 * GNU Lesser General Public
10 * License as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 * Author: Dodji Seketeli
23 * See COPYRIGHTS file for copyrights information.
24 */
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
75typedef struct _CRParserError CRParserError;
76
77/**
78 *An abstraction of an error reported by by the
79 *parsing routines.
80 */
81struct _CRParserError {
82        guchar *msg;
83        enum CRStatus status;
84        glong line;
85        glong column;
86        glong byte_num;
87};
88
89enum 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 */
109struct _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) \
157if ((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) \
182if ((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{\
201enum CRStatus status ; \
202status = cr_tknzr_peek_char  (PRIVATE (a_this)->tknzr, a_to_char) ; \
203CHECK_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) \
215status = cr_tknzr_read_char (PRIVATE (a_this)->tknzr, a_to_char) ; \
216CHECK_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) \
229status = cr_tknzr_get_cur_pos (PRIVATE \
230(a_this)->tknzr, a_pos) ; \
231g_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) \
241status = cr_tknzr_get_cur_byte_addr \
242            (PRIVATE (a_this)->tknzr, a_addr) ; \
243CHECK_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) \
257status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr, \
258                              a_offset, \
259                              a_byte_ptr) ; \
260CHECK_PARSING_STATUS (status, TRUE) ;
261
262#define BYTE(a_parser, a_offset, a_eof) \
263cr_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) \
273status = cr_tknzr_read_byte (PRIVATE (a_this)->tknzr, a_byte_ptr) ; \
274CHECK_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) \
285status = cr_tknzr_seek_index (PRIVATE (a_this)->tknzr, \
286                                        CR_SEEK_CUR, a_nb_bytes) ; \
287CHECK_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{ \
298glong nb_chars = a_nb_chars ; \
299status = cr_tknzr_consume_chars \
300     (PRIVATE (a_parser)->tknzr,0, &nb_chars) ; \
301CHECK_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) \
311if (! (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) \
315if (! (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) \
323status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, \
324                                  a_token_ptr) ; \
325ENSURE_PARSING_COND (status == CR_OK) ;
326
327#ifdef WITH_UNICODE_ESCAPE_AND_RANGE
328static enum CRStatus cr_parser_parse_unicode_escape (CRParser * a_this,
329                                                     guint32 * a_unicode);
330static enum CRStatus cr_parser_parse_escape (CRParser * a_this,
331                                             guint32 * a_esc_code);
332
333static enum CRStatus cr_parser_parse_unicode_range (CRParser * a_this,
334                                                    CRString ** a_inf,
335                                                    CRString ** a_sup);
336#endif
337
338static enum CRStatus cr_parser_parse_stylesheet_core (CRParser * a_this);
339
340static enum CRStatus cr_parser_parse_atrule_core (CRParser * a_this);
341
342static enum CRStatus cr_parser_parse_ruleset_core (CRParser * a_this);
343
344static enum CRStatus cr_parser_parse_selector_core (CRParser * a_this);
345
346static enum CRStatus cr_parser_parse_declaration_core (CRParser * a_this);
347
348static enum CRStatus cr_parser_parse_any_core (CRParser * a_this);
349
350static enum CRStatus cr_parser_parse_block_core (CRParser * a_this);
351
352static enum CRStatus cr_parser_parse_value_core (CRParser * a_this);
353
354static enum CRStatus cr_parser_parse_string (CRParser * a_this,
355                                             CRString ** a_str);
356
357static enum CRStatus cr_parser_parse_ident (CRParser * a_this,
358                                            CRString ** a_str);
359
360static enum CRStatus cr_parser_parse_uri (CRParser * a_this,
361                                          CRString ** a_str);
362
363static enum CRStatus cr_parser_parse_function (CRParser * a_this,
364                                               CRString ** a_func_name,
365                                               CRTerm ** a_expr);
366static enum CRStatus cr_parser_parse_property (CRParser * a_this,
367                                               CRString ** a_property);
368
369static enum CRStatus cr_parser_parse_attribute_selector (CRParser * a_this,
370                                                         CRAttrSel ** a_sel);
371
372static enum CRStatus cr_parser_parse_simple_selector (CRParser * a_this,
373                                                      CRSimpleSel ** a_sel);
374
375static enum CRStatus cr_parser_parse_simple_sels (CRParser * a_this,
376                                                  CRSimpleSel ** a_sel);
377
378static CRParserError *cr_parser_error_new (const guchar * a_msg,
379                                           enum CRStatus);
380
381static void cr_parser_error_set_msg (CRParserError * a_this,
382                                     const guchar * a_msg);
383
384static void cr_parser_error_dump (CRParserError * a_this);
385
386static void cr_parser_error_set_status (CRParserError * a_this,
387                                        enum CRStatus a_status);
388
389static void cr_parser_error_set_pos (CRParserError * a_this,
390                                     glong a_line,
391                                     glong a_column, glong a_byte_num);
392static void
393  cr_parser_error_destroy (CRParserError * a_this);
394
395static enum CRStatus cr_parser_push_error (CRParser * a_this,
396                                           const guchar * a_msg,
397                                           enum CRStatus a_status);
398
399static enum CRStatus cr_parser_dump_err_stack (CRParser * a_this,
400                                               gboolean a_clear_errs);
401static 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 */
414static CRParserError *
415cr_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 */
439static void
440cr_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 */
457static void
458cr_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 */
472static void
473cr_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
483static void
484cr_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 */
497static void
498cr_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 */
517static enum CRStatus
518cr_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 */
564static enum CRStatus
565cr_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 */
590static enum CRStatus
591cr_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 */
621enum CRStatus
622cr_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 */
677static enum CRStatus
678cr_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 */
759static enum CRStatus
760cr_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 */
834static enum CRStatus
835cr_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 */
923static enum CRStatus
924cr_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 */
962static enum CRStatus
963cr_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
1031static enum CRStatus
1032cr_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 */
1087static enum CRStatus
1088cr_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 */
1168static enum CRStatus
1169cr_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 */
1325static enum CRStatus
1326cr_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 */
1465static enum CRStatus
1466cr_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 */
1507enum CRStatus
1508cr_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 */
1662static enum CRStatus
1663cr_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 */
1929static enum CRStatus
1930cr_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 */
2004static enum CRStatus
2005cr_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 */
2127static enum CRStatus
2128cr_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 */
2213static enum CRStatus
2214cr_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 */
2244static enum CRStatus
2245cr_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 */
2272static enum CRStatus
2273cr_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 */
2304static enum CRStatus
2305cr_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 */
2751CRParser *
2752cr_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 */
2784CRParser *
2785cr_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 */
2813CRParser *
2814cr_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 */
2837CRParser *
2838cr_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 */
2863enum CRStatus
2864cr_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 */
2889enum CRStatus
2890cr_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 */
2908enum CRStatus
2909cr_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 */
2937enum CRStatus
2938cr_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 */
2955enum CRStatus
2956cr_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 */
2977enum CRStatus
2978cr_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 */
3011enum CRStatus
3012cr_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 */
3118enum CRStatus
3119cr_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 */
3169enum CRStatus
3170cr_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 */
3263enum CRStatus
3264cr_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 */
3327enum CRStatus
3328cr_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 */
3540enum CRStatus
3541cr_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 */
3701enum CRStatus
3702cr_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 */
3871enum CRStatus
3872cr_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 */
4122enum CRStatus
4123cr_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 */
4213enum CRStatus
4214cr_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 */
4370enum CRStatus
4371cr_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 */
4394enum CRStatus
4395cr_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 */
4421enum CRStatus
4422cr_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 */
4441enum CRStatus
4442cr_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 */
4464enum CRStatus
4465cr_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 */
4495void
4496cr_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}
4525