2 * attrvt.c: Implementation of the XSL Transformation 1.0 engine
3 * attribute value template handling part.
6 * http://www.w3.org/TR/1999/REC-xslt-19991116
8 * Michael Kay "XSLT Programmer's Reference" pp 637-643
9 * Writing Multiple Output Files
11 * See Copyright for the status of this software.
21 #include <libxml/xmlmemory.h>
22 #include <libxml/tree.h>
23 #include <libxml/xpath.h>
24 #include <libxml/xpathInternals.h>
26 #include "xsltutils.h"
27 #include "xsltInternals.h"
28 #include "templates.h"
30 #ifdef WITH_XSLT_DEBUG
31 #define WITH_XSLT_DEBUG_AVT
34 #define MAX_AVT_SEG 10
36 typedef struct _xsltAttrVT xsltAttrVT;
37 typedef xsltAttrVT *xsltAttrVTPtr;
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 */
44 * the namespaces in scope
49 * the content is an alternate of string and xmlXPathCompExprPtr
51 void *segments[MAX_AVT_SEG];
56 * @style: a XSLT process context
58 * Build a new xsltAttrVT structure
60 * Returns the structure or NULL in case of error
63 xsltNewAttrVT(xsltStylesheetPtr style) {
66 cur = (xsltAttrVTPtr) xmlMalloc(sizeof(xsltAttrVT));
68 xsltTransformError(NULL, style, NULL,
69 "xsltNewAttrVTPtr : malloc failed\n");
70 if (style != NULL) style->errors++;
73 memset(cur, 0, sizeof(xsltAttrVT));
76 cur->max_seg = MAX_AVT_SEG;
78 cur->next = style->attVTs;
80 * Note: this pointer may be changed by a re-alloc within xsltCompileAttr,
81 * so that code may change the stylesheet pointer also!
83 style->attVTs = (xsltAttrVTPtr) cur;
90 * @avt: pointer to an xsltAttrVT structure
92 * Free up the memory associated to the attribute value template
95 xsltFreeAttrVT(xsltAttrVTPtr avt) {
98 if (avt == NULL) return;
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]);
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]);
113 if (avt->nsList != NULL)
114 xmlFree(avt->nsList);
120 * @avt: pointer to an list of AVT structures
122 * Free up the memory associated to the attribute value templates
125 xsltFreeAVTList(void *avt) {
126 xsltAttrVTPtr cur = (xsltAttrVTPtr) avt, next;
128 while (cur != NULL) {
135 * xsltSetAttrVTsegment:
136 * @ avt: pointer to an xsltAttrVT structure
137 * @ val: the value to be set to the next available segment
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.
144 * Returns the avt pointer, which may have been changed by a re-alloc
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 *));
154 memset(&avt->segments[avt->nb_seg], 0, MAX_AVT_SEG*sizeof(void *));
155 avt->max_seg += MAX_AVT_SEG;
157 avt->segments[avt->nb_seg++] = val;
163 * @style: a XSLT process context
164 * @attr: the attribute coming from the stylesheet.
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.
171 xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) {
175 xmlChar *expr = NULL;
177 int i = 0, lastavt = 0;
179 if ((style == NULL) || (attr == NULL) || (attr->children == NULL))
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);
189 str = attr->children->content;
190 if ((xmlStrchr(str, '{') == NULL) &&
191 (xmlStrchr(str, '}') == NULL)) return;
193 #ifdef WITH_XSLT_DEBUG_AVT
194 xsltGenericDebug(xsltGenericDebugContext,
195 "Found AVT %s: %s\n", attr->name, str);
197 if (attr->psvi != NULL) {
198 #ifdef WITH_XSLT_DEBUG_AVT
199 xsltGenericDebug(xsltGenericDebugContext,
200 "AVT %s: already compiled\n", attr->name);
205 * Create a new AVT object.
207 avt = xsltNewAttrVT(style);
212 avt->nsList = xmlGetNsList(attr->doc, attr->parent);
213 if (avt->nsList != NULL) {
214 while (avt->nsList[i] != NULL)
222 if (*(cur+1) == '{') { /* escaped '{' */
224 ret = xmlStrncat(ret, str, cur - str);
229 if (*(cur+1) == '}') { /* skip empty AVT */
230 ret = xmlStrncat(ret, str, cur - str);
235 if ((ret != NULL) || (cur - str > 0)) {
236 ret = xmlStrncat(ret, str, cur - str);
238 if (avt->nb_seg == 0)
240 if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
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))
254 cur++; /* skip the ending delimiter */
259 xsltTransformError(NULL, style, attr->parent,
260 "Attribute '%s': The AVT has an unmatched '{'.\n",
266 expr = xmlStrndup(str, cur - str);
269 * TODO: What needs to be done here?
274 xmlXPathCompExprPtr comp;
276 comp = xsltXPathCompile(style, expr);
278 xsltTransformError(NULL, style, attr->parent,
279 "Attribute '%s': Failed to compile the expression "
280 "'%s' in the AVT.\n", attr->name, expr);
284 if (avt->nb_seg == 0)
287 if ((avt = xsltSetAttrVTsegment(avt, NULL)) == NULL)
290 if ((avt = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL)
298 } else if (*cur == '}') {
300 if (*cur == '}') { /* escaped '}' */
301 ret = xmlStrncat(ret, str, cur - str);
306 xsltTransformError(NULL, style, attr->parent,
307 "Attribute '%s': The AVT has an unmatched '}'.\n",
314 if ((ret != NULL) || (cur - str > 0)) {
315 ret = xmlStrncat(ret, str, cur - str);
317 if (avt->nb_seg == 0)
319 if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
326 xsltTransformError(NULL, style, attr->parent,
327 "xsltCompileAttr: malloc problem\n");
329 if (attr->psvi != avt) { /* may have changed from realloc */
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!
349 * @ctxt: the XSLT transformation context
350 * @avt: the prevompiled attribute value template info
351 * @node: the node hosting the attribute
353 * Process the given AVT, and return the new string value.
355 * Returns the computed string value or NULL, must be deallocated by the
359 xsltEvalAVT(xsltTransformContextPtr ctxt, void *avt, xmlNodePtr node) {
360 xmlChar *ret = NULL, *tmp;
361 xmlXPathCompExprPtr comp;
362 xsltAttrVTPtr cur = (xsltAttrVTPtr) avt;
366 if ((ctxt == NULL) || (avt == NULL) || (node == NULL))
369 for (i = 0;i < cur->nb_seg;i++) {
371 ret = xmlStrcat(ret, (const xmlChar *) cur->segments[i]);
373 comp = (xmlXPathCompExprPtr) cur->segments[i];
374 tmp = xsltEvalXPathStringNs(ctxt, comp, cur->nsNr, cur->nsList);
377 ret = xmlStrcat(ret, tmp);