Coverage Report

Created: 2023-03-26 10:05

/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
374k
       const xmlChar *name, const xmlChar *ns_uri) {
63
374k
    xmlXPathContextPtr ctxt = (xmlXPathContextPtr) vctxt;
64
374k
    xmlXPathFunction ret;
65
66
374k
    if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL))
67
247k
  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
127k
    XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri);
79
80
127k
    if (ret == NULL)
81
127k
  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
127k
    return(ret);
89
374k
}
90
91
92
/************************************************************************
93
 *                  *
94
 *      Module interfaces       *
95
 *                  *
96
 ************************************************************************/
97
98
static void
99
xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
100
9.00M
{
101
9.00M
    xsltTransformContextPtr tctxt;
102
9.00M
    xmlURIPtr uri;
103
9.00M
    xmlChar *fragment;
104
9.00M
    xsltDocumentPtr idoc; /* document info */
105
9.00M
    xmlDocPtr doc;
106
9.00M
    xmlXPathContextPtr xptrctxt = NULL;
107
9.00M
    xmlXPathObjectPtr resObj = NULL;
108
109
9.00M
    tctxt = xsltXPathGetTransformContext(ctxt);
110
9.00M
    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
9.00M
    uri = xmlParseURI((const char *) URI);
118
9.00M
    if (uri == NULL) {
119
6.28k
  xsltTransformError(tctxt, NULL, NULL,
120
6.28k
      "document() : failed to parse URI\n");
121
6.28k
  valuePush(ctxt, xmlXPathNewNodeSet(NULL));
122
6.28k
  return;
123
6.28k
    }
124
125
    /*
126
     * check for and remove fragment identifier
127
     */
128
8.99M
    fragment = (xmlChar *)uri->fragment;
129
8.99M
    if (fragment != NULL) {
130
699k
        xmlChar *newURI;
131
699k
  uri->fragment = NULL;
132
699k
  newURI = xmlSaveUri(uri);
133
699k
  idoc = xsltLoadDocument(tctxt, newURI);
134
699k
  xmlFree(newURI);
135
699k
    } else
136
8.29M
  idoc = xsltLoadDocument(tctxt, URI);
137
8.99M
    xmlFreeURI(uri);
138
139
8.99M
    if (idoc == NULL) {
140
8.99M
  if ((URI == NULL) ||
141
8.99M
      (URI[0] == '#') ||
142
8.99M
      ((tctxt->style->doc != NULL) &&
143
8.59M
      (xmlStrEqual(tctxt->style->doc->URL, URI))))
144
402k
  {
145
      /*
146
      * This selects the stylesheet's doc itself.
147
      */
148
402k
      doc = tctxt->style->doc;
149
8.59M
  } else {
150
8.59M
      valuePush(ctxt, xmlXPathNewNodeSet(NULL));
151
152
8.59M
      if (fragment != NULL)
153
297k
    xmlFree(fragment);
154
155
8.59M
      return;
156
8.59M
  }
157
8.99M
    } else
158
0
  doc = idoc->doc;
159
160
402k
    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
402k
#ifdef LIBXML_XPTR_ENABLED
167
402k
    xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
168
402k
    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
402k
#if LIBXML_VERSION >= 20911 || \
175
402k
    defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
176
402k
    xptrctxt->opLimit = ctxt->context->opLimit;
177
402k
    xptrctxt->opCount = ctxt->context->opCount;
178
402k
    xptrctxt->depth = ctxt->context->depth;
179
180
402k
    resObj = xmlXPtrEval(fragment, xptrctxt);
181
182
402k
    ctxt->context->opCount = xptrctxt->opCount;
183
#else
184
    resObj = xmlXPtrEval(fragment, xptrctxt);
185
#endif
186
187
402k
    xmlXPathFreeContext(xptrctxt);
188
402k
#endif /* LIBXML_XPTR_ENABLED */
189
190
402k
    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
402k
out_fragment:
199
402k
    if (resObj == NULL)
200
351k
        resObj = xmlXPathNewNodeSet(NULL);
201
402k
    valuePush(ctxt, resObj);
202
402k
    xmlFree(fragment);
203
402k
}
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
12.9M
{
216
12.9M
    xmlXPathObjectPtr obj, obj2 = NULL;
217
12.9M
    xmlChar *base = NULL, *URI;
218
219
220
12.9M
    if ((nargs < 1) || (nargs > 2)) {
221
476
        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
222
476
                         "document() : invalid number of args %d\n",
223
476
                         nargs);
224
476
        ctxt->error = XPATH_INVALID_ARITY;
225
476
        return;
226
476
    }
