Coverage Report

Created: 2025-06-22 06:55

/src/libxslt/libxslt/attrvt.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * attrvt.c: Implementation of the XSL Transformation 1.0 engine
3
 *           attribute value template handling part.
4
 *
5
 * References:
6
 *   http://www.w3.org/TR/1999/REC-xslt-19991116
7
 *
8
 *   Michael Kay "XSLT Programmer's Reference" pp 637-643
9
 *   Writing Multiple Output Files
10
 *
11
 * See Copyright for the status of this software.
12
 *
13
 * daniel@veillard.com
14
 */
15
16
#define IN_LIBXSLT
17
#include "libxslt.h"
18
19
#include <string.h>
20
21
#include <libxml/xmlmemory.h>
22
#include <libxml/tree.h>
23
#include <libxml/xpath.h>
24
#include <libxml/xpathInternals.h>
25
#include "xslt.h"
26
#include "xsltutils.h"
27
#include "xsltInternals.h"
28
#include "templates.h"
29
30
#ifdef WITH_XSLT_DEBUG
31
#define WITH_XSLT_DEBUG_AVT
32
#endif
33
34
0
#define MAX_AVT_SEG 10
35
36
typedef struct _xsltAttrVT xsltAttrVT;
37
typedef xsltAttrVT *xsltAttrVTPtr;
38
struct _xsltAttrVT {
39
    struct _xsltAttrVT *next; /* next xsltAttrVT */
40
    int nb_seg;   /* Number of segments */
41
    int max_seg;  /* max capacity before re-alloc needed */
42
    int strstart; /* is the start a string */
43
    /*
44
     * the namespaces in scope
45
     */
46
    xmlNsPtr *nsList;
47
    int nsNr;
48
    /*
49
     * the content is an alternate of string and xmlXPathCompExprPtr
50
     */
51
#if __STDC_VERSION__ >= 199901L
52
    /* Using a C99 flexible array member avoids false positives under UBSan */
53
    void *segments[];
54
#else
55
    void *segments[1];
56
#endif
57
};
58
59
/**
60
 * xsltNewAttrVT:
61
 * @style:  a XSLT process context
62
 *
63
 * Build a new xsltAttrVT structure
64
 *
65
 * Returns the structure or NULL in case of error
66
 */
67
static xsltAttrVTPtr
68
0
xsltNewAttrVT(xsltStylesheetPtr style) {
69
0
    xsltAttrVTPtr cur;
70
0
    size_t size = sizeof(xsltAttrVT) + MAX_AVT_SEG * sizeof(void*);
71
72
0
    cur = (xsltAttrVTPtr) xmlMalloc(size);
73
0
    if (cur == NULL) {
74
0
  xsltTransformError(NULL, style, NULL,
75
0
    "xsltNewAttrVTPtr : malloc failed\n");
76
0
  if (style != NULL) style->errors++;
77
0
  return(NULL);
78
0
    }
79
0
    memset(cur, 0, size);
80
81
0
    cur->nb_seg = 0;
82
0
    cur->max_seg = MAX_AVT_SEG;
83
0
    cur->strstart = 0;
84
0
    cur->next = style->attVTs;
85
    /*
86
     * Note: this pointer may be changed by a re-alloc within xsltCompileAttr,
87
     * so that code may change the stylesheet pointer also!
88
     */
89
0
    style->attVTs = (xsltAttrVTPtr) cur;
90
91
0
    return(cur);
92
0
}
93
94
/**
95
 * xsltFreeAttrVT:
96
 * @avt: pointer to an xsltAttrVT structure
97
 *
98
 * Free up the memory associated to the attribute value template
99
 */
