Coverage Report

Created: 2025-08-26 07:06

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