#define IN_LIBEXSLT #include "libexslt/libexslt.h" #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) #include #else #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #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; }