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