Coverage Report

Created: 2023-11-27 06:38

/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
399k
       const xmlChar *name, const xmlChar *ns_uri) {
63
399k
    xmlXPathContextPtr ctxt = (xmlXPathContextPtr) vctxt;
64
399k
    xmlXPathFunction ret;
65
66
399k
    if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL))
67
130k
  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
269k
    XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri);
79
80
269k
    if (ret == NULL)
81
268k
  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
269k
    return(ret);
89
399k
}
90
91
92
/************************************************************************
93
 *                  *
94
 *      Module interfaces       *
95
 *                  *
96
 ************************************************************************/
97
98
static void
99
xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
100
520k
{
101
520k
    xsltTransformContextPtr tctxt;
102
520k
    xmlURIPtr uri;
103
520k
    xmlChar *fragment;
104
520k
    xsltDocumentPtr idoc; /* document info */
105
520k
    xmlDocPtr doc;
106
520k
    xmlXPathContextPtr xptrctxt = NULL;
107
520k
    xmlXPathObjectPtr resObj = NULL;
108
109
520k
    tctxt = xsltXPathGetTransformContext(ctxt);
110
520k
    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
520k
    uri = xmlParseURI((const char *) URI);
118
520k
    if (uri == NULL) {
119
1.68k
  xsltTransformError(tctxt, NULL, NULL,
120
1.68k
      "document() : failed to parse URI\n");
121
1.68k
  valuePush(ctxt, xmlXPathNewNodeSet(NULL));
122
1.68k
  return;
123
1.68k
    }
124
125
    /*
126
     * check for and remove fragment identifier
127
     */
128
518k
    fragment = (xmlChar *)uri->fragment;
129
518k
    if (fragment != NULL) {
130
61.2k
        xmlChar *newURI;
131
61.2k
  uri->fragment = NULL;
132
61.2k
  newURI = xmlSaveUri(uri);
133
61.2k
  idoc = xsltLoadDocument(tctxt, newURI);
134
61.2k
  xmlFree(newURI);
135
61.2k
    } else
136
457k
  idoc = xsltLoadDocument(tctxt, URI);
137
518k
    xmlFreeURI(uri);
138
139
518k
    if (idoc == NULL) {
140
518k
  if ((URI == NULL) ||
141
518k
      (URI[0] == '#') ||
142
518k
      ((tctxt->style->doc != NULL) &&
143
477k
      (xmlStrEqual(tctxt->style->doc->URL, URI))))
144
396k
  {
145
      /*
146
      * This selects the stylesheet's doc itself.
147
      */
148
396k
      doc = tctxt->style->doc;
149
396k
  } else {
150
122k
      valuePush(ctxt, xmlXPathNewNodeSet(NULL));
151
152
122k
      if (fragment != NULL)
153
20.7k
    xmlFree(fragment);
154
155
122k
      return;
156
122k
  }
157
518k
    } else
158
0
  doc = idoc->doc;
159
160
396k
    if (fragment == NULL) {
161
355k
  valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc));
162
355k
  return;
163
355k
    }
164
165
    /* use XPointer of HTML location for fragment ID */
166
40.5k
#ifdef LIBXML_XPTR_ENABLED
167
40.5k
    xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
168
40.5k
    if (xptrctxt == NULL) {
169
14
  xsltTransformError(tctxt, NULL, NULL,
170
14
      "document() : internal error xptrctxt == NULL\n");
171
14
  goto out_fragment;
172
14
    }
173
174
40.5k
#if LIBXML_VERSION >= 20911 || \
175
40.5k
    defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
176
40.5k
    xptrctxt->opLimit = ctxt->context->opLimit;
177
40.5k
    xptrctxt->opCount = ctxt->context->opCount;
178
40.5k
    xptrctxt->depth = ctxt->context->depth;
179
180
40.5k
    resObj = xmlXPtrEval(fragment, xptrctxt);
181
182
40.5k
    ctxt->context->opCount = xptrctxt->opCount;
183
#else
184
    resObj = xmlXPtrEval(fragment, xptrctxt);
185
#endif
186
187
40.5k
    xmlXPathFreeContext(xptrctxt);
188
40.5k
#endif /* LIBXML_XPTR_ENABLED */
189
190
40.5k
    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
40.5k
out_fragment:
199
40.5k
    if (resObj == NULL)