227
12.9M
    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
12.9M
    if (nargs == 2) {
235
12.8M
        if (ctxt->value->type != XPATH_NODESET) {
236
46
            xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
237
46
                             "document() : invalid arg expecting a nodeset\n");
238
46
            ctxt->error = XPATH_INVALID_TYPE;
239
46
            return;
240
46
        }
241
242
12.8M
        obj2 = valuePop(ctxt);
243
12.8M
    }
244
245
12.9M
    if (ctxt->value->type == XPATH_NODESET) {
246
34.7k
        int i;
247
34.7k
        xmlXPathObjectPtr newobj, ret;
248
249
34.7k
        obj = valuePop(ctxt);
250
34.7k
        ret = xmlXPathNewNodeSet(NULL);
251
252
34.7k
        if ((obj != NULL) && obj->nodesetval) {
253
12.9M
            for (i = 0; i < obj->nodesetval->nodeNr; i++) {
254
12.8M
                valuePush(ctxt,
255
12.8M
                          xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
256
12.8M
                xmlXPathStringFunction(ctxt, 1);
257
12.8M
                if (nargs == 2) {
258
3.57M
                    valuePush(ctxt, xmlXPathObjectCopy(obj2));
259
9.31M
                } else {
260
9.31M
                    valuePush(ctxt,
261
9.31M
                              xmlXPathNewNodeSet(obj->nodesetval->
262
9.31M
                                                 nodeTab[i]));
263
9.31M
                }
264
12.8M
                xsltDocumentFunction(ctxt, 2);
265
12.8M
                newobj = valuePop(ctxt);
266
12.8M
                ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
267
12.8M
                                                       newobj->nodesetval);
268
12.8M
                xmlXPathFreeObject(newobj);
269
12.8M
            }
270
29.3k
        }
271
272
34.7k
        if (obj != NULL)
273
34.7k
            xmlXPathFreeObject(obj);
274
34.7k
        if (obj2 != NULL)
275
8.12k
            xmlXPathFreeObject(obj2);
276
34.7k
        valuePush(ctxt, ret);
277
34.7k
        return;
278
34.7k
    }
279
    /*
280
     * Make sure it's converted to a string
281
     */
282
12.9M
    xmlXPathStringFunction(ctxt, 1);
283
12.9M
    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
12.9M
    obj = valuePop(ctxt);
292
12.9M
    if (obj->stringval == NULL) {
293
0
        valuePush(ctxt, xmlXPathNewNodeSet(NULL));
294
12.9M
    } else {
295
12.9M
        xsltTransformContextPtr tctxt;
296
12.9M
        tctxt = xsltXPathGetTransformContext(ctxt);
297
12.9M
        if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
298
12.9M
            (obj2->nodesetval->nodeNr > 0) &&
299
12.9M
            IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) {
300
10.4M
            xmlNodePtr target;
301
302
10.4M
            target = obj2->nodesetval->nodeTab[0];
303
10.4M
            if ((target->type == XML_ATTRIBUTE_NODE) ||
304
10.4M
          (target->type == XML_PI_NODE)) {
305
41.9k
                target = ((xmlAttrPtr) target)->parent;
306
41.9k
            }
307
10.4M
            base = xmlNodeGetBase(target->doc, target);
308
10.4M
        } else {
309
2.50M
            if ((tctxt != NULL) && (tctxt->inst != NULL)) {
310
2.50M
                base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
311
2.50M
            } else if ((tctxt != NULL) && (tctxt->style != NULL) &&
312
0
                       (tctxt->style->doc != NULL)) {
313
0
                base = xmlNodeGetBase(tctxt->style->doc,
314
0
                                      (xmlNodePtr) tctxt->style->doc);
315
0
            }
316
2.50M
        }
