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