diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-11-17 21:21:33 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-11-17 21:21:33 (GMT) |
commit | 8705a471f0cb989dca5bed1ac1aa9c982146ceb0 (patch) | |
tree | c76eb1b28847599af70596ce7adbfd62ca4985f5 /libxslt/libexslt | |
parent | 8d530e150d787e9a74e27592d4e67a496cd922da (diff) | |
parent | 93eaa8f0a22ef3712b9a4bafdc50ba9a7d80ae8f (diff) | |
download | blt-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.am | 35 | ||||
-rw-r--r-- | libxslt/libexslt/common.c | 131 | ||||
-rw-r--r-- | libxslt/libexslt/crypto.c | 806 | ||||
-rw-r--r-- | libxslt/libexslt/date.c | 3911 | ||||
-rw-r--r-- | libxslt/libexslt/dynamic.c | 301 | ||||
-rw-r--r-- | libxslt/libexslt/exslt.c | 40 | ||||
-rw-r--r-- | libxslt/libexslt/exslt.h | 102 | ||||
-rw-r--r-- | libxslt/libexslt/exsltconfig.h.in | 73 | ||||
-rw-r--r-- | libxslt/libexslt/exsltexports.h | 140 | ||||
-rw-r--r-- | libxslt/libexslt/functions.c | 777 | ||||
-rw-r--r-- | libxslt/libexslt/libexslt.3 | 270 | ||||
-rw-r--r-- | libxslt/libexslt/libexslt.h | 29 | ||||
-rw-r--r-- | libxslt/libexslt/math.c | 1202 | ||||
-rw-r--r-- | libxslt/libexslt/saxon.c | 313 | ||||
-rw-r--r-- | libxslt/libexslt/sets.c | 334 | ||||
-rw-r--r-- | libxslt/libexslt/strings.c | 836 |
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; +} |