200
29.9k
        resObj = xmlXPathNewNodeSet(NULL);
201
40.5k
    valuePush(ctxt, resObj);
202
40.5k
    xmlFree(fragment);
203
40.5k
}
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
773k
{
216
773k
    xmlXPathObjectPtr obj, obj2 = NULL;
217
773k
    xmlChar *base = NULL, *URI;
218
219
220
773k
    if ((nargs < 1) || (nargs > 2)) {
221
15
        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
222
15
                         "document() : invalid number of args %d\n",
223
15
                         nargs);
224
15
        ctxt->error = XPATH_INVALID_ARITY;
225
15
        return;
226
15
    }
227
773k
    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
773k
    if (nargs == 2) {
235
763k
        if (ctxt->value->type != XPATH_NODESET) {
236
10
            xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
237
10
                             "document() : invalid arg expecting a nodeset\n");
238
10
            ctxt->error = XPATH_INVALID_TYPE;
239
10
            return;
240
10
        }
241
242
763k
        obj2 = valuePop(ctxt);
243
763k
    }
244
245
773k
    if ((ctxt->value != NULL) && (ctxt->value->type == XPATH_NODESET)) {
246
59.8k
        int i;
247
59.8k
        xmlXPathObjectPtr newobj, ret;
248
249
59.8k
        obj = valuePop(ctxt);
250
59.8k
        ret = xmlXPathNewNodeSet(NULL);
251
252
59.8k
        if ((obj != NULL) && (obj->nodesetval != NULL) && (ret != NULL)) {
253
767k
            for (i = 0; i < obj->nodesetval->nodeNr; i++) {
254
707k
                valuePush(ctxt,
255
707k
                          xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
256
707k
                xmlXPathStringFunction(ctxt, 1);
257
707k
                if (nargs == 2) {
258
601k
                    valuePush(ctxt, xmlXPathObjectCopy(obj2));
259
601k
                } else {
260
106k
                    valuePush(ctxt,
261
106k
                              xmlXPathNewNodeSet(obj->nodesetval->
262
106k
                                                 nodeTab[i]));
263
106k
                }
264
707k
                if (ctxt->error)
265
160
                    break;
266
707k
                xsltDocumentFunction(ctxt, 2);
267
707k
                newobj = valuePop(ctxt);
268
707k
                if (newobj != NULL) {
269
707k
                    ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
270
707k
                                                           newobj->nodesetval);
271
707k
                    xmlXPathFreeObject(newobj);
272
707k
                }
273
707k
            }
274
59.2k
        }
275
276
59.8k
        if (obj != NULL)
277
59.8k
            xmlXPathFreeObject(obj);
278
59.8k
        if (obj2 != NULL)
279
54.4k
            xmlXPathFreeObject(obj2);
280
59.8k
        valuePush(ctxt, ret);
281
59.8k
        return;
282
59.8k
    }
283
    /*
284
     * Make sure it's converted to a string
285
     */
286
713k
    xmlXPathStringFunction(ctxt, 1);
287
713k
    if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
288
100
        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
289
100
                         "document() : invalid arg expecting a string\n");
290
100
        ctxt->error = XPATH_INVALID_TYPE;
291
100
        if (obj2 != NULL)
292
7
            xmlXPathFreeObject(obj2);
293
100
        return;
294
100
    }
295
713k
    obj = valuePop(ctxt);
296
713k
    if (obj->stringval == NULL) {
297
0
        valuePush(ctxt, xmlXPathNewNodeSet(NULL));
298
713k
    } else {
299
713k
        xsltTransformContextPtr tctxt;
300
713k
        tctxt = xsltXPathGetTransformContext(ctxt);
301
713k
        if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
302
713k
            (obj2->nodesetval->nodeNr > 0) &&
303
713k
            IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) {
304
106k
            xmlNodePtr target;
305
306
106k
            target = obj2->nodesetval->nodeTab[0];
307
106k
            if ((target->type == XML_ATTRIBUTE_NODE) ||
308
106k
          (target->type == XML_PI_NODE)) {
309
1.99k
                target = ((xmlAttrPtr) target)->parent;
310
1.99k
            }
311
106k
            base = xmlNodeGetBase(target->doc, target);
312
607k
        } else {
313
607k
            if ((tctxt != NULL) && (tctxt->inst != NULL)) {
314
607k
                base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
315
607k
            } 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
607k
        }
