root/third_party/libxslt/libexslt/strings.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. exsltStrTokenizeFunction
  2. exsltStrSplitFunction
  3. exsltStrEncodeUriFunction
  4. exsltStrDecodeUriFunction
  5. exsltStrPaddingFunction
  6. exsltStrAlignFunction
  7. exsltStrConcatFunction
  8. exsltStrReplaceInternal
  9. exsltStrReplaceFunction
  10. exsltStrRegister
  11. exsltStrXpathCtxtRegister

#define IN_LIBEXSLT
#include "libexslt/libexslt.h"

#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
#include <win32config.h>
#else
#include "config.h"
#endif

#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/parser.h>
#include <libxml/encoding.h>
#include <libxml/uri.h>

#include <libxslt/xsltconfig.h>
#include <libxslt/xsltutils.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/extensions.h>

#include "exslt.h"

/**
 * exsltStrTokenizeFunction:
 * @ctxt: an XPath parser context
 * @nargs: the number of arguments
 *
 * Splits up a string on the characters of the delimiter string and returns a
 * node set of token elements, each containing one token from the string. 
 */
static void
exsltStrTokenizeFunction(xmlXPathParserContextPtr ctxt, int nargs)
{
    xsltTransformContextPtr tctxt;
    xmlChar *str, *delimiters, *cur;
    const xmlChar *token, *delimiter;
    xmlNodePtr node;
    xmlDocPtr container;
    xmlXPathObjectPtr ret = NULL;
    int clen;

    if ((nargs < 1) || (nargs > 2)) {
        xmlXPathSetArityError(ctxt);
        return;
    }

    if (nargs == 2) {
        delimiters = xmlXPathPopString(ctxt);
        if (xmlXPathCheckError(ctxt))
            return;
    } else {
        delimiters = xmlStrdup((const xmlChar *) "\t\r\n ");
    }
    if (delimiters == NULL)
        return;

    str = xmlXPathPopString(ctxt);
    if (xmlXPathCheckError(ctxt) || (str == NULL)) {
        xmlFree(delimiters);
        return;
    }

    /* Return a result tree fragment */
    tctxt = xsltXPathGetTransformContext(ctxt);
    if (tctxt == NULL) {
        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
              "exslt:tokenize : internal error tctxt == NULL\n");
        goto fail;
    }

    container = xsltCreateRVT(tctxt);
    if (container != NULL) {
        xsltRegisterLocalRVT(tctxt, container);
        ret = xmlXPathNewNodeSet(NULL);
        if (ret != NULL) {
            for (cur = str, token = str; *cur != 0; cur += clen) {
                clen = xmlUTF8Size(cur);
                if (*delimiters == 0) { /* empty string case */
                    xmlChar ctmp;
                    ctmp = *(cur+clen);
                    *(cur+clen) = 0;
                    node = xmlNewDocRawNode(container, NULL,
                                       (const xmlChar *) "token", cur);
                    xmlAddChild((xmlNodePtr) container, node);
                    xmlXPathNodeSetAddUnique(ret->nodesetval, node);
                    *(cur+clen) = ctmp; /* restore the changed byte */
                    token = cur + clen;
                } else for (delimiter = delimiters; *delimiter != 0;
                                delimiter += xmlUTF8Size(delimiter)) {
                    if (!xmlUTF8Charcmp(cur, delimiter)) {
                        if (cur == token) {
                            /* discard empty tokens */
                            token = cur + clen;
                            break;
                        }
                        *cur = 0;       /* terminate the token */
                        node = xmlNewDocRawNode(container, NULL,
                                           (const xmlChar *) "token", token);
                        xmlAddChild((xmlNodePtr) container, node);
                        xmlXPathNodeSetAddUnique(ret->nodesetval, node);
                        *cur = *delimiter; /* restore the changed byte */
                        token = cur + clen;
                        break;
                    }
                }
            }
            if (token != cur) {
                node = xmlNewDocRawNode(container, NULL,
                                    (const xmlChar *) "token", token);
                xmlAddChild((xmlNodePtr) container, node);
                xmlXPathNodeSetAddUnique(ret->nodesetval, node);
            }
            /*
             * Mark it as a function result in order to avoid garbage
             * collecting of tree fragments
             */
            xsltExtensionInstructionResultRegister(tctxt, ret);
        }
    }

fail:
    if (str != NULL)
        xmlFree(str);
    if (delimiters != NULL)
        xmlFree(delimiters);
    if (ret != NULL)
        valuePush(ctxt, ret);
    else
        valuePush(ctxt, xmlXPathNewNodeSet(NULL));
}

