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 #include "precomp.h" 17 18 #ifdef WITH_XSLT_DEBUG 19 #define WITH_XSLT_DEBUG_AVT 20 #endif 21 22 #define MAX_AVT_SEG 10 23 24 typedef struct _xsltAttrVT xsltAttrVT; 25 typedef xsltAttrVT *xsltAttrVTPtr; 26 struct _xsltAttrVT { 27 struct _xsltAttrVT *next; /* next xsltAttrVT */ 28 int nb_seg; /* Number of segments */ 29 int max_seg; /* max capacity before re-alloc needed */ 30 int strstart; /* is the start a string */ 31 /* 32 * the namespaces in scope 33 */ 34 xmlNsPtr *nsList; 35 int nsNr; 36 /* 37 * the content is an alternate of string and xmlXPathCompExprPtr 38 */ 39 #if __STDC_VERSION__ >= 199901L 40 /* Using a C99 flexible array member avoids false positives under UBSan */ 41 void *segments[]; 42 #else 43 void *segments[1]; 44 #endif 45 }; 46 47 /** 48 * xsltNewAttrVT: 49 * @style: a XSLT process context 50 * 51 * Build a new xsltAttrVT structure 52 * 53 * Returns the structure or NULL in case of error 54 */ 55 static xsltAttrVTPtr 56 xsltNewAttrVT(xsltStylesheetPtr style) { 57 xsltAttrVTPtr cur; 58 size_t size = sizeof(xsltAttrVT) + MAX_AVT_SEG * sizeof(void*); 59 60 cur = (xsltAttrVTPtr) xmlMalloc(size); 61 if (cur == NULL) { 62 xsltTransformError(NULL, style, NULL, 63 "xsltNewAttrVTPtr : malloc failed\n"); 64 if (style != NULL) style->errors++; 65 return(NULL); 66 } 67 memset(cur, 0, size); 68 69 cur->nb_seg = 0; 70 cur->max_seg = MAX_AVT_SEG; 71 cur->strstart = 0; 72 cur->next = style->attVTs; 73 /* 74 * Note: this pointer may be changed by a re-alloc within xsltCompileAttr, 75 * so that code may change the stylesheet pointer also! 76 */ 77 style->attVTs = (xsltAttrVTPtr) cur; 78 79 return(cur); 80 } 81 82 /** 83 * xsltFreeAttrVT: 84 * @avt: pointer to an xsltAttrVT structure 85 * 86 * Free up the memory associated to the attribute value template 87 */ 88 static void 89 xsltFreeAttrVT(xsltAttrVTPtr avt) { 90 int i; 91 92 if (avt == NULL) return; 93 94 if (avt->strstart == 1) { 95 for (i = 0;i < avt->nb_seg; i += 2) 96 if (avt->segments[i] != NULL) 97 xmlFree((xmlChar *) avt->segments[i]); 98 for (i = 1;i < avt->nb_seg; i += 2) 99 xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]); 100 } else { 101 for (i = 0;i < avt->nb_seg; i += 2) 102 xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]); 103 for (i = 1;i < avt->nb_seg; i += 2) 104 if (avt->segments[i] != NULL) 105 xmlFree((xmlChar *) avt->segments[i]); 106 } 107 if (avt->nsList != NULL) 108 xmlFree(avt->nsList); 109 xmlFree(avt); 110 } 111 112 /** 113 * xsltFreeAVTList: 114 * @avt: pointer to an list of AVT structures 115 * 116 * Free up the memory associated to the attribute value templates 117 */ 118 void 119 xsltFreeAVTList(void *avt) { 120 xsltAttrVTPtr cur = (xsltAttrVTPtr) avt, next; 121 122 while (cur != NULL) { 123 next = cur->next; 124 xsltFreeAttrVT(cur); 125 cur = next; 126 } 127 } 128 /** 129 * xsltSetAttrVTsegment: 130 * @ avt: pointer to an xsltAttrVT structure 131 * @ val: the value to be set to the next available segment 132 * 133 * Within xsltCompileAttr there are several places where a value 134 * needs to be added to the 'segments' array within the xsltAttrVT 135 * structure, and at each place the allocated size may have to be 136 * re-allocated. This routine takes care of that situation. 137 * 138 * Returns the avt pointer, which may have been changed by a re-alloc 139 */ 140 static xsltAttrVTPtr 141 xsltSetAttrVTsegment(xsltAttrVTPtr avt, void *val) { 142 if (avt->nb_seg >= avt->max_seg) { 143 size_t size = sizeof(xsltAttrVT) + 144 (avt->max_seg + MAX_AVT_SEG) * sizeof(void *); 145 xsltAttrVTPtr tmp = (xsltAttrVTPtr) xmlRealloc(avt, size); 146 if (tmp == NULL) { 147 xsltFreeAttrVT(avt); 148 return NULL; 149 } 150 avt = tmp; 151 memset(&avt->segments[avt->nb_seg], 0, MAX_AVT_SEG*sizeof(void *)); 152 avt->max_seg += MAX_AVT_SEG; 153 } 154 avt->segments[avt->nb_seg++] = val; 155 return avt; 156 } 157 158 /** 159 * xsltCompileAttr: 160 * @style: a XSLT process context 161 * @attr: the attribute coming from the stylesheet. 162 * 163 * Precompile an attribute in a stylesheet, basically it checks if it is 164 * an attribute value template, and if yes, establish some structures needed 165 * to process it at transformation time. 166 */ 167 void 168 xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) { 169 const xmlChar *str; 170 const xmlChar *cur; 171 xmlChar *ret = NULL; 172 xmlChar *expr = NULL; 173 xsltAttrVTPtr avt; 174 int i = 0, lastavt = 0; 175 176 if ((style == NULL) || (attr == NULL) || (attr->children == NULL)) 177 return; 178 if ((attr->children->type != XML_TEXT_NODE) || 179 (attr->children->next != NULL)) { 180 xsltTransformError(NULL, style, attr->parent, 181 "Attribute '%s': The content is expected to be a single text " 182 "node when compiling an AVT.\n", attr->name); 183 style->errors++; 184 return; 185 } 186 str = attr->children->content; 187 if ((xmlStrchr(str, '{') == NULL) && 188 (xmlStrchr(str, '}') == NULL)) return; 189 190 #ifdef WITH_XSLT_DEBUG_AVT 191 xsltGenericDebug(xsltGenericDebugContext, 192 "Found AVT %s: %s\n", attr->name, str); 193 #endif 194 if (attr->psvi != NULL) { 195 #ifdef WITH_XSLT_DEBUG_AVT 196 xsltGenericDebug(xsltGenericDebugContext, 197 "AVT %s: already compiled\n", attr->name); 198 #endif 199 return; 200 } 201 /* 202 * Create a new AVT object. 203 */ 204 avt = xsltNewAttrVT(style); 205 if (avt == NULL) 206 return; 207 attr->psvi = avt; 208 209 avt->nsList = xmlGetNsList(attr->doc, attr->parent); 210 if (avt->nsList != NULL) { 211 while (avt->nsList[i] != NULL) 212 i++; 213 } 214 avt->nsNr = i; 215 216 cur = str; 217 while (*cur != 0) { 218 if (*cur == '{') { 219 if (*(cur+1) == '{') { /* escaped '{' */ 220 cur++; 221 ret = xmlStrncat(ret, str, cur - str); 222 cur++; 223 str = cur; 224 continue; 225 } 226 if (*(cur+1) == '}') { /* skip empty AVT */ 227 ret = xmlStrncat(ret, str, cur - str); 228 cur += 2; 229 str = cur; 230 continue; 231 } 232 if ((ret != NULL) || (cur - str > 0)) { 233 ret = xmlStrncat(ret, str, cur - str); 234 str = cur; 235 if (avt->nb_seg == 0) 236 avt->strstart = 1; 237 if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL) 238 goto error; 239 ret = NULL; 240 lastavt = 0; 241 } 242 243 cur++; 244 while ((*cur != 0) && (*cur != '}')) { 245 /* Need to check for literal (bug539741) */ 246 if ((*cur == '\'') || (*cur == '"')) { 247 char delim = *(cur++); 248 while ((*cur != 0) && (*cur != delim)) 249 cur++; 250 if (*cur != 0) 251 cur++; /* skip the ending delimiter */ 252 } else 253 cur++; 254 } 255 if (*cur == 0) { 256 xsltTransformError(NULL, style, attr->parent, 257 "Attribute '%s': The AVT has an unmatched '{'.\n", 258 attr->name); 259 style->errors++; 260 goto error; 261 } 262 str++; 263 expr = xmlStrndup(str, cur - str); 264 if (expr == NULL) { 265 /* 266 * TODO: What needs to be done here? 267 */ 268 XSLT_TODO 269 goto error; 270 } else { 271 xmlXPathCompExprPtr comp; 272 273 comp = xsltXPathCompile(style, expr); 274 if (comp == NULL) { 275 xsltTransformError(NULL, style, attr->parent, 276 "Attribute '%s': Failed to compile the expression " 277 "'%s' in the AVT.\n", attr->name, expr); 278 style->errors++; 279 goto error; 280 } 281 if (avt->nb_seg == 0) 282 avt->strstart = 0; 283 if (lastavt == 1) { 284 if ((avt = xsltSetAttrVTsegment(avt, NULL)) == NULL) 285 goto error; 286 } 287 if ((avt = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL) 288 goto error; 289 lastavt = 1; 290 xmlFree(expr); 291 expr = NULL; 292 } 293 cur++; 294 str = cur; 295 } else if (*cur == '}') { 296 cur++; 297 if (*cur == '}') { /* escaped '}' */ 298 ret = xmlStrncat(ret, str, cur - str); 299 cur++; 300 str = cur; 301 continue; 302 } else { 303 xsltTransformError(NULL, style, attr->parent, 304 "Attribute '%s': The AVT has an unmatched '}'.\n", 305 attr->name); 306 goto error; 307 } 308 } else 309 cur++; 310 } 311 if ((ret != NULL) || (cur - str > 0)) { 312 ret = xmlStrncat(ret, str, cur - str); 313 str = cur; 314 if (avt->nb_seg == 0) 315 avt->strstart = 1; 316 if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL) 317 goto error; 318 ret = NULL; 319 } 320 321 error: 322 if (avt == NULL) { 323 xsltTransformError(NULL, style, attr->parent, 324 "xsltCompileAttr: malloc problem\n"); 325 } else { 326 if (attr->psvi != avt) { /* may have changed from realloc */ 327 attr->psvi = avt; 328 /* 329 * This is a "hack", but I can't see any clean method of 330 * doing it. If a re-alloc has taken place, then the pointer 331 * for this AVT may have changed. style->attVTs was set by 332 * xsltNewAttrVT, so it needs to be re-set to the new value! 333 */ 334 style->attVTs = avt; 335 } 336 } 337 if (ret != NULL) 338 xmlFree(ret); 339 if (expr != NULL) 340 xmlFree(expr); 341 } 342 343 344 /** 345 * xsltEvalAVT: 346 * @ctxt: the XSLT transformation context 347 * @avt: the prevompiled attribute value template info 348 * @node: the node hosting the attribute 349 * 350 * Process the given AVT, and return the new string value. 351 * 352 * Returns the computed string value or NULL, must be deallocated by the 353 * caller. 354 */ 355 xmlChar * 356 xsltEvalAVT(xsltTransformContextPtr ctxt, void *avt, xmlNodePtr node) { 357 xmlChar *ret = NULL, *tmp; 358 xmlXPathCompExprPtr comp; 359 xsltAttrVTPtr cur = (xsltAttrVTPtr) avt; 360 int i; 361 int str; 362 363 if ((ctxt == NULL) || (avt == NULL) || (node == NULL)) 364 return(NULL); 365 str = cur->strstart; 366 for (i = 0;i < cur->nb_seg;i++) { 367 if (str) { 368 ret = xmlStrcat(ret, (const xmlChar *) cur->segments[i]); 369 } else { 370 comp = (xmlXPathCompExprPtr) cur->segments[i]; 371 tmp = xsltEvalXPathStringNs(ctxt, comp, cur->nsNr, cur->nsList); 372 if (tmp != NULL) { 373 if (ret != NULL) { 374 ret = xmlStrcat(ret, tmp); 375 xmlFree(tmp); 376 } else { 377 ret = tmp; 378 } 379 } 380 } 381 str = !str; 382 } 383 return(ret); 384 } 385