numbers.c 37.1 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * numbers.c: Implementation of the XSLT number functions
 *
 * Reference:
 *   http://www.w3.org/TR/1999/REC-xslt-19991116
 *
 * See Copyright for the status of this software.
 *
9
 * daniel@veillard.com
10 11 12
 * Bjorn Reese <breese@users.sourceforge.net>
 */

13
#define IN_LIBXSLT
14
#include "libxslt.h"
15

16 17 18
#include <math.h>
#include <limits.h>
#include <float.h>
William M. Brack's avatar
William M. Brack committed
19
#include <string.h>
20

21
#include <libxml/xmlmemory.h>
22
#include <libxml/parserInternals.h>
23 24
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
25
#include <libxml/encoding.h>
26
#include "xsltutils.h"
Bjorn Reese's avatar
Bjorn Reese committed
27
#include "pattern.h"
28
#include "templates.h"
29
#include "transform.h"
30 31 32 33 34 35 36
#include "numbersInternals.h"

#ifndef FALSE
# define FALSE (0 == 1)
# define TRUE (1 == 1)
#endif

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
#define SYMBOL_QUOTE		((xmlChar)'\'')

#define DEFAULT_TOKEN		(xmlChar)'0'
#define DEFAULT_SEPARATOR	"."

#define MAX_TOKENS		1024

typedef struct _xsltFormatToken xsltFormatToken;
typedef xsltFormatToken *xsltFormatTokenPtr;
struct _xsltFormatToken {
    xmlChar	*separator;
    xmlChar	 token;
    int		 width;
};

typedef struct _xsltFormat xsltFormat;
typedef xsltFormat *xsltFormatPtr;
struct _xsltFormat {
    xmlChar		*start;
    xsltFormatToken	 tokens[MAX_TOKENS];
    int			 nTokens;
    xmlChar		*end;
};
Bjorn Reese's avatar
Bjorn Reese committed
60

61 62
static char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz";
63
static xsltFormatToken default_token;
Bjorn Reese's avatar
Bjorn Reese committed
64

65 66 67
/*
 * **** Start temp insert ****
 *
68 69 70
 * The following routine xsltUTF8Charcmp will be replaced with calls to
 * the corresponding libxml routine at a later date (when other
 * inter-library dependencies require it).
71 72 73 74 75 76 77 78 79 80 81 82
 */

/**
 * xsltUTF8Charcmp
 * @utf1: pointer to first UTF8 char
 * @utf2: pointer to second UTF8 char
 *
 * returns result of comparing the two UCS4 values
 * as with xmlStrncmp
 */
static int
xsltUTF8Charcmp(xmlChar *utf1, xmlChar *utf2) {
83
    int len = xmlUTF8Strsize(utf1, 1);
84

85 86
    if (len < 1)
        return -1;
87 88 89 90 91
    if (utf1 == NULL ) {
        if (utf2 == NULL)
            return 0;
        return -1;
    }
92
    return xmlStrncmp(utf1, utf2, len);
93 94 95
}

/***** Stop temp insert *****/
96 97 98 99 100 101
/************************************************************************
 *									*
 *			Utility functions				*
 *									*
 ************************************************************************/

102
#define IS_SPECIAL(self,letter)			\
103 104 105 106 107
    ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0)	    ||	\
     (xsltUTF8Charcmp((letter), (self)->digit) == 0)	    ||	\
     (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0)  ||	\
     (xsltUTF8Charcmp((letter), (self)->grouping) == 0)	    ||	\
     (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0))
108 109

#define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
110
#define IS_DIGIT_ONE(x) xsltIsDigitZero((xmlChar)(x)-1)
111 112

static int
113
xsltIsDigitZero(unsigned int ch)
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
{
    /*
     * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
     */
    switch (ch) {
    case 0x0030: case 0x0660: case 0x06F0: case 0x0966:
    case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66:
    case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50:
    case 0x0E60: case 0x0F20: case 0x1040: case 0x17E0:
    case 0x1810: case 0xFF10:
	return TRUE;
    default:
	return FALSE;
    }
}

130 131 132
static void
xsltNumberFormatDecimal(xmlBufferPtr buffer,
			double number,
133
			int digit_zero,
134 135
			int width,
			int digitsPerGroup,
136 137
			int groupingCharacter,
			int groupingCharacterLen)
