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