/**
 * exsltStrSplitFunction:
 * @ctxt: an XPath parser context
 * @nargs: the number of arguments
 *
 * Splits up a string on a delimiting string and returns a node set of token
 * elements, each containing one token from the string. 
 */
static void
exsltStrSplitFunction(xmlXPathParserContextPtr ctxt, int nargs) {
    xsltTransformContextPtr tctxt;
    xmlChar *str, *delimiter, *cur;
    const xmlChar *token;
    xmlNodePtr node;
    xmlDocPtr container;
    xmlXPathObjectPtr ret = NULL;
    int delimiterLength;

    if ((nargs < 1) || (nargs > 2)) {
        xmlXPathSetArityError(ctxt);
        return;
    }

    if (nargs == 2) {
        delimiter = xmlXPathPopString(ctxt);
        if (xmlXPathCheckError(ctxt))
            return;
    } else {
        delimiter = xmlStrdup((const xmlChar *) " ");
    }
    if (delimiter == NULL)
        return;
    delimiterLength = xmlStrlen (delimiter);

    str = xmlXPathPopString(ctxt);
    if (xmlXPathCheckError(ctxt) || (str == NULL)) {
        xmlFree(delimiter);
        return;
    }

    /* Return a result tree fragment */
    tctxt = xsltXPathGetTransformContext(ctxt);
    if (tctxt == NULL) {
        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
              "exslt:tokenize : internal error tctxt == NULL\n");
        goto fail;
    }

    /*
    * OPTIMIZE TODO: We are creating an xmlDoc for every split!
    */
    container = xsltCreateRVT(tctxt);
    if (container != NULL) {
        xsltRegisterLocalRVT(tctxt, container);
        ret = xmlXPathNewNodeSet(NULL);
        if (ret != NULL) {
            for (cur = str, token = str; *cur != 0; cur++) {
                if (delimiterLength == 0) {
                    if (cur != token) {
                        xmlChar tmp = *cur;
                        *cur = 0;
                        node = xmlNewDocRawNode(container, NULL,
                                           (const xmlChar *) "token", token);
                        xmlAddChild((xmlNodePtr) container, node);
                        xmlXPathNodeSetAddUnique(ret->nodesetval, node);
                        *cur = tmp;
                        token++;
                    }
                }
                else if (!xmlStrncasecmp(cur, delimiter, delimiterLength)) {
                    if (cur == token) {
                        /* discard empty tokens */
                        cur = cur + delimiterLength - 1;
                        token = cur + 1;
                        continue;
                    }
                    *cur = 0;
                    node = xmlNewDocRawNode(container, NULL,
                                       (const xmlChar *) "token", token);
                    xmlAddChild((xmlNodePtr) container, node);
                    xmlXPathNodeSetAddUnique(ret->nodesetval, node);
                    *cur = *delimiter;
                    cur = cur + delimiterLength - 1;
                    token = cur + 1;
                }
            }
            if (token != cur) {
                node = xmlNewDocRawNode(container, NULL,
                                   (const xmlChar *) "token", token);
                xmlAddChild((xmlNodePtr) container, node);
                xmlXPathNodeSetAddUnique(ret->nodesetval, node);
            }
            /*
             * Mark it as a function result in order to avoid garbage
             * collecting of tree fragments
             */
            xsltExtensionInstructionResultRegister(tctxt, ret);
        }
    }

fail:
    if (str != NULL)
        xmlFree(str);
    if (delimiter != NULL)
        xmlFree(delimiter);
    if (ret != NULL)
        valuePush(ctxt, ret);
    else
        valuePush(ctxt, xmlXPathNewNodeSet(NULL));
}

/**
 * exsltStrEncodeUriFunction:
 * @ctxt: an XPath parser context
 * @nargs: the number of arguments
 *
 * URI-Escapes a string
 */
static void
exsltStrEncodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) {
    int escape_all = 1, str_len = 0;
    xmlChar *str = NULL, *ret = NULL, *tmp;

    if ((nargs < 2) || (nargs > 3)) {
        xmlXPathSetArityError(ctxt);
        return;
    }

    if (nargs >= 3) {
        /* check for UTF-8 if encoding was explicitly given;
           we don't support anything else yet */
        tmp = xmlXPathPopString(ctxt);
        if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) {
            xmlXPathReturnEmptyString(ctxt);
            xmlFree(tmp);
            return;
        }
        xmlFree(tmp);
    }

    escape_all = xmlXPathPopBoolean(ctxt);

    str = xmlXPathPopString(ctxt);
    str_len = xmlUTF8Strlen(str);

    if (str_len == 0) {
        xmlXPathReturnEmptyString(ctxt);
        xmlFree(str);
        return;
    }

    ret = xmlURIEscapeStr(str,(const xmlChar *)(escape_all?"-_.!~*'()":"-_.!~*'();/?:@&=+$,[]"));
    xmlXPathReturnString(ctxt, ret);

    if (str != NULL)
        xmlFree(str);
}

