Coverage Report

Created: 2023-12-14 14:10

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