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