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