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