317
12.9M
        URI = xmlBuildURI(obj->stringval, base);
318
12.9M
        if (base != NULL)
319
3.66M
            xmlFree(base);
320
12.9M
        if (URI == NULL) {
321
3.93M
            if ((tctxt != NULL) && (tctxt->style != NULL) &&
322
3.93M
                (tctxt->style->doc != NULL) &&
323
3.93M
                (xmlStrEqual(URI, tctxt->style->doc->URL))) {
324
                /* This selects the stylesheet's doc itself. */
325
0
                valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc));
326
3.93M
            } else {
327
3.93M
                valuePush(ctxt, xmlXPathNewNodeSet(NULL));
328
3.93M
            }
329
9.00M
        } else {
330
9.00M
      xsltDocumentFunctionLoadDocument( ctxt, URI );
331
9.00M
      xmlFree(URI);
332
9.00M
  }
333
12.9M
    }
334
12.9M
    xmlXPathFreeObject(obj);
335
12.9M
    if (obj2 != NULL)
336
12.8M
        xmlXPathFreeObject(obj2);
337
12.9M
}
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
166
xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
546
166
    xmlXPathObjectPtr obj;
547
166
    xmlChar *str;
548
549
166
    if ((nargs != 1) || (ctxt->value == NULL)) {
550
19
        xsltGenericError(xsltGenericErrorContext,
551
19
    "unparsed-entity-uri() : expects one string arg\n");
552
19
  ctxt->error = XPATH_INVALID_ARITY;
553
19
  return;
554
19
    }
555
147
    obj = valuePop(ctxt);
556
147
    if (obj->type != XPATH_STRING) {
557
99
  obj = xmlXPathConvertString(obj);
558
99
    }
559
560
147
    str = obj->stringval;
561
147
    if (str == NULL) {
562
0
  valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
563
147
    } else {
564
147
  xmlEntityPtr entity;
565
566
147
  entity = xmlGetDocEntity(ctxt->context->doc, str);
567
147
  if (entity == NULL) {
568
147
      valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
569
147
  } 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
147
    }
576
147
    xmlXPathFreeObject(obj);
577
147
}
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
11.3k
{
590
11.3k
    xmlXPathObjectPtr numberObj = NULL;
591
11.3k
    xmlXPathObjectPtr formatObj = NULL;
592
11.3k
    xmlXPathObjectPtr decimalObj = NULL;
593
11.3k
    xsltStylesheetPtr sheet;
594
11.3k
    xsltDecimalFormatPtr formatValues = NULL;
595
11.3k
    xmlChar *result;
596
11.3k
    const xmlChar *ncname;
597
11.3k
    const xmlChar *prefix = NULL;
598
11.3k
    const xmlChar *nsUri = NULL;
599
11.3k
    xsltTransformContextPtr tctxt;
600
601
11.3k
    tctxt = xsltXPathGetTransformContext(ctxt);
602
11.3k
    if ((tctxt == NULL) || (tctxt->inst == NULL))
603
0
  return;
604
11.3k
    sheet = tctxt->style;
605
11.3k
    if (sheet == NULL)
606
0
  return;
607
11.3k
    formatValues = sheet->decimalFormat;
608
609
11.3k
    switch (nargs) {
610
3.54k
    case 3:
611
3.54k
        if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_STRING))
612
161
            xmlXPathStringFunction(ctxt, 1);
613
3.54k
  decimalObj = valuePop(ctxt);
614
3.54k
        ncname = xsltSplitQName(sheet->dict, decimalObj->stringval, &prefix);
615
3.54k
        if (prefix != NULL) {
616
3.16k
            xmlNsPtr ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, prefix);
