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