(root)/
gettext-0.22.4/
libtextstyle/
lib/
libcroco/
cr-term.c
       1  /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
       2  
       3  /* libcroco - Library for parsing and applying CSS
       4   * Copyright (C) 2006-2019 Free Software Foundation, Inc.
       5   *
       6   * This file is not part of the GNU gettext program, but is used with
       7   * GNU gettext.
       8   *
       9   * The original copyright notice is as follows:
      10   */
      11  
      12  /*
      13   * This file is part of The Croco Library
      14   *
      15   * Copyright (C) 2003-2004 Dodji Seketeli.  All Rights Reserved.
      16   *
      17   * This program is free software; you can redistribute it and/or
      18   * modify it under the terms of version 2.1 of the GNU Lesser General Public
      19   * License as published by the Free Software Foundation.
      20   *
      21   * This program is distributed in the hope that it will be useful,
      22   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      23   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      24   * GNU General Public License for more details.
      25   *
      26   * You should have received a copy of the GNU Lesser General Public License
      27   * along with this program; if not, write to the Free Software
      28   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
      29   * USA
      30   *
      31   * Author: Dodji Seketeli
      32   */
      33  
      34  #include <config.h>
      35  #include <stdio.h>
      36  #include <string.h>
      37  #include "cr-term.h"
      38  #include "cr-num.h"
      39  #include "cr-parser.h"
      40  
      41  /**
      42   *@file
      43   *Definition of the #CRTem class.
      44   */
      45  
      46  static void
      47  cr_term_clear (CRTerm * a_this)
      48  {
      49          g_return_if_fail (a_this);
      50  
      51          switch (a_this->type) {
      52          case TERM_NUMBER:
      53                  if (a_this->content.num) {
      54                          cr_num_destroy (a_this->content.num);
      55                          a_this->content.num = NULL;
      56                  }
      57                  break;
      58  
      59          case TERM_FUNCTION:
      60                  if (a_this->ext_content.func_param) {
      61                          cr_term_destroy (a_this->ext_content.func_param);
      62                          a_this->ext_content.func_param = NULL;
      63                  }
      64          case TERM_STRING:
      65          case TERM_IDENT:
      66          case TERM_URI:
      67          case TERM_HASH:
      68                  if (a_this->content.str) {
      69                          cr_string_destroy (a_this->content.str);
      70                          a_this->content.str = NULL;
      71                  }
      72                  break;
      73  
      74          case TERM_RGB:
      75                  if (a_this->content.rgb) {
      76                          cr_rgb_destroy (a_this->content.rgb);
      77                          a_this->content.rgb = NULL;
      78                  }
      79                  break;
      80  
      81          case TERM_UNICODERANGE:
      82          case TERM_NO_TYPE:
      83          default:
      84                  break;
      85          }
      86  
      87          a_this->type = TERM_NO_TYPE;
      88  }
      89  
      90  /**
      91   *Instanciate a #CRTerm.
      92   *@return the newly build instance
      93   *of #CRTerm.
      94   */
      95  CRTerm *
      96  cr_term_new (void)
      97  {
      98          CRTerm *result = NULL;
      99  
     100          result = g_try_malloc (sizeof (CRTerm));
     101          if (!result) {
     102                  cr_utils_trace_info ("Out of memory");
     103                  return NULL;
     104          }
     105          memset (result, 0, sizeof (CRTerm));
     106          return result;
     107  }
     108  
     109  /**
     110   *Parses an expresion as defined by the css2 spec
     111   *and builds the expression as a list of terms.
     112   *@param a_buf the buffer to parse.
     113   *@return a pointer to the first term of the expression or
     114   *NULL if parsing failed.
     115   */
     116  CRTerm *
     117  cr_term_parse_expression_from_buf (const guchar * a_buf,
     118                                     enum CREncoding a_encoding)
     119  {
     120          CRParser *parser = NULL;
     121          CRTerm *result = NULL;
     122          enum CRStatus status = CR_OK;
     123  
     124          g_return_val_if_fail (a_buf, NULL);
     125  
     126          parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
     127                                           a_encoding, FALSE);
     128          g_return_val_if_fail (parser, NULL);
     129  
     130          status = cr_parser_try_to_skip_spaces_and_comments (parser);
     131          if (status != CR_OK) {
     132                  goto cleanup;
     133          }
     134          status = cr_parser_parse_expr (parser, &result);
     135          if (status != CR_OK) {
     136                  if (result) {
     137                          cr_term_destroy (result);
     138                          result = NULL;
     139                  }
     140          }
     141  
     142        cleanup:
     143          if (parser) {
     144                  cr_parser_destroy (parser);
     145                  parser = NULL;
     146          }
     147  
     148          return result;
     149  }
     150  
     151  enum CRStatus
     152  cr_term_set_number (CRTerm * a_this, CRNum * a_num)
     153  {
     154          g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
     155  
     156          cr_term_clear (a_this);
     157  
     158          a_this->type = TERM_NUMBER;
     159          a_this->content.num = a_num;
     160          return CR_OK;
     161  }
     162  
     163  enum CRStatus
     164  cr_term_set_function (CRTerm * a_this, CRString * a_func_name,
     165                        CRTerm * a_func_param)
     166  {
     167          g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
     168  
     169          cr_term_clear (a_this);
     170  
     171          a_this->type = TERM_FUNCTION;
     172          a_this->content.str = a_func_name;
     173          a_this->ext_content.func_param = a_func_param;
     174          return CR_OK;
     175  }
     176  
     177  enum CRStatus
     178  cr_term_set_string (CRTerm * a_this, CRString * a_str)
     179  {
     180          g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
     181  
     182          cr_term_clear (a_this);
     183  
     184          a_this->type = TERM_STRING;
     185          a_this->content.str = a_str;
     186          return CR_OK;
     187  }
     188  
     189  enum CRStatus
     190  cr_term_set_ident (CRTerm * a_this, CRString * a_str)
     191  {
     192          g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
     193  
     194          cr_term_clear (a_this);
     195  
     196          a_this->type = TERM_IDENT;
     197          a_this->content.str = a_str;
     198          return CR_OK;
     199  }
     200  
     201  enum CRStatus
     202  cr_term_set_uri (CRTerm * a_this, CRString * a_str)
     203  {
     204          g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
     205  
     206          cr_term_clear (a_this);
     207  
     208          a_this->type = TERM_URI;
     209          a_this->content.str = a_str;
     210          return CR_OK;
     211  }
     212  
     213  enum CRStatus
     214  cr_term_set_rgb (CRTerm * a_this, CRRgb * a_rgb)
     215  {
     216          g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
     217  
     218          cr_term_clear (a_this);
     219  
     220          a_this->type = TERM_RGB;
     221          a_this->content.rgb = a_rgb;
     222          return CR_OK;
     223  }
     224  
     225  enum CRStatus
     226  cr_term_set_hash (CRTerm * a_this, CRString * a_str)
     227  {
     228          g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
     229  
     230          cr_term_clear (a_this);
     231  
     232          a_this->type = TERM_HASH;
     233          a_this->content.str = a_str;
     234          return CR_OK;
     235  }
     236  
     237  /**
     238   *Appends a new term to the current list of #CRTerm.
     239   *
     240   *@param a_this the "this pointer" of the current instance
     241   *of #CRTerm .
     242   *@param a_new_term the term to append.
     243   *@return the list of terms with the a_new_term appended to it.
     244   */
     245  CRTerm *
     246  cr_term_append_term (CRTerm * a_this, CRTerm * a_new_term)
     247  {
     248          CRTerm *cur = NULL;
     249  
     250          g_return_val_if_fail (a_new_term, NULL);
     251  
     252          if (a_this == NULL)
     253                  return a_new_term;
     254  
     255          for (cur = a_this; cur->next; cur = cur->next) ;
     256  
     257          cur->next = a_new_term;
     258          a_new_term->prev = cur;
     259  
     260          return a_this;
     261  }
     262  
     263  /**
     264   *Prepends a term to the list of terms represented by a_this.
     265   *
     266   *@param a_this the "this pointer" of the current instance of
     267   *#CRTerm .
     268   *@param a_new_term the term to prepend.
     269   *@return the head of the new list.
     270   */
     271  CRTerm *
     272  cr_term_prepend_term (CRTerm * a_this, CRTerm * a_new_term)
     273  {
     274          g_return_val_if_fail (a_this && a_new_term, NULL);
     275  
     276          a_new_term->next = a_this;
     277          a_this->prev = a_new_term;
     278  
     279          return a_new_term;
     280  }
     281  
     282  /**
     283   *Serializes the expression represented by
     284   *the chained instances of #CRterm.
     285   *@param a_this the current instance of #CRTerm
     286   *@return the zero terminated string containing the serialized
     287   *form of #CRTerm. MUST BE FREED BY THE CALLER using g_free().
     288   */
     289  guchar *
     290  cr_term_to_string (CRTerm const * a_this)
     291  {
     292          GString *str_buf = NULL;
     293          CRTerm const *cur = NULL;
     294          guchar *result = NULL,
     295                  *content = NULL;
     296  
     297          g_return_val_if_fail (a_this, NULL);
     298  
     299          str_buf = g_string_new (NULL);
     300          g_return_val_if_fail (str_buf, NULL);
     301  
     302          for (cur = a_this; cur; cur = cur->next) {
     303                  if ((cur->content.str == NULL)
     304                      && (cur->content.num == NULL)
     305                      && (cur->content.str == NULL)
     306                      && (cur->content.rgb == NULL))
     307                          continue;
     308  
     309                  switch (cur->the_operator) {
     310                  case DIVIDE:
     311                          g_string_append (str_buf, " / ");
     312                          break;
     313  
     314                  case COMMA:
     315                          g_string_append (str_buf, ", ");
     316                          break;
     317  
     318                  case NO_OP:
     319                          if (cur->prev) {
     320                                  g_string_append (str_buf, " ");
     321                          }
     322                          break;
     323                  default:
     324  
     325                          break;
     326                  }
     327  
     328                  switch (cur->unary_op) {
     329                  case PLUS_UOP:
     330                          g_string_append (str_buf, "+");
     331                          break;
     332  
     333                  case MINUS_UOP:
     334                          g_string_append (str_buf, "-");
     335                          break;
     336  
     337                  default:
     338                          break;
     339                  }
     340  
     341                  switch (cur->type) {
     342                  case TERM_NUMBER:
     343                          if (cur->content.num) {
     344                                  content = cr_num_to_string (cur->content.num);
     345                          }
     346  
     347                          if (content) {
     348                                  g_string_append (str_buf, (const gchar *) content);
     349                                  g_free (content);
     350                                  content = NULL;
     351                          }
     352  
     353                          break;
     354  
     355                  case TERM_FUNCTION:
     356                          if (cur->content.str) {
     357                                  content = (guchar *) g_strndup
     358                                          (cur->content.str->stryng->str,
     359                                           cur->content.str->stryng->len);
     360                          }
     361  
     362                          if (content) {
     363                                  g_string_append_printf (str_buf, "%s(",
     364                                                          content);
     365  
     366                                  if (cur->ext_content.func_param) {
     367                                          guchar *tmp_str = NULL;
     368  
     369                                          tmp_str = cr_term_to_string
     370                                                  (cur->
     371                                                   ext_content.func_param);
     372  
     373                                          if (tmp_str) {
     374                                                  g_string_append (str_buf, 
     375  								 (const gchar *) tmp_str);
     376                                                  g_free (tmp_str);
     377                                                  tmp_str = NULL;
     378                                          }
     379                                  }
     380                                  g_string_append (str_buf, ")");
     381                                  g_free (content);
     382                                  content = NULL;
     383                          }
     384  
     385                          break;
     386  
     387                  case TERM_STRING:
     388                          if (cur->content.str) {
     389                                  content = (guchar *) g_strndup
     390                                          (cur->content.str->stryng->str,
     391                                           cur->content.str->stryng->len);
     392                          }
     393  
     394                          if (content) {
     395                                  g_string_append_printf (str_buf,
     396                                                          "\"%s\"", content);
     397                                  g_free (content);
     398                                  content = NULL;
     399                          }
     400                          break;
     401  
     402                  case TERM_IDENT:
     403                          if (cur->content.str) {
     404                                  content = (guchar *) g_strndup
     405                                          (cur->content.str->stryng->str,
     406                                           cur->content.str->stryng->len);
     407                          }
     408  
     409                          if (content) {
     410                                  g_string_append (str_buf, (const gchar *) content);
     411                                  g_free (content);
     412                                  content = NULL;
     413                          }
     414                          break;
     415  
     416                  case TERM_URI:
     417                          if (cur->content.str) {
     418                                  content = (guchar *) g_strndup
     419                                          (cur->content.str->stryng->str,
     420                                           cur->content.str->stryng->len);
     421                          }
     422  
     423                          if (content) {
     424                                  g_string_append_printf
     425                                          (str_buf, "url(%s)", content);
     426                                  g_free (content);
     427                                  content = NULL;
     428                          }
     429                          break;
     430  
     431                  case TERM_RGB:
     432                          if (cur->content.rgb) {
     433                                  guchar *tmp_str = NULL;
     434  
     435                                  g_string_append (str_buf, "rgb(");
     436                                  tmp_str = cr_rgb_to_string (cur->content.rgb);
     437  
     438                                  if (tmp_str) {
     439                                          g_string_append (str_buf, (const gchar *) tmp_str);
     440                                          g_free (tmp_str);
     441                                          tmp_str = NULL;
     442                                  }
     443                                  g_string_append (str_buf, ")");
     444                          }
     445  
     446                          break;
     447  
     448                  case TERM_UNICODERANGE:
     449                          g_string_append
     450                                  (str_buf,
     451                                   "?found unicoderange: dump not supported yet?");
     452                          break;
     453  
     454                  case TERM_HASH:
     455                          if (cur->content.str) {
     456                                  content = (guchar *) g_strndup
     457                                          (cur->content.str->stryng->str,
     458                                           cur->content.str->stryng->len);
     459                          }
     460  
     461                          if (content) {
     462                                  g_string_append_printf (str_buf,
     463                                                          "#%s", content);
     464                                  g_free (content);
     465                                  content = NULL;
     466                          }
     467                          break;
     468  
     469                  default:
     470                          g_string_append (str_buf,
     471                                           "Unrecognized Term type");
     472                          break;
     473                  }
     474          }
     475  
     476          if (str_buf) {
     477                  result =(guchar *) str_buf->str;
     478                  g_string_free (str_buf, FALSE);
     479                  str_buf = NULL;
     480          }
     481  
     482          return result;
     483  }
     484  
     485  guchar *
     486  cr_term_one_to_string (CRTerm const * a_this)
     487  {
     488          GString *str_buf = NULL;
     489          guchar *result = NULL,
     490                  *content = NULL;
     491  
     492          g_return_val_if_fail (a_this, NULL);
     493  
     494          str_buf = g_string_new (NULL);
     495          g_return_val_if_fail (str_buf, NULL);
     496  
     497          if ((a_this->content.str == NULL)
     498              && (a_this->content.num == NULL)
     499              && (a_this->content.str == NULL)
     500              && (a_this->content.rgb == NULL))
     501                  return NULL ;
     502  
     503          switch (a_this->the_operator) {
     504          case DIVIDE:
     505                  g_string_append_printf (str_buf, " / ");
     506                  break;
     507  
     508          case COMMA:
     509                  g_string_append_printf (str_buf, ", ");
     510                  break;
     511  
     512          case NO_OP:
     513                  if (a_this->prev) {
     514                          g_string_append_printf (str_buf, " ");
     515                  }
     516                  break;
     517          default:
     518  
     519                  break;
     520          }
     521  
     522          switch (a_this->unary_op) {
     523          case PLUS_UOP:
     524                  g_string_append_printf (str_buf, "+");
     525                  break;
     526  
     527          case MINUS_UOP:
     528                  g_string_append_printf (str_buf, "-");
     529                  break;
     530  
     531          default:
     532                  break;
     533          }
     534  
     535          switch (a_this->type) {
     536          case TERM_NUMBER:
     537                  if (a_this->content.num) {
     538                          content = cr_num_to_string (a_this->content.num);
     539                  }
     540  
     541                  if (content) {
     542                          g_string_append (str_buf, (const gchar *) content);
     543                          g_free (content);
     544                          content = NULL;
     545                  }
     546  
     547                  break;
     548  
     549          case TERM_FUNCTION:
     550                  if (a_this->content.str) {
     551                          content = (guchar *) g_strndup
     552                                  (a_this->content.str->stryng->str,
     553                                   a_this->content.str->stryng->len);
     554                  }
     555  
     556                  if (content) {
     557                          g_string_append_printf (str_buf, "%s(",
     558                                                  content);
     559  
     560                          if (a_this->ext_content.func_param) {
     561                                  guchar *tmp_str = NULL;
     562  
     563                                  tmp_str = cr_term_to_string
     564                                          (a_this->
     565                                           ext_content.func_param);
     566  
     567                                  if (tmp_str) {
     568                                          g_string_append_printf
     569                                                  (str_buf,
     570                                                   "%s", tmp_str);
     571                                          g_free (tmp_str);
     572                                          tmp_str = NULL;
     573                                  }
     574  
     575                                  g_string_append_printf (str_buf, ")");
     576                                  g_free (content);
     577                                  content = NULL;
     578                          }
     579                  }
     580  
     581                  break;
     582  
     583          case TERM_STRING:
     584                  if (a_this->content.str) {
     585                          content = (guchar *) g_strndup
     586                                  (a_this->content.str->stryng->str,
     587                                   a_this->content.str->stryng->len);
     588                  }
     589  
     590                  if (content) {
     591                          g_string_append_printf (str_buf,
     592                                                  "\"%s\"", content);
     593                          g_free (content);
     594                          content = NULL;
     595                  }
     596                  break;
     597  
     598          case TERM_IDENT:
     599                  if (a_this->content.str) {
     600                          content = (guchar *) g_strndup
     601                                  (a_this->content.str->stryng->str,
     602                                   a_this->content.str->stryng->len);
     603                  }
     604  
     605                  if (content) {
     606                          g_string_append (str_buf, (const gchar *) content);
     607                          g_free (content);
     608                          content = NULL;
     609                  }
     610                  break;
     611  
     612          case TERM_URI:
     613                  if (a_this->content.str) {
     614                          content = (guchar *) g_strndup
     615                                  (a_this->content.str->stryng->str,
     616                                   a_this->content.str->stryng->len);
     617                  }
     618  
     619                  if (content) {
     620                          g_string_append_printf
     621                                  (str_buf, "url(%s)", content);
     622                          g_free (content);
     623                          content = NULL;
     624                  }
     625                  break;
     626  
     627          case TERM_RGB:
     628                  if (a_this->content.rgb) {
     629                          guchar *tmp_str = NULL;
     630  
     631                          g_string_append_printf (str_buf, "rgb(");
     632                          tmp_str = cr_rgb_to_string (a_this->content.rgb);
     633  
     634                          if (tmp_str) {
     635                                  g_string_append (str_buf, (const gchar *) tmp_str);
     636                                  g_free (tmp_str);
     637                                  tmp_str = NULL;
     638                          }
     639                          g_string_append_printf (str_buf, ")");
     640                  }
     641  
     642                  break;
     643  
     644          case TERM_UNICODERANGE:
     645                  g_string_append_printf
     646                          (str_buf,
     647                           "?found unicoderange: dump not supported yet?");
     648                  break;
     649  
     650          case TERM_HASH:
     651                  if (a_this->content.str) {
     652                          content = (guchar *) g_strndup
     653                                  (a_this->content.str->stryng->str,
     654                                   a_this->content.str->stryng->len);
     655                  }
     656  
     657                  if (content) {
     658                          g_string_append_printf (str_buf,
     659                                                  "#%s", content);
     660                          g_free (content);
     661                          content = NULL;
     662                  }
     663                  break;
     664  
     665          default:
     666                  g_string_append_printf (str_buf,
     667                                          "%s",
     668                                          "Unrecognized Term type");
     669                  break;
     670          }
     671  
     672          if (str_buf) {
     673                  result = (guchar *) str_buf->str;
     674                  g_string_free (str_buf, FALSE);
     675                  str_buf = NULL;
     676          }
     677  
     678          return result;
     679  }
     680  
     681  /**
     682   *Dumps the expression (a list of terms connected by operators)
     683   *to a file.
     684   *TODO: finish the dump. The dump of some type of terms have not yet been
     685   *implemented.
     686   *@param a_this the current instance of #CRTerm.
     687   *@param a_fp the destination file pointer.
     688   */
     689  void
     690  cr_term_dump (CRTerm const * a_this, FILE * a_fp)
     691  {
     692          guchar *content = NULL;
     693  
     694          g_return_if_fail (a_this);
     695  
     696          content = cr_term_to_string (a_this);
     697  
     698          if (content) {
     699                  fprintf (a_fp, "%s", content);
     700                  g_free (content);
     701          }
     702  }
     703  
     704  /**
     705   *Return the number of terms in the expression.
     706   *@param a_this the current instance of #CRTerm.
     707   *@return number of terms in the expression.
     708   */
     709  int
     710  cr_term_nr_values (CRTerm const *a_this)
     711  {
     712  	CRTerm const *cur = NULL ;
     713  	int nr = 0;
     714  
     715  	g_return_val_if_fail (a_this, -1) ;
     716  
     717  	for (cur = a_this ; cur ; cur = cur->next)
     718  		nr ++;
     719  	return nr;
     720  }
     721  
     722  /**
     723   *Use an index to get a CRTerm from the expression.
     724   *@param a_this the current instance of #CRTerm.
     725   *@param itemnr the index into the expression.
     726   *@return CRTerm at position itemnr, if itemnr > number of terms - 1,
     727   *it will return NULL.
     728   */
     729  CRTerm *
     730  cr_term_get_from_list (CRTerm *a_this, int itemnr)
     731  {
     732  	CRTerm *cur = NULL ;
     733  	int nr = 0;
     734  
     735  	g_return_val_if_fail (a_this, NULL) ;
     736  
     737  	for (cur = a_this ; cur ; cur = cur->next)
     738  		if (nr++ == itemnr)
     739  			return cur;
     740  	return NULL;
     741  }
     742  
     743  /**
     744   *Increments the reference counter of the current instance
     745   *of #CRTerm.*
     746   *@param a_this the current instance of #CRTerm.
     747   */
     748  void
     749  cr_term_ref (CRTerm * a_this)
     750  {
     751          g_return_if_fail (a_this);
     752  
     753          a_this->ref_count++;
     754  }
     755  
     756  /**
     757   *Decrements the ref count of the current instance of
     758   *#CRTerm. If the ref count reaches zero, the instance is
     759   *destroyed.
     760   *@param a_this the current instance of #CRTerm.
     761   *@return TRUE if the current instance has been destroyed, FALSE otherwise.
     762   */
     763  gboolean
     764  cr_term_unref (CRTerm * a_this)
     765  {
     766          g_return_val_if_fail (a_this, FALSE);
     767  
     768          if (a_this->ref_count) {
     769                  a_this->ref_count--;
     770          }
     771  
     772          if (a_this->ref_count == 0) {
     773                  cr_term_destroy (a_this);
     774                  return TRUE;
     775          }
     776  
     777          return FALSE;
     778  }
     779  
     780  /**
     781   *The destructor of the the #CRTerm class.
     782   *@param a_this the "this pointer" of the current instance
     783   *of #CRTerm.
     784   */
     785  void
     786  cr_term_destroy (CRTerm * a_this)
     787  {
     788          g_return_if_fail (a_this);
     789  
     790          cr_term_clear (a_this);
     791  
     792          if (a_this->next) {
     793                  cr_term_destroy (a_this->next);
     794                  a_this->next = NULL;
     795          }
     796  
     797          if (a_this) {
     798                  g_free (a_this);
     799          }
     800  
     801  }