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