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