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