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