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