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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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