138
{
139 140 141 142 143 144 145 146 147
    /*
     * This used to be
     *  xmlChar temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 4];
     * which would be length 68 on x86 arch.  It was changed to be a longer,
     * fixed length in order to try to cater for (reasonable) UTF8
     * separators and numeric characters.  The max UTF8 char size will be
     * 6 or less, so the value used [500] should be *much* larger than needed
     */
    xmlChar temp_string[500];
148
    xmlChar *pointer;
149
    xmlChar temp_char[6];
150
    int i;
151 152
    int val;
    int len;
153 154

    /* Build buffer from back */
155 156
    pointer = &temp_string[sizeof(temp_string)] - 1;	/* last char */
    *pointer = 0;
157 158
    i = 0;
    while (pointer > temp_string) {
159 160
	if ((i >= width) && (fabs(number) < 1.0))
	    break; /* for */
161
	if ((i > 0) && (groupingCharacter != 0) &&
162 163
	    (digitsPerGroup > 0) &&
	    ((i % digitsPerGroup) == 0)) {
164 165 166 167
	    if (pointer - groupingCharacterLen < temp_string) {
	        i = -1;		/* flag error */
		break;
	    }
168 169
	    pointer -= groupingCharacterLen;
	    xmlCopyCharMultiByte(pointer, groupingCharacter);
170
	}
Daniel Veillard's avatar
Daniel Veillard committed
171

172 173 174 175 176 177 178 179 180
	val = digit_zero + (int)fmod(number, 10.0);
	if (val < 0x80) {			/* shortcut if ASCII */
	    if (pointer <= temp_string) {	/* Check enough room */
	        i = -1;
		break;
	    }
	    *(--pointer) = val;
	}
	else {
Daniel Veillard's avatar
Daniel Veillard committed
181
	/*
182 183 184 185 186 187 188 189 190 191 192
	 * Here we have a multibyte character.  It's a little messy,
	 * because until we generate the char we don't know how long
	 * it is.  So, we generate it into the buffer temp_char, then
	 * copy from there into temp_string.
	 */
	    len = xmlCopyCharMultiByte(temp_char, val);
	    if ( (pointer - len) < temp_string ) {
	        i = -1;
		break;
	    }
	    pointer -= len;
193
	    memcpy(pointer, temp_char, len);
194
	}
195 196
	number /= 10.0;
	++i;
197
    }
198 199
    if (i < 0)
        xsltGenericError(xsltGenericErrorContext,
200
		"xsltNumberFormatDecimal: Internal buffer size exceeded\n");
201 202 203 204
    xmlBufferCat(buffer, pointer);
}

static void
205 206
xsltNumberFormatAlpha(xsltNumberDataPtr data,
		      xmlBufferPtr buffer,
207 208 209 210 211 212 213 214 215
		      double number,
		      int is_upper)
{
    char temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1];
    char *pointer;
    int i;
    char *alpha_list;
    double alpha_size = (double)(sizeof(alpha_upper_list) - 1);

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
    /*
     * XSLT 1.0 isn't clear on how to handle zero, but XSLT 2.0 says:
     *
     *     For all format tokens other than the first kind above (one that
     *     consists of decimal digits), there may be implementation-defined
     *     lower and upper bounds on the range of numbers that can be
     *     formatted using this format token; indeed, for some numbering
     *     sequences there may be intrinsic limits. [...] Numbers that fall
     *     outside this range must be formatted using the format token 1.
     *
     * The "a" token has an intrinsic lower limit of 1.
     */
    if (number < 1.0) {
        xsltNumberFormatDecimal(buffer, number, '0', 1,
                                data->digitsPerGroup,
                                data->groupingCharacter,
                                data->groupingCharacterLen);
        return;
    }

236 237 238 239
    /* Build buffer from back */
    pointer = &temp_string[sizeof(temp_string)];
    *(--pointer) = 0;
    alpha_list = (is_upper) ? alpha_upper_list : alpha_lower_list;
Daniel Veillard's avatar
Daniel Veillard committed
240

241
    for (i = 1; i < (int)sizeof(temp_string); i++) {
242 243 244
	number--;
	*(--pointer) = alpha_list[((int)fmod(number, alpha_size))];
	number /= alpha_size;
245
	if (number < 1.0)
246 247 248 249 250 251
	    break; /* for */
    }
    xmlBufferCCat(buffer, pointer);
}

static void
252 253
xsltNumberFormatRoman(xsltNumberDataPtr data,
		      xmlBufferPtr buffer,
254 255 256
		      double number,
		      int is_upper)
{
257 258 259 260 261 262 263 264 265 266 267 268
    /*
     * See discussion in xsltNumberFormatAlpha. Also use a reasonable upper
     * bound to avoid denial of service.
     */
    if (number < 1.0 || number > 5000.0) {
        xsltNumberFormatDecimal(buffer, number, '0', 1,
                                data->digitsPerGroup,
                                data->groupingCharacter,
                                data->groupingCharacterLen);
        return;
    }

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
    /*
     * Based on an example by Jim Walsh
     */
    while (number >= 1000.0) {
	xmlBufferCCat(buffer, (is_upper) ? "M" : "m");
	number -= 1000.0;
    }
    if (number >= 900.0) {
	xmlBufferCCat(buffer, (is_upper) ? "CM" : "cm");
	number -= 900.0;
    }
    while (number >= 500.0) {
	xmlBufferCCat(buffer, (is_upper) ? "D" : "d");
	number -= 500.0;
    }
    if (number >= 400.0) {
	xmlBufferCCat(buffer, (is_upper) ? "CD" : "cd");
	number -= 400.0;
    }
    while (number >= 100.0) {
	xmlBufferCCat(buffer, (is_upper) ? "C" : "c");
	number -= 100.0;
    }
    if (number >= 90.0) {
	xmlBufferCCat(buffer, (is_upper) ? "XC" : "xc");
	number -= 90.0;
    }
    while (number >= 50.0) {
	xmlBufferCCat(buffer, (is_upper) ? "L" : "l");
	number -= 50.0;
    }
    if (number >= 40.0) {
	xmlBufferCCat(buffer, (is_upper) ? "XL" : "xl");
	number -= 40.0;
    }
    while (number >= 10.0) {
	xmlBufferCCat(buffer, (is_upper) ? "X" : "x");
	number -= 10.0;
    }
    if (number >= 9.0) {
	xmlBufferCCat(buffer, (is_upper) ? "IX" : "ix");
	number -= 9.0;
    }
    while (number >= 5.0) {
	xmlBufferCCat(buffer, (is_upper) ? "V" : "v");
	number -= 5.0;
    }
    if (number >= 4.0) {
	xmlBufferCCat(buffer, (is_upper) ? "IV" : "iv");
	number -= 4.0;
    }
    while (number >= 1.0) {
	xmlBufferCCat(buffer, (is_upper) ? "I" : "i");
	number--;
    }
}