617
3.16k
            if (ns == NULL) {
618
3.13k
                xsltTransformError(tctxt, NULL, NULL,
619
3.13k
                    "format-number : No namespace found for QName '%s:%s'\n",
620
3.13k
                    prefix, ncname);
621
3.13k
                sheet->errors++;
622
3.13k
                ncname = NULL;
623
3.13k
            }
624
27
            else {
625
27
                nsUri = ns->href;
626
27
            }
627
3.16k
        }
628
3.54k
        if (ncname != NULL) {
629
412
      formatValues = xsltDecimalFormatGetByQName(sheet, nsUri, ncname);
630
412
        }
631
3.54k
  if (formatValues == NULL) {
632
412
      xsltTransformError(tctxt, NULL, NULL,
633
412
        "format-number() : undeclared decimal format '%s'\n",
634
412
        decimalObj->stringval);
635
412
  }
636
  /* Intentional fall-through */
637
11.3k
    case 2:
638
11.3k
        if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_STRING))
639
1.67k
            xmlXPathStringFunction(ctxt, 1);
640
11.3k
  formatObj = valuePop(ctxt);
641
11.3k
        if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_NUMBER))
642
450
            xmlXPathNumberFunction(ctxt, 1);
643
11.3k
  numberObj = valuePop(ctxt);
644
11.3k
  break;
645
81
    default:
646
81
  xmlXPathErr(ctxt, XPATH_INVALID_ARITY);
647
81
        return;
648
11.3k
    }
649
650
11.3k
    if (formatValues != NULL) {
651
10.8k
  if (xsltFormatNumberConversion(formatValues,
652
10.8k
               formatObj->stringval,
653
10.8k
               numberObj->floatval,
654
10.8k
               &result) == XPATH_EXPRESSION_OK) {
655
10.8k
      valuePush(ctxt, xmlXPathNewString(result));
656
10.8k
      xmlFree(result);
657
10.8k
  }
658
10.8k
    }
659
660
11.3k
    xmlXPathFreeObject(numberObj);
661
11.3k
    xmlXPathFreeObject(formatObj);
662
11.3k
    xmlXPathFreeObject(decimalObj);
663
11.3k
}
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
537
xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
675
537
    xsltTransformContextPtr tctxt;
676
537
    xmlNodePtr cur = NULL;
677
537
    xmlXPathObjectPtr obj = NULL;
678
537
    char *str;
679
537
    const xmlChar *nsPrefix = NULL;
680
537
    void **psviPtr;
681
537
    unsigned long id;
682
537
    size_t size, nsPrefixSize;
683
684
537
    tctxt = xsltXPathGetTransformContext(ctxt);
685
686
537
    if (nargs == 0) {
687
15
  cur = ctxt->context->node;
688
522
    } else if (nargs == 1) {
689
516
  xmlNodeSetPtr nodelist;
690
516
  int i, ret;
691
692
516
  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
497
  obj = valuePop(ctxt);
699
497
  nodelist = obj->nodesetval;
700
497
  if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
701
33
      valuePush(ctxt, xmlXPathNewCString(""));
702
33
      goto out;
703
33
  }
704
464
  cur = nodelist->nodeTab[0];
705
209k
  for (i = 1;i < nodelist->nodeNr;i++) {
706
208k
      ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
707
208k
      if (ret == -1)
708
5
          cur = nodelist->nodeTab[i];
709
208k
  }
710
464
    } else {
711
6
  xsltTransformError(tctxt, NULL, NULL,
712
6
    "generate-id() : invalid number of args %d\n", nargs);
713
6
  ctxt->error = XPATH_INVALID_ARITY;
714
6
  goto out;
715
6
    }
716
717
479
    size = 30; /* for "id%lu" */
