Coverage Report

Created: 2025-12-14 06:19

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