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