718
719
479
    if (cur->type == XML_NAMESPACE_DECL) {
720
25
        xmlNsPtr ns = (xmlNsPtr) cur;
721
722
25
        nsPrefix = ns->prefix;
723
25
        if (nsPrefix == NULL)
724
0
            nsPrefix = BAD_CAST "";
725
25
        nsPrefixSize = xmlStrlen(nsPrefix);
726
        /* For "ns" and hex-encoded string */
727
25
        size += nsPrefixSize * 2 + 2;
728
729
        /* Parent is stored in 'next'. */
730
25
        cur = (xmlNodePtr) ns->next;
731
25
    }
732
733
479
    psviPtr = xsltGetPSVIPtr(cur);
734
479
    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
479
    if (xsltGetSourceNodeFlags(cur) & XSLT_SOURCE_NODE_HAS_ID) {
742
43
        id = (unsigned long) (size_t) *psviPtr;
743
436
    } else {
744
436
        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
436
        } 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
436
        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
436
        id = ++tctxt->currentId;
762
436
        *psviPtr = (void *) (size_t) id;
763
436
        xsltSetSourceNodeFlags(tctxt, cur, XSLT_SOURCE_NODE_HAS_ID);
764
436
    }
765
766
479
    str = xmlMalloc(size);
767
479
    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
479
    if (nsPrefix == NULL) {
774
454
        snprintf(str, size, "id%lu", id);
775
454
    } else {
776
25
        size_t i, j;
777
778
25
        snprintf(str, size, "id%luns", id);
779
780
        /*
781
         * Only ASCII alphanumerics are allowed, so we hex-encode the prefix.
782
         */
783
25
        j = strlen(str);
784
98
        for (i = 0; i < nsPrefixSize; i++) {
785
73
            int v;
786
787
73
            v = nsPrefix[i] >> 4;
788
73
            str[j++] = v < 10 ? '0' + v : 'A' + (v - 10);
789
73
            v = nsPrefix[i] & 15;
790
73
            str[j++] = v < 10 ? '0' + v : 'A' + (v - 10);
791
73
        }
792
25
        str[j] = '\0';
793
25
    }
794
479
    valuePush(ctxt, xmlXPathWrapString(BAD_CAST str));
795
796
537
out:
797
537
    xmlXPathFreeObject(obj);
798
537
}
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
122
xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
810
122
    xmlXPathObjectPtr obj;
811
122
    xmlChar *prefix, *name;
812
122
    const xmlChar *nsURI = NULL;
813
814
122
    if (nargs != 1) {
815
17
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
816
17
    "system-property() : expects one string arg\n");
817
17
  ctxt->error = XPATH_INVALID_ARITY;
818
17
  return;
819
17
    }
820
105
    if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
821
20
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
822
20
      "system-property() : invalid arg expecting a string\n");
823
20
  ctxt->error = XPATH_INVALID_TYPE;
824
20
  return;
825
20
    }
826
85
    obj = valuePop(ctxt);
827
85
    if (obj->stringval == NULL) {
828
0
  valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
829
85
    } else {
830
85
  name = xmlSplitQName2(obj->stringval, &prefix);
831
85
  if (name == NULL) {
832
39
      name = xmlStrdup(obj->stringval);
833
46
  } else {
834
46
      nsURI = xmlXPathNsLookup(ctxt->context, prefix);
835
46
      if (nsURI == NULL) {
836
37
    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
837
37
        "system-property() : prefix %s is not bound\n", prefix);
838
37
      }
839
46
  }
840
841
85
  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
85
  } else {
884
85
      valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
885
85
        }
886
85
  if (name != NULL)
887
85
      xmlFree(name);
888
85
  if (prefix != NULL)
889
46
      xmlFree(prefix);
890
85
    }
891
85
    xmlXPathFreeObject(obj);
892
85
}
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
154
xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
904
154
    xmlXPathObjectPtr obj;
905
154
    xmlChar *prefix, *name;
906
154
    const xmlChar *nsURI = NULL;
907
154
    xsltTransformContextPtr tctxt;
908
909
154
    if (nargs != 1) {
910
16
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
911
16
    "element-available() : expects one string arg\n");
912
16
  ctxt->error = XPATH_INVALID_ARITY;
913
16
  return;
