xslt: Import upstream release 1.1.38.
[wine.git] / libs / xslt / libxslt / attrvt.c
blob9d74a81b49c27eef3148756bce84d9374701927b
1 /*
2 * attrvt.c: Implementation of the XSL Transformation 1.0 engine
3 * attribute value template handling part.
5 * References:
6 * http://www.w3.org/TR/1999/REC-xslt-19991116
8 * Michael Kay "XSLT Programmer's Reference" pp 637-643
9 * Writing Multiple Output Files
11 * See Copyright for the status of this software.
13 * daniel@veillard.com
16 #define IN_LIBXSLT
17 #include "libxslt.h"
19 #include <string.h>
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"
30 #ifdef WITH_XSLT_DEBUG
31 #define WITH_XSLT_DEBUG_AVT
32 #endif
34 #define MAX_AVT_SEG 10
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 */
44 * the namespaces in scope
46 xmlNsPtr *nsList;
47 int nsNr;
49 * the content is an alternate of string and xmlXPathCompExprPtr
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
59 /**
60 * xsltNewAttrVT:
61 * @style: a XSLT process context
63 * Build a new xsltAttrVT structure
65 * Returns the structure or NULL in case of error
67 static xsltAttrVTPtr
68 xsltNewAttrVT(xsltStylesheetPtr style) {
69 xsltAttrVTPtr cur;
70 size_t size = sizeof(xsltAttrVT) + MAX_AVT_SEG * sizeof(void*);
72 cur = (xsltAttrVTPtr) xmlMalloc(size);
73 if (cur == NULL) {
74 xsltTransformError(NULL, style, NULL,
75 "xsltNewAttrVTPtr : malloc failed\n");
76 if (style != NULL) style->errors++;
77 return(NULL);
79 memset(cur, 0, size);
81 cur->nb_seg = 0;
82 cur->max_seg = MAX_AVT_SEG;
83 cur->strstart = 0;
84 cur->next = style->attVTs;
86 * Note: this pointer may be changed by a re-alloc within xsltCompileAttr,
87 * so that code may change the stylesheet pointer also!
89 style->attVTs = (xsltAttrVTPtr) cur;
91 return(cur);
94 /**
95 * xsltFreeAttrVT:
96 * @avt: pointer to an xsltAttrVT structure
98 * Free up the memory associated to the attribute value template
100 static void
101 xsltFreeAttrVT(xsltAttrVTPtr avt) {
102 int i;
104 if (avt == NULL) return;
106 if (avt->strstart == 1) {
107 for (i = 0;i < avt->nb_seg; i += 2)
108 if (avt->segments[i] != NULL)
109 xmlFree((xmlChar *) avt->segments[i]);
110 for (i = 1;i < avt->nb_seg; i += 2)
111 xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
112 } else {
113 for (i = 0;i < avt->nb_seg; i += 2)
114 xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
115 for (i = 1;i < avt->nb_seg; i += 2)
116 if (avt->segments[i] != NULL)
117 xmlFree((xmlChar *) avt->segments[i]);
119 if (avt->nsList != NULL)
120 xmlFree(avt->nsList);
121 xmlFree(avt);
125 * xsltFreeAVTList:
126 * @avt: pointer to an list of AVT structures
128 * Free up the memory associated to the attribute value templates
130 void
131 xsltFreeAVTList(void *avt) {
132 xsltAttrVTPtr cur = (xsltAttrVTPtr) avt, next;
134 while (cur != NULL) {
135 next = cur->next;
136 xsltFreeAttrVT(cur);
137 cur = next;
141 * xsltSetAttrVTsegment:
142 * @ avt: pointer to an xsltAttrVT structure
143 * @ val: the value to be set to the next available segment
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.
150 * Returns the avt pointer, which may have been changed by a re-alloc
152 static xsltAttrVTPtr
153 xsltSetAttrVTsegment(xsltAttrVTPtr avt, void *val) {
154 if (avt->nb_seg >= avt->max_seg) {
155 size_t size = sizeof(xsltAttrVT) +
156 (avt->max_seg + MAX_AVT_SEG) * sizeof(void *);
157 avt = (xsltAttrVTPtr) xmlRealloc(avt, size);
158 if (avt == NULL)
159 return NULL;
160 memset(&avt->segments[avt->nb_seg], 0, MAX_AVT_SEG*sizeof(void *));
161 avt->max_seg += MAX_AVT_SEG;
163 avt->segments[avt->nb_seg++] = val;
164 return avt;
168 * xsltCompileAttr:
169 * @style: a XSLT process context
170 * @attr: the attribute coming from the stylesheet.
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.
176 void
177 xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) {
178 const xmlChar *str;
179 const xmlChar *cur;
180 xmlChar *ret = NULL;
181 xmlChar *expr = NULL;
182 xmlXPathCompExprPtr comp = NULL;
183 xsltAttrVTPtr avt, tmp;
184 int i = 0, lastavt = 0;
186 if ((style == NULL) || (attr == NULL) || (attr->children == NULL))
187 return;
188 if ((attr->children->type != XML_TEXT_NODE) ||
189 (attr->children->next != NULL)) {
190 xsltTransformError(NULL, style, attr->parent,
191 "Attribute '%s': The content is expected to be a single text "
192 "node when compiling an AVT.\n", attr->name);
193 style->errors++;
194 return;
196 str = attr->children->content;
197 if ((xmlStrchr(str, '{') == NULL) &&
198 (xmlStrchr(str, '}') == NULL)) return;
200 #ifdef WITH_XSLT_DEBUG_AVT
201 xsltGenericDebug(xsltGenericDebugContext,
202 "Found AVT %s: %s\n", attr->name, str);
203 #endif
204 if (attr->psvi != NULL) {
205 #ifdef WITH_XSLT_DEBUG_AVT
206 xsltGenericDebug(xsltGenericDebugContext,
207 "AVT %s: already compiled\n", attr->name);
208 #endif
209 return;
212 * Create a new AVT object.
214 avt = xsltNewAttrVT(style);
215 if (avt == NULL)
216 return;
217 attr->psvi = avt;
219 avt->nsList = xmlGetNsList(attr->doc, attr->parent);
220 if (avt->nsList != NULL) {
221 while (avt->nsList[i] != NULL)
222 i++;
224 avt->nsNr = i;
226 cur = str;
227 while (*cur != 0) {
228 if (*cur == '{') {
229 if (*(cur+1) == '{') { /* escaped '{' */
230 cur++;
231 ret = xmlStrncat(ret, str, cur - str);
232 cur++;
233 str = cur;
234 continue;
236 if (*(cur+1) == '}') { /* skip empty AVT */
237 ret = xmlStrncat(ret, str, cur - str);
238 cur += 2;
239 str = cur;
240 continue;
242 if ((ret != NULL) || (cur - str > 0)) {
243 ret = xmlStrncat(ret, str, cur - str);
244 str = cur;
245 if (avt->nb_seg == 0)
246 avt->strstart = 1;
247 if ((tmp = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
248 goto error;
249 avt = tmp;
250 ret = NULL;
251 lastavt = 0;
254 cur++;
255 while ((*cur != 0) && (*cur != '}')) {
256 /* Need to check for literal (bug539741) */
257 if ((*cur == '\'') || (*cur == '"')) {
258 char delim = *(cur++);
259 while ((*cur != 0) && (*cur != delim))
260 cur++;
261 if (*cur != 0)
262 cur++; /* skip the ending delimiter */
263 } else
264 cur++;
266 if (*cur == 0) {
267 xsltTransformError(NULL, style, attr->parent,
268 "Attribute '%s': The AVT has an unmatched '{'.\n",
269 attr->name);
270 style->errors++;
271 goto error;
273 str++;
274 expr = xmlStrndup(str, cur - str);
275 if (expr == NULL) {
277 * TODO: What needs to be done here?
279 XSLT_TODO
280 goto error;
281 } else {
282 comp = xsltXPathCompile(style, expr);
283 if (comp == NULL) {
284 xsltTransformError(NULL, style, attr->parent,
285 "Attribute '%s': Failed to compile the expression "
286 "'%s' in the AVT.\n", attr->name, expr);
287 style->errors++;
288 goto error;
290 if (avt->nb_seg == 0)
291 avt->strstart = 0;
292 if (lastavt == 1) {
293 if ((tmp = xsltSetAttrVTsegment(avt, NULL)) == NULL) {
294 xsltTransformError(NULL, style, attr->parent,
295 "out of memory\n");
296 goto error;
298 avt = tmp;
300 if ((tmp = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL) {
301 xsltTransformError(NULL, style, attr->parent,
302 "out of memory\n");
303 goto error;
305 avt = tmp;
306 lastavt = 1;
307 xmlFree(expr);
308 expr = NULL;
309 comp = NULL;
311 cur++;
312 str = cur;
313 } else if (*cur == '}') {
314 cur++;
315 if (*cur == '}') { /* escaped '}' */
316 ret = xmlStrncat(ret, str, cur - str);
317 cur++;
318 str = cur;
319 continue;
320 } else {
321 xsltTransformError(NULL, style, attr->parent,
322 "Attribute '%s': The AVT has an unmatched '}'.\n",
323 attr->name);
324 goto error;
326 } else
327 cur++;
329 if ((ret != NULL) || (cur - str > 0)) {
330 ret = xmlStrncat(ret, str, cur - str);
331 str = cur;
332 if (avt->nb_seg == 0)
333 avt->strstart = 1;
334 if ((tmp = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
335 goto error;
336 avt = tmp;
337 ret = NULL;
340 error:
341 if (avt == NULL) {
342 xsltTransformError(NULL, style, attr->parent,
343 "xsltCompileAttr: malloc problem\n");
344 } else {
345 if (attr->psvi != avt) { /* may have changed from realloc */
346 attr->psvi = avt;
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!
353 style->attVTs = avt;
356 if (ret != NULL)
357 xmlFree(ret);
358 if (expr != NULL)
359 xmlFree(expr);
360 if (comp != NULL)
361 xmlXPathFreeCompExpr(comp);
366 * xsltEvalAVT:
367 * @ctxt: the XSLT transformation context
368 * @avt: the prevompiled attribute value template info
369 * @node: the node hosting the attribute
371 * Process the given AVT, and return the new string value.
373 * Returns the computed string value or NULL, must be deallocated by the
374 * caller.
376 xmlChar *
377 xsltEvalAVT(xsltTransformContextPtr ctxt, void *avt, xmlNodePtr node) {
378 xmlChar *ret = NULL, *tmp;
379 xmlXPathCompExprPtr comp;
380 xsltAttrVTPtr cur = (xsltAttrVTPtr) avt;
381 int i;
382 int str;
384 if ((ctxt == NULL) || (avt == NULL) || (node == NULL))
385 return(NULL);
386 str = cur->strstart;
387 for (i = 0;i < cur->nb_seg;i++) {
388 if (str) {
389 ret = xmlStrcat(ret, (const xmlChar *) cur->segments[i]);
390 } else {
391 comp = (xmlXPathCompExprPtr) cur->segments[i];
392 tmp = xsltEvalXPathStringNs(ctxt, comp, cur->nsNr, cur->nsList);
393 if (tmp != NULL) {
394 if (ret != NULL) {
395 ret = xmlStrcat(ret, tmp);
396 xmlFree(tmp);
397 } else {
398 ret = tmp;
402 str = !str;
404 return(ret);