/*
 * 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.
 *
 * daniel@veillard.com
 * Bjorn Reese <breese@users.sourceforge.net>
 */

#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 char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz";
static xsltFormatToken default_token;

/*
 * **** Start temp insert ****
 *
 * 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).
 */

/**
 * 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) {
    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);
}

/***** Stop temp insert *****/
/************************************************************************
 *                                  *
 *          Utility functions               *
 *                                  *
 ************************************************************************/

#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(unsigned int ch)
{
    /*
     * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
     *
     * There a many more digit ranges in newer Unicode versions. These
     * are only the zeros that match Digit in XML 1.0 (IS_DIGIT macro).
     */
    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)
{
    /*
     * 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];
    xmlChar *pointer;
    xmlChar temp_char[6];
    int i;
    int val;
    int len;

    /* Build buffer from back */
    pointer = &temp_string[sizeof(temp_string)] - 1;    /* last char */
    *pointer = 0;
    i = 0;
    while (pointer > temp_string) {
    if ((i >= width) && (fabs(number) < 1.0))
        break; /* for */
    if ((i > 0) && (groupingCharacter != 0) &&
        (digitsPerGroup > 0) &&
        ((i % digitsPerGroup) == 0)) {
        if (pointer - groupingCharacterLen < temp_string) {
            i = -1;     /* flag error */
        break;
        }
        pointer -= groupingCharacterLen;
        xmlCopyCharMultiByte(pointer, groupingCharacter);
    }

    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 {
    /*
     * 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;
        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;
    char *alpha_list;
    double alpha_size = (double)(sizeof(alpha_upper_list) - 1);

    /*
     * 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;
    }

    /* Build buffer from back */
    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; /* for */
    }
    xmlBufferCCat(buffer, pointer);
}

static void
xsltNumberFormatRoman(xsltNumberDataPtr data,
              xmlBufferPtr buffer,
              double number,
              int is_upper)
{
    /*
     * 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;
    }

    /*
     * 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--;
    }
}

static void
xsltNumberFormatTokenize(const xmlChar *format,
             xsltFormatPtr tokens)
{
    int ix = 0;
    int j;
    int val;
    int len;

    default_token.token = DEFAULT_TOKEN;
    default_token.width = 1;
    default_token.separator = BAD_CAST(DEFAULT_SEPARATOR);


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

    /*
     * Insert initial non-alphanumeric token.
     * There is always such a token in the list, even if NULL
     */
    while (! (IS_LETTER(val=xmlStringCurrentChar(NULL, format+ix, &len)) ||
          IS_DIGIT(val)) ) {
    if (format[ix] == 0)        /* if end of format string */
        break; /* while */
    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; /* for */

    /*
     * 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;
    }

    val = xmlStringCurrentChar(NULL, 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 = xmlStringCurrentChar(NULL, format+ix, &len);
        }
        if (IS_DIGIT_ONE(val)) {
        tokens->tokens[tokens->nTokens].token = val - 1;
        ix += len;
        val = xmlStringCurrentChar(NULL, 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 = xmlStringCurrentChar(NULL, format+ix, &len);
    } 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."
         */
        tokens->tokens[tokens->nTokens].token = '0';
        tokens->tokens[tokens->nTokens].width = 1;
    }
    /*
     * 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).
     */
    while (IS_LETTER(val) || IS_DIGIT(val)) {
        ix += len;
        val = xmlStringCurrentChar(NULL, format+ix, &len);
    }

    /*
     * Insert temporary non-alphanumeric final tooken.
     */
    j = ix;
    while (! (IS_LETTER(val) || IS_DIGIT(val))) {
        if (val == 0)
        break; /* while */
        ix += len;
        val = xmlStringCurrentChar(NULL, 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;
    xsltFormatTokenPtr token;

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

    for (i = 0; i < numbers_max; i++) {
    /* Insert number */
    number = numbers[(numbers_max - 1) - i];
        /* Round to nearest like XSLT 2.0 */
        number = floor(number + 0.5);
        /*
         * 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;
        }
    if (i < tokens->nTokens) {
      /*
       * The "n"th format token will be used to format the "n"th
       * number in the list
       */
      token = &(tokens->tokens[i]);
    } else if (tokens->nTokens > 0) {
      /*
       * If there are more numbers than format tokens, then the
       * last format token will be used to format the remaining
       * numbers.
       */
      token = &(tokens->tokens[tokens->nTokens - 1]);
    } else {
      /*
       * If there are no format tokens, then a format token of
       * 1 is used to format all numbers.
       */
      token = &default_token;
    }

    /* Print separator, except for the first number */
    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;
        }
        }

    }
    }

    /*
     * Handle final non-alphanumeric token
     */
    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 {
        /*
         * 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));
    }
}

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) {
    /* process current node */
    if (xsltTestCompMatchCount(context, cur, countPat, node))
        cnt++;
    if ((fromPat != NULL) &&
        xsltTestCompMatchList(context, cur, fromPat)) {
        break; /* while */
    }

    /* 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 */

        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;
            }
        }
    }

    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 oldCtxtNode;
    xmlNodePtr ancestor;
    xmlNodePtr preceding;
    xmlXPathParserContextPtr parser;

    oldCtxtNode = context->xpathCtxt->node;
    parser = xmlXPathNewParserContext(NULL, context->xpathCtxt);
    if (parser) {
    /* ancestor-or-self::*[count] */
    ancestor = node;
    while ((ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE)) {
        if ((fromPat != NULL) &&
        xsltTestCompMatchList(context, ancestor, fromPat))
        break; /* for */

            /*
             * 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.
             */

        if (xsltTestCompMatchCount(context, ancestor, countPat, node)) {
        /* count(preceding-sibling::*) */
        cnt = 1;
                context->xpathCtxt->node = ancestor;
                preceding = xmlXPathNextPrecedingSibling(parser, ancestor);
                while (preceding != NULL) {
                if (xsltTestCompMatchCount(context, preceding, countPat,
                                               node))
            cnt++;
                    context->xpathCtxt->node = ancestor;
                    preceding =
                        xmlXPathNextPrecedingSibling(parser, preceding);
        }
        array[amount++] = (double)cnt;
        if (amount >= max)
            break; /* for */
        }
            context->xpathCtxt->node = node;
            ancestor = xmlXPathNextAncestor(parser, ancestor);
    }
    xmlXPathFreeParserContext(parser);
    }
    context->xpathCtxt->node = oldCtxtNode;
    return amount;
}