326
static void
327
xsltNumberFormatTokenize(const xmlChar *format,
328
			 xsltFormatPtr tokens)
329
{
330
    int ix = 0;
331
    int j;
332 333
    int val;
    int len;
334

335
    default_token.token = DEFAULT_TOKEN;
Bjorn Reese's avatar
Bjorn Reese committed
336
    default_token.width = 1;
337 338 339 340 341 342
    default_token.separator = BAD_CAST(DEFAULT_SEPARATOR);


    tokens->start = NULL;
    tokens->tokens[0].separator = NULL;
    tokens->end = NULL;
343 344

    /*
345 346
     * Insert initial non-alphanumeric token.
     * There is always such a token in the list, even if NULL
347
     */
348
    while (! (IS_LETTER(val=xmlStringCurrentChar(NULL, format+ix, &len)) ||
Daniel Veillard's avatar
Daniel Veillard committed
349
	      IS_DIGIT(val)) ) {
350
	if (format[ix] == 0)		/* if end of format string */
351
	    break; /* while */
352
	ix += len;
353
    }
354 355
    if (ix > 0)
	tokens->start = xmlStrndup(format, ix);
356

357

358 359
    for (tokens->nTokens = 0; tokens->nTokens < MAX_TOKENS;
	 tokens->nTokens++) {
360
	if (format[ix] == 0)
361
	    break; /* for */
362 363 364 365 366 367 368 369 370 371

	/*
	 * separator has already been parsed (except for the first
	 * number) in tokens->end, recover it.
	 */
	if (tokens->nTokens > 0) {
	    tokens->tokens[tokens->nTokens].separator = tokens->end;
	    tokens->end = NULL;
	}

372
	val = xmlStringCurrentChar(NULL, format+ix, &len);
373 374
	if (IS_DIGIT_ONE(val) ||
		 IS_DIGIT_ZERO(val)) {
375
	    tokens->tokens[tokens->nTokens].width = 1;
376
	    while (IS_DIGIT_ZERO(val)) {
377
		tokens->tokens[tokens->nTokens].width++;
378 379
		ix += len;
		val = xmlStringCurrentChar(NULL, format+ix, &len);
380
	    }
381 382
	    if (IS_DIGIT_ONE(val)) {
		tokens->tokens[tokens->nTokens].token = val - 1;
383 384
		ix += len;
		val = xmlStringCurrentChar(NULL, format+ix, &len);
385
	    }
386 387 388 389 390
	} else if ( (val == (xmlChar)'A') ||
		    (val == (xmlChar)'a') ||
		    (val == (xmlChar)'I') ||
		    (val == (xmlChar)'i') ) {
	    tokens->tokens[tokens->nTokens].token = val;
391 392
	    ix += len;
	    val = xmlStringCurrentChar(NULL, format+ix, &len);
393 394 395 396 397 398 399
	} else {
	    /* XSLT section 7.7
	     * "Any other format token indicates a numbering sequence
	     *  that starts with that token. If an implementation does
	     *  not support a numbering sequence that starts with that
	     *  token, it must use a format token of 1."
	     */
400 401
	    tokens->tokens[tokens->nTokens].token = (xmlChar)'0';
	    tokens->tokens[tokens->nTokens].width = 1;
402 403 404 405 406 407 408 409 410 411
	}
	/*
	 * Skip over remaining alphanumeric characters from the Nd
	 * (Number, decimal digit), Nl (Number, letter), No (Number,
	 * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt
	 * (Letters, titlecase), Lm (Letters, modifiers), and Lo
	 * (Letters, other (uncased)) Unicode categories. This happens
	 * to correspond to the Letter and Digit classes from XML (and
	 * one wonders why XSLT doesn't refer to these instead).
	 */
412
	while (IS_LETTER(val) || IS_DIGIT(val)) {
413 414
	    ix += len;
	    val = xmlStringCurrentChar(NULL, format+ix, &len);
415
	}
416

417
	/*
418
	 * Insert temporary non-alphanumeric final tooken.
419
	 */
420
	j = ix;
421 422
	while (! (IS_LETTER(val) || IS_DIGIT(val))) {
	    if (val == 0)
423
		break; /* while */
424 425
	    ix += len;
	    val = xmlStringCurrentChar(NULL, format+ix, &len);
426
	}
427 428
	if (ix > j)
	    tokens->end = xmlStrndup(&format[j], ix - j);
429 430 431 432 433 434 435
    }
}