914
16
    }
915
138
    xmlXPathStringFunction(ctxt, 1);
916
138
    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
138
    obj = valuePop(ctxt);
923
138
    tctxt = xsltXPathGetTransformContext(ctxt);
924
138
    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
138
    name = xmlSplitQName2(obj->stringval, &prefix);
934
138
    if (name == NULL) {
935
50
  xmlNsPtr ns;
936
937
50
  name = xmlStrdup(obj->stringval);
938
50
  ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL);
939
50
  if (ns != NULL) nsURI = ns->href;
940
88
    } else {
941
88
  nsURI = xmlXPathNsLookup(ctxt->context, prefix);
942
88
  if (nsURI == NULL) {
943
40
      xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
944
40
    "element-available() : prefix %s is not bound\n", prefix);
945
40
  }
946
88
    }
947
948
138
    if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) {
949
20
  valuePush(ctxt, xmlXPathNewBoolean(1));
950
118
    } else {
951
118
  valuePush(ctxt, xmlXPathNewBoolean(0));
952
118
    }
953
954
138
    xmlXPathFreeObject(obj);
955
138
    if (name != NULL)
956
138
  xmlFree(name);
957
138
    if (prefix != NULL)
958
88
  xmlFree(prefix);
959
138
}
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
143
xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
971
143
    xmlXPathObjectPtr obj;
972
143
    xmlChar *prefix, *name;
973
143
    const xmlChar *nsURI = NULL;
974
975
143
    if (nargs != 1) {
976
15
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
977
15
    "function-available() : expects one string arg\n");
978
15
  ctxt->error = XPATH_INVALID_ARITY;
979
15
  return;
980
15
    }
981
128
    xmlXPathStringFunction(ctxt, 1);
982
128
    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
128
    obj = valuePop(ctxt);
989
990
128
    name = xmlSplitQName2(obj->stringval, &prefix);
991
128
    if (name == NULL) {
992
57
  name = xmlStrdup(obj->stringval);
993
71
    } else {
994
71
  nsURI = xmlXPathNsLookup(ctxt->context, prefix);
995
71
  if (nsURI == NULL) {
996
45
      xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
997
45
    "function-available() : prefix %s is not bound\n", prefix);
998
45
  }
999
71
    }
1000
1001
128
    if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
1002
21
  valuePush(ctxt, xmlXPathNewBoolean(1));
1003
107
    } else {
1004
107
  valuePush(ctxt, xmlXPathNewBoolean(0));
1005
107
    }
1006
1007
128
    xmlXPathFreeObject(obj);
1008
128
    if (name != NULL)
1009
128
  xmlFree(name);
1010
128
    if (prefix != NULL)
1011
71
  xmlFree(prefix);
1012
128
}
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
49
xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
1024
49
    xsltTransformContextPtr tctxt;
1025
1026
49
    if (nargs != 0) {
1027
22
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
1028
22
    "current() : function uses no argument\n");
1029
22
  ctxt->error = XPATH_INVALID_ARITY;
1030
22
  return;
1031
22
    }
1032
27
    tctxt = xsltXPathGetTransformContext(ctxt);
1033
27
    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
27
    } else {
1038
27
  valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
1039
27
    }
1040
27
}
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.75k
{
1057
3.75k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current",
1058
3.75k
                         xsltCurrentFunction);
1059
3.75k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document",
1060
3.75k
                         xsltDocumentFunction);
1061
3.75k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction);
1062
3.75k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri",
1063
3.75k
                         xsltUnparsedEntityURIFunction);
1064
3.75k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number",
1065
3.75k
                         xsltFormatNumberFunction);
1066
3.75k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id",
1067
3.75k
                         xsltGenerateIdFunction);
1068
3.75k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property",
1069
3.75k
                         xsltSystemPropertyFunction);
1070
3.75k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available",
1071
3.75k
                         xsltElementAvailableFunction);
1072
3.75k
    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available",
1073
3.75k
                         xsltFunctionAvailableFunction);
1074
3.75k
}