/* -*- 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; gboolean first = TRUE; 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; } for (;; first = FALSE) { status = cr_parser_try_to_skip_spaces_and_comments (parser); if (status != CR_OK) goto cleanup; if (!first) { guint32 c = 0; status = cr_tknzr_peek_char (tokenizer, &c); if (status != CR_OK) { goto cleanup; } if (c == ';') { status = cr_tknzr_read_char (tokenizer, &c); } else { cr_tknzr_read_char (tokenizer, &c); continue; // try to keep reading until we reach the end or a ; } important = FALSE; 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_END_OF_INPUT_ERROR) { break; } else { continue; // even if one declaration is broken, it's no reason to discard others (see http://www.w3.org/TR/CSS21/syndata.html#declaration) } } cur_decl = cr_declaration_new (NULL, property, value); if (cur_decl) { cur_decl->important = important; if (result) { result = cr_declaration_append (result, cur_decl); } else { result = cur_decl; } property = NULL; value = NULL; cur_decl = NULL; } else { break; } } cleanup: if (status == CR_END_OF_INPUT_ERROR && result) { status = CR_OK; } 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 successful 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 *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) { gchar const *str = a_this->property->stryng->str; if (str) { cr_utils_dump_n_chars2 (' ', stringue, a_indent); g_string_append (stringue, str); } 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; } 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); }