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@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
85typedef struct _xsltAttrElem xsltAttrElem;
86typedef xsltAttrElem *xsltAttrElemPtr;
87struct _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 */
108static xsltAttrElemPtr
109xsltNewAttrElem(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 */
129static void
130xsltFreeAttrElem(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 */
140static void
141xsltFreeAttrElemList(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 */
165static xsltAttrElemPtr
166xsltAddAttrElemList(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 */
198static xsltAttrElemPtr
199xsltMergeAttrElemList(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
288void
289xsltParseStylesheetAttributeSet(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) || (cur->type != XML_ELEMENT_NODE))
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
400next_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 */
471static xsltAttrElemPtr
472xsltGetSAS(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 */
490static void
491xsltResolveSASCallback(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 */
545static void
546xsltMergeSASCallback(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 */
587void
588xsltResolveStylesheetAttributeSet(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 */
639static void
640xsltAttributeInternal(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        (inst->type != XML_ELEMENT_NODE) )
661        return;
662
663    /*
664    * A comp->has_name == 0 indicates that we need to skip this instruction,
665    * since it was evaluated to be invalid already during compilation.
666    */
667    if (!comp->has_name)
668        return;
669    /*
670    * BIG NOTE: This previously used xsltGetSpecialNamespace() and
671    *  xsltGetNamespace(), but since both are not appropriate, we
672    *  will process namespace lookup here to avoid adding yet another
673    *  ns-lookup function to namespaces.c.
674    */
675    /*
676    * SPEC XSLT 1.0: Error cases:
677    * - Creating nodes other than text nodes during the instantiation of
678    *   the content of the xsl:attribute element; implementations may
679    *   either signal the error or ignore the offending nodes."
680    */
681
682    if (comp == NULL) {
683        xsltTransformError(ctxt, NULL, inst,
684	    "Internal error in xsltAttributeInternal(): "
685	    "The XSLT 'attribute' instruction was not compiled.\n");
686        return;
687    }
688    /*
689    * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error?
690    *   So report an internal error?
691    */
692    if (ctxt->insert == NULL)
693        return;
694    /*
695    * SPEC XSLT 1.0:
696    *  "Adding an attribute to a node that is not an element;
697    *  implementations may either signal the error or ignore the attribute."
698    *
699    * TODO: I think we should signal such errors in the future, and maybe
700    *  provide an option to ignore such errors.
701    */
702    targetElem = ctxt->insert;
703    if (targetElem->type != XML_ELEMENT_NODE)
704	return;
705
706    /*
707    * SPEC XSLT 1.0:
708    * "Adding an attribute to an element after children have been added
709    *  to it; implementations may either signal the error or ignore the
710    *  attribute."
711    *
712    * TODO: We should decide whether not to report such errors or
713    *  to ignore them; note that we *ignore* if the parent is not an
714    *  element, but here we report an error.
715    */
716    if (targetElem->children != NULL) {
717	/*
718	* NOTE: Ah! This seems to be intended to support streamed
719	*  result generation!.
720	*/
721        xsltTransformError(ctxt, NULL, inst,
722	    "xsl:attribute: Cannot add attributes to an "
723	    "element if children have been already added "
724	    "to the element.\n");
725        return;
726    }
727
728    /*
729    * Process the name
730    * ----------------
731    */
732
733#ifdef WITH_DEBUGGER
734    if (ctxt->debugStatus != XSLT_DEBUG_NONE)
735        xslHandleDebugger(inst, contextNode, NULL, ctxt);
736#endif
737
738    if (comp->name == NULL) {
739	/* TODO: fix attr acquisition wrt to the XSLT namespace */
740        prop = xsltEvalAttrValueTemplate(ctxt, inst,
741	    (const xmlChar *) "name", XSLT_NAMESPACE);
742        if (prop == NULL) {
743            xsltTransformError(ctxt, NULL, inst,
744		"xsl:attribute: The attribute 'name' is missing.\n");
745            goto error;
746        }
747	if (xmlValidateQName(prop, 0)) {
748	    xsltTransformError(ctxt, NULL, inst,
749		"xsl:attribute: The effective name '%s' is not a "
750		"valid QName.\n", prop);
751	    /* we fall through to catch any further errors, if possible */
752	}
753
754	/*
755	* Reject a name of "xmlns".
756	*/
757	if (xmlStrEqual(prop, BAD_CAST "xmlns")) {
758            xsltTransformError(ctxt, NULL, inst,
759                "xsl:attribute: The effective name 'xmlns' is not allowed.\n");
760	    xmlFree(prop);
761	    goto error;
762	}
763
764	name = xsltSplitQName(ctxt->dict, prop, &prefix);
765	xmlFree(prop);
766    } else {
767	/*
768	* The "name" value was static.
769	*/
770#ifdef XSLT_REFACTORED
771	prefix = comp->nsPrefix;
772	name = comp->name;
773#else
774	name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
775#endif
776    }
777
778    /*
779    * Process namespace semantics
780    * ---------------------------
781    *
782    * Evaluate the namespace name.
783    */
784    if (comp->has_ns) {
785	/*
786	* The "namespace" attribute was existent.
787	*/
788	if (comp->ns != NULL) {
789	    /*
790	    * No AVT; just plain text for the namespace name.
791	    */
792	    if (comp->ns[0] != 0)
793		nsName = comp->ns;
794	} else {
795	    xmlChar *tmpNsName;
796	    /*
797	    * Eval the AVT.
798	    */
799	    /* TODO: check attr acquisition wrt to the XSLT namespace */
800	    tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
801		(const xmlChar *) "namespace", XSLT_NAMESPACE);
802	    /*
803	    * This fixes bug #302020: The AVT might also evaluate to the
804	    * empty string; this means that the empty string also indicates
805	    * "no namespace".
806	    * SPEC XSLT 1.0:
807	    *  "If the string is empty, then the expanded-name of the
808	    *  attribute has a null namespace URI."
809	    */
810	    if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
811		nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
812	    xmlFree(tmpNsName);
813	}
814
815        if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) {
816            xsltTransformError(ctxt, NULL, inst,
817                "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ "
818                "forbidden.\n");
819            goto error;
820        }
821        if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) {
822            prefix = BAD_CAST "xml";
823        } else if (xmlStrEqual(prefix, BAD_CAST "xml")) {
824            prefix = NULL;
825        }
826    } else if (prefix != NULL) {
827	/*
828	* SPEC XSLT 1.0:
829	*  "If the namespace attribute is not present, then the QName is
830	*  expanded into an expanded-name using the namespace declarations
831	*  in effect for the xsl:attribute element, *not* including any
832	*  default namespace declaration."
833	*/
834	ns = xmlSearchNs(inst->doc, inst, prefix);
835	if (ns == NULL) {
836	    /*
837	    * Note that this is treated as an error now (checked with
838	    *  Saxon, Xalan-J and MSXML).
839	    */
840	    xsltTransformError(ctxt, NULL, inst,
841		"xsl:attribute: The QName '%s:%s' has no "
842		"namespace binding in scope in the stylesheet; "
843		"this is an error, since the namespace was not "
844		"specified by the instruction itself.\n", prefix, name);
845	} else
846	    nsName = ns->href;
847    }
848
849    if (fromAttributeSet) {
850	/*
851	* This tries to ensure that xsl:attribute(s) coming
852	* from an xsl:attribute-set won't override attribute of
853	* literal result elements or of explicit xsl:attribute(s).
854	* URGENT TODO: This might be buggy, since it will miss to
855	*  overwrite two equal attributes both from attribute sets.
856	*/
857	attr = xmlHasNsProp(targetElem, name, nsName);
858	if (attr != NULL)
859	    return;
860    }
861
862    /*
863    * Find/create a matching ns-decl in the result tree.
864    */
865    ns = NULL;
866
867#if 0
868    if (0) {
869	/*
870	* OPTIMIZE TODO: How do we know if we are adding to a
871	*  fragment or to the result tree?
872	*
873	* If we are adding to a result tree fragment (i.e., not to the
874	* actual result tree), we'll don't bother searching for the
875	* ns-decl, but just store it in the dummy-doc of the result
876	* tree fragment.
877	*/
878	if (nsName != NULL) {
879	    /*
880	    * TODO: Get the doc of @targetElem.
881	    */
882	    ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix);
883	}
884    }
885#endif
886
887    if (nsName != NULL) {
888	/*
889	* Something about ns-prefixes:
890	* SPEC XSLT 1.0:
891	*  "XSLT processors may make use of the prefix of the QName specified
892	*  in the name attribute when selecting the prefix used for outputting
893	*  the created attribute as XML; however, they are not required to do
894	*  so and, if the prefix is xmlns, they must not do so"
895	*/
896	/*
897	* xsl:attribute can produce a scenario where the prefix is NULL,
898	* so generate a prefix.
899	*/
900	if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) {
901	    xmlChar *pref = xmlStrdup(BAD_CAST "ns_1");
902
903	    ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, 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
1007error:
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 */
1020void
1021xsltAttribute(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 */
1038void
1039xsltApplyAttributeSet(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 */
1133void
1134xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
1135    if (style->attributeSets != NULL)
1136	xmlHashFree((xmlHashTablePtr) style->attributeSets,
1137		    (xmlHashDeallocator) xsltFreeAttrElemList);
1138    style->attributeSets = NULL;
1139}
1140