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