OLD | NEW |
| (Empty) |
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 | |
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 | |
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 | |
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 | |
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 | |
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 * | |
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 } | |
OLD | NEW |