Home | History | Annotate | Download | only in libxslt
      1 /*
      2  * attributes.c: Implementation of the XSLT attributes handling
      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 #ifdef HAVE_SYS_TYPES_H
     18 #include <sys/types.h>
     19 #endif
     20 #ifdef HAVE_MATH_H
     21 #include <math.h>
     22 #endif
     23 #ifdef HAVE_FLOAT_H
     24 #include <float.h>
     25 #endif
     26 #ifdef HAVE_IEEEFP_H
     27 #include <ieeefp.h>
     28 #endif
     29 #ifdef HAVE_NAN_H
     30 #include <nan.h>
     31 #endif
     32 #ifdef HAVE_CTYPE_H
     33 #include <ctype.h>
     34 #endif
     35 
     36 #include <libxml/xmlmemory.h>
     37 #include <libxml/tree.h>
     38 #include <libxml/hash.h>
     39 #include <libxml/xmlerror.h>
     40 #include <libxml/uri.h>
     41 #include <libxml/parserInternals.h>
     42 #include "xslt.h"
     43 #include "xsltInternals.h"
     44 #include "xsltutils.h"
     45 #include "attributes.h"
     46 #include "namespaces.h"
     47 #include "templates.h"
     48 #include "imports.h"
     49 #include "transform.h"
     50 #include "preproc.h"
     51 
     52 #define WITH_XSLT_DEBUG_ATTRIBUTES
     53 #ifdef WITH_XSLT_DEBUG
     54 #define WITH_XSLT_DEBUG_ATTRIBUTES
     55 #endif
     56 
     57 /*
     58  * TODO: merge attribute sets from different import precedence.
     59  *       all this should be precomputed just before the transformation
     60  *       starts or at first hit with a cache in the context.
     61  *       The simple way for now would be to not allow redefinition of
     62  *       attributes once generated in the output tree, possibly costlier.
     63  */
     64 
     65 /*
     66  * Useful macros
     67  */
     68 #ifdef IS_BLANK
     69 #undef IS_BLANK
     70 #endif
     71 
     72 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) ||	\
     73                      ((c) == 0x0D))
     74 
     75 #define IS_BLANK_NODE(n)						\
     76     (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
     77 
     78 
     79 /*
     80  * The in-memory structure corresponding to an XSLT Attribute in
     81  * an attribute set
     82  */
     83 
     84 
     85 typedef struct _xsltAttrElem xsltAttrElem;
     86 typedef xsltAttrElem *xsltAttrElemPtr;
     87 struct _xsltAttrElem {
     88     struct _xsltAttrElem *next;/* chained list */
     89     xmlNodePtr attr;	/* the xsl:attribute definition */
     90     const xmlChar *set; /* or the attribute set */
     91     const xmlChar *ns;  /* and its namespace */
     92 };
     93 
     94 /************************************************************************
     95  *									*
     96  *			XSLT Attribute handling				*
     97  *									*
     98  ************************************************************************/
     99 
    100 /**
    101  * xsltNewAttrElem:
    102  * @attr:  the new xsl:attribute node
    103  *
    104  * Create a new XSLT AttrElem
    105  *
    106  * Returns the newly allocated xsltAttrElemPtr or NULL in case of error
    107  */
    108 static xsltAttrElemPtr
    109 xsltNewAttrElem(xmlNodePtr attr) {
    110     xsltAttrElemPtr cur;
    111 
    112     cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
    113     if (cur == NULL) {
    114         xsltGenericError(xsltGenericErrorContext,
    115 		"xsltNewAttrElem : malloc failed\n");
    116 	return(NULL);
    117     }
    118     memset(cur, 0, sizeof(xsltAttrElem));
    119     cur->attr = attr;
    120     return(cur);
    121 }
    122 
    123 /**
    124  * xsltFreeAttrElem:
    125  * @attr:  an XSLT AttrElem
    126  *
    127  * Free up the memory allocated by @attr
    128  */
    129 static void
    130 xsltFreeAttrElem(xsltAttrElemPtr attr) {
    131     xmlFree(attr);
    132 }
    133 
    134 /**
    135  * xsltFreeAttrElemList:
    136  * @list:  an XSLT AttrElem list
    137  *
    138  * Free up the memory allocated by @list
    139  */
    140 static void
    141 xsltFreeAttrElemList(xsltAttrElemPtr list) {
    142     xsltAttrElemPtr next;
    143 
    144     while (list != NULL) {
    145 	next = list->next;
    146 	xsltFreeAttrElem(list);
    147 	list = next;
    148     }
    149 }
    150 
    151 #ifdef XSLT_REFACTORED
    152     /*
    153     * This was moved to xsltParseStylesheetAttributeSet().
    154     */
    155 #else
    156 /**
    157  * xsltAddAttrElemList:
    158  * @list:  an XSLT AttrElem list
    159  * @attr:  the new xsl:attribute node
    160  *
    161  * Add the new attribute to the list.
    162  *
    163  * Returns the new list pointer
    164  */
    165 static xsltAttrElemPtr
    166 xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
    167     xsltAttrElemPtr next, cur;
    168 
    169     if (attr == NULL)
    170 	return(list);
    171     if (list == NULL)
    172 	return(xsltNewAttrElem(attr));
    173     cur = list;
    174     while (cur != NULL) {
    175 	next = cur->next;
    176 	if (cur->attr == attr)
    177 	    return(cur);
    178 	if (cur->next == NULL) {
    179 	    cur->next = xsltNewAttrElem(attr);
    180 	    return(list);
    181 	}
    182 	cur = next;
    183     }
    184     return(list);
    185 }
    186 #endif /* XSLT_REFACTORED */
    187 
    188 /**
    189  * xsltMergeAttrElemList:
    190  * @list:  an XSLT AttrElem list
    191  * @old:  another XSLT AttrElem list
    192  *
    193  * Add all the attributes from list @old to list @list,
    194  * but drop redefinition of existing values.
    195  *
    196  * Returns the new list pointer
    197  */
    198 static xsltAttrElemPtr
    199 xsltMergeAttrElemList(xsltStylesheetPtr style,
    200 		      xsltAttrElemPtr list, xsltAttrElemPtr old) {
    201     xsltAttrElemPtr cur;
    202     int add;
    203 
    204     while (old != NULL) {
    205 	if ((old->attr == NULL) && (old->set == NULL)) {
    206 	    old = old->next;
    207 	    continue;
    208 	}
    209 	/*
    210 	 * Check that the attribute is not yet in the list
    211 	 */
    212 	cur = list;
    213 	add = 1;
    214 	while (cur != NULL) {
    215 	    if ((cur->attr == NULL) && (cur->set == NULL)) {
    216 		if (cur->next == NULL)
    217 		    break;
    218 		cur = cur->next;
    219 		continue;
    220 	    }
    221 	    if ((cur->set != NULL) && (cur->set == old->set)) {
    222 		add = 0;
    223 		break;
    224 	    }
    225 	    if (cur->set != NULL) {
    226 		if (cur->next == NULL)
    227 		    break;
    228 		cur = cur->next;
    229 		continue;
    230 	    }
    231 	    if (old->set != NULL) {
    232 		if (cur->next == NULL)
    233 		    break;
    234 		cur = cur->next;
    235 		continue;
    236 	    }
    237 	    if (cur->attr == old->attr) {
    238 		xsltGenericError(xsltGenericErrorContext,
    239 	     "xsl:attribute-set : use-attribute-sets recursion detected\n");
    240 		return(list);
    241 	    }
    242 	    if (cur->next == NULL)
    243 		break;
    244             cur = cur->next;
    245 	}
    246 
    247 	if (add == 1) {
    248 	    /*
    249 	    * Changed to use the string-dict, rather than duplicating
    250 	    * @set and @ns; this fixes bug #340400.
    251 	    */
    252 	    if (cur == NULL) {
    253 		list = xsltNewAttrElem(old->attr);
    254 		if (old->set != NULL) {
    255 		    list->set = xmlDictLookup(style->dict, old->set, -1);
    256 		    if (old->ns != NULL)
    257 			list->ns = xmlDictLookup(style->dict, old->ns, -1);
    258 		}
    259 	    } else if (add) {
    260 		cur->next = xsltNewAttrElem(old->attr);
    261 		if (old->set != NULL) {
    262 		    cur->next->set = xmlDictLookup(style->dict, old->set, -1);
    263 		    if (old->ns != NULL)
    264 			cur->next->ns = xmlDictLookup(style->dict, old->ns, -1);
    265 		}
    266 	    }
    267 	}
    268 
    269 	old = old->next;
    270     }
    271     return(list);
    272 }
    273 
    274 /************************************************************************
    275  *									*
    276  *			Module interfaces				*
    277  *									*
    278  ************************************************************************/
    279 
    280 /**
    281  * xsltParseStylesheetAttributeSet:
    282  * @style:  the XSLT stylesheet
    283  * @cur:  the "attribute-set" element
    284  *
    285  * parse an XSLT stylesheet attribute-set element
    286  */
    287 
    288 void
    289 xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
    290     const xmlChar *ncname;
    291     const xmlChar *prefix;
    292     xmlChar *value;
    293     xmlNodePtr child;
    294     xsltAttrElemPtr attrItems;
    295 
    296     if ((cur == NULL) || (style == NULL))
    297 	return;
    298 
    299     value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL);
    300     if (value == NULL) {
    301 	xsltGenericError(xsltGenericErrorContext,
    302 	     "xsl:attribute-set : name is missing\n");
    303 	return;
    304     }
    305 
    306     ncname = xsltSplitQName(style->dict, value, &prefix);
    307     xmlFree(value);
    308     value = NULL;
    309 
    310     if (style->attributeSets == NULL) {
    311 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
    312 	xsltGenericDebug(xsltGenericDebugContext,
    313 	    "creating attribute set table\n");
    314 #endif
    315 	style->attributeSets = xmlHashCreate(10);
    316     }
    317     if (style->attributeSets == NULL)
    318 	return;
    319 
    320     attrItems = xmlHashLookup2(style->attributeSets, ncname, prefix);
    321 
    322     /*
    323     * Parse the content. Only xsl:attribute elements are allowed.
    324     */
    325     child = cur->children;
    326     while (child != NULL) {
    327 	/*
    328 	* Report invalid nodes.
    329 	*/
    330 	if ((child->type != XML_ELEMENT_NODE) ||
    331 	    (child->ns == NULL) ||
    332 	    (! IS_XSLT_ELEM(child)))
    333 	{
    334 	    if (child->type == XML_ELEMENT_NODE)
    335 		xsltTransformError(NULL, style, child,
    336 			"xsl:attribute-set : unexpected child %s\n",
    337 		                 child->name);
    338 	    else
    339 		xsltTransformError(NULL, style, child,
    340 			"xsl:attribute-set : child of unexpected type\n");
    341 	} else if (!IS_XSLT_NAME(child, "attribute")) {
    342 	    xsltTransformError(NULL, style, child,
    343 		"xsl:attribute-set : unexpected child xsl:%s\n",
    344 		child->name);
    345 	} else {
    346 #ifdef XSLT_REFACTORED
    347 	    xsltAttrElemPtr nextAttr, curAttr;
    348 
    349 	    /*
    350 	    * Process xsl:attribute
    351 	    * ---------------------
    352 	    */
    353 
    354 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
    355 	    xsltGenericDebug(xsltGenericDebugContext,
    356 		"add attribute to list %s\n", ncname);
    357 #endif
    358 	    /*
    359 	    * The following was taken over from
    360 	    * xsltAddAttrElemList().
    361 	    */
    362 	    if (attrItems == NULL) {
    363 		attrItems = xsltNewAttrElem(child);
    364 	    } else {
    365 		curAttr = attrItems;
    366 		while (curAttr != NULL) {
    367 		    nextAttr = curAttr->next;
    368 		    if (curAttr->attr == child) {
    369 			/*
    370 			* URGENT TODO: Can somebody explain
    371 			*  why attrItems is set to curAttr
    372 			*  here? Is this somehow related to
    373 			*  avoidance of recursions?
    374 			*/
    375 			attrItems = curAttr;
    376 			goto next_child;
    377 		    }
    378 		    if (curAttr->next == NULL)
    379 			curAttr->next = xsltNewAttrElem(child);
    380 		    curAttr = nextAttr;
    381 		}
    382 	    }
    383 	    /*
    384 	    * Parse the xsl:attribute and its content.
    385 	    */
    386 	    xsltParseAnyXSLTElem(XSLT_CCTXT(style), child);
    387 #else
    388 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
    389 	    xsltGenericDebug(xsltGenericDebugContext,
    390 		"add attribute to list %s\n", ncname);
    391 #endif
    392 	    /*
    393 	    * OLD behaviour:
    394 	    */
    395 	    attrItems = xsltAddAttrElemList(attrItems, child);
    396 #endif
    397 	}
    398 
    399 #ifdef XSLT_REFACTORED
    400 next_child:
    401 #endif
    402 	child = child->next;
    403     }
    404 
    405     /*
    406     * Process attribue "use-attribute-sets".
    407     */
    408     /* TODO check recursion */
    409     value = xmlGetNsProp(cur, (const xmlChar *)"use-attribute-sets",
    410 	NULL);
    411     if (value != NULL) {
    412 	const xmlChar *curval, *endval;
    413 	curval = value;
    414 	while (*curval != 0) {
    415 	    while (IS_BLANK(*curval)) curval++;
    416 	    if (*curval == 0)
    417 		break;
    418 	    endval = curval;
    419 	    while ((*endval != 0) && (!IS_BLANK(*endval))) endval++;
    420 	    curval = xmlDictLookup(style->dict, curval, endval - curval);
    421 	    if (curval) {
    422 		const xmlChar *ncname2 = NULL;
    423 		const xmlChar *prefix2 = NULL;
    424 		xsltAttrElemPtr refAttrItems;
    425 
    426 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
    427 		xsltGenericDebug(xsltGenericDebugContext,
    428 		    "xsl:attribute-set : %s adds use %s\n", ncname, curval);
    429 #endif
    430 		ncname2 = xsltSplitQName(style->dict, curval, &prefix2);
    431 		refAttrItems = xsltNewAttrElem(NULL);
    432 		if (refAttrItems != NULL) {
    433 		    refAttrItems->set = ncname2;
    434 		    refAttrItems->ns = prefix2;
    435 		    attrItems = xsltMergeAttrElemList(style,
    436 			attrItems, refAttrItems);
    437 		    xsltFreeAttrElem(refAttrItems);
    438 		}
    439 	    }
    440 	    curval = endval;
    441 	}
    442 	xmlFree(value);
    443 	value = NULL;
    444     }
    445 
    446     /*
    447      * Update the value
    448      */
    449     /*
    450     * TODO: Why is this dummy entry needed.?
    451     */
    452     if (attrItems == NULL)
    453 	attrItems = xsltNewAttrElem(NULL);
    454     xmlHashUpdateEntry2(style->attributeSets, ncname, prefix, attrItems, NULL);
    455 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
    456     xsltGenericDebug(xsltGenericDebugContext,
    457 	"updated attribute list %s\n", ncname);
    458 #endif
    459 }
    460 
    461 /**
    462  * xsltGetSAS:
    463  * @style:  the XSLT stylesheet
    464  * @name:  the attribute list name
    465  * @ns:  the attribute list namespace
    466  *
    467  * lookup an attribute set based on the style cascade
    468  *
    469  * Returns the attribute set or NULL
    470  */
    471 static xsltAttrElemPtr
    472 xsltGetSAS(xsltStylesheetPtr style, const xmlChar *name, const xmlChar *ns) {
    473     xsltAttrElemPtr values;
    474 
    475     while (style != NULL) {
    476 	values = xmlHashLookup2(style->attributeSets, name, ns);
    477 	if (values != NULL)
    478 	    return(values);
    479 	style = xsltNextImport(style);
    480     }
    481     return(NULL);
    482 }
    483 
    484 /**
    485  * xsltResolveSASCallback,:
    486  * @style:  the XSLT stylesheet
    487  *
    488  * resolve the references in an attribute set.
    489  */
    490 static void
    491 xsltResolveSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style,
    492 	               const xmlChar *name, const xmlChar *ns,
    493 		       ATTRIBUTE_UNUSED const xmlChar *ignored) {
    494     xsltAttrElemPtr tmp;
    495     xsltAttrElemPtr refs;
    496 
    497     tmp = values;
    498     while (tmp != NULL) {
    499 	if (tmp->set != NULL) {
    500 	    /*
    501 	     * Check against cycles !
    502 	     */
    503 	    if ((xmlStrEqual(name, tmp->set)) && (xmlStrEqual(ns, tmp->ns))) {
    504 		xsltGenericError(xsltGenericErrorContext,
    505      "xsl:attribute-set : use-attribute-sets recursion detected on %s\n",
    506                                  name);
    507 	    } else {
    508 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
    509 		xsltGenericDebug(xsltGenericDebugContext,
    510 			"Importing attribute list %s\n", tmp->set);
    511 #endif
    512 
    513 		refs = xsltGetSAS(style, tmp->set, tmp->ns);
    514 		if (refs == NULL) {
    515 		    xsltGenericError(xsltGenericErrorContext,
    516      "xsl:attribute-set : use-attribute-sets %s reference missing %s\n",
    517 				     name, tmp->set);
    518 		} else {
    519 		    /*
    520 		     * recurse first for cleanup
    521 		     */
    522 		    xsltResolveSASCallback(refs, style, name, ns, NULL);
    523 		    /*
    524 		     * Then merge
    525 		     */
    526 		    xsltMergeAttrElemList(style, values, refs);
    527 		    /*
    528 		     * Then suppress the reference
    529 		     */
    530 		    tmp->set = NULL;
    531 		    tmp->ns = NULL;
    532 		}
    533 	    }
    534 	}
    535 	tmp = tmp->next;
    536     }
    537 }
    538 
    539 /**
    540  * xsltMergeSASCallback,:
    541  * @style:  the XSLT stylesheet
    542  *
    543  * Merge an attribute set from an imported stylesheet.
    544  */
    545 static void
    546 xsltMergeSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style,
    547 	               const xmlChar *name, const xmlChar *ns,
    548 		       ATTRIBUTE_UNUSED const xmlChar *ignored) {
    549     int ret;
    550     xsltAttrElemPtr topSet;
    551 
    552     ret = xmlHashAddEntry2(style->attributeSets, name, ns, values);
    553     if (ret < 0) {
    554 	/*
    555 	 * Add failed, this attribute set can be removed.
    556 	 */
    557 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
    558 	xsltGenericDebug(xsltGenericDebugContext,
    559 		"attribute set %s present already in top stylesheet"
    560 		" - merging\n", name);
    561 #endif
    562 	topSet = xmlHashLookup2(style->attributeSets, name, ns);
    563 	if (topSet==NULL) {
    564 	    xsltGenericError(xsltGenericErrorContext,
    565 	        "xsl:attribute-set : logic error merging from imports for"
    566 		" attribute-set %s\n", name);
    567 	} else {
    568 	    topSet = xsltMergeAttrElemList(style, topSet, values);
    569 	    xmlHashUpdateEntry2(style->attributeSets, name, ns, topSet, NULL);
    570 	}
    571 	xsltFreeAttrElemList(values);
    572 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
    573     } else {
    574 	xsltGenericDebug(xsltGenericDebugContext,
    575 		"attribute set %s moved to top stylesheet\n",
    576 		         name);
    577 #endif
    578     }
    579 }
    580 
    581 /**
    582  * xsltResolveStylesheetAttributeSet:
    583  * @style:  the XSLT stylesheet
    584  *
    585  * resolve the references between attribute sets.
    586  */
    587 void
    588 xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
    589     xsltStylesheetPtr cur;
    590 
    591 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
    592     xsltGenericDebug(xsltGenericDebugContext,
    593 	    "Resolving attribute sets references\n");
    594 #endif
    595     /*
    596      * First aggregate all the attribute sets definitions from the imports
    597      */
    598     cur = xsltNextImport(style);
    599     while (cur != NULL) {
    600 	if (cur->attributeSets != NULL) {
    601 	    if (style->attributeSets == NULL) {
    602 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
    603 		xsltGenericDebug(xsltGenericDebugContext,
    604 		    "creating attribute set table\n");
    605 #endif
    606 		style->attributeSets = xmlHashCreate(10);
    607 	    }
    608 	    xmlHashScanFull(cur->attributeSets,
    609 		(xmlHashScannerFull) xsltMergeSASCallback, style);
    610 	    /*
    611 	     * the attribute lists have either been migrated to style
    612 	     * or freed directly in xsltMergeSASCallback()
    613 	     */
    614 	    xmlHashFree(cur->attributeSets, NULL);
    615 	    cur->attributeSets = NULL;
    616 	}
    617 	cur = xsltNextImport(cur);
    618     }
    619 
    620     /*
    621      * Then resolve all the references and computes the resulting sets
    622      */
    623     if (style->attributeSets != NULL) {
    624 	xmlHashScanFull(style->attributeSets,
    625 		(xmlHashScannerFull) xsltResolveSASCallback, style);
    626     }
    627 }
    628 
    629 /**
    630  * xsltAttributeInternal:
    631  * @ctxt:  a XSLT process context
    632  * @node:  the current node in the source tree
    633  * @inst:  the xsl:attribute element
    634  * @comp:  precomputed information
    635  * @fromAttributeSet:  the attribute comes from an attribute-set
    636  *
    637  * Process the xslt attribute node on the source node
    638  */
    639 static void
    640 xsltAttributeInternal(xsltTransformContextPtr ctxt,
    641 		      xmlNodePtr contextNode,
    642                       xmlNodePtr inst,
    643 		      xsltStylePreCompPtr castedComp,
    644                       int fromAttributeSet)
    645 {
    646 #ifdef XSLT_REFACTORED
    647     xsltStyleItemAttributePtr comp =
    648 	(xsltStyleItemAttributePtr) castedComp;
    649 #else
    650     xsltStylePreCompPtr comp = castedComp;
    651 #endif
    652     xmlNodePtr targetElem;
    653     xmlChar *prop = NULL;
    654     const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL;
    655     xmlChar *value = NULL;
    656     xmlNsPtr ns = NULL;
    657     xmlAttrPtr attr;
    658 
    659     if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
    660         return;
    661 
    662     /*
    663     * A comp->has_name == 0 indicates that we need to skip this instruction,
    664     * since it was evaluated to be invalid already during compilation.
    665     */
    666     if (!comp->has_name)
    667         return;
    668     /*
    669     * BIG NOTE: This previously used xsltGetSpecialNamespace() and
    670     *  xsltGetNamespace(), but since both are not appropriate, we
    671     *  will process namespace lookup here to avoid adding yet another
    672     *  ns-lookup function to namespaces.c.
    673     */
    674     /*
    675     * SPEC XSLT 1.0: Error cases:
    676     * - Creating nodes other than text nodes during the instantiation of
    677     *   the content of the xsl:attribute element; implementations may
    678     *   either signal the error or ignore the offending nodes."
    679     */
    680 
    681     if (comp == NULL) {
    682         xsltTransformError(ctxt, NULL, inst,
    683 	    "Internal error in xsltAttributeInternal(): "
    684 	    "The XSLT 'attribute' instruction was not compiled.\n");
    685         return;
    686     }
    687     /*
    688     * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error?
    689     *   So report an internal error?
    690     */
    691     if (ctxt->insert == NULL)
    692         return;
    693     /*
    694     * SPEC XSLT 1.0:
    695     *  "Adding an attribute to a node that is not an element;
    696     *  implementations may either signal the error or ignore the attribute."
    697     *
    698     * TODO: I think we should signal such errors in the future, and maybe
    699     *  provide an option to ignore such errors.
    700     */
    701     targetElem = ctxt->insert;
    702     if (targetElem->type != XML_ELEMENT_NODE)
    703 	return;
    704 
    705     /*
    706     * SPEC XSLT 1.0:
    707     * "Adding an attribute to an element after children have been added
    708     *  to it; implementations may either signal the error or ignore the
    709     *  attribute."
    710     *
    711     * TODO: We should decide whether not to report such errors or
    712     *  to ignore them; note that we *ignore* if the parent is not an
    713     *  element, but here we report an error.
    714     */
    715     if (targetElem->children != NULL) {
    716 	/*
    717 	* NOTE: Ah! This seems to be intended to support streamed
    718 	*  result generation!.
    719 	*/
    720         xsltTransformError(ctxt, NULL, inst,
    721 	    "xsl:attribute: Cannot add attributes to an "
    722 	    "element if children have been already added "
    723 	    "to the element.\n");
    724         return;
    725     }
    726 
    727     /*
    728     * Process the name
    729     * ----------------
    730     */
    731 
    732 #ifdef WITH_DEBUGGER
    733     if (ctxt->debugStatus != XSLT_DEBUG_NONE)
    734         xslHandleDebugger(inst, contextNode, NULL, ctxt);
    735 #endif
    736 
    737     if (comp->name == NULL) {
    738 	/* TODO: fix attr acquisition wrt to the XSLT namespace */
    739         prop = xsltEvalAttrValueTemplate(ctxt, inst,
    740 	    (const xmlChar *) "name", XSLT_NAMESPACE);
    741         if (prop == NULL) {
    742             xsltTransformError(ctxt, NULL, inst,
    743 		"xsl:attribute: The attribute 'name' is missing.\n");
    744             goto error;
    745         }
    746 	if (xmlValidateQName(prop, 0)) {
    747 	    xsltTransformError(ctxt, NULL, inst,
    748 		"xsl:attribute: The effective name '%s' is not a "
    749 		"valid QName.\n", prop);
    750 	    /* we fall through to catch any further errors, if possible */
    751 	}
    752 	name = xsltSplitQName(ctxt->dict, prop, &prefix);
    753 	xmlFree(prop);
    754 
    755 	/*
    756 	* Reject a prefix of "xmlns".
    757 	*/
    758 	if ((prefix != NULL) &&
    759 	    (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5)))
    760 	{
    761 #ifdef WITH_XSLT_DEBUG_PARSING
    762 	    xsltGenericDebug(xsltGenericDebugContext,
    763 		"xsltAttribute: xmlns prefix forbidden\n");
    764 #endif
    765 	    /*
    766 	    * SPEC XSLT 1.0:
    767 	    *  "It is an error if the string that results from instantiating
    768 	    *  the attribute value template is not a QName or is the string
    769 	    *  xmlns. An XSLT processor may signal the error; if it does not
    770 	    *  signal the error, it must recover by not adding the attribute
    771 	    *  to the result tree."
    772 	    * TODO: Decide which way to go here.
    773 	    */
    774 	    goto error;
    775 	}
    776 
    777     } else {
    778 	/*
    779 	* The "name" value was static.
    780 	*/
    781 #ifdef XSLT_REFACTORED
    782 	prefix = comp->nsPrefix;
    783 	name = comp->name;
    784 #else
    785 	name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
    786 #endif
    787     }
    788 
    789     /*
    790     * Process namespace semantics
    791     * ---------------------------
    792     *
    793     * Evaluate the namespace name.
    794     */
    795     if (comp->has_ns) {
    796 	/*
    797 	* The "namespace" attribute was existent.
    798 	*/
    799 	if (comp->ns != NULL) {
    800 	    /*
    801 	    * No AVT; just plain text for the namespace name.
    802 	    */
    803 	    if (comp->ns[0] != 0)
    804 		nsName = comp->ns;
    805 	} else {
    806 	    xmlChar *tmpNsName;
    807 	    /*
    808 	    * Eval the AVT.
    809 	    */
    810 	    /* TODO: check attr acquisition wrt to the XSLT namespace */
    811 	    tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
    812 		(const xmlChar *) "namespace", XSLT_NAMESPACE);
    813 	    /*
    814 	    * This fixes bug #302020: The AVT might also evaluate to the
    815 	    * empty string; this means that the empty string also indicates
    816 	    * "no namespace".
    817 	    * SPEC XSLT 1.0:
    818 	    *  "If the string is empty, then the expanded-name of the
    819 	    *  attribute has a null namespace URI."
    820 	    */
    821 	    if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
    822 		nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
    823 	    xmlFree(tmpNsName);
    824 	};
    825     } else if (prefix != NULL) {
    826 	/*
    827 	* SPEC XSLT 1.0:
    828 	*  "If the namespace attribute is not present, then the QName is
    829 	*  expanded into an expanded-name using the namespace declarations
    830 	*  in effect for the xsl:attribute element, *not* including any
    831 	*  default namespace declaration."
    832 	*/
    833 	ns = xmlSearchNs(inst->doc, inst, prefix);
    834 	if (ns == NULL) {
    835 	    /*
    836 	    * Note that this is treated as an error now (checked with
    837 	    *  Saxon, Xalan-J and MSXML).
    838 	    */
    839 	    xsltTransformError(ctxt, NULL, inst,
    840 		"xsl:attribute: The QName '%s:%s' has no "
    841 		"namespace binding in scope in the stylesheet; "
    842 		"this is an error, since the namespace was not "
    843 		"specified by the instruction itself.\n", prefix, name);
    844 	} else
    845 	    nsName = ns->href;
    846     }
    847 
    848     if (fromAttributeSet) {
    849 	/*
    850 	* This tries to ensure that xsl:attribute(s) coming
    851 	* from an xsl:attribute-set won't override attribute of
    852 	* literal result elements or of explicit xsl:attribute(s).
    853 	* URGENT TODO: This might be buggy, since it will miss to
    854 	*  overwrite two equal attributes both from attribute sets.
    855 	*/
    856 	attr = xmlHasNsProp(targetElem, name, nsName);
    857 	if (attr != NULL)
    858 	    return;
    859     }
    860 
    861     /*
    862     * Find/create a matching ns-decl in the result tree.
    863     */
    864     ns = NULL;
    865 
    866 #if 0
    867     if (0) {
    868 	/*
    869 	* OPTIMIZE TODO: How do we know if we are adding to a
    870 	*  fragment or to the result tree?
    871 	*
    872 	* If we are adding to a result tree fragment (i.e., not to the
    873 	* actual result tree), we'll don't bother searching for the
    874 	* ns-decl, but just store it in the dummy-doc of the result
    875 	* tree fragment.
    876 	*/
    877 	if (nsName != NULL) {
    878 	    /*
    879 	    * TODO: Get the doc of @targetElem.
    880 	    */
    881 	    ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix);
    882 	}
    883     }
    884 #endif
    885 
    886     if (nsName != NULL) {
    887 	/*
    888 	* Something about ns-prefixes:
    889 	* SPEC XSLT 1.0:
    890 	*  "XSLT processors may make use of the prefix of the QName specified
    891 	*  in the name attribute when selecting the prefix used for outputting
    892 	*  the created attribute as XML; however, they are not required to do
    893 	*  so and, if the prefix is xmlns, they must not do so"
    894 	*/
    895 	/*
    896 	* xsl:attribute can produce a scenario where the prefix is NULL,
    897 	* so generate a prefix.
    898 	*/
    899 	if (prefix == NULL) {
    900 	    xmlChar *pref = xmlStrdup(BAD_CAST "ns_1");
    901 
    902 	    ns = xsltGetSpecialNamespace(ctxt, inst, nsName, BAD_CAST pref,
    903 		targetElem);
    904 
    905 	    xmlFree(pref);
    906 	} else {
    907 	    ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix,
    908 		targetElem);
    909 	}
    910 	if (ns == NULL) {
    911 	    xsltTransformError(ctxt, NULL, inst,
    912 		"Namespace fixup error: Failed to acquire an in-scope "
    913 		"namespace binding for the generated attribute '{%s}%s'.\n",
    914 		nsName, name);
    915 	    goto error;
    916 	}
    917     }
    918     /*
    919     * Construction of the value
    920     * -------------------------
    921     */
    922     if (inst->children == NULL) {
    923 	/*
    924 	* No content.
    925 	* TODO: Do we need to put the empty string in ?
    926 	*/
    927 	attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) "");
    928     } else if ((inst->children->next == NULL) &&
    929 	    ((inst->children->type == XML_TEXT_NODE) ||
    930 	     (inst->children->type == XML_CDATA_SECTION_NODE)))
    931     {
    932 	xmlNodePtr copyTxt;
    933 
    934 	/*
    935 	* xmlSetNsProp() will take care of duplicates.
    936 	*/
    937 	attr = xmlSetNsProp(ctxt->insert, ns, name, NULL);
    938 	if (attr == NULL) /* TODO: report error ? */
    939 	    goto error;
    940 	/*
    941 	* This was taken over from xsltCopyText() (transform.c).
    942 	*/
    943 	if (ctxt->internalized &&
    944 	    (ctxt->insert->doc != NULL) &&
    945 	    (ctxt->insert->doc->dict == ctxt->dict))
    946 	{
    947 	    copyTxt = xmlNewText(NULL);
    948 	    if (copyTxt == NULL) /* TODO: report error */
    949 		goto error;
    950 	    /*
    951 	    * This is a safe scenario where we don't need to lookup
    952 	    * the dict.
    953 	    */
    954 	    copyTxt->content = inst->children->content;
    955 	    /*
    956 	    * Copy "disable-output-escaping" information.
    957 	    * TODO: Does this have any effect for attribute values
    958 	    *  anyway?
    959 	    */
    960 	    if (inst->children->name == xmlStringTextNoenc)
    961 		copyTxt->name = xmlStringTextNoenc;
    962 	} else {
    963 	    /*
    964 	    * Copy the value.
    965 	    */
    966 	    copyTxt = xmlNewText(inst->children->content);
    967 	    if (copyTxt == NULL) /* TODO: report error */
    968 		goto error;
    969 	}
    970 	attr->children = attr->last = copyTxt;
    971 	copyTxt->parent = (xmlNodePtr) attr;
    972 	copyTxt->doc = attr->doc;
    973 	/*
    974 	* Copy "disable-output-escaping" information.
    975 	* TODO: Does this have any effect for attribute values
    976 	*  anyway?
    977 	*/
    978 	if (inst->children->name == xmlStringTextNoenc)
    979 	    copyTxt->name = xmlStringTextNoenc;
    980 
    981         /*
    982          * since we create the attribute without content IDness must be
    983          * asserted as a second step
    984          */
    985         if ((copyTxt->content != NULL) &&
    986             (xmlIsID(attr->doc, attr->parent, attr)))
    987             xmlAddID(NULL, attr->doc, copyTxt->content, attr);
    988     } else {
    989 	/*
    990 	* The sequence constructor might be complex, so instantiate it.
    991 	*/
    992 	value = xsltEvalTemplateString(ctxt, contextNode, inst);
    993 	if (value != NULL) {
    994 	    attr = xmlSetNsProp(ctxt->insert, ns, name, value);
    995 	    xmlFree(value);
    996 	} else {
    997 	    /*
    998 	    * TODO: Do we have to add the empty string to the attr?
    999 	    * TODO: Does a  value of NULL indicate an
   1000 	    *  error in xsltEvalTemplateString() ?
   1001 	    */
   1002 	    attr = xmlSetNsProp(ctxt->insert, ns, name,
   1003 		(const xmlChar *) "");
   1004 	}
   1005     }
   1006 
   1007 error:
   1008     return;
   1009 }
   1010 
   1011 /**
   1012  * xsltAttribute:
   1013  * @ctxt:  a XSLT process context
   1014  * @node:  the node in the source tree.
   1015  * @inst:  the xslt attribute node
   1016  * @comp:  precomputed information
   1017  *
   1018  * Process the xslt attribute node on the source node
   1019  */
   1020 void
   1021 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
   1022 	      xmlNodePtr inst, xsltStylePreCompPtr comp) {
   1023     xsltAttributeInternal(ctxt, node, inst, comp, 0);
   1024 }
   1025 
   1026 /**
   1027  * xsltApplyAttributeSet:
   1028  * @ctxt:  the XSLT stylesheet
   1029  * @node:  the node in the source tree.
   1030  * @inst:  the attribute node "xsl:use-attribute-sets"
   1031  * @attrSets:  the list of QNames of the attribute-sets to be applied
   1032  *
   1033  * Apply the xsl:use-attribute-sets.
   1034  * If @attrSets is NULL, then @inst will be used to exctract this
   1035  * value.
   1036  * If both, @attrSets and @inst, are NULL, then this will do nothing.
   1037  */
   1038 void
   1039 xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
   1040                       xmlNodePtr inst,
   1041                       const xmlChar *attrSets)
   1042 {
   1043     const xmlChar *ncname = NULL;
   1044     const xmlChar *prefix = NULL;
   1045     const xmlChar *curstr, *endstr;
   1046     xsltAttrElemPtr attrs;
   1047     xsltStylesheetPtr style;
   1048 
   1049     if (attrSets == NULL) {
   1050 	if (inst == NULL)
   1051 	    return;
   1052 	else {
   1053 	    /*
   1054 	    * Extract the value from @inst.
   1055 	    */
   1056 	    if (inst->type == XML_ATTRIBUTE_NODE) {
   1057 		if ( ((xmlAttrPtr) inst)->children != NULL)
   1058 		    attrSets = ((xmlAttrPtr) inst)->children->content;
   1059 
   1060 	    }
   1061 	    if (attrSets == NULL) {
   1062 		/*
   1063 		* TODO: Return an error?
   1064 		*/
   1065 		return;
   1066 	    }
   1067 	}
   1068     }
   1069     /*
   1070     * Parse/apply the list of QNames.
   1071     */
   1072     curstr = attrSets;
   1073     while (*curstr != 0) {
   1074         while (IS_BLANK(*curstr))
   1075             curstr++;
   1076         if (*curstr == 0)
   1077             break;
   1078         endstr = curstr;
   1079         while ((*endstr != 0) && (!IS_BLANK(*endstr)))
   1080             endstr++;
   1081         curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr);
   1082         if (curstr) {
   1083 	    /*
   1084 	    * TODO: Validate the QName.
   1085 	    */
   1086 
   1087 #ifdef WITH_XSLT_DEBUG_curstrUTES
   1088             xsltGenericDebug(xsltGenericDebugContext,
   1089                              "apply curstrute set %s\n", curstr);
   1090 #endif
   1091             ncname = xsltSplitQName(ctxt->dict, curstr, &prefix);
   1092 
   1093             style = ctxt->style;
   1094 
   1095 #ifdef WITH_DEBUGGER
   1096             if ((style != NULL) &&
   1097 		(style->attributeSets != NULL) &&
   1098 		(ctxt->debugStatus != XSLT_DEBUG_NONE))
   1099 	    {
   1100                 attrs =
   1101                     xmlHashLookup2(style->attributeSets, ncname, prefix);
   1102                 if ((attrs != NULL) && (attrs->attr != NULL))
   1103                     xslHandleDebugger(attrs->attr->parent, node, NULL,
   1104 			ctxt);
   1105             }
   1106 #endif
   1107 	    /*
   1108 	    * Lookup the referenced curstrute-set.
   1109 	    */
   1110             while (style != NULL) {
   1111                 attrs =
   1112                     xmlHashLookup2(style->attributeSets, ncname, prefix);
   1113                 while (attrs != NULL) {
   1114                     if (attrs->attr != NULL) {
   1115                         xsltAttributeInternal(ctxt, node, attrs->attr,
   1116 			    attrs->attr->psvi, 1);
   1117                     }
   1118                     attrs = attrs->next;
   1119                 }
   1120                 style = xsltNextImport(style);
   1121             }
   1122         }
   1123         curstr = endstr;
   1124     }
   1125 }
   1126 
   1127 /**
   1128  * xsltFreeAttributeSetsHashes:
   1129  * @style: an XSLT stylesheet
   1130  *
   1131  * Free up the memory used by attribute sets
   1132  */
   1133 void
   1134 xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
   1135     if (style->attributeSets != NULL)
   1136 	xmlHashFree((xmlHashTablePtr) style->attributeSets,
   1137 		    (xmlHashDeallocator) xsltFreeAttrElemList);
   1138     style->attributeSets = NULL;
   1139 }
   1140