OLD | NEW |
| (Empty) |
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 /* | |
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 | |
184 if (resObj == NULL) | |
185 goto out_fragment; | |
186 | |
187 switch (resObj->type) { | |
188 case XPATH_NODESET: | |
189 break; | |
190 case XPATH_UNDEFINED: | |
191 case XPATH_BOOLEAN: | |
192 case XPATH_NUMBER: | |
193 case XPATH_STRING: | |
194 case XPATH_POINT: | |
195 case XPATH_USERS: | |
196 case XPATH_XSLT_TREE: | |
197 case XPATH_RANGE: | |
198 case XPATH_LOCATIONSET: | |
199 xsltTransformError(tctxt, NULL, NULL, | |
200 "document() : XPointer does not select a node set: #%s\n", | |
201 fragment); | |
202 goto out_object; | |
203 } | |
204 | |
205 valuePush(ctxt, resObj); | |
206 xmlFree(fragment); | |
207 return; | |
208 | |
209 out_object: | |
210 xmlXPathFreeObject(resObj); | |
211 | |
212 out_fragment: | |
213 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); | |
214 xmlFree(fragment); | |
215 } | |
216 | |
217 /** | |
218 * xsltDocumentFunction: | |
219 * @ctxt: the XPath Parser context | |
220 * @nargs: the number of arguments | |
221 * | |
222 * Implement the document() XSLT function | |
223 * node-set document(object, node-set?) | |
224 */ | |
225 void | |
226 xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) | |
227 { | |
228 xmlXPathObjectPtr obj, obj2 = NULL; | |
229 xmlChar *base = NULL, *URI; | |
230 | |
231 | |
232 if ((nargs < 1) || (nargs > 2)) { | |
233 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
234 "document() : invalid number of args %d\n", | |
235 nargs); | |
236 ctxt->error = XPATH_INVALID_ARITY; | |
237 return; | |
238 } | |
239 if (ctxt->value == NULL) { | |
240 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
241 "document() : invalid arg value\n"); | |
242 ctxt->error = XPATH_INVALID_TYPE; | |
243 return; | |
244 } | |
245 | |
246 if (nargs == 2) { | |
247 if (ctxt->value->type != XPATH_NODESET) { | |
248 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
249 "document() : invalid arg expecting a nodeset\n"); | |
250 ctxt->error = XPATH_INVALID_TYPE; | |
251 return; | |
252 } | |
253 | |
254 obj2 = valuePop(ctxt); | |
255 } | |
256 | |
257 if (ctxt->value->type == XPATH_NODESET) { | |
258 int i; | |
259 xmlXPathObjectPtr newobj, ret; | |
260 | |
261 obj = valuePop(ctxt); | |
262 ret = xmlXPathNewNodeSet(NULL); | |
263 | |
264 if ((obj != NULL) && obj->nodesetval) { | |
265 for (i = 0; i < obj->nodesetval->nodeNr; i++) { | |
266 valuePush(ctxt, | |
267 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); | |
268 xmlXPathStringFunction(ctxt, 1); | |
269 if (nargs == 2) { | |
270 valuePush(ctxt, xmlXPathObjectCopy(obj2)); | |
271 } else { | |
272 valuePush(ctxt, | |
273 xmlXPathNewNodeSet(obj->nodesetval-> | |
274 nodeTab[i])); | |
275 } | |
276 xsltDocumentFunction(ctxt, 2); | |
277 newobj = valuePop(ctxt); | |
278 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, | |
279 newobj->nodesetval); | |
280 xmlXPathFreeObject(newobj); | |
281 } | |
282 } | |
283 | |
284 if (obj != NULL) | |
285 xmlXPathFreeObject(obj); | |
286 if (obj2 != NULL) | |
287 xmlXPathFreeObject(obj2); | |
288 valuePush(ctxt, ret); | |
289 return; | |
290 } | |
291 /* | |
292 * Make sure it's converted to a string | |
293 */ | |
294 xmlXPathStringFunction(ctxt, 1); | |
295 if (ctxt->value->type != XPATH_STRING) { | |
296 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
297 "document() : invalid arg expecting a string\n"); | |
298 ctxt->error = XPATH_INVALID_TYPE; | |
299 if (obj2 != NULL) | |
300 xmlXPathFreeObject(obj2); | |
301 return; | |
302 } | |
303 obj = valuePop(ctxt); | |
304 if (obj->stringval == NULL) { | |
305 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); | |
306 } else { | |
307 xsltTransformContextPtr tctxt; | |
308 tctxt = xsltXPathGetTransformContext(ctxt); | |
309 if ((obj2 != NULL) && (obj2->nodesetval != NULL) && | |
310 (obj2->nodesetval->nodeNr > 0) && | |
311 IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { | |
312 xmlNodePtr target; | |
313 | |
314 target = obj2->nodesetval->nodeTab[0]; | |
315 if ((target->type == XML_ATTRIBUTE_NODE) || | |
316 (target->type == XML_PI_NODE)) { | |
317 target = ((xmlAttrPtr) target)->parent; | |
318 } | |
319 base = xmlNodeGetBase(target->doc, target); | |
320 } else { | |
321 if ((tctxt != NULL) && (tctxt->inst != NULL)) { | |
322 base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); | |
323 } else if ((tctxt != NULL) && (tctxt->style != NULL) && | |
324 (tctxt->style->doc != NULL)) { | |
325 base = xmlNodeGetBase(tctxt->style->doc, | |
326 (xmlNodePtr) tctxt->style->doc); | |
327 } | |
328 } | |
329 URI = xmlBuildURI(obj->stringval, base); | |
330 if (base != NULL) | |
331 xmlFree(base); | |
332 if (URI == NULL) { | |
333 if ((tctxt != NULL) && (tctxt->style != NULL) && | |
334 (tctxt->style->doc != NULL) && | |
335 (xmlStrEqual(URI, tctxt->style->doc->URL))) { | |
336 /* This selects the stylesheet's doc itself. */ | |
337 valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->do
c)); | |
338 } else { | |
339 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); | |
340 } | |
341 } else { | |
342 xsltDocumentFunctionLoadDocument( ctxt, URI ); | |
343 xmlFree(URI); | |
344 } | |
345 } | |
346 xmlXPathFreeObject(obj); | |
347 if (obj2 != NULL) | |
348 xmlXPathFreeObject(obj2); | |
349 } | |
350 | |
351 /** | |
352 * xsltKeyFunction: | |
353 * @ctxt: the XPath Parser context | |
354 * @nargs: the number of arguments | |
355 * | |
356 * Implement the key() XSLT function | |
357 * node-set key(string, object) | |
358 */ | |
359 void | |
360 xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ | |
361 xmlXPathObjectPtr obj1, obj2; | |
362 | |
363 if (nargs != 2) { | |
364 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
365 "key() : expects two arguments\n"); | |
366 ctxt->error = XPATH_INVALID_ARITY; | |
367 return; | |
368 } | |
369 | |
370 /* | |
371 * Get the key's value. | |
372 */ | |
373 obj2 = valuePop(ctxt); | |
374 xmlXPathStringFunction(ctxt, 1); | |
375 if ((obj2 == NULL) || | |
376 (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { | |
377 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
378 "key() : invalid arg expecting a string\n"); | |
379 ctxt->error = XPATH_INVALID_TYPE; | |
380 xmlXPathFreeObject(obj2); | |
381 | |
382 return; | |
383 } | |
384 /* | |
385 * Get the key's name. | |
386 */ | |
387 obj1 = valuePop(ctxt); | |
388 | |
389 if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { | |
390 int i; | |
391 xmlXPathObjectPtr newobj, ret; | |
392 | |
393 ret = xmlXPathNewNodeSet(NULL); | |
394 | |
395 if (obj2->nodesetval != NULL) { | |
396 for (i = 0; i < obj2->nodesetval->nodeNr; i++) { | |
397 valuePush(ctxt, xmlXPathObjectCopy(obj1)); | |
398 valuePush(ctxt, | |
399 xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i])); | |
400 xmlXPathStringFunction(ctxt, 1); | |
401 xsltKeyFunction(ctxt, 2); | |
402 newobj = valuePop(ctxt); | |
403 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, | |
404 newobj->nodesetval); | |
405 xmlXPathFreeObject(newobj); | |
406 } | |
407 } | |
408 valuePush(ctxt, ret); | |
409 } else { | |
410 xmlNodeSetPtr nodelist = NULL; | |
411 xmlChar *key = NULL, *value; | |
412 const xmlChar *keyURI; | |
413 xsltTransformContextPtr tctxt; | |
414 xmlChar *qname, *prefix; | |
415 xmlXPathContextPtr xpctxt = ctxt->context; | |
416 xmlNodePtr tmpNode = NULL; | |
417 xsltDocumentPtr oldDocInfo; | |
418 | |
419 tctxt = xsltXPathGetTransformContext(ctxt); | |
420 | |
421 oldDocInfo = tctxt->document; | |
422 | |
423 if (xpctxt->node == NULL) { | |
424 xsltTransformError(tctxt, NULL, tctxt->inst, | |
425 "Internal error in xsltKeyFunction(): " | |
426 "The context node is not set on the XPath context.\n"); | |
427 tctxt->state = XSLT_STATE_STOPPED; | |
428 goto error; | |
429 } | |
430 /* | |
431 * Get the associated namespace URI if qualified name | |
432 */ | |
433 qname = obj1->stringval; | |
434 key = xmlSplitQName2(qname, &prefix); | |
435 if (key == NULL) { | |
436 key = xmlStrdup(obj1->stringval); | |
437 keyURI = NULL; | |
438 if (prefix != NULL) | |
439 xmlFree(prefix); | |
440 } else { | |
441 if (prefix != NULL) { | |
442 keyURI = xmlXPathNsLookup(xpctxt, prefix); | |
443 if (keyURI == NULL) { | |
444 xsltTransformError(tctxt, NULL, tctxt->inst, | |
445 "key() : prefix %s is not bound\n", prefix); | |
446 /* | |
447 * TODO: Shouldn't we stop here? | |
448 */ | |
449 } | |
450 xmlFree(prefix); | |
451 } else { | |
452 keyURI = NULL; | |
453 } | |
454 } | |
455 | |
456 /* | |
457 * Force conversion of first arg to string | |
458 */ | |
459 valuePush(ctxt, obj2); | |
460 xmlXPathStringFunction(ctxt, 1); | |
461 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { | |
462 xsltTransformError(tctxt, NULL, tctxt->inst, | |
463 "key() : invalid arg expecting a string\n"); | |
464 ctxt->error = XPATH_INVALID_TYPE; | |
465 goto error; | |
466 } | |
467 obj2 = valuePop(ctxt); | |
468 value = obj2->stringval; | |
469 | |
470 /* | |
471 * We need to ensure that ctxt->document is available for | |
472 * xsltGetKey(). | |
473 * First find the relevant doc, which is the context node's | |
474 * owner doc; using context->doc is not safe, since | |
475 * the doc could have been acquired via the document() function, | |
476 * or the doc might be a Result Tree Fragment. | |
477 * FUTURE INFO: In XSLT 2.0 the key() function takes an additional | |
478 * argument indicating the doc to use. | |
479 */ | |
480 if (xpctxt->node->type == XML_NAMESPACE_DECL) { | |
481 /* | |
482 * REVISIT: This is a libxml hack! Check xpath.c for details. | |
483 * The XPath module sets the owner element of a ns-node on | |
484 * the ns->next field. | |
485 */ | |
486 if ((((xmlNsPtr) xpctxt->node)->next != NULL) && | |
487 (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE)) | |
488 { | |
489 tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next; | |
490 } | |
491 } else | |
492 tmpNode = xpctxt->node; | |
493 | |
494 if ((tmpNode == NULL) || (tmpNode->doc == NULL)) { | |
495 xsltTransformError(tctxt, NULL, tctxt->inst, | |
496 "Internal error in xsltKeyFunction(): " | |
497 "Couldn't get the doc of the XPath context node.\n"); | |
498 goto error; | |
499 } | |
500 | |
501 if ((tctxt->document == NULL) || | |
502 (tctxt->document->doc != tmpNode->doc)) | |
503 { | |
504 if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) { | |
505 /* | |
506 * This is a Result Tree Fragment. | |
507 */ | |
508 if (tmpNode->doc->_private == NULL) { | |
509 tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc
); | |
510 if (tmpNode->doc->_private == NULL) | |
511 goto error; | |
512 } | |
513 tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private; | |
514 } else { | |
515 /* | |
516 * May be the initial source doc or a doc acquired via the | |
517 * document() function. | |
518 */ | |
519 tctxt->document = xsltFindDocument(tctxt, tmpNode->doc); | |
520 } | |
521 if (tctxt->document == NULL) { | |
522 xsltTransformError(tctxt, NULL, tctxt->inst, | |
523 "Internal error in xsltKeyFunction(): " | |
524 "Could not get the document info of a context doc.\n"); | |
525 tctxt->state = XSLT_STATE_STOPPED; | |
526 goto error; | |
527 } | |
528 } | |
529 /* | |
530 * Get/compute the key value. | |
531 */ | |
532 nodelist = xsltGetKey(tctxt, key, keyURI, value); | |
533 | |
534 error: | |
535 tctxt->document = oldDocInfo; | |
536 valuePush(ctxt, xmlXPathWrapNodeSet( | |
537 xmlXPathNodeSetMerge(NULL, nodelist))); | |
538 if (key != NULL) | |
539 xmlFree(key); | |
540 } | |
541 | |
542 if (obj1 != NULL) | |
543 xmlXPathFreeObject(obj1); | |
544 if (obj2 != NULL) | |
545 xmlXPathFreeObject(obj2); | |
546 } | |
547 | |
548 /** | |
549 * xsltUnparsedEntityURIFunction: | |
550 * @ctxt: the XPath Parser context | |
551 * @nargs: the number of arguments | |
552 * | |
553 * Implement the unparsed-entity-uri() XSLT function | |
554 * string unparsed-entity-uri(string) | |
555 */ | |
556 void | |
557 xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){ | |
558 xmlXPathObjectPtr obj; | |
559 xmlChar *str; | |
560 | |
561 if ((nargs != 1) || (ctxt->value == NULL)) { | |
562 xsltGenericError(xsltGenericErrorContext, | |
563 "unparsed-entity-uri() : expects one string arg\n"); | |
564 ctxt->error = XPATH_INVALID_ARITY; | |
565 return; | |
566 } | |
567 obj = valuePop(ctxt); | |
568 if (obj->type != XPATH_STRING) { | |
569 obj = xmlXPathConvertString(obj); | |
570 } | |
571 | |
572 str = obj->stringval; | |
573 if (str == NULL) { | |
574 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); | |
575 } else { | |
576 xmlEntityPtr entity; | |
577 | |
578 entity = xmlGetDocEntity(ctxt->context->doc, str); | |
579 if (entity == NULL) { | |
580 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); | |
581 } else { | |
582 if (entity->URI != NULL) | |
583 valuePush(ctxt, xmlXPathNewString(entity->URI)); | |
584 else | |
585 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); | |
586 } | |
587 } | |
588 xmlXPathFreeObject(obj); | |
589 } | |
590 | |
591 /** | |
592 * xsltFormatNumberFunction: | |
593 * @ctxt: the XPath Parser context | |
594 * @nargs: the number of arguments | |
595 * | |
596 * Implement the format-number() XSLT function | |
597 * string format-number(number, string, string?) | |
598 */ | |
599 void | |
600 xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) | |
601 { | |
602 xmlXPathObjectPtr numberObj = NULL; | |
603 xmlXPathObjectPtr formatObj = NULL; | |
604 xmlXPathObjectPtr decimalObj = NULL; | |
605 xsltStylesheetPtr sheet; | |
606 xsltDecimalFormatPtr formatValues = NULL; | |
607 xmlChar *result; | |
608 const xmlChar *ncname; | |
609 const xmlChar *prefix = NULL; | |
610 const xmlChar *nsUri = NULL; | |
611 xsltTransformContextPtr tctxt; | |
612 | |
613 tctxt = xsltXPathGetTransformContext(ctxt); | |
614 if ((tctxt == NULL) || (tctxt->inst == NULL)) | |
615 return; | |
616 sheet = tctxt->style; | |
617 if (sheet == NULL) | |
618 return; | |
619 formatValues = sheet->decimalFormat; | |
620 | |
621 switch (nargs) { | |
622 case 3: | |
623 CAST_TO_STRING; | |
624 decimalObj = valuePop(ctxt); | |
625 ncname = xsltSplitQName(sheet->dict, decimalObj->stringval, &prefix); | |
626 if (prefix != NULL) { | |
627 xmlNsPtr ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, prefix); | |
628 if (ns == NULL) { | |
629 xsltTransformError(tctxt, NULL, NULL, | |
630 "format-number : No namespace found for QName '%s:%s'\n", | |
631 prefix, ncname); | |
632 sheet->errors++; | |
633 ncname = NULL; | |
634 } | |
635 nsUri = ns->href; | |
636 } | |
637 if (ncname != NULL) { | |
638 formatValues = xsltDecimalFormatGetByQName(sheet, nsUri, ncname); | |
639 } | |
640 if (formatValues == NULL) { | |
641 xsltTransformError(tctxt, NULL, NULL, | |
642 "format-number() : undeclared decimal format '%s'\n", | |
643 decimalObj->stringval); | |
644 } | |
645 /* Intentional fall-through */ | |
646 case 2: | |
647 CAST_TO_STRING; | |
648 formatObj = valuePop(ctxt); | |
649 CAST_TO_NUMBER; | |
650 numberObj = valuePop(ctxt); | |
651 break; | |
652 default: | |
653 XP_ERROR(XPATH_INVALID_ARITY); | |
654 } | |
655 | |
656 if (formatValues != NULL) { | |
657 if (xsltFormatNumberConversion(formatValues, | |
658 formatObj->stringval, | |
659 numberObj->floatval, | |
660 &result) == XPATH_EXPRESSION_OK) { | |
661 valuePush(ctxt, xmlXPathNewString(result)); | |
662 xmlFree(result); | |
663 } | |
664 } | |
665 | |
666 xmlXPathFreeObject(numberObj); | |
667 xmlXPathFreeObject(formatObj); | |
668 xmlXPathFreeObject(decimalObj); | |
669 } | |
670 | |
671 /** | |
672 * xsltGenerateIdFunction: | |
673 * @ctxt: the XPath Parser context | |
674 * @nargs: the number of arguments | |
675 * | |
676 * Implement the generate-id() XSLT function | |
677 * string generate-id(node-set?) | |
678 */ | |
679 void | |
680 xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){ | |
681 static char base_address; | |
682 xmlNodePtr cur = NULL; | |
683 xmlXPathObjectPtr obj = NULL; | |
684 long val; | |
685 xmlChar str[30]; | |
686 | |
687 if (nargs == 0) { | |
688 cur = ctxt->context->node; | |
689 } else if (nargs == 1) { | |
690 xmlNodeSetPtr nodelist; | |
691 int i, ret; | |
692 | |
693 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) { | |
694 ctxt->error = XPATH_INVALID_TYPE; | |
695 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
696 "generate-id() : invalid arg expecting a node-set\n"); | |
697 return; | |
698 } | |
699 obj = valuePop(ctxt); | |
700 nodelist = obj->nodesetval; | |
701 if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) { | |
702 xmlXPathFreeObject(obj); | |
703 valuePush(ctxt, xmlXPathNewCString("")); | |
704 return; | |
705 } | |
706 cur = nodelist->nodeTab[0]; | |
707 for (i = 1;i < nodelist->nodeNr;i++) { | |
708 ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]); | |
709 if (ret == -1) | |
710 cur = nodelist->nodeTab[i]; | |
711 } | |
712 } else { | |
713 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
714 "generate-id() : invalid number of args %d\n", nargs); | |
715 ctxt->error = XPATH_INVALID_ARITY; | |
716 return; | |
717 } | |
718 | |
719 if (obj) | |
720 xmlXPathFreeObject(obj); | |
721 | |
722 val = (long)((char *)cur - (char *)&base_address); | |
723 if (val >= 0) { | |
724 snprintf((char *)str, sizeof(str), "idp%ld", val); | |
725 } else { | |
726 snprintf((char *)str, sizeof(str), "idm%ld", -val); | |
727 } | |
728 valuePush(ctxt, xmlXPathNewString(str)); | |
729 } | |
730 | |
731 /** | |
732 * xsltSystemPropertyFunction: | |
733 * @ctxt: the XPath Parser context | |
734 * @nargs: the number of arguments | |
735 * | |
736 * Implement the system-property() XSLT function | |
737 * object system-property(string) | |
738 */ | |
739 void | |
740 xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){ | |
741 xmlXPathObjectPtr obj; | |
742 xmlChar *prefix, *name; | |
743 const xmlChar *nsURI = NULL; | |
744 | |
745 if (nargs != 1) { | |
746 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
747 "system-property() : expects one string arg\n"); | |
748 ctxt->error = XPATH_INVALID_ARITY; | |
749 return; | |
750 } | |
751 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { | |
752 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
753 "system-property() : invalid arg expecting a string\n"); | |
754 ctxt->error = XPATH_INVALID_TYPE; | |
755 return; | |
756 } | |
757 obj = valuePop(ctxt); | |
758 if (obj->stringval == NULL) { | |
759 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); | |
760 } else { | |
761 name = xmlSplitQName2(obj->stringval, &prefix); | |
762 if (name == NULL) { | |
763 name = xmlStrdup(obj->stringval); | |
764 } else { | |
765 nsURI = xmlXPathNsLookup(ctxt->context, prefix); | |
766 if (nsURI == NULL) { | |
767 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NUL
L, | |
768 "system-property() : prefix %s is not bound\n", prefix); | |
769 } | |
770 } | |
771 | |
772 if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) { | |
773 #ifdef DOCBOOK_XSL_HACK | |
774 if (xmlStrEqual(name, (const xmlChar *)"vendor")) { | |
775 xsltStylesheetPtr sheet; | |
776 xsltTransformContextPtr tctxt; | |
777 | |
778 tctxt = xsltXPathGetTransformContext(ctxt); | |
779 if ((tctxt != NULL) && (tctxt->inst != NULL) && | |
780 (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) && | |
781 (tctxt->inst->parent != NULL) && | |
782 (xmlStrEqual(tctxt->inst->parent->name, | |
783 BAD_CAST "template"))) | |
784 sheet = tctxt->style; | |
785 else | |
786 sheet = NULL; | |
787 if ((sheet != NULL) && (sheet->doc != NULL) && | |
788 (sheet->doc->URL != NULL) && | |
789 (xmlStrstr(sheet->doc->URL, | |
790 (const xmlChar *)"chunk") != NULL)) { | |
791 valuePush(ctxt, xmlXPathNewString( | |
792 (const xmlChar *)"libxslt (SAXON 6.2 compatible)")); | |
793 | |
794 } else { | |
795 valuePush(ctxt, xmlXPathNewString( | |
796 (const xmlChar *)XSLT_DEFAULT_VENDOR)); | |
797 } | |
798 } else | |
799 #else | |
800 if (xmlStrEqual(name, (const xmlChar *)"vendor")) { | |
801 valuePush(ctxt, xmlXPathNewString( | |
802 (const xmlChar *)XSLT_DEFAULT_VENDOR)); | |
803 } else | |
804 #endif | |
805 if (xmlStrEqual(name, (const xmlChar *)"version")) { | |
806 valuePush(ctxt, xmlXPathNewString( | |
807 (const xmlChar *)XSLT_DEFAULT_VERSION)); | |
808 } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) { | |
809 valuePush(ctxt, xmlXPathNewString( | |
810 (const xmlChar *)XSLT_DEFAULT_URL)); | |
811 } else { | |
812 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); | |
813 } | |
814 } else { | |
815 valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); | |
816 } | |
817 if (name != NULL) | |
818 xmlFree(name); | |
819 if (prefix != NULL) | |
820 xmlFree(prefix); | |
821 } | |
822 xmlXPathFreeObject(obj); | |
823 } | |
824 | |
825 /** | |
826 * xsltElementAvailableFunction: | |
827 * @ctxt: the XPath Parser context | |
828 * @nargs: the number of arguments | |
829 * | |
830 * Implement the element-available() XSLT function | |
831 * boolean element-available(string) | |
832 */ | |
833 void | |
834 xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ | |
835 xmlXPathObjectPtr obj; | |
836 xmlChar *prefix, *name; | |
837 const xmlChar *nsURI = NULL; | |
838 xsltTransformContextPtr tctxt; | |
839 | |
840 if (nargs != 1) { | |
841 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
842 "element-available() : expects one string arg\n"); | |
843 ctxt->error = XPATH_INVALID_ARITY; | |
844 return; | |
845 } | |
846 xmlXPathStringFunction(ctxt, 1); | |
847 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { | |
848 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
849 "element-available() : invalid arg expecting a string\n"); | |
850 ctxt->error = XPATH_INVALID_TYPE; | |
851 return; | |
852 } | |
853 obj = valuePop(ctxt); | |
854 tctxt = xsltXPathGetTransformContext(ctxt); | |
855 if ((tctxt == NULL) || (tctxt->inst == NULL)) { | |
856 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
857 "element-available() : internal error tctxt == NULL\n"); | |
858 xmlXPathFreeObject(obj); | |
859 valuePush(ctxt, xmlXPathNewBoolean(0)); | |
860 return; | |
861 } | |
862 | |
863 | |
864 name = xmlSplitQName2(obj->stringval, &prefix); | |
865 if (name == NULL) { | |
866 xmlNsPtr ns; | |
867 | |
868 name = xmlStrdup(obj->stringval); | |
869 ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL); | |
870 if (ns != NULL) nsURI = ns->href; | |
871 } else { | |
872 nsURI = xmlXPathNsLookup(ctxt->context, prefix); | |
873 if (nsURI == NULL) { | |
874 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
875 "element-available() : prefix %s is not bound\n", prefix); | |
876 } | |
877 } | |
878 | |
879 if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) { | |
880 valuePush(ctxt, xmlXPathNewBoolean(1)); | |
881 } else { | |
882 valuePush(ctxt, xmlXPathNewBoolean(0)); | |
883 } | |
884 | |
885 xmlXPathFreeObject(obj); | |
886 if (name != NULL) | |
887 xmlFree(name); | |
888 if (prefix != NULL) | |
889 xmlFree(prefix); | |
890 } | |
891 | |
892 /** | |
893 * xsltFunctionAvailableFunction: | |
894 * @ctxt: the XPath Parser context | |
895 * @nargs: the number of arguments | |
896 * | |
897 * Implement the function-available() XSLT function | |
898 * boolean function-available(string) | |
899 */ | |
900 void | |
901 xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ | |
902 xmlXPathObjectPtr obj; | |
903 xmlChar *prefix, *name; | |
904 const xmlChar *nsURI = NULL; | |
905 | |
906 if (nargs != 1) { | |
907 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
908 "function-available() : expects one string arg\n"); | |
909 ctxt->error = XPATH_INVALID_ARITY; | |
910 return; | |
911 } | |
912 xmlXPathStringFunction(ctxt, 1); | |
913 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { | |
914 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
915 "function-available() : invalid arg expecting a string\n"); | |
916 ctxt->error = XPATH_INVALID_TYPE; | |
917 return; | |
918 } | |
919 obj = valuePop(ctxt); | |
920 | |
921 name = xmlSplitQName2(obj->stringval, &prefix); | |
922 if (name == NULL) { | |
923 name = xmlStrdup(obj->stringval); | |
924 } else { | |
925 nsURI = xmlXPathNsLookup(ctxt->context, prefix); | |
926 if (nsURI == NULL) { | |
927 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
928 "function-available() : prefix %s is not bound\n", prefix); | |
929 } | |
930 } | |
931 | |
932 if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) { | |
933 valuePush(ctxt, xmlXPathNewBoolean(1)); | |
934 } else { | |
935 valuePush(ctxt, xmlXPathNewBoolean(0)); | |
936 } | |
937 | |
938 xmlXPathFreeObject(obj); | |
939 if (name != NULL) | |
940 xmlFree(name); | |
941 if (prefix != NULL) | |
942 xmlFree(prefix); | |
943 } | |
944 | |
945 /** | |
946 * xsltCurrentFunction: | |
947 * @ctxt: the XPath Parser context | |
948 * @nargs: the number of arguments | |
949 * | |
950 * Implement the current() XSLT function | |
951 * node-set current() | |
952 */ | |
953 static void | |
954 xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){ | |
955 xsltTransformContextPtr tctxt; | |
956 | |
957 if (nargs != 0) { | |
958 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
959 "current() : function uses no argument\n"); | |
960 ctxt->error = XPATH_INVALID_ARITY; | |
961 return; | |
962 } | |
963 tctxt = xsltXPathGetTransformContext(ctxt); | |
964 if (tctxt == NULL) { | |
965 xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, | |
966 "current() : internal error tctxt == NULL\n"); | |
967 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); | |
968 } else { | |
969 valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */ | |
970 } | |
971 } | |
972 | |
973 /************************************************************************ | |
974 * * | |
975 * Registration of XSLT and libxslt functions * | |
976 * * | |
977 ************************************************************************/ | |
978 | |
979 /** | |
980 * xsltRegisterAllFunctions: | |
981 * @ctxt: the XPath context | |
982 * | |
983 * Registers all default XSLT functions in this context | |
984 */ | |
985 void | |
986 xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) | |
987 { | |
988 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current", | |
989 xsltCurrentFunction); | |
990 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document", | |
991 xsltDocumentFunction); | |
992 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction); | |
993 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri", | |
994 xsltUnparsedEntityURIFunction); | |
995 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number", | |
996 xsltFormatNumberFunction); | |
997 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id", | |
998 xsltGenerateIdFunction); | |
999 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property", | |
1000 xsltSystemPropertyFunction); | |
1001 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available", | |
1002 xsltElementAvailableFunction); | |
1003 xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available", | |
1004 xsltFunctionAvailableFunction); | |
1005 } | |
OLD | NEW |