/**
 * exsltStrDecodeUriFunction:
 * @ctxt: an XPath parser context
 * @nargs: the number of arguments
 *
 * reverses URI-Escaping of a string
 */
static void
exsltStrDecodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) {
    int str_len = 0;
    xmlChar *str = NULL, *ret = NULL, *tmp;

    if ((nargs < 1) || (nargs > 2)) {
        xmlXPathSetArityError(ctxt);
        return;
    }

    if (nargs >= 2) {
        /* check for UTF-8 if encoding was explicitly given;
           we don't support anything else yet */
        tmp = xmlXPathPopString(ctxt);
        if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) {
            xmlXPathReturnEmptyString(ctxt);
            xmlFree(tmp);
            return;
        }
        xmlFree(tmp);
    }

    str = xmlXPathPopString(ctxt);
    str_len = xmlUTF8Strlen(str);

    if (str_len == 0) {
        xmlXPathReturnEmptyString(ctxt);
        xmlFree(str);
        return;
    }

    ret = (xmlChar *) xmlURIUnescapeString((const char *)str,0,NULL);
    if (!xmlCheckUTF8(ret)) {
        /* FIXME: instead of throwing away the whole URI, we should
        only discard the invalid sequence(s). How to do that? */
        xmlXPathReturnEmptyString(ctxt);
        xmlFree(str);
        xmlFree(ret);
        return;
    }
    
    xmlXPathReturnString(ctxt, ret);

    if (str != NULL)
        xmlFree(str);
}

/**
 * exsltStrPaddingFunction:
 * @ctxt: an XPath parser context
 * @nargs: the number of arguments
 *
 * Creates a padding string of a certain length.
 */
static void
exsltStrPaddingFunction (xmlXPathParserContextPtr ctxt, int nargs) {
    int number, str_len = 0;
    xmlChar *str = NULL, *ret = NULL, *tmp;

    if ((nargs < 1) || (nargs > 2)) {
        xmlXPathSetArityError(ctxt);
        return;
    }

    if (nargs == 2) {
        str = xmlXPathPopString(ctxt);
        str_len = xmlUTF8Strlen(str);
    }
    if (str_len == 0) {
        if (str != NULL) xmlFree(str);
        str = xmlStrdup((const xmlChar *) " ");
        str_len = 1;
    }

    number = (int) xmlXPathPopNumber(ctxt);

    if (number <= 0) {
        xmlXPathReturnEmptyString(ctxt);
        xmlFree(str);
        return;
    }

    while (number >= str_len) {
        ret = xmlStrncat(ret, str, str_len);
        number -= str_len;
    }
    tmp = xmlUTF8Strndup (str, number);
    ret = xmlStrcat(ret, tmp);
    if (tmp != NULL)
        xmlFree (tmp);

    xmlXPathReturnString(ctxt, ret);

    if (str != NULL)
        xmlFree(str);
}

/**
 * exsltStrAlignFunction:
 * @ctxt: an XPath parser context
 * @nargs: the number of arguments
 *
 * Aligns a string within another string.
 */
static void
exsltStrAlignFunction (xmlXPathParserContextPtr ctxt, int nargs) {
    xmlChar *str, *padding, *alignment, *ret;
    int str_l, padding_l;

    if ((nargs < 2) || (nargs > 3)) {
        xmlXPathSetArityError(ctxt);
        return;
    }

    if (nargs == 3)
        alignment = xmlXPathPopString(ctxt);
    else
        alignment = NULL;

    padding = xmlXPathPopString(ctxt);
    str = xmlXPathPopString(ctxt);

    str_l = xmlUTF8Strlen (str);
    padding_l = xmlUTF8Strlen (padding);

    if (str_l == padding_l) {
        xmlXPathReturnString (ctxt, str);
        xmlFree(padding);
        xmlFree(alignment);
        return;
    }

    if (str_l > padding_l) {
        ret = xmlUTF8Strndup (str, padding_l);
    } else {
        if (xmlStrEqual(alignment, (const xmlChar *) "right")) {
            ret = xmlUTF8Strndup (padding, padding_l - str_l);
            ret = xmlStrcat (ret, str);
        } else if (xmlStrEqual(alignment, (const xmlChar *) "center")) {
            int left = (padding_l - str_l) / 2;
            int right_start;

            ret = xmlUTF8Strndup (padding, left);
            ret = xmlStrcat (ret, str);

            right_start = xmlUTF8Strsize (padding, left + str_l);
            ret = xmlStrcat (ret, padding + right_start);
        } else {
            int str_s;

            str_s = xmlStrlen (str);
            ret = xmlStrdup (str);
            ret = xmlStrcat (ret, padding + str_s);
        }
    }

    xmlXPathReturnString (ctxt, ret);

    xmlFree(str);
    xmlFree(padding);
    xmlFree(alignment);
}

