1 /* Fo
2 * fo-expr-eval.c: XSL expression language evaluation module
3 *
4 * Copyright (C) 1998-2002 Daniel Veillard.
5 * Copyright (C) 2007 Menteith Consulting Ltd
6 * Copyright (C) 2001-2004 Sun Microsystems.
7 * Copyright (C) 2011 Mentea
8 *
9 * Based on a previous xmlroff XSL expression language evaluator that
10 * was based on the 'XML Path Language implementation' in 'xpath.c'
11 * from libxml2 by Daniel Veillard.
12 *
13 * See COPYING for the status of this software.
14 **/
15
16 #include "fo-expr-eval.h"
17 #include "fo-expr-context-private.h"
18 #include "property/fo-property.h"
19 #include "property/fo-property-font-size.h"
20 #include "datatype/fo-all-datatype.h"
21 #include "fo-context.h"
22 #include "fo-xml-char-util.h"
23 #include "datatype/fo-wsc.h"
24 #include "property/fo-property-util.h"
25 #include <stdlib.h>
26 #include <string.h>
27
28 #define POINT2DEVICE(x) (x)
29 #define PICA2DEVICE(x) (x * 12)
30 #define INCH2DEVICE(x) (x * 72)
31 #define CM2DEVICE(x) (x * (72 / 2.54))
32 #define MM2DEVICE(x) (x * (72 / 25.4))
33
34 #define IS_NUMERIC(context) \
35 (g_ascii_isdigit (fo_expr_context_cur (context)) || \
36 fo_expr_context_cur (context) == '.')
37
38 #define RETURN_IF_ERROR \
39 if(fo_expr_context_peek_stack (context) != NULL && \
40 FO_IS_ERROR (fo_expr_context_peek_stack (context))) return
41
42 GQuark
fo_expr_eval_error_quark(void)43 fo_expr_eval_error_quark (void)
44 {
45 static GQuark quark = 0;
46 if (quark == 0)
47 quark = g_quark_from_static_string ("Expression evaluation error");
48 return quark;
49 }
50
51 const gchar *fo_expr_eval_error_messages [] = {
52 N_("Invalid expression."),
53 N_("Unfinished literal."),
54 N_("Expected start of literal."),
55 N_("Invalid expression."),
56 N_("Unregistered function."),
57 N_("Invalid number of arguments."),
58 N_("Invalid type for function argument."),
59 N_("Property is not inherited."),
60 N_("Invalid property name."),
61 N_("Function invalid for property."),
62 N_("Formatting object is not 'fo:table-cell' or a descendant of one."),
63 N_("Ancestor 'fo:table-cell' does not have an associated 'fo:table-column'."),
64 N_("Formatting object is not 'fo:list-block' or a descendant of one."),
65 N_("Formatting object is not 'fo:table-column'."),
66 N_("'proportional-column-width()' can only be used with 'column-width' property."),
67 N_("'proportional-column-width()' can only be used with the 'fixed' table layout method."),
68 N_("Invalid number of characters in color declaration."),
69 N_("Unrecognized operator name."),
70 N_("Function evaluation returned NULL result."),
71 N_("No object from which to inherit property."),
72 N_("Expression contains a percentage but no function to resolve percentages was provided."),
73 N_("Unfinished URL.")
74 };
75
76 GQuark
fo_expr_error_quark(void)77 fo_expr_error_quark (void)
78 {
79 static GQuark quark = 0;
80 if (quark == 0)
81 quark = g_quark_from_static_string ("Expression evaluation error");
82 return quark;
83 }
84
85 const gchar *fo_expr_error_messages [] = {
86 N_("Expression evaluation failed"),
87 N_("No result"),
88 N_("Evaluation resulted in an error"),
89 N_("Object left on stack: '%s'"),
90 N_("Expression not completely evaluated: '%s'"),
91 };
92
93 static void eval_additive_expr (FoExprContext *context);
94
95 static gchar*
parse_error_indication_string(FoExprContext * context)96 parse_error_indication_string (FoExprContext *context)
97 {
98 GString *string = g_string_new (NULL);
99 gint n;
100 const gchar *cur = fo_expr_context_cur_ptr (context);
101 const gchar *base = fo_expr_context_string (context);
102
103 while ((cur > base) && ((*cur == '\n') || (*cur == '\r')))
104 {
105 cur--;
106 }
107 n = 0;
108 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
109 cur--;
110 if ((*cur == '\n') || (*cur == '\r')) cur++;
111 base = cur;
112 n = 0;
113 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79))
114 {
115 g_string_append_c (string, *cur++);
116 n++;
117 }
118 g_string_append (string, "\n");
119 cur = fo_expr_context_cur_ptr (context);
120 while ((*cur == '\n') || (*cur == '\r'))
121 cur--;
122 n = 0;
123 while ((cur != base) && (n++ < 80))
124 {
125 g_string_append (string, " ");
126 base++;
127 }
128 g_string_append (string, "^");
129
130 return g_string_free (string, FALSE);
131 }
132
133 /**
134 * fo_expr_eval_new_error:
135 * @context: #FoExprContext for expression.
136 * @error_type: Type of error.
137 *
138 * Creates an #FoError based on @error_type and using information from
139 * @context in the error message string.
140 *
141 * Return value: An #FoError for an error of type @error_type.
142 **/
143 FoDatatype*
fo_expr_eval_new_error(FoExprContext * context,FoExprEvalError error_type)144 fo_expr_eval_new_error (FoExprContext *context,
145 FoExprEvalError error_type)
146 {
147 GError *error = NULL;
148 gchar *error_indication = NULL;
149 const gchar *error_string = NULL;
150
151 g_return_val_if_fail (context != NULL, NULL);
152
153 error_indication = parse_error_indication_string (context);
154
155 if (error_indication != NULL)
156 {
157 error_string =
158 g_strconcat (_(fo_expr_eval_error_messages[error_type]),
159 "\n",
160 error_indication,
161 NULL);
162 g_free (error_indication);
163 }
164 else
165 {
166 error_string =
167 _(fo_expr_eval_error_messages[error_type]);
168 }
169
170 error = g_error_new (FO_EXPR_EVAL_ERROR,
171 error_type,
172 "%s",
173 error_string);
174
175 return fo_error_new_with_value (error);
176 }
177
178 #define CHECK_ARITY(context, x) \
179 if (nargs != (x)) \
180 return fo_expr_eval_new_error((context), \
181 FO_EXPR_EVAL_ERROR_INVALID_ARITY)
182
183 FoDatatype*
fo_expr_eval_propagate_error(FoExprContext * context,GError * error)184 fo_expr_eval_propagate_error (FoExprContext *context,
185 GError *error)
186 {
187 GError *new_error = NULL;
188 FoExprEvalError error_type = FO_EXPR_EVAL_ERROR_FAILED;
189 gchar *error_indication = NULL;
190 gchar *error_string = NULL;
191
192 g_return_val_if_fail (context != NULL, NULL);
193 g_return_val_if_fail (error != NULL, NULL);
194
195 error_indication = parse_error_indication_string (context);
196
197 if (error_indication != NULL)
198 {
199 error_string =
200 g_strconcat (_(fo_expr_eval_error_messages[error_type]),
201 "\n",
202 g_quark_to_string (error->domain),
203 ": ",
204 error->message,
205 "\n",
206 error_indication,
207 NULL);
208 g_free (error_indication);
209 }
210 else
211 {
212 error_string =
213 g_strconcat (_(fo_expr_eval_error_messages[error_type]),
214 "\n",
215 g_quark_to_string (error->domain),
216 ": ",
217 error->message,
218 NULL);
219 }
220
221 g_error_free (error);
222
223 new_error = g_error_new (FO_EXPR_EVAL_ERROR,
224 error_type,
225 "%s",
226 error_string);
227
228 return fo_error_new_with_value (new_error);
229 }
230
231 /**
232 * is_ncnamechar:
233 * @context: the #FoExprContext
234 *
235 * Test whether the current character in @context is an NCNameChar
236 * according to the "Namespaces in XML" Recommendation
237 *
238 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
239 * CombiningChar | Extender
240 *
241 * Return value: %TRUE if the current character is an NCNameChar,
242 * otherwise %FALSE
243 **/
244 static gboolean
is_ncnamechar(FoExprContext * context)245 is_ncnamechar (FoExprContext *context)
246 {
247 return ((IS_LETTER(g_utf8_get_char (fo_expr_context_cur_ptr (context)))) ||
248 (IS_DIGIT(g_utf8_get_char (fo_expr_context_cur_ptr (context)))) ||
249 (fo_expr_context_cur (context) == '.') ||
250 (fo_expr_context_cur (context) == '-') ||
251 (fo_expr_context_cur (context) == '_') ||
252 (IS_COMBINING(g_utf8_get_char (fo_expr_context_cur_ptr (context)))) ||
253 (IS_EXTENDER(g_utf8_get_char (fo_expr_context_cur_ptr (context)))));
254 }
255
256 /**
257 * parse_ncname:
258 * @context: the #FoExprContext
259 *
260 * parse an XML namespace non-qualified name.
261 *
262 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
263 *
264 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
265 * CombiningChar | Extender
266 *
267 * Returns: the name or %NULL
268 **/
269 static gchar *
parse_ncname(FoExprContext * context)270 parse_ncname (FoExprContext *context)
271 {
272 const gchar *q;
273 gchar *ret = NULL;
274
275 if (!IS_LETTER(g_utf8_get_char (fo_expr_context_cur_ptr (context))) &&
276 (fo_expr_context_cur (context) != '_'))
277 {
278 return (NULL);
279 }
280
281 q = fo_expr_context_cur_ptr (context);
282 fo_expr_context_next (context);
283
284 while (is_ncnamechar (context))
285 {
286 fo_expr_context_next (context);
287 }
288
289 ret = g_strndup (q, fo_expr_context_cur_ptr (context) - q);
290
291 return ret;
292 }
293
294 /**
295 * eval_inherit:
296 * @context: the #FoExprContext
297 *
298 * Compile the inherited value.
299 **/
300 static void
eval_inherit(FoExprContext * context)301 eval_inherit (FoExprContext *context)
302 {
303 FoProperty *inherited_property = NULL;
304 FoDatatype *result_datatype = NULL;
305
306 g_object_get ((gpointer) fo_expr_context_get_fo_context (context),
307 fo_expr_context_get_property_name (context),
308 &inherited_property,
309 NULL);
310
311 if (inherited_property == NULL)
312 {
313 result_datatype =
314 fo_expr_eval_new_error (context,
315 FO_EXPR_EVAL_ERROR_NOT_INHERITED);
316 }
317 else
318 {
319 result_datatype =
320 g_object_ref (fo_property_get_value (inherited_property));
321 }
322
323 fo_expr_context_push_stack (context, result_datatype);
324 }
325
326 /**
327 * is_unit_name:
328 * @context:
329 *
330 * Tests whether the characters beginning at the current character in
331 * @context make a unit name.
332
333 * Return value: TRUE if the next characters are a unit name,
334 * otherwise FALSE
335 */
336 static gboolean
is_unit_name(FoExprContext * context)337 is_unit_name (FoExprContext *context)
338 {
339 if (((fo_expr_context_cur (context) == 'p') &&
340 ((fo_expr_context_peek (context, 1) == 't') ||
341 (fo_expr_context_peek (context, 1) == 'c') ||
342 (fo_expr_context_peek (context, 1) == 'x'))) ||
343 (((fo_expr_context_cur (context) == 'c') ||
344 (fo_expr_context_cur (context) == 'm')) &&
345 (fo_expr_context_peek (context, 1) == 'm')) ||
346 ((fo_expr_context_cur (context) == 'i') &&
347 (fo_expr_context_peek (context, 1) == 'n')) ||
348 ((fo_expr_context_cur (context) == 'e') &&
349 (fo_expr_context_peek (context, 1) == 'm')))
350 {
351 return TRUE;
352 }
353 else
354 {
355 return FALSE;
356 }
357 }
358
359 static FoDatatype *
parse_length(FoExprContext * context,gdouble number)360 parse_length (FoExprContext *context,
361 gdouble number)
362 {
363 FoDatatype *length = NULL;
364
365 if ((fo_expr_context_cur (context) == 'p') &&
366 (fo_expr_context_peek (context, 1) == 'x'))
367 {
368 length = fo_length_new_from_pixels ((gint) number);
369
370 goto finish;
371 }
372 else if ((fo_expr_context_cur (context) == 'p') &&
373 (fo_expr_context_peek (context, 1) == 't'))
374 {
375 number = POINT2DEVICE(number);
376 }
377 else if ((fo_expr_context_cur (context) == 'p') &&
378 (fo_expr_context_peek (context, 1) == 'c'))
379 {
380 number = PICA2DEVICE(number);
381 }
382 else if ((fo_expr_context_cur (context) == 'c') &&
383 (fo_expr_context_peek (context, 1) == 'm'))
384 {
385 number = CM2DEVICE(number);
386 }
387 else if ((fo_expr_context_cur (context) == 'm') &&
388 (fo_expr_context_peek (context, 1) == 'm'))
389 {
390 number = MM2DEVICE(number);
391 }
392 else if ((fo_expr_context_cur (context) == 'i') &&
393 (fo_expr_context_peek (context, 1) == 'n'))
394 {
395 number = INCH2DEVICE(number);
396 }
397 else if ((fo_expr_context_cur (context) == 'e') &&
398 (fo_expr_context_peek (context, 1) == 'm'))
399 {
400 number =
401 number *
402 fo_length_get_value ((FoDatatype *) fo_expr_context_get_font_size (context));
403 }
404 else
405 {
406 g_assert_not_reached();
407 }
408
409 length = fo_length_new_with_value (number);
410
411 finish:
412 fo_expr_context_skip (context, 2);
413
414 return length;
415 }
416
417 /**
418 * parse_percentage:
419 * @context: #FoExprContext providing #FoResolvePercentFunc, etc.
420 * @number: Numeric value of percentage to be evaluated.
421 *
422 * Does not move expression context pointer. Does not change the
423 * stack.
424 *
425 * Return value: #FoDatatype representing evaluated percentage or an #FoError.
426 */
427 static FoDatatype *
parse_percentage(FoExprContext * context,gdouble number)428 parse_percentage (FoExprContext *context,
429 gdouble number)
430 {
431 FoDatatype *result_datatype;
432 FoResolvePercentFunc resolve_percent_func;
433
434 if (fo_expr_context_cur (context) != '%')
435 {
436 g_assert_not_reached();
437 }
438
439 fo_expr_context_skip (context, 1);
440
441 resolve_percent_func =
442 fo_expr_context_get_resolve_percent_func (context);
443
444 if (resolve_percent_func != NULL)
445 {
446 GError *tmp_error = NULL;
447
448 result_datatype =
449 resolve_percent_func (number,
450 fo_expr_context_get_font_size (context),
451 fo_expr_context_get_current_fo (context),
452 fo_expr_context_get_fo_context (context),
453 &tmp_error);
454 if (tmp_error != NULL)
455 {
456 if (result_datatype != NULL)
457 {
458 g_object_unref (result_datatype);
459 }
460
461 result_datatype = fo_error_new_with_value (tmp_error);
462 }
463 }
464 else
465 {
466 result_datatype =
467 fo_expr_eval_new_error (context,
468 FO_EXPR_EVAL_ERROR_NO_RESOLVE_PERCENT_FUNC);
469 }
470
471 return result_datatype;
472 }
473
474 /**
475 * parse_number:
476 * @context: The #FoExprContext.
477 *
478 * Parse the current context as a number.
479 *
480 * Return value: The number.
481 */
482 static gdouble
parse_number(FoExprContext * context)483 parse_number (FoExprContext *context)
484 {
485 gdouble number = 0.0;
486 gdouble mult = 1;
487 gboolean ok = FALSE;
488
489 if (!IS_NUMERIC (context))
490 {
491 g_assert_not_reached ();
492 }
493
494 while ((fo_expr_context_cur (context) >= '0') &&
495 (fo_expr_context_cur (context) <= '9'))
496 {
497 number = number * 10 + (fo_expr_context_cur (context) - '0');
498 ok = TRUE;
499 fo_expr_context_next (context);
500 }
501 if (fo_expr_context_cur (context) == '.')
502 {
503 fo_expr_context_next (context);
504 if (((fo_expr_context_cur (context) < '0') ||
505 (fo_expr_context_cur (context) > '9')) && (!ok))
506 {
507 g_assert_not_reached ();
508 }
509 while ((fo_expr_context_cur (context) >= '0') &&
510 (fo_expr_context_cur (context) <= '9'))
511 {
512 mult /= 10;
513 number = number + (fo_expr_context_cur (context) - '0') * mult;
514 fo_expr_context_next (context);
515 }
516 }
517
518 return number;
519 }
520
521 /**
522 * eval_numeric:
523 * @context: the #FoExprContext
524 *
525 * [5] Numeric ::= AbsoluteNumeric
526 * | RelativeNumeric
527 *
528 * [6] AbsoluteNumeric ::= AbsoluteLength
529 *
530 * [7] AbsoluteLength ::= Number AbsoluteUnitName?
531 *
532 * [8] RelativeNumeric ::= Percent
533 * | RelativeLength
534 *
535 * [9] Percent ::= Number '%'
536 *
537 * [10] RelativeLength ::= Number RelativeUnitName
538 *
539 * Compile a Numeric, then push it on the stack
540 *
541 * Does not implement the productions in the order shown since that
542 * would be inefficient. For example, this code handles absolute and
543 * relative units together, rather than as separate operations.
544 **/
545 static void
eval_numeric(FoExprContext * context)546 eval_numeric (FoExprContext *context)
547 {
548 FoDatatype *result_datatype = NULL;
549 gdouble number;
550
551 RETURN_IF_ERROR;
552
553 number = parse_number (context);
554
555 fo_expr_context_skip_blanks (context);
556 if (fo_expr_context_cur (context) == '%')
557 {
558 result_datatype = parse_percentage (context, number);
559 }
560 else if (is_unit_name (context))
561 {
562 result_datatype = parse_length (context, number);
563 }
564 else {
565 result_datatype = fo_number_new_with_value (number);
566 }
567
568 fo_expr_context_push_stack (context, result_datatype);
569 }
570
571 /**
572 * eval_color:
573 * @context: the #FoExprContext
574 *
575 * [18] Color ::= '#' AlphaOrDigits
576 *
577 * [19] AlphaOrDigits ::= [a-fA-F0-9]+
578 *
579 * Compile a color, then push it on the stack
580 *
581 **/
582 static void
eval_color(FoExprContext * context)583 eval_color (FoExprContext *context)
584 {
585 FoDatatype *result_datatype = NULL;
586 long long int full_color = 0;
587 int ret_length = 0;
588 guint16 redval = 0;
589 guint16 greenval = 0;
590 guint16 blueval = 0;
591
592 RETURN_IF_ERROR;
593 if (fo_expr_context_cur (context) != '#')
594 {
595 result_datatype =
596 fo_expr_eval_new_error (context,
597 FO_EXPR_EVAL_ERROR_EXPR_ERROR);
598 }
599 else {
600 fo_expr_context_next (context);
601
602 while (((fo_expr_context_cur (context) >= '0') &&
603 (fo_expr_context_cur (context) <= '9')) ||
604 ((fo_expr_context_cur (context) >= 'a') &&
605 (fo_expr_context_cur (context) <= 'f')) ||
606 ((fo_expr_context_cur (context) >= 'A') &&
607 (fo_expr_context_cur (context) <= 'F')))
608 {
609 /* Shift the current value */
610 full_color <<= 4;
611 /* Count the number of characters */
612 ret_length++;
613 if ((fo_expr_context_cur (context) >= '0') &&
614 (fo_expr_context_cur (context) <= '9'))
615 {
616 full_color += fo_expr_context_cur (context) - '0';
617 }
618 else if ((fo_expr_context_cur (context) >= 'a') &&
619 (fo_expr_context_cur (context) <= 'f'))
620 {
621 full_color += fo_expr_context_cur (context) - ('a' - 10);
622 }
623 else
624 {
625 full_color += fo_expr_context_cur (context) - ('A' - 10);
626 }
627 fo_expr_context_next (context);
628 }
629
630 if ((ret_length == 3) ||
631 (ret_length == 6) ||
632 (ret_length == 12))
633 {
634 if (ret_length == 3)
635 {
636 redval = full_color >> 8;
637 greenval = (full_color >> 4) & 0xF;
638 blueval = full_color & 0xF;
639
640 /* Convert to 16-bit color by repeating the 4-bit value so
641 0x0 -> 0x0000 and 0xF -> 0xFFFF */
642 redval =
643 (redval << 12) | (redval << 8) | (redval << 4) | redval;
644 greenval =
645 (greenval << 12 ) | (greenval << 8) | (greenval << 4) | greenval;
646 blueval =
647 (blueval << 12) | (blueval << 8) | (blueval << 4) | blueval;
648 }
649 else if (ret_length == 6)
650 {
651 redval = full_color >> 16;
652 greenval = (full_color >> 8) & 0xFF;
653 blueval = full_color & 0xFF;
654
655 redval = (redval << 8) | redval;
656 greenval = (greenval << 8) | greenval;
657 blueval = (blueval << 8) | blueval;
658 }
659 else if (ret_length == 12)
660 {
661 redval = full_color >> 32;
662 greenval = (full_color >> 16) & 0xFFFF;
663 blueval = full_color & 0xFFFF;
664 }
665
666 result_datatype = fo_color_new_with_value (redval, greenval, blueval);
667 }
668 else
669 {
670 result_datatype =
671 fo_expr_eval_new_error (context,
672 FO_EXPR_EVAL_ERROR_COLOR_DECL);
673 }
674 }
675
676 fo_expr_context_push_stack (context, result_datatype);
677 }
678
679 /**
680 * eval_literal:
681 * @context: the #FoExprContext
682 *
683 * Parse a Literal and push it on the stack.
684 *
685 * [20] Literal ::= '"' [^"]* '"'
686 * | "'" [^']* "'"
687 *
688 **/
689 static void
eval_literal(FoExprContext * context)690 eval_literal (FoExprContext *context)
691 {
692 FoDatatype *result_datatype = NULL;
693 const gchar *q;
694 gchar *ret = NULL;
695
696 if (fo_expr_context_cur (context) == '"')
697 {
698 fo_expr_context_next (context);
699 q = fo_expr_context_cur_ptr (context);
700 while ((IS_CHAR (g_utf8_get_char (fo_expr_context_cur_ptr (context)))) &&
701 (fo_expr_context_cur (context) != '"'))
702 fo_expr_context_next (context);
703 if (!IS_CHAR (g_utf8_get_char (fo_expr_context_cur_ptr (context))))
704 {
705 result_datatype =
706 fo_expr_eval_new_error (context,
707 FO_EXPR_EVAL_ERROR_UNFINISHED_LITERAL);
708 }
709 else
710 {
711 ret = g_strndup (q, fo_expr_context_cur_ptr (context) - q);
712 fo_expr_context_next (context);
713 }
714 }
715 else if (fo_expr_context_cur (context) == '\'')
716 {
717 fo_expr_context_next (context);
718 q = fo_expr_context_cur_ptr (context);
719
720 while ((IS_CHAR (g_utf8_get_char (fo_expr_context_cur_ptr (context)))) &&
721 (fo_expr_context_cur (context) != '\''))
722 {
723 fo_expr_context_next (context);
724 }
725
726 if (!IS_CHAR (g_utf8_get_char (fo_expr_context_cur_ptr (context))))
727 {
728 result_datatype =
729 fo_expr_eval_new_error (context,
730 FO_EXPR_EVAL_ERROR_UNFINISHED_LITERAL);
731 }
732 else
733 {
734 ret = g_strndup (q, fo_expr_context_cur_ptr (context) - q);
735 fo_expr_context_next (context);
736 }
737 }
738 else
739 {
740 result_datatype =
741 fo_expr_eval_new_error (context,
742 FO_EXPR_EVAL_ERROR_START_LITERAL);
743 }
744
745 if (ret != NULL)
746 {
747 result_datatype = fo_string_new_with_value (ret);
748 g_free (ret);
749 }
750
751 fo_expr_context_push_stack (context, result_datatype);
752 }
753
754 /**
755 * eval_enum:
756 * @context: the #FoExprContext
757 * @name: the potential enumeration token name
758 *
759 * [26] EnumerationToken ::= NCName
760 *
761 * Compile an enumeration token and push the result on the stack.
762 *
763 * If @name does not correspond to an allowed enumeration token, make
764 * an #FoName and push that on the stack.
765 **/
766 static void
eval_enum(FoExprContext * context,gchar * name)767 eval_enum (FoExprContext *context,
768 gchar *name)
769 {
770 FoResolveEnumFunc resolve_enum_func = NULL;
771 FoDatatype *object = NULL;
772 FoDatatype *result_datatype = NULL;
773
774 resolve_enum_func = fo_expr_context_get_resolve_enum_func (context);
775 if (resolve_enum_func != NULL)
776 {
777 object =
778 resolve_enum_func (name,
779 (FoContext *) fo_expr_context_get_fo_context (context),
780 NULL);
781 }
782
783 if (object != NULL)
784 {
785 /* there was a match */
786 result_datatype = object;
787 }
788 else
789 {
790 /* No match returned by lookup, so make it into a name */
791 result_datatype = fo_name_new_with_value (name);
792 }
793
794 fo_expr_context_push_stack (context, result_datatype);
795 }
796
797 /**
798 * eval_function_call:
799 * @context: the #FoExprContext
800 *
801 * [3] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
802 * [4] Argument ::= Expr
803 *
804 * Compile a function call, the evaluation of all arguments are
805 * pushed on the stack
806 **/
807 static void
eval_function_call(FoExprContext * context,gchar * name)808 eval_function_call (FoExprContext *context,
809 gchar *name)
810 {
811 FoExprFunc expr_func;
812 FoDatatype *result_datatype = NULL;
813 int nargs = 0;
814
815 g_return_if_fail (context != NULL);
816 g_return_if_fail (name != '\0');
817
818 if (fo_expr_context_cur (context) != '(')
819 {
820 result_datatype =
821 fo_expr_eval_new_error (context,
822 FO_EXPR_EVAL_ERROR_EXPR_ERROR);
823 }
824 else
825 {
826 fo_expr_context_next (context);
827 fo_expr_context_skip_blanks (context);
828
829 while (fo_expr_context_cur (context) != ')')
830 {
831 eval_additive_expr (context);
832 nargs++;
833 if (fo_expr_context_cur (context) == ')') break;
834 if (fo_expr_context_cur (context) != ',')
835 {
836 result_datatype =
837 fo_expr_eval_new_error (context,
838 FO_EXPR_EVAL_ERROR_EXPR_ERROR);
839 goto error;
840 }
841 fo_expr_context_next (context);
842 fo_expr_context_skip_blanks (context);
843 }
844
845 expr_func = fo_expr_context_get_func (context, name);
846
847 if (expr_func != NULL)
848 {
849 result_datatype = expr_func (context, nargs);
850
851 if (result_datatype == NULL)
852 {
853 result_datatype =
854 fo_expr_eval_new_error (context,
855 FO_EXPR_EVAL_ERROR_FUNC_RETURN_NULL);
856 }
857 }
858 else
859 {
860 result_datatype =
861 fo_expr_eval_new_error (context,
862 FO_EXPR_EVAL_ERROR_UNKNOWN_FUNC);
863 }
864 fo_expr_context_next (context);
865 fo_expr_context_skip_blanks (context);
866 }
867
868 error:
869 fo_expr_context_push_stack (context, result_datatype);
870 }
871
872 /**
873 * fo_expr_eval_url:
874 * @context: The expression context
875 *
876 * Implement the uri-specification XSL datatype
877 *
878 * url(...)
879 *
880 * It's not really an XSL function, but it does look like one.
881 *
882 * Return value: #FoDatatype, which could be an #FoError
883 */
884 void
eval_url(FoExprContext * context)885 eval_url (FoExprContext *context)
886 {
887 FoDatatype *result_datatype = NULL;
888 const gchar *q;
889 gchar *ret = NULL;
890
891 g_return_if_fail (context != NULL);
892
893 fo_expr_context_skip_blanks (context);
894
895 /* Skip the leading '('. */
896 fo_expr_context_next (context);
897
898 if (fo_expr_context_cur (context) == '"')
899 {
900 fo_expr_context_next (context);
901 q = fo_expr_context_cur_ptr (context);
902
903 while ((IS_CHAR (g_utf8_get_char (fo_expr_context_cur_ptr (context)))) &&
904 (fo_expr_context_cur (context) != '"'))
905 {
906 fo_expr_context_next (context);
907 }
908
909 if (!IS_CHAR (g_utf8_get_char (fo_expr_context_cur_ptr (context))))
910 {
911 result_datatype =
912 fo_expr_eval_new_error (context,
913 FO_EXPR_EVAL_ERROR_UNFINISHED_URL);
914 }
915 else
916 {
917 ret = g_strndup (q, fo_expr_context_cur_ptr (context) - q);
918 fo_expr_context_next (context);
919 }
920 }
921 else if (fo_expr_context_cur (context) == '\'')
922 {
923 fo_expr_context_next (context);
924 q = fo_expr_context_cur_ptr (context);
925
926 while ((IS_CHAR (g_utf8_get_char (fo_expr_context_cur_ptr (context)))) &&
927 (fo_expr_context_cur (context) != '\''))
928 {
929 fo_expr_context_next (context);
930 }
931
932 if (!IS_CHAR (g_utf8_get_char (fo_expr_context_cur_ptr (context))))
933 {
934 result_datatype =
935 fo_expr_eval_new_error (context,
936 FO_EXPR_EVAL_ERROR_UNFINISHED_URL);
937 }
938 else
939 {
940 ret = g_strndup (q, fo_expr_context_cur_ptr (context) - q);
941 fo_expr_context_next (context);
942 }
943 }
944 else
945 {
946 q = fo_expr_context_cur_ptr (context);
947
948 while ((IS_CHAR (g_utf8_get_char (fo_expr_context_cur_ptr (context)))) &&
949 (fo_expr_context_cur (context) != ' ') &&
950 (fo_expr_context_cur (context) != ' ') &&
951 (fo_expr_context_cur (context) != ')'))
952 {
953 fo_expr_context_next (context);
954 }
955
956 if (!IS_CHAR (g_utf8_get_char (fo_expr_context_cur_ptr (context))))
957 {
958 result_datatype =
959 fo_expr_eval_new_error (context,
960 FO_EXPR_EVAL_ERROR_UNFINISHED_URL);
961 }
962 else
963 {
964 ret = g_strndup (q, fo_expr_context_cur_ptr (context) - q);
965 }
966 }
967
968 fo_expr_context_skip_blanks (context);
969
970 if (fo_expr_context_cur (context) == ')')
971 {
972 fo_expr_context_next (context);
973 }
974 else
975 {
976 result_datatype =
977 fo_expr_eval_new_error (context,
978 FO_EXPR_EVAL_ERROR_UNFINISHED_URL);
979 }
980
981 if (ret != NULL && result_datatype == NULL)
982 {
983 result_datatype = fo_uri_specification_new_with_value (ret);
984 g_free (ret);
985 }
986
987 fo_expr_context_push_stack (context, result_datatype);
988 }
989
990 /**
991 * eval_primary_expr:
992 * @context:
993 *
994 * Compile a Primary expression:
995 *
996 * [2] PrimaryExpr ::= '(' Expr ')'
997 * | Numeric
998 * | Literal
999 * | Color
1000 * | Keyword
1001 * | EnumerationToken
1002 * | FunctionCall
1003 **/
1004 static void
eval_primary_expr(FoExprContext * context)1005 eval_primary_expr (FoExprContext *context)
1006 {
1007 fo_expr_context_skip_blanks (context);
1008
1009 if (fo_expr_context_cur (context) == '(')
1010 {
1011 fo_expr_context_next (context);
1012 fo_expr_context_skip_blanks (context);
1013 eval_additive_expr (context);
1014 if (fo_expr_context_cur (context) != ')')
1015 {
1016 /* FIXME: Do error reporting and/or recovery */
1017 g_assert_not_reached ();
1018 }
1019 fo_expr_context_next (context);
1020 fo_expr_context_skip_blanks (context);
1021 }
1022 else if (fo_expr_context_cur (context) == '#')
1023 {
1024 eval_color (context);
1025 }
1026 else if (fo_expr_context_cur (context) == '+')
1027 {
1028 fo_expr_context_next (context);
1029 fo_expr_context_skip_blanks (context);
1030
1031 if (g_ascii_isdigit (fo_expr_context_cur (context)) ||
1032 fo_expr_context_cur (context) == '.')
1033 {
1034 eval_numeric (context);
1035 }
1036 else
1037 {
1038 FoDatatype *result_datatype =
1039 fo_expr_eval_new_error (context,
1040 FO_EXPR_EVAL_ERROR_FAILED);
1041 fo_expr_context_push_stack (context, result_datatype);
1042 }
1043 }
1044 else if (IS_NUMERIC (context))
1045 {
1046 eval_numeric (context);
1047 }
1048 else if ((fo_expr_context_cur (context) == '\'') ||
1049 (fo_expr_context_cur (context) == '"'))
1050 {
1051 eval_literal (context);
1052 }
1053 else
1054 {
1055 gchar *name = parse_ncname (context);
1056
1057 if (name != NULL)
1058 {
1059 if (strcmp (name, "inherit") == 0)
1060 {
1061 eval_inherit (context);
1062 }
1063 else
1064 {
1065 fo_expr_context_skip_blanks (context);
1066 if (fo_expr_context_cur (context) != '(')
1067 {
1068 eval_enum (context, name);
1069 }
1070 else
1071 {
1072 if (strcmp (name, "url") == 0)
1073 {
1074 eval_url (context);
1075 }
1076 else
1077 {
1078 eval_function_call (context, name);
1079 }
1080 }
1081 }
1082
1083 g_free (name);
1084 }
1085 else
1086 {
1087 /*
1088 * No name means expression doesn't match grammar since
1089 * exhausted all other options.
1090 */
1091 FoDatatype *result_datatype =
1092 fo_expr_eval_new_error (context,
1093 FO_EXPR_EVAL_ERROR_FAILED);
1094 fo_expr_context_push_stack (context, result_datatype);
1095 }
1096 }
1097 fo_expr_context_skip_blanks (context);
1098 }
1099
1100 /**
1101 * eval_unary_expr:
1102 * @context:
1103 *
1104 * Compile a Unary expression:
1105 *
1106 * [13] UnaryExpr ::= PrimaryExpr
1107 * | '-' UnaryExpr
1108 **/
1109 static void
eval_unary_expr(FoExprContext * context)1110 eval_unary_expr (FoExprContext *context)
1111 {
1112 gboolean negate = FALSE;
1113
1114 fo_expr_context_skip_blanks (context);
1115
1116 while (fo_expr_context_cur (context) == '-')
1117 {
1118 negate = !negate;
1119 fo_expr_context_next (context);
1120 fo_expr_context_skip_blanks (context);
1121 }
1122
1123 eval_primary_expr (context);
1124 RETURN_IF_ERROR;
1125
1126 if (negate)
1127 {
1128 FoDatatype *arg = fo_expr_context_pop_stack (context);
1129 FoDatatype *result_datatype = NULL;
1130
1131 result_datatype = fo_datatype_negate (arg);
1132 fo_expr_context_push_stack (context, result_datatype);
1133 }
1134 }
1135
1136 /**
1137 * eval_multiplicative_expr:
1138 * @context:
1139 *
1140 * Compile a Multiplicative expression:
1141 *
1142 * [12] MultiplicativeExpr ::= UnaryExpr
1143 * | MultiplicativeExpr MultiplyOperator UnaryExpr
1144 * | MultiplicativeExpr 'div' UnaryExpr
1145 * | MultiplicativeExpr 'mod' UnaryExpr
1146 * [23] MultiplyOperator ::= '*'
1147 *
1148 * Errors:
1149 *
1150 * Section 5.9.11, Lexical Structure:
1151 *
1152 * If a NCName follows a numeric, it should be recognised as an
1153 * OperatorName or it is an error.
1154 *
1155 * Handle this by checking that 'div' and 'mod' aren't followed by an
1156 * NCNameChar.
1157 **/
1158 static void
eval_multiplicative_expr(FoExprContext * context)1159 eval_multiplicative_expr (FoExprContext *context)
1160 {
1161 eval_unary_expr (context);
1162 RETURN_IF_ERROR;
1163 fo_expr_context_skip_blanks (context);
1164
1165 while ((fo_expr_context_cur (context) == '*') ||
1166 ((fo_expr_context_cur (context) == 'd')
1167 && (fo_expr_context_peek (context, 1) == 'i') &&
1168 (fo_expr_context_peek (context, 2) == 'v')) ||
1169 ((fo_expr_context_cur (context) == 'm') &&
1170 (fo_expr_context_peek (context, 1) == 'o') &&
1171 (fo_expr_context_peek (context, 2) == 'd')))
1172 {
1173 FoExprFunc expr_func = NULL;
1174 FoDatatype *result_datatype = NULL;
1175
1176 if (fo_expr_context_cur (context) == '*')
1177 {
1178 expr_func = fo_expr_context_get_func (context, "*");
1179 fo_expr_context_next (context);
1180 }
1181 else if (fo_expr_context_cur (context) == 'd')
1182 {
1183 fo_expr_context_skip (context, 3);
1184
1185 if (!is_ncnamechar (context))
1186 {
1187 expr_func = fo_expr_context_get_func (context, "div");
1188 }
1189 }
1190 else if (fo_expr_context_cur (context) == 'm')
1191 {
1192 fo_expr_context_skip (context, 3);
1193
1194 if (!is_ncnamechar (context))
1195 {
1196 expr_func = fo_expr_context_get_func (context, "mod");
1197 }
1198 }
1199
1200 if (expr_func != NULL)
1201 {
1202 fo_expr_context_skip_blanks (context);
1203
1204 eval_unary_expr (context);
1205 RETURN_IF_ERROR;
1206
1207 result_datatype = expr_func (context, 2);
1208 }
1209 else
1210 {
1211 result_datatype =
1212 fo_expr_eval_new_error (context,
1213 FO_EXPR_EVAL_ERROR_UNKNOWN_OPERATOR);
1214
1215 }
1216
1217 fo_expr_context_push_stack (context, result_datatype);
1218 RETURN_IF_ERROR;
1219 fo_expr_context_skip_blanks (context);
1220 }
1221 }
1222
1223 /**
1224 * eval_additive_expr:
1225 * @context:
1226 *
1227 * Compile an additive expression.
1228 *
1229 * [11] AdditiveExpr ::= MultiplicativeExpr
1230 * | AdditiveExpr '+' MultiplicativeExpr
1231 * | AdditiveExpr '-' MultiplicativeExpr
1232 */
1233 static void
eval_additive_expr(FoExprContext * context)1234 eval_additive_expr (FoExprContext *context)
1235 {
1236 eval_multiplicative_expr (context);
1237 RETURN_IF_ERROR;
1238 fo_expr_context_skip_blanks (context);
1239
1240 while ((fo_expr_context_cur (context) == '+') ||
1241 (fo_expr_context_cur (context) == '-'))
1242 {
1243 FoExprFunc expr_func = NULL;
1244 FoDatatype *result_datatype = NULL;
1245
1246 if (fo_expr_context_cur (context) == '+')
1247 {
1248 expr_func = fo_expr_context_get_func (context, "+");
1249 }
1250 else if (fo_expr_context_cur (context) == '-')
1251 {
1252 expr_func = fo_expr_context_get_func (context, "-");
1253 }
1254
1255 if (expr_func != NULL)
1256 {
1257 fo_expr_context_next (context);
1258 fo_expr_context_skip_blanks (context);
1259
1260 eval_multiplicative_expr (context);
1261 RETURN_IF_ERROR;
1262
1263 result_datatype = expr_func (context, 2);
1264 }
1265 else
1266 {
1267 result_datatype =
1268 fo_expr_eval_new_error (context,
1269 FO_EXPR_EVAL_ERROR_UNKNOWN_OPERATOR);
1270 }
1271
1272 fo_expr_context_push_stack (context, result_datatype);
1273 RETURN_IF_ERROR;
1274 fo_expr_context_skip_blanks (context);
1275 }
1276 }
1277
1278 /**
1279 * fo_expr_eval:
1280 * @string: String form of expression.
1281 * @property_name: Name of property being evaluated.
1282 * @resolve_enum_func: Function used to resolve enumeration tokens.
1283 * @resolve_percent_func: Function used to resolve percentage values.
1284 * @font_size_prop: Current font size property.
1285 * @current_fo: Current formatting object.
1286 * @fo_context: #FoContext for inherited values.
1287 * @env_list: List of #FoExprEnv for evaluating expressions.
1288 * @error: #GError for reporting errors.
1289 *
1290 * Evaluates @string as an XSL expression.
1291 *
1292 * Return value: The result of evaluating @string, or NULL if an error
1293 * occurred
1294 **/
1295 FoDatatype*
fo_expr_eval(const gchar * string,const gchar * property_name,FoResolveEnumFunc resolve_enum_func,FoResolvePercentFunc resolve_percent_func,const FoProperty * font_size_prop,const FoFo * current_fo,const FoContext * fo_context,const GSList * env_list,GError ** error)1296 fo_expr_eval (const gchar *string,
1297 const gchar *property_name,
1298 FoResolveEnumFunc resolve_enum_func,
1299 FoResolvePercentFunc resolve_percent_func,
1300 const FoProperty *font_size_prop,
1301 const FoFo *current_fo,
1302 const FoContext *fo_context,
1303 const GSList *env_list,
1304 GError **error)
1305 {
1306 FoExprContext *context;
1307 FoDatatype *result_datatype = NULL;
1308
1309 g_return_val_if_fail (string != NULL &&
1310 IS_CHAR (g_utf8_get_char (string)),
1311 NULL);
1312 g_return_val_if_fail (property_name != NULL &&
1313 IS_CHAR (g_utf8_get_char (property_name)),
1314 NULL);
1315 g_return_val_if_fail ((font_size_prop) == NULL ||
1316 FO_IS_PROPERTY_FONT_SIZE (font_size_prop),
1317 NULL);
1318 g_return_val_if_fail (FO_IS_FO (current_fo), NULL);
1319 g_return_val_if_fail (FO_IS_CONTEXT (fo_context), NULL);
1320 g_return_val_if_fail (env_list != NULL, NULL);
1321 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1322 /* Can't make assertion about resolve_enum_func since it can be NULL */
1323 /* Can't make assertion about resolve_percent_func since it can be NULL */
1324
1325 context = fo_expr_context_new (string,
1326 property_name,
1327 resolve_enum_func,
1328 resolve_percent_func,
1329 font_size_prop,
1330 current_fo,
1331 fo_context,
1332 env_list);
1333
1334 eval_additive_expr (context);
1335
1336 result_datatype = fo_expr_context_pop_stack (context);
1337
1338 /* Check for possible error conditions */
1339 if (result_datatype == NULL)
1340 {
1341 g_set_error (error,
1342 FO_EXPR_ERROR,
1343 FO_EXPR_ERROR_NULL_RESULT,
1344 "%s",
1345 _(fo_expr_error_messages[FO_EXPR_ERROR_NULL_RESULT]));
1346 }
1347 else if (FO_IS_ERROR (result_datatype))
1348 {
1349 g_set_error (error,
1350 FO_EXPR_ERROR,
1351 FO_EXPR_ERROR_ERROR_RESULT,
1352 "%s",
1353 _(fo_expr_error_messages[FO_EXPR_ERROR_ERROR_RESULT]));
1354 }
1355 else if (!fo_expr_context_stack_is_empty (context))
1356 {
1357 gchar *string = fo_object_sprintf (fo_expr_context_peek_stack (context));
1358
1359 g_set_error (error,
1360 FO_EXPR_ERROR,
1361 FO_EXPR_ERROR_EXTRA_STACK,
1362 _(fo_expr_error_messages[FO_EXPR_ERROR_EXTRA_STACK]),
1363 string);
1364
1365 g_free (string);
1366 }
1367 else if (fo_expr_context_cur (context) != '\0')
1368 {
1369 g_set_error (error,
1370 FO_EXPR_ERROR,
1371 FO_EXPR_ERROR_EXTRA_EXPR,
1372 "%s\n%p",
1373 _(fo_expr_error_messages[FO_EXPR_ERROR_EXTRA_EXPR]),
1374 fo_expr_context_cur_ptr (context));
1375 }
1376
1377 fo_expr_context_free (context);
1378 context = NULL;
1379
1380 return result_datatype;
1381 }
1382
1383 /**
1384 * convert_to_tblr:
1385 * @context: #FoExprContext
1386 *
1387 * Convert the stack of #FoDatatype in @context to a #FoTblr.
1388 **/
1389 static void
convert_to_tblr(FoExprContext * context)1390 convert_to_tblr (FoExprContext *context)
1391 {
1392 /*
1393 * There could be as few as one or as many as four items on the
1394 * stack. The items will be on the stack in the order in which
1395 * they were evaluated. For example, when the spec says "If
1396 * there are two values, the top and bottom paddings are set to
1397 * the first value and the right and left paddings are set to
1398 * the second.", the first value popped off the stack applies to
1399 * the left and right paddings, and the second, to the top and
1400 * bottom paddings.
1401 *
1402 * From the spec for 'border-padding':
1403 *
1404 * If there is only one value, it applies to all sides. If
1405 * there are two values, the top and bottom paddings are set
1406 * to the first value and the right and left paddings are set
1407 * to the second. If there are three values, the top is set
1408 * to the first value, the left and right are set to the
1409 * second, and the bottom is set to the third. If there are
1410 * four values, they apply to the top, right, bottom, and
1411 * left, respectively.
1412 *
1413 * Popping entries off the stack until there's a null gives:
1414 *
1415 * First: All sides Left, right Bottom Left
1416 * Second: - Top, bottom Left, right Bottom
1417 * Third: - - Top Right
1418 * Fourth: - - - Top
1419 *
1420 * Going from stack entries to components gives:
1421 *
1422 * One entry Two entries Three entries Four entries
1423 * Top: Top Second Third Fourth
1424 * Bottom: Top Second First Second
1425 * Left: Top First Second First
1426 * Right: Top First Second Third
1427 *
1428 * A fifth entry on the stack indicates an error.
1429 */
1430
1431 FoDatatype *result_datatype = NULL;
1432
1433 /* First stack entry */
1434 FoDatatype *stack_first = fo_expr_context_pop_stack (context);
1435
1436 /* Handle possible conditions for top stack entry. */
1437 if (stack_first == NULL)
1438 {
1439 result_datatype =
1440 fo_expr_eval_new_error (context,
1441 FO_EXPR_ERROR_NULL_RESULT);
1442 goto push;
1443 }
1444 else if (FO_IS_ERROR (stack_first))
1445 {
1446 result_datatype =
1447 fo_expr_eval_new_error (context,
1448 FO_EXPR_ERROR_ERROR_RESULT);
1449 goto push;
1450 }
1451
1452 /* Second stack entry */
1453 FoDatatype *stack_second = fo_expr_context_pop_stack (context);
1454
1455 /* Handle possible conditions for second stack entry. */
1456 if (stack_second == NULL)
1457 {
1458 result_datatype = fo_tblr_new_from_values (stack_first,
1459 stack_first,
1460 stack_first,
1461 stack_first);
1462 goto push;
1463 }
1464 else if (FO_IS_ERROR (stack_second))
1465 {
1466 result_datatype =
1467 fo_expr_eval_new_error (context,
1468 FO_EXPR_ERROR_ERROR_RESULT);
1469 goto push;
1470 }
1471
1472 /* third stack entry */
1473 FoDatatype *stack_third = fo_expr_context_pop_stack (context);
1474
1475 /* Handle possible conditions for third stack entry. */
1476 if (stack_third == NULL)
1477 {
1478 result_datatype = fo_tblr_new_from_values (stack_second,
1479 stack_second,
1480 stack_first,
1481 stack_first);
1482 goto push;
1483 }
1484 else if (FO_IS_ERROR (stack_third))
1485 {
1486 result_datatype =
1487 fo_expr_eval_new_error (context,
1488 FO_EXPR_ERROR_ERROR_RESULT);
1489 goto push;
1490 }
1491
1492 /* fourth stack entry */
1493 FoDatatype *stack_fourth = fo_expr_context_pop_stack (context);
1494
1495 /* Handle possible conditions for fourth stack entry. */
1496 if (stack_fourth == NULL)
1497 {
1498 result_datatype = fo_tblr_new_from_values (stack_third,
1499 stack_first,
1500 stack_second,
1501 stack_second);
1502 goto push;
1503 }
1504 else if (FO_IS_ERROR (stack_fourth))
1505 {
1506 result_datatype =
1507 fo_expr_eval_new_error (context,
1508 FO_EXPR_ERROR_ERROR_RESULT);
1509 goto push;
1510 }
1511 else
1512 {
1513 result_datatype = fo_tblr_new_from_values (stack_fourth,
1514 stack_second,
1515 stack_first,
1516 stack_third);
1517 goto push;
1518 }
1519
1520 push:
1521 fo_expr_context_push_stack (context, result_datatype);
1522 }
1523
1524 /**
1525 * eval_padding_expr:
1526 *
1527 * <padding-width>{1,4} | inherit
1528 **/
1529 static void
eval_padding_expr(FoExprContext * context)1530 eval_padding_expr (FoExprContext *context)
1531 {
1532 fo_expr_context_skip_blanks (context);
1533
1534 gchar *name = parse_ncname (context);
1535
1536 if (name == NULL)
1537 {
1538 /* If not a name, then one to four <padding-width> values. */
1539
1540 do
1541 {
1542 FoDatatype *intermediate_value = NULL;
1543 gboolean negate = FALSE;
1544
1545 fo_expr_context_skip_blanks (context);
1546
1547 while (fo_expr_context_cur (context) == '-')
1548 {
1549 negate = !negate;
1550 fo_expr_context_next (context);
1551 fo_expr_context_skip_blanks (context);
1552 }
1553
1554 if (IS_NUMERIC (context))
1555 {
1556 gdouble number = parse_number (context);
1557
1558 fo_expr_context_skip_blanks (context);
1559
1560 if (fo_expr_context_cur (context) == '%')
1561 {
1562 intermediate_value = parse_percentage (context, number);
1563 }
1564 else if (is_unit_name (context))
1565 {
1566 intermediate_value = parse_length (context, number);
1567 }
1568 else
1569 {
1570 intermediate_value =
1571 fo_expr_eval_new_error (context,
1572 FO_EXPR_EVAL_ERROR_FAILED);
1573 }
1574 }
1575 else
1576 {
1577 intermediate_value =
1578 fo_expr_eval_new_error (context,
1579 FO_EXPR_EVAL_ERROR_FAILED);
1580 }
1581
1582 RETURN_IF_ERROR;
1583
1584 fo_expr_context_push_stack (context, intermediate_value);
1585
1586 if (negate)
1587 {
1588 FoDatatype *arg = fo_expr_context_pop_stack (context);
1589
1590 intermediate_value = fo_datatype_negate (arg);
1591 fo_expr_context_push_stack (context, intermediate_value);
1592 }
1593
1594 RETURN_IF_ERROR;
1595 }
1596 while (g_ascii_isspace (fo_expr_context_cur (context)));
1597
1598 convert_to_tblr(context);
1599 }
1600 else
1601 {
1602 FoDatatype *result_datatype = NULL;
1603
1604 /* If the expression is a name, there's only one valid name: 'inherit'. */
1605 if (strcmp (name, "inherit") == 0)
1606 {
1607 fo_expr_context_skip_blanks (context);
1608
1609 if (fo_expr_context_cur (context) == '\0')
1610 {
1611 eval_inherit (context);
1612 g_free (name);
1613 }
1614 else
1615 {
1616 result_datatype =
1617 fo_expr_eval_new_error (context,
1618 FO_EXPR_EVAL_ERROR_FAILED);
1619 }
1620 }
1621 else
1622 {
1623 result_datatype =
1624 fo_expr_eval_new_error (context,
1625 FO_EXPR_EVAL_ERROR_FAILED);
1626 }
1627
1628 fo_expr_context_push_stack (context, result_datatype);
1629 }
1630 }
1631
1632 /**
1633 * fo_expr_padding_eval:
1634 * @string: String form of expression.
1635 * @property_name: Name of property being evaluated.
1636 * @resolve_enum_func: Function used to resolve enumeration tokens.
1637 * @resolve_percent_func: Function used to resolve percentage values.
1638 * @font_size_prop: Current font size property.
1639 * @current_fo: Current formatting object.
1640 * @fo_context: #FoContext for inherited values.
1641 * @env_list: List of #FoExprEnv for evaluating expressions.
1642 * @error: #GError for reporting errors.
1643 *
1644 * Evaluates @string as an XSL expression.
1645 *
1646 * Return value: The result of evaluating @string, or NULL if an error
1647 * occurred
1648 **/
1649 FoDatatype*
fo_expr_padding_eval(const gchar * string,const gchar * property_name,FoResolveEnumFunc resolve_enum_func,FoResolvePercentFunc resolve_percent_func,const FoProperty * font_size_prop,const FoFo * current_fo,const FoContext * fo_context,const GSList * env_list,GError ** error)1650 fo_expr_padding_eval (const gchar *string,
1651 const gchar *property_name,
1652 FoResolveEnumFunc resolve_enum_func,
1653 FoResolvePercentFunc resolve_percent_func,
1654 const FoProperty *font_size_prop,
1655 const FoFo *current_fo,
1656 const FoContext *fo_context,
1657 const GSList *env_list,
1658 GError **error)
1659 {
1660 FoExprContext *context;
1661 FoDatatype *result_datatype = NULL;
1662
1663 g_return_val_if_fail (IS_CHAR (g_utf8_get_char (string)), NULL);
1664 g_return_val_if_fail (IS_CHAR (g_utf8_get_char (property_name)), NULL);
1665 g_return_val_if_fail ((font_size_prop) == NULL ||
1666 FO_IS_PROPERTY_FONT_SIZE (font_size_prop),
1667 NULL);
1668 g_return_val_if_fail (FO_IS_FO (current_fo), NULL);
1669 g_return_val_if_fail (FO_IS_CONTEXT (fo_context), NULL);
1670 g_return_val_if_fail (env_list != NULL, NULL);
1671 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1672 /* Can't make assertion about resolve_enum_func since it can be NULL */
1673 /* Can't make assertion about resolve_percent_func since it can be NULL */
1674
1675 context = fo_expr_context_new (string,
1676 property_name,
1677 resolve_enum_func,
1678 resolve_percent_func,
1679 font_size_prop,
1680 current_fo,
1681 fo_context,
1682 env_list);
1683
1684 eval_padding_expr (context);
1685
1686 result_datatype = fo_expr_context_pop_stack (context);
1687
1688 /* Check for possible error conditions */
1689 if (result_datatype == NULL)
1690 {
1691 g_set_error (error,
1692 FO_EXPR_ERROR,
1693 FO_EXPR_ERROR_NULL_RESULT,
1694 "%s",
1695 _(fo_expr_error_messages[FO_EXPR_ERROR_NULL_RESULT]));
1696 }
1697 else if (FO_IS_ERROR (result_datatype))
1698 {
1699 g_set_error (error,
1700 FO_EXPR_ERROR,
1701 FO_EXPR_ERROR_ERROR_RESULT,
1702 "%s",
1703 _(fo_expr_error_messages[FO_EXPR_ERROR_ERROR_RESULT]));
1704 }
1705 else if (!fo_expr_context_stack_is_empty (context))
1706 {
1707 gchar *string = fo_object_sprintf (fo_expr_context_peek_stack (context));
1708
1709 g_set_error (error,
1710 FO_EXPR_ERROR,
1711 FO_EXPR_ERROR_EXTRA_STACK,
1712 "%s\n\s",
1713 _(fo_expr_error_messages[FO_EXPR_ERROR_EXTRA_STACK]),
1714 string);
1715
1716 g_free (string);
1717 }
1718 else if (fo_expr_context_cur (context) != '\0')
1719 {
1720 g_set_error (error,
1721 FO_EXPR_ERROR,
1722 FO_EXPR_ERROR_EXTRA_EXPR,
1723 _(fo_expr_error_messages[FO_EXPR_ERROR_EXTRA_EXPR]),
1724 fo_expr_context_cur_ptr (context));
1725 }
1726
1727 fo_expr_context_free (context);
1728 context = NULL;
1729
1730 return result_datatype;
1731 }
1732
1733 /**
1734 * eval_border_color_expr:
1735 *
1736 * [<color>|transparent]{1,4} | inherit
1737 **/
1738 static void
eval_border_color_expr(FoExprContext * context)1739 eval_border_color_expr (FoExprContext *context)
1740 {
1741 /* If not 'inherit', then one to four <color> or 'transparent' values. */
1742 do
1743 {
1744 FoDatatype *intermediate_value = NULL;
1745
1746 fo_expr_context_skip_blanks (context);
1747
1748 if (fo_expr_context_cur (context) == '#')
1749 {
1750 eval_color (context);
1751 }
1752 else if ((fo_expr_context_cur (context) == '\'') ||
1753 (fo_expr_context_cur (context) == '"'))
1754 {
1755 eval_literal (context);
1756 }
1757 else
1758 {
1759 gchar *name = parse_ncname (context);
1760
1761 if (name != NULL)
1762 {
1763 eval_enum (context, name);
1764
1765 g_free (name);
1766 }
1767 else
1768 {
1769 /*
1770 * No name means expression doesn't match grammar since
1771 * exhausted all other options.
1772 */
1773 intermediate_value =
1774 fo_expr_eval_new_error (context,
1775 FO_EXPR_EVAL_ERROR_FAILED);
1776 fo_expr_context_push_stack (context, intermediate_value);
1777 }
1778
1779 RETURN_IF_ERROR;
1780
1781 }
1782 }
1783 while (g_ascii_isspace (fo_expr_context_cur (context)));
1784
1785 convert_to_tblr(context);
1786 }
1787
1788 /**
1789 * fo_expr_border_color_eval:
1790 * @string: String form of expression.
1791 * @property_name: Name of property being evaluated.
1792 * @resolve_enum_func: Function used to resolve enumeration tokens.
1793 * @resolve_percent_func: Function used to resolve percentage values.
1794 * @font_size_prop: Current font size property.
1795 * @current_fo: Current formatting object.
1796 * @fo_context: #FoContext for inherited values.
1797 * @env_list: List of #FoExprEnv for evaluating expressions.
1798 * @error: #GError for reporting errors.
1799 *
1800 * Evaluates @string as an XSL expression.
1801 *
1802 * Return value: The result of evaluating @string, or NULL if an error
1803 * occurred
1804 **/
1805 FoDatatype *
fo_expr_border_color_eval(const gchar * string,const gchar * property_name,FoResolveEnumFunc resolve_enum_func,FoResolvePercentFunc resolve_percent_func,const FoProperty * font_size_prop,const FoFo * current_fo,const FoContext * fo_context,const GSList * env_list,GError ** error)1806 fo_expr_border_color_eval (const gchar *string,
1807 const gchar *property_name,
1808 FoResolveEnumFunc resolve_enum_func,
1809 FoResolvePercentFunc resolve_percent_func,
1810 const FoProperty *font_size_prop,
1811 const FoFo *current_fo,
1812 const FoContext *fo_context,
1813 const GSList *env_list,
1814 GError **error)
1815 {
1816 FoExprContext *context;
1817 FoDatatype *result_datatype = NULL;
1818
1819 g_return_val_if_fail (IS_CHAR (g_utf8_get_char (string)), NULL);
1820 g_return_val_if_fail (IS_CHAR (g_utf8_get_char (property_name)), NULL);
1821 g_return_val_if_fail ((font_size_prop) == NULL ||
1822 FO_IS_PROPERTY_FONT_SIZE (font_size_prop),
1823 NULL);
1824 g_return_val_if_fail (FO_IS_FO (current_fo), NULL);
1825 g_return_val_if_fail (FO_IS_CONTEXT (fo_context), NULL);
1826 g_return_val_if_fail (env_list != NULL, NULL);
1827 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1828 /* Can't make assertion about resolve_enum_func since it can be NULL */
1829 /* Can't make assertion about resolve_percent_func since it can be NULL */
1830
1831 context = fo_expr_context_new (string,
1832 property_name,
1833 resolve_enum_func,
1834 resolve_percent_func,
1835 font_size_prop,
1836 current_fo,
1837 fo_context,
1838 env_list);
1839
1840 eval_border_color_expr (context);
1841
1842 result_datatype = fo_expr_context_pop_stack (context);
1843
1844 /* Check for possible error conditions */
1845 if (result_datatype == NULL)
1846 {
1847 g_set_error (error,
1848 FO_EXPR_ERROR,
1849 FO_EXPR_ERROR_NULL_RESULT,
1850 "%s",
1851 _(fo_expr_error_messages[FO_EXPR_ERROR_NULL_RESULT]));
1852 }
1853 else if (FO_IS_ERROR (result_datatype))
1854 {
1855 g_set_error (error,
1856 FO_EXPR_ERROR,
1857 FO_EXPR_ERROR_ERROR_RESULT,
1858 "%s",
1859 _(fo_expr_error_messages[FO_EXPR_ERROR_ERROR_RESULT]));
1860 }
1861 else if (!fo_expr_context_stack_is_empty (context))
1862 {
1863 gchar *string = fo_object_sprintf (fo_expr_context_peek_stack (context));
1864
1865 g_set_error (error,
1866 FO_EXPR_ERROR,
1867 FO_EXPR_ERROR_EXTRA_STACK,
1868 _(fo_expr_error_messages[FO_EXPR_ERROR_EXTRA_STACK]),
1869 string);
1870
1871 g_free (string);
1872 }
1873 else if (fo_expr_context_cur (context) != '\0')
1874 {
1875 g_set_error (error,
1876 FO_EXPR_ERROR,
1877 FO_EXPR_ERROR_EXTRA_EXPR,
1878 _(fo_expr_error_messages[FO_EXPR_ERROR_EXTRA_EXPR]),
1879 fo_expr_context_cur_ptr (context));
1880 }
1881
1882 fo_expr_context_free (context);
1883 context = NULL;
1884
1885 return result_datatype;
1886 }
1887
1888 /**
1889 * eval_border_style_expr:
1890 *
1891 * <border-style>{1,4} | inherit
1892 **/
1893 static void
eval_border_style_expr(FoExprContext * context)1894 eval_border_style_expr (FoExprContext *context)
1895 {
1896 /* If not 'inherit', then one to four <color> or 'transparent' values. */
1897 do
1898 {
1899 FoDatatype *intermediate_value = NULL;
1900
1901 fo_expr_context_skip_blanks (context);
1902
1903 gchar *name = parse_ncname (context);
1904
1905 if (name != NULL)
1906 {
1907 eval_enum (context, name);
1908
1909 g_free (name);
1910 }
1911 else
1912 {
1913 /*
1914 * No name means expression doesn't match grammar since
1915 * exhausted all other options.
1916 */
1917 intermediate_value =
1918 fo_expr_eval_new_error (context,
1919 FO_EXPR_EVAL_ERROR_FAILED);
1920 fo_expr_context_push_stack (context, intermediate_value);
1921 }
1922
1923 RETURN_IF_ERROR;
1924 }
1925 while (g_ascii_isspace (fo_expr_context_cur (context)));
1926
1927 convert_to_tblr(context);
1928 }
1929
1930 /**
1931 * fo_expr_border_style_eval:
1932 * @string: String form of expression.
1933 * @property_name: Name of property being evaluated.
1934 * @resolve_enum_func: Function used to resolve enumeration tokens.
1935 * @resolve_percent_func: Function used to resolve percentage values.
1936 * @font_size_prop: Current font size property.
1937 * @current_fo: Current formatting object.
1938 * @fo_context: #FoContext for inherited values.
1939 * @env_list: List of #FoExprEnv for evaluating expressions.
1940 * @error: #GError for reporting errors.
1941 *
1942 * Evaluates @string as an XSL expression.
1943 *
1944 * Return value: The result of evaluating @string, or NULL if an error
1945 * occurred
1946 **/
1947 FoDatatype *
fo_expr_border_style_eval(const gchar * string,const gchar * property_name,FoResolveEnumFunc resolve_enum_func,FoResolvePercentFunc resolve_percent_func,const FoProperty * font_size_prop,const FoFo * current_fo,const FoContext * fo_context,const GSList * env_list,GError ** error)1948 fo_expr_border_style_eval (const gchar *string,
1949 const gchar *property_name,
1950 FoResolveEnumFunc resolve_enum_func,
1951 FoResolvePercentFunc resolve_percent_func,
1952 const FoProperty *font_size_prop,
1953 const FoFo *current_fo,
1954 const FoContext *fo_context,
1955 const GSList *env_list,
1956 GError **error)
1957 {
1958 FoExprContext *context;
1959 FoDatatype *result_datatype = NULL;
1960
1961 g_return_val_if_fail (IS_CHAR (g_utf8_get_char (string)), NULL);
1962 g_return_val_if_fail (IS_CHAR (g_utf8_get_char (property_name)), NULL);
1963 g_return_val_if_fail ((font_size_prop) == NULL ||
1964 FO_IS_PROPERTY_FONT_SIZE (font_size_prop),
1965 NULL);
1966 g_return_val_if_fail (FO_IS_FO (current_fo), NULL);
1967 g_return_val_if_fail (FO_IS_CONTEXT (fo_context), NULL);
1968 g_return_val_if_fail (env_list != NULL, NULL);
1969 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1970 /* Can't make assertion about resolve_enum_func since it can be NULL */
1971 /* Can't make assertion about resolve_percent_func since it can be NULL */
1972
1973 context = fo_expr_context_new (string,
1974 property_name,
1975 resolve_enum_func,
1976 resolve_percent_func,
1977 font_size_prop,
1978 current_fo,
1979 fo_context,
1980 env_list);
1981
1982 eval_border_style_expr (context);
1983
1984 result_datatype = fo_expr_context_pop_stack (context);
1985
1986 /* Check for possible error conditions */
1987 if (result_datatype == NULL)
1988 {
1989 g_set_error (error,
1990 FO_EXPR_ERROR,
1991 FO_EXPR_ERROR_NULL_RESULT,
1992 "%s",
1993 _(fo_expr_error_messages[FO_EXPR_ERROR_NULL_RESULT]));
1994 }
1995 else if (FO_IS_ERROR (result_datatype))
1996 {
1997 g_set_error (error,
1998 FO_EXPR_ERROR,
1999 FO_EXPR_ERROR_ERROR_RESULT,
2000 "%s",
2001 _(fo_expr_error_messages[FO_EXPR_ERROR_ERROR_RESULT]));
2002 }
2003 else if (!fo_expr_context_stack_is_empty (context))
2004 {
2005 gchar *string = fo_object_sprintf (fo_expr_context_peek_stack (context));
2006
2007 g_set_error (error,
2008 FO_EXPR_ERROR,
2009 FO_EXPR_ERROR_EXTRA_STACK,
2010 _(fo_expr_error_messages[FO_EXPR_ERROR_EXTRA_STACK]),
2011 string);
2012
2013 g_free (string);
2014 }
2015 else if (fo_expr_context_cur (context) != '\0')
2016 {
2017 g_set_error (error,
2018 FO_EXPR_ERROR,
2019 FO_EXPR_ERROR_EXTRA_EXPR,
2020 _(fo_expr_error_messages[FO_EXPR_ERROR_EXTRA_EXPR]),
2021 fo_expr_context_cur_ptr (context));
2022 }
2023
2024 fo_expr_context_free (context);
2025 context = NULL;
2026
2027 return result_datatype;
2028 }
2029
2030 /**
2031 * convert_to_wsc:
2032 * @context: #FoExprContext
2033 *
2034 * Convert the stack of #FoDatatype in @context to a #FoWsc.
2035 **/
2036 static void
convert_to_wsc(FoExprContext * context)2037 convert_to_wsc (FoExprContext *context)
2038 {
2039 /*
2040 * There could be as few as one or as many as three items on the
2041 * stack. The items will be on the stack in the order in which
2042 * they were evaluated.
2043 *
2044 * A fourth entry on the stack indicates an error.
2045 */
2046
2047 /* Result to return on stack. */
2048 FoDatatype *result_datatype = NULL;
2049 /* FoWsc as popped off stack. */
2050 FoDatatype *wsc_datatype = NULL;
2051 /*
2052 * Components of FoWsc (that will be returned if no errors
2053 * occur).
2054 */
2055 FoDatatype *width_datatype = NULL;
2056 FoDatatype *style_datatype = NULL;
2057 FoDatatype *color_datatype = NULL;
2058
2059 /* First stack entry */
2060 FoDatatype *stack_first = fo_expr_context_pop_stack (context);
2061
2062 /* Handle possible conditions for top stack entry. */
2063 if (stack_first == NULL)
2064 {
2065 result_datatype =
2066 fo_expr_eval_new_error (context,
2067 FO_EXPR_ERROR_NULL_RESULT);
2068 goto push;
2069 }
2070 else if (FO_IS_ERROR (stack_first))
2071 {
2072 result_datatype =
2073 fo_expr_eval_new_error (context,
2074 FO_EXPR_ERROR_ERROR_RESULT);
2075 goto push;
2076 }
2077
2078 if (FO_IS_WSC (stack_first))
2079 {
2080 wsc_datatype = stack_first;
2081
2082 if (fo_wsc_get_width (wsc_datatype) != NULL)
2083 {
2084 width_datatype = fo_wsc_get_width (wsc_datatype);
2085 }
2086 else if (fo_wsc_get_style (wsc_datatype) != NULL)
2087 {
2088 style_datatype = fo_wsc_get_style (wsc_datatype);
2089 }
2090 else if (fo_wsc_get_color (wsc_datatype) != NULL)
2091 {
2092 color_datatype = fo_wsc_get_color (wsc_datatype);
2093 }
2094 }
2095
2096 /* Second stack entry */
2097 FoDatatype *stack_second = fo_expr_context_pop_stack (context);
2098
2099 /* Handle possible conditions for second stack entry. */
2100 if (stack_second == NULL)
2101 {
2102 goto push_wsc;
2103 }
2104 else if (FO_IS_WSC (stack_second))
2105 {
2106 wsc_datatype = stack_second;
2107
2108 if ((width_datatype == NULL) &&
2109 (fo_wsc_get_width (wsc_datatype) != NULL))
2110 {
2111 width_datatype = fo_wsc_get_width (wsc_datatype);
2112 }
2113 else if ((style_datatype == NULL) &&
2114 (fo_wsc_get_style (wsc_datatype) != NULL))
2115 {
2116 style_datatype = fo_wsc_get_style (wsc_datatype);
2117 }
2118 else if ((color_datatype == NULL) &&
2119 (fo_wsc_get_color (wsc_datatype) != NULL))
2120 {
2121 color_datatype = fo_wsc_get_color (wsc_datatype);
2122 }
2123 else
2124 {
2125 result_datatype =
2126 fo_expr_eval_new_error (context,
2127 FO_EXPR_EVAL_ERROR_EXPR_ERROR);
2128 goto push;
2129 }
2130 }
2131 else if (FO_IS_ERROR (stack_second))
2132 {
2133 result_datatype =
2134 fo_expr_eval_new_error (context,
2135 FO_EXPR_ERROR_ERROR_RESULT);
2136 goto push;
2137 }
2138
2139 /* Third stack entry */
2140 FoDatatype *stack_third = fo_expr_context_pop_stack (context);
2141
2142 /* Handle possible conditions for third stack entry. */
2143 if (stack_third == NULL)
2144 {
2145 goto push_wsc;
2146 }
2147 else if (FO_IS_WSC (stack_third))
2148 {
2149 wsc_datatype = stack_third;
2150
2151 if ((width_datatype == NULL) &&
2152 (fo_wsc_get_width (wsc_datatype) != NULL))
2153 {
2154 width_datatype = fo_wsc_get_width (wsc_datatype);
2155 }
2156 else if ((style_datatype == NULL) &&
2157 (fo_wsc_get_style (wsc_datatype) != NULL))
2158 {
2159 style_datatype = fo_wsc_get_style (wsc_datatype);
2160 }
2161 else if ((color_datatype == NULL) &&
2162 (fo_wsc_get_color (wsc_datatype) != NULL))
2163 {
2164 color_datatype = fo_wsc_get_color (wsc_datatype);
2165 }
2166 else
2167 {
2168 result_datatype =
2169 fo_expr_eval_new_error (context,
2170 FO_EXPR_EVAL_ERROR_EXPR_ERROR);
2171 goto push;
2172 }
2173 }
2174 else if (FO_IS_ERROR (stack_third))
2175 {
2176 result_datatype =
2177 fo_expr_eval_new_error (context,
2178 FO_EXPR_ERROR_ERROR_RESULT);
2179 goto push;
2180 }
2181
2182 push_wsc:
2183
2184 if (width_datatype == NULL)
2185 {
2186 width_datatype = g_object_ref (fo_property_util_get_width_initial ());
2187 }
2188
2189 if (style_datatype == NULL)
2190 {
2191 style_datatype = g_object_ref (fo_property_util_get_style_initial ());
2192 }
2193
2194 if (color_datatype == NULL)
2195 {
2196 color_datatype = g_object_ref (fo_property_util_get_color_initial ());
2197 }
2198
2199 result_datatype = g_object_ref (fo_wsc_new_from_values (width_datatype,
2200 style_datatype,
2201 color_datatype));
2202
2203 push:
2204 fo_expr_context_push_stack (context, result_datatype);
2205 }
2206
2207 /**
2208 * eval_width_style_color_expr:
2209 *
2210 * [ <border-width> || <border-style> || <color> ] | inherit
2211 **/
2212 static void
eval_width_style_color_expr(FoExprContext * context)2213 eval_width_style_color_expr (FoExprContext *context)
2214 {
2215 /* If not 'inherit', then one to four <color> or 'transparent' values. */
2216 do
2217 {
2218 FoDatatype *intermediate_value = NULL;
2219
2220 fo_expr_context_skip_blanks (context);
2221
2222 if (fo_expr_context_cur (context) == '#')
2223 {
2224 eval_color (context);
2225 }
2226 else if ((fo_expr_context_cur (context) == '\'') ||
2227 (fo_expr_context_cur (context) == '"'))
2228 {
2229 eval_literal (context);
2230 }
2231 else
2232 {
2233 gchar *name = parse_ncname (context);
2234
2235 if (name != NULL)
2236 {
2237 eval_enum (context, name);
2238
2239 g_free (name);
2240 }
2241 else
2242 {
2243 /*
2244 * No name means expression doesn't match grammar since
2245 * exhausted all other options.
2246 */
2247 intermediate_value =
2248 fo_expr_eval_new_error (context,
2249 FO_EXPR_EVAL_ERROR_FAILED);
2250 fo_expr_context_push_stack (context, intermediate_value);
2251 }
2252
2253 RETURN_IF_ERROR;
2254 }
2255 }
2256 while (g_ascii_isspace (fo_expr_context_cur (context)));
2257
2258 convert_to_wsc(context);
2259 }
2260
2261 /**
2262 * fo_expr_wsc_eval:
2263 * @string: String form of expression.
2264 * @property_name: Name of property being evaluated.
2265 * @resolve_enum_func: Function used to resolve enumeration tokens.
2266 * @resolve_percent_func: Function used to resolve percentage values.
2267 * @font_size_prop: Current font size property.
2268 * @current_fo: Current formatting object.
2269 * @fo_context: #FoContext for inherited values.
2270 * @env_list: List of #FoExprEnv for evaluating expressions.
2271 * @error: #GError for reporting errors.
2272 *
2273 * Evaluates @string as an XSL expression.
2274 *
2275 * Return value: The result of evaluating @string, or NULL if an error
2276 * occurred
2277 **/
2278 FoDatatype *
fo_expr_wsc_eval(const gchar * string,const gchar * property_name,FoResolveEnumFunc resolve_enum_func,FoResolvePercentFunc resolve_percent_func,const FoProperty * font_size_prop,const FoFo * current_fo,const FoContext * fo_context,const GSList * env_list,GError ** error)2279 fo_expr_wsc_eval (const gchar *string,
2280 const gchar *property_name,
2281 FoResolveEnumFunc resolve_enum_func,
2282 FoResolvePercentFunc resolve_percent_func,
2283 const FoProperty *font_size_prop,
2284 const FoFo *current_fo,
2285 const FoContext *fo_context,
2286 const GSList *env_list,
2287 GError **error)
2288 {
2289 FoExprContext *context;
2290 FoDatatype *result_datatype = NULL;
2291
2292 g_return_val_if_fail (IS_CHAR (g_utf8_get_char (string)), NULL);
2293 g_return_val_if_fail (IS_CHAR (g_utf8_get_char (property_name)), NULL);
2294 g_return_val_if_fail ((font_size_prop) == NULL ||
2295 FO_IS_PROPERTY_FONT_SIZE (font_size_prop),
2296 NULL);
2297 g_return_val_if_fail (FO_IS_FO (current_fo), NULL);
2298 g_return_val_if_fail (FO_IS_CONTEXT (fo_context), NULL);
2299 g_return_val_if_fail (env_list != NULL, NULL);
2300 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2301 /* Can't make assertion about resolve_enum_func since it can be NULL */
2302 /* Can't make assertion about resolve_percent_func since it can be NULL */
2303
2304 context = fo_expr_context_new (string,
2305 property_name,
2306 resolve_enum_func,
2307 resolve_percent_func,
2308 font_size_prop,
2309 current_fo,
2310 fo_context,
2311 env_list);
2312
2313 eval_width_style_color_expr (context);
2314 result_datatype = fo_expr_context_pop_stack (context);
2315
2316 /* Check for possible error conditions */
2317 if (result_datatype == NULL)
2318 {
2319 g_set_error (error,
2320 FO_EXPR_ERROR,
2321 FO_EXPR_ERROR_NULL_RESULT,
2322 "%s",
2323 _(fo_expr_error_messages[FO_EXPR_ERROR_NULL_RESULT]));
2324 }
2325 else if (FO_IS_ERROR (result_datatype))
2326 {
2327 g_set_error (error,
2328 FO_EXPR_ERROR,
2329 FO_EXPR_ERROR_ERROR_RESULT,
2330 "%s",
2331 _(fo_expr_error_messages[FO_EXPR_ERROR_ERROR_RESULT]));
2332 }
2333 else if (!fo_expr_context_stack_is_empty (context))
2334 {
2335 gchar *string = fo_object_sprintf (fo_expr_context_peek_stack (context));
2336
2337 g_set_error (error,
2338 FO_EXPR_ERROR,
2339 FO_EXPR_ERROR_EXTRA_STACK,
2340 _(fo_expr_error_messages[FO_EXPR_ERROR_EXTRA_STACK]),
2341 string);
2342
2343 g_free (string);
2344 }
2345 else if (fo_expr_context_cur (context) != '\0')
2346 {
2347 g_set_error (error,
2348 FO_EXPR_ERROR,
2349 FO_EXPR_ERROR_EXTRA_EXPR,
2350 _(fo_expr_error_messages[FO_EXPR_ERROR_EXTRA_EXPR]),
2351 fo_expr_context_cur_ptr (context));
2352 }
2353
2354 fo_expr_context_free (context);
2355 context = NULL;
2356
2357 return result_datatype;
2358 }
2359