321
713k
        URI = xmlBuildURI(obj->stringval, base);
322
713k
        if (base != NULL)
323
649k
            xmlFree(base);
324
713k
        if (URI == NULL) {
325
193k
            if ((tctxt != NULL) && (tctxt->style != NULL) &&
326
193k
                (tctxt->style->doc != NULL) &&
327
193k
                (xmlStrEqual(URI, tctxt->style->doc->URL))) {
328
                /* This selects the stylesheet's doc itself. */
329
0
                valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc));
330
193k
            } else {
331
193k
                valuePush(ctxt, xmlXPathNewNodeSet(NULL));
332
193k
            }
333
520k
        } else {
334
520k
      xsltDocumentFunctionLoadDocument( ctxt, URI );
335
520k
      xmlFree(URI);
336
520k
  }
337
713k
    }
338
713k
    xmlXPathFreeObject(obj);
339
713k
    if (obj2 != NULL)
340
708k
        xmlXPathFreeObject(obj2);
341
713k
}
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
17.1k
xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
353
17.1k
    xmlXPathObjectPtr obj1, obj2;
354
355
17.1k
    if (nargs != 2) {
356
186
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
357
186
    "key() : expects two arguments\n");
358
186
  ctxt->error = XPATH_INVALID_ARITY;
359
186
  return;
360
186
    }
361
362
    /*
363
    * Get the key's value.
364
    */
365
16.9k
    obj2 = valuePop(ctxt);
366
16.9k
    xmlXPathStringFunction(ctxt, 1);
367
16.9k
    if ((obj2 == NULL) ||
368
16.9k
  (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
369
464
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
370
464
      "key() : invalid arg expecting a string\n");
371
464
  ctxt->error = XPATH_INVALID_TYPE;
372
464
  xmlXPathFreeObject(obj2);
373
374
464
  return;
375
464
    }
376
    /*
377
    * Get the key's name.
378
    */
379
16.5k
    obj1 = valuePop(ctxt);
380
381
16.5k
    if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) {
382
843
  int i;
383
843
  xmlXPathObjectPtr newobj, ret;
384
385
843
  ret = xmlXPathNewNodeSet(NULL);
386
843
        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
841
  if (obj2->nodesetval != NULL) {
394
13.2k
      for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
395
12.5k
    valuePush(ctxt, xmlXPathObjectCopy(obj1));
396
12.5k
    valuePush(ctxt,
397
12.5k
        xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i]));
398
12.5k
    xmlXPathStringFunction(ctxt, 1);
399
12.5k
    xsltKeyFunction(ctxt, 2);
400
12.5k
    newobj = valuePop(ctxt);
401
12.5k
                if (newobj != NULL)
402
12.4k
        ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
403
12.4k
                       newobj->nodesetval);
404
12.5k
    xmlXPathFreeObject(newobj);
405
12.5k
      }
406
657
  }
407
841
  valuePush(ctxt, ret);
408
15.6k
    } else {
409
15.6k
  xmlNodeSetPtr nodelist = NULL;
410
15.6k
  xmlChar *key = NULL, *value;
411
15.6k
  const xmlChar *keyURI;
412
15.6k
  xsltTransformContextPtr tctxt;
413
15.6k
  xmlChar *qname, *prefix;
414
15.6k
  xmlXPathContextPtr xpctxt = ctxt->context;
415
15.6k
  xmlNodePtr tmpNode = NULL;
416
15.6k
  xsltDocumentPtr oldDocInfo;
417
418
15.6k
  tctxt = xsltXPathGetTransformContext(ctxt);
419
420
15.6k
  oldDocInfo = tctxt->document;
421
422
15.6k
  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
15.6k
  qname = obj1->stringval;
433
15.6k
  key = xmlSplitQName2(qname, &prefix);
434
15.6k
  if (key == NULL) {
435
15.2k
      key = xmlStrdup(obj1->stringval);
436
15.2k
      keyURI = NULL;
437
15.2k
      if (prefix != NULL)
438
0
    xmlFree(prefix);
439
15.2k
  } else {
440
428
      if (prefix != NULL) {
441
428
    keyURI = xmlXPathNsLookup(xpctxt, prefix);
442
428
    if (keyURI == NULL) {
443
334
        xsltTransformError(tctxt, NULL, tctxt->inst,
444
334
      "key() : prefix %s is not bound\n", prefix);
445
        /*
446
        * TODO: Shouldn't we stop here?
447
        */
448
334
    }
449
428
    xmlFree(prefix);
450
428
      } else {
451
0
    keyURI = NULL;
452
0
      }
453
428
  }
454
455
  /*
456
   * Force conversion of first arg to string
457
   */
458
15.6k
  valuePush(ctxt, obj2);
459
15.6k
  xmlXPathStringFunction(ctxt, 1);
460
15.6k
  obj2 = valuePop(ctxt);
461
15.6k
  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
15.6k
  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
15.6k
  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
37
      if ((((xmlNsPtr) xpctxt->node)->next != NULL) &&
486
37
    (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE))
487
37
      {
488
37
    tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next;
489
37
      }
490
37
  } else
