Adding Peter Thatcher to the owners file.
[chromium-blink-merge.git] / third_party / libxslt / libexslt / strings.c
blob1a11976c51b8f947b1a2928edcd5f5cd8b5f6428
1 #define IN_LIBEXSLT
2 #include "libexslt/libexslt.h"
4 #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
5 #include <win32config.h>
6 #else
7 #include "config.h"
8 #endif
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>
22 #include "exslt.h"
24 /**
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.
32 static void
33 exsltStrTokenizeFunction(xmlXPathParserContextPtr ctxt, int nargs)
35 xsltTransformContextPtr tctxt;
36 xmlChar *str, *delimiters, *cur;
37 const xmlChar *token, *delimiter;
38 xmlNodePtr node;
39 xmlDocPtr container;
40 xmlXPathObjectPtr ret = NULL;
41 int clen;
43 if ((nargs < 1) || (nargs > 2)) {
44 xmlXPathSetArityError(ctxt);
45 return;
48 if (nargs == 2) {
49 delimiters = xmlXPathPopString(ctxt);
50 if (xmlXPathCheckError(ctxt))
51 return;
52 } else {
53 delimiters = xmlStrdup((const xmlChar *) "\t\r\n ");
55 if (delimiters == NULL)
56 return;
58 str = xmlXPathPopString(ctxt);
59 if (xmlXPathCheckError(ctxt) || (str == NULL)) {
60 xmlFree(delimiters);
61 return;
64 /* Return a result tree fragment */
65 tctxt = xsltXPathGetTransformContext(ctxt);
66 if (tctxt == NULL) {
67 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
68 "exslt:tokenize : internal error tctxt == NULL\n");
69 goto fail;
72 container = xsltCreateRVT(tctxt);
73 if (container != NULL) {
74 xsltRegisterLocalRVT(tctxt, container);
75 ret = xmlXPathNewNodeSet(NULL);
76 if (ret != NULL) {
77 for (cur = str, token = str; *cur != 0; cur += clen) {
78 clen = xmlUTF8Size(cur);
79 if (*delimiters == 0) { /* empty string case */
80 xmlChar ctmp;
81 ctmp = *(cur+clen);
82 *(cur+clen) = 0;
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 */
88 token = cur + clen;
89 } else for (delimiter = delimiters; *delimiter != 0;
90 delimiter += xmlUTF8Size(delimiter)) {
91 if (!xmlUTF8Charcmp(cur, delimiter)) {
92 if (cur == token) {
93 /* discard empty tokens */
94 token = cur + clen;
95 break;
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 */
103 token = cur + clen;
104 break;
108 if (token != cur) {
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);
122 fail:
123 if (str != NULL)
124 xmlFree(str);
125 if (delimiters != NULL)
126 xmlFree(delimiters);
127 if (ret != NULL)
128 valuePush(ctxt, ret);
129 else
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.
141 static void
142 exsltStrSplitFunction(xmlXPathParserContextPtr ctxt, int nargs) {
143 xsltTransformContextPtr tctxt;
144 xmlChar *str, *delimiter, *cur;
145 const xmlChar *token;
146 xmlNodePtr node;
147 xmlDocPtr container;
148 xmlXPathObjectPtr ret = NULL;
149 int delimiterLength;
151 if ((nargs < 1) || (nargs > 2)) {
152 xmlXPathSetArityError(ctxt);
153 return;
156 if (nargs == 2) {
157 delimiter = xmlXPathPopString(ctxt);
158 if (xmlXPathCheckError(ctxt))
159 return;
160 } else {
161 delimiter = xmlStrdup((const xmlChar *) " ");
163 if (delimiter == NULL)
164 return;
165 delimiterLength = xmlStrlen (delimiter);
167 str = xmlXPathPopString(ctxt);
168 if (xmlXPathCheckError(ctxt) || (str == NULL)) {
169 xmlFree(delimiter);
170 return;
173 /* Return a result tree fragment */
174 tctxt = xsltXPathGetTransformContext(ctxt);
175 if (tctxt == NULL) {
176 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
177 "exslt:tokenize : internal error tctxt == NULL\n");
178 goto fail;
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);
188 if (ret != NULL) {
189 for (cur = str, token = str; *cur != 0; cur++) {
190 if (delimiterLength == 0) {
191 if (cur != token) {
192 xmlChar tmp = *cur;
193 *cur = 0;
194 node = xmlNewDocRawNode(container, NULL,
195 (const xmlChar *) "token", token);
196 xmlAddChild((xmlNodePtr) container, node);
197 xmlXPathNodeSetAddUnique(ret->nodesetval, node);
198 *cur = tmp;
199 token++;
202 else if (!xmlStrncasecmp(cur, delimiter, delimiterLength)) {
203 if (cur == token) {
204 /* discard empty tokens */
205 cur = cur + delimiterLength - 1;
206 token = cur + 1;
207 continue;
209 *cur = 0;
210 node = xmlNewDocRawNode(container, NULL,
211 (const xmlChar *) "token", token);
212 xmlAddChild((xmlNodePtr) container, node);
213 xmlXPathNodeSetAddUnique(ret->nodesetval, node);
214 *cur = *delimiter;
215 cur = cur + delimiterLength - 1;
216 token = cur + 1;
219 if (token != cur) {
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);
233 fail:
234 if (str != NULL)
235 xmlFree(str);
236 if (delimiter != NULL)
237 xmlFree(delimiter);
238 if (ret != NULL)
239 valuePush(ctxt, ret);
240 else
241 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
245 * exsltStrEncodeUriFunction:
246 * @ctxt: an XPath parser context
247 * @nargs: the number of arguments
249 * URI-Escapes a string
251 static void
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);
258 return;
261 if (nargs >= 3) {
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);
267 xmlFree(tmp);
268 return;
270 xmlFree(tmp);
273 escape_all = xmlXPathPopBoolean(ctxt);
275 str = xmlXPathPopString(ctxt);
276 str_len = xmlUTF8Strlen(str);
278 if (str_len == 0) {
279 xmlXPathReturnEmptyString(ctxt);
280 xmlFree(str);
281 return;
284 ret = xmlURIEscapeStr(str,(const xmlChar *)(escape_all?"-_.!~*'()":"-_.!~*'();/?:@&=+$,[]"));
285 xmlXPathReturnString(ctxt, ret);
287 if (str != NULL)
288 xmlFree(str);
292 * exsltStrDecodeUriFunction:
293 * @ctxt: an XPath parser context
294 * @nargs: the number of arguments
296 * reverses URI-Escaping of a string
298 static void
299 exsltStrDecodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) {
300 int str_len = 0;
301 xmlChar *str = NULL, *ret = NULL, *tmp;
303 if ((nargs < 1) || (nargs > 2)) {
304 xmlXPathSetArityError(ctxt);
305 return;
308 if (nargs >= 2) {
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);
314 xmlFree(tmp);
315 return;
317 xmlFree(tmp);
320 str = xmlXPathPopString(ctxt);
321 str_len = xmlUTF8Strlen(str);
323 if (str_len == 0) {
324 xmlXPathReturnEmptyString(ctxt);
325 xmlFree(str);
326 return;
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);
334 xmlFree(str);
335 xmlFree(ret);
336 return;
339 xmlXPathReturnString(ctxt, ret);
341 if (str != NULL)
342 xmlFree(str);
346 * exsltStrPaddingFunction:
347 * @ctxt: an XPath parser context
348 * @nargs: the number of arguments
350 * Creates a padding string of a certain length.
352 static void
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);
359 return;
362 if (nargs == 2) {
363 str = xmlXPathPopString(ctxt);
364 str_len = xmlUTF8Strlen(str);
366 if (str_len == 0) {
367 if (str != NULL) xmlFree(str);
368 str = xmlStrdup((const xmlChar *) " ");
369 str_len = 1;
372 number = (int) xmlXPathPopNumber(ctxt);
374 if (number <= 0) {
375 xmlXPathReturnEmptyString(ctxt);
376 xmlFree(str);
377 return;
380 while (number >= str_len) {
381 ret = xmlStrncat(ret, str, str_len);
382 number -= str_len;
384 tmp = xmlUTF8Strndup (str, number);
385 ret = xmlStrcat(ret, tmp);
386 if (tmp != NULL)
387 xmlFree (tmp);
389 xmlXPathReturnString(ctxt, ret);
391 if (str != NULL)
392 xmlFree(str);
396 * exsltStrAlignFunction:
397 * @ctxt: an XPath parser context
398 * @nargs: the number of arguments
400 * Aligns a string within another string.
402 static void
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);
409 return;
412 if (nargs == 3)
413 alignment = xmlXPathPopString(ctxt);
414 else
415 alignment = NULL;
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);
425 xmlFree(padding);
426 xmlFree(alignment);
427 return;
430 if (str_l > padding_l) {
431 ret = xmlUTF8Strndup (str, padding_l);
432 } else {
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;
438 int right_start;
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);
445 } else {
446 int str_s;
448 str_s = xmlStrlen (str);
449 ret = xmlStrdup (str);
450 ret = xmlStrcat (ret, padding + str_s);
454 xmlXPathReturnString (ctxt, ret);
456 xmlFree(str);
457 xmlFree(padding);
458 xmlFree(alignment);
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.
470 static void
471 exsltStrConcatFunction (xmlXPathParserContextPtr ctxt, int nargs) {
472 xmlXPathObjectPtr obj;
473 xmlChar *ret = NULL;
474 int i;
476 if (nargs != 1) {
477 xmlXPathSetArityError(ctxt);
478 return;
481 if (!xmlXPathStackIsNodeSet(ctxt)) {
482 xmlXPathSetTypeError(ctxt);
483 return;
486 obj = valuePop (ctxt);
488 if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
489 xmlXPathReturnEmptyString(ctxt);
490 return;
493 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
494 xmlChar *tmp;
495 tmp = xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
497 ret = xmlStrcat (ret, tmp);
499 xmlFree(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
515 static xmlChar*
516 exsltStrReplaceInternal(const xmlChar* str, const xmlChar* searchStr,
517 const xmlChar* replaceStr)
519 const xmlChar *curr, *next;
520 xmlChar *ret = NULL;
521 int searchStrSize;
523 curr = str;
524 searchStrSize = xmlStrlen(searchStr);
526 do {
527 next = xmlStrstr(curr, searchStr);
528 if (next == NULL) {
529 ret = xmlStrcat (ret, curr);
530 break;
533 ret = xmlStrncat (ret, curr, next - curr);
534 ret = xmlStrcat (ret, replaceStr);
535 curr = next + searchStrSize;
536 } while (*curr != 0);
538 return ret;
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.
548 static void
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;
553 int i;
555 if (nargs != 3) {
556 xmlXPathSetArityError(ctxt);
557 return;
560 /* pull out replace argument */
561 if (!xmlXPathStackIsNodeSet(ctxt)) {
562 replaceStr = xmlXPathPopString(ctxt);
564 else {
565 replaceSet = xmlXPathPopNodeSet(ctxt);
566 if (xmlXPathCheckError(ctxt)) {
567 xmlXPathSetTypeError(ctxt);
568 goto fail;
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);
579 goto fail;
582 ret = exsltStrReplaceInternal(str, searchStr, replaceStr);
584 else {
585 searchSet = xmlXPathPopNodeSet(ctxt);
586 if (searchSet == NULL || xmlXPathCheckError(ctxt)) {
587 xmlXPathSetTypeError(ctxt);
588 goto fail;
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) {
598 replaceStr = NULL;
599 if (i < replaceSet->nodeNr) {
600 replaceStr = xmlXPathCastNodeToString(replaceSet->nodeTab[i]);
603 retSwap = exsltStrReplaceInternal(ret, searchStr, replaceStr);
605 if (replaceStr != NULL) {
606 xmlFree(replaceStr);
607 replaceStr = NULL;
610 else {
611 retSwap = exsltStrReplaceInternal(ret, searchStr, replaceStr);
614 xmlFree(ret);
615 if (searchStr != NULL) {
616 xmlFree(searchStr);
617 searchStr = NULL;
620 ret = retSwap;
623 if (replaceSet != NULL)
624 xmlXPathFreeNodeSet(replaceSet);
626 if (searchSet != NULL)
627 xmlXPathFreeNodeSet(searchSet);
630 xmlXPathReturnString(ctxt, ret);
632 fail:
633 if (replaceStr != NULL)
634 xmlFree(replaceStr);
636 if (searchStr != NULL)
637 xmlFree(searchStr);
639 if (str != NULL)
640 xmlFree(str);
644 * exsltStrRegister:
646 * Registers the EXSLT - Strings module
649 void
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)
685 if (ctxt
686 && prefix
687 && !xmlXPathRegisterNs(ctxt,
688 prefix,
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)) {
714 return 0;
716 return -1;