enhanced previous fix to bug #120684, using excellent suggestion by Daniel
[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 "xslt.h"
42 #include "xsltInternals.h"
43 #include "xsltutils.h"
44 #include "attributes.h"
45 #include "namespaces.h"
46 #include "templates.h"
47 #include "imports.h"
48 #include "transform.h"
49
50 #define WITH_XSLT_DEBUG_ATTRIBUTES
51 #ifdef WITH_XSLT_DEBUG
52 #define WITH_XSLT_DEBUG_ATTRIBUTES
53 #endif
54
55 /*
56  * TODO: merge attribute sets from different import precedence.
57  *       all this should be precomputed just before the transformation
58  *       starts or at first hit with a cache in the context.
59  *       The simple way for now would be to not allow redefinition of
60  *       attributes once generated in the output tree, possibly costlier.
61  */
62
63 /*
64  * Useful macros
65  */
66
67 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) ||  \
68                      ((c) == 0x0D))
69
70 #define IS_BLANK_NODE(n)                                                \
71     (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
72
73
74 /*
75  * The in-memory structure corresponding to an XSLT Attribute in
76  * an attribute set
77  */
78
79
80 typedef struct _xsltAttrElem xsltAttrElem;
81 typedef xsltAttrElem *xsltAttrElemPtr;
82 struct _xsltAttrElem {
83     struct _xsltAttrElem *next;/* chained list */
84     xmlNodePtr attr;    /* the xsl:attribute definition */
85     const xmlChar *set; /* or the attribute set */
86     const xmlChar *ns;  /* and its namespace */
87 };
88
89 /************************************************************************
90  *                                                                      *
91  *                      XSLT Attribute handling                         *
92  *                                                                      *
93  ************************************************************************/
94
95 /**
96  * xsltNewAttrElem:
97  * @attr:  the new xsl:attribute node
98  *
99  * Create a new XSLT AttrElem
100  *
101  * Returns the newly allocated xsltAttrElemPtr or NULL in case of error
102  */
103 static xsltAttrElemPtr
104 xsltNewAttrElem(xmlNodePtr attr) {
105     xsltAttrElemPtr cur;
106
107     cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
108     if (cur == NULL) {
109         xsltGenericError(xsltGenericErrorContext,
110                 "xsltNewAttrElem : malloc failed\n");
111         return(NULL);
112     }
113     memset(cur, 0, sizeof(xsltAttrElem));
114     cur->attr = attr;
115     return(cur);
116 }
117
118 /**
119  * xsltFreeAttrElem:
120  * @attr:  an XSLT AttrElem
121  *
122  * Free up the memory allocated by @attr
123  */
124 static void
125 xsltFreeAttrElem(xsltAttrElemPtr attr) {
126     if (attr->set != NULL)
127         xmlFree((char *)attr->set);
128     if (attr->ns != NULL)
129         xmlFree((char *)attr->ns);
130     xmlFree(attr);
131 }
132
133 /**
134  * xsltFreeAttrElemList:
135  * @list:  an XSLT AttrElem list
136  *
137  * Free up the memory allocated by @list
138  */
139 static void
140 xsltFreeAttrElemList(xsltAttrElemPtr list) {
141     xsltAttrElemPtr next;
142     
143     while (list != NULL) {
144         next = list->next;
145         xsltFreeAttrElem(list);
146         list = next;
147     }
148 }
149
150 /**
151  * xsltAddAttrElemList:
152  * @list:  an XSLT AttrElem list
153  * @attr:  the new xsl:attribute node
154  *
155  * Add the new attribute to the list.
156  *
157  * Returns the new list pointer
158  */
159 static xsltAttrElemPtr
160 xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
161     xsltAttrElemPtr next, cur;
162
163     if (attr == NULL)
164         return(list);
165     if (list == NULL)
166         return(xsltNewAttrElem(attr));
167     cur = list;
168     while (cur != NULL) {
169         next = cur->next;
170         if (cur->attr == attr)
171             return(cur);
172         if (cur->next == NULL) {
173             cur->next = xsltNewAttrElem(attr);
174             return(list);
175         }
176         cur = next;
177     }
178     return(list);
179 }
180
181 /**
182  * xsltMergeAttrElemList:
183  * @list:  an XSLT AttrElem list
184  * @old:  another XSLT AttrElem list
185  *
186  * Add all the attributes from list @old to list @list,
187  * but drop redefinition of existing values.
188  *
189  * Returns the new list pointer
190  */
191 static xsltAttrElemPtr
192 xsltMergeAttrElemList(xsltAttrElemPtr list, xsltAttrElemPtr old) {
193     xsltAttrElemPtr cur;
194     int add;
195
196     while (old != NULL) {
197         if ((old->attr == NULL) && (old->set == NULL)) {
198             old = old->next;
199             continue;
200         }
201         /*
202          * Check that the attribute is not yet in the list
203          */
204         cur = list;
205         add = 1;
206         while (cur != NULL) {
207             if ((cur->attr == NULL) && (cur->set == NULL)) {
208                 if (cur->next == NULL)
209                     break;
210                 cur = cur->next;
211                 continue;
212             }
213             if ((cur->set != NULL) && (cur->set == old->set)) {
214                 add = 0;
215                 break;
216             }
217             if (cur->set != NULL) {
218                 if (cur->next == NULL)
219                     break;
220                 cur = cur->next;
221                 continue;
222             }
223             if (old->set != NULL) {
224                 if (cur->next == NULL)
225                     break;
226                 cur = cur->next;
227                 continue;
228             }
229             if (cur->attr == old->attr) {
230                 xsltGenericError(xsltGenericErrorContext,
231              "xsl:attribute-set : use-attribute-sets recursion detected\n");
232                 return(list);
233             }
234             if (cur->next == NULL)
235                 break;
236             cur = cur->next;
237         }
238
239         if (add == 1) {
240             if (cur == NULL) {
241                 list = xsltNewAttrElem(old->attr);
242                 if (old->set != NULL) {
243                     list->set = xmlStrdup(old->set);
244                     if (old->ns != NULL)
245                         list->ns = xmlStrdup(old->ns);
246                 }
247             } else if (add) {
248                 cur->next = xsltNewAttrElem(old->attr);
249                 if (old->set != NULL) {
250                     cur->next->set = xmlStrdup(old->set);
251                     if (old->ns != NULL)
252                         cur->next->ns = xmlStrdup(old->ns);
253                 }
254             }
255         }
256
257         old = old->next;
258     }
259     return(list);
260 }
261
262 /************************************************************************
263  *                                                                      *
264  *                      Module interfaces                               *
265  *                                                                      *
266  ************************************************************************/
267
268 /**
269  * xsltParseStylesheetAttributeSet:
270  * @style:  the XSLT stylesheet
271  * @cur:  the "attribute-set" element
272  *
273  * parse an XSLT stylesheet attribute-set element
274  */
275
276 void
277 xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
278     xmlChar *prop = NULL;
279     xmlChar *ncname = NULL;
280     xmlChar *prefix = NULL;
281     xmlChar *attributes;
282     xmlChar *attrib, *endattr;
283     xmlNodePtr list;
284     xsltAttrElemPtr values;
285
286     if ((cur == NULL) || (style == NULL))
287         return;
288
289     prop = xsltGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
290     if (prop == NULL) {
291         xsltGenericError(xsltGenericErrorContext,
292              "xsl:attribute-set : name is missing\n");
293         goto error;
294     }
295
296     ncname = xmlSplitQName2(prop, &prefix);
297     if (ncname == NULL) {
298         ncname = prop;
299         prop = NULL;
300         prefix = NULL;
301     }
302
303     if (style->attributeSets == NULL) {
304 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
305         xsltGenericDebug(xsltGenericDebugContext,
306             "creating attribute set table\n");
307 #endif
308         style->attributeSets = xmlHashCreate(10);
309     }
310     if (style->attributeSets == NULL)
311         goto error;
312
313     values = xmlHashLookup2(style->attributeSets, ncname, prefix);
314
315     /*
316      * check the children list
317      */
318     list = cur->children;
319     while (list != NULL) {
320         if (IS_XSLT_ELEM(list)) {
321             if (!IS_XSLT_NAME(list, "attribute")) {
322                 xsltGenericError(xsltGenericErrorContext,
323                     "xsl:attribute-set : unexpected child xsl:%s\n",
324                                  list->name);
325             } else {
326 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
327                 xsltGenericDebug(xsltGenericDebugContext,
328                     "add attribute to list %s\n", ncname);
329 #endif
330                 values = xsltAddAttrElemList(values, list);
331             }
332         } else {
333             xsltGenericError(xsltGenericErrorContext,
334                 "xsl:attribute-set : unexpected child %s\n", list->name);
335         }
336         list = list->next;
337     }
338
339     /*
340      * Check a possible use-attribute-sets definition
341      */
342     /* TODO check recursion */
343
344     attributes = xsltGetNsProp(cur, (const xmlChar *)"use-attribute-sets",
345                               XSLT_NAMESPACE);
346     if (attributes == NULL) {
347         goto done;
348     }
349
350     attrib = attributes;
351     while (*attrib != 0) {
352         while (IS_BLANK(*attrib)) attrib++;
353         if (*attrib == 0)
354             break;
355         endattr = attrib;
356         while ((*endattr != 0) && (!IS_BLANK(*endattr))) endattr++;
357         attrib = xmlStrndup(attrib, endattr - attrib);
358         if (attrib) {
359             xmlChar *ncname2 = NULL;
360             xmlChar *prefix2 = NULL;
361             xsltAttrElemPtr values2;
362 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
363             xsltGenericDebug(xsltGenericDebugContext,
364                 "xsl:attribute-set : %s adds use %s\n", ncname, attrib);
365 #endif
366             ncname2 = xmlSplitQName2(attrib, &prefix2);
367             if (ncname2 == NULL) {
368                 ncname2 = attrib;
369                 attrib = NULL;
370                 prefix = NULL;
371             }
372             values2 = xsltNewAttrElem(NULL);
373             if (values2 != NULL) {
374                 values2->set = ncname2;
375                 values2->ns = prefix2;
376                 values = xsltMergeAttrElemList(values, values2);
377                 xsltFreeAttrElem(values2);
378             } else {
379                 if (ncname2 != NULL)
380                     xmlFree(ncname2);
381                 if (prefix2 != NULL)
382                     xmlFree(prefix2);
383             }
384
385             if (attrib != NULL)
386                 xmlFree(attrib);
387         }
388         attrib = endattr;
389     }
390     xmlFree(attributes);
391
392 done:
393     /*
394      * Update the value
395      */
396     if (values == NULL)
397         values = xsltNewAttrElem(NULL);
398     xmlHashUpdateEntry2(style->attributeSets, ncname, prefix, values, NULL);
399 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
400     xsltGenericDebug(xsltGenericDebugContext,
401         "updated attribute list %s\n", ncname);
402 #endif
403
404 error:
405     if (prop != NULL)
406         xmlFree(prop);
407     if (ncname != NULL)
408         xmlFree(ncname);
409     if (prefix != NULL)
410         xmlFree(prefix);
411 }
412
413 /**
414  * xsltGetSAS:
415  * @style:  the XSLT stylesheet
416  * @name:  the attribute list name
417  * @ns:  the attribute list namespace
418  *
419  * lookup an attribute set based on the style cascade
420  *
421  * Returns the attribute set or NULL
422  */
423 static xsltAttrElemPtr
424 xsltGetSAS(xsltStylesheetPtr style, const xmlChar *name, const xmlChar *ns) {
425     xsltAttrElemPtr values;
426
427     while (style != NULL) {
428         values = xmlHashLookup2(style->attributeSets, name, ns);
429         if (values != NULL)
430             return(values);
431         style = xsltNextImport(style);
432     }
433     return(NULL);
434 }
435
436 /**
437  * xsltResolveSASCallback,:
438  * @style:  the XSLT stylesheet
439  *
440  * resolve the references in an attribute set.
441  */
442 static void
443 xsltResolveSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style,
444                        const xmlChar *name, const xmlChar *ns,
445                        ATTRIBUTE_UNUSED const xmlChar *ignored) {
446     xsltAttrElemPtr tmp;
447     xsltAttrElemPtr refs;
448
449     tmp = values;
450     while (tmp != NULL) {
451         if (tmp->set != NULL) {
452             /*
453              * Check against cycles !
454              */
455             if ((xmlStrEqual(name, tmp->set)) && (xmlStrEqual(ns, tmp->ns))) {
456                 xsltGenericError(xsltGenericErrorContext,
457      "xsl:attribute-set : use-attribute-sets recursion detected on %s\n",
458                                  name);
459             } else {
460 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
461                 xsltGenericDebug(xsltGenericDebugContext,
462                         "Importing attribute list %s\n", tmp->set);
463 #endif
464
465                 refs = xsltGetSAS(style, tmp->set, tmp->ns);
466                 if (refs == NULL) {
467                     xsltGenericError(xsltGenericErrorContext,
468      "xsl:attribute-set : use-attribute-sets %s reference missing %s\n",
469                                      name, tmp->set);
470                 } else {
471                     /*
472                      * recurse first for cleanup
473                      */
474                     xsltResolveSASCallback(refs, style, name, ns, NULL);
475                     /*
476                      * Then merge
477                      */
478                     xsltMergeAttrElemList(values, refs);
479                     /*
480                      * Then suppress the reference
481                      */
482                     xmlFree((char *)tmp->set);
483                     tmp->set = NULL;
484                     if (tmp->ns != NULL) {
485                         xmlFree((char *)tmp->ns);
486                     }
487                 }
488             }
489         }
490         tmp = tmp->next;
491     }
492 }
493
494 /**
495  * xsltMergeSASCallback,:
496  * @style:  the XSLT stylesheet
497  *
498  * Merge an attribute set from an imported stylesheet.
499  */
500 static void
501 xsltMergeSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style,
502                        const xmlChar *name, const xmlChar *ns,
503                        ATTRIBUTE_UNUSED const xmlChar *ignored) {
504     int ret;
505     xsltAttrElemPtr topSet;
506
507     ret = xmlHashAddEntry2(style->attributeSets, name, ns, values);
508     if (ret < 0) {
509         /*
510          * Add failed, this attribute set can be removed.
511          */
512 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
513         xsltGenericDebug(xsltGenericDebugContext,
514                 "attribute set %s present already in top stylesheet"
515                 " - merging\n", name);
516 #endif
517         topSet = xmlHashLookup2(style->attributeSets, name, ns);
518         if (topSet==NULL) {
519             xsltGenericError(xsltGenericErrorContext,
520                 "xsl:attribute-set : logic error merging from imports for"
521                 " attribute-set %s\n", name);
522         } else {
523             topSet = xsltMergeAttrElemList(topSet, values);
524             xmlHashUpdateEntry2(style->attributeSets, name, ns, topSet, NULL);
525         }
526         xsltFreeAttrElemList(values);
527 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
528     } else {
529         xsltGenericDebug(xsltGenericDebugContext,
530                 "attribute set %s moved to top stylesheet\n",
531                          name);
532 #endif
533     }
534 }
535
536 /**
537  * xsltResolveStylesheetAttributeSet:
538  * @style:  the XSLT stylesheet
539  *
540  * resolve the references between attribute sets.
541  */
542 void
543 xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
544     xsltStylesheetPtr cur;
545
546 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
547     xsltGenericDebug(xsltGenericDebugContext,
548             "Resolving attribute sets references\n");
549 #endif
550     /*
551      * First aggregate all the attribute sets definitions from the imports
552      */
553     cur = xsltNextImport(style);
554     while (cur != NULL) {
555         if (cur->attributeSets != NULL) {
556             if (style->attributeSets == NULL) {
557 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
558                 xsltGenericDebug(xsltGenericDebugContext,
559                     "creating attribute set table\n");
560 #endif
561                 style->attributeSets = xmlHashCreate(10);
562             }
563             xmlHashScanFull(cur->attributeSets, 
564                 (xmlHashScannerFull) xsltMergeSASCallback, style);
565             /*
566              * the attribute lists have either been migrated to style
567              * or freed directly in xsltMergeSASCallback()
568              */
569             xmlHashFree(cur->attributeSets, NULL);
570             cur->attributeSets = NULL;
571         }
572         cur = xsltNextImport(cur);
573     }
574
575     /*
576      * Then resolve all the references and computes the resulting sets
577      */
578     if (style->attributeSets != NULL) {
579         xmlHashScanFull(style->attributeSets, 
580                 (xmlHashScannerFull) xsltResolveSASCallback, style);
581     }
582 }
583
584 /**
585  * xsltAttributeInternal:
586  * @ctxt:  a XSLT process context
587  * @node:  the node in the source tree.
588  * @inst:  the xslt attribute node
589  * @comp:  precomputed information
590  * @fromset:  the attribute comes from an attribute-set
591  *
592  * Process the xslt attribute node on the source node
593  */
594 static void
595 xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node,
596                       xmlNodePtr inst, xsltStylePreCompPtr comp,
597                       int fromset)
598 {
599     xmlChar *prop = NULL;
600     xmlChar *ncname = NULL, *name, *namespace;
601     xmlChar *prefix = NULL;
602     xmlChar *value = NULL;
603     xmlNsPtr ns = NULL;
604     xmlAttrPtr attr;
605     const xmlChar *URL = NULL;
606
607
608     if (ctxt->insert == NULL)
609         return;
610     if (comp == NULL) {
611         xsltTransformError(ctxt, NULL, inst,
612                          "xsl:attribute : compilation failed\n");
613         return;
614     }
615
616     if ((ctxt == NULL) || (node == NULL) || (inst == NULL)
617         || (comp == NULL))
618         return;
619     if (!comp->has_name) {
620         return;
621     }
622     if (ctxt->insert->children != NULL) {
623         xsltTransformError(ctxt, NULL, inst,
624                          "xsl:attribute : node already has children\n");
625         return;
626     }
627 #ifdef WITH_DEBUGGER
628     if (xslDebugStatus != XSLT_DEBUG_NONE) {
629         xslHandleDebugger(inst, node, NULL, ctxt);
630     }
631 #endif
632
633     if (comp->name == NULL) {
634         prop =
635             xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *) "name",
636                                       XSLT_NAMESPACE);
637         if (prop == NULL) {
638             xsltTransformError(ctxt, NULL, inst,
639                              "xsl:attribute : name is missing\n");
640             goto error;
641         }
642         name = prop;
643     } else {
644         name = comp->name;
645     }
646
647     ncname = xmlSplitQName2(name, &prefix);
648     if (ncname == NULL) {
649         prefix = NULL;
650     } else {
651         name = ncname;
652     }
653     if (!xmlStrncasecmp(prefix, (xmlChar *) "xml", 3)) {
654 #ifdef WITH_XSLT_DEBUG_PARSING
655         xsltGenericDebug(xsltGenericDebugContext,
656                          "xsltAttribute: xml prefix forbidden\n");
657 #endif
658         goto error;
659     }
660     if ((comp->ns == NULL) && (comp->has_ns)) {
661         namespace = xsltEvalAttrValueTemplate(ctxt, inst,
662                                               (const xmlChar *)
663                                               "namespace", XSLT_NAMESPACE);
664         if (namespace != NULL) {
665             ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
666                                          ctxt->insert);
667             xmlFree(namespace);
668         } else {
669             if (prefix != NULL) {
670                 ns = xmlSearchNs(inst->doc, inst, prefix);
671                 if (ns == NULL) {
672                     xsltTransformError(ctxt, NULL, inst,
673                          "xsl:attribute : no namespace bound to prefix %s\n",
674                                      prefix);
675                 } else {
676                     ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
677                 }
678             }
679         }
680     } else if (comp->ns != NULL) {
681         ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
682                                      ctxt->insert);
683     } else if (prefix != NULL) {
684         xmlNsPtr tmp;
685         tmp = xmlSearchNs(inst->doc, inst, prefix);
686         if (tmp != NULL) {
687             ns = xsltGetNamespace(ctxt, inst, tmp, ctxt->insert);
688         }
689     }
690
691     if ((fromset) && (ns != NULL))
692         URL = ns->href;
693
694     if (fromset) {
695         if (URL != NULL)
696             attr = xmlHasNsProp(ctxt->insert, name, URL);
697         else
698             attr = xmlHasProp(ctxt->insert, name);
699         if (attr != NULL)
700             return;
701     }
702     value = xsltEvalTemplateString(ctxt, node, inst);
703     if (value == NULL) {
704         if (ns) {
705             attr = xmlSetNsProp(ctxt->insert, ns, name,
706                                 (const xmlChar *) "");
707         } else {
708             attr =
709                 xmlSetProp(ctxt->insert, name, (const xmlChar *) "");
710         }
711     } else {
712         if (ns) {
713             attr = xmlSetNsProp(ctxt->insert, ns, name, value);
714         } else {
715             attr = xmlSetProp(ctxt->insert, name, value);
716         }
717     }
718
719   error:
720     if (prop != NULL)
721         xmlFree(prop);
722     if (ncname != NULL)
723         xmlFree(ncname);
724     if (prefix != NULL)
725         xmlFree(prefix);
726     if (value != NULL)
727         xmlFree(value);
728 }
729
730 /**
731  * xsltAttribute:
732  * @ctxt:  a XSLT process context
733  * @node:  the node in the source tree.
734  * @inst:  the xslt attribute node
735  * @comp:  precomputed information
736  *
737  * Process the xslt attribute node on the source node
738  */
739 void
740 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
741               xmlNodePtr inst, xsltStylePreCompPtr comp) {
742     xsltAttributeInternal(ctxt, node, inst, comp, 0);
743 }
744
745 /**
746  * xsltApplyAttributeSet:
747  * @ctxt:  the XSLT stylesheet
748  * @node:  the node in the source tree.
749  * @inst:  the xslt attribute node
750  * @attributes:  the set list.
751  *
752  * Apply the xsl:use-attribute-sets
753  */
754
755 void
756 xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
757                       xmlNodePtr inst ATTRIBUTE_UNUSED,
758                       xmlChar * attributes)
759 {
760     xmlChar *ncname = NULL;
761     xmlChar *prefix = NULL;
762     xmlChar *attrib, *endattr;
763     xsltAttrElemPtr values;
764     xsltStylesheetPtr style;
765
766     if (attributes == NULL) {
767         return;
768     }
769
770     attrib = attributes;
771     while (*attrib != 0) {
772         while (IS_BLANK(*attrib))
773             attrib++;
774         if (*attrib == 0)
775             break;
776         endattr = attrib;
777         while ((*endattr != 0) && (!IS_BLANK(*endattr)))
778             endattr++;
779         attrib = xmlStrndup(attrib, endattr - attrib);
780         if (attrib) {
781 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
782             xsltGenericDebug(xsltGenericDebugContext,
783                              "apply attribute set %s\n", attrib);
784 #endif
785             ncname = xmlSplitQName2(attrib, &prefix);
786             if (ncname == NULL) {
787                 ncname = attrib;
788                 attrib = NULL;
789                 prefix = NULL;
790             }
791
792             style = ctxt->style;
793 #ifdef WITH_DEBUGGER
794             if ((style != NULL) && (style->attributeSets != NULL) &&
795                 (xslDebugStatus != XSLT_DEBUG_NONE)) {
796                 values =
797                     xmlHashLookup2(style->attributeSets, ncname, prefix);
798                 if ((values != NULL) && (values->attr != NULL))
799                     xslHandleDebugger(values->attr->parent, node, NULL,
800                                       ctxt);
801             }
802 #endif
803             while (style != NULL) {
804                 values =
805                     xmlHashLookup2(style->attributeSets, ncname, prefix);
806                 while (values != NULL) {
807                     if (values->attr != NULL) {
808                         xsltAttributeInternal(ctxt, node, values->attr,
809                                               values->attr->_private, 1);
810                     }
811                     values = values->next;
812                 }
813                 style = xsltNextImport(style);
814             }
815             if (attrib != NULL)
816                 xmlFree(attrib);
817             if (ncname != NULL)
818                 xmlFree(ncname);
819             if (prefix != NULL)
820                 xmlFree(prefix);
821         }
822         attrib = endattr;
823     }
824 }
825
826 /**
827  * xsltFreeAttributeSetsHashes:
828  * @style: an XSLT stylesheet
829  *
830  * Free up the memory used by attribute sets
831  */
832 void
833 xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
834     if (style->attributeSets != NULL)
835         xmlHashFree((xmlHashTablePtr) style->attributeSets,
836                     (xmlHashDeallocator) xsltFreeAttrElemList);
837     style->attributeSets = NULL;
838 }