/* -*- 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;
}
/**
*Instanciate a #CRTerm.
*@return the newly build instance
*of #CRTerm.
*/
CRTerm *
cr_term_new (void)
{
CRTerm *result = NULL;
result = (CRTerm *) g_try_malloc (sizeof (CRTerm));
if (!result) {
cr_utils_trace_info ("Out of memory");
return NULL;
}
memset (result, 0, sizeof (CRTerm));
return result;
}
/**
*Parses an expresion as defined by the css2 spec
*and builds the expression as a list of terms.
*@param a_buf the buffer to parse.
*@return a pointer to the first term of the expression or
*NULL if parsing failed.
*/
CRTerm *
cr_term_parse_expression_from_buf (const guchar * a_buf,
enum CREncoding a_encoding)
{
CRParser *parser = NULL;
CRTerm *result = NULL;
enum CRStatus status = CR_OK;
g_return_val_if_fail (a_buf, NULL);
parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
a_encoding, FALSE);
g_return_val_if_fail (parser, NULL);
status = cr_parser_try_to_skip_spaces_and_comments (parser);
if (status != CR_OK) {
goto cleanup;
}
status = cr_parser_parse_expr (parser, &result);
if (status != CR_OK) {
if (result) {
cr_term_destroy (result);
result = NULL;
}
}
cleanup:
if (parser) {
cr_parser_destroy (parser);
parser = NULL;
}
return result;
}
enum CRStatus
cr_term_set_number (CRTerm * a_this, CRNum * a_num)
{
g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
cr_term_clear (a_this);
a_this->type = TERM_NUMBER;
a_this->content.num = a_num;
return CR_OK;
}
enum CRStatus
cr_term_set_function (CRTerm * a_this, CRString * a_func_name,
CRTerm * a_func_param)
{
g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
cr_term_clear (a_this);
a_this->type = TERM_FUNCTION;
a_this->content.str = a_func_name;
a_this->ext_content.func_param = a_func_param;
return CR_OK;
}
enum CRStatus
cr_term_set_string (CRTerm * a_this, CRString * a_str)
{
g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
cr_term_clear (a_this);
a_this->type = TERM_STRING;
a_this->content.str = a_str;
return CR_OK;
}
enum CRStatus
cr_term_set_ident (CRTerm * a_this, CRString * a_str)
{
g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
cr_term_clear (a_this);
a_this->type = TERM_IDENT;
a_this->content.str = a_str;
return CR_OK;
}
enum CRStatus
cr_term_set_uri (CRTerm * a_this, CRString * a_str)
{
g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
cr_term_clear (a_this);
a_this->type = TERM_URI;
a_this->content.str = a_str;
return CR_OK;
}
enum CRStatus
cr_term_set_rgb (CRTerm * a_this, CRRgb * a_rgb)
{
g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
cr_term_clear (a_this);
a_this->type = TERM_RGB;
a_this->content.rgb = a_rgb;
return CR_OK;
}
enum CRStatus
cr_term_set_hash (CRTerm * a_this, CRString * a_str)
{
g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
cr_term_clear (a_this);
a_this->type = TERM_HASH;
a_this->content.str = a_str;
return CR_OK;
}
/**
*Appends a new term to the current list of #CRTerm.
*
*@param a_this the "this pointer" of the current instance
*of #CRTerm .
*@param a_new_term the term to append.
*@return the list of terms with the a_new_term appended to it.
*/
CRTerm *
cr_term_append_term (CRTerm * a_this, CRTerm * a_new_term)
{
CRTerm *cur = NULL;
g_return_val_if_fail (a_new_term, NULL);
if (a_this == NULL)
return a_new_term;
for (cur = a_this; cur->next; cur = cur->next) ;
cur->next = a_new_term;
a_new_term->prev = cur;
return a_this;
}
/**
*Prepends a term to the list of terms represented by a_this.
*
*@param a_this the "this pointer" of the current instance of
*#CRTerm .
*@param a_new_term the term to prepend.
*@return the head of the new list.
*/
CRTerm *
cr_term_prepend_term (CRTerm * a_this, CRTerm * a_new_term)
{
g_return_val_if_fail (a_this && a_new_term, NULL);
a_new_term->next = a_this;
a_this->prev = a_new_term;
return a_new_term;
}
/**
*Serializes the expression represented by
*the chained instances of #CRterm.
*@param a_this the current instance of #CRTerm
*@return the zero terminated string containing the serialized
*form of #CRTerm. MUST BE FREED BY THE CALLER using g_free().
*/
guchar *
cr_term_to_string (CRTerm const * a_this)
{
GString *str_buf = NULL;
CRTerm const *cur = NULL;
guchar *result = NULL,
*content = NULL;
g_return_val_if_fail (a_this, NULL);
str_buf = g_string_new (NULL);
g_return_val_if_fail (str_buf, NULL);
for (cur = a_this; cur; cur = cur->next) {
if ((cur->content.str == NULL)
&& (cur->content.num == NULL)
&& (cur->content.str == NULL)
&& (cur->content.rgb == NULL))
continue;
switch (cur->the_operator) {
case DIVIDE:
g_string_append (str_buf, " / ");
break;
case COMMA:
g_string_append (str_buf, ", ");
break;
case NO_OP:
if (cur->prev) {
g_string_append (str_buf, " ");
}
break;
default:
break;
}
switch (cur->unary_op) {
case PLUS_UOP:
g_string_append (str_buf, "+");
break;
case MINUS_UOP:
g_string_append (str_buf, "-");
break;
default:
break;
}
switch (cur->type) {
case TERM_NUMBER:
if (cur->content.num) {
content = cr_num_to_string (cur->content.num);
}
if (content) {
g_string_append (str_buf, (const gchar *) content);
g_free (content);
content = NULL;
}
break;
case TERM_FUNCTION:
if (cur->content.str) {
content = (guchar *) g_strndup
(cur->content.str->stryng->str,
cur->content.str->stryng->len);
}
if (content) {
g_string_append_printf (str_buf, "%s(",
content);
if (cur->ext_content.func_param) {
guchar *tmp_str = NULL;
tmp_str = cr_term_to_string
(cur->
ext_content.func_param);
if (tmp_str) {
g_string_append (str_buf,
(const gchar *) tmp_str);
g_free (tmp_str);
tmp_str = NULL;
}
}
g_string_append (str_buf, ")");
g_free (content);
content = NULL;
}
break;
case TERM_STRING:
if (cur->content.str) {
content = (guchar *) g_strndup
(cur->content.str->stryng->str,
cur->content.str->stryng->len);
}
if (content) {
g_string_append_printf (str_buf,
"\"%s\"", content);
g_free (content);
content = NULL;
}
break;
case TERM_IDENT:
if (cur->content.str) {
content = (guchar *) g_strndup
(cur->content.str->stryng->str,
cur->content.str->stryng->len);
}
if (content) {
g_string_append (str_buf, (const gchar *) content);
g_free (content);
content = NULL;
}
break;
case TERM_URI:
if (cur->content.str) {
content = (guchar *) g_strndup
(cur->content.str->stryng->str,
cur->content.str->stryng->len);
}
if (content) {
g_string_append_printf
(str_buf, "url(%s)", content);
g_free (content);
content = NULL;
}
break;
case TERM_RGB:
if (cur->content.rgb) {
guchar *tmp_str = NULL;
g_string_append (str_buf, "rgb(");
tmp_str = cr_rgb_to_string (cur->content.rgb);
if (tmp_str) {
g_string_append (str_buf, (const gchar *) tmp_str);
g_free (tmp_str);
tmp_str = NULL;
}
g_string_append (str_buf, ")");
}
break;
case TERM_UNICODERANGE:
g_string_append
(str_buf,
"?found unicoderange: dump not supported yet?");
break;
case TERM_HASH:
if (cur->content.str) {
content = (guchar *) g_strndup
(cur->content.str->stryng->str,
cur->content.str->stryng->len);
}
if (content) {
g_string_append_printf (str_buf,
"#%s", content);
g_free (content);
content = NULL;
}
break;
default:
g_string_append (str_buf,
"Unrecognized Term type");
break;
}
}
if (str_buf) {
result =(guchar *) str_buf->str;
g_string_free (str_buf, FALSE);
str_buf = NULL;
}
return result;
}
guchar *
cr_term_one_to_string (CRTerm const * a_this)
{
GString *str_buf = NULL;
guchar *result = NULL,
*content = NULL;
g_return_val_if_fail (a_this, NULL);
str_buf = g_string_new (NULL);
g_return_val_if_fail (str_buf, NULL);
if ((a_this->content.str == NULL)
&& (a_this->content.num == NULL)
&& (a_this->content.str == NULL)
&& (a_this->content.rgb == NULL))
return NULL ;
switch (a_this->the_operator) {
case DIVIDE:
g_string_append_printf (str_buf, " / ");
break;
case COMMA:
g_string_append_printf (str_buf, ", ");
break;
case NO_OP:
if (a_this->prev) {
g_string_append_printf (str_buf, " ");
}
break;
default:
break;
}
switch (a_this->unary_op) {
case PLUS_UOP:
g_string_append_printf (str_buf, "+");
break;
case MINUS_UOP:
g_string_append_printf (str_buf, "-");
break;
default:
break;
}
switch (a_this->type) {
case TERM_NUMBER:
if (a_this->content.num) {
content = cr_num_to_string (a_this->content.num);
}
if (content) {
g_string_append (str_buf, (const gchar *) content);
g_free (content);
content = NULL;
}
break;
case TERM_FUNCTION:
if (a_this->content.str) {
content = (guchar *) g_strndup
(a_this->content.str->stryng->str,
a_this->content.str->stryng->len);
}
if (content) {
g_string_append_printf (str_buf, "%s(",
content);
if (a_this->ext_content.func_param) {
guchar *tmp_str = NULL;
tmp_str = cr_term_to_string
(a_this->
ext_content.func_param);
if (tmp_str) {
g_string_append_printf
(str_buf,
"%s", tmp_str);
g_free (tmp_str);
tmp_str = NULL;
}
g_string_append_printf (str_buf, ")");
g_free (content);
content = NULL;
}
}
break;
case TERM_STRING:
if (a_this->content.str) {
content = (guchar *) g_strndup
(a_this->content.str->stryng->str,
a_this->content.str->stryng->len);
}
if (content) {
g_string_append_printf (str_buf,
"\"%s\"", content);
g_free (content);
content = NULL;
}
break;
case TERM_IDENT:
if (a_this->content.str) {
content = (guchar *) g_strndup
(a_this->content.str->stryng->str,
a_this->content.str->stryng->len);
}
if (content) {
g_string_append (str_buf, (const gchar *) content);
g_free (content);
content = NULL;
}
break;
case TERM_URI:
if (a_this->content.str) {
content = (guchar *) g_strndup
(a_this->content.str->stryng->str,
a_this->content.str->stryng->len);
}
if (content) {
g_string_append_printf
(str_buf, "url(%s)", content);
g_free (content);
content = NULL;
}
break;
case TERM_RGB:
if (a_this->content.rgb) {
guchar *tmp_str = NULL;
g_string_append_printf (str_buf, "rgb(");
tmp_str = cr_rgb_to_string (a_this->content.rgb);
if (tmp_str) {
g_string_append (str_buf, (const gchar *) tmp_str);
g_free (tmp_str);
tmp_str = NULL;
}
g_string_append_printf (str_buf, ")");
}
break;
case TERM_UNICODERANGE:
g_string_append_printf
(str_buf,
"?found unicoderange: dump not supported yet?");
break;
case TERM_HASH:
if (a_this->content.str) {
content = (guchar *) g_strndup
(a_this->content.str->stryng->str,
a_this->content.str->stryng->len);
}
if (content) {
g_string_append_printf (str_buf,
"#%s", content);
g_free (content);
content = NULL;
}
break;
default:
g_string_append_printf (str_buf,
"%s",
"Unrecognized Term type");
break;
}
if (str_buf) {
result = (guchar *) str_buf->str;
g_string_free (str_buf, FALSE);
str_buf = NULL;
}
return result;
}
/**
*Dumps the expression (a list of terms connected by operators)
*to a file.
*TODO: finish the dump. The dump of some type of terms have not yet been
*implemented.
*@param a_this the current instance of #CRTerm.
*@param a_fp the destination file pointer.
*/
void
cr_term_dump (CRTerm const * a_this, FILE * a_fp)
{
guchar *content = NULL;
g_return_if_fail (a_this);
content = cr_term_to_string (a_this);
if (content) {
fprintf (a_fp, "%s", content);
g_free (content);
}
}
/**
*Return the number of terms in the expression.
*@param a_this the current instance of #CRTerm.
*@return number of terms in the expression.
*/
int
cr_term_nr_values (CRTerm const *a_this)
{
CRTerm const *cur = NULL ;
int nr = 0;
g_return_val_if_fail (a_this, -1) ;
for (cur = a_this ; cur ; cur = cur->next)
nr ++;
return nr;
}
/**
*Use an index to get a CRTerm from the expression.
*@param a_this the current instance of #CRTerm.
*@param itemnr the index into the expression.
*@return CRTerm at position itemnr, if itemnr > number of terms - 1,
*it will return NULL.
*/
CRTerm *
cr_term_get_from_list (CRTerm *a_this, int itemnr)
{
CRTerm *cur = NULL ;
int nr = 0;
g_return_val_if_fail (a_this, NULL) ;
for (cur = a_this ; cur ; cur = cur->next)
if (nr++ == itemnr)
return cur;
return NULL;
}
/**
*Increments the reference counter of the current instance
*of #CRTerm.*
*@param a_this the current instance of #CRTerm.
*/
void
cr_term_ref (CRTerm * a_this)
{
g_return_if_fail (a_this);
a_this->ref_count++;
}
/**
*Decrements the ref count of the current instance of
*#CRTerm. If the ref count reaches zero, the instance is
*destroyed.
*@param a_this the current instance of #CRTerm.
*@return TRUE if the current instance has been destroyed, FALSE otherwise.
*/
gboolean
cr_term_unref (CRTerm * a_this)
{
g_return_val_if_fail (a_this, FALSE);
if (a_this->ref_count) {
a_this->ref_count--;
}
if (a_this->ref_count == 0) {
cr_term_destroy (a_this);
return TRUE;
}
return FALSE;
}
/**
*The destructor of the #CRTerm class.
*@param a_this the "this pointer" of the current instance
*of #CRTerm.
*/
void
cr_term_destroy (CRTerm * const 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;
}
g_free (a_this);
}