Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/libxslt/libxslt/templates.c
Line
Count
Source
1
/*
2
 * templates.c: Implementation of the template processing
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/globals.h>
19
#include <libxml/xmlerror.h>
20
#include <libxml/tree.h>
21
#include <libxml/dict.h>
22
#include <libxml/xpathInternals.h>
23
#include <libxml/parserInternals.h>
24
#include "xslt.h"
25
#include "xsltInternals.h"
26
#include "xsltutils.h"
27
#include "variables.h"
28
#include "functions.h"
29
#include "templates.h"
30
#include "transform.h"
31
#include "namespaces.h"
32
#include "attributes.h"
33
34
#ifdef WITH_XSLT_DEBUG
35
#define WITH_XSLT_DEBUG_TEMPLATES
36
#endif
37
38
/************************************************************************
39
 *                  *
40
 *      Module interfaces       *
41
 *                  *
42
 ************************************************************************/
43
44
/**
45
 * xsltEvalXPathPredicate:
46
 * @ctxt:  the XSLT transformation context
47
 * @comp:  the XPath compiled expression
48
 * @nsList:  the namespaces in scope
49
 * @nsNr:  the number of namespaces in scope
50
 *
51
 * Process the expression using XPath and evaluate the result as
52
 * an XPath predicate
53
 *
54
 * Returns 1 is the predicate was true, 0 otherwise
55
 */
56
int
57
xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
58
0
           xmlNsPtr *nsList, int nsNr) {
59
0
    int ret;
60
0
    xmlXPathObjectPtr res;
61
0
    int oldNsNr;
62
0
    xmlNsPtr *oldNamespaces;
63
0
    xmlNodePtr oldInst;
64
0
    xmlNodePtr oldNode;
65
0
    int oldProximityPosition, oldContextSize;
66
67
0
    if ((ctxt == NULL) || (ctxt->inst == NULL)) {
68
0
        xsltTransformError(ctxt, NULL, NULL,
69
0
            "xsltEvalXPathPredicate: No context or instruction\n");
70
0
        return(0);
71
0
    }
72
73
0
    oldNode = ctxt->xpathCtxt->node;
74
0
    oldContextSize = ctxt->xpathCtxt->contextSize;
75
0
    oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
76
0
    oldNsNr = ctxt->xpathCtxt->nsNr;
77
0
    oldNamespaces = ctxt->xpathCtxt->namespaces;
78
0
    oldInst = ctxt->inst;
79
80
0
    ctxt->xpathCtxt->node = ctxt->node;
81
0
    ctxt->xpathCtxt->namespaces = nsList;
82
0
    ctxt->xpathCtxt->nsNr = nsNr;
83
84
0
    res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
85
86
0
    if (res != NULL) {
87
0
  ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res);
88
0
  xmlXPathFreeObject(res);
89
#ifdef WITH_XSLT_DEBUG_TEMPLATES
90
  XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
91
       "xsltEvalXPathPredicate: returns %d\n", ret));
92
#endif
93
0
    } else {
94
#ifdef WITH_XSLT_DEBUG_TEMPLATES
95
  XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
96
       "xsltEvalXPathPredicate: failed\n"));
97
#endif
98
0
  ctxt->state = XSLT_STATE_STOPPED;
99
0
  ret = 0;
100
0
    }
101
102
0
    ctxt->xpathCtxt->node = oldNode;
103
0
    ctxt->xpathCtxt->nsNr = oldNsNr;
104
0
    ctxt->xpathCtxt->namespaces = oldNamespaces;
105
0
    ctxt->inst = oldInst;
106
0
    ctxt->xpathCtxt->contextSize = oldContextSize;
107
0
    ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
108
109
0
    return(ret);
110
0
}
111
112
/**
113
 * xsltEvalXPathStringNs:
114
 * @ctxt:  the XSLT transformation context
115
 * @comp:  the compiled XPath expression
116
 * @nsNr:  the number of namespaces in the list
117
 * @nsList:  the list of in-scope namespaces to use
118
 *
119
 * Process the expression using XPath, allowing to pass a namespace mapping
120
 * context and get a string
121
 *
122
 * Returns the computed string value or NULL, must be deallocated by the
123
 *    caller.
124
 */
