• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * numbers.c: Implementation of the XSLT number functions
3  *
4  * Reference:
5  *   http://www.w3.org/TR/1999/REC-xslt-19991116
6  *
7  * See Copyright for the status of this software.
8  *
9  * daniel@veillard.com
10  * Bjorn Reese <breese@users.sourceforge.net>
11  */
12 
13 #define IN_LIBXSLT
14 #include "libxslt.h"
15 
16 #include <math.h>
17 #include <limits.h>
18 #include <float.h>
19 #include <string.h>
20 
21 #include <libxml/xmlmemory.h>
22 #include <libxml/parserInternals.h>
23 #include <libxml/xpath.h>
24 #include <libxml/xpathInternals.h>
25 #include <libxml/encoding.h>
26 #include "xsltutils.h"
27 #include "pattern.h"
28 #include "templates.h"
29 #include "transform.h"
30 #include "numbersInternals.h"
31 
32 #ifndef FALSE
33 # define FALSE (0 == 1)
34 # define TRUE (1 == 1)
35 #endif
36 
37 #define SYMBOL_QUOTE		((xmlChar)'\'')
38 
39 #define DEFAULT_TOKEN		(xmlChar)'0'
40 #define DEFAULT_SEPARATOR	"."
41 
42 #define MAX_TOKENS		1024
43 
44 typedef struct _xsltFormatToken xsltFormatToken;
45 typedef xsltFormatToken *xsltFormatTokenPtr;
46 struct _xsltFormatToken {
47     xmlChar	*separator;
48     xmlChar	 token;
49     int		 width;
50 };
51 
52 typedef struct _xsltFormat xsltFormat;
53 typedef xsltFormat *xsltFormatPtr;
54 struct _xsltFormat {
55     xmlChar		*start;
56     xsltFormatToken	 tokens[MAX_TOKENS];
57     int			 nTokens;
58     xmlChar		*end;
59 };
60 
61 static char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
62 static char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz";
63 static xsltFormatToken default_token;
64 
65 /*
66  * **** Start temp insert ****
67  *
68  * The following two routines (xsltUTF8Size and xsltUTF8Charcmp)
69  * will be replaced with calls to the corresponding libxml routines
70  * at a later date (when other inter-library dependencies require it)
71  */
72 
73 /**
74  * xsltUTF8Size:
75  * @utf: pointer to the UTF8 character
76  *
77  * returns the numbers of bytes in the character, -1 on format error
78  */
79 static int
xsltUTF8Size(xmlChar * utf)80 xsltUTF8Size(xmlChar *utf) {
81     xmlChar mask;
82     int len;
83 
84     if (utf == NULL)
85         return -1;
86     if (*utf < 0x80)
87         return 1;
88     /* check valid UTF8 character */
89     if (!(*utf & 0x40))
90         return -1;
91     /* determine number of bytes in char */
92     len = 2;
93     for (mask=0x20; mask != 0; mask>>=1) {
94         if (!(*utf & mask))
95             return len;
96         len++;
97     }
98     return -1;
99 }
100 
101 /**
102  * xsltUTF8Charcmp
103  * @utf1: pointer to first UTF8 char
104  * @utf2: pointer to second UTF8 char
105  *
106  * returns result of comparing the two UCS4 values
107  * as with xmlStrncmp
108  */
109 static int
xsltUTF8Charcmp(xmlChar * utf1,xmlChar * utf2)110 xsltUTF8Charcmp(xmlChar *utf1, xmlChar *utf2) {
111 
112     if (utf1 == NULL ) {
113         if (utf2 == NULL)
114             return 0;
115         return -1;
116     }
117     return xmlStrncmp(utf1, utf2, xsltUTF8Size(utf1));
118 }
119 
120 /***** Stop temp insert *****/
121 /************************************************************************
122  *									*
123  *			Utility functions				*
124  *									*
125  ************************************************************************/
126 
127 #define IS_SPECIAL(self,letter)			\
128     ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0)	    ||	\
129      (xsltUTF8Charcmp((letter), (self)->digit) == 0)	    ||	\
130      (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0)  ||	\
131      (xsltUTF8Charcmp((letter), (self)->grouping) == 0)	    ||	\
132      (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0))
133 
134 #define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
135 #define IS_DIGIT_ONE(x) xsltIsDigitZero((xmlChar)(x)-1)
136 
137 static int
xsltIsDigitZero(unsigned int ch)138 xsltIsDigitZero(unsigned int ch)
139 {
140     /*
141      * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
142      */
143     switch (ch) {
144     case 0x0030: case 0x0660: case 0x06F0: case 0x0966:
145     case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66:
146     case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50:
147     case 0x0E60: case 0x0F20: case 0x1040: case 0x17E0:
148     case 0x1810: case 0xFF10:
149 	return TRUE;
150     default:
151 	return FALSE;
152     }
153 }
154 
155 static void
xsltNumberFormatDecimal(xmlBufferPtr buffer,double number,int digit_zero,int width,int digitsPerGroup,int groupingCharacter,int groupingCharacterLen)156 xsltNumberFormatDecimal(xmlBufferPtr buffer,
157 			double number,
158 			int digit_zero,
159 			int width,
160 			int digitsPerGroup,
161 			int groupingCharacter,
162 			int groupingCharacterLen)
163 {
164     /*
165      * This used to be
166      *  xmlChar temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 4];
167      * which would be length 68 on x86 arch.  It was changed to be a longer,
168      * fixed length in order to try to cater for (reasonable) UTF8
169      * separators and numeric characters.  The max UTF8 char size will be
170      * 6 or less, so the value used [500] should be *much* larger than needed
171      */
172     xmlChar temp_string[500];
173     xmlChar *pointer;
174     xmlChar temp_char[6];
175     int i;
176     int val;
177     int len;
178 
179     /* Build buffer from back */
180     pointer = &temp_string[sizeof(temp_string)] - 1;	/* last char */
181     *pointer = 0;
182     i = 0;
183     while (pointer > temp_string) {
184 	if ((i >= width) && (fabs(number) < 1.0))
185 	    break; /* for */
186 	if ((i > 0) && (groupingCharacter != 0) &&
187 	    (digitsPerGroup > 0) &&
188 	    ((i % digitsPerGroup) == 0)) {
189 	    if (pointer - groupingCharacterLen < temp_string) {
190 	        i = -1;		/* flag error */
191 		break;
192 	    }
193 	    pointer -= groupingCharacterLen;
194 	    xmlCopyCharMultiByte(pointer, groupingCharacter);
195 	}
196 
197 	val = digit_zero + (int)fmod(number, 10.0);
198 	if (val < 0x80) {			/* shortcut if ASCII */
199 	    if (pointer <= temp_string) {	/* Check enough room */
200 	        i = -1;
201 		break;
202 	    }
203 	    *(--pointer) = val;
204 	}
205 	else {
206 	/*
207 	 * Here we have a multibyte character.  It's a little messy,
208 	 * because until we generate the char we don't know how long
209 	 * it is.  So, we generate it into the buffer temp_char, then
210 	 * copy from there into temp_string.
211 	 */
212 	    len = xmlCopyCharMultiByte(temp_char, val);
213 	    if ( (pointer - len) < temp_string ) {
214 	        i = -1;
215 		break;
216 	    }
217 	    pointer -= len;
218 	    memcpy(pointer, temp_char, len);
219 	}
220 	number /= 10.0;
221 	++i;
222     }
223     if (i < 0)
224         xsltGenericError(xsltGenericErrorContext,
225 		"xsltNumberFormatDecimal: Internal buffer size exceeded");
226     xmlBufferCat(buffer, pointer);
227 }
228 
229 static void
xsltNumberFormatAlpha(xmlBufferPtr buffer,double number,int is_upper)230 xsltNumberFormatAlpha(xmlBufferPtr buffer,
231 		      double number,
232 		      int is_upper)
233 {
234     char temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1];
235     char *pointer;
236     int i;
237     char *alpha_list;
238     double alpha_size = (double)(sizeof(alpha_upper_list) - 1);
239 
240     /* Build buffer from back */
241     pointer = &temp_string[sizeof(temp_string)];
242     *(--pointer) = 0;
243     alpha_list = (is_upper) ? alpha_upper_list : alpha_lower_list;
244 
245     for (i = 1; i < (int)sizeof(temp_string); i++) {
246 	number--;
247 	*(--pointer) = alpha_list[((int)fmod(number, alpha_size))];
248 	number /= alpha_size;
249 	if (fabs(number) < 1.0)
250 	    break; /* for */
251     }
252     xmlBufferCCat(buffer, pointer);
253 }
254 
255 static void
xsltNumberFormatRoman(xmlBufferPtr buffer,double number,int is_upper)256 xsltNumberFormatRoman(xmlBufferPtr buffer,
257 		      double number,
258 		      int is_upper)
259 {
260     /*
261      * Based on an example by Jim Walsh
262      */
263     while (number >= 1000.0) {
264 	xmlBufferCCat(buffer, (is_upper) ? "M" : "m");
265 	number -= 1000.0;
266     }
267     if (number >= 900.0) {
268 	xmlBufferCCat(buffer, (is_upper) ? "CM" : "cm");
269 	number -= 900.0;
270     }
271     while (number >= 500.0) {
272 	xmlBufferCCat(buffer, (is_upper) ? "D" : "d");
273 	number -= 500.0;
274     }
275     if (number >= 400.0) {
276 	xmlBufferCCat(buffer, (is_upper) ? "CD" : "cd");
277 	number -= 400.0;
278     }
279     while (number >= 100.0) {
280 	xmlBufferCCat(buffer, (is_upper) ? "C" : "c");
281 	number -= 100.0;
282     }
283     if (number >= 90.0) {
284 	xmlBufferCCat(buffer, (is_upper) ? "XC" : "xc");
285 	number -= 90.0;
286     }
287     while (number >= 50.0) {
288 	xmlBufferCCat(buffer, (is_upper) ? "L" : "l");
289 	number -= 50.0;
290     }
291     if (number >= 40.0) {
292 	xmlBufferCCat(buffer, (is_upper) ? "XL" : "xl");
293 	number -= 40.0;
294     }
295     while (number >= 10.0) {
296 	xmlBufferCCat(buffer, (is_upper) ? "X" : "x");
297 	number -= 10.0;
298     }
299     if (number >= 9.0) {
300 	xmlBufferCCat(buffer, (is_upper) ? "IX" : "ix");
301 	number -= 9.0;
302     }
303     while (number >= 5.0) {
304 	xmlBufferCCat(buffer, (is_upper) ? "V" : "v");
305 	number -= 5.0;
306     }
307     if (number >= 4.0) {
308 	xmlBufferCCat(buffer, (is_upper) ? "IV" : "iv");
309 	number -= 4.0;
310     }
311     while (number >= 1.0) {
312 	xmlBufferCCat(buffer, (is_upper) ? "I" : "i");
313 	number--;
314     }
315 }
316 
317 static void
xsltNumberFormatTokenize(const xmlChar * format,xsltFormatPtr tokens)318 xsltNumberFormatTokenize(const xmlChar *format,
319 			 xsltFormatPtr tokens)
320 {
321     int ix = 0;
322     int j;
323     int val;
324     int len;
325 
326     default_token.token = DEFAULT_TOKEN;
327     default_token.width = 1;
328     default_token.separator = BAD_CAST(DEFAULT_SEPARATOR);
329 
330 
331     tokens->start = NULL;
332     tokens->tokens[0].separator = NULL;
333     tokens->end = NULL;
334 
335     /*
336      * Insert initial non-alphanumeric token.
337      * There is always such a token in the list, even if NULL
338      */
339     while (! (IS_LETTER(val=xmlStringCurrentChar(NULL, format+ix, &len)) ||
340     	      IS_DIGIT(val)) ) {
341 	if (format[ix] == 0)		/* if end of format string */
342 	    break; /* while */
343 	ix += len;
344     }
345     if (ix > 0)
346 	tokens->start = xmlStrndup(format, ix);
347 
348 
349     for (tokens->nTokens = 0; tokens->nTokens < MAX_TOKENS;
350 	 tokens->nTokens++) {
351 	if (format[ix] == 0)
352 	    break; /* for */
353 
354 	/*
355 	 * separator has already been parsed (except for the first
356 	 * number) in tokens->end, recover it.
357 	 */
358 	if (tokens->nTokens > 0) {
359 	    tokens->tokens[tokens->nTokens].separator = tokens->end;
360 	    tokens->end = NULL;
361 	}
362 
363 	val = xmlStringCurrentChar(NULL, format+ix, &len);
364 	if (IS_DIGIT_ONE(val) ||
365 		 IS_DIGIT_ZERO(val)) {
366 	    tokens->tokens[tokens->nTokens].width = 1;
367 	    while (IS_DIGIT_ZERO(val)) {
368 		tokens->tokens[tokens->nTokens].width++;
369 		ix += len;
370 		val = xmlStringCurrentChar(NULL, format+ix, &len);
371 	    }
372 	    if (IS_DIGIT_ONE(val)) {
373 		tokens->tokens[tokens->nTokens].token = val - 1;
374 		ix += len;
375 		val = xmlStringCurrentChar(NULL, format+ix, &len);
376 	    }
377 	} else if ( (val == (xmlChar)'A') ||
378 		    (val == (xmlChar)'a') ||
379 		    (val == (xmlChar)'I') ||
380 		    (val == (xmlChar)'i') ) {
381 	    tokens->tokens[tokens->nTokens].token = val;
382 	    ix += len;
383 	    val = xmlStringCurrentChar(NULL, format+ix, &len);
384 	} else {
385 	    /* XSLT section 7.7
386 	     * "Any other format token indicates a numbering sequence
387 	     *  that starts with that token. If an implementation does
388 	     *  not support a numbering sequence that starts with that
389 	     *  token, it must use a format token of 1."
390 	     */
391 	    tokens->tokens[tokens->nTokens].token = (xmlChar)'0';
392 	    tokens->tokens[tokens->nTokens].width = 1;
393 	}
394 	/*
395 	 * Skip over remaining alphanumeric characters from the Nd
396 	 * (Number, decimal digit), Nl (Number, letter), No (Number,
397 	 * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt
398 	 * (Letters, titlecase), Lm (Letters, modifiers), and Lo
399 	 * (Letters, other (uncased)) Unicode categories. This happens
400 	 * to correspond to the Letter and Digit classes from XML (and
401 	 * one wonders why XSLT doesn't refer to these instead).
402 	 */
403 	while (IS_LETTER(val) || IS_DIGIT(val)) {
404 	    ix += len;
405 	    val = xmlStringCurrentChar(NULL, format+ix, &len);
406 	}
407 
408 	/*
409 	 * Insert temporary non-alphanumeric final tooken.
410 	 */
411 	j = ix;
412 	while (! (IS_LETTER(val) || IS_DIGIT(val))) {
413 	    if (val == 0)
414 		break; /* while */
415 	    ix += len;
416 	    val = xmlStringCurrentChar(NULL, format+ix, &len);
417 	}
418 	if (ix > j)
419 	    tokens->end = xmlStrndup(&format[j], ix - j);
420     }
421 }
422 
423 static void
xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,double * numbers,int numbers_max,xsltFormatPtr tokens,xmlBufferPtr buffer)424 xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,
425 			      double *numbers,
426 			      int numbers_max,
427 			      xsltFormatPtr tokens,
428 			      xmlBufferPtr buffer)
429 {
430     int i = 0;
431     double number;
432     xsltFormatTokenPtr token;
433 
434     /*
435      * Handle initial non-alphanumeric token
436      */
437     if (tokens->start != NULL)
438 	 xmlBufferCat(buffer, tokens->start);
439 
440     for (i = 0; i < numbers_max; i++) {
441 	/* Insert number */
442 	number = numbers[(numbers_max - 1) - i];
443 	if (i < tokens->nTokens) {
444 	  /*
445 	   * The "n"th format token will be used to format the "n"th
446 	   * number in the list
447 	   */
448 	  token = &(tokens->tokens[i]);
449 	} else if (tokens->nTokens > 0) {
450 	  /*
451 	   * If there are more numbers than format tokens, then the
452 	   * last format token will be used to format the remaining
453 	   * numbers.
454 	   */
455 	  token = &(tokens->tokens[tokens->nTokens - 1]);
456 	} else {
457 	  /*
458 	   * If there are no format tokens, then a format token of
459 	   * 1 is used to format all numbers.
460 	   */
461 	  token = &default_token;
462 	}
463 
464 	/* Print separator, except for the first number */
465 	if (i > 0) {
466 	    if (token->separator != NULL)
467 		xmlBufferCat(buffer, token->separator);
468 	    else
469 		xmlBufferCCat(buffer, DEFAULT_SEPARATOR);
470 	}
471 
472 	switch (xmlXPathIsInf(number)) {
473 	case -1:
474 	    xmlBufferCCat(buffer, "-Infinity");
475 	    break;
476 	case 1:
477 	    xmlBufferCCat(buffer, "Infinity");
478 	    break;
479 	default:
480 	    if (xmlXPathIsNaN(number)) {
481 		xmlBufferCCat(buffer, "NaN");
482 	    } else {
483 
484 		switch (token->token) {
485 		case 'A':
486 		    xsltNumberFormatAlpha(buffer,
487 					  number,
488 					  TRUE);
489 
490 		    break;
491 		case 'a':
492 		    xsltNumberFormatAlpha(buffer,
493 					  number,
494 					  FALSE);
495 
496 		    break;
497 		case 'I':
498 		    xsltNumberFormatRoman(buffer,
499 					  number,
500 					  TRUE);
501 
502 		    break;
503 		case 'i':
504 		    xsltNumberFormatRoman(buffer,
505 					  number,
506 					  FALSE);
507 
508 		    break;
509 		default:
510 		    if (IS_DIGIT_ZERO(token->token)) {
511 			xsltNumberFormatDecimal(buffer,
512 						number,
513 						token->token,
514 						token->width,
515 						data->digitsPerGroup,
516 						data->groupingCharacter,
517 						data->groupingCharacterLen);
518 		    }
519 		    break;
520 		}
521 	    }
522 
523 	}
524     }
525 
526     /*
527      * Handle final non-alphanumeric token
528      */
529     if (tokens->end != NULL)
530 	 xmlBufferCat(buffer, tokens->end);
531 
532 }
533 
534 static int
xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context,xmlNodePtr node,const xmlChar * count,const xmlChar * from,double * array,xmlDocPtr doc,xmlNodePtr elem)535 xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context,
536 			    xmlNodePtr node,
537 			    const xmlChar *count,
538 			    const xmlChar *from,
539 			    double *array,
540 			    xmlDocPtr doc,
541 			    xmlNodePtr elem)
542 {
543     int amount = 0;
544     int cnt = 0;
545     xmlNodePtr cur;
546     xsltCompMatchPtr countPat = NULL;
547     xsltCompMatchPtr fromPat = NULL;
548 
549     if (count != NULL)
550 	countPat = xsltCompilePattern(count, doc, elem, NULL, context);
551     if (from != NULL)
552 	fromPat = xsltCompilePattern(from, doc, elem, NULL, context);
553 
554     /* select the starting node */
555     switch (node->type) {
556 	case XML_ELEMENT_NODE:
557 	    cur = node;
558 	    break;
559 	case XML_ATTRIBUTE_NODE:
560 	    cur = ((xmlAttrPtr) node)->parent;
561 	    break;
562 	case XML_TEXT_NODE:
563 	case XML_PI_NODE:
564 	case XML_COMMENT_NODE:
565 	    cur = node->parent;
566 	    break;
567 	default:
568 	    cur = NULL;
569 	    break;
570     }
571 
572     while (cur != NULL) {
573 	/* process current node */
574 	if (count == NULL) {
575 	    if ((node->type == cur->type) &&
576 		/* FIXME: must use expanded-name instead of local name */
577 		xmlStrEqual(node->name, cur->name)) {
578 		    if ((node->ns == cur->ns) ||
579 		        ((node->ns != NULL) &&
580 			 (cur->ns != NULL) &&
581 		         (xmlStrEqual(node->ns->href,
582 		             cur->ns->href) )))
583 		        cnt++;
584 	    }
585 	} else {
586 	    if (xsltTestCompMatchList(context, cur, countPat))
587 		cnt++;
588 	}
589 	if ((from != NULL) &&
590 	    xsltTestCompMatchList(context, cur, fromPat)) {
591 	    break; /* while */
592 	}
593 
594 	/* Skip to next preceding or ancestor */
595 	if ((cur->type == XML_DOCUMENT_NODE) ||
596 #ifdef LIBXML_DOCB_ENABLED
597             (cur->type == XML_DOCB_DOCUMENT_NODE) ||
598 #endif
599             (cur->type == XML_HTML_DOCUMENT_NODE))
600 	    break; /* while */
601 
602 	while ((cur->prev != NULL) && ((cur->prev->type == XML_DTD_NODE) ||
603 	       (cur->prev->type == XML_XINCLUDE_START) ||
604 	       (cur->prev->type == XML_XINCLUDE_END)))
605 	    cur = cur->prev;
606 	if (cur->prev != NULL) {
607 	    for (cur = cur->prev; cur->last != NULL; cur = cur->last);
608 	} else {
609 	    cur = cur->parent;
610 	}
611 
612     }
613 
614     array[amount++] = (double) cnt;
615 
616     if (countPat != NULL)
617 	xsltFreeCompMatchList(countPat);
618     if (fromPat != NULL)
619 	xsltFreeCompMatchList(fromPat);
620     return(amount);
621 }
622 
623 static int
xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,xmlNodePtr node,const xmlChar * count,const xmlChar * from,double * array,int max,xmlDocPtr doc,xmlNodePtr elem)624 xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
625 				 xmlNodePtr node,
626 				 const xmlChar *count,
627 				 const xmlChar *from,
628 				 double *array,
629 				 int max,
630 				 xmlDocPtr doc,
631 				 xmlNodePtr elem)
632 {
633     int amount = 0;
634     int cnt;
635     xmlNodePtr ancestor;
636     xmlNodePtr preceding;
637     xmlXPathParserContextPtr parser;
638     xsltCompMatchPtr countPat;
639     xsltCompMatchPtr fromPat;
640 
641     if (count != NULL)
642 	countPat = xsltCompilePattern(count, doc, elem, NULL, context);
643     else
644 	countPat = NULL;
645     if (from != NULL)
646 	fromPat = xsltCompilePattern(from, doc, elem, NULL, context);
647     else
648 	fromPat = NULL;
649     context->xpathCtxt->node = node;
650     parser = xmlXPathNewParserContext(NULL, context->xpathCtxt);
651     if (parser) {
652 	/* ancestor-or-self::*[count] */
653 	for (ancestor = node;
654 	     (ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE);
655 	     ancestor = xmlXPathNextAncestor(parser, ancestor)) {
656 
657 	    if ((from != NULL) &&
658 		xsltTestCompMatchList(context, ancestor, fromPat))
659 		break; /* for */
660 
661 	    if ((count == NULL && node->type == ancestor->type &&
662 		xmlStrEqual(node->name, ancestor->name)) ||
663 		xsltTestCompMatchList(context, ancestor, countPat)) {
664 		/* count(preceding-sibling::*) */
665 		cnt = 0;
666 		for (preceding = ancestor;
667 		     preceding != NULL;
668 		     preceding =
669 		        xmlXPathNextPrecedingSibling(parser, preceding)) {
670 		    if (count == NULL) {
671 			if ((preceding->type == ancestor->type) &&
672 			    xmlStrEqual(preceding->name, ancestor->name)){
673 			    if ((preceding->ns == ancestor->ns) ||
674 			        ((preceding->ns != NULL) &&
675 				 (ancestor->ns != NULL) &&
676 			         (xmlStrEqual(preceding->ns->href,
677 			             ancestor->ns->href) )))
678 			        cnt++;
679 			}
680 		    } else {
681 			if (xsltTestCompMatchList(context, preceding,
682 				                  countPat))
683 			    cnt++;
684 		    }
685 		}
686 		array[amount++] = (double)cnt;
687 		if (amount >= max)
688 		    break; /* for */
689 	    }
690 	}
691 	xmlXPathFreeParserContext(parser);
692     }
693     xsltFreeCompMatchList(countPat);
694     xsltFreeCompMatchList(fromPat);
695     return amount;
696 }
697 
698 static int
xsltNumberFormatGetValue(xmlXPathContextPtr context,xmlNodePtr node,const xmlChar * value,double * number)699 xsltNumberFormatGetValue(xmlXPathContextPtr context,
700 			 xmlNodePtr node,
701 			 const xmlChar *value,
702 			 double *number)
703 {
704     int amount = 0;
705     xmlBufferPtr pattern;
706     xmlXPathObjectPtr obj;
707 
708     pattern = xmlBufferCreate();
709     if (pattern != NULL) {
710 	xmlBufferCCat(pattern, "number(");
711 	xmlBufferCat(pattern, value);
712 	xmlBufferCCat(pattern, ")");
713 	context->node = node;
714 	obj = xmlXPathEvalExpression(xmlBufferContent(pattern),
715 				     context);
716 	if (obj != NULL) {
717 	    *number = obj->floatval;
718 	    amount++;
719 	    xmlXPathFreeObject(obj);
720 	}
721 	xmlBufferFree(pattern);
722     }
723     return amount;
724 }
725 
726 /**
727  * xsltNumberFormat:
728  * @ctxt: the XSLT transformation context
729  * @data: the formatting informations
730  * @node: the data to format
731  *
732  * Convert one number.
733  */
734 void
xsltNumberFormat(xsltTransformContextPtr ctxt,xsltNumberDataPtr data,xmlNodePtr node)735 xsltNumberFormat(xsltTransformContextPtr ctxt,
736 		 xsltNumberDataPtr data,
737 		 xmlNodePtr node)
738 {
739     xmlBufferPtr output = NULL;
740     int amount, i;
741     double number;
742     xsltFormat tokens;
743     int tempformat = 0;
744 
745     if ((data->format == NULL) && (data->has_format != 0)) {
746 	data->format = xsltEvalAttrValueTemplate(ctxt, data->node,
747 					     (const xmlChar *) "format",
748 					     XSLT_NAMESPACE);
749 	tempformat = 1;
750     }
751     if (data->format == NULL) {
752 	return;
753     }
754 
755     output = xmlBufferCreate();
756     if (output == NULL)
757 	goto XSLT_NUMBER_FORMAT_END;
758 
759     xsltNumberFormatTokenize(data->format, &tokens);
760 
761     /*
762      * Evaluate the XPath expression to find the value(s)
763      */
764     if (data->value) {
765 	amount = xsltNumberFormatGetValue(ctxt->xpathCtxt,
766 					  node,
767 					  data->value,
768 					  &number);
769 	if (amount == 1) {
770 	    xsltNumberFormatInsertNumbers(data,
771 					  &number,
772 					  1,
773 					  &tokens,
774 					  output);
775 	}
776 
777     } else if (data->level) {
778 
779 	if (xmlStrEqual(data->level, (const xmlChar *) "single")) {
780 	    amount = xsltNumberFormatGetMultipleLevel(ctxt,
781 						      node,
782 						      data->count,
783 						      data->from,
784 						      &number,
785 						      1,
786 						      data->doc,
787 						      data->node);
788 	    if (amount == 1) {
789 		xsltNumberFormatInsertNumbers(data,
790 					      &number,
791 					      1,
792 					      &tokens,
793 					      output);
794 	    }
795 	} else if (xmlStrEqual(data->level, (const xmlChar *) "multiple")) {
796 	    double numarray[1024];
797 	    int max = sizeof(numarray)/sizeof(numarray[0]);
798 	    amount = xsltNumberFormatGetMultipleLevel(ctxt,
799 						      node,
800 						      data->count,
801 						      data->from,
802 						      numarray,
803 						      max,
804 						      data->doc,
805 						      data->node);
806 	    if (amount > 0) {
807 		xsltNumberFormatInsertNumbers(data,
808 					      numarray,
809 					      amount,
810 					      &tokens,
811 					      output);
812 	    }
813 	} else if (xmlStrEqual(data->level, (const xmlChar *) "any")) {
814 	    amount = xsltNumberFormatGetAnyLevel(ctxt,
815 						 node,
816 						 data->count,
817 						 data->from,
818 						 &number,
819 						 data->doc,
820 						 data->node);
821 	    if (amount > 0) {
822 		xsltNumberFormatInsertNumbers(data,
823 					      &number,
824 					      1,
825 					      &tokens,
826 					      output);
827 	    }
828 	}
829     }
830     /* Insert number as text node */
831     xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0);
832 
833     if (tokens.start != NULL)
834 	xmlFree(tokens.start);
835     if (tokens.end != NULL)
836 	xmlFree(tokens.end);
837     for (i = 0;i < tokens.nTokens;i++) {
838 	if (tokens.tokens[i].separator != NULL)
839 	    xmlFree(tokens.tokens[i].separator);
840     }
841 
842 XSLT_NUMBER_FORMAT_END:
843     if (tempformat == 1) {
844 	/* The format need to be recomputed each time */
845 	data->format = NULL;
846     }
847     if (output != NULL)
848 	xmlBufferFree(output);
849 }
850 
851 static int
xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self,xmlChar ** format,xsltFormatNumberInfoPtr info)852 xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltFormatNumberInfoPtr info)
853 {
854     int	count=0;	/* will hold total length of prefix/suffix */
855     int len;
856 
857     while (1) {
858 	/*
859 	 * prefix / suffix ends at end of string or at
860 	 * first 'special' character
861 	 */
862 	if (**format == 0)
863 	    return count;
864 	/* if next character 'escaped' just count it */
865 	if (**format == SYMBOL_QUOTE) {
866 	    if (*++(*format) == 0)
867 		return -1;
868 	}
869 	else if (IS_SPECIAL(self, *format))
870 	    return count;
871 	/*
872 	 * else treat percent/per-mille as special cases,
873 	 * depending on whether +ve or -ve
874 	 */
875 	else {
876 	    /*
877 	     * for +ve prefix/suffix, allow only a
878 	     * single occurence of either
879 	     */
880 	    if (xsltUTF8Charcmp(*format, self->percent) == 0) {
881 		if (info->is_multiplier_set)
882 		    return -1;
883 		info->multiplier = 100;
884 		info->is_multiplier_set = TRUE;
885 	    } else if (xsltUTF8Charcmp(*format, self->permille) == 0) {
886 		if (info->is_multiplier_set)
887 		    return -1;
888 		info->multiplier = 1000;
889 		info->is_multiplier_set = TRUE;
890 	    }
891 	}
892 
893 	if ((len=xsltUTF8Size(*format)) < 1)
894 	    return -1;
895 	count += len;
896 	*format += len;
897     }
898 }
899 
900 /**
901  * xsltFormatNumberConversion:
902  * @self: the decimal format
903  * @format: the format requested
904  * @number: the value to format
905  * @result: the place to ouput the result
906  *
907  * format-number() uses the JDK 1.1 DecimalFormat class:
908  *
909  * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
910  *
911  * Structure:
912  *
913  *   pattern    := subpattern{;subpattern}
914  *   subpattern := {prefix}integer{.fraction}{suffix}
915  *   prefix     := '\\u0000'..'\\uFFFD' - specialCharacters
916  *   suffix     := '\\u0000'..'\\uFFFD' - specialCharacters
917  *   integer    := '#'* '0'* '0'
918  *   fraction   := '0'* '#'*
919  *
920  *   Notation:
921  *    X*       0 or more instances of X
922  *    (X | Y)  either X or Y.
923  *    X..Y     any character from X up to Y, inclusive.
924  *    S - T    characters in S, except those in T
925  *
926  * Special Characters:
927  *
928  *   Symbol Meaning
929  *   0      a digit
930  *   #      a digit, zero shows as absent
931  *   .      placeholder for decimal separator
932  *   ,      placeholder for grouping separator.
933  *   ;      separates formats.
934  *   -      default negative prefix.
935  *   %      multiply by 100 and show as percentage
936  *   ?      multiply by 1000 and show as per mille
937  *   X      any other characters can be used in the prefix or suffix
938  *   '      used to quote special characters in a prefix or suffix.
939  *
940  * Returns a possible XPath error
941  */
942 xmlXPathError
xsltFormatNumberConversion(xsltDecimalFormatPtr self,xmlChar * format,double number,xmlChar ** result)943 xsltFormatNumberConversion(xsltDecimalFormatPtr self,
944 			   xmlChar *format,
945 			   double number,
946 			   xmlChar **result)
947 {
948     xmlXPathError status = XPATH_EXPRESSION_OK;
949     xmlBufferPtr buffer;
950     xmlChar *the_format, *prefix = NULL, *suffix = NULL;
951     xmlChar *nprefix, *nsuffix = NULL;
952     xmlChar pchar;
953     int	    prefix_length, suffix_length = 0, nprefix_length, nsuffix_length;
954     double  scale;
955     int	    j, len;
956     int     self_grouping_len;
957     xsltFormatNumberInfo format_info;
958     /*
959      * delayed_multiplier allows a 'trailing' percent or
960      * permille to be treated as suffix
961      */
962     int		delayed_multiplier = 0;
963     /* flag to show no -ve format present for -ve number */
964     char	default_sign = 0;
965     /* flag to show error found, should use default format */
966     char	found_error = 0;
967 
968     if (xmlStrlen(format) <= 0) {
969 	xsltTransformError(NULL, NULL, NULL,
970                 "xsltFormatNumberConversion : "
971 		"Invalid format (0-length)\n");
972     }
973     *result = NULL;
974     switch (xmlXPathIsInf(number)) {
975 	case -1:
976 	    if (self->minusSign == NULL)
977 		*result = xmlStrdup(BAD_CAST "-");
978 	    else
979 		*result = xmlStrdup(self->minusSign);
980 	    /* no-break on purpose */
981 	case 1:
982 	    if ((self == NULL) || (self->infinity == NULL))
983 		*result = xmlStrcat(*result, BAD_CAST "Infinity");
984 	    else
985 		*result = xmlStrcat(*result, self->infinity);
986 	    return(status);
987 	default:
988 	    if (xmlXPathIsNaN(number)) {
989 		if ((self == NULL) || (self->noNumber == NULL))
990 		    *result = xmlStrdup(BAD_CAST "NaN");
991 		else
992 		    *result = xmlStrdup(self->noNumber);
993 		return(status);
994 	    }
995     }
996 
997     buffer = xmlBufferCreate();
998     if (buffer == NULL) {
999 	return XPATH_MEMORY_ERROR;
1000     }
1001 
1002     format_info.integer_hash = 0;
1003     format_info.integer_digits = 0;
1004     format_info.frac_digits = 0;
1005     format_info.frac_hash = 0;
1006     format_info.group = -1;
1007     format_info.multiplier = 1;
1008     format_info.add_decimal = FALSE;
1009     format_info.is_multiplier_set = FALSE;
1010     format_info.is_negative_pattern = FALSE;
1011 
1012     the_format = format;
1013 
1014     /*
1015      * First we process the +ve pattern to get percent / permille,
1016      * as well as main format
1017      */
1018     prefix = the_format;
1019     prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
1020     if (prefix_length < 0) {
1021 	found_error = 1;
1022 	goto OUTPUT_NUMBER;
1023     }
1024 
1025     /*
1026      * Here we process the "number" part of the format.  It gets
1027      * a little messy because of the percent/per-mille - if that
1028      * appears at the end, it may be part of the suffix instead
1029      * of part of the number, so the variable delayed_multiplier
1030      * is used to handle it
1031      */
1032     self_grouping_len = xmlStrlen(self->grouping);
1033     while ((*the_format != 0) &&
1034 	   (xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) &&
1035 	   (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) {
1036 
1037 	if (delayed_multiplier != 0) {
1038 	    format_info.multiplier = delayed_multiplier;
1039 	    format_info.is_multiplier_set = TRUE;
1040 	    delayed_multiplier = 0;
1041 	}
1042 	if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1043 	    if (format_info.integer_digits > 0) {
1044 		found_error = 1;
1045 		goto OUTPUT_NUMBER;
1046 	    }
1047 	    format_info.integer_hash++;
1048 	    if (format_info.group >= 0)
1049 		format_info.group++;
1050 	} else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1051 	    format_info.integer_digits++;
1052 	    if (format_info.group >= 0)
1053 		format_info.group++;
1054 	} else if ((self_grouping_len > 0) &&
1055 	    (!xmlStrncmp(the_format, self->grouping, self_grouping_len))) {
1056 	    /* Reset group count */
1057 	    format_info.group = 0;
1058 	    the_format += self_grouping_len;
1059 	    continue;
1060 	} else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1061 	    if (format_info.is_multiplier_set) {
1062 		found_error = 1;
1063 		goto OUTPUT_NUMBER;
1064 	    }
1065 	    delayed_multiplier = 100;
1066 	} else  if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1067 	    if (format_info.is_multiplier_set) {
1068 		found_error = 1;
1069 		goto OUTPUT_NUMBER;
1070 	    }
1071 	    delayed_multiplier = 1000;
1072 	} else
1073 	    break; /* while */
1074 
1075 	if ((len=xsltUTF8Size(the_format)) < 1) {
1076 	    found_error = 1;
1077 	    goto OUTPUT_NUMBER;
1078 	}
1079 	the_format += len;
1080 
1081     }
1082 
1083     /* We have finished the integer part, now work on fraction */
1084     if (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) {
1085         format_info.add_decimal = TRUE;
1086 	the_format += xsltUTF8Size(the_format);	/* Skip over the decimal */
1087     }
1088 
1089     while (*the_format != 0) {
1090 
1091 	if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1092 	    if (format_info.frac_hash != 0) {
1093 		found_error = 1;
1094 		goto OUTPUT_NUMBER;
1095 	    }
1096 	    format_info.frac_digits++;
1097 	} else if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1098 	    format_info.frac_hash++;
1099 	} else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1100 	    if (format_info.is_multiplier_set) {
1101 		found_error = 1;
1102 		goto OUTPUT_NUMBER;
1103 	    }
1104 	    delayed_multiplier = 100;
1105 	    if ((len = xsltUTF8Size(the_format)) < 1) {
1106 	        found_error = 1;
1107 		goto OUTPUT_NUMBER;
1108 	    }
1109 	    the_format += len;
1110 	    continue; /* while */
1111 	} else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1112 	    if (format_info.is_multiplier_set) {
1113 		found_error = 1;
1114 		goto OUTPUT_NUMBER;
1115 	    }
1116 	    delayed_multiplier = 1000;
1117 	    if  ((len = xsltUTF8Size(the_format)) < 1) {
1118 	        found_error = 1;
1119 		goto OUTPUT_NUMBER;
1120 	    }
1121 	    the_format += len;
1122 	    continue; /* while */
1123 	} else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) {
1124 	    break; /* while */
1125 	}
1126 	if ((len = xsltUTF8Size(the_format)) < 1) {
1127 	    found_error = 1;
1128 	    goto OUTPUT_NUMBER;
1129 	}
1130 	the_format += len;
1131 	if (delayed_multiplier != 0) {
1132 	    format_info.multiplier = delayed_multiplier;
1133 	    delayed_multiplier = 0;
1134 	    format_info.is_multiplier_set = TRUE;
1135 	}
1136     }
1137 
1138     /*
1139      * If delayed_multiplier is set after processing the
1140      * "number" part, should be in suffix
1141      */
1142     if (delayed_multiplier != 0) {
1143 	the_format -= len;
1144 	delayed_multiplier = 0;
1145     }
1146 
1147     suffix = the_format;
1148     suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
1149     if ( (suffix_length < 0) ||
1150 	 ((*the_format != 0) &&
1151 	  (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) ) {
1152 	found_error = 1;
1153 	goto OUTPUT_NUMBER;
1154     }
1155 
1156     /*
1157      * We have processed the +ve prefix, number part and +ve suffix.
1158      * If the number is -ve, we must substitute the -ve prefix / suffix
1159      */
1160     if (number < 0) {
1161         /*
1162 	 * Note that j is the number of UTF8 chars before the separator,
1163 	 * not the number of bytes! (bug 151975)
1164 	 */
1165         j =  xmlUTF8Strloc(format, self->patternSeparator);
1166 	if (j < 0) {
1167 	/* No -ve pattern present, so use default signing */
1168 	    default_sign = 1;
1169 	}
1170 	else {
1171 	    /* Skip over pattern separator (accounting for UTF8) */
1172 	    the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1);
1173 	    /*
1174 	     * Flag changes interpretation of percent/permille
1175 	     * in -ve pattern
1176 	     */
1177 	    format_info.is_negative_pattern = TRUE;
1178 	    format_info.is_multiplier_set = FALSE;
1179 
1180 	    /* First do the -ve prefix */
1181 	    nprefix = the_format;
1182 	    nprefix_length = xsltFormatNumberPreSuffix(self,
1183 	    				&the_format, &format_info);
1184 	    if (nprefix_length<0) {
1185 		found_error = 1;
1186 		goto OUTPUT_NUMBER;
1187 	    }
1188 
1189 	    while (*the_format != 0) {
1190 		if ( (xsltUTF8Charcmp(the_format, (self)->percent) == 0) ||
1191 		     (xsltUTF8Charcmp(the_format, (self)->permille)== 0) ) {
1192 		    if (format_info.is_multiplier_set) {
1193 			found_error = 1;
1194 			goto OUTPUT_NUMBER;
1195 		    }
1196 		    format_info.is_multiplier_set = TRUE;
1197 		    delayed_multiplier = 1;
1198 		}
1199 		else if (IS_SPECIAL(self, the_format))
1200 		    delayed_multiplier = 0;
1201 		else
1202 		    break; /* while */
1203 		if ((len = xsltUTF8Size(the_format)) < 1) {
1204 		    found_error = 1;
1205 		    goto OUTPUT_NUMBER;
1206 		}
1207 		the_format += len;
1208 	    }
1209 	    if (delayed_multiplier != 0) {
1210 		format_info.is_multiplier_set = FALSE;
1211 		the_format -= len;
1212 	    }
1213 
1214 	    /* Finally do the -ve suffix */
1215 	    if (*the_format != 0) {
1216 		nsuffix = the_format;
1217 		nsuffix_length = xsltFormatNumberPreSuffix(self,
1218 					&the_format, &format_info);
1219 		if (nsuffix_length < 0) {
1220 		    found_error = 1;
1221 		    goto OUTPUT_NUMBER;
1222 		}
1223 	    }
1224 	    else
1225 		nsuffix_length = 0;
1226 	    if (*the_format != 0) {
1227 		found_error = 1;
1228 		goto OUTPUT_NUMBER;
1229 	    }
1230 	    /*
1231 	     * Here's another Java peculiarity:
1232 	     * if -ve prefix/suffix == +ve ones, discard & use default
1233 	     */
1234 	    if ((nprefix_length != prefix_length) ||
1235 	    	(nsuffix_length != suffix_length) ||
1236 		((nprefix_length > 0) &&
1237 		 (xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) ||
1238 		((nsuffix_length > 0) &&
1239 		 (xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) {
1240 	 	prefix = nprefix;
1241 		prefix_length = nprefix_length;
1242 		suffix = nsuffix;
1243 		suffix_length = nsuffix_length;
1244 	    } /* else {
1245 		default_sign = 1;
1246 	    }
1247 	    */
1248 	}
1249     }
1250 
1251 OUTPUT_NUMBER:
1252     if (found_error != 0) {
1253 	xsltTransformError(NULL, NULL, NULL,
1254                 "xsltFormatNumberConversion : "
1255 		"error in format string '%s', using default\n", format);
1256 	default_sign = (number < 0.0) ? 1 : 0;
1257 	prefix_length = suffix_length = 0;
1258 	format_info.integer_hash = 0;
1259 	format_info.integer_digits = 1;
1260 	format_info.frac_digits = 1;
1261 	format_info.frac_hash = 4;
1262 	format_info.group = -1;
1263 	format_info.multiplier = 1;
1264 	format_info.add_decimal = TRUE;
1265     }
1266 
1267     /* Ready to output our number.  First see if "default sign" is required */
1268     if (default_sign != 0)
1269 	xmlBufferAdd(buffer, self->minusSign, xsltUTF8Size(self->minusSign));
1270 
1271     /* Put the prefix into the buffer */
1272     for (j = 0; j < prefix_length; j++) {
1273 	if ((pchar = *prefix++) == SYMBOL_QUOTE) {
1274 	    len = xsltUTF8Size(prefix);
1275 	    xmlBufferAdd(buffer, prefix, len);
1276 	    prefix += len;
1277 	    j += len - 1;	/* length of symbol less length of quote */
1278 	} else
1279 	    xmlBufferAdd(buffer, &pchar, 1);
1280     }
1281 
1282     /* Next do the integer part of the number */
1283     number = fabs(number) * (double)format_info.multiplier;
1284     scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash));
1285     number = floor((scale * number + 0.5)) / scale;
1286     if ((self->grouping != NULL) &&
1287         (self->grouping[0] != 0)) {
1288 
1289 	len = xmlStrlen(self->grouping);
1290 	pchar = xsltGetUTF8Char(self->grouping, &len);
1291 	xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1292 				format_info.integer_digits,
1293 				format_info.group,
1294 				pchar, len);
1295     } else
1296 	xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1297 				format_info.integer_digits,
1298 				format_info.group,
1299 				',', 1);
1300 
1301     /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
1302     if ((format_info.integer_digits + format_info.integer_hash +
1303 	 format_info.frac_digits == 0) && (format_info.frac_hash > 0)) {
1304         ++format_info.frac_digits;
1305 	--format_info.frac_hash;
1306     }
1307 
1308     /* Add leading zero, if required */
1309     if ((floor(number) == 0) &&
1310 	(format_info.integer_digits + format_info.frac_digits == 0)) {
1311         xmlBufferAdd(buffer, self->zeroDigit, xsltUTF8Size(self->zeroDigit));
1312     }
1313 
1314     /* Next the fractional part, if required */
1315     if (format_info.frac_digits + format_info.frac_hash == 0) {
1316         if (format_info.add_decimal)
1317 	    xmlBufferAdd(buffer, self->decimalPoint,
1318 	    		 xsltUTF8Size(self->decimalPoint));
1319     }
1320     else {
1321       number -= floor(number);
1322 	if ((number != 0) || (format_info.frac_digits != 0)) {
1323 	    xmlBufferAdd(buffer, self->decimalPoint,
1324 	    		 xsltUTF8Size(self->decimalPoint));
1325 	    number = floor(scale * number + 0.5);
1326 	    for (j = format_info.frac_hash; j > 0; j--) {
1327 		if (fmod(number, 10.0) >= 1.0)
1328 		    break; /* for */
1329 		number /= 10.0;
1330 	    }
1331 	    xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1332 				format_info.frac_digits + j,
1333 				0, 0, 0);
1334 	}
1335     }
1336     /* Put the suffix into the buffer */
1337     for (j = 0; j < suffix_length; j++) {
1338 	if ((pchar = *suffix++) == SYMBOL_QUOTE) {
1339             len = xsltUTF8Size(suffix);
1340 	    xmlBufferAdd(buffer, suffix, len);
1341 	    suffix += len;
1342 	    j += len - 1;	/* length of symbol less length of escape */
1343 	} else
1344 	    xmlBufferAdd(buffer, &pchar, 1);
1345     }
1346 
1347     *result = xmlStrdup(xmlBufferContent(buffer));
1348     xmlBufferFree(buffer);
1349     return status;
1350 }
1351 
1352