/**
 * exsltStrConcatFunction:
 * @ctxt: an XPath parser context
 * @nargs: the number of arguments
 *
 * Takes a node set and returns the concatenation of the string values
 * of the nodes in that node set.  If the node set is empty, it
 * returns an empty string.
 */
static void
exsltStrConcatFunction (xmlXPathParserContextPtr ctxt, int nargs) {
    xmlXPathObjectPtr obj;
    xmlChar *ret = NULL;
    int i;

    if (nargs  != 1) {
        xmlXPathSetArityError(ctxt);
        return;
    }

    if (!xmlXPathStackIsNodeSet(ctxt)) {
        xmlXPathSetTypeError(ctxt);
        return;
    }

    obj = valuePop (ctxt);

    if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
        xmlXPathReturnEmptyString(ctxt);
        return;
    }

    for (i = 0; i < obj->nodesetval->nodeNr; i++) {
        xmlChar *tmp;
        tmp = xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);

        ret = xmlStrcat (ret, tmp);

        xmlFree(tmp);
    }

    xmlXPathFreeObject (obj);

    xmlXPathReturnString(ctxt, ret);
}

/**
 * exsltStrReplaceInternal:
 * @str: string to modify
 * @searchStr: string to find
 * @replaceStr: string to replace occurrences of searchStr
 *
 * Search and replace string function used by exsltStrReplaceFunction
 */
static xmlChar*
exsltStrReplaceInternal(const xmlChar* str, const xmlChar* searchStr, 
                        const xmlChar* replaceStr)
{
    const xmlChar *curr, *next;
    xmlChar *ret = NULL;
    int searchStrSize;

    curr = str;
    searchStrSize = xmlStrlen(searchStr);

    do {
      next = xmlStrstr(curr, searchStr);
      if (next == NULL) {
        ret = xmlStrcat (ret, curr);
        break;
      }

      ret = xmlStrncat (ret, curr, next - curr);
      ret = xmlStrcat (ret, replaceStr);
      curr = next + searchStrSize;
    } while (*curr != 0);

    return ret;
}
/**
 * exsltStrReplaceFunction:
 * @ctxt: an XPath parser context
 * @nargs: the number of arguments
 *
 * Takes a string, and two node sets and returns the string with all strings in 
 * the first node set replaced by all strings in the second node set.
 */
static void
exsltStrReplaceFunction (xmlXPathParserContextPtr ctxt, int nargs) {
    xmlChar *str = NULL, *searchStr = NULL, *replaceStr = NULL;
    xmlNodeSetPtr replaceSet = NULL, searchSet = NULL;
    xmlChar *ret = NULL, *retSwap = NULL;
    int i;

    if (nargs  != 3) {
      xmlXPathSetArityError(ctxt);
      return;
    }

    /* pull out replace argument */
    if (!xmlXPathStackIsNodeSet(ctxt)) {
      replaceStr = xmlXPathPopString(ctxt);
    }
                else {
      replaceSet = xmlXPathPopNodeSet(ctxt);
      if (xmlXPathCheckError(ctxt)) {
        xmlXPathSetTypeError(ctxt);
        goto fail;
      }
    }

    /* behavior driven by search argument from here on */
    if (!xmlXPathStackIsNodeSet(ctxt)) {
      searchStr = xmlXPathPopString(ctxt);
      str = xmlXPathPopString(ctxt);

      if (replaceStr == NULL) {
        xmlXPathSetTypeError(ctxt);
        goto fail;
      }

      ret = exsltStrReplaceInternal(str, searchStr, replaceStr);
    }
                else {
      searchSet = xmlXPathPopNodeSet(ctxt);
      if (searchSet == NULL || xmlXPathCheckError(ctxt)) {
        xmlXPathSetTypeError(ctxt);
        goto fail;
      }

      str = xmlXPathPopString(ctxt);
      ret = xmlStrdup(str);

      for (i = 0; i < searchSet->nodeNr; i++) {
        searchStr = xmlXPathCastNodeToString(searchSet->nodeTab[i]);

        if (replaceSet != NULL) {
          replaceStr = NULL;
          if (i < replaceSet->nodeNr) {
            replaceStr = xmlXPathCastNodeToString(replaceSet->nodeTab[i]);
          }

          retSwap = exsltStrReplaceInternal(ret, searchStr, replaceStr);
          
          if (replaceStr != NULL) {
            xmlFree(replaceStr);
            replaceStr = NULL;
          }
        }
        else {
          retSwap = exsltStrReplaceInternal(ret, searchStr, replaceStr);
        }

                                xmlFree(ret);
        if (searchStr != NULL) {
          xmlFree(searchStr);
          searchStr = NULL;
        }

                                ret = retSwap;
                        }

      if (replaceSet != NULL)
        xmlXPathFreeNodeSet(replaceSet);

      if (searchSet != NULL)
        xmlXPathFreeNodeSet(searchSet);
                }

    xmlXPathReturnString(ctxt, ret);

 fail:
    if (replaceStr != NULL)
      xmlFree(replaceStr);

    if (searchStr != NULL)
      xmlFree(searchStr);

    if (str != NULL)
      xmlFree(str);
}

