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