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