Coverage Report

Created: 2023-10-09 10:09

/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
7.48M
       const xmlChar *name, const xmlChar *ns_uri) {
63
7.48M
    xmlXPathContextPtr ctxt = (xmlXPathContextPtr) vctxt;
64
7.48M
    xmlXPathFunction ret;
65
66
7.48M
    if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL))
67
7.11M
  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
369k
    XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri);
79
80
369k
    if (ret == NULL)
81
369k
  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
369k
    return(ret);
89
7.48M
}
90
91
92
/************************************************************************
93
 *                  *
94
 *      Module interfaces       *
95
 *                  *
96
 ************************************************************************/
97
98
static void
99
xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
100
11.3M
{
101
11.3M
    xsltTransformContextPtr tctxt;
102
11.3M
    xmlURIPtr uri;
103
11.3M
    xmlChar *fragment;
104
11.3M
    xsltDocumentPtr idoc; /* document info */
105
11.3M
    xmlDocPtr doc;
106
11.3M
    xmlXPathContextPtr xptrctxt = NULL;
107
11.3M
    xmlXPathObjectPtr resObj = NULL;
108
109
11.3M
    tctxt = xsltXPathGetTransformContext(ctxt);
110
11.3M
    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
11.3M
    uri = xmlParseURI((const char *) URI);
118
11.3M
    if (uri == NULL) {
119
219k
  xsltTransformError(tctxt, NULL, NULL,
120
219k
      "document() : failed to parse URI\n");
121
219k
  valuePush(ctxt, xmlXPathNewNodeSet(NULL));
122
219k
  return;
123
219k
    }
124
125
    /*
126
     * check for and remove fragment identifier
127
     */
128
11.0M
    fragment = (xmlChar *)uri->fragment;
129
11.0M
    if (fragment != NULL) {
130
4.06M
        xmlChar *newURI;
131
4.06M
  uri->fragment = NULL;
132
4.06M
  newURI = xmlSaveUri(uri);
133
4.06M
  idoc = xsltLoadDocument(tctxt, newURI);
134
4.06M
  xmlFree(newURI);
135
4.06M
    } else
136
7.03M
  idoc = xsltLoadDocument(tctxt, URI);
137
11.0M
    xmlFreeURI(uri);
138
139
11.0M
    if (idoc == NULL) {
140
11.0M
  if ((URI == NULL) ||
141
11.0M
      (URI[0] == '#') ||
142
11.0M
      ((tctxt->style->doc != NULL) &&
143
10.5M
      (xmlStrEqual(tctxt->style->doc->URL, URI))))
144
529k
  {
145
      /*
146
      * This selects the stylesheet's doc itself.
147
      */
148
529k
      doc = tctxt->style->doc;
149
10.5M
  } else {
150
10.5M
      valuePush(ctxt, xmlXPathNewNodeSet(NULL));
151
152
10.5M
      if (fragment != NULL)
153
3.53M
    xmlFree(fragment);
154
155
10.5M
      return;
156
10.5M
  }
157
11.0M
    } else
158
0
  doc = idoc->doc;
159
160
529k
    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
529k
#ifdef LIBXML_XPTR_ENABLED
167
529k
    xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
168
529k
    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
529k
#if LIBXML_VERSION >= 20911 || \
175
529k
    defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
176
529k
    xptrctxt->opLimit = ctxt->context->opLimit;
177
529k
    xptrctxt->opCount = ctxt->context->opCount;
178
529k
    xptrctxt->depth = ctxt->context->depth;
179
180
529k
    resObj = xmlXPtrEval(fragment, xptrctxt);
181
182
529k
    ctxt->context->opCount = xptrctxt->opCount;
183
#else
184
    resObj = xmlXPtrEval(fragment, xptrctxt);
185
#endif
186
187
529k
    xmlXPathFreeContext(xptrctxt);
188
529k
#endif /* LIBXML_XPTR_ENABLED */
189
190
529k
    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
529k
out_fragment:
199
529k
    if (resObj == NULL)
200
465k
        resObj = xmlXPathNewNodeSet(NULL);
201
529k
    valuePush(ctxt, resObj);
202
529k
    xmlFree(fragment);
203
529k
}
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
19.7M
{
216
19.7M
    xmlXPathObjectPtr obj, obj2 = NULL;
217
19.7M
    xmlChar *base = NULL, *URI;
218
219
220
19.7M
    if ((nargs < 1) || (nargs > 2)) {
221
10.6k
        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
222
10.6k
                         "document() : invalid number of args %d\n",
223
10.6k
                         nargs);
224
10.6k
        ctxt->error = XPATH_INVALID_ARITY;
225
10.6k
        return;
226
10.6k
    }
