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 * exsltSaxonSystemIdFunction: 184 * @ctxt: an XPath parser context 185 * @nargs: number of arguments 186 * 187 * Implements the SAXON systemId() function 188 * string saxon:systemId () 189 * This function returns the system ID of the document being styled. 190 */ 191static void 192exsltSaxonSystemIdFunction(xmlXPathParserContextPtr ctxt, int nargs) 193{ 194 if (ctxt == NULL) 195 return; 196 if (nargs != 0) { 197 xmlXPathSetArityError(ctxt); 198 return; 199 } 200 201 if ((ctxt->context) && (ctxt->context->doc) && 202 (ctxt->context->doc->URL)) 203 valuePush(ctxt, xmlXPathNewString(ctxt->context->doc->URL)); 204 else 205 valuePush(ctxt, xmlXPathNewString(BAD_CAST "")); 206} 207 208/** 209 * exsltSaxonLineNumberFunction: 210 * @ctxt: an XPath parser context 211 * @nargs: number of arguments 212 * 213 * Implements the SAXON line-number() function 214 * integer saxon:line-number() 215 * 216 * This returns the line number of the context node in the source document 217 * within the entity that contains it. There are no arguments. If line numbers 218 * are not maintained for the current document, the function returns -1. (To 219 * ensure that line numbers are maintained, use the -l option on the command 220 * line) 221 * 222 * The extension has been extended to have the following form: 223 * integer saxon:line-number([node-set-1]) 224 * If a node-set is given, this extension will return the line number of the 225 * node in the argument node-set that is first in document order. 226 */ 227static void 228exsltSaxonLineNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) { 229 xmlNodePtr cur = NULL; 230 231 if (nargs == 0) { 232 cur = ctxt->context->node; 233 } else if (nargs == 1) { 234 xmlXPathObjectPtr obj; 235 xmlNodeSetPtr nodelist; 236 int i; 237 238 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) { 239 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 240 "saxon:line-number() : invalid arg expecting a node-set\n"); 241 ctxt->error = XPATH_INVALID_TYPE; 242 return; 243 } 244 245 obj = valuePop(ctxt); 246 nodelist = obj->nodesetval; 247 if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) { 248 xmlXPathFreeObject(obj); 249 valuePush(ctxt, xmlXPathNewFloat(-1)); 250 return; 251 } 252 cur = nodelist->nodeTab[0]; 253 for (i = 1;i < nodelist->nodeNr;i++) { 254 int ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]); 255 if (ret == -1) 256 cur = nodelist->nodeTab[i]; 257 } 258 xmlXPathFreeObject(obj); 259 } else { 260 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, 261 "saxon:line-number() : invalid number of args %d\n", 262 nargs); 263 ctxt->error = XPATH_INVALID_ARITY; 264 return; 265 } 266 267 valuePush(ctxt, xmlXPathNewFloat(xmlGetLineNo(cur))); 268 return; 269} 270 271/** 272 * exsltSaxonRegister: 273 * 274 * Registers the SAXON extension module 275 */ 276void 277exsltSaxonRegister (void) { 278 xsltRegisterExtModule (SAXON_NAMESPACE, 279 (xsltExtInitFunction) exsltSaxonInit, 280 (xsltExtShutdownFunction) exsltSaxonShutdown); 281 xsltRegisterExtModuleFunction((const xmlChar *) "expression", 282 SAXON_NAMESPACE, 283 exsltSaxonExpressionFunction); 284 xsltRegisterExtModuleFunction((const xmlChar *) "eval", 285 SAXON_NAMESPACE, 286 exsltSaxonEvalFunction); 287 xsltRegisterExtModuleFunction((const xmlChar *) "evaluate", 288 SAXON_NAMESPACE, 289 exsltSaxonEvaluateFunction); 290 xsltRegisterExtModuleFunction ((const xmlChar *) "line-number", 291 SAXON_NAMESPACE, 292 exsltSaxonLineNumberFunction); 293 xsltRegisterExtModuleFunction ((const xmlChar *) "systemId", 294 SAXON_NAMESPACE, 295 exsltSaxonSystemIdFunction); 296} 297