diff --git a/ChangeLog b/ChangeLog index 451d45f339fe55c818e71c669a5b5bbbc1361719_Q2hhbmdlTG9n..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_Q2hhbmdlTG9n 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Thu Jan 25 12:13:04 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr> + + * functions.[ch]: Bjorn Reese <breese@mail1.stofanet.dk> provided + number formatting !!! + * acconfig.h config.h.in configure.in libxslt/Makefile.am + tests/Makefile.am; added testing for mathematical functions, + fixed make test(s) + * FEATURES: updated + Wed Jan 24 16:59:05 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr> * libxslt/xsltInternals.h libxslt/pattern.c: fixed problems diff --git a/FEATURES b/FEATURES index 451d45f339fe55c818e71c669a5b5bbbc1361719_RkVBVFVSRVM=..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_RkVBVFVSRVM= 100644 --- a/FEATURES +++ b/FEATURES @@ -203,7 +203,7 @@ NO node-set document(object, node-set?) NO node-set key(string, object) -NO string format-number(number, string, string?) +YES string format-number(number, string, string?) NO node-set current() NO string unparsed-entity-uri(string) NO string generate-id(node-set?) diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_YWNjb25maWcuaA== --- /dev/null +++ b/acconfig.h @@ -0,0 +1,5 @@ +#undef HAVE_ISINF +#undef HAVE_ISNAN +#undef HAVE_POW +#undef HAVE_FLOOR +#undef HAVE_FABS diff --git a/config.h.in b/config.h.in index 451d45f339fe55c818e71c669a5b5bbbc1361719_Y29uZmlnLmguaW4=..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_Y29uZmlnLmguaW4= 100644 --- a/config.h.in +++ b/config.h.in @@ -5,3 +5,24 @@ /* Define if you have the ANSI C header files. */ #undef STDC_HEADERS + +#undef HAVE_ISINF +#undef HAVE_ISNAN +#undef HAVE_POW +#undef HAVE_FLOOR +#undef HAVE_FABS + +/* Define if you have the <float.h> header file. */ +#undef HAVE_FLOAT_H + +/* Define if you have the <fp_class.h> header file. */ +#undef HAVE_FP_CLASS_H + +/* Define if you have the <ieeefp.h> header file. */ +#undef HAVE_IEEEFP_H + +/* Define if you have the <math.h> header file. */ +#undef HAVE_MATH_H + +/* Define if you have the <nan.h> header file. */ +#undef HAVE_NAN_H diff --git a/configure.in b/configure.in index 451d45f339fe55c818e71c669a5b5bbbc1361719_Y29uZmlndXJlLmlu..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_Y29uZmlndXJlLmlu 100644 --- a/configure.in +++ b/configure.in @@ -8,6 +8,37 @@ AM_MAINTAINER_MODE dnl +dnl Check the environment +dnl + +AC_ISC_POSIX +AC_PROG_CC +AC_STDC_HEADERS +AC_ARG_PROGRAM +AM_PROG_LIBTOOL + +dnl +dnl Math detection +dnl + +AC_CHECK_HEADERS(ieeefp.h nan.h math.h fp_class.h float.h) +AC_CHECK_FUNC(isnan, , AC_CHECK_LIB(m, isnan, + [M_LIBS="-lm"; AC_DEFINE(HAVE_ISNAN)])) + +AC_CHECK_FUNC(isinf, , AC_CHECK_LIB(m, isinf, + [M_LIBS="-lm"; AC_DEFINE(HAVE_ISINF)])) + +AC_CHECK_FUNC(pow, , AC_CHECK_LIB(m, pow, + [M_LIBS="-lm"; AC_DEFINE(HAVE_POW)])) + +AC_CHECK_FUNC(floor, , AC_CHECK_LIB(m, pow, + [M_LIBS="-lm"; AC_DEFINE(HAVE_FLOOR)])) + +AC_CHECK_FUNC(fabs, , AC_CHECK_LIB(m, pow, + [M_LIBS="-lm"; AC_DEFINE(HAVE_FABS)])) + + +dnl dnl Debug for DV dnl if test "${LOGNAME}" = "veillard" -a "`pwd`" = "/u/veillard/XSLT" ; then @@ -49,16 +80,6 @@ ) -dnl -dnl Check the environment -dnl - -AC_ISC_POSIX -AC_PROG_CC -AC_STDC_HEADERS -AC_ARG_PROGRAM -AM_PROG_LIBTOOL - dnl No internationalization (yet ?) dnl dnl ALL_LINGUAS="it ko fr de es no ga sv pt ja fi cs" @@ -110,7 +131,8 @@ XSLT_LIBDIR='-L${libdir}' XSLT_INCLUDEDIR='-I${includedir}' -XSLT_LIBS="-lxslt $LIBXML_LIBS" +EXTRA_LIBS="$LIBXML_LIBS $M_LIBS" +XSLT_LIBS="-lxslt $LIBXML_LIBS $M_LIBS" AC_SUBST(XSLT_LIBDIR) AC_SUBST(XSLT_INCLUDEDIR) @@ -114,6 +136,7 @@ AC_SUBST(XSLT_LIBDIR) AC_SUBST(XSLT_INCLUDEDIR) +AC_SUBST(EXTRA_LIBS) AC_SUBST(XSLT_LIBS) AC_OUTPUT([ diff --git a/libxslt/Makefile.am b/libxslt/Makefile.am index 451d45f339fe55c818e71c669a5b5bbbc1361719_bGlieHNsdC9NYWtlZmlsZS5hbQ==..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_bGlieHNsdC9NYWtlZmlsZS5hbQ== 100644 --- a/libxslt/Makefile.am +++ b/libxslt/Makefile.am @@ -32,7 +32,7 @@ bin_PROGRAMS = xsltproc DEPS = $(top_builddir)/libxslt/libxslt.la -LDADDS = -L. $(top_builddir)/libxslt/libxslt.la $(LIBXML_LIBS) +LDADDS = -L. $(top_builddir)/libxslt/libxslt.la $(EXTRA_LIBS) xsltproc_SOURCES = xsltproc.c xsltproc_LDFLAGS = diff --git a/libxslt/functions.c b/libxslt/functions.c index 451d45f339fe55c818e71c669a5b5bbbc1361719_bGlieHNsdC9mdW5jdGlvbnMuYw==..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_bGlieHNsdC9mdW5jdGlvbnMuYw== 100644 --- a/libxslt/functions.c +++ b/libxslt/functions.c @@ -7,9 +7,10 @@ * See Copyright for the status of this software. * * Daniel.Veillard@imag.fr + * Bjorn Reese <breese@mail1.stofanet.dk> for number formatting */ #include "xsltconfig.h" #include <string.h> @@ -10,7 +11,26 @@ */ #include "xsltconfig.h" #include <string.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_MATH_H +#include <math.h> +#endif +#ifdef HAVE_FLOAT_H +#include <float.h> +#endif +#ifdef HAVE_IEEEFP_H +#include <ieeefp.h> +#endif +#ifdef HAVE_NAN_H +#include <nan.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif + #include <libxml/xmlmemory.h> @@ -16,4 +36,5 @@ #include <libxml/xmlmemory.h> +#include <libxml/parser.h> #include <libxml/tree.h> #include <libxml/valid.h> #include <libxml/hash.h> @@ -28,6 +49,50 @@ #define DEBUG_FUNCTION +#ifndef FALSE +# define FALSE (0 == 1) +# define TRUE (1 == 1) +#endif + +#define DIGIT_LIST "0123456789" +#define SYMBOL_QUOTE ((xmlChar)'\'') +#define SYMBOL_PATTERN_SEPARATOR ((xmlChar)';') +#define SYMBOL_ZERO_DIGIT ((xmlChar)'#') +#define SYMBOL_DIGIT ((xmlChar)'0') +#define SYMBOL_DECIMAL_POINT ((xmlChar)'.') +#define SYMBOL_GROUPING ((xmlChar)',') +#define SYMBOL_MINUS ((xmlChar)'-') +#define SYMBOL_PERCENT ((xmlChar)'%') +#define SYMBOL_PERMILLE ((xmlChar)'?') + +static struct _xsltDecimalFormat globalDecimalFormat; + +/************************************************************************ + * * + * Utility functions * + * * + ************************************************************************/ + +#ifndef isnan +static int +isnan(volatile double number) +{ + return (!(number < 0.0 || number > 0.0) && (number != 0.0)); +} +#endif + +#ifndef isinf +static int +isinf(double number) +{ +# ifdef HUGE_VAL + return ((number == HUGE_VAL) ? 1 : ((number == -HUGE_VAL) ? -1 : 0)); +# else + return FALSE; +# endif +} +#endif + /************************************************************************ * * @@ -82,4 +147,212 @@ * Implement the format-number() XSLT function * string format-number(number, string, string?) */ +/* + * 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. + */ +/* TODO + * + * The JDK description does not tell where percent and permille may + * and may not appear within the format string. + * + * Error handling. + * + * Inf and NaN not tested. + */ +#define IS_SPECIAL(self,letter) \ + ((letter == self->zeroDigit) || \ + (letter == self->digit) || \ + (letter == self->decimalPoint) || \ + (letter == self->grouping) || \ + (letter == self->minusSign) || \ + (letter == self->percent) || \ + (letter == self->permille)) \ + +static xmlChar * +PrivateDecimalFormat(xsltDecimalFormatPtr self, + xmlChar *format, + double number) +{ + xmlChar *the_format; + xmlBufferPtr buffer; + xmlChar *result; + char digit_buffer[2]; + int use_minus; + int i, j; + int length; + int group; + int integer_digits = 0; + int integer_zeroes = 0; + int fraction_digits = 0; + int fraction_zeroes = 0; + int decimal_point; + double divisor; + int digit; + + buffer = xmlBufferCreate(); + + /* Find positive or negative template */ + the_format = (xmlChar *)xmlStrchr(format, + self->patternSeparator); + if ((the_format != NULL) && (number < 0.0)) { + /* Use negative template */ + the_format++; + use_minus = FALSE; + } else { + /* Use positive template */ + if (the_format) + the_format[0] = 0; + the_format = format; + use_minus = (number < 0.0) ? TRUE : FALSE; + } + + /* Prefix */ + length = xmlStrlen(the_format); + for (i = 0; i < length; i++) { + if (IS_SPECIAL(self, the_format[i])) { + break; /* for */ + } else { + if (the_format[i] == SYMBOL_QUOTE) { + /* Quote character */ + i++; + } + xmlBufferAdd(buffer, &the_format[i], 1); + } + } + + if (isinf(number)) { + xmlBufferCat(buffer, self->infinity); + /* Skip until suffix */ + for ( ; i < length; i++) { + if (! IS_SPECIAL(self, the_format[i])) + break; /* for */ + } + } else if (isnan(number)) { + xmlBufferCat(buffer, self->noNumber); + /* Skip until suffix */ + for ( ; i < length; i++) { + if (! IS_SPECIAL(self, the_format[i])) + break; /* for */ + } + } else { + + /* Parse the number part of the format string */ + decimal_point = FALSE; + group = 0; + for ( ; i < length; i++) { + + if (the_format[i] == self->digit) { + if (decimal_point) { + if (fraction_zeroes > 0) + ; /* Error in format */ + fraction_digits++; + } else { + integer_digits++; + group++; + } + + } else if (the_format[i] == self->zeroDigit) { + if (decimal_point) + fraction_zeroes++; + else { + if (integer_digits > 0) + ; /* Error in format */ + integer_zeroes++; + group++; + } + + } else if (the_format[i] == self->grouping) { + if (decimal_point) + ; /* Error in format */ + group = 0; + + } else if (the_format[i] == self->decimalPoint) { + if (decimal_point) + ; /* Error in format */ + decimal_point = TRUE; + + } else + break; + } + + /* Format the number */ + + if (use_minus) + xmlBufferAdd(buffer, &(self->minusSign), 1); + + number = fabs(number); + number = floor(0.5 + number * pow(10.0, (double)(fraction_digits + fraction_zeroes))); + + /* Integer part */ + digit_buffer[1] = (char)0; + j = integer_digits + integer_zeroes; + divisor = pow(10.0, (double)(integer_digits + integer_zeroes + fraction_digits + fraction_zeroes - 1)); + for ( ; j > 0; j--) { + digit = (int)(number / divisor); + number -= (double)digit * divisor; + divisor /= 10.0; + if ((digit > 0) || (j <= integer_digits)) { + digit_buffer[0] = DIGIT_LIST[digit]; + xmlBufferCCat(buffer, digit_buffer); + } + } + + if (decimal_point) + xmlBufferAdd(buffer, &(self->decimalPoint), 1); + + /* Fraction part */ + for (j = fraction_digits + fraction_zeroes; j > 0; j--) { + digit = (int)(number / divisor); + number -= (double)digit * divisor; + divisor /= 10.0; + if ((digit > 0) || (j > fraction_zeroes)) { + digit_buffer[0] = DIGIT_LIST[digit]; + xmlBufferCCat(buffer, digit_buffer); + } + } + } + + /* Suffix */ + for ( ; i < length; i++) { + if (the_format[i] == SYMBOL_QUOTE) + i++; + xmlBufferAdd(buffer, &the_format[i], 1); + } + + result = xmlStrdup(xmlBufferContent(buffer)); + xmlBufferFree(buffer); + return result; +} + void @@ -85,6 +358,34 @@ void -xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs){ - TODO /* function */ +xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) +{ + xmlXPathObjectPtr numberObj = NULL; + xmlXPathObjectPtr formatObj = NULL; + xmlXPathObjectPtr decimalObj = NULL; + + switch (nargs) { + case 3: + CAST_TO_STRING; + decimalObj = valuePop(ctxt); + globalDecimalFormat.decimalPoint = decimalObj->stringval[0]; /* hack */ + /* Intentional fall-through */ + case 2: + CAST_TO_STRING; + formatObj = valuePop(ctxt); + CAST_TO_NUMBER; + numberObj = valuePop(ctxt); + break; + default: + XP_ERROR(XPATH_INVALID_ARITY); + } + + valuePush(ctxt, + xmlXPathNewString(PrivateDecimalFormat(&globalDecimalFormat, + formatObj->stringval, + numberObj->floatval))); + + xmlXPathFreeObject(numberObj); + xmlXPathFreeObject(formatObj); + xmlXPathFreeObject(decimalObj); } /** diff --git a/libxslt/functions.h b/libxslt/functions.h index 451d45f339fe55c818e71c669a5b5bbbc1361719_bGlieHNsdC9mdW5jdGlvbnMuaA==..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_bGlieHNsdC9mdW5jdGlvbnMuaA== 100644 --- a/libxslt/functions.h +++ b/libxslt/functions.h @@ -4,6 +4,7 @@ * See Copyright for the status of this software. * * Daniel.Veillard@imag.fr + * Bjorn Reese <breese@mail1.stofanet.dk> for number formatting */ #ifndef __XML_XSLT_FUNCTIONS_H__ @@ -18,6 +19,25 @@ #endif /* + * Data structure of decimal-format + */ +typedef struct _xsltDecimalFormat { + /* Used for interpretation of pattern */ + xmlChar digit; + xmlChar patternSeparator; + /* May appear in result */ + xmlChar minusSign; + xmlChar *infinity; + xmlChar *noNumber; + /* Used for interpretation of pattern and may appear in result */ + xmlChar decimalPoint; + xmlChar grouping; + xmlChar percent; + xmlChar permille; + xmlChar zeroDigit; +} xsltDecimalFormat, *xsltDecimalFormatPtr; + +/* * Interfaces for the functions implementations */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 451d45f339fe55c818e71c669a5b5bbbc1361719_dGVzdHMvTWFrZWZpbGUuYW0=..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_dGVzdHMvTWFrZWZpbGUuYW0= 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,3 +2,4 @@ SUBDIRS=REC1 REC2 +test tests: all