1/* 2 * functions.c: Implementation of the XSLT extra functions 3 * 4 * Reference: 5 * http://www.w3.org/TR/1999/REC-xslt-19991116 6 * 7 * See Copyright for the status of this software. 8 * 9 * daniel@veillard.com 10 * Bjorn Reese <breese@users.sourceforge.net> for number formatting 11 */ 12 13#define IN_LIBXSLT 14#include "libxslt.h" 15 16#include <string.h> 17 18#ifdef HAVE_SYS_TYPES_H 19#include <sys/types.h> 20#endif 21#ifdef HAVE_CTYPE_H 22#include <ctype.h> 23#endif 24 25#include <libxml/xmlmemory.h> 26#include <libxml/parser.h> 27#include <libxml/tree.h> 28#include <libxml/valid.h> 29#include <libxml/hash.h> 30#include <libxml/xmlerror.h> 31#include <libxml/xpath.h> 32#include <libxml/xpathInternals.h> 33#include <libxml/parserInternals.h> 34#include <libxml/uri.h> 35#include <libxml/xpointer.h> 36#include "xslt.h" 37#include "xsltInternals.h" 38#include "xsltutils.h" 39#include "functions.h" 40#include "extensions.h" 41#include "numbersInternals.h" 42#include "keys.h" 43#include "documents.h" 44 45#ifdef WITH_XSLT_DEBUG 46#define WITH_XSLT_DEBUG_FUNCTION 47#endif 48 49/* 50 * Some versions of DocBook XSL use the vendor string to detect 51 * supporting chunking, this is a workaround to be considered 52 * in the list of decent XSLT processors <grin/> 53 */ 54#define DOCBOOK_XSL_HACK 55 56/** 57 * xsltXPathFunctionLookup: 58 * @ctxt: a void * but the XSLT transformation context actually 59 * @name: the function name 60 * @ns_uri: the function namespace URI 61 * 62 * This is the entry point when a function is needed by the XPath 63 * interpretor. 64 * 65 * Returns the callback function or NULL if not found 66 */ 67xmlXPathFunction 68xsltXPathFunctionLookup (xmlXPathContextPtr ctxt, 69 const xmlChar *name, const xmlChar *ns_uri) { 70 xmlXPathFunction ret; 71 72 if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL)) 73 return (NULL); 74 75#ifdef WITH_XSLT_DEBUG_FUNCTION 76 xsltGenericDebug(xsltGenericDebugContext, 77 "Lookup function {%s}%s\n", ns_uri, name); 78#endif 79 80 /* give priority to context-level functions */ 81 /* 82 ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); 83 */ 84 XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri); 85 86 if (ret == NULL) 87 ret = xsltExtModuleFunctionLookup(name, ns_uri); 88 89#ifdef WITH_XSLT_DEBUG_FUNCTION 90 if (ret != NULL) 91 xsltGenericDebug(xsltGenericDebugContext, 92 "found function %s\n", name); 93#endif 94 return(ret); 95} 96 97 98/************************************************************************ 99 * * 100 * Module interfaces * 101 * * 102 ************************************************************************/ 103 104static void 105xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI) 106{ 107 xsltTransformContextPtr tctxt; 108 xmlURIPtr uri; 109 xmlChar *fragment; 110 xsltDocumentPtr xsltdoc; 111 xmlDocPtr doc; 112 xmlXPathContextPtr xptrctxt = NULL; 113 xmlXPathObjectPtr object = NULL; 114 115 tctxt = xsltXPathGetTransformContext(ctxt); 116 if (tctxt == NULL) { 117 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 118 "document() : internal error tctxt == NULL\n"); 119 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 120 return; 121 } 122 123 uri = xmlParseURI((const char *) URI); 124 if (uri == NULL) { 125 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 126 "document() : failed to parse URI\n"); 127 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 128 return; 129 } 130 131 /* 132 * check for and remove fragment identifier 133 */ 134 fragment = (xmlChar *)uri->fragment; 135 if (fragment != NULL) { 136 uri->fragment = NULL; 137 URI = xmlSaveUri(uri); 138 xsltdoc = xsltLoadDocument(tctxt, URI); 139 xmlFree(URI); 140 } else 141 xsltdoc = xsltLoadDocument(tctxt, URI); 142 xmlFreeURI(uri); 143 144 if (xsltdoc == NULL) { 145 if ((URI == NULL) || 146 (URI[0] == '#') || 147 (xmlStrEqual(tctxt->style->doc->URL, URI))) { 148 doc = tctxt->style->doc; 149 } else { 150 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 151 152 if (fragment != NULL) 153 xmlFree(fragment); 154 155 return; 156 } 157 } else 158 doc = xsltdoc->doc; 159 160 if ( fragment == NULL ) { 161 valuePush(ctxt, 162 xmlXPathNewNodeSet((xmlNodePtr) doc)); 163 return; 164 } 165 166 /* use XPointer of HTML location for fragment ID */ 167#ifdef LIBXML_XPTR_ENABLED 168 xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); 169 if (xptrctxt == NULL) { 170 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 171 "document() : internal error xptrctxt == NULL\n"); 172 goto out_fragment; 173 } 174 175 object = xmlXPtrEval(fragment, xptrctxt); 176#endif 177 xmlFree(fragment); 178 if (xptrctxt != NULL) 179 xmlXPathFreeContext(xptrctxt); 180 181 if (object == NULL) 182 goto out_fragment; 183 184 switch (object->type) { 185 case XPATH_NODESET: 186 break; 187 case XPATH_UNDEFINED: 188 case XPATH_BOOLEAN: 189 case XPATH_NUMBER: 190 case XPATH_STRING: 191 case XPATH_POINT: 192 case XPATH_USERS: 193 case XPATH_XSLT_TREE: 194 case XPATH_RANGE: 195 case XPATH_LOCATIONSET: 196 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 197 "document() : XPointer does not select a node set: #%s\n", 198 fragment); 199 goto out_object; 200 } 201 202 valuePush(ctxt, object); 203 return; 204 205out_object: 206 xmlXPathFreeObject(object); 207 208out_fragment: 209 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 210} 211 212/** 213 * xsltDocumentFunction: 214 * @ctxt: the XPath Parser context 215 * @nargs: the number of arguments 216 * 217 * Implement the document() XSLT function 218 * node-set document(object, node-set?) 219 */ 220void 221xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) 222{ 223 xmlXPathObjectPtr obj, obj2 = NULL; 224 xmlChar *base = NULL, *URI; 225 226 227 if ((nargs < 1) || (nargs > 2)) { 228 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 229 "document() : invalid number of args %d\n", 230 nargs); 231 ctxt->error = XPATH_INVALID_ARITY; 232 return; 233 } 234 if (ctxt->value == NULL) { 235 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 236 "document() : invalid arg value\n"); 237 ctxt->error = XPATH_INVALID_TYPE; 238 return; 239 } 240 241 if (nargs == 2) { 242 if (ctxt->value->type != XPATH_NODESET) { 243 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 244 "document() : invalid arg expecting a nodeset\n"); 245 ctxt->error = XPATH_INVALID_TYPE; 246 return; 247 } 248 249 obj2 = valuePop(ctxt); 250 } 251 252 if (ctxt->value->type == XPATH_NODESET) { 253 int i; 254 xmlXPathObjectPtr newobj, ret; 255 256 obj = valuePop(ctxt); 257 ret = xmlXPathNewNodeSet(NULL); 258 259 if (obj->nodesetval) { 260 for (i = 0; i < obj->nodesetval->nodeNr; i++) { 261 valuePush(ctxt, 262 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); 263 xmlXPathStringFunction(ctxt, 1); 264 if (nargs == 2) { 265 valuePush(ctxt, xmlXPathObjectCopy(obj2)); 266 } else { 267 valuePush(ctxt, 268 xmlXPathNewNodeSet(obj->nodesetval-> 269 nodeTab[i])); 270 } 271 xsltDocumentFunction(ctxt, 2); 272 newobj = valuePop(ctxt); 273 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, 274 newobj->nodesetval); 275 xmlXPathFreeObject(newobj); 276 } 277 } 278 279 xmlXPathFreeObject(obj); 280 if (obj2 != NULL) 281 xmlXPathFreeObject(obj2); 282 valuePush(ctxt, ret); 283 return; 284 } 285 /* 286 * Make sure it's converted to a string 287 */ 288 xmlXPathStringFunction(ctxt, 1); 289 if (ctxt->value->type != XPATH_STRING) { 290 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 291 "document() : invalid arg expecting a string\n"); 292 ctxt->error = XPATH_INVALID_TYPE; 293 if (obj2 != NULL) 294 xmlXPathFreeObject(obj2); 295 return; 296 } 297 obj = valuePop(ctxt); 298 if (obj->stringval == NULL) { 299 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 300 } else { 301 if ((obj2 != NULL) && (obj2->nodesetval != NULL) && 302 (obj2->nodesetval->nodeNr > 0) && 303 IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { 304 xmlNodePtr target; 305 306 target = obj2->nodesetval->nodeTab[0]; 307 if ((target->type == XML_ATTRIBUTE_NODE) || 308 (target->type == XML_PI_NODE)) { 309 target = ((xmlAttrPtr) target)->parent; 310 } 311 base = xmlNodeGetBase(target->doc, target); 312 } else { 313 xsltTransformContextPtr tctxt; 314 315 tctxt = xsltXPathGetTransformContext(ctxt); 316 if ((tctxt != NULL) && (tctxt->inst != NULL)) { 317 base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); 318 } else if ((tctxt != NULL) && (tctxt->style != NULL) && 319 (tctxt->style->doc != NULL)) { 320 base = xmlNodeGetBase(tctxt->style->doc, 321 (xmlNodePtr) tctxt->style->doc); 322 } 323 } 324 URI = xmlBuildURI(obj->stringval, base); 325 if (base != NULL) 326 xmlFree(base); 327 if (URI == NULL) { 328 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 329 } else { 330 xsltDocumentFunctionLoadDocument( ctxt, URI ); 331 xmlFree(URI); 332 } 333 } 334 xmlXPathFreeObject(obj); 335 if (obj2 != NULL) 336 xmlXPathFreeObject(obj2); 337} 338 339/** 340 * xsltKeyFunction: 341 * @ctxt: the XPath Parser context 342 * @nargs: the number of arguments 343 * 344 * Implement the key() XSLT function 345 * node-set key(string, object) 346 */ 347void 348xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ 349 xmlNodeSetPtr nodelist; 350 xmlXPathObjectPtr obj1, obj2; 351 xmlChar *key = NULL, *value; 352 const xmlChar *keyURI; 353 xsltTransformContextPtr tctxt; 354 xsltDocumentPtr oldDocumentPtr; 355 xmlDocPtr oldXPathDocPtr; 356 357 if (nargs != 2) { 358 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 359 "key() : expects two arguments\n"); 360 ctxt->error = XPATH_INVALID_ARITY; 361 return; 362 } 363 364 obj2 = valuePop(ctxt); 365 xmlXPathStringFunction(ctxt, 1); 366 if ((obj2 == NULL) || 367 (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 368 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 369 "key() : invalid arg expecting a string\n"); 370 ctxt->error = XPATH_INVALID_TYPE; 371 xmlXPathFreeObject(obj2); 372 373 return; 374 } 375 obj1 = valuePop(ctxt); 376 377 if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { 378 int i; 379 xmlXPathObjectPtr newobj, ret; 380 381 ret = xmlXPathNewNodeSet(NULL); 382 383 if (obj2->nodesetval != NULL) { 384 for (i = 0; i < obj2->nodesetval->nodeNr; i++) { 385 valuePush(ctxt, xmlXPathObjectCopy(obj1)); 386 valuePush(ctxt, 387 xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i])); 388 xmlXPathStringFunction(ctxt, 1); 389 xsltKeyFunction(ctxt, 2); 390 newobj = valuePop(ctxt); 391 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, 392 newobj->nodesetval); 393 xmlXPathFreeObject(newobj); 394 } 395 } 396 valuePush(ctxt, ret); 397 } else { 398 xmlChar *qname, *prefix; 399 400 /* 401 * Get the associated namespace URI if qualified name 402 */ 403 qname = obj1->stringval; 404 key = xmlSplitQName2(qname, &prefix); 405 if (key == NULL) { 406 key = xmlStrdup(obj1->stringval); 407 keyURI = NULL; 408 if (prefix != NULL) 409 xmlFree(prefix); 410 } else { 411 if (prefix != NULL) { 412 keyURI = xmlXPathNsLookup(ctxt->context, prefix); 413 if (keyURI == NULL) { 414 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 415 "key() : prefix %s is not bound\n", prefix); 416 } 417 xmlFree(prefix); 418 } else { 419 keyURI = NULL; 420 } 421 } 422 423 /* 424 * Force conversion of first arg to string 425 */ 426 valuePush(ctxt, obj2); 427 xmlXPathStringFunction(ctxt, 1); 428 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 429 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 430 "key() : invalid arg expecting a string\n"); 431 ctxt->error = XPATH_INVALID_TYPE; 432 xmlXPathFreeObject(obj1); 433 434 return; 435 } 436 obj2 = valuePop(ctxt); 437 value = obj2->stringval; 438 439 tctxt = xsltXPathGetTransformContext(ctxt); 440 oldDocumentPtr = tctxt->document; 441 oldXPathDocPtr = tctxt->xpathCtxt->doc; 442 if ((ctxt->context->doc != NULL) && 443 (tctxt->document->doc != ctxt->context->doc)) { 444 /* 445 * The xpath context document needs to be changed. If the 446 * current context document is a node-set, we must use an 447 * xsltDocument associated with the node-set, which may or 448 * may not currently exist. 449 */ 450 if (xmlStrEqual((const xmlChar *)ctxt->context->doc->name, 451 BAD_CAST " fake node libxslt")) { /* node-set */ 452 /* 453 * Check whether we already have an xsltDocument set up 454 */ 455 if (ctxt->context->doc->_private == NULL) /* nope */ 456 ctxt->context->doc->_private = 457 xsltNewDocument(tctxt, ctxt->context->doc); 458 tctxt->document = ctxt->context->doc->_private; 459 } 460 else { 461 tctxt->document = xsltFindDocument(tctxt, ctxt->context->doc); 462 if (tctxt->document == NULL) 463 tctxt->document = oldDocumentPtr; 464 else 465 tctxt->xpathCtxt->doc = ctxt->context->doc; 466 } 467 } 468 nodelist = xsltGetKey(tctxt, key, keyURI, value); 469 tctxt->document = oldDocumentPtr; 470 tctxt->xpathCtxt->doc = oldXPathDocPtr; 471 valuePush(ctxt, xmlXPathWrapNodeSet( 472 xmlXPathNodeSetMerge(NULL, nodelist))); 473 } 474 475 476 if (obj1 != NULL) 477 xmlXPathFreeObject(obj1); 478 if (obj2 != NULL) 479 xmlXPathFreeObject(obj2); 480 if (key != NULL) 481 xmlFree(key); 482} 483 484/** 485 * xsltUnparsedEntityURIFunction: 486 * @ctxt: the XPath Parser context 487 * @nargs: the number of arguments 488 * 489 * Implement the unparsed-entity-uri() XSLT function 490 * string unparsed-entity-uri(string) 491 */ 492void 493xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){ 494 xmlXPathObjectPtr obj; 495 xmlChar *str; 496 497 if ((nargs != 1) || (ctxt->value == NULL)) { 498 xsltGenericError(xsltGenericErrorContext, 499 "unparsed-entity-uri() : expects one string arg\n"); 500 ctxt->error = XPATH_INVALID_ARITY; 501 return; 502 } 503 obj = valuePop(ctxt); 504 if (obj->type != XPATH_STRING) { 505 obj = xmlXPathConvertString(obj); 506 } 507 508 str = obj->stringval; 509 if (str == NULL) { 510 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 511 } else { 512 xmlEntityPtr entity; 513 514 entity = xmlGetDocEntity(ctxt->context->doc, str); 515 if (entity == NULL) { 516 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 517 } else { 518 if (entity->URI != NULL) 519 valuePush(ctxt, xmlXPathNewString(entity->URI)); 520 else 521 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 522 } 523 } 524 xmlXPathFreeObject(obj); 525} 526 527/** 528 * xsltFormatNumberFunction: 529 * @ctxt: the XPath Parser context 530 * @nargs: the number of arguments 531 * 532 * Implement the format-number() XSLT function 533 * string format-number(number, string, string?) 534 */ 535void 536xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) 537{ 538 xmlXPathObjectPtr numberObj = NULL; 539 xmlXPathObjectPtr formatObj = NULL; 540 xmlXPathObjectPtr decimalObj = NULL; 541 xsltStylesheetPtr sheet; 542 xsltDecimalFormatPtr formatValues; 543 xmlChar *result; 544 xsltTransformContextPtr tctxt; 545 546 tctxt = xsltXPathGetTransformContext(ctxt); 547 if (tctxt == NULL) 548 return; 549 sheet = tctxt->style; 550 if (sheet == NULL) 551 return; 552 formatValues = sheet->decimalFormat; 553 554 switch (nargs) { 555 case 3: 556 CAST_TO_STRING; 557 decimalObj = valuePop(ctxt); 558 formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval); 559 if (formatValues == NULL) { 560 xsltTransformError(tctxt, NULL, NULL, 561 "format-number() : undeclared decimal format '%s'\n", 562 decimalObj->stringval); 563 } 564 /* Intentional fall-through */ 565 case 2: 566 CAST_TO_STRING; 567 formatObj = valuePop(ctxt); 568 CAST_TO_NUMBER; 569 numberObj = valuePop(ctxt); 570 break; 571 default: 572 XP_ERROR(XPATH_INVALID_ARITY); 573 } 574 575 if (formatValues != NULL) { 576 if (xsltFormatNumberConversion(formatValues, 577 formatObj->stringval, 578 numberObj->floatval, 579 &result) == XPATH_EXPRESSION_OK) { 580 valuePush(ctxt, xmlXPathNewString(result)); 581 xmlFree(result); 582 } 583 } 584 585 xmlXPathFreeObject(numberObj); 586 xmlXPathFreeObject(formatObj); 587 xmlXPathFreeObject(decimalObj); 588} 589 590/** 591 * xsltGenerateIdFunction: 592 * @ctxt: the XPath Parser context 593 * @nargs: the number of arguments 594 * 595 * Implement the generate-id() XSLT function 596 * string generate-id(node-set?) 597 */ 598void 599xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){ 600 xmlNodePtr cur = NULL; 601 unsigned long val; 602 xmlChar str[20]; 603 604 if (nargs == 0) { 605 cur = ctxt->context->node; 606 } else if (nargs == 1) { 607 xmlXPathObjectPtr obj; 608 xmlNodeSetPtr nodelist; 609 int i, ret; 610 611 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) { 612 ctxt->error = XPATH_INVALID_TYPE; 613 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 614 "generate-id() : invalid arg expecting a node-set\n"); 615 return; 616 } 617 obj = valuePop(ctxt); 618 nodelist = obj->nodesetval; 619 if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) { 620 xmlXPathFreeObject(obj); 621 valuePush(ctxt, xmlXPathNewCString("")); 622 return; 623 } 624 cur = nodelist->nodeTab[0]; 625 for (i = 1;i < nodelist->nodeNr;i++) { 626 ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]); 627 if (ret == -1) 628 cur = nodelist->nodeTab[i]; 629 } 630 xmlXPathFreeObject(obj); 631 } else { 632 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 633 "generate-id() : invalid number of args %d\n", nargs); 634 ctxt->error = XPATH_INVALID_ARITY; 635 return; 636 } 637 /* 638 * Okay this is ugly but should work, use the NodePtr address 639 * to forge the ID 640 */ 641 val = (unsigned long)((char *)cur - (char *)0); 642 val /= sizeof(xmlNode); 643 sprintf((char *)str, "id%ld", val); 644 valuePush(ctxt, xmlXPathNewString(str)); 645} 646 647/** 648 * xsltSystemPropertyFunction: 649 * @ctxt: the XPath Parser context 650 * @nargs: the number of arguments 651 * 652 * Implement the system-property() XSLT function 653 * object system-property(string) 654 */ 655void 656xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){ 657 xmlXPathObjectPtr obj; 658 xmlChar *prefix, *name; 659 const xmlChar *nsURI = NULL; 660 661 if (nargs != 1) { 662 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 663 "system-property() : expects one string arg\n"); 664 ctxt->error = XPATH_INVALID_ARITY; 665 return; 666 } 667 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 668 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 669 "system-property() : invalid arg expecting a string\n"); 670 ctxt->error = XPATH_INVALID_TYPE; 671 return; 672 } 673 obj = valuePop(ctxt); 674 if (obj->stringval == NULL) { 675 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 676 } else { 677 name = xmlSplitQName2(obj->stringval, &prefix); 678 if (name == NULL) { 679 name = xmlStrdup(obj->stringval); 680 } else { 681 nsURI = xmlXPathNsLookup(ctxt->context, prefix); 682 if (nsURI == NULL) { 683 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 684 "system-property() : prefix %s is not bound\n", prefix); 685 } 686 } 687 688 if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) { 689#ifdef DOCBOOK_XSL_HACK 690 if (xmlStrEqual(name, (const xmlChar *)"vendor")) { 691 xsltStylesheetPtr sheet; 692 xsltTransformContextPtr tctxt; 693 694 tctxt = xsltXPathGetTransformContext(ctxt); 695 if ((tctxt != NULL) && (tctxt->inst != NULL) && 696 (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) && 697 (tctxt->inst->parent != NULL) && 698 (xmlStrEqual(tctxt->inst->parent->name, 699 BAD_CAST "template"))) 700 sheet = tctxt->style; 701 else 702 sheet = NULL; 703 if ((sheet != NULL) && (sheet->doc != NULL) && 704 (sheet->doc->URL != NULL) && 705 (xmlStrstr(sheet->doc->URL, 706 (const xmlChar *)"chunk") != NULL)) { 707 valuePush(ctxt, xmlXPathNewString( 708 (const xmlChar *)"libxslt (SAXON 6.2 compatible)")); 709 710 } else { 711 valuePush(ctxt, xmlXPathNewString( 712 (const xmlChar *)XSLT_DEFAULT_VENDOR)); 713 } 714 } else 715#else 716 if (xmlStrEqual(name, (const xmlChar *)"vendor")) { 717 valuePush(ctxt, xmlXPathNewString( 718 (const xmlChar *)XSLT_DEFAULT_VENDOR)); 719 } else 720#endif 721 if (xmlStrEqual(name, (const xmlChar *)"version")) { 722 valuePush(ctxt, xmlXPathNewString( 723 (const xmlChar *)XSLT_DEFAULT_VERSION)); 724 } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) { 725 valuePush(ctxt, xmlXPathNewString( 726 (const xmlChar *)XSLT_DEFAULT_URL)); 727 } else { 728 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 729 } 730 } 731 if (name != NULL) 732 xmlFree(name); 733 if (prefix != NULL) 734 xmlFree(prefix); 735 } 736 xmlXPathFreeObject(obj); 737} 738 739/** 740 * xsltElementAvailableFunction: 741 * @ctxt: the XPath Parser context 742 * @nargs: the number of arguments 743 * 744 * Implement the element-available() XSLT function 745 * boolean element-available(string) 746 */ 747void 748xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ 749 xmlXPathObjectPtr obj; 750 xmlChar *prefix, *name; 751 const xmlChar *nsURI = NULL; 752 xsltTransformContextPtr tctxt; 753 754 if (nargs != 1) { 755 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 756 "element-available() : expects one string arg\n"); 757 ctxt->error = XPATH_INVALID_ARITY; 758 return; 759 } 760 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 761 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 762 "element-available() : invalid arg expecting a string\n"); 763 ctxt->error = XPATH_INVALID_TYPE; 764 return; 765 } 766 obj = valuePop(ctxt); 767 tctxt = xsltXPathGetTransformContext(ctxt); 768 if (tctxt == NULL) { 769 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 770 "element-available() : internal error tctxt == NULL\n"); 771 xmlXPathFreeObject(obj); 772 valuePush(ctxt, xmlXPathNewBoolean(0)); 773 return; 774 } 775 776 777 name = xmlSplitQName2(obj->stringval, &prefix); 778 if (name == NULL) { 779 xmlNsPtr ns; 780 781 name = xmlStrdup(obj->stringval); 782 ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL); 783 if (ns != NULL) nsURI = xmlStrdup(ns->href); 784 } else { 785 nsURI = xmlXPathNsLookup(ctxt->context, prefix); 786 if (nsURI == NULL) { 787 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 788 "element-available() : prefix %s is not bound\n", prefix); 789 } 790 } 791 792 if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) { 793 valuePush(ctxt, xmlXPathNewBoolean(1)); 794 } else { 795 valuePush(ctxt, xmlXPathNewBoolean(0)); 796 } 797 798 xmlXPathFreeObject(obj); 799 if (name != NULL) 800 xmlFree(name); 801 if (prefix != NULL) 802 xmlFree(prefix); 803} 804 805/** 806 * xsltFunctionAvailableFunction: 807 * @ctxt: the XPath Parser context 808 * @nargs: the number of arguments 809 * 810 * Implement the function-available() XSLT function 811 * boolean function-available(string) 812 */ 813void 814xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ 815 xmlXPathObjectPtr obj; 816 xmlChar *prefix, *name; 817 const xmlChar *nsURI = NULL; 818 819 if (nargs != 1) { 820 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 821 "function-available() : expects one string arg\n"); 822 ctxt->error = XPATH_INVALID_ARITY; 823 return; 824 } 825 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 826 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 827 "function-available() : invalid arg expecting a string\n"); 828 ctxt->error = XPATH_INVALID_TYPE; 829 return; 830 } 831 obj = valuePop(ctxt); 832 833 name = xmlSplitQName2(obj->stringval, &prefix); 834 if (name == NULL) { 835 name = xmlStrdup(obj->stringval); 836 } else { 837 nsURI = xmlXPathNsLookup(ctxt->context, prefix); 838 if (nsURI == NULL) { 839 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 840 "function-available() : prefix %s is not bound\n", prefix); 841 } 842 } 843 844 if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) { 845 valuePush(ctxt, xmlXPathNewBoolean(1)); 846 } else { 847 valuePush(ctxt, xmlXPathNewBoolean(0)); 848 } 849 850 xmlXPathFreeObject(obj); 851 if (name != NULL) 852 xmlFree(name); 853 if (prefix != NULL) 854 xmlFree(prefix); 855} 856 857/** 858 * xsltCurrentFunction: 859 * @ctxt: the XPath Parser context 860 * @nargs: the number of arguments 861 * 862 * Implement the current() XSLT function 863 * node-set current() 864 */ 865static void 866xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){ 867 xsltTransformContextPtr tctxt; 868 869 if (nargs != 0) { 870 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 871 "current() : function uses no argument\n"); 872 ctxt->error = XPATH_INVALID_ARITY; 873 return; 874 } 875 tctxt = xsltXPathGetTransformContext(ctxt); 876 if (tctxt == NULL) { 877 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 878 "current() : internal error tctxt == NULL\n"); 879 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 880 } else { 881 valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */ 882 } 883} 884 885/************************************************************************ 886 * * 887 * Registration of XSLT and libxslt functions * 888 * * 889 ************************************************************************/ 890 891/** 892 * xsltRegisterAllFunctions: 893 * @ctxt: the XPath context 894 * 895 * Registers all default XSLT functions in this context 896 */ 897void 898xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) 899{ 900 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current", 901 xsltCurrentFunction); 902 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document", 903 xsltDocumentFunction); 904 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction); 905 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri", 906 xsltUnparsedEntityURIFunction); 907 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number", 908 xsltFormatNumberFunction); 909 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id", 910 xsltGenerateIdFunction); 911 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property", 912 xsltSystemPropertyFunction); 913 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available", 914 xsltElementAvailableFunction); 915 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available", 916 xsltFunctionAvailableFunction); 917} 918