125
xmlChar *
126
xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
127
0
                int nsNr, xmlNsPtr *nsList) {
128
0
    xmlChar *ret = NULL;
129
0
    xmlXPathObjectPtr res;
130
0
    xmlNodePtr oldInst;
131
0
    xmlNodePtr oldNode;
132
0
    int oldPos, oldSize;
133
0
    int oldNsNr;
134
0
    xmlNsPtr *oldNamespaces;
135
136
0
    if ((ctxt == NULL) || (ctxt->inst == NULL)) {
137
0
        xsltTransformError(ctxt, NULL, NULL,
138
0
            "xsltEvalXPathStringNs: No context or instruction\n");
139
0
        return(0);
140
0
    }
141
142
0
    oldInst = ctxt->inst;
143
0
    oldNode = ctxt->xpathCtxt->node;
144
0
    oldPos = ctxt->xpathCtxt->proximityPosition;
145
0
    oldSize = ctxt->xpathCtxt->contextSize;
146
0
    oldNsNr = ctxt->xpathCtxt->nsNr;
147
0
    oldNamespaces = ctxt->xpathCtxt->namespaces;
148
149
0
    ctxt->xpathCtxt->node = ctxt->node;
150
    /* TODO: do we need to propagate the namespaces here ? */
151
0
    ctxt->xpathCtxt->namespaces = nsList;
152
0
    ctxt->xpathCtxt->nsNr = nsNr;
153
0
    res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
154
0
    if (res != NULL) {
155
0
  if (res->type != XPATH_STRING)
156
0
      res = xmlXPathConvertString(res);
157
0
  if ((res != NULL) && (res->type == XPATH_STRING)) {
158
0
            ret = res->stringval;
159
0
      res->stringval = NULL;
160
0
  } else {
161
0
      xsltTransformError(ctxt, NULL, NULL,
162
0
     "xpath : string() function didn't return a String\n");
163
0
  }
164
0
  xmlXPathFreeObject(res);
165
0
    } else {
166
0
  ctxt->state = XSLT_STATE_STOPPED;
167
0
    }
168
#ifdef WITH_XSLT_DEBUG_TEMPLATES
169
    XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
170
   "xsltEvalXPathString: returns %s\n", ret));
171
#endif
172
0
    ctxt->inst = oldInst;
173
0
    ctxt->xpathCtxt->node = oldNode;
174
0
    ctxt->xpathCtxt->contextSize = oldSize;
175
0
    ctxt->xpathCtxt->proximityPosition = oldPos;
176
0
    ctxt->xpathCtxt->nsNr = oldNsNr;
177
0
    ctxt->xpathCtxt->namespaces = oldNamespaces;
178
0
    return(ret);
179
0
}
180
181
/**
182
 * xsltEvalXPathString:
183
 * @ctxt:  the XSLT transformation context
184
 * @comp:  the compiled XPath expression
185
 *
186
 * Process the expression using XPath and get a string
187
 *
188
 * Returns the computed string value or NULL, must be deallocated by the
189
 *    caller.
190
 */
191
xmlChar *
192
0
xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) {
193
0
    return(xsltEvalXPathStringNs(ctxt, comp, 0, NULL));
194
0
}
195
196
/**
197
 * xsltEvalTemplateString:
198
 * @ctxt:  the XSLT transformation context
199
 * @contextNode:  the current node in the source tree
200
 * @inst:  the XSLT instruction (xsl:comment, xsl:processing-instruction)
201
 *
202
 * Processes the sequence constructor of the given instruction on
203
 * @contextNode and converts the resulting tree to a string.
204
 * This is needed by e.g. xsl:comment and xsl:processing-instruction.
205
 *
206
 * Returns the computed string value or NULL; it's up to the caller to
207
 *         free the result.
208
 */
