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 * exsltStrReturnString:
509 * @ctxt: an XPath parser context
511 * @len: length of string
513 * Returns a string as a node set.
516 exsltStrReturnString(xmlXPathParserContextPtr ctxt
, const xmlChar
*str
,
519 xsltTransformContextPtr tctxt
= xsltXPathGetTransformContext(ctxt
);
521 xmlNodePtr text_node
;
522 xmlXPathObjectPtr ret
;
524 container
= xsltCreateRVT(tctxt
);
525 if (container
== NULL
) {
526 xmlXPathSetError(ctxt
, XPATH_MEMORY_ERROR
);
529 xsltRegisterLocalRVT(tctxt
, container
);
531 text_node
= xmlNewTextLen(str
, len
);
532 if (text_node
== NULL
) {
533 xmlXPathSetError(ctxt
, XPATH_MEMORY_ERROR
);
536 xmlAddChild((xmlNodePtr
) container
, text_node
);
538 ret
= xmlXPathNewNodeSet(text_node
);
540 xmlXPathSetError(ctxt
, XPATH_MEMORY_ERROR
);
544 xsltExtensionInstructionResultRegister(tctxt
, ret
);
545 valuePush(ctxt
, ret
);
551 * exsltStrReplaceFunction:
552 * @ctxt: an XPath parser context
553 * @nargs: the number of arguments
555 * Takes a string, and two node sets and returns the string with all strings in
556 * the first node set replaced by all strings in the second node set.
559 exsltStrReplaceFunction (xmlXPathParserContextPtr ctxt
, int nargs
) {
560 int i
, i_empty
, n
, slen0
, rlen0
, *slen
, *rlen
;
562 const xmlChar
*src
, *start
;
563 xmlChar
*string
, *search_str
= NULL
, *replace_str
= NULL
;
564 xmlChar
**search
, **replace
;
565 xmlNodeSetPtr search_set
= NULL
, replace_set
= NULL
;
569 xmlXPathSetArityError(ctxt
);
573 /* get replace argument */
575 if (!xmlXPathStackIsNodeSet(ctxt
))
576 replace_str
= xmlXPathPopString(ctxt
);
578 replace_set
= xmlXPathPopNodeSet(ctxt
);
580 if (xmlXPathCheckError(ctxt
))
583 /* get search argument */
585 if (!xmlXPathStackIsNodeSet(ctxt
)) {
586 search_str
= xmlXPathPopString(ctxt
);
590 search_set
= xmlXPathPopNodeSet(ctxt
);
591 n
= search_set
!= NULL
? search_set
->nodeNr
: 0;
594 if (xmlXPathCheckError(ctxt
))
597 /* get string argument */
599 string
= xmlXPathPopString(ctxt
);
600 if (xmlXPathCheckError(ctxt
))
603 /* check for empty search node list */
606 exsltStrReturnString(ctxt
, string
, xmlStrlen(string
));
607 goto done_empty_search
;
610 /* allocate memory for string pointer and length arrays */
613 search
= &search_str
;
614 replace
= &replace_str
;
619 mem
= xmlMalloc(2 * n
* (sizeof(const xmlChar
*) + sizeof(int)));
621 xmlXPathSetError(ctxt
, XPATH_MEMORY_ERROR
);
624 search
= (xmlChar
**) mem
;
625 replace
= search
+ n
;
626 slen
= (int *) (replace
+ n
);
630 /* process arguments */
634 for (i
=0; i
<n
; ++i
) {
635 if (search_set
!= NULL
) {
636 search
[i
] = xmlXPathCastNodeToString(search_set
->nodeTab
[i
]);
637 if (search
[i
] == NULL
) {
639 goto fail_process_args
;
643 slen
[i
] = xmlStrlen(search
[i
]);
644 if (i_empty
< 0 && slen
[i
] == 0)
647 if (replace_set
!= NULL
) {
648 if (i
< replace_set
->nodeNr
) {
649 replace
[i
] = xmlXPathCastNodeToString(replace_set
->nodeTab
[i
]);
650 if (replace
[i
] == NULL
) {
652 goto fail_process_args
;
660 replace
[i
] = replace_str
;
665 if (replace
[i
] == NULL
)
668 rlen
[i
] = xmlStrlen(replace
[i
]);
671 if (i_empty
>= 0 && rlen
[i_empty
] == 0)
674 /* replace operation */
676 buf
= xmlBufferCreate();
678 xmlXPathSetError(ctxt
, XPATH_MEMORY_ERROR
);
685 int max_len
= 0, i_match
= 0;
687 for (i
=0; i
<n
; ++i
) {
688 if (*src
== search
[i
][0] &&
690 xmlStrncmp(src
, search
[i
], slen
[i
]) == 0)
698 if (i_empty
>= 0 && start
< src
) {
699 if (xmlBufferAdd(buf
, start
, src
- start
) ||
700 xmlBufferAdd(buf
, replace
[i_empty
], rlen
[i_empty
]))
702 xmlXPathSetError(ctxt
, XPATH_MEMORY_ERROR
);
703 goto fail_buffer_add
;
708 src
+= xmlUTF8Size(src
);
712 xmlBufferAdd(buf
, start
, src
- start
)) ||
714 xmlBufferAdd(buf
, replace
[i_match
], rlen
[i_match
])))
716 xmlXPathSetError(ctxt
, XPATH_MEMORY_ERROR
);
717 goto fail_buffer_add
;
720 src
+= slen
[i_match
];
725 if (start
< src
&& xmlBufferAdd(buf
, start
, src
- start
)) {
726 xmlXPathSetError(ctxt
, XPATH_MEMORY_ERROR
);
727 goto fail_buffer_add
;
730 /* create result node set */
732 exsltStrReturnString(ctxt
, xmlBufferContent(buf
), xmlBufferLength(buf
));
741 if (search_set
!= NULL
) {
745 if (replace_set
!= NULL
) {
746 for (i
=0; i
<n
; ++i
) {
747 if (replace
[i
] != NULL
)
760 if (search_set
!= NULL
)
761 xmlXPathFreeNodeSet(search_set
);
766 if (replace_set
!= NULL
)
767 xmlXPathFreeNodeSet(replace_set
);
769 xmlFree(replace_str
);
778 * Registers the EXSLT - Strings module
782 exsltStrRegister (void) {
783 xsltRegisterExtModuleFunction ((const xmlChar
*) "tokenize",
784 EXSLT_STRINGS_NAMESPACE
,
785 exsltStrTokenizeFunction
);
786 xsltRegisterExtModuleFunction ((const xmlChar
*) "split",
787 EXSLT_STRINGS_NAMESPACE
,
788 exsltStrSplitFunction
);
789 xsltRegisterExtModuleFunction ((const xmlChar
*) "encode-uri",
790 EXSLT_STRINGS_NAMESPACE
,
791 exsltStrEncodeUriFunction
);
792 xsltRegisterExtModuleFunction ((const xmlChar
*) "decode-uri",
793 EXSLT_STRINGS_NAMESPACE
,
794 exsltStrDecodeUriFunction
);
795 xsltRegisterExtModuleFunction ((const xmlChar
*) "padding",
796 EXSLT_STRINGS_NAMESPACE
,
797 exsltStrPaddingFunction
);
798 xsltRegisterExtModuleFunction ((const xmlChar
*) "align",
799 EXSLT_STRINGS_NAMESPACE
,
800 exsltStrAlignFunction
);
801 xsltRegisterExtModuleFunction ((const xmlChar
*) "concat",
802 EXSLT_STRINGS_NAMESPACE
,
803 exsltStrConcatFunction
);
804 xsltRegisterExtModuleFunction ((const xmlChar
*) "replace",
805 EXSLT_STRINGS_NAMESPACE
,
806 exsltStrReplaceFunction
);
810 * exsltStrXpathCtxtRegister:
812 * Registers the EXSLT - Strings module for use outside XSLT
815 exsltStrXpathCtxtRegister (xmlXPathContextPtr ctxt
, const xmlChar
*prefix
)
819 && !xmlXPathRegisterNs(ctxt
,
821 (const xmlChar
*) EXSLT_STRINGS_NAMESPACE
)
822 && !xmlXPathRegisterFuncNS(ctxt
,
823 (const xmlChar
*) "encode-uri",
824 (const xmlChar
*) EXSLT_STRINGS_NAMESPACE
,
825 exsltStrEncodeUriFunction
)
826 && !xmlXPathRegisterFuncNS(ctxt
,
827 (const xmlChar
*) "decode-uri",
828 (const xmlChar
*) EXSLT_STRINGS_NAMESPACE
,
829 exsltStrDecodeUriFunction
)
830 && !xmlXPathRegisterFuncNS(ctxt
,
831 (const xmlChar
*) "padding",
832 (const xmlChar
*) EXSLT_STRINGS_NAMESPACE
,
833 exsltStrPaddingFunction
)
834 && !xmlXPathRegisterFuncNS(ctxt
,
835 (const xmlChar
*) "align",
836 (const xmlChar
*) EXSLT_STRINGS_NAMESPACE
,
837 exsltStrAlignFunction
)
838 && !xmlXPathRegisterFuncNS(ctxt
,
839 (const xmlChar
*) "concat",
840 (const xmlChar
*) EXSLT_STRINGS_NAMESPACE
,
841 exsltStrConcatFunction
)
842 && !xmlXPathRegisterFuncNS(ctxt
,
843 (const xmlChar
*) "replace",
844 (const xmlChar
*) EXSLT_STRINGS_NAMESPACE
,
845 exsltStrReplaceFunction
)) {