1/*
2 * extra.c: Implementation of non-standard features
3 *
4 * Reference:
5 *   Michael Kay "XSLT Programmer's Reference" pp 637-643
6 *   The node-set() extension function
7 *
8 * See Copyright for the status of this software.
9 *
10 * daniel@veillard.com
11 */
12
13#define IN_LIBXSLT
14#include "libxslt.h"
15
16#include <string.h>
17#ifdef HAVE_TIME_H
18#include <time.h>
19#endif
20#ifdef HAVE_STDLIB_H
21#include <stdlib.h>
22#endif
23
24#include <libxml/xmlmemory.h>
25#include <libxml/tree.h>
26#include <libxml/hash.h>
27#include <libxml/xmlerror.h>
28#include <libxml/parserInternals.h>
29#include "xslt.h"
30#include "xsltInternals.h"
31#include "xsltutils.h"
32#include "extensions.h"
33#include "variables.h"
34#include "transform.h"
35#include "extra.h"
36#include "preproc.h"
37
38#ifdef WITH_XSLT_DEBUG
39#define WITH_XSLT_DEBUG_EXTRA
40#endif
41
42/************************************************************************
43 * 									*
44 * 		Handling of XSLT debugging				*
45 * 									*
46 ************************************************************************/
47
48/**
49 * xsltDebug:
50 * @ctxt:  an XSLT processing context
51 * @node:  The current node
52 * @inst:  the instruction in the stylesheet
53 * @comp:  precomputed informations
54 *
55 * Process an debug node
56 */
57void
58xsltDebug(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
59          xmlNodePtr inst ATTRIBUTE_UNUSED,
60          xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
61{
62    int i, j;
63
64    xsltGenericError(xsltGenericErrorContext, "Templates:\n");
65    for (i = 0, j = ctxt->templNr - 1; ((i < 15) && (j >= 0)); i++, j--) {
66        xsltGenericError(xsltGenericErrorContext, "#%d ", i);
67        if (ctxt->templTab[j]->name != NULL)
68            xsltGenericError(xsltGenericErrorContext, "name %s ",
69                             ctxt->templTab[j]->name);
70        if (ctxt->templTab[j]->match != NULL)
71            xsltGenericError(xsltGenericErrorContext, "name %s ",
72                             ctxt->templTab[j]->match);
73        if (ctxt->templTab[j]->mode != NULL)
74            xsltGenericError(xsltGenericErrorContext, "name %s ",
75                             ctxt->templTab[j]->mode);
76        xsltGenericError(xsltGenericErrorContext, "\n");
77    }
78    xsltGenericError(xsltGenericErrorContext, "Variables:\n");
79    for (i = 0, j = ctxt->varsNr - 1; ((i < 15) && (j >= 0)); i++, j--) {
80        xsltStackElemPtr cur;
81
82        if (ctxt->varsTab[j] == NULL)
83            continue;
84        xsltGenericError(xsltGenericErrorContext, "#%d\n", i);
85        cur = ctxt->varsTab[j];
86        while (cur != NULL) {
87            if (cur->comp == NULL) {
88                xsltGenericError(xsltGenericErrorContext,
89                                 "corrupted !!!\n");
90            } else if (cur->comp->type == XSLT_FUNC_PARAM) {
91                xsltGenericError(xsltGenericErrorContext, "param ");
92            } else if (cur->comp->type == XSLT_FUNC_VARIABLE) {
93                xsltGenericError(xsltGenericErrorContext, "var ");
94            }
95            if (cur->name != NULL)
96                xsltGenericError(xsltGenericErrorContext, "%s ",
97                                 cur->name);
98            else
99                xsltGenericError(xsltGenericErrorContext, "noname !!!!");
100#ifdef LIBXML_DEBUG_ENABLED
101            if (cur->value != NULL) {
102                xmlXPathDebugDumpObject(stdout, cur->value, 1);
103            } else {
104                xsltGenericError(xsltGenericErrorContext, "NULL !!!!");
105            }
106#endif
107            xsltGenericError(xsltGenericErrorContext, "\n");
108            cur = cur->next;
109        }
110
111    }
112}
113
114/************************************************************************
115 * 									*
116 * 		Classic extensions as described by M. Kay		*
117 * 									*
118 ************************************************************************/
119
120/**
121 * xsltFunctionNodeSet:
122 * @ctxt:  the XPath Parser context
123 * @nargs:  the number of arguments
124 *
125 * Implement the node-set() XSLT function
126 *   node-set node-set(result-tree)
127 *
128 * This function is available in libxslt, saxon or xt namespace.
129 */
130void
131xsltFunctionNodeSet(xmlXPathParserContextPtr ctxt, int nargs){
132    if (nargs != 1) {
133	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
134		"node-set() : expects one result-tree arg\n");
135	ctxt->error = XPATH_INVALID_ARITY;
136	return;
137    }
138    if ((ctxt->value == NULL) ||
139	((ctxt->value->type != XPATH_XSLT_TREE) &&
140	 (ctxt->value->type != XPATH_NODESET))) {
141	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
142	    "node-set() invalid arg expecting a result tree\n");
143	ctxt->error = XPATH_INVALID_TYPE;
144	return;
145    }
146    if (ctxt->value->type == XPATH_XSLT_TREE) {
147	ctxt->value->type = XPATH_NODESET;
148    }
149}
150
151
152/*
153 * Okay the following really seems unportable and since it's not
154 * part of any standard I'm not too ashamed to do this
155 */
156#if defined(linux) || defined(__sun)
157#if defined(HAVE_MKTIME) && defined(HAVE_LOCALTIME) && defined(HAVE_ASCTIME)
158#define WITH_LOCALTIME
159
160/**
161 * xsltFunctionLocalTime:
162 * @ctxt:  the XPath Parser context
163 * @nargs:  the number of arguments
164 *
165 * Implement the localTime XSLT function used by NORM
166 *   string localTime(???)
167 *
168 * This function is available in Norm's extension namespace
169 * Code (and comments) contributed by Norm
170 */
171static void
172xsltFunctionLocalTime(xmlXPathParserContextPtr ctxt, int nargs) {
173    xmlXPathObjectPtr obj;
174    char *str;
175    char digits[5];
176    char result[29];
177    long int field;
178    time_t gmt, lmt;
179    struct tm gmt_tm;
180    struct tm *local_tm;
181
182    if (nargs != 1) {
183       xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
184                      "localTime() : invalid number of args %d\n", nargs);
185       ctxt->error = XPATH_INVALID_ARITY;
186       return;
187    }
188
189    obj = valuePop(ctxt);
190
191    if (obj->type != XPATH_STRING) {
192	obj = xmlXPathConvertString(obj);
193    }
194    if (obj == NULL) {
195	valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
196	return;
197    }
198
199    str = (char *) obj->stringval;
200
201    /* str = "$Date$" */
202    memset(digits, 0, sizeof(digits));
203    strncpy(digits, str+7, 4);
204    field = strtol(digits, NULL, 10);
205    gmt_tm.tm_year = field - 1900;
206
207    memset(digits, 0, sizeof(digits));
208    strncpy(digits, str+12, 2);
209    field = strtol(digits, NULL, 10);
210    gmt_tm.tm_mon = field - 1;
211
212    memset(digits, 0, sizeof(digits));
213    strncpy(digits, str+15, 2);
214    field = strtol(digits, NULL, 10);
215    gmt_tm.tm_mday = field;
216
217    memset(digits, 0, sizeof(digits));
218    strncpy(digits, str+18, 2);
219    field = strtol(digits, NULL, 10);
220    gmt_tm.tm_hour = field;
221
222    memset(digits, 0, sizeof(digits));
223    strncpy(digits, str+21, 2);
224    field = strtol(digits, NULL, 10);
225    gmt_tm.tm_min = field;
226
227    memset(digits, 0, sizeof(digits));
228    strncpy(digits, str+24, 2);
229    field = strtol(digits, NULL, 10);
230    gmt_tm.tm_sec = field;
231
232    /* Now turn gmt_tm into a time. */
233    gmt = mktime(&gmt_tm);
234
235
236    /*
237     * FIXME: it's been too long since I did manual memory management.
238     * (I swore never to do it again.) Does this introduce a memory leak?
239     */
240    local_tm = localtime(&gmt);
241
242    /*
243     * Calling localtime() has the side-effect of setting timezone.
244     * After we know the timezone, we can adjust for it
245     */
246    lmt = gmt - timezone;
247
248    /*
249     * FIXME: it's been too long since I did manual memory management.
250     * (I swore never to do it again.) Does this introduce a memory leak?
251     */
252    local_tm = localtime(&lmt);
253
254    /*
255     * Now convert local_tm back into a string. This doesn't introduce
256     * a memory leak, so says asctime(3).
257     */
258
259    str = asctime(local_tm);           /* "Tue Jun 26 05:02:16 2001" */
260                                       /*  0123456789 123456789 123 */
261
262    memset(result, 0, sizeof(result)); /* "Thu, 26 Jun 2001" */
263                                       /*  0123456789 12345 */
264
265    strncpy(result, str, 20);
266    strcpy(result+20, "???");          /* tzname doesn't work, fake it */
267    strncpy(result+23, str+19, 5);
268
269    /* Ok, now result contains the string I want to send back. */
270    valuePush(ctxt, xmlXPathNewString((xmlChar *)result));
271}
272#endif
273#endif /* linux or sun */
274
275
276/**
277 * xsltRegisterExtras:
278 * @ctxt:  a XSLT process context
279 *
280 * Registers the built-in extensions. This function is deprecated, use
281 * xsltRegisterAllExtras instead.
282 */
283void
284xsltRegisterExtras(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED) {
285    xsltRegisterAllExtras();
286}
287
288/**
289 * xsltRegisterAllExtras:
290 *
291 * Registers the built-in extensions
292 */
293void
294xsltRegisterAllExtras (void) {
295    xsltRegisterExtModuleFunction((const xmlChar *) "node-set",
296				  XSLT_LIBXSLT_NAMESPACE,
297				  xsltFunctionNodeSet);
298    xsltRegisterExtModuleFunction((const xmlChar *) "node-set",
299				  XSLT_SAXON_NAMESPACE,
300				  xsltFunctionNodeSet);
301    xsltRegisterExtModuleFunction((const xmlChar *) "node-set",
302				  XSLT_XT_NAMESPACE,
303				  xsltFunctionNodeSet);
304#ifdef WITH_LOCALTIME
305    xsltRegisterExtModuleFunction((const xmlChar *) "localTime",
306				  XSLT_NORM_SAXON_NAMESPACE,
307				  xsltFunctionLocalTime);
308#endif
309    xsltRegisterExtModuleElement((const xmlChar *) "debug",
310				 XSLT_LIBXSLT_NAMESPACE,
311				 NULL,
312				 (xsltTransformFunction) xsltDebug);
313    xsltRegisterExtModuleElement((const xmlChar *) "output",
314				 XSLT_SAXON_NAMESPACE,
315				 xsltDocumentComp,
316				 (xsltTransformFunction) xsltDocumentElem);
317    xsltRegisterExtModuleElement((const xmlChar *) "write",
318				 XSLT_XALAN_NAMESPACE,
319				 xsltDocumentComp,
320				 (xsltTransformFunction) xsltDocumentElem);
321    xsltRegisterExtModuleElement((const xmlChar *) "document",
322				 XSLT_XT_NAMESPACE,
323				 xsltDocumentComp,
324				 (xsltTransformFunction) xsltDocumentElem);
325    xsltRegisterExtModuleElement((const xmlChar *) "document",
326				 XSLT_NAMESPACE,
327				 xsltDocumentComp,
328				 (xsltTransformFunction) xsltDocumentElem);
329}
330