enhanced ApplyTemplates and ForEach to allow multiple documents
[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@imag.fr
10  * Bjorn Reese <breese@users.sourceforge.net> for number formatting
11  */
12
13 #include "xsltconfig.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 "numbersInternals.h"
39 #include "keys.h"
40 #include "documents.h"
41
42 #ifdef WITH_XSLT_DEBUG
43 #define WITH_XSLT_DEBUG_FUNCTION
44 #endif
45
46
47 /************************************************************************
48  *                                                                      *
49  *                      Module interfaces                               *
50  *                                                                      *
51  ************************************************************************/
52
53 /**
54  * xsltDocumentFunction:
55  * @ctxt:  the XPath Parser context
56  * @nargs:  the number of arguments
57  *
58  * Implement the document() XSLT function
59  *   node-set document(object, node-set?)
60  */
61 void
62 xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs){
63     xsltDocumentPtr doc;
64     xmlXPathObjectPtr obj, obj2 = NULL;
65     xmlChar *base = NULL, *URI;
66
67
68     if ((nargs < 1) || (nargs > 2)) {
69         xsltGenericError(xsltGenericErrorContext,
70                 "document() : invalid number of args %d\n", nargs);
71         ctxt->error = XPATH_INVALID_ARITY;
72         return;
73     }
74     if (ctxt->value == NULL) {
75         xsltGenericError(xsltGenericErrorContext,
76             "document() : invalid arg value\n");
77         ctxt->error = XPATH_INVALID_TYPE;
78         return;
79     }
80
81     if (nargs == 2) {
82         if (ctxt->value->type != XPATH_NODESET) {
83             xsltGenericError(xsltGenericErrorContext,
84                 "document() : invalid arg expecting a nodeset\n");
85             ctxt->error = XPATH_INVALID_TYPE;
86             return;
87         }
88
89         obj2 = valuePop(ctxt);
90     }
91
92     if (ctxt->value->type == XPATH_NODESET) {
93         int i;
94         xmlXPathObjectPtr newobj, ret;
95
96         obj = valuePop(ctxt);
97         ret = xmlXPathNewNodeSet(NULL);
98
99         if (obj->nodesetval) {
100             for (i = 0; i < obj->nodesetval->nodeNr; i++) {
101                 valuePush(ctxt,
102                           xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
103                 xmlXPathStringFunction(ctxt, 1);
104                 if (nargs == 2) {
105                     valuePush(ctxt, xmlXPathObjectCopy(obj2));
106                 } else {
107                     valuePush(ctxt,
108                               xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
109                 }
110                 xsltDocumentFunction(ctxt, 2);
111                 newobj = valuePop(ctxt);
112                 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
113                                                        newobj->nodesetval);
114                 xmlXPathFreeObject(newobj);
115             }
116         }
117
118         xmlXPathFreeObject(obj);
119         if (obj2 != NULL)
120             xmlXPathFreeObject(obj2);
121         valuePush(ctxt, ret);
122         return;
123     }
124     /*
125      * Make sure it's converted to a string
126      */
127     xmlXPathStringFunction(ctxt, 1);
128     if (ctxt->value->type != XPATH_STRING) {
129         xsltGenericError(xsltGenericErrorContext,
130             "document() : invalid arg expecting a string\n");
131         ctxt->error = XPATH_INVALID_TYPE;
132         if (obj2 != NULL)
133             xmlXPathFreeObject(obj2);
134         return;
135     }
136     obj = valuePop(ctxt);
137     if (obj->stringval == NULL) {
138         valuePush(ctxt, xmlXPathNewNodeSet(NULL));
139     } else {
140         if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
141               (obj2->nodesetval->nodeNr > 0)) {
142             xmlNodePtr target;
143
144             target = obj2->nodesetval->nodeTab[0];
145             if (target->type == XML_ATTRIBUTE_NODE) {
146                 target = ((xmlAttrPtr) target)->parent;
147             }
148             base = xmlNodeGetBase(target->doc, target);
149         } else {
150             xsltTransformContextPtr tctxt = ctxt->context->extra;
151             if ((tctxt != NULL) && (tctxt->inst != NULL)) {
152                 base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
153             } else if ((tctxt != NULL) && (tctxt->style != NULL) &&
154                        (tctxt->style->doc != NULL)) {
155                 base = xmlNodeGetBase(tctxt->style->doc, 
156                                       (xmlNodePtr) tctxt->style->doc);
157             }
158         }
159         URI = xmlBuildURI(obj->stringval, base);
160         if (base != NULL)
161             xmlFree(base);
162         if (URI == NULL) {
163             valuePush(ctxt, xmlXPathNewNodeSet(NULL));
164         } else {
165             xsltTransformContextPtr tctxt;
166
167             tctxt = (xsltTransformContextPtr) ctxt->context->extra;
168             if (tctxt == NULL) {
169                 xsltGenericError(xsltGenericErrorContext,
170                         "document() : internal error tctxt == NULL\n");
171                 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
172             } else {
173                 doc = xsltLoadDocument(tctxt, URI);
174                 if (doc == NULL)
175                     valuePush(ctxt, xmlXPathNewNodeSet(NULL));
176                 else {
177                     /* TODO: use XPointer of HTML location for fragment ID */
178                     /* pbm #xxx can lead to location sets, not nodesets :-) */
179                     valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc->doc));
180                 }
181             }
182             xmlFree(URI);
183         }
184     }
185     xmlXPathFreeObject(obj);
186     if (obj2 != NULL)
187         xmlXPathFreeObject(obj2);
188 }
189
190 /**
191  * xsltKeyFunction:
192  * @ctxt:  the XPath Parser context
193  * @nargs:  the number of arguments
194  *
195  * Implement the key() XSLT function
196  *   node-set key(string, object)
197  */
198 void
199 xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
200     xmlNodeSetPtr nodelist;
201     xmlXPathObjectPtr obj1, obj2;
202     xmlChar *key = NULL, *value;
203     const xmlChar *keyURI;
204     xsltTransformContextPtr tctxt;
205
206     tctxt = ((xsltTransformContextPtr)ctxt->context->extra);
207
208     if (nargs != 2) {
209         xsltGenericError(xsltGenericErrorContext,
210                 "key() : expects two arguments\n");
211         ctxt->error = XPATH_INVALID_ARITY;
212         return;
213     }
214
215     obj2 = valuePop(ctxt);
216     if ((obj2 == NULL) ||
217         (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
218         xsltGenericError(xsltGenericErrorContext,
219             "key() : invalid arg expecting a string\n");
220         ctxt->error = XPATH_INVALID_TYPE;
221         xmlXPathFreeObject(obj2);
222
223         return;
224     }
225     obj1 = valuePop(ctxt);
226
227     if (obj2->type == XPATH_NODESET) {
228         int i;
229         xmlXPathObjectPtr newobj, ret;
230
231         ret = xmlXPathNewNodeSet(NULL);
232
233         if (obj2->nodesetval != NULL) {
234             for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
235                 valuePush(ctxt, xmlXPathObjectCopy(obj1));
236                 valuePush(ctxt,
237                           xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i]));
238                 xmlXPathStringFunction(ctxt, 1);
239                 xsltKeyFunction(ctxt, 2);
240                 newobj = valuePop(ctxt);
241                 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
242                                                        newobj->nodesetval);
243                 xmlXPathFreeObject(newobj);
244             }
245         }
246         valuePush(ctxt, ret);
247     } else {
248         xmlChar *qname, *prefix;
249
250         /*
251          * Get the associated namespace URI if qualified name
252          */
253         qname = obj1->stringval;
254         key = xmlSplitQName2(qname, &prefix);
255         if (key == NULL) {
256             key = xmlStrdup(obj1->stringval);
257             keyURI = NULL;
258             if (prefix != NULL)
259                 xmlFree(prefix);
260         } else {
261             if (prefix != NULL) {
262                 keyURI = xmlXPathNsLookup(ctxt->context, prefix);
263                 if (keyURI == NULL) {
264                     xsltGenericError(xsltGenericErrorContext,
265                         "key() : prefix %s is not bound\n", prefix);
266                 }
267                 xmlFree(prefix);
268             } else {
269                 keyURI = NULL;
270             }
271         }
272
273         /*
274          * Force conversion of first arg to string
275          */
276         valuePush(ctxt, obj2);
277         xmlXPathStringFunction(ctxt, 1);
278         if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
279             xsltGenericError(xsltGenericErrorContext,
280                 "key() : invalid arg expecting a string\n");
281             ctxt->error = XPATH_INVALID_TYPE;
282             xmlXPathFreeObject(obj1);
283
284             return;
285         }
286         obj2 = valuePop(ctxt);
287         value = obj2->stringval;
288
289         nodelist = xsltGetKey(tctxt, key, keyURI, value);
290         valuePush(ctxt, xmlXPathWrapNodeSet(
291                         xmlXPathNodeSetMerge(NULL, nodelist)));
292     }
293
294
295     if (obj1 != NULL)
296         xmlXPathFreeObject(obj1);
297     if (obj2 != NULL)
298         xmlXPathFreeObject(obj2);
299     if (key != NULL)
300         xmlFree(key);
301 }
302
303 /**
304  * xsltUnparsedEntityURIFunction:
305  * @ctxt:  the XPath Parser context
306  * @nargs:  the number of arguments
307  *
308  * Implement the unparsed-entity-uri() XSLT function
309  *   string unparsed-entity-uri(string)
310  */
311 void
312 xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
313     xmlXPathObjectPtr obj;
314     xmlChar *str;
315
316     if ((nargs != 1) || (ctxt->value == NULL)) {
317         xsltGenericError(xsltGenericErrorContext,
318                 "unparsed-entity-uri() : expects one string arg\n");
319         ctxt->error = XPATH_INVALID_ARITY;
320         return;
321     }
322     obj = valuePop(ctxt);
323     if (obj->type != XPATH_STRING) {
324         obj = xmlXPathConvertString(obj);
325     }
326
327     str = obj->stringval;
328     if (str == NULL) {
329         valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
330     } else {
331         xmlEntityPtr entity;
332
333         entity = xmlGetDocEntity(ctxt->context->doc, str);
334         if (entity == NULL) {
335             valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
336         } else {
337             if (entity->URI != NULL)
338                 valuePush(ctxt, xmlXPathNewString(entity->URI));
339             else
340                 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
341         }
342     }
343     xmlXPathFreeObject(obj);
344 }
345
346 /**
347  * xsltFormatNumberFunction:
348  * @ctxt:  the XPath Parser context
349  * @nargs:  the number of arguments
350  *
351  * Implement the format-number() XSLT function
352  *   string format-number(number, string, string?)
353  */
354 void
355 xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
356 {
357     xmlXPathObjectPtr numberObj = NULL;
358     xmlXPathObjectPtr formatObj = NULL;
359     xmlXPathObjectPtr decimalObj = NULL;
360     xsltStylesheetPtr sheet;
361     xsltDecimalFormatPtr formatValues;
362     xmlChar *result;
363
364     sheet = ((xsltTransformContextPtr)ctxt->context->extra)->style;
365     formatValues = sheet->decimalFormat;
366     
367     switch (nargs) {
368     case 3:
369         CAST_TO_STRING;
370         decimalObj = valuePop(ctxt);
371         formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval);
372         /* Intentional fall-through */
373     case 2:
374         CAST_TO_STRING;
375         formatObj = valuePop(ctxt);
376         CAST_TO_NUMBER;
377         numberObj = valuePop(ctxt);
378         break;
379     default:
380         XP_ERROR(XPATH_INVALID_ARITY);
381     }
382
383     if (xsltFormatNumberConversion(formatValues,
384                                    formatObj->stringval,
385                                    numberObj->floatval,
386                                    &result) == XPATH_EXPRESSION_OK) {
387         valuePush(ctxt, xmlXPathNewString(result));
388         xmlFree(result);
389     }
390     
391     xmlXPathFreeObject(numberObj);
392     xmlXPathFreeObject(formatObj);
393     xmlXPathFreeObject(decimalObj);
394 }
395
396 /**
397  * xsltGenerateIdFunction:
398  * @ctxt:  the XPath Parser context
399  * @nargs:  the number of arguments
400  *
401  * Implement the generate-id() XSLT function
402  *   string generate-id(node-set?)
403  */
404 void
405 xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
406     xmlNodePtr cur = NULL;
407     unsigned long val;
408     xmlChar str[20];
409
410     if (nargs == 0) {
411         cur = ctxt->context->node;
412     } else if (nargs == 1) {
413         xmlXPathObjectPtr obj;
414         xmlNodeSetPtr nodelist;
415         int i, ret;
416
417         if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
418             ctxt->error = XPATH_INVALID_TYPE;
419             xsltGenericError(xsltGenericErrorContext,
420                 "generate-id() : invalid arg expecting a node-set\n");
421             return;
422         }
423         obj = valuePop(ctxt);
424         nodelist = obj->nodesetval;
425         if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
426             xmlXPathFreeObject(obj);
427             valuePush(ctxt, xmlXPathNewCString(""));
428             return;
429         }
430         cur = nodelist->nodeTab[0];
431         for (i = 1;i < nodelist->nodeNr;i++) {
432             ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
433             if (ret == -1)
434                 cur = nodelist->nodeTab[i];
435         }
436         xmlXPathFreeObject(obj);
437     } else {
438         xsltGenericError(xsltGenericErrorContext,
439                 "generate-id() : invalid number of args %d\n", nargs);
440         ctxt->error = XPATH_INVALID_ARITY;
441         return;
442     }
443     /*
444      * Okay this is ugly but should work, use the NodePtr address
445      * to forge the ID
446      */
447     val = (unsigned long)((char *)cur - (char *)0);
448     val /= sizeof(xmlNode);
449     sprintf((char *)str, "id%ld", val);
450     valuePush(ctxt, xmlXPathNewString(str));
451 }
452
453 /**
454  * xsltSystemPropertyFunction:
455  * @ctxt:  the XPath Parser context
456  * @nargs:  the number of arguments
457  *
458  * Implement the system-property() XSLT function
459  *   object system-property(string)
460  */
461 void
462 xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
463     xmlXPathObjectPtr obj;
464     xmlChar *prefix, *name;
465     const xmlChar *nsURI = NULL;
466
467     if (nargs != 1) {
468         xsltGenericError(xsltGenericErrorContext,
469                 "system-property() : expects one string arg\n");
470         ctxt->error = XPATH_INVALID_ARITY;
471         return;
472     }
473     if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
474         xsltGenericError(xsltGenericErrorContext,
475             "system-property() : invalid arg expecting a string\n");
476         ctxt->error = XPATH_INVALID_TYPE;
477         return;
478     }
479     obj = valuePop(ctxt);
480     if (obj->stringval == NULL) {
481         valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
482     } else {
483         name = xmlSplitQName2(obj->stringval, &prefix);
484         if (name == NULL) {
485             name = xmlStrdup(obj->stringval);
486         } else {
487             nsURI = xmlXPathNsLookup(ctxt->context, prefix);
488             if (nsURI == NULL) {
489                 xsltGenericError(xsltGenericErrorContext,
490                     "system-property(): prefix %s is not bound\n", prefix);
491             }
492         }
493
494         if (!xmlStrcmp(nsURI, XSLT_NAMESPACE)) {
495             if (!xmlStrcmp(name, (const xmlChar *)"version")) {
496                 valuePush(ctxt, xmlXPathNewString(
497                     (const xmlChar *)XSLT_DEFAULT_VERSION));
498             } else if (!xmlStrcmp(name, (const xmlChar *)"vendor")) {
499                 valuePush(ctxt, xmlXPathNewString(
500                     (const xmlChar *)XSLT_DEFAULT_VENDOR));
501             } else if (!xmlStrcmp(name, (const xmlChar *)"vendor-url")) {
502                 valuePush(ctxt, xmlXPathNewString(
503                     (const xmlChar *)XSLT_DEFAULT_URL));
504             } else {
505                 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
506             }
507         }
508         if (name != NULL)
509             xmlFree(name);
510         if (prefix != NULL)
511             xmlFree(prefix);
512     }
513     xmlXPathFreeObject(obj);
514 }
515
516 /**
517  * xsltElementAvailableFunction:
518  * @ctxt:  the XPath Parser context
519  * @nargs:  the number of arguments
520  *
521  * Implement the element-available() XSLT function
522  *   boolean element-available(string)
523  */
524 void
525 xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
526     xmlXPathObjectPtr obj;
527
528     if (nargs != 1) {
529         xsltGenericError(xsltGenericErrorContext,
530                 "element-available() : expects one string arg\n");
531         ctxt->error = XPATH_INVALID_ARITY;
532         return;
533     }
534     if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
535         xsltGenericError(xsltGenericErrorContext,
536             "element-available invalid arg expecting a string\n");
537         ctxt->error = XPATH_INVALID_TYPE;
538         return;
539     }
540     obj = valuePop(ctxt);
541     xmlXPathFreeObject(obj);
542     valuePush(ctxt, xmlXPathNewBoolean(0));
543 }
544
545 /**
546  * xsltFunctionAvailableFunction:
547  * @ctxt:  the XPath Parser context
548  * @nargs:  the number of arguments
549  *
550  * Implement the function-available() XSLT function
551  *   boolean function-available(string)
552  */
553 void
554 xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
555     xmlXPathObjectPtr obj;
556     xmlChar *prefix, *name;
557     const xmlChar *nsURI = NULL;
558
559     if (nargs != 1) {
560         xsltGenericError(xsltGenericErrorContext,
561                 "function-available() : expects one string arg\n");
562         ctxt->error = XPATH_INVALID_ARITY;
563         return;
564     }
565     if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
566         xsltGenericError(xsltGenericErrorContext,
567             "function-available invalid arg expecting a string\n");
568         ctxt->error = XPATH_INVALID_TYPE;
569         return;
570     }
571     obj = valuePop(ctxt);
572
573     name = xmlSplitQName2(obj->stringval, &prefix);
574     if (name == NULL) {
575         name = xmlStrdup(obj->stringval);
576     } else {
577         nsURI = xmlXPathNsLookup(ctxt->context, prefix);
578         if (nsURI == NULL) {
579             xsltGenericError(xsltGenericErrorContext,
580                 "function-available(): prefix %s is not bound\n", prefix);
581         }
582     }
583
584     if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
585         valuePush(ctxt, xmlXPathNewBoolean(1));
586     } else {
587         valuePush(ctxt, xmlXPathNewBoolean(0));
588     }
589
590     xmlXPathFreeObject(obj);
591     if (name != NULL)
592         xmlFree(name);
593     if (prefix != NULL)
594         xmlFree(prefix);
595 }
596
597 /**
598  * xsltCurrentFunction:
599  * @ctxt:  the XPath Parser context
600  * @nargs:  the number of arguments
601  *
602  * Implement the current() XSLT function
603  *   node-set current()
604  */
605 static void
606 xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
607     xsltTransformContextPtr tctxt;
608
609     if (nargs != 0) {
610         xsltGenericError(xsltGenericErrorContext,
611                 "document() : function uses no argument\n");
612         ctxt->error = XPATH_INVALID_ARITY;
613         return;
614     }
615     tctxt = (xsltTransformContextPtr) ctxt->context->extra;
616     if (tctxt == NULL) {
617         xsltGenericError(xsltGenericErrorContext,
618                 "current() : internal error tctxt == NULL\n");
619         valuePush(ctxt, xmlXPathNewNodeSet(NULL));
620     } else {
621         valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
622     }
623 }
624
625 /**
626  * xsltRegisterAllFunctions:
627  * @ctxt:  the XPath context
628  *
629  * Registers all default XSLT functions in this context
630  */
631 void
632 xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) {
633     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"current",
634                          xsltCurrentFunction);
635     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"document",
636                          xsltDocumentFunction);
637     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"key",
638                          xsltKeyFunction);
639     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"unparsed-entity-uri",
640                          xsltUnparsedEntityURIFunction);
641     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"format-number",
642                          xsltFormatNumberFunction);
643     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"generate-id",
644                          xsltGenerateIdFunction);
645     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"system-property",
646                          xsltSystemPropertyFunction);
647     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"element-available",
648                          xsltElementAvailableFunction);
649     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"function-available",
650                          xsltFunctionAvailableFunction);
651 }