209
xmlChar *
210
xsltEvalTemplateString(xsltTransformContextPtr ctxt,
211
           xmlNodePtr contextNode,
212
                 xmlNodePtr inst)
213
0
{
214
0
    xmlNodePtr oldInsert, insert = NULL;
215
0
    xmlChar *ret;
216
0
    const xmlChar *oldLastText;
217
0
    int oldLastTextSize, oldLastTextUse;
218
219
0
    if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) ||
220
0
        (inst->type != XML_ELEMENT_NODE))
221
0
  return(NULL);
222
223
0
    if (inst->children == NULL)
224
0
  return(NULL);
225
226
    /*
227
    * This creates a temporary element-node to add the resulting
228
    * text content to.
229
    * OPTIMIZE TODO: Keep such an element-node in the transformation
230
    *  context to avoid creating it every time.
231
    */
232
0
    insert = xmlNewDocNode(ctxt->output, NULL,
233
0
                     (const xmlChar *)"fake", NULL);
234
0
    if (insert == NULL) {
235
0
  xsltTransformError(ctxt, NULL, inst,
236
0
    "Failed to create temporary node\n");
237
0
  return(NULL);
238
0
    }
239
0
    oldInsert = ctxt->insert;
240
0
    ctxt->insert = insert;
241
0
    oldLastText = ctxt->lasttext;
242
0
    oldLastTextSize = ctxt->lasttsize;
243
0
    oldLastTextUse = ctxt->lasttuse;
244
    /*
245
    * OPTIMIZE TODO: if inst->children consists only of text-nodes.
246
    */
247
0
    xsltApplyOneTemplate(ctxt, contextNode, inst->children, NULL, NULL);
248
249
0
    ctxt->insert = oldInsert;
250
0
    ctxt->lasttext = oldLastText;
251
0
    ctxt->lasttsize = oldLastTextSize;
252
0
    ctxt->lasttuse = oldLastTextUse;
253
254
0
    ret = xmlNodeGetContent(insert);
255
0
    if (insert != NULL)
256
0
  xmlFreeNode(insert);
257
0
    return(ret);
258
0
}
259
260
/**
261
 * xsltAttrTemplateValueProcessNode:
262
 * @ctxt:  the XSLT transformation context
263
 * @str:  the attribute template node value
264
 * @inst:  the instruction (or LRE) in the stylesheet holding the
265
 *         attribute with an AVT
266
 *
267
 * Process the given string, allowing to pass a namespace mapping
268
 * context and return the new string value.
269
 *
270
 * Called by:
271
 *  - xsltAttrTemplateValueProcess() (templates.c)
272
 *  - xsltEvalAttrValueTemplate() (templates.c)
273
 *
274
 * QUESTION: Why is this function public? It is not used outside
275
 *  of templates.c.
276
 *
277
 * Returns the computed string value or NULL, must be deallocated by the
278
 *    caller.
279
 */
280
xmlChar *
281
xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt,
282
    const xmlChar *str, xmlNodePtr inst)
