Coverage Report

Created: 2025-10-10 06:37

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