static void
xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,
			      double *numbers,
			      int numbers_max,
436
			      xsltFormatPtr tokens,
437 438 439 440
			      xmlBufferPtr buffer)
{
    int i = 0;
    double number;
441
    xsltFormatTokenPtr token;
442 443 444 445

    /*
     * Handle initial non-alphanumeric token
     */
446 447
    if (tokens->start != NULL)
	 xmlBufferCat(buffer, tokens->start);
448

Bjorn Reese's avatar
Bjorn Reese committed
449
    for (i = 0; i < numbers_max; i++) {
450 451
	/* Insert number */
	number = numbers[(numbers_max - 1) - i];
452 453
        /* Round to nearest like XSLT 2.0 */
        number = floor(number + 0.5);
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
        /*
         * XSLT 1.0 isn't clear on how to handle negative numbers, but XSLT
         * 2.0 says:
         *
         *     It is a non-recoverable dynamic error if any undiscarded item
         *     in the atomized sequence supplied as the value of the value
         *     attribute of xsl:number cannot be converted to an integer, or
         *     if the resulting integer is less than 0 (zero).
         */
        if (number < 0.0) {
            xsltTransformError(NULL, NULL, NULL,
                    "xsl-number : negative value\n");
            /* Recover by treating negative values as zero. */
            number = 0.0;
        }
469
	if (i < tokens->nTokens) {
470 471 472 473
	  /*
	   * The "n"th format token will be used to format the "n"th
	   * number in the list
	   */
474 475
	  token = &(tokens->tokens[i]);
	} else if (tokens->nTokens > 0) {
476 477
	  /*
	   * If there are more numbers than format tokens, then the
478
	   * last format token will be used to format the remaining
479 480
	   * numbers.
	   */
481
	  token = &(tokens->tokens[tokens->nTokens - 1]);
Bjorn Reese's avatar
Bjorn Reese committed
482
	} else {
483 484 485 486
	  /*
	   * If there are no format tokens, then a format token of
	   * 1 is used to format all numbers.
	   */
Bjorn Reese's avatar
Bjorn Reese committed
487 488
	  token = &default_token;
	}
489 490 491 492 493 494 495 496 497

	/* Print separator, except for the first number */
	if (i > 0) {
	    if (token->separator != NULL)
		xmlBufferCat(buffer, token->separator);
	    else
		xmlBufferCCat(buffer, DEFAULT_SEPARATOR);
	}

498
	switch (xmlXPathIsInf(number)) {
499 500 501 502 503 504 505
	case -1:
	    xmlBufferCCat(buffer, "-Infinity");
	    break;
	case 1:
	    xmlBufferCCat(buffer, "Infinity");
	    break;
	default:
506
	    if (xmlXPathIsNaN(number)) {
507 508 509 510 511
		xmlBufferCCat(buffer, "NaN");
	    } else {

		switch (token->token) {
		case 'A':
512
		    xsltNumberFormatAlpha(data, buffer, number, TRUE);
513 514
		    break;
		case 'a':
515
		    xsltNumberFormatAlpha(data, buffer, number, FALSE);
516 517
		    break;
		case 'I':
518
		    xsltNumberFormatRoman(data, buffer, number, TRUE);
519 520
		    break;
		case 'i':
521
		    xsltNumberFormatRoman(data, buffer, number, FALSE);
522 523 524 525 526 527 528 529
		    break;
		default:
		    if (IS_DIGIT_ZERO(token->token)) {
			xsltNumberFormatDecimal(buffer,
						number,
						token->token,
						token->width,
						data->digitsPerGroup,
530 531
						data->groupingCharacter,
						data->groupingCharacterLen);
532 533 534 535 536 537 538
		    }
		    break;
		}
	    }

	}
    }
539 540 541 542 543 544 545

    /*
     * Handle final non-alphanumeric token
     */
    if (tokens->end != NULL)
	 xmlBufferCat(buffer, tokens->end);

546 547
}

548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
static int
xsltTestCompMatchCount(xsltTransformContextPtr context,
                       xmlNodePtr node,
                       xsltCompMatchPtr countPat,
                       xmlNodePtr cur)
{
    if (countPat != NULL) {
        return xsltTestCompMatchList(context, node, countPat);
    }
    else {
        /*
         * 7.7 Numbering
         *
         * If count attribute is not specified, then it defaults to the
         * pattern that matches any node with the same node type as the
         * current node and, if the current node has an expanded-name, with
         * the same expanded-name as the current node.
         */
        if (node->type != cur->type)
            return 0;
        if (node->type == XML_NAMESPACE_DECL)
            /*
             * Namespace nodes have no preceding siblings and no parents
             * that are namespace nodes. This means that node == cur.
             */
            return 1;
        /* TODO: Skip node types without expanded names like text nodes. */
        if (!xmlStrEqual(node->name, cur->name))
            return 0;
        if (node->ns == cur->ns)
            return 1;
        if ((node->ns == NULL) || (cur->ns == NULL))
            return 0;
        return (xmlStrEqual(node->ns->href, cur->ns->href));
    }
}