283
0
{
284
0
    xmlChar *ret = NULL;
285
0
    const xmlChar *cur;
286
0
    xmlChar *expr, *val;
287
0
    xmlNsPtr *nsList = NULL;
288
0
    int nsNr = 0;
289
290
0
    if (str == NULL) return(NULL);
291
0
    if (*str == 0)
292
0
  return(xmlStrndup((xmlChar *)"", 0));
293
294
0
    cur = str;
295
0
    while (*cur != 0) {
296
0
  if (*cur == '{') {
297
0
      if (*(cur+1) == '{') { /* escaped '{' */
298
0
          cur++;
299
0
    ret = xmlStrncat(ret, str, cur - str);
300
0
    cur++;
301
0
    str = cur;
302
0
    continue;
303
0
      }
304
0
      ret = xmlStrncat(ret, str, cur - str);
305
0
      str = cur;
306
0
      cur++;
307
0
      while ((*cur != 0) && (*cur != '}')) {
308
    /* Need to check for literal (bug539741) */
309
0
    if ((*cur == '\'') || (*cur == '"')) {
310
0
        char delim = *(cur++);
311
0
        while ((*cur != 0) && (*cur != delim))
312
0
      cur++;
313
0
        if (*cur != 0)
314
0
      cur++; /* skip the ending delimiter */
315
0
    } else
316
0
        cur++;
317
0
            }
318
0
      if (*cur == 0) {
319
0
          xsltTransformError(ctxt, NULL, inst,
320
0
      "xsltAttrTemplateValueProcessNode: unmatched '{'\n");
321
0
    ret = xmlStrncat(ret, str, cur - str);
322
0
    goto exit;
323
0
      }
324
0
      str++;
325
0
      expr = xmlStrndup(str, cur - str);
326
0
      if (expr == NULL)
327
0
    goto exit;
328
0
      else if (*expr == '{') {
329
0
    ret = xmlStrcat(ret, expr);
330
0
    xmlFree(expr);
331
0
      } else {
332
0
    xmlXPathCompExprPtr comp;
333
    /*
334
     * TODO: keep precompiled form around
335
     */
336
0
    if ((nsList == NULL) && (inst != NULL)) {
337
0
        int i = 0;
338
339
0
        nsList = xmlGetNsList(inst->doc, inst);
340
0
        if (nsList != NULL) {
341
0
      while (nsList[i] != NULL)
342
0
          i++;
343
0
      nsNr = i;
344
0
        }
345
0
    }
346
0
    comp = xmlXPathCtxtCompile(ctxt->xpathCtxt, expr);
347
0
                val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList);
348
0
    xmlXPathFreeCompExpr(comp);
349
0
    xmlFree(expr);
350
0
    if (val != NULL) {
351
0
        ret = xmlStrcat(ret, val);
352
0
        xmlFree(val);
353
0
    }
354
0
      }
355
0
      cur++;
356
0
      str = cur;
357
0
  } else if (*cur == '}') {
358
0
      cur++;
359
0
      if (*cur == '}') { /* escaped '}' */
360
0
    ret = xmlStrncat(ret, str, cur - str);
361
0
    cur++;
362
0
    str = cur;
363
0
    continue;
364
0
      } else {
365
0
          xsltTransformError(ctxt, NULL, inst,
366
0
         "xsltAttrTemplateValueProcessNode: unmatched '}'\n");
367
0
      }
368
0
  } else
369
0
      cur++;
370
0
    }
371
0
    if (cur != str) {
372
0
  ret = xmlStrncat(ret, str, cur - str);
373
0
    }
374
375
0
exit:
376
0
    if (nsList != NULL)
377
0
  xmlFree(nsList);
378
379
0
    return(ret);
380
0
}
381
382
/**
383
 * xsltAttrTemplateValueProcess:
384
 * @ctxt:  the XSLT transformation context
385
 * @str:  the attribute template node value
386
 *
387
 * Process the given node and return the new string value.
388
 *
389
 * Returns the computed string value or NULL, must be deallocated by the
390
 *    caller.
391
 */
392
xmlChar *
393
0
xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) {
394
0
    return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL));
395
0
}
396
397
/**
398
 * xsltEvalAttrValueTemplate:
399
 * @ctxt:  the XSLT transformation context
400
 * @inst:  the instruction (or LRE) in the stylesheet holding the
401
 *         attribute with an AVT
402
 * @name:  the attribute QName
403
 * @ns:  the attribute namespace URI
404
 *
405
 * Evaluate a attribute value template, i.e. the attribute value can
406
 * contain expressions contained in curly braces ({}) and those are
407
 * substituted by they computed value.
408
 *
409
 * Returns the computed string value or NULL, must be deallocated by the
410
 *    caller.
411
 */
412
xmlChar *
413
xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst,
414
                    const xmlChar *name, const xmlChar *ns)
