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