summaryrefslogtreecommitdiff
path: root/src/cr-tknzr.c
diff options
context:
space:
mode:
authorDodji Seketeli <dodji@gnome.org>2004-01-24 19:24:08 +0000
committerDodji Seketeli <dodji@src.gnome.org>2004-01-24 19:24:08 +0000
commitd058779c836ec6ac56721581dd44d1dd91d3206c (patch)
treef371ad9e5150f3526edd528e44f990e9d060014c /src/cr-tknzr.c
parenta97a943a32d37e1c92aa93e861a28d96f84f2163 (diff)
downloadlibcroco-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-tknzr.c')
-rw-r--r--src/cr-tknzr.c2851
1 files changed, 2851 insertions, 0 deletions
diff --git a/src/cr-tknzr.c b/src/cr-tknzr.c
new file mode 100644
index 0000000..4315460
--- /dev/null
+++ b/src/cr-tknzr.c
@@ -0,0 +1,2851 @@
+/* -*- 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 #CRTknzr (tokenizer)
+ *class.
+ */
+
+#include "string.h"
+#include "cr-tknzr.h"
+#include "cr-doc-handler.h"
+
+struct _CRTknzrPriv
+{
+ /**The parser input stream of bytes*/
+ CRInput *input ;
+
+ /**
+ *A cache where tknzr_unget_token()
+ *puts back the token. tknzr_get_next_token()
+ *first look in this cache, and if and
+ *only if it's empty, fetches the next token
+ *from the input stream.
+ */
+ CRToken *token_cache ;
+
+ /**
+ *The position of the end of the previous token
+ *or char fetched.
+ */
+ CRInputPos prev_pos ;
+
+ CRDocHandler *sac_handler ;
+
+ /**
+ *The reference count of the current instance
+ *of #CRTknzr. Is manipulated by cr_tknzr_ref()
+ *and cr_tknzr_unref().
+ */
+ glong ref_count ;
+} ;
+
+
+#define PRIVATE(obj) ((obj)->priv)
+
+/**
+ *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 ; \
+}
+
+
+/**
+ *Peeks the next char from the input stream of the current tokenizer.
+ *invokes CHECK_PARSING_STATUS on the status returned by
+ *cr_tknzr_input_peek_char().
+ *
+ *@param the current instance of #CRTkzr.
+ *@param to_char a pointer to the char where to store the
+ *char peeked.
+ */
+#define PEEK_NEXT_CHAR(a_tknzr, a_to_char) \
+{\
+status = cr_tknzr_peek_char (a_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 parser the curent instance of #CRTknzr
+ *@param to_char a pointer to the guint32 char where to store
+ *the character read.
+ */
+#define READ_NEXT_CHAR(a_tknzr, to_char) \
+status = cr_tknzr_read_char (a_tknzr, 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 parser the current instance of #CRTknzr.
+ *@param pos out parameter. A pointer to the position
+ *inside the current parser input. Must
+ */
+#define RECORD_INITIAL_POS(a_tknzr, a_pos) \
+status = cr_input_get_cur_pos (PRIVATE \
+(a_tknzr)->input, 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 #CRTknzr.
+ *@param addr out parameter a pointer (guchar*)
+ *to where the address must be put.
+ */
+#define RECORD_CUR_BYTE_ADDR(a_tknzr, a_addr) \
+status = cr_input_get_cur_byte_addr \
+ (PRIVATE (a_tknzr)->input, 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 #CRTknzr.
+ *@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_tknzr, a_offset, a_byte_ptr) \
+status = cr_tknzr_peek_byte (a_tknzr, \
+ a_offset, \
+ a_byte_ptr) ; \
+CHECK_PARSING_STATUS (status, TRUE) ;
+
+
+#define BYTE(a_input, a_n, a_eof) \
+cr_input_peek_byte2 (a_input, a_n, a_eof)
+
+/**
+ *Reads a byte from the topmost parser input
+ *steam.
+ *If it fails, goto the "error" label.
+ *@param a_parser the current instance of #CRTknzr.
+ *@param a_byte_ptr the guchar * where to put the read char.
+ */
+#define READ_NEXT_BYTE(a_tknzr, a_byte_ptr) \
+status = \
+cr_input_read_byte (PRIVATE (a_tknzr)->input, 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 #CRTknzr.
+ *@param a_nb_bytes the number of bytes to skip.
+ */
+#define SKIP_BYTES(a_tknzr, a_nb_bytes) \
+status = cr_input_seek_index (PRIVATE (a_tknzr)->input, \
+ 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 #CRTknzr.
+ *@param a_nb_chars the number of chars to skip. Must be of
+ *type glong.
+ */
+#define SKIP_CHARS(a_tknzr, a_nb_chars) \
+{ \
+glong nb_chars = a_nb_chars ; \
+status = cr_input_consume_chars \
+ (PRIVATE (a_tknzr)->input,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 ;}
+
+
+static enum CRStatus
+cr_tknzr_parse_nl (CRTknzr *a_this, guchar **a_start, guchar **a_end) ;
+
+static enum CRStatus
+cr_tknzr_parse_w (CRTknzr *a_this, guchar **a_start, guchar **a_end) ;
+
+static enum CRStatus
+cr_tknzr_parse_unicode_escape (CRTknzr *a_this,
+ guint32 *a_unicode) ;
+
+static enum CRStatus
+cr_tknzr_parse_escape (CRTknzr *a_this, guint32 *a_esc_code) ;
+
+
+static enum CRStatus
+cr_tknzr_parse_string (CRTknzr *a_this, GString **a_str) ;
+
+static enum CRStatus
+cr_tknzr_parse_comment (CRTknzr *a_this, GString **a_comment) ;
+
+static enum CRStatus
+cr_tknzr_parse_nmstart (CRTknzr *a_this, guint32 *a_char) ;
+
+static enum CRStatus
+cr_tknzr_parse_num (CRTknzr *a_this, CRNum ** a_num) ;
+
+/**********************************
+ *PRIVATE methods
+ **********************************/
+
+/**
+ *Parses a "w" as defined by the css spec at [4.1.1]:
+ * w ::= [ \t\r\n\f]*
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_start out param. Upon successfull completion, points
+ *to the beginning of the parsed white space, points to NULL otherwise.
+ *Can also point to NULL is there is no white space actually.
+ *@param a_end out param. Upon successfull completion, points
+ *to the end of the parsed white space, points to NULL otherwise.
+ *Can also point to NULL is there is no white space actually.
+ */
+static enum CRStatus
+cr_tknzr_parse_w (CRTknzr *a_this, guchar **a_start, guchar **a_end)
+{
+ guint32 cur_char = 0 ;
+ CRInputPos init_pos ;
+ enum CRStatus status = CR_OK ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_start && a_end,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ *a_start = NULL ;
+ *a_end = NULL ;
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ if (cr_utils_is_white_space (cur_char) == FALSE)
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ RECORD_CUR_BYTE_ADDR (a_this, a_start) ;
+ *a_end = *a_start ;
+
+ for (;;)
+ {
+ gboolean is_eof = FALSE ;
+
+ cr_input_get_end_of_file (PRIVATE (a_this)->input,
+ &is_eof) ;
+ if (is_eof)
+ break ;
+
+ status = cr_tknzr_peek_char (a_this, &cur_char) ;
+ if (status == CR_END_OF_INPUT_ERROR)
+ {
+ status = CR_OK ;
+ break ;
+ }
+ else if (status != CR_OK)
+ {
+ goto error ;
+ }
+
+ if (cr_utils_is_white_space (cur_char) == TRUE)
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ RECORD_CUR_BYTE_ADDR (a_this, a_end) ;
+ }
+ else
+ {
+ break ;
+ }
+ }
+
+ return CR_OK ;
+
+ error:
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return status ;
+}
+
+
+/**
+ *Parses a newline as defined in the css2 spec:
+ * nl ::= \n|\r\n|\r|\f
+ *
+ *@param a_this the "this pointer" of the current instance of #CRTknzr.
+ *@param a_start a pointer to the first character of the successfully
+ *parsed string.
+ *@param a_end a pointer to the last character of the successfully parsed
+ *string.
+ *@result CR_OK uppon successfull completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_nl (CRTknzr *a_this, guchar **a_start, guchar **a_end)
+{
+ CRInputPos init_pos ;
+ guchar next_chars[2] = {0} ;
+ enum CRStatus status = CR_PARSING_ERROR ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_start && a_end,
+ 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] == '\r' && next_chars[1] == '\n'))
+ {
+ SKIP_BYTES (a_this, 1) ;
+ SKIP_CHARS (a_this, 1) ;
+
+ RECORD_CUR_BYTE_ADDR (a_this, a_end) ;
+
+ status = CR_OK ;
+ }
+ else if (next_chars[0] == '\n'
+ || next_chars[0] == '\r'
+ || next_chars[0] == '\f')
+ {
+ SKIP_CHARS (a_this, 1) ;
+
+ RECORD_CUR_BYTE_ADDR (a_this, a_start) ;
+ *a_end = *a_start ;
+ status = CR_OK ;
+ }
+ else
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ return CR_OK ;
+
+ error:
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return status ;
+}
+
+
+/**
+ *Go ahead in the parser input, skipping all the spaces.
+ *If the next char if not a white space, this function does nothing.
+ *In any cases, it stops when it encounters a non white space character.
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *@return CR_OK upon successfull completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_try_to_skip_spaces (CRTknzr *a_this)
+{
+ enum CRStatus status = CR_ERROR ;
+ guint32 cur_char = 0 ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ status = cr_input_peek_char
+ (PRIVATE (a_this)->input, &cur_char) ;
+
+ if (status != CR_OK)
+ {
+ if (status == CR_END_OF_INPUT_ERROR) return CR_OK ;
+ return status ;
+ }
+
+ if (cr_utils_is_white_space (cur_char) == TRUE)
+ {
+ glong nb_chars = -1; /*consume all spaces*/
+
+ status = cr_input_consume_white_spaces
+ (PRIVATE (a_this)->input, &nb_chars) ;
+ }
+
+ return status ;
+}
+
+
+/**
+ *Parses a "comment" as defined in the css spec at [4.1.1]:
+ *COMMENT ::= \/\*[^*]*\*+([^/][^*]*\*+)*\/ .
+ *This complex regexp is just to say that comments start
+ *with the two chars '/''*' and ends with the two chars '*''/'.
+ *It also means that comments cannot be nested.
+ *So based on that, I've just tried to implement the parsing function
+ *simply and in a straight forward manner.
+ */
+static enum CRStatus
+cr_tknzr_parse_comment (CRTknzr *a_this, GString **a_comment)
+{
+ enum CRStatus status = CR_OK ;
+ CRInputPos init_pos ;
+ guint32 cur_char = 0 ;
+ GString *comment = NULL ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ ENSURE_PARSING_COND (cur_char == '/') ;
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ ENSURE_PARSING_COND (cur_char == '*') ;
+
+ comment = g_string_new (NULL) ;
+
+ for (;;)
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ /*make sure there are no nested comments*/
+ if (cur_char == '/') {
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ ENSURE_PARSING_COND (cur_char != '*') ;
+
+ g_string_append_c (comment, '/') ;
+ g_string_append_unichar (comment, cur_char) ;
+
+ continue ;
+ }
+
+ /*Detect the end of the comments region*/
+ if (cur_char == '*')
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ if (cur_char == '/')
+ {
+ /*
+ *end of comments region
+ *Now, call the right SAC callback.
+ */
+ status = CR_OK ;
+ break ;
+ }
+ else
+ {
+ g_string_append_c (comment, '*') ;
+
+ }
+ }
+
+ g_string_append_unichar (comment, cur_char) ;
+ }
+
+ if (status == CR_OK)
+ {
+ *a_comment = comment ;
+
+ return CR_OK ;
+ }
+
+ error:
+
+ if (comment)
+ {
+ g_string_free (comment, TRUE) ;
+ comment = NULL ;
+ }
+
+ cr_tknzr_set_cur_pos (a_this, &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 #CRTknzr.
+ *@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_tknzr_parse_unicode_escape (CRTknzr *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 ;
+ }
+
+ PEEK_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 ;
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ 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 ;
+
+ PEEK_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_tknzr_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_tknzr_parse_w (a_this, &tmp_char_ptr1,
+ &tmp_char_ptr2) ;
+ }
+
+ if (status == CR_OK)
+ {
+ *a_unicode = unicode ;
+ return CR_OK ;
+ }
+
+ error:
+ /*
+ *restore the initial position pointer backuped at
+ *the beginning of this function.
+ */
+ cr_tknzr_set_cur_pos (a_this, &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 #CRTknzr .
+ */
+static enum CRStatus
+cr_tknzr_parse_escape (CRTknzr *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 ;
+ goto error ;
+ }
+
+ if ((next_chars[1] >= '0' && next_chars[1] <= '9')
+ || (next_chars[1] >= 'a' && next_chars[1] <='f')
+ || (next_chars[1] >= 'A' && next_chars[1] <='F'))
+ {
+ status =
+ cr_tknzr_parse_unicode_escape (a_this,
+ a_esc_code);
+ }
+ 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 ;
+ goto error ;
+ }
+
+ *a_esc_code = cur_char ;
+
+ }
+
+ if (status == CR_OK)
+ {
+ return CR_OK ;
+ }
+
+ error:
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ 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 #CRTknzr.
+ *@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_tknzr_parse_string (CRTknzr *a_this, GString **a_str)
+{
+ guint32 cur_char = 0, delim = 0 ;
+ CRInputPos init_pos ;
+ enum CRStatus status = CR_OK ;
+ GString *str = NULL ;
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_str,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ if (cur_char == '"') delim = '"' ;
+ else if (cur_char == '\'') delim = '\'' ;
+ else
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ str = g_string_new (NULL) ;
+
+ for (;;)
+ {
+ guchar next_chars[2] = {0} ;
+
+ PEEK_BYTE (a_this, 1, &next_chars[0]) ;
+ PEEK_BYTE (a_this, 2, &next_chars[1]) ;
+
+ if (next_chars[0] == '\\')
+ {
+ guchar *tmp_char_ptr1 = NULL,
+ *tmp_char_ptr2 = NULL ;
+ guint32 esc_code = 0 ;
+
+ if (next_chars[1] == '\''
+ || next_chars[1] == '"')
+ {
+ g_string_append_unichar (str,
+ next_chars[1]) ;
+ SKIP_BYTES (a_this, 2) ;
+ status = CR_OK ;
+ }
+ else
+ {
+ status = cr_tknzr_parse_escape
+ (a_this, &esc_code) ;
+
+ if (status == CR_OK)
+ {
+ g_string_append_unichar
+ (str, esc_code);
+ }
+ }
+
+ if (status != CR_OK)
+ {
+ /*
+ *consume the '\' char, and try to parse
+ *a newline.
+ */
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ status = cr_tknzr_parse_nl
+ (a_this,&tmp_char_ptr1,
+ &tmp_char_ptr2) ;
+ }
+
+ CHECK_PARSING_STATUS (status, FALSE) ;
+ }
+ else if (strchr ("\t !#$%&", next_chars[0])
+ || (next_chars[0] >= '(' && next_chars[0] <= '~'))
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ g_string_append_unichar (str, cur_char) ;
+ status = CR_OK ;
+ }
+
+ else if (cr_utils_is_nonascii (next_chars[0]))
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ g_string_append_unichar (str, cur_char) ;
+ }
+ else if (next_chars[0] == delim)
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ break ;
+ }
+ else
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+ }
+
+ if (status == CR_OK)
+ {
+ if (*a_str == NULL)
+ {
+ *a_str = str ;
+ str = NULL ;
+ }
+ else
+ {
+ *a_str = g_string_append_len (*a_str,
+ str->str,
+ str->len) ;
+ g_string_free (str, TRUE) ;
+ }
+ return CR_OK ;
+ }
+
+ error:
+
+ if (str)
+ {
+ g_string_free (str, TRUE) ;
+ str = NULL ;
+ }
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return status ;
+}
+
+/**
+ *Parses the an nmstart as defined by css spec [4.1.1]:
+ * nmstart [a-zA-Z]|{nonascii}|{escape}
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_start out param. A pointer to the starting point of
+ *the token.
+ *@param a_end out param. A pointer to the ending point of the
+ *token.
+ *@param a_char out param. The actual parsed nmchar.
+ *@return CR_OK upon successfull completion,
+ *an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_nmstart (CRTknzr *a_this, guint32 *a_char)
+{
+ CRInputPos init_pos ;
+ enum CRStatus status = CR_OK ;
+ guint32 cur_char = 0, next_char = 0 ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_char,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ PEEK_NEXT_CHAR (a_this, &next_char) ;
+
+ if (next_char == '\\')
+ {
+ status =
+ cr_tknzr_parse_escape (a_this, a_char) ;
+
+ if (status != CR_OK) goto error ;
+
+ }
+ else if (cr_utils_is_nonascii (next_char) == TRUE
+ || ((next_char >= 'a') && (next_char <= 'z'))
+ || ((next_char >= 'A') && (next_char <= 'Z'))
+ )
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ *a_char = cur_char ;
+ status = CR_OK ;
+ }
+ else
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ return CR_OK ;
+
+ error:
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return status ;
+
+}
+
+/**
+ *Parses an nmchar as described in the css spec at
+ *chap 4.1.1:
+ *nmchar ::= [a-z0-9-]|{nonascii}|{escape}
+ *
+ *Humm, I have added the possibility for nmchar to
+ *contain upper case letters.
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_start out param. A pointer to the starting point of
+ *the token.
+ *@param a_end out param. A pointer to the ending point of the
+ *token.
+ *@param a_char out param. The actual parsed nmchar.
+ *@return CR_OK upon successfull completion,
+ *an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_nmchar (CRTknzr *a_this, guint32 *a_char)
+{
+ guint32 cur_char = 0, next_char = 0 ;
+ enum CRStatus status = CR_OK ;
+ CRInputPos init_pos ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_char,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ status =
+ cr_input_peek_char (PRIVATE (a_this)->input,
+ &next_char) ;
+
+ if (status != CR_OK) goto error ;
+
+ if (next_char == '\\')
+ {
+ status =
+ cr_tknzr_parse_escape (a_this, a_char) ;
+
+ if (status != CR_OK) goto error ;
+
+ }
+ else if (cr_utils_is_nonascii (next_char) == TRUE
+ || ((next_char >= 'a') && (next_char <= 'z'))
+ || ((next_char >= 'A') && (next_char <= 'Z'))
+ || ((next_char >= '0') && (next_char <= '9'))
+ || (next_char == '-')
+ || (next_char == '_') /*'_' not allowed by the spec.*/
+ )
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ *a_char = cur_char ;
+ status = CR_OK ;
+ }
+ else
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ return CR_OK ;
+
+ error:
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return status ;
+}
+
+
+/**
+ *Parses an "ident" as defined in css spec [4.1.1]:
+ *ident ::= {nmstart}{nmchar}*
+ *
+ *@param a_this the currens instance of #CRTknzr.
+ *
+ *@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_tknzr_parse_ident (CRTknzr *a_this, GString **a_str)
+{
+ guint32 tmp_char = 0 ;
+ CRInputPos init_pos ;
+ enum CRStatus status = CR_OK ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_str,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ status = cr_tknzr_parse_nmstart (a_this, &tmp_char) ;
+
+ if (status != CR_OK)
+ return CR_PARSING_ERROR ;
+
+ if (*a_str == NULL)
+ {
+ *a_str = g_string_new (NULL) ;
+ }
+
+ g_string_append_unichar (*a_str, tmp_char) ;
+
+ for (;;)
+ {
+ status =
+ cr_tknzr_parse_nmchar (a_this, &tmp_char);
+
+ if (status != CR_OK) break ;
+
+ g_string_append_unichar (*a_str, tmp_char) ;
+ }
+
+ return CR_OK ;
+}
+
+/**
+ *Parses a "name" as defined by css spec [4.1.1]:
+ *name ::= {nmchar}+
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *
+ *@param a_str out parameter. A pointer to the successfully parsed
+ *name. If *a_str is set to NULL, this function allocates a new instance
+ *of GString. If not, it just appends the parsed name to the passed *a_str.
+ *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_tknzr_parse_name (CRTknzr *a_this, GString **a_str)
+{
+ guint32 tmp_char = 0 ;
+ CRInputPos init_pos ;
+ enum CRStatus status = CR_OK ;
+ gboolean str_needs_free = FALSE ;
+ glong i = 0 ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_str, CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ if (*a_str == NULL)
+ {
+ *a_str = g_string_new (NULL) ;
+ str_needs_free = TRUE ;
+ }
+
+ for (i = 0 ; ; i++)
+ {
+ status = cr_tknzr_parse_nmchar (a_this, &tmp_char) ;
+
+ if (status != CR_OK) break ;
+
+ g_string_append_unichar (*a_str, tmp_char) ;
+ }
+
+ if (i > 0)
+ {
+ return CR_OK ;
+ }
+
+ if (str_needs_free == TRUE && *a_str)
+ {
+ g_string_free (*a_str, TRUE) ;
+ *a_str= NULL ;
+ }
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return CR_PARSING_ERROR ;
+}
+
+/**
+ *Parses a "hash" as defined by the css spec in [4.1.1]:
+ *HASH ::= #{name}
+ */
+static enum CRStatus
+cr_tknzr_parse_hash (CRTknzr *a_this, GString **a_str)
+{
+ guint32 cur_char = 0 ;
+ CRInputPos init_pos ;
+ enum CRStatus status = CR_OK ;
+ gboolean str_needs_free = FALSE ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ if (cur_char != '#')
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ if (*a_str == NULL)
+ {
+ *a_str = g_string_new (NULL) ;
+ str_needs_free = TRUE ;
+ }
+
+ status = cr_tknzr_parse_name (a_this, a_str) ;
+
+ if (status != CR_OK)
+ {
+ goto error ;
+ }
+
+ return CR_OK ;
+
+ error:
+
+ if (str_needs_free == TRUE && *a_str)
+ {
+ g_string_free (*a_str, TRUE) ;
+ *a_str = NULL ;
+ }
+
+ cr_tknzr_set_cur_pos (a_this, &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 #CRTknzr.
+ *@param a_str the successfully parsed url.
+ *@return CR_OK upon successfull completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_uri (CRTknzr *a_this, GString **a_str)
+{
+ guint32 cur_char = 0 ;
+ CRInputPos init_pos ;
+ enum CRStatus status = CR_PARSING_ERROR ;
+ guchar tab[4] = {0}, *tmp_ptr1 = NULL, *tmp_ptr2 = NULL ;
+ GString *str = NULL ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ PEEK_BYTE (a_this, 1, &tab[0]) ;
+ PEEK_BYTE (a_this, 2, &tab[1]) ;
+ PEEK_BYTE (a_this, 3, &tab[2]) ;
+ PEEK_BYTE (a_this, 4, &tab[3]) ;
+
+ if (tab[0] != 'u'
+ || tab[1] != 'r'
+ || tab[2] != 'l'
+ || tab[3] != '(')
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ SKIP_CHARS (a_this, 4) ;
+
+ cr_tknzr_try_to_skip_spaces (a_this) ;
+
+ status = cr_tknzr_parse_string (a_this, a_str) ;
+
+ if (status == CR_OK)
+ {
+ guint32 next_char = 0 ;
+
+ status = cr_tknzr_parse_w (a_this, &tmp_ptr1,
+ &tmp_ptr2) ;
+
+ cr_tknzr_try_to_skip_spaces (a_this) ;
+
+ PEEK_NEXT_CHAR (a_this, &next_char) ;
+
+ if (next_char == ')')
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ status = CR_OK ;
+ }
+ else
+ {
+ status = CR_PARSING_ERROR ;
+ }
+ }
+
+
+ if (status != CR_OK)
+ {
+ str = g_string_new (NULL) ;
+
+ for (;;)
+ {
+ guint32 next_char = 0 ;
+
+ PEEK_NEXT_CHAR (a_this, &next_char) ;
+
+ if (strchr ("!#$%&", next_char)
+ || (next_char >= '*' && next_char <= '~')
+ || (cr_utils_is_nonascii (next_char) == TRUE))
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ g_string_append_unichar (str,
+ cur_char);
+ status = CR_OK ;
+ }
+ else
+ {
+ guint32 esc_code = 0 ;
+
+ status = cr_tknzr_parse_escape
+ (a_this,
+ &esc_code) ;
+
+ if (status == CR_OK)
+ {
+ g_string_append_unichar
+ (str, esc_code) ;
+ }
+ else
+ {
+ status = CR_OK ;
+ break ;
+ }
+ }
+ }
+
+ cr_tknzr_try_to_skip_spaces (a_this) ;
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ if (cur_char == ')')
+ {
+ status = CR_OK ;
+ }
+ else
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+ if (str)
+ {
+ if (*a_str == NULL)
+ {
+ *a_str = str ;
+ str = NULL ;
+ }
+ else
+ {
+ g_string_append_len (*a_str,
+ str->str,
+ str->len) ;
+ g_string_free (str, TRUE) ;
+ }
+ }
+ }
+
+ return CR_OK ;
+
+ error:
+
+ if (str)
+ {
+ g_string_free (str, TRUE) ;
+ str = NULL ;
+ }
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return status ;
+}
+
+/**
+ *parses an RGB as defined in the css2 spec.
+ *rgb: rgb '('S*{num}%?S* ',' {num}#?S*,S*{num}#?S*')'
+ *
+ *@param a_this the "this pointer" of the current instance of
+ *@param a_rgb out parameter the parsed rgb.
+ *@return CR_OK upon successfull completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_rgb (CRTknzr *a_this, CRRgb **a_rgb)
+{
+ enum CRStatus status = CR_OK ;
+ CRInputPos init_pos ;
+ CRNum * num = NULL ;
+ guchar next_bytes[3] = {0}, cur_byte = 0 ;
+ glong red = 0, green = 0, blue = 0, i = 0 ;
+ gboolean is_percentage = FALSE ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this),
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ PEEK_BYTE (a_this, 1, &next_bytes[0]) ;
+ PEEK_BYTE (a_this, 2, &next_bytes[1]) ;
+ PEEK_BYTE (a_this, 3, &next_bytes[2]) ;
+
+ if (((next_bytes[0] == 'r') || (next_bytes[0] == 'R'))
+ && ((next_bytes[1] == 'g') || (next_bytes[1] == 'G'))
+ && ((next_bytes[2] == 'b') || (next_bytes[2] == 'B')))
+ {
+ SKIP_CHARS (a_this, 3) ;
+ }
+ else
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ READ_NEXT_BYTE (a_this, &cur_byte) ;
+
+ ENSURE_PARSING_COND (cur_byte == '(') ;
+
+ cr_tknzr_try_to_skip_spaces (a_this) ;
+
+ status = cr_tknzr_parse_num (a_this, &num) ;
+
+ ENSURE_PARSING_COND
+ ((status == CR_OK) && (num!= NULL)) ;
+
+ red = num->val ;
+
+ cr_num_destroy (num) ;
+ num = NULL ;
+
+ PEEK_BYTE (a_this, 1, &next_bytes[0]) ;
+
+ if (next_bytes[0] == '%')
+ {
+ SKIP_CHARS (a_this, 1) ;
+ is_percentage = TRUE ;
+ }
+
+ cr_tknzr_try_to_skip_spaces (a_this) ;
+
+ for (i = 0 ; i < 2 ; i++)
+ {
+ READ_NEXT_BYTE (a_this, &cur_byte) ;
+
+ ENSURE_PARSING_COND (cur_byte == ',') ;
+
+ cr_tknzr_try_to_skip_spaces (a_this) ;
+
+ status = cr_tknzr_parse_num (a_this, &num) ;
+
+ ENSURE_PARSING_COND
+ ((status == CR_OK)&&(num != NULL)) ;
+
+ PEEK_BYTE (a_this, 1, &next_bytes[0]) ;
+
+ if (next_bytes[0] == '%')
+ {
+ SKIP_CHARS (a_this, 1) ;
+ is_percentage = TRUE ;
+ }
+
+ if (i == 0)
+ {
+ green = num->val ;
+ }
+ else if (i == 1)
+ {
+ blue = num->val ;
+ }
+
+ if (num)
+ {
+ cr_num_destroy (num) ;
+ num = NULL ;
+ }
+
+ cr_tknzr_try_to_skip_spaces (a_this) ;
+ }
+
+ READ_NEXT_BYTE (a_this, &cur_byte) ;
+
+ if (*a_rgb == NULL)
+ {
+ *a_rgb =
+ cr_rgb_new_with_vals (red, green, blue,
+ is_percentage) ;
+
+ if (*a_rgb == NULL)
+ {
+ status = CR_ERROR ;
+ goto error ;
+ }
+ status = CR_OK ;
+ }
+ else
+ {
+ (*a_rgb)->red = red ;
+ (*a_rgb)->green = green ;
+ (*a_rgb)->blue = blue ;
+ (*a_rgb)->is_percentage = is_percentage ;
+
+ status = CR_OK ;
+ }
+
+ if (status == CR_OK)
+ {
+ return CR_OK ;
+ }
+
+ error:
+
+ if (num)
+ {
+ cr_num_destroy (num) ;
+ num = NULL ;
+ }
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return CR_OK ;
+}
+
+/**
+ *Parses a atkeyword as defined by the css spec in [4.1.1]:
+ *ATKEYWORD ::= @{ident}
+ *
+ *@param a_this the "this pointer" of the current instance of
+ *#CRTknzr.
+ *
+ *@param a_str out parameter. The parsed atkeyword. If *a_str is
+ *set to NULL this function allocates a new instance of GString and
+ *sets it to the parsed atkeyword. If not, this function just appends
+ *the parsed atkeyword to the end of *a_str. 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_tknzr_parse_atkeyword (CRTknzr *a_this, GString **a_str)
+{
+ guint32 cur_char = 0 ;
+ CRInputPos init_pos ;
+ gboolean str_needs_free = FALSE ;
+ enum CRStatus status = CR_OK ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_str,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ if (cur_char != '@')
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ if (*a_str == NULL)
+ {
+ *a_str = g_string_new (NULL) ;
+ str_needs_free = TRUE ;
+ }
+
+ status = cr_tknzr_parse_ident (a_this, a_str) ;
+
+ if (status != CR_OK)
+ {
+ goto error ;
+ }
+
+ return CR_OK ;
+
+ error:
+
+ if (str_needs_free == TRUE && *a_str)
+ {
+ g_string_free (*a_str, TRUE) ;
+ *a_str = NULL ;
+ }
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return status ;
+}
+
+
+
+static enum CRStatus
+cr_tknzr_parse_important (CRTknzr *a_this)
+{
+ guint32 cur_char = 0 ;
+ CRInputPos init_pos ;
+ enum CRStatus status = CR_OK ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ ENSURE_PARSING_COND (cur_char == '!') ;
+
+ cr_tknzr_try_to_skip_spaces (a_this) ;
+
+ if (BYTE (PRIVATE (a_this)->input, 1, NULL) == 'i'
+ && BYTE (PRIVATE (a_this)->input, 2, NULL) == 'm'
+ && BYTE (PRIVATE (a_this)->input, 3, NULL) == 'p'
+ && BYTE (PRIVATE (a_this)->input, 4, NULL) == 'o'
+ && BYTE (PRIVATE (a_this)->input, 5, NULL) == 'r'
+ && BYTE (PRIVATE (a_this)->input, 6, NULL) == 't'
+ && BYTE (PRIVATE (a_this)->input, 7, NULL) == 'a'
+ && BYTE (PRIVATE (a_this)->input, 8, NULL) == 'n'
+ && BYTE (PRIVATE (a_this)->input, 9, NULL) == 't')
+ {
+ SKIP_BYTES (a_this, 9) ;
+ return CR_OK ;
+ }
+ else
+ {
+ status = CR_PARSING_ERROR ;
+ }
+
+ error:
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return status ;
+}
+
+
+/**
+ *Parses a num as defined in the css spec [4.1.1]:
+ *[0-9]+|[0-9]*\.[0-9]+
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_num out parameter. The parsed number.
+ *@return CR_OK upon successfull completion,
+ *an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_num (CRTknzr *a_this, CRNum ** a_num)
+{
+ enum CRStatus status = CR_PARSING_ERROR ;
+ enum CRNumType val_type = NUM_GENERIC ;
+ gboolean parsing_dec = FALSE, parsed = FALSE ;
+ guint32 cur_char = 0, int_part = 0, dec_part = 0,
+ next_char = 0 ;
+ CRInputPos init_pos ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+
+ if (IS_NUM (cur_char) == TRUE)
+ {
+ int_part = int_part * 10
+ + (cur_char - '0') ;
+
+ parsed = TRUE ;
+ }
+ else if (cur_char == '.')
+ {
+ parsing_dec = TRUE ;
+ }
+ else
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ for (;;)
+ {
+ PEEK_NEXT_CHAR (a_this, &next_char) ;
+
+ if (next_char == '.')
+ {
+ if (parsing_dec == TRUE)
+ {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ parsing_dec = TRUE ;
+ parsed = TRUE ;
+ }
+ else if (IS_NUM (next_char) == TRUE)
+ {
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ parsed = TRUE ;
+
+ if (parsing_dec == FALSE)
+ {
+ int_part = int_part * 10
+ + (cur_char - '0') ;
+ }
+ else
+ {
+ dec_part = dec_part *10
+ + (cur_char - '0') ;
+ }
+ }
+ else
+ {
+ break ;
+ }
+ }
+
+ if (parsed == FALSE)
+ {
+ status = CR_PARSING_ERROR ;
+ }
+
+ /*
+ *Now, set the output param values.
+ */
+ if (status == CR_OK)
+ {
+ gdouble val = 0.0 ;
+
+ val = int_part ;
+ val += cr_utils_n_to_0_dot_n (dec_part) ;
+ if (*a_num == NULL)
+ {
+ *a_num = cr_num_new_with_val (val, val_type) ;
+
+ if (*a_num == NULL)
+ {
+ status = CR_ERROR ;
+ goto error ;
+ }
+ }
+ else
+ {
+ (*a_num)->val = val ;
+ (*a_num)->type = val_type ;
+ }
+
+ return CR_OK ;
+ }
+
+ error:
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+
+ return status ;
+}
+
+
+/*********************************************
+ *PUBLIC methods
+ ********************************************/
+
+CRTknzr *
+cr_tknzr_new (CRInput *a_input)
+{
+ CRTknzr * result = NULL ;
+
+ result = g_try_malloc (sizeof (CRTknzr)) ;
+
+ if (result == NULL)
+ {
+ cr_utils_trace_info ("Out of memory") ;
+ return NULL ;
+ }
+
+ memset (result, 0, sizeof (CRTknzr)) ;
+
+ result->priv = g_try_malloc (sizeof (CRTknzrPriv)) ;
+
+ if (result->priv == NULL)
+ {
+ cr_utils_trace_info ("Out of memory") ;
+
+ if (result)
+ {
+ g_free (result) ;
+ result = NULL ;
+ }
+
+ return NULL ;
+ }
+
+ memset (result->priv, 0, sizeof (CRTknzrPriv)) ;
+
+ if (a_input)
+ cr_tknzr_set_input (result, a_input) ;
+
+ return result ;
+}
+
+
+CRTknzr *
+cr_tknzr_new_from_buf (const guchar *a_buf, gulong a_len,
+ enum CREncoding a_enc,
+ gboolean a_free_at_destroy)
+{
+ CRTknzr * result = NULL ;
+ CRInput *input = NULL ;
+
+ input = cr_input_new_from_buf (a_buf, a_len, a_enc,
+ a_free_at_destroy) ;
+
+ g_return_val_if_fail (input != NULL, NULL) ;
+
+ result = cr_tknzr_new (input) ;
+
+ return result ;
+}
+
+CRTknzr *
+cr_tknzr_new_from_uri (const guchar *a_file_uri,
+ enum CREncoding a_enc)
+{
+ CRTknzr * result = NULL ;
+ CRInput *input = NULL ;
+
+ input = cr_input_new_from_uri (a_file_uri, a_enc) ;
+ g_return_val_if_fail (input != NULL, NULL) ;
+
+ result = cr_tknzr_new (input) ;
+
+ return result ;
+}
+
+void
+cr_tknzr_ref (CRTknzr *a_this)
+{
+ g_return_if_fail (a_this && PRIVATE (a_this)) ;
+
+ PRIVATE (a_this)->ref_count ++ ;
+}
+
+gboolean
+cr_tknzr_unref (CRTknzr *a_this)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), FALSE) ;
+
+ if (PRIVATE (a_this)->ref_count > 0)
+ {
+ PRIVATE (a_this)->ref_count -- ;
+ }
+
+ if (PRIVATE (a_this)->ref_count == 0)
+ {
+ cr_tknzr_destroy (a_this) ;
+ return TRUE ;
+ }
+
+ return FALSE ;
+}
+
+enum CRStatus
+cr_tknzr_set_input (CRTknzr *a_this, CRInput *a_input)
+{
+ g_return_val_if_fail (a_this
+ && PRIVATE (a_this),
+ CR_BAD_PARAM_ERROR) ;
+
+ if (PRIVATE (a_this)->input)
+ {
+ cr_input_unref (PRIVATE (a_this)->input) ;
+ }
+
+ PRIVATE (a_this)->input = a_input ;
+
+ cr_input_ref (PRIVATE (a_this)->input) ;
+
+ return CR_OK ;
+}
+
+
+enum CRStatus
+cr_tknzr_get_input (CRTknzr *a_this, CRInput **a_input)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this),
+ CR_BAD_PARAM_ERROR) ;
+
+ *a_input = PRIVATE (a_this)->input ;
+
+ return CR_OK ;
+}
+
+
+/*********************************
+ *Tokenizer input handling routines
+ *********************************/
+
+/**
+ *Reads the next byte from the parser input stream.
+ *@param a_this the "this pointer" of the current instance of
+ *#CRParser.
+ *@param a_byte out parameter the place where to store the byte
+ *read.
+ *@return CR_OK upon successfull completion, an error
+ *code otherwise.
+ */
+enum CRStatus
+cr_tknzr_read_byte (CRTknzr *a_this, guchar *a_byte)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this),
+ CR_BAD_PARAM_ERROR) ;
+
+ return cr_input_read_byte (PRIVATE (a_this)->input,
+ a_byte) ;
+
+}
+
+
+/**
+ *Reads the next char from the parser input stream.
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_char out parameter. The read char.
+ *@return CR_OK upon successfull completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_tknzr_read_char (CRTknzr *a_this, guint32 *a_char)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_char,
+ CR_BAD_PARAM_ERROR) ;
+
+ if (PRIVATE (a_this)->token_cache)
+ {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos) ;
+ cr_token_destroy (PRIVATE (a_this)->token_cache) ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ }
+
+ return cr_input_read_char (PRIVATE (a_this)->input,
+ a_char) ;
+}
+
+/**
+ *Peeks a char from the parser input stream.
+ *To "peek a char" means reads the next char without consuming it.
+ *Subsequent calls to this function return the same char.
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_char out parameter. The peeked char uppon successfull completion.
+ *@return CR_OK upon successfull completion, an error code otherwise.
+ */
+enum CRStatus
+cr_tknzr_peek_char (CRTknzr *a_this, guint32 *a_char)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_char,
+ CR_BAD_PARAM_ERROR) ;
+
+ if (PRIVATE (a_this)->token_cache)
+ {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos) ;
+ cr_token_destroy (PRIVATE (a_this)->token_cache) ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ }
+
+ return cr_input_peek_char (PRIVATE (a_this)->input,
+ a_char) ;
+}
+
+
+/**
+ *Peeks a byte ahead at a given postion in the parser input stream.
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_offset the offset of the peeked byte starting from the current
+ *byte in the parser input stream.
+ *@param a_byte out parameter. The peeked byte upon
+ *successfull completion.
+ *@return CR_OK upon successfull completion, an error code otherwise.
+ */
+enum CRStatus
+cr_tknzr_peek_byte (CRTknzr *a_this, gulong a_offset, guchar *a_byte)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input && a_byte,
+ CR_BAD_PARAM_ERROR) ;
+
+ if (PRIVATE (a_this)->token_cache)
+ {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos) ;
+ cr_token_destroy (PRIVATE (a_this)->token_cache) ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ }
+
+ return cr_input_peek_byte (PRIVATE (a_this)->input,
+ CR_SEEK_CUR, a_offset, a_byte) ;
+}
+
+/**
+ *Same as cr_tknzr_peek_byte() but this api returns the byte peeked.
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_offset the offset of the peeked byte starting from the current
+ *byte in the parser input stream.
+ *@param a_eof out parameter. If not NULL, is set to TRUE if we reached end of
+ *file, FALE otherwise. If the caller sets it to NULL, this parameter
+ *is just ignored.
+ *@return the peeked byte.
+ */
+guchar
+cr_tknzr_peek_byte2 (CRTknzr *a_this, gulong a_offset,
+ gboolean *a_eof)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ 0) ;
+
+ return cr_input_peek_byte2 (PRIVATE (a_this)->input,
+ a_offset, a_eof) ;
+}
+
+/**
+ *Gets the number of bytes left in the topmost input stream
+ *associated to this parser.
+ *@param a_this the current instance of #CRTknzr
+ *@return the number of bytes left or -1 in case of error.
+ */
+glong
+cr_tknzr_get_nb_bytes_left (CRTknzr *a_this)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ if (PRIVATE (a_this)->token_cache)
+ {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos) ;
+ cr_token_destroy (PRIVATE (a_this)->token_cache) ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ }
+
+ return cr_input_get_nb_bytes_left (PRIVATE (a_this)->input) ;
+}
+
+
+enum CRStatus
+cr_tknzr_get_cur_pos (CRTknzr *a_this, CRInputPos *a_pos)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_pos, CR_BAD_PARAM_ERROR) ;
+
+ if (PRIVATE (a_this)->token_cache)
+ {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos) ;
+ cr_token_destroy (PRIVATE (a_this)->token_cache) ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ }
+
+ return cr_input_get_cur_pos (PRIVATE (a_this)->input,
+ a_pos) ;
+}
+
+enum CRStatus
+cr_tknzr_get_cur_byte_addr (CRTknzr *a_this, guchar **a_addr)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+ if (PRIVATE (a_this)->token_cache)
+ {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos) ;
+ cr_token_destroy (PRIVATE (a_this)->token_cache) ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ }
+
+ return cr_input_get_cur_byte_addr
+ (PRIVATE (a_this)->input, a_addr) ;
+}
+
+
+enum CRStatus
+cr_tknzr_seek_index (CRTknzr *a_this,
+ enum CRSeekPos a_origin,
+ gint a_pos)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ if (PRIVATE (a_this)->token_cache)
+ {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos) ;
+ cr_token_destroy (PRIVATE (a_this)->token_cache) ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ }
+
+ return cr_input_seek_index (PRIVATE (a_this)->input,
+ a_origin, a_pos) ;
+}
+
+enum CRStatus
+cr_tknzr_consume_chars (CRTknzr *a_this, guint32 a_char,
+ glong *a_nb_char)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ if (PRIVATE (a_this)->token_cache)
+ {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos) ;
+ cr_token_destroy (PRIVATE (a_this)->token_cache) ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ }
+
+ return cr_input_consume_chars (PRIVATE (a_this)->input,
+ a_char, a_nb_char) ;
+}
+
+enum CRStatus
+cr_tknzr_set_cur_pos (CRTknzr *a_this, CRInputPos *a_pos)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ if (PRIVATE (a_this)->token_cache)
+ {
+ cr_token_destroy (PRIVATE (a_this)->token_cache) ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ }
+
+ return cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ a_pos) ;
+}
+
+enum CRStatus
+cr_tknzr_unget_token (CRTknzr *a_this, CRToken *a_token)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->token_cache == NULL,
+ CR_BAD_PARAM_ERROR) ;
+
+ PRIVATE (a_this)->token_cache = a_token ;
+
+ return CR_OK ;
+}
+
+/**
+ *Returns the next token of the input stream.
+ *This method is really central. Each parsing
+ *method calls it.
+ *@param a_this the current tokenizer.
+ *@param a_tk out parameter. The returned token.
+ *for the sake of mem leak avoidance, *a_tk must
+ *be NULL.
+ *@param CR_OK upon successfull completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_tknzr_get_next_token (CRTknzr *a_this, CRToken **a_tk)
+{
+ enum CRStatus status = CR_OK ;
+ CRToken *token = NULL ;
+ CRInputPos init_pos ;
+ guint32 next_char = 0 ;
+ guchar next_bytes[4] = {0} ;
+ gboolean reached_eof = FALSE ;
+ CRInput *input = NULL ;
+ GString *str = NULL ;
+ CRRgb *rgb = NULL ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_tk && *a_tk == NULL
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR) ;
+
+ if (PRIVATE (a_this)->token_cache)
+ {
+ *a_tk = PRIVATE (a_this)->token_cache ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ return CR_OK ;
+ }
+
+ RECORD_INITIAL_POS (a_this, &init_pos) ;
+
+ status = cr_input_get_end_of_file
+ (PRIVATE (a_this)->input, &reached_eof) ;
+ ENSURE_PARSING_COND (status == CR_OK) ;
+
+ if (reached_eof == TRUE)
+ {
+ status = CR_END_OF_INPUT_ERROR ;
+ goto error ;
+ }
+
+ input = PRIVATE (a_this)->input ;
+
+ PEEK_NEXT_CHAR (a_this, &next_char) ;
+ token = cr_token_new () ;
+ ENSURE_PARSING_COND (token) ;
+
+ switch (next_char)
+ {
+ case '@':
+ {
+ if (BYTE (input, 2, NULL) == 'f'
+ && BYTE (input, 3, NULL) == 'o'
+ && BYTE (input, 4, NULL) == 'n'
+ && BYTE (input, 5, NULL) == 't'
+ && BYTE (input, 6, NULL) == '-'
+ && BYTE (input, 7, NULL) == 'f'
+ && BYTE (input, 8, NULL) == 'a'
+ && BYTE (input, 9, NULL) == 'c'
+ && BYTE (input, 10, NULL) == 'e')
+ {
+ SKIP_CHARS (a_this, 10) ;
+ status = cr_token_set_font_face_sym (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+
+ if (BYTE (input, 2, NULL) == 'c'
+ && BYTE (input, 3, NULL) == 'h'
+ && BYTE (input, 4, NULL) == 'a'
+ && BYTE (input, 5, NULL) == 'r'
+ && BYTE (input, 6, NULL) == 's'
+ && BYTE (input, 7, NULL) == 'e'
+ && BYTE (input, 8, NULL) == 't')
+ {
+ SKIP_CHARS (a_this, 8) ;
+ status = cr_token_set_charset_sym (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+
+ if (BYTE (input, 2, NULL) == 'i'
+ && BYTE (input, 3, NULL) == 'm'
+ && BYTE (input, 4, NULL) == 'p'
+ && BYTE (input, 5, NULL) == 'o'
+ && BYTE (input, 6, NULL) == 'r'
+ && BYTE (input, 7, NULL) == 't')
+ {
+ SKIP_CHARS (a_this, 7) ;
+ status = cr_token_set_import_sym (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+
+ if (BYTE (input, 2, NULL) == 'm'
+ && BYTE (input, 3, NULL) == 'e'
+ && BYTE (input, 4, NULL) == 'd'
+ && BYTE (input, 5, NULL) == 'i'
+ && BYTE (input, 6, NULL) == 'a')
+ {
+ SKIP_CHARS (a_this, 6) ;
+ status = cr_token_set_media_sym (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+
+ if (BYTE (input, 2, NULL) == 'p'
+ && BYTE (input, 3, NULL) == 'a'
+ && BYTE (input, 4, NULL) == 'g'
+ && BYTE (input, 5, NULL) == 'e')
+ {
+ SKIP_CHARS (a_this, 5) ;
+ status = cr_token_set_page_sym (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+
+ status = cr_tknzr_parse_atkeyword (a_this, &str) ;
+ if (status == CR_OK)
+ {
+ status = cr_token_set_atkeyword (token, str) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+ }
+ break ;
+
+ case 'u':
+
+ if (BYTE (input, 2, NULL) == 'r'
+ && BYTE (input, 3, NULL) == 'l'
+ && BYTE (input, 4, NULL) == '(')
+ {
+ GString *str = NULL ;
+ status = cr_tknzr_parse_uri (a_this, &str) ;
+ if (status == CR_OK)
+ {
+ status = cr_token_set_uri (token, str) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+ }
+ else
+ {
+ status = cr_tknzr_parse_ident (a_this,
+ &str) ;
+ if (status == CR_OK && str)
+ {
+ status = cr_token_set_ident (token,
+ str) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+ }
+ break ;
+
+ case 'r':
+ if (BYTE (input, 2, NULL) == 'g'
+ && BYTE (input, 3, NULL) == 'b'
+ && BYTE (input, 4, NULL) == '(')
+ {
+ status = cr_tknzr_parse_rgb (a_this, &rgb) ;
+ if (status == CR_OK && rgb)
+ {
+ status = cr_token_set_rgb (token, rgb) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ rgb = NULL ;
+ goto done ;
+ }
+
+ }
+ else
+ {
+ status = cr_tknzr_parse_ident (a_this,
+ &str) ;
+ if (status == CR_OK)
+ {
+ status = cr_token_set_ident (token,
+ str) ;
+ CHECK_PARSING_STATUS(status, TRUE) ;
+ str = NULL ;
+ goto done ;
+ }
+ }
+ break ;
+
+ case '<':
+ if (BYTE (input, 2, NULL) == '-'
+ && BYTE (input, 3, NULL) == '-')
+ {
+ SKIP_CHARS (a_this, 3) ;
+ status = cr_token_set_cdo (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+ break ;
+
+ case '-':
+ if (BYTE (input, 2, NULL) == '-'
+ && BYTE (input, 3, NULL) == '>')
+ {
+ SKIP_CHARS (a_this, 3) ;
+ status = cr_token_set_cdc (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+
+ case '~':
+ if (BYTE (input, 2, NULL) == '=')
+ {
+ SKIP_CHARS (a_this, 2) ;
+ status = cr_token_set_includes (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+ break ;
+
+ case '|':
+ if (BYTE (input, 2, NULL) == '=')
+ {
+ SKIP_CHARS (a_this, 2) ;
+ status = cr_token_set_dashmatch (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+ break ;
+
+ case '/':
+ if (BYTE (input, 2, NULL) == '*')
+ {
+ status = cr_tknzr_parse_comment (a_this, &str) ;
+
+ if (status == CR_OK)
+ {
+ status = cr_token_set_comment (token,
+ str) ;
+ str = NULL ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+ }
+
+ case ';':
+ SKIP_CHARS (a_this, 1) ;
+ status = cr_token_set_semicolon (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+
+ case '{':
+ SKIP_CHARS (a_this, 1) ;
+ status = cr_token_set_cbo (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+
+ case '}':
+ SKIP_CHARS (a_this, 1) ;
+ status = cr_token_set_cbc (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+
+ case '(':
+ SKIP_CHARS (a_this, 1) ;
+ status = cr_token_set_po (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+
+ case ')':
+ SKIP_CHARS (a_this, 1) ;
+ status = cr_token_set_pc (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+
+ case '[':
+ SKIP_CHARS (a_this, 1) ;
+ status = cr_token_set_bo (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+
+ case ']':
+ SKIP_CHARS (a_this, 1) ;
+ status = cr_token_set_bc (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\f':
+ case '\r':
+ {
+ guchar *start = NULL, *end = NULL ;
+ status = cr_tknzr_parse_w (a_this, &start, &end) ;
+ if (status == CR_OK)
+ {
+ status = cr_token_set_s (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+ }
+ break ;
+
+ case '#':
+ {
+ status = cr_tknzr_parse_hash (a_this, &str) ;
+ if (status == CR_OK && str)
+ {
+ status = cr_token_set_hash (token, str) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ str = NULL ;
+ goto done ;
+ }
+ }
+ break ;
+
+ case '\'':
+ case '"':
+ status = cr_tknzr_parse_string (a_this, &str) ;
+ if (status == CR_OK && str)
+ {
+ status = cr_token_set_string (token, str) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ str = NULL ;
+ goto done ;
+ }
+ break ;
+
+ case '!':
+ status = cr_tknzr_parse_important (a_this) ;
+ if (status == CR_OK)
+ {
+ status = cr_token_set_important_sym (token) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ goto done ;
+ }
+ break ;
+
+ case '0' ... '9':
+ case '.':
+ {
+ CRNum *num = NULL ;
+
+ status = cr_tknzr_parse_num (a_this, &num) ;
+ if (status == CR_OK && num)
+ {
+ next_bytes[0] = BYTE (input, 1, NULL) ;
+ next_bytes[1] = BYTE (input, 2, NULL) ;
+ next_bytes[2] = BYTE (input, 3, NULL) ;
+ next_bytes[3] = BYTE (input, 3, NULL) ;
+
+ if (next_bytes[0] == 'e'
+ && next_bytes[1] == 'm')
+ {
+ num->type = NUM_LENGTH_EM ;
+ status = cr_token_set_ems (token, num) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 2) ;
+ }
+ else if (next_bytes[0] == 'e'
+ && next_bytes[1] == 'x')
+ {
+ num->type = NUM_LENGTH_EX ;
+ status = cr_token_set_exs (token, num) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 2) ;
+ }
+ else if (next_bytes[0] == 'p'
+ && next_bytes[1] == 'x')
+ {
+ num->type = NUM_LENGTH_PX ;
+ status = cr_token_set_length
+ (token, num,
+ LENGTH_PX_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 2) ;
+ }
+ else if (next_bytes[0] == 'c'
+ && next_bytes[1] == 'm')
+ {
+ num->type = NUM_LENGTH_CM ;
+ status = cr_token_set_length
+ (token, num, LENGTH_CM_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 2) ;
+ }
+ else if (next_bytes[0] == 'm'
+ && next_bytes[1] == 'm')
+ {
+ num->type = NUM_LENGTH_MM ;
+ status = cr_token_set_length
+ (token, num, LENGTH_MM_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 2) ;
+ }
+ else if (next_bytes[0] == 'i'
+ && next_bytes[1] == 'n')
+ {
+ num->type = NUM_LENGTH_IN ;
+ status = cr_token_set_length
+ (token, num, LENGTH_IN_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 2) ;
+ }
+ else if (next_bytes[0] == 'p'
+ && next_bytes[1] == 't')
+ {
+ num->type = NUM_LENGTH_PT ;
+ status = cr_token_set_length
+ (token, num, LENGTH_PT_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 2) ;
+ }
+ else if (next_bytes[0] == 'p'
+ && next_bytes[1] == 'c')
+ {
+ num->type = NUM_LENGTH_PC ;
+ status = cr_token_set_length
+ (token, num, LENGTH_PC_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 2) ;
+ }
+ else if (next_bytes[0] == 'd'
+ && next_bytes[1] == 'e'
+ && next_bytes[2] == 'g')
+ {
+ num->type = NUM_ANGLE_DEG ;
+ status = cr_token_set_angle
+ (token, num, ANGLE_DEG_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 3) ;
+ }
+ else if (next_bytes[0] == 'r'
+ && next_bytes[1] == 'a'
+ && next_bytes[2] == 'd')
+ {
+ num->type = NUM_ANGLE_RAD ;
+ status = cr_token_set_angle
+ (token, num, ANGLE_RAD_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 3) ;
+ }
+ else if (next_bytes[0] == 'g'
+ && next_bytes[1] == 'r'
+ && next_bytes[2] == 'a'
+ && next_bytes[3] == 'd')
+ {
+ num->type = NUM_ANGLE_GRAD ;
+ status = cr_token_set_angle
+ (token, num, ANGLE_GRAD_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 4) ;
+ }
+ else if (next_bytes[0] == 'm'
+ && next_bytes[1] == 's')
+ {
+ num->type = NUM_TIME_MS ;
+ status = cr_token_set_time
+ (token, num,TIME_MS_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 2) ;
+ }
+ else if (next_bytes[0] =='s')
+ {
+ num->type = NUM_TIME_S ;
+ status = cr_token_set_time
+ (token, num, TIME_S_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 1) ;
+ }
+ else if (next_bytes[0] == 'H'
+ && next_bytes[1] == 'z')
+ {
+ num->type = NUM_FREQ_HZ ;
+ status = cr_token_set_freq
+ (token, num, FREQ_HZ_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 2) ;
+ }
+ else if (next_bytes[0] == 'k'
+ && next_bytes[1] == 'H'
+ && next_bytes[2] == 'z')
+ {
+ num->type = NUM_FREQ_KHZ ;
+ status = cr_token_set_freq
+ (token, num, FREQ_KHZ_ET) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 3) ;
+ }
+ else if (next_bytes[0] == '%')
+ {
+ num->type = NUM_PERCENTAGE ;
+ status = cr_token_set_percentage (token,
+ num) ;
+ num = NULL ;
+ SKIP_CHARS (a_this, 1) ;
+ }
+ else
+ {
+ status = cr_tknzr_parse_ident (a_this,
+ &str) ;
+ if (status == CR_OK && str)
+ {
+ num->type = NUM_UNKNOWN_TYPE ;
+ status = cr_token_set_dimen
+ (token, num, str) ;
+ num = NULL ;
+ CHECK_PARSING_STATUS (status,
+ TRUE);
+ str = NULL ;
+ }
+ else
+ {
+ status = cr_token_set_number
+ (token, num) ;
+ num = NULL ;
+ CHECK_PARSING_STATUS (status,
+ TRUE) ;
+ str = NULL ;
+ }
+ }
+
+ goto done ;
+ }
+ }
+ break ;
+
+ default:
+ /*process the fallback cases here*/
+
+ if (next_char == '\\'
+ || (cr_utils_is_nonascii (next_bytes[0]) == TRUE)
+ || ((next_char >= 'a') && (next_char <= 'z'))
+ || ((next_char >= 'A') && (next_char <= 'Z')))
+ {
+ status = cr_tknzr_parse_ident (a_this, &str) ;
+ if (status == CR_OK && str)
+ {
+ guint32 next_c = 0 ;
+
+ status = cr_input_peek_char
+ (PRIVATE (a_this)->input,
+ &next_c);
+
+ if (status == CR_OK && next_c == '(')
+ {
+
+ SKIP_CHARS (a_this, 1) ;
+ status = cr_token_set_function
+ (token, str) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ /*ownership is transfered
+ *to token by cr_token_set_function.
+ */
+ str = NULL ;
+ }
+ else
+ {
+ status = cr_token_set_ident (token,
+ str) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ str = NULL ;
+ }
+ goto done ;
+ }
+ else
+ {
+ if (str)
+ {
+ g_string_free (str, TRUE) ;
+ str = NULL ;
+ }
+ }
+ }
+ break ;
+ }
+
+ READ_NEXT_CHAR (a_this, &next_char) ;
+ status = cr_token_set_delim (token, next_char) ;
+ CHECK_PARSING_STATUS (status, TRUE) ;
+
+ done:
+
+ if (status == CR_OK && token)
+ {
+ *a_tk = token ;
+ /*
+ *store the previous position input stream pos.
+ */
+ memmove (&PRIVATE (a_this)->prev_pos,
+ &init_pos, sizeof (CRInputPos)) ;
+ return CR_OK ;
+ }
+
+ error:
+ if (token)
+ {
+ cr_token_destroy (token) ;
+ token = NULL ;
+ }
+
+ if (str)
+ {
+ g_string_free (str, TRUE) ;
+ str = NULL ;
+ }
+ cr_tknzr_set_cur_pos (a_this,
+ &init_pos) ;
+ return status ;
+
+}
+
+enum CRStatus
+cr_tknzr_parse_token (CRTknzr *a_this, enum CRTokenType a_type,
+ enum CRTokenExtraType a_et, gpointer a_res,
+ gpointer a_extra_res)
+{
+ enum CRStatus status = CR_OK ;
+ CRToken *token = NULL ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_res,
+ CR_BAD_PARAM_ERROR) ;
+
+ status = cr_tknzr_get_next_token (a_this,
+ &token) ;
+ if (status != CR_OK) return status ;
+ if (token == NULL) return CR_PARSING_ERROR ;
+
+ if (token->type == a_type)
+ {
+ switch (a_type)
+ {
+ case NO_TK:
+ case S_TK:
+ case CDO_TK:
+ case CDC_TK:
+ case INCLUDES_TK:
+ case DASHMATCH_TK:
+ case IMPORT_SYM_TK:
+ case PAGE_SYM_TK:
+ case MEDIA_SYM_TK:
+ case FONT_FACE_SYM_TK:
+ case CHARSET_SYM_TK:
+ case IMPORTANT_SYM_TK:
+ status = CR_OK ;
+ break ;
+
+ case STRING_TK:
+ case IDENT_TK:
+ case HASH_TK:
+ case ATKEYWORD_TK:
+ case FUNCTION_TK:
+ case COMMENT_TK:
+ case URI_TK:
+ *((GString**)a_res) = token->u.str ;
+ token->u.str = NULL ;
+ status = CR_OK ;
+ break ;
+
+ case EMS_TK:
+ case EXS_TK:
+ case PERCENTAGE_TK:
+ case NUMBER_TK:
+ *((CRNum**)a_res) = token->u.num ;
+ token->u.num = NULL ;
+ status = CR_OK ;
+ break ;
+
+ case LENGTH_TK:
+ case ANGLE_TK:
+ case TIME_TK:
+ case FREQ_TK:
+ if (token->extra_type == a_et)
+ {
+ *((CRNum**)a_res) = token->u.num ;
+ token->u.num = NULL ;
+ status = CR_OK ;
+ }
+ break ;
+
+ case DIMEN_TK:
+ *((CRNum**)a_res) = token->u.num ;
+ if (a_extra_res == NULL)
+ {
+ status = CR_BAD_PARAM_ERROR ;
+ goto error ;
+ }
+
+ *((GString**)a_extra_res) = token->dimen ;
+ token->u.num = NULL ;
+ token->dimen = NULL ;
+ status = CR_OK ;
+ break ;
+
+ case DELIM_TK:
+ *((guint32*)a_res) = token->u.unichar ;
+ status = CR_OK ;
+ break ;
+
+ case UNICODERANGE_TK:
+ default:
+ status = CR_PARSING_ERROR ;
+ break ;
+ }
+
+ cr_token_destroy (token) ; token = NULL ;
+ }
+ else
+ {
+ cr_tknzr_unget_token (a_this,
+ token) ;
+ token = NULL ;
+ status = CR_PARSING_ERROR ;
+ }
+
+ return status ;
+
+ error:
+
+ if (token)
+ {
+ cr_tknzr_unget_token (a_this, token) ;
+ token = NULL ;
+ }
+
+ return status ;
+}
+
+void
+cr_tknzr_destroy (CRTknzr *a_this)
+{
+ g_return_if_fail (a_this) ;
+
+
+ if (PRIVATE (a_this) && PRIVATE (a_this)->input)
+ {
+ if (cr_input_unref (PRIVATE (a_this)->input)
+ == TRUE)
+ {
+ PRIVATE (a_this)->input = NULL ;
+ }
+ }
+
+ if (PRIVATE (a_this)->token_cache)
+ {
+ cr_token_destroy (PRIVATE (a_this)->token_cache) ;
+ PRIVATE (a_this)->token_cache = NULL ;
+ }
+
+ if (PRIVATE (a_this))
+ {
+ g_free (PRIVATE (a_this)) ;
+ PRIVATE (a_this) = NULL ;
+ }
+
+ g_free (a_this) ;
+}