415
0
{
416
0
    xmlChar *ret;
417
0
    xmlChar *expr;
418
419
0
    if ((ctxt == NULL) || (inst == NULL) || (name == NULL) ||
420
0
        (inst->type != XML_ELEMENT_NODE))
421
0
  return(NULL);
422
423
0
    expr = xsltGetNsProp(inst, name, ns);
424
0
    if (expr == NULL)
425
0
  return(NULL);
426
427
    /*
428
     * TODO: though now {} is detected ahead, it would still be good to
429
     *       optimize both functions to keep the splitted value if the
430
     *       attribute content and the XPath precompiled expressions around
431
     */
432
433
0
    ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst);
434
#ifdef WITH_XSLT_DEBUG_TEMPLATES
435
    XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
436
   "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret));
437
#endif
438
0
    if (expr != NULL)
439
0
  xmlFree(expr);
440
0
    return(ret);
441
0
}
442
443
/**
444
 * xsltEvalStaticAttrValueTemplate:
445
 * @style:  the XSLT stylesheet
446
 * @inst:  the instruction (or LRE) in the stylesheet holding the
447
 *         attribute with an AVT
448
 * @name:  the attribute Name
449
 * @ns:  the attribute namespace URI
450
 * @found:  indicator whether the attribute is present
451
 *
452
 * Check if an attribute value template has a static value, i.e. the
453
 * attribute value does not contain expressions contained in curly braces ({})
454
 *
455
 * Returns the static string value or NULL, must be deallocated by the
456
 *    caller.
457
 */
458
const xmlChar *
459
xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst,
460
0
      const xmlChar *name, const xmlChar *ns, int *found) {
461
0
    const xmlChar *ret;
462
0
    xmlChar *expr;
463
464
0
    if ((style == NULL) || (inst == NULL) || (name == NULL) ||
465
0
        (inst->type != XML_ELEMENT_NODE))
466
0
  return(NULL);
467
468
0
    expr = xsltGetNsProp(inst, name, ns);
469
0
    if (expr == NULL) {
470
0
  *found = 0;
471
0
  return(NULL);
472
0
    }
473
0
    *found = 1;
474
475
0
    ret = xmlStrchr(expr, '{');
476
0
    if (ret != NULL) {
477
0
  xmlFree(expr);
478
0
  return(NULL);
479
0
    }
480
0
    ret = xmlDictLookup(style->dict, expr, -1);
481
0
    xmlFree(expr);
482
0
    return(ret);
483
0
}
484
485
/**
486
 * xsltAttrTemplateProcess:
487
 * @ctxt:  the XSLT transformation context
488
 * @target:  the element where the attribute will be grafted
489
 * @attr:  the attribute node of a literal result element
490
 *
491
 * Process one attribute of a Literal Result Element (in the stylesheet).
492
 * Evaluates Attribute Value Templates and copies the attribute over to
493
 * the result element.
494
 * This does *not* process attribute sets (xsl:use-attribute-set).
495
 *
496
 *
497
 * Returns the generated attribute node.
498
 */
499
xmlAttrPtr
500
xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target,
501
                  xmlAttrPtr attr)
502
0
{
503
0
    const xmlChar *value;
504
0
    xmlAttrPtr ret;
505
506
0
    if ((ctxt == NULL) || (attr == NULL) || (target == NULL) ||
507
0
        (target->type != XML_ELEMENT_NODE))
508
0
  return(NULL);
509
510
0
    if (attr->type != XML_ATTRIBUTE_NODE)
511
0
  return(NULL);
512
513
    /*
514
    * Skip all XSLT attributes.
515
    */
516
#ifdef XSLT_REFACTORED
517
    if (attr->psvi == xsltXSLTAttrMarker)
518
  return(NULL);
519
#else
520
0
    if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
521
0
  return(NULL);
522
0
#endif
523
    /*
524
    * Get the value.
525
    */
526
0
    if (attr->children != NULL) {
527
0
  if ((attr->children->type != XML_TEXT_NODE) ||
528
0
      (attr->children->next != NULL))
529
0
  {
530
0
      xsltTransformError(ctxt, NULL, attr->parent,
531
0
    "Internal error: The children of an attribute node of a "
532
0
    "literal result element are not in the expected form.\n");
533
0
      return(NULL);
534
0
  }
535
0
  value = attr->children->content;
536
0
  if (value == NULL)
537
0
      value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
538
0
    } else
539
0
  value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
540
    /*
541
    * Overwrite duplicates.
542
    */
543
0
    ret = target->properties;
544
0
    while (ret != NULL) {
545
0
        if (((attr->ns != NULL) == (ret->ns != NULL)) &&
546
0
      xmlStrEqual(ret->name, attr->name) &&
547
0
      ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href)))
