(root)/
gettext-0.22.4/
libtextstyle/
lib/
libcroco/
cr-declaration.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 <string.h>
      36  #include "cr-declaration.h"
      37  #include "cr-statement.h"
      38  #include "cr-parser.h"
      39  
      40  /**
      41   *@CRDeclaration:
      42   *
      43   *The definition of the #CRDeclaration class.
      44   */
      45  
      46  /**
      47   * dump:
      48   *@a_this: the current instance of #CRDeclaration.
      49   *@a_fp: the destination file pointer.
      50   *@a_indent: the number of indentation white char. 
      51   *
      52   *Dumps (serializes) one css declaration to a file.
      53   */
      54  static void
      55  dump (CRDeclaration const * a_this, FILE * a_fp, glong a_indent)
      56  {
      57          guchar *str = NULL;
      58  
      59          g_return_if_fail (a_this);
      60  
      61          str = (guchar *) cr_declaration_to_string (a_this, a_indent);
      62          if (str) {
      63                  fprintf (a_fp, "%s", str);
      64                  g_free (str);
      65                  str = NULL;
      66          }
      67  }
      68  
      69  /**
      70   * cr_declaration_new:
      71   * @a_statement: the statement this declaration belongs to. can be NULL.
      72   *@a_property: the property string of the declaration
      73   *@a_value: the value expression of the declaration.
      74   *Constructor of #CRDeclaration.
      75   *
      76   *Returns the newly built instance of #CRDeclaration, or NULL in
      77   *case of error.
      78   *
      79   *The returned CRDeclaration takes ownership of @a_property and @a_value.
      80   *(E.g. cr_declaration_destroy on this CRDeclaration will also free
      81   *@a_property and @a_value.)
      82   */
      83  CRDeclaration *
      84  cr_declaration_new (CRStatement * a_statement,
      85                      CRString * a_property, CRTerm * a_value)
      86  {
      87          CRDeclaration *result = NULL;
      88  
      89          g_return_val_if_fail (a_property, NULL);
      90  
      91          if (a_statement)
      92                  g_return_val_if_fail (a_statement
      93                                        && ((a_statement->type == RULESET_STMT)
      94                                            || (a_statement->type
      95                                                == AT_FONT_FACE_RULE_STMT)
      96                                            || (a_statement->type
      97                                                == AT_PAGE_RULE_STMT)), NULL);
      98  
      99          result = g_try_malloc (sizeof (CRDeclaration));
     100          if (!result) {
     101                  cr_utils_trace_info ("Out of memory");
     102                  return NULL;
     103          }
     104          memset (result, 0, sizeof (CRDeclaration));
     105          result->property = a_property;
     106          result->value = a_value;
     107  
     108          if (a_value) {
     109                  cr_term_ref (a_value);
     110          }
     111          result->parent_statement = a_statement;
     112          return result;
     113  }
     114  
     115  /**
     116   * cr_declaration_parse_from_buf:
     117   *@a_statement: the parent css2 statement of this
     118   *this declaration. Must be non NULL and of type
     119   *RULESET_STMT (must be a ruleset).
     120   *@a_str: the string that contains the statement.
     121   *@a_enc: the encoding of a_str.
     122   *
     123   *Parses a text buffer that contains
     124   *a css declaration.
     125   *Returns the parsed declaration, or NULL in case of error.
     126   */
     127  CRDeclaration *
     128  cr_declaration_parse_from_buf (CRStatement * a_statement,
     129                                 const guchar * a_str, enum CREncoding a_enc)
     130  {
     131          enum CRStatus status = CR_OK;
     132          CRTerm *value = NULL;
     133          CRString *property = NULL;
     134          CRDeclaration *result = NULL;
     135          CRParser *parser = NULL;
     136          gboolean important = FALSE;
     137  
     138          g_return_val_if_fail (a_str, NULL);
     139          if (a_statement)
     140                  g_return_val_if_fail (a_statement->type == RULESET_STMT,
     141                                        NULL);
     142  
     143          parser = cr_parser_new_from_buf ((guchar*)a_str, strlen ((const char *) a_str), a_enc, FALSE);
     144          g_return_val_if_fail (parser, NULL);
     145  
     146          status = cr_parser_try_to_skip_spaces_and_comments (parser);
     147          if (status != CR_OK)
     148                  goto cleanup;
     149  
     150          status = cr_parser_parse_declaration (parser, &property,
     151                                                &value, &important);
     152          if (status != CR_OK || !property)
     153                  goto cleanup;
     154  
     155          result = cr_declaration_new (a_statement, property, value);
     156          if (result) {
     157                  property = NULL;
     158                  value = NULL;
     159                  result->important = important;
     160          }
     161  
     162        cleanup:
     163  
     164          if (parser) {
     165                  cr_parser_destroy (parser);
     166                  parser = NULL;
     167          }
     168  
     169          if (property) {
     170                  cr_string_destroy (property);
     171                  property = NULL;
     172          }
     173  
     174          if (value) {
     175                  cr_term_destroy (value);
     176                  value = NULL;
     177          }
     178  
     179          return result;
     180  }
     181  
     182  /**
     183   * cr_declaration_parse_list_from_buf:
     184   *@a_str: the input buffer that contains the list of declaration to
     185   *parse.
     186   *@a_enc: the encoding of a_str
     187   *
     188   *Parses a ';' separated list of properties declaration.
     189   *Returns the parsed list of declaration, NULL if parsing failed.
     190   */
     191  CRDeclaration *
     192  cr_declaration_parse_list_from_buf (const guchar * a_str,
     193                                      enum CREncoding a_enc)
     194  {
     195  
     196          enum CRStatus status = CR_OK;
     197          CRTerm *value = NULL;
     198          CRString *property = NULL;
     199          CRDeclaration *result = NULL,
     200                  *cur_decl = NULL;
     201          CRParser *parser = NULL;
     202          CRTknzr *tokenizer = NULL;
     203          gboolean important = FALSE;
     204  
     205          g_return_val_if_fail (a_str, NULL);
     206  
     207          parser = cr_parser_new_from_buf ((guchar*)a_str, strlen ((const char *) a_str), a_enc, FALSE);
     208          g_return_val_if_fail (parser, NULL);
     209          status = cr_parser_get_tknzr (parser, &tokenizer);
     210          if (status != CR_OK || !tokenizer) {
     211                  if (status == CR_OK)
     212                          status = CR_ERROR;
     213                  goto cleanup;
     214          }
     215          status = cr_parser_try_to_skip_spaces_and_comments (parser);
     216          if (status != CR_OK)
     217                  goto cleanup;
     218  
     219          status = cr_parser_parse_declaration (parser, &property,
     220                                                &value, &important);
     221          if (status != CR_OK || !property) {
     222                  if (status != CR_OK)
     223                          status = CR_ERROR;
     224                  goto cleanup;
     225          }
     226          result = cr_declaration_new (NULL, property, value);
     227          if (result) {
     228                  property = NULL;
     229                  value = NULL;
     230                  result->important = important;
     231          }
     232          /*now, go parse the other declarations */
     233          for (;;) {
     234                  guint32 c = 0;
     235  
     236                  cr_parser_try_to_skip_spaces_and_comments (parser);
     237                  status = cr_tknzr_peek_char (tokenizer, &c);
     238                  if (status != CR_OK) {
     239                          if (status == CR_END_OF_INPUT_ERROR)
     240                                  status = CR_OK;
     241                          goto cleanup;
     242                  }
     243                  if (c == ';') {
     244                          status = cr_tknzr_read_char (tokenizer, &c);
     245                  } else {
     246                          break;
     247                  }
     248                  important = FALSE;
     249                  cr_parser_try_to_skip_spaces_and_comments (parser);
     250                  status = cr_parser_parse_declaration (parser, &property,
     251                                                        &value, &important);
     252                  if (status != CR_OK || !property) {
     253                          if (status == CR_END_OF_INPUT_ERROR) {
     254                                  status = CR_OK;
     255                          }
     256                          break;
     257                  }
     258                  cur_decl = cr_declaration_new (NULL, property, value);
     259                  if (cur_decl) {
     260                          cur_decl->important = important;
     261                          result = cr_declaration_append (result, cur_decl);
     262                          property = NULL;
     263                          value = NULL;
     264                          cur_decl = NULL;
     265                  } else {
     266                          break;
     267                  }
     268          }
     269  
     270        cleanup:
     271  
     272          if (parser) {
     273                  cr_parser_destroy (parser);
     274                  parser = NULL;
     275          }
     276  
     277          if (property) {
     278                  cr_string_destroy (property);
     279                  property = NULL;
     280          }
     281  
     282          if (value) {
     283                  cr_term_destroy (value);
     284                  value = NULL;
     285          }
     286  
     287          if (status != CR_OK && result) {
     288                  cr_declaration_destroy (result);
     289                  result = NULL;
     290          }
     291          return result;
     292  }
     293  
     294  /**
     295   * cr_declaration_append:
     296   *@a_this: the current declaration list.
     297   *@a_new: the declaration to append.
     298   *
     299   *Appends a new declaration to the current declarations list.
     300   *Returns the declaration list with a_new appended to it, or NULL
     301   *in case of error.
     302   */
     303  CRDeclaration *
     304  cr_declaration_append (CRDeclaration * a_this, CRDeclaration * a_new)
     305  {
     306          CRDeclaration *cur = NULL;
     307  
     308          g_return_val_if_fail (a_new, NULL);
     309  
     310          if (!a_this)
     311                  return a_new;
     312  
     313          for (cur = a_this; cur && cur->next; cur = cur->next) ;
     314  
     315          cur->next = a_new;
     316          a_new->prev = cur;
     317  
     318          return a_this;
     319  }
     320  
     321  /**
     322   * cr_declaration_unlink:
     323   *@a_decls: the declaration to unlink.
     324   *
     325   *Unlinks the declaration from the declaration list.
     326   *case of a successfull completion, NULL otherwise.
     327   *
     328   *Returns a pointer to the unlinked declaration in
     329   */
     330  CRDeclaration *
     331  cr_declaration_unlink (CRDeclaration * a_decl)
     332  {
     333          CRDeclaration *result = a_decl;
     334  
     335          g_return_val_if_fail (result, NULL);
     336  
     337          /*
     338           *some sanity checks first
     339           */
     340          if (a_decl->prev) {
     341                  g_return_val_if_fail (a_decl->prev->next == a_decl, NULL);
     342  
     343          }
     344          if (a_decl->next) {
     345                  g_return_val_if_fail (a_decl->next->prev == a_decl, NULL);
     346          }
     347  
     348          /*
     349           *now, the real unlinking job.
     350           */
     351          if (a_decl->prev) {
     352                  a_decl->prev->next = a_decl->next;
     353          }
     354          if (a_decl->next) {
     355                  a_decl->next->prev = a_decl->prev;
     356          }
     357          if (a_decl->parent_statement) {
     358                  CRDeclaration **children_decl_ptr = NULL;
     359  
     360                  switch (a_decl->parent_statement->type) {
     361                  case RULESET_STMT:
     362                          if (a_decl->parent_statement->kind.ruleset) {
     363                                  children_decl_ptr =
     364                                          &a_decl->parent_statement->
     365                                          kind.ruleset->decl_list;
     366                          }
     367  
     368                          break;
     369  
     370                  case AT_FONT_FACE_RULE_STMT:
     371                          if (a_decl->parent_statement->kind.font_face_rule) {
     372                                  children_decl_ptr =
     373                                          &a_decl->parent_statement->
     374                                          kind.font_face_rule->decl_list;
     375                          }
     376                          break;
     377                  case AT_PAGE_RULE_STMT:
     378                          if (a_decl->parent_statement->kind.page_rule) {
     379                                  children_decl_ptr =
     380                                          &a_decl->parent_statement->
     381                                          kind.page_rule->decl_list;
     382                          }
     383  
     384                  default:
     385                          break;
     386                  }
     387                  if (children_decl_ptr
     388                      && *children_decl_ptr && *children_decl_ptr == a_decl)
     389                          *children_decl_ptr = (*children_decl_ptr)->next;
     390          }
     391  
     392          a_decl->next = NULL;
     393          a_decl->prev = NULL;
     394          a_decl->parent_statement = NULL;
     395  
     396          return result;
     397  }
     398  
     399  /**
     400   * cr_declaration_prepend:
     401   * @a_this: the current declaration list.
     402   * @a_new: the declaration to prepend.
     403   *
     404   * prepends a declaration to the current declaration list.
     405   *
     406   * Returns the list with a_new prepended or NULL in case of error.
     407   */
     408  CRDeclaration *
     409  cr_declaration_prepend (CRDeclaration * a_this, CRDeclaration * a_new)
     410  {
     411          CRDeclaration *cur = NULL;
     412  
     413          g_return_val_if_fail (a_new, NULL);
     414  
     415          if (!a_this)
     416                  return a_new;
     417  
     418          a_this->prev = a_new;
     419          a_new->next = a_this;
     420  
     421          for (cur = a_new; cur && cur->prev; cur = cur->prev) ;
     422  
     423          return cur;
     424  }
     425  
     426  /**
     427   * cr_declaration_append2:
     428   *@a_this: the current declaration list.
     429   *@a_prop: the property string of the declaration to append.
     430   *@a_value: the value of the declaration to append.
     431   *
     432   *Appends a declaration to the current declaration list.
     433   *Returns the list with the new property appended to it, or NULL in
     434   *case of an error.
     435   */
     436  CRDeclaration *
     437  cr_declaration_append2 (CRDeclaration * a_this,
     438                          CRString * a_prop, CRTerm * a_value)
     439  {
     440          CRDeclaration *new_elem = NULL;
     441  
     442          if (a_this) {
     443                  new_elem = cr_declaration_new (a_this->parent_statement,
     444                                                 a_prop, a_value);
     445          } else {
     446                  new_elem = cr_declaration_new (NULL, a_prop, a_value);
     447          }
     448  
     449          g_return_val_if_fail (new_elem, NULL);
     450  
     451          return cr_declaration_append (a_this, new_elem);
     452  }
     453  
     454  /**
     455   * cr_declaration_dump:
     456   *@a_this: the current instance of #CRDeclaration.
     457   *@a_fp: the destination file.
     458   *@a_indent: the number of indentation white char.
     459   *@a_one_per_line: whether to put one declaration per line of not .
     460   *
     461   *
     462   *Dumps a declaration list to a file.
     463   */
     464  void
     465  cr_declaration_dump (CRDeclaration const * a_this, FILE * a_fp, glong a_indent,
     466                       gboolean a_one_per_line)
     467  {
     468          CRDeclaration const *cur = NULL;
     469  
     470          g_return_if_fail (a_this);
     471  
     472          for (cur = a_this; cur; cur = cur->next) {
     473                  if (cur->prev) {
     474                          if (a_one_per_line == TRUE)
     475                                  fprintf (a_fp, ";\n");
     476                          else
     477                                  fprintf (a_fp, "; ");
     478                  }
     479                  dump (cur, a_fp, a_indent);
     480          }
     481  }
     482  
     483  /**
     484   * cr_declaration_dump_one:
     485   *@a_this: the current instance of #CRDeclaration.
     486   *@a_fp: the destination file.
     487   *@a_indent: the number of indentation white char.
     488   *
     489   *Dumps the first declaration of the declaration list to a file.
     490   */
     491  void
     492  cr_declaration_dump_one (CRDeclaration const * a_this, FILE * a_fp, glong a_indent)
     493  {
     494          g_return_if_fail (a_this);
     495  
     496          dump (a_this, a_fp, a_indent);
     497  }
     498  
     499  /**
     500   * cr_declaration_to_string:
     501   *@a_this: the current instance of #CRDeclaration.
     502   *@a_indent: the number of indentation white char
     503   *to put before the actual serialisation.
     504   *
     505   *Serializes the declaration into a string
     506   *Returns the serialized form the declaration. The caller must
     507   *free the string using g_free().
     508   */
     509  gchar *
     510  cr_declaration_to_string (CRDeclaration const * a_this, gulong a_indent)
     511  {
     512          GString *stringue = NULL;
     513  
     514          gchar *str = NULL,
     515                  *result = NULL;
     516  
     517          g_return_val_if_fail (a_this, NULL);
     518  
     519  	stringue = g_string_new (NULL);
     520  
     521  	if (a_this->property 
     522  	    && a_this->property->stryng
     523  	    && a_this->property->stryng->str) {
     524  		str = g_strndup (a_this->property->stryng->str,
     525  				 a_this->property->stryng->len);
     526  		if (str) {
     527  			cr_utils_dump_n_chars2 (' ', stringue, 
     528  						a_indent);
     529  			g_string_append (stringue, str);
     530  			g_free (str);
     531  			str = NULL;
     532  		} else
     533                          goto error;
     534  
     535                  if (a_this->value) {
     536                          guchar *value_str = NULL;
     537  
     538                          value_str = cr_term_to_string (a_this->value);
     539                          if (value_str) {
     540                                  g_string_append_printf (stringue, " : %s",
     541                                                          value_str);
     542                                  g_free (value_str);
     543                          } else
     544                                  goto error;
     545                  }
     546                  if (a_this->important == TRUE) {
     547                          g_string_append_printf (stringue, " %s",
     548                                                  "!important");
     549                  }
     550          }
     551          if (stringue && stringue->str) {
     552                  result = stringue->str;
     553                  g_string_free (stringue, FALSE);
     554          }
     555          return result;
     556  
     557        error:
     558          if (stringue) {
     559                  g_string_free (stringue, TRUE);
     560                  stringue = NULL;
     561          }
     562          if (str) {
     563                  g_free (str);
     564                  str = NULL;
     565          }
     566  
     567          return result;
     568  }
     569  
     570  /**
     571   * cr_declaration_list_to_string:
     572   *@a_this: the current instance of #CRDeclaration.
     573   *@a_indent: the number of indentation white char
     574   *to put before the actual serialisation.
     575   *
     576   *Serializes the declaration list into a string
     577   */
     578  guchar *
     579  cr_declaration_list_to_string (CRDeclaration const * a_this, gulong a_indent)
     580  {
     581          CRDeclaration const *cur = NULL;
     582          GString *stringue = NULL;
     583          guchar *str = NULL,
     584                  *result = NULL;
     585  
     586          g_return_val_if_fail (a_this, NULL);
     587  
     588          stringue = g_string_new (NULL);
     589  
     590          for (cur = a_this; cur; cur = cur->next) {
     591                  str = (guchar *) cr_declaration_to_string (cur, a_indent);
     592                  if (str) {
     593                          g_string_append_printf (stringue, "%s;", str);
     594                          g_free (str);
     595                  } else
     596                          break;
     597          }
     598          if (stringue && stringue->str) {
     599                  result = (guchar *) stringue->str;
     600                  g_string_free (stringue, FALSE);
     601          }
     602  
     603          return result;
     604  }
     605  
     606  /**
     607   * cr_declaration_list_to_string2:
     608   *@a_this: the current instance of #CRDeclaration.
     609   *@a_indent: the number of indentation white char
     610   *@a_one_decl_per_line: whether to output one doc per line or not.
     611   *to put before the actual serialisation.
     612   *
     613   *Serializes the declaration list into a string
     614   *Returns the serialized form the declararation.
     615   */
     616  guchar *
     617  cr_declaration_list_to_string2 (CRDeclaration const * a_this,
     618                                  gulong a_indent, gboolean a_one_decl_per_line)
     619  {
     620          CRDeclaration const *cur = NULL;
     621          GString *stringue = NULL;
     622          guchar *str = NULL,
     623                  *result = NULL;
     624  
     625          g_return_val_if_fail (a_this, NULL);
     626  
     627          stringue = g_string_new (NULL);
     628  
     629          for (cur = a_this; cur; cur = cur->next) {
     630                  str = (guchar *) cr_declaration_to_string (cur, a_indent);
     631                  if (str) {
     632                          if (a_one_decl_per_line == TRUE) {
     633                                  if (cur->next)
     634                                          g_string_append_printf (stringue,
     635                                                                  "%s;\n", str);
     636                                  else
     637                                          g_string_append (stringue,
     638                                                           (const gchar *) str);
     639                          } else {
     640                                  if (cur->next)
     641                                          g_string_append_printf (stringue,
     642                                                                  "%s;", str);
     643                                  else
     644                                          g_string_append (stringue,
     645                                                           (const gchar *) str);
     646                          }
     647                          g_free (str);
     648                  } else
     649                          break;
     650          }
     651          if (stringue && stringue->str) {
     652                  result = (guchar *) stringue->str;
     653                  g_string_free (stringue, FALSE);
     654          }
     655  
     656          return result;
     657  }
     658  
     659  /**
     660   * cr_declaration_nr_props:
     661   *@a_this: the current instance of #CRDeclaration.
     662   *Return the number of properties in the declaration
     663   */
     664  gint
     665  cr_declaration_nr_props (CRDeclaration const * a_this)
     666  {
     667          CRDeclaration const *cur = NULL;
     668          int nr = 0;
     669  
     670          g_return_val_if_fail (a_this, -1);
     671  
     672          for (cur = a_this; cur; cur = cur->next)
     673                  nr++;
     674          return nr;
     675  }
     676  
     677  /**
     678   * cr_declaration_get_from_list:
     679   *@a_this: the current instance of #CRDeclaration.
     680   *@itemnr: the index into the declaration list.
     681   *
     682   *Use an index to get a CRDeclaration from the declaration list.
     683   *
     684   *Returns #CRDeclaration at position itemnr, 
     685   *if itemnr > number of declarations - 1,
     686   *it will return NULL.
     687   */
     688  CRDeclaration *
     689  cr_declaration_get_from_list (CRDeclaration * a_this, int itemnr)
     690  {
     691          CRDeclaration *cur = NULL;
     692          int nr = 0;
     693  
     694          g_return_val_if_fail (a_this, NULL);
     695  
     696          for (cur = a_this; cur; cur = cur->next)
     697                  if (nr++ == itemnr)
     698                          return cur;
     699          return NULL;
     700  }
     701  
     702  /**
     703   * cr_declaration_get_by_prop_name:
     704   *@a_this: the current instance of #CRDeclaration.
     705   *@a_prop: the property name to search for.
     706   *
     707   *Use property name to get a CRDeclaration from the declaration list.
     708   *Returns #CRDeclaration with property name a_prop, or NULL if not found.
     709   */
     710  CRDeclaration *
     711  cr_declaration_get_by_prop_name (CRDeclaration * a_this,
     712                                   const guchar * a_prop)
     713  {
     714          CRDeclaration *cur = NULL;
     715  
     716          g_return_val_if_fail (a_this, NULL);
     717          g_return_val_if_fail (a_prop, NULL);
     718  
     719          for (cur = a_this; cur; cur = cur->next) {
     720  		if (cur->property 
     721  		    && cur->property->stryng
     722  		    && cur->property->stryng->str) {
     723  			if (!strcmp (cur->property->stryng->str, 
     724  				     (const char *) a_prop)) {
     725  				return cur;
     726  			}
     727  		}
     728  	}
     729          return NULL;
     730  }
     731  
     732  /**
     733   * cr_declaration_ref:
     734   *@a_this: the current instance of #CRDeclaration.
     735   *
     736   *Increases the ref count of the current instance of #CRDeclaration.
     737   */
     738  void
     739  cr_declaration_ref (CRDeclaration * a_this)
     740  {
     741          g_return_if_fail (a_this);
     742  
     743          a_this->ref_count++;
     744  }
     745  
     746  /**
     747   * cr_declaration_unref:
     748   *@a_this: the current instance of #CRDeclaration.
     749   *
     750   *Decrements the ref count of the current instance of #CRDeclaration.
     751   *If the ref count reaches zero, the current instance of #CRDeclaration
     752   *if destroyed.
     753   *Returns TRUE if @a_this was destroyed (ref count reached zero),
     754   *FALSE otherwise.
     755   */
     756  gboolean
     757  cr_declaration_unref (CRDeclaration * a_this)
     758  {
     759          g_return_val_if_fail (a_this, FALSE);
     760  
     761          if (a_this->ref_count) {
     762                  a_this->ref_count--;
     763          }
     764  
     765          if (a_this->ref_count == 0) {
     766                  cr_declaration_destroy (a_this);
     767                  return TRUE;
     768          }
     769          return FALSE;
     770  }
     771  
     772  /**
     773   * cr_declaration_destroy:
     774   *@a_this: the current instance of #CRDeclaration.
     775   *
     776   *Destructor of the declaration list.
     777   */
     778  void
     779  cr_declaration_destroy (CRDeclaration * a_this)
     780  {
     781          CRDeclaration *cur = NULL;
     782  
     783          g_return_if_fail (a_this);
     784  
     785          /*
     786           * Go to the last element of the list.
     787           */
     788          for (cur = a_this; cur->next; cur = cur->next)
     789                  g_assert (cur->next->prev == cur);
     790  
     791          /*
     792           * Walk backward the list and free each "next" element.
     793           * Meanwhile, free each property/value pair contained in the list.
     794           */
     795          for (; cur; cur = cur->prev) {
     796                  g_free (cur->next);
     797                  cur->next = NULL;
     798  
     799                  if (cur->property) {
     800                          cr_string_destroy (cur->property);
     801                          cur->property = NULL;
     802                  }
     803  
     804                  if (cur->value) {
     805                          cr_term_destroy (cur->value);
     806                          cur->value = NULL;
     807                  }
     808          }
     809  
     810          g_free (a_this);
     811  }