491
15.6k
      tmpNode = xpctxt->node;
492
493
15.6k
  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
15.6k
  if ((tctxt->document == NULL) ||
501
15.6k
      (tctxt->document->doc != tmpNode->doc))
502
13.9k
  {
503
13.9k
      if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) {
504
    /*
505
    * This is a Result Tree Fragment.
506
    */
507
13.9k
    if (tmpNode->doc->_private == NULL) {
508
1.65k
        tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc);
509
1.65k
        if (tmpNode->doc->_private == NULL)
510
4
      goto error;
511
1.65k
    }
512
13.9k
    tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private;
513
13.9k
      } 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
13.9k
      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
13.9k
  }
528
  /*
529
  * Get/compute the key value.
530
  */
531
15.6k
  nodelist = xsltGetKey(tctxt, key, keyURI, value);
532
533
15.6k
error:
534
15.6k
  tctxt->document = oldDocInfo;
535
15.6k
  valuePush(ctxt, xmlXPathWrapNodeSet(
536
15.6k
      xmlXPathNodeSetMerge(NULL, nodelist)));
537
15.6k
  if (key != NULL)
538
15.6k
      xmlFree(key);
539
15.6k
    }
540
541
16.5k
    if (obj1 != NULL)
542
16.5k
  xmlXPathFreeObject(obj1);
543
16.5k
    if (obj2 != NULL)
544
16.5k
  xmlXPathFreeObject(obj2);
545
16.5k
}
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
2.85k
xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
691
2.85k
    xsltTransformContextPtr tctxt;
692
2.85k
    xmlNodePtr cur = NULL;
693
2.85k
    xmlXPathObjectPtr obj = NULL;
694
2.85k
    char *str;
695
2.85k
    const xmlChar *nsPrefix = NULL;
696
2.85k
    void **psviPtr;
697
2.85k
    unsigned long id;
698
2.85k
    size_t size, nsPrefixSize;
699
700
2.85k
    tctxt = xsltXPathGetTransformContext(ctxt);
701
702
2.85k
    if (nargs == 0) {
703
1
  cur = ctxt->context->node;
704
2.85k
    } else if (nargs == 1) {
705
2.85k
  xmlNodeSetPtr nodelist;
706
2.85k
  int i, ret;
707
708
2.85k
  if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
709
1
      ctxt->error = XPATH_INVALID_TYPE;
710
1
      xsltTransformError(tctxt, NULL, NULL,
711
1
    "generate-id() : invalid arg expecting a node-set\n");
712
1
            goto out;
713
1
  }
714
2.85k
  obj = valuePop(ctxt);
715
2.85k
  nodelist = obj->nodesetval;
716
2.85k
  if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
717
535
      valuePush(ctxt, xmlXPathNewCString(""));
718
535
      goto out;
719
535
  }
720
2.31k
  cur = nodelist->nodeTab[0];
721
7.39k
  for (i = 1;i < nodelist->nodeNr;i++) {
722
5.07k
      ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
723
5.07k
      if (ret == -1)
724
0
          cur = nodelist->nodeTab[i];
725
5.07k
  }
726
2.31k
    } else {
727
0
  xsltTransformError(tctxt, NULL, NULL,
728
0
    "generate-id() : invalid number of args %d\n", nargs);
729
0
  ctxt->error = XPATH_INVALID_ARITY;
730
0
  goto out;
731
0
    }
732
733
2.31k
    size = 30; /* for "id%lu" */
