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#if !defined(__FreeBSD__)
247    lmt = gmt - timezone;
248#else	/* FreeBSD DOESN'T have such side-ffect */
249    lmt = gmt - local_tm->tm_gmtoff;
250#endif
251    /*
252     * FIXME: it's been too long since I did manual memory management.
253     * (I swore never to do it again.) Does this introduce a memory leak?
254     */
255    local_tm = localtime(&lmt);
256
257    /*
258     * Now convert local_tm back into a string. This doesn't introduce
259     * a memory leak, so says asctime(3).
260     */
261
262    str = asctime(local_tm);           /* "Tue Jun 26 05:02:16 2001" */
263                                       /*  0123456789 123456789 123 */
264
265    memset(result, 0, sizeof(result)); /* "Thu, 26 Jun 2001" */
266                                       /*  0123456789 12345 */
267
268    strncpy(result, str, 20);
269    strcpy(result+20, "???");          /* tzname doesn't work, fake it */
270    strncpy(result+23, str+19, 5);
271
272    /* Ok, now result contains the string I want to send back. */
273    valuePush(ctxt, xmlXPathNewString((xmlChar *)result));
274}
275#endif
276#endif /* linux or sun */
277
278
279/**
280 * xsltRegisterExtras:
281 * @ctxt:  a XSLT process context
282 *
283 * Registers the built-in extensions. This function is deprecated, use
284 * xsltRegisterAllExtras instead.
285 */
286void
287xsltRegisterExtras(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED) {
288    xsltRegisterAllExtras();
289}
290
291/**
292 * xsltRegisterAllExtras:
293 *
294 * Registers the built-in extensions
295 */
296void
297xsltRegisterAllExtras (void) {
298    xsltRegisterExtModuleFunction((const xmlChar *) "node-set",
299				  XSLT_LIBXSLT_NAMESPACE,
300				  xsltFunctionNodeSet);
301    xsltRegisterExtModuleFunction((const xmlChar *) "node-set",
302				  XSLT_SAXON_NAMESPACE,
303				  xsltFunctionNodeSet);
304    xsltRegisterExtModuleFunction((const xmlChar *) "node-set",
305				  XSLT_XT_NAMESPACE,
306				  xsltFunctionNodeSet);
307#ifdef WITH_LOCALTIME
308    xsltRegisterExtModuleFunction((const xmlChar *) "localTime",
309				  XSLT_NORM_SAXON_NAMESPACE,
310				  xsltFunctionLocalTime);
311#endif
312    xsltRegisterExtModuleElement((const xmlChar *) "debug",
313				 XSLT_LIBXSLT_NAMESPACE,
314				 NULL,
315				 (xsltTransformFunction) xsltDebug);
316    xsltRegisterExtModuleElement((const xmlChar *) "output",
317				 XSLT_SAXON_NAMESPACE,
318				 xsltDocumentComp,
319				 (xsltTransformFunction) xsltDocumentElem);
320    xsltRegisterExtModuleElement((const xmlChar *) "write",
321				 XSLT_XALAN_NAMESPACE,
322				 xsltDocumentComp,
323				 (xsltTransformFunction) xsltDocumentElem);
324    xsltRegisterExtModuleElement((const xmlChar *) "document",
325				 XSLT_XT_NAMESPACE,
326				 xsltDocumentComp,
327				 (xsltTransformFunction) xsltDocumentElem);
328    xsltRegisterExtModuleElement((const xmlChar *) "document",
329				 XSLT_NAMESPACE,
330				 xsltDocumentComp,
331				 (xsltTransformFunction) xsltDocumentElem);
332}
333