Coverage Report

Created: 2023-11-19 06:27

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