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