585
static int
Bjorn Reese's avatar
Bjorn Reese committed
586 587
xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context,
			    xmlNodePtr node,
588 589
			    xsltCompMatchPtr countPat,
			    xsltCompMatchPtr fromPat,
Nick Wellnhofer's avatar
Nick Wellnhofer committed
590
			    double *array)
591
{
Bjorn Reese's avatar
Bjorn Reese committed
592 593
    int amount = 0;
    int cnt = 0;
594
    xmlNodePtr cur = node;
595 596 597

    while (cur != NULL) {
	/* process current node */
598 599
	if (xsltTestCompMatchCount(context, cur, countPat, node))
	    cnt++;
600
	if ((fromPat != NULL) &&
601 602
	    xsltTestCompMatchList(context, cur, fromPat)) {
	    break; /* while */
Bjorn Reese's avatar
Bjorn Reese committed
603 604
	}

605 606 607 608 609 610 611 612
	/* Skip to next preceding or ancestor */
	if ((cur->type == XML_DOCUMENT_NODE) ||
#ifdef LIBXML_DOCB_ENABLED
            (cur->type == XML_DOCB_DOCUMENT_NODE) ||
#endif
            (cur->type == XML_HTML_DOCUMENT_NODE))
	    break; /* while */

613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
        if (cur->type == XML_NAMESPACE_DECL) {
            /*
            * The XPath module stores the parent of a namespace node in
            * the ns->next field.
            */
            cur = (xmlNodePtr) ((xmlNsPtr) cur)->next;
        } else if (cur->type == XML_ATTRIBUTE_NODE) {
            cur = cur->parent;
        } else {
            while ((cur->prev != NULL) && ((cur->prev->type == XML_DTD_NODE) ||
                   (cur->prev->type == XML_XINCLUDE_START) ||
                   (cur->prev->type == XML_XINCLUDE_END)))
                cur = cur->prev;
            if (cur->prev != NULL) {
                for (cur = cur->prev; cur->last != NULL; cur = cur->last);
            } else {
                cur = cur->parent;
            }
        }
Bjorn Reese's avatar
Bjorn Reese committed
632
    }
633 634 635

    array[amount++] = (double) cnt;

636
    return(amount);
637 638 639
}

static int
Bjorn Reese's avatar
Bjorn Reese committed
640
xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
641
				 xmlNodePtr node,
642 643
				 xsltCompMatchPtr countPat,
				 xsltCompMatchPtr fromPat,
644
				 double *array,
Nick Wellnhofer's avatar
Nick Wellnhofer committed
645
				 int max)
646 647 648
{
    int amount = 0;
    int cnt;
649
    xmlNodePtr oldCtxtNode;
650 651 652
    xmlNodePtr ancestor;
    xmlNodePtr preceding;
    xmlXPathParserContextPtr parser;
653

654
    oldCtxtNode = context->xpathCtxt->node;
Bjorn Reese's avatar
Bjorn Reese committed
655
    parser = xmlXPathNewParserContext(NULL, context->xpathCtxt);
656 657
    if (parser) {
	/* ancestor-or-self::*[count] */
658 659
	ancestor = node;
	while ((ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE)) {
660
	    if ((fromPat != NULL) &&
661
		xsltTestCompMatchList(context, ancestor, fromPat))
662
		break; /* for */
Daniel Veillard's avatar
Daniel Veillard committed
663

664 665 666 667 668 669 670
            /*
             * The xmlXPathNext* iterators require that the context node is
             * set to the start node. Calls to xsltTestCompMatch* may also
             * leave the context node in an undefined state, so make sure
             * that the context node is reset before each iterator invocation.
             */

671
	    if (xsltTestCompMatchCount(context, ancestor, countPat, node)) {
672
		/* count(preceding-sibling::*) */
673
		cnt = 1;
674 675 676
                context->xpathCtxt->node = ancestor;
                preceding = xmlXPathNextPrecedingSibling(parser, ancestor);
                while (preceding != NULL) {
677 678 679
	            if (xsltTestCompMatchCount(context, preceding, countPat,
                                               node))
			cnt++;
680 681 682
                    context->xpathCtxt->node = ancestor;
                    preceding =
                        xmlXPathNextPrecedingSibling(parser, preceding);
683 684
		}
		array[amount++] = (double)cnt;
Bjorn Reese's avatar
Bjorn Reese committed
685 686
		if (amount >= max)
		    break; /* for */
687
	    }
688 689
            context->xpathCtxt->node = node;
            ancestor = xmlXPathNextAncestor(parser, ancestor);
690 691 692
	}
	xmlXPathFreeParserContext(parser);
    }
693
    context->xpathCtxt->node = oldCtxtNode;
694 695 696 697 698 699
    return amount;
}