/**
 * exsltStrRegister:
 *
 * Registers the EXSLT - Strings module
 */

void
exsltStrRegister (void) {
    xsltRegisterExtModuleFunction ((const xmlChar *) "tokenize",
                                   EXSLT_STRINGS_NAMESPACE,
                                   exsltStrTokenizeFunction);
    xsltRegisterExtModuleFunction ((const xmlChar *) "split",
                                   EXSLT_STRINGS_NAMESPACE,
                                   exsltStrSplitFunction);
    xsltRegisterExtModuleFunction ((const xmlChar *) "encode-uri",
                                   EXSLT_STRINGS_NAMESPACE,
                                   exsltStrEncodeUriFunction);
    xsltRegisterExtModuleFunction ((const xmlChar *) "decode-uri",
                                   EXSLT_STRINGS_NAMESPACE,
                                   exsltStrDecodeUriFunction);
    xsltRegisterExtModuleFunction ((const xmlChar *) "padding",
                                   EXSLT_STRINGS_NAMESPACE,
                                   exsltStrPaddingFunction);
    xsltRegisterExtModuleFunction ((const xmlChar *) "align",
                                   EXSLT_STRINGS_NAMESPACE,
                                   exsltStrAlignFunction);
    xsltRegisterExtModuleFunction ((const xmlChar *) "concat",
                                   EXSLT_STRINGS_NAMESPACE,
                                   exsltStrConcatFunction);
    xsltRegisterExtModuleFunction ((const xmlChar *) "replace",
                                   EXSLT_STRINGS_NAMESPACE,
                                   exsltStrReplaceFunction);
}

/**
 * exsltStrXpathCtxtRegister:
 *
 * Registers the EXSLT - Strings module for use outside XSLT
 */
int
exsltStrXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix)
{
    if (ctxt
        && prefix
        && !xmlXPathRegisterNs(ctxt,
                               prefix,
                               (const xmlChar *) EXSLT_STRINGS_NAMESPACE)
        && !xmlXPathRegisterFuncNS(ctxt,
                                   (const xmlChar *) "encode-uri",
                                   (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
                                   exsltStrEncodeUriFunction)
        && !xmlXPathRegisterFuncNS(ctxt,
                                   (const xmlChar *) "decode-uri",
                                   (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
                                   exsltStrDecodeUriFunction)
        && !xmlXPathRegisterFuncNS(ctxt,
                                   (const xmlChar *) "padding",
                                   (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
                                   exsltStrPaddingFunction)
        && !xmlXPathRegisterFuncNS(ctxt,
                                   (const xmlChar *) "align",
                                   (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
                                   exsltStrAlignFunction)
        && !xmlXPathRegisterFuncNS(ctxt,
                                   (const xmlChar *) "concat",
                                   (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
                                   exsltStrConcatFunction)
        && !xmlXPathRegisterFuncNS(ctxt,
                                   (const xmlChar *) "replace",
                                   (const xmlChar *) EXSLT_STRINGS_NAMESPACE,
                                   exsltStrReplaceFunction)) {
        return 0;
    }
    return -1;
}

/* [<][>][^][v][top][bottom][index][help] */