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