static int
xsltNumberFormatGetValue(xmlXPathContextPtr context,
             xmlNodePtr node,
             const xmlChar *value,
             double *number)
{
    int amount = 0;
    xmlBufferPtr pattern;
    xmlXPathObjectPtr obj;

    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;
}

/**
 * xsltNumberFormat:
 * @ctxt: the XSLT transformation context
 * @data: the formatting information
 * @node: the data to format
 *
 * Convert one number.
 */
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;

    /* The format needs to be recomputed each time */
        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;

    /*
     * 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,
                      &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);
        }
    }

        /*
         * Unlike `match` patterns, `count` and `from` patterns can contain
         * variable references, so we have to clear the pattern match
         * cache if the "direct" matching algorithm was used.
         */
        if (data->countPat != NULL)
            xsltCompMatchClearCache(ctxt, data->countPat);
        if (data->fromPat != NULL)
            xsltCompMatchClearCache(ctxt, data->fromPat);
    }
    /* Insert number as text node */
    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)
{
    /* will hold total length of prefix/suffix without quote characters */
    int count=0;
    int len;

    while (1) {
    /*
     * prefix / suffix ends at end of string or at
     * first 'special' character
     */
    if (**format == 0)
        return count;
    /* if next character 'escaped' just count it */
    if (**format == SYMBOL_QUOTE) {
        if (*++(*format) == 0)
        return -1;
    }
    else if (IS_SPECIAL(self, *format))
        return count;
    /*
     * else treat percent/per-mille as special cases,
     * depending on whether +ve or -ve
     */
    else {
        /*
         * for +ve prefix/suffix, allow only a
         * single occurence of either
         */
        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;
    }
}

/**
 * xsltFormatNumberConversion:
 * @self: the decimal format
 * @format: the format requested
 * @number: the value to format
 * @result: the place to output the result
 *
 * format-number() uses the JDK 1.1 DecimalFormat class:
 *
 * 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.
 *
 * Returns a possible XPath error
 */
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;
    double  scale;
    int     j, len;
    int     self_grouping_len;
    xsltFormatNumberInfo format_info;
    /*
     * delayed_multiplier allows a 'trailing' percent or
     * permille to be treated as suffix
     */
    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;

    if (xmlStrlen(format) <= 0) {
    xsltTransformError(NULL, NULL, NULL,
                "xsltFormatNumberConversion : "
        "Invalid format (0-length)\n");
    }
    *result = NULL;
    switch (xmlXPathIsInf(number)) {
    case -1:
        if (self->minusSign == NULL)
        *result = xmlStrdup(BAD_CAST "-");
        else
        *result = xmlStrdup(self->minusSign);
        /* Intentional fall-through */
    case 1:
        if ((self == NULL) || (self->infinity == NULL))
        *result = xmlStrcat(*result, BAD_CAST "Infinity");
        else
        *result = xmlStrcat(*result, self->infinity);
        return(status);
    default:
        if (xmlXPathIsNaN(number)) {
        if ((self == NULL) || (self->noNumber == NULL))
            *result = xmlStrdup(BAD_CAST "NaN");
        else
            *result = xmlStrdup(self->noNumber);
        return(status);
        }
    }

    buffer = xmlBufferCreate();
    if (buffer == NULL) {
    return XPATH_MEMORY_ERROR;
    }

    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;

    /*
     * First we process the +ve pattern to get percent / permille,
     * as well as main format
     */
    prefix = the_format;
    prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
    if (prefix_length < 0) {
    found_error = 1;
    goto OUTPUT_NUMBER;
    }

    /*
     * Here we process the "number" part of the format.  It gets
     * a little messy because of the percent/per-mille - if that
     * 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
     */
    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))) {
        /* Reset group count */
        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; /* while */

    if ((len=xmlUTF8Strsize(the_format, 1)) < 1) {
        found_error = 1;
        goto OUTPUT_NUMBER;
    }
    the_format += len;

    }

    /* We have finished the integer part, now work on fraction */
    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;  /* Skip over the decimal */
    }

    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; /* while */
    } 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; /* while */
    } else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) {
        break; /* while */
    }
    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 is set after processing the
     * "number" part, should be in suffix
     */
    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;
    }

    /*
     * We have processed the +ve prefix, number part and +ve suffix.
     * If the number is -ve, we must substitute the -ve prefix / suffix
     */
    if (number < 0) {
        /*
     * Note that j is the number of UTF8 chars before the separator,
     * not the number of bytes! (bug 151975)
     */
        j =  xmlUTF8Strloc(format, self->patternSeparator);
    if (j < 0) {
    /* No -ve pattern present, so use default signing */
        default_sign = 1;
    }
    else {
        /* Skip over pattern separator (accounting for UTF8) */
        the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1);
        /*
         * Flag changes interpretation of percent/permille
         * in -ve pattern
         */
        format_info.is_negative_pattern = TRUE;
        format_info.is_multiplier_set = FALSE;

        /* First do the -ve prefix */
        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; /* while */
        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;
        }

        /* Finally do the -ve suffix */
        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;
        }
        /*
         * Here's another Java peculiarity:
         * if -ve prefix/suffix == +ve ones, discard & use default
         */
        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;
        } /* else {
        default_sign = 1;
        }
        */
    }
    }

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;
    }

    /* Ready to output our number.  First see if "default sign" is required */
    if (default_sign != 0)
    xmlBufferAdd(buffer, self->minusSign, xmlUTF8Strsize(self->minusSign, 1));

    /* Put the prefix into the buffer */
    for (j = 0; j < prefix_length; ) {
    if (*prefix == SYMBOL_QUOTE)
            prefix++;
        len = xmlUTF8Strsize(prefix, 1);
        xmlBufferAdd(buffer, prefix, len);
        prefix += len;
        j += len;
    }

    /* Next do the integer part of the number */
    number = fabs(number) * (double)format_info.multiplier;
    scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash));
    number = floor((scale * number + 0.5)) / 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);

    /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
    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;
    }

    /* Add leading zero, if required */
    if ((floor(number) == 0) &&
    (format_info.integer_digits + format_info.frac_digits == 0)) {
        xmlBufferAdd(buffer, self->zeroDigit, xmlUTF8Strsize(self->zeroDigit, 1));
    }

    /* Next the fractional part, if required */
    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; /* for */
        number /= 10.0;
        }
        xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
                format_info.frac_digits + j,
                0, 0, 0);
    }
    }
    /* Put the suffix into the buffer */
    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;
}

