Coverage Report

Created: 2025-12-14 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxslt/libxslt/variables.c
Line
Count
Source
1
/*
2
 * variables.c: Implementation of the variable storage and lookup
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
 */
11
12
#define IN_LIBXSLT
13
#include "libxslt.h"
14
15
#include <string.h>
16
17
#include <libxml/xmlmemory.h>
18
#include <libxml/tree.h>
19
#include <libxml/valid.h>
20
#include <libxml/hash.h>
21
#include <libxml/xmlerror.h>
22
#include <libxml/xpath.h>
23
#include <libxml/xpathInternals.h>
24
#include <libxml/parserInternals.h>
25
#include <libxml/dict.h>
26
#include "xslt.h"
27
#include "xsltInternals.h"
28
#include "xsltutils.h"
29
#include "variables.h"
30
#include "transform.h"
31
#include "imports.h"
32
#include "preproc.h"
33
#include "keys.h"
34
35
#ifdef WITH_XSLT_DEBUG
36
 #define WITH_XSLT_DEBUG_VARIABLE
37
#endif
38
39
#ifdef XSLT_REFACTORED
40
const xmlChar *xsltDocFragFake = (const xmlChar *) " fake node libxslt";
41
#endif
42
43
static const xmlChar *xsltComputingGlobalVarMarker =
44
 (const xmlChar *) " var/param being computed";
45
46
#define XSLT_VAR_GLOBAL (1<<0)
47
0
#define XSLT_VAR_IN_SELECT (1<<1)
48
0
#define XSLT_TCTXT_VARIABLE(c) ((xsltStackElemPtr) (c)->contextVariable)
49
50
/************************************************************************
51
 *                  *
52
 *  Result Value Tree (Result Tree Fragment) interfaces     *
53
 *                  *
54
 ************************************************************************/
55
/**
56
 * xsltCreateRVT:
57
 * @ctxt:  an XSLT transformation context
58
 *
59
 * Creates a Result Value Tree
60
 * (the XSLT 1.0 term for this is "Result Tree Fragment")
61
 *
62
 * Returns the result value tree or NULL in case of API or internal errors.
63
 */
64
xmlDocPtr
65
xsltCreateRVT(xsltTransformContextPtr ctxt)
66
0
{
67
0
    xmlDocPtr container;
68
69
    /*
70
    * Question: Why is this function public?
71
    * Answer: It is called by the EXSLT module.
72
    */
73
0
    if (ctxt == NULL)
74
0
  return(NULL);
75
76
    /*
77
    * Reuse a RTF from the cache if available.
78
    */
79
0
    if (ctxt->cache->RVT) {
80
0
  container = ctxt->cache->RVT;
81
0
  ctxt->cache->RVT = (xmlDocPtr) container->next;
82
  /* clear the internal pointers */
83
0
  container->next = NULL;
84
0
  container->prev = NULL;
85
0
  if (ctxt->cache->nbRVT > 0)
86
0
      ctxt->cache->nbRVT--;
87
#ifdef XSLT_DEBUG_PROFILE_CACHE
88
  ctxt->cache->dbgReusedRVTs++;
89
#endif
90
0
  return(container);
91
0
    }
92
93
0
    container = xmlNewDoc(NULL);
94
0
    if (container == NULL)
95
0
  return(NULL);
96
0
    container->dict = ctxt->dict;
97
0
    xmlDictReference(container->dict);
98
0
    XSLT_MARK_RES_TREE_FRAG(container);
99
0
    container->doc = container;
100
0
    container->parent = NULL;
101
0
    return(container);
102
0
}
103
104
/**
105
 * xsltRegisterTmpRVT:
106
 * @ctxt:  an XSLT transformation context
107
 * @RVT:  a result value tree (Result Tree Fragment)
108
 *
109
 * Registers the result value tree (XSLT 1.0 term: Result Tree Fragment)
110
 * in the garbage collector.
111
 * The fragment will be freed at the exit of the currently
112
 * instantiated xsl:template.
113
 * Obsolete; this function might produce massive memory overhead,
114
 * since the fragment is only freed when the current xsl:template
115
 * exits. Use xsltRegisterLocalRVT() instead.
116
 *
117
 * Returns 0 in case of success and -1 in case of API or internal errors.
118
 */
119
int
120
xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
121
0
{
122
0
    if ((ctxt == NULL) || (RVT == NULL))
123
0
  return(-1);
124
125
0
    RVT->prev = NULL;
126
0
    RVT->compression = XSLT_RVT_LOCAL;
127
128
    /*
129
    * We'll restrict the lifetime of user-created fragments
130
    * insinde an xsl:variable and xsl:param to the lifetime of the
131
    * var/param itself.
132
    */
133
0
    if (ctxt->contextVariable != NULL) {
134
0
  RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment;
135
0
  XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT;
136
0
  return(0);
137
0
    }
138
139
0
    RVT->next = (xmlNodePtr) ctxt->tmpRVT;
140
0
    if (ctxt->tmpRVT != NULL)
141
0
  ctxt->tmpRVT->prev = (xmlNodePtr) RVT;
142
0
    ctxt->tmpRVT = RVT;
143
0
    return(0);
144
0
}
145
146
/**
147
 * xsltRegisterLocalRVT:
148
 * @ctxt:  an XSLT transformation context
149
 * @RVT:  a result value tree (Result Tree Fragment; xmlDocPtr)
150
 *
151
 * Registers a result value tree (XSLT 1.0 term: Result Tree Fragment)
152
 * in the RVT garbage collector.
153
 * The fragment will be freed when the instruction which created the
154
 * fragment exits.
155
 *
156
 * Returns 0 in case of success and -1 in case of API or internal errors.
157
 */
158
int
159
xsltRegisterLocalRVT(xsltTransformContextPtr ctxt,
160
         xmlDocPtr RVT)
161
0
{
162
0
    if ((ctxt == NULL) || (RVT == NULL))
163
0
  return(-1);
164
165
0
    RVT->prev = NULL;
166
0
    RVT->compression = XSLT_RVT_LOCAL;
167
168
    /*
169
    * When evaluating "select" expressions of xsl:variable
170
    * and xsl:param, we need to bind newly created tree fragments
171
    * to the variable itself; otherwise the fragment will be
172
    * freed before we leave the scope of a var.
173
    */
174
0
    if ((ctxt->contextVariable != NULL) &&
175
0
  (XSLT_TCTXT_VARIABLE(ctxt)->flags & XSLT_VAR_IN_SELECT))
176
0
    {
177
0
  RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment;
178
0
  XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT;
179
0
  return(0);
180
0
    }
181
    /*
182
    * Store the fragment in the scope of the current instruction.
183
    * If not reference by a returning instruction (like EXSLT's function),
184
    * then this fragment will be freed, when the instruction exits.
185
    */
186
0
    RVT->next = (xmlNodePtr) ctxt->localRVT;
187
0
    if (ctxt->localRVT != NULL)
188
0
  ctxt->localRVT->prev = (xmlNodePtr) RVT;
189
0
    ctxt->localRVT = RVT;
190
0
    return(0);
191
0
}
192
193
/**
194
 * xsltExtensionInstructionResultFinalize:
195
 * @ctxt:  an XSLT transformation context
196
 *
197
 * Finalizes the data (e.g. result tree fragments) created
198
 * within a value-returning process (e.g. EXSLT's function).
199
 * Tree fragments marked as being returned by a function are
200
 * set to normal state, which means that the fragment garbage
201
 * collector will free them after the function-calling process exits.
202
 *
203
 * Returns 0 in case of success and -1 in case of API or internal errors.
204
 *
205
 * This function is unsupported in newer releases of libxslt.
206
 */
207
int
208
xsltExtensionInstructionResultFinalize(
209
        xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED)
210
0
{
211
0
    xmlGenericError(xmlGenericErrorContext,
212
0
            "xsltExtensionInstructionResultFinalize is unsupported "
213
0
            "in this release of libxslt.\n");
214
0
    return(-1);
215
0
}
216
217
/**
218
 * xsltExtensionInstructionResultRegister:
219
 * @ctxt: an XSLT transformation context
220
 * @obj: an XPath object to be inspected for result tree fragments
221
 *
222
 * Marks the result of a value-returning extension instruction
223
 * in order to avoid it being garbage collected before the
224
 * extension instruction exits.
225
 * Note that one still has to additionally register any newly created
226
 * tree fragments (via xsltCreateRVT()) with xsltRegisterLocalRVT().
227
 *
228
 * Returns 0 in case of success and -1 in case of error.
229
 *
230
 * It isn't necessary to call this function in newer releases of
231
 * libxslt.
232
 */
233
int
234
xsltExtensionInstructionResultRegister(
235
        xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
236
  xmlXPathObjectPtr obj ATTRIBUTE_UNUSED)
237
0
{
238
0
    return(0);
239
0
}
240
241
/**
242
 * xsltFlagRVTs:
243
 * @ctxt: an XSLT transformation context
244
 * @obj: an XPath object to be inspected for result tree fragments
245
 * @val: the flag value
246
 *
247
 * Updates ownership information of RVTs in @obj according to @val.
248
 *
249
 * @val = XSLT_RVT_FUNC_RESULT for the result of an extension function, so its
250
 *        RVTs won't be destroyed after leaving the returning scope.
251
 * @val = XSLT_RVT_LOCAL for the result of an extension function to reset
252
 *        the state of its RVTs after it was returned to a new scope.
253
 * @val = XSLT_RVT_GLOBAL for parts of global variables.
254
 *
255
 * Returns 0 in case of success and -1 in case of error.
256
 */
