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