Libcroco
|
00001 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ 00002 00003 /* 00004 * This file is part of The Croco Library 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of version 2.1 of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public License 00016 * along with this program; if not, write to the Free Software 00017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00018 * USA 00019 * 00020 * Author: Dodji Seketeli. 00021 * See COPYRIGHTS file for copyright information. 00022 */ 00023 00024 00025 #include <string.h> 00026 #include "cr-declaration.h" 00027 #include "cr-statement.h" 00028 #include "cr-parser.h" 00029 00030 /** 00031 *@CRDeclaration: 00032 * 00033 *The definition of the #CRDeclaration class. 00034 */ 00035 00036 /** 00037 * dump: 00038 *@a_this: the current instance of #CRDeclaration. 00039 *@a_fp: the destination file pointer. 00040 *@a_indent: the number of indentation white char. 00041 * 00042 *Dumps (serializes) one css declaration to a file. 00043 */ 00044 static void 00045 dump (CRDeclaration const * a_this, FILE * a_fp, glong a_indent) 00046 { 00047 guchar *str = NULL; 00048 00049 g_return_if_fail (a_this); 00050 00051 str = (guchar *) cr_declaration_to_string (a_this, a_indent); 00052 if (str) { 00053 fprintf (a_fp, "%s", str); 00054 g_free (str); 00055 str = NULL; 00056 } 00057 } 00058 00059 /** 00060 * cr_declaration_new: 00061 * @a_statement: the statement this declaration belongs to. can be NULL. 00062 *@a_property: the property string of the declaration 00063 *@a_value: the value expression of the declaration. 00064 *Constructor of #CRDeclaration. 00065 * 00066 *Returns the newly built instance of #CRDeclaration, or NULL in 00067 *case of error. 00068 * 00069 *The returned CRDeclaration takes ownership of @a_property and @a_value. 00070 *(E.g. cr_declaration_destroy on this CRDeclaration will also free 00071 *@a_property and @a_value.) 00072 */ 00073 CRDeclaration * 00074 cr_declaration_new (CRStatement * a_statement, 00075 CRString * a_property, CRTerm * a_value) 00076 { 00077 CRDeclaration *result = NULL; 00078 00079 g_return_val_if_fail (a_property, NULL); 00080 00081 if (a_statement) 00082 g_return_val_if_fail (a_statement 00083 && ((a_statement->type == RULESET_STMT) 00084 || (a_statement->type 00085 == AT_FONT_FACE_RULE_STMT) 00086 || (a_statement->type 00087 == AT_PAGE_RULE_STMT)), NULL); 00088 00089 result = g_try_malloc (sizeof (CRDeclaration)); 00090 if (!result) { 00091 cr_utils_trace_info ("Out of memory"); 00092 return NULL; 00093 } 00094 memset (result, 0, sizeof (CRDeclaration)); 00095 result->property = a_property; 00096 result->value = a_value; 00097 00098 if (a_value) { 00099 cr_term_ref (a_value); 00100 } 00101 result->parent_statement = a_statement; 00102 return result; 00103 } 00104 00105 /** 00106 * cr_declaration_parse_from_buf: 00107 *@a_statement: the parent css2 statement of this 00108 *this declaration. Must be non NULL and of type 00109 *RULESET_STMT (must be a ruleset). 00110 *@a_str: the string that contains the statement. 00111 *@a_enc: the encoding of a_str. 00112 * 00113 *Parses a text buffer that contains 00114 *a css declaration. 00115 *Returns the parsed declaration, or NULL in case of error. 00116 */ 00117 CRDeclaration * 00118 cr_declaration_parse_from_buf (CRStatement * a_statement, 00119 const guchar * a_str, enum CREncoding a_enc) 00120 { 00121 enum CRStatus status = CR_OK; 00122 CRTerm *value = NULL; 00123 CRString *property = NULL; 00124 CRDeclaration *result = NULL; 00125 CRParser *parser = NULL; 00126 gboolean important = FALSE; 00127 00128 g_return_val_if_fail (a_str, NULL); 00129 if (a_statement) 00130 g_return_val_if_fail (a_statement->type == RULESET_STMT, 00131 NULL); 00132 00133 parser = cr_parser_new_from_buf ((guchar*)a_str, strlen ((const char *) a_str), a_enc, FALSE); 00134 g_return_val_if_fail (parser, NULL); 00135 00136 status = cr_parser_try_to_skip_spaces_and_comments (parser); 00137 if (status != CR_OK) 00138 goto cleanup; 00139 00140 status = cr_parser_parse_declaration (parser, &property, 00141 &value, &important); 00142 if (status != CR_OK || !property) 00143 goto cleanup; 00144 00145 result = cr_declaration_new (a_statement, property, value); 00146 if (result) { 00147 property = NULL; 00148 value = NULL; 00149 result->important = important; 00150 } 00151 00152 cleanup: 00153 00154 if (parser) { 00155 cr_parser_destroy (parser); 00156 parser = NULL; 00157 } 00158 00159 if (property) { 00160 cr_string_destroy (property); 00161 property = NULL; 00162 } 00163 00164 if (value) { 00165 cr_term_destroy (value); 00166 value = NULL; 00167 } 00168 00169 return result; 00170 } 00171 00172 /** 00173 * cr_declaration_parse_list_from_buf: 00174 *@a_str: the input buffer that contains the list of declaration to 00175 *parse. 00176 *@a_enc: the encoding of a_str 00177 * 00178 *Parses a ';' separated list of properties declaration. 00179 *Returns the parsed list of declaration, NULL if parsing failed. 00180 */ 00181 CRDeclaration * 00182 cr_declaration_parse_list_from_buf (const guchar * a_str, 00183 enum CREncoding a_enc) 00184 { 00185 00186 enum CRStatus status = CR_OK; 00187 CRTerm *value = NULL; 00188 CRString *property = NULL; 00189 CRDeclaration *result = NULL, 00190 *cur_decl = NULL; 00191 CRParser *parser = NULL; 00192 CRTknzr *tokenizer = NULL; 00193 gboolean important = FALSE; 00194 00195 g_return_val_if_fail (a_str, NULL); 00196 00197 parser = cr_parser_new_from_buf ((guchar*)a_str, strlen ((const char *) a_str), a_enc, FALSE); 00198 g_return_val_if_fail (parser, NULL); 00199 status = cr_parser_get_tknzr (parser, &tokenizer); 00200 if (status != CR_OK || !tokenizer) { 00201 if (status == CR_OK) 00202 status = CR_ERROR; 00203 goto cleanup; 00204 } 00205 status = cr_parser_try_to_skip_spaces_and_comments (parser); 00206 if (status != CR_OK) 00207 goto cleanup; 00208 00209 status = cr_parser_parse_declaration (parser, &property, 00210 &value, &important); 00211 if (status != CR_OK || !property) { 00212 if (status != CR_OK) 00213 status = CR_ERROR; 00214 goto cleanup; 00215 } 00216 result = cr_declaration_new (NULL, property, value); 00217 if (result) { 00218 property = NULL; 00219 value = NULL; 00220 result->important = important; 00221 } 00222 /*now, go parse the other declarations */ 00223 for (;;) { 00224 guint32 c = 0; 00225 00226 cr_parser_try_to_skip_spaces_and_comments (parser); 00227 status = cr_tknzr_peek_char (tokenizer, &c); 00228 if (status != CR_OK) { 00229 if (status == CR_END_OF_INPUT_ERROR) 00230 status = CR_OK; 00231 goto cleanup; 00232 } 00233 if (c == ';') { 00234 status = cr_tknzr_read_char (tokenizer, &c); 00235 } else { 00236 break; 00237 } 00238 important = FALSE; 00239 cr_parser_try_to_skip_spaces_and_comments (parser); 00240 status = cr_parser_parse_declaration (parser, &property, 00241 &value, &important); 00242 if (status != CR_OK || !property) { 00243 if (status == CR_END_OF_INPUT_ERROR) { 00244 status = CR_OK; 00245 } 00246 break; 00247 } 00248 cur_decl = cr_declaration_new (NULL, property, value); 00249 if (cur_decl) { 00250 cur_decl->important = important; 00251 result = cr_declaration_append (result, cur_decl); 00252 property = NULL; 00253 value = NULL; 00254 cur_decl = NULL; 00255 } else { 00256 break; 00257 } 00258 } 00259 00260 cleanup: 00261 00262 if (parser) { 00263 cr_parser_destroy (parser); 00264 parser = NULL; 00265 } 00266 00267 if (property) { 00268 cr_string_destroy (property); 00269 property = NULL; 00270 } 00271 00272 if (value) { 00273 cr_term_destroy (value); 00274 value = NULL; 00275 } 00276 00277 if (status != CR_OK && result) { 00278 cr_declaration_destroy (result); 00279 result = NULL; 00280 } 00281 return result; 00282 } 00283 00284 /** 00285 * cr_declaration_append: 00286 *@a_this: the current declaration list. 00287 *@a_new: the declaration to append. 00288 * 00289 *Appends a new declaration to the current declarations list. 00290 *Returns the declaration list with a_new appended to it, or NULL 00291 *in case of error. 00292 */ 00293 CRDeclaration * 00294 cr_declaration_append (CRDeclaration * a_this, CRDeclaration * a_new) 00295 { 00296 CRDeclaration *cur = NULL; 00297 00298 g_return_val_if_fail (a_new, NULL); 00299 00300 if (!a_this) 00301 return a_new; 00302 00303 for (cur = a_this; cur && cur->next; cur = cur->next) ; 00304 00305 cur->next = a_new; 00306 a_new->prev = cur; 00307 00308 return a_this; 00309 } 00310 00311 /** 00312 * cr_declaration_unlink: 00313 *@a_decls: the declaration to unlink. 00314 * 00315 *Unlinks the declaration from the declaration list. 00316 *case of a successfull completion, NULL otherwise. 00317 * 00318 *Returns a pointer to the unlinked declaration in 00319 */ 00320 CRDeclaration * 00321 cr_declaration_unlink (CRDeclaration * a_decl) 00322 { 00323 CRDeclaration *result = a_decl; 00324 00325 g_return_val_if_fail (result, NULL); 00326 00327 /* 00328 *some sanity checks first 00329 */ 00330 if (a_decl->prev) { 00331 g_return_val_if_fail (a_decl->prev->next == a_decl, NULL); 00332 00333 } 00334 if (a_decl->next) { 00335 g_return_val_if_fail (a_decl->next->prev == a_decl, NULL); 00336 } 00337 00338 /* 00339 *now, the real unlinking job. 00340 */ 00341 if (a_decl->prev) { 00342 a_decl->prev->next = a_decl->next; 00343 } 00344 if (a_decl->next) { 00345 a_decl->next->prev = a_decl->prev; 00346 } 00347 if (a_decl->parent_statement) { 00348 CRDeclaration **children_decl_ptr = NULL; 00349 00350 switch (a_decl->parent_statement->type) { 00351 case RULESET_STMT: 00352 if (a_decl->parent_statement->kind.ruleset) { 00353 children_decl_ptr = 00354 &a_decl->parent_statement-> 00355 kind.ruleset->decl_list; 00356 } 00357 00358 break; 00359 00360 case AT_FONT_FACE_RULE_STMT: 00361 if (a_decl->parent_statement->kind.font_face_rule) { 00362 children_decl_ptr = 00363 &a_decl->parent_statement-> 00364 kind.font_face_rule->decl_list; 00365 } 00366 break; 00367 case AT_PAGE_RULE_STMT: 00368 if (a_decl->parent_statement->kind.page_rule) { 00369 children_decl_ptr = 00370 &a_decl->parent_statement-> 00371 kind.page_rule->decl_list; 00372 } 00373 00374 default: 00375 break; 00376 } 00377 if (children_decl_ptr 00378 && *children_decl_ptr && *children_decl_ptr == a_decl) 00379 *children_decl_ptr = (*children_decl_ptr)->next; 00380 } 00381 00382 a_decl->next = NULL; 00383 a_decl->prev = NULL; 00384 a_decl->parent_statement = NULL; 00385 00386 return result; 00387 } 00388 00389 /** 00390 * cr_declaration_prepend: 00391 * @a_this: the current declaration list. 00392 * @a_new: the declaration to prepend. 00393 * 00394 * prepends a declaration to the current declaration list. 00395 * 00396 * Returns the list with a_new prepended or NULL in case of error. 00397 */ 00398 CRDeclaration * 00399 cr_declaration_prepend (CRDeclaration * a_this, CRDeclaration * a_new) 00400 { 00401 CRDeclaration *cur = NULL; 00402 00403 g_return_val_if_fail (a_new, NULL); 00404 00405 if (!a_this) 00406 return a_new; 00407 00408 a_this->prev = a_new; 00409 a_new->next = a_this; 00410 00411 for (cur = a_new; cur && cur->prev; cur = cur->prev) ; 00412 00413 return cur; 00414 } 00415 00416 /** 00417 * cr_declaration_append2: 00418 *@a_this: the current declaration list. 00419 *@a_prop: the property string of the declaration to append. 00420 *@a_value: the value of the declaration to append. 00421 * 00422 *Appends a declaration to the current declaration list. 00423 *Returns the list with the new property appended to it, or NULL in 00424 *case of an error. 00425 */ 00426 CRDeclaration * 00427 cr_declaration_append2 (CRDeclaration * a_this, 00428 CRString * a_prop, CRTerm * a_value) 00429 { 00430 CRDeclaration *new_elem = NULL; 00431 00432 if (a_this) { 00433 new_elem = cr_declaration_new (a_this->parent_statement, 00434 a_prop, a_value); 00435 } else { 00436 new_elem = cr_declaration_new (NULL, a_prop, a_value); 00437 } 00438 00439 g_return_val_if_fail (new_elem, NULL); 00440 00441 return cr_declaration_append (a_this, new_elem); 00442 } 00443 00444 /** 00445 * cr_declaration_dump: 00446 *@a_this: the current instance of #CRDeclaration. 00447 *@a_fp: the destination file. 00448 *@a_indent: the number of indentation white char. 00449 *@a_one_per_line: whether to put one declaration per line of not . 00450 * 00451 * 00452 *Dumps a declaration list to a file. 00453 */ 00454 void 00455 cr_declaration_dump (CRDeclaration const * a_this, FILE * a_fp, glong a_indent, 00456 gboolean a_one_per_line) 00457 { 00458 CRDeclaration const *cur = NULL; 00459 00460 g_return_if_fail (a_this); 00461 00462 for (cur = a_this; cur; cur = cur->next) { 00463 if (cur->prev) { 00464 if (a_one_per_line == TRUE) 00465 fprintf (a_fp, ";\n"); 00466 else 00467 fprintf (a_fp, "; "); 00468 } 00469 dump (cur, a_fp, a_indent); 00470 } 00471 } 00472 00473 /** 00474 * cr_declaration_dump_one: 00475 *@a_this: the current instance of #CRDeclaration. 00476 *@a_fp: the destination file. 00477 *@a_indent: the number of indentation white char. 00478 * 00479 *Dumps the first declaration of the declaration list to a file. 00480 */ 00481 void 00482 cr_declaration_dump_one (CRDeclaration const * a_this, FILE * a_fp, glong a_indent) 00483 { 00484 g_return_if_fail (a_this); 00485 00486 dump (a_this, a_fp, a_indent); 00487 } 00488 00489 /** 00490 * cr_declaration_to_string: 00491 *@a_this: the current instance of #CRDeclaration. 00492 *@a_indent: the number of indentation white char 00493 *to put before the actual serialisation. 00494 * 00495 *Serializes the declaration into a string 00496 *Returns the serialized form the declaration. The caller must 00497 *free the string using g_free(). 00498 */ 00499 gchar * 00500 cr_declaration_to_string (CRDeclaration const * a_this, gulong a_indent) 00501 { 00502 GString *stringue = NULL; 00503 00504 gchar *str = NULL, 00505 *result = NULL; 00506 00507 g_return_val_if_fail (a_this, NULL); 00508 00509 stringue = g_string_new (NULL); 00510 00511 if (a_this->property 00512 && a_this->property->stryng 00513 && a_this->property->stryng->str) { 00514 str = g_strndup (a_this->property->stryng->str, 00515 a_this->property->stryng->len); 00516 if (str) { 00517 cr_utils_dump_n_chars2 (' ', stringue, 00518 a_indent); 00519 g_string_append (stringue, str); 00520 g_free (str); 00521 str = NULL; 00522 } else 00523 goto error; 00524 00525 if (a_this->value) { 00526 guchar *value_str = NULL; 00527 00528 value_str = cr_term_to_string (a_this->value); 00529 if (value_str) { 00530 g_string_append_printf (stringue, " : %s", 00531 value_str); 00532 g_free (value_str); 00533 } else 00534 goto error; 00535 } 00536 if (a_this->important == TRUE) { 00537 g_string_append_printf (stringue, " %s", 00538 "!important"); 00539 } 00540 } 00541 if (stringue && stringue->str) { 00542 result = stringue->str; 00543 g_string_free (stringue, FALSE); 00544 } 00545 return result; 00546 00547 error: 00548 if (stringue) { 00549 g_string_free (stringue, TRUE); 00550 stringue = NULL; 00551 } 00552 if (str) { 00553 g_free (str); 00554 str = NULL; 00555 } 00556 00557 return result; 00558 } 00559 00560 /** 00561 * cr_declaration_list_to_string: 00562 *@a_this: the current instance of #CRDeclaration. 00563 *@a_indent: the number of indentation white char 00564 *to put before the actual serialisation. 00565 * 00566 *Serializes the declaration list into a string 00567 */ 00568 guchar * 00569 cr_declaration_list_to_string (CRDeclaration const * a_this, gulong a_indent) 00570 { 00571 CRDeclaration const *cur = NULL; 00572 GString *stringue = NULL; 00573 guchar *str = NULL, 00574 *result = NULL; 00575 00576 g_return_val_if_fail (a_this, NULL); 00577 00578 stringue = g_string_new (NULL); 00579 00580 for (cur = a_this; cur; cur = cur->next) { 00581 str = (guchar *) cr_declaration_to_string (cur, a_indent); 00582 if (str) { 00583 g_string_append_printf (stringue, "%s;", str); 00584 g_free (str); 00585 } else 00586 break; 00587 } 00588 if (stringue && stringue->str) { 00589 result = (guchar *) stringue->str; 00590 g_string_free (stringue, FALSE); 00591 } 00592 00593 return result; 00594 } 00595 00596 /** 00597 * cr_declaration_list_to_string2: 00598 *@a_this: the current instance of #CRDeclaration. 00599 *@a_indent: the number of indentation white char 00600 *@a_one_decl_per_line: whether to output one doc per line or not. 00601 *to put before the actual serialisation. 00602 * 00603 *Serializes the declaration list into a string 00604 *Returns the serialized form the declararation. 00605 */ 00606 guchar * 00607 cr_declaration_list_to_string2 (CRDeclaration const * a_this, 00608 gulong a_indent, gboolean a_one_decl_per_line) 00609 { 00610 CRDeclaration const *cur = NULL; 00611 GString *stringue = NULL; 00612 guchar *str = NULL, 00613 *result = NULL; 00614 00615 g_return_val_if_fail (a_this, NULL); 00616 00617 stringue = g_string_new (NULL); 00618 00619 for (cur = a_this; cur; cur = cur->next) { 00620 str = (guchar *) cr_declaration_to_string (cur, a_indent); 00621 if (str) { 00622 if (a_one_decl_per_line == TRUE) { 00623 if (cur->next) 00624 g_string_append_printf (stringue, 00625 "%s;\n", str); 00626 else 00627 g_string_append (stringue, 00628 (const gchar *) str); 00629 } else { 00630 if (cur->next) 00631 g_string_append_printf (stringue, 00632 "%s;", str); 00633 else 00634 g_string_append (stringue, 00635 (const gchar *) str); 00636 } 00637 g_free (str); 00638 } else 00639 break; 00640 } 00641 if (stringue && stringue->str) { 00642 result = (guchar *) stringue->str; 00643 g_string_free (stringue, FALSE); 00644 } 00645 00646 return result; 00647 } 00648 00649 /** 00650 * cr_declaration_nr_props: 00651 *@a_this: the current instance of #CRDeclaration. 00652 *Return the number of properties in the declaration 00653 */ 00654 gint 00655 cr_declaration_nr_props (CRDeclaration const * a_this) 00656 { 00657 CRDeclaration const *cur = NULL; 00658 int nr = 0; 00659 00660 g_return_val_if_fail (a_this, -1); 00661 00662 for (cur = a_this; cur; cur = cur->next) 00663 nr++; 00664 return nr; 00665 } 00666 00667 /** 00668 * cr_declaration_get_from_list: 00669 *@a_this: the current instance of #CRDeclaration. 00670 *@itemnr: the index into the declaration list. 00671 * 00672 *Use an index to get a CRDeclaration from the declaration list. 00673 * 00674 *Returns #CRDeclaration at position itemnr, 00675 *if itemnr > number of declarations - 1, 00676 *it will return NULL. 00677 */ 00678 CRDeclaration * 00679 cr_declaration_get_from_list (CRDeclaration * a_this, int itemnr) 00680 { 00681 CRDeclaration *cur = NULL; 00682 int nr = 0; 00683 00684 g_return_val_if_fail (a_this, NULL); 00685 00686 for (cur = a_this; cur; cur = cur->next) 00687 if (nr++ == itemnr) 00688 return cur; 00689 return NULL; 00690 } 00691 00692 /** 00693 * cr_declaration_get_by_prop_name: 00694 *@a_this: the current instance of #CRDeclaration. 00695 *@a_prop: the property name to search for. 00696 * 00697 *Use property name to get a CRDeclaration from the declaration list. 00698 *Returns #CRDeclaration with property name a_prop, or NULL if not found. 00699 */ 00700 CRDeclaration * 00701 cr_declaration_get_by_prop_name (CRDeclaration * a_this, 00702 const guchar * a_prop) 00703 { 00704 CRDeclaration *cur = NULL; 00705 00706 g_return_val_if_fail (a_this, NULL); 00707 g_return_val_if_fail (a_prop, NULL); 00708 00709 for (cur = a_this; cur; cur = cur->next) { 00710 if (cur->property 00711 && cur->property->stryng 00712 && cur->property->stryng->str) { 00713 if (!strcmp (cur->property->stryng->str, 00714 (const char *) a_prop)) { 00715 return cur; 00716 } 00717 } 00718 } 00719 return NULL; 00720 } 00721 00722 /** 00723 * cr_declaration_ref: 00724 *@a_this: the current instance of #CRDeclaration. 00725 * 00726 *Increases the ref count of the current instance of #CRDeclaration. 00727 */ 00728 void 00729 cr_declaration_ref (CRDeclaration * a_this) 00730 { 00731 g_return_if_fail (a_this); 00732 00733 a_this->ref_count++; 00734 } 00735 00736 /** 00737 * cr_declaration_unref: 00738 *@a_this: the current instance of #CRDeclaration. 00739 * 00740 *Decrements the ref count of the current instance of #CRDeclaration. 00741 *If the ref count reaches zero, the current instance of #CRDeclaration 00742 *if destroyed. 00743 *Returns TRUE if @a_this was destroyed (ref count reached zero), 00744 *FALSE otherwise. 00745 */ 00746 gboolean 00747 cr_declaration_unref (CRDeclaration * a_this) 00748 { 00749 g_return_val_if_fail (a_this, FALSE); 00750 00751 if (a_this->ref_count) { 00752 a_this->ref_count--; 00753 } 00754 00755 if (a_this->ref_count == 0) { 00756 cr_declaration_destroy (a_this); 00757 return TRUE; 00758 } 00759 return FALSE; 00760 } 00761 00762 /** 00763 * cr_declaration_destroy: 00764 *@a_this: the current instance of #CRDeclaration. 00765 * 00766 *Destructor of the declaration list. 00767 */ 00768 void 00769 cr_declaration_destroy (CRDeclaration * a_this) 00770 { 00771 CRDeclaration *cur = NULL; 00772 00773 g_return_if_fail (a_this); 00774 00775 /* 00776 * Go to the last element of the list. 00777 */ 00778 for (cur = a_this; cur->next; cur = cur->next) 00779 g_assert (cur->next->prev == cur); 00780 00781 /* 00782 * Walk backward the list and free each "next" element. 00783 * Meanwhile, free each property/value pair contained in the list. 00784 */ 00785 for (; cur; cur = cur->prev) { 00786 g_free (cur->next); 00787 cur->next = NULL; 00788 00789 if (cur->property) { 00790 cr_string_destroy (cur->property); 00791 cur->property = NULL; 00792 } 00793 00794 if (cur->value) { 00795 cr_term_destroy (cur->value); 00796 cur->value = NULL; 00797 } 00798 } 00799 00800 g_free (a_this); 00801 }