summaryrefslogtreecommitdiffstats
path: root/libxslt/libexslt
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-11-17 21:21:33 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-11-17 21:21:33 (GMT)
commit8705a471f0cb989dca5bed1ac1aa9c982146ceb0 (patch)
treec76eb1b28847599af70596ce7adbfd62ca4985f5 /libxslt/libexslt
parent8d530e150d787e9a74e27592d4e67a496cd922da (diff)
parent93eaa8f0a22ef3712b9a4bafdc50ba9a7d80ae8f (diff)
downloadblt-8705a471f0cb989dca5bed1ac1aa9c982146ceb0.zip
blt-8705a471f0cb989dca5bed1ac1aa9c982146ceb0.tar.gz
blt-8705a471f0cb989dca5bed1ac1aa9c982146ceb0.tar.bz2
Merge commit '93eaa8f0a22ef3712b9a4bafdc50ba9a7d80ae8f' as 'libxslt'
Diffstat (limited to 'libxslt/libexslt')
-rw-r--r--libxslt/libexslt/Makefile.am35
-rw-r--r--libxslt/libexslt/common.c131
-rw-r--r--libxslt/libexslt/crypto.c806
-rw-r--r--libxslt/libexslt/date.c3911
-rw-r--r--libxslt/libexslt/dynamic.c301
-rw-r--r--libxslt/libexslt/exslt.c40
-rw-r--r--libxslt/libexslt/exslt.h102
-rw-r--r--libxslt/libexslt/exsltconfig.h.in73
-rw-r--r--libxslt/libexslt/exsltexports.h140
-rw-r--r--libxslt/libexslt/functions.c777
-rw-r--r--libxslt/libexslt/libexslt.3270
-rw-r--r--libxslt/libexslt/libexslt.h29
-rw-r--r--libxslt/libexslt/math.c1202
-rw-r--r--libxslt/libexslt/saxon.c313
-rw-r--r--libxslt/libexslt/sets.c334
-rw-r--r--libxslt/libexslt/strings.c836
16 files changed, 9300 insertions, 0 deletions
diff --git a/libxslt/libexslt/Makefile.am b/libxslt/libexslt/Makefile.am
new file mode 100644
index 0000000..1cf5138
--- /dev/null
+++ b/libxslt/libexslt/Makefile.am
@@ -0,0 +1,35 @@
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/libxslt -I$(top_srcdir)/libexslt \
+ -I$(top_builddir) -I$(top_builddir)/libxslt \
+ -I$(top_builddir)/libexslt
+
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBXML_CFLAGS)
+
+lib_LTLIBRARIES = libexslt.la
+
+exsltincdir = $(includedir)/libexslt
+
+exsltinc_HEADERS = \
+ exslt.h \
+ exsltexports.h
+nodist_exsltinc_HEADERS = \
+ exsltconfig.h
+
+libexslt_la_SOURCES = \
+ exslt.c \
+ common.c \
+ crypto.c \
+ math.c \
+ sets.c \
+ functions.c \
+ strings.c \
+ date.c \
+ saxon.c \
+ libexslt.h \
+ dynamic.c
+
+libexslt_la_LIBADD = $(top_builddir)/libxslt/libxslt.la $(EXTRA_LIBS) $(LIBGCRYPT_LIBS)
+libexslt_la_LDFLAGS = $(WIN32_EXTRA_LDFLAGS) -version-info $(LIBEXSLT_VERSION_INFO)
+
+man_MANS = libexslt.3
+
+EXTRA_DIST = $(man_MANS)
diff --git a/libxslt/libexslt/common.c b/libxslt/libexslt/common.c
new file mode 100644
index 0000000..91be04f
--- /dev/null
+++ b/libxslt/libexslt/common.c
@@ -0,0 +1,131 @@
+#define IN_LIBEXSLT
+#include "libexslt/libexslt.h"
+
+#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <libxslt/xsltconfig.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/extensions.h>
+#include <libxslt/transform.h>
+#include <libxslt/extra.h>
+#include <libxslt/preproc.h>
+
+#include "exslt.h"
+
+static void
+exsltNodeSetFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ if (xmlXPathStackIsNodeSet (ctxt)) {
+ xsltFunctionNodeSet (ctxt, nargs);
+ return;
+ } else {
+ xmlDocPtr fragment;
+ xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
+ xmlNodePtr txt;
+ xmlChar *strval;
+ xmlXPathObjectPtr obj;
+ /*
+ * SPEC EXSLT:
+ * "You can also use this function to turn a string into a text
+ * node, which is helpful if you want to pass a string to a
+ * function that only accepts a node-set."
+ */
+ fragment = xsltCreateRVT(tctxt);
+ if (fragment == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "exsltNodeSetFunction: Failed to create a tree fragment.\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ return;
+ }
+ xsltRegisterLocalRVT(tctxt, fragment);
+
+ strval = xmlXPathPopString (ctxt);
+
+ txt = xmlNewDocText (fragment, strval);
+ xmlAddChild((xmlNodePtr) fragment, txt);
+ obj = xmlXPathNewNodeSet(txt);
+ if (obj == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "exsltNodeSetFunction: Failed to create a node set object.\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ }
+ if (strval != NULL)
+ xmlFree (strval);
+
+ valuePush (ctxt, obj);
+ }
+}
+
+static void
+exsltObjectTypeFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr obj, ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ obj = valuePop(ctxt);
+
+ switch (obj->type) {
+ case XPATH_STRING:
+ ret = xmlXPathNewCString("string");
+ break;
+ case XPATH_NUMBER:
+ ret = xmlXPathNewCString("number");
+ break;
+ case XPATH_BOOLEAN:
+ ret = xmlXPathNewCString("boolean");
+ break;
+ case XPATH_NODESET:
+ ret = xmlXPathNewCString("node-set");
+ break;
+ case XPATH_XSLT_TREE:
+ ret = xmlXPathNewCString("RTF");
+ break;
+ case XPATH_USERS:
+ ret = xmlXPathNewCString("external");
+ break;
+ default:
+ xsltGenericError(xsltGenericErrorContext,
+ "object-type() invalid arg\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ xmlXPathFreeObject(obj);
+ return;
+ }
+ xmlXPathFreeObject(obj);
+ valuePush(ctxt, ret);
+}
+
+
+/**
+ * exsltCommonRegister:
+ *
+ * Registers the EXSLT - Common module
+ */
+
+void
+exsltCommonRegister (void) {
+ xsltRegisterExtModuleFunction((const xmlChar *) "node-set",
+ EXSLT_COMMON_NAMESPACE,
+ exsltNodeSetFunction);
+ xsltRegisterExtModuleFunction((const xmlChar *) "object-type",
+ EXSLT_COMMON_NAMESPACE,
+ exsltObjectTypeFunction);
+ xsltRegisterExtModuleElement((const xmlChar *) "document",
+ EXSLT_COMMON_NAMESPACE,
+ (xsltPreComputeFunction) xsltDocumentComp,
+ (xsltTransformFunction) xsltDocumentElem);
+}
diff --git a/libxslt/libexslt/crypto.c b/libxslt/libexslt/crypto.c
new file mode 100644
index 0000000..e13db8b
--- /dev/null
+++ b/libxslt/libexslt/crypto.c
@@ -0,0 +1,806 @@
+#define IN_LIBEXSLT
+#include "libexslt/libexslt.h"
+
+#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/parser.h>
+#include <libxml/encoding.h>
+#include <libxml/uri.h>
+
+#include <libxslt/xsltconfig.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/extensions.h>
+
+#include "exslt.h"
+
+#ifdef EXSLT_CRYPTO_ENABLED
+
+#define HASH_DIGEST_LENGTH 32
+#define MD5_DIGEST_LENGTH 16
+#define SHA1_DIGEST_LENGTH 20
+
+/* gcrypt rc4 can do 256 bit keys, but cryptoapi limit
+ seems to be 128 for the default provider */
+#define RC4_KEY_LENGTH 128
+
+/* The following routines have been declared static - this should be
+ reviewed to consider whether we want to expose them to the API
+ exsltCryptoBin2Hex
+ exsltCryptoHex2Bin
+ exsltCryptoGcryptInit
+ exsltCryptoGcryptHash
+ exsltCryptoGcryptRc4Encrypt
+ exsltCryptoGcryptRC4Decrypt
+*/
+
+/**
+ * exsltCryptoBin2Hex:
+ * @bin: binary blob to convert
+ * @binlen: length of binary blob
+ * @hex: buffer to store hex version of blob
+ * @hexlen: length of buffer to store hex version of blob
+ *
+ * Helper function which encodes a binary blob as hex.
+ */
+static void
+exsltCryptoBin2Hex (const unsigned char *bin, int binlen,
+ unsigned char *hex, int hexlen) {
+ static const char bin2hex[] = { '0', '1', '2', '3',
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 'c', 'd', 'e', 'f'
+ };
+
+ unsigned char lo, hi;
+ int i, pos;
+ for (i = 0, pos = 0; (i < binlen && pos < hexlen); i++) {
+ lo = bin[i] & 0xf;
+ hi = bin[i] >> 4;
+ hex[pos++] = bin2hex[hi];
+ hex[pos++] = bin2hex[lo];
+ }
+
+ hex[pos] = '\0';
+}
+
+/**
+ * exsltCryptoHex2Bin:
+ * @hex: hex version of blob to convert
+ * @hexlen: length of hex buffer
+ * @bin: destination binary buffer
+ * @binlen: length of binary buffer
+ *
+ * Helper function which decodes a hex blob to binary
+ */
+static int
+exsltCryptoHex2Bin (const unsigned char *hex, int hexlen,
+ unsigned char *bin, int binlen) {
+ int i = 0, j = 0;
+ unsigned char lo, hi, result, tmp;
+
+ while (i < hexlen && j < binlen) {
+ hi = lo = 0;
+
+ tmp = hex[i++];
+ if (tmp >= '0' && tmp <= '9')
+ hi = tmp - '0';
+ else if (tmp >= 'a' && tmp <= 'f')
+ hi = 10 + (tmp - 'a');
+
+ tmp = hex[i++];
+ if (tmp >= '0' && tmp <= '9')
+ lo = tmp - '0';
+ else if (tmp >= 'a' && tmp <= 'f')
+ lo = 10 + (tmp - 'a');
+
+ result = hi << 4;
+ result += lo;
+ bin[j++] = result;
+ }
+
+ return j;
+}
+
+#if defined(WIN32)
+
+#define HAVE_CRYPTO
+#define PLATFORM_HASH exsltCryptoCryptoApiHash
+#define PLATFORM_RC4_ENCRYPT exsltCryptoCryptoApiRc4Encrypt
+#define PLATFORM_RC4_DECRYPT exsltCryptoCryptoApiRc4Decrypt
+#define PLATFORM_MD4 CALG_MD4
+#define PLATFORM_MD5 CALG_MD5
+#define PLATFORM_SHA1 CALG_SHA1
+
+#include <windows.h>
+#include <wincrypt.h>
+#pragma comment(lib, "advapi32.lib")
+
+static void
+exsltCryptoCryptoApiReportError (xmlXPathParserContextPtr ctxt,
+ int line) {
+ LPVOID lpMsgBuf;
+ DWORD dw = GetLastError ();
+
+ FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) & lpMsgBuf, 0, NULL);
+
+ xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL, NULL,
+ "exslt:crypto error (line %d). %s", line,
+ lpMsgBuf);
+ LocalFree (lpMsgBuf);
+}
+
+static HCRYPTHASH
+exsltCryptoCryptoApiCreateHash (xmlXPathParserContextPtr ctxt,
+ HCRYPTPROV hCryptProv, ALG_ID algorithm,
+ const char *msg, unsigned int msglen,
+ char *dest, unsigned int destlen)
+{
+ HCRYPTHASH hHash = 0;
+ DWORD dwHashLen = destlen;
+
+ if (!CryptCreateHash (hCryptProv, algorithm, 0, 0, &hHash)) {
+ exsltCryptoCryptoApiReportError (ctxt, __LINE__);
+ return 0;
+ }
+
+ if (!CryptHashData (hHash, (const BYTE *) msg, msglen, 0)) {
+ exsltCryptoCryptoApiReportError (ctxt, __LINE__);
+ goto fail;
+ }
+
+ if (!CryptGetHashParam (hHash, HP_HASHVAL, dest, &dwHashLen, 0)) {
+ exsltCryptoCryptoApiReportError (ctxt, __LINE__);
+ goto fail;
+ }
+
+ fail:
+ return hHash;
+}
+
+/**
+ * exsltCryptoCryptoApiHash:
+ * @ctxt: an XPath parser context
+ * @algorithm: hashing algorithm to use
+ * @msg: text to be hashed
+ * @msglen: length of text to be hashed
+ * @dest: buffer to place hash result
+ *
+ * Helper function which hashes a message using MD4, MD5, or SHA1.
+ * Uses Win32 CryptoAPI.
+ */
+static void
+exsltCryptoCryptoApiHash (xmlXPathParserContextPtr ctxt,
+ ALG_ID algorithm, const char *msg,
+ unsigned long msglen,
+ char dest[HASH_DIGEST_LENGTH]) {
+ HCRYPTPROV hCryptProv;
+ HCRYPTHASH hHash;
+
+ if (!CryptAcquireContext (&hCryptProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+ exsltCryptoCryptoApiReportError (ctxt, __LINE__);
+ return;
+ }
+
+ hHash = exsltCryptoCryptoApiCreateHash (ctxt, hCryptProv,
+ algorithm, msg, msglen,
+ dest, HASH_DIGEST_LENGTH);
+ if (0 != hHash) {
+ CryptDestroyHash (hHash);
+ }
+
+ CryptReleaseContext (hCryptProv, 0);
+}
+
+static void
+exsltCryptoCryptoApiRc4Encrypt (xmlXPathParserContextPtr ctxt,
+ const unsigned char *key,
+ const unsigned char *msg, int msglen,
+ unsigned char *dest, int destlen) {
+ HCRYPTPROV hCryptProv;
+ HCRYPTKEY hKey;
+ HCRYPTHASH hHash;
+ DWORD dwDataLen;
+ unsigned char hash[HASH_DIGEST_LENGTH];
+
+ if (msglen > destlen) {
+ xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL,
+ NULL,
+ "exslt:crypto : internal error exsltCryptoCryptoApiRc4Encrypt dest buffer too small.\n");
+ return;
+ }
+
+ if (!CryptAcquireContext (&hCryptProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+ exsltCryptoCryptoApiReportError (ctxt, __LINE__);
+ return;
+ }
+
+ hHash = exsltCryptoCryptoApiCreateHash (ctxt, hCryptProv,
+ CALG_SHA1, key,
+ RC4_KEY_LENGTH, hash,
+ HASH_DIGEST_LENGTH);
+
+ if (!CryptDeriveKey
+ (hCryptProv, CALG_RC4, hHash, 0x00800000, &hKey)) {
+ exsltCryptoCryptoApiReportError (ctxt, __LINE__);
+ goto fail;
+ }
+/* Now encrypt data. */
+ dwDataLen = msglen;
+ memcpy (dest, msg, msglen);
+ if (!CryptEncrypt (hKey, 0, TRUE, 0, dest, &dwDataLen, msglen)) {
+ exsltCryptoCryptoApiReportError (ctxt, __LINE__);
+ goto fail;
+ }
+
+ fail:
+ if (0 != hHash) {
+ CryptDestroyHash (hHash);
+ }
+
+ CryptDestroyKey (hKey);
+ CryptReleaseContext (hCryptProv, 0);
+}
+
+static void
+exsltCryptoCryptoApiRc4Decrypt (xmlXPathParserContextPtr ctxt,
+ const unsigned char *key,
+ const unsigned char *msg, int msglen,
+ unsigned char *dest, int destlen) {
+ HCRYPTPROV hCryptProv;
+ HCRYPTKEY hKey;
+ HCRYPTHASH hHash;
+ DWORD dwDataLen;
+ unsigned char hash[HASH_DIGEST_LENGTH];
+
+ if (msglen > destlen) {
+ xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL,
+ NULL,
+ "exslt:crypto : internal error exsltCryptoCryptoApiRc4Encrypt dest buffer too small.\n");
+ return;
+ }
+
+ if (!CryptAcquireContext (&hCryptProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+ exsltCryptoCryptoApiReportError (ctxt, __LINE__);
+ return;
+ }
+
+ hHash = exsltCryptoCryptoApiCreateHash (ctxt, hCryptProv,
+ CALG_SHA1, key,
+ RC4_KEY_LENGTH, hash,
+ HASH_DIGEST_LENGTH);
+
+ if (!CryptDeriveKey
+ (hCryptProv, CALG_RC4, hHash, 0x00800000, &hKey)) {
+ exsltCryptoCryptoApiReportError (ctxt, __LINE__);
+ goto fail;
+ }
+/* Now encrypt data. */
+ dwDataLen = msglen;
+ memcpy (dest, msg, msglen);
+ if (!CryptDecrypt (hKey, 0, TRUE, 0, dest, &dwDataLen)) {
+ exsltCryptoCryptoApiReportError (ctxt, __LINE__);
+ goto fail;
+ }
+
+ fail:
+ if (0 != hHash) {
+ CryptDestroyHash (hHash);
+ }
+
+ CryptDestroyKey (hKey);
+ CryptReleaseContext (hCryptProv, 0);
+}
+
+#endif /* defined(WIN32) */
+
+#if defined(HAVE_GCRYPT)
+
+#define HAVE_CRYPTO
+#define PLATFORM_HASH exsltCryptoGcryptHash
+#define PLATFORM_RC4_ENCRYPT exsltCryptoGcryptRc4Encrypt
+#define PLATFORM_RC4_DECRYPT exsltCryptoGcryptRc4Decrypt
+#define PLATFORM_MD4 GCRY_MD_MD4
+#define PLATFORM_MD5 GCRY_MD_MD5
+#define PLATFORM_SHA1 GCRY_MD_SHA1
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> /* needed by gcrypt.h 4 Jul 04 */
+#endif
+#include <gcrypt.h>
+
+static void
+exsltCryptoGcryptInit (void) {
+ static int gcrypt_init;
+ xmlLockLibrary ();
+
+ if (!gcrypt_init) {
+/* The function `gcry_check_version' must be called before any other
+ function in the library, because it initializes the thread support
+ subsystem in Libgcrypt. To achieve this in all generality, it is
+ necessary to synchronize the call to this function with all other calls
+ to functions in the library, using the synchronization mechanisms
+ available in your thread library. (from gcrypt.info)
+*/
+ gcry_check_version (GCRYPT_VERSION);
+ gcrypt_init = 1;
+ }
+
+ xmlUnlockLibrary ();
+}
+
+/**
+ * exsltCryptoGcryptHash:
+ * @ctxt: an XPath parser context
+ * @algorithm: hashing algorithm to use
+ * @msg: text to be hashed
+ * @msglen: length of text to be hashed
+ * @dest: buffer to place hash result
+ *
+ * Helper function which hashes a message using MD4, MD5, or SHA1.
+ * using gcrypt
+ */
+static void
+exsltCryptoGcryptHash (xmlXPathParserContextPtr ctxt ATTRIBUTE_UNUSED,
+/* changed the enum to int */
+ int algorithm, const char *msg,
+ unsigned long msglen,
+ char dest[HASH_DIGEST_LENGTH]) {
+ exsltCryptoGcryptInit ();
+ gcry_md_hash_buffer (algorithm, dest, msg, msglen);
+}
+
+static void
+exsltCryptoGcryptRc4Encrypt (xmlXPathParserContextPtr ctxt,
+ const unsigned char *key,
+ const unsigned char *msg, int msglen,
+ unsigned char *dest, int destlen) {
+ gcry_cipher_hd_t cipher;
+ gcry_error_t rc = 0;
+
+ exsltCryptoGcryptInit ();
+
+ rc = gcry_cipher_open (&cipher, GCRY_CIPHER_ARCFOUR,
+ GCRY_CIPHER_MODE_STREAM, 0);
+ if (rc) {
+ xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL,
+ NULL,
+ "exslt:crypto internal error %s (gcry_cipher_open)\n",
+ gcry_strerror (rc));
+ }
+
+ rc = gcry_cipher_setkey (cipher, key, RC4_KEY_LENGTH);
+ if (rc) {
+ xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL,
+ NULL,
+ "exslt:crypto internal error %s (gcry_cipher_setkey)\n",
+ gcry_strerror (rc));
+ }
+
+ rc = gcry_cipher_encrypt (cipher, (unsigned char *) dest, destlen,
+ (const unsigned char *) msg, msglen);
+ if (rc) {
+ xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL,
+ NULL,
+ "exslt:crypto internal error %s (gcry_cipher_encrypt)\n",
+ gcry_strerror (rc));
+ }
+
+ gcry_cipher_close (cipher);
+}
+
+static void
+exsltCryptoGcryptRc4Decrypt (xmlXPathParserContextPtr ctxt,
+ const unsigned char *key,
+ const unsigned char *msg, int msglen,
+ unsigned char *dest, int destlen) {
+ gcry_cipher_hd_t cipher;
+ gcry_error_t rc = 0;
+
+ exsltCryptoGcryptInit ();
+
+ rc = gcry_cipher_open (&cipher, GCRY_CIPHER_ARCFOUR,
+ GCRY_CIPHER_MODE_STREAM, 0);
+ if (rc) {
+ xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL,
+ NULL,
+ "exslt:crypto internal error %s (gcry_cipher_open)\n",
+ gcry_strerror (rc));
+ }
+
+ rc = gcry_cipher_setkey (cipher, key, RC4_KEY_LENGTH);
+ if (rc) {
+ xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL,
+ NULL,
+ "exslt:crypto internal error %s (gcry_cipher_setkey)\n",
+ gcry_strerror (rc));
+ }
+
+ rc = gcry_cipher_decrypt (cipher, (unsigned char *) dest, destlen,
+ (const unsigned char *) msg, msglen);
+ if (rc) {
+ xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL,
+ NULL,
+ "exslt:crypto internal error %s (gcry_cipher_decrypt)\n",
+ gcry_strerror (rc));
+ }
+
+ gcry_cipher_close (cipher);
+}
+
+#endif /* defined(HAVE_GCRYPT) */
+
+#if defined(HAVE_CRYPTO)
+
+/**
+ * exsltCryptoPopString:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Helper function which checks for and returns first string argument and its
+ * length in bytes.
+ */
+static int
+exsltCryptoPopString (xmlXPathParserContextPtr ctxt, int nargs,
+ xmlChar ** str) {
+
+ int str_len = 0;
+
+ if ((nargs < 1) || (nargs > 2)) {
+ xmlXPathSetArityError (ctxt);
+ return 0;
+ }
+
+ *str = xmlXPathPopString (ctxt);
+ str_len = xmlStrlen (*str);
+
+ if (str_len == 0) {
+ xmlXPathReturnEmptyString (ctxt);
+ xmlFree (*str);
+ return 0;
+ }
+
+ return str_len;
+}
+
+/**
+ * exsltCryptoMd4Function:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * computes the md4 hash of a string and returns as hex
+ */
+static void
+exsltCryptoMd4Function (xmlXPathParserContextPtr ctxt, int nargs) {
+
+ int str_len = 0;
+ xmlChar *str = NULL, *ret = NULL;
+ unsigned char hash[HASH_DIGEST_LENGTH];
+ unsigned char hex[MD5_DIGEST_LENGTH * 2 + 1];
+
+ str_len = exsltCryptoPopString (ctxt, nargs, &str);
+ if (str_len == 0)
+ return;
+
+ PLATFORM_HASH (ctxt, PLATFORM_MD4, (const char *) str, str_len,
+ (char *) hash);
+ exsltCryptoBin2Hex (hash, sizeof (hash) - 1, hex, sizeof (hex) - 1);
+
+ ret = xmlStrdup ((xmlChar *) hex);
+ xmlXPathReturnString (ctxt, ret);
+
+ if (str != NULL)
+ xmlFree (str);
+}
+
+/**
+ * exsltCryptoMd5Function:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * computes the md5 hash of a string and returns as hex
+ */
+static void
+exsltCryptoMd5Function (xmlXPathParserContextPtr ctxt, int nargs) {
+
+ int str_len = 0;
+ xmlChar *str = NULL, *ret = NULL;
+ unsigned char hash[HASH_DIGEST_LENGTH];
+ unsigned char hex[MD5_DIGEST_LENGTH * 2 + 1];
+
+ str_len = exsltCryptoPopString (ctxt, nargs, &str);
+ if (str_len == 0)
+ return;
+
+ PLATFORM_HASH (ctxt, PLATFORM_MD5, (const char *) str, str_len,
+ (char *) hash);
+ exsltCryptoBin2Hex (hash, sizeof (hash) - 1, hex, sizeof (hex) - 1);
+
+ ret = xmlStrdup ((xmlChar *) hex);
+ xmlXPathReturnString (ctxt, ret);
+
+ if (str != NULL)
+ xmlFree (str);
+}
+
+/**
+ * exsltCryptoSha1Function:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * computes the sha1 hash of a string and returns as hex
+ */
+static void
+exsltCryptoSha1Function (xmlXPathParserContextPtr ctxt, int nargs) {
+
+ int str_len = 0;
+ xmlChar *str = NULL, *ret = NULL;
+ unsigned char hash[HASH_DIGEST_LENGTH];
+ unsigned char hex[SHA1_DIGEST_LENGTH * 2 + 1];
+
+ str_len = exsltCryptoPopString (ctxt, nargs, &str);
+ if (str_len == 0)
+ return;
+
+ PLATFORM_HASH (ctxt, PLATFORM_SHA1, (const char *) str, str_len,
+ (char *) hash);
+ exsltCryptoBin2Hex (hash, sizeof (hash) - 1, hex, sizeof (hex) - 1);
+
+ ret = xmlStrdup ((xmlChar *) hex);
+ xmlXPathReturnString (ctxt, ret);
+
+ if (str != NULL)
+ xmlFree (str);
+}
+
+/**
+ * exsltCryptoRc4EncryptFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * computes the sha1 hash of a string and returns as hex
+ */
+static void
+exsltCryptoRc4EncryptFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+
+ int key_len = 0;
+ int str_len = 0, bin_len = 0, hex_len = 0;
+ xmlChar *key = NULL, *str = NULL, *padkey = NULL;
+ xmlChar *bin = NULL, *hex = NULL;
+ xsltTransformContextPtr tctxt = NULL;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError (ctxt);
+ return;
+ }
+ tctxt = xsltXPathGetTransformContext(ctxt);
+
+ str = xmlXPathPopString (ctxt);
+ str_len = xmlStrlen (str);
+
+ if (str_len == 0) {
+ xmlXPathReturnEmptyString (ctxt);
+ xmlFree (str);
+ return;
+ }
+
+ key = xmlXPathPopString (ctxt);
+ key_len = xmlStrlen (key);
+
+ if (key_len == 0) {
+ xmlXPathReturnEmptyString (ctxt);
+ xmlFree (key);
+ xmlFree (str);
+ return;
+ }
+
+ padkey = xmlMallocAtomic (RC4_KEY_LENGTH + 1);
+ if (padkey == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "exsltCryptoRc4EncryptFunction: Failed to allocate padkey\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ xmlXPathReturnEmptyString (ctxt);
+ goto done;
+ }
+ memset(padkey, 0, RC4_KEY_LENGTH + 1);
+
+ if ((key_len > RC4_KEY_LENGTH) || (key_len < 0)) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "exsltCryptoRc4EncryptFunction: key size too long or key broken\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ xmlXPathReturnEmptyString (ctxt);
+ goto done;
+ }
+ memcpy (padkey, key, key_len);
+
+/* encrypt it */
+ bin_len = str_len;
+ bin = xmlStrdup (str);
+ if (bin == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "exsltCryptoRc4EncryptFunction: Failed to allocate string\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ xmlXPathReturnEmptyString (ctxt);
+ goto done;
+ }
+ PLATFORM_RC4_ENCRYPT (ctxt, padkey, str, str_len, bin, bin_len);
+
+/* encode it */
+ hex_len = str_len * 2 + 1;
+ hex = xmlMallocAtomic (hex_len);
+ if (hex == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "exsltCryptoRc4EncryptFunction: Failed to allocate result\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ xmlXPathReturnEmptyString (ctxt);
+ goto done;
+ }
+
+ exsltCryptoBin2Hex (bin, str_len, hex, hex_len);
+ xmlXPathReturnString (ctxt, hex);
+
+done:
+ if (key != NULL)
+ xmlFree (key);
+ if (str != NULL)
+ xmlFree (str);
+ if (padkey != NULL)
+ xmlFree (padkey);
+ if (bin != NULL)
+ xmlFree (bin);
+}
+
+/**
+ * exsltCryptoRc4DecryptFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * computes the sha1 hash of a string and returns as hex
+ */
+static void
+exsltCryptoRc4DecryptFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+
+ int key_len = 0;
+ int str_len = 0, bin_len = 0, ret_len = 0;
+ xmlChar *key = NULL, *str = NULL, *padkey = NULL, *bin =
+ NULL, *ret = NULL;
+ xsltTransformContextPtr tctxt = NULL;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError (ctxt);
+ return;
+ }
+ tctxt = xsltXPathGetTransformContext(ctxt);
+
+ str = xmlXPathPopString (ctxt);
+ str_len = xmlStrlen (str);
+
+ if (str_len == 0) {
+ xmlXPathReturnEmptyString (ctxt);
+ xmlFree (str);
+ return;
+ }
+
+ key = xmlXPathPopString (ctxt);
+ key_len = xmlStrlen (key);
+
+ if (key_len == 0) {
+ xmlXPathReturnEmptyString (ctxt);
+ xmlFree (key);
+ xmlFree (str);
+ return;
+ }
+
+ padkey = xmlMallocAtomic (RC4_KEY_LENGTH + 1);
+ if (padkey == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "exsltCryptoRc4EncryptFunction: Failed to allocate padkey\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ xmlXPathReturnEmptyString (ctxt);
+ goto done;
+ }
+ memset(padkey, 0, RC4_KEY_LENGTH + 1);
+ if ((key_len > RC4_KEY_LENGTH) || (key_len < 0)) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "exsltCryptoRc4EncryptFunction: key size too long or key broken\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ xmlXPathReturnEmptyString (ctxt);
+ goto done;
+ }
+ memcpy (padkey, key, key_len);
+
+/* decode hex to binary */
+ bin_len = str_len;
+ bin = xmlMallocAtomic (bin_len);
+ if (bin == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "exsltCryptoRc4EncryptFunction: Failed to allocate string\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ xmlXPathReturnEmptyString (ctxt);
+ goto done;
+ }
+ ret_len = exsltCryptoHex2Bin (str, str_len, bin, bin_len);
+
+/* decrypt the binary blob */
+ ret = xmlMallocAtomic (ret_len + 1);
+ if (ret == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "exsltCryptoRc4EncryptFunction: Failed to allocate result\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ xmlXPathReturnEmptyString (ctxt);
+ goto done;
+ }
+ PLATFORM_RC4_DECRYPT (ctxt, padkey, bin, ret_len, ret, ret_len);
+ ret[ret_len] = 0;
+
+ xmlXPathReturnString (ctxt, ret);
+
+done:
+ if (key != NULL)
+ xmlFree (key);
+ if (str != NULL)
+ xmlFree (str);
+ if (padkey != NULL)
+ xmlFree (padkey);
+ if (bin != NULL)
+ xmlFree (bin);
+}
+
+/**
+ * exsltCryptoRegister:
+ *
+ * Registers the EXSLT - Crypto module
+ */
+
+void
+exsltCryptoRegister (void) {
+ xsltRegisterExtModuleFunction ((const xmlChar *) "md4",
+ EXSLT_CRYPTO_NAMESPACE,
+ exsltCryptoMd4Function);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "md5",
+ EXSLT_CRYPTO_NAMESPACE,
+ exsltCryptoMd5Function);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "sha1",
+ EXSLT_CRYPTO_NAMESPACE,
+ exsltCryptoSha1Function);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "rc4_encrypt",
+ EXSLT_CRYPTO_NAMESPACE,
+ exsltCryptoRc4EncryptFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "rc4_decrypt",
+ EXSLT_CRYPTO_NAMESPACE,
+ exsltCryptoRc4DecryptFunction);
+}
+
+#else
+/**
+ * exsltCryptoRegister:
+ *
+ * Registers the EXSLT - Crypto module
+ */
+void
+exsltCryptoRegister (void) {
+}
+
+#endif /* defined(HAVE_CRYPTO) */
+
+#endif /* EXSLT_CRYPTO_ENABLED */
diff --git a/libxslt/libexslt/date.c b/libxslt/libexslt/date.c
new file mode 100644
index 0000000..3af6f7f
--- /dev/null
+++ b/libxslt/libexslt/date.c
@@ -0,0 +1,3911 @@
+/*
+ * date.c: Implementation of the EXSLT -- Dates and Times module
+ *
+ * References:
+ * http://www.exslt.org/date/date.html
+ *
+ * See Copyright for the status of this software.
+ *
+ * Authors:
+ * Charlie Bozeman <cbozeman@HiWAAY.net>
+ * Thomas Broyer <tbroyer@ltgt.net>
+ *
+ * TODO:
+ * elements:
+ * date-format
+ * functions:
+ * format-date
+ * parse-date
+ * sum
+ */
+
+#define IN_LIBEXSLT
+#include "libexslt/libexslt.h"
+
+#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#if defined(HAVE_LOCALTIME_R) && defined(__GLIBC__) /* _POSIX_SOURCE required by gnu libc */
+#ifndef _AIX51 /* but on AIX we're not using gnu libc */
+#define _POSIX_SOURCE
+#endif
+#endif
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <libxslt/xsltconfig.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/extensions.h>
+
+#include "exslt.h"
+
+#include <string.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
+/* needed to get localtime_r on Solaris */
+#ifdef __sun
+#ifndef __EXTENSIONS__
+#define __EXTENSIONS__
+#endif
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+/*
+ * types of date and/or time (from schema datatypes)
+ * somewhat ordered from least specific to most specific (i.e.
+ * most truncated to least truncated).
+ */
+typedef enum {
+ EXSLT_UNKNOWN = 0,
+ XS_TIME = 1, /* time is left-truncated */
+ XS_GDAY = (XS_TIME << 1),
+ XS_GMONTH = (XS_GDAY << 1),
+ XS_GMONTHDAY = (XS_GMONTH | XS_GDAY),
+ XS_GYEAR = (XS_GMONTH << 1),
+ XS_GYEARMONTH = (XS_GYEAR | XS_GMONTH),
+ XS_DATE = (XS_GYEAR | XS_GMONTH | XS_GDAY),
+ XS_DATETIME = (XS_DATE | XS_TIME),
+ XS_DURATION = (XS_GYEAR << 1)
+} exsltDateType;
+
+/* Date value */
+typedef struct _exsltDateValDate exsltDateValDate;
+typedef exsltDateValDate *exsltDateValDatePtr;
+struct _exsltDateValDate {
+ long year;
+ unsigned int mon :4; /* 1 <= mon <= 12 */
+ unsigned int day :5; /* 1 <= day <= 31 */
+ unsigned int hour :5; /* 0 <= hour <= 23 */
+ unsigned int min :6; /* 0 <= min <= 59 */
+ double sec;
+ unsigned int tz_flag :1; /* is tzo explicitely set? */
+ signed int tzo :12; /* -1440 <= tzo <= 1440 currently only -840 to +840 are needed */
+};
+
+/* Duration value */
+typedef struct _exsltDateValDuration exsltDateValDuration;
+typedef exsltDateValDuration *exsltDateValDurationPtr;
+struct _exsltDateValDuration {
+ long mon; /* mon stores years also */
+ long day;
+ double sec; /* sec stores min and hour also */
+};
+
+typedef struct _exsltDateVal exsltDateVal;
+typedef exsltDateVal *exsltDateValPtr;
+struct _exsltDateVal {
+ exsltDateType type;
+ union {
+ exsltDateValDate date;
+ exsltDateValDuration dur;
+ } value;
+};
+
+/****************************************************************
+ * *
+ * Compat./Port. macros *
+ * *
+ ****************************************************************/
+
+#if defined(HAVE_TIME_H) \
+ && (defined(HAVE_LOCALTIME) || defined(HAVE_LOCALTIME_R)) \
+ && (defined(HAVE_GMTIME) || defined(HAVE_GMTIME_R)) \
+ && defined(HAVE_TIME)
+#define WITH_TIME
+#endif
+
+/****************************************************************
+ * *
+ * Convenience macros and functions *
+ * *
+ ****************************************************************/
+
+#define IS_TZO_CHAR(c) \
+ ((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
+
+#define VALID_ALWAYS(num) (num >= 0)
+#define VALID_YEAR(yr) (yr != 0)
+#define VALID_MONTH(mon) ((mon >= 1) && (mon <= 12))
+/* VALID_DAY should only be used when month is unknown */
+#define VALID_DAY(day) ((day >= 1) && (day <= 31))
+#define VALID_HOUR(hr) ((hr >= 0) && (hr <= 23))
+#define VALID_MIN(min) ((min >= 0) && (min <= 59))
+#define VALID_SEC(sec) ((sec >= 0) && (sec < 60))
+#define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440))
+#define IS_LEAP(y) \
+ (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
+
+static const unsigned long daysInMonth[12] =
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static const unsigned long daysInMonthLeap[12] =
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+#define MAX_DAYINMONTH(yr,mon) \
+ (IS_LEAP(yr) ? daysInMonthLeap[mon - 1] : daysInMonth[mon - 1])
+
+#define VALID_MDAY(dt) \
+ (IS_LEAP(dt->year) ? \
+ (dt->day <= daysInMonthLeap[dt->mon - 1]) : \
+ (dt->day <= daysInMonth[dt->mon - 1]))
+
+#define VALID_DATE(dt) \
+ (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
+
+/*
+ hour and min structure vals are unsigned, so normal macros give
+ warnings on some compilers.
+*/
+#define VALID_TIME(dt) \
+ ((dt->hour <=23 ) && (dt->min <= 59) && \
+ VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
+
+#define VALID_DATETIME(dt) \
+ (VALID_DATE(dt) && VALID_TIME(dt))
+
+#define SECS_PER_MIN (60)
+#define SECS_PER_HOUR (60 * SECS_PER_MIN)
+#define SECS_PER_DAY (24 * SECS_PER_HOUR)
+
+static const unsigned long dayInYearByMonth[12] =
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+static const unsigned long dayInLeapYearByMonth[12] =
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
+
+#define DAY_IN_YEAR(day, month, year) \
+ ((IS_LEAP(year) ? \
+ dayInLeapYearByMonth[month - 1] : \
+ dayInYearByMonth[month - 1]) + day)
+
+/**
+ * _exsltDateParseGYear:
+ * @dt: pointer to a date structure
+ * @str: pointer to the string to analyze
+ *
+ * Parses a xs:gYear without time zone and fills in the appropriate
+ * field of the @dt structure. @str is updated to point just after the
+ * xs:gYear. It is supposed that @dt->year is big enough to contain
+ * the year.
+ *
+ * Returns 0 or the error code
+ */
+static int
+_exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str)
+{
+ const xmlChar *cur = *str, *firstChar;
+ int isneg = 0, digcnt = 0;
+
+ if (((*cur < '0') || (*cur > '9')) &&
+ (*cur != '-') && (*cur != '+'))
+ return -1;
+
+ if (*cur == '-') {
+ isneg = 1;
+ cur++;
+ }
+
+ firstChar = cur;
+
+ while ((*cur >= '0') && (*cur <= '9')) {
+ dt->year = dt->year * 10 + (*cur - '0');
+ cur++;
+ digcnt++;
+ }
+
+ /* year must be at least 4 digits (CCYY); over 4
+ * digits cannot have a leading zero. */
+ if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
+ return 1;
+
+ if (isneg)
+ dt->year = - dt->year;
+
+ if (!VALID_YEAR(dt->year))
+ return 2;
+
+ *str = cur;
+
+#ifdef DEBUG_EXSLT_DATE
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Parsed year %04i\n", dt->year);
+#endif
+
+ return 0;
+}
+
+/**
+ * FORMAT_GYEAR:
+ * @yr: the year to format
+ * @cur: a pointer to an allocated buffer
+ *
+ * Formats @yr in xsl:gYear format. Result is appended to @cur and
+ * @cur is updated to point after the xsl:gYear.
+ */
+#define FORMAT_GYEAR(yr, cur) \
+ if (yr < 0) { \
+ *cur = '-'; \
+ cur++; \
+ } \
+ { \
+ long year = (yr < 0) ? - yr : yr; \
+ xmlChar tmp_buf[100], *tmp = tmp_buf; \
+ /* result is in reverse-order */ \
+ while (year > 0) { \
+ *tmp = '0' + (xmlChar)(year % 10); \
+ year /= 10; \
+ tmp++; \
+ } \
+ /* virtually adds leading zeros */ \
+ while ((tmp - tmp_buf) < 4) \
+ *tmp++ = '0'; \
+ /* restore the correct order */ \
+ while (tmp > tmp_buf) { \
+ tmp--; \
+ *cur = *tmp; \
+ cur++; \
+ } \
+ }
+
+/**
+ * PARSE_2_DIGITS:
+ * @num: the integer to fill in
+ * @cur: an #xmlChar *
+ * @func: validation function for the number
+ * @invalid: an integer
+ *
+ * Parses a 2-digits integer and updates @num with the value. @cur is
+ * updated to point just after the integer.
+ * In case of error, @invalid is set to %TRUE, values of @num and
+ * @cur are undefined.
+ */
+#define PARSE_2_DIGITS(num, cur, func, invalid) \
+ if ((cur[0] < '0') || (cur[0] > '9') || \
+ (cur[1] < '0') || (cur[1] > '9')) \
+ invalid = 1; \
+ else { \
+ int val; \
+ val = (cur[0] - '0') * 10 + (cur[1] - '0'); \
+ if (!func(val)) \
+ invalid = 2; \
+ else \
+ num = val; \
+ } \
+ cur += 2;
+
+/**
+ * FORMAT_2_DIGITS:
+ * @num: the integer to format
+ * @cur: a pointer to an allocated buffer
+ *
+ * Formats a 2-digits integer. Result is appended to @cur and
+ * @cur is updated to point after the integer.
+ */
+#define FORMAT_2_DIGITS(num, cur) \
+ *cur = '0' + ((num / 10) % 10); \
+ cur++; \
+ *cur = '0' + (num % 10); \
+ cur++;
+
+/**
+ * PARSE_FLOAT:
+ * @num: the double to fill in
+ * @cur: an #xmlChar *
+ * @invalid: an integer
+ *
+ * Parses a float and updates @num with the value. @cur is
+ * updated to point just after the float. The float must have a
+ * 2-digits integer part and may or may not have a decimal part.
+ * In case of error, @invalid is set to %TRUE, values of @num and
+ * @cur are undefined.
+ */
+#define PARSE_FLOAT(num, cur, invalid) \
+ PARSE_2_DIGITS(num, cur, VALID_ALWAYS, invalid); \
+ if (!invalid && (*cur == '.')) { \
+ double mult = 1; \
+ cur++; \
+ if ((*cur < '0') || (*cur > '9')) \
+ invalid = 1; \
+ while ((*cur >= '0') && (*cur <= '9')) { \
+ mult /= 10; \
+ num += (*cur - '0') * mult; \
+ cur++; \
+ } \
+ }
+
+/**
+ * FORMAT_FLOAT:
+ * @num: the double to format
+ * @cur: a pointer to an allocated buffer
+ * @pad: a flag for padding to 2 integer digits
+ *
+ * Formats a float. Result is appended to @cur and @cur is updated to
+ * point after the integer. If the @pad flag is non-zero, then the
+ * float representation has a minimum 2-digits integer part. The
+ * fractional part is formatted if @num has a fractional value.
+ */
+#define FORMAT_FLOAT(num, cur, pad) \
+ { \
+ xmlChar *sav, *str; \
+ if ((pad) && (num < 10.0)) \
+ *cur++ = '0'; \
+ str = xmlXPathCastNumberToString(num); \
+ sav = str; \
+ while (*str != 0) \
+ *cur++ = *str++; \
+ xmlFree(sav); \
+ }
+
+/**
+ * _exsltDateParseGMonth:
+ * @dt: pointer to a date structure
+ * @str: pointer to the string to analyze
+ *
+ * Parses a xs:gMonth without time zone and fills in the appropriate
+ * field of the @dt structure. @str is updated to point just after the
+ * xs:gMonth.
+ *
+ * Returns 0 or the error code
+ */
+static int
+_exsltDateParseGMonth (exsltDateValDatePtr dt, const xmlChar **str)
+{
+ const xmlChar *cur = *str;
+ int ret = 0;
+
+ PARSE_2_DIGITS(dt->mon, cur, VALID_MONTH, ret);
+ if (ret != 0)
+ return ret;
+
+ *str = cur;
+
+#ifdef DEBUG_EXSLT_DATE
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Parsed month %02i\n", dt->mon);
+#endif
+
+ return 0;
+}
+
+/**
+ * FORMAT_GMONTH:
+ * @mon: the month to format
+ * @cur: a pointer to an allocated buffer
+ *
+ * Formats @mon in xsl:gMonth format. Result is appended to @cur and
+ * @cur is updated to point after the xsl:gMonth.
+ */
+#define FORMAT_GMONTH(mon, cur) \
+ FORMAT_2_DIGITS(mon, cur)
+
+/**
+ * _exsltDateParseGDay:
+ * @dt: pointer to a date structure
+ * @str: pointer to the string to analyze
+ *
+ * Parses a xs:gDay without time zone and fills in the appropriate
+ * field of the @dt structure. @str is updated to point just after the
+ * xs:gDay.
+ *
+ * Returns 0 or the error code
+ */
+static int
+_exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str)
+{
+ const xmlChar *cur = *str;
+ int ret = 0;
+
+ PARSE_2_DIGITS(dt->day, cur, VALID_DAY, ret);
+ if (ret != 0)
+ return ret;
+
+ *str = cur;
+
+#ifdef DEBUG_EXSLT_DATE
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Parsed day %02i\n", dt->day);
+#endif
+
+ return 0;
+}
+
+/**
+ * FORMAT_GDAY:
+ * @dt: the #exsltDateValDate to format
+ * @cur: a pointer to an allocated buffer
+ *
+ * Formats @dt in xsl:gDay format. Result is appended to @cur and
+ * @cur is updated to point after the xsl:gDay.
+ */
+#define FORMAT_GDAY(dt, cur) \
+ FORMAT_2_DIGITS(dt->day, cur)
+
+/**
+ * FORMAT_DATE:
+ * @dt: the #exsltDateValDate to format
+ * @cur: a pointer to an allocated buffer
+ *
+ * Formats @dt in xsl:date format. Result is appended to @cur and
+ * @cur is updated to point after the xsl:date.
+ */
+#define FORMAT_DATE(dt, cur) \
+ FORMAT_GYEAR(dt->year, cur); \
+ *cur = '-'; \
+ cur++; \
+ FORMAT_GMONTH(dt->mon, cur); \
+ *cur = '-'; \
+ cur++; \
+ FORMAT_GDAY(dt, cur);
+
+/**
+ * _exsltDateParseTime:
+ * @dt: pointer to a date structure
+ * @str: pointer to the string to analyze
+ *
+ * Parses a xs:time without time zone and fills in the appropriate
+ * fields of the @dt structure. @str is updated to point just after the
+ * xs:time.
+ * In case of error, values of @dt fields are undefined.
+ *
+ * Returns 0 or the error code
+ */
+static int
+_exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str)
+{
+ const xmlChar *cur = *str;
+ unsigned int hour = 0; /* use temp var in case str is not xs:time */
+ int ret = 0;
+
+ PARSE_2_DIGITS(hour, cur, VALID_HOUR, ret);
+ if (ret != 0)
+ return ret;
+
+ if (*cur != ':')
+ return 1;
+ cur++;
+
+ /* the ':' insures this string is xs:time */
+ dt->hour = hour;
+
+ PARSE_2_DIGITS(dt->min, cur, VALID_MIN, ret);
+ if (ret != 0)
+ return ret;
+
+ if (*cur != ':')
+ return 1;
+ cur++;
+
+ PARSE_FLOAT(dt->sec, cur, ret);
+ if (ret != 0)
+ return ret;
+
+ if (!VALID_TIME(dt))
+ return 2;
+
+ *str = cur;
+
+#ifdef DEBUG_EXSLT_DATE
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Parsed time %02i:%02i:%02.f\n",
+ dt->hour, dt->min, dt->sec);
+#endif
+
+ return 0;
+}
+
+/**
+ * FORMAT_TIME:
+ * @dt: the #exsltDateValDate to format
+ * @cur: a pointer to an allocated buffer
+ *
+ * Formats @dt in xsl:time format. Result is appended to @cur and
+ * @cur is updated to point after the xsl:time.
+ */
+#define FORMAT_TIME(dt, cur) \
+ FORMAT_2_DIGITS(dt->hour, cur); \
+ *cur = ':'; \
+ cur++; \
+ FORMAT_2_DIGITS(dt->min, cur); \
+ *cur = ':'; \
+ cur++; \
+ FORMAT_FLOAT(dt->sec, cur, 1);
+
+/**
+ * _exsltDateParseTimeZone:
+ * @dt: pointer to a date structure
+ * @str: pointer to the string to analyze
+ *
+ * Parses a time zone without time zone and fills in the appropriate
+ * field of the @dt structure. @str is updated to point just after the
+ * time zone.
+ *
+ * Returns 0 or the error code
+ */
+static int
+_exsltDateParseTimeZone (exsltDateValDatePtr dt, const xmlChar **str)
+{
+ const xmlChar *cur;
+ int ret = 0;
+
+ if (str == NULL)
+ return -1;
+ cur = *str;
+ switch (*cur) {
+ case 0:
+ dt->tz_flag = 0;
+ dt->tzo = 0;
+ break;
+
+ case 'Z':
+ dt->tz_flag = 1;
+ dt->tzo = 0;
+ cur++;
+ break;
+
+ case '+':
+ case '-': {
+ int isneg = 0, tmp = 0;
+ isneg = (*cur == '-');
+
+ cur++;
+
+ PARSE_2_DIGITS(tmp, cur, VALID_HOUR, ret);
+ if (ret != 0)
+ return ret;
+
+ if (*cur != ':')
+ return 1;
+ cur++;
+
+ dt->tzo = tmp * 60;
+
+ PARSE_2_DIGITS(tmp, cur, VALID_MIN, ret);
+ if (ret != 0)
+ return ret;
+
+ dt->tzo += tmp;
+ if (isneg)
+ dt->tzo = - dt->tzo;
+
+ if (!VALID_TZO(dt->tzo))
+ return 2;
+
+ break;
+ }
+ default:
+ return 1;
+ }
+
+ *str = cur;
+
+#ifdef DEBUG_EXSLT_DATE
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Parsed time zone offset (%s) %i\n",
+ dt->tz_flag ? "explicit" : "implicit", dt->tzo);
+#endif
+
+ return 0;
+}
+
+/**
+ * FORMAT_TZ:
+ * @tzo: the timezone offset to format
+ * @cur: a pointer to an allocated buffer
+ *
+ * Formats @tzo timezone. Result is appended to @cur and
+ * @cur is updated to point after the timezone.
+ */
+#define FORMAT_TZ(tzo, cur) \
+ if (tzo == 0) { \
+ *cur = 'Z'; \
+ cur++; \
+ } else { \
+ int aTzo = (tzo < 0) ? - tzo : tzo; \
+ int tzHh = aTzo / 60, tzMm = aTzo % 60; \
+ *cur = (tzo < 0) ? '-' : '+' ; \
+ cur++; \
+ FORMAT_2_DIGITS(tzHh, cur); \
+ *cur = ':'; \
+ cur++; \
+ FORMAT_2_DIGITS(tzMm, cur); \
+ }
+
+/****************************************************************
+ * *
+ * XML Schema Dates/Times Datatypes Handling *
+ * *
+ ****************************************************************/
+
+/**
+ * exsltDateCreateDate:
+ * @type: type to create
+ *
+ * Creates a new #exsltDateVal, uninitialized.
+ *
+ * Returns the #exsltDateValPtr
+ */
+static exsltDateValPtr
+exsltDateCreateDate (exsltDateType type)
+{
+ exsltDateValPtr ret;
+
+ ret = (exsltDateValPtr) xmlMalloc(sizeof(exsltDateVal));
+ if (ret == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltDateCreateDate: out of memory\n");
+ return (NULL);
+ }
+ memset (ret, 0, sizeof(exsltDateVal));
+
+ if (type != XS_DURATION) {
+ ret->value.date.mon = 1;
+ ret->value.date.day = 1;
+ }
+
+ if (type != EXSLT_UNKNOWN)
+ ret->type = type;
+
+ return ret;
+}
+
+/**
+ * exsltDateFreeDate:
+ * @date: an #exsltDateValPtr
+ *
+ * Frees up the @date
+ */
+static void
+exsltDateFreeDate (exsltDateValPtr date) {
+ if (date == NULL)
+ return;
+
+ xmlFree(date);
+}
+
+/**
+ * PARSE_DIGITS:
+ * @num: the integer to fill in
+ * @cur: an #xmlChar *
+ * @num_type: an integer flag
+ *
+ * Parses a digits integer and updates @num with the value. @cur is
+ * updated to point just after the integer.
+ * In case of error, @num_type is set to -1, values of @num and
+ * @cur are undefined.
+ */
+#define PARSE_DIGITS(num, cur, num_type) \
+ if ((*cur < '0') || (*cur > '9')) \
+ num_type = -1; \
+ else \
+ while ((*cur >= '0') && (*cur <= '9')) { \
+ num = num * 10 + (*cur - '0'); \
+ cur++; \
+ }
+
+/**
+ * PARSE_NUM:
+ * @num: the double to fill in
+ * @cur: an #xmlChar *
+ * @num_type: an integer flag
+ *
+ * Parses a float or integer and updates @num with the value. @cur is
+ * updated to point just after the number. If the number is a float,
+ * then it must have an integer part and a decimal part; @num_type will
+ * be set to 1. If there is no decimal part, @num_type is set to zero.
+ * In case of error, @num_type is set to -1, values of @num and
+ * @cur are undefined.
+ */
+#define PARSE_NUM(num, cur, num_type) \
+ num = 0; \
+ PARSE_DIGITS(num, cur, num_type); \
+ if (!num_type && (*cur == '.')) { \
+ double mult = 1; \
+ cur++; \
+ if ((*cur < '0') || (*cur > '9')) \
+ num_type = -1; \
+ else \
+ num_type = 1; \
+ while ((*cur >= '0') && (*cur <= '9')) { \
+ mult /= 10; \
+ num += (*cur - '0') * mult; \
+ cur++; \
+ } \
+ }
+
+#ifdef WITH_TIME
+/**
+ * exsltDateCurrent:
+ *
+ * Returns the current date and time.
+ */
+static exsltDateValPtr
+exsltDateCurrent (void)
+{
+ struct tm localTm, gmTm;
+#ifndef HAVE_GMTIME_R
+ struct tm *tb = NULL;
+#endif
+ time_t secs;
+ int local_s, gm_s;
+ exsltDateValPtr ret;
+#ifdef HAVE_ERRNO_H
+ char *source_date_epoch;
+#endif /* HAVE_ERRNO_H */
+ int override = 0;
+
+ ret = exsltDateCreateDate(XS_DATETIME);
+ if (ret == NULL)
+ return NULL;
+
+#ifdef HAVE_ERRNO_H
+ /*
+ * Allow the date and time to be set externally by an exported
+ * environment variable to enable reproducible builds.
+ */
+ source_date_epoch = getenv("SOURCE_DATE_EPOCH");
+ if (source_date_epoch) {
+ errno = 0;
+ secs = (time_t) strtol (source_date_epoch, NULL, 10);
+ if (errno == 0) {
+#if HAVE_GMTIME_R
+ if (gmtime_r(&secs, &localTm) != NULL)
+ override = 1;
+#else
+ tb = gmtime(&secs);
+ if (tb != NULL) {
+ localTm = *tb;
+ override = 1;
+ }
+#endif
+ }
+ }
+#endif /* HAVE_ERRNO_H */
+
+ if (override == 0) {
+ /* get current time */
+ secs = time(NULL);
+
+#if HAVE_LOCALTIME_R
+ localtime_r(&secs, &localTm);
+#else
+ localTm = *localtime(&secs);
+#endif
+ }
+
+ /* get real year, not years since 1900 */
+ ret->value.date.year = localTm.tm_year + 1900;
+
+ ret->value.date.mon = localTm.tm_mon + 1;
+ ret->value.date.day = localTm.tm_mday;
+ ret->value.date.hour = localTm.tm_hour;
+ ret->value.date.min = localTm.tm_min;
+
+ /* floating point seconds */
+ ret->value.date.sec = (double) localTm.tm_sec;
+
+ /* determine the time zone offset from local to gm time */
+#if HAVE_GMTIME_R
+ gmtime_r(&secs, &gmTm);
+#else
+ tb = gmtime(&secs);
+ if (tb != NULL)
+ gmTm = *tb;
+#endif
+ ret->value.date.tz_flag = 0;
+#if 0
+ ret->value.date.tzo = (((ret->value.date.day * 1440) +
+ (ret->value.date.hour * 60) +
+ ret->value.date.min) -
+ ((gmTm.tm_mday * 1440) + (gmTm.tm_hour * 60) +
+ gmTm.tm_min));
+#endif
+ local_s = localTm.tm_hour * SECS_PER_HOUR +
+ localTm.tm_min * SECS_PER_MIN +
+ localTm.tm_sec;
+
+ gm_s = gmTm.tm_hour * SECS_PER_HOUR +
+ gmTm.tm_min * SECS_PER_MIN +
+ gmTm.tm_sec;
+
+ if (localTm.tm_year < gmTm.tm_year) {
+ ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
+ } else if (localTm.tm_year > gmTm.tm_year) {
+ ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
+ } else if (localTm.tm_mon < gmTm.tm_mon) {
+ ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
+ } else if (localTm.tm_mon > gmTm.tm_mon) {
+ ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
+ } else if (localTm.tm_mday < gmTm.tm_mday) {
+ ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
+ } else if (localTm.tm_mday > gmTm.tm_mday) {
+ ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
+ } else {
+ ret->value.date.tzo = (local_s - gm_s)/60;
+ }
+
+ return ret;
+}
+#endif
+
+/**
+ * exsltDateParse:
+ * @dateTime: string to analyze
+ *
+ * Parses a date/time string
+ *
+ * Returns a newly built #exsltDateValPtr of NULL in case of error
+ */
+static exsltDateValPtr
+exsltDateParse (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ int ret;
+ const xmlChar *cur = dateTime;
+
+#define RETURN_TYPE_IF_VALID(t) \
+ if (IS_TZO_CHAR(*cur)) { \
+ ret = _exsltDateParseTimeZone(&(dt->value.date), &cur); \
+ if (ret == 0) { \
+ if (*cur != 0) \
+ goto error; \
+ dt->type = t; \
+ return dt; \
+ } \
+ }
+
+ if (dateTime == NULL)
+ return NULL;
+
+ if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
+ return NULL;
+
+ dt = exsltDateCreateDate(EXSLT_UNKNOWN);
+ if (dt == NULL)
+ return NULL;
+
+ if ((cur[0] == '-') && (cur[1] == '-')) {
+ /*
+ * It's an incomplete date (xs:gMonthDay, xs:gMonth or
+ * xs:gDay)
+ */
+ cur += 2;
+
+ /* is it an xs:gDay? */
+ if (*cur == '-') {
+ ++cur;
+ ret = _exsltDateParseGDay(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ RETURN_TYPE_IF_VALID(XS_GDAY);
+
+ goto error;
+ }
+
+ /*
+ * it should be an xs:gMonthDay or xs:gMonth
+ */
+ ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ if (*cur != '-')
+ goto error;
+ cur++;
+
+ /* is it an xs:gMonth? */
+ if (*cur == '-') {
+ cur++;
+ RETURN_TYPE_IF_VALID(XS_GMONTH);
+ goto error;
+ }
+
+ /* it should be an xs:gMonthDay */
+ ret = _exsltDateParseGDay(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ RETURN_TYPE_IF_VALID(XS_GMONTHDAY);
+
+ goto error;
+ }
+
+ /*
+ * It's a right-truncated date or an xs:time.
+ * Try to parse an xs:time then fallback on right-truncated dates.
+ */
+ if ((*cur >= '0') && (*cur <= '9')) {
+ ret = _exsltDateParseTime(&(dt->value.date), &cur);
+ if (ret == 0) {
+ /* it's an xs:time */
+ RETURN_TYPE_IF_VALID(XS_TIME);
+ }
+ }
+
+ /* fallback on date parsing */
+ cur = dateTime;
+
+ ret = _exsltDateParseGYear(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ /* is it an xs:gYear? */
+ RETURN_TYPE_IF_VALID(XS_GYEAR);
+
+ if (*cur != '-')
+ goto error;
+ cur++;
+
+ ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ /* is it an xs:gYearMonth? */
+ RETURN_TYPE_IF_VALID(XS_GYEARMONTH);
+
+ if (*cur != '-')
+ goto error;
+ cur++;
+
+ ret = _exsltDateParseGDay(&(dt->value.date), &cur);
+ if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
+ goto error;
+
+ /* is it an xs:date? */
+ RETURN_TYPE_IF_VALID(XS_DATE);
+
+ if (*cur != 'T')
+ goto error;
+ cur++;
+
+ /* it should be an xs:dateTime */
+ ret = _exsltDateParseTime(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ ret = _exsltDateParseTimeZone(&(dt->value.date), &cur);
+ if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
+ goto error;
+
+ dt->type = XS_DATETIME;
+
+ return dt;
+
+error:
+ if (dt != NULL)
+ exsltDateFreeDate(dt);
+ return NULL;
+}
+
+/**
+ * exsltDateParseDuration:
+ * @duration: string to analyze
+ *
+ * Parses a duration string
+ *
+ * Returns a newly built #exsltDateValPtr of NULL in case of error
+ */
+static exsltDateValPtr
+exsltDateParseDuration (const xmlChar *duration)
+{
+ const xmlChar *cur = duration;
+ exsltDateValPtr dur;
+ int isneg = 0;
+ unsigned int seq = 0;
+
+ if (duration == NULL)
+ return NULL;
+
+ if (*cur == '-') {
+ isneg = 1;
+ cur++;
+ }
+
+ /* duration must start with 'P' (after sign) */
+ if (*cur++ != 'P')
+ return NULL;
+
+ dur = exsltDateCreateDate(XS_DURATION);
+ if (dur == NULL)
+ return NULL;
+
+ while (*cur != 0) {
+ double num;
+ int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
+ const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
+ const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
+
+ /* input string should be empty or invalid date/time item */
+ if (seq >= sizeof(desig))
+ goto error;
+
+ /* T designator must be present for time items */
+ if (*cur == 'T') {
+ if (seq <= 3) {
+ seq = 3;
+ cur++;
+ } else
+ return NULL;
+ } else if (seq == 3)
+ goto error;
+
+ /* parse the number portion of the item */
+ PARSE_NUM(num, cur, num_type);
+
+ if ((num_type == -1) || (*cur == 0))
+ goto error;
+
+ /* update duration based on item type */
+ while (seq < sizeof(desig)) {
+ if (*cur == desig[seq]) {
+
+ /* verify numeric type; only seconds can be float */
+ if ((num_type != 0) && (seq < (sizeof(desig)-1)))
+ goto error;
+
+ switch (seq) {
+ case 0:
+ dur->value.dur.mon = (long)num * 12;
+ break;
+ case 1:
+ dur->value.dur.mon += (long)num;
+ break;
+ default:
+ /* convert to seconds using multiplier */
+ dur->value.dur.sec += num * multi[seq];
+ seq++;
+ break;
+ }
+
+ break; /* exit loop */
+ }
+ /* no date designators found? */
+ if (++seq == 3)
+ goto error;
+ }
+ cur++;
+ }
+
+ if (isneg) {
+ dur->value.dur.mon = -dur->value.dur.mon;
+ dur->value.dur.day = -dur->value.dur.day;
+ dur->value.dur.sec = -dur->value.dur.sec;
+ }
+
+#ifdef DEBUG_EXSLT_DATE
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Parsed duration %f\n", dur->value.dur.sec);
+#endif
+
+ return dur;
+
+error:
+ if (dur != NULL)
+ exsltDateFreeDate(dur);
+ return NULL;
+}
+
+/**
+ * FORMAT_ITEM:
+ * @num: number to format
+ * @cur: current location to convert number
+ * @limit: max value
+ * @item: char designator
+ *
+ */
+#define FORMAT_ITEM(num, cur, limit, item) \
+ if (num != 0) { \
+ long comp = (long)num / limit; \
+ if (comp != 0) { \
+ FORMAT_FLOAT((double)comp, cur, 0); \
+ *cur++ = item; \
+ num -= (double)(comp * limit); \
+ } \
+ }
+
+/**
+ * exsltDateFormatDuration:
+ * @dt: an #exsltDateValDurationPtr
+ *
+ * Formats @dt in xs:duration format.
+ *
+ * Returns a newly allocated string, or NULL in case of error
+ */
+static xmlChar *
+exsltDateFormatDuration (const exsltDateValDurationPtr dt)
+{
+ xmlChar buf[100], *cur = buf;
+ double secs, days;
+ double years, months;
+
+ if (dt == NULL)
+ return NULL;
+
+ /* quick and dirty check */
+ if ((dt->sec == 0.0) && (dt->day == 0) && (dt->mon == 0))
+ return xmlStrdup((xmlChar*)"P0D");
+
+ secs = dt->sec;
+ days = (double)dt->day;
+ years = (double)(dt->mon / 12);
+ months = (double)(dt->mon % 12);
+
+ *cur = '\0';
+ if (secs < 0.0) {
+ secs = -secs;
+ *cur = '-';
+ }
+ if (days < 0) {
+ days = -days;
+ *cur = '-';
+ }
+ if (years < 0) {
+ years = -years;
+ *cur = '-';
+ }
+ if (months < 0) {
+ months = -months;
+ *cur = '-';
+ }
+ if (*cur == '-')
+ cur++;
+
+ *cur++ = 'P';
+
+ if (years != 0.0) {
+ FORMAT_ITEM(years, cur, 1, 'Y');
+ }
+
+ if (months != 0.0) {
+ FORMAT_ITEM(months, cur, 1, 'M');
+ }
+
+ if (secs >= SECS_PER_DAY) {
+ double tmp = floor(secs / SECS_PER_DAY);
+ days += tmp;
+ secs -= (tmp * SECS_PER_DAY);
+ }
+
+ FORMAT_ITEM(days, cur, 1, 'D');
+ if (secs > 0.0) {
+ *cur++ = 'T';
+ }
+ FORMAT_ITEM(secs, cur, SECS_PER_HOUR, 'H');
+ FORMAT_ITEM(secs, cur, SECS_PER_MIN, 'M');
+ if (secs > 0.0) {
+ FORMAT_FLOAT(secs, cur, 0);
+ *cur++ = 'S';
+ }
+
+ *cur = 0;
+
+ return xmlStrdup(buf);
+}
+
+/**
+ * exsltDateFormatDateTime:
+ * @dt: an #exsltDateValDatePtr
+ *
+ * Formats @dt in xs:dateTime format.
+ *
+ * Returns a newly allocated string, or NULL in case of error
+ */
+static xmlChar *
+exsltDateFormatDateTime (const exsltDateValDatePtr dt)
+{
+ xmlChar buf[100], *cur = buf;
+
+ if ((dt == NULL) || !VALID_DATETIME(dt))
+ return NULL;
+
+ FORMAT_DATE(dt, cur);
+ *cur = 'T';
+ cur++;
+ FORMAT_TIME(dt, cur);
+ FORMAT_TZ(dt->tzo, cur);
+ *cur = 0;
+
+ return xmlStrdup(buf);
+}
+
+/**
+ * exsltDateFormatDate:
+ * @dt: an #exsltDateValDatePtr
+ *
+ * Formats @dt in xs:date format.
+ *
+ * Returns a newly allocated string, or NULL in case of error
+ */
+static xmlChar *
+exsltDateFormatDate (const exsltDateValDatePtr dt)
+{
+ xmlChar buf[100], *cur = buf;
+
+ if ((dt == NULL) || !VALID_DATETIME(dt))
+ return NULL;
+
+ FORMAT_DATE(dt, cur);
+ if (dt->tz_flag || (dt->tzo != 0)) {
+ FORMAT_TZ(dt->tzo, cur);
+ }
+ *cur = 0;
+
+ return xmlStrdup(buf);
+}
+
+/**
+ * exsltDateFormatTime:
+ * @dt: an #exsltDateValDatePtr
+ *
+ * Formats @dt in xs:time format.
+ *
+ * Returns a newly allocated string, or NULL in case of error
+ */
+static xmlChar *
+exsltDateFormatTime (const exsltDateValDatePtr dt)
+{
+ xmlChar buf[100], *cur = buf;
+
+ if ((dt == NULL) || !VALID_TIME(dt))
+ return NULL;
+
+ FORMAT_TIME(dt, cur);
+ if (dt->tz_flag || (dt->tzo != 0)) {
+ FORMAT_TZ(dt->tzo, cur);
+ }
+ *cur = 0;
+
+ return xmlStrdup(buf);
+}
+
+/**
+ * exsltDateFormat:
+ * @dt: an #exsltDateValPtr
+ *
+ * Formats @dt in the proper format.
+ * Note: xs:gmonth and xs:gday are not formatted as there are no
+ * routines that output them.
+ *
+ * Returns a newly allocated string, or NULL in case of error
+ */
+static xmlChar *
+exsltDateFormat (const exsltDateValPtr dt)
+{
+
+ if (dt == NULL)
+ return NULL;
+
+ switch (dt->type) {
+ case XS_DURATION:
+ return exsltDateFormatDuration(&(dt->value.dur));
+ case XS_DATETIME:
+ return exsltDateFormatDateTime(&(dt->value.date));
+ case XS_DATE:
+ return exsltDateFormatDate(&(dt->value.date));
+ case XS_TIME:
+ return exsltDateFormatTime(&(dt->value.date));
+ default:
+ break;
+ }
+
+ if (dt->type & XS_GYEAR) {
+ xmlChar buf[100], *cur = buf;
+
+ FORMAT_GYEAR(dt->value.date.year, cur);
+ if (dt->type == XS_GYEARMONTH) {
+ *cur = '-';
+ cur++;
+ FORMAT_GMONTH(dt->value.date.mon, cur);
+ }
+
+ if (dt->value.date.tz_flag || (dt->value.date.tzo != 0)) {
+ FORMAT_TZ(dt->value.date.tzo, cur);
+ }
+ *cur = 0;
+ return xmlStrdup(buf);
+ }
+
+ return NULL;
+}
+
+/**
+ * _exsltDateCastYMToDays:
+ * @dt: an #exsltDateValPtr
+ *
+ * Convert mon and year of @dt to total number of days. Take the
+ * number of years since (or before) 1 AD and add the number of leap
+ * years. This is a function because negative
+ * years must be handled a little differently and there is no zero year.
+ *
+ * Returns number of days.
+ */
+static long
+_exsltDateCastYMToDays (const exsltDateValPtr dt)
+{
+ long ret;
+
+ if (dt->value.date.year < 0)
+ ret = (dt->value.date.year * 365) +
+ (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+
+ ((dt->value.date.year+1)/400)) +
+ DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
+ else
+ ret = ((dt->value.date.year-1) * 365) +
+ (((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+
+ ((dt->value.date.year-1)/400)) +
+ DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
+
+ return ret;
+}
+
+/**
+ * TIME_TO_NUMBER:
+ * @dt: an #exsltDateValPtr
+ *
+ * Calculates the number of seconds in the time portion of @dt.
+ *
+ * Returns seconds.
+ */
+#define TIME_TO_NUMBER(dt) \
+ ((double)((dt->value.date.hour * SECS_PER_HOUR) + \
+ (dt->value.date.min * SECS_PER_MIN)) + dt->value.date.sec)
+
+/**
+ * exsltDateCastDateToNumber:
+ * @dt: an #exsltDateValPtr
+ *
+ * Calculates the number of seconds from year zero.
+ *
+ * Returns seconds from zero year.
+ */
+static double
+exsltDateCastDateToNumber (const exsltDateValPtr dt)
+{
+ double ret = 0.0;
+
+ if (dt == NULL)
+ return 0.0;
+
+ if ((dt->type & XS_GYEAR) == XS_GYEAR) {
+ ret = (double)_exsltDateCastYMToDays(dt) * SECS_PER_DAY;
+ }
+
+ /* add in days */
+ if (dt->type == XS_DURATION) {
+ ret += (double)dt->value.dur.day * SECS_PER_DAY;
+ ret += dt->value.dur.sec;
+ } else {
+ ret += (double)dt->value.date.day * SECS_PER_DAY;
+ /* add in time */
+ ret += TIME_TO_NUMBER(dt);
+ }
+
+
+ return ret;
+}
+
+/**
+ * _exsltDateTruncateDate:
+ * @dt: an #exsltDateValPtr
+ * @type: dateTime type to set to
+ *
+ * Set @dt to truncated @type.
+ *
+ * Returns 0 success, non-zero otherwise.
+ */
+static int
+_exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type)
+{
+ if (dt == NULL)
+ return 1;
+
+ if ((type & XS_TIME) != XS_TIME) {
+ dt->value.date.hour = 0;
+ dt->value.date.min = 0;
+ dt->value.date.sec = 0.0;
+ }
+
+ if ((type & XS_GDAY) != XS_GDAY)
+ dt->value.date.day = 1;
+
+ if ((type & XS_GMONTH) != XS_GMONTH)
+ dt->value.date.mon = 1;
+
+ if ((type & XS_GYEAR) != XS_GYEAR)
+ dt->value.date.year = 0;
+
+ dt->type = type;
+
+ return 0;
+}
+
+/**
+ * _exsltDayInWeek:
+ * @yday: year day (1-366)
+ * @yr: year
+ *
+ * Determine the day-in-week from @yday and @yr. 0001-01-01 was
+ * a Monday so all other days are calculated from there. Take the
+ * number of years since (or before) add the number of leap years and
+ * the day-in-year and mod by 7. This is a function because negative
+ * years must be handled a little differently and there is no zero year.
+ *
+ * Returns day in week (Sunday = 0).
+ */
+static long
+_exsltDateDayInWeek(long yday, long yr)
+{
+ long ret;
+
+ if (yr < 0) {
+ ret = ((yr + (((yr+1)/4)-((yr+1)/100)+((yr+1)/400)) + yday) % 7);
+ if (ret < 0)
+ ret += 7;
+ } else
+ ret = (((yr-1) + (((yr-1)/4)-((yr-1)/100)+((yr-1)/400)) + yday) % 7);
+
+ return ret;
+}
+
+/*
+ * macros for adding date/times and durations
+ */
+#define FQUOTIENT(a,b) ((floor(((double)a/(double)b))))
+#define MODULO(a,b) ((a - FQUOTIENT(a,b) * b))
+#define FQUOTIENT_RANGE(a,low,high) (FQUOTIENT((a-low),(high-low)))
+#define MODULO_RANGE(a,low,high) ((MODULO((a-low),(high-low)))+low)
+
+/**
+ * _exsltDateAdd:
+ * @dt: an #exsltDateValPtr
+ * @dur: an #exsltDateValPtr of type #XS_DURATION
+ *
+ * Compute a new date/time from @dt and @dur. This function assumes @dt
+ * is either #XS_DATETIME, #XS_DATE, #XS_GYEARMONTH, or #XS_GYEAR.
+ *
+ * Returns date/time pointer or NULL.
+ */
+static exsltDateValPtr
+_exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
+{
+ exsltDateValPtr ret;
+ long carry, tempdays, temp;
+ exsltDateValDatePtr r, d;
+ exsltDateValDurationPtr u;
+
+ if ((dt == NULL) || (dur == NULL))
+ return NULL;
+
+ ret = exsltDateCreateDate(dt->type);
+ if (ret == NULL)
+ return NULL;
+
+ r = &(ret->value.date);
+ d = &(dt->value.date);
+ u = &(dur->value.dur);
+
+ /* month */
+ carry = d->mon + u->mon;
+ r->mon = (unsigned int)MODULO_RANGE(carry, 1, 13);
+ carry = (long)FQUOTIENT_RANGE(carry, 1, 13);
+
+ /* year (may be modified later) */
+ r->year = d->year + carry;
+ if (r->year == 0) {
+ if (d->year > 0)
+ r->year--;
+ else
+ r->year++;
+ }
+
+ /* time zone */
+ r->tzo = d->tzo;
+ r->tz_flag = d->tz_flag;
+
+ /* seconds */
+ r->sec = d->sec + u->sec;
+ carry = (long)FQUOTIENT((long)r->sec, 60);
+ if (r->sec != 0.0) {
+ r->sec = MODULO(r->sec, 60.0);
+ }
+
+ /* minute */
+ carry += d->min;
+ r->min = (unsigned int)MODULO(carry, 60);
+ carry = (long)FQUOTIENT(carry, 60);
+
+ /* hours */
+ carry += d->hour;
+ r->hour = (unsigned int)MODULO(carry, 24);
+ carry = (long)FQUOTIENT(carry, 24);
+
+ /*
+ * days
+ * Note we use tempdays because the temporary values may need more
+ * than 5 bits
+ */
+ if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
+ (d->day > MAX_DAYINMONTH(r->year, r->mon)))
+ tempdays = MAX_DAYINMONTH(r->year, r->mon);
+ else if (d->day < 1)
+ tempdays = 1;
+ else
+ tempdays = d->day;
+
+ tempdays += u->day + carry;
+
+ while (1) {
+ if (tempdays < 1) {
+ long tmon = (long)MODULO_RANGE((int)r->mon-1, 1, 13);
+ long tyr = r->year + (long)FQUOTIENT_RANGE((int)r->mon-1, 1, 13);
+ if (tyr == 0)
+ tyr--;
+ /*
+ * Coverity detected an overrun in daysInMonth
+ * of size 12 at position 12 with index variable "((r)->mon - 1)"
+ */
+ if (tmon < 0)
+ tmon = 0;
+ if (tmon > 12)
+ tmon = 12;
+ tempdays += MAX_DAYINMONTH(tyr, tmon);
+ carry = -1;
+ } else if (tempdays > (long)MAX_DAYINMONTH(r->year, r->mon)) {
+ tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon);
+ carry = 1;
+ } else
+ break;
+
+ temp = r->mon + carry;
+ r->mon = (unsigned int)MODULO_RANGE(temp, 1, 13);
+ r->year = r->year + (long)FQUOTIENT_RANGE(temp, 1, 13);
+ if (r->year == 0) {
+ if (temp < 1)
+ r->year--;
+ else
+ r->year++;
+ }
+ }
+
+ r->day = tempdays;
+
+ /*
+ * adjust the date/time type to the date values
+ */
+ if (ret->type != XS_DATETIME) {
+ if ((r->hour) || (r->min) || (r->sec))
+ ret->type = XS_DATETIME;
+ else if (ret->type != XS_DATE) {
+ if (r->day != 1)
+ ret->type = XS_DATE;
+ else if ((ret->type != XS_GYEARMONTH) && (r->mon != 1))
+ ret->type = XS_GYEARMONTH;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * _exsltDateDifference:
+ * @x: an #exsltDateValPtr
+ * @y: an #exsltDateValPtr
+ * @flag: force difference in days
+ *
+ * Calculate the difference between @x and @y as a duration
+ * (i.e. y - x). If the @flag is set then even if the least specific
+ * format of @x or @y is xs:gYear or xs:gYearMonth.
+ *
+ * Returns date/time pointer or NULL.
+ */
+static exsltDateValPtr
+_exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
+{
+ exsltDateValPtr ret;
+
+ if ((x == NULL) || (y == NULL))
+ return NULL;
+
+ if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
+ ((y->type < XS_GYEAR) || (y->type > XS_DATETIME)))
+ return NULL;
+
+ /*
+ * the operand with the most specific format must be converted to
+ * the same type as the operand with the least specific format.
+ */
+ if (x->type != y->type) {
+ if (x->type < y->type) {
+ _exsltDateTruncateDate(y, x->type);
+ } else {
+ _exsltDateTruncateDate(x, y->type);
+ }
+ }
+
+ ret = exsltDateCreateDate(XS_DURATION);
+ if (ret == NULL)
+ return NULL;
+
+ if (((x->type == XS_GYEAR) || (x->type == XS_GYEARMONTH)) && (!flag)) {
+ /* compute the difference in months */
+ ret->value.dur.mon = ((y->value.date.year * 12) + y->value.date.mon) -
+ ((x->value.date.year * 12) + x->value.date.mon);
+ /* The above will give a wrong result if x and y are on different sides
+ of the September 1752. Resolution is welcome :-) */
+ } else {
+ ret->value.dur.day = _exsltDateCastYMToDays(y) -
+ _exsltDateCastYMToDays(x);
+ ret->value.dur.day += y->value.date.day - x->value.date.day;
+ ret->value.dur.sec = TIME_TO_NUMBER(y) - TIME_TO_NUMBER(x);
+ ret->value.dur.sec += (x->value.date.tzo - y->value.date.tzo) *
+ SECS_PER_MIN;
+ if (ret->value.dur.day > 0.0 && ret->value.dur.sec < 0.0) {
+ ret->value.dur.day -= 1;
+ ret->value.dur.sec = ret->value.dur.sec + SECS_PER_DAY;
+ } else if (ret->value.dur.day < 0.0 && ret->value.dur.sec > 0.0) {
+ ret->value.dur.day += 1;
+ ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * _exsltDateAddDurCalc
+ * @ret: an exsltDateValPtr for the return value:
+ * @x: an exsltDateValPtr for the first operand
+ * @y: an exsltDateValPtr for the second operand
+ *
+ * Add two durations, catering for possible negative values.
+ * The sum is placed in @ret.
+ *
+ * Returns 1 for success, 0 if error detected.
+ */
+static int
+_exsltDateAddDurCalc (exsltDateValPtr ret, exsltDateValPtr x,
+ exsltDateValPtr y)
+{
+ long carry;
+
+ /* months */
+ ret->value.dur.mon = x->value.dur.mon + y->value.dur.mon;
+
+ /* seconds */
+ ret->value.dur.sec = x->value.dur.sec + y->value.dur.sec;
+ carry = (long)FQUOTIENT(ret->value.dur.sec, SECS_PER_DAY);
+ if (ret->value.dur.sec != 0.0) {
+ ret->value.dur.sec = MODULO(ret->value.dur.sec, SECS_PER_DAY);
+ /*
+ * Our function MODULO always gives us a positive value, so
+ * if we end up with a "-ve" carry we need to adjust it
+ * appropriately (bug 154021)
+ */
+ if ((carry < 0) && (ret->value.dur.sec != 0)) {
+ /* change seconds to equiv negative modulus */
+ ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
+ carry++;
+ }
+ }
+
+ /* days */
+ ret->value.dur.day = x->value.dur.day + y->value.dur.day + carry;
+
+ /*
+ * are the results indeterminate? i.e. how do you subtract days from
+ * months or years?
+ */
+ if ((((ret->value.dur.day > 0) || (ret->value.dur.sec > 0)) &&
+ (ret->value.dur.mon < 0)) ||
+ (((ret->value.dur.day < 0) || (ret->value.dur.sec < 0)) &&
+ (ret->value.dur.mon > 0))) {
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * _exsltDateAddDuration:
+ * @x: an #exsltDateValPtr of type #XS_DURATION
+ * @y: an #exsltDateValPtr of type #XS_DURATION
+ *
+ * Compute a new duration from @x and @y.
+ *
+ * Returns date/time pointer or NULL.
+ */
+static exsltDateValPtr
+_exsltDateAddDuration (exsltDateValPtr x, exsltDateValPtr y)
+{
+ exsltDateValPtr ret;
+
+ if ((x == NULL) || (y == NULL))
+ return NULL;
+
+ ret = exsltDateCreateDate(XS_DURATION);
+ if (ret == NULL)
+ return NULL;
+
+ if (_exsltDateAddDurCalc(ret, x, y))
+ return ret;
+
+ exsltDateFreeDate(ret);
+ return NULL;
+}
+
+/****************************************************************
+ * *
+ * EXSLT - Dates and Times functions *
+ * *
+ ****************************************************************/
+
+/**
+ * exsltDateDateTime:
+ *
+ * Implements the EXSLT - Dates and Times date-time() function:
+ * string date:date-time()
+ *
+ * Returns the current date and time as a date/time string.
+ */
+static xmlChar *
+exsltDateDateTime (void)
+{
+ xmlChar *ret = NULL;
+#ifdef WITH_TIME
+ exsltDateValPtr cur;
+
+ cur = exsltDateCurrent();
+ if (cur != NULL) {
+ ret = exsltDateFormatDateTime(&(cur->value.date));
+ exsltDateFreeDate(cur);
+ }
+#endif
+
+ return ret;
+}
+
+/**
+ * exsltDateDate:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times date() function:
+ * string date:date (string?)
+ *
+ * Returns the date specified in the date/time string given as the
+ * argument. If no argument is given, then the current local
+ * date/time, as returned by date:date-time is used as a default
+ * argument.
+ * The date/time string specified as an argument must be a string in
+ * the format defined as the lexical representation of either
+ * xs:dateTime or xs:date. If the argument is not in either of these
+ * formats, returns NULL.
+ */
+static xmlChar *
+exsltDateDate (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt = NULL;
+ xmlChar *ret = NULL;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return NULL;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return NULL;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
+ exsltDateFreeDate(dt);
+ return NULL;
+ }
+ }
+
+ ret = exsltDateFormatDate(&(dt->value.date));
+ exsltDateFreeDate(dt);
+
+ return ret;
+}
+
+/**
+ * exsltDateTime:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times time() function:
+ * string date:time (string?)
+ *
+ * Returns the time specified in the date/time string given as the
+ * argument. If no argument is given, then the current local
+ * date/time, as returned by date:date-time is used as a default
+ * argument.
+ * The date/time string specified as an argument must be a string in
+ * the format defined as the lexical representation of either
+ * xs:dateTime or xs:time. If the argument is not in either of these
+ * formats, returns NULL.
+ */
+static xmlChar *
+exsltDateTime (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt = NULL;
+ xmlChar *ret = NULL;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return NULL;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return NULL;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
+ exsltDateFreeDate(dt);
+ return NULL;
+ }
+ }
+
+ ret = exsltDateFormatTime(&(dt->value.date));
+ exsltDateFreeDate(dt);
+
+ return ret;
+}
+
+/**
+ * exsltDateYear:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times year() function
+ * number date:year (string?)
+ * Returns the year of a date as a number. If no argument is given,
+ * then the current local date/time, as returned by date:date-time is
+ * used as a default argument.
+ * The date/time string specified as the first argument must be a
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * - xs:gYearMonth (CCYY-MM)
+ * - xs:gYear (CCYY)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ */
+static double
+exsltDateYear (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ double ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
+ (dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ ret = (double) dt->value.date.year;
+ exsltDateFreeDate(dt);
+
+ return ret;
+}
+
+/**
+ * exsltDateLeapYear:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times leap-year() function:
+ * boolean date:leap-yea (string?)
+ * Returns true if the year given in a date is a leap year. If no
+ * argument is given, then the current local date/time, as returned by
+ * date:date-time is used as a default argument.
+ * The date/time string specified as the first argument must be a
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * - xs:gYearMonth (CCYY-MM)
+ * - xs:gYear (CCYY)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ */
+static xmlXPathObjectPtr
+exsltDateLeapYear (const xmlChar *dateTime)
+{
+ double year;
+
+ year = exsltDateYear(dateTime);
+ if (xmlXPathIsNaN(year))
+ return xmlXPathNewFloat(xmlXPathNAN);
+
+ if (IS_LEAP((long)year))
+ return xmlXPathNewBoolean(1);
+
+ return xmlXPathNewBoolean(0);
+}
+
+/**
+ * exsltDateMonthInYear:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times month-in-year() function:
+ * number date:month-in-year (string?)
+ * Returns the month of a date as a number. If no argument is given,
+ * then the current local date/time, as returned by date:date-time is
+ * used the default argument.
+ * The date/time string specified as the argument is a left or
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * - xs:gYearMonth (CCYY-MM)
+ * - xs:gMonth (--MM--)
+ * - xs:gMonthDay (--MM-DD)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ */
+static double
+exsltDateMonthInYear (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ double ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
+ (dt->type != XS_GYEARMONTH) && (dt->type != XS_GMONTH) &&
+ (dt->type != XS_GMONTHDAY)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ ret = (double) dt->value.date.mon;
+ exsltDateFreeDate(dt);
+
+ return ret;
+}
+
+/**
+ * exsltDateMonthName:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Time month-name() function
+ * string date:month-name (string?)
+ * Returns the full name of the month of a date. If no argument is
+ * given, then the current local date/time, as returned by
+ * date:date-time is used the default argument.
+ * The date/time string specified as the argument is a left or
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * - xs:gYearMonth (CCYY-MM)
+ * - xs:gMonth (--MM--)
+ * If the date/time string is not in one of these formats, then an
+ * empty string ('') is returned.
+ * The result is an English month name: one of 'January', 'February',
+ * 'March', 'April', 'May', 'June', 'July', 'August', 'September',
+ * 'October', 'November' or 'December'.
+ */
+static const xmlChar *
+exsltDateMonthName (const xmlChar *dateTime)
+{
+ static const xmlChar monthNames[13][10] = {
+ { 0 },
+ { 'J', 'a', 'n', 'u', 'a', 'r', 'y', 0 },
+ { 'F', 'e', 'b', 'r', 'u', 'a', 'r', 'y', 0 },
+ { 'M', 'a', 'r', 'c', 'h', 0 },
+ { 'A', 'p', 'r', 'i', 'l', 0 },
+ { 'M', 'a', 'y', 0 },
+ { 'J', 'u', 'n', 'e', 0 },
+ { 'J', 'u', 'l', 'y', 0 },
+ { 'A', 'u', 'g', 'u', 's', 't', 0 },
+ { 'S', 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r', 0 },
+ { 'O', 'c', 't', 'o', 'b', 'e', 'r', 0 },
+ { 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 },
+ { 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 }
+ };
+ int month;
+ month = (int) exsltDateMonthInYear(dateTime);
+ if (!VALID_MONTH(month))
+ month = 0;
+ return monthNames[month];
+}
+
+/**
+ * exsltDateMonthAbbreviation:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Time month-abbreviation() function
+ * string date:month-abbreviation (string?)
+ * Returns the abbreviation of the month of a date. If no argument is
+ * given, then the current local date/time, as returned by
+ * date:date-time is used the default argument.
+ * The date/time string specified as the argument is a left or
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * - xs:gYearMonth (CCYY-MM)
+ * - xs:gMonth (--MM--)
+ * If the date/time string is not in one of these formats, then an
+ * empty string ('') is returned.
+ * The result is an English month abbreviation: one of 'Jan', 'Feb',
+ * 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or
+ * 'Dec'.
+ */
+static const xmlChar *
+exsltDateMonthAbbreviation (const xmlChar *dateTime)
+{
+ static const xmlChar monthAbbreviations[13][4] = {
+ { 0 },
+ { 'J', 'a', 'n', 0 },
+ { 'F', 'e', 'b', 0 },
+ { 'M', 'a', 'r', 0 },
+ { 'A', 'p', 'r', 0 },
+ { 'M', 'a', 'y', 0 },
+ { 'J', 'u', 'n', 0 },
+ { 'J', 'u', 'l', 0 },
+ { 'A', 'u', 'g', 0 },
+ { 'S', 'e', 'p', 0 },
+ { 'O', 'c', 't', 0 },
+ { 'N', 'o', 'v', 0 },
+ { 'D', 'e', 'c', 0 }
+ };
+ int month;
+ month = (int) exsltDateMonthInYear(dateTime);
+ if(!VALID_MONTH(month))
+ month = 0;
+ return monthAbbreviations[month];
+}
+
+/**
+ * exsltDateWeekInYear:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times week-in-year() function
+ * number date:week-in-year (string?)
+ * Returns the week of the year as a number. If no argument is given,
+ * then the current local date/time, as returned by date:date-time is
+ * used as the default argument. For the purposes of numbering,
+ * counting follows ISO 8601: week 1 in a year is the week containing
+ * the first Thursday of the year, with new weeks beginning on a
+ * Monday.
+ * The date/time string specified as the argument is a right-truncated
+ * string in the format defined as the lexical representation of
+ * xs:dateTime in one of the formats defined in [XML Schema Part 2:
+ * Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ */
+static double
+exsltDateWeekInYear (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ long diy, diw, year, ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
+ dt->value.date.year);
+
+ /*
+ * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
+ * is the first day-in-week
+ */
+ diw = (_exsltDateDayInWeek(diy, dt->value.date.year) + 6) % 7;
+
+ /* ISO 8601 adjustment, 3 is Thu */
+ diy += (3 - diw);
+ if(diy < 1) {
+ year = dt->value.date.year - 1;
+ if(year == 0) year--;
+ diy = DAY_IN_YEAR(31, 12, year) + diy;
+ } else if (diy > (long)DAY_IN_YEAR(31, 12, dt->value.date.year)) {
+ diy -= DAY_IN_YEAR(31, 12, dt->value.date.year);
+ }
+
+ ret = ((diy - 1) / 7) + 1;
+
+ exsltDateFreeDate(dt);
+
+ return (double) ret;
+}
+
+/**
+ * exsltDateWeekInMonth:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times week-in-month() function
+ * number date:week-in-month (string?)
+ * The date:week-in-month function returns the week in a month of a
+ * date as a number. If no argument is given, then the current local
+ * date/time, as returned by date:date-time is used the default
+ * argument. For the purposes of numbering, the first day of the month
+ * is in week 1 and new weeks begin on a Monday (so the first and last
+ * weeks in a month will often have less than 7 days in them).
+ * The date/time string specified as the argument is a right-truncated
+ * string in the format defined as the lexical representation of
+ * xs:dateTime in one of the formats defined in [XML Schema Part 2:
+ * Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ */
+static double
+exsltDateWeekInMonth (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ long fdiy, fdiw, ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ fdiy = DAY_IN_YEAR(1, dt->value.date.mon, dt->value.date.year);
+ /*
+ * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
+ * is the first day-in-week
+ */
+ fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7;
+
+ ret = ((dt->value.date.day + fdiw - 1) / 7) + 1;
+
+ exsltDateFreeDate(dt);
+
+ return (double) ret;
+}
+
+/**
+ * exsltDateDayInYear:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times day-in-year() function
+ * number date:day-in-year (string?)
+ * Returns the day of a date in a year as a number. If no argument is
+ * given, then the current local date/time, as returned by
+ * date:date-time is used the default argument.
+ * The date/time string specified as the argument is a right-truncated
+ * string in the format defined as the lexical representation of
+ * xs:dateTime in one of the formats defined in [XML Schema Part 2:
+ * Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ */
+static double
+exsltDateDayInYear (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ long ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ ret = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
+ dt->value.date.year);
+
+ exsltDateFreeDate(dt);
+
+ return (double) ret;
+}
+
+/**
+ * exsltDateDayInMonth:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times day-in-month() function:
+ * number date:day-in-month (string?)
+ * Returns the day of a date as a number. If no argument is given,
+ * then the current local date/time, as returned by date:date-time is
+ * used the default argument.
+ * The date/time string specified as the argument is a left or
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * - xs:gMonthDay (--MM-DD)
+ * - xs:gDay (---DD)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ */
+static double
+exsltDateDayInMonth (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ double ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
+ (dt->type != XS_GMONTHDAY) && (dt->type != XS_GDAY)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ ret = (double) dt->value.date.day;
+ exsltDateFreeDate(dt);
+
+ return ret;
+}
+
+/**
+ * exsltDateDayOfWeekInMonth:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times day-of-week-in-month() function:
+ * number date:day-of-week-in-month (string?)
+ * Returns the day-of-the-week in a month of a date as a number
+ * (e.g. 3 for the 3rd Tuesday in May). If no argument is
+ * given, then the current local date/time, as returned by
+ * date:date-time is used the default argument.
+ * The date/time string specified as the argument is a right-truncated
+ * string in the format defined as the lexical representation of
+ * xs:dateTime in one of the formats defined in [XML Schema Part 2:
+ * Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ */
+static double
+exsltDateDayOfWeekInMonth (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ long ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ ret = ((dt->value.date.day -1) / 7) + 1;
+
+ exsltDateFreeDate(dt);
+
+ return (double) ret;
+}
+
+/**
+ * exsltDateDayInWeek:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times day-in-week() function:
+ * number date:day-in-week (string?)
+ * Returns the day of the week given in a date as a number. If no
+ * argument is given, then the current local date/time, as returned by
+ * date:date-time is used the default argument.
+ * The date/time string specified as the argument is a left or
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ * The numbering of days of the week starts at 1 for Sunday, 2 for
+ * Monday and so on up to 7 for Saturday.
+ */
+static double
+exsltDateDayInWeek (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ long diy, ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
+ dt->value.date.year);
+
+ ret = _exsltDateDayInWeek(diy, dt->value.date.year) + 1;
+
+ exsltDateFreeDate(dt);
+
+ return (double) ret;
+}
+
+/**
+ * exsltDateDayName:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Time day-name() function
+ * string date:day-name (string?)
+ * Returns the full name of the day of the week of a date. If no
+ * argument is given, then the current local date/time, as returned by
+ * date:date-time is used the default argument.
+ * The date/time string specified as the argument is a left or
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * If the date/time string is not in one of these formats, then an
+ * empty string ('') is returned.
+ * The result is an English day name: one of 'Sunday', 'Monday',
+ * 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'.
+ */
+static const xmlChar *
+exsltDateDayName (const xmlChar *dateTime)
+{
+ static const xmlChar dayNames[8][10] = {
+ { 0 },
+ { 'S', 'u', 'n', 'd', 'a', 'y', 0 },
+ { 'M', 'o', 'n', 'd', 'a', 'y', 0 },
+ { 'T', 'u', 'e', 's', 'd', 'a', 'y', 0 },
+ { 'W', 'e', 'd', 'n', 'e', 's', 'd', 'a', 'y', 0 },
+ { 'T', 'h', 'u', 'r', 's', 'd', 'a', 'y', 0 },
+ { 'F', 'r', 'i', 'd', 'a', 'y', 0 },
+ { 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 }
+ };
+ int day;
+ day = (int) exsltDateDayInWeek(dateTime);
+ if((day < 1) || (day > 7))
+ day = 0;
+ return dayNames[day];
+}
+
+/**
+ * exsltDateDayAbbreviation:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Time day-abbreviation() function
+ * string date:day-abbreviation (string?)
+ * Returns the abbreviation of the day of the week of a date. If no
+ * argument is given, then the current local date/time, as returned by
+ * date:date-time is used the default argument.
+ * The date/time string specified as the argument is a left or
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * If the date/time string is not in one of these formats, then an
+ * empty string ('') is returned.
+ * The result is a three-letter English day abbreviation: one of
+ * 'Sun', 'Mon', 'Tue', 'Wed', 'Thu' or 'Fri'.
+ */
+static const xmlChar *
+exsltDateDayAbbreviation (const xmlChar *dateTime)
+{
+ static const xmlChar dayAbbreviations[8][4] = {
+ { 0 },
+ { 'S', 'u', 'n', 0 },
+ { 'M', 'o', 'n', 0 },
+ { 'T', 'u', 'e', 0 },
+ { 'W', 'e', 'd', 0 },
+ { 'T', 'h', 'u', 0 },
+ { 'F', 'r', 'i', 0 },
+ { 'S', 'a', 't', 0 }
+ };
+ int day;
+ day = (int) exsltDateDayInWeek(dateTime);
+ if((day < 1) || (day > 7))
+ day = 0;
+ return dayAbbreviations[day];
+}
+
+/**
+ * exsltDateHourInDay:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times day-in-month() function:
+ * number date:day-in-month (string?)
+ * Returns the hour of the day as a number. If no argument is given,
+ * then the current local date/time, as returned by date:date-time is
+ * used the default argument.
+ * The date/time string specified as the argument is a left or
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:time (hh:mm:ss)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ */
+static double
+exsltDateHourInDay (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ double ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ ret = (double) dt->value.date.hour;
+ exsltDateFreeDate(dt);
+
+ return ret;
+}
+
+/**
+ * exsltDateMinuteInHour:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times day-in-month() function:
+ * number date:day-in-month (string?)
+ * Returns the minute of the hour as a number. If no argument is
+ * given, then the current local date/time, as returned by
+ * date:date-time is used the default argument.
+ * The date/time string specified as the argument is a left or
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:time (hh:mm:ss)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ */
+static double
+exsltDateMinuteInHour (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ double ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ ret = (double) dt->value.date.min;
+ exsltDateFreeDate(dt);
+
+ return ret;
+}
+
+/**
+ * exsltDateSecondInMinute:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times second-in-minute() function:
+ * number date:day-in-month (string?)
+ * Returns the second of the minute as a number. If no argument is
+ * given, then the current local date/time, as returned by
+ * date:date-time is used the default argument.
+ * The date/time string specified as the argument is a left or
+ * right-truncated string in the format defined as the lexical
+ * representation of xs:dateTime in one of the formats defined in [XML
+ * Schema Part 2: Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:time (hh:mm:ss)
+ * If the date/time string is not in one of these formats, then NaN is
+ * returned.
+ *
+ * Returns the second or NaN.
+ */
+static double
+exsltDateSecondInMinute (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ double ret;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParse(dateTime);
+ if (dt == NULL)
+ return xmlXPathNAN;
+ if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
+ exsltDateFreeDate(dt);
+ return xmlXPathNAN;
+ }
+ }
+
+ ret = dt->value.date.sec;
+ exsltDateFreeDate(dt);
+
+ return ret;
+}
+
+/**
+ * exsltDateAdd:
+ * @xstr: date/time string
+ * @ystr: date/time string
+ *
+ * Implements the date:add (string,string) function which returns the
+ * date/time * resulting from adding a duration to a date/time.
+ * The first argument (@xstr) must be right-truncated date/time
+ * strings in one of the formats defined in [XML Schema Part 2:
+ * Datatypes]. The permitted formats are as follows:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * - xs:gYearMonth (CCYY-MM)
+ * - xs:gYear (CCYY)
+ * The second argument (@ystr) is a string in the format defined for
+ * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
+ * The return value is a right-truncated date/time strings in one of
+ * the formats defined in [XML Schema Part 2: Datatypes] and listed
+ * above. This value is calculated using the algorithm described in
+ * [Appendix E Adding durations to dateTimes] of [XML Schema Part 2:
+ * Datatypes].
+
+ * Returns date/time string or NULL.
+ */
+static xmlChar *
+exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr)
+{
+ exsltDateValPtr dt, dur, res;
+ xmlChar *ret;
+
+ if ((xstr == NULL) || (ystr == NULL))
+ return NULL;
+
+ dt = exsltDateParse(xstr);
+ if (dt == NULL)
+ return NULL;
+ else if ((dt->type < XS_GYEAR) || (dt->type > XS_DATETIME)) {
+ exsltDateFreeDate(dt);
+ return NULL;
+ }
+
+ dur = exsltDateParseDuration(ystr);
+ if (dur == NULL) {
+ exsltDateFreeDate(dt);
+ return NULL;
+ }
+
+ res = _exsltDateAdd(dt, dur);
+
+ exsltDateFreeDate(dt);
+ exsltDateFreeDate(dur);
+
+ if (res == NULL)
+ return NULL;
+
+ ret = exsltDateFormat(res);
+ exsltDateFreeDate(res);
+
+ return ret;
+}
+
+/**
+ * exsltDateAddDuration:
+ * @xstr: first duration string
+ * @ystr: second duration string
+ *
+ * Implements the date:add-duration (string,string) function which returns
+ * the duration resulting from adding two durations together.
+ * Both arguments are strings in the format defined for xs:duration
+ * in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. If either
+ * argument is not in this format, the function returns an empty string
+ * ('').
+ * The return value is a string in the format defined for xs:duration
+ * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
+ * The durations can usually be added by summing the numbers given for
+ * each of the components in the durations. However, if the durations
+ * are differently signed, then this sometimes results in durations
+ * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
+ * In these cases, the function returns an empty string ('').
+ *
+ * Returns duration string or NULL.
+ */
+static xmlChar *
+exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr)
+{
+ exsltDateValPtr x, y, res;
+ xmlChar *ret;
+
+ if ((xstr == NULL) || (ystr == NULL))
+ return NULL;
+
+ x = exsltDateParseDuration(xstr);
+ if (x == NULL)
+ return NULL;
+
+ y = exsltDateParseDuration(ystr);
+ if (y == NULL) {
+ exsltDateFreeDate(x);
+ return NULL;
+ }
+
+ res = _exsltDateAddDuration(x, y);
+
+ exsltDateFreeDate(x);
+ exsltDateFreeDate(y);
+
+ if (res == NULL)
+ return NULL;
+
+ ret = exsltDateFormatDuration(&(res->value.dur));
+ exsltDateFreeDate(res);
+
+ return ret;
+}
+
+/**
+ * exsltDateSumFunction:
+ * @ns: a node set of duration strings
+ *
+ * The date:sum function adds a set of durations together.
+ * The string values of the nodes in the node set passed as an argument
+ * are interpreted as durations and added together as if using the
+ * date:add-duration function. (from exslt.org)
+ *
+ * The return value is a string in the format defined for xs:duration
+ * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
+ * The durations can usually be added by summing the numbers given for
+ * each of the components in the durations. However, if the durations
+ * are differently signed, then this sometimes results in durations
+ * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
+ * In these cases, the function returns an empty string ('').
+ *
+ * Returns duration string or NULL.
+ */
+static void
+exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlNodeSetPtr ns;
+ void *user = NULL;
+ xmlChar *tmp;
+ exsltDateValPtr x, total;
+ xmlChar *ret;
+ int i;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError (ctxt);
+ return;
+ }
+
+ /* We need to delay the freeing of value->user */
+ if ((ctxt->value != NULL) && ctxt->value->boolval != 0) {
+ user = ctxt->value->user;
+ ctxt->value->boolval = 0;
+ ctxt->value->user = NULL;
+ }
+
+ ns = xmlXPathPopNodeSet (ctxt);
+ if (xmlXPathCheckError (ctxt))
+ return;
+
+ if ((ns == NULL) || (ns->nodeNr == 0)) {
+ xmlXPathReturnEmptyString (ctxt);
+ if (ns != NULL)
+ xmlXPathFreeNodeSet (ns);
+ return;
+ }
+
+ total = exsltDateCreateDate (XS_DURATION);
+ if (total == NULL) {
+ xmlXPathFreeNodeSet (ns);
+ return;
+ }
+
+ for (i = 0; i < ns->nodeNr; i++) {
+ int result;
+ tmp = xmlXPathCastNodeToString (ns->nodeTab[i]);
+ if (tmp == NULL) {
+ xmlXPathFreeNodeSet (ns);
+ exsltDateFreeDate (total);
+ return;
+ }
+
+ x = exsltDateParseDuration (tmp);
+ if (x == NULL) {
+ xmlFree (tmp);
+ exsltDateFreeDate (total);
+ xmlXPathFreeNodeSet (ns);
+ xmlXPathReturnEmptyString (ctxt);
+ return;
+ }
+
+ result = _exsltDateAddDurCalc(total, total, x);
+
+ exsltDateFreeDate (x);
+ xmlFree (tmp);
+ if (!result) {
+ exsltDateFreeDate (total);
+ xmlXPathFreeNodeSet (ns);
+ xmlXPathReturnEmptyString (ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateFormatDuration (&(total->value.dur));
+ exsltDateFreeDate (total);
+
+ xmlXPathFreeNodeSet (ns);
+ if (user != NULL)
+ xmlFreeNodeList ((xmlNodePtr) user);
+
+ if (ret == NULL)
+ xmlXPathReturnEmptyString (ctxt);
+ else
+ xmlXPathReturnString (ctxt, ret);
+}
+
+/**
+ * exsltDateSeconds:
+ * @dateTime: a date/time string
+ *
+ * Implements the EXSLT - Dates and Times seconds() function:
+ * number date:seconds(string?)
+ * The date:seconds function returns the number of seconds specified
+ * by the argument string. If no argument is given, then the current
+ * local date/time, as returned by exsltDateCurrent() is used as the
+ * default argument. If the date/time string is a xs:duration, then the
+ * years and months must be zero (or not present). Parsing a duration
+ * converts the fields to seconds. If the date/time string is not a
+ * duration (and not null), then the legal formats are:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * - xs:gYearMonth (CCYY-MM)
+ * - xs:gYear (CCYY)
+ * In these cases the difference between the @dateTime and
+ * 1970-01-01T00:00:00Z is calculated and converted to seconds.
+ *
+ * Note that there was some confusion over whether "difference" meant
+ * that a dateTime of 1970-01-01T00:00:01Z should be a positive one or
+ * a negative one. After correspondence with exslt.org, it was determined
+ * that the intent of the specification was to have it positive. The
+ * coding was modified in July 2003 to reflect this.
+ *
+ * Returns seconds or Nan.
+ */
+static double
+exsltDateSeconds (const xmlChar *dateTime)
+{
+ exsltDateValPtr dt;
+ double ret = xmlXPathNAN;
+
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+ if (dt == NULL)
+#endif
+ return xmlXPathNAN;
+ } else {
+ dt = exsltDateParseDuration(dateTime);
+ if (dt == NULL)
+ dt = exsltDateParse(dateTime);
+ }
+
+ if (dt == NULL)
+ return xmlXPathNAN;
+
+ if ((dt->type <= XS_DATETIME) && (dt->type >= XS_GYEAR)) {
+ exsltDateValPtr y, dur;
+
+ /*
+ * compute the difference between the given (or current) date
+ * and epoch date
+ */
+ y = exsltDateCreateDate(XS_DATETIME);
+ if (y != NULL) {
+ y->value.date.year = 1970;
+ y->value.date.mon = 1;
+ y->value.date.day = 1;
+ y->value.date.tz_flag = 1;
+
+ dur = _exsltDateDifference(y, dt, 1);
+ if (dur != NULL) {
+ ret = exsltDateCastDateToNumber(dur);
+ exsltDateFreeDate(dur);
+ }
+ exsltDateFreeDate(y);
+ }
+
+ } else if ((dt->type == XS_DURATION) && (dt->value.dur.mon == 0))
+ ret = exsltDateCastDateToNumber(dt);
+
+ exsltDateFreeDate(dt);
+
+ return ret;
+}
+
+/**
+ * exsltDateDifference:
+ * @xstr: date/time string
+ * @ystr: date/time string
+ *
+ * Implements the date:difference (string,string) function which returns
+ * the duration between the first date and the second date. If the first
+ * date occurs before the second date, then the result is a positive
+ * duration; if it occurs after the second date, the result is a
+ * negative duration. The two dates must both be right-truncated
+ * date/time strings in one of the formats defined in [XML Schema Part
+ * 2: Datatypes]. The date/time with the most specific format (i.e. the
+ * least truncation) is converted into the same format as the date with
+ * the least specific format (i.e. the most truncation). The permitted
+ * formats are as follows, from most specific to least specific:
+ * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
+ * - xs:date (CCYY-MM-DD)
+ * - xs:gYearMonth (CCYY-MM)
+ * - xs:gYear (CCYY)
+ * If either of the arguments is not in one of these formats,
+ * date:difference returns the empty string ('').
+ * The difference between the date/times is returned as a string in the
+ * format defined for xs:duration in [3.2.6 duration] of [XML Schema
+ * Part 2: Datatypes].
+ * If the date/time string with the least specific format is in either
+ * xs:gYearMonth or xs:gYear format, then the number of days, hours,
+ * minutes and seconds in the duration string must be equal to zero.
+ * (The format of the string will be PnYnM.) The number of months
+ * specified in the duration must be less than 12.
+ * Otherwise, the number of years and months in the duration string
+ * must be equal to zero. (The format of the string will be
+ * PnDTnHnMnS.) The number of seconds specified in the duration string
+ * must be less than 60; the number of minutes must be less than 60;
+ * the number of hours must be less than 24.
+ *
+ * Returns duration string or NULL.
+ */
+static xmlChar *
+exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr)
+{
+ exsltDateValPtr x, y, dur;
+ xmlChar *ret = NULL;
+
+ if ((xstr == NULL) || (ystr == NULL))
+ return NULL;
+
+ x = exsltDateParse(xstr);
+ if (x == NULL)
+ return NULL;
+
+ y = exsltDateParse(ystr);
+ if (y == NULL) {
+ exsltDateFreeDate(x);
+ return NULL;
+ }
+
+ if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
+ ((y->type < XS_GYEAR) || (y->type > XS_DATETIME))) {
+ exsltDateFreeDate(x);
+ exsltDateFreeDate(y);
+ return NULL;
+ }
+
+ dur = _exsltDateDifference(x, y, 0);
+
+ exsltDateFreeDate(x);
+ exsltDateFreeDate(y);
+
+ if (dur == NULL)
+ return NULL;
+
+ ret = exsltDateFormatDuration(&(dur->value.dur));
+ exsltDateFreeDate(dur);
+
+ return ret;
+}
+
+/**
+ * exsltDateDuration:
+ * @number: a xmlChar string
+ *
+ * Implements the The date:duration function returns a duration string
+ * representing the number of seconds specified by the argument string.
+ * If no argument is given, then the result of calling date:seconds
+ * without any arguments is used as a default argument.
+ * The duration is returned as a string in the format defined for
+ * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
+ * The number of years and months in the duration string must be equal
+ * to zero. (The format of the string will be PnDTnHnMnS.) The number
+ * of seconds specified in the duration string must be less than 60;
+ * the number of minutes must be less than 60; the number of hours must
+ * be less than 24.
+ * If the argument is Infinity, -Infinity or NaN, then date:duration
+ * returns an empty string ('').
+ *
+ * Returns duration string or NULL.
+ */
+static xmlChar *
+exsltDateDuration (const xmlChar *number)
+{
+ exsltDateValPtr dur;
+ double secs;
+ xmlChar *ret;
+
+ if (number == NULL)
+ secs = exsltDateSeconds(number);
+ else
+ secs = xmlXPathCastStringToNumber(number);
+
+ if ((xmlXPathIsNaN(secs)) || (xmlXPathIsInf(secs)))
+ return NULL;
+
+ dur = exsltDateCreateDate(XS_DURATION);
+ if (dur == NULL)
+ return NULL;
+
+ dur->value.dur.sec = secs;
+
+ ret = exsltDateFormatDuration(&(dur->value.dur));
+ exsltDateFreeDate(dur);
+
+ return ret;
+}
+
+/****************************************************************
+ * *
+ * Wrappers for use by the XPath engine *
+ * *
+ ****************************************************************/
+
+#ifdef WITH_TIME
+/**
+ * exsltDateDateTimeFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateDateTime() for use by the XPath engine.
+ */
+static void
+exsltDateDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *ret;
+
+ if (nargs != 0) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ ret = exsltDateDateTime();
+ if (ret == NULL)
+ xmlXPathReturnEmptyString(ctxt);
+ else
+ xmlXPathReturnString(ctxt, ret);
+}
+#endif
+
+/**
+ * exsltDateDateFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateDate() for use by the XPath engine.
+ */
+static void
+exsltDateDateFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *ret, *dt = NULL;
+
+ if ((nargs < 0) || (nargs > 1)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ if (nargs == 1) {
+ dt = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateDate(dt);
+
+ if (ret == NULL) {
+ xsltGenericDebug(xsltGenericDebugContext,
+ "{http://exslt.org/dates-and-times}date: "
+ "invalid date or format %s\n", dt);
+ xmlXPathReturnEmptyString(ctxt);
+ } else {
+ xmlXPathReturnString(ctxt, ret);
+ }
+
+ if (dt != NULL)
+ xmlFree(dt);
+}
+
+/**
+ * exsltDateTimeFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateTime() for use by the XPath engine.
+ */
+static void
+exsltDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *ret, *dt = NULL;
+
+ if ((nargs < 0) || (nargs > 1)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ if (nargs == 1) {
+ dt = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateTime(dt);
+
+ if (ret == NULL) {
+ xsltGenericDebug(xsltGenericDebugContext,
+ "{http://exslt.org/dates-and-times}time: "
+ "invalid date or format %s\n", dt);
+ xmlXPathReturnEmptyString(ctxt);
+ } else {
+ xmlXPathReturnString(ctxt, ret);
+ }
+
+ if (dt != NULL)
+ xmlFree(dt);
+}
+
+/**
+ * exsltDateYearFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateYear() for use by the XPath engine.
+ */
+static void
+exsltDateYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *dt = NULL;
+ double ret;
+
+ if ((nargs < 0) || (nargs > 1)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 1) {
+ dt = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateYear(dt);
+
+ if (dt != NULL)
+ xmlFree(dt);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltDateLeapYearFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateLeapYear() for use by the XPath engine.
+ */
+static void
+exsltDateLeapYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *dt = NULL;
+ xmlXPathObjectPtr ret;
+
+ if ((nargs < 0) || (nargs > 1)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 1) {
+ dt = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateLeapYear(dt);
+
+ if (dt != NULL)
+ xmlFree(dt);
+
+ valuePush(ctxt, ret);
+}
+
+#define X_IN_Y(x, y) \
+static void \
+exsltDate##x##In##y##Function (xmlXPathParserContextPtr ctxt, \
+ int nargs) { \
+ xmlChar *dt = NULL; \
+ double ret; \
+ \
+ if ((nargs < 0) || (nargs > 1)) { \
+ xmlXPathSetArityError(ctxt); \
+ return; \
+ } \
+ \
+ if (nargs == 1) { \
+ dt = xmlXPathPopString(ctxt); \
+ if (xmlXPathCheckError(ctxt)) { \
+ xmlXPathSetTypeError(ctxt); \
+ return; \
+ } \
+ } \
+ \
+ ret = exsltDate##x##In##y(dt); \
+ \
+ if (dt != NULL) \
+ xmlFree(dt); \
+ \
+ xmlXPathReturnNumber(ctxt, ret); \
+}
+
+/**
+ * exsltDateMonthInYearFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateMonthInYear() for use by the XPath engine.
+ */
+X_IN_Y(Month,Year)
+
+/**
+ * exsltDateMonthNameFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateMonthName() for use by the XPath engine.
+ */
+static void
+exsltDateMonthNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *dt = NULL;
+ const xmlChar *ret;
+
+ if ((nargs < 0) || (nargs > 1)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 1) {
+ dt = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateMonthName(dt);
+
+ if (dt != NULL)
+ xmlFree(dt);
+
+ if (ret == NULL)
+ xmlXPathReturnEmptyString(ctxt);
+ else
+ xmlXPathReturnString(ctxt, xmlStrdup(ret));
+}
+
+/**
+ * exsltDateMonthAbbreviationFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateMonthAbbreviation() for use by the XPath engine.
+ */
+static void
+exsltDateMonthAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *dt = NULL;
+ const xmlChar *ret;
+
+ if ((nargs < 0) || (nargs > 1)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 1) {
+ dt = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateMonthAbbreviation(dt);
+
+ if (dt != NULL)
+ xmlFree(dt);
+
+ if (ret == NULL)
+ xmlXPathReturnEmptyString(ctxt);
+ else
+ xmlXPathReturnString(ctxt, xmlStrdup(ret));
+}
+
+/**
+ * exsltDateWeekInYearFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateWeekInYear() for use by the XPath engine.
+ */
+X_IN_Y(Week,Year)
+
+/**
+ * exsltDateWeekInMonthFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateWeekInMonthYear() for use by the XPath engine.
+ */
+X_IN_Y(Week,Month)
+
+/**
+ * exsltDateDayInYearFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateDayInYear() for use by the XPath engine.
+ */
+X_IN_Y(Day,Year)
+
+/**
+ * exsltDateDayInMonthFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateDayInMonth() for use by the XPath engine.
+ */
+X_IN_Y(Day,Month)
+
+/**
+ * exsltDateDayOfWeekInMonthFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDayOfWeekInMonth() for use by the XPath engine.
+ */
+X_IN_Y(DayOfWeek,Month)
+
+/**
+ * exsltDateDayInWeekFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateDayInWeek() for use by the XPath engine.
+ */
+X_IN_Y(Day,Week)
+
+/**
+ * exsltDateDayNameFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateDayName() for use by the XPath engine.
+ */
+static void
+exsltDateDayNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *dt = NULL;
+ const xmlChar *ret;
+
+ if ((nargs < 0) || (nargs > 1)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 1) {
+ dt = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateDayName(dt);
+
+ if (dt != NULL)
+ xmlFree(dt);
+
+ if (ret == NULL)
+ xmlXPathReturnEmptyString(ctxt);
+ else
+ xmlXPathReturnString(ctxt, xmlStrdup(ret));
+}
+
+/**
+ * exsltDateMonthDayFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateDayAbbreviation() for use by the XPath engine.
+ */
+static void
+exsltDateDayAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *dt = NULL;
+ const xmlChar *ret;
+
+ if ((nargs < 0) || (nargs > 1)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 1) {
+ dt = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateDayAbbreviation(dt);
+
+ if (dt != NULL)
+ xmlFree(dt);
+
+ if (ret == NULL)
+ xmlXPathReturnEmptyString(ctxt);
+ else
+ xmlXPathReturnString(ctxt, xmlStrdup(ret));
+}
+
+
+/**
+ * exsltDateHourInDayFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateHourInDay() for use by the XPath engine.
+ */
+X_IN_Y(Hour,Day)
+
+/**
+ * exsltDateMinuteInHourFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateMinuteInHour() for use by the XPath engine.
+ */
+X_IN_Y(Minute,Hour)
+
+/**
+ * exsltDateSecondInMinuteFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateSecondInMinute() for use by the XPath engine.
+ */
+X_IN_Y(Second,Minute)
+
+/**
+ * exsltDateSecondsFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateSeconds() for use by the XPath engine.
+ */
+static void
+exsltDateSecondsFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *str = NULL;
+ double ret;
+
+ if (nargs > 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 1) {
+ str = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateSeconds(str);
+ if (str != NULL)
+ xmlFree(str);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltDateAddFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps exsltDateAdd() for use by the XPath processor.
+ */
+static void
+exsltDateAddFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *ret, *xstr, *ystr;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ystr = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ xstr = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlFree(ystr);
+ return;
+ }
+
+ ret = exsltDateAdd(xstr, ystr);
+
+ xmlFree(ystr);
+ xmlFree(xstr);
+
+ if (ret == NULL)
+ xmlXPathReturnEmptyString(ctxt);
+ else
+ xmlXPathReturnString(ctxt, ret);
+}
+
+/**
+ * exsltDateAddDurationFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps exsltDateAddDuration() for use by the XPath processor.
+ */
+static void
+exsltDateAddDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *ret, *xstr, *ystr;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ystr = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ xstr = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlFree(ystr);
+ return;
+ }
+
+ ret = exsltDateAddDuration(xstr, ystr);
+
+ xmlFree(ystr);
+ xmlFree(xstr);
+
+ if (ret == NULL)
+ xmlXPathReturnEmptyString(ctxt);
+ else
+ xmlXPathReturnString(ctxt, ret);
+}
+
+/**
+ * exsltDateDifferenceFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps exsltDateDifference() for use by the XPath processor.
+ */
+static void
+exsltDateDifferenceFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *ret, *xstr, *ystr;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ystr = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ xstr = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlFree(ystr);
+ return;
+ }
+
+ ret = exsltDateDifference(xstr, ystr);
+
+ xmlFree(ystr);
+ xmlFree(xstr);
+
+ if (ret == NULL)
+ xmlXPathReturnEmptyString(ctxt);
+ else
+ xmlXPathReturnString(ctxt, ret);
+}
+
+/**
+ * exsltDateDurationFunction:
+ * @ctxt: an XPath parser context
+ * @nargs : the number of arguments
+ *
+ * Wraps exsltDateDuration() for use by the XPath engine
+ */
+static void
+exsltDateDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *ret;
+ xmlChar *number = NULL;
+
+ if ((nargs < 0) || (nargs > 1)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 1) {
+ number = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ }
+
+ ret = exsltDateDuration(number);
+
+ if (number != NULL)
+ xmlFree(number);
+
+ if (ret == NULL)
+ xmlXPathReturnEmptyString(ctxt);
+ else
+ xmlXPathReturnString(ctxt, ret);
+}
+
+/**
+ * exsltDateRegister:
+ *
+ * Registers the EXSLT - Dates and Times module
+ */
+void
+exsltDateRegister (void)
+{
+ xsltRegisterExtModuleFunction ((const xmlChar *) "add",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateAddFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "add-duration",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateAddDurationFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "date",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDateFunction);
+#ifdef WITH_TIME
+ xsltRegisterExtModuleFunction ((const xmlChar *) "date-time",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDateTimeFunction);
+#endif
+ xsltRegisterExtModuleFunction ((const xmlChar *) "day-abbreviation",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayAbbreviationFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-month",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayInMonthFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-week",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayInWeekFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-year",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayInYearFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "day-name",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayNameFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "day-of-week-in-month",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayOfWeekInMonthFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "difference",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDifferenceFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "duration",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDurationFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "hour-in-day",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateHourInDayFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "leap-year",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateLeapYearFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "minute-in-hour",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateMinuteInHourFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "month-abbreviation",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateMonthAbbreviationFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "month-in-year",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateMonthInYearFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "month-name",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateMonthNameFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "second-in-minute",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateSecondInMinuteFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "seconds",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateSecondsFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "sum",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateSumFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "time",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateTimeFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-month",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateWeekInMonthFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-year",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateWeekInYearFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "year",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateYearFunction);
+}
+
+/**
+ * exsltDateXpathCtxtRegister:
+ *
+ * Registers the EXSLT - Dates and Times module for use outside XSLT
+ */
+int
+exsltDateXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix)
+{
+ if (ctxt
+ && prefix
+ && !xmlXPathRegisterNs(ctxt,
+ prefix,
+ (const xmlChar *) EXSLT_DATE_NAMESPACE)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "add",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateAddFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "add-duration",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateAddDurationFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "date",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDateFunction)
+#ifdef WITH_TIME
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "date-time",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDateTimeFunction)
+#endif
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "day-abbreviation",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayAbbreviationFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "day-in-month",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayInMonthFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "day-in-week",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayInWeekFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "day-in-year",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayInYearFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "day-name",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayNameFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "day-of-week-in-month",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDayOfWeekInMonthFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "difference",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDifferenceFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "duration",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateDurationFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "hour-in-day",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateHourInDayFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "leap-year",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateLeapYearFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "minute-in-hour",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateMinuteInHourFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "month-abbreviation",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateMonthAbbreviationFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "month-in-year",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateMonthInYearFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "month-name",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateMonthNameFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "second-in-minute",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateSecondInMinuteFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "seconds",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateSecondsFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "sum",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateSumFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "time",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateTimeFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "week-in-month",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateWeekInMonthFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "week-in-year",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateWeekInYearFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "year",
+ (const xmlChar *) EXSLT_DATE_NAMESPACE,
+ exsltDateYearFunction)) {
+ return 0;
+ }
+ return -1;
+}
diff --git a/libxslt/libexslt/dynamic.c b/libxslt/libexslt/dynamic.c
new file mode 100644
index 0000000..4a4944e
--- /dev/null
+++ b/libxslt/libexslt/dynamic.c
@@ -0,0 +1,301 @@
+/*
+ * dynamic.c: Implementation of the EXSLT -- Dynamic module
+ *
+ * References:
+ * http://www.exslt.org/dyn/dyn.html
+ *
+ * See Copyright for the status of this software.
+ *
+ * Authors:
+ * Mark Vakoc <mark_vakoc@jdedwards.com>
+ * Thomas Broyer <tbroyer@ltgt.net>
+ *
+ * TODO:
+ * elements:
+ * functions:
+ * min
+ * max
+ * sum
+ * map
+ * closure
+ */
+
+#define IN_LIBEXSLT
+#include "libexslt/libexslt.h"
+
+#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <libxslt/xsltconfig.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/extensions.h>
+
+#include "exslt.h"
+
+/**
+ * exsltDynEvaluateFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Evaluates the string as an XPath expression and returns the result
+ * value, which may be a boolean, number, string, node set, result tree
+ * fragment or external object.
+ */
+
+static void
+exsltDynEvaluateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlChar *str = NULL;
+ xmlXPathObjectPtr ret = NULL;
+
+ if (ctxt == NULL)
+ return;
+ if (nargs != 1) {
+ xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
+ xsltGenericError(xsltGenericErrorContext,
+ "dyn:evalute() : invalid number of args %d\n", nargs);
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+ str = xmlXPathPopString(ctxt);
+ /* return an empty node-set if an empty string is passed in */
+ if (!str||!xmlStrlen(str)) {
+ if (str) xmlFree(str);
+ valuePush(ctxt,xmlXPathNewNodeSet(NULL));
+ return;
+ }
+ ret = xmlXPathEval(str,ctxt->context);
+ if (ret)
+ valuePush(ctxt,ret);
+ else {
+ xsltGenericError(xsltGenericErrorContext,
+ "dyn:evaluate() : unable to evaluate expression '%s'\n",str);
+ valuePush(ctxt,xmlXPathNewNodeSet(NULL));
+ }
+ xmlFree(str);
+ return;
+}
+
+/**
+ * exsltDynMapFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Evaluates the string as an XPath expression and returns the result
+ * value, which may be a boolean, number, string, node set, result tree
+ * fragment or external object.
+ */
+
+static void
+exsltDynMapFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlChar *str = NULL;
+ xmlNodeSetPtr nodeset = NULL;
+ xsltTransformContextPtr tctxt;
+ xmlXPathCompExprPtr comp = NULL;
+ xmlXPathObjectPtr ret = NULL;
+ xmlDocPtr oldDoc, container = NULL;
+ xmlNodePtr oldNode;
+ int oldContextSize;
+ int oldProximityPosition;
+ int i, j;
+
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ str = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ nodeset = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+ if (str == NULL || !xmlStrlen(str) || !(comp = xmlXPathCompile(str))) {
+ if (nodeset != NULL)
+ xmlXPathFreeNodeSet(nodeset);
+ if (str != NULL)
+ xmlFree(str);
+ valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+ return;
+ }
+
+ ret = xmlXPathNewNodeSet(NULL);
+ if (ret == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltDynMapFunction: ret == NULL\n");
+ goto cleanup;
+ }
+
+ oldDoc = ctxt->context->doc;
+ oldNode = ctxt->context->node;
+ oldContextSize = ctxt->context->contextSize;
+ oldProximityPosition = ctxt->context->proximityPosition;
+
+ /**
+ * since we really don't know we're going to be adding node(s)
+ * down the road we create the RVT regardless
+ */
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if (tctxt == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "dyn:map : internal error tctxt == NULL\n");
+ goto cleanup;
+ }
+ container = xsltCreateRVT(tctxt);
+ if (container == NULL) {
+ xsltTransformError(tctxt, NULL, NULL,
+ "dyn:map : internal error container == NULL\n");
+ goto cleanup;
+ }
+ xsltRegisterLocalRVT(tctxt, container);
+ if (nodeset && nodeset->nodeNr > 0) {
+ xmlXPathNodeSetSort(nodeset);
+ ctxt->context->contextSize = nodeset->nodeNr;
+ ctxt->context->proximityPosition = 0;
+ for (i = 0; i < nodeset->nodeNr; i++) {
+ xmlXPathObjectPtr subResult = NULL;
+ xmlNodePtr cur = nodeset->nodeTab[i];
+
+ ctxt->context->proximityPosition++;
+ ctxt->context->node = cur;
+
+ if (cur->type == XML_NAMESPACE_DECL) {
+ /*
+ * The XPath module sets the owner element of a ns-node on
+ * the ns->next field.
+ */
+ cur = (xmlNodePtr) ((xmlNsPtr) cur)->next;
+ if ((cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
+ xsltGenericError(xsltGenericErrorContext,
+ "Internal error in exsltDynMapFunction: "
+ "Cannot retrieve the doc of a namespace node.\n");
+ continue;
+ }
+ ctxt->context->doc = cur->doc;
+ } else {
+ ctxt->context->doc = cur->doc;
+ }
+
+ subResult = xmlXPathCompiledEval(comp, ctxt->context);
+ if (subResult != NULL) {
+ switch (subResult->type) {
+ case XPATH_NODESET:
+ if (subResult->nodesetval != NULL)
+ for (j = 0; j < subResult->nodesetval->nodeNr;
+ j++)
+ xmlXPathNodeSetAdd(ret->nodesetval,
+ subResult->nodesetval->
+ nodeTab[j]);
+ break;
+ case XPATH_BOOLEAN:
+ if (container != NULL) {
+ xmlNodePtr cur =
+ xmlNewChild((xmlNodePtr) container, NULL,
+ BAD_CAST "boolean",
+ BAD_CAST (subResult->
+ boolval ? "true" : ""));
+ if (cur != NULL) {
+ cur->ns =
+ xmlNewNs(cur,
+ BAD_CAST
+ "http://exslt.org/common",
+ BAD_CAST "exsl");
+ xmlXPathNodeSetAddUnique(ret->nodesetval,
+ cur);
+ }
+ }
+ break;
+ case XPATH_NUMBER:
+ if (container != NULL) {
+ xmlChar *val =
+ xmlXPathCastNumberToString(subResult->
+ floatval);
+ xmlNodePtr cur =
+ xmlNewChild((xmlNodePtr) container, NULL,
+ BAD_CAST "number", val);
+ if (val != NULL)
+ xmlFree(val);
+
+ if (cur != NULL) {
+ cur->ns =
+ xmlNewNs(cur,
+ BAD_CAST
+ "http://exslt.org/common",
+ BAD_CAST "exsl");
+ xmlXPathNodeSetAddUnique(ret->nodesetval,
+ cur);
+ }
+ }
+ break;
+ case XPATH_STRING:
+ if (container != NULL) {
+ xmlNodePtr cur =
+ xmlNewChild((xmlNodePtr) container, NULL,
+ BAD_CAST "string",
+ subResult->stringval);
+ if (cur != NULL) {
+ cur->ns =
+ xmlNewNs(cur,
+ BAD_CAST
+ "http://exslt.org/common",
+ BAD_CAST "exsl");
+ xmlXPathNodeSetAddUnique(ret->nodesetval,
+ cur);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ xmlXPathFreeObject(subResult);
+ }
+ }
+ }
+ ctxt->context->doc = oldDoc;
+ ctxt->context->node = oldNode;
+ ctxt->context->contextSize = oldContextSize;
+ ctxt->context->proximityPosition = oldProximityPosition;
+
+
+ cleanup:
+ /* restore the xpath context */
+ if (comp != NULL)
+ xmlXPathFreeCompExpr(comp);
+ if (nodeset != NULL)
+ xmlXPathFreeNodeSet(nodeset);
+ if (str != NULL)
+ xmlFree(str);
+ valuePush(ctxt, ret);
+ return;
+}
+
+
+/**
+ * exsltDynRegister:
+ *
+ * Registers the EXSLT - Dynamic module
+ */
+
+void
+exsltDynRegister (void) {
+ xsltRegisterExtModuleFunction ((const xmlChar *) "evaluate",
+ EXSLT_DYNAMIC_NAMESPACE,
+ exsltDynEvaluateFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "map",
+ EXSLT_DYNAMIC_NAMESPACE,
+ exsltDynMapFunction);
+
+}
diff --git a/libxslt/libexslt/exslt.c b/libxslt/libexslt/exslt.c
new file mode 100644
index 0000000..0bccf17
--- /dev/null
+++ b/libxslt/libexslt/exslt.c
@@ -0,0 +1,40 @@
+#define IN_LIBEXSLT
+#include "libexslt/libexslt.h"
+
+#include <libxml/xmlversion.h>
+
+#include "config.h"
+
+#include <libxslt/xsltconfig.h>
+#include <libxslt/extensions.h>
+
+#include <libexslt/exsltconfig.h>
+#include "exslt.h"
+
+const char *exsltLibraryVersion = LIBEXSLT_VERSION_STRING
+ LIBEXSLT_VERSION_EXTRA;
+const int exsltLibexsltVersion = LIBEXSLT_VERSION;
+const int exsltLibxsltVersion = LIBXSLT_VERSION;
+const int exsltLibxmlVersion = LIBXML_VERSION;
+
+/**
+ * exsltRegisterAll:
+ *
+ * Registers all available EXSLT extensions
+ */
+void
+exsltRegisterAll (void) {
+ xsltInitGlobals();
+ exsltCommonRegister();
+#ifdef EXSLT_CRYPTO_ENABLED
+ exsltCryptoRegister();
+#endif
+ exsltMathRegister();
+ exsltSetsRegister();
+ exsltFuncRegister();
+ exsltStrRegister();
+ exsltDateRegister();
+ exsltSaxonRegister();
+ exsltDynRegister();
+}
+
diff --git a/libxslt/libexslt/exslt.h b/libxslt/libexslt/exslt.h
new file mode 100644
index 0000000..2147308
--- /dev/null
+++ b/libxslt/libexslt/exslt.h
@@ -0,0 +1,102 @@
+
+#ifndef __EXSLT_H__
+#define __EXSLT_H__
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include "exsltexports.h"
+#include <libexslt/exsltconfig.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EXSLTPUBVAR const char *exsltLibraryVersion;
+EXSLTPUBVAR const int exsltLibexsltVersion;
+EXSLTPUBVAR const int exsltLibxsltVersion;
+EXSLTPUBVAR const int exsltLibxmlVersion;
+
+/**
+ * EXSLT_COMMON_NAMESPACE:
+ *
+ * Namespace for EXSLT common functions
+ */
+#define EXSLT_COMMON_NAMESPACE ((const xmlChar *) "http://exslt.org/common")
+/**
+ * EXSLT_CRYPTO_NAMESPACE:
+ *
+ * Namespace for EXSLT crypto functions
+ */
+#define EXSLT_CRYPTO_NAMESPACE ((const xmlChar *) "http://exslt.org/crypto")
+/**
+ * EXSLT_MATH_NAMESPACE:
+ *
+ * Namespace for EXSLT math functions
+ */
+#define EXSLT_MATH_NAMESPACE ((const xmlChar *) "http://exslt.org/math")
+/**
+ * EXSLT_SETS_NAMESPACE:
+ *
+ * Namespace for EXSLT set functions
+ */
+#define EXSLT_SETS_NAMESPACE ((const xmlChar *) "http://exslt.org/sets")
+/**
+ * EXSLT_FUNCTIONS_NAMESPACE:
+ *
+ * Namespace for EXSLT functions extension functions
+ */
+#define EXSLT_FUNCTIONS_NAMESPACE ((const xmlChar *) "http://exslt.org/functions")
+/**
+ * EXSLT_STRINGS_NAMESPACE:
+ *
+ * Namespace for EXSLT strings functions
+ */
+#define EXSLT_STRINGS_NAMESPACE ((const xmlChar *) "http://exslt.org/strings")
+/**
+ * EXSLT_DATE_NAMESPACE:
+ *
+ * Namespace for EXSLT date functions
+ */
+#define EXSLT_DATE_NAMESPACE ((const xmlChar *) "http://exslt.org/dates-and-times")
+/**
+ * EXSLT_DYNAMIC_NAMESPACE:
+ *
+ * Namespace for EXSLT dynamic functions
+ */
+#define EXSLT_DYNAMIC_NAMESPACE ((const xmlChar *) "http://exslt.org/dynamic")
+
+/**
+ * SAXON_NAMESPACE:
+ *
+ * Namespace for SAXON extensions functions
+ */
+#define SAXON_NAMESPACE ((const xmlChar *) "http://icl.com/saxon")
+
+EXSLTPUBFUN void EXSLTCALL exsltCommonRegister (void);
+#ifdef EXSLT_CRYPTO_ENABLED
+EXSLTPUBFUN void EXSLTCALL exsltCryptoRegister (void);
+#endif
+EXSLTPUBFUN void EXSLTCALL exsltMathRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltSetsRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltFuncRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltStrRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltDateRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltSaxonRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltDynRegister(void);
+
+EXSLTPUBFUN void EXSLTCALL exsltRegisterAll (void);
+
+EXSLTPUBFUN int EXSLTCALL exsltDateXpathCtxtRegister (xmlXPathContextPtr ctxt,
+ const xmlChar *prefix);
+EXSLTPUBFUN int EXSLTCALL exsltMathXpathCtxtRegister (xmlXPathContextPtr ctxt,
+ const xmlChar *prefix);
+EXSLTPUBFUN int EXSLTCALL exsltSetsXpathCtxtRegister (xmlXPathContextPtr ctxt,
+ const xmlChar *prefix);
+EXSLTPUBFUN int EXSLTCALL exsltStrXpathCtxtRegister (xmlXPathContextPtr ctxt,
+ const xmlChar *prefix);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __EXSLT_H__ */
+
diff --git a/libxslt/libexslt/exsltconfig.h.in b/libxslt/libexslt/exsltconfig.h.in
new file mode 100644
index 0000000..b46ffc0
--- /dev/null
+++ b/libxslt/libexslt/exsltconfig.h.in
@@ -0,0 +1,73 @@
+/*
+ * exsltconfig.h: compile-time version informations for the EXSLT library
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#ifndef __XML_EXSLTCONFIG_H__
+#define __XML_EXSLTCONFIG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * LIBEXSLT_DOTTED_VERSION:
+ *
+ * the version string like "1.2.3"
+ */
+#define LIBEXSLT_DOTTED_VERSION "@VERSION@"
+
+/**
+ * LIBEXSLT_VERSION:
+ *
+ * the version number: 1.2.3 value is 10203
+ */
+#define LIBEXSLT_VERSION @LIBEXSLT_VERSION_NUMBER@
+
+/**
+ * LIBEXSLT_VERSION_STRING:
+ *
+ * the version number string, 1.2.3 value is "10203"
+ */
+#define LIBEXSLT_VERSION_STRING "@LIBEXSLT_VERSION_NUMBER@"
+
+/**
+ * LIBEXSLT_VERSION_EXTRA:
+ *
+ * extra version information, used to show a CVS compilation
+ */
+#define LIBEXSLT_VERSION_EXTRA "@LIBEXSLT_VERSION_EXTRA@"
+
+/**
+ * WITH_CRYPTO:
+ *
+ * Whether crypto support is configured into exslt
+ */
+#if @WITH_CRYPTO@
+#define EXSLT_CRYPTO_ENABLED
+#endif
+
+/**
+ * ATTRIBUTE_UNUSED:
+ *
+ * This macro is used to flag unused function parameters to GCC
+ */
+#ifdef __GNUC__
+#ifdef HAVE_ANSIDECL_H
+#include <ansidecl.h>
+#endif
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#endif
+#else
+#define ATTRIBUTE_UNUSED
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_EXSLTCONFIG_H__ */
diff --git a/libxslt/libexslt/exsltexports.h b/libxslt/libexslt/exsltexports.h
new file mode 100644
index 0000000..bead915
--- /dev/null
+++ b/libxslt/libexslt/exsltexports.h
@@ -0,0 +1,140 @@
+/*
+ * exsltexports.h : macros for marking symbols as exportable/importable.
+ *
+ * See Copyright for the status of this software.
+ *
+ * igor@zlatkovic.com
+ */
+
+#ifndef __EXSLT_EXPORTS_H__
+#define __EXSLT_EXPORTS_H__
+
+/**
+ * EXSLTPUBFUN, EXSLTPUBVAR, EXSLTCALL
+ *
+ * Macros which declare an exportable function, an exportable variable and
+ * the calling convention used for functions.
+ *
+ * Please use an extra block for every platform/compiler combination when
+ * modifying this, rather than overlong #ifdef lines. This helps
+ * readability as well as the fact that different compilers on the same
+ * platform might need different definitions.
+ */
+
+/**
+ * EXSLTPUBFUN:
+ *
+ * Macros which declare an exportable function
+ */
+#define EXSLTPUBFUN
+/**
+ * EXSLTPUBVAR:
+ *
+ * Macros which declare an exportable variable
+ */
+#define EXSLTPUBVAR extern
+/**
+ * EXSLTCALL:
+ *
+ * Macros which declare the called convention for exported functions
+ */
+#define EXSLTCALL
+
+/** DOC_DISABLE */
+
+/* Windows platform with MS compiler */
+#if defined(_WIN32) && defined(_MSC_VER)
+ #undef EXSLTPUBFUN
+ #undef EXSLTPUBVAR
+ #undef EXSLTCALL
+ #if defined(IN_LIBEXSLT) && !defined(LIBEXSLT_STATIC)
+ #define EXSLTPUBFUN __declspec(dllexport)
+ #define EXSLTPUBVAR __declspec(dllexport)
+ #else
+ #define EXSLTPUBFUN
+ #if !defined(LIBEXSLT_STATIC)
+ #define EXSLTPUBVAR __declspec(dllimport) extern
+ #else
+ #define EXSLTPUBVAR extern
+ #endif
+ #endif
+ #define EXSLTCALL __cdecl
+ #if !defined _REENTRANT
+ #define _REENTRANT
+ #endif
+#endif
+
+/* Windows platform with Borland compiler */
+#if defined(_WIN32) && defined(__BORLANDC__)
+ #undef EXSLTPUBFUN
+ #undef EXSLTPUBVAR
+ #undef EXSLTCALL
+ #if defined(IN_LIBEXSLT) && !defined(LIBEXSLT_STATIC)
+ #define EXSLTPUBFUN __declspec(dllexport)
+ #define EXSLTPUBVAR __declspec(dllexport) extern
+ #else
+ #define EXSLTPUBFUN
+ #if !defined(LIBEXSLT_STATIC)
+ #define EXSLTPUBVAR __declspec(dllimport) extern
+ #else
+ #define EXSLTPUBVAR extern
+ #endif
+ #endif
+ #define EXSLTCALL __cdecl
+ #if !defined _REENTRANT
+ #define _REENTRANT
+ #endif
+#endif
+
+/* Windows platform with GNU compiler (Mingw) */
+#if defined(_WIN32) && defined(__MINGW32__)
+ #undef EXSLTPUBFUN
+ #undef EXSLTPUBVAR
+ #undef EXSLTCALL
+/*
+ #if defined(IN_LIBEXSLT) && !defined(LIBEXSLT_STATIC)
+*/
+ #if !defined(LIBEXSLT_STATIC)
+ #define EXSLTPUBFUN __declspec(dllexport)
+ #define EXSLTPUBVAR __declspec(dllexport) extern
+ #else
+ #define EXSLTPUBFUN
+ #if !defined(LIBEXSLT_STATIC)
+ #define EXSLTPUBVAR __declspec(dllimport) extern
+ #else
+ #define EXSLTPUBVAR extern
+ #endif
+ #endif
+ #define EXSLTCALL __cdecl
+ #if !defined _REENTRANT
+ #define _REENTRANT
+ #endif
+#endif
+
+/* Cygwin platform, GNU compiler */
+#if defined(_WIN32) && defined(__CYGWIN__)
+ #undef EXSLTPUBFUN
+ #undef EXSLTPUBVAR
+ #undef EXSLTCALL
+ #if defined(IN_LIBEXSLT) && !defined(LIBEXSLT_STATIC)
+ #define EXSLTPUBFUN __declspec(dllexport)
+ #define EXSLTPUBVAR __declspec(dllexport)
+ #else
+ #define EXSLTPUBFUN
+ #if !defined(LIBEXSLT_STATIC)
+ #define EXSLTPUBVAR __declspec(dllimport) extern
+ #else
+ #define EXSLTPUBVAR
+ #endif
+ #endif
+ #define EXSLTCALL __cdecl
+#endif
+
+/* Compatibility */
+#if !defined(LIBEXSLT_PUBLIC)
+#define LIBEXSLT_PUBLIC EXSLTPUBVAR
+#endif
+
+#endif /* __EXSLT_EXPORTS_H__ */
+
+
diff --git a/libxslt/libexslt/functions.c b/libxslt/libexslt/functions.c
new file mode 100644
index 0000000..b49fbe6
--- /dev/null
+++ b/libxslt/libexslt/functions.c
@@ -0,0 +1,777 @@
+#define IN_LIBEXSLT
+#include "libexslt/libexslt.h"
+
+#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/hash.h>
+#include <libxml/debugXML.h>
+
+#include <libxslt/xsltutils.h>
+#include <libxslt/variables.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/extensions.h>
+#include <libxslt/transform.h>
+#include <libxslt/imports.h>
+
+#include "exslt.h"
+
+typedef struct _exsltFuncFunctionData exsltFuncFunctionData;
+struct _exsltFuncFunctionData {
+ int nargs; /* number of arguments to the function */
+ xmlNodePtr content; /* the func:fuction template content */
+};
+
+typedef struct _exsltFuncData exsltFuncData;
+struct _exsltFuncData {
+ xmlHashTablePtr funcs; /* pointer to the stylesheet module data */
+ xmlXPathObjectPtr result; /* returned by func:result */
+ int error; /* did an error occur? */
+};
+
+typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp;
+struct _exsltFuncResultPreComp {
+ xsltElemPreComp comp;
+ xmlXPathCompExprPtr select;
+ xmlNsPtr *nsList;
+ int nsNr;
+};
+
+/* Used for callback function in exsltInitFunc */
+typedef struct _exsltFuncImportRegData exsltFuncImportRegData;
+struct _exsltFuncImportRegData {
+ xsltTransformContextPtr ctxt;
+ xmlHashTablePtr hash;
+};
+
+static void exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt,
+ int nargs);
+static exsltFuncFunctionData *exsltFuncNewFunctionData(void);
+
+/*static const xmlChar *exsltResultDataID = (const xmlChar *) "EXSLT Result";*/
+
+/**
+ * exsltFuncRegisterFunc:
+ * @func: the #exsltFuncFunctionData for the function
+ * @ctxt: an XSLT transformation context
+ * @URI: the function namespace URI
+ * @name: the function name
+ *
+ * Registers a function declared by a func:function element
+ */
+static void
+exsltFuncRegisterFunc (exsltFuncFunctionData *data,
+ xsltTransformContextPtr ctxt,
+ const xmlChar *URI, const xmlChar *name,
+ ATTRIBUTE_UNUSED const xmlChar *ignored) {
+ if ((data == NULL) || (ctxt == NULL) || (URI == NULL) || (name == NULL))
+ return;
+
+ xsltGenericDebug(xsltGenericDebugContext,
+ "exsltFuncRegisterFunc: register {%s}%s\n",
+ URI, name);
+ xsltRegisterExtFunction(ctxt, name, URI,
+ exsltFuncFunctionFunction);
+}
+
+/*
+ * exsltFuncRegisterImportFunc
+ * @data: the exsltFuncFunctionData for the function
+ * @ch: structure containing context and hash table
+ * @URI: the function namespace URI
+ * @name: the function name
+ *
+ * Checks if imported function is already registered in top-level
+ * stylesheet. If not, copies function data and registers function
+ */
+static void
+exsltFuncRegisterImportFunc (exsltFuncFunctionData *data,
+ exsltFuncImportRegData *ch,
+ const xmlChar *URI, const xmlChar *name,
+ ATTRIBUTE_UNUSED const xmlChar *ignored) {
+ exsltFuncFunctionData *func=NULL;
+
+ if ((data == NULL) || (ch == NULL) || (URI == NULL) || (name == NULL))
+ return;
+
+ if (ch->ctxt == NULL || ch->hash == NULL)
+ return;
+
+ /* Check if already present */
+ func = (exsltFuncFunctionData*)xmlHashLookup2(ch->hash, URI, name);
+ if (func == NULL) { /* Not yet present - copy it in */
+ func = exsltFuncNewFunctionData();
+ if (func == NULL)
+ return;
+ memcpy(func, data, sizeof(exsltFuncFunctionData));
+ if (xmlHashAddEntry2(ch->hash, URI, name, func) < 0) {
+ xsltGenericError(xsltGenericErrorContext,
+ "Failed to register function {%s}%s\n",
+ URI, name);
+ } else { /* Do the registration */
+ xsltGenericDebug(xsltGenericDebugContext,
+ "exsltFuncRegisterImportFunc: register {%s}%s\n",
+ URI, name);
+ xsltRegisterExtFunction(ch->ctxt, name, URI,
+ exsltFuncFunctionFunction);
+ }
+ }
+}
+
+/**
+ * exsltFuncInit:
+ * @ctxt: an XSLT transformation context
+ * @URI: the namespace URI for the extension
+ *
+ * Initializes the EXSLT - Functions module.
+ * Called at transformation-time; merges all
+ * functions declared in the import tree taking
+ * import precedence into account, i.e. overriding
+ * functions with lower import precedence.
+ *
+ * Returns the data for this transformation
+ */
+static exsltFuncData *
+exsltFuncInit (xsltTransformContextPtr ctxt, const xmlChar *URI) {
+ exsltFuncData *ret;
+ xsltStylesheetPtr tmp;
+ exsltFuncImportRegData ch;
+ xmlHashTablePtr hash;
+
+ ret = (exsltFuncData *) xmlMalloc (sizeof(exsltFuncData));
+ if (ret == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltFuncInit: not enough memory\n");
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(exsltFuncData));
+
+ ret->result = NULL;
+ ret->error = 0;
+
+ ch.hash = (xmlHashTablePtr) xsltStyleGetExtData(ctxt->style, URI);
+ ret->funcs = ch.hash;
+ xmlHashScanFull(ch.hash, (xmlHashScannerFull) exsltFuncRegisterFunc, ctxt);
+ tmp = ctxt->style;
+ ch.ctxt = ctxt;
+ while ((tmp=xsltNextImport(tmp))!=NULL) {
+ hash = xsltGetExtInfo(tmp, URI);
+ if (hash != NULL) {
+ xmlHashScanFull(hash,
+ (xmlHashScannerFull) exsltFuncRegisterImportFunc, &ch);
+ }
+ }
+
+ return(ret);
+}
+
+/**
+ * exsltFuncShutdown:
+ * @ctxt: an XSLT transformation context
+ * @URI: the namespace URI for the extension
+ * @data: the module data to free up
+ *
+ * Shutdown the EXSLT - Functions module
+ * Called at transformation-time.
+ */
+static void
+exsltFuncShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
+ const xmlChar *URI ATTRIBUTE_UNUSED,
+ exsltFuncData *data) {
+ if (data->result != NULL)
+ xmlXPathFreeObject(data->result);
+ xmlFree(data);
+}
+
+/**
+ * exsltFuncStyleInit:
+ * @style: an XSLT stylesheet
+ * @URI: the namespace URI for the extension
+ *
+ * Allocates the stylesheet data for EXSLT - Function
+ * Called at compile-time.
+ *
+ * Returns the allocated data
+ */
+static xmlHashTablePtr
+exsltFuncStyleInit (xsltStylesheetPtr style ATTRIBUTE_UNUSED,
+ const xmlChar *URI ATTRIBUTE_UNUSED) {
+ return xmlHashCreate(1);
+}
+
+/**
+ * exsltFuncStyleShutdown:
+ * @style: an XSLT stylesheet
+ * @URI: the namespace URI for the extension
+ * @data: the stylesheet data to free up
+ *
+ * Shutdown the EXSLT - Function module
+ * Called at compile-time.
+ */
+static void
+exsltFuncStyleShutdown (xsltStylesheetPtr style ATTRIBUTE_UNUSED,
+ const xmlChar *URI ATTRIBUTE_UNUSED,
+ xmlHashTablePtr data) {
+ xmlHashFree(data, (xmlHashDeallocator) xmlFree);
+}
+
+/**
+ * exsltFuncNewFunctionData:
+ *
+ * Allocates an #exslFuncFunctionData object
+ *
+ * Returns the new structure
+ */
+static exsltFuncFunctionData *
+exsltFuncNewFunctionData (void) {
+ exsltFuncFunctionData *ret;
+
+ ret = (exsltFuncFunctionData *) xmlMalloc (sizeof(exsltFuncFunctionData));
+ if (ret == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltFuncNewFunctionData: not enough memory\n");
+ return (NULL);
+ }
+ memset(ret, 0, sizeof(exsltFuncFunctionData));
+
+ ret->nargs = 0;
+ ret->content = NULL;
+
+ return(ret);
+}
+
+/**
+ * exsltFreeFuncResultPreComp:
+ * @comp: the #exsltFuncResultPreComp to free up
+ *
+ * Deallocates an #exsltFuncResultPreComp
+ */
+static void
+exsltFreeFuncResultPreComp (exsltFuncResultPreComp *comp) {
+ if (comp == NULL)
+ return;
+
+ if (comp->select != NULL)
+ xmlXPathFreeCompExpr (comp->select);
+ if (comp->nsList != NULL)
+ xmlFree(comp->nsList);
+ xmlFree(comp);
+}
+
+/**
+ * exsltFuncFunctionFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Evaluates the func:function element that defines the called function.
+ */
+static void
+exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr oldResult, ret;
+ exsltFuncData *data;
+ exsltFuncFunctionData *func;
+ xmlNodePtr paramNode, oldInsert, fake;
+ int oldBase;
+ xsltStackElemPtr params = NULL, param;
+ xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
+ int i, notSet;
+ struct objChain {
+ struct objChain *next;
+ xmlXPathObjectPtr obj;
+ };
+ struct objChain *savedObjChain = NULL, *savedObj;
+
+ /*
+ * retrieve func:function template
+ */
+ data = (exsltFuncData *) xsltGetExtData (tctxt,
+ EXSLT_FUNCTIONS_NAMESPACE);
+ oldResult = data->result;
+ data->result = NULL;
+
+ func = (exsltFuncFunctionData*) xmlHashLookup2 (data->funcs,
+ ctxt->context->functionURI,
+ ctxt->context->function);
+ if (func == NULL) {
+ /* Should never happen */
+ xsltGenericError(xsltGenericErrorContext,
+ "{%s}%s: not found\n",
+ ctxt->context->functionURI, ctxt->context->function);
+ ctxt->error = XPATH_UNKNOWN_FUNC_ERROR;
+ return;
+ }
+
+ /*
+ * params handling
+ */
+ if (nargs > func->nargs) {
+ xsltGenericError(xsltGenericErrorContext,
+ "{%s}%s: called with too many arguments\n",
+ ctxt->context->functionURI, ctxt->context->function);
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+ if (func->content != NULL) {
+ paramNode = func->content->prev;
+ }
+ else
+ paramNode = NULL;
+ if ((paramNode == NULL) && (func->nargs != 0)) {
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltFuncFunctionFunction: nargs != 0 and "
+ "param == NULL\n");
+ return;
+ }
+
+ /*
+ * We have a problem with the evaluation of function parameters.
+ * The original library code did not evaluate XPath expressions until
+ * the last moment. After version 1.1.17 of the libxslt, the logic
+ * of other parts of the library was changed, and the evaluation of
+ * XPath expressions within parameters now takes place as soon as the
+ * parameter is parsed/evaluated (xsltParseStylesheetCallerParam).
+ * This means that the parameters need to be evaluated in lexical
+ * order (since a variable is "in scope" as soon as it is declared).
+ * However, on entry to this routine, the values (from the caller) are
+ * in reverse order (held on the XPath context variable stack). To
+ * accomplish what is required, I have added code to pop the XPath
+ * objects off of the stack at the beginning and save them, then use
+ * them (in the reverse order) as the params are evaluated. This
+ * requires an xmlMalloc/xmlFree for each param set by the caller,
+ * which is not very nice. There is probably a much better solution
+ * (like change other code to delay the evaluation).
+ */
+ /*
+ * In order to give the function params and variables a new 'scope'
+ * we change varsBase in the context.
+ */
+ oldBase = tctxt->varsBase;
+ tctxt->varsBase = tctxt->varsNr;
+ /* If there are any parameters */
+ if (paramNode != NULL) {
+ /* Fetch the stored argument values from the caller */
+ for (i = 0; i < nargs; i++) {
+ savedObj = xmlMalloc(sizeof(struct objChain));
+ savedObj->next = savedObjChain;
+ savedObj->obj = valuePop(ctxt);
+ savedObjChain = savedObj;
+ }
+
+ /*
+ * Prepare to process params in reverse order. First, go to
+ * the beginning of the param chain.
+ */
+ for (i = 1; i <= func->nargs; i++) {
+ if (paramNode->prev == NULL)
+ break;
+ paramNode = paramNode->prev;
+ }
+ /*
+ * i has total # params found, nargs is number which are present
+ * as arguments from the caller
+ * Calculate the number of un-set parameters
+ */
+ notSet = func->nargs - nargs;
+ for (; i > 0; i--) {
+ param = xsltParseStylesheetCallerParam (tctxt, paramNode);
+ if (i > notSet) { /* if parameter value set */
+ param->computed = 1;
+ if (param->value != NULL)
+ xmlXPathFreeObject(param->value);
+ savedObj = savedObjChain; /* get next val from chain */
+ param->value = savedObj->obj;
+ savedObjChain = savedObjChain->next;
+ xmlFree(savedObj);
+ }
+ xsltLocalVariablePush(tctxt, param, -1);
+ param->next = params;
+ params = param;
+ paramNode = paramNode->next;
+ }
+ }
+ /*
+ * actual processing
+ */
+ fake = xmlNewDocNode(tctxt->output, NULL,
+ (const xmlChar *)"fake", NULL);
+ oldInsert = tctxt->insert;
+ tctxt->insert = fake;
+ xsltApplyOneTemplate (tctxt, xmlXPathGetContextNode(ctxt),
+ func->content, NULL, NULL);
+ xsltLocalVariablePop(tctxt, tctxt->varsBase, -2);
+ tctxt->insert = oldInsert;
+ tctxt->varsBase = oldBase; /* restore original scope */
+ if (params != NULL)
+ xsltFreeStackElemList(params);
+
+ if (data->error != 0)
+ return;
+
+ if (data->result != NULL) {
+ ret = data->result;
+ /*
+ * IMPORTANT: This enables previously tree fragments marked as
+ * being results of a function, to be garbage-collected after
+ * the calling process exits.
+ */
+ xsltFlagRVTs(tctxt, ret, XSLT_RVT_LOCAL);
+ } else
+ ret = xmlXPathNewCString("");
+
+ data->result = oldResult;
+
+ /*
+ * It is an error if the instantiation of the template results in
+ * the generation of result nodes.
+ */
+ if (fake->children != NULL) {
+#ifdef LIBXML_DEBUG_ENABLED
+ xmlDebugDumpNode (stderr, fake, 1);
+#endif
+ xsltGenericError(xsltGenericErrorContext,
+ "{%s}%s: cannot write to result tree while "
+ "executing a function\n",
+ ctxt->context->functionURI, ctxt->context->function);
+ xmlFreeNode(fake);
+ return;
+ }
+ xmlFreeNode(fake);
+ valuePush(ctxt, ret);
+}
+
+
+static void
+exsltFuncFunctionComp (xsltStylesheetPtr style, xmlNodePtr inst) {
+ xmlChar *name, *prefix;
+ xmlNsPtr ns;
+ xmlHashTablePtr data;
+ exsltFuncFunctionData *func;
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+ {
+ xmlChar *qname;
+
+ qname = xmlGetProp(inst, (const xmlChar *) "name");
+ name = xmlSplitQName2 (qname, &prefix);
+ xmlFree(qname);
+ }
+ if ((name == NULL) || (prefix == NULL)) {
+ xsltGenericError(xsltGenericErrorContext,
+ "func:function: not a QName\n");
+ if (name != NULL)
+ xmlFree(name);
+ return;
+ }
+ /* namespace lookup */
+ ns = xmlSearchNs (inst->doc, inst, prefix);
+ if (ns == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "func:function: undeclared prefix %s\n",
+ prefix);
+ xmlFree(name);
+ xmlFree(prefix);
+ return;
+ }
+ xmlFree(prefix);
+
+ xsltParseTemplateContent(style, inst);
+
+ /*
+ * Create function data
+ */
+ func = exsltFuncNewFunctionData();
+ if (func == NULL) {
+ xmlFree(name);
+ return;
+ }
+ func->content = inst->children;
+ while (IS_XSLT_ELEM(func->content) &&
+ IS_XSLT_NAME(func->content, "param")) {
+ func->content = func->content->next;
+ func->nargs++;
+ }
+
+ /*
+ * Register the function data such that it can be retrieved
+ * by exslFuncFunctionFunction
+ */
+#ifdef XSLT_REFACTORED
+ /*
+ * Ensure that the hash table will be stored in the *current*
+ * stylesheet level in order to correctly evaluate the
+ * import precedence.
+ */
+ data = (xmlHashTablePtr)
+ xsltStyleStylesheetLevelGetExtData(style,
+ EXSLT_FUNCTIONS_NAMESPACE);
+#else
+ data = (xmlHashTablePtr)
+ xsltStyleGetExtData (style, EXSLT_FUNCTIONS_NAMESPACE);
+#endif
+ if (data == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltFuncFunctionComp: no stylesheet data\n");
+ xmlFree(name);
+ return;
+ }
+
+ if (xmlHashAddEntry2 (data, ns->href, name, func) < 0) {
+ xsltTransformError(NULL, style, inst,
+ "Failed to register function {%s}%s\n",
+ ns->href, name);
+ style->errors++;
+ } else {
+ xsltGenericDebug(xsltGenericDebugContext,
+ "exsltFuncFunctionComp: register {%s}%s\n",
+ ns->href, name);
+ }
+ xmlFree(name);
+}
+
+static xsltElemPreCompPtr
+exsltFuncResultComp (xsltStylesheetPtr style, xmlNodePtr inst,
+ xsltTransformFunction function) {
+ xmlNodePtr test;
+ xmlChar *sel;
+ exsltFuncResultPreComp *ret;
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return (NULL);
+
+ /*
+ * "Validity" checking
+ */
+ /* it is an error to have any following sibling elements aside
+ * from the xsl:fallback element.
+ */
+ for (test = inst->next; test != NULL; test = test->next) {
+ if (test->type != XML_ELEMENT_NODE)
+ continue;
+ if (IS_XSLT_ELEM(test) && IS_XSLT_NAME(test, "fallback"))
+ continue;
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltFuncResultElem: only xsl:fallback is "
+ "allowed to follow func:result\n");
+ style->errors++;
+ return (NULL);
+ }
+ /* it is an error for a func:result element to not be a descendant
+ * of func:function.
+ * it is an error if a func:result occurs within a func:result
+ * element.
+ * it is an error if instanciating the content of a variable
+ * binding element (i.e. xsl:variable, xsl:param) results in the
+ * instanciation of a func:result element.
+ */
+ for (test = inst->parent; test != NULL; test = test->parent) {
+ if (IS_XSLT_ELEM(test) &&
+ IS_XSLT_NAME(test, "stylesheet")) {
+ xsltGenericError(xsltGenericErrorContext,
+ "func:result element not a descendant "
+ "of a func:function\n");
+ style->errors++;
+ return (NULL);
+ }
+ if ((test->ns != NULL) &&
+ (xmlStrEqual(test->ns->href, EXSLT_FUNCTIONS_NAMESPACE))) {
+ if (xmlStrEqual(test->name, (const xmlChar *) "function")) {
+ break;
+ }
+ if (xmlStrEqual(test->name, (const xmlChar *) "result")) {
+ xsltGenericError(xsltGenericErrorContext,
+ "func:result element not allowed within"
+ " another func:result element\n");
+ style->errors++;
+ return (NULL);
+ }
+ }
+ if (IS_XSLT_ELEM(test) &&
+ (IS_XSLT_NAME(test, "variable") ||
+ IS_XSLT_NAME(test, "param"))) {
+ xsltGenericError(xsltGenericErrorContext,
+ "func:result element not allowed within"
+ " a variable binding element\n");
+ style->errors++;
+ return (NULL);
+ }
+ }
+
+ /*
+ * Precomputation
+ */
+ ret = (exsltFuncResultPreComp *)
+ xmlMalloc (sizeof(exsltFuncResultPreComp));
+ if (ret == NULL) {
+ xsltPrintErrorContext(NULL, NULL, NULL);
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltFuncResultComp : malloc failed\n");
+ style->errors++;
+ return (NULL);
+ }
+ memset(ret, 0, sizeof(exsltFuncResultPreComp));
+
+ xsltInitElemPreComp ((xsltElemPreCompPtr) ret, style, inst, function,
+ (xsltElemPreCompDeallocator) exsltFreeFuncResultPreComp);
+ ret->select = NULL;
+
+ /*
+ * Precompute the select attribute
+ */
+ sel = xmlGetNsProp(inst, (const xmlChar *) "select", NULL);
+ if (sel != NULL) {
+ ret->select = xmlXPathCompile (sel);
+ xmlFree(sel);
+ }
+ /*
+ * Precompute the namespace list
+ */
+ ret->nsList = xmlGetNsList(inst->doc, inst);
+ if (ret->nsList != NULL) {
+ int i = 0;
+ while (ret->nsList[i] != NULL)
+ i++;
+ ret->nsNr = i;
+ }
+ return ((xsltElemPreCompPtr) ret);
+}
+
+static void
+exsltFuncResultElem (xsltTransformContextPtr ctxt,
+ xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst,
+ exsltFuncResultPreComp *comp) {
+ exsltFuncData *data;
+ xmlXPathObjectPtr ret;
+
+
+ /* It is an error if instantiating the content of the
+ * func:function element results in the instantiation of more than
+ * one func:result elements.
+ */
+ data = (exsltFuncData *) xsltGetExtData (ctxt, EXSLT_FUNCTIONS_NAMESPACE);
+ if (data == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltFuncReturnElem: data == NULL\n");
+ return;
+ }
+ if (data->result != NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "func:result already instanciated\n");
+ data->error = 1;
+ return;
+ }
+ /*
+ * Processing
+ */
+ if (comp->select != NULL) {
+ xmlNsPtr *oldXPNsList;
+ int oldXPNsNr;
+ xmlNodePtr oldXPContextNode;
+ /* If the func:result element has a select attribute, then the
+ * value of the attribute must be an expression and the
+ * returned value is the object that results from evaluating
+ * the expression. In this case, the content must be empty.
+ */
+ if (inst->children != NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "func:result content must be empty if"
+ " the function has a select attribute\n");
+ data->error = 1;
+ return;
+ }
+ oldXPNsList = ctxt->xpathCtxt->namespaces;
+ oldXPNsNr = ctxt->xpathCtxt->nsNr;
+ oldXPContextNode = ctxt->xpathCtxt->node;
+
+ ctxt->xpathCtxt->namespaces = comp->nsList;
+ ctxt->xpathCtxt->nsNr = comp->nsNr;
+
+ ret = xmlXPathCompiledEval(comp->select, ctxt->xpathCtxt);
+
+ ctxt->xpathCtxt->node = oldXPContextNode;
+ ctxt->xpathCtxt->nsNr = oldXPNsNr;
+ ctxt->xpathCtxt->namespaces = oldXPNsList;
+
+ if (ret == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltFuncResultElem: ret == NULL\n");
+ return;
+ }
+ /*
+ * Mark it as a function result in order to avoid garbage
+ * collecting of tree fragments before the function exits.
+ */
+ xsltFlagRVTs(ctxt, ret, XSLT_RVT_FUNC_RESULT);
+ } else if (inst->children != NULL) {
+ /* If the func:result element does not have a select attribute
+ * and has non-empty content (i.e. the func:result element has
+ * one or more child nodes), then the content of the
+ * func:result element specifies the value.
+ */
+ xmlNodePtr oldInsert;
+ xmlDocPtr container;
+
+ container = xsltCreateRVT(ctxt);
+ if (container == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltFuncResultElem: out of memory\n");
+ data->error = 1;
+ return;
+ }
+ /* Mark as function result. */
+ container->psvi = XSLT_RVT_FUNC_RESULT;
+
+ oldInsert = ctxt->insert;
+ ctxt->insert = (xmlNodePtr) container;
+ xsltApplyOneTemplate (ctxt, ctxt->xpathCtxt->node,
+ inst->children, NULL, NULL);
+ ctxt->insert = oldInsert;
+
+ ret = xmlXPathNewValueTree((xmlNodePtr) container);
+ if (ret == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "exsltFuncResultElem: ret == NULL\n");
+ data->error = 1;
+ } else {
+ ret->boolval = 0; /* Freeing is not handled there anymore */
+ }
+ } else {
+ /* If the func:result element has empty content and does not
+ * have a select attribute, then the returned value is an
+ * empty string.
+ */
+ ret = xmlXPathNewCString("");
+ }
+ data->result = ret;
+}
+
+/**
+ * exsltFuncRegister:
+ *
+ * Registers the EXSLT - Functions module
+ */
+void
+exsltFuncRegister (void) {
+ xsltRegisterExtModuleFull (EXSLT_FUNCTIONS_NAMESPACE,
+ (xsltExtInitFunction) exsltFuncInit,
+ (xsltExtShutdownFunction) exsltFuncShutdown,
+ (xsltStyleExtInitFunction) exsltFuncStyleInit,
+ (xsltStyleExtShutdownFunction) exsltFuncStyleShutdown);
+
+ xsltRegisterExtModuleTopLevel ((const xmlChar *) "function",
+ EXSLT_FUNCTIONS_NAMESPACE,
+ exsltFuncFunctionComp);
+ xsltRegisterExtModuleElement ((const xmlChar *) "result",
+ EXSLT_FUNCTIONS_NAMESPACE,
+ (xsltPreComputeFunction)exsltFuncResultComp,
+ (xsltTransformFunction) exsltFuncResultElem);
+}
diff --git a/libxslt/libexslt/libexslt.3 b/libxslt/libexslt/libexslt.3
new file mode 100644
index 0000000..83c57d3
--- /dev/null
+++ b/libxslt/libexslt/libexslt.3
@@ -0,0 +1,270 @@
+.TH LIBEXSLT 3 "04 November 2003" libxslt
+.SH NAME
+libexslt \- extension library for XSLT
+.SH SYNOPSIS
+.B #include <libexslt/exslt.h>
+.sp
+.B void exsltCommonRegister(void);
+.br
+.B void exsltDateRegister(void);
+.br
+.B void exsltDynRegister(void);
+.br
+.B void exsltFuncRegister(void);
+.br
+.B void exsltMathRegister(void);
+.br
+.B void exsltSetsRegister(void);
+.br
+.B void exsltStrRegister(void);
+.br
+.B void exsltRegisterAll(void);
+.br
+.B void exsltSaxonRegister(void);
+.SH DESCRIPTION
+The
+.B libexslt
+library is used to provide extensions to
+.SM XSLT
+functions. These extensions come from the
+.SM EXSLT
+project <http://www.exslt.org/>
+.LP
+.SH USAGE
+To make use of these functions in
+.SM XSLT
+the appropriate namespace must be defined on the
+.B xsl:stylesheet
+element. To enable support for them in
+.BR libxslt (3)
+you must call the appropriate functions (listed in the
+.B SYNOPSIS
+section) to register the extensions. The
+.I xslt-config
+shell script can be used to obtain the necessary flags for
+the pre-processor and linker.
+The supported extensions are:
+.SS COMMON
+.TP 2.2i
+Namespace: http://exslt.org/common
+.TP 2.2i
+See http://www.exslt.org/exsl/index.html for a description.
+.TP 2.2i
+.B node-set()
+convert the given RTF into a node-set.
+.TP
+.B object-type()
+returns the type of the given argument.
+.TP
+.B document
+Create multiple output documents. See http://www.exslt.org/exsl/elements/document/index.html
+
+.SS MATH
+.TP 2.2i
+Namespace: http://exslt.org/math
+.TP 2.2i
+See http://www.exslt.org/math/index.html for a description.
+.TP 2.2i
+.B min()
+returns the minimum value of the given node-set
+.TP
+.B max()
+returns the maximum value of the given node-set
+.TP
+.B highest()
+returns the nodes in the node-set whose value is the maximum value for the node-set.
+.TP
+.B lowest()
+returns the nodes in the node-set whose value is the minimum value for the node-set.
+.TP
+.B constant()
+returns a number value of the given constant with the given precision. The constants are PI, E, SQRRT2, LN2, LN10, LOG2E, and SQRT1_2.
+.TP
+.B random()
+returns a random number between 0 and 1 inclusive.
+.TP
+.B abs()
+returns the absolute value of the argument.
+.TP
+.B sqrt()
+returns the square root of the argument.
+.TP
+.B power()
+returns the power base and power arguments.
+.TP
+.B log()
+returns the natural log of the argument.
+.TP
+.B sin()
+returns the sine of the argument.
+.TP
+.B cos()
+returns the cosine of the argument.
+.TP
+.B tan()
+returns the tangent of the argument.
+.TP
+.B asin()
+returns the arc sine of the argument.
+.TP
+.B acos()
+returns the arc cosine of the argument.
+.TP
+.B atan()
+returns the arc tangent of the argument.
+.TP
+.B atan2()
+returns the arc tangent function of the y/x arguments.
+.TP
+.B exp()
+returns the exponential function of the argument.
+
+.SS SETS
+.TP 2.2i
+Namespace: http://exslt.org/sets
+.TP 2.2i
+See http://www.exslt.org/set/index.html for a description.
+.TP 2.2i
+.B difference()
+returns the difference between the two given node-sets.
+.TP
+.B intersection()
+returns a node-set of the nodes within both given node-sets.
+.TP
+.B distinct()
+returns a node-set of all nodes in the first argument that are not in the seconds argument.
+.TP
+.B has-same-node()
+returns TRUE if there is an intersection between the two given node-sets.
+.TP
+.B leading()
+returns a node-set of all nodes in the first argument that precede the first node in the second argument.
+.TP
+.B trailing()
+returns a node-set of all nodes in the first argument that follow the first node in the second argument.
+
+.SS "DATES and TIMES"
+.TP 2.2i
+Namespace: http://exslt.org/dates-and-times
+.TP 2.2i
+See http://www.exslt.org/date/date.html for a description.
+.TP 2.2i
+.B date-time()
+returns the current date and time as a date/time string.
+.TP
+.B date()
+returns the date specified in the given date/time string.
+.TP
+.B time()
+returns the time specified in the date/time string given as the argument.
+.TP
+.B year()
+returns the year of a date as a number.
+.TP
+.B leap-year()
+returns true if the year given in a date is a leap year.
+.TP
+.B month-in-year()
+returns the month of a date as a number.
+.TP
+.B month-name()
+returns the full name of the month of a date.
+.TP
+.B month-abbreviation()
+returns the abbreviation of the month of a date.
+.TP
+.B week-in-year()
+returns the week of the year as a number.
+.TP
+.B week-in-month()
+returns the week in a month of a date as a number.
+.TP
+.B day-in-year()
+returns the month of a date as a number.
+.TP
+.B day-in-month()
+returns the day of a date as a number.
+.TP
+.B day-of-week-in-month()
+returns the day-of-the-week in a month of a date as a number.
+.TP
+.B day-in-week()
+returns the day of the week given in a date as a number.
+.TP
+.B day-name()
+returns the full name of the day of the week of a date.
+.TP
+.B day-abbreviation()
+returns the abbreviation of the day of the week of a date.
+.TP
+.B hour-in-day()
+returns the hour of the day as a number.
+.TP
+.B minute-in-hour()
+returns the minute of the hour as a number.
+.TP
+.B second-in-minute()
+returns the second of the minute as a number.
+.TP
+.B seconds()
+returns the number of seconds specified by the argument string.
+.TP
+.B add()
+returns the date/time resulting from adding a duration to a date/time.
+.TP
+.B add-duration()
+returns the duration resulting from adding two given durations together.
+.TP
+.B difference()
+returns the duration between the first date and the second date.
+.TP
+.B duration()
+returns a duration string that represents the given number of seconds since 1970-01-01T00:00:00.
+
+.SS STRINGS
+.TP 2.2i
+Namespace: http://exslt.org/strings
+.TP 2.2i
+See http://www.exslt.org/str/index.html for a description.
+.TP 2.2i
+.B tokenize()
+returns a node set of token elements, each containing one token from the string.
+.TP
+.B padding()
+returns a string padded to a certain length.
+.TP
+.B align()
+returns a string aligned within another string.
+.TP
+.B concat()
+returns the concatenation of the string values of the nodes in that node set.
+
+.SS FUNCTIONS
+.TP 2.2i
+Namespace: http://exslt.org/functions
+.TP 2.2i
+See http://www.exslt.org/func/index.html for a description.
+.TP 2.2i
+.B function
+declares an extension function.
+.TP
+.B result
+returns the result of an extension function declared in function().
+.SH FILES
+.TP
+.I /usr/bin/xslt-config
+shell script giving pre-processor and linker flags.
+.TP
+.I /usr/lib/libexslt.a
+static library
+.TP
+.I /usr/lib/libexslt.so
+sharable library
+.SH AUTHORS
+Manual page by Heiko W. Rupp (hwr@pilhuhn.de)
+.SH "SEE ALSO"
+.BR libxml (3),
+.BR libxslt (3),
+.BR xmllint (1)
+.BR xsltproc (1),
+.\" end of manual page
diff --git a/libxslt/libexslt/libexslt.h b/libxslt/libexslt/libexslt.h
new file mode 100644
index 0000000..2dd9b37
--- /dev/null
+++ b/libxslt/libexslt/libexslt.h
@@ -0,0 +1,29 @@
+/*
+ * libexslt.h: internal header only used during the compilation of libexslt
+ *
+ * See COPYRIGHT for the status of this software
+ *
+ * Author: daniel@veillard.com
+ */
+
+#ifndef __XSLT_LIBEXSLT_H__
+#define __XSLT_LIBEXSLT_H__
+
+#if defined(WIN32) && !defined (__CYGWIN__) && !defined (__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#include <libxslt/xsltconfig.h>
+#include <libxml/xmlversion.h>
+
+#if !defined LIBEXSLT_PUBLIC
+#if (defined (__CYGWIN__) || defined _MSC_VER) && !defined IN_LIBEXSLT && !defined LIBEXSLT_STATIC
+#define LIBEXSLT_PUBLIC __declspec(dllimport)
+#else
+#define LIBEXSLT_PUBLIC
+#endif
+#endif
+
+#endif /* ! __XSLT_LIBEXSLT_H__ */
diff --git a/libxslt/libexslt/math.c b/libxslt/libexslt/math.c
new file mode 100644
index 0000000..6b24dbe
--- /dev/null
+++ b/libxslt/libexslt/math.c
@@ -0,0 +1,1202 @@
+#define IN_LIBEXSLT
+#include "libexslt/libexslt.h"
+
+#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <libxslt/xsltconfig.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/extensions.h>
+
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "exslt.h"
+
+/**
+ * exsltMathMin:
+ * @ns: a node-set
+ *
+ * Implements the EXSLT - Math min() function:
+ * number math:min (node-set)
+ *
+ * Returns the minimum value of the nodes passed as the argument, or
+ * xmlXPathNAN if @ns is NULL or empty or if one of the nodes
+ * turns into NaN.
+ */
+static double
+exsltMathMin (xmlNodeSetPtr ns) {
+ double ret, cur;
+ int i;
+
+ if ((ns == NULL) || (ns->nodeNr == 0))
+ return(xmlXPathNAN);
+ ret = xmlXPathCastNodeToNumber(ns->nodeTab[0]);
+ if (xmlXPathIsNaN(ret))
+ return(xmlXPathNAN);
+ for (i = 1; i < ns->nodeNr; i++) {
+ cur = xmlXPathCastNodeToNumber(ns->nodeTab[i]);
+ if (xmlXPathIsNaN(cur))
+ return(xmlXPathNAN);
+ if (cur < ret)
+ ret = cur;
+ }
+ return(ret);
+}
+
+/**
+ * exsltMathMinFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathMin for use by the XPath processor.
+ */
+static void
+exsltMathMinFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlNodeSetPtr ns;
+ double ret;
+ void *user = NULL;
+
+ if (nargs != 1) {
+ xsltGenericError(xsltGenericErrorContext,
+ "math:min: invalid number of arguments\n");
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+ /* We need to delay the freeing of value->user */
+ if ((ctxt->value != NULL) && (ctxt->value->boolval != 0)) {
+ user = ctxt->value->user;
+ ctxt->value->boolval = 0;
+ ctxt->value->user = NULL;
+ }
+ ns = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathMin(ns);
+
+ xmlXPathFreeNodeSet(ns);
+ if (user != NULL)
+ xmlFreeNodeList((xmlNodePtr)user);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathMax:
+ * @ns: a node-set
+ *
+ * Implements the EXSLT - Math max() function:
+ * number math:max (node-set)
+ *
+ * Returns the maximum value of the nodes passed as arguments, or
+ * xmlXPathNAN if @ns is NULL or empty or if one of the nodes
+ * turns into NaN.
+ */
+static double
+exsltMathMax (xmlNodeSetPtr ns) {
+ double ret, cur;
+ int i;
+
+ if ((ns == NULL) || (ns->nodeNr == 0))
+ return(xmlXPathNAN);
+ ret = xmlXPathCastNodeToNumber(ns->nodeTab[0]);
+ if (xmlXPathIsNaN(ret))
+ return(xmlXPathNAN);
+ for (i = 1; i < ns->nodeNr; i++) {
+ cur = xmlXPathCastNodeToNumber(ns->nodeTab[i]);
+ if (xmlXPathIsNaN(cur))
+ return(xmlXPathNAN);
+ if (cur > ret)
+ ret = cur;
+ }
+ return(ret);
+}
+
+/**
+ * exsltMathMaxFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathMax for use by the XPath processor.
+ */
+static void
+exsltMathMaxFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlNodeSetPtr ns;
+ double ret;
+ void *user = NULL;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ /* We need to delay the freeing of value->user */
+ if ((ctxt->value != NULL) && (ctxt->value->boolval != 0)) {
+ user = ctxt->value->user;
+ ctxt->value->boolval = 0;
+ ctxt->value->user = 0;
+ }
+ ns = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathMax(ns);
+
+ xmlXPathFreeNodeSet(ns);
+
+ if (user != NULL)
+ xmlFreeNodeList((xmlNodePtr)user);
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathHighest:
+ * @ns: a node-set
+ *
+ * Implements the EXSLT - Math highest() function:
+ * node-set math:highest (node-set)
+ *
+ * Returns the nodes in the node-set whose value is the maximum value
+ * for the node-set.
+ */
+static xmlNodeSetPtr
+exsltMathHighest (xmlNodeSetPtr ns) {
+ xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
+ double max, cur;
+ int i;
+
+ if ((ns == NULL) || (ns->nodeNr == 0))
+ return(ret);
+
+ max = xmlXPathCastNodeToNumber(ns->nodeTab[0]);
+ if (xmlXPathIsNaN(max))
+ return(ret);
+ else
+ xmlXPathNodeSetAddUnique(ret, ns->nodeTab[0]);
+
+ for (i = 1; i < ns->nodeNr; i++) {
+ cur = xmlXPathCastNodeToNumber(ns->nodeTab[i]);
+ if (xmlXPathIsNaN(cur)) {
+ xmlXPathEmptyNodeSet(ret);
+ return(ret);
+ }
+ if (cur < max)
+ continue;
+ if (cur > max) {
+ max = cur;
+ xmlXPathEmptyNodeSet(ret);
+ xmlXPathNodeSetAddUnique(ret, ns->nodeTab[i]);
+ continue;
+ }
+ xmlXPathNodeSetAddUnique(ret, ns->nodeTab[i]);
+ }
+ return(ret);
+}
+
+/**
+ * exsltMathHighestFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathHighest for use by the XPath processor
+ */
+static void
+exsltMathHighestFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlNodeSetPtr ns, ret;
+ void *user = NULL;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ /* We need to delay the freeing of value->user */
+ if ((ctxt->value != NULL) && ctxt->value->boolval != 0) {
+ user = ctxt->value->user;
+ ctxt->value->boolval = 0;
+ ctxt->value->user = NULL;
+ }
+ ns = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathHighest(ns);
+
+ xmlXPathFreeNodeSet(ns);
+ if (user != NULL)
+ xmlFreeNodeList((xmlNodePtr)user);
+
+ xmlXPathReturnNodeSet(ctxt, ret);
+}
+
+/**
+ * exsltMathLowest:
+ * @ns: a node-set
+ *
+ * Implements the EXSLT - Math lowest() function
+ * node-set math:lowest (node-set)
+ *
+ * Returns the nodes in the node-set whose value is the minimum value
+ * for the node-set.
+ */
+static xmlNodeSetPtr
+exsltMathLowest (xmlNodeSetPtr ns) {
+ xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
+ double min, cur;
+ int i;
+
+ if ((ns == NULL) || (ns->nodeNr == 0))
+ return(ret);
+
+ min = xmlXPathCastNodeToNumber(ns->nodeTab[0]);
+ if (xmlXPathIsNaN(min))
+ return(ret);
+ else
+ xmlXPathNodeSetAddUnique(ret, ns->nodeTab[0]);
+
+ for (i = 1; i < ns->nodeNr; i++) {
+ cur = xmlXPathCastNodeToNumber(ns->nodeTab[i]);
+ if (xmlXPathIsNaN(cur)) {
+ xmlXPathEmptyNodeSet(ret);
+ return(ret);
+ }
+ if (cur > min)
+ continue;
+ if (cur < min) {
+ min = cur;
+ xmlXPathEmptyNodeSet(ret);
+ xmlXPathNodeSetAddUnique(ret, ns->nodeTab[i]);
+ continue;
+ }
+ xmlXPathNodeSetAddUnique(ret, ns->nodeTab[i]);
+ }
+ return(ret);
+}
+
+/**
+ * exsltMathLowestFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathLowest for use by the XPath processor
+ */
+static void
+exsltMathLowestFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlNodeSetPtr ns, ret;
+ void *user = NULL;
+
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ /* We need to delay the freeing of value->user */
+ if ((ctxt->value != NULL) && (ctxt->value->boolval != 0)) {
+ user = ctxt->value->user;
+ ctxt->value->boolval = 0;
+ ctxt->value->user = NULL;
+ }
+ ns = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathLowest(ns);
+
+ xmlXPathFreeNodeSet(ns);
+ if (user != NULL)
+ xmlFreeNodeList((xmlNodePtr)user);
+
+ xmlXPathReturnNodeSet(ctxt, ret);
+}
+
+/* math other functions */
+
+/* constant values */
+#define EXSLT_PI (const xmlChar *) \
+ "3.1415926535897932384626433832795028841971693993751"
+#define EXSLT_E (const xmlChar *) \
+ "2.71828182845904523536028747135266249775724709369996"
+#define EXSLT_SQRRT2 (const xmlChar *) \
+ "1.41421356237309504880168872420969807856967187537694"
+#define EXSLT_LN2 (const xmlChar *) \
+ "0.69314718055994530941723212145817656807550013436025"
+#define EXSLT_LN10 (const xmlChar *) \
+ "2.30258509299404568402"
+#define EXSLT_LOG2E (const xmlChar *) \
+ "1.4426950408889634074"
+#define EXSLT_SQRT1_2 (const xmlChar *) \
+ "0.70710678118654752440"
+
+/**
+ * exsltMathConstant
+ * @name: string
+ * @precision: number
+ *
+ * Implements the EXSLT - Math constant function:
+ * number math:constant(string, number)
+ *
+ * Returns a number value of the given constant with the given precision or
+ * xmlXPathNAN if name is unknown.
+ * The constants are PI, E, SQRRT2, LN2, LN10, LOG2E, and SQRT1_2
+ */
+static double
+exsltMathConstant (xmlChar *name, double precision) {
+ xmlChar *str;
+ double ret;
+
+ if ((name == NULL) || (xmlXPathIsNaN(precision)) || (precision < 1.0)) {
+ return xmlXPathNAN;
+ }
+
+ if (xmlStrEqual(name, BAD_CAST "PI")) {
+ int len = xmlStrlen(EXSLT_PI);
+
+ if (precision <= len)
+ len = (int)precision;
+
+ str = xmlStrsub(EXSLT_PI, 0, len);
+
+ } else if (xmlStrEqual(name, BAD_CAST "E")) {
+ int len = xmlStrlen(EXSLT_E);
+
+ if (precision <= len)
+ len = (int)precision;
+
+ str = xmlStrsub(EXSLT_E, 0, len);
+
+ } else if (xmlStrEqual(name, BAD_CAST "SQRRT2")) {
+ int len = xmlStrlen(EXSLT_SQRRT2);
+
+ if (precision <= len)
+ len = (int)precision;
+
+ str = xmlStrsub(EXSLT_SQRRT2, 0, len);
+
+ } else if (xmlStrEqual(name, BAD_CAST "LN2")) {
+ int len = xmlStrlen(EXSLT_LN2);
+
+ if (precision <= len)
+ len = (int)precision;
+
+ str = xmlStrsub(EXSLT_LN2, 0, len);
+
+ } else if (xmlStrEqual(name, BAD_CAST "LN10")) {
+ int len = xmlStrlen(EXSLT_LN10);
+
+ if (precision <= len)
+ len = (int)precision;
+
+ str = xmlStrsub(EXSLT_LN10, 0, len);
+
+ } else if (xmlStrEqual(name, BAD_CAST "LOG2E")) {
+ int len = xmlStrlen(EXSLT_LOG2E);
+
+ if (precision <= len)
+ len = (int)precision;
+
+ str = xmlStrsub(EXSLT_LOG2E, 0, len);
+
+ } else if (xmlStrEqual(name, BAD_CAST "SQRT1_2")) {
+ int len = xmlStrlen(EXSLT_SQRT1_2);
+
+ if (precision <= len)
+ len = (int)precision;
+
+ str = xmlStrsub(EXSLT_SQRT1_2, 0, len);
+
+ } else {
+ str = NULL;
+ }
+ if (str == NULL)
+ return xmlXPathNAN;
+ ret = xmlXPathCastStringToNumber(str);
+ xmlFree(str);
+ return ret;
+}
+
+/**
+ * exsltMathConstantFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathConstant for use by the XPath processor.
+ */
+static void
+exsltMathConstantFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+ xmlChar *name;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ name = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathConstant(name, ret);
+ if (name != NULL)
+ xmlFree(name);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+#if defined(HAVE_STDLIB_H) && defined(RAND_MAX)
+
+/**
+ * exsltMathRandom:
+ *
+ * Implements the EXSLT - Math random() function:
+ * number math:random ()
+ *
+ * Returns a random number between 0 and 1 inclusive.
+ */
+static double
+exsltMathRandom (void) {
+ double ret;
+ int num;
+
+ num = rand();
+ ret = (double)num / (double)RAND_MAX;
+ return(ret);
+}
+
+/**
+ * exsltMathRandomFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathRandom for use by the XPath processor.
+ */
+static void
+exsltMathRandomFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 0) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ ret = exsltMathRandom();
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+#endif /* defined(HAVE_STDLIB_H) && defined(RAND_MAX) */
+
+#if HAVE_MATH_H
+
+/**
+ * exsltMathAbs:
+ * @num: a double
+ *
+ * Implements the EXSLT - Math abs() function:
+ * number math:abs (number)
+ *
+ * Returns the absolute value of the argument, or xmlXPathNAN if @num is Nan.
+ */
+static double
+exsltMathAbs (double num) {
+ double ret;
+
+ if (xmlXPathIsNaN(num))
+ return(xmlXPathNAN);
+ ret = fabs(num);
+ return(ret);
+}
+
+/**
+ * exsltMathAbsFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathAbs for use by the XPath processor.
+ */
+static void
+exsltMathAbsFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathAbs(ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathSqrt:
+ * @num: a double
+ *
+ * Implements the EXSLT - Math sqrt() function:
+ * number math:sqrt (number)
+ *
+ * Returns the square root of the argument, or xmlXPathNAN if @num is Nan.
+ */
+static double
+exsltMathSqrt (double num) {
+ double ret;
+
+ if (xmlXPathIsNaN(num))
+ return(xmlXPathNAN);
+ ret = sqrt(num);
+ return(ret);
+}
+
+/**
+ * exsltMathSqrtFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathSqrt for use by the XPath processor.
+ */
+static void
+exsltMathSqrtFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathSqrt(ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathPower:
+ * @base: a double
+ * @power: a double
+ *
+ * Implements the EXSLT - Math power() function:
+ * number math:power (number, number)
+ *
+ * Returns the power base and power arguments, or xmlXPathNAN
+ * if either @base or @power is Nan.
+ */
+static double
+exsltMathPower (double base, double power) {
+ double ret;
+
+ if ((xmlXPathIsNaN(base) || xmlXPathIsNaN(power)))
+ return(xmlXPathNAN);
+ ret = pow(base, power);
+ return(ret);
+}
+
+/**
+ * exsltMathPower:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathPower for use by the XPath processor.
+ */
+static void
+exsltMathPowerFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret, base;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ /* power */
+ base = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathPower(base, ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathLog:
+ * @num: a double
+ *
+ * Implements the EXSLT - Math log() function:
+ * number math:log (number)
+ *
+ * Returns the natural log of the argument, or xmlXPathNAN if @num is Nan.
+ */
+static double
+exsltMathLog (double num) {
+ double ret;
+
+ if (xmlXPathIsNaN(num))
+ return(xmlXPathNAN);
+ ret = log(num);
+ return(ret);
+}
+
+/**
+ * exsltMathLogFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathLog for use by the XPath processor.
+ */
+static void
+exsltMathLogFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathLog(ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathSin:
+ * @num: a double
+ *
+ * Implements the EXSLT - Math sin() function:
+ * number math:sin (number)
+ *
+ * Returns the sine of the argument, or xmlXPathNAN if @num is Nan.
+ */
+static double
+exsltMathSin (double num) {
+ double ret;
+
+ if (xmlXPathIsNaN(num))
+ return(xmlXPathNAN);
+ ret = sin(num);
+ return(ret);
+}
+
+/**
+ * exsltMathSinFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathSin for use by the XPath processor.
+ */
+static void
+exsltMathSinFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathSin(ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathCos:
+ * @num: a double
+ *
+ * Implements the EXSLT - Math cos() function:
+ * number math:cos (number)
+ *
+ * Returns the cosine of the argument, or xmlXPathNAN if @num is Nan.
+ */
+static double
+exsltMathCos (double num) {
+ double ret;
+
+ if (xmlXPathIsNaN(num))
+ return(xmlXPathNAN);
+ ret = cos(num);
+ return(ret);
+}
+
+/**
+ * exsltMathCosFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathCos for use by the XPath processor.
+ */
+static void
+exsltMathCosFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathCos(ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathTan:
+ * @num: a double
+ *
+ * Implements the EXSLT - Math tan() function:
+ * number math:tan (number)
+ *
+ * Returns the tangent of the argument, or xmlXPathNAN if @num is Nan.
+ */
+static double
+exsltMathTan (double num) {
+ double ret;
+
+ if (xmlXPathIsNaN(num))
+ return(xmlXPathNAN);
+ ret = tan(num);
+ return(ret);
+}
+
+/**
+ * exsltMathTanFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathTan for use by the XPath processor.
+ */
+static void
+exsltMathTanFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathTan(ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathAsin:
+ * @num: a double
+ *
+ * Implements the EXSLT - Math asin() function:
+ * number math:asin (number)
+ *
+ * Returns the arc sine of the argument, or xmlXPathNAN if @num is Nan.
+ */
+static double
+exsltMathAsin (double num) {
+ double ret;
+
+ if (xmlXPathIsNaN(num))
+ return(xmlXPathNAN);
+ ret = asin(num);
+ return(ret);
+}
+
+/**
+ * exsltMathAsinFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathAsin for use by the XPath processor.
+ */
+static void
+exsltMathAsinFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathAsin(ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathAcos:
+ * @num: a double
+ *
+ * Implements the EXSLT - Math acos() function:
+ * number math:acos (number)
+ *
+ * Returns the arc cosine of the argument, or xmlXPathNAN if @num is Nan.
+ */
+static double
+exsltMathAcos (double num) {
+ double ret;
+
+ if (xmlXPathIsNaN(num))
+ return(xmlXPathNAN);
+ ret = acos(num);
+ return(ret);
+}
+
+/**
+ * exsltMathAcosFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathAcos for use by the XPath processor.
+ */
+static void
+exsltMathAcosFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathAcos(ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathAtan:
+ * @num: a double
+ *
+ * Implements the EXSLT - Math atan() function:
+ * number math:atan (number)
+ *
+ * Returns the arc tangent of the argument, or xmlXPathNAN if @num is Nan.
+ */
+static double
+exsltMathAtan (double num) {
+ double ret;
+
+ if (xmlXPathIsNaN(num))
+ return(xmlXPathNAN);
+ ret = atan(num);
+ return(ret);
+}
+
+/**
+ * exsltMathAtanFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathAtan for use by the XPath processor.
+ */
+static void
+exsltMathAtanFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathAtan(ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathAtan2:
+ * @y: a double
+ * @x: a double
+ *
+ * Implements the EXSLT - Math atan2() function:
+ * number math:atan2 (number, number)
+ *
+ * Returns the arc tangent function of the y/x arguments, or xmlXPathNAN
+ * if either @y or @x is Nan.
+ */
+static double
+exsltMathAtan2 (double y, double x) {
+ double ret;
+
+ if ((xmlXPathIsNaN(y) || xmlXPathIsNaN(x)))
+ return(xmlXPathNAN);
+ ret = atan2(y, x);
+ return(ret);
+}
+
+/**
+ * exsltMathAtan2Function:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathAtan2 for use by the XPath processor.
+ */
+static void
+exsltMathAtan2Function (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret, x;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ x = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ /* y */
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathAtan2(ret, x);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+/**
+ * exsltMathExp:
+ * @num: a double
+ *
+ * Implements the EXSLT - Math exp() function:
+ * number math:exp (number)
+ *
+ * Returns the exponential function of the argument, or xmlXPathNAN if
+ * @num is Nan.
+ */
+static double
+exsltMathExp (double num) {
+ double ret;
+
+ if (xmlXPathIsNaN(num))
+ return(xmlXPathNAN);
+ ret = exp(num);
+ return(ret);
+}
+
+/**
+ * exsltMathExpFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #exsltMathExp for use by the XPath processor.
+ */
+static void
+exsltMathExpFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ double ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+ ret = xmlXPathPopNumber(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ ret = exsltMathExp(ret);
+
+ xmlXPathReturnNumber(ctxt, ret);
+}
+
+#endif /* HAVE_MATH_H */
+
+/**
+ * exsltMathRegister:
+ *
+ * Registers the EXSLT - Math module
+ */
+
+void
+exsltMathRegister (void) {
+ xsltRegisterExtModuleFunction ((const xmlChar *) "min",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathMinFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "max",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathMaxFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "highest",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathHighestFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "lowest",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathLowestFunction);
+ /* register other math functions */
+ xsltRegisterExtModuleFunction ((const xmlChar *) "constant",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathConstantFunction);
+#ifdef HAVE_STDLIB_H
+ xsltRegisterExtModuleFunction ((const xmlChar *) "random",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathRandomFunction);
+#endif
+#if HAVE_MATH_H
+ xsltRegisterExtModuleFunction ((const xmlChar *) "abs",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathAbsFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "sqrt",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathSqrtFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "power",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathPowerFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "log",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathLogFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "sin",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathSinFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "cos",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathCosFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "tan",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathTanFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "asin",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathAsinFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "acos",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathAcosFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "atan",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathAtanFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "atan2",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathAtan2Function);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "exp",
+ EXSLT_MATH_NAMESPACE,
+ exsltMathExpFunction);
+#endif
+}
+
+/**
+ * exsltMathXpathCtxtRegister:
+ *
+ * Registers the EXSLT - Math module for use outside XSLT
+ */
+int
+exsltMathXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix)
+{
+ if (ctxt
+ && prefix
+ && !xmlXPathRegisterNs(ctxt,
+ prefix,
+ (const xmlChar *) EXSLT_MATH_NAMESPACE)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "min",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathMinFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "max",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathMaxFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "highest",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathHighestFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "lowest",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathLowestFunction)
+#ifdef HAVE_STDLIB_H
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "random",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathRandomFunction)
+#endif
+#if HAVE_MATH_H
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "abs",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathAbsFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "sqrt",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathSqrtFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "power",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathPowerFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "log",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathLogFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "sin",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathSinFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "cos",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathCosFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "tan",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathTanFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "asin",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathAsinFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "acos",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathAcosFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "atan",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathAtanFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "atan2",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathAtan2Function)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "exp",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathExpFunction)
+#endif
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "constant",
+ (const xmlChar *) EXSLT_MATH_NAMESPACE,
+ exsltMathConstantFunction)) {
+ return 0;
+ }
+ return -1;
+}
diff --git a/libxslt/libexslt/saxon.c b/libxslt/libexslt/saxon.c
new file mode 100644
index 0000000..7a2f63b
--- /dev/null
+++ b/libxslt/libexslt/saxon.c
@@ -0,0 +1,313 @@
+#define IN_LIBEXSLT
+#include "libexslt/libexslt.h"
+
+#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/parser.h>
+#include <libxml/hash.h>
+
+#include <libxslt/xsltconfig.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/extensions.h>
+
+#include "exslt.h"
+
+/**
+ * exsltSaxonInit:
+ * @ctxt: an XSLT transformation context
+ * @URI: the namespace URI for the extension
+ *
+ * Initializes the SAXON module.
+ *
+ * Returns the data for this transformation
+ */
+static xmlHashTablePtr
+exsltSaxonInit (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
+ const xmlChar *URI ATTRIBUTE_UNUSED) {
+ return xmlHashCreate(1);
+}
+
+/**
+ * exsltSaxonShutdown:
+ * @ctxt: an XSLT transformation context
+ * @URI: the namespace URI for the extension
+ * @data: the module data to free up
+ *
+ * Shutdown the SAXON extension module
+ */
+static void
+exsltSaxonShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
+ const xmlChar *URI ATTRIBUTE_UNUSED,
+ xmlHashTablePtr data) {
+ xmlHashFree(data, (xmlHashDeallocator) xmlXPathFreeCompExpr);
+}
+
+
+/**
+ * exsltSaxonExpressionFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * The supplied string must contain an XPath expression. The result of
+ * the function is a stored expression, which may be supplied as an
+ * argument to other extension functions such as saxon:eval(),
+ * saxon:sum() and saxon:distinct(). The result of the expression will
+ * usually depend on the current node. The expression may contain
+ * references to variables that are in scope at the point where
+ * saxon:expression() is called: these variables will be replaced in
+ * the stored expression with the values they take at the time
+ * saxon:expression() is called, not the values of the variables at
+ * the time the stored expression is evaluated. Similarly, if the
+ * expression contains namespace prefixes, these are interpreted in
+ * terms of the namespace declarations in scope at the point where the
+ * saxon:expression() function is called, not those in scope where the
+ * stored expression is evaluated.
+ *
+ * TODO: current implementation doesn't conform to SAXON behaviour
+ * regarding context and namespaces.
+ */
+static void
+exsltSaxonExpressionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlChar *arg;
+ xmlXPathCompExprPtr ret;
+ xmlHashTablePtr hash;
+ xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ arg = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt) || (arg == NULL)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ hash = (xmlHashTablePtr) xsltGetExtData(tctxt,
+ ctxt->context->functionURI);
+
+ ret = xmlHashLookup(hash, arg);
+
+ if (ret == NULL) {
+ ret = xmlXPathCompile(arg);
+ if (ret == NULL) {
+ xmlFree(arg);
+ xmlXPathSetError(ctxt, XPATH_EXPR_ERROR);
+ return;
+ }
+ xmlHashAddEntry(hash, arg, (void *) ret);
+ }
+
+ xmlFree(arg);
+
+ xmlXPathReturnExternal(ctxt, ret);
+}
+
+/**
+ * exsltSaxonEvalFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: number of arguments
+ *
+ * Implements de SAXON eval() function:
+ * object saxon:eval (saxon:stored-expression)
+ * Returns the result of evaluating the supplied stored expression.
+ * A stored expression may be obtained as the result of calling
+ * the saxon:expression() function.
+ * The stored expression is evaluated in the current context, that
+ * is, the context node is the current node, and the context position
+ * and context size are the same as the result of calling position()
+ * or last() respectively.
+ */
+static void
+exsltSaxonEvalFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathCompExprPtr expr;
+ xmlXPathObjectPtr ret;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (!xmlXPathStackIsExternal(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ expr = (xmlXPathCompExprPtr) xmlXPathPopExternal(ctxt);
+
+ ret = xmlXPathCompiledEval(expr, ctxt->context);
+ if (ret == NULL) {
+ xmlXPathSetError(ctxt, XPATH_EXPR_ERROR);
+ return;
+ }
+
+ valuePush(ctxt, ret);
+}
+
+/**
+ * exsltSaxonEvaluateFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: number of arguments
+ *
+ * Implements the SAXON evaluate() function
+ * object saxon:evaluate (string)
+ * The supplied string must contain an XPath expression. The result of
+ * the function is the result of evaluating the XPath expression. This
+ * is useful where an expression needs to be constructed at run-time or
+ * passed to the stylesheet as a parameter, for example where the sort
+ * key is determined dynamically. The context for the expression (e.g.
+ * which variables and namespaces are available) is exactly the same as
+ * if the expression were written explicitly at this point in the
+ * stylesheet. The function saxon:evaluate(string) is shorthand for
+ * saxon:eval(saxon:expression(string)).
+ */
+static void
+exsltSaxonEvaluateFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ exsltSaxonExpressionFunction(ctxt, 1);
+ exsltSaxonEvalFunction(ctxt, 1);
+}
+
+/**
+ * exsltSaxonSystemIdFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: number of arguments
+ *
+ * Implements the SAXON systemId() function
+ * string saxon:systemId ()
+ * This function returns the system ID of the document being styled.
+ */
+static void
+exsltSaxonSystemIdFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (ctxt == NULL)
+ return;
+ if (nargs != 0) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if ((ctxt->context) && (ctxt->context->doc) &&
+ (ctxt->context->doc->URL))
+ valuePush(ctxt, xmlXPathNewString(ctxt->context->doc->URL));
+ else
+ valuePush(ctxt, xmlXPathNewString(BAD_CAST ""));
+}
+
+/**
+ * exsltSaxonLineNumberFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: number of arguments
+ *
+ * Implements the SAXON line-number() function
+ * integer saxon:line-number()
+ *
+ * This returns the line number of the context node in the source document
+ * within the entity that contains it. There are no arguments. If line numbers
+ * are not maintained for the current document, the function returns -1. (To
+ * ensure that line numbers are maintained, use the -l option on the command
+ * line)
+ *
+ * The extension has been extended to have the following form:
+ * integer saxon:line-number([node-set-1])
+ * If a node-set is given, this extension will return the line number of the
+ * node in the argument node-set that is first in document order.
+ */
+static void
+exsltSaxonLineNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlNodePtr cur = NULL;
+ xmlXPathObjectPtr obj = NULL;
+ long lineNo = -1;
+
+ if (nargs == 0) {
+ cur = ctxt->context->node;
+ } else if (nargs == 1) {
+ xmlNodeSetPtr nodelist;
+ int i;
+
+ if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "saxon:line-number() : invalid arg expecting a node-set\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ return;
+ }
+
+ obj = valuePop(ctxt);
+ nodelist = obj->nodesetval;
+ if ((nodelist != NULL) && (nodelist->nodeNr > 0)) {
+ cur = nodelist->nodeTab[0];
+ for (i = 1;i < nodelist->nodeNr;i++) {
+ int ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
+ if (ret == -1)
+ cur = nodelist->nodeTab[i];
+ }
+ }
+ } else {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "saxon:line-number() : invalid number of args %d\n",
+ nargs);
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+
+ if ((cur != NULL) && (cur->type == XML_NAMESPACE_DECL)) {
+ /*
+ * The XPath module sets the owner element of a ns-node on
+ * the ns->next field.
+ */
+ cur = (xmlNodePtr) ((xmlNsPtr) cur)->next;
+ if (cur == NULL || cur->type != XML_ELEMENT_NODE) {
+ xsltGenericError(xsltGenericErrorContext,
+ "Internal error in exsltSaxonLineNumberFunction: "
+ "Cannot retrieve the doc of a namespace node.\n");
+ cur = NULL;
+ }
+ }
+
+ if (cur != NULL)
+ lineNo = xmlGetLineNo(cur);
+
+ valuePush(ctxt, xmlXPathNewFloat(lineNo));
+
+ xmlXPathFreeObject(obj);
+}
+
+/**
+ * exsltSaxonRegister:
+ *
+ * Registers the SAXON extension module
+ */
+void
+exsltSaxonRegister (void) {
+ xsltRegisterExtModule (SAXON_NAMESPACE,
+ (xsltExtInitFunction) exsltSaxonInit,
+ (xsltExtShutdownFunction) exsltSaxonShutdown);
+ xsltRegisterExtModuleFunction((const xmlChar *) "expression",
+ SAXON_NAMESPACE,
+ exsltSaxonExpressionFunction);
+ xsltRegisterExtModuleFunction((const xmlChar *) "eval",
+ SAXON_NAMESPACE,
+ exsltSaxonEvalFunction);
+ xsltRegisterExtModuleFunction((const xmlChar *) "evaluate",
+ SAXON_NAMESPACE,
+ exsltSaxonEvaluateFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "line-number",
+ SAXON_NAMESPACE,
+ exsltSaxonLineNumberFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "systemId",
+ SAXON_NAMESPACE,
+ exsltSaxonSystemIdFunction);
+}
diff --git a/libxslt/libexslt/sets.c b/libxslt/libexslt/sets.c
new file mode 100644
index 0000000..a5a7913
--- /dev/null
+++ b/libxslt/libexslt/sets.c
@@ -0,0 +1,334 @@
+#define IN_LIBEXSLT
+#include "libexslt/libexslt.h"
+
+#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <libxslt/xsltutils.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/extensions.h>
+
+#include "exslt.h"
+
+/**
+ * exsltSetsDifferenceFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #xmlXPathDifference for use by the XPath processor
+ */
+static void
+exsltSetsDifferenceFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlNodeSetPtr arg1, arg2, ret;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ arg2 = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ arg1 = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ ret = xmlXPathDifference(arg1, arg2);
+
+ if (ret != arg1)
+ xmlXPathFreeNodeSet(arg1);
+ xmlXPathFreeNodeSet(arg2);
+
+ xmlXPathReturnNodeSet(ctxt, ret);
+}
+
+/**
+ * exsltSetsIntersectionFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #xmlXPathIntersection for use by the XPath processor
+ */
+static void
+exsltSetsIntersectionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlNodeSetPtr arg1, arg2, ret;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ arg2 = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ arg1 = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ ret = xmlXPathIntersection(arg1, arg2);
+
+ xmlXPathFreeNodeSet(arg1);
+ xmlXPathFreeNodeSet(arg2);
+
+ xmlXPathReturnNodeSet(ctxt, ret);
+}
+
+/**
+ * exsltSetsDistinctFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #xmlXPathDistinct for use by the XPath processor
+ */
+static void
+exsltSetsDistinctFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr obj;
+ xmlNodeSetPtr ns, ret;
+ int boolval = 0;
+ void *user = NULL;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (ctxt->value != NULL) {
+ boolval = ctxt->value->boolval;
+ user = ctxt->value->user;
+ ctxt->value->boolval = 0;
+ ctxt->value->user = NULL;
+ }
+ ns = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+
+ /* !!! must be sorted !!! */
+ ret = xmlXPathDistinctSorted(ns);
+
+ if (ret != ns)
+ xmlXPathFreeNodeSet(ns);
+
+ obj = xmlXPathWrapNodeSet(ret);
+ obj->user = user;
+ obj->boolval = boolval;
+ valuePush((ctxt), obj);
+}
+
+/**
+ * exsltSetsHasSameNodesFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #xmlXPathHasSameNodes for use by the XPath processor
+ */
+static void
+exsltSetsHasSameNodesFunction (xmlXPathParserContextPtr ctxt,
+ int nargs) {
+ xmlNodeSetPtr arg1, arg2;
+ int ret;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ arg2 = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ arg1 = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ ret = xmlXPathHasSameNodes(arg1, arg2);
+
+ xmlXPathFreeNodeSet(arg1);
+ xmlXPathFreeNodeSet(arg2);
+
+ xmlXPathReturnBoolean(ctxt, ret);
+}
+
+/**
+ * exsltSetsLeadingFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #xmlXPathLeading for use by the XPath processor
+ */
+static void
+exsltSetsLeadingFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlNodeSetPtr arg1, arg2, ret;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ arg2 = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ arg1 = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ /* If the second node set is empty, then the first node set is
+ * returned.
+ */
+ if (xmlXPathNodeSetIsEmpty(arg2)) {
+ xmlXPathReturnNodeSet(ctxt, arg1);
+
+ xmlXPathFreeNodeSet(arg2);
+
+ return;
+ }
+ /* !!! must be sorted */
+ ret = xmlXPathNodeLeadingSorted(arg1, xmlXPathNodeSetItem(arg2, 0));
+
+ xmlXPathFreeNodeSet(arg1);
+ xmlXPathFreeNodeSet(arg2);
+
+ xmlXPathReturnNodeSet(ctxt, ret);
+}
+
+/**
+ * exsltSetsTrailingFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Wraps #xmlXPathTrailing for use by the XPath processor
+ */
+static void
+exsltSetsTrailingFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlNodeSetPtr arg1, arg2, ret;
+
+ if (nargs != 2) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ arg2 = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ arg1 = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ /* If the second node set is empty, then the first node set is
+ * returned.
+ */
+ if (xmlXPathNodeSetIsEmpty(arg2)) {
+ xmlXPathReturnNodeSet(ctxt, arg1);
+
+ xmlXPathFreeNodeSet(arg2);
+
+ return;
+ }
+ /* !!! mist be sorted */
+ ret = xmlXPathNodeTrailingSorted(arg1, xmlXPathNodeSetItem(arg2, 0));
+
+ xmlXPathFreeNodeSet(arg1);
+ xmlXPathFreeNodeSet(arg2);
+
+ xmlXPathReturnNodeSet(ctxt, ret);
+}
+
+/**
+ * exsltSetsRegister:
+ *
+ * Registers the EXSLT - Sets module
+ */
+
+void
+exsltSetsRegister (void) {
+ xsltRegisterExtModuleFunction ((const xmlChar *) "difference",
+ EXSLT_SETS_NAMESPACE,
+ exsltSetsDifferenceFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "intersection",
+ EXSLT_SETS_NAMESPACE,
+ exsltSetsIntersectionFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "distinct",
+ EXSLT_SETS_NAMESPACE,
+ exsltSetsDistinctFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "has-same-node",
+ EXSLT_SETS_NAMESPACE,
+ exsltSetsHasSameNodesFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "leading",
+ EXSLT_SETS_NAMESPACE,
+ exsltSetsLeadingFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "trailing",
+ EXSLT_SETS_NAMESPACE,
+ exsltSetsTrailingFunction);
+}
+
+/**
+ * exsltSetsXpathCtxtRegister:
+ *
+ * Registers the EXSLT - Sets module for use outside XSLT
+ */
+int
+exsltSetsXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix)
+{
+ if (ctxt
+ && prefix
+ && !xmlXPathRegisterNs(ctxt,
+ prefix,
+ (const xmlChar *) EXSLT_SETS_NAMESPACE)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "difference",
+ (const xmlChar *) EXSLT_SETS_NAMESPACE,
+ exsltSetsDifferenceFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "intersection",
+ (const xmlChar *) EXSLT_SETS_NAMESPACE,
+ exsltSetsIntersectionFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "distinct",
+ (const xmlChar *) EXSLT_SETS_NAMESPACE,
+ exsltSetsDistinctFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "has-same-node",
+ (const xmlChar *) EXSLT_SETS_NAMESPACE,
+ exsltSetsHasSameNodesFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "leading",
+ (const xmlChar *) EXSLT_SETS_NAMESPACE,
+ exsltSetsLeadingFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "trailing",
+ (const xmlChar *) EXSLT_SETS_NAMESPACE,
+ exsltSetsTrailingFunction)) {
+ return 0;
+ }
+ return -1;
+}
diff --git a/libxslt/libexslt/strings.c b/libxslt/libexslt/strings.c
new file mode 100644
index 0000000..62f76fb
--- /dev/null
+++ b/libxslt/libexslt/strings.c
@@ -0,0 +1,836 @@
+#define IN_LIBEXSLT
+#include "libexslt/libexslt.h"
+
+#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/parser.h>
+#include <libxml/encoding.h>
+#include <libxml/uri.h>
+
+#include <libxslt/xsltconfig.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/extensions.h>
+
+#include "exslt.h"
+
+/**
+ * exsltStrTokenizeFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Splits up a string on the characters of the delimiter string and returns a
+ * node set of token elements, each containing one token from the string.
+ */
+static void
+exsltStrTokenizeFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xsltTransformContextPtr tctxt;
+ xmlChar *str, *delimiters, *cur;
+ const xmlChar *token, *delimiter;
+ xmlNodePtr node;
+ xmlDocPtr container;
+ xmlXPathObjectPtr ret = NULL;
+ int clen;
+
+ if ((nargs < 1) || (nargs > 2)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 2) {
+ delimiters = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+ } else {
+ delimiters = xmlStrdup((const xmlChar *) "\t\r\n ");
+ }
+ if (delimiters == NULL)
+ return;
+
+ str = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt) || (str == NULL)) {
+ xmlFree(delimiters);
+ return;
+ }
+
+ /* Return a result tree fragment */
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if (tctxt == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "exslt:tokenize : internal error tctxt == NULL\n");
+ goto fail;
+ }
+
+ container = xsltCreateRVT(tctxt);
+ if (container != NULL) {
+ xsltRegisterLocalRVT(tctxt, container);
+ ret = xmlXPathNewNodeSet(NULL);
+ if (ret != NULL) {
+ for (cur = str, token = str; *cur != 0; cur += clen) {
+ clen = xmlUTF8Size(cur);
+ if (*delimiters == 0) { /* empty string case */
+ xmlChar ctmp;
+ ctmp = *(cur+clen);
+ *(cur+clen) = 0;
+ node = xmlNewDocRawNode(container, NULL,
+ (const xmlChar *) "token", cur);
+ xmlAddChild((xmlNodePtr) container, node);
+ xmlXPathNodeSetAddUnique(ret->nodesetval, node);
+ *(cur+clen) = ctmp; /* restore the changed byte */
+ token = cur + clen;
+ } else for (delimiter = delimiters; *delimiter != 0;
+ delimiter += xmlUTF8Size(delimiter)) {
+ if (!xmlUTF8Charcmp(cur, delimiter)) {
+ if (cur == token) {
+ /* discard empty tokens */
+ token = cur + clen;
+ break;
+ }
+ *cur = 0; /* terminate the token */
+ node = xmlNewDocRawNode(container, NULL,
+ (const xmlChar *) "token", token);
+ xmlAddChild((xmlNodePtr) container, node);
+ xmlXPathNodeSetAddUnique(ret->nodesetval, node);
+ *cur = *delimiter; /* restore the changed byte */
+ token = cur + clen;
+ break;
+ }
+ }
+ }
+ if (token != cur) {
+ node = xmlNewDocRawNode(container, NULL,
+ (const xmlChar *) "token", token);
+ xmlAddChild((xmlNodePtr) container, node);
+ xmlXPathNodeSetAddUnique(ret->nodesetval, node);
+ }
+ }
+ }
+
+fail:
+ if (str != NULL)
+ xmlFree(str);
+ if (delimiters != NULL)
+ xmlFree(delimiters);
+ if (ret != NULL)
+ valuePush(ctxt, ret);
+ else
+ valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+}
+
+/**
+ * exsltStrSplitFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Splits up a string on a delimiting string and returns a node set of token
+ * elements, each containing one token from the string.
+ */
+static void
+exsltStrSplitFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xsltTransformContextPtr tctxt;
+ xmlChar *str, *delimiter, *cur;
+ const xmlChar *token;
+ xmlNodePtr node;
+ xmlDocPtr container;
+ xmlXPathObjectPtr ret = NULL;
+ int delimiterLength;
+
+ if ((nargs < 1) || (nargs > 2)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 2) {
+ delimiter = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ return;
+ } else {
+ delimiter = xmlStrdup((const xmlChar *) " ");
+ }
+ if (delimiter == NULL)
+ return;
+ delimiterLength = xmlStrlen (delimiter);
+
+ str = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt) || (str == NULL)) {
+ xmlFree(delimiter);
+ return;
+ }
+
+ /* Return a result tree fragment */
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if (tctxt == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "exslt:tokenize : internal error tctxt == NULL\n");
+ goto fail;
+ }
+
+ /*
+ * OPTIMIZE TODO: We are creating an xmlDoc for every split!
+ */
+ container = xsltCreateRVT(tctxt);
+ if (container != NULL) {
+ xsltRegisterLocalRVT(tctxt, container);
+ ret = xmlXPathNewNodeSet(NULL);
+ if (ret != NULL) {
+ for (cur = str, token = str; *cur != 0; cur++) {
+ if (delimiterLength == 0) {
+ if (cur != token) {
+ xmlChar tmp = *cur;
+ *cur = 0;
+ node = xmlNewDocRawNode(container, NULL,
+ (const xmlChar *) "token", token);
+ xmlAddChild((xmlNodePtr) container, node);
+ xmlXPathNodeSetAddUnique(ret->nodesetval, node);
+ *cur = tmp;
+ token++;
+ }
+ }
+ else if (!xmlStrncasecmp(cur, delimiter, delimiterLength)) {
+ if (cur == token) {
+ /* discard empty tokens */
+ cur = cur + delimiterLength - 1;
+ token = cur + 1;
+ continue;
+ }
+ *cur = 0;
+ node = xmlNewDocRawNode(container, NULL,
+ (const xmlChar *) "token", token);
+ xmlAddChild((xmlNodePtr) container, node);
+ xmlXPathNodeSetAddUnique(ret->nodesetval, node);
+ *cur = *delimiter;
+ cur = cur + delimiterLength - 1;
+ token = cur + 1;
+ }
+ }
+ if (token != cur) {
+ node = xmlNewDocRawNode(container, NULL,
+ (const xmlChar *) "token", token);
+ xmlAddChild((xmlNodePtr) container, node);
+ xmlXPathNodeSetAddUnique(ret->nodesetval, node);
+ }
+ }
+ }
+
+fail:
+ if (str != NULL)
+ xmlFree(str);
+ if (delimiter != NULL)
+ xmlFree(delimiter);
+ if (ret != NULL)
+ valuePush(ctxt, ret);
+ else
+ valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+}
+
+/**
+ * exsltStrEncodeUriFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * URI-Escapes a string
+ */
+static void
+exsltStrEncodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ int escape_all = 1, str_len = 0;
+ xmlChar *str = NULL, *ret = NULL, *tmp;
+
+ if ((nargs < 2) || (nargs > 3)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs >= 3) {
+ /* check for UTF-8 if encoding was explicitly given;
+ we don't support anything else yet */
+ tmp = xmlXPathPopString(ctxt);
+ if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) {
+ xmlXPathReturnEmptyString(ctxt);
+ xmlFree(tmp);
+ return;
+ }
+ xmlFree(tmp);
+ }
+
+ escape_all = xmlXPathPopBoolean(ctxt);
+
+ str = xmlXPathPopString(ctxt);
+ str_len = xmlUTF8Strlen(str);
+
+ if (str_len == 0) {
+ xmlXPathReturnEmptyString(ctxt);
+ xmlFree(str);
+ return;
+ }
+
+ ret = xmlURIEscapeStr(str,(const xmlChar *)(escape_all?"-_.!~*'()":"-_.!~*'();/?:@&=+$,[]"));
+ xmlXPathReturnString(ctxt, ret);
+
+ if (str != NULL)
+ xmlFree(str);
+}
+
+/**
+ * exsltStrDecodeUriFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * reverses URI-Escaping of a string
+ */
+static void
+exsltStrDecodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ int str_len = 0;
+ xmlChar *str = NULL, *ret = NULL, *tmp;
+
+ if ((nargs < 1) || (nargs > 2)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs >= 2) {
+ /* check for UTF-8 if encoding was explicitly given;
+ we don't support anything else yet */
+ tmp = xmlXPathPopString(ctxt);
+ if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) {
+ xmlXPathReturnEmptyString(ctxt);
+ xmlFree(tmp);
+ return;
+ }
+ xmlFree(tmp);
+ }
+
+ str = xmlXPathPopString(ctxt);
+ str_len = xmlUTF8Strlen(str);
+
+ if (str_len == 0) {
+ xmlXPathReturnEmptyString(ctxt);
+ xmlFree(str);
+ return;
+ }
+
+ ret = (xmlChar *) xmlURIUnescapeString((const char *)str,0,NULL);
+ if (!xmlCheckUTF8(ret)) {
+ /* FIXME: instead of throwing away the whole URI, we should
+ only discard the invalid sequence(s). How to do that? */
+ xmlXPathReturnEmptyString(ctxt);
+ xmlFree(str);
+ xmlFree(ret);
+ return;
+ }
+
+ xmlXPathReturnString(ctxt, ret);
+
+ if (str != NULL)
+ xmlFree(str);
+}
+
+/**
+ * exsltStrPaddingFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Creates a padding string of a certain length.
+ */
+static void
+exsltStrPaddingFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ int number, str_len = 0, str_size = 0;
+ xmlChar *str = NULL, *ret = NULL;
+
+ if ((nargs < 1) || (nargs > 2)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 2) {
+ str = xmlXPathPopString(ctxt);
+ str_len = xmlUTF8Strlen(str);
+ str_size = xmlStrlen(str);
+ }
+ if (str_len == 0) {
+ if (str != NULL) xmlFree(str);
+ str = xmlStrdup((const xmlChar *) " ");
+ str_len = 1;
+ str_size = 1;
+ }
+
+ number = (int) xmlXPathPopNumber(ctxt);
+
+ if (number <= 0) {
+ xmlXPathReturnEmptyString(ctxt);
+ xmlFree(str);
+ return;
+ }
+
+ while (number >= str_len) {
+ ret = xmlStrncat(ret, str, str_size);
+ number -= str_len;
+ }
+ if (number > 0) {
+ str_size = xmlUTF8Strsize(str, number);
+ ret = xmlStrncat(ret, str, str_size);
+ }
+
+ xmlXPathReturnString(ctxt, ret);
+
+ if (str != NULL)
+ xmlFree(str);
+}
+
+/**
+ * exsltStrAlignFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Aligns a string within another string.
+ */
+static void
+exsltStrAlignFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlChar *str, *padding, *alignment, *ret;
+ int str_l, padding_l;
+
+ if ((nargs < 2) || (nargs > 3)) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (nargs == 3)
+ alignment = xmlXPathPopString(ctxt);
+ else
+ alignment = NULL;
+
+ padding = xmlXPathPopString(ctxt);
+ str = xmlXPathPopString(ctxt);
+
+ str_l = xmlUTF8Strlen (str);
+ padding_l = xmlUTF8Strlen (padding);
+
+ if (str_l == padding_l) {
+ xmlXPathReturnString (ctxt, str);
+ xmlFree(padding);
+ xmlFree(alignment);
+ return;
+ }
+
+ if (str_l > padding_l) {
+ ret = xmlUTF8Strndup (str, padding_l);
+ } else {
+ if (xmlStrEqual(alignment, (const xmlChar *) "right")) {
+ ret = xmlUTF8Strndup (padding, padding_l - str_l);
+ ret = xmlStrcat (ret, str);
+ } else if (xmlStrEqual(alignment, (const xmlChar *) "center")) {
+ int left = (padding_l - str_l) / 2;
+ int right_start;
+
+ ret = xmlUTF8Strndup (padding, left);
+ ret = xmlStrcat (ret, str);
+
+ right_start = xmlUTF8Strsize (padding, left + str_l);
+ ret = xmlStrcat (ret, padding + right_start);
+ } else {
+ int str_s;
+
+ str_s = xmlUTF8Strsize(padding, str_l);
+ ret = xmlStrdup (str);
+ ret = xmlStrcat (ret, padding + str_s);
+ }
+ }
+
+ xmlXPathReturnString (ctxt, ret);
+
+ xmlFree(str);
+ xmlFree(padding);
+ xmlFree(alignment);
+}
+
+/**
+ * exsltStrConcatFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Takes a node set and returns the concatenation of the string values
+ * of the nodes in that node set. If the node set is empty, it
+ * returns an empty string.
+ */
+static void
+exsltStrConcatFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr obj;
+ xmlChar *ret = NULL;
+ int i;
+
+ if (nargs != 1) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ if (!xmlXPathStackIsNodeSet(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return;
+ }
+
+ obj = valuePop (ctxt);
+
+ if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
+ xmlXPathReturnEmptyString(ctxt);
+ return;
+ }
+
+ for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+ xmlChar *tmp;
+ tmp = xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
+
+ ret = xmlStrcat (ret, tmp);
+
+ xmlFree(tmp);
+ }
+
+ xmlXPathFreeObject (obj);
+
+ xmlXPathReturnString(ctxt, ret);
+}
+
+/**
+ * exsltStrReturnString:
+ * @ctxt: an XPath parser context
+ * @str: a string
+ * @len: length of string
+ *
+ * Returns a string as a node set.
+ */
+static int
+exsltStrReturnString(xmlXPathParserContextPtr ctxt, const xmlChar *str,
+ int len)
+{
+ xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
+ xmlDocPtr container;
+ xmlNodePtr text_node;
+ xmlXPathObjectPtr ret;
+
+ container = xsltCreateRVT(tctxt);
+ if (container == NULL) {
+ xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
+ return(-1);
+ }
+ xsltRegisterLocalRVT(tctxt, container);
+
+ text_node = xmlNewTextLen(str, len);
+ if (text_node == NULL) {
+ xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
+ return(-1);
+ }
+ xmlAddChild((xmlNodePtr) container, text_node);
+
+ ret = xmlXPathNewNodeSet(text_node);
+ if (ret == NULL) {
+ xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
+ return(-1);
+ }
+
+ valuePush(ctxt, ret);
+
+ return(0);
+}
+
+/**
+ * exsltStrReplaceFunction:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments
+ *
+ * Takes a string, and two node sets and returns the string with all strings in
+ * the first node set replaced by all strings in the second node set.
+ */
+static void
+exsltStrReplaceFunction (xmlXPathParserContextPtr ctxt, int nargs) {
+ int i, i_empty, n, slen0, rlen0, *slen, *rlen;
+ void *mem = NULL;
+ const xmlChar *src, *start;
+ xmlChar *string, *search_str = NULL, *replace_str = NULL;
+ xmlChar **search, **replace;
+ xmlNodeSetPtr search_set = NULL, replace_set = NULL;
+ xmlBufferPtr buf;
+
+ if (nargs != 3) {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ /* get replace argument */
+
+ if (!xmlXPathStackIsNodeSet(ctxt))
+ replace_str = xmlXPathPopString(ctxt);
+ else
+ replace_set = xmlXPathPopNodeSet(ctxt);
+
+ if (xmlXPathCheckError(ctxt))
+ goto fail_replace;
+
+ /* get search argument */
+
+ if (!xmlXPathStackIsNodeSet(ctxt)) {
+ search_str = xmlXPathPopString(ctxt);
+ n = 1;
+ }
+ else {
+ search_set = xmlXPathPopNodeSet(ctxt);
+ n = search_set != NULL ? search_set->nodeNr : 0;
+ }
+
+ if (xmlXPathCheckError(ctxt))
+ goto fail_search;
+
+ /* get string argument */
+
+ string = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt))
+ goto fail_string;
+
+ /* check for empty search node list */
+
+ if (n <= 0) {
+ exsltStrReturnString(ctxt, string, xmlStrlen(string));
+ goto done_empty_search;
+ }
+
+ /* allocate memory for string pointer and length arrays */
+
+ if (n == 1) {
+ search = &search_str;
+ replace = &replace_str;
+ slen = &slen0;
+ rlen = &rlen0;
+ }
+ else {
+ mem = xmlMalloc(2 * n * (sizeof(const xmlChar *) + sizeof(int)));
+ if (mem == NULL) {
+ xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
+ goto fail_malloc;
+ }
+ search = (xmlChar **) mem;
+ replace = search + n;
+ slen = (int *) (replace + n);
+ rlen = slen + n;
+ }
+
+ /* process arguments */
+
+ i_empty = -1;
+
+ for (i=0; i<n; ++i) {
+ if (search_set != NULL) {
+ search[i] = xmlXPathCastNodeToString(search_set->nodeTab[i]);
+ if (search[i] == NULL) {
+ n = i;
+ goto fail_process_args;
+ }
+ }
+
+ slen[i] = xmlStrlen(search[i]);
+ if (i_empty < 0 && slen[i] == 0)
+ i_empty = i;
+
+ if (replace_set != NULL) {
+ if (i < replace_set->nodeNr) {
+ replace[i] = xmlXPathCastNodeToString(replace_set->nodeTab[i]);
+ if (replace[i] == NULL) {
+ n = i + 1;
+ goto fail_process_args;
+ }
+ }
+ else
+ replace[i] = NULL;
+ }
+ else {
+ if (i == 0)
+ replace[i] = replace_str;
+ else
+ replace[i] = NULL;
+ }
+
+ if (replace[i] == NULL)
+ rlen[i] = 0;
+ else
+ rlen[i] = xmlStrlen(replace[i]);
+ }
+
+ if (i_empty >= 0 && rlen[i_empty] == 0)
+ i_empty = -1;
+
+ /* replace operation */
+
+ buf = xmlBufferCreate();
+ if (buf == NULL) {
+ xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
+ goto fail_buffer;
+ }
+ src = string;
+ start = string;
+
+ while (*src != 0) {
+ int max_len = 0, i_match = 0;
+
+ for (i=0; i<n; ++i) {
+ if (*src == search[i][0] &&
+ slen[i] > max_len &&
+ xmlStrncmp(src, search[i], slen[i]) == 0)
+ {
+ i_match = i;
+ max_len = slen[i];
+ }
+ }
+
+ if (max_len == 0) {
+ if (i_empty >= 0 && start < src) {
+ if (xmlBufferAdd(buf, start, src - start) ||
+ xmlBufferAdd(buf, replace[i_empty], rlen[i_empty]))
+ {
+ xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
+ goto fail_buffer_add;
+ }
+ start = src;
+ }
+
+ src += xmlUTF8Size(src);
+ }
+ else {
+ if ((start < src &&
+ xmlBufferAdd(buf, start, src - start)) ||
+ (rlen[i_match] &&
+ xmlBufferAdd(buf, replace[i_match], rlen[i_match])))
+ {
+ xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
+ goto fail_buffer_add;
+ }
+
+ src += slen[i_match];
+ start = src;
+ }
+ }
+
+ if (start < src && xmlBufferAdd(buf, start, src - start)) {
+ xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
+ goto fail_buffer_add;
+ }
+
+ /* create result node set */
+
+ exsltStrReturnString(ctxt, xmlBufferContent(buf), xmlBufferLength(buf));
+
+ /* clean up */
+
+fail_buffer_add:
+ xmlBufferFree(buf);
+
+fail_buffer:
+fail_process_args:
+ if (search_set != NULL) {
+ for (i=0; i<n; ++i)
+ xmlFree(search[i]);
+ }
+ if (replace_set != NULL) {
+ for (i=0; i<n; ++i) {
+ if (replace[i] != NULL)
+ xmlFree(replace[i]);
+ }
+ }
+
+ if (mem != NULL)
+ xmlFree(mem);
+
+fail_malloc:
+done_empty_search:
+ xmlFree(string);
+
+fail_string:
+ if (search_set != NULL)
+ xmlXPathFreeNodeSet(search_set);
+ else
+ xmlFree(search_str);
+
+fail_search:
+ if (replace_set != NULL)
+ xmlXPathFreeNodeSet(replace_set);
+ else
+ xmlFree(replace_str);
+
+fail_replace:
+ return;
+}
+
+/**
+ * exsltStrRegister:
+ *
+ * Registers the EXSLT - Strings module
+ */
+
+void
+exsltStrRegister (void) {
+ xsltRegisterExtModuleFunction ((const xmlChar *) "tokenize",
+ EXSLT_STRINGS_NAMESPACE,
+ exsltStrTokenizeFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "split",
+ EXSLT_STRINGS_NAMESPACE,
+ exsltStrSplitFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "encode-uri",
+ EXSLT_STRINGS_NAMESPACE,
+ exsltStrEncodeUriFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "decode-uri",
+ EXSLT_STRINGS_NAMESPACE,
+ exsltStrDecodeUriFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "padding",
+ EXSLT_STRINGS_NAMESPACE,
+ exsltStrPaddingFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "align",
+ EXSLT_STRINGS_NAMESPACE,
+ exsltStrAlignFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "concat",
+ EXSLT_STRINGS_NAMESPACE,
+ exsltStrConcatFunction);
+ xsltRegisterExtModuleFunction ((const xmlChar *) "replace",
+ EXSLT_STRINGS_NAMESPACE,
+ exsltStrReplaceFunction);
+}
+
+/**
+ * exsltStrXpathCtxtRegister:
+ *
+ * Registers the EXSLT - Strings module for use outside XSLT
+ */
+int
+exsltStrXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix)
+{
+ if (ctxt
+ && prefix
+ && !xmlXPathRegisterNs(ctxt,
+ prefix,
+ (const xmlChar *) EXSLT_STRINGS_NAMESPACE)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "encode-uri",
+ (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
+ exsltStrEncodeUriFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "decode-uri",
+ (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
+ exsltStrDecodeUriFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "padding",
+ (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
+ exsltStrPaddingFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "align",
+ (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
+ exsltStrAlignFunction)
+ && !xmlXPathRegisterFuncNS(ctxt,
+ (const xmlChar *) "concat",
+ (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
+ exsltStrConcatFunction)) {
+ return 0;
+ }
+ return -1;
+}