- add sources.
[platform/framework/web/crosswalk.git] / src / third_party / libxslt / 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 #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 }