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