548
0
  {
549
0
      break;
550
0
  }
551
0
        ret = ret->next;
552
0
    }
553
0
    if (ret != NULL) {
554
        /* free the existing value */
555
0
  xmlFreeNodeList(ret->children);
556
0
  ret->children = ret->last = NULL;
557
  /*
558
  * Adjust ns-prefix if needed.
559
  */
560
0
  if ((ret->ns != NULL) &&
561
0
      (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix)))
562
0
  {
563
0
      ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target);
564
0
  }
565
0
    } else {
566
        /* create a new attribute */
567
0
  if (attr->ns != NULL)
568
0
      ret = xmlNewNsProp(target,
569
0
    xsltGetNamespace(ctxt, attr->parent, attr->ns, target),
570
0
        attr->name, NULL);
571
0
  else
572
0
      ret = xmlNewNsProp(target, NULL, attr->name, NULL);
573
0
    }
574
    /*
575
    * Set the value.
576
    */
577
0
    if (ret != NULL) {
578
0
        xmlNodePtr text;
579
580
0
        text = xmlNewText(NULL);
581
0
  if (text != NULL) {
582
0
      ret->last = ret->children = text;
583
0
      text->parent = (xmlNodePtr) ret;
584
0
      text->doc = ret->doc;
585
586
0
      if (attr->psvi != NULL) {
587
    /*
588
    * Evaluate the Attribute Value Template.
589
    */
590
0
    xmlChar *val;
591
0
    val = xsltEvalAVT(ctxt, attr->psvi, attr->parent);
592
0
    if (val == NULL) {
593
        /*
594
        * TODO: Damn, we need an easy mechanism to report
595
        * qualified names!
596
        */
597
0
        if (attr->ns) {
598
0
      xsltTransformError(ctxt, NULL, attr->parent,
599
0
          "Internal error: Failed to evaluate the AVT "
600
0
          "of attribute '{%s}%s'.\n",
601
0
          attr->ns->href, attr->name);
602
0
        } else {
603
0
      xsltTransformError(ctxt, NULL, attr->parent,
604
0
          "Internal error: Failed to evaluate the AVT "
605
0
          "of attribute '%s'.\n",
606
0
          attr->name);
607
0
        }
608
0
        text->content = xmlStrdup(BAD_CAST "");
609
0
    } else {
610
0
        text->content = val;
611
0
    }
612
0
      } else if ((ctxt->internalized) && (target != NULL) &&
613
0
                 (target->doc != NULL) &&
614
0
           (target->doc->dict == ctxt->dict) &&
615
0
           xmlDictOwns(ctxt->dict, value)) {
616
0
    text->content = (xmlChar *) value;
617
0
      } else {
618
0
    text->content = xmlStrdup(value);
619
0
      }
620
0
  }
621
0
    } else {
622
0
  if (attr->ns) {
623
0
      xsltTransformError(ctxt, NULL, attr->parent,
624
0
    "Internal error: Failed to create attribute '{%s}%s'.\n",
625
0
    attr->ns->href, attr->name);
626
0
  } else {
627
0
      xsltTransformError(ctxt, NULL, attr->parent,
628
0
    "Internal error: Failed to create attribute '%s'.\n",
629
0
    attr->name);
630
0
  }
631
0
    }
632
0
    return(ret);
