#define IN_LIBXSLT
#include "libxslt.h"
#include <math.h>
#include <limits.h>
#include <float.h>
#include <string.h>
#include <libxml/xmlmemory.h>
#include <libxml/parserInternals.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/encoding.h>
#include "xsltutils.h"
#include "pattern.h"
#include "templates.h"
#include "transform.h"
#include "numbersInternals.h"
#ifndef FALSE
# define FALSE (0 == 1)
# define TRUE (1 == 1)
#endif
#define SYMBOL_QUOTE ((xmlChar)'\'')
#define DEFAULT_TOKEN '0'
#define DEFAULT_SEPARATOR "."
#define MAX_TOKENS 1024
typedef struct _xsltFormatToken xsltFormatToken;
typedef xsltFormatToken *xsltFormatTokenPtr;
struct _xsltFormatToken {
xmlChar *separator;
int token;
int width;
};
typedef struct _xsltFormat xsltFormat;
typedef xsltFormat *xsltFormatPtr;
struct _xsltFormat {
xmlChar *start;
xsltFormatToken tokens[MAX_TOKENS];
int nTokens;
xmlChar *end;
};
static const char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz";
static const xsltFormatToken default_token = {
BAD_CAST(DEFAULT_SEPARATOR),
DEFAULT_TOKEN,
1
};
static int
xsltCopyCharMultiByte(xmlChar *out, int val) {
if ((out == NULL) || (val < 0)) return(0);
if (val >= 0x80) {
xmlChar *savedout = out;
int bits;
if (val < 0x800) { *out++= (val >> 6) | 0xC0; bits= 0; }
else if (val < 0x10000) { *out++= (val >> 12) | 0xE0; bits= 6;}
else if (val < 0x110000) { *out++= (val >> 18) | 0xF0; bits= 12; }
else {
return(0);
}
for ( ; bits >= 0; bits-= 6)
*out++= ((val >> bits) & 0x3F) | 0x80 ;
return (out - savedout);
}
*out = val;
return 1;
}
static int
xsltUTF8Charcmp(xmlChar *utf1, xmlChar *utf2) {
int len = xmlUTF8Strsize(utf1, 1);
if (len < 1)
return -1;
if (utf1 == NULL ) {
if (utf2 == NULL)
return 0;
return -1;
}
return xmlStrncmp(utf1, utf2, len);
}
static int
xsltIsLetterDigit(int val) {
return xmlIsBaseCharQ(val) || xmlIsIdeographicQ(val) ||
xmlIsDigitQ(val);
}
#define IS_SPECIAL(self,letter) \
((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))
#define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
#define IS_DIGIT_ONE(x) xsltIsDigitZero((x)-1)
static int
xsltIsDigitZero(int ch)
{
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 0x0ED0: case 0x0F20:
return TRUE;
default:
return FALSE;
}
}
static void
xsltNumberFormatDecimal(xmlBufferPtr buffer,
double number,
int digit_zero,
int width,
int digitsPerGroup,
int groupingCharacter,
int groupingCharacterLen)
{
xmlChar temp_string[500];
xmlChar *pointer;
xmlChar temp_char[6];
int i;
int val;
int len;
pointer = &temp_string[sizeof(temp_string)] - 1;
*pointer = 0;
i = 0;
while (pointer > temp_string) {
if ((i >= width) && (fabs(number) < 1.0))
break;
if ((i > 0) && (groupingCharacter != 0) &&
(digitsPerGroup > 0) &&
((i % digitsPerGroup) == 0)) {
if (pointer - groupingCharacterLen < temp_string) {
i = -1;
break;
}
pointer -= groupingCharacterLen;
xsltCopyCharMultiByte(pointer, groupingCharacter);
}
val = digit_zero + (int)fmod(number, 10.0);
if (val < 0x80) {
if (pointer <= temp_string) {
i = -1;
break;
}
*(--pointer) = val;
}
else {
len = xsltCopyCharMultiByte(temp_char, val);
if ( (pointer - len) < temp_string ) {
i = -1;
break;
}
pointer -= len;
memcpy(pointer, temp_char, len);
}
number /= 10.0;
++i;
}
if (i < 0)
xsltGenericError(xsltGenericErrorContext,
"xsltNumberFormatDecimal: Internal buffer size exceeded\n");
xmlBufferCat(buffer, pointer);
}
static void
xsltNumberFormatAlpha(xsltNumberDataPtr data,
xmlBufferPtr buffer,
double number,
int is_upper)
{
char temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1];
char *pointer;
int i;
const char *alpha_list;
double alpha_size = (double)(sizeof(alpha_upper_list) - 1);
if (number < 1.0) {
xsltNumberFormatDecimal(buffer, number, '0', 1,
data->digitsPerGroup,
data->groupingCharacter,
data->groupingCharacterLen);
return;
}
pointer = &temp_string[sizeof(temp_string)];
*(--pointer) = 0;
alpha_list = (is_upper) ? alpha_upper_list : alpha_lower_list;
for (i = 1; i < (int)sizeof(temp_string); i++) {
number--;
*(--pointer) = alpha_list[((int)fmod(number, alpha_size))];
number /= alpha_size;
if (number < 1.0)
break;
}
xmlBufferCCat(buffer, pointer);
}
static void
xsltNumberFormatRoman(xsltNumberDataPtr data,
xmlBufferPtr buffer,
double number,
int is_upper)
{
if (number < 1.0 || number > 5000.0) {
xsltNumberFormatDecimal(buffer, number, '0', 1,
data->digitsPerGroup,
data->groupingCharacter,
data->groupingCharacterLen);
return;
}
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--;
}
}
static void
xsltNumberFormatTokenize(const xmlChar *format,
xsltFormatPtr tokens)
{
int ix = 0;
int j;
int val;
int len;
tokens->start = NULL;
tokens->tokens[0].separator = NULL;
tokens->end = NULL;
while (!xsltIsLetterDigit(val = xsltGetUTF8CharZ(format+ix, &len))) {
if (format[ix] == 0)
break;
ix += len;
}
if (ix > 0)
tokens->start = xmlStrndup(format, ix);
for (tokens->nTokens = 0; tokens->nTokens < MAX_TOKENS;
tokens->nTokens++) {
if (format[ix] == 0)
break;
if (tokens->nTokens > 0) {
tokens->tokens[tokens->nTokens].separator = tokens->end;
tokens->end = NULL;
}
val = xsltGetUTF8CharZ(format+ix, &len);
if (IS_DIGIT_ONE(val) ||
IS_DIGIT_ZERO(val)) {
tokens->tokens[tokens->nTokens].width = 1;
while (IS_DIGIT_ZERO(val)) {
tokens->tokens[tokens->nTokens].width++;
ix += len;
val = xsltGetUTF8CharZ(format+ix, &len);
}
if (IS_DIGIT_ONE(val)) {
tokens->tokens[tokens->nTokens].token = val - 1;
ix += len;
val = xsltGetUTF8CharZ(format+ix, &len);
} else {
tokens->tokens[tokens->nTokens].token = '0';
tokens->tokens[tokens->nTokens].width = 1;
}
} else if ( (val == 'A') ||
(val == 'a') ||
(val == 'I') ||
(val == 'i') ) {
tokens->tokens[tokens->nTokens].token = val;
ix += len;
val = xsltGetUTF8CharZ(format+ix, &len);
} else {
tokens->tokens[tokens->nTokens].token = '0';
tokens->tokens[tokens->nTokens].width = 1;
}
while (xsltIsLetterDigit(val)) {
ix += len;
val = xsltGetUTF8CharZ(format+ix, &len);
}
j = ix;
while (!xsltIsLetterDigit(val)) {
if (val == 0)
break;
ix += len;
val = xsltGetUTF8CharZ(format+ix, &len);
}
if (ix > j)
tokens->end = xmlStrndup(&format[j], ix - j);
}
}
static void
xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,
double *numbers,
int numbers_max,
xsltFormatPtr tokens,
xmlBufferPtr buffer)
{
int i = 0;
double number;
const xsltFormatToken *token;
if (tokens->start != NULL)
xmlBufferCat(buffer, tokens->start);
for (i = 0; i < numbers_max; i++) {
number = numbers[(numbers_max - 1) - i];
number = floor(number + 0.5);
if (number < 0.0) {
xsltTransformError(NULL, NULL, NULL,
"xsl-number : negative value\n");
number = 0.0;
}
if (i < tokens->nTokens) {
token = &(tokens->tokens[i]);
} else if (tokens->nTokens > 0) {
token = &(tokens->tokens[tokens->nTokens - 1]);
} else {
token = &default_token;
}
if (i > 0) {
if (token->separator != NULL)
xmlBufferCat(buffer, token->separator);
else
xmlBufferCCat(buffer, DEFAULT_SEPARATOR);
}
switch (xmlXPathIsInf(number)) {
case -1:
xmlBufferCCat(buffer, "-Infinity");
break;
case 1:
xmlBufferCCat(buffer, "Infinity");
break;
default:
if (xmlXPathIsNaN(number)) {
xmlBufferCCat(buffer, "NaN");
} else {
switch (token->token) {
case 'A':
xsltNumberFormatAlpha(data, buffer, number, TRUE);
break;
case 'a':
xsltNumberFormatAlpha(data, buffer, number, FALSE);
break;
case 'I':
xsltNumberFormatRoman(data, buffer, number, TRUE);
break;
case 'i':
xsltNumberFormatRoman(data, buffer, number, FALSE);
break;
default:
if (IS_DIGIT_ZERO(token->token)) {
xsltNumberFormatDecimal(buffer,
number,
token->token,
token->width,
data->digitsPerGroup,
data->groupingCharacter,
data->groupingCharacterLen);
}
break;
}
}
}
}
if (tokens->end != NULL)
xmlBufferCat(buffer, tokens->end);
}
static int
xsltTestCompMatchCount(xsltTransformContextPtr context,
xmlNodePtr node,
xsltCompMatchPtr countPat,
xmlNodePtr cur)
{
if (countPat != NULL) {
return xsltTestCompMatchList(context, node, countPat);
}
else {
if (node->type != cur->type)
return 0;
if (node->type == XML_NAMESPACE_DECL)
return 1;
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));
}
}
static int
xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context,
xmlNodePtr node,
xsltCompMatchPtr countPat,
xsltCompMatchPtr fromPat,
double *array)
{
int amount = 0;
int cnt = 0;
xmlNodePtr cur = node;
while (cur != NULL) {
if (xsltTestCompMatchCount(context, cur, countPat, node))
cnt++;
if ((fromPat != NULL) &&
xsltTestCompMatchList(context, cur, fromPat)) {
break;
}
if ((cur->type == XML_DOCUMENT_NODE) ||
#ifdef LIBXML_DOCB_ENABLED
(cur->type == XML_DOCB_DOCUMENT_NODE) ||
#endif
(cur->type == XML_HTML_DOCUMENT_NODE))
break;
if (cur->type == XML_NAMESPACE_DECL) {
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;
}
}
}
array[amount++] = (double) cnt;
return(amount);
}
static int
xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
xmlNodePtr node,
xsltCompMatchPtr countPat,
xsltCompMatchPtr fromPat,
double *array,
int max)
{
int amount = 0;
int cnt;
xmlNodePtr ancestor;
xmlNodePtr preceding;
ancestor = node;
while ((ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE)) {
if ((fromPat != NULL) &&
xsltTestCompMatchList(context, ancestor, fromPat))
break;
if (xsltTestCompMatchCount(context, ancestor, countPat, node)) {
cnt = 1;
if (ancestor->type != XML_NAMESPACE_DECL)
preceding = ancestor->prev;
else
preceding = NULL;
while (preceding != NULL) {
if (xsltTestCompMatchCount(context, preceding, countPat,
node))
cnt++;
preceding = preceding->prev;
}
array[amount++] = (double)cnt;
if (amount >= max)
break;
}
if ((ancestor != NULL) && (ancestor->type == XML_NAMESPACE_DECL)) {
xmlNsPtr ns = (xmlNsPtr) ancestor;
if ((ns->next != NULL) &&
(ns->next->type != XML_NAMESPACE_DECL))
ancestor = (xmlNodePtr) ns->next;
else
ancestor = NULL;
} else {
ancestor = ancestor->parent;
}
}
return amount;
}
static int
xsltNumberFormatGetValue(xmlXPathContextPtr context,
xmlNodePtr node,
const xmlChar *value,
double *number)
{
int amount = 0;
xmlBufferPtr pattern;
xmlXPathObjectPtr obj;
xmlNodePtr oldNode;
pattern = xmlBufferCreate();
if (pattern != NULL) {
oldNode = context->node;
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);
context->node = oldNode;
}
return amount;
}
void
xsltNumberFormat(xsltTransformContextPtr ctxt,
xsltNumberDataPtr data,
xmlNodePtr node)
{
xmlBufferPtr output = NULL;
int amount, i;
double number;
xsltFormat tokens;
if (data->format != NULL) {
xsltNumberFormatTokenize(data->format, &tokens);
}
else {
xmlChar *format;
if (data->has_format == 0)
return;
format = xsltEvalAttrValueTemplate(ctxt, data->node,
(const xmlChar *) "format",
XSLT_NAMESPACE);
if (format == NULL)
return;
xsltNumberFormatTokenize(format, &tokens);
xmlFree(format);
}
output = xmlBufferCreate();
if (output == NULL)
goto XSLT_NUMBER_FORMAT_END;
if (data->value) {
amount = xsltNumberFormatGetValue(ctxt->xpathCtxt,
node,
data->value,
&number);
if (amount == 1) {
xsltNumberFormatInsertNumbers(data,
&number,
1,
&tokens,
output);
}
} else if (data->level) {
if (xmlStrEqual(data->level, (const xmlChar *) "single")) {
amount = xsltNumberFormatGetMultipleLevel(ctxt,
node,
data->countPat,
data->fromPat,
&number,
1);
if (amount == 1) {
xsltNumberFormatInsertNumbers(data,
&number,
1,
&tokens,
output);
}
} else if (xmlStrEqual(data->level, (const xmlChar *) "multiple")) {
double numarray[1024];
int max = sizeof(numarray)/sizeof(numarray[0]);
amount = xsltNumberFormatGetMultipleLevel(ctxt,
node,
data->countPat,
data->fromPat,
numarray,
max);
if (amount > 0) {
xsltNumberFormatInsertNumbers(data,
numarray,
amount,
&tokens,
output);
}
} else if (xmlStrEqual(data->level, (const xmlChar *) "any")) {
amount = xsltNumberFormatGetAnyLevel(ctxt,
node,
data->countPat,
data->fromPat,
&number);
if (amount > 0) {
xsltNumberFormatInsertNumbers(data,
&number,
1,
&tokens,
output);
}
}
if (data->countPat != NULL)
xsltCompMatchClearCache(ctxt, data->countPat);
if (data->fromPat != NULL)
xsltCompMatchClearCache(ctxt, data->fromPat);
}
xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0);
xmlBufferFree(output);
XSLT_NUMBER_FORMAT_END:
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);
}
}
static int
xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltFormatNumberInfoPtr info)
{
int count=0;
int len;
while (1) {
if (**format == 0)
return count;
if (**format == SYMBOL_QUOTE) {
if (*++(*format) == 0)
return -1;
}
else if (IS_SPECIAL(self, *format))
return count;
else {
if (xsltUTF8Charcmp(*format, self->percent) == 0) {
if (info->is_multiplier_set)
return -1;
info->multiplier = 100;
info->is_multiplier_set = TRUE;
} else if (xsltUTF8Charcmp(*format, self->permille) == 0) {
if (info->is_multiplier_set)
return -1;
info->multiplier = 1000;
info->is_multiplier_set = TRUE;
}
}
if ((len=xmlUTF8Strsize(*format, 1)) < 1)
return -1;
count += len;
*format += len;
}
}
xmlXPathError
xsltFormatNumberConversion(xsltDecimalFormatPtr self,
xmlChar *format,
double number,
xmlChar **result)
{
xmlXPathError status = XPATH_EXPRESSION_OK;
xmlBufferPtr buffer;
xmlChar *the_format, *prefix = NULL, *suffix = NULL;
xmlChar *nprefix, *nsuffix = NULL;
int prefix_length, suffix_length = 0, nprefix_length, nsuffix_length;
int exp10;
double scale;
int j, len = 0;
int self_grouping_len;
xsltFormatNumberInfo format_info;
int delayed_multiplier = 0;
char default_sign = 0;
char found_error = 0;
if (xmlStrlen(format) <= 0) {
xsltTransformError(NULL, NULL, NULL,
"xsltFormatNumberConversion : "
"Invalid format (0-length)\n");
}
*result = NULL;
if (xmlXPathIsNaN(number)) {
if ((self == NULL) || (self->noNumber == NULL))
*result = xmlStrdup(BAD_CAST "NaN");
else
*result = xmlStrdup(self->noNumber);
return(status);
}
format_info.integer_hash = 0;
format_info.integer_digits = 0;
format_info.frac_digits = 0;
format_info.frac_hash = 0;
format_info.group = -1;
format_info.multiplier = 1;
format_info.add_decimal = FALSE;
format_info.is_multiplier_set = FALSE;
format_info.is_negative_pattern = FALSE;
the_format = format;
prefix = the_format;
prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
if (prefix_length < 0) {
found_error = 1;
goto OUTPUT_NUMBER;
}
self_grouping_len = xmlStrlen(self->grouping);
while ((*the_format != 0) &&
(xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) &&
(xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) {
if (delayed_multiplier != 0) {
format_info.multiplier = delayed_multiplier;
format_info.is_multiplier_set = TRUE;
delayed_multiplier = 0;
}
if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
if (format_info.integer_digits > 0) {
found_error = 1;
goto OUTPUT_NUMBER;
}
format_info.integer_hash++;
if (format_info.group >= 0)
format_info.group++;
} else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
format_info.integer_digits++;
if (format_info.group >= 0)
format_info.group++;
} else if ((self_grouping_len > 0) &&
(!xmlStrncmp(the_format, self->grouping, self_grouping_len))) {
format_info.group = 0;
the_format += self_grouping_len;
continue;
} else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
if (format_info.is_multiplier_set) {
found_error = 1;
goto OUTPUT_NUMBER;
}
delayed_multiplier = 100;
} else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
if (format_info.is_multiplier_set) {
found_error = 1;
goto OUTPUT_NUMBER;
}
delayed_multiplier = 1000;
} else
break;
if ((len=xmlUTF8Strsize(the_format, 1)) < 1) {
found_error = 1;
goto OUTPUT_NUMBER;
}
the_format += len;
}
if ( (*the_format != 0) &&
(xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) ) {
format_info.add_decimal = TRUE;
if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
found_error = 1;
goto OUTPUT_NUMBER;
}
the_format += len;
}
while (*the_format != 0) {
if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
if (format_info.frac_hash != 0) {
found_error = 1;
goto OUTPUT_NUMBER;
}
format_info.frac_digits++;
} else if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
format_info.frac_hash++;
} else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
if (format_info.is_multiplier_set) {
found_error = 1;
goto OUTPUT_NUMBER;
}
delayed_multiplier = 100;
if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
found_error = 1;
goto OUTPUT_NUMBER;
}
the_format += len;
continue;
} else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
if (format_info.is_multiplier_set) {
found_error = 1;
goto OUTPUT_NUMBER;
}
delayed_multiplier = 1000;
if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
found_error = 1;
goto OUTPUT_NUMBER;
}
the_format += len;
continue;
} else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) {
break;
}
if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
found_error = 1;
goto OUTPUT_NUMBER;
}
the_format += len;
if (delayed_multiplier != 0) {
format_info.multiplier = delayed_multiplier;
delayed_multiplier = 0;
format_info.is_multiplier_set = TRUE;
}
}
if (delayed_multiplier != 0) {
the_format -= len;
delayed_multiplier = 0;
}
suffix = the_format;
suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
if ( (suffix_length < 0) ||
((*the_format != 0) &&
(xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) ) {
found_error = 1;
goto OUTPUT_NUMBER;
}
if (number < 0) {
j = xmlUTF8Strloc(format, self->patternSeparator);
if (j < 0) {
default_sign = 1;
}
else {
the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1);
format_info.is_negative_pattern = TRUE;
format_info.is_multiplier_set = FALSE;
nprefix = the_format;
nprefix_length = xsltFormatNumberPreSuffix(self,
&the_format, &format_info);
if (nprefix_length<0) {
found_error = 1;
goto OUTPUT_NUMBER;
}
while (*the_format != 0) {
if ( (xsltUTF8Charcmp(the_format, (self)->percent) == 0) ||
(xsltUTF8Charcmp(the_format, (self)->permille)== 0) ) {
if (format_info.is_multiplier_set) {
found_error = 1;
goto OUTPUT_NUMBER;
}
format_info.is_multiplier_set = TRUE;
delayed_multiplier = 1;
}
else if (IS_SPECIAL(self, the_format))
delayed_multiplier = 0;
else
break;
if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
found_error = 1;
goto OUTPUT_NUMBER;
}
the_format += len;
}
if (delayed_multiplier != 0) {
format_info.is_multiplier_set = FALSE;
the_format -= len;
}
if (*the_format != 0) {
nsuffix = the_format;
nsuffix_length = xsltFormatNumberPreSuffix(self,
&the_format, &format_info);
if (nsuffix_length < 0) {
found_error = 1;
goto OUTPUT_NUMBER;
}
}
else
nsuffix_length = 0;
if (*the_format != 0) {
found_error = 1;
goto OUTPUT_NUMBER;
}
if ((nprefix_length != prefix_length) ||
(nsuffix_length != suffix_length) ||
((nprefix_length > 0) &&
(xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) ||
((nsuffix_length > 0) &&
(xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) {
prefix = nprefix;
prefix_length = nprefix_length;
suffix = nsuffix;
suffix_length = nsuffix_length;
}
}
}
OUTPUT_NUMBER:
if (found_error != 0) {
xsltTransformError(NULL, NULL, NULL,
"xsltFormatNumberConversion : "
"error in format string '%s', using default\n", format);
default_sign = (number < 0.0) ? 1 : 0;
prefix_length = suffix_length = 0;
format_info.integer_hash = 0;
format_info.integer_digits = 1;
format_info.frac_digits = 1;
format_info.frac_hash = 4;
format_info.group = -1;
format_info.multiplier = 1;
format_info.add_decimal = TRUE;
}
number *= (double)format_info.multiplier;
switch (xmlXPathIsInf(number)) {
case -1:
if (self->minusSign == NULL)
*result = xmlStrdup(BAD_CAST "-");
else
*result = xmlStrdup(self->minusSign);
case 1:
if ((self == NULL) || (self->infinity == NULL))
*result = xmlStrcat(*result, BAD_CAST "Infinity");
else
*result = xmlStrcat(*result, self->infinity);
return(status);
default:
break;
}
buffer = xmlBufferCreate();
if (buffer == NULL) {
return XPATH_MEMORY_ERROR;
}
if (default_sign != 0)
xmlBufferAdd(buffer, self->minusSign, xmlUTF8Strsize(self->minusSign, 1));
for (j = 0; j < prefix_length; ) {
if (*prefix == SYMBOL_QUOTE)
prefix++;
len = xmlUTF8Strsize(prefix, 1);
xmlBufferAdd(buffer, prefix, len);
prefix += len;
j += len;
}
number = fabs(number);
exp10 = format_info.frac_digits + format_info.frac_hash;
if (exp10 > DBL_MAX_10_EXP) {
if (format_info.frac_digits > DBL_MAX_10_EXP) {
format_info.frac_digits = DBL_MAX_10_EXP;
format_info.frac_hash = 0;
} else {
format_info.frac_hash = DBL_MAX_10_EXP - format_info.frac_digits;
}
exp10 = DBL_MAX_10_EXP;
}
scale = pow(10.0, (double) exp10);
number += .5 / scale;
number -= fmod(number, 1 / scale);
if ((self->grouping != NULL) &&
(self->grouping[0] != 0)) {
int gchar;
len = xmlStrlen(self->grouping);
gchar = xsltGetUTF8Char(self->grouping, &len);
xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
format_info.integer_digits,
format_info.group,
gchar, len);
} else
xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
format_info.integer_digits,
format_info.group,
',', 1);
if ((format_info.integer_digits + format_info.integer_hash +
format_info.frac_digits == 0) && (format_info.frac_hash > 0)) {
++format_info.frac_digits;
--format_info.frac_hash;
}
if ((floor(number) == 0) &&
(format_info.integer_digits + format_info.frac_digits == 0)) {
xmlBufferAdd(buffer, self->zeroDigit, xmlUTF8Strsize(self->zeroDigit, 1));
}
if (format_info.frac_digits + format_info.frac_hash == 0) {
if (format_info.add_decimal)
xmlBufferAdd(buffer, self->decimalPoint,
xmlUTF8Strsize(self->decimalPoint, 1));
}
else {
number -= floor(number);
if ((number != 0) || (format_info.frac_digits != 0)) {
xmlBufferAdd(buffer, self->decimalPoint,
xmlUTF8Strsize(self->decimalPoint, 1));
number = floor(scale * number + 0.5);
for (j = format_info.frac_hash; j > 0; j--) {
if (fmod(number, 10.0) >= 1.0)
break;
number /= 10.0;
}
xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
format_info.frac_digits + j,
0, 0, 0);
}
}
for (j = 0; j < suffix_length; ) {
if (*suffix == SYMBOL_QUOTE)
suffix++;
len = xmlUTF8Strsize(suffix, 1);
xmlBufferAdd(buffer, suffix, len);
suffix += len;
j += len;
}
*result = xmlStrdup(xmlBufferContent(buffer));
xmlBufferFree(buffer);
return status;
}