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