227
19.7M
    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
19.7M
    if (nargs == 2) {
235
8.32M
        if (ctxt->value->type != XPATH_NODESET) {
236
10.3k
            xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
237
10.3k
                             "document() : invalid arg expecting a nodeset\n");
238
10.3k
            ctxt->error = XPATH_INVALID_TYPE;
239
10.3k
            return;
240
10.3k
        }
241
242
8.31M
        obj2 = valuePop(ctxt);
243
8.31M
    }
244
245
19.7M
    if (ctxt->value->type == XPATH_NODESET) {
246
605k
        int i;
247
605k
        xmlXPathObjectPtr newobj, ret;
248
249
605k
        obj = valuePop(ctxt);
250
605k
        ret = xmlXPathNewNodeSet(NULL);
251
252
605k
        if ((obj != NULL) && obj->nodesetval) {
253
8.46M
            for (i = 0; i < obj->nodesetval->nodeNr; i++) {
254
8.08M
                valuePush(ctxt,
255
8.08M
                          xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
256
8.08M
                xmlXPathStringFunction(ctxt, 1);
257
8.08M
                if (nargs == 2) {
258
2.18M
                    valuePush(ctxt, xmlXPathObjectCopy(obj2));
259
5.90M
                } else {
260
5.90M
                    valuePush(ctxt,
261
5.90M
                              xmlXPathNewNodeSet(obj->nodesetval->
262
5.90M
                                                 nodeTab[i]));
263
5.90M
                }
264
8.08M
                xsltDocumentFunction(ctxt, 2);
265
8.08M
                newobj = valuePop(ctxt);
266
8.08M
                ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
267
8.08M
                                                       newobj->nodesetval);
268
8.08M
                xmlXPathFreeObject(newobj);
269
8.08M
            }
270
375k
        }
271
272
605k
        if (obj != NULL)
273
605k
            xmlXPathFreeObject(obj);
274
605k
        if (obj2 != NULL)
275
108k
            xmlXPathFreeObject(obj2);
276
605k
        valuePush(ctxt, ret);
277
605k
        return;
278
605k
    }
279
    /*
280
     * Make sure it's converted to a string
281
     */
282
19.1M
    xmlXPathStringFunction(ctxt, 1);
283
19.1M
    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
19.1M
    obj = valuePop(ctxt);
292
19.1M
    if (obj->stringval == NULL) {
293
0
        valuePush(ctxt, xmlXPathNewNodeSet(NULL));
294
19.1M
    } else {
295
19.1M
        xsltTransformContextPtr tctxt;
296
19.1M
        tctxt = xsltXPathGetTransformContext(ctxt);
297
19.1M
        if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
298
19.1M
            (obj2->nodesetval->nodeNr > 0) &&
299
19.1M
            IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) {
300
7.10M
            xmlNodePtr target;
301
302
7.10M
            target = obj2->nodesetval->nodeTab[0];
303
7.10M
            if ((target->type == XML_ATTRIBUTE_NODE) ||
304
7.10M
          (target->type == XML_PI_NODE)) {
305
768k
                target = ((xmlAttrPtr) target)->parent;
306
768k
            }
307
7.10M
            base = xmlNodeGetBase(target->doc, target);
308
12.0M
        } else {
309
12.0M
            if ((tctxt != NULL) && (tctxt->inst != NULL)) {
310
12.0M
                base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
311
12.0M
            } 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
12.0M
        }
317
19.1M
        URI = xmlBuildURI(obj->stringval, base);
318
19.1M
        if (base != NULL)
319
15.1M
            xmlFree(base);
320
19.1M
        if (URI == NULL) {
321
7.82M
            if ((tctxt != NULL) && (tctxt->style != NULL) &&
322
7.82M
                (tctxt->style->doc != NULL) &&
323
7.82M
                (xmlStrEqual(URI, tctxt->style->doc->URL))) {
324
                /* This selects the stylesheet's doc itself. */
325
0
                valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc));