257
int
258
0
xsltFlagRVTs(xsltTransformContextPtr ctxt, xmlXPathObjectPtr obj, int val) {
259
0
    int i;
260
0
    xmlNodePtr cur;
261
0
    xmlDocPtr doc;
262
263
0
    if ((ctxt == NULL) || (obj == NULL))
264
0
  return(-1);
265
266
    /*
267
    * OPTIMIZE TODO: If no local variables/params and no local tree
268
    * fragments were created, then we don't need to analyse the XPath
269
    * objects for tree fragments.
270
    */
271
272
0
    if ((obj->type != XPATH_NODESET) && (obj->type != XPATH_XSLT_TREE))
273
0
  return(0);
274
0
    if ((obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0))
275
0
  return(0);
276
277
0
    for (i = 0; i < obj->nodesetval->nodeNr; i++) {
278
0
  cur = obj->nodesetval->nodeTab[i];
279
0
  if (cur->type == XML_NAMESPACE_DECL) {
280
      /*
281
      * The XPath module sets the owner element of a ns-node on
282
      * the ns->next field.
283
      */
284
0
      if ((((xmlNsPtr) cur)->next != NULL) &&
285
0
    (((xmlNsPtr) cur)->next->type == XML_ELEMENT_NODE))
286
0
      {
287
0
    cur = (xmlNodePtr) ((xmlNsPtr) cur)->next;
288
0
    doc = cur->doc;
289
0
      } else {
290
0
    xsltTransformError(ctxt, NULL, ctxt->inst,
291
0
        "Internal error in xsltFlagRVTs(): "
292
0
        "Cannot retrieve the doc of a namespace node.\n");
293
0
    return(-1);
294
0
      }
295
0
  } else {
296
0
      doc = cur->doc;
297
0
  }
298
0
  if (doc == NULL) {
299
0
      xsltTransformError(ctxt, NULL, ctxt->inst,
300
0
    "Internal error in xsltFlagRVTs(): "
301
0
    "Cannot retrieve the doc of a node.\n");
302
0
      return(-1);
303
0
  }
304
0
  if (doc->name && (doc->name[0] == ' ') &&
305
0
            doc->compression != XSLT_RVT_GLOBAL) {
306
      /*
307
      * This is a result tree fragment.
308
      * We store ownership information in the @compression field.
309
      * TODO: How do we know if this is a doc acquired via the
310
      *  document() function?
311
      */
312
#ifdef WITH_XSLT_DEBUG_VARIABLE
313
            XSLT_TRACE(ctxt, XSLT_TRACE_VARIABLES,
314
                       xsltGenericDebug(xsltGenericDebugContext,
315
                       "Flagging RVT %p: %d -> %d\n",
316
                       (void *) doc, doc->compression, val));
317
#endif
318
319
0
            if (val == XSLT_RVT_LOCAL) {
320
0
                if (doc->compression == XSLT_RVT_FUNC_RESULT)
321
0
                    doc->compression = XSLT_RVT_LOCAL;
322
0
            } else if (val == XSLT_RVT_GLOBAL) {
323
0
                if (doc->compression != XSLT_RVT_LOCAL) {
324
0
        xmlGenericError(xmlGenericErrorContext,
325
0
                            "xsltFlagRVTs: Invalid transition %d => GLOBAL\n",
326
0
                            doc->compression);
327
0
                    doc->compression = XSLT_RVT_GLOBAL;
328
0
                    return(-1);
329
0
                }
330
331
                /* Will be registered as persistant in xsltReleaseLocalRVTs. */
332
0
                doc->compression = XSLT_RVT_GLOBAL;
333
0
            } else if (val == XSLT_RVT_FUNC_RESULT) {
334
0
          doc->compression = val;
335
0
            }
336
0
  }
337
0
    }
338
339
0
    return(0);
340
0
}
341
342
/**
343
 * xsltReleaseRVT:
344
 * @ctxt:  an XSLT transformation context
345
 * @RVT:  a result value tree (Result Tree Fragment)
346
 *
347
 * Either frees the RVT (which is an xmlDoc) or stores
348
 * it in the context's cache for later reuse.
349
 */
350
void
351
xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
352
0
{
353
0
    if (RVT == NULL)
354
0
  return;
355
356
0
    if (ctxt && (ctxt->cache->nbRVT < 40)) {
357
  /*
358
  * Store the Result Tree Fragment.
359
  * Free the document info.
360
  */
361
0
  if (RVT->_private != NULL) {
362
0
      xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private);
363
0
      xmlFree(RVT->_private);
364
0
      RVT->_private = NULL;
365
0
  }
366
  /*
367
  * Clear the document tree.
368
  */
369
0
  if (RVT->children != NULL) {
370
0
      xmlFreeNodeList(RVT->children);
371
0
      RVT->children = NULL;
372
0
      RVT->last = NULL;
373
0
  }
374
0
  if (RVT->ids != NULL) {
375
0
      xmlFreeIDTable((xmlIDTablePtr) RVT->ids);
376
0
      RVT->ids = NULL;
377
0
  }
378
379
  /*
380
  * Reset the ownership information.
381
  */
382
0
  RVT->compression = 0;
383
384
0
  RVT->next = (xmlNodePtr) ctxt->cache->RVT;
385
0
  ctxt->cache->RVT = RVT;
386
387
0
  ctxt->cache->nbRVT++;
388
389
#ifdef XSLT_DEBUG_PROFILE_CACHE
390
  ctxt->cache->dbgCachedRVTs++;
391
#endif
392
0
  return;
393
0
    }
394
    /*
395
    * Free it.
396
    */
397
0
    if (RVT->_private != NULL) {
398
0
  xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private);
399
0
  xmlFree(RVT->_private);
400
0
    }
401
0
    xmlFreeDoc(RVT);
402
0
}
403
404
/**
405
 * xsltRegisterPersistRVT:
406
 * @ctxt:  an XSLT transformation context
407
 * @RVT:  a result value tree (Result Tree Fragment)
408
 *
409
 * Register the result value tree (XSLT 1.0 term: Result Tree Fragment)
410
 * in the fragment garbage collector.
411
 * The fragment will be freed when the transformation context is
412
 * freed.
413
 *
414
 * Returns 0 in case of success and -1 in case of error.
415
 */
416
int
417
xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
418
0
{
419
0
    if ((ctxt == NULL) || (RVT == NULL)) return(-1);
420
421
0
    RVT->compression = XSLT_RVT_GLOBAL;
422
0
    RVT->prev = NULL;
423
0
    RVT->next = (xmlNodePtr) ctxt->persistRVT;
424
0
    if (ctxt->persistRVT != NULL)
425
0
  ctxt->persistRVT->prev = (xmlNodePtr) RVT;
426
0
    ctxt->persistRVT = RVT;
427
0
    return(0);
428
0
}
429
430
/**
431
 * xsltFreeRVTs:
432
 * @ctxt:  an XSLT transformation context
433
 *
434
 * Frees all registered result value trees (Result Tree Fragments)
435
 * of the transformation. Internal function; should not be called
436
 * by user-code.
437
 */
438
void
439
xsltFreeRVTs(xsltTransformContextPtr ctxt)
440
0
{
441
0
    xmlDocPtr cur, next;
442
443
0
    if (ctxt == NULL)
444
0
  return;
445
    /*
446
    * Local fragments.
447
    */
448
0
    cur = ctxt->localRVT;
449
0
    while (cur != NULL) {
450
0
        next = (xmlDocPtr) cur->next;
451
0
  if (cur->_private != NULL) {
452
0
      xsltFreeDocumentKeys(cur->_private);
453
0
      xmlFree(cur->_private);
454
0
  }
455
0
  xmlFreeDoc(cur);
456
0
  cur = next;
457
0
    }
458
0
    ctxt->localRVT = NULL;
459
    /*
460
    * User-created per-template fragments.
461
    */
462
0
    cur = ctxt->tmpRVT;
463
0
    while (cur != NULL) {
464
0
        next = (xmlDocPtr) cur->next;
465
0
  if (cur->_private != NULL) {
466
0
      xsltFreeDocumentKeys(cur->_private);
467
0
      xmlFree(cur->_private);
468
0
  }
469
0
  xmlFreeDoc(cur);
470
0
  cur = next;
471
0
    }
472
0
    ctxt->tmpRVT = NULL;
473
    /*
474
    * Global fragments.
475
    */
476
0
    cur = ctxt->persistRVT;
477
0
    while (cur != NULL) {
478
0
        next = (xmlDocPtr) cur->next;
479
0
  if (cur->_private != NULL) {
480
0
      xsltFreeDocumentKeys(cur->_private);
481
0
      xmlFree(cur->_private);
482
0
  }
483
0
  xmlFreeDoc(cur);
484
0
  cur = next;
485
0
    }
486
0
    ctxt->persistRVT = NULL;
487
0
}
488
489
/************************************************************************
490
 *                  *
491
 *      Module interfaces       *
492
 *                  *
493
 ************************************************************************/
494
495
/**
496
 * xsltNewStackElem:
497
 *
498
 * Create a new XSLT ParserContext
499
 *
500
 * Returns the newly allocated xsltParserStackElem or NULL in case of error
501
 */
502
static xsltStackElemPtr
503
xsltNewStackElem(xsltTransformContextPtr ctxt)
504
0
{
505
0
    xsltStackElemPtr ret;
506
    /*
507
    * Reuse a stack item from the cache if available.
508
    */
509
0
    if (ctxt && ctxt->cache->stackItems) {
510
0
  ret = ctxt->cache->stackItems;
511
0
  ctxt->cache->stackItems = ret->next;
512
0
  ret->next = NULL;
513
0
  ctxt->cache->nbStackItems--;
514
#ifdef XSLT_DEBUG_PROFILE_CACHE
515
  ctxt->cache->dbgReusedVars++;
516
#endif
517
0
  return(ret);
518
0
    }
519
0
    ret = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
520
0
    if (ret == NULL) {
521
0
  xsltTransformError(NULL, NULL, NULL,
522
0
    "xsltNewStackElem : malloc failed\n");
523
0
  return(NULL);
524
0
    }
525
0
    memset(ret, 0, sizeof(xsltStackElem));
526
0
    ret->context = ctxt;
527
0
    return(ret);
528
0
}
529
530
/**
531
 * xsltCopyStackElem:
532
 * @elem:  an XSLT stack element
533
 *
534
 * Makes a copy of the stack element
535
 *
536
 * Returns the copy of NULL
537
 */
538
static xsltStackElemPtr
539
0
xsltCopyStackElem(xsltStackElemPtr elem) {
540
0
    xsltStackElemPtr cur;
541
542
0
    cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
543
0
    if (cur == NULL) {
544
0
  xsltTransformError(NULL, NULL, NULL,
545
0
    "xsltCopyStackElem : malloc failed\n");
546
0
  return(NULL);
547
0
    }
548
0
    memset(cur, 0, sizeof(xsltStackElem));
549
0
    cur->context = elem->context;
550
0
    cur->name = elem->name;
551
0
    cur->nameURI = elem->nameURI;
552
0
    cur->select = elem->select;
553
0
    cur->tree = elem->tree;
554
0
    cur->comp = elem->comp;
555
0
    return(cur);
556
0
}
557
558
/**
559
 * xsltFreeStackElem:
560
 * @elem:  an XSLT stack element
561
 *
562
 * Free up the memory allocated by @elem
563
 */
564
static void
565
0
xsltFreeStackElem(xsltStackElemPtr elem) {
566
0
    if (elem == NULL)
567
0
  return;
568
0
    if (elem->value != NULL)
569
0
  xmlXPathFreeObject(elem->value);
570
    /*
571
    * Release the list of temporary Result Tree Fragments.
572
    */
573
0
    if (elem->context) {
574
0
  xmlDocPtr cur;
575
576
0
  while (elem->fragment != NULL) {
577
0
      cur = elem->fragment;
578
0
      elem->fragment = (xmlDocPtr) cur->next;
579
580
0
            if (cur->compression == XSLT_RVT_LOCAL) {
581
0
    xsltReleaseRVT(elem->context, cur);
582
0
            } else if (cur->compression == XSLT_RVT_FUNC_RESULT) {
583
0
                xsltRegisterLocalRVT(elem->context, cur);
584
0
                cur->compression = XSLT_RVT_FUNC_RESULT;
585
0
            } else {
586
0
                xmlGenericError(xmlGenericErrorContext,
587
0
                        "xsltFreeStackElem: Unexpected RVT flag %d\n",
588
0
                        cur->compression);
589
0
            }
590
0
  }
591
0
    }
592
    /*
593
    * Cache or free the variable structure.
594
    */
595
0
    if (elem->context && (elem->context->cache->nbStackItems < 50)) {
596
  /*
597
  * Store the item in the cache.
598
  */
599
0
  xsltTransformContextPtr ctxt = elem->context;
600
0
  memset(elem, 0, sizeof(xsltStackElem));
601
0
  elem->context = ctxt;
602
0
  elem->next = ctxt->cache->stackItems;
603
0
  ctxt->cache->stackItems = elem;
604
0
  ctxt->cache->nbStackItems++;
605
#ifdef XSLT_DEBUG_PROFILE_CACHE
606
  ctxt->cache->dbgCachedVars++;
607
#endif
608
0
  return;
609
0
    }
610
0
    xmlFree(elem);
611
0
}
612
613
static void
614
0
xsltFreeStackElemEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
615
0
    xsltFreeStackElem((xsltStackElemPtr) payload);
