• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
dump(CRDeclaration const * a_this,FILE * a_fp,glong a_indent)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 *
cr_declaration_new(CRStatement * a_statement,CRString * a_property,CRTerm * a_value)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 *
cr_declaration_parse_from_buf(CRStatement * a_statement,const guchar * a_str,enum CREncoding a_enc)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 *
cr_declaration_parse_list_from_buf(const guchar * a_str,enum CREncoding a_enc)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 *
cr_declaration_append(CRDeclaration * a_this,CRDeclaration * a_new)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 *
cr_declaration_unlink(CRDeclaration * a_decl)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 *
cr_declaration_prepend(CRDeclaration * a_this,CRDeclaration * a_new)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 *
cr_declaration_append2(CRDeclaration * a_this,CRString * a_prop,CRTerm * a_value)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
cr_declaration_dump(CRDeclaration const * a_this,FILE * a_fp,glong a_indent,gboolean a_one_per_line)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
cr_declaration_dump_one(CRDeclaration const * a_this,FILE * a_fp,glong a_indent)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 *
cr_declaration_to_string(CRDeclaration const * a_this,gulong a_indent)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 *
cr_declaration_list_to_string(CRDeclaration const * a_this,gulong a_indent)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 *
cr_declaration_list_to_string2(CRDeclaration const * a_this,gulong a_indent,gboolean a_one_decl_per_line)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
cr_declaration_nr_props(CRDeclaration const * a_this)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 *
cr_declaration_get_from_list(CRDeclaration * a_this,int itemnr)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 *
cr_declaration_get_by_prop_name(CRDeclaration * a_this,const guchar * a_prop)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
cr_declaration_ref(CRDeclaration * a_this)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
cr_declaration_unref(CRDeclaration * a_this)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
cr_declaration_destroy(CRDeclaration * a_this)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 }
812