| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * transform.c: Implementation of the XSL Transformation 1.0 engine | |
| 3 * transform part, i.e. applying a Stylesheet to a document | |
| 4 * | |
| 5 * References: | |
| 6 * http://www.w3.org/TR/1999/REC-xslt-19991116 | |
| 7 * | |
| 8 * Michael Kay "XSLT Programmer's Reference" pp 637-643 | |
| 9 * Writing Multiple Output Files | |
| 10 * | |
| 11 * XSLT-1.1 Working Draft | |
| 12 * http://www.w3.org/TR/xslt11#multiple-output | |
| 13 * | |
| 14 * See Copyright for the status of this software. | |
| 15 * | |
| 16 * daniel@veillard.com | |
| 17 */ | |
| 18 | |
| 19 #define IN_LIBXSLT | |
| 20 #include "libxslt.h" | |
| 21 | |
| 22 #include <string.h> | |
| 23 #include <stdio.h> | |
| 24 | |
| 25 #include <libxml/xmlmemory.h> | |
| 26 #include <libxml/parser.h> | |
| 27 #include <libxml/tree.h> | |
| 28 #include <libxml/valid.h> | |
| 29 #include <libxml/hash.h> | |
| 30 #include <libxml/encoding.h> | |
| 31 #include <libxml/xmlerror.h> | |
| 32 #include <libxml/xpath.h> | |
| 33 #include <libxml/parserInternals.h> | |
| 34 #include <libxml/xpathInternals.h> | |
| 35 #include <libxml/HTMLtree.h> | |
| 36 #include <libxml/debugXML.h> | |
| 37 #include <libxml/uri.h> | |
| 38 #include "xslt.h" | |
| 39 #include "xsltInternals.h" | |
| 40 #include "xsltutils.h" | |
| 41 #include "pattern.h" | |
| 42 #include "transform.h" | |
| 43 #include "variables.h" | |
| 44 #include "numbersInternals.h" | |
| 45 #include "namespaces.h" | |
| 46 #include "attributes.h" | |
| 47 #include "templates.h" | |
| 48 #include "imports.h" | |
| 49 #include "keys.h" | |
| 50 #include "documents.h" | |
| 51 #include "extensions.h" | |
| 52 #include "extra.h" | |
| 53 #include "preproc.h" | |
| 54 #include "security.h" | |
| 55 | |
| 56 #ifdef WITH_XSLT_DEBUG | |
| 57 #define WITH_XSLT_DEBUG_EXTRA | |
| 58 #define WITH_XSLT_DEBUG_PROCESS | |
| 59 #define WITH_XSLT_DEBUG_VARIABLE | |
| 60 #endif | |
| 61 | |
| 62 #define XSLT_GENERATE_HTML_DOCTYPE | |
| 63 #ifdef XSLT_GENERATE_HTML_DOCTYPE | |
| 64 static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, | |
| 65 const xmlChar **systemID); | |
| 66 #endif | |
| 67 | |
| 68 int xsltMaxDepth = 3000; | |
| 69 int xsltMaxVars = 15000; | |
| 70 | |
| 71 /* | |
| 72 * Useful macros | |
| 73 */ | |
| 74 | |
| 75 #ifndef FALSE | |
| 76 # define FALSE (0 == 1) | |
| 77 # define TRUE (!FALSE) | |
| 78 #endif | |
| 79 | |
| 80 #define IS_BLANK_NODE(n) \ | |
| 81 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) | |
| 82 | |
| 83 | |
| 84 /* | |
| 85 * Forward declarations | |
| 86 */ | |
| 87 | |
| 88 static xmlNsPtr | |
| 89 xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur); | |
| 90 | |
| 91 static xmlNodePtr | |
| 92 xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, | |
| 93 xmlNodePtr node, xmlNodePtr insert, int isLRE, | |
| 94 int topElemVisited); | |
| 95 | |
| 96 static void | |
| 97 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, | |
| 98 xmlNodePtr contextNode, xmlNodePtr list, | |
| 99 xsltTemplatePtr templ); | |
| 100 | |
| 101 static void | |
| 102 xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt, | |
| 103 xmlNodePtr contextNode, | |
| 104 xmlNodePtr list, | |
| 105 xsltTemplatePtr templ, | |
| 106 xsltStackElemPtr withParams); | |
| 107 | |
| 108 /** | |
| 109 * templPush: | |
| 110 * @ctxt: the transformation context | |
| 111 * @value: the template to push on the stack | |
| 112 * | |
| 113 * Push a template on the stack | |
| 114 * | |
| 115 * Returns the new index in the stack or 0 in case of error | |
| 116 */ | |
| 117 static int | |
| 118 templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value) | |
| 119 { | |
| 120 if (ctxt->templMax == 0) { | |
| 121 ctxt->templMax = 4; | |
| 122 ctxt->templTab = | |
| 123 (xsltTemplatePtr *) xmlMalloc(ctxt->templMax * | |
| 124 sizeof(ctxt->templTab[0])); | |
| 125 if (ctxt->templTab == NULL) { | |
| 126 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); | |
| 127 return (0); | |
| 128 } | |
| 129 } | |
| 130 else if (ctxt->templNr >= ctxt->templMax) { | |
| 131 ctxt->templMax *= 2; | |
| 132 ctxt->templTab = | |
| 133 (xsltTemplatePtr *) xmlRealloc(ctxt->templTab, | |
| 134 ctxt->templMax * | |
| 135 sizeof(ctxt->templTab[0])); | |
| 136 if (ctxt->templTab == NULL) { | |
| 137 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); | |
| 138 return (0); | |
| 139 } | |
| 140 } | |
| 141 ctxt->templTab[ctxt->templNr] = value; | |
| 142 ctxt->templ = value; | |
| 143 return (ctxt->templNr++); | |
| 144 } | |
| 145 /** | |
| 146 * templPop: | |
| 147 * @ctxt: the transformation context | |
| 148 * | |
| 149 * Pop a template value from the stack | |
| 150 * | |
| 151 * Returns the stored template value | |
| 152 */ | |
| 153 static xsltTemplatePtr | |
| 154 templPop(xsltTransformContextPtr ctxt) | |
| 155 { | |
| 156 xsltTemplatePtr ret; | |
| 157 | |
| 158 if (ctxt->templNr <= 0) | |
| 159 return (0); | |
| 160 ctxt->templNr--; | |
| 161 if (ctxt->templNr > 0) | |
| 162 ctxt->templ = ctxt->templTab[ctxt->templNr - 1]; | |
| 163 else | |
| 164 ctxt->templ = (xsltTemplatePtr) 0; | |
| 165 ret = ctxt->templTab[ctxt->templNr]; | |
| 166 ctxt->templTab[ctxt->templNr] = 0; | |
| 167 return (ret); | |
| 168 } | |
| 169 | |
| 170 /** | |
| 171 * xsltLocalVariablePop: | |
| 172 * @ctxt: the transformation context | |
| 173 * @limitNr: number of variables which should remain | |
| 174 * @level: the depth in the xsl:template's tree | |
| 175 * | |
| 176 * Pops all variable values at the given @depth from the stack. | |
| 177 * | |
| 178 * Returns the stored variable value | |
| 179 * **NOTE:** | |
| 180 * This is an internal routine and should not be called by users! | |
| 181 */ | |
| 182 void | |
| 183 xsltLocalVariablePop(xsltTransformContextPtr ctxt, int limitNr, int level) | |
| 184 { | |
| 185 xsltStackElemPtr variable; | |
| 186 | |
| 187 if (ctxt->varsNr <= 0) | |
| 188 return; | |
| 189 | |
| 190 do { | |
| 191 if (ctxt->varsNr <= limitNr) | |
| 192 break; | |
| 193 variable = ctxt->varsTab[ctxt->varsNr - 1]; | |
| 194 if (variable->level <= level) | |
| 195 break; | |
| 196 if (variable->level >= 0) | |
| 197 xsltFreeStackElemList(variable); | |
| 198 ctxt->varsNr--; | |
| 199 } while (ctxt->varsNr != 0); | |
| 200 if (ctxt->varsNr > 0) | |
| 201 ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1]; | |
| 202 else | |
| 203 ctxt->vars = NULL; | |
| 204 } | |
| 205 | |
| 206 /** | |
| 207 * xsltTemplateParamsCleanup: | |
| 208 * | |
| 209 * Removes xsl:param and xsl:with-param items from the | |
| 210 * variable-stack. Only xsl:with-param items are not freed. | |
| 211 */ | |
| 212 static void | |
| 213 xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt) | |
| 214 { | |
| 215 xsltStackElemPtr param; | |
| 216 | |
| 217 for (; ctxt->varsNr > ctxt->varsBase; ctxt->varsNr--) { | |
| 218 param = ctxt->varsTab[ctxt->varsNr -1]; | |
| 219 /* | |
| 220 * Free xsl:param items. | |
| 221 * xsl:with-param items will have a level of -1 or -2. | |
| 222 */ | |
| 223 if (param->level >= 0) { | |
| 224 xsltFreeStackElemList(param); | |
| 225 } | |
| 226 } | |
| 227 if (ctxt->varsNr > 0) | |
| 228 ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1]; | |
| 229 else | |
| 230 ctxt->vars = NULL; | |
| 231 } | |
| 232 | |
| 233 /** | |
| 234 * profPush: | |
| 235 * @ctxt: the transformation context | |
| 236 * @value: the profiling value to push on the stack | |
| 237 * | |
| 238 * Push a profiling value on the stack | |
| 239 * | |
| 240 * Returns the new index in the stack or 0 in case of error | |
| 241 */ | |
| 242 static int | |
| 243 profPush(xsltTransformContextPtr ctxt, long value) | |
| 244 { | |
| 245 if (ctxt->profMax == 0) { | |
| 246 ctxt->profMax = 4; | |
| 247 ctxt->profTab = | |
| 248 (long *) xmlMalloc(ctxt->profMax * sizeof(ctxt->profTab[0])); | |
| 249 if (ctxt->profTab == NULL) { | |
| 250 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); | |
| 251 return (0); | |
| 252 } | |
| 253 } | |
| 254 else if (ctxt->profNr >= ctxt->profMax) { | |
| 255 ctxt->profMax *= 2; | |
| 256 ctxt->profTab = | |
| 257 (long *) xmlRealloc(ctxt->profTab, | |
| 258 ctxt->profMax * sizeof(ctxt->profTab[0])); | |
| 259 if (ctxt->profTab == NULL) { | |
| 260 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); | |
| 261 return (0); | |
| 262 } | |
| 263 } | |
| 264 ctxt->profTab[ctxt->profNr] = value; | |
| 265 ctxt->prof = value; | |
| 266 return (ctxt->profNr++); | |
| 267 } | |
| 268 /** | |
| 269 * profPop: | |
| 270 * @ctxt: the transformation context | |
| 271 * | |
| 272 * Pop a profiling value from the stack | |
| 273 * | |
| 274 * Returns the stored profiling value | |
| 275 */ | |
| 276 static long | |
| 277 profPop(xsltTransformContextPtr ctxt) | |
| 278 { | |
| 279 long ret; | |
| 280 | |
| 281 if (ctxt->profNr <= 0) | |
| 282 return (0); | |
| 283 ctxt->profNr--; | |
| 284 if (ctxt->profNr > 0) | |
| 285 ctxt->prof = ctxt->profTab[ctxt->profNr - 1]; | |
| 286 else | |
| 287 ctxt->prof = (long) 0; | |
| 288 ret = ctxt->profTab[ctxt->profNr]; | |
| 289 ctxt->profTab[ctxt->profNr] = 0; | |
| 290 return (ret); | |
| 291 } | |
| 292 | |
| 293 static void | |
| 294 profCallgraphAdd(xsltTemplatePtr templ, xsltTemplatePtr parent) | |
| 295 { | |
| 296 int i; | |
| 297 | |
| 298 if (templ->templMax == 0) { | |
| 299 templ->templMax = 4; | |
| 300 templ->templCalledTab = | |
| 301 (xsltTemplatePtr *) xmlMalloc(templ->templMax * | |
| 302 sizeof(templ->templCalledTab[0])); | |
| 303 templ->templCountTab = | |
| 304 (int *) xmlMalloc(templ->templMax * | |
| 305 sizeof(templ->templCountTab[0])); | |
| 306 if (templ->templCalledTab == NULL || templ->templCountTab == NULL) { | |
| 307 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); | |
| 308 return; | |
| 309 } | |
| 310 } | |
| 311 else if (templ->templNr >= templ->templMax) { | |
| 312 templ->templMax *= 2; | |
| 313 templ->templCalledTab = | |
| 314 (xsltTemplatePtr *) xmlRealloc(templ->templCalledTab, | |
| 315 templ->templMax * | |
| 316 sizeof(templ->templCalledTab[0])); | |
| 317 templ->templCountTab = | |
| 318 (int *) xmlRealloc(templ->templCountTab, | |
| 319 templ->templMax * | |
| 320 sizeof(templ->templCountTab[0])); | |
| 321 if (templ->templCalledTab == NULL || templ->templCountTab == NULL) { | |
| 322 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); | |
| 323 return; | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 for (i = 0; i < templ->templNr; i++) { | |
| 328 if (templ->templCalledTab[i] == parent) { | |
| 329 templ->templCountTab[i]++; | |
| 330 break; | |
| 331 } | |
| 332 } | |
| 333 if (i == templ->templNr) { | |
| 334 /* not found, add new one */ | |
| 335 templ->templCalledTab[templ->templNr] = parent; | |
| 336 templ->templCountTab[templ->templNr] = 1; | |
| 337 templ->templNr++; | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 /** | |
| 342 * xsltPreCompEval: | |
| 343 * @ctxt: transform context | |
| 344 * @node: context node | |
| 345 * @comp: precompiled expression | |
| 346 * | |
| 347 * Evaluate a precompiled XPath expression. | |
| 348 */ | |
| 349 static xmlXPathObjectPtr | |
| 350 xsltPreCompEval(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 351 xsltStylePreCompPtr comp) { | |
| 352 xmlXPathObjectPtr res; | |
| 353 xmlXPathContextPtr xpctxt; | |
| 354 xmlNodePtr oldXPContextNode; | |
| 355 xmlNsPtr *oldXPNamespaces; | |
| 356 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; | |
| 357 | |
| 358 xpctxt = ctxt->xpathCtxt; | |
| 359 oldXPContextNode = xpctxt->node; | |
| 360 oldXPProximityPosition = xpctxt->proximityPosition; | |
| 361 oldXPContextSize = xpctxt->contextSize; | |
| 362 oldXPNsNr = xpctxt->nsNr; | |
| 363 oldXPNamespaces = xpctxt->namespaces; | |
| 364 | |
| 365 xpctxt->node = node; | |
| 366 #ifdef XSLT_REFACTORED | |
| 367 if (comp->inScopeNs != NULL) { | |
| 368 xpctxt->namespaces = comp->inScopeNs->list; | |
| 369 xpctxt->nsNr = comp->inScopeNs->xpathNumber; | |
| 370 } else { | |
| 371 xpctxt->namespaces = NULL; | |
| 372 xpctxt->nsNr = 0; | |
| 373 } | |
| 374 #else | |
| 375 xpctxt->namespaces = comp->nsList; | |
| 376 xpctxt->nsNr = comp->nsNr; | |
| 377 #endif | |
| 378 | |
| 379 res = xmlXPathCompiledEval(comp->comp, xpctxt); | |
| 380 | |
| 381 xpctxt->node = oldXPContextNode; | |
| 382 xpctxt->proximityPosition = oldXPProximityPosition; | |
| 383 xpctxt->contextSize = oldXPContextSize; | |
| 384 xpctxt->nsNr = oldXPNsNr; | |
| 385 xpctxt->namespaces = oldXPNamespaces; | |
| 386 | |
| 387 return(res); | |
| 388 } | |
| 389 | |
| 390 /** | |
| 391 * xsltPreCompEvalToBoolean: | |
| 392 * @ctxt: transform context | |
| 393 * @node: context node | |
| 394 * @comp: precompiled expression | |
| 395 * | |
| 396 * Evaluate a precompiled XPath expression as boolean. | |
| 397 */ | |
| 398 static int | |
| 399 xsltPreCompEvalToBoolean(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 400 xsltStylePreCompPtr comp) { | |
| 401 int res; | |
| 402 xmlXPathContextPtr xpctxt; | |
| 403 xmlNodePtr oldXPContextNode; | |
| 404 xmlNsPtr *oldXPNamespaces; | |
| 405 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; | |
| 406 | |
| 407 xpctxt = ctxt->xpathCtxt; | |
| 408 oldXPContextNode = xpctxt->node; | |
| 409 oldXPProximityPosition = xpctxt->proximityPosition; | |
| 410 oldXPContextSize = xpctxt->contextSize; | |
| 411 oldXPNsNr = xpctxt->nsNr; | |
| 412 oldXPNamespaces = xpctxt->namespaces; | |
| 413 | |
| 414 xpctxt->node = node; | |
| 415 #ifdef XSLT_REFACTORED | |
| 416 if (comp->inScopeNs != NULL) { | |
| 417 xpctxt->namespaces = comp->inScopeNs->list; | |
| 418 xpctxt->nsNr = comp->inScopeNs->xpathNumber; | |
| 419 } else { | |
| 420 xpctxt->namespaces = NULL; | |
| 421 xpctxt->nsNr = 0; | |
| 422 } | |
| 423 #else | |
| 424 xpctxt->namespaces = comp->nsList; | |
| 425 xpctxt->nsNr = comp->nsNr; | |
| 426 #endif | |
| 427 | |
| 428 res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt); | |
| 429 | |
| 430 xpctxt->node = oldXPContextNode; | |
| 431 xpctxt->proximityPosition = oldXPProximityPosition; | |
| 432 xpctxt->contextSize = oldXPContextSize; | |
| 433 xpctxt->nsNr = oldXPNsNr; | |
| 434 xpctxt->namespaces = oldXPNamespaces; | |
| 435 | |
| 436 return(res); | |
| 437 } | |
| 438 | |
| 439 /************************************************************************ | |
| 440 * * | |
| 441 * XInclude default settings * | |
| 442 * * | |
| 443 ************************************************************************/ | |
| 444 | |
| 445 static int xsltDoXIncludeDefault = 0; | |
| 446 | |
| 447 /** | |
| 448 * xsltSetXIncludeDefault: | |
| 449 * @xinclude: whether to do XInclude processing | |
| 450 * | |
| 451 * Set whether XInclude should be processed on document being loaded by default | |
| 452 */ | |
| 453 void | |
| 454 xsltSetXIncludeDefault(int xinclude) { | |
| 455 xsltDoXIncludeDefault = (xinclude != 0); | |
| 456 } | |
| 457 | |
| 458 /** | |
| 459 * xsltGetXIncludeDefault: | |
| 460 * | |
| 461 * Provides the default state for XInclude processing | |
| 462 * | |
| 463 * Returns 0 if there is no processing 1 otherwise | |
| 464 */ | |
| 465 int | |
| 466 xsltGetXIncludeDefault(void) { | |
| 467 return(xsltDoXIncludeDefault); | |
| 468 } | |
| 469 | |
| 470 static unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL; | |
| 471 | |
| 472 /** | |
| 473 * xsltDebugSetDefaultTrace: | |
| 474 * @val: tracing level mask | |
| 475 * | |
| 476 * Set the default debug tracing level mask | |
| 477 */ | |
| 478 void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) { | |
| 479 xsltDefaultTrace = val; | |
| 480 } | |
| 481 | |
| 482 /** | |
| 483 * xsltDebugGetDefaultTrace: | |
| 484 * | |
| 485 * Get the current default debug tracing level mask | |
| 486 * | |
| 487 * Returns the current default debug tracing level mask | |
| 488 */ | |
| 489 xsltDebugTraceCodes xsltDebugGetDefaultTrace() { | |
| 490 return xsltDefaultTrace; | |
| 491 } | |
| 492 | |
| 493 /************************************************************************ | |
| 494 * * | |
| 495 * Handling of Transformation Contexts * | |
| 496 * * | |
| 497 ************************************************************************/ | |
| 498 | |
| 499 static xsltTransformCachePtr | |
| 500 xsltTransformCacheCreate(void) | |
| 501 { | |
| 502 xsltTransformCachePtr ret; | |
| 503 | |
| 504 ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache)); | |
| 505 if (ret == NULL) { | |
| 506 xsltTransformError(NULL, NULL, NULL, | |
| 507 "xsltTransformCacheCreate : malloc failed\n"); | |
| 508 return(NULL); | |
| 509 } | |
| 510 memset(ret, 0, sizeof(xsltTransformCache)); | |
| 511 return(ret); | |
| 512 } | |
| 513 | |
| 514 static void | |
| 515 xsltTransformCacheFree(xsltTransformCachePtr cache) | |
| 516 { | |
| 517 if (cache == NULL) | |
| 518 return; | |
| 519 /* | |
| 520 * Free tree fragments. | |
| 521 */ | |
| 522 if (cache->RVT) { | |
| 523 xmlDocPtr tmp, cur = cache->RVT; | |
| 524 while (cur) { | |
| 525 tmp = cur; | |
| 526 cur = (xmlDocPtr) cur->next; | |
| 527 if (tmp->_private != NULL) { | |
| 528 /* | |
| 529 * Tree the document info. | |
| 530 */ | |
| 531 xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private); | |
| 532 xmlFree(tmp->_private); | |
| 533 } | |
| 534 xmlFreeDoc(tmp); | |
| 535 } | |
| 536 } | |
| 537 /* | |
| 538 * Free vars/params. | |
| 539 */ | |
| 540 if (cache->stackItems) { | |
| 541 xsltStackElemPtr tmp, cur = cache->stackItems; | |
| 542 while (cur) { | |
| 543 tmp = cur; | |
| 544 cur = cur->next; | |
| 545 /* | |
| 546 * REVISIT TODO: Should be call a destruction-function | |
| 547 * instead? | |
| 548 */ | |
| 549 xmlFree(tmp); | |
| 550 } | |
| 551 } | |
| 552 xmlFree(cache); | |
| 553 } | |
| 554 | |
| 555 /** | |
| 556 * xsltNewTransformContext: | |
| 557 * @style: a parsed XSLT stylesheet | |
| 558 * @doc: the input document | |
| 559 * | |
| 560 * Create a new XSLT TransformContext | |
| 561 * | |
| 562 * Returns the newly allocated xsltTransformContextPtr or NULL in case of error | |
| 563 */ | |
| 564 xsltTransformContextPtr | |
| 565 xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) { | |
| 566 xsltTransformContextPtr cur; | |
| 567 xsltDocumentPtr docu; | |
| 568 int i; | |
| 569 | |
| 570 xsltInitGlobals(); | |
| 571 | |
| 572 cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext)); | |
| 573 if (cur == NULL) { | |
| 574 xsltTransformError(NULL, NULL, (xmlNodePtr)doc, | |
| 575 "xsltNewTransformContext : malloc failed\n"); | |
| 576 return(NULL); | |
| 577 } | |
| 578 memset(cur, 0, sizeof(xsltTransformContext)); | |
| 579 | |
| 580 cur->cache = xsltTransformCacheCreate(); | |
| 581 if (cur->cache == NULL) | |
| 582 goto internal_err; | |
| 583 /* | |
| 584 * setup of the dictionary must be done early as some of the | |
| 585 * processing later like key handling may need it. | |
| 586 */ | |
| 587 cur->dict = xmlDictCreateSub(style->dict); | |
| 588 cur->internalized = ((style->internalized) && (cur->dict != NULL)); | |
| 589 #ifdef WITH_XSLT_DEBUG | |
| 590 xsltGenericDebug(xsltGenericDebugContext, | |
| 591 "Creating sub-dictionary from stylesheet for transformation\n"); | |
| 592 #endif | |
| 593 | |
| 594 /* | |
| 595 * initialize the template stack | |
| 596 */ | |
| 597 cur->templTab = (xsltTemplatePtr *) | |
| 598 xmlMalloc(10 * sizeof(xsltTemplatePtr)); | |
| 599 if (cur->templTab == NULL) { | |
| 600 xsltTransformError(NULL, NULL, (xmlNodePtr) doc, | |
| 601 "xsltNewTransformContext: out of memory\n"); | |
| 602 goto internal_err; | |
| 603 } | |
| 604 cur->templNr = 0; | |
| 605 cur->templMax = 5; | |
| 606 cur->templ = NULL; | |
| 607 cur->maxTemplateDepth = xsltMaxDepth; | |
| 608 | |
| 609 /* | |
| 610 * initialize the variables stack | |
| 611 */ | |
| 612 cur->varsTab = (xsltStackElemPtr *) | |
| 613 xmlMalloc(10 * sizeof(xsltStackElemPtr)); | |
| 614 if (cur->varsTab == NULL) { | |
| 615 xmlGenericError(xmlGenericErrorContext, | |
| 616 "xsltNewTransformContext: out of memory\n"); | |
| 617 goto internal_err; | |
| 618 } | |
| 619 cur->varsNr = 0; | |
| 620 cur->varsMax = 10; | |
| 621 cur->vars = NULL; | |
| 622 cur->varsBase = 0; | |
| 623 cur->maxTemplateVars = xsltMaxVars; | |
| 624 | |
| 625 /* | |
| 626 * the profiling stack is not initialized by default | |
| 627 */ | |
| 628 cur->profTab = NULL; | |
| 629 cur->profNr = 0; | |
| 630 cur->profMax = 0; | |
| 631 cur->prof = 0; | |
| 632 | |
| 633 cur->style = style; | |
| 634 xmlXPathInit(); | |
| 635 cur->xpathCtxt = xmlXPathNewContext(doc); | |
| 636 if (cur->xpathCtxt == NULL) { | |
| 637 xsltTransformError(NULL, NULL, (xmlNodePtr) doc, | |
| 638 "xsltNewTransformContext : xmlXPathNewContext failed\n"); | |
| 639 goto internal_err; | |
| 640 } | |
| 641 /* | |
| 642 * Create an XPath cache. | |
| 643 */ | |
| 644 if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1) | |
| 645 goto internal_err; | |
| 646 /* | |
| 647 * Initialize the extras array | |
| 648 */ | |
| 649 if (style->extrasNr != 0) { | |
| 650 cur->extrasMax = style->extrasNr + 20; | |
| 651 cur->extras = (xsltRuntimeExtraPtr) | |
| 652 xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra)); | |
| 653 if (cur->extras == NULL) { | |
| 654 xmlGenericError(xmlGenericErrorContext, | |
| 655 "xsltNewTransformContext: out of memory\n"); | |
| 656 goto internal_err; | |
| 657 } | |
| 658 cur->extrasNr = style->extrasNr; | |
| 659 for (i = 0;i < cur->extrasMax;i++) { | |
| 660 cur->extras[i].info = NULL; | |
| 661 cur->extras[i].deallocate = NULL; | |
| 662 cur->extras[i].val.ptr = NULL; | |
| 663 } | |
| 664 } else { | |
| 665 cur->extras = NULL; | |
| 666 cur->extrasNr = 0; | |
| 667 cur->extrasMax = 0; | |
| 668 } | |
| 669 | |
| 670 XSLT_REGISTER_VARIABLE_LOOKUP(cur); | |
| 671 XSLT_REGISTER_FUNCTION_LOOKUP(cur); | |
| 672 cur->xpathCtxt->nsHash = style->nsHash; | |
| 673 /* | |
| 674 * Initialize the registered external modules | |
| 675 */ | |
| 676 xsltInitCtxtExts(cur); | |
| 677 /* | |
| 678 * Setup document element ordering for later efficiencies | |
| 679 * (bug 133289) | |
| 680 */ | |
| 681 if (xslDebugStatus == XSLT_DEBUG_NONE) | |
| 682 xmlXPathOrderDocElems(doc); | |
| 683 /* | |
| 684 * Must set parserOptions before calling xsltNewDocument | |
| 685 * (bug 164530) | |
| 686 */ | |
| 687 cur->parserOptions = XSLT_PARSE_OPTIONS; | |
| 688 docu = xsltNewDocument(cur, doc); | |
| 689 if (docu == NULL) { | |
| 690 xsltTransformError(cur, NULL, (xmlNodePtr)doc, | |
| 691 "xsltNewTransformContext : xsltNewDocument failed\n"); | |
| 692 goto internal_err; | |
| 693 } | |
| 694 docu->main = 1; | |
| 695 cur->document = docu; | |
| 696 cur->inst = NULL; | |
| 697 cur->outputFile = NULL; | |
| 698 cur->sec = xsltGetDefaultSecurityPrefs(); | |
| 699 cur->debugStatus = xslDebugStatus; | |
| 700 cur->traceCode = (unsigned long*) &xsltDefaultTrace; | |
| 701 cur->xinclude = xsltGetXIncludeDefault(); | |
| 702 cur->keyInitLevel = 0; | |
| 703 | |
| 704 return(cur); | |
| 705 | |
| 706 internal_err: | |
| 707 if (cur != NULL) | |
| 708 xsltFreeTransformContext(cur); | |
| 709 return(NULL); | |
| 710 } | |
| 711 | |
| 712 /** | |
| 713 * xsltFreeTransformContext: | |
| 714 * @ctxt: an XSLT parser context | |
| 715 * | |
| 716 * Free up the memory allocated by @ctxt | |
| 717 */ | |
| 718 void | |
| 719 xsltFreeTransformContext(xsltTransformContextPtr ctxt) { | |
| 720 if (ctxt == NULL) | |
| 721 return; | |
| 722 | |
| 723 /* | |
| 724 * Shutdown the extension modules associated to the stylesheet | |
| 725 * used if needed. | |
| 726 */ | |
| 727 xsltShutdownCtxtExts(ctxt); | |
| 728 | |
| 729 if (ctxt->xpathCtxt != NULL) { | |
| 730 ctxt->xpathCtxt->nsHash = NULL; | |
| 731 xmlXPathFreeContext(ctxt->xpathCtxt); | |
| 732 } | |
| 733 if (ctxt->templTab != NULL) | |
| 734 xmlFree(ctxt->templTab); | |
| 735 if (ctxt->varsTab != NULL) | |
| 736 xmlFree(ctxt->varsTab); | |
| 737 if (ctxt->profTab != NULL) | |
| 738 xmlFree(ctxt->profTab); | |
| 739 if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) { | |
| 740 int i; | |
| 741 | |
| 742 for (i = 0;i < ctxt->extrasNr;i++) { | |
| 743 if ((ctxt->extras[i].deallocate != NULL) && | |
| 744 (ctxt->extras[i].info != NULL)) | |
| 745 ctxt->extras[i].deallocate(ctxt->extras[i].info); | |
| 746 } | |
| 747 xmlFree(ctxt->extras); | |
| 748 } | |
| 749 xsltFreeGlobalVariables(ctxt); | |
| 750 xsltFreeDocuments(ctxt); | |
| 751 xsltFreeCtxtExts(ctxt); | |
| 752 xsltFreeRVTs(ctxt); | |
| 753 xsltTransformCacheFree(ctxt->cache); | |
| 754 xmlDictFree(ctxt->dict); | |
| 755 #ifdef WITH_XSLT_DEBUG | |
| 756 xsltGenericDebug(xsltGenericDebugContext, | |
| 757 "freeing transformation dictionary\n"); | |
| 758 #endif | |
| 759 memset(ctxt, -1, sizeof(xsltTransformContext)); | |
| 760 xmlFree(ctxt); | |
| 761 } | |
| 762 | |
| 763 /************************************************************************ | |
| 764 * * | |
| 765 * Copy of Nodes in an XSLT fashion * | |
| 766 * * | |
| 767 ************************************************************************/ | |
| 768 | |
| 769 /** | |
| 770 * xsltAddChild: | |
| 771 * @parent: the parent node | |
| 772 * @cur: the child node | |
| 773 * | |
| 774 * Wrapper version of xmlAddChild with a more consistent behaviour on | |
| 775 * error. One expect the use to be child = xsltAddChild(parent, child); | |
| 776 * and the routine will take care of not leaking on errors or node merge | |
| 777 * | |
| 778 * Returns the child is successfully attached or NULL if merged or freed | |
| 779 */ | |
| 780 static xmlNodePtr | |
| 781 xsltAddChild(xmlNodePtr parent, xmlNodePtr cur) { | |
| 782 xmlNodePtr ret; | |
| 783 | |
| 784 if (cur == NULL) | |
| 785 return(NULL); | |
| 786 if (parent == NULL) { | |
| 787 xmlFreeNode(cur); | |
| 788 return(NULL); | |
| 789 } | |
| 790 ret = xmlAddChild(parent, cur); | |
| 791 | |
| 792 return(ret); | |
| 793 } | |
| 794 | |
| 795 /** | |
| 796 * xsltAddTextString: | |
| 797 * @ctxt: a XSLT process context | |
| 798 * @target: the text node where the text will be attached | |
| 799 * @string: the text string | |
| 800 * @len: the string length in byte | |
| 801 * | |
| 802 * Extend the current text node with the new string, it handles coalescing | |
| 803 * | |
| 804 * Returns: the text node | |
| 805 */ | |
| 806 static xmlNodePtr | |
| 807 xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target, | |
| 808 const xmlChar *string, int len) { | |
| 809 /* | |
| 810 * optimization | |
| 811 */ | |
| 812 if ((len <= 0) || (string == NULL) || (target == NULL)) | |
| 813 return(target); | |
| 814 | |
| 815 if (ctxt->lasttext == target->content) { | |
| 816 int minSize; | |
| 817 | |
| 818 /* Check for integer overflow accounting for NUL terminator. */ | |
| 819 if (len >= INT_MAX - ctxt->lasttuse) { | |
| 820 xsltTransformError(ctxt, NULL, target, | |
| 821 "xsltCopyText: text allocation failed\n"); | |
| 822 return(NULL); | |
| 823 } | |
| 824 minSize = ctxt->lasttuse + len + 1; | |
| 825 | |
| 826 if (ctxt->lasttsize < minSize) { | |
| 827 xmlChar *newbuf; | |
| 828 int size; | |
| 829 int extra; | |
| 830 | |
| 831 /* Double buffer size but increase by at least 100 bytes. */ | |
| 832 extra = minSize < 100 ? 100 : minSize; | |
| 833 | |
| 834 /* Check for integer overflow. */ | |
| 835 if (extra > INT_MAX - ctxt->lasttsize) { | |
| 836 size = INT_MAX; | |
| 837 } | |
| 838 else { | |
| 839 size = ctxt->lasttsize + extra; | |
| 840 } | |
| 841 | |
| 842 newbuf = (xmlChar *) xmlRealloc(target->content,size); | |
| 843 if (newbuf == NULL) { | |
| 844 xsltTransformError(ctxt, NULL, target, | |
| 845 "xsltCopyText: text allocation failed\n"); | |
| 846 return(NULL); | |
| 847 } | |
| 848 ctxt->lasttsize = size; | |
| 849 ctxt->lasttext = newbuf; | |
| 850 target->content = newbuf; | |
| 851 } | |
| 852 memcpy(&(target->content[ctxt->lasttuse]), string, len); | |
| 853 ctxt->lasttuse += len; | |
| 854 target->content[ctxt->lasttuse] = 0; | |
| 855 } else { | |
| 856 xmlNodeAddContent(target, string); | |
| 857 ctxt->lasttext = target->content; | |
| 858 len = xmlStrlen(target->content); | |
| 859 ctxt->lasttsize = len; | |
| 860 ctxt->lasttuse = len; | |
| 861 } | |
| 862 return(target); | |
| 863 } | |
| 864 | |
| 865 /** | |
| 866 * xsltCopyTextString: | |
| 867 * @ctxt: a XSLT process context | |
| 868 * @target: the element where the text will be attached | |
| 869 * @string: the text string | |
| 870 * @noescape: should disable-escaping be activated for this text node. | |
| 871 * | |
| 872 * Adds @string to a newly created or an existent text node child of | |
| 873 * @target. | |
| 874 * | |
| 875 * Returns: the text node, where the text content of @cur is copied to. | |
| 876 * NULL in case of API or internal errors. | |
| 877 */ | |
| 878 xmlNodePtr | |
| 879 xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target, | |
| 880 const xmlChar *string, int noescape) | |
| 881 { | |
| 882 xmlNodePtr copy; | |
| 883 int len; | |
| 884 | |
| 885 if (string == NULL) | |
| 886 return(NULL); | |
| 887 | |
| 888 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 889 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContex
t, | |
| 890 "xsltCopyTextString: copy text %s\n", | |
| 891 string)); | |
| 892 #endif | |
| 893 | |
| 894 /* | |
| 895 * Play safe and reset the merging mechanism for every new | |
| 896 * target node. | |
| 897 */ | |
| 898 if ((target == NULL) || (target->children == NULL)) { | |
| 899 ctxt->lasttext = NULL; | |
| 900 } | |
| 901 | |
| 902 /* handle coalescing of text nodes here */ | |
| 903 len = xmlStrlen(string); | |
| 904 if ((ctxt->type == XSLT_OUTPUT_XML) && | |
| 905 (ctxt->style->cdataSection != NULL) && | |
| 906 (target != NULL) && | |
| 907 (target->type == XML_ELEMENT_NODE) && | |
| 908 (((target->ns == NULL) && | |
| 909 (xmlHashLookup2(ctxt->style->cdataSection, | |
| 910 target->name, NULL) != NULL)) || | |
| 911 ((target->ns != NULL) && | |
| 912 (xmlHashLookup2(ctxt->style->cdataSection, | |
| 913 target->name, target->ns->href) != NULL)))) | |
| 914 { | |
| 915 /* | |
| 916 * Process "cdata-section-elements". | |
| 917 */ | |
| 918 if ((target->last != NULL) && | |
| 919 (target->last->type == XML_CDATA_SECTION_NODE)) | |
| 920 { | |
| 921 return(xsltAddTextString(ctxt, target->last, string, len)); | |
| 922 } | |
| 923 copy = xmlNewCDataBlock(ctxt->output, string, len); | |
| 924 } else if (noescape) { | |
| 925 /* | |
| 926 * Process "disable-output-escaping". | |
| 927 */ | |
| 928 if ((target != NULL) && (target->last != NULL) && | |
| 929 (target->last->type == XML_TEXT_NODE) && | |
| 930 (target->last->name == xmlStringTextNoenc)) | |
| 931 { | |
| 932 return(xsltAddTextString(ctxt, target->last, string, len)); | |
| 933 } | |
| 934 copy = xmlNewTextLen(string, len); | |
| 935 if (copy != NULL) | |
| 936 copy->name = xmlStringTextNoenc; | |
| 937 } else { | |
| 938 /* | |
| 939 * Default processing. | |
| 940 */ | |
| 941 if ((target != NULL) && (target->last != NULL) && | |
| 942 (target->last->type == XML_TEXT_NODE) && | |
| 943 (target->last->name == xmlStringText)) { | |
| 944 return(xsltAddTextString(ctxt, target->last, string, len)); | |
| 945 } | |
| 946 copy = xmlNewTextLen(string, len); | |
| 947 } | |
| 948 if (copy != NULL && target != NULL) | |
| 949 copy = xsltAddChild(target, copy); | |
| 950 if (copy != NULL) { | |
| 951 ctxt->lasttext = copy->content; | |
| 952 ctxt->lasttsize = len; | |
| 953 ctxt->lasttuse = len; | |
| 954 } else { | |
| 955 xsltTransformError(ctxt, NULL, target, | |
| 956 "xsltCopyTextString: text copy failed\n"); | |
| 957 ctxt->lasttext = NULL; | |
| 958 } | |
| 959 return(copy); | |
| 960 } | |
| 961 | |
| 962 /** | |
| 963 * xsltCopyText: | |
| 964 * @ctxt: a XSLT process context | |
| 965 * @target: the element where the text will be attached | |
| 966 * @cur: the text or CDATA node | |
| 967 * @interned: the string is in the target doc dictionary | |
| 968 * | |
| 969 * Copy the text content of @cur and append it to @target's children. | |
| 970 * | |
| 971 * Returns: the text node, where the text content of @cur is copied to. | |
| 972 * NULL in case of API or internal errors. | |
| 973 */ | |
| 974 static xmlNodePtr | |
| 975 xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target, | |
| 976 xmlNodePtr cur, int interned) | |
| 977 { | |
| 978 xmlNodePtr copy; | |
| 979 | |
| 980 if ((cur->type != XML_TEXT_NODE) && | |
| 981 (cur->type != XML_CDATA_SECTION_NODE)) | |
| 982 return(NULL); | |
| 983 if (cur->content == NULL) | |
| 984 return(NULL); | |
| 985 | |
| 986 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 987 if (cur->type == XML_CDATA_SECTION_NODE) { | |
| 988 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugCo
ntext, | |
| 989 "xsltCopyText: copy CDATA text %s\n", | |
| 990 cur->content)); | |
| 991 } else if (cur->name == xmlStringTextNoenc) { | |
| 992 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugCo
ntext, | |
| 993 "xsltCopyText: copy unescaped text %s\n", | |
| 994 cur->content)); | |
| 995 } else { | |
| 996 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugCo
ntext, | |
| 997 "xsltCopyText: copy text %s\n", | |
| 998 cur->content)); | |
| 999 } | |
| 1000 #endif | |
| 1001 | |
| 1002 /* | |
| 1003 * Play save and reset the merging mechanism for every new | |
| 1004 * target node. | |
| 1005 */ | |
| 1006 if ((target == NULL) || (target->children == NULL)) { | |
| 1007 ctxt->lasttext = NULL; | |
| 1008 } | |
| 1009 | |
| 1010 if ((ctxt->style->cdataSection != NULL) && | |
| 1011 (ctxt->type == XSLT_OUTPUT_XML) && | |
| 1012 (target != NULL) && | |
| 1013 (target->type == XML_ELEMENT_NODE) && | |
| 1014 (((target->ns == NULL) && | |
| 1015 (xmlHashLookup2(ctxt->style->cdataSection, | |
| 1016 target->name, NULL) != NULL)) || | |
| 1017 ((target->ns != NULL) && | |
| 1018 (xmlHashLookup2(ctxt->style->cdataSection, | |
| 1019 target->name, target->ns->href) != NULL)))) | |
| 1020 { | |
| 1021 /* | |
| 1022 * Process "cdata-section-elements". | |
| 1023 */ | |
| 1024 /* | |
| 1025 * OPTIMIZE TODO: xsltCopyText() is also used for attribute content. | |
| 1026 */ | |
| 1027 /* | |
| 1028 * TODO: Since this doesn't merge adjacent CDATA-section nodes, | |
| 1029 * we'll get: <![CDATA[x]]><!CDATA[y]]>. | |
| 1030 * TODO: Reported in #321505. | |
| 1031 */ | |
| 1032 if ((target->last != NULL) && | |
| 1033 (target->last->type == XML_CDATA_SECTION_NODE)) | |
| 1034 { | |
| 1035 /* | |
| 1036 * Append to existing CDATA-section node. | |
| 1037 */ | |
| 1038 copy = xsltAddTextString(ctxt, target->last, cur->content, | |
| 1039 xmlStrlen(cur->content)); | |
| 1040 goto exit; | |
| 1041 } else { | |
| 1042 unsigned int len; | |
| 1043 | |
| 1044 len = xmlStrlen(cur->content); | |
| 1045 copy = xmlNewCDataBlock(ctxt->output, cur->content, len); | |
| 1046 if (copy == NULL) | |
| 1047 goto exit; | |
| 1048 ctxt->lasttext = copy->content; | |
| 1049 ctxt->lasttsize = len; | |
| 1050 ctxt->lasttuse = len; | |
| 1051 } | |
| 1052 } else if ((target != NULL) && | |
| 1053 (target->last != NULL) && | |
| 1054 /* both escaped or both non-escaped text-nodes */ | |
| 1055 (((target->last->type == XML_TEXT_NODE) && | |
| 1056 (target->last->name == cur->name)) || | |
| 1057 /* non-escaped text nodes and CDATA-section nodes */ | |
| 1058 (((target->last->type == XML_CDATA_SECTION_NODE) && | |
| 1059 (cur->name == xmlStringTextNoenc))))) | |
| 1060 { | |
| 1061 /* | |
| 1062 * we are appending to an existing text node | |
| 1063 */ | |
| 1064 copy = xsltAddTextString(ctxt, target->last, cur->content, | |
| 1065 xmlStrlen(cur->content)); | |
| 1066 goto exit; | |
| 1067 } else if ((interned) && (target != NULL) && | |
| 1068 (target->doc != NULL) && | |
| 1069 (target->doc->dict == ctxt->dict)) | |
| 1070 { | |
| 1071 /* | |
| 1072 * TODO: DO we want to use this also for "text" output? | |
| 1073 */ | |
| 1074 copy = xmlNewTextLen(NULL, 0); | |
| 1075 if (copy == NULL) | |
| 1076 goto exit; | |
| 1077 if (cur->name == xmlStringTextNoenc) | |
| 1078 copy->name = xmlStringTextNoenc; | |
| 1079 | |
| 1080 /* | |
| 1081 * Must confirm that content is in dict (bug 302821) | |
| 1082 * TODO: This check should be not needed for text coming | |
| 1083 * from the stylesheets | |
| 1084 */ | |
| 1085 if (xmlDictOwns(ctxt->dict, cur->content)) | |
| 1086 copy->content = cur->content; | |
| 1087 else { | |
| 1088 if ((copy->content = xmlStrdup(cur->content)) == NULL) | |
| 1089 return NULL; | |
| 1090 } | |
| 1091 } else { | |
| 1092 /* | |
| 1093 * normal processing. keep counters to extend the text node | |
| 1094 * in xsltAddTextString if needed. | |
| 1095 */ | |
| 1096 unsigned int len; | |
| 1097 | |
| 1098 len = xmlStrlen(cur->content); | |
| 1099 copy = xmlNewTextLen(cur->content, len); | |
| 1100 if (copy == NULL) | |
| 1101 goto exit; | |
| 1102 if (cur->name == xmlStringTextNoenc) | |
| 1103 copy->name = xmlStringTextNoenc; | |
| 1104 ctxt->lasttext = copy->content; | |
| 1105 ctxt->lasttsize = len; | |
| 1106 ctxt->lasttuse = len; | |
| 1107 } | |
| 1108 if (copy != NULL) { | |
| 1109 if (target != NULL) { | |
| 1110 copy->doc = target->doc; | |
| 1111 /* | |
| 1112 * MAYBE TODO: Maybe we should reset the ctxt->lasttext here | |
| 1113 * to ensure that the optimized text-merging mechanism | |
| 1114 * won't interfere with normal node-merging in any case. | |
| 1115 */ | |
| 1116 copy = xsltAddChild(target, copy); | |
| 1117 } | |
| 1118 } else { | |
| 1119 xsltTransformError(ctxt, NULL, target, | |
| 1120 "xsltCopyText: text copy failed\n"); | |
| 1121 } | |
| 1122 | |
| 1123 exit: | |
| 1124 if ((copy == NULL) || (copy->content == NULL)) { | |
| 1125 xsltTransformError(ctxt, NULL, target, | |
| 1126 "Internal error in xsltCopyText(): " | |
| 1127 "Failed to copy the string.\n"); | |
| 1128 ctxt->state = XSLT_STATE_STOPPED; | |
| 1129 } | |
| 1130 return(copy); | |
| 1131 } | |
| 1132 | |
| 1133 /** | |
| 1134 * xsltShallowCopyAttr: | |
| 1135 * @ctxt: a XSLT process context | |
| 1136 * @invocNode: responsible node in the stylesheet; used for error reports | |
| 1137 * @target: the element where the attribute will be grafted | |
| 1138 * @attr: the attribute to be copied | |
| 1139 * | |
| 1140 * Do a copy of an attribute. | |
| 1141 * Called by: | |
| 1142 * - xsltCopyTree() | |
| 1143 * - xsltCopyOf() | |
| 1144 * - xsltCopy() | |
| 1145 * | |
| 1146 * Returns: a new xmlAttrPtr, or NULL in case of error. | |
| 1147 */ | |
| 1148 static xmlAttrPtr | |
| 1149 xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, | |
| 1150 xmlNodePtr target, xmlAttrPtr attr) | |
| 1151 { | |
| 1152 xmlAttrPtr copy; | |
| 1153 xmlChar *value; | |
| 1154 | |
| 1155 if (attr == NULL) | |
| 1156 return(NULL); | |
| 1157 | |
| 1158 if (target->type != XML_ELEMENT_NODE) { | |
| 1159 xsltTransformError(ctxt, NULL, invocNode, | |
| 1160 "Cannot add an attribute node to a non-element node.\n"); | |
| 1161 return(NULL); | |
| 1162 } | |
| 1163 | |
| 1164 if (target->children != NULL) { | |
| 1165 xsltTransformError(ctxt, NULL, invocNode, | |
| 1166 "Attribute nodes must be added before " | |
| 1167 "any child nodes to an element.\n"); | |
| 1168 return(NULL); | |
| 1169 } | |
| 1170 | |
| 1171 value = xmlNodeListGetString(attr->doc, attr->children, 1); | |
| 1172 if (attr->ns != NULL) { | |
| 1173 xmlNsPtr ns; | |
| 1174 | |
| 1175 ns = xsltGetSpecialNamespace(ctxt, invocNode, | |
| 1176 attr->ns->href, attr->ns->prefix, target); | |
| 1177 if (ns == NULL) { | |
| 1178 xsltTransformError(ctxt, NULL, invocNode, | |
| 1179 "Namespace fixup error: Failed to acquire an in-scope " | |
| 1180 "namespace binding of the copied attribute '{%s}%s'.\n", | |
| 1181 attr->ns->href, attr->name); | |
| 1182 /* | |
| 1183 * TODO: Should we just stop here? | |
| 1184 */ | |
| 1185 } | |
| 1186 /* | |
| 1187 * Note that xmlSetNsProp() will take care of duplicates | |
| 1188 * and assigns the new namespace even to a duplicate. | |
| 1189 */ | |
| 1190 copy = xmlSetNsProp(target, ns, attr->name, value); | |
| 1191 } else { | |
| 1192 copy = xmlSetNsProp(target, NULL, attr->name, value); | |
| 1193 } | |
| 1194 if (value != NULL) | |
| 1195 xmlFree(value); | |
| 1196 | |
| 1197 if (copy == NULL) | |
| 1198 return(NULL); | |
| 1199 | |
| 1200 #if 0 | |
| 1201 /* | |
| 1202 * NOTE: This was optimized according to bug #342695. | |
| 1203 * TODO: Can this further be optimized, if source and target | |
| 1204 * share the same dict and attr->children is just 1 text node | |
| 1205 * which is in the dict? How probable is such a case? | |
| 1206 */ | |
| 1207 /* | |
| 1208 * TODO: Do we need to create an empty text node if the value | |
| 1209 * is the empty string? | |
| 1210 */ | |
| 1211 value = xmlNodeListGetString(attr->doc, attr->children, 1); | |
| 1212 if (value != NULL) { | |
| 1213 txtNode = xmlNewDocText(target->doc, NULL); | |
| 1214 if (txtNode == NULL) | |
| 1215 return(NULL); | |
| 1216 if ((target->doc != NULL) && | |
| 1217 (target->doc->dict != NULL)) | |
| 1218 { | |
| 1219 txtNode->content = | |
| 1220 (xmlChar *) xmlDictLookup(target->doc->dict, | |
| 1221 BAD_CAST value, -1); | |
| 1222 xmlFree(value); | |
| 1223 } else | |
| 1224 txtNode->content = value; | |
| 1225 copy->children = txtNode; | |
| 1226 } | |
| 1227 #endif | |
| 1228 | |
| 1229 return(copy); | |
| 1230 } | |
| 1231 | |
| 1232 /** | |
| 1233 * xsltCopyAttrListNoOverwrite: | |
| 1234 * @ctxt: a XSLT process context | |
| 1235 * @invocNode: responsible node in the stylesheet; used for error reports | |
| 1236 * @target: the element where the new attributes will be grafted | |
| 1237 * @attr: the first attribute in the list to be copied | |
| 1238 * | |
| 1239 * Copies a list of attribute nodes, starting with @attr, over to the | |
| 1240 * @target element node. | |
| 1241 * | |
| 1242 * Called by: | |
| 1243 * - xsltCopyTree() | |
| 1244 * | |
| 1245 * Returns 0 on success and -1 on errors and internal errors. | |
| 1246 */ | |
| 1247 static int | |
| 1248 xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt, | |
| 1249 xmlNodePtr invocNode, | |
| 1250 xmlNodePtr target, xmlAttrPtr attr) | |
| 1251 { | |
| 1252 xmlAttrPtr copy; | |
| 1253 xmlNsPtr origNs = NULL, copyNs = NULL; | |
| 1254 xmlChar *value; | |
| 1255 | |
| 1256 /* | |
| 1257 * Don't use xmlCopyProp() here, since it will try to | |
| 1258 * reconciliate namespaces. | |
| 1259 */ | |
| 1260 while (attr != NULL) { | |
| 1261 /* | |
| 1262 * Find a namespace node in the tree of @target. | |
| 1263 * Avoid searching for the same ns. | |
| 1264 */ | |
| 1265 if (attr->ns != origNs) { | |
| 1266 origNs = attr->ns; | |
| 1267 if (attr->ns != NULL) { | |
| 1268 copyNs = xsltGetSpecialNamespace(ctxt, invocNode, | |
| 1269 attr->ns->href, attr->ns->prefix, target); | |
| 1270 if (copyNs == NULL) | |
| 1271 return(-1); | |
| 1272 } else | |
| 1273 copyNs = NULL; | |
| 1274 } | |
| 1275 /* | |
| 1276 * If attribute has a value, we need to copy it (watching out | |
| 1277 * for possible entities) | |
| 1278 */ | |
| 1279 if ((attr->children) && (attr->children->type == XML_TEXT_NODE) && | |
| 1280 (attr->children->next == NULL)) { | |
| 1281 copy = xmlNewNsProp(target, copyNs, attr->name, | |
| 1282 attr->children->content); | |
| 1283 } else if (attr->children != NULL) { | |
| 1284 value = xmlNodeListGetString(attr->doc, attr->children, 1); | |
| 1285 copy = xmlNewNsProp(target, copyNs, attr->name, BAD_CAST value); | |
| 1286 xmlFree(value); | |
| 1287 } else { | |
| 1288 copy = xmlNewNsProp(target, copyNs, attr->name, NULL); | |
| 1289 } | |
| 1290 | |
| 1291 if (copy == NULL) | |
| 1292 return(-1); | |
| 1293 | |
| 1294 attr = attr->next; | |
| 1295 } | |
| 1296 return(0); | |
| 1297 } | |
| 1298 | |
| 1299 /** | |
| 1300 * xsltShallowCopyElem: | |
| 1301 * @ctxt: the XSLT process context | |
| 1302 * @node: the element node in the source tree | |
| 1303 * or the Literal Result Element | |
| 1304 * @insert: the parent in the result tree | |
| 1305 * @isLRE: if @node is a Literal Result Element | |
| 1306 * | |
| 1307 * Make a copy of the element node @node | |
| 1308 * and insert it as last child of @insert. | |
| 1309 * | |
| 1310 * URGENT TODO: The problem with this one (for the non-refactored code) | |
| 1311 * is that it is used for both, Literal Result Elements *and* | |
| 1312 * copying input nodes. | |
| 1313 * | |
| 1314 * BIG NOTE: This is only called for XML_ELEMENT_NODEs. | |
| 1315 * | |
| 1316 * Called from: | |
| 1317 * xsltApplySequenceConstructor() | |
| 1318 * (for Literal Result Elements - which is a problem) | |
| 1319 * xsltCopy() (for shallow-copying elements via xsl:copy) | |
| 1320 * | |
| 1321 * Returns a pointer to the new node, or NULL in case of error | |
| 1322 */ | |
| 1323 static xmlNodePtr | |
| 1324 xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 1325 xmlNodePtr insert, int isLRE) | |
| 1326 { | |
| 1327 xmlNodePtr copy; | |
| 1328 | |
| 1329 if ((node->type == XML_DTD_NODE) || (insert == NULL)) | |
| 1330 return(NULL); | |
| 1331 if ((node->type == XML_TEXT_NODE) || | |
| 1332 (node->type == XML_CDATA_SECTION_NODE)) | |
| 1333 return(xsltCopyText(ctxt, insert, node, 0)); | |
| 1334 | |
| 1335 copy = xmlDocCopyNode(node, insert->doc, 0); | |
| 1336 if (copy != NULL) { | |
| 1337 copy->doc = ctxt->output; | |
| 1338 copy = xsltAddChild(insert, copy); | |
| 1339 if (copy == NULL) { | |
| 1340 xsltTransformError(ctxt, NULL, node, | |
| 1341 "xsltShallowCopyElem: copy failed\n"); | |
| 1342 return (copy); | |
| 1343 } | |
| 1344 | |
| 1345 if (node->type == XML_ELEMENT_NODE) { | |
| 1346 /* | |
| 1347 * Add namespaces as they are needed | |
| 1348 */ | |
| 1349 if (node->nsDef != NULL) { | |
| 1350 /* | |
| 1351 * TODO: Remove the LRE case in the refactored code | |
| 1352 * gets enabled. | |
| 1353 */ | |
| 1354 if (isLRE) | |
| 1355 xsltCopyNamespaceList(ctxt, copy, node->nsDef); | |
| 1356 else | |
| 1357 xsltCopyNamespaceListInternal(copy, node->nsDef); | |
| 1358 } | |
| 1359 | |
| 1360 /* | |
| 1361 * URGENT TODO: The problem with this is that it does not | |
| 1362 * copy over all namespace nodes in scope. | |
| 1363 * The damn thing about this is, that we would need to | |
| 1364 * use the xmlGetNsList(), for every single node; this is | |
| 1365 * also done in xsltCopyTree(), but only for the top node. | |
| 1366 */ | |
| 1367 if (node->ns != NULL) { | |
| 1368 if (isLRE) { | |
| 1369 /* | |
| 1370 * REVISIT TODO: Since the non-refactored code still does | |
| 1371 * ns-aliasing, we need to call xsltGetNamespace() here. | |
| 1372 * Remove this when ready. | |
| 1373 */ | |
| 1374 copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy); | |
| 1375 } else { | |
| 1376 copy->ns = xsltGetSpecialNamespace(ctxt, | |
| 1377 node, node->ns->href, node->ns->prefix, copy); | |
| 1378 | |
| 1379 } | |
| 1380 } else if ((insert->type == XML_ELEMENT_NODE) && | |
| 1381 (insert->ns != NULL)) | |
| 1382 { | |
| 1383 /* | |
| 1384 * "Undeclare" the default namespace. | |
| 1385 */ | |
| 1386 xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy); | |
| 1387 } | |
| 1388 } | |
| 1389 } else { | |
| 1390 xsltTransformError(ctxt, NULL, node, | |
| 1391 "xsltShallowCopyElem: copy %s failed\n", node->name); | |
| 1392 } | |
| 1393 return(copy); | |
| 1394 } | |
| 1395 | |
| 1396 /** | |
| 1397 * xsltCopyTreeList: | |
| 1398 * @ctxt: a XSLT process context | |
| 1399 * @invocNode: responsible node in the stylesheet; used for error reports | |
| 1400 * @list: the list of element nodes in the source tree. | |
| 1401 * @insert: the parent in the result tree. | |
| 1402 * @isLRE: is this a literal result element list | |
| 1403 * @topElemVisited: indicates if a top-most element was already processed | |
| 1404 * | |
| 1405 * Make a copy of the full list of tree @list | |
| 1406 * and insert it as last children of @insert | |
| 1407 * | |
| 1408 * NOTE: Not to be used for Literal Result Elements. | |
| 1409 * | |
| 1410 * Used by: | |
| 1411 * - xsltCopyOf() | |
| 1412 * | |
| 1413 * Returns a pointer to the new list, or NULL in case of error | |
| 1414 */ | |
| 1415 static xmlNodePtr | |
| 1416 xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, | |
| 1417 xmlNodePtr list, | |
| 1418 xmlNodePtr insert, int isLRE, int topElemVisited) | |
| 1419 { | |
| 1420 xmlNodePtr copy, ret = NULL; | |
| 1421 | |
| 1422 while (list != NULL) { | |
| 1423 copy = xsltCopyTree(ctxt, invocNode, | |
| 1424 list, insert, isLRE, topElemVisited); | |
| 1425 if (copy != NULL) { | |
| 1426 if (ret == NULL) { | |
| 1427 ret = copy; | |
| 1428 } | |
| 1429 } | |
| 1430 list = list->next; | |
| 1431 } | |
| 1432 return(ret); | |
| 1433 } | |
| 1434 | |
| 1435 /** | |
| 1436 * xsltCopyNamespaceListInternal: | |
| 1437 * @node: the target node | |
| 1438 * @cur: the first namespace | |
| 1439 * | |
| 1440 * Do a copy of a namespace list. If @node is non-NULL the | |
| 1441 * new namespaces are added automatically. | |
| 1442 * Called by: | |
| 1443 * xsltCopyTree() | |
| 1444 * | |
| 1445 * QUESTION: What is the exact difference between this function | |
| 1446 * and xsltCopyNamespaceList() in "namespaces.c"? | |
| 1447 * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases. | |
| 1448 * | |
| 1449 * Returns: a new xmlNsPtr, or NULL in case of error. | |
| 1450 */ | |
| 1451 static xmlNsPtr | |
| 1452 xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) { | |
| 1453 xmlNsPtr ret = NULL; | |
| 1454 xmlNsPtr p = NULL, q, luNs; | |
| 1455 | |
| 1456 if (ns == NULL) | |
| 1457 return(NULL); | |
| 1458 /* | |
| 1459 * One can add namespaces only on element nodes | |
| 1460 */ | |
| 1461 if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE)) | |
| 1462 elem = NULL; | |
| 1463 | |
| 1464 do { | |
| 1465 if (ns->type != XML_NAMESPACE_DECL) | |
| 1466 break; | |
| 1467 /* | |
| 1468 * Avoid duplicating namespace declarations on the tree. | |
| 1469 */ | |
| 1470 if (elem != NULL) { | |
| 1471 if ((elem->ns != NULL) && | |
| 1472 xmlStrEqual(elem->ns->prefix, ns->prefix) && | |
| 1473 xmlStrEqual(elem->ns->href, ns->href)) | |
| 1474 { | |
| 1475 ns = ns->next; | |
| 1476 continue; | |
| 1477 } | |
| 1478 luNs = xmlSearchNs(elem->doc, elem, ns->prefix); | |
| 1479 if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href))) | |
| 1480 { | |
| 1481 ns = ns->next; | |
| 1482 continue; | |
| 1483 } | |
| 1484 } | |
| 1485 q = xmlNewNs(elem, ns->href, ns->prefix); | |
| 1486 if (p == NULL) { | |
| 1487 ret = p = q; | |
| 1488 } else if (q != NULL) { | |
| 1489 p->next = q; | |
| 1490 p = q; | |
| 1491 } | |
| 1492 ns = ns->next; | |
| 1493 } while (ns != NULL); | |
| 1494 return(ret); | |
| 1495 } | |
| 1496 | |
| 1497 /** | |
| 1498 * xsltShallowCopyNsNode: | |
| 1499 * @ctxt: the XSLT transformation context | |
| 1500 * @invocNode: responsible node in the stylesheet; used for error reports | |
| 1501 * @insert: the target element node in the result tree | |
| 1502 * @ns: the namespace node | |
| 1503 * | |
| 1504 * This is used for copying ns-nodes with xsl:copy-of and xsl:copy. | |
| 1505 * | |
| 1506 * Returns a new/existing ns-node, or NULL. | |
| 1507 */ | |
| 1508 static xmlNsPtr | |
| 1509 xsltShallowCopyNsNode(xsltTransformContextPtr ctxt, | |
| 1510 xmlNodePtr invocNode, | |
| 1511 xmlNodePtr insert, | |
| 1512 xmlNsPtr ns) | |
| 1513 { | |
| 1514 /* | |
| 1515 * TODO: Contrary to header comments, this is declared as int. | |
| 1516 * be modified to return a node pointer, or NULL if any error | |
| 1517 */ | |
| 1518 xmlNsPtr tmpns; | |
| 1519 | |
| 1520 if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE)) | |
| 1521 return(NULL); | |
| 1522 | |
| 1523 if (insert->children != NULL) { | |
| 1524 xsltTransformError(ctxt, NULL, invocNode, | |
| 1525 "Namespace nodes must be added before " | |
| 1526 "any child nodes are added to an element.\n"); | |
| 1527 return(NULL); | |
| 1528 } | |
| 1529 /* | |
| 1530 * BIG NOTE: Xalan-J simply overwrites any ns-decls with | |
| 1531 * an equal prefix. We definitively won't do that. | |
| 1532 * | |
| 1533 * MSXML 4.0 and the .NET ignores ns-decls for which an | |
| 1534 * equal prefix is already in use. | |
| 1535 * | |
| 1536 * Saxon raises an error like: | |
| 1537 * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace | |
| 1538 * nodes with the same name". | |
| 1539 * | |
| 1540 * NOTE: We'll currently follow MSXML here. | |
| 1541 * REVISIT TODO: Check if it's better to follow Saxon here. | |
| 1542 */ | |
| 1543 if (ns->prefix == NULL) { | |
| 1544 /* | |
| 1545 * If we are adding ns-nodes to an element using e.g. | |
| 1546 * <xsl:copy-of select="/foo/namespace::*">, then we need | |
| 1547 * to ensure that we don't incorrectly declare a default | |
| 1548 * namespace on an element in no namespace, which otherwise | |
| 1549 * would move the element incorrectly into a namespace, if | |
| 1550 * the node tree is serialized. | |
| 1551 */ | |
| 1552 if (insert->ns == NULL) | |
| 1553 goto occupied; | |
| 1554 } else if ((ns->prefix[0] == 'x') && | |
| 1555 xmlStrEqual(ns->prefix, BAD_CAST "xml")) | |
| 1556 { | |
| 1557 /* | |
| 1558 * The XML namespace is built in. | |
| 1559 */ | |
| 1560 return(NULL); | |
| 1561 } | |
| 1562 | |
| 1563 if (insert->nsDef != NULL) { | |
| 1564 tmpns = insert->nsDef; | |
| 1565 do { | |
| 1566 if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) { | |
| 1567 if ((tmpns->prefix == ns->prefix) || | |
| 1568 xmlStrEqual(tmpns->prefix, ns->prefix)) | |
| 1569 { | |
| 1570 /* | |
| 1571 * Same prefix. | |
| 1572 */ | |
| 1573 if (xmlStrEqual(tmpns->href, ns->href)) | |
| 1574 return(NULL); | |
| 1575 goto occupied; | |
| 1576 } | |
| 1577 } | |
| 1578 tmpns = tmpns->next; | |
| 1579 } while (tmpns != NULL); | |
| 1580 } | |
| 1581 tmpns = xmlSearchNs(insert->doc, insert, ns->prefix); | |
| 1582 if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href)) | |
| 1583 return(NULL); | |
| 1584 /* | |
| 1585 * Declare a new namespace. | |
| 1586 * TODO: The problem (wrt efficiency) with this xmlNewNs() is | |
| 1587 * that it will again search the already declared namespaces | |
| 1588 * for a duplicate :-/ | |
| 1589 */ | |
| 1590 return(xmlNewNs(insert, ns->href, ns->prefix)); | |
| 1591 | |
| 1592 occupied: | |
| 1593 /* | |
| 1594 * TODO: We could as well raise an error here (like Saxon does), | |
| 1595 * or at least generate a warning. | |
| 1596 */ | |
| 1597 return(NULL); | |
| 1598 } | |
| 1599 | |
| 1600 /** | |
| 1601 * xsltCopyTree: | |
| 1602 * @ctxt: the XSLT transformation context | |
| 1603 * @invocNode: responsible node in the stylesheet; used for error reports | |
| 1604 * @node: the element node in the source tree | |
| 1605 * @insert: the parent in the result tree | |
| 1606 * @isLRE: indicates if @node is a Literal Result Element | |
| 1607 * @topElemVisited: indicates if a top-most element was already processed | |
| 1608 * | |
| 1609 * Make a copy of the full tree under the element node @node | |
| 1610 * and insert it as last child of @insert | |
| 1611 * | |
| 1612 * NOTE: Not to be used for Literal Result Elements. | |
| 1613 * | |
| 1614 * Used by: | |
| 1615 * - xsltCopyOf() | |
| 1616 * | |
| 1617 * Returns a pointer to the new tree, or NULL in case of error | |
| 1618 */ | |
| 1619 static xmlNodePtr | |
| 1620 xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, | |
| 1621 xmlNodePtr node, xmlNodePtr insert, int isLRE, | |
| 1622 int topElemVisited) | |
| 1623 { | |
| 1624 xmlNodePtr copy; | |
| 1625 | |
| 1626 if (node == NULL) | |
| 1627 return(NULL); | |
| 1628 switch (node->type) { | |
| 1629 case XML_ELEMENT_NODE: | |
| 1630 case XML_ENTITY_REF_NODE: | |
| 1631 case XML_ENTITY_NODE: | |
| 1632 case XML_PI_NODE: | |
| 1633 case XML_COMMENT_NODE: | |
| 1634 case XML_DOCUMENT_NODE: | |
| 1635 case XML_HTML_DOCUMENT_NODE: | |
| 1636 #ifdef LIBXML_DOCB_ENABLED | |
| 1637 case XML_DOCB_DOCUMENT_NODE: | |
| 1638 #endif | |
| 1639 break; | |
| 1640 case XML_TEXT_NODE: { | |
| 1641 int noenc = (node->name == xmlStringTextNoenc); | |
| 1642 return(xsltCopyTextString(ctxt, insert, node->content, noenc)); | |
| 1643 } | |
| 1644 case XML_CDATA_SECTION_NODE: | |
| 1645 return(xsltCopyTextString(ctxt, insert, node->content, 0)); | |
| 1646 case XML_ATTRIBUTE_NODE: | |
| 1647 return((xmlNodePtr) | |
| 1648 xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node))
; | |
| 1649 case XML_NAMESPACE_DECL: | |
| 1650 return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode, | |
| 1651 insert, (xmlNsPtr) node)); | |
| 1652 | |
| 1653 case XML_DOCUMENT_TYPE_NODE: | |
| 1654 case XML_DOCUMENT_FRAG_NODE: | |
| 1655 case XML_NOTATION_NODE: | |
| 1656 case XML_DTD_NODE: | |
| 1657 case XML_ELEMENT_DECL: | |
| 1658 case XML_ATTRIBUTE_DECL: | |
| 1659 case XML_ENTITY_DECL: | |
| 1660 case XML_XINCLUDE_START: | |
| 1661 case XML_XINCLUDE_END: | |
| 1662 return(NULL); | |
| 1663 } | |
| 1664 if (XSLT_IS_RES_TREE_FRAG(node)) { | |
| 1665 if (node->children != NULL) | |
| 1666 copy = xsltCopyTreeList(ctxt, invocNode, | |
| 1667 node->children, insert, 0, 0); | |
| 1668 else | |
| 1669 copy = NULL; | |
| 1670 return(copy); | |
| 1671 } | |
| 1672 copy = xmlDocCopyNode(node, insert->doc, 0); | |
| 1673 if (copy != NULL) { | |
| 1674 copy->doc = ctxt->output; | |
| 1675 copy = xsltAddChild(insert, copy); | |
| 1676 if (copy == NULL) { | |
| 1677 xsltTransformError(ctxt, NULL, invocNode, | |
| 1678 "xsltCopyTree: Copying of '%s' failed.\n", node->name); | |
| 1679 return (copy); | |
| 1680 } | |
| 1681 /* | |
| 1682 * The node may have been coalesced into another text node. | |
| 1683 */ | |
| 1684 if (insert->last != copy) | |
| 1685 return(insert->last); | |
| 1686 copy->next = NULL; | |
| 1687 | |
| 1688 if (node->type == XML_ELEMENT_NODE) { | |
| 1689 /* | |
| 1690 * Copy in-scope namespace nodes. | |
| 1691 * | |
| 1692 * REVISIT: Since we try to reuse existing in-scope ns-decls by | |
| 1693 * using xmlSearchNsByHref(), this will eventually change | |
| 1694 * the prefix of an original ns-binding; thus it might | |
| 1695 * break QNames in element/attribute content. | |
| 1696 * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation | |
| 1697 * context, plus a ns-lookup function, which writes directly | |
| 1698 * to a given list, then we wouldn't need to create/free the | |
| 1699 * nsList every time. | |
| 1700 */ | |
| 1701 if ((topElemVisited == 0) && | |
| 1702 (node->parent != NULL) && | |
| 1703 (node->parent->type != XML_DOCUMENT_NODE) && | |
| 1704 (node->parent->type != XML_HTML_DOCUMENT_NODE)) | |
| 1705 { | |
| 1706 xmlNsPtr *nsList, *curns, ns; | |
| 1707 | |
| 1708 /* | |
| 1709 * If this is a top-most element in a tree to be | |
| 1710 * copied, then we need to ensure that all in-scope | |
| 1711 * namespaces are copied over. For nodes deeper in the | |
| 1712 * tree, it is sufficient to reconcile only the ns-decls | |
| 1713 * (node->nsDef entries). | |
| 1714 */ | |
| 1715 | |
| 1716 nsList = xmlGetNsList(node->doc, node); | |
| 1717 if (nsList != NULL) { | |
| 1718 curns = nsList; | |
| 1719 do { | |
| 1720 /* | |
| 1721 * Search by prefix first in order to break as less | |
| 1722 * QNames in element/attribute content as possible. | |
| 1723 */ | |
| 1724 ns = xmlSearchNs(insert->doc, insert, | |
| 1725 (*curns)->prefix); | |
| 1726 | |
| 1727 if ((ns == NULL) || | |
| 1728 (! xmlStrEqual(ns->href, (*curns)->href))) | |
| 1729 { | |
| 1730 ns = NULL; | |
| 1731 /* | |
| 1732 * Search by namespace name. | |
| 1733 * REVISIT TODO: Currently disabled. | |
| 1734 */ | |
| 1735 #if 0 | |
| 1736 ns = xmlSearchNsByHref(insert->doc, | |
| 1737 insert, (*curns)->href); | |
| 1738 #endif | |
| 1739 } | |
| 1740 if (ns == NULL) { | |
| 1741 /* | |
| 1742 * Declare a new namespace on the copied element. | |
| 1743 */ | |
| 1744 ns = xmlNewNs(copy, (*curns)->href, | |
| 1745 (*curns)->prefix); | |
| 1746 /* TODO: Handle errors */ | |
| 1747 } | |
| 1748 if (node->ns == *curns) { | |
| 1749 /* | |
| 1750 * If this was the original's namespace then set | |
| 1751 * the generated counterpart on the copy. | |
| 1752 */ | |
| 1753 copy->ns = ns; | |
| 1754 } | |
| 1755 curns++; | |
| 1756 } while (*curns != NULL); | |
| 1757 xmlFree(nsList); | |
| 1758 } | |
| 1759 } else if (node->nsDef != NULL) { | |
| 1760 /* | |
| 1761 * Copy over all namespace declaration attributes. | |
| 1762 */ | |
| 1763 if (node->nsDef != NULL) { | |
| 1764 if (isLRE) | |
| 1765 xsltCopyNamespaceList(ctxt, copy, node->nsDef); | |
| 1766 else | |
| 1767 xsltCopyNamespaceListInternal(copy, node->nsDef); | |
| 1768 } | |
| 1769 } | |
| 1770 /* | |
| 1771 * Set the namespace. | |
| 1772 */ | |
| 1773 if (node->ns != NULL) { | |
| 1774 if (copy->ns == NULL) { | |
| 1775 /* | |
| 1776 * This will map copy->ns to one of the newly created | |
| 1777 * in-scope ns-decls, OR create a new ns-decl on @copy. | |
| 1778 */ | |
| 1779 copy->ns = xsltGetSpecialNamespace(ctxt, invocNode, | |
| 1780 node->ns->href, node->ns->prefix, copy); | |
| 1781 } | |
| 1782 } else if ((insert->type == XML_ELEMENT_NODE) && | |
| 1783 (insert->ns != NULL)) | |
| 1784 { | |
| 1785 /* | |
| 1786 * "Undeclare" the default namespace on @copy with xmlns="". | |
| 1787 */ | |
| 1788 xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy); | |
| 1789 } | |
| 1790 /* | |
| 1791 * Copy attribute nodes. | |
| 1792 */ | |
| 1793 if (node->properties != NULL) { | |
| 1794 xsltCopyAttrListNoOverwrite(ctxt, invocNode, | |
| 1795 copy, node->properties); | |
| 1796 } | |
| 1797 if (topElemVisited == 0) | |
| 1798 topElemVisited = 1; | |
| 1799 } | |
| 1800 /* | |
| 1801 * Copy the subtree. | |
| 1802 */ | |
| 1803 if (node->children != NULL) { | |
| 1804 xsltCopyTreeList(ctxt, invocNode, | |
| 1805 node->children, copy, isLRE, topElemVisited); | |
| 1806 } | |
| 1807 } else { | |
| 1808 xsltTransformError(ctxt, NULL, invocNode, | |
| 1809 "xsltCopyTree: Copying of '%s' failed.\n", node->name); | |
| 1810 } | |
| 1811 return(copy); | |
| 1812 } | |
| 1813 | |
| 1814 /************************************************************************ | |
| 1815 * * | |
| 1816 * Error/fallback processing * | |
| 1817 * * | |
| 1818 ************************************************************************/ | |
| 1819 | |
| 1820 /** | |
| 1821 * xsltApplyFallbacks: | |
| 1822 * @ctxt: a XSLT process context | |
| 1823 * @node: the node in the source tree. | |
| 1824 * @inst: the node generating the error | |
| 1825 * | |
| 1826 * Process possible xsl:fallback nodes present under @inst | |
| 1827 * | |
| 1828 * Returns the number of xsl:fallback element found and processed | |
| 1829 */ | |
| 1830 static int | |
| 1831 xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 1832 xmlNodePtr inst) { | |
| 1833 | |
| 1834 xmlNodePtr child; | |
| 1835 int ret = 0; | |
| 1836 | |
| 1837 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || | |
| 1838 (inst->children == NULL)) | |
| 1839 return(0); | |
| 1840 | |
| 1841 child = inst->children; | |
| 1842 while (child != NULL) { | |
| 1843 if ((IS_XSLT_ELEM(child)) && | |
| 1844 (xmlStrEqual(child->name, BAD_CAST "fallback"))) { | |
| 1845 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 1846 xsltGenericDebug(xsltGenericDebugContext, | |
| 1847 "applying xsl:fallback\n"); | |
| 1848 #endif | |
| 1849 ret++; | |
| 1850 xsltApplySequenceConstructor(ctxt, node, child->children, | |
| 1851 NULL); | |
| 1852 } | |
| 1853 child = child->next; | |
| 1854 } | |
| 1855 return(ret); | |
| 1856 } | |
| 1857 | |
| 1858 /************************************************************************ | |
| 1859 * * | |
| 1860 * Default processing * | |
| 1861 * * | |
| 1862 ************************************************************************/ | |
| 1863 | |
| 1864 /** | |
| 1865 * xsltDefaultProcessOneNode: | |
| 1866 * @ctxt: a XSLT process context | |
| 1867 * @node: the node in the source tree. | |
| 1868 * @params: extra parameters passed to the template if any | |
| 1869 * | |
| 1870 * Process the source node with the default built-in template rule: | |
| 1871 * <xsl:template match="*|/"> | |
| 1872 * <xsl:apply-templates/> | |
| 1873 * </xsl:template> | |
| 1874 * | |
| 1875 * and | |
| 1876 * | |
| 1877 * <xsl:template match="text()|@*"> | |
| 1878 * <xsl:value-of select="."/> | |
| 1879 * </xsl:template> | |
| 1880 * | |
| 1881 * Note also that namespace declarations are copied directly: | |
| 1882 * | |
| 1883 * the built-in template rule is the only template rule that is applied | |
| 1884 * for namespace nodes. | |
| 1885 */ | |
| 1886 static void | |
| 1887 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 1888 xsltStackElemPtr params) { | |
| 1889 xmlNodePtr copy; | |
| 1890 xmlNodePtr delete = NULL, cur; | |
| 1891 int nbchild = 0, oldSize; | |
| 1892 int childno = 0, oldPos; | |
| 1893 xsltTemplatePtr template; | |
| 1894 | |
| 1895 CHECK_STOPPED; | |
| 1896 /* | |
| 1897 * Handling of leaves | |
| 1898 */ | |
| 1899 switch (node->type) { | |
| 1900 case XML_DOCUMENT_NODE: | |
| 1901 case XML_HTML_DOCUMENT_NODE: | |
| 1902 case XML_ELEMENT_NODE: | |
| 1903 break; | |
| 1904 case XML_CDATA_SECTION_NODE: | |
| 1905 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 1906 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGeneric
DebugContext, | |
| 1907 "xsltDefaultProcessOneNode: copy CDATA %s\n", | |
| 1908 node->content)); | |
| 1909 #endif | |
| 1910 copy = xsltCopyText(ctxt, ctxt->insert, node, 0); | |
| 1911 if (copy == NULL) { | |
| 1912 xsltTransformError(ctxt, NULL, node, | |
| 1913 "xsltDefaultProcessOneNode: cdata copy failed\n"); | |
| 1914 } | |
| 1915 return; | |
| 1916 case XML_TEXT_NODE: | |
| 1917 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 1918 if (node->content == NULL) { | |
| 1919 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGen
ericDebugContext, | |
| 1920 "xsltDefaultProcessOneNode: copy empty text\n")); | |
| 1921 return; | |
| 1922 } else { | |
| 1923 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGen
ericDebugContext, | |
| 1924 "xsltDefaultProcessOneNode: copy text %s\n", | |
| 1925 node->content)); | |
| 1926 } | |
| 1927 #endif | |
| 1928 copy = xsltCopyText(ctxt, ctxt->insert, node, 0); | |
| 1929 if (copy == NULL) { | |
| 1930 xsltTransformError(ctxt, NULL, node, | |
| 1931 "xsltDefaultProcessOneNode: text copy failed\n"); | |
| 1932 } | |
| 1933 return; | |
| 1934 case XML_ATTRIBUTE_NODE: | |
| 1935 cur = node->children; | |
| 1936 while ((cur != NULL) && (cur->type != XML_TEXT_NODE)) | |
| 1937 cur = cur->next; | |
| 1938 if (cur == NULL) { | |
| 1939 xsltTransformError(ctxt, NULL, node, | |
| 1940 "xsltDefaultProcessOneNode: no text for attribute\n"); | |
| 1941 } else { | |
| 1942 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 1943 if (cur->content == NULL) { | |
| 1944 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsl
tGenericDebugContext, | |
| 1945 "xsltDefaultProcessOneNode: copy empty text\n")); | |
| 1946 } else { | |
| 1947 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsl
tGenericDebugContext, | |
| 1948 "xsltDefaultProcessOneNode: copy text %s\n", | |
| 1949 cur->content)); | |
| 1950 } | |
| 1951 #endif | |
| 1952 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); | |
| 1953 if (copy == NULL) { | |
| 1954 xsltTransformError(ctxt, NULL, node, | |
| 1955 "xsltDefaultProcessOneNode: text copy failed\n"); | |
| 1956 } | |
| 1957 } | |
| 1958 return; | |
| 1959 default: | |
| 1960 return; | |
| 1961 } | |
| 1962 /* | |
| 1963 * Handling of Elements: first pass, cleanup and counting | |
| 1964 */ | |
| 1965 cur = node->children; | |
| 1966 while (cur != NULL) { | |
| 1967 switch (cur->type) { | |
| 1968 case XML_TEXT_NODE: | |
| 1969 case XML_CDATA_SECTION_NODE: | |
| 1970 case XML_DOCUMENT_NODE: | |
| 1971 case XML_HTML_DOCUMENT_NODE: | |
| 1972 case XML_ELEMENT_NODE: | |
| 1973 case XML_PI_NODE: | |
| 1974 case XML_COMMENT_NODE: | |
| 1975 nbchild++; | |
| 1976 break; | |
| 1977 case XML_DTD_NODE: | |
| 1978 /* Unlink the DTD, it's still reachable using doc->intSubset */ | |
| 1979 if (cur->next != NULL) | |
| 1980 cur->next->prev = cur->prev; | |
| 1981 if (cur->prev != NULL) | |
| 1982 cur->prev->next = cur->next; | |
| 1983 break; | |
| 1984 default: | |
| 1985 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 1986 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGen
ericDebugContext, | |
| 1987 "xsltDefaultProcessOneNode: skipping node type %d\n", | |
| 1988 cur->type)); | |
| 1989 #endif | |
| 1990 delete = cur; | |
| 1991 } | |
| 1992 cur = cur->next; | |
| 1993 if (delete != NULL) { | |
| 1994 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 1995 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGeneric
DebugContext, | |
| 1996 "xsltDefaultProcessOneNode: removing ignorable blank node\n")); | |
| 1997 #endif | |
| 1998 xmlUnlinkNode(delete); | |
| 1999 xmlFreeNode(delete); | |
| 2000 delete = NULL; | |
| 2001 } | |
| 2002 } | |
| 2003 if (delete != NULL) { | |
| 2004 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2005 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebu
gContext, | |
| 2006 "xsltDefaultProcessOneNode: removing ignorable blank node\n")); | |
| 2007 #endif | |
| 2008 xmlUnlinkNode(delete); | |
| 2009 xmlFreeNode(delete); | |
| 2010 delete = NULL; | |
| 2011 } | |
| 2012 | |
| 2013 /* | |
| 2014 * Handling of Elements: second pass, actual processing | |
| 2015 * | |
| 2016 * Note that params are passed to the next template. This matches | |
| 2017 * XSLT 2.0 behavior but doesn't conform to XSLT 1.0. | |
| 2018 */ | |
| 2019 oldSize = ctxt->xpathCtxt->contextSize; | |
| 2020 oldPos = ctxt->xpathCtxt->proximityPosition; | |
| 2021 cur = node->children; | |
| 2022 while (cur != NULL) { | |
| 2023 childno++; | |
| 2024 switch (cur->type) { | |
| 2025 case XML_DOCUMENT_NODE: | |
| 2026 case XML_HTML_DOCUMENT_NODE: | |
| 2027 case XML_ELEMENT_NODE: | |
| 2028 ctxt->xpathCtxt->contextSize = nbchild; | |
| 2029 ctxt->xpathCtxt->proximityPosition = childno; | |
| 2030 xsltProcessOneNode(ctxt, cur, params); | |
| 2031 break; | |
| 2032 case XML_CDATA_SECTION_NODE: | |
| 2033 template = xsltGetTemplate(ctxt, cur, NULL); | |
| 2034 if (template) { | |
| 2035 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2036 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsl
tGenericDebugContext, | |
| 2037 "xsltDefaultProcessOneNode: applying template for CDATA %s\n", | |
| 2038 cur->content)); | |
| 2039 #endif | |
| 2040 /* | |
| 2041 * Instantiate the xsl:template. | |
| 2042 */ | |
| 2043 xsltApplyXSLTTemplate(ctxt, cur, template->content, | |
| 2044 template, params); | |
| 2045 } else /* if (ctxt->mode == NULL) */ { | |
| 2046 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2047 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsl
tGenericDebugContext, | |
| 2048 "xsltDefaultProcessOneNode: copy CDATA %s\n", | |
| 2049 cur->content)); | |
| 2050 #endif | |
| 2051 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); | |
| 2052 if (copy == NULL) { | |
| 2053 xsltTransformError(ctxt, NULL, cur, | |
| 2054 "xsltDefaultProcessOneNode: cdata copy failed\n"); | |
| 2055 } | |
| 2056 } | |
| 2057 break; | |
| 2058 case XML_TEXT_NODE: | |
| 2059 template = xsltGetTemplate(ctxt, cur, NULL); | |
| 2060 if (template) { | |
| 2061 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2062 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsl
tGenericDebugContext, | |
| 2063 "xsltDefaultProcessOneNode: applying template for text %s\n", | |
| 2064 cur->content)); | |
| 2065 #endif | |
| 2066 ctxt->xpathCtxt->contextSize = nbchild; | |
| 2067 ctxt->xpathCtxt->proximityPosition = childno; | |
| 2068 /* | |
| 2069 * Instantiate the xsl:template. | |
| 2070 */ | |
| 2071 xsltApplyXSLTTemplate(ctxt, cur, template->content, | |
| 2072 template, params); | |
| 2073 } else /* if (ctxt->mode == NULL) */ { | |
| 2074 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2075 if (cur->content == NULL) { | |
| 2076 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug
(xsltGenericDebugContext, | |
| 2077 "xsltDefaultProcessOneNode: copy empty text\n")); | |
| 2078 } else { | |
| 2079 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug
(xsltGenericDebugContext, | |
| 2080 "xsltDefaultProcessOneNode: copy text %s\n", | |
| 2081 cur->content)); | |
| 2082 } | |
| 2083 #endif | |
| 2084 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); | |
| 2085 if (copy == NULL) { | |
| 2086 xsltTransformError(ctxt, NULL, cur, | |
| 2087 "xsltDefaultProcessOneNode: text copy failed\n"); | |
| 2088 } | |
| 2089 } | |
| 2090 break; | |
| 2091 case XML_PI_NODE: | |
| 2092 case XML_COMMENT_NODE: | |
| 2093 template = xsltGetTemplate(ctxt, cur, NULL); | |
| 2094 if (template) { | |
| 2095 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2096 if (cur->type == XML_PI_NODE) { | |
| 2097 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug
(xsltGenericDebugContext, | |
| 2098 "xsltDefaultProcessOneNode: template found for PI %s\n", | |
| 2099 cur->name)); | |
| 2100 } else if (cur->type == XML_COMMENT_NODE) { | |
| 2101 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug
(xsltGenericDebugContext, | |
| 2102 "xsltDefaultProcessOneNode: template found for comment\n"))
; | |
| 2103 } | |
| 2104 #endif | |
| 2105 ctxt->xpathCtxt->contextSize = nbchild; | |
| 2106 ctxt->xpathCtxt->proximityPosition = childno; | |
| 2107 /* | |
| 2108 * Instantiate the xsl:template. | |
| 2109 */ | |
| 2110 xsltApplyXSLTTemplate(ctxt, cur, template->content, | |
| 2111 template, params); | |
| 2112 } | |
| 2113 break; | |
| 2114 default: | |
| 2115 break; | |
| 2116 } | |
| 2117 cur = cur->next; | |
| 2118 } | |
| 2119 ctxt->xpathCtxt->contextSize = oldSize; | |
| 2120 ctxt->xpathCtxt->proximityPosition = oldPos; | |
| 2121 } | |
| 2122 | |
| 2123 /** | |
| 2124 * xsltProcessOneNode: | |
| 2125 * @ctxt: a XSLT process context | |
| 2126 * @contextNode: the "current node" in the source tree | |
| 2127 * @withParams: extra parameters (e.g. xsl:with-param) passed to the | |
| 2128 * template if any | |
| 2129 * | |
| 2130 * Process the source node. | |
| 2131 */ | |
| 2132 void | |
| 2133 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, | |
| 2134 xsltStackElemPtr withParams) | |
| 2135 { | |
| 2136 xsltTemplatePtr templ; | |
| 2137 xmlNodePtr oldNode; | |
| 2138 | |
| 2139 templ = xsltGetTemplate(ctxt, contextNode, NULL); | |
| 2140 /* | |
| 2141 * If no template is found, apply the default rule. | |
| 2142 */ | |
| 2143 if (templ == NULL) { | |
| 2144 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2145 if (contextNode->type == XML_DOCUMENT_NODE) { | |
| 2146 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGeneric
DebugContext, | |
| 2147 "xsltProcessOneNode: no template found for /\n")); | |
| 2148 } else if (contextNode->type == XML_CDATA_SECTION_NODE) { | |
| 2149 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGeneric
DebugContext, | |
| 2150 "xsltProcessOneNode: no template found for CDATA\n")); | |
| 2151 } else if (contextNode->type == XML_ATTRIBUTE_NODE) { | |
| 2152 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGeneric
DebugContext, | |
| 2153 "xsltProcessOneNode: no template found for attribute %s\n", | |
| 2154 ((xmlAttrPtr) contextNode)->name)); | |
| 2155 } else { | |
| 2156 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGeneric
DebugContext, | |
| 2157 "xsltProcessOneNode: no template found for %s\n", contextNode->name
)); | |
| 2158 } | |
| 2159 #endif | |
| 2160 oldNode = ctxt->node; | |
| 2161 ctxt->node = contextNode; | |
| 2162 xsltDefaultProcessOneNode(ctxt, contextNode, withParams); | |
| 2163 ctxt->node = oldNode; | |
| 2164 return; | |
| 2165 } | |
| 2166 | |
| 2167 if (contextNode->type == XML_ATTRIBUTE_NODE) { | |
| 2168 xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule; | |
| 2169 /* | |
| 2170 * Set the "current template rule". | |
| 2171 */ | |
| 2172 ctxt->currentTemplateRule = templ; | |
| 2173 | |
| 2174 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2175 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebu
gContext, | |
| 2176 "xsltProcessOneNode: applying template '%s' for attribute %s\n", | |
| 2177 templ->match, contextNode->name)); | |
| 2178 #endif | |
| 2179 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withPara
ms); | |
| 2180 | |
| 2181 ctxt->currentTemplateRule = oldCurTempRule; | |
| 2182 } else { | |
| 2183 xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule; | |
| 2184 /* | |
| 2185 * Set the "current template rule". | |
| 2186 */ | |
| 2187 ctxt->currentTemplateRule = templ; | |
| 2188 | |
| 2189 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2190 if (contextNode->type == XML_DOCUMENT_NODE) { | |
| 2191 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGeneric
DebugContext, | |
| 2192 "xsltProcessOneNode: applying template '%s' for /\n", | |
| 2193 templ->match)); | |
| 2194 } else { | |
| 2195 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGeneric
DebugContext, | |
| 2196 "xsltProcessOneNode: applying template '%s' for %s\n", | |
| 2197 templ->match, contextNode->name)); | |
| 2198 } | |
| 2199 #endif | |
| 2200 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withPara
ms); | |
| 2201 | |
| 2202 ctxt->currentTemplateRule = oldCurTempRule; | |
| 2203 } | |
| 2204 } | |
| 2205 | |
| 2206 static xmlNodePtr | |
| 2207 xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt, | |
| 2208 xmlNodePtr contextNode, | |
| 2209 xmlNodePtr list, | |
| 2210 xsltTemplatePtr templ, | |
| 2211 int *addCallResult) | |
| 2212 { | |
| 2213 xmlNodePtr debugedNode = NULL; | |
| 2214 | |
| 2215 if (ctxt->debugStatus != XSLT_DEBUG_NONE) { | |
| 2216 if (templ) { | |
| 2217 *addCallResult = xslAddCall(templ, templ->elem); | |
| 2218 } else { | |
| 2219 *addCallResult = xslAddCall(NULL, list); | |
| 2220 } | |
| 2221 switch (ctxt->debugStatus) { | |
| 2222 case XSLT_DEBUG_RUN_RESTART: | |
| 2223 case XSLT_DEBUG_QUIT: | |
| 2224 if (*addCallResult) | |
| 2225 xslDropCall(); | |
| 2226 return(NULL); | |
| 2227 } | |
| 2228 if (templ) { | |
| 2229 xslHandleDebugger(templ->elem, contextNode, templ, ctxt); | |
| 2230 debugedNode = templ->elem; | |
| 2231 } else if (list) { | |
| 2232 xslHandleDebugger(list, contextNode, templ, ctxt); | |
| 2233 debugedNode = list; | |
| 2234 } else if (ctxt->inst) { | |
| 2235 xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt); | |
| 2236 debugedNode = ctxt->inst; | |
| 2237 } | |
| 2238 } | |
| 2239 return(debugedNode); | |
| 2240 } | |
| 2241 | |
| 2242 /** | |
| 2243 * xsltLocalVariablePush: | |
| 2244 * @ctxt: the transformation context | |
| 2245 * @variable: variable to be pushed to the variable stack | |
| 2246 * @level: new value for variable's level | |
| 2247 * | |
| 2248 * Places the variable onto the local variable stack | |
| 2249 * | |
| 2250 * Returns: 0 for success, -1 for any error | |
| 2251 * **NOTE:** | |
| 2252 * This is an internal routine and should not be called by users! | |
| 2253 */ | |
| 2254 int | |
| 2255 xsltLocalVariablePush(xsltTransformContextPtr ctxt, | |
| 2256 xsltStackElemPtr variable, | |
| 2257 int level) | |
| 2258 { | |
| 2259 if (ctxt->varsMax == 0) { | |
| 2260 ctxt->varsMax = 10; | |
| 2261 ctxt->varsTab = | |
| 2262 (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax * | |
| 2263 sizeof(ctxt->varsTab[0])); | |
| 2264 if (ctxt->varsTab == NULL) { | |
| 2265 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); | |
| 2266 return (-1); | |
| 2267 } | |
| 2268 } | |
| 2269 if (ctxt->varsNr >= ctxt->varsMax) { | |
| 2270 ctxt->varsMax *= 2; | |
| 2271 ctxt->varsTab = | |
| 2272 (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab, | |
| 2273 ctxt->varsMax * | |
| 2274 sizeof(ctxt->varsTab[0])); | |
| 2275 if (ctxt->varsTab == NULL) { | |
| 2276 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); | |
| 2277 return (-1); | |
| 2278 } | |
| 2279 } | |
| 2280 ctxt->varsTab[ctxt->varsNr++] = variable; | |
| 2281 ctxt->vars = variable; | |
| 2282 variable->level = level; | |
| 2283 return(0); | |
| 2284 } | |
| 2285 | |
| 2286 /** | |
| 2287 * xsltReleaseLocalRVTs: | |
| 2288 * | |
| 2289 * Fragments which are results of extension instructions | |
| 2290 * are preserved; all other fragments are freed/cached. | |
| 2291 */ | |
| 2292 static void | |
| 2293 xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base) | |
| 2294 { | |
| 2295 xmlDocPtr cur = ctxt->localRVT, tmp; | |
| 2296 | |
| 2297 if (cur == base) | |
| 2298 return; | |
| 2299 if (cur->prev != NULL) | |
| 2300 xsltTransformError(ctxt, NULL, NULL, "localRVT not head of list\n"); | |
| 2301 | |
| 2302 do { | |
| 2303 tmp = cur; | |
| 2304 cur = (xmlDocPtr) cur->next; | |
| 2305 if (tmp->psvi == XSLT_RVT_LOCAL) { | |
| 2306 xsltReleaseRVT(ctxt, tmp); | |
| 2307 } else if (tmp->psvi == XSLT_RVT_GLOBAL) { | |
| 2308 xsltRegisterPersistRVT(ctxt, tmp); | |
| 2309 } else if (tmp->psvi != XSLT_RVT_FUNC_RESULT) { | |
| 2310 xmlGenericError(xmlGenericErrorContext, | |
| 2311 "xsltReleaseLocalRVTs: Unexpected RVT flag %p\n", | |
| 2312 tmp->psvi); | |
| 2313 } | |
| 2314 } while (cur != base); | |
| 2315 | |
| 2316 if (base != NULL) | |
| 2317 base->prev = NULL; | |
| 2318 ctxt->localRVT = base; | |
| 2319 } | |
| 2320 | |
| 2321 /** | |
| 2322 * xsltApplySequenceConstructor: | |
| 2323 * @ctxt: a XSLT process context | |
| 2324 * @contextNode: the "current node" in the source tree | |
| 2325 * @list: the nodes of a sequence constructor; | |
| 2326 * (plus leading xsl:param elements) | |
| 2327 * @templ: the compiled xsl:template (optional) | |
| 2328 * | |
| 2329 * Processes a sequence constructor. | |
| 2330 * | |
| 2331 * NOTE: ctxt->currentTemplateRule was introduced to reflect the | |
| 2332 * semantics of "current template rule". I.e. the field ctxt->templ | |
| 2333 * is not intended to reflect this, thus always pushed onto the | |
| 2334 * template stack. | |
| 2335 */ | |
| 2336 static void | |
| 2337 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, | |
| 2338 xmlNodePtr contextNode, xmlNodePtr list, | |
| 2339 xsltTemplatePtr templ) | |
| 2340 { | |
| 2341 xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode; | |
| 2342 xmlNodePtr cur, insert, copy = NULL; | |
| 2343 int level = 0, oldVarsNr; | |
| 2344 xmlDocPtr oldLocalFragmentTop; | |
| 2345 | |
| 2346 #ifdef XSLT_REFACTORED | |
| 2347 xsltStylePreCompPtr info; | |
| 2348 #endif | |
| 2349 | |
| 2350 #ifdef WITH_DEBUGGER | |
| 2351 int addCallResult = 0; | |
| 2352 xmlNodePtr debuggedNode = NULL; | |
| 2353 #endif | |
| 2354 | |
| 2355 if (ctxt == NULL) | |
| 2356 return; | |
| 2357 | |
| 2358 #ifdef WITH_DEBUGGER | |
| 2359 if (ctxt->debugStatus != XSLT_DEBUG_NONE) { | |
| 2360 debuggedNode = | |
| 2361 xsltDebuggerStartSequenceConstructor(ctxt, contextNode, | |
| 2362 list, templ, &addCallResult); | |
| 2363 if (debuggedNode == NULL) | |
| 2364 return; | |
| 2365 } | |
| 2366 #endif | |
| 2367 | |
| 2368 if (list == NULL) | |
| 2369 return; | |
| 2370 CHECK_STOPPED; | |
| 2371 | |
| 2372 /* | |
| 2373 * Check for infinite recursion: stop if the maximum of nested templates | |
| 2374 * is excceeded. Adjust xsltMaxDepth if you need more. | |
| 2375 */ | |
| 2376 if (ctxt->depth >= ctxt->maxTemplateDepth) { | |
| 2377 xsltTransformError(ctxt, NULL, list, | |
| 2378 "xsltApplySequenceConstructor: A potential infinite template " | |
| 2379 "recursion was detected.\n" | |
| 2380 "You can adjust xsltMaxDepth (--maxdepth) in order to " | |
| 2381 "raise the maximum number of nested template calls and " | |
| 2382 "variables/params (currently set to %d).\n", | |
| 2383 ctxt->maxTemplateDepth); | |
| 2384 xsltDebug(ctxt, contextNode, list, NULL); | |
| 2385 ctxt->state = XSLT_STATE_STOPPED; | |
| 2386 return; | |
| 2387 } | |
| 2388 ctxt->depth++; | |
| 2389 | |
| 2390 oldLocalFragmentTop = ctxt->localRVT; | |
| 2391 oldInsert = insert = ctxt->insert; | |
| 2392 oldInst = oldCurInst = ctxt->inst; | |
| 2393 oldContextNode = ctxt->node; | |
| 2394 /* | |
| 2395 * Save current number of variables on the stack; new vars are popped when | |
| 2396 * exiting. | |
| 2397 */ | |
| 2398 oldVarsNr = ctxt->varsNr; | |
| 2399 /* | |
| 2400 * Process the sequence constructor. | |
| 2401 */ | |
| 2402 cur = list; | |
| 2403 while (cur != NULL) { | |
| 2404 ctxt->inst = cur; | |
| 2405 | |
| 2406 #ifdef WITH_DEBUGGER | |
| 2407 switch (ctxt->debugStatus) { | |
| 2408 case XSLT_DEBUG_RUN_RESTART: | |
| 2409 case XSLT_DEBUG_QUIT: | |
| 2410 break; | |
| 2411 | |
| 2412 } | |
| 2413 #endif | |
| 2414 /* | |
| 2415 * Test; we must have a valid insertion point. | |
| 2416 */ | |
| 2417 if (insert == NULL) { | |
| 2418 | |
| 2419 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2420 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGener
icDebugContext, | |
| 2421 "xsltApplySequenceConstructor: insert == NULL !\n")); | |
| 2422 #endif | |
| 2423 goto error; | |
| 2424 } | |
| 2425 | |
| 2426 #ifdef WITH_DEBUGGER | |
| 2427 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur)) | |
| 2428 xslHandleDebugger(cur, contextNode, templ, ctxt); | |
| 2429 #endif | |
| 2430 | |
| 2431 #ifdef XSLT_REFACTORED | |
| 2432 if (cur->type == XML_ELEMENT_NODE) { | |
| 2433 info = (xsltStylePreCompPtr) cur->psvi; | |
| 2434 /* | |
| 2435 * We expect a compiled representation on: | |
| 2436 * 1) XSLT instructions of this XSLT version (1.0) | |
| 2437 * (with a few exceptions) | |
| 2438 * 2) Literal result elements | |
| 2439 * 3) Extension instructions | |
| 2440 * 4) XSLT instructions of future XSLT versions | |
| 2441 * (forwards-compatible mode). | |
| 2442 */ | |
| 2443 if (info == NULL) { | |
| 2444 /* | |
| 2445 * Handle the rare cases where we don't expect a compiled | |
| 2446 * representation on an XSLT element. | |
| 2447 */ | |
| 2448 if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) { | |
| 2449 xsltMessage(ctxt, contextNode, cur); | |
| 2450 goto skip_children; | |
| 2451 } | |
| 2452 /* | |
| 2453 * Something really went wrong: | |
| 2454 */ | |
| 2455 xsltTransformError(ctxt, NULL, cur, | |
| 2456 "Internal error in xsltApplySequenceConstructor(): " | |
| 2457 "The element '%s' in the stylesheet has no compiled " | |
| 2458 "representation.\n", | |
| 2459 cur->name); | |
| 2460 goto skip_children; | |
| 2461 } | |
| 2462 | |
| 2463 if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) { | |
| 2464 xsltStyleItemLRElementInfoPtr lrInfo = | |
| 2465 (xsltStyleItemLRElementInfoPtr) info; | |
| 2466 /* | |
| 2467 * Literal result elements | |
| 2468 * -------------------------------------------------------- | |
| 2469 */ | |
| 2470 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2471 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, | |
| 2472 xsltGenericDebug(xsltGenericDebugContext, | |
| 2473 "xsltApplySequenceConstructor: copy literal result " | |
| 2474 "element '%s'\n", cur->name)); | |
| 2475 #endif | |
| 2476 /* | |
| 2477 * Copy the raw element-node. | |
| 2478 * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert)) | |
| 2479 * == NULL) | |
| 2480 * goto error; | |
| 2481 */ | |
| 2482 copy = xmlDocCopyNode(cur, insert->doc, 0); | |
| 2483 if (copy == NULL) { | |
| 2484 xsltTransformError(ctxt, NULL, cur, | |
| 2485 "Internal error in xsltApplySequenceConstructor(): " | |
| 2486 "Failed to copy literal result element '%s'.\n", | |
| 2487 cur->name); | |
| 2488 goto error; | |
| 2489 } else { | |
| 2490 /* | |
| 2491 * Add the element-node to the result tree. | |
| 2492 */ | |
| 2493 copy->doc = ctxt->output; | |
| 2494 copy = xsltAddChild(insert, copy); | |
| 2495 /* | |
| 2496 * Create effective namespaces declarations. | |
| 2497 * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef); | |
| 2498 */ | |
| 2499 if (lrInfo->effectiveNs != NULL) { | |
| 2500 xsltEffectiveNsPtr effNs = lrInfo->effectiveNs; | |
| 2501 xmlNsPtr ns, lastns = NULL; | |
| 2502 | |
| 2503 while (effNs != NULL) { | |
| 2504 /* | |
| 2505 * Avoid generating redundant namespace | |
| 2506 * declarations; thus lookup if there is already | |
| 2507 * such a ns-decl in the result. | |
| 2508 */ | |
| 2509 ns = xmlSearchNs(copy->doc, copy, effNs->prefix); | |
| 2510 if ((ns != NULL) && | |
| 2511 (xmlStrEqual(ns->href, effNs->nsName))) | |
| 2512 { | |
| 2513 effNs = effNs->next; | |
| 2514 continue; | |
| 2515 } | |
| 2516 ns = xmlNewNs(copy, effNs->nsName, effNs->prefix); | |
| 2517 if (ns == NULL) { | |
| 2518 xsltTransformError(ctxt, NULL, cur, | |
| 2519 "Internal error in " | |
| 2520 "xsltApplySequenceConstructor(): " | |
| 2521 "Failed to copy a namespace " | |
| 2522 "declaration.\n"); | |
| 2523 goto error; | |
| 2524 } | |
| 2525 | |
| 2526 if (lastns == NULL) | |
| 2527 copy->nsDef = ns; | |
| 2528 else | |
| 2529 lastns->next =ns; | |
| 2530 lastns = ns; | |
| 2531 | |
| 2532 effNs = effNs->next; | |
| 2533 } | |
| 2534 | |
| 2535 } | |
| 2536 /* | |
| 2537 * NOTE that we don't need to apply ns-alising: this was | |
| 2538 * already done at compile-time. | |
| 2539 */ | |
| 2540 if (cur->ns != NULL) { | |
| 2541 /* | |
| 2542 * If there's no such ns-decl in the result tree, | |
| 2543 * then xsltGetSpecialNamespace() will | |
| 2544 * create a ns-decl on the copied node. | |
| 2545 */ | |
| 2546 copy->ns = xsltGetSpecialNamespace(ctxt, cur, | |
| 2547 cur->ns->href, cur->ns->prefix, copy); | |
| 2548 } else { | |
| 2549 /* | |
| 2550 * Undeclare the default namespace if needed. | |
| 2551 * This can be skipped, if the result element has | |
| 2552 * no ns-decls, in which case the result element | |
| 2553 * obviously does not declare a default namespace; | |
| 2554 * AND there's either no parent, or the parent | |
| 2555 * element is in no namespace; this means there's no | |
| 2556 * default namespace is scope to care about. | |
| 2557 * | |
| 2558 * REVISIT: This might result in massive | |
| 2559 * generation of ns-decls if nodes in a default | |
| 2560 * namespaces are mixed with nodes in no namespace. | |
| 2561 * | |
| 2562 */ | |
| 2563 if (copy->nsDef || | |
| 2564 ((insert != NULL) && | |
| 2565 (insert->type == XML_ELEMENT_NODE) && | |
| 2566 (insert->ns != NULL))) | |
| 2567 { | |
| 2568 xsltGetSpecialNamespace(ctxt, cur, | |
| 2569 NULL, NULL, copy); | |
| 2570 } | |
| 2571 } | |
| 2572 } | |
| 2573 /* | |
| 2574 * SPEC XSLT 2.0 "Each attribute of the literal result | |
| 2575 * element, other than an attribute in the XSLT namespace, | |
| 2576 * is processed to produce an attribute for the element in | |
| 2577 * the result tree." | |
| 2578 * NOTE: See bug #341325. | |
| 2579 */ | |
| 2580 if (cur->properties != NULL) { | |
| 2581 xsltAttrListTemplateProcess(ctxt, copy, cur->properties); | |
| 2582 } | |
| 2583 } else if (IS_XSLT_ELEM_FAST(cur)) { | |
| 2584 /* | |
| 2585 * XSLT instructions | |
| 2586 * -------------------------------------------------------- | |
| 2587 */ | |
| 2588 if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) { | |
| 2589 /* | |
| 2590 * We hit an unknown XSLT element. | |
| 2591 * Try to apply one of the fallback cases. | |
| 2592 */ | |
| 2593 ctxt->insert = insert; | |
| 2594 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { | |
| 2595 xsltTransformError(ctxt, NULL, cur, | |
| 2596 "The is no fallback behaviour defined for " | |
| 2597 "the unknown XSLT element '%s'.\n", | |
| 2598 cur->name); | |
| 2599 } | |
| 2600 ctxt->insert = oldInsert; | |
| 2601 } else if (info->func != NULL) { | |
| 2602 /* | |
| 2603 * Execute the XSLT instruction. | |
| 2604 */ | |
| 2605 ctxt->insert = insert; | |
| 2606 | |
| 2607 info->func(ctxt, contextNode, cur, | |
| 2608 (xsltElemPreCompPtr) info); | |
| 2609 | |
| 2610 /* | |
| 2611 * Cleanup temporary tree fragments. | |
| 2612 */ | |
| 2613 if (oldLocalFragmentTop != ctxt->localRVT) | |
| 2614 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); | |
| 2615 | |
| 2616 ctxt->insert = oldInsert; | |
| 2617 } else if (info->type == XSLT_FUNC_VARIABLE) { | |
| 2618 xsltStackElemPtr tmpvar = ctxt->vars; | |
| 2619 | |
| 2620 xsltParseStylesheetVariable(ctxt, cur); | |
| 2621 | |
| 2622 if (tmpvar != ctxt->vars) { | |
| 2623 /* | |
| 2624 * TODO: Using a @tmpvar is an annoying workaround, but | |
| 2625 * the current mechanisms do not provide any other way | |
| 2626 * of knowing if the var was really pushed onto the | |
| 2627 * stack. | |
| 2628 */ | |
| 2629 ctxt->vars->level = level; | |
| 2630 } | |
| 2631 } else if (info->type == XSLT_FUNC_MESSAGE) { | |
| 2632 /* | |
| 2633 * TODO: Won't be hit, since we don't compile xsl:message. | |
| 2634 */ | |
| 2635 xsltMessage(ctxt, contextNode, cur); | |
| 2636 } else { | |
| 2637 xsltTransformError(ctxt, NULL, cur, | |
| 2638 "Unexpected XSLT element '%s'.\n", cur->name); | |
| 2639 } | |
| 2640 goto skip_children; | |
| 2641 | |
| 2642 } else { | |
| 2643 xsltTransformFunction func; | |
| 2644 /* | |
| 2645 * Extension intructions (elements) | |
| 2646 * -------------------------------------------------------- | |
| 2647 */ | |
| 2648 if (cur->psvi == xsltExtMarker) { | |
| 2649 /* | |
| 2650 * The xsltExtMarker was set during the compilation | |
| 2651 * of extension instructions if there was no registered | |
| 2652 * handler for this specific extension function at | |
| 2653 * compile-time. | |
| 2654 * Libxslt will now lookup if a handler is | |
| 2655 * registered in the context of this transformation. | |
| 2656 */ | |
| 2657 func = (xsltTransformFunction) | |
| 2658 xsltExtElementLookup(ctxt, cur->name, cur->ns->href); | |
| 2659 } else | |
| 2660 func = ((xsltElemPreCompPtr) cur->psvi)->func; | |
| 2661 | |
| 2662 if (func == NULL) { | |
| 2663 /* | |
| 2664 * No handler available. | |
| 2665 * Try to execute fallback behaviour via xsl:fallback. | |
| 2666 */ | |
| 2667 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2668 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, | |
| 2669 xsltGenericDebug(xsltGenericDebugContext, | |
| 2670 "xsltApplySequenceConstructor: unknown extension %s\
n", | |
| 2671 cur->name)); | |
| 2672 #endif | |
| 2673 ctxt->insert = insert; | |
| 2674 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { | |
| 2675 xsltTransformError(ctxt, NULL, cur, | |
| 2676 "Unknown extension instruction '{%s}%s'.\n", | |
| 2677 cur->ns->href, cur->name); | |
| 2678 } | |
| 2679 ctxt->insert = oldInsert; | |
| 2680 } else { | |
| 2681 /* | |
| 2682 * Execute the handler-callback. | |
| 2683 */ | |
| 2684 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2685 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(x
sltGenericDebugContext, | |
| 2686 "xsltApplySequenceConstructor: extension construct %s\n"
, | |
| 2687 cur->name)); | |
| 2688 #endif | |
| 2689 /* | |
| 2690 * Disable the xsltCopyTextString optimization for | |
| 2691 * extension elements. Extensions could append text using | |
| 2692 * xmlAddChild which will free the buffer pointed to by | |
| 2693 * 'lasttext'. This buffer could later be reallocated with | |
| 2694 * a different size than recorded in 'lasttsize'. See bug | |
| 2695 * #777432. | |
| 2696 */ | |
| 2697 if (cur->psvi == xsltExtMarker) { | |
| 2698 ctxt->lasttext = NULL; | |
| 2699 } | |
| 2700 | |
| 2701 ctxt->insert = insert; | |
| 2702 | |
| 2703 func(ctxt, contextNode, cur, cur->psvi); | |
| 2704 | |
| 2705 /* | |
| 2706 * Cleanup temporary tree fragments. | |
| 2707 */ | |
| 2708 if (oldLocalFragmentTop != ctxt->localRVT) | |
| 2709 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); | |
| 2710 | |
| 2711 ctxt->insert = oldInsert; | |
| 2712 } | |
| 2713 goto skip_children; | |
| 2714 } | |
| 2715 | |
| 2716 } else if (XSLT_IS_TEXT_NODE(cur)) { | |
| 2717 /* | |
| 2718 * Text | |
| 2719 * ------------------------------------------------------------ | |
| 2720 */ | |
| 2721 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2722 if (cur->name == xmlStringTextNoenc) { | |
| 2723 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, | |
| 2724 xsltGenericDebug(xsltGenericDebugContext, | |
| 2725 "xsltApplySequenceConstructor: copy unescaped text '%s'\n", | |
| 2726 cur->content)); | |
| 2727 } else { | |
| 2728 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, | |
| 2729 xsltGenericDebug(xsltGenericDebugContext, | |
| 2730 "xsltApplySequenceConstructor: copy text '%s'\n", | |
| 2731 cur->content)); | |
| 2732 } | |
| 2733 #endif | |
| 2734 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) | |
| 2735 goto error; | |
| 2736 } | |
| 2737 | |
| 2738 #else /* XSLT_REFACTORED */ | |
| 2739 | |
| 2740 if (IS_XSLT_ELEM(cur)) { | |
| 2741 /* | |
| 2742 * This is an XSLT node | |
| 2743 */ | |
| 2744 xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi; | |
| 2745 | |
| 2746 if (info == NULL) { | |
| 2747 if (IS_XSLT_NAME(cur, "message")) { | |
| 2748 xsltMessage(ctxt, contextNode, cur); | |
| 2749 } else { | |
| 2750 /* | |
| 2751 * That's an error try to apply one of the fallback cases | |
| 2752 */ | |
| 2753 ctxt->insert = insert; | |
| 2754 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { | |
| 2755 xsltGenericError(xsltGenericErrorContext, | |
| 2756 "xsltApplySequenceConstructor: %s was not compiled\n
", | |
| 2757 cur->name); | |
| 2758 } | |
| 2759 ctxt->insert = oldInsert; | |
| 2760 } | |
| 2761 goto skip_children; | |
| 2762 } | |
| 2763 | |
| 2764 if (info->func != NULL) { | |
| 2765 oldCurInst = ctxt->inst; | |
| 2766 ctxt->inst = cur; | |
| 2767 ctxt->insert = insert; | |
| 2768 | |
| 2769 info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info); | |
| 2770 | |
| 2771 /* | |
| 2772 * Cleanup temporary tree fragments. | |
| 2773 */ | |
| 2774 if (oldLocalFragmentTop != ctxt->localRVT) | |
| 2775 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); | |
| 2776 | |
| 2777 ctxt->insert = oldInsert; | |
| 2778 ctxt->inst = oldCurInst; | |
| 2779 goto skip_children; | |
| 2780 } | |
| 2781 | |
| 2782 if (IS_XSLT_NAME(cur, "variable")) { | |
| 2783 xsltStackElemPtr tmpvar = ctxt->vars; | |
| 2784 | |
| 2785 oldCurInst = ctxt->inst; | |
| 2786 ctxt->inst = cur; | |
| 2787 | |
| 2788 xsltParseStylesheetVariable(ctxt, cur); | |
| 2789 | |
| 2790 ctxt->inst = oldCurInst; | |
| 2791 | |
| 2792 if (tmpvar != ctxt->vars) { | |
| 2793 /* | |
| 2794 * TODO: Using a @tmpvar is an annoying workaround, but | |
| 2795 * the current mechanisms do not provide any other way | |
| 2796 * of knowing if the var was really pushed onto the | |
| 2797 * stack. | |
| 2798 */ | |
| 2799 ctxt->vars->level = level; | |
| 2800 } | |
| 2801 } else if (IS_XSLT_NAME(cur, "message")) { | |
| 2802 xsltMessage(ctxt, contextNode, cur); | |
| 2803 } else { | |
| 2804 xsltTransformError(ctxt, NULL, cur, | |
| 2805 "Unexpected XSLT element '%s'.\n", cur->name); | |
| 2806 } | |
| 2807 goto skip_children; | |
| 2808 } else if ((cur->type == XML_TEXT_NODE) || | |
| 2809 (cur->type == XML_CDATA_SECTION_NODE)) { | |
| 2810 | |
| 2811 /* | |
| 2812 * This text comes from the stylesheet | |
| 2813 * For stylesheets, the set of whitespace-preserving | |
| 2814 * element names consists of just xsl:text. | |
| 2815 */ | |
| 2816 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2817 if (cur->type == XML_CDATA_SECTION_NODE) { | |
| 2818 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltG
enericDebugContext, | |
| 2819 "xsltApplySequenceConstructor: copy CDATA text
%s\n", | |
| 2820 cur->content)); | |
| 2821 } else if (cur->name == xmlStringTextNoenc) { | |
| 2822 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltG
enericDebugContext, | |
| 2823 "xsltApplySequenceConstructor: copy unescaped t
ext %s\n", | |
| 2824 cur->content)); | |
| 2825 } else { | |
| 2826 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltG
enericDebugContext, | |
| 2827 "xsltApplySequenceConstructor: copy text %s\n", | |
| 2828 cur->content)); | |
| 2829 } | |
| 2830 #endif | |
| 2831 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) | |
| 2832 goto error; | |
| 2833 } else if ((cur->type == XML_ELEMENT_NODE) && | |
| 2834 (cur->ns != NULL) && (cur->psvi != NULL)) { | |
| 2835 xsltTransformFunction function; | |
| 2836 | |
| 2837 oldCurInst = ctxt->inst; | |
| 2838 ctxt->inst = cur; | |
| 2839 /* | |
| 2840 * Flagged as an extension element | |
| 2841 */ | |
| 2842 if (cur->psvi == xsltExtMarker) | |
| 2843 function = (xsltTransformFunction) | |
| 2844 xsltExtElementLookup(ctxt, cur->name, cur->ns->href); | |
| 2845 else | |
| 2846 function = ((xsltElemPreCompPtr) cur->psvi)->func; | |
| 2847 | |
| 2848 if (function == NULL) { | |
| 2849 xmlNodePtr child; | |
| 2850 int found = 0; | |
| 2851 | |
| 2852 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2853 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltG
enericDebugContext, | |
| 2854 "xsltApplySequenceConstructor: unknown extension %s\n", | |
| 2855 cur->name)); | |
| 2856 #endif | |
| 2857 /* | |
| 2858 * Search if there are fallbacks | |
| 2859 */ | |
| 2860 child = cur->children; | |
| 2861 while (child != NULL) { | |
| 2862 if ((IS_XSLT_ELEM(child)) && | |
| 2863 (IS_XSLT_NAME(child, "fallback"))) | |
| 2864 { | |
| 2865 found = 1; | |
| 2866 xsltApplySequenceConstructor(ctxt, contextNode, | |
| 2867 child->children, NULL); | |
| 2868 } | |
| 2869 child = child->next; | |
| 2870 } | |
| 2871 | |
| 2872 if (!found) { | |
| 2873 xsltTransformError(ctxt, NULL, cur, | |
| 2874 "xsltApplySequenceConstructor: failed to find extension
%s\n", | |
| 2875 cur->name); | |
| 2876 } | |
| 2877 } else { | |
| 2878 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2879 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltG
enericDebugContext, | |
| 2880 "xsltApplySequenceConstructor: extension construct %s\n", | |
| 2881 cur->name)); | |
| 2882 #endif | |
| 2883 | |
| 2884 /* | |
| 2885 * Disable the xsltCopyTextString optimization for | |
| 2886 * extension elements. Extensions could append text using | |
| 2887 * xmlAddChild which will free the buffer pointed to by | |
| 2888 * 'lasttext'. This buffer could later be reallocated with | |
| 2889 * a different size than recorded in 'lasttsize'. See bug | |
| 2890 * #777432. | |
| 2891 */ | |
| 2892 if (cur->psvi == xsltExtMarker) { | |
| 2893 ctxt->lasttext = NULL; | |
| 2894 } | |
| 2895 | |
| 2896 ctxt->insert = insert; | |
| 2897 | |
| 2898 function(ctxt, contextNode, cur, cur->psvi); | |
| 2899 /* | |
| 2900 * Cleanup temporary tree fragments. | |
| 2901 */ | |
| 2902 if (oldLocalFragmentTop != ctxt->localRVT) | |
| 2903 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); | |
| 2904 | |
| 2905 ctxt->insert = oldInsert; | |
| 2906 | |
| 2907 } | |
| 2908 ctxt->inst = oldCurInst; | |
| 2909 goto skip_children; | |
| 2910 } else if (cur->type == XML_ELEMENT_NODE) { | |
| 2911 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 2912 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGener
icDebugContext, | |
| 2913 "xsltApplySequenceConstructor: copy node %s\n", | |
| 2914 cur->name)); | |
| 2915 #endif | |
| 2916 oldCurInst = ctxt->inst; | |
| 2917 ctxt->inst = cur; | |
| 2918 | |
| 2919 if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL) | |
| 2920 goto error; | |
| 2921 /* | |
| 2922 * Add extra namespaces inherited from the current template | |
| 2923 * if we are in the first level children and this is a | |
| 2924 * "real" template. | |
| 2925 */ | |
| 2926 if ((templ != NULL) && (oldInsert == insert) && | |
| 2927 (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) { | |
| 2928 int i; | |
| 2929 xmlNsPtr ns, ret; | |
| 2930 | |
| 2931 for (i = 0; i < ctxt->templ->inheritedNsNr; i++) { | |
| 2932 const xmlChar *URI = NULL; | |
| 2933 xsltStylesheetPtr style; | |
| 2934 ns = ctxt->templ->inheritedNs[i]; | |
| 2935 | |
| 2936 /* Note that the XSLT namespace was already excluded | |
| 2937 * in xsltGetInheritedNsList(). | |
| 2938 */ | |
| 2939 #if 0 | |
| 2940 if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) | |
| 2941 continue; | |
| 2942 #endif | |
| 2943 style = ctxt->style; | |
| 2944 while (style != NULL) { | |
| 2945 if (style->nsAliases != NULL) | |
| 2946 URI = (const xmlChar *) | |
| 2947 xmlHashLookup(style->nsAliases, ns->href); | |
| 2948 if (URI != NULL) | |
| 2949 break; | |
| 2950 | |
| 2951 style = xsltNextImport(style); | |
| 2952 } | |
| 2953 if (URI == UNDEFINED_DEFAULT_NS) | |
| 2954 continue; | |
| 2955 if (URI == NULL) | |
| 2956 URI = ns->href; | |
| 2957 /* | |
| 2958 * TODO: The following will still be buggy for the | |
| 2959 * non-refactored code. | |
| 2960 */ | |
| 2961 ret = xmlSearchNs(copy->doc, copy, ns->prefix); | |
| 2962 if ((ret == NULL) || (!xmlStrEqual(ret->href, URI))) | |
| 2963 { | |
| 2964 xmlNewNs(copy, URI, ns->prefix); | |
| 2965 } | |
| 2966 } | |
| 2967 if (copy->ns != NULL) { | |
| 2968 /* | |
| 2969 * Fix the node namespace if needed | |
| 2970 */ | |
| 2971 copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy); | |
| 2972 } | |
| 2973 } | |
| 2974 /* | |
| 2975 * all the attributes are directly inherited | |
| 2976 */ | |
| 2977 if (cur->properties != NULL) { | |
| 2978 xsltAttrListTemplateProcess(ctxt, copy, cur->properties); | |
| 2979 } | |
| 2980 ctxt->inst = oldCurInst; | |
| 2981 } | |
| 2982 #endif /* else of XSLT_REFACTORED */ | |
| 2983 | |
| 2984 /* | |
| 2985 * Descend into content in document order. | |
| 2986 */ | |
| 2987 if (cur->children != NULL) { | |
| 2988 if (cur->children->type != XML_ENTITY_DECL) { | |
| 2989 cur = cur->children; | |
| 2990 level++; | |
| 2991 if (copy != NULL) | |
| 2992 insert = copy; | |
| 2993 continue; | |
| 2994 } | |
| 2995 } | |
| 2996 | |
| 2997 skip_children: | |
| 2998 /* | |
| 2999 * If xslt:message was just processed, we might have hit a | |
| 3000 * terminate='yes'; if so, then break the loop and clean up. | |
| 3001 * TODO: Do we need to check this also before trying to descend | |
| 3002 * into the content? | |
| 3003 */ | |
| 3004 if (ctxt->state == XSLT_STATE_STOPPED) | |
| 3005 break; | |
| 3006 if (cur->next != NULL) { | |
| 3007 cur = cur->next; | |
| 3008 continue; | |
| 3009 } | |
| 3010 | |
| 3011 do { | |
| 3012 cur = cur->parent; | |
| 3013 level--; | |
| 3014 /* | |
| 3015 * Pop variables/params (xsl:variable and xsl:param). | |
| 3016 */ | |
| 3017 if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) { | |
| 3018 xsltLocalVariablePop(ctxt, oldVarsNr, level); | |
| 3019 } | |
| 3020 | |
| 3021 insert = insert->parent; | |
| 3022 if (cur == NULL) | |
| 3023 break; | |
| 3024 if (cur == list->parent) { | |
| 3025 cur = NULL; | |
| 3026 break; | |
| 3027 } | |
| 3028 if (cur->next != NULL) { | |
| 3029 cur = cur->next; | |
| 3030 break; | |
| 3031 } | |
| 3032 } while (cur != NULL); | |
| 3033 } | |
| 3034 | |
| 3035 error: | |
| 3036 /* | |
| 3037 * In case of errors: pop remaining variables. | |
| 3038 */ | |
| 3039 if (ctxt->varsNr > oldVarsNr) | |
| 3040 xsltLocalVariablePop(ctxt, oldVarsNr, -1); | |
| 3041 | |
| 3042 ctxt->node = oldContextNode; | |
| 3043 ctxt->inst = oldInst; | |
| 3044 ctxt->insert = oldInsert; | |
| 3045 | |
| 3046 ctxt->depth--; | |
| 3047 | |
| 3048 #ifdef WITH_DEBUGGER | |
| 3049 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) { | |
| 3050 xslDropCall(); | |
| 3051 } | |
| 3052 #endif | |
| 3053 } | |
| 3054 | |
| 3055 /* | |
| 3056 * xsltApplyXSLTTemplate: | |
| 3057 * @ctxt: a XSLT transformation context | |
| 3058 * @contextNode: the node in the source tree. | |
| 3059 * @list: the nodes of a sequence constructor; | |
| 3060 * (plus leading xsl:param elements) | |
| 3061 * @templ: the compiled xsl:template declaration; | |
| 3062 * NULL if a sequence constructor | |
| 3063 * @withParams: a set of caller-parameters (xsl:with-param) or NULL | |
| 3064 * | |
| 3065 * Called by: | |
| 3066 * - xsltApplyImports() | |
| 3067 * - xsltCallTemplate() | |
| 3068 * - xsltDefaultProcessOneNode() | |
| 3069 * - xsltProcessOneNode() | |
| 3070 */ | |
| 3071 static void | |
| 3072 xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt, | |
| 3073 xmlNodePtr contextNode, | |
| 3074 xmlNodePtr list, | |
| 3075 xsltTemplatePtr templ, | |
| 3076 xsltStackElemPtr withParams) | |
| 3077 { | |
| 3078 int oldVarsBase = 0; | |
| 3079 long start = 0; | |
| 3080 xmlNodePtr cur; | |
| 3081 xsltStackElemPtr tmpParam = NULL; | |
| 3082 xmlDocPtr oldUserFragmentTop; | |
| 3083 | |
| 3084 #ifdef XSLT_REFACTORED | |
| 3085 xsltStyleItemParamPtr iparam; | |
| 3086 #else | |
| 3087 xsltStylePreCompPtr iparam; | |
| 3088 #endif | |
| 3089 | |
| 3090 #ifdef WITH_DEBUGGER | |
| 3091 int addCallResult = 0; | |
| 3092 #endif | |
| 3093 | |
| 3094 if (ctxt == NULL) | |
| 3095 return; | |
| 3096 if (templ == NULL) { | |
| 3097 xsltTransformError(ctxt, NULL, list, | |
| 3098 "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n"); | |
| 3099 return; | |
| 3100 } | |
| 3101 | |
| 3102 #ifdef WITH_DEBUGGER | |
| 3103 if (ctxt->debugStatus != XSLT_DEBUG_NONE) { | |
| 3104 if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode, | |
| 3105 list, templ, &addCallResult) == NULL) | |
| 3106 return; | |
| 3107 } | |
| 3108 #endif | |
| 3109 | |
| 3110 if (list == NULL) | |
| 3111 return; | |
| 3112 CHECK_STOPPED; | |
| 3113 | |
| 3114 if (ctxt->varsNr >= ctxt->maxTemplateVars) | |
| 3115 { | |
| 3116 xsltTransformError(ctxt, NULL, list, | |
| 3117 "xsltApplyXSLTTemplate: A potential infinite template recursion " | |
| 3118 "was detected.\n" | |
| 3119 "You can adjust maxTemplateVars (--maxvars) in order to " | |
| 3120 "raise the maximum number of variables/params (currently set to %d).
\n", | |
| 3121 ctxt->maxTemplateVars); | |
| 3122 xsltDebug(ctxt, contextNode, list, NULL); | |
| 3123 ctxt->state = XSLT_STATE_STOPPED; | |
| 3124 return; | |
| 3125 } | |
| 3126 | |
| 3127 oldUserFragmentTop = ctxt->tmpRVT; | |
| 3128 ctxt->tmpRVT = NULL; | |
| 3129 | |
| 3130 /* | |
| 3131 * Initiate a distinct scope of local params/variables. | |
| 3132 */ | |
| 3133 oldVarsBase = ctxt->varsBase; | |
| 3134 ctxt->varsBase = ctxt->varsNr; | |
| 3135 | |
| 3136 ctxt->node = contextNode; | |
| 3137 if (ctxt->profile) { | |
| 3138 templ->nbCalls++; | |
| 3139 start = xsltTimestamp(); | |
| 3140 profPush(ctxt, 0); | |
| 3141 profCallgraphAdd(templ, ctxt->templ); | |
| 3142 } | |
| 3143 /* | |
| 3144 * Push the xsl:template declaration onto the stack. | |
| 3145 */ | |
| 3146 templPush(ctxt, templ); | |
| 3147 | |
| 3148 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 3149 if (templ->name != NULL) | |
| 3150 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDe
bugContext, | |
| 3151 "applying xsl:template '%s'\n", templ->name)); | |
| 3152 #endif | |
| 3153 /* | |
| 3154 * Process xsl:param instructions and skip those elements for | |
| 3155 * further processing. | |
| 3156 */ | |
| 3157 cur = list; | |
| 3158 do { | |
| 3159 if (cur->type == XML_TEXT_NODE) { | |
| 3160 cur = cur->next; | |
| 3161 continue; | |
| 3162 } | |
| 3163 if ((cur->type != XML_ELEMENT_NODE) || | |
| 3164 (cur->name[0] != 'p') || | |
| 3165 (cur->psvi == NULL) || | |
| 3166 (! xmlStrEqual(cur->name, BAD_CAST "param")) || | |
| 3167 (! IS_XSLT_ELEM(cur))) | |
| 3168 { | |
| 3169 break; | |
| 3170 } | |
| 3171 | |
| 3172 list = cur->next; | |
| 3173 | |
| 3174 #ifdef XSLT_REFACTORED | |
| 3175 iparam = (xsltStyleItemParamPtr) cur->psvi; | |
| 3176 #else | |
| 3177 iparam = (xsltStylePreCompPtr) cur->psvi; | |
| 3178 #endif | |
| 3179 | |
| 3180 /* | |
| 3181 * Substitute xsl:param for a given xsl:with-param. | |
| 3182 * Since the XPath expression will reference the params/vars | |
| 3183 * by index, we need to slot the xsl:with-params in the | |
| 3184 * order of encountered xsl:params to keep the sequence of | |
| 3185 * params/variables in the stack exactly as it was at | |
| 3186 * compile time, | |
| 3187 */ | |
| 3188 tmpParam = NULL; | |
| 3189 if (withParams) { | |
| 3190 tmpParam = withParams; | |
| 3191 do { | |
| 3192 if ((tmpParam->name == (iparam->name)) && | |
| 3193 (tmpParam->nameURI == (iparam->ns))) | |
| 3194 { | |
| 3195 /* | |
| 3196 * Push the caller-parameter. | |
| 3197 */ | |
| 3198 xsltLocalVariablePush(ctxt, tmpParam, -1); | |
| 3199 break; | |
| 3200 } | |
| 3201 tmpParam = tmpParam->next; | |
| 3202 } while (tmpParam != NULL); | |
| 3203 } | |
| 3204 /* | |
| 3205 * Push the xsl:param. | |
| 3206 */ | |
| 3207 if (tmpParam == NULL) { | |
| 3208 /* | |
| 3209 * Note that we must assume that the added parameter | |
| 3210 * has a @depth of 0. | |
| 3211 */ | |
| 3212 xsltParseStylesheetParam(ctxt, cur); | |
| 3213 } | |
| 3214 cur = cur->next; | |
| 3215 } while (cur != NULL); | |
| 3216 /* | |
| 3217 * Process the sequence constructor. | |
| 3218 */ | |
| 3219 xsltApplySequenceConstructor(ctxt, contextNode, list, templ); | |
| 3220 | |
| 3221 /* | |
| 3222 * Remove remaining xsl:param and xsl:with-param items from | |
| 3223 * the stack. Don't free xsl:with-param items. | |
| 3224 */ | |
| 3225 if (ctxt->varsNr > ctxt->varsBase) | |
| 3226 xsltTemplateParamsCleanup(ctxt); | |
| 3227 ctxt->varsBase = oldVarsBase; | |
| 3228 | |
| 3229 /* | |
| 3230 * Release user-created fragments stored in the scope | |
| 3231 * of xsl:template. Note that this mechanism is deprecated: | |
| 3232 * user code should now use xsltRegisterLocalRVT() instead | |
| 3233 * of the obsolete xsltRegisterTmpRVT(). | |
| 3234 */ | |
| 3235 if (ctxt->tmpRVT) { | |
| 3236 xmlDocPtr curdoc = ctxt->tmpRVT, tmp; | |
| 3237 | |
| 3238 while (curdoc != NULL) { | |
| 3239 tmp = curdoc; | |
| 3240 curdoc = (xmlDocPtr) curdoc->next; | |
| 3241 xsltReleaseRVT(ctxt, tmp); | |
| 3242 } | |
| 3243 } | |
| 3244 ctxt->tmpRVT = oldUserFragmentTop; | |
| 3245 | |
| 3246 /* | |
| 3247 * Pop the xsl:template declaration from the stack. | |
| 3248 */ | |
| 3249 templPop(ctxt); | |
| 3250 if (ctxt->profile) { | |
| 3251 long spent, child, total, end; | |
| 3252 | |
| 3253 end = xsltTimestamp(); | |
| 3254 child = profPop(ctxt); | |
| 3255 total = end - start; | |
| 3256 spent = total - child; | |
| 3257 if (spent <= 0) { | |
| 3258 /* | |
| 3259 * Not possible unless the original calibration failed | |
| 3260 * we can try to correct it on the fly. | |
| 3261 */ | |
| 3262 xsltCalibrateAdjust(spent); | |
| 3263 spent = 0; | |
| 3264 } | |
| 3265 | |
| 3266 templ->time += spent; | |
| 3267 if (ctxt->profNr > 0) | |
| 3268 ctxt->profTab[ctxt->profNr - 1] += total; | |
| 3269 } | |
| 3270 | |
| 3271 #ifdef WITH_DEBUGGER | |
| 3272 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) { | |
| 3273 xslDropCall(); | |
| 3274 } | |
| 3275 #endif | |
| 3276 } | |
| 3277 | |
| 3278 | |
| 3279 /** | |
| 3280 * xsltApplyOneTemplate: | |
| 3281 * @ctxt: a XSLT process context | |
| 3282 * @contextNode: the node in the source tree. | |
| 3283 * @list: the nodes of a sequence constructor | |
| 3284 * @templ: not used | |
| 3285 * @params: a set of parameters (xsl:param) or NULL | |
| 3286 * | |
| 3287 * Processes a sequence constructor on the current node in the source tree. | |
| 3288 * | |
| 3289 * @params are the already computed variable stack items; this function | |
| 3290 * pushes them on the variable stack, and pops them before exiting; it's | |
| 3291 * left to the caller to free or reuse @params afterwards. The initial | |
| 3292 * states of the variable stack will always be restored before this | |
| 3293 * function exits. | |
| 3294 * NOTE that this does *not* initiate a new distinct variable scope; i.e. | |
| 3295 * variables already on the stack are visible to the process. The caller's | |
| 3296 * side needs to start a new variable scope if needed (e.g. in exsl:function). | |
| 3297 * | |
| 3298 * @templ is obsolete and not used anymore (e.g. <exslt:function> does not | |
| 3299 * provide a @templ); a non-NULL @templ might raise an error in the future. | |
| 3300 * | |
| 3301 * BIG NOTE: This function is not intended to process the content of an | |
| 3302 * xsl:template; it does not expect xsl:param instructions in @list and | |
| 3303 * will report errors if found. | |
| 3304 * | |
| 3305 * Called by: | |
| 3306 * - xsltEvalVariable() (variables.c) | |
| 3307 * - exsltFuncFunctionFunction() (libexsl/functions.c) | |
| 3308 */ | |
| 3309 void | |
| 3310 xsltApplyOneTemplate(xsltTransformContextPtr ctxt, | |
| 3311 xmlNodePtr contextNode, | |
| 3312 xmlNodePtr list, | |
| 3313 xsltTemplatePtr templ ATTRIBUTE_UNUSED, | |
| 3314 xsltStackElemPtr params) | |
| 3315 { | |
| 3316 if ((ctxt == NULL) || (list == NULL)) | |
| 3317 return; | |
| 3318 CHECK_STOPPED; | |
| 3319 | |
| 3320 if (params) { | |
| 3321 /* | |
| 3322 * This code should be obsolete - was previously used | |
| 3323 * by libexslt/functions.c, but due to bug 381319 the | |
| 3324 * logic there was changed. | |
| 3325 */ | |
| 3326 int oldVarsNr = ctxt->varsNr; | |
| 3327 | |
| 3328 /* | |
| 3329 * Push the given xsl:param(s) onto the variable stack. | |
| 3330 */ | |
| 3331 while (params != NULL) { | |
| 3332 xsltLocalVariablePush(ctxt, params, -1); | |
| 3333 params = params->next; | |
| 3334 } | |
| 3335 xsltApplySequenceConstructor(ctxt, contextNode, list, templ); | |
| 3336 /* | |
| 3337 * Pop the given xsl:param(s) from the stack but don't free them. | |
| 3338 */ | |
| 3339 xsltLocalVariablePop(ctxt, oldVarsNr, -2); | |
| 3340 } else | |
| 3341 xsltApplySequenceConstructor(ctxt, contextNode, list, templ); | |
| 3342 } | |
| 3343 | |
| 3344 /************************************************************************ | |
| 3345 * * | |
| 3346 * XSLT-1.1 extensions * | |
| 3347 * * | |
| 3348 ************************************************************************/ | |
| 3349 | |
| 3350 /** | |
| 3351 * xsltDocumentElem: | |
| 3352 * @ctxt: an XSLT processing context | |
| 3353 * @node: The current node | |
| 3354 * @inst: the instruction in the stylesheet | |
| 3355 * @castedComp: precomputed information | |
| 3356 * | |
| 3357 * Process an EXSLT/XSLT-1.1 document element | |
| 3358 */ | |
| 3359 void | |
| 3360 xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 3361 xmlNodePtr inst, xsltStylePreCompPtr castedComp) | |
| 3362 { | |
| 3363 #ifdef XSLT_REFACTORED | |
| 3364 xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp; | |
| 3365 #else | |
| 3366 xsltStylePreCompPtr comp = castedComp; | |
| 3367 #endif | |
| 3368 xsltStylesheetPtr style = NULL; | |
| 3369 int ret; | |
| 3370 xmlChar *filename = NULL, *prop, *elements; | |
| 3371 xmlChar *element, *end; | |
| 3372 xmlDocPtr res = NULL; | |
| 3373 xmlDocPtr oldOutput; | |
| 3374 xmlNodePtr oldInsert, root; | |
| 3375 const char *oldOutputFile; | |
| 3376 xsltOutputType oldType; | |
| 3377 xmlChar *URL = NULL; | |
| 3378 const xmlChar *method; | |
| 3379 const xmlChar *doctypePublic; | |
| 3380 const xmlChar *doctypeSystem; | |
| 3381 const xmlChar *version; | |
| 3382 const xmlChar *encoding; | |
| 3383 int redirect_write_append = 0; | |
| 3384 | |
| 3385 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) | |
| 3386 return; | |
| 3387 | |
| 3388 if (comp->filename == NULL) { | |
| 3389 | |
| 3390 if (xmlStrEqual(inst->name, (const xmlChar *) "output")) { | |
| 3391 /* | |
| 3392 * The element "output" is in the namespace XSLT_SAXON_NAMESPACE | |
| 3393 * (http://icl.com/saxon) | |
| 3394 * The @file is in no namespace. | |
| 3395 */ | |
| 3396 #ifdef WITH_XSLT_DEBUG_EXTRA | |
| 3397 xsltGenericDebug(xsltGenericDebugContext, | |
| 3398 "Found saxon:output extension\n"); | |
| 3399 #endif | |
| 3400 URL = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3401 (const xmlChar *) "file", | |
| 3402 XSLT_SAXON_NAMESPACE); | |
| 3403 | |
| 3404 if (URL == NULL) | |
| 3405 URL = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3406 (const xmlChar *) "href", | |
| 3407 XSLT_SAXON_NAMESPACE); | |
| 3408 } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) { | |
| 3409 #ifdef WITH_XSLT_DEBUG_EXTRA | |
| 3410 xsltGenericDebug(xsltGenericDebugContext, | |
| 3411 "Found xalan:write extension\n"); | |
| 3412 #endif | |
| 3413 URL = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3414 (const xmlChar *) | |
| 3415 "select", | |
| 3416 XSLT_XALAN_NAMESPACE); | |
| 3417 if (URL != NULL) { | |
| 3418 xmlXPathCompExprPtr cmp; | |
| 3419 xmlChar *val; | |
| 3420 | |
| 3421 /* | |
| 3422 * Trying to handle bug #59212 | |
| 3423 * The value of the "select" attribute is an | |
| 3424 * XPath expression. | |
| 3425 * (see http://xml.apache.org/xalan-j/extensionslib.html#redirec
t) | |
| 3426 */ | |
| 3427 cmp = xmlXPathCompile(URL); | |
| 3428 val = xsltEvalXPathString(ctxt, cmp); | |
| 3429 xmlXPathFreeCompExpr(cmp); | |
| 3430 xmlFree(URL); | |
| 3431 URL = val; | |
| 3432 } | |
| 3433 if (URL == NULL) | |
| 3434 URL = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3435 (const xmlChar *) | |
| 3436 "file", | |
| 3437 XSLT_XALAN_NAMESPACE); | |
| 3438 if (URL == NULL) | |
| 3439 URL = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3440 (const xmlChar *) | |
| 3441 "href", | |
| 3442 XSLT_XALAN_NAMESPACE); | |
| 3443 } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) { | |
| 3444 URL = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3445 (const xmlChar *) "href", | |
| 3446 NULL); | |
| 3447 } | |
| 3448 | |
| 3449 } else { | |
| 3450 URL = xmlStrdup(comp->filename); | |
| 3451 } | |
| 3452 | |
| 3453 if (URL == NULL) { | |
| 3454 xsltTransformError(ctxt, NULL, inst, | |
| 3455 "xsltDocumentElem: href/URI-Reference not found\n"); | |
| 3456 return; | |
| 3457 } | |
| 3458 | |
| 3459 /* | |
| 3460 * If the computation failed, it's likely that the URL wasn't escaped | |
| 3461 */ | |
| 3462 filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile); | |
| 3463 if (filename == NULL) { | |
| 3464 xmlChar *escURL; | |
| 3465 | |
| 3466 escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,"); | |
| 3467 if (escURL != NULL) { | |
| 3468 filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile); | |
| 3469 xmlFree(escURL); | |
| 3470 } | |
| 3471 } | |
| 3472 | |
| 3473 if (filename == NULL) { | |
| 3474 xsltTransformError(ctxt, NULL, inst, | |
| 3475 "xsltDocumentElem: URL computation failed for %s\n", | |
| 3476 URL); | |
| 3477 xmlFree(URL); | |
| 3478 return; | |
| 3479 } | |
| 3480 | |
| 3481 /* | |
| 3482 * Security checking: can we write to this resource | |
| 3483 */ | |
| 3484 if (ctxt->sec != NULL) { | |
| 3485 ret = xsltCheckWrite(ctxt->sec, ctxt, filename); | |
| 3486 if (ret == 0) { | |
| 3487 xsltTransformError(ctxt, NULL, inst, | |
| 3488 "xsltDocumentElem: write rights for %s denied\n", | |
| 3489 filename); | |
| 3490 xmlFree(URL); | |
| 3491 xmlFree(filename); | |
| 3492 return; | |
| 3493 } | |
| 3494 } | |
| 3495 | |
| 3496 oldOutputFile = ctxt->outputFile; | |
| 3497 oldOutput = ctxt->output; | |
| 3498 oldInsert = ctxt->insert; | |
| 3499 oldType = ctxt->type; | |
| 3500 ctxt->outputFile = (const char *) filename; | |
| 3501 | |
| 3502 style = xsltNewStylesheet(); | |
| 3503 if (style == NULL) { | |
| 3504 xsltTransformError(ctxt, NULL, inst, | |
| 3505 "xsltDocumentElem: out of memory\n"); | |
| 3506 goto error; | |
| 3507 } | |
| 3508 | |
| 3509 /* | |
| 3510 * Version described in 1.1 draft allows full parameterization | |
| 3511 * of the output. | |
| 3512 */ | |
| 3513 prop = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3514 (const xmlChar *) "version", | |
| 3515 NULL); | |
| 3516 if (prop != NULL) { | |
| 3517 if (style->version != NULL) | |
| 3518 xmlFree(style->version); | |
| 3519 style->version = prop; | |
| 3520 } | |
| 3521 prop = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3522 (const xmlChar *) "encoding", | |
| 3523 NULL); | |
| 3524 if (prop != NULL) { | |
| 3525 if (style->encoding != NULL) | |
| 3526 xmlFree(style->encoding); | |
| 3527 style->encoding = prop; | |
| 3528 } | |
| 3529 prop = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3530 (const xmlChar *) "method", | |
| 3531 NULL); | |
| 3532 if (prop != NULL) { | |
| 3533 const xmlChar *URI; | |
| 3534 | |
| 3535 if (style->method != NULL) | |
| 3536 xmlFree(style->method); | |
| 3537 style->method = NULL; | |
| 3538 if (style->methodURI != NULL) | |
| 3539 xmlFree(style->methodURI); | |
| 3540 style->methodURI = NULL; | |
| 3541 | |
| 3542 URI = xsltGetQNameURI(inst, &prop); | |
| 3543 if (prop == NULL) { | |
| 3544 if (style != NULL) style->errors++; | |
| 3545 } else if (URI == NULL) { | |
| 3546 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) || | |
| 3547 (xmlStrEqual(prop, (const xmlChar *) "html")) || | |
| 3548 (xmlStrEqual(prop, (const xmlChar *) "text"))) { | |
| 3549 style->method = prop; | |
| 3550 } else { | |
| 3551 xsltTransformError(ctxt, NULL, inst, | |
| 3552 "invalid value for method: %s\n", prop); | |
| 3553 if (style != NULL) style->warnings++; | |
| 3554 } | |
| 3555 } else { | |
| 3556 style->method = prop; | |
| 3557 style->methodURI = xmlStrdup(URI); | |
| 3558 } | |
| 3559 } | |
| 3560 prop = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3561 (const xmlChar *) | |
| 3562 "doctype-system", NULL); | |
| 3563 if (prop != NULL) { | |
| 3564 if (style->doctypeSystem != NULL) | |
| 3565 xmlFree(style->doctypeSystem); | |
| 3566 style->doctypeSystem = prop; | |
| 3567 } | |
| 3568 prop = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3569 (const xmlChar *) | |
| 3570 "doctype-public", NULL); | |
| 3571 if (prop != NULL) { | |
| 3572 if (style->doctypePublic != NULL) | |
| 3573 xmlFree(style->doctypePublic); | |
| 3574 style->doctypePublic = prop; | |
| 3575 } | |
| 3576 prop = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3577 (const xmlChar *) "standalone", | |
| 3578 NULL); | |
| 3579 if (prop != NULL) { | |
| 3580 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { | |
| 3581 style->standalone = 1; | |
| 3582 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { | |
| 3583 style->standalone = 0; | |
| 3584 } else { | |
| 3585 xsltTransformError(ctxt, NULL, inst, | |
| 3586 "invalid value for standalone: %s\n", | |
| 3587 prop); | |
| 3588 if (style != NULL) style->warnings++; | |
| 3589 } | |
| 3590 xmlFree(prop); | |
| 3591 } | |
| 3592 | |
| 3593 prop = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3594 (const xmlChar *) "indent", | |
| 3595 NULL); | |
| 3596 if (prop != NULL) { | |
| 3597 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { | |
| 3598 style->indent = 1; | |
| 3599 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { | |
| 3600 style->indent = 0; | |
| 3601 } else { | |
| 3602 xsltTransformError(ctxt, NULL, inst, | |
| 3603 "invalid value for indent: %s\n", prop); | |
| 3604 if (style != NULL) style->warnings++; | |
| 3605 } | |
| 3606 xmlFree(prop); | |
| 3607 } | |
| 3608 | |
| 3609 prop = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3610 (const xmlChar *) | |
| 3611 "omit-xml-declaration", | |
| 3612 NULL); | |
| 3613 if (prop != NULL) { | |
| 3614 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { | |
| 3615 style->omitXmlDeclaration = 1; | |
| 3616 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { | |
| 3617 style->omitXmlDeclaration = 0; | |
| 3618 } else { | |
| 3619 xsltTransformError(ctxt, NULL, inst, | |
| 3620 "invalid value for omit-xml-declaration: %s\n", | |
| 3621 prop); | |
| 3622 if (style != NULL) style->warnings++; | |
| 3623 } | |
| 3624 xmlFree(prop); | |
| 3625 } | |
| 3626 | |
| 3627 elements = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 3628 (const xmlChar *) | |
| 3629 "cdata-section-elements", | |
| 3630 NULL); | |
| 3631 if (elements != NULL) { | |
| 3632 if (style->stripSpaces == NULL) | |
| 3633 style->stripSpaces = xmlHashCreate(10); | |
| 3634 if (style->stripSpaces == NULL) | |
| 3635 return; | |
| 3636 | |
| 3637 element = elements; | |
| 3638 while (*element != 0) { | |
| 3639 while (IS_BLANK_CH(*element)) | |
| 3640 element++; | |
| 3641 if (*element == 0) | |
| 3642 break; | |
| 3643 end = element; | |
| 3644 while ((*end != 0) && (!IS_BLANK_CH(*end))) | |
| 3645 end++; | |
| 3646 element = xmlStrndup(element, end - element); | |
| 3647 if (element) { | |
| 3648 const xmlChar *URI; | |
| 3649 | |
| 3650 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 3651 xsltGenericDebug(xsltGenericDebugContext, | |
| 3652 "add cdata section output element %s\n", | |
| 3653 element); | |
| 3654 #endif | |
| 3655 URI = xsltGetQNameURI(inst, &element); | |
| 3656 | |
| 3657 xmlHashAddEntry2(style->stripSpaces, element, URI, | |
| 3658 (xmlChar *) "cdata"); | |
| 3659 xmlFree(element); | |
| 3660 } | |
| 3661 element = end; | |
| 3662 } | |
| 3663 xmlFree(elements); | |
| 3664 } | |
| 3665 | |
| 3666 /* | |
| 3667 * Create a new document tree and process the element template | |
| 3668 */ | |
| 3669 XSLT_GET_IMPORT_PTR(method, style, method) | |
| 3670 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) | |
| 3671 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) | |
| 3672 XSLT_GET_IMPORT_PTR(version, style, version) | |
| 3673 XSLT_GET_IMPORT_PTR(encoding, style, encoding) | |
| 3674 | |
| 3675 if ((method != NULL) && | |
| 3676 (!xmlStrEqual(method, (const xmlChar *) "xml"))) { | |
| 3677 if (xmlStrEqual(method, (const xmlChar *) "html")) { | |
| 3678 ctxt->type = XSLT_OUTPUT_HTML; | |
| 3679 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) | |
| 3680 res = htmlNewDoc(doctypeSystem, doctypePublic); | |
| 3681 else { | |
| 3682 if (version != NULL) { | |
| 3683 #ifdef XSLT_GENERATE_HTML_DOCTYPE | |
| 3684 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); | |
| 3685 #endif | |
| 3686 } | |
| 3687 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); | |
| 3688 } | |
| 3689 if (res == NULL) | |
| 3690 goto error; | |
| 3691 res->dict = ctxt->dict; | |
| 3692 xmlDictReference(res->dict); | |
| 3693 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { | |
| 3694 xsltTransformError(ctxt, NULL, inst, | |
| 3695 "xsltDocumentElem: unsupported method xhtml\n"); | |
| 3696 ctxt->type = XSLT_OUTPUT_HTML; | |
| 3697 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); | |
| 3698 if (res == NULL) | |
| 3699 goto error; | |
| 3700 res->dict = ctxt->dict; | |
| 3701 xmlDictReference(res->dict); | |
| 3702 } else if (xmlStrEqual(method, (const xmlChar *) "text")) { | |
| 3703 ctxt->type = XSLT_OUTPUT_TEXT; | |
| 3704 res = xmlNewDoc(style->version); | |
| 3705 if (res == NULL) | |
| 3706 goto error; | |
| 3707 res->dict = ctxt->dict; | |
| 3708 xmlDictReference(res->dict); | |
| 3709 #ifdef WITH_XSLT_DEBUG | |
| 3710 xsltGenericDebug(xsltGenericDebugContext, | |
| 3711 "reusing transformation dict for output\n"); | |
| 3712 #endif | |
| 3713 } else { | |
| 3714 xsltTransformError(ctxt, NULL, inst, | |
| 3715 "xsltDocumentElem: unsupported method (%s)\n", | |
| 3716 method); | |
| 3717 goto error; | |
| 3718 } | |
| 3719 } else { | |
| 3720 ctxt->type = XSLT_OUTPUT_XML; | |
| 3721 res = xmlNewDoc(style->version); | |
| 3722 if (res == NULL) | |
| 3723 goto error; | |
| 3724 res->dict = ctxt->dict; | |
| 3725 xmlDictReference(res->dict); | |
| 3726 #ifdef WITH_XSLT_DEBUG | |
| 3727 xsltGenericDebug(xsltGenericDebugContext, | |
| 3728 "reusing transformation dict for output\n"); | |
| 3729 #endif | |
| 3730 } | |
| 3731 res->charset = XML_CHAR_ENCODING_UTF8; | |
| 3732 if (encoding != NULL) | |
| 3733 res->encoding = xmlStrdup(encoding); | |
| 3734 ctxt->output = res; | |
| 3735 ctxt->insert = (xmlNodePtr) res; | |
| 3736 xsltApplySequenceConstructor(ctxt, node, inst->children, NULL); | |
| 3737 | |
| 3738 /* | |
| 3739 * Do some post processing work depending on the generated output | |
| 3740 */ | |
| 3741 root = xmlDocGetRootElement(res); | |
| 3742 if (root != NULL) { | |
| 3743 const xmlChar *doctype = NULL; | |
| 3744 | |
| 3745 if ((root->ns != NULL) && (root->ns->prefix != NULL)) | |
| 3746 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); | |
| 3747 if (doctype == NULL) | |
| 3748 doctype = root->name; | |
| 3749 | |
| 3750 /* | |
| 3751 * Apply the default selection of the method | |
| 3752 */ | |
| 3753 if ((method == NULL) && | |
| 3754 (root->ns == NULL) && | |
| 3755 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { | |
| 3756 xmlNodePtr tmp; | |
| 3757 | |
| 3758 tmp = res->children; | |
| 3759 while ((tmp != NULL) && (tmp != root)) { | |
| 3760 if (tmp->type == XML_ELEMENT_NODE) | |
| 3761 break; | |
| 3762 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) | |
| 3763 break; | |
| 3764 tmp = tmp->next; | |
| 3765 } | |
| 3766 if (tmp == root) { | |
| 3767 ctxt->type = XSLT_OUTPUT_HTML; | |
| 3768 res->type = XML_HTML_DOCUMENT_NODE; | |
| 3769 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { | |
| 3770 res->intSubset = xmlCreateIntSubset(res, doctype, | |
| 3771 doctypePublic, | |
| 3772 doctypeSystem); | |
| 3773 #ifdef XSLT_GENERATE_HTML_DOCTYPE | |
| 3774 } else if (version != NULL) { | |
| 3775 xsltGetHTMLIDs(version, &doctypePublic, | |
| 3776 &doctypeSystem); | |
| 3777 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) | |
| 3778 res->intSubset = | |
| 3779 xmlCreateIntSubset(res, doctype, | |
| 3780 doctypePublic, | |
| 3781 doctypeSystem); | |
| 3782 #endif | |
| 3783 } | |
| 3784 } | |
| 3785 | |
| 3786 } | |
| 3787 if (ctxt->type == XSLT_OUTPUT_XML) { | |
| 3788 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) | |
| 3789 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) | |
| 3790 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) | |
| 3791 res->intSubset = xmlCreateIntSubset(res, doctype, | |
| 3792 doctypePublic, | |
| 3793 doctypeSystem); | |
| 3794 } | |
| 3795 } | |
| 3796 | |
| 3797 /* | |
| 3798 * Calls to redirect:write also take an optional attribute append. | |
| 3799 * Attribute append="true|yes" which will attempt to simply append | |
| 3800 * to an existing file instead of always opening a new file. The | |
| 3801 * default behavior of always overwriting the file still happens | |
| 3802 * if we do not specify append. | |
| 3803 * Note that append use will forbid use of remote URI target. | |
| 3804 */ | |
| 3805 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"append", | |
| 3806 NULL); | |
| 3807 if (prop != NULL) { | |
| 3808 if (xmlStrEqual(prop, (const xmlChar *) "true") || | |
| 3809 xmlStrEqual(prop, (const xmlChar *) "yes")) { | |
| 3810 style->omitXmlDeclaration = 1; | |
| 3811 redirect_write_append = 1; | |
| 3812 } else | |
| 3813 style->omitXmlDeclaration = 0; | |
| 3814 xmlFree(prop); | |
| 3815 } | |
| 3816 | |
| 3817 if (redirect_write_append) { | |
| 3818 FILE *f; | |
| 3819 | |
| 3820 f = fopen((const char *) filename, "ab"); | |
| 3821 if (f == NULL) { | |
| 3822 ret = -1; | |
| 3823 } else { | |
| 3824 ret = xsltSaveResultToFile(f, res, style); | |
| 3825 fclose(f); | |
| 3826 } | |
| 3827 } else { | |
| 3828 ret = xsltSaveResultToFilename((const char *) filename, res, style, 0); | |
| 3829 } | |
| 3830 if (ret < 0) { | |
| 3831 xsltTransformError(ctxt, NULL, inst, | |
| 3832 "xsltDocumentElem: unable to save to %s\n", | |
| 3833 filename); | |
| 3834 #ifdef WITH_XSLT_DEBUG_EXTRA | |
| 3835 } else { | |
| 3836 xsltGenericDebug(xsltGenericDebugContext, | |
| 3837 "Wrote %d bytes to %s\n", ret, filename); | |
| 3838 #endif | |
| 3839 } | |
| 3840 | |
| 3841 error: | |
| 3842 ctxt->output = oldOutput; | |
| 3843 ctxt->insert = oldInsert; | |
| 3844 ctxt->type = oldType; | |
| 3845 ctxt->outputFile = oldOutputFile; | |
| 3846 if (URL != NULL) | |
| 3847 xmlFree(URL); | |
| 3848 if (filename != NULL) | |
| 3849 xmlFree(filename); | |
| 3850 if (style != NULL) | |
| 3851 xsltFreeStylesheet(style); | |
| 3852 if (res != NULL) | |
| 3853 xmlFreeDoc(res); | |
| 3854 } | |
| 3855 | |
| 3856 /************************************************************************ | |
| 3857 * * | |
| 3858 * Most of the XSLT-1.0 transformations * | |
| 3859 * * | |
| 3860 ************************************************************************/ | |
| 3861 | |
| 3862 /** | |
| 3863 * xsltSort: | |
| 3864 * @ctxt: a XSLT process context | |
| 3865 * @node: the node in the source tree. | |
| 3866 * @inst: the xslt sort node | |
| 3867 * @comp: precomputed information | |
| 3868 * | |
| 3869 * function attached to xsl:sort nodes, but this should not be | |
| 3870 * called directly | |
| 3871 */ | |
| 3872 void | |
| 3873 xsltSort(xsltTransformContextPtr ctxt, | |
| 3874 xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, | |
| 3875 xsltStylePreCompPtr comp) { | |
| 3876 if (comp == NULL) { | |
| 3877 xsltTransformError(ctxt, NULL, inst, | |
| 3878 "xsl:sort : compilation failed\n"); | |
| 3879 return; | |
| 3880 } | |
| 3881 xsltTransformError(ctxt, NULL, inst, | |
| 3882 "xsl:sort : improper use this should not be reached\n"); | |
| 3883 } | |
| 3884 | |
| 3885 /** | |
| 3886 * xsltCopy: | |
| 3887 * @ctxt: an XSLT process context | |
| 3888 * @node: the node in the source tree | |
| 3889 * @inst: the element node of the XSLT-copy instruction | |
| 3890 * @castedComp: computed information of the XSLT-copy instruction | |
| 3891 * | |
| 3892 * Execute the XSLT-copy instruction on the source node. | |
| 3893 */ | |
| 3894 void | |
| 3895 xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 3896 xmlNodePtr inst, xsltStylePreCompPtr castedComp) | |
| 3897 { | |
| 3898 #ifdef XSLT_REFACTORED | |
| 3899 xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp; | |
| 3900 #else | |
| 3901 xsltStylePreCompPtr comp = castedComp; | |
| 3902 #endif | |
| 3903 xmlNodePtr copy, oldInsert; | |
| 3904 | |
| 3905 oldInsert = ctxt->insert; | |
| 3906 if (ctxt->insert != NULL) { | |
| 3907 switch (node->type) { | |
| 3908 case XML_TEXT_NODE: | |
| 3909 case XML_CDATA_SECTION_NODE: | |
| 3910 /* | |
| 3911 * This text comes from the stylesheet | |
| 3912 * For stylesheets, the set of whitespace-preserving | |
| 3913 * element names consists of just xsl:text. | |
| 3914 */ | |
| 3915 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 3916 if (node->type == XML_CDATA_SECTION_NODE) { | |
| 3917 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGeneric
DebugContext, | |
| 3918 "xsltCopy: CDATA text %s\n", node->content)); | |
| 3919 } else { | |
| 3920 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGeneric
DebugContext, | |
| 3921 "xsltCopy: text %s\n", node->content)); | |
| 3922 } | |
| 3923 #endif | |
| 3924 xsltCopyText(ctxt, ctxt->insert, node, 0); | |
| 3925 break; | |
| 3926 case XML_DOCUMENT_NODE: | |
| 3927 case XML_HTML_DOCUMENT_NODE: | |
| 3928 break; | |
| 3929 case XML_ELEMENT_NODE: | |
| 3930 /* | |
| 3931 * REVISIT NOTE: The "fake" is a doc-node, not an element node. | |
| 3932 * REMOVED: | |
| 3933 * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt")) | |
| 3934 * return; | |
| 3935 */ | |
| 3936 | |
| 3937 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 3938 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebu
gContext, | |
| 3939 "xsltCopy: node %s\n", node->name)); | |
| 3940 #endif | |
| 3941 copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0); | |
| 3942 ctxt->insert = copy; | |
| 3943 if (comp->use != NULL) { | |
| 3944 xsltApplyAttributeSet(ctxt, node, inst, comp->use); | |
| 3945 } | |
| 3946 break; | |
| 3947 case XML_ATTRIBUTE_NODE: { | |
| 3948 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 3949 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebu
gContext, | |
| 3950 "xsltCopy: attribute %s\n", node->name)); | |
| 3951 #endif | |
| 3952 /* | |
| 3953 * REVISIT: We could also raise an error if the parent is not | |
| 3954 * an element node. | |
| 3955 * OPTIMIZE TODO: Can we set the value/children of the | |
| 3956 * attribute without an intermediate copy of the string value? | |
| 3957 */ | |
| 3958 xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node)
; | |
| 3959 break; | |
| 3960 } | |
| 3961 case XML_PI_NODE: | |
| 3962 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 3963 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebu
gContext, | |
| 3964 "xsltCopy: PI %s\n", node->name)); | |
| 3965 #endif | |
| 3966 copy = xmlNewDocPI(ctxt->insert->doc, node->name, | |
| 3967 node->content); | |
| 3968 copy = xsltAddChild(ctxt->insert, copy); | |
| 3969 break; | |
| 3970 case XML_COMMENT_NODE: | |
| 3971 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 3972 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebu
gContext, | |
| 3973 "xsltCopy: comment\n")); | |
| 3974 #endif | |
| 3975 copy = xmlNewComment(node->content); | |
| 3976 copy = xsltAddChild(ctxt->insert, copy); | |
| 3977 break; | |
| 3978 case XML_NAMESPACE_DECL: | |
| 3979 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 3980 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebu
gContext, | |
| 3981 "xsltCopy: namespace declaration\n")); | |
| 3982 #endif | |
| 3983 xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node); | |
| 3984 break; | |
| 3985 default: | |
| 3986 break; | |
| 3987 | |
| 3988 } | |
| 3989 } | |
| 3990 | |
| 3991 switch (node->type) { | |
| 3992 case XML_DOCUMENT_NODE: | |
| 3993 case XML_HTML_DOCUMENT_NODE: | |
| 3994 case XML_ELEMENT_NODE: | |
| 3995 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, | |
| 3996 NULL); | |
| 3997 break; | |
| 3998 default: | |
| 3999 break; | |
| 4000 } | |
| 4001 ctxt->insert = oldInsert; | |
| 4002 } | |
| 4003 | |
| 4004 /** | |
| 4005 * xsltText: | |
| 4006 * @ctxt: a XSLT process context | |
| 4007 * @node: the node in the source tree. | |
| 4008 * @inst: the xslt text node | |
| 4009 * @comp: precomputed information | |
| 4010 * | |
| 4011 * Process the xslt text node on the source node | |
| 4012 */ | |
| 4013 void | |
| 4014 xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED, | |
| 4015 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { | |
| 4016 if ((inst->children != NULL) && (comp != NULL)) { | |
| 4017 xmlNodePtr text = inst->children; | |
| 4018 xmlNodePtr copy; | |
| 4019 | |
| 4020 while (text != NULL) { | |
| 4021 if ((text->type != XML_TEXT_NODE) && | |
| 4022 (text->type != XML_CDATA_SECTION_NODE)) { | |
| 4023 xsltTransformError(ctxt, NULL, inst, | |
| 4024 "xsl:text content problem\n"); | |
| 4025 break; | |
| 4026 } | |
| 4027 copy = xmlNewDocText(ctxt->output, text->content); | |
| 4028 if (text->type != XML_CDATA_SECTION_NODE) { | |
| 4029 #ifdef WITH_XSLT_DEBUG_PARSING | |
| 4030 xsltGenericDebug(xsltGenericDebugContext, | |
| 4031 "Disable escaping: %s\n", text->content); | |
| 4032 #endif | |
| 4033 copy->name = xmlStringTextNoenc; | |
| 4034 } | |
| 4035 copy = xsltAddChild(ctxt->insert, copy); | |
| 4036 text = text->next; | |
| 4037 } | |
| 4038 } | |
| 4039 } | |
| 4040 | |
| 4041 /** | |
| 4042 * xsltElement: | |
| 4043 * @ctxt: a XSLT process context | |
| 4044 * @node: the node in the source tree. | |
| 4045 * @inst: the xslt element node | |
| 4046 * @castedComp: precomputed information | |
| 4047 * | |
| 4048 * Process the xslt element node on the source node | |
| 4049 */ | |
| 4050 void | |
| 4051 xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 4052 xmlNodePtr inst, xsltStylePreCompPtr castedComp) { | |
| 4053 #ifdef XSLT_REFACTORED | |
| 4054 xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp; | |
| 4055 #else | |
| 4056 xsltStylePreCompPtr comp = castedComp; | |
| 4057 #endif | |
| 4058 xmlChar *prop = NULL; | |
| 4059 const xmlChar *name, *prefix = NULL, *nsName = NULL; | |
| 4060 xmlNodePtr copy; | |
| 4061 xmlNodePtr oldInsert; | |
| 4062 | |
| 4063 if (ctxt->insert == NULL) | |
| 4064 return; | |
| 4065 | |
| 4066 /* | |
| 4067 * A comp->has_name == 0 indicates that we need to skip this instruction, | |
| 4068 * since it was evaluated to be invalid already during compilation. | |
| 4069 */ | |
| 4070 if (!comp->has_name) | |
| 4071 return; | |
| 4072 | |
| 4073 /* | |
| 4074 * stack and saves | |
| 4075 */ | |
| 4076 oldInsert = ctxt->insert; | |
| 4077 | |
| 4078 if (comp->name == NULL) { | |
| 4079 /* TODO: fix attr acquisition wrt to the XSLT namespace */ | |
| 4080 prop = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 4081 (const xmlChar *) "name", XSLT_NAMESPACE); | |
| 4082 if (prop == NULL) { | |
| 4083 xsltTransformError(ctxt, NULL, inst, | |
| 4084 "xsl:element: The attribute 'name' is missing.\n"); | |
| 4085 goto error; | |
| 4086 } | |
| 4087 if (xmlValidateQName(prop, 0)) { | |
| 4088 xsltTransformError(ctxt, NULL, inst, | |
| 4089 "xsl:element: The effective name '%s' is not a " | |
| 4090 "valid QName.\n", prop); | |
| 4091 /* we fall through to catch any further errors, if possible */ | |
| 4092 } | |
| 4093 name = xsltSplitQName(ctxt->dict, prop, &prefix); | |
| 4094 xmlFree(prop); | |
| 4095 } else { | |
| 4096 /* | |
| 4097 * The "name" value was static. | |
| 4098 */ | |
| 4099 #ifdef XSLT_REFACTORED | |
| 4100 prefix = comp->nsPrefix; | |
| 4101 name = comp->name; | |
| 4102 #else | |
| 4103 name = xsltSplitQName(ctxt->dict, comp->name, &prefix); | |
| 4104 #endif | |
| 4105 } | |
| 4106 | |
| 4107 /* | |
| 4108 * Create the new element | |
| 4109 */ | |
| 4110 if (ctxt->output->dict == ctxt->dict) { | |
| 4111 copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL); | |
| 4112 } else { | |
| 4113 copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL); | |
| 4114 } | |
| 4115 if (copy == NULL) { | |
| 4116 xsltTransformError(ctxt, NULL, inst, | |
| 4117 "xsl:element : creation of %s failed\n", name); | |
| 4118 return; | |
| 4119 } | |
| 4120 copy = xsltAddChild(ctxt->insert, copy); | |
| 4121 if (copy == NULL) { | |
| 4122 xsltTransformError(ctxt, NULL, inst, | |
| 4123 "xsl:element : xsltAddChild failed\n"); | |
| 4124 return; | |
| 4125 } | |
| 4126 | |
| 4127 /* | |
| 4128 * Namespace | |
| 4129 * --------- | |
| 4130 */ | |
| 4131 if (comp->has_ns) { | |
| 4132 if (comp->ns != NULL) { | |
| 4133 /* | |
| 4134 * No AVT; just plain text for the namespace name. | |
| 4135 */ | |
| 4136 if (comp->ns[0] != 0) | |
| 4137 nsName = comp->ns; | |
| 4138 } else { | |
| 4139 xmlChar *tmpNsName; | |
| 4140 /* | |
| 4141 * Eval the AVT. | |
| 4142 */ | |
| 4143 /* TODO: check attr acquisition wrt to the XSLT namespace */ | |
| 4144 tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 4145 (const xmlChar *) "namespace", XSLT_NAMESPACE); | |
| 4146 /* | |
| 4147 * SPEC XSLT 1.0: | |
| 4148 * "If the string is empty, then the expanded-name of the | |
| 4149 * attribute has a null namespace URI." | |
| 4150 */ | |
| 4151 if ((tmpNsName != NULL) && (tmpNsName[0] != 0)) | |
| 4152 nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1); | |
| 4153 xmlFree(tmpNsName); | |
| 4154 } | |
| 4155 | |
| 4156 if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { | |
| 4157 xsltTransformError(ctxt, NULL, inst, | |
| 4158 "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " | |
| 4159 "forbidden.\n"); | |
| 4160 goto error; | |
| 4161 } | |
| 4162 if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { | |
| 4163 prefix = BAD_CAST "xml"; | |
| 4164 } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { | |
| 4165 prefix = NULL; | |
| 4166 } | |
| 4167 } else { | |
| 4168 xmlNsPtr ns; | |
| 4169 /* | |
| 4170 * SPEC XSLT 1.0: | |
| 4171 * "If the namespace attribute is not present, then the QName is | |
| 4172 * expanded into an expanded-name using the namespace declarations | |
| 4173 * in effect for the xsl:element element, including any default | |
| 4174 * namespace declaration. | |
| 4175 */ | |
| 4176 ns = xmlSearchNs(inst->doc, inst, prefix); | |
| 4177 if (ns == NULL) { | |
| 4178 /* | |
| 4179 * TODO: Check this in the compilation layer in case it's a | |
| 4180 * static value. | |
| 4181 */ | |
| 4182 if (prefix != NULL) { | |
| 4183 xsltTransformError(ctxt, NULL, inst, | |
| 4184 "xsl:element: The QName '%s:%s' has no " | |
| 4185 "namespace binding in scope in the stylesheet; " | |
| 4186 "this is an error, since the namespace was not " | |
| 4187 "specified by the instruction itself.\n", prefix, name); | |
| 4188 } | |
| 4189 } else | |
| 4190 nsName = ns->href; | |
| 4191 } | |
| 4192 /* | |
| 4193 * Find/create a matching ns-decl in the result tree. | |
| 4194 */ | |
| 4195 if (nsName != NULL) { | |
| 4196 if (xmlStrEqual(prefix, BAD_CAST "xmlns")) { | |
| 4197 /* Don't use a prefix of "xmlns" */ | |
| 4198 xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); | |
| 4199 | |
| 4200 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, copy); | |
| 4201 | |
| 4202 xmlFree(pref); | |
| 4203 } else { | |
| 4204 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, | |
| 4205 copy); | |
| 4206 } | |
| 4207 } else if ((copy->parent != NULL) && | |
| 4208 (copy->parent->type == XML_ELEMENT_NODE) && | |
| 4209 (copy->parent->ns != NULL)) | |
| 4210 { | |
| 4211 /* | |
| 4212 * "Undeclare" the default namespace. | |
| 4213 */ | |
| 4214 xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy); | |
| 4215 } | |
| 4216 | |
| 4217 ctxt->insert = copy; | |
| 4218 | |
| 4219 if (comp->has_use) { | |
| 4220 if (comp->use != NULL) { | |
| 4221 xsltApplyAttributeSet(ctxt, node, inst, comp->use); | |
| 4222 } else { | |
| 4223 xmlChar *attrSets = NULL; | |
| 4224 /* | |
| 4225 * BUG TODO: use-attribute-sets is not a value template. | |
| 4226 * use-attribute-sets = qnames | |
| 4227 */ | |
| 4228 attrSets = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 4229 (const xmlChar *)"use-attribute-sets", NULL); | |
| 4230 if (attrSets != NULL) { | |
| 4231 xsltApplyAttributeSet(ctxt, node, inst, attrSets); | |
| 4232 xmlFree(attrSets); | |
| 4233 } | |
| 4234 } | |
| 4235 } | |
| 4236 /* | |
| 4237 * Instantiate the sequence constructor. | |
| 4238 */ | |
| 4239 if (inst->children != NULL) | |
| 4240 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, | |
| 4241 NULL); | |
| 4242 | |
| 4243 error: | |
| 4244 ctxt->insert = oldInsert; | |
| 4245 return; | |
| 4246 } | |
| 4247 | |
| 4248 | |
| 4249 /** | |
| 4250 * xsltComment: | |
| 4251 * @ctxt: a XSLT process context | |
| 4252 * @node: the node in the source tree. | |
| 4253 * @inst: the xslt comment node | |
| 4254 * @comp: precomputed information | |
| 4255 * | |
| 4256 * Process the xslt comment node on the source node | |
| 4257 */ | |
| 4258 void | |
| 4259 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 4260 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { | |
| 4261 xmlChar *value = NULL; | |
| 4262 xmlNodePtr commentNode; | |
| 4263 int len; | |
| 4264 | |
| 4265 value = xsltEvalTemplateString(ctxt, node, inst); | |
| 4266 /* TODO: use or generate the compiled form */ | |
| 4267 len = xmlStrlen(value); | |
| 4268 if (len > 0) { | |
| 4269 if ((value[len-1] == '-') || | |
| 4270 (xmlStrstr(value, BAD_CAST "--"))) { | |
| 4271 xsltTransformError(ctxt, NULL, inst, | |
| 4272 "xsl:comment : '--' or ending '-' not allowed in comment\n")
; | |
| 4273 /* fall through to try to catch further errors */ | |
| 4274 } | |
| 4275 } | |
| 4276 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4277 if (value == NULL) { | |
| 4278 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugCont
ext, | |
| 4279 "xsltComment: empty\n")); | |
| 4280 } else { | |
| 4281 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugCont
ext, | |
| 4282 "xsltComment: content %s\n", value)); | |
| 4283 } | |
| 4284 #endif | |
| 4285 | |
| 4286 commentNode = xmlNewComment(value); | |
| 4287 commentNode = xsltAddChild(ctxt->insert, commentNode); | |
| 4288 | |
| 4289 if (value != NULL) | |
| 4290 xmlFree(value); | |
| 4291 } | |
| 4292 | |
| 4293 /** | |
| 4294 * xsltProcessingInstruction: | |
| 4295 * @ctxt: a XSLT process context | |
| 4296 * @node: the node in the source tree. | |
| 4297 * @inst: the xslt processing-instruction node | |
| 4298 * @castedComp: precomputed information | |
| 4299 * | |
| 4300 * Process the xslt processing-instruction node on the source node | |
| 4301 */ | |
| 4302 void | |
| 4303 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 4304 xmlNodePtr inst, xsltStylePreCompPtr castedComp) { | |
| 4305 #ifdef XSLT_REFACTORED | |
| 4306 xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp; | |
| 4307 #else | |
| 4308 xsltStylePreCompPtr comp = castedComp; | |
| 4309 #endif | |
| 4310 const xmlChar *name; | |
| 4311 xmlChar *value = NULL; | |
| 4312 xmlNodePtr pi; | |
| 4313 | |
| 4314 | |
| 4315 if (ctxt->insert == NULL) | |
| 4316 return; | |
| 4317 if (comp->has_name == 0) | |
| 4318 return; | |
| 4319 if (comp->name == NULL) { | |
| 4320 name = xsltEvalAttrValueTemplate(ctxt, inst, | |
| 4321 (const xmlChar *)"name", NULL); | |
| 4322 if (name == NULL) { | |
| 4323 xsltTransformError(ctxt, NULL, inst, | |
| 4324 "xsl:processing-instruction : name is missing\n"); | |
| 4325 goto error; | |
| 4326 } | |
| 4327 } else { | |
| 4328 name = comp->name; | |
| 4329 } | |
| 4330 /* TODO: check that it's both an an NCName and a PITarget. */ | |
| 4331 | |
| 4332 | |
| 4333 value = xsltEvalTemplateString(ctxt, node, inst); | |
| 4334 if (xmlStrstr(value, BAD_CAST "?>") != NULL) { | |
| 4335 xsltTransformError(ctxt, NULL, inst, | |
| 4336 "xsl:processing-instruction: '?>' not allowed within PI content\n")
; | |
| 4337 goto error; | |
| 4338 } | |
| 4339 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4340 if (value == NULL) { | |
| 4341 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, | |
| 4342 "xsltProcessingInstruction: %s empty\n", name)); | |
| 4343 } else { | |
| 4344 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, | |
| 4345 "xsltProcessingInstruction: %s content %s\n", name, value)); | |
| 4346 } | |
| 4347 #endif | |
| 4348 | |
| 4349 pi = xmlNewDocPI(ctxt->insert->doc, name, value); | |
| 4350 pi = xsltAddChild(ctxt->insert, pi); | |
| 4351 | |
| 4352 error: | |
| 4353 if ((name != NULL) && (name != comp->name)) | |
| 4354 xmlFree((xmlChar *) name); | |
| 4355 if (value != NULL) | |
| 4356 xmlFree(value); | |
| 4357 } | |
| 4358 | |
| 4359 /** | |
| 4360 * xsltCopyOf: | |
| 4361 * @ctxt: an XSLT transformation context | |
| 4362 * @node: the current node in the source tree | |
| 4363 * @inst: the element node of the XSLT copy-of instruction | |
| 4364 * @castedComp: precomputed information of the XSLT copy-of instruction | |
| 4365 * | |
| 4366 * Process the XSLT copy-of instruction. | |
| 4367 */ | |
| 4368 void | |
| 4369 xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 4370 xmlNodePtr inst, xsltStylePreCompPtr castedComp) { | |
| 4371 #ifdef XSLT_REFACTORED | |
| 4372 xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp; | |
| 4373 #else | |
| 4374 xsltStylePreCompPtr comp = castedComp; | |
| 4375 #endif | |
| 4376 xmlXPathObjectPtr res = NULL; | |
| 4377 xmlNodeSetPtr list = NULL; | |
| 4378 int i; | |
| 4379 | |
| 4380 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) | |
| 4381 return; | |
| 4382 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { | |
| 4383 xsltTransformError(ctxt, NULL, inst, | |
| 4384 "xsl:copy-of : compilation failed\n"); | |
| 4385 return; | |
| 4386 } | |
| 4387 | |
| 4388 /* | |
| 4389 * SPEC XSLT 1.0: | |
| 4390 * "The xsl:copy-of element can be used to insert a result tree | |
| 4391 * fragment into the result tree, without first converting it to | |
| 4392 * a string as xsl:value-of does (see [7.6.1 Generating Text with | |
| 4393 * xsl:value-of]). The required select attribute contains an | |
| 4394 * expression. When the result of evaluating the expression is a | |
| 4395 * result tree fragment, the complete fragment is copied into the | |
| 4396 * result tree. When the result is a node-set, all the nodes in the | |
| 4397 * set are copied in document order into the result tree; copying | |
| 4398 * an element node copies the attribute nodes, namespace nodes and | |
| 4399 * children of the element node as well as the element node itself; | |
| 4400 * a root node is copied by copying its children. When the result | |
| 4401 * is neither a node-set nor a result tree fragment, the result is | |
| 4402 * converted to a string and then inserted into the result tree, | |
| 4403 * as with xsl:value-of. | |
| 4404 */ | |
| 4405 | |
| 4406 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4407 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, | |
| 4408 "xsltCopyOf: select %s\n", comp->select)); | |
| 4409 #endif | |
| 4410 | |
| 4411 /* | |
| 4412 * Evaluate the "select" expression. | |
| 4413 */ | |
| 4414 res = xsltPreCompEval(ctxt, node, comp); | |
| 4415 | |
| 4416 if (res != NULL) { | |
| 4417 if (res->type == XPATH_NODESET) { | |
| 4418 /* | |
| 4419 * Node-set | |
| 4420 * -------- | |
| 4421 */ | |
| 4422 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4423 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebug
Context, | |
| 4424 "xsltCopyOf: result is a node set\n")); | |
| 4425 #endif | |
| 4426 list = res->nodesetval; | |
| 4427 if (list != NULL) { | |
| 4428 xmlNodePtr cur; | |
| 4429 /* | |
| 4430 * The list is already sorted in document order by XPath. | |
| 4431 * Append everything in this order under ctxt->insert. | |
| 4432 */ | |
| 4433 for (i = 0;i < list->nodeNr;i++) { | |
| 4434 cur = list->nodeTab[i]; | |
| 4435 if (cur == NULL) | |
| 4436 continue; | |
| 4437 if ((cur->type == XML_DOCUMENT_NODE) || | |
| 4438 (cur->type == XML_HTML_DOCUMENT_NODE)) | |
| 4439 { | |
| 4440 xsltCopyTreeList(ctxt, inst, | |
| 4441 cur->children, ctxt->insert, 0, 0); | |
| 4442 } else if (cur->type == XML_ATTRIBUTE_NODE) { | |
| 4443 xsltShallowCopyAttr(ctxt, inst, | |
| 4444 ctxt->insert, (xmlAttrPtr) cur); | |
| 4445 } else { | |
| 4446 xsltCopyTree(ctxt, inst, cur, ctxt->insert, 0, 0); | |
| 4447 } | |
| 4448 } | |
| 4449 } | |
| 4450 } else if (res->type == XPATH_XSLT_TREE) { | |
| 4451 /* | |
| 4452 * Result tree fragment | |
| 4453 * -------------------- | |
| 4454 * E.g. via <xsl:variable ...><foo/></xsl:variable> | |
| 4455 * Note that the root node of such trees is an xmlDocPtr in Libxslt. | |
| 4456 */ | |
| 4457 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4458 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebug
Context, | |
| 4459 "xsltCopyOf: result is a result tree fragment\n")); | |
| 4460 #endif | |
| 4461 list = res->nodesetval; | |
| 4462 if ((list != NULL) && (list->nodeTab != NULL) && | |
| 4463 (list->nodeTab[0] != NULL) && | |
| 4464 (IS_XSLT_REAL_NODE(list->nodeTab[0]))) | |
| 4465 { | |
| 4466 xsltCopyTreeList(ctxt, inst, | |
| 4467 list->nodeTab[0]->children, ctxt->insert, 0, 0); | |
| 4468 } | |
| 4469 } else { | |
| 4470 xmlChar *value = NULL; | |
| 4471 /* | |
| 4472 * Convert to a string. | |
| 4473 */ | |
| 4474 value = xmlXPathCastToString(res); | |
| 4475 if (value == NULL) { | |
| 4476 xsltTransformError(ctxt, NULL, inst, | |
| 4477 "Internal error in xsltCopyOf(): " | |
| 4478 "failed to cast an XPath object to string.\n"); | |
| 4479 ctxt->state = XSLT_STATE_STOPPED; | |
| 4480 } else { | |
| 4481 if (value[0] != 0) { | |
| 4482 /* | |
| 4483 * Append content as text node. | |
| 4484 */ | |
| 4485 xsltCopyTextString(ctxt, ctxt->insert, value, 0); | |
| 4486 } | |
| 4487 xmlFree(value); | |
| 4488 | |
| 4489 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4490 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericD
ebugContext, | |
| 4491 "xsltCopyOf: result %s\n", res->stringval)); | |
| 4492 #endif | |
| 4493 } | |
| 4494 } | |
| 4495 } else { | |
| 4496 ctxt->state = XSLT_STATE_STOPPED; | |
| 4497 } | |
| 4498 | |
| 4499 if (res != NULL) | |
| 4500 xmlXPathFreeObject(res); | |
| 4501 } | |
| 4502 | |
| 4503 /** | |
| 4504 * xsltValueOf: | |
| 4505 * @ctxt: a XSLT process context | |
| 4506 * @node: the node in the source tree. | |
| 4507 * @inst: the xslt value-of node | |
| 4508 * @castedComp: precomputed information | |
| 4509 * | |
| 4510 * Process the xslt value-of node on the source node | |
| 4511 */ | |
| 4512 void | |
| 4513 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 4514 xmlNodePtr inst, xsltStylePreCompPtr castedComp) | |
| 4515 { | |
| 4516 #ifdef XSLT_REFACTORED | |
| 4517 xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp; | |
| 4518 #else | |
| 4519 xsltStylePreCompPtr comp = castedComp; | |
| 4520 #endif | |
| 4521 xmlXPathObjectPtr res = NULL; | |
| 4522 xmlChar *value = NULL; | |
| 4523 | |
| 4524 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) | |
| 4525 return; | |
| 4526 | |
| 4527 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { | |
| 4528 xsltTransformError(ctxt, NULL, inst, | |
| 4529 "Internal error in xsltValueOf(): " | |
| 4530 "The XSLT 'value-of' instruction was not compiled.\n"); | |
| 4531 return; | |
| 4532 } | |
| 4533 | |
| 4534 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4535 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext
, | |
| 4536 "xsltValueOf: select %s\n", comp->select)); | |
| 4537 #endif | |
| 4538 | |
| 4539 res = xsltPreCompEval(ctxt, node, comp); | |
| 4540 | |
| 4541 /* | |
| 4542 * Cast the XPath object to string. | |
| 4543 */ | |
| 4544 if (res != NULL) { | |
| 4545 value = xmlXPathCastToString(res); | |
| 4546 if (value == NULL) { | |
| 4547 xsltTransformError(ctxt, NULL, inst, | |
| 4548 "Internal error in xsltValueOf(): " | |
| 4549 "failed to cast an XPath object to string.\n"); | |
| 4550 ctxt->state = XSLT_STATE_STOPPED; | |
| 4551 goto error; | |
| 4552 } | |
| 4553 if (value[0] != 0) { | |
| 4554 xsltCopyTextString(ctxt, ctxt->insert, value, comp->noescape); | |
| 4555 } | |
| 4556 } else { | |
| 4557 xsltTransformError(ctxt, NULL, inst, | |
| 4558 "XPath evaluation returned no result.\n"); | |
| 4559 ctxt->state = XSLT_STATE_STOPPED; | |
| 4560 goto error; | |
| 4561 } | |
| 4562 | |
| 4563 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4564 if (value) { | |
| 4565 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugCon
text, | |
| 4566 "xsltValueOf: result '%s'\n", value)); | |
| 4567 } | |
| 4568 #endif | |
| 4569 | |
| 4570 error: | |
| 4571 if (value != NULL) | |
| 4572 xmlFree(value); | |
| 4573 if (res != NULL) | |
| 4574 xmlXPathFreeObject(res); | |
| 4575 } | |
| 4576 | |
| 4577 /** | |
| 4578 * xsltNumber: | |
| 4579 * @ctxt: a XSLT process context | |
| 4580 * @node: the node in the source tree. | |
| 4581 * @inst: the xslt number node | |
| 4582 * @castedComp: precomputed information | |
| 4583 * | |
| 4584 * Process the xslt number node on the source node | |
| 4585 */ | |
| 4586 void | |
| 4587 xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 4588 xmlNodePtr inst, xsltStylePreCompPtr castedComp) | |
| 4589 { | |
| 4590 #ifdef XSLT_REFACTORED | |
| 4591 xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp; | |
| 4592 #else | |
| 4593 xsltStylePreCompPtr comp = castedComp; | |
| 4594 #endif | |
| 4595 xmlXPathContextPtr xpctxt; | |
| 4596 xmlNsPtr *oldXPNamespaces; | |
| 4597 int oldXPNsNr; | |
| 4598 | |
| 4599 if (comp == NULL) { | |
| 4600 xsltTransformError(ctxt, NULL, inst, | |
| 4601 "xsl:number : compilation failed\n"); | |
| 4602 return; | |
| 4603 } | |
| 4604 | |
| 4605 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) | |
| 4606 return; | |
| 4607 | |
| 4608 comp->numdata.doc = inst->doc; | |
| 4609 comp->numdata.node = inst; | |
| 4610 | |
| 4611 xpctxt = ctxt->xpathCtxt; | |
| 4612 oldXPNsNr = xpctxt->nsNr; | |
| 4613 oldXPNamespaces = xpctxt->namespaces; | |
| 4614 | |
| 4615 #ifdef XSLT_REFACTORED | |
| 4616 if (comp->inScopeNs != NULL) { | |
| 4617 xpctxt->namespaces = comp->inScopeNs->list; | |
| 4618 xpctxt->nsNr = comp->inScopeNs->xpathNumber; | |
| 4619 } else { | |
| 4620 xpctxt->namespaces = NULL; | |
| 4621 xpctxt->nsNr = 0; | |
| 4622 } | |
| 4623 #else | |
| 4624 xpctxt->namespaces = comp->nsList; | |
| 4625 xpctxt->nsNr = comp->nsNr; | |
| 4626 #endif | |
| 4627 | |
| 4628 xsltNumberFormat(ctxt, &comp->numdata, node); | |
| 4629 | |
| 4630 xpctxt->nsNr = oldXPNsNr; | |
| 4631 xpctxt->namespaces = oldXPNamespaces; | |
| 4632 } | |
| 4633 | |
| 4634 /** | |
| 4635 * xsltApplyImports: | |
| 4636 * @ctxt: an XSLT transformation context | |
| 4637 * @contextNode: the current node in the source tree. | |
| 4638 * @inst: the element node of the XSLT 'apply-imports' instruction | |
| 4639 * @comp: the compiled instruction | |
| 4640 * | |
| 4641 * Process the XSLT apply-imports element. | |
| 4642 */ | |
| 4643 void | |
| 4644 xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, | |
| 4645 xmlNodePtr inst, | |
| 4646 xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) | |
| 4647 { | |
| 4648 xsltTemplatePtr templ; | |
| 4649 | |
| 4650 if ((ctxt == NULL) || (inst == NULL)) | |
| 4651 return; | |
| 4652 | |
| 4653 if (comp == NULL) { | |
| 4654 xsltTransformError(ctxt, NULL, inst, | |
| 4655 "Internal error in xsltApplyImports(): " | |
| 4656 "The XSLT 'apply-imports' instruction was not compiled.\n"); | |
| 4657 return; | |
| 4658 } | |
| 4659 /* | |
| 4660 * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the | |
| 4661 * same; the former is the "Current Template Rule" as defined by the | |
| 4662 * XSLT spec, the latter is simply the template struct being | |
| 4663 * currently processed. | |
| 4664 */ | |
| 4665 if (ctxt->currentTemplateRule == NULL) { | |
| 4666 /* | |
| 4667 * SPEC XSLT 2.0: | |
| 4668 * "[ERR XTDE0560] It is a non-recoverable dynamic error if | |
| 4669 * xsl:apply-imports or xsl:next-match is evaluated when the | |
| 4670 * current template rule is null." | |
| 4671 */ | |
| 4672 xsltTransformError(ctxt, NULL, inst, | |
| 4673 "It is an error to call 'apply-imports' " | |
| 4674 "when there's no current template rule.\n"); | |
| 4675 return; | |
| 4676 } | |
| 4677 /* | |
| 4678 * TODO: Check if this is correct. | |
| 4679 */ | |
| 4680 templ = xsltGetTemplate(ctxt, contextNode, | |
| 4681 ctxt->currentTemplateRule->style); | |
| 4682 | |
| 4683 if (templ != NULL) { | |
| 4684 xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule; | |
| 4685 /* | |
| 4686 * Set the current template rule. | |
| 4687 */ | |
| 4688 ctxt->currentTemplateRule = templ; | |
| 4689 /* | |
| 4690 * URGENT TODO: Need xsl:with-param be handled somehow here? | |
| 4691 */ | |
| 4692 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, | |
| 4693 templ, NULL); | |
| 4694 | |
| 4695 ctxt->currentTemplateRule = oldCurTemplRule; | |
| 4696 } | |
| 4697 else { | |
| 4698 /* Use built-in templates. */ | |
| 4699 xsltDefaultProcessOneNode(ctxt, contextNode, NULL); | |
| 4700 } | |
| 4701 } | |
| 4702 | |
| 4703 /** | |
| 4704 * xsltCallTemplate: | |
| 4705 * @ctxt: a XSLT transformation context | |
| 4706 * @node: the "current node" in the source tree | |
| 4707 * @inst: the XSLT 'call-template' instruction | |
| 4708 * @castedComp: the compiled information of the instruction | |
| 4709 * | |
| 4710 * Processes the XSLT call-template instruction on the source node. | |
| 4711 */ | |
| 4712 void | |
| 4713 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 4714 xmlNodePtr inst, xsltStylePreCompPtr castedComp) | |
| 4715 { | |
| 4716 #ifdef XSLT_REFACTORED | |
| 4717 xsltStyleItemCallTemplatePtr comp = | |
| 4718 (xsltStyleItemCallTemplatePtr) castedComp; | |
| 4719 #else | |
| 4720 xsltStylePreCompPtr comp = castedComp; | |
| 4721 #endif | |
| 4722 xsltStackElemPtr withParams = NULL; | |
| 4723 | |
| 4724 if (ctxt->insert == NULL) | |
| 4725 return; | |
| 4726 if (comp == NULL) { | |
| 4727 xsltTransformError(ctxt, NULL, inst, | |
| 4728 "The XSLT 'call-template' instruction was not compiled.\n"); | |
| 4729 return; | |
| 4730 } | |
| 4731 | |
| 4732 /* | |
| 4733 * The template must have been precomputed | |
| 4734 */ | |
| 4735 if (comp->templ == NULL) { | |
| 4736 comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns); | |
| 4737 if (comp->templ == NULL) { | |
| 4738 if (comp->ns != NULL) { | |
| 4739 xsltTransformError(ctxt, NULL, inst, | |
| 4740 "The called template '{%s}%s' was not found.\n", | |
| 4741 comp->ns, comp->name); | |
| 4742 } else { | |
| 4743 xsltTransformError(ctxt, NULL, inst, | |
| 4744 "The called template '%s' was not found.\n", | |
| 4745 comp->name); | |
| 4746 } | |
| 4747 return; | |
| 4748 } | |
| 4749 } | |
| 4750 | |
| 4751 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4752 if ((comp != NULL) && (comp->name != NULL)) | |
| 4753 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDeb
ugContext, | |
| 4754 "call-template: name %s\n", comp->name)); | |
| 4755 #endif | |
| 4756 | |
| 4757 if (inst->children) { | |
| 4758 xmlNodePtr cur; | |
| 4759 xsltStackElemPtr param; | |
| 4760 | |
| 4761 cur = inst->children; | |
| 4762 while (cur != NULL) { | |
| 4763 #ifdef WITH_DEBUGGER | |
| 4764 if (ctxt->debugStatus != XSLT_DEBUG_NONE) | |
| 4765 xslHandleDebugger(cur, node, comp->templ, ctxt); | |
| 4766 #endif | |
| 4767 if (ctxt->state == XSLT_STATE_STOPPED) break; | |
| 4768 /* | |
| 4769 * TODO: The "with-param"s could be part of the "call-template" | |
| 4770 * structure. Avoid to "search" for params dynamically | |
| 4771 * in the XML tree every time. | |
| 4772 */ | |
| 4773 if (IS_XSLT_ELEM(cur)) { | |
| 4774 if (IS_XSLT_NAME(cur, "with-param")) { | |
| 4775 param = xsltParseStylesheetCallerParam(ctxt, cur); | |
| 4776 if (param != NULL) { | |
| 4777 param->next = withParams; | |
| 4778 withParams = param; | |
| 4779 } | |
| 4780 } else { | |
| 4781 xsltGenericError(xsltGenericErrorContext, | |
| 4782 "xsl:call-template: misplaced xsl:%s\n", cur->name); | |
| 4783 } | |
| 4784 } else { | |
| 4785 xsltGenericError(xsltGenericErrorContext, | |
| 4786 "xsl:call-template: misplaced %s element\n", cur->name); | |
| 4787 } | |
| 4788 cur = cur->next; | |
| 4789 } | |
| 4790 } | |
| 4791 /* | |
| 4792 * Create a new frame using the params first | |
| 4793 */ | |
| 4794 xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ, | |
| 4795 withParams); | |
| 4796 if (withParams != NULL) | |
| 4797 xsltFreeStackElemList(withParams); | |
| 4798 | |
| 4799 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4800 if ((comp != NULL) && (comp->name != NULL)) | |
| 4801 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDeb
ugContext, | |
| 4802 "call-template returned: name %s\n", comp->name)); | |
| 4803 #endif | |
| 4804 } | |
| 4805 | |
| 4806 /** | |
| 4807 * xsltApplyTemplates: | |
| 4808 * @ctxt: a XSLT transformation context | |
| 4809 * @node: the 'current node' in the source tree | |
| 4810 * @inst: the element node of an XSLT 'apply-templates' instruction | |
| 4811 * @castedComp: the compiled instruction | |
| 4812 * | |
| 4813 * Processes the XSLT 'apply-templates' instruction on the current node. | |
| 4814 */ | |
| 4815 void | |
| 4816 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 4817 xmlNodePtr inst, xsltStylePreCompPtr castedComp) | |
| 4818 { | |
| 4819 #ifdef XSLT_REFACTORED | |
| 4820 xsltStyleItemApplyTemplatesPtr comp = | |
| 4821 (xsltStyleItemApplyTemplatesPtr) castedComp; | |
| 4822 #else | |
| 4823 xsltStylePreCompPtr comp = castedComp; | |
| 4824 #endif | |
| 4825 int i; | |
| 4826 xmlNodePtr cur, delNode = NULL, oldContextNode; | |
| 4827 xmlNodeSetPtr list = NULL, oldList; | |
| 4828 xsltStackElemPtr withParams = NULL; | |
| 4829 int oldXPProximityPosition, oldXPContextSize; | |
| 4830 const xmlChar *oldMode, *oldModeURI; | |
| 4831 xmlDocPtr oldXPDoc; | |
| 4832 xsltDocumentPtr oldDocInfo; | |
| 4833 xmlXPathContextPtr xpctxt; | |
| 4834 | |
| 4835 if (comp == NULL) { | |
| 4836 xsltTransformError(ctxt, NULL, inst, | |
| 4837 "xsl:apply-templates : compilation failed\n"); | |
| 4838 return; | |
| 4839 } | |
| 4840 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) | |
| 4841 return; | |
| 4842 | |
| 4843 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4844 if ((node != NULL) && (node->name != NULL)) | |
| 4845 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericD
ebugContext, | |
| 4846 "xsltApplyTemplates: node: '%s'\n", node->name)); | |
| 4847 #endif | |
| 4848 | |
| 4849 xpctxt = ctxt->xpathCtxt; | |
| 4850 /* | |
| 4851 * Save context states. | |
| 4852 */ | |
| 4853 oldContextNode = ctxt->node; | |
| 4854 oldMode = ctxt->mode; | |
| 4855 oldModeURI = ctxt->modeURI; | |
| 4856 oldDocInfo = ctxt->document; | |
| 4857 oldList = ctxt->nodeList; | |
| 4858 | |
| 4859 /* | |
| 4860 * The xpath context size and proximity position, as | |
| 4861 * well as the xpath and context documents, may be changed | |
| 4862 * so we save their initial state and will restore on exit | |
| 4863 */ | |
| 4864 oldXPContextSize = xpctxt->contextSize; | |
| 4865 oldXPProximityPosition = xpctxt->proximityPosition; | |
| 4866 oldXPDoc = xpctxt->doc; | |
| 4867 | |
| 4868 /* | |
| 4869 * Set up contexts. | |
| 4870 */ | |
| 4871 ctxt->mode = comp->mode; | |
| 4872 ctxt->modeURI = comp->modeURI; | |
| 4873 | |
| 4874 if (comp->select != NULL) { | |
| 4875 xmlXPathObjectPtr res = NULL; | |
| 4876 | |
| 4877 if (comp->comp == NULL) { | |
| 4878 xsltTransformError(ctxt, NULL, inst, | |
| 4879 "xsl:apply-templates : compilation failed\n"); | |
| 4880 goto error; | |
| 4881 } | |
| 4882 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4883 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericD
ebugContext, | |
| 4884 "xsltApplyTemplates: select %s\n", comp->select)); | |
| 4885 #endif | |
| 4886 | |
| 4887 res = xsltPreCompEval(ctxt, node, comp); | |
| 4888 | |
| 4889 if (res != NULL) { | |
| 4890 if (res->type == XPATH_NODESET) { | |
| 4891 list = res->nodesetval; /* consume the node set */ | |
| 4892 res->nodesetval = NULL; | |
| 4893 } else { | |
| 4894 xsltTransformError(ctxt, NULL, inst, | |
| 4895 "The 'select' expression did not evaluate to a " | |
| 4896 "node set.\n"); | |
| 4897 ctxt->state = XSLT_STATE_STOPPED; | |
| 4898 xmlXPathFreeObject(res); | |
| 4899 goto error; | |
| 4900 } | |
| 4901 xmlXPathFreeObject(res); | |
| 4902 /* | |
| 4903 * Note: An xsl:apply-templates with a 'select' attribute, | |
| 4904 * can change the current source doc. | |
| 4905 */ | |
| 4906 } else { | |
| 4907 xsltTransformError(ctxt, NULL, inst, | |
| 4908 "Failed to evaluate the 'select' expression.\n"); | |
| 4909 ctxt->state = XSLT_STATE_STOPPED; | |
| 4910 goto error; | |
| 4911 } | |
| 4912 if (list == NULL) { | |
| 4913 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 4914 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGene
ricDebugContext, | |
| 4915 "xsltApplyTemplates: select didn't evaluate to a node list\n")); | |
| 4916 #endif | |
| 4917 goto exit; | |
| 4918 } | |
| 4919 /* | |
| 4920 * | |
| 4921 * NOTE: Previously a document info (xsltDocument) was | |
| 4922 * created and attached to the Result Tree Fragment. | |
| 4923 * But such a document info is created on demand in | |
| 4924 * xsltKeyFunction() (functions.c), so we need to create | |
| 4925 * it here beforehand. | |
| 4926 * In order to take care of potential keys we need to | |
| 4927 * do some extra work for the case when a Result Tree Fragment | |
| 4928 * is converted into a nodeset (e.g. exslt:node-set()) : | |
| 4929 * We attach a "pseudo-doc" (xsltDocument) to _private. | |
| 4930 * This xsltDocument, together with the keyset, will be freed | |
| 4931 * when the Result Tree Fragment is freed. | |
| 4932 * | |
| 4933 */ | |
| 4934 #if 0 | |
| 4935 if ((ctxt->nbKeys > 0) && | |
| 4936 (list->nodeNr != 0) && | |
| 4937 (list->nodeTab[0]->doc != NULL) && | |
| 4938 XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc)) | |
| 4939 { | |
| 4940 /* | |
| 4941 * NOTE that it's also OK if @effectiveDocInfo will be | |
| 4942 * set to NULL. | |
| 4943 */ | |
| 4944 isRTF = 1; | |
| 4945 effectiveDocInfo = list->nodeTab[0]->doc->_private; | |
| 4946 } | |
| 4947 #endif | |
| 4948 } else { | |
| 4949 /* | |
| 4950 * Build an XPath node set with the children | |
| 4951 */ | |
| 4952 list = xmlXPathNodeSetCreate(NULL); | |
| 4953 if (list == NULL) | |
| 4954 goto error; | |
| 4955 if (node->type != XML_NAMESPACE_DECL) | |
| 4956 cur = node->children; | |
| 4957 else | |
| 4958 cur = NULL; | |
| 4959 while (cur != NULL) { | |
| 4960 switch (cur->type) { | |
| 4961 case XML_TEXT_NODE: | |
| 4962 if ((IS_BLANK_NODE(cur)) && | |
| 4963 (cur->parent != NULL) && | |
| 4964 (cur->parent->type == XML_ELEMENT_NODE) && | |
| 4965 (ctxt->style->stripSpaces != NULL)) { | |
| 4966 const xmlChar *val; | |
| 4967 | |
| 4968 if (cur->parent->ns != NULL) { | |
| 4969 val = (const xmlChar *) | |
| 4970 xmlHashLookup2(ctxt->style->stripSpaces, | |
| 4971 cur->parent->name, | |
| 4972 cur->parent->ns->href); | |
| 4973 if (val == NULL) { | |
| 4974 val = (const xmlChar *) | |
| 4975 xmlHashLookup2(ctxt->style->stripSpaces, | |
| 4976 BAD_CAST "*", | |
| 4977 cur->parent->ns->href); | |
| 4978 } | |
| 4979 } else { | |
| 4980 val = (const xmlChar *) | |
| 4981 xmlHashLookup2(ctxt->style->stripSpaces, | |
| 4982 cur->parent->name, NULL); | |
| 4983 } | |
| 4984 if ((val != NULL) && | |
| 4985 (xmlStrEqual(val, (xmlChar *) "strip"))) { | |
| 4986 delNode = cur; | |
| 4987 break; | |
| 4988 } | |
| 4989 } | |
| 4990 /* no break on purpose */ | |
| 4991 case XML_ELEMENT_NODE: | |
| 4992 case XML_DOCUMENT_NODE: | |
| 4993 case XML_HTML_DOCUMENT_NODE: | |
| 4994 case XML_CDATA_SECTION_NODE: | |
| 4995 case XML_PI_NODE: | |
| 4996 case XML_COMMENT_NODE: | |
| 4997 xmlXPathNodeSetAddUnique(list, cur); | |
| 4998 break; | |
| 4999 case XML_DTD_NODE: | |
| 5000 /* Unlink the DTD, it's still reachable | |
| 5001 * using doc->intSubset */ | |
| 5002 if (cur->next != NULL) | |
| 5003 cur->next->prev = cur->prev; | |
| 5004 if (cur->prev != NULL) | |
| 5005 cur->prev->next = cur->next; | |
| 5006 break; | |
| 5007 case XML_NAMESPACE_DECL: | |
| 5008 break; | |
| 5009 default: | |
| 5010 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5011 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(
xsltGenericDebugContext, | |
| 5012 "xsltApplyTemplates: skipping cur type %d\n", | |
| 5013 cur->type)); | |
| 5014 #endif | |
| 5015 delNode = cur; | |
| 5016 } | |
| 5017 cur = cur->next; | |
| 5018 if (delNode != NULL) { | |
| 5019 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5020 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xslt
GenericDebugContext, | |
| 5021 "xsltApplyTemplates: removing ignorable blank cur\n")); | |
| 5022 #endif | |
| 5023 xmlUnlinkNode(delNode); | |
| 5024 xmlFreeNode(delNode); | |
| 5025 delNode = NULL; | |
| 5026 } | |
| 5027 } | |
| 5028 } | |
| 5029 | |
| 5030 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5031 if (list != NULL) | |
| 5032 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebug
Context, | |
| 5033 "xsltApplyTemplates: list of %d nodes\n", list->nodeNr)); | |
| 5034 #endif | |
| 5035 | |
| 5036 if ((list == NULL) || (list->nodeNr == 0)) | |
| 5037 goto exit; | |
| 5038 | |
| 5039 /* | |
| 5040 * Set the context's node set and size; this is also needed for | |
| 5041 * for xsltDoSortFunction(). | |
| 5042 */ | |
| 5043 ctxt->nodeList = list; | |
| 5044 /* | |
| 5045 * Process xsl:with-param and xsl:sort instructions. | |
| 5046 * (The code became so verbose just to avoid the | |
| 5047 * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort) | |
| 5048 * BUG TODO: We are not using namespaced potentially defined on the | |
| 5049 * xsl:sort or xsl:with-param elements; XPath expression might fail. | |
| 5050 */ | |
| 5051 if (inst->children) { | |
| 5052 xsltStackElemPtr param; | |
| 5053 | |
| 5054 cur = inst->children; | |
| 5055 while (cur) { | |
| 5056 | |
| 5057 #ifdef WITH_DEBUGGER | |
| 5058 if (ctxt->debugStatus != XSLT_DEBUG_NONE) | |
| 5059 xslHandleDebugger(cur, node, NULL, ctxt); | |
| 5060 #endif | |
| 5061 if (ctxt->state == XSLT_STATE_STOPPED) | |
| 5062 break; | |
| 5063 if (cur->type == XML_TEXT_NODE) { | |
| 5064 cur = cur->next; | |
| 5065 continue; | |
| 5066 } | |
| 5067 if (! IS_XSLT_ELEM(cur)) | |
| 5068 break; | |
| 5069 if (IS_XSLT_NAME(cur, "with-param")) { | |
| 5070 param = xsltParseStylesheetCallerParam(ctxt, cur); | |
| 5071 if (param != NULL) { | |
| 5072 param->next = withParams; | |
| 5073 withParams = param; | |
| 5074 } | |
| 5075 } | |
| 5076 if (IS_XSLT_NAME(cur, "sort")) { | |
| 5077 xsltTemplatePtr oldCurTempRule = | |
| 5078 ctxt->currentTemplateRule; | |
| 5079 int nbsorts = 0; | |
| 5080 xmlNodePtr sorts[XSLT_MAX_SORT]; | |
| 5081 | |
| 5082 sorts[nbsorts++] = cur; | |
| 5083 | |
| 5084 while (cur) { | |
| 5085 | |
| 5086 #ifdef WITH_DEBUGGER | |
| 5087 if (ctxt->debugStatus != XSLT_DEBUG_NONE) | |
| 5088 xslHandleDebugger(cur, node, NULL, ctxt); | |
| 5089 #endif | |
| 5090 if (ctxt->state == XSLT_STATE_STOPPED) | |
| 5091 break; | |
| 5092 | |
| 5093 if (cur->type == XML_TEXT_NODE) { | |
| 5094 cur = cur->next; | |
| 5095 continue; | |
| 5096 } | |
| 5097 | |
| 5098 if (! IS_XSLT_ELEM(cur)) | |
| 5099 break; | |
| 5100 if (IS_XSLT_NAME(cur, "with-param")) { | |
| 5101 param = xsltParseStylesheetCallerParam(ctxt, cur); | |
| 5102 if (param != NULL) { | |
| 5103 param->next = withParams; | |
| 5104 withParams = param; | |
| 5105 } | |
| 5106 } | |
| 5107 if (IS_XSLT_NAME(cur, "sort")) { | |
| 5108 if (nbsorts >= XSLT_MAX_SORT) { | |
| 5109 xsltTransformError(ctxt, NULL, cur, | |
| 5110 "The number (%d) of xsl:sort instructions exceed
s the " | |
| 5111 "maximum allowed by this processor's settings.\n
", | |
| 5112 nbsorts); | |
| 5113 ctxt->state = XSLT_STATE_STOPPED; | |
| 5114 break; | |
| 5115 } else { | |
| 5116 sorts[nbsorts++] = cur; | |
| 5117 } | |
| 5118 } | |
| 5119 cur = cur->next; | |
| 5120 } | |
| 5121 /* | |
| 5122 * The "current template rule" is cleared for xsl:sort. | |
| 5123 */ | |
| 5124 ctxt->currentTemplateRule = NULL; | |
| 5125 /* | |
| 5126 * Sort. | |
| 5127 */ | |
| 5128 xsltDoSortFunction(ctxt, sorts, nbsorts); | |
| 5129 ctxt->currentTemplateRule = oldCurTempRule; | |
| 5130 break; | |
| 5131 } | |
| 5132 cur = cur->next; | |
| 5133 } | |
| 5134 } | |
| 5135 xpctxt->contextSize = list->nodeNr; | |
| 5136 /* | |
| 5137 * Apply templates for all selected source nodes. | |
| 5138 */ | |
| 5139 for (i = 0; i < list->nodeNr; i++) { | |
| 5140 cur = list->nodeTab[i]; | |
| 5141 /* | |
| 5142 * The node becomes the "current node". | |
| 5143 */ | |
| 5144 ctxt->node = cur; | |
| 5145 /* | |
| 5146 * An xsl:apply-templates can change the current context doc. | |
| 5147 * OPTIMIZE TODO: Get rid of the need to set the context doc. | |
| 5148 */ | |
| 5149 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) | |
| 5150 xpctxt->doc = cur->doc; | |
| 5151 | |
| 5152 xpctxt->proximityPosition = i + 1; | |
| 5153 /* | |
| 5154 * Find and apply a template for this node. | |
| 5155 */ | |
| 5156 xsltProcessOneNode(ctxt, cur, withParams); | |
| 5157 } | |
| 5158 | |
| 5159 exit: | |
| 5160 error: | |
| 5161 /* | |
| 5162 * Free the parameter list. | |
| 5163 */ | |
| 5164 if (withParams != NULL) | |
| 5165 xsltFreeStackElemList(withParams); | |
| 5166 if (list != NULL) | |
| 5167 xmlXPathFreeNodeSet(list); | |
| 5168 /* | |
| 5169 * Restore context states. | |
| 5170 */ | |
| 5171 xpctxt->doc = oldXPDoc; | |
| 5172 xpctxt->contextSize = oldXPContextSize; | |
| 5173 xpctxt->proximityPosition = oldXPProximityPosition; | |
| 5174 | |
| 5175 ctxt->document = oldDocInfo; | |
| 5176 ctxt->nodeList = oldList; | |
| 5177 ctxt->node = oldContextNode; | |
| 5178 ctxt->mode = oldMode; | |
| 5179 ctxt->modeURI = oldModeURI; | |
| 5180 } | |
| 5181 | |
| 5182 | |
| 5183 /** | |
| 5184 * xsltChoose: | |
| 5185 * @ctxt: a XSLT process context | |
| 5186 * @contextNode: the current node in the source tree | |
| 5187 * @inst: the xsl:choose instruction | |
| 5188 * @comp: compiled information of the instruction | |
| 5189 * | |
| 5190 * Processes the xsl:choose instruction on the source node. | |
| 5191 */ | |
| 5192 void | |
| 5193 xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, | |
| 5194 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) | |
| 5195 { | |
| 5196 xmlNodePtr cur; | |
| 5197 | |
| 5198 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) | |
| 5199 return; | |
| 5200 | |
| 5201 /* | |
| 5202 * TODO: Content model checks should be done only at compilation | |
| 5203 * time. | |
| 5204 */ | |
| 5205 cur = inst->children; | |
| 5206 if (cur == NULL) { | |
| 5207 xsltTransformError(ctxt, NULL, inst, | |
| 5208 "xsl:choose: The instruction has no content.\n"); | |
| 5209 return; | |
| 5210 } | |
| 5211 | |
| 5212 #ifdef XSLT_REFACTORED | |
| 5213 /* | |
| 5214 * We don't check the content model during transformation. | |
| 5215 */ | |
| 5216 #else | |
| 5217 if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) { | |
| 5218 xsltTransformError(ctxt, NULL, inst, | |
| 5219 "xsl:choose: xsl:when expected first\n"); | |
| 5220 return; | |
| 5221 } | |
| 5222 #endif | |
| 5223 | |
| 5224 { | |
| 5225 int testRes = 0, res = 0; | |
| 5226 | |
| 5227 #ifdef XSLT_REFACTORED | |
| 5228 xsltStyleItemWhenPtr wcomp = NULL; | |
| 5229 #else | |
| 5230 xsltStylePreCompPtr wcomp = NULL; | |
| 5231 #endif | |
| 5232 | |
| 5233 /* | |
| 5234 * Process xsl:when --------------------------------------------------- | |
| 5235 */ | |
| 5236 while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) { | |
| 5237 wcomp = cur->psvi; | |
| 5238 | |
| 5239 if ((wcomp == NULL) || (wcomp->test == NULL) || | |
| 5240 (wcomp->comp == NULL)) | |
| 5241 { | |
| 5242 xsltTransformError(ctxt, NULL, cur, | |
| 5243 "Internal error in xsltChoose(): " | |
| 5244 "The XSLT 'when' instruction was not compiled.\n"); | |
| 5245 goto error; | |
| 5246 } | |
| 5247 | |
| 5248 | |
| 5249 #ifdef WITH_DEBUGGER | |
| 5250 if (xslDebugStatus != XSLT_DEBUG_NONE) { | |
| 5251 /* | |
| 5252 * TODO: Isn't comp->templ always NULL for xsl:choose? | |
| 5253 */ | |
| 5254 xslHandleDebugger(cur, contextNode, NULL, ctxt); | |
| 5255 } | |
| 5256 #endif | |
| 5257 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5258 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugC
ontext, | |
| 5259 "xsltChoose: test %s\n", wcomp->test)); | |
| 5260 #endif | |
| 5261 | |
| 5262 #ifdef XSLT_FAST_IF | |
| 5263 res = xsltPreCompEvalToBoolean(ctxt, contextNode, wcomp); | |
| 5264 | |
| 5265 if (res == -1) { | |
| 5266 ctxt->state = XSLT_STATE_STOPPED; | |
| 5267 goto error; | |
| 5268 } | |
| 5269 testRes = (res == 1) ? 1 : 0; | |
| 5270 | |
| 5271 #else /* XSLT_FAST_IF */ | |
| 5272 | |
| 5273 res = xsltPreCompEval(ctxt, cotextNode, wcomp); | |
| 5274 | |
| 5275 if (res != NULL) { | |
| 5276 if (res->type != XPATH_BOOLEAN) | |
| 5277 res = xmlXPathConvertBoolean(res); | |
| 5278 if (res->type == XPATH_BOOLEAN) | |
| 5279 testRes = res->boolval; | |
| 5280 else { | |
| 5281 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5282 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGener
icDebugContext, | |
| 5283 "xsltChoose: test didn't evaluate to a boolean\n")); | |
| 5284 #endif | |
| 5285 goto error; | |
| 5286 } | |
| 5287 xmlXPathFreeObject(res); | |
| 5288 res = NULL; | |
| 5289 } else { | |
| 5290 ctxt->state = XSLT_STATE_STOPPED; | |
| 5291 goto error; | |
| 5292 } | |
| 5293 | |
| 5294 #endif /* else of XSLT_FAST_IF */ | |
| 5295 | |
| 5296 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5297 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugC
ontext, | |
| 5298 "xsltChoose: test evaluate to %d\n", testRes)); | |
| 5299 #endif | |
| 5300 if (testRes) | |
| 5301 goto test_is_true; | |
| 5302 | |
| 5303 cur = cur->next; | |
| 5304 } | |
| 5305 | |
| 5306 /* | |
| 5307 * Process xsl:otherwise ---------------------------------------------- | |
| 5308 */ | |
| 5309 if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) { | |
| 5310 | |
| 5311 #ifdef WITH_DEBUGGER | |
| 5312 if (xslDebugStatus != XSLT_DEBUG_NONE) | |
| 5313 xslHandleDebugger(cur, contextNode, NULL, ctxt); | |
| 5314 #endif | |
| 5315 | |
| 5316 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5317 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugC
ontext, | |
| 5318 "evaluating xsl:otherwise\n")); | |
| 5319 #endif | |
| 5320 goto test_is_true; | |
| 5321 } | |
| 5322 goto exit; | |
| 5323 | |
| 5324 test_is_true: | |
| 5325 | |
| 5326 goto process_sequence; | |
| 5327 } | |
| 5328 | |
| 5329 process_sequence: | |
| 5330 | |
| 5331 /* | |
| 5332 * Instantiate the sequence constructor. | |
| 5333 */ | |
| 5334 xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children, | |
| 5335 NULL); | |
| 5336 | |
| 5337 exit: | |
| 5338 error: | |
| 5339 return; | |
| 5340 } | |
| 5341 | |
| 5342 /** | |
| 5343 * xsltIf: | |
| 5344 * @ctxt: a XSLT process context | |
| 5345 * @contextNode: the current node in the source tree | |
| 5346 * @inst: the xsl:if instruction | |
| 5347 * @castedComp: compiled information of the instruction | |
| 5348 * | |
| 5349 * Processes the xsl:if instruction on the source node. | |
| 5350 */ | |
| 5351 void | |
| 5352 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, | |
| 5353 xmlNodePtr inst, xsltStylePreCompPtr castedComp) | |
| 5354 { | |
| 5355 int res = 0; | |
| 5356 | |
| 5357 #ifdef XSLT_REFACTORED | |
| 5358 xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp; | |
| 5359 #else | |
| 5360 xsltStylePreCompPtr comp = castedComp; | |
| 5361 #endif | |
| 5362 | |
| 5363 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) | |
| 5364 return; | |
| 5365 if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) { | |
| 5366 xsltTransformError(ctxt, NULL, inst, | |
| 5367 "Internal error in xsltIf(): " | |
| 5368 "The XSLT 'if' instruction was not compiled.\n"); | |
| 5369 return; | |
| 5370 } | |
| 5371 | |
| 5372 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5373 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, | |
| 5374 "xsltIf: test %s\n", comp->test)); | |
| 5375 #endif | |
| 5376 | |
| 5377 #ifdef XSLT_FAST_IF | |
| 5378 { | |
| 5379 xmlDocPtr oldLocalFragmentTop = ctxt->localRVT; | |
| 5380 | |
| 5381 res = xsltPreCompEvalToBoolean(ctxt, contextNode, comp); | |
| 5382 | |
| 5383 /* | |
| 5384 * Cleanup fragments created during evaluation of the | |
| 5385 * "select" expression. | |
| 5386 */ | |
| 5387 if (oldLocalFragmentTop != ctxt->localRVT) | |
| 5388 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); | |
| 5389 } | |
| 5390 | |
| 5391 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5392 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, | |
| 5393 "xsltIf: test evaluate to %d\n", res)); | |
| 5394 #endif | |
| 5395 | |
| 5396 if (res == -1) { | |
| 5397 ctxt->state = XSLT_STATE_STOPPED; | |
| 5398 goto error; | |
| 5399 } | |
| 5400 if (res == 1) { | |
| 5401 /* | |
| 5402 * Instantiate the sequence constructor of xsl:if. | |
| 5403 */ | |
| 5404 xsltApplySequenceConstructor(ctxt, | |
| 5405 contextNode, inst->children, NULL); | |
| 5406 } | |
| 5407 | |
| 5408 #else /* XSLT_FAST_IF */ | |
| 5409 { | |
| 5410 /* | |
| 5411 * OLD CODE: | |
| 5412 */ | |
| 5413 xmlXPathObjectPtr xpobj = xsltPreCompEval(ctxt, contextNode, comp); | |
| 5414 if (xpobj != NULL) { | |
| 5415 if (xpobj->type != XPATH_BOOLEAN) | |
| 5416 xpobj = xmlXPathConvertBoolean(xpobj); | |
| 5417 if (xpobj->type == XPATH_BOOLEAN) { | |
| 5418 res = xpobj->boolval; | |
| 5419 | |
| 5420 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5421 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugC
ontext, | |
| 5422 "xsltIf: test evaluate to %d\n", res)); | |
| 5423 #endif | |
| 5424 if (res) { | |
| 5425 xsltApplySequenceConstructor(ctxt, | |
| 5426 contextNode, inst->children, NULL); | |
| 5427 } | |
| 5428 } else { | |
| 5429 | |
| 5430 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5431 XSLT_TRACE(ctxt, XSLT_TRACE_IF, | |
| 5432 xsltGenericDebug(xsltGenericDebugContext, | |
| 5433 "xsltIf: test didn't evaluate to a boolean\n")); | |
| 5434 #endif | |
| 5435 ctxt->state = XSLT_STATE_STOPPED; | |
| 5436 } | |
| 5437 xmlXPathFreeObject(xpobj); | |
| 5438 } else { | |
| 5439 ctxt->state = XSLT_STATE_STOPPED; | |
| 5440 } | |
| 5441 } | |
| 5442 #endif /* else of XSLT_FAST_IF */ | |
| 5443 | |
| 5444 error: | |
| 5445 return; | |
| 5446 } | |
| 5447 | |
| 5448 /** | |
| 5449 * xsltForEach: | |
| 5450 * @ctxt: an XSLT transformation context | |
| 5451 * @contextNode: the "current node" in the source tree | |
| 5452 * @inst: the element node of the xsl:for-each instruction | |
| 5453 * @castedComp: the compiled information of the instruction | |
| 5454 * | |
| 5455 * Process the xslt for-each node on the source node | |
| 5456 */ | |
| 5457 void | |
| 5458 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, | |
| 5459 xmlNodePtr inst, xsltStylePreCompPtr castedComp) | |
| 5460 { | |
| 5461 #ifdef XSLT_REFACTORED | |
| 5462 xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp; | |
| 5463 #else | |
| 5464 xsltStylePreCompPtr comp = castedComp; | |
| 5465 #endif | |
| 5466 int i; | |
| 5467 xmlXPathObjectPtr res = NULL; | |
| 5468 xmlNodePtr cur, curInst; | |
| 5469 xmlNodeSetPtr list = NULL; | |
| 5470 xmlNodeSetPtr oldList; | |
| 5471 int oldXPProximityPosition, oldXPContextSize; | |
| 5472 xmlNodePtr oldContextNode; | |
| 5473 xsltTemplatePtr oldCurTemplRule; | |
| 5474 xmlDocPtr oldXPDoc; | |
| 5475 xsltDocumentPtr oldDocInfo; | |
| 5476 xmlXPathContextPtr xpctxt; | |
| 5477 | |
| 5478 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) { | |
| 5479 xsltGenericError(xsltGenericErrorContext, | |
| 5480 "xsltForEach(): Bad arguments.\n"); | |
| 5481 return; | |
| 5482 } | |
| 5483 | |
| 5484 if (comp == NULL) { | |
| 5485 xsltTransformError(ctxt, NULL, inst, | |
| 5486 "Internal error in xsltForEach(): " | |
| 5487 "The XSLT 'for-each' instruction was not compiled.\n"); | |
| 5488 return; | |
| 5489 } | |
| 5490 if ((comp->select == NULL) || (comp->comp == NULL)) { | |
| 5491 xsltTransformError(ctxt, NULL, inst, | |
| 5492 "Internal error in xsltForEach(): " | |
| 5493 "The selecting expression of the XSLT 'for-each' " | |
| 5494 "instruction was not compiled correctly.\n"); | |
| 5495 return; | |
| 5496 } | |
| 5497 xpctxt = ctxt->xpathCtxt; | |
| 5498 | |
| 5499 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5500 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext
, | |
| 5501 "xsltForEach: select %s\n", comp->select)); | |
| 5502 #endif | |
| 5503 | |
| 5504 /* | |
| 5505 * Save context states. | |
| 5506 */ | |
| 5507 oldDocInfo = ctxt->document; | |
| 5508 oldList = ctxt->nodeList; | |
| 5509 oldContextNode = ctxt->node; | |
| 5510 /* | |
| 5511 * The "current template rule" is cleared for the instantiation of | |
| 5512 * xsl:for-each. | |
| 5513 */ | |
| 5514 oldCurTemplRule = ctxt->currentTemplateRule; | |
| 5515 ctxt->currentTemplateRule = NULL; | |
| 5516 | |
| 5517 oldXPDoc = xpctxt->doc; | |
| 5518 oldXPProximityPosition = xpctxt->proximityPosition; | |
| 5519 oldXPContextSize = xpctxt->contextSize; | |
| 5520 | |
| 5521 /* | |
| 5522 * Evaluate the 'select' expression. | |
| 5523 */ | |
| 5524 res = xsltPreCompEval(ctxt, contextNode, comp); | |
| 5525 | |
| 5526 if (res != NULL) { | |
| 5527 if (res->type == XPATH_NODESET) | |
| 5528 list = res->nodesetval; | |
| 5529 else { | |
| 5530 xsltTransformError(ctxt, NULL, inst, | |
| 5531 "The 'select' expression does not evaluate to a node set.\n"); | |
| 5532 | |
| 5533 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5534 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebu
gContext, | |
| 5535 "xsltForEach: select didn't evaluate to a node list\n")); | |
| 5536 #endif | |
| 5537 goto error; | |
| 5538 } | |
| 5539 } else { | |
| 5540 xsltTransformError(ctxt, NULL, inst, | |
| 5541 "Failed to evaluate the 'select' expression.\n"); | |
| 5542 ctxt->state = XSLT_STATE_STOPPED; | |
| 5543 goto error; | |
| 5544 } | |
| 5545 | |
| 5546 if ((list == NULL) || (list->nodeNr <= 0)) | |
| 5547 goto exit; | |
| 5548 | |
| 5549 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5550 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext
, | |
| 5551 "xsltForEach: select evaluates to %d nodes\n", list->nodeNr)); | |
| 5552 #endif | |
| 5553 | |
| 5554 /* | |
| 5555 * Set the list; this has to be done already here for xsltDoSortFunction(). | |
| 5556 */ | |
| 5557 ctxt->nodeList = list; | |
| 5558 /* | |
| 5559 * Handle xsl:sort instructions and skip them for further processing. | |
| 5560 * BUG TODO: We are not using namespaced potentially defined on the | |
| 5561 * xsl:sort element; XPath expression might fail. | |
| 5562 */ | |
| 5563 curInst = inst->children; | |
| 5564 if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { | |
| 5565 int nbsorts = 0; | |
| 5566 xmlNodePtr sorts[XSLT_MAX_SORT]; | |
| 5567 | |
| 5568 sorts[nbsorts++] = curInst; | |
| 5569 | |
| 5570 #ifdef WITH_DEBUGGER | |
| 5571 if (xslDebugStatus != XSLT_DEBUG_NONE) | |
| 5572 xslHandleDebugger(curInst, contextNode, NULL, ctxt); | |
| 5573 #endif | |
| 5574 | |
| 5575 curInst = curInst->next; | |
| 5576 while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { | |
| 5577 if (nbsorts >= XSLT_MAX_SORT) { | |
| 5578 xsltTransformError(ctxt, NULL, curInst, | |
| 5579 "The number of xsl:sort instructions exceeds the " | |
| 5580 "maximum (%d) allowed by this processor.\n", | |
| 5581 XSLT_MAX_SORT); | |
| 5582 goto error; | |
| 5583 } else { | |
| 5584 sorts[nbsorts++] = curInst; | |
| 5585 } | |
| 5586 | |
| 5587 #ifdef WITH_DEBUGGER | |
| 5588 if (xslDebugStatus != XSLT_DEBUG_NONE) | |
| 5589 xslHandleDebugger(curInst, contextNode, NULL, ctxt); | |
| 5590 #endif | |
| 5591 curInst = curInst->next; | |
| 5592 } | |
| 5593 xsltDoSortFunction(ctxt, sorts, nbsorts); | |
| 5594 } | |
| 5595 xpctxt->contextSize = list->nodeNr; | |
| 5596 /* | |
| 5597 * Instantiate the sequence constructor for each selected node. | |
| 5598 */ | |
| 5599 for (i = 0; i < list->nodeNr; i++) { | |
| 5600 cur = list->nodeTab[i]; | |
| 5601 /* | |
| 5602 * The selected node becomes the "current node". | |
| 5603 */ | |
| 5604 ctxt->node = cur; | |
| 5605 /* | |
| 5606 * An xsl:for-each can change the current context doc. | |
| 5607 * OPTIMIZE TODO: Get rid of the need to set the context doc. | |
| 5608 */ | |
| 5609 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) | |
| 5610 xpctxt->doc = cur->doc; | |
| 5611 | |
| 5612 xpctxt->proximityPosition = i + 1; | |
| 5613 | |
| 5614 xsltApplySequenceConstructor(ctxt, cur, curInst, NULL); | |
| 5615 } | |
| 5616 | |
| 5617 exit: | |
| 5618 error: | |
| 5619 if (res != NULL) | |
| 5620 xmlXPathFreeObject(res); | |
| 5621 /* | |
| 5622 * Restore old states. | |
| 5623 */ | |
| 5624 ctxt->document = oldDocInfo; | |
| 5625 ctxt->nodeList = oldList; | |
| 5626 ctxt->node = oldContextNode; | |
| 5627 ctxt->currentTemplateRule = oldCurTemplRule; | |
| 5628 | |
| 5629 xpctxt->doc = oldXPDoc; | |
| 5630 xpctxt->contextSize = oldXPContextSize; | |
| 5631 xpctxt->proximityPosition = oldXPProximityPosition; | |
| 5632 } | |
| 5633 | |
| 5634 /************************************************************************ | |
| 5635 * * | |
| 5636 * Generic interface * | |
| 5637 * * | |
| 5638 ************************************************************************/ | |
| 5639 | |
| 5640 #ifdef XSLT_GENERATE_HTML_DOCTYPE | |
| 5641 typedef struct xsltHTMLVersion { | |
| 5642 const char *version; | |
| 5643 const char *public; | |
| 5644 const char *system; | |
| 5645 } xsltHTMLVersion; | |
| 5646 | |
| 5647 static xsltHTMLVersion xsltHTMLVersions[] = { | |
| 5648 { "5", NULL, "about:legacy-compat" }, | |
| 5649 { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN", | |
| 5650 "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"}, | |
| 5651 { "4.01strict", "-//W3C//DTD HTML 4.01//EN", | |
| 5652 "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"}, | |
| 5653 { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN", | |
| 5654 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, | |
| 5655 { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN", | |
| 5656 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, | |
| 5657 { "4.0strict", "-//W3C//DTD HTML 4.01//EN", | |
| 5658 "http://www.w3.org/TR/html4/strict.dtd"}, | |
| 5659 { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN", | |
| 5660 "http://www.w3.org/TR/html4/loose.dtd"}, | |
| 5661 { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN", | |
| 5662 "http://www.w3.org/TR/html4/frameset.dtd"}, | |
| 5663 { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN", | |
| 5664 "http://www.w3.org/TR/html4/loose.dtd"}, | |
| 5665 { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL } | |
| 5666 }; | |
| 5667 | |
| 5668 /** | |
| 5669 * xsltGetHTMLIDs: | |
| 5670 * @version: the version string | |
| 5671 * @publicID: used to return the public ID | |
| 5672 * @systemID: used to return the system ID | |
| 5673 * | |
| 5674 * Returns -1 if not found, 0 otherwise and the system and public | |
| 5675 * Identifier for this given verion of HTML | |
| 5676 */ | |
| 5677 static int | |
| 5678 xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, | |
| 5679 const xmlChar **systemID) { | |
| 5680 unsigned int i; | |
| 5681 if (version == NULL) | |
| 5682 return(-1); | |
| 5683 for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1])); | |
| 5684 i++) { | |
| 5685 if (!xmlStrcasecmp(version, | |
| 5686 (const xmlChar *) xsltHTMLVersions[i].version)) { | |
| 5687 if (publicID != NULL) | |
| 5688 *publicID = (const xmlChar *) xsltHTMLVersions[i].public; | |
| 5689 if (systemID != NULL) | |
| 5690 *systemID = (const xmlChar *) xsltHTMLVersions[i].system; | |
| 5691 return(0); | |
| 5692 } | |
| 5693 } | |
| 5694 return(-1); | |
| 5695 } | |
| 5696 #endif | |
| 5697 | |
| 5698 /** | |
| 5699 * xsltApplyStripSpaces: | |
| 5700 * @ctxt: a XSLT process context | |
| 5701 * @node: the root of the XML tree | |
| 5702 * | |
| 5703 * Strip the unwanted ignorable spaces from the input tree | |
| 5704 */ | |
| 5705 void | |
| 5706 xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) { | |
| 5707 xmlNodePtr current; | |
| 5708 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5709 int nb = 0; | |
| 5710 #endif | |
| 5711 | |
| 5712 | |
| 5713 current = node; | |
| 5714 while (current != NULL) { | |
| 5715 /* | |
| 5716 * Cleanup children empty nodes if asked for | |
| 5717 */ | |
| 5718 if ((IS_XSLT_REAL_NODE(current)) && | |
| 5719 (current->children != NULL) && | |
| 5720 (xsltFindElemSpaceHandling(ctxt, current))) { | |
| 5721 xmlNodePtr delete = NULL, cur = current->children; | |
| 5722 | |
| 5723 while (cur != NULL) { | |
| 5724 if (IS_BLANK_NODE(cur)) | |
| 5725 delete = cur; | |
| 5726 | |
| 5727 cur = cur->next; | |
| 5728 if (delete != NULL) { | |
| 5729 xmlUnlinkNode(delete); | |
| 5730 xmlFreeNode(delete); | |
| 5731 delete = NULL; | |
| 5732 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5733 nb++; | |
| 5734 #endif | |
| 5735 } | |
| 5736 } | |
| 5737 } | |
| 5738 | |
| 5739 /* | |
| 5740 * Skip to next node in document order. | |
| 5741 */ | |
| 5742 if (node->type == XML_ENTITY_REF_NODE) { | |
| 5743 /* process deep in entities */ | |
| 5744 xsltApplyStripSpaces(ctxt, node->children); | |
| 5745 } | |
| 5746 if ((current->children != NULL) && | |
| 5747 (current->type != XML_ENTITY_REF_NODE)) { | |
| 5748 current = current->children; | |
| 5749 } else if (current->next != NULL) { | |
| 5750 current = current->next; | |
| 5751 } else { | |
| 5752 do { | |
| 5753 current = current->parent; | |
| 5754 if (current == NULL) | |
| 5755 break; | |
| 5756 if (current == node) | |
| 5757 goto done; | |
| 5758 if (current->next != NULL) { | |
| 5759 current = current->next; | |
| 5760 break; | |
| 5761 } | |
| 5762 } while (current != NULL); | |
| 5763 } | |
| 5764 } | |
| 5765 | |
| 5766 done: | |
| 5767 #ifdef WITH_XSLT_DEBUG_PROCESS | |
| 5768 XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugCon
text, | |
| 5769 "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb)); | |
| 5770 #endif | |
| 5771 return; | |
| 5772 } | |
| 5773 | |
| 5774 static int | |
| 5775 xsltCountKeys(xsltTransformContextPtr ctxt) | |
| 5776 { | |
| 5777 xsltStylesheetPtr style; | |
| 5778 xsltKeyDefPtr keyd; | |
| 5779 | |
| 5780 if (ctxt == NULL) | |
| 5781 return(-1); | |
| 5782 | |
| 5783 /* | |
| 5784 * Do we have those nastly templates with a key() in the match pattern? | |
| 5785 */ | |
| 5786 ctxt->hasTemplKeyPatterns = 0; | |
| 5787 style = ctxt->style; | |
| 5788 while (style != NULL) { | |
| 5789 if (style->keyMatch != NULL) { | |
| 5790 ctxt->hasTemplKeyPatterns = 1; | |
| 5791 break; | |
| 5792 } | |
| 5793 style = xsltNextImport(style); | |
| 5794 } | |
| 5795 /* | |
| 5796 * Count number of key declarations. | |
| 5797 */ | |
| 5798 ctxt->nbKeys = 0; | |
| 5799 style = ctxt->style; | |
| 5800 while (style != NULL) { | |
| 5801 keyd = style->keys; | |
| 5802 while (keyd) { | |
| 5803 ctxt->nbKeys++; | |
| 5804 keyd = keyd->next; | |
| 5805 } | |
| 5806 style = xsltNextImport(style); | |
| 5807 } | |
| 5808 return(ctxt->nbKeys); | |
| 5809 } | |
| 5810 | |
| 5811 /** | |
| 5812 * xsltApplyStylesheetInternal: | |
| 5813 * @style: a parsed XSLT stylesheet | |
| 5814 * @doc: a parsed XML document | |
| 5815 * @params: a NULL terminated array of parameters names/values tuples | |
| 5816 * @output: the targetted output | |
| 5817 * @profile: profile FILE * output or NULL | |
| 5818 * @user: user provided parameter | |
| 5819 * | |
| 5820 * Apply the stylesheet to the document | |
| 5821 * NOTE: This may lead to a non-wellformed output XML wise ! | |
| 5822 * | |
| 5823 * Returns the result document or NULL in case of error | |
| 5824 */ | |
| 5825 static xmlDocPtr | |
| 5826 xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc, | |
| 5827 const char **params, const char *output, | |
| 5828 FILE * profile, xsltTransformContextPtr userCtxt) | |
| 5829 { | |
| 5830 xmlDocPtr res = NULL; | |
| 5831 xsltTransformContextPtr ctxt = NULL; | |
| 5832 xmlNodePtr root, node; | |
| 5833 const xmlChar *method; | |
| 5834 const xmlChar *doctypePublic; | |
| 5835 const xmlChar *doctypeSystem; | |
| 5836 const xmlChar *version; | |
| 5837 const xmlChar *encoding; | |
| 5838 xsltStackElemPtr variables; | |
| 5839 xsltStackElemPtr vptr; | |
| 5840 | |
| 5841 xsltInitGlobals(); | |
| 5842 | |
| 5843 if ((style == NULL) || (doc == NULL)) | |
| 5844 return (NULL); | |
| 5845 | |
| 5846 if (style->internalized == 0) { | |
| 5847 #ifdef WITH_XSLT_DEBUG | |
| 5848 xsltGenericDebug(xsltGenericDebugContext, | |
| 5849 "Stylesheet was not fully internalized !\n"); | |
| 5850 #endif | |
| 5851 } | |
| 5852 if (doc->intSubset != NULL) { | |
| 5853 /* | |
| 5854 * Avoid hitting the DTD when scanning nodes | |
| 5855 * but keep it linked as doc->intSubset | |
| 5856 */ | |
| 5857 xmlNodePtr cur = (xmlNodePtr) doc->intSubset; | |
| 5858 if (cur->next != NULL) | |
| 5859 cur->next->prev = cur->prev; | |
| 5860 if (cur->prev != NULL) | |
| 5861 cur->prev->next = cur->next; | |
| 5862 if (doc->children == cur) | |
| 5863 doc->children = cur->next; | |
| 5864 if (doc->last == cur) | |
| 5865 doc->last = cur->prev; | |
| 5866 cur->prev = cur->next = NULL; | |
| 5867 } | |
| 5868 | |
| 5869 /* | |
| 5870 * Check for XPath document order availability | |
| 5871 */ | |
| 5872 root = xmlDocGetRootElement(doc); | |
| 5873 if (root != NULL) { | |
| 5874 if (((long) root->content) >= 0 && (xslDebugStatus == XSLT_DEBUG_NONE)) | |
| 5875 xmlXPathOrderDocElems(doc); | |
| 5876 } | |
| 5877 | |
| 5878 if (userCtxt != NULL) | |
| 5879 ctxt = userCtxt; | |
| 5880 else | |
| 5881 ctxt = xsltNewTransformContext(style, doc); | |
| 5882 | |
| 5883 if (ctxt == NULL) | |
| 5884 return (NULL); | |
| 5885 | |
| 5886 ctxt->initialContextDoc = doc; | |
| 5887 ctxt->initialContextNode = (xmlNodePtr) doc; | |
| 5888 | |
| 5889 if (profile != NULL) | |
| 5890 ctxt->profile = 1; | |
| 5891 | |
| 5892 if (output != NULL) | |
| 5893 ctxt->outputFile = output; | |
| 5894 else | |
| 5895 ctxt->outputFile = NULL; | |
| 5896 | |
| 5897 /* | |
| 5898 * internalize the modes if needed | |
| 5899 */ | |
| 5900 if (ctxt->dict != NULL) { | |
| 5901 if (ctxt->mode != NULL) | |
| 5902 ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1); | |
| 5903 if (ctxt->modeURI != NULL) | |
| 5904 ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1); | |
| 5905 } | |
| 5906 | |
| 5907 XSLT_GET_IMPORT_PTR(method, style, method) | |
| 5908 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) | |
| 5909 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) | |
| 5910 XSLT_GET_IMPORT_PTR(version, style, version) | |
| 5911 XSLT_GET_IMPORT_PTR(encoding, style, encoding) | |
| 5912 | |
| 5913 if ((method != NULL) && | |
| 5914 (!xmlStrEqual(method, (const xmlChar *) "xml"))) | |
| 5915 { | |
| 5916 if (xmlStrEqual(method, (const xmlChar *) "html")) { | |
| 5917 ctxt->type = XSLT_OUTPUT_HTML; | |
| 5918 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { | |
| 5919 res = htmlNewDoc(doctypeSystem, doctypePublic); | |
| 5920 } else { | |
| 5921 if (version == NULL) { | |
| 5922 xmlDtdPtr dtd; | |
| 5923 | |
| 5924 res = htmlNewDoc(NULL, NULL); | |
| 5925 /* | |
| 5926 * Make sure no DTD node is generated in this case | |
| 5927 */ | |
| 5928 if (res != NULL) { | |
| 5929 dtd = xmlGetIntSubset(res); | |
| 5930 if (dtd != NULL) { | |
| 5931 xmlUnlinkNode((xmlNodePtr) dtd); | |
| 5932 xmlFreeDtd(dtd); | |
| 5933 } | |
| 5934 res->intSubset = NULL; | |
| 5935 res->extSubset = NULL; | |
| 5936 } | |
| 5937 } else { | |
| 5938 | |
| 5939 #ifdef XSLT_GENERATE_HTML_DOCTYPE | |
| 5940 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); | |
| 5941 #endif | |
| 5942 res = htmlNewDoc(doctypeSystem, doctypePublic); | |
| 5943 } | |
| 5944 } | |
| 5945 if (res == NULL) | |
| 5946 goto error; | |
| 5947 res->dict = ctxt->dict; | |
| 5948 xmlDictReference(res->dict); | |
| 5949 | |
| 5950 #ifdef WITH_XSLT_DEBUG | |
| 5951 xsltGenericDebug(xsltGenericDebugContext, | |
| 5952 "reusing transformation dict for output\n"); | |
| 5953 #endif | |
| 5954 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { | |
| 5955 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, | |
| 5956 "xsltApplyStylesheetInternal: unsupported method xhtml, using ht
ml\n"); | |
| 5957 ctxt->type = XSLT_OUTPUT_HTML; | |
| 5958 res = htmlNewDoc(doctypeSystem, doctypePublic); | |
| 5959 if (res == NULL) | |
| 5960 goto error; | |
| 5961 res->dict = ctxt->dict; | |
| 5962 xmlDictReference(res->dict); | |
| 5963 | |
| 5964 #ifdef WITH_XSLT_DEBUG | |
| 5965 xsltGenericDebug(xsltGenericDebugContext, | |
| 5966 "reusing transformation dict for output\n"); | |
| 5967 #endif | |
| 5968 } else if (xmlStrEqual(method, (const xmlChar *) "text")) { | |
| 5969 ctxt->type = XSLT_OUTPUT_TEXT; | |
| 5970 res = xmlNewDoc(style->version); | |
| 5971 if (res == NULL) | |
| 5972 goto error; | |
| 5973 res->dict = ctxt->dict; | |
| 5974 xmlDictReference(res->dict); | |
| 5975 | |
| 5976 #ifdef WITH_XSLT_DEBUG | |
| 5977 xsltGenericDebug(xsltGenericDebugContext, | |
| 5978 "reusing transformation dict for output\n"); | |
| 5979 #endif | |
| 5980 } else { | |
| 5981 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, | |
| 5982 "xsltApplyStylesheetInternal: unsupported method (%s)\n", | |
| 5983 method); | |
| 5984 goto error; | |
| 5985 } | |
| 5986 } else { | |
| 5987 ctxt->type = XSLT_OUTPUT_XML; | |
| 5988 res = xmlNewDoc(style->version); | |
| 5989 if (res == NULL) | |
| 5990 goto error; | |
| 5991 res->dict = ctxt->dict; | |
| 5992 xmlDictReference(ctxt->dict); | |
| 5993 #ifdef WITH_XSLT_DEBUG | |
| 5994 xsltGenericDebug(xsltGenericDebugContext, | |
| 5995 "reusing transformation dict for output\n"); | |
| 5996 #endif | |
| 5997 } | |
| 5998 res->charset = XML_CHAR_ENCODING_UTF8; | |
| 5999 if (encoding != NULL) | |
| 6000 res->encoding = xmlStrdup(encoding); | |
| 6001 variables = style->variables; | |
| 6002 | |
| 6003 /* | |
| 6004 * Start the evaluation, evaluate the params, the stylesheets globals | |
| 6005 * and start by processing the top node. | |
| 6006 */ | |
| 6007 if (xsltNeedElemSpaceHandling(ctxt)) | |
| 6008 xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc)); | |
| 6009 /* | |
| 6010 * Evaluate global params and user-provided params. | |
| 6011 */ | |
| 6012 ctxt->node = (xmlNodePtr) doc; | |
| 6013 if (ctxt->globalVars == NULL) | |
| 6014 ctxt->globalVars = xmlHashCreate(20); | |
| 6015 if (params != NULL) { | |
| 6016 xsltEvalUserParams(ctxt, params); | |
| 6017 } | |
| 6018 | |
| 6019 /* need to be called before evaluating global variables */ | |
| 6020 xsltCountKeys(ctxt); | |
| 6021 | |
| 6022 xsltEvalGlobalVariables(ctxt); | |
| 6023 | |
| 6024 /* Clean up any unused RVTs. */ | |
| 6025 xsltReleaseLocalRVTs(ctxt, NULL); | |
| 6026 | |
| 6027 ctxt->node = (xmlNodePtr) doc; | |
| 6028 ctxt->output = res; | |
| 6029 ctxt->insert = (xmlNodePtr) res; | |
| 6030 ctxt->varsBase = ctxt->varsNr - 1; | |
| 6031 | |
| 6032 ctxt->xpathCtxt->contextSize = 1; | |
| 6033 ctxt->xpathCtxt->proximityPosition = 1; | |
| 6034 ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */ | |
| 6035 /* | |
| 6036 * Start processing the source tree ----------------------------------- | |
| 6037 */ | |
| 6038 xsltProcessOneNode(ctxt, ctxt->node, NULL); | |
| 6039 /* | |
| 6040 * Remove all remaining vars from the stack. | |
| 6041 */ | |
| 6042 xsltLocalVariablePop(ctxt, 0, -2); | |
| 6043 xsltShutdownCtxtExts(ctxt); | |
| 6044 | |
| 6045 xsltCleanupTemplates(style); /* TODO: <- style should be read only */ | |
| 6046 | |
| 6047 /* | |
| 6048 * Now cleanup our variables so stylesheet can be re-used | |
| 6049 * | |
| 6050 * TODO: this is not needed anymore global variables are copied | |
| 6051 * and not evaluated directly anymore, keep this as a check | |
| 6052 */ | |
| 6053 if (style->variables != variables) { | |
| 6054 vptr = style->variables; | |
| 6055 while (vptr->next != variables) | |
| 6056 vptr = vptr->next; | |
| 6057 vptr->next = NULL; | |
| 6058 xsltFreeStackElemList(style->variables); | |
| 6059 style->variables = variables; | |
| 6060 } | |
| 6061 vptr = style->variables; | |
| 6062 while (vptr != NULL) { | |
| 6063 if (vptr->computed) { | |
| 6064 if (vptr->value != NULL) { | |
| 6065 xmlXPathFreeObject(vptr->value); | |
| 6066 vptr->value = NULL; | |
| 6067 vptr->computed = 0; | |
| 6068 } | |
| 6069 } | |
| 6070 vptr = vptr->next; | |
| 6071 } | |
| 6072 #if 0 | |
| 6073 /* | |
| 6074 * code disabled by wmb; awaiting kb's review | |
| 6075 * problem is that global variable(s) may contain xpath objects | |
| 6076 * from doc associated with RVT, so can't be freed at this point. | |
| 6077 * xsltFreeTransformContext includes a call to xsltFreeRVTs, so | |
| 6078 * I assume this shouldn't be required at this point. | |
| 6079 */ | |
| 6080 /* | |
| 6081 * Free all remaining tree fragments. | |
| 6082 */ | |
| 6083 xsltFreeRVTs(ctxt); | |
| 6084 #endif | |
| 6085 /* | |
| 6086 * Do some post processing work depending on the generated output | |
| 6087 */ | |
| 6088 root = xmlDocGetRootElement(res); | |
| 6089 if (root != NULL) { | |
| 6090 const xmlChar *doctype = NULL; | |
| 6091 | |
| 6092 if ((root->ns != NULL) && (root->ns->prefix != NULL)) | |
| 6093 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); | |
| 6094 if (doctype == NULL) | |
| 6095 doctype = root->name; | |
| 6096 | |
| 6097 /* | |
| 6098 * Apply the default selection of the method | |
| 6099 */ | |
| 6100 if ((method == NULL) && | |
| 6101 (root->ns == NULL) && | |
| 6102 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { | |
| 6103 xmlNodePtr tmp; | |
| 6104 | |
| 6105 tmp = res->children; | |
| 6106 while ((tmp != NULL) && (tmp != root)) { | |
| 6107 if (tmp->type == XML_ELEMENT_NODE) | |
| 6108 break; | |
| 6109 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) | |
| 6110 break; | |
| 6111 tmp = tmp->next; | |
| 6112 } | |
| 6113 if (tmp == root) { | |
| 6114 ctxt->type = XSLT_OUTPUT_HTML; | |
| 6115 /* | |
| 6116 * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the | |
| 6117 * transformation on the doc, but functions like | |
| 6118 */ | |
| 6119 res->type = XML_HTML_DOCUMENT_NODE; | |
| 6120 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { | |
| 6121 res->intSubset = xmlCreateIntSubset(res, doctype, | |
| 6122 doctypePublic, | |
| 6123 doctypeSystem); | |
| 6124 #ifdef XSLT_GENERATE_HTML_DOCTYPE | |
| 6125 } else if (version != NULL) { | |
| 6126 xsltGetHTMLIDs(version, &doctypePublic, | |
| 6127 &doctypeSystem); | |
| 6128 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) | |
| 6129 res->intSubset = | |
| 6130 xmlCreateIntSubset(res, doctype, | |
| 6131 doctypePublic, | |
| 6132 doctypeSystem); | |
| 6133 #endif | |
| 6134 } | |
| 6135 } | |
| 6136 | |
| 6137 } | |
| 6138 if (ctxt->type == XSLT_OUTPUT_XML) { | |
| 6139 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) | |
| 6140 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) | |
| 6141 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { | |
| 6142 xmlNodePtr last; | |
| 6143 /* Need a small "hack" here to assure DTD comes before | |
| 6144 possible comment nodes */ | |
| 6145 node = res->children; | |
| 6146 last = res->last; | |
| 6147 res->children = NULL; | |
| 6148 res->last = NULL; | |
| 6149 res->intSubset = xmlCreateIntSubset(res, doctype, | |
| 6150 doctypePublic, | |
| 6151 doctypeSystem); | |
| 6152 if (res->children != NULL) { | |
| 6153 res->children->next = node; | |
| 6154 node->prev = res->children; | |
| 6155 res->last = last; | |
| 6156 } else { | |
| 6157 res->children = node; | |
| 6158 res->last = last; | |
| 6159 } | |
| 6160 } | |
| 6161 } | |
| 6162 } | |
| 6163 xmlXPathFreeNodeSet(ctxt->nodeList); | |
| 6164 if (profile != NULL) { | |
| 6165 xsltSaveProfiling(ctxt, profile); | |
| 6166 } | |
| 6167 | |
| 6168 /* | |
| 6169 * Be pedantic. | |
| 6170 */ | |
| 6171 if ((ctxt != NULL) && (ctxt->state != XSLT_STATE_OK)) { | |
| 6172 xmlFreeDoc(res); | |
| 6173 res = NULL; | |
| 6174 } | |
| 6175 if ((res != NULL) && (ctxt != NULL) && (output != NULL)) { | |
| 6176 int ret; | |
| 6177 | |
| 6178 ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output); | |
| 6179 if (ret == 0) { | |
| 6180 xsltTransformError(ctxt, NULL, NULL, | |
| 6181 "xsltApplyStylesheet: forbidden to save to %s\n", | |
| 6182 output); | |
| 6183 } else if (ret < 0) { | |
| 6184 xsltTransformError(ctxt, NULL, NULL, | |
| 6185 "xsltApplyStylesheet: saving to %s may not be possible\n", | |
| 6186 output); | |
| 6187 } | |
| 6188 } | |
| 6189 | |
| 6190 #ifdef XSLT_DEBUG_PROFILE_CACHE | |
| 6191 printf("# Cache:\n"); | |
| 6192 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); | |
| 6193 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); | |
| 6194 #endif | |
| 6195 | |
| 6196 if ((ctxt != NULL) && (userCtxt == NULL)) | |
| 6197 xsltFreeTransformContext(ctxt); | |
| 6198 | |
| 6199 return (res); | |
| 6200 | |
| 6201 error: | |
| 6202 if (res != NULL) | |
| 6203 xmlFreeDoc(res); | |
| 6204 | |
| 6205 #ifdef XSLT_DEBUG_PROFILE_CACHE | |
| 6206 printf("# Cache:\n"); | |
| 6207 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); | |
| 6208 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); | |
| 6209 #endif | |
| 6210 | |
| 6211 if ((ctxt != NULL) && (userCtxt == NULL)) | |
| 6212 xsltFreeTransformContext(ctxt); | |
| 6213 return (NULL); | |
| 6214 } | |
| 6215 | |
| 6216 /** | |
| 6217 * xsltApplyStylesheet: | |
| 6218 * @style: a parsed XSLT stylesheet | |
| 6219 * @doc: a parsed XML document | |
| 6220 * @params: a NULL terminated arry of parameters names/values tuples | |
| 6221 * | |
| 6222 * Apply the stylesheet to the document | |
| 6223 * NOTE: This may lead to a non-wellformed output XML wise ! | |
| 6224 * | |
| 6225 * Returns the result document or NULL in case of error | |
| 6226 */ | |
| 6227 xmlDocPtr | |
| 6228 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, | |
| 6229 const char **params) | |
| 6230 { | |
| 6231 return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL)); | |
| 6232 } | |
| 6233 | |
| 6234 /** | |
| 6235 * xsltProfileStylesheet: | |
| 6236 * @style: a parsed XSLT stylesheet | |
| 6237 * @doc: a parsed XML document | |
| 6238 * @params: a NULL terminated arry of parameters names/values tuples | |
| 6239 * @output: a FILE * for the profiling output | |
| 6240 * | |
| 6241 * Apply the stylesheet to the document and dump the profiling to | |
| 6242 * the given output. | |
| 6243 * | |
| 6244 * Returns the result document or NULL in case of error | |
| 6245 */ | |
| 6246 xmlDocPtr | |
| 6247 xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, | |
| 6248 const char **params, FILE * output) | |
| 6249 { | |
| 6250 xmlDocPtr res; | |
| 6251 | |
| 6252 res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL); | |
| 6253 return (res); | |
| 6254 } | |
| 6255 | |
| 6256 /** | |
| 6257 * xsltApplyStylesheetUser: | |
| 6258 * @style: a parsed XSLT stylesheet | |
| 6259 * @doc: a parsed XML document | |
| 6260 * @params: a NULL terminated array of parameters names/values tuples | |
| 6261 * @output: the targetted output | |
| 6262 * @profile: profile FILE * output or NULL | |
| 6263 * @userCtxt: user provided transform context | |
| 6264 * | |
| 6265 * Apply the stylesheet to the document and allow the user to provide | |
| 6266 * its own transformation context. | |
| 6267 * | |
| 6268 * Returns the result document or NULL in case of error | |
| 6269 */ | |
| 6270 xmlDocPtr | |
| 6271 xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, | |
| 6272 const char **params, const char *output, | |
| 6273 FILE * profile, xsltTransformContextPtr userCtxt) | |
| 6274 { | |
| 6275 xmlDocPtr res; | |
| 6276 | |
| 6277 res = xsltApplyStylesheetInternal(style, doc, params, output, | |
| 6278 profile, userCtxt); | |
| 6279 return (res); | |
| 6280 } | |
| 6281 | |
| 6282 /** | |
| 6283 * xsltRunStylesheetUser: | |
| 6284 * @style: a parsed XSLT stylesheet | |
| 6285 * @doc: a parsed XML document | |
| 6286 * @params: a NULL terminated array of parameters names/values tuples | |
| 6287 * @output: the URL/filename ot the generated resource if available | |
| 6288 * @SAX: a SAX handler for progressive callback output (not implemented yet) | |
| 6289 * @IObuf: an output buffer for progressive output (not implemented yet) | |
| 6290 * @profile: profile FILE * output or NULL | |
| 6291 * @userCtxt: user provided transform context | |
| 6292 * | |
| 6293 * Apply the stylesheet to the document and generate the output according | |
| 6294 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. | |
| 6295 * | |
| 6296 * NOTE: This may lead to a non-wellformed output XML wise ! | |
| 6297 * NOTE: This may also result in multiple files being generated | |
| 6298 * NOTE: using IObuf, the result encoding used will be the one used for | |
| 6299 * creating the output buffer, use the following macro to read it | |
| 6300 * from the stylesheet | |
| 6301 * XSLT_GET_IMPORT_PTR(encoding, style, encoding) | |
| 6302 * NOTE: using SAX, any encoding specified in the stylesheet will be lost | |
| 6303 * since the interface uses only UTF8 | |
| 6304 * | |
| 6305 * Returns the number of by written to the main resource or -1 in case of | |
| 6306 * error. | |
| 6307 */ | |
| 6308 int | |
| 6309 xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, | |
| 6310 const char **params, const char *output, | |
| 6311 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf, | |
| 6312 FILE * profile, xsltTransformContextPtr userCtxt) | |
| 6313 { | |
| 6314 xmlDocPtr tmp; | |
| 6315 int ret; | |
| 6316 | |
| 6317 if ((output == NULL) && (SAX == NULL) && (IObuf == NULL)) | |
| 6318 return (-1); | |
| 6319 if ((SAX != NULL) && (IObuf != NULL)) | |
| 6320 return (-1); | |
| 6321 | |
| 6322 /* unsupported yet */ | |
| 6323 if (SAX != NULL) { | |
| 6324 XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */ | |
| 6325 return (-1); | |
| 6326 } | |
| 6327 | |
| 6328 tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile, | |
| 6329 userCtxt); | |
| 6330 if (tmp == NULL) { | |
| 6331 xsltTransformError(NULL, NULL, (xmlNodePtr) doc, | |
| 6332 "xsltRunStylesheet : run failed\n"); | |
| 6333 return (-1); | |
| 6334 } | |
| 6335 if (IObuf != NULL) { | |
| 6336 /* TODO: incomplete, IObuf output not progressive */ | |
| 6337 ret = xsltSaveResultTo(IObuf, tmp, style); | |
| 6338 } else { | |
| 6339 ret = xsltSaveResultToFilename(output, tmp, style, 0); | |
| 6340 } | |
| 6341 xmlFreeDoc(tmp); | |
| 6342 return (ret); | |
| 6343 } | |
| 6344 | |
| 6345 /** | |
| 6346 * xsltRunStylesheet: | |
| 6347 * @style: a parsed XSLT stylesheet | |
| 6348 * @doc: a parsed XML document | |
| 6349 * @params: a NULL terminated array of parameters names/values tuples | |
| 6350 * @output: the URL/filename ot the generated resource if available | |
| 6351 * @SAX: a SAX handler for progressive callback output (not implemented yet) | |
| 6352 * @IObuf: an output buffer for progressive output (not implemented yet) | |
| 6353 * | |
| 6354 * Apply the stylesheet to the document and generate the output according | |
| 6355 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. | |
| 6356 * | |
| 6357 * NOTE: This may lead to a non-wellformed output XML wise ! | |
| 6358 * NOTE: This may also result in multiple files being generated | |
| 6359 * NOTE: using IObuf, the result encoding used will be the one used for | |
| 6360 * creating the output buffer, use the following macro to read it | |
| 6361 * from the stylesheet | |
| 6362 * XSLT_GET_IMPORT_PTR(encoding, style, encoding) | |
| 6363 * NOTE: using SAX, any encoding specified in the stylesheet will be lost | |
| 6364 * since the interface uses only UTF8 | |
| 6365 * | |
| 6366 * Returns the number of bytes written to the main resource or -1 in case of | |
| 6367 * error. | |
| 6368 */ | |
| 6369 int | |
| 6370 xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, | |
| 6371 const char **params, const char *output, | |
| 6372 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf) | |
| 6373 { | |
| 6374 return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf, | |
| 6375 NULL, NULL)); | |
| 6376 } | |
| 6377 | |
| 6378 /** | |
| 6379 * xsltRegisterAllElement: | |
| 6380 * @ctxt: the XPath context | |
| 6381 * | |
| 6382 * Registers all default XSLT elements in this context | |
| 6383 */ | |
| 6384 void | |
| 6385 xsltRegisterAllElement(xsltTransformContextPtr ctxt) | |
| 6386 { | |
| 6387 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates", | |
| 6388 XSLT_NAMESPACE, | |
| 6389 (xsltTransformFunction) xsltApplyTemplates); | |
| 6390 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports", | |
| 6391 XSLT_NAMESPACE, | |
| 6392 (xsltTransformFunction) xsltApplyImports); | |
| 6393 xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template", | |
| 6394 XSLT_NAMESPACE, | |
| 6395 (xsltTransformFunction) xsltCallTemplate); | |
| 6396 xsltRegisterExtElement(ctxt, (const xmlChar *) "element", | |
| 6397 XSLT_NAMESPACE, | |
| 6398 (xsltTransformFunction) xsltElement); | |
| 6399 xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute", | |
| 6400 XSLT_NAMESPACE, | |
| 6401 (xsltTransformFunction) xsltAttribute); | |
| 6402 xsltRegisterExtElement(ctxt, (const xmlChar *) "text", | |
| 6403 XSLT_NAMESPACE, | |
| 6404 (xsltTransformFunction) xsltText); | |
| 6405 xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction", | |
| 6406 XSLT_NAMESPACE, | |
| 6407 (xsltTransformFunction) xsltProcessingInstruction); | |
| 6408 xsltRegisterExtElement(ctxt, (const xmlChar *) "comment", | |
| 6409 XSLT_NAMESPACE, | |
| 6410 (xsltTransformFunction) xsltComment); | |
| 6411 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy", | |
| 6412 XSLT_NAMESPACE, | |
| 6413 (xsltTransformFunction) xsltCopy); | |
| 6414 xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of", | |
| 6415 XSLT_NAMESPACE, | |
| 6416 (xsltTransformFunction) xsltValueOf); | |
| 6417 xsltRegisterExtElement(ctxt, (const xmlChar *) "number", | |
| 6418 XSLT_NAMESPACE, | |
| 6419 (xsltTransformFunction) xsltNumber); | |
| 6420 xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each", | |
| 6421 XSLT_NAMESPACE, | |
| 6422 (xsltTransformFunction) xsltForEach); | |
| 6423 xsltRegisterExtElement(ctxt, (const xmlChar *) "if", | |
| 6424 XSLT_NAMESPACE, | |
| 6425 (xsltTransformFunction) xsltIf); | |
| 6426 xsltRegisterExtElement(ctxt, (const xmlChar *) "choose", | |
| 6427 XSLT_NAMESPACE, | |
| 6428 (xsltTransformFunction) xsltChoose); | |
| 6429 xsltRegisterExtElement(ctxt, (const xmlChar *) "sort", | |
| 6430 XSLT_NAMESPACE, | |
| 6431 (xsltTransformFunction) xsltSort); | |
| 6432 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of", | |
| 6433 XSLT_NAMESPACE, | |
| 6434 (xsltTransformFunction) xsltCopyOf); | |
| 6435 xsltRegisterExtElement(ctxt, (const xmlChar *) "message", | |
| 6436 XSLT_NAMESPACE, | |
| 6437 (xsltTransformFunction) xsltMessage); | |
| 6438 | |
| 6439 /* | |
| 6440 * Those don't have callable entry points but are registered anyway | |
| 6441 */ | |
| 6442 xsltRegisterExtElement(ctxt, (const xmlChar *) "variable", | |
| 6443 XSLT_NAMESPACE, | |
| 6444 (xsltTransformFunction) xsltDebug); | |
| 6445 xsltRegisterExtElement(ctxt, (const xmlChar *) "param", | |
| 6446 XSLT_NAMESPACE, | |
| 6447 (xsltTransformFunction) xsltDebug); | |
| 6448 xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param", | |
| 6449 XSLT_NAMESPACE, | |
| 6450 (xsltTransformFunction) xsltDebug); | |
| 6451 xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format", | |
| 6452 XSLT_NAMESPACE, | |
| 6453 (xsltTransformFunction) xsltDebug); | |
| 6454 xsltRegisterExtElement(ctxt, (const xmlChar *) "when", | |
| 6455 XSLT_NAMESPACE, | |
| 6456 (xsltTransformFunction) xsltDebug); | |
| 6457 xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise", | |
| 6458 XSLT_NAMESPACE, | |
| 6459 (xsltTransformFunction) xsltDebug); | |
| 6460 xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback", | |
| 6461 XSLT_NAMESPACE, | |
| 6462 (xsltTransformFunction) xsltDebug); | |
| 6463 | |
| 6464 } | |
| OLD | NEW |