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