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