734
735
2.31k
    if (cur->type == XML_NAMESPACE_DECL) {
736
519
        xmlNsPtr ns = (xmlNsPtr) cur;
737
738
519
        nsPrefix = ns->prefix;
739
519
        if (nsPrefix == NULL)
740
282
            nsPrefix = BAD_CAST "";
741
519
        nsPrefixSize = xmlStrlen(nsPrefix);
742
        /* For "ns" and hex-encoded string */
743
519
        size += nsPrefixSize * 2 + 2;
744
745
        /* Parent is stored in 'next'. */
746
519
        cur = (xmlNodePtr) ns->next;
747
519
    }
748
749
2.31k
    psviPtr = xsltGetPSVIPtr(cur);
750
2.31k
    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
2.31k
    if (xsltGetSourceNodeFlags(cur) & XSLT_SOURCE_NODE_HAS_ID) {
758
1.08k
        id = (unsigned long) (size_t) *psviPtr;
759
1.22k
    } else {
760
1.22k
        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
1.22k
        } 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
1.22k
        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
1.22k
        id = ++tctxt->currentId;
778
1.22k
        *psviPtr = (void *) (size_t) id;
779
1.22k
        xsltSetSourceNodeFlags(tctxt, cur, XSLT_SOURCE_NODE_HAS_ID);
780
1.22k
    }
781
782
2.31k
    str = xmlMalloc(size);
783
2.31k
    if (str == NULL) {
784
3
        xsltTransformError(tctxt, NULL, NULL,
785
3
                "generate-id(): out of memory\n");
786
3
        ctxt->error = XPATH_MEMORY_ERROR;
787
3
        goto out;
788
3
    }
789
2.31k
    if (nsPrefix == NULL) {
790
1.79k
        snprintf(str, size, "id%lu", id);
791
1.79k
    } else {
792
519
        size_t i, j;
793
794
519
        snprintf(str, size, "id%luns", id);
795
796
        /*
797
         * Only ASCII alphanumerics are allowed, so we hex-encode the prefix.
798
         */
799
519
        j = strlen(str);
800
1.07k
        for (i = 0; i < nsPrefixSize; i++) {
801
558
            int v;
802
803
558
            v = nsPrefix[i] >> 4;
804
558
            str[j++] = v < 10 ? '0' + v : 'A' + (v - 10);
805
558
            v = nsPrefix[i] & 15;
806
558
            str[j++] = v < 10 ? '0' + v : 'A' + (v - 10);
807
558
        }
808
519
        str[j] = '\0';
809
519
    }
810
2.31k
    valuePush(ctxt, xmlXPathWrapString(BAD_CAST str));
811
812
2.85k
out:
813
2.85k
    xmlXPathFreeObject(obj);
814
2.85k
}
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
360
xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
826
360
    xmlXPathObjectPtr obj;
827
360
    xmlChar *prefix, *name;
828
360
    const xmlChar *nsURI = NULL;
829
830
360
    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
359
    if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
837
0
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
838
0
      "system-property() : invalid arg expecting a string\n");
839
0
  ctxt->error = XPATH_INVALID_TYPE;
840
0
  return;
841
0
    }
842
359
    obj = valuePop(ctxt);
843
359
    if (obj->stringval == NULL) {
844
0
  valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
845
359
    } else {
846
359
  name = xmlSplitQName2(obj->stringval, &prefix);
847
359
  if (name == NULL) {
848
81
      name = xmlStrdup(obj->stringval);
849
278
  } else {
850
278
      nsURI = xmlXPathNsLookup(ctxt->context, prefix);
851
278
      if (nsURI == NULL) {
852
70
    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
853
70
        "system-property() : prefix %s is not bound\n", prefix);
854
70
      }
855
278
  }
856
857
359
  if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) {
858
208
#ifdef DOCBOOK_XSL_HACK
859
208
      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
208
      if (xmlStrEqual(name, (const xmlChar *)"version")) {
891
117
    valuePush(ctxt, xmlXPathNewString(
892
117
        (const xmlChar *)XSLT_DEFAULT_VERSION));
893
117
      } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) {
894
0
    valuePush(ctxt, xmlXPathNewString(
895
0
        (const xmlChar *)XSLT_DEFAULT_URL));
896
91
      } else {
897
91
    valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
898
91
      }
899
208
  } else {
900
151
      valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
901
151
        }
902
359
  if (name != NULL)
903
358
      xmlFree(name);
904
359
  if (prefix != NULL)
905
278
      xmlFree(prefix);
906
359
    }
