summaryrefslogtreecommitdiff
path: root/src/cr-statement.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cr-statement.c')
-rw-r--r--src/cr-statement.c2396
1 files changed, 2396 insertions, 0 deletions
diff --git a/src/cr-statement.c b/src/cr-statement.c
new file mode 100644
index 0000000..a857680
--- /dev/null
+++ b/src/cr-statement.c
@@ -0,0 +1,2396 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * Copyright (C) 2002-2003 Dodji Seketeli <dodji@seketeli.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ *$Id$
+ */
+
+#include <string.h>
+#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
+cr_statement_dump_ruleset (CRStatement *a_this, FILE *a_fp, glong a_indent) ;
+
+static void
+cr_statement_dump_charset (CRStatement *a_this, FILE *a_fp,
+ gulong a_indent) ;
+
+static void
+cr_statement_dump_page (CRStatement *a_this, FILE *a_fp, gulong a_indent) ;
+
+static void
+cr_statement_dump_media_rule (CRStatement *a_this, FILE *a_fp,
+ gulong a_indent) ;
+
+static void
+cr_statement_dump_import_rule (CRStatement *a_this, FILE *a_fp,
+ gulong a_indent) ;
+
+static void
+parse_font_face_start_font_face_cb (CRDocHandler *a_this)
+{
+ 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 ;
+ enum CRStatus status = CR_OK ;
+
+ g_return_if_fail (a_this) ;
+
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer*)&stmt) ;
+ 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,
+ GString *a_name,
+ CRTerm *a_value)
+{
+ enum CRStatus status = CR_OK ;
+ GString *name = NULL ;
+ CRDeclaration *decl = NULL ;
+ CRStatement *stmt = NULL ;
+
+ g_return_if_fail (a_this && a_name) ;
+
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer*)&stmt) ;
+ g_return_if_fail (status == CR_OK && stmt) ;
+ g_return_if_fail (stmt->type == AT_FONT_FACE_RULE_STMT) ;
+
+ name = g_string_new_len (a_name->str, a_name->len) ;
+ 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)
+ {
+ g_string_free (name, TRUE) ;
+ name = NULL ;
+ }
+}
+
+static void
+parse_font_face_end_font_face_cb (CRDocHandler *a_this)
+
+{
+ CRStatement *result = NULL ;
+ enum CRStatus status = CR_OK ;
+
+ g_return_if_fail (a_this) ;
+
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer*)&result) ;
+ 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,
+ GString *a_name,
+ GString *a_pseudo_page)
+{
+ CRStatement *stmt = NULL ;
+ enum CRStatus status = CR_OK ;
+
+ stmt = cr_statement_new_at_page_rule (NULL, NULL, a_name,
+ a_pseudo_page) ;
+ 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 ;
+ enum CRStatus status = CR_OK ;
+
+ g_return_if_fail (a_this) ;
+
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer*)&stmt) ;
+ 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,
+ GString *a_name,
+ CRTerm *a_expression)
+{
+ GString *name = NULL ;
+ CRStatement *stmt = NULL ;
+ CRDeclaration *decl = NULL ;
+ enum CRStatus status = CR_OK ;
+
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer*)&stmt) ;
+ g_return_if_fail (status == CR_OK && stmt->type == AT_PAGE_RULE_STMT) ;
+
+ name = g_string_new_len (a_name->str, a_name->len) ;
+ g_return_if_fail (name) ;
+
+ decl = cr_declaration_new (stmt, name, a_expression) ;
+ g_return_if_fail (decl) ;
+
+ 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,
+ GString *a_name,
+ GString *a_pseudo_page)
+{
+ enum CRStatus status = CR_OK ;
+ CRStatement *stmt = NULL ;
+
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer*)&stmt) ;
+ 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)
+{
+ 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_dup_glist_of_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 ;
+
+ g_return_if_fail (a_this) ;
+
+ status = cr_doc_handler_get_result (a_this, (gpointer*)&stmt) ;
+ 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, *ruleset = NULL ;
+
+ g_return_if_fail (a_this
+ && a_this->priv
+ && a_sellist) ;
+
+ status = cr_doc_handler_get_ctxt (a_this,
+ (gpointer*)&at_media) ;
+ 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,
+ GString *a_name, CRTerm *a_value)
+{
+ enum CRStatus status = CR_OK ;
+ /*
+ *the current ruleset stmt, child of the
+ *current at-media being parsed.
+ */
+ CRStatement *stmt = NULL;
+ GString *name = NULL ;
+
+ g_return_if_fail (a_this && a_name) ;
+
+ name = g_string_new_len (a_name->str, a_name->len) ;
+ g_return_if_fail (name) ;
+
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer*)&stmt) ;
+ g_return_if_fail (status == CR_OK && stmt) ;
+ g_return_if_fail (stmt->type == RULESET_STMT) ;
+
+ status = cr_statement_ruleset_append_decl2
+ (stmt, name, a_value) ;
+ 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 ;
+
+ g_return_if_fail (a_this && a_sellist) ;
+
+ status = cr_doc_handler_get_ctxt (a_this,
+ (gpointer *)&stmt) ;
+ 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 ;
+ g_return_if_fail (a_this && a_this->priv) ;
+
+ status = cr_doc_handler_get_ctxt (a_this,
+ (gpointer*)&at_media) ;
+ 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 ;
+ enum CRStatus status = CR_OK ;
+
+ status = cr_doc_handler_get_result (a_this, (gpointer*)&stmt) ;
+ 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,
+ GString *a_name, CRTerm *a_value)
+{
+ enum CRStatus status = CR_OK ;
+ CRStatement *ruleset = NULL ;
+ GString * stringue = NULL ;
+
+ g_return_if_fail (a_this && a_this->priv && a_name) ;
+
+ stringue = g_string_new (a_name->str) ;
+ g_return_if_fail (stringue) ;
+
+ status = cr_doc_handler_get_result (a_this, (gpointer *)&ruleset) ;
+ g_return_if_fail (status == CR_OK
+ && ruleset && ruleset->type == RULESET_STMT) ;
+
+ status = cr_statement_ruleset_append_decl2
+ (ruleset, stringue, a_value) ;
+ g_return_if_fail (status == CR_OK) ;
+}
+
+static void
+parse_ruleset_end_selector_cb (CRDocHandler *a_this,
+ CRSelector *a_sellist)
+{
+ CRStatement *result = NULL ;
+ enum CRStatus status = CR_OK ;
+
+ g_return_if_fail (a_this && a_sellist) ;
+
+ status = cr_doc_handler_get_result (a_this, (gpointer *)&result) ;
+
+ 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)
+ {
+ g_string_free
+ (a_this->kind.import_rule->url,
+ TRUE) ;
+ 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)
+ {
+ g_string_free ((GString*)cur->data,
+ TRUE) ;
+ 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)
+ {
+ g_string_free (a_this->kind.page_rule->name,
+ TRUE) ;
+ a_this->kind.page_rule->name = NULL ;
+ }
+ if (a_this->kind.page_rule->pseudo)
+ {
+ g_string_free (a_this->kind.page_rule->pseudo,
+ TRUE) ;
+ 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)
+ {
+ g_string_free
+ (a_this->kind.charset_rule->charset,
+ TRUE) ;
+ 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 ;
+ }
+}
+
+/**
+ *Dumps a ruleset statement to a file.
+ *@param a_this the current instance of #CRStatement.
+ *@param a_fp the destination file pointer.
+ *@param a_indent the number of indentation white spaces to add.
+ */
+static void
+cr_statement_dump_ruleset (CRStatement *a_this, FILE *a_fp, glong a_indent)
+{
+ guchar *str = NULL, *tmp_str = NULL ;
+
+ g_return_if_fail (a_this && a_this->type == RULESET_STMT) ;
+
+ if (a_this->kind.ruleset->sel_list)
+ {
+ if (a_indent)
+ cr_utils_dump_n_chars (' ', a_fp, a_indent) ;
+
+ cr_selector_dump (a_this->kind.ruleset->sel_list, a_fp) ;
+ }
+
+ if (a_this->kind.ruleset->decl_list)
+ {
+ fprintf (a_fp," {\n") ;
+ cr_declaration_dump (a_this->kind.ruleset->decl_list,
+ a_fp, a_indent + DECLARATION_INDENT_NB,
+ TRUE) ;
+ fprintf (a_fp,"\n") ;
+ cr_utils_dump_n_chars (' ', a_fp, a_indent) ;
+ fprintf (a_fp,"}") ;
+ }
+
+ if (str)
+ {
+ g_free (str) ;
+ str = NULL ;
+ }
+ if (tmp_str)
+ {
+ g_free (tmp_str) ;
+ tmp_str = NULL ;
+ }
+}
+
+/**
+ *Dumps a font face rule statement to a file.
+ *@param a_this the current instance of font face rule statement.
+ *@param a_fp the destination file pointer.
+ *@param a_indent the number of white space indentation.
+ */
+static void
+cr_statement_dump_font_face_rule (CRStatement *a_this , FILE *a_fp,
+ glong a_indent)
+{
+ g_return_if_fail (a_this
+ && a_this->type == AT_FONT_FACE_RULE_STMT) ;
+
+ if (a_this->kind.font_face_rule->decl_list)
+ {
+ cr_utils_dump_n_chars (' ', a_fp, a_indent) ;
+
+ if (a_indent)
+ cr_utils_dump_n_chars (' ', a_fp, a_indent) ;
+
+ fprintf (a_fp,"@font-face {\n") ;
+ cr_declaration_dump
+ (a_this->kind.font_face_rule->decl_list,
+ a_fp, a_indent + DECLARATION_INDENT_NB, TRUE) ;
+ fprintf (a_fp,"\n}") ;
+ }
+}
+
+/**
+ *Dumps an @charset rule statement to a file.
+ *@param a_this the current instance of the @charset rule statement.
+ *@param a_fp the destination file pointer.
+ *@param a_indent the number of indentation white spaces.
+ */
+static void
+cr_statement_dump_charset (CRStatement *a_this, FILE *a_fp,
+ gulong a_indent)
+{
+ guchar *str = NULL ;
+
+ g_return_if_fail (a_this
+ && a_this->type == AT_CHARSET_RULE_STMT) ;
+
+ if (a_this->kind.charset_rule
+ && a_this->kind.charset_rule->charset)
+ {
+ str = g_strndup (a_this->kind.charset_rule->charset->str,
+ a_this->kind.charset_rule->charset->len) ;
+
+ g_return_if_fail (str) ;
+
+ cr_utils_dump_n_chars (' ', a_fp, a_indent) ;
+ fprintf (a_fp,"@charset \"%s\" ;", str) ;
+ if (str)
+ {
+ g_free (str) ;
+ str = NULL;
+ }
+ }
+}
+
+/**
+ *Dumps an @page rule statement on stdout.
+ *@param a_this the statement to dump on stdout.
+ *@param a_fp the destination file pointer.
+ *@param a_indent the number of indentation white spaces.
+ */
+static void
+cr_statement_dump_page (CRStatement *a_this, FILE *a_fp, gulong a_indent)
+{
+ guchar *str = NULL ;
+
+ g_return_if_fail (a_this
+ && a_this->type == AT_PAGE_RULE_STMT
+ && a_this->kind.page_rule) ;
+
+ cr_utils_dump_n_chars (' ', a_fp, a_indent) ;
+ fprintf (a_fp,"@page") ;
+
+ if (a_this->kind.page_rule->name)
+ {
+ str = g_strndup (a_this->kind.page_rule->name->str,
+ a_this->kind.page_rule->name->len) ;
+ g_return_if_fail (str) ;
+ fprintf (a_fp," %s", str) ;
+ if (str)
+ {
+ g_free (str) ;
+ str = NULL ;
+ }
+ }
+ else
+ {
+ fprintf (a_fp," ") ;
+ }
+
+ if (a_this->kind.page_rule->pseudo)
+ {
+ str = g_strndup (a_this->kind.page_rule->pseudo->str,
+ a_this->kind.page_rule->pseudo->len) ;
+ g_return_if_fail (str) ;
+ fprintf (a_fp,":%s", str) ;
+ if (str)
+ {
+ g_free (str) ;
+ str = NULL ;
+ }
+ }
+
+ if (a_this->kind.page_rule->decl_list)
+ {
+ fprintf (a_fp," {\n") ;
+ cr_declaration_dump
+ (a_this->kind.page_rule->decl_list,
+ a_fp, a_indent + DECLARATION_INDENT_NB, TRUE) ;
+ fprintf (a_fp,"\n}\n") ;
+ }
+}
+
+/**
+ *Dumps an @media rule statement to a file.
+ *@param a_this the statement to dump.
+ *@param a_fp the destination file pointer
+ *@param a_indent the number of white spaces indentation.
+ */
+static void
+cr_statement_dump_media_rule (CRStatement *a_this, FILE *a_fp,
+ gulong a_indent)
+{
+ GList *cur = NULL ;
+
+ g_return_if_fail (a_this->type == AT_MEDIA_RULE_STMT) ;
+
+ if (a_this->kind.media_rule)
+ {
+ cr_utils_dump_n_chars (' ', a_fp, a_indent) ;
+ fprintf (a_fp,"@media") ;
+ for (cur = a_this->kind.media_rule->media_list ; cur ;
+ cur = cur->next)
+ {
+ if (cur->data)
+ {
+ guchar *str = g_strndup
+ (((GString*)cur->data)->str,
+ ((GString*)cur->data)->len) ;
+ if (str)
+ {
+ if (cur->prev)
+ {
+ fprintf (a_fp,",") ;
+ }
+ fprintf (a_fp," %s", str) ;
+ g_free (str) ; str = NULL ;
+ }
+ }
+ }
+ fprintf (a_fp," {\n") ;
+ cr_statement_dump (a_this->kind.media_rule->rulesets,
+ a_fp, a_indent + DECLARATION_INDENT_NB) ;
+ fprintf (a_fp,"\n}") ;
+ }
+}
+
+/**
+ *Dumps an @import rule statement to a file.
+ *@param a_fp the destination file pointer.
+ *@param a_indent the number of white space indentations.
+ */
+static void
+cr_statement_dump_import_rule (CRStatement *a_this, FILE *a_fp,
+ gulong a_indent)
+{
+ g_return_if_fail (a_this
+ && a_this->type == AT_IMPORT_RULE_STMT
+ && a_this->kind.import_rule) ;
+
+ if (a_this->kind.import_rule->url)
+ {
+ guchar *str = NULL ;
+
+ str = g_strndup (a_this->kind.import_rule->url->str,
+ a_this->kind.import_rule->url->len) ;
+ cr_utils_dump_n_chars (' ', a_fp, a_indent) ;
+
+ if (str)
+ {
+ fprintf (a_fp,"@import url(\"%s\")", str) ;
+ g_free (str) ;
+ }
+ else /*there is no url, so no import rule, get out!*/
+ return ;
+
+ if (a_this->kind.import_rule->media_list)
+ {
+ GList *cur = NULL ;
+
+ for (cur = a_this->kind.import_rule->media_list ;
+ cur; cur = cur->next)
+ {
+ if (cur->data)
+ {
+ GString *gstr = cur->data ;
+
+ if (cur->prev)
+ {
+ fprintf (a_fp,", ") ;
+ }
+
+ str = g_strndup (gstr->str,
+ gstr->len) ;
+ if (str)
+ {
+ fprintf (a_fp,str) ;
+ g_free (str) ;
+ }
+ }
+ }
+ fprintf (a_fp," ;") ;
+ }
+ }
+}
+
+
+/*******************
+ *public functions
+ ******************/
+
+/**
+ *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.
+ *@param a_buf the buffer to parse.
+ *@param a_encoding the character encoding of a_buf.
+ *@return 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 (a_buf, strlen (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 ;
+}
+
+/**
+ *Parses a buffer that contains a css statement and returns
+ *an instance of #CRStatement in case of successfull parsing.
+ *TODO: at support of "@import" rules.
+ *@param a_buf the buffer to parse.
+ *@param a_encoding the character encoding of a_buf.
+ *@return the newly built instance of #CRStatement in case
+ *of successfull 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 ;
+}
+
+/**
+ *Parses a buffer that contains a ruleset statement an instanciates
+ *a #CRStatement of type RULESET_STMT.
+ *@param a_buf the buffer to parse.
+ *@param a_enc the character encoding of a_buf.
+ *@param the newly built instance of #CRStatement in case of successfull 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;
+ CRParser *parser = NULL ;
+ CRDocHandler *sac_handler = NULL ;
+
+ g_return_val_if_fail (a_buf, NULL) ;
+
+ parser = cr_parser_new_from_buf (a_buf, strlen (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 ;
+ }
+
+ status = cr_doc_handler_get_result (sac_handler,
+ (gpointer*)&result) ;
+ if (! ((status == CR_OK) && result) )
+ {
+ if (result)
+ {
+ cr_statement_destroy (result) ;
+ result = NULL ;
+ }
+ }
+
+ cleanup:
+ if (parser)
+ {
+ cr_parser_destroy (parser) ;
+ parser = NULL ;
+ }
+ if (sac_handler)
+ {
+ cr_doc_handler_unref (sac_handler) ;
+ sac_handler = NULL ;
+ }
+ return result ;
+}
+
+/**
+ *Creates a new instance of #CRStatement of type
+ *#CRRulSet.
+ *@param a_sel_list the list of #CRSimpleSel (selectors)
+ *the rule applies to.
+ *@param a_decl_list the list of instances of #CRDeclaration
+ *that composes the ruleset.
+ *@param a_media_types a list of instances of GString that
+ *describe the media list this ruleset applies to.
+ *@return 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 ;
+}
+
+/**
+ *Parses a buffer that contains an "@media" declaration
+ *and builds an @media css statement.
+ *@param a_buf the input to parse.
+ *@param a_enc the encoding of the buffer.
+ *@return 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 ;
+ CRDocHandler *sac_handler = NULL ;
+ enum CRStatus status = CR_OK ;
+
+ parser = cr_parser_new_from_buf (a_buf, strlen (a_buf),
+ a_enc, FALSE) ;
+ if (!parser)
+ {
+ cr_utils_trace_info ("Instanciation of the parser failed") ;
+ goto cleanup ;
+ }
+
+ sac_handler = cr_doc_handler_new () ;
+ if (!sac_handler)
+ {
+ cr_utils_trace_info ("Instanciation 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 ;
+
+ status = cr_doc_handler_get_result (sac_handler,
+ (gpointer*)&result) ;
+ if (status != CR_OK)
+ goto cleanup ;
+
+
+ cleanup:
+
+ if (parser)
+ {
+ cr_parser_destroy (parser) ;
+ parser = NULL ;
+ }
+ if (sac_handler)
+ {
+ cr_doc_handler_unref (sac_handler) ;
+ sac_handler = NULL ;
+ }
+
+ return result ;
+}
+
+/**
+ *Instanciates an instance of #CRStatement of type
+ *AT_MEDIA_RULE_STMT (@media ruleset).
+ *@param a_ruleset the ruleset statements contained
+ *in the @media rule.
+ *@param a_media, the media string list. A list of GString pointers.
+ */
+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 ;
+}
+
+
+/**
+ *Creates a new instance of #CRStatment of type
+ *#CRAtImportRule.
+ *@param a_url the url to connect to the get the file
+ *to be imported.
+ *@param a_sheet the imported parsed stylesheet.
+ *@return the newly built instance of #CRStatement.
+ */
+CRStatement*
+cr_statement_new_at_import_rule (CRStyleSheet *a_container_sheet,
+ GString *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 ;
+}
+
+/**
+ *Parses a buffer that contains an "@import" rule and
+ *instanciate a #CRStatement of type AT_IMPORT_RULE_STMT
+ *@param a_buf the buffer to parse.
+ *@param a_encoding the encoding of a_buf.
+ *@return the newly built instance of #CRStatement in case of
+ *a successfull 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 ;
+ GString *import_string = NULL ;
+
+ parser = cr_parser_new_from_buf (a_buf, strlen (a_buf),
+ a_encoding, FALSE) ;
+ if (!parser)
+ {
+ cr_utils_trace_info ("Instanciation 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) ;
+ if (status != CR_OK || !import_string)
+ goto cleanup ;
+
+ result = cr_statement_new_at_import_rule (NULL, import_string,
+ media_list, NULL) ;
+ if (result)
+ {
+ import_string = NULL ;
+ media_list = NULL ;
+ }
+
+ cleanup:
+ if (parser)
+ {
+ cr_parser_destroy (parser) ;
+ parser = NULL ;
+ }
+ if (media_list)
+ {
+ GList *cur = NULL ;
+ for (cur = media_list; media_list;
+ media_list = g_list_next (media_list))
+ {
+ if (media_list->data)
+ {
+ g_string_free (media_list->data, TRUE);
+ media_list->data = NULL ;
+ }
+ }
+ g_list_free (media_list) ;
+ media_list = NULL;
+ }
+ if (import_string)
+ {
+ g_string_free (import_string, TRUE) ;
+ import_string = NULL;
+ }
+
+ return result ;
+}
+
+/**
+ *Creates a new instance of #CRStatement of type
+ *#CRAtPageRule.
+ *@param a_decl_list a list of instances of #CRDeclarations
+ *which is actually the list of declarations that applies to
+ *this page rule.
+ *@param a_selector the page rule selector.
+ *@return 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,
+ GString *a_name,
+ GString *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->name = a_pseudo ;
+ if (a_sheet)
+ cr_statement_set_parent_sheet (result, a_sheet) ;
+
+ return result ;
+}
+
+/**
+ *Parses a buffer that contains an "@page" production and,
+ *if the parsing succeeds, builds the page statement.
+ *@param a_buf the character buffer to parse.
+ *@param a_encoding the character encoding of a_buf.
+ *@return the newly built at page statement in case of successfull 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 ;
+
+ g_return_val_if_fail (a_buf, NULL) ;
+
+ parser = cr_parser_new_from_buf (a_buf, strlen (a_buf),
+ a_encoding, FALSE) ;
+ if (!parser)
+ {
+ cr_utils_trace_info ("Instanciation of the parser failed.") ;
+ goto cleanup ;
+ }
+
+ sac_handler = cr_doc_handler_new () ;
+ if (!sac_handler)
+ {
+ cr_utils_trace_info
+ ("Instanciation 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 ;
+
+ status = cr_doc_handler_get_result (sac_handler,
+ (gpointer*)&result) ;
+
+ cleanup:
+
+ if (parser)
+ {
+ cr_parser_destroy (parser) ;
+ parser = NULL ;
+ }
+ if (sac_handler)
+ {
+ cr_doc_handler_unref (sac_handler) ;
+ sac_handler = NULL ;
+ }
+ return result ;
+
+}
+
+/**
+ *Creates a new instance of #CRStatement of type
+ *#CRAtCharsetRule.
+ *@param 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 !!!.
+ *@return the newly built instance of #CRStatement or NULL
+ *if an error arises.
+ */
+CRStatement *
+cr_statement_new_at_charset_rule (CRStyleSheet *a_sheet,
+ GString *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 ;
+}
+
+/**
+ *Parses a buffer that contains an '@charset' rule and
+ *creates an instance of #CRStatement of type AT_CHARSET_RULE_STMT.
+ *@param a_buf the buffer to parse.
+ *@param the character encoding of the buffer.
+ *@return 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 ;
+ GString *charset = NULL ;
+
+ g_return_val_if_fail (a_buf, NULL) ;
+
+ parser = cr_parser_new_from_buf (a_buf, strlen (a_buf),
+ a_encoding, FALSE) ;
+ if (!parser)
+ {
+ cr_utils_trace_info ("Instanciation 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) ;
+ 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)
+ {
+ g_string_free (charset, TRUE) ;
+ }
+
+ return result ;
+}
+
+/**
+ *Creates an instance of #CRStatement of type #CRAtFontFaceRule.
+ *@param a_font_decls a list of instances of #CRDeclaration. Each declaration
+ *is actually a font declaration.
+ *@return 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 ;
+}
+
+/**
+ *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.
+ *@param a_buf the buffer to parse.
+ *@param a_encoding the character encoding of a_buf.
+ *@return 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 ;
+ CRParser *parser = NULL ;
+ CRDocHandler *sac_handler = NULL ;
+ enum CRStatus status = CR_OK ;
+
+ parser = cr_parser_new_from_buf (a_buf, strlen (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 ;
+
+ status = cr_doc_handler_get_result (sac_handler, (gpointer*)&result) ;
+ if (status != CR_OK || !result)
+ goto cleanup ;
+
+ cleanup:
+ if (parser)
+ {
+ cr_parser_destroy (parser) ;
+ parser = NULL ;
+ }
+ if (sac_handler)
+ {
+ cr_doc_handler_unref (sac_handler) ;
+ sac_handler = NULL ;
+ }
+ return result ;
+}
+
+/**
+ *Sets the container stylesheet.
+ *@param a_this the current instance of #CRStatement.
+ *@param a_sheet the sheet that contains the current statement.
+ *@return CR_OK upon successfull completion, an errror 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 ;
+}
+
+/**
+ *Gets the sheets that contains the current statement.
+ *@param a_this the current #CRStatement.
+ *@param a_sheet out parameter. A pointer to the sheets that
+ *@return CR_OK upon successfull 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 ;
+}
+
+/**
+ *Appends a new statement to the statement list.
+ *@param a_this the current instance of the statement list.
+ *@param a_this a_new the new instance of #CRStatement to append.
+ *@return 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 ;
+}
+
+/**
+ *Prepends the an instance of #CRStatement to
+ *the current statement list.
+ *@param a_this the current instance of #CRStatement.
+ *@param a_new the new statement to prepend.
+ *@return 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 ;
+}
+
+/**
+ *Unlinks a statement from the statements list.
+ *@param a_this the current statements list.
+ *@param a_to_unlink the statement to unlink from the list.
+ *@return 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 ;
+}
+
+
+/**
+ *Sets a selector list to a ruleset statement.
+ *@param a_this the current ruleset statement.
+ *@param 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.
+ *@return CR_OK upon successfull 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 ;
+}
+
+/**
+ *Gets a pointer to the list of declaration contained
+ *in the ruleset statement.
+ *@param 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.
+ *@return CR_OK upon successfull 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 ;
+}
+
+/**
+ *Gets a pointer to the selector list contained in
+ *the current ruleset statement.
+ *@param a_this the current ruleset statement.
+ *@param a_list out parameter. The returned selector list,
+ *if and only if the function returned CR_OK.
+ *@return CR_OK upon successfull completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_ruleset_get_sel_list (CRStatement *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 ;
+}
+
+
+/**
+ *Sets a declaration list to the current ruleset statement.
+ *@param a_this the current ruleset statement.
+ *@param a_list the declaration list to be added to the current
+ *ruleset statement.
+ *@return CR_OK upon successfull 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 ;
+}
+
+
+/**
+ *Appends a declaration to the current ruleset statement.
+ *@param a_this the current statement.
+ *@param a_prop the property of the declaration.
+ *@param a_value the value of the declaration.
+ *@return CR_OK uppon successfull completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_statement_ruleset_append_decl2 (CRStatement *a_this,
+ GString *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 ;
+}
+
+/**
+ *Appends a declaration to the current statement.
+ *@param a_this the current statement.
+ *@param a_declaration the declaration to append.
+ *@return CR_OK upon sucessfull 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 ;
+}
+
+
+/**
+ *Sets a stylesheet to the current @import rule.
+ *@param a_this the current @import rule.
+ *@param 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 will be destroyed.
+ *@return CR_OK upon successfull 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 ;
+}
+
+
+/**
+ *Gets the stylesheet contained by the @import rule statement.
+ *@param a_this the current @import rule statement.
+ *@param a_sheet out parameter. The returned stylesheet if and
+ *only if the function returns CR_OK.
+ *@return CR_OK upon sucessfull 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 ;
+
+}
+
+/**
+ *Sets an url to the current @import rule statement.
+ *@param a_this the current @import rule statement.
+ *@param a_url the url to set.
+ *@return CR_OK upon successfull completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_import_rule_set_url (CRStatement *a_this,
+ GString *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)
+ {
+ g_string_free (a_this->kind.import_rule->url, TRUE) ;
+ }
+
+ a_this->kind.import_rule->url = a_url ;
+
+ return CR_OK ;
+}
+
+
+/**
+ *Gets the url of the @import rule statement.
+ *@param the current @import rule statement.
+ *@param a_url out parameter. The returned url if
+ *and only if the function returned CR_OK.
+ */
+enum CRStatus
+cr_statement_at_import_rule_get_url (CRStatement *a_this,
+ GString **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 ;
+}
+
+/**
+ *Sets a declaration list to the current @page rule statement.
+ *@param a_this the current @page rule statement.
+ *@param a_decl_list the declaration list to add. Will be freed
+ *by the current instance of #CRStatement when it is destroyed.
+ *@return CR_OK upon successfull 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 ;
+}
+
+
+/**
+ *Gets the declaration list associated to the current @page rule
+ *statement.
+ *@param a_this the current @page rule statement.
+ *@param a_decl_list out parameter. The returned declaration list.
+ *@return CR_OK upon successfull 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 ;
+}
+
+
+/**
+ *Sets the charset of the current @charset rule statement.
+ *@param a_this the current @charset rule statement.
+ *@param a_charset the charset to set.
+ *@return CR_OK upon successfull completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_charset_rule_set_charset (CRStatement *a_this,
+ GString *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)
+ {
+ g_string_free (a_this->kind.charset_rule->charset,
+ TRUE) ;
+ }
+
+ a_this->kind.charset_rule->charset = a_charset ;
+ return CR_OK ;
+}
+
+
+/**
+ *Gets the charset string associated to the current
+ *@charset rule statement.
+ *@param a_this the current @charset rule statement.
+ *@param a_charset out parameter. The returned charset string if
+ *and only if the function returned CR_OK.
+ */
+enum CRStatus
+cr_statement_at_charset_rule_get_charset (CRStatement *a_this,
+ GString **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 ;
+}
+
+
+/**
+ *Sets a declaration list to the current @font-face rule statement.
+ *@param a_this the current @font-face rule statement.
+ *@param a_decls the declarations list to set.
+ *@return CR_OK upon successfull 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 ;
+}
+
+
+/**
+ *Gets the declaration list associated to the current instance
+ *of @font-face rule statement.
+ *@param a_this the current @font-face rule statement.
+ *@param a_decls out parameter. The returned declaration list if
+ *and only if this function returns CR_OK.
+ *@return CR_OK upon successfull 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 ;
+}
+
+
+/**
+ *Adds a declaration to the current @font-face rule
+ *statement.
+ *@param a_this the current @font-face rule statement.
+ *@param a_prop the property of the declaration.
+ *@param a_value the value of the declaration.
+ *@return CR_OK upon successfull completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_font_face_rule_add_decl (CRStatement *a_this,
+ GString *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 ;
+}
+
+/**
+ *Dumps the css2 statement to a file.
+ *@param a_this the current css2 statement.
+ *@param a_fp the destination file pointer.
+ *@param a_indent the number of white space indentation characters.
+ */
+void
+cr_statement_dump (CRStatement *a_this, FILE *a_fp, gulong a_indent)
+{
+
+ if (!a_this)
+ return ;
+
+ if (a_this->prev)
+ {
+ fprintf (a_fp,"\n\n") ;
+ }
+
+ switch (a_this->type)
+ {
+ case RULESET_STMT:
+ cr_statement_dump_ruleset (a_this, a_fp, a_indent) ;
+ break ;
+
+ case AT_FONT_FACE_RULE_STMT:
+ cr_statement_dump_font_face_rule
+ (a_this, a_fp, a_indent);
+ break ;
+
+ case AT_CHARSET_RULE_STMT:
+ cr_statement_dump_charset (a_this, a_fp, a_indent) ;
+ break ;
+
+ case AT_PAGE_RULE_STMT:
+ cr_statement_dump_page (a_this, a_fp, a_indent) ;
+ break ;
+
+ case AT_MEDIA_RULE_STMT:
+ cr_statement_dump_media_rule (a_this, a_fp, a_indent) ;
+ break ;
+
+ case AT_IMPORT_RULE_STMT:
+ cr_statement_dump_import_rule (a_this, a_fp, a_indent) ;
+ break ;
+
+ default :
+ fprintf (a_fp, "Statement unrecognized at %s:%d",
+ __FILE__, __LINE__) ;
+ break ;
+ }
+}
+
+/**
+ *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 ;
+}