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