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