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