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
)) {