616
0
}
617
618
619
/**
620
 * xsltFreeStackElemList:
621
 * @elem:  an XSLT stack element
622
 *
623
 * Free up the memory allocated by @elem
624
 */
625
void
626
0
xsltFreeStackElemList(xsltStackElemPtr elem) {
627
0
    xsltStackElemPtr next;
628
629
0
    while (elem != NULL) {
630
0
  next = elem->next;
631
0
  xsltFreeStackElem(elem);
632
0
  elem = next;
633
0
    }
634
0
}
635
636
/**
637
 * xsltStackLookup:
638
 * @ctxt:  an XSLT transformation context
639
 * @name:  the local part of the name
640
 * @nameURI:  the URI part of the name
641
 *
642
 * Locate an element in the stack based on its name.
643
 */
644
static xsltStackElemPtr
645
xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
646
0
          const xmlChar *nameURI) {
647
0
    int i;
648
0
    xsltStackElemPtr cur;
649
650
0
    if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0))
651
0
  return(NULL);
652
653
    /*
654
     * Do the lookup from the top of the stack, but
655
     * don't use params being computed in a call-param
656
     * First lookup expects the variable name and URI to
657
     * come from the disctionnary and hence pointer comparison.
658
     */
659
0
    for (i = ctxt->varsNr; i > ctxt->varsBase; i--) {
660
0
  cur = ctxt->varsTab[i-1];
661
0
  while (cur != NULL) {
662
0
      if ((cur->name == name) && (cur->nameURI == nameURI)) {
663
0
    return(cur);
664
0
      }
665
0
      cur = cur->next;
666
0
  }
667
0
    }
668
669
    /*
670
     * Redo the lookup with interned string compares
671
     * to avoid string compares.
672
     */
673
0
    name = xmlDictLookup(ctxt->dict, name, -1);
674
0
    if (nameURI != NULL)
675
0
        nameURI = xmlDictLookup(ctxt->dict, nameURI, -1);
676
677
0
    for (i = ctxt->varsNr; i > ctxt->varsBase; i--) {
678
0
  cur = ctxt->varsTab[i-1];
679
0
  while (cur != NULL) {
680
0
      if ((cur->name == name) && (cur->nameURI == nameURI)) {
681
0
    return(cur);
682
0
      }
683
0
      cur = cur->next;
684
0
  }
685
0
    }
686
687
0
    return(NULL);
688
0
}
689
690
#ifdef XSLT_REFACTORED
691
#else
692
693
/**
694
 * xsltCheckStackElem:
695
 * @ctxt:  xn XSLT transformation context
696
 * @name:  the variable name
697
 * @nameURI:  the variable namespace URI
698
 *
699
 * Checks whether a variable or param is already defined.
700
 *
701
 * URGENT TODO: Checks for redefinition of vars/params should be
702
 *  done only at compilation time.
703
 *
704
 * Returns 1 if variable is present, 2 if param is present, 3 if this
705
 *         is an inherited param, 0 if not found, -1 in case of failure.
706
 */
707
static int
708
xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name,
709
0
             const xmlChar *nameURI) {
710
0
    xsltStackElemPtr cur;
711
712
0
    if ((ctxt == NULL) || (name == NULL))
713
0
  return(-1);
714
715
0
    cur = xsltStackLookup(ctxt, name, nameURI);
716
0
    if (cur == NULL)
717
0
        return(0);
718
0
    if (cur->comp != NULL) {
719
0
        if (cur->comp->type == XSLT_FUNC_WITHPARAM)
720
0
      return(3);
721
0
  else if (cur->comp->type == XSLT_FUNC_PARAM)
722
0
      return(2);
723
0
    }
724
725
0
    return(1);
726
0
}
727
728
#endif /* XSLT_REFACTORED */
729
730
/**
731
 * xsltAddStackElem:
732
 * @ctxt:  xn XSLT transformation context
733
 * @elem:  a stack element
734
 *
735
 * Push an element (or list) onto the stack.
736
 * In case of a list, each member will be pushed into
737
 * a seperate slot; i.e. there's always 1 stack entry for
738
 * 1 stack element.
739
 *
740
 * Returns 0 in case of success, -1 in case of failure.
741
 */
742
static int
743
xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem)
744
0
{
745
0
    if ((ctxt == NULL) || (elem == NULL))
746
0
  return(-1);
747
748
0
    do {
749
0
  if (ctxt->varsNr >= ctxt->varsMax) {
750
0
            xsltStackElemPtr *tmp;
751
0
            int newMax = ctxt->varsMax == 0 ? 10 : 2 * ctxt->varsMax;
752
753
0
            tmp = (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
754
0
                    newMax * sizeof(*tmp));
755
0
            if (tmp == NULL) {
756
0
                xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
757
0
                return (-1);
758
0
            }
759
0
            ctxt->varsTab = tmp;
760
0
            ctxt->varsMax = newMax;
761
0
  }
762
0
  ctxt->varsTab[ctxt->varsNr++] = elem;
763
0
  ctxt->vars = elem;
764
765
0
  elem = elem->next;
766
0
    } while (elem != NULL);
767
768
0
    return(0);
769
0
}
770
771
/**
772
 * xsltAddStackElemList:
773
 * @ctxt:  xn XSLT transformation context
774
 * @elems:  a stack element list
775
 *
776
 * Push an element list onto the stack.
777
 *
778
 * Returns 0 in case of success, -1 in case of failure.
779
 */
780
int
781
xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems)
782
0
{
783
0
    return(xsltAddStackElem(ctxt, elems));
784
0
}
785
786
/************************************************************************
787
 *                  *
788
 *      Module interfaces       *
789
 *                  *
790
 ************************************************************************/
791
792
/**
793
 * xsltEvalVariable:
794
 * @ctxt:  the XSLT transformation context
795
 * @variable:  the variable or parameter item
796
 * @comp: the compiled XSLT instruction
797
 *
798
 * Evaluate a variable value.
799
 *
800
 * Returns the XPath Object value or NULL in case of error
801
 */
802
static xmlXPathObjectPtr
803
xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable,
804
           xsltStylePreCompPtr castedComp)
805
0
{
806
#ifdef XSLT_REFACTORED
807
    xsltStyleItemVariablePtr comp =
808
  (xsltStyleItemVariablePtr) castedComp;
809
#else
810
0
    xsltStylePreCompPtr comp = castedComp;
811
0
#endif
812
0
    xmlXPathObjectPtr result = NULL;
813
0
    xmlNodePtr oldInst;
814
815
0
    if ((ctxt == NULL) || (variable == NULL))
816
0
  return(NULL);
817
818
    /*
819
    * A variable or parameter are evaluated on demand; thus the
820
    * context (of XSLT and XPath) need to be temporarily adjusted and
821
    * restored on exit.
822
    */
823
0
    oldInst = ctxt->inst;
824
825
#ifdef WITH_XSLT_DEBUG_VARIABLE
826
    XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
827
  "Evaluating variable '%s'\n", variable->name));
828
#endif
829
0
    if (variable->select != NULL) {
830
0
  xmlXPathCompExprPtr xpExpr = NULL;
831
0
  xmlDocPtr oldXPDoc;
832
0
  xmlNodePtr oldXPContextNode;
833
0
  int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
834
0
  xmlNsPtr *oldXPNamespaces;
835
0
  xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
836
0
  xsltStackElemPtr oldVar = ctxt->contextVariable;
837
838
0
  if ((comp != NULL) && (comp->comp != NULL)) {
839
0
      xpExpr = comp->comp;
840
0
  } else {
841
0
      xpExpr = xmlXPathCtxtCompile(ctxt->xpathCtxt, variable->select);
842
0
  }
843
0
  if (xpExpr == NULL)
844
0
      return(NULL);
845
  /*
846
  * Save context states.
847
  */
848
0
  oldXPDoc = xpctxt->doc;
849
0
  oldXPContextNode = xpctxt->node;
850
0
  oldXPProximityPosition = xpctxt->proximityPosition;
851
0
  oldXPContextSize = xpctxt->contextSize;
852
0
  oldXPNamespaces = xpctxt->namespaces;
853
0
  oldXPNsNr = xpctxt->nsNr;
854
855
0
  xpctxt->node = ctxt->node;
856
  /*
857
  * OPTIMIZE TODO: Lame try to set the context doc.
858
  *   Get rid of this somehow in xpath.c.
859
  */
860
0
  if ((ctxt->node->type != XML_NAMESPACE_DECL) &&
861
0
      ctxt->node->doc)
862
0
      xpctxt->doc = ctxt->node->doc;
863
  /*
864
  * BUG TODO: The proximity position and the context size will
865
  *  potentially be wrong.
866
  *  Example:
867
  *  <xsl:template select="foo">
868
  *    <xsl:variable name="pos" select="position()"/>
869
  *    <xsl:for-each select="bar">
870
  *      <xsl:value-of select="$pos"/>
871
  *    </xsl:for-each>
872
  *  </xsl:template>
873
  *  Here the proximity position and context size are changed
874
  *  to the context of <xsl:for-each select="bar">, but
875
  *  the variable needs to be evaluated in the context of
876
  *  <xsl:template select="foo">.
877
  */
878
0
  if (comp != NULL) {
879
880
#ifdef XSLT_REFACTORED
881
      if (comp->inScopeNs != NULL) {
882
    xpctxt->namespaces = comp->inScopeNs->list;
883
    xpctxt->nsNr = comp->inScopeNs->xpathNumber;
884
      } else {
885
    xpctxt->namespaces = NULL;
886
    xpctxt->nsNr = 0;
887
      }
888
#else
889
0
      xpctxt->namespaces = comp->nsList;
890
0
      xpctxt->nsNr = comp->nsNr;
891
0
#endif
892
0
  } else {
893
0
      xpctxt->namespaces = NULL;
894
0
      xpctxt->nsNr = 0;
895
0
  }
896
897
  /*
898
  * We need to mark that we are "selecting" a var's value;
899
  * if any tree fragments are created inside the expression,
900
  * then those need to be stored inside the variable; otherwise
901
  * we'll eventually free still referenced fragments, before
902
  * we leave the scope of the variable.
903
  */
904
0
  ctxt->contextVariable = variable;
905
0
  variable->flags |= XSLT_VAR_IN_SELECT;
906
907
0
  result = xmlXPathCompiledEval(xpExpr, xpctxt);
908
909
0
  variable->flags ^= XSLT_VAR_IN_SELECT;
910
  /*
911
  * Restore Context states.
912
  */
913
0
  ctxt->contextVariable = oldVar;
914
915
0
  xpctxt->doc = oldXPDoc;
916
0
  xpctxt->node = oldXPContextNode;
917
0
  xpctxt->contextSize = oldXPContextSize;
918
0
  xpctxt->proximityPosition = oldXPProximityPosition;
919
0
  xpctxt->namespaces = oldXPNamespaces;
920
0
  xpctxt->nsNr = oldXPNsNr;
921
922
0
  if ((comp == NULL) || (comp->comp == NULL))
923
0
      xmlXPathFreeCompExpr(xpExpr);
924
0
  if (result == NULL) {
925
0
      xsltTransformError(ctxt, NULL,
926
0
    (comp != NULL) ? comp->inst : NULL,
927
0
    "Failed to evaluate the expression of variable '%s'.\n",
928
0
    variable->name);
929
0
      ctxt->state = XSLT_STATE_STOPPED;
930
931
#ifdef WITH_XSLT_DEBUG_VARIABLE
932
#ifdef LIBXML_DEBUG_ENABLED
933
  } else {
934
      if ((xsltGenericDebugContext == stdout) ||
935
    (xsltGenericDebugContext == stderr))
936
    xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
937
          result, 0);
938
#endif
939
#endif
940
0
  }
