| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * xslt.c: Implemetation of an XSL Transformation 1.0 engine | |
| 3 * | |
| 4 * Reference: | |
| 5 * XSLT specification | |
| 6 * http://www.w3.org/TR/1999/REC-xslt-19991116 | |
| 7 * | |
| 8 * Associating Style Sheets with XML documents | |
| 9 * http://www.w3.org/1999/06/REC-xml-stylesheet-19990629 | |
| 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/parser.h> | |
| 23 #include <libxml/tree.h> | |
| 24 #include <libxml/valid.h> | |
| 25 #include <libxml/hash.h> | |
| 26 #include <libxml/uri.h> | |
| 27 #include <libxml/xmlerror.h> | |
| 28 #include <libxml/parserInternals.h> | |
| 29 #include <libxml/xpathInternals.h> | |
| 30 #include <libxml/xpath.h> | |
| 31 #include "xslt.h" | |
| 32 #include "xsltInternals.h" | |
| 33 #include "pattern.h" | |
| 34 #include "variables.h" | |
| 35 #include "namespaces.h" | |
| 36 #include "attributes.h" | |
| 37 #include "xsltutils.h" | |
| 38 #include "imports.h" | |
| 39 #include "keys.h" | |
| 40 #include "documents.h" | |
| 41 #include "extensions.h" | |
| 42 #include "preproc.h" | |
| 43 #include "extra.h" | |
| 44 #include "security.h" | |
| 45 | |
| 46 #ifdef WITH_XSLT_DEBUG | |
| 47 #define WITH_XSLT_DEBUG_PARSING | |
| 48 /* #define WITH_XSLT_DEBUG_BLANKS */ | |
| 49 #endif | |
| 50 | |
| 51 const char *xsltEngineVersion = LIBXSLT_VERSION_STRING LIBXSLT_VERSION_EXTRA; | |
| 52 const int xsltLibxsltVersion = LIBXSLT_VERSION; | |
| 53 const int xsltLibxmlVersion = LIBXML_VERSION; | |
| 54 | |
| 55 #ifdef XSLT_REFACTORED | |
| 56 | |
| 57 const xmlChar *xsltConstNamespaceNameXSLT = (const xmlChar *) XSLT_NAMESPACE; | |
| 58 | |
| 59 #define XSLT_ELEMENT_CATEGORY_XSLT 0 | |
| 60 #define XSLT_ELEMENT_CATEGORY_EXTENSION 1 | |
| 61 #define XSLT_ELEMENT_CATEGORY_LRE 2 | |
| 62 | |
| 63 /* | |
| 64 * xsltLiteralResultMarker: | |
| 65 * Marker for Literal result elements, in order to avoid multiple attempts | |
| 66 * to recognize such elements in the stylesheet's tree. | |
| 67 * This marker is set on node->psvi during the initial traversal | |
| 68 * of a stylesheet's node tree. | |
| 69 * | |
| 70 const xmlChar *xsltLiteralResultMarker = | |
| 71 (const xmlChar *) "Literal Result Element"; | |
| 72 */ | |
| 73 | |
| 74 /* | |
| 75 * xsltXSLTTextMarker: | |
| 76 * Marker for xsl:text elements. Used to recognize xsl:text elements | |
| 77 * for post-processing of the stylesheet's tree, where those | |
| 78 * elements are removed from the tree. | |
| 79 */ | |
| 80 const xmlChar *xsltXSLTTextMarker = (const xmlChar *) "XSLT Text Element"; | |
| 81 | |
| 82 /* | |
| 83 * xsltXSLTAttrMarker: | |
| 84 * Marker for XSLT attribute on Literal Result Elements. | |
| 85 */ | |
| 86 const xmlChar *xsltXSLTAttrMarker = (const xmlChar *) "LRE XSLT Attr"; | |
| 87 | |
| 88 #endif | |
| 89 | |
| 90 #ifdef XSLT_LOCALE_WINAPI | |
| 91 extern xmlRMutexPtr xsltLocaleMutex; | |
| 92 #endif | |
| 93 /* | |
| 94 * Harmless but avoiding a problem when compiling against a | |
| 95 * libxml <= 2.3.11 without LIBXML_DEBUG_ENABLED | |
| 96 */ | |
| 97 #ifndef LIBXML_DEBUG_ENABLED | |
| 98 double xmlXPathStringEvalNumber(const xmlChar *str); | |
| 99 #endif | |
| 100 /* | |
| 101 * Useful macros | |
| 102 */ | |
| 103 | |
| 104 #ifdef IS_BLANK | |
| 105 #undef IS_BLANK | |
| 106 #endif | |
| 107 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \ | |
| 108 ((c) == 0x0D)) | |
| 109 | |
| 110 #ifdef IS_BLANK_NODE | |
| 111 #undef IS_BLANK_NODE | |
| 112 #endif | |
| 113 #define IS_BLANK_NODE(n) \ | |
| 114 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) | |
| 115 | |
| 116 /** | |
| 117 * xsltParseContentError: | |
| 118 * | |
| 119 * @style: the stylesheet | |
| 120 * @node: the node where the error occured | |
| 121 * | |
| 122 * Compile-time error function. | |
| 123 */ | |
| 124 static void | |
| 125 xsltParseContentError(xsltStylesheetPtr style, | |
| 126 xmlNodePtr node) | |
| 127 { | |
| 128 if ((style == NULL) || (node == NULL)) | |
| 129 return; | |
| 130 | |
| 131 if (IS_XSLT_ELEM(node)) | |
| 132 xsltTransformError(NULL, style, node, | |
| 133 "The XSLT-element '%s' is not allowed at this position.\n", | |
| 134 node->name); | |
| 135 else | |
| 136 xsltTransformError(NULL, style, node, | |
| 137 "The element '%s' is not allowed at this position.\n", | |
| 138 node->name); | |
| 139 style->errors++; | |
| 140 } | |
| 141 | |
| 142 #ifdef XSLT_REFACTORED | |
| 143 #else | |
| 144 /** | |
| 145 * exclPrefixPush: | |
| 146 * @style: the transformation stylesheet | |
| 147 * @value: the excluded namespace name to push on the stack | |
| 148 * | |
| 149 * Push an excluded namespace name on the stack | |
| 150 * | |
| 151 * Returns the new index in the stack or -1 if already present or | |
| 152 * in case of error | |
| 153 */ | |
| 154 static int | |
| 155 exclPrefixPush(xsltStylesheetPtr style, xmlChar * value) | |
| 156 { | |
| 157 int i; | |
| 158 | |
| 159 if (style->exclPrefixMax == 0) { | |
| 160 style->exclPrefixMax = 4; | |
| 161 style->exclPrefixTab = | |
| 162 (xmlChar * *)xmlMalloc(style->exclPrefixMax * | |
| 163 sizeof(style->exclPrefixTab[0])); | |
| 164 if (style->exclPrefixTab == NULL) { | |
| 165 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); | |
| 166 return (-1); | |
| 167 } | |
| 168 } | |
| 169 /* do not push duplicates */ | |
| 170 for (i = 0;i < style->exclPrefixNr;i++) { | |
| 171 if (xmlStrEqual(style->exclPrefixTab[i], value)) | |
| 172 return(-1); | |
| 173 } | |
| 174 if (style->exclPrefixNr >= style->exclPrefixMax) { | |
| 175 style->exclPrefixMax *= 2; | |
| 176 style->exclPrefixTab = | |
| 177 (xmlChar * *)xmlRealloc(style->exclPrefixTab, | |
| 178 style->exclPrefixMax * | |
| 179 sizeof(style->exclPrefixTab[0])); | |
| 180 if (style->exclPrefixTab == NULL) { | |
| 181 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); | |
| 182 return (-1); | |
| 183 } | |
| 184 } | |
| 185 style->exclPrefixTab[style->exclPrefixNr] = value; | |
| 186 style->exclPrefix = value; | |
| 187 return (style->exclPrefixNr++); | |
| 188 } | |
| 189 /** | |
| 190 * exclPrefixPop: | |
| 191 * @style: the transformation stylesheet | |
| 192 * | |
| 193 * Pop an excluded prefix value from the stack | |
| 194 * | |
| 195 * Returns the stored excluded prefix value | |
| 196 */ | |
| 197 static xmlChar * | |
| 198 exclPrefixPop(xsltStylesheetPtr style) | |
| 199 { | |
| 200 xmlChar *ret; | |
| 201 | |
| 202 if (style->exclPrefixNr <= 0) | |
| 203 return (0); | |
| 204 style->exclPrefixNr--; | |
| 205 if (style->exclPrefixNr > 0) | |
| 206 style->exclPrefix = style->exclPrefixTab[style->exclPrefixNr - 1]; | |
| 207 else | |
| 208 style->exclPrefix = NULL; | |
| 209 ret = style->exclPrefixTab[style->exclPrefixNr]; | |
| 210 style->exclPrefixTab[style->exclPrefixNr] = 0; | |
| 211 return (ret); | |
| 212 } | |
| 213 #endif | |
| 214 | |
| 215 /************************************************************************ | |
| 216 * * | |
| 217 * Helper functions * | |
| 218 * * | |
| 219 ************************************************************************/ | |
| 220 | |
| 221 static int initialized = 0; | |
| 222 /** | |
| 223 * xsltInit: | |
| 224 * | |
| 225 * Initializes the processor (e.g. registers built-in extensions, | |
| 226 * etc.) | |
| 227 */ | |
| 228 void | |
| 229 xsltInit (void) { | |
| 230 if (initialized == 0) { | |
| 231 initialized = 1; | |
| 232 #ifdef XSLT_LOCALE_WINAPI | |
| 233 xsltLocaleMutex = xmlNewRMutex(); | |
| 234 #endif | |
| 235 xsltRegisterAllExtras(); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 /** | |
| 240 * xsltUninit: | |
| 241 * | |
| 242 * Uninitializes the processor. | |
| 243 */ | |
| 244 void | |
| 245 xsltUninit (void) { | |
| 246 #ifdef XSLT_LOCALE_WINAPI | |
| 247 xmlFreeRMutex(xsltLocaleMutex); | |
| 248 xsltLocaleMutex = NULL; | |
| 249 #endif | |
| 250 initialized = 0; | |
| 251 } | |
| 252 | |
| 253 /** | |
| 254 * xsltIsBlank: | |
| 255 * @str: a string | |
| 256 * | |
| 257 * Check if a string is ignorable | |
| 258 * | |
| 259 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise | |
| 260 */ | |
| 261 int | |
| 262 xsltIsBlank(xmlChar *str) { | |
| 263 if (str == NULL) | |
| 264 return(1); | |
| 265 while (*str != 0) { | |
| 266 if (!(IS_BLANK(*str))) return(0); | |
| 267 str++; | |
| 268 } | |
| 269 return(1); | |
| 270 } | |
| 271 | |
| 272 /************************************************************************ | |
| 273 * * | |
| 274 * Routines to handle XSLT data structures * | |
| 275 * * | |
| 276 ************************************************************************/ | |
| 277 static xsltDecimalFormatPtr | |
| 278 xsltNewDecimalFormat(const xmlChar *nsUri, xmlChar *name) | |
| 279 { | |
| 280 xsltDecimalFormatPtr self; | |
| 281 /* UTF-8 for 0x2030 */ | |
| 282 static const xmlChar permille[4] = {0xe2, 0x80, 0xb0, 0}; | |
| 283 | |
| 284 self = xmlMalloc(sizeof(xsltDecimalFormat)); | |
| 285 if (self != NULL) { | |
| 286 self->next = NULL; | |
| 287 self->nsUri = nsUri; | |
| 288 self->name = name; | |
| 289 | |
| 290 /* Default values */ | |
| 291 self->digit = xmlStrdup(BAD_CAST("#")); | |
| 292 self->patternSeparator = xmlStrdup(BAD_CAST(";")); | |
| 293 self->decimalPoint = xmlStrdup(BAD_CAST(".")); | |
| 294 self->grouping = xmlStrdup(BAD_CAST(",")); | |
| 295 self->percent = xmlStrdup(BAD_CAST("%")); | |
| 296 self->permille = xmlStrdup(BAD_CAST(permille)); | |
| 297 self->zeroDigit = xmlStrdup(BAD_CAST("0")); | |
| 298 self->minusSign = xmlStrdup(BAD_CAST("-")); | |
| 299 self->infinity = xmlStrdup(BAD_CAST("Infinity")); | |
| 300 self->noNumber = xmlStrdup(BAD_CAST("NaN")); | |
| 301 } | |
| 302 return self; | |
| 303 } | |
| 304 | |
| 305 static void | |
| 306 xsltFreeDecimalFormat(xsltDecimalFormatPtr self) | |
| 307 { | |
| 308 if (self != NULL) { | |
| 309 if (self->digit) | |
| 310 xmlFree(self->digit); | |
| 311 if (self->patternSeparator) | |
| 312 xmlFree(self->patternSeparator); | |
| 313 if (self->decimalPoint) | |
| 314 xmlFree(self->decimalPoint); | |
| 315 if (self->grouping) | |
| 316 xmlFree(self->grouping); | |
| 317 if (self->percent) | |
| 318 xmlFree(self->percent); | |
| 319 if (self->permille) | |
| 320 xmlFree(self->permille); | |
| 321 if (self->zeroDigit) | |
| 322 xmlFree(self->zeroDigit); | |
| 323 if (self->minusSign) | |
| 324 xmlFree(self->minusSign); | |
| 325 if (self->infinity) | |
| 326 xmlFree(self->infinity); | |
| 327 if (self->noNumber) | |
| 328 xmlFree(self->noNumber); | |
| 329 if (self->name) | |
| 330 xmlFree(self->name); | |
| 331 xmlFree(self); | |
| 332 } | |
| 333 } | |
| 334 | |
| 335 static void | |
| 336 xsltFreeDecimalFormatList(xsltStylesheetPtr self) | |
| 337 { | |
| 338 xsltDecimalFormatPtr iter; | |
| 339 xsltDecimalFormatPtr tmp; | |
| 340 | |
| 341 if (self == NULL) | |
| 342 return; | |
| 343 | |
| 344 iter = self->decimalFormat; | |
| 345 while (iter != NULL) { | |
| 346 tmp = iter->next; | |
| 347 xsltFreeDecimalFormat(iter); | |
| 348 iter = tmp; | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 /** | |
| 353 * xsltDecimalFormatGetByName: | |
| 354 * @style: the XSLT stylesheet | |
| 355 * @name: the decimal-format name to find | |
| 356 * | |
| 357 * Find decimal-format by name | |
| 358 * | |
| 359 * Returns the xsltDecimalFormatPtr | |
| 360 */ | |
| 361 xsltDecimalFormatPtr | |
| 362 xsltDecimalFormatGetByName(xsltStylesheetPtr style, xmlChar *name) | |
| 363 { | |
| 364 xsltDecimalFormatPtr result = NULL; | |
| 365 | |
| 366 if (name == NULL) | |
| 367 return style->decimalFormat; | |
| 368 | |
| 369 while (style != NULL) { | |
| 370 for (result = style->decimalFormat->next; | |
| 371 result != NULL; | |
| 372 result = result->next) { | |
| 373 if ((result->nsUri == NULL) && xmlStrEqual(name, result->name)) | |
| 374 return result; | |
| 375 } | |
| 376 style = xsltNextImport(style); | |
| 377 } | |
| 378 return result; | |
| 379 } | |
| 380 | |
| 381 /** | |
| 382 * xsltDecimalFormatGetByQName: | |
| 383 * @style: the XSLT stylesheet | |
| 384 * @nsUri: the namespace URI of the QName | |
| 385 * @name: the local part of the QName | |
| 386 * | |
| 387 * Find decimal-format by QName | |
| 388 * | |
| 389 * Returns the xsltDecimalFormatPtr | |
| 390 */ | |
| 391 xsltDecimalFormatPtr | |
| 392 xsltDecimalFormatGetByQName(xsltStylesheetPtr style, const xmlChar *nsUri, | |
| 393 const xmlChar *name) | |
| 394 { | |
| 395 xsltDecimalFormatPtr result = NULL; | |
| 396 | |
| 397 if (name == NULL) | |
| 398 return style->decimalFormat; | |
| 399 | |
| 400 while (style != NULL) { | |
| 401 for (result = style->decimalFormat->next; | |
| 402 result != NULL; | |
| 403 result = result->next) { | |
| 404 if (xmlStrEqual(nsUri, result->nsUri) && | |
| 405 xmlStrEqual(name, result->name)) | |
| 406 return result; | |
| 407 } | |
| 408 style = xsltNextImport(style); | |
| 409 } | |
| 410 return result; | |
| 411 } | |
| 412 | |
| 413 | |
| 414 /** | |
| 415 * xsltNewTemplate: | |
| 416 * | |
| 417 * Create a new XSLT Template | |
| 418 * | |
| 419 * Returns the newly allocated xsltTemplatePtr or NULL in case of error | |
| 420 */ | |
| 421 static xsltTemplatePtr | |
| 422 xsltNewTemplate(void) { | |
| 423 xsltTemplatePtr cur; | |
| 424 | |
| 425 cur = (xsltTemplatePtr) xmlMalloc(sizeof(xsltTemplate)); | |
| 426 if (cur == NULL) { | |
| 427 xsltTransformError(NULL, NULL, NULL, | |
| 428 "xsltNewTemplate : malloc failed\n"); | |
| 429 return(NULL); | |
| 430 } | |
| 431 memset(cur, 0, sizeof(xsltTemplate)); | |
| 432 cur->priority = XSLT_PAT_NO_PRIORITY; | |
| 433 return(cur); | |
| 434 } | |
| 435 | |
| 436 /** | |
| 437 * xsltFreeTemplate: | |
| 438 * @template: an XSLT template | |
| 439 * | |
| 440 * Free up the memory allocated by @template | |
| 441 */ | |
| 442 static void | |
| 443 xsltFreeTemplate(xsltTemplatePtr template) { | |
| 444 if (template == NULL) | |
| 445 return; | |
| 446 if (template->match) xmlFree(template->match); | |
| 447 /* | |
| 448 * NOTE: @name and @nameURI are put into the string dict now. | |
| 449 * if (template->name) xmlFree(template->name); | |
| 450 * if (template->nameURI) xmlFree(template->nameURI); | |
| 451 */ | |
| 452 /* | |
| 453 if (template->mode) xmlFree(template->mode); | |
| 454 if (template->modeURI) xmlFree(template->modeURI); | |
| 455 */ | |
| 456 if (template->inheritedNs) xmlFree(template->inheritedNs); | |
| 457 | |
| 458 /* free profiling data */ | |
| 459 if (template->templCalledTab) xmlFree(template->templCalledTab); | |
| 460 if (template->templCountTab) xmlFree(template->templCountTab); | |
| 461 | |
| 462 memset(template, -1, sizeof(xsltTemplate)); | |
| 463 xmlFree(template); | |
| 464 } | |
| 465 | |
| 466 /** | |
| 467 * xsltFreeTemplateList: | |
| 468 * @template: an XSLT template list | |
| 469 * | |
| 470 * Free up the memory allocated by all the elements of @template | |
| 471 */ | |
| 472 static void | |
| 473 xsltFreeTemplateList(xsltTemplatePtr template) { | |
| 474 xsltTemplatePtr cur; | |
| 475 | |
| 476 while (template != NULL) { | |
| 477 cur = template; | |
| 478 template = template->next; | |
| 479 xsltFreeTemplate(cur); | |
| 480 } | |
| 481 } | |
| 482 | |
| 483 #ifdef XSLT_REFACTORED | |
| 484 | |
| 485 static void | |
| 486 xsltFreeNsAliasList(xsltNsAliasPtr item) | |
| 487 { | |
| 488 xsltNsAliasPtr tmp; | |
| 489 | |
| 490 while (item) { | |
| 491 tmp = item; | |
| 492 item = item->next; | |
| 493 xmlFree(tmp); | |
| 494 } | |
| 495 return; | |
| 496 } | |
| 497 | |
| 498 #ifdef XSLT_REFACTORED_XSLT_NSCOMP | |
| 499 static void | |
| 500 xsltFreeNamespaceMap(xsltNsMapPtr item) | |
| 501 { | |
| 502 xsltNsMapPtr tmp; | |
| 503 | |
| 504 while (item) { | |
| 505 tmp = item; | |
| 506 item = item->next; | |
| 507 xmlFree(tmp); | |
| 508 } | |
| 509 return; | |
| 510 } | |
| 511 | |
| 512 static xsltNsMapPtr | |
| 513 xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt, | |
| 514 xmlDocPtr doc, | |
| 515 xmlNsPtr ns, | |
| 516 xmlNodePtr elem) | |
| 517 { | |
| 518 xsltNsMapPtr ret; | |
| 519 | |
| 520 if ((cctxt == NULL) || (doc == NULL) || (ns == NULL)) | |
| 521 return(NULL); | |
| 522 | |
| 523 ret = (xsltNsMapPtr) xmlMalloc(sizeof(xsltNsMap)); | |
| 524 if (ret == NULL) { | |
| 525 xsltTransformError(NULL, cctxt->style, elem, | |
| 526 "Internal error: (xsltNewNamespaceMapItem) " | |
| 527 "memory allocation failed.\n"); | |
| 528 return(NULL); | |
| 529 } | |
| 530 memset(ret, 0, sizeof(xsltNsMap)); | |
| 531 ret->doc = doc; | |
| 532 ret->ns = ns; | |
| 533 ret->origNsName = ns->href; | |
| 534 /* | |
| 535 * Store the item at current stylesheet-level. | |
| 536 */ | |
| 537 if (cctxt->psData->nsMap != NULL) | |
| 538 ret->next = cctxt->psData->nsMap; | |
| 539 cctxt->psData->nsMap = ret; | |
| 540 | |
| 541 return(ret); | |
| 542 } | |
| 543 #endif /* XSLT_REFACTORED_XSLT_NSCOMP */ | |
| 544 | |
| 545 /** | |
| 546 * xsltCompilerVarInfoFree: | |
| 547 * @cctxt: the compilation context | |
| 548 * | |
| 549 * Frees the list of information for vars/params. | |
| 550 */ | |
| 551 static void | |
| 552 xsltCompilerVarInfoFree(xsltCompilerCtxtPtr cctxt) | |
| 553 { | |
| 554 xsltVarInfoPtr ivar = cctxt->ivars, ivartmp; | |
| 555 | |
| 556 while (ivar) { | |
| 557 ivartmp = ivar; | |
| 558 ivar = ivar->next; | |
| 559 xmlFree(ivartmp); | |
| 560 } | |
| 561 } | |
| 562 | |
| 563 /** | |
| 564 * xsltCompilerCtxtFree: | |
| 565 * | |
| 566 * Free an XSLT compiler context. | |
| 567 */ | |
| 568 static void | |
| 569 xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt) | |
| 570 { | |
| 571 if (cctxt == NULL) | |
| 572 return; | |
| 573 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 574 xsltGenericDebug(xsltGenericDebugContext, | |
| 575 "Freeing compilation context\n"); | |
| 576 xsltGenericDebug(xsltGenericDebugContext, | |
| 577 "### Max inodes: %d\n", cctxt->maxNodeInfos); | |
| 578 xsltGenericDebug(xsltGenericDebugContext, | |
| 579 "### Max LREs : %d\n", cctxt->maxLREs); | |
| 580 #endif | |
| 581 /* | |
| 582 * Free node-infos. | |
| 583 */ | |
| 584 if (cctxt->inodeList != NULL) { | |
| 585 xsltCompilerNodeInfoPtr tmp, cur = cctxt->inodeList; | |
| 586 while (cur != NULL) { | |
| 587 tmp = cur; | |
| 588 cur = cur->next; | |
| 589 xmlFree(tmp); | |
| 590 } | |
| 591 } | |
| 592 if (cctxt->tmpList != NULL) | |
| 593 xsltPointerListFree(cctxt->tmpList); | |
| 594 #ifdef XSLT_REFACTORED_XPATHCOMP | |
| 595 if (cctxt->xpathCtxt != NULL) | |
| 596 xmlXPathFreeContext(cctxt->xpathCtxt); | |
| 597 #endif | |
| 598 if (cctxt->nsAliases != NULL) | |
| 599 xsltFreeNsAliasList(cctxt->nsAliases); | |
| 600 | |
| 601 if (cctxt->ivars) | |
| 602 xsltCompilerVarInfoFree(cctxt); | |
| 603 | |
| 604 xmlFree(cctxt); | |
| 605 } | |
| 606 | |
| 607 /** | |
| 608 * xsltCompilerCreate: | |
| 609 * | |
| 610 * Creates an XSLT compiler context. | |
| 611 * | |
| 612 * Returns the pointer to the created xsltCompilerCtxt or | |
| 613 * NULL in case of an internal error. | |
| 614 */ | |
| 615 static xsltCompilerCtxtPtr | |
| 616 xsltCompilationCtxtCreate(xsltStylesheetPtr style) { | |
| 617 xsltCompilerCtxtPtr ret; | |
| 618 | |
| 619 ret = (xsltCompilerCtxtPtr) xmlMalloc(sizeof(xsltCompilerCtxt)); | |
| 620 if (ret == NULL) { | |
| 621 xsltTransformError(NULL, style, NULL, | |
| 622 "xsltCompilerCreate: allocation of compiler " | |
| 623 "context failed.\n"); | |
| 624 return(NULL); | |
| 625 } | |
| 626 memset(ret, 0, sizeof(xsltCompilerCtxt)); | |
| 627 | |
| 628 ret->errSeverity = XSLT_ERROR_SEVERITY_ERROR; | |
| 629 ret->tmpList = xsltPointerListCreate(20); | |
| 630 if (ret->tmpList == NULL) { | |
| 631 goto internal_err; | |
| 632 } | |
| 633 #ifdef XSLT_REFACTORED_XPATHCOMP | |
| 634 /* | |
| 635 * Create the XPath compilation context in order | |
| 636 * to speed up precompilation of XPath expressions. | |
| 637 */ | |
| 638 ret->xpathCtxt = xmlXPathNewContext(NULL); | |
| 639 if (ret->xpathCtxt == NULL) | |
| 640 goto internal_err; | |
| 641 #endif | |
| 642 | |
| 643 return(ret); | |
| 644 | |
| 645 internal_err: | |
| 646 xsltCompilationCtxtFree(ret); | |
| 647 return(NULL); | |
| 648 } | |
| 649 | |
| 650 static void | |
| 651 xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first) | |
| 652 { | |
| 653 xsltEffectiveNsPtr tmp; | |
| 654 | |
| 655 while (first != NULL) { | |
| 656 tmp = first; | |
| 657 first = first->nextInStore; | |
| 658 xmlFree(tmp); | |
| 659 } | |
| 660 } | |
| 661 | |
| 662 static void | |
| 663 xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data) | |
| 664 { | |
| 665 if (data == NULL) | |
| 666 return; | |
| 667 | |
| 668 if (data->inScopeNamespaces != NULL) { | |
| 669 int i; | |
| 670 xsltNsListContainerPtr nsi; | |
| 671 xsltPointerListPtr list = | |
| 672 (xsltPointerListPtr) data->inScopeNamespaces; | |
| 673 | |
| 674 for (i = 0; i < list->number; i++) { | |
| 675 /* | |
| 676 * REVISIT TODO: Free info of in-scope namespaces. | |
| 677 */ | |
| 678 nsi = (xsltNsListContainerPtr) list->items[i]; | |
| 679 if (nsi->list != NULL) | |
| 680 xmlFree(nsi->list); | |
| 681 xmlFree(nsi); | |
| 682 } | |
| 683 xsltPointerListFree(list); | |
| 684 data->inScopeNamespaces = NULL; | |
| 685 } | |
| 686 | |
| 687 if (data->exclResultNamespaces != NULL) { | |
| 688 int i; | |
| 689 xsltPointerListPtr list = (xsltPointerListPtr) | |
| 690 data->exclResultNamespaces; | |
| 691 | |
| 692 for (i = 0; i < list->number; i++) | |
| 693 xsltPointerListFree((xsltPointerListPtr) list->items[i]); | |
| 694 | |
| 695 xsltPointerListFree(list); | |
| 696 data->exclResultNamespaces = NULL; | |
| 697 } | |
| 698 | |
| 699 if (data->extElemNamespaces != NULL) { | |
| 700 xsltPointerListPtr list = (xsltPointerListPtr) | |
| 701 data->extElemNamespaces; | |
| 702 int i; | |
| 703 | |
| 704 for (i = 0; i < list->number; i++) | |
| 705 xsltPointerListFree((xsltPointerListPtr) list->items[i]); | |
| 706 | |
| 707 xsltPointerListFree(list); | |
| 708 data->extElemNamespaces = NULL; | |
| 709 } | |
| 710 if (data->effectiveNs) { | |
| 711 xsltLREEffectiveNsNodesFree(data->effectiveNs); | |
| 712 data->effectiveNs = NULL; | |
| 713 } | |
| 714 #ifdef XSLT_REFACTORED_XSLT_NSCOMP | |
| 715 xsltFreeNamespaceMap(data->nsMap); | |
| 716 #endif | |
| 717 xmlFree(data); | |
| 718 } | |
| 719 | |
| 720 static xsltPrincipalStylesheetDataPtr | |
| 721 xsltNewPrincipalStylesheetData(void) | |
| 722 { | |
| 723 xsltPrincipalStylesheetDataPtr ret; | |
| 724 | |
| 725 ret = (xsltPrincipalStylesheetDataPtr) | |
| 726 xmlMalloc(sizeof(xsltPrincipalStylesheetData)); | |
| 727 if (ret == NULL) { | |
| 728 xsltTransformError(NULL, NULL, NULL, | |
| 729 "xsltNewPrincipalStylesheetData: memory allocation failed.\n"); | |
| 730 return(NULL); | |
| 731 } | |
| 732 memset(ret, 0, sizeof(xsltPrincipalStylesheetData)); | |
| 733 | |
| 734 /* | |
| 735 * Global list of in-scope namespaces. | |
| 736 */ | |
| 737 ret->inScopeNamespaces = xsltPointerListCreate(-1); | |
| 738 if (ret->inScopeNamespaces == NULL) | |
| 739 goto internal_err; | |
| 740 /* | |
| 741 * Global list of excluded result ns-decls. | |
| 742 */ | |
| 743 ret->exclResultNamespaces = xsltPointerListCreate(-1); | |
| 744 if (ret->exclResultNamespaces == NULL) | |
| 745 goto internal_err; | |
| 746 /* | |
| 747 * Global list of extension instruction namespace names. | |
| 748 */ | |
| 749 ret->extElemNamespaces = xsltPointerListCreate(-1); | |
| 750 if (ret->extElemNamespaces == NULL) | |
| 751 goto internal_err; | |
| 752 | |
| 753 return(ret); | |
| 754 | |
| 755 internal_err: | |
| 756 | |
| 757 return(NULL); | |
| 758 } | |
| 759 | |
| 760 #endif | |
| 761 | |
| 762 /** | |
| 763 * xsltNewStylesheet: | |
| 764 * | |
| 765 * Create a new XSLT Stylesheet | |
| 766 * | |
| 767 * Returns the newly allocated xsltStylesheetPtr or NULL in case of error | |
| 768 */ | |
| 769 xsltStylesheetPtr | |
| 770 xsltNewStylesheet(void) { | |
| 771 xsltStylesheetPtr ret = NULL; | |
| 772 | |
| 773 ret = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet)); | |
| 774 if (ret == NULL) { | |
| 775 xsltTransformError(NULL, NULL, NULL, | |
| 776 "xsltNewStylesheet : malloc failed\n"); | |
| 777 goto internal_err; | |
| 778 } | |
| 779 memset(ret, 0, sizeof(xsltStylesheet)); | |
| 780 | |
| 781 ret->omitXmlDeclaration = -1; | |
| 782 ret->standalone = -1; | |
| 783 ret->decimalFormat = xsltNewDecimalFormat(NULL, NULL); | |
| 784 ret->indent = -1; | |
| 785 ret->errors = 0; | |
| 786 ret->warnings = 0; | |
| 787 ret->exclPrefixNr = 0; | |
| 788 ret->exclPrefixMax = 0; | |
| 789 ret->exclPrefixTab = NULL; | |
| 790 ret->extInfos = NULL; | |
| 791 ret->extrasNr = 0; | |
| 792 ret->internalized = 1; | |
| 793 ret->literal_result = 0; | |
| 794 ret->forwards_compatible = 0; | |
| 795 ret->dict = xmlDictCreate(); | |
| 796 #ifdef WITH_XSLT_DEBUG | |
| 797 xsltGenericDebug(xsltGenericDebugContext, | |
| 798 "creating dictionary for stylesheet\n"); | |
| 799 #endif | |
| 800 | |
| 801 xsltInit(); | |
| 802 | |
| 803 return(ret); | |
| 804 | |
| 805 internal_err: | |
| 806 if (ret != NULL) | |
| 807 xsltFreeStylesheet(ret); | |
| 808 return(NULL); | |
| 809 } | |
| 810 | |
| 811 /** | |
| 812 * xsltAllocateExtra: | |
| 813 * @style: an XSLT stylesheet | |
| 814 * | |
| 815 * Allocate an extra runtime information slot statically while compiling | |
| 816 * the stylesheet and return its number | |
| 817 * | |
| 818 * Returns the number of the slot | |
| 819 */ | |
| 820 int | |
| 821 xsltAllocateExtra(xsltStylesheetPtr style) | |
| 822 { | |
| 823 return(style->extrasNr++); | |
| 824 } | |
| 825 | |
| 826 /** | |
| 827 * xsltAllocateExtraCtxt: | |
| 828 * @ctxt: an XSLT transformation context | |
| 829 * | |
| 830 * Allocate an extra runtime information slot at run-time | |
| 831 * and return its number | |
| 832 * This make sure there is a slot ready in the transformation context | |
| 833 * | |
| 834 * Returns the number of the slot | |
| 835 */ | |
| 836 int | |
| 837 xsltAllocateExtraCtxt(xsltTransformContextPtr ctxt) | |
| 838 { | |
| 839 if (ctxt->extrasNr >= ctxt->extrasMax) { | |
| 840 int i; | |
| 841 if (ctxt->extrasNr == 0) { | |
| 842 ctxt->extrasMax = 20; | |
| 843 ctxt->extras = (xsltRuntimeExtraPtr) | |
| 844 xmlMalloc(ctxt->extrasMax * sizeof(xsltRuntimeExtra)); | |
| 845 if (ctxt->extras == NULL) { | |
| 846 xsltTransformError(ctxt, NULL, NULL, | |
| 847 "xsltAllocateExtraCtxt: out of memory\n"); | |
| 848 return(0); | |
| 849 } | |
| 850 for (i = 0;i < ctxt->extrasMax;i++) { | |
| 851 ctxt->extras[i].info = NULL; | |
| 852 ctxt->extras[i].deallocate = NULL; | |
| 853 ctxt->extras[i].val.ptr = NULL; | |
| 854 } | |
| 855 | |
| 856 } else { | |
| 857 xsltRuntimeExtraPtr tmp; | |
| 858 | |
| 859 ctxt->extrasMax += 100; | |
| 860 tmp = (xsltRuntimeExtraPtr) xmlRealloc(ctxt->extras, | |
| 861 ctxt->extrasMax * sizeof(xsltRuntimeExtra)); | |
| 862 if (tmp == NULL) { | |
| 863 xsltTransformError(ctxt, NULL, NULL, | |
| 864 "xsltAllocateExtraCtxt: out of memory\n"); | |
| 865 return(0); | |
| 866 } | |
| 867 ctxt->extras = tmp; | |
| 868 for (i = ctxt->extrasNr;i < ctxt->extrasMax;i++) { | |
| 869 ctxt->extras[i].info = NULL; | |
| 870 ctxt->extras[i].deallocate = NULL; | |
| 871 ctxt->extras[i].val.ptr = NULL; | |
| 872 } | |
| 873 } | |
| 874 } | |
| 875 return(ctxt->extrasNr++); | |
| 876 } | |
| 877 | |
| 878 /** | |
| 879 * xsltFreeStylesheetList: | |
| 880 * @style: an XSLT stylesheet list | |
| 881 * | |
| 882 * Free up the memory allocated by the list @style | |
| 883 */ | |
| 884 static void | |
| 885 xsltFreeStylesheetList(xsltStylesheetPtr style) { | |
| 886 xsltStylesheetPtr next; | |
| 887 | |
| 888 while (style != NULL) { | |
| 889 next = style->next; | |
| 890 xsltFreeStylesheet(style); | |
| 891 style = next; | |
| 892 } | |
| 893 } | |
| 894 | |
| 895 /** | |
| 896 * xsltCleanupStylesheetTree: | |
| 897 * | |
| 898 * @doc: the document-node | |
| 899 * @node: the element where the stylesheet is rooted at | |
| 900 * | |
| 901 * Actually @node need not be the document-element, but | |
| 902 * currently Libxslt does not support embedded stylesheets. | |
| 903 * | |
| 904 * Returns 0 if OK, -1 on API or internal errors. | |
| 905 */ | |
| 906 static int | |
| 907 xsltCleanupStylesheetTree(xmlDocPtr doc ATTRIBUTE_UNUSED, | |
| 908 xmlNodePtr rootElem ATTRIBUTE_UNUSED) | |
| 909 { | |
| 910 #if 0 /* TODO: Currently disabled, since probably not needed. */ | |
| 911 xmlNodePtr cur; | |
| 912 | |
| 913 if ((doc == NULL) || (rootElem == NULL) || | |
| 914 (rootElem->type != XML_ELEMENT_NODE) || | |
| 915 (doc != rootElem->doc)) | |
| 916 return(-1); | |
| 917 | |
| 918 /* | |
| 919 * Cleanup was suggested by Aleksey Sanin: | |
| 920 * Clear the PSVI field to avoid problems if the | |
| 921 * node-tree of the stylesheet is intended to be used for | |
| 922 * further processing by the user (e.g. for compiling it | |
| 923 * once again - although not recommended). | |
| 924 */ | |
| 925 | |
| 926 cur = rootElem; | |
| 927 while (cur != NULL) { | |
| 928 if (cur->type == XML_ELEMENT_NODE) { | |
| 929 /* | |
| 930 * Clear the PSVI field. | |
| 931 */ | |
| 932 cur->psvi = NULL; | |
| 933 if (cur->children) { | |
| 934 cur = cur->children; | |
| 935 continue; | |
| 936 } | |
| 937 } | |
| 938 | |
| 939 leave_node: | |
| 940 if (cur == rootElem) | |
| 941 break; | |
| 942 if (cur->next != NULL) | |
| 943 cur = cur->next; | |
| 944 else { | |
| 945 cur = cur->parent; | |
| 946 if (cur == NULL) | |
| 947 break; | |
| 948 goto leave_node; | |
| 949 } | |
| 950 } | |
| 951 #endif /* #if 0 */ | |
| 952 return(0); | |
| 953 } | |
| 954 | |
| 955 /** | |
| 956 * xsltFreeStylesheet: | |
| 957 * @style: an XSLT stylesheet | |
| 958 * | |
| 959 * Free up the memory allocated by @style | |
| 960 */ | |
| 961 void | |
| 962 xsltFreeStylesheet(xsltStylesheetPtr style) | |
| 963 { | |
| 964 if (style == NULL) | |
| 965 return; | |
| 966 | |
| 967 #ifdef XSLT_REFACTORED | |
| 968 /* | |
| 969 * Start with a cleanup of the main stylesheet's doc. | |
| 970 */ | |
| 971 if ((style->principal == style) && (style->doc)) | |
| 972 xsltCleanupStylesheetTree(style->doc, | |
| 973 xmlDocGetRootElement(style->doc)); | |
| 974 #ifdef XSLT_REFACTORED_XSLT_NSCOMP | |
| 975 /* | |
| 976 * Restore changed ns-decls before freeing the document. | |
| 977 */ | |
| 978 if ((style->doc != NULL) && | |
| 979 XSLT_HAS_INTERNAL_NSMAP(style)) | |
| 980 { | |
| 981 xsltRestoreDocumentNamespaces(XSLT_GET_INTERNAL_NSMAP(style), | |
| 982 style->doc); | |
| 983 } | |
| 984 #endif /* XSLT_REFACTORED_XSLT_NSCOMP */ | |
| 985 #else | |
| 986 /* | |
| 987 * Start with a cleanup of the main stylesheet's doc. | |
| 988 */ | |
| 989 if ((style->parent == NULL) && (style->doc)) | |
| 990 xsltCleanupStylesheetTree(style->doc, | |
| 991 xmlDocGetRootElement(style->doc)); | |
| 992 #endif /* XSLT_REFACTORED */ | |
| 993 | |
| 994 xsltFreeKeys(style); | |
| 995 xsltFreeExts(style); | |
| 996 xsltFreeTemplateHashes(style); | |
| 997 xsltFreeDecimalFormatList(style); | |
| 998 xsltFreeTemplateList(style->templates); | |
| 999 xsltFreeAttributeSetsHashes(style); | |
| 1000 xsltFreeNamespaceAliasHashes(style); | |
| 1001 xsltFreeStylePreComps(style); | |
| 1002 /* | |
| 1003 * Free documents of all included stylsheet modules of this | |
| 1004 * stylesheet level. | |
| 1005 */ | |
| 1006 xsltFreeStyleDocuments(style); | |
| 1007 /* | |
| 1008 * TODO: Best time to shutdown extension stuff? | |
| 1009 */ | |
| 1010 xsltShutdownExts(style); | |
| 1011 | |
| 1012 if (style->variables != NULL) | |
| 1013 xsltFreeStackElemList(style->variables); | |
| 1014 if (style->cdataSection != NULL) | |
| 1015 xmlHashFree(style->cdataSection, NULL); | |
| 1016 if (style->stripSpaces != NULL) | |
| 1017 xmlHashFree(style->stripSpaces, NULL); | |
| 1018 if (style->nsHash != NULL) | |
| 1019 xmlHashFree(style->nsHash, NULL); | |
| 1020 if (style->exclPrefixTab != NULL) | |
| 1021 xmlFree(style->exclPrefixTab); | |
| 1022 if (style->method != NULL) | |
| 1023 xmlFree(style->method); | |
| 1024 if (style->methodURI != NULL) | |
| 1025 xmlFree(style->methodURI); | |
| 1026 if (style->version != NULL) | |
| 1027 xmlFree(style->version); | |
| 1028 if (style->encoding != NULL) | |
| 1029 xmlFree(style->encoding); | |
| 1030 if (style->doctypePublic != NULL) | |
| 1031 xmlFree(style->doctypePublic); | |
| 1032 if (style->doctypeSystem != NULL) | |
| 1033 xmlFree(style->doctypeSystem); | |
| 1034 if (style->mediaType != NULL) | |
| 1035 xmlFree(style->mediaType); | |
| 1036 if (style->attVTs) | |
| 1037 xsltFreeAVTList(style->attVTs); | |
| 1038 if (style->imports != NULL) | |
| 1039 xsltFreeStylesheetList(style->imports); | |
| 1040 | |
| 1041 #ifdef XSLT_REFACTORED | |
| 1042 /* | |
| 1043 * If this is the principal stylesheet, then | |
| 1044 * free its internal data. | |
| 1045 */ | |
| 1046 if (style->principal == style) { | |
| 1047 if (style->principalData) { | |
| 1048 xsltFreePrincipalStylesheetData(style->principalData); | |
| 1049 style->principalData = NULL; | |
| 1050 } | |
| 1051 } | |
| 1052 #endif | |
| 1053 /* | |
| 1054 * Better to free the main document of this stylesheet level | |
| 1055 * at the end - so here. | |
| 1056 */ | |
| 1057 if (style->doc != NULL) { | |
| 1058 xmlFreeDoc(style->doc); | |
| 1059 } | |
| 1060 | |
| 1061 #ifdef WITH_XSLT_DEBUG | |
| 1062 xsltGenericDebug(xsltGenericDebugContext, | |
| 1063 "freeing dictionary from stylesheet\n"); | |
| 1064 #endif | |
| 1065 xmlDictFree(style->dict); | |
| 1066 | |
| 1067 memset(style, -1, sizeof(xsltStylesheet)); | |
| 1068 xmlFree(style); | |
| 1069 } | |
| 1070 | |
| 1071 /************************************************************************ | |
| 1072 * * | |
| 1073 * Parsing of an XSLT Stylesheet * | |
| 1074 * * | |
| 1075 ************************************************************************/ | |
| 1076 | |
| 1077 #ifdef XSLT_REFACTORED | |
| 1078 /* | |
| 1079 * This is now performed in an optimized way in xsltParseXSLTTemplate. | |
| 1080 */ | |
| 1081 #else | |
| 1082 /** | |
| 1083 * xsltGetInheritedNsList: | |
| 1084 * @style: the stylesheet | |
| 1085 * @template: the template | |
| 1086 * @node: the current node | |
| 1087 * | |
| 1088 * Search all the namespace applying to a given element except the ones | |
| 1089 * from excluded output prefixes currently in scope. Initialize the | |
| 1090 * template inheritedNs list with it. | |
| 1091 * | |
| 1092 * Returns the number of entries found | |
| 1093 */ | |
| 1094 static int | |
| 1095 xsltGetInheritedNsList(xsltStylesheetPtr style, | |
| 1096 xsltTemplatePtr template, | |
| 1097 xmlNodePtr node) | |
| 1098 { | |
| 1099 xmlNsPtr cur; | |
| 1100 xmlNsPtr *ret = NULL; | |
| 1101 int nbns = 0; | |
| 1102 int maxns = 10; | |
| 1103 int i; | |
| 1104 | |
| 1105 if ((style == NULL) || (template == NULL) || (node == NULL) || | |
| 1106 (template->inheritedNsNr != 0) || (template->inheritedNs != NULL)) | |
| 1107 return(0); | |
| 1108 while (node != NULL) { | |
| 1109 if (node->type == XML_ELEMENT_NODE) { | |
| 1110 cur = node->nsDef; | |
| 1111 while (cur != NULL) { | |
| 1112 if (xmlStrEqual(cur->href, XSLT_NAMESPACE)) | |
| 1113 goto skip_ns; | |
| 1114 | |
| 1115 if ((cur->prefix != NULL) && | |
| 1116 (xsltCheckExtPrefix(style, cur->prefix))) | |
| 1117 goto skip_ns; | |
| 1118 /* | |
| 1119 * Check if this namespace was excluded. | |
| 1120 * Note that at this point only the exclusions defined | |
| 1121 * on the topmost stylesheet element are in the exclusion-list. | |
| 1122 */ | |
| 1123 for (i = 0;i < style->exclPrefixNr;i++) { | |
| 1124 if (xmlStrEqual(cur->href, style->exclPrefixTab[i])) | |
| 1125 goto skip_ns; | |
| 1126 } | |
| 1127 if (ret == NULL) { | |
| 1128 ret = | |
| 1129 (xmlNsPtr *) xmlMalloc((maxns + 1) * | |
| 1130 sizeof(xmlNsPtr)); | |
| 1131 if (ret == NULL) { | |
| 1132 xmlGenericError(xmlGenericErrorContext, | |
| 1133 "xsltGetInheritedNsList : out of memory!
\n"); | |
| 1134 return(0); | |
| 1135 } | |
| 1136 ret[nbns] = NULL; | |
| 1137 } | |
| 1138 /* | |
| 1139 * Skip shadowed namespace bindings. | |
| 1140 */ | |
| 1141 for (i = 0; i < nbns; i++) { | |
| 1142 if ((cur->prefix == ret[i]->prefix) || | |
| 1143 (xmlStrEqual(cur->prefix, ret[i]->prefix))) | |
| 1144 break; | |
| 1145 } | |
| 1146 if (i >= nbns) { | |
| 1147 if (nbns >= maxns) { | |
| 1148 maxns *= 2; | |
| 1149 ret = (xmlNsPtr *) xmlRealloc(ret, | |
| 1150 (maxns + | |
| 1151 1) * | |
| 1152 sizeof(xmlNsPtr)); | |
| 1153 if (ret == NULL) { | |
| 1154 xmlGenericError(xmlGenericErrorContext, | |
| 1155 "xsltGetInheritedNsList : realloc fa
iled!\n"); | |
| 1156 return(0); | |
| 1157 } | |
| 1158 } | |
| 1159 ret[nbns++] = cur; | |
| 1160 ret[nbns] = NULL; | |
| 1161 } | |
| 1162 skip_ns: | |
| 1163 cur = cur->next; | |
| 1164 } | |
| 1165 } | |
| 1166 node = node->parent; | |
| 1167 } | |
| 1168 if (nbns != 0) { | |
| 1169 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 1170 xsltGenericDebug(xsltGenericDebugContext, | |
| 1171 "template has %d inherited namespaces\n", nbns); | |
| 1172 #endif | |
| 1173 template->inheritedNsNr = nbns; | |
| 1174 template->inheritedNs = ret; | |
| 1175 } | |
| 1176 return (nbns); | |
| 1177 } | |
| 1178 #endif /* else of XSLT_REFACTORED */ | |
| 1179 | |
| 1180 /** | |
| 1181 * xsltParseStylesheetOutput: | |
| 1182 * @style: the XSLT stylesheet | |
| 1183 * @cur: the "output" element | |
| 1184 * | |
| 1185 * parse an XSLT stylesheet output element and record | |
| 1186 * information related to the stylesheet output | |
| 1187 */ | |
| 1188 | |
| 1189 void | |
| 1190 xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur) | |
| 1191 { | |
| 1192 xmlChar *elements, | |
| 1193 *prop; | |
| 1194 xmlChar *element, | |
| 1195 *end; | |
| 1196 | |
| 1197 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) | |
| 1198 return; | |
| 1199 | |
| 1200 prop = xmlGetNsProp(cur, (const xmlChar *) "version", NULL); | |
| 1201 if (prop != NULL) { | |
| 1202 if (style->version != NULL) | |
| 1203 xmlFree(style->version); | |
| 1204 style->version = prop; | |
| 1205 } | |
| 1206 | |
| 1207 prop = xmlGetNsProp(cur, (const xmlChar *) "encoding", NULL); | |
| 1208 if (prop != NULL) { | |
| 1209 if (style->encoding != NULL) | |
| 1210 xmlFree(style->encoding); | |
| 1211 style->encoding = prop; | |
| 1212 } | |
| 1213 | |
| 1214 /* relaxed to support xt:document | |
| 1215 * TODO KB: What does "relaxed to support xt:document" mean? | |
| 1216 */ | |
| 1217 prop = xmlGetNsProp(cur, (const xmlChar *) "method", NULL); | |
| 1218 if (prop != NULL) { | |
| 1219 const xmlChar *URI; | |
| 1220 | |
| 1221 if (style->method != NULL) | |
| 1222 xmlFree(style->method); | |
| 1223 style->method = NULL; | |
| 1224 if (style->methodURI != NULL) | |
| 1225 xmlFree(style->methodURI); | |
| 1226 style->methodURI = NULL; | |
| 1227 | |
| 1228 /* | |
| 1229 * TODO: Don't use xsltGetQNameURI(). | |
| 1230 */ | |
| 1231 URI = xsltGetQNameURI(cur, &prop); | |
| 1232 if (prop == NULL) { | |
| 1233 if (style != NULL) style->errors++; | |
| 1234 } else if (URI == NULL) { | |
| 1235 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) || | |
| 1236 (xmlStrEqual(prop, (const xmlChar *) "html")) || | |
| 1237 (xmlStrEqual(prop, (const xmlChar *) "text"))) { | |
| 1238 style->method = prop; | |
| 1239 } else { | |
| 1240 xsltTransformError(NULL, style, cur, | |
| 1241 "invalid value for method: %s\n", prop); | |
| 1242 if (style != NULL) style->warnings++; | |
| 1243 } | |
| 1244 } else { | |
| 1245 style->method = prop; | |
| 1246 style->methodURI = xmlStrdup(URI); | |
| 1247 } | |
| 1248 } | |
| 1249 | |
| 1250 prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-system", NULL); | |
| 1251 if (prop != NULL) { | |
| 1252 if (style->doctypeSystem != NULL) | |
| 1253 xmlFree(style->doctypeSystem); | |
| 1254 style->doctypeSystem = prop; | |
| 1255 } | |
| 1256 | |
| 1257 prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-public", NULL); | |
| 1258 if (prop != NULL) { | |
| 1259 if (style->doctypePublic != NULL) | |
| 1260 xmlFree(style->doctypePublic); | |
| 1261 style->doctypePublic = prop; | |
| 1262 } | |
| 1263 | |
| 1264 prop = xmlGetNsProp(cur, (const xmlChar *) "standalone", NULL); | |
| 1265 if (prop != NULL) { | |
| 1266 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { | |
| 1267 style->standalone = 1; | |
| 1268 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { | |
| 1269 style->standalone = 0; | |
| 1270 } else { | |
| 1271 xsltTransformError(NULL, style, cur, | |
| 1272 "invalid value for standalone: %s\n", prop); | |
| 1273 style->errors++; | |
| 1274 } | |
| 1275 xmlFree(prop); | |
| 1276 } | |
| 1277 | |
| 1278 prop = xmlGetNsProp(cur, (const xmlChar *) "indent", NULL); | |
| 1279 if (prop != NULL) { | |
| 1280 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { | |
| 1281 style->indent = 1; | |
| 1282 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { | |
| 1283 style->indent = 0; | |
| 1284 } else { | |
| 1285 xsltTransformError(NULL, style, cur, | |
| 1286 "invalid value for indent: %s\n", prop); | |
| 1287 style->errors++; | |
| 1288 } | |
| 1289 xmlFree(prop); | |
| 1290 } | |
| 1291 | |
| 1292 prop = xmlGetNsProp(cur, (const xmlChar *) "omit-xml-declaration", NULL); | |
| 1293 if (prop != NULL) { | |
| 1294 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { | |
| 1295 style->omitXmlDeclaration = 1; | |
| 1296 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { | |
| 1297 style->omitXmlDeclaration = 0; | |
| 1298 } else { | |
| 1299 xsltTransformError(NULL, style, cur, | |
| 1300 "invalid value for omit-xml-declaration: %s\n", | |
| 1301 prop); | |
| 1302 style->errors++; | |
| 1303 } | |
| 1304 xmlFree(prop); | |
| 1305 } | |
| 1306 | |
| 1307 elements = xmlGetNsProp(cur, (const xmlChar *) "cdata-section-elements", | |
| 1308 NULL); | |
| 1309 if (elements != NULL) { | |
| 1310 if (style->cdataSection == NULL) | |
| 1311 style->cdataSection = xmlHashCreate(10); | |
| 1312 if (style->cdataSection == NULL) | |
| 1313 return; | |
| 1314 | |
| 1315 element = elements; | |
| 1316 while (*element != 0) { | |
| 1317 while (IS_BLANK(*element)) | |
| 1318 element++; | |
| 1319 if (*element == 0) | |
| 1320 break; | |
| 1321 end = element; | |
| 1322 while ((*end != 0) && (!IS_BLANK(*end))) | |
| 1323 end++; | |
| 1324 element = xmlStrndup(element, end - element); | |
| 1325 if (element) { | |
| 1326 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 1327 xsltGenericDebug(xsltGenericDebugContext, | |
| 1328 "add cdata section output element %s\n", | |
| 1329 element); | |
| 1330 #endif | |
| 1331 if (xmlValidateQName(BAD_CAST element, 0) != 0) { | |
| 1332 xsltTransformError(NULL, style, cur, | |
| 1333 "Attribute 'cdata-section-elements': The value " | |
| 1334 "'%s' is not a valid QName.\n", element); | |
| 1335 xmlFree(element); | |
| 1336 style->errors++; | |
| 1337 } else { | |
| 1338 const xmlChar *URI; | |
| 1339 | |
| 1340 /* | |
| 1341 * TODO: Don't use xsltGetQNameURI(). | |
| 1342 */ | |
| 1343 URI = xsltGetQNameURI(cur, &element); | |
| 1344 if (element == NULL) { | |
| 1345 /* | |
| 1346 * TODO: We'll report additionally an error | |
| 1347 * via the stylesheet's error handling. | |
| 1348 */ | |
| 1349 xsltTransformError(NULL, style, cur, | |
| 1350 "Attribute 'cdata-section-elements': The value " | |
| 1351 "'%s' is not a valid QName.\n", element); | |
| 1352 style->errors++; | |
| 1353 } else { | |
| 1354 xmlNsPtr ns; | |
| 1355 | |
| 1356 /* | |
| 1357 * XSLT-1.0 "Each QName is expanded into an | |
| 1358 * expanded-name using the namespace declarations in | |
| 1359 * effect on the xsl:output element in which the QName | |
| 1360 * occurs; if there is a default namespace, it is used | |
| 1361 * for QNames that do not have a prefix" | |
| 1362 * NOTE: Fix of bug #339570. | |
| 1363 */ | |
| 1364 if (URI == NULL) { | |
| 1365 ns = xmlSearchNs(style->doc, cur, NULL); | |
| 1366 if (ns != NULL) | |
| 1367 URI = ns->href; | |
| 1368 } | |
| 1369 xmlHashAddEntry2(style->cdataSection, element, URI, | |
| 1370 (void *) "cdata"); | |
| 1371 xmlFree(element); | |
| 1372 } | |
| 1373 } | |
| 1374 } | |
| 1375 element = end; | |
| 1376 } | |
| 1377 xmlFree(elements); | |
| 1378 } | |
| 1379 | |
| 1380 prop = xmlGetNsProp(cur, (const xmlChar *) "media-type", NULL); | |
| 1381 if (prop != NULL) { | |
| 1382 if (style->mediaType) | |
| 1383 xmlFree(style->mediaType); | |
| 1384 style->mediaType = prop; | |
| 1385 } | |
| 1386 if (cur->children != NULL) { | |
| 1387 xsltParseContentError(style, cur->children); | |
| 1388 } | |
| 1389 } | |
| 1390 | |
| 1391 /** | |
| 1392 * xsltParseStylesheetDecimalFormat: | |
| 1393 * @style: the XSLT stylesheet | |
| 1394 * @cur: the "decimal-format" element | |
| 1395 * | |
| 1396 * <!-- Category: top-level-element --> | |
| 1397 * <xsl:decimal-format | |
| 1398 * name = qname, decimal-separator = char, grouping-separator = char, | |
| 1399 * infinity = string, minus-sign = char, NaN = string, percent = char | |
| 1400 * per-mille = char, zero-digit = char, digit = char, | |
| 1401 * pattern-separator = char /> | |
| 1402 * | |
| 1403 * parse an XSLT stylesheet decimal-format element and | |
| 1404 * and record the formatting characteristics | |
| 1405 */ | |
| 1406 static void | |
| 1407 xsltParseStylesheetDecimalFormat(xsltStylesheetPtr style, xmlNodePtr cur) | |
| 1408 { | |
| 1409 xmlChar *prop; | |
| 1410 xsltDecimalFormatPtr format; | |
| 1411 xsltDecimalFormatPtr iter; | |
| 1412 | |
| 1413 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) | |
| 1414 return; | |
| 1415 | |
| 1416 format = style->decimalFormat; | |
| 1417 | |
| 1418 prop = xmlGetNsProp(cur, BAD_CAST("name"), NULL); | |
| 1419 if (prop != NULL) { | |
| 1420 const xmlChar *nsUri; | |
| 1421 | |
| 1422 if (xmlValidateQName(prop, 0) != 0) { | |
| 1423 xsltTransformError(NULL, style, cur, | |
| 1424 "xsl:decimal-format: Invalid QName '%s'.\n", prop); | |
| 1425 style->warnings++; | |
| 1426 xmlFree(prop); | |
| 1427 return; | |
| 1428 } | |
| 1429 /* | |
| 1430 * TODO: Don't use xsltGetQNameURI(). | |
| 1431 */ | |
| 1432 nsUri = xsltGetQNameURI(cur, &prop); | |
| 1433 if (prop == NULL) { | |
| 1434 style->warnings++; | |
| 1435 return; | |
| 1436 } | |
| 1437 format = xsltDecimalFormatGetByQName(style, nsUri, prop); | |
| 1438 if (format != NULL) { | |
| 1439 xsltTransformError(NULL, style, cur, | |
| 1440 "xsltParseStylestyleDecimalFormat: %s already exists\n", prop); | |
| 1441 style->warnings++; | |
| 1442 xmlFree(prop); | |
| 1443 return; | |
| 1444 } | |
| 1445 format = xsltNewDecimalFormat(nsUri, prop); | |
| 1446 if (format == NULL) { | |
| 1447 xsltTransformError(NULL, style, cur, | |
| 1448 "xsltParseStylestyleDecimalFormat: failed creating new decimal-format\n"); | |
| 1449 style->errors++; | |
| 1450 xmlFree(prop); | |
| 1451 return; | |
| 1452 } | |
| 1453 /* Append new decimal-format structure */ | |
| 1454 for (iter = style->decimalFormat; iter->next; iter = iter->next) | |
| 1455 ; | |
| 1456 if (iter) | |
| 1457 iter->next = format; | |
| 1458 } | |
| 1459 | |
| 1460 prop = xmlGetNsProp(cur, (const xmlChar *)"decimal-separator", NULL); | |
| 1461 if (prop != NULL) { | |
| 1462 if (format->decimalPoint != NULL) xmlFree(format->decimalPoint); | |
| 1463 format->decimalPoint = prop; | |
| 1464 } | |
| 1465 | |
| 1466 prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", NULL); | |
| 1467 if (prop != NULL) { | |
| 1468 if (format->grouping != NULL) xmlFree(format->grouping); | |
| 1469 format->grouping = prop; | |
| 1470 } | |
| 1471 | |
| 1472 prop = xmlGetNsProp(cur, (const xmlChar *)"infinity", NULL); | |
| 1473 if (prop != NULL) { | |
| 1474 if (format->infinity != NULL) xmlFree(format->infinity); | |
| 1475 format->infinity = prop; | |
| 1476 } | |
| 1477 | |
| 1478 prop = xmlGetNsProp(cur, (const xmlChar *)"minus-sign", NULL); | |
| 1479 if (prop != NULL) { | |
| 1480 if (format->minusSign != NULL) xmlFree(format->minusSign); | |
| 1481 format->minusSign = prop; | |
| 1482 } | |
| 1483 | |
| 1484 prop = xmlGetNsProp(cur, (const xmlChar *)"NaN", NULL); | |
| 1485 if (prop != NULL) { | |
| 1486 if (format->noNumber != NULL) xmlFree(format->noNumber); | |
| 1487 format->noNumber = prop; | |
| 1488 } | |
| 1489 | |
| 1490 prop = xmlGetNsProp(cur, (const xmlChar *)"percent", NULL); | |
| 1491 if (prop != NULL) { | |
| 1492 if (format->percent != NULL) xmlFree(format->percent); | |
| 1493 format->percent = prop; | |
| 1494 } | |
| 1495 | |
| 1496 prop = xmlGetNsProp(cur, (const xmlChar *)"per-mille", NULL); | |
| 1497 if (prop != NULL) { | |
| 1498 if (format->permille != NULL) xmlFree(format->permille); | |
| 1499 format->permille = prop; | |
| 1500 } | |
| 1501 | |
| 1502 prop = xmlGetNsProp(cur, (const xmlChar *)"zero-digit", NULL); | |
| 1503 if (prop != NULL) { | |
| 1504 if (format->zeroDigit != NULL) xmlFree(format->zeroDigit); | |
| 1505 format->zeroDigit = prop; | |
| 1506 } | |
| 1507 | |
| 1508 prop = xmlGetNsProp(cur, (const xmlChar *)"digit", NULL); | |
| 1509 if (prop != NULL) { | |
| 1510 if (format->digit != NULL) xmlFree(format->digit); | |
| 1511 format->digit = prop; | |
| 1512 } | |
| 1513 | |
| 1514 prop = xmlGetNsProp(cur, (const xmlChar *)"pattern-separator", NULL); | |
| 1515 if (prop != NULL) { | |
| 1516 if (format->patternSeparator != NULL) xmlFree(format->patternSeparator); | |
| 1517 format->patternSeparator = prop; | |
| 1518 } | |
| 1519 if (cur->children != NULL) { | |
| 1520 xsltParseContentError(style, cur->children); | |
| 1521 } | |
| 1522 } | |
| 1523 | |
| 1524 /** | |
| 1525 * xsltParseStylesheetPreserveSpace: | |
| 1526 * @style: the XSLT stylesheet | |
| 1527 * @cur: the "preserve-space" element | |
| 1528 * | |
| 1529 * parse an XSLT stylesheet preserve-space element and record | |
| 1530 * elements needing preserving | |
| 1531 */ | |
| 1532 | |
| 1533 static void | |
| 1534 xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) { | |
| 1535 xmlChar *elements; | |
| 1536 xmlChar *element, *end; | |
| 1537 | |
| 1538 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) | |
| 1539 return; | |
| 1540 | |
| 1541 elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL); | |
| 1542 if (elements == NULL) { | |
| 1543 xsltTransformError(NULL, style, cur, | |
| 1544 "xsltParseStylesheetPreserveSpace: missing elements attribute\n"); | |
| 1545 if (style != NULL) style->warnings++; | |
| 1546 return; | |
| 1547 } | |
| 1548 | |
| 1549 if (style->stripSpaces == NULL) | |
| 1550 style->stripSpaces = xmlHashCreate(10); | |
| 1551 if (style->stripSpaces == NULL) | |
| 1552 return; | |
| 1553 | |
| 1554 element = elements; | |
| 1555 while (*element != 0) { | |
| 1556 while (IS_BLANK(*element)) element++; | |
| 1557 if (*element == 0) | |
| 1558 break; | |
| 1559 end = element; | |
| 1560 while ((*end != 0) && (!IS_BLANK(*end))) end++; | |
| 1561 element = xmlStrndup(element, end - element); | |
| 1562 if (element) { | |
| 1563 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 1564 xsltGenericDebug(xsltGenericDebugContext, | |
| 1565 "add preserved space element %s\n", element); | |
| 1566 #endif | |
| 1567 if (xmlStrEqual(element, (const xmlChar *)"*")) { | |
| 1568 style->stripAll = -1; | |
| 1569 } else { | |
| 1570 const xmlChar *URI; | |
| 1571 | |
| 1572 /* | |
| 1573 * TODO: Don't use xsltGetQNameURI(). | |
| 1574 */ | |
| 1575 URI = xsltGetQNameURI(cur, &element); | |
| 1576 | |
| 1577 xmlHashAddEntry2(style->stripSpaces, element, URI, | |
| 1578 (xmlChar *) "preserve"); | |
| 1579 } | |
| 1580 xmlFree(element); | |
| 1581 } | |
| 1582 element = end; | |
| 1583 } | |
| 1584 xmlFree(elements); | |
| 1585 if (cur->children != NULL) { | |
| 1586 xsltParseContentError(style, cur->children); | |
| 1587 } | |
| 1588 } | |
| 1589 | |
| 1590 #ifdef XSLT_REFACTORED | |
| 1591 #else | |
| 1592 /** | |
| 1593 * xsltParseStylesheetExtPrefix: | |
| 1594 * @style: the XSLT stylesheet | |
| 1595 * @template: the "extension-element-prefixes" prefix | |
| 1596 * | |
| 1597 * parse an XSLT stylesheet's "extension-element-prefix" attribute value | |
| 1598 * and register the namespaces of extension instruction. | |
| 1599 * SPEC "A namespace is designated as an extension namespace by using | |
| 1600 * an extension-element-prefixes attribute on: | |
| 1601 * 1) an xsl:stylesheet element | |
| 1602 * 2) an xsl:extension-element-prefixes attribute on a | |
| 1603 * literal result element | |
| 1604 * 3) an extension instruction." | |
| 1605 */ | |
| 1606 static void | |
| 1607 xsltParseStylesheetExtPrefix(xsltStylesheetPtr style, xmlNodePtr cur, | |
| 1608 int isXsltElem) { | |
| 1609 xmlChar *prefixes; | |
| 1610 xmlChar *prefix, *end; | |
| 1611 | |
| 1612 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) | |
| 1613 return; | |
| 1614 | |
| 1615 if (isXsltElem) { | |
| 1616 /* For xsl:stylesheet/xsl:transform. */ | |
| 1617 prefixes = xmlGetNsProp(cur, | |
| 1618 (const xmlChar *)"extension-element-prefixes", NULL); | |
| 1619 } else { | |
| 1620 /* For literal result elements and extension instructions. */ | |
| 1621 prefixes = xmlGetNsProp(cur, | |
| 1622 (const xmlChar *)"extension-element-prefixes", XSLT_NAMESPACE); | |
| 1623 } | |
| 1624 if (prefixes == NULL) { | |
| 1625 return; | |
| 1626 } | |
| 1627 | |
| 1628 prefix = prefixes; | |
| 1629 while (*prefix != 0) { | |
| 1630 while (IS_BLANK(*prefix)) prefix++; | |
| 1631 if (*prefix == 0) | |
| 1632 break; | |
| 1633 end = prefix; | |
| 1634 while ((*end != 0) && (!IS_BLANK(*end))) end++; | |
| 1635 prefix = xmlStrndup(prefix, end - prefix); | |
| 1636 if (prefix) { | |
| 1637 xmlNsPtr ns; | |
| 1638 | |
| 1639 if (xmlStrEqual(prefix, (const xmlChar *)"#default")) | |
| 1640 ns = xmlSearchNs(style->doc, cur, NULL); | |
| 1641 else | |
| 1642 ns = xmlSearchNs(style->doc, cur, prefix); | |
| 1643 if (ns == NULL) { | |
| 1644 xsltTransformError(NULL, style, cur, | |
| 1645 "xsl:extension-element-prefix : undefined namespace %s\n", | |
| 1646 prefix); | |
| 1647 if (style != NULL) style->warnings++; | |
| 1648 } else { | |
| 1649 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 1650 xsltGenericDebug(xsltGenericDebugContext, | |
| 1651 "add extension prefix %s\n", prefix); | |
| 1652 #endif | |
| 1653 xsltRegisterExtPrefix(style, prefix, ns->href); | |
| 1654 } | |
| 1655 xmlFree(prefix); | |
| 1656 } | |
| 1657 prefix = end; | |
| 1658 } | |
| 1659 xmlFree(prefixes); | |
| 1660 } | |
| 1661 #endif /* else of XSLT_REFACTORED */ | |
| 1662 | |
| 1663 /** | |
| 1664 * xsltParseStylesheetStripSpace: | |
| 1665 * @style: the XSLT stylesheet | |
| 1666 * @cur: the "strip-space" element | |
| 1667 * | |
| 1668 * parse an XSLT stylesheet's strip-space element and record | |
| 1669 * the elements needing stripping | |
| 1670 */ | |
| 1671 | |
| 1672 static void | |
| 1673 xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) { | |
| 1674 xmlChar *elements; | |
| 1675 xmlChar *element, *end; | |
| 1676 | |
| 1677 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) | |
| 1678 return; | |
| 1679 | |
| 1680 elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL); | |
| 1681 if (elements == NULL) { | |
| 1682 xsltTransformError(NULL, style, cur, | |
| 1683 "xsltParseStylesheetStripSpace: missing elements attribute\n"); | |
| 1684 if (style != NULL) style->warnings++; | |
| 1685 return; | |
| 1686 } | |
| 1687 | |
| 1688 if (style->stripSpaces == NULL) | |
| 1689 style->stripSpaces = xmlHashCreate(10); | |
| 1690 if (style->stripSpaces == NULL) | |
| 1691 return; | |
| 1692 | |
| 1693 element = elements; | |
| 1694 while (*element != 0) { | |
| 1695 while (IS_BLANK(*element)) element++; | |
| 1696 if (*element == 0) | |
| 1697 break; | |
| 1698 end = element; | |
| 1699 while ((*end != 0) && (!IS_BLANK(*end))) end++; | |
| 1700 element = xmlStrndup(element, end - element); | |
| 1701 if (element) { | |
| 1702 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 1703 xsltGenericDebug(xsltGenericDebugContext, | |
| 1704 "add stripped space element %s\n", element); | |
| 1705 #endif | |
| 1706 if (xmlStrEqual(element, (const xmlChar *)"*")) { | |
| 1707 style->stripAll = 1; | |
| 1708 } else { | |
| 1709 const xmlChar *URI; | |
| 1710 | |
| 1711 /* | |
| 1712 * TODO: Don't use xsltGetQNameURI(). | |
| 1713 */ | |
| 1714 URI = xsltGetQNameURI(cur, &element); | |
| 1715 | |
| 1716 xmlHashAddEntry2(style->stripSpaces, element, URI, | |
| 1717 (xmlChar *) "strip"); | |
| 1718 } | |
| 1719 xmlFree(element); | |
| 1720 } | |
| 1721 element = end; | |
| 1722 } | |
| 1723 xmlFree(elements); | |
| 1724 if (cur->children != NULL) { | |
| 1725 xsltParseContentError(style, cur->children); | |
| 1726 } | |
| 1727 } | |
| 1728 | |
| 1729 #ifdef XSLT_REFACTORED | |
| 1730 #else | |
| 1731 /** | |
| 1732 * xsltParseStylesheetExcludePrefix: | |
| 1733 * @style: the XSLT stylesheet | |
| 1734 * @cur: the current point in the stylesheet | |
| 1735 * | |
| 1736 * parse an XSLT stylesheet exclude prefix and record | |
| 1737 * namespaces needing stripping | |
| 1738 * | |
| 1739 * Returns the number of Excluded prefixes added at that level | |
| 1740 */ | |
| 1741 | |
| 1742 static int | |
| 1743 xsltParseStylesheetExcludePrefix(xsltStylesheetPtr style, xmlNodePtr cur, | |
| 1744 int isXsltElem) | |
| 1745 { | |
| 1746 int nb = 0; | |
| 1747 xmlChar *prefixes; | |
| 1748 xmlChar *prefix, *end; | |
| 1749 | |
| 1750 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) | |
| 1751 return(0); | |
| 1752 | |
| 1753 if (isXsltElem) | |
| 1754 prefixes = xmlGetNsProp(cur, | |
| 1755 (const xmlChar *)"exclude-result-prefixes", NULL); | |
| 1756 else | |
| 1757 prefixes = xmlGetNsProp(cur, | |
| 1758 (const xmlChar *)"exclude-result-prefixes", XSLT_NAMESPACE); | |
| 1759 | |
| 1760 if (prefixes == NULL) { | |
| 1761 return(0); | |
| 1762 } | |
| 1763 | |
| 1764 prefix = prefixes; | |
| 1765 while (*prefix != 0) { | |
| 1766 while (IS_BLANK(*prefix)) prefix++; | |
| 1767 if (*prefix == 0) | |
| 1768 break; | |
| 1769 end = prefix; | |
| 1770 while ((*end != 0) && (!IS_BLANK(*end))) end++; | |
| 1771 prefix = xmlStrndup(prefix, end - prefix); | |
| 1772 if (prefix) { | |
| 1773 xmlNsPtr ns; | |
| 1774 | |
| 1775 if (xmlStrEqual(prefix, (const xmlChar *)"#default")) | |
| 1776 ns = xmlSearchNs(style->doc, cur, NULL); | |
| 1777 else | |
| 1778 ns = xmlSearchNs(style->doc, cur, prefix); | |
| 1779 if (ns == NULL) { | |
| 1780 xsltTransformError(NULL, style, cur, | |
| 1781 "xsl:exclude-result-prefixes : undefined namespace %s\n", | |
| 1782 prefix); | |
| 1783 if (style != NULL) style->warnings++; | |
| 1784 } else { | |
| 1785 if (exclPrefixPush(style, (xmlChar *) ns->href) >= 0) { | |
| 1786 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 1787 xsltGenericDebug(xsltGenericDebugContext, | |
| 1788 "exclude result prefix %s\n", prefix); | |
| 1789 #endif | |
| 1790 nb++; | |
| 1791 } | |
| 1792 } | |
| 1793 xmlFree(prefix); | |
| 1794 } | |
| 1795 prefix = end; | |
| 1796 } | |
| 1797 xmlFree(prefixes); | |
| 1798 return(nb); | |
| 1799 } | |
| 1800 #endif /* else of XSLT_REFACTORED */ | |
| 1801 | |
| 1802 #ifdef XSLT_REFACTORED | |
| 1803 | |
| 1804 /* | |
| 1805 * xsltTreeEnsureXMLDecl: | |
| 1806 * @doc: the doc | |
| 1807 * | |
| 1808 * BIG NOTE: | |
| 1809 * This was copy&pasted from Libxml2's xmlTreeEnsureXMLDecl() in "tree.c". | |
| 1810 * Ensures that there is an XML namespace declaration on the doc. | |
| 1811 * | |
| 1812 * Returns the XML ns-struct or NULL on API and internal errors. | |
| 1813 */ | |
| 1814 static xmlNsPtr | |
| 1815 xsltTreeEnsureXMLDecl(xmlDocPtr doc) | |
| 1816 { | |
| 1817 if (doc == NULL) | |
| 1818 return (NULL); | |
| 1819 if (doc->oldNs != NULL) | |
| 1820 return (doc->oldNs); | |
| 1821 { | |
| 1822 xmlNsPtr ns; | |
| 1823 ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); | |
| 1824 if (ns == NULL) { | |
| 1825 xmlGenericError(xmlGenericErrorContext, | |
| 1826 "xsltTreeEnsureXMLDecl: Failed to allocate " | |
| 1827 "the XML namespace.\n"); | |
| 1828 return (NULL); | |
| 1829 } | |
| 1830 memset(ns, 0, sizeof(xmlNs)); | |
| 1831 ns->type = XML_LOCAL_NAMESPACE; | |
| 1832 /* | |
| 1833 * URGENT TODO: revisit this. | |
| 1834 */ | |
| 1835 #ifdef LIBXML_NAMESPACE_DICT | |
| 1836 if (doc->dict) | |
| 1837 ns->href = xmlDictLookup(doc->dict, XML_XML_NAMESPACE, -1); | |
| 1838 else | |
| 1839 ns->href = xmlStrdup(XML_XML_NAMESPACE); | |
| 1840 #else | |
| 1841 ns->href = xmlStrdup(XML_XML_NAMESPACE); | |
| 1842 #endif | |
| 1843 ns->prefix = xmlStrdup((const xmlChar *)"xml"); | |
| 1844 doc->oldNs = ns; | |
| 1845 return (ns); | |
| 1846 } | |
| 1847 } | |
| 1848 | |
| 1849 /* | |
| 1850 * xsltTreeAcquireStoredNs: | |
| 1851 * @doc: the doc | |
| 1852 * @nsName: the namespace name | |
| 1853 * @prefix: the prefix | |
| 1854 * | |
| 1855 * BIG NOTE: | |
| 1856 * This was copy&pasted from Libxml2's xmlDOMWrapStoreNs() in "tree.c". | |
| 1857 * Creates or reuses an xmlNs struct on doc->oldNs with | |
| 1858 * the given prefix and namespace name. | |
| 1859 * | |
| 1860 * Returns the aquired ns struct or NULL in case of an API | |
| 1861 * or internal error. | |
| 1862 */ | |
| 1863 static xmlNsPtr | |
| 1864 xsltTreeAcquireStoredNs(xmlDocPtr doc, | |
| 1865 const xmlChar *nsName, | |
| 1866 const xmlChar *prefix) | |
| 1867 { | |
| 1868 xmlNsPtr ns; | |
| 1869 | |
| 1870 if (doc == NULL) | |
| 1871 return (NULL); | |
| 1872 if (doc->oldNs != NULL) | |
| 1873 ns = doc->oldNs; | |
| 1874 else | |
| 1875 ns = xsltTreeEnsureXMLDecl(doc); | |
| 1876 if (ns == NULL) | |
| 1877 return (NULL); | |
| 1878 if (ns->next != NULL) { | |
| 1879 /* Reuse. */ | |
| 1880 ns = ns->next; | |
| 1881 while (ns != NULL) { | |
| 1882 if ((ns->prefix == NULL) != (prefix == NULL)) { | |
| 1883 /* NOP */ | |
| 1884 } else if (prefix == NULL) { | |
| 1885 if (xmlStrEqual(ns->href, nsName)) | |
| 1886 return (ns); | |
| 1887 } else { | |
| 1888 if ((ns->prefix[0] == prefix[0]) && | |
| 1889 xmlStrEqual(ns->prefix, prefix) && | |
| 1890 xmlStrEqual(ns->href, nsName)) | |
| 1891 return (ns); | |
| 1892 | |
| 1893 } | |
| 1894 if (ns->next == NULL) | |
| 1895 break; | |
| 1896 ns = ns->next; | |
| 1897 } | |
| 1898 } | |
| 1899 /* Create. */ | |
| 1900 ns->next = xmlNewNs(NULL, nsName, prefix); | |
| 1901 return (ns->next); | |
| 1902 } | |
| 1903 | |
| 1904 /** | |
| 1905 * xsltLREBuildEffectiveNs: | |
| 1906 * | |
| 1907 * Apply ns-aliasing on the namespace of the given @elem and | |
| 1908 * its attributes. | |
| 1909 */ | |
| 1910 static int | |
| 1911 xsltLREBuildEffectiveNs(xsltCompilerCtxtPtr cctxt, | |
| 1912 xmlNodePtr elem) | |
| 1913 { | |
| 1914 xmlNsPtr ns; | |
| 1915 xsltNsAliasPtr alias; | |
| 1916 | |
| 1917 if ((cctxt == NULL) || (elem == NULL)) | |
| 1918 return(-1); | |
| 1919 if ((cctxt->nsAliases == NULL) || (! cctxt->hasNsAliases)) | |
| 1920 return(0); | |
| 1921 | |
| 1922 alias = cctxt->nsAliases; | |
| 1923 while (alias != NULL) { | |
| 1924 if ( /* If both namespaces are NULL... */ | |
| 1925 ( (elem->ns == NULL) && | |
| 1926 ((alias->literalNs == NULL) || | |
| 1927 (alias->literalNs->href == NULL)) ) || | |
| 1928 /* ... or both namespace are equal */ | |
| 1929 ( (elem->ns != NULL) && | |
| 1930 (alias->literalNs != NULL) && | |
| 1931 xmlStrEqual(elem->ns->href, alias->literalNs->href) ) ) | |
| 1932 { | |
| 1933 if ((alias->targetNs != NULL) && | |
| 1934 (alias->targetNs->href != NULL)) | |
| 1935 { | |
| 1936 /* | |
| 1937 * Convert namespace. | |
| 1938 */ | |
| 1939 if (elem->doc == alias->docOfTargetNs) { | |
| 1940 /* | |
| 1941 * This is the nice case: same docs. | |
| 1942 * This will eventually assign a ns-decl which | |
| 1943 * is shadowed, but this has no negative effect on | |
| 1944 * the generation of the result tree. | |
| 1945 */ | |
| 1946 elem->ns = alias->targetNs; | |
| 1947 } else { | |
| 1948 /* | |
| 1949 * This target xmlNs originates from a different | |
| 1950 * stylesheet tree. Try to locate it in the | |
| 1951 * in-scope namespaces. | |
| 1952 * OPTIMIZE TODO: Use the compiler-node-info inScopeNs. | |
| 1953 */ | |
| 1954 ns = xmlSearchNs(elem->doc, elem, | |
| 1955 alias->targetNs->prefix); | |
| 1956 /* | |
| 1957 * If no matching ns-decl found, then assign a | |
| 1958 * ns-decl stored in xmlDoc. | |
| 1959 */ | |
| 1960 if ((ns == NULL) || | |
| 1961 (! xmlStrEqual(ns->href, alias->targetNs->href))) | |
| 1962 { | |
| 1963 /* | |
| 1964 * BIG NOTE: The use of xsltTreeAcquireStoredNs() | |
| 1965 * is not very efficient, but currently I don't | |
| 1966 * see an other way of *safely* changing a node's | |
| 1967 * namespace, since the xmlNs struct in | |
| 1968 * alias->targetNs might come from an other | |
| 1969 * stylesheet tree. So we need to anchor it in the | |
| 1970 * current document, without adding it to the tree, | |
| 1971 * which would otherwise change the in-scope-ns | |
| 1972 * semantic of the tree. | |
| 1973 */ | |
| 1974 ns = xsltTreeAcquireStoredNs(elem->doc, | |
| 1975 alias->targetNs->href, | |
| 1976 alias->targetNs->prefix); | |
| 1977 | |
| 1978 if (ns == NULL) { | |
| 1979 xsltTransformError(NULL, cctxt->style, elem, | |
| 1980 "Internal error in " | |
| 1981 "xsltLREBuildEffectiveNs(): " | |
| 1982 "failed to acquire a stored " | |
| 1983 "ns-declaration.\n"); | |
| 1984 cctxt->style->errors++; | |
| 1985 return(-1); | |
| 1986 | |
| 1987 } | |
| 1988 } | |
| 1989 elem->ns = ns; | |
| 1990 } | |
| 1991 } else { | |
| 1992 /* | |
| 1993 * Move into or leave in the NULL namespace. | |
| 1994 */ | |
| 1995 elem->ns = NULL; | |
| 1996 } | |
| 1997 break; | |
| 1998 } | |
| 1999 alias = alias->next; | |
| 2000 } | |
| 2001 /* | |
| 2002 * Same with attributes of literal result elements. | |
| 2003 */ | |
| 2004 if (elem->properties != NULL) { | |
| 2005 xmlAttrPtr attr = elem->properties; | |
| 2006 | |
| 2007 while (attr != NULL) { | |
| 2008 if (attr->ns == NULL) { | |
| 2009 attr = attr->next; | |
| 2010 continue; | |
| 2011 } | |
| 2012 alias = cctxt->nsAliases; | |
| 2013 while (alias != NULL) { | |
| 2014 if ( /* If both namespaces are NULL... */ | |
| 2015 ( (elem->ns == NULL) && | |
| 2016 ((alias->literalNs == NULL) || | |
| 2017 (alias->literalNs->href == NULL)) ) || | |
| 2018 /* ... or both namespace are equal */ | |
| 2019 ( (elem->ns != NULL) && | |
| 2020 (alias->literalNs != NULL) && | |
| 2021 xmlStrEqual(elem->ns->href, alias->literalNs->href) ) ) | |
| 2022 { | |
| 2023 if ((alias->targetNs != NULL) && | |
| 2024 (alias->targetNs->href != NULL)) | |
| 2025 { | |
| 2026 if (elem->doc == alias->docOfTargetNs) { | |
| 2027 elem->ns = alias->targetNs; | |
| 2028 } else { | |
| 2029 ns = xmlSearchNs(elem->doc, elem, | |
| 2030 alias->targetNs->prefix); | |
| 2031 if ((ns == NULL) || | |
| 2032 (! xmlStrEqual(ns->href, alias->targetNs->href))
) | |
| 2033 { | |
| 2034 ns = xsltTreeAcquireStoredNs(elem->doc, | |
| 2035 alias->targetNs->href, | |
| 2036 alias->targetNs->prefix); | |
| 2037 | |
| 2038 if (ns == NULL) { | |
| 2039 xsltTransformError(NULL, cctxt->style, elem, | |
| 2040 "Internal error in " | |
| 2041 "xsltLREBuildEffectiveNs(): " | |
| 2042 "failed to acquire a stored " | |
| 2043 "ns-declaration.\n"); | |
| 2044 cctxt->style->errors++; | |
| 2045 return(-1); | |
| 2046 | |
| 2047 } | |
| 2048 } | |
| 2049 elem->ns = ns; | |
| 2050 } | |
| 2051 } else { | |
| 2052 /* | |
| 2053 * Move into or leave in the NULL namespace. | |
| 2054 */ | |
| 2055 elem->ns = NULL; | |
| 2056 } | |
| 2057 break; | |
| 2058 } | |
| 2059 alias = alias->next; | |
| 2060 } | |
| 2061 | |
| 2062 attr = attr->next; | |
| 2063 } | |
| 2064 } | |
| 2065 return(0); | |
| 2066 } | |
| 2067 | |
| 2068 /** | |
| 2069 * xsltLREBuildEffectiveNsNodes: | |
| 2070 * | |
| 2071 * Computes the effective namespaces nodes for a literal result | |
| 2072 * element. | |
| 2073 * @effectiveNs is the set of effective ns-nodes | |
| 2074 * on the literal result element, which will be added to the result | |
| 2075 * element if not already existing in the result tree. | |
| 2076 * This means that excluded namespaces (via exclude-result-prefixes, | |
| 2077 * extension-element-prefixes and the XSLT namespace) not added | |
| 2078 * to the set. | |
| 2079 * Namespace-aliasing was applied on the @effectiveNs. | |
| 2080 */ | |
| 2081 static int | |
| 2082 xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt, | |
| 2083 xsltStyleItemLRElementInfoPtr item, | |
| 2084 xmlNodePtr elem, | |
| 2085 int isLRE) | |
| 2086 { | |
| 2087 xmlNsPtr ns, tmpns; | |
| 2088 xsltEffectiveNsPtr effNs, lastEffNs = NULL; | |
| 2089 int i, j, holdByElem; | |
| 2090 xsltPointerListPtr extElemNs = cctxt->inode->extElemNs; | |
| 2091 xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs; | |
| 2092 | |
| 2093 if ((cctxt == NULL) || (cctxt->inode == NULL) || (elem == NULL) || | |
| 2094 (item == NULL) || (item->effectiveNs != NULL)) | |
| 2095 return(-1); | |
| 2096 | |
| 2097 if (item->inScopeNs == NULL) | |
| 2098 return(0); | |
| 2099 | |
| 2100 extElemNs = cctxt->inode->extElemNs; | |
| 2101 exclResultNs = cctxt->inode->exclResultNs; | |
| 2102 | |
| 2103 for (i = 0; i < item->inScopeNs->totalNumber; i++) { | |
| 2104 ns = item->inScopeNs->list[i]; | |
| 2105 /* | |
| 2106 * Skip namespaces designated as excluded namespaces | |
| 2107 * ------------------------------------------------- | |
| 2108 * | |
| 2109 * XSLT-20 TODO: In XSLT 2.0 we need to keep namespaces | |
| 2110 * which are target namespaces of namespace-aliases | |
| 2111 * regardless if designated as excluded. | |
| 2112 * | |
| 2113 * Exclude the XSLT namespace. | |
| 2114 */ | |
| 2115 if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) | |
| 2116 goto skip_ns; | |
| 2117 | |
| 2118 /* | |
| 2119 * Apply namespace aliasing | |
| 2120 * ------------------------ | |
| 2121 * | |
| 2122 * SPEC XSLT 2.0 | |
| 2123 * "- A namespace node whose string value is a literal namespace | |
| 2124 * URI is not copied to the result tree. | |
| 2125 * - A namespace node whose string value is a target namespace URI | |
| 2126 * is copied to the result tree, whether or not the URI | |
| 2127 * identifies an excluded namespace." | |
| 2128 * | |
| 2129 * NOTE: The ns-aliasing machanism is non-cascading. | |
| 2130 * (checked with Saxon, Xalan and MSXML .NET). | |
| 2131 * URGENT TODO: is style->nsAliases the effective list of | |
| 2132 * ns-aliases, or do we need to lookup the whole | |
| 2133 * import-tree? | |
| 2134 * TODO: Get rid of import-tree lookup. | |
| 2135 */ | |
| 2136 if (cctxt->hasNsAliases) { | |
| 2137 xsltNsAliasPtr alias; | |
| 2138 /* | |
| 2139 * First check for being a target namespace. | |
| 2140 */ | |
| 2141 alias = cctxt->nsAliases; | |
| 2142 do { | |
| 2143 /* | |
| 2144 * TODO: Is xmlns="" handled already? | |
| 2145 */ | |
| 2146 if ((alias->targetNs != NULL) && | |
| 2147 (xmlStrEqual(alias->targetNs->href, ns->href))) | |
| 2148 { | |
| 2149 /* | |
| 2150 * Recognized as a target namespace; use it regardless | |
| 2151 * if excluded otherwise. | |
| 2152 */ | |
| 2153 goto add_effective_ns; | |
| 2154 } | |
| 2155 alias = alias->next; | |
| 2156 } while (alias != NULL); | |
| 2157 | |
| 2158 alias = cctxt->nsAliases; | |
| 2159 do { | |
| 2160 /* | |
| 2161 * TODO: Is xmlns="" handled already? | |
| 2162 */ | |
| 2163 if ((alias->literalNs != NULL) && | |
| 2164 (xmlStrEqual(alias->literalNs->href, ns->href))) | |
| 2165 { | |
| 2166 /* | |
| 2167 * Recognized as an namespace alias; do not use it. | |
| 2168 */ | |
| 2169 goto skip_ns; | |
| 2170 } | |
| 2171 alias = alias->next; | |
| 2172 } while (alias != NULL); | |
| 2173 } | |
| 2174 | |
| 2175 /* | |
| 2176 * Exclude excluded result namespaces. | |
| 2177 */ | |
| 2178 if (exclResultNs) { | |
| 2179 for (j = 0; j < exclResultNs->number; j++) | |
| 2180 if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[j])) | |
| 2181 goto skip_ns; | |
| 2182 } | |
| 2183 /* | |
| 2184 * Exclude extension-element namespaces. | |
| 2185 */ | |
| 2186 if (extElemNs) { | |
| 2187 for (j = 0; j < extElemNs->number; j++) | |
| 2188 if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j])) | |
| 2189 goto skip_ns; | |
| 2190 } | |
| 2191 | |
| 2192 add_effective_ns: | |
| 2193 /* | |
| 2194 * OPTIMIZE TODO: This information may not be needed. | |
| 2195 */ | |
| 2196 if (isLRE && (elem->nsDef != NULL)) { | |
| 2197 holdByElem = 0; | |
| 2198 tmpns = elem->nsDef; | |
| 2199 do { | |
| 2200 if (tmpns == ns) { | |
| 2201 holdByElem = 1; | |
| 2202 break; | |
| 2203 } | |
| 2204 tmpns = tmpns->next; | |
| 2205 } while (tmpns != NULL); | |
| 2206 } else | |
| 2207 holdByElem = 0; | |
| 2208 | |
| 2209 | |
| 2210 /* | |
| 2211 * Add the effective namespace declaration. | |
| 2212 */ | |
| 2213 effNs = (xsltEffectiveNsPtr) xmlMalloc(sizeof(xsltEffectiveNs)); | |
| 2214 if (effNs == NULL) { | |
| 2215 xsltTransformError(NULL, cctxt->style, elem, | |
| 2216 "Internal error in xsltLREBuildEffectiveNs(): " | |
| 2217 "failed to allocate memory.\n"); | |
| 2218 cctxt->style->errors++; | |
| 2219 return(-1); | |
| 2220 } | |
| 2221 if (cctxt->psData->effectiveNs == NULL) { | |
| 2222 cctxt->psData->effectiveNs = effNs; | |
| 2223 effNs->nextInStore = NULL; | |
| 2224 } else { | |
| 2225 effNs->nextInStore = cctxt->psData->effectiveNs; | |
| 2226 cctxt->psData->effectiveNs = effNs; | |
| 2227 } | |
| 2228 | |
| 2229 effNs->next = NULL; | |
| 2230 effNs->prefix = ns->prefix; | |
| 2231 effNs->nsName = ns->href; | |
| 2232 effNs->holdByElem = holdByElem; | |
| 2233 | |
| 2234 if (lastEffNs == NULL) | |
| 2235 item->effectiveNs = effNs; | |
| 2236 else | |
| 2237 lastEffNs->next = effNs; | |
| 2238 lastEffNs = effNs; | |
| 2239 | |
| 2240 skip_ns: | |
| 2241 {} | |
| 2242 } | |
| 2243 return(0); | |
| 2244 } | |
| 2245 | |
| 2246 | |
| 2247 /** | |
| 2248 * xsltLREInfoCreate: | |
| 2249 * | |
| 2250 * @isLRE: indicates if the given @elem is a literal result element | |
| 2251 * | |
| 2252 * Creates a new info for a literal result element. | |
| 2253 */ | |
| 2254 static int | |
| 2255 xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt, | |
| 2256 xmlNodePtr elem, | |
| 2257 int isLRE) | |
| 2258 { | |
| 2259 xsltStyleItemLRElementInfoPtr item; | |
| 2260 | |
| 2261 if ((cctxt == NULL) || (cctxt->inode == NULL)) | |
| 2262 return(-1); | |
| 2263 | |
| 2264 item = (xsltStyleItemLRElementInfoPtr) | |
| 2265 xmlMalloc(sizeof(xsltStyleItemLRElementInfo)); | |
| 2266 if (item == NULL) { | |
| 2267 xsltTransformError(NULL, cctxt->style, NULL, | |
| 2268 "Internal error in xsltLREInfoCreate(): " | |
| 2269 "memory allocation failed.\n"); | |
| 2270 cctxt->style->errors++; | |
| 2271 return(-1); | |
| 2272 } | |
| 2273 memset(item, 0, sizeof(xsltStyleItemLRElementInfo)); | |
| 2274 item->type = XSLT_FUNC_LITERAL_RESULT_ELEMENT; | |
| 2275 /* | |
| 2276 * Store it in the stylesheet. | |
| 2277 */ | |
| 2278 item->next = cctxt->style->preComps; | |
| 2279 cctxt->style->preComps = (xsltElemPreCompPtr) item; | |
| 2280 /* | |
| 2281 * @inScopeNs are used for execution of XPath expressions | |
| 2282 * in AVTs. | |
| 2283 */ | |
| 2284 item->inScopeNs = cctxt->inode->inScopeNs; | |
| 2285 | |
| 2286 if (elem) | |
| 2287 xsltLREBuildEffectiveNsNodes(cctxt, item, elem, isLRE); | |
| 2288 | |
| 2289 cctxt->inode->litResElemInfo = item; | |
| 2290 cctxt->inode->nsChanged = 0; | |
| 2291 cctxt->maxLREs++; | |
| 2292 return(0); | |
| 2293 } | |
| 2294 | |
| 2295 /** | |
| 2296 * xsltCompilerVarInfoPush: | |
| 2297 * @cctxt: the compilation context | |
| 2298 * | |
| 2299 * Pushes a new var/param info onto the stack. | |
| 2300 * | |
| 2301 * Returns the acquired variable info. | |
| 2302 */ | |
| 2303 static xsltVarInfoPtr | |
| 2304 xsltCompilerVarInfoPush(xsltCompilerCtxtPtr cctxt, | |
| 2305 xmlNodePtr inst, | |
| 2306 const xmlChar *name, | |
| 2307 const xmlChar *nsName) | |
| 2308 { | |
| 2309 xsltVarInfoPtr ivar; | |
| 2310 | |
| 2311 if ((cctxt->ivar != NULL) && (cctxt->ivar->next != NULL)) { | |
| 2312 ivar = cctxt->ivar->next; | |
| 2313 } else if ((cctxt->ivar == NULL) && (cctxt->ivars != NULL)) { | |
| 2314 ivar = cctxt->ivars; | |
| 2315 } else { | |
| 2316 ivar = (xsltVarInfoPtr) xmlMalloc(sizeof(xsltVarInfo)); | |
| 2317 if (ivar == NULL) { | |
| 2318 xsltTransformError(NULL, cctxt->style, inst, | |
| 2319 "xsltParseInScopeVarPush: xmlMalloc() failed!\n"); | |
| 2320 cctxt->style->errors++; | |
| 2321 return(NULL); | |
| 2322 } | |
| 2323 /* memset(retVar, 0, sizeof(xsltInScopeVar)); */ | |
| 2324 if (cctxt->ivars == NULL) { | |
| 2325 cctxt->ivars = ivar; | |
| 2326 ivar->prev = NULL; | |
| 2327 } else { | |
| 2328 cctxt->ivar->next = ivar; | |
| 2329 ivar->prev = cctxt->ivar; | |
| 2330 } | |
| 2331 cctxt->ivar = ivar; | |
| 2332 ivar->next = NULL; | |
| 2333 } | |
| 2334 ivar->depth = cctxt->depth; | |
| 2335 ivar->name = name; | |
| 2336 ivar->nsName = nsName; | |
| 2337 return(ivar); | |
| 2338 } | |
| 2339 | |
| 2340 /** | |
| 2341 * xsltCompilerVarInfoPop: | |
| 2342 * @cctxt: the compilation context | |
| 2343 * | |
| 2344 * Pops all var/param infos from the stack, which | |
| 2345 * have the current depth. | |
| 2346 */ | |
| 2347 static void | |
| 2348 xsltCompilerVarInfoPop(xsltCompilerCtxtPtr cctxt) | |
| 2349 { | |
| 2350 | |
| 2351 while ((cctxt->ivar != NULL) && | |
| 2352 (cctxt->ivar->depth > cctxt->depth)) | |
| 2353 { | |
| 2354 cctxt->ivar = cctxt->ivar->prev; | |
| 2355 } | |
| 2356 } | |
| 2357 | |
| 2358 /* | |
| 2359 * xsltCompilerNodePush: | |
| 2360 * | |
| 2361 * @cctxt: the compilation context | |
| 2362 * @node: the node to be pushed (this can also be the doc-node) | |
| 2363 * | |
| 2364 * | |
| 2365 * | |
| 2366 * Returns the current node info structure or | |
| 2367 * NULL in case of an internal error. | |
| 2368 */ | |
| 2369 static xsltCompilerNodeInfoPtr | |
| 2370 xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) | |
| 2371 { | |
| 2372 xsltCompilerNodeInfoPtr inode, iprev; | |
| 2373 | |
| 2374 if ((cctxt->inode != NULL) && (cctxt->inode->next != NULL)) { | |
| 2375 inode = cctxt->inode->next; | |
| 2376 } else if ((cctxt->inode == NULL) && (cctxt->inodeList != NULL)) { | |
| 2377 inode = cctxt->inodeList; | |
| 2378 } else { | |
| 2379 /* | |
| 2380 * Create a new node-info. | |
| 2381 */ | |
| 2382 inode = (xsltCompilerNodeInfoPtr) | |
| 2383 xmlMalloc(sizeof(xsltCompilerNodeInfo)); | |
| 2384 if (inode == NULL) { | |
| 2385 xsltTransformError(NULL, cctxt->style, NULL, | |
| 2386 "xsltCompilerNodePush: malloc failed.\n"); | |
| 2387 return(NULL); | |
| 2388 } | |
| 2389 memset(inode, 0, sizeof(xsltCompilerNodeInfo)); | |
| 2390 if (cctxt->inodeList == NULL) | |
| 2391 cctxt->inodeList = inode; | |
| 2392 else { | |
| 2393 cctxt->inodeLast->next = inode; | |
| 2394 inode->prev = cctxt->inodeLast; | |
| 2395 } | |
| 2396 cctxt->inodeLast = inode; | |
| 2397 cctxt->maxNodeInfos++; | |
| 2398 if (cctxt->inode == NULL) { | |
| 2399 cctxt->inode = inode; | |
| 2400 /* | |
| 2401 * Create an initial literal result element info for | |
| 2402 * the root of the stylesheet. | |
| 2403 */ | |
| 2404 xsltLREInfoCreate(cctxt, NULL, 0); | |
| 2405 } | |
| 2406 } | |
| 2407 cctxt->depth++; | |
| 2408 cctxt->inode = inode; | |
| 2409 /* | |
| 2410 * REVISIT TODO: Keep the reset always complete. | |
| 2411 * NOTE: Be carefull with the @node, since it might be | |
| 2412 * a doc-node. | |
| 2413 */ | |
| 2414 inode->node = node; | |
| 2415 inode->depth = cctxt->depth; | |
| 2416 inode->templ = NULL; | |
| 2417 inode->category = XSLT_ELEMENT_CATEGORY_XSLT; | |
| 2418 inode->type = 0; | |
| 2419 inode->item = NULL; | |
| 2420 inode->curChildType = 0; | |
| 2421 inode->extContentHandled = 0; | |
| 2422 inode->isRoot = 0; | |
| 2423 | |
| 2424 if (inode->prev != NULL) { | |
| 2425 iprev = inode->prev; | |
| 2426 /* | |
| 2427 * Inherit the following information: | |
| 2428 * --------------------------------- | |
| 2429 * | |
| 2430 * In-scope namespaces | |
| 2431 */ | |
| 2432 inode->inScopeNs = iprev->inScopeNs; | |
| 2433 /* | |
| 2434 * Info for literal result elements | |
| 2435 */ | |
| 2436 inode->litResElemInfo = iprev->litResElemInfo; | |
| 2437 inode->nsChanged = iprev->nsChanged; | |
| 2438 /* | |
| 2439 * Excluded result namespaces | |
| 2440 */ | |
| 2441 inode->exclResultNs = iprev->exclResultNs; | |
| 2442 /* | |
| 2443 * Extension instruction namespaces | |
| 2444 */ | |
| 2445 inode->extElemNs = iprev->extElemNs; | |
| 2446 /* | |
| 2447 * Whitespace preservation | |
| 2448 */ | |
| 2449 inode->preserveWhitespace = iprev->preserveWhitespace; | |
| 2450 /* | |
| 2451 * Forwards-compatible mode | |
| 2452 */ | |
| 2453 inode->forwardsCompat = iprev->forwardsCompat; | |
| 2454 } else { | |
| 2455 inode->inScopeNs = NULL; | |
| 2456 inode->exclResultNs = NULL; | |
| 2457 inode->extElemNs = NULL; | |
| 2458 inode->preserveWhitespace = 0; | |
| 2459 inode->forwardsCompat = 0; | |
| 2460 } | |
| 2461 | |
| 2462 return(inode); | |
| 2463 } | |
| 2464 | |
| 2465 /* | |
| 2466 * xsltCompilerNodePop: | |
| 2467 * | |
| 2468 * @cctxt: the compilation context | |
| 2469 * @node: the node to be pushed (this can also be the doc-node) | |
| 2470 * | |
| 2471 * Pops the current node info. | |
| 2472 */ | |
| 2473 static void | |
| 2474 xsltCompilerNodePop(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) | |
| 2475 { | |
| 2476 if (cctxt->inode == NULL) { | |
| 2477 xmlGenericError(xmlGenericErrorContext, | |
| 2478 "xsltCompilerNodePop: Top-node mismatch.\n"); | |
| 2479 return; | |
| 2480 } | |
| 2481 /* | |
| 2482 * NOTE: Be carefull with the @node, since it might be | |
| 2483 * a doc-node. | |
| 2484 */ | |
| 2485 if (cctxt->inode->node != node) { | |
| 2486 xmlGenericError(xmlGenericErrorContext, | |
| 2487 "xsltCompilerNodePop: Node mismatch.\n"); | |
| 2488 goto mismatch; | |
| 2489 } | |
| 2490 if (cctxt->inode->depth != cctxt->depth) { | |
| 2491 xmlGenericError(xmlGenericErrorContext, | |
| 2492 "xsltCompilerNodePop: Depth mismatch.\n"); | |
| 2493 goto mismatch; | |
| 2494 } | |
| 2495 cctxt->depth--; | |
| 2496 /* | |
| 2497 * Pop information of variables. | |
| 2498 */ | |
| 2499 if ((cctxt->ivar) && (cctxt->ivar->depth > cctxt->depth)) | |
| 2500 xsltCompilerVarInfoPop(cctxt); | |
| 2501 | |
| 2502 cctxt->inode = cctxt->inode->prev; | |
| 2503 if (cctxt->inode != NULL) | |
| 2504 cctxt->inode->curChildType = 0; | |
| 2505 return; | |
| 2506 | |
| 2507 mismatch: | |
| 2508 { | |
| 2509 const xmlChar *nsName = NULL, *name = NULL; | |
| 2510 const xmlChar *infnsName = NULL, *infname = NULL; | |
| 2511 | |
| 2512 if (node) { | |
| 2513 if (node->type == XML_ELEMENT_NODE) { | |
| 2514 name = node->name; | |
| 2515 if (node->ns != NULL) | |
| 2516 nsName = node->ns->href; | |
| 2517 else | |
| 2518 nsName = BAD_CAST ""; | |
| 2519 } else { | |
| 2520 name = BAD_CAST "#document"; | |
| 2521 nsName = BAD_CAST ""; | |
| 2522 } | |
| 2523 } else | |
| 2524 name = BAD_CAST "Not given"; | |
| 2525 | |
| 2526 if (cctxt->inode->node) { | |
| 2527 if (node->type == XML_ELEMENT_NODE) { | |
| 2528 infname = cctxt->inode->node->name; | |
| 2529 if (cctxt->inode->node->ns != NULL) | |
| 2530 infnsName = cctxt->inode->node->ns->href; | |
| 2531 else | |
| 2532 infnsName = BAD_CAST ""; | |
| 2533 } else { | |
| 2534 infname = BAD_CAST "#document"; | |
| 2535 infnsName = BAD_CAST ""; | |
| 2536 } | |
| 2537 } else | |
| 2538 infname = BAD_CAST "Not given"; | |
| 2539 | |
| 2540 | |
| 2541 xmlGenericError(xmlGenericErrorContext, | |
| 2542 "xsltCompilerNodePop: Given : '%s' URI '%s'\n", | |
| 2543 name, nsName); | |
| 2544 xmlGenericError(xmlGenericErrorContext, | |
| 2545 "xsltCompilerNodePop: Expected: '%s' URI '%s'\n", | |
| 2546 infname, infnsName); | |
| 2547 } | |
| 2548 } | |
| 2549 | |
| 2550 /* | |
| 2551 * xsltCompilerBuildInScopeNsList: | |
| 2552 * | |
| 2553 * Create and store the list of in-scope namespaces for the given | |
| 2554 * node in the stylesheet. If there are no changes in the in-scope | |
| 2555 * namespaces then the last ns-info of the ancestor axis will be returned. | |
| 2556 * Compilation-time only. | |
| 2557 * | |
| 2558 * Returns the ns-info or NULL if there are no namespaces in scope. | |
| 2559 */ | |
| 2560 static xsltNsListContainerPtr | |
| 2561 xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) | |
| 2562 { | |
| 2563 xsltNsListContainerPtr nsi = NULL; | |
| 2564 xmlNsPtr *list = NULL, ns; | |
| 2565 int i, maxns = 5; | |
| 2566 /* | |
| 2567 * Create a new ns-list for this position in the node-tree. | |
| 2568 * xmlGetNsList() will return NULL, if there are no ns-decls in the | |
| 2569 * tree. Note that the ns-decl for the XML namespace is not added | |
| 2570 * to the resulting list; the XPath module handles the XML namespace | |
| 2571 * internally. | |
| 2572 */ | |
| 2573 while (node != NULL) { | |
| 2574 if (node->type == XML_ELEMENT_NODE) { | |
| 2575 ns = node->nsDef; | |
| 2576 while (ns != NULL) { | |
| 2577 if (nsi == NULL) { | |
| 2578 nsi = (xsltNsListContainerPtr) | |
| 2579 xmlMalloc(sizeof(xsltNsListContainer)); | |
| 2580 if (nsi == NULL) { | |
| 2581 xsltTransformError(NULL, cctxt->style, NULL, | |
| 2582 "xsltCompilerBuildInScopeNsList: " | |
| 2583 "malloc failed!\n"); | |
| 2584 goto internal_err; | |
| 2585 } | |
| 2586 memset(nsi, 0, sizeof(xsltNsListContainer)); | |
| 2587 nsi->list = | |
| 2588 (xmlNsPtr *) xmlMalloc(maxns * sizeof(xmlNsPtr)); | |
| 2589 if (nsi->list == NULL) { | |
| 2590 xsltTransformError(NULL, cctxt->style, NULL, | |
| 2591 "xsltCompilerBuildInScopeNsList: " | |
| 2592 "malloc failed!\n"); | |
| 2593 goto internal_err; | |
| 2594 } | |
| 2595 nsi->list[0] = NULL; | |
| 2596 } | |
| 2597 /* | |
| 2598 * Skip shadowed namespace bindings. | |
| 2599 */ | |
| 2600 for (i = 0; i < nsi->totalNumber; i++) { | |
| 2601 if ((ns->prefix == nsi->list[i]->prefix) || | |
| 2602 (xmlStrEqual(ns->prefix, nsi->list[i]->prefix))) | |
| 2603 break; | |
| 2604 } | |
| 2605 if (i >= nsi->totalNumber) { | |
| 2606 if (nsi->totalNumber +1 >= maxns) { | |
| 2607 maxns *= 2; | |
| 2608 nsi->list = | |
| 2609 (xmlNsPtr *) xmlRealloc(nsi->list, | |
| 2610 maxns * sizeof(xmlNsPtr)); | |
| 2611 if (nsi->list == NULL) { | |
| 2612 xsltTransformError(NULL, cctxt->style, NULL, | |
| 2613 "xsltCompilerBuildInScopeNsList: " | |
| 2614 "realloc failed!\n"); | |
| 2615 goto internal_err; | |
| 2616 } | |
| 2617 } | |
| 2618 nsi->list[nsi->totalNumber++] = ns; | |
| 2619 nsi->list[nsi->totalNumber] = NULL; | |
| 2620 } | |
| 2621 | |
| 2622 ns = ns->next; | |
| 2623 } | |
| 2624 } | |
| 2625 node = node->parent; | |
| 2626 } | |
| 2627 if (nsi == NULL) | |
| 2628 return(NULL); | |
| 2629 /* | |
| 2630 * Move the default namespace to last position. | |
| 2631 */ | |
| 2632 nsi->xpathNumber = nsi->totalNumber; | |
| 2633 for (i = 0; i < nsi->totalNumber; i++) { | |
| 2634 if (nsi->list[i]->prefix == NULL) { | |
| 2635 ns = nsi->list[i]; | |
| 2636 nsi->list[i] = nsi->list[nsi->totalNumber-1]; | |
| 2637 nsi->list[nsi->totalNumber-1] = ns; | |
| 2638 nsi->xpathNumber--; | |
| 2639 break; | |
| 2640 } | |
| 2641 } | |
| 2642 /* | |
| 2643 * Store the ns-list in the stylesheet. | |
| 2644 */ | |
| 2645 if (xsltPointerListAddSize( | |
| 2646 (xsltPointerListPtr)cctxt->psData->inScopeNamespaces, | |
| 2647 (void *) nsi, 5) == -1) | |
| 2648 { | |
| 2649 xmlFree(nsi); | |
| 2650 nsi = NULL; | |
| 2651 xsltTransformError(NULL, cctxt->style, NULL, | |
| 2652 "xsltCompilerBuildInScopeNsList: failed to add ns-info.\n"); | |
| 2653 goto internal_err; | |
| 2654 } | |
| 2655 /* | |
| 2656 * Notify of change in status wrt namespaces. | |
| 2657 */ | |
| 2658 if (cctxt->inode != NULL) | |
| 2659 cctxt->inode->nsChanged = 1; | |
| 2660 | |
| 2661 return(nsi); | |
| 2662 | |
| 2663 internal_err: | |
| 2664 if (list != NULL) | |
| 2665 xmlFree(list); | |
| 2666 cctxt->style->errors++; | |
| 2667 return(NULL); | |
| 2668 } | |
| 2669 | |
| 2670 static int | |
| 2671 xsltParseNsPrefixList(xsltCompilerCtxtPtr cctxt, | |
| 2672 xsltPointerListPtr list, | |
| 2673 xmlNodePtr node, | |
| 2674 const xmlChar *value) | |
| 2675 { | |
| 2676 xmlChar *cur, *end; | |
| 2677 xmlNsPtr ns; | |
| 2678 | |
| 2679 if ((cctxt == NULL) || (value == NULL) || (list == NULL)) | |
| 2680 return(-1); | |
| 2681 | |
| 2682 list->number = 0; | |
| 2683 | |
| 2684 cur = (xmlChar *) value; | |
| 2685 while (*cur != 0) { | |
| 2686 while (IS_BLANK(*cur)) cur++; | |
| 2687 if (*cur == 0) | |
| 2688 break; | |
| 2689 end = cur; | |
| 2690 while ((*end != 0) && (!IS_BLANK(*end))) end++; | |
| 2691 cur = xmlStrndup(cur, end - cur); | |
| 2692 if (cur == NULL) { | |
| 2693 cur = end; | |
| 2694 continue; | |
| 2695 } | |
| 2696 /* | |
| 2697 * TODO: Export and use xmlSearchNsByPrefixStrict() | |
| 2698 * in Libxml2, tree.c, since xmlSearchNs() is in most | |
| 2699 * cases not efficient and in some cases not correct. | |
| 2700 * | |
| 2701 * XSLT-2 TODO: XSLT 2.0 allows an additional "#all" value. | |
| 2702 */ | |
| 2703 if ((cur[0] == '#') && | |
| 2704 xmlStrEqual(cur, (const xmlChar *)"#default")) | |
| 2705 ns = xmlSearchNs(cctxt->style->doc, node, NULL); | |
| 2706 else | |
| 2707 ns = xmlSearchNs(cctxt->style->doc, node, cur); | |
| 2708 | |
| 2709 if (ns == NULL) { | |
| 2710 /* | |
| 2711 * TODO: Better to report the attr-node, otherwise | |
| 2712 * the user won't know which attribute was invalid. | |
| 2713 */ | |
| 2714 xsltTransformError(NULL, cctxt->style, node, | |
| 2715 "No namespace binding in scope for prefix '%s'.\n", cur); | |
| 2716 /* | |
| 2717 * XSLT-1.0: "It is an error if there is no namespace | |
| 2718 * bound to the prefix on the element bearing the | |
| 2719 * exclude-result-prefixes or xsl:exclude-result-prefixes | |
| 2720 * attribute." | |
| 2721 */ | |
| 2722 cctxt->style->errors++; | |
| 2723 } else { | |
| 2724 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 2725 xsltGenericDebug(xsltGenericDebugContext, | |
| 2726 "resolved prefix '%s'\n", cur); | |
| 2727 #endif | |
| 2728 /* | |
| 2729 * Note that we put the namespace name into the dict. | |
| 2730 */ | |
| 2731 if (xsltPointerListAddSize(list, | |
| 2732 (void *) xmlDictLookup(cctxt->style->dict, | |
| 2733 ns->href, -1), 5) == -1) | |
| 2734 { | |
| 2735 xmlFree(cur); | |
| 2736 goto internal_err; | |
| 2737 } | |
| 2738 } | |
| 2739 xmlFree(cur); | |
| 2740 | |
| 2741 cur = end; | |
| 2742 } | |
| 2743 return(0); | |
| 2744 | |
| 2745 internal_err: | |
| 2746 cctxt->style->errors++; | |
| 2747 return(-1); | |
| 2748 } | |
| 2749 | |
| 2750 /** | |
| 2751 * xsltCompilerUtilsCreateMergedList: | |
| 2752 * @dest: the destination list (optional) | |
| 2753 * @first: the first list | |
| 2754 * @second: the second list (optional) | |
| 2755 * | |
| 2756 * Appends the content of @second to @first into @destination. | |
| 2757 * If @destination is NULL a new list will be created. | |
| 2758 * | |
| 2759 * Returns the merged list of items or NULL if there's nothing to merge. | |
| 2760 */ | |
| 2761 static xsltPointerListPtr | |
| 2762 xsltCompilerUtilsCreateMergedList(xsltPointerListPtr first, | |
| 2763 xsltPointerListPtr second) | |
| 2764 { | |
| 2765 xsltPointerListPtr ret; | |
| 2766 size_t num; | |
| 2767 | |
| 2768 if (first) | |
| 2769 num = first->number; | |
| 2770 else | |
| 2771 num = 0; | |
| 2772 if (second) | |
| 2773 num += second->number; | |
| 2774 if (num == 0) | |
| 2775 return(NULL); | |
| 2776 ret = xsltPointerListCreate(num); | |
| 2777 if (ret == NULL) | |
| 2778 return(NULL); | |
| 2779 /* | |
| 2780 * Copy contents. | |
| 2781 */ | |
| 2782 if ((first != NULL) && (first->number != 0)) { | |
| 2783 memcpy(ret->items, first->items, | |
| 2784 first->number * sizeof(void *)); | |
| 2785 if ((second != NULL) && (second->number != 0)) | |
| 2786 memcpy(ret->items + first->number, second->items, | |
| 2787 second->number * sizeof(void *)); | |
| 2788 } else if ((second != NULL) && (second->number != 0)) | |
| 2789 memcpy(ret->items, (void *) second->items, | |
| 2790 second->number * sizeof(void *)); | |
| 2791 ret->number = num; | |
| 2792 return(ret); | |
| 2793 } | |
| 2794 | |
| 2795 /* | |
| 2796 * xsltParseExclResultPrefixes: | |
| 2797 * | |
| 2798 * Create and store the list of in-scope namespaces for the given | |
| 2799 * node in the stylesheet. If there are no changes in the in-scope | |
| 2800 * namespaces then the last ns-info of the ancestor axis will be returned. | |
| 2801 * Compilation-time only. | |
| 2802 * | |
| 2803 * Returns the ns-info or NULL if there are no namespaces in scope. | |
| 2804 */ | |
| 2805 static xsltPointerListPtr | |
| 2806 xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, | |
| 2807 xsltPointerListPtr def, | |
| 2808 int instrCategory) | |
| 2809 { | |
| 2810 xsltPointerListPtr list = NULL; | |
| 2811 xmlChar *value; | |
| 2812 xmlAttrPtr attr; | |
| 2813 | |
| 2814 if ((cctxt == NULL) || (node == NULL)) | |
| 2815 return(NULL); | |
| 2816 | |
| 2817 if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) | |
| 2818 attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", NULL); | |
| 2819 else | |
| 2820 attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", | |
| 2821 XSLT_NAMESPACE); | |
| 2822 if (attr == NULL) | |
| 2823 return(def); | |
| 2824 | |
| 2825 if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { | |
| 2826 /* | |
| 2827 * Mark the XSLT attr. | |
| 2828 */ | |
| 2829 attr->psvi = (void *) xsltXSLTAttrMarker; | |
| 2830 } | |
| 2831 | |
| 2832 if ((attr->children != NULL) && | |
| 2833 (attr->children->content != NULL)) | |
| 2834 value = attr->children->content; | |
| 2835 else { | |
| 2836 xsltTransformError(NULL, cctxt->style, node, | |
| 2837 "Attribute 'exclude-result-prefixes': Invalid value.\n"); | |
| 2838 cctxt->style->errors++; | |
| 2839 return(def); | |
| 2840 } | |
| 2841 | |
| 2842 if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node, | |
| 2843 BAD_CAST value) != 0) | |
| 2844 goto exit; | |
| 2845 if (cctxt->tmpList->number == 0) | |
| 2846 goto exit; | |
| 2847 /* | |
| 2848 * Merge the list with the inherited list. | |
| 2849 */ | |
| 2850 list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList); | |
| 2851 if (list == NULL) | |
| 2852 goto exit; | |
| 2853 /* | |
| 2854 * Store the list in the stylesheet/compiler context. | |
| 2855 */ | |
| 2856 if (xsltPointerListAddSize( | |
| 2857 cctxt->psData->exclResultNamespaces, list, 5) == -1) | |
| 2858 { | |
| 2859 xsltPointerListFree(list); | |
| 2860 list = NULL; | |
| 2861 goto exit; | |
| 2862 } | |
| 2863 /* | |
| 2864 * Notify of change in status wrt namespaces. | |
| 2865 */ | |
| 2866 if (cctxt->inode != NULL) | |
| 2867 cctxt->inode->nsChanged = 1; | |
| 2868 | |
| 2869 exit: | |
| 2870 if (list != NULL) | |
| 2871 return(list); | |
| 2872 else | |
| 2873 return(def); | |
| 2874 } | |
| 2875 | |
| 2876 /* | |
| 2877 * xsltParseExtElemPrefixes: | |
| 2878 * | |
| 2879 * Create and store the list of in-scope namespaces for the given | |
| 2880 * node in the stylesheet. If there are no changes in the in-scope | |
| 2881 * namespaces then the last ns-info of the ancestor axis will be returned. | |
| 2882 * Compilation-time only. | |
| 2883 * | |
| 2884 * Returns the ns-info or NULL if there are no namespaces in scope. | |
| 2885 */ | |
| 2886 static xsltPointerListPtr | |
| 2887 xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, | |
| 2888 xsltPointerListPtr def, | |
| 2889 int instrCategory) | |
| 2890 { | |
| 2891 xsltPointerListPtr list = NULL; | |
| 2892 xmlAttrPtr attr; | |
| 2893 xmlChar *value; | |
| 2894 int i; | |
| 2895 | |
| 2896 if ((cctxt == NULL) || (node == NULL)) | |
| 2897 return(NULL); | |
| 2898 | |
| 2899 if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) | |
| 2900 attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", NULL); | |
| 2901 else | |
| 2902 attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", | |
| 2903 XSLT_NAMESPACE); | |
| 2904 if (attr == NULL) | |
| 2905 return(def); | |
| 2906 | |
| 2907 if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { | |
| 2908 /* | |
| 2909 * Mark the XSLT attr. | |
| 2910 */ | |
| 2911 attr->psvi = (void *) xsltXSLTAttrMarker; | |
| 2912 } | |
| 2913 | |
| 2914 if ((attr->children != NULL) && | |
| 2915 (attr->children->content != NULL)) | |
| 2916 value = attr->children->content; | |
| 2917 else { | |
| 2918 xsltTransformError(NULL, cctxt->style, node, | |
| 2919 "Attribute 'extension-element-prefixes': Invalid value.\n"); | |
| 2920 cctxt->style->errors++; | |
| 2921 return(def); | |
| 2922 } | |
| 2923 | |
| 2924 | |
| 2925 if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node, | |
| 2926 BAD_CAST value) != 0) | |
| 2927 goto exit; | |
| 2928 | |
| 2929 if (cctxt->tmpList->number == 0) | |
| 2930 goto exit; | |
| 2931 /* | |
| 2932 * REVISIT: Register the extension namespaces. | |
| 2933 */ | |
| 2934 for (i = 0; i < cctxt->tmpList->number; i++) | |
| 2935 xsltRegisterExtPrefix(cctxt->style, NULL, | |
| 2936 BAD_CAST cctxt->tmpList->items[i]); | |
| 2937 /* | |
| 2938 * Merge the list with the inherited list. | |
| 2939 */ | |
| 2940 list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList); | |
| 2941 if (list == NULL) | |
| 2942 goto exit; | |
| 2943 /* | |
| 2944 * Store the list in the stylesheet. | |
| 2945 */ | |
| 2946 if (xsltPointerListAddSize( | |
| 2947 cctxt->psData->extElemNamespaces, list, 5) == -1) | |
| 2948 { | |
| 2949 xsltPointerListFree(list); | |
| 2950 list = NULL; | |
| 2951 goto exit; | |
| 2952 } | |
| 2953 /* | |
| 2954 * Notify of change in status wrt namespaces. | |
| 2955 */ | |
| 2956 if (cctxt->inode != NULL) | |
| 2957 cctxt->inode->nsChanged = 1; | |
| 2958 | |
| 2959 exit: | |
| 2960 if (list != NULL) | |
| 2961 return(list); | |
| 2962 else | |
| 2963 return(def); | |
| 2964 } | |
| 2965 | |
| 2966 /* | |
| 2967 * xsltParseAttrXSLTVersion: | |
| 2968 * | |
| 2969 * @cctxt: the compilation context | |
| 2970 * @node: the element-node | |
| 2971 * @isXsltElem: whether this is an XSLT element | |
| 2972 * | |
| 2973 * Parses the attribute xsl:version. | |
| 2974 * | |
| 2975 * Returns 1 if there was such an attribute, 0 if not and | |
| 2976 * -1 if an internal or API error occured. | |
| 2977 */ | |
| 2978 static int | |
| 2979 xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, | |
| 2980 int instrCategory) | |
| 2981 { | |
| 2982 xmlChar *value; | |
| 2983 xmlAttrPtr attr; | |
| 2984 | |
| 2985 if ((cctxt == NULL) || (node == NULL)) | |
| 2986 return(-1); | |
| 2987 | |
| 2988 if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) | |
| 2989 attr = xmlHasNsProp(node, BAD_CAST "version", NULL); | |
| 2990 else | |
| 2991 attr = xmlHasNsProp(node, BAD_CAST "version", XSLT_NAMESPACE); | |
| 2992 | |
| 2993 if (attr == NULL) | |
| 2994 return(0); | |
| 2995 | |
| 2996 attr->psvi = (void *) xsltXSLTAttrMarker; | |
| 2997 | |
| 2998 if ((attr->children != NULL) && | |
| 2999 (attr->children->content != NULL)) | |
| 3000 value = attr->children->content; | |
| 3001 else { | |
| 3002 xsltTransformError(NULL, cctxt->style, node, | |
| 3003 "Attribute 'version': Invalid value.\n"); | |
| 3004 cctxt->style->errors++; | |
| 3005 return(1); | |
| 3006 } | |
| 3007 | |
| 3008 if (! xmlStrEqual(value, (const xmlChar *)"1.0")) { | |
| 3009 cctxt->inode->forwardsCompat = 1; | |
| 3010 /* | |
| 3011 * TODO: To what extent do we support the | |
| 3012 * forwards-compatible mode? | |
| 3013 */ | |
| 3014 /* | |
| 3015 * Report this only once per compilation episode. | |
| 3016 */ | |
| 3017 if (! cctxt->hasForwardsCompat) { | |
| 3018 cctxt->hasForwardsCompat = 1; | |
| 3019 cctxt->errSeverity = XSLT_ERROR_SEVERITY_WARNING; | |
| 3020 xsltTransformError(NULL, cctxt->style, node, | |
| 3021 "Warning: the attribute xsl:version specifies a value " | |
| 3022 "different from '1.0'. Switching to forwards-compatible " | |
| 3023 "mode. Only features of XSLT 1.0 are supported by this " | |
| 3024 "processor.\n"); | |
| 3025 cctxt->style->warnings++; | |
| 3026 cctxt->errSeverity = XSLT_ERROR_SEVERITY_ERROR; | |
| 3027 } | |
| 3028 } else { | |
| 3029 cctxt->inode->forwardsCompat = 0; | |
| 3030 } | |
| 3031 | |
| 3032 if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { | |
| 3033 /* | |
| 3034 * Set a marker on XSLT attributes. | |
| 3035 */ | |
| 3036 attr->psvi = (void *) xsltXSLTAttrMarker; | |
| 3037 } | |
| 3038 return(1); | |
| 3039 } | |
| 3040 | |
| 3041 static int | |
| 3042 xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) | |
| 3043 { | |
| 3044 xmlNodePtr deleteNode, cur, txt, textNode = NULL; | |
| 3045 xmlDocPtr doc; | |
| 3046 xsltStylesheetPtr style; | |
| 3047 int internalize = 0, findSpaceAttr; | |
| 3048 int xsltStylesheetElemDepth; | |
| 3049 xmlAttrPtr attr; | |
| 3050 xmlChar *value; | |
| 3051 const xmlChar *name, *nsNameXSLT = NULL; | |
| 3052 int strictWhitespace, inXSLText = 0; | |
| 3053 #ifdef XSLT_REFACTORED_XSLT_NSCOMP | |
| 3054 xsltNsMapPtr nsMapItem; | |
| 3055 #endif | |
| 3056 | |
| 3057 if ((cctxt == NULL) || (cctxt->style == NULL) || | |
| 3058 (node == NULL) || (node->type != XML_ELEMENT_NODE)) | |
| 3059 return(-1); | |
| 3060 | |
| 3061 doc = node->doc; | |
| 3062 if (doc == NULL) | |
| 3063 goto internal_err; | |
| 3064 | |
| 3065 style = cctxt->style; | |
| 3066 if ((style->dict != NULL) && (doc->dict == style->dict)) | |
| 3067 internalize = 1; | |
| 3068 else | |
| 3069 style->internalized = 0; | |
| 3070 | |
| 3071 /* | |
| 3072 * Init value of xml:space. Since this might be an embedded | |
| 3073 * stylesheet, this is needed to be performed on the element | |
| 3074 * where the stylesheet is rooted at, taking xml:space of | |
| 3075 * ancestors into account. | |
| 3076 */ | |
| 3077 if (! cctxt->simplified) | |
| 3078 xsltStylesheetElemDepth = cctxt->depth +1; | |
| 3079 else | |
| 3080 xsltStylesheetElemDepth = 0; | |
| 3081 | |
| 3082 if (xmlNodeGetSpacePreserve(node) != 1) | |
| 3083 cctxt->inode->preserveWhitespace = 0; | |
| 3084 else | |
| 3085 cctxt->inode->preserveWhitespace = 1; | |
| 3086 | |
| 3087 /* | |
| 3088 * Eval if we should keep the old incorrect behaviour. | |
| 3089 */ | |
| 3090 strictWhitespace = (cctxt->strict != 0) ? 1 : 0; | |
| 3091 | |
| 3092 nsNameXSLT = xsltConstNamespaceNameXSLT; | |
| 3093 | |
| 3094 deleteNode = NULL; | |
| 3095 cur = node; | |
| 3096 while (cur != NULL) { | |
| 3097 if (deleteNode != NULL) { | |
| 3098 | |
| 3099 #ifdef WITH_XSLT_DEBUG_BLANKS | |
| 3100 xsltGenericDebug(xsltGenericDebugContext, | |
| 3101 "xsltParsePreprocessStylesheetTree: removing node\n"); | |
| 3102 #endif | |
| 3103 xmlUnlinkNode(deleteNode); | |
| 3104 xmlFreeNode(deleteNode); | |
| 3105 deleteNode = NULL; | |
| 3106 } | |
| 3107 if (cur->type == XML_ELEMENT_NODE) { | |
| 3108 | |
| 3109 /* | |
| 3110 * Clear the PSVI field. | |
| 3111 */ | |
| 3112 cur->psvi = NULL; | |
| 3113 | |
| 3114 xsltCompilerNodePush(cctxt, cur); | |
| 3115 | |
| 3116 inXSLText = 0; | |
| 3117 textNode = NULL; | |
| 3118 findSpaceAttr = 1; | |
| 3119 cctxt->inode->stripWhitespace = 0; | |
| 3120 /* | |
| 3121 * TODO: I'd love to use a string pointer comparison here :-/ | |
| 3122 */ | |
| 3123 if (IS_XSLT_ELEM(cur)) { | |
| 3124 #ifdef XSLT_REFACTORED_XSLT_NSCOMP | |
| 3125 if (cur->ns->href != nsNameXSLT) { | |
| 3126 nsMapItem = xsltNewNamespaceMapItem(cctxt, | |
| 3127 doc, cur->ns, cur); | |
| 3128 if (nsMapItem == NULL) | |
| 3129 goto internal_err; | |
| 3130 cur->ns->href = nsNameXSLT; | |
| 3131 } | |
| 3132 #endif | |
| 3133 | |
| 3134 if (cur->name == NULL) | |
| 3135 goto process_attributes; | |
| 3136 /* | |
| 3137 * Mark the XSLT element for later recognition. | |
| 3138 * TODO: Using the marker is still too dangerous, since if | |
| 3139 * the parsing mechanism leaves out an XSLT element, then | |
| 3140 * this might hit the transformation-mechanism, which | |
| 3141 * will break if it doesn't expect such a marker. | |
| 3142 */ | |
| 3143 /* cur->psvi = (void *) xsltXSLTElemMarker; */ | |
| 3144 | |
| 3145 /* | |
| 3146 * XSLT 2.0: "Any whitespace text node whose parent is | |
| 3147 * one of the following elements is removed from the " | |
| 3148 * tree, regardless of any xml:space attributes:..." | |
| 3149 * xsl:apply-imports, | |
| 3150 * xsl:apply-templates, | |
| 3151 * xsl:attribute-set, | |
| 3152 * xsl:call-template, | |
| 3153 * xsl:choose, | |
| 3154 * xsl:stylesheet, xsl:transform. | |
| 3155 * XSLT 2.0: xsl:analyze-string, | |
| 3156 * xsl:character-map, | |
| 3157 * xsl:next-match | |
| 3158 * | |
| 3159 * TODO: I'd love to use a string pointer comparison here :-/ | |
| 3160 */ | |
| 3161 name = cur->name; | |
| 3162 switch (*name) { | |
| 3163 case 't': | |
| 3164 if ((name[0] == 't') && (name[1] == 'e') && | |
| 3165 (name[2] == 'x') && (name[3] == 't') && | |
| 3166 (name[4] == 0)) | |
| 3167 { | |
| 3168 /* | |
| 3169 * Process the xsl:text element. | |
| 3170 * ---------------------------- | |
| 3171 * Mark it for later recognition. | |
| 3172 */ | |
| 3173 cur->psvi = (void *) xsltXSLTTextMarker; | |
| 3174 /* | |
| 3175 * For stylesheets, the set of | |
| 3176 * whitespace-preserving element names | |
| 3177 * consists of just xsl:text. | |
| 3178 */ | |
| 3179 findSpaceAttr = 0; | |
| 3180 cctxt->inode->preserveWhitespace = 1; | |
| 3181 inXSLText = 1; | |
| 3182 } | |
| 3183 break; | |
| 3184 case 'c': | |
| 3185 if (xmlStrEqual(name, BAD_CAST "choose") || | |
| 3186 xmlStrEqual(name, BAD_CAST "call-template")) | |
| 3187 cctxt->inode->stripWhitespace = 1; | |
| 3188 break; | |
| 3189 case 'a': | |
| 3190 if (xmlStrEqual(name, BAD_CAST "apply-templates") || | |
| 3191 xmlStrEqual(name, BAD_CAST "apply-imports") || | |
| 3192 xmlStrEqual(name, BAD_CAST "attribute-set")) | |
| 3193 | |
| 3194 cctxt->inode->stripWhitespace = 1; | |
| 3195 break; | |
| 3196 default: | |
| 3197 if (xsltStylesheetElemDepth == cctxt->depth) { | |
| 3198 /* | |
| 3199 * This is a xsl:stylesheet/xsl:transform. | |
| 3200 */ | |
| 3201 cctxt->inode->stripWhitespace = 1; | |
| 3202 break; | |
| 3203 } | |
| 3204 | |
| 3205 if ((cur->prev != NULL) && | |
| 3206 (cur->prev->type == XML_TEXT_NODE)) | |
| 3207 { | |
| 3208 /* | |
| 3209 * XSLT 2.0 : "Any whitespace text node whose | |
| 3210 * following-sibling node is an xsl:param or | |
| 3211 * xsl:sort element is removed from the tree, | |
| 3212 * regardless of any xml:space attributes." | |
| 3213 */ | |
| 3214 if (((*name == 'p') || (*name == 's')) && | |
| 3215 (xmlStrEqual(name, BAD_CAST "param") || | |
| 3216 xmlStrEqual(name, BAD_CAST "sort"))) | |
| 3217 { | |
| 3218 do { | |
| 3219 if (IS_BLANK_NODE(cur->prev)) { | |
| 3220 txt = cur->prev; | |
| 3221 xmlUnlinkNode(txt); | |
| 3222 xmlFreeNode(txt); | |
| 3223 } else { | |
| 3224 /* | |
| 3225 * This will result in a content | |
| 3226 * error, when hitting the parsing | |
| 3227 * functions. | |
| 3228 */ | |
| 3229 break; | |
| 3230 } | |
| 3231 } while (cur->prev); | |
| 3232 } | |
| 3233 } | |
| 3234 break; | |
| 3235 } | |
| 3236 } | |
| 3237 | |
| 3238 process_attributes: | |
| 3239 /* | |
| 3240 * Process attributes. | |
| 3241 * ------------------ | |
| 3242 */ | |
| 3243 if (cur->properties != NULL) { | |
| 3244 if (cur->children == NULL) | |
| 3245 findSpaceAttr = 0; | |
| 3246 attr = cur->properties; | |
| 3247 do { | |
| 3248 #ifdef XSLT_REFACTORED_XSLT_NSCOMP | |
| 3249 if ((attr->ns) && (attr->ns->href != nsNameXSLT) && | |
| 3250 xmlStrEqual(attr->ns->href, nsNameXSLT)) | |
| 3251 { | |
| 3252 nsMapItem = xsltNewNamespaceMapItem(cctxt, | |
| 3253 doc, attr->ns, cur); | |
| 3254 if (nsMapItem == NULL) | |
| 3255 goto internal_err; | |
| 3256 attr->ns->href = nsNameXSLT; | |
| 3257 } | |
| 3258 #endif | |
| 3259 if (internalize) { | |
| 3260 /* | |
| 3261 * Internalize the attribute's value; the goal is to | |
| 3262 * speed up operations and minimize used space by | |
| 3263 * compiled stylesheets. | |
| 3264 */ | |
| 3265 txt = attr->children; | |
| 3266 /* | |
| 3267 * NOTE that this assumes only one | |
| 3268 * text-node in the attribute's content. | |
| 3269 */ | |
| 3270 if ((txt != NULL) && (txt->content != NULL) && | |
| 3271 (!xmlDictOwns(style->dict, txt->content))) | |
| 3272 { | |
| 3273 value = (xmlChar *) xmlDictLookup(style->dict, | |
| 3274 txt->content, -1); | |
| 3275 xmlNodeSetContent(txt, NULL); | |
| 3276 txt->content = value; | |
| 3277 } | |
| 3278 } | |
| 3279 /* | |
| 3280 * Process xml:space attributes. | |
| 3281 * ---------------------------- | |
| 3282 */ | |
| 3283 if ((findSpaceAttr != 0) && | |
| 3284 (attr->ns != NULL) && | |
| 3285 (attr->name != NULL) && | |
| 3286 (attr->name[0] == 's') && | |
| 3287 (attr->ns->prefix != NULL) && | |
| 3288 (attr->ns->prefix[0] == 'x') && | |
| 3289 (attr->ns->prefix[1] == 'm') && | |
| 3290 (attr->ns->prefix[2] == 'l') && | |
| 3291 (attr->ns->prefix[3] == 0)) | |
| 3292 { | |
| 3293 value = xmlGetNsProp(cur, BAD_CAST "space", | |
| 3294 XML_XML_NAMESPACE); | |
| 3295 if (value != NULL) { | |
| 3296 if (xmlStrEqual(value, BAD_CAST "preserve")) { | |
| 3297 cctxt->inode->preserveWhitespace = 1; | |
| 3298 } else if (xmlStrEqual(value, BAD_CAST "default")) { | |
| 3299 cctxt->inode->preserveWhitespace = 0; | |
| 3300 } else { | |
| 3301 /* Invalid value for xml:space. */ | |
| 3302 xsltTransformError(NULL, style, cur, | |
| 3303 "Attribute xml:space: Invalid value.\n"); | |
| 3304 cctxt->style->warnings++; | |
| 3305 } | |
| 3306 findSpaceAttr = 0; | |
| 3307 xmlFree(value); | |
| 3308 } | |
| 3309 | |
| 3310 } | |
| 3311 attr = attr->next; | |
| 3312 } while (attr != NULL); | |
| 3313 } | |
| 3314 /* | |
| 3315 * We'll descend into the children of element nodes only. | |
| 3316 */ | |
| 3317 if (cur->children != NULL) { | |
| 3318 cur = cur->children; | |
| 3319 continue; | |
| 3320 } | |
| 3321 } else if ((cur->type == XML_TEXT_NODE) || | |
| 3322 (cur->type == XML_CDATA_SECTION_NODE)) | |
| 3323 { | |
| 3324 /* | |
| 3325 * Merge adjacent text/CDATA-section-nodes | |
| 3326 * --------------------------------------- | |
| 3327 * In order to avoid breaking of existing stylesheets, | |
| 3328 * if the old behaviour is wanted (strictWhitespace == 0), | |
| 3329 * then we *won't* merge adjacent text-nodes | |
| 3330 * (except in xsl:text); this will ensure that whitespace-only | |
| 3331 * text nodes are (incorrectly) not stripped in some cases. | |
| 3332 * | |
| 3333 * Example: : <foo> <!-- bar -->zoo</foo> | |
| 3334 * Corrent (strict) result: <foo> zoo</foo> | |
| 3335 * Incorrect (old) result : <foo>zoo</foo> | |
| 3336 * | |
| 3337 * NOTE that we *will* merge adjacent text-nodes if | |
| 3338 * they are in xsl:text. | |
| 3339 * Example, the following: | |
| 3340 * <xsl:text> <!-- bar -->zoo<xsl:text> | |
| 3341 * will result in both cases in: | |
| 3342 * <xsl:text> zoo<xsl:text> | |
| 3343 */ | |
| 3344 cur->type = XML_TEXT_NODE; | |
| 3345 if ((strictWhitespace != 0) || (inXSLText != 0)) { | |
| 3346 /* | |
| 3347 * New behaviour; merge nodes. | |
| 3348 */ | |
| 3349 if (textNode == NULL) | |
| 3350 textNode = cur; | |
| 3351 else { | |
| 3352 if (cur->content != NULL) | |
| 3353 xmlNodeAddContent(textNode, cur->content); | |
| 3354 deleteNode = cur; | |
| 3355 } | |
| 3356 if ((cur->next == NULL) || | |
| 3357 (cur->next->type == XML_ELEMENT_NODE)) | |
| 3358 goto end_of_text; | |
| 3359 else | |
| 3360 goto next_sibling; | |
| 3361 } else { | |
| 3362 /* | |
| 3363 * Old behaviour. | |
| 3364 */ | |
| 3365 if (textNode == NULL) | |
| 3366 textNode = cur; | |
| 3367 goto end_of_text; | |
| 3368 } | |
| 3369 } else if ((cur->type == XML_COMMENT_NODE) || | |
| 3370 (cur->type == XML_PI_NODE)) | |
| 3371 { | |
| 3372 /* | |
| 3373 * Remove processing instructions and comments. | |
| 3374 */ | |
| 3375 deleteNode = cur; | |
| 3376 if ((cur->next == NULL) || | |
| 3377 (cur->next->type == XML_ELEMENT_NODE)) | |
| 3378 goto end_of_text; | |
| 3379 else | |
| 3380 goto next_sibling; | |
| 3381 } else { | |
| 3382 textNode = NULL; | |
| 3383 /* | |
| 3384 * Invalid node-type for this data-model. | |
| 3385 */ | |
| 3386 xsltTransformError(NULL, style, cur, | |
| 3387 "Invalid type of node for the XSLT data model.\n"); | |
| 3388 cctxt->style->errors++; | |
| 3389 goto next_sibling; | |
| 3390 } | |
| 3391 | |
| 3392 end_of_text: | |
| 3393 if (textNode) { | |
| 3394 value = textNode->content; | |
| 3395 /* | |
| 3396 * At this point all adjacent text/CDATA-section nodes | |
| 3397 * have been merged. | |
| 3398 * | |
| 3399 * Strip whitespace-only text-nodes. | |
| 3400 * (cctxt->inode->stripWhitespace) | |
| 3401 */ | |
| 3402 if ((value == NULL) || (*value == 0) || | |
| 3403 (((cctxt->inode->stripWhitespace) || | |
| 3404 (! cctxt->inode->preserveWhitespace)) && | |
| 3405 IS_BLANK(*value) && | |
| 3406 xsltIsBlank(value))) | |
| 3407 { | |
| 3408 if (textNode != cur) { | |
| 3409 xmlUnlinkNode(textNode); | |
| 3410 xmlFreeNode(textNode); | |
| 3411 } else | |
| 3412 deleteNode = textNode; | |
| 3413 textNode = NULL; | |
| 3414 goto next_sibling; | |
| 3415 } | |
| 3416 /* | |
| 3417 * Convert CDATA-section nodes to text-nodes. | |
| 3418 * TODO: Can this produce problems? | |
| 3419 */ | |
| 3420 if (textNode->type != XML_TEXT_NODE) { | |
| 3421 textNode->type = XML_TEXT_NODE; | |
| 3422 textNode->name = xmlStringText; | |
| 3423 } | |
| 3424 if (internalize && | |
| 3425 (textNode->content != NULL) && | |
| 3426 (!xmlDictOwns(style->dict, textNode->content))) | |
| 3427 { | |
| 3428 /* | |
| 3429 * Internalize the string. | |
| 3430 */ | |
| 3431 value = (xmlChar *) xmlDictLookup(style->dict, | |
| 3432 textNode->content, -1); | |
| 3433 xmlNodeSetContent(textNode, NULL); | |
| 3434 textNode->content = value; | |
| 3435 } | |
| 3436 textNode = NULL; | |
| 3437 /* | |
| 3438 * Note that "disable-output-escaping" of the xsl:text | |
| 3439 * element will be applied at a later level, when | |
| 3440 * XSLT elements are processed. | |
| 3441 */ | |
| 3442 } | |
| 3443 | |
| 3444 next_sibling: | |
| 3445 if (cur->type == XML_ELEMENT_NODE) { | |
| 3446 xsltCompilerNodePop(cctxt, cur); | |
| 3447 } | |
| 3448 if (cur == node) | |
| 3449 break; | |
| 3450 if (cur->next != NULL) { | |
| 3451 cur = cur->next; | |
| 3452 } else { | |
| 3453 cur = cur->parent; | |
| 3454 inXSLText = 0; | |
| 3455 goto next_sibling; | |
| 3456 }; | |
| 3457 } | |
| 3458 if (deleteNode != NULL) { | |
| 3459 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 3460 xsltGenericDebug(xsltGenericDebugContext, | |
| 3461 "xsltParsePreprocessStylesheetTree: removing node\n"); | |
| 3462 #endif | |
| 3463 xmlUnlinkNode(deleteNode); | |
| 3464 xmlFreeNode(deleteNode); | |
| 3465 } | |
| 3466 return(0); | |
| 3467 | |
| 3468 internal_err: | |
| 3469 return(-1); | |
| 3470 } | |
| 3471 | |
| 3472 #endif /* XSLT_REFACTORED */ | |
| 3473 | |
| 3474 #ifdef XSLT_REFACTORED | |
| 3475 #else | |
| 3476 static void | |
| 3477 xsltPreprocessStylesheet(xsltStylesheetPtr style, xmlNodePtr cur) | |
| 3478 { | |
| 3479 xmlNodePtr deleteNode, styleelem; | |
| 3480 int internalize = 0; | |
| 3481 | |
| 3482 if ((style == NULL) || (cur == NULL)) | |
| 3483 return; | |
| 3484 | |
| 3485 if ((cur->doc != NULL) && (style->dict != NULL) && | |
| 3486 (cur->doc->dict == style->dict)) | |
| 3487 internalize = 1; | |
| 3488 else | |
| 3489 style->internalized = 0; | |
| 3490 | |
| 3491 if ((cur != NULL) && (IS_XSLT_ELEM(cur)) && | |
| 3492 (IS_XSLT_NAME(cur, "stylesheet"))) { | |
| 3493 styleelem = cur; | |
| 3494 } else { | |
| 3495 styleelem = NULL; | |
| 3496 } | |
| 3497 | |
| 3498 /* | |
| 3499 * This content comes from the stylesheet | |
| 3500 * For stylesheets, the set of whitespace-preserving | |
| 3501 * element names consists of just xsl:text. | |
| 3502 */ | |
| 3503 deleteNode = NULL; | |
| 3504 while (cur != NULL) { | |
| 3505 if (deleteNode != NULL) { | |
| 3506 #ifdef WITH_XSLT_DEBUG_BLANKS | |
| 3507 xsltGenericDebug(xsltGenericDebugContext, | |
| 3508 "xsltPreprocessStylesheet: removing ignorable blank node\n"); | |
| 3509 #endif | |
| 3510 xmlUnlinkNode(deleteNode); | |
| 3511 xmlFreeNode(deleteNode); | |
| 3512 deleteNode = NULL; | |
| 3513 } | |
| 3514 if (cur->type == XML_ELEMENT_NODE) { | |
| 3515 int exclPrefixes; | |
| 3516 /* | |
| 3517 * Internalize attributes values. | |
| 3518 */ | |
| 3519 if ((internalize) && (cur->properties != NULL)) { | |
| 3520 xmlAttrPtr attr = cur->properties; | |
| 3521 xmlNodePtr txt; | |
| 3522 | |
| 3523 while (attr != NULL) { | |
| 3524 txt = attr->children; | |
| 3525 if ((txt != NULL) && (txt->type == XML_TEXT_NODE) && | |
| 3526 (txt->content != NULL) && | |
| 3527 (!xmlDictOwns(style->dict, txt->content))) | |
| 3528 { | |
| 3529 xmlChar *tmp; | |
| 3530 | |
| 3531 /* | |
| 3532 * internalize the text string, goal is to speed | |
| 3533 * up operations and minimize used space by compiled | |
| 3534 * stylesheets. | |
| 3535 */ | |
| 3536 tmp = (xmlChar *) xmlDictLookup(style->dict, | |
| 3537 txt->content, -1); | |
| 3538 if (tmp != txt->content) { | |
| 3539 xmlNodeSetContent(txt, NULL); | |
| 3540 txt->content = tmp; | |
| 3541 } | |
| 3542 } | |
| 3543 attr = attr->next; | |
| 3544 } | |
| 3545 } | |
| 3546 if (IS_XSLT_ELEM(cur)) { | |
| 3547 exclPrefixes = 0; | |
| 3548 if (IS_XSLT_NAME(cur, "text")) { | |
| 3549 for (;exclPrefixes > 0;exclPrefixes--) | |
| 3550 exclPrefixPop(style); | |
| 3551 goto skip_children; | |
| 3552 } | |
| 3553 } else { | |
| 3554 exclPrefixes = xsltParseStylesheetExcludePrefix(style, cur, 0); | |
| 3555 } | |
| 3556 | |
| 3557 if ((cur->nsDef != NULL) && (style->exclPrefixNr > 0)) { | |
| 3558 xmlNsPtr ns = cur->nsDef, prev = NULL, next; | |
| 3559 xmlNodePtr root = NULL; | |
| 3560 int i, moved; | |
| 3561 | |
| 3562 root = xmlDocGetRootElement(cur->doc); | |
| 3563 if ((root != NULL) && (root != cur)) { | |
| 3564 while (ns != NULL) { | |
| 3565 moved = 0; | |
| 3566 next = ns->next; | |
| 3567 for (i = 0;i < style->exclPrefixNr;i++) { | |
| 3568 if ((ns->prefix != NULL) && | |
| 3569 (xmlStrEqual(ns->href, | |
| 3570 style->exclPrefixTab[i]))) { | |
| 3571 /* | |
| 3572 * Move the namespace definition on the root | |
| 3573 * element to avoid duplicating it without | |
| 3574 * loosing it. | |
| 3575 */ | |
| 3576 if (prev == NULL) { | |
| 3577 cur->nsDef = ns->next; | |
| 3578 } else { | |
| 3579 prev->next = ns->next; | |
| 3580 } | |
| 3581 ns->next = root->nsDef; | |
| 3582 root->nsDef = ns; | |
| 3583 moved = 1; | |
| 3584 break; | |
| 3585 } | |
| 3586 } | |
| 3587 if (moved == 0) | |
| 3588 prev = ns; | |
| 3589 ns = next; | |
| 3590 } | |
| 3591 } | |
| 3592 } | |
| 3593 /* | |
| 3594 * If we have prefixes locally, recurse and pop them up when | |
| 3595 * going back | |
| 3596 */ | |
| 3597 if (exclPrefixes > 0) { | |
| 3598 xsltPreprocessStylesheet(style, cur->children); | |
| 3599 for (;exclPrefixes > 0;exclPrefixes--) | |
| 3600 exclPrefixPop(style); | |
| 3601 goto skip_children; | |
| 3602 } | |
| 3603 } else if (cur->type == XML_TEXT_NODE) { | |
| 3604 if (IS_BLANK_NODE(cur)) { | |
| 3605 if (xmlNodeGetSpacePreserve(cur->parent) != 1) { | |
| 3606 deleteNode = cur; | |
| 3607 } | |
| 3608 } else if ((cur->content != NULL) && (internalize) && | |
| 3609 (!xmlDictOwns(style->dict, cur->content))) { | |
| 3610 xmlChar *tmp; | |
| 3611 | |
| 3612 /* | |
| 3613 * internalize the text string, goal is to speed | |
| 3614 * up operations and minimize used space by compiled | |
| 3615 * stylesheets. | |
| 3616 */ | |
| 3617 tmp = (xmlChar *) xmlDictLookup(style->dict, cur->content, -1); | |
| 3618 xmlNodeSetContent(cur, NULL); | |
| 3619 cur->content = tmp; | |
| 3620 } | |
| 3621 } else if ((cur->type != XML_ELEMENT_NODE) && | |
| 3622 (cur->type != XML_CDATA_SECTION_NODE)) { | |
| 3623 deleteNode = cur; | |
| 3624 goto skip_children; | |
| 3625 } | |
| 3626 | |
| 3627 /* | |
| 3628 * Skip to next node. In case of a namespaced element children of | |
| 3629 * the stylesheet and not in the XSLT namespace and not an extension | |
| 3630 * element, ignore its content. | |
| 3631 */ | |
| 3632 if ((cur->type == XML_ELEMENT_NODE) && (cur->ns != NULL) && | |
| 3633 (styleelem != NULL) && (cur->parent == styleelem) && | |
| 3634 (!xmlStrEqual(cur->ns->href, XSLT_NAMESPACE)) && | |
| 3635 (!xsltCheckExtURI(style, cur->ns->href))) { | |
| 3636 goto skip_children; | |
| 3637 } else if (cur->children != NULL) { | |
| 3638 if ((cur->children->type != XML_ENTITY_DECL) && | |
| 3639 (cur->children->type != XML_ENTITY_REF_NODE) && | |
| 3640 (cur->children->type != XML_ENTITY_NODE)) { | |
| 3641 cur = cur->children; | |
| 3642 continue; | |
| 3643 } | |
| 3644 } | |
| 3645 | |
| 3646 skip_children: | |
| 3647 if (cur->next != NULL) { | |
| 3648 cur = cur->next; | |
| 3649 continue; | |
| 3650 } | |
| 3651 do { | |
| 3652 | |
| 3653 cur = cur->parent; | |
| 3654 if (cur == NULL) | |
| 3655 break; | |
| 3656 if (cur == (xmlNodePtr) style->doc) { | |
| 3657 cur = NULL; | |
| 3658 break; | |
| 3659 } | |
| 3660 if (cur->next != NULL) { | |
| 3661 cur = cur->next; | |
| 3662 break; | |
| 3663 } | |
| 3664 } while (cur != NULL); | |
| 3665 } | |
| 3666 if (deleteNode != NULL) { | |
| 3667 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 3668 xsltGenericDebug(xsltGenericDebugContext, | |
| 3669 "xsltPreprocessStylesheet: removing ignorable blank node\n"); | |
| 3670 #endif | |
| 3671 xmlUnlinkNode(deleteNode); | |
| 3672 xmlFreeNode(deleteNode); | |
| 3673 } | |
| 3674 } | |
| 3675 #endif /* end of else XSLT_REFACTORED */ | |
| 3676 | |
| 3677 /** | |
| 3678 * xsltGatherNamespaces: | |
| 3679 * @style: the XSLT stylesheet | |
| 3680 * | |
| 3681 * Browse the stylesheet and build the namspace hash table which | |
| 3682 * will be used for XPath interpretation. If needed do a bit of normalization | |
| 3683 */ | |
| 3684 | |
| 3685 static void | |
| 3686 xsltGatherNamespaces(xsltStylesheetPtr style) { | |
| 3687 xmlNodePtr cur; | |
| 3688 const xmlChar *URI; | |
| 3689 | |
| 3690 if (style == NULL) | |
| 3691 return; | |
| 3692 /* | |
| 3693 * TODO: basically if the stylesheet uses the same prefix for different | |
| 3694 * patterns, well they may be in problem, hopefully they will get | |
| 3695 * a warning first. | |
| 3696 */ | |
| 3697 /* | |
| 3698 * TODO: Eliminate the use of the hash for XPath expressions. | |
| 3699 * An expression should be evaluated in the context of the in-scope | |
| 3700 * namespaces; eliminate the restriction of an XML document to contain | |
| 3701 * no duplicate prefixes for different namespace names. | |
| 3702 * | |
| 3703 */ | |
| 3704 cur = xmlDocGetRootElement(style->doc); | |
| 3705 while (cur != NULL) { | |
| 3706 if (cur->type == XML_ELEMENT_NODE) { | |
| 3707 xmlNsPtr ns = cur->nsDef; | |
| 3708 while (ns != NULL) { | |
| 3709 if (ns->prefix != NULL) { | |
| 3710 if (style->nsHash == NULL) { | |
| 3711 style->nsHash = xmlHashCreate(10); | |
| 3712 if (style->nsHash == NULL) { | |
| 3713 xsltTransformError(NULL, style, cur, | |
| 3714 "xsltGatherNamespaces: failed to create hash table\n"); | |
| 3715 style->errors++; | |
| 3716 return; | |
| 3717 } | |
| 3718 } | |
| 3719 URI = xmlHashLookup(style->nsHash, ns->prefix); | |
| 3720 if ((URI != NULL) && (!xmlStrEqual(URI, ns->href))) { | |
| 3721 xsltTransformError(NULL, style, cur, | |
| 3722 "Namespaces prefix %s used for multiple namespaces\n",ns->prefix); | |
| 3723 style->warnings++; | |
| 3724 } else if (URI == NULL) { | |
| 3725 xmlHashUpdateEntry(style->nsHash, ns->prefix, | |
| 3726 (void *) ns->href, (xmlHashDeallocator)xmlFree); | |
| 3727 | |
| 3728 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 3729 xsltGenericDebug(xsltGenericDebugContext, | |
| 3730 "Added namespace: %s mapped to %s\n", ns->prefix, ns->href); | |
| 3731 #endif | |
| 3732 } | |
| 3733 } | |
| 3734 ns = ns->next; | |
| 3735 } | |
| 3736 } | |
| 3737 | |
| 3738 /* | |
| 3739 * Skip to next node | |
| 3740 */ | |
| 3741 if (cur->children != NULL) { | |
| 3742 if (cur->children->type != XML_ENTITY_DECL) { | |
| 3743 cur = cur->children; | |
| 3744 continue; | |
| 3745 } | |
| 3746 } | |
| 3747 if (cur->next != NULL) { | |
| 3748 cur = cur->next; | |
| 3749 continue; | |
| 3750 } | |
| 3751 | |
| 3752 do { | |
| 3753 cur = cur->parent; | |
| 3754 if (cur == NULL) | |
| 3755 break; | |
| 3756 if (cur == (xmlNodePtr) style->doc) { | |
| 3757 cur = NULL; | |
| 3758 break; | |
| 3759 } | |
| 3760 if (cur->next != NULL) { | |
| 3761 cur = cur->next; | |
| 3762 break; | |
| 3763 } | |
| 3764 } while (cur != NULL); | |
| 3765 } | |
| 3766 } | |
| 3767 | |
| 3768 #ifdef XSLT_REFACTORED | |
| 3769 | |
| 3770 static xsltStyleType | |
| 3771 xsltGetXSLTElementTypeByNode(xsltCompilerCtxtPtr cctxt, | |
| 3772 xmlNodePtr node) | |
| 3773 { | |
| 3774 if ((node == NULL) || (node->type != XML_ELEMENT_NODE) || | |
| 3775 (node->name == NULL)) | |
| 3776 return(0); | |
| 3777 | |
| 3778 if (node->name[0] == 'a') { | |
| 3779 if (IS_XSLT_NAME(node, "apply-templates")) | |
| 3780 return(XSLT_FUNC_APPLYTEMPLATES); | |
| 3781 else if (IS_XSLT_NAME(node, "attribute")) | |
| 3782 return(XSLT_FUNC_ATTRIBUTE); | |
| 3783 else if (IS_XSLT_NAME(node, "apply-imports")) | |
| 3784 return(XSLT_FUNC_APPLYIMPORTS); | |
| 3785 else if (IS_XSLT_NAME(node, "attribute-set")) | |
| 3786 return(0); | |
| 3787 | |
| 3788 } else if (node->name[0] == 'c') { | |
| 3789 if (IS_XSLT_NAME(node, "choose")) | |
| 3790 return(XSLT_FUNC_CHOOSE); | |
| 3791 else if (IS_XSLT_NAME(node, "copy")) | |
| 3792 return(XSLT_FUNC_COPY); | |
| 3793 else if (IS_XSLT_NAME(node, "copy-of")) | |
| 3794 return(XSLT_FUNC_COPYOF); | |
| 3795 else if (IS_XSLT_NAME(node, "call-template")) | |
| 3796 return(XSLT_FUNC_CALLTEMPLATE); | |
| 3797 else if (IS_XSLT_NAME(node, "comment")) | |
| 3798 return(XSLT_FUNC_COMMENT); | |
| 3799 | |
| 3800 } else if (node->name[0] == 'd') { | |
| 3801 if (IS_XSLT_NAME(node, "document")) | |
| 3802 return(XSLT_FUNC_DOCUMENT); | |
| 3803 else if (IS_XSLT_NAME(node, "decimal-format")) | |
| 3804 return(0); | |
| 3805 | |
| 3806 } else if (node->name[0] == 'e') { | |
| 3807 if (IS_XSLT_NAME(node, "element")) | |
| 3808 return(XSLT_FUNC_ELEMENT); | |
| 3809 | |
| 3810 } else if (node->name[0] == 'f') { | |
| 3811 if (IS_XSLT_NAME(node, "for-each")) | |
| 3812 return(XSLT_FUNC_FOREACH); | |
| 3813 else if (IS_XSLT_NAME(node, "fallback")) | |
| 3814 return(XSLT_FUNC_FALLBACK); | |
| 3815 | |
| 3816 } else if (*(node->name) == 'i') { | |
| 3817 if (IS_XSLT_NAME(node, "if")) | |
| 3818 return(XSLT_FUNC_IF); | |
| 3819 else if (IS_XSLT_NAME(node, "include")) | |
| 3820 return(0); | |
| 3821 else if (IS_XSLT_NAME(node, "import")) | |
| 3822 return(0); | |
| 3823 | |
| 3824 } else if (*(node->name) == 'k') { | |
| 3825 if (IS_XSLT_NAME(node, "key")) | |
| 3826 return(0); | |
| 3827 | |
| 3828 } else if (*(node->name) == 'm') { | |
| 3829 if (IS_XSLT_NAME(node, "message")) | |
| 3830 return(XSLT_FUNC_MESSAGE); | |
| 3831 | |
| 3832 } else if (*(node->name) == 'n') { | |
| 3833 if (IS_XSLT_NAME(node, "number")) | |
| 3834 return(XSLT_FUNC_NUMBER); | |
| 3835 else if (IS_XSLT_NAME(node, "namespace-alias")) | |
| 3836 return(0); | |
| 3837 | |
| 3838 } else if (*(node->name) == 'o') { | |
| 3839 if (IS_XSLT_NAME(node, "otherwise")) | |
| 3840 return(XSLT_FUNC_OTHERWISE); | |
| 3841 else if (IS_XSLT_NAME(node, "output")) | |
| 3842 return(0); | |
| 3843 | |
| 3844 } else if (*(node->name) == 'p') { | |
| 3845 if (IS_XSLT_NAME(node, "param")) | |
| 3846 return(XSLT_FUNC_PARAM); | |
| 3847 else if (IS_XSLT_NAME(node, "processing-instruction")) | |
| 3848 return(XSLT_FUNC_PI); | |
| 3849 else if (IS_XSLT_NAME(node, "preserve-space")) | |
| 3850 return(0); | |
| 3851 | |
| 3852 } else if (*(node->name) == 's') { | |
| 3853 if (IS_XSLT_NAME(node, "sort")) | |
| 3854 return(XSLT_FUNC_SORT); | |
| 3855 else if (IS_XSLT_NAME(node, "strip-space")) | |
| 3856 return(0); | |
| 3857 else if (IS_XSLT_NAME(node, "stylesheet")) | |
| 3858 return(0); | |
| 3859 | |
| 3860 } else if (node->name[0] == 't') { | |
| 3861 if (IS_XSLT_NAME(node, "text")) | |
| 3862 return(XSLT_FUNC_TEXT); | |
| 3863 else if (IS_XSLT_NAME(node, "template")) | |
| 3864 return(0); | |
| 3865 else if (IS_XSLT_NAME(node, "transform")) | |
| 3866 return(0); | |
| 3867 | |
| 3868 } else if (*(node->name) == 'v') { | |
| 3869 if (IS_XSLT_NAME(node, "value-of")) | |
| 3870 return(XSLT_FUNC_VALUEOF); | |
| 3871 else if (IS_XSLT_NAME(node, "variable")) | |
| 3872 return(XSLT_FUNC_VARIABLE); | |
| 3873 | |
| 3874 } else if (*(node->name) == 'w') { | |
| 3875 if (IS_XSLT_NAME(node, "when")) | |
| 3876 return(XSLT_FUNC_WHEN); | |
| 3877 if (IS_XSLT_NAME(node, "with-param")) | |
| 3878 return(XSLT_FUNC_WITHPARAM); | |
| 3879 } | |
| 3880 return(0); | |
| 3881 } | |
| 3882 | |
| 3883 /** | |
| 3884 * xsltParseAnyXSLTElem: | |
| 3885 * | |
| 3886 * @cctxt: the compilation context | |
| 3887 * @elem: the element node of the XSLT instruction | |
| 3888 * | |
| 3889 * Parses, validates the content models and compiles XSLT instructions. | |
| 3890 * | |
| 3891 * Returns 0 if everything's fine; | |
| 3892 * -1 on API or internal errors. | |
| 3893 */ | |
| 3894 int | |
| 3895 xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem) | |
| 3896 { | |
| 3897 if ((cctxt == NULL) || (elem == NULL) || | |
| 3898 (elem->type != XML_ELEMENT_NODE)) | |
| 3899 return(-1); | |
| 3900 | |
| 3901 elem->psvi = NULL; | |
| 3902 | |
| 3903 if (! (IS_XSLT_ELEM_FAST(elem))) | |
| 3904 return(-1); | |
| 3905 /* | |
| 3906 * Detection of handled content of extension instructions. | |
| 3907 */ | |
| 3908 if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { | |
| 3909 cctxt->inode->extContentHandled = 1; | |
| 3910 } | |
| 3911 | |
| 3912 xsltCompilerNodePush(cctxt, elem); | |
| 3913 /* | |
| 3914 * URGENT TODO: Find a way to speed up this annoying redundant | |
| 3915 * textual node-name and namespace comparison. | |
| 3916 */ | |
| 3917 if (cctxt->inode->prev->curChildType != 0) | |
| 3918 cctxt->inode->type = cctxt->inode->prev->curChildType; | |
| 3919 else | |
| 3920 cctxt->inode->type = xsltGetXSLTElementTypeByNode(cctxt, elem); | |
| 3921 /* | |
| 3922 * Update the in-scope namespaces if needed. | |
| 3923 */ | |
| 3924 if (elem->nsDef != NULL) | |
| 3925 cctxt->inode->inScopeNs = | |
| 3926 xsltCompilerBuildInScopeNsList(cctxt, elem); | |
| 3927 /* | |
| 3928 * xsltStylePreCompute(): | |
| 3929 * This will compile the information found on the current | |
| 3930 * element's attributes. NOTE that this won't process the | |
| 3931 * children of the instruction. | |
| 3932 */ | |
| 3933 xsltStylePreCompute(cctxt->style, elem); | |
| 3934 /* | |
| 3935 * TODO: How to react on errors in xsltStylePreCompute() ? | |
| 3936 */ | |
| 3937 | |
| 3938 /* | |
| 3939 * Validate the content model of the XSLT-element. | |
| 3940 */ | |
| 3941 switch (cctxt->inode->type) { | |
| 3942 case XSLT_FUNC_APPLYIMPORTS: | |
| 3943 /* EMPTY */ | |
| 3944 goto empty_content; | |
| 3945 case XSLT_FUNC_APPLYTEMPLATES: | |
| 3946 /* <!-- Content: (xsl:sort | xsl:with-param)* --> */ | |
| 3947 goto apply_templates; | |
| 3948 case XSLT_FUNC_ATTRIBUTE: | |
| 3949 /* <!-- Content: template --> */ | |
| 3950 goto sequence_constructor; | |
| 3951 case XSLT_FUNC_CALLTEMPLATE: | |
| 3952 /* <!-- Content: xsl:with-param* --> */ | |
| 3953 goto call_template; | |
| 3954 case XSLT_FUNC_CHOOSE: | |
| 3955 /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */ | |
| 3956 goto choose; | |
| 3957 case XSLT_FUNC_COMMENT: | |
| 3958 /* <!-- Content: template --> */ | |
| 3959 goto sequence_constructor; | |
| 3960 case XSLT_FUNC_COPY: | |
| 3961 /* <!-- Content: template --> */ | |
| 3962 goto sequence_constructor; | |
| 3963 case XSLT_FUNC_COPYOF: | |
| 3964 /* EMPTY */ | |
| 3965 goto empty_content; | |
| 3966 case XSLT_FUNC_DOCUMENT: /* Extra one */ | |
| 3967 /* ?? template ?? */ | |
| 3968 goto sequence_constructor; | |
| 3969 case XSLT_FUNC_ELEMENT: | |
| 3970 /* <!-- Content: template --> */ | |
| 3971 goto sequence_constructor; | |
| 3972 case XSLT_FUNC_FALLBACK: | |
| 3973 /* <!-- Content: template --> */ | |
| 3974 goto sequence_constructor; | |
| 3975 case XSLT_FUNC_FOREACH: | |
| 3976 /* <!-- Content: (xsl:sort*, template) --> */ | |
| 3977 goto for_each; | |
| 3978 case XSLT_FUNC_IF: | |
| 3979 /* <!-- Content: template --> */ | |
| 3980 goto sequence_constructor; | |
| 3981 case XSLT_FUNC_OTHERWISE: | |
| 3982 /* <!-- Content: template --> */ | |
| 3983 goto sequence_constructor; | |
| 3984 case XSLT_FUNC_MESSAGE: | |
| 3985 /* <!-- Content: template --> */ | |
| 3986 goto sequence_constructor; | |
| 3987 case XSLT_FUNC_NUMBER: | |
| 3988 /* EMPTY */ | |
| 3989 goto empty_content; | |
| 3990 case XSLT_FUNC_PARAM: | |
| 3991 /* | |
| 3992 * Check for redefinition. | |
| 3993 */ | |
| 3994 if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) { | |
| 3995 xsltVarInfoPtr ivar = cctxt->ivar; | |
| 3996 | |
| 3997 do { | |
| 3998 if ((ivar->name == | |
| 3999 ((xsltStyleItemParamPtr) elem->psvi)->name) && | |
| 4000 (ivar->nsName == | |
| 4001 ((xsltStyleItemParamPtr) elem->psvi)->ns)) | |
| 4002 { | |
| 4003 elem->psvi = NULL; | |
| 4004 xsltTransformError(NULL, cctxt->style, elem, | |
| 4005 "Redefinition of variable or parameter '%s'.\n", | |
| 4006 ivar->name); | |
| 4007 cctxt->style->errors++; | |
| 4008 goto error; | |
| 4009 } | |
| 4010 ivar = ivar->prev; | |
| 4011 } while (ivar != NULL); | |
| 4012 } | |
| 4013 /* <!-- Content: template --> */ | |
| 4014 goto sequence_constructor; | |
| 4015 case XSLT_FUNC_PI: | |
| 4016 /* <!-- Content: template --> */ | |
| 4017 goto sequence_constructor; | |
| 4018 case XSLT_FUNC_SORT: | |
| 4019 /* EMPTY */ | |
| 4020 goto empty_content; | |
| 4021 case XSLT_FUNC_TEXT: | |
| 4022 /* <!-- Content: #PCDATA --> */ | |
| 4023 goto text; | |
| 4024 case XSLT_FUNC_VALUEOF: | |
| 4025 /* EMPTY */ | |
| 4026 goto empty_content; | |
| 4027 case XSLT_FUNC_VARIABLE: | |
| 4028 /* | |
| 4029 * Check for redefinition. | |
| 4030 */ | |
| 4031 if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) { | |
| 4032 xsltVarInfoPtr ivar = cctxt->ivar; | |
| 4033 | |
| 4034 do { | |
| 4035 if ((ivar->name == | |
| 4036 ((xsltStyleItemVariablePtr) elem->psvi)->name) && | |
| 4037 (ivar->nsName == | |
| 4038 ((xsltStyleItemVariablePtr) elem->psvi)->ns)) | |
| 4039 { | |
| 4040 elem->psvi = NULL; | |
| 4041 xsltTransformError(NULL, cctxt->style, elem, | |
| 4042 "Redefinition of variable or parameter '%s'.\n", | |
| 4043 ivar->name); | |
| 4044 cctxt->style->errors++; | |
| 4045 goto error; | |
| 4046 } | |
| 4047 ivar = ivar->prev; | |
| 4048 } while (ivar != NULL); | |
| 4049 } | |
| 4050 /* <!-- Content: template --> */ | |
| 4051 goto sequence_constructor; | |
| 4052 case XSLT_FUNC_WHEN: | |
| 4053 /* <!-- Content: template --> */ | |
| 4054 goto sequence_constructor; | |
| 4055 case XSLT_FUNC_WITHPARAM: | |
| 4056 /* <!-- Content: template --> */ | |
| 4057 goto sequence_constructor; | |
| 4058 default: | |
| 4059 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 4060 xsltGenericDebug(xsltGenericDebugContext, | |
| 4061 "xsltParseXSLTNode: Unhandled XSLT element '%s'.\n", | |
| 4062 elem->name); | |
| 4063 #endif | |
| 4064 xsltTransformError(NULL, cctxt->style, elem, | |
| 4065 "xsltParseXSLTNode: Internal error; " | |
| 4066 "unhandled XSLT element '%s'.\n", elem->name); | |
| 4067 cctxt->style->errors++; | |
| 4068 goto internal_err; | |
| 4069 } | |
| 4070 | |
| 4071 apply_templates: | |
| 4072 /* <!-- Content: (xsl:sort | xsl:with-param)* --> */ | |
| 4073 if (elem->children != NULL) { | |
| 4074 xmlNodePtr child = elem->children; | |
| 4075 do { | |
| 4076 if (child->type == XML_ELEMENT_NODE) { | |
| 4077 if (IS_XSLT_ELEM_FAST(child)) { | |
| 4078 if (xmlStrEqual(child->name, BAD_CAST "with-param")) { | |
| 4079 cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM; | |
| 4080 xsltParseAnyXSLTElem(cctxt, child); | |
| 4081 } else if (xmlStrEqual(child->name, BAD_CAST "sort")) { | |
| 4082 cctxt->inode->curChildType = XSLT_FUNC_SORT; | |
| 4083 xsltParseAnyXSLTElem(cctxt, child); | |
| 4084 } else | |
| 4085 xsltParseContentError(cctxt->style, child); | |
| 4086 } else | |
| 4087 xsltParseContentError(cctxt->style, child); | |
| 4088 } | |
| 4089 child = child->next; | |
| 4090 } while (child != NULL); | |
| 4091 } | |
| 4092 goto exit; | |
| 4093 | |
| 4094 call_template: | |
| 4095 /* <!-- Content: xsl:with-param* --> */ | |
| 4096 if (elem->children != NULL) { | |
| 4097 xmlNodePtr child = elem->children; | |
| 4098 do { | |
| 4099 if (child->type == XML_ELEMENT_NODE) { | |
| 4100 if (IS_XSLT_ELEM_FAST(child)) { | |
| 4101 xsltStyleType type; | |
| 4102 | |
| 4103 type = xsltGetXSLTElementTypeByNode(cctxt, child); | |
| 4104 if (type == XSLT_FUNC_WITHPARAM) { | |
| 4105 cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM; | |
| 4106 xsltParseAnyXSLTElem(cctxt, child); | |
| 4107 } else { | |
| 4108 xsltParseContentError(cctxt->style, child); | |
| 4109 } | |
| 4110 } else | |
| 4111 xsltParseContentError(cctxt->style, child); | |
| 4112 } | |
| 4113 child = child->next; | |
| 4114 } while (child != NULL); | |
| 4115 } | |
| 4116 goto exit; | |
| 4117 | |
| 4118 text: | |
| 4119 if (elem->children != NULL) { | |
| 4120 xmlNodePtr child = elem->children; | |
| 4121 do { | |
| 4122 if ((child->type != XML_TEXT_NODE) && | |
| 4123 (child->type != XML_CDATA_SECTION_NODE)) | |
| 4124 { | |
| 4125 xsltTransformError(NULL, cctxt->style, elem, | |
| 4126 "The XSLT 'text' element must have only character " | |
| 4127 "data as content.\n"); | |
| 4128 } | |
| 4129 child = child->next; | |
| 4130 } while (child != NULL); | |
| 4131 } | |
| 4132 goto exit; | |
| 4133 | |
| 4134 empty_content: | |
| 4135 if (elem->children != NULL) { | |
| 4136 xmlNodePtr child = elem->children; | |
| 4137 /* | |
| 4138 * Relaxed behaviour: we will allow whitespace-only text-nodes. | |
| 4139 */ | |
| 4140 do { | |
| 4141 if (((child->type != XML_TEXT_NODE) && | |
| 4142 (child->type != XML_CDATA_SECTION_NODE)) || | |
| 4143 (! IS_BLANK_NODE(child))) | |
| 4144 { | |
| 4145 xsltTransformError(NULL, cctxt->style, elem, | |
| 4146 "This XSLT element must have no content.\n"); | |
| 4147 cctxt->style->errors++; | |
| 4148 break; | |
| 4149 } | |
| 4150 child = child->next; | |
| 4151 } while (child != NULL); | |
| 4152 } | |
| 4153 goto exit; | |
| 4154 | |
| 4155 choose: | |
| 4156 /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */ | |
| 4157 /* | |
| 4158 * TODO: text-nodes in between are *not* allowed in XSLT 1.0. | |
| 4159 * The old behaviour did not check this. | |
| 4160 * NOTE: In XSLT 2.0 they are stripped beforehand | |
| 4161 * if whitespace-only (regardless of xml:space). | |
| 4162 */ | |
| 4163 if (elem->children != NULL) { | |
| 4164 xmlNodePtr child = elem->children; | |
| 4165 int nbWhen = 0, nbOtherwise = 0, err = 0; | |
| 4166 do { | |
| 4167 if (child->type == XML_ELEMENT_NODE) { | |
| 4168 if (IS_XSLT_ELEM_FAST(child)) { | |
| 4169 xsltStyleType type; | |
| 4170 | |
| 4171 type = xsltGetXSLTElementTypeByNode(cctxt, child); | |
| 4172 if (type == XSLT_FUNC_WHEN) { | |
| 4173 nbWhen++; | |
| 4174 if (nbOtherwise) { | |
| 4175 xsltParseContentError(cctxt->style, child); | |
| 4176 err = 1; | |
| 4177 break; | |
| 4178 } | |
| 4179 cctxt->inode->curChildType = XSLT_FUNC_WHEN; | |
| 4180 xsltParseAnyXSLTElem(cctxt, child); | |
| 4181 } else if (type == XSLT_FUNC_OTHERWISE) { | |
| 4182 if (! nbWhen) { | |
| 4183 xsltParseContentError(cctxt->style, child); | |
| 4184 err = 1; | |
| 4185 break; | |
| 4186 } | |
| 4187 if (nbOtherwise) { | |
| 4188 xsltTransformError(NULL, cctxt->style, elem, | |
| 4189 "The XSLT 'choose' element must not contain " | |
| 4190 "more than one XSLT 'otherwise' element.\n"); | |
| 4191 cctxt->style->errors++; | |
| 4192 err = 1; | |
| 4193 break; | |
| 4194 } | |
| 4195 nbOtherwise++; | |
| 4196 cctxt->inode->curChildType = XSLT_FUNC_OTHERWISE; | |
| 4197 xsltParseAnyXSLTElem(cctxt, child); | |
| 4198 } else | |
| 4199 xsltParseContentError(cctxt->style, child); | |
| 4200 } else | |
| 4201 xsltParseContentError(cctxt->style, child); | |
| 4202 } | |
| 4203 /* | |
| 4204 else | |
| 4205 xsltParseContentError(cctxt, child); | |
| 4206 */ | |
| 4207 child = child->next; | |
| 4208 } while (child != NULL); | |
| 4209 if ((! err) && (! nbWhen)) { | |
| 4210 xsltTransformError(NULL, cctxt->style, elem, | |
| 4211 "The XSLT element 'choose' must contain at least one " | |
| 4212 "XSLT element 'when'.\n"); | |
| 4213 cctxt->style->errors++; | |
| 4214 } | |
| 4215 } | |
| 4216 goto exit; | |
| 4217 | |
| 4218 for_each: | |
| 4219 /* <!-- Content: (xsl:sort*, template) --> */ | |
| 4220 /* | |
| 4221 * NOTE: Text-nodes before xsl:sort are *not* allowed in XSLT 1.0. | |
| 4222 * The old behaviour did not allow this, but it catched this | |
| 4223 * only at transformation-time. | |
| 4224 * In XSLT 2.0 they are stripped beforehand if whitespace-only | |
| 4225 * (regardless of xml:space). | |
| 4226 */ | |
| 4227 if (elem->children != NULL) { | |
| 4228 xmlNodePtr child = elem->children; | |
| 4229 /* | |
| 4230 * Parse xsl:sort first. | |
| 4231 */ | |
| 4232 do { | |
| 4233 if ((child->type == XML_ELEMENT_NODE) && | |
| 4234 IS_XSLT_ELEM_FAST(child)) | |
| 4235 { | |
| 4236 if (xsltGetXSLTElementTypeByNode(cctxt, child) == | |
| 4237 XSLT_FUNC_SORT) | |
| 4238 { | |
| 4239 cctxt->inode->curChildType = XSLT_FUNC_SORT; | |
| 4240 xsltParseAnyXSLTElem(cctxt, child); | |
| 4241 } else | |
| 4242 break; | |
| 4243 } else | |
| 4244 break; | |
| 4245 child = child->next; | |
| 4246 } while (child != NULL); | |
| 4247 /* | |
| 4248 * Parse the sequece constructor. | |
| 4249 */ | |
| 4250 if (child != NULL) | |
| 4251 xsltParseSequenceConstructor(cctxt, child); | |
| 4252 } | |
| 4253 goto exit; | |
| 4254 | |
| 4255 sequence_constructor: | |
| 4256 /* | |
| 4257 * Parse the sequence constructor. | |
| 4258 */ | |
| 4259 if (elem->children != NULL) | |
| 4260 xsltParseSequenceConstructor(cctxt, elem->children); | |
| 4261 | |
| 4262 /* | |
| 4263 * Register information for vars/params. Only needed if there | |
| 4264 * are any following siblings. | |
| 4265 */ | |
| 4266 if ((elem->next != NULL) && | |
| 4267 ((cctxt->inode->type == XSLT_FUNC_VARIABLE) || | |
| 4268 (cctxt->inode->type == XSLT_FUNC_PARAM))) | |
| 4269 { | |
| 4270 if ((elem->psvi != NULL) && | |
| 4271 (((xsltStyleBasicItemVariablePtr) elem->psvi)->name)) | |
| 4272 { | |
| 4273 xsltCompilerVarInfoPush(cctxt, elem, | |
| 4274 ((xsltStyleBasicItemVariablePtr) elem->psvi)->name, | |
| 4275 ((xsltStyleBasicItemVariablePtr) elem->psvi)->ns); | |
| 4276 } | |
| 4277 } | |
| 4278 | |
| 4279 error: | |
| 4280 exit: | |
| 4281 xsltCompilerNodePop(cctxt, elem); | |
| 4282 return(0); | |
| 4283 | |
| 4284 internal_err: | |
| 4285 xsltCompilerNodePop(cctxt, elem); | |
| 4286 return(-1); | |
| 4287 } | |
| 4288 | |
| 4289 /** | |
| 4290 * xsltForwardsCompatUnkownItemCreate: | |
| 4291 * | |
| 4292 * @cctxt: the compilation context | |
| 4293 * | |
| 4294 * Creates a compiled representation of the unknown | |
| 4295 * XSLT instruction. | |
| 4296 * | |
| 4297 * Returns the compiled representation. | |
| 4298 */ | |
| 4299 static xsltStyleItemUknownPtr | |
| 4300 xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt) | |
| 4301 { | |
| 4302 xsltStyleItemUknownPtr item; | |
| 4303 | |
| 4304 item = (xsltStyleItemUknownPtr) xmlMalloc(sizeof(xsltStyleItemUknown)); | |
| 4305 if (item == NULL) { | |
| 4306 xsltTransformError(NULL, cctxt->style, NULL, | |
| 4307 "Internal error in xsltForwardsCompatUnkownItemCreate(): " | |
| 4308 "Failed to allocate memory.\n"); | |
| 4309 cctxt->style->errors++; | |
| 4310 return(NULL); | |
| 4311 } | |
| 4312 memset(item, 0, sizeof(xsltStyleItemUknown)); | |
| 4313 item->type = XSLT_FUNC_UNKOWN_FORWARDS_COMPAT; | |
| 4314 /* | |
| 4315 * Store it in the stylesheet. | |
| 4316 */ | |
| 4317 item->next = cctxt->style->preComps; | |
| 4318 cctxt->style->preComps = (xsltElemPreCompPtr) item; | |
| 4319 return(item); | |
| 4320 } | |
| 4321 | |
| 4322 /** | |
| 4323 * xsltParseUnknownXSLTElem: | |
| 4324 * | |
| 4325 * @cctxt: the compilation context | |
| 4326 * @node: the element of the unknown XSLT instruction | |
| 4327 * | |
| 4328 * Parses an unknown XSLT element. | |
| 4329 * If forwards compatible mode is enabled this will allow | |
| 4330 * such an unknown XSLT and; otherwise it is rejected. | |
| 4331 * | |
| 4332 * Returns 1 in the unknown XSLT instruction is rejected, | |
| 4333 * 0 if everything's fine and | |
| 4334 * -1 on API or internal errors. | |
| 4335 */ | |
| 4336 static int | |
| 4337 xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt, | |
| 4338 xmlNodePtr node) | |
| 4339 { | |
| 4340 if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE)) | |
| 4341 return(-1); | |
| 4342 | |
| 4343 /* | |
| 4344 * Detection of handled content of extension instructions. | |
| 4345 */ | |
| 4346 if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { | |
| 4347 cctxt->inode->extContentHandled = 1; | |
| 4348 } | |
| 4349 if (cctxt->inode->forwardsCompat == 0) { | |
| 4350 /* | |
| 4351 * We are not in forwards-compatible mode, so raise an error. | |
| 4352 */ | |
| 4353 xsltTransformError(NULL, cctxt->style, node, | |
| 4354 "Unknown XSLT element '%s'.\n", node->name); | |
| 4355 cctxt->style->errors++; | |
| 4356 return(1); | |
| 4357 } | |
| 4358 /* | |
| 4359 * Forwards-compatible mode. | |
| 4360 * ------------------------ | |
| 4361 * | |
| 4362 * Parse/compile xsl:fallback elements. | |
| 4363 * | |
| 4364 * QUESTION: Do we have to raise an error if there's no xsl:fallback? | |
| 4365 * ANSWER: No, since in the stylesheet the fallback behaviour might | |
| 4366 * also be provided by using the XSLT function "element-available". | |
| 4367 */ | |
| 4368 if (cctxt->unknownItem == NULL) { | |
| 4369 /* | |
| 4370 * Create a singleton for all unknown XSLT instructions. | |
| 4371 */ | |
| 4372 cctxt->unknownItem = xsltForwardsCompatUnkownItemCreate(cctxt); | |
| 4373 if (cctxt->unknownItem == NULL) { | |
| 4374 node->psvi = NULL; | |
| 4375 return(-1); | |
| 4376 } | |
| 4377 } | |
| 4378 node->psvi = cctxt->unknownItem; | |
| 4379 if (node->children == NULL) | |
| 4380 return(0); | |
| 4381 else { | |
| 4382 xmlNodePtr child = node->children; | |
| 4383 | |
| 4384 xsltCompilerNodePush(cctxt, node); | |
| 4385 /* | |
| 4386 * Update the in-scope namespaces if needed. | |
| 4387 */ | |
| 4388 if (node->nsDef != NULL) | |
| 4389 cctxt->inode->inScopeNs = | |
| 4390 xsltCompilerBuildInScopeNsList(cctxt, node); | |
| 4391 /* | |
| 4392 * Parse all xsl:fallback children. | |
| 4393 */ | |
| 4394 do { | |
| 4395 if ((child->type == XML_ELEMENT_NODE) && | |
| 4396 IS_XSLT_ELEM_FAST(child) && | |
| 4397 IS_XSLT_NAME(child, "fallback")) | |
| 4398 { | |
| 4399 cctxt->inode->curChildType = XSLT_FUNC_FALLBACK; | |
| 4400 xsltParseAnyXSLTElem(cctxt, child); | |
| 4401 } | |
| 4402 child = child->next; | |
| 4403 } while (child != NULL); | |
| 4404 | |
| 4405 xsltCompilerNodePop(cctxt, node); | |
| 4406 } | |
| 4407 return(0); | |
| 4408 } | |
| 4409 | |
| 4410 /** | |
| 4411 * xsltParseSequenceConstructor: | |
| 4412 * | |
| 4413 * @cctxt: the compilation context | |
| 4414 * @cur: the start-node of the content to be parsed | |
| 4415 * | |
| 4416 * Parses a "template" content (or "sequence constructor" in XSLT 2.0 terms). | |
| 4417 * This will additionally remove xsl:text elements from the tree. | |
| 4418 */ | |
| 4419 void | |
| 4420 xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) | |
| 4421 { | |
| 4422 xsltStyleType type; | |
| 4423 xmlNodePtr deleteNode = NULL; | |
| 4424 | |
| 4425 if (cctxt == NULL) { | |
| 4426 xmlGenericError(xmlGenericErrorContext, | |
| 4427 "xsltParseSequenceConstructor: Bad arguments\n"); | |
| 4428 cctxt->style->errors++; | |
| 4429 return; | |
| 4430 } | |
| 4431 /* | |
| 4432 * Detection of handled content of extension instructions. | |
| 4433 */ | |
| 4434 if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { | |
| 4435 cctxt->inode->extContentHandled = 1; | |
| 4436 } | |
| 4437 if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) | |
| 4438 return; | |
| 4439 /* | |
| 4440 * This is the content reffered to as a "template". | |
| 4441 * E.g. an xsl:element has such content model: | |
| 4442 * <xsl:element | |
| 4443 * name = { qname } | |
| 4444 * namespace = { uri-reference } | |
| 4445 * use-attribute-sets = qnames> | |
| 4446 * <!-- Content: template --> | |
| 4447 * | |
| 4448 * NOTE that in XSLT-2 the term "template" was abandoned due to | |
| 4449 * confusion with xsl:template and the term "sequence constructor" | |
| 4450 * was introduced instead. | |
| 4451 * | |
| 4452 * The following XSLT-instructions are allowed to appear: | |
| 4453 * xsl:apply-templates, xsl:call-template, xsl:apply-imports, | |
| 4454 * xsl:for-each, xsl:value-of, xsl:copy-of, xsl:number, | |
| 4455 * xsl:choose, xsl:if, xsl:text, xsl:copy, xsl:variable, | |
| 4456 * xsl:message, xsl:fallback, | |
| 4457 * xsl:processing-instruction, xsl:comment, xsl:element | |
| 4458 * xsl:attribute. | |
| 4459 * Additional allowed content: | |
| 4460 * 1) extension instructions | |
| 4461 * 2) literal result elements | |
| 4462 * 3) PCDATA | |
| 4463 * | |
| 4464 * NOTE that this content model does *not* allow xsl:param. | |
| 4465 */ | |
| 4466 while (cur != NULL) { | |
| 4467 if (deleteNode != NULL) { | |
| 4468 #ifdef WITH_XSLT_DEBUG_BLANKS | |
| 4469 xsltGenericDebug(xsltGenericDebugContext, | |
| 4470 "xsltParseSequenceConstructor: removing xsl:text element\n"); | |
| 4471 #endif | |
| 4472 xmlUnlinkNode(deleteNode); | |
| 4473 xmlFreeNode(deleteNode); | |
| 4474 deleteNode = NULL; | |
| 4475 } | |
| 4476 if (cur->type == XML_ELEMENT_NODE) { | |
| 4477 | |
| 4478 if (cur->psvi == xsltXSLTTextMarker) { | |
| 4479 /* | |
| 4480 * xsl:text elements | |
| 4481 * -------------------------------------------------------- | |
| 4482 */ | |
| 4483 xmlNodePtr tmp; | |
| 4484 | |
| 4485 cur->psvi = NULL; | |
| 4486 /* | |
| 4487 * Mark the xsl:text element for later deletion. | |
| 4488 */ | |
| 4489 deleteNode = cur; | |
| 4490 /* | |
| 4491 * Validate content. | |
| 4492 */ | |
| 4493 tmp = cur->children; | |
| 4494 if (tmp) { | |
| 4495 /* | |
| 4496 * We don't expect more than one text-node in the | |
| 4497 * content, since we already merged adjacent | |
| 4498 * text/CDATA-nodes and eliminated PI/comment-nodes. | |
| 4499 */ | |
| 4500 if ((tmp->type == XML_TEXT_NODE) || | |
| 4501 (tmp->next == NULL)) | |
| 4502 { | |
| 4503 /* | |
| 4504 * Leave the contained text-node in the tree. | |
| 4505 */ | |
| 4506 xmlUnlinkNode(tmp); | |
| 4507 xmlAddPrevSibling(cur, tmp); | |
| 4508 } else { | |
| 4509 tmp = NULL; | |
| 4510 xsltTransformError(NULL, cctxt->style, cur, | |
| 4511 "Element 'xsl:text': Invalid type " | |
| 4512 "of node found in content.\n"); | |
| 4513 cctxt->style->errors++; | |
| 4514 } | |
| 4515 } | |
| 4516 if (cur->properties) { | |
| 4517 xmlAttrPtr attr; | |
| 4518 /* | |
| 4519 * TODO: We need to report errors for | |
| 4520 * invalid attrs. | |
| 4521 */ | |
| 4522 attr = cur->properties; | |
| 4523 do { | |
| 4524 if ((attr->ns == NULL) && | |
| 4525 (attr->name != NULL) && | |
| 4526 (attr->name[0] == 'd') && | |
| 4527 xmlStrEqual(attr->name, | |
| 4528 BAD_CAST "disable-output-escaping")) | |
| 4529 { | |
| 4530 /* | |
| 4531 * Attr "disable-output-escaping". | |
| 4532 * XSLT-2: This attribute is deprecated. | |
| 4533 */ | |
| 4534 if ((attr->children != NULL) && | |
| 4535 xmlStrEqual(attr->children->content, | |
| 4536 BAD_CAST "yes")) | |
| 4537 { | |
| 4538 /* | |
| 4539 * Disable output escaping for this | |
| 4540 * text node. | |
| 4541 */ | |
| 4542 if (tmp) | |
| 4543 tmp->name = xmlStringTextNoenc; | |
| 4544 } else if ((attr->children == NULL) || | |
| 4545 (attr->children->content == NULL) || | |
| 4546 (!xmlStrEqual(attr->children->content, | |
| 4547 BAD_CAST "no"))) | |
| 4548 { | |
| 4549 xsltTransformError(NULL, cctxt->style, | |
| 4550 cur, | |
| 4551 "Attribute 'disable-output-escaping': " | |
| 4552 "Invalid value. Expected is " | |
| 4553 "'yes' or 'no'.\n"); | |
| 4554 cctxt->style->errors++; | |
| 4555 } | |
| 4556 break; | |
| 4557 } | |
| 4558 attr = attr->next; | |
| 4559 } while (attr != NULL); | |
| 4560 } | |
| 4561 } else if (IS_XSLT_ELEM_FAST(cur)) { | |
| 4562 /* | |
| 4563 * TODO: Using the XSLT-marker is still not stable yet. | |
| 4564 */ | |
| 4565 /* if (cur->psvi == xsltXSLTElemMarker) { */ | |
| 4566 /* | |
| 4567 * XSLT instructions | |
| 4568 * -------------------------------------------------------- | |
| 4569 */ | |
| 4570 cur->psvi = NULL; | |
| 4571 type = xsltGetXSLTElementTypeByNode(cctxt, cur); | |
| 4572 switch (type) { | |
| 4573 case XSLT_FUNC_APPLYIMPORTS: | |
| 4574 case XSLT_FUNC_APPLYTEMPLATES: | |
| 4575 case XSLT_FUNC_ATTRIBUTE: | |
| 4576 case XSLT_FUNC_CALLTEMPLATE: | |
| 4577 case XSLT_FUNC_CHOOSE: | |
| 4578 case XSLT_FUNC_COMMENT: | |
| 4579 case XSLT_FUNC_COPY: | |
| 4580 case XSLT_FUNC_COPYOF: | |
| 4581 case XSLT_FUNC_DOCUMENT: /* Extra one */ | |
| 4582 case XSLT_FUNC_ELEMENT: | |
| 4583 case XSLT_FUNC_FALLBACK: | |
| 4584 case XSLT_FUNC_FOREACH: | |
| 4585 case XSLT_FUNC_IF: | |
| 4586 case XSLT_FUNC_MESSAGE: | |
| 4587 case XSLT_FUNC_NUMBER: | |
| 4588 case XSLT_FUNC_PI: | |
| 4589 case XSLT_FUNC_TEXT: | |
| 4590 case XSLT_FUNC_VALUEOF: | |
| 4591 case XSLT_FUNC_VARIABLE: | |
| 4592 /* | |
| 4593 * Parse the XSLT element. | |
| 4594 */ | |
| 4595 cctxt->inode->curChildType = type; | |
| 4596 xsltParseAnyXSLTElem(cctxt, cur); | |
| 4597 break; | |
| 4598 default: | |
| 4599 xsltParseUnknownXSLTElem(cctxt, cur); | |
| 4600 cur = cur->next; | |
| 4601 continue; | |
| 4602 } | |
| 4603 } else { | |
| 4604 /* | |
| 4605 * Non-XSLT elements | |
| 4606 * ----------------- | |
| 4607 */ | |
| 4608 xsltCompilerNodePush(cctxt, cur); | |
| 4609 /* | |
| 4610 * Update the in-scope namespaces if needed. | |
| 4611 */ | |
| 4612 if (cur->nsDef != NULL) | |
| 4613 cctxt->inode->inScopeNs = | |
| 4614 xsltCompilerBuildInScopeNsList(cctxt, cur); | |
| 4615 /* | |
| 4616 * The current element is either a literal result element | |
| 4617 * or an extension instruction. | |
| 4618 * | |
| 4619 * Process attr "xsl:extension-element-prefixes". | |
| 4620 * FUTURE TODO: IIRC in XSLT 2.0 this attribute must be | |
| 4621 * processed by the implementor of the extension function; | |
| 4622 * i.e., it won't be handled by the XSLT processor. | |
| 4623 */ | |
| 4624 /* SPEC 1.0: | |
| 4625 * "exclude-result-prefixes" is only allowed on literal | |
| 4626 * result elements and "xsl:exclude-result-prefixes" | |
| 4627 * on xsl:stylesheet/xsl:transform. | |
| 4628 * SPEC 2.0: | |
| 4629 * "There are a number of standard attributes | |
| 4630 * that may appear on any XSLT element: specifically | |
| 4631 * version, exclude-result-prefixes, | |
| 4632 * extension-element-prefixes, xpath-default-namespace, | |
| 4633 * default-collation, and use-when." | |
| 4634 * | |
| 4635 * SPEC 2.0: | |
| 4636 * For literal result elements: | |
| 4637 * "xsl:version, xsl:exclude-result-prefixes, | |
| 4638 * xsl:extension-element-prefixes, | |
| 4639 * xsl:xpath-default-namespace, | |
| 4640 * xsl:default-collation, or xsl:use-when." | |
| 4641 */ | |
| 4642 if (cur->properties) | |
| 4643 cctxt->inode->extElemNs = | |
| 4644 xsltParseExtElemPrefixes(cctxt, | |
| 4645 cur, cctxt->inode->extElemNs, | |
| 4646 XSLT_ELEMENT_CATEGORY_LRE); | |
| 4647 /* | |
| 4648 * Eval if we have an extension instruction here. | |
| 4649 */ | |
| 4650 if ((cur->ns != NULL) && | |
| 4651 (cctxt->inode->extElemNs != NULL) && | |
| 4652 (xsltCheckExtPrefix(cctxt->style, cur->ns->href) == 1)) | |
| 4653 { | |
| 4654 /* | |
| 4655 * Extension instructions | |
| 4656 * ---------------------------------------------------- | |
| 4657 * Mark the node information. | |
| 4658 */ | |
| 4659 cctxt->inode->category = XSLT_ELEMENT_CATEGORY_EXTENSION; | |
| 4660 cctxt->inode->extContentHandled = 0; | |
| 4661 if (cur->psvi != NULL) { | |
| 4662 cur->psvi = NULL; | |
| 4663 /* | |
| 4664 * TODO: Temporary sanity check. | |
| 4665 */ | |
| 4666 xsltTransformError(NULL, cctxt->style, cur, | |
| 4667 "Internal error in xsltParseSequenceConstructor(): " | |
| 4668 "Occupied PSVI field.\n"); | |
| 4669 cctxt->style->errors++; | |
| 4670 cur = cur->next; | |
| 4671 continue; | |
| 4672 } | |
| 4673 cur->psvi = (void *) | |
| 4674 xsltPreComputeExtModuleElement(cctxt->style, cur); | |
| 4675 | |
| 4676 if (cur->psvi == NULL) { | |
| 4677 /* | |
| 4678 * OLD COMMENT: "Unknown element, maybe registered | |
| 4679 * at the context level. Mark it for later | |
| 4680 * recognition." | |
| 4681 * QUESTION: What does the xsltExtMarker mean? | |
| 4682 * ANSWER: It is used in | |
| 4683 * xsltApplySequenceConstructor() at | |
| 4684 * transformation-time to look out for extension | |
| 4685 * registered in the transformation context. | |
| 4686 */ | |
| 4687 cur->psvi = (void *) xsltExtMarker; | |
| 4688 } | |
| 4689 /* | |
| 4690 * BIG NOTE: Now the ugly part. In previous versions | |
| 4691 * of Libxslt (until 1.1.16), all the content of an | |
| 4692 * extension instruction was processed and compiled without | |
| 4693 * the need of the extension-author to explicitely call | |
| 4694 * such a processing;.We now need to mimic this old | |
| 4695 * behaviour in order to avoid breaking old code | |
| 4696 * on the extension-author's side. | |
| 4697 * The mechanism: | |
| 4698 * 1) If the author does *not* set the | |
| 4699 * compile-time-flag @extContentHandled, then we'll | |
| 4700 * parse the content assuming that it's a "template" | |
| 4701 * (or "sequence constructor in XSLT 2.0 terms). | |
| 4702 * NOTE: If the extension is registered at | |
| 4703 * transformation-time only, then there's no way of | |
| 4704 * knowing that content shall be valid, and we'll | |
| 4705 * process the content the same way. | |
| 4706 * 2) If the author *does* set the flag, then we'll assume | |
| 4707 * that the author has handled the parsing him/herself | |
| 4708 * (e.g. called xsltParseSequenceConstructor(), etc. | |
| 4709 * explicitely in his/her code). | |
| 4710 */ | |
| 4711 if ((cur->children != NULL) && | |
| 4712 (cctxt->inode->extContentHandled == 0)) | |
| 4713 { | |
| 4714 /* | |
| 4715 * Default parsing of the content using the | |
| 4716 * sequence-constructor model. | |
| 4717 */ | |
| 4718 xsltParseSequenceConstructor(cctxt, cur->children); | |
| 4719 } | |
| 4720 } else { | |
| 4721 /* | |
| 4722 * Literal result element | |
| 4723 * ---------------------------------------------------- | |
| 4724 * Allowed XSLT attributes: | |
| 4725 * xsl:extension-element-prefixes CDATA #IMPLIED | |
| 4726 * xsl:exclude-result-prefixes CDATA #IMPLIED | |
| 4727 * TODO: xsl:use-attribute-sets %qnames; #IMPLIED | |
| 4728 * xsl:version NMTOKEN #IMPLIED | |
| 4729 */ | |
| 4730 cur->psvi = NULL; | |
| 4731 cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LRE; | |
| 4732 if (cur->properties != NULL) { | |
| 4733 xmlAttrPtr attr = cur->properties; | |
| 4734 /* | |
| 4735 * Attribute "xsl:exclude-result-prefixes". | |
| 4736 */ | |
| 4737 cctxt->inode->exclResultNs = | |
| 4738 xsltParseExclResultPrefixes(cctxt, cur, | |
| 4739 cctxt->inode->exclResultNs, | |
| 4740 XSLT_ELEMENT_CATEGORY_LRE); | |
| 4741 /* | |
| 4742 * Attribute "xsl:version". | |
| 4743 */ | |
| 4744 xsltParseAttrXSLTVersion(cctxt, cur, | |
| 4745 XSLT_ELEMENT_CATEGORY_LRE); | |
| 4746 /* | |
| 4747 * Report invalid XSLT attributes. | |
| 4748 * For XSLT 1.0 only xsl:use-attribute-sets is allowed | |
| 4749 * next to xsl:version, xsl:exclude-result-prefixes and | |
| 4750 * xsl:extension-element-prefixes. | |
| 4751 * | |
| 4752 * Mark all XSLT attributes, in order to skip such | |
| 4753 * attributes when instantiating the LRE. | |
| 4754 */ | |
| 4755 do { | |
| 4756 if ((attr->psvi != xsltXSLTAttrMarker) && | |
| 4757 IS_XSLT_ATTR_FAST(attr)) | |
| 4758 { | |
| 4759 if (! xmlStrEqual(attr->name, | |
| 4760 BAD_CAST "use-attribute-sets")) | |
| 4761 { | |
| 4762 xsltTransformError(NULL, cctxt->style, | |
| 4763 cur, | |
| 4764 "Unknown XSLT attribute '%s'.\n", | |
| 4765 attr->name); | |
| 4766 cctxt->style->errors++; | |
| 4767 } else { | |
| 4768 /* | |
| 4769 * XSLT attr marker. | |
| 4770 */ | |
| 4771 attr->psvi = (void *) xsltXSLTAttrMarker; | |
| 4772 } | |
| 4773 } | |
| 4774 attr = attr->next; | |
| 4775 } while (attr != NULL); | |
| 4776 } | |
| 4777 /* | |
| 4778 * Create/reuse info for the literal result element. | |
| 4779 */ | |
| 4780 if (cctxt->inode->nsChanged) | |
| 4781 xsltLREInfoCreate(cctxt, cur, 1); | |
| 4782 cur->psvi = cctxt->inode->litResElemInfo; | |
| 4783 /* | |
| 4784 * Apply ns-aliasing on the element and on its attributes. | |
| 4785 */ | |
| 4786 if (cctxt->hasNsAliases) | |
| 4787 xsltLREBuildEffectiveNs(cctxt, cur); | |
| 4788 /* | |
| 4789 * Compile attribute value templates (AVT). | |
| 4790 */ | |
| 4791 if (cur->properties) { | |
| 4792 xmlAttrPtr attr = cur->properties; | |
| 4793 | |
| 4794 while (attr != NULL) { | |
| 4795 xsltCompileAttr(cctxt->style, attr); | |
| 4796 attr = attr->next; | |
| 4797 } | |
| 4798 } | |
| 4799 /* | |
| 4800 * Parse the content, which is defined to be a "template" | |
| 4801 * (or "sequence constructor" in XSLT 2.0 terms). | |
| 4802 */ | |
| 4803 if (cur->children != NULL) { | |
| 4804 xsltParseSequenceConstructor(cctxt, cur->children); | |
| 4805 } | |
| 4806 } | |
| 4807 /* | |
| 4808 * Leave the non-XSLT element. | |
| 4809 */ | |
| 4810 xsltCompilerNodePop(cctxt, cur); | |
| 4811 } | |
| 4812 } | |
| 4813 cur = cur->next; | |
| 4814 } | |
| 4815 if (deleteNode != NULL) { | |
| 4816 #ifdef WITH_XSLT_DEBUG_BLANKS | |
| 4817 xsltGenericDebug(xsltGenericDebugContext, | |
| 4818 "xsltParseSequenceConstructor: removing xsl:text element\n"); | |
| 4819 #endif | |
| 4820 xmlUnlinkNode(deleteNode); | |
| 4821 xmlFreeNode(deleteNode); | |
| 4822 deleteNode = NULL; | |
| 4823 } | |
| 4824 } | |
| 4825 | |
| 4826 /** | |
| 4827 * xsltParseTemplateContent: | |
| 4828 * @style: the XSLT stylesheet | |
| 4829 * @templ: the node containing the content to be parsed | |
| 4830 * | |
| 4831 * Parses and compiles the content-model of an xsl:template element. | |
| 4832 * Note that this is *not* the "template" content model (or "sequence | |
| 4833 * constructor" in XSLT 2.0); it it allows addional xsl:param | |
| 4834 * elements as immediate children of @templ. | |
| 4835 * | |
| 4836 * Called by: | |
| 4837 * exsltFuncFunctionComp() (EXSLT, functions.c) | |
| 4838 * So this is intended to be called from extension functions. | |
| 4839 */ | |
| 4840 void | |
| 4841 xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) { | |
| 4842 if ((style == NULL) || (templ == NULL) || | |
| 4843 (templ->type == XML_NAMESPACE_DECL)) | |
| 4844 return; | |
| 4845 | |
| 4846 /* | |
| 4847 * Detection of handled content of extension instructions. | |
| 4848 */ | |
| 4849 if (XSLT_CCTXT(style)->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { | |
| 4850 XSLT_CCTXT(style)->inode->extContentHandled = 1; | |
| 4851 } | |
| 4852 | |
| 4853 if (templ->children != NULL) { | |
| 4854 xmlNodePtr child = templ->children; | |
| 4855 /* | |
| 4856 * Process xsl:param elements, which can only occur as the | |
| 4857 * immediate children of xsl:template (well, and of any | |
| 4858 * user-defined extension instruction if needed). | |
| 4859 */ | |
| 4860 do { | |
| 4861 if ((child->type == XML_ELEMENT_NODE) && | |
| 4862 IS_XSLT_ELEM_FAST(child) && | |
| 4863 IS_XSLT_NAME(child, "param")) | |
| 4864 { | |
| 4865 XSLT_CCTXT(style)->inode->curChildType = XSLT_FUNC_PARAM; | |
| 4866 xsltParseAnyXSLTElem(XSLT_CCTXT(style), child); | |
| 4867 } else | |
| 4868 break; | |
| 4869 child = child->next; | |
| 4870 } while (child != NULL); | |
| 4871 /* | |
| 4872 * Parse the content and register the pattern. | |
| 4873 */ | |
| 4874 xsltParseSequenceConstructor(XSLT_CCTXT(style), child); | |
| 4875 } | |
| 4876 } | |
| 4877 | |
| 4878 #else /* XSLT_REFACTORED */ | |
| 4879 | |
| 4880 /** | |
| 4881 * xsltParseTemplateContent: | |
| 4882 * @style: the XSLT stylesheet | |
| 4883 * @templ: the container node (can be a document for literal results) | |
| 4884 * | |
| 4885 * parse a template content-model | |
| 4886 * Clean-up the template content from unwanted ignorable blank nodes | |
| 4887 * and process xslt:text | |
| 4888 */ | |
| 4889 void | |
| 4890 xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) { | |
| 4891 xmlNodePtr cur, delete; | |
| 4892 | |
| 4893 if ((style == NULL) || (templ == NULL) || | |
| 4894 (templ->type == XML_NAMESPACE_DECL)) return; | |
| 4895 | |
| 4896 /* | |
| 4897 * This content comes from the stylesheet | |
| 4898 * For stylesheets, the set of whitespace-preserving | |
| 4899 * element names consists of just xsl:text. | |
| 4900 */ | |
| 4901 cur = templ->children; | |
| 4902 delete = NULL; | |
| 4903 while (cur != NULL) { | |
| 4904 if (delete != NULL) { | |
| 4905 #ifdef WITH_XSLT_DEBUG_BLANKS | |
| 4906 xsltGenericDebug(xsltGenericDebugContext, | |
| 4907 "xsltParseTemplateContent: removing text\n"); | |
| 4908 #endif | |
| 4909 xmlUnlinkNode(delete); | |
| 4910 xmlFreeNode(delete); | |
| 4911 delete = NULL; | |
| 4912 } | |
| 4913 if (IS_XSLT_ELEM(cur)) { | |
| 4914 xsltStylePreCompute(style, cur); | |
| 4915 | |
| 4916 if (IS_XSLT_NAME(cur, "text")) { | |
| 4917 /* | |
| 4918 * TODO: Processing of xsl:text should be moved to | |
| 4919 * xsltPreprocessStylesheet(), since otherwise this | |
| 4920 * will be performed for every multiply included | |
| 4921 * stylesheet; i.e. this here is not skipped with | |
| 4922 * the use of the style->nopreproc flag. | |
| 4923 */ | |
| 4924 if (cur->children != NULL) { | |
| 4925 xmlChar *prop; | |
| 4926 xmlNodePtr text = cur->children, next; | |
| 4927 int noesc = 0; | |
| 4928 | |
| 4929 prop = xmlGetNsProp(cur, | |
| 4930 (const xmlChar *)"disable-output-escaping", | |
| 4931 NULL); | |
| 4932 if (prop != NULL) { | |
| 4933 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 4934 xsltGenericDebug(xsltGenericDebugContext, | |
| 4935 "Disable escaping: %s\n", text->content); | |
| 4936 #endif | |
| 4937 if (xmlStrEqual(prop, (const xmlChar *)"yes")) { | |
| 4938 noesc = 1; | |
| 4939 } else if (!xmlStrEqual(prop, | |
| 4940 (const xmlChar *)"no")){ | |
| 4941 xsltTransformError(NULL, style, cur, | |
| 4942 "xsl:text: disable-output-escaping allows only yes or no\n"); | |
| 4943 style->warnings++; | |
| 4944 | |
| 4945 } | |
| 4946 xmlFree(prop); | |
| 4947 } | |
| 4948 | |
| 4949 while (text != NULL) { | |
| 4950 if (text->type == XML_COMMENT_NODE) { | |
| 4951 text = text->next; | |
| 4952 continue; | |
| 4953 } | |
| 4954 if ((text->type != XML_TEXT_NODE) && | |
| 4955 (text->type != XML_CDATA_SECTION_NODE)) { | |
| 4956 xsltTransformError(NULL, style, cur, | |
| 4957 "xsltParseTemplateContent: xslt:text content problem\n"); | |
| 4958 style->errors++; | |
| 4959 break; | |
| 4960 } | |
| 4961 if ((noesc) && (text->type != XML_CDATA_SECTION_NODE)) | |
| 4962 text->name = xmlStringTextNoenc; | |
| 4963 text = text->next; | |
| 4964 } | |
| 4965 | |
| 4966 /* | |
| 4967 * replace xsl:text by the list of childs | |
| 4968 */ | |
| 4969 if (text == NULL) { | |
| 4970 text = cur->children; | |
| 4971 while (text != NULL) { | |
| 4972 if ((style->internalized) && | |
| 4973 (text->content != NULL) && | |
| 4974 (!xmlDictOwns(style->dict, text->content))) { | |
| 4975 | |
| 4976 /* | |
| 4977 * internalize the text string | |
| 4978 */ | |
| 4979 if (text->doc->dict != NULL) { | |
| 4980 const xmlChar *tmp; | |
| 4981 | |
| 4982 tmp = xmlDictLookup(text->doc->dict, | |
| 4983 text->content, -1); | |
| 4984 if (tmp != text->content) { | |
| 4985 xmlNodeSetContent(text, NULL); | |
| 4986 text->content = (xmlChar *) tmp; | |
| 4987 } | |
| 4988 } | |
| 4989 } | |
| 4990 | |
| 4991 next = text->next; | |
| 4992 xmlUnlinkNode(text); | |
| 4993 xmlAddPrevSibling(cur, text); | |
| 4994 text = next; | |
| 4995 } | |
| 4996 } | |
| 4997 } | |
| 4998 delete = cur; | |
| 4999 goto skip_children; | |
| 5000 } | |
| 5001 } | |
| 5002 else if ((cur->ns != NULL) && (style->nsDefs != NULL) && | |
| 5003 (xsltCheckExtPrefix(style, cur->ns->prefix))) | |
| 5004 { | |
| 5005 /* | |
| 5006 * okay this is an extension element compile it too | |
| 5007 */ | |
| 5008 xsltStylePreCompute(style, cur); | |
| 5009 } | |
| 5010 else if (cur->type == XML_ELEMENT_NODE) | |
| 5011 { | |
| 5012 /* | |
| 5013 * This is an element which will be output as part of the | |
| 5014 * template exectution, precompile AVT if found. | |
| 5015 */ | |
| 5016 if ((cur->ns == NULL) && (style->defaultAlias != NULL)) { | |
| 5017 cur->ns = xmlSearchNsByHref(cur->doc, cur, | |
| 5018 style->defaultAlias); | |
| 5019 } | |
| 5020 if (cur->properties != NULL) { | |
| 5021 xmlAttrPtr attr = cur->properties; | |
| 5022 | |
| 5023 while (attr != NULL) { | |
| 5024 xsltCompileAttr(style, attr); | |
| 5025 attr = attr->next; | |
| 5026 } | |
| 5027 } | |
| 5028 } | |
| 5029 /* | |
| 5030 * Skip to next node | |
| 5031 */ | |
| 5032 if (cur->children != NULL) { | |
| 5033 if (cur->children->type != XML_ENTITY_DECL) { | |
| 5034 cur = cur->children; | |
| 5035 continue; | |
| 5036 } | |
| 5037 } | |
| 5038 skip_children: | |
| 5039 if (cur->next != NULL) { | |
| 5040 cur = cur->next; | |
| 5041 continue; | |
| 5042 } | |
| 5043 | |
| 5044 do { | |
| 5045 cur = cur->parent; | |
| 5046 if (cur == NULL) | |
| 5047 break; | |
| 5048 if (cur == templ) { | |
| 5049 cur = NULL; | |
| 5050 break; | |
| 5051 } | |
| 5052 if (cur->next != NULL) { | |
| 5053 cur = cur->next; | |
| 5054 break; | |
| 5055 } | |
| 5056 } while (cur != NULL); | |
| 5057 } | |
| 5058 if (delete != NULL) { | |
| 5059 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 5060 xsltGenericDebug(xsltGenericDebugContext, | |
| 5061 "xsltParseTemplateContent: removing text\n"); | |
| 5062 #endif | |
| 5063 xmlUnlinkNode(delete); | |
| 5064 xmlFreeNode(delete); | |
| 5065 delete = NULL; | |
| 5066 } | |
| 5067 | |
| 5068 /* | |
| 5069 * Skip the first params | |
| 5070 */ | |
| 5071 cur = templ->children; | |
| 5072 while (cur != NULL) { | |
| 5073 if ((IS_XSLT_ELEM(cur)) && (!(IS_XSLT_NAME(cur, "param")))) | |
| 5074 break; | |
| 5075 cur = cur->next; | |
| 5076 } | |
| 5077 | |
| 5078 /* | |
| 5079 * Browse the remainder of the template | |
| 5080 */ | |
| 5081 while (cur != NULL) { | |
| 5082 if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) { | |
| 5083 xmlNodePtr param = cur; | |
| 5084 | |
| 5085 xsltTransformError(NULL, style, cur, | |
| 5086 "xsltParseTemplateContent: ignoring misplaced param element\n"); | |
| 5087 if (style != NULL) style->warnings++; | |
| 5088 cur = cur->next; | |
| 5089 xmlUnlinkNode(param); | |
| 5090 xmlFreeNode(param); | |
| 5091 } else | |
| 5092 break; | |
| 5093 } | |
| 5094 } | |
| 5095 | |
| 5096 #endif /* else XSLT_REFACTORED */ | |
| 5097 | |
| 5098 /** | |
| 5099 * xsltParseStylesheetKey: | |
| 5100 * @style: the XSLT stylesheet | |
| 5101 * @key: the "key" element | |
| 5102 * | |
| 5103 * <!-- Category: top-level-element --> | |
| 5104 * <xsl:key name = qname, match = pattern, use = expression /> | |
| 5105 * | |
| 5106 * parse an XSLT stylesheet key definition and register it | |
| 5107 */ | |
| 5108 | |
| 5109 static void | |
| 5110 xsltParseStylesheetKey(xsltStylesheetPtr style, xmlNodePtr key) { | |
| 5111 xmlChar *prop = NULL; | |
| 5112 xmlChar *use = NULL; | |
| 5113 xmlChar *match = NULL; | |
| 5114 xmlChar *name = NULL; | |
| 5115 xmlChar *nameURI = NULL; | |
| 5116 | |
| 5117 if ((style == NULL) || (key == NULL) || (key->type != XML_ELEMENT_NODE)) | |
| 5118 return; | |
| 5119 | |
| 5120 /* | |
| 5121 * Get arguments | |
| 5122 */ | |
| 5123 prop = xmlGetNsProp(key, (const xmlChar *)"name", NULL); | |
| 5124 if (prop != NULL) { | |
| 5125 const xmlChar *URI; | |
| 5126 | |
| 5127 /* | |
| 5128 * TODO: Don't use xsltGetQNameURI(). | |
| 5129 */ | |
| 5130 URI = xsltGetQNameURI(key, &prop); | |
| 5131 if (prop == NULL) { | |
| 5132 if (style != NULL) style->errors++; | |
| 5133 goto error; | |
| 5134 } else { | |
| 5135 name = prop; | |
| 5136 if (URI != NULL) | |
| 5137 nameURI = xmlStrdup(URI); | |
| 5138 } | |
| 5139 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 5140 xsltGenericDebug(xsltGenericDebugContext, | |
| 5141 "xsltParseStylesheetKey: name %s\n", name); | |
| 5142 #endif | |
| 5143 } else { | |
| 5144 xsltTransformError(NULL, style, key, | |
| 5145 "xsl:key : error missing name\n"); | |
| 5146 if (style != NULL) style->errors++; | |
| 5147 goto error; | |
| 5148 } | |
| 5149 | |
| 5150 match = xmlGetNsProp(key, (const xmlChar *)"match", NULL); | |
| 5151 if (match == NULL) { | |
| 5152 xsltTransformError(NULL, style, key, | |
| 5153 "xsl:key : error missing match\n"); | |
| 5154 if (style != NULL) style->errors++; | |
| 5155 goto error; | |
| 5156 } | |
| 5157 | |
| 5158 use = xmlGetNsProp(key, (const xmlChar *)"use", NULL); | |
| 5159 if (use == NULL) { | |
| 5160 xsltTransformError(NULL, style, key, | |
| 5161 "xsl:key : error missing use\n"); | |
| 5162 if (style != NULL) style->errors++; | |
| 5163 goto error; | |
| 5164 } | |
| 5165 | |
| 5166 /* | |
| 5167 * register the keys | |
| 5168 */ | |
| 5169 xsltAddKey(style, name, nameURI, match, use, key); | |
| 5170 | |
| 5171 | |
| 5172 error: | |
| 5173 if (use != NULL) | |
| 5174 xmlFree(use); | |
| 5175 if (match != NULL) | |
| 5176 xmlFree(match); | |
| 5177 if (name != NULL) | |
| 5178 xmlFree(name); | |
| 5179 if (nameURI != NULL) | |
| 5180 xmlFree(nameURI); | |
| 5181 | |
| 5182 if (key->children != NULL) { | |
| 5183 xsltParseContentError(style, key->children); | |
| 5184 } | |
| 5185 } | |
| 5186 | |
| 5187 #ifdef XSLT_REFACTORED | |
| 5188 /** | |
| 5189 * xsltParseXSLTTemplate: | |
| 5190 * @style: the XSLT stylesheet | |
| 5191 * @template: the "template" element | |
| 5192 * | |
| 5193 * parse an XSLT stylesheet template building the associated structures | |
| 5194 * TODO: Is @style ever expected to be NULL? | |
| 5195 * | |
| 5196 * Called from: | |
| 5197 * xsltParseXSLTStylesheet() | |
| 5198 * xsltParseStylesheetTop() | |
| 5199 */ | |
| 5200 | |
| 5201 static void | |
| 5202 xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) { | |
| 5203 xsltTemplatePtr templ; | |
| 5204 xmlChar *prop; | |
| 5205 double priority; | |
| 5206 | |
| 5207 if ((cctxt == NULL) || (templNode == NULL) || | |
| 5208 (templNode->type != XML_ELEMENT_NODE)) | |
| 5209 return; | |
| 5210 | |
| 5211 /* | |
| 5212 * Create and link the structure | |
| 5213 */ | |
| 5214 templ = xsltNewTemplate(); | |
| 5215 if (templ == NULL) | |
| 5216 return; | |
| 5217 | |
| 5218 xsltCompilerNodePush(cctxt, templNode); | |
| 5219 if (templNode->nsDef != NULL) | |
| 5220 cctxt->inode->inScopeNs = | |
| 5221 xsltCompilerBuildInScopeNsList(cctxt, templNode); | |
| 5222 | |
| 5223 templ->next = cctxt->style->templates; | |
| 5224 cctxt->style->templates = templ; | |
| 5225 templ->style = cctxt->style; | |
| 5226 | |
| 5227 /* | |
| 5228 * Attribute "mode". | |
| 5229 */ | |
| 5230 prop = xmlGetNsProp(templNode, (const xmlChar *)"mode", NULL); | |
| 5231 if (prop != NULL) { | |
| 5232 const xmlChar *modeURI; | |
| 5233 | |
| 5234 /* | |
| 5235 * TODO: We need a standardized function for extraction | |
| 5236 * of namespace names and local names from QNames. | |
| 5237 * Don't use xsltGetQNameURI() as it cannot channe� | |
| 5238 * reports through the context. | |
| 5239 */ | |
| 5240 modeURI = xsltGetQNameURI(templNode, &prop); | |
| 5241 if (prop == NULL) { | |
| 5242 cctxt->style->errors++; | |
| 5243 goto error; | |
| 5244 } | |
| 5245 templ->mode = xmlDictLookup(cctxt->style->dict, prop, -1); | |
| 5246 xmlFree(prop); | |
| 5247 prop = NULL; | |
| 5248 if (xmlValidateNCName(templ->mode, 0)) { | |
| 5249 xsltTransformError(NULL, cctxt->style, templNode, | |
| 5250 "xsl:template: Attribute 'mode': The local part '%s' " | |
| 5251 "of the value is not a valid NCName.\n", templ->name); | |
| 5252 cctxt->style->errors++; | |
| 5253 goto error; | |
| 5254 } | |
| 5255 if (modeURI != NULL) | |
| 5256 templ->modeURI = xmlDictLookup(cctxt->style->dict, modeURI, -1); | |
| 5257 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 5258 xsltGenericDebug(xsltGenericDebugContext, | |
| 5259 "xsltParseXSLTTemplate: mode %s\n", templ->mode); | |
| 5260 #endif | |
| 5261 } | |
| 5262 /* | |
| 5263 * Attribute "match". | |
| 5264 */ | |
| 5265 prop = xmlGetNsProp(templNode, (const xmlChar *)"match", NULL); | |
| 5266 if (prop != NULL) { | |
| 5267 templ->match = prop; | |
| 5268 prop = NULL; | |
| 5269 } | |
| 5270 /* | |
| 5271 * Attribute "priority". | |
| 5272 */ | |
| 5273 prop = xmlGetNsProp(templNode, (const xmlChar *)"priority", NULL); | |
| 5274 if (prop != NULL) { | |
| 5275 priority = xmlXPathStringEvalNumber(prop); | |
| 5276 templ->priority = (float) priority; | |
| 5277 xmlFree(prop); | |
| 5278 prop = NULL; | |
| 5279 } | |
| 5280 /* | |
| 5281 * Attribute "name". | |
| 5282 */ | |
| 5283 prop = xmlGetNsProp(templNode, (const xmlChar *)"name", NULL); | |
| 5284 if (prop != NULL) { | |
| 5285 const xmlChar *nameURI; | |
| 5286 xsltTemplatePtr curTempl; | |
| 5287 | |
| 5288 /* | |
| 5289 * TODO: Don't use xsltGetQNameURI(). | |
| 5290 */ | |
| 5291 nameURI = xsltGetQNameURI(templNode, &prop); | |
| 5292 if (prop == NULL) { | |
| 5293 cctxt->style->errors++; | |
| 5294 goto error; | |
| 5295 } | |
| 5296 templ->name = xmlDictLookup(cctxt->style->dict, prop, -1); | |
| 5297 xmlFree(prop); | |
| 5298 prop = NULL; | |
| 5299 if (xmlValidateNCName(templ->name, 0)) { | |
| 5300 xsltTransformError(NULL, cctxt->style, templNode, | |
| 5301 "xsl:template: Attribute 'name': The local part '%s' of " | |
| 5302 "the value is not a valid NCName.\n", templ->name); | |
| 5303 cctxt->style->errors++; | |
| 5304 goto error; | |
| 5305 } | |
| 5306 if (nameURI != NULL) | |
| 5307 templ->nameURI = xmlDictLookup(cctxt->style->dict, nameURI, -1); | |
| 5308 curTempl = templ->next; | |
| 5309 while (curTempl != NULL) { | |
| 5310 if ((nameURI != NULL && xmlStrEqual(curTempl->name, templ->name) && | |
| 5311 xmlStrEqual(curTempl->nameURI, nameURI) ) || | |
| 5312 (nameURI == NULL && curTempl->nameURI == NULL && | |
| 5313 xmlStrEqual(curTempl->name, templ->name))) | |
| 5314 { | |
| 5315 xsltTransformError(NULL, cctxt->style, templNode, | |
| 5316 "xsl:template: error duplicate name '%s'\n", templ->name); | |
| 5317 cctxt->style->errors++; | |
| 5318 goto error; | |
| 5319 } | |
| 5320 curTempl = curTempl->next; | |
| 5321 } | |
| 5322 } | |
| 5323 if (templNode->children != NULL) { | |
| 5324 xsltParseTemplateContent(cctxt->style, templNode); | |
| 5325 /* | |
| 5326 * MAYBE TODO: Custom behaviour: In order to stay compatible with | |
| 5327 * Xalan and MSXML(.NET), we could allow whitespace | |
| 5328 * to appear before an xml:param element; this whitespace | |
| 5329 * will additionally become part of the "template". | |
| 5330 * NOTE that this is totally deviates from the spec, but | |
| 5331 * is the de facto behaviour of Xalan and MSXML(.NET). | |
| 5332 * Personally I wouldn't allow this, since if we have: | |
| 5333 * <xsl:template ...xml:space="preserve"> | |
| 5334 * <xsl:param name="foo"/> | |
| 5335 * <xsl:param name="bar"/> | |
| 5336 * <xsl:param name="zoo"/> | |
| 5337 * ... the whitespace between every xsl:param would be | |
| 5338 * added to the result tree. | |
| 5339 */ | |
| 5340 } | |
| 5341 | |
| 5342 templ->elem = templNode; | |
| 5343 templ->content = templNode->children; | |
| 5344 xsltAddTemplate(cctxt->style, templ, templ->mode, templ->modeURI); | |
| 5345 | |
| 5346 error: | |
| 5347 xsltCompilerNodePop(cctxt, templNode); | |
| 5348 return; | |
| 5349 } | |
| 5350 | |
| 5351 #else /* XSLT_REFACTORED */ | |
| 5352 | |
| 5353 /** | |
| 5354 * xsltParseStylesheetTemplate: | |
| 5355 * @style: the XSLT stylesheet | |
| 5356 * @template: the "template" element | |
| 5357 * | |
| 5358 * parse an XSLT stylesheet template building the associated structures | |
| 5359 */ | |
| 5360 | |
| 5361 static void | |
| 5362 xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) { | |
| 5363 xsltTemplatePtr ret; | |
| 5364 xmlChar *prop; | |
| 5365 xmlChar *mode = NULL; | |
| 5366 xmlChar *modeURI = NULL; | |
| 5367 double priority; | |
| 5368 | |
| 5369 if ((style == NULL) || (template == NULL) || | |
| 5370 (template->type != XML_ELEMENT_NODE)) | |
| 5371 return; | |
| 5372 | |
| 5373 /* | |
| 5374 * Create and link the structure | |
| 5375 */ | |
| 5376 ret = xsltNewTemplate(); | |
| 5377 if (ret == NULL) | |
| 5378 return; | |
| 5379 ret->next = style->templates; | |
| 5380 style->templates = ret; | |
| 5381 ret->style = style; | |
| 5382 | |
| 5383 /* | |
| 5384 * Get inherited namespaces | |
| 5385 */ | |
| 5386 /* | |
| 5387 * TODO: Apply the optimized in-scope-namespace mechanism | |
| 5388 * as for the other XSLT instructions. | |
| 5389 */ | |
| 5390 xsltGetInheritedNsList(style, ret, template); | |
| 5391 | |
| 5392 /* | |
| 5393 * Get arguments | |
| 5394 */ | |
| 5395 prop = xmlGetNsProp(template, (const xmlChar *)"mode", NULL); | |
| 5396 if (prop != NULL) { | |
| 5397 const xmlChar *URI; | |
| 5398 | |
| 5399 /* | |
| 5400 * TODO: Don't use xsltGetQNameURI(). | |
| 5401 */ | |
| 5402 URI = xsltGetQNameURI(template, &prop); | |
| 5403 if (prop == NULL) { | |
| 5404 if (style != NULL) style->errors++; | |
| 5405 goto error; | |
| 5406 } else { | |
| 5407 mode = prop; | |
| 5408 if (URI != NULL) | |
| 5409 modeURI = xmlStrdup(URI); | |
| 5410 } | |
| 5411 ret->mode = xmlDictLookup(style->dict, mode, -1); | |
| 5412 ret->modeURI = xmlDictLookup(style->dict, modeURI, -1); | |
| 5413 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 5414 xsltGenericDebug(xsltGenericDebugContext, | |
| 5415 "xsltParseStylesheetTemplate: mode %s\n", mode); | |
| 5416 #endif | |
| 5417 if (mode != NULL) xmlFree(mode); | |
| 5418 if (modeURI != NULL) xmlFree(modeURI); | |
| 5419 } | |
| 5420 prop = xmlGetNsProp(template, (const xmlChar *)"match", NULL); | |
| 5421 if (prop != NULL) { | |
| 5422 if (ret->match != NULL) xmlFree(ret->match); | |
| 5423 ret->match = prop; | |
| 5424 } | |
| 5425 | |
| 5426 prop = xmlGetNsProp(template, (const xmlChar *)"priority", NULL); | |
| 5427 if (prop != NULL) { | |
| 5428 priority = xmlXPathStringEvalNumber(prop); | |
| 5429 ret->priority = (float) priority; | |
| 5430 xmlFree(prop); | |
| 5431 } | |
| 5432 | |
| 5433 prop = xmlGetNsProp(template, (const xmlChar *)"name", NULL); | |
| 5434 if (prop != NULL) { | |
| 5435 const xmlChar *URI; | |
| 5436 | |
| 5437 /* | |
| 5438 * TODO: Don't use xsltGetQNameURI(). | |
| 5439 */ | |
| 5440 URI = xsltGetQNameURI(template, &prop); | |
| 5441 if (prop == NULL) { | |
| 5442 if (style != NULL) style->errors++; | |
| 5443 goto error; | |
| 5444 } else { | |
| 5445 if (xmlValidateNCName(prop,0)) { | |
| 5446 xsltTransformError(NULL, style, template, | |
| 5447 "xsl:template : error invalid name '%s'\n", prop); | |
| 5448 if (style != NULL) style->errors++; | |
| 5449 goto error; | |
| 5450 } | |
| 5451 ret->name = xmlDictLookup(style->dict, BAD_CAST prop, -1); | |
| 5452 xmlFree(prop); | |
| 5453 prop = NULL; | |
| 5454 if (URI != NULL) | |
| 5455 ret->nameURI = xmlDictLookup(style->dict, BAD_CAST URI, -1); | |
| 5456 else | |
| 5457 ret->nameURI = NULL; | |
| 5458 } | |
| 5459 } | |
| 5460 | |
| 5461 /* | |
| 5462 * parse the content and register the pattern | |
| 5463 */ | |
| 5464 xsltParseTemplateContent(style, template); | |
| 5465 ret->elem = template; | |
| 5466 ret->content = template->children; | |
| 5467 xsltAddTemplate(style, ret, ret->mode, ret->modeURI); | |
| 5468 | |
| 5469 error: | |
| 5470 return; | |
| 5471 } | |
| 5472 | |
| 5473 #endif /* else XSLT_REFACTORED */ | |
| 5474 | |
| 5475 #ifdef XSLT_REFACTORED | |
| 5476 | |
| 5477 /** | |
| 5478 * xsltIncludeComp: | |
| 5479 * @cctxt: the compilation contenxt | |
| 5480 * @node: the xsl:include node | |
| 5481 * | |
| 5482 * Process the xslt include node on the source node | |
| 5483 */ | |
| 5484 static xsltStyleItemIncludePtr | |
| 5485 xsltCompileXSLTIncludeElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) { | |
| 5486 xsltStyleItemIncludePtr item; | |
| 5487 | |
| 5488 if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE)) | |
| 5489 return(NULL); | |
| 5490 | |
| 5491 node->psvi = NULL; | |
| 5492 item = (xsltStyleItemIncludePtr) xmlMalloc(sizeof(xsltStyleItemInclude)); | |
| 5493 if (item == NULL) { | |
| 5494 xsltTransformError(NULL, cctxt->style, node, | |
| 5495 "xsltIncludeComp : malloc failed\n"); | |
| 5496 cctxt->style->errors++; | |
| 5497 return(NULL); | |
| 5498 } | |
| 5499 memset(item, 0, sizeof(xsltStyleItemInclude)); | |
| 5500 | |
| 5501 node->psvi = item; | |
| 5502 item->inst = node; | |
| 5503 item->type = XSLT_FUNC_INCLUDE; | |
| 5504 | |
| 5505 item->next = cctxt->style->preComps; | |
| 5506 cctxt->style->preComps = (xsltElemPreCompPtr) item; | |
| 5507 | |
| 5508 return(item); | |
| 5509 } | |
| 5510 | |
| 5511 /** | |
| 5512 * xsltParseFindTopLevelElem: | |
| 5513 */ | |
| 5514 static int | |
| 5515 xsltParseFindTopLevelElem(xsltCompilerCtxtPtr cctxt, | |
| 5516 xmlNodePtr cur, | |
| 5517 const xmlChar *name, | |
| 5518 const xmlChar *namespaceURI, | |
| 5519 int breakOnOtherElem, | |
| 5520 xmlNodePtr *resultNode) | |
| 5521 { | |
| 5522 if (name == NULL) | |
| 5523 return(-1); | |
| 5524 | |
| 5525 *resultNode = NULL; | |
| 5526 while (cur != NULL) { | |
| 5527 if (cur->type == XML_ELEMENT_NODE) { | |
| 5528 if ((cur->ns != NULL) && (cur->name != NULL)) { | |
| 5529 if ((*(cur->name) == *name) && | |
| 5530 xmlStrEqual(cur->name, name) && | |
| 5531 xmlStrEqual(cur->ns->href, namespaceURI)) | |
| 5532 { | |
| 5533 *resultNode = cur; | |
| 5534 return(1); | |
| 5535 } | |
| 5536 } | |
| 5537 if (breakOnOtherElem) | |
| 5538 break; | |
| 5539 } | |
| 5540 cur = cur->next; | |
| 5541 } | |
| 5542 *resultNode = cur; | |
| 5543 return(0); | |
| 5544 } | |
| 5545 | |
| 5546 static int | |
| 5547 xsltParseTopLevelXSLTElem(xsltCompilerCtxtPtr cctxt, | |
| 5548 xmlNodePtr node, | |
| 5549 xsltStyleType type) | |
| 5550 { | |
| 5551 int ret = 0; | |
| 5552 | |
| 5553 /* | |
| 5554 * TODO: The reason why this function exists: | |
| 5555 * due to historical reasons some of the | |
| 5556 * top-level declarations are processed by functions | |
| 5557 * in other files. Since we need still to set | |
| 5558 * up the node-info and generate information like | |
| 5559 * in-scope namespaces, this is a wrapper around | |
| 5560 * those old parsing functions. | |
| 5561 */ | |
| 5562 xsltCompilerNodePush(cctxt, node); | |
| 5563 if (node->nsDef != NULL) | |
| 5564 cctxt->inode->inScopeNs = | |
| 5565 xsltCompilerBuildInScopeNsList(cctxt, node); | |
| 5566 cctxt->inode->type = type; | |
| 5567 | |
| 5568 switch (type) { | |
| 5569 case XSLT_FUNC_INCLUDE: | |
| 5570 { | |
| 5571 int oldIsInclude; | |
| 5572 | |
| 5573 if (xsltCompileXSLTIncludeElem(cctxt, node) == NULL) | |
| 5574 goto exit; | |
| 5575 /* | |
| 5576 * Mark this stylesheet tree as being currently included. | |
| 5577 */ | |
| 5578 oldIsInclude = cctxt->isInclude; | |
| 5579 cctxt->isInclude = 1; | |
| 5580 | |
| 5581 if (xsltParseStylesheetInclude(cctxt->style, node) != 0) { | |
| 5582 cctxt->style->errors++; | |
| 5583 } | |
| 5584 cctxt->isInclude = oldIsInclude; | |
| 5585 } | |
| 5586 break; | |
| 5587 case XSLT_FUNC_PARAM: | |
| 5588 xsltStylePreCompute(cctxt->style, node); | |
| 5589 xsltParseGlobalParam(cctxt->style, node); | |
| 5590 break; | |
| 5591 case XSLT_FUNC_VARIABLE: | |
| 5592 xsltStylePreCompute(cctxt->style, node); | |
| 5593 xsltParseGlobalVariable(cctxt->style, node); | |
| 5594 break; | |
| 5595 case XSLT_FUNC_ATTRSET: | |
| 5596 xsltParseStylesheetAttributeSet(cctxt->style, node); | |
| 5597 break; | |
| 5598 default: | |
| 5599 xsltTransformError(NULL, cctxt->style, node, | |
| 5600 "Internal error: (xsltParseTopLevelXSLTElem) " | |
| 5601 "Cannot handle this top-level declaration.\n"); | |
| 5602 cctxt->style->errors++; | |
| 5603 ret = -1; | |
| 5604 } | |
| 5605 | |
| 5606 exit: | |
| 5607 xsltCompilerNodePop(cctxt, node); | |
| 5608 | |
| 5609 return(ret); | |
| 5610 } | |
| 5611 | |
| 5612 #if 0 | |
| 5613 static int | |
| 5614 xsltParseRemoveWhitespace(xmlNodePtr node) | |
| 5615 { | |
| 5616 if ((node == NULL) || (node->children == NULL)) | |
| 5617 return(0); | |
| 5618 else { | |
| 5619 xmlNodePtr delNode = NULL, child = node->children; | |
| 5620 | |
| 5621 do { | |
| 5622 if (delNode) { | |
| 5623 xmlUnlinkNode(delNode); | |
| 5624 xmlFreeNode(delNode); | |
| 5625 delNode = NULL; | |
| 5626 } | |
| 5627 if (((child->type == XML_TEXT_NODE) || | |
| 5628 (child->type == XML_CDATA_SECTION_NODE)) && | |
| 5629 (IS_BLANK_NODE(child))) | |
| 5630 delNode = child; | |
| 5631 child = child->next; | |
| 5632 } while (child != NULL); | |
| 5633 if (delNode) { | |
| 5634 xmlUnlinkNode(delNode); | |
| 5635 xmlFreeNode(delNode); | |
| 5636 delNode = NULL; | |
| 5637 } | |
| 5638 } | |
| 5639 return(0); | |
| 5640 } | |
| 5641 #endif | |
| 5642 | |
| 5643 static int | |
| 5644 xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) | |
| 5645 { | |
| 5646 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 5647 int templates = 0; | |
| 5648 #endif | |
| 5649 xmlNodePtr cur, start = NULL; | |
| 5650 xsltStylesheetPtr style; | |
| 5651 | |
| 5652 if ((cctxt == NULL) || (node == NULL) || | |
| 5653 (node->type != XML_ELEMENT_NODE)) | |
| 5654 return(-1); | |
| 5655 | |
| 5656 style = cctxt->style; | |
| 5657 /* | |
| 5658 * At this stage all import declarations of all stylesheet modules | |
| 5659 * with the same stylesheet level have been processed. | |
| 5660 * Now we can safely parse the rest of the declarations. | |
| 5661 */ | |
| 5662 if (IS_XSLT_ELEM_FAST(node) && IS_XSLT_NAME(node, "include")) | |
| 5663 { | |
| 5664 xsltDocumentPtr include; | |
| 5665 /* | |
| 5666 * URGENT TODO: Make this work with simplified stylesheets! | |
| 5667 * I.e., when we won't find an xsl:stylesheet element. | |
| 5668 */ | |
| 5669 /* | |
| 5670 * This is as include declaration. | |
| 5671 */ | |
| 5672 include = ((xsltStyleItemIncludePtr) node->psvi)->include; | |
| 5673 if (include == NULL) { | |
| 5674 /* TODO: raise error? */ | |
| 5675 return(-1); | |
| 5676 } | |
| 5677 /* | |
| 5678 * TODO: Actually an xsl:include should locate an embedded | |
| 5679 * stylesheet as well; so the document-element won't always | |
| 5680 * be the element where the actual stylesheet is rooted at. | |
| 5681 * But such embedded stylesheets are not supported by Libxslt yet. | |
| 5682 */ | |
| 5683 node = xmlDocGetRootElement(include->doc); | |
| 5684 if (node == NULL) { | |
| 5685 return(-1); | |
| 5686 } | |
| 5687 } | |
| 5688 | |
| 5689 if (node->children == NULL) | |
| 5690 return(0); | |
| 5691 /* | |
| 5692 * Push the xsl:stylesheet/xsl:transform element. | |
| 5693 */ | |
| 5694 xsltCompilerNodePush(cctxt, node); | |
| 5695 cctxt->inode->isRoot = 1; | |
| 5696 cctxt->inode->nsChanged = 0; | |
| 5697 /* | |
| 5698 * Start with the naked dummy info for literal result elements. | |
| 5699 */ | |
| 5700 cctxt->inode->litResElemInfo = cctxt->inodeList->litResElemInfo; | |
| 5701 | |
| 5702 /* | |
| 5703 * In every case, we need to have | |
| 5704 * the in-scope namespaces of the element, where the | |
| 5705 * stylesheet is rooted at, regardless if it's an XSLT | |
| 5706 * instruction or a literal result instruction (or if | |
| 5707 * this is an embedded stylesheet). | |
| 5708 */ | |
| 5709 cctxt->inode->inScopeNs = | |
| 5710 xsltCompilerBuildInScopeNsList(cctxt, node); | |
| 5711 | |
| 5712 /* | |
| 5713 * Process attributes of xsl:stylesheet/xsl:transform. | |
| 5714 * -------------------------------------------------- | |
| 5715 * Allowed are: | |
| 5716 * id = id | |
| 5717 * extension-element-prefixes = tokens | |
| 5718 * exclude-result-prefixes = tokens | |
| 5719 * version = number (mandatory) | |
| 5720 */ | |
| 5721 if (xsltParseAttrXSLTVersion(cctxt, node, | |
| 5722 XSLT_ELEMENT_CATEGORY_XSLT) == 0) | |
| 5723 { | |
| 5724 /* | |
| 5725 * Attribute "version". | |
| 5726 * XSLT 1.0: "An xsl:stylesheet element *must* have a version | |
| 5727 * attribute, indicating the version of XSLT that the | |
| 5728 * stylesheet requires". | |
| 5729 * The root element of a simplified stylesheet must also have | |
| 5730 * this attribute. | |
| 5731 */ | |
| 5732 #ifdef XSLT_REFACTORED_MANDATORY_VERSION | |
| 5733 if (isXsltElem) | |
| 5734 xsltTransformError(NULL, cctxt->style, node, | |
| 5735 "The attribute 'version' is missing.\n"); | |
| 5736 cctxt->style->errors++; | |
| 5737 #else | |
| 5738 /* OLD behaviour. */ | |
| 5739 xsltTransformError(NULL, cctxt->style, node, | |
| 5740 "xsl:version is missing: document may not be a stylesheet\n"); | |
| 5741 cctxt->style->warnings++; | |
| 5742 #endif | |
| 5743 } | |
| 5744 /* | |
| 5745 * The namespaces declared by the attributes | |
| 5746 * "extension-element-prefixes" and | |
| 5747 * "exclude-result-prefixes" are local to *this* | |
| 5748 * stylesheet tree; i.e., they are *not* visible to | |
| 5749 * other stylesheet-modules, whether imported or included. | |
| 5750 * | |
| 5751 * Attribute "extension-element-prefixes". | |
| 5752 */ | |
| 5753 cctxt->inode->extElemNs = | |
| 5754 xsltParseExtElemPrefixes(cctxt, node, NULL, | |
| 5755 XSLT_ELEMENT_CATEGORY_XSLT); | |
| 5756 /* | |
| 5757 * Attribute "exclude-result-prefixes". | |
| 5758 */ | |
| 5759 cctxt->inode->exclResultNs = | |
| 5760 xsltParseExclResultPrefixes(cctxt, node, NULL, | |
| 5761 XSLT_ELEMENT_CATEGORY_XSLT); | |
| 5762 /* | |
| 5763 * Create/reuse info for the literal result element. | |
| 5764 */ | |
| 5765 if (cctxt->inode->nsChanged) | |
| 5766 xsltLREInfoCreate(cctxt, node, 0); | |
| 5767 /* | |
| 5768 * Processed top-level elements: | |
| 5769 * ---------------------------- | |
| 5770 * xsl:variable, xsl:param (QName, in-scope ns, | |
| 5771 * expression (vars allowed)) | |
| 5772 * xsl:attribute-set (QName, in-scope ns) | |
| 5773 * xsl:strip-space, xsl:preserve-space (XPath NameTests, | |
| 5774 * in-scope ns) | |
| 5775 * I *think* global scope, merge with includes | |
| 5776 * xsl:output (QName, in-scope ns) | |
| 5777 * xsl:key (QName, in-scope ns, pattern, | |
| 5778 * expression (vars *not* allowed)) | |
| 5779 * xsl:decimal-format (QName, needs in-scope ns) | |
| 5780 * xsl:namespace-alias (in-scope ns) | |
| 5781 * global scope, merge with includes | |
| 5782 * xsl:template (last, QName, pattern) | |
| 5783 * | |
| 5784 * (whitespace-only text-nodes have *not* been removed | |
| 5785 * yet; this will be done in xsltParseSequenceConstructor) | |
| 5786 * | |
| 5787 * Report misplaced child-nodes first. | |
| 5788 */ | |
| 5789 cur = node->children; | |
| 5790 while (cur != NULL) { | |
| 5791 if (cur->type == XML_TEXT_NODE) { | |
| 5792 xsltTransformError(NULL, style, cur, | |
| 5793 "Misplaced text node (content: '%s').\n", | |
| 5794 (cur->content != NULL) ? cur->content : BAD_CAST ""); | |
| 5795 style->errors++; | |
| 5796 } else if (cur->type != XML_ELEMENT_NODE) { | |
| 5797 xsltTransformError(NULL, style, cur, "Misplaced node.\n"); | |
| 5798 style->errors++; | |
| 5799 } | |
| 5800 cur = cur->next; | |
| 5801 } | |
| 5802 /* | |
| 5803 * Skip xsl:import elements; they have been processed | |
| 5804 * already. | |
| 5805 */ | |
| 5806 cur = node->children; | |
| 5807 while ((cur != NULL) && xsltParseFindTopLevelElem(cctxt, cur, | |
| 5808 BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1) | |
| 5809 cur = cur->next; | |
| 5810 if (cur == NULL) | |
| 5811 goto exit; | |
| 5812 | |
| 5813 start = cur; | |
| 5814 /* | |
| 5815 * Process all top-level xsl:param elements. | |
| 5816 */ | |
| 5817 while ((cur != NULL) && | |
| 5818 xsltParseFindTopLevelElem(cctxt, cur, | |
| 5819 BAD_CAST "param", XSLT_NAMESPACE, 0, &cur) == 1) | |
| 5820 { | |
| 5821 xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_PARAM); | |
| 5822 cur = cur->next; | |
| 5823 } | |
| 5824 /* | |
| 5825 * Process all top-level xsl:variable elements. | |
| 5826 */ | |
| 5827 cur = start; | |
| 5828 while ((cur != NULL) && | |
| 5829 xsltParseFindTopLevelElem(cctxt, cur, | |
| 5830 BAD_CAST "variable", XSLT_NAMESPACE, 0, &cur) == 1) | |
| 5831 { | |
| 5832 xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_VARIABLE); | |
| 5833 cur = cur->next; | |
| 5834 } | |
| 5835 /* | |
| 5836 * Process all the rest of top-level elements. | |
| 5837 */ | |
| 5838 cur = start; | |
| 5839 while (cur != NULL) { | |
| 5840 /* | |
| 5841 * Process element nodes. | |
| 5842 */ | |
| 5843 if (cur->type == XML_ELEMENT_NODE) { | |
| 5844 if (cur->ns == NULL) { | |
| 5845 xsltTransformError(NULL, style, cur, | |
| 5846 "Unexpected top-level element in no namespace.\n"); | |
| 5847 style->errors++; | |
| 5848 cur = cur->next; | |
| 5849 continue; | |
| 5850 } | |
| 5851 /* | |
| 5852 * Process all XSLT elements. | |
| 5853 */ | |
| 5854 if (IS_XSLT_ELEM_FAST(cur)) { | |
| 5855 /* | |
| 5856 * xsl:import is only allowed at the beginning. | |
| 5857 */ | |
| 5858 if (IS_XSLT_NAME(cur, "import")) { | |
| 5859 xsltTransformError(NULL, style, cur, | |
| 5860 "Misplaced xsl:import element.\n"); | |
| 5861 style->errors++; | |
| 5862 cur = cur->next; | |
| 5863 continue; | |
| 5864 } | |
| 5865 /* | |
| 5866 * TODO: Change the return type of the parsing functions | |
| 5867 * to int. | |
| 5868 */ | |
| 5869 if (IS_XSLT_NAME(cur, "template")) { | |
| 5870 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 5871 templates++; | |
| 5872 #endif | |
| 5873 /* | |
| 5874 * TODO: Is the position of xsl:template in the | |
| 5875 * tree significant? If not it would be easier to | |
| 5876 * parse them at a later stage. | |
| 5877 */ | |
| 5878 xsltParseXSLTTemplate(cctxt, cur); | |
| 5879 } else if (IS_XSLT_NAME(cur, "variable")) { | |
| 5880 /* NOP; done already */ | |
| 5881 } else if (IS_XSLT_NAME(cur, "param")) { | |
| 5882 /* NOP; done already */ | |
| 5883 } else if (IS_XSLT_NAME(cur, "include")) { | |
| 5884 if (cur->psvi != NULL) | |
| 5885 xsltParseXSLTStylesheetElemCore(cctxt, cur); | |
| 5886 else { | |
| 5887 xsltTransformError(NULL, style, cur, | |
| 5888 "Internal error: " | |
| 5889 "(xsltParseXSLTStylesheetElemCore) " | |
| 5890 "The xsl:include element was not compiled.\n"); | |
| 5891 style->errors++; | |
| 5892 } | |
| 5893 } else if (IS_XSLT_NAME(cur, "strip-space")) { | |
| 5894 /* No node info needed. */ | |
| 5895 xsltParseStylesheetStripSpace(style, cur); | |
| 5896 } else if (IS_XSLT_NAME(cur, "preserve-space")) { | |
| 5897 /* No node info needed. */ | |
| 5898 xsltParseStylesheetPreserveSpace(style, cur); | |
| 5899 } else if (IS_XSLT_NAME(cur, "output")) { | |
| 5900 /* No node-info needed. */ | |
| 5901 xsltParseStylesheetOutput(style, cur); | |
| 5902 } else if (IS_XSLT_NAME(cur, "key")) { | |
| 5903 /* TODO: node-info needed for expressions ? */ | |
| 5904 xsltParseStylesheetKey(style, cur); | |
| 5905 } else if (IS_XSLT_NAME(cur, "decimal-format")) { | |
| 5906 /* No node-info needed. */ | |
| 5907 xsltParseStylesheetDecimalFormat(style, cur); | |
| 5908 } else if (IS_XSLT_NAME(cur, "attribute-set")) { | |
| 5909 xsltParseTopLevelXSLTElem(cctxt, cur, | |
| 5910 XSLT_FUNC_ATTRSET); | |
| 5911 } else if (IS_XSLT_NAME(cur, "namespace-alias")) { | |
| 5912 /* NOP; done already */ | |
| 5913 } else { | |
| 5914 if (cctxt->inode->forwardsCompat) { | |
| 5915 /* | |
| 5916 * Forwards-compatible mode: | |
| 5917 * | |
| 5918 * XSLT-1: "if it is a top-level element and | |
| 5919 * XSLT 1.0 does not allow such elements as top-level | |
| 5920 * elements, then the element must be ignored along | |
| 5921 * with its content;" | |
| 5922 */ | |
| 5923 /* | |
| 5924 * TODO: I don't think we should generate a warning. | |
| 5925 */ | |
| 5926 xsltTransformError(NULL, style, cur, | |
| 5927 "Forwards-compatible mode: Ignoring unknown XSLT " | |
| 5928 "element '%s'.\n", cur->name); | |
| 5929 style->warnings++; | |
| 5930 } else { | |
| 5931 xsltTransformError(NULL, style, cur, | |
| 5932 "Unknown XSLT element '%s'.\n", cur->name); | |
| 5933 style->errors++; | |
| 5934 } | |
| 5935 } | |
| 5936 } else { | |
| 5937 xsltTopLevelFunction function; | |
| 5938 | |
| 5939 /* | |
| 5940 * Process non-XSLT elements, which are in a | |
| 5941 * non-NULL namespace. | |
| 5942 */ | |
| 5943 /* | |
| 5944 * QUESTION: What does xsltExtModuleTopLevelLookup() | |
| 5945 * do exactly? | |
| 5946 */ | |
| 5947 function = xsltExtModuleTopLevelLookup(cur->name, | |
| 5948 cur->ns->href); | |
| 5949 if (function != NULL) | |
| 5950 function(style, cur); | |
| 5951 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 5952 xsltGenericDebug(xsltGenericDebugContext, | |
| 5953 "xsltParseXSLTStylesheetElemCore : User-defined " | |
| 5954 "data element '%s'.\n", cur->name); | |
| 5955 #endif | |
| 5956 } | |
| 5957 } | |
| 5958 cur = cur->next; | |
| 5959 } | |
| 5960 | |
| 5961 exit: | |
| 5962 | |
| 5963 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 5964 xsltGenericDebug(xsltGenericDebugContext, | |
| 5965 "### END of parsing top-level elements of doc '%s'.\n", | |
| 5966 node->doc->URL); | |
| 5967 xsltGenericDebug(xsltGenericDebugContext, | |
| 5968 "### Templates: %d\n", templates); | |
| 5969 #ifdef XSLT_REFACTORED | |
| 5970 xsltGenericDebug(xsltGenericDebugContext, | |
| 5971 "### Max inodes: %d\n", cctxt->maxNodeInfos); | |
| 5972 xsltGenericDebug(xsltGenericDebugContext, | |
| 5973 "### Max LREs : %d\n", cctxt->maxLREs); | |
| 5974 #endif /* XSLT_REFACTORED */ | |
| 5975 #endif /* WITH_XSLT_DEBUG_PARSING */ | |
| 5976 | |
| 5977 xsltCompilerNodePop(cctxt, node); | |
| 5978 return(0); | |
| 5979 } | |
| 5980 | |
| 5981 /** | |
| 5982 * xsltParseXSLTStylesheet: | |
| 5983 * @cctxt: the compiler context | |
| 5984 * @node: the xsl:stylesheet/xsl:transform element-node | |
| 5985 * | |
| 5986 * Parses the xsl:stylesheet and xsl:transform element. | |
| 5987 * | |
| 5988 * <xsl:stylesheet | |
| 5989 * id = id | |
| 5990 * extension-element-prefixes = tokens | |
| 5991 * exclude-result-prefixes = tokens | |
| 5992 * version = number> | |
| 5993 * <!-- Content: (xsl:import*, top-level-elements) --> | |
| 5994 * </xsl:stylesheet> | |
| 5995 * | |
| 5996 * BIG TODO: The xsl:include stuff. | |
| 5997 * | |
| 5998 * Called by xsltParseStylesheetTree() | |
| 5999 * | |
| 6000 * Returns 0 on success, a positive result on errors and | |
| 6001 * -1 on API or internal errors. | |
| 6002 */ | |
| 6003 static int | |
| 6004 xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) | |
| 6005 { | |
| 6006 xmlNodePtr cur, start; | |
| 6007 | |
| 6008 if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE)) | |
| 6009 return(-1); | |
| 6010 | |
| 6011 if (node->children == NULL) | |
| 6012 goto exit; | |
| 6013 | |
| 6014 /* | |
| 6015 * Process top-level elements: | |
| 6016 * xsl:import (must be first) | |
| 6017 * xsl:include (this is just a pre-processing) | |
| 6018 */ | |
| 6019 cur = node->children; | |
| 6020 /* | |
| 6021 * Process xsl:import elements. | |
| 6022 * XSLT 1.0: "The xsl:import element children must precede all | |
| 6023 * other element children of an xsl:stylesheet element, | |
| 6024 * including any xsl:include element children." | |
| 6025 */ | |
| 6026 while ((cur != NULL) && | |
| 6027 xsltParseFindTopLevelElem(cctxt, cur, | |
| 6028 BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1) | |
| 6029 { | |
| 6030 if (xsltParseStylesheetImport(cctxt->style, cur) != 0) { | |
| 6031 cctxt->style->errors++; | |
| 6032 } | |
| 6033 cur = cur->next; | |
| 6034 } | |
| 6035 if (cur == NULL) | |
| 6036 goto exit; | |
| 6037 start = cur; | |
| 6038 /* | |
| 6039 * Pre-process all xsl:include elements. | |
| 6040 */ | |
| 6041 cur = start; | |
| 6042 while ((cur != NULL) && | |
| 6043 xsltParseFindTopLevelElem(cctxt, cur, | |
| 6044 BAD_CAST "include", XSLT_NAMESPACE, 0, &cur) == 1) | |
| 6045 { | |
| 6046 xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_INCLUDE); | |
| 6047 cur = cur->next; | |
| 6048 } | |
| 6049 /* | |
| 6050 * Pre-process all xsl:namespace-alias elements. | |
| 6051 * URGENT TODO: This won't work correctly: the order of included | |
| 6052 * aliases and aliases defined here is significant. | |
| 6053 */ | |
| 6054 cur = start; | |
| 6055 while ((cur != NULL) && | |
| 6056 xsltParseFindTopLevelElem(cctxt, cur, | |
| 6057 BAD_CAST "namespace-alias", XSLT_NAMESPACE, 0, &cur) == 1) | |
| 6058 { | |
| 6059 xsltNamespaceAlias(cctxt->style, cur); | |
| 6060 cur = cur->next; | |
| 6061 } | |
| 6062 | |
| 6063 if (cctxt->isInclude) { | |
| 6064 /* | |
| 6065 * If this stylesheet is intended for inclusion, then | |
| 6066 * we will process only imports and includes. | |
| 6067 */ | |
| 6068 goto exit; | |
| 6069 } | |
| 6070 /* | |
| 6071 * Now parse the rest of the top-level elements. | |
| 6072 */ | |
| 6073 xsltParseXSLTStylesheetElemCore(cctxt, node); | |
| 6074 exit: | |
| 6075 | |
| 6076 return(0); | |
| 6077 } | |
| 6078 | |
| 6079 #else /* XSLT_REFACTORED */ | |
| 6080 | |
| 6081 /** | |
| 6082 * xsltParseStylesheetTop: | |
| 6083 * @style: the XSLT stylesheet | |
| 6084 * @top: the top level "stylesheet" or "transform" element | |
| 6085 * | |
| 6086 * scan the top level elements of an XSL stylesheet | |
| 6087 */ | |
| 6088 static void | |
| 6089 xsltParseStylesheetTop(xsltStylesheetPtr style, xmlNodePtr top) { | |
| 6090 xmlNodePtr cur; | |
| 6091 xmlChar *prop; | |
| 6092 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6093 int templates = 0; | |
| 6094 #endif | |
| 6095 | |
| 6096 if ((top == NULL) || (top->type != XML_ELEMENT_NODE)) | |
| 6097 return; | |
| 6098 | |
| 6099 prop = xmlGetNsProp(top, (const xmlChar *)"version", NULL); | |
| 6100 if (prop == NULL) { | |
| 6101 xsltTransformError(NULL, style, top, | |
| 6102 "xsl:version is missing: document may not be a stylesheet\n"); | |
| 6103 if (style != NULL) style->warnings++; | |
| 6104 } else { | |
| 6105 if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) && | |
| 6106 (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) { | |
| 6107 xsltTransformError(NULL, style, top, | |
| 6108 "xsl:version: only 1.1 features are supported\n"); | |
| 6109 if (style != NULL) { | |
| 6110 style->forwards_compatible = 1; | |
| 6111 style->warnings++; | |
| 6112 } | |
| 6113 } | |
| 6114 xmlFree(prop); | |
| 6115 } | |
| 6116 | |
| 6117 /* | |
| 6118 * process xsl:import elements | |
| 6119 */ | |
| 6120 cur = top->children; | |
| 6121 while (cur != NULL) { | |
| 6122 if (IS_BLANK_NODE(cur)) { | |
| 6123 cur = cur->next; | |
| 6124 continue; | |
| 6125 } | |
| 6126 if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "import")) { | |
| 6127 if (xsltParseStylesheetImport(style, cur) != 0) | |
| 6128 if (style != NULL) style->errors++; | |
| 6129 } else | |
| 6130 break; | |
| 6131 cur = cur->next; | |
| 6132 } | |
| 6133 | |
| 6134 /* | |
| 6135 * process other top-level elements | |
| 6136 */ | |
| 6137 while (cur != NULL) { | |
| 6138 if (IS_BLANK_NODE(cur)) { | |
| 6139 cur = cur->next; | |
| 6140 continue; | |
| 6141 } | |
| 6142 if (cur->type == XML_TEXT_NODE) { | |
| 6143 if (cur->content != NULL) { | |
| 6144 xsltTransformError(NULL, style, cur, | |
| 6145 "misplaced text node: '%s'\n", cur->content); | |
| 6146 } | |
| 6147 if (style != NULL) style->errors++; | |
| 6148 cur = cur->next; | |
| 6149 continue; | |
| 6150 } | |
| 6151 if ((cur->type == XML_ELEMENT_NODE) && (cur->ns == NULL)) { | |
| 6152 xsltGenericError(xsltGenericErrorContext, | |
| 6153 "Found a top-level element %s with null namespace URI\n", | |
| 6154 cur->name); | |
| 6155 if (style != NULL) style->errors++; | |
| 6156 cur = cur->next; | |
| 6157 continue; | |
| 6158 } | |
| 6159 if ((cur->type == XML_ELEMENT_NODE) && (!(IS_XSLT_ELEM(cur)))) { | |
| 6160 xsltTopLevelFunction function; | |
| 6161 | |
| 6162 function = xsltExtModuleTopLevelLookup(cur->name, | |
| 6163 cur->ns->href); | |
| 6164 if (function != NULL) | |
| 6165 function(style, cur); | |
| 6166 | |
| 6167 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6168 xsltGenericDebug(xsltGenericDebugContext, | |
| 6169 "xsltParseStylesheetTop : found foreign element %s\n", | |
| 6170 cur->name); | |
| 6171 #endif | |
| 6172 cur = cur->next; | |
| 6173 continue; | |
| 6174 } | |
| 6175 if (IS_XSLT_NAME(cur, "import")) { | |
| 6176 xsltTransformError(NULL, style, cur, | |
| 6177 "xsltParseStylesheetTop: ignoring misplaced import eleme
nt\n"); | |
| 6178 if (style != NULL) style->errors++; | |
| 6179 } else if (IS_XSLT_NAME(cur, "include")) { | |
| 6180 if (xsltParseStylesheetInclude(style, cur) != 0) | |
| 6181 if (style != NULL) style->errors++; | |
| 6182 } else if (IS_XSLT_NAME(cur, "strip-space")) { | |
| 6183 xsltParseStylesheetStripSpace(style, cur); | |
| 6184 } else if (IS_XSLT_NAME(cur, "preserve-space")) { | |
| 6185 xsltParseStylesheetPreserveSpace(style, cur); | |
| 6186 } else if (IS_XSLT_NAME(cur, "output")) { | |
| 6187 xsltParseStylesheetOutput(style, cur); | |
| 6188 } else if (IS_XSLT_NAME(cur, "key")) { | |
| 6189 xsltParseStylesheetKey(style, cur); | |
| 6190 } else if (IS_XSLT_NAME(cur, "decimal-format")) { | |
| 6191 xsltParseStylesheetDecimalFormat(style, cur); | |
| 6192 } else if (IS_XSLT_NAME(cur, "attribute-set")) { | |
| 6193 xsltParseStylesheetAttributeSet(style, cur); | |
| 6194 } else if (IS_XSLT_NAME(cur, "variable")) { | |
| 6195 xsltParseGlobalVariable(style, cur); | |
| 6196 } else if (IS_XSLT_NAME(cur, "param")) { | |
| 6197 xsltParseGlobalParam(style, cur); | |
| 6198 } else if (IS_XSLT_NAME(cur, "template")) { | |
| 6199 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6200 templates++; | |
| 6201 #endif | |
| 6202 xsltParseStylesheetTemplate(style, cur); | |
| 6203 } else if (IS_XSLT_NAME(cur, "namespace-alias")) { | |
| 6204 xsltNamespaceAlias(style, cur); | |
| 6205 } else { | |
| 6206 if ((style != NULL) && (style->forwards_compatible == 0)) { | |
| 6207 xsltTransformError(NULL, style, cur, | |
| 6208 "xsltParseStylesheetTop: unknown %s element\n", | |
| 6209 cur->name); | |
| 6210 if (style != NULL) style->errors++; | |
| 6211 } | |
| 6212 } | |
| 6213 cur = cur->next; | |
| 6214 } | |
| 6215 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6216 xsltGenericDebug(xsltGenericDebugContext, | |
| 6217 "parsed %d templates\n", templates); | |
| 6218 #endif | |
| 6219 } | |
| 6220 | |
| 6221 #endif /* else of XSLT_REFACTORED */ | |
| 6222 | |
| 6223 #ifdef XSLT_REFACTORED | |
| 6224 /** | |
| 6225 * xsltParseSimplifiedStylesheetTree: | |
| 6226 * | |
| 6227 * @style: the stylesheet (TODO: Change this to the compiler context) | |
| 6228 * @doc: the document containing the stylesheet. | |
| 6229 * @node: the node where the stylesheet is rooted at | |
| 6230 * | |
| 6231 * Returns 0 in case of success, a positive result if an error occurred | |
| 6232 * and -1 on API and internal errors. | |
| 6233 */ | |
| 6234 static int | |
| 6235 xsltParseSimplifiedStylesheetTree(xsltCompilerCtxtPtr cctxt, | |
| 6236 xmlDocPtr doc, | |
| 6237 xmlNodePtr node) | |
| 6238 { | |
| 6239 xsltTemplatePtr templ; | |
| 6240 | |
| 6241 if ((cctxt == NULL) || (node == NULL)) | |
| 6242 return(-1); | |
| 6243 | |
| 6244 if (xsltParseAttrXSLTVersion(cctxt, node, 0) == XSLT_ELEMENT_CATEGORY_LRE) | |
| 6245 { | |
| 6246 /* | |
| 6247 * TODO: Adjust report, since this might be an | |
| 6248 * embedded stylesheet. | |
| 6249 */ | |
| 6250 xsltTransformError(NULL, cctxt->style, node, | |
| 6251 "The attribute 'xsl:version' is missing; cannot identify " | |
| 6252 "this document as an XSLT stylesheet document.\n"); | |
| 6253 cctxt->style->errors++; | |
| 6254 return(1); | |
| 6255 } | |
| 6256 | |
| 6257 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6258 xsltGenericDebug(xsltGenericDebugContext, | |
| 6259 "xsltParseSimplifiedStylesheetTree: document is stylesheet\n"); | |
| 6260 #endif | |
| 6261 | |
| 6262 /* | |
| 6263 * Create and link the template | |
| 6264 */ | |
| 6265 templ = xsltNewTemplate(); | |
| 6266 if (templ == NULL) { | |
| 6267 return(-1); | |
| 6268 } | |
| 6269 templ->next = cctxt->style->templates; | |
| 6270 cctxt->style->templates = templ; | |
| 6271 templ->match = xmlStrdup(BAD_CAST "/"); | |
| 6272 | |
| 6273 /* | |
| 6274 * Note that we push the document-node in this special case. | |
| 6275 */ | |
| 6276 xsltCompilerNodePush(cctxt, (xmlNodePtr) doc); | |
| 6277 /* | |
| 6278 * In every case, we need to have | |
| 6279 * the in-scope namespaces of the element, where the | |
| 6280 * stylesheet is rooted at, regardless if it's an XSLT | |
| 6281 * instruction or a literal result instruction (or if | |
| 6282 * this is an embedded stylesheet). | |
| 6283 */ | |
| 6284 cctxt->inode->inScopeNs = | |
| 6285 xsltCompilerBuildInScopeNsList(cctxt, node); | |
| 6286 /* | |
| 6287 * Parse the content and register the match-pattern. | |
| 6288 */ | |
| 6289 xsltParseSequenceConstructor(cctxt, node); | |
| 6290 xsltCompilerNodePop(cctxt, (xmlNodePtr) doc); | |
| 6291 | |
| 6292 templ->elem = (xmlNodePtr) doc; | |
| 6293 templ->content = node; | |
| 6294 xsltAddTemplate(cctxt->style, templ, NULL, NULL); | |
| 6295 cctxt->style->literal_result = 1; | |
| 6296 return(0); | |
| 6297 } | |
| 6298 | |
| 6299 #ifdef XSLT_REFACTORED_XSLT_NSCOMP | |
| 6300 /** | |
| 6301 * xsltRestoreDocumentNamespaces: | |
| 6302 * @ns: map of namespaces | |
| 6303 * @doc: the document | |
| 6304 * | |
| 6305 * Restore the namespaces for the document | |
| 6306 * | |
| 6307 * Returns 0 in case of success, -1 in case of failure | |
| 6308 */ | |
| 6309 int | |
| 6310 xsltRestoreDocumentNamespaces(xsltNsMapPtr ns, xmlDocPtr doc) | |
| 6311 { | |
| 6312 if (doc == NULL) | |
| 6313 return(-1); | |
| 6314 /* | |
| 6315 * Revert the changes we have applied to the namespace-URIs of | |
| 6316 * ns-decls. | |
| 6317 */ | |
| 6318 while (ns != NULL) { | |
| 6319 if ((ns->doc == doc) && (ns->ns != NULL)) { | |
| 6320 ns->ns->href = ns->origNsName; | |
| 6321 ns->origNsName = NULL; | |
| 6322 ns->ns = NULL; | |
| 6323 } | |
| 6324 ns = ns->next; | |
| 6325 } | |
| 6326 return(0); | |
| 6327 } | |
| 6328 #endif /* XSLT_REFACTORED_XSLT_NSCOMP */ | |
| 6329 | |
| 6330 /** | |
| 6331 * xsltParseStylesheetProcess: | |
| 6332 * @style: the XSLT stylesheet (the current stylesheet-level) | |
| 6333 * @doc: and xmlDoc parsed XML | |
| 6334 * | |
| 6335 * Parses an XSLT stylesheet, adding the associated structures. | |
| 6336 * Called by: | |
| 6337 * xsltParseStylesheetImportedDoc() (xslt.c) | |
| 6338 * xsltParseStylesheetInclude() (imports.c) | |
| 6339 * | |
| 6340 * Returns the value of the @style parameter if everything | |
| 6341 * went right, NULL if something went amiss. | |
| 6342 */ | |
| 6343 xsltStylesheetPtr | |
| 6344 xsltParseStylesheetProcess(xsltStylesheetPtr style, xmlDocPtr doc) | |
| 6345 { | |
| 6346 xsltCompilerCtxtPtr cctxt; | |
| 6347 xmlNodePtr cur; | |
| 6348 int oldIsSimplifiedStylesheet; | |
| 6349 | |
| 6350 xsltInitGlobals(); | |
| 6351 | |
| 6352 if ((style == NULL) || (doc == NULL)) | |
| 6353 return(NULL); | |
| 6354 | |
| 6355 cctxt = XSLT_CCTXT(style); | |
| 6356 | |
| 6357 cur = xmlDocGetRootElement(doc); | |
| 6358 if (cur == NULL) { | |
| 6359 xsltTransformError(NULL, style, (xmlNodePtr) doc, | |
| 6360 "xsltParseStylesheetProcess : empty stylesheet\n"); | |
| 6361 return(NULL); | |
| 6362 } | |
| 6363 oldIsSimplifiedStylesheet = cctxt->simplified; | |
| 6364 | |
| 6365 if ((IS_XSLT_ELEM(cur)) && | |
| 6366 ((IS_XSLT_NAME(cur, "stylesheet")) || | |
| 6367 (IS_XSLT_NAME(cur, "transform")))) { | |
| 6368 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6369 xsltGenericDebug(xsltGenericDebugContext, | |
| 6370 "xsltParseStylesheetProcess : found stylesheet\n"); | |
| 6371 #endif | |
| 6372 cctxt->simplified = 0; | |
| 6373 style->literal_result = 0; | |
| 6374 } else { | |
| 6375 cctxt->simplified = 1; | |
| 6376 style->literal_result = 1; | |
| 6377 } | |
| 6378 /* | |
| 6379 * Pre-process the stylesheet if not already done before. | |
| 6380 * This will remove PIs and comments, merge adjacent | |
| 6381 * text nodes, internalize strings, etc. | |
| 6382 */ | |
| 6383 if (! style->nopreproc) | |
| 6384 xsltParsePreprocessStylesheetTree(cctxt, cur); | |
| 6385 /* | |
| 6386 * Parse and compile the stylesheet. | |
| 6387 */ | |
| 6388 if (style->literal_result == 0) { | |
| 6389 if (xsltParseXSLTStylesheetElem(cctxt, cur) != 0) | |
| 6390 return(NULL); | |
| 6391 } else { | |
| 6392 if (xsltParseSimplifiedStylesheetTree(cctxt, doc, cur) != 0) | |
| 6393 return(NULL); | |
| 6394 } | |
| 6395 | |
| 6396 cctxt->simplified = oldIsSimplifiedStylesheet; | |
| 6397 | |
| 6398 return(style); | |
| 6399 } | |
| 6400 | |
| 6401 #else /* XSLT_REFACTORED */ | |
| 6402 | |
| 6403 /** | |
| 6404 * xsltParseStylesheetProcess: | |
| 6405 * @ret: the XSLT stylesheet (the current stylesheet-level) | |
| 6406 * @doc: and xmlDoc parsed XML | |
| 6407 * | |
| 6408 * Parses an XSLT stylesheet, adding the associated structures. | |
| 6409 * Called by: | |
| 6410 * xsltParseStylesheetImportedDoc() (xslt.c) | |
| 6411 * xsltParseStylesheetInclude() (imports.c) | |
| 6412 * | |
| 6413 * Returns the value of the @style parameter if everything | |
| 6414 * went right, NULL if something went amiss. | |
| 6415 */ | |
| 6416 xsltStylesheetPtr | |
| 6417 xsltParseStylesheetProcess(xsltStylesheetPtr ret, xmlDocPtr doc) { | |
| 6418 xmlNodePtr cur; | |
| 6419 | |
| 6420 xsltInitGlobals(); | |
| 6421 | |
| 6422 if (doc == NULL) | |
| 6423 return(NULL); | |
| 6424 if (ret == NULL) | |
| 6425 return(ret); | |
| 6426 | |
| 6427 /* | |
| 6428 * First steps, remove blank nodes, | |
| 6429 * locate the xsl:stylesheet element and the | |
| 6430 * namespace declaration. | |
| 6431 */ | |
| 6432 cur = xmlDocGetRootElement(doc); | |
| 6433 if (cur == NULL) { | |
| 6434 xsltTransformError(NULL, ret, (xmlNodePtr) doc, | |
| 6435 "xsltParseStylesheetProcess : empty stylesheet\n"); | |
| 6436 return(NULL); | |
| 6437 } | |
| 6438 | |
| 6439 if ((IS_XSLT_ELEM(cur)) && | |
| 6440 ((IS_XSLT_NAME(cur, "stylesheet")) || | |
| 6441 (IS_XSLT_NAME(cur, "transform")))) { | |
| 6442 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6443 xsltGenericDebug(xsltGenericDebugContext, | |
| 6444 "xsltParseStylesheetProcess : found stylesheet\n"); | |
| 6445 #endif | |
| 6446 ret->literal_result = 0; | |
| 6447 xsltParseStylesheetExcludePrefix(ret, cur, 1); | |
| 6448 xsltParseStylesheetExtPrefix(ret, cur, 1); | |
| 6449 } else { | |
| 6450 xsltParseStylesheetExcludePrefix(ret, cur, 0); | |
| 6451 xsltParseStylesheetExtPrefix(ret, cur, 0); | |
| 6452 ret->literal_result = 1; | |
| 6453 } | |
| 6454 if (!ret->nopreproc) { | |
| 6455 xsltPreprocessStylesheet(ret, cur); | |
| 6456 } | |
| 6457 if (ret->literal_result == 0) { | |
| 6458 xsltParseStylesheetTop(ret, cur); | |
| 6459 } else { | |
| 6460 xmlChar *prop; | |
| 6461 xsltTemplatePtr template; | |
| 6462 | |
| 6463 /* | |
| 6464 * the document itself might be the template, check xsl:version | |
| 6465 */ | |
| 6466 prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE); | |
| 6467 if (prop == NULL) { | |
| 6468 xsltTransformError(NULL, ret, cur, | |
| 6469 "xsltParseStylesheetProcess : document is not a stylesheet\n"); | |
| 6470 return(NULL); | |
| 6471 } | |
| 6472 | |
| 6473 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6474 xsltGenericDebug(xsltGenericDebugContext, | |
| 6475 "xsltParseStylesheetProcess : document is stylesheet\n"); | |
| 6476 #endif | |
| 6477 | |
| 6478 if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) && | |
| 6479 (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) { | |
| 6480 xsltTransformError(NULL, ret, cur, | |
| 6481 "xsl:version: only 1.1 features are supported\n"); | |
| 6482 ret->forwards_compatible = 1; | |
| 6483 ret->warnings++; | |
| 6484 } | |
| 6485 xmlFree(prop); | |
| 6486 | |
| 6487 /* | |
| 6488 * Create and link the template | |
| 6489 */ | |
| 6490 template = xsltNewTemplate(); | |
| 6491 if (template == NULL) { | |
| 6492 return(NULL); | |
| 6493 } | |
| 6494 template->next = ret->templates; | |
| 6495 ret->templates = template; | |
| 6496 template->match = xmlStrdup((const xmlChar *)"/"); | |
| 6497 | |
| 6498 /* | |
| 6499 * parse the content and register the pattern | |
| 6500 */ | |
| 6501 xsltParseTemplateContent(ret, (xmlNodePtr) doc); | |
| 6502 template->elem = (xmlNodePtr) doc; | |
| 6503 template->content = doc->children; | |
| 6504 xsltAddTemplate(ret, template, NULL, NULL); | |
| 6505 ret->literal_result = 1; | |
| 6506 } | |
| 6507 | |
| 6508 return(ret); | |
| 6509 } | |
| 6510 | |
| 6511 #endif /* else of XSLT_REFACTORED */ | |
| 6512 | |
| 6513 /** | |
| 6514 * xsltParseStylesheetImportedDoc: | |
| 6515 * @doc: an xmlDoc parsed XML | |
| 6516 * @parentStyle: pointer to the parent stylesheet (if it exists) | |
| 6517 * | |
| 6518 * parse an XSLT stylesheet building the associated structures | |
| 6519 * except the processing not needed for imported documents. | |
| 6520 * | |
| 6521 * Returns a new XSLT stylesheet structure. | |
| 6522 */ | |
| 6523 | |
| 6524 xsltStylesheetPtr | |
| 6525 xsltParseStylesheetImportedDoc(xmlDocPtr doc, | |
| 6526 xsltStylesheetPtr parentStyle) { | |
| 6527 xsltStylesheetPtr retStyle; | |
| 6528 | |
| 6529 if (doc == NULL) | |
| 6530 return(NULL); | |
| 6531 | |
| 6532 retStyle = xsltNewStylesheet(); | |
| 6533 if (retStyle == NULL) | |
| 6534 return(NULL); | |
| 6535 /* | |
| 6536 * Set the importing stylesheet module; also used to detect recursion. | |
| 6537 */ | |
| 6538 retStyle->parent = parentStyle; | |
| 6539 /* | |
| 6540 * Adjust the string dict. | |
| 6541 */ | |
| 6542 if (doc->dict != NULL) { | |
| 6543 xmlDictFree(retStyle->dict); | |
| 6544 retStyle->dict = doc->dict; | |
| 6545 #ifdef WITH_XSLT_DEBUG | |
| 6546 xsltGenericDebug(xsltGenericDebugContext, | |
| 6547 "reusing dictionary from %s for stylesheet\n", | |
| 6548 doc->URL); | |
| 6549 #endif | |
| 6550 xmlDictReference(retStyle->dict); | |
| 6551 } | |
| 6552 | |
| 6553 /* | |
| 6554 * TODO: Eliminate xsltGatherNamespaces(); we must not restrict | |
| 6555 * the stylesheet to containt distinct namespace prefixes. | |
| 6556 */ | |
| 6557 xsltGatherNamespaces(retStyle); | |
| 6558 | |
| 6559 #ifdef XSLT_REFACTORED | |
| 6560 { | |
| 6561 xsltCompilerCtxtPtr cctxt; | |
| 6562 xsltStylesheetPtr oldCurSheet; | |
| 6563 | |
| 6564 if (parentStyle == NULL) { | |
| 6565 xsltPrincipalStylesheetDataPtr principalData; | |
| 6566 /* | |
| 6567 * Principal stylesheet | |
| 6568 * -------------------- | |
| 6569 */ | |
| 6570 retStyle->principal = retStyle; | |
| 6571 /* | |
| 6572 * Create extra data for the principal stylesheet. | |
| 6573 */ | |
| 6574 principalData = xsltNewPrincipalStylesheetData(); | |
| 6575 if (principalData == NULL) { | |
| 6576 xsltFreeStylesheet(retStyle); | |
| 6577 return(NULL); | |
| 6578 } | |
| 6579 retStyle->principalData = principalData; | |
| 6580 /* | |
| 6581 * Create the compilation context | |
| 6582 * ------------------------------ | |
| 6583 * (only once; for the principal stylesheet). | |
| 6584 * This is currently the only function where the | |
| 6585 * compilation context is created. | |
| 6586 */ | |
| 6587 cctxt = xsltCompilationCtxtCreate(retStyle); | |
| 6588 if (cctxt == NULL) { | |
| 6589 xsltFreeStylesheet(retStyle); | |
| 6590 return(NULL); | |
| 6591 } | |
| 6592 retStyle->compCtxt = (void *) cctxt; | |
| 6593 cctxt->style = retStyle; | |
| 6594 cctxt->dict = retStyle->dict; | |
| 6595 cctxt->psData = principalData; | |
| 6596 /* | |
| 6597 * Push initial dummy node info. | |
| 6598 */ | |
| 6599 cctxt->depth = -1; | |
| 6600 xsltCompilerNodePush(cctxt, (xmlNodePtr) doc); | |
| 6601 } else { | |
| 6602 /* | |
| 6603 * Imported stylesheet. | |
| 6604 */ | |
| 6605 retStyle->principal = parentStyle->principal; | |
| 6606 cctxt = parentStyle->compCtxt; | |
| 6607 retStyle->compCtxt = cctxt; | |
| 6608 } | |
| 6609 /* | |
| 6610 * Save the old and set the current stylesheet structure in the | |
| 6611 * compilation context. | |
| 6612 */ | |
| 6613 oldCurSheet = cctxt->style; | |
| 6614 cctxt->style = retStyle; | |
| 6615 | |
| 6616 retStyle->doc = doc; | |
| 6617 xsltParseStylesheetProcess(retStyle, doc); | |
| 6618 | |
| 6619 cctxt->style = oldCurSheet; | |
| 6620 if (parentStyle == NULL) { | |
| 6621 /* | |
| 6622 * Pop the initial dummy node info. | |
| 6623 */ | |
| 6624 xsltCompilerNodePop(cctxt, (xmlNodePtr) doc); | |
| 6625 } else { | |
| 6626 /* | |
| 6627 * Clear the compilation context of imported | |
| 6628 * stylesheets. | |
| 6629 * TODO: really? | |
| 6630 */ | |
| 6631 /* retStyle->compCtxt = NULL; */ | |
| 6632 } | |
| 6633 /* | |
| 6634 * Free the stylesheet if there were errors. | |
| 6635 */ | |
| 6636 if (retStyle != NULL) { | |
| 6637 if (retStyle->errors != 0) { | |
| 6638 #ifdef XSLT_REFACTORED_XSLT_NSCOMP | |
| 6639 /* | |
| 6640 * Restore all changes made to namespace URIs of ns-decls. | |
| 6641 */ | |
| 6642 if (cctxt->psData->nsMap) | |
| 6643 xsltRestoreDocumentNamespaces(cctxt->psData->nsMap, doc); | |
| 6644 #endif | |
| 6645 /* | |
| 6646 * Detach the doc from the stylesheet; otherwise the doc | |
| 6647 * will be freed in xsltFreeStylesheet(). | |
| 6648 */ | |
| 6649 retStyle->doc = NULL; | |
| 6650 /* | |
| 6651 * Cleanup the doc if its the main stylesheet. | |
| 6652 */ | |
| 6653 if (parentStyle == NULL) { | |
| 6654 xsltCleanupStylesheetTree(doc, xmlDocGetRootElement(doc)); | |
| 6655 if (retStyle->compCtxt != NULL) { | |
| 6656 xsltCompilationCtxtFree(retStyle->compCtxt); | |
| 6657 retStyle->compCtxt = NULL; | |
| 6658 } | |
| 6659 } | |
| 6660 | |
| 6661 xsltFreeStylesheet(retStyle); | |
| 6662 retStyle = NULL; | |
| 6663 } | |
| 6664 } | |
| 6665 } | |
| 6666 | |
| 6667 #else /* XSLT_REFACTORED */ | |
| 6668 /* | |
| 6669 * Old behaviour. | |
| 6670 */ | |
| 6671 retStyle->doc = doc; | |
| 6672 if (xsltParseStylesheetProcess(retStyle, doc) == NULL) { | |
| 6673 retStyle->doc = NULL; | |
| 6674 xsltFreeStylesheet(retStyle); | |
| 6675 retStyle = NULL; | |
| 6676 } | |
| 6677 if (retStyle != NULL) { | |
| 6678 if (retStyle->errors != 0) { | |
| 6679 retStyle->doc = NULL; | |
| 6680 if (parentStyle == NULL) | |
| 6681 xsltCleanupStylesheetTree(doc, | |
| 6682 xmlDocGetRootElement(doc)); | |
| 6683 xsltFreeStylesheet(retStyle); | |
| 6684 retStyle = NULL; | |
| 6685 } | |
| 6686 } | |
| 6687 #endif /* else of XSLT_REFACTORED */ | |
| 6688 | |
| 6689 return(retStyle); | |
| 6690 } | |
| 6691 | |
| 6692 /** | |
| 6693 * xsltParseStylesheetDoc: | |
| 6694 * @doc: and xmlDoc parsed XML | |
| 6695 * | |
| 6696 * parse an XSLT stylesheet, building the associated structures. doc | |
| 6697 * is kept as a reference within the returned stylesheet, so changes | |
| 6698 * to doc after the parsing will be reflected when the stylesheet | |
| 6699 * is applied, and the doc is automatically freed when the | |
| 6700 * stylesheet is closed. | |
| 6701 * | |
| 6702 * Returns a new XSLT stylesheet structure. | |
| 6703 */ | |
| 6704 | |
| 6705 xsltStylesheetPtr | |
| 6706 xsltParseStylesheetDoc(xmlDocPtr doc) { | |
| 6707 xsltStylesheetPtr ret; | |
| 6708 | |
| 6709 xsltInitGlobals(); | |
| 6710 | |
| 6711 ret = xsltParseStylesheetImportedDoc(doc, NULL); | |
| 6712 if (ret == NULL) | |
| 6713 return(NULL); | |
| 6714 | |
| 6715 xsltResolveStylesheetAttributeSet(ret); | |
| 6716 #ifdef XSLT_REFACTORED | |
| 6717 /* | |
| 6718 * Free the compilation context. | |
| 6719 * TODO: Check if it's better to move this cleanup to | |
| 6720 * xsltParseStylesheetImportedDoc(). | |
| 6721 */ | |
| 6722 if (ret->compCtxt != NULL) { | |
| 6723 xsltCompilationCtxtFree(XSLT_CCTXT(ret)); | |
| 6724 ret->compCtxt = NULL; | |
| 6725 } | |
| 6726 #endif | |
| 6727 return(ret); | |
| 6728 } | |
| 6729 | |
| 6730 /** | |
| 6731 * xsltParseStylesheetFile: | |
| 6732 * @filename: the filename/URL to the stylesheet | |
| 6733 * | |
| 6734 * Load and parse an XSLT stylesheet | |
| 6735 * | |
| 6736 * Returns a new XSLT stylesheet structure. | |
| 6737 */ | |
| 6738 | |
| 6739 xsltStylesheetPtr | |
| 6740 xsltParseStylesheetFile(const xmlChar* filename) { | |
| 6741 xsltSecurityPrefsPtr sec; | |
| 6742 xsltStylesheetPtr ret; | |
| 6743 xmlDocPtr doc; | |
| 6744 | |
| 6745 xsltInitGlobals(); | |
| 6746 | |
| 6747 if (filename == NULL) | |
| 6748 return(NULL); | |
| 6749 | |
| 6750 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6751 xsltGenericDebug(xsltGenericDebugContext, | |
| 6752 "xsltParseStylesheetFile : parse %s\n", filename); | |
| 6753 #endif | |
| 6754 | |
| 6755 /* | |
| 6756 * Security framework check | |
| 6757 */ | |
| 6758 sec = xsltGetDefaultSecurityPrefs(); | |
| 6759 if (sec != NULL) { | |
| 6760 int res; | |
| 6761 | |
| 6762 res = xsltCheckRead(sec, NULL, filename); | |
| 6763 if (res == 0) { | |
| 6764 xsltTransformError(NULL, NULL, NULL, | |
| 6765 "xsltParseStylesheetFile: read rights for %s denied\n", | |
| 6766 filename); | |
| 6767 return(NULL); | |
| 6768 } | |
| 6769 } | |
| 6770 | |
| 6771 doc = xsltDocDefaultLoader(filename, NULL, XSLT_PARSE_OPTIONS, | |
| 6772 NULL, XSLT_LOAD_START); | |
| 6773 if (doc == NULL) { | |
| 6774 xsltTransformError(NULL, NULL, NULL, | |
| 6775 "xsltParseStylesheetFile : cannot parse %s\n", filename); | |
| 6776 return(NULL); | |
| 6777 } | |
| 6778 ret = xsltParseStylesheetDoc(doc); | |
| 6779 if (ret == NULL) { | |
| 6780 xmlFreeDoc(doc); | |
| 6781 return(NULL); | |
| 6782 } | |
| 6783 | |
| 6784 return(ret); | |
| 6785 } | |
| 6786 | |
| 6787 /************************************************************************ | |
| 6788 * * | |
| 6789 * Handling of Stylesheet PI * | |
| 6790 * * | |
| 6791 ************************************************************************/ | |
| 6792 | |
| 6793 #define CUR (*cur) | |
| 6794 #define SKIP(val) cur += (val) | |
| 6795 #define NXT(val) cur[(val)] | |
| 6796 #define SKIP_BLANKS \ | |
| 6797 while (IS_BLANK(CUR)) NEXT | |
| 6798 #define NEXT ((*cur) ? cur++ : cur) | |
| 6799 | |
| 6800 /** | |
| 6801 * xsltParseStylesheetPI: | |
| 6802 * @value: the value of the PI | |
| 6803 * | |
| 6804 * This function checks that the type is text/xml and extracts | |
| 6805 * the URI-Reference for the stylesheet | |
| 6806 * | |
| 6807 * Returns the URI-Reference for the stylesheet or NULL (it need to | |
| 6808 * be freed by the caller) | |
| 6809 */ | |
| 6810 static xmlChar * | |
| 6811 xsltParseStylesheetPI(const xmlChar *value) { | |
| 6812 const xmlChar *cur; | |
| 6813 const xmlChar *start; | |
| 6814 xmlChar *val; | |
| 6815 xmlChar tmp; | |
| 6816 xmlChar *href = NULL; | |
| 6817 int isXml = 0; | |
| 6818 | |
| 6819 if (value == NULL) | |
| 6820 return(NULL); | |
| 6821 | |
| 6822 cur = value; | |
| 6823 while (CUR != 0) { | |
| 6824 SKIP_BLANKS; | |
| 6825 if ((CUR == 't') && (NXT(1) == 'y') && (NXT(2) == 'p') && | |
| 6826 (NXT(3) == 'e')) { | |
| 6827 SKIP(4); | |
| 6828 SKIP_BLANKS; | |
| 6829 if (CUR != '=') | |
| 6830 continue; | |
| 6831 NEXT; | |
| 6832 if ((CUR != '\'') && (CUR != '"')) | |
| 6833 continue; | |
| 6834 tmp = CUR; | |
| 6835 NEXT; | |
| 6836 start = cur; | |
| 6837 while ((CUR != 0) && (CUR != tmp)) | |
| 6838 NEXT; | |
| 6839 if (CUR != tmp) | |
| 6840 continue; | |
| 6841 val = xmlStrndup(start, cur - start); | |
| 6842 NEXT; | |
| 6843 if (val == NULL) | |
| 6844 return(NULL); | |
| 6845 if ((xmlStrcasecmp(val, BAD_CAST "text/xml")) && | |
| 6846 (xmlStrcasecmp(val, BAD_CAST "text/xsl"))) { | |
| 6847 xmlFree(val); | |
| 6848 break; | |
| 6849 } | |
| 6850 isXml = 1; | |
| 6851 xmlFree(val); | |
| 6852 } else if ((CUR == 'h') && (NXT(1) == 'r') && (NXT(2) == 'e') && | |
| 6853 (NXT(3) == 'f')) { | |
| 6854 SKIP(4); | |
| 6855 SKIP_BLANKS; | |
| 6856 if (CUR != '=') | |
| 6857 continue; | |
| 6858 NEXT; | |
| 6859 if ((CUR != '\'') && (CUR != '"')) | |
| 6860 continue; | |
| 6861 tmp = CUR; | |
| 6862 NEXT; | |
| 6863 start = cur; | |
| 6864 while ((CUR != 0) && (CUR != tmp)) | |
| 6865 NEXT; | |
| 6866 if (CUR != tmp) | |
| 6867 continue; | |
| 6868 if (href == NULL) | |
| 6869 href = xmlStrndup(start, cur - start); | |
| 6870 NEXT; | |
| 6871 } else { | |
| 6872 while ((CUR != 0) && (!IS_BLANK(CUR))) | |
| 6873 NEXT; | |
| 6874 } | |
| 6875 | |
| 6876 } | |
| 6877 | |
| 6878 if (!isXml) { | |
| 6879 if (href != NULL) | |
| 6880 xmlFree(href); | |
| 6881 href = NULL; | |
| 6882 } | |
| 6883 return(href); | |
| 6884 } | |
| 6885 | |
| 6886 /** | |
| 6887 * xsltLoadStylesheetPI: | |
| 6888 * @doc: a document to process | |
| 6889 * | |
| 6890 * This function tries to locate the stylesheet PI in the given document | |
| 6891 * If found, and if contained within the document, it will extract | |
| 6892 * that subtree to build the stylesheet to process @doc (doc itself will | |
| 6893 * be modified). If found but referencing an external document it will | |
| 6894 * attempt to load it and generate a stylesheet from it. In both cases, | |
| 6895 * the resulting stylesheet and the document need to be freed once the | |
| 6896 * transformation is done. | |
| 6897 * | |
| 6898 * Returns a new XSLT stylesheet structure or NULL if not found. | |
| 6899 */ | |
| 6900 xsltStylesheetPtr | |
| 6901 xsltLoadStylesheetPI(xmlDocPtr doc) { | |
| 6902 xmlNodePtr child; | |
| 6903 xsltStylesheetPtr ret = NULL; | |
| 6904 xmlChar *href = NULL; | |
| 6905 xmlURIPtr URI; | |
| 6906 | |
| 6907 xsltInitGlobals(); | |
| 6908 | |
| 6909 if (doc == NULL) | |
| 6910 return(NULL); | |
| 6911 | |
| 6912 /* | |
| 6913 * Find the text/xml stylesheet PI id any before the root | |
| 6914 */ | |
| 6915 child = doc->children; | |
| 6916 while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) { | |
| 6917 if ((child->type == XML_PI_NODE) && | |
| 6918 (xmlStrEqual(child->name, BAD_CAST "xml-stylesheet"))) { | |
| 6919 href = xsltParseStylesheetPI(child->content); | |
| 6920 if (href != NULL) | |
| 6921 break; | |
| 6922 } | |
| 6923 child = child->next; | |
| 6924 } | |
| 6925 | |
| 6926 /* | |
| 6927 * If found check the href to select processing | |
| 6928 */ | |
| 6929 if (href != NULL) { | |
| 6930 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6931 xsltGenericDebug(xsltGenericDebugContext, | |
| 6932 "xsltLoadStylesheetPI : found PI href=%s\n", href); | |
| 6933 #endif | |
| 6934 URI = xmlParseURI((const char *) href); | |
| 6935 if (URI == NULL) { | |
| 6936 xsltTransformError(NULL, NULL, child, | |
| 6937 "xml-stylesheet : href %s is not valid\n", href); | |
| 6938 xmlFree(href); | |
| 6939 return(NULL); | |
| 6940 } | |
| 6941 if ((URI->fragment != NULL) && (URI->scheme == NULL) && | |
| 6942 (URI->opaque == NULL) && (URI->authority == NULL) && | |
| 6943 (URI->server == NULL) && (URI->user == NULL) && | |
| 6944 (URI->path == NULL) && (URI->query == NULL)) { | |
| 6945 xmlAttrPtr ID; | |
| 6946 | |
| 6947 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 6948 xsltGenericDebug(xsltGenericDebugContext, | |
| 6949 "xsltLoadStylesheetPI : Reference to ID %s\n", href); | |
| 6950 #endif | |
| 6951 if (URI->fragment[0] == '#') | |
| 6952 ID = xmlGetID(doc, (const xmlChar *) &(URI->fragment[1])); | |
| 6953 else | |
| 6954 ID = xmlGetID(doc, (const xmlChar *) URI->fragment); | |
| 6955 if (ID == NULL) { | |
| 6956 xsltTransformError(NULL, NULL, child, | |
| 6957 "xml-stylesheet : no ID %s found\n", URI->fragment); | |
| 6958 } else { | |
| 6959 xmlDocPtr fake; | |
| 6960 xmlNodePtr subtree, newtree; | |
| 6961 xmlNsPtr ns; | |
| 6962 | |
| 6963 #ifdef WITH_XSLT_DEBUG | |
| 6964 xsltGenericDebug(xsltGenericDebugContext, | |
| 6965 "creating new document from %s for embedded stylesheet\n", | |
| 6966 doc->URL); | |
| 6967 #endif | |
| 6968 /* | |
| 6969 * move the subtree in a new document passed to | |
| 6970 * the stylesheet analyzer | |
| 6971 */ | |
| 6972 subtree = ID->parent; | |
| 6973 fake = xmlNewDoc(NULL); | |
| 6974 if (fake != NULL) { | |
| 6975 /* | |
| 6976 * Should the dictionary still be shared even though | |
| 6977 * the nodes are being copied rather than moved? | |
| 6978 */ | |
| 6979 fake->dict = doc->dict; | |
| 6980 xmlDictReference(doc->dict); | |
| 6981 #ifdef WITH_XSLT_DEBUG | |
| 6982 xsltGenericDebug(xsltGenericDebugContext, | |
| 6983 "reusing dictionary from %s for embedded stylesheet\n", | |
| 6984 doc->URL); | |
| 6985 #endif | |
| 6986 | |
| 6987 newtree = xmlDocCopyNode(subtree, fake, 1); | |
| 6988 | |
| 6989 fake->URL = xmlNodeGetBase(doc, subtree->parent); | |
| 6990 #ifdef WITH_XSLT_DEBUG | |
| 6991 xsltGenericDebug(xsltGenericDebugContext, | |
| 6992 "set base URI for embedded stylesheet as %s\n", | |
| 6993 fake->URL); | |
| 6994 #endif | |
| 6995 | |
| 6996 /* | |
| 6997 * Add all namespaces in scope of embedded stylesheet to | |
| 6998 * root element of newly created stylesheet document | |
| 6999 */ | |
| 7000 while ((subtree = subtree->parent) != (xmlNodePtr)doc) { | |
| 7001 for (ns = subtree->ns; ns; ns = ns->next) { | |
| 7002 xmlNewNs(newtree, ns->href, ns->prefix); | |
| 7003 } | |
| 7004 } | |
| 7005 | |
| 7006 xmlAddChild((xmlNodePtr)fake, newtree); | |
| 7007 ret = xsltParseStylesheetDoc(fake); | |
| 7008 if (ret == NULL) | |
| 7009 xmlFreeDoc(fake); | |
| 7010 } | |
| 7011 } | |
| 7012 } else { | |
| 7013 xmlChar *URL, *base; | |
| 7014 | |
| 7015 /* | |
| 7016 * Reference to an external stylesheet | |
| 7017 */ | |
| 7018 | |
| 7019 base = xmlNodeGetBase(doc, (xmlNodePtr) doc); | |
| 7020 URL = xmlBuildURI(href, base); | |
| 7021 if (URL != NULL) { | |
| 7022 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 7023 xsltGenericDebug(xsltGenericDebugContext, | |
| 7024 "xsltLoadStylesheetPI : fetching %s\n", URL); | |
| 7025 #endif | |
| 7026 ret = xsltParseStylesheetFile(URL); | |
| 7027 xmlFree(URL); | |
| 7028 } else { | |
| 7029 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 7030 xsltGenericDebug(xsltGenericDebugContext, | |
| 7031 "xsltLoadStylesheetPI : fetching %s\n", href); | |
| 7032 #endif | |
| 7033 ret = xsltParseStylesheetFile(href); | |
| 7034 } | |
| 7035 if (base != NULL) | |
| 7036 xmlFree(base); | |
| 7037 } | |
| 7038 xmlFreeURI(URI); | |
| 7039 xmlFree(href); | |
| 7040 } | |
| 7041 return(ret); | |
| 7042 } | |
| OLD | NEW |