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