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  * exsltStrReturnString:
 509  * @ctxt: an XPath parser context
 510  * @str: a string
 511  * @len: length of string
 512  *
 513  * Returns a string as a node set.
 514  */
 515 static int
 516 exsltStrReturnString(xmlXPathParserContextPtr ctxt, const xmlChar *str,
 517                      int len)
 518 {
 519     xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
 520     xmlDocPtr container;
 521     xmlNodePtr text_node;
 522     xmlXPathObjectPtr ret;
 523 
 524     container = xsltCreateRVT(tctxt);
 525     if (container == NULL) {
 526         xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
 527         return(-1);
 528     }
 529     xsltRegisterLocalRVT(tctxt, container);
 530 
 531     text_node = xmlNewTextLen(str, len);
 532     if (text_node == NULL) {
 533         xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
 534         return(-1);
 535     }
 536     xmlAddChild((xmlNodePtr) container, text_node);
 537 
 538     ret = xmlXPathNewNodeSet(text_node);
 539     if (ret == NULL) {
 540         xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
 541         return(-1);
 542     }
 543 
 544     xsltExtensionInstructionResultRegister(tctxt, ret);
 545     valuePush(ctxt, ret);
 546 
 547     return(0);
 548 }
 549 
 550 /**
 551  * exsltStrReplaceFunction:
 552  * @ctxt: an XPath parser context
 553  * @nargs: the number of arguments
 554  *
 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.
 557  */
 558 static void
 559 exsltStrReplaceFunction (xmlXPathParserContextPtr ctxt, int nargs) {
 560     int i, i_empty, n, slen0, rlen0, *slen, *rlen;
 561     void *mem = NULL;
 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;
 566     xmlBufferPtr buf;
 567 
 568     if (nargs  != 3) {
 569         xmlXPathSetArityError(ctxt);
 570         return;
 571     }
 572 
 573     /* get replace argument */
 574 
 575     if (!xmlXPathStackIsNodeSet(ctxt))
 576         replace_str = xmlXPathPopString(ctxt);
 577     else
 578         replace_set = xmlXPathPopNodeSet(ctxt);
 579 
 580     if (xmlXPathCheckError(ctxt))
 581         goto fail_replace;
 582 
 583     /* get search argument */
 584 
 585     if (!xmlXPathStackIsNodeSet(ctxt)) {
 586         search_str = xmlXPathPopString(ctxt);
 587         n = 1;
 588     }
 589     else {
 590         search_set = xmlXPathPopNodeSet(ctxt);
 591         n = search_set != NULL ? search_set->nodeNr : 0;
 592     }
 593 
 594     if (xmlXPathCheckError(ctxt))
 595         goto fail_search;
 596 
 597     /* get string argument */
 598 
 599     string = xmlXPathPopString(ctxt);
 600     if (xmlXPathCheckError(ctxt))
 601         goto fail_string;
 602 
 603     /* check for empty search node list */
 604 
 605     if (n <= 0) {
 606         exsltStrReturnString(ctxt, string, xmlStrlen(string));
 607         goto done_empty_search;
 608     }
 609 
 610     /* allocate memory for string pointer and length arrays */
 611 
 612     if (n == 1) {
 613         search = &search_str;
 614         replace = &replace_str;
 615         slen = &slen0;
 616         rlen = &rlen0;
 617     }
 618     else {
 619         mem = xmlMalloc(2 * n * (sizeof(const xmlChar *) + sizeof(int)));
 620         if (mem == NULL) {
 621             xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
 622             goto fail_malloc;
 623         }
 624         search = (xmlChar **) mem;
 625         replace = search + n;
 626         slen = (int *) (replace + n);
 627         rlen = slen + n;
 628     }
 629 
 630     /* process arguments */
 631 
 632     i_empty = -1;
 633 
 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) {
 638                 n = i;
 639                 goto fail_process_args;
 640             }
 641         }
 642 
 643         slen[i] = xmlStrlen(search[i]);
 644         if (i_empty < 0 && slen[i] == 0)
 645             i_empty = i;
 646 
 647         if (replace_set != NULL) {
 648             if (i < replace_set->nodeNr) {
 649                 replace[i] = xmlXPathCastNodeToString(replace_set->nodeTab[i]);
 650                 if (replace[i] == NULL) {
 651                     n = i + 1;
 652                     goto fail_process_args;
 653                 }
 654             }
 655             else
 656                 replace[i] = NULL;
 657         }
 658         else {
 659             if (i == 0)
 660                 replace[i] = replace_str;
 661             else
 662                 replace[i] = NULL;
 663         }
 664 
 665         if (replace[i] == NULL)
 666             rlen[i] = 0;
 667         else
 668             rlen[i] = xmlStrlen(replace[i]);
 669     }
 670 
 671     if (i_empty >= 0 && rlen[i_empty] == 0)
 672         i_empty = -1;
 673 
 674     /* replace operation */
 675 
 676     buf = xmlBufferCreate();
 677     if (buf == NULL) {
 678         xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
 679         goto fail_buffer;
 680     }
 681     src = string;
 682     start = string;
 683 
 684     while (*src != 0) {
 685         int max_len = 0, i_match = 0;
 686 
 687         for (i=0; i<n; ++i) {
 688             if (*src == search[i][0] &&
 689                 slen[i] > max_len &&
 690                 xmlStrncmp(src, search[i], slen[i]) == 0)
 691             {
 692                 i_match = i;
 693                 max_len = slen[i];
 694             }
 695         }
 696 
 697         if (max_len == 0) {
 698             if (i_empty >= 0 && start < src) {
 699                 if (xmlBufferAdd(buf, start, src - start) ||
 700                     xmlBufferAdd(buf, replace[i_empty], rlen[i_empty]))
 701                 {
 702                     xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
 703                     goto fail_buffer_add;
 704                 }
 705                 start = src;
 706             }
 707 
 708             src += xmlUTF8Size(src);
 709         }
 710         else {
 711             if ((start < src &&
 712                  xmlBufferAdd(buf, start, src - start)) ||
 713                 (rlen[i_match] &&
 714                  xmlBufferAdd(buf, replace[i_match], rlen[i_match])))
 715             {
 716                 xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
 717                 goto fail_buffer_add;
 718             }
 719 
 720             src += slen[i_match];
 721             start = src;
 722         }
 723     }
 724 
 725     if (start < src && xmlBufferAdd(buf, start, src - start)) {
 726         xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
 727         goto fail_buffer_add;
 728     }
 729 
 730     /* create result node set */
 731 
 732     exsltStrReturnString(ctxt, xmlBufferContent(buf), xmlBufferLength(buf));
 733 
 734     /* clean up */
 735 
 736 fail_buffer_add:
 737     xmlBufferFree(buf);
 738 
 739 fail_buffer:
 740 fail_process_args:
 741     if (search_set != NULL) {
 742         for (i=0; i<n; ++i)
 743             xmlFree(search[i]);
 744     }
 745     if (replace_set != NULL) {
 746         for (i=0; i<n; ++i) {
 747             if (replace[i] != NULL)
 748                 xmlFree(replace[i]);
 749         }
 750     }
 751 
 752     if (mem != NULL)
 753         xmlFree(mem);
 754 
 755 fail_malloc:
 756 done_empty_search:
 757     xmlFree(string);
 758 
 759 fail_string:
 760     if (search_set != NULL)
 761         xmlXPathFreeNodeSet(search_set);
 762     else
 763         xmlFree(search_str);
 764 
 765 fail_search:
 766     if (replace_set != NULL)
 767         xmlXPathFreeNodeSet(replace_set);
 768     else
 769         xmlFree(replace_str);
 770 
 771 fail_replace:
 772     return;
 773 }
 774 
 775 /**
 776  * exsltStrRegister:
 777  *
 778  * Registers the EXSLT - Strings module
 779  */
 780 
 781 void
 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);
 807 }
 808 
 809 /**
 810  * exsltStrXpathCtxtRegister:
 811  *
 812  * Registers the EXSLT - Strings module for use outside XSLT
 813  */
 814 int
 815 exsltStrXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix)
 816 {
 817     if (ctxt
 818         && prefix
 819         && !xmlXPathRegisterNs(ctxt,
 820                                prefix,
 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)) {
 846         return 0;
 847     }
 848     return -1;
 849 }