/src/libxslt/libxslt/functions.c
Line | Count | Source |
1 | | /* |
2 | | * functions.c: Implementation of the XSLT extra functions |
3 | | * |
4 | | * Reference: |
5 | | * http://www.w3.org/TR/1999/REC-xslt-19991116 |
6 | | * |
7 | | * See Copyright for the status of this software. |
8 | | * |
9 | | * daniel@veillard.com |
10 | | * Bjorn Reese <breese@users.sourceforge.net> for number formatting |
11 | | */ |
12 | | |
13 | | #define IN_LIBXSLT |
14 | | #include "libxslt.h" |
15 | | |
16 | | #include <string.h> |
17 | | |
18 | | #include <libxml/xmlmemory.h> |
19 | | #include <libxml/parser.h> |
20 | | #include <libxml/tree.h> |
21 | | #include <libxml/valid.h> |
22 | | #include <libxml/hash.h> |
23 | | #include <libxml/xmlerror.h> |
24 | | #include <libxml/xpath.h> |
25 | | #include <libxml/xpathInternals.h> |
26 | | #include <libxml/parserInternals.h> |
27 | | #include <libxml/uri.h> |
28 | | #include <libxml/xpointer.h> |
29 | | #include "xslt.h" |
30 | | #include "xsltInternals.h" |
31 | | #include "xsltutils.h" |
32 | | #include "functions.h" |
33 | | #include "extensions.h" |
34 | | #include "numbersInternals.h" |
35 | | #include "keys.h" |
36 | | #include "documents.h" |
37 | | #include "transformInternals.h" |
38 | | |
39 | | #ifdef WITH_XSLT_DEBUG |
40 | | #define WITH_XSLT_DEBUG_FUNCTION |
41 | | #endif |
42 | | |
43 | | /* |
44 | | * Some versions of DocBook XSL use the vendor string to detect |
45 | | * supporting chunking, this is a workaround to be considered |
46 | | * in the list of decent XSLT processors <grin/> |
47 | | */ |
48 | | #define DOCBOOK_XSL_HACK |
49 | | |
50 | | /** |
51 | | * xsltXPathFunctionLookup: |
52 | | * @vctxt: a void * but the XSLT transformation context actually |
53 | | * @name: the function name |
54 | | * @ns_uri: the function namespace URI |
55 | | * |
56 | | * This is the entry point when a function is needed by the XPath |
57 | | * interpretor. |
58 | | * |
59 | | * Returns the callback function or NULL if not found |
60 | | */ |
61 | | xmlXPathFunction |
62 | | xsltXPathFunctionLookup (void *vctxt, |
63 | 0 | const xmlChar *name, const xmlChar *ns_uri) { |
64 | 0 | xmlXPathContextPtr ctxt = (xmlXPathContextPtr) vctxt; |
65 | 0 | xmlXPathFunction ret; |
66 | |
|
67 | 0 | if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL)) |
68 | 0 | return (NULL); |
69 | | |
70 | | #ifdef WITH_XSLT_DEBUG_FUNCTION |
71 | | xsltGenericDebug(xsltGenericDebugContext, |
72 | | "Lookup function {%s}%s\n", ns_uri, name); |
73 | | #endif |
74 | | |
75 | | /* give priority to context-level functions */ |
76 | | /* |
77 | | ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); |
78 | | */ |
79 | 0 | XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri); |
80 | |
|
81 | 0 | if (ret == NULL) |
82 | 0 | ret = xsltExtModuleFunctionLookup(name, ns_uri); |
83 | |
|
84 | | #ifdef WITH_XSLT_DEBUG_FUNCTION |
85 | | if (ret != NULL) |
86 | | xsltGenericDebug(xsltGenericDebugContext, |
87 | | "found function %s\n", name); |
88 | | #endif |
89 | 0 | return(ret); |
90 | 0 | } |
91 | | |
92 | | |
93 | | /************************************************************************ |
94 | | * * |
95 | | * Module interfaces * |
96 | | * * |
97 | | ************************************************************************/ |
98 | | |
99 | | static void |
100 | | xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, |
101 | | const xmlChar* URI, const xmlChar *fragment) |
102 | 0 | { |
103 | 0 | xsltTransformContextPtr tctxt; |
104 | 0 | xsltDocumentPtr idoc; /* document info */ |
105 | 0 | xmlDocPtr doc; |
106 | 0 | xmlXPathContextPtr xptrctxt = NULL; |
107 | 0 | xmlXPathObjectPtr resObj = NULL; |
108 | |
|
109 | 0 | (void) xptrctxt; |
110 | |
|
111 | 0 | tctxt = xsltXPathGetTransformContext(ctxt); |
112 | 0 | if (tctxt == NULL) { |
113 | 0 | xsltTransformError(NULL, NULL, NULL, |
114 | 0 | "document() : internal error tctxt == NULL\n"); |
115 | 0 | goto out_fragment; |
116 | 0 | } |
117 | | |
118 | 0 | idoc = xsltLoadDocument(tctxt, URI); |
119 | |
|
120 | 0 | if (idoc == NULL) { |
121 | 0 | if ((URI == NULL) || |
122 | 0 | (URI[0] == '#') || |
123 | 0 | ((tctxt->style->doc != NULL) && |
124 | 0 | (xmlStrEqual(tctxt->style->doc->URL, URI)))) |
125 | 0 | { |
126 | | /* |
127 | | * This selects the stylesheet's doc itself. |
128 | | */ |
129 | 0 | doc = xmlCopyDoc(tctxt->style->doc, 1); |
130 | 0 | if (doc == NULL) { |
131 | 0 | xsltTransformError(tctxt, NULL, NULL, |
132 | 0 | "document() : failed to copy style doc\n"); |
133 | 0 | goto out_fragment; |
134 | 0 | } |
135 | 0 | xsltCleanupSourceDoc(doc); /* Remove psvi fields. */ |
136 | 0 | idoc = xsltNewDocument(tctxt, doc); |
137 | 0 | if (idoc == NULL) { |
138 | 0 | xsltTransformError(tctxt, NULL, NULL, |
139 | 0 | "document() : failed to create xsltDocument\n"); |
140 | 0 | xmlFreeDoc(doc); |
141 | 0 | goto out_fragment; |
142 | 0 | } |
143 | 0 | } else { |
144 | 0 | goto out_fragment; |
145 | 0 | } |
146 | 0 | } else |
147 | 0 | doc = idoc->doc; |
148 | | |
149 | 0 | if (fragment == NULL) { |
150 | 0 | valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc)); |
151 | 0 | return; |
152 | 0 | } |
153 | | |
154 | | /* use XPointer of HTML location for fragment ID */ |
155 | 0 | #ifdef LIBXML_XPTR_ENABLED |
156 | 0 | xptrctxt = xmlXPathNewContext(doc); |
157 | 0 | if (xptrctxt == NULL) { |
158 | 0 | xsltTransformError(tctxt, NULL, NULL, |
159 | 0 | "document() : internal error xptrctxt == NULL\n"); |
160 | 0 | goto out_fragment; |
161 | 0 | } |
162 | | |
163 | 0 | #if LIBXML_VERSION >= 20911 || \ |
164 | 0 | defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) |
165 | 0 | xptrctxt->opLimit = ctxt->context->opLimit; |
166 | 0 | xptrctxt->opCount = ctxt->context->opCount; |
167 | 0 | xptrctxt->depth = ctxt->context->depth; |
168 | |
|
169 | 0 | resObj = xmlXPtrEval(fragment, xptrctxt); |
170 | |
|
171 | 0 | ctxt->context->opCount = xptrctxt->opCount; |
172 | | #else |
173 | | resObj = xmlXPtrEval(fragment, xptrctxt); |
174 | | #endif |
175 | |
|
176 | 0 | xmlXPathFreeContext(xptrctxt); |
177 | 0 | #endif /* LIBXML_XPTR_ENABLED */ |
178 | |
|
179 | 0 | if ((resObj != NULL) && (resObj->type != XPATH_NODESET)) { |
180 | 0 | xsltTransformError(tctxt, NULL, NULL, |
181 | 0 | "document() : XPointer does not select a node set: #%s\n", |
182 | 0 | fragment); |
183 | 0 | xmlXPathFreeObject(resObj); |
184 | 0 | resObj = NULL; |
185 | 0 | } |
186 | |
|
187 | 0 | out_fragment: |
188 | 0 | if (resObj == NULL) |
189 | 0 | resObj = xmlXPathNewNodeSet(NULL); |
190 | 0 | valuePush(ctxt, resObj); |
191 | 0 | } |
192 | | |
193 | | /** |
194 | | * xsltDocumentFunction: |
195 | | * @ctxt: the XPath Parser context |
196 | | * @nargs: the number of arguments |
197 | | * |
198 | | * Implement the document() XSLT function |
199 | | * node-set document(object, node-set?) |
200 | | */ |
201 | | void |
202 | | xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) |
203 | 0 | { |
204 | 0 | xmlXPathObjectPtr obj, obj2 = NULL; |
205 | 0 | xmlChar *base = NULL, *URI; |
206 | 0 | xmlChar *newURI = NULL; |
207 | 0 | xmlChar *fragment = NULL; |
208 | |
|
209 | 0 | if ((nargs < 1) || (nargs > 2)) { |
210 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
211 | 0 | "document() : invalid number of args %d\n", |
212 | 0 | nargs); |
213 | 0 | ctxt->error = XPATH_INVALID_ARITY; |
214 | 0 | return; |
215 | 0 | } |
216 | 0 | if (ctxt->value == NULL) { |
217 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
218 | 0 | "document() : invalid arg value\n"); |
219 | 0 | ctxt->error = XPATH_INVALID_TYPE; |
220 | 0 | return; |
221 | 0 | } |
222 | | |
223 | 0 | if (nargs == 2) { |
224 | 0 | if (ctxt->value->type != XPATH_NODESET) { |
225 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
226 | 0 | "document() : invalid arg expecting a nodeset\n"); |
227 | 0 | ctxt->error = XPATH_INVALID_TYPE; |
228 | 0 | return; |
229 | 0 | } |
230 | | |
231 | 0 | obj2 = valuePop(ctxt); |
232 | 0 | } |
233 | | |
234 | 0 | if ((ctxt->value != NULL) && (ctxt->value->type == XPATH_NODESET)) { |
235 | 0 | int i; |
236 | 0 | xmlXPathObjectPtr newobj, ret; |
237 | |
|
238 | 0 | obj = valuePop(ctxt); |
239 | 0 | ret = xmlXPathNewNodeSet(NULL); |
240 | |
|
241 | 0 | if ((obj != NULL) && (obj->nodesetval != NULL) && (ret != NULL)) { |
242 | 0 | for (i = 0; i < obj->nodesetval->nodeNr; i++) { |
243 | 0 | valuePush(ctxt, |
244 | 0 | xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); |
245 | 0 | xmlXPathStringFunction(ctxt, 1); |
246 | 0 | if (nargs == 2) { |
247 | 0 | valuePush(ctxt, xmlXPathObjectCopy(obj2)); |
248 | 0 | } else { |
249 | 0 | valuePush(ctxt, |
250 | 0 | xmlXPathNewNodeSet(obj->nodesetval-> |
251 | 0 | nodeTab[i])); |
252 | 0 | } |
253 | 0 | if (ctxt->error) |
254 | 0 | break; |
255 | 0 | xsltDocumentFunction(ctxt, 2); |
256 | 0 | newobj = valuePop(ctxt); |
257 | 0 | if (newobj != NULL) { |
258 | 0 | ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, |
259 | 0 | newobj->nodesetval); |
260 | 0 | xmlXPathFreeObject(newobj); |
261 | 0 | } |
262 | 0 | } |
263 | 0 | } |
264 | |
|
265 | 0 | if (obj != NULL) |
266 | 0 | xmlXPathFreeObject(obj); |
267 | 0 | if (obj2 != NULL) |
268 | 0 | xmlXPathFreeObject(obj2); |
269 | 0 | valuePush(ctxt, ret); |
270 | 0 | return; |
271 | 0 | } |
272 | | /* |
273 | | * Make sure it's converted to a string |
274 | | */ |
275 | 0 | xmlXPathStringFunction(ctxt, 1); |
276 | 0 | if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
277 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
278 | 0 | "document() : invalid arg expecting a string\n"); |
279 | 0 | ctxt->error = XPATH_INVALID_TYPE; |
280 | 0 | if (obj2 != NULL) |
281 | 0 | xmlXPathFreeObject(obj2); |
282 | 0 | return; |
283 | 0 | } |
284 | 0 | obj = valuePop(ctxt); |
285 | 0 | if (obj->stringval == NULL) { |
286 | 0 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
287 | 0 | } else { |
288 | 0 | xsltTransformContextPtr tctxt; |
289 | 0 | xmlURIPtr uri; |
290 | 0 | const xmlChar *url; |
291 | |
|
292 | 0 | tctxt = xsltXPathGetTransformContext(ctxt); |
293 | |
|
294 | 0 | url = obj->stringval; |
295 | |
|
296 | 0 | uri = xmlParseURI((const char *) url); |
297 | 0 | if (uri == NULL) { |
298 | 0 | xsltTransformError(tctxt, NULL, NULL, |
299 | 0 | "document() : failed to parse URI '%s'\n", url); |
300 | 0 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
301 | 0 | goto error; |
302 | 0 | } |
303 | | |
304 | | /* |
305 | | * check for and remove fragment identifier |
306 | | */ |
307 | 0 | fragment = (xmlChar *)uri->fragment; |
308 | 0 | if (fragment != NULL) { |
309 | 0 | uri->fragment = NULL; |
310 | 0 | newURI = xmlSaveUri(uri); |
311 | 0 | url = newURI; |
312 | 0 | } |
313 | 0 | xmlFreeURI(uri); |
314 | |
|
315 | 0 | if ((obj2 != NULL) && (obj2->nodesetval != NULL) && |
316 | 0 | (obj2->nodesetval->nodeNr > 0) && |
317 | 0 | IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { |
318 | 0 | xmlNodePtr target; |
319 | |
|
320 | 0 | target = obj2->nodesetval->nodeTab[0]; |
321 | 0 | if ((target->type == XML_ATTRIBUTE_NODE) || |
322 | 0 | (target->type == XML_PI_NODE)) { |
323 | 0 | target = ((xmlAttrPtr) target)->parent; |
324 | 0 | } |
325 | 0 | base = xmlNodeGetBase(target->doc, target); |
326 | 0 | } else { |
327 | 0 | if ((tctxt != NULL) && (tctxt->inst != NULL)) { |
328 | 0 | base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); |
329 | 0 | } else if ((tctxt != NULL) && (tctxt->style != NULL) && |
330 | 0 | (tctxt->style->doc != NULL)) { |
331 | 0 | base = xmlNodeGetBase(tctxt->style->doc, |
332 | 0 | (xmlNodePtr) tctxt->style->doc); |
333 | 0 | } |
334 | 0 | } |
335 | |
|
336 | 0 | URI = xmlBuildURI(url, base); |
337 | 0 | if (base != NULL) |
338 | 0 | xmlFree(base); |
339 | 0 | if (URI == NULL) { |
340 | 0 | if ((tctxt != NULL) && (tctxt->style != NULL) && |
341 | 0 | (tctxt->style->doc != NULL) && |
342 | 0 | (xmlStrEqual(URI, tctxt->style->doc->URL))) { |
343 | | /* This selects the stylesheet's doc itself. */ |
344 | 0 | valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc)); |
345 | 0 | } else { |
346 | 0 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
347 | 0 | } |
348 | 0 | } else { |
349 | 0 | xsltDocumentFunctionLoadDocument(ctxt, URI, fragment); |
350 | 0 | xmlFree(URI); |
351 | 0 | } |
352 | 0 | } |
353 | | |
354 | 0 | error: |
355 | 0 | xmlFree(newURI); |
356 | 0 | xmlFree(fragment); |
357 | 0 | xmlXPathFreeObject(obj); |
358 | 0 | if (obj2 != NULL) |
359 | 0 | xmlXPathFreeObject(obj2); |
360 | 0 | } |
361 | | |
362 | | /** |
363 | | * xsltKeyFunction: |
364 | | * @ctxt: the XPath Parser context |
365 | | * @nargs: the number of arguments |
366 | | * |
367 | | * Implement the key() XSLT function |
368 | | * node-set key(string, object) |
369 | | */ |
370 | | void |
371 | 0 | xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
372 | 0 | xmlXPathObjectPtr obj1, obj2; |
373 | |
|
374 | 0 | if (nargs != 2) { |
375 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
376 | 0 | "key() : expects two arguments\n"); |
377 | 0 | ctxt->error = XPATH_INVALID_ARITY; |
378 | 0 | return; |
379 | 0 | } |
380 | | |
381 | | /* |
382 | | * Get the key's value. |
383 | | */ |
384 | 0 | obj2 = valuePop(ctxt); |
385 | 0 | xmlXPathStringFunction(ctxt, 1); |
386 | 0 | if ((obj2 == NULL) || |
387 | 0 | (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
388 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
389 | 0 | "key() : invalid arg expecting a string\n"); |
390 | 0 | ctxt->error = XPATH_INVALID_TYPE; |
391 | 0 | xmlXPathFreeObject(obj2); |
392 | |
|
393 | 0 | return; |
394 | 0 | } |
395 | | /* |
396 | | * Get the key's name. |
397 | | */ |
398 | 0 | obj1 = valuePop(ctxt); |
399 | |
|
400 | 0 | if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { |
401 | 0 | int i; |
402 | 0 | xmlXPathObjectPtr newobj, ret; |
403 | |
|
404 | 0 | ret = xmlXPathNewNodeSet(NULL); |
405 | 0 | if (ret == NULL) { |
406 | 0 | ctxt->error = XPATH_MEMORY_ERROR; |
407 | 0 | xmlXPathFreeObject(obj1); |
408 | 0 | xmlXPathFreeObject(obj2); |
409 | 0 | return; |
410 | 0 | } |
411 | | |
412 | 0 | if (obj2->nodesetval != NULL) { |
413 | 0 | for (i = 0; i < obj2->nodesetval->nodeNr; i++) { |
414 | 0 | valuePush(ctxt, xmlXPathObjectCopy(obj1)); |
415 | 0 | valuePush(ctxt, |
416 | 0 | xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i])); |
417 | 0 | xmlXPathStringFunction(ctxt, 1); |
418 | 0 | xsltKeyFunction(ctxt, 2); |
419 | 0 | newobj = valuePop(ctxt); |
420 | 0 | if (newobj != NULL) |
421 | 0 | ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, |
422 | 0 | newobj->nodesetval); |
423 | 0 | xmlXPathFreeObject(newobj); |
424 | 0 | } |
425 | 0 | } |
426 | 0 | valuePush(ctxt, ret); |
427 | 0 | } else { |
428 | 0 | xmlNodeSetPtr nodelist = NULL; |
429 | 0 | xmlChar *key = NULL, *value; |
430 | 0 | const xmlChar *keyURI; |
431 | 0 | xsltTransformContextPtr tctxt; |
432 | 0 | xmlChar *qname, *prefix; |
433 | 0 | xmlXPathContextPtr xpctxt = ctxt->context; |
434 | 0 | xmlNodePtr tmpNode = NULL; |
435 | 0 | xsltDocumentPtr oldDocInfo; |
436 | |
|
437 | 0 | tctxt = xsltXPathGetTransformContext(ctxt); |
438 | |
|
439 | 0 | oldDocInfo = tctxt->document; |
440 | |
|
441 | 0 | if (xpctxt->node == NULL) { |
442 | 0 | xsltTransformError(tctxt, NULL, tctxt->inst, |
443 | 0 | "Internal error in xsltKeyFunction(): " |
444 | 0 | "The context node is not set on the XPath context.\n"); |
445 | 0 | tctxt->state = XSLT_STATE_STOPPED; |
446 | 0 | goto error; |
447 | 0 | } |
448 | | /* |
449 | | * Get the associated namespace URI if qualified name |
450 | | */ |
451 | 0 | qname = obj1->stringval; |
452 | 0 | key = xmlSplitQName2(qname, &prefix); |
453 | 0 | if (key == NULL) { |
454 | 0 | key = xmlStrdup(obj1->stringval); |
455 | 0 | keyURI = NULL; |
456 | 0 | if (prefix != NULL) |
457 | 0 | xmlFree(prefix); |
458 | 0 | } else { |
459 | 0 | if (prefix != NULL) { |
460 | 0 | keyURI = xmlXPathNsLookup(xpctxt, prefix); |
461 | 0 | if (keyURI == NULL) { |
462 | 0 | xsltTransformError(tctxt, NULL, tctxt->inst, |
463 | 0 | "key() : prefix %s is not bound\n", prefix); |
464 | | /* |
465 | | * TODO: Shouldn't we stop here? |
466 | | */ |
467 | 0 | } |
468 | 0 | xmlFree(prefix); |
469 | 0 | } else { |
470 | 0 | keyURI = NULL; |
471 | 0 | } |
472 | 0 | } |
473 | | |
474 | | /* |
475 | | * Force conversion of first arg to string |
476 | | */ |
477 | 0 | valuePush(ctxt, obj2); |
478 | 0 | xmlXPathStringFunction(ctxt, 1); |
479 | 0 | obj2 = valuePop(ctxt); |
480 | 0 | if ((obj2 == NULL) || (obj2->type != XPATH_STRING)) { |
481 | 0 | xsltTransformError(tctxt, NULL, tctxt->inst, |
482 | 0 | "key() : invalid arg expecting a string\n"); |
483 | 0 | ctxt->error = XPATH_INVALID_TYPE; |
484 | 0 | goto error; |
485 | 0 | } |
486 | 0 | value = obj2->stringval; |
487 | | |
488 | | /* |
489 | | * We need to ensure that ctxt->document is available for |
490 | | * xsltGetKey(). |
491 | | * First find the relevant doc, which is the context node's |
492 | | * owner doc; using context->doc is not safe, since |
493 | | * the doc could have been acquired via the document() function, |
494 | | * or the doc might be a Result Tree Fragment. |
495 | | * FUTURE INFO: In XSLT 2.0 the key() function takes an additional |
496 | | * argument indicating the doc to use. |
497 | | */ |
498 | 0 | if (xpctxt->node->type == XML_NAMESPACE_DECL) { |
499 | | /* |
500 | | * REVISIT: This is a libxml hack! Check xpath.c for details. |
501 | | * The XPath module sets the owner element of a ns-node on |
502 | | * the ns->next field. |
503 | | */ |
504 | 0 | if ((((xmlNsPtr) xpctxt->node)->next != NULL) && |
505 | 0 | (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE)) |
506 | 0 | { |
507 | 0 | tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next; |
508 | 0 | } |
509 | 0 | } else |
510 | 0 | tmpNode = xpctxt->node; |
511 | |
|
512 | 0 | if ((tmpNode == NULL) || (tmpNode->doc == NULL)) { |
513 | 0 | xsltTransformError(tctxt, NULL, tctxt->inst, |
514 | 0 | "Internal error in xsltKeyFunction(): " |
515 | 0 | "Couldn't get the doc of the XPath context node.\n"); |
516 | 0 | goto error; |
517 | 0 | } |
518 | | |
519 | 0 | if ((tctxt->document == NULL) || |
520 | 0 | (tctxt->document->doc != tmpNode->doc)) |
521 | 0 | { |
522 | 0 | if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) { |
523 | | /* |
524 | | * This is a Result Tree Fragment. |
525 | | */ |
526 | 0 | if (tmpNode->doc->_private == NULL) { |
527 | 0 | tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc); |
528 | 0 | if (tmpNode->doc->_private == NULL) |
529 | 0 | goto error; |
530 | 0 | } |
531 | 0 | tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private; |
532 | 0 | } else { |
533 | | /* |
534 | | * May be the initial source doc or a doc acquired via the |
535 | | * document() function. |
536 | | */ |
537 | 0 | tctxt->document = xsltFindDocument(tctxt, tmpNode->doc); |
538 | 0 | } |
539 | 0 | if (tctxt->document == NULL) { |
540 | 0 | xsltTransformError(tctxt, NULL, tctxt->inst, |
541 | 0 | "Internal error in xsltKeyFunction(): " |
542 | 0 | "Could not get the document info of a context doc.\n"); |
543 | 0 | tctxt->state = XSLT_STATE_STOPPED; |
544 | 0 | goto error; |
545 | 0 | } |
546 | 0 | } |
547 | | /* |
548 | | * Get/compute the key value. |
549 | | */ |
550 | 0 | nodelist = xsltGetKey(tctxt, key, keyURI, value); |
551 | |
|
552 | 0 | error: |
553 | 0 | tctxt->document = oldDocInfo; |
554 | 0 | valuePush(ctxt, xmlXPathWrapNodeSet( |
555 | 0 | xmlXPathNodeSetMerge(NULL, nodelist))); |
556 | 0 | if (key != NULL) |
557 | 0 | xmlFree(key); |
558 | 0 | } |
559 | | |
560 | 0 | if (obj1 != NULL) |
561 | 0 | xmlXPathFreeObject(obj1); |
562 | 0 | if (obj2 != NULL) |
563 | 0 | xmlXPathFreeObject(obj2); |
564 | 0 | } |
565 | | |
566 | | /** |
567 | | * xsltUnparsedEntityURIFunction: |
568 | | * @ctxt: the XPath Parser context |
569 | | * @nargs: the number of arguments |
570 | | * |
571 | | * Implement the unparsed-entity-uri() XSLT function |
572 | | * string unparsed-entity-uri(string) |
573 | | */ |
574 | | void |
575 | 0 | xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
576 | 0 | xmlXPathObjectPtr obj; |
577 | 0 | xmlChar *str; |
578 | |
|
579 | 0 | if ((nargs != 1) || (ctxt->value == NULL)) { |
580 | 0 | xsltGenericError(xsltGenericErrorContext, |
581 | 0 | "unparsed-entity-uri() : expects one string arg\n"); |
582 | 0 | ctxt->error = XPATH_INVALID_ARITY; |
583 | 0 | return; |
584 | 0 | } |
585 | 0 | obj = valuePop(ctxt); |
586 | 0 | if (obj->type != XPATH_STRING) { |
587 | 0 | obj = xmlXPathConvertString(obj); |
588 | 0 | if (obj == NULL) { |
589 | 0 | xmlXPathErr(ctxt, XPATH_MEMORY_ERROR); |
590 | 0 | return; |
591 | 0 | } |
592 | 0 | } |
593 | | |
594 | 0 | str = obj->stringval; |
595 | 0 | if (str == NULL) { |
596 | 0 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
597 | 0 | } else { |
598 | 0 | xmlEntityPtr entity; |
599 | |
|
600 | 0 | entity = xmlGetDocEntity(ctxt->context->doc, str); |
601 | 0 | if (entity == NULL) { |
602 | 0 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
603 | 0 | } else { |
604 | 0 | if (entity->URI != NULL) |
605 | 0 | valuePush(ctxt, xmlXPathNewString(entity->URI)); |
606 | 0 | else |
607 | 0 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
608 | 0 | } |
609 | 0 | } |
610 | 0 | xmlXPathFreeObject(obj); |
611 | 0 | } |
612 | | |
613 | | /** |
614 | | * xsltFormatNumberFunction: |
615 | | * @ctxt: the XPath Parser context |
616 | | * @nargs: the number of arguments |
617 | | * |
618 | | * Implement the format-number() XSLT function |
619 | | * string format-number(number, string, string?) |
620 | | */ |
621 | | void |
622 | | xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) |
623 | 0 | { |
624 | 0 | xmlXPathObjectPtr numberObj = NULL; |
625 | 0 | xmlXPathObjectPtr formatObj = NULL; |
626 | 0 | xmlXPathObjectPtr decimalObj = NULL; |
627 | 0 | xsltStylesheetPtr sheet; |
628 | 0 | xsltDecimalFormatPtr formatValues = NULL; |
629 | 0 | xmlChar *result; |
630 | 0 | const xmlChar *ncname; |
631 | 0 | const xmlChar *prefix = NULL; |
632 | 0 | const xmlChar *nsUri = NULL; |
633 | 0 | xsltTransformContextPtr tctxt; |
634 | |
|
635 | 0 | tctxt = xsltXPathGetTransformContext(ctxt); |
636 | 0 | if ((tctxt == NULL) || (tctxt->inst == NULL)) |
637 | 0 | return; |
638 | 0 | sheet = tctxt->style; |
639 | 0 | if (sheet == NULL) |
640 | 0 | return; |
641 | 0 | formatValues = sheet->decimalFormat; |
642 | |
|
643 | 0 | switch (nargs) { |
644 | 0 | case 3: |
645 | 0 | if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_STRING)) |
646 | 0 | xmlXPathStringFunction(ctxt, 1); |
647 | 0 | decimalObj = valuePop(ctxt); |
648 | 0 | ncname = xsltSplitQName(sheet->dict, decimalObj->stringval, &prefix); |
649 | 0 | if (prefix != NULL) { |
650 | 0 | xmlNsPtr ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, prefix); |
651 | 0 | if (ns == NULL) { |
652 | 0 | xsltTransformError(tctxt, NULL, NULL, |
653 | 0 | "format-number : No namespace found for QName '%s:%s'\n", |
654 | 0 | prefix, ncname); |
655 | 0 | sheet->errors++; |
656 | 0 | ncname = NULL; |
657 | 0 | } |
658 | 0 | else { |
659 | 0 | nsUri = ns->href; |
660 | 0 | } |
661 | 0 | } |
662 | 0 | if (ncname != NULL) { |
663 | 0 | formatValues = xsltDecimalFormatGetByQName(sheet, nsUri, ncname); |
664 | 0 | } |
665 | 0 | if (formatValues == NULL) { |
666 | 0 | xsltTransformError(tctxt, NULL, NULL, |
667 | 0 | "format-number() : undeclared decimal format '%s'\n", |
668 | 0 | decimalObj->stringval); |
669 | 0 | } |
670 | | /* Intentional fall-through */ |
671 | 0 | case 2: |
672 | 0 | if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_STRING)) |
673 | 0 | xmlXPathStringFunction(ctxt, 1); |
674 | 0 | formatObj = valuePop(ctxt); |
675 | 0 | if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_NUMBER)) |
676 | 0 | xmlXPathNumberFunction(ctxt, 1); |
677 | 0 | numberObj = valuePop(ctxt); |
678 | 0 | break; |
679 | 0 | default: |
680 | 0 | xmlXPathErr(ctxt, XPATH_INVALID_ARITY); |
681 | 0 | return; |
682 | 0 | } |
683 | | |
684 | 0 | if ((ctxt->error == 0) && |
685 | 0 | (formatValues != NULL) && (formatObj != NULL) && (numberObj != NULL)) { |
686 | 0 | if (xsltFormatNumberConversion(formatValues, |
687 | 0 | formatObj->stringval, |
688 | 0 | numberObj->floatval, |
689 | 0 | &result) == XPATH_EXPRESSION_OK) { |
690 | 0 | valuePush(ctxt, xmlXPathNewString(result)); |
691 | 0 | xmlFree(result); |
692 | 0 | } |
693 | 0 | } |
694 | |
|
695 | 0 | xmlXPathFreeObject(numberObj); |
696 | 0 | xmlXPathFreeObject(formatObj); |
697 | 0 | xmlXPathFreeObject(decimalObj); |
698 | 0 | } |
699 | | |
700 | | /** |
701 | | * xsltGenerateIdFunction: |
702 | | * @ctxt: the XPath Parser context |
703 | | * @nargs: the number of arguments |
704 | | * |
705 | | * Implement the generate-id() XSLT function |
706 | | * string generate-id(node-set?) |
707 | | */ |
708 | | void |
709 | 0 | xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
710 | 0 | xsltTransformContextPtr tctxt; |
711 | 0 | xmlNodePtr cur = NULL; |
712 | 0 | xmlXPathObjectPtr obj = NULL; |
713 | 0 | char *str; |
714 | 0 | const xmlChar *nsPrefix = NULL; |
715 | 0 | void **psviPtr; |
716 | 0 | unsigned long id; |
717 | 0 | size_t size, nsPrefixSize = 0; |
718 | |
|
719 | 0 | tctxt = xsltXPathGetTransformContext(ctxt); |
720 | |
|
721 | 0 | if (nargs == 0) { |
722 | 0 | cur = ctxt->context->node; |
723 | 0 | } else if (nargs == 1) { |
724 | 0 | xmlNodeSetPtr nodelist; |
725 | 0 | int i, ret; |
726 | |
|
727 | 0 | if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) { |
728 | 0 | ctxt->error = XPATH_INVALID_TYPE; |
729 | 0 | xsltTransformError(tctxt, NULL, NULL, |
730 | 0 | "generate-id() : invalid arg expecting a node-set\n"); |
731 | 0 | goto out; |
732 | 0 | } |
733 | 0 | obj = valuePop(ctxt); |
734 | 0 | nodelist = obj->nodesetval; |
735 | 0 | if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) { |
736 | 0 | valuePush(ctxt, xmlXPathNewCString("")); |
737 | 0 | goto out; |
738 | 0 | } |
739 | 0 | cur = nodelist->nodeTab[0]; |
740 | 0 | for (i = 1;i < nodelist->nodeNr;i++) { |
741 | 0 | ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]); |
742 | 0 | if (ret == -1) |
743 | 0 | cur = nodelist->nodeTab[i]; |
744 | 0 | } |
745 | 0 | } else { |
746 | 0 | xsltTransformError(tctxt, NULL, NULL, |
747 | 0 | "generate-id() : invalid number of args %d\n", nargs); |
748 | 0 | ctxt->error = XPATH_INVALID_ARITY; |
749 | 0 | goto out; |
750 | 0 | } |
751 | | |
752 | 0 | size = 30; /* for "id%lu" */ |
753 | |
|
754 | 0 | if (cur->type == XML_NAMESPACE_DECL) { |
755 | 0 | xmlNsPtr ns = (xmlNsPtr) cur; |
756 | |
|
757 | 0 | nsPrefix = ns->prefix; |
758 | 0 | if (nsPrefix == NULL) |
759 | 0 | nsPrefix = BAD_CAST ""; |
760 | 0 | nsPrefixSize = xmlStrlen(nsPrefix); |
761 | | /* For "ns" and hex-encoded string */ |
762 | 0 | size += nsPrefixSize * 2 + 2; |
763 | | |
764 | | /* Parent is stored in 'next'. */ |
765 | 0 | cur = (xmlNodePtr) ns->next; |
766 | 0 | } |
767 | |
|
768 | 0 | psviPtr = xsltGetPSVIPtr(cur); |
769 | 0 | if (psviPtr == NULL) { |
770 | 0 | xsltTransformError(tctxt, NULL, NULL, |
771 | 0 | "generate-id(): invalid node type %d\n", cur->type); |
772 | 0 | ctxt->error = XPATH_INVALID_TYPE; |
773 | 0 | goto out; |
774 | 0 | } |
775 | | |
776 | 0 | if (xsltGetSourceNodeFlags(cur) & XSLT_SOURCE_NODE_HAS_ID) { |
777 | 0 | id = (unsigned long) (size_t) *psviPtr; |
778 | 0 | } else { |
779 | 0 | if (cur->type == XML_TEXT_NODE && cur->line == USHRT_MAX) { |
780 | | /* Text nodes store big line numbers in psvi. */ |
781 | 0 | cur->line = 0; |
782 | 0 | } else if (*psviPtr != NULL) { |
783 | 0 | xsltTransformError(tctxt, NULL, NULL, |
784 | 0 | "generate-id(): psvi already set\n"); |
785 | 0 | ctxt->error = XPATH_MEMORY_ERROR; |
786 | 0 | goto out; |
787 | 0 | } |
788 | | |
789 | 0 | if (tctxt->currentId == ULONG_MAX) { |
790 | 0 | xsltTransformError(tctxt, NULL, NULL, |
791 | 0 | "generate-id(): id overflow\n"); |
792 | 0 | ctxt->error = XPATH_MEMORY_ERROR; |
793 | 0 | goto out; |
794 | 0 | } |
795 | | |
796 | 0 | id = ++tctxt->currentId; |
797 | 0 | *psviPtr = (void *) (size_t) id; |
798 | 0 | xsltSetSourceNodeFlags(tctxt, cur, XSLT_SOURCE_NODE_HAS_ID); |
799 | 0 | } |
800 | | |
801 | 0 | str = xmlMalloc(size); |
802 | 0 | if (str == NULL) { |
803 | 0 | xsltTransformError(tctxt, NULL, NULL, |
804 | 0 | "generate-id(): out of memory\n"); |
805 | 0 | ctxt->error = XPATH_MEMORY_ERROR; |
806 | 0 | goto out; |
807 | 0 | } |
808 | 0 | if (nsPrefix == NULL) { |
809 | 0 | snprintf(str, size, "id%lu", id); |
810 | 0 | } else { |
811 | 0 | size_t i, j; |
812 | |
|
813 | 0 | snprintf(str, size, "id%luns", id); |
814 | | |
815 | | /* |
816 | | * Only ASCII alphanumerics are allowed, so we hex-encode the prefix. |
817 | | */ |
818 | 0 | j = strlen(str); |
819 | 0 | for (i = 0; i < nsPrefixSize; i++) { |
820 | 0 | int v; |
821 | |
|
822 | 0 | v = nsPrefix[i] >> 4; |
823 | 0 | str[j++] = v < 10 ? '0' + v : 'A' + (v - 10); |
824 | 0 | v = nsPrefix[i] & 15; |
825 | 0 | str[j++] = v < 10 ? '0' + v : 'A' + (v - 10); |
826 | 0 | } |
827 | 0 | str[j] = '\0'; |
828 | 0 | } |
829 | 0 | valuePush(ctxt, xmlXPathWrapString(BAD_CAST str)); |
830 | |
|
831 | 0 | out: |
832 | 0 | xmlXPathFreeObject(obj); |
833 | 0 | } |
834 | | |
835 | | /** |
836 | | * xsltSystemPropertyFunction: |
837 | | * @ctxt: the XPath Parser context |
838 | | * @nargs: the number of arguments |
839 | | * |
840 | | * Implement the system-property() XSLT function |
841 | | * object system-property(string) |
842 | | */ |
843 | | void |
844 | 0 | xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
845 | 0 | xmlXPathObjectPtr obj; |
846 | 0 | xmlChar *prefix, *name; |
847 | 0 | const xmlChar *nsURI = NULL; |
848 | |
|
849 | 0 | if (nargs != 1) { |
850 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
851 | 0 | "system-property() : expects one string arg\n"); |
852 | 0 | ctxt->error = XPATH_INVALID_ARITY; |
853 | 0 | return; |
854 | 0 | } |
855 | 0 | if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
856 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
857 | 0 | "system-property() : invalid arg expecting a string\n"); |
858 | 0 | ctxt->error = XPATH_INVALID_TYPE; |
859 | 0 | return; |
860 | 0 | } |
861 | 0 | obj = valuePop(ctxt); |
862 | 0 | if (obj->stringval == NULL) { |
863 | 0 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
864 | 0 | } else { |
865 | 0 | name = xmlSplitQName2(obj->stringval, &prefix); |
866 | 0 | if (name == NULL) { |
867 | 0 | name = xmlStrdup(obj->stringval); |
868 | 0 | } else { |
869 | 0 | nsURI = xmlXPathNsLookup(ctxt->context, prefix); |
870 | 0 | if (nsURI == NULL) { |
871 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
872 | 0 | "system-property() : prefix %s is not bound\n", prefix); |
873 | 0 | } |
874 | 0 | } |
875 | |
|
876 | 0 | if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) { |
877 | 0 | #ifdef DOCBOOK_XSL_HACK |
878 | 0 | if (xmlStrEqual(name, (const xmlChar *)"vendor")) { |
879 | 0 | xsltStylesheetPtr sheet; |
880 | 0 | xsltTransformContextPtr tctxt; |
881 | |
|
882 | 0 | tctxt = xsltXPathGetTransformContext(ctxt); |
883 | 0 | if ((tctxt != NULL) && (tctxt->inst != NULL) && |
884 | 0 | (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) && |
885 | 0 | (tctxt->inst->parent != NULL) && |
886 | 0 | (xmlStrEqual(tctxt->inst->parent->name, |
887 | 0 | BAD_CAST "template"))) |
888 | 0 | sheet = tctxt->style; |
889 | 0 | else |
890 | 0 | sheet = NULL; |
891 | 0 | if ((sheet != NULL) && (sheet->doc != NULL) && |
892 | 0 | (sheet->doc->URL != NULL) && |
893 | 0 | (xmlStrstr(sheet->doc->URL, |
894 | 0 | (const xmlChar *)"chunk") != NULL)) { |
895 | 0 | valuePush(ctxt, xmlXPathNewString( |
896 | 0 | (const xmlChar *)"libxslt (SAXON 6.2 compatible)")); |
897 | |
|
898 | 0 | } else { |
899 | 0 | valuePush(ctxt, xmlXPathNewString( |
900 | 0 | (const xmlChar *)XSLT_DEFAULT_VENDOR)); |
901 | 0 | } |
902 | 0 | } else |
903 | | #else |
904 | | if (xmlStrEqual(name, (const xmlChar *)"vendor")) { |
905 | | valuePush(ctxt, xmlXPathNewString( |
906 | | (const xmlChar *)XSLT_DEFAULT_VENDOR)); |
907 | | } else |
908 | | #endif |
909 | 0 | if (xmlStrEqual(name, (const xmlChar *)"version")) { |
910 | 0 | valuePush(ctxt, xmlXPathNewString( |
911 | 0 | (const xmlChar *)XSLT_DEFAULT_VERSION)); |
912 | 0 | } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) { |
913 | 0 | valuePush(ctxt, xmlXPathNewString( |
914 | 0 | (const xmlChar *)XSLT_DEFAULT_URL)); |
915 | 0 | } else { |
916 | 0 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
917 | 0 | } |
918 | 0 | } else { |
919 | 0 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
920 | 0 | } |
921 | 0 | if (name != NULL) |
922 | 0 | xmlFree(name); |
923 | 0 | if (prefix != NULL) |
924 | 0 | xmlFree(prefix); |
925 | 0 | } |
926 | 0 | xmlXPathFreeObject(obj); |
927 | 0 | } |
928 | | |
929 | | /** |
930 | | * xsltElementAvailableFunction: |
931 | | * @ctxt: the XPath Parser context |
932 | | * @nargs: the number of arguments |
933 | | * |
934 | | * Implement the element-available() XSLT function |
935 | | * boolean element-available(string) |
936 | | */ |
937 | | void |
938 | 0 | xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
939 | 0 | xmlXPathObjectPtr obj; |
940 | 0 | xmlChar *prefix, *name; |
941 | 0 | const xmlChar *nsURI = NULL; |
942 | 0 | xsltTransformContextPtr tctxt; |
943 | |
|
944 | 0 | if (nargs != 1) { |
945 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
946 | 0 | "element-available() : expects one string arg\n"); |
947 | 0 | ctxt->error = XPATH_INVALID_ARITY; |
948 | 0 | return; |
949 | 0 | } |
950 | 0 | xmlXPathStringFunction(ctxt, 1); |
951 | 0 | if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
952 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
953 | 0 | "element-available() : invalid arg expecting a string\n"); |
954 | 0 | ctxt->error = XPATH_INVALID_TYPE; |
955 | 0 | return; |
956 | 0 | } |
957 | 0 | obj = valuePop(ctxt); |
958 | 0 | tctxt = xsltXPathGetTransformContext(ctxt); |
959 | 0 | if ((tctxt == NULL) || (tctxt->inst == NULL)) { |
960 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
961 | 0 | "element-available() : internal error tctxt == NULL\n"); |
962 | 0 | xmlXPathFreeObject(obj); |
963 | 0 | valuePush(ctxt, xmlXPathNewBoolean(0)); |
964 | 0 | return; |
965 | 0 | } |
966 | | |
967 | | |
968 | 0 | name = xmlSplitQName2(obj->stringval, &prefix); |
969 | 0 | if (name == NULL) { |
970 | 0 | xmlNsPtr ns; |
971 | |
|
972 | 0 | name = xmlStrdup(obj->stringval); |
973 | 0 | ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL); |
974 | 0 | if (ns != NULL) nsURI = ns->href; |
975 | 0 | } else { |
976 | 0 | nsURI = xmlXPathNsLookup(ctxt->context, prefix); |
977 | 0 | if (nsURI == NULL) { |
978 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
979 | 0 | "element-available() : prefix %s is not bound\n", prefix); |
980 | 0 | } |
981 | 0 | } |
982 | |
|
983 | 0 | if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) { |
984 | 0 | valuePush(ctxt, xmlXPathNewBoolean(1)); |
985 | 0 | } else { |
986 | 0 | valuePush(ctxt, xmlXPathNewBoolean(0)); |
987 | 0 | } |
988 | |
|
989 | 0 | xmlXPathFreeObject(obj); |
990 | 0 | if (name != NULL) |
991 | 0 | xmlFree(name); |
992 | 0 | if (prefix != NULL) |
993 | 0 | xmlFree(prefix); |
994 | 0 | } |
995 | | |
996 | | /** |
997 | | * xsltFunctionAvailableFunction: |
998 | | * @ctxt: the XPath Parser context |
999 | | * @nargs: the number of arguments |
1000 | | * |
1001 | | * Implement the function-available() XSLT function |
1002 | | * boolean function-available(string) |
1003 | | */ |
1004 | | void |
1005 | 0 | xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
1006 | 0 | xmlXPathObjectPtr obj; |
1007 | 0 | xmlChar *prefix, *name; |
1008 | 0 | const xmlChar *nsURI = NULL; |
1009 | |
|
1010 | 0 | if (nargs != 1) { |
1011 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
1012 | 0 | "function-available() : expects one string arg\n"); |
1013 | 0 | ctxt->error = XPATH_INVALID_ARITY; |
1014 | 0 | return; |
1015 | 0 | } |
1016 | 0 | xmlXPathStringFunction(ctxt, 1); |
1017 | 0 | if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
1018 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
1019 | 0 | "function-available() : invalid arg expecting a string\n"); |
1020 | 0 | ctxt->error = XPATH_INVALID_TYPE; |
1021 | 0 | return; |
1022 | 0 | } |
1023 | 0 | obj = valuePop(ctxt); |
1024 | |
|
1025 | 0 | name = xmlSplitQName2(obj->stringval, &prefix); |
1026 | 0 | if (name == NULL) { |
1027 | 0 | name = xmlStrdup(obj->stringval); |
1028 | 0 | } else { |
1029 | 0 | nsURI = xmlXPathNsLookup(ctxt->context, prefix); |
1030 | 0 | if (nsURI == NULL) { |
1031 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
1032 | 0 | "function-available() : prefix %s is not bound\n", prefix); |
1033 | 0 | } |
1034 | 0 | } |
1035 | |
|
1036 | 0 | if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) { |
1037 | 0 | valuePush(ctxt, xmlXPathNewBoolean(1)); |
1038 | 0 | } else { |
1039 | 0 | valuePush(ctxt, xmlXPathNewBoolean(0)); |
1040 | 0 | } |
1041 | |
|
1042 | 0 | xmlXPathFreeObject(obj); |
1043 | 0 | if (name != NULL) |
1044 | 0 | xmlFree(name); |
1045 | 0 | if (prefix != NULL) |
1046 | 0 | xmlFree(prefix); |
1047 | 0 | } |
1048 | | |
1049 | | /** |
1050 | | * xsltCurrentFunction: |
1051 | | * @ctxt: the XPath Parser context |
1052 | | * @nargs: the number of arguments |
1053 | | * |
1054 | | * Implement the current() XSLT function |
1055 | | * node-set current() |
1056 | | */ |
1057 | | static void |
1058 | 0 | xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
1059 | 0 | xsltTransformContextPtr tctxt; |
1060 | |
|
1061 | 0 | if (nargs != 0) { |
1062 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
1063 | 0 | "current() : function uses no argument\n"); |
1064 | 0 | ctxt->error = XPATH_INVALID_ARITY; |
1065 | 0 | return; |
1066 | 0 | } |
1067 | 0 | tctxt = xsltXPathGetTransformContext(ctxt); |
1068 | 0 | if (tctxt == NULL) { |
1069 | 0 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
1070 | 0 | "current() : internal error tctxt == NULL\n"); |
1071 | 0 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
1072 | 0 | } else { |
1073 | 0 | valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */ |
1074 | 0 | } |
1075 | 0 | } |
1076 | | |
1077 | | /************************************************************************ |
1078 | | * * |
1079 | | * Registration of XSLT and libxslt functions * |
1080 | | * * |
1081 | | ************************************************************************/ |
1082 | | |
1083 | | /** |
1084 | | * xsltRegisterAllFunctions: |
1085 | | * @ctxt: the XPath context |
1086 | | * |
1087 | | * Registers all default XSLT functions in this context |
1088 | | */ |
1089 | | void |
1090 | | xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) |
1091 | 0 | { |
1092 | 0 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current", |
1093 | 0 | xsltCurrentFunction); |
1094 | 0 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document", |
1095 | 0 | xsltDocumentFunction); |
1096 | 0 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction); |
1097 | 0 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri", |
1098 | 0 | xsltUnparsedEntityURIFunction); |
1099 | 0 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number", |
1100 | 0 | xsltFormatNumberFunction); |
1101 | 0 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id", |
1102 | 0 | xsltGenerateIdFunction); |
1103 | 0 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property", |
1104 | 0 | xsltSystemPropertyFunction); |
1105 | 0 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available", |
1106 | 0 | xsltElementAvailableFunction); |
1107 | 0 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available", |
1108 | 0 | xsltFunctionAvailableFunction); |
1109 | 0 | } |