Coverage Report

Created: 2024-01-21 07:00

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