907
359
    xmlXPathFreeObject(obj);
908
359
}
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
387
xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
920
387
    xmlXPathObjectPtr obj;
921
387
    xmlChar *prefix, *name;
922
387
    const xmlChar *nsURI = NULL;
923
387
    xsltTransformContextPtr tctxt;
924
925
387
    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
387
    xmlXPathStringFunction(ctxt, 1);
932
387
    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
387
    obj = valuePop(ctxt);
939
387
    tctxt = xsltXPathGetTransformContext(ctxt);
940
387
    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
387
    name = xmlSplitQName2(obj->stringval, &prefix);
950
387
    if (name == NULL) {
951
346
  xmlNsPtr ns;
952
953
346
  name = xmlStrdup(obj->stringval);
954
346
  ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL);
955
346
  if (ns != NULL) nsURI = ns->href;
956
346
    } else {
957
41
  nsURI = xmlXPathNsLookup(ctxt->context, prefix);
958
41
  if (nsURI == NULL) {
959
13
      xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
960
13
    "element-available() : prefix %s is not bound\n", prefix);
961
13
  }
962
41
    }
963
964
387
    if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) {
965
2
  valuePush(ctxt, xmlXPathNewBoolean(1));
966
385
    } else {
967
385
  valuePush(ctxt, xmlXPathNewBoolean(0));
968
385
    }
969
970
387
    xmlXPathFreeObject(obj);
971
387
    if (name != NULL)
972
386
  xmlFree(name);
973
387
    if (prefix != NULL)
974
41
  xmlFree(prefix);
975
387
}
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.66k
xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
987
1.66k
    xmlXPathObjectPtr obj;
988
1.66k
    xmlChar *prefix, *name;
989
1.66k
    const xmlChar *nsURI = NULL;
990
991
1.66k
    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.66k
    xmlXPathStringFunction(ctxt, 1);
998
1.66k
    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.66k
    obj = valuePop(ctxt);
1005
1006
1.66k
    name = xmlSplitQName2(obj->stringval, &prefix);
1007
1.66k
    if (name == NULL) {
1008
150
  name = xmlStrdup(obj->stringval);
1009
1.51k
    } else {
1010
1.51k
  nsURI = xmlXPathNsLookup(ctxt->context, prefix);
1011
1.51k
  if (nsURI == NULL) {
1012
1.39k
      xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
1013
1.39k
    "function-available() : prefix %s is not bound\n", prefix);
1014
1.39k
  }
1015
1.51k
    }
1016
1017
1.66k
    if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
1018
12
  valuePush(ctxt, xmlXPathNewBoolean(1));
1019
1.65k
    } else {
1020
1.65k
  valuePush(ctxt, xmlXPathNewBoolean(0));
1021
1.65k
    }
1022
1023
1.66k
    xmlXPathFreeObject(obj);
1024
1.66k
    if (name != NULL)
1025
1.66k
  xmlFree(name);
1026
1.66k
    if (prefix != NULL)
1027
1.51k
  xmlFree(prefix);
1028
1.66k
}
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
26.9k
xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
1040
26.9k
    xsltTransformContextPtr tctxt;
1041
1042
26.9k
    if (nargs != 0) {
1043
89
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
1044
89
    "current() : function uses no argument\n");
1045
89
  ctxt->error = XPATH_INVALID_ARITY;
1046
89
  return;
1047
89
    }
1048
26.8k
    tctxt = xsltXPathGetTransformContext(ctxt);
1049
26.8k
    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
26.8k
    } else {
1054
26.8k
  valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
1055
26.8k
    }
1056
26.8k
}
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
13.7k
{
1073
13.7k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current",
1074
13.7k
                         xsltCurrentFunction);
1075
13.7k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document",
1076
13.7k
                         xsltDocumentFunction);
1077
13.7k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction);
1078
13.7k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri",
1079
13.7k
                         xsltUnparsedEntityURIFunction);
1080
13.7k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number",
1081
13.7k
                         xsltFormatNumberFunction);
1082
13.7k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id",
1083
13.7k
                         xsltGenerateIdFunction);
1084
13.7k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property",
1085
13.7k
                         xsltSystemPropertyFunction);
1086
13.7k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available",
1087
13.7k
                         xsltElementAvailableFunction);
1088
13.7k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available",
1089
13.7k
                         xsltFunctionAvailableFunction);
1090
13.7k
}