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