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