/* -*- 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; } /** *Instantiate 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 expression 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.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 *) 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.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 *) 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); } }