633
0
}
634
635
636
/**
637
 * xsltAttrListTemplateProcess:
638
 * @ctxt:  the XSLT transformation context
639
 * @target:  the element where the attributes will be grafted
640
 * @attrs:  the first attribute
641
 *
642
 * Processes all attributes of a Literal Result Element.
643
 * Attribute references are applied via xsl:use-attribute-set
644
 * attributes.
645
 * Copies all non XSLT-attributes over to the @target element
646
 * and evaluates Attribute Value Templates.
647
 *
648
 * Called by xsltApplySequenceConstructor() (transform.c).
649
 *
650
 * Returns a new list of attribute nodes, or NULL in case of error.
651
 *         (Don't assign the result to @target->properties; if
652
 *         the result is NULL, you'll get memory leaks, since the
653
 *         attributes will be disattached.)
654
 */
655
xmlAttrPtr
656
xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt,
657
                      xmlNodePtr target, xmlAttrPtr attrs)
658
0
{
659
0
    xmlAttrPtr attr, copy, last = NULL;
660
0
    xmlNodePtr oldInsert, text;
661
0
    xmlNsPtr origNs = NULL, copyNs = NULL;
662
0
    const xmlChar *value;
663
0
    xmlChar *valueAVT;
664
0
    int hasAttr = 0;
665
666
0
    if ((ctxt == NULL) || (target == NULL) || (attrs == NULL) ||
667
0
        (target->type != XML_ELEMENT_NODE))
668
0
  return(NULL);
669
670
0
    oldInsert = ctxt->insert;
671
0
    ctxt->insert = target;
672
673
    /*
674
    * Apply attribute-sets.
675
    */
676
0
    attr = attrs;
677
0
    do {
678
#ifdef XSLT_REFACTORED
679
  if ((attr->psvi == xsltXSLTAttrMarker) &&
680
      xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets"))
681
  {
682
      xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL);
683
  }
684
#else
685
0
  if ((attr->ns != NULL) &&
686
0
      xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") &&
687
0
      xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
688
0
  {
689
0
      xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL);
690
0
  }
691
0
#endif
692
0
  attr = attr->next;
693
0
    } while (attr != NULL);
694
695
0
    if (target->properties != NULL) {
696
0
        hasAttr = 1;
697
0
    }
698
699
    /*
700
    * Instantiate LRE-attributes.
701
    */
702
0
    attr = attrs;
703
0
    do {
704
  /*
705
  * Skip XSLT attributes.
706
  */
707
#ifdef XSLT_REFACTORED
708
  if (attr->psvi == xsltXSLTAttrMarker) {
709
      goto next_attribute;
710
  }
711
#else
712
0
  if ((attr->ns != NULL) &&
713
0
      xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
714
0
  {
715
0
      goto next_attribute;
716
0
  }
717
0
#endif
718
  /*
719
  * Get the value.
720
  */
721
0
  if (attr->children != NULL) {
722
0
      if ((attr->children->type != XML_TEXT_NODE) ||
723
0
    (attr->children->next != NULL))
724
0
      {
725
0
    xsltTransformError(ctxt, NULL, attr->parent,
726
0
        "Internal error: The children of an attribute node of a "
727
0
        "literal result element are not in the expected form.\n");
728
0
    goto error;
729
0
      }
730
0
      value = attr->children->content;
731
0
      if (value == NULL)
732
0
    value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
733
0
  } else
734
0
      value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
735
736
  /*
737
  * Get the namespace. Avoid lookups of same namespaces.
738
  */
739
0
  if (attr->ns != origNs) {
740
0
      origNs = attr->ns;
741
0
      if (attr->ns != NULL) {
742
#ifdef XSLT_REFACTORED
743
    copyNs = xsltGetSpecialNamespace(ctxt, attr->parent,
744
        attr->ns->href, attr->ns->prefix, target);
745
#else
746
0
    copyNs = xsltGetNamespace(ctxt, attr->parent,
747
0
        attr->ns, target);
748
0
#endif
749
0
    if (copyNs == NULL)
750
0
        goto error;
751
0
      } else
752
0
    copyNs = NULL;
753
0
  }
754
  /*
755
  * Create a new attribute.
756
  */
757
0
        if (hasAttr) {
758
0
      copy = xmlSetNsProp(target, copyNs, attr->name, NULL);
759
0
        } else {
760
            /*
761
            * Avoid checking for duplicate attributes if there aren't
762
            * any attribute sets.
763
            */
764
0
      copy = xmlNewDocProp(target->doc, attr->name, NULL);
765
766
0
      if (copy != NULL) {
767
0
                copy->ns = copyNs;
768
769
                /*
770
                * Attach it to the target element.
771
                */
772
0
                copy->parent = target;
773
0
                if (last == NULL) {
774
0
                    target->properties = copy;
775
0
                    last = copy;
776
0
                } else {
777
0
                    last->next = copy;
778
0
                    copy->prev = last;
779
0
                    last = copy;
780
0
                }
781
0
            }
782
0
        }
783
0
  if (copy == NULL) {
784
0
      if (attr->ns) {
785
0
    xsltTransformError(ctxt, NULL, attr->parent,
786
0
        "Internal error: Failed to create attribute '{%s}%s'.\n",
787
0
        attr->ns->href, attr->name);
788
0
      } else {
789
0
    xsltTransformError(ctxt, NULL, attr->parent,
790
0
        "Internal error: Failed to create attribute '%s'.\n",
791
0
        attr->name);
792
0
      }
793
0
      goto error;
794
0
  }
795
796
  /*
797
  * Set the value.
798
  */
799
0
  text = xmlNewText(NULL);
800
0
  if (text != NULL) {
801
0
      copy->last = copy->children = text;
802
0
      text->parent = (xmlNodePtr) copy;
803
0
      text->doc = copy->doc;
804
805
0
      if (attr->psvi != NULL) {
806
    /*
807
    * Evaluate the Attribute Value Template.
808
    */
809
0
    valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent);
810
0
    if (valueAVT == NULL) {
811
        /*
812
        * TODO: Damn, we need an easy mechanism to report
813
        * qualified names!
814
        */
815
0
        if (attr->ns) {
816
0
      xsltTransformError(ctxt, NULL, attr->parent,
817
0
          "Internal error: Failed to evaluate the AVT "
818
0
          "of attribute '{%s}%s'.\n",
819
0
          attr->ns->href, attr->name);
820
0
        } else {
821
0
      xsltTransformError(ctxt, NULL, attr->parent,
822
0
          "Internal error: Failed to evaluate the AVT "
823
0
          "of attribute '%s'.\n",
824
0
          attr->name);
825
0
        }
826
0
        text->content = xmlStrdup(BAD_CAST "");
827
0
        goto error;
828
0
    } else {
829
0
        text->content = valueAVT;
830
0
    }
831
0
      } else if ((ctxt->internalized) &&
832
0
    (target->doc != NULL) &&
833
0
    (target->doc->dict == ctxt->dict) &&
834
0
    xmlDictOwns(ctxt->dict, value))
835
0
      {
836
0
    text->content = (xmlChar *) value;
837
0
      } else {
838
0
    text->content = xmlStrdup(value);
839
0
      }
840
0
            if ((copy != NULL) && (text != NULL) &&
841
0
                (xmlIsID(copy->doc, copy->parent, copy)))
842
0
                xmlAddID(NULL, copy->doc, text->content, copy);
843
0
  }
844
845
0
next_attribute:
846
0
  attr = attr->next;
847
0
    } while (attr != NULL);
848
849
0
    ctxt->insert = oldInsert;
850
0
    return(target->properties);
851
852
0
error:
853
0
    ctxt->insert = oldInsert;
854
0
    return(NULL);
855
0
}
856
857
858
/**
859
 * xsltTemplateProcess:
860
 * @ctxt:  the XSLT transformation context
861
 * @node:  the attribute template node
862
 *
863
 * Obsolete. Don't use it.
864
 *
865
 * Returns NULL.
866
 */
867
xmlNodePtr *
868
0
xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) {
869
0
    if (node == NULL)
870
0
  return(NULL);
871
872
0
    return(0);
873
0
}
874
875