Home | History | Annotate | Download | only in libxslt
      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 (at) veillard.com
     10  * Bjorn Reese <breese (at) 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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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