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    xmlXPathObjectPtr obj = NULL;
658    long val;
659    xmlChar str[30];
660    xmlDocPtr doc;
661
662    if (nargs == 0) {
663	cur = ctxt->context->node;
664    } else if (nargs == 1) {
665	xmlNodeSetPtr nodelist;
666	int i, ret;
667
668	if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
669	    ctxt->error = XPATH_INVALID_TYPE;
670	    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
671		"generate-id() : invalid arg expecting a node-set\n");
672	    return;
673	}
674	obj = valuePop(ctxt);
675	nodelist = obj->nodesetval;
676	if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
677	    xmlXPathFreeObject(obj);
678	    valuePush(ctxt, xmlXPathNewCString(""));
679	    return;
680	}
681	cur = nodelist->nodeTab[0];
682	for (i = 1;i < nodelist->nodeNr;i++) {
683	    ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
684	    if (ret == -1)
685	        cur = nodelist->nodeTab[i];
686	}
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    if (cur->type != XML_NAMESPACE_DECL)
698        doc = cur->doc;
699    else {
700        xmlNsPtr ns = (xmlNsPtr) cur;
701
702        if (ns->context != NULL)
703            doc = ns->context;
704        else
705            doc = ctxt->context->doc;
706
707    }
708
709    if (obj)
710        xmlXPathFreeObject(obj);
711
712    val = (long)((char *)cur - (char *)doc);
713    if (val >= 0) {
714      sprintf((char *)str, "idp%ld", val);
715    } else {
716      sprintf((char *)str, "idm%ld", -val);
717    }
718    valuePush(ctxt, xmlXPathNewString(str));
719}
720
721/**
722 * xsltSystemPropertyFunction:
723 * @ctxt:  the XPath Parser context
724 * @nargs:  the number of arguments
725 *
726 * Implement the system-property() XSLT function
727 *   object system-property(string)
728 */
729void
730xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
731    xmlXPathObjectPtr obj;
732    xmlChar *prefix, *name;
733    const xmlChar *nsURI = NULL;
734
735    if (nargs != 1) {
736	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
737		"system-property() : expects one string arg\n");
738	ctxt->error = XPATH_INVALID_ARITY;
739	return;
740    }
741    if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
742	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
743	    "system-property() : invalid arg expecting a string\n");
744	ctxt->error = XPATH_INVALID_TYPE;
745	return;
746    }
747    obj = valuePop(ctxt);
748    if (obj->stringval == NULL) {
749	valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
750    } else {
751	name = xmlSplitQName2(obj->stringval, &prefix);
752	if (name == NULL) {
753	    name = xmlStrdup(obj->stringval);
754	} else {
755	    nsURI = xmlXPathNsLookup(ctxt->context, prefix);
756	    if (nsURI == NULL) {
757		xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
758		    "system-property() : prefix %s is not bound\n", prefix);
759	    }
760	}
761
762	if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) {
763#ifdef DOCBOOK_XSL_HACK
764	    if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
765		xsltStylesheetPtr sheet;
766		xsltTransformContextPtr tctxt;
767
768		tctxt = xsltXPathGetTransformContext(ctxt);
769		if ((tctxt != NULL) && (tctxt->inst != NULL) &&
770		    (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) &&
771		    (tctxt->inst->parent != NULL) &&
772		    (xmlStrEqual(tctxt->inst->parent->name,
773				 BAD_CAST "template")))
774		    sheet = tctxt->style;
775		else
776		    sheet = NULL;
777		if ((sheet != NULL) && (sheet->doc != NULL) &&
778		    (sheet->doc->URL != NULL) &&
779		    (xmlStrstr(sheet->doc->URL,
780			       (const xmlChar *)"chunk") != NULL)) {
781		    valuePush(ctxt, xmlXPathNewString(
782			(const xmlChar *)"libxslt (SAXON 6.2 compatible)"));
783
784		} else {
785		    valuePush(ctxt, xmlXPathNewString(
786			(const xmlChar *)XSLT_DEFAULT_VENDOR));
787		}
788	    } else
789#else
790	    if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
791		valuePush(ctxt, xmlXPathNewString(
792			  (const xmlChar *)XSLT_DEFAULT_VENDOR));
793	    } else
794#endif
795	    if (xmlStrEqual(name, (const xmlChar *)"version")) {
796		valuePush(ctxt, xmlXPathNewString(
797		    (const xmlChar *)XSLT_DEFAULT_VERSION));
798	    } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) {
799		valuePush(ctxt, xmlXPathNewString(
800		    (const xmlChar *)XSLT_DEFAULT_URL));
801	    } else {
802		valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
803	    }
804	}
805	if (name != NULL)
806	    xmlFree(name);
807	if (prefix != NULL)
808	    xmlFree(prefix);
809    }
810    xmlXPathFreeObject(obj);
811}
812
813/**
814 * xsltElementAvailableFunction:
815 * @ctxt:  the XPath Parser context
816 * @nargs:  the number of arguments
817 *
818 * Implement the element-available() XSLT function
819 *   boolean element-available(string)
820 */
821void
822xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
823    xmlXPathObjectPtr obj;
824    xmlChar *prefix, *name;
825    const xmlChar *nsURI = NULL;
826    xsltTransformContextPtr tctxt;
827
828    if (nargs != 1) {
829	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
830		"element-available() : expects one string arg\n");
831	ctxt->error = XPATH_INVALID_ARITY;
832	return;
833    }
834    xmlXPathStringFunction(ctxt, 1);
835    if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
836	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
837	    "element-available() : invalid arg expecting a string\n");
838	ctxt->error = XPATH_INVALID_TYPE;
839	return;
840    }
841    obj = valuePop(ctxt);
842    tctxt = xsltXPathGetTransformContext(ctxt);
843    if (tctxt == NULL) {
844	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
845		"element-available() : internal error tctxt == NULL\n");
846	xmlXPathFreeObject(obj);
847	valuePush(ctxt, xmlXPathNewBoolean(0));
848	return;
849    }
850
851
852    name = xmlSplitQName2(obj->stringval, &prefix);
853    if (name == NULL) {
854	xmlNsPtr ns;
855
856	name = xmlStrdup(obj->stringval);
857	ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL);
858	if (ns != NULL) nsURI = xmlStrdup(ns->href);
859    } else {
860	nsURI = xmlXPathNsLookup(ctxt->context, prefix);
861	if (nsURI == NULL) {
862	    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
863		"element-available() : prefix %s is not bound\n", prefix);
864	}
865    }
866
867    if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) {
868	valuePush(ctxt, xmlXPathNewBoolean(1));
869    } else {
870	valuePush(ctxt, xmlXPathNewBoolean(0));
871    }
872
873    xmlXPathFreeObject(obj);
874    if (name != NULL)
875	xmlFree(name);
876    if (prefix != NULL)
877	xmlFree(prefix);
878}
879
880/**
881 * xsltFunctionAvailableFunction:
882 * @ctxt:  the XPath Parser context
883 * @nargs:  the number of arguments
884 *
885 * Implement the function-available() XSLT function
886 *   boolean function-available(string)
887 */
888void
889xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
890    xmlXPathObjectPtr obj;
891    xmlChar *prefix, *name;
892    const xmlChar *nsURI = NULL;
893
894    if (nargs != 1) {
895	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
896		"function-available() : expects one string arg\n");
897	ctxt->error = XPATH_INVALID_ARITY;
898	return;
899    }
900    xmlXPathStringFunction(ctxt, 1);
901    if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
902	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
903	    "function-available() : invalid arg expecting a string\n");
904	ctxt->error = XPATH_INVALID_TYPE;
905	return;
906    }
907    obj = valuePop(ctxt);
908
909    name = xmlSplitQName2(obj->stringval, &prefix);
910    if (name == NULL) {
911	name = xmlStrdup(obj->stringval);
912    } else {
913	nsURI = xmlXPathNsLookup(ctxt->context, prefix);
914	if (nsURI == NULL) {
915	    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
916		"function-available() : prefix %s is not bound\n", prefix);
917	}
918    }
919
920    if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
921	valuePush(ctxt, xmlXPathNewBoolean(1));
922    } else {
923	valuePush(ctxt, xmlXPathNewBoolean(0));
924    }
925
926    xmlXPathFreeObject(obj);
927    if (name != NULL)
928	xmlFree(name);
929    if (prefix != NULL)
930	xmlFree(prefix);
931}
932
933/**
934 * xsltCurrentFunction:
935 * @ctxt:  the XPath Parser context
936 * @nargs:  the number of arguments
937 *
938 * Implement the current() XSLT function
939 *   node-set current()
940 */
941static void
942xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
943    xsltTransformContextPtr tctxt;
944
945    if (nargs != 0) {
946	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
947		"current() : function uses no argument\n");
948	ctxt->error = XPATH_INVALID_ARITY;
949	return;
950    }
951    tctxt = xsltXPathGetTransformContext(ctxt);
952    if (tctxt == NULL) {
953	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
954		"current() : internal error tctxt == NULL\n");
955	valuePush(ctxt, xmlXPathNewNodeSet(NULL));
956    } else {
957	valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
958    }
959}
960
961/************************************************************************
962 * 									*
963 * 		Registration of XSLT and libxslt functions		*
964 * 									*
965 ************************************************************************/
966
967/**
968 * xsltRegisterAllFunctions:
969 * @ctxt:  the XPath context
970 *
971 * Registers all default XSLT functions in this context
972 */
973void
974xsltRegisterAllFunctions(xmlXPathContextPtr ctxt)
975{
976    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current",
977                         xsltCurrentFunction);
978    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document",
979                         xsltDocumentFunction);
980    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction);
981    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri",
982                         xsltUnparsedEntityURIFunction);
983    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number",
984                         xsltFormatNumberFunction);
985    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id",
986                         xsltGenerateIdFunction);
987    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property",
988                         xsltSystemPropertyFunction);
989    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available",
990                         xsltElementAvailableFunction);
991    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available",
992                         xsltFunctionAvailableFunction);
993}
994