static int
xsltNumberFormatGetValue(xmlXPathContextPtr context,
			 xmlNodePtr node,
700
			 const xmlChar *value,
701 702 703 704 705
			 double *number)
{
    int amount = 0;
    xmlBufferPtr pattern;
    xmlXPathObjectPtr obj;
Daniel Veillard's avatar
Daniel Veillard committed
706

707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
    pattern = xmlBufferCreate();
    if (pattern != NULL) {
	xmlBufferCCat(pattern, "number(");
	xmlBufferCat(pattern, value);
	xmlBufferCCat(pattern, ")");
	context->node = node;
	obj = xmlXPathEvalExpression(xmlBufferContent(pattern),
				     context);
	if (obj != NULL) {
	    *number = obj->floatval;
	    amount++;
	    xmlXPathFreeObject(obj);
	}
	xmlBufferFree(pattern);
    }
    return amount;
}

725 726 727
/**
 * xsltNumberFormat:
 * @ctxt: the XSLT transformation context
Nick Wellnhofer's avatar
Nick Wellnhofer committed
728
 * @data: the formatting information
729 730 731 732
 * @node: the data to format
 *
 * Convert one number.
 */
733 734 735 736 737 738
void
xsltNumberFormat(xsltTransformContextPtr ctxt,
		 xsltNumberDataPtr data,
		 xmlNodePtr node)
{
    xmlBufferPtr output = NULL;
739
    int amount, i;
740
    double number;
741
    xsltFormat tokens;
742

743 744 745 746 747 748 749 750 751 752
    if (data->format != NULL) {
        xsltNumberFormatTokenize(data->format, &tokens);
    }
    else {
        xmlChar *format;

	/* The format needs to be recomputed each time */
        if (data->has_format == 0)
            return;
	format = xsltEvalAttrValueTemplate(ctxt, data->node,
753 754
					     (const xmlChar *) "format",
					     XSLT_NAMESPACE);
755 756 757 758
        if (format == NULL)
            return;
        xsltNumberFormatTokenize(format, &tokens);
	xmlFree(format);
759 760
    }

761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
    output = xmlBufferCreate();
    if (output == NULL)
	goto XSLT_NUMBER_FORMAT_END;

    /*
     * Evaluate the XPath expression to find the value(s)
     */
    if (data->value) {
	amount = xsltNumberFormatGetValue(ctxt->xpathCtxt,
					  node,
					  data->value,
					  &number);
	if (amount == 1) {
	    xsltNumberFormatInsertNumbers(data,
					  &number,
					  1,
777
					  &tokens,
778 779
					  output);
	}
Daniel Veillard's avatar
Daniel Veillard committed
780

781
    } else if (data->level) {
Daniel Veillard's avatar
Daniel Veillard committed
782

783
	if (xmlStrEqual(data->level, (const xmlChar *) "single")) {
Bjorn Reese's avatar
Bjorn Reese committed
784
	    amount = xsltNumberFormatGetMultipleLevel(ctxt,
785
						      node,
786 787
						      data->countPat,
						      data->fromPat,
788
						      &number,
Nick Wellnhofer's avatar
Nick Wellnhofer committed
789
						      1);
790 791 792 793
	    if (amount == 1) {
		xsltNumberFormatInsertNumbers(data,
					      &number,
					      1,
794
					      &tokens,
795 796
					      output);
	    }
797
	} else if (xmlStrEqual(data->level, (const xmlChar *) "multiple")) {
798 799
	    double numarray[1024];
	    int max = sizeof(numarray)/sizeof(numarray[0]);
Bjorn Reese's avatar
Bjorn Reese committed
800
	    amount = xsltNumberFormatGetMultipleLevel(ctxt,
801
						      node,
802 803
						      data->countPat,
						      data->fromPat,
804
						      numarray,
Nick Wellnhofer's avatar
Nick Wellnhofer committed
805
						      max);
806 807 808 809
	    if (amount > 0) {
		xsltNumberFormatInsertNumbers(data,
					      numarray,
					      amount,
810
					      &tokens,
811 812
					      output);
	    }
813
	} else if (xmlStrEqual(data->level, (const xmlChar *) "any")) {
Bjorn Reese's avatar
Bjorn Reese committed
814 815
	    amount = xsltNumberFormatGetAnyLevel(ctxt,
						 node,
816 817
						 data->countPat,
						 data->fromPat,
Nick Wellnhofer's avatar
Nick Wellnhofer committed
818
						 &number);
Bjorn Reese's avatar
Bjorn Reese committed
819 820
	    if (amount > 0) {
		xsltNumberFormatInsertNumbers(data,
Bjorn Reese's avatar
Bjorn Reese committed
821 822
					      &number,
					      1,
823
					      &tokens,
Bjorn Reese's avatar
Bjorn Reese committed
824 825
					      output);
	    }
826 827 828
	}
    }
    /* Insert number as text node */
829
    xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0);
830

