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