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