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 idoc; /* document info */ 111 xmlDocPtr doc; 112 xmlXPathContextPtr xptrctxt = NULL; 113 xmlXPathObjectPtr resObj = NULL; 114 115 tctxt = xsltXPathGetTransformContext(ctxt); 116 if (tctxt == NULL) { 117 xsltTransformError(NULL, 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(tctxt, 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 xmlChar *newURI; 137 uri->fragment = NULL; 138 newURI = xmlSaveUri(uri); 139 idoc = xsltLoadDocument(tctxt, newURI); 140 xmlFree(newURI); 141 } else 142 idoc = xsltLoadDocument(tctxt, URI); 143 xmlFreeURI(uri); 144 145 if (idoc == NULL) { 146 if ((URI == NULL) || 147 (URI[0] == '#') || 148 ((tctxt->style->doc != NULL) && 149 (xmlStrEqual(tctxt->style->doc->URL, URI)))) 150 { 151 /* 152 * This selects the stylesheet's doc itself. 153 */ 154 doc = tctxt->style->doc; 155 } else { 156 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 157 158 if (fragment != NULL) 159 xmlFree(fragment); 160 161 return; 162 } 163 } else 164 doc = idoc->doc; 165 166 if (fragment == NULL) { 167 valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc)); 168 return; 169 } 170 171 /* use XPointer of HTML location for fragment ID */ 172#ifdef LIBXML_XPTR_ENABLED 173 xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); 174 if (xptrctxt == NULL) { 175 xsltTransformError(tctxt, NULL, NULL, 176 "document() : internal error xptrctxt == NULL\n"); 177 goto out_fragment; 178 } 179 180 resObj = xmlXPtrEval(fragment, xptrctxt); 181 xmlXPathFreeContext(xptrctxt); 182#endif 183 xmlFree(fragment); 184 185 if (resObj == NULL) 186 goto out_fragment; 187 188 switch (resObj->type) { 189 case XPATH_NODESET: 190 break; 191 case XPATH_UNDEFINED: 192 case XPATH_BOOLEAN: 193 case XPATH_NUMBER: 194 case XPATH_STRING: 195 case XPATH_POINT: 196 case XPATH_USERS: 197 case XPATH_XSLT_TREE: 198 case XPATH_RANGE: 199 case XPATH_LOCATIONSET: 200 xsltTransformError(tctxt, NULL, NULL, 201 "document() : XPointer does not select a node set: #%s\n", 202 fragment); 203 goto out_object; 204 } 205 206 valuePush(ctxt, resObj); 207 return; 208 209out_object: 210 xmlXPathFreeObject(resObj); 211 212out_fragment: 213 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 214} 215 216/** 217 * xsltDocumentFunction: 218 * @ctxt: the XPath Parser context 219 * @nargs: the number of arguments 220 * 221 * Implement the document() XSLT function 222 * node-set document(object, node-set?) 223 */ 224void 225xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) 226{ 227 xmlXPathObjectPtr obj, obj2 = NULL; 228 xmlChar *base = NULL, *URI; 229 230 231 if ((nargs < 1) || (nargs > 2)) { 232 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 233 "document() : invalid number of args %d\n", 234 nargs); 235 ctxt->error = XPATH_INVALID_ARITY; 236 return; 237 } 238 if (ctxt->value == NULL) { 239 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 240 "document() : invalid arg value\n"); 241 ctxt->error = XPATH_INVALID_TYPE; 242 return; 243 } 244 245 if (nargs == 2) { 246 if (ctxt->value->type != XPATH_NODESET) { 247 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 248 "document() : invalid arg expecting a nodeset\n"); 249 ctxt->error = XPATH_INVALID_TYPE; 250 return; 251 } 252 253 obj2 = valuePop(ctxt); 254 } 255 256 if (ctxt->value->type == XPATH_NODESET) { 257 int i; 258 xmlXPathObjectPtr newobj, ret; 259 260 obj = valuePop(ctxt); 261 ret = xmlXPathNewNodeSet(NULL); 262 263 if ((obj != NULL) && obj->nodesetval) { 264 for (i = 0; i < obj->nodesetval->nodeNr; i++) { 265 valuePush(ctxt, 266 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); 267 xmlXPathStringFunction(ctxt, 1); 268 if (nargs == 2) { 269 valuePush(ctxt, xmlXPathObjectCopy(obj2)); 270 } else { 271 valuePush(ctxt, 272 xmlXPathNewNodeSet(obj->nodesetval-> 273 nodeTab[i])); 274 } 275 xsltDocumentFunction(ctxt, 2); 276 newobj = valuePop(ctxt); 277 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, 278 newobj->nodesetval); 279 xmlXPathFreeObject(newobj); 280 } 281 } 282 283 if (obj != NULL) 284 xmlXPathFreeObject(obj); 285 if (obj2 != NULL) 286 xmlXPathFreeObject(obj2); 287 valuePush(ctxt, ret); 288 return; 289 } 290 /* 291 * Make sure it's converted to a string 292 */ 293 xmlXPathStringFunction(ctxt, 1); 294 if (ctxt->value->type != XPATH_STRING) { 295 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 296 "document() : invalid arg expecting a string\n"); 297 ctxt->error = XPATH_INVALID_TYPE; 298 if (obj2 != NULL) 299 xmlXPathFreeObject(obj2); 300 return; 301 } 302 obj = valuePop(ctxt); 303 if (obj->stringval == NULL) { 304 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 305 } else { 306 xsltTransformContextPtr tctxt; 307 tctxt = xsltXPathGetTransformContext(ctxt); 308 if ((obj2 != NULL) && (obj2->nodesetval != NULL) && 309 (obj2->nodesetval->nodeNr > 0) && 310 IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { 311 xmlNodePtr target; 312 313 target = obj2->nodesetval->nodeTab[0]; 314 if ((target->type == XML_ATTRIBUTE_NODE) || 315 (target->type == XML_PI_NODE)) { 316 target = ((xmlAttrPtr) target)->parent; 317 } 318 base = xmlNodeGetBase(target->doc, target); 319 } else { 320 if ((tctxt != NULL) && (tctxt->inst != NULL)) { 321 base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); 322 } else if ((tctxt != NULL) && (tctxt->style != NULL) && 323 (tctxt->style->doc != NULL)) { 324 base = xmlNodeGetBase(tctxt->style->doc, 325 (xmlNodePtr) tctxt->style->doc); 326 } 327 } 328 URI = xmlBuildURI(obj->stringval, base); 329 if (base != NULL) 330 xmlFree(base); 331 if (URI == NULL) { 332 if ((tctxt != NULL) && (tctxt->style != NULL) && 333 (tctxt->style->doc != NULL) && 334 (xmlStrEqual(URI, tctxt->style->doc->URL))) { 335 /* This selects the stylesheet's doc itself. */ 336 valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc)); 337 } else { 338 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 339 } 340 } else { 341 xsltDocumentFunctionLoadDocument( ctxt, URI ); 342 xmlFree(URI); 343 } 344 } 345 xmlXPathFreeObject(obj); 346 if (obj2 != NULL) 347 xmlXPathFreeObject(obj2); 348} 349 350/** 351 * xsltKeyFunction: 352 * @ctxt: the XPath Parser context 353 * @nargs: the number of arguments 354 * 355 * Implement the key() XSLT function 356 * node-set key(string, object) 357 */ 358void 359xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ 360 xmlXPathObjectPtr obj1, obj2; 361 362 if (nargs != 2) { 363 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 364 "key() : expects two arguments\n"); 365 ctxt->error = XPATH_INVALID_ARITY; 366 return; 367 } 368 369 /* 370 * Get the key's value. 371 */ 372 obj2 = valuePop(ctxt); 373 xmlXPathStringFunction(ctxt, 1); 374 if ((obj2 == NULL) || 375 (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 376 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 377 "key() : invalid arg expecting a string\n"); 378 ctxt->error = XPATH_INVALID_TYPE; 379 xmlXPathFreeObject(obj2); 380 381 return; 382 } 383 /* 384 * Get the key's name. 385 */ 386 obj1 = valuePop(ctxt); 387 388 if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { 389 int i; 390 xmlXPathObjectPtr newobj, ret; 391 392 ret = xmlXPathNewNodeSet(NULL); 393 394 if (obj2->nodesetval != NULL) { 395 for (i = 0; i < obj2->nodesetval->nodeNr; i++) { 396 valuePush(ctxt, xmlXPathObjectCopy(obj1)); 397 valuePush(ctxt, 398 xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i])); 399 xmlXPathStringFunction(ctxt, 1); 400 xsltKeyFunction(ctxt, 2); 401 newobj = valuePop(ctxt); 402 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, 403 newobj->nodesetval); 404 xmlXPathFreeObject(newobj); 405 } 406 } 407 valuePush(ctxt, ret); 408 } else { 409 xmlNodeSetPtr nodelist = NULL; 410 xmlChar *key = NULL, *value; 411 const xmlChar *keyURI; 412 xsltTransformContextPtr tctxt; 413 xmlChar *qname, *prefix; 414 xmlXPathContextPtr xpctxt = ctxt->context; 415 xmlNodePtr tmpNode = NULL; 416 xsltDocumentPtr oldDocInfo; 417 418 tctxt = xsltXPathGetTransformContext(ctxt); 419 420 oldDocInfo = tctxt->document; 421 422 if (xpctxt->node == NULL) { 423 xsltTransformError(tctxt, NULL, tctxt->inst, 424 "Internal error in xsltKeyFunction(): " 425 "The context node is not set on the XPath context.\n"); 426 tctxt->state = XSLT_STATE_STOPPED; 427 goto error; 428 } 429 /* 430 * Get the associated namespace URI if qualified name 431 */ 432 qname = obj1->stringval; 433 key = xmlSplitQName2(qname, &prefix); 434 if (key == NULL) { 435 key = xmlStrdup(obj1->stringval); 436 keyURI = NULL; 437 if (prefix != NULL) 438 xmlFree(prefix); 439 } else { 440 if (prefix != NULL) { 441 keyURI = xmlXPathNsLookup(xpctxt, prefix); 442 if (keyURI == NULL) { 443 xsltTransformError(tctxt, NULL, tctxt->inst, 444 "key() : prefix %s is not bound\n", prefix); 445 /* 446 * TODO: Shouldn't we stop here? 447 */ 448 } 449 xmlFree(prefix); 450 } else { 451 keyURI = NULL; 452 } 453 } 454 455 /* 456 * Force conversion of first arg to string 457 */ 458 valuePush(ctxt, obj2); 459 xmlXPathStringFunction(ctxt, 1); 460 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 461 xsltTransformError(tctxt, NULL, tctxt->inst, 462 "key() : invalid arg expecting a string\n"); 463 ctxt->error = XPATH_INVALID_TYPE; 464 goto error; 465 } 466 obj2 = valuePop(ctxt); 467 value = obj2->stringval; 468 469 /* 470 * We need to ensure that ctxt->document is available for 471 * xsltGetKey(). 472 * First find the relevant doc, which is the context node's 473 * owner doc; using context->doc is not safe, since 474 * the doc could have been acquired via the document() function, 475 * or the doc might be a Result Tree Fragment. 476 * FUTURE INFO: In XSLT 2.0 the key() function takes an additional 477 * argument indicating the doc to use. 478 */ 479 if (xpctxt->node->type == XML_NAMESPACE_DECL) { 480 /* 481 * REVISIT: This is a libxml hack! Check xpath.c for details. 482 * The XPath module sets the owner element of a ns-node on 483 * the ns->next field. 484 */ 485 if ((((xmlNsPtr) xpctxt->node)->next != NULL) && 486 (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE)) 487 { 488 tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next; 489 } 490 } else 491 tmpNode = xpctxt->node; 492 493 if ((tmpNode == NULL) || (tmpNode->doc == NULL)) { 494 xsltTransformError(tctxt, NULL, tctxt->inst, 495 "Internal error in xsltKeyFunction(): " 496 "Couldn't get the doc of the XPath context node.\n"); 497 goto error; 498 } 499 500 if ((tctxt->document == NULL) || 501 (tctxt->document->doc != tmpNode->doc)) 502 { 503 if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) { 504 /* 505 * This is a Result Tree Fragment. 506 */ 507 if (tmpNode->doc->_private == NULL) { 508 tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc); 509 if (tmpNode->doc->_private == NULL) 510 goto error; 511 } 512 tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private; 513 } else { 514 /* 515 * May be the initial source doc or a doc acquired via the 516 * document() function. 517 */ 518 tctxt->document = xsltFindDocument(tctxt, tmpNode->doc); 519 } 520 if (tctxt->document == NULL) { 521 xsltTransformError(tctxt, NULL, tctxt->inst, 522 "Internal error in xsltKeyFunction(): " 523 "Could not get the document info of a context doc.\n"); 524 tctxt->state = XSLT_STATE_STOPPED; 525 goto error; 526 } 527 } 528 /* 529 * Get/compute the key value. 530 */ 531 nodelist = xsltGetKey(tctxt, key, keyURI, value); 532 533error: 534 tctxt->document = oldDocInfo; 535 valuePush(ctxt, xmlXPathWrapNodeSet( 536 xmlXPathNodeSetMerge(NULL, nodelist))); 537 if (key != NULL) 538 xmlFree(key); 539 } 540 541 if (obj1 != NULL) 542 xmlXPathFreeObject(obj1); 543 if (obj2 != NULL) 544 xmlXPathFreeObject(obj2); 545} 546 547/** 548 * xsltUnparsedEntityURIFunction: 549 * @ctxt: the XPath Parser context 550 * @nargs: the number of arguments 551 * 552 * Implement the unparsed-entity-uri() XSLT function 553 * string unparsed-entity-uri(string) 554 */ 555void 556xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){ 557 xmlXPathObjectPtr obj; 558 xmlChar *str; 559 560 if ((nargs != 1) || (ctxt->value == NULL)) { 561 xsltGenericError(xsltGenericErrorContext, 562 "unparsed-entity-uri() : expects one string arg\n"); 563 ctxt->error = XPATH_INVALID_ARITY; 564 return; 565 } 566 obj = valuePop(ctxt); 567 if (obj->type != XPATH_STRING) { 568 obj = xmlXPathConvertString(obj); 569 } 570 571 str = obj->stringval; 572 if (str == NULL) { 573 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 574 } else { 575 xmlEntityPtr entity; 576 577 entity = xmlGetDocEntity(ctxt->context->doc, str); 578 if (entity == NULL) { 579 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 580 } else { 581 if (entity->URI != NULL) 582 valuePush(ctxt, xmlXPathNewString(entity->URI)); 583 else 584 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 585 } 586 } 587 xmlXPathFreeObject(obj); 588} 589 590/** 591 * xsltFormatNumberFunction: 592 * @ctxt: the XPath Parser context 593 * @nargs: the number of arguments 594 * 595 * Implement the format-number() XSLT function 596 * string format-number(number, string, string?) 597 */ 598void 599xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) 600{ 601 xmlXPathObjectPtr numberObj = NULL; 602 xmlXPathObjectPtr formatObj = NULL; 603 xmlXPathObjectPtr decimalObj = NULL; 604 xsltStylesheetPtr sheet; 605 xsltDecimalFormatPtr formatValues; 606 xmlChar *result; 607 xsltTransformContextPtr tctxt; 608 609 tctxt = xsltXPathGetTransformContext(ctxt); 610 if (tctxt == NULL) 611 return; 612 sheet = tctxt->style; 613 if (sheet == NULL) 614 return; 615 formatValues = sheet->decimalFormat; 616 617 switch (nargs) { 618 case 3: 619 CAST_TO_STRING; 620 decimalObj = valuePop(ctxt); 621 formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval); 622 if (formatValues == NULL) { 623 xsltTransformError(tctxt, NULL, NULL, 624 "format-number() : undeclared decimal format '%s'\n", 625 decimalObj->stringval); 626 } 627 /* Intentional fall-through */ 628 case 2: 629 CAST_TO_STRING; 630 formatObj = valuePop(ctxt); 631 CAST_TO_NUMBER; 632 numberObj = valuePop(ctxt); 633 break; 634 default: 635 XP_ERROR(XPATH_INVALID_ARITY); 636 } 637 638 if (formatValues != NULL) { 639 if (xsltFormatNumberConversion(formatValues, 640 formatObj->stringval, 641 numberObj->floatval, 642 &result) == XPATH_EXPRESSION_OK) { 643 valuePush(ctxt, xmlXPathNewString(result)); 644 xmlFree(result); 645 } 646 } 647 648 xmlXPathFreeObject(numberObj); 649 xmlXPathFreeObject(formatObj); 650 xmlXPathFreeObject(decimalObj); 651} 652 653/** 654 * xsltGenerateIdFunction: 655 * @ctxt: the XPath Parser context 656 * @nargs: the number of arguments 657 * 658 * Implement the generate-id() XSLT function 659 * string generate-id(node-set?) 660 */ 661void 662xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){ 663 static char base_address; 664 xmlNodePtr cur = NULL; 665 xmlXPathObjectPtr obj = NULL; 666 long val; 667 xmlChar str[30]; 668 xmlDocPtr doc; 669 670 if (nargs == 0) { 671 cur = ctxt->context->node; 672 } else if (nargs == 1) { 673 xmlNodeSetPtr nodelist; 674 int i, ret; 675 676 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) { 677 ctxt->error = XPATH_INVALID_TYPE; 678 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 679 "generate-id() : invalid arg expecting a node-set\n"); 680 return; 681 } 682 obj = valuePop(ctxt); 683 nodelist = obj->nodesetval; 684 if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) { 685 xmlXPathFreeObject(obj); 686 valuePush(ctxt, xmlXPathNewCString("")); 687 return; 688 } 689 cur = nodelist->nodeTab[0]; 690 for (i = 1;i < nodelist->nodeNr;i++) { 691 ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]); 692 if (ret == -1) 693 cur = nodelist->nodeTab[i]; 694 } 695 } else { 696 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 697 "generate-id() : invalid number of args %d\n", nargs); 698 ctxt->error = XPATH_INVALID_ARITY; 699 return; 700 } 701 /* 702 * Okay this is ugly but should work, use the NodePtr address 703 * to forge the ID 704 */ 705 if (cur->type != XML_NAMESPACE_DECL) 706 doc = cur->doc; 707 else { 708 xmlNsPtr ns = (xmlNsPtr) cur; 709 710 if (ns->context != NULL) 711 doc = ns->context; 712 else 713 doc = ctxt->context->doc; 714 715 } 716 717 if (obj) 718 xmlXPathFreeObject(obj); 719 720 val = (long)((char *)cur - (char *)&base_address); 721 if (val >= 0) { 722 sprintf((char *)str, "idp%ld", val); 723 } else { 724 sprintf((char *)str, "idm%ld", -val); 725 } 726 valuePush(ctxt, xmlXPathNewString(str)); 727} 728 729/** 730 * xsltSystemPropertyFunction: 731 * @ctxt: the XPath Parser context 732 * @nargs: the number of arguments 733 * 734 * Implement the system-property() XSLT function 735 * object system-property(string) 736 */ 737void 738xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){ 739 xmlXPathObjectPtr obj; 740 xmlChar *prefix, *name; 741 const xmlChar *nsURI = NULL; 742 743 if (nargs != 1) { 744 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 745 "system-property() : expects one string arg\n"); 746 ctxt->error = XPATH_INVALID_ARITY; 747 return; 748 } 749 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 750 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 751 "system-property() : invalid arg expecting a string\n"); 752 ctxt->error = XPATH_INVALID_TYPE; 753 return; 754 } 755 obj = valuePop(ctxt); 756 if (obj->stringval == NULL) { 757 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 758 } else { 759 name = xmlSplitQName2(obj->stringval, &prefix); 760 if (name == NULL) { 761 name = xmlStrdup(obj->stringval); 762 } else { 763 nsURI = xmlXPathNsLookup(ctxt->context, prefix); 764 if (nsURI == NULL) { 765 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 766 "system-property() : prefix %s is not bound\n", prefix); 767 } 768 } 769 770 if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) { 771#ifdef DOCBOOK_XSL_HACK 772 if (xmlStrEqual(name, (const xmlChar *)"vendor")) { 773 xsltStylesheetPtr sheet; 774 xsltTransformContextPtr tctxt; 775 776 tctxt = xsltXPathGetTransformContext(ctxt); 777 if ((tctxt != NULL) && (tctxt->inst != NULL) && 778 (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) && 779 (tctxt->inst->parent != NULL) && 780 (xmlStrEqual(tctxt->inst->parent->name, 781 BAD_CAST "template"))) 782 sheet = tctxt->style; 783 else 784 sheet = NULL; 785 if ((sheet != NULL) && (sheet->doc != NULL) && 786 (sheet->doc->URL != NULL) && 787 (xmlStrstr(sheet->doc->URL, 788 (const xmlChar *)"chunk") != NULL)) { 789 valuePush(ctxt, xmlXPathNewString( 790 (const xmlChar *)"libxslt (SAXON 6.2 compatible)")); 791 792 } else { 793 valuePush(ctxt, xmlXPathNewString( 794 (const xmlChar *)XSLT_DEFAULT_VENDOR)); 795 } 796 } else 797#else 798 if (xmlStrEqual(name, (const xmlChar *)"vendor")) { 799 valuePush(ctxt, xmlXPathNewString( 800 (const xmlChar *)XSLT_DEFAULT_VENDOR)); 801 } else 802#endif 803 if (xmlStrEqual(name, (const xmlChar *)"version")) { 804 valuePush(ctxt, xmlXPathNewString( 805 (const xmlChar *)XSLT_DEFAULT_VERSION)); 806 } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) { 807 valuePush(ctxt, xmlXPathNewString( 808 (const xmlChar *)XSLT_DEFAULT_URL)); 809 } else { 810 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 811 } 812 } else { 813 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 814 } 815 if (name != NULL) 816 xmlFree(name); 817 if (prefix != NULL) 818 xmlFree(prefix); 819 } 820 xmlXPathFreeObject(obj); 821} 822 823/** 824 * xsltElementAvailableFunction: 825 * @ctxt: the XPath Parser context 826 * @nargs: the number of arguments 827 * 828 * Implement the element-available() XSLT function 829 * boolean element-available(string) 830 */ 831void 832xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ 833 xmlXPathObjectPtr obj; 834 xmlChar *prefix, *name; 835 const xmlChar *nsURI = NULL; 836 xsltTransformContextPtr tctxt; 837 838 if (nargs != 1) { 839 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 840 "element-available() : expects one string arg\n"); 841 ctxt->error = XPATH_INVALID_ARITY; 842 return; 843 } 844 xmlXPathStringFunction(ctxt, 1); 845 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 846 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 847 "element-available() : invalid arg expecting a string\n"); 848 ctxt->error = XPATH_INVALID_TYPE; 849 return; 850 } 851 obj = valuePop(ctxt); 852 tctxt = xsltXPathGetTransformContext(ctxt); 853 if (tctxt == NULL) { 854 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 855 "element-available() : internal error tctxt == NULL\n"); 856 xmlXPathFreeObject(obj); 857 valuePush(ctxt, xmlXPathNewBoolean(0)); 858 return; 859 } 860 861 862 name = xmlSplitQName2(obj->stringval, &prefix); 863 if (name == NULL) { 864 xmlNsPtr ns; 865 866 name = xmlStrdup(obj->stringval); 867 ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL); 868 if (ns != NULL) nsURI = xmlStrdup(ns->href); 869 } else { 870 nsURI = xmlXPathNsLookup(ctxt->context, prefix); 871 if (nsURI == NULL) { 872 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 873 "element-available() : prefix %s is not bound\n", prefix); 874 } 875 } 876 877 if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) { 878 valuePush(ctxt, xmlXPathNewBoolean(1)); 879 } else { 880 valuePush(ctxt, xmlXPathNewBoolean(0)); 881 } 882 883 xmlXPathFreeObject(obj); 884 if (name != NULL) 885 xmlFree(name); 886 if (prefix != NULL) 887 xmlFree(prefix); 888} 889 890/** 891 * xsltFunctionAvailableFunction: 892 * @ctxt: the XPath Parser context 893 * @nargs: the number of arguments 894 * 895 * Implement the function-available() XSLT function 896 * boolean function-available(string) 897 */ 898void 899xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ 900 xmlXPathObjectPtr obj; 901 xmlChar *prefix, *name; 902 const xmlChar *nsURI = NULL; 903 904 if (nargs != 1) { 905 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 906 "function-available() : expects one string arg\n"); 907 ctxt->error = XPATH_INVALID_ARITY; 908 return; 909 } 910 xmlXPathStringFunction(ctxt, 1); 911 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 912 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 913 "function-available() : invalid arg expecting a string\n"); 914 ctxt->error = XPATH_INVALID_TYPE; 915 return; 916 } 917 obj = valuePop(ctxt); 918 919 name = xmlSplitQName2(obj->stringval, &prefix); 920 if (name == NULL) { 921 name = xmlStrdup(obj->stringval); 922 } else { 923 nsURI = xmlXPathNsLookup(ctxt->context, prefix); 924 if (nsURI == NULL) { 925 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 926 "function-available() : prefix %s is not bound\n", prefix); 927 } 928 } 929 930 if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) { 931 valuePush(ctxt, xmlXPathNewBoolean(1)); 932 } else { 933 valuePush(ctxt, xmlXPathNewBoolean(0)); 934 } 935 936 xmlXPathFreeObject(obj); 937 if (name != NULL) 938 xmlFree(name); 939 if (prefix != NULL) 940 xmlFree(prefix); 941} 942 943/** 944 * xsltCurrentFunction: 945 * @ctxt: the XPath Parser context 946 * @nargs: the number of arguments 947 * 948 * Implement the current() XSLT function 949 * node-set current() 950 */ 951static void 952xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){ 953 xsltTransformContextPtr tctxt; 954 955 if (nargs != 0) { 956 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 957 "current() : function uses no argument\n"); 958 ctxt->error = XPATH_INVALID_ARITY; 959 return; 960 } 961 tctxt = xsltXPathGetTransformContext(ctxt); 962 if (tctxt == NULL) { 963 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 964 "current() : internal error tctxt == NULL\n"); 965 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 966 } else { 967 valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */ 968 } 969} 970 971/************************************************************************ 972 * * 973 * Registration of XSLT and libxslt functions * 974 * * 975 ************************************************************************/ 976 977/** 978 * xsltRegisterAllFunctions: 979 * @ctxt: the XPath context 980 * 981 * Registers all default XSLT functions in this context 982 */ 983void 984xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) 985{ 986 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current", 987 xsltCurrentFunction); 988 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document", 989 xsltDocumentFunction); 990 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction); 991 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri", 992 xsltUnparsedEntityURIFunction); 993 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number", 994 xsltFormatNumberFunction); 995 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id", 996 xsltGenerateIdFunction); 997 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property", 998 xsltSystemPropertyFunction); 999 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available", 1000 xsltElementAvailableFunction); 1001 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available", 1002 xsltFunctionAvailableFunction); 1003} 1004