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@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 36typedef struct _xsltAttrVT xsltAttrVT; 37typedef xsltAttrVT *xsltAttrVTPtr; 38struct _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 */ 62static xsltAttrVTPtr 63xsltNewAttrVT(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 */ 94static void 95xsltFreeAttrVT(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 */ 124void 125xsltFreeAVTList(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 */ 146static xsltAttrVTPtr 147xsltSetAttrVTsegment(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 */ 170void 171xsltCompileAttr(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 content is not a text node\n", attr->name); 185 style->errors++; 186 return; 187 } 188 str = attr->children->content; 189 if ((xmlStrchr(str, '{') == NULL) && 190 (xmlStrchr(str, '}') == NULL)) return; 191 192#ifdef WITH_XSLT_DEBUG_AVT 193 xsltGenericDebug(xsltGenericDebugContext, 194 "Found AVT %s: %s\n", attr->name, str); 195#endif 196 if (attr->psvi != NULL) { 197#ifdef WITH_XSLT_DEBUG_AVT 198 xsltGenericDebug(xsltGenericDebugContext, 199 "AVT %s: already compiled\n", attr->name); 200#endif 201 return; 202 } 203 avt = xsltNewAttrVT(style); 204 if (avt == NULL) return; 205 attr->psvi = avt; 206 207 avt->nsList = xmlGetNsList(attr->doc, attr->parent); 208 if (avt->nsList != NULL) { 209 while (avt->nsList[i] != NULL) 210 i++; 211 } 212 avt->nsNr = i; 213 214 cur = str; 215 while (*cur != 0) { 216 if (*cur == '{') { 217 if (*(cur+1) == '{') { /* escaped '{' */ 218 cur++; 219 ret = xmlStrncat(ret, str, cur - str); 220 cur++; 221 str = cur; 222 continue; 223 } 224 if (*(cur+1) == '}') { /* skip empty AVT */ 225 ret = xmlStrncat(ret, str, cur - str); 226 cur+=2; 227 str = cur; 228 continue; 229 } 230 if ((ret != NULL) || (cur - str > 0)) { 231 ret = xmlStrncat(ret, str, cur - str); 232 str = cur; 233 if (avt->nb_seg == 0) 234 avt->strstart = 1; 235 if ((avt=xsltSetAttrVTsegment(avt, (void *) ret)) == NULL) 236 goto error; 237 ret = NULL; 238 lastavt = 0; 239 } 240 241 cur++; 242 while ((*cur != 0) && (*cur != '}')) cur++; 243 if (*cur == 0) { 244 xsltTransformError(NULL, style, attr->parent, 245 "Attribute template %s: unmatched '{'\n", attr->name); 246 style->errors++; 247 goto error; 248 } 249 str++; 250 expr = xmlStrndup(str, cur - str); 251 if (expr == NULL) { 252 XSLT_TODO 253 goto error; 254 } else { 255 xmlXPathCompExprPtr comp; 256 257 comp = xsltXPathCompile(style, expr); 258 if (comp == NULL) { 259 xsltTransformError(NULL, style, attr->parent, 260 "Attribute template %s: failed to compile %s\n", 261 attr->name, expr); 262 style->errors++; 263 goto error; 264 } 265 if (avt->nb_seg == 0) 266 avt->strstart = 0; 267 if (lastavt == 1) { 268 if ((avt=xsltSetAttrVTsegment(avt, NULL)) == NULL) 269 goto error; 270 } 271 if ((avt=xsltSetAttrVTsegment(avt, (void *) comp))==NULL) 272 goto error; 273 lastavt = 1; 274 xmlFree(expr); 275 expr = NULL; 276 } 277 cur++; 278 str = cur; 279 } else if (*cur == '}') { 280 cur++; 281 if (*cur == '}') { /* escaped '}' */ 282 ret = xmlStrncat(ret, str, cur - str); 283 cur++; 284 str = cur; 285 continue; 286 } else { 287 xsltTransformError(NULL, style, attr->parent, 288 "Attribute template %s: unmatched '}'\n", attr->name); 289 goto error; 290 } 291 } else 292 cur++; 293 } 294 if ((ret != NULL) || (cur - str > 0)) { 295 ret = xmlStrncat(ret, str, cur - str); 296 str = cur; 297 if (avt->nb_seg == 0) 298 avt->strstart = 1; 299 if ((avt=xsltSetAttrVTsegment(avt, (void *) ret)) == NULL) 300 goto error; 301 ret = NULL; 302 } 303 304error: 305 if (avt == NULL) { 306 xsltTransformError(NULL, style, attr->parent, 307 "xsltCompileAttr: malloc problem\n"); 308 } else { 309 if (attr->psvi != avt) { /* may have changed from realloc */ 310 attr->psvi = avt; 311 /* 312 * This is a "hack", but I can't see any clean method of 313 * doing it. If a re-alloc has taken place, then the pointer 314 * for this AVT may have changed. style->attVTs was set by 315 * xsltNewAttrVT, so it needs to be re-set to the new value! 316 */ 317 style->attVTs = avt; 318 } 319 } 320 if (ret != NULL) 321 xmlFree(ret); 322 if (expr != NULL) 323 xmlFree(expr); 324} 325 326 327/** 328 * xsltEvalAVT: 329 * @ctxt: the XSLT transformation context 330 * @avt: the prevompiled attribute value template info 331 * @node: the node hosting the attribute 332 * 333 * Process the given AVT, and return the new string value. 334 * 335 * Returns the computed string value or NULL, must be deallocated by the 336 * caller. 337 */ 338xmlChar * 339xsltEvalAVT(xsltTransformContextPtr ctxt, void *avt, xmlNodePtr node) { 340 xmlChar *ret = NULL, *tmp; 341 xmlXPathCompExprPtr comp; 342 xsltAttrVTPtr cur = (xsltAttrVTPtr) avt; 343 int i; 344 int str; 345 346 if ((ctxt == NULL) || (avt == NULL) || (node == NULL)) 347 return(NULL); 348 str = cur->strstart; 349 for (i = 0;i < cur->nb_seg;i++) { 350 if (str) { 351 ret = xmlStrcat(ret, (const xmlChar *) cur->segments[i]); 352 } else { 353 comp = (xmlXPathCompExprPtr) cur->segments[i]; 354 tmp = xsltEvalXPathStringNs(ctxt, comp, cur->nsNr, cur->nsList); 355 if (tmp != NULL) { 356 if (ret != NULL) { 357 ret = xmlStrcat(ret, tmp); 358 xmlFree(tmp); 359 } else { 360 ret = tmp; 361 } 362 } 363 } 364 str = !str; 365 } 366 return(ret); 367} 368