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