1 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
4 * This file is part of The Croco Library
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 3 of the
10 * License as published by the Free Software Foundation.
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.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * Author: Dodji Seketeli
23 * See COPYRIGHTS file for copyrights information.
29 *The definition of the #CRParser class.
34 #include "cr-parser.h"
37 #include "cr-simple-sel.h"
38 #include "cr-attr-sel.h"
42 *CSS core syntax vs CSS level 2 syntax
43 *=====================================
45 *One must keep in mind
46 *that css UA must comply with two syntaxes.
48 *1/the specific syntax that defines the css language
49 *for a given level of specificatin (e.g css2 syntax
50 *defined in appendix D.1 of the css2 spec)
52 *2/the core (general) syntax that is there to allow
53 *UAs to parse style sheets written in levels of CSS that
54 *didn't exist at the time the UAs were created.
56 *the name of parsing functions (or methods) contained in this file
57 *follows the following scheme: cr_parser_parse_<production_name> (...) ;
58 *where <production_name> is the name
59 *of a production of the css2 language.
60 *When a given production is
61 *defined by the css2 level grammar *and* by the
62 *css core syntax, there will be two functions to parse that production:
63 *one will parse the production defined by the css2 level grammar and the
64 *other will parse the production defined by the css core grammar.
65 *The css2 level grammar related parsing function will be called:
66 *cr_parser_parse_<production_name> (...) ;
67 *Then css core grammar related parsing function will be called:
68 *cr_parser_parse_<production_name>_core (...) ;
70 *If a production is defined only by the css core grammar, then
72 *cr_parser_parse_<production_name>_core (...) ;
75 typedef struct _CRParserError CRParserError;
78 *An abstraction of an error reported by by the
81 struct _CRParserError {
91 TRY_PARSE_CHARSET_STATE,
93 TRY_PARSE_IMPORT_STATE,
95 TRY_PARSE_RULESET_STATE,
97 TRY_PARSE_MEDIA_STATE,
101 TRY_PARSE_FONT_FACE_STATE,
102 FONT_FACE_PARSED_STATE
106 *The private attributes of
109 struct _CRParserPriv {
116 *The sac handlers to call
117 *to notify the parsing of
118 *the css2 constructions.
120 CRDocHandler *sac_handler;
123 *A stack of errors reported
124 *by the parsing routines.
125 *Contains instance of #CRParserError.
126 *This pointer is the top of the stack.
130 enum CRParserState state;
131 gboolean resolve_import;
132 gboolean is_case_sensitive;
133 gboolean use_core_grammar;
136 #define PRIVATE(obj) ((obj)->priv)
138 #define CHARS_TAB_SIZE 12
142 *@a_char: the char to test.
143 *return TRUE if the character is a number ([0-9]), FALSE otherwise
145 #define IS_NUM(a_char) (((a_char) >= '0' && (a_char) <= '9')?TRUE:FALSE)
148 *Checks if 'status' equals CR_OK. If not, goto the 'error' label.
150 *@param status the status (of type enum CRStatus) to test.
151 *@param is_exception if set to FALSE, the final status returned
152 *by the current function will be CR_PARSING_ERROR. If set to TRUE, the
153 *current status will be the current value of the 'status' variable.
156 #define CHECK_PARSING_STATUS(status, is_exception) \
157 if ((status) != CR_OK) \
159 if (is_exception == FALSE) \
161 status = CR_PARSING_ERROR ; \
167 * CHECK_PARSING_STATUS_ERR:
168 *@a_this: the current instance of #CRParser .
169 *@a_status: the status to check. Is of type enum #CRStatus.
170 *@a_is_exception: in case of error, if is TRUE, the status
171 *is set to CR_PARSING_ERROR before goto error. If is false, the
172 *real low level status is kept and will be returned by the
173 *upper level function that called this macro. Usally,this must
176 *same as CHECK_PARSING_STATUS() but this one pushes an error
177 *on the parser error stack when an error arises.
180 #define CHECK_PARSING_STATUS_ERR(a_this, a_status, a_is_exception,\
181 a_err_msg, a_err_status) \
182 if ((a_status) != CR_OK) \
184 if (a_is_exception == FALSE) a_status = CR_PARSING_ERROR ; \
185 cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \
190 *Peeks the next char from the input stream of the current parser
191 *by invoking cr_tknzr_input_peek_char().
192 *invokes CHECK_PARSING_STATUS on the status returned by
193 *cr_tknzr_peek_char().
195 *@param a_this the current instance of #CRParser.
196 *@param a_to_char a pointer to the char where to store the
199 #define PEEK_NEXT_CHAR(a_this, a_to_char) \
201 enum CRStatus status ; \
202 status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, a_to_char) ; \
203 CHECK_PARSING_STATUS (status, TRUE) \
207 *Reads the next char from the input stream of the current parser.
208 *In case of error, jumps to the "error:" label located in the
209 *function where this macro is called.
210 *@param a_this the curent instance of #CRParser
211 *@param to_char a pointer to the guint32 char where to store
214 #define READ_NEXT_CHAR(a_this, a_to_char) \
215 status = cr_tknzr_read_char (PRIVATE (a_this)->tknzr, a_to_char) ; \
216 CHECK_PARSING_STATUS (status, TRUE)
219 *Gets information about the current position in
220 *the input of the parser.
221 *In case of failure, this macro returns from the
222 *calling function and
223 *returns a status code of type enum #CRStatus.
224 *@param a_this the current instance of #CRParser.
225 *@param a_pos out parameter. A pointer to the position
226 *inside the current parser input. Must
228 #define RECORD_INITIAL_POS(a_this, a_pos) \
229 status = cr_tknzr_get_cur_pos (PRIVATE \
230 (a_this)->tknzr, a_pos) ; \
231 g_return_val_if_fail (status == CR_OK, status)
234 *Gets the address of the current byte inside the
236 *@param parser the current instance of #CRParser.
237 *@param addr out parameter a pointer (guchar*)
238 *to where the address must be put.
240 #define RECORD_CUR_BYTE_ADDR(a_this, a_addr) \
241 status = cr_tknzr_get_cur_byte_addr \
242 (PRIVATE (a_this)->tknzr, a_addr) ; \
243 CHECK_PARSING_STATUS (status, TRUE)
246 *Peeks a byte from the topmost parser input at
247 *a given offset from the current position.
248 *If it fails, goto the "error:" label.
250 *@param a_parser the current instance of #CRParser.
251 *@param a_offset the offset of the byte to peek, the
252 *current byte having the offset '0'.
253 *@param a_byte_ptr out parameter a pointer (guchar*) to
254 *where the peeked char is to be stored.
256 #define PEEK_BYTE(a_parser, a_offset, a_byte_ptr) \
257 status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr, \
260 CHECK_PARSING_STATUS (status, TRUE) ;
262 #define BYTE(a_parser, a_offset, a_eof) \
263 cr_tknzr_peek_byte2 (PRIVATE (a_this)->tknzr, a_offset, a_eof)
266 *Reads a byte from the topmost parser input
268 *If it fails, goto the "error" label.
269 *@param a_this the current instance of #CRParser.
270 *@param a_byte_ptr the guchar * where to put the read char.
272 #define READ_NEXT_BYTE(a_this, a_byte_ptr) \
273 status = cr_tknzr_read_byte (PRIVATE (a_this)->tknzr, a_byte_ptr) ; \
274 CHECK_PARSING_STATUS (status, TRUE) ;
277 *Skips a given number of byte in the topmost
278 *parser input. Don't update line and column number.
279 *In case of error, jumps to the "error:" label
280 *of the surrounding function.
281 *@param a_parser the current instance of #CRParser.
282 *@param a_nb_bytes the number of bytes to skip.
284 #define SKIP_BYTES(a_this, a_nb_bytes) \
285 status = cr_tknzr_seek_index (PRIVATE (a_this)->tknzr, \
286 CR_SEEK_CUR, a_nb_bytes) ; \
287 CHECK_PARSING_STATUS (status, TRUE) ;
290 *Skip utf8 encoded characters.
291 *Updates line and column numbers.
292 *@param a_parser the current instance of #CRParser.
293 *@param a_nb_chars the number of chars to skip. Must be of
296 #define SKIP_CHARS(a_parser, a_nb_chars) \
298 glong nb_chars = a_nb_chars ; \
299 status = cr_tknzr_consume_chars \
300 (PRIVATE (a_parser)->tknzr,0, &nb_chars) ; \
301 CHECK_PARSING_STATUS (status, TRUE) ; \
305 *Tests the condition and if it is false, sets
306 *status to "CR_PARSING_ERROR" and goto the 'error'
308 *@param condition the condition to test.
310 #define ENSURE_PARSING_COND(condition) \
311 if (! (condition)) {status = CR_PARSING_ERROR; goto error ;}
313 #define ENSURE_PARSING_COND_ERR(a_this, a_condition, \
314 a_err_msg, a_err_status) \
315 if (! (a_condition)) \
317 status = CR_PARSING_ERROR; \
318 cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \
322 #define GET_NEXT_TOKEN(a_this, a_token_ptr) \
323 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, \
325 ENSURE_PARSING_COND (status == CR_OK) ;
327 #ifdef WITH_UNICODE_ESCAPE_AND_RANGE
328 static enum CRStatus cr_parser_parse_unicode_escape (CRParser * a_this,
329 guint32 * a_unicode);
330 static enum CRStatus cr_parser_parse_escape (CRParser * a_this,
331 guint32 * a_esc_code);
333 static enum CRStatus cr_parser_parse_unicode_range (CRParser * a_this,
338 static enum CRStatus cr_parser_parse_stylesheet_core (CRParser * a_this);
340 static enum CRStatus cr_parser_parse_atrule_core (CRParser * a_this);
342 static enum CRStatus cr_parser_parse_ruleset_core (CRParser * a_this);
344 static enum CRStatus cr_parser_parse_selector_core (CRParser * a_this);
346 static enum CRStatus cr_parser_parse_declaration_core (CRParser * a_this);
348 static enum CRStatus cr_parser_parse_any_core (CRParser * a_this);
350 static enum CRStatus cr_parser_parse_block_core (CRParser * a_this);
352 static enum CRStatus cr_parser_parse_value_core (CRParser * a_this);
354 static enum CRStatus cr_parser_parse_string (CRParser * a_this,
357 static enum CRStatus cr_parser_parse_ident (CRParser * a_this,
360 static enum CRStatus cr_parser_parse_uri (CRParser * a_this,
363 static enum CRStatus cr_parser_parse_function (CRParser * a_this,
364 CRString ** a_func_name,
366 static enum CRStatus cr_parser_parse_property (CRParser * a_this,
367 CRString ** a_property);
369 static enum CRStatus cr_parser_parse_attribute_selector (CRParser * a_this,
372 static enum CRStatus cr_parser_parse_simple_selector (CRParser * a_this,
373 CRSimpleSel ** a_sel);
375 static enum CRStatus cr_parser_parse_simple_sels (CRParser * a_this,
376 CRSimpleSel ** a_sel);
378 static CRParserError *cr_parser_error_new (const guchar * a_msg,
381 static void cr_parser_error_set_msg (CRParserError * a_this,
382 const guchar * a_msg);
384 static void cr_parser_error_dump (CRParserError * a_this);
386 static void cr_parser_error_set_status (CRParserError * a_this,
387 enum CRStatus a_status);
389 static void cr_parser_error_set_pos (CRParserError * a_this,
391 glong a_column, glong a_byte_num);
393 cr_parser_error_destroy (CRParserError * a_this);
395 static enum CRStatus cr_parser_push_error (CRParser * a_this,
396 const guchar * a_msg,
397 enum CRStatus a_status);
399 static enum CRStatus cr_parser_dump_err_stack (CRParser * a_this,
400 gboolean a_clear_errs);
402 cr_parser_clear_errors (CRParser * a_this);
404 /*****************************
405 *error managemet methods
406 *****************************/
409 *Constructor of #CRParserError class.
410 *@param a_msg the brute error message.
411 *@param a_status the error status.
412 *@return the newly built instance of #CRParserError.
414 static CRParserError *
415 cr_parser_error_new (const guchar * a_msg, enum CRStatus a_status)
417 CRParserError *result = NULL;
419 result = g_try_malloc (sizeof (CRParserError));
421 if (result == NULL) {
422 cr_utils_trace_info ("Out of memory");
426 memset (result, 0, sizeof (CRParserError));
428 cr_parser_error_set_msg (result, a_msg);
429 cr_parser_error_set_status (result, a_status);
435 *Sets the message associated to this instance of #CRError.
436 *@param a_this the current instance of #CRParserError.
437 *@param a_msg the new message.
440 cr_parser_error_set_msg (CRParserError * a_this, const guchar * a_msg)
442 g_return_if_fail (a_this);
445 g_free (a_this->msg);
448 a_this->msg = g_strdup (a_msg);
452 *Sets the error status.
453 *@param a_this the current instance of #CRParserError.
454 *@param a_status the new error status.
458 cr_parser_error_set_status (CRParserError * a_this, enum CRStatus a_status)
460 g_return_if_fail (a_this);
462 a_this->status = a_status;
466 *Sets the position of the parser error.
467 *@param a_this the current instance of #CRParserError.
468 *@param a_line the line number.
469 *@param a_column the column number.
470 *@param a_byte_num the byte number.
473 cr_parser_error_set_pos (CRParserError * a_this,
474 glong a_line, glong a_column, glong a_byte_num)
476 g_return_if_fail (a_this);
478 a_this->line = a_line;
479 a_this->column = a_column;
480 a_this->byte_num = a_byte_num;
484 cr_parser_error_dump (CRParserError * a_this)
486 g_return_if_fail (a_this);
488 g_printerr ("parsing error: %ld:%ld:", a_this->line, a_this->column);
490 g_printerr ("%s\n", a_this->msg);
494 *The destructor of #CRParserError.
495 *@param a_this the current instance of #CRParserError.
498 cr_parser_error_destroy (CRParserError * a_this)
500 g_return_if_fail (a_this);
503 g_free (a_this->msg);
511 *Pushes an error on the parser error stack.
512 *@param a_this the current instance of #CRParser.
513 *@param a_msg the error message.
514 *@param a_status the error status.
515 *@return CR_OK upon successfull completion, an error code otherwise.
518 cr_parser_push_error (CRParser * a_this,
519 const guchar * a_msg, enum CRStatus a_status)
521 enum CRStatus status = CR_OK;
523 CRParserError *error = NULL;
526 g_return_val_if_fail (a_this && PRIVATE (a_this)
527 && a_msg, CR_BAD_PARAM_ERROR);
529 error = cr_parser_error_new (a_msg, a_status);
531 g_return_val_if_fail (error, CR_ERROR);
533 RECORD_INITIAL_POS (a_this, &pos);
535 cr_parser_error_set_pos
536 (error, pos.line, pos.col, pos.next_byte_index - 1);
538 PRIVATE (a_this)->err_stack =
539 g_list_prepend (PRIVATE (a_this)->err_stack, error);
541 if (PRIVATE (a_this)->err_stack == NULL)
549 cr_parser_error_destroy (error);
557 *Dumps the error stack on stdout.
558 *@param a_this the current instance of #CRParser.
559 *@param a_clear_errs whether to clear the error stack
560 *after the dump or not.
561 *@return CR_OK upon successfull completion, an error code
565 cr_parser_dump_err_stack (CRParser * a_this, gboolean a_clear_errs)
569 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
571 if (PRIVATE (a_this)->err_stack == NULL)
574 for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
575 cr_parser_error_dump ((CRParserError *) cur->data);
578 if (a_clear_errs == TRUE) {
579 cr_parser_clear_errors (a_this);
586 *Clears all the errors contained in the parser error stack.
587 *Frees all the errors, and the stack that contains'em.
588 *@param a_this the current instance of #CRParser.
591 cr_parser_clear_errors (CRParser * a_this)
595 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
597 for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
599 cr_parser_error_destroy ((CRParserError *)
604 if (PRIVATE (a_this)->err_stack) {
605 g_list_free (PRIVATE (a_this)->err_stack);
606 PRIVATE (a_this)->err_stack = NULL;
613 * cr_parser_try_to_skip_spaces_and_comments:
614 *@a_this: the current instance of #CRParser.
616 *Same as cr_parser_try_to_skip_spaces() but this one skips
617 *spaces and comments.
619 *Returns CR_OK upon successfull completion, an error code otherwise.
622 cr_parser_try_to_skip_spaces_and_comments (CRParser * a_this)
624 enum CRStatus status = CR_ERROR;
625 CRToken *token = NULL;
627 g_return_val_if_fail (a_this && PRIVATE (a_this)
628 && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
631 cr_token_destroy (token);
635 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
640 while ((token != NULL)
641 && (token->type == COMMENT_TK || token->type == S_TK));
643 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
650 cr_token_destroy (token);
657 /***************************************
658 *End of Parser input handling routines
659 ***************************************/
662 /*************************************
663 *Non trivial terminal productions
665 *************************************/
668 *Parses a css stylesheet following the core css grammar.
669 *This is mainly done for test purposes.
670 *During the parsing, no callback is called. This is just
671 *to validate that the stylesheet is well formed according to the
673 *stylesheet : [ CDO | CDC | S | statement ]*;
674 *@param a_this the current instance of #CRParser.
675 *@return CR_OK upon successful completion, an error code otherwise.
678 cr_parser_parse_stylesheet_core (CRParser * a_this)
680 CRToken *token = NULL;
682 enum CRStatus status = CR_ERROR;
684 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
686 RECORD_INITIAL_POS (a_this, &init_pos);
691 cr_token_destroy (token);
695 cr_parser_try_to_skip_spaces_and_comments (a_this);
696 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
697 if (status == CR_END_OF_INPUT_ERROR) {
700 } else if (status != CR_OK) {
704 switch (token->type) {
708 goto continue_parsing;
711 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
713 CHECK_PARSING_STATUS (status, TRUE);
715 status = cr_parser_parse_statement_core (a_this);
716 cr_parser_clear_errors (a_this);
717 if (status == CR_OK) {
718 goto continue_parsing;
719 } else if (status == CR_END_OF_INPUT_ERROR) {
728 cr_token_destroy (token);
732 cr_parser_clear_errors (a_this);
737 (a_this, "could not recognize next production", CR_ERROR);
739 cr_parser_dump_err_stack (a_this, TRUE);
742 cr_token_destroy (token);
746 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
752 *Parses an at-rule as defined by the css core grammar
753 *in chapter 4.1 in the css2 spec.
754 *at-rule : ATKEYWORD S* any* [ block | ';' S* ];
755 *@param a_this the current instance of #CRParser.
756 *@return CR_OK upon successfull completion, an error code
760 cr_parser_parse_atrule_core (CRParser * a_this)
762 CRToken *token = NULL;
764 enum CRStatus status = CR_ERROR;
766 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
768 RECORD_INITIAL_POS (a_this, &init_pos);
770 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
772 ENSURE_PARSING_COND (status == CR_OK
775 (token->type == ATKEYWORD_TK
776 || token->type == IMPORT_SYM_TK
777 || token->type == PAGE_SYM_TK
778 || token->type == MEDIA_SYM_TK
779 || token->type == FONT_FACE_SYM_TK
780 || token->type == CHARSET_SYM_TK));
782 cr_token_destroy (token);
785 cr_parser_try_to_skip_spaces_and_comments (a_this);
788 status = cr_parser_parse_any_core (a_this);
789 } while (status == CR_OK);
791 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
793 ENSURE_PARSING_COND (status == CR_OK && token);
795 if (token->type == CBO_TK) {
796 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
799 status = cr_parser_parse_block_core (a_this);
800 CHECK_PARSING_STATUS (status,
803 } else if (token->type == SEMICOLON_TK) {
806 status = CR_PARSING_ERROR ;
812 cr_token_destroy (token);
819 cr_token_destroy (token);
822 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr,
828 *Parses a ruleset as defined by the css core grammar in chapter
829 *4.1 of the css2 spec.
830 *ruleset ::= selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*;
831 *@param a_this the current instance of #CRParser.
832 *@return CR_OK upon successfull completion, an error code otherwise.
835 cr_parser_parse_ruleset_core (CRParser * a_this)
837 CRToken *token = NULL;
839 enum CRStatus status = CR_ERROR;
841 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
842 RECORD_INITIAL_POS (a_this, &init_pos);
844 status = cr_parser_parse_selector_core (a_this);
846 ENSURE_PARSING_COND (status == CR_OK
847 || status == CR_PARSING_ERROR
848 || status == CR_END_OF_INPUT_ERROR);
850 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
851 ENSURE_PARSING_COND (status == CR_OK && token
852 && token->type == CBO_TK);
853 cr_token_destroy (token);
856 cr_parser_try_to_skip_spaces_and_comments (a_this);
857 status = cr_parser_parse_declaration_core (a_this);
859 parse_declaration_list:
861 cr_token_destroy (token);
865 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
866 ENSURE_PARSING_COND (status == CR_OK && token);
867 if (token->type == CBC_TK) {
871 ENSURE_PARSING_COND (status == CR_OK
872 && token && token->type == SEMICOLON_TK);
874 cr_token_destroy (token);
876 cr_parser_try_to_skip_spaces_and_comments (a_this);
877 status = cr_parser_parse_declaration_core (a_this);
878 cr_parser_clear_errors (a_this);
879 ENSURE_PARSING_COND (status == CR_OK || status == CR_PARSING_ERROR);
880 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
881 ENSURE_PARSING_COND (status == CR_OK && token);
882 if (token->type == CBC_TK) {
883 cr_token_destroy (token);
885 cr_parser_try_to_skip_spaces_and_comments (a_this);
888 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
891 goto parse_declaration_list;
896 cr_token_destroy (token);
900 if (status == CR_OK) {
906 cr_token_destroy (token);
910 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
916 *Parses a "selector" as specified by the css core
919 *@param a_this the current instance of #CRParser.
920 *@return CR_OK upon successfull completion, an error code
924 cr_parser_parse_selector_core (CRParser * a_this)
926 CRToken *token = NULL;
928 enum CRStatus status = CR_ERROR;
930 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
932 RECORD_INITIAL_POS (a_this, &init_pos);
934 status = cr_parser_parse_any_core (a_this);
935 CHECK_PARSING_STATUS (status, FALSE);
938 status = cr_parser_parse_any_core (a_this);
940 } while (status == CR_OK);
946 cr_token_destroy (token);
950 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
956 *Parses a "block" as defined in the css core grammar
957 *in chapter 4.1 of the css2 spec.
958 *block ::= '{' S* [ any | block | ATKEYWORD S* | ';' ]* '}' S*;
959 *@param a_this the current instance of #CRParser.
960 *FIXME: code this function.
963 cr_parser_parse_block_core (CRParser * a_this)
965 CRToken *token = NULL;
967 enum CRStatus status = CR_ERROR;
969 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
971 RECORD_INITIAL_POS (a_this, &init_pos);
973 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
974 ENSURE_PARSING_COND (status == CR_OK && token
975 && token->type == CBO_TK);
980 cr_token_destroy (token);
984 cr_parser_try_to_skip_spaces_and_comments (a_this);
986 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
987 ENSURE_PARSING_COND (status == CR_OK && token);
989 if (token->type == CBC_TK) {
990 cr_parser_try_to_skip_spaces_and_comments (a_this);
992 } else if (token->type == SEMICOLON_TK) {
993 goto parse_block_content;
994 } else if (token->type == ATKEYWORD_TK) {
995 cr_parser_try_to_skip_spaces_and_comments (a_this);
996 goto parse_block_content;
997 } else if (token->type == CBO_TK) {
998 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
1000 status = cr_parser_parse_block_core (a_this);
1001 CHECK_PARSING_STATUS (status, FALSE);
1002 goto parse_block_content;
1004 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
1006 status = cr_parser_parse_any_core (a_this);
1007 CHECK_PARSING_STATUS (status, FALSE);
1008 goto parse_block_content;
1013 cr_token_destroy (token);
1017 if (status == CR_OK)
1022 cr_token_destroy (token);
1026 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1031 static enum CRStatus
1032 cr_parser_parse_declaration_core (CRParser * a_this)
1034 CRToken *token = NULL;
1035 CRInputPos init_pos;
1036 enum CRStatus status = CR_ERROR;
1037 CRString *prop = NULL;
1039 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1041 RECORD_INITIAL_POS (a_this, &init_pos);
1043 status = cr_parser_parse_property (a_this, &prop);
1044 CHECK_PARSING_STATUS (status, FALSE);
1045 cr_parser_clear_errors (a_this);
1046 ENSURE_PARSING_COND (status == CR_OK && prop);
1047 cr_string_destroy (prop);
1050 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1051 ENSURE_PARSING_COND (status == CR_OK
1053 && token->type == DELIM_TK
1054 && token->u.unichar == ':');
1055 cr_token_destroy (token);
1057 cr_parser_try_to_skip_spaces_and_comments (a_this);
1058 status = cr_parser_parse_value_core (a_this);
1059 CHECK_PARSING_STATUS (status, FALSE);
1066 cr_string_destroy (prop);
1071 cr_token_destroy (token);
1075 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1081 *Parses a "value" production as defined by the css core grammar
1083 *value ::= [ any | block | ATKEYWORD S* ]+;
1084 *@param a_this the current instance of #CRParser.
1085 *@return CR_OK upon successfull completion, an error code otherwise.
1087 static enum CRStatus
1088 cr_parser_parse_value_core (CRParser * a_this)
1090 CRToken *token = NULL;
1091 CRInputPos init_pos;
1092 enum CRStatus status = CR_ERROR;
1095 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1096 RECORD_INITIAL_POS (a_this, &init_pos);
1101 cr_token_destroy (token);
1105 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1106 ENSURE_PARSING_COND (status == CR_OK && token);
1108 switch (token->type) {
1110 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
1113 status = cr_parser_parse_block_core (a_this);
1114 CHECK_PARSING_STATUS (status, FALSE);
1116 goto continue_parsing;
1119 cr_parser_try_to_skip_spaces_and_comments (a_this);
1121 goto continue_parsing;
1124 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
1127 status = cr_parser_parse_any_core (a_this);
1128 if (status == CR_OK) {
1130 goto continue_parsing;
1131 } else if (status == CR_PARSING_ERROR) {
1141 cr_token_destroy (token);
1145 if (status == CR_OK && ref)
1149 cr_token_destroy (token);
1153 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1159 *Parses an "any" as defined by the css core grammar in the
1160 *css2 spec in chapter 4.1.
1161 *any ::= [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING
1162 * | DELIM | URI | HASH | UNICODE-RANGE | INCLUDES
1163 * | FUNCTION | DASHMATCH | '(' any* ')' | '[' any* ']' ] S*;
1165 *@param a_this the current instance of #CRParser.
1166 *@return CR_OK upon successfull completion, an error code otherwise.
1168 static enum CRStatus
1169 cr_parser_parse_any_core (CRParser * a_this)
1171 CRToken *token1 = NULL,
1173 CRInputPos init_pos;
1174 enum CRStatus status = CR_ERROR;
1176 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
1178 RECORD_INITIAL_POS (a_this, &init_pos);
1180 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token1);
1182 ENSURE_PARSING_COND (status == CR_OK && token1);
1184 switch (token1->type) {
1200 case UNICODERANGE_TK:
1205 case IMPORTANT_SYM_TK:
1210 *this case isn't specified by the spec but it
1211 *does happen. So we have to handle it.
1212 *We must consider function with parameters.
1213 *We consider parameter as being an "any*" production.
1216 status = cr_parser_parse_any_core (a_this);
1217 } while (status == CR_OK);
1219 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
1220 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1222 ENSURE_PARSING_COND (status == CR_OK
1223 && token2 && token2->type == PC_TK);
1226 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1228 ENSURE_PARSING_COND (status == CR_OK && token2);
1230 if (token2->type == PC_TK) {
1231 cr_token_destroy (token2);
1235 status = cr_tknzr_unget_token
1236 (PRIVATE (a_this)->tknzr, token2);
1241 status = cr_parser_parse_any_core (a_this);
1242 } while (status == CR_OK);
1244 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
1246 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1248 ENSURE_PARSING_COND (status == CR_OK
1249 && token2 && token2->type == PC_TK);
1254 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1256 ENSURE_PARSING_COND (status == CR_OK && token2);
1258 if (token2->type == BC_TK) {
1259 cr_token_destroy (token2);
1263 status = cr_tknzr_unget_token
1264 (PRIVATE (a_this)->tknzr, token2);
1269 status = cr_parser_parse_any_core (a_this);
1270 } while (status == CR_OK);
1272 ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
1274 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1276 ENSURE_PARSING_COND (status == CR_OK
1277 && token2 && token2->type == BC_TK);
1281 status = CR_PARSING_ERROR;
1287 cr_token_destroy (token1);
1292 cr_token_destroy (token2);
1301 cr_token_destroy (token1);
1306 cr_token_destroy (token2);
1310 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1315 *Parses an attribute selector as defined in the css2 spec in
1317 *attrib ::= '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
1318 * [ IDENT | STRING ] S* ]? ']'
1320 *@param a_this the "this pointer" of the current instance of
1322 *@param a_sel out parameter. The successfully parsed attribute selector.
1323 *@return CR_OK upon successfull completion, an error code otherwise.
1325 static enum CRStatus
1326 cr_parser_parse_attribute_selector (CRParser * a_this,
1329 enum CRStatus status = CR_OK;
1330 CRInputPos init_pos;
1331 CRToken *token = NULL;
1332 CRAttrSel *result = NULL;
1333 CRParsingLocation location = {0} ;
1335 g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
1337 RECORD_INITIAL_POS (a_this, &init_pos);
1339 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1340 ENSURE_PARSING_COND (status == CR_OK && token
1341 && token->type == BO_TK);
1342 cr_parsing_location_copy
1343 (&location, &token->location) ;
1344 cr_token_destroy (token);
1347 cr_parser_try_to_skip_spaces_and_comments (a_this);
1349 result = cr_attr_sel_new ();
1351 cr_utils_trace_info ("result failed") ;
1352 status = CR_OUT_OF_MEMORY_ERROR ;
1355 cr_parsing_location_copy (&result->location,
1357 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1358 ENSURE_PARSING_COND (status == CR_OK
1359 && token && token->type == IDENT_TK);
1361 result->name = token->u.str;
1362 token->u.str = NULL;
1363 cr_token_destroy (token);
1366 cr_parser_try_to_skip_spaces_and_comments (a_this);
1368 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1369 ENSURE_PARSING_COND (status == CR_OK && token);
1371 if (token->type == INCLUDES_TK) {
1372 result->match_way = INCLUDES;
1373 goto parse_right_part;
1374 } else if (token->type == DASHMATCH_TK) {
1375 result->match_way = DASHMATCH;
1376 goto parse_right_part;
1377 } else if (token->type == DELIM_TK && token->u.unichar == '=') {
1378 result->match_way = EQUALS;
1379 goto parse_right_part;
1380 } else if (token->type == BC_TK) {
1381 result->match_way = SET;
1388 cr_token_destroy (token);
1392 cr_parser_try_to_skip_spaces_and_comments (a_this);
1394 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1395 ENSURE_PARSING_COND (status == CR_OK && token);
1397 if (token->type == IDENT_TK) {
1398 result->value = token->u.str;
1399 token->u.str = NULL;
1400 } else if (token->type == STRING_TK) {
1401 result->value = token->u.str;
1402 token->u.str = NULL;
1404 status = CR_PARSING_ERROR;
1409 cr_token_destroy (token);
1413 cr_parser_try_to_skip_spaces_and_comments (a_this);
1415 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1417 ENSURE_PARSING_COND (status == CR_OK && token
1418 && token->type == BC_TK);
1421 cr_token_destroy (token);
1426 status = cr_attr_sel_append_attr_sel (*a_sel, result);
1427 CHECK_PARSING_STATUS (status, FALSE);
1432 cr_parser_clear_errors (a_this);
1438 cr_attr_sel_destroy (result);
1443 cr_token_destroy (token);
1447 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1453 *Parses a "property" as specified by the css2 spec at [4.1.1]:
1454 *property : IDENT S*;
1456 *@param a_this the "this pointer" of the current instance of #CRParser.
1457 *@param GString a_property out parameter. The parsed property without the
1458 *trailing spaces. If *a_property is NULL, this function allocates a
1459 *new instance of GString and set it content to the parsed property.
1460 *If not, the property is just appended to a_property's previous content.
1461 *In both cases, it is up to the caller to free a_property.
1462 *@return CR_OK upon successfull completion, CR_PARSING_ERROR if the
1463 *next construction was not a "property", or an error code.
1465 static enum CRStatus
1466 cr_parser_parse_property (CRParser * a_this,
1467 CRString ** a_property)
1469 enum CRStatus status = CR_OK;
1470 CRInputPos init_pos;
1472 g_return_val_if_fail (a_this && PRIVATE (a_this)
1473 && PRIVATE (a_this)->tknzr
1475 CR_BAD_PARAM_ERROR);
1477 RECORD_INITIAL_POS (a_this, &init_pos);
1479 status = cr_parser_parse_ident (a_this, a_property);
1480 CHECK_PARSING_STATUS (status, TRUE);
1482 cr_parser_try_to_skip_spaces_and_comments (a_this);
1484 cr_parser_clear_errors (a_this);
1489 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1495 * cr_parser_parse_term:
1496 *@a_term: out parameter. The successfully parsed term.
1498 *Parses a "term" as defined in the css2 spec, appendix D.1:
1499 *term ::= unary_operator? [NUMBER S* | PERCENTAGE S* | LENGTH S* |
1500 *EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* | function ] |
1501 *STRING S* | IDENT S* | URI S* | RGB S* | UNICODERANGE S* | hexcolor
1503 *TODO: handle parsing of 'RGB'
1505 *Returns CR_OK upon successfull completion, an error code otherwise.
1508 cr_parser_parse_term (CRParser * a_this, CRTerm ** a_term)
1510 enum CRStatus status = CR_PARSING_ERROR;
1511 CRInputPos init_pos;
1512 CRTerm *result = NULL;
1513 CRTerm *param = NULL;
1514 CRToken *token = NULL;
1515 CRString *func_name = NULL;
1516 CRParsingLocation location = {0} ;
1518 g_return_val_if_fail (a_this && a_term, CR_BAD_PARAM_ERROR);
1520 RECORD_INITIAL_POS (a_this, &init_pos);
1522 result = cr_term_new ();
1524 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1526 if (status != CR_OK || !token)
1529 cr_parsing_location_copy (&location, &token->location) ;
1530 if (token->type == DELIM_TK && token->u.unichar == '+') {
1531 result->unary_op = PLUS_UOP;
1532 cr_token_destroy (token) ;
1534 cr_parser_try_to_skip_spaces_and_comments (a_this);
1535 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1537 if (status != CR_OK || !token)
1539 } else if (token->type == DELIM_TK && token->u.unichar == '-') {
1540 result->unary_op = MINUS_UOP;
1541 cr_token_destroy (token) ;
1543 cr_parser_try_to_skip_spaces_and_comments (a_this);
1544 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
1546 if (status != CR_OK || !token)
1550 if (token->type == EMS_TK
1551 || token->type == EXS_TK
1552 || token->type == LENGTH_TK
1553 || token->type == ANGLE_TK
1554 || token->type == TIME_TK
1555 || token->type == FREQ_TK
1556 || token->type == PERCENTAGE_TK
1557 || token->type == NUMBER_TK) {
1558 status = cr_term_set_number (result, token->u.num);
1559 CHECK_PARSING_STATUS (status, TRUE);
1560 token->u.num = NULL;
1562 } else if (token && token->type == FUNCTION_TK) {
1563 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
1566 status = cr_parser_parse_function (a_this, &func_name,
1569 if (status == CR_OK) {
1570 status = cr_term_set_function (result,
1573 CHECK_PARSING_STATUS (status, TRUE);
1575 } else if (token && token->type == STRING_TK) {
1576 status = cr_term_set_string (result,
1578 CHECK_PARSING_STATUS (status, TRUE);
1579 token->u.str = NULL;
1580 } else if (token && token->type == IDENT_TK) {
1581 status = cr_term_set_ident (result, token->u.str);
1582 CHECK_PARSING_STATUS (status, TRUE);
1583 token->u.str = NULL;
1584 } else if (token && token->type == URI_TK) {
1585 status = cr_term_set_uri (result, token->u.str);
1586 CHECK_PARSING_STATUS (status, TRUE);
1587 token->u.str = NULL;
1588 } else if (token && token->type == RGB_TK) {
1589 status = cr_term_set_rgb (result, token->u.rgb);
1590 CHECK_PARSING_STATUS (status, TRUE);
1591 token->u.rgb = NULL;
1592 } else if (token && token->type == UNICODERANGE_TK) {
1593 result->type = TERM_UNICODERANGE;
1594 status = CR_PARSING_ERROR;
1595 } else if (token && token->type == HASH_TK) {
1596 status = cr_term_set_hash (result, token->u.str);
1597 CHECK_PARSING_STATUS (status, TRUE);
1598 token->u.str = NULL;
1600 status = CR_PARSING_ERROR;
1603 if (status != CR_OK) {
1606 cr_parsing_location_copy (&result->location,
1608 *a_term = cr_term_append_term (*a_term, result);
1612 cr_parser_try_to_skip_spaces_and_comments (a_this);
1615 cr_token_destroy (token);
1619 cr_parser_clear_errors (a_this);
1625 cr_term_destroy (result);
1630 cr_token_destroy (token);
1635 cr_term_destroy (param);
1640 cr_string_destroy (func_name);
1644 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1650 * cr_parser_parse_simple_selector:
1651 *@a_this: the "this pointer" of the current instance of #CRParser.
1652 *@a_sel: out parameter. Is set to the successfully parsed simple
1655 *Parses a "simple_selector" as defined by the css2 spec in appendix D.1 :
1656 *element_name? [ HASH | class | attrib | pseudo ]* S*
1657 *and where pseudo is:
1658 *pseudo ::= ':' [ IDENT | FUNCTION S* IDENT S* ')' ]
1660 *Returns CR_OK upon successfull completion, an error code otherwise.
1662 static enum CRStatus
1663 cr_parser_parse_simple_selector (CRParser * a_this, CRSimpleSel ** a_sel)
1665 enum CRStatus status = CR_ERROR;
1666 CRInputPos init_pos;
1667 CRToken *token = NULL;
1668 CRSimpleSel *sel = NULL;
1669 CRAdditionalSel *add_sel_list = NULL;
1670 gboolean found_sel = FALSE;
1671 guint32 cur_char = 0;
1673 g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
1675 RECORD_INITIAL_POS (a_this, &init_pos);
1677 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
1678 if (status != CR_OK)
1681 sel = cr_simple_sel_new ();
1682 ENSURE_PARSING_COND (sel);
1684 cr_parsing_location_copy
1688 if (token && token->type == DELIM_TK
1689 && token->u.unichar == '*') {
1690 sel->type_mask |= UNIVERSAL_SELECTOR;
1691 sel->name = cr_string_new_from_string ("*");
1693 } else if (token && token->type == IDENT_TK) {
1694 sel->name = token->u.str;
1695 sel->type_mask |= TYPE_SELECTOR;
1696 token->u.str = NULL;
1699 status = cr_tknzr_unget_token
1700 (PRIVATE (a_this)->tknzr,
1706 cr_token_destroy (token);
1710 cr_parser_try_to_skip_spaces_and_comments (a_this);
1714 cr_token_destroy (token);
1718 status = cr_tknzr_get_next_token
1719 (PRIVATE (a_this)->tknzr,
1721 if (status != CR_OK)
1724 if (token && token->type == HASH_TK) {
1725 /*we parsed an attribute id */
1726 CRAdditionalSel *add_sel = NULL;
1728 add_sel = cr_additional_sel_new_with_type
1731 add_sel->content.id_name = token->u.str;
1732 token->u.str = NULL;
1734 cr_parsing_location_copy
1735 (&add_sel->location,
1738 cr_additional_sel_append
1739 (add_sel_list, add_sel);
1741 } else if (token && (token->type == DELIM_TK)
1742 && (token->u.unichar == '.')) {
1743 cr_token_destroy (token);
1746 status = cr_tknzr_get_next_token
1747 (PRIVATE (a_this)->tknzr, &token);
1748 if (status != CR_OK)
1751 if (token && token->type == IDENT_TK) {
1752 CRAdditionalSel *add_sel = NULL;
1754 add_sel = cr_additional_sel_new_with_type
1755 (CLASS_ADD_SELECTOR);
1757 add_sel->content.class_name = token->u.str;
1758 token->u.str = NULL;
1761 cr_additional_sel_append
1762 (add_sel_list, add_sel);
1765 cr_parsing_location_copy
1766 (&add_sel->location,
1767 & token->location) ;
1769 status = CR_PARSING_ERROR;
1772 } else if (token && token->type == BO_TK) {
1773 CRAttrSel *attr_sel = NULL;
1774 CRAdditionalSel *add_sel = NULL;
1776 status = cr_tknzr_unget_token
1777 (PRIVATE (a_this)->tknzr, token);
1778 if (status != CR_OK)
1782 status = cr_parser_parse_attribute_selector
1783 (a_this, &attr_sel);
1784 CHECK_PARSING_STATUS (status, FALSE);
1786 add_sel = cr_additional_sel_new_with_type
1787 (ATTRIBUTE_ADD_SELECTOR);
1789 ENSURE_PARSING_COND (add_sel != NULL);
1791 add_sel->content.attr_sel = attr_sel;
1794 cr_additional_sel_append
1795 (add_sel_list, add_sel);
1797 cr_parsing_location_copy
1798 (&add_sel->location,
1799 &attr_sel->location) ;
1800 } else if (token && (token->type == DELIM_TK)
1801 && (token->u.unichar == ':')) {
1802 CRPseudo *pseudo = NULL;
1804 /*try to parse a pseudo */
1807 cr_token_destroy (token);
1811 pseudo = cr_pseudo_new ();
1813 status = cr_tknzr_get_next_token
1814 (PRIVATE (a_this)->tknzr, &token);
1815 ENSURE_PARSING_COND (status == CR_OK && token);
1817 cr_parsing_location_copy
1821 if (token->type == IDENT_TK) {
1822 pseudo->type = IDENT_PSEUDO;
1823 pseudo->name = token->u.str;
1824 token->u.str = NULL;
1826 } else if (token->type == FUNCTION_TK) {
1827 pseudo->name = token->u.str;
1828 token->u.str = NULL;
1829 cr_parser_try_to_skip_spaces_and_comments
1831 status = cr_parser_parse_ident
1832 (a_this, &pseudo->extra);
1834 ENSURE_PARSING_COND (status == CR_OK);
1835 READ_NEXT_CHAR (a_this, &cur_char);
1836 ENSURE_PARSING_COND (cur_char == ')');
1837 pseudo->type = FUNCTION_PSEUDO;
1840 status = CR_PARSING_ERROR;
1844 if (status == CR_OK) {
1845 CRAdditionalSel *add_sel = NULL;
1847 add_sel = cr_additional_sel_new_with_type
1848 (PSEUDO_CLASS_ADD_SELECTOR);
1850 add_sel->content.pseudo = pseudo;
1851 cr_parsing_location_copy
1852 (&add_sel->location,
1853 &pseudo->location) ;
1855 cr_additional_sel_append
1856 (add_sel_list, add_sel);
1860 status = cr_tknzr_unget_token
1861 (PRIVATE (a_this)->tknzr, token);
1867 if (status == CR_OK && found_sel == TRUE) {
1868 cr_parser_try_to_skip_spaces_and_comments (a_this);
1870 sel->add_sel = add_sel_list;
1871 add_sel_list = NULL;
1873 if (*a_sel == NULL) {
1876 cr_simple_sel_append_simple_sel (*a_sel, sel);
1882 cr_token_destroy (token);
1886 cr_parser_clear_errors (a_this);
1889 status = CR_PARSING_ERROR;
1895 cr_token_destroy (token);
1900 cr_additional_sel_destroy (add_sel_list);
1901 add_sel_list = NULL;
1905 cr_simple_sel_destroy (sel);
1909 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1916 * cr_parser_parse_simple_sels:
1917 *@a_this: the this pointer of the current instance of #CRParser.
1918 *@a_start: a pointer to the
1919 *first chararcter of the successfully parsed
1921 *@a_end: a pointer to the last character of the successfully parsed
1924 *Parses a "selector" as defined by the css2 spec in appendix D.1:
1925 *selector ::= simple_selector [ combinator simple_selector ]*
1927 *Returns CR_OK upon successfull completion, an error code otherwise.
1929 static enum CRStatus
1930 cr_parser_parse_simple_sels (CRParser * a_this,
1931 CRSimpleSel ** a_sel)
1933 enum CRStatus status = CR_ERROR;
1934 CRInputPos init_pos;
1935 CRSimpleSel *sel = NULL;
1936 guint32 cur_char = 0;
1938 g_return_val_if_fail (a_this
1941 CR_BAD_PARAM_ERROR);
1943 RECORD_INITIAL_POS (a_this, &init_pos);
1945 status = cr_parser_parse_simple_selector (a_this, &sel);
1946 CHECK_PARSING_STATUS (status, FALSE);
1948 *a_sel = cr_simple_sel_append_simple_sel (*a_sel, sel);
1951 guint32 next_char = 0;
1952 enum Combinator comb = 0;
1956 PEEK_NEXT_CHAR (a_this, &next_char);
1958 if (next_char == '+') {
1959 READ_NEXT_CHAR (a_this, &cur_char);
1961 cr_parser_try_to_skip_spaces_and_comments (a_this);
1962 } else if (next_char == '>') {
1963 READ_NEXT_CHAR (a_this, &cur_char);
1965 cr_parser_try_to_skip_spaces_and_comments (a_this);
1970 status = cr_parser_parse_simple_selector (a_this, &sel);
1971 if (status != CR_OK)
1975 sel->combinator = comb;
1979 *a_sel = cr_simple_sel_append_simple_sel (*a_sel,
1983 cr_parser_clear_errors (a_this);
1988 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
1994 * cr_parser_parse_selector:
1995 *@a_this: the current instance of #CRParser.
1996 *@a_selector: the parsed list of comma separated
1999 *Parses a comma separated list of selectors.
2001 *Returns CR_OK upon successful completion, an error
2004 static enum CRStatus
2005 cr_parser_parse_selector (CRParser * a_this,
2006 CRSelector ** a_selector)
2008 enum CRStatus status = CR_OK;
2009 CRInputPos init_pos;
2010 guint32 cur_char = 0,
2012 CRSimpleSel *simple_sels = NULL;
2013 CRSelector *selector = NULL;
2015 g_return_val_if_fail (a_this && a_selector, CR_BAD_PARAM_ERROR);
2017 RECORD_INITIAL_POS (a_this, &init_pos);
2019 status = cr_parser_parse_simple_sels (a_this, &simple_sels);
2020 CHECK_PARSING_STATUS (status, FALSE);
2023 selector = cr_selector_append_simple_sel
2024 (selector, simple_sels);
2026 cr_parsing_location_copy
2027 (&selector->location,
2028 &simple_sels->location) ;
2032 status = CR_PARSING_ERROR ;
2036 status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
2038 if (status != CR_OK) {
2039 if (status == CR_END_OF_INPUT_ERROR) {
2047 if (next_char == ',') {
2051 status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
2053 if (status != CR_OK) {
2054 if (status == CR_END_OF_INPUT_ERROR) {
2062 if (next_char != ',')
2065 /*consume the ',' char */
2066 READ_NEXT_CHAR (a_this, &cur_char);
2068 cr_parser_try_to_skip_spaces_and_comments (a_this);
2070 status = cr_parser_parse_simple_sels
2071 (a_this, &simple_sels);
2073 CHECK_PARSING_STATUS (status, FALSE);
2077 cr_selector_append_simple_sel
2078 (selector, simple_sels);
2086 cr_parser_try_to_skip_spaces_and_comments (a_this);
2089 *a_selector = selector;
2091 *a_selector = cr_selector_append (*a_selector, selector);
2100 cr_simple_sel_destroy (simple_sels);
2105 cr_selector_unref (selector);
2109 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
2115 * cr_parser_parse_function:
2116 *@a_this: the "this pointer" of the current instance of #CRParser.
2118 *@a_func_name: out parameter. The parsed function name
2119 *@a_expr: out parameter. The successfully parsed term.
2121 *Parses a "function" as defined in css spec at appendix D.1:
2122 *function ::= FUNCTION S* expr ')' S*
2123 *FUNCTION ::= ident'('
2125 *Returns CR_OK upon successfull completion, an error code otherwise.
2127 static enum CRStatus
2128 cr_parser_parse_function (CRParser * a_this,
2129 CRString ** a_func_name,
2132 CRInputPos init_pos;
2133 enum CRStatus status = CR_OK;
2134 CRToken *token = NULL;
2135 CRTerm *expr = NULL;
2137 g_return_val_if_fail (a_this && PRIVATE (a_this)
2139 CR_BAD_PARAM_ERROR);
2141 RECORD_INITIAL_POS (a_this, &init_pos);
2143 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
2144 if (status != CR_OK)
2147 if (token && token->type == FUNCTION_TK) {
2148 *a_func_name = token->u.str;
2149 token->u.str = NULL;
2151 status = CR_PARSING_ERROR;
2154 cr_token_destroy (token);
2157 cr_parser_try_to_skip_spaces_and_comments (a_this) ;
2159 status = cr_parser_parse_expr (a_this, &expr);
2161 CHECK_PARSING_STATUS (status, FALSE);
2163 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
2164 if (status != CR_OK)
2167 ENSURE_PARSING_COND (token && token->type == PC_TK);
2169 cr_token_destroy (token);
2173 *a_expr = cr_term_append_term (*a_expr, expr);
2177 cr_parser_clear_errors (a_this);
2183 cr_string_destroy (*a_func_name);
2184 *a_func_name = NULL;
2188 cr_term_destroy (expr);
2193 cr_token_destroy (token);
2197 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
2203 * cr_parser_parse_uri:
2204 *@a_this: the current instance of #CRParser.
2205 *@a_str: the successfully parsed url.
2207 *Parses an uri as defined by the css spec [4.1.1]:
2208 * URI ::= url\({w}{string}{w}\)
2209 * |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)
2211 *Returns CR_OK upon successfull completion, an error code otherwise.
2213 static enum CRStatus
2214 cr_parser_parse_uri (CRParser * a_this, CRString ** a_str)
2217 enum CRStatus status = CR_PARSING_ERROR;
2219 g_return_val_if_fail (a_this && PRIVATE (a_this)
2220 && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
2222 status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
2223 URI_TK, NO_ET, a_str, NULL);
2228 * cr_parser_parse_string:
2229 *@a_this: the current instance of #CRParser.
2230 *@a_start: out parameter. Upon successfull completion,
2231 *points to the beginning of the string, points to an undefined value
2233 *@a_end: out parameter. Upon successfull completion, points to
2234 *the beginning of the string, points to an undefined value otherwise.
2236 *Parses a string type as defined in css spec [4.1.1]:
2238 *string ::= {string1}|{string2}
2239 *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
2240 *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
2242 *Returns CR_OK upon successfull completion, an error code otherwise.
2244 static enum CRStatus
2245 cr_parser_parse_string (CRParser * a_this, CRString ** a_str)
2247 enum CRStatus status = CR_OK;
2249 g_return_val_if_fail (a_this && PRIVATE (a_this)
2250 && PRIVATE (a_this)->tknzr
2251 && a_str, CR_BAD_PARAM_ERROR);
2253 status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
2254 STRING_TK, NO_ET, a_str, NULL);
2259 *Parses an "ident" as defined in css spec [4.1.1]:
2260 *ident ::= {nmstart}{nmchar}*
2262 *@param a_this the currens instance of #CRParser.
2264 *@param a_str a pointer to parsed ident. If *a_str is NULL,
2265 *this function allocates a new instance of #CRString. If not,
2266 *the function just appends the parsed string to the one passed.
2267 *In both cases it is up to the caller to free *a_str.
2269 *@return CR_OK upon successfull completion, an error code
2272 static enum CRStatus
2273 cr_parser_parse_ident (CRParser * a_this, CRString ** a_str)
2275 enum CRStatus status = CR_OK;
2277 g_return_val_if_fail (a_this && PRIVATE (a_this)
2278 && PRIVATE (a_this)->tknzr
2279 && a_str, CR_BAD_PARAM_ERROR);
2281 status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
2282 IDENT_TK, NO_ET, a_str, NULL);
2287 *the next rule is ignored as well. This seems to be a bug
2288 *Parses a stylesheet as defined in the css2 spec in appendix D.1:
2289 *stylesheet ::= [ CHARSET_SYM S* STRING S* ';' ]?
2290 * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
2291 * [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]*
2293 *TODO: Finish the code of this function. Think about splitting it into
2296 *@param a_this the "this pointer" of the current instance of #CRParser.
2297 *@param a_start out parameter. A pointer to the first character of
2298 *the successfully parsed string.
2299 *@param a_end out parameter. A pointer to the first character of
2300 *the successfully parsed string.
2302 *@return CR_OK upon successfull completion, an error code otherwise.
2304 static enum CRStatus
2305 cr_parser_parse_stylesheet (CRParser * a_this)
2307 enum CRStatus status = CR_OK;
2308 CRInputPos init_pos;
2309 CRToken *token = NULL;
2310 CRString *charset = NULL;
2312 g_return_val_if_fail (a_this && PRIVATE (a_this)
2313 && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
2315 RECORD_INITIAL_POS (a_this, &init_pos);
2317 PRIVATE (a_this)->state = READY_STATE;
2319 if (PRIVATE (a_this)->sac_handler
2320 && PRIVATE (a_this)->sac_handler->start_document) {
2321 PRIVATE (a_this)->sac_handler->start_document
2322 (PRIVATE (a_this)->sac_handler);
2326 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
2328 if (status == CR_END_OF_INPUT_ERROR)
2330 CHECK_PARSING_STATUS (status, TRUE);
2332 if (token && token->type == CHARSET_SYM_TK) {
2333 CRParsingLocation location = {0} ;
2334 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2336 CHECK_PARSING_STATUS (status, TRUE);
2339 status = cr_parser_parse_charset (a_this,
2343 if (status == CR_OK && charset) {
2344 if (PRIVATE (a_this)->sac_handler
2345 && PRIVATE (a_this)->sac_handler->charset) {
2346 PRIVATE (a_this)->sac_handler->charset
2347 (PRIVATE (a_this)->sac_handler,
2348 charset, &location);
2350 } else if (status != CR_END_OF_INPUT_ERROR) {
2351 status = cr_parser_parse_atrule_core (a_this);
2352 CHECK_PARSING_STATUS (status, FALSE);
2356 cr_string_destroy (charset);
2360 && (token->type == S_TK
2361 || token->type == COMMENT_TK)) {
2362 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2365 CHECK_PARSING_STATUS (status, TRUE);
2367 cr_parser_try_to_skip_spaces_and_comments (a_this);
2368 goto parse_charset ;
2370 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2373 CHECK_PARSING_STATUS (status, TRUE);
2379 cr_token_destroy (token);
2382 cr_parser_try_to_skip_spaces_and_comments (a_this) ;
2383 status = cr_tknzr_get_next_token
2384 (PRIVATE (a_this)->tknzr, &token);
2386 if (status == CR_END_OF_INPUT_ERROR)
2388 CHECK_PARSING_STATUS (status, TRUE);
2390 && (token->type == S_TK
2391 || token->type == CDO_TK || token->type == CDC_TK));
2394 status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
2400 status = cr_tknzr_get_next_token
2401 (PRIVATE (a_this)->tknzr, &token);
2402 if (status == CR_END_OF_INPUT_ERROR)
2404 CHECK_PARSING_STATUS (status, TRUE);
2406 if (token && token->type == IMPORT_SYM_TK) {
2407 GList *media_list = NULL;
2408 CRString *import_string = NULL;
2409 CRParsingLocation location = {0} ;
2411 status = cr_tknzr_unget_token
2412 (PRIVATE (a_this)->tknzr, token);
2414 CHECK_PARSING_STATUS (status, TRUE);
2416 status = cr_parser_parse_import (a_this,
2420 if (status == CR_OK) {
2422 && PRIVATE (a_this)->sac_handler
2423 && PRIVATE (a_this)->sac_handler->import_style) {
2424 PRIVATE (a_this)->sac_handler->import_style
2425 (PRIVATE(a_this)->sac_handler,
2430 if ((PRIVATE (a_this)->sac_handler->resolve_import == TRUE)) {
2437 if ((PRIVATE (a_this)->sac_handler->import_style_result)) {
2438 PRIVATE (a_this)->sac_handler->import_style_result
2439 (PRIVATE (a_this)->sac_handler,
2440 media_list, import_string,
2444 } else if (status != CR_END_OF_INPUT_ERROR) {
2445 if (PRIVATE (a_this)->sac_handler
2446 && PRIVATE (a_this)->sac_handler->error) {
2447 PRIVATE (a_this)->sac_handler->error
2448 (PRIVATE (a_this)->sac_handler);
2450 status = cr_parser_parse_atrule_core (a_this);
2451 CHECK_PARSING_STATUS (status, TRUE) ;
2457 *then, after calling the appropriate
2459 *the media_list and import_string.
2464 /*free the medium list */
2465 for (cur = media_list; cur; cur = cur->next) {
2467 cr_string_destroy (cur->data);
2471 g_list_free (media_list);
2475 if (import_string) {
2476 cr_string_destroy (import_string);
2477 import_string = NULL;
2480 cr_parser_try_to_skip_spaces_and_comments (a_this);
2482 && (token->type == S_TK
2483 || token->type == CDO_TK
2484 || token->type == CDC_TK)) {
2485 status = cr_tknzr_unget_token
2486 (PRIVATE (a_this)->tknzr, token);
2491 cr_token_destroy (token);
2495 status = cr_tknzr_get_next_token
2496 (PRIVATE (a_this)->tknzr, &token);
2498 if (status == CR_END_OF_INPUT_ERROR)
2500 CHECK_PARSING_STATUS (status, TRUE);
2502 && (token->type == S_TK
2503 || token->type == CDO_TK
2504 || token->type == CDC_TK));
2507 status = cr_tknzr_unget_token
2508 (PRIVATE (a_this)->tknzr, token);
2511 goto parse_ruleset_and_others;
2515 parse_ruleset_and_others:
2517 cr_parser_try_to_skip_spaces_and_comments (a_this);
2520 status = cr_tknzr_get_next_token
2521 (PRIVATE (a_this)->tknzr, &token);
2522 if (status == CR_END_OF_INPUT_ERROR)
2524 CHECK_PARSING_STATUS (status, TRUE);
2527 && (token->type == S_TK
2528 || token->type == CDO_TK || token->type == CDC_TK)) {
2529 status = cr_tknzr_unget_token
2530 (PRIVATE (a_this)->tknzr, token);
2535 cr_token_destroy (token);
2539 cr_parser_try_to_skip_spaces_and_comments
2541 status = cr_tknzr_get_next_token
2542 (PRIVATE (a_this)->tknzr, &token);
2544 && (token->type == S_TK
2545 || token->type == COMMENT_TK
2546 || token->type == CDO_TK
2547 || token->type == CDC_TK));
2549 cr_tknzr_unget_token
2550 (PRIVATE (a_this)->tknzr, token);
2554 && (token->type == HASH_TK
2555 || (token->type == DELIM_TK
2556 && token->u.unichar == '.')
2557 || (token->type == DELIM_TK
2558 && token->u.unichar == ':')
2559 || (token->type == DELIM_TK
2560 && token->u.unichar == '*')
2561 || (token->type == BO_TK)
2562 || token->type == IDENT_TK)) {
2564 *Try to parse a CSS2 ruleset.
2565 *if the parsing fails, try to parse
2566 *a css core ruleset.
2568 status = cr_tknzr_unget_token
2569 (PRIVATE (a_this)->tknzr, token);
2570 CHECK_PARSING_STATUS (status, TRUE);
2573 status = cr_parser_parse_ruleset (a_this);
2575 if (status == CR_OK) {
2578 if (PRIVATE (a_this)->sac_handler
2579 && PRIVATE (a_this)->sac_handler->error) {
2580 PRIVATE (a_this)->sac_handler->
2586 status = cr_parser_parse_ruleset_core
2589 if (status == CR_OK) {
2595 } else if (token && token->type == MEDIA_SYM_TK) {
2596 status = cr_tknzr_unget_token
2597 (PRIVATE (a_this)->tknzr, token);
2598 CHECK_PARSING_STATUS (status, TRUE);
2601 status = cr_parser_parse_media (a_this);
2602 if (status == CR_OK) {
2605 if (PRIVATE (a_this)->sac_handler
2606 && PRIVATE (a_this)->sac_handler->error) {
2607 PRIVATE (a_this)->sac_handler->
2613 status = cr_parser_parse_atrule_core (a_this);
2615 if (status == CR_OK) {
2622 } else if (token && token->type == PAGE_SYM_TK) {
2623 status = cr_tknzr_unget_token
2624 (PRIVATE (a_this)->tknzr, token);
2625 CHECK_PARSING_STATUS (status, TRUE);
2627 status = cr_parser_parse_page (a_this);
2629 if (status == CR_OK) {
2632 if (PRIVATE (a_this)->sac_handler
2633 && PRIVATE (a_this)->sac_handler->error) {
2634 PRIVATE (a_this)->sac_handler->
2640 status = cr_parser_parse_atrule_core (a_this);
2642 if (status == CR_OK) {
2648 } else if (token && token->type == FONT_FACE_SYM_TK) {
2649 status = cr_tknzr_unget_token
2650 (PRIVATE (a_this)->tknzr, token);
2651 CHECK_PARSING_STATUS (status, TRUE);
2653 status = cr_parser_parse_font_face (a_this);
2655 if (status == CR_OK) {
2658 if (PRIVATE (a_this)->sac_handler
2659 && PRIVATE (a_this)->sac_handler->error) {
2660 PRIVATE (a_this)->sac_handler->
2666 status = cr_parser_parse_atrule_core (a_this);
2668 if (status == CR_OK) {
2675 status = cr_tknzr_unget_token
2676 (PRIVATE (a_this)->tknzr, token);
2677 CHECK_PARSING_STATUS (status, TRUE);
2679 status = cr_parser_parse_statement_core (a_this);
2681 if (status == CR_OK) {
2691 cr_token_destroy (token);
2695 if (status == CR_END_OF_INPUT_ERROR || status == CR_OK) {
2697 if (PRIVATE (a_this)->sac_handler
2698 && PRIVATE (a_this)->sac_handler->end_document) {
2699 PRIVATE (a_this)->sac_handler->end_document
2700 (PRIVATE (a_this)->sac_handler);
2706 cr_parser_push_error
2707 (a_this, "could not recognize next production", CR_ERROR);
2709 if (PRIVATE (a_this)->sac_handler
2710 && PRIVATE (a_this)->sac_handler->unrecoverable_error) {
2711 PRIVATE (a_this)->sac_handler->
2712 unrecoverable_error (PRIVATE (a_this)->sac_handler);
2715 cr_parser_dump_err_stack (a_this, TRUE);
2722 cr_token_destroy (token);
2726 if (PRIVATE (a_this)->sac_handler
2727 && PRIVATE (a_this)->sac_handler->unrecoverable_error) {
2728 PRIVATE (a_this)->sac_handler->
2729 unrecoverable_error (PRIVATE (a_this)->sac_handler);
2732 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
2737 /****************************************
2738 *Public CRParser Methods
2739 ****************************************/
2743 * @a_tknzr: the tokenizer to use for the parsing.
2745 *Creates a new parser to parse data
2746 *coming the input stream given in parameter.
2748 *Returns the newly created instance of #CRParser,
2749 *or NULL if an error occured.
2752 cr_parser_new (CRTknzr * a_tknzr)
2754 CRParser *result = NULL;
2755 enum CRStatus status = CR_OK;
2757 result = g_malloc0 (sizeof (CRParser));
2759 PRIVATE (result) = g_malloc0 (sizeof (CRParserPriv));
2762 status = cr_parser_set_tknzr (result, a_tknzr);
2765 g_return_val_if_fail (status == CR_OK, NULL);
2771 * cr_parser_new_from_buf:
2772 *@a_buf: the buffer to parse.
2773 *@a_len: the length of the data in the buffer.
2774 *@a_enc: the encoding of the input buffer a_buf.
2775 *@a_free_buf: if set to TRUE, a_buf will be freed
2776 *during the destruction of the newly built instance
2777 *of #CRParser. If set to FALSE, it is up to the caller to
2778 *eventually free it.
2780 *Instanciates a new parser from a memory buffer.
2782 *Returns the newly built parser, or NULL if an error arises.
2785 cr_parser_new_from_buf (guchar * a_buf,
2787 enum CREncoding a_enc,
2788 gboolean a_free_buf)
2790 CRParser *result = NULL;
2791 CRInput *input = NULL;
2793 g_return_val_if_fail (a_buf && a_len, NULL);
2795 input = cr_input_new_from_buf (a_buf, a_len, a_enc, a_free_buf);
2796 g_return_val_if_fail (input, NULL);
2798 result = cr_parser_new_from_input (input);
2800 cr_input_destroy (input);
2808 * cr_parser_new_from_input:
2809 * @a_input: the parser input stream to use.
2811 * Returns a newly built parser input.
2814 cr_parser_new_from_input (CRInput * a_input)
2816 CRParser *result = NULL;
2817 CRTknzr *tokenizer = NULL;
2820 tokenizer = cr_tknzr_new (a_input);
2821 g_return_val_if_fail (tokenizer, NULL);
2824 result = cr_parser_new (tokenizer);
2825 g_return_val_if_fail (result, NULL);
2831 * cr_parser_new_from_file:
2832 * @a_file_uri: the uri of the file to parse.
2833 * @a_enc: the file encoding to use.
2835 * Returns the newly built parser.
2838 cr_parser_new_from_file (const guchar * a_file_uri, enum CREncoding a_enc)
2840 CRParser *result = NULL;
2841 CRTknzr *tokenizer = NULL;
2843 tokenizer = cr_tknzr_new_from_uri (a_file_uri, a_enc);
2845 cr_utils_trace_info ("Could not open input file");
2849 result = cr_parser_new (tokenizer);
2850 g_return_val_if_fail (result, NULL);
2855 * cr_parser_set_sac_handler:
2856 *@a_this: the "this pointer" of the current instance of #CRParser.
2857 *@a_handler: the handler to set.
2859 *Sets a SAC document handler to the parser.
2861 *Returns CR_OK upon successfull completion, an error code otherwise.
2864 cr_parser_set_sac_handler (CRParser * a_this, CRDocHandler * a_handler)
2866 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
2868 if (PRIVATE (a_this)->sac_handler) {
2869 cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
2872 PRIVATE (a_this)->sac_handler = a_handler;
2873 cr_doc_handler_ref (a_handler);
2879 * cr_parser_get_sac_handler:
2880 *@a_this: the "this pointer" of the current instance of
2882 *@a_handler: out parameter. The returned handler.
2884 *Gets the SAC document handler.
2886 *Returns CR_OK upon successfull completion, an error code
2890 cr_parser_get_sac_handler (CRParser * a_this, CRDocHandler ** a_handler)
2892 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
2894 *a_handler = PRIVATE (a_this)->sac_handler;
2900 * cr_parser_set_default_sac_handler:
2901 *@a_this: a pointer to the current instance of #CRParser.
2903 *Sets the SAC handler associated to the current instance
2904 *of #CRParser to the default SAC handler.
2906 *Returns CR_OK upon successfull completion, an error code otherwise.
2909 cr_parser_set_default_sac_handler (CRParser * a_this)
2911 CRDocHandler *default_sac_handler = NULL;
2912 enum CRStatus status = CR_ERROR;
2914 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
2916 default_sac_handler = cr_doc_handler_new ();
2918 cr_doc_handler_set_default_sac_handler (default_sac_handler);
2920 status = cr_parser_set_sac_handler (a_this, default_sac_handler);
2922 if (status != CR_OK) {
2923 cr_doc_handler_destroy (default_sac_handler);
2924 default_sac_handler = NULL;
2931 * cr_parser_set_use_core_grammar:
2932 * @a_this: the current instance of #CRParser.
2933 * @a_use_core_grammar: where to parse against the css core grammar.
2935 * Returns CR_OK upon succesful completion, an error code otherwise.
2938 cr_parser_set_use_core_grammar (CRParser * a_this,
2939 gboolean a_use_core_grammar)
2941 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
2943 PRIVATE (a_this)->use_core_grammar = a_use_core_grammar;
2949 * cr_parser_get_use_core_grammar:
2950 * @a_this: the current instance of #CRParser.
2951 * @a_use_core_grammar: wether to use the core grammar or not.
2953 * Returns CR_OK upon succesful completion, an error code otherwise.
2956 cr_parser_get_use_core_grammar (CRParser * a_this,
2957 gboolean * a_use_core_grammar)
2959 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
2961 *a_use_core_grammar = PRIVATE (a_this)->use_core_grammar;
2967 * cr_parser_parse_file:
2968 *@a_this: a pointer to the current instance of #CRParser.
2969 *@a_file_uri: the uri to the file to load. For the time being,
2970 *@a_enc: the encoding of the file to parse.
2971 *only local files are supported.
2973 *Parses a the given in parameter.
2975 *Returns CR_OK upon successfull completion, an error code otherwise.
2978 cr_parser_parse_file (CRParser * a_this,
2979 const guchar * a_file_uri, enum CREncoding a_enc)
2981 enum CRStatus status = CR_ERROR;
2982 CRTknzr *tknzr = NULL;
2984 g_return_val_if_fail (a_this && PRIVATE (a_this)
2985 && a_file_uri, CR_BAD_PARAM_ERROR);
2987 tknzr = cr_tknzr_new_from_uri (a_file_uri, a_enc);
2989 g_return_val_if_fail (tknzr != NULL, CR_ERROR);
2991 status = cr_parser_set_tknzr (a_this, tknzr);
2992 g_return_val_if_fail (status == CR_OK, CR_ERROR);
2994 status = cr_parser_parse (a_this);
3000 * cr_parser_parse_expr:
3001 * @a_this: the current instance of #CRParser.
3002 * @a_expr: out parameter. the parsed expression.
3004 *Parses an expression as defined by the css2 spec in appendix
3006 *expr: term [ operator term ]*
3009 * Returns CR_OK upon successful completion, an error code otherwise.
3012 cr_parser_parse_expr (CRParser * a_this, CRTerm ** a_expr)
3014 enum CRStatus status = CR_ERROR;
3015 CRInputPos init_pos;
3016 CRTerm *expr = NULL,
3018 guchar next_byte = 0;
3019 gulong nb_terms = 0;
3021 g_return_val_if_fail (a_this && PRIVATE (a_this)
3022 && a_expr, CR_BAD_PARAM_ERROR);
3024 RECORD_INITIAL_POS (a_this, &init_pos);
3026 status = cr_parser_parse_term (a_this, &expr);
3028 CHECK_PARSING_STATUS (status, FALSE);
3031 guchar operator = 0;
3033 status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr,
3035 if (status != CR_OK) {
3036 if (status == CR_END_OF_INPUT_ERROR) {
3050 if (next_byte == '/' || next_byte == ',') {
3051 READ_NEXT_BYTE (a_this, &operator);
3054 cr_parser_try_to_skip_spaces_and_comments (a_this);
3056 status = cr_parser_parse_term (a_this, &expr2);
3058 if (status != CR_OK || expr2 == NULL) {
3065 expr2->the_operator = DIVIDE;
3068 expr2->the_operator = COMMA;
3074 expr = cr_term_append_term (expr, expr2);
3080 if (status == CR_OK) {
3081 *a_expr = cr_term_append_term (*a_expr, expr);
3084 cr_parser_clear_errors (a_this);
3091 cr_term_destroy (expr);
3096 cr_term_destroy (expr2);
3100 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3106 * cr_parser_parse_prio:
3107 *@a_this: the current instance of #CRParser.
3108 *@a_prio: a string representing the priority.
3109 *Today, only "!important" is returned as only this
3110 *priority is defined by css2.
3112 *Parses a declaration priority as defined by
3113 *the css2 grammar in appendix C:
3114 *prio: IMPORTANT_SYM S*
3116 * Returns CR_OK upon successful completion, an error code otherwise.
3119 cr_parser_parse_prio (CRParser * a_this, CRString ** a_prio)
3121 enum CRStatus status = CR_ERROR;
3122 CRInputPos init_pos;
3123 CRToken *token = NULL;
3125 g_return_val_if_fail (a_this && PRIVATE (a_this)
3127 && *a_prio == NULL, CR_BAD_PARAM_ERROR);
3129 RECORD_INITIAL_POS (a_this, &init_pos);
3131 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3132 if (status == CR_END_OF_INPUT_ERROR) {
3135 ENSURE_PARSING_COND (status == CR_OK
3136 && token && token->type == IMPORTANT_SYM_TK);
3138 cr_parser_try_to_skip_spaces_and_comments (a_this);
3139 *a_prio = cr_string_new_from_string ("!important");
3140 cr_token_destroy (token);
3146 cr_token_destroy (token);
3149 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3155 * cr_parser_parse_declaration:
3156 *@a_this: the "this pointer" of the current instance of #CRParser.
3157 *@a_property: the successfully parsed property. The caller
3158 * *must* free the returned pointer.
3159 *@a_expr: the expression that represents the attribute value.
3160 *The caller *must* free the returned pointer.
3162 *TODO: return the parsed priority, so that
3163 *upper layers can take benefit from it.
3164 *Parses a "declaration" as defined by the css2 spec in appendix D.1:
3165 *declaration ::= [property ':' S* expr prio?]?
3167 *Returns CR_OK upon successfull completion, an error code otherwise.
3170 cr_parser_parse_declaration (CRParser * a_this,
3171 CRString ** a_property,
3172 CRTerm ** a_expr, gboolean * a_important)
3174 enum CRStatus status = CR_ERROR;
3175 CRInputPos init_pos;
3176 guint32 cur_char = 0;
3177 CRTerm *expr = NULL;
3178 CRString *prio = NULL;
3180 g_return_val_if_fail (a_this && PRIVATE (a_this)
3181 && a_property && a_expr
3182 && a_important, CR_BAD_PARAM_ERROR);
3184 RECORD_INITIAL_POS (a_this, &init_pos);
3186 status = cr_parser_parse_property (a_this, a_property);
3188 if (status == CR_END_OF_INPUT_ERROR)
3191 CHECK_PARSING_STATUS_ERR
3192 (a_this, status, FALSE,
3193 "while parsing declaration: next property is malformed",
3196 READ_NEXT_CHAR (a_this, &cur_char);
3198 if (cur_char != ':') {
3199 status = CR_PARSING_ERROR;
3200 cr_parser_push_error
3202 "while parsing declaration: this char must be ':'",
3207 cr_parser_try_to_skip_spaces_and_comments (a_this);
3209 status = cr_parser_parse_expr (a_this, &expr);
3211 CHECK_PARSING_STATUS_ERR
3212 (a_this, status, FALSE,
3213 "while parsing declaration: next expression is malformed",
3216 cr_parser_try_to_skip_spaces_and_comments (a_this);
3217 status = cr_parser_parse_prio (a_this, &prio);
3219 cr_string_destroy (prio);
3221 *a_important = TRUE;
3223 *a_important = FALSE;
3226 cr_term_append_term (*a_expr, expr);
3233 cr_parser_clear_errors (a_this);
3239 cr_term_destroy (expr);
3244 cr_string_destroy (*a_property);
3248 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3254 * cr_parser_parse_statement_core:
3255 *@a_this: the current instance of #CRParser.
3257 *Parses a statement as defined by the css core grammar in
3258 *chapter 4.1 of the css2 spec.
3259 *statement : ruleset | at-rule;
3261 *Returns CR_OK upon successfull completion, an error code otherwise.
3264 cr_parser_parse_statement_core (CRParser * a_this)
3266 CRToken *token = NULL;
3267 CRInputPos init_pos;
3268 enum CRStatus status = CR_ERROR;
3270 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
3272 RECORD_INITIAL_POS (a_this, &init_pos);
3274 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3276 ENSURE_PARSING_COND (status == CR_OK && token);
3278 switch (token->type) {
3283 case FONT_FACE_SYM_TK:
3284 case CHARSET_SYM_TK:
3285 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3287 status = cr_parser_parse_atrule_core (a_this);
3288 CHECK_PARSING_STATUS (status, TRUE);
3292 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3294 status = cr_parser_parse_ruleset_core (a_this);
3295 cr_parser_clear_errors (a_this);
3296 CHECK_PARSING_STATUS (status, TRUE);
3303 cr_token_destroy (token);
3307 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3313 * cr_parser_parse_ruleset:
3314 *@a_this: the "this pointer" of the current instance of #CRParser.
3316 *Parses a "ruleset" as defined in the css2 spec at appendix D.1.
3317 *ruleset ::= selector [ ',' S* selector ]*
3318 *'{' S* declaration? [ ';' S* declaration? ]* '}' S*;
3320 *This methods calls the the SAC handler on the relevant SAC handler
3321 *callbacks whenever it encounters some specific constructions.
3322 *See the documentation of #CRDocHandler (the SAC handler) to know
3323 *when which SAC handler is called.
3325 *Returns CR_OK upon successfull completion, an error code otherwise.
3328 cr_parser_parse_ruleset (CRParser * a_this)
3330 enum CRStatus status = CR_OK;
3331 CRInputPos init_pos;
3332 guint32 cur_char = 0,
3334 CRString *property = NULL;
3335 CRTerm *expr = NULL;
3336 CRSimpleSel *simple_sels = NULL;
3337 CRSelector *selector = NULL;
3338 gboolean start_selector = FALSE,
3339 is_important = FALSE;
3341 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
3343 RECORD_INITIAL_POS (a_this, &init_pos);
3345 status = cr_parser_parse_selector (a_this, &selector);
3346 CHECK_PARSING_STATUS (status, FALSE);
3348 READ_NEXT_CHAR (a_this, &cur_char);
3350 ENSURE_PARSING_COND_ERR
3351 (a_this, cur_char == '{',
3352 "while parsing rulset: current char should be '{'",
3355 if (PRIVATE (a_this)->sac_handler
3356 && PRIVATE (a_this)->sac_handler->start_selector) {
3358 *the selector is ref counted so that the parser's user
3359 *can choose to keep it.
3362 cr_selector_ref (selector);
3365 PRIVATE (a_this)->sac_handler->start_selector
3366 (PRIVATE (a_this)->sac_handler, selector);
3367 start_selector = TRUE;
3370 cr_parser_try_to_skip_spaces_and_comments (a_this);
3372 PRIVATE (a_this)->state = TRY_PARSE_RULESET_STATE;
3374 status = cr_parser_parse_declaration (a_this, &property,
3381 && PRIVATE (a_this)->sac_handler
3382 && PRIVATE (a_this)->sac_handler->property) {
3383 PRIVATE (a_this)->sac_handler->property
3384 (PRIVATE (a_this)->sac_handler, property, expr,
3387 if (status == CR_OK) {
3390 *'property' and 'term' before parsing
3394 cr_string_destroy (property);
3398 cr_term_unref (expr);
3401 } else {/*status != CR_OK*/
3404 *test if we have reached '}', which
3405 *would mean that we are parsing an empty ruleset (eg. x{ })
3406 *In that case, goto end_of_ruleset.
3408 status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, &c) ;
3409 if (status == CR_OK && c == '}') {
3411 goto end_of_ruleset ;
3414 CHECK_PARSING_STATUS_ERR
3415 (a_this, status, FALSE,
3416 "while parsing ruleset: next construction should be a declaration",
3420 PEEK_NEXT_CHAR (a_this, &next_char);
3421 if (next_char != ';')
3424 /*consume the ';' char */
3425 READ_NEXT_CHAR (a_this, &cur_char);
3427 cr_parser_try_to_skip_spaces_and_comments (a_this);
3429 status = cr_parser_parse_declaration (a_this, &property,
3430 &expr, &is_important);
3436 && PRIVATE (a_this)->sac_handler
3437 && PRIVATE (a_this)->sac_handler->property) {
3438 PRIVATE (a_this)->sac_handler->property
3439 (PRIVATE (a_this)->sac_handler,
3440 property, expr, is_important);
3443 cr_string_destroy (property);
3447 cr_term_unref (expr);
3453 cr_parser_try_to_skip_spaces_and_comments (a_this);
3454 READ_NEXT_CHAR (a_this, &cur_char);
3455 ENSURE_PARSING_COND_ERR
3456 (a_this, cur_char == '}',
3457 "while parsing rulset: current char must be a '}'",
3460 if (PRIVATE (a_this)->sac_handler
3461 && PRIVATE (a_this)->sac_handler->end_selector) {
3462 PRIVATE (a_this)->sac_handler->end_selector
3463 (PRIVATE (a_this)->sac_handler, selector);
3464 start_selector = FALSE;
3468 cr_term_unref (expr);
3473 cr_simple_sel_destroy (simple_sels);
3478 cr_selector_unref (selector);
3482 cr_parser_clear_errors (a_this);
3483 PRIVATE (a_this)->state = RULESET_PARSED_STATE;
3488 if (start_selector == TRUE
3489 && PRIVATE (a_this)->sac_handler
3490 && PRIVATE (a_this)->sac_handler->error) {
3491 PRIVATE (a_this)->sac_handler->error
3492 (PRIVATE (a_this)->sac_handler);
3495 cr_term_unref (expr);
3499 cr_simple_sel_destroy (simple_sels);
3503 cr_string_destroy (property);
3506 cr_selector_unref (selector);
3510 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3516 * cr_parser_parse_import:
3517 *@a_this: the "this pointer" of the current instance
3519 *@a_media_list: out parameter. A linked list of
3521 *Each CRString is a string that contains
3522 *a 'medium' declaration part of the successfully
3523 *parsed 'import' declaration.
3524 *@a_import_string: out parameter.
3525 *A string that contains the 'import
3526 *string". The import string can be either an uri (if it starts with
3527 *the substring "uri(") or a any other css2 string. Note that
3528 * *a_import_string must be initially set to NULL or else, this function
3529 *will return CR_BAD_PARAM_ERROR.
3530 *@a_location: the location (line, column) where the import has been parsed
3532 *Parses an 'import' declaration as defined in the css2 spec
3536 *@import [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S*
3538 *Returns CR_OK upon sucessfull completion, an error code otherwise.
3541 cr_parser_parse_import (CRParser * a_this,
3542 GList ** a_media_list,
3543 CRString ** a_import_string,
3544 CRParsingLocation *a_location)
3546 enum CRStatus status = CR_OK;
3547 CRInputPos init_pos;
3548 guint32 cur_char = 0,
3550 CRString *medium = NULL;
3552 g_return_val_if_fail (a_this
3554 && (*a_import_string == NULL),
3555 CR_BAD_PARAM_ERROR);
3557 RECORD_INITIAL_POS (a_this, &init_pos);
3559 if (BYTE (a_this, 1, NULL) == '@'
3560 && BYTE (a_this, 2, NULL) == 'i'
3561 && BYTE (a_this, 3, NULL) == 'm'
3562 && BYTE (a_this, 4, NULL) == 'p'
3563 && BYTE (a_this, 5, NULL) == 'o'
3564 && BYTE (a_this, 6, NULL) == 'r'
3565 && BYTE (a_this, 7, NULL) == 't') {
3566 SKIP_CHARS (a_this, 1);
3568 cr_parser_get_parsing_location
3569 (a_this, a_location) ;
3571 SKIP_CHARS (a_this, 6);
3574 status = CR_PARSING_ERROR;
3578 cr_parser_try_to_skip_spaces_and_comments (a_this);
3580 PRIVATE (a_this)->state = TRY_PARSE_IMPORT_STATE;
3582 PEEK_NEXT_CHAR (a_this, &next_char);
3584 if (next_char == '"' || next_char == '\'') {
3585 status = cr_parser_parse_string (a_this, a_import_string);
3587 CHECK_PARSING_STATUS (status, FALSE);
3589 status = cr_parser_parse_uri (a_this, a_import_string);
3591 CHECK_PARSING_STATUS (status, FALSE);
3594 cr_parser_try_to_skip_spaces_and_comments (a_this);
3596 status = cr_parser_parse_ident (a_this, &medium);
3598 if (status == CR_OK && medium) {
3599 *a_media_list = g_list_append (*a_media_list, medium);
3603 cr_parser_try_to_skip_spaces_and_comments (a_this);
3605 for (; status == CR_OK;) {
3606 if ((status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
3607 &next_char)) != CR_OK) {
3608 if (status == CR_END_OF_INPUT_ERROR) {
3615 if (next_char == ',') {
3616 READ_NEXT_CHAR (a_this, &cur_char);
3621 cr_parser_try_to_skip_spaces_and_comments (a_this);
3623 status = cr_parser_parse_ident (a_this, &medium);
3625 cr_parser_try_to_skip_spaces_and_comments (a_this);
3627 if ((status == CR_OK) && medium) {
3628 *a_media_list = g_list_append (*a_media_list, medium);
3633 CHECK_PARSING_STATUS (status, FALSE);
3634 cr_parser_try_to_skip_spaces_and_comments (a_this);
3636 cr_parser_try_to_skip_spaces_and_comments (a_this);
3637 READ_NEXT_CHAR (a_this, &cur_char);
3638 ENSURE_PARSING_COND (cur_char == ';');
3639 cr_parser_try_to_skip_spaces_and_comments (a_this);
3641 cr_parser_clear_errors (a_this);
3642 PRIVATE (a_this)->state = IMPORT_PARSED_STATE;
3648 if (*a_media_list) {
3652 *free each element of *a_media_list.
3653 *Note that each element of *a_medium list *must*
3654 *be a GString* or else, the code that is coming next
3655 *will corrupt the memory and lead to hard to debug
3657 *This is where C++ and its compile time
3658 *type checking mecanism (through STL containers) would
3659 *have prevented us to go through this hassle.
3661 for (cur = *a_media_list; cur; cur = cur->next) {
3663 cr_string_destroy (cur->data);
3667 g_list_free (*a_media_list);
3668 *a_media_list = NULL;
3671 if (*a_import_string) {
3672 cr_string_destroy (*a_import_string);
3673 *a_import_string = NULL;
3677 cr_string_destroy (medium);
3681 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3687 * cr_parser_parse_media:
3688 *@a_this: the "this pointer" of the current instance of #CRParser.
3690 *Parses a 'media' declaration as specified in the css2 spec at
3693 *media ::= @media S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S*
3695 *Note that this function calls the required sac handlers during the parsing
3696 *to notify media productions. See #CRDocHandler to know the callback called
3697 *during @media parsing.
3699 *Returns CR_OK upon successfull completion, an error code otherwise.
3702 cr_parser_parse_media (CRParser * a_this)
3704 enum CRStatus status = CR_OK;
3705 CRInputPos init_pos;
3706 CRToken *token = NULL;
3707 guint32 next_char = 0,
3709 CRString *medium = NULL;
3710 GList *media_list = NULL;
3711 CRParsingLocation location = {0} ;
3713 g_return_val_if_fail (a_this
3714 && PRIVATE (a_this),
3715 CR_BAD_PARAM_ERROR);
3717 RECORD_INITIAL_POS (a_this, &init_pos);
3719 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
3721 ENSURE_PARSING_COND (status == CR_OK
3723 && token->type == MEDIA_SYM_TK);
3724 cr_parsing_location_copy (&location, &token->location) ;
3725 cr_token_destroy (token);
3728 cr_parser_try_to_skip_spaces_and_comments (a_this);
3730 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3731 ENSURE_PARSING_COND (status == CR_OK
3732 && token && token->type == IDENT_TK);
3734 medium = token->u.str;
3735 token->u.str = NULL;
3736 cr_token_destroy (token);
3740 media_list = g_list_append (media_list, medium);
3744 for (; status == CR_OK;) {
3745 cr_parser_try_to_skip_spaces_and_comments (a_this);
3746 PEEK_NEXT_CHAR (a_this, &next_char);
3748 if (next_char == ',') {
3749 READ_NEXT_CHAR (a_this, &cur_char);
3754 cr_parser_try_to_skip_spaces_and_comments (a_this);
3756 status = cr_parser_parse_ident (a_this, &medium);
3758 CHECK_PARSING_STATUS (status, FALSE);
3761 media_list = g_list_append (media_list, medium);
3766 READ_NEXT_CHAR (a_this, &cur_char);
3768 ENSURE_PARSING_COND (cur_char == '{');
3771 *call the SAC handler api here.
3773 if (PRIVATE (a_this)->sac_handler
3774 && PRIVATE (a_this)->sac_handler->start_media) {
3775 PRIVATE (a_this)->sac_handler->start_media
3776 (PRIVATE (a_this)->sac_handler, media_list,
3780 cr_parser_try_to_skip_spaces_and_comments (a_this);
3782 PRIVATE (a_this)->state = TRY_PARSE_MEDIA_STATE;
3784 for (; status == CR_OK;) {
3785 status = cr_parser_parse_ruleset (a_this);
3786 cr_parser_try_to_skip_spaces_and_comments (a_this);
3789 READ_NEXT_CHAR (a_this, &cur_char);
3791 ENSURE_PARSING_COND (cur_char == '}');
3794 *call the right SAC handler api here.
3796 if (PRIVATE (a_this)->sac_handler
3797 && PRIVATE (a_this)->sac_handler->end_media) {
3798 PRIVATE (a_this)->sac_handler->end_media
3799 (PRIVATE (a_this)->sac_handler, media_list);
3802 cr_parser_try_to_skip_spaces_and_comments (a_this);
3805 *Then, free the data structures passed to
3806 *the last call to the SAC handler.
3809 cr_string_destroy (medium);
3816 for (cur = media_list; cur; cur = cur->next) {
3817 cr_string_destroy (cur->data);
3820 g_list_free (media_list);
3824 cr_parser_clear_errors (a_this);
3825 PRIVATE (a_this)->state = MEDIA_PARSED_STATE;
3832 cr_token_destroy (token);
3837 cr_string_destroy (medium);
3844 for (cur = media_list; cur; cur = cur->next) {
3845 cr_string_destroy (cur->data);
3848 g_list_free (media_list);
3852 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
3858 * cr_parser_parse_page:
3859 *@a_this: the "this pointer" of the current instance of #CRParser.
3861 *Parses '@page' rule as specified in the css2 spec in appendix D.1:
3862 *page ::= PAGE_SYM S* IDENT? pseudo_page? S*
3863 *'{' S* declaration [ ';' S* declaration ]* '}' S*
3865 *This function also calls the relevant SAC handlers whenever it
3866 *encounters a construction that must
3867 *be reported to the calling application.
3869 *Returns CR_OK upon successfull completion, an error code otherwise.
3872 cr_parser_parse_page (CRParser * a_this)
3874 enum CRStatus status = CR_OK;
3875 CRInputPos init_pos;
3876 CRToken *token = NULL;
3877 CRTerm *css_expression = NULL;
3878 CRString *page_selector = NULL,
3879 *page_pseudo_class = NULL,
3881 gboolean important = TRUE;
3882 CRParsingLocation location = {0} ;
3884 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
3886 RECORD_INITIAL_POS (a_this, &init_pos);
3888 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
3890 ENSURE_PARSING_COND (status == CR_OK
3892 && token->type == PAGE_SYM_TK);
3894 cr_parsing_location_copy (&location, &token->location) ;
3895 cr_token_destroy (token);
3898 cr_parser_try_to_skip_spaces_and_comments (a_this);
3900 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3901 ENSURE_PARSING_COND (status == CR_OK && token);
3903 if (token->type == IDENT_TK) {
3904 page_selector = token->u.str;
3905 token->u.str = NULL;
3906 cr_token_destroy (token);
3909 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3914 *try to parse pseudo_page
3916 cr_parser_try_to_skip_spaces_and_comments (a_this);
3917 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3918 ENSURE_PARSING_COND (status == CR_OK && token);
3920 if (token->type == DELIM_TK && token->u.unichar == ':') {
3921 cr_token_destroy (token);
3923 status = cr_parser_parse_ident (a_this, &page_pseudo_class);
3924 CHECK_PARSING_STATUS (status, FALSE);
3926 cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
3934 cr_parser_try_to_skip_spaces_and_comments (a_this);
3936 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
3938 ENSURE_PARSING_COND (status == CR_OK && token
3939 && token->type == CBO_TK);
3941 cr_token_destroy (token);
3945 *Call the appropriate SAC handler here.
3947 if (PRIVATE (a_this)->sac_handler
3948 && PRIVATE (a_this)->sac_handler->start_page) {
3949 PRIVATE (a_this)->sac_handler->start_page
3950 (PRIVATE (a_this)->sac_handler,
3951 page_selector, page_pseudo_class,
3954 cr_parser_try_to_skip_spaces_and_comments (a_this);
3956 PRIVATE (a_this)->state = TRY_PARSE_PAGE_STATE;
3958 status = cr_parser_parse_declaration (a_this, &property,
3961 ENSURE_PARSING_COND (status == CR_OK);
3964 *call the relevant SAC handler here...
3966 if (PRIVATE (a_this)->sac_handler
3967 && PRIVATE (a_this)->sac_handler->property) {
3969 cr_term_ref (css_expression);
3971 PRIVATE (a_this)->sac_handler->property
3972 (PRIVATE (a_this)->sac_handler,
3973 property, css_expression, important);
3976 *... and free the data structure passed to that last
3980 cr_string_destroy (property);
3983 if (css_expression) {
3984 cr_term_unref (css_expression);
3985 css_expression = NULL;
3989 /*parse the other ';' separated declarations */
3991 cr_token_destroy (token);
3994 status = cr_tknzr_get_next_token
3995 (PRIVATE (a_this)->tknzr, &token);
3997 ENSURE_PARSING_COND (status == CR_OK && token);
3999 if (token->type != SEMICOLON_TK) {
4000 cr_tknzr_unget_token
4001 (PRIVATE (a_this)->tknzr,
4007 cr_token_destroy (token);
4009 cr_parser_try_to_skip_spaces_and_comments (a_this);
4011 status = cr_parser_parse_declaration (a_this, &property,
4014 if (status != CR_OK)
4018 *call the relevant SAC handler here...
4020 if (PRIVATE (a_this)->sac_handler
4021 && PRIVATE (a_this)->sac_handler->property) {
4022 cr_term_ref (css_expression);
4023 PRIVATE (a_this)->sac_handler->property
4024 (PRIVATE (a_this)->sac_handler,
4025 property, css_expression, important);
4028 *... and free the data structure passed to that last
4032 cr_string_destroy (property);
4035 if (css_expression) {
4036 cr_term_unref (css_expression);
4037 css_expression = NULL;
4040 cr_parser_try_to_skip_spaces_and_comments
4043 cr_token_destroy (token) ;
4047 status = cr_tknzr_get_next_token
4048 (PRIVATE (a_this)->tknzr, &token);
4049 ENSURE_PARSING_COND (status == CR_OK
4051 && token->type == CBC_TK) ;
4052 cr_token_destroy (token) ;
4055 *call the relevant SAC handler here.
4057 if (PRIVATE (a_this)->sac_handler
4058 && PRIVATE (a_this)->sac_handler->end_page) {
4059 PRIVATE (a_this)->sac_handler->end_page
4060 (PRIVATE (a_this)->sac_handler,
4061 page_selector, page_pseudo_class);
4064 if (page_selector) {
4065 cr_string_destroy (page_selector);
4066 page_selector = NULL;
4069 if (page_pseudo_class) {
4070 cr_string_destroy (page_pseudo_class);
4071 page_pseudo_class = NULL;
4074 cr_parser_try_to_skip_spaces_and_comments (a_this);
4076 /*here goes the former implem of this function ... */
4078 cr_parser_clear_errors (a_this);
4079 PRIVATE (a_this)->state = PAGE_PARSED_STATE;
4085 cr_token_destroy (token);
4088 if (page_selector) {
4089 cr_string_destroy (page_selector);
4090 page_selector = NULL;
4092 if (page_pseudo_class) {
4093 cr_string_destroy (page_pseudo_class);
4094 page_pseudo_class = NULL;
4097 cr_string_destroy (property);
4100 if (css_expression) {
4101 cr_term_destroy (css_expression);
4102 css_expression = NULL;
4104 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
4109 * cr_parser_parse_charset:
4110 *@a_this: the "this pointer" of the current instance of #CRParser.
4111 *@a_value: out parameter. The actual parsed value of the charset
4112 *declararation. Note that for safety check reasons, *a_value must be
4114 *@a_charset_sym_location: the parsing location of the charset rule
4116 *Parses a charset declaration as defined implictly by the css2 spec in
4118 *charset ::= CHARSET_SYM S* STRING S* ';'
4120 *Returns CR_OK upon successfull completion, an error code otherwise.
4123 cr_parser_parse_charset (CRParser * a_this, CRString ** a_value,
4124 CRParsingLocation *a_charset_sym_location)
4126 enum CRStatus status = CR_OK;
4127 CRInputPos init_pos;
4128 CRToken *token = NULL;
4129 CRString *charset_str = NULL;
4131 g_return_val_if_fail (a_this && a_value
4132 && (*a_value == NULL),
4133 CR_BAD_PARAM_ERROR);
4135 RECORD_INITIAL_POS (a_this, &init_pos);
4137 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4139 ENSURE_PARSING_COND (status == CR_OK
4140 && token && token->type == CHARSET_SYM_TK);
4141 if (a_charset_sym_location) {
4142 cr_parsing_location_copy (a_charset_sym_location,
4145 cr_token_destroy (token);
4148 PRIVATE (a_this)->state = TRY_PARSE_CHARSET_STATE;
4150 cr_parser_try_to_skip_spaces_and_comments (a_this);
4152 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4153 ENSURE_PARSING_COND (status == CR_OK
4154 && token && token->type == STRING_TK);
4155 charset_str = token->u.str;
4156 token->u.str = NULL;
4157 cr_token_destroy (token);
4160 cr_parser_try_to_skip_spaces_and_comments (a_this);
4162 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4164 ENSURE_PARSING_COND (status == CR_OK
4165 && token && token->type == SEMICOLON_TK);
4166 cr_token_destroy (token);
4170 *a_value = charset_str;
4174 PRIVATE (a_this)->state = CHARSET_PARSED_STATE;
4180 cr_token_destroy (token);
4185 cr_string_destroy (*a_value);
4190 cr_string_destroy (charset_str);
4194 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
4200 * cr_parser_parse_font_face:
4201 *@a_this: the current instance of #CRParser.
4203 *Parses the "@font-face" rule specified in the css1 spec in
4206 *font_face ::= FONT_FACE_SYM S*
4207 *'{' S* declaration [ ';' S* declaration ]* '}' S*
4209 *This function will call SAC handlers whenever it is necessary.
4211 *Returns CR_OK upon successfull completion, an error code otherwise.
4214 cr_parser_parse_font_face (CRParser * a_this)
4216 enum CRStatus status = CR_ERROR;
4217 CRInputPos init_pos;
4218 CRString *property = NULL;
4219 CRTerm *css_expression = NULL;
4220 CRToken *token = NULL;
4221 gboolean important = FALSE;
4222 guint32 next_char = 0,
4224 CRParsingLocation location = {0} ;
4226 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
4228 RECORD_INITIAL_POS (a_this, &init_pos);
4230 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
4231 ENSURE_PARSING_COND (status == CR_OK
4233 && token->type == FONT_FACE_SYM_TK);
4235 cr_parser_try_to_skip_spaces_and_comments (a_this);
4237 cr_parsing_location_copy (&location,
4239 cr_token_destroy (token);
4242 status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
4244 ENSURE_PARSING_COND (status == CR_OK && token
4245 && token->type == CBO_TK);
4247 cr_token_destroy (token);
4251 *here, call the relevant SAC handler.
4253 if (PRIVATE (a_this)->sac_handler
4254 && PRIVATE (a_this)->sac_handler->start_font_face) {
4255 PRIVATE (a_this)->sac_handler->start_font_face
4256 (PRIVATE (a_this)->sac_handler, &location);
4258 PRIVATE (a_this)->state = TRY_PARSE_FONT_FACE_STATE;
4260 *and resume the parsing.
4262 cr_parser_try_to_skip_spaces_and_comments (a_this);
4263 status = cr_parser_parse_declaration (a_this, &property,
4264 &css_expression, &important);
4265 if (status == CR_OK) {
4267 *here, call the relevant SAC handler.
4269 cr_term_ref (css_expression);
4270 if (PRIVATE (a_this)->sac_handler &&
4271 PRIVATE (a_this)->sac_handler->property) {
4272 PRIVATE (a_this)->sac_handler->property
4273 (PRIVATE (a_this)->sac_handler,
4274 property, css_expression, important);
4276 ENSURE_PARSING_COND (css_expression && property);
4278 /*free the data structures allocated during last parsing. */
4280 cr_string_destroy (property);
4283 if (css_expression) {
4284 cr_term_unref (css_expression);
4285 css_expression = NULL;
4288 PEEK_NEXT_CHAR (a_this, &next_char);
4289 if (next_char == ';') {
4290 READ_NEXT_CHAR (a_this, &cur_char);
4294 cr_parser_try_to_skip_spaces_and_comments (a_this);
4295 status = cr_parser_parse_declaration (a_this,
4299 if (status != CR_OK)
4302 *here, call the relevant SAC handler.
4304 cr_term_ref (css_expression);
4305 if (PRIVATE (a_this)->sac_handler->property) {
4306 PRIVATE (a_this)->sac_handler->property
4307 (PRIVATE (a_this)->sac_handler,
4308 property, css_expression, important);
4311 *Then, free the data structures allocated during
4315 cr_string_destroy (property);
4318 if (css_expression) {
4319 cr_term_unref (css_expression);
4320 css_expression = NULL;
4323 cr_parser_try_to_skip_spaces_and_comments (a_this);
4324 READ_NEXT_CHAR (a_this, &cur_char);
4325 ENSURE_PARSING_COND (cur_char == '}');
4327 *here, call the relevant SAC handler.
4329 if (PRIVATE (a_this)->sac_handler->end_font_face) {
4330 PRIVATE (a_this)->sac_handler->end_font_face
4331 (PRIVATE (a_this)->sac_handler);
4333 cr_parser_try_to_skip_spaces_and_comments (a_this);
4336 cr_token_destroy (token);
4339 cr_parser_clear_errors (a_this);
4340 PRIVATE (a_this)->state = FONT_FACE_PARSED_STATE;
4345 cr_token_destroy (token);
4349 cr_string_destroy (property);
4352 if (css_expression) {
4353 cr_term_destroy (css_expression);
4354 css_expression = NULL;
4356 cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
4362 *@a_this: the current instance of #CRParser.
4364 *Parses the data that comes from the
4365 *input previously associated to the current instance of
4368 *Returns CR_OK upon succesful completion, an error code otherwise.
4371 cr_parser_parse (CRParser * a_this)
4373 enum CRStatus status = CR_ERROR;
4375 g_return_val_if_fail (a_this && PRIVATE (a_this)
4376 && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
4378 if (PRIVATE (a_this)->use_core_grammar == FALSE) {
4379 status = cr_parser_parse_stylesheet (a_this);
4381 status = cr_parser_parse_stylesheet_core (a_this);
4388 * cr_parser_set_tknzr:
4389 * @a_this: the current instance of #CRParser;
4390 * @a_tknzr: the new tokenizer.
4392 * Returns CR_OK upon successful completion, an error code otherwise.
4395 cr_parser_set_tknzr (CRParser * a_this, CRTknzr * a_tknzr)
4397 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
4399 if (PRIVATE (a_this)->tknzr) {
4400 cr_tknzr_unref (PRIVATE (a_this)->tknzr);
4403 PRIVATE (a_this)->tknzr = a_tknzr;
4406 cr_tknzr_ref (a_tknzr);
4412 * cr_parser_get_tknzr:
4413 *@a_this: the current instance of #CRParser
4414 *@a_tknzr: out parameter. The returned tokenizer
4416 *Getter of the parser's underlying tokenizer
4418 *Returns CR_OK upon succesful completion, an error code
4422 cr_parser_get_tknzr (CRParser * a_this, CRTknzr ** a_tknzr)
4424 g_return_val_if_fail (a_this && PRIVATE (a_this)
4425 && a_tknzr, CR_BAD_PARAM_ERROR);
4427 *a_tknzr = PRIVATE (a_this)->tknzr;
4432 * cr_parser_get_parsing_location:
4433 *@a_this: the current instance of #CRParser
4434 *@a_loc: the parsing location to get.
4436 *Gets the current parsing location.
4438 *Returns CR_OK upon succesful completion, an error code
4442 cr_parser_get_parsing_location (CRParser *a_this,
4443 CRParsingLocation *a_loc)
4445 g_return_val_if_fail (a_this
4447 && a_loc, CR_BAD_PARAM_ERROR) ;
4449 return cr_tknzr_get_parsing_location
4450 (PRIVATE (a_this)->tknzr, a_loc) ;
4454 * cr_parser_parse_buf:
4455 *@a_this: the current instance of #CRparser
4456 *@a_buf: the input buffer
4457 *@a_len: the length of the input buffer
4458 *@a_enc: the encoding of the buffer
4460 *Parses a stylesheet from a buffer
4462 *Returns CR_OK upon successful completion, an error code otherwise.
4465 cr_parser_parse_buf (CRParser * a_this,
4466 const guchar * a_buf,
4467 gulong a_len, enum CREncoding a_enc)
4469 enum CRStatus status = CR_ERROR;
4470 CRTknzr *tknzr = NULL;
4472 g_return_val_if_fail (a_this && PRIVATE (a_this)
4473 && a_buf, CR_BAD_PARAM_ERROR);
4475 tknzr = cr_tknzr_new_from_buf ((guchar*)a_buf, a_len, a_enc, FALSE);
4477 g_return_val_if_fail (tknzr != NULL, CR_ERROR);
4479 status = cr_parser_set_tknzr (a_this, tknzr);
4480 g_return_val_if_fail (status == CR_OK, CR_ERROR);
4482 status = cr_parser_parse (a_this);
4488 * cr_parser_destroy:
4489 *@a_this: the current instance of #CRParser to
4492 *Destroys the current instance
4496 cr_parser_destroy (CRParser * a_this)
4498 g_return_if_fail (a_this && PRIVATE (a_this));
4500 if (PRIVATE (a_this)->tknzr) {
4501 if (cr_tknzr_unref (PRIVATE (a_this)->tknzr) == TRUE)
4502 PRIVATE (a_this)->tknzr = NULL;
4505 if (PRIVATE (a_this)->sac_handler) {
4506 cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
4507 PRIVATE (a_this)->sac_handler = NULL;
4510 if (PRIVATE (a_this)->err_stack) {
4511 cr_parser_clear_errors (a_this);
4512 PRIVATE (a_this)->err_stack = NULL;
4515 if (PRIVATE (a_this)) {
4516 g_free (PRIVATE (a_this));
4517 PRIVATE (a_this) = NULL;
4522 a_this = NULL; /*useless. Just for the sake of coherence */