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 = (CRTerm *)g_try_malloc (sizeof (CRTerm));
88 if (!result) {
89 cr_utils_trace_info ("Out of memory");
90 return NULL;
91 }
92 memset (result, 0, sizeof (CRTerm));
93 return result;
94 }
96 /**
97 *Parses an expresion as defined by the css2 spec
98 *and builds the expression as a list of terms.
99 *@param a_buf the buffer to parse.
100 *@return a pointer to the first term of the expression or
101 *NULL if parsing failed.
102 */
103 CRTerm *
104 cr_term_parse_expression_from_buf (const guchar * a_buf,
105 enum CREncoding a_encoding)
106 {
107 CRTerm *result = NULL;
108 enum CRStatus status = CR_OK;
110 g_return_val_if_fail (a_buf, NULL);
112 CRParser *parser = cr_parser_new_from_buf (
113 (guchar*)a_buf,
114 strlen ((char *)a_buf),
115 a_encoding, FALSE);
116 g_return_val_if_fail (parser, NULL);
118 status = cr_parser_try_to_skip_spaces_and_comments (parser);
119 if (status != CR_OK) {
120 goto cleanup;
121 }
122 status = cr_parser_parse_expr (parser, &result);
123 if (status != CR_OK) {
124 if (result) {
125 cr_term_destroy (result);
126 result = NULL;
127 }
128 }
130 cleanup:
131 if (parser) {
132 cr_parser_destroy (parser);
133 parser = NULL;
134 }
136 return result;
137 }
139 enum CRStatus
140 cr_term_set_number (CRTerm * a_this, CRNum * a_num)
141 {
142 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
144 cr_term_clear (a_this);
146 a_this->type = TERM_NUMBER;
147 a_this->content.num = a_num;
148 return CR_OK;
149 }
151 enum CRStatus
152 cr_term_set_function (CRTerm * a_this, CRString * a_func_name,
153 CRTerm * a_func_param)
154 {
155 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
157 cr_term_clear (a_this);
159 a_this->type = TERM_FUNCTION;
160 a_this->content.str = a_func_name;
161 a_this->ext_content.func_param = a_func_param;
162 return CR_OK;
163 }
165 enum CRStatus
166 cr_term_set_string (CRTerm * a_this, CRString * a_str)
167 {
168 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
170 cr_term_clear (a_this);
172 a_this->type = TERM_STRING;
173 a_this->content.str = a_str;
174 return CR_OK;
175 }
177 enum CRStatus
178 cr_term_set_ident (CRTerm * a_this, CRString * a_str)
179 {
180 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
182 cr_term_clear (a_this);
184 a_this->type = TERM_IDENT;
185 a_this->content.str = a_str;
186 return CR_OK;
187 }
189 enum CRStatus
190 cr_term_set_uri (CRTerm * a_this, CRString * a_str)
191 {
192 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
194 cr_term_clear (a_this);
196 a_this->type = TERM_URI;
197 a_this->content.str = a_str;
198 return CR_OK;
199 }
201 enum CRStatus
202 cr_term_set_rgb (CRTerm * a_this, CRRgb * a_rgb)
203 {
204 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
206 cr_term_clear (a_this);
208 a_this->type = TERM_RGB;
209 a_this->content.rgb = a_rgb;
210 return CR_OK;
211 }
213 enum CRStatus
214 cr_term_set_hash (CRTerm * a_this, CRString * a_str)
215 {
216 g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
218 cr_term_clear (a_this);
220 a_this->type = TERM_HASH;
221 a_this->content.str = a_str;
222 return CR_OK;
223 }
225 /**
226 *Appends a new term to the current list of #CRTerm.
227 *
228 *@param a_this the "this pointer" of the current instance
229 *of #CRTerm .
230 *@param a_new_term the term to append.
231 *@return the list of terms with the a_new_term appended to it.
232 */
233 CRTerm *
234 cr_term_append_term (CRTerm * a_this, CRTerm * a_new_term)
235 {
236 CRTerm *cur = NULL;
238 g_return_val_if_fail (a_new_term, NULL);
240 if (a_this == NULL)
241 return a_new_term;
243 for (cur = a_this; cur->next; cur = cur->next) ;
245 cur->next = a_new_term;
246 a_new_term->prev = cur;
248 return a_this;
249 }
251 /**
252 *Prepends a term to the list of terms represented by a_this.
253 *
254 *@param a_this the "this pointer" of the current instance of
255 *#CRTerm .
256 *@param a_new_term the term to prepend.
257 *@return the head of the new list.
258 */
259 CRTerm *
260 cr_term_prepend_term (CRTerm * a_this, CRTerm * a_new_term)
261 {
262 g_return_val_if_fail (a_this && a_new_term, NULL);
264 a_new_term->next = a_this;
265 a_this->prev = a_new_term;
267 return a_new_term;
268 }
270 /**
271 *Serializes the expression represented by
272 *the chained instances of #CRterm.
273 *@param a_this the current instance of #CRTerm
274 *@return the zero terminated string containing the serialized
275 *form of #CRTerm. MUST BE FREED BY THE CALLER using g_free().
276 */
277 guchar *
278 cr_term_to_string (CRTerm * a_this)
279 {
280 GString *str_buf = NULL;
281 CRTerm *cur = NULL;
282 guchar *result = NULL;
283 gchar *content = NULL;
285 g_return_val_if_fail (a_this, NULL);
287 str_buf = g_string_new (NULL);
288 g_return_val_if_fail (str_buf, NULL);
290 for (cur = a_this; cur; cur = cur->next) {
291 if ((cur->content.str == NULL)
292 && (cur->content.num == NULL)
293 && (cur->content.str == NULL)
294 && (cur->content.rgb == NULL))
295 continue;
297 switch (cur->the_operator) {
298 case DIVIDE:
299 g_string_append (str_buf, " / ");
300 break;
302 case COMMA:
303 g_string_append (str_buf, ", ");
304 break;
306 case NO_OP:
307 if (cur->prev) {
308 g_string_append (str_buf, " ");
309 }
310 break;
311 default:
313 break;
314 }
316 switch (cur->unary_op) {
317 case PLUS_UOP:
318 g_string_append (str_buf, "+");
319 break;
321 case MINUS_UOP:
322 g_string_append (str_buf, "-");
323 break;
325 default:
326 break;
327 }
329 switch (cur->type) {
330 case TERM_NUMBER:
331 if (cur->content.num) {
332 content = (gchar *)cr_num_to_string (cur->content.num);
333 }
335 if (content) {
336 g_string_append (str_buf, content);
337 g_free (content);
338 content = NULL;
339 }
341 break;
343 case TERM_FUNCTION:
344 if (cur->content.str) {
345 content = g_strndup
346 (cur->content.str->stryng->str,
347 cur->content.str->stryng->len);
348 }
350 if (content) {
351 g_string_append_printf (str_buf, "%s(",
352 content);
354 if (cur->ext_content.func_param) {
355 guchar *tmp_str = NULL;
357 tmp_str = cr_term_to_string
358 (cur->
359 ext_content.func_param);
361 if (tmp_str) {
362 g_string_append (str_buf,
363 (gchar *)tmp_str);
364 g_free (tmp_str);
365 tmp_str = NULL;
366 }
368 g_free (content);
369 content = NULL;
370 }
371 g_string_append (str_buf, ")");
372 }
374 break;
376 case TERM_STRING:
377 if (cur->content.str) {
378 content = g_strndup
379 (cur->content.str->stryng->str,
380 cur->content.str->stryng->len);
381 }
383 if (content) {
384 g_string_append_printf (str_buf,
385 "\"%s\"", content);
386 g_free (content);
387 content = NULL;
388 }
389 break;
391 case TERM_IDENT:
392 if (cur->content.str) {
393 content = g_strndup
394 (cur->content.str->stryng->str,
395 cur->content.str->stryng->len);
396 }
398 if (content) {
399 g_string_append (str_buf, content);
400 g_free (content);
401 content = NULL;
402 }
403 break;
405 case TERM_URI:
406 if (cur->content.str) {
407 content = g_strndup
408 (cur->content.str->stryng->str,
409 cur->content.str->stryng->len);
410 }
412 if (content) {
413 g_string_append_printf
414 (str_buf, "url(%s)", content);
415 g_free (content);
416 content = NULL;
417 }
418 break;
420 case TERM_RGB:
421 if (cur->content.rgb) {
422 guchar *tmp_str = NULL;
424 g_string_append (str_buf, "rgb(");
425 tmp_str = cr_rgb_to_string (cur->content.rgb);
427 if (tmp_str) {
428 g_string_append (str_buf, (gchar *)tmp_str);
429 g_free (tmp_str);
430 tmp_str = NULL;
431 }
432 g_string_append (str_buf, ")");
433 }
435 break;
437 case TERM_UNICODERANGE:
438 g_string_append
439 (str_buf,
440 "?found unicoderange: dump not supported yet?");
441 break;
443 case TERM_HASH:
444 if (cur->content.str) {
445 content = g_strndup
446 (cur->content.str->stryng->str,
447 cur->content.str->stryng->len);
448 }
450 if (content) {
451 g_string_append_printf (str_buf,
452 "#%s", content);
453 g_free (content);
454 content = NULL;
455 }
456 break;
458 default:
459 g_string_append (str_buf,
460 "Unrecognized Term type");
461 break;
462 }
463 }
465 if (str_buf) {
466 result = (guchar *)str_buf->str;
467 g_string_free (str_buf, FALSE);
468 str_buf = NULL;
469 }
471 return result;
472 }
474 guchar *
475 cr_term_one_to_string (CRTerm * a_this)
476 {
477 GString *str_buf = NULL;
478 guchar *result = NULL;
479 gchar *content = NULL;
481 g_return_val_if_fail (a_this, NULL);
483 str_buf = g_string_new (NULL);
484 g_return_val_if_fail (str_buf, NULL);
486 if ((a_this->content.str == NULL)
487 && (a_this->content.num == NULL)
488 && (a_this->content.str == NULL)
489 && (a_this->content.rgb == NULL))
490 return NULL ;
492 switch (a_this->the_operator) {
493 case DIVIDE:
494 g_string_append_printf (str_buf, " / ");
495 break;
497 case COMMA:
498 g_string_append_printf (str_buf, ", ");
499 break;
501 case NO_OP:
502 if (a_this->prev) {
503 g_string_append_printf (str_buf, " ");
504 }
505 break;
506 default:
508 break;
509 }
511 switch (a_this->unary_op) {
512 case PLUS_UOP:
513 g_string_append_printf (str_buf, "+");
514 break;
516 case MINUS_UOP:
517 g_string_append_printf (str_buf, "-");
518 break;
520 default:
521 break;
522 }
524 switch (a_this->type) {
525 case TERM_NUMBER:
526 if (a_this->content.num) {
527 content = (gchar *)cr_num_to_string (a_this->content.num);
528 }
530 if (content) {
531 g_string_append (str_buf, content);
532 g_free (content);
533 content = NULL;
534 }
536 break;
538 case TERM_FUNCTION:
539 if (a_this->content.str) {
540 content = g_strndup
541 (a_this->content.str->stryng->str,
542 a_this->content.str->stryng->len);
543 }
545 if (content) {
546 g_string_append_printf (str_buf, "%s(",
547 content);
549 if (a_this->ext_content.func_param) {
550 guchar *tmp_str = NULL;
552 tmp_str = cr_term_to_string
553 (a_this->
554 ext_content.func_param);
556 if (tmp_str) {
557 g_string_append_printf
558 (str_buf,
559 "%s", tmp_str);
560 g_free (tmp_str);
561 tmp_str = NULL;
562 }
564 g_string_append_printf (str_buf, ")");
565 g_free (content);
566 content = NULL;
567 }
568 }
570 break;
572 case TERM_STRING:
573 if (a_this->content.str) {
574 content = g_strndup
575 (a_this->content.str->stryng->str,
576 a_this->content.str->stryng->len);
577 }
579 if (content) {
580 g_string_append_printf (str_buf,
581 "\"%s\"", content);
582 g_free (content);
583 content = NULL;
584 }
585 break;
587 case TERM_IDENT:
588 if (a_this->content.str) {
589 content = g_strndup
590 (a_this->content.str->stryng->str,
591 a_this->content.str->stryng->len);
592 }
594 if (content) {
595 g_string_append (str_buf, content);
596 g_free (content);
597 content = NULL;
598 }
599 break;
601 case TERM_URI:
602 if (a_this->content.str) {
603 content = g_strndup
604 (a_this->content.str->stryng->str,
605 a_this->content.str->stryng->len);
606 }
608 if (content) {
609 g_string_append_printf
610 (str_buf, "url(%s)", content);
611 g_free (content);
612 content = NULL;
613 }
614 break;
616 case TERM_RGB:
617 if (a_this->content.rgb) {
619 g_string_append_printf (str_buf, "rgb(");
620 gchar *tmp_str = (gchar *)cr_rgb_to_string (a_this->content.rgb);
622 if (tmp_str) {
623 g_string_append (str_buf, tmp_str);
624 g_free (tmp_str);
625 tmp_str = NULL;
626 }
627 g_string_append_printf (str_buf, ")");
628 }
630 break;
632 case TERM_UNICODERANGE:
633 g_string_append_printf
634 (str_buf,
635 "?found unicoderange: dump not supported yet?");
636 break;
638 case TERM_HASH:
639 if (a_this->content.str) {
640 content = g_strndup
641 (a_this->content.str->stryng->str,
642 a_this->content.str->stryng->len);
643 }
645 if (content) {
646 g_string_append_printf (str_buf,
647 "#%s", content);
648 g_free (content);
649 content = NULL;
650 }
651 break;
653 default:
654 g_string_append_printf (str_buf,
655 "%s",
656 "Unrecognized Term type");
657 break;
658 }
660 if (str_buf) {
661 result = (guchar *)str_buf->str;
662 g_string_free (str_buf, FALSE);
663 str_buf = NULL;
664 }
666 return result;
667 }
669 /**
670 *Dumps the expression (a list of terms connected by operators)
671 *to a file.
672 *TODO: finish the dump. The dump of some type of terms have not yet been
673 *implemented.
674 *@param a_this the current instance of #CRTerm.
675 *@param a_fp the destination file pointer.
676 */
677 void
678 cr_term_dump (CRTerm * a_this, FILE * a_fp)
679 {
680 guchar *content = NULL;
682 g_return_if_fail (a_this);
684 content = cr_term_to_string (a_this);
686 if (content) {
687 fprintf (a_fp, "%s", content);
688 g_free (content);
689 }
690 }
692 /**
693 *Return the number of terms in the expression.
694 *@param a_this the current instance of #CRTerm.
695 *@return number of terms in the expression.
696 */
697 int
698 cr_term_nr_values (CRTerm *a_this)
699 {
700 CRTerm *cur = NULL ;
701 int nr = 0;
703 g_return_val_if_fail (a_this, -1) ;
705 for (cur = a_this ; cur ; cur = cur->next)
706 nr ++;
707 return nr;
708 }
710 /**
711 *Use an index to get a CRTerm from the expression.
712 *@param a_this the current instance of #CRTerm.
713 *@param itemnr the index into the expression.
714 *@return CRTerm at position itemnr, if itemnr > number of terms - 1,
715 *it will return NULL.
716 */
717 CRTerm *
718 cr_term_get_from_list (CRTerm *a_this, int itemnr)
719 {
720 CRTerm *cur = NULL ;
721 int nr = 0;
723 g_return_val_if_fail (a_this, NULL) ;
725 for (cur = a_this ; cur ; cur = cur->next)
726 if (nr++ == itemnr)
727 return cur;
728 return NULL;
729 }
731 /**
732 *Increments the reference counter of the current instance
733 *of #CRTerm.*
734 *@param a_this the current instance of #CRTerm.
735 */
736 void
737 cr_term_ref (CRTerm * a_this)
738 {
739 g_return_if_fail (a_this);
741 a_this->ref_count++;
742 }
744 /**
745 *Decrements the ref count of the current instance of
746 *#CRTerm. If the ref count reaches zero, the instance is
747 *destroyed.
748 *@param a_this the current instance of #CRTerm.
749 *@return TRUE if the current instance has been destroyed, FALSE otherwise.
750 */
751 gboolean
752 cr_term_unref (CRTerm * a_this)
753 {
754 g_return_val_if_fail (a_this, FALSE);
756 if (a_this->ref_count) {
757 a_this->ref_count--;
758 }
760 if (a_this->ref_count == 0) {
761 cr_term_destroy (a_this);
762 return TRUE;
763 }
765 return FALSE;
766 }
768 /**
769 *The destructor of the the #CRTerm class.
770 *@param a_this the "this pointer" of the current instance
771 *of #CRTerm.
772 */
773 void
774 cr_term_destroy (CRTerm * a_this)
775 {
776 g_return_if_fail (a_this);
778 cr_term_clear (a_this);
780 if (a_this->next) {
781 cr_term_destroy (a_this->next);
782 a_this->next = NULL;
783 }
785 if (a_this) {
786 g_free (a_this);
787 }
789 }