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