Coverage Report

Created: 2024-08-17 06:44

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