diff --git a/ChangeLog b/ChangeLog index be3d6bfa..c20b98df 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Mon Sep 3 02:14:58 CEST 2001 Thomas Broyer + + * libexslt/Makefile.am libexslt/exslt.[ch] libexslt/strings.c: + added implementation of EXSLT - Strings. + Currently implemented functins are str:tokenize, str:align + str:concat and str:padding. + * configure.in tests/exslt/Makefile.am + tests/exslt/strings/Makefile.am + tests/exslt/strings/tokenize.1.*: added a test for the + str:tokenize function. + Fri Aug 31 13:51:53 CEST 2001 Daniel Veillard * libxslt/libxslt.4 libexslt/libexslt.4 libxslt/Makefile.am diff --git a/configure.in b/configure.in index 65d9d4b0..3ebd0649 100644 --- a/configure.in +++ b/configure.in @@ -252,6 +252,7 @@ tests/exslt/common/Makefile tests/exslt/functions/Makefile tests/exslt/math/Makefile tests/exslt/sets/Makefile +tests/exslt/strings/Makefile doc/Makefile xslt-config libxslt.spec diff --git a/libexslt/Makefile.am b/libexslt/Makefile.am index bc2d8ce0..260b928f 100644 --- a/libexslt/Makefile.am +++ b/libexslt/Makefile.am @@ -17,7 +17,8 @@ libexslt_la_SOURCES = \ common.c \ math.c \ sets.c \ - functions.c + functions.c \ + strings.c libexslt_la_LIBADD = $(EXTRA_LIBS) libexslt_la_LDFLAGS = -version-info @LIBEXSLT_VERSION_INFO@ diff --git a/libexslt/exslt.c b/libexslt/exslt.c index 9298500c..78cde86a 100644 --- a/libexslt/exslt.c +++ b/libexslt/exslt.c @@ -26,5 +26,6 @@ exsltRegisterAll (void) { exsltMathRegister(); exsltSetsRegister(); exsltFuncRegister(); + exsltStrRegister(); } diff --git a/libexslt/exslt.h b/libexslt/exslt.h index fd7f5db2..79d10e41 100644 --- a/libexslt/exslt.h +++ b/libexslt/exslt.h @@ -14,11 +14,13 @@ LIBEXSLT_PUBLIC extern const int exsltLibxmlVersion; #define EXSLT_MATH_NAMESPACE ((const xmlChar *) "http://exslt.org/math") #define EXSLT_SETS_NAMESPACE ((const xmlChar *) "http://exslt.org/sets") #define EXSLT_FUNCTIONS_NAMESPACE ((const xmlChar *) "http://exslt.org/functions") +#define EXSLT_STRINGS_NAMESPACE ((const xmlChar *) "http://exslt.org/strings") void exsltCommonRegister (void); void exsltMathRegister (void); void exsltSetsRegister (void); void exsltFuncRegister (void); +void exsltStrRegister (void); void exsltRegisterAll (void); diff --git a/libexslt/strings.c b/libexslt/strings.c new file mode 100644 index 00000000..129a877d --- /dev/null +++ b/libexslt/strings.c @@ -0,0 +1,283 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "exslt.h" + +/** + * exsltStrTokenizeFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * Splits up a string and returns a node set of token elements, each + * containing one token from the string. + */ +static void +exsltStrTokenizeFunction (xmlXPathParserContextPtr ctxt, int nargs) { + xmlChar *str, *delimiters, *cur; + const xmlChar *token, *delimiter; + xmlNodePtr node; + xmlDocPtr doc; + xmlXPathObjectPtr ret; + + 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; + } + + doc = xsltXPathGetTransformContext(ctxt)->output; + ret = xmlXPathNewNodeSet (NULL); + if (ret != NULL) { + ret->boolval = 1; + /* + * This is a hack: token elements are added as children of a + * fake element node. This is necessary to free them up + * correctly when freeing the node-set. + */ + ret->user = (void *) xmlNewDocNode (doc, NULL, + (const xmlChar *) "fake", NULL); + if (ret->user == NULL) + goto error; + } + + for (cur = str, token = str; *cur != 0; cur++) { + for (delimiter = delimiters; *delimiter != 0; delimiter++) { + if (*cur == *delimiter) { + if (cur == token) { + /* discard empty tokens */ + break; + } + *cur = 0; + node = xmlNewDocNode (doc, NULL, + (const xmlChar *) "token", token); + *cur = *delimiter; + token = cur + 1; + + xmlAddChild ((xmlNodePtr) ret->user, node); + xmlXPathNodeSetAdd (ret->nodesetval, node); + break; + } + } + } + node = xmlNewDocNode (doc, NULL, (const xmlChar *) "token", token); + xmlAddChild ((xmlNodePtr) ret->user, node); + xmlXPathNodeSetAdd (ret->nodesetval, node); + + valuePush (ctxt, ret); + ret = NULL; /* hack not to free ret later */ + +error: + if (ret != NULL) + xmlXPathFreeObject (ret); + if (str != NULL) + xmlFree(str); + if (delimiters != NULL) + xmlFree(delimiters); +} + +/** + * 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; + xmlChar *str, *ret = NULL, *tmp; + + if ((nargs < 1) && (nargs > 2)) { + xmlXPathSetArityError(ctxt); + return; + } + + if (nargs == 2) { + str = xmlXPathPopString(ctxt); + str_len = xmlUTF8Strlen(str); + } + if (str_len == 0) { + if (str != NULL) xmlFree(str); + str = xmlStrdup((const xmlChar *) " "); + str_len = 1; + } + + number = (int) xmlXPathPopNumber(ctxt); + + if (number <= 0) { + xmlXPathReturnEmptyString(ctxt); + xmlFree(str); + return; + } + + while (number >= str_len) { + ret = xmlStrncat(ret, str, str_len); + number -= str_len; + } + tmp = xmlUTF8Strndup (str, number); + ret = xmlStrcat(ret, tmp); + if (tmp != NULL) + xmlFree (tmp); + + 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 = xmlStrlen (str); + 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); +} + +/** + * exsltStrRegister: + * + * Registers the EXSLT - Strings module + */ + +void +exsltStrRegister (void) { + xsltRegisterExtModuleFunction ((const xmlChar *) "tokenize", + EXSLT_STRINGS_NAMESPACE, + exsltStrTokenizeFunction); + xsltRegisterExtModuleFunction ((const xmlChar *) "padding", + EXSLT_STRINGS_NAMESPACE, + exsltStrPaddingFunction); + xsltRegisterExtModuleFunction ((const xmlChar *) "align", + EXSLT_STRINGS_NAMESPACE, + exsltStrAlignFunction); + xsltRegisterExtModuleFunction ((const xmlChar *) "concat", + EXSLT_STRINGS_NAMESPACE, + exsltStrConcatFunction); +} diff --git a/tests/exslt/Makefile.am b/tests/exslt/Makefile.am index 2239f133..b9d76665 100644 --- a/tests/exslt/Makefile.am +++ b/tests/exslt/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS=common functions math sets +SUBDIRS=common functions math sets strings all: diff --git a/tests/exslt/strings/Makefile.am b/tests/exslt/strings/Makefile.am new file mode 100644 index 00000000..07c4a967 --- /dev/null +++ b/tests/exslt/strings/Makefile.am @@ -0,0 +1,23 @@ +## Process this file with automake to produce Makefile.in + +$(top_builddir)/xsltproc/xsltproc: + @(cd ../../../xsltproc ; $(MAKE) xsltproc) + +EXTRA_DIST = \ + tokenize.1.xml tokenize.1.xsl tokenize.1.out + +all: test + +test tests: $(top_builddir)/xsltproc/xsltproc + @(echo > .memdump) + @(for i in $(srcdir)/*.xsl ; do \ + name=`basename $$i .xsl` ; \ + if [ ! -f $(srcdir)/$$name.xml ] ; then continue ; fi ; \ + echo $$name.xml ; \ + $(top_builddir)/xsltproc/xsltproc $(srcdir)/$$name.xsl $(srcdir)/$$name.xml > $$name.res;\ + if [ ! -f $(srcdir)/$$name.out ] ; then cp $$name.res $(srcdir)/$$name.out ; \ + else if [ ! -s $$name.res ] ; then echo "Fatal error, no $$name.res\n" ; \ + else diff $(srcdir)/$$name.out $$name.res ; fi ; fi; \ + grep "MORY ALLO" .memdump | grep -v "MEMORY ALLOCATED : 0" || true;\ + rm -f $$name.res ; \ + done) diff --git a/tests/exslt/strings/tokenize.1.out b/tests/exslt/strings/tokenize.1.out new file mode 100644 index 00000000..a521f4bd --- /dev/null +++ b/tests/exslt/strings/tokenize.1.out @@ -0,0 +1,8 @@ + +; + str:tokenize('2001-06-03T11:40:23', '-T:') + 20010603114023; + + str:tokenize('date math str') + datemathstr; + diff --git a/tests/exslt/strings/tokenize.1.xml b/tests/exslt/strings/tokenize.1.xml new file mode 100644 index 00000000..04e42eae --- /dev/null +++ b/tests/exslt/strings/tokenize.1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/exslt/strings/tokenize.1.xsl b/tests/exslt/strings/tokenize.1.xsl new file mode 100644 index 00000000..81de97fe --- /dev/null +++ b/tests/exslt/strings/tokenize.1.xsl @@ -0,0 +1,17 @@ + + + + +; + str:tokenize('2001-06-03T11:40:23', '-T:') + ; + + str:tokenize('date math str') + ; + + + + \ No newline at end of file