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 #include "precomp.h" 14 15 #include <libxml/xpointer.h> 16 17 #ifdef WITH_XSLT_DEBUG 18 #define WITH_XSLT_DEBUG_FUNCTION 19 #endif 20 21 /* 22 * Some versions of DocBook XSL use the vendor string to detect 23 * supporting chunking, this is a workaround to be considered 24 * in the list of decent XSLT processors <grin/> 25 */ 26 #define DOCBOOK_XSL_HACK 27 28 /** 29 * xsltXPathFunctionLookup: 30 * @vctxt: a void * but the XSLT transformation context actually 31 * @name: the function name 32 * @ns_uri: the function namespace URI 33 * 34 * This is the entry point when a function is needed by the XPath 35 * interpretor. 36 * 37 * Returns the callback function or NULL if not found 38 */ 39 xmlXPathFunction 40 xsltXPathFunctionLookup (void *vctxt, 41 const xmlChar *name, const xmlChar *ns_uri) { 42 xmlXPathContextPtr ctxt = (xmlXPathContextPtr) vctxt; 43 xmlXPathFunction ret; 44 45 if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL)) 46 return (NULL); 47 48 #ifdef WITH_XSLT_DEBUG_FUNCTION 49 xsltGenericDebug(xsltGenericDebugContext, 50 "Lookup function {%s}%s\n", ns_uri, name); 51 #endif 52 53 /* give priority to context-level functions */ 54 /* 55 ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); 56 */ 57 XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri); 58 59 if (ret == NULL) 60 ret = xsltExtModuleFunctionLookup(name, ns_uri); 61 62 #ifdef WITH_XSLT_DEBUG_FUNCTION 63 if (ret != NULL) 64 xsltGenericDebug(xsltGenericDebugContext, 65 "found function %s\n", name); 66 #endif 67 return(ret); 68 } 69 70 71 /************************************************************************ 72 * * 73 * Module interfaces * 74 * * 75 ************************************************************************/ 76 77 static void 78 xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI) 79 { 80 xsltTransformContextPtr tctxt; 81 xmlURIPtr uri; 82 xmlChar *fragment; 83 xsltDocumentPtr idoc; /* document info */ 84 xmlDocPtr doc; 85 xmlXPathContextPtr xptrctxt = NULL; 86 xmlXPathObjectPtr resObj = NULL; 87 88 tctxt = xsltXPathGetTransformContext(ctxt); 89 if (tctxt == NULL) { 90 xsltTransformError(NULL, NULL, NULL, 91 "document() : internal error tctxt == NULL\n"); 92 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 93 return; 94 } 95 96 uri = xmlParseURI((const char *) URI); 97 if (uri == NULL) { 98 xsltTransformError(tctxt, NULL, NULL, 99 "document() : failed to parse URI\n"); 100 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 101 return; 102 } 103 104 /* 105 * check for and remove fragment identifier 106 */ 107 fragment = (xmlChar *)uri->fragment; 108 if (fragment != NULL) { 109 xmlChar *newURI; 110 uri->fragment = NULL; 111 newURI = xmlSaveUri(uri); 112 idoc = xsltLoadDocument(tctxt, newURI); 113 xmlFree(newURI); 114 } else 115 idoc = xsltLoadDocument(tctxt, URI); 116 xmlFreeURI(uri); 117 118 if (idoc == NULL) { 119 if ((URI == NULL) || 120 (URI[0] == '#') || 121 ((tctxt->style->doc != NULL) && 122 (xmlStrEqual(tctxt->style->doc->URL, URI)))) 123 { 124 /* 125 * This selects the stylesheet's doc itself. 126 */ 127 doc = tctxt->style->doc; 128 } else { 129 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 130 131 if (fragment != NULL) 132 xmlFree(fragment); 133 134 return; 135 } 136 } else 137 doc = idoc->doc; 138 139 if (fragment == NULL) { 140 valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc)); 141 return; 142 } 143 144 /* use XPointer of HTML location for fragment ID */ 145 #ifdef LIBXML_XPTR_ENABLED 146 xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); 147 if (xptrctxt == NULL) { 148 xsltTransformError(tctxt, NULL, NULL, 149 "document() : internal error xptrctxt == NULL\n"); 150 goto out_fragment; 151 } 152 153 resObj = xmlXPtrEval(fragment, xptrctxt); 154 xmlXPathFreeContext(xptrctxt); 155 #endif 156 157 if (resObj == NULL) 158 goto out_fragment; 159 160 switch (resObj->type) { 161 case XPATH_NODESET: 162 break; 163 case XPATH_UNDEFINED: 164 case XPATH_BOOLEAN: 165 case XPATH_NUMBER: 166 case XPATH_STRING: 167 case XPATH_POINT: 168 case XPATH_USERS: 169 case XPATH_XSLT_TREE: 170 case XPATH_RANGE: 171 case XPATH_LOCATIONSET: 172 xsltTransformError(tctxt, NULL, NULL, 173 "document() : XPointer does not select a node set: #%s\n", 174 fragment); 175 goto out_object; 176 } 177 178 valuePush(ctxt, resObj); 179 xmlFree(fragment); 180 return; 181 182 out_object: 183 xmlXPathFreeObject(resObj); 184 185 out_fragment: 186 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 187 xmlFree(fragment); 188 } 189 190 /** 191 * xsltDocumentFunction: 192 * @ctxt: the XPath Parser context 193 * @nargs: the number of arguments 194 * 195 * Implement the document() XSLT function 196 * node-set document(object, node-set?) 197 */ 198 void 199 xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) 200 { 201 xmlXPathObjectPtr obj, obj2 = NULL; 202 xmlChar *base = NULL, *URI; 203 204 205 if ((nargs < 1) || (nargs > 2)) { 206 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 207 "document() : invalid number of args %d\n", 208 nargs); 209 ctxt->error = XPATH_INVALID_ARITY; 210 return; 211 } 212 if (ctxt->value == NULL) { 213 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 214 "document() : invalid arg value\n"); 215 ctxt->error = XPATH_INVALID_TYPE; 216 return; 217 } 218 219 if (nargs == 2) { 220 if (ctxt->value->type != XPATH_NODESET) { 221 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 222 "document() : invalid arg expecting a nodeset\n"); 223 ctxt->error = XPATH_INVALID_TYPE; 224 return; 225 } 226 227 obj2 = valuePop(ctxt); 228 } 229 230 if (ctxt->value->type == XPATH_NODESET) { 231 int i; 232 xmlXPathObjectPtr newobj, ret; 233 234 obj = valuePop(ctxt); 235 ret = xmlXPathNewNodeSet(NULL); 236 237 if ((obj != NULL) && obj->nodesetval) { 238 for (i = 0; i < obj->nodesetval->nodeNr; i++) { 239 valuePush(ctxt, 240 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); 241 xmlXPathStringFunction(ctxt, 1); 242 if (nargs == 2) { 243 valuePush(ctxt, xmlXPathObjectCopy(obj2)); 244 } else { 245 valuePush(ctxt, 246 xmlXPathNewNodeSet(obj->nodesetval-> 247 nodeTab[i])); 248 } 249 xsltDocumentFunction(ctxt, 2); 250 newobj = valuePop(ctxt); 251 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, 252 newobj->nodesetval); 253 xmlXPathFreeObject(newobj); 254 } 255 } 256 257 if (obj != NULL) 258 xmlXPathFreeObject(obj); 259 if (obj2 != NULL) 260 xmlXPathFreeObject(obj2); 261 valuePush(ctxt, ret); 262 return; 263 } 264 /* 265 * Make sure it's converted to a string 266 */ 267 xmlXPathStringFunction(ctxt, 1); 268 if (ctxt->value->type != XPATH_STRING) { 269 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 270 "document() : invalid arg expecting a string\n"); 271 ctxt->error = XPATH_INVALID_TYPE; 272 if (obj2 != NULL) 273 xmlXPathFreeObject(obj2); 274 return; 275 } 276 obj = valuePop(ctxt); 277 if (obj->stringval == NULL) { 278 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 279 } else { 280 xsltTransformContextPtr tctxt; 281 tctxt = xsltXPathGetTransformContext(ctxt); 282 if ((obj2 != NULL) && (obj2->nodesetval != NULL) && 283 (obj2->nodesetval->nodeNr > 0) && 284 IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { 285 xmlNodePtr target; 286 287 target = obj2->nodesetval->nodeTab[0]; 288 if ((target->type == XML_ATTRIBUTE_NODE) || 289 (target->type == XML_PI_NODE)) { 290 target = ((xmlAttrPtr) target)->parent; 291 } 292 base = xmlNodeGetBase(target->doc, target); 293 } else { 294 if ((tctxt != NULL) && (tctxt->inst != NULL)) { 295 base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); 296 } else if ((tctxt != NULL) && (tctxt->style != NULL) && 297 (tctxt->style->doc != NULL)) { 298 base = xmlNodeGetBase(tctxt->style->doc, 299 (xmlNodePtr) tctxt->style->doc); 300 } 301 } 302 URI = xmlBuildURI(obj->stringval, base); 303 if (base != NULL) 304 xmlFree(base); 305 if (URI == NULL) { 306 if ((tctxt != NULL) && (tctxt->style != NULL) && 307 (tctxt->style->doc != NULL) && 308 (xmlStrEqual(URI, tctxt->style->doc->URL))) { 309 /* This selects the stylesheet's doc itself. */ 310 valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc)); 311 } else { 312 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 313 } 314 } else { 315 xsltDocumentFunctionLoadDocument( ctxt, URI ); 316 xmlFree(URI); 317 } 318 } 319 xmlXPathFreeObject(obj); 320 if (obj2 != NULL) 321 xmlXPathFreeObject(obj2); 322 } 323 324 /** 325 * xsltKeyFunction: 326 * @ctxt: the XPath Parser context 327 * @nargs: the number of arguments 328 * 329 * Implement the key() XSLT function 330 * node-set key(string, object) 331 */ 332 void 333 xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ 334 xmlXPathObjectPtr obj1, obj2; 335 336 if (nargs != 2) { 337 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 338 "key() : expects two arguments\n"); 339 ctxt->error = XPATH_INVALID_ARITY; 340 return; 341 } 342 343 /* 344 * Get the key's value. 345 */ 346 obj2 = valuePop(ctxt); 347 xmlXPathStringFunction(ctxt, 1); 348 if ((obj2 == NULL) || 349 (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 350 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 351 "key() : invalid arg expecting a string\n"); 352 ctxt->error = XPATH_INVALID_TYPE; 353 xmlXPathFreeObject(obj2); 354 355 return; 356 } 357 /* 358 * Get the key's name. 359 */ 360 obj1 = valuePop(ctxt); 361 362 if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { 363 int i; 364 xmlXPathObjectPtr newobj, ret; 365 366 ret = xmlXPathNewNodeSet(NULL); 367 368 if (obj2->nodesetval != NULL) { 369 for (i = 0; i < obj2->nodesetval->nodeNr; i++) { 370 valuePush(ctxt, xmlXPathObjectCopy(obj1)); 371 valuePush(ctxt, 372 xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i])); 373 xmlXPathStringFunction(ctxt, 1); 374 xsltKeyFunction(ctxt, 2); 375 newobj = valuePop(ctxt); 376 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, 377 newobj->nodesetval); 378 xmlXPathFreeObject(newobj); 379 } 380 } 381 valuePush(ctxt, ret); 382 } else { 383 xmlNodeSetPtr nodelist = NULL; 384 xmlChar *key = NULL, *value; 385 const xmlChar *keyURI; 386 xsltTransformContextPtr tctxt; 387 xmlChar *qname, *prefix; 388 xmlXPathContextPtr xpctxt = ctxt->context; 389 xmlNodePtr tmpNode = NULL; 390 xsltDocumentPtr oldDocInfo; 391 392 tctxt = xsltXPathGetTransformContext(ctxt); 393 394 oldDocInfo = tctxt->document; 395 396 if (xpctxt->node == NULL) { 397 xsltTransformError(tctxt, NULL, tctxt->inst, 398 "Internal error in xsltKeyFunction(): " 399 "The context node is not set on the XPath context.\n"); 400 tctxt->state = XSLT_STATE_STOPPED; 401 goto error; 402 } 403 /* 404 * Get the associated namespace URI if qualified name 405 */ 406 qname = obj1->stringval; 407 key = xmlSplitQName2(qname, &prefix); 408 if (key == NULL) { 409 key = xmlStrdup(obj1->stringval); 410 keyURI = NULL; 411 if (prefix != NULL) 412 xmlFree(prefix); 413 } else { 414 if (prefix != NULL) { 415 keyURI = xmlXPathNsLookup(xpctxt, prefix); 416 if (keyURI == NULL) { 417 xsltTransformError(tctxt, NULL, tctxt->inst, 418 "key() : prefix %s is not bound\n", prefix); 419 /* 420 * TODO: Shouldn't we stop here? 421 */ 422 } 423 xmlFree(prefix); 424 } else { 425 keyURI = NULL; 426 } 427 } 428 429 /* 430 * Force conversion of first arg to string 431 */ 432 valuePush(ctxt, obj2); 433 xmlXPathStringFunction(ctxt, 1); 434 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 435 xsltTransformError(tctxt, NULL, tctxt->inst, 436 "key() : invalid arg expecting a string\n"); 437 ctxt->error = XPATH_INVALID_TYPE; 438 goto error; 439 } 440 obj2 = valuePop(ctxt); 441 value = obj2->stringval; 442 443 /* 444 * We need to ensure that ctxt->document is available for 445 * xsltGetKey(). 446 * First find the relevant doc, which is the context node's 447 * owner doc; using context->doc is not safe, since 448 * the doc could have been acquired via the document() function, 449 * or the doc might be a Result Tree Fragment. 450 * FUTURE INFO: In XSLT 2.0 the key() function takes an additional 451 * argument indicating the doc to use. 452 */ 453 if (xpctxt->node->type == XML_NAMESPACE_DECL) { 454 /* 455 * REVISIT: This is a libxml hack! Check xpath.c for details. 456 * The XPath module sets the owner element of a ns-node on 457 * the ns->next field. 458 */ 459 if ((((xmlNsPtr) xpctxt->node)->next != NULL) && 460 (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE)) 461 { 462 tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next; 463 } 464 } else 465 tmpNode = xpctxt->node; 466 467 if ((tmpNode == NULL) || (tmpNode->doc == NULL)) { 468 xsltTransformError(tctxt, NULL, tctxt->inst, 469 "Internal error in xsltKeyFunction(): " 470 "Couldn't get the doc of the XPath context node.\n"); 471 goto error; 472 } 473 474 if ((tctxt->document == NULL) || 475 (tctxt->document->doc != tmpNode->doc)) 476 { 477 if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) { 478 /* 479 * This is a Result Tree Fragment. 480 */ 481 if (tmpNode->doc->_private == NULL) { 482 tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc); 483 if (tmpNode->doc->_private == NULL) 484 goto error; 485 } 486 tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private; 487 } else { 488 /* 489 * May be the initial source doc or a doc acquired via the 490 * document() function. 491 */ 492 tctxt->document = xsltFindDocument(tctxt, tmpNode->doc); 493 } 494 if (tctxt->document == NULL) { 495 xsltTransformError(tctxt, NULL, tctxt->inst, 496 "Internal error in xsltKeyFunction(): " 497 "Could not get the document info of a context doc.\n"); 498 tctxt->state = XSLT_STATE_STOPPED; 499 goto error; 500 } 501 } 502 /* 503 * Get/compute the key value. 504 */ 505 nodelist = xsltGetKey(tctxt, key, keyURI, value); 506 507 error: 508 tctxt->document = oldDocInfo; 509 valuePush(ctxt, xmlXPathWrapNodeSet( 510 xmlXPathNodeSetMerge(NULL, nodelist))); 511 if (key != NULL) 512 xmlFree(key); 513 } 514 515 if (obj1 != NULL) 516 xmlXPathFreeObject(obj1); 517 if (obj2 != NULL) 518 xmlXPathFreeObject(obj2); 519 } 520 521 /** 522 * xsltUnparsedEntityURIFunction: 523 * @ctxt: the XPath Parser context 524 * @nargs: the number of arguments 525 * 526 * Implement the unparsed-entity-uri() XSLT function 527 * string unparsed-entity-uri(string) 528 */ 529 void 530 xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){ 531 xmlXPathObjectPtr obj; 532 xmlChar *str; 533 534 if ((nargs != 1) || (ctxt->value == NULL)) { 535 xsltGenericError(xsltGenericErrorContext, 536 "unparsed-entity-uri() : expects one string arg\n"); 537 ctxt->error = XPATH_INVALID_ARITY; 538 return; 539 } 540 obj = valuePop(ctxt); 541 if (obj->type != XPATH_STRING) { 542 obj = xmlXPathConvertString(obj); 543 } 544 545 str = obj->stringval; 546 if (str == NULL) { 547 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 548 } else { 549 xmlEntityPtr entity; 550 551 entity = xmlGetDocEntity(ctxt->context->doc, str); 552 if (entity == NULL) { 553 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 554 } else { 555 if (entity->URI != NULL) 556 valuePush(ctxt, xmlXPathNewString(entity->URI)); 557 else 558 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 559 } 560 } 561 xmlXPathFreeObject(obj); 562 } 563 564 /** 565 * xsltFormatNumberFunction: 566 * @ctxt: the XPath Parser context 567 * @nargs: the number of arguments 568 * 569 * Implement the format-number() XSLT function 570 * string format-number(number, string, string?) 571 */ 572 void 573 xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) 574 { 575 xmlXPathObjectPtr numberObj = NULL; 576 xmlXPathObjectPtr formatObj = NULL; 577 xmlXPathObjectPtr decimalObj = NULL; 578 xsltStylesheetPtr sheet; 579 xsltDecimalFormatPtr formatValues = NULL; 580 xmlChar *result; 581 const xmlChar *ncname; 582 const xmlChar *prefix = NULL; 583 const xmlChar *nsUri = NULL; 584 xsltTransformContextPtr tctxt; 585 586 tctxt = xsltXPathGetTransformContext(ctxt); 587 if ((tctxt == NULL) || (tctxt->inst == NULL)) 588 return; 589 sheet = tctxt->style; 590 if (sheet == NULL) 591 return; 592 formatValues = sheet->decimalFormat; 593 594 switch (nargs) { 595 case 3: 596 CAST_TO_STRING; 597 decimalObj = valuePop(ctxt); 598 ncname = xsltSplitQName(sheet->dict, decimalObj->stringval, &prefix); 599 if (prefix != NULL) { 600 xmlNsPtr ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, prefix); 601 if (ns == NULL) { 602 xsltTransformError(tctxt, NULL, NULL, 603 "format-number : No namespace found for QName '%s:%s'\n", 604 prefix, ncname); 605 sheet->errors++; 606 ncname = NULL; 607 } 608 else { 609 nsUri = ns->href; 610 } 611 } 612 if (ncname != NULL) { 613 formatValues = xsltDecimalFormatGetByQName(sheet, nsUri, ncname); 614 } 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 static char base_address; 657 xmlNodePtr cur = NULL; 658 xmlXPathObjectPtr obj = NULL; 659 long val; 660 xmlChar str[30]; 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 if (obj) 695 xmlXPathFreeObject(obj); 696 697 val = (long)((char *)cur - (char *)&base_address); 698 if (val >= 0) { 699 snprintf((char *)str, sizeof(str), "idp%ld", val); 700 } else { 701 snprintf((char *)str, sizeof(str), "idm%ld", -val); 702 } 703 valuePush(ctxt, xmlXPathNewString(str)); 704 } 705 706 /** 707 * xsltSystemPropertyFunction: 708 * @ctxt: the XPath Parser context 709 * @nargs: the number of arguments 710 * 711 * Implement the system-property() XSLT function 712 * object system-property(string) 713 */ 714 void 715 xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){ 716 xmlXPathObjectPtr obj; 717 xmlChar *prefix, *name; 718 const xmlChar *nsURI = NULL; 719 720 if (nargs != 1) { 721 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 722 "system-property() : expects one string arg\n"); 723 ctxt->error = XPATH_INVALID_ARITY; 724 return; 725 } 726 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 727 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 728 "system-property() : invalid arg expecting a string\n"); 729 ctxt->error = XPATH_INVALID_TYPE; 730 return; 731 } 732 obj = valuePop(ctxt); 733 if (obj->stringval == NULL) { 734 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 735 } else { 736 name = xmlSplitQName2(obj->stringval, &prefix); 737 if (name == NULL) { 738 name = xmlStrdup(obj->stringval); 739 } else { 740 nsURI = xmlXPathNsLookup(ctxt->context, prefix); 741 if (nsURI == NULL) { 742 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 743 "system-property() : prefix %s is not bound\n", prefix); 744 } 745 } 746 747 if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) { 748 #ifdef DOCBOOK_XSL_HACK 749 if (xmlStrEqual(name, (const xmlChar *)"vendor")) { 750 xsltStylesheetPtr sheet; 751 xsltTransformContextPtr tctxt; 752 753 tctxt = xsltXPathGetTransformContext(ctxt); 754 if ((tctxt != NULL) && (tctxt->inst != NULL) && 755 (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) && 756 (tctxt->inst->parent != NULL) && 757 (xmlStrEqual(tctxt->inst->parent->name, 758 BAD_CAST "template"))) 759 sheet = tctxt->style; 760 else 761 sheet = NULL; 762 if ((sheet != NULL) && (sheet->doc != NULL) && 763 (sheet->doc->URL != NULL) && 764 (xmlStrstr(sheet->doc->URL, 765 (const xmlChar *)"chunk") != NULL)) { 766 valuePush(ctxt, xmlXPathNewString( 767 (const xmlChar *)"libxslt (SAXON 6.2 compatible)")); 768 769 } else { 770 valuePush(ctxt, xmlXPathNewString( 771 (const xmlChar *)XSLT_DEFAULT_VENDOR)); 772 } 773 } else 774 #else 775 if (xmlStrEqual(name, (const xmlChar *)"vendor")) { 776 valuePush(ctxt, xmlXPathNewString( 777 (const xmlChar *)XSLT_DEFAULT_VENDOR)); 778 } else 779 #endif 780 if (xmlStrEqual(name, (const xmlChar *)"version")) { 781 valuePush(ctxt, xmlXPathNewString( 782 (const xmlChar *)XSLT_DEFAULT_VERSION)); 783 } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) { 784 valuePush(ctxt, xmlXPathNewString( 785 (const xmlChar *)XSLT_DEFAULT_URL)); 786 } else { 787 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 788 } 789 } else { 790 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); 791 } 792 if (name != NULL) 793 xmlFree(name); 794 if (prefix != NULL) 795 xmlFree(prefix); 796 } 797 xmlXPathFreeObject(obj); 798 } 799 800 /** 801 * xsltElementAvailableFunction: 802 * @ctxt: the XPath Parser context 803 * @nargs: the number of arguments 804 * 805 * Implement the element-available() XSLT function 806 * boolean element-available(string) 807 */ 808 void 809 xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ 810 xmlXPathObjectPtr obj; 811 xmlChar *prefix, *name; 812 const xmlChar *nsURI = NULL; 813 xsltTransformContextPtr tctxt; 814 815 if (nargs != 1) { 816 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 817 "element-available() : expects one string arg\n"); 818 ctxt->error = XPATH_INVALID_ARITY; 819 return; 820 } 821 xmlXPathStringFunction(ctxt, 1); 822 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 823 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 824 "element-available() : invalid arg expecting a string\n"); 825 ctxt->error = XPATH_INVALID_TYPE; 826 return; 827 } 828 obj = valuePop(ctxt); 829 tctxt = xsltXPathGetTransformContext(ctxt); 830 if ((tctxt == NULL) || (tctxt->inst == NULL)) { 831 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 832 "element-available() : internal error tctxt == NULL\n"); 833 xmlXPathFreeObject(obj); 834 valuePush(ctxt, xmlXPathNewBoolean(0)); 835 return; 836 } 837 838 839 name = xmlSplitQName2(obj->stringval, &prefix); 840 if (name == NULL) { 841 xmlNsPtr ns; 842 843 name = xmlStrdup(obj->stringval); 844 ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL); 845 if (ns != NULL) nsURI = ns->href; 846 } else { 847 nsURI = xmlXPathNsLookup(ctxt->context, prefix); 848 if (nsURI == NULL) { 849 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 850 "element-available() : prefix %s is not bound\n", prefix); 851 } 852 } 853 854 if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) { 855 valuePush(ctxt, xmlXPathNewBoolean(1)); 856 } else { 857 valuePush(ctxt, xmlXPathNewBoolean(0)); 858 } 859 860 xmlXPathFreeObject(obj); 861 if (name != NULL) 862 xmlFree(name); 863 if (prefix != NULL) 864 xmlFree(prefix); 865 } 866 867 /** 868 * xsltFunctionAvailableFunction: 869 * @ctxt: the XPath Parser context 870 * @nargs: the number of arguments 871 * 872 * Implement the function-available() XSLT function 873 * boolean function-available(string) 874 */ 875 void 876 xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ 877 xmlXPathObjectPtr obj; 878 xmlChar *prefix, *name; 879 const xmlChar *nsURI = NULL; 880 881 if (nargs != 1) { 882 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 883 "function-available() : expects one string arg\n"); 884 ctxt->error = XPATH_INVALID_ARITY; 885 return; 886 } 887 xmlXPathStringFunction(ctxt, 1); 888 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { 889 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 890 "function-available() : invalid arg expecting a string\n"); 891 ctxt->error = XPATH_INVALID_TYPE; 892 return; 893 } 894 obj = valuePop(ctxt); 895 896 name = xmlSplitQName2(obj->stringval, &prefix); 897 if (name == NULL) { 898 name = xmlStrdup(obj->stringval); 899 } else { 900 nsURI = xmlXPathNsLookup(ctxt->context, prefix); 901 if (nsURI == NULL) { 902 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 903 "function-available() : prefix %s is not bound\n", prefix); 904 } 905 } 906 907 if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) { 908 valuePush(ctxt, xmlXPathNewBoolean(1)); 909 } else { 910 valuePush(ctxt, xmlXPathNewBoolean(0)); 911 } 912 913 xmlXPathFreeObject(obj); 914 if (name != NULL) 915 xmlFree(name); 916 if (prefix != NULL) 917 xmlFree(prefix); 918 } 919 920 /** 921 * xsltCurrentFunction: 922 * @ctxt: the XPath Parser context 923 * @nargs: the number of arguments 924 * 925 * Implement the current() XSLT function 926 * node-set current() 927 */ 928 static void 929 xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){ 930 xsltTransformContextPtr tctxt; 931 932 if (nargs != 0) { 933 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 934 "current() : function uses no argument\n"); 935 ctxt->error = XPATH_INVALID_ARITY; 936 return; 937 } 938 tctxt = xsltXPathGetTransformContext(ctxt); 939 if (tctxt == NULL) { 940 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 941 "current() : internal error tctxt == NULL\n"); 942 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 943 } else { 944 valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */ 945 } 946 } 947 948 /************************************************************************ 949 * * 950 * Registration of XSLT and libxslt functions * 951 * * 952 ************************************************************************/ 953 954 /** 955 * xsltRegisterAllFunctions: 956 * @ctxt: the XPath context 957 * 958 * Registers all default XSLT functions in this context 959 */ 960 void 961 xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) 962 { 963 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current", 964 xsltCurrentFunction); 965 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document", 966 xsltDocumentFunction); 967 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction); 968 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri", 969 xsltUnparsedEntityURIFunction); 970 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number", 971 xsltFormatNumberFunction); 972 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id", 973 xsltGenerateIdFunction); 974 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property", 975 xsltSystemPropertyFunction); 976 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available", 977 xsltElementAvailableFunction); 978 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available", 979 xsltFunctionAvailableFunction); 980 } 981