[LIBXSLT] Update to version 1.1.32. CORE-14291
[reactos.git] / dll / 3rdparty / libxslt / numbers.c
index 55bea60..470ee32 100644 (file)
@@ -48,39 +48,11 @@ static xsltFormatToken default_token;
 /*
  * **** Start temp insert ****
  *
- * The following two routines (xsltUTF8Size and xsltUTF8Charcmp)
- * will be replaced with calls to the corresponding libxml routines
- * at a later date (when other inter-library dependencies require it)
+ * 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).
  */
 
-/**
- * xsltUTF8Size:
- * @utf: pointer to the UTF8 character
- *
- * returns the numbers of bytes in the character, -1 on format error
- */
-static int
-xsltUTF8Size(xmlChar *utf) {
-    xmlChar mask;
-    int len;
-
-    if (utf == NULL)
-        return -1;
-    if (*utf < 0x80)
-        return 1;
-    /* check valid UTF8 character */
-    if (!(*utf & 0x40))
-        return -1;
-    /* determine number of bytes in char */
-    len = 2;
-    for (mask=0x20; mask != 0; mask>>=1) {
-        if (!(*utf & mask))
-            return len;
-        len++;
-    }
-    return -1;
-}
-
 /**
  * xsltUTF8Charcmp
  * @utf1: pointer to first UTF8 char
@@ -91,13 +63,16 @@ xsltUTF8Size(xmlChar *utf) {
  */
 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, xsltUTF8Size(utf1));
+    return xmlStrncmp(utf1, utf2, len);
 }
 
 /***** Stop temp insert *****/
@@ -205,12 +180,13 @@ xsltNumberFormatDecimal(xmlBufferPtr buffer,
     }
     if (i < 0)
         xsltGenericError(xsltGenericErrorContext,
-               "xsltNumberFormatDecimal: Internal buffer size exceeded");
+               "xsltNumberFormatDecimal: Internal buffer size exceeded\n");
     xmlBufferCat(buffer, pointer);
 }
 
 static void
