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