85b2e54380c2e8992d496e8a73f274ab66a39338
[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
506     ret = xmlHashAddEntry2(style->attributeSets, name, ns, values);
507     if (ret < 0) {
508         /*
509          * Add failed, this attribute set can be removed.
510          */
511 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
512         xsltGenericDebug(xsltGenericDebugContext,
513                 "attribute sets %s present already in top stylesheet\n",
514                          name);
515 #endif
516         xsltFreeAttrElem(values);
517 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
518     } else {
519         xsltGenericDebug(xsltGenericDebugContext,
520                 "attribute sets %s moved to top stylesheet\n",
521                          name);
522 #endif
523     }
524 }
525
526 /**
527  * xsltResolveStylesheetAttributeSet:
528  * @style:  the XSLT stylesheet
529  *
530  * resolve the references between attribute sets.
531  */
532 void
533 xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
534     xsltStylesheetPtr cur;
535
536 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
537     xsltGenericDebug(xsltGenericDebugContext,
538             "Resolving attribute sets references\n");
539 #endif
540     /*
541      * First aggregate all the attribute sets definitions from the imports
542      */
543     cur = xsltNextImport(style);
544     while (cur != NULL) {
545         if (cur->attributeSets != NULL) {
546             if (style->attributeSets == NULL) {
547 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
548                 xsltGenericDebug(xsltGenericDebugContext,
549                     "creating attribute set table\n");
550 #endif
551                 style->attributeSets = xmlHashCreate(10);
552             }
553             xmlHashScanFull(cur->attributeSets, 
554                 (xmlHashScannerFull) xsltMergeSASCallback, style);
555             /*
556              * the attribute lists have either been migrated to style
557              * or freed directly in xsltMergeSASCallback()
558              */
559             xmlHashFree(cur->attributeSets, NULL);
560             cur->attributeSets = NULL;
561         }
562         cur = xsltNextImport(cur);
563     }
564
565     /*
566      * Then resolve all the references and computes the resulting sets
567      */
568     if (style->attributeSets != NULL) {
569         xmlHashScanFull(style->attributeSets, 
570                 (xmlHashScannerFull) xsltResolveSASCallback, style);
571     }
572 }
573
574 /**
575  * xsltAttributeInternal:
576  * @ctxt:  a XSLT process context
577  * @node:  the node in the source tree.
578  * @inst:  the xslt attribute node
579  * @comp:  precomputed information
580  * @fromset:  the attribute comes from an attribute-set
581  *
582  * Process the xslt attribute node on the source node
583  */
584 static void
585 xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node,
586                       xmlNodePtr inst, xsltStylePreCompPtr comp,
587                       int fromset)
588 {
589     xmlChar *prop = NULL;
590     xmlChar *ncname = NULL, *name, *namespace;
591     xmlChar *prefix = NULL;
592     xmlChar *value = NULL;
593     xmlNsPtr ns = NULL;
594     xmlAttrPtr attr;
595     const xmlChar *URL = NULL;
596
597
598     if (ctxt->insert == NULL)
599         return;
600     if (comp == NULL) {
601         xsltTransformError(ctxt, NULL, inst,
602                          "xsl:attribute : compilation failed\n");
603         return;
604     }
605
606     if ((ctxt == NULL) || (node == NULL) || (inst == NULL)
607         || (comp == NULL))
608         return;
609     if (!comp->has_name) {
610         return;
611     }
612     if (ctxt->insert->children != NULL) {
613         xsltTransformError(ctxt, NULL, inst,
614                          "xsl:attribute : node already has children\n");
615         return;
616     }
617 #ifdef WITH_DEBUGGER
618     if (xslDebugStatus != XSLT_DEBUG_NONE) {
619         xslHandleDebugger(inst, node, NULL, ctxt);
620     }
621 #endif
622
623     if (comp->name == NULL) {
624         prop =
625             xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *) "name",
626                                       XSLT_NAMESPACE);
627         if (prop == NULL) {
628             xsltTransformError(ctxt, NULL, inst,
629                              "xsl:attribute : name is missing\n");
630             goto error;
631         }
632         name = prop;
633     } else {
634         name = comp->name;
635     }
636
637     ncname = xmlSplitQName2(name, &prefix);
638     if (ncname == NULL) {
639         prefix = NULL;
640     } else {
641         name = ncname;
642     }
643     if (!xmlStrncasecmp(prefix, (xmlChar *) "xml", 3)) {
644 #ifdef WITH_XSLT_DEBUG_PARSING
645         xsltGenericDebug(xsltGenericDebugContext,
646                          "xsltAttribute: xml prefix forbidden\n");
647 #endif
648         goto error;
649     }
650     if ((comp->ns == NULL) && (comp->has_ns)) {
651         namespace = xsltEvalAttrValueTemplate(ctxt, inst,
652                                               (const xmlChar *)
653                                               "namespace", XSLT_NAMESPACE);
654         if (namespace != NULL) {
655             ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
656                                          ctxt->insert);
657             xmlFree(namespace);
658         } else {
659             if (prefix != NULL) {
660                 ns = xmlSearchNs(inst->doc, inst, prefix);
661                 if (ns == NULL) {
662                     xsltTransformError(ctxt, NULL, inst,
663                          "xsl:attribute : no namespace bound to prefix %s\n",
664                                      prefix);
665                 } else {
666                     ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
667                 }
668             }
669         }
670     } else if (comp->ns != NULL) {
671         ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
672                                      ctxt->insert);
673     } else if (prefix != NULL) {
674         xmlNsPtr tmp;
675         tmp = xmlSearchNs(inst->doc, inst, prefix);
676         if (tmp != NULL) {
677             ns = xsltGetNamespace(ctxt, inst, tmp, ctxt->insert);
678         }
679     }
680
681     if ((fromset) && (ns != NULL))
682         URL = ns->href;
683
684     if (fromset) {
685         if (URL != NULL)
686             attr = xmlHasNsProp(ctxt->insert, name, URL);
687         else
688             attr = xmlHasProp(ctxt->insert, name);
689         if (attr != NULL)
690             return;
691     }
692     value = xsltEvalTemplateString(ctxt, node, inst);
693     if (value == NULL) {
694         if (ns) {
695             attr = xmlSetNsProp(ctxt->insert, ns, name,
696                                 (const xmlChar *) "");
697         } else {
698             attr =
699                 xmlSetProp(ctxt->insert, name, (const xmlChar *) "");
700         }
701     } else {
702         if (ns) {
703             attr = xmlSetNsProp(ctxt->insert, ns, name, value);
704         } else {
705             attr = xmlSetProp(ctxt->insert, name, value);
706         }
707     }
708
709   error:
710     if (prop != NULL)
711         xmlFree(prop);
712     if (ncname != NULL)
713         xmlFree(ncname);
714     if (prefix != NULL)
715         xmlFree(prefix);
716     if (value != NULL)
717         xmlFree(value);
718 }
719
720 /**
721  * xsltAttribute:
722  * @ctxt:  a XSLT process context
723  * @node:  the node in the source tree.
724  * @inst:  the xslt attribute node
725  * @comp:  precomputed information
726  *
727  * Process the xslt attribute node on the source node
728  */
729 void
730 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
731               xmlNodePtr inst, xsltStylePreCompPtr comp) {
732     xsltAttributeInternal(ctxt, node, inst, comp, 0);
733 }
734
735 /**
736  * xsltApplyAttributeSet:
737  * @ctxt:  the XSLT stylesheet
738  * @node:  the node in the source tree.
739  * @inst:  the xslt attribute node
740  * @attributes:  the set list.
741  *
742  * Apply the xsl:use-attribute-sets
743  */
744
745 void
746 xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
747                       xmlNodePtr inst ATTRIBUTE_UNUSED,
748                       xmlChar * attributes)
749 {
750     xmlChar *ncname = NULL;
751     xmlChar *prefix = NULL;
752     xmlChar *attrib, *endattr;
753     xsltAttrElemPtr values;
754     xsltStylesheetPtr style;
755
756     if (attributes == NULL) {
757         return;
758     }
759
760     attrib = attributes;
761     while (*attrib != 0) {
762         while (IS_BLANK(*attrib))
763             attrib++;
764         if (*attrib == 0)
765             break;
766         endattr = attrib;
767         while ((*endattr != 0) && (!IS_BLANK(*endattr)))
768             endattr++;
769         attrib = xmlStrndup(attrib, endattr - attrib);
770         if (attrib) {
771 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
772             xsltGenericDebug(xsltGenericDebugContext,
773                              "apply attribute set %s\n", attrib);
774 #endif
775             ncname = xmlSplitQName2(attrib, &prefix);
776             if (ncname == NULL) {
777                 ncname = attrib;
778                 attrib = NULL;
779                 prefix = NULL;
780             }
781
782             style = ctxt->style;
783 #ifdef WITH_DEBUGGER
784             if ((style != NULL) && (style->attributeSets != NULL) &&
785                 (xslDebugStatus != XSLT_DEBUG_NONE)) {
786                 values =
787                     xmlHashLookup2(style->attributeSets, ncname, prefix);
788                 if ((values != NULL) && (values->attr != NULL))
789                     xslHandleDebugger(values->attr->parent, node, NULL,
790                                       ctxt);
791             }
792 #endif
793             while (style != NULL) {
794                 values =
795                     xmlHashLookup2(style->attributeSets, ncname, prefix);
796                 while (values != NULL) {
797                     if (values->attr != NULL) {
798                         xsltAttributeInternal(ctxt, node, values->attr,
799                                               values->attr->_private, 1);
800                     }
801                     values = values->next;
802                 }
803                 style = xsltNextImport(style);
804             }
805             if (attrib != NULL)
806                 xmlFree(attrib);
807             if (ncname != NULL)
808                 xmlFree(ncname);
809             if (prefix != NULL)
810                 xmlFree(prefix);
811         }
812         attrib = endattr;
813     }
814 }
815
816 /**
817  * xsltFreeAttributeSetsHashes:
818  * @style: an XSLT stylesheet
819  *
820  * Free up the memory used by attribute sets
821  */
822 void
823 xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
824     if (style->attributeSets != NULL)
825         xmlHashFree((xmlHashTablePtr) style->attributeSets,
826                     (xmlHashDeallocator) xsltFreeAttrElemList);
827     style->attributeSets = NULL;
828 }