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 2.1 of the
19 * GNU Lesser 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 Lesser 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 ¶m);
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