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