blob: bda0910e0e6cbbc68737ae957cf495e0d6999802 [file] [log] [blame]
/*
* attrvt.c: Implementation of the XSL Transformation 1.0 engine
* attribute value template handling part.
*
* References:
* http://www.w3.org/TR/1999/REC-xslt-19991116
*
* Michael Kay "XSLT Programmer's Reference" pp 637-643
* Writing Multiple Output Files
*
* See Copyright for the status of this software.
*
* daniel@veillard.com
*/
#define IN_LIBXSLT
#include "libxslt.h"
#include <string.h>
#include <libxml/xmlmemory.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include "xslt.h"
#include "xsltutils.h"
#include "xsltInternals.h"
#include "templates.h"
#ifdef WITH_XSLT_DEBUG
#define WITH_XSLT_DEBUG_AVT
#endif
#define MAX_AVT_SEG 10
typedef struct _xsltAttrVT xsltAttrVT;
typedef xsltAttrVT *xsltAttrVTPtr;
struct _xsltAttrVT {
struct _xsltAttrVT *next; /* next xsltAttrVT */
int nb_seg; /* Number of segments */
int max_seg; /* max capacity before re-alloc needed */
int strstart; /* is the start a string */
/*
* the namespaces in scope
*/
xmlNsPtr *nsList;
int nsNr;
/*
* the content is an alternate of string and xmlXPathCompExprPtr
*/
void *segments[MAX_AVT_SEG];
};
/**
* xsltNewAttrVT:
* @style: a XSLT process context
*
* Build a new xsltAttrVT structure
*
* Returns the structure or NULL in case of error
*/
static xsltAttrVTPtr
xsltNewAttrVT(xsltStylesheetPtr style) {
xsltAttrVTPtr cur;
cur = (xsltAttrVTPtr) xmlMalloc(sizeof(xsltAttrVT));
if (cur == NULL) {
xsltTransformError(NULL, style, NULL,
"xsltNewAttrVTPtr : malloc failed\n");
if (style != NULL) style->errors++;
return(NULL);
}
memset(cur, 0, sizeof(xsltAttrVT));
cur->nb_seg = 0;
cur->max_seg = MAX_AVT_SEG;
cur->strstart = 0;
cur->next = style->attVTs;
/*
* Note: this pointer may be changed by a re-alloc within xsltCompileAttr,
* so that code may change the stylesheet pointer also!
*/
style->attVTs = (xsltAttrVTPtr) cur;
return(cur);
}
/**
* xsltFreeAttrVT:
* @avt: pointer to an xsltAttrVT structure
*
* Free up the memory associated to the attribute value template
*/
static void
xsltFreeAttrVT(xsltAttrVTPtr avt) {
int i;
if (avt == NULL) return;
if (avt->strstart == 1) {
for (i = 0;i < avt->nb_seg; i += 2)
if (avt->segments[i] != NULL)
xmlFree((xmlChar *) avt->segments[i]);
for (i = 1;i < avt->nb_seg; i += 2)
xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
} else {
for (i = 0;i < avt->nb_seg; i += 2)
xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
for (i = 1;i < avt->nb_seg; i += 2)
if (avt->segments[i] != NULL)
xmlFree((xmlChar *) avt->segments[i]);
}
if (avt->nsList != NULL)
xmlFree(avt->nsList);
xmlFree(avt);
}
/**
* xsltFreeAVTList:
* @avt: pointer to an list of AVT structures
*
* Free up the memory associated to the attribute value templates
*/
void
xsltFreeAVTList(void *avt) {
xsltAttrVTPtr cur = (xsltAttrVTPtr) avt, next;
while (cur != NULL) {
next = cur->next;
xsltFreeAttrVT(cur);
cur = next;
}
}
/**
* xsltSetAttrVTsegment:
* @ avt: pointer to an xsltAttrVT structure
* @ val: the value to be set to the next available segment
*
* Within xsltCompileAttr there are several places where a value
* needs to be added to the 'segments' array within the xsltAttrVT
* structure, and at each place the allocated size may have to be
* re-allocated. This routine takes care of that situation.
*
* Returns the avt pointer, which may have been changed by a re-alloc
*/
static xsltAttrVTPtr
xsltSetAttrVTsegment(xsltAttrVTPtr avt, void *val) {
if (avt->nb_seg >= avt->max_seg) {
avt = (xsltAttrVTPtr) xmlRealloc(avt, sizeof(xsltAttrVT) +
avt->max_seg * sizeof(void *));
if (avt == NULL) {
return NULL;
}
memset(&avt->segments[avt->nb_seg], 0, MAX_AVT_SEG*sizeof(void *));
avt->max_seg += MAX_AVT_SEG;
}
avt->segments[avt->nb_seg++] = val;
return avt;
}
/**
* xsltCompileAttr:
* @style: a XSLT process context
* @attr: the attribute coming from the stylesheet.
*
* Precompile an attribute in a stylesheet, basically it checks if it is
* an attrubute value template, and if yes establish some structures needed
* to process it at transformation time.
*/
void
xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) {
const xmlChar *str;
const xmlChar *cur;
xmlChar *ret = NULL;
xmlChar *expr = NULL;
xsltAttrVTPtr avt;
int i = 0, lastavt = 0;
if ((style == NULL) || (attr == NULL) || (attr->children == NULL))
return;
if ((attr->children->type != XML_TEXT_NODE) ||
(attr->children->next != NULL)) {
xsltTransformError(NULL, style, attr->parent,
"Attribute '%s': The content is expected to be a single text "
"node when compiling an AVT.\n", attr->name);
style->errors++;
return;
}
str = attr->children->content;
if ((xmlStrchr(str, '{') == NULL) &&
(xmlStrchr(str, '}') == NULL)) return;
#ifdef WITH_XSLT_DEBUG_AVT
xsltGenericDebug(xsltGenericDebugContext,
"Found AVT %s: %s\n", attr->name, str);
#endif
if (attr->psvi != NULL) {
#ifdef WITH_XSLT_DEBUG_AVT
xsltGenericDebug(xsltGenericDebugContext,
"AVT %s: already compiled\n", attr->name);
#endif
return;
}
/*
* Create a new AVT object.
*/
avt = xsltNewAttrVT(style);
if (avt == NULL)
return;
attr->psvi = avt;
avt->nsList = xmlGetNsList(attr->doc, attr->parent);
if (avt->nsList != NULL) {
while (avt->nsList[i] != NULL)
i++;
}
avt->nsNr = i;
cur = str;
while (*cur != 0) {
if (*cur == '{') {
if (*(cur+1) == '{') { /* escaped '{' */
cur++;
ret = xmlStrncat(ret, str, cur - str);
cur++;
str = cur;
continue;
}
if (*(cur+1) == '}') { /* skip empty AVT */
ret = xmlStrncat(ret, str, cur - str);
cur += 2;
str = cur;
continue;
}
if ((ret != NULL) || (cur - str > 0)) {
ret = xmlStrncat(ret, str, cur - str);
str = cur;
if (avt->nb_seg == 0)
avt->strstart = 1;
if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
goto error;
ret = NULL;
lastavt = 0;
}
cur++;
while ((*cur != 0) && (*cur != '}')) {
/* Need to check for literal (bug539741) */
if ((*cur == '\'') || (*cur == '"')) {
char delim = *(cur++);
while ((*cur != 0) && (*cur != delim))
cur++;
if (*cur != 0)
cur++; /* skip the ending delimiter */
} else
cur++;
}
if (*cur == 0) {
xsltTransformError(NULL, style, attr->parent,
"Attribute '%s': The AVT has an unmatched '{'.\n",
attr->name);
style->errors++;
goto error;
}
str++;
expr = xmlStrndup(str, cur - str);
if (expr == NULL) {
/*
* TODO: What needs to be done here?
*/
XSLT_TODO
goto error;
} else {
xmlXPathCompExprPtr comp;
comp = xsltXPathCompile(style, expr);
if (comp == NULL) {
xsltTransformError(NULL, style, attr->parent,
"Attribute '%s': Failed to compile the expression "
"'%s' in the AVT.\n", attr->name, expr);
style->errors++;
goto error;
}
if (avt->nb_seg == 0)
avt->strstart = 0;
if (lastavt == 1) {
if ((avt = xsltSetAttrVTsegment(avt, NULL)) == NULL)
goto error;
}
if ((avt = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL)
goto error;
lastavt = 1;
xmlFree(expr);
expr = NULL;
}
cur++;
str = cur;
} else if (*cur == '}') {
cur++;
if (*cur == '}') { /* escaped '}' */
ret = xmlStrncat(ret, str, cur - str);
cur++;
str = cur;
continue;
} else {
xsltTransformError(NULL, style, attr->parent,
"Attribute '%s': The AVT has an unmatched '}'.\n",
attr->name);
goto error;
}
} else
cur++;
}
if ((ret != NULL) || (cur - str > 0)) {
ret = xmlStrncat(ret, str, cur - str);
str = cur;
if (avt->nb_seg == 0)
avt->strstart = 1;
if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
goto error;
ret = NULL;
}
error:
if (avt == NULL) {
xsltTransformError(NULL, style, attr->parent,
"xsltCompileAttr: malloc problem\n");
} else {
if (attr->psvi != avt) { /* may have changed from realloc */
attr->psvi = avt;
/*
* This is a "hack", but I can't see any clean method of
* doing it. If a re-alloc has taken place, then the pointer
* for this AVT may have changed. style->attVTs was set by
* xsltNewAttrVT, so it needs to be re-set to the new value!
*/
style->attVTs = avt;
}
}
if (ret != NULL)
xmlFree(ret);
if (expr != NULL)
xmlFree(expr);
}
/**
* xsltEvalAVT:
* @ctxt: the XSLT transformation context
* @avt: the prevompiled attribute value template info
* @node: the node hosting the attribute
*
* Process the given AVT, and return the new string value.
*
* Returns the computed string value or NULL, must be deallocated by the
* caller.
*/
xmlChar *
xsltEvalAVT(xsltTransformContextPtr ctxt, void *avt, xmlNodePtr node) {
xmlChar *ret = NULL, *tmp;
xmlXPathCompExprPtr comp;
xsltAttrVTPtr cur = (xsltAttrVTPtr) avt;
int i;
int str;
if ((ctxt == NULL) || (avt == NULL) || (node == NULL))
return(NULL);
str = cur->strstart;
for (i = 0;i < cur->nb_seg;i++) {
if (str) {
ret = xmlStrcat(ret, (const xmlChar *) cur->segments[i]);
} else {
comp = (xmlXPathCompExprPtr) cur->segments[i];
tmp = xsltEvalXPathStringNs(ctxt, comp, cur->nsNr, cur->nsList);
if (tmp != NULL) {
if (ret != NULL) {
ret = xmlStrcat(ret, tmp);
xmlFree(tmp);
} else {
ret = tmp;
}
}
}
str = !str;
}
return(ret);
}