326
7.82M
            } else {
327
7.82M
                valuePush(ctxt, xmlXPathNewNodeSet(NULL));
328
7.82M
            }
329
11.3M
        } else {
330
11.3M
      xsltDocumentFunctionLoadDocument( ctxt, URI );
331
11.3M
      xmlFree(URI);
332
11.3M
  }
333
19.1M
    }
334
19.1M
    xmlXPathFreeObject(obj);
335
19.1M
    if (obj2 != NULL)
336
8.20M
        xmlXPathFreeObject(obj2);
337
19.1M
}
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
372k
xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
349
372k
    xmlXPathObjectPtr obj1, obj2;
350
351
372k
    if (nargs != 2) {
352
1.50k
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
353
1.50k
    "key() : expects two arguments\n");
354
1.50k
  ctxt->error = XPATH_INVALID_ARITY;
355
1.50k
  return;
356
1.50k
    }
357
358
    /*
359
    * Get the key's value.
360
    */
361
371k
    obj2 = valuePop(ctxt);
362
371k
    xmlXPathStringFunction(ctxt, 1);
363
371k
    if ((obj2 == NULL) ||
364
371k
  (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
371k
    obj1 = valuePop(ctxt);
376
377
371k
    if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) {
378
68.3k
  int i;
379
68.3k
  xmlXPathObjectPtr newobj, ret;
380
381
68.3k
  ret = xmlXPathNewNodeSet(NULL);
382
383
68.3k
  if (obj2->nodesetval != NULL) {
384
350k
      for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
385
290k
    valuePush(ctxt, xmlXPathObjectCopy(obj1));
386
290k
    valuePush(ctxt,
387
290k
        xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i]));
388
290k
    xmlXPathStringFunction(ctxt, 1);
389
290k
    xsltKeyFunction(ctxt, 2);
390
290k
    newobj = valuePop(ctxt);
391
290k
    ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
392
290k
                   newobj->nodesetval);
393
290k
    xmlXPathFreeObject(newobj);
394
290k
      }
395
60.3k
  }
396
68.3k
  valuePush(ctxt, ret);
397
302k
    } else {
398
302k
  xmlNodeSetPtr nodelist = NULL;
399
302k
  xmlChar *key = NULL, *value;
400
302k
  const xmlChar *keyURI;
401
302k
  xsltTransformContextPtr tctxt;
402
302k
  xmlChar *qname, *prefix;
403
302k
  xmlXPathContextPtr xpctxt = ctxt->context;
404
302k
  xmlNodePtr tmpNode = NULL;
405
302k
  xsltDocumentPtr oldDocInfo;
406
407
302k
  tctxt = xsltXPathGetTransformContext(ctxt);
408
409
302k
  oldDocInfo = tctxt->document;
410
411
302k
  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
302k
  qname = obj1->stringval;
422
302k
  key = xmlSplitQName2(qname, &prefix);
423
302k
  if (key == NULL) {
424
297k
      key = xmlStrdup(obj1->stringval);
425
297k
      keyURI = NULL;
426
297k
      if (prefix != NULL)
427
0
    xmlFree(prefix);
428
297k
  } else {
429
5.16k
      if (prefix != NULL) {
430
5.16k
    keyURI = xmlXPathNsLookup(xpctxt, prefix);
431
5.16k
    if (keyURI == NULL) {
432
2.80k
        xsltTransformError(tctxt, NULL, tctxt->inst,
433
2.80k
      "key() : prefix %s is not bound\n", prefix);
434
        /*
435
        * TODO: Shouldn't we stop here?
436
        */
437
2.80k
    }
438
5.16k
    xmlFree(prefix);
439
5.16k
      } else {
440
0
    keyURI = NULL;
441
0
      }
442
5.16k
  }
443
444
  /*
445
   * Force conversion of first arg to string
446
   */
447
302k
  valuePush(ctxt, obj2);
448
302k
  xmlXPathStringFunction(ctxt, 1);
449
302k
  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
302k
  obj2 = valuePop(ctxt);
456
302k
  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
302k
  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.2k
      if ((((xmlNsPtr) xpctxt->node)->next != NULL) &&
475
11.2k
    (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE))
476
11.2k
      {
477
11.2k
    tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next;
478
11.2k
      }
479
11.2k
  } else
480
291k
      tmpNode = xpctxt->node;
