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