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