941
0
    } else {
942
0
  if (variable->tree == NULL) {
943
0
      result = xmlXPathNewCString("");
944
0
  } else {
945
0
      if (variable->tree) {
946
0
    xmlDocPtr container;
947
0
    xmlNodePtr oldInsert;
948
0
    xmlDocPtr  oldOutput;
949
0
                const xmlChar *oldLastText;
950
0
                int oldLastTextSize, oldLastTextUse;
951
0
    xsltStackElemPtr oldVar = ctxt->contextVariable;
952
953
    /*
954
    * Generate a result tree fragment.
955
    */
956
0
    container = xsltCreateRVT(ctxt);
957
0
    if (container == NULL)
958
0
        goto error;
959
    /*
960
    * NOTE: Local Result Tree Fragments of params/variables
961
    * are not registered globally anymore; the life-time
962
    * is not directly dependant of the param/variable itself.
963
    *
964
    * OLD: xsltRegisterTmpRVT(ctxt, container);
965
    */
966
    /*
967
    * Attach the Result Tree Fragment to the variable;
968
    * when the variable is freed, it will also free
969
    * the Result Tree Fragment.
970
    */
971
0
    variable->fragment = container;
972
0
                container->compression = XSLT_RVT_LOCAL;
973
974
0
    oldOutput = ctxt->output;
975
0
    oldInsert = ctxt->insert;
976
0
                oldLastText = ctxt->lasttext;
977
0
                oldLastTextSize = ctxt->lasttsize;
978
0
                oldLastTextUse = ctxt->lasttuse;
979
980
0
    ctxt->output = container;
981
0
    ctxt->insert = (xmlNodePtr) container;
982
0
    ctxt->contextVariable = variable;
983
    /*
984
    * Process the sequence constructor (variable->tree).
985
    * The resulting tree will be held by @container.
986
    */
987
0
    xsltApplyOneTemplate(ctxt, ctxt->node, variable->tree,
988
0
        NULL, NULL);
989
990
0
    ctxt->contextVariable = oldVar;
991
0
    ctxt->insert = oldInsert;
992
0
    ctxt->output = oldOutput;
993
0
                ctxt->lasttext = oldLastText;
994
0
                ctxt->lasttsize = oldLastTextSize;
995
0
                ctxt->lasttuse = oldLastTextUse;
996
997
0
    result = xmlXPathNewValueTree((xmlNodePtr) container);
998
0
      }
999
0
      if (result == NULL) {
1000
0
    result = xmlXPathNewCString("");
1001
0
      } else {
1002
                /*
1003
                 * This stops older libxml2 versions from freeing the nodes
1004
                 * in the tree.
1005
                 */
1006
0
          result->boolval = 0;
1007
0
      }
1008
#ifdef WITH_XSLT_DEBUG_VARIABLE
1009
#ifdef LIBXML_DEBUG_ENABLED
1010
1011
      if ((xsltGenericDebugContext == stdout) ||
1012
    (xsltGenericDebugContext == stderr))
1013
    xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
1014
          result, 0);
1015
#endif
1016
#endif
1017
0
  }
1018
0
    }
1019
1020
0
error:
1021
0
    ctxt->inst = oldInst;
1022
0
    return(result);
1023
0
}
1024
1025
/**
1026
 * xsltEvalGlobalVariable:
1027
 * @elem:  the variable or parameter
1028
 * @ctxt:  the XSLT transformation context
1029
 *
1030
 * Evaluates a the value of a global xsl:variable or
1031
 * xsl:param declaration.
1032
 *
1033
 * Returns the XPath Object value or NULL in case of error
1034
 */
1035
static xmlXPathObjectPtr
1036
xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt)
1037
0
{
1038
0
    xmlXPathObjectPtr result = NULL;
1039
0
    xmlNodePtr oldInst;
1040
0
    const xmlChar* oldVarName;
1041
0
    xsltStackElemPtr oldCtxtVar = ctxt->contextVariable;
1042
1043
#ifdef XSLT_REFACTORED
1044
    xsltStyleBasicItemVariablePtr comp;
1045
#else
1046
0
    xsltStylePreCompPtr comp;
1047
0
#endif
1048
1049
0
    if ((ctxt == NULL) || (elem == NULL))
1050
0
  return(NULL);
1051
0
    if (elem->computed)
1052
0
  return(elem->value);
1053
1054
1055
#ifdef WITH_XSLT_DEBUG_VARIABLE
1056
    XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1057
  "Evaluating global variable %s\n", elem->name));
1058
#endif
1059
1060
#ifdef WITH_DEBUGGER
1061
    if ((ctxt->debugStatus != XSLT_DEBUG_NONE) &&
1062
        elem->comp && elem->comp->inst)
1063
        xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt);
1064
#endif
1065
1066
0
    oldInst = ctxt->inst;
1067
#ifdef XSLT_REFACTORED
1068
    comp = (xsltStyleBasicItemVariablePtr) elem->comp;
1069
#else
1070
0
    comp = elem->comp;
1071
0
#endif
1072
0
    oldVarName = elem->name;
1073
0
    elem->name = xsltComputingGlobalVarMarker;
1074
1075
    /*
1076
     * The "context variable" isn't used for globals, so we must
1077
     * make sure to set it to NULL.
1078
     */
1079
0
    oldCtxtVar = ctxt->contextVariable;
1080
0
    ctxt->contextVariable = NULL;
1081
1082
    /*
1083
    * OPTIMIZE TODO: We should consider instantiating global vars/params
1084
    *  on-demand. The vars/params don't need to be evaluated if never
1085
    *  called; and in the case of global params, if values for such params
1086
    *  are provided by the user.
1087
    */
1088
0
    if (elem->select != NULL) {
1089
0
  xmlXPathCompExprPtr xpExpr = NULL;
1090
0
  xmlDocPtr oldXPDoc;
1091
0
  xmlNodePtr oldXPContextNode;
1092
0
  int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
1093
0
  xmlNsPtr *oldXPNamespaces;
1094
0
  xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
1095
1096
0
  if ((comp != NULL) && (comp->comp != NULL)) {
1097
0
      xpExpr = comp->comp;
1098
0
  } else {
1099
0
      xpExpr = xmlXPathCtxtCompile(ctxt->xpathCtxt, elem->select);
1100
0
  }
1101
0
  if (xpExpr == NULL)
1102
0
      goto error;
1103
1104
1105
0
  if (comp != NULL)
1106
0
      ctxt->inst = comp->inst;
1107
0
  else
1108
0
      ctxt->inst = NULL;
1109
  /*
1110
  * SPEC XSLT 1.0:
1111
  * "At top-level, the expression or template specifying the
1112
  *  variable value is evaluated with the same context as that used
1113
  *  to process the root node of the source document: the current
1114
  *  node is the root node of the source document and the current
1115
  *  node list is a list containing just the root node of the source
1116
  *  document."
1117
  */
1118
  /*
1119
  * Save context states.
1120
  */
1121
0
  oldXPDoc = xpctxt->doc;
1122
0
  oldXPContextNode = xpctxt->node;
1123
0
  oldXPProximityPosition = xpctxt->proximityPosition;
1124
0
  oldXPContextSize = xpctxt->contextSize;
1125
0
  oldXPNamespaces = xpctxt->namespaces;
1126
0
  oldXPNsNr = xpctxt->nsNr;
1127
1128
0
  xpctxt->node = ctxt->initialContextNode;
1129
0
  xpctxt->doc = ctxt->initialContextDoc;
1130
0
  xpctxt->contextSize = 1;
1131
0
  xpctxt->proximityPosition = 1;
1132
1133
0
  if (comp != NULL) {
1134
1135
#ifdef XSLT_REFACTORED
1136
      if (comp->inScopeNs != NULL) {
1137
    xpctxt->namespaces = comp->inScopeNs->list;
1138
    xpctxt->nsNr = comp->inScopeNs->xpathNumber;
1139
      } else {
1140
    xpctxt->namespaces = NULL;
1141
    xpctxt->nsNr = 0;
1142
      }
1143
#else
1144
0
      xpctxt->namespaces = comp->nsList;
1145
0
      xpctxt->nsNr = comp->nsNr;
1146
0
#endif
1147
0
  } else {
1148
0
      xpctxt->namespaces = NULL;
1149
0
      xpctxt->nsNr = 0;
1150
0
  }
1151
1152
0
  result = xmlXPathCompiledEval(xpExpr, xpctxt);
1153
1154
  /*
1155
  * Restore Context states.
1156
  */
1157
0
  xpctxt->doc = oldXPDoc;
1158
0
  xpctxt->node = oldXPContextNode;
1159
0
  xpctxt->contextSize = oldXPContextSize;
1160
0
  xpctxt->proximityPosition = oldXPProximityPosition;
1161
0
  xpctxt->namespaces = oldXPNamespaces;
1162
0
  xpctxt->nsNr = oldXPNsNr;
1163
1164
0
  if ((comp == NULL) || (comp->comp == NULL))
1165
0
      xmlXPathFreeCompExpr(xpExpr);
1166
0
  if (result == NULL) {
1167
0
      if (comp == NULL)
1168
0
    xsltTransformError(ctxt, NULL, NULL,
1169
0
        "Evaluating global variable %s failed\n", elem->name);
1170
0
      else
1171
0
    xsltTransformError(ctxt, NULL, comp->inst,
1172
0
        "Evaluating global variable %s failed\n", elem->name);
1173
0
      ctxt->state = XSLT_STATE_STOPPED;
1174
0
            goto error;
1175
0
        }
1176
1177
        /*
1178
         * Mark all RVTs that are referenced from result as part
1179
         * of this variable so they won't be freed too early.
1180
         */
1181
0
        xsltFlagRVTs(ctxt, result, XSLT_RVT_GLOBAL);
1182
1183
#ifdef WITH_XSLT_DEBUG_VARIABLE
1184
#ifdef LIBXML_DEBUG_ENABLED
1185
  if ((xsltGenericDebugContext == stdout) ||
1186
      (xsltGenericDebugContext == stderr))
1187
      xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
1188
            result, 0);
