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