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