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