1 #define IN_LIBEXSLT 2 #include "libexslt/libexslt.h" 3 4 #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) 5 #include <win32config.h> 6 #else 7 #include "config.h" 8 #endif 9 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> 16 17 #include <libxslt/xsltconfig.h> 18 #include <libxslt/xsltutils.h> 19 #include <libxslt/xsltInternals.h> 20 #include <libxslt/extensions.h> 21 22 #include "exslt.h" 23 24 /** 25 * exsltStrTokenizeFunction: 26 * @ctxt: an XPath parser context 27 * @nargs: the number of arguments 28 * 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. 31 */ 32 static void 33 exsltStrTokenizeFunction(xmlXPathParserContextPtr ctxt, int nargs) 34 { 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; 42 43 if ((nargs < 1) || (nargs > 2)) { 44 xmlXPathSetArityError(ctxt); 45 return; 46 } 47 48 if (nargs == 2) { 49 delimiters = xmlXPathPopString(ctxt); 50 if (xmlXPathCheckError(ctxt)) 51 return; 52 } else { 53 delimiters = xmlStrdup((const xmlChar *) "\t\r\n "); 54 } 55 if (delimiters == NULL) 56 return; 57 58 str = xmlXPathPopString(ctxt); 59 if (xmlXPathCheckError(ctxt) || (str == NULL)) { 60 xmlFree(delimiters); 61 return; 62 } 63 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; 70 } 71 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; 96 } 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; 105 } 106 } 107 } 108 if (token != cur) { 109 node = xmlNewDocRawNode(container, NULL, 110 (const xmlChar *) "token", token); 111 xmlAddChild((xmlNodePtr) container, node); 112 xmlXPathNodeSetAddUnique(ret->nodesetval, node); 113 } 114 /* 115 * Mark it as a function result in order to avoid garbage 116 * collecting of tree fragments 117 */ 118 xsltExtensionInstructionResultRegister(tctxt, ret); 119 } 120 } 121 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)); 131 } 132 133 /** 134 * exsltStrSplitFunction: 135 * @ctxt: an XPath parser context 136 * @nargs: the number of arguments 137 * 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. 140 */ 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; 150 151 if ((nargs < 1) || (nargs > 2)) { 152 xmlXPathSetArityError(ctxt); 153 return; 154 } 155 156 if (nargs == 2) { 157 delimiter = xmlXPathPopString(ctxt); 158 if (xmlXPathCheckError(ctxt)) 159 return; 160 } else { 161 delimiter = xmlStrdup((const xmlChar *) " "); 162 } 163 if (delimiter == NULL) 164 return; 165 delimiterLength = xmlStrlen (delimiter); 166 167 str = xmlXPathPopString(ctxt); 168 if (xmlXPathCheckError(ctxt) || (str == NULL)) { 169 xmlFree(delimiter); 170 return; 171 } 172 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; 179 } 180 181 /* 182 * OPTIMIZE TODO: We are creating an xmlDoc for every split! 183 */ 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++; 200 } 201 } 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; 208 } 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; 217 } 218 } 219 if (token != cur) { 220 node = xmlNewDocRawNode(container, NULL, 221 (const xmlChar *) "token", token); 222 xmlAddChild((xmlNodePtr) container, node); 223 xmlXPathNodeSetAddUnique(ret->nodesetval, node); 224 } 225 /* 226 * Mark it as a function result in order to avoid garbage 227 * collecting of tree fragments 228 */ 229 xsltExtensionInstructionResultRegister(tctxt, ret); 230 } 231 } 232 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)); 242 } 243 244 /** 245 * exsltStrEncodeUriFunction: 246 * @ctxt: an XPath parser context 247 * @nargs: the number of arguments 248 * 249 * URI-Escapes a string 250 */ 251 static void 252 exsltStrEncodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) { 253 int escape_all = 1, str_len = 0; 254 xmlChar *str = NULL, *ret = NULL, *tmp; 255 256 if ((nargs < 2) || (nargs > 3)) { 257 xmlXPathSetArityError(ctxt); 258 return; 259 } 260 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; 269 } 270 xmlFree(tmp); 271 } 272 273 escape_all = xmlXPathPopBoolean(ctxt); 274 275 str = xmlXPathPopString(ctxt); 276 str_len = xmlUTF8Strlen(str); 277 278 if (str_len == 0) { 279 xmlXPathReturnEmptyString(ctxt); 280 xmlFree(str); 281 return; 282 } 283 284 ret = xmlURIEscapeStr(str,(const xmlChar *)(escape_all?"-_.!~*'()":"-_.!~*'();/?:@&=+$,[]")); 285 xmlXPathReturnString(ctxt, ret); 286 287 if (str != NULL) 288 xmlFree(str); 289 } 290 291 /** 292 * exsltStrDecodeUriFunction: 293 * @ctxt: an XPath parser context 294 * @nargs: the number of arguments 295 * 296 * reverses URI-Escaping of a string 297 */ 298 static void 299 exsltStrDecodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) { 300 int str_len = 0; 301 xmlChar *str = NULL, *ret = NULL, *tmp; 302 303 if ((nargs < 1) || (nargs > 2)) { 304 xmlXPathSetArityError(ctxt); 305 return; 306 } 307 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; 316 } 317 xmlFree(tmp); 318 } 319 320 str = xmlXPathPopString(ctxt); 321 str_len = xmlUTF8Strlen(str); 322 323 if (str_len == 0) { 324 xmlXPathReturnEmptyString(ctxt); 325 xmlFree(str); 326 return; 327 } 328 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; 337 } 338 339 xmlXPathReturnString(ctxt, ret); 340 341 if (str != NULL) 342 xmlFree(str); 343 } 344 345 /** 346 * exsltStrPaddingFunction: 347 * @ctxt: an XPath parser context 348 * @nargs: the number of arguments 349 * 350 * Creates a padding string of a certain length. 351 */ 352 static void 353 exsltStrPaddingFunction (xmlXPathParserContextPtr ctxt, int nargs) { 354 int number, str_len = 0; 355 xmlChar *str = NULL, *ret = NULL, *tmp; 356 357 if ((nargs < 1) || (nargs > 2)) { 358 xmlXPathSetArityError(ctxt); 359 return; 360 } 361 362 if (nargs == 2) { 363 str = xmlXPathPopString(ctxt); 364 str_len = xmlUTF8Strlen(str); 365 } 366 if (str_len == 0) { 367 if (str != NULL) xmlFree(str); 368 str = xmlStrdup((const xmlChar *) " "); 369 str_len = 1; 370 } 371 372 number = (int) xmlXPathPopNumber(ctxt); 373 374 if (number <= 0) { 375 xmlXPathReturnEmptyString(ctxt); 376 xmlFree(str); 377 return; 378 } 379 380 while (number >= str_len) { 381 ret = xmlStrncat(ret, str, str_len); 382 number -= str_len; 383 } 384 tmp = xmlUTF8Strndup (str, number); 385 ret = xmlStrcat(ret, tmp); 386 if (tmp != NULL) 387 xmlFree (tmp); 388 389 xmlXPathReturnString(ctxt, ret); 390 391 if (str != NULL) 392 xmlFree(str); 393 } 394 395 /** 396 * exsltStrAlignFunction: 397 * @ctxt: an XPath parser context 398 * @nargs: the number of arguments 399 * 400 * Aligns a string within another string. 401 */ 402 static void 403 exsltStrAlignFunction (xmlXPathParserContextPtr ctxt, int nargs) { 404 xmlChar *str, *padding, *alignment, *ret; 405 int str_l, padding_l; 406 407 if ((nargs < 2) || (nargs > 3)) { 408 xmlXPathSetArityError(ctxt); 409 return; 410 } 411 412 if (nargs == 3) 413 alignment = xmlXPathPopString(ctxt); 414 else 415 alignment = NULL; 416 417 padding = xmlXPathPopString(ctxt); 418 str = xmlXPathPopString(ctxt); 419 420 str_l = xmlUTF8Strlen (str); 421 padding_l = xmlUTF8Strlen (padding); 422 423 if (str_l == padding_l) { 424 xmlXPathReturnString (ctxt, str); 425 xmlFree(padding); 426 xmlFree(alignment); 427 return; 428 } 429 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; 439 440 ret = xmlUTF8Strndup (padding, left); 441 ret = xmlStrcat (ret, str); 442 443 right_start = xmlUTF8Strsize (padding, left + str_l); 444 ret = xmlStrcat (ret, padding + right_start); 445 } else { 446 int str_s; 447 448 str_s = xmlStrlen (str); 449 ret = xmlStrdup (str); 450 ret = xmlStrcat (ret, padding + str_s); 451 } 452 } 453 454 xmlXPathReturnString (ctxt, ret); 455 456 xmlFree(str); 457 xmlFree(padding); 458 xmlFree(alignment); 459 } 460 461 /** 462 * exsltStrConcatFunction: 463 * @ctxt: an XPath parser context 464 * @nargs: the number of arguments 465 * 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. 469 */ 470 static void 471 exsltStrConcatFunction (xmlXPathParserContextPtr ctxt, int nargs) { 472 xmlXPathObjectPtr obj; 473 xmlChar *ret = NULL; 474 int i; 475 476 if (nargs != 1) { 477 xmlXPathSetArityError(ctxt); 478 return; 479 } 480 481 if (!xmlXPathStackIsNodeSet(ctxt)) { 482 xmlXPathSetTypeError(ctxt); 483 return; 484 } 485 486 obj = valuePop (ctxt); 487 488 if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) { 489 xmlXPathReturnEmptyString(ctxt); 490 return; 491 } 492 493 for (i = 0; i < obj->nodesetval->nodeNr; i++) { 494 xmlChar *tmp; 495 tmp = xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]); 496 497 ret = xmlStrcat (ret, tmp); 498 499 xmlFree(tmp); 500 } 501 502 xmlXPathFreeObject (obj); 503 504 xmlXPathReturnString(ctxt, ret); 505 } 506 507 /** 508 * exsltStrReplaceInternal: 509 * @str: string to modify 510 * @searchStr: string to find 511 * @replaceStr: string to replace occurrences of searchStr 512 * 513 * Search and replace string function used by exsltStrReplaceFunction 514 */ 515 static xmlChar* 516 exsltStrReplaceInternal(const xmlChar* str, const xmlChar* searchStr, 517 const xmlChar* replaceStr) 518 { 519 const xmlChar *curr, *next; 520 xmlChar *ret = NULL; 521 int searchStrSize; 522 523 curr = str; 524 searchStrSize = xmlStrlen(searchStr); 525 526 do { 527 next = xmlStrstr(curr, searchStr); 528 if (next == NULL) { 529 ret = xmlStrcat (ret, curr); 530 break; 531 } 532 533 ret = xmlStrncat (ret, curr, next - curr); 534 ret = xmlStrcat (ret, replaceStr); 535 curr = next + searchStrSize; 536 } while (*curr != 0); 537 538 return ret; 539 } 540 /** 541 * exsltStrReplaceFunction: 542 * @ctxt: an XPath parser context 543 * @nargs: the number of arguments 544 * 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. 547 */ 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; 554 555 if (nargs != 3) { 556 xmlXPathSetArityError(ctxt); 557 return; 558 } 559 560 /* pull out replace argument */ 561 if (!xmlXPathStackIsNodeSet(ctxt)) { 562 replaceStr = xmlXPathPopString(ctxt); 563 } 564 else { 565 replaceSet = xmlXPathPopNodeSet(ctxt); 566 if (xmlXPathCheckError(ctxt)) { 567 xmlXPathSetTypeError(ctxt); 568 goto fail; 569 } 570 } 571 572 /* behavior driven by search argument from here on */ 573 if (!xmlXPathStackIsNodeSet(ctxt)) { 574 searchStr = xmlXPathPopString(ctxt); 575 str = xmlXPathPopString(ctxt); 576 577 if (replaceStr == NULL) { 578 xmlXPathSetTypeError(ctxt); 579 goto fail; 580 } 581 582 ret = exsltStrReplaceInternal(str, searchStr, replaceStr); 583 } 584 else { 585 searchSet = xmlXPathPopNodeSet(ctxt); 586 if (searchSet == NULL || xmlXPathCheckError(ctxt)) { 587 xmlXPathSetTypeError(ctxt); 588 goto fail; 589 } 590 591 str = xmlXPathPopString(ctxt); 592 ret = xmlStrdup(str); 593 594 for (i = 0; i < searchSet->nodeNr; i++) { 595 searchStr = xmlXPathCastNodeToString(searchSet->nodeTab[i]); 596 597 if (replaceSet != NULL) { 598 replaceStr = NULL; 599 if (i < replaceSet->nodeNr) { 600 replaceStr = xmlXPathCastNodeToString(replaceSet->nodeTab[i]); 601 } 602 603 retSwap = exsltStrReplaceInternal(ret, searchStr, replaceStr); 604 605 if (replaceStr != NULL) { 606 xmlFree(replaceStr); 607 replaceStr = NULL; 608 } 609 } 610 else { 611 retSwap = exsltStrReplaceInternal(ret, searchStr, replaceStr); 612 } 613 614 xmlFree(ret); 615 if (searchStr != NULL) { 616 xmlFree(searchStr); 617 searchStr = NULL; 618 } 619 620 ret = retSwap; 621 } 622 623 if (replaceSet != NULL) 624 xmlXPathFreeNodeSet(replaceSet); 625 626 if (searchSet != NULL) 627 xmlXPathFreeNodeSet(searchSet); 628 } 629 630 xmlXPathReturnString(ctxt, ret); 631 632 fail: 633 if (replaceStr != NULL) 634 xmlFree(replaceStr); 635 636 if (searchStr != NULL) 637 xmlFree(searchStr); 638 639 if (str != NULL) 640 xmlFree(str); 641 } 642 643 /** 644 * exsltStrRegister: 645 * 646 * Registers the EXSLT - Strings module 647 */ 648 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); 675 } 676 677 /** 678 * exsltStrXpathCtxtRegister: 679 * 680 * Registers the EXSLT - Strings module for use outside XSLT 681 */ 682 int 683 exsltStrXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix) 684 { 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; 715 } 716 return -1; 717 } 718