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 #if __STDC_VERSION__ >= 199901L
52 /* Using a C99 flexible array member avoids false positives under UBSan */
61 * @style: a XSLT process context
63 * Build a new xsltAttrVT structure
65 * Returns the structure or NULL in case of error
68 xsltNewAttrVT(xsltStylesheetPtr style
) {
70 size_t size
= sizeof(xsltAttrVT
) + MAX_AVT_SEG
* sizeof(void*);
72 cur
= (xsltAttrVTPtr
) xmlMalloc(size
);
74 xsltTransformError(NULL
, style
, NULL
,
75 "xsltNewAttrVTPtr : malloc failed\n");
76 if (style
!= NULL
) style
->errors
++;
82 cur
->max_seg
= MAX_AVT_SEG
;
84 cur
->next
= style
->attVTs
;
86 * Note: this pointer may be changed by a re-alloc within xsltCompileAttr,
87 * so that code may change the stylesheet pointer also!
89 style
->attVTs
= (xsltAttrVTPtr
) cur
;
96 * @avt: pointer to an xsltAttrVT structure
98 * Free up the memory associated to the attribute value template
101 xsltFreeAttrVT(xsltAttrVTPtr avt
) {
104 if (avt
== NULL
) return;
106 if (avt
->strstart
== 1) {
107 for (i
= 0;i
< avt
->nb_seg
; i
+= 2)
108 if (avt
->segments
[i
] != NULL
)
109 xmlFree((xmlChar
*) avt
->segments
[i
]);
110 for (i
= 1;i
< avt
->nb_seg
; i
+= 2)
111 xmlXPathFreeCompExpr((xmlXPathCompExprPtr
) avt
->segments
[i
]);
113 for (i
= 0;i
< avt
->nb_seg
; i
+= 2)
114 xmlXPathFreeCompExpr((xmlXPathCompExprPtr
) avt
->segments
[i
]);
115 for (i
= 1;i
< avt
->nb_seg
; i
+= 2)
116 if (avt
->segments
[i
] != NULL
)
117 xmlFree((xmlChar
*) avt
->segments
[i
]);
119 if (avt
->nsList
!= NULL
)
120 xmlFree(avt
->nsList
);
126 * @avt: pointer to an list of AVT structures
128 * Free up the memory associated to the attribute value templates
131 xsltFreeAVTList(void *avt
) {
132 xsltAttrVTPtr cur
= (xsltAttrVTPtr
) avt
, next
;
134 while (cur
!= NULL
) {
141 * xsltSetAttrVTsegment:
142 * @ avt: pointer to an xsltAttrVT structure
143 * @ val: the value to be set to the next available segment
145 * Within xsltCompileAttr there are several places where a value
146 * needs to be added to the 'segments' array within the xsltAttrVT
147 * structure, and at each place the allocated size may have to be
148 * re-allocated. This routine takes care of that situation.
150 * Returns the avt pointer, which may have been changed by a re-alloc
153 xsltSetAttrVTsegment(xsltAttrVTPtr avt
, void *val
) {
154 if (avt
->nb_seg
>= avt
->max_seg
) {
155 size_t size
= sizeof(xsltAttrVT
) +
156 (avt
->max_seg
+ MAX_AVT_SEG
) * sizeof(void *);
157 avt
= (xsltAttrVTPtr
) xmlRealloc(avt
, size
);
160 memset(&avt
->segments
[avt
->nb_seg
], 0, MAX_AVT_SEG
*sizeof(void *));
161 avt
->max_seg
+= MAX_AVT_SEG
;
163 avt
->segments
[avt
->nb_seg
++] = val
;
169 * @style: a XSLT process context
170 * @attr: the attribute coming from the stylesheet.
172 * Precompile an attribute in a stylesheet, basically it checks if it is
173 * an attribute value template, and if yes, establish some structures needed
174 * to process it at transformation time.
177 xsltCompileAttr(xsltStylesheetPtr style
, xmlAttrPtr attr
) {
181 xmlChar
*expr
= NULL
;
182 xmlXPathCompExprPtr comp
= NULL
;
183 xsltAttrVTPtr avt
, tmp
;
184 int i
= 0, lastavt
= 0;
186 if ((style
== NULL
) || (attr
== NULL
) || (attr
->children
== NULL
))
188 if ((attr
->children
->type
!= XML_TEXT_NODE
) ||
189 (attr
->children
->next
!= NULL
)) {
190 xsltTransformError(NULL
, style
, attr
->parent
,
191 "Attribute '%s': The content is expected to be a single text "
192 "node when compiling an AVT.\n", attr
->name
);
196 str
= attr
->children
->content
;
197 if ((xmlStrchr(str
, '{') == NULL
) &&
198 (xmlStrchr(str
, '}') == NULL
)) return;
200 #ifdef WITH_XSLT_DEBUG_AVT
201 xsltGenericDebug(xsltGenericDebugContext
,
202 "Found AVT %s: %s\n", attr
->name
, str
);
204 if (attr
->psvi
!= NULL
) {
205 #ifdef WITH_XSLT_DEBUG_AVT
206 xsltGenericDebug(xsltGenericDebugContext
,
207 "AVT %s: already compiled\n", attr
->name
);
212 * Create a new AVT object.
214 avt
= xsltNewAttrVT(style
);
219 avt
->nsList
= xmlGetNsList(attr
->doc
, attr
->parent
);
220 if (avt
->nsList
!= NULL
) {
221 while (avt
->nsList
[i
] != NULL
)
229 if (*(cur
+1) == '{') { /* escaped '{' */
231 ret
= xmlStrncat(ret
, str
, cur
- str
);
236 if (*(cur
+1) == '}') { /* skip empty AVT */
237 ret
= xmlStrncat(ret
, str
, cur
- str
);
242 if ((ret
!= NULL
) || (cur
- str
> 0)) {
243 ret
= xmlStrncat(ret
, str
, cur
- str
);
245 if (avt
->nb_seg
== 0)
247 if ((tmp
= xsltSetAttrVTsegment(avt
, (void *) ret
)) == NULL
)
255 while ((*cur
!= 0) && (*cur
!= '}')) {
256 /* Need to check for literal (bug539741) */
257 if ((*cur
== '\'') || (*cur
== '"')) {
258 char delim
= *(cur
++);
259 while ((*cur
!= 0) && (*cur
!= delim
))
262 cur
++; /* skip the ending delimiter */
267 xsltTransformError(NULL
, style
, attr
->parent
,
268 "Attribute '%s': The AVT has an unmatched '{'.\n",
274 expr
= xmlStrndup(str
, cur
- str
);
277 * TODO: What needs to be done here?
282 comp
= xsltXPathCompile(style
, expr
);
284 xsltTransformError(NULL
, style
, attr
->parent
,
285 "Attribute '%s': Failed to compile the expression "
286 "'%s' in the AVT.\n", attr
->name
, expr
);
290 if (avt
->nb_seg
== 0)
293 if ((tmp
= xsltSetAttrVTsegment(avt
, NULL
)) == NULL
) {
294 xsltTransformError(NULL
, style
, attr
->parent
,
300 if ((tmp
= xsltSetAttrVTsegment(avt
, (void *) comp
)) == NULL
) {
301 xsltTransformError(NULL
, style
, attr
->parent
,
313 } else if (*cur
== '}') {
315 if (*cur
== '}') { /* escaped '}' */
316 ret
= xmlStrncat(ret
, str
, cur
- str
);
321 xsltTransformError(NULL
, style
, attr
->parent
,
322 "Attribute '%s': The AVT has an unmatched '}'.\n",
329 if ((ret
!= NULL
) || (cur
- str
> 0)) {
330 ret
= xmlStrncat(ret
, str
, cur
- str
);
332 if (avt
->nb_seg
== 0)
334 if ((tmp
= xsltSetAttrVTsegment(avt
, (void *) ret
)) == NULL
)
342 xsltTransformError(NULL
, style
, attr
->parent
,
343 "xsltCompileAttr: malloc problem\n");
345 if (attr
->psvi
!= avt
) { /* may have changed from realloc */
348 * This is a "hack", but I can't see any clean method of
349 * doing it. If a re-alloc has taken place, then the pointer
350 * for this AVT may have changed. style->attVTs was set by
351 * xsltNewAttrVT, so it needs to be re-set to the new value!
361 xmlXPathFreeCompExpr(comp
);
367 * @ctxt: the XSLT transformation context
368 * @avt: the prevompiled attribute value template info
369 * @node: the node hosting the attribute
371 * Process the given AVT, and return the new string value.
373 * Returns the computed string value or NULL, must be deallocated by the
377 xsltEvalAVT(xsltTransformContextPtr ctxt
, void *avt
, xmlNodePtr node
) {
378 xmlChar
*ret
= NULL
, *tmp
;
379 xmlXPathCompExprPtr comp
;
380 xsltAttrVTPtr cur
= (xsltAttrVTPtr
) avt
;
384 if ((ctxt
== NULL
) || (avt
== NULL
) || (node
== NULL
))
387 for (i
= 0;i
< cur
->nb_seg
;i
++) {
389 ret
= xmlStrcat(ret
, (const xmlChar
*) cur
->segments
[i
]);
391 comp
= (xmlXPathCompExprPtr
) cur
->segments
[i
];
392 tmp
= xsltEvalXPathStringNs(ctxt
, comp
, cur
->nsNr
, cur
->nsList
);
395 ret
= xmlStrcat(ret
, tmp
);