100
static void
101
0
xsltFreeAttrVT(xsltAttrVTPtr avt) {
102
0
    int i;
103
104
0
    if (avt == NULL) return;
105
106
0
    if (avt->strstart == 1) {
107
0
  for (i = 0;i < avt->nb_seg; i += 2)
108
0
      if (avt->segments[i] != NULL)
109
0
    xmlFree((xmlChar *) avt->segments[i]);
110
0
  for (i = 1;i < avt->nb_seg; i += 2)
111
0
      xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
112
0
    } else {
113
0
  for (i = 0;i < avt->nb_seg; i += 2)
114
0
      xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
115
0
  for (i = 1;i < avt->nb_seg; i += 2)
116
0
      if (avt->segments[i] != NULL)
117
0
    xmlFree((xmlChar *) avt->segments[i]);
118
0
    }
119
0
    if (avt->nsList != NULL)
120
0
        xmlFree(avt->nsList);
121
0
    xmlFree(avt);
122
0
}
123
124
/**
125
 * xsltFreeAVTList:
126
 * @avt: pointer to an list of AVT structures
127
 *
128
 * Free up the memory associated to the attribute value templates
129
 */
130
void
131
0
xsltFreeAVTList(void *avt) {
132
0
    xsltAttrVTPtr cur = (xsltAttrVTPtr) avt, next;
133
134
0
    while (cur != NULL) {
135
0
        next = cur->next;
136
0
  xsltFreeAttrVT(cur);
137
0
  cur = next;
138
0
    }
139
0
}
140
/**
141
 * xsltSetAttrVTsegment:
142
 * @ avt: pointer to an xsltAttrVT structure
143
 * @ val: the value to be set to the next available segment
144
 *
145
 * Within xsltCompileAttr there are several places where a value
146
 * needs to be added to the 'segments' array within the xsltAttrVT
147
 * structure, and at each place the allocated size may have to be
148
 * re-allocated.  This routine takes care of that situation.
149
 *
150
 * Returns the avt pointer, which may have been changed by a re-alloc
151
 */
152
static xsltAttrVTPtr
153
0
xsltSetAttrVTsegment(xsltAttrVTPtr avt, void *val) {
154
0
    if (avt->nb_seg >= avt->max_seg) {
155
0
        size_t size = sizeof(xsltAttrVT) +
156
0
                      (avt->max_seg + MAX_AVT_SEG) * sizeof(void *);
157
0
  avt = (xsltAttrVTPtr) xmlRealloc(avt, size);
158
0
  if (avt == NULL)
159
0
      return NULL;
160
0
  memset(&avt->segments[avt->nb_seg], 0, MAX_AVT_SEG*sizeof(void *));
161
0
  avt->max_seg += MAX_AVT_SEG;
162
0
    }
163
0
    avt->segments[avt->nb_seg++] = val;
164
0
    return avt;
165
0
}
166
167
/**
168
 * xsltCompileAttr:
169
 * @style:  a XSLT process context
170
 * @attr: the attribute coming from the stylesheet.
171
 *
172
 * Precompile an attribute in a stylesheet, basically it checks if it is
173
 * an attribute value template, and if yes, establish some structures needed
174
 * to process it at transformation time.
175
 */
