OLD | NEW |
| (Empty) |
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 <string.h> | |
11 | |
12 #include <libxml/tree.h> | |
13 #include <libxml/xpath.h> | |
14 #include <libxml/xpathInternals.h> | |
15 #include <libxml/hash.h> | |
16 #include <libxml/debugXML.h> | |
17 | |
18 #include <libxslt/xsltutils.h> | |
19 #include <libxslt/variables.h> | |
20 #include <libxslt/xsltInternals.h> | |
21 #include <libxslt/extensions.h> | |
22 #include <libxslt/transform.h> | |
23 #include <libxslt/imports.h> | |
24 | |
25 #include "exslt.h" | |
26 | |
27 typedef struct _exsltFuncFunctionData exsltFuncFunctionData; | |
28 struct _exsltFuncFunctionData { | |
29 int nargs; /* number of arguments to the function */ | |
30 xmlNodePtr content; /* the func:fuction template content */ | |
31 }; | |
32 | |
33 typedef struct _exsltFuncData exsltFuncData; | |
34 struct _exsltFuncData { | |
35 xmlHashTablePtr funcs; /* pointer to the stylesheet module data */ | |
36 xmlXPathObjectPtr result; /* returned by func:result */ | |
37 int error; /* did an error occur? */ | |
38 }; | |
39 | |
40 typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp; | |
41 struct _exsltFuncResultPreComp { | |
42 xsltElemPreComp comp; | |
43 xmlXPathCompExprPtr select; | |
44 xmlNsPtr *nsList; | |
45 int nsNr; | |
46 }; | |
47 | |
48 /* Used for callback function in exsltInitFunc */ | |
49 typedef struct _exsltFuncImportRegData exsltFuncImportRegData; | |
50 struct _exsltFuncImportRegData { | |
51 xsltTransformContextPtr ctxt; | |
52 xmlHashTablePtr hash; | |
53 }; | |
54 | |
55 static void exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, | |
56 int nargs); | |
57 static exsltFuncFunctionData *exsltFuncNewFunctionData(void); | |
58 | |
59 /*static const xmlChar *exsltResultDataID = (const xmlChar *) "EXSLT Result";*/ | |
60 | |
61 /** | |
62 * exsltFuncRegisterFunc: | |
63 * @func: the #exsltFuncFunctionData for the function | |
64 * @ctxt: an XSLT transformation context | |
65 * @URI: the function namespace URI | |
66 * @name: the function name | |
67 * | |
68 * Registers a function declared by a func:function element | |
69 */ | |
70 static void | |
71 exsltFuncRegisterFunc (exsltFuncFunctionData *data, | |
72 xsltTransformContextPtr ctxt, | |
73 const xmlChar *URI, const xmlChar *name, | |
74 ATTRIBUTE_UNUSED const xmlChar *ignored) { | |
75 if ((data == NULL) || (ctxt == NULL) || (URI == NULL) || (name == NULL)) | |
76 return; | |
77 | |
78 xsltGenericDebug(xsltGenericDebugContext, | |
79 "exsltFuncRegisterFunc: register {%s}%s\n", | |
80 URI, name); | |
81 xsltRegisterExtFunction(ctxt, name, URI, | |
82 exsltFuncFunctionFunction); | |
83 } | |
84 | |
85 /* | |
86 * exsltFuncRegisterImportFunc | |
87 * @data: the exsltFuncFunctionData for the function | |
88 * @ch: structure containing context and hash table | |
89 * @URI: the function namespace URI | |
90 * @name: the function name | |
91 * | |
92 * Checks if imported function is already registered in top-level | |
93 * stylesheet. If not, copies function data and registers function | |
94 */ | |
95 static void | |
96 exsltFuncRegisterImportFunc (exsltFuncFunctionData *data, | |
97 exsltFuncImportRegData *ch, | |
98 const xmlChar *URI, const xmlChar *name, | |
99 ATTRIBUTE_UNUSED const xmlChar *ignored) { | |
100 exsltFuncFunctionData *func=NULL; | |
101 | |
102 if ((data == NULL) || (ch == NULL) || (URI == NULL) || (name == NULL)) | |
103 return; | |
104 | |
105 if (ch->ctxt == NULL || ch->hash == NULL) | |
106 return; | |
107 | |
108 /* Check if already present */ | |
109 func = (exsltFuncFunctionData*)xmlHashLookup2(ch->hash, URI, name); | |
110 if (func == NULL) { /* Not yet present - copy it in */ | |
111 func = exsltFuncNewFunctionData(); | |
112 if (func == NULL) | |
113 return; | |
114 memcpy(func, data, sizeof(exsltFuncFunctionData)); | |
115 if (xmlHashAddEntry2(ch->hash, URI, name, func) < 0) { | |
116 xsltGenericError(xsltGenericErrorContext, | |
117 "Failed to register function {%s}%s\n", | |
118 URI, name); | |
119 } else { /* Do the registration */ | |
120 xsltGenericDebug(xsltGenericDebugContext, | |
121 "exsltFuncRegisterImportFunc: register {%s}%s\n", | |
122 URI, name); | |
123 xsltRegisterExtFunction(ch->ctxt, name, URI, | |
124 exsltFuncFunctionFunction); | |
125 } | |
126 } | |
127 } | |
128 | |
129 /** | |
130 * exsltFuncInit: | |
131 * @ctxt: an XSLT transformation context | |
132 * @URI: the namespace URI for the extension | |
133 * | |
134 * Initializes the EXSLT - Functions module. | |
135 * Called at transformation-time; merges all | |
136 * functions declared in the import tree taking | |
137 * import precedence into account, i.e. overriding | |
138 * functions with lower import precedence. | |
139 * | |
140 * Returns the data for this transformation | |
141 */ | |
142 static exsltFuncData * | |
143 exsltFuncInit (xsltTransformContextPtr ctxt, const xmlChar *URI) { | |
144 exsltFuncData *ret; | |
145 xsltStylesheetPtr tmp; | |
146 exsltFuncImportRegData ch; | |
147 xmlHashTablePtr hash; | |
148 | |
149 ret = (exsltFuncData *) xmlMalloc (sizeof(exsltFuncData)); | |
150 if (ret == NULL) { | |
151 xsltGenericError(xsltGenericErrorContext, | |
152 "exsltFuncInit: not enough memory\n"); | |
153 return(NULL); | |
154 } | |
155 memset(ret, 0, sizeof(exsltFuncData)); | |
156 | |
157 ret->result = NULL; | |
158 ret->error = 0; | |
159 | |
160 ch.hash = (xmlHashTablePtr) xsltStyleGetExtData(ctxt->style, URI); | |
161 ret->funcs = ch.hash; | |
162 xmlHashScanFull(ch.hash, (xmlHashScannerFull) exsltFuncRegisterFunc, ctxt); | |
163 tmp = ctxt->style; | |
164 ch.ctxt = ctxt; | |
165 while ((tmp=xsltNextImport(tmp))!=NULL) { | |
166 hash = xsltGetExtInfo(tmp, URI); | |
167 if (hash != NULL) { | |
168 xmlHashScanFull(hash, | |
169 (xmlHashScannerFull) exsltFuncRegisterImportFunc, &ch); | |
170 } | |
171 } | |
172 | |
173 return(ret); | |
174 } | |
175 | |
176 /** | |
177 * exsltFuncShutdown: | |
178 * @ctxt: an XSLT transformation context | |
179 * @URI: the namespace URI for the extension | |
180 * @data: the module data to free up | |
181 * | |
182 * Shutdown the EXSLT - Functions module | |
183 * Called at transformation-time. | |
184 */ | |
185 static void | |
186 exsltFuncShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, | |
187 const xmlChar *URI ATTRIBUTE_UNUSED, | |
188 exsltFuncData *data) { | |
189 if (data->result != NULL) | |
190 xmlXPathFreeObject(data->result); | |
191 xmlFree(data); | |
192 } | |
193 | |
194 /** | |
195 * exsltFuncStyleInit: | |
196 * @style: an XSLT stylesheet | |
197 * @URI: the namespace URI for the extension | |
198 * | |
199 * Allocates the stylesheet data for EXSLT - Function | |
200 * Called at compile-time. | |
201 * | |
202 * Returns the allocated data | |
203 */ | |
204 static xmlHashTablePtr | |
205 exsltFuncStyleInit (xsltStylesheetPtr style ATTRIBUTE_UNUSED, | |
206 const xmlChar *URI ATTRIBUTE_UNUSED) { | |
207 return xmlHashCreate(1); | |
208 } | |
209 | |
210 /** | |
211 * exsltFuncStyleShutdown: | |
212 * @style: an XSLT stylesheet | |
213 * @URI: the namespace URI for the extension | |
214 * @data: the stylesheet data to free up | |
215 * | |
216 * Shutdown the EXSLT - Function module | |
217 * Called at compile-time. | |
218 */ | |
219 static void | |
220 exsltFuncStyleShutdown (xsltStylesheetPtr style ATTRIBUTE_UNUSED, | |
221 const xmlChar *URI ATTRIBUTE_UNUSED, | |
222 xmlHashTablePtr data) { | |
223 xmlHashFree(data, (xmlHashDeallocator) xmlFree); | |
224 } | |
225 | |
226 /** | |
227 * exsltFuncNewFunctionData: | |
228 * | |
229 * Allocates an #exslFuncFunctionData object | |
230 * | |
231 * Returns the new structure | |
232 */ | |
233 static exsltFuncFunctionData * | |
234 exsltFuncNewFunctionData (void) { | |
235 exsltFuncFunctionData *ret; | |
236 | |
237 ret = (exsltFuncFunctionData *) xmlMalloc (sizeof(exsltFuncFunctionData)); | |
238 if (ret == NULL) { | |
239 xsltGenericError(xsltGenericErrorContext, | |
240 "exsltFuncNewFunctionData: not enough memory\n"); | |
241 return (NULL); | |
242 } | |
243 memset(ret, 0, sizeof(exsltFuncFunctionData)); | |
244 | |
245 ret->nargs = 0; | |
246 ret->content = NULL; | |
247 | |
248 return(ret); | |
249 } | |
250 | |
251 /** | |
252 * exsltFreeFuncResultPreComp: | |
253 * @comp: the #exsltFuncResultPreComp to free up | |
254 * | |
255 * Deallocates an #exsltFuncResultPreComp | |
256 */ | |
257 static void | |
258 exsltFreeFuncResultPreComp (exsltFuncResultPreComp *comp) { | |
259 if (comp == NULL) | |
260 return; | |
261 | |
262 if (comp->select != NULL) | |
263 xmlXPathFreeCompExpr (comp->select); | |
264 if (comp->nsList != NULL) | |
265 xmlFree(comp->nsList); | |
266 xmlFree(comp); | |
267 } | |
268 | |
269 /** | |
270 * exsltFuncFunctionFunction: | |
271 * @ctxt: an XPath parser context | |
272 * @nargs: the number of arguments | |
273 * | |
274 * Evaluates the func:function element that defines the called function. | |
275 */ | |
276 static void | |
277 exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) { | |
278 xmlXPathObjectPtr oldResult, ret; | |
279 exsltFuncData *data; | |
280 exsltFuncFunctionData *func; | |
281 xmlNodePtr paramNode, oldInsert, fake; | |
282 int oldBase; | |
283 xsltStackElemPtr params = NULL, param; | |
284 xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt); | |
285 int i, notSet; | |
286 struct objChain { | |
287 struct objChain *next; | |
288 xmlXPathObjectPtr obj; | |
289 }; | |
290 struct objChain *savedObjChain = NULL, *savedObj; | |
291 | |
292 /* | |
293 * retrieve func:function template | |
294 */ | |
295 data = (exsltFuncData *) xsltGetExtData (tctxt, | |
296 EXSLT_FUNCTIONS_NAMESPACE); | |
297 oldResult = data->result; | |
298 data->result = NULL; | |
299 | |
300 func = (exsltFuncFunctionData*) xmlHashLookup2 (data->funcs, | |
301 ctxt->context->functionURI, | |
302 ctxt->context->function); | |
303 if (func == NULL) { | |
304 /* Should never happen */ | |
305 xsltGenericError(xsltGenericErrorContext, | |
306 "{%s}%s: not found\n", | |
307 ctxt->context->functionURI, ctxt->context->function); | |
308 ctxt->error = XPATH_UNKNOWN_FUNC_ERROR; | |
309 return; | |
310 } | |
311 | |
312 /* | |
313 * params handling | |
314 */ | |
315 if (nargs > func->nargs) { | |
316 xsltGenericError(xsltGenericErrorContext, | |
317 "{%s}%s: called with too many arguments\n", | |
318 ctxt->context->functionURI, ctxt->context->function); | |
319 ctxt->error = XPATH_INVALID_ARITY; | |
320 return; | |
321 } | |
322 if (func->content != NULL) { | |
323 paramNode = func->content->prev; | |
324 } | |
325 else | |
326 paramNode = NULL; | |
327 if ((paramNode == NULL) && (func->nargs != 0)) { | |
328 xsltGenericError(xsltGenericErrorContext, | |
329 "exsltFuncFunctionFunction: nargs != 0 and " | |
330 "param == NULL\n"); | |
331 return; | |
332 } | |
333 | |
334 /* | |
335 * When a function is called recursively during evaluation of its | |
336 * arguments, the recursion check in xsltApplySequenceConstructor | |
337 * isn't reached. | |
338 */ | |
339 if (tctxt->depth >= tctxt->maxTemplateDepth) { | |
340 xsltTransformError(tctxt, NULL, NULL, | |
341 "exsltFuncFunctionFunction: Potentially infinite recursion " | |
342 "detected in function {%s}%s.\n", | |
343 ctxt->context->functionURI, ctxt->context->function); | |
344 tctxt->state = XSLT_STATE_STOPPED; | |
345 return; | |
346 } | |
347 tctxt->depth++; | |
348 | |
349 /* | |
350 * We have a problem with the evaluation of function parameters. | |
351 * The original library code did not evaluate XPath expressions until | |
352 * the last moment. After version 1.1.17 of the libxslt, the logic | |
353 * of other parts of the library was changed, and the evaluation of | |
354 * XPath expressions within parameters now takes place as soon as the | |
355 * parameter is parsed/evaluated (xsltParseStylesheetCallerParam). | |
356 * This means that the parameters need to be evaluated in lexical | |
357 * order (since a variable is "in scope" as soon as it is declared). | |
358 * However, on entry to this routine, the values (from the caller) are | |
359 * in reverse order (held on the XPath context variable stack). To | |
360 * accomplish what is required, I have added code to pop the XPath | |
361 * objects off of the stack at the beginning and save them, then use | |
362 * them (in the reverse order) as the params are evaluated. This | |
363 * requires an xmlMalloc/xmlFree for each param set by the caller, | |
364 * which is not very nice. There is probably a much better solution | |
365 * (like change other code to delay the evaluation). | |
366 */ | |
367 /* | |
368 * In order to give the function params and variables a new 'scope' | |
369 * we change varsBase in the context. | |
370 */ | |
371 oldBase = tctxt->varsBase; | |
372 tctxt->varsBase = tctxt->varsNr; | |
373 /* If there are any parameters */ | |
374 if (paramNode != NULL) { | |
375 /* Fetch the stored argument values from the caller */ | |
376 for (i = 0; i < nargs; i++) { | |
377 savedObj = xmlMalloc(sizeof(struct objChain)); | |
378 savedObj->next = savedObjChain; | |
379 savedObj->obj = valuePop(ctxt); | |
380 savedObjChain = savedObj; | |
381 } | |
382 | |
383 /* | |
384 * Prepare to process params in reverse order. First, go to | |
385 * the beginning of the param chain. | |
386 */ | |
387 for (i = 1; i <= func->nargs; i++) { | |
388 if (paramNode->prev == NULL) | |
389 break; | |
390 paramNode = paramNode->prev; | |
391 } | |
392 /* | |
393 * i has total # params found, nargs is number which are present | |
394 * as arguments from the caller | |
395 * Calculate the number of un-set parameters | |
396 */ | |
397 notSet = func->nargs - nargs; | |
398 for (; i > 0; i--) { | |
399 param = xsltParseStylesheetCallerParam (tctxt, paramNode); | |
400 if (i > notSet) { /* if parameter value set */ | |
401 param->computed = 1; | |
402 if (param->value != NULL) | |
403 xmlXPathFreeObject(param->value); | |
404 savedObj = savedObjChain; /* get next val from chain */ | |
405 param->value = savedObj->obj; | |
406 savedObjChain = savedObjChain->next; | |
407 xmlFree(savedObj); | |
408 } | |
409 xsltLocalVariablePush(tctxt, param, -1); | |
410 param->next = params; | |
411 params = param; | |
412 paramNode = paramNode->next; | |
413 } | |
414 } | |
415 /* | |
416 * actual processing | |
417 */ | |
418 fake = xmlNewDocNode(tctxt->output, NULL, | |
419 (const xmlChar *)"fake", NULL); | |
420 oldInsert = tctxt->insert; | |
421 tctxt->insert = fake; | |
422 xsltApplyOneTemplate (tctxt, xmlXPathGetContextNode(ctxt), | |
423 func->content, NULL, NULL); | |
424 xsltLocalVariablePop(tctxt, tctxt->varsBase, -2); | |
425 tctxt->insert = oldInsert; | |
426 tctxt->varsBase = oldBase; /* restore original scope */ | |
427 if (params != NULL) | |
428 xsltFreeStackElemList(params); | |
429 | |
430 if (data->error != 0) | |
431 goto error; | |
432 | |
433 if (data->result != NULL) { | |
434 ret = data->result; | |
435 /* | |
436 * IMPORTANT: This enables previously tree fragments marked as | |
437 * being results of a function, to be garbage-collected after | |
438 * the calling process exits. | |
439 */ | |
440 xsltFlagRVTs(tctxt, ret, XSLT_RVT_LOCAL); | |
441 } else | |
442 ret = xmlXPathNewCString(""); | |
443 | |
444 data->result = oldResult; | |
445 | |
446 /* | |
447 * It is an error if the instantiation of the template results in | |
448 * the generation of result nodes. | |
449 */ | |
450 if (fake->children != NULL) { | |
451 #ifdef LIBXML_DEBUG_ENABLED | |
452 xmlDebugDumpNode (stderr, fake, 1); | |
453 #endif | |
454 xsltGenericError(xsltGenericErrorContext, | |
455 "{%s}%s: cannot write to result tree while " | |
456 "executing a function\n", | |
457 ctxt->context->functionURI, ctxt->context->function); | |
458 xmlFreeNode(fake); | |
459 goto error; | |
460 } | |
461 xmlFreeNode(fake); | |
462 valuePush(ctxt, ret); | |
463 | |
464 error: | |
465 tctxt->depth--; | |
466 } | |
467 | |
468 | |
469 static void | |
470 exsltFuncFunctionComp (xsltStylesheetPtr style, xmlNodePtr inst) { | |
471 xmlChar *name, *prefix; | |
472 xmlNsPtr ns; | |
473 xmlHashTablePtr data; | |
474 exsltFuncFunctionData *func; | |
475 | |
476 if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) | |
477 return; | |
478 | |
479 { | |
480 xmlChar *qname; | |
481 | |
482 qname = xmlGetProp(inst, (const xmlChar *) "name"); | |
483 name = xmlSplitQName2 (qname, &prefix); | |
484 xmlFree(qname); | |
485 } | |
486 if ((name == NULL) || (prefix == NULL)) { | |
487 xsltGenericError(xsltGenericErrorContext, | |
488 "func:function: not a QName\n"); | |
489 if (name != NULL) | |
490 xmlFree(name); | |
491 return; | |
492 } | |
493 /* namespace lookup */ | |
494 ns = xmlSearchNs (inst->doc, inst, prefix); | |
495 if (ns == NULL) { | |
496 xsltGenericError(xsltGenericErrorContext, | |
497 "func:function: undeclared prefix %s\n", | |
498 prefix); | |
499 xmlFree(name); | |
500 xmlFree(prefix); | |
501 return; | |
502 } | |
503 xmlFree(prefix); | |
504 | |
505 xsltParseTemplateContent(style, inst); | |
506 | |
507 /* | |
508 * Create function data | |
509 */ | |
510 func = exsltFuncNewFunctionData(); | |
511 if (func == NULL) { | |
512 xmlFree(name); | |
513 return; | |
514 } | |
515 func->content = inst->children; | |
516 while (IS_XSLT_ELEM(func->content) && | |
517 IS_XSLT_NAME(func->content, "param")) { | |
518 func->content = func->content->next; | |
519 func->nargs++; | |
520 } | |
521 | |
522 /* | |
523 * Register the function data such that it can be retrieved | |
524 * by exslFuncFunctionFunction | |
525 */ | |
526 #ifdef XSLT_REFACTORED | |
527 /* | |
528 * Ensure that the hash table will be stored in the *current* | |
529 * stylesheet level in order to correctly evaluate the | |
530 * import precedence. | |
531 */ | |
532 data = (xmlHashTablePtr) | |
533 xsltStyleStylesheetLevelGetExtData(style, | |
534 EXSLT_FUNCTIONS_NAMESPACE); | |
535 #else | |
536 data = (xmlHashTablePtr) | |
537 xsltStyleGetExtData (style, EXSLT_FUNCTIONS_NAMESPACE); | |
538 #endif | |
539 if (data == NULL) { | |
540 xsltGenericError(xsltGenericErrorContext, | |
541 "exsltFuncFunctionComp: no stylesheet data\n"); | |
542 xmlFree(name); | |
543 return; | |
544 } | |
545 | |
546 if (xmlHashAddEntry2 (data, ns->href, name, func) < 0) { | |
547 xsltTransformError(NULL, style, inst, | |
548 "Failed to register function {%s}%s\n", | |
549 ns->href, name); | |
550 style->errors++; | |
551 } else { | |
552 xsltGenericDebug(xsltGenericDebugContext, | |
553 "exsltFuncFunctionComp: register {%s}%s\n", | |
554 ns->href, name); | |
555 } | |
556 xmlFree(name); | |
557 } | |
558 | |
559 static xsltElemPreCompPtr | |
560 exsltFuncResultComp (xsltStylesheetPtr style, xmlNodePtr inst, | |
561 xsltTransformFunction function) { | |
562 xmlNodePtr test; | |
563 xmlChar *sel; | |
564 exsltFuncResultPreComp *ret; | |
565 | |
566 if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) | |
567 return (NULL); | |
568 | |
569 /* | |
570 * "Validity" checking | |
571 */ | |
572 /* it is an error to have any following sibling elements aside | |
573 * from the xsl:fallback element. | |
574 */ | |
575 for (test = inst->next; test != NULL; test = test->next) { | |
576 if (test->type != XML_ELEMENT_NODE) | |
577 continue; | |
578 if (IS_XSLT_ELEM(test) && IS_XSLT_NAME(test, "fallback")) | |
579 continue; | |
580 xsltGenericError(xsltGenericErrorContext, | |
581 "exsltFuncResultElem: only xsl:fallback is " | |
582 "allowed to follow func:result\n"); | |
583 style->errors++; | |
584 return (NULL); | |
585 } | |
586 /* it is an error for a func:result element to not be a descendant | |
587 * of func:function. | |
588 * it is an error if a func:result occurs within a func:result | |
589 * element. | |
590 * it is an error if instanciating the content of a variable | |
591 * binding element (i.e. xsl:variable, xsl:param) results in the | |
592 * instanciation of a func:result element. | |
593 */ | |
594 for (test = inst->parent; test != NULL; test = test->parent) { | |
595 if (IS_XSLT_ELEM(test) && | |
596 IS_XSLT_NAME(test, "stylesheet")) { | |
597 xsltGenericError(xsltGenericErrorContext, | |
598 "func:result element not a descendant " | |
599 "of a func:function\n"); | |
600 style->errors++; | |
601 return (NULL); | |
602 } | |
603 if ((test->ns != NULL) && | |
604 (xmlStrEqual(test->ns->href, EXSLT_FUNCTIONS_NAMESPACE))) { | |
605 if (xmlStrEqual(test->name, (const xmlChar *) "function")) { | |
606 break; | |
607 } | |
608 if (xmlStrEqual(test->name, (const xmlChar *) "result")) { | |
609 xsltGenericError(xsltGenericErrorContext, | |
610 "func:result element not allowed within" | |
611 " another func:result element\n"); | |
612 style->errors++; | |
613 return (NULL); | |
614 } | |
615 } | |
616 if (IS_XSLT_ELEM(test) && | |
617 (IS_XSLT_NAME(test, "variable") || | |
618 IS_XSLT_NAME(test, "param"))) { | |
619 xsltGenericError(xsltGenericErrorContext, | |
620 "func:result element not allowed within" | |
621 " a variable binding element\n"); | |
622 style->errors++; | |
623 return (NULL); | |
624 } | |
625 } | |
626 | |
627 /* | |
628 * Precomputation | |
629 */ | |
630 ret = (exsltFuncResultPreComp *) | |
631 xmlMalloc (sizeof(exsltFuncResultPreComp)); | |
632 if (ret == NULL) { | |
633 xsltPrintErrorContext(NULL, NULL, NULL); | |
634 xsltGenericError(xsltGenericErrorContext, | |
635 "exsltFuncResultComp : malloc failed\n"); | |
636 style->errors++; | |
637 return (NULL); | |
638 } | |
639 memset(ret, 0, sizeof(exsltFuncResultPreComp)); | |
640 | |
641 xsltInitElemPreComp ((xsltElemPreCompPtr) ret, style, inst, function, | |
642 (xsltElemPreCompDeallocator) exsltFreeFuncResultPreComp); | |
643 ret->select = NULL; | |
644 | |
645 /* | |
646 * Precompute the select attribute | |
647 */ | |
648 sel = xmlGetNsProp(inst, (const xmlChar *) "select", NULL); | |
649 if (sel != NULL) { | |
650 ret->select = xmlXPathCompile (sel); | |
651 xmlFree(sel); | |
652 } | |
653 /* | |
654 * Precompute the namespace list | |
655 */ | |
656 ret->nsList = xmlGetNsList(inst->doc, inst); | |
657 if (ret->nsList != NULL) { | |
658 int i = 0; | |
659 while (ret->nsList[i] != NULL) | |
660 i++; | |
661 ret->nsNr = i; | |
662 } | |
663 return ((xsltElemPreCompPtr) ret); | |
664 } | |
665 | |
666 static void | |
667 exsltFuncResultElem (xsltTransformContextPtr ctxt, | |
668 xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, | |
669 exsltFuncResultPreComp *comp) { | |
670 exsltFuncData *data; | |
671 xmlXPathObjectPtr ret; | |
672 | |
673 | |
674 /* It is an error if instantiating the content of the | |
675 * func:function element results in the instantiation of more than | |
676 * one func:result elements. | |
677 */ | |
678 data = (exsltFuncData *) xsltGetExtData (ctxt, EXSLT_FUNCTIONS_NAMESPACE); | |
679 if (data == NULL) { | |
680 xsltGenericError(xsltGenericErrorContext, | |
681 "exsltFuncReturnElem: data == NULL\n"); | |
682 return; | |
683 } | |
684 if (data->result != NULL) { | |
685 xsltGenericError(xsltGenericErrorContext, | |
686 "func:result already instanciated\n"); | |
687 data->error = 1; | |
688 return; | |
689 } | |
690 /* | |
691 * Processing | |
692 */ | |
693 if (comp->select != NULL) { | |
694 xmlNsPtr *oldXPNsList; | |
695 int oldXPNsNr; | |
696 xmlNodePtr oldXPContextNode; | |
697 /* If the func:result element has a select attribute, then the | |
698 * value of the attribute must be an expression and the | |
699 * returned value is the object that results from evaluating | |
700 * the expression. In this case, the content must be empty. | |
701 */ | |
702 if (inst->children != NULL) { | |
703 xsltGenericError(xsltGenericErrorContext, | |
704 "func:result content must be empty if" | |
705 " the function has a select attribute\n"); | |
706 data->error = 1; | |
707 return; | |
708 } | |
709 oldXPNsList = ctxt->xpathCtxt->namespaces; | |
710 oldXPNsNr = ctxt->xpathCtxt->nsNr; | |
711 oldXPContextNode = ctxt->xpathCtxt->node; | |
712 | |
713 ctxt->xpathCtxt->namespaces = comp->nsList; | |
714 ctxt->xpathCtxt->nsNr = comp->nsNr; | |
715 | |
716 ret = xmlXPathCompiledEval(comp->select, ctxt->xpathCtxt); | |
717 | |
718 ctxt->xpathCtxt->node = oldXPContextNode; | |
719 ctxt->xpathCtxt->nsNr = oldXPNsNr; | |
720 ctxt->xpathCtxt->namespaces = oldXPNsList; | |
721 | |
722 if (ret == NULL) { | |
723 xsltGenericError(xsltGenericErrorContext, | |
724 "exsltFuncResultElem: ret == NULL\n"); | |
725 return; | |
726 } | |
727 /* | |
728 * Mark it as a function result in order to avoid garbage | |
729 * collecting of tree fragments before the function exits. | |
730 */ | |
731 xsltFlagRVTs(ctxt, ret, XSLT_RVT_FUNC_RESULT); | |
732 } else if (inst->children != NULL) { | |
733 /* If the func:result element does not have a select attribute | |
734 * and has non-empty content (i.e. the func:result element has | |
735 * one or more child nodes), then the content of the | |
736 * func:result element specifies the value. | |
737 */ | |
738 xmlNodePtr oldInsert; | |
739 xmlDocPtr container; | |
740 | |
741 container = xsltCreateRVT(ctxt); | |
742 if (container == NULL) { | |
743 xsltGenericError(xsltGenericErrorContext, | |
744 "exsltFuncResultElem: out of memory\n"); | |
745 data->error = 1; | |
746 return; | |
747 } | |
748 /* Mark as function result. */ | |
749 container->psvi = XSLT_RVT_FUNC_RESULT; | |
750 | |
751 oldInsert = ctxt->insert; | |
752 ctxt->insert = (xmlNodePtr) container; | |
753 xsltApplyOneTemplate (ctxt, ctxt->xpathCtxt->node, | |
754 inst->children, NULL, NULL); | |
755 ctxt->insert = oldInsert; | |
756 | |
757 ret = xmlXPathNewValueTree((xmlNodePtr) container); | |
758 if (ret == NULL) { | |
759 xsltGenericError(xsltGenericErrorContext, | |
760 "exsltFuncResultElem: ret == NULL\n"); | |
761 data->error = 1; | |
762 } else { | |
763 ret->boolval = 0; /* Freeing is not handled there anymore */ | |
764 } | |
765 } else { | |
766 /* If the func:result element has empty content and does not | |
767 * have a select attribute, then the returned value is an | |
768 * empty string. | |
769 */ | |
770 ret = xmlXPathNewCString(""); | |
771 } | |
772 data->result = ret; | |
773 } | |
774 | |
775 /** | |
776 * exsltFuncRegister: | |
777 * | |
778 * Registers the EXSLT - Functions module | |
779 */ | |
780 void | |
781 exsltFuncRegister (void) { | |
782 xsltRegisterExtModuleFull (EXSLT_FUNCTIONS_NAMESPACE, | |
783 (xsltExtInitFunction) exsltFuncInit, | |
784 (xsltExtShutdownFunction) exsltFuncShutdown, | |
785 (xsltStyleExtInitFunction) exsltFuncStyleInit, | |
786 (xsltStyleExtShutdownFunction) exsltFuncStyleShutdown); | |
787 | |
788 xsltRegisterExtModuleTopLevel ((const xmlChar *) "function", | |
789 EXSLT_FUNCTIONS_NAMESPACE, | |
790 exsltFuncFunctionComp); | |
791 xsltRegisterExtModuleElement ((const xmlChar *) "result", | |
792 EXSLT_FUNCTIONS_NAMESPACE, | |
793 (xsltPreComputeFunction)exsltFuncResultComp, | |
794 (xsltTransformFunction) exsltFuncResultElem); | |
795 } | |
OLD | NEW |