| OLD | NEW |
| (Empty) |
| 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 | |
| 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 } | |
| OLD | NEW |