1#define IN_LIBEXSLT
2#include "libexslt/libexslt.h"
3
4#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
5#include <win32config.h>
6#else
7#include "config.h"
8#endif
9
10#include <libxml/tree.h>
11#include <libxml/xpath.h>
12#include <libxml/xpathInternals.h>
13#include <libxml/parser.h>
14#include <libxml/hash.h>
15
16#include <libxslt/xsltconfig.h>
17#include <libxslt/xsltutils.h>
18#include <libxslt/xsltInternals.h>
19#include <libxslt/extensions.h>
20
21#include "exslt.h"
22
23/**
24 * exsltSaxonInit:
25 * @ctxt: an XSLT transformation context
26 * @URI: the namespace URI for the extension
27 *
28 * Initializes the SAXON module.
29 *
30 * Returns the data for this transformation
31 */
32static xmlHashTablePtr
33exsltSaxonInit (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
34		const xmlChar *URI ATTRIBUTE_UNUSED) {
35    return xmlHashCreate(1);
36}
37
38/**
39 * exsltSaxonShutdown:
40 * @ctxt: an XSLT transformation context
41 * @URI: the namespace URI for the extension
42 * @data: the module data to free up
43 *
44 * Shutdown the SAXON extension module
45 */
46static void
47exsltSaxonShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
48		    const xmlChar *URI ATTRIBUTE_UNUSED,
49		    xmlHashTablePtr data) {
50    xmlHashFree(data, (xmlHashDeallocator) xmlXPathFreeCompExpr);
51}
52
53
54/**
55 * exsltSaxonExpressionFunction:
56 * @ctxt: an XPath parser context
57 * @nargs: the number of arguments
58 *
59 * The supplied string must contain an XPath expression. The result of
60 * the function is a stored expression, which may be supplied as an
61 * argument to other extension functions such as saxon:eval(),
62 * saxon:sum() and saxon:distinct(). The result of the expression will
63 * usually depend on the current node. The expression may contain
64 * references to variables that are in scope at the point where
65 * saxon:expression() is called: these variables will be replaced in
66 * the stored expression with the values they take at the time
67 * saxon:expression() is called, not the values of the variables at
68 * the time the stored expression is evaluated.  Similarly, if the
69 * expression contains namespace prefixes, these are interpreted in
70 * terms of the namespace declarations in scope at the point where the
71 * saxon:expression() function is called, not those in scope where the
72 * stored expression is evaluated.
73 *
74 * TODO: current implementation doesn't conform to SAXON behaviour
75 * regarding context and namespaces.
76 */
77static void
78exsltSaxonExpressionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
79    xmlChar *arg;
80    xmlXPathCompExprPtr ret;
81    xmlHashTablePtr hash;
82    xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
83
84    if (nargs != 1) {
85	xmlXPathSetArityError(ctxt);
86	return;
87    }
88
89    arg = xmlXPathPopString(ctxt);
90    if (xmlXPathCheckError(ctxt) || (arg == NULL)) {
91	xmlXPathSetTypeError(ctxt);
92	return;
93    }
94
95    hash = (xmlHashTablePtr) xsltGetExtData(tctxt,
96					    ctxt->context->functionURI);
97
98    ret = xmlHashLookup(hash, arg);
99
100    if (ret == NULL) {
101	 ret = xmlXPathCompile(arg);
102	 if (ret == NULL) {
103	      xmlFree(arg);
104	      xsltGenericError(xsltGenericErrorContext,
105			"{%s}:%s: argument is not an XPath expression\n",
106			ctxt->context->functionURI, ctxt->context->function);
107	      return;
108	 }
109	 xmlHashAddEntry(hash, arg, (void *) ret);
110    }
111
112    xmlFree(arg);
113
114    xmlXPathReturnExternal(ctxt, ret);
115}
116
117/**
118 * exsltSaxonEvalFunction:
119 * @ctxt:  an XPath parser context
120 * @nargs:  number of arguments
121 *
122 * Implements de SAXON eval() function:
123 *    object saxon:eval (saxon:stored-expression)
124 * Returns the result of evaluating the supplied stored expression.
125 * A stored expression may be obtained as the result of calling
126 * the saxon:expression() function.
127 * The stored expression is evaluated in the current context, that
128 * is, the context node is the current node, and the context position
129 * and context size are the same as the result of calling position()
130 * or last() respectively.
131 */
132static void
133exsltSaxonEvalFunction (xmlXPathParserContextPtr ctxt, int nargs) {
134     xmlXPathCompExprPtr expr;
135     xmlXPathObjectPtr ret;
136
137     if (nargs != 1) {
138	  xmlXPathSetArityError(ctxt);
139	  return;
140     }
141
142     if (!xmlXPathStackIsExternal(ctxt)) {
143	  xmlXPathSetTypeError(ctxt);
144	  return;
145     }
146
147     expr = (xmlXPathCompExprPtr) xmlXPathPopExternal(ctxt);
148
149     ret = xmlXPathCompiledEval(expr, ctxt->context);
150
151     valuePush(ctxt, ret);
152}
153
154/**
155 * exsltSaxonEvaluateFunction:
156 * @ctxt:  an XPath parser context
157 * @nargs: number of arguments
158 *
159 * Implements the SAXON evaluate() function
160 *     object saxon:evaluate (string)
161 * The supplied string must contain an XPath expression. The result of
162 * the function is the result of evaluating the XPath expression. This
163 * is useful where an expression needs to be constructed at run-time or
164 * passed to the stylesheet as a parameter, for example where the sort
165 * key is determined dynamically. The context for the expression (e.g.
166 * which variables and namespaces are available) is exactly the same as
167 * if the expression were written explicitly at this point in the
168 * stylesheet. The function saxon:evaluate(string) is shorthand for
169 * saxon:eval(saxon:expression(string)).
170 */
171static void
172exsltSaxonEvaluateFunction (xmlXPathParserContextPtr ctxt, int nargs) {
173     if (nargs != 1) {
174	  xmlXPathSetArityError(ctxt);
175	  return;
176     }
177
178     exsltSaxonExpressionFunction(ctxt, 1);
179     exsltSaxonEvalFunction(ctxt, 1);
180}
181
182/**
183 * exsltSaxonLineNumberFunction:
184 * @ctxt:  an XPath parser context
185 * @nargs: number of arguments
186 *
187 * Implements the SAXON line-number() function
188 *     integer saxon:line-number()
189 *
190 * This returns the line number of the context node in the source document
191 * within the entity that contains it. There are no arguments. If line numbers
192 * are not maintained for the current document, the function returns -1. (To
193 * ensure that line numbers are maintained, use the -l option on the command
194 * line)
195 *
196 * The extension has been extended to have the following form:
197 *     integer saxon:line-number([node-set-1])
198 * If a node-set is given, this extension will return the line number of the
199 * node in the argument node-set that is first in document order.
200 */
201static void
202exsltSaxonLineNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
203    xmlNodePtr cur = NULL;
204
205    if (nargs == 0) {
206	cur = ctxt->context->node;
207    } else if (nargs == 1) {
208	xmlXPathObjectPtr obj;
209	xmlNodeSetPtr nodelist;
210	int i;
211
212	if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
213	    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
214		"saxon:line-number() : invalid arg expecting a node-set\n");
215	    ctxt->error = XPATH_INVALID_TYPE;
216	    return;
217	}
218
219	obj = valuePop(ctxt);
220	nodelist = obj->nodesetval;
221	if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
222	    xmlXPathFreeObject(obj);
223	    valuePush(ctxt, xmlXPathNewFloat(-1));
224	    return;
225	}
226	cur = nodelist->nodeTab[0];
227	for (i = 1;i < nodelist->nodeNr;i++) {
228	    int ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
229	    if (ret == -1)
230		cur = nodelist->nodeTab[i];
231	}
232	xmlXPathFreeObject(obj);
233    } else {
234	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
235		"saxon:line-number() : invalid number of args %d\n",
236		nargs);
237	ctxt->error = XPATH_INVALID_ARITY;
238	return;
239    }
240
241    valuePush(ctxt, xmlXPathNewFloat(xmlGetLineNo(cur)));
242    return;
243}
244
245/**
246 * exsltSaxonRegister:
247 *
248 * Registers the SAXON extension module
249 */
250void
251exsltSaxonRegister (void) {
252     xsltRegisterExtModule (SAXON_NAMESPACE,
253			    (xsltExtInitFunction) exsltSaxonInit,
254			    (xsltExtShutdownFunction) exsltSaxonShutdown);
255     xsltRegisterExtModuleFunction((const xmlChar *) "expression",
256				   SAXON_NAMESPACE,
257				   exsltSaxonExpressionFunction);
258     xsltRegisterExtModuleFunction((const xmlChar *) "eval",
259				   SAXON_NAMESPACE,
260				   exsltSaxonEvalFunction);
261     xsltRegisterExtModuleFunction((const xmlChar *) "evaluate",
262				   SAXON_NAMESPACE,
263				   exsltSaxonEvaluateFunction);
264    xsltRegisterExtModuleFunction ((const xmlChar *) "line-number",
265				   SAXON_NAMESPACE,
266				   exsltSaxonLineNumberFunction);
267}
268