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