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