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