1189
#endif
1190
#endif
1191
0
    } else {
1192
0
  if (elem->tree == NULL) {
1193
0
      result = xmlXPathNewCString("");
1194
0
  } else {
1195
0
      xmlDocPtr container;
1196
0
      xmlNodePtr oldInsert;
1197
0
      xmlDocPtr  oldOutput, oldXPDoc;
1198
      /*
1199
      * Generate a result tree fragment.
1200
      */
1201
0
      container = xsltCreateRVT(ctxt);
1202
0
      if (container == NULL)
1203
0
    goto error;
1204
      /*
1205
      * Let the lifetime of the tree fragment be handled by
1206
      * the Libxslt's garbage collector.
1207
      */
1208
0
      xsltRegisterPersistRVT(ctxt, container);
1209
1210
0
      oldOutput = ctxt->output;
1211
0
      oldInsert = ctxt->insert;
1212
1213
0
      oldXPDoc = ctxt->xpathCtxt->doc;
1214
1215
0
      ctxt->output = container;
1216
0
      ctxt->insert = (xmlNodePtr) container;
1217
1218
0
      ctxt->xpathCtxt->doc = ctxt->initialContextDoc;
1219
      /*
1220
      * Process the sequence constructor.
1221
      */
1222
0
      xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL);
1223
1224
0
      ctxt->xpathCtxt->doc = oldXPDoc;
1225
1226
0
      ctxt->insert = oldInsert;
1227
0
      ctxt->output = oldOutput;
1228
1229
0
      result = xmlXPathNewValueTree((xmlNodePtr) container);
1230
0
      if (result == NULL) {
1231
0
    result = xmlXPathNewCString("");
1232
0
      } else {
1233
                /*
1234
                 * This stops older libxml2 versions from freeing the nodes
1235
                 * in the tree.
1236
                 */
1237
0
          result->boolval = 0;
1238
0
      }
1239
#ifdef WITH_XSLT_DEBUG_VARIABLE
1240
#ifdef LIBXML_DEBUG_ENABLED
1241
      if ((xsltGenericDebugContext == stdout) ||
1242
    (xsltGenericDebugContext == stderr))
1243
    xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
1244
          result, 0);
1245
#endif
1246
#endif
1247
0
  }
1248
0
    }
1249
1250
0
error:
1251
0
    ctxt->contextVariable = oldCtxtVar;
1252
1253
0
    elem->name = oldVarName;
1254
0
    ctxt->inst = oldInst;
1255
0
    if (result != NULL) {
1256
0
  elem->value = result;
1257
0
  elem->computed = 1;
1258
0
    }
1259
0
    return(result);
1260
0
}
1261
1262
/**
1263
 * xsltEvalGlobalVariables:
1264
 * @ctxt:  the XSLT transformation context
1265
 *
1266
 * Evaluates all global variables and parameters of a stylesheet.
1267
 * For internal use only. This is called at start of a transformation.
1268
 *
1269
 * Returns 0 in case of success, -1 in case of error
1270
 */
1271
int
1272
0
xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) {
1273
0
    xsltStackElemPtr elem;
1274
0
    xsltStackElemPtr head = NULL;
1275
0
    xsltStylesheetPtr style;
1276
1277
0
    if ((ctxt == NULL) || (ctxt->document == NULL))
1278
0
  return(-1);
1279
1280
#ifdef WITH_XSLT_DEBUG_VARIABLE
1281
    XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1282
  "Registering global variables\n"));
1283
#endif
1284
    /*
1285
     * Walk the list from the stylesheets and populate the hash table
1286
     */
1287
0
    style = ctxt->style;
1288
0
    while (style != NULL) {
1289
0
  elem = style->variables;
1290
1291
#ifdef WITH_XSLT_DEBUG_VARIABLE
1292
  if ((style->doc != NULL) && (style->doc->URL != NULL)) {
1293
      XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1294
           "Registering global variables from %s\n",
1295
                 style->doc->URL));
1296
  }
1297
#endif
1298
1299
0
  while (elem != NULL) {
1300
0
      xsltStackElemPtr def;
1301
1302
      /*
1303
       * Global variables are stored in the variables pool.
1304
       */
1305
0
      def = (xsltStackElemPtr)
1306
0
        xmlHashLookup2(ctxt->globalVars,
1307
0
                     elem->name, elem->nameURI);
1308
0
      if (def == NULL) {
1309
1310
0
    def = xsltCopyStackElem(elem);
1311
0
    if (xmlHashAddEntry2(ctxt->globalVars,
1312
0
             elem->name, elem->nameURI, def) < 0) {
1313
0
                    xmlGenericError(xmlGenericErrorContext,
1314
0
                                    "hash update failed\n");
1315
0
                    xsltFreeStackElem(def);
1316
0
                    return(-1);
1317
0
                }
1318
0
                def->next = head;
1319
0
                head = def;
1320
0
      } else if ((elem->comp != NULL) &&
1321
0
           (elem->comp->type == XSLT_FUNC_VARIABLE)) {
1322
    /*
1323
     * Redefinition of variables from a different stylesheet
1324
     * should not generate a message.
1325
     */
1326
0
    if ((elem->comp->inst != NULL) &&
1327
0
        (def->comp != NULL) && (def->comp->inst != NULL) &&
1328
0
        (elem->comp->inst->doc == def->comp->inst->doc))
1329
0
    {
1330
0
        xsltTransformError(ctxt, style, elem->comp->inst,
1331
0
      "Global variable %s already defined\n", elem->name);
1332
0
        if (style != NULL) style->errors++;
1333
0
    }
1334
0
      }
1335
0
      elem = elem->next;
1336
0
  }
1337
1338
0
  style = xsltNextImport(style);
1339
0
    }
1340
1341
    /*
1342
     * This part does the actual evaluation. Note that scanning the hash
1343
     * table would result in a non-deterministic order, leading to
1344
     * non-deterministic generated IDs.
1345
     */
1346
0
    elem = head;
1347
0
    while (elem != NULL) {
1348
0
        xsltStackElemPtr next;
1349
1350
0
        xsltEvalGlobalVariable(elem, ctxt);
1351
0
        next = elem->next;
1352
0
        elem->next = NULL;
1353
0
        elem = next;
1354
0
    }
1355
1356
0
    return(0);
1357
0
}
1358
1359
/**
1360
 * xsltRegisterGlobalVariable:
1361
 * @style:  the XSLT transformation context
1362
 * @name:  the variable name
1363
 * @ns_uri:  the variable namespace URI
1364
 * @sel:  the expression which need to be evaluated to generate a value
1365
 * @tree:  the subtree if sel is NULL
1366
 * @comp:  the precompiled value
1367
 * @value:  the string value if available
1368
 *
1369
 * Register a new variable value. If @value is NULL it unregisters
1370
 * the variable
1371
 *
1372
 * Returns 0 in case of success, -1 in case of error
1373
 */
1374
static int
1375
xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name,
1376
         const xmlChar *ns_uri, const xmlChar *sel,
1377
         xmlNodePtr tree, xsltStylePreCompPtr comp,
1378
0
         const xmlChar *value) {
1379
0
    xsltStackElemPtr elem, tmp;
1380
0
    if (style == NULL)
1381
0
  return(-1);
1382
0
    if (name == NULL)
1383
0
  return(-1);
1384
0
    if (comp == NULL)
1385
0
  return(-1);
1386
1387
#ifdef WITH_XSLT_DEBUG_VARIABLE
1388
    if (comp->type == XSLT_FUNC_PARAM)
1389
  xsltGenericDebug(xsltGenericDebugContext,
1390
       "Defining global param %s\n", name);
1391
    else
1392
  xsltGenericDebug(xsltGenericDebugContext,
1393
       "Defining global variable %s\n", name);
1394
#endif
1395
1396
0
    elem = xsltNewStackElem(NULL);
1397
0
    if (elem == NULL)
1398
0
  return(-1);
1399
0
    elem->comp = comp;
1400
0
    elem->name = xmlDictLookup(style->dict, name, -1);
1401
0
    elem->select = xmlDictLookup(style->dict, sel, -1);
1402
0
    if (ns_uri)
1403
0
  elem->nameURI = xmlDictLookup(style->dict, ns_uri, -1);
1404
0
    elem->tree = tree;
1405
0
    tmp = style->variables;
1406
0
    while (tmp != NULL) {
1407
0
        if ((elem->comp->type == XSLT_FUNC_VARIABLE) &&
1408
0
            (tmp->comp->type == XSLT_FUNC_VARIABLE) &&
1409
0
            (xmlStrEqual(elem->name, tmp->name)) &&
1410
0
            ((elem->nameURI == tmp->nameURI) ||
1411
0
             (xmlStrEqual(elem->nameURI, tmp->nameURI))))
1412
0
        {
1413
0
            xsltTransformError(NULL, style, comp->inst,
1414
0
            "redefinition of global variable %s\n", elem->name);
1415
0
            style->errors++;
1416
0
        }
1417
0
        tmp = tmp->next;
1418
0
    }
1419
0
    elem->next = style->variables;;
1420
0
    style->variables = elem;
1421
0
    if (value != NULL) {
1422
0
  elem->computed = 1;
1423
0
  elem->value = xmlXPathNewString(value);
1424
0
    }
1425
0
    return(0);
1426
0
}
1427
1428
/**
1429
 * xsltProcessUserParamInternal
1430
 *
1431
 * @ctxt:  the XSLT transformation context
1432
 * @name:  a null terminated parameter name
1433
 * @value: a null terminated value (may be an XPath expression)
1434
 * @eval:  0 to treat the value literally, else evaluate as XPath expression
1435
 *
1436
 * If @eval is 0 then @value is treated literally and is stored in the global
1437
 * parameter/variable table without any change.
1438
 *
1439
 * Uf @eval is 1 then @value is treated as an XPath expression and is
1440
 * evaluated.  In this case, if you want to pass a string which will be
1441
 * interpreted literally then it must be enclosed in single or double quotes.
1442
 * If the string contains single quotes (double quotes) then it cannot be
1443
 * enclosed single quotes (double quotes).  If the string which you want to
1444
 * be treated literally contains both single and double quotes (e.g. Meet
1445
 * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable
1446
 * quoting character.  You cannot use &apos; or &quot; inside the string
1447
 * because the replacement of character entities with their equivalents is
1448
 * done at a different stage of processing.  The solution is to call
1449
 * xsltQuoteUserParams or xsltQuoteOneUserParam.
1450
 *
1451
 * This needs to be done on parsed stylesheets before starting to apply
1452
 * transformations.  Normally this will be called (directly or indirectly)
1453
 * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams,
1454
 * or xsltQuoteOneUserParam.
1455
 *
1456
 * Returns 0 in case of success, -1 in case of error
1457
 */
1458
1459
static
1460
int
1461
xsltProcessUserParamInternal(xsltTransformContextPtr ctxt,
1462
                 const xmlChar * name,
1463
           const xmlChar * value,
1464
0
           int eval) {
1465
1466
0
    xsltStylesheetPtr style;
1467
0
    const xmlChar *prefix;
1468
0
    const xmlChar *href;
1469
0
    xmlXPathCompExprPtr xpExpr;
1470
0
    xmlXPathObjectPtr result;
1471
1472
0
    xsltStackElemPtr elem;
1473
0
    int res;
1474
0
    void *res_ptr;
1475
1476
0
    if (ctxt == NULL)
1477
0
  return(-1);
1478
0
    if (name == NULL)
1479
0
  return(0);
1480
0
    if (value == NULL)
1481
0
  return(0);
1482
1483
0
    style = ctxt->style;
1484
1485
#ifdef WITH_XSLT_DEBUG_VARIABLE
1486
    XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1487
      "Evaluating user parameter %s=%s\n", name, value));
1488
#endif
1489
1490
    /*
1491
     * Name lookup
1492
     */
1493
0
    href = NULL;
