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