481
482
302k
  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
302k
  if ((tctxt->document == NULL) ||
490
302k
      (tctxt->document->doc != tmpNode->doc))
491
5.05k
  {
492
5.05k
      if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) {
493
    /*
494
    * This is a Result Tree Fragment.
495
    */
496
5.05k
    if (tmpNode->doc->_private == NULL) {
497
70
        tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc);
498
70
        if (tmpNode->doc->_private == NULL)
499
0
      goto error;
500
70
    }
501
5.05k
    tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private;
502
5.05k
      } 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
5.05k
      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
5.05k
  }
517
  /*
518
  * Get/compute the key value.
519
  */
520
302k
  nodelist = xsltGetKey(tctxt, key, keyURI, value);
521
522
302k
error:
523
302k
  tctxt->document = oldDocInfo;
524
302k
  valuePush(ctxt, xmlXPathWrapNodeSet(
525
302k
      xmlXPathNodeSetMerge(NULL, nodelist)));
526
302k
  if (key != NULL)
527
302k
      xmlFree(key);
528
302k
    }
529
530
371k
    if (obj1 != NULL)
531
371k
  xmlXPathFreeObject(obj1);
532
371k
    if (obj2 != NULL)
533
371k
  xmlXPathFreeObject(obj2);
534
371k
}
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
102k
xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
546
102k
    xmlXPathObjectPtr obj;
547
102k
    xmlChar *str;
548
549
102k
    if ((nargs != 1) || (ctxt->value == NULL)) {
550
80
        xsltGenericError(xsltGenericErrorContext,
551
80
    "unparsed-entity-uri() : expects one string arg\n");
552
80
  ctxt->error = XPATH_INVALID_ARITY;
553
80
  return;
554
80
    }
555
102k
    obj = valuePop(ctxt);
556
102k
    if (obj->type != XPATH_STRING) {
557
32.2k
  obj = xmlXPathConvertString(obj);
558
32.2k
    }
559
560
102k
    str = obj->stringval;
561
102k
    if (str == NULL) {
562
0
  valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
563
102k
    } else {
564
102k
  xmlEntityPtr entity;
565
566
102k
  entity = xmlGetDocEntity(ctxt->context->doc, str);
567
102k
  if (entity == NULL) {
568
100k
      valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
569
100k
  } else {
570
2.05k
      if (entity->URI != NULL)
571
0
    valuePush(ctxt, xmlXPathNewString(entity->URI));
572
2.05k
      else
573
2.05k
    valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
574
2.05k
  }
575
102k
    }
576
102k
    xmlXPathFreeObject(obj);
577
102k
}
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
2.50M
{
590
2.50M
    xmlXPathObjectPtr numberObj = NULL;
591
2.50M
    xmlXPathObjectPtr formatObj = NULL;
592
2.50M
    xmlXPathObjectPtr decimalObj = NULL;
593
2.50M
    xsltStylesheetPtr sheet;
594
2.50M
    xsltDecimalFormatPtr formatValues = NULL;
595
2.50M
    xmlChar *result;
596
2.50M
    const xmlChar *ncname;
597
2.50M
    const xmlChar *prefix = NULL;
598
2.50M
    const xmlChar *nsUri = NULL;
599
2.50M
    xsltTransformContextPtr tctxt;
600
601
2.50M
    tctxt = xsltXPathGetTransformContext(ctxt);
602
2.50M
    if ((tctxt == NULL) || (tctxt->inst == NULL))
603
0
  return;
604
2.50M
    sheet = tctxt->style;
605
2.50M
    if (sheet == NULL)
606
0
  return;
607
2.50M
    formatValues = sheet->decimalFormat;
608
609
2.50M
    switch (nargs) {
610
605k
    case 3:
611
605k
        if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_STRING))
612
196
            xmlXPathStringFunction(ctxt, 1);
613
605k
  decimalObj = valuePop(ctxt);
614
605k
        ncname = xsltSplitQName(sheet->dict, decimalObj->stringval, &prefix);
615
605k
        if (prefix != NULL) {
616
604k
            xmlNsPtr ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, prefix);
617
604k
            if (ns == NULL) {
618
604k
                xsltTransformError(tctxt, NULL, NULL,
619
604k
                    "format-number : No namespace found for QName '%s:%s'\n",
620
604k
                    prefix, ncname);
621
604k
                sheet->errors++;
622
604k
                ncname = NULL;
623
604k
            }