1494
1495
0
    if (name[0] == '{') {
1496
0
        int len = 0;
1497
1498
0
        while ((name[len] != 0) && (name[len] != '}')) len++;
1499
0
        if (name[len] == 0) {
1500
0
           xsltTransformError(ctxt, style, NULL,
1501
0
           "user param : malformed parameter name : %s\n", name);
1502
0
        } else {
1503
0
           href = xmlDictLookup(ctxt->dict, &name[1], len-1);
1504
0
           name = xmlDictLookup(ctxt->dict, &name[len + 1], -1);
1505
0
       }
1506
0
    }
1507
0
    else {
1508
0
        name = xsltSplitQName(ctxt->dict, name, &prefix);
1509
0
        if (prefix != NULL) {
1510
0
            xmlNsPtr ns;
1511
1512
0
            ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc),
1513
0
                             prefix);
1514
0
            if (ns == NULL) {
1515
0
                xsltTransformError(ctxt, style, NULL,
1516
0
                "user param : no namespace bound to prefix %s\n", prefix);
1517
0
                href = NULL;
1518
0
            } else {
1519
0
                href = ns->href;
1520
0
            }
1521
0
        }
1522
0
    }
1523
1524
0
    if (name == NULL)
1525
0
  return (-1);
1526
1527
0
    res_ptr = xmlHashLookup2(ctxt->globalVars, name, href);
1528
0
    if (res_ptr != 0) {
1529
0
  xsltTransformError(ctxt, style, NULL,
1530
0
      "Global parameter %s already defined\n", name);
1531
0
    }
1532
0
    if (ctxt->globalVars == NULL)
1533
0
  ctxt->globalVars = xmlHashCreate(20);
1534
1535
    /*
1536
     * do not overwrite variables with parameters from the command line
1537
     */
1538
0
    while (style != NULL) {
1539
0
        elem = ctxt->style->variables;
1540
0
  while (elem != NULL) {
1541
0
      if ((elem->comp != NULL) &&
1542
0
          (elem->comp->type == XSLT_FUNC_VARIABLE) &&
1543
0
    (xmlStrEqual(elem->name, name)) &&
1544
0
    (xmlStrEqual(elem->nameURI, href))) {
1545
0
    return(0);
1546
0
      }
1547
0
            elem = elem->next;
1548
0
  }
1549
0
        style = xsltNextImport(style);
1550
0
    }
1551
0
    style = ctxt->style;
1552
0
    elem = NULL;
1553
1554
    /*
1555
     * Do the evaluation if @eval is non-zero.
1556
     */
1557
1558
0
    result = NULL;
1559
0
    if (eval != 0) {
1560
0
        xpExpr = xmlXPathCtxtCompile(ctxt->xpathCtxt, value);
1561
0
  if (xpExpr != NULL) {
1562
0
      xmlDocPtr oldXPDoc;
1563
0
      xmlNodePtr oldXPContextNode;
1564
0
      int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
1565
0
      xmlNsPtr *oldXPNamespaces;
1566
0
      xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
1567
1568
      /*
1569
      * Save context states.
1570
      */
1571
0
      oldXPDoc = xpctxt->doc;
1572
0
      oldXPContextNode = xpctxt->node;
1573
0
      oldXPProximityPosition = xpctxt->proximityPosition;
1574
0
      oldXPContextSize = xpctxt->contextSize;
1575
0
      oldXPNamespaces = xpctxt->namespaces;
1576
0
      oldXPNsNr = xpctxt->nsNr;
1577
1578
      /*
1579
      * SPEC XSLT 1.0:
1580
      * "At top-level, the expression or template specifying the
1581
      *  variable value is evaluated with the same context as that used
1582
      *  to process the root node of the source document: the current
1583
      *  node is the root node of the source document and the current
1584
      *  node list is a list containing just the root node of the source
1585
      *  document."
1586
      */
1587
0
      xpctxt->doc = ctxt->initialContextDoc;
1588
0
      xpctxt->node = ctxt->initialContextNode;
1589
0
      xpctxt->contextSize = 1;
1590
0
      xpctxt->proximityPosition = 1;
1591
      /*
1592
      * There is really no in scope namespace for parameters on the
1593
      * command line.
1594
      */
1595
0
      xpctxt->namespaces = NULL;
1596
0
      xpctxt->nsNr = 0;
1597
1598
0
      result = xmlXPathCompiledEval(xpExpr, xpctxt);
1599
1600
      /*
1601
      * Restore Context states.
1602
      */
1603
0
      xpctxt->doc = oldXPDoc;
1604
0
      xpctxt->node = oldXPContextNode;
1605
0
      xpctxt->contextSize = oldXPContextSize;
1606
0
      xpctxt->proximityPosition = oldXPProximityPosition;
1607
0
      xpctxt->namespaces = oldXPNamespaces;
1608
0
      xpctxt->nsNr = oldXPNsNr;
1609
1610
0
      xmlXPathFreeCompExpr(xpExpr);
1611
0
  }
1612
0
  if (result == NULL) {
1613
0
      xsltTransformError(ctxt, style, NULL,
1614
0
    "Evaluating user parameter %s failed\n", name);
1615
0
      ctxt->state = XSLT_STATE_STOPPED;
1616
0
      return(-1);
1617
0
  }
1618
0
    }
1619
1620
    /*
1621
     * If @eval is 0 then @value is to be taken literally and result is NULL
1622
     *
1623
     * If @eval is not 0, then @value is an XPath expression and has been
1624
     * successfully evaluated and result contains the resulting value and
1625
     * is not NULL.
1626
     *
1627
     * Now create an xsltStackElemPtr for insertion into the context's
1628
     * global variable/parameter hash table.
1629
     */
1630
1631
#ifdef WITH_XSLT_DEBUG_VARIABLE
1632
#ifdef LIBXML_DEBUG_ENABLED
1633
    if ((xsltGenericDebugContext == stdout) ||
1634
        (xsltGenericDebugContext == stderr))
1635
      xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
1636
            result, 0);
1637
#endif
1638
#endif
1639
1640
0
    elem = xsltNewStackElem(NULL);
1641
0
    if (elem != NULL) {
1642
0
  elem->name = name;
1643
0
  elem->select = xmlDictLookup(ctxt->dict, value, -1);
1644
0
  if (href != NULL)
1645
0
      elem->nameURI = xmlDictLookup(ctxt->dict, href, -1);
1646
0
  elem->tree = NULL;
1647
0
  elem->computed = 1;
1648
0
  if (eval == 0) {
1649
0
      elem->value = xmlXPathNewString(value);
1650
0
  }
1651
0
  else {
1652
0
      elem->value = result;
1653
0
  }
1654
0
    }
1655
1656
    /*
1657
     * Global parameters are stored in the XPath context variables pool.
1658
     */
1659
1660
0
    res = xmlHashAddEntry2(ctxt->globalVars, name, href, elem);
1661
0
    if (res != 0) {
1662
0
  xsltFreeStackElem(elem);
1663
0
  xsltTransformError(ctxt, style, NULL,
1664
0
      "Global parameter %s already defined\n", name);
1665
0
    }
1666
0
    return(0);
1667
0
}
1668
1669
/**
1670
 * xsltEvalUserParams:
1671
 *
1672
 * @ctxt:  the XSLT transformation context
1673
 * @params:  a NULL terminated array of parameters name/value tuples
1674
 *
1675
 * Evaluate the global variables of a stylesheet. This needs to be
1676
 * done on parsed stylesheets before starting to apply transformations.
1677
 * Each of the parameters is evaluated as an XPath expression and stored
1678
 * in the global variables/parameter hash table.  If you want your
1679
 * parameter used literally, use xsltQuoteUserParams.
1680
 *
1681
 * Returns 0 in case of success, -1 in case of error
1682
 */
1683
1684
int
1685
0
xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) {
1686
0
    size_t indx = 0;
1687
0
    const xmlChar *name;
1688
0
    const xmlChar *value;
1689
1690
0
    if (params == NULL)
1691
0
  return(0);
1692
0
    while (params[indx] != NULL) {
1693
0
  name = (const xmlChar *) params[indx++];
1694
0
  value = (const xmlChar *) params[indx++];
1695
0
  if (xsltEvalOneUserParam(ctxt, name, value) != 0)
1696
0
      return(-1);
1697
0
    }
1698
0
    return 0;
1699
0
}
1700
1701
/**
1702
 * xsltQuoteUserParams:
1703
 *
1704
 * @ctxt:  the XSLT transformation context
1705
 * @params:  a NULL terminated arry of parameters names/values tuples
1706
 *
1707
 * Similar to xsltEvalUserParams, but the values are treated literally and
1708
 * are * *not* evaluated as XPath expressions. This should be done on parsed
1709
 * stylesheets before starting to apply transformations.
1710
 *
1711
 * Returns 0 in case of success, -1 in case of error.
1712
 */
1713
1714
int
1715
0
xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) {
1716
0
    size_t indx = 0;
1717
0
    const xmlChar *name;
1718
0
    const xmlChar *value;
1719
1720
0
    if (params == NULL)
1721
0
  return(0);
1722
0
    while (params[indx] != NULL) {
1723
0
  name = (const xmlChar *) params[indx++];
1724
0
  value = (const xmlChar *) params[indx++];
1725
0
  if (xsltQuoteOneUserParam(ctxt, name, value) != 0)
1726
0
      return(-1);
1727
0
    }
1728
0
    return 0;
1729
0
}
1730
1731
/**
1732
 * xsltEvalOneUserParam:
1733
 * @ctxt:  the XSLT transformation context
1734
 * @name:  a null terminated string giving the name of the parameter
1735
 * @value:  a null terminated string giving the XPath expression to be evaluated
1736
 *
1737
 * This is normally called from xsltEvalUserParams to process a single
1738
 * parameter from a list of parameters.  The @value is evaluated as an
1739
 * XPath expression and the result is stored in the context's global
1740
 * variable/parameter hash table.
1741
 *
1742
 * To have a parameter treated literally (not as an XPath expression)
1743
 * use xsltQuoteUserParams (or xsltQuoteOneUserParam).  For more
1744
 * details see description of xsltProcessOneUserParamInternal.
1745
 *
1746
 * Returns 0 in case of success, -1 in case of error.
1747
 */
1748
1749
int
1750
xsltEvalOneUserParam(xsltTransformContextPtr ctxt,
1751
         const xmlChar * name,
1752
0
         const xmlChar * value) {
1753
0
    return xsltProcessUserParamInternal(ctxt, name, value,
1754
0
                            1 /* xpath eval ? */);
1755
0
}
1756
1757
/**
1758
 * xsltQuoteOneUserParam:
1759
 * @ctxt:  the XSLT transformation context
1760
 * @name:  a null terminated string giving the name of the parameter
1761
 * @value:  a null terminated string giving the parameter value
1762
 *
1763
 * This is normally called from xsltQuoteUserParams to process a single
1764
 * parameter from a list of parameters.  The @value is stored in the
1765
 * context's global variable/parameter hash table.
1766
 *
1767
 * Returns 0 in case of success, -1 in case of error.
1768
 */
