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
);