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