1769
1770
int
1771
xsltQuoteOneUserParam(xsltTransformContextPtr ctxt,
1772
       const xmlChar * name,
1773
0
       const xmlChar * value) {
1774
0
    return xsltProcessUserParamInternal(ctxt, name, value,
1775
0
          0 /* xpath eval ? */);
1776
0
}
1777
1778
/**
1779
 * xsltBuildVariable:
1780
 * @ctxt:  the XSLT transformation context
1781
 * @comp:  the precompiled form
1782
 * @tree:  the tree if select is NULL
1783
 *
1784
 * Computes a new variable value.
1785
 *
1786
 * Returns the xsltStackElemPtr or NULL in case of error
1787
 */
1788
static xsltStackElemPtr
1789
xsltBuildVariable(xsltTransformContextPtr ctxt,
1790
      xsltStylePreCompPtr castedComp,
1791
      xmlNodePtr tree)
1792
0
{
1793
#ifdef XSLT_REFACTORED
1794
    xsltStyleBasicItemVariablePtr comp =
1795
  (xsltStyleBasicItemVariablePtr) castedComp;
1796
#else
1797
0
    xsltStylePreCompPtr comp = castedComp;
1798
0
#endif
1799
0
    xsltStackElemPtr elem;
1800
1801
#ifdef WITH_XSLT_DEBUG_VARIABLE
1802
    XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1803
         "Building variable %s", comp->name));
1804
    if (comp->select != NULL)
1805
  XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1806
       " select %s", comp->select));
1807
    XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, "\n"));
1808
#endif
1809
1810
0
    elem = xsltNewStackElem(ctxt);
1811
0
    if (elem == NULL)
1812
0
  return(NULL);
1813
0
    elem->comp = (xsltStylePreCompPtr) comp;
1814
0
    elem->name = comp->name;
1815
0
    elem->select = comp->select;
1816
0
    elem->nameURI = comp->ns;
1817
0
    elem->tree = tree;
1818
0
    elem->value = xsltEvalVariable(ctxt, elem,
1819
0
  (xsltStylePreCompPtr) comp);
1820
0
    elem->computed = 1;
1821
0
    return(elem);
1822
0
}
1823
1824
/**
1825
 * xsltRegisterVariable:
1826
 * @ctxt:  the XSLT transformation context
1827
 * @comp: the compiled XSLT-variable (or param) instruction
1828
 * @tree:  the tree if select is NULL
1829
 * @isParam:  indicates if this is a parameter
1830
 *
1831
 * Computes and registers a new variable.
1832
 *
1833
 * Returns 0 in case of success, -1 in case of error
1834
 */
1835
static int
1836
xsltRegisterVariable(xsltTransformContextPtr ctxt,
1837
         xsltStylePreCompPtr castedComp,
1838
         xmlNodePtr tree, int isParam)
1839
0
{
1840
#ifdef XSLT_REFACTORED
1841
    xsltStyleBasicItemVariablePtr comp =
1842
  (xsltStyleBasicItemVariablePtr) castedComp;
1843
#else
1844
0
    xsltStylePreCompPtr comp = castedComp;
1845
0
    int present;
1846
0
#endif
1847
0
    xsltStackElemPtr variable;
1848
1849
#ifdef XSLT_REFACTORED
1850
    /*
1851
    * REFACTORED NOTE: Redefinitions of vars/params are checked
1852
    *  at compilation time in the refactored code.
1853
    * xsl:with-param parameters are checked in xsltApplyXSLTTemplate().
1854
    */
1855
#else
1856
0
    present = xsltCheckStackElem(ctxt, comp->name, comp->ns);
1857
0
    if (isParam == 0) {
1858
0
  if ((present != 0) && (present != 3)) {
1859
      /* TODO: report QName. */
1860
0
      xsltTransformError(ctxt, NULL, comp->inst,
1861
0
    "XSLT-variable: Redefinition of variable '%s'.\n", comp->name);
1862
0
      return(0);
1863
0
  }
1864
0
    } else if (present != 0) {
1865
0
  if ((present == 1) || (present == 2)) {
1866
      /* TODO: report QName. */
1867
0
      xsltTransformError(ctxt, NULL, comp->inst,
1868
0
    "XSLT-param: Redefinition of parameter '%s'.\n", comp->name);
1869
0
      return(0);
1870
0
  }
1871
#ifdef WITH_XSLT_DEBUG_VARIABLE
1872
  XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1873
     "param %s defined by caller\n", comp->name));
1874
#endif
1875
0
  return(0);
1876
0
    }
1877
0
#endif /* else of XSLT_REFACTORED */
1878
1879
0
    variable = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree);
1880
0
    if (xsltAddStackElem(ctxt, variable) < 0) {
1881
0
        xsltFreeStackElem(variable);
1882
0
        return(-1);
1883
0
    }
1884
0
    return(0);
1885
0
}
1886
1887
/**
1888
 * xsltGlobalVariableLookup:
1889
 * @ctxt:  the XSLT transformation context
1890
 * @name:  the variable name
1891
 * @ns_uri:  the variable namespace URI
1892
 *
1893
 * Search in the Variable array of the context for the given
1894
 * variable value.
1895
 *
1896
 * Returns the value or NULL if not found
1897
 */
1898
static xmlXPathObjectPtr
1899
xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
1900
0
             const xmlChar *ns_uri) {
1901
0
    xsltStackElemPtr elem;
1902
0
    xmlXPathObjectPtr ret = NULL;
1903
1904
    /*
1905
     * Lookup the global variables in XPath global variable hash table
1906
     */
1907
0
    if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL))
1908
0
  return(NULL);
1909
0
    elem = (xsltStackElemPtr)
1910
0
      xmlHashLookup2(ctxt->globalVars, name, ns_uri);
1911
0
    if (elem == NULL) {
1912
#ifdef WITH_XSLT_DEBUG_VARIABLE
1913
  XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1914
       "global variable not found %s\n", name));
1915
#endif
1916
0
  return(NULL);
1917
0
    }
1918
    /*
1919
    * URGENT TODO: Move the detection of recursive definitions
1920
    * to compile-time.
1921
    */
1922
0
    if (elem->computed == 0) {
1923
0
  if (elem->name == xsltComputingGlobalVarMarker) {
1924
0
      xsltTransformError(ctxt, NULL, elem->comp->inst,
1925
0
    "Recursive definition of %s\n", name);
1926
0
      return(NULL);
1927
0
  }
1928
0
  ret = xsltEvalGlobalVariable(elem, ctxt);
1929
0
    } else
1930
0
  ret = elem->value;
1931
0
    return(xmlXPathObjectCopy(ret));
1932
0
}
1933
1934
/**
1935
 * xsltVariableLookup:
1936
 * @ctxt:  the XSLT transformation context
1937
 * @name:  the variable name
1938
 * @ns_uri:  the variable namespace URI
1939
 *
1940
 * Search in the Variable array of the context for the given
1941
 * variable value.
1942
 *
1943
 * Returns the value or NULL if not found
1944
 */
1945
xmlXPathObjectPtr
1946
xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
1947
0
       const xmlChar *ns_uri) {
1948
0
    xsltStackElemPtr elem;
1949
1950
0
    if (ctxt == NULL)
1951
0
  return(NULL);
1952
1953
0
    elem = xsltStackLookup(ctxt, name, ns_uri);
1954
0
    if (elem == NULL) {
1955
0
  return(xsltGlobalVariableLookup(ctxt, name, ns_uri));
1956
0
    }
1957
0
    if (elem->computed == 0) {
1958
#ifdef WITH_XSLT_DEBUG_VARIABLE
1959
  XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1960
             "uncomputed variable %s\n", name));
1961
#endif
1962
0
        elem->value = xsltEvalVariable(ctxt, elem, NULL);
1963
0
  elem->computed = 1;
1964
0
    }
1965
0
    if (elem->value != NULL)
1966
0
  return(xmlXPathObjectCopy(elem->value));
1967
#ifdef WITH_XSLT_DEBUG_VARIABLE
1968
    XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1969
         "variable not found %s\n", name));
1970
#endif
1971
0
    return(NULL);
1972
0
}
1973
1974
/**
1975
 * xsltParseStylesheetCallerParam:
1976
 * @ctxt:  the XSLT transformation context
1977
 * @inst:  the xsl:with-param instruction element
1978
 *
1979
 * Processes an xsl:with-param instruction at transformation time.
1980
 * The value is computed, but not recorded.
1981
 * NOTE that this is also called with an *xsl:param* element
1982
 * from exsltFuncFunctionFunction().
1983
 *
1984
 * Returns the new xsltStackElemPtr or NULL
1985
 */
1986
1987
xsltStackElemPtr
1988
xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr inst)
1989
0
{
1990
#ifdef XSLT_REFACTORED
1991
    xsltStyleBasicItemVariablePtr comp;
1992
#else
1993
0
    xsltStylePreCompPtr comp;
1994
0
#endif
1995
0
    xmlNodePtr tree = NULL; /* The first child node of the instruction or
1996
                               the instruction itself. */
1997
0
    xsltStackElemPtr param = NULL;
1998
1999
0
    if ((ctxt == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
2000
0
  return(NULL);
2001
2002
#ifdef XSLT_REFACTORED
2003
    comp = (xsltStyleBasicItemVariablePtr) inst->psvi;
2004
#else
2005
0
    comp = (xsltStylePreCompPtr) inst->psvi;
2006
0
#endif
2007
2008
0
    if (comp == NULL) {
2009
0
        xsltTransformError(ctxt, NULL, inst,
2010
0
      "Internal error in xsltParseStylesheetCallerParam(): "
2011
0
      "The XSLT 'with-param' instruction was not compiled.\n");
2012
0
        return(NULL);
2013
0
    }
2014
0
    if (comp->name == NULL) {
2015
0
  xsltTransformError(ctxt, NULL, inst,
2016
0
      "Internal error in xsltParseStylesheetCallerParam(): "
2017
0
      "XSLT 'with-param': The attribute 'name' was not compiled.\n");
2018
0
  return(NULL);
2019
0
    }
2020
2021
#ifdef WITH_XSLT_DEBUG_VARIABLE
2022
    XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2023
      "Handling xsl:with-param %s\n", comp->name));
2024
#endif
2025
2026
0
    if (comp->select == NULL) {
2027
0
  tree = inst->children;
2028
0
    } else {
2029
#ifdef WITH_XSLT_DEBUG_VARIABLE
2030
  XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2031
      "        select %s\n", comp->select));
2032
#endif
2033
0
  tree = inst;
2034
0
    }
2035
2036
0
    param = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree);
2037
2038
0
    return(param);
2039
0
}
2040
2041
/**
2042
 * xsltParseGlobalVariable:
2043
 * @style:  the XSLT stylesheet
2044
 * @cur:  the "variable" element
2045
 *
2046
 * Parses a global XSLT 'variable' declaration at compilation time
2047
 * and registers it
2048
 */