624
72
            else {
625
72
                nsUri = ns->href;
626
72
            }
627
604k
        }
628
605k
        if (ncname != NULL) {
629
953
      formatValues = xsltDecimalFormatGetByQName(sheet, nsUri, ncname);
630
953
        }
631
605k
  if (formatValues == NULL) {
632
953
      xsltTransformError(tctxt, NULL, NULL,
633
953
        "format-number() : undeclared decimal format '%s'\n",
634
953
        decimalObj->stringval);
635
953
  }
636
  /* Intentional fall-through */
637
2.50M
    case 2:
638
2.50M
        if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_STRING))
639
320k
            xmlXPathStringFunction(ctxt, 1);
640
2.50M
  formatObj = valuePop(ctxt);
641
2.50M
        if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_NUMBER))
642
197k
            xmlXPathNumberFunction(ctxt, 1);
643
2.50M
  numberObj = valuePop(ctxt);
644
2.50M
  break;
645
223
    default:
646
223
  xmlXPathErr(ctxt, XPATH_INVALID_ARITY);
647
223
        return;
648
2.50M
    }
649
650
2.50M
    if (formatValues != NULL) {
651
2.50M
  if (xsltFormatNumberConversion(formatValues,
652
2.50M
               formatObj->stringval,
653
2.50M
               numberObj->floatval,
654
2.50M
               &result) == XPATH_EXPRESSION_OK) {
655
2.50M
      valuePush(ctxt, xmlXPathNewString(result));
656
2.50M
      xmlFree(result);
657
2.50M
  }
658
2.50M
    }
659
660
2.50M
    xmlXPathFreeObject(numberObj);
661
2.50M
    xmlXPathFreeObject(formatObj);
662
2.50M
    xmlXPathFreeObject(decimalObj);
663
2.50M
}
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
854k
xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
675
854k
    xsltTransformContextPtr tctxt;
676
854k
    xmlNodePtr cur = NULL;
677
854k
    xmlXPathObjectPtr obj = NULL;
678
854k
    char *str;
679
854k
    const xmlChar *nsPrefix = NULL;
680
854k
    void **psviPtr;
681
854k
    unsigned long id;
682
854k
    size_t size, nsPrefixSize;
683
684
854k
    tctxt = xsltXPathGetTransformContext(ctxt);
685
686
854k
    if (nargs == 0) {
687
266k
  cur = ctxt->context->node;
688
588k
    } else if (nargs == 1) {
689
587k
  xmlNodeSetPtr nodelist;
690
587k
  int i, ret;
691
692
587k
  if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
693
2.02k
      ctxt->error = XPATH_INVALID_TYPE;
694
2.02k
      xsltTransformError(tctxt, NULL, NULL,
695
2.02k
    "generate-id() : invalid arg expecting a node-set\n");
696
2.02k
            goto out;
697
2.02k
  }
698
585k
  obj = valuePop(ctxt);
699
585k
  nodelist = obj->nodesetval;
700
585k
  if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
701
188k
      valuePush(ctxt, xmlXPathNewCString(""));
702
188k
      goto out;
703
188k
  }
704
397k
  cur = nodelist->nodeTab[0];
705
3.19M
  for (i = 1;i < nodelist->nodeNr;i++) {
706
2.79M
      ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
707
2.79M
      if (ret == -1)
708
12.0k
          cur = nodelist->nodeTab[i];
709
2.79M
  }
710
397k
    } else {
711
385
  xsltTransformError(tctxt, NULL, NULL,
712
385
    "generate-id() : invalid number of args %d\n", nargs);
713
385
  ctxt->error = XPATH_INVALID_ARITY;
714
385
  goto out;
715
385
    }
716
717
663k
    size = 30; /* for "id%lu" */
718
719
663k
    if (cur->type == XML_NAMESPACE_DECL) {
720
17.5k
        xmlNsPtr ns = (xmlNsPtr) cur;
721
722
17.5k
        nsPrefix = ns->prefix;
723
17.5k
        if (nsPrefix == NULL)
724
0
            nsPrefix = BAD_CAST "";
725
17.5k
        nsPrefixSize = xmlStrlen(nsPrefix);
726
        /* For "ns" and hex-encoded string */
727
17.5k
        size += nsPrefixSize * 2 + 2;
728
729
        /* Parent is stored in 'next'. */
730
17.5k
        cur = (xmlNodePtr) ns->next;
731
17.5k
    }
