dab6bc7eeadb95704298c0dd323c4373fbab3202
[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
40 #define DEBUG_FUNCTION
41
42
43 /************************************************************************
44  *                                                                      *
45  *                      Module interfaces                               *
46  *                                                                      *
47  ************************************************************************/
48
49 /**
50  * xsltDocumentFunction:
51  * @ctxt:  the XPath Parser context
52  * @nargs:  the number of arguments
53  *
54  * Implement the document() XSLT function
55  *   node-set document(object, node-set?)
56  */
57 void
58 xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs){
59     xmlDocPtr doc;
60     xmlXPathObjectPtr obj;
61     xmlChar *base, *URI;
62
63
64     if ((nargs < 1) || (nargs > 2)) {
65         xsltGenericError(xsltGenericErrorContext,
66                 "document() : invalid number of args %d\n", nargs);
67         ctxt->error = XPATH_INVALID_ARITY;
68         return;
69     }
70     if (ctxt->value == NULL) {
71         xsltGenericError(xsltGenericErrorContext,
72             "document() : invalid arg value\n");
73         ctxt->error = XPATH_INVALID_TYPE;
74         return;
75     }
76     if (ctxt->value->type == XPATH_NODESET) {
77         TODO
78         xsltGenericError(xsltGenericErrorContext,
79                 "document() : with node-sets args not yet supported\n");
80         return;
81     }
82     /*
83      * Make sure it's converted to a string
84      */
85     xmlXPathStringFunction(ctxt, 1);
86     if (ctxt->value->type != XPATH_STRING) {
87         xsltGenericError(xsltGenericErrorContext,
88             "document() : invalid arg expecting a string\n");
89         ctxt->error = XPATH_INVALID_TYPE;
90         return;
91     }
92     obj = valuePop(ctxt);
93     if (obj->stringval == NULL) {
94         valuePush(ctxt, xmlXPathNewNodeSet(NULL));
95     } else {
96         base = xmlNodeGetBase(ctxt->context->doc, ctxt->context->node);
97         URI = xmlBuildURI(obj->stringval, base);
98         if (base != NULL)
99             xmlFree(base);
100         if (URI == NULL) {
101             valuePush(ctxt, xmlXPathNewNodeSet(NULL));
102         } else {
103             doc = xmlParseDoc(URI);
104             if (doc == NULL)
105                 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
106             else {
107                 xsltTransformContextPtr tctxt;
108
109                 /*
110                  * link it to the context for cleanup when done
111                  */
112                 tctxt = (xsltTransformContextPtr) ctxt->context->extra;
113                 if (tctxt == NULL) {
114                     xsltGenericError(xsltGenericErrorContext,
115                         "document() : internal error tctxt == NULL\n");
116                     xmlFreeDoc(doc);
117                     valuePush(ctxt, xmlXPathNewNodeSet(NULL));
118                 } else {
119                     /*
120                      * Keep a link from the context to be able to deallocate
121                      */
122                     doc->next = (xmlNodePtr) tctxt->extraDocs;
123                     tctxt->extraDocs = doc;
124
125                     /* TODO: use XPointer of HTML location for fragment ID */
126                     /* pbm #xxx can lead to location sets, not nodesets :-) */
127                     valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc));
128                 }
129             }
130         }
131     }
132     xmlXPathFreeObject(obj);
133 }
134
135 /**
136  * xsltKeyFunction:
137  * @ctxt:  the XPath Parser context
138  * @nargs:  the number of arguments
139  *
140  * Implement the key() XSLT function
141  *   node-set key(string, object)
142  */
143 void
144 xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
145     TODO /* function */
146 }
147
148 /**
149  * xsltUnparsedEntityURIFunction:
150  * @ctxt:  the XPath Parser context
151  * @nargs:  the number of arguments
152  *
153  * Implement the unparsed-entity-uri() XSLT function
154  *   string unparsed-entity-uri(string)
155  */
156 void
157 xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
158     xmlXPathObjectPtr obj;
159     xmlChar *str;
160
161     if (nargs != 1) {
162         xsltGenericError(xsltGenericErrorContext,
163                 "system-property() : expects one string arg\n");
164         ctxt->error = XPATH_INVALID_ARITY;
165         return;
166     }
167     if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
168         xsltGenericError(xsltGenericErrorContext,
169             "generate-id() : invalid arg expecting a string\n");
170         ctxt->error = XPATH_INVALID_TYPE;
171         return;
172     }
173     obj = valuePop(ctxt);
174     str = obj->stringval;
175     if (str == NULL) {
176         valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
177     } else {
178         xmlEntityPtr entity;
179
180         entity = xmlGetDocEntity(ctxt->context->doc, str);
181         if (entity == NULL) {
182             valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
183         } else {
184             if (entity->URI != NULL)
185                 valuePush(ctxt, xmlXPathNewString(entity->URI));
186             else
187                 valuePush(ctxt, xmlXPathNewString(
188                             xmlStrdup((const xmlChar *)"")));
189         }
190     }
191     xmlXPathFreeObject(obj);
192 }
193
194 /**
195  * xsltFormatNumberFunction:
196  * @ctxt:  the XPath Parser context
197  * @nargs:  the number of arguments
198  *
199  * Implement the format-number() XSLT function
200  *   string format-number(number, string, string?)
201  */
202 void
203 xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
204 {
205     xmlXPathObjectPtr numberObj = NULL;
206     xmlXPathObjectPtr formatObj = NULL;
207     xmlXPathObjectPtr decimalObj = NULL;
208     xsltStylesheetPtr sheet;
209     xsltDecimalFormatPtr formatValues;
210     xmlChar *result;
211
212     sheet = ((xsltTransformContextPtr)ctxt->context->extra)->style;
213     formatValues = sheet->decimalFormat;
214     
215     switch (nargs) {
216     case 3:
217         CAST_TO_STRING;
218         decimalObj = valuePop(ctxt);
219         formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval);
220         /* Intentional fall-through */
221     case 2:
222         CAST_TO_STRING;
223         formatObj = valuePop(ctxt);
224         CAST_TO_NUMBER;
225         numberObj = valuePop(ctxt);
226         break;
227     default:
228         XP_ERROR(XPATH_INVALID_ARITY);
229         return;
230     }
231
232     if (xsltFormatNumberConversion(formatValues,
233                                    formatObj->stringval,
234                                    numberObj->floatval,
235                                    &result) == XPATH_EXPRESSION_OK) {
236         valuePush(ctxt, xmlXPathNewString(result));
237         xmlFree(result);
238     }
239     
240     xmlXPathFreeObject(numberObj);
241     xmlXPathFreeObject(formatObj);
242     xmlXPathFreeObject(decimalObj);
243 }
244
245 /**
246  * xsltGenerateIdFunction:
247  * @ctxt:  the XPath Parser context
248  * @nargs:  the number of arguments
249  *
250  * Implement the generate-id() XSLT function
251  *   string generate-id(node-set?)
252  */
253 void
254 xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
255     xmlNodePtr cur = NULL;
256     unsigned long val;
257     xmlChar str[20];
258
259     if (nargs == 0) {
260         cur = ctxt->context->node;
261     } else if (nargs == 1) {
262         xmlXPathObjectPtr obj;
263         xmlNodeSetPtr nodelist;
264         int i, ret;
265
266         if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
267             ctxt->error = XPATH_INVALID_TYPE;
268             xsltGenericError(xsltGenericErrorContext,
269                 "generate-id() : invalid arg expecting a node-set\n");
270             return;
271         }
272         obj = valuePop(ctxt);
273         nodelist = obj->nodesetval;
274         if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
275             ctxt->error = XPATH_INVALID_TYPE;
276             xsltGenericError(xsltGenericErrorContext,
277                 "generate-id() : got an empty node-set\n");
278             xmlXPathFreeObject(obj);
279             return;
280         }
281         cur = nodelist->nodeTab[0];
282         for (i = 2;i <= nodelist->nodeNr;i++) {
283             ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
284             if (ret == -1)
285                 cur = nodelist->nodeTab[i];
286         }
287         xmlXPathFreeObject(obj);
288     } else {
289         xsltGenericError(xsltGenericErrorContext,
290                 "generate-id() : invalid number of args %d\n", nargs);
291         ctxt->error = XPATH_INVALID_ARITY;
292         return;
293     }
294     /*
295      * Okay this is ugly but should work, use the NodePtr address
296      * to forge the ID
297      */
298     val = (unsigned long)((char *)cur - (char *)0);
299     val /= sizeof(xmlNode);
300     val |= 0xFFFFFF;
301     sprintf((char *)str, "id%10ld", val);
302     valuePush(ctxt, xmlXPathNewString(str));
303 }
304
305 /**
306  * xsltSystemPropertyFunction:
307  * @ctxt:  the XPath Parser context
308  * @nargs:  the number of arguments
309  *
310  * Implement the system-property() XSLT function
311  *   object system-property(string)
312  */
313 void
314 xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
315     xmlXPathObjectPtr obj;
316     xmlChar *str;
317
318     if (nargs != 1) {
319         xsltGenericError(xsltGenericErrorContext,
320                 "system-property() : expects one string arg\n");
321         ctxt->error = XPATH_INVALID_ARITY;
322         return;
323     }
324     if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
325         xsltGenericError(xsltGenericErrorContext,
326             "generate-id() : invalid arg expecting a string\n");
327         ctxt->error = XPATH_INVALID_TYPE;
328         return;
329     }
330     obj = valuePop(ctxt);
331     str = obj->stringval;
332     if (str == NULL) {
333         valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
334     } else if (!xmlStrcmp(str, (const xmlChar *)"xsl:version")) {
335         valuePush(ctxt, xmlXPathNewString(
336                 (const xmlChar *)XSLT_DEFAULT_VERSION));
337     } else if (!xmlStrcmp(str, (const xmlChar *)"xsl:vendor")) {
338         valuePush(ctxt, xmlXPathNewString(
339                 (const xmlChar *)XSLT_DEFAULT_VENDOR));
340     } else if (!xmlStrcmp(str, (const xmlChar *)"xsl:vendor-url")) {
341         valuePush(ctxt, xmlXPathNewString(
342                 (const xmlChar *)XSLT_DEFAULT_URL));
343     } else {
344         /* TODO cheated with the QName resolution */
345         valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
346     }
347     xmlXPathFreeObject(obj);
348 }
349
350 /**
351  * xsltElementAvailableFunction:
352  * @ctxt:  the XPath Parser context
353  * @nargs:  the number of arguments
354  *
355  * Implement the element-available() XSLT function
356  *   boolean element-available(string)
357  */
358 void
359 xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
360     xmlXPathObjectPtr obj;
361
362     if (nargs != 1) {
363         xsltGenericError(xsltGenericErrorContext,
364                 "element-available() : expects one string arg\n");
365         ctxt->error = XPATH_INVALID_ARITY;
366         return;
367     }
368     if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
369         xsltGenericError(xsltGenericErrorContext,
370             "element-available invalid arg expecting a string\n");
371         ctxt->error = XPATH_INVALID_TYPE;
372         return;
373     }
374     obj = valuePop(ctxt);
375     xmlXPathFreeObject(obj);
376     valuePush(ctxt, xmlXPathNewBoolean(0));
377 }
378
379 /**
380  * xsltFunctionAvailableFunction:
381  * @ctxt:  the XPath Parser context
382  * @nargs:  the number of arguments
383  *
384  * Implement the function-available() XSLT function
385  *   boolean function-available(string)
386  */
387 void
388 xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
389     xmlXPathObjectPtr obj;
390
391     if (nargs != 1) {
392         xsltGenericError(xsltGenericErrorContext,
393                 "function-available() : expects one string arg\n");
394         ctxt->error = XPATH_INVALID_ARITY;
395         return;
396     }
397     if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
398         xsltGenericError(xsltGenericErrorContext,
399             "function-available invalid arg expecting a string\n");
400         ctxt->error = XPATH_INVALID_TYPE;
401         return;
402     }
403     obj = valuePop(ctxt);
404     xmlXPathFreeObject(obj);
405     valuePush(ctxt, xmlXPathNewBoolean(0));
406 }
407
408 /**
409  * xsltCurrentFunction:
410  * @ctxt:  the XPath Parser context
411  * @nargs:  the number of arguments
412  *
413  * Implement the current() XSLT function
414  *   node-set current()
415  */
416 void
417 xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
418     if (nargs != 0) {
419         xsltGenericError(xsltGenericErrorContext,
420                 "document() : function uses no argument\n");
421         ctxt->error = XPATH_INVALID_ARITY;
422         return;
423     }
424     valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
425 }
426
427 /**
428  * xsltRegisterAllFunctions:
429  * @ctxt:  the XPath context
430  *
431  * Registers all default XSLT functions in this context
432  */
433 void
434 xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) {
435     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"current",
436                          xsltCurrentFunction);
437     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"document",
438                          xsltDocumentFunction);
439     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"key",
440                          xsltKeyFunction);
441     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"unparsed-entity-uri",
442                          xsltUnparsedEntityURIFunction);
443     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"format-number",
444                          xsltFormatNumberFunction);
445     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"generate-id",
446                          xsltGenerateIdFunction);
447     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"system-property",
448                          xsltSystemPropertyFunction);
449     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"element-available",
450                          xsltElementAvailableFunction);
451     xmlXPathRegisterFunc(ctxt, (const xmlChar *)"function-available",
452                          xsltFunctionAvailableFunction);
453 }