From 872b5e3aea2ebbaea188278aabdee916020840ed Mon Sep 17 00:00:00 2001 From: wangxiao65 <287608437@qq.com> Date: Thu, 3 Dec 2020 14:47:02 +0800 Subject: [PATCH] move the libcroco sources directly under src/st, remove the libcroco dependency from the meson.build files. --- ...roco-sources-directly-under-src-st-c.patch | 25201 ++++++++++++++++ gnome-shell.spec | 7 +- 2 files changed, 25207 insertions(+), 1 deletion(-) create mode 100644 0001-Include-the-libcroco-sources-directly-under-src-st-c.patch diff --git a/0001-Include-the-libcroco-sources-directly-under-src-st-c.patch b/0001-Include-the-libcroco-sources-directly-under-src-st-c.patch new file mode 100644 index 0000000..2c7e2cf --- /dev/null +++ b/0001-Include-the-libcroco-sources-directly-under-src-st-c.patch @@ -0,0 +1,25201 @@ +From 47758d16ff0acd9edbd8ff6eb3922e59fb14f8bb Mon Sep 17 00:00:00 2001 +From: Federico Mena Quintero +Date: Thu, 21 Nov 2019 14:24:02 -0600 +Subject: [PATCH] Include the libcroco sources directly under src/st/croco + +This is all of the original libcroco, minus these two which we don't use: + + - cr-sel-eng - the CSS selection engine for xmlNode. + - cr-style. + +Part of https://gitlab.gnome.org/GNOME/gnome-shell/issues/1934 +--- + meson.build | 2 - + src/st/croco/cr-additional-sel.c | 500 +++ + src/st/croco/cr-additional-sel.h | 98 + + src/st/croco/cr-attr-sel.c | 235 ++ + src/st/croco/cr-attr-sel.h | 74 + + src/st/croco/cr-cascade.c | 215 ++ + src/st/croco/cr-cascade.h | 74 + + src/st/croco/cr-declaration.c | 801 +++++ + src/st/croco/cr-declaration.h | 136 + + src/st/croco/cr-doc-handler.c | 276 ++ + src/st/croco/cr-doc-handler.h | 298 ++ + src/st/croco/cr-enc-handler.c | 184 ++ + src/st/croco/cr-enc-handler.h | 94 + + src/st/croco/cr-fonts.c | 949 ++++++ + src/st/croco/cr-fonts.h | 315 ++ + src/st/croco/cr-input.c | 1191 ++++++++ + src/st/croco/cr-input.h | 174 ++ + src/st/croco/cr-num.c | 313 ++ + src/st/croco/cr-num.h | 127 + + src/st/croco/cr-om-parser.c | 1142 +++++++ + src/st/croco/cr-om-parser.h | 98 + + src/st/croco/cr-parser.c | 4525 ++++++++++++++++++++++++++++ + src/st/croco/cr-parser.h | 128 + + src/st/croco/cr-parsing-location.c | 172 ++ + src/st/croco/cr-parsing-location.h | 70 + + src/st/croco/cr-prop-list.c | 404 +++ + src/st/croco/cr-prop-list.h | 80 + + src/st/croco/cr-pseudo.c | 167 + + src/st/croco/cr-pseudo.h | 64 + + src/st/croco/cr-rgb.c | 687 +++++ + src/st/croco/cr-rgb.h | 94 + + src/st/croco/cr-selector.c | 306 ++ + src/st/croco/cr-selector.h | 95 + + src/st/croco/cr-simple-sel.c | 325 ++ + src/st/croco/cr-simple-sel.h | 130 + + src/st/croco/cr-statement.c | 2794 +++++++++++++++++ + src/st/croco/cr-statement.h | 440 +++ + src/st/croco/cr-string.c | 168 ++ + src/st/croco/cr-string.h | 76 + + src/st/croco/cr-stylesheet.c | 178 ++ + src/st/croco/cr-stylesheet.h | 102 + + src/st/croco/cr-term.c | 790 +++++ + src/st/croco/cr-term.h | 190 ++ + src/st/croco/cr-tknzr.c | 2762 +++++++++++++++++ + src/st/croco/cr-tknzr.h | 115 + + src/st/croco/cr-token.c | 636 ++++ + src/st/croco/cr-token.h | 212 ++ + src/st/croco/cr-utils.c | 1330 ++++++++ + src/st/croco/cr-utils.h | 246 ++ + src/st/croco/libcroco-config.h | 13 + + src/st/croco/libcroco.h | 42 + + src/st/meson.build | 62 +- + src/st/st-theme-node-private.h | 2 +- + src/st/st-theme-private.h | 2 +- + 54 files changed, 24696 insertions(+), 7 deletions(-) + create mode 100644 src/st/croco/cr-additional-sel.c + create mode 100644 src/st/croco/cr-additional-sel.h + create mode 100644 src/st/croco/cr-attr-sel.c + create mode 100644 src/st/croco/cr-attr-sel.h + create mode 100644 src/st/croco/cr-cascade.c + create mode 100644 src/st/croco/cr-cascade.h + create mode 100644 src/st/croco/cr-declaration.c + create mode 100644 src/st/croco/cr-declaration.h + create mode 100644 src/st/croco/cr-doc-handler.c + create mode 100644 src/st/croco/cr-doc-handler.h + create mode 100644 src/st/croco/cr-enc-handler.c + create mode 100644 src/st/croco/cr-enc-handler.h + create mode 100644 src/st/croco/cr-fonts.c + create mode 100644 src/st/croco/cr-fonts.h + create mode 100644 src/st/croco/cr-input.c + create mode 100644 src/st/croco/cr-input.h + create mode 100644 src/st/croco/cr-num.c + create mode 100644 src/st/croco/cr-num.h + create mode 100644 src/st/croco/cr-om-parser.c + create mode 100644 src/st/croco/cr-om-parser.h + create mode 100644 src/st/croco/cr-parser.c + create mode 100644 src/st/croco/cr-parser.h + create mode 100644 src/st/croco/cr-parsing-location.c + create mode 100644 src/st/croco/cr-parsing-location.h + create mode 100644 src/st/croco/cr-prop-list.c + create mode 100644 src/st/croco/cr-prop-list.h + create mode 100644 src/st/croco/cr-pseudo.c + create mode 100644 src/st/croco/cr-pseudo.h + create mode 100644 src/st/croco/cr-rgb.c + create mode 100644 src/st/croco/cr-rgb.h + create mode 100644 src/st/croco/cr-selector.c + create mode 100644 src/st/croco/cr-selector.h + create mode 100644 src/st/croco/cr-simple-sel.c + create mode 100644 src/st/croco/cr-simple-sel.h + create mode 100644 src/st/croco/cr-statement.c + create mode 100644 src/st/croco/cr-statement.h + create mode 100644 src/st/croco/cr-string.c + create mode 100644 src/st/croco/cr-string.h + create mode 100644 src/st/croco/cr-stylesheet.c + create mode 100644 src/st/croco/cr-stylesheet.h + create mode 100644 src/st/croco/cr-term.c + create mode 100644 src/st/croco/cr-term.h + create mode 100644 src/st/croco/cr-tknzr.c + create mode 100644 src/st/croco/cr-tknzr.h + create mode 100644 src/st/croco/cr-token.c + create mode 100644 src/st/croco/cr-token.h + create mode 100644 src/st/croco/cr-utils.c + create mode 100644 src/st/croco/cr-utils.h + create mode 100644 src/st/croco/libcroco-config.h + create mode 100644 src/st/croco/libcroco.h + +diff --git a/meson.build b/meson.build +index bfe4ee8a6..9c6d5b946 100644 +--- a/meson.build ++++ b/meson.build +@@ -13,7 +13,6 @@ cogl_pc = 'mutter-cogl-' + mutter_api_version + cogl_pango_pc = 'mutter-cogl-pango-' + mutter_api_version + libmutter_pc = 'libmutter-' + mutter_api_version + +-croco_req = '>= 0.6.8' + ecal_req = '>= 3.5.3' + eds_req = '>= 3.17.2' + gcr_req = '>= 3.7.5' +@@ -89,7 +88,6 @@ gio_unix_dep = dependency('gio-unix-2.0', version: gio_req) + gjs_dep = dependency('gjs-1.0', version: gjs_req) + gtk_dep = dependency('gtk+-3.0', version: gtk_req) + libxml_dep = dependency('libxml-2.0') +-croco_dep = dependency('libcroco-0.6', version: croco_req) + clutter_dep = dependency(clutter_pc, version: mutter_req) + cogl_dep = dependency(cogl_pc, version: mutter_req) + cogl_pango_dep = dependency(cogl_pango_pc, version: mutter_req) +diff --git a/src/st/croco/cr-additional-sel.c b/src/st/croco/cr-additional-sel.c +new file mode 100644 +index 000000000..c34b8d243 +--- /dev/null ++++ b/src/st/croco/cr-additional-sel.c +@@ -0,0 +1,500 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ * ++ */ ++ ++#include "cr-additional-sel.h" ++#include "string.h" ++ ++/** ++ * CRAdditionalSel: ++ * ++ * #CRAdditionalSel abstracts an additionnal selector. ++ * An additional selector is the selector part ++ * that comes after the combination of type selectors. ++ * It can be either "a class selector (the .class part), ++ * a pseudo class selector, an attribute selector ++ * or an id selector. ++ */ ++ ++/** ++ * cr_additional_sel_new: ++ * ++ * Default constructor of #CRAdditionalSel. ++ * Returns the newly build instance of #CRAdditionalSel. ++ */ ++CRAdditionalSel * ++cr_additional_sel_new (void) ++{ ++ CRAdditionalSel *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRAdditionalSel)); ++ ++ if (result == NULL) { ++ cr_utils_trace_debug ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRAdditionalSel)); ++ ++ return result; ++} ++ ++/** ++ * cr_additional_sel_new_with_type: ++ * @a_sel_type: the type of the newly built instance ++ * of #CRAdditionalSel. ++ * ++ * Constructor of #CRAdditionalSel. ++ * Returns the newly built instance of #CRAdditionalSel. ++ */ ++CRAdditionalSel * ++cr_additional_sel_new_with_type (enum AddSelectorType a_sel_type) ++{ ++ CRAdditionalSel *result = NULL; ++ ++ result = cr_additional_sel_new (); ++ ++ g_return_val_if_fail (result, NULL); ++ ++ result->type = a_sel_type; ++ ++ return result; ++} ++ ++/** ++ * cr_additional_sel_set_class_name: ++ * @a_this: the "this pointer" of the current instance ++ * of #CRAdditionalSel . ++ * @a_class_name: the new class name to set. ++ * ++ * Sets a new class name to a ++ * CLASS additional selector. ++ */ ++void ++cr_additional_sel_set_class_name (CRAdditionalSel * a_this, ++ CRString * a_class_name) ++{ ++ g_return_if_fail (a_this && a_this->type == CLASS_ADD_SELECTOR); ++ ++ if (a_this->content.class_name) { ++ cr_string_destroy (a_this->content.class_name); ++ } ++ ++ a_this->content.class_name = a_class_name; ++} ++ ++/** ++ * cr_additional_sel_set_id_name: ++ * @a_this: the "this pointer" of the current instance ++ * of #CRAdditionalSel . ++ * @a_id: the new id to set. ++ * ++ * Sets a new id name to an ++ * ID additional selector. ++ */ ++void ++cr_additional_sel_set_id_name (CRAdditionalSel * a_this, CRString * a_id) ++{ ++ g_return_if_fail (a_this && a_this->type == ID_ADD_SELECTOR); ++ ++ if (a_this->content.id_name) { ++ cr_string_destroy (a_this->content.id_name); ++ } ++ ++ a_this->content.id_name = a_id; ++} ++ ++/** ++ * cr_additional_sel_set_pseudo: ++ * @a_this: the "this pointer" of the current instance ++ * of #CRAdditionalSel . ++ * @a_pseudo: the new pseudo to set. ++ * ++ * Sets a new pseudo to a ++ * PSEUDO additional selector. ++ */ ++void ++cr_additional_sel_set_pseudo (CRAdditionalSel * a_this, CRPseudo * a_pseudo) ++{ ++ g_return_if_fail (a_this ++ && a_this->type == PSEUDO_CLASS_ADD_SELECTOR); ++ ++ if (a_this->content.pseudo) { ++ cr_pseudo_destroy (a_this->content.pseudo); ++ } ++ ++ a_this->content.pseudo = a_pseudo; ++} ++ ++/** ++ * cr_additional_sel_set_attr_sel: ++ * @a_this: the "this pointer" of the current instance ++ * of #CRAdditionalSel . ++ * @a_sel: the new instance of #CRAttrSel to set. ++ * ++ * Sets a new instance of #CRAttrSel to ++ * a ATTRIBUTE additional selector. ++ */ ++void ++cr_additional_sel_set_attr_sel (CRAdditionalSel * a_this, CRAttrSel * a_sel) ++{ ++ g_return_if_fail (a_this && a_this->type == ATTRIBUTE_ADD_SELECTOR); ++ ++ if (a_this->content.attr_sel) { ++ cr_attr_sel_destroy (a_this->content.attr_sel); ++ } ++ ++ a_this->content.attr_sel = a_sel; ++} ++ ++/** ++ * cr_additional_sel_append: ++ * @a_this: the "this pointer" of the current instance ++ * of #CRAdditionalSel . ++ * @a_sel: the new instance to #CRAdditional to append. ++ * ++ * Appends a new instance of #CRAdditional to the ++ * current list of #CRAdditional. ++ * ++ * Returns the new list of CRAdditionalSel or NULL if an error arises. ++ */ ++CRAdditionalSel * ++cr_additional_sel_append (CRAdditionalSel * a_this, CRAdditionalSel * a_sel) ++{ ++ CRAdditionalSel *cur_sel = NULL; ++ ++ g_return_val_if_fail (a_sel, NULL); ++ ++ if (a_this == NULL) { ++ return a_sel; ++ } ++ ++ if (a_sel == NULL) ++ return NULL; ++ ++ for (cur_sel = a_this; ++ cur_sel && cur_sel->next; cur_sel = cur_sel->next) ; ++ ++ g_return_val_if_fail (cur_sel != NULL, NULL); ++ ++ cur_sel->next = a_sel; ++ a_sel->prev = cur_sel; ++ ++ return a_this; ++} ++ ++/** ++ * cr_additional_sel_prepend: ++ * @a_this: the "this pointer" of the current instance ++ * of #CRAdditionalSel . ++ * @a_sel: the new instance to #CRAdditional to preappend. ++ * ++ * Preppends a new instance of #CRAdditional to the ++ * current list of #CRAdditional. ++ * ++ * Returns the new list of CRAdditionalSel or NULL if an error arises. ++ */ ++CRAdditionalSel * ++cr_additional_sel_prepend (CRAdditionalSel * a_this, CRAdditionalSel * a_sel) ++{ ++ g_return_val_if_fail (a_sel, NULL); ++ ++ if (a_this == NULL) { ++ return a_sel; ++ } ++ ++ a_sel->next = a_this; ++ a_this->prev = a_sel; ++ ++ return a_sel; ++} ++ ++guchar * ++cr_additional_sel_to_string (CRAdditionalSel const * a_this) ++{ ++ guchar *result = NULL; ++ GString *str_buf = NULL; ++ CRAdditionalSel const *cur = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ str_buf = g_string_new (NULL); ++ ++ for (cur = a_this; cur; cur = cur->next) { ++ switch (cur->type) { ++ case CLASS_ADD_SELECTOR: ++ { ++ guchar *name = NULL; ++ ++ if (cur->content.class_name) { ++ name = (guchar *) g_strndup ++ (cur->content.class_name->stryng->str, ++ cur->content.class_name->stryng->len); ++ ++ if (name) { ++ g_string_append_printf ++ (str_buf, ".%s", ++ name); ++ g_free (name); ++ name = NULL; ++ } ++ } ++ } ++ break; ++ ++ case ID_ADD_SELECTOR: ++ { ++ guchar *name = NULL; ++ ++ if (cur->content.id_name) { ++ name = (guchar *) g_strndup ++ (cur->content.id_name->stryng->str, ++ cur->content.id_name->stryng->len); ++ ++ if (name) { ++ g_string_append_printf ++ (str_buf, "#%s", ++ name); ++ g_free (name); ++ name = NULL; ++ } ++ } ++ } ++ ++ break; ++ ++ case PSEUDO_CLASS_ADD_SELECTOR: ++ { ++ if (cur->content.pseudo) { ++ guchar *tmp_str = NULL; ++ ++ tmp_str = cr_pseudo_to_string ++ (cur->content.pseudo); ++ if (tmp_str) { ++ g_string_append_printf ++ (str_buf, ":%s", ++ tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ } ++ break; ++ ++ case ATTRIBUTE_ADD_SELECTOR: ++ if (cur->content.attr_sel) { ++ guchar *tmp_str = NULL; ++ ++ g_string_append_c (str_buf, '['); ++ tmp_str = cr_attr_sel_to_string ++ (cur->content.attr_sel); ++ if (tmp_str) { ++ g_string_append_printf ++ (str_buf, "%s]", tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ if (str_buf) { ++ result = (guchar *) str_buf->str; ++ g_string_free (str_buf, FALSE); ++ str_buf = NULL; ++ } ++ ++ return result; ++} ++ ++guchar * ++cr_additional_sel_one_to_string (CRAdditionalSel const *a_this) ++{ ++ guchar *result = NULL; ++ GString *str_buf = NULL; ++ ++ g_return_val_if_fail (a_this, NULL) ; ++ ++ str_buf = g_string_new (NULL) ; ++ ++ switch (a_this->type) { ++ case CLASS_ADD_SELECTOR: ++ { ++ guchar *name = NULL; ++ ++ if (a_this->content.class_name) { ++ name = (guchar *) g_strndup ++ (a_this->content.class_name->stryng->str, ++ a_this->content.class_name->stryng->len); ++ ++ if (name) { ++ g_string_append_printf ++ (str_buf, ".%s", ++ name); ++ g_free (name); ++ name = NULL; ++ } ++ } ++ } ++ break; ++ ++ case ID_ADD_SELECTOR: ++ { ++ guchar *name = NULL; ++ ++ if (a_this->content.id_name) { ++ name = (guchar *) g_strndup ++ (a_this->content.id_name->stryng->str, ++ a_this->content.id_name->stryng->len); ++ ++ if (name) { ++ g_string_append_printf ++ (str_buf, "#%s", ++ name); ++ g_free (name); ++ name = NULL; ++ } ++ } ++ } ++ ++ break; ++ ++ case PSEUDO_CLASS_ADD_SELECTOR: ++ { ++ if (a_this->content.pseudo) { ++ guchar *tmp_str = NULL; ++ ++ tmp_str = cr_pseudo_to_string ++ (a_this->content.pseudo); ++ if (tmp_str) { ++ g_string_append_printf ++ (str_buf, ":%s", ++ tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ } ++ break; ++ ++ case ATTRIBUTE_ADD_SELECTOR: ++ if (a_this->content.attr_sel) { ++ guchar *tmp_str = NULL; ++ ++ g_string_append_printf (str_buf, "["); ++ tmp_str = cr_attr_sel_to_string ++ (a_this->content.attr_sel); ++ if (tmp_str) { ++ g_string_append_printf ++ (str_buf, "%s]", tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (str_buf) { ++ result = (guchar *) str_buf->str; ++ g_string_free (str_buf, FALSE); ++ str_buf = NULL; ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_additional_sel_dump: ++ * @a_this: the "this pointer" of the current instance of ++ * #CRAdditionalSel. ++ * @a_fp: the destination file. ++ * ++ * Dumps the current instance of #CRAdditionalSel to a file ++ */ ++void ++cr_additional_sel_dump (CRAdditionalSel const * a_this, FILE * a_fp) ++{ ++ guchar *tmp_str = NULL; ++ ++ g_return_if_fail (a_fp); ++ ++ if (a_this) { ++ tmp_str = cr_additional_sel_to_string (a_this); ++ if (tmp_str) { ++ fprintf (a_fp, "%s", tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++} ++ ++/** ++ * cr_additional_sel_destroy: ++ * @a_this: the "this pointer" of the current instance ++ * of #CRAdditionalSel . ++ * ++ * Destroys an instance of #CRAdditional. ++ */ ++void ++cr_additional_sel_destroy (CRAdditionalSel * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ switch (a_this->type) { ++ case CLASS_ADD_SELECTOR: ++ cr_string_destroy (a_this->content.class_name); ++ a_this->content.class_name = NULL; ++ break; ++ ++ case PSEUDO_CLASS_ADD_SELECTOR: ++ cr_pseudo_destroy (a_this->content.pseudo); ++ a_this->content.pseudo = NULL; ++ break; ++ ++ case ID_ADD_SELECTOR: ++ cr_string_destroy (a_this->content.id_name); ++ a_this->content.id_name = NULL; ++ break; ++ ++ case ATTRIBUTE_ADD_SELECTOR: ++ cr_attr_sel_destroy (a_this->content.attr_sel); ++ a_this->content.attr_sel = NULL; ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (a_this->next) { ++ cr_additional_sel_destroy (a_this->next); ++ } ++ ++ g_free (a_this); ++} +diff --git a/src/st/croco/cr-additional-sel.h b/src/st/croco/cr-additional-sel.h +new file mode 100644 +index 000000000..7ca3e07d5 +--- /dev/null ++++ b/src/st/croco/cr-additional-sel.h +@@ -0,0 +1,98 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See the COPYRIGHTS file for copyright information. ++ */ ++ ++ ++#ifndef __CR_ADD_SEL_H__ ++#define __CR_ADD_SEL_H__ ++ ++#include ++#include ++#include "cr-utils.h" ++#include "cr-attr-sel.h" ++#include "cr-pseudo.h" ++#include "cr-additional-sel.h" ++ ++G_BEGIN_DECLS ++ ++enum AddSelectorType ++{ ++ NO_ADD_SELECTOR = 0 , ++ CLASS_ADD_SELECTOR = 1 , ++ PSEUDO_CLASS_ADD_SELECTOR = 1 << 1, ++ ID_ADD_SELECTOR = 1 << 3, ++ ATTRIBUTE_ADD_SELECTOR = 1 << 4 ++} ; ++ ++union CRAdditionalSelectorContent ++{ ++ CRString *class_name ; ++ CRString *id_name ; ++ CRPseudo *pseudo ; ++ CRAttrSel *attr_sel ; ++} ; ++ ++typedef struct _CRAdditionalSel CRAdditionalSel ; ++ ++struct _CRAdditionalSel ++{ ++ enum AddSelectorType type ; ++ union CRAdditionalSelectorContent content ; ++ ++ CRAdditionalSel * next ; ++ CRAdditionalSel * prev ; ++ CRParsingLocation location ; ++} ; ++ ++CRAdditionalSel * cr_additional_sel_new (void) ; ++ ++CRAdditionalSel * cr_additional_sel_new_with_type (enum AddSelectorType a_sel_type) ; ++ ++CRAdditionalSel * cr_additional_sel_append (CRAdditionalSel *a_this, ++ CRAdditionalSel *a_sel) ; ++ ++void cr_additional_sel_set_class_name (CRAdditionalSel *a_this, ++ CRString *a_class_name) ; ++ ++void cr_additional_sel_set_id_name (CRAdditionalSel *a_this, ++ CRString *a_id) ; ++ ++void cr_additional_sel_set_pseudo (CRAdditionalSel *a_this, ++ CRPseudo *a_pseudo) ; ++ ++void cr_additional_sel_set_attr_sel (CRAdditionalSel *a_this, ++ CRAttrSel *a_sel) ; ++ ++CRAdditionalSel * cr_additional_sel_prepend (CRAdditionalSel *a_this, ++ CRAdditionalSel *a_sel) ; ++ ++guchar * cr_additional_sel_to_string (CRAdditionalSel const *a_this) ; ++ ++guchar * cr_additional_sel_one_to_string (CRAdditionalSel const *a_this) ; ++ ++void cr_additional_sel_dump (CRAdditionalSel const *a_this, FILE *a_fp) ; ++ ++void cr_additional_sel_destroy (CRAdditionalSel *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_ADD_SEL_H*/ +diff --git a/src/st/croco/cr-attr-sel.c b/src/st/croco/cr-attr-sel.c +new file mode 100644 +index 000000000..c057bbbf6 +--- /dev/null ++++ b/src/st/croco/cr-attr-sel.c +@@ -0,0 +1,235 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * See COPYRIGHTS file for copyrights information. ++ */ ++ ++#include ++#include "cr-attr-sel.h" ++ ++/** ++ * CRAttrSel: ++ * ++ * #CRAdditionalSel abstracts an attribute selector. ++ * Attributes selectors are described in the css2 spec [5.8]. ++ * There are more generally used in the css2 selectors described in ++ * css2 spec [5] . ++ */ ++ ++/** ++ * cr_attr_sel_new: ++ * The constructor of #CRAttrSel. ++ * Returns the newly allocated instance ++ * of #CRAttrSel. ++ */ ++CRAttrSel * ++cr_attr_sel_new (void) ++{ ++ CRAttrSel *result = NULL; ++ ++ result = g_malloc0 (sizeof (CRAttrSel)); ++ ++ return result; ++} ++ ++/** ++ * cr_attr_sel_append_attr_sel: ++ * @a_this: the this pointer of the current instance of #CRAttrSel. ++ * @a_attr_sel: selector to append. ++ * ++ * Appends an attribute selector to the current list of ++ * attribute selectors represented by a_this. ++ * Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_attr_sel_append_attr_sel (CRAttrSel * a_this, CRAttrSel * a_attr_sel) ++{ ++ CRAttrSel *cur_sel = NULL; ++ ++ g_return_val_if_fail (a_this && a_attr_sel, ++ CR_BAD_PARAM_ERROR); ++ ++ for (cur_sel = a_this; ++ cur_sel->next; ++ cur_sel = cur_sel->next) ; ++ ++ cur_sel->next = a_attr_sel; ++ a_attr_sel->prev = cur_sel; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_attr_sel_prepend_attr_sel: ++ *@a_this: the "this pointer" of the current instance *of #CRAttrSel. ++ *@a_attr_sel: the attribute selector to append. ++ * ++ *Prepends an attribute selector to the list of ++ *attributes selector represented by a_this. ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_attr_sel_prepend_attr_sel (CRAttrSel * a_this, ++ CRAttrSel * a_attr_sel) ++{ ++ g_return_val_if_fail (a_this && a_attr_sel, ++ CR_BAD_PARAM_ERROR); ++ ++ a_attr_sel->next = a_this; ++ a_this->prev = a_attr_sel; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_attr_sel_to_string: ++ * @a_this: the current instance of #CRAttrSel. ++ * ++ * Serializes an attribute selector into a string ++ * Returns the serialized attribute selector. ++ */ ++guchar * ++cr_attr_sel_to_string (CRAttrSel const * a_this) ++{ ++ CRAttrSel const *cur = NULL; ++ guchar *result = NULL; ++ GString *str_buf = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ str_buf = g_string_new (NULL); ++ ++ for (cur = a_this; cur; cur = cur->next) { ++ if (cur->prev) { ++ g_string_append_c (str_buf, ' '); ++ } ++ ++ if (cur->name) { ++ guchar *name = NULL; ++ ++ name = (guchar *) g_strndup (cur->name->stryng->str, ++ cur->name->stryng->len); ++ if (name) { ++ g_string_append (str_buf, (const gchar *) name); ++ g_free (name); ++ name = NULL; ++ } ++ } ++ ++ if (cur->value) { ++ guchar *value = NULL; ++ ++ value = (guchar *) g_strndup (cur->value->stryng->str, ++ cur->value->stryng->len); ++ if (value) { ++ switch (cur->match_way) { ++ case SET: ++ break; ++ ++ case EQUALS: ++ g_string_append_c (str_buf, '='); ++ break; ++ ++ case INCLUDES: ++ g_string_append (str_buf, "~="); ++ break; ++ ++ case DASHMATCH: ++ g_string_append (str_buf, "|="); ++ break; ++ ++ default: ++ break; ++ } ++ ++ g_string_append_printf ++ (str_buf, "\"%s\"", value); ++ ++ g_free (value); ++ value = NULL; ++ } ++ } ++ } ++ ++ if (str_buf) { ++ result = (guchar *) str_buf->str; ++ g_string_free (str_buf, FALSE); ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_attr_sel_dump: ++ * @a_this: the "this pointer" of the current instance of ++ * #CRAttrSel. ++ * @a_fp: the destination file. ++ * ++ * Dumps the current instance of #CRAttrSel to a file. ++ */ ++void ++cr_attr_sel_dump (CRAttrSel const * a_this, FILE * a_fp) ++{ ++ guchar *tmp_str = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ tmp_str = cr_attr_sel_to_string (a_this); ++ ++ if (tmp_str) { ++ fprintf (a_fp, "%s", tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++} ++ ++/** ++ *cr_attr_sel_destroy: ++ *@a_this: the "this pointer" of the current ++ *instance of #CRAttrSel. ++ * ++ *Destroys the current instance of #CRAttrSel. ++ *Frees all the fields if they are non null. ++ */ ++void ++cr_attr_sel_destroy (CRAttrSel * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ if (a_this->name) { ++ cr_string_destroy (a_this->name); ++ a_this->name = NULL; ++ } ++ ++ if (a_this->value) { ++ cr_string_destroy (a_this->value); ++ a_this->value = NULL; ++ } ++ ++ if (a_this->next) { ++ cr_attr_sel_destroy (a_this->next); ++ a_this->next = NULL; ++ } ++ ++ if (a_this) { ++ g_free (a_this); ++ a_this = NULL; ++ } ++} ++ +diff --git a/src/st/croco/cr-attr-sel.h b/src/st/croco/cr-attr-sel.h +new file mode 100644 +index 000000000..82d5a87d7 +--- /dev/null ++++ b/src/st/croco/cr-attr-sel.h +@@ -0,0 +1,74 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#ifndef __CR_ATTR_SEL_H__ ++#define __CR_ATTR_SEL_H__ ++ ++#include ++#include ++#include "cr-utils.h" ++#include "cr-parsing-location.h" ++#include "cr-string.h" ++ ++G_BEGIN_DECLS ++ ++ ++struct _CRAttrSel ; ++typedef struct _CRAttrSel CRAttrSel ; ++ ++enum AttrMatchWay ++{ ++ NO_MATCH = 0, ++ SET, ++ EQUALS, ++ INCLUDES, ++ DASHMATCH ++} ; ++ ++struct _CRAttrSel ++{ ++ CRString *name ; ++ CRString *value ; ++ enum AttrMatchWay match_way ; ++ CRAttrSel *next ; ++ CRAttrSel *prev ; ++ CRParsingLocation location ; ++} ; ++ ++CRAttrSel * cr_attr_sel_new (void) ; ++ ++enum CRStatus cr_attr_sel_append_attr_sel (CRAttrSel * a_this, ++ CRAttrSel *a_attr_sel) ; ++ ++enum CRStatus cr_attr_sel_prepend_attr_sel (CRAttrSel *a_this, ++ CRAttrSel *a_attr_sel) ; ++ ++guchar * cr_attr_sel_to_string (CRAttrSel const *a_this) ; ++ ++void cr_attr_sel_dump (CRAttrSel const *a_this, FILE *a_fp) ; ++ ++void cr_attr_sel_destroy (CRAttrSel *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_ATTR_SEL_H__*/ +diff --git a/src/st/croco/cr-cascade.c b/src/st/croco/cr-cascade.c +new file mode 100644 +index 000000000..b8f827716 +--- /dev/null ++++ b/src/st/croco/cr-cascade.c +@@ -0,0 +1,215 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * Copyright (C) 2002-2003 Dodji Seketeli ++ * ++ * 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$ ++ */ ++ ++#include ++#include "cr-cascade.h" ++ ++#define PRIVATE(a_this) ((a_this)->priv) ++ ++struct _CRCascadePriv { ++ /** ++ *the 3 style sheets of the cascade: ++ *author, user, and useragent sheet. ++ *Intended to be addressed by ++ *sheets[ORIGIN_AUTHOR] or sheets[ORIGIN_USER] ++ *of sheets[ORIGIN_UA] ; ++ */ ++ CRStyleSheet *sheets[3]; ++ guint ref_count; ++}; ++ ++/** ++ * cr_cascade_new: ++ *@a_author_sheet: the author origin style sheet. May be NULL. ++ *@a_user_sheet: the user origin style sheet. May be NULL. ++ *@a_ua_sheet: the user agent origin style sheet. May be NULL. ++ * ++ *Constructor of the #CRCascade class. ++ *Note that all three parameters of this ++ *method are ref counted and their refcount is increased. ++ *Their refcount will be decreased at the destruction of ++ *the instance of #CRCascade. ++ *So the caller should not call their destructor. The caller ++ *should call their ref/unref method instead if it wants ++ * ++ *Returns the newly built instance of CRCascade or NULL if ++ *an error arose during constrution. ++ */ ++CRCascade * ++cr_cascade_new (CRStyleSheet * a_author_sheet, ++ CRStyleSheet * a_user_sheet, CRStyleSheet * a_ua_sheet) ++{ ++ CRCascade *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRCascade)); ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (CRCascade)); ++ ++ PRIVATE (result) = g_try_malloc (sizeof (CRCascadePriv)); ++ if (!PRIVATE (result)) { ++ cr_utils_trace_info ("Out of memory"); ++ g_free (result); ++ return NULL; ++ } ++ memset (PRIVATE (result), 0, sizeof (CRCascadePriv)); ++ ++ if (a_author_sheet) { ++ cr_cascade_set_sheet (result, a_author_sheet, ORIGIN_AUTHOR); ++ } ++ if (a_user_sheet) { ++ cr_cascade_set_sheet (result, a_user_sheet, ORIGIN_USER); ++ } ++ if (a_ua_sheet) { ++ cr_cascade_set_sheet (result, a_ua_sheet, ORIGIN_UA); ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_cascade_get_sheet: ++ *@a_this: the current instance of #CRCascade. ++ *@a_origin: the origin of the style sheet as ++ *defined in the css2 spec in chapter 6.4. ++ *Gets a given origin sheet. ++ * ++ *Gets a sheet, part of the cascade. ++ *Note that the returned stylesheet ++ *is refcounted so if the caller wants ++ *to manage it's lifecycle, it must use ++ *cr_stylesheet_ref()/cr_stylesheet_unref() instead ++ *of the cr_stylesheet_destroy() method. ++ *Returns the style sheet, or NULL if it does not ++ *exist. ++ */ ++CRStyleSheet * ++cr_cascade_get_sheet (CRCascade * a_this, enum CRStyleOrigin a_origin) ++{ ++ g_return_val_if_fail (a_this ++ && a_origin >= ORIGIN_UA ++ && a_origin < NB_ORIGINS, NULL); ++ ++ return PRIVATE (a_this)->sheets[a_origin]; ++} ++ ++/** ++ * cr_cascade_set_sheet: ++ *@a_this: the current instance of #CRCascade. ++ *@a_sheet: the stylesheet to set. ++ *@a_origin: the origin of the stylesheet. ++ * ++ *Sets a stylesheet in the cascade ++ * ++ *Returns CR_OK upon successfull completion, an error ++ *code otherwise. ++ */ ++enum CRStatus ++cr_cascade_set_sheet (CRCascade * a_this, ++ CRStyleSheet * a_sheet, enum CRStyleOrigin a_origin) ++{ ++ g_return_val_if_fail (a_this ++ && a_sheet ++ && a_origin >= ORIGIN_UA ++ && a_origin < NB_ORIGINS, CR_BAD_PARAM_ERROR); ++ ++ if (PRIVATE (a_this)->sheets[a_origin]) ++ cr_stylesheet_unref (PRIVATE (a_this)->sheets[a_origin]); ++ PRIVATE (a_this)->sheets[a_origin] = a_sheet; ++ cr_stylesheet_ref (a_sheet); ++ a_sheet->origin = a_origin; ++ return CR_OK; ++} ++ ++/** ++ *cr_cascade_ref: ++ *@a_this: the current instance of #CRCascade ++ * ++ *Increases the reference counter of the current instance ++ *of #CRCascade. ++ */ ++void ++cr_cascade_ref (CRCascade * a_this) ++{ ++ g_return_if_fail (a_this && PRIVATE (a_this)); ++ ++ PRIVATE (a_this)->ref_count++; ++} ++ ++/** ++ * cr_cascade_unref: ++ *@a_this: the current instance of ++ *#CRCascade. ++ * ++ *Decrements the reference counter associated ++ *to this instance of #CRCascade. If the reference ++ *counter reaches zero, the instance is destroyed ++ *using cr_cascade_destroy() ++ */ ++void ++cr_cascade_unref (CRCascade * a_this) ++{ ++ g_return_if_fail (a_this && PRIVATE (a_this)); ++ ++ if (PRIVATE (a_this)->ref_count) ++ PRIVATE (a_this)->ref_count--; ++ if (!PRIVATE (a_this)->ref_count) { ++ cr_cascade_destroy (a_this); ++ } ++} ++ ++/** ++ * cr_cascade_destroy: ++ * @a_this: the current instance of #CRCascade ++ * ++ * Destructor of #CRCascade. ++ */ ++void ++cr_cascade_destroy (CRCascade * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ if (PRIVATE (a_this)) { ++ gulong i = 0; ++ ++ for (i = 0; PRIVATE (a_this)->sheets && i < NB_ORIGINS; i++) { ++ if (PRIVATE (a_this)->sheets[i]) { ++ if (cr_stylesheet_unref ++ (PRIVATE (a_this)->sheets[i]) ++ == TRUE) { ++ PRIVATE (a_this)->sheets[i] = NULL; ++ } ++ } ++ } ++ g_free (PRIVATE (a_this)); ++ PRIVATE (a_this) = NULL; ++ } ++ g_free (a_this); ++} +diff --git a/src/st/croco/cr-cascade.h b/src/st/croco/cr-cascade.h +new file mode 100644 +index 000000000..3119ae85f +--- /dev/null ++++ b/src/st/croco/cr-cascade.h +@@ -0,0 +1,74 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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$ ++ */ ++ ++#ifndef __CR_CASCADE_H__ ++#define __CR_CASCADE_H__ ++ ++#include "cr-stylesheet.h" ++ ++/** ++ *@file ++ *the declaration of the #CRCascade class. ++ */ ++ ++G_BEGIN_DECLS ++ ++ ++typedef struct _CRCascadePriv CRCascadePriv ; ++ ++/** ++ *An abstraction of the "Cascade" defined ++ *in the css2 spec, chapter 6.4. ++ */ ++typedef struct _CRCascade CRCascade ; ++ ++struct _CRCascade ++{ ++ CRCascadePriv *priv ; ++}; ++ ++ ++CRCascade * cr_cascade_new (CRStyleSheet *a_author_sheet, ++ CRStyleSheet *a_user_sheet, ++ CRStyleSheet *a_ua_sheet) ; ++ ++CRStyleSheet * cr_cascade_get_sheet (CRCascade *a_this, ++ enum CRStyleOrigin a_origin) ; ++ ++enum CRStatus cr_cascade_set_sheet (CRCascade *a_this, ++ CRStyleSheet *a_sheet, ++ enum CRStyleOrigin a_origin) ; ++ ++void cr_cascade_ref (CRCascade *a_this) ; ++ ++void cr_cascade_unref (CRCascade *a_this) ; ++ ++void cr_cascade_destroy (CRCascade *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_CASCADE_H__*/ +diff --git a/src/st/croco/cr-declaration.c b/src/st/croco/cr-declaration.c +new file mode 100644 +index 000000000..2a13c8266 +--- /dev/null ++++ b/src/st/croco/cr-declaration.c +@@ -0,0 +1,801 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli. ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++ ++#include ++#include "cr-declaration.h" ++#include "cr-statement.h" ++#include "cr-parser.h" ++ ++/** ++ *@CRDeclaration: ++ * ++ *The definition of the #CRDeclaration class. ++ */ ++ ++/** ++ * dump: ++ *@a_this: the current instance of #CRDeclaration. ++ *@a_fp: the destination file pointer. ++ *@a_indent: the number of indentation white char. ++ * ++ *Dumps (serializes) one css declaration to a file. ++ */ ++static void ++dump (CRDeclaration const * a_this, FILE * a_fp, glong a_indent) ++{ ++ guchar *str = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ str = (guchar *) cr_declaration_to_string (a_this, a_indent); ++ if (str) { ++ fprintf (a_fp, "%s", str); ++ g_free (str); ++ str = NULL; ++ } ++} ++ ++/** ++ * cr_declaration_new: ++ * @a_statement: the statement this declaration belongs to. can be NULL. ++ *@a_property: the property string of the declaration ++ *@a_value: the value expression of the declaration. ++ *Constructor of #CRDeclaration. ++ * ++ *Returns the newly built instance of #CRDeclaration, or NULL in ++ *case of error. ++ * ++ *The returned CRDeclaration takes ownership of @a_property and @a_value. ++ *(E.g. cr_declaration_destroy on this CRDeclaration will also free ++ *@a_property and @a_value.) ++ */ ++CRDeclaration * ++cr_declaration_new (CRStatement * a_statement, ++ CRString * a_property, CRTerm * a_value) ++{ ++ CRDeclaration *result = NULL; ++ ++ g_return_val_if_fail (a_property, NULL); ++ ++ if (a_statement) ++ g_return_val_if_fail (a_statement ++ && ((a_statement->type == RULESET_STMT) ++ || (a_statement->type ++ == AT_FONT_FACE_RULE_STMT) ++ || (a_statement->type ++ == AT_PAGE_RULE_STMT)), NULL); ++ ++ result = g_try_malloc (sizeof (CRDeclaration)); ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (CRDeclaration)); ++ result->property = a_property; ++ result->value = a_value; ++ ++ if (a_value) { ++ cr_term_ref (a_value); ++ } ++ result->parent_statement = a_statement; ++ return result; ++} ++ ++/** ++ * cr_declaration_parse_from_buf: ++ *@a_statement: the parent css2 statement of this ++ *this declaration. Must be non NULL and of type ++ *RULESET_STMT (must be a ruleset). ++ *@a_str: the string that contains the statement. ++ *@a_enc: the encoding of a_str. ++ * ++ *Parses a text buffer that contains ++ *a css declaration. ++ *Returns the parsed declaration, or NULL in case of error. ++ */ ++CRDeclaration * ++cr_declaration_parse_from_buf (CRStatement * a_statement, ++ const guchar * a_str, enum CREncoding a_enc) ++{ ++ enum CRStatus status = CR_OK; ++ CRTerm *value = NULL; ++ CRString *property = NULL; ++ CRDeclaration *result = NULL; ++ CRParser *parser = NULL; ++ gboolean important = FALSE; ++ ++ g_return_val_if_fail (a_str, NULL); ++ if (a_statement) ++ g_return_val_if_fail (a_statement->type == RULESET_STMT, ++ NULL); ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_str, strlen ((const char *) a_str), a_enc, FALSE); ++ g_return_val_if_fail (parser, NULL); ++ ++ status = cr_parser_try_to_skip_spaces_and_comments (parser); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ status = cr_parser_parse_declaration (parser, &property, ++ &value, &important); ++ if (status != CR_OK || !property) ++ goto cleanup; ++ ++ result = cr_declaration_new (a_statement, property, value); ++ if (result) { ++ property = NULL; ++ value = NULL; ++ result->important = important; ++ } ++ ++ cleanup: ++ ++ if (parser) { ++ cr_parser_destroy (parser); ++ parser = NULL; ++ } ++ ++ if (property) { ++ cr_string_destroy (property); ++ property = NULL; ++ } ++ ++ if (value) { ++ cr_term_destroy (value); ++ value = NULL; ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_declaration_parse_list_from_buf: ++ *@a_str: the input buffer that contains the list of declaration to ++ *parse. ++ *@a_enc: the encoding of a_str ++ * ++ *Parses a ';' separated list of properties declaration. ++ *Returns the parsed list of declaration, NULL if parsing failed. ++ */ ++CRDeclaration * ++cr_declaration_parse_list_from_buf (const guchar * a_str, ++ enum CREncoding a_enc) ++{ ++ ++ enum CRStatus status = CR_OK; ++ CRTerm *value = NULL; ++ CRString *property = NULL; ++ CRDeclaration *result = NULL, ++ *cur_decl = NULL; ++ CRParser *parser = NULL; ++ CRTknzr *tokenizer = NULL; ++ gboolean important = FALSE; ++ ++ g_return_val_if_fail (a_str, NULL); ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_str, strlen ((const char *) a_str), a_enc, FALSE); ++ g_return_val_if_fail (parser, NULL); ++ status = cr_parser_get_tknzr (parser, &tokenizer); ++ if (status != CR_OK || !tokenizer) { ++ if (status == CR_OK) ++ status = CR_ERROR; ++ goto cleanup; ++ } ++ status = cr_parser_try_to_skip_spaces_and_comments (parser); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ status = cr_parser_parse_declaration (parser, &property, ++ &value, &important); ++ if (status != CR_OK || !property) { ++ if (status != CR_OK) ++ status = CR_ERROR; ++ goto cleanup; ++ } ++ result = cr_declaration_new (NULL, property, value); ++ if (result) { ++ property = NULL; ++ value = NULL; ++ result->important = important; ++ } ++ /*now, go parse the other declarations */ ++ for (;;) { ++ guint32 c = 0; ++ ++ cr_parser_try_to_skip_spaces_and_comments (parser); ++ status = cr_tknzr_peek_char (tokenizer, &c); ++ if (status != CR_OK) { ++ if (status == CR_END_OF_INPUT_ERROR) ++ status = CR_OK; ++ goto cleanup; ++ } ++ if (c == ';') { ++ status = cr_tknzr_read_char (tokenizer, &c); ++ } else { ++ break; ++ } ++ important = FALSE; ++ cr_parser_try_to_skip_spaces_and_comments (parser); ++ status = cr_parser_parse_declaration (parser, &property, ++ &value, &important); ++ if (status != CR_OK || !property) { ++ if (status == CR_END_OF_INPUT_ERROR) { ++ status = CR_OK; ++ } ++ break; ++ } ++ cur_decl = cr_declaration_new (NULL, property, value); ++ if (cur_decl) { ++ cur_decl->important = important; ++ result = cr_declaration_append (result, cur_decl); ++ property = NULL; ++ value = NULL; ++ cur_decl = NULL; ++ } else { ++ break; ++ } ++ } ++ ++ cleanup: ++ ++ if (parser) { ++ cr_parser_destroy (parser); ++ parser = NULL; ++ } ++ ++ if (property) { ++ cr_string_destroy (property); ++ property = NULL; ++ } ++ ++ if (value) { ++ cr_term_destroy (value); ++ value = NULL; ++ } ++ ++ if (status != CR_OK && result) { ++ cr_declaration_destroy (result); ++ result = NULL; ++ } ++ return result; ++} ++ ++/** ++ * cr_declaration_append: ++ *@a_this: the current declaration list. ++ *@a_new: the declaration to append. ++ * ++ *Appends a new declaration to the current declarations list. ++ *Returns the declaration list with a_new appended to it, or NULL ++ *in case of error. ++ */ ++CRDeclaration * ++cr_declaration_append (CRDeclaration * a_this, CRDeclaration * a_new) ++{ ++ CRDeclaration *cur = NULL; ++ ++ g_return_val_if_fail (a_new, NULL); ++ ++ if (!a_this) ++ return a_new; ++ ++ for (cur = a_this; cur && cur->next; cur = cur->next) ; ++ ++ cur->next = a_new; ++ a_new->prev = cur; ++ ++ return a_this; ++} ++ ++/** ++ * cr_declaration_unlink: ++ *@a_decls: the declaration to unlink. ++ * ++ *Unlinks the declaration from the declaration list. ++ *case of a successfull completion, NULL otherwise. ++ * ++ *Returns a pointer to the unlinked declaration in ++ */ ++CRDeclaration * ++cr_declaration_unlink (CRDeclaration * a_decl) ++{ ++ CRDeclaration *result = a_decl; ++ ++ g_return_val_if_fail (result, NULL); ++ ++ /* ++ *some sanity checks first ++ */ ++ if (a_decl->prev) { ++ g_return_val_if_fail (a_decl->prev->next == a_decl, NULL); ++ ++ } ++ if (a_decl->next) { ++ g_return_val_if_fail (a_decl->next->prev == a_decl, NULL); ++ } ++ ++ /* ++ *now, the real unlinking job. ++ */ ++ if (a_decl->prev) { ++ a_decl->prev->next = a_decl->next; ++ } ++ if (a_decl->next) { ++ a_decl->next->prev = a_decl->prev; ++ } ++ if (a_decl->parent_statement) { ++ CRDeclaration **children_decl_ptr = NULL; ++ ++ switch (a_decl->parent_statement->type) { ++ case RULESET_STMT: ++ if (a_decl->parent_statement->kind.ruleset) { ++ children_decl_ptr = ++ &a_decl->parent_statement-> ++ kind.ruleset->decl_list; ++ } ++ ++ break; ++ ++ case AT_FONT_FACE_RULE_STMT: ++ if (a_decl->parent_statement->kind.font_face_rule) { ++ children_decl_ptr = ++ &a_decl->parent_statement-> ++ kind.font_face_rule->decl_list; ++ } ++ break; ++ case AT_PAGE_RULE_STMT: ++ if (a_decl->parent_statement->kind.page_rule) { ++ children_decl_ptr = ++ &a_decl->parent_statement-> ++ kind.page_rule->decl_list; ++ } ++ ++ default: ++ break; ++ } ++ if (children_decl_ptr ++ && *children_decl_ptr && *children_decl_ptr == a_decl) ++ *children_decl_ptr = (*children_decl_ptr)->next; ++ } ++ ++ a_decl->next = NULL; ++ a_decl->prev = NULL; ++ a_decl->parent_statement = NULL; ++ ++ return result; ++} ++ ++/** ++ * cr_declaration_prepend: ++ * @a_this: the current declaration list. ++ * @a_new: the declaration to prepend. ++ * ++ * prepends a declaration to the current declaration list. ++ * ++ * Returns the list with a_new prepended or NULL in case of error. ++ */ ++CRDeclaration * ++cr_declaration_prepend (CRDeclaration * a_this, CRDeclaration * a_new) ++{ ++ CRDeclaration *cur = NULL; ++ ++ g_return_val_if_fail (a_new, NULL); ++ ++ if (!a_this) ++ return a_new; ++ ++ a_this->prev = a_new; ++ a_new->next = a_this; ++ ++ for (cur = a_new; cur && cur->prev; cur = cur->prev) ; ++ ++ return cur; ++} ++ ++/** ++ * cr_declaration_append2: ++ *@a_this: the current declaration list. ++ *@a_prop: the property string of the declaration to append. ++ *@a_value: the value of the declaration to append. ++ * ++ *Appends a declaration to the current declaration list. ++ *Returns the list with the new property appended to it, or NULL in ++ *case of an error. ++ */ ++CRDeclaration * ++cr_declaration_append2 (CRDeclaration * a_this, ++ CRString * a_prop, CRTerm * a_value) ++{ ++ CRDeclaration *new_elem = NULL; ++ ++ if (a_this) { ++ new_elem = cr_declaration_new (a_this->parent_statement, ++ a_prop, a_value); ++ } else { ++ new_elem = cr_declaration_new (NULL, a_prop, a_value); ++ } ++ ++ g_return_val_if_fail (new_elem, NULL); ++ ++ return cr_declaration_append (a_this, new_elem); ++} ++ ++/** ++ * cr_declaration_dump: ++ *@a_this: the current instance of #CRDeclaration. ++ *@a_fp: the destination file. ++ *@a_indent: the number of indentation white char. ++ *@a_one_per_line: whether to put one declaration per line of not . ++ * ++ * ++ *Dumps a declaration list to a file. ++ */ ++void ++cr_declaration_dump (CRDeclaration const * a_this, FILE * a_fp, glong a_indent, ++ gboolean a_one_per_line) ++{ ++ CRDeclaration const *cur = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ for (cur = a_this; cur; cur = cur->next) { ++ if (cur->prev) { ++ if (a_one_per_line == TRUE) ++ fprintf (a_fp, ";\n"); ++ else ++ fprintf (a_fp, "; "); ++ } ++ dump (cur, a_fp, a_indent); ++ } ++} ++ ++/** ++ * cr_declaration_dump_one: ++ *@a_this: the current instance of #CRDeclaration. ++ *@a_fp: the destination file. ++ *@a_indent: the number of indentation white char. ++ * ++ *Dumps the first declaration of the declaration list to a file. ++ */ ++void ++cr_declaration_dump_one (CRDeclaration const * a_this, FILE * a_fp, glong a_indent) ++{ ++ g_return_if_fail (a_this); ++ ++ dump (a_this, a_fp, a_indent); ++} ++ ++/** ++ * cr_declaration_to_string: ++ *@a_this: the current instance of #CRDeclaration. ++ *@a_indent: the number of indentation white char ++ *to put before the actual serialisation. ++ * ++ *Serializes the declaration into a string ++ *Returns the serialized form the declaration. The caller must ++ *free the string using g_free(). ++ */ ++gchar * ++cr_declaration_to_string (CRDeclaration const * a_this, gulong a_indent) ++{ ++ GString *stringue = NULL; ++ ++ gchar *str = NULL, ++ *result = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ stringue = g_string_new (NULL); ++ ++ if (a_this->property ++ && a_this->property->stryng ++ && a_this->property->stryng->str) { ++ str = g_strndup (a_this->property->stryng->str, ++ a_this->property->stryng->len); ++ if (str) { ++ cr_utils_dump_n_chars2 (' ', stringue, ++ a_indent); ++ g_string_append (stringue, str); ++ g_free (str); ++ str = NULL; ++ } else ++ goto error; ++ ++ if (a_this->value) { ++ guchar *value_str = NULL; ++ ++ value_str = cr_term_to_string (a_this->value); ++ if (value_str) { ++ g_string_append_printf (stringue, " : %s", ++ value_str); ++ g_free (value_str); ++ } else ++ goto error; ++ } ++ if (a_this->important == TRUE) { ++ g_string_append_printf (stringue, " %s", ++ "!important"); ++ } ++ } ++ if (stringue && stringue->str) { ++ result = stringue->str; ++ g_string_free (stringue, FALSE); ++ } ++ return result; ++ ++ error: ++ if (stringue) { ++ g_string_free (stringue, TRUE); ++ stringue = NULL; ++ } ++ if (str) { ++ g_free (str); ++ str = NULL; ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_declaration_list_to_string: ++ *@a_this: the current instance of #CRDeclaration. ++ *@a_indent: the number of indentation white char ++ *to put before the actual serialisation. ++ * ++ *Serializes the declaration list into a string ++ */ ++guchar * ++cr_declaration_list_to_string (CRDeclaration const * a_this, gulong a_indent) ++{ ++ CRDeclaration const *cur = NULL; ++ GString *stringue = NULL; ++ guchar *str = NULL, ++ *result = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ stringue = g_string_new (NULL); ++ ++ for (cur = a_this; cur; cur = cur->next) { ++ str = (guchar *) cr_declaration_to_string (cur, a_indent); ++ if (str) { ++ g_string_append_printf (stringue, "%s;", str); ++ g_free (str); ++ } else ++ break; ++ } ++ if (stringue && stringue->str) { ++ result = (guchar *) stringue->str; ++ g_string_free (stringue, FALSE); ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_declaration_list_to_string2: ++ *@a_this: the current instance of #CRDeclaration. ++ *@a_indent: the number of indentation white char ++ *@a_one_decl_per_line: whether to output one doc per line or not. ++ *to put before the actual serialisation. ++ * ++ *Serializes the declaration list into a string ++ *Returns the serialized form the declararation. ++ */ ++guchar * ++cr_declaration_list_to_string2 (CRDeclaration const * a_this, ++ gulong a_indent, gboolean a_one_decl_per_line) ++{ ++ CRDeclaration const *cur = NULL; ++ GString *stringue = NULL; ++ guchar *str = NULL, ++ *result = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ stringue = g_string_new (NULL); ++ ++ for (cur = a_this; cur; cur = cur->next) { ++ str = (guchar *) cr_declaration_to_string (cur, a_indent); ++ if (str) { ++ if (a_one_decl_per_line == TRUE) { ++ if (cur->next) ++ g_string_append_printf (stringue, ++ "%s;\n", str); ++ else ++ g_string_append (stringue, ++ (const gchar *) str); ++ } else { ++ if (cur->next) ++ g_string_append_printf (stringue, ++ "%s;", str); ++ else ++ g_string_append (stringue, ++ (const gchar *) str); ++ } ++ g_free (str); ++ } else ++ break; ++ } ++ if (stringue && stringue->str) { ++ result = (guchar *) stringue->str; ++ g_string_free (stringue, FALSE); ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_declaration_nr_props: ++ *@a_this: the current instance of #CRDeclaration. ++ *Return the number of properties in the declaration ++ */ ++gint ++cr_declaration_nr_props (CRDeclaration const * a_this) ++{ ++ CRDeclaration const *cur = NULL; ++ int nr = 0; ++ ++ g_return_val_if_fail (a_this, -1); ++ ++ for (cur = a_this; cur; cur = cur->next) ++ nr++; ++ return nr; ++} ++ ++/** ++ * cr_declaration_get_from_list: ++ *@a_this: the current instance of #CRDeclaration. ++ *@itemnr: the index into the declaration list. ++ * ++ *Use an index to get a CRDeclaration from the declaration list. ++ * ++ *Returns #CRDeclaration at position itemnr, ++ *if itemnr > number of declarations - 1, ++ *it will return NULL. ++ */ ++CRDeclaration * ++cr_declaration_get_from_list (CRDeclaration * a_this, int itemnr) ++{ ++ CRDeclaration *cur = NULL; ++ int nr = 0; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ for (cur = a_this; cur; cur = cur->next) ++ if (nr++ == itemnr) ++ return cur; ++ return NULL; ++} ++ ++/** ++ * cr_declaration_get_by_prop_name: ++ *@a_this: the current instance of #CRDeclaration. ++ *@a_prop: the property name to search for. ++ * ++ *Use property name to get a CRDeclaration from the declaration list. ++ *Returns #CRDeclaration with property name a_prop, or NULL if not found. ++ */ ++CRDeclaration * ++cr_declaration_get_by_prop_name (CRDeclaration * a_this, ++ const guchar * a_prop) ++{ ++ CRDeclaration *cur = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ g_return_val_if_fail (a_prop, NULL); ++ ++ for (cur = a_this; cur; cur = cur->next) { ++ if (cur->property ++ && cur->property->stryng ++ && cur->property->stryng->str) { ++ if (!strcmp (cur->property->stryng->str, ++ (const char *) a_prop)) { ++ return cur; ++ } ++ } ++ } ++ return NULL; ++} ++ ++/** ++ * cr_declaration_ref: ++ *@a_this: the current instance of #CRDeclaration. ++ * ++ *Increases the ref count of the current instance of #CRDeclaration. ++ */ ++void ++cr_declaration_ref (CRDeclaration * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ a_this->ref_count++; ++} ++ ++/** ++ * cr_declaration_unref: ++ *@a_this: the current instance of #CRDeclaration. ++ * ++ *Decrements the ref count of the current instance of #CRDeclaration. ++ *If the ref count reaches zero, the current instance of #CRDeclaration ++ *if destroyed. ++ *Returns TRUE if @a_this was destroyed (ref count reached zero), ++ *FALSE otherwise. ++ */ ++gboolean ++cr_declaration_unref (CRDeclaration * a_this) ++{ ++ g_return_val_if_fail (a_this, FALSE); ++ ++ if (a_this->ref_count) { ++ a_this->ref_count--; ++ } ++ ++ if (a_this->ref_count == 0) { ++ cr_declaration_destroy (a_this); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++/** ++ * cr_declaration_destroy: ++ *@a_this: the current instance of #CRDeclaration. ++ * ++ *Destructor of the declaration list. ++ */ ++void ++cr_declaration_destroy (CRDeclaration * a_this) ++{ ++ CRDeclaration *cur = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ /* ++ * Go to the last element of the list. ++ */ ++ for (cur = a_this; cur->next; cur = cur->next) ++ g_assert (cur->next->prev == cur); ++ ++ /* ++ * Walk backward the list and free each "next" element. ++ * Meanwhile, free each property/value pair contained in the list. ++ */ ++ for (; cur; cur = cur->prev) { ++ g_free (cur->next); ++ cur->next = NULL; ++ ++ if (cur->property) { ++ cr_string_destroy (cur->property); ++ cur->property = NULL; ++ } ++ ++ if (cur->value) { ++ cr_term_destroy (cur->value); ++ cur->value = NULL; ++ } ++ } ++ ++ g_free (a_this); ++} +diff --git a/src/st/croco/cr-declaration.h b/src/st/croco/cr-declaration.h +new file mode 100644 +index 000000000..eee8be321 +--- /dev/null ++++ b/src/st/croco/cr-declaration.h +@@ -0,0 +1,136 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * See the COPYRIGHTS file for copyright information. ++ */ ++ ++#ifndef __CR_DECLARATION_H__ ++#define __CR_DECLARATION_H__ ++ ++#include ++#include "cr-utils.h" ++#include "cr-term.h" ++#include "cr-parsing-location.h" ++ ++G_BEGIN_DECLS ++ ++/** ++ *@file ++ *The declaration of the #CRDeclaration class. ++ */ ++ ++/*forward declaration of what is defined in cr-statement.h*/ ++typedef struct _CRStatement CRStatement ; ++ ++/** ++ *The abstraction of a css declaration defined by the ++ *css2 spec in chapter 4. ++ *It is actually a chained list of property/value pairs. ++ */ ++typedef struct _CRDeclaration CRDeclaration ; ++struct _CRDeclaration ++{ ++ /**The property.*/ ++ CRString *property ; ++ ++ /**The value of the property.*/ ++ CRTerm *value ; ++ ++ /*the ruleset that contains this declaration*/ ++ CRStatement *parent_statement ; ++ ++ /*the next declaration*/ ++ CRDeclaration *next ; ++ ++ /*the previous one declaration*/ ++ CRDeclaration *prev ; ++ ++ /*does the declaration have the important keyword ?*/ ++ gboolean important ; ++ ++ glong ref_count ; ++ ++ CRParsingLocation location ; ++ /*reserved for future usage*/ ++ gpointer rfu0 ; ++ gpointer rfu1 ; ++ gpointer rfu2 ; ++ gpointer rfu3 ; ++} ; ++ ++ ++CRDeclaration * cr_declaration_new (CRStatement *a_statement, ++ CRString *a_property, ++ CRTerm *a_value) ; ++ ++ ++CRDeclaration * cr_declaration_parse_from_buf (CRStatement *a_statement, ++ const guchar *a_str, ++ enum CREncoding a_enc) ; ++ ++CRDeclaration * cr_declaration_parse_list_from_buf (const guchar *a_str, ++ enum CREncoding a_enc) ; ++ ++CRDeclaration * cr_declaration_append (CRDeclaration *a_this, ++ CRDeclaration *a_new) ; ++ ++CRDeclaration * cr_declaration_append2 (CRDeclaration *a_this, ++ CRString *a_prop, ++ CRTerm *a_value) ; ++ ++CRDeclaration * cr_declaration_prepend (CRDeclaration *a_this, ++ CRDeclaration *a_new) ; ++ ++CRDeclaration * cr_declaration_unlink (CRDeclaration * a_decl) ; ++ ++void ++cr_declaration_dump (CRDeclaration const *a_this, ++ FILE *a_fp, glong a_indent, ++ gboolean a_one_per_line) ; ++ ++void cr_declaration_dump_one (CRDeclaration const *a_this, ++ FILE *a_fp, glong a_indent) ; ++ ++gint cr_declaration_nr_props (CRDeclaration const *a_this) ; ++ ++CRDeclaration * cr_declaration_get_from_list (CRDeclaration *a_this, ++ int itemnr) ; ++ ++CRDeclaration * cr_declaration_get_by_prop_name (CRDeclaration *a_this, ++ const guchar *a_str) ; ++ ++gchar * cr_declaration_to_string (CRDeclaration const *a_this, ++ gulong a_indent) ; ++ ++guchar * cr_declaration_list_to_string (CRDeclaration const *a_this, ++ gulong a_indent) ; ++ ++guchar * cr_declaration_list_to_string2 (CRDeclaration const *a_this, ++ gulong a_indent, ++ gboolean a_one_decl_per_line) ; ++ ++void cr_declaration_ref (CRDeclaration *a_this) ; ++ ++gboolean cr_declaration_unref (CRDeclaration *a_this) ; ++ ++void cr_declaration_destroy (CRDeclaration *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_DECLARATION_H__*/ +diff --git a/src/st/croco/cr-doc-handler.c b/src/st/croco/cr-doc-handler.c +new file mode 100644 +index 000000000..bbb158298 +--- /dev/null ++++ b/src/st/croco/cr-doc-handler.c +@@ -0,0 +1,276 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * See COPRYRIGHTS file for copyright information. ++ */ ++ ++#include ++#include "cr-doc-handler.h" ++#include "cr-parser.h" ++ ++/** ++ *@CRDocHandler: ++ * ++ *The definition of the CRDocHandler class. ++ *Contains methods to instantiate, destroy, ++ *and initialyze instances of #CRDocHandler ++ *to custom values. ++ */ ++ ++#define PRIVATE(obj) (obj)->priv ++ ++struct _CRDocHandlerPriv { ++ /** ++ *This pointer is to hold an application parsing context. ++ *For example, it used by the Object Model parser to ++ *store it parsing context. #CRParser does not touch it, but ++ *#CROMParser does. #CROMParser allocates this pointer at ++ *the beginning of the css document, and frees it at the end ++ *of the document. ++ */ ++ gpointer context; ++ ++ /** ++ *The place where #CROMParser puts the result of its parsing, if ++ *any. ++ */ ++ gpointer result; ++ /** ++ *a pointer to the parser used to parse ++ *the current document. ++ */ ++ CRParser *parser ; ++}; ++ ++/** ++ * cr_doc_handler_new: ++ *Constructor of #CRDocHandler. ++ * ++ *Returns the newly built instance of ++ *#CRDocHandler ++ * ++ */ ++CRDocHandler * ++cr_doc_handler_new (void) ++{ ++ CRDocHandler *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRDocHandler)); ++ ++ g_return_val_if_fail (result, NULL); ++ ++ memset (result, 0, sizeof (CRDocHandler)); ++ result->ref_count++; ++ ++ result->priv = g_try_malloc (sizeof (CRDocHandlerPriv)); ++ if (!result->priv) { ++ cr_utils_trace_info ("Out of memory exception"); ++ g_free (result); ++ return NULL; ++ } ++ ++ cr_doc_handler_set_default_sac_handler (result); ++ ++ return result; ++} ++ ++/** ++ * cr_doc_handler_get_ctxt: ++ *@a_this: the current instance of #CRDocHandler. ++ *@a_ctxt: out parameter. The new parsing context. ++ * ++ *Gets the private parsing context associated to the document handler ++ *The private parsing context is used by libcroco only. ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_doc_handler_get_ctxt (CRDocHandler const * a_this, gpointer * a_ctxt) ++{ ++ g_return_val_if_fail (a_this && a_this->priv, CR_BAD_PARAM_ERROR); ++ ++ *a_ctxt = a_this->priv->context; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_doc_handler_set_ctxt: ++ *@a_this: the current instance of #CRDocHandler ++ *@a_ctxt: a pointer to the parsing context. ++ * ++ *Sets the private parsing context. ++ *This is used by libcroco only. ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_doc_handler_set_ctxt (CRDocHandler * a_this, gpointer a_ctxt) ++{ ++ g_return_val_if_fail (a_this && a_this->priv, CR_BAD_PARAM_ERROR); ++ a_this->priv->context = a_ctxt; ++ return CR_OK; ++} ++ ++/** ++ * cr_doc_handler_get_result: ++ *@a_this: the current instance of #CRDocHandler ++ *@a_result: out parameter. The returned result. ++ * ++ *Gets the private parsing result. ++ *The private parsing result is used by libcroco only. ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_doc_handler_get_result (CRDocHandler const * a_this, gpointer * a_result) ++{ ++ g_return_val_if_fail (a_this && a_this->priv, CR_BAD_PARAM_ERROR); ++ ++ *a_result = a_this->priv->result; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_doc_handler_set_result: ++ *@a_this: the current instance of #CRDocHandler ++ *@a_result: the new result. ++ * ++ *Sets the private parsing context. ++ *This is used by libcroco only. ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_doc_handler_set_result (CRDocHandler * a_this, gpointer a_result) ++{ ++ g_return_val_if_fail (a_this && a_this->priv, CR_BAD_PARAM_ERROR); ++ a_this->priv->result = a_result; ++ return CR_OK; ++} ++ ++/** ++ *cr_doc_handler_set_default_sac_handler: ++ *@a_this: a pointer to the current instance of #CRDocHandler. ++ * ++ *Sets the sac handlers contained in the current ++ *instance of DocHandler to the default handlers. ++ *For the time being the default handlers are ++ *test handlers. This is expected to change in a ++ *near future, when the libcroco gets a bit debugged. ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_doc_handler_set_default_sac_handler (CRDocHandler * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ a_this->start_document = NULL; ++ a_this->end_document = NULL; ++ a_this->import_style = NULL; ++ a_this->namespace_declaration = NULL; ++ a_this->comment = NULL; ++ a_this->start_selector = NULL; ++ a_this->end_selector = NULL; ++ a_this->property = NULL; ++ a_this->start_font_face = NULL; ++ a_this->end_font_face = NULL; ++ a_this->start_media = NULL; ++ a_this->end_media = NULL; ++ a_this->start_page = NULL; ++ a_this->end_page = NULL; ++ a_this->ignorable_at_rule = NULL; ++ a_this->error = NULL; ++ a_this->unrecoverable_error = NULL; ++ return CR_OK; ++} ++ ++/** ++ * cr_doc_handler_ref: ++ *@a_this: the current instance of #CRDocHandler. ++ */ ++void ++cr_doc_handler_ref (CRDocHandler * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ a_this->ref_count++; ++} ++ ++/** ++ * cr_doc_handler_unref: ++ *@a_this: the currrent instance of #CRDocHandler. ++ * ++ *Decreases the ref count of the current instance of #CRDocHandler. ++ *If the ref count reaches '0' then, destroys the instance. ++ * ++ *Returns TRUE if the instance as been destroyed, FALSE otherwise. ++ */ ++gboolean ++cr_doc_handler_unref (CRDocHandler * a_this) ++{ ++ g_return_val_if_fail (a_this, FALSE); ++ ++ if (a_this->ref_count > 0) { ++ a_this->ref_count--; ++ } ++ ++ if (a_this->ref_count == 0) { ++ cr_doc_handler_destroy (a_this); ++ return TRUE; ++ } ++ return FALSE ; ++} ++ ++/** ++ * cr_doc_handler_destroy: ++ *@a_this: the instance of #CRDocHandler to ++ *destroy. ++ * ++ *The destructor of the #CRDocHandler class. ++ */ ++void ++cr_doc_handler_destroy (CRDocHandler * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ if (a_this->priv) { ++ g_free (a_this->priv); ++ a_this->priv = NULL; ++ } ++ g_free (a_this); ++} ++ ++/** ++ * cr_doc_handler_associate_a_parser: ++ *Associates a parser to the current document handler ++ * ++ *@a_this: the current instance of document handler. ++ *@a_parser: the parser to associate. ++ */ ++void ++cr_doc_handler_associate_a_parser (CRDocHandler *a_this, ++ gpointer a_parser) ++{ ++ g_return_if_fail (a_this && PRIVATE (a_this) ++ && a_parser) ; ++ ++ PRIVATE (a_this)->parser = a_parser ; ++} +diff --git a/src/st/croco/cr-doc-handler.h b/src/st/croco/cr-doc-handler.h +new file mode 100644 +index 000000000..d12673f31 +--- /dev/null ++++ b/src/st/croco/cr-doc-handler.h +@@ -0,0 +1,298 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * See the COPYRIGHTS file for copyright information. ++ */ ++ ++#ifndef __CR_DOC_HANDLER_H__ ++#define __CR_DOC_HANDLER_H__ ++ ++/** ++ *@file ++ *The declaration of the #CRDocumentHandler class. ++ *This class is actually the parsing events handler. ++ */ ++ ++#include ++#include "cr-utils.h" ++#include "cr-input.h" ++#include "cr-stylesheet.h" ++ ++G_BEGIN_DECLS ++ ++ ++typedef struct _CRDocHandler CRDocHandler ; ++ ++struct _CRDocHandlerPriv ; ++typedef struct _CRDocHandlerPriv CRDocHandlerPriv ; ++ ++ ++/** ++ *The SAC document handler. ++ *An instance of this class is to ++ *be passed to a parser. Then, during the parsing ++ *the parser calls the convenient function pointer ++ *whenever a particular event (a css construction) occurs. ++ */ ++struct _CRDocHandler ++{ ++ CRDocHandlerPriv *priv ; ++ ++ /** ++ *This pointer is to be used by the application for ++ *it custom needs. It is there to extend the doc handler. ++ */ ++ gpointer app_data ; ++ ++ /** ++ *Is called at the beginning of the parsing of the document. ++ *@param a_this a pointer to the current instance of ++ *#CRDocHandler. ++ */ ++ void (*start_document) (CRDocHandler *a_this) ; ++ ++ /** ++ *Is called to notify the end of the parsing of the document. ++ *@param a_this a pointer to the current instance of ++ *#CRDocHandler. ++ */ ++ void (*end_document) (CRDocHandler *a_this) ; ++ ++ /** ++ *Is called to notify an at charset rule. ++ *@param a_this the document handler. ++ *@param a_charset the declared charset. ++ */ ++ void (*charset) (CRDocHandler *a_this, ++ CRString *a_charset, ++ CRParsingLocation *a_charset_sym_location) ; ++ ++ /** ++ *Is called to notify an import statement in ++ *the stylesheet. ++ *@param a_this the current instance of #CRDocHandler. ++ *@param a_media_list a doubly linked list of GString objects. ++ *Each GString object contains a string which is the ++ *destination media for style information. ++ *@param a_uri the uri of the imported style sheet. ++ *@param a_uri_default_ns the default namespace of URI ++ *@param a_location the parsing location of the '\@import' ++ *keyword. ++ *of the imported style sheet. ++ */ ++ void (*import_style) (CRDocHandler *a_this, ++ GList *a_media_list, ++ CRString *a_uri, ++ CRString *a_uri_default_ns, ++ CRParsingLocation *a_location) ; ++ ++ void (*import_style_result) (CRDocHandler *a_this, ++ GList *a_media_list, ++ CRString *a_uri, ++ CRString *a_uri_default_ns, ++ CRStyleSheet *a_sheet) ; ++ ++ /** ++ *Is called to notify a namespace declaration. ++ *Not used yet. ++ *@param a_this the current instance of #CRDocHandler. ++ *@param a_prefix the prefix of the namespace. ++ *@param a_uri the uri of the namespace. ++ *@param a_location the location of the "@namespace" keyword. ++ */ ++ void (*namespace_declaration) (CRDocHandler *a_this, ++ CRString *a_prefix, ++ CRString *a_uri, ++ CRParsingLocation *a_location) ; ++ ++ /** ++ *Is called to notify a comment. ++ *@param a_this a pointer to the current instance ++ *of #CRDocHandler. ++ *@param a_comment the comment. ++ */ ++ void (*comment) (CRDocHandler *a_this, ++ CRString *a_comment) ; ++ ++ /** ++ *Is called to notify the beginning of a rule ++ *statement. ++ *@param a_this the current instance of #CRDocHandler. ++ *@param a_selector_list the list of selectors that precedes ++ *the rule declarations. ++ */ ++ void (*start_selector) (CRDocHandler * a_this, ++ CRSelector *a_selector_list) ; ++ ++ /** ++ *Is called to notify the end of a rule statement. ++ *@param a_this the current instance of #CRDocHandler. ++ *@param a_selector_list the list of selectors that precedes ++ *the rule declarations. This pointer is the same as ++ *the one passed to start_selector() ; ++ */ ++ void (*end_selector) (CRDocHandler *a_this, ++ CRSelector *a_selector_list) ; ++ ++ ++ /** ++ *Is called to notify a declaration. ++ *@param a_this a pointer to the current instance ++ *of #CRDocHandler. ++ *@param a_name the name of the parsed property. ++ *@param a_expression a css expression that represents ++ *the value of the property. A css expression is ++ *actually a linked list of 'terms'. Each term can ++ *be linked to other using operators. ++ * ++ */ ++ void (*property) (CRDocHandler *a_this, ++ CRString *a_name, ++ CRTerm *a_expression, ++ gboolean a_is_important) ; ++ /** ++ *Is called to notify the start of a font face statement. ++ *The parser invokes this method at the beginning of every ++ *font face statement in the style sheet. There will ++ *be a corresponding end_font_face () event for every ++ *start_font_face () event. ++ * ++ *@param a_this a pointer to the current instance of ++ *#CRDocHandler. ++ *@param a_location the parsing location of the "\@font-face" ++ *keyword. ++ */ ++ void (*start_font_face) (CRDocHandler *a_this, ++ CRParsingLocation *a_location) ; ++ ++ /** ++ *Is called to notify the end of a font face statement. ++ *@param a_this a pointer to the current instance of ++ *#CRDocHandler. ++ */ ++ void (*end_font_face) (CRDocHandler *a_this) ; ++ ++ ++ /** ++ *Is called to notify the beginning of a media statement. ++ *The parser will invoke this method at the beginning of ++ *every media statement in the style sheet. There will be ++ *a corresponding end_media() event for every start_media() ++ *event. ++ *@param a_this a pointer to the current instance of ++ *#CRDocHandler. ++ *@param a_media_list a double linked list of ++ #CRString * objects. ++ *Each CRString objects is actually a destination media for ++ *the style information. ++ */ ++ void (*start_media) (CRDocHandler *a_this, ++ GList *a_media_list, ++ CRParsingLocation *a_location) ; ++ ++ /** ++ *Is called to notify the end of a media statement. ++ *@param a_this a pointer to the current instance ++ *of #CRDocHandler. ++ *@param a_media_list a double linked list of GString * objects. ++ *Each GString objects is actually a destination media for ++ *the style information. ++ */ ++ void (*end_media) (CRDocHandler *a_this, ++ GList *a_media_list) ; ++ ++ /** ++ *Is called to notify the beginning of a page statement. ++ *The parser invokes this function at the beginning of ++ *every page statement in the style sheet. There will be ++ *a corresponding end_page() event for every single ++ *start_page() event. ++ *@param a_this a pointer to the current instance of ++ *#CRDocHandler. ++ *@param a_name the name of the page (if any, null otherwise). ++ *@param a_pseudo_page the pseudo page (if any, null otherwise). ++ *@param a_location the parsing location of the "\@page" keyword. ++ */ ++ void (*start_page) (CRDocHandler *a_this, ++ CRString *a_name, ++ CRString *a_pseudo_page, ++ CRParsingLocation *a_location) ; ++ ++ /** ++ *Is called to notify the end of a page statement. ++ *@param a_this a pointer to the current instance of ++ *#CRDocHandler. ++ *@param a_name the name of the page (if any, null otherwise). ++ *@param a_pseudo_page the pseudo page (if any, null otherwise). ++ */ ++ void (*end_page) (CRDocHandler *a_this, ++ CRString *a_name, ++ CRString *pseudo_page) ; ++ ++ /** ++ *Is Called to notify an unknown at-rule not supported ++ *by this parser. ++ */ ++ void (*ignorable_at_rule) (CRDocHandler *a_this, ++ CRString *a_name) ; ++ ++ /** ++ *Is called to notify a parsing error. After this error ++ *the application must ignore the rule being parsed, if ++ *any. After completion of this callback, ++ *the parser will then try to resume the parsing, ++ *ignoring the current error. ++ */ ++ void (*error) (CRDocHandler *a_this) ; ++ ++ /** ++ *Is called to notify an unrecoverable parsing error. ++ *This is the place to put emergency routines that free allocated ++ *resources. ++ */ ++ void (*unrecoverable_error) (CRDocHandler *a_this) ; ++ ++ gboolean resolve_import ; ++ gulong ref_count ; ++} ; ++ ++CRDocHandler * cr_doc_handler_new (void) ; ++ ++enum CRStatus cr_doc_handler_set_result (CRDocHandler *a_this, gpointer a_result) ; ++ ++enum CRStatus cr_doc_handler_get_result (CRDocHandler const *a_this, gpointer * a_result) ; ++ ++enum CRStatus cr_doc_handler_set_ctxt (CRDocHandler *a_this, gpointer a_ctxt) ; ++ ++enum CRStatus cr_doc_handler_get_ctxt (CRDocHandler const *a_this, gpointer * a_ctxt) ; ++ ++enum CRStatus cr_doc_handler_set_default_sac_handler (CRDocHandler *a_this) ; ++ ++void cr_doc_handler_associate_a_parser (CRDocHandler *a_this, ++ gpointer a_parser) ; ++ ++void cr_doc_handler_ref (CRDocHandler *a_this) ; ++ ++gboolean cr_doc_handler_unref (CRDocHandler *a_this) ; ++ ++void cr_doc_handler_destroy (CRDocHandler *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_DOC_HANDLER_H__*/ +diff --git a/src/st/croco/cr-enc-handler.c b/src/st/croco/cr-enc-handler.c +new file mode 100644 +index 000000000..a7c4269ad +--- /dev/null ++++ b/src/st/croco/cr-enc-handler.c +@@ -0,0 +1,184 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * Copyright (C) 2002-2003 Dodji Seketeli ++ * ++ * 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 #CREncHandler class. ++ */ ++ ++#include "cr-enc-handler.h" ++#include "cr-utils.h" ++ ++#include ++ ++struct CREncAlias { ++ const gchar *name; ++ enum CREncoding encoding; ++}; ++ ++static struct CREncAlias gv_default_aliases[] = { ++ {"UTF-8", CR_UTF_8}, ++ {"UTF_8", CR_UTF_8}, ++ {"UTF8", CR_UTF_8}, ++ {"UTF-16", CR_UTF_16}, ++ {"UTF_16", CR_UTF_16}, ++ {"UTF16", CR_UTF_16}, ++ {"UCS1", CR_UCS_1}, ++ {"UCS-1", CR_UCS_1}, ++ {"UCS_1", CR_UCS_1}, ++ {"ISO-8859-1", CR_UCS_1}, ++ {"ISO_8859-1", CR_UCS_1}, ++ {"UCS-1", CR_UCS_1}, ++ {"UCS_1", CR_UCS_1}, ++ {"UCS4", CR_UCS_4}, ++ {"UCS-4", CR_UCS_4}, ++ {"UCS_4", CR_UCS_4}, ++ {"ASCII", CR_ASCII}, ++ {0, 0} ++}; ++ ++static CREncHandler gv_default_enc_handlers[] = { ++ {CR_UCS_1, cr_utils_ucs1_to_utf8, cr_utils_utf8_to_ucs1, ++ cr_utils_ucs1_str_len_as_utf8, cr_utils_utf8_str_len_as_ucs1}, ++ ++ {CR_ISO_8859_1, cr_utils_ucs1_to_utf8, cr_utils_utf8_to_ucs1, ++ cr_utils_ucs1_str_len_as_utf8, cr_utils_utf8_str_len_as_ucs1}, ++ ++ {CR_ASCII, cr_utils_ucs1_to_utf8, cr_utils_utf8_to_ucs1, ++ cr_utils_ucs1_str_len_as_utf8, cr_utils_utf8_str_len_as_ucs1}, ++ ++ {0, NULL, NULL, NULL, NULL} ++}; ++ ++/** ++ * cr_enc_handler_get_instance: ++ *@a_enc: the encoding of the Handler. ++ * ++ *Gets the instance of encoding handler. ++ *This function implements a singleton pattern. ++ * ++ *Returns the instance of #CREncHandler. ++ */ ++CREncHandler * ++cr_enc_handler_get_instance (enum CREncoding a_enc) ++{ ++ gulong i = 0; ++ ++ for (i = 0; gv_default_enc_handlers[i].encoding; i++) { ++ if (gv_default_enc_handlers[i].encoding == a_enc) { ++ return (CREncHandler *) & gv_default_enc_handlers[i]; ++ } ++ } ++ ++ return NULL; ++} ++ ++/** ++ * cr_enc_handler_resolve_enc_alias: ++ *@a_alias_name: the encoding name. ++ *@a_enc: output param. The returned encoding type ++ *or 0 if the alias is not supported. ++ * ++ *Given an encoding name (called an alias name) ++ *the function returns the matching encoding type. ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_enc_handler_resolve_enc_alias (const guchar * a_alias_name, ++ enum CREncoding *a_enc) ++{ ++ gulong i = 0; ++ guchar *alias_name_up = NULL; ++ enum CRStatus status = CR_ENCODING_NOT_FOUND_ERROR; ++ ++ g_return_val_if_fail (a_alias_name != NULL, CR_BAD_PARAM_ERROR); ++ ++ alias_name_up = (guchar *) g_ascii_strup ((const gchar *) a_alias_name, -1); ++ ++ for (i = 0; gv_default_aliases[i].name; i++) { ++ if (!strcmp (gv_default_aliases[i].name, (const gchar *) alias_name_up)) { ++ *a_enc = gv_default_aliases[i].encoding; ++ status = CR_OK; ++ break; ++ } ++ } ++ ++ return status; ++} ++ ++/** ++ * cr_enc_handler_convert_input: ++ *@a_this: the current instance of #CREncHandler. ++ *@a_in: the input buffer to convert. ++ *@a_in_len: in/out parameter. The len of the input ++ *buffer to convert. After return, contains the number of ++ *bytes actually consumed. ++ *@a_out: output parameter. The converted output buffer. ++ *Must be freed by the buffer. ++ *@a_out_len: output parameter. The length of the output buffer. ++ * ++ *Converts a raw input buffer into an utf8 buffer. ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_enc_handler_convert_input (CREncHandler * a_this, ++ const guchar * a_in, ++ gulong * a_in_len, ++ guchar ** a_out, gulong * a_out_len) ++{ ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_this && a_in && a_in_len && a_out, ++ CR_BAD_PARAM_ERROR); ++ ++ if (a_this->decode_input == NULL) ++ return CR_OK; ++ ++ if (a_this->enc_str_len_as_utf8) { ++ status = a_this->enc_str_len_as_utf8 (a_in, ++ &a_in[*a_in_len - 1], ++ a_out_len); ++ ++ g_return_val_if_fail (status == CR_OK, status); ++ } else { ++ *a_out_len = *a_in_len; ++ } ++ ++ *a_out = g_malloc0 (*a_out_len); ++ ++ status = a_this->decode_input (a_in, a_in_len, *a_out, a_out_len); ++ ++ if (status != CR_OK) { ++ g_free (*a_out); ++ *a_out = NULL; ++ } ++ ++ g_return_val_if_fail (status == CR_OK, status); ++ ++ return CR_OK; ++} +diff --git a/src/st/croco/cr-enc-handler.h b/src/st/croco/cr-enc-handler.h +new file mode 100644 +index 000000000..0727764c0 +--- /dev/null ++++ b/src/st/croco/cr-enc-handler.h +@@ -0,0 +1,94 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * Copyright (C) 2002-2003 Dodji Seketeli ++ * ++ * 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 declaration of the #CREncHandler class. ++ * ++ */ ++ ++#ifndef __CR_ENC_HANDLER_H__ ++#define __CR_ENC_HANDLER_H__ ++ ++#include "cr-utils.h" ++ ++G_BEGIN_DECLS ++ ++ ++typedef struct _CREncHandler CREncHandler ; ++ ++typedef enum CRStatus (*CREncInputFunc) (const guchar * a_in, ++ gulong *a_in_len, ++ guchar *a_out, ++ gulong *a_out_len) ; ++ ++typedef enum CRStatus (*CREncOutputFunc) (const guchar * a_in, ++ gulong *a_in_len, ++ guchar *a_out, ++ gulong *a_out_len) ; ++ ++typedef enum CRStatus (*CREncInputStrLenAsUtf8Func) ++(const guchar *a_in_start, ++ const guchar *a_in_end, ++ gulong *a_in_size); ++ ++typedef enum CRStatus (*CREncUtf8StrLenAsOutputFunc) ++(const guchar *a_in_start, ++ const guchar *a_in_end, ++ gulong *a_in_size) ; ++ ++/** ++ *This class is responsible of the ++ *the encoding conversions stuffs in ++ *libcroco. ++ */ ++ ++struct _CREncHandler ++{ ++ enum CREncoding encoding ; ++ CREncInputFunc decode_input ; ++ CREncInputFunc encode_output ; ++ CREncInputStrLenAsUtf8Func enc_str_len_as_utf8 ; ++ CREncUtf8StrLenAsOutputFunc utf8_str_len_as_enc ; ++} ; ++ ++CREncHandler * ++cr_enc_handler_get_instance (enum CREncoding a_enc) ; ++ ++enum CRStatus ++cr_enc_handler_resolve_enc_alias (const guchar *a_alias_name, ++ enum CREncoding *a_enc) ; ++ ++enum CRStatus ++cr_enc_handler_convert_input (CREncHandler *a_this, ++ const guchar *a_in, ++ gulong *a_in_len, ++ guchar **a_out, ++ gulong *a_out_len) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_ENC_HANDLER_H__*/ +diff --git a/src/st/croco/cr-fonts.c b/src/st/croco/cr-fonts.c +new file mode 100644 +index 000000000..3a5788cea +--- /dev/null ++++ b/src/st/croco/cr-fonts.c +@@ -0,0 +1,949 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * ++ * 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 ++ * ++ *See COPYRIGHTS file for copyright information ++ */ ++ ++#include "cr-fonts.h" ++#include ++ ++static enum CRStatus ++cr_font_family_to_string_real (CRFontFamily const * a_this, ++ gboolean a_walk_list, GString ** a_string) ++{ ++ guchar const *name = NULL; ++ enum CRStatus result = CR_OK; ++ ++ if (!*a_string) { ++ *a_string = g_string_new (NULL); ++ g_return_val_if_fail (*a_string, ++ CR_INSTANCIATION_FAILED_ERROR); ++ } ++ ++ if (!a_this) { ++ g_string_append (*a_string, "NULL"); ++ return CR_OK; ++ } ++ ++ switch (a_this->type) { ++ case FONT_FAMILY_SANS_SERIF: ++ name = (guchar const *) "sans-serif"; ++ break; ++ ++ case FONT_FAMILY_SERIF: ++ name = (guchar const *) "sans-serif"; ++ break; ++ ++ case FONT_FAMILY_CURSIVE: ++ name = (guchar const *) "cursive"; ++ break; ++ ++ case FONT_FAMILY_FANTASY: ++ name = (guchar const *) "fantasy"; ++ break; ++ ++ case FONT_FAMILY_MONOSPACE: ++ name = (guchar const *) "monospace"; ++ break; ++ ++ case FONT_FAMILY_NON_GENERIC: ++ name = (guchar const *) a_this->name; ++ break; ++ ++ default: ++ name = NULL; ++ break; ++ } ++ ++ if (name) { ++ if (a_this->prev) { ++ g_string_append_printf (*a_string, ", %s", name); ++ } else { ++ g_string_append (*a_string, (const gchar *) name); ++ } ++ } ++ if (a_walk_list == TRUE && a_this->next) { ++ result = cr_font_family_to_string_real (a_this->next, ++ TRUE, a_string); ++ } ++ return result; ++} ++ ++static const gchar * ++cr_predefined_absolute_font_size_to_string (enum CRPredefinedAbsoluteFontSize ++ a_code) ++{ ++ gchar const *str = NULL; ++ ++ switch (a_code) { ++ case FONT_SIZE_XX_SMALL: ++ str = "xx-small"; ++ break; ++ case FONT_SIZE_X_SMALL: ++ str = "x-small"; ++ break; ++ case FONT_SIZE_SMALL: ++ str = "small"; ++ break; ++ case FONT_SIZE_MEDIUM: ++ str = "medium"; ++ break; ++ case FONT_SIZE_LARGE: ++ str = "large"; ++ break; ++ case FONT_SIZE_X_LARGE: ++ str = "x-large"; ++ break; ++ case FONT_SIZE_XX_LARGE: ++ str = "xx-large"; ++ break; ++ default: ++ str = "unknown absolute font size value"; ++ } ++ return str; ++} ++ ++static const gchar * ++cr_relative_font_size_to_string (enum CRRelativeFontSize a_code) ++{ ++ gchar const *str = NULL; ++ ++ switch (a_code) { ++ case FONT_SIZE_LARGER: ++ str = "larger"; ++ break; ++ case FONT_SIZE_SMALLER: ++ str = "smaller"; ++ break; ++ default: ++ str = "unknown relative font size value"; ++ break; ++ } ++ return str; ++} ++ ++/** ++ * cr_font_family_new: ++ * @a_type: the type of font family to create. ++ * @a_name: the name of the font family. ++ * ++ * create a font family. ++ * ++ * Returns the newly built font family. ++ */ ++CRFontFamily * ++cr_font_family_new (enum CRFontFamilyType a_type, guchar * a_name) ++{ ++ CRFontFamily *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRFontFamily)); ++ ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRFontFamily)); ++ result->type = a_type; ++ ++ cr_font_family_set_name (result, a_name); ++ ++ return result; ++} ++ ++/** ++ * cr_font_family_to_string: ++ * @a_this: the current instance of #CRFontFamily. ++ * @a_walk_font_family_list: wether the serialize the entire list. ++ * ++ * Returns the seriliazed font family. The caller has to free it using ++ * g_free(). ++ */ ++guchar * ++cr_font_family_to_string (CRFontFamily const * a_this, ++ gboolean a_walk_font_family_list) ++{ ++ enum CRStatus status = CR_OK; ++ guchar *result = NULL; ++ GString *stringue = NULL; ++ ++ if (!a_this) { ++ result = (guchar *) g_strdup ("NULL"); ++ g_return_val_if_fail (result, NULL); ++ return result; ++ } ++ status = cr_font_family_to_string_real (a_this, ++ a_walk_font_family_list, ++ &stringue); ++ ++ if (status == CR_OK && stringue) { ++ result = (guchar *) stringue->str; ++ g_string_free (stringue, FALSE); ++ stringue = NULL; ++ ++ } else { ++ if (stringue) { ++ g_string_free (stringue, TRUE); ++ stringue = NULL; ++ } ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_font_family_set_name: ++ * @a_this: the current instance of #CRFontFamily. ++ * @a_name: the new name ++ * ++ * Returns CR_OK upon sucessful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_font_family_set_name (CRFontFamily * a_this, guchar * a_name) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ /* ++ *only non generic font families can have a name ++ */ ++ ++ if (a_this->type != FONT_FAMILY_NON_GENERIC) { ++ return CR_BAD_PARAM_ERROR; ++ } ++ ++ if (a_this->name) { ++ g_free (a_this->name); ++ a_this->name = NULL; ++ } ++ ++ a_this->name = a_name; ++ return CR_OK; ++} ++ ++/** ++ * cr_font_family_append: ++ * @a_this: the current instance of #CRFontFamily. ++ * @a_family_to_append: the font family to append to the list ++ * ++ * Returns the new font family list. ++ */ ++CRFontFamily * ++cr_font_family_append (CRFontFamily * a_this, ++ CRFontFamily * a_family_to_append) ++{ ++ CRFontFamily *cur_ff = NULL; ++ ++ g_return_val_if_fail (a_family_to_append, NULL); ++ ++ if (!a_this) ++ return a_family_to_append; ++ ++ for (cur_ff = a_this; cur_ff && cur_ff->next; cur_ff = cur_ff->next) ; ++ ++ cur_ff->next = a_family_to_append; ++ a_family_to_append->prev = cur_ff; ++ ++ return a_this; ++ ++} ++ ++/** ++ * cr_font_family_prepend: ++ * @a_this: the current instance #CRFontFamily. ++ * @a_family_to_prepend: the font family to prepend to the list. ++ * ++ * Returns the font family list. ++ */ ++CRFontFamily * ++cr_font_family_prepend (CRFontFamily * a_this, ++ CRFontFamily * a_family_to_prepend) ++{ ++ g_return_val_if_fail (a_this && a_family_to_prepend, NULL); ++ ++ if (!a_this) ++ return a_family_to_prepend; ++ ++ a_family_to_prepend->next = a_this; ++ a_this->prev = a_family_to_prepend; ++ ++ return a_family_to_prepend; ++} ++ ++/** ++ * cr_font_family_destroy: ++ * @a_this: the current instance of #CRFontFamily. ++ * ++ * Returns CR_OK upon sucessful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_font_family_destroy (CRFontFamily * a_this) ++{ ++ CRFontFamily *cur_ff = NULL; ++ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ for (cur_ff = a_this; cur_ff && cur_ff->next; cur_ff = cur_ff->next) ; ++ ++ for (; cur_ff; cur_ff = cur_ff->prev) { ++ if (a_this->name) { ++ g_free (a_this->name); ++ a_this->name = NULL; ++ } ++ ++ if (cur_ff->next) { ++ g_free (cur_ff->next); ++ ++ } ++ ++ if (cur_ff->prev == NULL) { ++ g_free (a_this); ++ } ++ } ++ ++ return CR_OK; ++} ++ ++/*************************************************** ++ *'font-size' manipulation functions definitions ++ ***************************************************/ ++ ++/** ++ * cr_font_size_new: ++ * ++ * Returns the newly created font size. ++ */ ++CRFontSize * ++cr_font_size_new (void) ++{ ++ CRFontSize *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRFontSize)); ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (CRFontSize)); ++ ++ return result; ++} ++ ++/** ++ * cr_font_size_clear: ++ * @a_this: the current instance of #CRFontSize ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_font_size_clear (CRFontSize * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ switch (a_this->type) { ++ case PREDEFINED_ABSOLUTE_FONT_SIZE: ++ case RELATIVE_FONT_SIZE: ++ case INHERITED_FONT_SIZE: ++ memset (a_this, 0, sizeof (CRFontSize)); ++ break; ++ ++ case ABSOLUTE_FONT_SIZE: ++ memset (a_this, 0, sizeof (CRFontSize)); ++ break; ++ ++ default: ++ return CR_UNKNOWN_TYPE_ERROR; ++ } ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_font_size_copy: ++ * @a_dst: the destination #CRFontSize (where to copy to). ++ * @a_src: the source #CRFontSize (where to copy from). ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_font_size_copy (CRFontSize * a_dst, CRFontSize const * a_src) ++{ ++ g_return_val_if_fail (a_dst && a_src, CR_BAD_PARAM_ERROR); ++ ++ switch (a_src->type) { ++ case PREDEFINED_ABSOLUTE_FONT_SIZE: ++ case RELATIVE_FONT_SIZE: ++ case INHERITED_FONT_SIZE: ++ cr_font_size_clear (a_dst); ++ memcpy (a_dst, a_src, sizeof (CRFontSize)); ++ break; ++ ++ case ABSOLUTE_FONT_SIZE: ++ cr_font_size_clear (a_dst); ++ cr_num_copy (&a_dst->value.absolute, ++ &a_src->value.absolute); ++ a_dst->type = a_src->type; ++ break; ++ ++ default: ++ return CR_UNKNOWN_TYPE_ERROR; ++ } ++ return CR_OK; ++} ++ ++/** ++ * cr_font_size_set_predefined_absolute_font_size: ++ * @a_this: the current instance of #CRFontSize. ++ * @a_predefined: what to set. ++ * ++ * Returns CR_OK upon sucessful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_font_size_set_predefined_absolute_font_size (CRFontSize *a_this, ++ enum CRPredefinedAbsoluteFontSize a_predefined) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; ++ g_return_val_if_fail (a_predefined >= FONT_SIZE_XX_SMALL ++ && a_predefined < NB_PREDEFINED_ABSOLUTE_FONT_SIZES, ++ CR_BAD_PARAM_ERROR) ; ++ ++ a_this->type = PREDEFINED_ABSOLUTE_FONT_SIZE ; ++ a_this->value.predefined = a_predefined ; ++ ++ return CR_OK ; ++} ++ ++/** ++ * cr_font_size_set_relative_font_size: ++ * @a_this: the current instance of #CRFontSize ++ * @a_relative: the new relative font size ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_font_size_set_relative_font_size (CRFontSize *a_this, ++ enum CRRelativeFontSize a_relative) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; ++ g_return_val_if_fail (a_relative >= FONT_SIZE_LARGER ++ && a_relative < NB_RELATIVE_FONT_SIZE, ++ CR_BAD_PARAM_ERROR) ; ++ ++ a_this->type = RELATIVE_FONT_SIZE ; ++ a_this->value.relative = a_relative ; ++ return CR_OK ; ++} ++ ++/** ++ * cr_font_size_set_absolute_font_size: ++ * @a_this: the current instance of #CRFontSize ++ * @a_num_type: the type of number to set. ++ * @a_value: the actual value to set. ++ * ++ * Returns CR_OK upon succesful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_font_size_set_absolute_font_size (CRFontSize *a_this, ++ enum CRNumType a_num_type, ++ gdouble a_value) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; ++ g_return_val_if_fail (a_num_type >= NUM_AUTO ++ && a_num_type < NB_NUM_TYPE, ++ CR_BAD_PARAM_ERROR) ; ++ ++ a_this->type = ABSOLUTE_FONT_SIZE ; ++ cr_num_set (&a_this->value.absolute, ++ a_value, a_num_type) ; ++ return CR_OK ; ++} ++ ++/** ++ * cr_font_size_set_to_inherit: ++ * @a_this: the current instance of #CRFontSize ++ * ++ * Returns CR_OK upon succesful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_font_size_set_to_inherit (CRFontSize *a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; ++ ++ cr_font_size_clear (a_this) ; ++ a_this->type = INHERITED_FONT_SIZE ; ++ ++ return CR_OK ; ++} ++ ++/** ++ * cr_font_size_is_set_to_inherit: ++ * @a_this: the current instance of #CRFontSize. ++ * ++ * Returns TRUE if the current instance is set to 'inherit'. ++ */ ++gboolean ++cr_font_size_is_set_to_inherit (CRFontSize const *a_this) ++{ ++ g_return_val_if_fail (a_this, FALSE) ; ++ ++ return a_this->type == INHERITED_FONT_SIZE ; ++} ++ ++/** ++ * cr_font_size_to_string: ++ * @a_this: the current instance of #CRFontSize ++ * ++ * Returns the serialized form of #CRFontSize. The returned string ++ * has to bee freed using g_free(). ++ */ ++gchar * ++cr_font_size_to_string (CRFontSize const * a_this) ++{ ++ gchar *str = NULL; ++ ++ if (!a_this) { ++ str = g_strdup ("NULL"); ++ g_return_val_if_fail (str, NULL); ++ return str; ++ } ++ switch (a_this->type) { ++ case PREDEFINED_ABSOLUTE_FONT_SIZE: ++ str = g_strdup (cr_predefined_absolute_font_size_to_string ++ (a_this->value.predefined)); ++ break; ++ case ABSOLUTE_FONT_SIZE: ++ str = (gchar *) cr_num_to_string (&a_this->value.absolute); ++ break; ++ case RELATIVE_FONT_SIZE: ++ str = g_strdup (cr_relative_font_size_to_string ++ (a_this->value.relative)); ++ break; ++ case INHERITED_FONT_SIZE: ++ str = g_strdup ("inherit"); ++ break; ++ default: ++ break; ++ } ++ return str; ++} ++ ++/** ++ * cr_font_size_get_smaller_predefined: ++ * @a_font_size: the font size to consider. ++ * @a_smaller_size: out parameter. The a smaller value than @a_font_size. ++ */ ++void ++cr_font_size_get_smaller_predefined_font_size ++ (enum CRPredefinedAbsoluteFontSize a_font_size, ++ enum CRPredefinedAbsoluteFontSize *a_smaller_size) ++{ ++ enum CRPredefinedAbsoluteFontSize result = FONT_SIZE_MEDIUM ; ++ ++ g_return_if_fail (a_smaller_size) ; ++ g_return_if_fail (a_font_size < NB_PREDEFINED_ABSOLUTE_FONT_SIZES ++ && a_font_size >= FONT_SIZE_XX_SMALL) ; ++ ++ switch (a_font_size) { ++ case FONT_SIZE_XX_SMALL: ++ result = FONT_SIZE_XX_SMALL ; ++ break ; ++ case FONT_SIZE_X_SMALL: ++ result = FONT_SIZE_XX_SMALL ; ++ break ; ++ case FONT_SIZE_SMALL: ++ result = FONT_SIZE_X_SMALL; ++ break ; ++ case FONT_SIZE_MEDIUM: ++ result = FONT_SIZE_SMALL; ++ break ; ++ case FONT_SIZE_LARGE: ++ result = FONT_SIZE_MEDIUM; ++ break ; ++ case FONT_SIZE_X_LARGE: ++ result = FONT_SIZE_LARGE; ++ break ; ++ case FONT_SIZE_XX_LARGE: ++ result = FONT_SIZE_XX_LARGE; ++ break ; ++ case FONT_SIZE_INHERIT: ++ cr_utils_trace_info ("can't return a smaller size for FONT_SIZE_INHERIT") ; ++ result = FONT_SIZE_MEDIUM ; ++ break ; ++ default: ++ cr_utils_trace_info ("Unknown FONT_SIZE") ; ++ result = FONT_SIZE_MEDIUM ; ++ break ; ++ } ++ *a_smaller_size = result ; ++} ++ ++ ++/** ++ * cr_font_size_get_larger_predefined_font_size: ++ * @a_font_size: the font size to consider. ++ * @a_larger_size: out parameter. the font size considered larger than ++ * @a_font_size. ++ * ++ */ ++void ++cr_font_size_get_larger_predefined_font_size ++ (enum CRPredefinedAbsoluteFontSize a_font_size, ++ enum CRPredefinedAbsoluteFontSize *a_larger_size) ++{ ++ enum CRPredefinedAbsoluteFontSize result = FONT_SIZE_MEDIUM ; ++ ++ g_return_if_fail (a_larger_size) ; ++ g_return_if_fail (a_font_size >= FONT_SIZE_XX_SMALL ++ && a_font_size < NB_PREDEFINED_ABSOLUTE_FONT_SIZES) ; ++ ++ switch (a_font_size) { ++ case FONT_SIZE_XX_SMALL: ++ result = FONT_SIZE_X_SMALL ; ++ break ; ++ case FONT_SIZE_X_SMALL: ++ result = FONT_SIZE_SMALL ; ++ break ; ++ case FONT_SIZE_SMALL: ++ result = FONT_SIZE_MEDIUM; ++ break ; ++ case FONT_SIZE_MEDIUM: ++ result = FONT_SIZE_LARGE; ++ break ; ++ case FONT_SIZE_LARGE: ++ result = FONT_SIZE_X_LARGE; ++ break ; ++ case FONT_SIZE_X_LARGE: ++ result = FONT_SIZE_XX_LARGE ; ++ break ; ++ case FONT_SIZE_XX_LARGE: ++ result = FONT_SIZE_XX_LARGE; ++ break ; ++ case FONT_SIZE_INHERIT: ++ cr_utils_trace_info ("can't return a bigger size for FONT_SIZE_INHERIT") ; ++ result = FONT_SIZE_MEDIUM ; ++ break ; ++ default: ++ cr_utils_trace_info ("Unknown FONT_SIZE") ; ++ result = FONT_SIZE_MEDIUM ; ++ break ; ++ } ++ *a_larger_size = result ; ++} ++ ++/** ++ * cr_font_size_is_predefined_absolute_font_size: ++ * @a_font_size: the font size to consider. ++ * ++ * Returns TRUE if the instance is an predefined absolute font size, FALSE ++ * otherwise. ++ */ ++gboolean ++cr_font_size_is_predefined_absolute_font_size ++ (enum CRPredefinedAbsoluteFontSize a_font_size) ++{ ++ if (a_font_size >= FONT_SIZE_XX_SMALL ++ && a_font_size < NB_PREDEFINED_ABSOLUTE_FONT_SIZES) { ++ return TRUE ; ++ } else { ++ return FALSE ; ++ } ++} ++ ++/** ++ * cr_font_size_adjust_to_string: ++ * @a_this: the instance of #CRFontSizeAdjust. ++ * ++ * Returns the serialized form of #CRFontSizeAdjust ++ */ ++gchar * ++cr_font_size_adjust_to_string (CRFontSizeAdjust const * a_this) ++{ ++ gchar *str = NULL; ++ ++ if (!a_this) { ++ str = g_strdup ("NULL"); ++ g_return_val_if_fail (str, NULL); ++ return str; ++ } ++ ++ switch (a_this->type) { ++ case FONT_SIZE_ADJUST_NONE: ++ str = g_strdup ("none"); ++ break; ++ case FONT_SIZE_ADJUST_NUMBER: ++ if (a_this->num) ++ str = (gchar *) cr_num_to_string (a_this->num); ++ else ++ str = g_strdup ("unknown font-size-adjust property value"); /* Should raise an error no?*/ ++ break; ++ case FONT_SIZE_ADJUST_INHERIT: ++ str = g_strdup ("inherit"); ++ } ++ return str; ++} ++ ++/** ++ * cr_font_style_to_string: ++ * @a_code: the current instance of #CRFontStyle . ++ * ++ * Returns the serialized #CRFontStyle. The caller must free the returned ++ * string using g_free(). ++ */ ++const gchar * ++cr_font_style_to_string (enum CRFontStyle a_code) ++{ ++ gchar *str = NULL; ++ ++ switch (a_code) { ++ case FONT_STYLE_NORMAL: ++ str = (gchar *) "normal"; ++ break; ++ case FONT_STYLE_ITALIC: ++ str = (gchar *) "italic"; ++ break; ++ case FONT_STYLE_OBLIQUE: ++ str = (gchar *) "oblique"; ++ break; ++ case FONT_STYLE_INHERIT: ++ str = (gchar *) "inherit"; ++ break; ++ default: ++ str = (gchar *) "unknown font style value"; ++ break; ++ } ++ return str; ++} ++ ++/** ++ * cr_font_variant_to_string: ++ * @a_code: the current instance of #CRFontVariant. ++ * ++ * Returns the serialized form of #CRFontVariant. The caller has ++ * to free the returned string using g_free(). ++ */ ++const gchar * ++cr_font_variant_to_string (enum CRFontVariant a_code) ++{ ++ gchar *str = NULL; ++ ++ switch (a_code) { ++ case FONT_VARIANT_NORMAL: ++ str = (gchar *) "normal"; ++ break; ++ case FONT_VARIANT_SMALL_CAPS: ++ str = (gchar *) "small-caps"; ++ break; ++ case FONT_VARIANT_INHERIT: ++ str = (gchar *) "inherit"; ++ break; ++ } ++ return str; ++} ++ ++/** ++ * cr_font_weight_get_bolder: ++ * @a_weight: the #CRFontWeight to consider. ++ * ++ * Returns a font weight bolder than @a_weight ++ */ ++enum CRFontWeight ++cr_font_weight_get_bolder (enum CRFontWeight a_weight) ++{ ++ if (a_weight == FONT_WEIGHT_INHERIT) { ++ cr_utils_trace_info ("can't return a bolder weight for FONT_WEIGHT_INHERIT") ; ++ return a_weight; ++ } else if (a_weight >= FONT_WEIGHT_900) { ++ return FONT_WEIGHT_900 ; ++ } else if (a_weight < FONT_WEIGHT_NORMAL) { ++ return FONT_WEIGHT_NORMAL ; ++ } else if (a_weight == FONT_WEIGHT_BOLDER ++ || a_weight == FONT_WEIGHT_LIGHTER) { ++ cr_utils_trace_info ("FONT_WEIGHT_BOLDER or FONT_WEIGHT_LIGHTER should not appear here") ; ++ return FONT_WEIGHT_NORMAL ; ++ } else { ++ return a_weight << 1 ; ++ } ++} ++ ++/** ++ * cr_font_weight_to_string: ++ * @a_code: the font weight to consider. ++ * ++ * Returns the serialized form of #CRFontWeight. ++ */ ++const gchar * ++cr_font_weight_to_string (enum CRFontWeight a_code) ++{ ++ gchar *str = NULL; ++ ++ switch (a_code) { ++ case FONT_WEIGHT_NORMAL: ++ str = (gchar *) "normal"; ++ break; ++ case FONT_WEIGHT_BOLD: ++ str = (gchar *) "bold"; ++ break; ++ case FONT_WEIGHT_BOLDER: ++ str = (gchar *) "bolder"; ++ break; ++ case FONT_WEIGHT_LIGHTER: ++ str = (gchar *) "lighter"; ++ break; ++ case FONT_WEIGHT_100: ++ str = (gchar *) "100"; ++ break; ++ case FONT_WEIGHT_200: ++ str = (gchar *) "200"; ++ break; ++ case FONT_WEIGHT_300: ++ str = (gchar *) "300"; ++ break; ++ case FONT_WEIGHT_400: ++ str = (gchar *) "400"; ++ break; ++ case FONT_WEIGHT_500: ++ str = (gchar *) "500"; ++ break; ++ case FONT_WEIGHT_600: ++ str = (gchar *) "600"; ++ break; ++ case FONT_WEIGHT_700: ++ str = (gchar *) "700"; ++ break; ++ case FONT_WEIGHT_800: ++ str = (gchar *) "800"; ++ break; ++ case FONT_WEIGHT_900: ++ str = (gchar *) "900"; ++ break; ++ case FONT_WEIGHT_INHERIT: ++ str = (gchar *) "inherit"; ++ break; ++ default: ++ str = (gchar *) "unknown font-weight property value"; ++ break; ++ } ++ return str; ++} ++ ++/** ++ * cr_font_stretch_to_string: ++ * @a_code: the instance of #CRFontStretch to consider. ++ * ++ * Returns the serialized form of #CRFontStretch. ++ */ ++const gchar * ++cr_font_stretch_to_string (enum CRFontStretch a_code) ++{ ++ gchar *str = NULL; ++ ++ switch (a_code) { ++ case FONT_STRETCH_NORMAL: ++ str = (gchar *) "normal"; ++ break; ++ case FONT_STRETCH_WIDER: ++ str = (gchar *) "wider"; ++ break; ++ case FONT_STRETCH_NARROWER: ++ str = (gchar *) "narrower"; ++ break; ++ case FONT_STRETCH_ULTRA_CONDENSED: ++ str = (gchar *) "ultra-condensed"; ++ break; ++ case FONT_STRETCH_EXTRA_CONDENSED: ++ str = (gchar *) "extra-condensed"; ++ break; ++ case FONT_STRETCH_CONDENSED: ++ str = (gchar *) "condensed"; ++ break; ++ case FONT_STRETCH_SEMI_CONDENSED: ++ str = (gchar *) "semi-condensed"; ++ break; ++ case FONT_STRETCH_SEMI_EXPANDED: ++ str = (gchar *) "semi-expanded"; ++ break; ++ case FONT_STRETCH_EXPANDED: ++ str = (gchar *) "expanded"; ++ break; ++ case FONT_STRETCH_EXTRA_EXPANDED: ++ str = (gchar *) "extra-expaned"; ++ break; ++ case FONT_STRETCH_ULTRA_EXPANDED: ++ str = (gchar *) "ultra-expanded"; ++ break; ++ case FONT_STRETCH_INHERIT: ++ str = (gchar *) "inherit"; ++ break; ++ } ++ return str; ++} ++ ++/** ++ * cr_font_size_destroy: ++ * @a_font_size: the font size to destroy ++ * ++ */ ++void ++cr_font_size_destroy (CRFontSize * a_font_size) ++{ ++ g_return_if_fail (a_font_size); ++ ++ g_free (a_font_size) ; ++} ++ ++/******************************************************* ++ *'font-size-adjust' manipulation function definition ++ *******************************************************/ ++ ++/** ++ * cr_font_size_adjust_new: ++ * ++ * Returns a newly built instance of #CRFontSizeAdjust ++ */ ++CRFontSizeAdjust * ++cr_font_size_adjust_new (void) ++{ ++ CRFontSizeAdjust *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRFontSizeAdjust)); ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (CRFontSizeAdjust)); ++ ++ return result; ++} ++ ++/** ++ * cr_font_size_adjust_destroy: ++ * @a_this: the current instance of #CRFontSizeAdjust. ++ * ++ */ ++void ++cr_font_size_adjust_destroy (CRFontSizeAdjust * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ if (a_this->type == FONT_SIZE_ADJUST_NUMBER && a_this->num) { ++ cr_num_destroy (a_this->num); ++ a_this->num = NULL; ++ } ++} +diff --git a/src/st/croco/cr-fonts.h b/src/st/croco/cr-fonts.h +new file mode 100644 +index 000000000..9eaeeeb98 +--- /dev/null ++++ b/src/st/croco/cr-fonts.h +@@ -0,0 +1,315 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#ifndef __CR_FONTS_H__ ++#define __CR_FONTS_H__ ++ ++#include "cr-utils.h" ++#include "cr-num.h" ++ ++/** ++ *@file ++ *Various type declarations about font selection related ++ *properties. ++ */ ++G_BEGIN_DECLS ++ ++ ++enum CRFontFamilyType ++{ ++ FONT_FAMILY_SANS_SERIF, ++ FONT_FAMILY_SERIF, ++ FONT_FAMILY_CURSIVE, ++ FONT_FAMILY_FANTASY, ++ FONT_FAMILY_MONOSPACE, ++ FONT_FAMILY_NON_GENERIC, ++ FONT_FAMILY_INHERIT, ++ /**/ ++ NB_FONT_FAMILIE_TYPES ++} ; ++ ++typedef struct _CRFontFamily CRFontFamily ; ++ ++struct _CRFontFamily ++{ ++ enum CRFontFamilyType type ; ++ ++ /* ++ *The name of the font family, in case ++ *it is non generic. ++ *Is set only if the type is FONT_FAMILY_NON_GENERIC. ++ */ ++ guchar *name ; ++ ++ CRFontFamily *next ; ++ CRFontFamily *prev ; ++} ; ++ ++ ++/** ++ *The different types ++ *of absolute font size. ++ *This is used by the 'font-size' ++ *property defined in css2 spec ++ *in chapter 15.2.4 . ++ *These values a indexes of ++ *table of size so please, do not ++ *change their definition order unless ++ *you know what you are doing. ++ */ ++enum CRPredefinedAbsoluteFontSize ++{ ++ FONT_SIZE_XX_SMALL=0, ++ FONT_SIZE_X_SMALL, ++ FONT_SIZE_SMALL, ++ FONT_SIZE_MEDIUM, ++ FONT_SIZE_LARGE, ++ FONT_SIZE_X_LARGE, ++ FONT_SIZE_XX_LARGE, ++ FONT_SIZE_INHERIT, ++ NB_PREDEFINED_ABSOLUTE_FONT_SIZES ++} ; ++ ++/** ++ *The different types ++ *of relative font size. ++ *This is used by the 'font-size' ++ *property defined in css2 spec ++ *in chapter 15.2.4 . ++ *These values a indexes of ++ *table of size so please, do not ++ *change their definition order unless ++ *you know what you are doing. ++ */ ++enum CRRelativeFontSize ++{ ++ FONT_SIZE_LARGER, ++ FONT_SIZE_SMALLER, ++ NB_RELATIVE_FONT_SIZE ++} ; ++ ++/** ++ *The type of font-size property. ++ *Used to define the type of #CRFontSize . ++ *See css2 spec chapter 15.2.4 to understand. ++ */ ++enum CRFontSizeType { ++ /** ++ *If the type of #CRFontSize is ++ *PREDEFINED_ABSOLUTE_FONT_SIZE, ++ *the CRFontSize::value.predefined_absolute ++ *field will be defined. ++ */ ++ PREDEFINED_ABSOLUTE_FONT_SIZE, ++ ++ /** ++ *If the type of #CRFontSize is ++ *ABSOLUTE_FONT_SIZE, ++ *the CRFontSize::value.absolute ++ *field will be defined. ++ */ ++ ABSOLUTE_FONT_SIZE, ++ ++ /** ++ *If the type of #CRFontSize is ++ *RELATIVE_FONT_SIZE, ++ *the CRFontSize::value.relative ++ *field will be defined. ++ */ ++ RELATIVE_FONT_SIZE, ++ ++ /** ++ *If the type of #CRFontSize is ++ *INHERITED_FONT_SIZE, ++ *the None of the field of the CRFontSize::value enum ++ *will be defined. ++ */ ++ INHERITED_FONT_SIZE, ++ ++ NB_FONT_SIZE_TYPE ++} ; ++ ++typedef struct _CRFontSize CRFontSize ; ++struct _CRFontSize { ++ enum CRFontSizeType type ; ++ union { ++ enum CRPredefinedAbsoluteFontSize predefined ; ++ enum CRRelativeFontSize relative ; ++ CRNum absolute ; ++ } value; ++} ; ++ ++enum CRFontSizeAdjustType ++{ ++ FONT_SIZE_ADJUST_NONE = 0, ++ FONT_SIZE_ADJUST_NUMBER, ++ FONT_SIZE_ADJUST_INHERIT ++} ; ++typedef struct _CRFontSizeAdjust CRFontSizeAdjust ; ++struct _CRFontSizeAdjust ++{ ++ enum CRFontSizeAdjustType type ; ++ CRNum *num ; ++} ; ++ ++enum CRFontStyle ++{ ++ FONT_STYLE_NORMAL=0, ++ FONT_STYLE_ITALIC, ++ FONT_STYLE_OBLIQUE, ++ FONT_STYLE_INHERIT ++} ; ++ ++enum CRFontVariant ++{ ++ FONT_VARIANT_NORMAL=0, ++ FONT_VARIANT_SMALL_CAPS, ++ FONT_VARIANT_INHERIT ++} ; ++ ++enum CRFontWeight ++{ ++ FONT_WEIGHT_NORMAL = 1, ++ FONT_WEIGHT_BOLD = 1<<1, ++ FONT_WEIGHT_BOLDER = 1<<2, ++ FONT_WEIGHT_LIGHTER = 1<<3, ++ FONT_WEIGHT_100 = 1<<4, ++ FONT_WEIGHT_200 = 1<<5, ++ FONT_WEIGHT_300 = 1<<6, ++ FONT_WEIGHT_400 = 1<<7, ++ FONT_WEIGHT_500 = 1<<8, ++ FONT_WEIGHT_600 = 1<<9, ++ FONT_WEIGHT_700 = 1<<10, ++ FONT_WEIGHT_800 = 1<<11, ++ FONT_WEIGHT_900 = 1<<12, ++ FONT_WEIGHT_INHERIT = 1<<13, ++ NB_FONT_WEIGHTS ++} ; ++ ++enum CRFontStretch ++{ ++ FONT_STRETCH_NORMAL=0, ++ FONT_STRETCH_WIDER, ++ FONT_STRETCH_NARROWER, ++ FONT_STRETCH_ULTRA_CONDENSED, ++ FONT_STRETCH_EXTRA_CONDENSED, ++ FONT_STRETCH_CONDENSED, ++ FONT_STRETCH_SEMI_CONDENSED, ++ FONT_STRETCH_SEMI_EXPANDED, ++ FONT_STRETCH_EXPANDED, ++ FONT_STRETCH_EXTRA_EXPANDED, ++ FONT_STRETCH_ULTRA_EXPANDED, ++ FONT_STRETCH_INHERIT ++} ; ++ ++/************************************** ++ *'font-family' manipulation functions ++ ***************************************/ ++CRFontFamily * ++cr_font_family_new (enum CRFontFamilyType a_type, guchar *a_name) ; ++ ++CRFontFamily * ++cr_font_family_append (CRFontFamily *a_this, ++ CRFontFamily *a_family_to_append) ; ++ ++guchar * ++cr_font_family_to_string (CRFontFamily const *a_this, ++ gboolean a_walk_font_family_list) ; ++ ++CRFontFamily * ++cr_font_family_prepend (CRFontFamily *a_this, ++ CRFontFamily *a_family_to_prepend); ++ ++enum CRStatus ++cr_font_family_destroy (CRFontFamily *a_this) ; ++ ++enum CRStatus ++cr_font_family_set_name (CRFontFamily *a_this, guchar *a_name) ; ++ ++ ++/************************************ ++ *'font-size' manipulation functions ++ ***********************************/ ++ ++CRFontSize * cr_font_size_new (void) ; ++ ++enum CRStatus cr_font_size_clear (CRFontSize *a_this) ; ++ ++enum CRStatus cr_font_size_copy (CRFontSize *a_dst, ++ CRFontSize const *a_src) ; ++enum CRStatus cr_font_size_set_predefined_absolute_font_size (CRFontSize *a_this, ++ enum CRPredefinedAbsoluteFontSize a_predefined) ; ++enum CRStatus cr_font_size_set_relative_font_size (CRFontSize *a_this, ++ enum CRRelativeFontSize a_relative) ; ++ ++enum CRStatus cr_font_size_set_absolute_font_size (CRFontSize *a_this, ++ enum CRNumType a_num_type, ++ gdouble a_value) ; ++ ++enum CRStatus cr_font_size_set_to_inherit (CRFontSize *a_this) ; ++ ++gboolean cr_font_size_is_set_to_inherit (CRFontSize const *a_this) ; ++ ++gchar* cr_font_size_to_string (CRFontSize const *a_this) ; ++ ++void cr_font_size_destroy (CRFontSize *a_font_size) ; ++ ++/******************************************************* ++ *'font-size-adjust' manipulation function declarations ++ *******************************************************/ ++ ++CRFontSizeAdjust * cr_font_size_adjust_new (void) ; ++ ++gchar * cr_font_size_adjust_to_string (CRFontSizeAdjust const *a_this) ; ++ ++void cr_font_size_adjust_destroy (CRFontSizeAdjust *a_this) ; ++ ++void ++cr_font_size_get_smaller_predefined_font_size (enum CRPredefinedAbsoluteFontSize a_font_size, ++ enum CRPredefinedAbsoluteFontSize *a_smaller_size) ; ++void ++cr_font_size_get_larger_predefined_font_size (enum CRPredefinedAbsoluteFontSize a_font_size, ++ enum CRPredefinedAbsoluteFontSize *a_larger_size) ; ++ ++gboolean ++cr_font_size_is_predefined_absolute_font_size (enum CRPredefinedAbsoluteFontSize a_font_size) ; ++ ++/*********************************** ++ *various other font related functions ++ ***********************************/ ++const gchar * cr_font_style_to_string (enum CRFontStyle a_code) ; ++ ++const gchar * cr_font_weight_to_string (enum CRFontWeight a_code) ; ++ ++enum CRFontWeight ++cr_font_weight_get_bolder (enum CRFontWeight a_weight) ; ++ ++const gchar * cr_font_variant_to_string (enum CRFontVariant a_code) ; ++ ++const gchar * cr_font_stretch_to_string (enum CRFontStretch a_code) ; ++ ++G_END_DECLS ++ ++#endif +diff --git a/src/st/croco/cr-input.c b/src/st/croco/cr-input.c +new file mode 100644 +index 000000000..3b63a88ee +--- /dev/null ++++ b/src/st/croco/cr-input.c +@@ -0,0 +1,1191 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#include "stdio.h" ++#include ++#include "cr-input.h" ++#include "cr-enc-handler.h" ++ ++/** ++ *@CRInput: ++ * ++ *The definition of the #CRInput class. ++ */ ++ ++/******************* ++ *Private type defs ++ *******************/ ++ ++/** ++ *The private attributes of ++ *the #CRInputPriv class. ++ */ ++struct _CRInputPriv { ++ /* ++ *The input buffer ++ */ ++ guchar *in_buf; ++ gulong in_buf_size; ++ ++ gulong nb_bytes; ++ ++ /* ++ *The index of the next byte ++ *to be read. ++ */ ++ gulong next_byte_index; ++ ++ /* ++ *The current line number ++ */ ++ gulong line; ++ ++ /* ++ *The current col number ++ */ ++ gulong col; ++ ++ gboolean end_of_line; ++ gboolean end_of_input; ++ ++ /* ++ *the reference count of this ++ *instance. ++ */ ++ guint ref_count; ++ gboolean free_in_buf; ++}; ++ ++#define PRIVATE(object) (object)->priv ++ ++/*************************** ++ *private constants ++ **************************/ ++#define CR_INPUT_MEM_CHUNK_SIZE 1024 * 4 ++ ++static CRInput *cr_input_new_real (void); ++ ++static CRInput * ++cr_input_new_real (void) ++{ ++ CRInput *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRInput)); ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (CRInput)); ++ ++ PRIVATE (result) = g_try_malloc (sizeof (CRInputPriv)); ++ if (!PRIVATE (result)) { ++ cr_utils_trace_info ("Out of memory"); ++ g_free (result); ++ return NULL; ++ } ++ memset (PRIVATE (result), 0, sizeof (CRInputPriv)); ++ PRIVATE (result)->free_in_buf = TRUE; ++ return result; ++} ++ ++/**************** ++ *Public methods ++ ***************/ ++ ++/** ++ * cr_input_new_from_buf: ++ *@a_buf: the memory buffer to create the input stream from. ++ *The #CRInput keeps this pointer so user should not free it !. ++ *@a_len: the size of the input buffer. ++ *@a_enc: the buffer's encoding. ++ *@a_free_buf: if set to TRUE, this a_buf will be freed ++ *at the destruction of this instance. If set to false, it is up ++ *to the caller to free it. ++ * ++ *Creates a new input stream from a memory buffer. ++ *Returns the newly built instance of #CRInput. ++ */ ++CRInput * ++cr_input_new_from_buf (guchar * a_buf, ++ gulong a_len, ++ enum CREncoding a_enc, ++ gboolean a_free_buf) ++{ ++ CRInput *result = NULL; ++ enum CRStatus status = CR_OK; ++ CREncHandler *enc_handler = NULL; ++ gulong len = a_len; ++ ++ g_return_val_if_fail (a_buf, NULL); ++ ++ result = cr_input_new_real (); ++ g_return_val_if_fail (result, NULL); ++ ++ /*transform the encoding in utf8 */ ++ if (a_enc != CR_UTF_8) { ++ enc_handler = cr_enc_handler_get_instance (a_enc); ++ if (!enc_handler) { ++ goto error; ++ } ++ ++ status = cr_enc_handler_convert_input ++ (enc_handler, a_buf, &len, ++ &PRIVATE (result)->in_buf, ++ &PRIVATE (result)->in_buf_size); ++ if (status != CR_OK) ++ goto error; ++ PRIVATE (result)->free_in_buf = TRUE; ++ if (a_free_buf == TRUE && a_buf) { ++ g_free (a_buf) ; ++ a_buf = NULL ; ++ } ++ PRIVATE (result)->nb_bytes = PRIVATE (result)->in_buf_size; ++ } else { ++ PRIVATE (result)->in_buf = (guchar *) a_buf; ++ PRIVATE (result)->in_buf_size = a_len; ++ PRIVATE (result)->nb_bytes = a_len; ++ PRIVATE (result)->free_in_buf = a_free_buf; ++ } ++ PRIVATE (result)->line = 1; ++ PRIVATE (result)->col = 0; ++ return result; ++ ++ error: ++ if (result) { ++ cr_input_destroy (result); ++ result = NULL; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * cr_input_new_from_uri: ++ *@a_file_uri: the file to create *the input stream from. ++ *@a_enc: the encoding of the file *to create the input from. ++ * ++ *Creates a new input stream from ++ *a file. ++ * ++ *Returns the newly created input stream if ++ *this method could read the file and create it, ++ *NULL otherwise. ++ */ ++ ++CRInput * ++cr_input_new_from_uri (const gchar * a_file_uri, enum CREncoding a_enc) ++{ ++ CRInput *result = NULL; ++ enum CRStatus status = CR_OK; ++ FILE *file_ptr = NULL; ++ guchar tmp_buf[CR_INPUT_MEM_CHUNK_SIZE] = { 0 }; ++ gulong nb_read = 0, ++ len = 0, ++ buf_size = 0; ++ gboolean loop = TRUE; ++ guchar *buf = NULL; ++ ++ g_return_val_if_fail (a_file_uri, NULL); ++ ++ file_ptr = fopen (a_file_uri, "r"); ++ ++ if (file_ptr == NULL) { ++ ++#ifdef CR_DEBUG ++ cr_utils_trace_debug ("could not open file"); ++#endif ++ g_warning ("Could not open file %s\n", a_file_uri); ++ ++ return NULL; ++ } ++ ++ /*load the file */ ++ while (loop) { ++ nb_read = fread (tmp_buf, 1 /*read bytes */ , ++ CR_INPUT_MEM_CHUNK_SIZE /*nb of bytes */ , ++ file_ptr); ++ ++ if (nb_read != CR_INPUT_MEM_CHUNK_SIZE) { ++ /*we read less chars than we wanted */ ++ if (feof (file_ptr)) { ++ /*we reached eof */ ++ loop = FALSE; ++ } else { ++ /*a pb occurred !! */ ++ cr_utils_trace_debug ("an io error occurred"); ++ status = CR_ERROR; ++ goto cleanup; ++ } ++ } ++ ++ if (status == CR_OK) { ++ /*read went well */ ++ buf = g_realloc (buf, len + CR_INPUT_MEM_CHUNK_SIZE); ++ memcpy (buf + len, tmp_buf, nb_read); ++ len += nb_read; ++ buf_size += CR_INPUT_MEM_CHUNK_SIZE; ++ } ++ } ++ ++ if (status == CR_OK) { ++ result = cr_input_new_from_buf (buf, len, a_enc, TRUE); ++ if (!result) { ++ goto cleanup; ++ } ++ /* ++ *we should free buf here because it's own by CRInput. ++ *(see the last parameter of cr_input_new_from_buf(). ++ */ ++ buf = NULL; ++ } ++ ++ cleanup: ++ if (file_ptr) { ++ fclose (file_ptr); ++ file_ptr = NULL; ++ } ++ ++ if (buf) { ++ g_free (buf); ++ buf = NULL; ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_input_destroy: ++ *@a_this: the current instance of #CRInput. ++ * ++ *The destructor of the #CRInput class. ++ */ ++void ++cr_input_destroy (CRInput * a_this) ++{ ++ if (a_this == NULL) ++ return; ++ ++ if (PRIVATE (a_this)) { ++ if (PRIVATE (a_this)->in_buf && PRIVATE (a_this)->free_in_buf) { ++ g_free (PRIVATE (a_this)->in_buf); ++ PRIVATE (a_this)->in_buf = NULL; ++ } ++ ++ g_free (PRIVATE (a_this)); ++ PRIVATE (a_this) = NULL; ++ } ++ ++ g_free (a_this); ++} ++ ++/** ++ * cr_input_ref: ++ *@a_this: the current instance of #CRInput. ++ * ++ *Increments the reference count of the current ++ *instance of #CRInput. ++ */ ++void ++cr_input_ref (CRInput * a_this) ++{ ++ g_return_if_fail (a_this && PRIVATE (a_this)); ++ ++ PRIVATE (a_this)->ref_count++; ++} ++ ++/** ++ * cr_input_unref: ++ *@a_this: the current instance of #CRInput. ++ * ++ *Decrements the reference count of this instance ++ *of #CRInput. If the reference count goes down to ++ *zero, this instance is destroyed. ++ * ++ * Returns TRUE if the instance of #CRInput got destroyed, false otherwise. ++ */ ++gboolean ++cr_input_unref (CRInput * a_this) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), FALSE); ++ ++ if (PRIVATE (a_this)->ref_count) { ++ PRIVATE (a_this)->ref_count--; ++ } ++ ++ if (PRIVATE (a_this)->ref_count == 0) { ++ cr_input_destroy (a_this); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++/** ++ * cr_input_end_of_input: ++ *@a_this: the current instance of #CRInput. ++ *@a_end_of_input: out parameter. Is set to TRUE if ++ *the current instance has reached the end of its input buffer, ++ *FALSE otherwise. ++ * ++ *Tests wether the current instance of ++ *#CRInput has reached its input buffer. ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ * Note that all the out parameters of this method are valid if ++ * and only if this method returns CR_OK. ++ */ ++enum CRStatus ++cr_input_end_of_input (CRInput const * a_this, gboolean * a_end_of_input) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_end_of_input, CR_BAD_PARAM_ERROR); ++ ++ *a_end_of_input = (PRIVATE (a_this)->next_byte_index ++ >= PRIVATE (a_this)->in_buf_size) ? TRUE : FALSE; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_get_nb_bytes_left: ++ *@a_this: the current instance of #CRInput. ++ * ++ *Returns the number of bytes left in the input stream ++ *before the end, -1 in case of error. ++ */ ++glong ++cr_input_get_nb_bytes_left (CRInput const * a_this) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), -1); ++ g_return_val_if_fail (PRIVATE (a_this)->nb_bytes ++ <= PRIVATE (a_this)->in_buf_size, -1); ++ g_return_val_if_fail (PRIVATE (a_this)->next_byte_index ++ <= PRIVATE (a_this)->nb_bytes, -1); ++ ++ if (PRIVATE (a_this)->end_of_input) ++ return 0; ++ ++ return PRIVATE (a_this)->nb_bytes - PRIVATE (a_this)->next_byte_index; ++} ++ ++/** ++ * cr_input_read_byte: ++ *@a_this: the current instance of #CRInput. ++ *@a_byte: out parameter the returned byte. ++ * ++ *Gets the next byte of the input. ++ *Updates the state of the input so that ++ *the next invocation of this method returns ++ *the next coming byte. ++ * ++ *Returns CR_OK upon successful completion, an error code ++ *otherwise. All the out parameters of this method are valid if ++ *and only if this method returns CR_OK. ++ */ ++enum CRStatus ++cr_input_read_byte (CRInput * a_this, guchar * a_byte) ++{ ++ gulong nb_bytes_left = 0; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_byte, CR_BAD_PARAM_ERROR); ++ ++ g_return_val_if_fail (PRIVATE (a_this)->next_byte_index <= ++ PRIVATE (a_this)->nb_bytes, CR_BAD_PARAM_ERROR); ++ ++ if (PRIVATE (a_this)->end_of_input == TRUE) ++ return CR_END_OF_INPUT_ERROR; ++ ++ nb_bytes_left = cr_input_get_nb_bytes_left (a_this); ++ ++ if (nb_bytes_left < 1) { ++ return CR_END_OF_INPUT_ERROR; ++ } ++ ++ *a_byte = PRIVATE (a_this)->in_buf[PRIVATE (a_this)->next_byte_index]; ++ ++ if (PRIVATE (a_this)->nb_bytes - ++ PRIVATE (a_this)->next_byte_index < 2) { ++ PRIVATE (a_this)->end_of_input = TRUE; ++ } else { ++ PRIVATE (a_this)->next_byte_index++; ++ } ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_read_char: ++ *@a_this: the current instance of CRInput. ++ *@a_char: out parameter. The read character. ++ * ++ *Reads an unicode character from the current instance of ++ *#CRInput. ++ * ++ *Returns CR_OK upon successful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_input_read_char (CRInput * a_this, guint32 * a_char) ++{ ++ enum CRStatus status = CR_OK; ++ gulong consumed = 0, ++ nb_bytes_left = 0; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_char, ++ CR_BAD_PARAM_ERROR); ++ ++ if (PRIVATE (a_this)->end_of_input == TRUE) ++ return CR_END_OF_INPUT_ERROR; ++ ++ nb_bytes_left = cr_input_get_nb_bytes_left (a_this); ++ ++ if (nb_bytes_left < 1) { ++ return CR_END_OF_INPUT_ERROR; ++ } ++ ++ status = cr_utils_read_char_from_utf8_buf ++ (PRIVATE (a_this)->in_buf ++ + ++ PRIVATE (a_this)->next_byte_index, ++ nb_bytes_left, a_char, &consumed); ++ ++ if (status == CR_OK) { ++ /*update next byte index */ ++ PRIVATE (a_this)->next_byte_index += consumed; ++ ++ /*update line and column number */ ++ if (PRIVATE (a_this)->end_of_line == TRUE) { ++ PRIVATE (a_this)->col = 1; ++ PRIVATE (a_this)->line++; ++ PRIVATE (a_this)->end_of_line = FALSE; ++ } else if (*a_char != '\n') { ++ PRIVATE (a_this)->col++; ++ } ++ ++ if (*a_char == '\n') { ++ PRIVATE (a_this)->end_of_line = TRUE; ++ } ++ } ++ ++ return status; ++} ++ ++/** ++ * cr_input_set_line_num: ++ *@a_this: the "this pointer" of the current instance of #CRInput. ++ *@a_line_num: the new line number. ++ * ++ *Setter of the current line number. ++ * ++ *Return CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_set_line_num (CRInput * a_this, glong a_line_num) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); ++ ++ PRIVATE (a_this)->line = a_line_num; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_get_line_num: ++ *@a_this: the "this pointer" of the current instance of #CRInput. ++ *@a_line_num: the returned line number. ++ * ++ *Getter of the current line number. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_get_line_num (CRInput const * a_this, glong * a_line_num) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_line_num, CR_BAD_PARAM_ERROR); ++ ++ *a_line_num = PRIVATE (a_this)->line; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_set_column_num: ++ *@a_this: the "this pointer" of the current instance of #CRInput. ++ *@a_col: the new column number. ++ * ++ *Setter of the current column number. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_set_column_num (CRInput * a_this, glong a_col) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); ++ ++ PRIVATE (a_this)->col = a_col; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_get_column_num: ++ *@a_this: the "this pointer" of the current instance of #CRInput. ++ *@a_col: out parameter ++ * ++ *Getter of the current column number. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_get_column_num (CRInput const * a_this, glong * a_col) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_col, ++ CR_BAD_PARAM_ERROR); ++ ++ *a_col = PRIVATE (a_this)->col; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_increment_line_num: ++ *@a_this: the "this pointer" of the current instance of #CRInput. ++ *@a_increment: the increment to add to the line number. ++ * ++ *Increments the current line number. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_increment_line_num (CRInput * a_this, glong a_increment) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); ++ ++ PRIVATE (a_this)->line += a_increment; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_increment_col_num: ++ *@a_this: the "this pointer" of the current instance of #CRInput. ++ *@a_increment: the increment to add to the column number. ++ * ++ *Increments the current column number. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_increment_col_num (CRInput * a_this, glong a_increment) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); ++ ++ PRIVATE (a_this)->col += a_increment; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_consume_char: ++ *@a_this: the this pointer. ++ *@a_char: the character to consume. If set to zero, ++ *consumes any character. ++ * ++ *Consumes the next character of the input stream if ++ *and only if that character equals a_char. ++ * ++ *Returns CR_OK upon successful completion, CR_PARSING_ERROR if ++ *next char is different from a_char, an other error code otherwise ++ */ ++enum CRStatus ++cr_input_consume_char (CRInput * a_this, guint32 a_char) ++{ ++ guint32 c; ++ enum CRStatus status; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); ++ ++ if ((status = cr_input_peek_char (a_this, &c)) != CR_OK) { ++ return status; ++ } ++ ++ if (c == a_char || a_char == 0) { ++ status = cr_input_read_char (a_this, &c); ++ } else { ++ return CR_PARSING_ERROR; ++ } ++ ++ return status; ++} ++ ++/** ++ * cr_input_consume_chars: ++ *@a_this: the this pointer of the current instance of #CRInput. ++ *@a_char: the character to consume. ++ *@a_nb_char: in/out parameter. The number of characters to consume. ++ *If set to a negative value, the function will consume all the occurences ++ *of a_char found. ++ *After return, if the return value equals CR_OK, this variable contains ++ *the number of characters actually consumed. ++ * ++ *Consumes up to a_nb_char occurences of the next contiguous characters ++ *which equal a_char. Note that the next character of the input stream ++ **MUST* equal a_char to trigger the consumption, or else, the error ++ *code CR_PARSING_ERROR is returned. ++ *If the number of contiguous characters that equals a_char is less than ++ *a_nb_char, then this function consumes all the characters it can consume. ++ * ++ *Returns CR_OK if at least one character has been consumed, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_input_consume_chars (CRInput * a_this, guint32 a_char, gulong * a_nb_char) ++{ ++ enum CRStatus status = CR_OK; ++ gulong nb_consumed = 0; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_nb_char, ++ CR_BAD_PARAM_ERROR); ++ ++ g_return_val_if_fail (a_char != 0 || a_nb_char != NULL, ++ CR_BAD_PARAM_ERROR); ++ ++ for (nb_consumed = 0; ((status == CR_OK) ++ && (*a_nb_char > 0 ++ && nb_consumed < *a_nb_char)); ++ nb_consumed++) { ++ status = cr_input_consume_char (a_this, a_char); ++ } ++ ++ *a_nb_char = nb_consumed; ++ ++ if ((nb_consumed > 0) ++ && ((status == CR_PARSING_ERROR) ++ || (status == CR_END_OF_INPUT_ERROR))) { ++ status = CR_OK; ++ } ++ ++ return status; ++} ++ ++/** ++ * cr_input_consume_white_spaces: ++ *@a_this: the "this pointer" of the current instance of #CRInput. ++ *@a_nb_chars: in/out parameter. The number of white spaces to ++ *consume. After return, holds the number of white spaces actually consumed. ++ * ++ *Same as cr_input_consume_chars() but this one consumes white ++ *spaces. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_consume_white_spaces (CRInput * a_this, gulong * a_nb_chars) ++{ ++ enum CRStatus status = CR_OK; ++ guint32 cur_char = 0, ++ nb_consumed = 0; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_nb_chars, ++ CR_BAD_PARAM_ERROR); ++ ++ for (nb_consumed = 0; ++ ((*a_nb_chars > 0) && (nb_consumed < *a_nb_chars)); ++ nb_consumed++) { ++ status = cr_input_peek_char (a_this, &cur_char); ++ if (status != CR_OK) ++ break; ++ ++ /*if the next char is a white space, consume it ! */ ++ if (cr_utils_is_white_space (cur_char) == TRUE) { ++ status = cr_input_read_char (a_this, &cur_char); ++ if (status != CR_OK) ++ break; ++ continue; ++ } ++ ++ break; ++ ++ } ++ ++ *a_nb_chars = (gulong) nb_consumed; ++ ++ if (nb_consumed && status == CR_END_OF_INPUT_ERROR) { ++ status = CR_OK; ++ } ++ ++ return status; ++} ++ ++/** ++ * cr_input_peek_char: ++ *@a_this: the current instance of #CRInput. ++ *@a_char: out parameter. The returned character. ++ * ++ *Same as cr_input_read_char() but does not update the ++ *internal state of the input stream. The next call ++ *to cr_input_peek_char() or cr_input_read_char() will thus ++ *return the same character as the current one. ++ * ++ *Returns CR_OK upon successful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_input_peek_char (CRInput const * a_this, guint32 * a_char) ++{ ++ enum CRStatus status = CR_OK; ++ gulong consumed = 0, ++ nb_bytes_left = 0; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_char, CR_BAD_PARAM_ERROR); ++ ++ if (PRIVATE (a_this)->next_byte_index >= ++ PRIVATE (a_this)->in_buf_size) { ++ return CR_END_OF_INPUT_ERROR; ++ } ++ ++ nb_bytes_left = cr_input_get_nb_bytes_left (a_this); ++ ++ if (nb_bytes_left < 1) { ++ return CR_END_OF_INPUT_ERROR; ++ } ++ ++ status = cr_utils_read_char_from_utf8_buf ++ (PRIVATE (a_this)->in_buf + ++ PRIVATE (a_this)->next_byte_index, ++ nb_bytes_left, a_char, &consumed); ++ ++ return status; ++} ++ ++/** ++ * cr_input_peek_byte: ++ *@a_this: the current instance of #CRInput. ++ *@a_origin: the origin to consider in the calculation ++ *of the position of the byte to peek. ++ *@a_offset: the offset of the byte to peek, starting from ++ *the origin specified by a_origin. ++ *@a_byte: out parameter the peeked byte. ++ * ++ *Gets a byte from the input stream, ++ *starting from the current position in the input stream. ++ *Unlike cr_input_peek_next_byte() this method ++ *does not update the state of the current input stream. ++ *Subsequent calls to cr_input_peek_byte with the same arguments ++ *will return the same byte. ++ * ++ *Returns CR_OK upon successful completion or, ++ *CR_BAD_PARAM_ERROR if at least one of the parameters is invalid; ++ *CR_OUT_OF_BOUNDS_ERROR if the indexed byte is out of bounds. ++ */ ++enum CRStatus ++cr_input_peek_byte (CRInput const * a_this, enum CRSeekPos a_origin, ++ gulong a_offset, guchar * a_byte) ++{ ++ gulong abs_offset = 0; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_byte, CR_BAD_PARAM_ERROR); ++ ++ switch (a_origin) { ++ ++ case CR_SEEK_CUR: ++ abs_offset = PRIVATE (a_this)->next_byte_index - 1 + a_offset; ++ break; ++ ++ case CR_SEEK_BEGIN: ++ abs_offset = a_offset; ++ break; ++ ++ case CR_SEEK_END: ++ abs_offset = PRIVATE (a_this)->in_buf_size - 1 - a_offset; ++ break; ++ ++ default: ++ return CR_BAD_PARAM_ERROR; ++ } ++ ++ if (abs_offset < PRIVATE (a_this)->in_buf_size) { ++ ++ *a_byte = PRIVATE (a_this)->in_buf[abs_offset]; ++ ++ return CR_OK; ++ ++ } else { ++ return CR_END_OF_INPUT_ERROR; ++ } ++} ++ ++/** ++ * cr_input_peek_byte2: ++ *@a_this: the current byte input stream. ++ *@a_offset: the offset of the byte to peek, starting ++ *from the current input position pointer. ++ *@a_eof: out parameter. Is set to true is we reach end of ++ *stream. If set to NULL by the caller, this parameter is not taken ++ *in account. ++ * ++ *Same as cr_input_peek_byte() but with a simplified ++ *interface. ++ * ++ *Returns the read byte or 0 if something bad happened. ++ */ ++guchar ++cr_input_peek_byte2 (CRInput const * a_this, gulong a_offset, gboolean * a_eof) ++{ ++ guchar result = 0; ++ enum CRStatus status = CR_ERROR; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), 0); ++ ++ if (a_eof) ++ *a_eof = FALSE; ++ ++ status = cr_input_peek_byte (a_this, CR_SEEK_CUR, a_offset, &result); ++ ++ if ((status == CR_END_OF_INPUT_ERROR) ++ && a_eof) ++ *a_eof = TRUE; ++ ++ return result; ++} ++ ++/** ++ * cr_input_get_byte_addr: ++ *@a_this: the current instance of #CRInput. ++ *@a_offset: the offset of the byte in the input stream starting ++ *from the beginning of the stream. ++ * ++ *Gets the memory address of the byte located at a given offset ++ *in the input stream. ++ * ++ *Returns the address, otherwise NULL if an error occurred. ++ */ ++guchar * ++cr_input_get_byte_addr (CRInput * a_this, gulong a_offset) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), NULL); ++ ++ if (a_offset >= PRIVATE (a_this)->nb_bytes) { ++ return NULL; ++ } ++ ++ return &PRIVATE (a_this)->in_buf[a_offset]; ++} ++ ++/** ++ * cr_input_get_cur_byte_addr: ++ *@a_this: the current input stream ++ *@a_offset: out parameter. The returned address. ++ * ++ *Gets the address of the current character pointer. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_get_cur_byte_addr (CRInput * a_this, guchar ** a_offset) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_offset, ++ CR_BAD_PARAM_ERROR); ++ ++ if (!PRIVATE (a_this)->next_byte_index) { ++ return CR_START_OF_INPUT_ERROR; ++ } ++ ++ *a_offset = cr_input_get_byte_addr ++ (a_this, PRIVATE (a_this)->next_byte_index - 1); ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_seek_index: ++ *@a_this: the current instance of #CRInput. ++ *@a_origin: the origin to consider during the calculation ++ *of the absolute position of the new "current byte index". ++ *@a_pos: the relative offset of the new "current byte index." ++ *This offset is relative to the origin a_origin. ++ * ++ *Sets the "current byte index" of the current instance ++ *of #CRInput. Next call to cr_input_get_byte() will return ++ *the byte next after the new "current byte index". ++ * ++ *Returns CR_OK upon successful completion otherwise returns ++ *CR_BAD_PARAM_ERROR if at least one of the parameters is not valid ++ *or CR_OUT_BOUNDS_ERROR in case of error. ++ */ ++enum CRStatus ++cr_input_seek_index (CRInput * a_this, enum CRSeekPos a_origin, gint a_pos) ++{ ++ ++ glong abs_offset = 0; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); ++ ++ switch (a_origin) { ++ ++ case CR_SEEK_CUR: ++ abs_offset = PRIVATE (a_this)->next_byte_index - 1 + a_pos; ++ break; ++ ++ case CR_SEEK_BEGIN: ++ abs_offset = a_pos; ++ break; ++ ++ case CR_SEEK_END: ++ abs_offset = PRIVATE (a_this)->in_buf_size - 1 - a_pos; ++ break; ++ ++ default: ++ return CR_BAD_PARAM_ERROR; ++ } ++ ++ if ((abs_offset > 0) ++ && (gulong) abs_offset < PRIVATE (a_this)->nb_bytes) { ++ ++ /*update the input stream's internal state */ ++ PRIVATE (a_this)->next_byte_index = abs_offset + 1; ++ ++ return CR_OK; ++ } ++ ++ return CR_OUT_OF_BOUNDS_ERROR; ++} ++ ++/** ++ * cr_input_get_cur_pos: ++ *@a_this: the current instance of #CRInput. ++ *@a_pos: out parameter. The returned position. ++ * ++ *Gets the position of the "current byte index" which ++ *is basically the position of the last returned byte in the ++ *input stream. ++ * ++ *Returns CR_OK upon successful completion. Otherwise, ++ *CR_BAD_PARAMETER_ERROR if at least one of the arguments is invalid. ++ *CR_START_OF_INPUT if no call to either cr_input_read_byte() ++ *or cr_input_seek_index() have been issued before calling ++ *cr_input_get_cur_pos() ++ *Note that the out parameters of this function are valid if and only if this ++ *function returns CR_OK. ++ */ ++enum CRStatus ++cr_input_get_cur_pos (CRInput const * a_this, CRInputPos * a_pos) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_pos, ++ CR_BAD_PARAM_ERROR); ++ ++ a_pos->next_byte_index = PRIVATE (a_this)->next_byte_index; ++ a_pos->line = PRIVATE (a_this)->line; ++ a_pos->col = PRIVATE (a_this)->col; ++ a_pos->end_of_line = PRIVATE (a_this)->end_of_line; ++ a_pos->end_of_file = PRIVATE (a_this)->end_of_input; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_get_parsing_location: ++ *@a_this: the current instance of #CRInput ++ *@a_loc: the set parsing location. ++ * ++ *Gets the current parsing location. ++ *The Parsing location is a public datastructure that ++ *represents the current line/column/byte offset/ in the input ++ *stream. ++ * ++ *Returns CR_OK upon successful completion, an error ++ *code otherwise. ++ */ ++enum CRStatus ++cr_input_get_parsing_location (CRInput const *a_this, ++ CRParsingLocation *a_loc) ++{ ++ g_return_val_if_fail (a_this ++ && PRIVATE (a_this) ++ && a_loc, ++ CR_BAD_PARAM_ERROR) ; ++ ++ a_loc->line = PRIVATE (a_this)->line ; ++ a_loc->column = PRIVATE (a_this)->col ; ++ if (PRIVATE (a_this)->next_byte_index) { ++ a_loc->byte_offset = PRIVATE (a_this)->next_byte_index - 1 ; ++ } else { ++ a_loc->byte_offset = PRIVATE (a_this)->next_byte_index ; ++ } ++ return CR_OK ; ++} ++ ++/** ++ * cr_input_get_cur_index: ++ *@a_this: the "this pointer" of the current instance of ++ *#CRInput ++ *@a_index: out parameter. The returned index. ++ * ++ *Getter of the next byte index. ++ *It actually returns the index of the ++ *next byte to be read. ++ * ++ *Returns CR_OK upon successful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_input_get_cur_index (CRInput const * a_this, glong * a_index) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_index, CR_BAD_PARAM_ERROR); ++ ++ *a_index = PRIVATE (a_this)->next_byte_index; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_set_cur_index: ++ *@a_this: the "this pointer" of the current instance ++ *of #CRInput . ++ *@a_index: the new index to set. ++ * ++ *Setter of the next byte index. ++ *It sets the index of the next byte to be read. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_set_cur_index (CRInput * a_this, glong a_index) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); ++ ++ PRIVATE (a_this)->next_byte_index = a_index; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_set_end_of_file: ++ *@a_this: the current instance of #CRInput. ++ *@a_eof: the new end of file flag. ++ * ++ *Sets the end of file flag. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_set_end_of_file (CRInput * a_this, gboolean a_eof) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); ++ ++ PRIVATE (a_this)->end_of_input = a_eof; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_get_end_of_file: ++ *@a_this: the current instance of #CRInput. ++ *@a_eof: out parameter the place to put the end of ++ *file flag. ++ * ++ *Gets the end of file flag. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_get_end_of_file (CRInput const * a_this, gboolean * a_eof) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_eof, CR_BAD_PARAM_ERROR); ++ ++ *a_eof = PRIVATE (a_this)->end_of_input; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_set_end_of_line: ++ *@a_this: the current instance of #CRInput. ++ *@a_eol: the new end of line flag. ++ * ++ *Sets the end of line flag. ++ * ++ *Returns CR_OK upon successful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_input_set_end_of_line (CRInput * a_this, gboolean a_eol) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); ++ ++ PRIVATE (a_this)->end_of_line = a_eol; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_get_end_of_line: ++ *@a_this: the current instance of #CRInput ++ *@a_eol: out parameter. The place to put ++ *the returned flag ++ * ++ *Gets the end of line flag of the current input. ++ * ++ *Returns CR_OK upon successful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_input_get_end_of_line (CRInput const * a_this, gboolean * a_eol) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_eol, CR_BAD_PARAM_ERROR); ++ ++ *a_eol = PRIVATE (a_this)->end_of_line; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_input_set_cur_pos: ++ *@a_this: the "this pointer" of the current instance of ++ *#CRInput. ++ *@a_pos: the new position. ++ * ++ *Sets the current position in the input stream. ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_input_set_cur_pos (CRInput * a_this, CRInputPos const * a_pos) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_pos, ++ CR_BAD_PARAM_ERROR); ++ ++ cr_input_set_column_num (a_this, a_pos->col); ++ cr_input_set_line_num (a_this, a_pos->line); ++ cr_input_set_cur_index (a_this, a_pos->next_byte_index); ++ cr_input_set_end_of_line (a_this, a_pos->end_of_line); ++ cr_input_set_end_of_file (a_this, a_pos->end_of_file); ++ ++ return CR_OK; ++} +diff --git a/src/st/croco/cr-input.h b/src/st/croco/cr-input.h +new file mode 100644 +index 000000000..9eb402a87 +--- /dev/null ++++ b/src/st/croco/cr-input.h +@@ -0,0 +1,174 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset:8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See the COPYRIGHTS file for copyrights information. ++ */ ++ ++#ifndef __CR_INPUT_SRC_H__ ++#define __CR_INPUT_SRC_H__ ++ ++ ++#include ++#include "cr-utils.h" ++#include "cr-parsing-location.h" ++ ++G_BEGIN_DECLS ++ ++/** ++ *@file ++ *The libcroco basic input stream class ++ *declaration file. ++ */ ++ ++typedef struct _CRInput CRInput ; ++typedef struct _CRInputPriv CRInputPriv ; ++ ++/** ++ *The #CRInput class provides the abstraction of ++ *an utf8-encoded character stream. ++ */ ++struct _CRInput ++{ ++ CRInputPriv *priv ; ++} ; ++ ++typedef struct _CRInputPos CRInputPos ; ++ ++struct _CRInputPos ++{ ++ glong line ; ++ glong col ; ++ gboolean end_of_file ; ++ gboolean end_of_line ; ++ glong next_byte_index ; ++} ; ++ ++CRInput * ++cr_input_new_from_buf (guchar *a_buf, gulong a_len, ++ enum CREncoding a_enc, gboolean a_free_buf) ; ++CRInput * ++cr_input_new_from_uri (const gchar *a_file_uri, ++ enum CREncoding a_enc) ; ++ ++void ++cr_input_destroy (CRInput *a_this) ; ++ ++void ++cr_input_ref (CRInput *a_this) ; ++ ++gboolean ++cr_input_unref (CRInput *a_this) ; ++ ++enum CRStatus ++cr_input_read_byte (CRInput *a_this, guchar *a_byte) ; ++ ++enum CRStatus ++cr_input_read_char (CRInput *a_this, guint32 *a_char) ; ++ ++enum CRStatus ++cr_input_consume_chars (CRInput *a_this, guint32 a_char, ++ gulong *a_nb_char) ; ++ ++enum CRStatus ++cr_input_consume_char (CRInput *a_this, guint32 a_char) ; ++ ++enum CRStatus ++cr_input_consume_white_spaces (CRInput *a_this, gulong *a_nb_chars) ; ++ ++enum CRStatus ++cr_input_peek_byte (CRInput const *a_this, enum CRSeekPos a_origin, ++ gulong a_offset, guchar *a_byte) ; ++ ++guchar ++cr_input_peek_byte2 (CRInput const *a_this, gulong a_offset, ++ gboolean *a_eof) ; ++ ++enum CRStatus ++cr_input_peek_char (CRInput const *a_this, guint32 *a_char) ; ++ ++guchar * ++cr_input_get_byte_addr (CRInput *a_this, ++ gulong a_offset) ; ++ ++enum CRStatus ++cr_input_get_cur_byte_addr (CRInput *a_this, guchar ** a_offset) ; ++ ++enum CRStatus ++cr_input_seek_index (CRInput *a_this, ++ enum CRSeekPos a_origin, gint a_pos) ; ++ ++enum CRStatus ++cr_input_get_cur_index (CRInput const *a_this, glong *a_index) ; ++ ++enum CRStatus ++cr_input_set_cur_index (CRInput *a_this, glong a_index) ; ++ ++enum CRStatus ++cr_input_get_cur_pos (CRInput const *a_this, CRInputPos * a_pos) ; ++ ++enum CRStatus ++cr_input_set_cur_pos (CRInput *a_this, CRInputPos const *a_pos) ; ++ ++enum CRStatus ++cr_input_get_parsing_location (CRInput const *a_this, ++ CRParsingLocation *a_loc) ; ++ ++enum CRStatus ++cr_input_get_end_of_line (CRInput const *a_this, gboolean *a_eol) ; ++ ++enum CRStatus ++cr_input_set_end_of_line (CRInput *a_this, gboolean a_eol) ; ++ ++enum CRStatus ++cr_input_get_end_of_file (CRInput const *a_this, gboolean *a_eof) ; ++ ++enum CRStatus ++cr_input_set_end_of_file (CRInput *a_this, gboolean a_eof) ; ++ ++enum CRStatus ++cr_input_set_line_num (CRInput *a_this, glong a_line_num) ; ++ ++enum CRStatus ++cr_input_get_line_num (CRInput const *a_this, glong *a_line_num) ; ++ ++enum CRStatus ++cr_input_set_column_num (CRInput *a_this, glong a_col) ; ++ ++enum CRStatus ++cr_input_get_column_num (CRInput const *a_this, glong *a_col) ; ++ ++enum CRStatus ++cr_input_increment_line_num (CRInput *a_this, ++ glong a_increment) ; ++ ++enum CRStatus ++cr_input_increment_col_num (CRInput *a_this, ++ glong a_increment) ; ++ ++glong ++cr_input_get_nb_bytes_left (CRInput const *a_this) ; ++ ++enum CRStatus ++cr_input_end_of_input (CRInput const *a_this, gboolean *a_end_of_input) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_INPUT_SRC_H__*/ ++ +diff --git a/src/st/croco/cr-num.c b/src/st/croco/cr-num.c +new file mode 100644 +index 000000000..d5dbd5fb0 +--- /dev/null ++++ b/src/st/croco/cr-num.c +@@ -0,0 +1,313 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyrights information. ++ */ ++ ++/** ++ *@CRNum: ++ * ++ *The definition ++ *of the #CRNum class. ++ */ ++ ++#include "cr-num.h" ++#include "string.h" ++ ++/** ++ * cr_num_new: ++ * ++ *#CRNum. ++ * ++ *Returns the newly built instance of ++ *#CRNum. ++ */ ++CRNum * ++cr_num_new (void) ++{ ++ CRNum *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRNum)); ++ ++ if (result == NULL) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRNum)); ++ ++ return result; ++} ++ ++/** ++ * cr_num_new_with_val: ++ * @a_val: the numerical value of the number. ++ * @a_type: the type of number. ++ * ++ * A constructor of #CRNum. ++ * ++ * Returns the newly built instance of #CRNum or ++ * NULL if an error arises. ++ */ ++CRNum * ++cr_num_new_with_val (gdouble a_val, enum CRNumType a_type) ++{ ++ CRNum *result = NULL; ++ ++ result = cr_num_new (); ++ ++ g_return_val_if_fail (result, NULL); ++ ++ result->val = a_val; ++ result->type = a_type; ++ ++ return result; ++} ++ ++/** ++ * cr_num_to_string: ++ *@a_this: the current instance of #CRNum. ++ * ++ *Returns the newly built string representation ++ *of the current instance of #CRNum. The returned ++ *string is NULL terminated. The caller *must* ++ *free the returned string. ++ */ ++guchar * ++cr_num_to_string (CRNum const * a_this) ++{ ++ gdouble test_val = 0.0; ++ ++ guchar *tmp_char1 = NULL, ++ *tmp_char2 = NULL, ++ *result = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ test_val = a_this->val - (glong) a_this->val; ++ ++ if (!test_val) { ++ tmp_char1 = (guchar *) g_strdup_printf ("%ld", (glong) a_this->val); ++ } else { ++ tmp_char1 = (guchar *) g_new0 (char, G_ASCII_DTOSTR_BUF_SIZE + 1); ++ if (tmp_char1 != NULL) ++ g_ascii_dtostr ((gchar *) tmp_char1, G_ASCII_DTOSTR_BUF_SIZE, a_this->val); ++ } ++ ++ g_return_val_if_fail (tmp_char1, NULL); ++ ++ switch (a_this->type) { ++ case NUM_LENGTH_EM: ++ tmp_char2 = (guchar *) "em"; ++ break; ++ ++ case NUM_LENGTH_EX: ++ tmp_char2 = (guchar *) "ex"; ++ break; ++ ++ case NUM_LENGTH_PX: ++ tmp_char2 = (guchar *) "px"; ++ break; ++ ++ case NUM_LENGTH_IN: ++ tmp_char2 = (guchar *) "in"; ++ break; ++ ++ case NUM_LENGTH_CM: ++ tmp_char2 = (guchar *) "cm"; ++ break; ++ ++ case NUM_LENGTH_MM: ++ tmp_char2 = (guchar *) "mm"; ++ break; ++ ++ case NUM_LENGTH_PT: ++ tmp_char2 = (guchar *) "pt"; ++ break; ++ ++ case NUM_LENGTH_PC: ++ tmp_char2 = (guchar *) "pc"; ++ break; ++ ++ case NUM_ANGLE_DEG: ++ tmp_char2 = (guchar *) "deg"; ++ break; ++ ++ case NUM_ANGLE_RAD: ++ tmp_char2 = (guchar *) "rad"; ++ break; ++ ++ case NUM_ANGLE_GRAD: ++ tmp_char2 = (guchar *) "grad"; ++ break; ++ ++ case NUM_TIME_MS: ++ tmp_char2 = (guchar *) "ms"; ++ break; ++ ++ case NUM_TIME_S: ++ tmp_char2 = (guchar *) "s"; ++ break; ++ ++ case NUM_FREQ_HZ: ++ tmp_char2 = (guchar *) "Hz"; ++ break; ++ ++ case NUM_FREQ_KHZ: ++ tmp_char2 = (guchar *) "KHz"; ++ break; ++ ++ case NUM_PERCENTAGE: ++ tmp_char2 = (guchar *) "%"; ++ break; ++ case NUM_INHERIT: ++ tmp_char2 = (guchar *) "inherit"; ++ break ; ++ case NUM_AUTO: ++ tmp_char2 = (guchar *) "auto"; ++ break ; ++ case NUM_GENERIC: ++ tmp_char2 = NULL ; ++ break ; ++ default: ++ tmp_char2 = (guchar *) "unknown"; ++ break; ++ } ++ ++ if (tmp_char2) { ++ result = (guchar *) g_strconcat ((gchar *) tmp_char1, tmp_char2, NULL); ++ g_free (tmp_char1); ++ } else { ++ result = tmp_char1; ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_num_copy: ++ *@a_src: the instance of #CRNum to copy. ++ *Must be non NULL. ++ *@a_dest: the destination of the copy. ++ *Must be non NULL ++ * ++ *Copies an instance of #CRNum. ++ * ++ *Returns CR_OK upon successful completion, an ++ *error code otherwise. ++ */ ++enum CRStatus ++cr_num_copy (CRNum * a_dest, CRNum const * a_src) ++{ ++ g_return_val_if_fail (a_dest && a_src, CR_BAD_PARAM_ERROR); ++ ++ memcpy (a_dest, a_src, sizeof (CRNum)); ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_num_dup: ++ *@a_this: the instance of #CRNum to duplicate. ++ * ++ *Duplicates an instance of #CRNum ++ * ++ *Returns the newly created (duplicated) instance of #CRNum. ++ *Must be freed by cr_num_destroy(). ++ */ ++CRNum * ++cr_num_dup (CRNum const * a_this) ++{ ++ CRNum *result = NULL; ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ result = cr_num_new (); ++ g_return_val_if_fail (result, NULL); ++ ++ status = cr_num_copy (result, a_this); ++ g_return_val_if_fail (status == CR_OK, NULL); ++ ++ return result; ++} ++ ++/** ++ * cr_num_set: ++ *Sets an instance of #CRNum. ++ *@a_this: the current instance of #CRNum to be set. ++ *@a_val: the new numerical value to be hold by the current ++ *instance of #CRNum ++ *@a_type: the new type of #CRNum. ++ * ++ * Returns CR_OK upon succesful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_num_set (CRNum * a_this, gdouble a_val, enum CRNumType a_type) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ a_this->val = a_val; ++ a_this->type = a_type; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_num_is_fixed_length: ++ * @a_this: the current instance of #CRNum . ++ * ++ *Tests if the current instance of #CRNum is a fixed ++ *length value or not. Typically a fixed length value ++ *is anything from NUM_LENGTH_EM to NUM_LENGTH_PC. ++ *See the definition of #CRNumType to see what we mean. ++ * ++ *Returns TRUE if the instance of #CRNum is a fixed length number, ++ *FALSE otherwise. ++ */ ++gboolean ++cr_num_is_fixed_length (CRNum const * a_this) ++{ ++ gboolean result = FALSE; ++ ++ g_return_val_if_fail (a_this, FALSE); ++ ++ if (a_this->type >= NUM_LENGTH_EM ++ && a_this->type <= NUM_LENGTH_PC) { ++ result = TRUE ; ++ } ++ return result ; ++} ++ ++/** ++ * cr_num_destroy: ++ *@a_this: the this pointer of ++ *the current instance of #CRNum. ++ * ++ *The destructor of #CRNum. ++ */ ++void ++cr_num_destroy (CRNum * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ g_free (a_this); ++} +diff --git a/src/st/croco/cr-num.h b/src/st/croco/cr-num.h +new file mode 100644 +index 000000000..2b73aaf79 +--- /dev/null ++++ b/src/st/croco/cr-num.h +@@ -0,0 +1,127 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information ++ */ ++ ++ ++/** ++ *@file ++ *The declaration ++ *of the #CRNum class. ++ */ ++ ++#ifndef __CR_NUM_H__ ++#define __CR_NUM_H__ ++ ++#include ++#include "cr-utils.h" ++#include "cr-parsing-location.h" ++ ++G_BEGIN_DECLS ++ ++/** ++ *@file ++ *The declaration of the #CRNum class. ++ * ++ */ ++ ++/** ++ *The different types ++ *of numbers. ++ *Please, do not modify ++ *the declaration order of the enum ++ *members, unless you know ++ *what you are doing. ++ */ ++enum CRNumType ++{ ++ NUM_AUTO = 0, ++ NUM_GENERIC, ++ NUM_LENGTH_EM, ++ NUM_LENGTH_EX, ++ NUM_LENGTH_PX, ++ NUM_LENGTH_IN, ++ NUM_LENGTH_CM, ++ NUM_LENGTH_MM, ++ NUM_LENGTH_PT, ++ NUM_LENGTH_PC, ++ NUM_ANGLE_DEG, ++ NUM_ANGLE_RAD, ++ NUM_ANGLE_GRAD, ++ NUM_TIME_MS, ++ NUM_TIME_S, ++ NUM_FREQ_HZ, ++ NUM_FREQ_KHZ, ++ NUM_PERCENTAGE, ++ NUM_INHERIT, ++ NUM_UNKNOWN_TYPE, ++ NB_NUM_TYPE ++} ; ++ ++ ++/** ++ *An abstraction of a number (num) ++ *as defined in the css2 spec. ++ */ ++typedef struct _CRNum CRNum ; ++ ++/** ++ *An abstraction of a number (num) ++ *as defined in the css2 spec. ++ */ ++struct _CRNum ++{ ++ enum CRNumType type ; ++ gdouble val ; ++ CRParsingLocation location ; ++} ; ++ ++CRNum * ++cr_num_new (void) ; ++ ++CRNum * ++cr_num_new_with_val (gdouble a_val, ++ enum CRNumType a_type) ; ++ ++CRNum * ++cr_num_dup (CRNum const *a_this) ; ++ ++guchar * ++cr_num_to_string (CRNum const *a_this) ; ++ ++enum CRStatus ++cr_num_copy (CRNum *a_dest, CRNum const *a_src) ; ++ ++enum CRStatus ++cr_num_set (CRNum *a_this, gdouble a_val, ++ enum CRNumType a_type) ; ++ ++gboolean ++cr_num_is_fixed_length (CRNum const *a_this) ; ++ ++void ++cr_num_destroy (CRNum *a_this) ; ++ ++ ++G_END_DECLS ++ ++ ++#endif /*__CR_NUM_H__*/ +diff --git a/src/st/croco/cr-om-parser.c b/src/st/croco/cr-om-parser.c +new file mode 100644 +index 000000000..ccc45b3e9 +--- /dev/null ++++ b/src/st/croco/cr-om-parser.c +@@ -0,0 +1,1142 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#include ++#include "cr-utils.h" ++#include "cr-om-parser.h" ++ ++/** ++ *@CROMParser: ++ * ++ *The definition of the CSS Object Model Parser. ++ *This parser uses (and sits) the SAC api of libcroco defined ++ *in cr-parser.h and cr-doc-handler.h ++ */ ++ ++struct _CROMParserPriv { ++ CRParser *parser; ++}; ++ ++#define PRIVATE(a_this) ((a_this)->priv) ++ ++/* ++ *Forward declaration of a type defined later ++ *in this file. ++ */ ++struct _ParsingContext; ++typedef struct _ParsingContext ParsingContext; ++ ++static ParsingContext *new_parsing_context (void); ++ ++static void destroy_context (ParsingContext * a_ctxt); ++ ++static void unrecoverable_error (CRDocHandler * a_this); ++ ++static void error (CRDocHandler * a_this); ++ ++static void property (CRDocHandler * a_this, ++ CRString * a_name, ++ CRTerm * a_expression, ++ gboolean a_important); ++ ++static void end_selector (CRDocHandler * a_this, ++ CRSelector * a_selector_list); ++ ++static void start_selector (CRDocHandler * a_this, ++ CRSelector * a_selector_list); ++ ++static void start_font_face (CRDocHandler * a_this, ++ CRParsingLocation *a_location); ++ ++static void end_font_face (CRDocHandler * a_this); ++ ++static void end_document (CRDocHandler * a_this); ++ ++static void start_document (CRDocHandler * a_this); ++ ++static void charset (CRDocHandler * a_this, ++ CRString * a_charset, ++ CRParsingLocation *a_location); ++ ++static void start_page (CRDocHandler * a_this, CRString * a_page, ++ CRString * a_pseudo_page, ++ CRParsingLocation *a_location); ++ ++static void end_page (CRDocHandler * a_this, CRString * a_page, ++ CRString * a_pseudo_page); ++ ++static void start_media (CRDocHandler * a_this, ++ GList * a_media_list, ++ CRParsingLocation *a_location); ++ ++static void end_media (CRDocHandler * a_this, ++ GList * a_media_list); ++ ++static void import_style (CRDocHandler * a_this, ++ GList * a_media_list, ++ CRString * a_uri, ++ CRString * a_uri_default_ns, ++ CRParsingLocation *a_location); ++ ++struct _ParsingContext { ++ CRStyleSheet *stylesheet; ++ CRStatement *cur_stmt; ++ CRStatement *cur_media_stmt; ++}; ++ ++/******************************************** ++ *Private methods ++ ********************************************/ ++ ++static ParsingContext * ++new_parsing_context (void) ++{ ++ ParsingContext *result = NULL; ++ ++ result = g_try_malloc (sizeof (ParsingContext)); ++ if (!result) { ++ cr_utils_trace_info ("Out of Memory"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (ParsingContext)); ++ return result; ++} ++ ++static void ++destroy_context (ParsingContext * a_ctxt) ++{ ++ g_return_if_fail (a_ctxt); ++ ++ if (a_ctxt->stylesheet) { ++ cr_stylesheet_destroy (a_ctxt->stylesheet); ++ a_ctxt->stylesheet = NULL; ++ } ++ if (a_ctxt->cur_stmt) { ++ cr_statement_destroy (a_ctxt->cur_stmt); ++ a_ctxt->cur_stmt = NULL; ++ } ++ g_free (a_ctxt); ++} ++ ++static enum CRStatus ++cr_om_parser_init_default_sac_handler (CROMParser * a_this) ++{ ++ CRDocHandler *sac_handler = NULL; ++ gboolean created_handler = FALSE; ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && PRIVATE (a_this)->parser, ++ CR_BAD_PARAM_ERROR); ++ ++ status = cr_parser_get_sac_handler (PRIVATE (a_this)->parser, ++ &sac_handler); ++ g_return_val_if_fail (status == CR_OK, status); ++ ++ if (!sac_handler) { ++ sac_handler = cr_doc_handler_new (); ++ created_handler = TRUE; ++ } ++ ++ /* ++ *initialyze here the sac handler. ++ */ ++ sac_handler->start_document = start_document; ++ sac_handler->end_document = end_document; ++ sac_handler->start_selector = start_selector; ++ sac_handler->end_selector = end_selector; ++ sac_handler->property = property; ++ sac_handler->start_font_face = start_font_face; ++ sac_handler->end_font_face = end_font_face; ++ sac_handler->error = error; ++ sac_handler->unrecoverable_error = unrecoverable_error; ++ sac_handler->charset = charset; ++ sac_handler->start_page = start_page; ++ sac_handler->end_page = end_page; ++ sac_handler->start_media = start_media; ++ sac_handler->end_media = end_media; ++ sac_handler->import_style = import_style; ++ ++ if (created_handler) { ++ status = cr_parser_set_sac_handler (PRIVATE (a_this)->parser, ++ sac_handler); ++ cr_doc_handler_unref (sac_handler); ++ } ++ ++ return status; ++ ++} ++ ++static void ++start_document (CRDocHandler * a_this) ++{ ++ ParsingContext *ctxt = NULL; ++ CRStyleSheet *stylesheet = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ ctxt = new_parsing_context (); ++ g_return_if_fail (ctxt); ++ ++ stylesheet = cr_stylesheet_new (NULL); ++ ctxt->stylesheet = stylesheet; ++ cr_doc_handler_set_ctxt (a_this, ctxt); ++} ++ ++static void ++start_font_face (CRDocHandler * a_this, ++ CRParsingLocation *a_location) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ g_return_if_fail (a_this); ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ g_return_if_fail (status == CR_OK && ctxt); ++ g_return_if_fail (ctxt->cur_stmt == NULL); ++ ++ ctxt->cur_stmt = ++ cr_statement_new_at_font_face_rule (ctxt->stylesheet, NULL); ++ ++ g_return_if_fail (ctxt->cur_stmt); ++} ++ ++static void ++end_font_face (CRDocHandler * a_this) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ CRStatement *stmts = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ g_return_if_fail (a_this); ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ g_return_if_fail (status == CR_OK && ctxt); ++ g_return_if_fail ++ (ctxt->cur_stmt ++ && ctxt->cur_stmt->type == AT_FONT_FACE_RULE_STMT ++ && ctxt->stylesheet); ++ ++ stmts = cr_statement_append (ctxt->stylesheet->statements, ++ ctxt->cur_stmt); ++ if (!stmts) ++ goto error; ++ ++ ctxt->stylesheet->statements = stmts; ++ stmts = NULL; ++ ctxt->cur_stmt = NULL; ++ ++ return; ++ ++ error: ++ ++ if (ctxt->cur_stmt) { ++ cr_statement_destroy (ctxt->cur_stmt); ++ ctxt->cur_stmt = NULL; ++ } ++ ++ if (!stmts) { ++ cr_statement_destroy (stmts); ++ stmts = NULL; ++ } ++} ++ ++static void ++end_document (CRDocHandler * a_this) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ ++ g_return_if_fail (a_this); ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ g_return_if_fail (status == CR_OK && ctxt); ++ ++ if (!ctxt->stylesheet || ctxt->cur_stmt) ++ goto error; ++ ++ status = cr_doc_handler_set_result (a_this, ctxt->stylesheet); ++ g_return_if_fail (status == CR_OK); ++ ++ ctxt->stylesheet = NULL; ++ destroy_context (ctxt); ++ cr_doc_handler_set_ctxt (a_this, NULL); ++ ++ return; ++ ++ error: ++ if (ctxt) { ++ destroy_context (ctxt); ++ } ++} ++ ++static void ++charset (CRDocHandler * a_this, CRString * a_charset, ++ CRParsingLocation *a_location) ++{ ++ enum CRStatus status = CR_OK; ++ CRStatement *stmt = NULL, ++ *stmt2 = NULL; ++ CRString *charset = NULL; ++ ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ ++ g_return_if_fail (a_this); ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ g_return_if_fail (status == CR_OK && ctxt); ++ g_return_if_fail (ctxt->stylesheet); ++ ++ charset = cr_string_dup (a_charset) ; ++ stmt = cr_statement_new_at_charset_rule (ctxt->stylesheet, charset); ++ g_return_if_fail (stmt); ++ stmt2 = cr_statement_append (ctxt->stylesheet->statements, stmt); ++ if (!stmt2) { ++ if (stmt) { ++ cr_statement_destroy (stmt); ++ stmt = NULL; ++ } ++ if (charset) { ++ cr_string_destroy (charset); ++ } ++ return; ++ } ++ ctxt->stylesheet->statements = stmt2; ++ stmt2 = NULL; ++} ++ ++static void ++start_page (CRDocHandler * a_this, ++ CRString * a_page, ++ CRString * a_pseudo, ++ CRParsingLocation *a_location) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ ++ g_return_if_fail (a_this); ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ g_return_if_fail (status == CR_OK && ctxt); ++ g_return_if_fail (ctxt->cur_stmt == NULL); ++ ++ ctxt->cur_stmt = cr_statement_new_at_page_rule ++ (ctxt->stylesheet, NULL, NULL, NULL); ++ if (a_page) { ++ ctxt->cur_stmt->kind.page_rule->name = ++ cr_string_dup (a_page) ; ++ ++ if (!ctxt->cur_stmt->kind.page_rule->name) { ++ goto error; ++ } ++ } ++ if (a_pseudo) { ++ ctxt->cur_stmt->kind.page_rule->pseudo = ++ cr_string_dup (a_pseudo) ; ++ if (!ctxt->cur_stmt->kind.page_rule->pseudo) { ++ goto error; ++ } ++ } ++ return; ++ ++ error: ++ if (ctxt->cur_stmt) { ++ cr_statement_destroy (ctxt->cur_stmt); ++ ctxt->cur_stmt = NULL; ++ } ++} ++ ++static void ++end_page (CRDocHandler * a_this, ++ CRString * a_page, ++ CRString * a_pseudo_page) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ CRStatement *stmt = NULL; ++ ++ (void) a_page; ++ (void) a_pseudo_page; ++ ++ g_return_if_fail (a_this); ++ ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ ++ g_return_if_fail (status == CR_OK && ctxt); ++ ++ g_return_if_fail (ctxt->cur_stmt ++ && ctxt->cur_stmt->type == AT_PAGE_RULE_STMT ++ && ctxt->stylesheet); ++ ++ stmt = cr_statement_append (ctxt->stylesheet->statements, ++ ctxt->cur_stmt); ++ ++ if (stmt) { ++ ctxt->stylesheet->statements = stmt; ++ stmt = NULL; ++ ctxt->cur_stmt = NULL; ++ } ++ ++ if (ctxt->cur_stmt) { ++ cr_statement_destroy (ctxt->cur_stmt); ++ ctxt->cur_stmt = NULL; ++ } ++ a_page = NULL; /*keep compiler happy */ ++ a_pseudo_page = NULL; /*keep compiler happy */ ++} ++ ++static void ++start_media (CRDocHandler * a_this, ++ GList * a_media_list, ++ CRParsingLocation *a_location) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ GList *media_list = NULL; ++ ++ g_return_if_fail (a_this); ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ g_return_if_fail (status == CR_OK && ctxt); ++ ++ g_return_if_fail (ctxt ++ && ctxt->cur_stmt == NULL ++ && ctxt->cur_media_stmt == NULL ++ && ctxt->stylesheet); ++ if (a_media_list) { ++ /*duplicate the media_list */ ++ media_list = cr_utils_dup_glist_of_cr_string ++ (a_media_list); ++ } ++ ctxt->cur_media_stmt = ++ cr_statement_new_at_media_rule ++ (ctxt->stylesheet, NULL, media_list); ++ ++} ++ ++static void ++end_media (CRDocHandler * a_this, GList * a_media_list) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ CRStatement *stmts = NULL; ++ ++ (void) a_media_list; ++ ++ g_return_if_fail (a_this); ++ ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ ++ g_return_if_fail (status == CR_OK && ctxt); ++ ++ g_return_if_fail (ctxt ++ && ctxt->cur_media_stmt ++ && ctxt->cur_media_stmt->type == AT_MEDIA_RULE_STMT ++ && ctxt->stylesheet); ++ ++ stmts = cr_statement_append (ctxt->stylesheet->statements, ++ ctxt->cur_media_stmt); ++ ++ if (!stmts) { ++ cr_statement_destroy (ctxt->cur_media_stmt); ++ ctxt->cur_media_stmt = NULL; ++ } ++ ++ ctxt->stylesheet->statements = stmts; ++ stmts = NULL; ++ ++ ctxt->cur_stmt = NULL ; ++ ctxt->cur_media_stmt = NULL ; ++ a_media_list = NULL; ++} ++ ++static void ++import_style (CRDocHandler * a_this, ++ GList * a_media_list, ++ CRString * a_uri, ++ CRString * a_uri_default_ns, ++ CRParsingLocation *a_location) ++{ ++ enum CRStatus status = CR_OK; ++ CRString *uri = NULL; ++ CRStatement *stmt = NULL, ++ *stmt2 = NULL; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ GList *media_list = NULL ; ++ ++ (void) a_uri_default_ns; ++ ++ g_return_if_fail (a_this); ++ ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ ++ g_return_if_fail (status == CR_OK && ctxt); ++ ++ g_return_if_fail (ctxt->stylesheet); ++ ++ uri = cr_string_dup (a_uri) ; ++ ++ if (a_media_list) ++ media_list = cr_utils_dup_glist_of_cr_string (a_media_list) ; ++ ++ stmt = cr_statement_new_at_import_rule ++ (ctxt->stylesheet, uri, media_list, NULL); ++ ++ if (!stmt) ++ goto error; ++ ++ if (ctxt->cur_stmt) { ++ stmt2 = cr_statement_append (ctxt->cur_stmt, stmt); ++ if (!stmt2) ++ goto error; ++ ctxt->cur_stmt = stmt2; ++ stmt2 = NULL; ++ stmt = NULL; ++ } else { ++ stmt2 = cr_statement_append (ctxt->stylesheet->statements, ++ stmt); ++ if (!stmt2) ++ goto error; ++ ctxt->stylesheet->statements = stmt2; ++ stmt2 = NULL; ++ stmt = NULL; ++ } ++ ++ return; ++ ++ error: ++ if (uri) { ++ cr_string_destroy (uri); ++ } ++ ++ if (stmt) { ++ cr_statement_destroy (stmt); ++ stmt = NULL; ++ } ++ a_uri_default_ns = NULL; /*keep compiler happy */ ++} ++ ++static void ++start_selector (CRDocHandler * a_this, CRSelector * a_selector_list) ++{ ++ enum CRStatus status = CR_OK ; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ ++ g_return_if_fail (a_this); ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ g_return_if_fail (status == CR_OK && ctxt); ++ if (ctxt->cur_stmt) { ++ /*hmm, this should be NULL so free it */ ++ cr_statement_destroy (ctxt->cur_stmt); ++ ctxt->cur_stmt = NULL; ++ } ++ ++ ctxt->cur_stmt = cr_statement_new_ruleset ++ (ctxt->stylesheet, a_selector_list, NULL, NULL); ++} ++ ++static void ++end_selector (CRDocHandler * a_this, CRSelector * a_selector_list) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ ++ (void) a_selector_list; ++ ++ g_return_if_fail (a_this); ++ ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ ++ g_return_if_fail (status == CR_OK && ctxt); ++ ++ g_return_if_fail (ctxt->cur_stmt && ctxt->stylesheet); ++ ++ if (ctxt->cur_stmt) { ++ CRStatement *stmts = NULL; ++ ++ if (ctxt->cur_media_stmt) { ++ CRAtMediaRule *media_rule = NULL; ++ ++ media_rule = ctxt->cur_media_stmt->kind.media_rule; ++ ++ stmts = cr_statement_append ++ (media_rule->rulesets, ctxt->cur_stmt); ++ ++ if (!stmts) { ++ cr_utils_trace_info ++ ("Could not append a new statement"); ++ cr_statement_destroy (media_rule->rulesets); ++ ctxt->cur_media_stmt-> ++ kind.media_rule->rulesets = NULL; ++ return; ++ } ++ media_rule->rulesets = stmts; ++ ctxt->cur_stmt = NULL; ++ } else { ++ stmts = cr_statement_append ++ (ctxt->stylesheet->statements, ++ ctxt->cur_stmt); ++ if (!stmts) { ++ cr_utils_trace_info ++ ("Could not append a new statement"); ++ cr_statement_destroy (ctxt->cur_stmt); ++ ctxt->cur_stmt = NULL; ++ return; ++ } ++ ctxt->stylesheet->statements = stmts; ++ ctxt->cur_stmt = NULL; ++ } ++ ++ } ++ ++ a_selector_list = NULL; /*keep compiler happy */ ++} ++ ++static void ++property (CRDocHandler * a_this, ++ CRString * a_name, ++ CRTerm * a_expression, ++ gboolean a_important) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ CRDeclaration *decl = NULL, ++ *decl2 = NULL; ++ CRString *str = NULL; ++ ++ g_return_if_fail (a_this); ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ g_return_if_fail (status == CR_OK && ctxt); ++ ++ /* ++ *make sure a current ruleset statement has been allocated ++ *already. ++ */ ++ g_return_if_fail ++ (ctxt->cur_stmt ++ && ++ (ctxt->cur_stmt->type == RULESET_STMT ++ || ctxt->cur_stmt->type == AT_FONT_FACE_RULE_STMT ++ || ctxt->cur_stmt->type == AT_PAGE_RULE_STMT)); ++ ++ if (a_name) { ++ str = cr_string_dup (a_name); ++ g_return_if_fail (str); ++ } ++ ++ /*instanciates a new declaration */ ++ decl = cr_declaration_new (ctxt->cur_stmt, str, a_expression); ++ g_return_if_fail (decl); ++ str = NULL; ++ decl->important = a_important; ++ /* ++ *add the new declaration to the current statement ++ *being build. ++ */ ++ switch (ctxt->cur_stmt->type) { ++ case RULESET_STMT: ++ decl2 = cr_declaration_append ++ (ctxt->cur_stmt->kind.ruleset->decl_list, decl); ++ if (!decl2) { ++ cr_declaration_destroy (decl); ++ cr_utils_trace_info ++ ("Could not append decl to ruleset"); ++ goto error; ++ } ++ ctxt->cur_stmt->kind.ruleset->decl_list = decl2; ++ decl = NULL; ++ decl2 = NULL; ++ break; ++ ++ case AT_FONT_FACE_RULE_STMT: ++ decl2 = cr_declaration_append ++ (ctxt->cur_stmt->kind.font_face_rule->decl_list, ++ decl); ++ if (!decl2) { ++ cr_declaration_destroy (decl); ++ cr_utils_trace_info ++ ("Could not append decl to ruleset"); ++ goto error; ++ } ++ ctxt->cur_stmt->kind.font_face_rule->decl_list = decl2; ++ decl = NULL; ++ decl2 = NULL; ++ break; ++ case AT_PAGE_RULE_STMT: ++ decl2 = cr_declaration_append ++ (ctxt->cur_stmt->kind.page_rule->decl_list, decl); ++ if (!decl2) { ++ cr_declaration_destroy (decl); ++ cr_utils_trace_info ++ ("Could not append decl to ruleset"); ++ goto error; ++ } ++ ctxt->cur_stmt->kind.page_rule->decl_list = decl2; ++ decl = NULL; ++ decl2 = NULL; ++ break; ++ ++ default: ++ goto error; ++ break; ++ } ++ ++ return; ++ ++ error: ++ if (str) { ++ g_free (str); ++ str = NULL; ++ } ++ ++ if (decl) { ++ cr_declaration_destroy (decl); ++ decl = NULL; ++ } ++} ++ ++static void ++error (CRDocHandler * a_this) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ ++ g_return_if_fail (a_this); ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ g_return_if_fail (status == CR_OK && ctxt); ++ ++ if (ctxt->cur_stmt) { ++ cr_statement_destroy (ctxt->cur_stmt); ++ ctxt->cur_stmt = NULL; ++ } ++} ++ ++static void ++unrecoverable_error (CRDocHandler * a_this) ++{ ++ enum CRStatus status = CR_OK; ++ ParsingContext *ctxt = NULL; ++ ParsingContext **ctxtptr = NULL; ++ ++ ctxtptr = &ctxt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr); ++ g_return_if_fail (status == CR_OK); ++ ++ if (ctxt) { ++ if (ctxt->stylesheet) { ++ status = cr_doc_handler_set_result ++ (a_this, ctxt->stylesheet); ++ g_return_if_fail (status == CR_OK); ++ } ++ g_free (ctxt); ++ cr_doc_handler_set_ctxt (a_this, NULL); ++ } ++} ++ ++/******************************************** ++ *Public methods ++ ********************************************/ ++ ++/** ++ * cr_om_parser_new: ++ *@a_input: the input stream. ++ * ++ *Constructor of the CROMParser. ++ *Returns the newly built instance of #CROMParser. ++ */ ++CROMParser * ++cr_om_parser_new (CRInput * a_input) ++{ ++ CROMParser *result = NULL; ++ enum CRStatus status = CR_OK; ++ ++ result = g_try_malloc (sizeof (CROMParser)); ++ ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CROMParser)); ++ PRIVATE (result) = g_try_malloc (sizeof (CROMParserPriv)); ++ ++ if (!PRIVATE (result)) { ++ cr_utils_trace_info ("Out of memory"); ++ goto error; ++ } ++ ++ memset (PRIVATE (result), 0, sizeof (CROMParserPriv)); ++ ++ PRIVATE (result)->parser = cr_parser_new_from_input (a_input); ++ ++ if (!PRIVATE (result)->parser) { ++ cr_utils_trace_info ("parsing instantiation failed"); ++ goto error; ++ } ++ ++ status = cr_om_parser_init_default_sac_handler (result); ++ ++ if (status != CR_OK) { ++ goto error; ++ } ++ ++ return result; ++ ++ error: ++ ++ if (result) { ++ cr_om_parser_destroy (result); ++ } ++ ++ return NULL; ++} ++ ++/** ++ * cr_om_parser_parse_buf: ++ *@a_this: the current instance of #CROMParser. ++ *@a_buf: the in memory buffer to parse. ++ *@a_len: the length of the in memory buffer in number of bytes. ++ *@a_enc: the encoding of the in memory buffer. ++ *@a_result: out parameter the resulting style sheet ++ * ++ *Parses the content of an in memory buffer. ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_om_parser_parse_buf (CROMParser * a_this, ++ const guchar * a_buf, ++ gulong a_len, ++ enum CREncoding a_enc, CRStyleSheet ** a_result) ++{ ++ ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_this && a_result, CR_BAD_PARAM_ERROR); ++ ++ if (!PRIVATE (a_this)->parser) { ++ PRIVATE (a_this)->parser = cr_parser_new (NULL); ++ } ++ ++ status = cr_parser_parse_buf (PRIVATE (a_this)->parser, ++ a_buf, a_len, a_enc); ++ ++ if (status == CR_OK) { ++ CRStyleSheet *result = NULL; ++ CRStyleSheet **resultptr = NULL; ++ CRDocHandler *sac_handler = NULL; ++ ++ cr_parser_get_sac_handler (PRIVATE (a_this)->parser, ++ &sac_handler); ++ g_return_val_if_fail (sac_handler, CR_ERROR); ++ resultptr = &result; ++ status = cr_doc_handler_get_result (sac_handler, ++ (gpointer *) resultptr); ++ g_return_val_if_fail (status == CR_OK, status); ++ ++ if (result) ++ *a_result = result; ++ } ++ ++ return status; ++} ++ ++/** ++ * cr_om_parser_simply_parse_buf: ++ *@a_buf: the css2 in memory buffer. ++ *@a_len: the length of the in memory buffer. ++ *@a_enc: the encoding of the in memory buffer. ++ *@a_result: out parameter. The resulting css2 style sheet. ++ * ++ *The simpler way to parse an in memory css2 buffer. ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_om_parser_simply_parse_buf (const guchar * a_buf, ++ gulong a_len, ++ enum CREncoding a_enc, ++ CRStyleSheet ** a_result) ++{ ++ CROMParser *parser = NULL; ++ enum CRStatus status = CR_OK; ++ ++ parser = cr_om_parser_new (NULL); ++ if (!parser) { ++ cr_utils_trace_info ("Could not create om parser"); ++ cr_utils_trace_info ("System possibly out of memory"); ++ return CR_ERROR; ++ } ++ ++ status = cr_om_parser_parse_buf (parser, a_buf, a_len, ++ a_enc, a_result); ++ ++ if (parser) { ++ cr_om_parser_destroy (parser); ++ parser = NULL; ++ } ++ ++ return status; ++} ++ ++/** ++ * cr_om_parser_parse_file: ++ *@a_this: the current instance of the cssom parser. ++ *@a_file_uri: the uri of the file. ++ *(only local file paths are suppported so far) ++ *@a_enc: the encoding of the file. ++ *@a_result: out parameter. A pointer ++ *the build css object model. ++ * ++ *Parses a css2 stylesheet contained ++ *in a file. ++ * ++ * Returns CR_OK upon succesful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_om_parser_parse_file (CROMParser * a_this, ++ const guchar * a_file_uri, ++ enum CREncoding a_enc, CRStyleSheet ** a_result) ++{ ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_this && a_file_uri && a_result, ++ CR_BAD_PARAM_ERROR); ++ ++ if (!PRIVATE (a_this)->parser) { ++ PRIVATE (a_this)->parser = cr_parser_new_from_file ++ (a_file_uri, a_enc); ++ } ++ ++ status = cr_parser_parse_file (PRIVATE (a_this)->parser, ++ a_file_uri, a_enc); ++ ++ if (status == CR_OK) { ++ CRStyleSheet *result = NULL; ++ CRStyleSheet **resultptr = NULL; ++ CRDocHandler *sac_handler = NULL; ++ ++ cr_parser_get_sac_handler (PRIVATE (a_this)->parser, ++ &sac_handler); ++ g_return_val_if_fail (sac_handler, CR_ERROR); ++ resultptr = &result; ++ status = cr_doc_handler_get_result ++ (sac_handler, (gpointer *) resultptr); ++ g_return_val_if_fail (status == CR_OK, status); ++ if (result) ++ *a_result = result; ++ } ++ ++ return status; ++} ++ ++/** ++ * cr_om_parser_simply_parse_file: ++ *@a_file_path: the css2 local file path. ++ *@a_enc: the file encoding. ++ *@a_result: out parameter. The returned css stylesheet. ++ *Must be freed by the caller using cr_stylesheet_destroy. ++ * ++ *The simpler method to parse a css2 file. ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ *Note that this method uses cr_om_parser_parse_file() so both methods ++ *have the same return values. ++ */ ++enum CRStatus ++cr_om_parser_simply_parse_file (const guchar * a_file_path, ++ enum CREncoding a_enc, ++ CRStyleSheet ** a_result) ++{ ++ CROMParser *parser = NULL; ++ enum CRStatus status = CR_OK; ++ ++ parser = cr_om_parser_new (NULL); ++ if (!parser) { ++ cr_utils_trace_info ("Could not allocate om parser"); ++ cr_utils_trace_info ("System may be out of memory"); ++ return CR_ERROR; ++ } ++ ++ status = cr_om_parser_parse_file (parser, a_file_path, ++ a_enc, a_result); ++ if (parser) { ++ cr_om_parser_destroy (parser); ++ parser = NULL; ++ } ++ ++ return status; ++} ++ ++/** ++ * cr_om_parser_parse_paths_to_cascade: ++ *@a_this: the current instance of #CROMParser ++ *@a_author_path: the path to the author stylesheet ++ *@a_user_path: the path to the user stylesheet ++ *@a_ua_path: the path to the User Agent stylesheet ++ *@a_encoding: the encoding of the sheets. ++ *@a_result: out parameter. The resulting cascade if the parsing ++ *was okay ++ * ++ *Parses three sheets located by their paths and build a cascade ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise ++ */ ++enum CRStatus ++cr_om_parser_parse_paths_to_cascade (CROMParser * a_this, ++ const guchar * a_author_path, ++ const guchar * a_user_path, ++ const guchar * a_ua_path, ++ enum CREncoding a_encoding, ++ CRCascade ** a_result) ++{ ++ enum CRStatus status = CR_OK; ++ ++ /*0->author sheet, 1->user sheet, 2->UA sheet */ ++ CRStyleSheet *sheets[3]; ++ guchar *paths[3]; ++ CRCascade *result = NULL; ++ gint i = 0; ++ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ memset (sheets, 0, sizeof (CRStyleSheet*) * 3); ++ paths[0] = (guchar *) a_author_path; ++ paths[1] = (guchar *) a_user_path; ++ paths[2] = (guchar *) a_ua_path; ++ ++ for (i = 0; i < 3; i++) { ++ status = cr_om_parser_parse_file (a_this, paths[i], ++ a_encoding, &sheets[i]); ++ if (status != CR_OK) { ++ if (sheets[i]) { ++ cr_stylesheet_unref (sheets[i]); ++ sheets[i] = NULL; ++ } ++ continue; ++ } ++ } ++ result = cr_cascade_new (sheets[0], sheets[1], sheets[2]); ++ if (!result) { ++ for (i = 0; i < 3; i++) { ++ cr_stylesheet_unref (sheets[i]); ++ sheets[i] = 0; ++ } ++ return CR_ERROR; ++ } ++ *a_result = result; ++ return CR_OK; ++} ++ ++/** ++ * cr_om_parser_simply_parse_paths_to_cascade: ++ *@a_author_path: the path to the author stylesheet ++ *@a_user_path: the path to the user stylesheet ++ *@a_ua_path: the path to the User Agent stylesheet ++ *@a_encoding: the encoding of the sheets. ++ *@a_result: out parameter. The resulting cascade if the parsing ++ *was okay ++ * ++ *Parses three sheets located by their paths and build a cascade ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise ++ */ ++enum CRStatus ++cr_om_parser_simply_parse_paths_to_cascade (const guchar * a_author_path, ++ const guchar * a_user_path, ++ const guchar * a_ua_path, ++ enum CREncoding a_encoding, ++ CRCascade ** a_result) ++{ ++ enum CRStatus status = CR_OK; ++ CROMParser *parser = NULL; ++ ++ parser = cr_om_parser_new (NULL); ++ if (!parser) { ++ cr_utils_trace_info ("could not allocated om parser"); ++ cr_utils_trace_info ("System may be out of memory"); ++ return CR_ERROR; ++ } ++ status = cr_om_parser_parse_paths_to_cascade (parser, ++ a_author_path, ++ a_user_path, ++ a_ua_path, ++ a_encoding, a_result); ++ if (parser) { ++ cr_om_parser_destroy (parser); ++ parser = NULL; ++ } ++ return status; ++} ++ ++/** ++ * cr_om_parser_destroy: ++ *@a_this: the current instance of #CROMParser. ++ * ++ *Destructor of the #CROMParser. ++ */ ++void ++cr_om_parser_destroy (CROMParser * a_this) ++{ ++ g_return_if_fail (a_this && PRIVATE (a_this)); ++ ++ if (PRIVATE (a_this)->parser) { ++ cr_parser_destroy (PRIVATE (a_this)->parser); ++ PRIVATE (a_this)->parser = NULL; ++ } ++ ++ if (PRIVATE (a_this)) { ++ g_free (PRIVATE (a_this)); ++ PRIVATE (a_this) = NULL; ++ } ++ ++ if (a_this) { ++ g_free (a_this); ++ a_this = NULL; ++ } ++} +diff --git a/src/st/croco/cr-om-parser.h b/src/st/croco/cr-om-parser.h +new file mode 100644 +index 000000000..13d35b1cd +--- /dev/null ++++ b/src/st/croco/cr-om-parser.h +@@ -0,0 +1,98 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * Copyright (C) 2002-2003 Dodji Seketeli ++ * ++ * 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$ ++ */ ++ ++#ifndef __CR_OM_PARSER_H__ ++#define __CR_OM_PARSER_H__ ++ ++#include "cr-parser.h" ++#include "cr-cascade.h" ++ ++ ++/** ++ *@file ++ *The definition of the CSS Object Model Parser. ++ *This parser uses (and sits) the SAC api of libcroco defined ++ *in cr-parser.h and cr-doc-handler.h ++ */ ++ ++G_BEGIN_DECLS ++ ++typedef struct _CROMParser CROMParser ; ++typedef struct _CROMParserPriv CROMParserPriv ; ++ ++/** ++ *The Object model parser. ++ *Can parse a css file and build a css object model. ++ *This parser uses an instance of #CRParser and defines ++ *a set of SAC callbacks to build the Object Model. ++ */ ++struct _CROMParser ++{ ++ CROMParserPriv *priv ; ++} ; ++ ++CROMParser * cr_om_parser_new (CRInput *a_input) ; ++ ++ ++enum CRStatus cr_om_parser_simply_parse_file (const guchar *a_file_path, ++ enum CREncoding a_enc, ++ CRStyleSheet **a_result) ; ++ ++enum CRStatus cr_om_parser_parse_file (CROMParser *a_this, ++ const guchar *a_file_uri, ++ enum CREncoding a_enc, ++ CRStyleSheet **a_result) ; ++ ++enum CRStatus cr_om_parser_simply_parse_buf (const guchar *a_buf, ++ gulong a_len, ++ enum CREncoding a_enc, ++ CRStyleSheet **a_result) ; ++ ++enum CRStatus cr_om_parser_parse_buf (CROMParser *a_this, ++ const guchar *a_buf, ++ gulong a_len, ++ enum CREncoding a_enc, ++ CRStyleSheet **a_result) ; ++ ++enum CRStatus cr_om_parser_parse_paths_to_cascade (CROMParser *a_this, ++ const guchar *a_author_path, ++ const guchar *a_user_path, ++ const guchar *a_ua_path, ++ enum CREncoding a_encoding, ++ CRCascade ** a_result) ; ++ ++enum CRStatus cr_om_parser_simply_parse_paths_to_cascade (const guchar *a_author_path, ++ const guchar *a_user_path, ++ const guchar *a_ua_path, ++ enum CREncoding a_encoding, ++ CRCascade ** a_result) ; ++ ++void cr_om_parser_destroy (CROMParser *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_OM_PARSER_H__*/ +diff --git a/src/st/croco/cr-parser.c b/src/st/croco/cr-parser.c +new file mode 100644 +index 000000000..07f4ed9e8 +--- /dev/null ++++ b/src/st/croco/cr-parser.c +@@ -0,0 +1,4525 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyrights information. ++ */ ++ ++/** ++ *@CRParser: ++ * ++ *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 syntaxes. ++ * ++ *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_ (...) ; ++ *where 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_ (...) ; ++ *Then css core grammar related parsing function will be called: ++ *cr_parser_parse__core (...) ; ++ * ++ *If a production is defined only by the css core grammar, then ++ *it will be named: ++ *cr_parser_parse__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 ++ ++/** ++ * IS_NUM: ++ *@a_char: the char to test. ++ *return TRUE if the character is a number ([0-9]), FALSE otherwise ++ */ ++#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 ++ *by 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 ; \ ++} ++ ++/** ++ * CHECK_PARSING_STATUS_ERR: ++ *@a_this: the current instance of #CRParser . ++ *@a_status: the status to check. Is of type enum #CRStatus. ++ *@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. ++ * ++ *same as CHECK_PARSING_STATUS() but this one pushes an error ++ *on the parser error stack when an error arises. ++ * ++ */ ++#define CHECK_PARSING_STATUS_ERR(a_this, a_status, a_is_exception,\ ++ a_err_msg, a_err_status) \ ++if ((a_status) != CR_OK) \ ++{ \ ++ if (a_is_exception == FALSE) a_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) \ ++{\ ++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, ++ CRString ** a_inf, ++ CRString ** 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, ++ CRString ** a_str); ++ ++static enum CRStatus cr_parser_parse_ident (CRParser * a_this, ++ CRString ** a_str); ++ ++static enum CRStatus cr_parser_parse_uri (CRParser * a_this, ++ CRString ** a_str); ++ ++static enum CRStatus cr_parser_parse_function (CRParser * a_this, ++ CRString ** a_func_name, ++ CRTerm ** a_expr); ++static enum CRStatus cr_parser_parse_property (CRParser * a_this, ++ CRString ** 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 = (guchar *) g_strdup ((const gchar *) 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; ++} ++ ++/** ++ * cr_parser_try_to_skip_spaces_and_comments: ++ *@a_this: the current instance of #CRParser. ++ * ++ *Same as cr_parser_try_to_skip_spaces() but this one skips ++ *spaces and comments. ++ * ++ *Returns 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 ++ ***************************************/ ++ ++ ++/************************************* ++ *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 successful 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, (const guchar *) "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 { ++ status = CR_PARSING_ERROR ; ++ 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; ++ CRString *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); ++ cr_string_destroy (prop); ++ 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) { ++ cr_string_destroy (prop); ++ 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 COMMENT_TK: ++ case IMPORTANT_SYM_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; ++ CRParsingLocation location = {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); ++ ENSURE_PARSING_COND (status == CR_OK && token ++ && token->type == BO_TK); ++ cr_parsing_location_copy ++ (&location, &token->location) ; ++ cr_token_destroy (token); ++ token = NULL; ++ ++ cr_parser_try_to_skip_spaces_and_comments (a_this); ++ ++ result = cr_attr_sel_new (); ++ if (!result) { ++ cr_utils_trace_info ("result failed") ; ++ status = CR_OUT_OF_MEMORY_ERROR ; ++ goto error ; ++ } ++ cr_parsing_location_copy (&result->location, ++ &location) ; ++ 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, ++ CRString ** 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, TRUE); ++ ++ 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; ++} ++ ++/** ++ * cr_parser_parse_term: ++ *@a_term: out parameter. The successfully parsed term. ++ * ++ *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' ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++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; ++ CRString *func_name = NULL; ++ CRParsingLocation location = {0} ; ++ ++ 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; ++ ++ cr_parsing_location_copy (&location, &token->location) ; ++ if (token->type == DELIM_TK && token->u.unichar == '+') { ++ result->unary_op = PLUS_UOP; ++ 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_OK || !token) ++ goto error; ++ } else if (token->type == DELIM_TK && token->u.unichar == '-') { ++ result->unary_op = MINUS_UOP; ++ 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_OK || !token) ++ goto error; ++ } ++ ++ 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; ++ } ++ cr_parsing_location_copy (&result->location, ++ &location) ; ++ *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) { ++ cr_string_destroy (func_name); ++ func_name = NULL; ++ } ++ ++ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos); ++ ++ return status; ++} ++ ++/** ++ * cr_parser_parse_simple_selector: ++ *@a_this: the "this pointer" of the current instance of #CRParser. ++ *@a_sel: out parameter. Is set to the successfully parsed simple ++ *selector. ++ * ++ *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* ')' ] ++ * ++ *Returns 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); ++ ++ cr_parsing_location_copy ++ (&sel->location, ++ &token->location) ; ++ ++ if (token && token->type == DELIM_TK ++ && token->u.unichar == '*') { ++ sel->type_mask |= UNIVERSAL_SELECTOR; ++ sel->name = cr_string_new_from_string ("*"); ++ 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; ++ ++ cr_parsing_location_copy ++ (&add_sel->location, ++ &token->location) ; ++ 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; ++ ++ cr_parsing_location_copy ++ (&add_sel->location, ++ & token->location) ; ++ } else { ++ status = CR_PARSING_ERROR; ++ 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; ++ cr_parsing_location_copy ++ (&add_sel->location, ++ &attr_sel->location) ; ++ } 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); ++ ++ cr_parsing_location_copy ++ (&pseudo->location, ++ &token->location) ; ++ ++ 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; ++ cr_parsing_location_copy ++ (&add_sel->location, ++ &pseudo->location) ; ++ 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; ++ ++} ++ ++/** ++ * cr_parser_parse_simple_sels: ++ *@a_this: the this pointer of the current instance of #CRParser. ++ *@a_start: a pointer to the ++ *first chararcter of the successfully parsed ++ *string. ++ *@a_end: a pointer to the last character of the successfully parsed ++ *string. ++ * ++ *Parses a "selector" as defined by the css2 spec in appendix D.1: ++ *selector ::= simple_selector [ combinator simple_selector ]* ++ * ++ *Returns 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) ++ && a_sel, ++ 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) { ++ sel->combinator = comb; ++ comb = 0; ++ } ++ if (sel) { ++ *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; ++} ++ ++/** ++ * cr_parser_parse_selector: ++ *@a_this: the current instance of #CRParser. ++ *@a_selector: the parsed list of comma separated ++ *selectors. ++ * ++ *Parses a comma separated list of selectors. ++ * ++ *Returns CR_OK upon successful completion, an error ++ *code otherwise. ++ */ ++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); ++ if (selector) { ++ cr_parsing_location_copy ++ (&selector->location, ++ &simple_sels->location) ; ++ } ++ simple_sels = NULL; ++ } else { ++ status = CR_PARSING_ERROR ; ++ goto error ; ++ } ++ ++ 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; ++} ++ ++/** ++ * cr_parser_parse_function: ++ *@a_this: the "this pointer" of the current instance of #CRParser. ++ * ++ *@a_func_name: out parameter. The parsed function name ++ *@a_expr: out parameter. The successfully parsed term. ++ * ++ *Parses a "function" as defined in css spec at appendix D.1: ++ *function ::= FUNCTION S* expr ')' S* ++ *FUNCTION ::= ident'(' ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++static enum CRStatus ++cr_parser_parse_function (CRParser * a_this, ++ CRString ** 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; ++ ++ cr_parser_try_to_skip_spaces_and_comments (a_this) ; ++ ++ status = cr_parser_parse_expr (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) { ++ cr_string_destroy (*a_func_name); ++ *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; ++} ++ ++/** ++ * cr_parser_parse_uri: ++ *@a_this: the current instance of #CRParser. ++ *@a_str: the successfully parsed url. ++ * ++ *Parses an uri as defined by the css spec [4.1.1]: ++ * URI ::= url\({w}{string}{w}\) ++ * |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\) ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++static enum CRStatus ++cr_parser_parse_uri (CRParser * a_this, CRString ** 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; ++} ++ ++/** ++ * cr_parser_parse_string: ++ *@a_this: the current instance of #CRParser. ++ *@a_start: out parameter. Upon successfull completion, ++ *points to the beginning of the string, points to an undefined value ++ *otherwise. ++ *@a_end: out parameter. Upon successfull completion, points to ++ *the beginning of the string, points to an undefined value otherwise. ++ * ++ *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})*\' ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++static enum CRStatus ++cr_parser_parse_string (CRParser * a_this, CRString ** 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 #CRString. 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, CRString ** 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; ++} ++ ++/** ++ *the next rule is ignored as well. This seems to be a bug ++ *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; ++ CRString *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) { ++ CRParsingLocation location = {0} ; ++ 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, ++ &location); ++ ++ 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, &location); ++ } ++ } else if (status != CR_END_OF_INPUT_ERROR) { ++ status = cr_parser_parse_atrule_core (a_this); ++ CHECK_PARSING_STATUS (status, FALSE); ++ } ++ ++ if (charset) { ++ cr_string_destroy (charset); ++ 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); ++ goto parse_charset ; ++ } 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; ++ } ++ 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) ++ 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; ++ CRString *import_string = NULL; ++ CRParsingLocation location = {0} ; ++ ++ 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, ++ &location); ++ 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, &location) ; ++ ++ 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); ++ CHECK_PARSING_STATUS (status, TRUE) ; ++ } else { ++ goto error ; ++ } ++ ++ /* ++ *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) { ++ cr_string_destroy (cur->data); ++ } ++ } ++ ++ g_list_free (media_list); ++ media_list = NULL; ++ } ++ ++ if (import_string) { ++ cr_string_destroy (import_string); ++ 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, (const guchar *) "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 ++ ****************************************/ ++ ++/** ++ * cr_parser_new: ++ * @a_tknzr: the tokenizer to use for the parsing. ++ * ++ *Creates a new parser to parse data ++ *coming the input stream given in parameter. ++ * ++ *Returns the newly created instance of #CRParser, ++ *or NULL if an error occurred. ++ */ ++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; ++} ++ ++/** ++ * cr_parser_new_from_buf: ++ *@a_buf: the buffer to parse. ++ *@a_len: the length of the data in the buffer. ++ *@a_enc: the encoding of the input buffer a_buf. ++ *@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. ++ * ++ *Instanciates a new parser from a memory buffer. ++ * ++ *Returns the newly built parser, or NULL if an error arises. ++ */ ++CRParser * ++cr_parser_new_from_buf (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; ++} ++ ++/** ++ * cr_parser_new_from_input: ++ * @a_input: the parser input stream to use. ++ * ++ * Returns a newly built parser input. ++ */ ++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; ++} ++ ++/** ++ * cr_parser_new_from_file: ++ * @a_file_uri: the uri of the file to parse. ++ * @a_enc: the file encoding to use. ++ * ++ * Returns the newly built parser. ++ */ ++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; ++} ++ ++/** ++ * cr_parser_set_sac_handler: ++ *@a_this: the "this pointer" of the current instance of #CRParser. ++ *@a_handler: the handler to set. ++ * ++ *Sets a SAC document handler to the parser. ++ * ++ *Returns 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; ++} ++ ++/** ++ * cr_parser_get_sac_handler: ++ *@a_this: the "this pointer" of the current instance of ++ *#CRParser. ++ *@a_handler: out parameter. The returned handler. ++ * ++ *Gets the SAC document handler. ++ * ++ *Returns 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; ++} ++ ++/** ++ * cr_parser_set_default_sac_handler: ++ *@a_this: a pointer to the current instance of #CRParser. ++ * ++ *Sets the SAC handler associated to the current instance ++ *of #CRParser to the default SAC handler. ++ * ++ *Returns 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; ++} ++ ++/** ++ * cr_parser_set_use_core_grammar: ++ * @a_this: the current instance of #CRParser. ++ * @a_use_core_grammar: where to parse against the css core grammar. ++ * ++ * Returns CR_OK upon succesful completion, an error code otherwise. ++ */ ++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; ++} ++ ++/** ++ * cr_parser_get_use_core_grammar: ++ * @a_this: the current instance of #CRParser. ++ * @a_use_core_grammar: wether to use the core grammar or not. ++ * ++ * Returns CR_OK upon succesful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_parser_get_use_core_grammar (CRParser const * 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; ++} ++ ++/** ++ * cr_parser_parse_file: ++ *@a_this: a pointer to the current instance of #CRParser. ++ *@a_file_uri: the uri to the file to load. For the time being, ++ *@a_enc: the encoding of the file to parse. ++ *only local files are supported. ++ * ++ *Parses a the given in parameter. ++ * ++ *Returns 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; ++} ++ ++/** ++ * cr_parser_parse_expr: ++ * @a_this: the current instance of #CRParser. ++ * @a_expr: out parameter. the parsed expression. ++ * ++ *Parses an expression as defined by the css2 spec in appendix ++ *D.1: ++ *expr: term [ operator term ]* ++ * ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++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; ++} ++ ++/** ++ * cr_parser_parse_prio: ++ *@a_this: the current instance of #CRParser. ++ *@a_prio: a string representing the priority. ++ *Today, only "!important" is returned as only this ++ *priority is defined by css2. ++ * ++ *Parses a declaration priority as defined by ++ *the css2 grammar in appendix C: ++ *prio: IMPORTANT_SYM S* ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_parser_parse_prio (CRParser * a_this, CRString ** a_prio) ++{ ++ enum CRStatus status = CR_ERROR; ++ CRInputPos init_pos; ++ CRToken *token = NULL; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_prio ++ && *a_prio == NULL, 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_END_OF_INPUT_ERROR) { ++ goto error; ++ } ++ ENSURE_PARSING_COND (status == CR_OK ++ && token && token->type == IMPORTANT_SYM_TK); ++ ++ cr_parser_try_to_skip_spaces_and_comments (a_this); ++ *a_prio = cr_string_new_from_string ("!important"); ++ 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; ++} ++ ++/** ++ * cr_parser_parse_declaration: ++ *@a_this: the "this pointer" of the current instance of #CRParser. ++ *@a_property: the successfully parsed property. The caller ++ * *must* free the returned pointer. ++ *@a_expr: the expression that represents the attribute value. ++ *The caller *must* free the returned pointer. ++ * ++ *TODO: return the parsed priority, so that ++ *upper layers can take benefit from it. ++ *Parses a "declaration" as defined by the css2 spec in appendix D.1: ++ *declaration ::= [property ':' S* expr prio?]? ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_parser_parse_declaration (CRParser * a_this, ++ CRString ** a_property, ++ CRTerm ** a_expr, gboolean * a_important) ++{ ++ enum CRStatus status = CR_ERROR; ++ CRInputPos init_pos; ++ guint32 cur_char = 0; ++ CRTerm *expr = NULL; ++ CRString *prio = NULL; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_property && a_expr ++ && a_important, CR_BAD_PARAM_ERROR); ++ ++ RECORD_INITIAL_POS (a_this, &init_pos); ++ ++ status = cr_parser_parse_property (a_this, a_property); ++ ++ if (status == CR_END_OF_INPUT_ERROR) ++ goto error; ++ ++ CHECK_PARSING_STATUS_ERR ++ (a_this, status, FALSE, ++ (const guchar *) "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, ++ (const guchar *) "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, ++ (const guchar *) "while parsing declaration: next expression is malformed", ++ CR_SYNTAX_ERROR); ++ ++ cr_parser_try_to_skip_spaces_and_comments (a_this); ++ status = cr_parser_parse_prio (a_this, &prio); ++ if (prio) { ++ cr_string_destroy (prio); ++ prio = NULL; ++ *a_important = TRUE; ++ } else { ++ *a_important = FALSE; ++ } ++ 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) { ++ cr_string_destroy (*a_property); ++ *a_property = NULL; ++ } ++ ++ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos); ++ ++ return status; ++} ++ ++/** ++ * cr_parser_parse_statement_core: ++ *@a_this: the current instance of #CRParser. ++ * ++ *Parses a statement as defined by the css core grammar in ++ *chapter 4.1 of the css2 spec. ++ *statement : ruleset | at-rule; ++ * ++ *Returns 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; ++} ++ ++/** ++ * cr_parser_parse_ruleset: ++ *@a_this: the "this pointer" of the current instance of #CRParser. ++ * ++ *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. ++ * ++ *Returns 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; ++ CRString *property = NULL; ++ CRTerm *expr = NULL; ++ CRSimpleSel *simple_sels = NULL; ++ CRSelector *selector = NULL; ++ gboolean start_selector = FALSE, ++ is_important = FALSE; ++ CRParsingLocation end_parsing_location; ++ ++ 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 == '{', ++ (const guchar *) "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 is 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, ++ &is_important); ++ 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, ++ is_important); ++ } ++ if (status == CR_OK) { ++ /* ++ *free the allocated ++ *'property' and 'term' before parsing ++ *next declarations. ++ */ ++ if (property) { ++ cr_string_destroy (property); ++ property = NULL; ++ } ++ if (expr) { ++ cr_term_unref (expr); ++ expr = NULL; ++ } ++ } else {/*status != CR_OK*/ ++ guint32 c = 0 ; ++ /* ++ *test if we have reached '}', which ++ *would mean that we are parsing an empty ruleset (eg. x{ }) ++ *In that case, goto end_of_ruleset. ++ */ ++ status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, &c) ; ++ if (status == CR_OK && c == '}') { ++ status = CR_OK ; ++ goto end_of_ruleset ; ++ } ++ } ++ CHECK_PARSING_STATUS_ERR ++ (a_this, status, FALSE, ++ (const guchar *) "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, &is_important); ++ ++ 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, is_important); ++ } ++ if (property) { ++ cr_string_destroy (property); ++ property = NULL; ++ } ++ if (expr) { ++ cr_term_unref (expr); ++ expr = NULL; ++ } ++ } ++ ++ end_of_ruleset: ++ cr_parser_try_to_skip_spaces_and_comments (a_this); ++ cr_parser_get_parsing_location (a_this, &end_parsing_location); ++ READ_NEXT_CHAR (a_this, &cur_char); ++ ENSURE_PARSING_COND_ERR ++ (a_this, cur_char == '}', ++ (const guchar *) "while parsing rulset: current char must be a '}'", ++ CR_SYNTAX_ERROR); ++ ++ selector->location = end_parsing_location; ++ 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) { ++ cr_string_destroy (property); ++ } ++ if (selector) { ++ cr_selector_unref (selector); ++ selector = NULL; ++ } ++ ++ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos); ++ ++ return status; ++} ++ ++/** ++ * cr_parser_parse_import: ++ *@a_this: the "this pointer" of the current instance ++ *of #CRParser. ++ *@a_media_list: out parameter. A linked list of ++ *#CRString ++ *Each CRString is a string that contains ++ *a 'medium' declaration part of the successfully ++ *parsed 'import' declaration. ++ *@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. ++ *@a_location: the location (line, column) where the import has been parsed ++ * ++ *Parses an 'import' declaration as defined in the css2 spec ++ *in appendix D.1: ++ * ++ *import ::= ++ *\@import [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S* ++ * ++ *Returns CR_OK upon sucessfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_parser_parse_import (CRParser * a_this, ++ GList ** a_media_list, ++ CRString ** a_import_string, ++ CRParsingLocation *a_location) ++{ ++ enum CRStatus status = CR_OK; ++ CRInputPos init_pos; ++ guint32 cur_char = 0, ++ next_char = 0; ++ CRString *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, 1); ++ if (a_location) { ++ cr_parser_get_parsing_location ++ (a_this, a_location) ; ++ } ++ SKIP_CHARS (a_this, 6); ++ 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); ++ cr_parser_try_to_skip_spaces_and_comments (a_this); ++ } ++ cr_parser_try_to_skip_spaces_and_comments (a_this); ++ 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) { ++ cr_string_destroy (cur->data); ++ } ++ } ++ ++ g_list_free (*a_media_list); ++ *a_media_list = NULL; ++ } ++ ++ if (*a_import_string) { ++ cr_string_destroy (*a_import_string); ++ *a_import_string = NULL; ++ } ++ ++ if (medium) { ++ cr_string_destroy (medium); ++ medium = NULL; ++ } ++ ++ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos); ++ ++ return status; ++} ++ ++/** ++ * cr_parser_parse_media: ++ *@a_this: the "this pointer" of the current instance of #CRParser. ++ * ++ *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. ++ * ++ *Returns 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; ++ CRString *medium = NULL; ++ GList *media_list = NULL; ++ CRParsingLocation location = {0} ; ++ ++ 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_parsing_location_copy (&location, &token->location) ; ++ 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, ++ &location); ++ } ++ ++ 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) { ++ cr_string_destroy (medium); ++ medium = NULL; ++ } ++ ++ if (media_list) { ++ GList *cur = NULL; ++ ++ for (cur = media_list; cur; cur = cur->next) { ++ cr_string_destroy (cur->data); ++ } ++ ++ 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) { ++ cr_string_destroy (medium); ++ medium = NULL; ++ } ++ ++ if (media_list) { ++ GList *cur = NULL; ++ ++ for (cur = media_list; cur; cur = cur->next) { ++ cr_string_destroy (cur->data); ++ } ++ ++ g_list_free (media_list); ++ media_list = NULL; ++ } ++ ++ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos); ++ ++ return status; ++} ++ ++/** ++ * cr_parser_parse_page: ++ *@a_this: the "this pointer" of the current instance of #CRParser. ++ * ++ *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. ++ * ++ *Returns 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; ++ CRString *page_selector = NULL, ++ *page_pseudo_class = NULL, ++ *property = NULL; ++ gboolean important = TRUE; ++ CRParsingLocation location = {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 == PAGE_SYM_TK); ++ ++ cr_parsing_location_copy (&location, &token->location) ; ++ 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 ++ */ ++ 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 == 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, ++ &location); ++ } ++ 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, ++ &important); ++ 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, important); ++ } ++ /* ++ *... and free the data structure passed to that last ++ *SAC handler. ++ */ ++ if (property) { ++ cr_string_destroy (property); ++ 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) { ++ cr_tknzr_unget_token ++ (PRIVATE (a_this)->tknzr, ++ token); ++ token = NULL ; ++ 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, ++ &important); ++ if (status != CR_OK) ++ break ; ++ ++ /* ++ *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, important); ++ } ++ /* ++ *... and free the data structure passed to that last ++ *SAC handler. ++ */ ++ if (property) { ++ cr_string_destroy (property); ++ property = NULL; ++ } ++ if (css_expression) { ++ cr_term_unref (css_expression); ++ css_expression = NULL; ++ } ++ } ++ 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 == 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) { ++ cr_string_destroy (page_selector); ++ page_selector = NULL; ++ } ++ ++ if (page_pseudo_class) { ++ cr_string_destroy (page_pseudo_class); ++ 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) { ++ cr_string_destroy (page_selector); ++ page_selector = NULL; ++ } ++ if (page_pseudo_class) { ++ cr_string_destroy (page_pseudo_class); ++ page_pseudo_class = NULL; ++ } ++ if (property) { ++ cr_string_destroy (property); ++ 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; ++} ++ ++/** ++ * cr_parser_parse_charset: ++ *@a_this: the "this pointer" of the current instance of #CRParser. ++ *@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. ++ *@a_charset_sym_location: the parsing location of the charset rule ++ * ++ *Parses a charset declaration as defined implictly by the css2 spec in ++ *appendix D.1: ++ *charset ::= CHARSET_SYM S* STRING S* ';' ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_parser_parse_charset (CRParser * a_this, CRString ** a_value, ++ CRParsingLocation *a_charset_sym_location) ++{ ++ enum CRStatus status = CR_OK; ++ CRInputPos init_pos; ++ CRToken *token = NULL; ++ CRString *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); ++ if (a_charset_sym_location) { ++ cr_parsing_location_copy (a_charset_sym_location, ++ &token->location) ; ++ } ++ 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) { ++ cr_string_destroy (*a_value); ++ *a_value = NULL; ++ } ++ ++ if (charset_str) { ++ cr_string_destroy (charset_str); ++ charset_str = NULL; ++ } ++ ++ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos); ++ ++ return status; ++} ++ ++/** ++ * cr_parser_parse_font_face: ++ *@a_this: the current instance of #CRParser. ++ * ++ *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. ++ * ++ *Returns 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; ++ CRString *property = NULL; ++ CRTerm *css_expression = NULL; ++ CRToken *token = NULL; ++ gboolean important = FALSE; ++ guint32 next_char = 0, ++ cur_char = 0; ++ CRParsingLocation location = {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_parsing_location_copy (&location, ++ &token->location) ; ++ 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, &location); ++ } ++ 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, &important); ++ 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, important); ++ } ++ ENSURE_PARSING_COND (css_expression && property); ++ } ++ /*free the data structures allocated during last parsing. */ ++ if (property) { ++ cr_string_destroy (property); ++ 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, ++ &important); ++ 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, important); ++ } ++ /* ++ *Then, free the data structures allocated during ++ *last parsing. ++ */ ++ if (property) { ++ cr_string_destroy (property); ++ 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) { ++ cr_string_destroy (property); ++ 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; ++} ++ ++/** ++ * cr_parser_parse: ++ *@a_this: the current instance of #CRParser. ++ * ++ *Parses the data that comes from the ++ *input previously associated to the current instance of ++ *#CRParser. ++ * ++ *Returns CR_OK upon succesful completion, an error code otherwise. ++ */ ++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; ++} ++ ++/** ++ * cr_parser_set_tknzr: ++ * @a_this: the current instance of #CRParser; ++ * @a_tknzr: the new tokenizer. ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++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; ++} ++ ++/** ++ * cr_parser_get_tknzr: ++ *@a_this: the current instance of #CRParser ++ *@a_tknzr: out parameter. The returned tokenizer ++ * ++ *Getter of the parser's underlying tokenizer ++ * ++ *Returns CR_OK upon succesful completion, an error code ++ *otherwise ++ */ ++enum CRStatus ++cr_parser_get_tknzr (CRParser * a_this, CRTknzr ** a_tknzr) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_tknzr, CR_BAD_PARAM_ERROR); ++ ++ *a_tknzr = PRIVATE (a_this)->tknzr; ++ return CR_OK; ++} ++ ++/** ++ * cr_parser_get_parsing_location: ++ *@a_this: the current instance of #CRParser ++ *@a_loc: the parsing location to get. ++ * ++ *Gets the current parsing location. ++ * ++ *Returns CR_OK upon succesful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_parser_get_parsing_location (CRParser const *a_this, ++ CRParsingLocation *a_loc) ++{ ++ g_return_val_if_fail (a_this ++ && PRIVATE (a_this) ++ && a_loc, CR_BAD_PARAM_ERROR) ; ++ ++ return cr_tknzr_get_parsing_location ++ (PRIVATE (a_this)->tknzr, a_loc) ; ++} ++ ++/** ++ * cr_parser_parse_buf: ++ *@a_this: the current instance of #CRparser ++ *@a_buf: the input buffer ++ *@a_len: the length of the input buffer ++ *@a_enc: the encoding of the buffer ++ * ++ *Parses a stylesheet from a buffer ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++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 ((guchar*)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; ++} ++ ++/** ++ * cr_parser_destroy: ++ *@a_this: the current instance of #CRParser to ++ *destroy. ++ * ++ *Destroys the current instance ++ *of #CRParser. ++ */ ++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 */ ++ } ++} +diff --git a/src/st/croco/cr-parser.h b/src/st/croco/cr-parser.h +new file mode 100644 +index 000000000..6dce9439e +--- /dev/null ++++ b/src/st/croco/cr-parser.h +@@ -0,0 +1,128 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyrights information. ++ */ ++ ++#ifndef __CR_PARSER_H__ ++#define __CR_PARSER_H__ ++ ++#include ++#include "cr-input.h" ++#include "cr-tknzr.h" ++#include "cr-utils.h" ++#include "cr-doc-handler.h" ++ ++G_BEGIN_DECLS ++ ++/** ++ *@file ++ *The declaration file ++ *of the #CRParser class. ++ */ ++typedef struct _CRParser CRParser ; ++typedef struct _CRParserPriv CRParserPriv ; ++ ++ ++/** ++ *The implementation of ++ *the SAC parser. ++ *The Class is opaque ++ *and must be manipulated through ++ *the provided methods. ++ */ ++struct _CRParser { ++ CRParserPriv *priv ; ++} ; ++ ++ ++CRParser * cr_parser_new (CRTknzr *a_tknzr) ; ++ ++CRParser * cr_parser_new_from_buf (guchar *a_buf, gulong a_len, ++ enum CREncoding a_enc, ++ gboolean a_free_buf) ; ++ ++CRParser * cr_parser_new_from_file (const guchar *a_file_uri, ++ enum CREncoding a_enc) ; ++ ++CRParser * cr_parser_new_from_input (CRInput *a_input) ; ++ ++enum CRStatus cr_parser_set_tknzr (CRParser *a_this, CRTknzr *a_tknzr) ; ++ ++enum CRStatus cr_parser_get_tknzr (CRParser *a_this, CRTknzr **a_tknzr) ; ++ ++enum CRStatus cr_parser_get_parsing_location (CRParser const *a_this, CRParsingLocation *a_loc) ; ++ ++enum CRStatus cr_parser_try_to_skip_spaces_and_comments (CRParser *a_this) ; ++ ++ ++enum CRStatus cr_parser_set_sac_handler (CRParser *a_this, ++ CRDocHandler *a_handler) ; ++ ++enum CRStatus cr_parser_get_sac_handler (CRParser *a_this, ++ CRDocHandler **a_handler) ; ++ ++enum CRStatus cr_parser_set_use_core_grammar (CRParser *a_this, ++ gboolean a_use_core_grammar) ; ++enum CRStatus cr_parser_get_use_core_grammar (CRParser const *a_this, ++ gboolean *a_use_core_grammar) ; ++ ++enum CRStatus cr_parser_parse (CRParser *a_this) ; ++ ++enum CRStatus cr_parser_parse_file (CRParser *a_this, ++ const guchar *a_file_uri, ++ enum CREncoding a_enc) ; ++ ++enum CRStatus cr_parser_parse_buf (CRParser *a_this, const guchar *a_buf, ++ gulong a_len, enum CREncoding a_enc) ; ++ ++enum CRStatus cr_parser_set_default_sac_handler (CRParser *a_this) ; ++ ++enum CRStatus cr_parser_parse_term (CRParser *a_this, CRTerm **a_term) ; ++ ++enum CRStatus cr_parser_parse_expr (CRParser *a_this, CRTerm **a_expr) ; ++ ++enum CRStatus cr_parser_parse_prio (CRParser *a_this, CRString **a_prio) ; ++ ++enum CRStatus cr_parser_parse_declaration (CRParser *a_this, CRString **a_property, ++ CRTerm **a_expr, gboolean *a_important) ; ++ ++enum CRStatus cr_parser_parse_statement_core (CRParser *a_this) ; ++ ++enum CRStatus cr_parser_parse_ruleset (CRParser *a_this) ; ++ ++enum CRStatus cr_parser_parse_import (CRParser *a_this, GList ** a_media_list, ++ CRString **a_import_string, ++ CRParsingLocation *a_location) ; ++ ++enum CRStatus cr_parser_parse_media (CRParser *a_this) ; ++ ++enum CRStatus cr_parser_parse_page (CRParser *a_this) ; ++ ++enum CRStatus cr_parser_parse_charset (CRParser *a_this, CRString **a_value, ++ CRParsingLocation *a_charset_sym_location) ; ++ ++enum CRStatus cr_parser_parse_font_face (CRParser *a_this) ; ++ ++void cr_parser_destroy (CRParser *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_PARSER_H__*/ +diff --git a/src/st/croco/cr-parsing-location.c b/src/st/croco/cr-parsing-location.c +new file mode 100644 +index 000000000..4fe4acc30 +--- /dev/null ++++ b/src/st/croco/cr-parsing-location.c +@@ -0,0 +1,172 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli. ++ * See the COPYRIGHTS file for copyright information. ++ */ ++ ++#include ++#include "cr-parsing-location.h" ++ ++/** ++ *@CRParsingLocation: ++ * ++ *Definition of the #CRparsingLocation class. ++ */ ++ ++ ++/** ++ * cr_parsing_location_new: ++ *Instanciates a new parsing location. ++ * ++ *Returns the newly instanciated #CRParsingLocation. ++ *Must be freed by cr_parsing_location_destroy() ++ */ ++CRParsingLocation * ++cr_parsing_location_new (void) ++{ ++ CRParsingLocation * result = NULL ; ++ ++ result = g_try_malloc (sizeof (CRParsingLocation)) ; ++ if (!result) { ++ cr_utils_trace_info ("Out of memory error") ; ++ return NULL ; ++ } ++ cr_parsing_location_init (result) ; ++ return result ; ++} ++ ++/** ++ * cr_parsing_location_init: ++ *@a_this: the current instance of #CRParsingLocation. ++ * ++ *Initializes the an instance of #CRparsingLocation. ++ * ++ *Returns CR_OK upon succesful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_parsing_location_init (CRParsingLocation *a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; ++ ++ memset (a_this, 0, sizeof (CRParsingLocation)) ; ++ return CR_OK ; ++} ++ ++/** ++ * cr_parsing_location_copy: ++ *@a_to: the destination of the copy. ++ *Must be allocated by the caller. ++ *@a_from: the source of the copy. ++ * ++ *Copies an instance of CRParsingLocation into another one. ++ * ++ *Returns CR_OK upon succesful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_parsing_location_copy (CRParsingLocation *a_to, ++ CRParsingLocation const *a_from) ++{ ++ g_return_val_if_fail (a_to && a_from, CR_BAD_PARAM_ERROR) ; ++ ++ memcpy (a_to, a_from, sizeof (CRParsingLocation)) ; ++ return CR_OK ; ++} ++ ++/** ++ * cr_parsing_location_to_string: ++ *@a_this: the current instance of #CRParsingLocation. ++ *@a_mask: a bitmap that defines which parts of the ++ *parsing location are to be serialized (line, column or byte offset) ++ * ++ *Returns the serialized string or NULL in case of an error. ++ */ ++gchar * ++cr_parsing_location_to_string (CRParsingLocation const *a_this, ++ enum CRParsingLocationSerialisationMask a_mask) ++{ ++ GString *result = NULL ; ++ gchar *str = NULL ; ++ ++ g_return_val_if_fail (a_this, NULL) ; ++ ++ if (!a_mask) { ++ a_mask = DUMP_LINE | DUMP_COLUMN | DUMP_BYTE_OFFSET ; ++ } ++ result =g_string_new (NULL) ; ++ if (!result) ++ return NULL ; ++ if (a_mask & DUMP_LINE) { ++ g_string_append_printf (result, "line:%d ", ++ a_this->line) ; ++ } ++ if (a_mask & DUMP_COLUMN) { ++ g_string_append_printf (result, "column:%d ", ++ a_this->column) ; ++ } ++ if (a_mask & DUMP_BYTE_OFFSET) { ++ g_string_append_printf (result, "byte offset:%d ", ++ a_this->byte_offset) ; ++ } ++ if (result->len) { ++ str = result->str ; ++ g_string_free (result, FALSE) ; ++ } else { ++ g_string_free (result, TRUE) ; ++ } ++ return str ; ++} ++ ++/** ++ * cr_parsing_location_dump: ++ * @a_this: current instance of #CRParsingLocation ++ * @a_mask: the serialization mask. ++ * @a_fp: the file pointer to dump the parsing location to. ++ */ ++void ++cr_parsing_location_dump (CRParsingLocation const *a_this, ++ enum CRParsingLocationSerialisationMask a_mask, ++ FILE *a_fp) ++{ ++ gchar *str = NULL ; ++ ++ g_return_if_fail (a_this && a_fp) ; ++ str = cr_parsing_location_to_string (a_this, a_mask) ; ++ if (str) { ++ fprintf (a_fp, "%s", str) ; ++ g_free (str) ; ++ str = NULL ; ++ } ++} ++ ++/** ++ * cr_parsing_location_destroy: ++ *@a_this: the current instance of #CRParsingLocation. Must ++ *have been allocated with cr_parsing_location_new(). ++ * ++ *Destroys the current instance of #CRParsingLocation ++ */ ++void ++cr_parsing_location_destroy (CRParsingLocation *a_this) ++{ ++ g_return_if_fail (a_this) ; ++ g_free (a_this) ; ++} ++ +diff --git a/src/st/croco/cr-parsing-location.h b/src/st/croco/cr-parsing-location.h +new file mode 100644 +index 000000000..b8064a560 +--- /dev/null ++++ b/src/st/croco/cr-parsing-location.h +@@ -0,0 +1,70 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli. ++ * See the COPYRIGHTS file for copyright information. ++ */ ++ ++#ifndef __CR_PARSING_LOCATION_H__ ++#define __CR_PARSING_LOCATION_H__ ++ ++#include "cr-utils.h" ++ ++G_BEGIN_DECLS ++ ++/** ++ *@file ++ *The declaration of the CRParsingLocation ++ *object. This object keeps track of line/column/byte offset/ ++ *at which the parsing of a given CSS construction appears. ++ */ ++ ++typedef struct _CRParsingLocation CRParsingLocation; ++struct _CRParsingLocation { ++ guint line ; ++ guint column ; ++ guint byte_offset ; ++} ; ++ ++ ++enum CRParsingLocationSerialisationMask { ++ DUMP_LINE = 1, ++ DUMP_COLUMN = 1 << 1, ++ DUMP_BYTE_OFFSET = 1 << 2 ++} ; ++ ++CRParsingLocation * cr_parsing_location_new (void) ; ++ ++enum CRStatus cr_parsing_location_init (CRParsingLocation *a_this) ; ++ ++enum CRStatus cr_parsing_location_copy (CRParsingLocation *a_to, ++ CRParsingLocation const *a_from) ; ++ ++gchar * cr_parsing_location_to_string (CRParsingLocation const *a_this, ++ enum CRParsingLocationSerialisationMask a_mask) ; ++void cr_parsing_location_dump (CRParsingLocation const *a_this, ++ enum CRParsingLocationSerialisationMask a_mask, ++ FILE *a_fp) ; ++ ++void cr_parsing_location_destroy (CRParsingLocation *a_this) ; ++ ++ ++ ++G_END_DECLS ++#endif +diff --git a/src/st/croco/cr-prop-list.c b/src/st/croco/cr-prop-list.c +new file mode 100644 +index 000000000..70a04f337 +--- /dev/null ++++ b/src/st/croco/cr-prop-list.c +@@ -0,0 +1,404 @@ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyrights information. ++ */ ++ ++#include ++#include "cr-prop-list.h" ++ ++#define PRIVATE(a_obj) (a_obj)->priv ++ ++struct _CRPropListPriv { ++ CRString *prop; ++ CRDeclaration *decl; ++ CRPropList *next; ++ CRPropList *prev; ++}; ++ ++static CRPropList *cr_prop_list_allocate (void); ++ ++/** ++ *Default allocator of CRPropList ++ *@return the newly allocated CRPropList or NULL ++ *if an error arises. ++ */ ++static CRPropList * ++cr_prop_list_allocate (void) ++{ ++ CRPropList *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRPropList)); ++ if (!result) { ++ cr_utils_trace_info ("could not allocate CRPropList"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (CRPropList)); ++ PRIVATE (result) = g_try_malloc (sizeof (CRPropListPriv)); ++ if (!result) { ++ cr_utils_trace_info ("could not allocate CRPropListPriv"); ++ g_free (result); ++ return NULL; ++ } ++ memset (PRIVATE (result), 0, sizeof (CRPropListPriv)); ++ return result; ++} ++ ++/**************** ++ *public methods ++ ***************/ ++ ++/** ++ * cr_prop_list_append: ++ *@a_this: the current instance of #CRPropList ++ *@a_to_append: the property list to append ++ * ++ *Appends a property list to the current one. ++ * ++ *Returns the resulting prop list, or NULL if an error ++ *occurred ++ */ ++CRPropList * ++cr_prop_list_append (CRPropList * a_this, CRPropList * a_to_append) ++{ ++ CRPropList *cur = NULL; ++ ++ g_return_val_if_fail (a_to_append, NULL); ++ ++ if (!a_this) ++ return a_to_append; ++ ++ /*go fetch the last element of the list */ ++ for (cur = a_this; ++ cur && PRIVATE (cur) && PRIVATE (cur)->next; ++ cur = PRIVATE (cur)->next) ; ++ g_return_val_if_fail (cur, NULL); ++ PRIVATE (cur)->next = a_to_append; ++ PRIVATE (a_to_append)->prev = cur; ++ return a_this; ++} ++ ++/** ++ * cr_prop_list_append2: ++ *Appends a pair of prop/declaration to ++ *the current prop list. ++ *@a_this: the current instance of #CRPropList ++ *@a_prop: the property to consider ++ *@a_decl: the declaration to consider ++ * ++ *Returns the resulting property list, or NULL in case ++ *of an error. ++ */ ++CRPropList * ++cr_prop_list_append2 (CRPropList * a_this, ++ CRString * a_prop, ++ CRDeclaration * a_decl) ++{ ++ CRPropList *list = NULL, ++ *result = NULL; ++ ++ g_return_val_if_fail (a_prop && a_decl, NULL); ++ ++ list = cr_prop_list_allocate (); ++ g_return_val_if_fail (list && PRIVATE (list), NULL); ++ ++ PRIVATE (list)->prop = a_prop; ++ PRIVATE (list)->decl = a_decl; ++ ++ result = cr_prop_list_append (a_this, list); ++ return result; ++} ++ ++/** ++ * cr_prop_list_prepend: ++ *@a_this: the current instance of #CRPropList ++ *@a_to_prepend: the new list to prepend. ++ * ++ *Prepends a list to the current list ++ *Returns the new properties list. ++ */ ++CRPropList * ++cr_prop_list_prepend (CRPropList * a_this, CRPropList * a_to_prepend) ++{ ++ CRPropList *cur = NULL; ++ ++ g_return_val_if_fail (a_to_prepend, NULL); ++ ++ if (!a_this) ++ return a_to_prepend; ++ ++ for (cur = a_to_prepend; cur && PRIVATE (cur)->next; ++ cur = PRIVATE (cur)->next) ; ++ g_return_val_if_fail (cur, NULL); ++ PRIVATE (cur)->next = a_this; ++ PRIVATE (a_this)->prev = cur; ++ return a_to_prepend; ++} ++ ++/** ++ * cr_prop_list_prepend2: ++ *@a_this: the current instance of #CRPropList ++ *@a_prop_name: property name to append ++ *@a_decl: the property value to append. ++ * ++ *Prepends a propertie to a list of properties ++ * ++ *Returns the new property list. ++ */ ++CRPropList * ++cr_prop_list_prepend2 (CRPropList * a_this, ++ CRString * a_prop_name, CRDeclaration * a_decl) ++{ ++ CRPropList *list = NULL, ++ *result = NULL; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_prop_name && a_decl, NULL); ++ ++ list = cr_prop_list_allocate (); ++ g_return_val_if_fail (list, NULL); ++ PRIVATE (list)->prop = a_prop_name; ++ PRIVATE (list)->decl = a_decl; ++ result = cr_prop_list_prepend (a_this, list); ++ return result; ++} ++ ++/** ++ * cr_prop_list_set_prop: ++ *@a_this: the current instance of #CRPropList ++ *@a_prop: the property to set ++ * ++ *Sets the property of a CRPropList ++ */ ++enum CRStatus ++cr_prop_list_set_prop (CRPropList * a_this, CRString * a_prop) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_prop, CR_BAD_PARAM_ERROR); ++ ++ PRIVATE (a_this)->prop = a_prop; ++ return CR_OK; ++} ++ ++/** ++ * cr_prop_list_get_prop: ++ *@a_this: the current instance of #CRPropList ++ *@a_prop: out parameter. The returned property ++ * ++ *Getter of the property associated to the current instance ++ *of #CRPropList ++ * ++ *Returns CR_OK upon successful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_prop_list_get_prop (CRPropList const * a_this, CRString ** a_prop) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_prop, CR_BAD_PARAM_ERROR); ++ ++ *a_prop = PRIVATE (a_this)->prop; ++ return CR_OK; ++} ++ ++/** ++ * cr_prop_list_set_decl: ++ * @a_this: the current instance of #CRPropList ++ * @a_decl: the new property value. ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_prop_list_set_decl (CRPropList * a_this, CRDeclaration * a_decl) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_decl, CR_BAD_PARAM_ERROR); ++ ++ PRIVATE (a_this)->decl = a_decl; ++ return CR_OK; ++} ++ ++/** ++ * cr_prop_list_get_decl: ++ * @a_this: the current instance of #CRPropList ++ * @a_decl: out parameter. The property value ++ * ++ * Returns CR_OK upon successful completion. ++ */ ++enum CRStatus ++cr_prop_list_get_decl (CRPropList const * a_this, CRDeclaration ** a_decl) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) ++ && a_decl, CR_BAD_PARAM_ERROR); ++ ++ *a_decl = PRIVATE (a_this)->decl; ++ return CR_OK; ++} ++ ++/** ++ * cr_prop_list_lookup_prop: ++ *@a_this: the current instance of #CRPropList ++ *@a_prop: the property to lookup ++ *@a_prop_list: out parameter. The property/declaration ++ *pair found (if and only if the function returned code if CR_OK) ++ * ++ *Lookup a given property/declaration pair ++ * ++ *Returns CR_OK if a prop/decl pair has been found, ++ *CR_VALUE_NOT_FOUND_ERROR if not, or an error code if something ++ *bad happens. ++ */ ++enum CRStatus ++cr_prop_list_lookup_prop (CRPropList * a_this, ++ CRString * a_prop, CRPropList ** a_pair) ++{ ++ CRPropList *cur = NULL; ++ ++ g_return_val_if_fail (a_prop && a_pair, CR_BAD_PARAM_ERROR); ++ ++ if (!a_this) ++ return CR_VALUE_NOT_FOUND_ERROR; ++ ++ g_return_val_if_fail (PRIVATE (a_this), CR_BAD_PARAM_ERROR); ++ ++ for (cur = a_this; cur; cur = PRIVATE (cur)->next) { ++ if (PRIVATE (cur)->prop ++ && PRIVATE (cur)->prop->stryng ++ && PRIVATE (cur)->prop->stryng->str ++ && a_prop->stryng ++ && a_prop->stryng->str ++ && !strcmp (PRIVATE (cur)->prop->stryng->str, ++ a_prop->stryng->str)) ++ break; ++ } ++ ++ if (cur) { ++ *a_pair = cur; ++ return CR_OK; ++ } ++ ++ return CR_VALUE_NOT_FOUND_ERROR; ++} ++ ++/** ++ * cr_prop_list_get_next: ++ *@a_this: the current instance of CRPropList ++ * ++ *Gets the next prop/decl pair in the list ++ * ++ *Returns the next prop/declaration pair of the list, ++ *or NULL if we reached end of list (or if an error occurs) ++ */ ++CRPropList * ++cr_prop_list_get_next (CRPropList * a_this) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), NULL); ++ ++ return PRIVATE (a_this)->next; ++} ++ ++/** ++ * cr_prop_list_get_prev: ++ *@a_this: the current instance of CRPropList ++ * ++ *Gets the previous prop/decl pair in the list ++ * ++ *Returns the previous prop/declaration pair of the list, ++ *or NULL if we reached end of list (or if an error occurs) ++ */ ++CRPropList * ++cr_prop_list_get_prev (CRPropList * a_this) ++{ ++ g_return_val_if_fail (a_this && PRIVATE (a_this), NULL); ++ ++ return PRIVATE (a_this)->prev; ++} ++ ++/** ++ * cr_prop_list_unlink: ++ *@a_this: the current list of prop/decl pairs ++ *@a_pair: the prop/decl pair to unlink. ++ * ++ *Unlinks a prop/decl pair from the list ++ * ++ *Returns the new list or NULL in case of an error. ++ */ ++CRPropList * ++cr_prop_list_unlink (CRPropList * a_this, CRPropList * a_pair) ++{ ++ CRPropList *prev = NULL, ++ *next = NULL; ++ ++ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_pair, NULL); ++ ++ /*some sanity checks */ ++ if (PRIVATE (a_pair)->next) { ++ next = PRIVATE (a_pair)->next; ++ g_return_val_if_fail (PRIVATE (next), NULL); ++ g_return_val_if_fail (PRIVATE (next)->prev == a_pair, NULL); ++ } ++ if (PRIVATE (a_pair)->prev) { ++ prev = PRIVATE (a_pair)->prev; ++ g_return_val_if_fail (PRIVATE (prev), NULL); ++ g_return_val_if_fail (PRIVATE (prev)->next == a_pair, NULL); ++ } ++ if (prev) { ++ PRIVATE (prev)->next = next; ++ } ++ if (next) { ++ PRIVATE (next)->prev = prev; ++ } ++ PRIVATE (a_pair)->prev = PRIVATE (a_pair)->next = NULL; ++ if (a_this == a_pair) { ++ if (next) ++ return next; ++ return NULL; ++ } ++ return a_this; ++} ++ ++/** ++ * cr_prop_list_destroy: ++ * @a_this: the current instance of #CRPropList ++ */ ++void ++cr_prop_list_destroy (CRPropList * a_this) ++{ ++ CRPropList *tail = NULL, ++ *cur = NULL; ++ ++ g_return_if_fail (a_this && PRIVATE (a_this)); ++ ++ for (tail = a_this; ++ tail && PRIVATE (tail) && PRIVATE (tail)->next; ++ tail = cr_prop_list_get_next (tail)) ; ++ g_return_if_fail (tail); ++ ++ cur = tail; ++ ++ while (cur) { ++ tail = PRIVATE (cur)->prev; ++ if (tail && PRIVATE (tail)) ++ PRIVATE (tail)->next = NULL; ++ PRIVATE (cur)->prev = NULL; ++ g_free (PRIVATE (cur)); ++ PRIVATE (cur) = NULL; ++ g_free (cur); ++ cur = tail; ++ } ++} +diff --git a/src/st/croco/cr-prop-list.h b/src/st/croco/cr-prop-list.h +new file mode 100644 +index 000000000..797ba43ea +--- /dev/null ++++ b/src/st/croco/cr-prop-list.h +@@ -0,0 +1,80 @@ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyrights information. ++ */ ++ ++#ifndef __CR_PROP_LIST_H__ ++#define __CR_PROP_LIST_H__ ++ ++#include "cr-utils.h" ++#include "cr-declaration.h" ++#include "cr-string.h" ++ ++G_BEGIN_DECLS ++ ++typedef struct _CRPropList CRPropList ; ++typedef struct _CRPropListPriv CRPropListPriv ; ++ ++struct _CRPropList ++{ ++ CRPropListPriv * priv; ++} ; ++ ++CRPropList * cr_prop_list_append (CRPropList *a_this, ++ CRPropList *a_to_append) ; ++ ++CRPropList * cr_prop_list_append2 (CRPropList *a_this, ++ CRString *a_prop, ++ CRDeclaration *a_decl) ; ++ ++CRPropList * cr_prop_list_prepend (CRPropList *a_this, ++ CRPropList *a_to_append) ; ++ ++CRPropList * cr_prop_list_prepend2 (CRPropList *a_this, ++ CRString *a_prop, ++ CRDeclaration *a_decl) ; ++ ++enum CRStatus cr_prop_list_set_prop (CRPropList *a_this, ++ CRString *a_prop) ; ++ ++enum CRStatus cr_prop_list_get_prop (CRPropList const *a_this, ++ CRString **a_prop) ; ++ ++enum CRStatus cr_prop_list_lookup_prop (CRPropList *a_this, ++ CRString *a_prop, ++ CRPropList**a_pair) ; ++ ++CRPropList * cr_prop_list_get_next (CRPropList *a_this) ; ++ ++CRPropList * cr_prop_list_get_prev (CRPropList *a_this) ; ++ ++enum CRStatus cr_prop_list_set_decl (CRPropList *a_this, ++ CRDeclaration *a_decl); ++ ++enum CRStatus cr_prop_list_get_decl (CRPropList const *a_this, ++ CRDeclaration **a_decl) ; ++ ++CRPropList * cr_prop_list_unlink (CRPropList *a_this, ++ CRPropList *a_pair) ; ++ ++void cr_prop_list_destroy (CRPropList *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_PROP_LIST_H__*/ +diff --git a/src/st/croco/cr-pseudo.c b/src/st/croco/cr-pseudo.c +new file mode 100644 +index 000000000..cee3fc869 +--- /dev/null ++++ b/src/st/croco/cr-pseudo.c +@@ -0,0 +1,167 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#include "cr-pseudo.h" ++ ++/** ++ *@CRPseudo: ++ *The definition of the #CRPseudo class. ++ */ ++ ++/** ++ * cr_pseudo_new: ++ *Constructor of the #CRPseudo class. ++ * ++ *Returns the newly build instance. ++ */ ++CRPseudo * ++cr_pseudo_new (void) ++{ ++ CRPseudo *result = NULL; ++ ++ result = g_malloc0 (sizeof (CRPseudo)); ++ ++ return result; ++} ++ ++/** ++ * cr_pseudo_to_string: ++ * @a_this: the current instance of #CRPseud. ++ * ++ * Returns the serialized pseudo. Caller must free the returned ++ * string using g_free(). ++ */ ++guchar * ++cr_pseudo_to_string (CRPseudo const * a_this) ++{ ++ guchar *result = NULL; ++ GString *str_buf = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ str_buf = g_string_new (NULL); ++ ++ if (a_this->type == IDENT_PSEUDO) { ++ guchar *name = NULL; ++ ++ if (a_this->name == NULL) { ++ goto error; ++ } ++ ++ name = (guchar *) g_strndup (a_this->name->stryng->str, ++ a_this->name->stryng->len); ++ ++ if (name) { ++ g_string_append (str_buf, (const gchar *) name); ++ g_free (name); ++ name = NULL; ++ } ++ } else if (a_this->type == FUNCTION_PSEUDO) { ++ guchar *name = NULL, ++ *arg = NULL; ++ ++ if (a_this->name == NULL) ++ goto error; ++ ++ name = (guchar *) g_strndup (a_this->name->stryng->str, ++ a_this->name->stryng->len); ++ ++ if (a_this->extra) { ++ arg = (guchar *) g_strndup (a_this->extra->stryng->str, ++ a_this->extra->stryng->len); ++ } ++ ++ if (name) { ++ g_string_append_printf (str_buf, "%s(", name); ++ g_free (name); ++ name = NULL; ++ ++ if (arg) { ++ g_string_append (str_buf, (const gchar *) arg); ++ g_free (arg); ++ arg = NULL; ++ } ++ ++ g_string_append_c (str_buf, ')'); ++ } ++ } ++ ++ if (str_buf) { ++ result = (guchar *) str_buf->str; ++ g_string_free (str_buf, FALSE); ++ str_buf = NULL; ++ } ++ ++ return result; ++ ++ error: ++ g_string_free (str_buf, TRUE); ++ return NULL; ++} ++ ++/** ++ * cr_pseudo_dump: ++ *@a_this: the current instance of pseudo ++ *@a_fp: the destination file pointer. ++ * ++ *Dumps the pseudo to a file. ++ * ++ */ ++void ++cr_pseudo_dump (CRPseudo const * a_this, FILE * a_fp) ++{ ++ guchar *tmp_str = NULL; ++ ++ if (a_this) { ++ tmp_str = cr_pseudo_to_string (a_this); ++ if (tmp_str) { ++ fprintf (a_fp, "%s", tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++} ++ ++/** ++ * cr_pseudo_destroy: ++ *@a_this: the current instance to destroy. ++ * ++ *destructor of the #CRPseudo class. ++ */ ++void ++cr_pseudo_destroy (CRPseudo * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ if (a_this->name) { ++ cr_string_destroy (a_this->name); ++ a_this->name = NULL; ++ } ++ ++ if (a_this->extra) { ++ cr_string_destroy (a_this->extra); ++ a_this->extra = NULL; ++ } ++ ++ g_free (a_this); ++} +diff --git a/src/st/croco/cr-pseudo.h b/src/st/croco/cr-pseudo.h +new file mode 100644 +index 000000000..8917da45e +--- /dev/null ++++ b/src/st/croco/cr-pseudo.h +@@ -0,0 +1,64 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * See COPYRIGHTS file for copyright information ++ */ ++ ++#ifndef __CR_PSEUDO_H__ ++#define __CR_PSEUDO_H__ ++ ++#include ++#include ++#include "cr-attr-sel.h" ++#include "cr-parsing-location.h" ++ ++G_BEGIN_DECLS ++ ++enum CRPseudoType ++{ ++ IDENT_PSEUDO = 0, ++ FUNCTION_PSEUDO ++} ; ++ ++typedef struct _CRPseudo CRPseudo ; ++ ++/** ++ *The CRPseudo Class. ++ *Abstract a "pseudo" as defined by the css2 spec ++ *in appendix D.1 . ++ */ ++struct _CRPseudo ++{ ++ enum CRPseudoType type ; ++ CRString *name ; ++ CRString *extra ; ++ CRParsingLocation location ; ++} ; ++ ++CRPseudo * cr_pseudo_new (void) ; ++ ++guchar * cr_pseudo_to_string (CRPseudo const *a_this) ; ++ ++void cr_pseudo_dump (CRPseudo const *a_this, FILE *a_fp) ; ++ ++void cr_pseudo_destroy (CRPseudo *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_PSEUDO_H__*/ +diff --git a/src/st/croco/cr-rgb.c b/src/st/croco/cr-rgb.c +new file mode 100644 +index 000000000..1b8b66256 +--- /dev/null ++++ b/src/st/croco/cr-rgb.c +@@ -0,0 +1,687 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyrights information. ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include "cr-rgb.h" ++#include "cr-term.h" ++#include "cr-parser.h" ++ ++static const CRRgb gv_standard_colors[] = { ++ {(const guchar*)"aliceblue", 240, 248, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"antiquewhite", 250, 235, 215, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"aqua", 0, 255, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"aquamarine", 127, 255, 212, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"azure", 240, 255, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"beige", 245, 245, 220, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"bisque", 255, 228, 196, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"black", 0, 0, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"blanchedalmond", 255, 235, 205, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"blue", 0, 0, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"blueviolet", 138, 43, 226, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"brown", 165, 42, 42, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"burlywood", 222, 184, 135, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"cadetblue", 95, 158, 160, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"chartreuse", 127, 255, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"chocolate", 210, 105, 30, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"coral", 255, 127, 80, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"cornflowerblue", 100, 149, 237, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"cornsilk", 255, 248, 220, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"crimson", 220, 20, 60, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"cyan", 0, 255, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkblue", 0, 0, 139, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkcyan", 0, 139, 139, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkgoldenrod", 184, 134, 11, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkgray", 169, 169, 169, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkgreen", 0, 100, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkgrey", 169, 169, 169, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkkhaki", 189, 183, 107, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkmagenta", 139, 0, 139, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkolivegreen", 85, 107, 47, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkorange", 255, 140, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkorchid", 153, 50, 204, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkred", 139, 0, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darksalmon", 233, 150, 122, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkseagreen", 143, 188, 143, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkslateblue", 72, 61, 139, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkslategray", 47, 79, 79, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkslategrey", 47, 79, 79, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkturquoise", 0, 206, 209, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"darkviolet", 148, 0, 211, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"deeppink", 255, 20, 147, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"deepskyblue", 0, 191, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"dimgray", 105, 105, 105, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"dimgrey", 105, 105, 105, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"dodgerblue", 30, 144, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"firebrick", 178, 34, 34, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"floralwhite", 255, 250, 240, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"forestgreen", 34, 139, 34, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"fuchsia", 255, 0, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"gainsboro", 220, 220, 220, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"ghostwhite", 248, 248, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"gold", 255, 215, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"goldenrod", 218, 165, 32, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"gray", 128, 128, 128, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"green", 0, 128, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"greenyellow", 173, 255, 47, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"grey", 128, 128, 128, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"honeydew", 240, 255, 240, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"hotpink", 255, 105, 180, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"indianred", 205, 92, 92, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"indigo", 75, 0, 130, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"ivory", 255, 255, 240, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"khaki", 240, 230, 140, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lavender", 230, 230, 250, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lavenderblush", 255, 240, 245, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lawngreen", 124, 252, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lemonchiffon", 255, 250, 205, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightblue", 173, 216, 230, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightcoral", 240, 128, 128, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightcyan", 224, 255, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightgoldenrodyellow", 250, 250, 210, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightgray", 211, 211, 211, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightgreen", 144, 238, 144, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightgrey", 211, 211, 211, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightpink", 255, 182, 193, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightsalmon", 255, 160, 122, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightseagreen", 32, 178, 170, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightskyblue", 135, 206, 250, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightslategray", 119, 136, 153, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightslategrey", 119, 136, 153, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightsteelblue", 176, 196, 222, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lightyellow", 255, 255, 224, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"lime", 0, 255, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"limegreen", 50, 205, 50, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"linen", 250, 240, 230, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"magenta", 255, 0, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"maroon", 128, 0, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mediumaquamarine", 102, 205, 170, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mediumblue", 0, 0, 205, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mediumorchid", 186, 85, 211, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mediumpurple", 147, 112, 219, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mediumseagreen", 60, 179, 113, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mediumslateblue", 123, 104, 238, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mediumspringgreen", 0, 250, 154, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mediumturquoise", 72, 209, 204, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mediumvioletred", 199, 21, 133, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"midnightblue", 25, 25, 112, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mintcream", 245, 255, 250, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"mistyrose", 255, 228, 225, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"moccasin", 255, 228, 181, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"navajowhite", 255, 222, 173, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"navy", 0, 0, 128, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"oldlace", 253, 245, 230, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"olive", 128, 128, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"olivedrab", 107, 142, 35, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"orange", 255, 165, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"orangered", 255, 69, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"orchid", 218, 112, 214, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"palegoldenrod", 238, 232, 170, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"palegreen", 152, 251, 152, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"paleturquoise", 175, 238, 238, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"palevioletred", 219, 112, 147, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"papayawhip", 255, 239, 213, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"peachpuff", 255, 218, 185, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"peru", 205, 133, 63, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"pink", 255, 192, 203, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"plum", 221, 160, 221, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"powderblue", 176, 224, 230, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"purple", 128, 0, 128, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"red", 255, 0, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"rosybrown", 188, 143, 143, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"royalblue", 65, 105, 225, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"saddlebrown", 139, 69, 19, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"salmon", 250, 128, 114, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"sandybrown", 244, 164, 96, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"seagreen", 46, 139, 87, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"seashell", 255, 245, 238, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"sienna", 160, 82, 45, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"silver", 192, 192, 192, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"skyblue", 135, 206, 235, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"slateblue", 106, 90, 205, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"slategray", 112, 128, 144, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"slategrey", 112, 128, 144, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"snow", 255, 250, 250, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"springgreen", 0, 255, 127, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"steelblue", 70, 130, 180, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"tan", 210, 180, 140, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"teal", 0, 128, 128, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"thistle", 216, 191, 216, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"tomato", 255, 99, 71, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"transparent", 255, 255, 255, FALSE, FALSE, TRUE, {0,0,0}}, ++ {(const guchar*)"turquoise", 64, 224, 208, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"violet", 238, 130, 238, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"wheat", 245, 222, 179, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"white", 255, 255, 255, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"whitesmoke", 245, 245, 245, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"yellow", 255, 255, 0, FALSE, FALSE, FALSE, {0,0,0}}, ++ {(const guchar*)"yellowgreen", 154, 205, 50, FALSE, FALSE, FALSE, {0,0,0}} ++}; ++ ++/** ++ * cr_rgb_new: ++ * ++ *The default constructor of #CRRgb. ++ * ++ *Returns the newly built instance of #CRRgb ++ */ ++CRRgb * ++cr_rgb_new (void) ++{ ++ CRRgb *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRRgb)); ++ ++ if (result == NULL) { ++ cr_utils_trace_info ("No more memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRRgb)); ++ ++ return result; ++} ++ ++/** ++ * cr_rgb_new_with_vals: ++ *@a_red: the red component of the color. ++ *@a_green: the green component of the color. ++ *@a_blue: the blue component of the color. ++ *@a_unit: the unit of the rgb values. ++ *(either percentage or integer values) ++ * ++ *A constructor of #CRRgb. ++ * ++ *Returns the newly built instance of #CRRgb. ++ */ ++CRRgb * ++cr_rgb_new_with_vals (gulong a_red, gulong a_green, ++ gulong a_blue, gboolean a_is_percentage) ++{ ++ CRRgb *result = NULL; ++ ++ result = cr_rgb_new (); ++ ++ g_return_val_if_fail (result, NULL); ++ ++ result->red = a_red; ++ result->green = a_green; ++ result->blue = a_blue; ++ result->is_percentage = a_is_percentage; ++ ++ return result; ++} ++ ++/** ++ * cr_rgb_to_string: ++ *@a_this: the instance of #CRRgb to serialize. ++ * ++ *Serializes the rgb into a zero terminated string. ++ * ++ *Returns the zero terminated string containing the serialized ++ *rgb. MUST BE FREED by the caller using g_free(). ++ */ ++guchar * ++cr_rgb_to_string (CRRgb const * a_this) ++{ ++ guchar *result = NULL; ++ GString *str_buf = NULL; ++ ++ str_buf = g_string_new (NULL); ++ g_return_val_if_fail (str_buf, NULL); ++ ++ if (a_this->is_percentage == 1) { ++ g_string_append_printf (str_buf, "%ld", a_this->red); ++ ++ g_string_append (str_buf, "%, "); ++ ++ g_string_append_printf (str_buf, "%ld", a_this->green); ++ g_string_append (str_buf, "%, "); ++ ++ g_string_append_printf (str_buf, "%ld", a_this->blue); ++ g_string_append_c (str_buf, '%'); ++ } else { ++ g_string_append_printf (str_buf, "%ld", a_this->red); ++ g_string_append (str_buf, ", "); ++ ++ g_string_append_printf (str_buf, "%ld", a_this->green); ++ g_string_append (str_buf, ", "); ++ ++ g_string_append_printf (str_buf, "%ld", a_this->blue); ++ } ++ ++ if (str_buf) { ++ result = (guchar *) str_buf->str; ++ g_string_free (str_buf, FALSE); ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_rgb_dump: ++ *@a_this: the "this pointer" of ++ *the current instance of #CRRgb. ++ *@a_fp: the destination file pointer. ++ * ++ *Dumps the current instance of #CRRgb ++ *to a file. ++ */ ++void ++cr_rgb_dump (CRRgb const * a_this, FILE * a_fp) ++{ ++ guchar *str = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ str = cr_rgb_to_string (a_this); ++ ++ if (str) { ++ fprintf (a_fp, "%s", str); ++ g_free (str); ++ str = NULL; ++ } ++} ++ ++/** ++ * cr_rgb_compute_from_percentage: ++ *@a_this: the current instance of #CRRgb ++ * ++ *If the rgb values are expressed in percentage, ++ *compute their real value. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_rgb_compute_from_percentage (CRRgb * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ if (a_this->is_percentage == FALSE) ++ return CR_OK; ++ a_this->red = a_this->red * 255 / 100; ++ a_this->green = a_this->green * 255 / 100; ++ a_this->blue = a_this->blue * 255 / 100; ++ a_this->is_percentage = FALSE; ++ return CR_OK; ++} ++ ++/** ++ * cr_rgb_set: ++ *@a_this: the current instance of #CRRgb. ++ *@a_red: the red value. ++ *@a_green: the green value. ++ *@a_blue: the blue value. ++ * ++ *Sets rgb values to the RGB. ++ * ++ *Returns CR_OK upon successful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_rgb_set (CRRgb * a_this, gulong a_red, ++ gulong a_green, gulong a_blue, gboolean a_is_percentage) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ if (a_is_percentage != FALSE) { ++ g_return_val_if_fail (a_red <= 100 ++ && a_green <= 100 ++ && a_blue <= 100, CR_BAD_PARAM_ERROR); ++ } ++ ++ a_this->is_percentage = a_is_percentage; ++ ++ a_this->red = a_red; ++ a_this->green = a_green; ++ a_this->blue = a_blue; ++ a_this->inherit = FALSE ; ++ a_this->is_transparent = FALSE ; ++ return CR_OK; ++} ++ ++/** ++ * cr_rgb_set_to_inherit: ++ *@a_this: the current instance of #CRRgb ++ * ++ *sets the value of the rgb to inherit. ++ *Look at the css spec from chapter 6.1 to 6.2 to understand ++ *the meaning of "inherit". ++ * ++ * Returns CR_OK upon succesful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_rgb_set_to_inherit (CRRgb *a_this, gboolean a_inherit) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; ++ ++ a_this->inherit = a_inherit ; ++ ++ return CR_OK ; ++} ++ ++/** ++ * cr_rgb_is_set_to_inherit: ++ * ++ * @a_this: the current instance of #CRRgb. ++ * ++ * Returns TRUE if the rgb is set to the value "inherit", FALSE otherwise. ++ */ ++gboolean ++cr_rgb_is_set_to_inherit (CRRgb const *a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; ++ ++ return a_this->inherit ; ++} ++ ++/** ++ * cr_rgb_is_set_to_transparent: ++ *@a_this: the current instance of ++ *#CRRgb ++ * ++ *Tests if the the rgb is set to the ++ *value "transparent" or not. ++ * ++ *Returns TRUE if the rgb has been set to ++ *transparent, FALSE otherwise. ++ */ ++gboolean ++cr_rgb_is_set_to_transparent (CRRgb const *a_this) ++{ ++ g_return_val_if_fail (a_this, FALSE) ; ++ return a_this->is_transparent ; ++} ++ ++ ++/** ++ * cr_rgb_set_to_transparent: ++ *@a_this: the current instance of #CRRgb ++ *@a_is_transparent: set to transparent or not. ++ * ++ *Sets the rgb to the "transparent" value (or not) ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_rgb_set_to_transparent (CRRgb *a_this, ++ gboolean a_is_transparent) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ; ++ a_this->is_transparent = a_is_transparent ; ++ return CR_OK ; ++} ++ ++/** ++ * cr_rgb_set_from_rgb: ++ *@a_this: the current instance of #CRRgb. ++ *@a_rgb: the rgb to "copy" ++ * ++ *Sets the rgb from an other one. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_rgb_set_from_rgb (CRRgb * a_this, CRRgb const * a_rgb) ++{ ++ g_return_val_if_fail (a_this && a_rgb, CR_BAD_PARAM_ERROR); ++ ++ cr_rgb_copy (a_this, a_rgb) ; ++ ++ return CR_OK; ++} ++ ++static int ++cr_rgb_color_name_compare (const void *a, ++ const void *b) ++{ ++ const char *a_color_name = a; ++ const CRRgb *rgb = b; ++ ++ return g_ascii_strcasecmp (a_color_name, (const char *) rgb->name); ++} ++ ++/** ++ * cr_rgb_set_from_name: ++ * @a_this: the current instance of #CRRgb ++ * @a_color_name: the color name ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_rgb_set_from_name (CRRgb * a_this, const guchar * a_color_name) ++{ ++ enum CRStatus status = CR_OK; ++ CRRgb *result; ++ ++ g_return_val_if_fail (a_this && a_color_name, CR_BAD_PARAM_ERROR); ++ ++ result = bsearch (a_color_name, ++ gv_standard_colors, ++ G_N_ELEMENTS (gv_standard_colors), ++ sizeof (gv_standard_colors[0]), ++ cr_rgb_color_name_compare); ++ if (result != NULL) ++ cr_rgb_set_from_rgb (a_this, result); ++ else ++ status = CR_UNKNOWN_TYPE_ERROR; ++ ++ return status; ++} ++ ++/** ++ * cr_rgb_set_from_hex_str: ++ * @a_this: the current instance of #CRRgb ++ * @a_hex: the hexadecimal value to set. ++ * ++ * Returns CR_OK upon successful completion. ++ */ ++enum CRStatus ++cr_rgb_set_from_hex_str (CRRgb * a_this, const guchar * a_hex) ++{ ++ enum CRStatus status = CR_OK; ++ gulong i = 0; ++ guchar colors[3] = { 0 }; ++ ++ g_return_val_if_fail (a_this && a_hex, CR_BAD_PARAM_ERROR); ++ ++ if (strlen ((const char *) a_hex) == 3) { ++ for (i = 0; i < 3; i++) { ++ if (a_hex[i] >= '0' && a_hex[i] <= '9') { ++ colors[i] = a_hex[i] - '0'; ++ colors[i] = (colors[i] << 4) | colors[i]; ++ } else if (a_hex[i] >= 'a' && a_hex[i] <= 'z') { ++ colors[i] = 10 + a_hex[i] - 'a'; ++ colors[i] = (colors[i] << 4) | colors[i]; ++ } else if (a_hex[i] >= 'A' && a_hex[i] <= 'Z') { ++ colors[i] = 10 + a_hex[i] - 'A'; ++ colors[i] = (colors[i] << 4) | colors[i]; ++ } else { ++ status = CR_UNKNOWN_TYPE_ERROR; ++ } ++ } ++ } else if (strlen ((const char *) a_hex) == 6) { ++ for (i = 0; i < 6; i++) { ++ if (a_hex[i] >= '0' && a_hex[i] <= '9') { ++ colors[i / 2] <<= 4; ++ colors[i / 2] |= a_hex[i] - '0'; ++ status = CR_OK; ++ } else if (a_hex[i] >= 'a' && a_hex[i] <= 'z') { ++ colors[i / 2] <<= 4; ++ colors[i / 2] |= 10 + a_hex[i] - 'a'; ++ status = CR_OK; ++ } else if (a_hex[i] >= 'A' && a_hex[i] <= 'Z') { ++ colors[i / 2] <<= 4; ++ colors[i / 2] |= 10 + a_hex[i] - 'A'; ++ status = CR_OK; ++ } else { ++ status = CR_UNKNOWN_TYPE_ERROR; ++ } ++ } ++ } else { ++ status = CR_UNKNOWN_TYPE_ERROR; ++ } ++ ++ if (status == CR_OK) { ++ status = cr_rgb_set (a_this, colors[0], ++ colors[1], colors[2], FALSE); ++ cr_rgb_set_to_transparent (a_this, FALSE) ; ++ } ++ return status; ++} ++ ++/** ++ * cr_rgb_set_from_term: ++ *@a_this: the instance of #CRRgb to set ++ *@a_value: the terminal from which to set ++ * ++ *Set the rgb from a terminal symbol ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_rgb_set_from_term (CRRgb *a_this, const struct _CRTerm *a_value) ++{ ++ enum CRStatus status = CR_OK ; ++ g_return_val_if_fail (a_this && a_value, ++ CR_BAD_PARAM_ERROR) ; ++ ++ switch(a_value->type) { ++ case TERM_RGB: ++ if (a_value->content.rgb) { ++ cr_rgb_set_from_rgb ++ (a_this, a_value->content.rgb) ; ++ } ++ break ; ++ case TERM_IDENT: ++ if (a_value->content.str ++ && a_value->content.str->stryng ++ && a_value->content.str->stryng->str) { ++ if (!strncmp ("inherit", ++ a_value->content.str->stryng->str, ++ sizeof ("inherit")-1)) { ++ a_this->inherit = TRUE; ++ a_this->is_transparent = FALSE ; ++ } else { ++ status = cr_rgb_set_from_name ++ (a_this, ++ (const guchar *) a_value->content.str->stryng->str) ; ++ } ++ } else { ++ cr_utils_trace_info ++ ("a_value has NULL string value") ; ++ } ++ break ; ++ case TERM_HASH: ++ if (a_value->content.str ++ && a_value->content.str->stryng ++ && a_value->content.str->stryng->str) { ++ status = cr_rgb_set_from_hex_str ++ (a_this, ++ (const guchar *) a_value->content.str->stryng->str) ; ++ } else { ++ cr_utils_trace_info ++ ("a_value has NULL string value") ; ++ } ++ break ; ++ default: ++ status = CR_UNKNOWN_TYPE_ERROR ; ++ } ++ return status ; ++} ++ ++enum CRStatus ++cr_rgb_copy (CRRgb *a_dest, CRRgb const *a_src) ++{ ++ g_return_val_if_fail (a_dest && a_src, ++ CR_BAD_PARAM_ERROR) ; ++ ++ memcpy (a_dest, a_src, sizeof (CRRgb)) ; ++ return CR_OK ; ++} ++ ++/** ++ * cr_rgb_destroy: ++ *@a_this: the "this pointer" of the ++ *current instance of #CRRgb. ++ * ++ *Destructor of #CRRgb. ++ */ ++void ++cr_rgb_destroy (CRRgb * a_this) ++{ ++ g_return_if_fail (a_this); ++ g_free (a_this); ++} ++ ++/** ++ * cr_rgb_parse_from_buf: ++ *@a_str: a string that contains a color description ++ *@a_enc: the encoding of a_str ++ * ++ *Parses a text buffer that contains a rgb color ++ * ++ *Returns the parsed color, or NULL in case of error ++ */ ++CRRgb * ++cr_rgb_parse_from_buf (const guchar *a_str, ++ enum CREncoding a_enc) ++{ ++ enum CRStatus status = CR_OK ; ++ CRTerm *value = NULL ; ++ CRParser * parser = NULL; ++ CRRgb *result = NULL; ++ ++ g_return_val_if_fail (a_str, NULL); ++ ++ parser = cr_parser_new_from_buf ((guchar *) a_str, strlen ((const char *) a_str), a_enc, FALSE); ++ ++ g_return_val_if_fail (parser, NULL); ++ ++ status = cr_parser_try_to_skip_spaces_and_comments (parser) ; ++ if (status != CR_OK) ++ goto cleanup; ++ ++ status = cr_parser_parse_term (parser, &value); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ result = cr_rgb_new (); ++ if (!result) ++ goto cleanup; ++ ++ status = cr_rgb_set_from_term (result, value); ++ ++cleanup: ++ if (parser) { ++ cr_parser_destroy (parser); ++ parser = NULL; ++ } ++ if (value) { ++ cr_term_destroy(value); ++ value = NULL; ++ } ++ return result ; ++} ++ +diff --git a/src/st/croco/cr-rgb.h b/src/st/croco/cr-rgb.h +new file mode 100644 +index 000000000..a127a440e +--- /dev/null ++++ b/src/st/croco/cr-rgb.h +@@ -0,0 +1,94 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * see COPYRIGHTS file for copyright information. ++ */ ++ ++#ifndef __CR_RGB_H__ ++#define __CR_RGB_H__ ++ ++#include ++#include ++#include "cr-utils.h" ++#include "cr-parsing-location.h" ++ ++G_BEGIN_DECLS ++ ++ ++typedef struct _CRRgb CRRgb ; ++struct _CRRgb ++{ ++ /* ++ *the unit of the rgb. ++ *Either NO_UNIT (integer) or ++ *UNIT_PERCENTAGE (percentage). ++ */ ++ const guchar *name ; ++ glong red ; ++ glong green ; ++ glong blue ; ++ gboolean is_percentage ; ++ gboolean inherit ; ++ gboolean is_transparent ; ++ CRParsingLocation location ; ++} ; ++ ++CRRgb * cr_rgb_new (void) ; ++ ++CRRgb * cr_rgb_new_with_vals (gulong a_red, gulong a_green, ++ gulong a_blue, gboolean a_is_percentage) ; ++ ++CRRgb *cr_rgb_parse_from_buf(const guchar *a_str, ++ enum CREncoding a_enc); ++ ++enum CRStatus cr_rgb_compute_from_percentage (CRRgb *a_this) ; ++ ++enum CRStatus cr_rgb_set (CRRgb *a_this, gulong a_red, ++ gulong a_green, gulong a_blue, ++ gboolean a_is_percentage) ; ++ ++enum CRStatus cr_rgb_copy (CRRgb *a_dest, CRRgb const *a_src) ; ++ ++enum CRStatus cr_rgb_set_to_inherit (CRRgb *a_this, gboolean a_inherit) ; ++ ++gboolean cr_rgb_is_set_to_inherit (CRRgb const *a_this) ; ++ ++gboolean cr_rgb_is_set_to_transparent (CRRgb const *a_this) ; ++ ++enum CRStatus cr_rgb_set_to_transparent (CRRgb *a_this, ++ gboolean a_is_transparent) ; ++enum CRStatus cr_rgb_set_from_rgb (CRRgb *a_this, CRRgb const *a_rgb) ; ++ ++enum CRStatus cr_rgb_set_from_name (CRRgb *a_this, const guchar *a_color_name) ; ++ ++enum CRStatus cr_rgb_set_from_hex_str (CRRgb *a_this, const guchar * a_hex_value) ; ++ ++struct _CRTerm; ++ ++enum CRStatus cr_rgb_set_from_term (CRRgb *a_this, const struct _CRTerm *a_value); ++ ++guchar * cr_rgb_to_string (CRRgb const *a_this) ; ++ ++void cr_rgb_dump (CRRgb const *a_this, FILE *a_fp) ; ++ ++void cr_rgb_destroy (CRRgb *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_RGB_H__*/ +diff --git a/src/st/croco/cr-selector.c b/src/st/croco/cr-selector.c +new file mode 100644 +index 000000000..8902e1c0f +--- /dev/null ++++ b/src/st/croco/cr-selector.c +@@ -0,0 +1,306 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#include ++#include "cr-selector.h" ++#include "cr-parser.h" ++ ++/** ++ * cr_selector_new: ++ * ++ *@a_simple_sel: the initial simple selector list ++ *of the current instance of #CRSelector. ++ * ++ *Creates a new instance of #CRSelector. ++ * ++ *Returns the newly built instance of #CRSelector, or ++ *NULL in case of failure. ++ */ ++CRSelector * ++cr_selector_new (CRSimpleSel * a_simple_sel) ++{ ++ CRSelector *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRSelector)); ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (CRSelector)); ++ result->simple_sel = a_simple_sel; ++ return result; ++} ++ ++CRSelector * ++cr_selector_parse_from_buf (const guchar * a_char_buf, enum CREncoding a_enc) ++{ ++ CRParser *parser = NULL; ++ ++ g_return_val_if_fail (a_char_buf, NULL); ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_char_buf, strlen ((const char *) a_char_buf), ++ a_enc, FALSE); ++ g_return_val_if_fail (parser, NULL); ++ ++ return NULL; ++} ++ ++/** ++ * cr_selector_append: ++ * ++ *@a_this: the current instance of #CRSelector. ++ *@a_new: the instance of #CRSelector to be appended. ++ * ++ *Appends a new instance of #CRSelector to the current selector list. ++ * ++ *Returns the new list. ++ */ ++CRSelector * ++cr_selector_append (CRSelector * a_this, CRSelector * a_new) ++{ ++ CRSelector *cur = NULL; ++ ++ if (!a_this) { ++ return a_new; ++ } ++ ++ /*walk forward the list headed by a_this to get the list tail */ ++ for (cur = a_this; cur && cur->next; cur = cur->next) ; ++ ++ cur->next = a_new; ++ a_new->prev = cur; ++ ++ return a_this; ++} ++ ++/** ++ * cr_selector_prepend: ++ * ++ *@a_this: the current instance of #CRSelector list. ++ *@a_new: the instance of #CRSelector. ++ * ++ *Prepends an element to the #CRSelector list. ++ * ++ *Returns the new list. ++ */ ++CRSelector * ++cr_selector_prepend (CRSelector * a_this, CRSelector * a_new) ++{ ++ CRSelector *cur = NULL; ++ ++ a_new->next = a_this; ++ a_this->prev = a_new; ++ ++ for (cur = a_new; cur && cur->prev; cur = cur->prev) ; ++ ++ return cur; ++} ++ ++/** ++ * cr_selector_append_simple_sel: ++ * ++ *@a_this: the current instance of #CRSelector. ++ *@a_simple_sel: the simple selector to append. ++ * ++ *append a simple selector to the current #CRSelector list. ++ * ++ *Returns the new list or NULL in case of failure. ++ */ ++CRSelector * ++cr_selector_append_simple_sel (CRSelector * a_this, ++ CRSimpleSel * a_simple_sel) ++{ ++ CRSelector *selector = NULL; ++ ++ selector = cr_selector_new (a_simple_sel); ++ g_return_val_if_fail (selector, NULL); ++ ++ return cr_selector_append (a_this, selector); ++} ++ ++guchar * ++cr_selector_to_string (CRSelector const * a_this) ++{ ++ guchar *result = NULL; ++ GString *str_buf = NULL; ++ ++ str_buf = g_string_new (NULL); ++ g_return_val_if_fail (str_buf, NULL); ++ ++ if (a_this) { ++ CRSelector const *cur = NULL; ++ ++ for (cur = a_this; cur; cur = cur->next) { ++ if (cur->simple_sel) { ++ guchar *tmp_str = NULL; ++ ++ tmp_str = cr_simple_sel_to_string ++ (cur->simple_sel); ++ ++ if (tmp_str) { ++ if (cur->prev) ++ g_string_append (str_buf, ++ ", "); ++ ++ g_string_append (str_buf, (const gchar *) tmp_str); ++ ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ } ++ } ++ ++ if (str_buf) { ++ result = (guchar *) str_buf->str; ++ g_string_free (str_buf, FALSE); ++ str_buf = NULL; ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_selector_dump: ++ * ++ *@a_this: the current instance of #CRSelector. ++ *@a_fp: the destination file. ++ * ++ *Serializes the current instance of #CRSelector to a file. ++ */ ++void ++cr_selector_dump (CRSelector const * a_this, FILE * a_fp) ++{ ++ guchar *tmp_buf = NULL; ++ ++ if (a_this) { ++ tmp_buf = cr_selector_to_string (a_this); ++ if (tmp_buf) { ++ fprintf (a_fp, "%s", tmp_buf); ++ g_free (tmp_buf); ++ tmp_buf = NULL; ++ } ++ } ++} ++ ++/** ++ * cr_selector_ref: ++ * ++ *@a_this: the current instance of #CRSelector. ++ * ++ *Increments the ref count of the current instance ++ *of #CRSelector. ++ */ ++void ++cr_selector_ref (CRSelector * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ a_this->ref_count++; ++} ++ ++/** ++ * cr_selector_unref: ++ * ++ *@a_this: the current instance of #CRSelector. ++ * ++ *Decrements the ref count of the current instance of ++ *#CRSelector. ++ *If the ref count reaches zero, the current instance of ++ *#CRSelector is destroyed. ++ * ++ *Returns TRUE if this function destroyed the current instance ++ *of #CRSelector, FALSE otherwise. ++ */ ++gboolean ++cr_selector_unref (CRSelector * a_this) ++{ ++ g_return_val_if_fail (a_this, FALSE); ++ ++ if (a_this->ref_count) { ++ a_this->ref_count--; ++ } ++ ++ if (a_this->ref_count == 0) { ++ cr_selector_destroy (a_this); ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++/** ++ * cr_selector_destroy: ++ * ++ *@a_this: the current instance of #CRSelector. ++ * ++ *Destroys the selector list. ++ */ ++void ++cr_selector_destroy (CRSelector * a_this) ++{ ++ CRSelector *cur = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ /* ++ *go and get the list tail. In the same time, free ++ *all the simple selectors contained in the list. ++ */ ++ for (cur = a_this; cur && cur->next; cur = cur->next) { ++ if (cur->simple_sel) { ++ cr_simple_sel_destroy (cur->simple_sel); ++ cur->simple_sel = NULL; ++ } ++ } ++ ++ if (cur) { ++ if (cur->simple_sel) { ++ cr_simple_sel_destroy (cur->simple_sel); ++ cur->simple_sel = NULL; ++ } ++ } ++ ++ /*in case the list has only one element */ ++ if (cur && !cur->prev) { ++ g_free (cur); ++ return; ++ } ++ ++ /*walk backward the list and free each "next element" */ ++ for (cur = cur->prev; cur && cur->prev; cur = cur->prev) { ++ if (cur->next) { ++ g_free (cur->next); ++ cur->next = NULL; ++ } ++ } ++ ++ if (!cur) ++ return; ++ ++ if (cur->next) { ++ g_free (cur->next); ++ cur->next = NULL; ++ } ++ ++ g_free (cur); ++} +diff --git a/src/st/croco/cr-selector.h b/src/st/croco/cr-selector.h +new file mode 100644 +index 000000000..dd6a7f786 +--- /dev/null ++++ b/src/st/croco/cr-selector.h +@@ -0,0 +1,95 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#ifndef __CR_SELECTOR_H__ ++#define __CR_SELECTOR_H__ ++ ++#include ++#include "cr-utils.h" ++#include "cr-simple-sel.h" ++#include "cr-parsing-location.h" ++ ++/** ++ *@file ++ *The declaration file of the #CRSelector file. ++ */ ++ ++G_BEGIN_DECLS ++ ++typedef struct _CRSelector CRSelector ; ++ ++/** ++ *Abstracts a CSS2 selector as defined in the right part ++ *of the 'ruleset" production in the appendix D.1 of the ++ *css2 spec. ++ *It is actually the abstraction of a comma separated list ++ *of simple selectors list. ++ *In a css2 file, a selector is a list of simple selectors ++ *separated by a comma. ++ *e.g: sel0, sel1, sel2 ... ++ *Each seln is a simple selector ++ */ ++struct _CRSelector ++{ ++ /** ++ *A Selection expression. ++ *It is a list of basic selectors. ++ *Each basic selector can be either an element ++ *selector, an id selector, a class selector, an ++ *attribute selector, an universal selector etc ... ++ */ ++ CRSimpleSel *simple_sel ; ++ ++ /**The next selector list element*/ ++ CRSelector *next ; ++ CRSelector *prev ; ++ CRParsingLocation location ; ++ glong ref_count ; ++}; ++ ++CRSelector* cr_selector_new (CRSimpleSel *a_sel_expr) ; ++ ++CRSelector * cr_selector_parse_from_buf (const guchar * a_char_buf, ++ enum CREncoding a_enc) ; ++ ++CRSelector* cr_selector_append (CRSelector *a_this, CRSelector *a_new) ; ++ ++CRSelector* cr_selector_append_simple_sel (CRSelector *a_this, ++ CRSimpleSel *a_simple_sel) ; ++ ++CRSelector* cr_selector_prepend (CRSelector *a_this, CRSelector *a_new) ; ++ ++guchar * cr_selector_to_string (CRSelector const *a_this) ; ++ ++void cr_selector_dump (CRSelector const *a_this, FILE *a_fp) ; ++ ++void cr_selector_ref (CRSelector *a_this) ; ++ ++gboolean cr_selector_unref (CRSelector *a_this) ; ++ ++void cr_selector_destroy (CRSelector *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_SELECTOR_H__*/ +diff --git a/src/st/croco/cr-simple-sel.c b/src/st/croco/cr-simple-sel.c +new file mode 100644 +index 000000000..ac906858d +--- /dev/null ++++ b/src/st/croco/cr-simple-sel.c +@@ -0,0 +1,325 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#include ++#include ++#include "cr-simple-sel.h" ++ ++/** ++ * cr_simple_sel_new: ++ * ++ *The constructor of #CRSimpleSel. ++ * ++ *Returns the new instance of #CRSimpleSel. ++ */ ++CRSimpleSel * ++cr_simple_sel_new (void) ++{ ++ CRSimpleSel *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRSimpleSel)); ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (CRSimpleSel)); ++ ++ return result; ++} ++ ++/** ++ * cr_simple_sel_append_simple_sel: ++ * ++ *Appends a simpe selector to the current list of simple selector. ++ * ++ *@a_this: the this pointer of the current instance of #CRSimpleSel. ++ *@a_sel: the simple selector to append. ++ * ++ *Returns: the new list upon successfull completion, an error code otherwise. ++ */ ++CRSimpleSel * ++cr_simple_sel_append_simple_sel (CRSimpleSel * a_this, CRSimpleSel * a_sel) ++{ ++ CRSimpleSel *cur = NULL; ++ ++ g_return_val_if_fail (a_sel, NULL); ++ ++ if (a_this == NULL) ++ return a_sel; ++ ++ for (cur = a_this; cur->next; cur = cur->next) ; ++ ++ cur->next = a_sel; ++ a_sel->prev = cur; ++ ++ return a_this; ++} ++ ++/** ++ * cr_simple_sel_prepend_simple_sel: ++ * ++ *@a_this: the this pointer of the current instance of #CRSimpleSel. ++ *@a_sel: the simple selector to prepend. ++ * ++ *Prepends a simple selector to the current list of simple selectors. ++ * ++ *Returns the new list upon successfull completion, an error code otherwise. ++ */ ++CRSimpleSel * ++cr_simple_sel_prepend_simple_sel (CRSimpleSel * a_this, CRSimpleSel * a_sel) ++{ ++ g_return_val_if_fail (a_sel, NULL); ++ ++ if (a_this == NULL) ++ return a_sel; ++ ++ a_sel->next = a_this; ++ a_this->prev = a_sel; ++ ++ return a_sel; ++} ++ ++guchar * ++cr_simple_sel_to_string (CRSimpleSel const * a_this) ++{ ++ GString *str_buf = NULL; ++ guchar *result = NULL; ++ ++ CRSimpleSel const *cur = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ str_buf = g_string_new (NULL); ++ for (cur = a_this; cur; cur = cur->next) { ++ if (cur->name) { ++ guchar *str = (guchar *) g_strndup (cur->name->stryng->str, ++ cur->name->stryng->len); ++ ++ if (str) { ++ switch (cur->combinator) { ++ case COMB_WS: ++ g_string_append (str_buf, " "); ++ break; ++ ++ case COMB_PLUS: ++ g_string_append (str_buf, "+"); ++ break; ++ ++ case COMB_GT: ++ g_string_append (str_buf, ">"); ++ break; ++ ++ default: ++ break; ++ } ++ ++ g_string_append (str_buf, (const gchar *) str); ++ g_free (str); ++ str = NULL; ++ } ++ } ++ ++ if (cur->add_sel) { ++ guchar *tmp_str = NULL; ++ ++ tmp_str = cr_additional_sel_to_string (cur->add_sel); ++ if (tmp_str) { ++ g_string_append (str_buf, (const gchar *) tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ } ++ ++ if (str_buf) { ++ result = (guchar *) str_buf->str; ++ g_string_free (str_buf, FALSE); ++ str_buf = NULL; ++ } ++ ++ return result; ++} ++ ++ ++guchar * ++cr_simple_sel_one_to_string (CRSimpleSel const * a_this) ++{ ++ GString *str_buf = NULL; ++ guchar *result = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ str_buf = g_string_new (NULL); ++ if (a_this->name) { ++ guchar *str = (guchar *) g_strndup (a_this->name->stryng->str, ++ a_this->name->stryng->len); ++ ++ if (str) { ++ g_string_append_printf (str_buf, "%s", str); ++ g_free (str); ++ str = NULL; ++ } ++ } ++ ++ if (a_this->add_sel) { ++ guchar *tmp_str = NULL; ++ ++ tmp_str = cr_additional_sel_to_string (a_this->add_sel); ++ if (tmp_str) { ++ g_string_append_printf ++ (str_buf, "%s", tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ ++ if (str_buf) { ++ result = (guchar *) str_buf->str; ++ g_string_free (str_buf, FALSE); ++ str_buf = NULL; ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_simple_sel_dump: ++ *@a_this: the current instance of #CRSimpleSel. ++ *@a_fp: the destination file pointer. ++ * ++ *Dumps the selector to a file. ++ *TODO: add the support of unicode in the dump. ++ * ++ *Returns CR_OK upon successfull completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_simple_sel_dump (CRSimpleSel const * a_this, FILE * a_fp) ++{ ++ guchar *tmp_str = NULL; ++ ++ g_return_val_if_fail (a_fp, CR_BAD_PARAM_ERROR); ++ ++ if (a_this) { ++ tmp_str = cr_simple_sel_to_string (a_this); ++ if (tmp_str) { ++ fprintf (a_fp, "%s", tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_simple_sel_compute_specificity: ++ * ++ *@a_this: the current instance of #CRSimpleSel ++ * ++ *Computes the selector (combinator separated list of simple selectors) ++ *as defined in the css2 spec in chapter 6.4.3 ++ * ++ *Returns CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_simple_sel_compute_specificity (CRSimpleSel * a_this) ++{ ++ CRAdditionalSel const *cur_add_sel = NULL; ++ CRSimpleSel const *cur_sel = NULL; ++ gulong a = 0, ++ b = 0, ++ c = 0; ++ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ for (cur_sel = a_this; cur_sel; cur_sel = cur_sel->next) { ++ if (cur_sel->type_mask & TYPE_SELECTOR) { ++ c++; /*hmmh, is this a new language ? */ ++ } else if (!cur_sel->name ++ || !cur_sel->name->stryng ++ || !cur_sel->name->stryng->str) { ++ if (cur_sel->add_sel->type == ++ PSEUDO_CLASS_ADD_SELECTOR) { ++ /* ++ *this is a pseudo element, and ++ *the spec says, "ignore pseudo elements". ++ */ ++ continue; ++ } ++ } ++ ++ for (cur_add_sel = cur_sel->add_sel; ++ cur_add_sel; cur_add_sel = cur_add_sel->next) { ++ switch (cur_add_sel->type) { ++ case ID_ADD_SELECTOR: ++ a++; ++ break; ++ ++ case NO_ADD_SELECTOR: ++ continue; ++ ++ default: ++ b++; ++ break; ++ } ++ } ++ } ++ ++ /*we suppose a, b and c have 1 to 3 digits */ ++ a_this->specificity = a * 1000000 + b * 1000 + c; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_simple_sel_destroy: ++ * ++ *@a_this: the this pointer of the current instance of #CRSimpleSel. ++ * ++ *The destructor of the current instance of ++ *#CRSimpleSel. ++ */ ++void ++cr_simple_sel_destroy (CRSimpleSel * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ if (a_this->name) { ++ cr_string_destroy (a_this->name); ++ a_this->name = NULL; ++ } ++ ++ if (a_this->add_sel) { ++ cr_additional_sel_destroy (a_this->add_sel); ++ a_this->add_sel = NULL; ++ } ++ ++ if (a_this->next) { ++ cr_simple_sel_destroy (a_this->next); ++ } ++ ++ if (a_this) { ++ g_free (a_this); ++ } ++} +diff --git a/src/st/croco/cr-simple-sel.h b/src/st/croco/cr-simple-sel.h +new file mode 100644 +index 000000000..d8edc0025 +--- /dev/null ++++ b/src/st/croco/cr-simple-sel.h +@@ -0,0 +1,130 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++ ++#ifndef __CR_SEL_H__ ++#define __CR_SEL_H__ ++ ++#include ++#include ++#include "cr-additional-sel.h" ++#include "cr-parsing-location.h" ++ ++G_BEGIN_DECLS ++ ++/** ++ *@file ++ *the declaration of the #CRSimpleSel class. ++ * ++ */ ++enum Combinator ++{ ++ NO_COMBINATOR, ++ COMB_WS,/*whitespace: descendent*/ ++ COMB_PLUS,/*'+': preceded by*/ ++ COMB_GT/*greater than ('>'): child*/ ++} ; ++ ++enum SimpleSelectorType ++{ ++ NO_SELECTOR_TYPE = 0, ++ UNIVERSAL_SELECTOR = 1, ++ TYPE_SELECTOR = 1 << 1 ++} ; ++ ++typedef struct _CRSimpleSel CRSimpleSel ; ++ ++/** ++ *The abstraction of a css2 simple selection list ++ *as defined by the right part of the "selector" production in the ++ *appendix D.1 of the css2 spec. ++ *It is basically a list of simple selector, each ++ *simple selector being separated by a combinator. ++ * ++ *In the libcroco's implementation, each simple selector ++ *is made of at most two parts: ++ * ++ *1/An element name or 'type selector' (which can hold a '*' and ++ *then been called 'universal selector') ++ * ++ *2/An additional selector that "specializes" the preceding type or ++ *universal selector. The additionnal selector can be either ++ *an id selector, or a class selector, or an attribute selector. ++ */ ++struct _CRSimpleSel ++{ ++ enum SimpleSelectorType type_mask ; ++ gboolean is_case_sentive ; ++ CRString * name ; ++ /** ++ *The combinator that separates ++ *this simple selector from the previous ++ *one. ++ */ ++ enum Combinator combinator ; ++ ++ /** ++ *The additional selector list of the ++ *current simple selector. ++ *An additional selector may ++ *be a class selector, an id selector, ++ *or an attribute selector. ++ *Note that this field is a linked list. ++ */ ++ CRAdditionalSel *add_sel ; ++ ++ /* ++ *the specificity as specified by ++ *chapter 6.4.3 of the spec. ++ */ ++ gulong specificity ; ++ ++ CRSimpleSel *next ; ++ CRSimpleSel *prev ; ++ CRParsingLocation location ; ++} ; ++ ++CRSimpleSel * cr_simple_sel_new (void) ; ++ ++CRSimpleSel * cr_simple_sel_append_simple_sel (CRSimpleSel *a_this, ++ CRSimpleSel *a_sel) ; ++ ++CRSimpleSel * cr_simple_sel_prepend_simple_sel (CRSimpleSel *a_this, ++ CRSimpleSel *a_sel) ; ++ ++guchar * cr_simple_sel_to_string (CRSimpleSel const *a_this) ; ++ ++guchar * cr_simple_sel_one_to_string (CRSimpleSel const * a_this) ; ++ ++enum CRStatus cr_simple_sel_dump (CRSimpleSel const *a_this, FILE *a_fp) ; ++ ++enum CRStatus cr_simple_sel_dump_attr_sel_list (CRSimpleSel const *a_this) ; ++ ++enum CRStatus cr_simple_sel_compute_specificity (CRSimpleSel *a_this) ; ++ ++void cr_simple_sel_destroy (CRSimpleSel *a_this) ; ++ ++G_END_DECLS ++ ++ ++#endif /*__CR_SIMPLE_SEL_H__*/ +diff --git a/src/st/croco/cr-statement.c b/src/st/croco/cr-statement.c +new file mode 100644 +index 000000000..241fa5f3b +--- /dev/null ++++ b/src/st/croco/cr-statement.c +@@ -0,0 +1,2794 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli. ++ * See COPYRIGHTS files for copyrights information. ++ */ ++ ++#include ++#include "cr-statement.h" ++#include "cr-parser.h" ++ ++/** ++ *@file ++ *Definition of the #CRStatement class. ++ */ ++ ++#define DECLARATION_INDENT_NB 2 ++ ++static void cr_statement_clear (CRStatement * a_this); ++ ++static void ++parse_font_face_start_font_face_cb (CRDocHandler * a_this, ++ CRParsingLocation *a_location) ++{ ++ CRStatement *stmt = NULL; ++ enum CRStatus status = CR_OK; ++ ++ stmt = cr_statement_new_at_font_face_rule (NULL, NULL); ++ g_return_if_fail (stmt); ++ ++ status = cr_doc_handler_set_ctxt (a_this, stmt); ++ g_return_if_fail (status == CR_OK); ++} ++ ++static void ++parse_font_face_unrecoverable_error_cb (CRDocHandler * a_this) ++{ ++ CRStatement *stmt = NULL; ++ CRStatement **stmtptr = NULL; ++ enum CRStatus status = CR_OK; ++ ++ g_return_if_fail (a_this); ++ ++ stmtptr = &stmt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr); ++ if (status != CR_OK) { ++ cr_utils_trace_info ("Couldn't get parsing context. " ++ "This may lead to some memory leaks."); ++ return; ++ } ++ if (stmt) { ++ cr_statement_destroy (stmt); ++ cr_doc_handler_set_ctxt (a_this, NULL); ++ return; ++ } ++} ++ ++static void ++parse_font_face_property_cb (CRDocHandler * a_this, ++ CRString * a_name, ++ CRTerm * a_value, gboolean a_important) ++{ ++ enum CRStatus status = CR_OK; ++ CRString *name = NULL; ++ CRDeclaration *decl = NULL; ++ CRStatement *stmt = NULL; ++ CRStatement **stmtptr = NULL; ++ ++ g_return_if_fail (a_this && a_name); ++ ++ stmtptr = &stmt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr); ++ g_return_if_fail (status == CR_OK && stmt); ++ g_return_if_fail (stmt->type == AT_FONT_FACE_RULE_STMT); ++ ++ name = cr_string_dup (a_name) ; ++ g_return_if_fail (name); ++ decl = cr_declaration_new (stmt, name, a_value); ++ if (!decl) { ++ cr_utils_trace_info ("cr_declaration_new () failed."); ++ goto error; ++ } ++ name = NULL; ++ ++ stmt->kind.font_face_rule->decl_list = ++ cr_declaration_append (stmt->kind.font_face_rule->decl_list, ++ decl); ++ if (!stmt->kind.font_face_rule->decl_list) ++ goto error; ++ decl = NULL; ++ ++ error: ++ if (decl) { ++ cr_declaration_unref (decl); ++ decl = NULL; ++ } ++ if (name) { ++ cr_string_destroy (name); ++ name = NULL; ++ } ++} ++ ++static void ++parse_font_face_end_font_face_cb (CRDocHandler * a_this) ++{ ++ CRStatement *result = NULL; ++ CRStatement **resultptr = NULL; ++ enum CRStatus status = CR_OK; ++ ++ g_return_if_fail (a_this); ++ ++ resultptr = &result; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) resultptr); ++ g_return_if_fail (status == CR_OK && result); ++ g_return_if_fail (result->type == AT_FONT_FACE_RULE_STMT); ++ ++ status = cr_doc_handler_set_result (a_this, result); ++ g_return_if_fail (status == CR_OK); ++} ++ ++static void ++parse_page_start_page_cb (CRDocHandler * a_this, ++ CRString * a_name, ++ CRString * a_pseudo_page, ++ CRParsingLocation *a_location) ++{ ++ CRStatement *stmt = NULL; ++ enum CRStatus status = CR_OK; ++ CRString *page_name = NULL, *pseudo_name = NULL ; ++ ++ if (a_name) ++ page_name = cr_string_dup (a_name) ; ++ if (a_pseudo_page) ++ pseudo_name = cr_string_dup (a_pseudo_page) ; ++ ++ stmt = cr_statement_new_at_page_rule (NULL, NULL, ++ page_name, ++ pseudo_name); ++ page_name = NULL ; ++ pseudo_name = NULL ; ++ g_return_if_fail (stmt); ++ status = cr_doc_handler_set_ctxt (a_this, stmt); ++ g_return_if_fail (status == CR_OK); ++} ++ ++static void ++parse_page_unrecoverable_error_cb (CRDocHandler * a_this) ++{ ++ CRStatement *stmt = NULL; ++ CRStatement **stmtptr = NULL; ++ enum CRStatus status = CR_OK; ++ ++ g_return_if_fail (a_this); ++ ++ stmtptr = &stmt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr); ++ if (status != CR_OK) { ++ cr_utils_trace_info ("Couldn't get parsing context. " ++ "This may lead to some memory leaks."); ++ return; ++ } ++ if (stmt) { ++ cr_statement_destroy (stmt); ++ stmt = NULL; ++ cr_doc_handler_set_ctxt (a_this, NULL); ++ } ++} ++ ++static void ++parse_page_property_cb (CRDocHandler * a_this, ++ CRString * a_name, ++ CRTerm * a_expression, gboolean a_important) ++{ ++ CRString *name = NULL; ++ CRStatement *stmt = NULL; ++ CRStatement **stmtptr = NULL; ++ CRDeclaration *decl = NULL; ++ enum CRStatus status = CR_OK; ++ ++ stmtptr = &stmt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr); ++ g_return_if_fail (status == CR_OK && stmt->type == AT_PAGE_RULE_STMT); ++ ++ name = cr_string_dup (a_name); ++ g_return_if_fail (name); ++ ++ decl = cr_declaration_new (stmt, name, a_expression); ++ g_return_if_fail (decl); ++ decl->important = a_important; ++ stmt->kind.page_rule->decl_list = ++ cr_declaration_append (stmt->kind.page_rule->decl_list, decl); ++ g_return_if_fail (stmt->kind.page_rule->decl_list); ++} ++ ++static void ++parse_page_end_page_cb (CRDocHandler * a_this, ++ CRString * a_name, ++ CRString * a_pseudo_page) ++{ ++ enum CRStatus status = CR_OK; ++ CRStatement *stmt = NULL; ++ CRStatement **stmtptr = NULL; ++ ++ stmtptr = &stmt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr); ++ g_return_if_fail (status == CR_OK && stmt); ++ g_return_if_fail (stmt->type == AT_PAGE_RULE_STMT); ++ ++ status = cr_doc_handler_set_result (a_this, stmt); ++ g_return_if_fail (status == CR_OK); ++} ++ ++static void ++parse_at_media_start_media_cb (CRDocHandler * a_this, ++ GList * a_media_list, ++ CRParsingLocation *a_location) ++{ ++ enum CRStatus status = CR_OK; ++ CRStatement *at_media = NULL; ++ GList *media_list = NULL; ++ ++ g_return_if_fail (a_this && a_this->priv); ++ ++ if (a_media_list) { ++ /*duplicate media list */ ++ media_list = cr_utils_dup_glist_of_cr_string ++ (a_media_list); ++ } ++ ++ g_return_if_fail (media_list); ++ ++ /*make sure cr_statement_new_at_media_rule works in this case. */ ++ at_media = cr_statement_new_at_media_rule (NULL, NULL, media_list); ++ ++ status = cr_doc_handler_set_ctxt (a_this, at_media); ++ g_return_if_fail (status == CR_OK); ++ status = cr_doc_handler_set_result (a_this, at_media); ++ g_return_if_fail (status == CR_OK); ++} ++ ++static void ++parse_at_media_unrecoverable_error_cb (CRDocHandler * a_this) ++{ ++ enum CRStatus status = CR_OK; ++ CRStatement *stmt = NULL; ++ CRStatement **stmtptr = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ stmtptr = &stmt; ++ status = cr_doc_handler_get_result (a_this, (gpointer *) stmtptr); ++ if (status != CR_OK) { ++ cr_utils_trace_info ("Couldn't get parsing context. " ++ "This may lead to some memory leaks."); ++ return; ++ } ++ if (stmt) { ++ cr_statement_destroy (stmt); ++ stmt = NULL; ++ cr_doc_handler_set_ctxt (a_this, NULL); ++ cr_doc_handler_set_result (a_this, NULL); ++ } ++} ++ ++static void ++parse_at_media_start_selector_cb (CRDocHandler * a_this, ++ CRSelector * a_sellist) ++{ ++ enum CRStatus status = CR_OK; ++ CRStatement *at_media = NULL; ++ CRStatement **at_media_ptr = NULL; ++ CRStatement *ruleset = NULL; ++ ++ g_return_if_fail (a_this && a_this->priv && a_sellist); ++ ++ at_media_ptr = &at_media; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) at_media_ptr); ++ g_return_if_fail (status == CR_OK && at_media); ++ g_return_if_fail (at_media->type == AT_MEDIA_RULE_STMT); ++ ruleset = cr_statement_new_ruleset (NULL, a_sellist, NULL, at_media); ++ g_return_if_fail (ruleset); ++ status = cr_doc_handler_set_ctxt (a_this, ruleset); ++ g_return_if_fail (status == CR_OK); ++} ++ ++static void ++parse_at_media_property_cb (CRDocHandler * a_this, ++ CRString * a_name, CRTerm * a_value, ++ gboolean a_important) ++{ ++ enum CRStatus status = CR_OK; ++ ++ /* ++ *the current ruleset stmt, child of the ++ *current at-media being parsed. ++ */ ++ CRStatement *stmt = NULL; ++ CRStatement **stmtptr = NULL; ++ CRDeclaration *decl = NULL; ++ CRString *name = NULL; ++ ++ g_return_if_fail (a_this && a_name); ++ ++ name = cr_string_dup (a_name) ; ++ g_return_if_fail (name); ++ ++ stmtptr = &stmt; ++ status = cr_doc_handler_get_ctxt (a_this, ++ (gpointer *) stmtptr); ++ g_return_if_fail (status == CR_OK && stmt); ++ g_return_if_fail (stmt->type == RULESET_STMT); ++ ++ decl = cr_declaration_new (stmt, name, a_value); ++ g_return_if_fail (decl); ++ decl->important = a_important; ++ status = cr_statement_ruleset_append_decl (stmt, decl); ++ g_return_if_fail (status == CR_OK); ++} ++ ++static void ++parse_at_media_end_selector_cb (CRDocHandler * a_this, ++ CRSelector * a_sellist) ++{ ++ enum CRStatus status = CR_OK; ++ ++ /* ++ *the current ruleset stmt, child of the ++ *current at-media being parsed. ++ */ ++ CRStatement *stmt = NULL; ++ CRStatement **stmtptr = NULL; ++ ++ g_return_if_fail (a_this && a_sellist); ++ ++ stmtptr = &stmt; ++ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr); ++ g_return_if_fail (status == CR_OK && stmt ++ && stmt->type == RULESET_STMT); ++ g_return_if_fail (stmt->kind.ruleset->parent_media_rule); ++ ++ status = cr_doc_handler_set_ctxt ++ (a_this, stmt->kind.ruleset->parent_media_rule); ++ g_return_if_fail (status == CR_OK); ++} ++ ++static void ++parse_at_media_end_media_cb (CRDocHandler * a_this, ++ GList * a_media_list) ++{ ++ enum CRStatus status = CR_OK; ++ CRStatement *at_media = NULL; ++ CRStatement **at_media_ptr = NULL; ++ ++ g_return_if_fail (a_this && a_this->priv); ++ ++ at_media_ptr = &at_media; ++ status = cr_doc_handler_get_ctxt (a_this, ++ (gpointer *) at_media_ptr); ++ g_return_if_fail (status == CR_OK && at_media); ++ status = cr_doc_handler_set_result (a_this, at_media); ++} ++ ++static void ++parse_ruleset_start_selector_cb (CRDocHandler * a_this, ++ CRSelector * a_sellist) ++{ ++ CRStatement *ruleset = NULL; ++ ++ g_return_if_fail (a_this && a_this->priv && a_sellist); ++ ++ ruleset = cr_statement_new_ruleset (NULL, a_sellist, NULL, NULL); ++ g_return_if_fail (ruleset); ++ ++ cr_doc_handler_set_result (a_this, ruleset); ++} ++ ++static void ++parse_ruleset_unrecoverable_error_cb (CRDocHandler * a_this) ++{ ++ CRStatement *stmt = NULL; ++ CRStatement **stmtptr = NULL; ++ enum CRStatus status = CR_OK; ++ ++ stmtptr = &stmt; ++ status = cr_doc_handler_get_result (a_this, (gpointer *) stmtptr); ++ if (status != CR_OK) { ++ cr_utils_trace_info ("Couldn't get parsing context. " ++ "This may lead to some memory leaks."); ++ return; ++ } ++ if (stmt) { ++ cr_statement_destroy (stmt); ++ stmt = NULL; ++ cr_doc_handler_set_result (a_this, NULL); ++ } ++} ++ ++static void ++parse_ruleset_property_cb (CRDocHandler * a_this, ++ CRString * a_name, ++ CRTerm * a_value, gboolean a_important) ++{ ++ enum CRStatus status = CR_OK; ++ CRStatement *ruleset = NULL; ++ CRStatement **rulesetptr = NULL; ++ CRDeclaration *decl = NULL; ++ CRString *stringue = NULL; ++ ++ g_return_if_fail (a_this && a_this->priv && a_name); ++ ++ stringue = cr_string_dup (a_name); ++ g_return_if_fail (stringue); ++ ++ rulesetptr = &ruleset; ++ status = cr_doc_handler_get_result (a_this, (gpointer *) rulesetptr); ++ g_return_if_fail (status == CR_OK ++ && ruleset ++ && ruleset->type == RULESET_STMT); ++ ++ decl = cr_declaration_new (ruleset, stringue, a_value); ++ g_return_if_fail (decl); ++ decl->important = a_important; ++ status = cr_statement_ruleset_append_decl (ruleset, decl); ++ g_return_if_fail (status == CR_OK); ++} ++ ++static void ++parse_ruleset_end_selector_cb (CRDocHandler * a_this, ++ CRSelector * a_sellist) ++{ ++ CRStatement *result = NULL; ++ CRStatement **resultptr = NULL; ++ enum CRStatus status = CR_OK; ++ ++ g_return_if_fail (a_this && a_sellist); ++ ++ resultptr = &result; ++ status = cr_doc_handler_get_result (a_this, (gpointer *) resultptr); ++ ++ g_return_if_fail (status == CR_OK ++ && result ++ && result->type == RULESET_STMT); ++} ++ ++static void ++cr_statement_clear (CRStatement * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ switch (a_this->type) { ++ case AT_RULE_STMT: ++ break; ++ case RULESET_STMT: ++ if (!a_this->kind.ruleset) ++ return; ++ if (a_this->kind.ruleset->sel_list) { ++ cr_selector_unref (a_this->kind.ruleset->sel_list); ++ a_this->kind.ruleset->sel_list = NULL; ++ } ++ if (a_this->kind.ruleset->decl_list) { ++ cr_declaration_destroy ++ (a_this->kind.ruleset->decl_list); ++ a_this->kind.ruleset->decl_list = NULL; ++ } ++ g_free (a_this->kind.ruleset); ++ a_this->kind.ruleset = NULL; ++ break; ++ ++ case AT_IMPORT_RULE_STMT: ++ if (!a_this->kind.import_rule) ++ return; ++ if (a_this->kind.import_rule->url) { ++ cr_string_destroy ++ (a_this->kind.import_rule->url) ; ++ a_this->kind.import_rule->url = NULL; ++ } ++ g_free (a_this->kind.import_rule); ++ a_this->kind.import_rule = NULL; ++ break; ++ ++ case AT_MEDIA_RULE_STMT: ++ if (!a_this->kind.media_rule) ++ return; ++ if (a_this->kind.media_rule->rulesets) { ++ cr_statement_destroy ++ (a_this->kind.media_rule->rulesets); ++ a_this->kind.media_rule->rulesets = NULL; ++ } ++ if (a_this->kind.media_rule->media_list) { ++ GList *cur = NULL; ++ ++ for (cur = a_this->kind.media_rule->media_list; ++ cur; cur = cur->next) { ++ if (cur->data) { ++ cr_string_destroy ((CRString *) cur->data); ++ cur->data = NULL; ++ } ++ ++ } ++ g_list_free (a_this->kind.media_rule->media_list); ++ a_this->kind.media_rule->media_list = NULL; ++ } ++ g_free (a_this->kind.media_rule); ++ a_this->kind.media_rule = NULL; ++ break; ++ ++ case AT_PAGE_RULE_STMT: ++ if (!a_this->kind.page_rule) ++ return; ++ ++ if (a_this->kind.page_rule->decl_list) { ++ cr_declaration_destroy ++ (a_this->kind.page_rule->decl_list); ++ a_this->kind.page_rule->decl_list = NULL; ++ } ++ if (a_this->kind.page_rule->name) { ++ cr_string_destroy ++ (a_this->kind.page_rule->name); ++ a_this->kind.page_rule->name = NULL; ++ } ++ if (a_this->kind.page_rule->pseudo) { ++ cr_string_destroy ++ (a_this->kind.page_rule->pseudo); ++ a_this->kind.page_rule->pseudo = NULL; ++ } ++ g_free (a_this->kind.page_rule); ++ a_this->kind.page_rule = NULL; ++ break; ++ ++ case AT_CHARSET_RULE_STMT: ++ if (!a_this->kind.charset_rule) ++ return; ++ ++ if (a_this->kind.charset_rule->charset) { ++ cr_string_destroy ++ (a_this->kind.charset_rule->charset); ++ a_this->kind.charset_rule->charset = NULL; ++ } ++ g_free (a_this->kind.charset_rule); ++ a_this->kind.charset_rule = NULL; ++ break; ++ ++ case AT_FONT_FACE_RULE_STMT: ++ if (!a_this->kind.font_face_rule) ++ return; ++ ++ if (a_this->kind.font_face_rule->decl_list) { ++ cr_declaration_unref ++ (a_this->kind.font_face_rule->decl_list); ++ a_this->kind.font_face_rule->decl_list = NULL; ++ } ++ g_free (a_this->kind.font_face_rule); ++ a_this->kind.font_face_rule = NULL; ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++/** ++ * cr_statement_ruleset_to_string: ++ * ++ *@a_this: the current instance of #CRStatement ++ *@a_indent: the number of whitespace to use for indentation ++ * ++ *Serializes the ruleset statement into a string ++ * ++ *Returns the newly allocated serialised string. Must be freed ++ *by the caller, using g_free(). ++ */ ++static gchar * ++cr_statement_ruleset_to_string (CRStatement const * a_this, glong a_indent) ++{ ++ GString *stringue = NULL; ++ gchar *tmp_str = NULL, ++ *result = NULL; ++ ++ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT, NULL); ++ ++ stringue = g_string_new (NULL); ++ ++ if (a_this->kind.ruleset->sel_list) { ++ if (a_indent) ++ cr_utils_dump_n_chars2 (' ', stringue, a_indent); ++ ++ tmp_str = ++ (gchar *) cr_selector_to_string (a_this->kind.ruleset-> ++ sel_list); ++ if (tmp_str) { ++ g_string_append (stringue, tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ g_string_append (stringue, " {\n"); ++ if (a_this->kind.ruleset->decl_list) { ++ tmp_str = (gchar *) cr_declaration_list_to_string2 ++ (a_this->kind.ruleset->decl_list, ++ a_indent + DECLARATION_INDENT_NB, TRUE); ++ if (tmp_str) { ++ g_string_append (stringue, tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ g_string_append (stringue, "\n"); ++ cr_utils_dump_n_chars2 (' ', stringue, a_indent); ++ } ++ g_string_append (stringue, "}"); ++ result = stringue->str; ++ ++ if (stringue) { ++ g_string_free (stringue, FALSE); ++ stringue = NULL; ++ } ++ if (tmp_str) { ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ return result; ++} ++ ++ ++/** ++ * cr_statement_font_face_rule_to_string: ++ * ++ *@a_this: the current instance of #CRStatement to consider ++ *It must be a font face rule statement. ++ *@a_indent: the number of white spaces of indentation. ++ * ++ *Serializes a font face rule statement into a string. ++ * ++ *Returns the serialized string. Must be deallocated by the caller ++ *using g_free(). ++ */ ++static gchar * ++cr_statement_font_face_rule_to_string (CRStatement const * a_this, ++ glong a_indent) ++{ ++ gchar *result = NULL, *tmp_str = NULL ; ++ GString *stringue = NULL ; ++ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_FONT_FACE_RULE_STMT, ++ NULL); ++ ++ if (a_this->kind.font_face_rule->decl_list) { ++ stringue = g_string_new (NULL) ; ++ g_return_val_if_fail (stringue, NULL) ; ++ if (a_indent) ++ cr_utils_dump_n_chars2 (' ', stringue, ++ a_indent); ++ g_string_append (stringue, "@font-face {\n"); ++ tmp_str = (gchar *) cr_declaration_list_to_string2 ++ (a_this->kind.font_face_rule->decl_list, ++ a_indent + DECLARATION_INDENT_NB, TRUE) ; ++ if (tmp_str) { ++ g_string_append (stringue, ++ tmp_str) ; ++ g_free (tmp_str) ; ++ tmp_str = NULL ; ++ } ++ g_string_append (stringue, "\n}"); ++ } ++ if (stringue) { ++ result = stringue->str ; ++ g_string_free (stringue, FALSE) ; ++ stringue = NULL ; ++ } ++ return result ; ++} ++ ++ ++/** ++ * cr_statement_charset_to_string: ++ * ++ *Serialises an \@charset statement into a string. ++ *@a_this: the statement to serialize. ++ *@a_indent: the number of indentation spaces ++ * ++ *Returns the serialized charset statement. Must be ++ *freed by the caller using g_free(). ++ */ ++static gchar * ++cr_statement_charset_to_string (CRStatement const *a_this, ++ gulong a_indent) ++{ ++ gchar *str = NULL ; ++ GString *stringue = NULL ; ++ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_CHARSET_RULE_STMT, ++ NULL) ; ++ ++ if (a_this->kind.charset_rule ++ && a_this->kind.charset_rule->charset ++ && a_this->kind.charset_rule->charset->stryng ++ && a_this->kind.charset_rule->charset->stryng->str) { ++ str = g_strndup (a_this->kind.charset_rule->charset->stryng->str, ++ a_this->kind.charset_rule->charset->stryng->len); ++ g_return_val_if_fail (str, NULL); ++ stringue = g_string_new (NULL) ; ++ g_return_val_if_fail (stringue, NULL) ; ++ cr_utils_dump_n_chars2 (' ', stringue, a_indent); ++ g_string_append_printf (stringue, ++ "@charset \"%s\" ;", str); ++ if (str) { ++ g_free (str); ++ str = NULL; ++ } ++ } ++ if (stringue) { ++ str = stringue->str ; ++ g_string_free (stringue, FALSE) ; ++ } ++ return str ; ++} ++ ++ ++/** ++ * cr_statement_at_page_rule_to_string: ++ * ++ *Serialises the at page rule statement into a string ++ *@a_this: the current instance of #CRStatement. Must ++ *be an "\@page" rule statement. ++ * ++ *Returns the serialized string. Must be freed by the caller ++ */ ++static gchar * ++cr_statement_at_page_rule_to_string (CRStatement const *a_this, ++ gulong a_indent) ++{ ++ GString *stringue = NULL; ++ gchar *result = NULL ; ++ ++ stringue = g_string_new (NULL) ; ++ ++ cr_utils_dump_n_chars2 (' ', stringue, a_indent) ; ++ g_string_append (stringue, "@page"); ++ if (a_this->kind.page_rule->name ++ && a_this->kind.page_rule->name->stryng) { ++ g_string_append_printf ++ (stringue, " %s", ++ a_this->kind.page_rule->name->stryng->str) ; ++ } else { ++ g_string_append (stringue, " "); ++ } ++ if (a_this->kind.page_rule->pseudo ++ && a_this->kind.page_rule->pseudo->stryng) { ++ g_string_append_printf ++ (stringue, " :%s", ++ a_this->kind.page_rule->pseudo->stryng->str) ; ++ } ++ if (a_this->kind.page_rule->decl_list) { ++ gchar *str = NULL ; ++ g_string_append (stringue, " {\n"); ++ str = (gchar *) cr_declaration_list_to_string2 ++ (a_this->kind.page_rule->decl_list, ++ a_indent + DECLARATION_INDENT_NB, TRUE) ; ++ if (str) { ++ g_string_append (stringue, str) ; ++ g_free (str) ; ++ str = NULL ; ++ } ++ g_string_append (stringue, "\n}\n"); ++ } ++ result = stringue->str ; ++ g_string_free (stringue, FALSE) ; ++ stringue = NULL ; ++ return result ; ++} ++ ++ ++/** ++ *Serializes an \@media statement. ++ *@param a_this the current instance of #CRStatement ++ *@param a_indent the number of spaces of indentation. ++ *@return the serialized \@media statement. Must be freed ++ *by the caller using g_free(). ++ */ ++static gchar * ++cr_statement_media_rule_to_string (CRStatement const *a_this, ++ gulong a_indent) ++{ ++ gchar *str = NULL ; ++ GString *stringue = NULL ; ++ GList const *cur = NULL; ++ ++ g_return_val_if_fail (a_this->type == AT_MEDIA_RULE_STMT, ++ NULL); ++ ++ if (a_this->kind.media_rule) { ++ stringue = g_string_new (NULL) ; ++ cr_utils_dump_n_chars2 (' ', stringue, a_indent); ++ g_string_append (stringue, "@media"); ++ ++ for (cur = a_this->kind.media_rule->media_list; cur; ++ cur = cur->next) { ++ if (cur->data) { ++ gchar *str2 = cr_string_dup2 ++ ((CRString const *) cur->data); ++ ++ if (str2) { ++ if (cur->prev) { ++ g_string_append ++ (stringue, ++ ","); ++ } ++ g_string_append_printf ++ (stringue, ++ " %s", str2); ++ g_free (str2); ++ str2 = NULL; ++ } ++ } ++ } ++ g_string_append (stringue, " {\n"); ++ str = cr_statement_list_to_string ++ (a_this->kind.media_rule->rulesets, ++ a_indent + DECLARATION_INDENT_NB) ; ++ if (str) { ++ g_string_append (stringue, str) ; ++ g_free (str) ; ++ str = NULL ; ++ } ++ g_string_append (stringue, "\n}"); ++ } ++ if (stringue) { ++ str = stringue->str ; ++ g_string_free (stringue, FALSE) ; ++ } ++ return str ; ++} ++ ++ ++static gchar * ++cr_statement_import_rule_to_string (CRStatement const *a_this, ++ gulong a_indent) ++{ ++ GString *stringue = NULL ; ++ gchar *str = NULL; ++ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_IMPORT_RULE_STMT ++ && a_this->kind.import_rule, ++ NULL) ; ++ ++ if (a_this->kind.import_rule->url ++ && a_this->kind.import_rule->url->stryng) { ++ stringue = g_string_new (NULL) ; ++ g_return_val_if_fail (stringue, NULL) ; ++ str = g_strndup (a_this->kind.import_rule->url->stryng->str, ++ a_this->kind.import_rule->url->stryng->len); ++ cr_utils_dump_n_chars2 (' ', stringue, a_indent); ++ if (str) { ++ g_string_append_printf (stringue, ++ "@import url(\"%s\")", ++ str); ++ g_free (str); ++ str = NULL ; ++ } else /*there is no url, so no import rule, get out! */ ++ return NULL; ++ ++ if (a_this->kind.import_rule->media_list) { ++ GList const *cur = NULL; ++ ++ for (cur = a_this->kind.import_rule->media_list; ++ cur; cur = cur->next) { ++ if (cur->data) { ++ CRString const *crstr = cur->data; ++ ++ if (cur->prev) { ++ g_string_append ++ (stringue, ", "); ++ } ++ if (crstr ++ && crstr->stryng ++ && crstr->stryng->str) { ++ g_string_append_len ++ (stringue, ++ crstr->stryng->str, ++ crstr->stryng->len) ; ++ } ++ } ++ } ++ } ++ g_string_append (stringue, " ;"); ++ } ++ if (stringue) { ++ str = stringue->str ; ++ g_string_free (stringue, FALSE) ; ++ stringue = NULL ; ++ } ++ return str ; ++} ++ ++ ++/******************* ++ *public functions ++ ******************/ ++ ++/** ++ * cr_statement_does_buf_parses_against_core: ++ * ++ *@a_buf: the buffer to parse. ++ *@a_encoding: the character encoding of a_buf. ++ * ++ *Tries to parse a buffer and says whether if the content of the buffer ++ *is a css statement as defined by the "Core CSS Grammar" (chapter 4 of the ++ *css spec) or not. ++ * ++ *Returns TRUE if the buffer parses against the core grammar, false otherwise. ++ */ ++gboolean ++cr_statement_does_buf_parses_against_core (const guchar * a_buf, ++ enum CREncoding a_encoding) ++{ ++ CRParser *parser = NULL; ++ enum CRStatus status = CR_OK; ++ gboolean result = FALSE; ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf), ++ a_encoding, FALSE); ++ g_return_val_if_fail (parser, FALSE); ++ ++ status = cr_parser_set_use_core_grammar (parser, TRUE); ++ if (status != CR_OK) { ++ goto cleanup; ++ } ++ ++ status = cr_parser_parse_statement_core (parser); ++ if (status == CR_OK) { ++ result = TRUE; ++ } ++ ++ cleanup: ++ if (parser) { ++ cr_parser_destroy (parser); ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_statement_parse_from_buf: ++ * ++ *@a_buf: the buffer to parse. ++ *@a_encoding: the character encoding of a_buf. ++ * ++ *Parses a buffer that contains a css statement and returns ++ *an instance of #CRStatement in case of successful parsing. ++ *TODO: at support of "\@import" rules. ++ * ++ *Returns the newly built instance of #CRStatement in case ++ *of successful parsing, NULL otherwise. ++ */ ++CRStatement * ++cr_statement_parse_from_buf (const guchar * a_buf, enum CREncoding a_encoding) ++{ ++ CRStatement *result = NULL; ++ ++ /* ++ *The strategy of this function is "brute force". ++ *It tries to parse all the types of CRStatement it knows about. ++ *I could do this a smarter way but I don't have the time now. ++ *I think I will revisit this when time of performances and ++ *pull based incremental parsing comes. ++ */ ++ ++ result = cr_statement_ruleset_parse_from_buf (a_buf, a_encoding); ++ if (!result) { ++ result = cr_statement_at_charset_rule_parse_from_buf ++ (a_buf, a_encoding); ++ } else { ++ goto out; ++ } ++ ++ if (!result) { ++ result = cr_statement_at_media_rule_parse_from_buf ++ (a_buf, a_encoding); ++ } else { ++ goto out; ++ } ++ ++ if (!result) { ++ result = cr_statement_at_charset_rule_parse_from_buf ++ (a_buf, a_encoding); ++ } else { ++ goto out; ++ } ++ ++ if (!result) { ++ result = cr_statement_font_face_rule_parse_from_buf ++ (a_buf, a_encoding); ++ ++ } else { ++ goto out; ++ } ++ ++ if (!result) { ++ result = cr_statement_at_page_rule_parse_from_buf ++ (a_buf, a_encoding); ++ } else { ++ goto out; ++ } ++ ++ if (!result) { ++ result = cr_statement_at_import_rule_parse_from_buf ++ (a_buf, a_encoding); ++ } else { ++ goto out; ++ } ++ ++ out: ++ return result; ++} ++ ++/** ++ * cr_statement_ruleset_parse_from_buf: ++ * ++ *@a_buf: the buffer to parse. ++ *@a_enc: the character encoding of a_buf. ++ * ++ *Parses a buffer that contains a ruleset statement an instanciates ++ *a #CRStatement of type RULESET_STMT. ++ * ++ *Returns the newly built instance of #CRStatement in case of successful parsing, ++ *NULL otherwise. ++ */ ++CRStatement * ++cr_statement_ruleset_parse_from_buf (const guchar * a_buf, ++ enum CREncoding a_enc) ++{ ++ enum CRStatus status = CR_OK; ++ CRStatement *result = NULL; ++ CRStatement **resultptr = NULL; ++ CRParser *parser = NULL; ++ CRDocHandler *sac_handler = NULL; ++ ++ g_return_val_if_fail (a_buf, NULL); ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf), ++ a_enc, FALSE); ++ ++ g_return_val_if_fail (parser, NULL); ++ ++ sac_handler = cr_doc_handler_new (); ++ g_return_val_if_fail (parser, NULL); ++ ++ sac_handler->start_selector = parse_ruleset_start_selector_cb; ++ sac_handler->end_selector = parse_ruleset_end_selector_cb; ++ sac_handler->property = parse_ruleset_property_cb; ++ sac_handler->unrecoverable_error = ++ parse_ruleset_unrecoverable_error_cb; ++ ++ cr_parser_set_sac_handler (parser, sac_handler); ++ cr_parser_try_to_skip_spaces_and_comments (parser); ++ status = cr_parser_parse_ruleset (parser); ++ if (status != CR_OK) { ++ goto cleanup; ++ } ++ ++ resultptr = &result; ++ status = cr_doc_handler_get_result (sac_handler, ++ (gpointer *) resultptr); ++ if (!((status == CR_OK) && result)) { ++ if (result) { ++ cr_statement_destroy (result); ++ result = NULL; ++ } ++ } ++ ++ cleanup: ++ if (parser) { ++ cr_parser_destroy (parser); ++ parser = NULL; ++ sac_handler = NULL ; ++ } ++ if (sac_handler) { ++ cr_doc_handler_unref (sac_handler); ++ sac_handler = NULL; ++ } ++ return result; ++} ++ ++/** ++ * cr_statement_new_ruleset: ++ * ++ *@a_sel_list: the list of #CRSimpleSel (selectors) ++ *the rule applies to. ++ *@a_decl_list: the list of instances of #CRDeclaration ++ *that composes the ruleset. ++ *@a_media_types: a list of instances of GString that ++ *describe the media list this ruleset applies to. ++ * ++ *Creates a new instance of #CRStatement of type ++ *#CRRulSet. ++ * ++ *Returns the new instance of #CRStatement or NULL if something ++ *went wrong. ++ */ ++CRStatement * ++cr_statement_new_ruleset (CRStyleSheet * a_sheet, ++ CRSelector * a_sel_list, ++ CRDeclaration * a_decl_list, ++ CRStatement * a_parent_media_rule) ++{ ++ CRStatement *result = NULL; ++ ++ g_return_val_if_fail (a_sel_list, NULL); ++ ++ if (a_parent_media_rule) { ++ g_return_val_if_fail ++ (a_parent_media_rule->type == AT_MEDIA_RULE_STMT, ++ NULL); ++ g_return_val_if_fail (a_parent_media_rule->kind.media_rule, ++ NULL); ++ } ++ ++ result = g_try_malloc (sizeof (CRStatement)); ++ ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRStatement)); ++ result->type = RULESET_STMT; ++ result->kind.ruleset = g_try_malloc (sizeof (CRRuleSet)); ++ ++ if (!result->kind.ruleset) { ++ cr_utils_trace_info ("Out of memory"); ++ if (result) ++ g_free (result); ++ return NULL; ++ } ++ ++ memset (result->kind.ruleset, 0, sizeof (CRRuleSet)); ++ result->kind.ruleset->sel_list = a_sel_list; ++ if (a_sel_list) ++ cr_selector_ref (a_sel_list); ++ result->kind.ruleset->decl_list = a_decl_list; ++ ++ if (a_parent_media_rule) { ++ result->kind.ruleset->parent_media_rule = a_parent_media_rule; ++ a_parent_media_rule->kind.media_rule->rulesets = ++ cr_statement_append ++ (a_parent_media_rule->kind.media_rule->rulesets, ++ result); ++ } ++ ++ cr_statement_set_parent_sheet (result, a_sheet); ++ ++ return result; ++} ++ ++/** ++ * cr_statement_at_media_rule_parse_from_buf: ++ * ++ *@a_buf: the input to parse. ++ *@a_enc: the encoding of the buffer. ++ * ++ *Parses a buffer that contains an "\@media" declaration ++ *and builds an \@media css statement. ++ * ++ *Returns the \@media statement, or NULL if the buffer could not ++ *be successfully parsed. ++ */ ++CRStatement * ++cr_statement_at_media_rule_parse_from_buf (const guchar * a_buf, ++ enum CREncoding a_enc) ++{ ++ CRParser *parser = NULL; ++ CRStatement *result = NULL; ++ CRStatement **resultptr = NULL; ++ CRDocHandler *sac_handler = NULL; ++ enum CRStatus status = CR_OK; ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf), ++ a_enc, FALSE); ++ if (!parser) { ++ cr_utils_trace_info ("Instantiation of the parser failed"); ++ goto cleanup; ++ } ++ ++ sac_handler = cr_doc_handler_new (); ++ if (!sac_handler) { ++ cr_utils_trace_info ++ ("Instantiation of the sac handler failed"); ++ goto cleanup; ++ } ++ ++ sac_handler->start_media = parse_at_media_start_media_cb; ++ sac_handler->start_selector = parse_at_media_start_selector_cb; ++ sac_handler->property = parse_at_media_property_cb; ++ sac_handler->end_selector = parse_at_media_end_selector_cb; ++ sac_handler->end_media = parse_at_media_end_media_cb; ++ sac_handler->unrecoverable_error = ++ parse_at_media_unrecoverable_error_cb; ++ ++ status = cr_parser_set_sac_handler (parser, sac_handler); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ status = cr_parser_try_to_skip_spaces_and_comments (parser); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ status = cr_parser_parse_media (parser); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ resultptr = &result; ++ status = cr_doc_handler_get_result (sac_handler, ++ (gpointer *) resultptr); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ cleanup: ++ ++ if (parser) { ++ cr_parser_destroy (parser); ++ parser = NULL; ++ sac_handler = NULL ; ++ } ++ if (sac_handler) { ++ cr_doc_handler_unref (sac_handler); ++ sac_handler = NULL; ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_statement_new_at_media_rule: ++ * ++ *@a_ruleset: the ruleset statements contained ++ *in the \@media rule. ++ *@a_media: the media string list. A list of GString pointers. ++ * ++ *Instanciates an instance of #CRStatement of type ++ *AT_MEDIA_RULE_STMT (\@media ruleset). ++ * ++ */ ++CRStatement * ++cr_statement_new_at_media_rule (CRStyleSheet * a_sheet, ++ CRStatement * a_rulesets, GList * a_media) ++{ ++ CRStatement *result = NULL, ++ *cur = NULL; ++ ++ if (a_rulesets) ++ g_return_val_if_fail (a_rulesets->type == RULESET_STMT, NULL); ++ ++ result = g_try_malloc (sizeof (CRStatement)); ++ ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRStatement)); ++ result->type = AT_MEDIA_RULE_STMT; ++ ++ result->kind.media_rule = g_try_malloc (sizeof (CRAtMediaRule)); ++ if (!result->kind.media_rule) { ++ cr_utils_trace_info ("Out of memory"); ++ g_free (result); ++ return NULL; ++ } ++ memset (result->kind.media_rule, 0, sizeof (CRAtMediaRule)); ++ result->kind.media_rule->rulesets = a_rulesets; ++ for (cur = a_rulesets; cur; cur = cur->next) { ++ if (cur->type != RULESET_STMT || !cur->kind.ruleset) { ++ cr_utils_trace_info ("Bad parameter a_rulesets. " ++ "It should be a list of " ++ "correct ruleset statement only !"); ++ goto error; ++ } ++ cur->kind.ruleset->parent_media_rule = result; ++ } ++ ++ result->kind.media_rule->media_list = a_media; ++ if (a_sheet) { ++ cr_statement_set_parent_sheet (result, a_sheet); ++ } ++ ++ return result; ++ ++ error: ++ return NULL; ++} ++ ++/** ++ * cr_statement_new_at_import_rule: ++ * ++ *@a_url: the url to connect to the get the file ++ *to be imported. ++ *@a_sheet: the imported parsed stylesheet. ++ * ++ *Creates a new instance of #CRStatment of type ++ *#CRAtImportRule. ++ * ++ *Returns the newly built instance of #CRStatement. ++ */ ++CRStatement * ++cr_statement_new_at_import_rule (CRStyleSheet * a_container_sheet, ++ CRString * a_url, ++ GList * a_media_list, ++ CRStyleSheet * a_imported_sheet) ++{ ++ CRStatement *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRStatement)); ++ ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRStatement)); ++ result->type = AT_IMPORT_RULE_STMT; ++ ++ result->kind.import_rule = g_try_malloc (sizeof (CRAtImportRule)); ++ ++ if (!result->kind.import_rule) { ++ cr_utils_trace_info ("Out of memory"); ++ g_free (result); ++ return NULL; ++ } ++ ++ memset (result->kind.import_rule, 0, sizeof (CRAtImportRule)); ++ result->kind.import_rule->url = a_url; ++ result->kind.import_rule->media_list = a_media_list; ++ result->kind.import_rule->sheet = a_imported_sheet; ++ if (a_container_sheet) ++ cr_statement_set_parent_sheet (result, a_container_sheet); ++ ++ return result; ++} ++ ++/** ++ * cr_statement_at_import_rule_parse_from_buf: ++ * ++ *@a_buf: the buffer to parse. ++ *@a_encoding: the encoding of a_buf. ++ * ++ *Parses a buffer that contains an "\@import" rule and ++ *instanciate a #CRStatement of type AT_IMPORT_RULE_STMT ++ * ++ *Returns the newly built instance of #CRStatement in case of ++ *a successful parsing, NULL otherwise. ++ */ ++CRStatement * ++cr_statement_at_import_rule_parse_from_buf (const guchar * a_buf, ++ enum CREncoding a_encoding) ++{ ++ enum CRStatus status = CR_OK; ++ CRParser *parser = NULL; ++ CRStatement *result = NULL; ++ GList *media_list = NULL; ++ CRString *import_string = NULL; ++ CRParsingLocation location = {0} ; ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf), ++ a_encoding, FALSE); ++ if (!parser) { ++ cr_utils_trace_info ("Instantiation of parser failed."); ++ goto cleanup; ++ } ++ ++ status = cr_parser_try_to_skip_spaces_and_comments (parser); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ status = cr_parser_parse_import (parser, ++ &media_list, ++ &import_string, ++ &location); ++ if (status != CR_OK || !import_string) ++ goto cleanup; ++ ++ result = cr_statement_new_at_import_rule (NULL, import_string, ++ media_list, NULL); ++ if (result) { ++ cr_parsing_location_copy (&result->location, ++ &location) ; ++ import_string = NULL; ++ media_list = NULL; ++ } ++ ++ cleanup: ++ if (parser) { ++ cr_parser_destroy (parser); ++ parser = NULL; ++ } ++ if (media_list) { ++ for (; media_list; ++ media_list = g_list_next (media_list)) { ++ if (media_list->data) { ++ cr_string_destroy ((CRString*)media_list->data); ++ media_list->data = NULL; ++ } ++ } ++ g_list_free (media_list); ++ media_list = NULL; ++ } ++ if (import_string) { ++ cr_string_destroy (import_string); ++ import_string = NULL; ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_statement_new_at_page_rule: ++ * ++ *@a_decl_list: a list of instances of #CRDeclarations ++ *which is actually the list of declarations that applies to ++ *this page rule. ++ *@a_selector: the page rule selector. ++ * ++ *Creates a new instance of #CRStatement of type ++ *#CRAtPageRule. ++ * ++ *Returns the newly built instance of #CRStatement or NULL ++ *in case of error. ++ */ ++CRStatement * ++cr_statement_new_at_page_rule (CRStyleSheet * a_sheet, ++ CRDeclaration * a_decl_list, ++ CRString * a_name, CRString * a_pseudo) ++{ ++ CRStatement *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRStatement)); ++ ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRStatement)); ++ result->type = AT_PAGE_RULE_STMT; ++ ++ result->kind.page_rule = g_try_malloc (sizeof (CRAtPageRule)); ++ ++ if (!result->kind.page_rule) { ++ cr_utils_trace_info ("Out of memory"); ++ g_free (result); ++ return NULL; ++ } ++ ++ memset (result->kind.page_rule, 0, sizeof (CRAtPageRule)); ++ if (a_decl_list) { ++ result->kind.page_rule->decl_list = a_decl_list; ++ cr_declaration_ref (a_decl_list); ++ } ++ result->kind.page_rule->name = a_name; ++ result->kind.page_rule->pseudo = a_pseudo; ++ if (a_sheet) ++ cr_statement_set_parent_sheet (result, a_sheet); ++ ++ return result; ++} ++ ++/** ++ * cr_statement_at_page_rule_parse_from_buf: ++ * ++ *@a_buf: the character buffer to parse. ++ *@a_encoding: the character encoding of a_buf. ++ * ++ *Parses a buffer that contains an "\@page" production and, ++ *if the parsing succeeds, builds the page statement. ++ * ++ *Returns the newly built at page statement in case of successful parsing, ++ *NULL otherwise. ++ */ ++CRStatement * ++cr_statement_at_page_rule_parse_from_buf (const guchar * a_buf, ++ enum CREncoding a_encoding) ++{ ++ enum CRStatus status = CR_OK; ++ CRParser *parser = NULL; ++ CRDocHandler *sac_handler = NULL; ++ CRStatement *result = NULL; ++ CRStatement **resultptr = NULL; ++ ++ g_return_val_if_fail (a_buf, NULL); ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf), ++ a_encoding, FALSE); ++ if (!parser) { ++ cr_utils_trace_info ("Instantiation of the parser failed."); ++ goto cleanup; ++ } ++ ++ sac_handler = cr_doc_handler_new (); ++ if (!sac_handler) { ++ cr_utils_trace_info ++ ("Instantiation of the sac handler failed."); ++ goto cleanup; ++ } ++ ++ sac_handler->start_page = parse_page_start_page_cb; ++ sac_handler->property = parse_page_property_cb; ++ sac_handler->end_page = parse_page_end_page_cb; ++ sac_handler->unrecoverable_error = parse_page_unrecoverable_error_cb; ++ ++ status = cr_parser_set_sac_handler (parser, sac_handler); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ /*Now, invoke the parser to parse the "@page production" */ ++ cr_parser_try_to_skip_spaces_and_comments (parser); ++ if (status != CR_OK) ++ goto cleanup; ++ status = cr_parser_parse_page (parser); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ resultptr = &result; ++ status = cr_doc_handler_get_result (sac_handler, ++ (gpointer *) resultptr); ++ ++ cleanup: ++ ++ if (parser) { ++ cr_parser_destroy (parser); ++ parser = NULL; ++ sac_handler = NULL ; ++ } ++ if (sac_handler) { ++ cr_doc_handler_unref (sac_handler); ++ sac_handler = NULL; ++ } ++ return result; ++} ++ ++/** ++ * cr_statement_new_at_charset_rule: ++ * ++ *@a_charset: the string representing the charset. ++ *Note that the newly built instance of #CRStatement becomes ++ *the owner of a_charset. The caller must not free a_charset !!!. ++ * ++ *Creates a new instance of #CRStatement of type ++ *#CRAtCharsetRule. ++ * ++ *Returns the newly built instance of #CRStatement or NULL ++ *if an error arises. ++ */ ++CRStatement * ++cr_statement_new_at_charset_rule (CRStyleSheet * a_sheet, ++ CRString * a_charset) ++{ ++ CRStatement *result = NULL; ++ ++ g_return_val_if_fail (a_charset, NULL); ++ ++ result = g_try_malloc (sizeof (CRStatement)); ++ ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRStatement)); ++ result->type = AT_CHARSET_RULE_STMT; ++ ++ result->kind.charset_rule = g_try_malloc (sizeof (CRAtCharsetRule)); ++ ++ if (!result->kind.charset_rule) { ++ cr_utils_trace_info ("Out of memory"); ++ g_free (result); ++ return NULL; ++ } ++ memset (result->kind.charset_rule, 0, sizeof (CRAtCharsetRule)); ++ result->kind.charset_rule->charset = a_charset; ++ cr_statement_set_parent_sheet (result, a_sheet); ++ ++ return result; ++} ++ ++/** ++ * cr_statement_at_charset_rule_parse_from_buf: ++ * ++ *@a_buf: the buffer to parse. ++ *@a_encoding: the character encoding of the buffer. ++ * ++ *Parses a buffer that contains an '\@charset' rule and ++ *creates an instance of #CRStatement of type AT_CHARSET_RULE_STMT. ++ * ++ *Returns the newly built instance of #CRStatement. ++ */ ++CRStatement * ++cr_statement_at_charset_rule_parse_from_buf (const guchar * a_buf, ++ enum CREncoding a_encoding) ++{ ++ enum CRStatus status = CR_OK; ++ CRParser *parser = NULL; ++ CRStatement *result = NULL; ++ CRString *charset = NULL; ++ ++ g_return_val_if_fail (a_buf, NULL); ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf), ++ a_encoding, FALSE); ++ if (!parser) { ++ cr_utils_trace_info ("Instantiation of the parser failed."); ++ goto cleanup; ++ } ++ ++ /*Now, invoke the parser to parse the "@charset production" */ ++ cr_parser_try_to_skip_spaces_and_comments (parser); ++ if (status != CR_OK) ++ goto cleanup; ++ status = cr_parser_parse_charset (parser, &charset, NULL); ++ if (status != CR_OK || !charset) ++ goto cleanup; ++ ++ result = cr_statement_new_at_charset_rule (NULL, charset); ++ if (result) ++ charset = NULL; ++ ++ cleanup: ++ ++ if (parser) { ++ cr_parser_destroy (parser); ++ parser = NULL; ++ } ++ if (charset) { ++ cr_string_destroy (charset); ++ } ++ ++ return result; ++} ++ ++/** ++ * cr_statement_new_at_font_face_rule: ++ * ++ *@a_font_decls: a list of instances of #CRDeclaration. Each declaration ++ *is actually a font declaration. ++ * ++ *Creates an instance of #CRStatement of type #CRAtFontFaceRule. ++ * ++ *Returns the newly built instance of #CRStatement. ++ */ ++CRStatement * ++cr_statement_new_at_font_face_rule (CRStyleSheet * a_sheet, ++ CRDeclaration * a_font_decls) ++{ ++ CRStatement *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRStatement)); ++ ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (CRStatement)); ++ result->type = AT_FONT_FACE_RULE_STMT; ++ ++ result->kind.font_face_rule = g_try_malloc ++ (sizeof (CRAtFontFaceRule)); ++ ++ if (!result->kind.font_face_rule) { ++ cr_utils_trace_info ("Out of memory"); ++ g_free (result); ++ return NULL; ++ } ++ memset (result->kind.font_face_rule, 0, sizeof (CRAtFontFaceRule)); ++ ++ result->kind.font_face_rule->decl_list = a_font_decls; ++ if (a_sheet) ++ cr_statement_set_parent_sheet (result, a_sheet); ++ ++ return result; ++} ++ ++/** ++ * cr_statement_font_face_rule_parse_from_buf: ++ * ++ * ++ *@a_buf: the buffer to parse. ++ *@a_encoding: the character encoding of a_buf. ++ * ++ *Parses a buffer that contains an "\@font-face" rule and builds ++ *an instance of #CRStatement of type AT_FONT_FACE_RULE_STMT out of it. ++ * ++ *Returns the newly built instance of #CRStatement in case of successufull ++ *parsing, NULL otherwise. ++ */ ++CRStatement * ++cr_statement_font_face_rule_parse_from_buf (const guchar * a_buf, ++ enum CREncoding a_encoding) ++{ ++ CRStatement *result = NULL; ++ CRStatement **resultptr = NULL; ++ CRParser *parser = NULL; ++ CRDocHandler *sac_handler = NULL; ++ enum CRStatus status = CR_OK; ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf), ++ a_encoding, FALSE); ++ if (!parser) ++ goto cleanup; ++ ++ sac_handler = cr_doc_handler_new (); ++ if (!sac_handler) ++ goto cleanup; ++ ++ /* ++ *set sac callbacks here ++ */ ++ sac_handler->start_font_face = parse_font_face_start_font_face_cb; ++ sac_handler->property = parse_font_face_property_cb; ++ sac_handler->end_font_face = parse_font_face_end_font_face_cb; ++ sac_handler->unrecoverable_error = ++ parse_font_face_unrecoverable_error_cb; ++ ++ status = cr_parser_set_sac_handler (parser, sac_handler); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ /* ++ *cleanup spaces of comment that may be there before the real ++ *"@font-face" thing. ++ */ ++ status = cr_parser_try_to_skip_spaces_and_comments (parser); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ status = cr_parser_parse_font_face (parser); ++ if (status != CR_OK) ++ goto cleanup; ++ ++ resultptr = &result; ++ status = cr_doc_handler_get_result (sac_handler, ++ (gpointer *) resultptr); ++ if (status != CR_OK || !result) ++ goto cleanup; ++ ++ cleanup: ++ if (parser) { ++ cr_parser_destroy (parser); ++ parser = NULL; ++ sac_handler = NULL ; ++ } ++ if (sac_handler) { ++ cr_doc_handler_unref (sac_handler); ++ sac_handler = NULL; ++ } ++ return result; ++} ++ ++/** ++ * cr_statement_set_parent_sheet: ++ * ++ *@a_this: the current instance of #CRStatement. ++ *@a_sheet: the sheet that contains the current statement. ++ * ++ *Sets the container stylesheet. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_set_parent_sheet (CRStatement * a_this, CRStyleSheet * a_sheet) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ a_this->parent_sheet = a_sheet; ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_get_parent_sheet: ++ * ++ *@a_this: the current #CRStatement. ++ *@a_sheet: out parameter. A pointer to the sheets that ++ * ++ *Gets the sheets that contains the current statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_get_parent_sheet (CRStatement * a_this, CRStyleSheet ** a_sheet) ++{ ++ g_return_val_if_fail (a_this && a_sheet, CR_BAD_PARAM_ERROR); ++ *a_sheet = a_this->parent_sheet; ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_append: ++ * ++ *@a_this: the current instance of the statement list. ++ *@a_new: a_new the new instance of #CRStatement to append. ++ * ++ *Appends a new statement to the statement list. ++ * ++ *Returns the new list statement list, or NULL in cas of failure. ++ */ ++CRStatement * ++cr_statement_append (CRStatement * a_this, CRStatement * a_new) ++{ ++ CRStatement *cur = NULL; ++ ++ g_return_val_if_fail (a_new, NULL); ++ ++ if (!a_this) { ++ return a_new; ++ } ++ ++ /*walk forward in the current list to find the tail list element */ ++ for (cur = a_this; cur && cur->next; cur = cur->next) ; ++ ++ cur->next = a_new; ++ a_new->prev = cur; ++ ++ return a_this; ++} ++ ++/** ++ * cr_statement_prepend: ++ * ++ *@a_this: the current instance of #CRStatement. ++ *@a_new: the new statement to prepend. ++ * ++ *Prepends the an instance of #CRStatement to ++ *the current statement list. ++ * ++ *Returns the new list with the new statement prepended, ++ *or NULL in case of an error. ++ */ ++CRStatement * ++cr_statement_prepend (CRStatement * a_this, CRStatement * a_new) ++{ ++ CRStatement *cur = NULL; ++ ++ g_return_val_if_fail (a_new, NULL); ++ ++ if (!a_this) ++ return a_new; ++ ++ a_new->next = a_this; ++ a_this->prev = a_new; ++ ++ /*walk backward in the prepended list to find the head list element */ ++ for (cur = a_new; cur && cur->prev; cur = cur->prev) ; ++ ++ return cur; ++} ++ ++/** ++ * cr_statement_unlink: ++ * ++ *@a_this: the current statements list. ++ *@a_to_unlink: the statement to unlink from the list. ++ * ++ *Unlinks a statement from the statements list. ++ * ++ *Returns the new list where a_to_unlink has been unlinked ++ *from, or NULL in case of error. ++ */ ++CRStatement * ++cr_statement_unlink (CRStatement * a_stmt) ++{ ++ CRStatement *result = a_stmt; ++ ++ g_return_val_if_fail (result, NULL); ++ ++ /** ++ *Some sanity checks first ++ */ ++ if (a_stmt->next) { ++ g_return_val_if_fail (a_stmt->next->prev == a_stmt, NULL); ++ } ++ if (a_stmt->prev) { ++ g_return_val_if_fail (a_stmt->prev->next == a_stmt, NULL); ++ } ++ ++ /** ++ *Now, the real unlinking job. ++ */ ++ if (a_stmt->next) { ++ a_stmt->next->prev = a_stmt->prev; ++ } ++ if (a_stmt->prev) { ++ a_stmt->prev->next = a_stmt->next; ++ } ++ ++ if (a_stmt->parent_sheet ++ && a_stmt->parent_sheet->statements == a_stmt) { ++ a_stmt->parent_sheet->statements = ++ a_stmt->parent_sheet->statements->next; ++ } ++ ++ a_stmt->next = NULL; ++ a_stmt->prev = NULL; ++ a_stmt->parent_sheet = NULL; ++ ++ return result; ++} ++ ++/** ++ * cr_statement_nr_rules: ++ * ++ *@a_this: the current instance of #CRStatement. ++ * ++ *Gets the number of rules in the statement list; ++ * ++ *Returns number of rules in the statement list. ++ */ ++gint ++cr_statement_nr_rules (CRStatement const * a_this) ++{ ++ CRStatement const *cur = NULL; ++ int nr = 0; ++ ++ g_return_val_if_fail (a_this, -1); ++ ++ for (cur = a_this; cur; cur = cur->next) ++ nr++; ++ return nr; ++} ++ ++/** ++ * cr_statement_get_from_list: ++ * ++ *@a_this: the current instance of #CRStatement. ++ *@itemnr: the index into the statement list. ++ * ++ *Use an index to get a CRStatement from the statement list. ++ * ++ *Returns CRStatement at position itemnr, if itemnr > number of statements - 1, ++ *it will return NULL. ++ */ ++CRStatement * ++cr_statement_get_from_list (CRStatement * a_this, int itemnr) ++{ ++ CRStatement *cur = NULL; ++ int nr = 0; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ for (cur = a_this; cur; cur = cur->next) ++ if (nr++ == itemnr) ++ return cur; ++ return NULL; ++} ++ ++/** ++ * cr_statement_ruleset_set_sel_list: ++ * ++ *@a_this: the current ruleset statement. ++ *@a_sel_list: the selector list to set. Note ++ *that this function increments the ref count of a_sel_list. ++ *The sel list will be destroyed at the destruction of the ++ *current instance of #CRStatement. ++ * ++ *Sets a selector list to a ruleset statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_ruleset_set_sel_list (CRStatement * a_this, ++ CRSelector * a_sel_list) ++{ ++ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT, ++ CR_BAD_PARAM_ERROR); ++ ++ if (a_this->kind.ruleset->sel_list) ++ cr_selector_unref (a_this->kind.ruleset->sel_list); ++ ++ a_this->kind.ruleset->sel_list = a_sel_list; ++ ++ if (a_sel_list) ++ cr_selector_ref (a_sel_list); ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_ruleset_get_declarations: ++ * ++ *@a_this: the current instance of #CRStatement. ++ *@a_decl_list: out parameter. A pointer to the the returned ++ *list of declaration. Must not be NULL. ++ * ++ *Gets a pointer to the list of declaration contained ++ *in the ruleset statement. ++ * ++ *Returns CR_OK upon successful completion, an error code if something ++ *bad happened. ++ */ ++enum CRStatus ++cr_statement_ruleset_get_declarations (CRStatement * a_this, ++ CRDeclaration ** a_decl_list) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == RULESET_STMT ++ && a_this->kind.ruleset ++ && a_decl_list, CR_BAD_PARAM_ERROR); ++ ++ *a_decl_list = a_this->kind.ruleset->decl_list; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_ruleset_get_sel_list: ++ * ++ *@a_this: the current ruleset statement. ++ *@a_list: out parameter. The returned selector list, ++ *if and only if the function returned CR_OK. ++ * ++ *Gets a pointer to the selector list contained in ++ *the current ruleset statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_ruleset_get_sel_list (CRStatement const * a_this, CRSelector ** a_list) ++{ ++ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT ++ && a_this->kind.ruleset, CR_BAD_PARAM_ERROR); ++ ++ *a_list = a_this->kind.ruleset->sel_list; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_ruleset_set_decl_list: ++ * ++ *@a_this: the current ruleset statement. ++ *@a_list: the declaration list to be added to the current ++ *ruleset statement. ++ * ++ *Sets a declaration list to the current ruleset statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_ruleset_set_decl_list (CRStatement * a_this, ++ CRDeclaration * a_list) ++{ ++ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT ++ && a_this->kind.ruleset, CR_BAD_PARAM_ERROR); ++ ++ if (a_this->kind.ruleset->decl_list == a_list) ++ return CR_OK; ++ ++ if (a_this->kind.ruleset->sel_list) { ++ cr_declaration_destroy (a_this->kind.ruleset->decl_list); ++ } ++ ++ a_this->kind.ruleset->sel_list = NULL; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_ruleset_append_decl2: ++ * ++ *@a_this: the current statement. ++ *@a_prop: the property of the declaration. ++ *@a_value: the value of the declaration. ++ * ++ *Appends a declaration to the current ruleset statement. ++ * ++ *Returns CR_OK upon successful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_statement_ruleset_append_decl2 (CRStatement * a_this, ++ CRString * a_prop, ++ CRTerm * a_value) ++{ ++ CRDeclaration *new_decls = NULL; ++ ++ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT ++ && a_this->kind.ruleset, CR_BAD_PARAM_ERROR); ++ ++ new_decls = cr_declaration_append2 ++ (a_this->kind.ruleset->decl_list, ++ a_prop, a_value); ++ g_return_val_if_fail (new_decls, CR_ERROR); ++ a_this->kind.ruleset->decl_list = new_decls; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_ruleset_append_decl: ++ * ++ *Appends a declaration to the current statement. ++ * ++ *@a_this: the current statement. ++ *@a_declaration: the declaration to append. ++ * ++ *Returns CR_OK upon sucessful completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_statement_ruleset_append_decl (CRStatement * a_this, ++ CRDeclaration * a_decl) ++{ ++ CRDeclaration *new_decls = NULL; ++ ++ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT ++ && a_this->kind.ruleset, CR_BAD_PARAM_ERROR); ++ ++ new_decls = cr_declaration_append ++ (a_this->kind.ruleset->decl_list, a_decl); ++ g_return_val_if_fail (new_decls, CR_ERROR); ++ a_this->kind.ruleset->decl_list = new_decls; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_at_import_rule_set_imported_sheet: ++ * ++ *Sets a stylesheet to the current \@import rule. ++ *@a_this: the current \@import rule. ++ *@a_sheet: the stylesheet. The stylesheet is owned ++ *by the current instance of #CRStatement, that is, the ++ *stylesheet will be destroyed when the current instance ++ *of #CRStatement is destroyed. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_import_rule_set_imported_sheet (CRStatement * a_this, ++ CRStyleSheet * a_sheet) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_IMPORT_RULE_STMT ++ && a_this->kind.import_rule, ++ CR_BAD_PARAM_ERROR); ++ ++ a_this->kind.import_rule->sheet = a_sheet; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_at_import_rule_get_imported_sheet: ++ * ++ *@a_this: the current \@import rule statement. ++ *@a_sheet: out parameter. The returned stylesheet if and ++ *only if the function returns CR_OK. ++ * ++ *Gets the stylesheet contained by the \@import rule statement. ++ *Returns CR_OK upon sucessful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_import_rule_get_imported_sheet (CRStatement * a_this, ++ CRStyleSheet ** a_sheet) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_IMPORT_RULE_STMT ++ && a_this->kind.import_rule, ++ CR_BAD_PARAM_ERROR); ++ *a_sheet = a_this->kind.import_rule->sheet; ++ return CR_OK; ++ ++} ++ ++/** ++ * cr_statement_at_import_rule_set_url: ++ * ++ *@a_this: the current \@import rule statement. ++ *@a_url: the url to set. ++ * ++ *Sets an url to the current \@import rule statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_import_rule_set_url (CRStatement * a_this, ++ CRString * a_url) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_IMPORT_RULE_STMT ++ && a_this->kind.import_rule, ++ CR_BAD_PARAM_ERROR); ++ ++ if (a_this->kind.import_rule->url) { ++ cr_string_destroy (a_this->kind.import_rule->url); ++ } ++ ++ a_this->kind.import_rule->url = a_url; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_at_import_rule_get_url: ++ * ++ *@a_this: the current \@import rule statement. ++ *@a_url: out parameter. The returned url if ++ *and only if the function returned CR_OK. ++ * ++ *Gets the url of the \@import rule statement. ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_import_rule_get_url (CRStatement const * a_this, ++ CRString ** a_url) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_IMPORT_RULE_STMT ++ && a_this->kind.import_rule, ++ CR_BAD_PARAM_ERROR); ++ ++ *a_url = a_this->kind.import_rule->url; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_at_media_nr_rules: ++ * ++ *@a_this: the current instance of #CRStatement. ++ * ++ *Returns the number of rules in the media rule; ++ */ ++int ++cr_statement_at_media_nr_rules (CRStatement const * a_this) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_MEDIA_RULE_STMT ++ && a_this->kind.media_rule, CR_BAD_PARAM_ERROR); ++ ++ return cr_statement_nr_rules (a_this->kind.media_rule->rulesets); ++} ++ ++/** ++ * cr_statement_at_media_get_from_list: ++ * ++ *@a_this: the current instance of #CRStatement. ++ *@itemnr: the index into the media rule list of rules. ++ * ++ *Use an index to get a CRStatement from the media rule list of rules. ++ * ++ *Returns CRStatement at position itemnr, if itemnr > number of rules - 1, ++ *it will return NULL. ++ */ ++CRStatement * ++cr_statement_at_media_get_from_list (CRStatement * a_this, int itemnr) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_MEDIA_RULE_STMT ++ && a_this->kind.media_rule, NULL); ++ ++ return cr_statement_get_from_list (a_this->kind.media_rule->rulesets, ++ itemnr); ++} ++ ++/** ++ * cr_statement_at_page_rule_set_declarations: ++ * ++ *@a_this: the current \@page rule statement. ++ *@a_decl_list: the declaration list to add. Will be freed ++ *by the current instance of #CRStatement when it is destroyed. ++ * ++ *Sets a declaration list to the current \@page rule statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_page_rule_set_declarations (CRStatement * a_this, ++ CRDeclaration * a_decl_list) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_PAGE_RULE_STMT ++ && a_this->kind.page_rule, CR_BAD_PARAM_ERROR); ++ ++ if (a_this->kind.page_rule->decl_list) { ++ cr_declaration_unref (a_this->kind.page_rule->decl_list); ++ } ++ ++ a_this->kind.page_rule->decl_list = a_decl_list; ++ ++ if (a_decl_list) { ++ cr_declaration_ref (a_decl_list); ++ } ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_at_page_rule_get_declarations: ++ * ++ *@a_this: the current \@page rule statement. ++ *@a_decl_list: out parameter. The returned declaration list. ++ * ++ *Gets the declaration list associated to the current \@page rule ++ *statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_page_rule_get_declarations (CRStatement * a_this, ++ CRDeclaration ** a_decl_list) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_PAGE_RULE_STMT ++ && a_this->kind.page_rule, CR_BAD_PARAM_ERROR); ++ ++ *a_decl_list = a_this->kind.page_rule->decl_list; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_at_charset_rule_set_charset: ++ * ++ * ++ *@a_this: the current \@charset rule statement. ++ *@a_charset: the charset to set. ++ * ++ *Sets the charset of the current \@charset rule statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_charset_rule_set_charset (CRStatement * a_this, ++ CRString * a_charset) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_CHARSET_RULE_STMT ++ && a_this->kind.charset_rule, ++ CR_BAD_PARAM_ERROR); ++ ++ if (a_this->kind.charset_rule->charset) { ++ cr_string_destroy (a_this->kind.charset_rule->charset); ++ } ++ a_this->kind.charset_rule->charset = a_charset; ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_at_charset_rule_get_charset: ++ *@a_this: the current \@charset rule statement. ++ *@a_charset: out parameter. The returned charset string if ++ *and only if the function returned CR_OK. ++ * ++ *Gets the charset string associated to the current ++ *\@charset rule statement. ++ * ++ * Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_charset_rule_get_charset (CRStatement const * a_this, ++ CRString ** a_charset) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_CHARSET_RULE_STMT ++ && a_this->kind.charset_rule, ++ CR_BAD_PARAM_ERROR); ++ ++ *a_charset = a_this->kind.charset_rule->charset; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_at_font_face_rule_set_decls: ++ * ++ *@a_this: the current \@font-face rule statement. ++ *@a_decls: the declarations list to set. ++ * ++ *Sets a declaration list to the current \@font-face rule statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_font_face_rule_set_decls (CRStatement * a_this, ++ CRDeclaration * a_decls) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_FONT_FACE_RULE_STMT ++ && a_this->kind.font_face_rule, ++ CR_BAD_PARAM_ERROR); ++ ++ if (a_this->kind.font_face_rule->decl_list) { ++ cr_declaration_unref (a_this->kind.font_face_rule->decl_list); ++ } ++ ++ a_this->kind.font_face_rule->decl_list = a_decls; ++ cr_declaration_ref (a_decls); ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_at_font_face_rule_get_decls: ++ * ++ *@a_this: the current \@font-face rule statement. ++ *@a_decls: out parameter. The returned declaration list if ++ *and only if this function returns CR_OK. ++ * ++ *Gets the declaration list associated to the current instance ++ *of \@font-face rule statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_font_face_rule_get_decls (CRStatement * a_this, ++ CRDeclaration ** a_decls) ++{ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_FONT_FACE_RULE_STMT ++ && a_this->kind.font_face_rule, ++ CR_BAD_PARAM_ERROR); ++ ++ *a_decls = a_this->kind.font_face_rule->decl_list; ++ ++ return CR_OK; ++} ++ ++/** ++ * cr_statement_at_font_face_rule_add_decl: ++ * ++ *@a_this: the current \@font-face rule statement. ++ *@a_prop: the property of the declaration. ++ *@a_value: the value of the declaration. ++ * ++ *Adds a declaration to the current \@font-face rule ++ *statement. ++ * ++ *Returns CR_OK upon successful completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_statement_at_font_face_rule_add_decl (CRStatement * a_this, ++ CRString * a_prop, CRTerm * a_value) ++{ ++ CRDeclaration *decls = NULL; ++ ++ g_return_val_if_fail (a_this ++ && a_this->type == AT_FONT_FACE_RULE_STMT ++ && a_this->kind.font_face_rule, ++ CR_BAD_PARAM_ERROR); ++ ++ decls = cr_declaration_append2 ++ (a_this->kind.font_face_rule->decl_list, ++ a_prop, a_value); ++ ++ g_return_val_if_fail (decls, CR_ERROR); ++ ++ if (a_this->kind.font_face_rule->decl_list == NULL) ++ cr_declaration_ref (decls); ++ ++ a_this->kind.font_face_rule->decl_list = decls; ++ ++ return CR_OK; ++} ++ ++ ++/** ++ * cr_statement_to_string: ++ * ++ *@a_this: the current statement to serialize ++ *@a_indent: the number of white space of indentation. ++ * ++ *Serializes a css statement into a string ++ * ++ *Returns the serialized statement. Must be freed by the caller ++ *using g_free(). ++ */ ++gchar * ++cr_statement_to_string (CRStatement const * a_this, gulong a_indent) ++{ ++ gchar *str = NULL ; ++ ++ if (!a_this) ++ return NULL; ++ ++ switch (a_this->type) { ++ case RULESET_STMT: ++ str = cr_statement_ruleset_to_string ++ (a_this, a_indent); ++ break; ++ ++ case AT_FONT_FACE_RULE_STMT: ++ str = cr_statement_font_face_rule_to_string ++ (a_this, a_indent) ; ++ break; ++ ++ case AT_CHARSET_RULE_STMT: ++ str = cr_statement_charset_to_string ++ (a_this, a_indent); ++ break; ++ ++ case AT_PAGE_RULE_STMT: ++ str = cr_statement_at_page_rule_to_string ++ (a_this, a_indent); ++ break; ++ ++ case AT_MEDIA_RULE_STMT: ++ str = cr_statement_media_rule_to_string ++ (a_this, a_indent); ++ break; ++ ++ case AT_IMPORT_RULE_STMT: ++ str = cr_statement_import_rule_to_string ++ (a_this, a_indent); ++ break; ++ ++ default: ++ cr_utils_trace_info ("Statement unrecognized"); ++ break; ++ } ++ return str ; ++} ++ ++gchar* ++cr_statement_list_to_string (CRStatement const *a_this, gulong a_indent) ++{ ++ CRStatement const *cur_stmt = NULL ; ++ GString *stringue = NULL ; ++ gchar *str = NULL ; ++ ++ g_return_val_if_fail (a_this, NULL) ; ++ ++ stringue = g_string_new (NULL) ; ++ if (!stringue) { ++ cr_utils_trace_info ("Out of memory") ; ++ return NULL ; ++ } ++ for (cur_stmt = a_this ; cur_stmt; ++ cur_stmt = cur_stmt->next) { ++ str = cr_statement_to_string (cur_stmt, a_indent) ; ++ if (str) { ++ if (!cur_stmt->prev) { ++ g_string_append (stringue, str) ; ++ } else { ++ g_string_append_printf ++ (stringue, "\n%s", str) ; ++ } ++ g_free (str) ; ++ str = NULL ; ++ } ++ } ++ str = stringue->str ; ++ g_string_free (stringue, FALSE) ; ++ return str ; ++} ++ ++/** ++ * cr_statement_dump: ++ * ++ *@a_this: the current css2 statement. ++ *@a_fp: the destination file pointer. ++ *@a_indent: the number of white space indentation characters. ++ * ++ *Dumps the css2 statement to a file. ++ */ ++void ++cr_statement_dump (CRStatement const * a_this, FILE * a_fp, gulong a_indent) ++{ ++ gchar *str = NULL ; ++ ++ if (!a_this) ++ return; ++ ++ str = cr_statement_to_string (a_this, a_indent) ; ++ if (str) { ++ fprintf (a_fp, "%s",str) ; ++ g_free (str) ; ++ str = NULL ; ++ } ++} ++ ++/** ++ * cr_statement_dump_ruleset: ++ * ++ *@a_this: the current instance of #CRStatement. ++ *@a_fp: the destination file pointer. ++ *@a_indent: the number of indentation white spaces to add. ++ * ++ *Dumps a ruleset statement to a file. ++ */ ++void ++cr_statement_dump_ruleset (CRStatement const * a_this, FILE * a_fp, glong a_indent) ++{ ++ gchar *str = NULL; ++ ++ g_return_if_fail (a_fp && a_this); ++ str = cr_statement_ruleset_to_string (a_this, a_indent); ++ if (str) { ++ fprintf (a_fp, "%s", str); ++ g_free (str); ++ str = NULL; ++ } ++} ++ ++/** ++ * cr_statement_dump_font_face_rule: ++ * ++ *@a_this: the current instance of font face rule statement. ++ *@a_fp: the destination file pointer. ++ *@a_indent: the number of white space indentation. ++ * ++ *Dumps a font face rule statement to a file. ++ */ ++void ++cr_statement_dump_font_face_rule (CRStatement const * a_this, FILE * a_fp, ++ glong a_indent) ++{ ++ gchar *str = NULL ; ++ g_return_if_fail (a_this ++ && a_this->type == AT_FONT_FACE_RULE_STMT); ++ ++ str = cr_statement_font_face_rule_to_string (a_this, ++ a_indent) ; ++ if (str) { ++ fprintf (a_fp, "%s", str) ; ++ g_free (str) ; ++ str = NULL ; ++ } ++} ++ ++/** ++ * cr_statement_dump_charset: ++ * ++ *@a_this: the current instance of the \@charset rule statement. ++ *@a_fp: the destination file pointer. ++ *@a_indent: the number of indentation white spaces. ++ * ++ *Dumps an \@charset rule statement to a file. ++ */ ++void ++cr_statement_dump_charset (CRStatement const * a_this, FILE * a_fp, gulong a_indent) ++{ ++ gchar *str = NULL; ++ ++ g_return_if_fail (a_this && a_this->type == AT_CHARSET_RULE_STMT); ++ ++ str = cr_statement_charset_to_string (a_this, ++ a_indent) ; ++ if (str) { ++ fprintf (a_fp, "%s", str) ; ++ g_free (str) ; ++ str = NULL ; ++ } ++} ++ ++ ++/** ++ * cr_statement_dump_page: ++ * ++ *@a_this: the statement to dump on stdout. ++ *@a_fp: the destination file pointer. ++ *@a_indent: the number of indentation white spaces. ++ * ++ *Dumps an \@page rule statement on stdout. ++ */ ++void ++cr_statement_dump_page (CRStatement const * a_this, FILE * a_fp, gulong a_indent) ++{ ++ gchar *str = NULL; ++ ++ g_return_if_fail (a_this ++ && a_this->type == AT_PAGE_RULE_STMT ++ && a_this->kind.page_rule); ++ ++ str = cr_statement_at_page_rule_to_string (a_this, a_indent) ; ++ if (str) { ++ fprintf (a_fp, "%s", str); ++ g_free (str) ; ++ str = NULL ; ++ } ++} ++ ++ ++/** ++ * cr_statement_dump_media_rule: ++ * ++ *@a_this: the statement to dump. ++ *@a_fp: the destination file pointer ++ *@a_indent: the number of white spaces indentation. ++ * ++ *Dumps an \@media rule statement to a file. ++ */ ++void ++cr_statement_dump_media_rule (CRStatement const * a_this, ++ FILE * a_fp, ++ gulong a_indent) ++{ ++ gchar *str = NULL ; ++ g_return_if_fail (a_this->type == AT_MEDIA_RULE_STMT); ++ ++ str = cr_statement_media_rule_to_string (a_this, a_indent) ; ++ if (str) { ++ fprintf (a_fp, "%s", str) ; ++ g_free (str) ; ++ str = NULL ; ++ } ++} ++ ++/** ++ * cr_statement_dump_import_rule: ++ * ++ *@a_fp: the destination file pointer. ++ *@a_indent: the number of white space indentations. ++ * ++ *Dumps an \@import rule statement to a file. ++ */ ++void ++cr_statement_dump_import_rule (CRStatement const * a_this, FILE * a_fp, ++ gulong a_indent) ++{ ++ gchar *str = NULL ; ++ g_return_if_fail (a_this ++ && a_this->type == AT_IMPORT_RULE_STMT ++ && a_fp ++ && a_this->kind.import_rule); ++ ++ str = cr_statement_import_rule_to_string (a_this, a_indent) ; ++ if (str) { ++ fprintf (a_fp, "%s", str) ; ++ g_free (str) ; ++ str = NULL ; ++ } ++} ++ ++/** ++ * cr_statement_destroy: ++ * ++ * @a_this: the current instance of #CRStatement. ++ * ++ *Destructor of #CRStatement. ++ */ ++void ++cr_statement_destroy (CRStatement * a_this) ++{ ++ CRStatement *cur = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ /*go get the tail of the list */ ++ for (cur = a_this; cur && cur->next; cur = cur->next) { ++ cr_statement_clear (cur); ++ } ++ ++ if (cur) ++ cr_statement_clear (cur); ++ ++ if (cur->prev == NULL) { ++ g_free (a_this); ++ return; ++ } ++ ++ /*walk backward and free next element */ ++ for (cur = cur->prev; cur && cur->prev; cur = cur->prev) { ++ if (cur->next) { ++ g_free (cur->next); ++ cur->next = NULL; ++ } ++ } ++ ++ if (!cur) ++ return; ++ ++ /*free the one remaining list */ ++ if (cur->next) { ++ g_free (cur->next); ++ cur->next = NULL; ++ } ++ ++ g_free (cur); ++ cur = NULL; ++} +diff --git a/src/st/croco/cr-statement.h b/src/st/croco/cr-statement.h +new file mode 100644 +index 000000000..74a233055 +--- /dev/null ++++ b/src/st/croco/cr-statement.h +@@ -0,0 +1,440 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#include ++#include "cr-utils.h" ++#include "cr-term.h" ++#include "cr-selector.h" ++#include "cr-declaration.h" ++ ++#ifndef __CR_STATEMENT_H__ ++#define __CR_STATEMENT_H__ ++ ++G_BEGIN_DECLS ++ ++/** ++ *@file ++ *Declaration of the #CRStatement class. ++ */ ++ ++/* ++ *forward declaration of CRStyleSheet which is defined in ++ *cr-stylesheet.h ++ */ ++ ++struct _CRStatement ; ++ ++/* ++ *typedef struct _CRStatement CRStatement ; ++ *this is forward declared in ++ *cr-declaration.h already. ++ */ ++ ++struct _CRAtMediaRule ; ++typedef struct _CRAtMediaRule CRAtMediaRule ; ++ ++typedef struct _CRRuleSet CRRuleSet ; ++ ++/** ++ *The abstraction of a css ruleset. ++ *A ruleset is made of a list of selectors, ++ *followed by a list of declarations. ++ */ ++struct _CRRuleSet ++{ ++ /**A list of instances of #CRSimpeSel*/ ++ CRSelector *sel_list ; ++ ++ /**A list of instances of #CRDeclaration*/ ++ CRDeclaration *decl_list ; ++ ++ /** ++ *The parent media rule, or NULL if ++ *no parent media rule exists. ++ */ ++ CRStatement *parent_media_rule ; ++} ; ++ ++/* ++ *a forward declaration of CRStylesheet. ++ *CRStylesheet is actually declared in ++ *cr-stylesheet.h ++ */ ++struct _CRStyleSheet ; ++typedef struct _CRStyleSheet CRStyleSheet; ++ ++ ++/**The \@import rule abstraction.*/ ++typedef struct _CRAtImportRule CRAtImportRule ; ++struct _CRAtImportRule ++{ ++ /**the url of the import rule*/ ++ CRString *url ; ++ ++ GList *media_list ; ++ ++ /** ++ *the stylesheet fetched from the url, if any. ++ *this is not "owned" by #CRAtImportRule which means ++ *it is not destroyed by the destructor of #CRAtImportRule. ++ */ ++ CRStyleSheet * sheet; ++}; ++ ++ ++/**abstraction of an \@media rule*/ ++struct _CRAtMediaRule ++{ ++ GList *media_list ; ++ CRStatement *rulesets ; ++} ; ++ ++ ++typedef struct _CRAtPageRule CRAtPageRule ; ++/**The \@page rule abstraction*/ ++struct _CRAtPageRule ++{ ++ /**a list of instances of #CRDeclaration*/ ++ CRDeclaration *decl_list ; ++ ++ /**page selector. Is a pseudo selector*/ ++ CRString *name ; ++ CRString *pseudo ; ++} ; ++ ++/**The \@charset rule abstraction*/ ++typedef struct _CRAtCharsetRule CRAtCharsetRule ; ++struct _CRAtCharsetRule ++{ ++ CRString * charset ; ++}; ++ ++/**The abstaction of the \@font-face rule.*/ ++typedef struct _CRAtFontFaceRule CRAtFontFaceRule ; ++struct _CRAtFontFaceRule ++{ ++ /*a list of instanaces of #CRDeclaration*/ ++ CRDeclaration *decl_list ; ++} ; ++ ++ ++/** ++ *The possible types of css2 statements. ++ */ ++enum CRStatementType ++{ ++ /** ++ *A generic css at-rule ++ *each unknown at-rule will ++ *be of this type. ++ */ ++ ++ /**A css at-rule*/ ++ AT_RULE_STMT = 0, ++ ++ /*A css ruleset*/ ++ RULESET_STMT, ++ ++ /**A css2 import rule*/ ++ AT_IMPORT_RULE_STMT, ++ ++ /**A css2 media rule*/ ++ AT_MEDIA_RULE_STMT, ++ ++ /**A css2 page rule*/ ++ AT_PAGE_RULE_STMT, ++ ++ /**A css2 charset rule*/ ++ AT_CHARSET_RULE_STMT, ++ ++ /**A css2 font face rule*/ ++ AT_FONT_FACE_RULE_STMT ++} ; ++ ++ ++/** ++ *The abstraction of css statement as defined ++ *in the chapter 4 and appendix D.1 of the css2 spec. ++ *A statement is actually a double chained list of ++ *statements.A statement can be a ruleset, an \@import ++ *rule, an \@page rule etc ... ++ */ ++struct _CRStatement ++{ ++ /** ++ *The type of the statement. ++ */ ++ enum CRStatementType type ; ++ ++ union ++ { ++ CRRuleSet *ruleset ; ++ CRAtImportRule *import_rule ; ++ CRAtMediaRule *media_rule ; ++ CRAtPageRule *page_rule ; ++ CRAtCharsetRule *charset_rule ; ++ CRAtFontFaceRule *font_face_rule ; ++ } kind ; ++ ++ /* ++ *the specificity of the selector ++ *that matched this statement. ++ *This is only used by the cascading ++ *order determination algorithm. ++ */ ++ gulong specificity ; ++ ++ /* ++ *the style sheet that contains ++ *this css statement. ++ */ ++ CRStyleSheet *parent_sheet ; ++ CRStatement *next ; ++ CRStatement *prev ; ++ ++ CRParsingLocation location ; ++ ++ /** ++ *a custom pointer useable by ++ *applications that use libcroco. ++ *libcroco itself will never modify ++ *this pointer. ++ */ ++ gpointer app_data ; ++ ++ /** ++ *a custom pointer used ++ *by the upper layers of libcroco. ++ *application should never use this ++ *pointer. ++ */ ++ gpointer croco_data ; ++ ++} ; ++ ++ ++gboolean ++cr_statement_does_buf_parses_against_core (const guchar *a_buf, ++ enum CREncoding a_encoding) ; ++CRStatement * ++cr_statement_parse_from_buf (const guchar *a_buf, ++ enum CREncoding a_encoding) ; ++CRStatement* ++cr_statement_new_ruleset (CRStyleSheet *a_sheet, ++ CRSelector *a_sel_list, ++ CRDeclaration *a_decl_list, ++ CRStatement *a_media_rule) ; ++CRStatement * ++cr_statement_ruleset_parse_from_buf (const guchar * a_buf, ++ enum CREncoding a_enc) ; ++ ++CRStatement* ++cr_statement_new_at_import_rule (CRStyleSheet *a_container_sheet, ++ CRString *a_url, ++ GList *a_media_list, ++ CRStyleSheet *a_imported_sheet) ; ++ ++CRStatement * ++cr_statement_at_import_rule_parse_from_buf (const guchar * a_buf, ++ enum CREncoding a_encoding) ; ++ ++CRStatement * ++cr_statement_new_at_media_rule (CRStyleSheet *a_sheet, ++ CRStatement *a_ruleset, ++ GList *a_media) ; ++CRStatement * ++cr_statement_at_media_rule_parse_from_buf (const guchar *a_buf, ++ enum CREncoding a_enc) ; ++ ++CRStatement * ++cr_statement_new_at_charset_rule (CRStyleSheet *a_sheet, ++ CRString *a_charset) ; ++CRStatement * ++cr_statement_at_charset_rule_parse_from_buf (const guchar *a_buf, ++ enum CREncoding a_encoding); ++ ++ ++CRStatement * ++cr_statement_new_at_font_face_rule (CRStyleSheet *a_sheet, ++ CRDeclaration *a_font_decls) ; ++CRStatement * ++cr_statement_font_face_rule_parse_from_buf (const guchar *a_buf, ++ enum CREncoding a_encoding) ; ++ ++CRStatement * ++cr_statement_new_at_page_rule (CRStyleSheet *a_sheet, ++ CRDeclaration *a_decl_list, ++ CRString *a_name, ++ CRString *a_pseudo) ; ++CRStatement * ++cr_statement_at_page_rule_parse_from_buf (const guchar *a_buf, ++ enum CREncoding a_encoding) ; ++ ++enum CRStatus ++cr_statement_set_parent_sheet (CRStatement *a_this, ++ CRStyleSheet *a_sheet) ; ++ ++enum CRStatus ++cr_statement_get_parent_sheet (CRStatement *a_this, ++ CRStyleSheet **a_sheet) ; ++ ++CRStatement * ++cr_statement_append (CRStatement *a_this, ++ CRStatement *a_new) ; ++ ++CRStatement* ++cr_statement_prepend (CRStatement *a_this, ++ CRStatement *a_new) ; ++ ++CRStatement * ++cr_statement_unlink (CRStatement *a_stmt) ; ++ ++enum CRStatus ++cr_statement_ruleset_set_sel_list (CRStatement *a_this, ++ CRSelector *a_sel_list) ; ++ ++enum CRStatus ++cr_statement_ruleset_get_sel_list (CRStatement const *a_this, ++ CRSelector **a_list) ; ++ ++enum CRStatus ++cr_statement_ruleset_set_decl_list (CRStatement *a_this, ++ CRDeclaration *a_list) ; ++ ++enum CRStatus ++cr_statement_ruleset_get_declarations (CRStatement *a_this, ++ CRDeclaration **a_decl_list) ; ++ ++enum CRStatus ++cr_statement_ruleset_append_decl2 (CRStatement *a_this, ++ CRString *a_prop, CRTerm *a_value) ; ++ ++enum CRStatus ++cr_statement_ruleset_append_decl (CRStatement *a_this, ++ CRDeclaration *a_decl) ; ++ ++enum CRStatus ++cr_statement_at_import_rule_set_imported_sheet (CRStatement *a_this, ++ CRStyleSheet *a_sheet) ; ++ ++enum CRStatus ++cr_statement_at_import_rule_get_imported_sheet (CRStatement *a_this, ++ CRStyleSheet **a_sheet) ; ++ ++enum CRStatus ++cr_statement_at_import_rule_set_url (CRStatement *a_this, ++ CRString *a_url) ; ++ ++enum CRStatus ++cr_statement_at_import_rule_get_url (CRStatement const *a_this, ++ CRString **a_url) ; ++ ++gint ++cr_statement_at_media_nr_rules (CRStatement const *a_this) ; ++ ++CRStatement * ++cr_statement_at_media_get_from_list (CRStatement *a_this, int itemnr) ; ++ ++enum CRStatus ++cr_statement_at_page_rule_set_sel (CRStatement *a_this, ++ CRSelector *a_sel) ; ++ ++enum CRStatus ++cr_statement_at_page_rule_get_sel (CRStatement const *a_this, ++ CRSelector **a_sel) ; ++ ++enum CRStatus ++cr_statement_at_page_rule_set_declarations (CRStatement *a_this, ++ CRDeclaration *a_decl_list) ; ++ ++enum CRStatus ++cr_statement_at_page_rule_get_declarations (CRStatement *a_this, ++ CRDeclaration **a_decl_list) ; ++ ++enum CRStatus ++cr_statement_at_charset_rule_set_charset (CRStatement *a_this, ++ CRString *a_charset) ; ++ ++enum CRStatus ++cr_statement_at_charset_rule_get_charset (CRStatement const *a_this, ++ CRString **a_charset) ; ++ ++enum CRStatus ++cr_statement_at_font_face_rule_set_decls (CRStatement *a_this, ++ CRDeclaration *a_decls) ; ++ ++enum CRStatus ++cr_statement_at_font_face_rule_get_decls (CRStatement *a_this, ++ CRDeclaration **a_decls) ; ++ ++enum CRStatus ++cr_statement_at_font_face_rule_add_decl (CRStatement *a_this, ++ CRString *a_prop, ++ CRTerm *a_value) ; ++ ++gchar * ++cr_statement_to_string (CRStatement const * a_this, gulong a_indent) ; ++ ++gchar* ++cr_statement_list_to_string (CRStatement const *a_this, gulong a_indent) ; ++ ++void ++cr_statement_dump (CRStatement const *a_this, FILE *a_fp, gulong a_indent) ; ++ ++void ++cr_statement_dump_ruleset (CRStatement const * a_this, FILE * a_fp, ++ glong a_indent) ; ++ ++void ++cr_statement_dump_font_face_rule (CRStatement const * a_this, ++ FILE * a_fp, ++ glong a_indent) ; ++ ++void ++cr_statement_dump_page (CRStatement const * a_this, FILE * a_fp, ++ gulong a_indent) ; ++ ++ ++void ++cr_statement_dump_media_rule (CRStatement const * a_this, ++ FILE * a_fp, ++ gulong a_indent) ; ++ ++void ++cr_statement_dump_import_rule (CRStatement const * a_this, FILE * a_fp, ++ gulong a_indent) ; ++void ++cr_statement_dump_charset (CRStatement const * a_this, FILE * a_fp, ++ gulong a_indent) ; ++gint ++cr_statement_nr_rules (CRStatement const *a_this) ; ++ ++CRStatement * ++cr_statement_get_from_list (CRStatement *a_this, int itemnr) ; ++ ++void ++cr_statement_destroy (CRStatement *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_STATEMENT_H__*/ +diff --git a/src/st/croco/cr-string.c b/src/st/croco/cr-string.c +new file mode 100644 +index 000000000..1b10bb2ab +--- /dev/null ++++ b/src/st/croco/cr-string.c +@@ -0,0 +1,168 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli. ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#include ++#include "cr-string.h" ++ ++/** ++ *Instanciates a #CRString ++ *@return the newly instanciated #CRString ++ *Must be freed with cr_string_destroy(). ++ */ ++CRString * ++cr_string_new (void) ++{ ++ CRString *result = NULL ; ++ ++ result = g_try_malloc (sizeof (CRString)) ; ++ if (!result) { ++ cr_utils_trace_info ("Out of memory") ; ++ return NULL ; ++ } ++ memset (result, 0, sizeof (CRString)) ; ++ result->stryng = g_string_new (NULL) ; ++ return result ; ++} ++ ++/** ++ *Instanciate a string and initialise it to ++ *a_string. ++ *@param a_string the initial string ++ *@return the newly instanciated string. ++ */ ++CRString * ++cr_string_new_from_string (const gchar * a_string) ++{ ++ CRString *result = NULL ; ++ ++ result = cr_string_new () ; ++ if (!result) { ++ cr_utils_trace_info ("Out of memory") ; ++ return NULL ; ++ } ++ if (a_string) ++ g_string_append (result->stryng, a_string) ; ++ return result ; ++} ++ ++/** ++ *Instanciates a #CRString from an instance of GString. ++ *@param a_string the input string that will be copied into ++ *the newly instanciated #CRString ++ *@return the newly instanciated #CRString. ++ */ ++CRString * ++cr_string_new_from_gstring (GString const *a_string) ++{ ++ CRString *result = NULL ; ++ ++ result = cr_string_new () ; ++ if (!result) { ++ cr_utils_trace_info ("Out of memory") ; ++ return NULL ; ++ } ++ if (a_string) { ++ g_string_append_len (result->stryng, ++ a_string->str, ++ a_string->len); ++ ++ } ++ return result ; ++} ++ ++CRString * ++cr_string_dup (CRString const *a_this) ++{ ++ CRString *result = NULL ; ++ g_return_val_if_fail (a_this, NULL) ; ++ ++ result = cr_string_new_from_gstring (a_this->stryng) ; ++ if (!result) { ++ cr_utils_trace_info ("Out of memory") ; ++ return NULL ; ++ } ++ cr_parsing_location_copy (&result->location, ++ &a_this->location) ; ++ return result ; ++} ++ ++gchar * ++cr_string_dup2 (CRString const *a_this) ++{ ++ gchar *result = NULL ; ++ ++ g_return_val_if_fail (a_this, NULL) ; ++ ++ if (a_this ++ && a_this->stryng ++ && a_this->stryng->str) { ++ result = g_strndup (a_this->stryng->str, ++ a_this->stryng->len) ; ++ } ++ return result ; ++} ++ ++/** ++ *Returns a pointer to the internal raw NULL terminated string ++ *of the current instance of #CRString. ++ *@param a_this the current instance of #CRString ++ */ ++const gchar * ++cr_string_peek_raw_str (CRString const *a_this) ++{ ++ g_return_val_if_fail (a_this, NULL) ; ++ ++ if (a_this->stryng && a_this->stryng->str) ++ return a_this->stryng->str ; ++ return NULL ; ++} ++ ++/** ++ *Returns the length of the internal raw NULL terminated ++ *string of the current instance of #CRString. ++ *@param a_this the current instance of #CRString. ++ *@return the len of the internal raw NULL termninated string, ++ *of -1 if no length can be returned. ++ */ ++gint ++cr_string_peek_raw_str_len (CRString const *a_this) ++{ ++ g_return_val_if_fail (a_this && a_this->stryng, ++ -1) ; ++ return a_this->stryng->len ; ++} ++ ++/** ++ *@param a_this the #CRString to destroy. ++ */ ++void ++cr_string_destroy (CRString *a_this) ++{ ++ g_return_if_fail (a_this) ; ++ ++ if (a_this->stryng) { ++ g_string_free (a_this->stryng, TRUE) ; ++ a_this->stryng = NULL ; ++ } ++ g_free (a_this) ; ++} +diff --git a/src/st/croco/cr-string.h b/src/st/croco/cr-string.h +new file mode 100644 +index 000000000..2700f0e2e +--- /dev/null ++++ b/src/st/croco/cr-string.h +@@ -0,0 +1,76 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++/** ++ *@file ++ *Declaration file of the #CRString class. ++ */ ++ ++#ifndef __CR_STRING_H__ ++#define __CR_STRING_H__ ++ ++#include ++#include "cr-utils.h" ++#include "cr-parsing-location.h" ++ ++G_BEGIN_DECLS ++ ++typedef struct _CRString CRString ; ++ ++/** ++ *This is a ship implementation of string based on GString. ++ *Actually, the aim of CRString is to store the parsing location ++ *(line,column,byte offset) at which a given string has been parsed ++ *in the input CSS. ++ *So this class has a gstring field of type GString that users can ++ *freely manipulate, and also a CRParginLocation type where the ++ *parsing location is store. If you don't want to deal with parsing ++ *location stuffs, then use GString instead. If we were in C++ for example, ++ *CRString would just inherit GString and just add accessors to ++ *the CRParsingLocation data ... but we are not and we still have ++ *to provide the parsing location information. ++ */ ++struct _CRString { ++ /** ++ *The GString where all the string ++ *operation happen. ++ */ ++ GString *stryng ; ++ /** ++ *The parsing location storage area. ++ */ ++ CRParsingLocation location ; ++} ; ++ ++CRString * cr_string_new (void) ; ++ ++CRString *cr_string_new_from_string (const gchar * a_string) ; ++CRString * cr_string_new_from_gstring (GString const *a_string) ; ++CRString *cr_string_dup (CRString const *a_this) ; ++gchar *cr_string_dup2 (CRString const *a_this) ; ++const gchar *cr_string_peek_raw_str (CRString const *a_this) ; ++gint cr_string_peek_raw_str_len (CRString const *a_this) ; ++void cr_string_destroy (CRString *a_this) ; ++ ++G_END_DECLS ++ ++#endif +diff --git a/src/st/croco/cr-stylesheet.c b/src/st/croco/cr-stylesheet.c +new file mode 100644 +index 000000000..69909da24 +--- /dev/null ++++ b/src/st/croco/cr-stylesheet.c +@@ -0,0 +1,178 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * Copyright (C) 2002-2004 Dodji Seketeli ++ * ++ * 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 ++ */ ++ ++#include "string.h" ++#include "cr-stylesheet.h" ++ ++/** ++ *@file ++ *The definition of the #CRStyleSheet class ++ */ ++ ++/** ++ *Constructor of the #CRStyleSheet class. ++ *@param the initial list of css statements. ++ *@return the newly built css2 stylesheet, or NULL in case of error. ++ */ ++CRStyleSheet * ++cr_stylesheet_new (CRStatement * a_stmts) ++{ ++ CRStyleSheet *result; ++ ++ result = g_try_malloc (sizeof (CRStyleSheet)); ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRStyleSheet)); ++ ++ if (a_stmts) ++ result->statements = a_stmts; ++ ++ return result; ++} ++ ++/** ++ *@param a_this the current instance of #CRStyleSheet ++ *@return the serialized stylesheet. ++ */ ++gchar * ++cr_stylesheet_to_string (CRStyleSheet const *a_this) ++{ ++ gchar *str = NULL; ++ GString *stringue = NULL; ++ CRStatement const *cur_stmt = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ if (a_this->statements) { ++ stringue = g_string_new (NULL) ; ++ g_return_val_if_fail (stringue, NULL) ; ++ } ++ for (cur_stmt = a_this->statements; ++ cur_stmt; cur_stmt = cur_stmt->next) { ++ if (cur_stmt->prev) { ++ g_string_append (stringue, "\n\n") ; ++ } ++ str = cr_statement_to_string (cur_stmt, 0) ; ++ if (str) { ++ g_string_append (stringue, str) ; ++ g_free (str) ; ++ str = NULL ; ++ } ++ } ++ if (stringue) { ++ str = stringue->str ; ++ g_string_free (stringue, FALSE) ; ++ stringue = NULL ; ++ } ++ return str ; ++} ++ ++/** ++ *Dumps the current css2 stylesheet to a file. ++ *@param a_this the current instance of #CRStyleSheet. ++ *@param a_fp the destination file ++ */ ++void ++cr_stylesheet_dump (CRStyleSheet const * a_this, FILE * a_fp) ++{ ++ gchar *str = NULL ; ++ ++ g_return_if_fail (a_this); ++ ++ str = cr_stylesheet_to_string (a_this) ; ++ if (str) { ++ fprintf (a_fp, "%s", str) ; ++ g_free (str) ; ++ str = NULL ; ++ } ++} ++ ++/** ++ *Return the number of rules in the stylesheet. ++ *@param a_this the current instance of #CRStyleSheet. ++ *@return number of rules in the stylesheet. ++ */ ++gint ++cr_stylesheet_nr_rules (CRStyleSheet const * a_this) ++{ ++ g_return_val_if_fail (a_this, -1); ++ ++ return cr_statement_nr_rules (a_this->statements); ++} ++ ++/** ++ *Use an index to get a CRStatement from the rules in a given stylesheet. ++ *@param a_this the current instance of #CRStatement. ++ *@param itemnr the index into the rules. ++ *@return CRStatement at position itemnr, if itemnr > number of rules - 1, ++ *it will return NULL. ++ */ ++CRStatement * ++cr_stylesheet_statement_get_from_list (CRStyleSheet * a_this, int itemnr) ++{ ++ g_return_val_if_fail (a_this, NULL); ++ ++ return cr_statement_get_from_list (a_this->statements, itemnr); ++} ++ ++void ++cr_stylesheet_ref (CRStyleSheet * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ a_this->ref_count++; ++} ++ ++gboolean ++cr_stylesheet_unref (CRStyleSheet * a_this) ++{ ++ g_return_val_if_fail (a_this, FALSE); ++ ++ if (a_this->ref_count) ++ a_this->ref_count--; ++ ++ if (!a_this->ref_count) { ++ cr_stylesheet_destroy (a_this); ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++/** ++ *Destructor of the #CRStyleSheet class. ++ *@param a_this the current instance of the #CRStyleSheet class. ++ */ ++void ++cr_stylesheet_destroy (CRStyleSheet * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ if (a_this->statements) { ++ cr_statement_destroy (a_this->statements); ++ a_this->statements = NULL; ++ } ++ g_free (a_this); ++} +diff --git a/src/st/croco/cr-stylesheet.h b/src/st/croco/cr-stylesheet.h +new file mode 100644 +index 000000000..f35c94e37 +--- /dev/null ++++ b/src/st/croco/cr-stylesheet.h +@@ -0,0 +1,102 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * see COPYRIGHTS file for copyright information. ++ */ ++ ++ ++#ifndef __CR_STYLESHEET_H__ ++#define __CR_STYLESHEET_H__ ++ ++#include "cr-utils.h" ++#include "cr-statement.h" ++ ++G_BEGIN_DECLS ++ ++/** ++ *@file ++ *The declaration of the #CRStyleSheet class. ++ */ ++ ++ ++enum CRStyleOrigin ++{ ++ /*Please don't change the order of ++ *the values enumerated here ... ++ *New values should be added at the end, ++ *just before ORIGIN_END. ++ */ ++ ORIGIN_UA = 0, ++ ORIGIN_USER, ++ ORIGIN_AUTHOR, ++ ++ /*must always be the last one*/ ++ NB_ORIGINS ++} ; ++ ++/** ++ *An abstraction of a css stylesheet as defined ++ *by the css2 spec in chapter 4. ++ */ ++struct _CRStyleSheet ++{ ++ /**The css statements list*/ ++ CRStatement *statements ; ++ ++ enum CRStyleOrigin origin ; ++ ++ /*the parent import rule, if any.*/ ++ CRStatement *parent_import_rule ; ++ ++ /**custom data used by libcroco*/ ++ gpointer croco_data ; ++ ++ /** ++ *custom application data pointer ++ *Can be used by applications. ++ */ ++ gpointer app_data ; ++ ++ /** ++ *the reference count of this insance ++ *Please, don't never ever modify it ++ *directly. Use cr_stylesheet_ref() ++ *and cr_stylesheet_unref() instead. ++ */ ++ gulong ref_count ; ++} ; ++ ++CRStyleSheet * cr_stylesheet_new (CRStatement *a_stmts) ; ++ ++gchar * cr_stylesheet_to_string (CRStyleSheet const *a_this) ; ++void cr_stylesheet_dump (CRStyleSheet const *a_this, FILE *a_fp) ; ++ ++gint cr_stylesheet_nr_rules (CRStyleSheet const *a_this) ; ++ ++CRStatement * cr_stylesheet_statement_get_from_list (CRStyleSheet *a_this, int itemnr) ; ++ ++void cr_stylesheet_ref (CRStyleSheet *a_this) ; ++ ++gboolean cr_stylesheet_unref (CRStyleSheet *a_this) ; ++ ++void cr_stylesheet_destroy (CRStyleSheet *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_STYLESHEET_H__*/ +diff --git a/src/st/croco/cr-term.c b/src/st/croco/cr-term.c +new file mode 100644 +index 000000000..9ffe6727b +--- /dev/null ++++ b/src/st/croco/cr-term.c +@@ -0,0 +1,790 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#include ++#include ++#include "cr-term.h" ++#include "cr-num.h" ++#include "cr-parser.h" ++ ++/** ++ *@file ++ *Definition of the #CRTem class. ++ */ ++ ++static void ++cr_term_clear (CRTerm * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ switch (a_this->type) { ++ case TERM_NUMBER: ++ if (a_this->content.num) { ++ cr_num_destroy (a_this->content.num); ++ a_this->content.num = NULL; ++ } ++ break; ++ ++ case TERM_FUNCTION: ++ if (a_this->ext_content.func_param) { ++ cr_term_destroy (a_this->ext_content.func_param); ++ a_this->ext_content.func_param = NULL; ++ } ++ case TERM_STRING: ++ case TERM_IDENT: ++ case TERM_URI: ++ case TERM_HASH: ++ if (a_this->content.str) { ++ cr_string_destroy (a_this->content.str); ++ a_this->content.str = NULL; ++ } ++ break; ++ ++ case TERM_RGB: ++ if (a_this->content.rgb) { ++ cr_rgb_destroy (a_this->content.rgb); ++ a_this->content.rgb = NULL; ++ } ++ break; ++ ++ case TERM_UNICODERANGE: ++ case TERM_NO_TYPE: ++ default: ++ break; ++ } ++ ++ a_this->type = TERM_NO_TYPE; ++} ++ ++/** ++ *Instanciate a #CRTerm. ++ *@return the newly build instance ++ *of #CRTerm. ++ */ ++CRTerm * ++cr_term_new (void) ++{ ++ CRTerm *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRTerm)); ++ if (!result) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ memset (result, 0, sizeof (CRTerm)); ++ return result; ++} ++ ++/** ++ *Parses an expresion as defined by the css2 spec ++ *and builds the expression as a list of terms. ++ *@param a_buf the buffer to parse. ++ *@return a pointer to the first term of the expression or ++ *NULL if parsing failed. ++ */ ++CRTerm * ++cr_term_parse_expression_from_buf (const guchar * a_buf, ++ enum CREncoding a_encoding) ++{ ++ CRParser *parser = NULL; ++ CRTerm *result = NULL; ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_buf, NULL); ++ ++ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf), ++ a_encoding, FALSE); ++ g_return_val_if_fail (parser, NULL); ++ ++ status = cr_parser_try_to_skip_spaces_and_comments (parser); ++ if (status != CR_OK) { ++ goto cleanup; ++ } ++ status = cr_parser_parse_expr (parser, &result); ++ if (status != CR_OK) { ++ if (result) { ++ cr_term_destroy (result); ++ result = NULL; ++ } ++ } ++ ++ cleanup: ++ if (parser) { ++ cr_parser_destroy (parser); ++ parser = NULL; ++ } ++ ++ return result; ++} ++ ++enum CRStatus ++cr_term_set_number (CRTerm * a_this, CRNum * a_num) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_term_clear (a_this); ++ ++ a_this->type = TERM_NUMBER; ++ a_this->content.num = a_num; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_term_set_function (CRTerm * a_this, CRString * a_func_name, ++ CRTerm * a_func_param) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_term_clear (a_this); ++ ++ a_this->type = TERM_FUNCTION; ++ a_this->content.str = a_func_name; ++ a_this->ext_content.func_param = a_func_param; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_term_set_string (CRTerm * a_this, CRString * a_str) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_term_clear (a_this); ++ ++ a_this->type = TERM_STRING; ++ a_this->content.str = a_str; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_term_set_ident (CRTerm * a_this, CRString * a_str) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_term_clear (a_this); ++ ++ a_this->type = TERM_IDENT; ++ a_this->content.str = a_str; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_term_set_uri (CRTerm * a_this, CRString * a_str) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_term_clear (a_this); ++ ++ a_this->type = TERM_URI; ++ a_this->content.str = a_str; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_term_set_rgb (CRTerm * a_this, CRRgb * a_rgb) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_term_clear (a_this); ++ ++ a_this->type = TERM_RGB; ++ a_this->content.rgb = a_rgb; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_term_set_hash (CRTerm * a_this, CRString * a_str) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_term_clear (a_this); ++ ++ a_this->type = TERM_HASH; ++ a_this->content.str = a_str; ++ return CR_OK; ++} ++ ++/** ++ *Appends a new term to the current list of #CRTerm. ++ * ++ *@param a_this the "this pointer" of the current instance ++ *of #CRTerm . ++ *@param a_new_term the term to append. ++ *@return the list of terms with the a_new_term appended to it. ++ */ ++CRTerm * ++cr_term_append_term (CRTerm * a_this, CRTerm * a_new_term) ++{ ++ CRTerm *cur = NULL; ++ ++ g_return_val_if_fail (a_new_term, NULL); ++ ++ if (a_this == NULL) ++ return a_new_term; ++ ++ for (cur = a_this; cur->next; cur = cur->next) ; ++ ++ cur->next = a_new_term; ++ a_new_term->prev = cur; ++ ++ return a_this; ++} ++ ++/** ++ *Prepends a term to the list of terms represented by a_this. ++ * ++ *@param a_this the "this pointer" of the current instance of ++ *#CRTerm . ++ *@param a_new_term the term to prepend. ++ *@return the head of the new list. ++ */ ++CRTerm * ++cr_term_prepend_term (CRTerm * a_this, CRTerm * a_new_term) ++{ ++ g_return_val_if_fail (a_this && a_new_term, NULL); ++ ++ a_new_term->next = a_this; ++ a_this->prev = a_new_term; ++ ++ return a_new_term; ++} ++ ++/** ++ *Serializes the expression represented by ++ *the chained instances of #CRterm. ++ *@param a_this the current instance of #CRTerm ++ *@return the zero terminated string containing the serialized ++ *form of #CRTerm. MUST BE FREED BY THE CALLER using g_free(). ++ */ ++guchar * ++cr_term_to_string (CRTerm const * a_this) ++{ ++ GString *str_buf = NULL; ++ CRTerm const *cur = NULL; ++ guchar *result = NULL, ++ *content = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ str_buf = g_string_new (NULL); ++ g_return_val_if_fail (str_buf, NULL); ++ ++ for (cur = a_this; cur; cur = cur->next) { ++ if ((cur->content.str == NULL) ++ && (cur->content.num == NULL) ++ && (cur->content.str == NULL) ++ && (cur->content.rgb == NULL)) ++ continue; ++ ++ switch (cur->the_operator) { ++ case DIVIDE: ++ g_string_append (str_buf, " / "); ++ break; ++ ++ case COMMA: ++ g_string_append (str_buf, ", "); ++ break; ++ ++ case NO_OP: ++ if (cur->prev) { ++ g_string_append (str_buf, " "); ++ } ++ break; ++ default: ++ ++ break; ++ } ++ ++ switch (cur->unary_op) { ++ case PLUS_UOP: ++ g_string_append (str_buf, "+"); ++ break; ++ ++ case MINUS_UOP: ++ g_string_append (str_buf, "-"); ++ break; ++ ++ default: ++ break; ++ } ++ ++ switch (cur->type) { ++ case TERM_NUMBER: ++ if (cur->content.num) { ++ content = cr_num_to_string (cur->content.num); ++ } ++ ++ if (content) { ++ g_string_append (str_buf, (const gchar *) content); ++ g_free (content); ++ content = NULL; ++ } ++ ++ break; ++ ++ case TERM_FUNCTION: ++ if (cur->content.str) { ++ content = (guchar *) g_strndup ++ (cur->content.str->stryng->str, ++ cur->content.str->stryng->len); ++ } ++ ++ if (content) { ++ g_string_append_printf (str_buf, "%s(", ++ content); ++ ++ if (cur->ext_content.func_param) { ++ guchar *tmp_str = NULL; ++ ++ tmp_str = cr_term_to_string ++ (cur-> ++ ext_content.func_param); ++ ++ if (tmp_str) { ++ g_string_append (str_buf, ++ (const gchar *) tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ g_string_append (str_buf, ")"); ++ g_free (content); ++ content = NULL; ++ } ++ ++ break; ++ ++ case TERM_STRING: ++ if (cur->content.str) { ++ content = (guchar *) g_strndup ++ (cur->content.str->stryng->str, ++ cur->content.str->stryng->len); ++ } ++ ++ if (content) { ++ g_string_append_printf (str_buf, ++ "\"%s\"", content); ++ g_free (content); ++ content = NULL; ++ } ++ break; ++ ++ case TERM_IDENT: ++ if (cur->content.str) { ++ content = (guchar *) g_strndup ++ (cur->content.str->stryng->str, ++ cur->content.str->stryng->len); ++ } ++ ++ if (content) { ++ g_string_append (str_buf, (const gchar *) content); ++ g_free (content); ++ content = NULL; ++ } ++ break; ++ ++ case TERM_URI: ++ if (cur->content.str) { ++ content = (guchar *) g_strndup ++ (cur->content.str->stryng->str, ++ cur->content.str->stryng->len); ++ } ++ ++ if (content) { ++ g_string_append_printf ++ (str_buf, "url(%s)", content); ++ g_free (content); ++ content = NULL; ++ } ++ break; ++ ++ case TERM_RGB: ++ if (cur->content.rgb) { ++ guchar *tmp_str = NULL; ++ ++ g_string_append (str_buf, "rgb("); ++ tmp_str = cr_rgb_to_string (cur->content.rgb); ++ ++ if (tmp_str) { ++ g_string_append (str_buf, (const gchar *) tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ g_string_append (str_buf, ")"); ++ } ++ ++ break; ++ ++ case TERM_UNICODERANGE: ++ g_string_append ++ (str_buf, ++ "?found unicoderange: dump not supported yet?"); ++ break; ++ ++ case TERM_HASH: ++ if (cur->content.str) { ++ content = (guchar *) g_strndup ++ (cur->content.str->stryng->str, ++ cur->content.str->stryng->len); ++ } ++ ++ if (content) { ++ g_string_append_printf (str_buf, ++ "#%s", content); ++ g_free (content); ++ content = NULL; ++ } ++ break; ++ ++ default: ++ g_string_append (str_buf, ++ "Unrecognized Term type"); ++ break; ++ } ++ } ++ ++ if (str_buf) { ++ result =(guchar *) str_buf->str; ++ g_string_free (str_buf, FALSE); ++ str_buf = NULL; ++ } ++ ++ return result; ++} ++ ++guchar * ++cr_term_one_to_string (CRTerm const * a_this) ++{ ++ GString *str_buf = NULL; ++ guchar *result = NULL, ++ *content = NULL; ++ ++ g_return_val_if_fail (a_this, NULL); ++ ++ str_buf = g_string_new (NULL); ++ g_return_val_if_fail (str_buf, NULL); ++ ++ if ((a_this->content.str == NULL) ++ && (a_this->content.num == NULL) ++ && (a_this->content.str == NULL) ++ && (a_this->content.rgb == NULL)) ++ return NULL ; ++ ++ switch (a_this->the_operator) { ++ case DIVIDE: ++ g_string_append_printf (str_buf, " / "); ++ break; ++ ++ case COMMA: ++ g_string_append_printf (str_buf, ", "); ++ break; ++ ++ case NO_OP: ++ if (a_this->prev) { ++ g_string_append_printf (str_buf, " "); ++ } ++ break; ++ default: ++ ++ break; ++ } ++ ++ switch (a_this->unary_op) { ++ case PLUS_UOP: ++ g_string_append_printf (str_buf, "+"); ++ break; ++ ++ case MINUS_UOP: ++ g_string_append_printf (str_buf, "-"); ++ break; ++ ++ default: ++ break; ++ } ++ ++ switch (a_this->type) { ++ case TERM_NUMBER: ++ if (a_this->content.num) { ++ content = cr_num_to_string (a_this->content.num); ++ } ++ ++ if (content) { ++ g_string_append (str_buf, (const gchar *) content); ++ g_free (content); ++ content = NULL; ++ } ++ ++ break; ++ ++ case TERM_FUNCTION: ++ if (a_this->content.str) { ++ content = (guchar *) g_strndup ++ (a_this->content.str->stryng->str, ++ a_this->content.str->stryng->len); ++ } ++ ++ if (content) { ++ g_string_append_printf (str_buf, "%s(", ++ content); ++ ++ if (a_this->ext_content.func_param) { ++ guchar *tmp_str = NULL; ++ ++ tmp_str = cr_term_to_string ++ (a_this-> ++ ext_content.func_param); ++ ++ if (tmp_str) { ++ g_string_append_printf ++ (str_buf, ++ "%s", tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ ++ g_string_append_printf (str_buf, ")"); ++ g_free (content); ++ content = NULL; ++ } ++ } ++ ++ break; ++ ++ case TERM_STRING: ++ if (a_this->content.str) { ++ content = (guchar *) g_strndup ++ (a_this->content.str->stryng->str, ++ a_this->content.str->stryng->len); ++ } ++ ++ if (content) { ++ g_string_append_printf (str_buf, ++ "\"%s\"", content); ++ g_free (content); ++ content = NULL; ++ } ++ break; ++ ++ case TERM_IDENT: ++ if (a_this->content.str) { ++ content = (guchar *) g_strndup ++ (a_this->content.str->stryng->str, ++ a_this->content.str->stryng->len); ++ } ++ ++ if (content) { ++ g_string_append (str_buf, (const gchar *) content); ++ g_free (content); ++ content = NULL; ++ } ++ break; ++ ++ case TERM_URI: ++ if (a_this->content.str) { ++ content = (guchar *) g_strndup ++ (a_this->content.str->stryng->str, ++ a_this->content.str->stryng->len); ++ } ++ ++ if (content) { ++ g_string_append_printf ++ (str_buf, "url(%s)", content); ++ g_free (content); ++ content = NULL; ++ } ++ break; ++ ++ case TERM_RGB: ++ if (a_this->content.rgb) { ++ guchar *tmp_str = NULL; ++ ++ g_string_append_printf (str_buf, "rgb("); ++ tmp_str = cr_rgb_to_string (a_this->content.rgb); ++ ++ if (tmp_str) { ++ g_string_append (str_buf, (const gchar *) tmp_str); ++ g_free (tmp_str); ++ tmp_str = NULL; ++ } ++ g_string_append_printf (str_buf, ")"); ++ } ++ ++ break; ++ ++ case TERM_UNICODERANGE: ++ g_string_append_printf ++ (str_buf, ++ "?found unicoderange: dump not supported yet?"); ++ break; ++ ++ case TERM_HASH: ++ if (a_this->content.str) { ++ content = (guchar *) g_strndup ++ (a_this->content.str->stryng->str, ++ a_this->content.str->stryng->len); ++ } ++ ++ if (content) { ++ g_string_append_printf (str_buf, ++ "#%s", content); ++ g_free (content); ++ content = NULL; ++ } ++ break; ++ ++ default: ++ g_string_append_printf (str_buf, ++ "%s", ++ "Unrecognized Term type"); ++ break; ++ } ++ ++ if (str_buf) { ++ result = (guchar *) str_buf->str; ++ g_string_free (str_buf, FALSE); ++ str_buf = NULL; ++ } ++ ++ return result; ++} ++ ++/** ++ *Dumps the expression (a list of terms connected by operators) ++ *to a file. ++ *TODO: finish the dump. The dump of some type of terms have not yet been ++ *implemented. ++ *@param a_this the current instance of #CRTerm. ++ *@param a_fp the destination file pointer. ++ */ ++void ++cr_term_dump (CRTerm const * a_this, FILE * a_fp) ++{ ++ guchar *content = NULL; ++ ++ g_return_if_fail (a_this); ++ ++ content = cr_term_to_string (a_this); ++ ++ if (content) { ++ fprintf (a_fp, "%s", content); ++ g_free (content); ++ } ++} ++ ++/** ++ *Return the number of terms in the expression. ++ *@param a_this the current instance of #CRTerm. ++ *@return number of terms in the expression. ++ */ ++int ++cr_term_nr_values (CRTerm const *a_this) ++{ ++ CRTerm const *cur = NULL ; ++ int nr = 0; ++ ++ g_return_val_if_fail (a_this, -1) ; ++ ++ for (cur = a_this ; cur ; cur = cur->next) ++ nr ++; ++ return nr; ++} ++ ++/** ++ *Use an index to get a CRTerm from the expression. ++ *@param a_this the current instance of #CRTerm. ++ *@param itemnr the index into the expression. ++ *@return CRTerm at position itemnr, if itemnr > number of terms - 1, ++ *it will return NULL. ++ */ ++CRTerm * ++cr_term_get_from_list (CRTerm *a_this, int itemnr) ++{ ++ CRTerm *cur = NULL ; ++ int nr = 0; ++ ++ g_return_val_if_fail (a_this, NULL) ; ++ ++ for (cur = a_this ; cur ; cur = cur->next) ++ if (nr++ == itemnr) ++ return cur; ++ return NULL; ++} ++ ++/** ++ *Increments the reference counter of the current instance ++ *of #CRTerm.* ++ *@param a_this the current instance of #CRTerm. ++ */ ++void ++cr_term_ref (CRTerm * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ a_this->ref_count++; ++} ++ ++/** ++ *Decrements the ref count of the current instance of ++ *#CRTerm. If the ref count reaches zero, the instance is ++ *destroyed. ++ *@param a_this the current instance of #CRTerm. ++ *@return TRUE if the current instance has been destroyed, FALSE otherwise. ++ */ ++gboolean ++cr_term_unref (CRTerm * a_this) ++{ ++ g_return_val_if_fail (a_this, FALSE); ++ ++ if (a_this->ref_count) { ++ a_this->ref_count--; ++ } ++ ++ if (a_this->ref_count == 0) { ++ cr_term_destroy (a_this); ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++/** ++ *The destructor of the the #CRTerm class. ++ *@param a_this the "this pointer" of the current instance ++ *of #CRTerm. ++ */ ++void ++cr_term_destroy (CRTerm * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ cr_term_clear (a_this); ++ ++ if (a_this->next) { ++ cr_term_destroy (a_this->next); ++ a_this->next = NULL; ++ } ++ ++ if (a_this) { ++ g_free (a_this); ++ } ++ ++} +diff --git a/src/st/croco/cr-term.h b/src/st/croco/cr-term.h +new file mode 100644 +index 000000000..0f22dda75 +--- /dev/null ++++ b/src/st/croco/cr-term.h +@@ -0,0 +1,190 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#include ++#include ++#include "cr-utils.h" ++#include "cr-rgb.h" ++#include "cr-num.h" ++#include "cr-string.h" ++ ++#ifndef __CR_TERM_H__ ++#define __CR_TERM_H__ ++ ++G_BEGIN_DECLS ++ ++/** ++ *@file ++ *Declaration of the #CRTem class. ++ */ ++ ++enum CRTermType ++{ ++ TERM_NO_TYPE = 0, ++ TERM_NUMBER, ++ TERM_FUNCTION, ++ TERM_STRING, ++ TERM_IDENT, ++ TERM_URI, ++ TERM_RGB, ++ TERM_UNICODERANGE, ++ TERM_HASH ++} ; ++ ++ ++enum UnaryOperator ++{ ++ NO_UNARY_UOP = 0, ++ PLUS_UOP, ++ MINUS_UOP, ++ EMPTY_UNARY_UOP ++} ; ++ ++enum Operator ++{ ++ NO_OP = 0, ++ DIVIDE, ++ COMMA ++} ; ++ ++struct _CRTerm ; ++typedef struct _CRTerm CRTerm ; ++ ++/** ++ *An abstraction of a css2 term as ++ *defined in the CSS2 spec in appendix D.1: ++ *term ::= ++ *[ 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 ++ */ ++struct _CRTerm ++{ ++ /** ++ *The type of the term. ++ */ ++ enum CRTermType type ; ++ ++ /** ++ *The unary operator associated to ++ *the current term. ++ */ ++ enum UnaryOperator unary_op ; ++ ++ /** ++ *The operator associated to the current term. ++ */ ++ enum Operator the_operator ; ++ ++ ++ /** ++ *The content of the term. ++ *Depending of the type of the term, ++ *this holds either a number, a percentage ... ++ */ ++ union ++ { ++ CRNum *num ; ++ CRString * str ; ++ CRRgb * rgb ; ++ } content ; ++ ++ /** ++ *If the term is of type UNICODERANGE, ++ *this field holds the upper bound of the range. ++ *if the term is of type FUNCTION, this holds ++ *an instance of CRTerm that represents ++ * the expression which is the argument of the function. ++ */ ++ union ++ { ++ CRTerm *func_param ; ++ } ext_content ; ++ ++ /** ++ *A spare pointer, just in case. ++ *Can be used by the application. ++ */ ++ gpointer app_data ; ++ ++ glong ref_count ; ++ ++ /** ++ *A pointer to the next term, ++ *just in case this term is part of ++ *an expression. ++ */ ++ CRTerm *next ; ++ ++ /** ++ *A pointer to the previous ++ *term. ++ */ ++ CRTerm *prev ; ++ CRParsingLocation location ; ++} ; ++ ++CRTerm * cr_term_parse_expression_from_buf (const guchar *a_buf, ++ enum CREncoding a_encoding) ; ++CRTerm * cr_term_new (void) ; ++ ++enum CRStatus cr_term_set_number (CRTerm *a_this, CRNum *a_num) ; ++ ++enum CRStatus cr_term_set_function (CRTerm *a_this, ++ CRString *a_func_name, ++ CRTerm *a_func_param) ; ++ ++enum CRStatus cr_term_set_string (CRTerm *a_this, CRString *a_str) ; ++ ++enum CRStatus cr_term_set_ident (CRTerm *a_this, CRString *a_str) ; ++ ++enum CRStatus cr_term_set_uri (CRTerm *a_this, CRString *a_str) ; ++ ++enum CRStatus cr_term_set_rgb (CRTerm *a_this, CRRgb *a_rgb) ; ++ ++enum CRStatus cr_term_set_hash (CRTerm *a_this, CRString *a_str) ; ++ ++CRTerm * cr_term_append_term (CRTerm *a_this, CRTerm *a_new_term) ; ++ ++CRTerm * cr_term_prepend_term (CRTerm *a_this, CRTerm *a_new_term) ; ++ ++guchar * cr_term_to_string (CRTerm const *a_this) ; ++ ++guchar * cr_term_one_to_string (CRTerm const * a_this) ; ++ ++void cr_term_dump (CRTerm const *a_this, FILE *a_fp) ; ++ ++int cr_term_nr_values (CRTerm const *a_this) ; ++ ++CRTerm * cr_term_get_from_list (CRTerm *a_this, int itemnr) ; ++ ++void cr_term_ref (CRTerm *a_this) ; ++ ++gboolean cr_term_unref (CRTerm *a_this) ; ++ ++void cr_term_destroy (CRTerm * a_term) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_TERM_H__*/ +diff --git a/src/st/croco/cr-tknzr.c b/src/st/croco/cr-tknzr.c +new file mode 100644 +index 000000000..1548c35c6 +--- /dev/null ++++ b/src/st/croco/cr-tknzr.c +@@ -0,0 +1,2762 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See the COPYRIGHTS file for copyrights information. ++ */ ++ ++/** ++ *@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) \ ++{ \ ++gulong 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, ++ CRParsingLocation *a_location); ++ ++static enum CRStatus cr_tknzr_parse_w (CRTknzr * a_this, ++ guchar ** a_start, ++ guchar ** a_end, ++ CRParsingLocation *a_location) ; ++ ++static enum CRStatus cr_tknzr_parse_unicode_escape (CRTknzr * a_this, ++ guint32 * a_unicode, ++ CRParsingLocation *a_location) ; ++ ++static enum CRStatus cr_tknzr_parse_escape (CRTknzr * a_this, ++ guint32 * a_esc_code, ++ CRParsingLocation *a_location); ++ ++static enum CRStatus cr_tknzr_parse_string (CRTknzr * a_this, ++ CRString ** a_str); ++ ++static enum CRStatus cr_tknzr_parse_comment (CRTknzr * a_this, ++ CRString ** a_comment); ++ ++static enum CRStatus cr_tknzr_parse_nmstart (CRTknzr * a_this, ++ guint32 * a_char, ++ CRParsingLocation *a_location); ++ ++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, ++ CRParsingLocation *a_location) ++{ ++ 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; ++ } ++ if (a_location) { ++ cr_tknzr_get_parsing_location (a_this, ++ a_location) ; ++ } ++ 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) { ++ 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, ++ CRParsingLocation *a_location) ++{ ++ 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); ++ if (a_location) { ++ cr_tknzr_get_parsing_location ++ (a_this, a_location) ; ++ } ++ 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); ++ if (a_location) { ++ cr_tknzr_get_parsing_location ++ (a_this, a_location) ; ++ } ++ 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) { ++ gulong 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, ++ CRString ** a_comment) ++{ ++ enum CRStatus status = CR_OK; ++ CRInputPos init_pos; ++ guint32 cur_char = 0, next_char= 0; ++ CRString *comment = NULL; ++ CRParsingLocation loc = {0} ; ++ ++ 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_get_parsing_location (a_this, &loc) ; ++ ++ READ_NEXT_CHAR (a_this, &cur_char); ++ ENSURE_PARSING_COND (cur_char == '*'); ++ comment = cr_string_new (); ++ for (;;) { /* [^*]* */ ++ PEEK_NEXT_CHAR (a_this, &next_char); ++ if (next_char == '*') ++ break; ++ READ_NEXT_CHAR (a_this, &cur_char); ++ g_string_append_unichar (comment->stryng, cur_char); ++ } ++ /* Stop condition: next_char == '*' */ ++ for (;;) { /* \*+ */ ++ READ_NEXT_CHAR(a_this, &cur_char); ++ ENSURE_PARSING_COND (cur_char == '*'); ++ g_string_append_unichar (comment->stryng, cur_char); ++ PEEK_NEXT_CHAR (a_this, &next_char); ++ if (next_char != '*') ++ break; ++ } ++ /* Stop condition: next_char != '*' */ ++ for (;;) { /* ([^/][^*]*\*+)* */ ++ if (next_char == '/') ++ break; ++ READ_NEXT_CHAR(a_this, &cur_char); ++ g_string_append_unichar (comment->stryng, cur_char); ++ for (;;) { /* [^*]* */ ++ PEEK_NEXT_CHAR (a_this, &next_char); ++ if (next_char == '*') ++ break; ++ READ_NEXT_CHAR (a_this, &cur_char); ++ g_string_append_unichar (comment->stryng, cur_char); ++ } ++ /* Stop condition: next_char = '*', no need to verify, because peek and read exit to error anyway */ ++ for (;;) { /* \*+ */ ++ READ_NEXT_CHAR(a_this, &cur_char); ++ ENSURE_PARSING_COND (cur_char == '*'); ++ g_string_append_unichar (comment->stryng, cur_char); ++ PEEK_NEXT_CHAR (a_this, &next_char); ++ if (next_char != '*') ++ break; ++ } ++ /* Continue condition: next_char != '*' */ ++ } ++ /* Stop condition: next_char == '\/' */ ++ READ_NEXT_CHAR(a_this, &cur_char); ++ g_string_append_unichar (comment->stryng, cur_char); ++ ++ if (status == CR_OK) { ++ cr_parsing_location_copy (&comment->location, ++ &loc) ; ++ *a_comment = comment; ++ return CR_OK; ++ } ++ error: ++ ++ if (comment) { ++ cr_string_destroy (comment); ++ 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 occurred. ++ */ ++static enum CRStatus ++cr_tknzr_parse_unicode_escape (CRTknzr * a_this, ++ guint32 * a_unicode, ++ CRParsingLocation *a_location) ++{ ++ 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; ++ } ++ if (a_location) { ++ cr_tknzr_get_parsing_location ++ (a_this, a_location) ; ++ } ++ 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 * 16 + cur_char_val; ++ ++ PEEK_NEXT_CHAR (a_this, &cur_char); ++ } ++ ++ /* Eat a whitespace if possible. */ ++ cr_tknzr_parse_w (a_this, &tmp_char_ptr1, ++ &tmp_char_ptr2, NULL); ++ *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, ++ CRParsingLocation *a_location) ++{ ++ 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, ++ a_location); ++ } else { ++ /*consume the '\' char */ ++ READ_NEXT_CHAR (a_this, &cur_char); ++ if (a_location) { ++ cr_tknzr_get_parsing_location (a_this, ++ a_location) ; ++ } ++ /*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, CRString ** a_str) ++{ ++ guint32 cur_char = 0, ++ delim = 0; ++ CRInputPos init_pos; ++ enum CRStatus status = CR_OK; ++ CRString *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 = cr_string_new (); ++ if (str) { ++ cr_tknzr_get_parsing_location ++ (a_this, &str->location) ; ++ } ++ 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->stryng, ++ next_chars[1]); ++ SKIP_BYTES (a_this, 2); ++ status = CR_OK; ++ } else { ++ status = cr_tknzr_parse_escape ++ (a_this, &esc_code, NULL); ++ ++ if (status == CR_OK) { ++ g_string_append_unichar ++ (str->stryng, ++ 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, NULL); ++ } ++ ++ 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->stryng, ++ 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->stryng, 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)->stryng = g_string_append_len ++ ((*a_str)->stryng, ++ str->stryng->str, ++ str->stryng->len); ++ cr_string_destroy (str); ++ } ++ return CR_OK; ++ } ++ ++ error: ++ ++ if (str) { ++ cr_string_destroy (str) ; ++ str = NULL; ++ } ++ cr_tknzr_set_cur_pos (a_this, &init_pos); ++ return status; ++} ++ ++/** ++ *Parses the an nmstart as defined by the css2 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, ++ CRParsingLocation *a_location) ++{ ++ 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, ++ a_location); ++ ++ 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); ++ if (a_location) { ++ cr_tknzr_get_parsing_location (a_this, ++ a_location) ; ++ } ++ *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, ++ CRParsingLocation *a_location) ++{ ++ 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, ++ a_location); ++ ++ 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; ++ if (a_location) { ++ cr_tknzr_get_parsing_location ++ (a_this, a_location) ; ++ } ++ } 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}* ++ * ++ *Actually parses it using the css3 grammar: ++ *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 CRString. 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, CRString ** a_str) ++{ ++ guint32 tmp_char = 0; ++ CRString *stringue = NULL ; ++ CRInputPos init_pos; ++ enum CRStatus status = CR_OK; ++ gboolean location_is_set = FALSE ; ++ ++ 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); ++ PEEK_NEXT_CHAR (a_this, &tmp_char) ; ++ stringue = cr_string_new () ; ++ g_return_val_if_fail (stringue, ++ CR_OUT_OF_MEMORY_ERROR) ; ++ ++ if (tmp_char == '-') { ++ READ_NEXT_CHAR (a_this, &tmp_char) ; ++ cr_tknzr_get_parsing_location ++ (a_this, &stringue->location) ; ++ location_is_set = TRUE ; ++ g_string_append_unichar (stringue->stryng, ++ tmp_char) ; ++ } ++ status = cr_tknzr_parse_nmstart (a_this, &tmp_char, NULL); ++ if (status != CR_OK) { ++ status = CR_PARSING_ERROR; ++ goto end ; ++ } ++ if (location_is_set == FALSE) { ++ cr_tknzr_get_parsing_location ++ (a_this, &stringue->location) ; ++ location_is_set = TRUE ; ++ } ++ g_string_append_unichar (stringue->stryng, tmp_char); ++ for (;;) { ++ status = cr_tknzr_parse_nmchar (a_this, ++ &tmp_char, ++ NULL); ++ if (status != CR_OK) { ++ status = CR_OK ; ++ break; ++ } ++ g_string_append_unichar (stringue->stryng, tmp_char); ++ } ++ if (status == CR_OK) { ++ if (!*a_str) { ++ *a_str = stringue ; ++ ++ } else { ++ g_string_append_len ((*a_str)->stryng, ++ stringue->stryng->str, ++ stringue->stryng->len) ; ++ cr_string_destroy (stringue) ; ++ } ++ stringue = NULL ; ++ } ++ ++ error: ++ end: ++ if (stringue) { ++ cr_string_destroy (stringue) ; ++ stringue = NULL ; ++ } ++ if (status != CR_OK ) { ++ cr_tknzr_set_cur_pos (a_this, &init_pos) ; ++ } ++ return status ; ++} ++ ++ ++/** ++ *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 CRString. 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, ++ CRString ** a_str) ++{ ++ guint32 tmp_char = 0; ++ CRInputPos init_pos; ++ enum CRStatus status = CR_OK; ++ gboolean str_needs_free = FALSE, ++ is_first_nmchar=TRUE ; ++ glong i = 0; ++ CRParsingLocation loc = {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 = cr_string_new (); ++ str_needs_free = TRUE; ++ } ++ for (i = 0;; i++) { ++ if (is_first_nmchar == TRUE) { ++ status = cr_tknzr_parse_nmchar ++ (a_this, &tmp_char, ++ &loc) ; ++ is_first_nmchar = FALSE ; ++ } else { ++ status = cr_tknzr_parse_nmchar ++ (a_this, &tmp_char, NULL) ; ++ } ++ if (status != CR_OK) ++ break; ++ g_string_append_unichar ((*a_str)->stryng, ++ tmp_char); ++ } ++ if (i > 0) { ++ cr_parsing_location_copy ++ (&(*a_str)->location, &loc) ; ++ return CR_OK; ++ } ++ if (str_needs_free == TRUE && *a_str) { ++ cr_string_destroy (*a_str); ++ *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, CRString ** a_str) ++{ ++ guint32 cur_char = 0; ++ CRInputPos init_pos; ++ enum CRStatus status = CR_OK; ++ gboolean str_needs_free = FALSE; ++ CRParsingLocation loc = {0} ; ++ ++ 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 = cr_string_new (); ++ str_needs_free = TRUE; ++ } ++ cr_tknzr_get_parsing_location (a_this, ++ &loc) ; ++ status = cr_tknzr_parse_name (a_this, a_str); ++ cr_parsing_location_copy (&(*a_str)->location, &loc) ; ++ if (status != CR_OK) { ++ goto error; ++ } ++ return CR_OK; ++ ++ error: ++ if (str_needs_free == TRUE && *a_str) { ++ cr_string_destroy (*a_str); ++ *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, ++ CRString ** 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; ++ CRString *str = NULL; ++ CRParsingLocation location = {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); ++ ++ 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; ++ } ++ /* ++ *Here, we want to skip 4 bytes ('u''r''l''('). ++ *But we also need to keep track of the parsing location ++ *of the 'u'. So, we skip 1 byte, we record the parsing ++ *location, then we skip the 3 remaining bytes. ++ */ ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, &location) ; ++ SKIP_CHARS (a_this, 3); ++ 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, NULL); ++ 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 = cr_string_new (); ++ 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->stryng, cur_char); ++ status = CR_OK; ++ } else { ++ guint32 esc_code = 0; ++ status = cr_tknzr_parse_escape ++ (a_this, &esc_code, NULL); ++ if (status == CR_OK) { ++ g_string_append_unichar ++ (str->stryng, ++ 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)->stryng, ++ str->stryng->str, ++ str->stryng->len); ++ cr_string_destroy (str); ++ } ++ } ++ } ++ ++ cr_parsing_location_copy ++ (&(*a_str)->location, ++ &location) ; ++ return CR_OK ; ++ error: ++ if (str) { ++ cr_string_destroy (str); ++ 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; ++ CRParsingLocation location = {0} ; ++ ++ 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, 1); ++ cr_tknzr_get_parsing_location (a_this, &location) ; ++ SKIP_CHARS (a_this, 2); ++ } 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)); ++ ++ if (num->val > G_MAXLONG) { ++ status = CR_PARSING_ERROR; ++ goto error; ++ } ++ ++ 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)); ++ ++ if (num->val > G_MAXLONG) { ++ status = CR_PARSING_ERROR; ++ goto error; ++ } ++ ++ PEEK_BYTE (a_this, 1, &next_bytes[0]); ++ if (next_bytes[0] == '%') { ++ SKIP_CHARS (a_this, 1); ++ is_percentage = 1; ++ } ++ ++ 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) { ++ if (a_rgb && *a_rgb) { ++ cr_parsing_location_copy ++ (&(*a_rgb)->location, ++ &location) ; ++ } ++ 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 CRString 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, ++ CRString ** 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 = cr_string_new (); ++ 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) { ++ cr_string_destroy (*a_str); ++ *a_str = NULL; ++ } ++ cr_tknzr_set_cur_pos (a_this, &init_pos); ++ return status; ++} ++ ++static enum CRStatus ++cr_tknzr_parse_important (CRTknzr * a_this, ++ CRParsingLocation *a_location) ++{ ++ 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 == '!'); ++ if (a_location) { ++ cr_tknzr_get_parsing_location (a_this, ++ a_location) ; ++ } ++ 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); ++ if (a_location) { ++ cr_tknzr_get_parsing_location (a_this, ++ a_location) ; ++ } ++ 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. ++ * ++ *The CSS specification says that numbers may be ++ *preceeded by '+' or '-' to indicate the sign. ++ *Technically, the "num" construction as defined ++ *by the tokenizer doesn't allow this, but we parse ++ *it here for simplicity. ++ */ ++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, /* true iff seen decimal point. */ ++ parsed; /* true iff the substring seen so far is a valid CSS ++ number, i.e. `[0-9]+|[0-9]*\.[0-9]+'. */ ++ guint32 cur_char = 0, ++ next_char = 0; ++ gdouble numerator, denominator = 1; ++ CRInputPos init_pos; ++ CRParsingLocation location = {0} ; ++ int sign = 1; ++ ++ 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 == '+' || cur_char == '-') { ++ if (cur_char == '-') { ++ sign = -1; ++ } ++ READ_NEXT_CHAR (a_this, &cur_char); ++ } ++ ++ if (IS_NUM (cur_char)) { ++ numerator = (cur_char - '0'); ++ parsing_dec = FALSE; ++ parsed = TRUE; ++ } else if (cur_char == '.') { ++ numerator = 0; ++ parsing_dec = TRUE; ++ parsed = FALSE; ++ } else { ++ status = CR_PARSING_ERROR; ++ goto error; ++ } ++ cr_tknzr_get_parsing_location (a_this, &location) ; ++ ++ for (;;) { ++ status = cr_tknzr_peek_char (a_this, &next_char); ++ if (status != CR_OK) { ++ if (status == CR_END_OF_INPUT_ERROR) ++ status = CR_OK; ++ break; ++ } ++ if (next_char == '.') { ++ if (parsing_dec) { ++ status = CR_PARSING_ERROR; ++ goto error; ++ } ++ ++ READ_NEXT_CHAR (a_this, &cur_char); ++ parsing_dec = TRUE; ++ parsed = FALSE; /* In CSS, there must be at least ++ one digit after `.'. */ ++ } else if (IS_NUM (next_char)) { ++ READ_NEXT_CHAR (a_this, &cur_char); ++ parsed = TRUE; ++ ++ numerator = numerator * 10 + (cur_char - '0'); ++ if (parsing_dec) { ++ denominator *= 10; ++ } ++ } else { ++ break; ++ } ++ } ++ ++ if (!parsed) { ++ status = CR_PARSING_ERROR; ++ } ++ ++ /* ++ *Now, set the output param values. ++ */ ++ if (status == CR_OK) { ++ gdouble val = (numerator / denominator) * sign; ++ 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; ++ } ++ cr_parsing_location_copy (&(*a_num)->location, ++ &location) ; ++ 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 (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 ((const gchar *) 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_parsing_location (CRTknzr *a_this, ++ CRParsingLocation *a_loc) ++{ ++ g_return_val_if_fail (a_this ++ && PRIVATE (a_this) ++ && a_loc, ++ CR_BAD_PARAM_ERROR) ; ++ ++ return cr_input_get_parsing_location ++ (PRIVATE (a_this)->input, a_loc) ; ++} ++ ++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) ++{ ++ gulong consumed = *(gulong *) a_nb_char; ++ enum CRStatus status; ++ 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; ++ } ++ ++ status = cr_input_consume_chars (PRIVATE (a_this)->input, ++ a_char, &consumed); ++ *a_nb_char = (glong) consumed; ++ return status; ++} ++ ++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; ++ CRString *str = NULL; ++ CRRgb *rgb = NULL; ++ CRParsingLocation location = {0} ; ++ ++ 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, 1); ++ cr_tknzr_get_parsing_location ++ (a_this, &location) ; ++ SKIP_CHARS (a_this, 9); ++ status = cr_token_set_font_face_sym (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ 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, 1); ++ cr_tknzr_get_parsing_location ++ (a_this, &location) ; ++ SKIP_CHARS (a_this, 7); ++ status = cr_token_set_charset_sym (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ 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, 1); ++ cr_tknzr_get_parsing_location ++ (a_this, &location) ; ++ SKIP_CHARS (a_this, 6); ++ status = cr_token_set_import_sym (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ 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, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ SKIP_CHARS (a_this, 5); ++ status = cr_token_set_media_sym (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ 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, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ SKIP_CHARS (a_this, 4); ++ status = cr_token_set_page_sym (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ 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); ++ if (str) { ++ cr_parsing_location_copy (&token->location, ++ &str->location) ; ++ } ++ goto done; ++ } ++ } ++ break; ++ ++ case 'u': ++ ++ if (BYTE (input, 2, NULL) == 'r' ++ && BYTE (input, 3, NULL) == 'l' ++ && BYTE (input, 4, NULL) == '(') { ++ CRString *str2 = NULL; ++ ++ status = cr_tknzr_parse_uri (a_this, &str2); ++ if (status == CR_OK) { ++ status = cr_token_set_uri (token, str2); ++ CHECK_PARSING_STATUS (status, TRUE); ++ if (str2) { ++ cr_parsing_location_copy (&token->location, ++ &str2->location) ; ++ } ++ goto done; ++ } ++ } ++ goto fallback; ++ 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); ++ if (rgb) { ++ cr_parsing_location_copy (&token->location, ++ &rgb->location) ; ++ } ++ rgb = NULL; ++ goto done; ++ } ++ ++ } ++ goto fallback; ++ break; ++ ++ case '<': ++ if (BYTE (input, 2, NULL) == '!' ++ && BYTE (input, 3, NULL) == '-' ++ && BYTE (input, 4, NULL) == '-') { ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ SKIP_CHARS (a_this, 3); ++ status = cr_token_set_cdo (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ goto done; ++ } ++ break; ++ ++ case '-': ++ if (BYTE (input, 2, NULL) == '-' ++ && BYTE (input, 3, NULL) == '>') { ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ SKIP_CHARS (a_this, 2); ++ status = cr_token_set_cdc (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ goto done; ++ } else { ++ status = cr_tknzr_parse_ident ++ (a_this, &str); ++ if (status == CR_OK) { ++ cr_token_set_ident ++ (token, str); ++ if (str) { ++ cr_parsing_location_copy (&token->location, ++ &str->location) ; ++ } ++ goto done; ++ } else { ++ goto parse_number; ++ } ++ } ++ break; ++ ++ case '~': ++ if (BYTE (input, 2, NULL) == '=') { ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ SKIP_CHARS (a_this, 1); ++ status = cr_token_set_includes (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ goto done; ++ } ++ break; ++ ++ case '|': ++ if (BYTE (input, 2, NULL) == '=') { ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ SKIP_CHARS (a_this, 1); ++ status = cr_token_set_dashmatch (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ 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); ++ if (str) { ++ cr_parsing_location_copy (&token->location, ++ &str->location) ; ++ } ++ goto done; ++ } ++ } ++ break ; ++ ++ case ';': ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ status = cr_token_set_semicolon (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ goto done; ++ ++ case '{': ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ status = cr_token_set_cbo (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ goto done; ++ ++ case '}': ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ status = cr_token_set_cbc (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ goto done; ++ ++ case '(': ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ status = cr_token_set_po (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ goto done; ++ ++ case ')': ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ status = cr_token_set_pc (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ goto done; ++ ++ case '[': ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ status = cr_token_set_bo (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ goto done; ++ ++ case ']': ++ SKIP_CHARS (a_this, 1); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ status = cr_token_set_bc (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ 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, &location); ++ if (status == CR_OK) { ++ status = cr_token_set_s (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ 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); ++ if (str) { ++ cr_parsing_location_copy (&token->location, ++ &str->location) ; ++ } ++ 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); ++ if (str) { ++ cr_parsing_location_copy (&token->location, ++ &str->location) ; ++ } ++ str = NULL; ++ goto done; ++ } ++ break; ++ ++ case '!': ++ status = cr_tknzr_parse_important (a_this, &location); ++ if (status == CR_OK) { ++ status = cr_token_set_important_sym (token); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ goto done; ++ } ++ break; ++ ++ case '0': ++ case '1': ++ case '2': ++ case '3': ++ case '4': ++ case '5': ++ case '6': ++ case '7': ++ case '8': ++ case '9': ++ case '.': ++ case '+': ++ /* '-' case is handled separately above for --> comments */ ++ parse_number: ++ { ++ 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, 4, 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, CR_OK); ++ str = NULL; ++ } ++ } ++ if (token && token->u.num) { ++ cr_parsing_location_copy (&token->location, ++ &token->u.num->location) ; ++ } else { ++ status = CR_ERROR ; ++ } ++ goto done ; ++ } ++ } ++ break; ++ ++ default: ++ fallback: ++ /*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. ++ */ ++ if (str) { ++ cr_parsing_location_copy (&token->location, ++ &str->location) ; ++ } ++ str = NULL; ++ } else { ++ status = cr_token_set_ident (token, ++ str); ++ CHECK_PARSING_STATUS (status, TRUE); ++ if (str) { ++ cr_parsing_location_copy (&token->location, ++ &str->location) ; ++ } ++ str = NULL; ++ } ++ goto done; ++ } else { ++ if (str) { ++ cr_string_destroy (str); ++ str = NULL; ++ } ++ } ++ } ++ break; ++ } ++ ++ READ_NEXT_CHAR (a_this, &next_char); ++ cr_tknzr_get_parsing_location (a_this, ++ &location) ; ++ status = cr_token_set_delim (token, next_char); ++ CHECK_PARSING_STATUS (status, TRUE); ++ cr_parsing_location_copy (&token->location, ++ &location) ; ++ 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) { ++ cr_string_destroy (str); ++ 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: ++ *((CRString **) 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; ++ } ++ ++ *((CRString **) 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); ++} +diff --git a/src/st/croco/cr-tknzr.h b/src/st/croco/cr-tknzr.h +new file mode 100644 +index 000000000..13985b30e +--- /dev/null ++++ b/src/st/croco/cr-tknzr.h +@@ -0,0 +1,115 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for coypyright information. ++ */ ++ ++/** ++ *@file ++ *The declaration of the #CRTknzr (tokenizer) ++ *class. ++ */ ++ ++#ifndef __CR_TKNZR_H__ ++#define __CR_TKNZR_H__ ++ ++#include "cr-utils.h" ++#include "cr-input.h" ++#include "cr-token.h" ++ ++G_BEGIN_DECLS ++ ++ ++typedef struct _CRTknzr CRTknzr ; ++typedef struct _CRTknzrPriv CRTknzrPriv ; ++ ++/** ++ *The tokenizer is the class that knows ++ *about all the css token. Its main job is ++ *to return the next token found in the character ++ *input stream. ++ */ ++struct _CRTknzr ++{ ++ /*the private data of the tokenizer.*/ ++ CRTknzrPriv *priv ; ++} ; ++ ++CRTknzr * cr_tknzr_new (CRInput *a_input) ; ++ ++CRTknzr * cr_tknzr_new_from_uri (const guchar *a_file_uri, ++ enum CREncoding a_enc) ; ++ ++CRTknzr * cr_tknzr_new_from_buf (guchar *a_buf, gulong a_len, ++ enum CREncoding a_enc, ++ gboolean a_free_at_destroy) ; ++ ++gboolean cr_tknzr_unref (CRTknzr *a_this) ; ++ ++void cr_tknzr_ref (CRTknzr *a_this) ; ++ ++enum CRStatus cr_tknzr_read_byte (CRTknzr *a_this, guchar *a_byte) ; ++ ++enum CRStatus cr_tknzr_read_char (CRTknzr *a_this, guint32 *a_char); ++ ++enum CRStatus cr_tknzr_peek_char (CRTknzr *a_this, guint32 *a_char) ; ++ ++enum CRStatus cr_tknzr_peek_byte (CRTknzr *a_this, gulong a_offset, ++ guchar *a_byte) ; ++ ++guchar cr_tknzr_peek_byte2 (CRTknzr *a_this, gulong a_offset, ++ gboolean *a_eof) ; ++ ++enum CRStatus cr_tknzr_set_cur_pos (CRTknzr *a_this, CRInputPos *a_pos) ; ++ ++glong cr_tknzr_get_nb_bytes_left (CRTknzr *a_this) ; ++ ++enum CRStatus cr_tknzr_get_cur_pos (CRTknzr *a_this, CRInputPos *a_pos) ; ++ ++enum CRStatus cr_tknzr_get_parsing_location (CRTknzr *a_this, ++ CRParsingLocation *a_loc) ; ++ ++enum CRStatus cr_tknzr_seek_index (CRTknzr *a_this, ++ enum CRSeekPos a_origin, ++ gint a_pos) ; ++ ++enum CRStatus cr_tknzr_get_cur_byte_addr (CRTknzr *a_this, guchar **a_addr) ; ++ ++ ++enum CRStatus cr_tknzr_consume_chars (CRTknzr *a_this, guint32 a_char, ++ glong *a_nb_char) ; ++ ++enum CRStatus cr_tknzr_get_next_token (CRTknzr *a_this, CRToken ** a_tk) ; ++ ++enum CRStatus cr_tknzr_unget_token (CRTknzr *a_this, CRToken *a_token) ; ++ ++ ++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 cr_tknzr_set_input (CRTknzr *a_this, CRInput *a_input) ; ++ ++enum CRStatus cr_tknzr_get_input (CRTknzr *a_this, CRInput **a_input) ; ++ ++void cr_tknzr_destroy (CRTknzr *a_this) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_TKZNR_H__*/ +diff --git a/src/st/croco/cr-token.c b/src/st/croco/cr-token.c +new file mode 100644 +index 000000000..e240ab8f1 +--- /dev/null ++++ b/src/st/croco/cr-token.c +@@ -0,0 +1,636 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * see COPYRIGHTS file for copyright information. ++ */ ++ ++/** ++ *@file ++ *The definition of the #CRToken class. ++ *Abstracts a css2 token. ++ */ ++#include ++#include "cr-token.h" ++ ++/* ++ *TODO: write a CRToken::to_string() method. ++ */ ++ ++/** ++ *Frees the attributes of the current instance ++ *of #CRtoken. ++ *@param a_this the current instance of #CRToken. ++ */ ++static void ++cr_token_clear (CRToken * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ switch (a_this->type) { ++ case S_TK: ++ case CDO_TK: ++ case CDC_TK: ++ case INCLUDES_TK: ++ case DASHMATCH_TK: ++ case PAGE_SYM_TK: ++ case MEDIA_SYM_TK: ++ case FONT_FACE_SYM_TK: ++ case CHARSET_SYM_TK: ++ case IMPORT_SYM_TK: ++ case IMPORTANT_SYM_TK: ++ case SEMICOLON_TK: ++ case NO_TK: ++ case DELIM_TK: ++ case CBO_TK: ++ case CBC_TK: ++ case BO_TK: ++ case BC_TK: ++ break; ++ ++ case STRING_TK: ++ case IDENT_TK: ++ case HASH_TK: ++ case URI_TK: ++ case FUNCTION_TK: ++ case COMMENT_TK: ++ case ATKEYWORD_TK: ++ if (a_this->u.str) { ++ cr_string_destroy (a_this->u.str); ++ a_this->u.str = NULL; ++ } ++ break; ++ ++ case EMS_TK: ++ case EXS_TK: ++ case LENGTH_TK: ++ case ANGLE_TK: ++ case TIME_TK: ++ case FREQ_TK: ++ case PERCENTAGE_TK: ++ case NUMBER_TK: ++ case PO_TK: ++ case PC_TK: ++ if (a_this->u.num) { ++ cr_num_destroy (a_this->u.num); ++ a_this->u.num = NULL; ++ } ++ break; ++ ++ case DIMEN_TK: ++ if (a_this->u.num) { ++ cr_num_destroy (a_this->u.num); ++ a_this->u.num = NULL; ++ } ++ ++ if (a_this->dimen) { ++ cr_string_destroy (a_this->dimen); ++ a_this->dimen = NULL; ++ } ++ ++ break; ++ ++ case RGB_TK: ++ if (a_this->u.rgb) { ++ cr_rgb_destroy (a_this->u.rgb) ; ++ a_this->u.rgb = NULL ; ++ } ++ break ; ++ ++ case UNICODERANGE_TK: ++ /*not supported yet. */ ++ break; ++ ++ default: ++ cr_utils_trace_info ("I don't know how to clear this token\n") ; ++ break; ++ } ++ ++ a_this->type = NO_TK; ++} ++ ++/** ++ *Default constructor of ++ *the #CRToken class. ++ *@return the newly built instance of #CRToken. ++ */ ++CRToken * ++cr_token_new (void) ++{ ++ CRToken *result = NULL; ++ ++ result = g_try_malloc (sizeof (CRToken)); ++ ++ if (result == NULL) { ++ cr_utils_trace_info ("Out of memory"); ++ return NULL; ++ } ++ ++ memset (result, 0, sizeof (CRToken)); ++ ++ return result; ++} ++ ++/** ++ *Sets the type of curren instance of ++ *#CRToken to 'S_TK' (S in the css2 spec) ++ *@param a_this the current instance of #CRToken. ++ *@return CR_OK upon successfull completion, an error ++ *code otherwise. ++ */ ++enum CRStatus ++cr_token_set_s (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = S_TK; ++ ++ return CR_OK; ++} ++ ++/** ++ *Sets the type of the current instance of ++ *#CRToken to 'CDO_TK' (CDO as said by the css2 spec) ++ *@param a_this the current instance of #CRToken. ++ *@return CR_OK upon successfull completion, an error ++ *code otherwise. ++ */ ++enum CRStatus ++cr_token_set_cdo (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = CDO_TK; ++ ++ return CR_OK; ++} ++ ++/** ++ *Sets the type of the current token to ++ *CDC_TK (CDC as said by the css2 spec). ++ *@param a_this the current instance of #CRToken. ++ *@return CR_OK upon successfull completion, an error ++ *code otherwise. ++ */ ++enum CRStatus ++cr_token_set_cdc (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = CDC_TK; ++ ++ return CR_OK; ++} ++ ++/** ++ *Sets the type of the current instance of ++ *#CRToken to INCLUDES_TK (INCLUDES as said by the css2 spec). ++ *@param a_this the current instance of #CRToken. ++ *@return CR_OK upon successfull completion, an error ++ *code otherwise. ++ */ ++enum CRStatus ++cr_token_set_includes (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = INCLUDES_TK; ++ ++ return CR_OK; ++} ++ ++/** ++ *Sets the type of the current instance of ++ *#CRToken to DASHMATCH_TK (DASHMATCH as said by the css2 spec). ++ *@param a_this the current instance of #CRToken. ++ *@return CR_OK upon successfull completion, an error ++ *code otherwise. ++ */ ++enum CRStatus ++cr_token_set_dashmatch (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = DASHMATCH_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_comment (CRToken * a_this, CRString * a_str) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ a_this->type = COMMENT_TK; ++ a_this->u.str = a_str ; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_string (CRToken * a_this, CRString * a_str) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = STRING_TK; ++ ++ a_this->u.str = a_str ; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_ident (CRToken * a_this, CRString * a_ident) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ a_this->type = IDENT_TK; ++ a_this->u.str = a_ident; ++ return CR_OK; ++} ++ ++ ++enum CRStatus ++cr_token_set_function (CRToken * a_this, CRString * a_fun_name) ++{ ++ g_return_val_if_fail (a_this, ++ CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ a_this->type = FUNCTION_TK; ++ a_this->u.str = a_fun_name; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_hash (CRToken * a_this, CRString * a_hash) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ a_this->type = HASH_TK; ++ a_this->u.str = a_hash; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_rgb (CRToken * a_this, CRRgb * a_rgb) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ a_this->type = RGB_TK; ++ a_this->u.rgb = a_rgb; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_import_sym (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = IMPORT_SYM_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_page_sym (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = PAGE_SYM_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_media_sym (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = MEDIA_SYM_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_font_face_sym (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ a_this->type = FONT_FACE_SYM_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_charset_sym (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ a_this->type = CHARSET_SYM_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_atkeyword (CRToken * a_this, CRString * a_atname) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ a_this->type = ATKEYWORD_TK; ++ a_this->u.str = a_atname; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_important_sym (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ cr_token_clear (a_this); ++ a_this->type = IMPORTANT_SYM_TK; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_ems (CRToken * a_this, CRNum * a_num) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ cr_token_clear (a_this); ++ a_this->type = EMS_TK; ++ a_this->u.num = a_num; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_exs (CRToken * a_this, CRNum * a_num) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ cr_token_clear (a_this); ++ a_this->type = EXS_TK; ++ a_this->u.num = a_num; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_length (CRToken * a_this, CRNum * a_num, ++ enum CRTokenExtraType a_et) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = LENGTH_TK; ++ a_this->extra_type = a_et; ++ a_this->u.num = a_num; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_angle (CRToken * a_this, CRNum * a_num, ++ enum CRTokenExtraType a_et) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = ANGLE_TK; ++ a_this->extra_type = a_et; ++ a_this->u.num = a_num; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_time (CRToken * a_this, CRNum * a_num, ++ enum CRTokenExtraType a_et) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = TIME_TK; ++ a_this->extra_type = a_et; ++ a_this->u.num = a_num; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_freq (CRToken * a_this, CRNum * a_num, ++ enum CRTokenExtraType a_et) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = FREQ_TK; ++ a_this->extra_type = a_et; ++ a_this->u.num = a_num; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_dimen (CRToken * a_this, CRNum * a_num, ++ CRString * a_dim) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ cr_token_clear (a_this); ++ a_this->type = DIMEN_TK; ++ a_this->u.num = a_num; ++ a_this->dimen = a_dim; ++ return CR_OK; ++ ++} ++ ++enum CRStatus ++cr_token_set_percentage (CRToken * a_this, CRNum * a_num) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = PERCENTAGE_TK; ++ a_this->u.num = a_num; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_number (CRToken * a_this, CRNum * a_num) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = NUMBER_TK; ++ a_this->u.num = a_num; ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_uri (CRToken * a_this, CRString * a_uri) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = URI_TK; ++ a_this->u.str = a_uri; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_delim (CRToken * a_this, guint32 a_char) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = DELIM_TK; ++ a_this->u.unichar = a_char; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_semicolon (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = SEMICOLON_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_cbo (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = CBO_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_cbc (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = CBC_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_po (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = PO_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_pc (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = PC_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_bo (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = BO_TK; ++ ++ return CR_OK; ++} ++ ++enum CRStatus ++cr_token_set_bc (CRToken * a_this) ++{ ++ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); ++ ++ cr_token_clear (a_this); ++ ++ a_this->type = BC_TK; ++ ++ return CR_OK; ++} ++ ++/** ++ *The destructor of the #CRToken class. ++ *@param a_this the current instance of #CRToken. ++ */ ++void ++cr_token_destroy (CRToken * a_this) ++{ ++ g_return_if_fail (a_this); ++ ++ cr_token_clear (a_this); ++ ++ g_free (a_this); ++} +diff --git a/src/st/croco/cr-token.h b/src/st/croco/cr-token.h +new file mode 100644 +index 000000000..f1257b7a8 +--- /dev/null ++++ b/src/st/croco/cr-token.h +@@ -0,0 +1,212 @@ ++/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#ifndef __CR_TOKEN_H__ ++#define __CR_TOKEN_H__ ++ ++#include "cr-utils.h" ++#include "cr-input.h" ++#include "cr-num.h" ++#include "cr-rgb.h" ++#include "cr-string.h" ++#include "cr-parsing-location.h" ++ ++G_BEGIN_DECLS ++ ++enum CRTokenType ++{ ++ NO_TK, ++ S_TK, ++ CDO_TK, ++ CDC_TK, ++ INCLUDES_TK, ++ DASHMATCH_TK, ++ COMMENT_TK, ++ STRING_TK, ++ IDENT_TK, ++ HASH_TK, ++ IMPORT_SYM_TK, ++ PAGE_SYM_TK, ++ MEDIA_SYM_TK, ++ FONT_FACE_SYM_TK, ++ CHARSET_SYM_TK, ++ ATKEYWORD_TK, ++ IMPORTANT_SYM_TK, ++ EMS_TK, ++ EXS_TK, ++ LENGTH_TK, ++ ANGLE_TK, ++ TIME_TK, ++ FREQ_TK, ++ DIMEN_TK, ++ PERCENTAGE_TK, ++ NUMBER_TK, ++ RGB_TK, ++ URI_TK, ++ FUNCTION_TK, ++ UNICODERANGE_TK, ++ SEMICOLON_TK, ++ CBO_TK, /*opening curly bracket*/ ++ CBC_TK, /*closing curly bracket*/ ++ PO_TK, /*opening parenthesis*/ ++ PC_TK, /*closing parenthesis*/ ++ BO_TK, /*opening bracket*/ ++ BC_TK, /*closing bracket*/ ++ DELIM_TK ++} ; ++ ++enum CRTokenExtraType ++{ ++ NO_ET = 0, ++ LENGTH_PX_ET, ++ LENGTH_CM_ET, ++ LENGTH_MM_ET, ++ LENGTH_IN_ET, ++ LENGTH_PT_ET, ++ LENGTH_PC_ET, ++ ANGLE_DEG_ET, ++ ANGLE_RAD_ET, ++ ANGLE_GRAD_ET, ++ TIME_MS_ET, ++ TIME_S_ET, ++ FREQ_HZ_ET, ++ FREQ_KHZ_ET ++} ; ++ ++typedef struct _CRToken CRToken ; ++ ++/** ++ *This class abstracts a css2 token. ++ */ ++struct _CRToken ++{ ++ enum CRTokenType type ; ++ enum CRTokenExtraType extra_type ; ++ CRInputPos pos ; ++ ++ union ++ { ++ CRString *str ; ++ CRRgb *rgb ; ++ CRNum *num ; ++ guint32 unichar ; ++ } u ; ++ ++ CRString * dimen ; ++ CRParsingLocation location ; ++} ; ++ ++CRToken* cr_token_new (void) ; ++ ++enum CRStatus cr_token_set_s (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_cdo (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_cdc (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_includes (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_dashmatch (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_comment (CRToken *a_this, CRString *a_str) ; ++ ++enum CRStatus cr_token_set_string (CRToken *a_this, CRString *a_str) ; ++ ++enum CRStatus cr_token_set_ident (CRToken *a_this, CRString * a_ident) ; ++ ++enum CRStatus cr_token_set_hash (CRToken *a_this, CRString *a_hash) ; ++ ++enum CRStatus cr_token_set_rgb (CRToken *a_this, CRRgb *a_rgb) ; ++ ++enum CRStatus cr_token_set_import_sym (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_page_sym (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_media_sym (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_font_face_sym (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_charset_sym (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_atkeyword (CRToken *a_this, CRString *a_atname) ; ++ ++enum CRStatus cr_token_set_important_sym (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_ems (CRToken *a_this, CRNum *a_num) ; ++ ++enum CRStatus cr_token_set_exs (CRToken *a_this, CRNum *a_num) ; ++ ++enum CRStatus cr_token_set_length (CRToken *a_this, CRNum *a_num, ++ enum CRTokenExtraType a_et) ; ++ ++enum CRStatus cr_token_set_angle (CRToken *a_this, CRNum *a_num, ++ enum CRTokenExtraType a_et) ; ++ ++enum CRStatus cr_token_set_time (CRToken *a_this, CRNum *a_num, ++ enum CRTokenExtraType a_et) ; ++ ++enum CRStatus cr_token_set_freq (CRToken *a_this, CRNum *a_num, ++ enum CRTokenExtraType a_et) ; ++ ++enum CRStatus cr_token_set_dimen (CRToken *a_this, CRNum *a_num, ++ CRString *a_dim) ; ++ ++enum CRStatus cr_token_set_percentage (CRToken *a_this, CRNum *a_num) ; ++ ++enum CRStatus cr_token_set_number (CRToken *a_this, CRNum *a_num) ; ++ ++enum CRStatus cr_token_set_uri (CRToken *a_this, CRString *a_uri) ; ++ ++enum CRStatus cr_token_set_function (CRToken *a_this, ++ CRString *a_fun_name) ; ++ ++enum CRStatus cr_token_set_bc (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_bo (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_po (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_pc (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_cbc (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_cbo (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_semicolon (CRToken *a_this) ; ++ ++enum CRStatus cr_token_set_delim (CRToken *a_this, guint32 a_char) ; ++ ++ ++/* ++ enum CRStatus ++ cr_token_set_unicoderange (CRToken *a_this, ++ CRUnicodeRange *a_range) ; ++*/ ++ ++void ++cr_token_destroy (CRToken *a_this) ; ++ ++ ++G_END_DECLS ++ ++#endif /*__CR_TOKEN_H__*/ +diff --git a/src/st/croco/cr-utils.c b/src/st/croco/cr-utils.c +new file mode 100644 +index 000000000..2420cec7c +--- /dev/null ++++ b/src/st/croco/cr-utils.c +@@ -0,0 +1,1330 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * See COPYRIGHTS file for copyright information. ++ */ ++ ++#include "cr-utils.h" ++#include "cr-string.h" ++ ++/** ++ *@file: ++ *Some misc utility functions used ++ *in the libcroco. ++ *Note that troughout this file I will ++ *refer to the CSS SPECIFICATIONS DOCUMENTATION ++ *written by the w3c guys. You can find that document ++ *at http://www.w3.org/TR/REC-CSS2/ . ++ */ ++ ++/**************************** ++ *Encoding transformations and ++ *encoding helpers ++ ****************************/ ++ ++/* ++ *Here is the correspondance between the ucs-4 charactere codes ++ *and there matching utf-8 encoding pattern as dscribed by RFC 2279: ++ * ++ *UCS-4 range (hex.) UTF-8 octet sequence (binary) ++ *------------------ ----------------------------- ++ *0000 0000-0000 007F 0xxxxxxx ++ *0000 0080-0000 07FF 110xxxxx 10xxxxxx ++ *0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx ++ *0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx ++ *0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx ++ *0400 0000-7FFF FFFF 1111110x 10xxxxxx ... 10xxxxxx ++ */ ++ ++/** ++ *Given an utf8 string buffer, calculates ++ *the length of this string if it was encoded ++ *in ucs4. ++ *@param a_in_start a pointer to the begining of ++ *the input utf8 string. ++ *@param a_in_end a pointre to the end of the input ++ *utf8 string (points to the last byte of the buffer) ++ *@param a_len out parameter the calculated length. ++ *@return CR_OK upon succesfull completion, an error code ++ *otherwise. ++ */ ++enum CRStatus ++cr_utils_utf8_str_len_as_ucs4 (const guchar * a_in_start, ++ const guchar * a_in_end, gulong * a_len) ++{ ++ guchar *byte_ptr = NULL; ++ gint len = 0; ++ ++ /* ++ *to store the final decoded ++ *unicode char ++ */ ++ guint c = 0; ++ ++ g_return_val_if_fail (a_in_start && a_in_end && a_len, ++ CR_BAD_PARAM_ERROR); ++ *a_len = 0; ++ ++ for (byte_ptr = (guchar *) a_in_start; ++ byte_ptr <= a_in_end; byte_ptr++) { ++ gint nb_bytes_2_decode = 0; ++ ++ if (*byte_ptr <= 0x7F) { ++ /* ++ *7 bits long char ++ *encoded over 1 byte: ++ * 0xxx xxxx ++ */ ++ c = *byte_ptr; ++ nb_bytes_2_decode = 1; ++ ++ } else if ((*byte_ptr & 0xE0) == 0xC0) { ++ /* ++ *up to 11 bits long char. ++ *encoded over 2 bytes: ++ *110x xxxx 10xx xxxx ++ */ ++ c = *byte_ptr & 0x1F; ++ nb_bytes_2_decode = 2; ++ ++ } else if ((*byte_ptr & 0xF0) == 0xE0) { ++ /* ++ *up to 16 bit long char ++ *encoded over 3 bytes: ++ *1110 xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = *byte_ptr & 0x0F; ++ nb_bytes_2_decode = 3; ++ ++ } else if ((*byte_ptr & 0xF8) == 0xF0) { ++ /* ++ *up to 21 bits long char ++ *encoded over 4 bytes: ++ *1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = *byte_ptr & 0x7; ++ nb_bytes_2_decode = 4; ++ ++ } else if ((*byte_ptr & 0xFC) == 0xF8) { ++ /* ++ *up to 26 bits long char ++ *encoded over 5 bytes. ++ *1111 10xx 10xx xxxx 10xx xxxx ++ *10xx xxxx 10xx xxxx ++ */ ++ c = *byte_ptr & 3; ++ nb_bytes_2_decode = 5; ++ ++ } else if ((*byte_ptr & 0xFE) == 0xFC) { ++ /* ++ *up to 31 bits long char ++ *encoded over 6 bytes: ++ *1111 110x 10xx xxxx 10xx xxxx ++ *10xx xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = *byte_ptr & 1; ++ nb_bytes_2_decode = 6; ++ ++ } else { ++ /* ++ *BAD ENCODING ++ */ ++ return CR_ENCODING_ERROR; ++ } ++ ++ /* ++ *Go and decode the remaining byte(s) ++ *(if any) to get the current character. ++ */ ++ for (; nb_bytes_2_decode > 1; nb_bytes_2_decode--) { ++ /*decode the next byte */ ++ byte_ptr++; ++ ++ /*byte pattern must be: 10xx xxxx */ ++ if ((*byte_ptr & 0xC0) != 0x80) { ++ return CR_ENCODING_ERROR; ++ } ++ ++ c = (c << 6) | (*byte_ptr & 0x3F); ++ } ++ ++ len++; ++ } ++ ++ *a_len = len; ++ ++ return CR_OK; ++} ++ ++/** ++ *Given an ucs4 string, this function ++ *returns the size (in bytes) this string ++ *would have occupied if it was encoded in utf-8. ++ *@param a_in_start a pointer to the beginning of the input ++ *buffer. ++ *@param a_in_end a pointer to the end of the input buffer. ++ *@param a_len out parameter. The computed length. ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_utils_ucs4_str_len_as_utf8 (const guint32 * a_in_start, ++ const guint32 * a_in_end, gulong * a_len) ++{ ++ gint len = 0; ++ guint32 *char_ptr = NULL; ++ ++ g_return_val_if_fail (a_in_start && a_in_end && a_len, ++ CR_BAD_PARAM_ERROR); ++ ++ for (char_ptr = (guint32 *) a_in_start; ++ char_ptr <= a_in_end; char_ptr++) { ++ if (*char_ptr <= 0x7F) { ++ /*the utf-8 char would take 1 byte */ ++ len += 1; ++ } else if (*char_ptr <= 0x7FF) { ++ /*the utf-8 char would take 2 bytes */ ++ len += 2; ++ } else if (*char_ptr <= 0xFFFF) { ++ len += 3; ++ } else if (*char_ptr <= 0x1FFFFF) { ++ len += 4; ++ } else if (*char_ptr <= 0x3FFFFFF) { ++ len += 5; ++ } else if (*char_ptr <= 0x7FFFFFFF) { ++ len += 6; ++ } ++ } ++ ++ *a_len = len; ++ return CR_OK; ++} ++ ++/** ++ *Given an ucsA string, this function ++ *returns the size (in bytes) this string ++ *would have occupied if it was encoded in utf-8. ++ *@param a_in_start a pointer to the beginning of the input ++ *buffer. ++ *@param a_in_end a pointer to the end of the input buffer. ++ *@param a_len out parameter. The computed length. ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_utils_ucs1_str_len_as_utf8 (const guchar * a_in_start, ++ const guchar * a_in_end, gulong * a_len) ++{ ++ gint len = 0; ++ guchar *char_ptr = NULL; ++ ++ g_return_val_if_fail (a_in_start && a_in_end && a_len, ++ CR_BAD_PARAM_ERROR); ++ ++ for (char_ptr = (guchar *) a_in_start; ++ char_ptr <= a_in_end; char_ptr++) { ++ if (*char_ptr <= 0x7F) { ++ /*the utf-8 char would take 1 byte */ ++ len += 1; ++ } else { ++ /*the utf-8 char would take 2 bytes */ ++ len += 2; ++ } ++ } ++ ++ *a_len = len; ++ return CR_OK; ++} ++ ++/** ++ *Converts an utf8 buffer into an ucs4 buffer. ++ * ++ *@param a_in the input utf8 buffer to convert. ++ *@param a_in_len in/out parameter. The size of the ++ *input buffer to convert. After return, this parameter contains ++ *the actual number of bytes consumed. ++ *@param a_out the output converted ucs4 buffer. Must be allocated by ++ *the caller. ++ *@param a_out_len in/out parameter. The size of the output buffer. ++ *If this size is actually smaller than the real needed size, the function ++ *just converts what it can and returns a success status. After return, ++ *this param points to the actual number of characters decoded. ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_utils_utf8_to_ucs4 (const guchar * a_in, ++ gulong * a_in_len, guint32 * a_out, gulong * a_out_len) ++{ ++ gulong in_len = 0, ++ out_len = 0, ++ in_index = 0, ++ out_index = 0; ++ enum CRStatus status = CR_OK; ++ ++ /* ++ *to store the final decoded ++ *unicode char ++ */ ++ guint c = 0; ++ ++ g_return_val_if_fail (a_in && a_in_len ++ && a_out && a_out_len, CR_BAD_PARAM_ERROR); ++ ++ if (*a_in_len < 1) { ++ status = CR_OK; ++ goto end; ++ } ++ ++ in_len = *a_in_len; ++ out_len = *a_out_len; ++ ++ for (in_index = 0, out_index = 0; ++ (in_index < in_len) && (out_index < out_len); ++ in_index++, out_index++) { ++ gint nb_bytes_2_decode = 0; ++ ++ if (a_in[in_index] <= 0x7F) { ++ /* ++ *7 bits long char ++ *encoded over 1 byte: ++ * 0xxx xxxx ++ */ ++ c = a_in[in_index]; ++ nb_bytes_2_decode = 1; ++ ++ } else if ((a_in[in_index] & 0xE0) == 0xC0) { ++ /* ++ *up to 11 bits long char. ++ *encoded over 2 bytes: ++ *110x xxxx 10xx xxxx ++ */ ++ c = a_in[in_index] & 0x1F; ++ nb_bytes_2_decode = 2; ++ ++ } else if ((a_in[in_index] & 0xF0) == 0xE0) { ++ /* ++ *up to 16 bit long char ++ *encoded over 3 bytes: ++ *1110 xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = a_in[in_index] & 0x0F; ++ nb_bytes_2_decode = 3; ++ ++ } else if ((a_in[in_index] & 0xF8) == 0xF0) { ++ /* ++ *up to 21 bits long char ++ *encoded over 4 bytes: ++ *1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = a_in[in_index] & 0x7; ++ nb_bytes_2_decode = 4; ++ ++ } else if ((a_in[in_index] & 0xFC) == 0xF8) { ++ /* ++ *up to 26 bits long char ++ *encoded over 5 bytes. ++ *1111 10xx 10xx xxxx 10xx xxxx ++ *10xx xxxx 10xx xxxx ++ */ ++ c = a_in[in_index] & 3; ++ nb_bytes_2_decode = 5; ++ ++ } else if ((a_in[in_index] & 0xFE) == 0xFC) { ++ /* ++ *up to 31 bits long char ++ *encoded over 6 bytes: ++ *1111 110x 10xx xxxx 10xx xxxx ++ *10xx xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = a_in[in_index] & 1; ++ nb_bytes_2_decode = 6; ++ ++ } else { ++ /*BAD ENCODING */ ++ goto end; ++ } ++ ++ /* ++ *Go and decode the remaining byte(s) ++ *(if any) to get the current character. ++ */ ++ for (; nb_bytes_2_decode > 1; nb_bytes_2_decode--) { ++ /*decode the next byte */ ++ in_index++; ++ ++ /*byte pattern must be: 10xx xxxx */ ++ if ((a_in[in_index] & 0xC0) != 0x80) { ++ goto end; ++ } ++ ++ c = (c << 6) | (a_in[in_index] & 0x3F); ++ } ++ ++ /* ++ *The decoded ucs4 char is now ++ *in c. ++ */ ++ ++ /************************ ++ *Some security tests ++ ***********************/ ++ ++ /*be sure c is a char */ ++ if (c == 0xFFFF || c == 0xFFFE) ++ goto end; ++ ++ /*be sure c is inferior to the max ucs4 char value */ ++ if (c > 0x10FFFF) ++ goto end; ++ ++ /* ++ *c must be less than UTF16 "lower surrogate begin" ++ *or higher than UTF16 "High surrogate end" ++ */ ++ if (c >= 0xD800 && c <= 0xDFFF) ++ goto end; ++ ++ /*Avoid characters that equals zero */ ++ if (c == 0) ++ goto end; ++ ++ a_out[out_index] = c; ++ } ++ ++ end: ++ *a_out_len = out_index + 1; ++ *a_in_len = in_index + 1; ++ ++ return status; ++} ++ ++/** ++ *Reads a character from an utf8 buffer. ++ *Actually decode the next character code (unicode character code) ++ *and returns it. ++ *@param a_in the starting address of the utf8 buffer. ++ *@param a_in_len the length of the utf8 buffer. ++ *@param a_out output parameter. The resulting read char. ++ *@param a_consumed the number of the bytes consumed to ++ *decode the returned character code. ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_utils_read_char_from_utf8_buf (const guchar * a_in, ++ gulong a_in_len, ++ guint32 * a_out, gulong * a_consumed) ++{ ++ gulong in_index = 0, ++ nb_bytes_2_decode = 0; ++ enum CRStatus status = CR_OK; ++ ++ /* ++ *to store the final decoded ++ *unicode char ++ */ ++ guint32 c = 0; ++ ++ g_return_val_if_fail (a_in && a_out && a_out ++ && a_consumed, CR_BAD_PARAM_ERROR); ++ ++ if (a_in_len < 1) { ++ status = CR_OK; ++ goto end; ++ } ++ ++ if (*a_in <= 0x7F) { ++ /* ++ *7 bits long char ++ *encoded over 1 byte: ++ * 0xxx xxxx ++ */ ++ c = *a_in; ++ nb_bytes_2_decode = 1; ++ ++ } else if ((*a_in & 0xE0) == 0xC0) { ++ /* ++ *up to 11 bits long char. ++ *encoded over 2 bytes: ++ *110x xxxx 10xx xxxx ++ */ ++ c = *a_in & 0x1F; ++ nb_bytes_2_decode = 2; ++ ++ } else if ((*a_in & 0xF0) == 0xE0) { ++ /* ++ *up to 16 bit long char ++ *encoded over 3 bytes: ++ *1110 xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = *a_in & 0x0F; ++ nb_bytes_2_decode = 3; ++ ++ } else if ((*a_in & 0xF8) == 0xF0) { ++ /* ++ *up to 21 bits long char ++ *encoded over 4 bytes: ++ *1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = *a_in & 0x7; ++ nb_bytes_2_decode = 4; ++ ++ } else if ((*a_in & 0xFC) == 0xF8) { ++ /* ++ *up to 26 bits long char ++ *encoded over 5 bytes. ++ *1111 10xx 10xx xxxx 10xx xxxx ++ *10xx xxxx 10xx xxxx ++ */ ++ c = *a_in & 3; ++ nb_bytes_2_decode = 5; ++ ++ } else if ((*a_in & 0xFE) == 0xFC) { ++ /* ++ *up to 31 bits long char ++ *encoded over 6 bytes: ++ *1111 110x 10xx xxxx 10xx xxxx ++ *10xx xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = *a_in & 1; ++ nb_bytes_2_decode = 6; ++ ++ } else { ++ /*BAD ENCODING */ ++ goto end; ++ } ++ ++ if (nb_bytes_2_decode > a_in_len) { ++ status = CR_END_OF_INPUT_ERROR; ++ goto end; ++ } ++ ++ /* ++ *Go and decode the remaining byte(s) ++ *(if any) to get the current character. ++ */ ++ for (in_index = 1; in_index < nb_bytes_2_decode; in_index++) { ++ /*byte pattern must be: 10xx xxxx */ ++ if ((a_in[in_index] & 0xC0) != 0x80) { ++ goto end; ++ } ++ ++ c = (c << 6) | (a_in[in_index] & 0x3F); ++ } ++ ++ /* ++ *The decoded ucs4 char is now ++ *in c. ++ */ ++ ++ /************************ ++ *Some security tests ++ ***********************/ ++ ++ /*be sure c is a char */ ++ if (c == 0xFFFF || c == 0xFFFE) ++ goto end; ++ ++ /*be sure c is inferior to the max ucs4 char value */ ++ if (c > 0x10FFFF) ++ goto end; ++ ++ /* ++ *c must be less than UTF16 "lower surrogate begin" ++ *or higher than UTF16 "High surrogate end" ++ */ ++ if (c >= 0xD800 && c <= 0xDFFF) ++ goto end; ++ ++ /*Avoid characters that equals zero */ ++ if (c == 0) ++ goto end; ++ ++ *a_out = c; ++ ++ end: ++ *a_consumed = nb_bytes_2_decode; ++ ++ return status; ++} ++ ++/** ++ * ++ */ ++enum CRStatus ++cr_utils_utf8_str_len_as_ucs1 (const guchar * a_in_start, ++ const guchar * a_in_end, gulong * a_len) ++{ ++ /* ++ *Note: this function can be made shorter ++ *but it considers all the cases of the utf8 encoding ++ *to ease further extensions ... ++ */ ++ ++ guchar *byte_ptr = NULL; ++ gint len = 0; ++ ++ /* ++ *to store the final decoded ++ *unicode char ++ */ ++ guint c = 0; ++ ++ g_return_val_if_fail (a_in_start && a_in_end && a_len, ++ CR_BAD_PARAM_ERROR); ++ *a_len = 0; ++ ++ for (byte_ptr = (guchar *) a_in_start; ++ byte_ptr <= a_in_end; byte_ptr++) { ++ gint nb_bytes_2_decode = 0; ++ ++ if (*byte_ptr <= 0x7F) { ++ /* ++ *7 bits long char ++ *encoded over 1 byte: ++ * 0xxx xxxx ++ */ ++ c = *byte_ptr; ++ nb_bytes_2_decode = 1; ++ ++ } else if ((*byte_ptr & 0xE0) == 0xC0) { ++ /* ++ *up to 11 bits long char. ++ *encoded over 2 bytes: ++ *110x xxxx 10xx xxxx ++ */ ++ c = *byte_ptr & 0x1F; ++ nb_bytes_2_decode = 2; ++ ++ } else if ((*byte_ptr & 0xF0) == 0xE0) { ++ /* ++ *up to 16 bit long char ++ *encoded over 3 bytes: ++ *1110 xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = *byte_ptr & 0x0F; ++ nb_bytes_2_decode = 3; ++ ++ } else if ((*byte_ptr & 0xF8) == 0xF0) { ++ /* ++ *up to 21 bits long char ++ *encoded over 4 bytes: ++ *1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = *byte_ptr & 0x7; ++ nb_bytes_2_decode = 4; ++ ++ } else if ((*byte_ptr & 0xFC) == 0xF8) { ++ /* ++ *up to 26 bits long char ++ *encoded over 5 bytes. ++ *1111 10xx 10xx xxxx 10xx xxxx ++ *10xx xxxx 10xx xxxx ++ */ ++ c = *byte_ptr & 3; ++ nb_bytes_2_decode = 5; ++ ++ } else if ((*byte_ptr & 0xFE) == 0xFC) { ++ /* ++ *up to 31 bits long char ++ *encoded over 6 bytes: ++ *1111 110x 10xx xxxx 10xx xxxx ++ *10xx xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = *byte_ptr & 1; ++ nb_bytes_2_decode = 6; ++ ++ } else { ++ /* ++ *BAD ENCODING ++ */ ++ return CR_ENCODING_ERROR; ++ } ++ ++ /* ++ *Go and decode the remaining byte(s) ++ *(if any) to get the current character. ++ */ ++ for (; nb_bytes_2_decode > 1; nb_bytes_2_decode--) { ++ /*decode the next byte */ ++ byte_ptr++; ++ ++ /*byte pattern must be: 10xx xxxx */ ++ if ((*byte_ptr & 0xC0) != 0x80) { ++ return CR_ENCODING_ERROR; ++ } ++ ++ c = (c << 6) | (*byte_ptr & 0x3F); ++ } ++ ++ /* ++ *The decoded ucs4 char is now ++ *in c. ++ */ ++ ++ if (c <= 0xFF) { /*Add other conditions to support ++ *other char sets (ucs2, ucs3, ucs4). ++ */ ++ len++; ++ } else { ++ /*the char is too long to fit ++ *into the supposed charset len. ++ */ ++ return CR_ENCODING_ERROR; ++ } ++ } ++ ++ *a_len = len; ++ ++ return CR_OK; ++} ++ ++/** ++ *Converts an utf8 string into an ucs4 string. ++ *@param a_in the input string to convert. ++ *@param a_in_len in/out parameter. The length of the input ++ *string. After return, points to the actual number of bytes ++ *consumed. This can be usefull to debug the input stream in case ++ *of encoding error. ++ *@param a_out out parameter. Points to the output string. It is allocated ++ *by this function and must be freed by the caller. ++ *@param a_out_len out parameter. The length of the output string. ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ * ++ */ ++enum CRStatus ++cr_utils_utf8_str_to_ucs4 (const guchar * a_in, ++ gulong * a_in_len, ++ guint32 ** a_out, gulong * a_out_len) ++{ ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_in && a_in_len ++ && a_out && a_out_len, CR_BAD_PARAM_ERROR); ++ ++ status = cr_utils_utf8_str_len_as_ucs4 (a_in, ++ &a_in[*a_in_len - 1], ++ a_out_len); ++ ++ g_return_val_if_fail (status == CR_OK, status); ++ ++ *a_out = g_malloc0 (*a_out_len * sizeof (guint32)); ++ ++ status = cr_utils_utf8_to_ucs4 (a_in, a_in_len, *a_out, a_out_len); ++ ++ return status; ++} ++ ++/** ++ *Converts an ucs4 buffer into an utf8 buffer. ++ * ++ *@param a_in the input ucs4 buffer to convert. ++ *@param a_in_len in/out parameter. The size of the ++ *input buffer to convert. After return, this parameter contains ++ *the actual number of characters consumed. ++ *@param a_out the output converted utf8 buffer. Must be allocated by ++ *the caller. ++ *@param a_out_len in/out parameter. The size of the output buffer. ++ *If this size is actually smaller than the real needed size, the function ++ *just converts what it can and returns a success status. After return, ++ *this param points to the actual number of bytes in the buffer. ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_utils_ucs4_to_utf8 (const guint32 * a_in, ++ gulong * a_in_len, guchar * a_out, gulong * a_out_len) ++{ ++ gulong in_len = 0, ++ in_index = 0, ++ out_index = 0; ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_in && a_in_len && a_out && a_out_len, ++ CR_BAD_PARAM_ERROR); ++ ++ if (*a_in_len < 1) { ++ status = CR_OK; ++ goto end; ++ } ++ ++ in_len = *a_in_len; ++ ++ for (in_index = 0; in_index < in_len; in_index++) { ++ /* ++ *FIXME: return whenever we encounter forbidden char values. ++ */ ++ ++ if (a_in[in_index] <= 0x7F) { ++ a_out[out_index] = a_in[in_index]; ++ out_index++; ++ } else if (a_in[in_index] <= 0x7FF) { ++ a_out[out_index] = (0xC0 | (a_in[in_index] >> 6)); ++ a_out[out_index + 1] = ++ (0x80 | (a_in[in_index] & 0x3F)); ++ out_index += 2; ++ } else if (a_in[in_index] <= 0xFFFF) { ++ a_out[out_index] = (0xE0 | (a_in[in_index] >> 12)); ++ a_out[out_index + 1] = ++ (0x80 | ((a_in[in_index] >> 6) & 0x3F)); ++ a_out[out_index + 2] = ++ (0x80 | (a_in[in_index] & 0x3F)); ++ out_index += 3; ++ } else if (a_in[in_index] <= 0x1FFFFF) { ++ a_out[out_index] = (0xF0 | (a_in[in_index] >> 18)); ++ a_out[out_index + 1] ++ = (0x80 | ((a_in[in_index] >> 12) & 0x3F)); ++ a_out[out_index + 2] ++ = (0x80 | ((a_in[in_index] >> 6) & 0x3F)); ++ a_out[out_index + 3] ++ = (0x80 | (a_in[in_index] & 0x3F)); ++ out_index += 4; ++ } else if (a_in[in_index] <= 0x3FFFFFF) { ++ a_out[out_index] = (0xF8 | (a_in[in_index] >> 24)); ++ a_out[out_index + 1] = ++ (0x80 | (a_in[in_index] >> 18)); ++ a_out[out_index + 2] ++ = (0x80 | ((a_in[in_index] >> 12) & 0x3F)); ++ a_out[out_index + 3] ++ = (0x80 | ((a_in[in_index] >> 6) & 0x3F)); ++ a_out[out_index + 4] ++ = (0x80 | (a_in[in_index] & 0x3F)); ++ out_index += 5; ++ } else if (a_in[in_index] <= 0x7FFFFFFF) { ++ a_out[out_index] = (0xFC | (a_in[in_index] >> 30)); ++ a_out[out_index + 1] = ++ (0x80 | (a_in[in_index] >> 24)); ++ a_out[out_index + 2] ++ = (0x80 | ((a_in[in_index] >> 18) & 0x3F)); ++ a_out[out_index + 3] ++ = (0x80 | ((a_in[in_index] >> 12) & 0x3F)); ++ a_out[out_index + 4] ++ = (0x80 | ((a_in[in_index] >> 6) & 0x3F)); ++ a_out[out_index + 4] ++ = (0x80 | (a_in[in_index] & 0x3F)); ++ out_index += 6; ++ } else { ++ status = CR_ENCODING_ERROR; ++ goto end; ++ } ++ } /*end for */ ++ ++ end: ++ *a_in_len = in_index + 1; ++ *a_out_len = out_index + 1; ++ ++ return status; ++} ++ ++/** ++ *Converts an ucs4 string into an utf8 string. ++ *@param a_in the input string to convert. ++ *@param a_in_len in/out parameter. The length of the input ++ *string. After return, points to the actual number of characters ++ *consumed. This can be usefull to debug the input string in case ++ *of encoding error. ++ *@param a_out out parameter. Points to the output string. It is allocated ++ *by this function and must be freed by the caller. ++ *@param a_out_len out parameter. The length (in bytes) of the output string. ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_utils_ucs4_str_to_utf8 (const guint32 * a_in, ++ gulong * a_in_len, ++ guchar ** a_out, gulong * a_out_len) ++{ ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_in && a_in_len && a_out ++ && a_out_len, CR_BAD_PARAM_ERROR); ++ ++ status = cr_utils_ucs4_str_len_as_utf8 (a_in, ++ &a_in[*a_out_len - 1], ++ a_out_len); ++ ++ g_return_val_if_fail (status == CR_OK, status); ++ ++ status = cr_utils_ucs4_to_utf8 (a_in, a_in_len, *a_out, a_out_len); ++ ++ return status; ++} ++ ++/** ++ *Converts an ucs1 buffer into an utf8 buffer. ++ *The caller must know the size of the resulting buffer and ++ *allocate it prior to calling this function. ++ * ++ *@param a_in the input ucs1 buffer. ++ * ++ *@param a_in_len in/out parameter. The length of the input buffer. ++ *After return, points to the number of bytes actually consumed even ++ *in case of encoding error. ++ * ++ *@param a_out out parameter. The output utf8 converted buffer. ++ * ++ *@param a_out_len in/out parameter. The size of the output buffer. ++ *If the output buffer size is shorter than the actual needed size, ++ *this function just convert what it can. ++ * ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ * ++ */ ++enum CRStatus ++cr_utils_ucs1_to_utf8 (const guchar * a_in, ++ gulong * a_in_len, guchar * a_out, gulong * a_out_len) ++{ ++ gulong out_index = 0, ++ in_index = 0, ++ in_len = 0, ++ out_len = 0; ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_in && a_in_len ++ && a_out_len, ++ CR_BAD_PARAM_ERROR); ++ ++ if (*a_in_len == 0) { ++ *a_out_len = 0 ; ++ return status; ++ } ++ g_return_val_if_fail (a_out, CR_BAD_PARAM_ERROR) ; ++ ++ in_len = *a_in_len; ++ out_len = *a_out_len; ++ ++ for (in_index = 0, out_index = 0; ++ (in_index < in_len) && (out_index < out_len); in_index++) { ++ /* ++ *FIXME: return whenever we encounter forbidden char values. ++ */ ++ ++ if (a_in[in_index] <= 0x7F) { ++ a_out[out_index] = a_in[in_index]; ++ out_index++; ++ } else { ++ a_out[out_index] = (0xC0 | (a_in[in_index] >> 6)); ++ a_out[out_index + 1] = ++ (0x80 | (a_in[in_index] & 0x3F)); ++ out_index += 2; ++ } ++ } /*end for */ ++ ++ *a_in_len = in_index; ++ *a_out_len = out_index; ++ ++ return status; ++} ++ ++/** ++ *Converts an ucs1 string into an utf8 string. ++ *@param a_in_start the beginning of the input string to convert. ++ *@param a_in_end the end of the input string to convert. ++ *@param a_out out parameter. The converted string. ++ *@param a_out out parameter. The length of the converted string. ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ * ++ */ ++enum CRStatus ++cr_utils_ucs1_str_to_utf8 (const guchar * a_in, ++ gulong * a_in_len, ++ guchar ** a_out, gulong * a_out_len) ++{ ++ gulong out_len = 0; ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_in && a_in_len && a_out ++ && a_out_len, CR_BAD_PARAM_ERROR); ++ ++ if (*a_in_len < 1) { ++ *a_out_len = 0; ++ *a_out = NULL; ++ return CR_OK; ++ } ++ ++ status = cr_utils_ucs1_str_len_as_utf8 (a_in, &a_in[*a_in_len - 1], ++ &out_len); ++ ++ g_return_val_if_fail (status == CR_OK, status); ++ ++ *a_out = g_malloc0 (out_len); ++ ++ status = cr_utils_ucs1_to_utf8 (a_in, a_in_len, *a_out, &out_len); ++ ++ *a_out_len = out_len; ++ ++ return status; ++} ++ ++/** ++ *Converts an utf8 buffer into an ucs1 buffer. ++ *The caller must know the size of the resulting ++ *converted buffer, and allocated it prior to calling this ++ *function. ++ * ++ *@param a_in the input utf8 buffer to convert. ++ * ++ *@param a_in_len in/out parameter. The size of the input utf8 buffer. ++ *After return, points to the number of bytes consumed ++ *by the function even in case of encoding error. ++ * ++ *@param a_out out parameter. Points to the resulting buffer. ++ *Must be allocated by the caller. If the size of a_out is shorter ++ *than its required size, this function converts what it can and return ++ *a successfull status. ++ * ++ *@param a_out_len in/out parameter. The size of the output buffer. ++ *After return, points to the number of bytes consumed even in case of ++ *encoding error. ++ * ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ */ ++enum CRStatus ++cr_utils_utf8_to_ucs1 (const guchar * a_in, ++ gulong * a_in_len, guchar * a_out, gulong * a_out_len) ++{ ++ gulong in_index = 0, ++ out_index = 0, ++ in_len = 0, ++ out_len = 0; ++ enum CRStatus status = CR_OK; ++ ++ /* ++ *to store the final decoded ++ *unicode char ++ */ ++ guint32 c = 0; ++ ++ g_return_val_if_fail (a_in && a_in_len ++ && a_out && a_out_len, CR_BAD_PARAM_ERROR); ++ ++ if (*a_in_len < 1) { ++ goto end; ++ } ++ ++ in_len = *a_in_len; ++ out_len = *a_out_len; ++ ++ for (in_index = 0, out_index = 0; ++ (in_index < in_len) && (out_index < out_len); ++ in_index++, out_index++) { ++ gint nb_bytes_2_decode = 0; ++ ++ if (a_in[in_index] <= 0x7F) { ++ /* ++ *7 bits long char ++ *encoded over 1 byte: ++ * 0xxx xxxx ++ */ ++ c = a_in[in_index]; ++ nb_bytes_2_decode = 1; ++ ++ } else if ((a_in[in_index] & 0xE0) == 0xC0) { ++ /* ++ *up to 11 bits long char. ++ *encoded over 2 bytes: ++ *110x xxxx 10xx xxxx ++ */ ++ c = a_in[in_index] & 0x1F; ++ nb_bytes_2_decode = 2; ++ ++ } else if ((a_in[in_index] & 0xF0) == 0xE0) { ++ /* ++ *up to 16 bit long char ++ *encoded over 3 bytes: ++ *1110 xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = a_in[in_index] & 0x0F; ++ nb_bytes_2_decode = 3; ++ ++ } else if ((a_in[in_index] & 0xF8) == 0xF0) { ++ /* ++ *up to 21 bits long char ++ *encoded over 4 bytes: ++ *1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = a_in[in_index] & 0x7; ++ nb_bytes_2_decode = 4; ++ ++ } else if ((a_in[in_index] & 0xFC) == 0xF8) { ++ /* ++ *up to 26 bits long char ++ *encoded over 5 bytes. ++ *1111 10xx 10xx xxxx 10xx xxxx ++ *10xx xxxx 10xx xxxx ++ */ ++ c = a_in[in_index] & 3; ++ nb_bytes_2_decode = 5; ++ ++ } else if ((a_in[in_index] & 0xFE) == 0xFC) { ++ /* ++ *up to 31 bits long char ++ *encoded over 6 bytes: ++ *1111 110x 10xx xxxx 10xx xxxx ++ *10xx xxxx 10xx xxxx 10xx xxxx ++ */ ++ c = a_in[in_index] & 1; ++ nb_bytes_2_decode = 6; ++ ++ } else { ++ /*BAD ENCODING */ ++ status = CR_ENCODING_ERROR; ++ goto end; ++ } ++ ++ /* ++ *Go and decode the remaining byte(s) ++ *(if any) to get the current character. ++ */ ++ if (in_index + nb_bytes_2_decode - 1 >= in_len) { ++ goto end; ++ } ++ ++ for (; nb_bytes_2_decode > 1; nb_bytes_2_decode--) { ++ /*decode the next byte */ ++ in_index++; ++ ++ /*byte pattern must be: 10xx xxxx */ ++ if ((a_in[in_index] & 0xC0) != 0x80) { ++ status = CR_ENCODING_ERROR; ++ goto end; ++ } ++ ++ c = (c << 6) | (a_in[in_index] & 0x3F); ++ } ++ ++ /* ++ *The decoded ucs4 char is now ++ *in c. ++ */ ++ ++ if (c > 0xFF) { ++ status = CR_ENCODING_ERROR; ++ goto end; ++ } ++ ++ a_out[out_index] = c; ++ } ++ ++ end: ++ *a_out_len = out_index; ++ *a_in_len = in_index; ++ ++ return status; ++} ++ ++/** ++ *Converts an utf8 buffer into an ++ *ucs1 buffer. ++ *@param a_in_start the start of the input buffer. ++ *@param a_in_end the end of the input buffer. ++ *@param a_out out parameter. The resulting converted ucs4 buffer. ++ *Must be freed by the caller. ++ *@param a_out_len out parameter. The length of the converted buffer. ++ *@return CR_OK upon successfull completion, an error code otherwise. ++ *Note that out parameters are valid if and only if this function ++ *returns CR_OK. ++ */ ++enum CRStatus ++cr_utils_utf8_str_to_ucs1 (const guchar * a_in, ++ gulong * a_in_len, ++ guchar ** a_out, gulong * a_out_len) ++{ ++ enum CRStatus status = CR_OK; ++ ++ g_return_val_if_fail (a_in && a_in_len ++ && a_out && a_out_len, CR_BAD_PARAM_ERROR); ++ ++ if (*a_in_len < 1) { ++ *a_out_len = 0; ++ *a_out = NULL; ++ return CR_OK; ++ } ++ ++ status = cr_utils_utf8_str_len_as_ucs4 (a_in, &a_in[*a_in_len - 1], ++ a_out_len); ++ ++ g_return_val_if_fail (status == CR_OK, status); ++ ++ *a_out = g_malloc0 (*a_out_len * sizeof (guint32)); ++ ++ status = cr_utils_utf8_to_ucs1 (a_in, a_in_len, *a_out, a_out_len); ++ return status; ++} ++ ++/***************************************** ++ *CSS basic types identification utilities ++ *****************************************/ ++ ++/** ++ *Returns TRUE if a_char is a white space as ++ *defined in the css spec in chap 4.1.1. ++ * ++ *white-space ::= ' '| \t|\r|\n|\f ++ * ++ *@param a_char the character to test. ++ *return TRUE if is a white space, false otherwise. ++ */ ++gboolean ++cr_utils_is_white_space (guint32 a_char) ++{ ++ switch (a_char) { ++ case ' ': ++ case '\t': ++ case '\r': ++ case '\n': ++ case '\f': ++ return TRUE; ++ break; ++ default: ++ return FALSE; ++ } ++} ++ ++/** ++ *Returns true if the character is a newline ++ *as defined in the css spec in the chap 4.1.1. ++ * ++ *nl ::= \n|\r\n|\r|\f ++ * ++ *@param a_char the character to test. ++ *@return TRUE if the character is a newline, FALSE otherwise. ++ */ ++gboolean ++cr_utils_is_newline (guint32 a_char) ++{ ++ switch (a_char) { ++ case '\n': ++ case '\r': ++ case '\f': ++ return TRUE; ++ break; ++ default: ++ return FALSE; ++ } ++} ++ ++/** ++ *returns TRUE if the char is part of an hexa num char: ++ *i.e hexa_char ::= [0-9A-F] ++ */ ++gboolean ++cr_utils_is_hexa_char (guint32 a_char) ++{ ++ if ((a_char >= '0' && a_char <= '9') ++ || (a_char >= 'A' && a_char <= 'F')) { ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++/** ++ *Returns true if the character is a nonascii ++ *character (as defined in the css spec chap 4.1.1): ++ * ++ *nonascii ::= [^\0-\177] ++ * ++ *@param a_char the character to test. ++ *@return TRUE if the character is a nonascii char, ++ *FALSE otherwise. ++ */ ++gboolean ++cr_utils_is_nonascii (guint32 a_char) ++{ ++ if (a_char <= 177) { ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++/** ++ *Dumps a character a_nb times on a file. ++ *@param a_char the char to dump ++ *@param a_fp the destination file pointer ++ *@param a_nb the number of times a_char is to be dumped. ++ */ ++void ++cr_utils_dump_n_chars (guchar a_char, FILE * a_fp, glong a_nb) ++{ ++ glong i = 0; ++ ++ for (i = 0; i < a_nb; i++) { ++ fprintf (a_fp, "%c", a_char); ++ } ++} ++ ++void ++cr_utils_dump_n_chars2 (guchar a_char, GString * a_string, glong a_nb) ++{ ++ glong i = 0; ++ ++ g_return_if_fail (a_string); ++ ++ for (i = 0; i < a_nb; i++) { ++ g_string_append_printf (a_string, "%c", a_char); ++ } ++} ++ ++/** ++ *Duplicates a list of GString instances. ++ *@return the duplicated list of GString instances or NULL if ++ *something bad happened. ++ *@param a_list_of_strings the list of strings to be duplicated. ++ */ ++GList * ++cr_utils_dup_glist_of_string (GList const * a_list_of_strings) ++{ ++ GList const *cur = NULL; ++ GList *result = NULL; ++ ++ g_return_val_if_fail (a_list_of_strings, NULL); ++ ++ for (cur = a_list_of_strings; cur; cur = cur->next) { ++ GString *str = NULL; ++ ++ str = g_string_new_len (((GString *) cur->data)->str, ++ ((GString *) cur->data)->len); ++ if (str) ++ result = g_list_append (result, str); ++ } ++ ++ return result; ++} ++ ++/** ++ *Duplicate a GList where the GList::data is a CRString. ++ *@param a_list_of_strings the list to duplicate ++ *@return the duplicated list, or NULL if something bad ++ *happened. ++ */ ++GList * ++cr_utils_dup_glist_of_cr_string (GList const * a_list_of_strings) ++{ ++ GList const *cur = NULL; ++ GList *result = NULL; ++ ++ g_return_val_if_fail (a_list_of_strings, NULL); ++ ++ for (cur = a_list_of_strings; cur; cur = cur->next) { ++ CRString *str = NULL; ++ ++ str = cr_string_dup ((CRString const *) cur->data) ; ++ if (str) ++ result = g_list_append (result, str); ++ } ++ ++ return result; ++} +diff --git a/src/st/croco/cr-utils.h b/src/st/croco/cr-utils.h +new file mode 100644 +index 000000000..54aa24973 +--- /dev/null ++++ b/src/st/croco/cr-utils.h +@@ -0,0 +1,246 @@ ++/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++ ++/* ++ * This file is part of The Croco Library ++ * ++ * 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 ++ * ++ * Author: Dodji Seketeli ++ * Look at file COPYRIGHTS for copyright information ++ */ ++ ++#ifndef __CR_DEFS_H__ ++#define __CR_DEFS_H__ ++ ++#include ++#include ++#include "libcroco-config.h" ++ ++G_BEGIN_DECLS ++ ++/** ++ *@file ++ *The Croco library basic types definitions ++ *And global definitions. ++ */ ++ ++/** ++ *The status type returned ++ *by the methods of the croco library. ++ */ ++enum CRStatus { ++ CR_OK, ++ CR_BAD_PARAM_ERROR, ++ CR_INSTANCIATION_FAILED_ERROR, ++ CR_UNKNOWN_TYPE_ERROR, ++ CR_UNKNOWN_PROP_ERROR, ++ CR_UNKNOWN_PROP_VAL_ERROR, ++ CR_UNEXPECTED_POSITION_SCHEME, ++ CR_START_OF_INPUT_ERROR, ++ CR_END_OF_INPUT_ERROR, ++ CR_OUTPUT_TOO_SHORT_ERROR, ++ CR_INPUT_TOO_SHORT_ERROR, ++ CR_OUT_OF_BOUNDS_ERROR, ++ CR_EMPTY_PARSER_INPUT_ERROR, ++ CR_ENCODING_ERROR, ++ CR_ENCODING_NOT_FOUND_ERROR, ++ CR_PARSING_ERROR, ++ CR_SYNTAX_ERROR, ++ CR_NO_ROOT_NODE_ERROR, ++ CR_NO_TOKEN, ++ CR_OUT_OF_MEMORY_ERROR, ++ CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR, ++ CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR, ++ CR_ERROR, ++ CR_FILE_NOT_FOUND_ERROR, ++ CR_VALUE_NOT_FOUND_ERROR ++} ; ++ ++/** ++ *Values used by ++ *cr_input_seek_position() ; ++ */ ++enum CRSeekPos { ++ CR_SEEK_CUR, ++ CR_SEEK_BEGIN, ++ CR_SEEK_END ++} ; ++ ++/** ++ *Encoding values. ++ */ ++enum CREncoding ++{ ++ CR_UCS_4 = 1/*Must be not NULL*/, ++ CR_UCS_1, ++ CR_ISO_8859_1, ++ CR_ASCII, ++ CR_UTF_8, ++ CR_UTF_16, ++ CR_AUTO/*should be the last one*/ ++} ; ++ ++ ++ ++ ++#define CROCO_LOG_DOMAIN "LIBCROCO" ++ ++#ifdef __GNUC__ ++#define cr_utils_trace(a_log_level, a_msg) \ ++g_log (CROCO_LOG_DOMAIN, \ ++ G_LOG_LEVEL_CRITICAL, \ ++ "file %s: line %d (%s): %s\n", \ ++ __FILE__, \ ++ __LINE__, \ ++ __PRETTY_FUNCTION__, \ ++ a_msg) ++#else /*__GNUC__*/ ++ ++#define cr_utils_trace(a_log_level, a_msg) \ ++g_log (CROCO_LOG_DOMAIN, \ ++ G_LOG_LEVEL_CRITICAL, \ ++ "file %s: line %d: %s\n", \ ++ __FILE__, \ ++ __LINE__, \ ++ a_msg) ++#endif ++ ++/** ++ *Traces an info message. ++ *The file, line and enclosing function ++ *of the message will be automatically ++ *added to the message. ++ *@param a_msg the msg to trace. ++ */ ++#define cr_utils_trace_info(a_msg) \ ++cr_utils_trace (G_LOG_LEVEL_INFO, a_msg) ++ ++/** ++ *Trace a debug message. ++ *The file, line and enclosing function ++ *of the message will be automatically ++ *added to the message. ++ *@param a_msg the msg to trace. ++ */ ++#define cr_utils_trace_debug(a_msg) \ ++cr_utils_trace (G_LOG_LEVEL_DEBUG, a_msg) ; ++ ++ ++/**************************** ++ *Encoding transformations and ++ *encoding helpers ++ ****************************/ ++ ++enum CRStatus ++cr_utils_read_char_from_utf8_buf (const guchar * a_in, gulong a_in_len, ++ guint32 *a_out, gulong *a_consumed) ; ++ ++enum CRStatus ++cr_utils_ucs1_to_utf8 (const guchar *a_in, gulong *a_in_len, ++ guchar *a_out, gulong *a_out_len) ; ++ ++enum CRStatus ++cr_utils_utf8_to_ucs1 (const guchar * a_in, gulong * a_in_len, ++ guchar *a_out, gulong *a_out_len) ; ++ ++enum CRStatus ++cr_utils_ucs4_to_utf8 (const guint32 *a_in, gulong *a_in_len, ++ guchar *a_out, gulong *a_out_len) ; ++ ++enum CRStatus ++cr_utils_utf8_str_len_as_ucs4 (const guchar *a_in_start, ++ const guchar *a_in_end, ++ gulong *a_len) ; ++enum CRStatus ++cr_utils_ucs1_str_len_as_utf8 (const guchar *a_in_start, ++ const guchar *a_in_end, ++ gulong *a_len) ; ++enum CRStatus ++cr_utils_utf8_str_len_as_ucs1 (const guchar *a_in_start, ++ const guchar *a_in_end, ++ gulong *a_len) ; ++enum CRStatus ++cr_utils_ucs4_str_len_as_utf8 (const guint32 *a_in_start, ++ const guint32 *a_in_end, ++ gulong *a_len) ; ++ ++enum CRStatus ++cr_utils_ucs1_str_to_utf8 (const guchar *a_in_start, ++ gulong *a_in_len, ++ guchar **a_out, ++ gulong *a_len) ; ++ ++enum CRStatus ++cr_utils_utf8_str_to_ucs1 (const guchar * a_in_start, ++ gulong * a_in_len, ++ guchar **a_out, ++ gulong *a_out_len) ; ++ ++enum CRStatus ++cr_utils_utf8_to_ucs4 (const guchar * a_in, ++ gulong * a_in_len, ++ guint32 *a_out, gulong *a_out_len) ; ++ ++enum CRStatus ++cr_utils_ucs4_str_to_utf8 (const guint32 *a_in, ++ gulong *a_in_len, ++ guchar **a_out, gulong *a_out_len) ; ++ ++enum CRStatus ++cr_utils_utf8_str_to_ucs4 (const guchar * a_in, ++ gulong *a_in_len, ++ guint32 **a_out, ++ gulong *a_out_len) ; ++ ++ ++/***************************************** ++ *CSS basic types identification utilities ++ *****************************************/ ++ ++gboolean ++cr_utils_is_newline (guint32 a_char) ; ++ ++gboolean ++cr_utils_is_white_space (guint32 a_char) ; ++ ++gboolean ++cr_utils_is_nonascii (guint32 a_char) ; ++ ++gboolean ++cr_utils_is_hexa_char (guint32 a_char) ; ++ ++ ++/********************************** ++ *Miscellaneous utility functions ++ ***********************************/ ++ ++void ++cr_utils_dump_n_chars (guchar a_char, ++ FILE *a_fp, ++ glong a_nb) ; ++ ++void ++cr_utils_dump_n_chars2 (guchar a_char, ++ GString *a_string, ++ glong a_nb) ; ++GList * ++cr_utils_dup_glist_of_string (GList const *a_list) ; ++ ++GList * ++cr_utils_dup_glist_of_cr_string (GList const * a_list_of_strings) ; ++ ++G_END_DECLS ++ ++#endif /*__CR_DEFS_H__*/ +diff --git a/src/st/croco/libcroco-config.h b/src/st/croco/libcroco-config.h +new file mode 100644 +index 000000000..1ffb758ea +--- /dev/null ++++ b/src/st/croco/libcroco-config.h +@@ -0,0 +1,13 @@ ++#ifndef LIBCROCO_VERSION_NUMBER ++#define LIBCROCO_VERSION_NUMBER 612 ++#endif ++ ++#ifndef LIBCROCO_VERSION ++#define LIBCROCO_VERSION "0.6.12" ++#endif ++ ++#ifndef G_DISABLE_CHECKS ++#if 0 ++#define G_DISABLE_CHECKS 0 ++#endif ++#endif +diff --git a/src/st/croco/libcroco.h b/src/st/croco/libcroco.h +new file mode 100644 +index 000000000..6187a7cb9 +--- /dev/null ++++ b/src/st/croco/libcroco.h +@@ -0,0 +1,42 @@ ++/* ++ * This file is part of The Croco Library ++ * ++ * Copyright (C) 2002-2003 Dodji Seketeli ++ * ++ * 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 ++ */ ++ ++#ifndef __LIBCROCO_H__ ++#define __LIBCROCO_H__ ++ ++#include "libcroco-config.h" ++ ++#include "cr-utils.h" ++#include "cr-pseudo.h" ++#include "cr-term.h" ++#include "cr-attr-sel.h" ++#include "cr-simple-sel.h" ++#include "cr-selector.h" ++#include "cr-enc-handler.h" ++#include "cr-doc-handler.h" ++#include "cr-input.h" ++#include "cr-parser.h" ++#include "cr-statement.h" ++#include "cr-stylesheet.h" ++#include "cr-om-parser.h" ++#include "cr-prop-list.h" ++#include "cr-string.h" ++ ++#endif /*__LIBCROCO_H__*/ +diff --git a/src/st/meson.build b/src/st/meson.build +index 97ce6134d..c1994755b 100644 +--- a/src/st/meson.build ++++ b/src/st/meson.build +@@ -1,3 +1,4 @@ ++# please, keep this sorted alphabetically + st_headers = [ + 'st-adjustment.h', + 'st-bin.h', +@@ -43,13 +44,68 @@ st_h = configure_file( + + st_inc = include_directories('.', '..') + ++# please, keep this sorted alphabetically + st_private_headers = [ ++ 'croco/cr-additional-sel.h', ++ 'croco/cr-attr-sel.h', ++ 'croco/cr-cascade.h', ++ 'croco/cr-declaration.h', ++ 'croco/cr-doc-handler.h', ++ 'croco/cr-enc-handler.h', ++ 'croco/cr-fonts.h', ++ 'croco/cr-input.h', ++ 'croco/cr-num.h', ++ 'croco/cr-om-parser.h', ++ 'croco/cr-parser.h', ++ 'croco/cr-parsing-location.h', ++ 'croco/cr-prop-list.h', ++ 'croco/cr-pseudo.h', ++ 'croco/cr-rgb.h', ++ 'croco/cr-selector.h', ++ 'croco/cr-simple-sel.h', ++ 'croco/cr-statement.h', ++ 'croco/cr-string.h', ++ 'croco/cr-stylesheet.h', ++ 'croco/cr-term.h', ++ 'croco/cr-tknzr.h', ++ 'croco/cr-token.h', ++ 'croco/cr-utils.h', ++ 'croco/libcroco-config.h', ++ 'croco/libcroco.h', + 'st-private.h', + 'st-theme-private.h', + 'st-theme-node-private.h', + 'st-theme-node-transition.h' + ] + ++# please, keep this sorted alphabetically ++croco_sources = [ ++ 'croco/cr-additional-sel.c', ++ 'croco/cr-attr-sel.c', ++ 'croco/cr-cascade.c', ++ 'croco/cr-declaration.c', ++ 'croco/cr-doc-handler.c', ++ 'croco/cr-enc-handler.c', ++ 'croco/cr-fonts.c', ++ 'croco/cr-input.c', ++ 'croco/cr-num.c', ++ 'croco/cr-om-parser.c', ++ 'croco/cr-parser.c', ++ 'croco/cr-parsing-location.c', ++ 'croco/cr-prop-list.c', ++ 'croco/cr-pseudo.c', ++ 'croco/cr-rgb.c', ++ 'croco/cr-selector.c', ++ 'croco/cr-simple-sel.c', ++ 'croco/cr-statement.c', ++ 'croco/cr-string.c', ++ 'croco/cr-stylesheet.c', ++ 'croco/cr-term.c', ++ 'croco/cr-tknzr.c', ++ 'croco/cr-token.c', ++ 'croco/cr-utils.c', ++] ++ + # please, keep this sorted alphabetically + st_sources = [ + 'st-adjustment.c', +@@ -119,9 +175,9 @@ st_cflags = [ + + # Currently meson requires a shared library for building girs + libst = shared_library('st-1.0', +- sources: st_gir_sources + st_non_gir_sources, ++ sources: st_gir_sources + st_non_gir_sources + croco_sources, + c_args: st_cflags, +- dependencies: [clutter_dep, gtk_dep, croco_dep, x11_dep, m_dep], ++ dependencies: [clutter_dep, gtk_dep, x11_dep, m_dep], + install_rpath: mutter_typelibdir, + install_dir: pkglibdir, + install: true +@@ -134,7 +190,7 @@ libst_dep = declare_dependency(link_with: libst, + test_theme = executable('test-theme', + sources: 'test-theme.c', + c_args: st_cflags, +- dependencies: [clutter_dep, gtk_dep], ++ dependencies: [clutter_dep, gtk_dep, libxml_dep], + link_with: libst + ) + +diff --git a/src/st/st-theme-node-private.h b/src/st/st-theme-node-private.h +index 2e6b232f6..659bc37ba 100644 +--- a/src/st/st-theme-node-private.h ++++ b/src/st/st-theme-node-private.h +@@ -25,7 +25,7 @@ + #include + + #include "st-theme-node.h" +-#include ++#include "croco/libcroco.h" + #include "st-types.h" + + G_BEGIN_DECLS +diff --git a/src/st/st-theme-private.h b/src/st/st-theme-private.h +index 08f3a1864..2083a7c84 100644 +--- a/src/st/st-theme-private.h ++++ b/src/st/st-theme-private.h +@@ -21,7 +21,7 @@ + #ifndef __ST_THEME_PRIVATE_H__ + #define __ST_THEME_PRIVATE_H__ + +-#include ++#include "croco/libcroco.h" + #include "st-theme.h" + + G_BEGIN_DECLS +-- +2.23.0 + diff --git a/gnome-shell.spec b/gnome-shell.spec index f0a8f25..e5d830b 100644 --- a/gnome-shell.spec +++ b/gnome-shell.spec @@ -1,6 +1,6 @@ Name: gnome-shell Version: 3.30.1 -Release: 5 +Release: 6 Summary: Core user interface functions for the GNOME 3 desktop Group: User Interface/Desktops License: GPLv2+ @@ -11,6 +11,7 @@ Patch1: gnome-shell-favourite-apps-firefox.patch Patch2: 0001-endSessionDialog-Immediately-add-buttons-to-the-dial.patch Patch3: 0002-endSessionDialog-Support-rebooting-into-the-bootload.patch Patch4: 0001-keyboardManager-Avoid-idempotent-calls-to-meta_backe.patch +Patch5: 0001-Include-the-libcroco-sources-directly-under-src-st-c.patch BuildRequires: meson git ibus-devel chrpath dbus-glib-devel desktop-file-utils BuildRequires: evolution-data-server-devel gcr-devel gjs-devel glib2-devel @@ -120,6 +121,10 @@ glib-compile-schemas --allow-any-name %{_datadir}/glib-2.0/schemas &> /dev/null %{_mandir}/man1/%{name}.1.gz %changelog +* Thu Dec 03 2020 wangxiao -3.30.1-6 +- move the libcroco sources directly under src/st + remove the libcroco dependency from the meson.build files + * Fri Dec 27 2019 Jiangping Hu - 3.30.1-5 - Type:bugfix - Id:NA -- Gitee