176
void
177
0
xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) {
178
0
    const xmlChar *str;
179
0
    const xmlChar *cur;
180
0
    xmlChar *ret = NULL;
181
0
    xmlChar *expr = NULL;
182
0
    xmlXPathCompExprPtr comp = NULL;
183
0
    xsltAttrVTPtr avt, tmp;
184
0
    int i = 0, lastavt = 0;
185
186
0
    if ((style == NULL) || (attr == NULL) || (attr->children == NULL))
187
0
        return;
188
0
    if ((attr->children->type != XML_TEXT_NODE) ||
189
0
        (attr->children->next != NULL)) {
190
0
        xsltTransformError(NULL, style, attr->parent,
191
0
      "Attribute '%s': The content is expected to be a single text "
192
0
      "node when compiling an AVT.\n", attr->name);
193
0
  style->errors++;
194
0
  return;
195
0
    }
196
0
    str = attr->children->content;
197
0
    if ((xmlStrchr(str, '{') == NULL) &&
198
0
        (xmlStrchr(str, '}') == NULL)) return;
199
200
#ifdef WITH_XSLT_DEBUG_AVT
201
    xsltGenericDebug(xsltGenericDebugContext,
202
        "Found AVT %s: %s\n", attr->name, str);
203
#endif
204
0
    if (attr->psvi != NULL) {
205
#ifdef WITH_XSLT_DEBUG_AVT
206
  xsltGenericDebug(xsltGenericDebugContext,
207
      "AVT %s: already compiled\n", attr->name);
208
#endif
209
0
        return;
210
0
    }
211
    /*
212
    * Create a new AVT object.
213
    */
214
0
    avt = xsltNewAttrVT(style);
215
0
    if (avt == NULL)
216
0
  return;
217
0
    attr->psvi = avt;
218
219
0
    avt->nsList = xmlGetNsList(attr->doc, attr->parent);
220
0
    if (avt->nsList != NULL) {
221
0
  while (avt->nsList[i] != NULL)
222
0
      i++;
223
0
    }
224
0
    avt->nsNr = i;
225
226
0
    cur = str;
227
0
    while (*cur != 0) {
228
0
  if (*cur == '{') {
229
0
      if (*(cur+1) == '{') { /* escaped '{' */
230
0
          cur++;
231
0
    ret = xmlStrncat(ret, str, cur - str);
232
0
    cur++;
233
0
    str = cur;
234
0
    continue;
235
0
      }
236
0
      if (*(cur+1) == '}') { /* skip empty AVT */
237
0
    ret = xmlStrncat(ret, str, cur - str);
238
0
          cur += 2;
239
0
    str = cur;
240
0
    continue;
241
0
      }
242
0
      if ((ret != NULL) || (cur - str > 0)) {
243
0
    ret = xmlStrncat(ret, str, cur - str);
244
0
    str = cur;
245
0
    if (avt->nb_seg == 0)
246
0
        avt->strstart = 1;
247
0
    if ((tmp = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
248
0
        goto error;
249
0
                avt = tmp;
250
0
    ret = NULL;
251
0
    lastavt = 0;
252
0
      }
253
254
0
      cur++;
255
0
      while ((*cur != 0) && (*cur != '}')) {
256
    /* Need to check for literal (bug539741) */
257
0
    if ((*cur == '\'') || (*cur == '"')) {
258
0
        char delim = *(cur++);
259
0
        while ((*cur != 0) && (*cur != delim))
260
0
      cur++;
261
0
        if (*cur != 0)
262
0
      cur++; /* skip the ending delimiter */
263
0
    } else
264
0
        cur++;
265
0
      }
266
0
      if (*cur == 0) {
267
0
          xsltTransformError(NULL, style, attr->parent,
268
0
         "Attribute '%s': The AVT has an unmatched '{'.\n",
269
0
         attr->name);
270
0
    style->errors++;
271
0
    goto error;
272
0
      }
273
0
      str++;
274
0
      expr = xmlStrndup(str, cur - str);
275
0
      if (expr == NULL) {
276
    /*
277
    * TODO: What needs to be done here?
278
    */
279
0
          XSLT_TODO
280
0
    goto error;
281
0
      } else {
282
0
    comp = xsltXPathCompile(style, expr);
283
0
    if (comp == NULL) {
284
0
        xsltTransformError(NULL, style, attr->parent,
285
0
       "Attribute '%s': Failed to compile the expression "
286
0
       "'%s' in the AVT.\n", attr->name, expr);
287
0
        style->errors++;
288
0
        goto error;
289
0
    }
290
0
    if (avt->nb_seg == 0)
291
0
        avt->strstart = 0;
292
0
    if (lastavt == 1) {
293
0
        if ((tmp = xsltSetAttrVTsegment(avt, NULL)) == NULL) {
294
0
                        xsltTransformError(NULL, style, attr->parent,
295
0
                                           "out of memory\n");
296
0
            goto error;
297
0
                    }
298
0
                    avt = tmp;
299
0
    }
300
0
    if ((tmp = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL) {
301
0
                    xsltTransformError(NULL, style, attr->parent,
302
0
                                       "out of memory\n");
303
0
        goto error;
304
0
                }
305
0
                avt = tmp;
306
0
    lastavt = 1;
307
0
    xmlFree(expr);
308
0
    expr = NULL;
309
0
                comp = NULL;
310
0
      }
311
0
      cur++;
312
0
      str = cur;
313
0
  } else if (*cur == '}') {
314
0
      cur++;
315
0
      if (*cur == '}') { /* escaped '}' */
316
0
    ret = xmlStrncat(ret, str, cur - str);
317
0
    cur++;
318
0
    str = cur;
319
0
    continue;
320
0
      } else {
321
0
          xsltTransformError(NULL, style, attr->parent,
322
0
         "Attribute '%s': The AVT has an unmatched '}'.\n",
323
0
         attr->name);
324
0
    goto error;
325
0
      }
326
0
  } else
327
0
      cur++;
328
0
    }
329
0
    if ((ret != NULL) || (cur - str > 0)) {
330
0
  ret = xmlStrncat(ret, str, cur - str);
331
0
  str = cur;
332
0
  if (avt->nb_seg == 0)
333
0
      avt->strstart = 1;
334
0
  if ((tmp = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
335
0
      goto error;
336
0
        avt = tmp;
337
0
  ret = NULL;
338
0
    }
339
340
0
error:
341
0
    if (avt == NULL) {
342
0
        xsltTransformError(NULL, style, attr->parent,
343
0
    "xsltCompileAttr: malloc problem\n");
344
0
    } else {
345
0
        if (attr->psvi != avt) {  /* may have changed from realloc */
346
0
            attr->psvi = avt;
347
      /*
348
       * This is a "hack", but I can't see any clean method of
349
       * doing it.  If a re-alloc has taken place, then the pointer
350
       * for this AVT may have changed.  style->attVTs was set by
351
       * xsltNewAttrVT, so it needs to be re-set to the new value!
352
       */
353
0
      style->attVTs = avt;
354
0
  }
355
0
    }
356
0
    if (ret != NULL)
357
0
  xmlFree(ret);
358
0
    if (expr != NULL)
359
0
  xmlFree(expr);
360
0
    if (comp != NULL)
361
0
        xmlXPathFreeCompExpr(comp);
362
0
}
363
364
365
/**
366
 * xsltEvalAVT:
367
 * @ctxt: the XSLT transformation context
368
 * @avt: the prevompiled attribute value template info
369
 * @node: the node hosting the attribute
370
 *
371
 * Process the given AVT, and return the new string value.
372
 *
373
 * Returns the computed string value or NULL, must be deallocated by the
374
 *         caller.
375
 */
376
xmlChar *
377
0
xsltEvalAVT(xsltTransformContextPtr ctxt, void *avt, xmlNodePtr node) {
378
0
    xmlChar *ret = NULL, *tmp;
379
0
    xmlXPathCompExprPtr comp;
380
0
    xsltAttrVTPtr cur = (xsltAttrVTPtr) avt;
381
0
    int i;
382
0
    int str;
383
384
0
    if ((ctxt == NULL) || (avt == NULL) || (node == NULL))
385
0
        return(NULL);
386
0
    str = cur->strstart;
387
0
    for (i = 0;i < cur->nb_seg;i++) {
388
0
        if (str) {
389
0
      ret = xmlStrcat(ret, (const xmlChar *) cur->segments[i]);
390
0
  } else {
391
0
      comp = (xmlXPathCompExprPtr) cur->segments[i];
392
0
      tmp = xsltEvalXPathStringNs(ctxt, comp, cur->nsNr, cur->nsList);
393
0
      if (tmp != NULL) {
394
0
          if (ret != NULL) {
395
0
        ret = xmlStrcat(ret, tmp);
396
0
        xmlFree(tmp);
397
0
    } else {
398
0
        ret = tmp;
399
0
    }
400
0
      }
401
0
  }
402
0
  str = !str;
403
0
    }
404
0
    return(ret);
405
0
}