• 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/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  */
55 int
xsltEvalXPathPredicate(xsltTransformContextPtr ctxt,xmlXPathCompExprPtr comp,xmlNsPtr * nsList,int nsNr)56 xsltEvalXPathPredicate(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  */
115 xmlChar *
xsltEvalXPathStringNs(xsltTransformContextPtr ctxt,xmlXPathCompExprPtr comp,int nsNr,xmlNsPtr * nsList)116 xsltEvalXPathStringNs(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  */
175 xmlChar *
xsltEvalXPathString(xsltTransformContextPtr ctxt,xmlXPathCompExprPtr comp)176 xsltEvalXPathString(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  */
193 xmlChar *
xsltEvalTemplateString(xsltTransformContextPtr ctxt,xmlNodePtr contextNode,xmlNodePtr inst)194 xsltEvalTemplateString(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  */
255 xmlChar *
xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt,const xmlChar * str,xmlNodePtr inst)256 xsltAttrTemplateValueProcessNode(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  */
356 xmlChar *
xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt,const xmlChar * str)357 xsltAttrTemplateValueProcess(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  */
376 xmlChar *
xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt,xmlNodePtr inst,const xmlChar * name,const xmlChar * ns)377 xsltEvalAttrValueTemplate(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  */
421 const xmlChar *
xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style,xmlNodePtr inst,const xmlChar * name,const xmlChar * ns,int * found)422 xsltEvalStaticAttrValueTemplate(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  */
461 xmlAttrPtr
xsltAttrTemplateProcess(xsltTransformContextPtr ctxt,xmlNodePtr target,xmlAttrPtr attr)462 xsltAttrTemplateProcess(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  */
615 xmlAttrPtr
xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt,xmlNodePtr target,xmlAttrPtr attrs)616 xsltAttrListTemplateProcess(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 
771 next_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 
802 error:
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  */
817 xmlNodePtr *
xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,xmlNodePtr node)818 xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) {
819     if (node == NULL)
820 	return(NULL);
821 
822     return(0);
823 }
824 
825 
826