2 #include "libexslt/libexslt.h"
4 #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
5 #include <win32config.h>
10 #include <libxml/tree.h>
11 #include <libxml/xpath.h>
12 #include <libxml/xpathInternals.h>
13 #include <libxml/parser.h>
14 #include <libxml/encoding.h>
15 #include <libxml/uri.h>
17 #include <libxslt/xsltconfig.h>
18 #include <libxslt/xsltutils.h>
19 #include <libxslt/xsltInternals.h>
20 #include <libxslt/extensions.h>
25 * exsltStrTokenizeFunction:
26 * @ctxt: an XPath parser context
27 * @nargs: the number of arguments
29 * Splits up a string on the characters of the delimiter string and returns a
30 * node set of token elements, each containing one token from the string.
33 exsltStrTokenizeFunction(xmlXPathParserContextPtr ctxt, int nargs)
35 xsltTransformContextPtr tctxt;
36 xmlChar *str, *delimiters, *cur;
37 const xmlChar *token, *delimiter;
40 xmlXPathObjectPtr ret = NULL;
43 if ((nargs < 1) || (nargs > 2)) {
44 xmlXPathSetArityError(ctxt);
49 delimiters = xmlXPathPopString(ctxt);
50 if (xmlXPathCheckError(ctxt))
53 delimiters = xmlStrdup((const xmlChar *) "\t\r\n ");
55 if (delimiters == NULL)
58 str = xmlXPathPopString(ctxt);
59 if (xmlXPathCheckError(ctxt) || (str == NULL)) {
64 /* Return a result tree fragment */
65 tctxt = xsltXPathGetTransformContext(ctxt);
67 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
68 "exslt:tokenize : internal error tctxt == NULL\n");
72 container = xsltCreateRVT(tctxt);
73 if (container != NULL) {
74 xsltRegisterLocalRVT(tctxt, container);
75 ret = xmlXPathNewNodeSet(NULL);
77 for (cur = str, token = str; *cur != 0; cur += clen) {
78 clen = xmlUTF8Size(cur);
79 if (*delimiters == 0) { /* empty string case */
83 node = xmlNewDocRawNode(container, NULL,
84 (const xmlChar *) "token", cur);
85 xmlAddChild((xmlNodePtr) container, node);
86 xmlXPathNodeSetAddUnique(ret->nodesetval, node);
87 *(cur+clen) = ctmp; /* restore the changed byte */
89 } else for (delimiter = delimiters; *delimiter != 0;
90 delimiter += xmlUTF8Size(delimiter)) {
91 if (!xmlUTF8Charcmp(cur, delimiter)) {
93 /* discard empty tokens */
97 *cur = 0; /* terminate the token */
98 node = xmlNewDocRawNode(container, NULL,
99 (const xmlChar *) "token", token);
100 xmlAddChild((xmlNodePtr) container, node);
101 xmlXPathNodeSetAddUnique(ret->nodesetval, node);
102 *cur = *delimiter; /* restore the changed byte */
109 node = xmlNewDocRawNode(container, NULL,
110 (const xmlChar *) "token", token);
111 xmlAddChild((xmlNodePtr) container, node);
112 xmlXPathNodeSetAddUnique(ret->nodesetval, node);
115 * Mark it as a function result in order to avoid garbage
116 * collecting of tree fragments
118 xsltExtensionInstructionResultRegister(tctxt, ret);
125 if (delimiters != NULL)
128 valuePush(ctxt, ret);
130 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
134 * exsltStrSplitFunction:
135 * @ctxt: an XPath parser context
136 * @nargs: the number of arguments
138 * Splits up a string on a delimiting string and returns a node set of token
139 * elements, each containing one token from the string.
142 exsltStrSplitFunction(xmlXPathParserContextPtr ctxt, int nargs) {
143 xsltTransformContextPtr tctxt;
144 xmlChar *str, *delimiter, *cur;
145 const xmlChar *token;
148 xmlXPathObjectPtr ret = NULL;
151 if ((nargs < 1) || (nargs > 2)) {
152 xmlXPathSetArityError(ctxt);
157 delimiter = xmlXPathPopString(ctxt);
158 if (xmlXPathCheckError(ctxt))
161 delimiter = xmlStrdup((const xmlChar *) " ");
163 if (delimiter == NULL)
165 delimiterLength = xmlStrlen (delimiter);
167 str = xmlXPathPopString(ctxt);
168 if (xmlXPathCheckError(ctxt) || (str == NULL)) {
173 /* Return a result tree fragment */
174 tctxt = xsltXPathGetTransformContext(ctxt);
176 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
177 "exslt:tokenize : internal error tctxt == NULL\n");
182 * OPTIMIZE TODO: We are creating an xmlDoc for every split!
184 container = xsltCreateRVT(tctxt);
185 if (container != NULL) {
186 xsltRegisterLocalRVT(tctxt, container);
187 ret = xmlXPathNewNodeSet(NULL);
189 for (cur = str, token = str; *cur != 0; cur++) {
190 if (delimiterLength == 0) {
194 node = xmlNewDocRawNode(container, NULL,
195 (const xmlChar *) "token", token);
196 xmlAddChild((xmlNodePtr) container, node);
197 xmlXPathNodeSetAddUnique(ret->nodesetval, node);
202 else if (!xmlStrncasecmp(cur, delimiter, delimiterLength)) {
204 /* discard empty tokens */
205 cur = cur + delimiterLength - 1;
210 node = xmlNewDocRawNode(container, NULL,
211 (const xmlChar *) "token", token);
212 xmlAddChild((xmlNodePtr) container, node);
213 xmlXPathNodeSetAddUnique(ret->nodesetval, node);
215 cur = cur + delimiterLength - 1;
220 node = xmlNewDocRawNode(container, NULL,
221 (const xmlChar *) "token", token);
222 xmlAddChild((xmlNodePtr) container, node);
223 xmlXPathNodeSetAddUnique(ret->nodesetval, node);
226 * Mark it as a function result in order to avoid garbage
227 * collecting of tree fragments
229 xsltExtensionInstructionResultRegister(tctxt, ret);
236 if (delimiter != NULL)
239 valuePush(ctxt, ret);
241 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
245 * exsltStrEncodeUriFunction:
246 * @ctxt: an XPath parser context
247 * @nargs: the number of arguments
249 * URI-Escapes a string
252 exsltStrEncodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) {
253 int escape_all = 1, str_len = 0;
254 xmlChar *str = NULL, *ret = NULL, *tmp;
256 if ((nargs < 2) || (nargs > 3)) {
257 xmlXPathSetArityError(ctxt);
262 /* check for UTF-8 if encoding was explicitly given;
263 we don't support anything else yet */
264 tmp = xmlXPathPopString(ctxt);
265 if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) {
266 xmlXPathReturnEmptyString(ctxt);
273 escape_all = xmlXPathPopBoolean(ctxt);
275 str = xmlXPathPopString(ctxt);
276 str_len = xmlUTF8Strlen(str);
279 xmlXPathReturnEmptyString(ctxt);
284 ret = xmlURIEscapeStr(str,(const xmlChar *)(escape_all?"-_.!~*'()":"-_.!~*'();/?:@&=+$,[]"));
285 xmlXPathReturnString(ctxt, ret);
292 * exsltStrDecodeUriFunction:
293 * @ctxt: an XPath parser context
294 * @nargs: the number of arguments
296 * reverses URI-Escaping of a string
299 exsltStrDecodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) {
301 xmlChar *str = NULL, *ret = NULL, *tmp;
303 if ((nargs < 1) || (nargs > 2)) {
304 xmlXPathSetArityError(ctxt);
309 /* check for UTF-8 if encoding was explicitly given;
310 we don't support anything else yet */
311 tmp = xmlXPathPopString(ctxt);
312 if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) {
313 xmlXPathReturnEmptyString(ctxt);
320 str = xmlXPathPopString(ctxt);
321 str_len = xmlUTF8Strlen(str);
324 xmlXPathReturnEmptyString(ctxt);
329 ret = (xmlChar *) xmlURIUnescapeString((const char *)str,0,NULL);
330 if (!xmlCheckUTF8(ret)) {
331 /* FIXME: instead of throwing away the whole URI, we should
332 only discard the invalid sequence(s). How to do that? */
333 xmlXPathReturnEmptyString(ctxt);
339 xmlXPathReturnString(ctxt, ret);
346 * exsltStrPaddingFunction:
347 * @ctxt: an XPath parser context
348 * @nargs: the number of arguments
350 * Creates a padding string of a certain length.
353 exsltStrPaddingFunction (xmlXPathParserContextPtr ctxt, int nargs) {
354 int number, str_len = 0;
355 xmlChar *str = NULL, *ret = NULL, *tmp;
357 if ((nargs < 1) || (nargs > 2)) {
358 xmlXPathSetArityError(ctxt);
363 str = xmlXPathPopString(ctxt);
364 str_len = xmlUTF8Strlen(str);
367 if (str != NULL) xmlFree(str);
368 str = xmlStrdup((const xmlChar *) " ");
372 number = (int) xmlXPathPopNumber(ctxt);
375 xmlXPathReturnEmptyString(ctxt);
380 while (number >= str_len) {
381 ret = xmlStrncat(ret, str, str_len);
384 tmp = xmlUTF8Strndup (str, number);
385 ret = xmlStrcat(ret, tmp);
389 xmlXPathReturnString(ctxt, ret);
396 * exsltStrAlignFunction:
397 * @ctxt: an XPath parser context
398 * @nargs: the number of arguments
400 * Aligns a string within another string.
403 exsltStrAlignFunction (xmlXPathParserContextPtr ctxt, int nargs) {
404 xmlChar *str, *padding, *alignment, *ret;
405 int str_l, padding_l;
407 if ((nargs < 2) || (nargs > 3)) {
408 xmlXPathSetArityError(ctxt);
413 alignment = xmlXPathPopString(ctxt);
417 padding = xmlXPathPopString(ctxt);
418 str = xmlXPathPopString(ctxt);
420 str_l = xmlUTF8Strlen (str);
421 padding_l = xmlUTF8Strlen (padding);
423 if (str_l == padding_l) {
424 xmlXPathReturnString (ctxt, str);
430 if (str_l > padding_l) {
431 ret = xmlUTF8Strndup (str, padding_l);
433 if (xmlStrEqual(alignment, (const xmlChar *) "right")) {
434 ret = xmlUTF8Strndup (padding, padding_l - str_l);
435 ret = xmlStrcat (ret, str);
436 } else if (xmlStrEqual(alignment, (const xmlChar *) "center")) {
437 int left = (padding_l - str_l) / 2;
440 ret = xmlUTF8Strndup (padding, left);
441 ret = xmlStrcat (ret, str);
443 right_start = xmlUTF8Strsize (padding, left + str_l);
444 ret = xmlStrcat (ret, padding + right_start);
448 str_s = xmlStrlen (str);
449 ret = xmlStrdup (str);
450 ret = xmlStrcat (ret, padding + str_s);
454 xmlXPathReturnString (ctxt, ret);
462 * exsltStrConcatFunction:
463 * @ctxt: an XPath parser context
464 * @nargs: the number of arguments
466 * Takes a node set and returns the concatenation of the string values
467 * of the nodes in that node set. If the node set is empty, it
468 * returns an empty string.
471 exsltStrConcatFunction (xmlXPathParserContextPtr ctxt, int nargs) {
472 xmlXPathObjectPtr obj;
477 xmlXPathSetArityError(ctxt);
481 if (!xmlXPathStackIsNodeSet(ctxt)) {
482 xmlXPathSetTypeError(ctxt);
486 obj = valuePop (ctxt);
488 if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
489 xmlXPathReturnEmptyString(ctxt);
493 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
495 tmp = xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
497 ret = xmlStrcat (ret, tmp);
502 xmlXPathFreeObject (obj);
504 xmlXPathReturnString(ctxt, ret);
508 * exsltStrReplaceInternal:
509 * @str: string to modify
510 * @searchStr: string to find
511 * @replaceStr: string to replace occurrences of searchStr
513 * Search and replace string function used by exsltStrReplaceFunction
516 exsltStrReplaceInternal(const xmlChar* str, const xmlChar* searchStr,
517 const xmlChar* replaceStr)
519 const xmlChar *curr, *next;
524 searchStrSize = xmlStrlen(searchStr);
527 next = xmlStrstr(curr, searchStr);
529 ret = xmlStrcat (ret, curr);
533 ret = xmlStrncat (ret, curr, next - curr);
534 ret = xmlStrcat (ret, replaceStr);
535 curr = next + searchStrSize;
536 } while (*curr != 0);
541 * exsltStrReplaceFunction:
542 * @ctxt: an XPath parser context
543 * @nargs: the number of arguments
545 * Takes a string, and two node sets and returns the string with all strings in
546 * the first node set replaced by all strings in the second node set.
549 exsltStrReplaceFunction (xmlXPathParserContextPtr ctxt, int nargs) {
550 xmlChar *str = NULL, *searchStr = NULL, *replaceStr = NULL;
551 xmlNodeSetPtr replaceSet = NULL, searchSet = NULL;
552 xmlChar *ret = NULL, *retSwap = NULL;
556 xmlXPathSetArityError(ctxt);
560 /* pull out replace argument */
561 if (!xmlXPathStackIsNodeSet(ctxt)) {
562 replaceStr = xmlXPathPopString(ctxt);
565 replaceSet = xmlXPathPopNodeSet(ctxt);
566 if (xmlXPathCheckError(ctxt)) {
567 xmlXPathSetTypeError(ctxt);
572 /* behavior driven by search argument from here on */
573 if (!xmlXPathStackIsNodeSet(ctxt)) {
574 searchStr = xmlXPathPopString(ctxt);
575 str = xmlXPathPopString(ctxt);
577 if (replaceStr == NULL) {
578 xmlXPathSetTypeError(ctxt);
582 ret = exsltStrReplaceInternal(str, searchStr, replaceStr);
585 searchSet = xmlXPathPopNodeSet(ctxt);
586 if (searchSet == NULL || xmlXPathCheckError(ctxt)) {
587 xmlXPathSetTypeError(ctxt);
591 str = xmlXPathPopString(ctxt);
592 ret = xmlStrdup(str);
594 for (i = 0; i < searchSet->nodeNr; i++) {
595 searchStr = xmlXPathCastNodeToString(searchSet->nodeTab[i]);
597 if (replaceSet != NULL) {
599 if (i < replaceSet->nodeNr) {
600 replaceStr = xmlXPathCastNodeToString(replaceSet->nodeTab[i]);
603 retSwap = exsltStrReplaceInternal(ret, searchStr, replaceStr);
605 if (replaceStr != NULL) {
611 retSwap = exsltStrReplaceInternal(ret, searchStr, replaceStr);
615 if (searchStr != NULL) {
623 if (replaceSet != NULL)
624 xmlXPathFreeNodeSet(replaceSet);
626 if (searchSet != NULL)
627 xmlXPathFreeNodeSet(searchSet);
630 xmlXPathReturnString(ctxt, ret);
633 if (replaceStr != NULL)
636 if (searchStr != NULL)
646 * Registers the EXSLT - Strings module
650 exsltStrRegister (void) {
651 xsltRegisterExtModuleFunction ((const xmlChar *) "tokenize",
652 EXSLT_STRINGS_NAMESPACE,
653 exsltStrTokenizeFunction);
654 xsltRegisterExtModuleFunction ((const xmlChar *) "split",
655 EXSLT_STRINGS_NAMESPACE,
656 exsltStrSplitFunction);
657 xsltRegisterExtModuleFunction ((const xmlChar *) "encode-uri",
658 EXSLT_STRINGS_NAMESPACE,
659 exsltStrEncodeUriFunction);
660 xsltRegisterExtModuleFunction ((const xmlChar *) "decode-uri",
661 EXSLT_STRINGS_NAMESPACE,
662 exsltStrDecodeUriFunction);
663 xsltRegisterExtModuleFunction ((const xmlChar *) "padding",
664 EXSLT_STRINGS_NAMESPACE,
665 exsltStrPaddingFunction);
666 xsltRegisterExtModuleFunction ((const xmlChar *) "align",
667 EXSLT_STRINGS_NAMESPACE,
668 exsltStrAlignFunction);
669 xsltRegisterExtModuleFunction ((const xmlChar *) "concat",
670 EXSLT_STRINGS_NAMESPACE,
671 exsltStrConcatFunction);
672 xsltRegisterExtModuleFunction ((const xmlChar *) "replace",
673 EXSLT_STRINGS_NAMESPACE,
674 exsltStrReplaceFunction);
678 * exsltStrXpathCtxtRegister:
680 * Registers the EXSLT - Strings module for use outside XSLT
683 exsltStrXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix)
687 && !xmlXPathRegisterNs(ctxt,
689 (const xmlChar *) EXSLT_STRINGS_NAMESPACE)
690 && !xmlXPathRegisterFuncNS(ctxt,
691 (const xmlChar *) "encode-uri",
692 (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
693 exsltStrEncodeUriFunction)
694 && !xmlXPathRegisterFuncNS(ctxt,
695 (const xmlChar *) "decode-uri",
696 (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
697 exsltStrDecodeUriFunction)
698 && !xmlXPathRegisterFuncNS(ctxt,
699 (const xmlChar *) "padding",
700 (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
701 exsltStrPaddingFunction)
702 && !xmlXPathRegisterFuncNS(ctxt,
703 (const xmlChar *) "align",
704 (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
705 exsltStrAlignFunction)
706 && !xmlXPathRegisterFuncNS(ctxt,
707 (const xmlChar *) "concat",
708 (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
709 exsltStrConcatFunction)
710 && !xmlXPathRegisterFuncNS(ctxt,
711 (const xmlChar *) "replace",
712 (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
713 exsltStrReplaceFunction)) {