Home | History | Annotate | Download | only in libxslt
      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 (at) 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
     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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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
    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
    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 *
    821 xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) {
    822     if (node == NULL)
    823 	return(NULL);
    824 
    825     return(0);
    826 }
    827 
    828 
    829