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