Imported Upstream version 0.18.1.1
[platform/upstream/gettext.git] / gnulib-local / lib / libcroco / cr-term.c
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 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  */
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
36 static void
37 cr_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  */
85 CRTerm *
86 cr_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  */
106 CRTerm *
107 cr_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
141 enum CRStatus
142 cr_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
153 enum CRStatus
154 cr_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
167 enum CRStatus
168 cr_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
179 enum CRStatus
180 cr_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
191 enum CRStatus
192 cr_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
203 enum CRStatus
204 cr_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
215 enum CRStatus
216 cr_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  */
235 CRTerm *
236 cr_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  */
261 CRTerm *
262 cr_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  */
279 guchar *
280 cr_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
476 guchar *
477 cr_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  */
680 void
681 cr_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  */
700 int
701 cr_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  */
720 CRTerm *
721 cr_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  */
739 void
740 cr_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  */
754 gboolean
755 cr_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  */
776 void
777 cr_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 }