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 */ 32static void 33exsltStrTokenizeFunction(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 xsltRegisterTmpRVT(tctxt, container); 75 ret = xmlXPathNewNodeSet(NULL); 76 if (ret != NULL) { 77 ret->boolval = 0; /* Freeing is not handled there anymore */ 78 for (cur = str, token = str; *cur != 0; cur += clen) { 79 clen = xmlUTF8Size(cur); 80 if (*delimiters == 0) { /* empty string case */ 81 xmlChar ctmp; 82 ctmp = *(cur+clen); 83 *(cur+clen) = 0; 84 node = xmlNewDocRawNode(container, NULL, 85 (const xmlChar *) "token", cur); 86 xmlAddChild((xmlNodePtr) container, node); 87 xmlXPathNodeSetAddUnique(ret->nodesetval, node); 88 *(cur+clen) = ctmp; /* restore the changed byte */ 89 token = cur + clen; 90 } else for (delimiter = delimiters; *delimiter != 0; 91 delimiter += xmlUTF8Size(delimiter)) { 92 if (!xmlUTF8Charcmp(cur, delimiter)) { 93 if (cur == token) { 94 /* discard empty tokens */ 95 token = cur + clen; 96 break; 97 } 98 *cur = 0; /* terminate the token */ 99 node = xmlNewDocRawNode(container, NULL, 100 (const xmlChar *) "token", token); 101 xmlAddChild((xmlNodePtr) container, node); 102 xmlXPathNodeSetAddUnique(ret->nodesetval, node); 103 *cur = *delimiter; /* restore the changed byte */ 104 token = cur + clen; 105 break; 106 } 107 } 108 } 109 if (token != cur) { 110 node = xmlNewDocRawNode(container, NULL, 111 (const xmlChar *) "token", token); 112 xmlAddChild((xmlNodePtr) container, node); 113 xmlXPathNodeSetAddUnique(ret->nodesetval, node); 114 } 115 } 116 } 117 118fail: 119 if (str != NULL) 120 xmlFree(str); 121 if (delimiters != NULL) 122 xmlFree(delimiters); 123 if (ret != NULL) 124 valuePush(ctxt, ret); 125 else 126 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 127} 128 129/** 130 * exsltStrSplitFunction: 131 * @ctxt: an XPath parser context 132 * @nargs: the number of arguments 133 * 134 * Splits up a string on a delimiting string and returns a node set of token 135 * elements, each containing one token from the string. 136 */ 137static void 138exsltStrSplitFunction(xmlXPathParserContextPtr ctxt, int nargs) { 139 xsltTransformContextPtr tctxt; 140 xmlChar *str, *delimiter, *cur; 141 const xmlChar *token; 142 xmlNodePtr node; 143 xmlDocPtr container; 144 xmlXPathObjectPtr ret = NULL; 145 int delimiterLength; 146 147 if ((nargs < 1) || (nargs > 2)) { 148 xmlXPathSetArityError(ctxt); 149 return; 150 } 151 152 if (nargs == 2) { 153 delimiter = xmlXPathPopString(ctxt); 154 if (xmlXPathCheckError(ctxt)) 155 return; 156 } else { 157 delimiter = xmlStrdup((const xmlChar *) " "); 158 } 159 if (delimiter == NULL) 160 return; 161 delimiterLength = xmlStrlen (delimiter); 162 163 str = xmlXPathPopString(ctxt); 164 if (xmlXPathCheckError(ctxt) || (str == NULL)) { 165 xmlFree(delimiter); 166 return; 167 } 168 169 /* Return a result tree fragment */ 170 tctxt = xsltXPathGetTransformContext(ctxt); 171 if (tctxt == NULL) { 172 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 173 "exslt:tokenize : internal error tctxt == NULL\n"); 174 goto fail; 175 } 176 177 container = xsltCreateRVT(tctxt); 178 if (container != NULL) { 179 xsltRegisterTmpRVT(tctxt, container); 180 ret = xmlXPathNewNodeSet(NULL); 181 if (ret != NULL) { 182 ret->boolval = 0; /* Freeing is not handled there anymore */ 183 for (cur = str, token = str; *cur != 0; cur++) { 184 if (delimiterLength == 0) { 185 if (cur != token) { 186 xmlChar tmp = *cur; 187 *cur = 0; 188 node = xmlNewDocRawNode(container, NULL, 189 (const xmlChar *) "token", token); 190 xmlAddChild((xmlNodePtr) container, node); 191 xmlXPathNodeSetAddUnique(ret->nodesetval, node); 192 *cur = tmp; 193 token++; 194 } 195 } 196 else if (!xmlStrncasecmp(cur, delimiter, delimiterLength)) { 197 if (cur == token) { 198 /* discard empty tokens */ 199 cur = cur + delimiterLength - 1; 200 token = cur + 1; 201 continue; 202 } 203 *cur = 0; 204 node = xmlNewDocRawNode(container, NULL, 205 (const xmlChar *) "token", token); 206 xmlAddChild((xmlNodePtr) container, node); 207 xmlXPathNodeSetAddUnique(ret->nodesetval, node); 208 *cur = *delimiter; 209 cur = cur + delimiterLength - 1; 210 token = cur + 1; 211 } 212 } 213 if (token != cur) { 214 node = xmlNewDocRawNode(container, NULL, 215 (const xmlChar *) "token", token); 216 xmlAddChild((xmlNodePtr) container, node); 217 xmlXPathNodeSetAddUnique(ret->nodesetval, node); 218 } 219 } 220 } 221 222fail: 223 if (str != NULL) 224 xmlFree(str); 225 if (delimiter != NULL) 226 xmlFree(delimiter); 227 if (ret != NULL) 228 valuePush(ctxt, ret); 229 else 230 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 231} 232 233/** 234 * exsltStrEncodeUriFunction: 235 * @ctxt: an XPath parser context 236 * @nargs: the number of arguments 237 * 238 * URI-Escapes a string 239 */ 240static void 241exsltStrEncodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) { 242 int escape_all = 1, str_len = 0; 243 xmlChar *str = NULL, *ret = NULL, *tmp; 244 245 if ((nargs < 2) || (nargs > 3)) { 246 xmlXPathSetArityError(ctxt); 247 return; 248 } 249 250 if (nargs >= 3) { 251 /* check for UTF-8 if encoding was explicitly given; 252 we don't support anything else yet */ 253 tmp = xmlXPathPopString(ctxt); 254 if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) { 255 xmlXPathReturnEmptyString(ctxt); 256 xmlFree(tmp); 257 return; 258 } 259 xmlFree(tmp); 260 } 261 262 escape_all = xmlXPathPopBoolean(ctxt); 263 264 str = xmlXPathPopString(ctxt); 265 str_len = xmlUTF8Strlen(str); 266 267 if (str_len == 0) { 268 xmlXPathReturnEmptyString(ctxt); 269 xmlFree(str); 270 return; 271 } 272 273 ret = xmlURIEscapeStr(str,(const xmlChar *)(escape_all?"-_.!~*'()":"-_.!~*'();/?:@&=+$,[]")); 274 xmlXPathReturnString(ctxt, ret); 275 276 if (str != NULL) 277 xmlFree(str); 278} 279 280/** 281 * exsltStrDecodeUriFunction: 282 * @ctxt: an XPath parser context 283 * @nargs: the number of arguments 284 * 285 * reverses URI-Escaping of a string 286 */ 287static void 288exsltStrDecodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) { 289 int str_len = 0; 290 xmlChar *str = NULL, *ret = NULL, *tmp; 291 292 if ((nargs < 1) || (nargs > 2)) { 293 xmlXPathSetArityError(ctxt); 294 return; 295 } 296 297 if (nargs >= 2) { 298 /* check for UTF-8 if encoding was explicitly given; 299 we don't support anything else yet */ 300 tmp = xmlXPathPopString(ctxt); 301 if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) { 302 xmlXPathReturnEmptyString(ctxt); 303 xmlFree(tmp); 304 return; 305 } 306 xmlFree(tmp); 307 } 308 309 str = xmlXPathPopString(ctxt); 310 str_len = xmlUTF8Strlen(str); 311 312 if (str_len == 0) { 313 xmlXPathReturnEmptyString(ctxt); 314 xmlFree(str); 315 return; 316 } 317 318 ret = (xmlChar *) xmlURIUnescapeString((const char *)str,0,NULL); 319 if (!xmlCheckUTF8(ret)) { 320 /* FIXME: instead of throwing away the whole URI, we should 321 only discard the invalid sequence(s). How to do that? */ 322 xmlXPathReturnEmptyString(ctxt); 323 xmlFree(str); 324 xmlFree(ret); 325 return; 326 } 327 328 xmlXPathReturnString(ctxt, ret); 329 330 if (str != NULL) 331 xmlFree(str); 332} 333 334/** 335 * exsltStrPaddingFunction: 336 * @ctxt: an XPath parser context 337 * @nargs: the number of arguments 338 * 339 * Creates a padding string of a certain length. 340 */ 341static void 342exsltStrPaddingFunction (xmlXPathParserContextPtr ctxt, int nargs) { 343 int number, str_len = 0; 344 xmlChar *str = NULL, *ret = NULL, *tmp; 345 346 if ((nargs < 1) || (nargs > 2)) { 347 xmlXPathSetArityError(ctxt); 348 return; 349 } 350 351 if (nargs == 2) { 352 str = xmlXPathPopString(ctxt); 353 str_len = xmlUTF8Strlen(str); 354 } 355 if (str_len == 0) { 356 if (str != NULL) xmlFree(str); 357 str = xmlStrdup((const xmlChar *) " "); 358 str_len = 1; 359 } 360 361 number = (int) xmlXPathPopNumber(ctxt); 362 363 if (number <= 0) { 364 xmlXPathReturnEmptyString(ctxt); 365 xmlFree(str); 366 return; 367 } 368 369 while (number >= str_len) { 370 ret = xmlStrncat(ret, str, str_len); 371 number -= str_len; 372 } 373 tmp = xmlUTF8Strndup (str, number); 374 ret = xmlStrcat(ret, tmp); 375 if (tmp != NULL) 376 xmlFree (tmp); 377 378 xmlXPathReturnString(ctxt, ret); 379 380 if (str != NULL) 381 xmlFree(str); 382} 383 384/** 385 * exsltStrAlignFunction: 386 * @ctxt: an XPath parser context 387 * @nargs: the number of arguments 388 * 389 * Aligns a string within another string. 390 */ 391static void 392exsltStrAlignFunction (xmlXPathParserContextPtr ctxt, int nargs) { 393 xmlChar *str, *padding, *alignment, *ret; 394 int str_l, padding_l; 395 396 if ((nargs < 2) || (nargs > 3)) { 397 xmlXPathSetArityError(ctxt); 398 return; 399 } 400 401 if (nargs == 3) 402 alignment = xmlXPathPopString(ctxt); 403 else 404 alignment = NULL; 405 406 padding = xmlXPathPopString(ctxt); 407 str = xmlXPathPopString(ctxt); 408 409 str_l = xmlUTF8Strlen (str); 410 padding_l = xmlUTF8Strlen (padding); 411 412 if (str_l == padding_l) { 413 xmlXPathReturnString (ctxt, str); 414 xmlFree(padding); 415 xmlFree(alignment); 416 return; 417 } 418 419 if (str_l > padding_l) { 420 ret = xmlUTF8Strndup (str, padding_l); 421 } else { 422 if (xmlStrEqual(alignment, (const xmlChar *) "right")) { 423 ret = xmlUTF8Strndup (padding, padding_l - str_l); 424 ret = xmlStrcat (ret, str); 425 } else if (xmlStrEqual(alignment, (const xmlChar *) "center")) { 426 int left = (padding_l - str_l) / 2; 427 int right_start; 428 429 ret = xmlUTF8Strndup (padding, left); 430 ret = xmlStrcat (ret, str); 431 432 right_start = xmlUTF8Strsize (padding, left + str_l); 433 ret = xmlStrcat (ret, padding + right_start); 434 } else { 435 int str_s; 436 437 str_s = xmlStrlen (str); 438 ret = xmlStrdup (str); 439 ret = xmlStrcat (ret, padding + str_s); 440 } 441 } 442 443 xmlXPathReturnString (ctxt, ret); 444 445 xmlFree(str); 446 xmlFree(padding); 447 xmlFree(alignment); 448} 449 450/** 451 * exsltStrConcatFunction: 452 * @ctxt: an XPath parser context 453 * @nargs: the number of arguments 454 * 455 * Takes a node set and returns the concatenation of the string values 456 * of the nodes in that node set. If the node set is empty, it 457 * returns an empty string. 458 */ 459static void 460exsltStrConcatFunction (xmlXPathParserContextPtr ctxt, int nargs) { 461 xmlXPathObjectPtr obj; 462 xmlChar *ret = NULL; 463 int i; 464 465 if (nargs != 1) { 466 xmlXPathSetArityError(ctxt); 467 return; 468 } 469 470 if (!xmlXPathStackIsNodeSet(ctxt)) { 471 xmlXPathSetTypeError(ctxt); 472 return; 473 } 474 475 obj = valuePop (ctxt); 476 477 if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) { 478 xmlXPathReturnEmptyString(ctxt); 479 return; 480 } 481 482 for (i = 0; i < obj->nodesetval->nodeNr; i++) { 483 xmlChar *tmp; 484 tmp = xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]); 485 486 ret = xmlStrcat (ret, tmp); 487 488 xmlFree(tmp); 489 } 490 491 xmlXPathFreeObject (obj); 492 493 xmlXPathReturnString(ctxt, ret); 494} 495 496/** 497 * exsltStrRegister: 498 * 499 * Registers the EXSLT - Strings module 500 */ 501 502void 503exsltStrRegister (void) { 504 xsltRegisterExtModuleFunction ((const xmlChar *) "tokenize", 505 EXSLT_STRINGS_NAMESPACE, 506 exsltStrTokenizeFunction); 507 xsltRegisterExtModuleFunction ((const xmlChar *) "split", 508 EXSLT_STRINGS_NAMESPACE, 509 exsltStrSplitFunction); 510 xsltRegisterExtModuleFunction ((const xmlChar *) "encode-uri", 511 EXSLT_STRINGS_NAMESPACE, 512 exsltStrEncodeUriFunction); 513 xsltRegisterExtModuleFunction ((const xmlChar *) "decode-uri", 514 EXSLT_STRINGS_NAMESPACE, 515 exsltStrDecodeUriFunction); 516 xsltRegisterExtModuleFunction ((const xmlChar *) "padding", 517 EXSLT_STRINGS_NAMESPACE, 518 exsltStrPaddingFunction); 519 xsltRegisterExtModuleFunction ((const xmlChar *) "align", 520 EXSLT_STRINGS_NAMESPACE, 521 exsltStrAlignFunction); 522 xsltRegisterExtModuleFunction ((const xmlChar *) "concat", 523 EXSLT_STRINGS_NAMESPACE, 524 exsltStrConcatFunction); 525} 526