Code

finally getting closer to processing axes and contexts correctly
[inkscape.git] / src / libcroco / cr-term.c
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)
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;
140 enum CRStatus
141 cr_term_set_number (CRTerm * a_this, CRNum * a_num)
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;
152 enum CRStatus
153 cr_term_set_function (CRTerm * a_this, CRString * a_func_name,
154                       CRTerm * a_func_param)
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;
166 enum CRStatus
167 cr_term_set_string (CRTerm * a_this, CRString * a_str)
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;
178 enum CRStatus
179 cr_term_set_ident (CRTerm * a_this, CRString * a_str)
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;
190 enum CRStatus
191 cr_term_set_uri (CRTerm * a_this, CRString * a_str)
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;
202 enum CRStatus
203 cr_term_set_rgb (CRTerm * a_this, CRRgb * a_rgb)
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;
214 enum CRStatus
215 cr_term_set_hash (CRTerm * a_this, CRString * a_str)
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;
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)
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;
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)
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;
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)
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;
475 guchar *
476 cr_term_one_to_string (CRTerm * a_this)
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;
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)
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         }
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)
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;
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)
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;
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)
741         g_return_if_fail (a_this);
743         a_this->ref_count++;
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)
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;
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)
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         }