831 832 833
    xmlBufferFree(output);

XSLT_NUMBER_FORMAT_END:
834 835 836 837 838 839 840
    if (tokens.start != NULL)
	xmlFree(tokens.start);
    if (tokens.end != NULL)
	xmlFree(tokens.end);
    for (i = 0;i < tokens.nTokens;i++) {
	if (tokens.tokens[i].separator != NULL)
	    xmlFree(tokens.tokens[i].separator);
841
    }
842 843
}

844 845 846 847
static int
xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltFormatNumberInfoPtr info)
{
    int	count=0;	/* will hold total length of prefix/suffix */
848
    int len;
849 850

    while (1) {
Daniel Veillard's avatar
Daniel Veillard committed
851 852 853
	/*
	 * prefix / suffix ends at end of string or at
	 * first 'special' character
854
	 */
855 856 857 858 859 860 861
	if (**format == 0)
	    return count;
	/* if next character 'escaped' just count it */
	if (**format == SYMBOL_QUOTE) {
	    if (*++(*format) == 0)
		return -1;
	}
862
	else if (IS_SPECIAL(self, *format))
863
	    return count;
864 865
	/*
	 * else treat percent/per-mille as special cases,
Daniel Veillard's avatar
Daniel Veillard committed
866
	 * depending on whether +ve or -ve
867
	 */
868
	else {
869
	    /*
Daniel Veillard's avatar
Daniel Veillard committed
870 871
	     * for +ve prefix/suffix, allow only a
	     * single occurence of either
872 873
	     */
	    if (xsltUTF8Charcmp(*format, self->percent) == 0) {
874 875 876 877
		if (info->is_multiplier_set)
		    return -1;
		info->multiplier = 100;
		info->is_multiplier_set = TRUE;
878
	    } else if (xsltUTF8Charcmp(*format, self->permille) == 0) {
879 880 881 882 883 884
		if (info->is_multiplier_set)
		    return -1;
		info->multiplier = 1000;
		info->is_multiplier_set = TRUE;
	    }
	}
Daniel Veillard's avatar
Daniel Veillard committed
885

886
	if ((len=xmlUTF8Strsize(*format, 1)) < 1)
887 888 889
	    return -1;
	count += len;
	*format += len;
890 891
    }
}
Daniel Veillard's avatar
Daniel Veillard committed
892

893 894 895 896 897
/**
 * xsltFormatNumberConversion:
 * @self: the decimal format
 * @format: the format requested
 * @number: the value to format
Nick Wellnhofer's avatar
Nick Wellnhofer committed
898
 * @result: the place to output the result
899 900
 *
 * format-number() uses the JDK 1.1 DecimalFormat class:
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
 *
 * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
 *
 * Structure:
 *
 *   pattern    := subpattern{;subpattern}
 *   subpattern := {prefix}integer{.fraction}{suffix}
 *   prefix     := '\\u0000'..'\\uFFFD' - specialCharacters
 *   suffix     := '\\u0000'..'\\uFFFD' - specialCharacters
 *   integer    := '#'* '0'* '0'
 *   fraction   := '0'* '#'*
 *
 *   Notation:
 *    X*       0 or more instances of X
 *    (X | Y)  either X or Y.
 *    X..Y     any character from X up to Y, inclusive.
 *    S - T    characters in S, except those in T
 *
 * Special Characters:
 *
 *   Symbol Meaning
 *   0      a digit
 *   #      a digit, zero shows as absent
 *   .      placeholder for decimal separator
 *   ,      placeholder for grouping separator.
 *   ;      separates formats.
 *   -      default negative prefix.
 *   %      multiply by 100 and show as percentage
 *   ?      multiply by 1000 and show as per mille
 *   X      any other characters can be used in the prefix or suffix
 *   '      used to quote special characters in a prefix or suffix.
932 933
 *
 * Returns a possible XPath error
934 935 936 937 938 939 940 941 942
 */
