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