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 | #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 | ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); |
---|
82 | |
---|
83 | if (ret == NULL) |
---|
84 | ret = xsltExtModuleFunctionLookup(name, ns_uri); |
---|
85 | |
---|
86 | #ifdef WITH_XSLT_DEBUG_FUNCTION |
---|
87 | if (ret != NULL) |
---|
88 | xsltGenericDebug(xsltGenericDebugContext, |
---|
89 | "found function %s\n", name); |
---|
90 | #endif |
---|
91 | return(ret); |
---|
92 | } |
---|
93 | |
---|
94 | |
---|
95 | /************************************************************************ |
---|
96 | * * |
---|
97 | * Module interfaces * |
---|
98 | * * |
---|
99 | ************************************************************************/ |
---|
100 | |
---|
101 | static void |
---|
102 | xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI) |
---|
103 | { |
---|
104 | xsltTransformContextPtr tctxt; |
---|
105 | xmlURIPtr uri; |
---|
106 | xmlChar *fragment; |
---|
107 | xsltDocumentPtr xsltdoc; |
---|
108 | xmlDocPtr doc; |
---|
109 | xmlXPathContextPtr xptrctxt; |
---|
110 | xmlXPathObjectPtr object; |
---|
111 | |
---|
112 | tctxt = xsltXPathGetTransformContext(ctxt); |
---|
113 | if (tctxt == NULL) { |
---|
114 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
115 | "document() : internal error tctxt == NULL\n"); |
---|
116 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
---|
117 | return; |
---|
118 | } |
---|
119 | |
---|
120 | uri = xmlParseURI((const char *) URI); |
---|
121 | if (uri == NULL) { |
---|
122 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
123 | "document() : failed to parse URI\n"); |
---|
124 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
---|
125 | return; |
---|
126 | } |
---|
127 | |
---|
128 | /* |
---|
129 | * check for and remove fragment identifier |
---|
130 | */ |
---|
131 | fragment = uri->fragment; |
---|
132 | if (fragment != NULL) { |
---|
133 | uri->fragment = NULL; |
---|
134 | URI = xmlSaveUri(uri); |
---|
135 | xsltdoc = xsltLoadDocument(tctxt, URI); |
---|
136 | xmlFree(URI); |
---|
137 | } else |
---|
138 | xsltdoc = xsltLoadDocument(tctxt, URI); |
---|
139 | xmlFreeURI(uri); |
---|
140 | |
---|
141 | if (xsltdoc == NULL) { |
---|
142 | if ((URI == NULL) || |
---|
143 | (URI[0] == '#') || |
---|
144 | (xmlStrEqual(tctxt->style->doc->URL, URI))) { |
---|
145 | doc = tctxt->style->doc; |
---|
146 | } else { |
---|
147 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
---|
148 | |
---|
149 | if (fragment != NULL) |
---|
150 | xmlFree(fragment); |
---|
151 | |
---|
152 | return; |
---|
153 | } |
---|
154 | } else |
---|
155 | doc = xsltdoc->doc; |
---|
156 | |
---|
157 | if ( fragment == NULL ) { |
---|
158 | valuePush(ctxt, |
---|
159 | xmlXPathNewNodeSet((xmlNodePtr) doc)); |
---|
160 | return; |
---|
161 | } |
---|
162 | |
---|
163 | /* use XPointer of HTML location for fragment ID */ |
---|
164 | |
---|
165 | xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); |
---|
166 | if (xptrctxt == NULL) { |
---|
167 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
168 | "document() : internal error xptrctxt == NULL\n"); |
---|
169 | goto out_fragment; |
---|
170 | } |
---|
171 | |
---|
172 | object = xmlXPtrEval(fragment, xptrctxt); |
---|
173 | xmlFree(fragment); |
---|
174 | xmlXPathFreeContext(xptrctxt); |
---|
175 | |
---|
176 | if (object == NULL) |
---|
177 | goto out_fragment; |
---|
178 | |
---|
179 | switch (object->type) { |
---|
180 | case XPATH_NODESET: |
---|
181 | break; |
---|
182 | case XPATH_UNDEFINED: |
---|
183 | case XPATH_BOOLEAN: |
---|
184 | case XPATH_NUMBER: |
---|
185 | case XPATH_STRING: |
---|
186 | case XPATH_POINT: |
---|
187 | case XPATH_USERS: |
---|
188 | case XPATH_XSLT_TREE: |
---|
189 | case XPATH_RANGE: |
---|
190 | case XPATH_LOCATIONSET: |
---|
191 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
192 | "document() : XPointer does not select a node set: #%s\n", |
---|
193 | fragment); |
---|
194 | goto out_object; |
---|
195 | } |
---|
196 | |
---|
197 | valuePush(ctxt, object); |
---|
198 | return; |
---|
199 | |
---|
200 | out_object: |
---|
201 | xmlXPathFreeObject(object); |
---|
202 | |
---|
203 | out_fragment: |
---|
204 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
---|
205 | } |
---|
206 | |
---|
207 | /** |
---|
208 | * xsltDocumentFunction: |
---|
209 | * @ctxt: the XPath Parser context |
---|
210 | * @nargs: the number of arguments |
---|
211 | * |
---|
212 | * Implement the document() XSLT function |
---|
213 | * node-set document(object, node-set?) |
---|
214 | */ |
---|
215 | void |
---|
216 | xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) |
---|
217 | { |
---|
218 | xmlXPathObjectPtr obj, obj2 = NULL; |
---|
219 | xmlChar *base = NULL, *URI; |
---|
220 | |
---|
221 | |
---|
222 | if ((nargs < 1) || (nargs > 2)) { |
---|
223 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
224 | "document() : invalid number of args %d\n", |
---|
225 | nargs); |
---|
226 | ctxt->error = XPATH_INVALID_ARITY; |
---|
227 | return; |
---|
228 | } |
---|
229 | if (ctxt->value == NULL) { |
---|
230 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
231 | "document() : invalid arg value\n"); |
---|
232 | ctxt->error = XPATH_INVALID_TYPE; |
---|
233 | return; |
---|
234 | } |
---|
235 | |
---|
236 | if (nargs == 2) { |
---|
237 | if (ctxt->value->type != XPATH_NODESET) { |
---|
238 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
239 | "document() : invalid arg expecting a nodeset\n"); |
---|
240 | ctxt->error = XPATH_INVALID_TYPE; |
---|
241 | return; |
---|
242 | } |
---|
243 | |
---|
244 | obj2 = valuePop(ctxt); |
---|
245 | } |
---|
246 | |
---|
247 | if (ctxt->value->type == XPATH_NODESET) { |
---|
248 | int i; |
---|
249 | xmlXPathObjectPtr newobj, ret; |
---|
250 | |
---|
251 | obj = valuePop(ctxt); |
---|
252 | ret = xmlXPathNewNodeSet(NULL); |
---|
253 | |
---|
254 | if (obj->nodesetval) { |
---|
255 | for (i = 0; i < obj->nodesetval->nodeNr; i++) { |
---|
256 | valuePush(ctxt, |
---|
257 | xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); |
---|
258 | xmlXPathStringFunction(ctxt, 1); |
---|
259 | if (nargs == 2) { |
---|
260 | valuePush(ctxt, xmlXPathObjectCopy(obj2)); |
---|
261 | } else { |
---|
262 | valuePush(ctxt, |
---|
263 | xmlXPathNewNodeSet(obj->nodesetval-> |
---|
264 | nodeTab[i])); |
---|
265 | } |
---|
266 | xsltDocumentFunction(ctxt, 2); |
---|
267 | newobj = valuePop(ctxt); |
---|
268 | ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, |
---|
269 | newobj->nodesetval); |
---|
270 | xmlXPathFreeObject(newobj); |
---|
271 | } |
---|
272 | } |
---|
273 | |
---|
274 | xmlXPathFreeObject(obj); |
---|
275 | if (obj2 != NULL) |
---|
276 | xmlXPathFreeObject(obj2); |
---|
277 | valuePush(ctxt, ret); |
---|
278 | return; |
---|
279 | } |
---|
280 | /* |
---|
281 | * Make sure it's converted to a string |
---|
282 | */ |
---|
283 | xmlXPathStringFunction(ctxt, 1); |
---|
284 | if (ctxt->value->type != XPATH_STRING) { |
---|
285 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
286 | "document() : invalid arg expecting a string\n"); |
---|
287 | ctxt->error = XPATH_INVALID_TYPE; |
---|
288 | if (obj2 != NULL) |
---|
289 | xmlXPathFreeObject(obj2); |
---|
290 | return; |
---|
291 | } |
---|
292 | obj = valuePop(ctxt); |
---|
293 | if (obj->stringval == NULL) { |
---|
294 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
---|
295 | } else { |
---|
296 | if ((obj2 != NULL) && (obj2->nodesetval != NULL) && |
---|
297 | (obj2->nodesetval->nodeNr > 0) && |
---|
298 | IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { |
---|
299 | xmlNodePtr target; |
---|
300 | |
---|
301 | target = obj2->nodesetval->nodeTab[0]; |
---|
302 | if (target->type == XML_ATTRIBUTE_NODE) { |
---|
303 | target = ((xmlAttrPtr) target)->parent; |
---|
304 | } |
---|
305 | base = xmlNodeGetBase(target->doc, target); |
---|
306 | } else { |
---|
307 | xsltTransformContextPtr tctxt; |
---|
308 | |
---|
309 | tctxt = xsltXPathGetTransformContext(ctxt); |
---|
310 | if ((tctxt != NULL) && (tctxt->inst != NULL)) { |
---|
311 | base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); |
---|
312 | } else if ((tctxt != NULL) && (tctxt->style != NULL) && |
---|
313 | (tctxt->style->doc != NULL)) { |
---|
314 | base = xmlNodeGetBase(tctxt->style->doc, |
---|
315 | (xmlNodePtr) tctxt->style->doc); |
---|
316 | } |
---|
317 | } |
---|
318 | URI = xmlBuildURI(obj->stringval, base); |
---|
319 | if (base != NULL) |
---|
320 | xmlFree(base); |
---|
321 | if (URI == NULL) { |
---|
322 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
---|
323 | } else { |
---|
324 | xsltDocumentFunctionLoadDocument( ctxt, URI ); |
---|
325 | xmlFree(URI); |
---|
326 | } |
---|
327 | } |
---|
328 | xmlXPathFreeObject(obj); |
---|
329 | if (obj2 != NULL) |
---|
330 | xmlXPathFreeObject(obj2); |
---|
331 | } |
---|
332 | |
---|
333 | /** |
---|
334 | * xsltKeyFunction: |
---|
335 | * @ctxt: the XPath Parser context |
---|
336 | * @nargs: the number of arguments |
---|
337 | * |
---|
338 | * Implement the key() XSLT function |
---|
339 | * node-set key(string, object) |
---|
340 | */ |
---|
341 | void |
---|
342 | xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
---|
343 | xmlNodeSetPtr nodelist; |
---|
344 | xmlXPathObjectPtr obj1, obj2; |
---|
345 | xmlChar *key = NULL, *value; |
---|
346 | const xmlChar *keyURI; |
---|
347 | xsltTransformContextPtr tctxt; |
---|
348 | |
---|
349 | if (nargs != 2) { |
---|
350 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
351 | "key() : expects two arguments\n"); |
---|
352 | ctxt->error = XPATH_INVALID_ARITY; |
---|
353 | return; |
---|
354 | } |
---|
355 | |
---|
356 | obj2 = valuePop(ctxt); |
---|
357 | xmlXPathStringFunction(ctxt, 1); |
---|
358 | if ((obj2 == NULL) || |
---|
359 | (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
---|
360 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
361 | "key() : invalid arg expecting a string\n"); |
---|
362 | ctxt->error = XPATH_INVALID_TYPE; |
---|
363 | xmlXPathFreeObject(obj2); |
---|
364 | |
---|
365 | return; |
---|
366 | } |
---|
367 | obj1 = valuePop(ctxt); |
---|
368 | |
---|
369 | if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { |
---|
370 | int i; |
---|
371 | xmlXPathObjectPtr newobj, ret; |
---|
372 | |
---|
373 | ret = xmlXPathNewNodeSet(NULL); |
---|
374 | |
---|
375 | if (obj2->nodesetval != NULL) { |
---|
376 | for (i = 0; i < obj2->nodesetval->nodeNr; i++) { |
---|
377 | valuePush(ctxt, xmlXPathObjectCopy(obj1)); |
---|
378 | valuePush(ctxt, |
---|
379 | xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i])); |
---|
380 | xmlXPathStringFunction(ctxt, 1); |
---|
381 | xsltKeyFunction(ctxt, 2); |
---|
382 | newobj = valuePop(ctxt); |
---|
383 | ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, |
---|
384 | newobj->nodesetval); |
---|
385 | xmlXPathFreeObject(newobj); |
---|
386 | } |
---|
387 | } |
---|
388 | valuePush(ctxt, ret); |
---|
389 | } else { |
---|
390 | xmlChar *qname, *prefix; |
---|
391 | |
---|
392 | /* |
---|
393 | * Get the associated namespace URI if qualified name |
---|
394 | */ |
---|
395 | qname = obj1->stringval; |
---|
396 | key = xmlSplitQName2(qname, &prefix); |
---|
397 | if (key == NULL) { |
---|
398 | key = xmlStrdup(obj1->stringval); |
---|
399 | keyURI = NULL; |
---|
400 | if (prefix != NULL) |
---|
401 | xmlFree(prefix); |
---|
402 | } else { |
---|
403 | if (prefix != NULL) { |
---|
404 | keyURI = xmlXPathNsLookup(ctxt->context, prefix); |
---|
405 | if (keyURI == NULL) { |
---|
406 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
407 | "key() : prefix %s is not bound\n", prefix); |
---|
408 | } |
---|
409 | xmlFree(prefix); |
---|
410 | } else { |
---|
411 | keyURI = NULL; |
---|
412 | } |
---|
413 | } |
---|
414 | |
---|
415 | /* |
---|
416 | * Force conversion of first arg to string |
---|
417 | */ |
---|
418 | valuePush(ctxt, obj2); |
---|
419 | xmlXPathStringFunction(ctxt, 1); |
---|
420 | if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
---|
421 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
422 | "key() : invalid arg expecting a string\n"); |
---|
423 | ctxt->error = XPATH_INVALID_TYPE; |
---|
424 | xmlXPathFreeObject(obj1); |
---|
425 | |
---|
426 | return; |
---|
427 | } |
---|
428 | obj2 = valuePop(ctxt); |
---|
429 | value = obj2->stringval; |
---|
430 | |
---|
431 | tctxt = xsltXPathGetTransformContext(ctxt); |
---|
432 | |
---|
433 | nodelist = xsltGetKey(tctxt, key, keyURI, value); |
---|
434 | valuePush(ctxt, xmlXPathWrapNodeSet( |
---|
435 | xmlXPathNodeSetMerge(NULL, nodelist))); |
---|
436 | } |
---|
437 | |
---|
438 | |
---|
439 | if (obj1 != NULL) |
---|
440 | xmlXPathFreeObject(obj1); |
---|
441 | if (obj2 != NULL) |
---|
442 | xmlXPathFreeObject(obj2); |
---|
443 | if (key != NULL) |
---|
444 | xmlFree(key); |
---|
445 | } |
---|
446 | |
---|
447 | /** |
---|
448 | * xsltUnparsedEntityURIFunction: |
---|
449 | * @ctxt: the XPath Parser context |
---|
450 | * @nargs: the number of arguments |
---|
451 | * |
---|
452 | * Implement the unparsed-entity-uri() XSLT function |
---|
453 | * string unparsed-entity-uri(string) |
---|
454 | */ |
---|
455 | void |
---|
456 | xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
---|
457 | xmlXPathObjectPtr obj; |
---|
458 | xmlChar *str; |
---|
459 | |
---|
460 | if ((nargs != 1) || (ctxt->value == NULL)) { |
---|
461 | xsltGenericError(xsltGenericErrorContext, |
---|
462 | "unparsed-entity-uri() : expects one string arg\n"); |
---|
463 | ctxt->error = XPATH_INVALID_ARITY; |
---|
464 | return; |
---|
465 | } |
---|
466 | obj = valuePop(ctxt); |
---|
467 | if (obj->type != XPATH_STRING) { |
---|
468 | obj = xmlXPathConvertString(obj); |
---|
469 | } |
---|
470 | |
---|
471 | str = obj->stringval; |
---|
472 | if (str == NULL) { |
---|
473 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
---|
474 | } else { |
---|
475 | xmlEntityPtr entity; |
---|
476 | |
---|
477 | entity = xmlGetDocEntity(ctxt->context->doc, str); |
---|
478 | if (entity == NULL) { |
---|
479 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
---|
480 | } else { |
---|
481 | if (entity->URI != NULL) |
---|
482 | valuePush(ctxt, xmlXPathNewString(entity->URI)); |
---|
483 | else |
---|
484 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
---|
485 | } |
---|
486 | } |
---|
487 | xmlXPathFreeObject(obj); |
---|
488 | } |
---|
489 | |
---|
490 | /** |
---|
491 | * xsltFormatNumberFunction: |
---|
492 | * @ctxt: the XPath Parser context |
---|
493 | * @nargs: the number of arguments |
---|
494 | * |
---|
495 | * Implement the format-number() XSLT function |
---|
496 | * string format-number(number, string, string?) |
---|
497 | */ |
---|
498 | void |
---|
499 | xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) |
---|
500 | { |
---|
501 | xmlXPathObjectPtr numberObj = NULL; |
---|
502 | xmlXPathObjectPtr formatObj = NULL; |
---|
503 | xmlXPathObjectPtr decimalObj = NULL; |
---|
504 | xsltStylesheetPtr sheet; |
---|
505 | xsltDecimalFormatPtr formatValues; |
---|
506 | xmlChar *result; |
---|
507 | xsltTransformContextPtr tctxt; |
---|
508 | |
---|
509 | tctxt = xsltXPathGetTransformContext(ctxt); |
---|
510 | if (tctxt == NULL) |
---|
511 | return; |
---|
512 | sheet = tctxt->style; |
---|
513 | if (sheet == NULL) |
---|
514 | return; |
---|
515 | formatValues = sheet->decimalFormat; |
---|
516 | |
---|
517 | switch (nargs) { |
---|
518 | case 3: |
---|
519 | CAST_TO_STRING; |
---|
520 | decimalObj = valuePop(ctxt); |
---|
521 | formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval); |
---|
522 | if (formatValues == NULL) { |
---|
523 | xsltTransformError(tctxt, NULL, NULL, |
---|
524 | "format-number() : undeclared decimal format '%s'\n", |
---|
525 | decimalObj->stringval); |
---|
526 | } |
---|
527 | /* Intentional fall-through */ |
---|
528 | case 2: |
---|
529 | CAST_TO_STRING; |
---|
530 | formatObj = valuePop(ctxt); |
---|
531 | CAST_TO_NUMBER; |
---|
532 | numberObj = valuePop(ctxt); |
---|
533 | break; |
---|
534 | default: |
---|
535 | XP_ERROR(XPATH_INVALID_ARITY); |
---|
536 | } |
---|
537 | |
---|
538 | if (formatValues != NULL) { |
---|
539 | if (xsltFormatNumberConversion(formatValues, |
---|
540 | formatObj->stringval, |
---|
541 | numberObj->floatval, |
---|
542 | &result) == XPATH_EXPRESSION_OK) { |
---|
543 | valuePush(ctxt, xmlXPathNewString(result)); |
---|
544 | xmlFree(result); |
---|
545 | } |
---|
546 | } |
---|
547 | |
---|
548 | xmlXPathFreeObject(numberObj); |
---|
549 | xmlXPathFreeObject(formatObj); |
---|
550 | xmlXPathFreeObject(decimalObj); |
---|
551 | } |
---|
552 | |
---|
553 | /** |
---|
554 | * xsltGenerateIdFunction: |
---|
555 | * @ctxt: the XPath Parser context |
---|
556 | * @nargs: the number of arguments |
---|
557 | * |
---|
558 | * Implement the generate-id() XSLT function |
---|
559 | * string generate-id(node-set?) |
---|
560 | */ |
---|
561 | void |
---|
562 | xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
---|
563 | xmlNodePtr cur = NULL; |
---|
564 | unsigned long val; |
---|
565 | xmlChar str[20]; |
---|
566 | |
---|
567 | if (nargs == 0) { |
---|
568 | cur = ctxt->context->node; |
---|
569 | } else if (nargs == 1) { |
---|
570 | xmlXPathObjectPtr obj; |
---|
571 | xmlNodeSetPtr nodelist; |
---|
572 | int i, ret; |
---|
573 | |
---|
574 | if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) { |
---|
575 | ctxt->error = XPATH_INVALID_TYPE; |
---|
576 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
577 | "generate-id() : invalid arg expecting a node-set\n"); |
---|
578 | return; |
---|
579 | } |
---|
580 | obj = valuePop(ctxt); |
---|
581 | nodelist = obj->nodesetval; |
---|
582 | if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) { |
---|
583 | xmlXPathFreeObject(obj); |
---|
584 | valuePush(ctxt, xmlXPathNewCString("")); |
---|
585 | return; |
---|
586 | } |
---|
587 | cur = nodelist->nodeTab[0]; |
---|
588 | for (i = 1;i < nodelist->nodeNr;i++) { |
---|
589 | ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]); |
---|
590 | if (ret == -1) |
---|
591 | cur = nodelist->nodeTab[i]; |
---|
592 | } |
---|
593 | xmlXPathFreeObject(obj); |
---|
594 | } else { |
---|
595 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
596 | "generate-id() : invalid number of args %d\n", nargs); |
---|
597 | ctxt->error = XPATH_INVALID_ARITY; |
---|
598 | return; |
---|
599 | } |
---|
600 | /* |
---|
601 | * Okay this is ugly but should work, use the NodePtr address |
---|
602 | * to forge the ID |
---|
603 | */ |
---|
604 | val = (unsigned long)((char *)cur - (char *)0); |
---|
605 | val /= sizeof(xmlNode); |
---|
606 | sprintf((char *)str, "id%ld", val); |
---|
607 | valuePush(ctxt, xmlXPathNewString(str)); |
---|
608 | } |
---|
609 | |
---|
610 | /** |
---|
611 | * xsltSystemPropertyFunction: |
---|
612 | * @ctxt: the XPath Parser context |
---|
613 | * @nargs: the number of arguments |
---|
614 | * |
---|
615 | * Implement the system-property() XSLT function |
---|
616 | * object system-property(string) |
---|
617 | */ |
---|
618 | void |
---|
619 | xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
---|
620 | xmlXPathObjectPtr obj; |
---|
621 | xmlChar *prefix, *name; |
---|
622 | const xmlChar *nsURI = NULL; |
---|
623 | |
---|
624 | if (nargs != 1) { |
---|
625 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
626 | "system-property() : expects one string arg\n"); |
---|
627 | ctxt->error = XPATH_INVALID_ARITY; |
---|
628 | return; |
---|
629 | } |
---|
630 | if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
---|
631 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
632 | "system-property() : invalid arg expecting a string\n"); |
---|
633 | ctxt->error = XPATH_INVALID_TYPE; |
---|
634 | return; |
---|
635 | } |
---|
636 | obj = valuePop(ctxt); |
---|
637 | if (obj->stringval == NULL) { |
---|
638 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
---|
639 | } else { |
---|
640 | name = xmlSplitQName2(obj->stringval, &prefix); |
---|
641 | if (name == NULL) { |
---|
642 | name = xmlStrdup(obj->stringval); |
---|
643 | } else { |
---|
644 | nsURI = xmlXPathNsLookup(ctxt->context, prefix); |
---|
645 | if (nsURI == NULL) { |
---|
646 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
647 | "system-property() : prefix %s is not bound\n", prefix); |
---|
648 | } |
---|
649 | } |
---|
650 | |
---|
651 | if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) { |
---|
652 | #ifdef DOCBOOK_XSL_HACK |
---|
653 | if (xmlStrEqual(name, (const xmlChar *)"vendor")) { |
---|
654 | xsltStylesheetPtr sheet; |
---|
655 | xsltTransformContextPtr tctxt; |
---|
656 | |
---|
657 | tctxt = xsltXPathGetTransformContext(ctxt); |
---|
658 | if ((tctxt != NULL) && (tctxt->inst != NULL) && |
---|
659 | (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) && |
---|
660 | (tctxt->inst->parent != NULL) && |
---|
661 | (xmlStrEqual(tctxt->inst->parent->name, |
---|
662 | BAD_CAST "template"))) |
---|
663 | sheet = tctxt->style; |
---|
664 | else |
---|
665 | sheet = NULL; |
---|
666 | if ((sheet != NULL) && (sheet->doc != NULL) && |
---|
667 | (sheet->doc->URL != NULL) && |
---|
668 | (xmlStrstr(sheet->doc->URL, |
---|
669 | (const xmlChar *)"chunk") != NULL)) { |
---|
670 | valuePush(ctxt, xmlXPathNewString( |
---|
671 | (const xmlChar *)"libxslt (SAXON 6.2 compatible)")); |
---|
672 | |
---|
673 | } else { |
---|
674 | valuePush(ctxt, xmlXPathNewString( |
---|
675 | (const xmlChar *)XSLT_DEFAULT_VENDOR)); |
---|
676 | } |
---|
677 | } else |
---|
678 | #else |
---|
679 | if (xmlStrEqual(name, (const xmlChar *)"vendor")) { |
---|
680 | valuePush(ctxt, xmlXPathNewString( |
---|
681 | (const xmlChar *)XSLT_DEFAULT_VENDOR)); |
---|
682 | } else |
---|
683 | #endif |
---|
684 | if (xmlStrEqual(name, (const xmlChar *)"version")) { |
---|
685 | valuePush(ctxt, xmlXPathNewString( |
---|
686 | (const xmlChar *)XSLT_DEFAULT_VERSION)); |
---|
687 | } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) { |
---|
688 | valuePush(ctxt, xmlXPathNewString( |
---|
689 | (const xmlChar *)XSLT_DEFAULT_URL)); |
---|
690 | } else { |
---|
691 | valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
---|
692 | } |
---|
693 | } |
---|
694 | if (name != NULL) |
---|
695 | xmlFree(name); |
---|
696 | if (prefix != NULL) |
---|
697 | xmlFree(prefix); |
---|
698 | } |
---|
699 | xmlXPathFreeObject(obj); |
---|
700 | } |
---|
701 | |
---|
702 | /** |
---|
703 | * xsltElementAvailableFunction: |
---|
704 | * @ctxt: the XPath Parser context |
---|
705 | * @nargs: the number of arguments |
---|
706 | * |
---|
707 | * Implement the element-available() XSLT function |
---|
708 | * boolean element-available(string) |
---|
709 | */ |
---|
710 | void |
---|
711 | xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
---|
712 | xmlXPathObjectPtr obj; |
---|
713 | xmlChar *prefix, *name; |
---|
714 | const xmlChar *nsURI = NULL; |
---|
715 | xsltTransformContextPtr tctxt; |
---|
716 | |
---|
717 | if (nargs != 1) { |
---|
718 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
719 | "element-available() : 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 | "element-available() : invalid arg expecting a string\n"); |
---|
726 | ctxt->error = XPATH_INVALID_TYPE; |
---|
727 | return; |
---|
728 | } |
---|
729 | obj = valuePop(ctxt); |
---|
730 | tctxt = xsltXPathGetTransformContext(ctxt); |
---|
731 | if (tctxt == NULL) { |
---|
732 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
733 | "element-available() : internal error tctxt == NULL\n"); |
---|
734 | xmlXPathFreeObject(obj); |
---|
735 | valuePush(ctxt, xmlXPathNewBoolean(0)); |
---|
736 | return; |
---|
737 | } |
---|
738 | |
---|
739 | |
---|
740 | name = xmlSplitQName2(obj->stringval, &prefix); |
---|
741 | if (name == NULL) { |
---|
742 | xmlNsPtr ns; |
---|
743 | |
---|
744 | name = xmlStrdup(obj->stringval); |
---|
745 | ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL); |
---|
746 | if (ns != NULL) nsURI = xmlStrdup(ns->href); |
---|
747 | } else { |
---|
748 | nsURI = xmlXPathNsLookup(ctxt->context, prefix); |
---|
749 | if (nsURI == NULL) { |
---|
750 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
751 | "element-available() : prefix %s is not bound\n", prefix); |
---|
752 | } |
---|
753 | } |
---|
754 | |
---|
755 | if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) { |
---|
756 | valuePush(ctxt, xmlXPathNewBoolean(1)); |
---|
757 | } else { |
---|
758 | valuePush(ctxt, xmlXPathNewBoolean(0)); |
---|
759 | } |
---|
760 | |
---|
761 | xmlXPathFreeObject(obj); |
---|
762 | if (name != NULL) |
---|
763 | xmlFree(name); |
---|
764 | if (prefix != NULL) |
---|
765 | xmlFree(prefix); |
---|
766 | } |
---|
767 | |
---|
768 | /** |
---|
769 | * xsltFunctionAvailableFunction: |
---|
770 | * @ctxt: the XPath Parser context |
---|
771 | * @nargs: the number of arguments |
---|
772 | * |
---|
773 | * Implement the function-available() XSLT function |
---|
774 | * boolean function-available(string) |
---|
775 | */ |
---|
776 | void |
---|
777 | xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
---|
778 | xmlXPathObjectPtr obj; |
---|
779 | xmlChar *prefix, *name; |
---|
780 | const xmlChar *nsURI = NULL; |
---|
781 | |
---|
782 | if (nargs != 1) { |
---|
783 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
784 | "function-available() : expects one string arg\n"); |
---|
785 | ctxt->error = XPATH_INVALID_ARITY; |
---|
786 | return; |
---|
787 | } |
---|
788 | if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
---|
789 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
790 | "function-available() : invalid arg expecting a string\n"); |
---|
791 | ctxt->error = XPATH_INVALID_TYPE; |
---|
792 | return; |
---|
793 | } |
---|
794 | obj = valuePop(ctxt); |
---|
795 | |
---|
796 | name = xmlSplitQName2(obj->stringval, &prefix); |
---|
797 | if (name == NULL) { |
---|
798 | name = xmlStrdup(obj->stringval); |
---|
799 | } else { |
---|
800 | nsURI = xmlXPathNsLookup(ctxt->context, prefix); |
---|
801 | if (nsURI == NULL) { |
---|
802 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
803 | "function-available() : prefix %s is not bound\n", prefix); |
---|
804 | } |
---|
805 | } |
---|
806 | |
---|
807 | if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) { |
---|
808 | valuePush(ctxt, xmlXPathNewBoolean(1)); |
---|
809 | } else { |
---|
810 | valuePush(ctxt, xmlXPathNewBoolean(0)); |
---|
811 | } |
---|
812 | |
---|
813 | xmlXPathFreeObject(obj); |
---|
814 | if (name != NULL) |
---|
815 | xmlFree(name); |
---|
816 | if (prefix != NULL) |
---|
817 | xmlFree(prefix); |
---|
818 | } |
---|
819 | |
---|
820 | /** |
---|
821 | * xsltCurrentFunction: |
---|
822 | * @ctxt: the XPath Parser context |
---|
823 | * @nargs: the number of arguments |
---|
824 | * |
---|
825 | * Implement the current() XSLT function |
---|
826 | * node-set current() |
---|
827 | */ |
---|
828 | static void |
---|
829 | xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
---|
830 | xsltTransformContextPtr tctxt; |
---|
831 | |
---|
832 | if (nargs != 0) { |
---|
833 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
834 | "current() : function uses no argument\n"); |
---|
835 | ctxt->error = XPATH_INVALID_ARITY; |
---|
836 | return; |
---|
837 | } |
---|
838 | tctxt = xsltXPathGetTransformContext(ctxt); |
---|
839 | if (tctxt == NULL) { |
---|
840 | xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
---|
841 | "current() : internal error tctxt == NULL\n"); |
---|
842 | valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
---|
843 | } else { |
---|
844 | valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */ |
---|
845 | } |
---|
846 | } |
---|
847 | |
---|
848 | /************************************************************************ |
---|
849 | * * |
---|
850 | * Registration of XSLT and libxslt functions * |
---|
851 | * * |
---|
852 | ************************************************************************/ |
---|
853 | |
---|
854 | /** |
---|
855 | * xsltRegisterAllFunctions: |
---|
856 | * @ctxt: the XPath context |
---|
857 | * |
---|
858 | * Registers all default XSLT functions in this context |
---|
859 | */ |
---|
860 | void |
---|
861 | xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) |
---|
862 | { |
---|
863 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current", |
---|
864 | xsltCurrentFunction); |
---|
865 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document", |
---|
866 | xsltDocumentFunction); |
---|
867 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction); |
---|
868 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri", |
---|
869 | xsltUnparsedEntityURIFunction); |
---|
870 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number", |
---|
871 | xsltFormatNumberFunction); |
---|
872 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id", |
---|
873 | xsltGenerateIdFunction); |
---|
874 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property", |
---|
875 | xsltSystemPropertyFunction); |
---|
876 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available", |
---|
877 | xsltElementAvailableFunction); |
---|
878 | xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available", |
---|
879 | xsltFunctionAvailableFunction); |
---|
880 | } |
---|