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