| /* |
| * functions.c: Implementation of the XSLT extra functions |
| * |
| * Reference: |
| * http://www.w3.org/TR/1999/REC-xslt-19991116 |
| * |
| * See Copyright for the status of this software. |
| * |
| * daniel@veillard.com |
| * Bjorn Reese <breese@users.sourceforge.net> for number formatting |
| */ |
| |
| #define IN_LIBXSLT |
| #include "libxslt.h" |
| |
| #include <string.h> |
| |
| #ifdef HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| #ifdef HAVE_CTYPE_H |
| #include <ctype.h> |
| #endif |
| |
| #include <libxml/xmlmemory.h> |
| #include <libxml/parser.h> |
| #include <libxml/tree.h> |
| #include <libxml/valid.h> |
| #include <libxml/hash.h> |
| #include <libxml/xmlerror.h> |
| #include <libxml/xpath.h> |
| #include <libxml/xpathInternals.h> |
| #include <libxml/parserInternals.h> |
| #include <libxml/uri.h> |
| #include <libxml/xpointer.h> |
| #include "xslt.h" |
| #include "xsltInternals.h" |
| #include "xsltutils.h" |
| #include "functions.h" |
| #include "extensions.h" |
| #include "numbersInternals.h" |
| #include "keys.h" |
| #include "documents.h" |
| |
| #ifdef WITH_XSLT_DEBUG |
| #define WITH_XSLT_DEBUG_FUNCTION |
| #endif |
| |
| /* |
| * Some versions of DocBook XSL use the vendor string to detect |
| * supporting chunking, this is a workaround to be considered |
| * in the list of decent XSLT processors <grin/> |
| */ |
| #define DOCBOOK_XSL_HACK |
| |
| /** |
| * xsltXPathFunctionLookup: |
| * @ctxt: a void * but the XSLT transformation context actually |
| * @name: the function name |
| * @ns_uri: the function namespace URI |
| * |
| * This is the entry point when a function is needed by the XPath |
| * interpretor. |
| * |
| * Returns the callback function or NULL if not found |
| */ |
| xmlXPathFunction |
| xsltXPathFunctionLookup (xmlXPathContextPtr ctxt, |
| const xmlChar *name, const xmlChar *ns_uri) { |
| xmlXPathFunction ret; |
| |
| if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL)) |
| return (NULL); |
| |
| #ifdef WITH_XSLT_DEBUG_FUNCTION |
| xsltGenericDebug(xsltGenericDebugContext, |
| "Lookup function {%s}%s\n", ns_uri, name); |
| #endif |
| |
| /* give priority to context-level functions */ |
| /* |
| ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); |
| */ |
| XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri); |
| |
| if (ret == NULL) |
| ret = xsltExtModuleFunctionLookup(name, ns_uri); |
| |
| #ifdef WITH_XSLT_DEBUG_FUNCTION |
| if (ret != NULL) |
| xsltGenericDebug(xsltGenericDebugContext, |
| "found function %s\n", name); |
| #endif |
| return(ret); |
| } |
| |
| |
| /************************************************************************ |
| * * |
| * Module interfaces * |
| * * |
| ************************************************************************/ |
| |
| static void |
| xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI) |
| { |
| xsltTransformContextPtr tctxt; |
| xmlURIPtr uri; |
| xmlChar *fragment; |
| xsltDocumentPtr idoc; /* document info */ |
| xmlDocPtr doc; |
| xmlXPathContextPtr xptrctxt = NULL; |
| xmlXPathObjectPtr resObj = NULL; |
| |
| tctxt = xsltXPathGetTransformContext(ctxt); |
| if (tctxt == NULL) { |
| xsltTransformError(NULL, NULL, NULL, |
| "document() : internal error tctxt == NULL\n"); |
| valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
| return; |
| } |
| |
| uri = xmlParseURI((const char *) URI); |
| if (uri == NULL) { |
| xsltTransformError(tctxt, NULL, NULL, |
| "document() : failed to parse URI\n"); |
| valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
| return; |
| } |
| |
| /* |
| * check for and remove fragment identifier |
| */ |
| fragment = (xmlChar *)uri->fragment; |
| if (fragment != NULL) { |
| xmlChar *newURI; |
| uri->fragment = NULL; |
| newURI = xmlSaveUri(uri); |
| idoc = xsltLoadDocument(tctxt, newURI); |
| xmlFree(newURI); |
| } else |
| idoc = xsltLoadDocument(tctxt, URI); |
| xmlFreeURI(uri); |
| |
| if (idoc == NULL) { |
| if ((URI == NULL) || |
| (URI[0] == '#') || |
| ((tctxt->style->doc != NULL) && |
| (xmlStrEqual(tctxt->style->doc->URL, URI)))) |
| { |
| /* |
| * This selects the stylesheet's doc itself. |
| */ |
| doc = tctxt->style->doc; |
| } else { |
| valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
| |
| if (fragment != NULL) |
| xmlFree(fragment); |
| |
| return; |
| } |
| } else |
| doc = idoc->doc; |
| |
| if (fragment == NULL) { |
| valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc)); |
| return; |
| } |
| |
| /* use XPointer of HTML location for fragment ID */ |
| #ifdef LIBXML_XPTR_ENABLED |
| xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); |
| if (xptrctxt == NULL) { |
| xsltTransformError(tctxt, NULL, NULL, |
| "document() : internal error xptrctxt == NULL\n"); |
| goto out_fragment; |
| } |
| |
| resObj = xmlXPtrEval(fragment, xptrctxt); |
| xmlXPathFreeContext(xptrctxt); |
| #endif |
| xmlFree(fragment); |
| |
| if (resObj == NULL) |
| goto out_fragment; |
| |
| switch (resObj->type) { |
| case XPATH_NODESET: |
| break; |
| case XPATH_UNDEFINED: |
| case XPATH_BOOLEAN: |
| case XPATH_NUMBER: |
| case XPATH_STRING: |
| case XPATH_POINT: |
| case XPATH_USERS: |
| case XPATH_XSLT_TREE: |
| case XPATH_RANGE: |
| case XPATH_LOCATIONSET: |
| xsltTransformError(tctxt, NULL, NULL, |
| "document() : XPointer does not select a node set: #%s\n", |
| fragment); |
| goto out_object; |
| } |
| |
| valuePush(ctxt, resObj); |
| return; |
| |
| out_object: |
| xmlXPathFreeObject(resObj); |
| |
| out_fragment: |
| valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
| } |
| |
| /** |
| * xsltDocumentFunction: |
| * @ctxt: the XPath Parser context |
| * @nargs: the number of arguments |
| * |
| * Implement the document() XSLT function |
| * node-set document(object, node-set?) |
| */ |
| void |
| xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) |
| { |
| xmlXPathObjectPtr obj, obj2 = NULL; |
| xmlChar *base = NULL, *URI; |
| |
| |
| if ((nargs < 1) || (nargs > 2)) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "document() : invalid number of args %d\n", |
| nargs); |
| ctxt->error = XPATH_INVALID_ARITY; |
| return; |
| } |
| if (ctxt->value == NULL) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "document() : invalid arg value\n"); |
| ctxt->error = XPATH_INVALID_TYPE; |
| return; |
| } |
| |
| if (nargs == 2) { |
| if (ctxt->value->type != XPATH_NODESET) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "document() : invalid arg expecting a nodeset\n"); |
| ctxt->error = XPATH_INVALID_TYPE; |
| return; |
| } |
| |
| obj2 = valuePop(ctxt); |
| } |
| |
| if (ctxt->value->type == XPATH_NODESET) { |
| int i; |
| xmlXPathObjectPtr newobj, ret; |
| |
| obj = valuePop(ctxt); |
| ret = xmlXPathNewNodeSet(NULL); |
| |
| if (obj->nodesetval) { |
| for (i = 0; i < obj->nodesetval->nodeNr; i++) { |
| valuePush(ctxt, |
| xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); |
| xmlXPathStringFunction(ctxt, 1); |
| if (nargs == 2) { |
| valuePush(ctxt, xmlXPathObjectCopy(obj2)); |
| } else { |
| valuePush(ctxt, |
| xmlXPathNewNodeSet(obj->nodesetval-> |
| nodeTab[i])); |
| } |
| xsltDocumentFunction(ctxt, 2); |
| newobj = valuePop(ctxt); |
| ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, |
| newobj->nodesetval); |
| xmlXPathFreeObject(newobj); |
| } |
| } |
| |
| xmlXPathFreeObject(obj); |
| if (obj2 != NULL) |
| xmlXPathFreeObject(obj2); |
| valuePush(ctxt, ret); |
| return; |
| } |
| /* |
| * Make sure it's converted to a string |
| */ |
| xmlXPathStringFunction(ctxt, 1); |
| if (ctxt->value->type != XPATH_STRING) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "document() : invalid arg expecting a string\n"); |
| ctxt->error = XPATH_INVALID_TYPE; |
| if (obj2 != NULL) |
| xmlXPathFreeObject(obj2); |
| return; |
| } |
| obj = valuePop(ctxt); |
| if (obj->stringval == NULL) { |
| valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
| } else { |
| if ((obj2 != NULL) && (obj2->nodesetval != NULL) && |
| (obj2->nodesetval->nodeNr > 0) && |
| IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { |
| xmlNodePtr target; |
| |
| target = obj2->nodesetval->nodeTab[0]; |
| if ((target->type == XML_ATTRIBUTE_NODE) || |
| (target->type == XML_PI_NODE)) { |
| target = ((xmlAttrPtr) target)->parent; |
| } |
| base = xmlNodeGetBase(target->doc, target); |
| } else { |
| xsltTransformContextPtr tctxt; |
| |
| tctxt = xsltXPathGetTransformContext(ctxt); |
| if ((tctxt != NULL) && (tctxt->inst != NULL)) { |
| base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); |
| } else if ((tctxt != NULL) && (tctxt->style != NULL) && |
| (tctxt->style->doc != NULL)) { |
| base = xmlNodeGetBase(tctxt->style->doc, |
| (xmlNodePtr) tctxt->style->doc); |
| } |
| } |
| URI = xmlBuildURI(obj->stringval, base); |
| if (base != NULL) |
| xmlFree(base); |
| if (URI == NULL) { |
| valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
| } else { |
| xsltDocumentFunctionLoadDocument( ctxt, URI ); |
| xmlFree(URI); |
| } |
| } |
| xmlXPathFreeObject(obj); |
| if (obj2 != NULL) |
| xmlXPathFreeObject(obj2); |
| } |
| |
| /** |
| * xsltKeyFunction: |
| * @ctxt: the XPath Parser context |
| * @nargs: the number of arguments |
| * |
| * Implement the key() XSLT function |
| * node-set key(string, object) |
| */ |
| void |
| xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
| xmlXPathObjectPtr obj1, obj2; |
| |
| if (nargs != 2) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "key() : expects two arguments\n"); |
| ctxt->error = XPATH_INVALID_ARITY; |
| return; |
| } |
| |
| /* |
| * Get the key's value. |
| */ |
| obj2 = valuePop(ctxt); |
| xmlXPathStringFunction(ctxt, 1); |
| if ((obj2 == NULL) || |
| (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "key() : invalid arg expecting a string\n"); |
| ctxt->error = XPATH_INVALID_TYPE; |
| xmlXPathFreeObject(obj2); |
| |
| return; |
| } |
| /* |
| * Get the key's name. |
| */ |
| obj1 = valuePop(ctxt); |
| |
| if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { |
| int i; |
| xmlXPathObjectPtr newobj, ret; |
| |
| ret = xmlXPathNewNodeSet(NULL); |
| |
| if (obj2->nodesetval != NULL) { |
| for (i = 0; i < obj2->nodesetval->nodeNr; i++) { |
| valuePush(ctxt, xmlXPathObjectCopy(obj1)); |
| valuePush(ctxt, |
| xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i])); |
| xmlXPathStringFunction(ctxt, 1); |
| xsltKeyFunction(ctxt, 2); |
| newobj = valuePop(ctxt); |
| ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, |
| newobj->nodesetval); |
| xmlXPathFreeObject(newobj); |
| } |
| } |
| valuePush(ctxt, ret); |
| } else { |
| xmlNodeSetPtr nodelist = NULL; |
| xmlChar *key = NULL, *value; |
| const xmlChar *keyURI; |
| xsltTransformContextPtr tctxt; |
| xmlChar *qname, *prefix; |
| xmlXPathContextPtr xpctxt = ctxt->context; |
| xmlNodePtr tmpNode = NULL; |
| xsltDocumentPtr oldDocInfo; |
| |
| tctxt = xsltXPathGetTransformContext(ctxt); |
| |
| oldDocInfo = tctxt->document; |
| |
| if (xpctxt->node == NULL) { |
| xsltTransformError(tctxt, NULL, tctxt->inst, |
| "Internal error in xsltKeyFunction(): " |
| "The context node is not set on the XPath context.\n"); |
| tctxt->state = XSLT_STATE_STOPPED; |
| goto error; |
| } |
| /* |
| * Get the associated namespace URI if qualified name |
| */ |
| qname = obj1->stringval; |
| key = xmlSplitQName2(qname, &prefix); |
| if (key == NULL) { |
| key = xmlStrdup(obj1->stringval); |
| keyURI = NULL; |
| if (prefix != NULL) |
| xmlFree(prefix); |
| } else { |
| if (prefix != NULL) { |
| keyURI = xmlXPathNsLookup(xpctxt, prefix); |
| if (keyURI == NULL) { |
| xsltTransformError(tctxt, NULL, tctxt->inst, |
| "key() : prefix %s is not bound\n", prefix); |
| /* |
| * TODO: Shouldn't we stop here? |
| */ |
| } |
| xmlFree(prefix); |
| } else { |
| keyURI = NULL; |
| } |
| } |
| |
| /* |
| * Force conversion of first arg to string |
| */ |
| valuePush(ctxt, obj2); |
| xmlXPathStringFunction(ctxt, 1); |
| if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
| xsltTransformError(tctxt, NULL, tctxt->inst, |
| "key() : invalid arg expecting a string\n"); |
| ctxt->error = XPATH_INVALID_TYPE; |
| goto error; |
| } |
| obj2 = valuePop(ctxt); |
| value = obj2->stringval; |
| |
| /* |
| * We need to ensure that ctxt->document is available for |
| * xsltGetKey(). |
| * First find the relevant doc, which is the context node's |
| * owner doc; using context->doc is not safe, since |
| * the doc could have been acquired via the document() function, |
| * or the doc might be a Result Tree Fragment. |
| * FUTURE INFO: In XSLT 2.0 the key() function takes an additional |
| * argument indicating the doc to use. |
| */ |
| if (xpctxt->node->type == XML_NAMESPACE_DECL) { |
| /* |
| * REVISIT: This is a libxml hack! Check xpath.c for details. |
| * The XPath module sets the owner element of a ns-node on |
| * the ns->next field. |
| */ |
| if ((((xmlNsPtr) xpctxt->node)->next != NULL) && |
| (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE)) |
| { |
| tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next; |
| } |
| } else |
| tmpNode = xpctxt->node; |
| |
| if ((tmpNode == NULL) || (tmpNode->doc == NULL)) { |
| xsltTransformError(tctxt, NULL, tctxt->inst, |
| "Internal error in xsltKeyFunction(): " |
| "Couldn't get the doc of the XPath context node.\n"); |
| goto error; |
| } |
| |
| if ((tctxt->document == NULL) || |
| (tctxt->document->doc != tmpNode->doc)) |
| { |
| if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) { |
| /* |
| * This is a Result Tree Fragment. |
| */ |
| if (tmpNode->doc->_private == NULL) { |
| tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc); |
| if (tmpNode->doc->_private == NULL) |
| goto error; |
| } |
| tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private; |
| } else { |
| /* |
| * May be the initial source doc or a doc acquired via the |
| * document() function. |
| */ |
| tctxt->document = xsltFindDocument(tctxt, tmpNode->doc); |
| } |
| if (tctxt->document == NULL) { |
| xsltTransformError(tctxt, NULL, tctxt->inst, |
| "Internal error in xsltKeyFunction(): " |
| "Could not get the document info of a context doc.\n"); |
| tctxt->state = XSLT_STATE_STOPPED; |
| goto error; |
| } |
| } |
| /* |
| * Get/compute the key value. |
| */ |
| nodelist = xsltGetKey(tctxt, key, keyURI, value); |
| |
| error: |
| tctxt->document = oldDocInfo; |
| valuePush(ctxt, xmlXPathWrapNodeSet( |
| xmlXPathNodeSetMerge(NULL, nodelist))); |
| if (key != NULL) |
| xmlFree(key); |
| } |
| |
| if (obj1 != NULL) |
| xmlXPathFreeObject(obj1); |
| if (obj2 != NULL) |
| xmlXPathFreeObject(obj2); |
| } |
| |
| /** |
| * xsltUnparsedEntityURIFunction: |
| * @ctxt: the XPath Parser context |
| * @nargs: the number of arguments |
| * |
| * Implement the unparsed-entity-uri() XSLT function |
| * string unparsed-entity-uri(string) |
| */ |
| void |
| xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
| xmlXPathObjectPtr obj; |
| xmlChar *str; |
| |
| if ((nargs != 1) || (ctxt->value == NULL)) { |
| xsltGenericError(xsltGenericErrorContext, |
| "unparsed-entity-uri() : expects one string arg\n"); |
| ctxt->error = XPATH_INVALID_ARITY; |
| return; |
| } |
| obj = valuePop(ctxt); |
| if (obj->type != XPATH_STRING) { |
| obj = xmlXPathConvertString(obj); |
| } |
| |
| str = obj->stringval; |
| if (str == NULL) { |
| valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
| } else { |
| xmlEntityPtr entity; |
| |
| entity = xmlGetDocEntity(ctxt->context->doc, str); |
| if (entity == NULL) { |
| valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
| } else { |
| if (entity->URI != NULL) |
| valuePush(ctxt, xmlXPathNewString(entity->URI)); |
| else |
| valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
| } |
| } |
| xmlXPathFreeObject(obj); |
| } |
| |
| /** |
| * xsltFormatNumberFunction: |
| * @ctxt: the XPath Parser context |
| * @nargs: the number of arguments |
| * |
| * Implement the format-number() XSLT function |
| * string format-number(number, string, string?) |
| */ |
| void |
| xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) |
| { |
| xmlXPathObjectPtr numberObj = NULL; |
| xmlXPathObjectPtr formatObj = NULL; |
| xmlXPathObjectPtr decimalObj = NULL; |
| xsltStylesheetPtr sheet; |
| xsltDecimalFormatPtr formatValues; |
| xmlChar *result; |
| xsltTransformContextPtr tctxt; |
| |
| tctxt = xsltXPathGetTransformContext(ctxt); |
| if (tctxt == NULL) |
| return; |
| sheet = tctxt->style; |
| if (sheet == NULL) |
| return; |
| formatValues = sheet->decimalFormat; |
| |
| switch (nargs) { |
| case 3: |
| CAST_TO_STRING; |
| decimalObj = valuePop(ctxt); |
| formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval); |
| if (formatValues == NULL) { |
| xsltTransformError(tctxt, NULL, NULL, |
| "format-number() : undeclared decimal format '%s'\n", |
| decimalObj->stringval); |
| } |
| /* Intentional fall-through */ |
| case 2: |
| CAST_TO_STRING; |
| formatObj = valuePop(ctxt); |
| CAST_TO_NUMBER; |
| numberObj = valuePop(ctxt); |
| break; |
| default: |
| XP_ERROR(XPATH_INVALID_ARITY); |
| } |
| |
| if (formatValues != NULL) { |
| if (xsltFormatNumberConversion(formatValues, |
| formatObj->stringval, |
| numberObj->floatval, |
| &result) == XPATH_EXPRESSION_OK) { |
| valuePush(ctxt, xmlXPathNewString(result)); |
| xmlFree(result); |
| } |
| } |
| |
| xmlXPathFreeObject(numberObj); |
| xmlXPathFreeObject(formatObj); |
| xmlXPathFreeObject(decimalObj); |
| } |
| |
| /** |
| * xsltGenerateIdFunction: |
| * @ctxt: the XPath Parser context |
| * @nargs: the number of arguments |
| * |
| * Implement the generate-id() XSLT function |
| * string generate-id(node-set?) |
| */ |
| void |
| xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
| xmlNodePtr cur = NULL; |
| unsigned long val; |
| xmlChar str[20]; |
| |
| if (nargs == 0) { |
| cur = ctxt->context->node; |
| } else if (nargs == 1) { |
| xmlXPathObjectPtr obj; |
| xmlNodeSetPtr nodelist; |
| int i, ret; |
| |
| if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) { |
| ctxt->error = XPATH_INVALID_TYPE; |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "generate-id() : invalid arg expecting a node-set\n"); |
| return; |
| } |
| obj = valuePop(ctxt); |
| nodelist = obj->nodesetval; |
| if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) { |
| xmlXPathFreeObject(obj); |
| valuePush(ctxt, xmlXPathNewCString("")); |
| return; |
| } |
| cur = nodelist->nodeTab[0]; |
| for (i = 1;i < nodelist->nodeNr;i++) { |
| ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]); |
| if (ret == -1) |
| cur = nodelist->nodeTab[i]; |
| } |
| xmlXPathFreeObject(obj); |
| } else { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "generate-id() : invalid number of args %d\n", nargs); |
| ctxt->error = XPATH_INVALID_ARITY; |
| return; |
| } |
| /* |
| * Okay this is ugly but should work, use the NodePtr address |
| * to forge the ID |
| */ |
| val = (unsigned long)((char *)cur - (char *)0); |
| val /= sizeof(xmlNode); |
| sprintf((char *)str, "id%ld", val); |
| valuePush(ctxt, xmlXPathNewString(str)); |
| } |
| |
| /** |
| * xsltSystemPropertyFunction: |
| * @ctxt: the XPath Parser context |
| * @nargs: the number of arguments |
| * |
| * Implement the system-property() XSLT function |
| * object system-property(string) |
| */ |
| void |
| xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
| xmlXPathObjectPtr obj; |
| xmlChar *prefix, *name; |
| const xmlChar *nsURI = NULL; |
| |
| if (nargs != 1) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "system-property() : expects one string arg\n"); |
| ctxt->error = XPATH_INVALID_ARITY; |
| return; |
| } |
| if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "system-property() : invalid arg expecting a string\n"); |
| ctxt->error = XPATH_INVALID_TYPE; |
| return; |
| } |
| obj = valuePop(ctxt); |
| if (obj->stringval == NULL) { |
| valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
| } else { |
| name = xmlSplitQName2(obj->stringval, &prefix); |
| if (name == NULL) { |
| name = xmlStrdup(obj->stringval); |
| } else { |
| nsURI = xmlXPathNsLookup(ctxt->context, prefix); |
| if (nsURI == NULL) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "system-property() : prefix %s is not bound\n", prefix); |
| } |
| } |
| |
| if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) { |
| #ifdef DOCBOOK_XSL_HACK |
| if (xmlStrEqual(name, (const xmlChar *)"vendor")) { |
| xsltStylesheetPtr sheet; |
| xsltTransformContextPtr tctxt; |
| |
| tctxt = xsltXPathGetTransformContext(ctxt); |
| if ((tctxt != NULL) && (tctxt->inst != NULL) && |
| (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) && |
| (tctxt->inst->parent != NULL) && |
| (xmlStrEqual(tctxt->inst->parent->name, |
| BAD_CAST "template"))) |
| sheet = tctxt->style; |
| else |
| sheet = NULL; |
| if ((sheet != NULL) && (sheet->doc != NULL) && |
| (sheet->doc->URL != NULL) && |
| (xmlStrstr(sheet->doc->URL, |
| (const xmlChar *)"chunk") != NULL)) { |
| valuePush(ctxt, xmlXPathNewString( |
| (const xmlChar *)"libxslt (SAXON 6.2 compatible)")); |
| |
| } else { |
| valuePush(ctxt, xmlXPathNewString( |
| (const xmlChar *)XSLT_DEFAULT_VENDOR)); |
| } |
| } else |
| #else |
| if (xmlStrEqual(name, (const xmlChar *)"vendor")) { |
| valuePush(ctxt, xmlXPathNewString( |
| (const xmlChar *)XSLT_DEFAULT_VENDOR)); |
| } else |
| #endif |
| if (xmlStrEqual(name, (const xmlChar *)"version")) { |
| valuePush(ctxt, xmlXPathNewString( |
| (const xmlChar *)XSLT_DEFAULT_VERSION)); |
| } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) { |
| valuePush(ctxt, xmlXPathNewString( |
| (const xmlChar *)XSLT_DEFAULT_URL)); |
| } else { |
| valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); |
| } |
| } |
| if (name != NULL) |
| xmlFree(name); |
| if (prefix != NULL) |
| xmlFree(prefix); |
| } |
| xmlXPathFreeObject(obj); |
| } |
| |
| /** |
| * xsltElementAvailableFunction: |
| * @ctxt: the XPath Parser context |
| * @nargs: the number of arguments |
| * |
| * Implement the element-available() XSLT function |
| * boolean element-available(string) |
| */ |
| void |
| xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
| xmlXPathObjectPtr obj; |
| xmlChar *prefix, *name; |
| const xmlChar *nsURI = NULL; |
| xsltTransformContextPtr tctxt; |
| |
| if (nargs != 1) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "element-available() : expects one string arg\n"); |
| ctxt->error = XPATH_INVALID_ARITY; |
| return; |
| } |
| xmlXPathStringFunction(ctxt, 1); |
| if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "element-available() : invalid arg expecting a string\n"); |
| ctxt->error = XPATH_INVALID_TYPE; |
| return; |
| } |
| obj = valuePop(ctxt); |
| tctxt = xsltXPathGetTransformContext(ctxt); |
| if (tctxt == NULL) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "element-available() : internal error tctxt == NULL\n"); |
| xmlXPathFreeObject(obj); |
| valuePush(ctxt, xmlXPathNewBoolean(0)); |
| return; |
| } |
| |
| |
| name = xmlSplitQName2(obj->stringval, &prefix); |
| if (name == NULL) { |
| xmlNsPtr ns; |
| |
| name = xmlStrdup(obj->stringval); |
| ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL); |
| if (ns != NULL) nsURI = xmlStrdup(ns->href); |
| } else { |
| nsURI = xmlXPathNsLookup(ctxt->context, prefix); |
| if (nsURI == NULL) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "element-available() : prefix %s is not bound\n", prefix); |
| } |
| } |
| |
| if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) { |
| valuePush(ctxt, xmlXPathNewBoolean(1)); |
| } else { |
| valuePush(ctxt, xmlXPathNewBoolean(0)); |
| } |
| |
| xmlXPathFreeObject(obj); |
| if (name != NULL) |
| xmlFree(name); |
| if (prefix != NULL) |
| xmlFree(prefix); |
| } |
| |
| /** |
| * xsltFunctionAvailableFunction: |
| * @ctxt: the XPath Parser context |
| * @nargs: the number of arguments |
| * |
| * Implement the function-available() XSLT function |
| * boolean function-available(string) |
| */ |
| void |
| xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
| xmlXPathObjectPtr obj; |
| xmlChar *prefix, *name; |
| const xmlChar *nsURI = NULL; |
| |
| if (nargs != 1) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "function-available() : expects one string arg\n"); |
| ctxt->error = XPATH_INVALID_ARITY; |
| return; |
| } |
| xmlXPathStringFunction(ctxt, 1); |
| if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "function-available() : invalid arg expecting a string\n"); |
| ctxt->error = XPATH_INVALID_TYPE; |
| return; |
| } |
| obj = valuePop(ctxt); |
| |
| name = xmlSplitQName2(obj->stringval, &prefix); |
| if (name == NULL) { |
| name = xmlStrdup(obj->stringval); |
| } else { |
| nsURI = xmlXPathNsLookup(ctxt->context, prefix); |
| if (nsURI == NULL) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "function-available() : prefix %s is not bound\n", prefix); |
| } |
| } |
| |
| if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) { |
| valuePush(ctxt, xmlXPathNewBoolean(1)); |
| } else { |
| valuePush(ctxt, xmlXPathNewBoolean(0)); |
| } |
| |
| xmlXPathFreeObject(obj); |
| if (name != NULL) |
| xmlFree(name); |
| if (prefix != NULL) |
| xmlFree(prefix); |
| } |
| |
| /** |
| * xsltCurrentFunction: |
| * @ctxt: the XPath Parser context |
| * @nargs: the number of arguments |
| * |
| * Implement the current() XSLT function |
| * node-set current() |
| */ |
| static void |
| xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){ |
| xsltTransformContextPtr tctxt; |
| |
| if (nargs != 0) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "current() : function uses no argument\n"); |
| ctxt->error = XPATH_INVALID_ARITY; |
| return; |
| } |
| tctxt = xsltXPathGetTransformContext(ctxt); |
| if (tctxt == NULL) { |
| xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, |
| "current() : internal error tctxt == NULL\n"); |
| valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
| } else { |
| valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */ |
| } |
| } |
| |
| /************************************************************************ |
| * * |
| * Registration of XSLT and libxslt functions * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltRegisterAllFunctions: |
| * @ctxt: the XPath context |
| * |
| * Registers all default XSLT functions in this context |
| */ |
| void |
| xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) |
| { |
| xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current", |
| xsltCurrentFunction); |
| xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document", |
| xsltDocumentFunction); |
| xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction); |
| xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri", |
| xsltUnparsedEntityURIFunction); |
| xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number", |
| xsltFormatNumberFunction); |
| xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id", |
| xsltGenerateIdFunction); |
| xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property", |
| xsltSystemPropertyFunction); |
| xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available", |
| xsltElementAvailableFunction); |
| xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available", |
| xsltFunctionAvailableFunction); |
| } |