-xsltNumberFormatAlpha(xmlBufferPtr buffer,
+xsltNumberFormatAlpha(xsltNumberDataPtr data,
+                     xmlBufferPtr buffer,
                      double number,
                      int is_upper)
 {
@@ -220,6 +196,26 @@ xsltNumberFormatAlpha(xmlBufferPtr buffer,
     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;
@@ -229,17 +225,30 @@ xsltNumberFormatAlpha(xmlBufferPtr buffer,
        number--;
        *(--pointer) = alpha_list[((int)fmod(number, alpha_size))];
        number /= alpha_size;
-       if (fabs(number) < 1.0)
+       if (number < 1.0)
            break; /* for */
     }
     xmlBufferCCat(buffer, pointer);
 }
 
 static void
-xsltNumberFormatRoman(xmlBufferPtr buffer,
+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
      */
@@ -423,6 +432,23 @@ xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,
     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
@@ -466,28 +492,16 @@ xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,
 
                switch (token->token) {
                case 'A':
-                   xsltNumberFormatAlpha(buffer,
-                                         number,
-                                         TRUE);
-
+                   xsltNumberFormatAlpha(data, buffer, number, TRUE);
                    break;
                case 'a':
-                   xsltNumberFormatAlpha(buffer,
-                                         number,
-                                         FALSE);
-
+                   xsltNumberFormatAlpha(data, buffer, number, FALSE);
                    break;
                case 'I':
-                   xsltNumberFormatRoman(buffer,
-                                         number,
-                                         TRUE);
-
+                   xsltNumberFormatRoman(data, buffer, number, TRUE);
                    break;
                case 'i':
-                   xsltNumberFormatRoman(buffer,
-                                         number,
-                                         FALSE);
-
+                   xsltNumberFormatRoman(data, buffer, number, FALSE);
                    break;
                default:
                    if (IS_DIGIT_ZERO(token->token)) {
@@ -514,54 +528,58 @@ xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,
 
 }
 
+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,
-                           xmlDocPtr doc,
-                           xmlNodePtr elem)
+                           double *array)
 {
     int amount = 0;
     int cnt = 0;
-    xmlNodePtr cur;
-
-    /* select the starting node */
-    switch (node->type) {
-       case XML_ELEMENT_NODE:
-           cur = node;
-           break;
-       case XML_ATTRIBUTE_NODE:
-           cur = ((xmlAttrPtr) node)->parent;
-           break;
-       case XML_TEXT_NODE:
-       case XML_PI_NODE:
-       case XML_COMMENT_NODE:
-           cur = node->parent;
-           break;
-       default:
-           cur = NULL;
-           break;
-    }
+    xmlNodePtr cur = node;
 
     while (cur != NULL) {
        /* process current node */
-       if (countPat == NULL) {
-           if ((node->type == cur->type) &&
-               /* FIXME: must use expanded-name instead of local name */
-               xmlStrEqual(node->name, cur->name)) {
-                   if ((node->ns == cur->ns) ||
-                       ((node->ns != NULL) &&
-                        (cur->ns != NULL) &&
-                        (xmlStrEqual(node->ns->href,
-                            cur->ns->href) )))
-                       cnt++;
-           }
-       } else {
-           if (xsltTestCompMatchList(context, cur, countPat))
-               cnt++;
-       }
+       if (xsltTestCompMatchCount(context, cur, countPat, node))
+           cnt++;
        if ((fromPat != NULL) &&
            xsltTestCompMatchList(context, cur, fromPat)) {
            break; /* while */
@@ -575,16 +593,25 @@ xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context,
             (cur->type == XML_HTML_DOCUMENT_NODE))
            break; /* while */
 
-       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;
-       }
-
+        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;
@@ -598,9 +625,7 @@ xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
                                 xsltCompMatchPtr countPat,
                                 xsltCompMatchPtr fromPat,
                                 double *array,
-                                int max,
-                                xmlDocPtr doc,
-                                xmlNodePtr elem)
+                                int max)
 {
     int amount = 0;
     int cnt;
@@ -620,30 +645,18 @@ xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
                xsltTestCompMatchList(context, ancestor, fromPat))
                break; /* for */
 
-           if ((countPat == NULL && node->type == ancestor->type &&
-               xmlStrEqual(node->name, ancestor->name)) ||
-               xsltTestCompMatchList(context, ancestor, countPat)) {
+           if (xsltTestCompMatchCount(context, ancestor, countPat, node)) {
                /* count(preceding-sibling::*) */
-               cnt = 0;
-               for (preceding = ancestor;
+               cnt = 1;
+               for (preceding =
+                        xmlXPathNextPrecedingSibling(parser, ancestor);
                     preceding != NULL;
                     preceding =
                        xmlXPathNextPrecedingSibling(parser, preceding)) {
-                   if (countPat == NULL) {
-                       if ((preceding->type == ancestor->type) &&
-                           xmlStrEqual(preceding->name, ancestor->name)){
-                           if ((preceding->ns == ancestor->ns) ||
-                               ((preceding->ns != NULL) &&
-                                (ancestor->ns != NULL) &&
-                                (xmlStrEqual(preceding->ns->href,
-                                    ancestor->ns->href) )))
-                               cnt++;
-                       }
-                   } else {
-                       if (xsltTestCompMatchList(context, preceding,
-                                                 countPat))
-                           cnt++;
-                   }
+
+                   if (xsltTestCompMatchCount(context, preceding, countPat,
+                                               node))
+                       cnt++;
                }
                array[amount++] = (double)cnt;
                if (amount >= max)
@@ -700,24 +713,29 @@ xsltNumberFormat(xsltTransformContextPtr ctxt,
     int amount, i;
     double number;
     xsltFormat tokens;
-    int tempformat = 0;
 
-    if ((data->format == NULL) && (data->has_format != 0)) {
-       data->format = xsltEvalAttrValueTemplate(ctxt, data->node,
+    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);
-       tempformat = 1;
-    }
-    if (data->format == NULL) {
-       return;
+        if (format == NULL)
+            return;
+        xsltNumberFormatTokenize(format, &tokens);
+       xmlFree(format);
     }
 
     output = xmlBufferCreate();
     if (output == NULL)
        goto XSLT_NUMBER_FORMAT_END;
 
-    xsltNumberFormatTokenize(data->format, &tokens);
-
     /*
      * Evaluate the XPath expression to find the value(s)
      */
@@ -742,9 +760,7 @@ xsltNumberFormat(xsltTransformContextPtr ctxt,
                                                      data->countPat,
                                                      data->fromPat,
                                                      &number,
-                                                     1,
-                                                     data->doc,
-                                                     data->node);
+                                                     1);
            if (amount == 1) {
                xsltNumberFormatInsertNumbers(data,
                                              &number,
@@ -760,9 +776,7 @@ xsltNumberFormat(xsltTransformContextPtr ctxt,
                                                      data->countPat,
                                                      data->fromPat,
                                                      numarray,
-                                                     max,
-                                                     data->doc,
-                                                     data->node);
+                                                     max);
            if (amount > 0) {
                xsltNumberFormatInsertNumbers(data,
                                              numarray,
@@ -775,9 +789,7 @@ xsltNumberFormat(xsltTransformContextPtr ctxt,
                                                 node,
                                                 data->countPat,
                                                 data->fromPat,
-                                                &number,
-                                                data->doc,
-                                                data->node);
+                                                &number);
            if (amount > 0) {
                xsltNumberFormatInsertNumbers(data,
                                              &number,
@@ -790,6 +802,9 @@ xsltNumberFormat(xsltTransformContextPtr ctxt,
     /* 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)
@@ -798,14 +813,6 @@ xsltNumberFormat(xsltTransformContextPtr ctxt,
        if (tokens.tokens[i].separator != NULL)
            xmlFree(tokens.tokens[i].separator);
     }
-
-XSLT_NUMBER_FORMAT_END:
-    if (tempformat == 1) {
-       /* The format need to be recomputed each time */
-       data->format = NULL;
-    }
-    if (output != NULL)
-       xmlBufferFree(output);
 }
 
 static int
@@ -850,7 +857,7 @@ xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltForma
            }
        }
 
-       if ((len=xsltUTF8Size(*format)) < 1)
+       if ((len=xmlUTF8Strsize(*format, 1)) < 1)
            return -1;
        count += len;
        *format += len;
@@ -1032,7 +1039,7 @@ xsltFormatNumberConversion(xsltDecimalFormatPtr self,
        } else
            break; /* while */
 
-       if ((len=xsltUTF8Size(the_format)) < 1) {
+       if ((len=xmlUTF8Strsize(the_format, 1)) < 1) {
            found_error = 1;
            goto OUTPUT_NUMBER;
        }
@@ -1041,9 +1048,14 @@ xsltFormatNumberConversion(xsltDecimalFormatPtr self,
     }
 
     /* We have finished the integer part, now work on fraction */
-    if (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) {
+    if ( (*the_format != 0) &&
+         (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) ) {
         format_info.add_decimal = TRUE;
-       the_format += xsltUTF8Size(the_format); /* Skip over the decimal */
+        if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
+            found_error = 1;
+            goto OUTPUT_NUMBER;
+        }
+       the_format += len;      /* Skip over the decimal */
     }
 
     while (*the_format != 0) {
@@ -1062,7 +1074,7 @@ xsltFormatNumberConversion(xsltDecimalFormatPtr self,
                goto OUTPUT_NUMBER;
            }
            delayed_multiplier = 100;
-           if ((len = xsltUTF8Size(the_format)) < 1) {
+           if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
                found_error = 1;
                goto OUTPUT_NUMBER;
            }
@@ -1074,7 +1086,7 @@ xsltFormatNumberConversion(xsltDecimalFormatPtr self,
                goto OUTPUT_NUMBER;
            }
            delayed_multiplier = 1000;
-           if  ((len = xsltUTF8Size(the_format)) < 1) {
+           if  ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
                found_error = 1;
                goto OUTPUT_NUMBER;
            }
@@ -1083,7 +1095,7 @@ xsltFormatNumberConversion(xsltDecimalFormatPtr self,
        } else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) {
            break; /* while */
        }
-       if ((len = xsltUTF8Size(the_format)) < 1) {
+       if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
            found_error = 1;
            goto OUTPUT_NUMBER;
        }
@@ -1160,7 +1172,7 @@ xsltFormatNumberConversion(xsltDecimalFormatPtr self,
                    delayed_multiplier = 0;
                else
                    break; /* while */
-               if ((len = xsltUTF8Size(the_format)) < 1) {
+               if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
                    found_error = 1;
                    goto OUTPUT_NUMBER;
                }
@@ -1226,12 +1238,12 @@ OUTPUT_NUMBER:
 
     /* Ready to output our number.  First see if "default sign" is required */
     if (default_sign != 0)
-       xmlBufferAdd(buffer, self->minusSign, xsltUTF8Size(self->minusSign));
+       xmlBufferAdd(buffer, self->minusSign, xmlUTF8Strsize(self->minusSign, 1));
 
     /* Put the prefix into the buffer */
     for (j = 0; j < prefix_length; j++) {
        if ((pchar = *prefix++) == SYMBOL_QUOTE) {
-           len = xsltUTF8Size(prefix);
+           len = xmlUTF8Strsize(prefix, 1);
            xmlBufferAdd(buffer, prefix, len);
            prefix += len;
            j += len - 1;       /* length of symbol less length of quote */
@@ -1268,20 +1280,20 @@ OUTPUT_NUMBER:
     /* Add leading zero, if required */
     if ((floor(number) == 0) &&
        (format_info.integer_digits + format_info.frac_digits == 0)) {
-        xmlBufferAdd(buffer, self->zeroDigit, xsltUTF8Size(self->zeroDigit));
+        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,
-                        xsltUTF8Size(self->decimalPoint));
+                        xmlUTF8Strsize(self->decimalPoint, 1));
     }
     else {
       number -= floor(number);
        if ((number != 0) || (format_info.frac_digits != 0)) {
            xmlBufferAdd(buffer, self->decimalPoint,
-                        xsltUTF8Size(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)
@@ -1296,7 +1308,7 @@ OUTPUT_NUMBER:
     /* Put the suffix into the buffer */
     for (j = 0; j < suffix_length; j++) {
        if ((pchar = *suffix++) == SYMBOL_QUOTE) {
-            len = xsltUTF8Size(suffix);
+            len = xmlUTF8Strsize(suffix, 1);
            xmlBufferAdd(buffer, suffix, len);
            suffix += len;
            j += len - 1;       /* length of symbol less length of escape */