• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
xsltXPathFunctionLookup(xmlXPathContextPtr ctxt,const xmlChar * name,const xmlChar * ns_uri)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
xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt,xmlChar * URI)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
xsltDocumentFunction(xmlXPathParserContextPtr ctxt,int nargs)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
xsltKeyFunction(xmlXPathParserContextPtr ctxt,int nargs)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
xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt,int nargs)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
xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt,int nargs)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
xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt,int nargs)655 xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
656     xmlNodePtr cur = NULL;
657     xmlXPathObjectPtr obj = NULL;
658     long val;
659     xmlChar str[30];
660     xmlDocPtr doc;
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      * Okay this is ugly but should work, use the NodePtr address
695      * to forge the ID
696      */
697     if (cur->type != XML_NAMESPACE_DECL)
698         doc = cur->doc;
699     else {
700         xmlNsPtr ns = (xmlNsPtr) cur;
701 
702         if (ns->context != NULL)
703             doc = ns->context;
704         else
705             doc = ctxt->context->doc;
706 
707     }
708 
709     if (obj)
710         xmlXPathFreeObject(obj);
711 
712     val = (long)((char *)cur - (char *)doc);
713     if (val >= 0) {
714       sprintf((char *)str, "idp%ld", val);
715     } else {
716       sprintf((char *)str, "idm%ld", -val);
717     }
718     valuePush(ctxt, xmlXPathNewString(str));
719 }
720 
721 /**
722  * xsltSystemPropertyFunction:
723  * @ctxt:  the XPath Parser context
724  * @nargs:  the number of arguments
725  *
726  * Implement the system-property() XSLT function
727  *   object system-property(string)
728  */
729 void
xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt,int nargs)730 xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
731     xmlXPathObjectPtr obj;
732     xmlChar *prefix, *name;
733     const xmlChar *nsURI = NULL;
734 
735     if (nargs != 1) {
736 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
737 		"system-property() : expects one string arg\n");
738 	ctxt->error = XPATH_INVALID_ARITY;
739 	return;
740     }
741     if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
742 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
743 	    "system-property() : invalid arg expecting a string\n");
744 	ctxt->error = XPATH_INVALID_TYPE;
745 	return;
746     }
747     obj = valuePop(ctxt);
748     if (obj->stringval == NULL) {
749 	valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
750     } else {
751 	name = xmlSplitQName2(obj->stringval, &prefix);
752 	if (name == NULL) {
753 	    name = xmlStrdup(obj->stringval);
754 	} else {
755 	    nsURI = xmlXPathNsLookup(ctxt->context, prefix);
756 	    if (nsURI == NULL) {
757 		xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
758 		    "system-property() : prefix %s is not bound\n", prefix);
759 	    }
760 	}
761 
762 	if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) {
763 #ifdef DOCBOOK_XSL_HACK
764 	    if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
765 		xsltStylesheetPtr sheet;
766 		xsltTransformContextPtr tctxt;
767 
768 		tctxt = xsltXPathGetTransformContext(ctxt);
769 		if ((tctxt != NULL) && (tctxt->inst != NULL) &&
770 		    (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) &&
771 		    (tctxt->inst->parent != NULL) &&
772 		    (xmlStrEqual(tctxt->inst->parent->name,
773 				 BAD_CAST "template")))
774 		    sheet = tctxt->style;
775 		else
776 		    sheet = NULL;
777 		if ((sheet != NULL) && (sheet->doc != NULL) &&
778 		    (sheet->doc->URL != NULL) &&
779 		    (xmlStrstr(sheet->doc->URL,
780 			       (const xmlChar *)"chunk") != NULL)) {
781 		    valuePush(ctxt, xmlXPathNewString(
782 			(const xmlChar *)"libxslt (SAXON 6.2 compatible)"));
783 
784 		} else {
785 		    valuePush(ctxt, xmlXPathNewString(
786 			(const xmlChar *)XSLT_DEFAULT_VENDOR));
787 		}
788 	    } else
789 #else
790 	    if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
791 		valuePush(ctxt, xmlXPathNewString(
792 			  (const xmlChar *)XSLT_DEFAULT_VENDOR));
793 	    } else
794 #endif
795 	    if (xmlStrEqual(name, (const xmlChar *)"version")) {
796 		valuePush(ctxt, xmlXPathNewString(
797 		    (const xmlChar *)XSLT_DEFAULT_VERSION));
798 	    } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) {
799 		valuePush(ctxt, xmlXPathNewString(
800 		    (const xmlChar *)XSLT_DEFAULT_URL));
801 	    } else {
802 		valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
803 	    }
804 	}
805 	if (name != NULL)
806 	    xmlFree(name);
807 	if (prefix != NULL)
808 	    xmlFree(prefix);
809     }
810     xmlXPathFreeObject(obj);
811 }
812 
813 /**
814  * xsltElementAvailableFunction:
815  * @ctxt:  the XPath Parser context
816  * @nargs:  the number of arguments
817  *
818  * Implement the element-available() XSLT function
819  *   boolean element-available(string)
820  */
821 void
xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt,int nargs)822 xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
823     xmlXPathObjectPtr obj;
824     xmlChar *prefix, *name;
825     const xmlChar *nsURI = NULL;
826     xsltTransformContextPtr tctxt;
827 
828     if (nargs != 1) {
829 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
830 		"element-available() : expects one string arg\n");
831 	ctxt->error = XPATH_INVALID_ARITY;
832 	return;
833     }
834     xmlXPathStringFunction(ctxt, 1);
835     if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
836 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
837 	    "element-available() : invalid arg expecting a string\n");
838 	ctxt->error = XPATH_INVALID_TYPE;
839 	return;
840     }
841     obj = valuePop(ctxt);
842     tctxt = xsltXPathGetTransformContext(ctxt);
843     if (tctxt == NULL) {
844 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
845 		"element-available() : internal error tctxt == NULL\n");
846 	xmlXPathFreeObject(obj);
847 	valuePush(ctxt, xmlXPathNewBoolean(0));
848 	return;
849     }
850 
851 
852     name = xmlSplitQName2(obj->stringval, &prefix);
853     if (name == NULL) {
854 	xmlNsPtr ns;
855 
856 	name = xmlStrdup(obj->stringval);
857 	ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL);
858 	if (ns != NULL) nsURI = xmlStrdup(ns->href);
859     } else {
860 	nsURI = xmlXPathNsLookup(ctxt->context, prefix);
861 	if (nsURI == NULL) {
862 	    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
863 		"element-available() : prefix %s is not bound\n", prefix);
864 	}
865     }
866 
867     if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) {
868 	valuePush(ctxt, xmlXPathNewBoolean(1));
869     } else {
870 	valuePush(ctxt, xmlXPathNewBoolean(0));
871     }
872 
873     xmlXPathFreeObject(obj);
874     if (name != NULL)
875 	xmlFree(name);
876     if (prefix != NULL)
877 	xmlFree(prefix);
878 }
879 
880 /**
881  * xsltFunctionAvailableFunction:
882  * @ctxt:  the XPath Parser context
883  * @nargs:  the number of arguments
884  *
885  * Implement the function-available() XSLT function
886  *   boolean function-available(string)
887  */
888 void
xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt,int nargs)889 xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
890     xmlXPathObjectPtr obj;
891     xmlChar *prefix, *name;
892     const xmlChar *nsURI = NULL;
893 
894     if (nargs != 1) {
895 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
896 		"function-available() : expects one string arg\n");
897 	ctxt->error = XPATH_INVALID_ARITY;
898 	return;
899     }
900     xmlXPathStringFunction(ctxt, 1);
901     if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
902 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
903 	    "function-available() : invalid arg expecting a string\n");
904 	ctxt->error = XPATH_INVALID_TYPE;
905 	return;
906     }
907     obj = valuePop(ctxt);
908 
909     name = xmlSplitQName2(obj->stringval, &prefix);
910     if (name == NULL) {
911 	name = xmlStrdup(obj->stringval);
912     } else {
913 	nsURI = xmlXPathNsLookup(ctxt->context, prefix);
914 	if (nsURI == NULL) {
915 	    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
916 		"function-available() : prefix %s is not bound\n", prefix);
917 	}
918     }
919 
920     if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
921 	valuePush(ctxt, xmlXPathNewBoolean(1));
922     } else {
923 	valuePush(ctxt, xmlXPathNewBoolean(0));
924     }
925 
926     xmlXPathFreeObject(obj);
927     if (name != NULL)
928 	xmlFree(name);
929     if (prefix != NULL)
930 	xmlFree(prefix);
931 }
932 
933 /**
934  * xsltCurrentFunction:
935  * @ctxt:  the XPath Parser context
936  * @nargs:  the number of arguments
937  *
938  * Implement the current() XSLT function
939  *   node-set current()
940  */
941 static void
xsltCurrentFunction(xmlXPathParserContextPtr ctxt,int nargs)942 xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
943     xsltTransformContextPtr tctxt;
944 
945     if (nargs != 0) {
946 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
947 		"current() : function uses no argument\n");
948 	ctxt->error = XPATH_INVALID_ARITY;
949 	return;
950     }
951     tctxt = xsltXPathGetTransformContext(ctxt);
952     if (tctxt == NULL) {
953 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
954 		"current() : internal error tctxt == NULL\n");
955 	valuePush(ctxt, xmlXPathNewNodeSet(NULL));
956     } else {
957 	valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
958     }
959 }
960 
961 /************************************************************************
962  * 									*
963  * 		Registration of XSLT and libxslt functions		*
964  * 									*
965  ************************************************************************/
966 
967 /**
968  * xsltRegisterAllFunctions:
969  * @ctxt:  the XPath context
970  *
971  * Registers all default XSLT functions in this context
972  */
973 void
xsltRegisterAllFunctions(xmlXPathContextPtr ctxt)974 xsltRegisterAllFunctions(xmlXPathContextPtr ctxt)
975 {
976     xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current",
977                          xsltCurrentFunction);
978     xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document",
979                          xsltDocumentFunction);
980     xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction);
981     xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri",
982                          xsltUnparsedEntityURIFunction);
983     xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number",
984                          xsltFormatNumberFunction);
985     xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id",
986                          xsltGenerateIdFunction);
987     xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property",
988                          xsltSystemPropertyFunction);
989     xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available",
990                          xsltElementAvailableFunction);
991     xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available",
992                          xsltFunctionAvailableFunction);
993 }
994