Home | History | Annotate | Download | only in libxslt
      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 (at) 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 }
    388