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