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 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 <stdio.h> 26#include <string.h> 27#include "cr-term.h" 28#include "cr-num.h" 29#include "cr-parser.h" 30 31/** 32 *@file 33 *Definition of the #CRTem class. 34 */ 35 36static void 37cr_term_clear (CRTerm * a_this) 38{ 39 g_return_if_fail (a_this); 40 41 switch (a_this->type) { 42 case TERM_NUMBER: 43 if (a_this->content.num) { 44 cr_num_destroy (a_this->content.num); 45 a_this->content.num = NULL; 46 } 47 break; 48 49 case TERM_FUNCTION: 50 if (a_this->ext_content.func_param) { 51 cr_term_destroy (a_this->ext_content.func_param); 52 a_this->ext_content.func_param = NULL; 53 } 54 case TERM_STRING: 55 case TERM_IDENT: 56 case TERM_URI: 57 case TERM_HASH: 58 if (a_this->content.str) { 59 cr_string_destroy (a_this->content.str); 60 a_this->content.str = NULL; 61 } 62 break; 63 64 case TERM_RGB: 65 if (a_this->content.rgb) { 66 cr_rgb_destroy (a_this->content.rgb); 67 a_this->content.rgb = NULL; 68 } 69 break; 70 71 case TERM_UNICODERANGE: 72 case TERM_NO_TYPE: 73 default: 74 break; 75 } 76 77 a_this->type = TERM_NO_TYPE; 78} 79 80/** 81 *Instanciate a #CRTerm. 82 *@return the newly build instance 83 *of #CRTerm. 84 */ 85CRTerm * 86cr_term_new (void) 87{ 88 CRTerm *result = NULL; 89 90 result = g_try_malloc (sizeof (CRTerm)); 91 if (!result) { 92 cr_utils_trace_info ("Out of memory"); 93 return NULL; 94 } 95 memset (result, 0, sizeof (CRTerm)); 96 return result; 97} 98 99/** 100 *Parses an expresion as defined by the css2 spec 101 *and builds the expression as a list of terms. 102 *@param a_buf the buffer to parse. 103 *@return a pointer to the first term of the expression or 104 *NULL if parsing failed. 105 */ 106CRTerm * 107cr_term_parse_expression_from_buf (const guchar * a_buf, 108 enum CREncoding a_encoding) 109{ 110 CRParser *parser = NULL; 111 CRTerm *result = NULL; 112 enum CRStatus status = CR_OK; 113 114 g_return_val_if_fail (a_buf, NULL); 115 116 parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen (a_buf), 117 a_encoding, FALSE); 118 g_return_val_if_fail (parser, NULL); 119 120 status = cr_parser_try_to_skip_spaces_and_comments (parser); 121 if (status != CR_OK) { 122 goto cleanup; 123 } 124 status = cr_parser_parse_expr (parser, &result); 125 if (status != CR_OK) { 126 if (result) { 127 cr_term_destroy (result); 128 result = NULL; 129 } 130 } 131 132 cleanup: 133 if (parser) { 134 cr_parser_destroy (parser); 135 parser = NULL; 136 } 137 138 return result; 139} 140 141enum CRStatus 142cr_term_set_number (CRTerm * a_this, CRNum * a_num) 143{ 144 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); 145 146 cr_term_clear (a_this); 147 148 a_this->type = TERM_NUMBER; 149 a_this->content.num = a_num; 150 return CR_OK; 151} 152 153enum CRStatus 154cr_term_set_function (CRTerm * a_this, CRString * a_func_name, 155 CRTerm * a_func_param) 156{ 157 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); 158 159 cr_term_clear (a_this); 160 161 a_this->type = TERM_FUNCTION; 162 a_this->content.str = a_func_name; 163 a_this->ext_content.func_param = a_func_param; 164 return CR_OK; 165} 166 167enum CRStatus 168cr_term_set_string (CRTerm * a_this, CRString * a_str) 169{ 170 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); 171 172 cr_term_clear (a_this); 173 174 a_this->type = TERM_STRING; 175 a_this->content.str = a_str; 176 return CR_OK; 177} 178 179enum CRStatus 180cr_term_set_ident (CRTerm * a_this, CRString * a_str) 181{ 182 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); 183 184 cr_term_clear (a_this); 185 186 a_this->type = TERM_IDENT; 187 a_this->content.str = a_str; 188 return CR_OK; 189} 190 191enum CRStatus 192cr_term_set_uri (CRTerm * a_this, CRString * a_str) 193{ 194 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); 195 196 cr_term_clear (a_this); 197 198 a_this->type = TERM_URI; 199 a_this->content.str = a_str; 200 return CR_OK; 201} 202 203enum CRStatus 204cr_term_set_rgb (CRTerm * a_this, CRRgb * a_rgb) 205{ 206 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); 207 208 cr_term_clear (a_this); 209 210 a_this->type = TERM_RGB; 211 a_this->content.rgb = a_rgb; 212 return CR_OK; 213} 214 215enum CRStatus 216cr_term_set_hash (CRTerm * a_this, CRString * a_str) 217{ 218 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); 219 220 cr_term_clear (a_this); 221 222 a_this->type = TERM_HASH; 223 a_this->content.str = a_str; 224 return CR_OK; 225} 226 227/** 228 *Appends a new term to the current list of #CRTerm. 229 * 230 *@param a_this the "this pointer" of the current instance 231 *of #CRTerm . 232 *@param a_new_term the term to append. 233 *@return the list of terms with the a_new_term appended to it. 234 */ 235CRTerm * 236cr_term_append_term (CRTerm * a_this, CRTerm * a_new_term) 237{ 238 CRTerm *cur = NULL; 239 240 g_return_val_if_fail (a_new_term, NULL); 241 242 if (a_this == NULL) 243 return a_new_term; 244 245 for (cur = a_this; cur->next; cur = cur->next) ; 246 247 cur->next = a_new_term; 248 a_new_term->prev = cur; 249 250 return a_this; 251} 252 253/** 254 *Prepends a term to the list of terms represented by a_this. 255 * 256 *@param a_this the "this pointer" of the current instance of 257 *#CRTerm . 258 *@param a_new_term the term to prepend. 259 *@return the head of the new list. 260 */ 261CRTerm * 262cr_term_prepend_term (CRTerm * a_this, CRTerm * a_new_term) 263{ 264 g_return_val_if_fail (a_this && a_new_term, NULL); 265 266 a_new_term->next = a_this; 267 a_this->prev = a_new_term; 268 269 return a_new_term; 270} 271 272/** 273 *Serializes the expression represented by 274 *the chained instances of #CRterm. 275 *@param a_this the current instance of #CRTerm 276 *@return the zero terminated string containing the serialized 277 *form of #CRTerm. MUST BE FREED BY THE CALLER using g_free(). 278 */ 279guchar * 280cr_term_to_string (CRTerm * a_this) 281{ 282 GString *str_buf = NULL; 283 CRTerm *cur = NULL; 284 guchar *result = NULL, 285 *content = NULL; 286 287 g_return_val_if_fail (a_this, NULL); 288 289 str_buf = g_string_new (NULL); 290 g_return_val_if_fail (str_buf, NULL); 291 292 for (cur = a_this; cur; cur = cur->next) { 293 if ((cur->content.str == NULL) 294 && (cur->content.num == NULL) 295 && (cur->content.str == NULL) 296 && (cur->content.rgb == NULL)) 297 continue; 298 299 switch (cur->the_operator) { 300 case DIVIDE: 301 g_string_append (str_buf, " / "); 302 break; 303 304 case COMMA: 305 g_string_append (str_buf, ", "); 306 break; 307 308 case NO_OP: 309 if (cur->prev) { 310 g_string_append (str_buf, " "); 311 } 312 break; 313 default: 314 315 break; 316 } 317 318 switch (cur->unary_op) { 319 case PLUS_UOP: 320 g_string_append (str_buf, "+"); 321 break; 322 323 case MINUS_UOP: 324 g_string_append (str_buf, "-"); 325 break; 326 327 default: 328 break; 329 } 330 331 switch (cur->type) { 332 case TERM_NUMBER: 333 if (cur->content.num) { 334 content = cr_num_to_string (cur->content.num); 335 } 336 337 if (content) { 338 g_string_append (str_buf, content); 339 g_free (content); 340 content = NULL; 341 } 342 343 break; 344 345 case TERM_FUNCTION: 346 if (cur->content.str) { 347 content = g_strndup 348 (cur->content.str->stryng->str, 349 cur->content.str->stryng->len); 350 } 351 352 if (content) { 353 g_string_append_printf (str_buf, "%s(", 354 content); 355 356 if (cur->ext_content.func_param) { 357 guchar *tmp_str = NULL; 358 359 tmp_str = cr_term_to_string 360 (cur-> 361 ext_content.func_param); 362 363 if (tmp_str) { 364 g_string_append (str_buf, 365 tmp_str); 366 g_free (tmp_str); 367 tmp_str = NULL; 368 } 369 370 g_free (content); 371 content = NULL; 372 } 373 g_string_append (str_buf, ")"); 374 } 375 376 break; 377 378 case TERM_STRING: 379 if (cur->content.str) { 380 content = g_strndup 381 (cur->content.str->stryng->str, 382 cur->content.str->stryng->len); 383 } 384 385 if (content) { 386 g_string_append_printf (str_buf, 387 "\"%s\"", content); 388 g_free (content); 389 content = NULL; 390 } 391 break; 392 393 case TERM_IDENT: 394 if (cur->content.str) { 395 content = g_strndup 396 (cur->content.str->stryng->str, 397 cur->content.str->stryng->len); 398 } 399 400 if (content) { 401 g_string_append (str_buf, content); 402 g_free (content); 403 content = NULL; 404 } 405 break; 406 407 case TERM_URI: 408 if (cur->content.str) { 409 content = g_strndup 410 (cur->content.str->stryng->str, 411 cur->content.str->stryng->len); 412 } 413 414 if (content) { 415 g_string_append_printf 416 (str_buf, "url(%s)", content); 417 g_free (content); 418 content = NULL; 419 } 420 break; 421 422 case TERM_RGB: 423 if (cur->content.rgb) { 424 guchar *tmp_str = NULL; 425 426 g_string_append (str_buf, "rgb("); 427 tmp_str = cr_rgb_to_string (cur->content.rgb); 428 429 if (tmp_str) { 430 g_string_append (str_buf, tmp_str); 431 g_free (tmp_str); 432 tmp_str = NULL; 433 } 434 g_string_append (str_buf, ")"); 435 } 436 437 break; 438 439 case TERM_UNICODERANGE: 440 g_string_append 441 (str_buf, 442 "?found unicoderange: dump not supported yet?"); 443 break; 444 445 case TERM_HASH: 446 if (cur->content.str) { 447 content = g_strndup 448 (cur->content.str->stryng->str, 449 cur->content.str->stryng->len); 450 } 451 452 if (content) { 453 g_string_append_printf (str_buf, 454 "#%s", content); 455 g_free (content); 456 content = NULL; 457 } 458 break; 459 460 default: 461 g_string_append (str_buf, 462 "Unrecognized Term type"); 463 break; 464 } 465 } 466 467 if (str_buf) { 468 result = str_buf->str; 469 g_string_free (str_buf, FALSE); 470 str_buf = NULL; 471 } 472 473 return result; 474} 475 476guchar * 477cr_term_one_to_string (CRTerm * a_this) 478{ 479 GString *str_buf = NULL; 480 guchar *result = NULL, 481 *content = NULL; 482 483 g_return_val_if_fail (a_this, NULL); 484 485 str_buf = g_string_new (NULL); 486 g_return_val_if_fail (str_buf, NULL); 487 488 if ((a_this->content.str == NULL) 489 && (a_this->content.num == NULL) 490 && (a_this->content.str == NULL) 491 && (a_this->content.rgb == NULL)) 492 return NULL ; 493 494 switch (a_this->the_operator) { 495 case DIVIDE: 496 g_string_append_printf (str_buf, " / "); 497 break; 498 499 case COMMA: 500 g_string_append_printf (str_buf, ", "); 501 break; 502 503 case NO_OP: 504 if (a_this->prev) { 505 g_string_append_printf (str_buf, " "); 506 } 507 break; 508 default: 509 510 break; 511 } 512 513 switch (a_this->unary_op) { 514 case PLUS_UOP: 515 g_string_append_printf (str_buf, "+"); 516 break; 517 518 case MINUS_UOP: 519 g_string_append_printf (str_buf, "-"); 520 break; 521 522 default: 523 break; 524 } 525 526 switch (a_this->type) { 527 case TERM_NUMBER: 528 if (a_this->content.num) { 529 content = cr_num_to_string (a_this->content.num); 530 } 531 532 if (content) { 533 g_string_append (str_buf, content); 534 g_free (content); 535 content = NULL; 536 } 537 538 break; 539 540 case TERM_FUNCTION: 541 if (a_this->content.str) { 542 content = g_strndup 543 (a_this->content.str->stryng->str, 544 a_this->content.str->stryng->len); 545 } 546 547 if (content) { 548 g_string_append_printf (str_buf, "%s(", 549 content); 550 551 if (a_this->ext_content.func_param) { 552 guchar *tmp_str = NULL; 553 554 tmp_str = cr_term_to_string 555 (a_this-> 556 ext_content.func_param); 557 558 if (tmp_str) { 559 g_string_append_printf 560 (str_buf, 561 "%s", tmp_str); 562 g_free (tmp_str); 563 tmp_str = NULL; 564 } 565 566 g_string_append_printf (str_buf, ")"); 567 g_free (content); 568 content = NULL; 569 } 570 } 571 572 break; 573 574 case TERM_STRING: 575 if (a_this->content.str) { 576 content = g_strndup 577 (a_this->content.str->stryng->str, 578 a_this->content.str->stryng->len); 579 } 580 581 if (content) { 582 g_string_append_printf (str_buf, 583 "\"%s\"", content); 584 g_free (content); 585 content = NULL; 586 } 587 break; 588 589 case TERM_IDENT: 590 if (a_this->content.str) { 591 content = g_strndup 592 (a_this->content.str->stryng->str, 593 a_this->content.str->stryng->len); 594 } 595 596 if (content) { 597 g_string_append (str_buf, content); 598 g_free (content); 599 content = NULL; 600 } 601 break; 602 603 case TERM_URI: 604 if (a_this->content.str) { 605 content = g_strndup 606 (a_this->content.str->stryng->str, 607 a_this->content.str->stryng->len); 608 } 609 610 if (content) { 611 g_string_append_printf 612 (str_buf, "url(%s)", content); 613 g_free (content); 614 content = NULL; 615 } 616 break; 617 618 case TERM_RGB: 619 if (a_this->content.rgb) { 620 guchar *tmp_str = NULL; 621 622 g_string_append_printf (str_buf, "rgb("); 623 tmp_str = cr_rgb_to_string (a_this->content.rgb); 624 625 if (tmp_str) { 626 g_string_append (str_buf, tmp_str); 627 g_free (tmp_str); 628 tmp_str = NULL; 629 } 630 g_string_append_printf (str_buf, ")"); 631 } 632 633 break; 634 635 case TERM_UNICODERANGE: 636 g_string_append_printf 637 (str_buf, 638 "?found unicoderange: dump not supported yet?"); 639 break; 640 641 case TERM_HASH: 642 if (a_this->content.str) { 643 content = g_strndup 644 (a_this->content.str->stryng->str, 645 a_this->content.str->stryng->len); 646 } 647 648 if (content) { 649 g_string_append_printf (str_buf, 650 "#%s", content); 651 g_free (content); 652 content = NULL; 653 } 654 break; 655 656 default: 657 g_string_append_printf (str_buf, 658 "%s", 659 "Unrecognized Term type"); 660 break; 661 } 662 663 if (str_buf) { 664 result = str_buf->str; 665 g_string_free (str_buf, FALSE); 666 str_buf = NULL; 667 } 668 669 return result; 670} 671 672/** 673 *Dumps the expression (a list of terms connected by operators) 674 *to a file. 675 *TODO: finish the dump. The dump of some type of terms have not yet been 676 *implemented. 677 *@param a_this the current instance of #CRTerm. 678 *@param a_fp the destination file pointer. 679 */ 680void 681cr_term_dump (CRTerm * a_this, FILE * a_fp) 682{ 683 guchar *content = NULL; 684 685 g_return_if_fail (a_this); 686 687 content = cr_term_to_string (a_this); 688 689 if (content) { 690 fprintf (a_fp, "%s", content); 691 g_free (content); 692 } 693} 694 695/** 696 *Return the number of terms in the expression. 697 *@param a_this the current instance of #CRTerm. 698 *@return number of terms in the expression. 699 */ 700int 701cr_term_nr_values (CRTerm *a_this) 702{ 703 CRTerm *cur = NULL ; 704 int nr = 0; 705 706 g_return_val_if_fail (a_this, -1) ; 707 708 for (cur = a_this ; cur ; cur = cur->next) 709 nr ++; 710 return nr; 711} 712 713/** 714 *Use an index to get a CRTerm from the expression. 715 *@param a_this the current instance of #CRTerm. 716 *@param itemnr the index into the expression. 717 *@return CRTerm at position itemnr, if itemnr > number of terms - 1, 718 *it will return NULL. 719 */ 720CRTerm * 721cr_term_get_from_list (CRTerm *a_this, int itemnr) 722{ 723 CRTerm *cur = NULL ; 724 int nr = 0; 725 726 g_return_val_if_fail (a_this, NULL) ; 727 728 for (cur = a_this ; cur ; cur = cur->next) 729 if (nr++ == itemnr) 730 return cur; 731 return NULL; 732} 733 734/** 735 *Increments the reference counter of the current instance 736 *of #CRTerm.* 737 *@param a_this the current instance of #CRTerm. 738 */ 739void 740cr_term_ref (CRTerm * a_this) 741{ 742 g_return_if_fail (a_this); 743 744 a_this->ref_count++; 745} 746 747/** 748 *Decrements the ref count of the current instance of 749 *#CRTerm. If the ref count reaches zero, the instance is 750 *destroyed. 751 *@param a_this the current instance of #CRTerm. 752 *@return TRUE if the current instance has been destroyed, FALSE otherwise. 753 */ 754gboolean 755cr_term_unref (CRTerm * a_this) 756{ 757 g_return_val_if_fail (a_this, FALSE); 758 759 if (a_this->ref_count) { 760 a_this->ref_count--; 761 } 762 763 if (a_this->ref_count == 0) { 764 cr_term_destroy (a_this); 765 return TRUE; 766 } 767 768 return FALSE; 769} 770 771/** 772 *The destructor of the the #CRTerm class. 773 *@param a_this the "this pointer" of the current instance 774 *of #CRTerm. 775 */ 776void 777cr_term_destroy (CRTerm * a_this) 778{ 779 g_return_if_fail (a_this); 780 781 cr_term_clear (a_this); 782 783 if (a_this->next) { 784 cr_term_destroy (a_this->next); 785 a_this->next = NULL; 786 } 787 788 if (a_this) { 789 g_free (a_this); 790 } 791 792} 793