diff options
author | Dodji Seketeli <dodji@gnome.org> | 2004-01-24 19:24:08 +0000 |
---|---|---|
committer | Dodji Seketeli <dodji@src.gnome.org> | 2004-01-24 19:24:08 +0000 |
commit | d058779c836ec6ac56721581dd44d1dd91d3206c (patch) | |
tree | f371ad9e5150f3526edd528e44f990e9d060014c /src/cr-parser.c | |
parent | a97a943a32d37e1c92aa93e861a28d96f84f2163 (diff) | |
download | libcroco-d058779c836ec6ac56721581dd44d1dd91d3206c.tar.gz |
separated the library into two projects. Libcroco is now the combination
2004-01-24 Dodji Seketeli <dodji@gnome.org>
* all: separated the library into two projects. Libcroco
is now the combination of the parser and the selection engine.
A new project is born: sewfox. It is basically the
xml rendering/layout engine.
Libcroco now needs libxml2 and glib only.
Sewfox need libgnomecanvas2.
Diffstat (limited to 'src/cr-parser.c')
-rw-r--r-- | src/cr-parser.c | 5194 |
1 files changed, 5194 insertions, 0 deletions
diff --git a/src/cr-parser.c b/src/cr-parser.c new file mode 100644 index 0000000..c580799 --- /dev/null +++ b/src/cr-parser.c @@ -0,0 +1,5194 @@ +/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ + +/* + * This file is part of The Croco Library + * + * Copyright (C) 2002-2003 Dodji Seketeli <dodji@seketeli.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2.1 of the + * GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/* + *$Id$ + */ + +/** + *@file + *The definition of the #CRParser class. + */ + +#include "string.h" +#include "cr-parser.h" +#include "cr-num.h" +#include "cr-term.h" +#include "cr-simple-sel.h" +#include "cr-attr-sel.h" + +/* + *Random notes: + *CSS core syntax vs CSS level 2 syntax + *===================================== + * + *One must keep in mind + *that css UA must comply with two syntax. + * + *1/the specific syntax that defines the css language + *for a given level of specificatin (e.g css2 syntax + *defined in appendix D.1 of the css2 spec) + * + *2/the core (general) syntax that is there to allow + *UAs to parse style sheets written in levels of CSS that + *didn't exist at the time the UAs were created. + * + *the name of parsing functions (or methods) contained in this file + *follows the following scheme: cr_parser_parse_<production_name> (...) ; + *where <production_name> is the name + *of a production of the css2 language. + *When a given production is + *defined by the css2 level grammar *and* by the + *css core syntax, there will be two functions to parse that production: + *one will parse the production defined by the css2 level grammar and the + *other will parse the production defined by the css core grammar. + *The css2 level grammar related parsing function will be called: + *cr_parser_parse_<production_name> (...) ; + *Then css core grammar related parsing function will be called: + *cr_parser_parse_<production_name>_core (...) ; + * + *If a production is defined only by the css core grammar, then + *it will be named: + *cr_parser_parse_<production_name>_core (...) ; + */ + + + +typedef struct _CRParserError CRParserError ; + +/** + *An abstraction of an error reported by by the + *parsing routines. + */ +struct _CRParserError +{ + guchar * msg ; + enum CRStatus status ; + glong line ; + glong column ; + glong byte_num ; +} ; + + +enum CRParserState +{ + READY_STATE = 0, + TRY_PARSE_CHARSET_STATE, + CHARSET_PARSED_STATE, + TRY_PARSE_IMPORT_STATE, + IMPORT_PARSED_STATE, + TRY_PARSE_RULESET_STATE, + RULESET_PARSED_STATE, + TRY_PARSE_MEDIA_STATE, + MEDIA_PARSED_STATE, + TRY_PARSE_PAGE_STATE, + PAGE_PARSED_STATE, + TRY_PARSE_FONT_FACE_STATE, + FONT_FACE_PARSED_STATE +} ; + + +/** + *The private attributes of + *#CRParser. + */ +struct _CRParserPriv +{ + /** + *The tokenizer + */ + CRTknzr *tknzr ; + + /** + *The sac handlers to call + *to notify the parsing of + *the css2 constructions. + */ + CRDocHandler *sac_handler ; + + /** + *A stack of errors reported + *by the parsing routines. + *Contains instance of #CRParserError. + *This pointer is the top of the stack. + */ + GList *err_stack ; + + enum CRParserState state ; + gboolean resolve_import ; + gboolean is_case_sensitive ; + gboolean use_core_grammar ; +} ; + + +#define PRIVATE(obj) ((obj)->priv) + +#define CHARS_TAB_SIZE 12 + +/** + *return TRUE if the character is a number ([0-9]), FALSE otherwise + *@param a_char the char to test. + */ +#define IS_NUM(a_char) (((a_char) >= '0' && (a_char) <= '9')?TRUE:FALSE) + +/** + *Checks if 'status' equals CR_OK. If not, goto the 'error' label. + * + *@param status the status (of type enum CRStatus) to test. + *@param is_exception if set to FALSE, the final status returned the + *current function will be CR_PARSING_ERROR. If set to TRUE, the + *current status will be the current value of the 'status' variable. + * + */ +#define CHECK_PARSING_STATUS(status, is_exception) \ +if ((status) != CR_OK) \ +{ \ + if (is_exception == FALSE) \ + { \ + status = CR_PARSING_ERROR ; \ + } \ + goto error ; \ +} + + +/** + *same as CHECK_PARSING_STATUS() but this one pushes an error + *on the parser error stack when an error arises. + *@param a_this the current instance of #CRParser . + *@param a_status the status to check. Is of type enum #CRStatus. + *@param a_is_exception in case of error, if is TRUE, the status + *is set to CR_PARSING_ERROR before goto error. If is false, the + *real low level status is kept and will be returned by the + *upper level function that called this macro. Usally,this must + *be set to FALSE. + * + */ +#define CHECK_PARSING_STATUS_ERR(a_this, a_status, a_is_exception,\ + a_err_msg, a_err_status) \ +if ((status) != CR_OK) \ +{ \ + if (a_is_exception == FALSE) status = CR_PARSING_ERROR ; \ + cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \ + goto error ; \ +} + + +/** + *Peeks the next char from the input stream of the current parser + *by invoking cr_tknzr_input_peek_char(). + *invokes CHECK_PARSING_STATUS on the status returned by + *cr_tknzr_peek_char(). + * + *@param a_this the current instance of #CRParser. + *@param a_to_char a pointer to the char where to store the + *char peeked. + */ +#define PEEK_NEXT_CHAR(a_this, a_to_char) \ +{\ +enum CRStatus status ; \ +status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, a_to_char) ; \ +CHECK_PARSING_STATUS (status, TRUE) \ +} + + + +/** + *Reads the next char from the input stream of the current parser. + *In case of error, jumps to the "error:" label located in the + *function where this macro is called. + *@param a_this the curent instance of #CRParser + *@param to_char a pointer to the guint32 char where to store + *the character read. + */ +#define READ_NEXT_CHAR(a_this, a_to_char) \ +status = cr_tknzr_read_char (PRIVATE (a_this)->tknzr, a_to_char) ; \ +CHECK_PARSING_STATUS (status, TRUE) + + +/** + *Gets information about the current position in + *the input of the parser. + *In case of failure, this macro returns from the + *calling function and + *returns a status code of type enum #CRStatus. + *@param a_this the current instance of #CRParser. + *@param a_pos out parameter. A pointer to the position + *inside the current parser input. Must + */ +#define RECORD_INITIAL_POS(a_this, a_pos) \ +status = cr_tknzr_get_cur_pos (PRIVATE \ +(a_this)->tknzr, a_pos) ; \ +g_return_val_if_fail (status == CR_OK, status) + + +/** + *Gets the address of the current byte inside the + *parser input. + *@param parser the current instance of #CRParser. + *@param addr out parameter a pointer (guchar*) + *to where the address must be put. + */ +#define RECORD_CUR_BYTE_ADDR(a_this, a_addr) \ +status = cr_tknzr_get_cur_byte_addr \ + (PRIVATE (a_this)->tknzr, a_addr) ; \ +CHECK_PARSING_STATUS (status, TRUE) + + +/** + *Peeks a byte from the topmost parser input at + *a given offset from the current position. + *If it fails, goto the "error:" label. + * + *@param a_parser the current instance of #CRParser. + *@param a_offset the offset of the byte to peek, the + *current byte having the offset '0'. + *@param a_byte_ptr out parameter a pointer (guchar*) to + *where the peeked char is to be stored. + */ +#define PEEK_BYTE(a_parser, a_offset, a_byte_ptr) \ +status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr, \ + a_offset, \ + a_byte_ptr) ; \ +CHECK_PARSING_STATUS (status, TRUE) ; + +#define BYTE(a_parser, a_offset, a_eof) \ +cr_tknzr_peek_byte2 (PRIVATE (a_this)->tknzr, a_offset, a_eof) + +/** + *Reads a byte from the topmost parser input + *steam. + *If it fails, goto the "error" label. + *@param a_this the current instance of #CRParser. + *@param a_byte_ptr the guchar * where to put the read char. + */ +#define READ_NEXT_BYTE(a_this, a_byte_ptr) \ +status = cr_tknzr_read_byte (PRIVATE (a_this)->tknzr, a_byte_ptr) ; \ +CHECK_PARSING_STATUS (status, TRUE) ; + + +/** + *Skips a given number of byte in the topmost + *parser input. Don't update line and column number. + *In case of error, jumps to the "error:" label + *of the surrounding function. + *@param a_parser the current instance of #CRParser. + *@param a_nb_bytes the number of bytes to skip. + */ +#define SKIP_BYTES(a_this, a_nb_bytes) \ +status = cr_tknzr_seek_index (PRIVATE (a_this)->tknzr, \ + CR_SEEK_CUR, a_nb_bytes) ; \ +CHECK_PARSING_STATUS (status, TRUE) ; + + +/** + *Skip utf8 encoded characters. + *Updates line and column numbers. + *@param a_parser the current instance of #CRParser. + *@param a_nb_chars the number of chars to skip. Must be of + *type glong. + */ +#define SKIP_CHARS(a_parser, a_nb_chars) \ +{ \ +glong nb_chars = a_nb_chars ; \ +status = cr_tknzr_consume_chars \ + (PRIVATE (a_parser)->tknzr,0, &nb_chars) ; \ +CHECK_PARSING_STATUS (status, TRUE) ; \ +} + + +/** + *Tests the condition and if it is false, sets + *status to "CR_PARSING_ERROR" and goto the 'error' + *label. + *@param condition the condition to test. + */ +#define ENSURE_PARSING_COND(condition) \ +if (! (condition)) {status = CR_PARSING_ERROR; goto error ;} + + + +#define ENSURE_PARSING_COND_ERR(a_this, a_condition, \ + a_err_msg, a_err_status) \ +if (! (a_condition)) \ +{ \ + status = CR_PARSING_ERROR; \ + cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \ + goto error ; \ +} + + +#define GET_NEXT_TOKEN(a_this, a_token_ptr) \ +status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, \ + a_token_ptr) ; \ +ENSURE_PARSING_COND (status == CR_OK) ; + + +#ifdef WITH_UNICODE_ESCAPE_AND_RANGE +static enum CRStatus +cr_parser_parse_unicode_escape (CRParser *a_this, + guint32 *a_unicode) ; +static enum CRStatus +cr_parser_parse_escape (CRParser *a_this, guint32 *a_esc_code) ; + +static enum CRStatus +cr_parser_parse_unicode_range (CRParser *a_this, + GString **a_inf, + GString **a_sup) ; +#endif + +static enum CRStatus +cr_parser_parse_stylesheet_core (CRParser *a_this) ; + +static enum CRStatus +cr_parser_parse_atrule_core (CRParser *a_this) ; + +static enum CRStatus +cr_parser_parse_ruleset_core (CRParser *a_this) ; + +static enum CRStatus +cr_parser_parse_selector_core (CRParser *a_this) ; + +static enum CRStatus +cr_parser_parse_declaration_core (CRParser *a_this) ; + +static enum CRStatus +cr_parser_parse_any_core (CRParser *a_this) ; + +static enum CRStatus +cr_parser_parse_block_core (CRParser *a_this) ; + +static enum CRStatus +cr_parser_parse_value_core (CRParser *a_this) ; + +static enum CRStatus +cr_parser_parse_string (CRParser *a_this, GString **a_str) ; + +static enum CRStatus +cr_parser_parse_ident (CRParser *a_this, GString **a_str) ; + +static enum CRStatus +cr_parser_parse_uri (CRParser *a_this, GString **a_str) ; + +static enum CRStatus +cr_parser_parse_term (CRParser *a_this, CRTerm **a_term) ; + +static enum CRStatus +cr_parser_parse_function (CRParser *a_this, GString **a_func_name, + CRTerm **a_expr) ; +static enum CRStatus +cr_parser_parse_property (CRParser *a_this, GString **a_property) ; + +static enum CRStatus +cr_parser_parse_attribute_selector (CRParser *a_this, CRAttrSel **a_sel) ; + +static enum CRStatus +cr_parser_parse_simple_selector (CRParser *a_this, CRSimpleSel **a_sel) ; + +static enum CRStatus +cr_parser_parse_simple_sels (CRParser *a_this, CRSimpleSel **a_sel) ; + +static CRParserError * +cr_parser_error_new (const guchar * a_msg, enum CRStatus) ; + +static void +cr_parser_error_set_msg (CRParserError *a_this, const guchar *a_msg) ; + +static void +cr_parser_error_dump (CRParserError *a_this) ; + +static void +cr_parser_error_set_status (CRParserError *a_this, + enum CRStatus a_status) ; + +static void +cr_parser_error_set_pos (CRParserError *a_this, + glong a_line, + glong a_column, + glong a_byte_num) ; +static void +cr_parser_error_destroy (CRParserError *a_this) ; + +static enum CRStatus +cr_parser_push_error (CRParser *a_this, + const guchar * a_msg, + enum CRStatus a_status) ; + +static enum CRStatus +cr_parser_dump_err_stack (CRParser *a_this, + gboolean a_clear_errs) ; +static enum CRStatus +cr_parser_clear_errors (CRParser *a_this) ; + + +/***************************** + *error managemet methods + *****************************/ + +/** + *Constructor of #CRParserError class. + *@param a_msg the brute error message. + *@param a_status the error status. + *@return the newly built instance of #CRParserError. + */ +static CRParserError * +cr_parser_error_new (const guchar * a_msg, enum CRStatus a_status) +{ + CRParserError * result = NULL ; + + result = g_try_malloc (sizeof (CRParserError)) ; + + if (result == NULL) + { + cr_utils_trace_info ("Out of memory") ; + return NULL ; + } + + memset (result, 0, sizeof (CRParserError)) ; + + cr_parser_error_set_msg (result, a_msg) ; + cr_parser_error_set_status (result, a_status) ; + + return result ; +} + +/** + *Sets the message associated to this instance of #CRError. + *@param a_this the current instance of #CRParserError. + *@param a_msg the new message. + */ +static void +cr_parser_error_set_msg (CRParserError *a_this, const guchar *a_msg) +{ + g_return_if_fail (a_this) ; + + if (a_this->msg) + { + g_free (a_this->msg) ; + } + + a_this->msg = g_strdup (a_msg) ; +} + + +/** + *Sets the error status. + *@param a_this the current instance of #CRParserError. + *@param a_status the new error status. + * + */ +static void +cr_parser_error_set_status (CRParserError *a_this, + enum CRStatus a_status) +{ + g_return_if_fail (a_this) ; + + a_this->status = a_status ; +} + +/** + *Sets the position of the parser error. + *@param a_this the current instance of #CRParserError. + *@param a_line the line number. + *@param a_column the column number. + *@param a_byte_num the byte number. + */ +static void +cr_parser_error_set_pos (CRParserError *a_this, + glong a_line, + glong a_column, + glong a_byte_num) +{ + g_return_if_fail (a_this) ; + + a_this->line = a_line ; + a_this->column = a_column ; + a_this->byte_num = a_byte_num ; +} + + +static void +cr_parser_error_dump (CRParserError *a_this) +{ + g_return_if_fail (a_this) ; + + g_printerr ("parsing error: %ld:%ld:", a_this->line, + a_this->column) ; + + g_printerr ("%s\n", a_this->msg) ; +} + + +/** + *The destructor of #CRParserError. + *@param a_this the current instance of #CRParserError. + */ +static void +cr_parser_error_destroy (CRParserError *a_this) +{ + g_return_if_fail (a_this) ; + + if (a_this->msg) + { + g_free (a_this->msg) ; + a_this->msg = NULL ; + } + + g_free (a_this) ; +} + + +/** + *Pushes an error on the parser error stack. + *@param a_this the current instance of #CRParser. + *@param a_msg the error message. + *@param a_status the error status. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_push_error (CRParser *a_this, + const guchar * a_msg, + enum CRStatus a_status) +{ + enum CRStatus status = CR_OK ; + + CRParserError *error = NULL ; + CRInputPos pos ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && a_msg, + CR_BAD_PARAM_ERROR) ; + + error = cr_parser_error_new (a_msg, a_status) ; + + g_return_val_if_fail (error, CR_ERROR) ; + + RECORD_INITIAL_POS (a_this, &pos) ; + + cr_parser_error_set_pos + (error, pos.line, + pos.col, + pos.next_byte_index - 1); + + + PRIVATE (a_this)->err_stack = + g_list_prepend (PRIVATE (a_this)->err_stack, + error) ; + + if (PRIVATE (a_this)->err_stack == NULL) goto error ; + + return CR_OK ; + + error: + + if (error) + { + cr_parser_error_destroy (error) ; + error = NULL ; + } + + return status ; +} + +/** + *Dumps the error stack on stdout. + *@param a_this the current instance of #CRParser. + *@param a_clear_errs whether to clear the error stack + *after the dump or not. + *@return CR_OK upon successfull completion, an error code + *otherwise. + */ +static enum CRStatus +cr_parser_dump_err_stack (CRParser *a_this, + gboolean a_clear_errs) +{ + GList *cur = NULL ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + if (PRIVATE (a_this)->err_stack == NULL) + return CR_OK ; + + for (cur = PRIVATE (a_this)->err_stack ; + cur; + cur = cur->next) + { + cr_parser_error_dump ((CRParserError*)cur->data) ; + } + + if (a_clear_errs == TRUE) + { + cr_parser_clear_errors (a_this) ; + } + + return CR_OK ; +} + + +/** + *Clears all the errors contained in the parser error stack. + *Frees all the errors, and the stack that contains'em. + *@param a_this the current instance of #CRParser. + */ +static enum CRStatus +cr_parser_clear_errors (CRParser *a_this) +{ + GList *cur = NULL ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + for (cur = PRIVATE (a_this)->err_stack ; + cur ; + cur = cur->next) + { + if (cur->data) + { + cr_parser_error_destroy ((CRParserError*) + cur->data) ; + } + } + + if (PRIVATE (a_this)->err_stack) + { + g_list_free (PRIVATE (a_this)->err_stack) ; + PRIVATE (a_this)->err_stack = NULL ; + } + + return CR_OK ; +} + +/** + *Same as cr_parser_try_to_skip_spaces() but this one skips + *spaces and comments. + * + *@param a_this the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_try_to_skip_spaces_and_comments (CRParser *a_this) +{ + enum CRStatus status = CR_ERROR ; + CRToken *token = NULL ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && PRIVATE (a_this)->tknzr, + CR_BAD_PARAM_ERROR) ; + do + { + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + if (status != CR_OK) goto error ; + } + while ((token != NULL) + && (token->type == COMMENT_TK || token->type == S_TK)) ; + + cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token) ; + + return status ; + + error: + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + return status ; +} + +/*************************************** + *End of Parser input handling routines + ***************************************/ + +#ifdef WITH_UNICODE_ESCAPE_AND_RANGE + +/** + *Parses an "unicode-range" as defined in the css spec at [4.1.1]: + * UNICODE-RANGE ::= U\+[0-9A-F?]{1,6}(-[0-9A-F]{1,6})? + *@param a_this the current instance of #CRParser. + *@param a_inf out parameter. The inferior barrier of the range. + *@param a_sup out parameter. The superior barrier of the range. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_unicode_range (CRParser *a_this, GString **a_inf, + GString **a_sup) +{ + enum CRStatus status = CR_OK ; + CRInputPos init_pos; + glong i = 0 ; + gboolean min_str_needs_free = FALSE, + sup_str_needs_free = FALSE ; + guint32 cur_char = 0, next_char = 0 ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && PRIVATE (a_this)->tknzr + && a_inf && a_sup, + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + READ_NEXT_CHAR (a_this, &cur_char) ; + + ENSURE_PARSING_COND_ERR + (a_this, cur_char == 'U', + "while parsing an unicode range: unicode range must start with an U", + CR_SYNTAX_ERROR) ; + + READ_NEXT_CHAR (a_this, &cur_char) ; + + ENSURE_PARSING_COND_ERR + (a_this, cur_char == '+', + "while parsing an unicode range: there must be a + after the U", + CR_SYNTAX_ERROR) ; + + if (*a_inf == NULL) + { + *a_inf = g_string_new (NULL) ; + min_str_needs_free = TRUE ; + } + + for (i = 0 ;i < 6 ; i++) + { + PEEK_NEXT_CHAR (a_this, &next_char) ; + + if (cr_utils_is_hexa_char (next_char) == TRUE + || next_char == '?') + { + READ_NEXT_CHAR (a_this, &cur_char) ; + g_string_append_unichar (*a_inf, cur_char) ; + } + else + { + break ; + } + } + + if (i < 1) + { + status = CR_PARSING_ERROR ; + cr_parser_push_error (a_this, + "No unicode range expressed", + CR_SYNTAX_ERROR) ; + goto error ; + } + + if (next_char != '-') + { + return CR_OK ; + } + + READ_NEXT_CHAR (a_this, &cur_char) ; + /*we are sure that cur_char == '-'*/ + + if (*a_sup == NULL) + { + *a_sup = g_string_new (NULL) ; + sup_str_needs_free = TRUE ; + } + + + for (i = 0 ;i < 6 ; i++) + { + PEEK_NEXT_CHAR (a_this, &next_char) ; + + if (cr_utils_is_hexa_char (next_char) == TRUE) + { + READ_NEXT_CHAR (a_this, &cur_char) ; + if (*a_sup == NULL) + { + *a_sup = g_string_new (NULL) ; + sup_str_needs_free = TRUE ; + } + g_string_append_unichar (*a_sup, cur_char) ; + } + else + { + break ; + } + } + + cr_parser_clear_errors (a_this) ; + return CR_OK ; + + error: + + if (min_str_needs_free == TRUE && *a_inf) + { + g_free (*a_inf) ; + *a_inf = NULL ; + } + + if (sup_str_needs_free == TRUE && *a_sup) + { + g_free (*a_sup) ; + *a_sup = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + +/** + *Parses an 'unicode' escape sequence defined + *in css spec at chap 4.1.1: + *unicode ::= \\[0-9a-f]{1,6}[ \n\r\t\f]? + *@param a_this the current instance of #CRParser. + *@param a_start out parameter. A pointer to the start + *of the unicode escape sequence. Must *NOT* be deleted by + *the caller. + *@param a_end out parameter. A pointer to the last character + *of the unicode escape sequence. Must *NOT* be deleted by the caller. + *@return CR_OK if parsing succeded, an error code otherwise. + *Error code can be either CR_PARSING_ERROR if the string + *parsed just doesn't + *respect the production or another error if a + *lower level error occured. + */ +static enum CRStatus +cr_parser_parse_unicode_escape (CRParser *a_this, + guint32 *a_unicode) +{ + guint32 cur_char ; + CRInputPos init_pos ; + glong occur = 0 ; + guint32 unicode = 0 ; + guchar *tmp_char_ptr1 = NULL, *tmp_char_ptr2 = NULL ; + enum CRStatus status = CR_OK ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && a_unicode, + CR_BAD_PARAM_ERROR) ; + + /*first, let's backup the current position pointer*/ + RECORD_INITIAL_POS (a_this, &init_pos) ; + + READ_NEXT_CHAR (a_this, &cur_char) ; + + if (cur_char != '\\') + { + status = CR_PARSING_ERROR ; + goto error ; + } + + READ_NEXT_CHAR (a_this, &cur_char) ; + + for (occur = 0, unicode = 0 ; + ((cur_char >= '0' && cur_char <= '9') + || (cur_char >= 'a' && cur_char <= 'f') + || (cur_char >= 'A' && cur_char <= 'F')) && occur < 6 ; + occur ++) + { + gint cur_char_val = 0 ; + + if ((cur_char >= '0' && cur_char <= '9')) + { + cur_char_val = (cur_char - '0') ; + } + else if ((cur_char >= 'a' && cur_char <= 'f')) + { + cur_char_val = 10 + (cur_char - 'a') ; + } + else if ((cur_char >= 'A' && cur_char <= 'F')) + { + cur_char_val = 10 + (cur_char - 'A') ; + } + + unicode = unicode * 10 + cur_char_val ; + + READ_NEXT_CHAR (a_this, &cur_char) ; + } + + + if (occur == 5) + { + /* + *the unicode escape is 6 digit length + */ + + /* + *parse one space that may + *appear just after the unicode + *escape. + */ + cr_parser_parse_w (a_this, &tmp_char_ptr1, + &tmp_char_ptr2) ; + status = CR_OK ; + } + else + { + /* + *The unicode escape is less than + *6 digit length. The character + *that comes right after the escape + *must be a white space. + */ + status = cr_parser_parse_w (a_this, &tmp_char_ptr1, + &tmp_char_ptr2) ; + + CHECK_PARSING_STATUS_ERR + (a_this, status, FALSE, + "next char expected to be a space", + CR_SYNTAX_ERROR) ; + } + + if (status == CR_OK) + { + *a_unicode = unicode ; + cr_parser_clear_errors (a_this) ; + return CR_OK ; + } + + error: + /* + *restore the initial position pointer backuped at + *the beginning of this function. + */ + cr_tknzr_set_cur_pos (PRIVATE (a_this)tknzr, + &init_pos) ; + + return status ; +} + + +/** + *parses an escape sequence as defined by the css spec: + *escape ::= {unicode}|\\[ -~\200-\4177777] + *@param a_this the current instance of #CRParser . + */ +static enum CRStatus +cr_parser_parse_escape (CRParser *a_this, guint32 *a_esc_code) +{ + enum CRStatus status = CR_OK ; + guint32 cur_char = 0 ; + CRInputPos init_pos ; + guchar next_chars[2] ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && a_esc_code, CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + PEEK_BYTE (a_this, 1, &next_chars[0]) ; + PEEK_BYTE (a_this, 2, &next_chars[1]) ; + + if (next_chars[0] != '\\') + { + status = CR_PARSING_ERROR ; + cr_parser_push_error + (a_this, + "next char expected to be a '\\'", + CR_SYNTAX_ERROR) ; + + goto error ; + } + + if ((next_chars[1] >= '0' && next_chars[1] <= '9') + || (next_chars[1] >= 'a' && next_chars[1] <='z') + || (next_chars[1] >= 'A' && next_chars[1] <='Z')) + { + status = + cr_parser_parse_unicode_escape (a_this, + a_esc_code); + if (status != CR_OK + && cr_parser_errors_exist (a_this) == TRUE) + { + cr_parser_clear_errors (a_this) ; + } + } + else + { + /*consume the '\' char*/ + READ_NEXT_CHAR (a_this, &cur_char) ; + + /*then read the char after the '\'*/ + READ_NEXT_CHAR (a_this, &cur_char) ; + + if (cur_char != ' ' + && (cur_char < 200 || cur_char > 4177777)) + { + status = CR_PARSING_ERROR ; + + cr_parser_push_error + (a_this, + "next char expected to be a space", + CR_SYNTAX_ERROR) ; + + goto error ; + } + + *a_esc_code = cur_char ; + + } + + if (status == CR_OK) + { + cr_parser_clear_errors (a_this) ; + return CR_OK ; + } + + error: + + cr_tknzr_set_cur_pos (PRIVATE (a_this)tknzr, + &init_pos) ; + + return status ; +} +#endif /*WITH_UNICODE_ESCAPE_AND_RANGE*/ + + +/************************************* + *Non trivial terminal productions + *parsing routines + *************************************/ + +/** + *Parses a css stylesheet following the core css grammar. + *This is mainly done for test purposes. + *During the parsing, no callback is called. This is just + *to validate that the stylesheet is well formed according to the + *css core syntax. + *stylesheet : [ CDO | CDC | S | statement ]*; + *@param a_this the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_stylesheet_core (CRParser *a_this) +{ + CRToken *token = NULL ; + CRInputPos init_pos ; + enum CRStatus status = CR_ERROR ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + continue_parsing: + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + if (status == CR_END_OF_INPUT_ERROR) + { + status = CR_OK ; + goto done ; + } + else if (status != CR_OK) + { + goto error ; + } + + switch (token->type) + { + + case CDO_TK: + case CDC_TK: + goto continue_parsing ; + break ; + default: + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token = NULL ; + status = cr_parser_parse_statement_core (a_this) ; + cr_parser_clear_errors (a_this) ; + if (status == CR_OK) + { + goto continue_parsing ; + } + else if (status == CR_END_OF_INPUT_ERROR) + { + goto done ; + } + else + { + goto error ; + } + } + + done: + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_parser_clear_errors (a_this) ; + return CR_OK ; + error: + + cr_parser_push_error + (a_this, "could not recognize next production", + CR_ERROR) ; + + cr_parser_dump_err_stack (a_this, TRUE) ; + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + +/** + *Parses an at-rule as defined by the css core grammar + *in chapter 4.1 in the css2 spec. + *at-rule : ATKEYWORD S* any* [ block | ';' S* ]; + *@param a_this the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code + *otherwise. + */ +static enum CRStatus +cr_parser_parse_atrule_core (CRParser *a_this) +{ + CRToken *token = NULL ; + CRInputPos init_pos ; + enum CRStatus status = CR_ERROR ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token + && + (token->type == ATKEYWORD_TK + ||token->type == IMPORT_SYM_TK + ||token->type == PAGE_SYM_TK + ||token->type == MEDIA_SYM_TK + ||token->type == FONT_FACE_SYM_TK + ||token->type == CHARSET_SYM_TK)) ; + + cr_token_destroy (token) ; token = NULL ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + do + { + status = cr_parser_parse_any_core (a_this) ; + } while (status == CR_OK) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token) ; + ENSURE_PARSING_COND (status == CR_OK && token) ; + + if (token->type == CBO_TK) + { + cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + status = cr_parser_parse_block_core (a_this) ; + CHECK_PARSING_STATUS (status, FALSE) ; + goto done ; + } + else if (token->type == SEMICOLON_TK) + { + goto done ; + } + else + { + goto error ; + } + + done: + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + return CR_OK ; + + error: + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + +/** + *Parses a ruleset as defined by the css core grammar in chapter + *4.1 of the css2 spec. + *ruleset ::= selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*; + *@param a_this the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_ruleset_core (CRParser *a_this) +{ + CRToken *token = NULL ; + CRInputPos init_pos ; + enum CRStatus status = CR_ERROR ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_parser_parse_selector_core (a_this) ; + + ENSURE_PARSING_COND (status == CR_OK + || status == CR_PARSING_ERROR + || status == CR_END_OF_INPUT_ERROR) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == CBO_TK) ; + cr_token_destroy (token) ; token = NULL ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + status = cr_parser_parse_declaration_core (a_this) ; + + parse_declaration_list: + if (token) + { + cr_token_destroy (token) ; token = NULL ; + } + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token) ; + if (token->type == CBC_TK) + { + goto done ; + } + + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == SEMICOLON_TK) ; + + cr_token_destroy (token) ; token = NULL ; + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + status = cr_parser_parse_declaration_core (a_this) ; + cr_parser_clear_errors (a_this) ; + ENSURE_PARSING_COND (status == CR_OK + || status == CR_PARSING_ERROR) ; + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK && token) ; + if (token->type == CBC_TK) + { + cr_token_destroy (token) ; token = NULL ; + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + goto done ; + } + else + { + status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, + token) ; + token = NULL ; + goto parse_declaration_list ; + } + + done: + if (token) + { + cr_token_destroy (token) ; token = NULL ; + } + + if (status == CR_OK) + { + return CR_OK ; + } + + error: + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + +/** + *Parses a "selector" as specified by the css core + *grammar. + *selector : any+; + *@param a_this the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code + *otherwise. + */ +static enum CRStatus +cr_parser_parse_selector_core (CRParser *a_this) +{ + CRToken *token = NULL ; + CRInputPos init_pos ; + enum CRStatus status = CR_ERROR ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_parser_parse_any_core (a_this) ; + CHECK_PARSING_STATUS (status, FALSE) ; + + do + { + status = cr_parser_parse_any_core (a_this) ; + + }while (status == CR_OK) ; + + return CR_OK ; + + error: + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + +/** + *Parses a "block" as defined in the css core grammar + *in chapter 4.1 of the css2 spec. + *block ::= '{' S* [ any | block | ATKEYWORD S* | ';' ]* '}' S*; + *@param a_this the current instance of #CRParser. + *FIXME: code this function. + */ +static enum CRStatus +cr_parser_parse_block_core (CRParser *a_this) +{ + CRToken *token = NULL ; + CRInputPos init_pos ; + enum CRStatus status = CR_ERROR ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == CBO_TK) ; + + parse_block_content: + + if (token) + { + cr_token_destroy (token) ; token = NULL ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK && token) ; + + if (token->type == CBC_TK) + { + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + goto done ; + } + else if (token->type == SEMICOLON_TK) + { + goto parse_block_content ; + } + else if (token->type == ATKEYWORD_TK) + { + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + goto parse_block_content ; + } + else if (token->type == CBO_TK) + { + cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + status = cr_parser_parse_block_core (a_this) ; + CHECK_PARSING_STATUS (status, FALSE) ; + goto parse_block_content ; + } + else + { + cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + status = cr_parser_parse_any_core (a_this) ; + CHECK_PARSING_STATUS (status, FALSE) ; + goto parse_block_content ; + } + + done: + if (token) + { + cr_token_destroy (token) ; token = NULL ; + } + + if (status == CR_OK) return CR_OK ; + + error: + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + + +static enum CRStatus +cr_parser_parse_declaration_core (CRParser *a_this) +{ + CRToken *token = NULL ; + CRInputPos init_pos ; + enum CRStatus status = CR_ERROR ; + GString *prop = NULL ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_parser_parse_property (a_this, &prop) ; + CHECK_PARSING_STATUS (status, FALSE) ; + cr_parser_clear_errors (a_this) ; + ENSURE_PARSING_COND (status == CR_OK && prop) ; + g_string_free (prop, TRUE) ; prop = NULL ; + + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token); + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == DELIM_TK + && token->u.unichar == ':' ) ; + cr_token_destroy (token) ; token = NULL ; + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + status = cr_parser_parse_value_core (a_this) ; + CHECK_PARSING_STATUS (status, FALSE) ; + + return CR_OK ; + + error: + + if (prop) + { + g_string_free (prop, TRUE) ; + prop = NULL ; + } + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + +/** + *Parses a "value" production as defined by the css core grammar + *in chapter 4.1. + *value ::= [ any | block | ATKEYWORD S* ]+; + *@param a_this the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_value_core (CRParser *a_this) +{ + CRToken *token = NULL ; + CRInputPos init_pos ; + enum CRStatus status = CR_ERROR ; + glong ref = 0 ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + RECORD_INITIAL_POS (a_this, &init_pos) ; + + + continue_parsing: + + if (token) + { + cr_token_destroy (token) ; token = NULL ; + } + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK && token) ; + + switch (token->type) + { + case CBO_TK: + status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, + token) ; + token = NULL ; + status = cr_parser_parse_block_core (a_this) ; + CHECK_PARSING_STATUS (status, FALSE) ; + ref ++ ; + goto continue_parsing ; + + case ATKEYWORD_TK: + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + ref ++ ; + goto continue_parsing ; + + default : + status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, + token) ; + token = NULL ; + status = cr_parser_parse_any_core (a_this) ; + if (status == CR_OK) + { + ref ++ ; + goto continue_parsing ; + } + else if (status == CR_PARSING_ERROR) + { + status = CR_OK ; + goto done ; + } + else + { + goto error ; + } + } + + done: + if (token) + { + cr_token_destroy (token) ; token = NULL ; + } + + if (status == CR_OK && ref) + return CR_OK ; + error: + if (token) + { + cr_token_destroy (token) ; token = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + + +/** + *Parses an "any" as defined by the css core grammar in the + *css2 spec in chapter 4.1. + *any ::= [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING + * | DELIM | URI | HASH | UNICODE-RANGE | INCLUDES + * | FUNCTION | DASHMATCH | '(' any* ')' | '[' any* ']' ] S*; + * + *@param a_this the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_any_core (CRParser *a_this) +{ + CRToken *token1 = NULL, *token2 = NULL ; + CRInputPos init_pos ; + enum CRStatus status = CR_ERROR ; + + g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token1) ; + + ENSURE_PARSING_COND (status == CR_OK && token1) ; + + switch (token1->type) + { + case IDENT_TK: + case NUMBER_TK: + case RGB_TK: + case PERCENTAGE_TK: + case DIMEN_TK: + case EMS_TK: + case EXS_TK: + case LENGTH_TK: + case ANGLE_TK: + case FREQ_TK: + case TIME_TK: + case STRING_TK: + case DELIM_TK: + case URI_TK: + case HASH_TK: + case UNICODERANGE_TK: + case INCLUDES_TK: + case DASHMATCH_TK: + case S_TK: + case IMPORTANT_SYM_TK: + case SEMICOLON_TK: + status = CR_OK ; + break ; + case FUNCTION_TK: + /* + *this case isn't specified by the spec but it + *does happen. So we have to handle it. + *We must consider function with parameters. + *We consider parameter as being an "any*" production. + */ + do + { + status = cr_parser_parse_any_core (a_this) ; + } while (status == CR_OK) ; + + ENSURE_PARSING_COND (status == CR_PARSING_ERROR) ; + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token2) ; + ENSURE_PARSING_COND (status == CR_OK + && token2 + && token2->type == PC_TK) ; + break ; + case PO_TK: + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token2) ; + ENSURE_PARSING_COND (status == CR_OK && token2) ; + + if (token2->type == PC_TK) + { + cr_token_destroy (token2) ; + token2 = NULL ; + goto done ; + } + else + { + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token2) ; + token2 = NULL ; + } + + do + { + status = cr_parser_parse_any_core (a_this) ; + } while (status == CR_OK) ; + + ENSURE_PARSING_COND (status == CR_PARSING_ERROR) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token2) ; + ENSURE_PARSING_COND (status == CR_OK + && token2 + && token2->type == PC_TK) ; + status = CR_OK ; + break ; + + case BO_TK: + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token2) ; + ENSURE_PARSING_COND (status == CR_OK && token2) ; + + if (token2->type == BC_TK) + { + cr_token_destroy (token2) ; + token2 = NULL ; + goto done ; + } + else + { + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token2) ; + token2 = NULL ; + } + + do + { + status = cr_parser_parse_any_core (a_this) ; + } while (status == CR_OK) ; + + ENSURE_PARSING_COND (status == CR_PARSING_ERROR) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token2) ; + ENSURE_PARSING_COND (status == CR_OK + && token2 + && token2->type == BC_TK) ; + status = CR_OK ; + break ; + default: + status = CR_PARSING_ERROR ; + goto error ; + } + + done: + if (token1) + { + cr_token_destroy (token1) ; + token1 = NULL ; + } + + if (token2) + { + cr_token_destroy (token2) ; + token2 = NULL ; + } + + return CR_OK ; + + error: + + if (token1) + { + cr_token_destroy (token1) ; + token1 = NULL ; + } + + if (token2) + { + cr_token_destroy (token2) ; + token2 = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + return status ; +} + + + +/** + *Parses an attribute selector as defined in the css2 spec in + *appendix D.1: + *attrib ::= '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S* + * [ IDENT | STRING ] S* ]? ']' + * + *@param a_this the "this pointer" of the current instance of + *#CRParser . + *@param a_sel out parameter. The successfully parsed attribute selector. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_attribute_selector (CRParser *a_this, CRAttrSel **a_sel) +{ + enum CRStatus status = CR_OK ; + CRInputPos init_pos ; + CRToken *token = NULL ; + CRAttrSel *result = NULL ; + + g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == BO_TK) ; + + cr_token_destroy (token) ; + token = NULL ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + result = cr_attr_sel_new () ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == IDENT_TK) ; + + result->name = token->u.str ; + token->u.str = NULL ; + cr_token_destroy (token) ; + token = NULL ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token) ; + + if (token->type == INCLUDES_TK) + { + result->match_way = INCLUDES ; + goto parse_right_part ; + } + else if (token->type == DASHMATCH_TK) + { + result->match_way = DASHMATCH ; + goto parse_right_part ; + } + else if (token->type == DELIM_TK && token->u.unichar == '=') + { + result->match_way = EQUALS ; + goto parse_right_part ; + } + else if (token->type == BC_TK) + { + result->match_way = SET ; + goto done ; + } + + parse_right_part: + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + + ENSURE_PARSING_COND (status == CR_OK + && token) ; + + if (token->type == IDENT_TK) + { + result->value = token->u.str ; + token->u.str = NULL ; + } + else if (token->type == STRING_TK) + { + result->value = token->u.str ; + token->u.str = NULL ; + } + else + { + status = CR_PARSING_ERROR ; + goto error ; + } + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == BC_TK) ; + done: + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + if (*a_sel) + { + status = cr_attr_sel_append_attr_sel (*a_sel, + result) ; + CHECK_PARSING_STATUS (status, FALSE) ; + } + else + { + *a_sel = result ; + } + + cr_parser_clear_errors (a_this) ; + return CR_OK ; + + error: + + if (result) + { + cr_attr_sel_destroy (result) ; + result = NULL ; + } + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + + + +/** + *Parses a "property" as specified by the css2 spec at [4.1.1]: + *property : IDENT S*; + * + *@param a_this the "this pointer" of the current instance of #CRParser. + *@param GString a_property out parameter. The parsed property without the + *trailing spaces. If *a_property is NULL, this function allocates a + *new instance of GString and set it content to the parsed property. + *If not, the property is just appended to a_property's previous content. + *In both cases, it is up to the caller to free a_property. + *@return CR_OK upon successfull completion, CR_PARSING_ERROR if the + *next construction was not a "property", or an error code. + */ +static enum CRStatus +cr_parser_parse_property (CRParser *a_this, GString **a_property) +{ + enum CRStatus status = CR_OK ; + CRInputPos init_pos ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && PRIVATE (a_this)->tknzr + && a_property, + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_parser_parse_ident (a_this, a_property) ; + + CHECK_PARSING_STATUS (status, FALSE) ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + cr_parser_clear_errors (a_this) ; + return CR_OK ; + + error: + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + + +/** + *Parses a "term" as defined in the css2 spec, appendix D.1: + *term ::= unary_operator? [NUMBER S* | PERCENTAGE S* | LENGTH S* | + *EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* | function ] | + *STRING S* | IDENT S* | URI S* | RGB S* | UNICODERANGE S* | hexcolor + * + *TODO: handle parsing of 'RGB' + * + *@param a_term out parameter. The successfully parsed term. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_term (CRParser *a_this, CRTerm **a_term) +{ + enum CRStatus status = CR_PARSING_ERROR ; + CRInputPos init_pos ; + CRTerm *result = NULL; + CRTerm *param = NULL ; + CRToken * token = NULL ; + GString *func_name = NULL ; + + g_return_val_if_fail (a_this && a_term, CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + result = cr_term_new () ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + if (status != CR_OK || !token) goto error ; + + if (token->type == DELIM_TK && token->u.unichar == '+') + { + result->unary_op = PLUS_UOP ; + } + else if (token->type + == DELIM_TK && token->u.unichar == '-') + { + result->unary_op = MINUS_UOP ; + } + else if (token->type == EMS_TK + || token->type == EXS_TK + || token->type == LENGTH_TK + || token->type == ANGLE_TK + || token->type == TIME_TK + || token->type == FREQ_TK + || token->type == PERCENTAGE_TK + || token->type == NUMBER_TK) + { + status = cr_term_set_number (result, token->u.num) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token->u.num = NULL ; + status = CR_OK ; + } + else if (token && token->type == FUNCTION_TK) + { + status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, + token) ; + token = NULL ; + status = cr_parser_parse_function + (a_this, &func_name, ¶m) ; + + if (status == CR_OK) + { + status = cr_term_set_function (result, func_name, + param) ; + CHECK_PARSING_STATUS (status, TRUE) ; + } + } + else if (token && token->type == STRING_TK) + { + status = cr_term_set_string (result, token->u.str) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token->u.str = NULL ; + } + else if (token && token->type == IDENT_TK) + { + status = cr_term_set_ident (result, token->u.str) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token->u.str = NULL ; + } + else if (token && token->type == URI_TK) + { + status = cr_term_set_uri (result, token->u.str) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token->u.str = NULL ; + } + else if (token && token->type == RGB_TK) + { + status = cr_term_set_rgb (result, token->u.rgb) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token->u.rgb = NULL ; + } + else if (token && token->type == UNICODERANGE_TK) + { + result->type = TERM_UNICODERANGE ; + status = CR_PARSING_ERROR ; + } + else if (token && token->type == HASH_TK) + { + status = cr_term_set_hash (result, token->u.str) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token->u.str = NULL ; + } + else + { + status = CR_PARSING_ERROR ; + } + + if (status != CR_OK) + { + goto error ; + } + + *a_term = cr_term_append_term (*a_term, result) ; + + result = NULL ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + if (token) + { + cr_token_destroy (token); + token = NULL ; + } + + cr_parser_clear_errors (a_this) ; + return CR_OK ; + + error: + + if (result) + { + cr_term_destroy (result) ; + result = NULL ; + } + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + if (param) + { + cr_term_destroy (param) ; + param = NULL ; + } + + if (func_name) + { + g_string_free (func_name, TRUE) ; + func_name = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + + + +/** + *Parses a "simple_selector" as defined by the css2 spec in appendix D.1 : + *element_name? [ HASH | class | attrib | pseudo ]* S* + *and where pseudo is: + *pseudo ::= ':' [ IDENT | FUNCTION S* IDENT S* ')' ] + * + *@Param a_this the "this pointer" of the current instance of #CRParser. + *@param a_sel out parameter. Is set to the successfully parsed simple + *selector. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_simple_selector (CRParser *a_this, CRSimpleSel **a_sel) +{ + enum CRStatus status = CR_ERROR ; + CRInputPos init_pos ; + CRToken *token = NULL ; + CRSimpleSel * sel = NULL ; + CRAdditionalSel *add_sel_list = NULL ; + gboolean found_sel = FALSE ; + guint32 cur_char = 0 ; + + g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + if (status != CR_OK) goto error ; + + sel = cr_simple_sel_new () ; + ENSURE_PARSING_COND (sel) ; + + if (token && token->type == DELIM_TK && token->u.unichar == '*') + { + sel->type_mask |= UNIVERSAL_SELECTOR ; + sel->name = g_string_new ("*") ; + found_sel = TRUE ; + } + else if (token && token->type == IDENT_TK) + { + sel->name = token->u.str ; + sel->type_mask |= TYPE_SELECTOR ; + token->u.str = NULL ; + found_sel = TRUE ; + + } + else + { + status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, + token) ; + token = NULL ; + } + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + for (;;) + { + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + if (status != CR_OK) goto error ; + + if (token && token->type == HASH_TK) + { + /*we parsed an attribute id*/ + CRAdditionalSel *add_sel = NULL ; + + add_sel = cr_additional_sel_new_with_type + (ID_ADD_SELECTOR) ; + + add_sel->content.id_name = token->u.str ; + token->u.str = NULL ; + + add_sel_list = + cr_additional_sel_append + (add_sel_list, add_sel) ; + + found_sel = TRUE ; + } + else if (token + && (token->type == DELIM_TK) + && (token->u.unichar == '.')) + { + cr_token_destroy (token) ; + token = NULL ; + + status = cr_tknzr_get_next_token + (PRIVATE (a_this)->tknzr, &token) ; + if (status != CR_OK) goto error ; + + if (token && token->type == IDENT_TK) + { + CRAdditionalSel *add_sel = NULL ; + + add_sel = cr_additional_sel_new_with_type + (CLASS_ADD_SELECTOR) ; + + add_sel->content.class_name = token->u.str ; + token->u.str = NULL ; + + add_sel_list = + cr_additional_sel_append + (add_sel_list, add_sel) ; + found_sel = TRUE ; + } + else + { + status = CR_OK ; + goto error ; + } + } + else if (token + && token->type == BO_TK) + { + CRAttrSel *attr_sel = NULL ; + CRAdditionalSel *add_sel = NULL ; + + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + if (status != CR_OK) goto error ; + token = NULL ; + + status = cr_parser_parse_attribute_selector + (a_this, &attr_sel) ; + CHECK_PARSING_STATUS (status, FALSE) ; + + add_sel = cr_additional_sel_new_with_type + (ATTRIBUTE_ADD_SELECTOR) ; + + ENSURE_PARSING_COND (add_sel != NULL) ; + + add_sel->content.attr_sel = attr_sel ; + + add_sel_list = + cr_additional_sel_append + (add_sel_list, add_sel) ; + found_sel = TRUE ; + } + else if (token + && (token->type == DELIM_TK) + && (token->u.unichar == ':')) + { + CRPseudo *pseudo = NULL ; + + /*try to parse a pseudo*/ + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + pseudo = cr_pseudo_new () ; + + status = cr_tknzr_get_next_token + (PRIVATE (a_this)->tknzr, &token) ; + + ENSURE_PARSING_COND (status == CR_OK + && token) ; + + if (token->type == IDENT_TK) + { + pseudo->type = IDENT_PSEUDO ; + pseudo->name = token->u.str ; + token->u.str = NULL ; + found_sel = TRUE ; + } + else if (token->type == FUNCTION_TK) + { + pseudo->name = token->u.str ; + token->u.str = NULL ; + cr_parser_try_to_skip_spaces_and_comments + (a_this) ; + status = cr_parser_parse_ident + (a_this, &pseudo->extra) ; + + ENSURE_PARSING_COND (status == CR_OK) ; + READ_NEXT_CHAR (a_this, &cur_char) ; + ENSURE_PARSING_COND (cur_char == ')') ; + pseudo->type = FUNCTION_PSEUDO ; + found_sel = TRUE ; + } + else + { + status = CR_PARSING_ERROR ; + goto error ; + } + + if (status == CR_OK) + { + CRAdditionalSel *add_sel = NULL ; + + add_sel = cr_additional_sel_new_with_type + (PSEUDO_CLASS_ADD_SELECTOR) ; + + add_sel->content.pseudo = pseudo ; + + add_sel_list = + cr_additional_sel_append + (add_sel_list, add_sel) ; + + status = CR_OK ; + } + } + else + { + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + break ; + } + } + + if (status == CR_OK && found_sel == TRUE) + { + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + sel->add_sel = add_sel_list ; + add_sel_list = NULL ; + + if (*a_sel == NULL) + { + *a_sel = sel ; + } + else + { + cr_simple_sel_append_simple_sel (*a_sel, sel) ; + } + + sel = NULL ; + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_parser_clear_errors (a_this) ; + return CR_OK ; + } + else + { + status = CR_PARSING_ERROR ; + } + + error: + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + if (add_sel_list) + { + cr_additional_sel_destroy (add_sel_list) ; + add_sel_list = NULL ; + } + + if (sel) + { + cr_simple_sel_destroy (sel) ; + sel = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; + + +} + + +/** + *Parses a "selector" as defined by the css2 spec in appendix D.1: + *selector ::= simple_selector [ combinator simple_selector ]* + * + *@param a_this the this pointer of the current instance of #CRParser. + *@param a_start a pointer to the + *first chararcter of the successfully parsed + *string. + *@param a_end a pointer to the last character of the successfully parsed + *string. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_simple_sels (CRParser *a_this, CRSimpleSel **a_sel) +{ + enum CRStatus status = CR_ERROR ; + CRInputPos init_pos ; + CRSimpleSel *sel = NULL ; + guint32 cur_char = 0 ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_parser_parse_simple_selector (a_this, &sel) ; + CHECK_PARSING_STATUS (status, FALSE) ; + + *a_sel = cr_simple_sel_append_simple_sel (*a_sel, sel) ; + + for (;;) + { + guint32 next_char = 0 ; + enum Combinator comb = 0 ; + + sel = NULL ; + + PEEK_NEXT_CHAR (a_this, &next_char) ; + + if (next_char == '+') + { + READ_NEXT_CHAR (a_this, &cur_char) ; + comb = COMB_PLUS ; + cr_parser_try_to_skip_spaces_and_comments + (a_this) ; + } + else if (next_char == '>') + { + READ_NEXT_CHAR (a_this, &cur_char) ; + comb = COMB_GT ; + cr_parser_try_to_skip_spaces_and_comments + (a_this) ; + } + else + { + comb = COMB_WS ; + } + + status = cr_parser_parse_simple_selector (a_this, &sel) ; + if (status != CR_OK) break ; + + if (comb) + { + sel->combinator = comb ; + comb = 0 ; + } + + *a_sel = cr_simple_sel_append_simple_sel (*a_sel, sel) ; + } + + cr_parser_clear_errors (a_this) ; + return CR_OK ; + + error: + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + + + +static enum CRStatus +cr_parser_parse_selector (CRParser *a_this, CRSelector **a_selector) +{ + enum CRStatus status = CR_OK ; + CRInputPos init_pos ; + guint32 cur_char = 0, next_char = 0 ; + CRSimpleSel * simple_sels = NULL ; + CRSelector *selector = NULL ; + + g_return_val_if_fail (a_this && a_selector, + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_parser_parse_simple_sels (a_this, &simple_sels) ; + + CHECK_PARSING_STATUS (status, FALSE) ; + + if (simple_sels) + { + selector = cr_selector_append_simple_sel + (selector, simple_sels) ; + simple_sels = NULL ; + } + + status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, + &next_char) ; + if (status != CR_OK) + { + if (status == CR_END_OF_INPUT_ERROR) + { + status = CR_OK ; + goto okay ; + } + else + { + goto error ; + } + } + + if (next_char == ',') + { + for (;;) + { + simple_sels = NULL ; + + status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, + &next_char) ; + if (status != CR_OK) + { + if (status == CR_END_OF_INPUT_ERROR) + { + status = CR_OK ; + break ; + } + else + { + goto error ; + } + } + + if (next_char != ',') break ; + + /*consume the ',' char*/ + READ_NEXT_CHAR (a_this, &cur_char) ; + + cr_parser_try_to_skip_spaces_and_comments + (a_this) ; + + status = cr_parser_parse_simple_sels + (a_this, &simple_sels) ; + + CHECK_PARSING_STATUS (status, FALSE) ; + + if (simple_sels) + { + selector = + cr_selector_append_simple_sel + (selector, simple_sels) ; + + simple_sels = NULL ; + } + } + } + + okay: + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + if (!*a_selector) + { + *a_selector = selector ; + } + else + { + *a_selector = cr_selector_append (*a_selector, + selector) ; + } + + selector = NULL ; + return CR_OK ; + + error: + + if (simple_sels) + { + cr_simple_sel_destroy (simple_sels) ; + simple_sels = NULL ; + } + + if (selector) + { + cr_selector_unref (selector) ; + selector = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + + +/** + *Parses a "function" as defined in css spec at appendix D.1: + *function ::= FUNCTION S* expr ')' S* + *FUNCTION ::= ident'(' + * + *@param a_this the "this pointer" of the current instance of + *#CRParser. + * + *@param a_func_name out parameter. The parsed function name + *@param a_expr out parameter. The successfully parsed term. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_function (CRParser *a_this, GString **a_func_name, + CRTerm **a_expr) +{ + CRInputPos init_pos ; + enum CRStatus status = CR_OK ; + CRToken *token = NULL ; + CRTerm *expr = NULL ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && a_func_name, + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + if (status != CR_OK) goto error ; + + if (token && token->type == FUNCTION_TK) + { + *a_func_name = token->u.str ; + token->u.str = NULL ; + } + else + { + status = CR_PARSING_ERROR ; + goto error ; + } + + cr_token_destroy (token) ; + token = NULL ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + if (status != CR_OK) goto error ; + + ENSURE_PARSING_COND (token && token->type == PO_TK) ; + + cr_token_destroy (token) ; + token = NULL ; + + status = cr_parser_parse_term (a_this, &expr) ; + + CHECK_PARSING_STATUS (status, FALSE) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + if (status != CR_OK) goto error ; + + ENSURE_PARSING_COND (token && token->type == PC_TK) ; + + cr_token_destroy (token) ; + token = NULL ; + + if (expr) + { + *a_expr = cr_term_append_term (*a_expr, expr) ; + expr = NULL ; + } + + cr_parser_clear_errors (a_this) ; + return CR_OK ; + + error: + + if (*a_func_name) + { + g_string_free (*a_func_name, TRUE) ; + *a_func_name = NULL ; + } + + if (expr) + { + cr_term_destroy (expr) ; + expr = NULL ; + } + + if (token) + { + cr_token_destroy (token) ; + + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + + + + +/** + *Parses an uri as defined by the css spec [4.1.1]: + * URI ::= url\({w}{string}{w}\) + * |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\) + * + *@param a_this the current instance of #CRParser. + *@param a_str the successfully parsed url. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_uri (CRParser *a_this, GString **a_str) +{ + + enum CRStatus status = CR_PARSING_ERROR ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && PRIVATE (a_this)->tknzr, + CR_BAD_PARAM_ERROR) ; + + + status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr, + URI_TK, NO_ET, a_str, NULL) ; + return status ; +} + + +/** + *Parses a string type as defined in css spec [4.1.1]: + * + *string ::= {string1}|{string2} + *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\" + *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\' + * + *@param a_this the current instance of #CRParser. + *@param a_start out parameter. Upon successfull completion, + *points to the beginning of the string, points to an undefined value + *otherwise. + *@param a_end out parameter. Upon successfull completion, points to + *the beginning of the string, points to an undefined value otherwise. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_string (CRParser *a_this, GString **a_str) +{ + enum CRStatus status = CR_OK ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && PRIVATE (a_this)->tknzr + && a_str, + CR_BAD_PARAM_ERROR) ; + + status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr, + STRING_TK, NO_ET, a_str, NULL) ; + return status ; +} + +/** + *Parses an "ident" as defined in css spec [4.1.1]: + *ident ::= {nmstart}{nmchar}* + * + *@param a_this the currens instance of #CRParser. + * + *@param a_str a pointer to parsed ident. If *a_str is NULL, + *this function allocates a new instance of GString. If not, + *the function just appends the parsed string to the one passed. + *In both cases it is up to the caller to free *a_str. + * + *@return CR_OK upon successfull completion, an error code + *otherwise. + */ +static enum CRStatus +cr_parser_parse_ident (CRParser *a_this, GString **a_str) +{ + enum CRStatus status = CR_OK ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && PRIVATE (a_this)->tknzr + && a_str, + CR_BAD_PARAM_ERROR) ; + + status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr, + IDENT_TK, NO_ET, a_str, NULL) ; + return status ; +} + +/** + *Parses a stylesheet as defined in the css2 spec in appendix D.1: + *stylesheet ::= [ CHARSET_SYM S* STRING S* ';' ]? + * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]* + * [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]* + * + *TODO: Finish the code of this function. Think about splitting it into + *smaller functions. + * + *@param a_this the "this pointer" of the current instance of #CRParser. + *@param a_start out parameter. A pointer to the first character of + *the successfully parsed string. + *@param a_end out parameter. A pointer to the first character of + *the successfully parsed string. + * + *@return CR_OK upon successfull completion, an error code otherwise. + */ +static enum CRStatus +cr_parser_parse_stylesheet (CRParser *a_this) +{ + enum CRStatus status = CR_OK ; + CRInputPos init_pos ; + CRToken * token = NULL ; + GString *charset = NULL ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && PRIVATE (a_this)->tknzr, + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + PRIVATE (a_this)->state = READY_STATE ; + + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->start_document) + { + PRIVATE (a_this)->sac_handler->start_document + (PRIVATE (a_this)->sac_handler) ; + } + +/* parse_charset:*/ + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + + if (status == CR_END_OF_INPUT_ERROR) goto done ; + CHECK_PARSING_STATUS (status, TRUE) ; + + if (token && token->type == CHARSET_SYM_TK) + { + status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, + token) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token = NULL ; + + status = cr_parser_parse_charset (a_this, &charset) ; + + if (status == CR_OK && charset) + { + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->charset) + { + PRIVATE (a_this)->sac_handler->charset + (PRIVATE (a_this)->sac_handler, + charset) ; + } + } + else if (status != CR_END_OF_INPUT_ERROR) + { + status = cr_parser_parse_atrule_core (a_this) ; + CHECK_PARSING_STATUS (status, FALSE) ; + } + + if (charset) + { + g_string_free (charset, TRUE) ; + charset = NULL ; + } + } + else if (token + && (token->type == S_TK || token->type == COMMENT_TK)) + { + status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, + token) ; + token = NULL ; + CHECK_PARSING_STATUS (status, TRUE) ; + + cr_parser_try_to_skip_spaces_and_comments + (a_this) ; + } + else if (token) + { + status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, + token) ; + token = NULL ; + CHECK_PARSING_STATUS (status, TRUE) ; + } + + +/* parse_imports:*/ + do + { + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + status = cr_tknzr_get_next_token + (PRIVATE (a_this)->tknzr, &token) ; + + if (status == CR_END_OF_INPUT_ERROR) goto done ; + CHECK_PARSING_STATUS (status, TRUE) ; + } while (token + && (token->type == S_TK + || token->type == CDO_TK + || token->type == CDC_TK)) ; + + if (token) + { + status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, + token) ; + token = NULL ; + } + + for (;;) + { + status = cr_tknzr_get_next_token + (PRIVATE (a_this)->tknzr, &token) ; + if (status == CR_END_OF_INPUT_ERROR) goto done ; + CHECK_PARSING_STATUS (status, TRUE) ; + + if (token && token->type == IMPORT_SYM_TK) + { + GList *media_list = NULL ; + GString *import_string = NULL ; + + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + CHECK_PARSING_STATUS (status, TRUE) ; + + status = cr_parser_parse_import (a_this, + &media_list, + &import_string) ; + + if (status == CR_OK) + { + if (import_string + && PRIVATE (a_this)->sac_handler + && PRIVATE(a_this)->sac_handler-> + import_style) + { + PRIVATE (a_this)->sac_handler-> + import_style (PRIVATE + (a_this)-> + sac_handler, + media_list, + import_string, + NULL) ; + + if ((PRIVATE (a_this)->sac_handler-> + resolve_import == TRUE) ) + { + /* + *TODO: resolve the + *import rule. + */ + } + + if ((PRIVATE (a_this)->sac_handler-> + import_style_result)) + { + PRIVATE (a_this)-> + sac_handler-> + import_style_result + (PRIVATE (a_this)-> + sac_handler, + media_list, + import_string, + NULL, + NULL) ; + } + } + } + else if (status != CR_END_OF_INPUT_ERROR) + { + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler-> + error) + { + PRIVATE (a_this)->sac_handler-> + error + (PRIVATE + (a_this)->sac_handler) ; + } + + status = cr_parser_parse_atrule_core + (a_this) ; + } + + /* + *then, after calling the appropriate + *SAC handler, free + *the media_list and import_string. + */ + if (media_list) + { + GList * cur = NULL ; + + /*free the medium list*/ + for (cur = media_list ; + cur ; cur = cur->next) + { + if (cur->data) + { + g_string_free + (cur->data, + TRUE) ; + } + } + + g_list_free (media_list) ; + media_list = NULL ; + } + + if (import_string) + { + g_string_free (import_string, + TRUE) ; + import_string = NULL ; + } + + cr_parser_try_to_skip_spaces_and_comments + (a_this) ; + } + else if (token + && (token->type == S_TK + || token->type == CDO_TK + || token->type == CDC_TK)) + { + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + + do + { + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + status = cr_tknzr_get_next_token + (PRIVATE (a_this)->tknzr, &token); + + if (status == CR_END_OF_INPUT_ERROR) + goto done ; + CHECK_PARSING_STATUS (status, TRUE) ; + } while (token + && (token->type == S_TK + || token->type == CDO_TK + || token->type == CDC_TK)) ; + } + else + { + if (token) + { + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + } + goto parse_ruleset_and_others ; + } + } + + parse_ruleset_and_others: + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + for (;;) + { + status = cr_tknzr_get_next_token + (PRIVATE (a_this)->tknzr, &token) ; + if (status == CR_END_OF_INPUT_ERROR) + goto done ; + CHECK_PARSING_STATUS (status, TRUE) ; + + if (token + && (token->type == S_TK + || token->type == CDO_TK + || token->type == CDC_TK)) + { + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + + do + { + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_parser_try_to_skip_spaces_and_comments + (a_this) ; + status = cr_tknzr_get_next_token + (PRIVATE (a_this)->tknzr, &token); + }while (token + && (token->type == S_TK + || token->type == COMMENT_TK + || token->type == CDO_TK + || token->type == CDC_TK)) ; + if (token) + { + cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + } + } + else if (token + && (token->type == HASH_TK + || (token->type == DELIM_TK + && token->u.unichar == '.') + || (token->type == DELIM_TK + && token->u.unichar == ':') + || (token->type == DELIM_TK + && token->u.unichar == '*') + || (token->type == BO_TK) + || token->type == IDENT_TK)) + { + /* + *Try to parse a CSS2 ruleset. + *if the parsing fails, try to parse + *a css core ruleset. + */ + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token = NULL ; + + status = cr_parser_parse_ruleset (a_this) ; + + if (status == CR_OK) + { + continue ; + } + else + { + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler-> + error) + { + PRIVATE (a_this)->sac_handler-> + error + (PRIVATE + (a_this)->sac_handler) ; + } + + status = cr_parser_parse_ruleset_core + (a_this) ; + + if (status == CR_OK) + { + continue ; + } + else + { + break ; + } + } + } + else if (token && token->type == MEDIA_SYM_TK) + { + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token = NULL ; + + status = cr_parser_parse_media (a_this) ; + if (status == CR_OK) + { + continue ; + } + else + { + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler-> + error) + { + PRIVATE (a_this)->sac_handler-> + error + (PRIVATE + (a_this)->sac_handler) ; + } + + status = cr_parser_parse_atrule_core + (a_this) ; + + if (status == CR_OK) + { + continue ; + } + else + { + break ; + } + } + + } + else if (token && token->type == PAGE_SYM_TK) + { + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token = NULL ; + status = cr_parser_parse_page (a_this) ; + + if (status == CR_OK) + { + continue ; + } + else + { + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler-> + error) + { + PRIVATE (a_this)->sac_handler-> + error + (PRIVATE + (a_this)->sac_handler) ; + } + + status = cr_parser_parse_atrule_core + (a_this) ; + + if (status == CR_OK) + { + continue ; + } + else + { + break ; + } + } + } + else if (token && token->type == FONT_FACE_SYM_TK) + { + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token = NULL ; + status = cr_parser_parse_font_face (a_this) ; + + if (status == CR_OK) + { + continue ; + } + else + { + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler-> + error) + { + PRIVATE (a_this)->sac_handler-> + error + (PRIVATE + (a_this)->sac_handler) ; + } + + status = cr_parser_parse_atrule_core + (a_this) ; + + if (status == CR_OK) + { + continue ; + } + else + { + break ; + } + } + } + else + { + status = cr_tknzr_unget_token + (PRIVATE (a_this)->tknzr, token) ; + CHECK_PARSING_STATUS (status, TRUE) ; + token = NULL ; + status = cr_parser_parse_statement_core + (a_this) ; + + if (status == CR_OK) + { + continue ; + } + else + { + break ; + } + } + } + + done: + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + if (status == CR_END_OF_INPUT_ERROR + || status == CR_OK) + { + + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->end_document) + { + PRIVATE (a_this)->sac_handler->end_document + (PRIVATE (a_this)->sac_handler) ; + } + + return CR_OK ; + } + + cr_parser_push_error + (a_this, "could not recognize next production", + CR_ERROR) ; + + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler-> + unrecoverable_error) + { + PRIVATE (a_this)->sac_handler-> + unrecoverable_error + (PRIVATE + (a_this)->sac_handler) ; + } + + cr_parser_dump_err_stack (a_this, TRUE) ; + + return status ; + + error: + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler-> + unrecoverable_error) + { + PRIVATE (a_this)->sac_handler-> + unrecoverable_error + (PRIVATE + (a_this)->sac_handler) ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, + &init_pos) ; + + return status ; +} + + +/**************************************** + *Public CRParser Methods + ****************************************/ + + +/** + *Creates a new parser to parse data + *coming the input stream given in parameter. + *@param a_input the input stream of the parser. + *Note that the newly created parser will ref + *a_input and unref it when parsing reaches the + *end of the input stream. + *@return the newly created instance of #CRParser, + *or NULL if an error occured. + */ +CRParser * +cr_parser_new (CRTknzr *a_tknzr) +{ + CRParser * result = NULL ; + enum CRStatus status = CR_OK ; + + result = g_malloc0 (sizeof (CRParser)) ; + + PRIVATE (result) = g_malloc0 (sizeof (CRParserPriv)) ; + + if (a_tknzr) + { + status = cr_parser_set_tknzr (result, a_tknzr) ; + } + + g_return_val_if_fail (status == CR_OK, NULL) ; + + return result ; +} + + +/** + *Instanciates a new parser from a memory buffer. + *@param a_buf the buffer to parse. + *@param a_len the length of the data in the buffer. + *@param a_enc the encoding of the input buffer a_buf. + *@param a_free_buf if set to TRUE, a_buf will be freed + *during the destruction of the newly built instance + *of #CRParser. If set to FALSE, it is up to the caller to + *eventually free it. + *@return the newly built parser, or NULL if an error arises. + */ +CRParser * +cr_parser_new_from_buf (const guchar *a_buf, + gulong a_len, + enum CREncoding a_enc, + gboolean a_free_buf) +{ + CRParser * result = NULL ; + CRInput *input = NULL ; + g_return_val_if_fail (a_buf && a_len, NULL) ; + + input = cr_input_new_from_buf (a_buf, a_len, a_enc, + a_free_buf) ; + g_return_val_if_fail (input, NULL) ; + + result = cr_parser_new_from_input (input) ; + if (!result) + { + cr_input_destroy (input) ; + input = NULL ; + return NULL ; + } + return result ; +} + +CRParser * +cr_parser_new_from_input (CRInput *a_input) +{ + CRParser *result = NULL ; + CRTknzr *tokenizer = NULL ; + + if (a_input) + { + tokenizer = cr_tknzr_new (a_input) ; + g_return_val_if_fail (tokenizer, NULL) ; + } + + result = cr_parser_new (tokenizer) ; + g_return_val_if_fail (result, NULL) ; + + return result ; +} + + +CRParser * +cr_parser_new_from_file (const guchar *a_file_uri, + enum CREncoding a_enc) +{ + CRParser *result = NULL ; + CRTknzr *tokenizer = NULL ; + + tokenizer = cr_tknzr_new_from_uri (a_file_uri, a_enc) ; + if (!tokenizer) + { + cr_utils_trace_info ("Could not open input file") ; + return NULL ; + } + + result = cr_parser_new (tokenizer) ; + g_return_val_if_fail (result, NULL) ; + return result ; +} + + +/** + *Sets a SAC document handler to the parser. + *@param a_this the "this pointer" of the current instance of #CRParser. + *@param a_handler the handler to set. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_set_sac_handler (CRParser *a_this, CRDocHandler *a_handler) +{ + g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; + + if (PRIVATE (a_this)->sac_handler) + { + cr_doc_handler_unref (PRIVATE (a_this)->sac_handler) ; + } + + PRIVATE (a_this)->sac_handler = a_handler ; + cr_doc_handler_ref (a_handler) ; + + return CR_OK ; +} + + +/** + *Gets the SAC document handler. + *@param a_this the "this pointer" of the current instance of + *#CRParser. + *@param a_handler out parameter. The returned handler. + *@return CR_OK upon successfull completion, an error code + *otherwise. + */ +enum CRStatus +cr_parser_get_sac_handler (CRParser *a_this, CRDocHandler **a_handler) +{ + g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; + + *a_handler = PRIVATE (a_this)->sac_handler ; + + return CR_OK ; +} + + +/** + *Sets the SAC handler associated to the current instance + *of #CRParser to the default SAC handler. + *@param a_this a pointer to the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_set_default_sac_handler (CRParser *a_this) +{ + CRDocHandler *default_sac_handler = NULL ; + enum CRStatus status = CR_ERROR ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + default_sac_handler = cr_doc_handler_new () ; + + cr_doc_handler_set_default_sac_handler (default_sac_handler) ; + + status = cr_parser_set_sac_handler (a_this, default_sac_handler) ; + + if (status != CR_OK) + { + cr_doc_handler_destroy (default_sac_handler) ; + default_sac_handler = NULL ; + } + + return status ; +} + + +enum CRStatus +cr_parser_set_use_core_grammar (CRParser *a_this, + gboolean a_use_core_grammar) +{ + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + PRIVATE (a_this)->use_core_grammar = a_use_core_grammar ; + + return CR_OK ; +} + +enum CRStatus +cr_parser_get_use_core_grammar (CRParser *a_this, + gboolean *a_use_core_grammar) +{ + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + *a_use_core_grammar = PRIVATE (a_this)->use_core_grammar ; + + return CR_OK ; +} + +/** + *Parses a the given in parameter. + *@param a_this a pointer to the current instance of #CRParser. + *@param a_file_uri the uri to the file to load. For the time being, + *only local files are supported. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_parse_file (CRParser *a_this, + const guchar *a_file_uri, + enum CREncoding a_enc) +{ + enum CRStatus status = CR_ERROR ; + CRTknzr *tknzr = NULL ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && a_file_uri, + CR_BAD_PARAM_ERROR) ; + + tknzr = cr_tknzr_new_from_uri (a_file_uri, a_enc) ; + + g_return_val_if_fail (tknzr != NULL, CR_ERROR) ; + + status = cr_parser_set_tknzr (a_this, tknzr) ; + g_return_val_if_fail (status == CR_OK, CR_ERROR) ; + + status = cr_parser_parse (a_this) ; + + return status ; +} + +/** + *Parses an expression as defined by the css2 spec in appendix + *D.1: + *expr: term [ operator term ]* + */ +enum CRStatus +cr_parser_parse_expr (CRParser *a_this, CRTerm **a_expr) +{ + enum CRStatus status = CR_ERROR ; + CRInputPos init_pos ; + CRTerm *expr = NULL, *expr2 = NULL ; + guchar next_byte = 0 ; + gulong nb_terms = 0 ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && a_expr, + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_parser_parse_term (a_this, &expr) ; + + CHECK_PARSING_STATUS (status, FALSE) ; + + for (;;) + { + guchar operator = 0 ; + status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr, + 1, &next_byte) ; + if (status != CR_OK) + { + if (status == CR_END_OF_INPUT_ERROR) + { + if (!nb_terms) + { + goto error ; + } + status = CR_OK ; + break ; + } + else + { + goto error ; + } + } + + if (next_byte == '/' || next_byte == ',') + { + READ_NEXT_BYTE (a_this, &operator) ; + } + + cr_parser_try_to_skip_spaces_and_comments + (a_this) ; + + status = cr_parser_parse_term (a_this, &expr2) ; + + if (status != CR_OK || expr2 == NULL) + { + status = CR_OK ; + break ; + } + + switch (operator) + { + case '/': + expr2->the_operator = DIVIDE ; + break ; + case ',': + expr2->the_operator = COMMA ; + + default: + break ; + } + + expr = cr_term_append_term (expr, expr2) ; + expr2 = NULL ; + operator = 0 ; + nb_terms ++ ; + } + + if (status == CR_OK) + { + *a_expr = cr_term_append_term (*a_expr, expr) ; + expr = NULL ; + + cr_parser_clear_errors (a_this) ; + return CR_OK ; + } + + error: + + if (expr) + { + cr_term_destroy (expr) ; + expr = NULL ; + } + + if (expr2) + { + cr_term_destroy (expr2) ; + expr2 = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, + &init_pos) ; + + return status ; +} + +/** + *Parses a "declaration" as defined by the css2 spec in appendix D.1: + *declaration ::= [property ':' S* expr prio?]? + * + *@param a_this the "this pointer" of the current instance of #CRParser. + *@param a_property the successfully parsed property. The caller + * *must* free the returned pointer. + *@param a_expr the expression that represents the attribute value. + *The caller *must* free the returned pointer. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_parse_declaration (CRParser *a_this, GString **a_property, + CRTerm **a_expr) +{ + enum CRStatus status = CR_ERROR ; + CRInputPos init_pos ; + guint32 cur_char = 0 ; + CRTerm *expr = NULL ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && a_property && a_expr, + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_parser_parse_property (a_this, a_property) ; + + CHECK_PARSING_STATUS_ERR + (a_this, status, FALSE, + "while parsing declaration: next property is malformed", + CR_SYNTAX_ERROR) ; + + + READ_NEXT_CHAR (a_this, &cur_char) ; + + if (cur_char != ':') + { + status = CR_PARSING_ERROR ; + cr_parser_push_error + (a_this, + "while parsing declaration: this char must be ':'", + CR_SYNTAX_ERROR) ; + goto error ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_parser_parse_expr (a_this, &expr) ; + + CHECK_PARSING_STATUS_ERR + (a_this, status, FALSE, + "while parsing declaration: next expression is malformed", + CR_SYNTAX_ERROR) ; + + if (*a_expr) + { + cr_term_append_term (*a_expr, expr) ; + expr = NULL ; + } + else + { + *a_expr = expr ; + expr = NULL ; + } + + cr_parser_clear_errors (a_this) ; + return CR_OK ; + + error: + + if (expr) + { + cr_term_destroy (expr) ; + expr = NULL ; + } + + if (*a_property) + { + g_string_free (*a_property, TRUE) ; + *a_property = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + +/** + *Parses a statement as defined by the css core grammar in + *chapter 4.1 of the css2 spec. + *statement : ruleset | at-rule; + *@param a_this the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_parse_statement_core (CRParser *a_this) +{ + CRToken *token = NULL ; + CRInputPos init_pos ; + enum CRStatus status = CR_ERROR ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + + ENSURE_PARSING_COND (status == CR_OK && token) ; + + switch (token->type) + { + case ATKEYWORD_TK: + case IMPORT_SYM_TK: + case PAGE_SYM_TK: + case MEDIA_SYM_TK: + case FONT_FACE_SYM_TK: + case CHARSET_SYM_TK: + cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + status = cr_parser_parse_atrule_core (a_this) ; + CHECK_PARSING_STATUS (status, TRUE) ; + break ; + + default: + cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + status = cr_parser_parse_ruleset_core (a_this) ; + cr_parser_clear_errors (a_this) ; + CHECK_PARSING_STATUS (status, TRUE) ; + } + + return CR_OK ; + + error: + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + +/** + *Parses a "ruleset" as defined in the css2 spec at appendix D.1. + *ruleset ::= selector [ ',' S* selector ]* + *'{' S* declaration? [ ';' S* declaration? ]* '}' S*; + * + *This methods calls the the SAC handler on the relevant SAC handler + *callbacks whenever it encounters some specific constructions. + *See the documentation of #CRDocHandler (the SAC handler) to know + *when which SAC handler is called. + *@param a_this the "this pointer" of the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_parse_ruleset (CRParser *a_this) +{ + enum CRStatus status = CR_OK ; + CRInputPos init_pos ; + guint32 cur_char = 0, next_char = 0 ; + GString * property = NULL ; + CRTerm *expr = NULL ; + CRSimpleSel * simple_sels = NULL ; + CRSelector *selector = NULL ; + gboolean start_selector = FALSE ; + + g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_parser_parse_selector (a_this, &selector) ; + CHECK_PARSING_STATUS (status, FALSE) ; + + READ_NEXT_CHAR (a_this, &cur_char) ; + + ENSURE_PARSING_COND_ERR + (a_this, cur_char == '{', + "while parsing rulset: current char should be '{'", + CR_SYNTAX_ERROR) ; + + + if (PRIVATE (a_this)->sac_handler + &&PRIVATE (a_this)->sac_handler->start_selector) + { + /* + *the selector if ref counted so that the parser's user + *can choose to keep it. + */ + if (selector) + { + cr_selector_ref (selector) ; + } + + PRIVATE (a_this)->sac_handler->start_selector + (PRIVATE (a_this)->sac_handler, selector) ; + start_selector = TRUE ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + PRIVATE (a_this)->state = TRY_PARSE_RULESET_STATE ; + + status = cr_parser_parse_declaration (a_this, &property, &expr) ; + + if (expr) + { + cr_term_ref (expr) ; + } + + if ( status == CR_OK + && PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->property) + { + PRIVATE (a_this)->sac_handler->property + (PRIVATE (a_this)->sac_handler, property, expr) ; + } + + if (status == CR_OK) + { + /* + *free the allocated + *'property' and 'term' before parsing + *next declarations. + */ + if (property) + { + g_string_free (property, TRUE) ; + property = NULL ; + } + + if (expr) + { + cr_term_unref (expr) ; + expr = NULL ; + } + } + + CHECK_PARSING_STATUS_ERR + (a_this, status, FALSE, + "while parsing ruleset: next construction should be a declaration", + CR_SYNTAX_ERROR) ; + + for (;;) + { + PEEK_NEXT_CHAR (a_this, &next_char) ; + if (next_char != ';') break ; + + /*consume the ';' char*/ + READ_NEXT_CHAR (a_this, &cur_char) ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_parser_parse_declaration (a_this, &property, + &expr) ; + + if (expr) + { + cr_term_ref (expr) ; + } + + if (status == CR_OK + && PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->property) + { + PRIVATE (a_this)->sac_handler->property + (PRIVATE (a_this)->sac_handler, + property, expr) ; + } + + if (property) + { + g_string_free (property, TRUE) ; + property = NULL ; + } + + if (expr) + { + cr_term_unref (expr) ; + expr = NULL ; + } + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + READ_NEXT_CHAR (a_this, &cur_char) ; + + ENSURE_PARSING_COND_ERR + (a_this, cur_char == '}', + "while parsing rulset: current char must be a '}'", + CR_SYNTAX_ERROR) ; + + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->end_selector) + { + PRIVATE (a_this)->sac_handler->end_selector + (PRIVATE (a_this)->sac_handler, selector) ; + start_selector = FALSE ; + } + + if (expr) + { + cr_term_unref (expr) ; + expr = NULL ; + } + + if (simple_sels) + { + cr_simple_sel_destroy (simple_sels) ; + simple_sels = NULL ; + } + + if (selector) + { + cr_selector_unref (selector) ; + selector = NULL ; + } + + cr_parser_clear_errors (a_this) ; + PRIVATE (a_this)->state = RULESET_PARSED_STATE ; + + return CR_OK ; + + error: + + if (start_selector == TRUE + && PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->error) + { + PRIVATE (a_this)->sac_handler->error + (PRIVATE (a_this)->sac_handler) ; + } + + if (expr) + { + cr_term_unref (expr) ; + expr = NULL ; + } + + if (simple_sels) + { + cr_simple_sel_destroy (simple_sels) ; + simple_sels = NULL ; + } + + if (property) + { + g_string_free (property, TRUE) ; + } + + if (selector) + { + cr_selector_unref (selector) ; + selector = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + + +/** + *Parses an 'import' declaration as defined in the css2 spec + *in appendix D.1: + * + *import ::= + *@import [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S* + * + *@param a_this the "this pointer" of the current instance + *of #CRParser. + * + *@param a_medium_list out parameter. A linked list of + *GString (see the doc of glib-2). + *Each GString is a string that contains + *a 'medium' declaration part of the successfully + *parsed 'import' declaration. + * + *@param a_import_string out parameter. + *A string that contains the 'import + *string". The import string can be either an uri (if it starts with + *the substring "uri(") or a any other css2 string. Note that + * *a_import_string must be initially set to NULL or else, this function + *will return CR_BAD_PARAM_ERROR. + * + *@return CR_OK upon sucessfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_parse_import (CRParser *a_this, GList ** a_media_list, + GString **a_import_string) +{ + enum CRStatus status = CR_OK ; + CRInputPos init_pos ; + guint32 cur_char = 0, next_char = 0 ; + GString *medium = NULL ; + + g_return_val_if_fail (a_this + && a_import_string + && (*a_import_string == NULL), + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + if (BYTE (a_this, 1, NULL) == '@' + && BYTE (a_this, 2, NULL) == 'i' + && BYTE (a_this, 3, NULL) == 'm' + && BYTE (a_this, 4, NULL) == 'p' + && BYTE (a_this, 5, NULL) == 'o' + && BYTE (a_this, 6, NULL) == 'r' + && BYTE (a_this, 7, NULL) == 't') + { + SKIP_CHARS (a_this, 7) ; + status = CR_OK ; + } + else + { + status = CR_PARSING_ERROR ; + goto error ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + PRIVATE (a_this)->state = TRY_PARSE_IMPORT_STATE ; + + PEEK_NEXT_CHAR (a_this, &next_char) ; + + if (next_char == '"' || next_char == '\'') + { + status = cr_parser_parse_string + (a_this, a_import_string) ; + + CHECK_PARSING_STATUS (status, FALSE) ; + } + else + { + status = cr_parser_parse_uri + (a_this, a_import_string) ; + + CHECK_PARSING_STATUS (status, FALSE) ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_parser_parse_ident (a_this, &medium) ; + + if (status == CR_OK && medium) + { + *a_media_list = g_list_append (*a_media_list, + medium) ; + medium = NULL ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + for (;status == CR_OK;) + { + if ( (status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, + &next_char)) != CR_OK) + { + if (status == CR_END_OF_INPUT_ERROR) + { + status = CR_OK ; + goto okay ; + } + goto error ; + } + + + if (next_char == ',') + { + READ_NEXT_CHAR (a_this, &cur_char) ; + } + else + { + break ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_parser_parse_ident (a_this, + &medium) ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + if ((status == CR_OK) && medium) + { + *a_media_list = g_list_append + (*a_media_list, medium) ; + + medium = NULL ; + } + + CHECK_PARSING_STATUS (status, FALSE) ; + + READ_NEXT_CHAR (a_this, &cur_char) ; + + ENSURE_PARSING_COND (cur_char == ';') ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + } + + okay: + cr_parser_clear_errors (a_this) ; + PRIVATE (a_this)->state = IMPORT_PARSED_STATE ; + + return CR_OK ; + + error: + + if (*a_media_list) + { + GList *cur = NULL ; + /* + *free each element of *a_media_list. + *Note that each element of *a_medium list *must* + *be a GString* or else, the code that is coming next + *will corrupt the memory and lead to hard to debug + *random crashes. + *This is where C++ and its compile time + *type checking mecanism (through STL containers) would + *have prevented us to go through this hassle. + */ + for (cur = *a_media_list; cur ; cur = cur->next) + { + if (cur->data) + { + g_string_free (cur->data, TRUE) ; + } + } + + g_list_free (*a_media_list) ; + *a_media_list = NULL ; + } + + if (*a_import_string) + { + g_string_free (*a_import_string, TRUE) ; + *a_import_string = NULL ; + } + + if (medium) + { + g_string_free (medium, TRUE) ; + medium = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + +/** + *Parses a 'media' declaration as specified in the css2 spec at + *appendix D.1: + * + *media ::= @media S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S* + * + *Note that this function calls the required sac handlers during the parsing + *to notify media productions. See #CRDocHandler to know the callback called + *during @media parsing. + *@param a_this the "this pointer" of the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_parse_media (CRParser *a_this) +{ + enum CRStatus status = CR_OK ; + CRInputPos init_pos ; + CRToken * token = NULL ; + guint32 next_char = 0, cur_char = 0 ; + GString * medium = NULL ; + GList *media_list = NULL ; + + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == MEDIA_SYM_TK) ; + + cr_token_destroy (token) ; + token = NULL ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token && token->type == IDENT_TK) ; + + medium = token->u.str ; + token->u.str = NULL ; + cr_token_destroy (token) ; + token = NULL ; + + if (medium) + { + media_list = g_list_append (media_list, medium) ; + medium = NULL ; + } + + for (;status == CR_OK;) + { + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + PEEK_NEXT_CHAR (a_this, &next_char) ; + + if (next_char == ',') + { + READ_NEXT_CHAR (a_this, &cur_char) ; + } + else + { + break ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_parser_parse_ident (a_this, &medium) ; + + CHECK_PARSING_STATUS (status, FALSE) ; + + if (medium) + { + media_list = g_list_append (media_list, + medium) ; + medium = NULL ; + } + } + + READ_NEXT_CHAR (a_this, &cur_char) ; + + ENSURE_PARSING_COND (cur_char == '{') ; + + /* + *call the SAC handler api here. + */ + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->start_media) + { + PRIVATE (a_this)->sac_handler->start_media + (PRIVATE (a_this)->sac_handler, media_list) ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + PRIVATE (a_this)->state = TRY_PARSE_MEDIA_STATE ; + + for (;status == CR_OK;) + { + status = cr_parser_parse_ruleset (a_this) ; + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + } + + READ_NEXT_CHAR (a_this, &cur_char) ; + + ENSURE_PARSING_COND (cur_char == '}') ; + + /* + *call the right SAC handler api here. + */ + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->end_media) + { + PRIVATE (a_this)->sac_handler->end_media + (PRIVATE (a_this)->sac_handler, + media_list) ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + /* + *Then, free the data structures passed to + *the last call to the SAC handler. + */ + if (medium) + { + g_string_free (medium, TRUE) ; + medium = NULL ; + } + + if (media_list) + { + GList *cur = NULL ; + + for (cur = media_list ; cur ; cur = cur->next) + { + g_string_free (cur->data, TRUE) ; + } + + g_list_free (media_list) ; + media_list = NULL ; + } + + + cr_parser_clear_errors (a_this) ; + PRIVATE (a_this)->state = MEDIA_PARSED_STATE ; + + return CR_OK ; + + error: + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + if (medium) + { + g_string_free (medium, TRUE) ; + medium = NULL ; + } + + if (media_list) + { + GList *cur = NULL ; + + for (cur = media_list ; cur ; cur = cur->next) + { + g_string_free (cur->data, TRUE) ; + } + + g_list_free (media_list) ; + media_list = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos); + + return status ; +} + +/** + *Parses '@page' rule as specified in the css2 spec in appendix D.1: + *page ::= PAGE_SYM S* IDENT? pseudo_page? S* + *'{' S* declaration [ ';' S* declaration ]* '}' S* + * + *This function also calls the relevant SAC handlers whenever it + *encounters a construction that must + *be reported to the calling application. + *@param a_this the "this pointer" of the current instance of #CRParser. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_parse_page (CRParser *a_this) +{ + enum CRStatus status = CR_OK ; + CRInputPos init_pos ; + CRToken * token = NULL ; + CRTerm * css_expression = NULL ; + GString *page_selector = NULL, + *page_pseudo_class = NULL, + *property = NULL ; + + g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == PAGE_SYM_TK) ; + + cr_token_destroy (token) ; + token = NULL ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK && token) ; + + if (token->type == IDENT_TK) + { + page_selector = token->u.str ; + token->u.str = NULL ; + cr_token_destroy (token) ; + token = NULL ; + } + else + { + cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, + token) ; + token = NULL ; + } + + /* + *try to parse pseudo_page + */ + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token) ; + + if (token->type == DELIM_TK && token->u.unichar == ':') + { + cr_token_destroy (token) ; + token = NULL ; + status = cr_parser_parse_ident (a_this, &page_pseudo_class) ; + CHECK_PARSING_STATUS (status, FALSE) ; + } + else + { + cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token) ; + token = NULL ; + } + + /* + *parse_block + * + */ + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == CBO_TK) ; + + cr_token_destroy (token) ; token = NULL ; + + /* + *Call the appropriate SAC handler here. + */ + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->start_page) + { + PRIVATE (a_this)->sac_handler->start_page + (PRIVATE (a_this)->sac_handler, + page_selector, page_pseudo_class) ; + } + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + PRIVATE (a_this)->state = TRY_PARSE_PAGE_STATE ; + + status = cr_parser_parse_declaration (a_this, &property, + &css_expression) ; + ENSURE_PARSING_COND (status == CR_OK) + + /* + *call the relevant SAC handler here... + */ + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->property) + { + if (css_expression) + cr_term_ref (css_expression) ; + + PRIVATE (a_this)->sac_handler->property + (PRIVATE (a_this)->sac_handler, + property, + css_expression) ; + } + + /* + *... and free the data structure passed to that last + *SAC handler. + */ + + if (property) + { + g_string_free (property, TRUE) ; + property = NULL ; + } + + if (css_expression) + { + cr_term_unref (css_expression) ; + css_expression = NULL ; + } + + for (;;) + { + /*parse the other ';' separated declarations*/ + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + status = cr_tknzr_get_next_token + (PRIVATE (a_this)->tknzr, &token) ; + + ENSURE_PARSING_COND (status == CR_OK && token) ; + + if (token->type != SEMICOLON_TK) break ; + + cr_token_destroy (token) ; + token = NULL ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_parser_parse_declaration (a_this, &property, + &css_expression) ; + CHECK_PARSING_STATUS (status, FALSE) ; + + /* + *call the relevant SAC handler here... + */ + + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->property) + { + cr_term_ref (css_expression) ; + PRIVATE (a_this)->sac_handler->property + (PRIVATE (a_this)->sac_handler, + property, + css_expression) ; + } + + /* + *... and free the data structure passed to that last + *SAC handler. + */ + + if (property) + { + g_string_free (property, TRUE) ; + property = NULL ; + } + + if (css_expression) + { + cr_term_unref (css_expression) ; + css_expression = NULL ; + } + } + + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == CBC_TK) ; + + cr_token_destroy (token) ; token = NULL ; + + + /* + *call the relevant SAC handler here. + */ + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->end_page) + { + PRIVATE (a_this)->sac_handler->end_page + (PRIVATE (a_this)->sac_handler, + page_selector, page_pseudo_class) ; + } + + if (page_selector) + { + g_string_free (page_selector, TRUE) ; + page_selector = NULL ; + } + + if (page_pseudo_class) + { + g_string_free (page_pseudo_class, TRUE) ; + page_pseudo_class = NULL ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + /*here goes the former implem of this function ...*/ + + cr_parser_clear_errors (a_this) ; + PRIVATE (a_this)->state = PAGE_PARSED_STATE ; + + return CR_OK ; + + error: + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + if (page_selector) + { + g_string_free (page_selector, TRUE) ; + page_selector = NULL ; + } + + if (page_pseudo_class) + { + g_string_free (page_pseudo_class, TRUE) ; + page_pseudo_class = NULL ; + } + + if (property) + { + g_string_free (property, TRUE) ; + property = NULL ; + } + + if (css_expression) + { + cr_term_destroy (css_expression) ; + css_expression = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + + +/** + *Parses a charset declaration as defined implictly by the css2 spec in + *appendix D.1: + *charset ::= CHARSET_SYM S* STRING S* ';' + * + *@param a_this the "this pointer" of the current instance of #CRParser. + *@param a_value out parameter. The actual parsed value of the charset + *declararation. Note that for safety check reasons, *a_value must be + *set to NULL. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_parse_charset (CRParser *a_this, GString **a_value) +{ + enum CRStatus status = CR_OK ; + CRInputPos init_pos ; + CRToken *token = NULL ; + GString *charset_str = NULL ; + + g_return_val_if_fail (a_this && a_value + && (*a_value == NULL), + CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + + ENSURE_PARSING_COND (status == CR_OK + && token && token->type == CHARSET_SYM_TK) ; + + cr_token_destroy (token) ; + token = NULL ; + + PRIVATE (a_this)->state = TRY_PARSE_CHARSET_STATE ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token && token->type == STRING_TK) ; + charset_str = token->u.str ; + token->u.str = NULL ; + cr_token_destroy (token) ; + token = NULL ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + + ENSURE_PARSING_COND (status == CR_OK + && token && token->type == SEMICOLON_TK) ; + cr_token_destroy (token) ; + token = NULL ; + + if (charset_str) + { + *a_value = charset_str ; + charset_str = NULL ; + } + + PRIVATE (a_this)->state = CHARSET_PARSED_STATE ; + return CR_OK ; + + error: + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + if (*a_value) + { + g_string_free (*a_value, TRUE) ; + *a_value = NULL ; + } + + if (charset_str) + { + g_string_free (charset_str, TRUE) ; + charset_str = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, + &init_pos) ; + + return status ; +} + + + + +/** + *Parses the "@font-face" rule specified in the css1 spec in + *appendix D.1: + * + *font_face ::= FONT_FACE_SYM S* + *'{' S* declaration [ ';' S* declaration ]* '}' S* + * + *This function will call SAC handlers whenever it is necessary. + *@return CR_OK upon successfull completion, an error code otherwise. + */ +enum CRStatus +cr_parser_parse_font_face (CRParser *a_this) +{ + enum CRStatus status = CR_ERROR ; + CRInputPos init_pos ; + GString *property = NULL ; + CRTerm * css_expression = NULL ; + CRToken *token = NULL ; + guint32 next_char = 0, cur_char = 0 ; + + g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; + + RECORD_INITIAL_POS (a_this, &init_pos) ; + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == FONT_FACE_SYM_TK) ; + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, + &token) ; + ENSURE_PARSING_COND (status == CR_OK + && token + && token->type == CBO_TK) ; + + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + /* + *here, call the relevant SAC handler. + */ + + if (PRIVATE (a_this)->sac_handler + && PRIVATE (a_this)->sac_handler->start_font_face) + { + PRIVATE (a_this)->sac_handler->start_font_face + (PRIVATE (a_this)->sac_handler) ; + } + + PRIVATE (a_this)->state = TRY_PARSE_FONT_FACE_STATE ; + + /* + *and resume the parsing. + */ + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + + status = cr_parser_parse_declaration (a_this, &property, + &css_expression) ; + + if (status == CR_OK) + { + /* + *here, call the relevant SAC handler. + */ + cr_term_ref (css_expression) ; + + if (PRIVATE (a_this)->sac_handler && + PRIVATE (a_this)->sac_handler->property) + { + PRIVATE (a_this)->sac_handler->property + (PRIVATE (a_this)->sac_handler, + property, css_expression) ; + } + ENSURE_PARSING_COND (css_expression && property) ; + } + + /*free the data structures allocated during last parsing.*/ + if (property) + { + g_string_free (property, TRUE) ; + property = NULL ; + } + + if (css_expression) + { + cr_term_unref (css_expression) ; + css_expression = NULL ; + } + + for (;;) + { + PEEK_NEXT_CHAR (a_this, &next_char) ; + + if (next_char == ';') + { + READ_NEXT_CHAR (a_this, &cur_char) ; + } + else + { + break ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + status = cr_parser_parse_declaration (a_this, &property, + &css_expression) ; + + if (status != CR_OK) break ; + + /* + *here, call the relevant SAC handler. + */ + cr_term_ref (css_expression) ; + + if (PRIVATE (a_this)->sac_handler->property) + { + PRIVATE (a_this)->sac_handler->property + (PRIVATE (a_this)->sac_handler, + property, css_expression) ; + } + + /* + *Then, free the data structures allocated during + *last parsing. + */ + if (property) + { + g_string_free (property, TRUE) ; + property = NULL ; + } + + if (css_expression) + { + cr_term_unref (css_expression) ; + css_expression = NULL ; + } + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + READ_NEXT_CHAR (a_this, &cur_char) ; + + ENSURE_PARSING_COND (cur_char == '}') ; + + /* + *here, call the relevant SAC handler. + */ + + if (PRIVATE (a_this)->sac_handler->end_font_face) + { + PRIVATE (a_this)->sac_handler->end_font_face + (PRIVATE (a_this)->sac_handler) ; + } + + cr_parser_try_to_skip_spaces_and_comments (a_this) ; + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + cr_parser_clear_errors (a_this) ; + + PRIVATE (a_this)->state = FONT_FACE_PARSED_STATE ; + + return CR_OK ; + + error: + + if (token) + { + cr_token_destroy (token) ; + token = NULL ; + } + + if (property) + { + g_string_free (property, TRUE) ; + property = NULL ; + } + + if (css_expression) + { + cr_term_destroy (css_expression) ; + css_expression = NULL ; + } + + cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos) ; + + return status ; +} + +/** + *Parses the data that comes from the + *input previously associated to the current instance of + *#CRParser. + *@param a_this the current instance of #CRParser. + *@return CR_OK ; + */ +enum CRStatus +cr_parser_parse (CRParser *a_this) +{ + enum CRStatus status = CR_ERROR ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && PRIVATE (a_this)->tknzr, + CR_BAD_PARAM_ERROR) ; + + if (PRIVATE (a_this)->use_core_grammar == FALSE) + { + status = cr_parser_parse_stylesheet (a_this) ; + } + else + { + status = cr_parser_parse_stylesheet_core (a_this) ; + } + + return status ; +} + + +enum CRStatus +cr_parser_set_tknzr (CRParser *a_this, CRTknzr *a_tknzr) +{ + g_return_val_if_fail (a_this && PRIVATE (a_this), + CR_BAD_PARAM_ERROR) ; + + if (PRIVATE (a_this)->tknzr) + { + cr_tknzr_unref (PRIVATE (a_this)->tknzr) ; + } + + PRIVATE (a_this)->tknzr = a_tknzr ; + + if (a_tknzr) + cr_tknzr_ref (a_tknzr) ; + + return CR_OK ; +} + + +enum CRStatus +cr_parser_parse_buf (CRParser *a_this, const guchar *a_buf, + gulong a_len, enum CREncoding a_enc) +{ + enum CRStatus status = CR_ERROR ; + CRTknzr *tknzr = NULL ; + + g_return_val_if_fail (a_this && PRIVATE (a_this) + && a_buf, + CR_BAD_PARAM_ERROR) ; + + tknzr = cr_tknzr_new_from_buf (a_buf,a_len, + a_enc, FALSE) ; + + g_return_val_if_fail (tknzr != NULL, CR_ERROR) ; + + status = cr_parser_set_tknzr (a_this, tknzr) ; + g_return_val_if_fail (status == CR_OK, CR_ERROR) ; + + status = cr_parser_parse (a_this) ; + + return status ; +} + + +/** + *Destroys the current instance + *of #CRParser. + *@param a_this the current instance of #CRParser to + *destroy. + */ +void +cr_parser_destroy (CRParser *a_this) +{ + g_return_if_fail (a_this && PRIVATE (a_this)) ; + + + if (PRIVATE (a_this)->tknzr) + { + if (cr_tknzr_unref (PRIVATE (a_this)->tknzr) == TRUE) + PRIVATE (a_this)->tknzr = NULL ; + } + + if (PRIVATE (a_this)->sac_handler) + { + cr_doc_handler_unref (PRIVATE (a_this)->sac_handler) ; + PRIVATE (a_this)->sac_handler = NULL ; + } + + if (PRIVATE (a_this)->err_stack) + { + cr_parser_clear_errors (a_this); + PRIVATE (a_this)->err_stack = NULL ; + } + + if (PRIVATE (a_this)) + { + g_free (PRIVATE (a_this)) ; + PRIVATE (a_this) = NULL ; + } + + if (a_this) + { + g_free (a_this) ; + a_this = NULL ;/*useless. Just for the sake of coherence*/ + } +} |