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