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