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