Branching for 0.3.15 release after two days of no response from a certain sphere...
[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 #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 xsltCompMatchPtr countPat,
538 xsltCompMatchPtr fromPat,
539 double *array,
540 xmlDocPtr doc,
541 xmlNodePtr elem)
542 {
543 int amount = 0;
544 int cnt = 0;
545 xmlNodePtr cur;
546
547 /* select the starting node */
548 switch (node->type) {
549 case XML_ELEMENT_NODE:
550 cur = node;
551 break;
552 case XML_ATTRIBUTE_NODE:
553 cur = ((xmlAttrPtr) node)->parent;
554 break;
555 case XML_TEXT_NODE:
556 case XML_PI_NODE:
557 case XML_COMMENT_NODE:
558 cur = node->parent;
559 break;
560 default:
561 cur = NULL;
562 break;
563 }
564
565 while (cur != NULL) {
566 /* process current node */
567 if (countPat == NULL) {
568 if ((node->type == cur->type) &&
569 /* FIXME: must use expanded-name instead of local name */
570 xmlStrEqual(node->name, cur->name)) {
571 if ((node->ns == cur->ns) ||
572 ((node->ns != NULL) &&
573 (cur->ns != NULL) &&
574 (xmlStrEqual(node->ns->href,
575 cur->ns->href) )))
576 cnt++;
577 }
578 } else {
579 if (xsltTestCompMatchList(context, cur, countPat))
580 cnt++;
581 }
582 if ((fromPat != NULL) &&
583 xsltTestCompMatchList(context, cur, fromPat)) {
584 break; /* while */
585 }
586
587 /* Skip to next preceding or ancestor */
588 if ((cur->type == XML_DOCUMENT_NODE) ||
589 #ifdef LIBXML_DOCB_ENABLED
590 (cur->type == XML_DOCB_DOCUMENT_NODE) ||
591 #endif
592 (cur->type == XML_HTML_DOCUMENT_NODE))
593 break; /* while */
594
595 while ((cur->prev != NULL) && ((cur->prev->type == XML_DTD_NODE) ||
596 (cur->prev->type == XML_XINCLUDE_START) ||
597 (cur->prev->type == XML_XINCLUDE_END)))
598 cur = cur->prev;
599 if (cur->prev != NULL) {
600 for (cur = cur->prev; cur->last != NULL; cur = cur->last);
601 } else {
602 cur = cur->parent;
603 }
604
605 }
606
607 array[amount++] = (double) cnt;
608
609 return(amount);
610 }
611
612 static int
613 xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
614 xmlNodePtr node,
615 xsltCompMatchPtr countPat,
616 xsltCompMatchPtr fromPat,
617 double *array,
618 int max,
619 xmlDocPtr doc,
620 xmlNodePtr elem)
621 {
622 int amount = 0;
623 int cnt;
624 xmlNodePtr ancestor;
625 xmlNodePtr preceding;
626 xmlXPathParserContextPtr parser;
627
628 context->xpathCtxt->node = node;
629 parser = xmlXPathNewParserContext(NULL, context->xpathCtxt);
630 if (parser) {
631 /* ancestor-or-self::*[count] */
632 for (ancestor = node;
633 (ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE);
634 ancestor = xmlXPathNextAncestor(parser, ancestor)) {
635
636 if ((fromPat != NULL) &&
637 xsltTestCompMatchList(context, ancestor, fromPat))
638 break; /* for */
639
640 if ((countPat == NULL && node->type == ancestor->type &&
641 xmlStrEqual(node->name, ancestor->name)) ||
642 xsltTestCompMatchList(context, ancestor, countPat)) {
643 /* count(preceding-sibling::*) */
644 cnt = 0;
645 for (preceding = ancestor;
646 preceding != NULL;
647 preceding =
648 xmlXPathNextPrecedingSibling(parser, preceding)) {
649 if (countPat == NULL) {
650 if ((preceding->type == ancestor->type) &&
651 xmlStrEqual(preceding->name, ancestor->name)){
652 if ((preceding->ns == ancestor->ns) ||
653 ((preceding->ns != NULL) &&
654 (ancestor->ns != NULL) &&
655 (xmlStrEqual(preceding->ns->href,
656 ancestor->ns->href) )))
657 cnt++;
658 }
659 } else {
660 if (xsltTestCompMatchList(context, preceding,
661 countPat))
662 cnt++;
663 }
664 }
665 array[amount++] = (double)cnt;
666 if (amount >= max)
667 break; /* for */
668 }
669 }
670 xmlXPathFreeParserContext(parser);
671 }
672 return amount;
673 }
674
675 static int
676 xsltNumberFormatGetValue(xmlXPathContextPtr context,
677 xmlNodePtr node,
678 const xmlChar *value,
679 double *number)
680 {
681 int amount = 0;
682 xmlBufferPtr pattern;
683 xmlXPathObjectPtr obj;
684
685 pattern = xmlBufferCreate();
686 if (pattern != NULL) {
687 xmlBufferCCat(pattern, "number(");
688 xmlBufferCat(pattern, value);
689 xmlBufferCCat(pattern, ")");
690 context->node = node;
691 obj = xmlXPathEvalExpression(xmlBufferContent(pattern),
692 context);
693 if (obj != NULL) {
694 *number = obj->floatval;
695 amount++;
696 xmlXPathFreeObject(obj);
697 }
698 xmlBufferFree(pattern);
699 }
700 return amount;
701 }
702
703 /**
704 * xsltNumberFormat:
705 * @ctxt: the XSLT transformation context
706 * @data: the formatting informations
707 * @node: the data to format
708 *
709 * Convert one number.
710 */
711 void
712 xsltNumberFormat(xsltTransformContextPtr ctxt,
713 xsltNumberDataPtr data,
714 xmlNodePtr node)
715 {
716 xmlBufferPtr output = NULL;
717 int amount, i;
718 double number;
719 xsltFormat tokens;
720 int tempformat = 0;
721
722 if ((data->format == NULL) && (data->has_format != 0)) {
723 data->format = xsltEvalAttrValueTemplate(ctxt, data->node,
724 (const xmlChar *) "format",
725 XSLT_NAMESPACE);
726 tempformat = 1;
727 }
728 if (data->format == NULL) {
729 return;
730 }
731
732 output = xmlBufferCreate();
733 if (output == NULL)
734 goto XSLT_NUMBER_FORMAT_END;
735
736 xsltNumberFormatTokenize(data->format, &tokens);
737
738 /*
739 * Evaluate the XPath expression to find the value(s)
740 */
741 if (data->value) {
742 amount = xsltNumberFormatGetValue(ctxt->xpathCtxt,
743 node,
744 data->value,
745 &number);
746 if (amount == 1) {
747 xsltNumberFormatInsertNumbers(data,
748 &number,
749 1,
750 &tokens,
751 output);
752 }
753
754 } else if (data->level) {
755
756 if (xmlStrEqual(data->level, (const xmlChar *) "single")) {
757 amount = xsltNumberFormatGetMultipleLevel(ctxt,
758 node,
759 data->countPat,
760 data->fromPat,
761 &number,
762 1,
763 data->doc,
764 data->node);
765 if (amount == 1) {
766 xsltNumberFormatInsertNumbers(data,
767 &number,
768 1,
769 &tokens,
770 output);
771 }
772 } else if (xmlStrEqual(data->level, (const xmlChar *) "multiple")) {
773 double numarray[1024];
774 int max = sizeof(numarray)/sizeof(numarray[0]);
775 amount = xsltNumberFormatGetMultipleLevel(ctxt,
776 node,
777 data->countPat,
778 data->fromPat,
779 numarray,
780 max,
781 data->doc,
782 data->node);
783 if (amount > 0) {
784 xsltNumberFormatInsertNumbers(data,
785 numarray,
786 amount,
787 &tokens,
788 output);
789 }
790 } else if (xmlStrEqual(data->level, (const xmlChar *) "any")) {
791 amount = xsltNumberFormatGetAnyLevel(ctxt,
792 node,
793 data->countPat,
794 data->fromPat,
795 &number,
796 data->doc,
797 data->node);
798 if (amount > 0) {
799 xsltNumberFormatInsertNumbers(data,
800 &number,
801 1,
802 &tokens,
803 output);
804 }
805 }
806 }
807 /* Insert number as text node */
808 xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0);
809
810 if (tokens.start != NULL)
811 xmlFree(tokens.start);
812 if (tokens.end != NULL)
813 xmlFree(tokens.end);
814 for (i = 0;i < tokens.nTokens;i++) {
815 if (tokens.tokens[i].separator != NULL)
816 xmlFree(tokens.tokens[i].separator);
817 }
818
819 XSLT_NUMBER_FORMAT_END:
820 if (tempformat == 1) {
821 /* The format need to be recomputed each time */
822 data->format = NULL;
823 }
824 if (output != NULL)
825 xmlBufferFree(output);
826 }
827
828 static int
829 xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltFormatNumberInfoPtr info)
830 {
831 int count=0; /* will hold total length of prefix/suffix */
832 int len;
833
834 while (1) {
835 /*
836 * prefix / suffix ends at end of string or at
837 * first 'special' character
838 */
839 if (**format == 0)
840 return count;
841 /* if next character 'escaped' just count it */
842 if (**format == SYMBOL_QUOTE) {
843 if (*++(*format) == 0)
844 return -1;
845 }
846 else if (IS_SPECIAL(self, *format))
847 return count;
848 /*
849 * else treat percent/per-mille as special cases,
850 * depending on whether +ve or -ve
851 */
852 else {
853 /*
854 * for +ve prefix/suffix, allow only a
855 * single occurence of either
856 */
857 if (xsltUTF8Charcmp(*format, self->percent) == 0) {
858 if (info->is_multiplier_set)
859 return -1;
860 info->multiplier = 100;
861 info->is_multiplier_set = TRUE;
862 } else if (xsltUTF8Charcmp(*format, self->permille) == 0) {
863 if (info->is_multiplier_set)
864 return -1;
865 info->multiplier = 1000;
866 info->is_multiplier_set = TRUE;
867 }
868 }
869
870 if ((len=xsltUTF8Size(*format)) < 1)
871 return -1;
872 count += len;
873 *format += len;
874 }
875 }
876
877 /**
878 * xsltFormatNumberConversion:
879 * @self: the decimal format
880 * @format: the format requested
881 * @number: the value to format
882 * @result: the place to ouput the result
883 *
884 * format-number() uses the JDK 1.1 DecimalFormat class:
885 *
886 * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
887 *
888 * Structure:
889 *
890 * pattern := subpattern{;subpattern}
891 * subpattern := {prefix}integer{.fraction}{suffix}
892 * prefix := '\\u0000'..'\\uFFFD' - specialCharacters
893 * suffix := '\\u0000'..'\\uFFFD' - specialCharacters
894 * integer := '#'* '0'* '0'
895 * fraction := '0'* '#'*
896 *
897 * Notation:
898 * X* 0 or more instances of X
899 * (X | Y) either X or Y.
900 * X..Y any character from X up to Y, inclusive.
901 * S - T characters in S, except those in T
902 *
903 * Special Characters:
904 *
905 * Symbol Meaning
906 * 0 a digit
907 * # a digit, zero shows as absent
908 * . placeholder for decimal separator
909 * , placeholder for grouping separator.
910 * ; separates formats.
911 * - default negative prefix.
912 * % multiply by 100 and show as percentage
913 * ? multiply by 1000 and show as per mille
914 * X any other characters can be used in the prefix or suffix
915 * ' used to quote special characters in a prefix or suffix.
916 *
917 * Returns a possible XPath error
918 */
919 xmlXPathError
920 xsltFormatNumberConversion(xsltDecimalFormatPtr self,
921 xmlChar *format,
922 double number,
923 xmlChar **result)
924 {
925 xmlXPathError status = XPATH_EXPRESSION_OK;
926 xmlBufferPtr buffer;
927 xmlChar *the_format, *prefix = NULL, *suffix = NULL;
928 xmlChar *nprefix, *nsuffix = NULL;
929 xmlChar pchar;
930 int prefix_length, suffix_length = 0, nprefix_length, nsuffix_length;
931 double scale;
932 int j, len;
933 int self_grouping_len;
934 xsltFormatNumberInfo format_info;
935 /*
936 * delayed_multiplier allows a 'trailing' percent or
937 * permille to be treated as suffix
938 */
939 int delayed_multiplier = 0;
940 /* flag to show no -ve format present for -ve number */
941 char default_sign = 0;
942 /* flag to show error found, should use default format */
943 char found_error = 0;
944
945 if (xmlStrlen(format) <= 0) {
946 xsltTransformError(NULL, NULL, NULL,
947 "xsltFormatNumberConversion : "
948 "Invalid format (0-length)\n");
949 }
950 *result = NULL;
951 switch (xmlXPathIsInf(number)) {
952 case -1:
953 if (self->minusSign == NULL)
954 *result = xmlStrdup(BAD_CAST "-");
955 else
956 *result = xmlStrdup(self->minusSign);
957 /* no-break on purpose */
958 case 1:
959 if ((self == NULL) || (self->infinity == NULL))
960 *result = xmlStrcat(*result, BAD_CAST "Infinity");
961 else
962 *result = xmlStrcat(*result, self->infinity);
963 return(status);
964 default:
965 if (xmlXPathIsNaN(number)) {
966 if ((self == NULL) || (self->noNumber == NULL))
967 *result = xmlStrdup(BAD_CAST "NaN");
968 else
969 *result = xmlStrdup(self->noNumber);
970 return(status);
971 }
972 }
973
974 buffer = xmlBufferCreate();
975 if (buffer == NULL) {
976 return XPATH_MEMORY_ERROR;
977 }
978
979 format_info.integer_hash = 0;
980 format_info.integer_digits = 0;
981 format_info.frac_digits = 0;
982 format_info.frac_hash = 0;
983 format_info.group = -1;
984 format_info.multiplier = 1;
985 format_info.add_decimal = FALSE;
986 format_info.is_multiplier_set = FALSE;
987 format_info.is_negative_pattern = FALSE;
988
989 the_format = format;
990
991 /*
992 * First we process the +ve pattern to get percent / permille,
993 * as well as main format
994 */
995 prefix = the_format;
996 prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
997 if (prefix_length < 0) {
998 found_error = 1;
999 goto OUTPUT_NUMBER;
1000 }
1001
1002 /*
1003 * Here we process the "number" part of the format. It gets
1004 * a little messy because of the percent/per-mille - if that
1005 * appears at the end, it may be part of the suffix instead
1006 * of part of the number, so the variable delayed_multiplier
1007 * is used to handle it
1008 */
1009 self_grouping_len = xmlStrlen(self->grouping);
1010 while ((*the_format != 0) &&
1011 (xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) &&
1012 (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) {
1013
1014 if (delayed_multiplier != 0) {
1015 format_info.multiplier = delayed_multiplier;
1016 format_info.is_multiplier_set = TRUE;
1017 delayed_multiplier = 0;
1018 }
1019 if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1020 if (format_info.integer_digits > 0) {
1021 found_error = 1;
1022 goto OUTPUT_NUMBER;
1023 }
1024 format_info.integer_hash++;
1025 if (format_info.group >= 0)
1026 format_info.group++;
1027 } else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1028 format_info.integer_digits++;
1029 if (format_info.group >= 0)
1030 format_info.group++;
1031 } else if ((self_grouping_len > 0) &&
1032 (!xmlStrncmp(the_format, self->grouping, self_grouping_len))) {
1033 /* Reset group count */
1034 format_info.group = 0;
1035 the_format += self_grouping_len;
1036 continue;
1037 } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1038 if (format_info.is_multiplier_set) {
1039 found_error = 1;
1040 goto OUTPUT_NUMBER;
1041 }
1042 delayed_multiplier = 100;
1043 } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1044 if (format_info.is_multiplier_set) {
1045 found_error = 1;
1046 goto OUTPUT_NUMBER;
1047 }
1048 delayed_multiplier = 1000;
1049 } else
1050 break; /* while */
1051
1052 if ((len=xsltUTF8Size(the_format)) < 1) {
1053 found_error = 1;
1054 goto OUTPUT_NUMBER;
1055 }
1056 the_format += len;
1057
1058 }
1059
1060 /* We have finished the integer part, now work on fraction */
1061 if (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) {
1062 format_info.add_decimal = TRUE;
1063 the_format += xsltUTF8Size(the_format); /* Skip over the decimal */
1064 }
1065
1066 while (*the_format != 0) {
1067
1068 if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1069 if (format_info.frac_hash != 0) {
1070 found_error = 1;
1071 goto OUTPUT_NUMBER;
1072 }
1073 format_info.frac_digits++;
1074 } else if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1075 format_info.frac_hash++;
1076 } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1077 if (format_info.is_multiplier_set) {
1078 found_error = 1;
1079 goto OUTPUT_NUMBER;
1080 }
1081 delayed_multiplier = 100;
1082 if ((len = xsltUTF8Size(the_format)) < 1) {
1083 found_error = 1;
1084 goto OUTPUT_NUMBER;
1085 }
1086 the_format += len;
1087 continue; /* while */
1088 } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1089 if (format_info.is_multiplier_set) {
1090 found_error = 1;
1091 goto OUTPUT_NUMBER;
1092 }
1093 delayed_multiplier = 1000;
1094 if ((len = xsltUTF8Size(the_format)) < 1) {
1095 found_error = 1;
1096 goto OUTPUT_NUMBER;
1097 }
1098 the_format += len;
1099 continue; /* while */
1100 } else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) {
1101 break; /* while */
1102 }
1103 if ((len = xsltUTF8Size(the_format)) < 1) {
1104 found_error = 1;
1105 goto OUTPUT_NUMBER;
1106 }
1107 the_format += len;
1108 if (delayed_multiplier != 0) {
1109 format_info.multiplier = delayed_multiplier;
1110 delayed_multiplier = 0;
1111 format_info.is_multiplier_set = TRUE;
1112 }
1113 }
1114
1115 /*
1116 * If delayed_multiplier is set after processing the
1117 * "number" part, should be in suffix
1118 */
1119 if (delayed_multiplier != 0) {
1120 the_format -= len;
1121 delayed_multiplier = 0;
1122 }
1123
1124 suffix = the_format;
1125 suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
1126 if ( (suffix_length < 0) ||
1127 ((*the_format != 0) &&
1128 (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) ) {
1129 found_error = 1;
1130 goto OUTPUT_NUMBER;
1131 }
1132
1133 /*
1134 * We have processed the +ve prefix, number part and +ve suffix.
1135 * If the number is -ve, we must substitute the -ve prefix / suffix
1136 */
1137 if (number < 0) {
1138 /*
1139 * Note that j is the number of UTF8 chars before the separator,
1140 * not the number of bytes! (bug 151975)
1141 */
1142 j = xmlUTF8Strloc(format, self->patternSeparator);
1143 if (j < 0) {
1144 /* No -ve pattern present, so use default signing */
1145 default_sign = 1;
1146 }
1147 else {
1148 /* Skip over pattern separator (accounting for UTF8) */
1149 the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1);
1150 /*
1151 * Flag changes interpretation of percent/permille
1152 * in -ve pattern
1153 */
1154 format_info.is_negative_pattern = TRUE;
1155 format_info.is_multiplier_set = FALSE;
1156
1157 /* First do the -ve prefix */
1158 nprefix = the_format;
1159 nprefix_length = xsltFormatNumberPreSuffix(self,
1160 &the_format, &format_info);
1161 if (nprefix_length<0) {
1162 found_error = 1;
1163 goto OUTPUT_NUMBER;
1164 }
1165
1166 while (*the_format != 0) {
1167 if ( (xsltUTF8Charcmp(the_format, (self)->percent) == 0) ||
1168 (xsltUTF8Charcmp(the_format, (self)->permille)== 0) ) {
1169 if (format_info.is_multiplier_set) {
1170 found_error = 1;
1171 goto OUTPUT_NUMBER;
1172 }
1173 format_info.is_multiplier_set = TRUE;
1174 delayed_multiplier = 1;
1175 }
1176 else if (IS_SPECIAL(self, the_format))
1177 delayed_multiplier = 0;
1178 else
1179 break; /* while */
1180 if ((len = xsltUTF8Size(the_format)) < 1) {
1181 found_error = 1;
1182 goto OUTPUT_NUMBER;
1183 }
1184 the_format += len;
1185 }
1186 if (delayed_multiplier != 0) {
1187 format_info.is_multiplier_set = FALSE;
1188 the_format -= len;
1189 }
1190
1191 /* Finally do the -ve suffix */
1192 if (*the_format != 0) {
1193 nsuffix = the_format;
1194 nsuffix_length = xsltFormatNumberPreSuffix(self,
1195 &the_format, &format_info);
1196 if (nsuffix_length < 0) {
1197 found_error = 1;
1198 goto OUTPUT_NUMBER;
1199 }
1200 }
1201 else
1202 nsuffix_length = 0;
1203 if (*the_format != 0) {
1204 found_error = 1;
1205 goto OUTPUT_NUMBER;
1206 }
1207 /*
1208 * Here's another Java peculiarity:
1209 * if -ve prefix/suffix == +ve ones, discard & use default
1210 */
1211 if ((nprefix_length != prefix_length) ||
1212 (nsuffix_length != suffix_length) ||
1213 ((nprefix_length > 0) &&
1214 (xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) ||
1215 ((nsuffix_length > 0) &&
1216 (xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) {
1217 prefix = nprefix;
1218 prefix_length = nprefix_length;
1219 suffix = nsuffix;
1220 suffix_length = nsuffix_length;
1221 } /* else {
1222 default_sign = 1;
1223 }
1224 */
1225 }
1226 }
1227
1228 OUTPUT_NUMBER:
1229 if (found_error != 0) {
1230 xsltTransformError(NULL, NULL, NULL,
1231 "xsltFormatNumberConversion : "
1232 "error in format string '%s', using default\n", format);
1233 default_sign = (number < 0.0) ? 1 : 0;
1234 prefix_length = suffix_length = 0;
1235 format_info.integer_hash = 0;
1236 format_info.integer_digits = 1;
1237 format_info.frac_digits = 1;
1238 format_info.frac_hash = 4;
1239 format_info.group = -1;
1240 format_info.multiplier = 1;
1241 format_info.add_decimal = TRUE;
1242 }
1243
1244 /* Ready to output our number. First see if "default sign" is required */
1245 if (default_sign != 0)
1246 xmlBufferAdd(buffer, self->minusSign, xsltUTF8Size(self->minusSign));
1247
1248 /* Put the prefix into the buffer */
1249 for (j = 0; j < prefix_length; j++) {
1250 if ((pchar = *prefix++) == SYMBOL_QUOTE) {
1251 len = xsltUTF8Size(prefix);
1252 xmlBufferAdd(buffer, prefix, len);
1253 prefix += len;
1254 j += len - 1; /* length of symbol less length of quote */
1255 } else
1256 xmlBufferAdd(buffer, &pchar, 1);
1257 }
1258
1259 /* Next do the integer part of the number */
1260 number = fabs(number) * (double)format_info.multiplier;
1261 scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash));
1262 number = floor((scale * number + 0.5)) / scale;
1263 if ((self->grouping != NULL) &&
1264 (self->grouping[0] != 0)) {
1265
1266 len = xmlStrlen(self->grouping);
1267 pchar = xsltGetUTF8Char(self->grouping, &len);
1268 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1269 format_info.integer_digits,
1270 format_info.group,
1271 pchar, len);
1272 } else
1273 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1274 format_info.integer_digits,
1275 format_info.group,
1276 ',', 1);
1277
1278 /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
1279 if ((format_info.integer_digits + format_info.integer_hash +
1280 format_info.frac_digits == 0) && (format_info.frac_hash > 0)) {
1281 ++format_info.frac_digits;
1282 --format_info.frac_hash;
1283 }
1284
1285 /* Add leading zero, if required */
1286 if ((floor(number) == 0) &&
1287 (format_info.integer_digits + format_info.frac_digits == 0)) {
1288 xmlBufferAdd(buffer, self->zeroDigit, xsltUTF8Size(self->zeroDigit));
1289 }
1290
1291 /* Next the fractional part, if required */
1292 if (format_info.frac_digits + format_info.frac_hash == 0) {
1293 if (format_info.add_decimal)
1294 xmlBufferAdd(buffer, self->decimalPoint,
1295 xsltUTF8Size(self->decimalPoint));
1296 }
1297 else {
1298 number -= floor(number);
1299 if ((number != 0) || (format_info.frac_digits != 0)) {
1300 xmlBufferAdd(buffer, self->decimalPoint,
1301 xsltUTF8Size(self->decimalPoint));
1302 number = floor(scale * number + 0.5);
1303 for (j = format_info.frac_hash; j > 0; j--) {
1304 if (fmod(number, 10.0) >= 1.0)
1305 break; /* for */
1306 number /= 10.0;
1307 }
1308 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1309 format_info.frac_digits + j,
1310 0, 0, 0);
1311 }
1312 }
1313 /* Put the suffix into the buffer */
1314 for (j = 0; j < suffix_length; j++) {
1315 if ((pchar = *suffix++) == SYMBOL_QUOTE) {
1316 len = xsltUTF8Size(suffix);
1317 xmlBufferAdd(buffer, suffix, len);
1318 suffix += len;
1319 j += len - 1; /* length of symbol less length of escape */
1320 } else
1321 xmlBufferAdd(buffer, &pchar, 1);
1322 }
1323
1324 *result = xmlStrdup(xmlBufferContent(buffer));
1325 xmlBufferFree(buffer);
1326 return status;
1327 }
1328