xmlXPathError
xsltFormatNumberConversion(xsltDecimalFormatPtr self,
			   xmlChar *format,
			   double number,
			   xmlChar **result)
{
    xmlXPathError status = XPATH_EXPRESSION_OK;
    xmlBufferPtr buffer;
943 944
    xmlChar *the_format, *prefix = NULL, *suffix = NULL;
    xmlChar *nprefix, *nsuffix = NULL;
945
    xmlChar pchar;
946
    int	    prefix_length, suffix_length = 0, nprefix_length, nsuffix_length;
947
    double  scale;
948
    int	    j, len;
949
    int     self_grouping_len;
950
    xsltFormatNumberInfo format_info;
Daniel Veillard's avatar
Daniel Veillard committed
951
    /*
952
     * delayed_multiplier allows a 'trailing' percent or
Daniel Veillard's avatar
Daniel Veillard committed
953
     * permille to be treated as suffix
954
     */
955 956 957 958 959
    int		delayed_multiplier = 0;
    /* flag to show no -ve format present for -ve number */
    char	default_sign = 0;
    /* flag to show error found, should use default format */
    char	found_error = 0;
960

961 962 963 964 965
    if (xmlStrlen(format) <= 0) {
	xsltTransformError(NULL, NULL, NULL,
                "xsltFormatNumberConversion : "
		"Invalid format (0-length)\n");
    }
966
    *result = NULL;
967 968
    switch (xmlXPathIsInf(number)) {
	case -1:
969 970 971 972 973
	    if (self->minusSign == NULL)
		*result = xmlStrdup(BAD_CAST "-");
	    else
		*result = xmlStrdup(self->minusSign);
	    /* no-break on purpose */
974
	case 1:
975 976 977 978
	    if ((self == NULL) || (self->infinity == NULL))
		*result = xmlStrcat(*result, BAD_CAST "Infinity");
	    else
		*result = xmlStrcat(*result, self->infinity);
979 980 981
	    return(status);
	default:
	    if (xmlXPathIsNaN(number)) {
982 983 984 985
		if ((self == NULL) || (self->noNumber == NULL))
		    *result = xmlStrdup(BAD_CAST "NaN");
		else
		    *result = xmlStrdup(self->noNumber);
986 987 988 989
		return(status);
	    }
    }

990 991 992 993 994
    buffer = xmlBufferCreate();
    if (buffer == NULL) {
	return XPATH_MEMORY_ERROR;
    }

995
    format_info.integer_hash = 0;
996 997 998 999 1000
    format_info.integer_digits = 0;
    format_info.frac_digits = 0;
    format_info.frac_hash = 0;
    format_info.group = -1;
    format_info.multiplier = 1;
1001
    format_info.add_decimal = FALSE;
1002 1003 1004 1005 1006
    format_info.is_multiplier_set = FALSE;
    format_info.is_negative_pattern = FALSE;

    the_format = format;

1007 1008
    /*
     * First we process the +ve pattern to get percent / permille,
Daniel Veillard's avatar
Daniel Veillard committed
1009
     * as well as main format
1010
     */
1011 1012 1013 1014 1015
    prefix = the_format;
    prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
    if (prefix_length < 0) {
	found_error = 1;
	goto OUTPUT_NUMBER;
1016 1017
    }

Daniel Veillard's avatar
Daniel Veillard committed
1018 1019
    /*
     * Here we process the "number" part of the format.  It gets
1020
     * a little messy because of the percent/per-mille - if that
Daniel Veillard's avatar
Daniel Veillard committed
1021 1022 1023
     * appears at the end, it may be part of the suffix instead
     * of part of the number, so the variable delayed_multiplier
     * is used to handle it
1024
     */
1025
    self_grouping_len = xmlStrlen(self->grouping);
1026
    while ((*the_format != 0) &&
1027 1028
	   (xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) &&
	   (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) {
Daniel Veillard's avatar
Daniel Veillard committed
1029

1030 1031 1032 1033
	if (delayed_multiplier != 0) {
	    format_info.multiplier = delayed_multiplier;
	    format_info.is_multiplier_set = TRUE;
	    delayed_multiplier = 0;
1034
	}
1035
	if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1036 1037 1038 1039
	    if (format_info.integer_digits > 0) {
		found_error = 1;
		goto OUTPUT_NUMBER;
	    }
1040
	    format_info.integer_hash++;
1041 1042
	    if (format_info.group >= 0)
		format_info.group++;
1043
	} else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1044 1045 1046
	    format_info.integer_digits++;
	    if (format_info.group >= 0)
		format_info.group++;
1047 1048
	} else if ((self_grouping_len > 0) &&
	    (!xmlStrncmp(the_format, self->grouping, self_grouping_len))) {
1049 1050
	    /* Reset group count */
	    format_info.group = 0;
1051 1052
	    the_format += self_grouping_len;
	    continue;
1053
	} else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1054 1055 1056 1057 1058
	    if (format_info.is_multiplier_set) {
		found_error = 1;
		goto OUTPUT_NUMBER;
	    }
	    delayed_multiplier = 100;
1059
	} else  if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1060 1061 1062 1063 1064 1065 1066
	    if (format_info.is_multiplier_set) {
		found_error = 1;
		goto OUTPUT_NUMBER;
	    }
	    delayed_multiplier = 1000;
	} else
	    break; /* while */
Daniel Veillard's avatar
Daniel Veillard committed
1067

1068
	if ((len=xmlUTF8Strsize(the_format, 1)) < 1) {
1069 1070 1071 1072 1073
	    found_error = 1;
	    goto OUTPUT_NUMBER;
	}
	the_format += len;

1074 1075 1076
    }

    /* We have finished the integer part, now work on fraction */
1077 1078
    if ( (*the_format != 0) &&
         (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) ) {
1079
        format_info.add_decimal = TRUE;
1080 1081 1082 1083 1084
        if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
            found_error = 1;
            goto OUTPUT_NUMBER;
        }
	the_format += len;	/* Skip over the decimal */
1085
    }
Daniel Veillard's avatar
Daniel Veillard committed
1086

1087
    while (*the_format != 0) {
Daniel Veillard's avatar
Daniel Veillard committed
1088

1089
	if (xsltUTF8C