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