diff options
Diffstat (limited to 'src/st/croco/cr-term.c')
-rw-r--r-- | src/st/croco/cr-term.c | 786 |
1 files changed, 786 insertions, 0 deletions
diff --git a/src/st/croco/cr-term.c b/src/st/croco/cr-term.c new file mode 100644 index 0000000..b527d95 --- /dev/null +++ b/src/st/croco/cr-term.c @@ -0,0 +1,786 @@ +/* -*- 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 <string.h> +#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); + } + +} |