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