Coverage Report

Created: 2024-01-20 12:31

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