732
733
663k
    psviPtr = xsltGetPSVIPtr(cur);
734
663k
    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
663k
    if (xsltGetSourceNodeFlags(cur) & XSLT_SOURCE_NODE_HAS_ID) {
742
651k
        id = (unsigned long) (size_t) *psviPtr;
743
651k
    } else {
744
12.1k
        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.1k
        } 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.1k
        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.1k
        id = ++tctxt->currentId;
762
12.1k
        *psviPtr = (void *) (size_t) id;
763
12.1k
        xsltSetSourceNodeFlags(tctxt, cur, XSLT_SOURCE_NODE_HAS_ID);
764
12.1k
    }
765
766
663k
    str = xmlMalloc(size);
767
663k
    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
663k
    if (nsPrefix == NULL) {
774
645k
        snprintf(str, size, "id%lu", id);
775
645k
    } else {
776
17.5k
        size_t i, j;
777
778
17.5k
        snprintf(str, size, "id%luns", id);
779
780
        /*
781
         * Only ASCII alphanumerics are allowed, so we hex-encode the prefix.
782
         */
783
17.5k
        j = strlen(str);
784
47.2k
        for (i = 0; i < nsPrefixSize; i++) {
785
29.7k
            int v;
786
787
29.7k
            v = nsPrefix[i] >> 4;
788
29.7k
            str[j++] = v < 10 ? '0' + v : 'A' + (v - 10);
789
29.7k
            v = nsPrefix[i] & 15;
790
29.7k
            str[j++] = v < 10 ? '0' + v : 'A' + (v - 10);
791
29.7k
        }
792
17.5k
        str[j] = '\0';
793
17.5k
    }
794
663k
    valuePush(ctxt, xmlXPathWrapString(BAD_CAST str));
795
796
854k
out:
797
854k
    xmlXPathFreeObject(obj);
798
854k
}
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
10.3k
xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
810
10.3k
    xmlXPathObjectPtr obj;
811
10.3k
    xmlChar *prefix, *name;
812
10.3k
    const xmlChar *nsURI = NULL;
813
814
10.3k
    if (nargs != 1) {
815
21
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
816
21
    "system-property() : expects one string arg\n");
817
21
  ctxt->error = XPATH_INVALID_ARITY;
818
21
  return;
819
21
    }
820
10.2k
    if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
821
26
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
822
26
      "system-property() : invalid arg expecting a string\n");
823
26
  ctxt->error = XPATH_INVALID_TYPE;
824
26
  return;
825
26
    }
826
10.2k
    obj = valuePop(ctxt);
827
10.2k
    if (obj->stringval == NULL) {
828
0
  valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
829
10.2k
    } else {
830
10.2k
  name = xmlSplitQName2(obj->stringval, &prefix);
831
10.2k
  if (name == NULL) {
832
3.88k
      name = xmlStrdup(obj->stringval);
833
6.37k
  } else {
834
6.37k
      nsURI = xmlXPathNsLookup(ctxt->context, prefix);
835
6.37k
      if (nsURI == NULL) {
836
3.98k
    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
837
3.98k
        "system-property() : prefix %s is not bound\n", prefix);
838
3.98k
      }
839
6.37k
  }
840
841
10.2k
  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
10.2k
  } else {
884
10.2k
      valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
885
10.2k
        }
886
10.2k
  if (name != NULL)
887
10.2k
      xmlFree(name);
888
10.2k
  if (prefix != NULL)
889
6.37k
      xmlFree(prefix);
890
10.2k
    }
891
10.2k
    xmlXPathFreeObject(obj);
892
10.2k
}
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
117k
xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
904
117k
    xmlXPathObjectPtr obj;
905
117k
    xmlChar *prefix, *name;
906
117k
    const xmlChar *nsURI = NULL;
907
117k
    xsltTransformContextPtr tctxt;
908
909
117k
    if (nargs != 1) {
910
20
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
911
20
    "element-available() : expects one string arg\n");
912
20
  ctxt->error = XPATH_INVALID_ARITY;
913
20
  return;
914
20
    }
915
117k
    xmlXPathStringFunction(ctxt, 1);
