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