2 * functions.c: Implementation of the XSLT extra functions
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
10 * Bjorn Reese <breese@users.sourceforge.net> for number formatting
18 #ifdef HAVE_SYS_TYPES_H
19 #include <sys/types.h>
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>
36 #include "xsltInternals.h"
37 #include "xsltutils.h"
38 #include "functions.h"
39 #include "extensions.h"
40 #include "numbersInternals.h"
42 #include "documents.h"
44 #ifdef WITH_XSLT_DEBUG
45 #define WITH_XSLT_DEBUG_FUNCTION
49 * Some versions of DocBook XSL use the vendor string to detect
50 * supporting chunking, this is a workaround to be considered
51 * in the list of decent XSLT processors <grin/>
53 #define DOCBOOK_XSL_HACK
56 * xsltXPathFunctionLookup:
57 * @ctxt: a void * but the XSLT transformation context actually
58 * @name: the function name
59 * @ns_uri: the function namespace URI
61 * This is the entry point when a function is needed by the XPath
64 * Returns the callback function or NULL if not found
67 xsltXPathFunctionLookup (xmlXPathContextPtr ctxt,
68 const xmlChar *name, const xmlChar *ns_uri) {
71 if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL))
74 #ifdef WITH_XSLT_DEBUG_FUNCTION
75 xsltGenericDebug(xsltGenericDebugContext,
76 "Lookup function {%s}%s\n", ns_uri, name);
79 /* give priority to context-level functions */
80 ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri);
83 ret = xsltExtModuleFunctionLookup(name, ns_uri);
85 #ifdef WITH_XSLT_DEBUG_FUNCTION
87 xsltGenericDebug(xsltGenericDebugContext,
88 "found function %s\n", name);
94 /************************************************************************
98 ************************************************************************/
101 * xsltDocumentFunction:
102 * @ctxt: the XPath Parser context
103 * @nargs: the number of arguments
105 * Implement the document() XSLT function
106 * node-set document(object, node-set?)
109 xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs){
111 xmlXPathObjectPtr obj, obj2 = NULL;
112 xmlChar *base = NULL, *URI;
115 if ((nargs < 1) || (nargs > 2)) {
116 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
117 xsltGenericError(xsltGenericErrorContext,
118 "document() : invalid number of args %d\n", nargs);
119 ctxt->error = XPATH_INVALID_ARITY;
122 if (ctxt->value == NULL) {
123 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
124 xsltGenericError(xsltGenericErrorContext,
125 "document() : invalid arg value\n");
126 ctxt->error = XPATH_INVALID_TYPE;
131 if (ctxt->value->type != XPATH_NODESET) {
132 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt),
134 xsltGenericError(xsltGenericErrorContext,
135 "document() : invalid arg expecting a nodeset\n");
136 ctxt->error = XPATH_INVALID_TYPE;
140 obj2 = valuePop(ctxt);
143 if (ctxt->value->type == XPATH_NODESET) {
145 xmlXPathObjectPtr newobj, ret;
147 obj = valuePop(ctxt);
148 ret = xmlXPathNewNodeSet(NULL);
150 if (obj->nodesetval) {
151 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
153 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
154 xmlXPathStringFunction(ctxt, 1);
156 valuePush(ctxt, xmlXPathObjectCopy(obj2));
159 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
161 xsltDocumentFunction(ctxt, 2);
162 newobj = valuePop(ctxt);
163 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
165 xmlXPathFreeObject(newobj);
169 xmlXPathFreeObject(obj);
171 xmlXPathFreeObject(obj2);
172 valuePush(ctxt, ret);
176 * Make sure it's converted to a string
178 xmlXPathStringFunction(ctxt, 1);
179 if (ctxt->value->type != XPATH_STRING) {
180 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
181 xsltGenericError(xsltGenericErrorContext,
182 "document() : invalid arg expecting a string\n");
183 ctxt->error = XPATH_INVALID_TYPE;
185 xmlXPathFreeObject(obj2);
188 obj = valuePop(ctxt);
189 if (obj->stringval == NULL) {
190 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
192 if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
193 (obj2->nodesetval->nodeNr > 0) &&
194 IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) {
197 target = obj2->nodesetval->nodeTab[0];
198 if (target->type == XML_ATTRIBUTE_NODE) {
199 target = ((xmlAttrPtr) target)->parent;
201 base = xmlNodeGetBase(target->doc, target);
203 xsltTransformContextPtr tctxt;
205 tctxt = xsltXPathGetTransformContext(ctxt);
206 if ((tctxt != NULL) && (tctxt->inst != NULL)) {
207 base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
208 } else if ((tctxt != NULL) && (tctxt->style != NULL) &&
209 (tctxt->style->doc != NULL)) {
210 base = xmlNodeGetBase(tctxt->style->doc,
211 (xmlNodePtr) tctxt->style->doc);
214 URI = xmlBuildURI(obj->stringval, base);
218 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
220 xsltTransformContextPtr tctxt;
222 tctxt = xsltXPathGetTransformContext(ctxt);
224 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt),
226 xsltGenericError(xsltGenericErrorContext,
227 "document() : internal error tctxt == NULL\n");
228 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
230 if (xmlStrEqual(tctxt->style->doc->URL, URI)) {
231 valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr)tctxt->style->doc));
234 doc = xsltLoadDocument(tctxt, URI);
236 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
238 /* TODO: use XPointer of HTML location for fragment ID */
239 /* pbm #xxx can lead to location sets, not nodesets :-) */
240 valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc->doc));
247 xmlXPathFreeObject(obj);
249 xmlXPathFreeObject(obj2);
254 * @ctxt: the XPath Parser context
255 * @nargs: the number of arguments
257 * Implement the key() XSLT function
258 * node-set key(string, object)
261 xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
262 xmlNodeSetPtr nodelist;
263 xmlXPathObjectPtr obj1, obj2;
264 xmlChar *key = NULL, *value;
265 const xmlChar *keyURI;
266 xsltTransformContextPtr tctxt;
269 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
270 xsltGenericError(xsltGenericErrorContext,
271 "key() : expects two arguments\n");
272 ctxt->error = XPATH_INVALID_ARITY;
276 obj2 = valuePop(ctxt);
277 if ((obj2 == NULL) ||
278 (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
279 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
280 xsltGenericError(xsltGenericErrorContext,
281 "key() : invalid arg expecting a string\n");
282 ctxt->error = XPATH_INVALID_TYPE;
283 xmlXPathFreeObject(obj2);
287 obj1 = valuePop(ctxt);
289 if (obj2->type == XPATH_NODESET) {
291 xmlXPathObjectPtr newobj, ret;
293 ret = xmlXPathNewNodeSet(NULL);
295 if (obj2->nodesetval != NULL) {
296 for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
297 valuePush(ctxt, xmlXPathObjectCopy(obj1));
299 xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i]));
300 xmlXPathStringFunction(ctxt, 1);
301 xsltKeyFunction(ctxt, 2);
302 newobj = valuePop(ctxt);
303 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
305 xmlXPathFreeObject(newobj);
308 valuePush(ctxt, ret);
310 xmlChar *qname, *prefix;
313 * Get the associated namespace URI if qualified name
315 qname = obj1->stringval;
316 key = xmlSplitQName2(qname, &prefix);
318 key = xmlStrdup(obj1->stringval);
323 if (prefix != NULL) {
324 keyURI = xmlXPathNsLookup(ctxt->context, prefix);
325 if (keyURI == NULL) {
326 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt),
328 xsltGenericError(xsltGenericErrorContext,
329 "key() : prefix %s is not bound\n", prefix);
338 * Force conversion of first arg to string
340 valuePush(ctxt, obj2);
341 xmlXPathStringFunction(ctxt, 1);
342 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
343 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt),
345 xsltGenericError(xsltGenericErrorContext,
346 "key() : invalid arg expecting a string\n");
347 ctxt->error = XPATH_INVALID_TYPE;
348 xmlXPathFreeObject(obj1);
352 obj2 = valuePop(ctxt);
353 value = obj2->stringval;
355 tctxt = xsltXPathGetTransformContext(ctxt);
357 nodelist = xsltGetKey(tctxt, key, keyURI, value);
358 valuePush(ctxt, xmlXPathWrapNodeSet(
359 xmlXPathNodeSetMerge(NULL, nodelist)));
364 xmlXPathFreeObject(obj1);
366 xmlXPathFreeObject(obj2);
372 * xsltUnparsedEntityURIFunction:
373 * @ctxt: the XPath Parser context
374 * @nargs: the number of arguments
376 * Implement the unparsed-entity-uri() XSLT function
377 * string unparsed-entity-uri(string)
380 xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
381 xmlXPathObjectPtr obj;
384 if ((nargs != 1) || (ctxt->value == NULL)) {
385 xsltGenericError(xsltGenericErrorContext,
386 "unparsed-entity-uri() : expects one string arg\n");
387 ctxt->error = XPATH_INVALID_ARITY;
390 obj = valuePop(ctxt);
391 if (obj->type != XPATH_STRING) {
392 obj = xmlXPathConvertString(obj);
395 str = obj->stringval;
397 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
401 entity = xmlGetDocEntity(ctxt->context->doc, str);
402 if (entity == NULL) {
403 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
405 if (entity->URI != NULL)
406 valuePush(ctxt, xmlXPathNewString(entity->URI));
408 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
411 xmlXPathFreeObject(obj);
415 * xsltFormatNumberFunction:
416 * @ctxt: the XPath Parser context
417 * @nargs: the number of arguments
419 * Implement the format-number() XSLT function
420 * string format-number(number, string, string?)
423 xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
425 xmlXPathObjectPtr numberObj = NULL;
426 xmlXPathObjectPtr formatObj = NULL;
427 xmlXPathObjectPtr decimalObj = NULL;
428 xsltStylesheetPtr sheet;
429 xsltDecimalFormatPtr formatValues;
431 xsltTransformContextPtr tctxt;
433 tctxt = xsltXPathGetTransformContext(ctxt);
436 sheet = tctxt->style;
439 formatValues = sheet->decimalFormat;
444 decimalObj = valuePop(ctxt);
445 formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval);
446 /* Intentional fall-through */
449 formatObj = valuePop(ctxt);
451 numberObj = valuePop(ctxt);
454 XP_ERROR(XPATH_INVALID_ARITY);
457 if (xsltFormatNumberConversion(formatValues,
458 formatObj->stringval,
460 &result) == XPATH_EXPRESSION_OK) {
461 valuePush(ctxt, xmlXPathNewString(result));
465 xmlXPathFreeObject(numberObj);
466 xmlXPathFreeObject(formatObj);
467 xmlXPathFreeObject(decimalObj);
471 * xsltGenerateIdFunction:
472 * @ctxt: the XPath Parser context
473 * @nargs: the number of arguments
475 * Implement the generate-id() XSLT function
476 * string generate-id(node-set?)
479 xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
480 xmlNodePtr cur = NULL;
485 cur = ctxt->context->node;
486 } else if (nargs == 1) {
487 xmlXPathObjectPtr obj;
488 xmlNodeSetPtr nodelist;
491 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
492 ctxt->error = XPATH_INVALID_TYPE;
493 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt),
495 xsltGenericError(xsltGenericErrorContext,
496 "generate-id() : invalid arg expecting a node-set\n");
499 obj = valuePop(ctxt);
500 nodelist = obj->nodesetval;
501 if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
502 xmlXPathFreeObject(obj);
503 valuePush(ctxt, xmlXPathNewCString(""));
506 cur = nodelist->nodeTab[0];
507 for (i = 1;i < nodelist->nodeNr;i++) {
508 ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
510 cur = nodelist->nodeTab[i];
512 xmlXPathFreeObject(obj);
514 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
515 xsltGenericError(xsltGenericErrorContext,
516 "generate-id() : invalid number of args %d\n", nargs);
517 ctxt->error = XPATH_INVALID_ARITY;
521 * Okay this is ugly but should work, use the NodePtr address
524 val = (unsigned long)((char *)cur - (char *)0);
525 val /= sizeof(xmlNode);
526 sprintf((char *)str, "id%ld", val);
527 valuePush(ctxt, xmlXPathNewString(str));
531 * xsltSystemPropertyFunction:
532 * @ctxt: the XPath Parser context
533 * @nargs: the number of arguments
535 * Implement the system-property() XSLT function
536 * object system-property(string)
539 xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
540 xmlXPathObjectPtr obj;
541 xmlChar *prefix, *name;
542 const xmlChar *nsURI = NULL;
545 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
546 xsltGenericError(xsltGenericErrorContext,
547 "system-property() : expects one string arg\n");
548 ctxt->error = XPATH_INVALID_ARITY;
551 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
552 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
553 xsltGenericError(xsltGenericErrorContext,
554 "system-property() : invalid arg expecting a string\n");
555 ctxt->error = XPATH_INVALID_TYPE;
558 obj = valuePop(ctxt);
559 if (obj->stringval == NULL) {
560 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
562 name = xmlSplitQName2(obj->stringval, &prefix);
564 name = xmlStrdup(obj->stringval);
566 nsURI = xmlXPathNsLookup(ctxt->context, prefix);
568 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt),
570 xsltGenericError(xsltGenericErrorContext,
571 "system-property() : prefix %s is not bound\n", prefix);
575 if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) {
576 #ifdef DOCBOOK_XSL_HACK
577 if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
578 xsltStylesheetPtr sheet;
579 xsltTransformContextPtr tctxt;
581 tctxt = xsltXPathGetTransformContext(ctxt);
582 if ((tctxt != NULL) && (tctxt->inst != NULL) &&
583 (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) &&
584 (tctxt->inst->parent != NULL) &&
585 (xmlStrEqual(tctxt->inst->parent->name,
586 BAD_CAST "template")))
587 sheet = tctxt->style;
590 if ((sheet != NULL) && (sheet->doc != NULL) &&
591 (sheet->doc->URL != NULL) &&
592 (xmlStrstr(sheet->doc->URL,
593 (const xmlChar *)"chunk") != NULL)) {
594 valuePush(ctxt, xmlXPathNewString(
595 (const xmlChar *)"libxslt (SAXON 6.2 compatible)"));
598 valuePush(ctxt, xmlXPathNewString(
599 (const xmlChar *)XSLT_DEFAULT_VENDOR));
603 if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
604 valuePush(ctxt, xmlXPathNewString(
605 (const xmlChar *)XSLT_DEFAULT_VENDOR));
608 if (xmlStrEqual(name, (const xmlChar *)"version")) {
609 valuePush(ctxt, xmlXPathNewString(
610 (const xmlChar *)XSLT_DEFAULT_VERSION));
611 } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) {
612 valuePush(ctxt, xmlXPathNewString(
613 (const xmlChar *)XSLT_DEFAULT_URL));
615 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
623 xmlXPathFreeObject(obj);
627 * xsltElementAvailableFunction:
628 * @ctxt: the XPath Parser context
629 * @nargs: the number of arguments
631 * Implement the element-available() XSLT function
632 * boolean element-available(string)
635 xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
636 xmlXPathObjectPtr obj;
637 xmlChar *prefix, *name;
638 const xmlChar *nsURI = NULL;
639 xsltTransformContextPtr tctxt;
642 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
643 xsltGenericError(xsltGenericErrorContext,
644 "element-available() : expects one string arg\n");
645 ctxt->error = XPATH_INVALID_ARITY;
648 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
649 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
650 xsltGenericError(xsltGenericErrorContext,
651 "element-available() : invalid arg expecting a string\n");
652 ctxt->error = XPATH_INVALID_TYPE;
655 obj = valuePop(ctxt);
656 tctxt = xsltXPathGetTransformContext(ctxt);
658 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
659 xsltGenericError(xsltGenericErrorContext,
660 "element-available() : internal error tctxt == NULL\n");
661 xmlXPathFreeObject(obj);
662 valuePush(ctxt, xmlXPathNewBoolean(0));
667 name = xmlSplitQName2(obj->stringval, &prefix);
671 name = xmlStrdup(obj->stringval);
672 ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL);
673 nsURI = xmlStrdup(ns->href);
675 nsURI = xmlXPathNsLookup(ctxt->context, prefix);
677 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt),
679 xsltGenericError(xsltGenericErrorContext,
680 "element-available() : prefix %s is not bound\n", prefix);
684 if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) {
685 valuePush(ctxt, xmlXPathNewBoolean(1));
687 valuePush(ctxt, xmlXPathNewBoolean(0));
690 xmlXPathFreeObject(obj);
698 * xsltFunctionAvailableFunction:
699 * @ctxt: the XPath Parser context
700 * @nargs: the number of arguments
702 * Implement the function-available() XSLT function
703 * boolean function-available(string)
706 xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
707 xmlXPathObjectPtr obj;
708 xmlChar *prefix, *name;
709 const xmlChar *nsURI = NULL;
712 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
713 xsltGenericError(xsltGenericErrorContext,
714 "function-available() : expects one string arg\n");
715 ctxt->error = XPATH_INVALID_ARITY;
718 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
719 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
720 xsltGenericError(xsltGenericErrorContext,
721 "function-available() : invalid arg expecting a string\n");
722 ctxt->error = XPATH_INVALID_TYPE;
725 obj = valuePop(ctxt);
727 name = xmlSplitQName2(obj->stringval, &prefix);
729 name = xmlStrdup(obj->stringval);
731 nsURI = xmlXPathNsLookup(ctxt->context, prefix);
733 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt),
735 xsltGenericError(xsltGenericErrorContext,
736 "function-available() : prefix %s is not bound\n", prefix);
740 if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
741 valuePush(ctxt, xmlXPathNewBoolean(1));
743 valuePush(ctxt, xmlXPathNewBoolean(0));
746 xmlXPathFreeObject(obj);
754 * xsltCurrentFunction:
755 * @ctxt: the XPath Parser context
756 * @nargs: the number of arguments
758 * Implement the current() XSLT function
762 xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
763 xsltTransformContextPtr tctxt;
766 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
767 xsltGenericError(xsltGenericErrorContext,
768 "current() : function uses no argument\n");
769 ctxt->error = XPATH_INVALID_ARITY;
772 tctxt = xsltXPathGetTransformContext(ctxt);
774 xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
775 xsltGenericError(xsltGenericErrorContext,
776 "current() : internal error tctxt == NULL\n");
777 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
779 valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
783 /************************************************************************
785 * Registration of XSLT and libxslt functions *
787 ************************************************************************/
790 * xsltRegisterAllFunctions:
791 * @ctxt: the XPath context
793 * Registers all default XSLT functions in this context
796 xsltRegisterAllFunctions(xmlXPathContextPtr ctxt)
798 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current",
799 xsltCurrentFunction);
800 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document",
801 xsltDocumentFunction);
802 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction);
803 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri",
804 xsltUnparsedEntityURIFunction);
805 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number",
806 xsltFormatNumberFunction);
807 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id",
808 xsltGenerateIdFunction);
809 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property",
810 xsltSystemPropertyFunction);
811 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available",
812 xsltElementAvailableFunction);
813 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available",
814 xsltFunctionAvailableFunction);