916
117k
    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
117k
    obj = valuePop(ctxt);
923
117k
    tctxt = xsltXPathGetTransformContext(ctxt);
924
117k
    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
117k
    name = xmlSplitQName2(obj->stringval, &prefix);
934
117k
    if (name == NULL) {
935
100k
  xmlNsPtr ns;
936
937
100k
  name = xmlStrdup(obj->stringval);
938
100k
  ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL);
939
100k
  if (ns != NULL) nsURI = ns->href;
940
100k
    } else {
941
17.6k
  nsURI = xmlXPathNsLookup(ctxt->context, prefix);
942
17.6k
  if (nsURI == NULL) {
943
9.60k
      xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
944
9.60k
    "element-available() : prefix %s is not bound\n", prefix);
945
9.60k
  }
946
17.6k
    }
947
948
117k
    if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) {
949
441
  valuePush(ctxt, xmlXPathNewBoolean(1));
950
117k
    } else {
951
117k
  valuePush(ctxt, xmlXPathNewBoolean(0));
952
117k
    }
953
954
117k
    xmlXPathFreeObject(obj);
955
117k
    if (name != NULL)
956
117k
  xmlFree(name);
957
117k
    if (prefix != NULL)
958
17.6k
  xmlFree(prefix);
959
117k
}
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
35.1k
xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
971
35.1k
    xmlXPathObjectPtr obj;
972
35.1k
    xmlChar *prefix, *name;
973
35.1k
    const xmlChar *nsURI = NULL;
974
975
35.1k
    if (nargs != 1) {
976
24
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
977
24
    "function-available() : expects one string arg\n");
978
24
  ctxt->error = XPATH_INVALID_ARITY;
979
24
  return;
980
24
    }
981
35.1k
    xmlXPathStringFunction(ctxt, 1);
982
35.1k
    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
35.1k
    obj = valuePop(ctxt);
989
990
35.1k
    name = xmlSplitQName2(obj->stringval, &prefix);
991
35.1k
    if (name == NULL) {
992
27.7k
  name = xmlStrdup(obj->stringval);
993
27.7k
    } else {
994
7.35k
  nsURI = xmlXPathNsLookup(ctxt->context, prefix);
995
7.35k
  if (nsURI == NULL) {
996
6.85k
      xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
997
6.85k
    "function-available() : prefix %s is not bound\n", prefix);
998
6.85k
  }
999
7.35k
    }
1000
1001
35.1k
    if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
1002
428
  valuePush(ctxt, xmlXPathNewBoolean(1));
1003
34.6k
    } else {
1004
34.6k
  valuePush(ctxt, xmlXPathNewBoolean(0));
1005
34.6k
    }
1006
1007
35.1k
    xmlXPathFreeObject(obj);
1008
35.1k
    if (name != NULL)
1009
35.1k
  xmlFree(name);
1010
35.1k
    if (prefix != NULL)
1011
7.35k
  xmlFree(prefix);
1012
35.1k
}
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
10.3k
xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
1024
10.3k
    xsltTransformContextPtr tctxt;
1025
1026
10.3k
    if (nargs != 0) {
1027
130
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
1028
130
    "current() : function uses no argument\n");
1029
130
  ctxt->error = XPATH_INVALID_ARITY;
1030
130
  return;
1031
130
    }
1032
10.2k
    tctxt = xsltXPathGetTransformContext(ctxt);
1033
10.2k
    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
10.2k
    } else {
1038
10.2k
  valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
1039
10.2k
    }
1040
10.2k
}
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.72k
{
1057
3.72k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current",
1058
3.72k
                         xsltCurrentFunction);
1059
3.72k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document",
1060
3.72k
                         xsltDocumentFunction);
1061
3.72k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction);
1062
3.72k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri",
1063
3.72k
                         xsltUnparsedEntityURIFunction);
1064
3.72k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number",
1065
3.72k
                         xsltFormatNumberFunction);
1066
3.72k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id",
1067
3.72k
                         xsltGenerateIdFunction);
1068
3.72k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property",
1069
3.72k
                         xsltSystemPropertyFunction);
1070
3.72k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available",
1071
3.72k
                         xsltElementAvailableFunction);
1072
3.72k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available",
1073
3.72k
                         xsltFunctionAvailableFunction);
1074
3.72k
}