Libcroco
cr-term.c
Go to the documentation of this file.
00001 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
00002 
00003 /*
00004  * This file is part of The Croco Library
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of version 2.1 of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00018  * USA
00019  *
00020  * Author: Dodji Seketeli
00021  * See COPYRIGHTS file for copyright information.
00022  */
00023 
00024 #include <stdio.h>
00025 #include <string.h>
00026 #include "cr-term.h"
00027 #include "cr-num.h"
00028 #include "cr-parser.h"
00029 
00030 /**
00031  *@file
00032  *Definition of the #CRTem class.
00033  */
00034 
00035 static void
00036 cr_term_clear (CRTerm * a_this)
00037 {
00038         g_return_if_fail (a_this);
00039 
00040         switch (a_this->type) {
00041         case TERM_NUMBER:
00042                 if (a_this->content.num) {
00043                         cr_num_destroy (a_this->content.num);
00044                         a_this->content.num = NULL;
00045                 }
00046                 break;
00047 
00048         case TERM_FUNCTION:
00049                 if (a_this->ext_content.func_param) {
00050                         cr_term_destroy (a_this->ext_content.func_param);
00051                         a_this->ext_content.func_param = NULL;
00052                 }
00053         case TERM_STRING:
00054         case TERM_IDENT:
00055         case TERM_URI:
00056         case TERM_HASH:
00057                 if (a_this->content.str) {
00058                         cr_string_destroy (a_this->content.str);
00059                         a_this->content.str = NULL;
00060                 }
00061                 break;
00062 
00063         case TERM_RGB:
00064                 if (a_this->content.rgb) {
00065                         cr_rgb_destroy (a_this->content.rgb);
00066                         a_this->content.rgb = NULL;
00067                 }
00068                 break;
00069 
00070         case TERM_UNICODERANGE:
00071         case TERM_NO_TYPE:
00072         default:
00073                 break;
00074         }
00075 
00076         a_this->type = TERM_NO_TYPE;
00077 }
00078 
00079 /**
00080  *Instanciate a #CRTerm.
00081  *@return the newly build instance
00082  *of #CRTerm.
00083  */
00084 CRTerm *
00085 cr_term_new (void)
00086 {
00087         CRTerm *result = NULL;
00088 
00089         result = g_try_malloc (sizeof (CRTerm));
00090         if (!result) {
00091                 cr_utils_trace_info ("Out of memory");
00092                 return NULL;
00093         }
00094         memset (result, 0, sizeof (CRTerm));
00095         return result;
00096 }
00097 
00098 /**
00099  *Parses an expresion as defined by the css2 spec
00100  *and builds the expression as a list of terms.
00101  *@param a_buf the buffer to parse.
00102  *@return a pointer to the first term of the expression or
00103  *NULL if parsing failed.
00104  */
00105 CRTerm *
00106 cr_term_parse_expression_from_buf (const guchar * a_buf,
00107                                    enum CREncoding a_encoding)
00108 {
00109         CRParser *parser = NULL;
00110         CRTerm *result = NULL;
00111         enum CRStatus status = CR_OK;
00112 
00113         g_return_val_if_fail (a_buf, NULL);
00114 
00115         parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
00116                                          a_encoding, FALSE);
00117         g_return_val_if_fail (parser, NULL);
00118 
00119         status = cr_parser_try_to_skip_spaces_and_comments (parser);
00120         if (status != CR_OK) {
00121                 goto cleanup;
00122         }
00123         status = cr_parser_parse_expr (parser, &result);
00124         if (status != CR_OK) {
00125                 if (result) {
00126                         cr_term_destroy (result);
00127                         result = NULL;
00128                 }
00129         }
00130 
00131       cleanup:
00132         if (parser) {
00133                 cr_parser_destroy (parser);
00134                 parser = NULL;
00135         }
00136 
00137         return result;
00138 }
00139 
00140 enum CRStatus
00141 cr_term_set_number (CRTerm * a_this, CRNum * a_num)
00142 {
00143         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
00144 
00145         cr_term_clear (a_this);
00146 
00147         a_this->type = TERM_NUMBER;
00148         a_this->content.num = a_num;
00149         return CR_OK;
00150 }
00151 
00152 enum CRStatus
00153 cr_term_set_function (CRTerm * a_this, CRString * a_func_name,
00154                       CRTerm * a_func_param)
00155 {
00156         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
00157 
00158         cr_term_clear (a_this);
00159 
00160         a_this->type = TERM_FUNCTION;
00161         a_this->content.str = a_func_name;
00162         a_this->ext_content.func_param = a_func_param;
00163         return CR_OK;
00164 }
00165 
00166 enum CRStatus
00167 cr_term_set_string (CRTerm * a_this, CRString * a_str)
00168 {
00169         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
00170 
00171         cr_term_clear (a_this);
00172 
00173         a_this->type = TERM_STRING;
00174         a_this->content.str = a_str;
00175         return CR_OK;
00176 }
00177 
00178 enum CRStatus
00179 cr_term_set_ident (CRTerm * a_this, CRString * a_str)
00180 {
00181         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
00182 
00183         cr_term_clear (a_this);
00184 
00185         a_this->type = TERM_IDENT;
00186         a_this->content.str = a_str;
00187         return CR_OK;
00188 }
00189 
00190 enum CRStatus
00191 cr_term_set_uri (CRTerm * a_this, CRString * a_str)
00192 {
00193         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
00194 
00195         cr_term_clear (a_this);
00196 
00197         a_this->type = TERM_URI;
00198         a_this->content.str = a_str;
00199         return CR_OK;
00200 }
00201 
00202 enum CRStatus
00203 cr_term_set_rgb (CRTerm * a_this, CRRgb * a_rgb)
00204 {
00205         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
00206 
00207         cr_term_clear (a_this);
00208 
00209         a_this->type = TERM_RGB;
00210         a_this->content.rgb = a_rgb;
00211         return CR_OK;
00212 }
00213 
00214 enum CRStatus
00215 cr_term_set_hash (CRTerm * a_this, CRString * a_str)
00216 {
00217         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
00218 
00219         cr_term_clear (a_this);
00220 
00221         a_this->type = TERM_HASH;
00222         a_this->content.str = a_str;
00223         return CR_OK;
00224 }
00225 
00226 /**
00227  *Appends a new term to the current list of #CRTerm.
00228  *
00229  *@param a_this the "this pointer" of the current instance
00230  *of #CRTerm .
00231  *@param a_new_term the term to append.
00232  *@return the list of terms with the a_new_term appended to it.
00233  */
00234 CRTerm *
00235 cr_term_append_term (CRTerm * a_this, CRTerm * a_new_term)
00236 {
00237         CRTerm *cur = NULL;
00238 
00239         g_return_val_if_fail (a_new_term, NULL);
00240 
00241         if (a_this == NULL)
00242                 return a_new_term;
00243 
00244         for (cur = a_this; cur->next; cur = cur->next) ;
00245 
00246         cur->next = a_new_term;
00247         a_new_term->prev = cur;
00248 
00249         return a_this;
00250 }
00251 
00252 /**
00253  *Prepends a term to the list of terms represented by a_this.
00254  *
00255  *@param a_this the "this pointer" of the current instance of
00256  *#CRTerm .
00257  *@param a_new_term the term to prepend.
00258  *@return the head of the new list.
00259  */
00260 CRTerm *
00261 cr_term_prepend_term (CRTerm * a_this, CRTerm * a_new_term)
00262 {
00263         g_return_val_if_fail (a_this && a_new_term, NULL);
00264 
00265         a_new_term->next = a_this;
00266         a_this->prev = a_new_term;
00267 
00268         return a_new_term;
00269 }
00270 
00271 /**
00272  *Serializes the expression represented by
00273  *the chained instances of #CRterm.
00274  *@param a_this the current instance of #CRTerm
00275  *@return the zero terminated string containing the serialized
00276  *form of #CRTerm. MUST BE FREED BY THE CALLER using g_free().
00277  */
00278 guchar *
00279 cr_term_to_string (CRTerm const * a_this)
00280 {
00281         GString *str_buf = NULL;
00282         CRTerm const *cur = NULL;
00283         guchar *result = NULL,
00284                 *content = NULL;
00285 
00286         g_return_val_if_fail (a_this, NULL);
00287 
00288         str_buf = g_string_new (NULL);
00289         g_return_val_if_fail (str_buf, NULL);
00290 
00291         for (cur = a_this; cur; cur = cur->next) {
00292                 if ((cur->content.str == NULL)
00293                     && (cur->content.num == NULL)
00294                     && (cur->content.str == NULL)
00295                     && (cur->content.rgb == NULL))
00296                         continue;
00297 
00298                 switch (cur->the_operator) {
00299                 case DIVIDE:
00300                         g_string_append (str_buf, " / ");
00301                         break;
00302 
00303                 case COMMA:
00304                         g_string_append (str_buf, ", ");
00305                         break;
00306 
00307                 case NO_OP:
00308                         if (cur->prev) {
00309                                 g_string_append (str_buf, " ");
00310                         }
00311                         break;
00312                 default:
00313 
00314                         break;
00315                 }
00316 
00317                 switch (cur->unary_op) {
00318                 case PLUS_UOP:
00319                         g_string_append (str_buf, "+");
00320                         break;
00321 
00322                 case MINUS_UOP:
00323                         g_string_append (str_buf, "-");
00324                         break;
00325 
00326                 default:
00327                         break;
00328                 }
00329 
00330                 switch (cur->type) {
00331                 case TERM_NUMBER:
00332                         if (cur->content.num) {
00333                                 content = cr_num_to_string (cur->content.num);
00334                         }
00335 
00336                         if (content) {
00337                                 g_string_append (str_buf, (const gchar *) content);
00338                                 g_free (content);
00339                                 content = NULL;
00340                         }
00341 
00342                         break;
00343 
00344                 case TERM_FUNCTION:
00345                         if (cur->content.str) {
00346                                 content = (guchar *) g_strndup
00347                                         (cur->content.str->stryng->str,
00348                                          cur->content.str->stryng->len);
00349                         }
00350 
00351                         if (content) {
00352                                 g_string_append_printf (str_buf, "%s(",
00353                                                         content);
00354 
00355                                 if (cur->ext_content.func_param) {
00356                                         guchar *tmp_str = NULL;
00357 
00358                                         tmp_str = cr_term_to_string
00359                                                 (cur->
00360                                                  ext_content.func_param);
00361 
00362                                         if (tmp_str) {
00363                                                 g_string_append (str_buf, 
00364                                                                  (const gchar *) tmp_str);
00365                                                 g_free (tmp_str);
00366                                                 tmp_str = NULL;
00367                                         }
00368                                 }
00369                                 g_string_append (str_buf, ")");
00370                                 g_free (content);
00371                                 content = NULL;
00372                         }
00373 
00374                         break;
00375 
00376                 case TERM_STRING:
00377                         if (cur->content.str) {
00378                                 content = (guchar *) g_strndup
00379                                         (cur->content.str->stryng->str,
00380                                          cur->content.str->stryng->len);
00381                         }
00382 
00383                         if (content) {
00384                                 g_string_append_printf (str_buf,
00385                                                         "\"%s\"", content);
00386                                 g_free (content);
00387                                 content = NULL;
00388                         }
00389                         break;
00390 
00391                 case TERM_IDENT:
00392                         if (cur->content.str) {
00393                                 content = (guchar *) g_strndup
00394                                         (cur->content.str->stryng->str,
00395                                          cur->content.str->stryng->len);
00396                         }
00397 
00398                         if (content) {
00399                                 g_string_append (str_buf, (const gchar *) content);
00400                                 g_free (content);
00401                                 content = NULL;
00402                         }
00403                         break;
00404 
00405                 case TERM_URI:
00406                         if (cur->content.str) {
00407                                 content = (guchar *) g_strndup
00408                                         (cur->content.str->stryng->str,
00409                                          cur->content.str->stryng->len);
00410                         }
00411 
00412                         if (content) {
00413                                 g_string_append_printf
00414                                         (str_buf, "url(%s)", content);
00415                                 g_free (content);
00416                                 content = NULL;
00417                         }
00418                         break;
00419 
00420                 case TERM_RGB:
00421                         if (cur->content.rgb) {
00422                                 guchar *tmp_str = NULL;
00423 
00424                                 g_string_append (str_buf, "rgb(");
00425                                 tmp_str = cr_rgb_to_string (cur->content.rgb);
00426 
00427                                 if (tmp_str) {
00428                                         g_string_append (str_buf, (const gchar *) tmp_str);
00429                                         g_free (tmp_str);
00430                                         tmp_str = NULL;
00431                                 }
00432                                 g_string_append (str_buf, ")");
00433                         }
00434 
00435                         break;
00436 
00437                 case TERM_UNICODERANGE:
00438                         g_string_append
00439                                 (str_buf,
00440                                  "?found unicoderange: dump not supported yet?");
00441                         break;
00442 
00443                 case TERM_HASH:
00444                         if (cur->content.str) {
00445                                 content = (guchar *) g_strndup
00446                                         (cur->content.str->stryng->str,
00447                                          cur->content.str->stryng->len);
00448                         }
00449 
00450                         if (content) {
00451                                 g_string_append_printf (str_buf,
00452                                                         "#%s", content);
00453                                 g_free (content);
00454                                 content = NULL;
00455                         }
00456                         break;
00457 
00458                 default:
00459                         g_string_append (str_buf,
00460                                          "Unrecognized Term type");
00461                         break;
00462                 }
00463         }
00464 
00465         if (str_buf) {
00466                 result =(guchar *) str_buf->str;
00467                 g_string_free (str_buf, FALSE);
00468                 str_buf = NULL;
00469         }
00470 
00471         return result;
00472 }
00473 
00474 guchar *
00475 cr_term_one_to_string (CRTerm const * a_this)
00476 {
00477         GString *str_buf = NULL;
00478         guchar *result = NULL,
00479                 *content = NULL;
00480 
00481         g_return_val_if_fail (a_this, NULL);
00482 
00483         str_buf = g_string_new (NULL);
00484         g_return_val_if_fail (str_buf, NULL);
00485 
00486         if ((a_this->content.str == NULL)
00487             && (a_this->content.num == NULL)
00488             && (a_this->content.str == NULL)
00489             && (a_this->content.rgb == NULL))
00490                 return NULL ;
00491 
00492         switch (a_this->the_operator) {
00493         case DIVIDE:
00494                 g_string_append_printf (str_buf, " / ");
00495                 break;
00496 
00497         case COMMA:
00498                 g_string_append_printf (str_buf, ", ");
00499                 break;
00500 
00501         case NO_OP:
00502                 if (a_this->prev) {
00503                         g_string_append_printf (str_buf, " ");
00504                 }
00505                 break;
00506         default:
00507 
00508                 break;
00509         }
00510 
00511         switch (a_this->unary_op) {
00512         case PLUS_UOP:
00513                 g_string_append_printf (str_buf, "+");
00514                 break;
00515 
00516         case MINUS_UOP:
00517                 g_string_append_printf (str_buf, "-");
00518                 break;
00519 
00520         default:
00521                 break;
00522         }
00523 
00524         switch (a_this->type) {
00525         case TERM_NUMBER:
00526                 if (a_this->content.num) {
00527                         content = cr_num_to_string (a_this->content.num);
00528                 }
00529 
00530                 if (content) {
00531                         g_string_append (str_buf, (const gchar *) content);
00532                         g_free (content);
00533                         content = NULL;
00534                 }
00535 
00536                 break;
00537 
00538         case TERM_FUNCTION:
00539                 if (a_this->content.str) {
00540                         content = (guchar *) g_strndup
00541                                 (a_this->content.str->stryng->str,
00542                                  a_this->content.str->stryng->len);
00543                 }
00544 
00545                 if (content) {
00546                         g_string_append_printf (str_buf, "%s(",
00547                                                 content);
00548 
00549                         if (a_this->ext_content.func_param) {
00550                                 guchar *tmp_str = NULL;
00551 
00552                                 tmp_str = cr_term_to_string
00553                                         (a_this->
00554                                          ext_content.func_param);
00555 
00556                                 if (tmp_str) {
00557                                         g_string_append_printf
00558                                                 (str_buf,
00559                                                  "%s", tmp_str);
00560                                         g_free (tmp_str);
00561                                         tmp_str = NULL;
00562                                 }
00563 
00564                                 g_string_append_printf (str_buf, ")");
00565                                 g_free (content);
00566                                 content = NULL;
00567                         }
00568                 }
00569 
00570                 break;
00571 
00572         case TERM_STRING:
00573                 if (a_this->content.str) {
00574                         content = (guchar *) g_strndup
00575                                 (a_this->content.str->stryng->str,
00576                                  a_this->content.str->stryng->len);
00577                 }
00578 
00579                 if (content) {
00580                         g_string_append_printf (str_buf,
00581                                                 "\"%s\"", content);
00582                         g_free (content);
00583                         content = NULL;
00584                 }
00585                 break;
00586 
00587         case TERM_IDENT:
00588                 if (a_this->content.str) {
00589                         content = (guchar *) g_strndup
00590                                 (a_this->content.str->stryng->str,
00591                                  a_this->content.str->stryng->len);
00592                 }
00593 
00594                 if (content) {
00595                         g_string_append (str_buf, (const gchar *) content);
00596                         g_free (content);
00597                         content = NULL;
00598                 }
00599                 break;
00600 
00601         case TERM_URI:
00602                 if (a_this->content.str) {
00603                         content = (guchar *) g_strndup
00604                                 (a_this->content.str->stryng->str,
00605                                  a_this->content.str->stryng->len);
00606                 }
00607 
00608                 if (content) {
00609                         g_string_append_printf
00610                                 (str_buf, "url(%s)", content);
00611                         g_free (content);
00612                         content = NULL;
00613                 }
00614                 break;
00615 
00616         case TERM_RGB:
00617                 if (a_this->content.rgb) {
00618                         guchar *tmp_str = NULL;
00619 
00620                         g_string_append_printf (str_buf, "rgb(");
00621                         tmp_str = cr_rgb_to_string (a_this->content.rgb);
00622 
00623                         if (tmp_str) {
00624                                 g_string_append (str_buf, (const gchar *) tmp_str);
00625                                 g_free (tmp_str);
00626                                 tmp_str = NULL;
00627                         }
00628                         g_string_append_printf (str_buf, ")");
00629                 }
00630 
00631                 break;
00632 
00633         case TERM_UNICODERANGE:
00634                 g_string_append_printf
00635                         (str_buf,
00636                          "?found unicoderange: dump not supported yet?");
00637                 break;
00638 
00639         case TERM_HASH:
00640                 if (a_this->content.str) {
00641                         content = (guchar *) g_strndup
00642                                 (a_this->content.str->stryng->str,
00643                                  a_this->content.str->stryng->len);
00644                 }
00645 
00646                 if (content) {
00647                         g_string_append_printf (str_buf,
00648                                                 "#%s", content);
00649                         g_free (content);
00650                         content = NULL;
00651                 }
00652                 break;
00653 
00654         default:
00655                 g_string_append_printf (str_buf,
00656                                         "%s",
00657                                         "Unrecognized Term type");
00658                 break;
00659         }
00660 
00661         if (str_buf) {
00662                 result = (guchar *) str_buf->str;
00663                 g_string_free (str_buf, FALSE);
00664                 str_buf = NULL;
00665         }
00666 
00667         return result;
00668 }
00669 
00670 /**
00671  *Dumps the expression (a list of terms connected by operators)
00672  *to a file.
00673  *TODO: finish the dump. The dump of some type of terms have not yet been
00674  *implemented.
00675  *@param a_this the current instance of #CRTerm.
00676  *@param a_fp the destination file pointer.
00677  */
00678 void
00679 cr_term_dump (CRTerm const * a_this, FILE * a_fp)
00680 {
00681         guchar *content = NULL;
00682 
00683         g_return_if_fail (a_this);
00684 
00685         content = cr_term_to_string (a_this);
00686 
00687         if (content) {
00688                 fprintf (a_fp, "%s", content);
00689                 g_free (content);
00690         }
00691 }
00692 
00693 /**
00694  *Return the number of terms in the expression.
00695  *@param a_this the current instance of #CRTerm.
00696  *@return number of terms in the expression.
00697  */
00698 int
00699 cr_term_nr_values (CRTerm const *a_this)
00700 {
00701         CRTerm const *cur = NULL ;
00702         int nr = 0;
00703 
00704         g_return_val_if_fail (a_this, -1) ;
00705 
00706         for (cur = a_this ; cur ; cur = cur->next)
00707                 nr ++;
00708         return nr;
00709 }
00710 
00711 /**
00712  *Use an index to get a CRTerm from the expression.
00713  *@param a_this the current instance of #CRTerm.
00714  *@param itemnr the index into the expression.
00715  *@return CRTerm at position itemnr, if itemnr > number of terms - 1,
00716  *it will return NULL.
00717  */
00718 CRTerm *
00719 cr_term_get_from_list (CRTerm *a_this, int itemnr)
00720 {
00721         CRTerm *cur = NULL ;
00722         int nr = 0;
00723 
00724         g_return_val_if_fail (a_this, NULL) ;
00725 
00726         for (cur = a_this ; cur ; cur = cur->next)
00727                 if (nr++ == itemnr)
00728                         return cur;
00729         return NULL;
00730 }
00731 
00732 /**
00733  *Increments the reference counter of the current instance
00734  *of #CRTerm.*
00735  *@param a_this the current instance of #CRTerm.
00736  */
00737 void
00738 cr_term_ref (CRTerm * a_this)
00739 {
00740         g_return_if_fail (a_this);
00741 
00742         a_this->ref_count++;
00743 }
00744 
00745 /**
00746  *Decrements the ref count of the current instance of
00747  *#CRTerm. If the ref count reaches zero, the instance is
00748  *destroyed.
00749  *@param a_this the current instance of #CRTerm.
00750  *@return TRUE if the current instance has been destroyed, FALSE otherwise.
00751  */
00752 gboolean
00753 cr_term_unref (CRTerm * a_this)
00754 {
00755         g_return_val_if_fail (a_this, FALSE);
00756 
00757         if (a_this->ref_count) {
00758                 a_this->ref_count--;
00759         }
00760 
00761         if (a_this->ref_count == 0) {
00762                 cr_term_destroy (a_this);
00763                 return TRUE;
00764         }
00765 
00766         return FALSE;
00767 }
00768 
00769 /**
00770  *The destructor of the the #CRTerm class.
00771  *@param a_this the "this pointer" of the current instance
00772  *of #CRTerm.
00773  */
00774 void
00775 cr_term_destroy (CRTerm * a_this)
00776 {
00777         g_return_if_fail (a_this);
00778 
00779         cr_term_clear (a_this);
00780 
00781         if (a_this->next) {
00782                 cr_term_destroy (a_this->next);
00783                 a_this->next = NULL;
00784         }
00785 
00786         if (a_this) {
00787                 g_free (a_this);
00788         }
00789 
00790 }