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