2049
void
2050
xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur)
2051
0
{
2052
#ifdef XSLT_REFACTORED
2053
    xsltStyleItemVariablePtr comp;
2054
#else
2055
0
    xsltStylePreCompPtr comp;
2056
0
#endif
2057
2058
0
    if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
2059
0
  return;
2060
2061
#ifdef XSLT_REFACTORED
2062
    /*
2063
    * Note that xsltStylePreCompute() will be called from
2064
    * xslt.c only.
2065
    */
2066
    comp = (xsltStyleItemVariablePtr) cur->psvi;
2067
#else
2068
0
    xsltStylePreCompute(style, cur);
2069
0
    comp = (xsltStylePreCompPtr) cur->psvi;
2070
0
#endif
2071
0
    if (comp == NULL) {
2072
0
  xsltTransformError(NULL, style, cur,
2073
0
       "xsl:variable : compilation failed\n");
2074
0
  return;
2075
0
    }
2076
2077
0
    if (comp->name == NULL) {
2078
0
  xsltTransformError(NULL, style, cur,
2079
0
      "xsl:variable : missing name attribute\n");
2080
0
  return;
2081
0
    }
2082
2083
    /*
2084
    * Parse the content (a sequence constructor) of xsl:variable.
2085
    */
2086
0
    if (cur->children != NULL) {
2087
#ifdef XSLT_REFACTORED
2088
        xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children);
2089
#else
2090
0
        xsltParseTemplateContent(style, cur);
2091
0
#endif
2092
0
    }
2093
#ifdef WITH_XSLT_DEBUG_VARIABLE
2094
    xsltGenericDebug(xsltGenericDebugContext,
2095
  "Registering global variable %s\n", comp->name);
2096
#endif
2097
2098
0
    xsltRegisterGlobalVariable(style, comp->name, comp->ns,
2099
0
  comp->select, cur->children, (xsltStylePreCompPtr) comp,
2100
0
  NULL);
2101
0
}
2102
2103
/**
2104
 * xsltParseGlobalParam:
2105
 * @style:  the XSLT stylesheet
2106
 * @cur:  the "param" element
2107
 *
2108
 * parse an XSLT transformation param declaration and record
2109
 * its value.
2110
 */
2111
2112
void
2113
0
xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
2114
#ifdef XSLT_REFACTORED
2115
    xsltStyleItemParamPtr comp;
2116
#else
2117
0
    xsltStylePreCompPtr comp;
2118
0
#endif
2119
2120
0
    if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
2121
0
  return;
2122
2123
#ifdef XSLT_REFACTORED
2124
    /*
2125
    * Note that xsltStylePreCompute() will be called from
2126
    * xslt.c only.
2127
    */
2128
    comp = (xsltStyleItemParamPtr) cur->psvi;
2129
#else
2130
0
    xsltStylePreCompute(style, cur);
2131
0
    comp = (xsltStylePreCompPtr) cur->psvi;
2132
0
#endif
2133
0
    if (comp == NULL) {
2134
0
  xsltTransformError(NULL, style, cur,
2135
0
       "xsl:param : compilation failed\n");
2136
0
  return;
2137
0
    }
2138
2139
0
    if (comp->name == NULL) {
2140
0
  xsltTransformError(NULL, style, cur,
2141
0
      "xsl:param : missing name attribute\n");
2142
0
  return;
2143
0
    }
2144
2145
    /*
2146
    * Parse the content (a sequence constructor) of xsl:param.
2147
    */
2148
0
    if (cur->children != NULL) {
2149
#ifdef XSLT_REFACTORED
2150
        xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children);
2151
#else
2152
0
        xsltParseTemplateContent(style, cur);
2153
0
#endif
2154
0
    }
2155
2156
#ifdef WITH_XSLT_DEBUG_VARIABLE
2157
    xsltGenericDebug(xsltGenericDebugContext,
2158
  "Registering global param %s\n", comp->name);
2159
#endif
2160
2161
0
    xsltRegisterGlobalVariable(style, comp->name, comp->ns,
2162
0
  comp->select, cur->children, (xsltStylePreCompPtr) comp,
2163
0
  NULL);
2164
0
}
2165
2166
/**
2167
 * xsltParseStylesheetVariable:
2168
 * @ctxt:  the XSLT transformation context
2169
 * @inst:  the xsl:variable instruction element
2170
 *
2171
 * Registers a local XSLT 'variable' instruction at transformation time
2172
 * and evaluates its value.
2173
 */
2174
void
2175
xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr inst)
2176
0
{
2177
#ifdef XSLT_REFACTORED
2178
    xsltStyleItemVariablePtr comp;
2179
#else
2180
0
    xsltStylePreCompPtr comp;
2181
0
#endif
2182
2183
0
    if ((inst == NULL) || (ctxt == NULL) || (inst->type != XML_ELEMENT_NODE))
2184
0
  return;
2185
2186
0
    comp = inst->psvi;
2187
0
    if (comp == NULL) {
2188
0
        xsltTransformError(ctxt, NULL, inst,
2189
0
      "Internal error in xsltParseStylesheetVariable(): "
2190
0
      "The XSLT 'variable' instruction was not compiled.\n");
2191
0
        return;
2192
0
    }
2193
0
    if (comp->name == NULL) {
2194
0
  xsltTransformError(ctxt, NULL, inst,
2195
0
      "Internal error in xsltParseStylesheetVariable(): "
2196
0
      "The attribute 'name' was not compiled.\n");
2197
0
  return;
2198
0
    }
2199
2200
#ifdef WITH_XSLT_DEBUG_VARIABLE
2201
    XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2202
  "Registering variable '%s'\n", comp->name));
2203
#endif
2204
2205
0
    xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, inst->children, 0);
2206
0
}
2207
2208
/**
2209
 * xsltParseStylesheetParam:
2210
 * @ctxt:  the XSLT transformation context
2211
 * @cur:  the XSLT 'param' element
2212
 *
2213
 * Registers a local XSLT 'param' declaration at transformation time and
2214
 * evaluates its value.
2215
 */
2216
void
2217
xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur)
2218
0
{
2219
#ifdef XSLT_REFACTORED
2220
    xsltStyleItemParamPtr comp;
2221
#else
2222
0
    xsltStylePreCompPtr comp;
2223
0
#endif
2224
2225
0
    if ((cur == NULL) || (ctxt == NULL) || (cur->type != XML_ELEMENT_NODE))
2226
0
  return;
2227
2228
0
    comp = cur->psvi;
2229
0
    if ((comp == NULL) || (comp->name == NULL)) {
2230
0
  xsltTransformError(ctxt, NULL, cur,
2231
0
      "Internal error in xsltParseStylesheetParam(): "
2232
0
      "The XSLT 'param' declaration was not compiled correctly.\n");
2233
0
  return;
2234
0
    }
2235
2236
#ifdef WITH_XSLT_DEBUG_VARIABLE
2237
    XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2238
  "Registering param %s\n", comp->name));
2239
#endif
2240
2241
0
    xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, cur->children, 1);
2242
0
}
2243
2244
/**
2245
 * xsltFreeGlobalVariables:
2246
 * @ctxt:  the XSLT transformation context
2247
 *
2248
 * Free up the data associated to the global variables
2249
 * its value.
2250
 */
2251
2252
void
2253
0
xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) {
2254
0
    xmlHashFree(ctxt->globalVars, xsltFreeStackElemEntry);
2255
0
}
2256
2257
/**
2258
 * xsltXPathVariableLookup:
2259
 * @ctxt:  a void * but the the XSLT transformation context actually
2260
 * @name:  the variable name
2261
 * @ns_uri:  the variable namespace URI
2262
 *
2263
 * This is the entry point when a varibale is needed by the XPath
2264
 * interpretor.
2265
 *
2266
 * Returns the value or NULL if not found
2267
 */
2268
xmlXPathObjectPtr
2269
xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
2270
0
                  const xmlChar *ns_uri) {
2271
0
    xsltTransformContextPtr tctxt;
2272
0
    xmlXPathObjectPtr valueObj = NULL;
2273
2274
0
    if ((ctxt == NULL) || (name == NULL))
2275
0
  return(NULL);
2276
2277
#ifdef WITH_XSLT_DEBUG_VARIABLE
2278
    XSLT_TRACE(((xsltTransformContextPtr)ctxt),XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2279
      "Lookup variable '%s'\n", name));
2280
#endif
2281
2282
0
    tctxt = (xsltTransformContextPtr) ctxt;
2283
    /*
2284
    * Local variables/params ---------------------------------------------
2285
    *
2286
    * Do the lookup from the top of the stack, but
2287
    * don't use params being computed in a call-param
2288
    * First lookup expects the variable name and URI to
2289
    * come from the disctionnary and hence pointer comparison.
2290
    */
2291
0
    if (tctxt->varsNr != 0) {
2292
0
  int i;
2293
0
  xsltStackElemPtr variable = NULL, cur;
2294
2295
0
  for (i = tctxt->varsNr; i > tctxt->varsBase; i--) {
2296
0
      cur = tctxt->varsTab[i-1];
2297
0
      if ((cur->name == name) && (cur->nameURI == ns_uri)) {
2298
0
    variable = cur;
2299
0
    goto local_variable_found;
2300
0
      }
2301
0
      cur = cur->next;
2302
0
  }
2303
  /*
2304
  * Redo the lookup with interned strings to avoid string comparison.
2305
  *
2306
  * OPTIMIZE TODO: The problem here is, that if we request a
2307
  *  global variable, then this will be also executed.
2308
  */
2309
0
  {
2310
0
      const xmlChar *tmpName = name, *tmpNsName = ns_uri;
2311
2312
0
      name = xmlDictLookup(tctxt->dict, name, -1);
2313
0
      if (ns_uri)
2314
0
    ns_uri = xmlDictLookup(tctxt->dict, ns_uri, -1);
2315
0
      if ((tmpName != name) || (tmpNsName != ns_uri)) {
2316
0
    for (i = tctxt->varsNr; i > tctxt->varsBase; i--) {
2317
0
        cur = tctxt->varsTab[i-1];
2318
0
        if ((cur->name == name) && (cur->nameURI == ns_uri)) {
2319
0
      variable = cur;
2320
0
      goto local_variable_found;
2321
0
        }
2322
0
    }
2323
0
      }
2324
0
  }
2325
2326
0
local_variable_found:
2327
2328
0
  if (variable) {
2329
0
      if (variable->computed == 0) {
2330
2331
#ifdef WITH_XSLT_DEBUG_VARIABLE
2332
    XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2333
        "uncomputed variable '%s'\n", name));
2334
#endif
2335
0
    variable->value = xsltEvalVariable(tctxt, variable, NULL);
2336
0
    variable->computed = 1;
2337
0
      }
2338
0
      if (variable->value != NULL) {
2339
0
    valueObj = xmlXPathObjectCopy(variable->value);
2340
0
      }
2341
0
      return(valueObj);
2342
0
  }
2343
0
    }
2344
    /*
2345
    * Global variables/params --------------------------------------------
2346
    */
2347
0
    if (tctxt->globalVars) {
2348
0
  valueObj = xsltGlobalVariableLookup(tctxt, name, ns_uri);
2349
0
    }
2350
2351
0
    if (valueObj == NULL) {
2352
2353
#ifdef WITH_XSLT_DEBUG_VARIABLE
2354
    XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2355
         "variable not found '%s'\n", name));
2356
#endif
2357
2358
0
  if (ns_uri) {
2359
0
      xsltTransformError(tctxt, NULL, tctxt->inst,
2360
0
    "Variable '{%s}%s' has not been declared.\n", ns_uri, name);
2361
0
  } else {
2362
0
      xsltTransformError(tctxt, NULL, tctxt->inst,
2363
0
    "Variable '%s' has not been declared.\n", name);
2364
0
  }
2365
0
    } else {
2366
2367
#ifdef WITH_XSLT_DEBUG_VARIABLE
2368
  XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2369
      "found variable '%s'\n", name));
2370
#endif
2371
0
    }
2372
2373
0
    return(valueObj);
2374
0
}
2375
2376