Reactos
at master 2534 lines 68 kB view raw
1/* 2 * pattern.c: Implemetation of the template match compilation and lookup 3 * 4 * Reference: 5 * http://www.w3.org/TR/1999/REC-xslt-19991116 6 * 7 * See Copyright for the status of this software. 8 * 9 * daniel@veillard.com 10 */ 11 12/* 13 * TODO: handle pathological cases like *[*[@a="b"]] 14 * TODO: detect [number] at compilation, optimize accordingly 15 */ 16 17#define IN_LIBXSLT 18#include "libxslt.h" 19 20#include <string.h> 21 22#include <libxml/xmlmemory.h> 23#include <libxml/tree.h> 24#include <libxml/valid.h> 25#include <libxml/hash.h> 26#include <libxml/xmlerror.h> 27#include <libxml/parserInternals.h> 28#include <libxml/xpath.h> 29#include "xslt.h" 30#include "xsltInternals.h" 31#include "xsltutils.h" 32#include "imports.h" 33#include "templates.h" 34#include "keys.h" 35#include "pattern.h" 36#include "documents.h" 37 38#ifdef WITH_XSLT_DEBUG 39#define WITH_XSLT_DEBUG_PATTERN 40#endif 41 42/* 43 * Types are private: 44 */ 45 46typedef enum { 47 XSLT_OP_END=0, 48 XSLT_OP_ROOT, 49 XSLT_OP_ELEM, 50 XSLT_OP_ATTR, 51 XSLT_OP_PARENT, 52 XSLT_OP_ANCESTOR, 53 XSLT_OP_ID, 54 XSLT_OP_KEY, 55 XSLT_OP_NS, 56 XSLT_OP_ALL, 57 XSLT_OP_PI, 58 XSLT_OP_COMMENT, 59 XSLT_OP_TEXT, 60 XSLT_OP_NODE, 61 XSLT_OP_PREDICATE 62} xsltOp; 63 64typedef enum { 65 AXIS_CHILD=1, 66 AXIS_ATTRIBUTE 67} xsltAxis; 68 69typedef struct _xsltStepState xsltStepState; 70typedef xsltStepState *xsltStepStatePtr; 71struct _xsltStepState { 72 int step; 73 xmlNodePtr node; 74}; 75 76typedef struct _xsltStepStates xsltStepStates; 77typedef xsltStepStates *xsltStepStatesPtr; 78struct _xsltStepStates { 79 int nbstates; 80 int maxstates; 81 xsltStepStatePtr states; 82}; 83 84typedef struct _xsltStepOp xsltStepOp; 85typedef xsltStepOp *xsltStepOpPtr; 86struct _xsltStepOp { 87 xsltOp op; 88 xmlChar *value; 89 xmlChar *value2; 90 xmlChar *value3; 91 xmlXPathCompExprPtr comp; 92 /* 93 * Optimisations for count 94 */ 95 int previousExtra; 96 int indexExtra; 97 int lenExtra; 98}; 99 100struct _xsltCompMatch { 101 struct _xsltCompMatch *next; /* siblings in the name hash */ 102 float priority; /* the priority */ 103 const xmlChar *pattern; /* the pattern */ 104 const xmlChar *mode; /* the mode */ 105 const xmlChar *modeURI; /* the mode URI */ 106 xsltTemplatePtr template; /* the associated template */ 107 xmlNodePtr node; /* the containing element */ 108 109 int direct; 110 /* TODO fix the statically allocated size steps[] */ 111 int nbStep; 112 int maxStep; 113 xmlNsPtr *nsList; /* the namespaces in scope */ 114 int nsNr; /* the number of namespaces in scope */ 115 xsltStepOpPtr steps; /* ops for computation */ 116}; 117 118typedef struct _xsltParserContext xsltParserContext; 119typedef xsltParserContext *xsltParserContextPtr; 120struct _xsltParserContext { 121 xsltStylesheetPtr style; /* the stylesheet */ 122 xsltTransformContextPtr ctxt; /* the transformation or NULL */ 123 const xmlChar *cur; /* the current char being parsed */ 124 const xmlChar *base; /* the full expression */ 125 xmlDocPtr doc; /* the source document */ 126 xmlNodePtr elem; /* the source element */ 127 int error; /* error code */ 128 xsltCompMatchPtr comp; /* the result */ 129}; 130 131/************************************************************************ 132 * * 133 * Type functions * 134 * * 135 ************************************************************************/ 136 137/** 138 * xsltNewCompMatch: 139 * 140 * Create a new XSLT CompMatch 141 * 142 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error 143 */ 144static xsltCompMatchPtr 145xsltNewCompMatch(void) { 146 xsltCompMatchPtr cur; 147 148 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch)); 149 if (cur == NULL) { 150 xsltTransformError(NULL, NULL, NULL, 151 "xsltNewCompMatch : out of memory error\n"); 152 return(NULL); 153 } 154 memset(cur, 0, sizeof(xsltCompMatch)); 155 cur->maxStep = 10; 156 cur->nbStep = 0; 157 cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) * 158 cur->maxStep); 159 if (cur->steps == NULL) { 160 xsltTransformError(NULL, NULL, NULL, 161 "xsltNewCompMatch : out of memory error\n"); 162 xmlFree(cur); 163 return(NULL); 164 } 165 cur->nsNr = 0; 166 cur->nsList = NULL; 167 cur->direct = 0; 168 return(cur); 169} 170 171/** 172 * xsltFreeCompMatch: 173 * @comp: an XSLT comp 174 * 175 * Free up the memory allocated by @comp 176 */ 177static void 178xsltFreeCompMatch(xsltCompMatchPtr comp) { 179 xsltStepOpPtr op; 180 int i; 181 182 if (comp == NULL) 183 return; 184 if (comp->pattern != NULL) 185 xmlFree((xmlChar *)comp->pattern); 186 if (comp->nsList != NULL) 187 xmlFree(comp->nsList); 188 for (i = 0;i < comp->nbStep;i++) { 189 op = &comp->steps[i]; 190 if (op->value != NULL) 191 xmlFree(op->value); 192 if (op->value2 != NULL) 193 xmlFree(op->value2); 194 if (op->value3 != NULL) 195 xmlFree(op->value3); 196 if (op->comp != NULL) 197 xmlXPathFreeCompExpr(op->comp); 198 } 199 xmlFree(comp->steps); 200 memset(comp, -1, sizeof(xsltCompMatch)); 201 xmlFree(comp); 202} 203 204/** 205 * xsltFreeCompMatchList: 206 * @comp: an XSLT comp list 207 * 208 * Free up the memory allocated by all the elements of @comp 209 */ 210void 211xsltFreeCompMatchList(xsltCompMatchPtr comp) { 212 xsltCompMatchPtr cur; 213 214 while (comp != NULL) { 215 cur = comp; 216 comp = comp->next; 217 xsltFreeCompMatch(cur); 218 } 219} 220 221static void 222xsltFreeCompMatchListEntry(void *payload, 223 const xmlChar *name ATTRIBUTE_UNUSED) { 224 xsltFreeCompMatchList((xsltCompMatchPtr) payload); 225} 226 227/** 228 * xsltNormalizeCompSteps: 229 * @payload: pointer to template hash table entry 230 * @data: pointer to the stylesheet 231 * @name: template match name 232 * 233 * This is a hashtable scanner function to normalize the compiled 234 * steps of an imported stylesheet. 235 */ 236void xsltNormalizeCompSteps(void *payload, 237 void *data, const xmlChar *name ATTRIBUTE_UNUSED) { 238 xsltCompMatchPtr comp = payload; 239 xsltStylesheetPtr style = data; 240 int ix; 241 242 for (ix = 0; ix < comp->nbStep; ix++) { 243 comp->steps[ix].previousExtra += style->extrasNr; 244 comp->steps[ix].indexExtra += style->extrasNr; 245 comp->steps[ix].lenExtra += style->extrasNr; 246 } 247} 248 249/** 250 * xsltNewParserContext: 251 * @style: the stylesheet 252 * @ctxt: the transformation context, if done at run-time 253 * 254 * Create a new XSLT ParserContext 255 * 256 * Returns the newly allocated xsltParserContextPtr or NULL in case of error 257 */ 258static xsltParserContextPtr 259xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) { 260 xsltParserContextPtr cur; 261 262 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext)); 263 if (cur == NULL) { 264 xsltTransformError(NULL, NULL, NULL, 265 "xsltNewParserContext : malloc failed\n"); 266 return(NULL); 267 } 268 memset(cur, 0, sizeof(xsltParserContext)); 269 cur->style = style; 270 cur->ctxt = ctxt; 271 return(cur); 272} 273 274/** 275 * xsltFreeParserContext: 276 * @ctxt: an XSLT parser context 277 * 278 * Free up the memory allocated by @ctxt 279 */ 280static void 281xsltFreeParserContext(xsltParserContextPtr ctxt) { 282 if (ctxt == NULL) 283 return; 284 memset(ctxt, -1, sizeof(xsltParserContext)); 285 xmlFree(ctxt); 286} 287 288/** 289 * xsltCompMatchAdd: 290 * @comp: the compiled match expression 291 * @op: an op 292 * @value: the first value 293 * @value2: the second value 294 * @novar: flag to set XML_XPATH_NOVAR 295 * 296 * Add an step to an XSLT Compiled Match 297 * 298 * Returns -1 in case of failure, 0 otherwise. 299 */ 300static int 301xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp, 302 xsltOp op, xmlChar * value, xmlChar * value2, int novar) 303{ 304 if (comp->nbStep >= comp->maxStep) { 305 xsltStepOpPtr tmp; 306 307 tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 * 308 sizeof(xsltStepOp)); 309 if (tmp == NULL) { 310 xsltGenericError(xsltGenericErrorContext, 311 "xsltCompMatchAdd: memory re-allocation failure.\n"); 312 if (ctxt->style != NULL) 313 ctxt->style->errors++; 314 return (-1); 315 } 316 comp->maxStep *= 2; 317 comp->steps = tmp; 318 } 319 comp->steps[comp->nbStep].op = op; 320 comp->steps[comp->nbStep].value = value; 321 comp->steps[comp->nbStep].value2 = value2; 322 comp->steps[comp->nbStep].value3 = NULL; 323 comp->steps[comp->nbStep].comp = NULL; 324 if (ctxt->ctxt != NULL) { 325 comp->steps[comp->nbStep].previousExtra = 326 xsltAllocateExtraCtxt(ctxt->ctxt); 327 comp->steps[comp->nbStep].indexExtra = 328 xsltAllocateExtraCtxt(ctxt->ctxt); 329 comp->steps[comp->nbStep].lenExtra = 330 xsltAllocateExtraCtxt(ctxt->ctxt); 331 } else { 332 comp->steps[comp->nbStep].previousExtra = 333 xsltAllocateExtra(ctxt->style); 334 comp->steps[comp->nbStep].indexExtra = 335 xsltAllocateExtra(ctxt->style); 336 comp->steps[comp->nbStep].lenExtra = 337 xsltAllocateExtra(ctxt->style); 338 } 339 if (op == XSLT_OP_PREDICATE) { 340 int flags = 0; 341 342#ifdef XML_XPATH_NOVAR 343 if (novar != 0) 344 flags = XML_XPATH_NOVAR; 345#endif 346 comp->steps[comp->nbStep].comp = xsltXPathCompileFlags(ctxt->style, 347 value, flags); 348 if (comp->steps[comp->nbStep].comp == NULL) { 349 xsltTransformError(NULL, ctxt->style, ctxt->elem, 350 "Failed to compile predicate\n"); 351 if (ctxt->style != NULL) 352 ctxt->style->errors++; 353 } 354 } 355 comp->nbStep++; 356 return (0); 357} 358 359/** 360 * xsltSwapTopCompMatch: 361 * @comp: the compiled match expression 362 * 363 * reverse the two top steps. 364 */ 365static void 366xsltSwapTopCompMatch(xsltCompMatchPtr comp) { 367 int i; 368 int j = comp->nbStep - 1; 369 370 if (j > 0) { 371 register xmlChar *tmp; 372 register xsltOp op; 373 register xmlXPathCompExprPtr expr; 374 register int t; 375 i = j - 1; 376 tmp = comp->steps[i].value; 377 comp->steps[i].value = comp->steps[j].value; 378 comp->steps[j].value = tmp; 379 tmp = comp->steps[i].value2; 380 comp->steps[i].value2 = comp->steps[j].value2; 381 comp->steps[j].value2 = tmp; 382 tmp = comp->steps[i].value3; 383 comp->steps[i].value3 = comp->steps[j].value3; 384 comp->steps[j].value3 = tmp; 385 op = comp->steps[i].op; 386 comp->steps[i].op = comp->steps[j].op; 387 comp->steps[j].op = op; 388 expr = comp->steps[i].comp; 389 comp->steps[i].comp = comp->steps[j].comp; 390 comp->steps[j].comp = expr; 391 t = comp->steps[i].previousExtra; 392 comp->steps[i].previousExtra = comp->steps[j].previousExtra; 393 comp->steps[j].previousExtra = t; 394 t = comp->steps[i].indexExtra; 395 comp->steps[i].indexExtra = comp->steps[j].indexExtra; 396 comp->steps[j].indexExtra = t; 397 t = comp->steps[i].lenExtra; 398 comp->steps[i].lenExtra = comp->steps[j].lenExtra; 399 comp->steps[j].lenExtra = t; 400 } 401} 402 403/** 404 * xsltReverseCompMatch: 405 * @ctxt: the parser context 406 * @comp: the compiled match expression 407 * 408 * reverse all the stack of expressions 409 */ 410static void 411xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) { 412 int i = 0; 413 int j = comp->nbStep - 1; 414 415 while (j > i) { 416 register xmlChar *tmp; 417 register xsltOp op; 418 register xmlXPathCompExprPtr expr; 419 register int t; 420 421 tmp = comp->steps[i].value; 422 comp->steps[i].value = comp->steps[j].value; 423 comp->steps[j].value = tmp; 424 tmp = comp->steps[i].value2; 425 comp->steps[i].value2 = comp->steps[j].value2; 426 comp->steps[j].value2 = tmp; 427 tmp = comp->steps[i].value3; 428 comp->steps[i].value3 = comp->steps[j].value3; 429 comp->steps[j].value3 = tmp; 430 op = comp->steps[i].op; 431 comp->steps[i].op = comp->steps[j].op; 432 comp->steps[j].op = op; 433 expr = comp->steps[i].comp; 434 comp->steps[i].comp = comp->steps[j].comp; 435 comp->steps[j].comp = expr; 436 t = comp->steps[i].previousExtra; 437 comp->steps[i].previousExtra = comp->steps[j].previousExtra; 438 comp->steps[j].previousExtra = t; 439 t = comp->steps[i].indexExtra; 440 comp->steps[i].indexExtra = comp->steps[j].indexExtra; 441 comp->steps[j].indexExtra = t; 442 t = comp->steps[i].lenExtra; 443 comp->steps[i].lenExtra = comp->steps[j].lenExtra; 444 comp->steps[j].lenExtra = t; 445 j--; 446 i++; 447 } 448 xsltCompMatchAdd(ctxt, comp, XSLT_OP_END, NULL, NULL, 0); 449 450 /* 451 * Detect consecutive XSLT_OP_PREDICATE indicating a direct matching 452 * should be done. 453 */ 454 for (i = 0;i < comp->nbStep - 1;i++) { 455 if ((comp->steps[i].op == XSLT_OP_PREDICATE) && 456 (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) { 457 458 comp->direct = 1; 459 if (comp->pattern[0] != '/') { 460 xmlChar *query; 461 462 query = xmlStrdup((const xmlChar *)"//"); 463 query = xmlStrcat(query, comp->pattern); 464 465 xmlFree((xmlChar *) comp->pattern); 466 comp->pattern = query; 467 } 468 break; 469 } 470 } 471} 472 473/************************************************************************ 474 * * 475 * The interpreter for the precompiled patterns * 476 * * 477 ************************************************************************/ 478 479static int 480xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states, 481 int step, xmlNodePtr node) { 482 if (states->maxstates <= states->nbstates) { 483 xsltStepState *tmp; 484 int newMax = states->maxstates == 0 ? 4 : 2 * states->maxstates; 485 486 tmp = (xsltStepStatePtr) xmlRealloc(states->states, 487 newMax * sizeof(xsltStepState)); 488 if (tmp == NULL) { 489 xsltGenericError(xsltGenericErrorContext, 490 "xsltPatPushState: memory re-allocation failure.\n"); 491 ctxt->state = XSLT_STATE_STOPPED; 492 return(-1); 493 } 494 states->states = tmp; 495 states->maxstates = newMax; 496 } 497 states->states[states->nbstates].step = step; 498 states->states[states->nbstates++].node = node; 499#if 0 500 fprintf(stderr, "Push: %d, %s\n", step, node->name); 501#endif 502 return(0); 503} 504 505static void 506xmlXPathFreeObjectWrapper(void *obj) { 507 xmlXPathFreeObject((xmlXPathObjectPtr) obj); 508} 509 510/** 511 * xsltTestCompMatchDirect: 512 * @ctxt: a XSLT process context 513 * @comp: the precompiled pattern 514 * @node: a node 515 * @nsList: the namespaces in scope 516 * @nsNr: the number of namespaces in scope 517 * 518 * Test whether the node matches the pattern, do a direct evalutation 519 * and not a step by step evaluation. 520 * 521 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure 522 */ 523static int 524xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, 525 xmlNodePtr node, xmlNsPtr *nsList, int nsNr) { 526 xsltStepOpPtr sel = NULL; 527 xmlDocPtr prevdoc; 528 xmlDocPtr doc; 529 xmlXPathObjectPtr list; 530 int ix, j; 531 int nocache = 0; 532 int isRVT; 533 534 doc = node->doc; 535 if (XSLT_IS_RES_TREE_FRAG(doc)) 536 isRVT = 1; 537 else 538 isRVT = 0; 539 sel = &comp->steps[0]; /* store extra in first step arbitrarily */ 540 541 prevdoc = (xmlDocPtr) 542 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); 543 ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival); 544 list = (xmlXPathObjectPtr) 545 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra); 546 547 if ((list == NULL) || (prevdoc != doc)) { 548 xmlXPathObjectPtr newlist; 549 xmlNodePtr parent = node->parent; 550 xmlDocPtr olddoc; 551 xmlNodePtr oldnode; 552 int oldNsNr, oldContextSize, oldProximityPosition; 553 xmlNsPtr *oldNamespaces; 554 555 oldnode = ctxt->xpathCtxt->node; 556 olddoc = ctxt->xpathCtxt->doc; 557 oldNsNr = ctxt->xpathCtxt->nsNr; 558 oldNamespaces = ctxt->xpathCtxt->namespaces; 559 oldContextSize = ctxt->xpathCtxt->contextSize; 560 oldProximityPosition = ctxt->xpathCtxt->proximityPosition; 561 ctxt->xpathCtxt->node = node; 562 ctxt->xpathCtxt->doc = doc; 563 ctxt->xpathCtxt->namespaces = nsList; 564 ctxt->xpathCtxt->nsNr = nsNr; 565 newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt); 566 ctxt->xpathCtxt->node = oldnode; 567 ctxt->xpathCtxt->doc = olddoc; 568 ctxt->xpathCtxt->namespaces = oldNamespaces; 569 ctxt->xpathCtxt->nsNr = oldNsNr; 570 ctxt->xpathCtxt->contextSize = oldContextSize; 571 ctxt->xpathCtxt->proximityPosition = oldProximityPosition; 572 if (newlist == NULL) 573 return(-1); 574 if (newlist->type != XPATH_NODESET) { 575 xmlXPathFreeObject(newlist); 576 return(-1); 577 } 578 ix = 0; 579 580 if ((parent == NULL) || (node->doc == NULL) || isRVT) 581 nocache = 1; 582 583 if (nocache == 0) { 584 if (list != NULL) 585 xmlXPathFreeObject(list); 586 list = newlist; 587 588 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) = 589 (void *) list; 590 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = 591 (void *) doc; 592 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = 593 0; 594 XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) = 595 xmlXPathFreeObjectWrapper; 596 } else 597 list = newlist; 598 } 599 if ((list->nodesetval == NULL) || 600 (list->nodesetval->nodeNr <= 0)) { 601 if (nocache == 1) 602 xmlXPathFreeObject(list); 603 return(0); 604 } 605 /* TODO: store the index and use it for the scan */ 606 if (ix == 0) { 607 for (j = 0;j < list->nodesetval->nodeNr;j++) { 608 if (list->nodesetval->nodeTab[j] == node) { 609 if (nocache == 1) 610 xmlXPathFreeObject(list); 611 return(1); 612 } 613 } 614 } else { 615 } 616 if (nocache == 1) 617 xmlXPathFreeObject(list); 618 return(0); 619} 620 621/** 622 * xsltTestStepMatch: 623 * @ctxt: a XSLT process context 624 * @node: a node 625 * @step: the step 626 * 627 * Test whether the node matches the step. 628 * 629 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure 630 */ 631static int 632xsltTestStepMatch(xsltTransformContextPtr ctxt, xmlNodePtr node, 633 xsltStepOpPtr step) { 634 switch (step->op) { 635 case XSLT_OP_ROOT: 636 if ((node->type == XML_DOCUMENT_NODE) || 637#ifdef LIBXML_DOCB_ENABLED 638 (node->type == XML_DOCB_DOCUMENT_NODE) || 639#endif 640 (node->type == XML_HTML_DOCUMENT_NODE)) 641 return(1); 642 if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' ')) 643 return(1); 644 return(0); 645 case XSLT_OP_ELEM: 646 if (node->type != XML_ELEMENT_NODE) 647 return(0); 648 if (step->value == NULL) 649 return(1); 650 if (step->value[0] != node->name[0]) 651 return(0); 652 if (!xmlStrEqual(step->value, node->name)) 653 return(0); 654 655 /* Namespace test */ 656 if (node->ns == NULL) { 657 if (step->value2 != NULL) 658 return(0); 659 } else if (node->ns->href != NULL) { 660 if (step->value2 == NULL) 661 return(0); 662 if (!xmlStrEqual(step->value2, node->ns->href)) 663 return(0); 664 } 665 return(1); 666 case XSLT_OP_ATTR: 667 if (node->type != XML_ATTRIBUTE_NODE) 668 return(0); 669 if (step->value != NULL) { 670 if (step->value[0] != node->name[0]) 671 return(0); 672 if (!xmlStrEqual(step->value, node->name)) 673 return(0); 674 } 675 /* Namespace test */ 676 if (node->ns == NULL) { 677 if (step->value2 != NULL) 678 return(0); 679 } else if (step->value2 != NULL) { 680 if (!xmlStrEqual(step->value2, node->ns->href)) 681 return(0); 682 } 683 return(1); 684 case XSLT_OP_ID: { 685 /* TODO Handle IDs decently, must be done differently */ 686 xmlAttrPtr id; 687 688 if (node->type != XML_ELEMENT_NODE) 689 return(0); 690 691 id = xmlGetID(node->doc, step->value); 692 if ((id == NULL) || (id->parent != node)) 693 return(0); 694 break; 695 } 696 case XSLT_OP_KEY: { 697 xmlNodeSetPtr list; 698 int indx; 699 700 list = xsltGetKey(ctxt, step->value, 701 step->value3, step->value2); 702 if (list == NULL) 703 return(0); 704 for (indx = 0;indx < list->nodeNr;indx++) 705 if (list->nodeTab[indx] == node) 706 break; 707 if (indx >= list->nodeNr) 708 return(0); 709 break; 710 } 711 case XSLT_OP_NS: 712 if (node->type != XML_ELEMENT_NODE) 713 return(0); 714 if (node->ns == NULL) { 715 if (step->value != NULL) 716 return(0); 717 } else if (node->ns->href != NULL) { 718 if (step->value == NULL) 719 return(0); 720 if (!xmlStrEqual(step->value, node->ns->href)) 721 return(0); 722 } 723 break; 724 case XSLT_OP_ALL: 725 if (node->type != XML_ELEMENT_NODE) 726 return(0); 727 break; 728 case XSLT_OP_PI: 729 if (node->type != XML_PI_NODE) 730 return(0); 731 if (step->value != NULL) { 732 if (!xmlStrEqual(step->value, node->name)) 733 return(0); 734 } 735 break; 736 case XSLT_OP_COMMENT: 737 if (node->type != XML_COMMENT_NODE) 738 return(0); 739 break; 740 case XSLT_OP_TEXT: 741 if ((node->type != XML_TEXT_NODE) && 742 (node->type != XML_CDATA_SECTION_NODE)) 743 return(0); 744 break; 745 case XSLT_OP_NODE: 746 switch (node->type) { 747 case XML_ELEMENT_NODE: 748 case XML_CDATA_SECTION_NODE: 749 case XML_PI_NODE: 750 case XML_COMMENT_NODE: 751 case XML_TEXT_NODE: 752 break; 753 default: 754 return(0); 755 } 756 break; 757 default: 758 xsltTransformError(ctxt, NULL, node, 759 "xsltTestStepMatch: unexpected step op %d\n", 760 step->op); 761 return(-1); 762 } 763 764 return(1); 765} 766 767/** 768 * xsltTestPredicateMatch: 769 * @ctxt: a XSLT process context 770 * @comp: the precompiled pattern 771 * @node: a node 772 * @step: the predicate step 773 * @sel: the previous step 774 * 775 * Test whether the node matches the predicate 776 * 777 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure 778 */ 779static int 780xsltTestPredicateMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, 781 xmlNodePtr node, xsltStepOpPtr step, 782 xsltStepOpPtr sel) { 783 xmlNodePtr oldNode; 784 xmlDocPtr doc; 785 int oldCS, oldCP; 786 int pos = 0, len = 0; 787 int isRVT; 788 int match; 789 790 if (step->value == NULL) 791 return(0); 792 if (step->comp == NULL) 793 return(0); 794 if (sel == NULL) 795 return(0); 796 797 doc = node->doc; 798 if (XSLT_IS_RES_TREE_FRAG(doc)) 799 isRVT = 1; 800 else 801 isRVT = 0; 802 803 /* 804 * Recompute contextSize and proximityPosition. 805 * 806 * This could be improved in the following ways: 807 * 808 * - Skip recomputation if predicates don't use position() or last() 809 * - Keep data for multiple parents. This would require a hash table 810 * or an unused member in xmlNode. 811 * - Store node test results in a bitmap to avoid computing them twice. 812 */ 813 oldCS = ctxt->xpathCtxt->contextSize; 814 oldCP = ctxt->xpathCtxt->proximityPosition; 815 { 816 xmlNodePtr previous; 817 int nocache = 0; 818 819 previous = (xmlNodePtr) 820 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); 821 if ((previous != NULL) && 822 (previous->parent == node->parent)) { 823 /* 824 * just walk back to adjust the index 825 */ 826 int indx = 0; 827 xmlNodePtr sibling = node; 828 829 while (sibling != NULL) { 830 if (sibling == previous) 831 break; 832 if (xsltTestStepMatch(ctxt, sibling, sel)) 833 indx++; 834 sibling = sibling->prev; 835 } 836 if (sibling == NULL) { 837 /* hum going backward in document order ... */ 838 indx = 0; 839 sibling = node; 840 while (sibling != NULL) { 841 if (sibling == previous) 842 break; 843 if (xsltTestStepMatch(ctxt, sibling, sel)) 844 indx--; 845 sibling = sibling->next; 846 } 847 } 848 if (sibling != NULL) { 849 pos = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) + indx; 850 /* 851 * If the node is in a Value Tree we need to 852 * save len, but cannot cache the node! 853 * (bugs 153137 and 158840) 854 */ 855 if (node->doc != NULL) { 856 len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival); 857 if (!isRVT) { 858 XSLT_RUNTIME_EXTRA(ctxt, 859 sel->previousExtra, ptr) = node; 860 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; 861 } 862 } 863 } else 864 pos = 0; 865 } else { 866 /* 867 * recompute the index 868 */ 869 xmlNodePtr parent = node->parent; 870 xmlNodePtr siblings = NULL; 871 872 if (parent) siblings = parent->children; 873 874 while (siblings != NULL) { 875 if (siblings == node) { 876 len++; 877 pos = len; 878 } else if (xsltTestStepMatch(ctxt, siblings, sel)) { 879 len++; 880 } 881 siblings = siblings->next; 882 } 883 if ((parent == NULL) || (node->doc == NULL)) 884 nocache = 1; 885 else { 886 while (parent->parent != NULL) 887 parent = parent->parent; 888 if (((parent->type != XML_DOCUMENT_NODE) && 889 (parent->type != XML_HTML_DOCUMENT_NODE)) || 890 (parent != (xmlNodePtr) node->doc)) 891 nocache = 1; 892 } 893 } 894 if (pos != 0) { 895 ctxt->xpathCtxt->contextSize = len; 896 ctxt->xpathCtxt->proximityPosition = pos; 897 /* 898 * If the node is in a Value Tree we cannot 899 * cache it ! 900 */ 901 if ((!isRVT) && (node->doc != NULL) && 902 (nocache == 0)) { 903 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node; 904 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; 905 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len; 906 } 907 } 908 } 909 910 oldNode = ctxt->node; 911 ctxt->node = node; 912 913 match = xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList, comp->nsNr); 914 915 if (pos != 0) { 916 ctxt->xpathCtxt->contextSize = oldCS; 917 ctxt->xpathCtxt->proximityPosition = oldCP; 918 } 919 ctxt->node = oldNode; 920 921 return match; 922} 923 924/** 925 * xsltTestCompMatch: 926 * @ctxt: a XSLT process context 927 * @comp: the precompiled pattern 928 * @node: a node 929 * @mode: the mode name or NULL 930 * @modeURI: the mode URI or NULL 931 * 932 * Test whether the node matches the pattern 933 * 934 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure 935 */ 936static int 937xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, 938 xmlNodePtr matchNode, const xmlChar *mode, 939 const xmlChar *modeURI) { 940 int i; 941 int found = 0; 942 xmlNodePtr node = matchNode; 943 xmlNodePtr oldInst; 944 xsltStepOpPtr step, sel = NULL; 945 xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */ 946 947 if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) { 948 xsltTransformError(ctxt, NULL, node, 949 "xsltTestCompMatch: null arg\n"); 950 return(-1); 951 } 952 if (mode != NULL) { 953 if (comp->mode == NULL) 954 return(0); 955 /* 956 * both mode strings must be interned on the stylesheet dictionary 957 */ 958 if (comp->mode != mode) 959 return(0); 960 } else { 961 if (comp->mode != NULL) 962 return(0); 963 } 964 if (modeURI != NULL) { 965 if (comp->modeURI == NULL) 966 return(0); 967 /* 968 * both modeURI strings must be interned on the stylesheet dictionary 969 */ 970 if (comp->modeURI != modeURI) 971 return(0); 972 } else { 973 if (comp->modeURI != NULL) 974 return(0); 975 } 976 977 /* Some XPath functions rely on inst being set correctly. */ 978 oldInst = ctxt->inst; 979 ctxt->inst = comp->node; 980 981 i = 0; 982restart: 983 for (;i < comp->nbStep;i++) { 984 step = &comp->steps[i]; 985 if (step->op != XSLT_OP_PREDICATE) 986 sel = step; 987 switch (step->op) { 988 case XSLT_OP_END: 989 goto found; 990 case XSLT_OP_PARENT: 991 if ((node->type == XML_DOCUMENT_NODE) || 992 (node->type == XML_HTML_DOCUMENT_NODE) || 993#ifdef LIBXML_DOCB_ENABLED 994 (node->type == XML_DOCB_DOCUMENT_NODE) || 995#endif 996 (node->type == XML_NAMESPACE_DECL)) 997 goto rollback; 998 node = node->parent; 999 if (node == NULL) 1000 goto rollback; 1001 if (step->value == NULL) 1002 continue; 1003 if (step->value[0] != node->name[0]) 1004 goto rollback; 1005 if (!xmlStrEqual(step->value, node->name)) 1006 goto rollback; 1007 /* Namespace test */ 1008 if (node->ns == NULL) { 1009 if (step->value2 != NULL) 1010 goto rollback; 1011 } else if (node->ns->href != NULL) { 1012 if (step->value2 == NULL) 1013 goto rollback; 1014 if (!xmlStrEqual(step->value2, node->ns->href)) 1015 goto rollback; 1016 } 1017 continue; 1018 case XSLT_OP_ANCESTOR: 1019 /* TODO: implement coalescing of ANCESTOR/NODE ops */ 1020 if (step->value == NULL) { 1021 step = &comp->steps[i+1]; 1022 if (step->op == XSLT_OP_ROOT) 1023 goto found; 1024 /* added NS, ID and KEY as a result of bug 168208 */ 1025 if ((step->op != XSLT_OP_ELEM) && 1026 (step->op != XSLT_OP_ALL) && 1027 (step->op != XSLT_OP_NS) && 1028 (step->op != XSLT_OP_ID) && 1029 (step->op != XSLT_OP_KEY)) 1030 goto rollback; 1031 } 1032 if (node == NULL) 1033 goto rollback; 1034 if ((node->type == XML_DOCUMENT_NODE) || 1035 (node->type == XML_HTML_DOCUMENT_NODE) || 1036#ifdef LIBXML_DOCB_ENABLED 1037 (node->type == XML_DOCB_DOCUMENT_NODE) || 1038#endif 1039 (node->type == XML_NAMESPACE_DECL)) 1040 goto rollback; 1041 node = node->parent; 1042 if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) { 1043 xsltPatPushState(ctxt, &states, i, node); 1044 continue; 1045 } 1046 i++; 1047 sel = step; 1048 if (step->value == NULL) { 1049 xsltPatPushState(ctxt, &states, i - 1, node); 1050 continue; 1051 } 1052 while (node != NULL) { 1053 if ((node->type == XML_ELEMENT_NODE) && 1054 (step->value[0] == node->name[0]) && 1055 (xmlStrEqual(step->value, node->name))) { 1056 /* Namespace test */ 1057 if (node->ns == NULL) { 1058 if (step->value2 == NULL) 1059 break; 1060 } else if (node->ns->href != NULL) { 1061 if ((step->value2 != NULL) && 1062 (xmlStrEqual(step->value2, node->ns->href))) 1063 break; 1064 } 1065 } 1066 node = node->parent; 1067 } 1068 if (node == NULL) 1069 goto rollback; 1070 xsltPatPushState(ctxt, &states, i - 1, node); 1071 continue; 1072 case XSLT_OP_PREDICATE: { 1073 /* 1074 * When there is cascading XSLT_OP_PREDICATE or a predicate 1075 * after an op which hasn't been optimized yet, then use a 1076 * direct computation approach. It's not done directly 1077 * at the beginning of the routine to filter out as much 1078 * as possible this costly computation. 1079 */ 1080 if (comp->direct) { 1081 found = xsltTestCompMatchDirect(ctxt, comp, matchNode, 1082 comp->nsList, comp->nsNr); 1083 goto exit; 1084 } 1085 1086 if (!xsltTestPredicateMatch(ctxt, comp, node, step, sel)) 1087 goto rollback; 1088 1089 break; 1090 } 1091 default: 1092 if (xsltTestStepMatch(ctxt, node, step) != 1) 1093 goto rollback; 1094 break; 1095 } 1096 } 1097found: 1098 found = 1; 1099exit: 1100 ctxt->inst = oldInst; 1101 if (states.states != NULL) { 1102 /* Free the rollback states */ 1103 xmlFree(states.states); 1104 } 1105 return found; 1106rollback: 1107 /* got an error try to rollback */ 1108 if (states.states == NULL || states.nbstates <= 0) { 1109 found = 0; 1110 goto exit; 1111 } 1112 states.nbstates--; 1113 i = states.states[states.nbstates].step; 1114 node = states.states[states.nbstates].node; 1115#if 0 1116 fprintf(stderr, "Pop: %d, %s\n", i, node->name); 1117#endif 1118 goto restart; 1119} 1120 1121/** 1122 * xsltTestCompMatchList: 1123 * @ctxt: a XSLT process context 1124 * @node: a node 1125 * @comp: the precompiled pattern list 1126 * 1127 * Test whether the node matches one of the patterns in the list 1128 * 1129 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure 1130 */ 1131int 1132xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node, 1133 xsltCompMatchPtr comp) { 1134 int ret; 1135 1136 if ((ctxt == NULL) || (node == NULL)) 1137 return(-1); 1138 while (comp != NULL) { 1139 ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL); 1140 if (ret == 1) 1141 return(1); 1142 comp = comp->next; 1143 } 1144 return(0); 1145} 1146 1147/** 1148 * xsltCompMatchClearCache: 1149 * @ctxt: a XSLT process context 1150 * @comp: the precompiled pattern list 1151 * 1152 * Clear pattern match cache. 1153 */ 1154void 1155xsltCompMatchClearCache(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp) { 1156 xsltStepOpPtr sel; 1157 xmlXPathObjectPtr list; 1158 1159 if ((ctxt == NULL) || (comp == NULL)) 1160 return; 1161 1162 sel = &comp->steps[0]; 1163 list = (xmlXPathObjectPtr) XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra); 1164 1165 if (list != NULL) { 1166 xmlXPathFreeObject(list); 1167 1168 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) = NULL; 1169 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = NULL; 1170 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = 0; 1171 XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) = NULL; 1172 } 1173} 1174 1175/************************************************************************ 1176 * * 1177 * Dedicated parser for templates * 1178 * * 1179 ************************************************************************/ 1180 1181#define CUR (*ctxt->cur) 1182#define SKIP(val) ctxt->cur += (val) 1183#define NXT(val) ctxt->cur[(val)] 1184#define CUR_PTR ctxt->cur 1185 1186#define SKIP_BLANKS \ 1187 while (xmlIsBlank_ch(CUR)) NEXT 1188 1189#define CURRENT (*ctxt->cur) 1190#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) 1191 1192 1193#define PUSH(op, val, val2, novar) \ 1194 if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error; 1195 1196#define SWAP() \ 1197 xsltSwapTopCompMatch(ctxt->comp); 1198 1199#define XSLT_ERROR(X) \ 1200 { xsltError(ctxt, __FILE__, __LINE__, X); \ 1201 ctxt->error = (X); return; } 1202 1203#define XSLT_ERROR0(X) \ 1204 { xsltError(ctxt, __FILE__, __LINE__, X); \ 1205 ctxt->error = (X); return(0); } 1206 1207/** 1208 * xsltScanLiteral: 1209 * @ctxt: the XPath Parser context 1210 * 1211 * Parse an XPath Litteral: 1212 * 1213 * [29] Literal ::= '"' [^"]* '"' 1214 * | "'" [^']* "'" 1215 * 1216 * Returns the Literal parsed or NULL 1217 */ 1218 1219static xmlChar * 1220xsltScanLiteral(xsltParserContextPtr ctxt) { 1221 const xmlChar *q, *cur; 1222 xmlChar *ret = NULL; 1223 int val, len; 1224 1225 SKIP_BLANKS; 1226 if (CUR == '"') { 1227 NEXT; 1228 cur = q = CUR_PTR; 1229 val = xsltGetUTF8CharZ(cur, &len); 1230 while ((xmlIsCharQ(val)) && (val != '"')) { 1231 cur += len; 1232 val = xsltGetUTF8CharZ(cur, &len); 1233 } 1234 if (!xmlIsCharQ(val)) { 1235 ctxt->error = 1; 1236 return(NULL); 1237 } else { 1238 ret = xmlStrndup(q, cur - q); 1239 } 1240 cur += len; 1241 CUR_PTR = cur; 1242 } else if (CUR == '\'') { 1243 NEXT; 1244 cur = q = CUR_PTR; 1245 val = xsltGetUTF8CharZ(cur, &len); 1246 while ((xmlIsCharQ(val)) && (val != '\'')) { 1247 cur += len; 1248 val = xsltGetUTF8CharZ(cur, &len); 1249 } 1250 if (!xmlIsCharQ(val)) { 1251 ctxt->error = 1; 1252 return(NULL); 1253 } else { 1254 ret = xmlStrndup(q, cur - q); 1255 } 1256 cur += len; 1257 CUR_PTR = cur; 1258 } else { 1259 ctxt->error = 1; 1260 return(NULL); 1261 } 1262 return(ret); 1263} 1264 1265/** 1266 * xsltScanNCName: 1267 * @ctxt: the XPath Parser context 1268 * 1269 * Parses a non qualified name 1270 * 1271 * Returns the Name parsed or NULL 1272 */ 1273 1274static xmlChar * 1275xsltScanNCName(xsltParserContextPtr ctxt) { 1276 const xmlChar *q, *cur; 1277 xmlChar *ret = NULL; 1278 int val, len; 1279 1280 SKIP_BLANKS; 1281 1282 cur = q = CUR_PTR; 1283 val = xsltGetUTF8CharZ(cur, &len); 1284 if (!xmlIsBaseCharQ(val) && !xmlIsIdeographicQ(val) && (val != '_')) 1285 return(NULL); 1286 1287 while (xmlIsBaseCharQ(val) || xmlIsIdeographicQ(val) || 1288 xmlIsDigitQ(val) || 1289 (val == '.') || (val == '-') || 1290 (val == '_') || 1291 xmlIsCombiningQ(val) || 1292 xmlIsExtenderQ(val)) { 1293 cur += len; 1294 val = xsltGetUTF8CharZ(cur, &len); 1295 } 1296 ret = xmlStrndup(q, cur - q); 1297 CUR_PTR = cur; 1298 return(ret); 1299} 1300 1301/* 1302 * xsltCompileIdKeyPattern: 1303 * @ctxt: the compilation context 1304 * @name: a preparsed name 1305 * @aid: whether id/key are allowed there 1306 * @novar: flag to prohibit xslt var 1307 * 1308 * Compile the XSLT LocationIdKeyPattern 1309 * [3] IdKeyPattern ::= 'id' '(' Literal ')' 1310 * | 'key' '(' Literal ',' Literal ')' 1311 * 1312 * also handle NodeType and PI from: 1313 * 1314 * [7] NodeTest ::= NameTest 1315 * | NodeType '(' ')' 1316 * | 'processing-instruction' '(' Literal ')' 1317 */ 1318static void 1319xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, 1320 int aid, int novar, xsltAxis axis) { 1321 xmlChar *lit = NULL; 1322 xmlChar *lit2 = NULL; 1323 1324 if (CUR != '(') { 1325 xsltTransformError(NULL, NULL, NULL, 1326 "xsltCompileIdKeyPattern : ( expected\n"); 1327 ctxt->error = 1; 1328 return; 1329 } 1330 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) { 1331 if (axis != 0) { 1332 xsltTransformError(NULL, NULL, NULL, 1333 "xsltCompileIdKeyPattern : NodeTest expected\n"); 1334 ctxt->error = 1; 1335 return; 1336 } 1337 NEXT; 1338 SKIP_BLANKS; 1339 lit = xsltScanLiteral(ctxt); 1340 if (ctxt->error) { 1341 xsltTransformError(NULL, NULL, NULL, 1342 "xsltCompileIdKeyPattern : Literal expected\n"); 1343 xmlFree(lit); 1344 return; 1345 } 1346 SKIP_BLANKS; 1347 if (CUR != ')') { 1348 xsltTransformError(NULL, NULL, NULL, 1349 "xsltCompileIdKeyPattern : ) expected\n"); 1350 xmlFree(lit); 1351 ctxt->error = 1; 1352 return; 1353 } 1354 NEXT; 1355 PUSH(XSLT_OP_ID, lit, NULL, novar); 1356 lit = NULL; 1357 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) { 1358 if (axis != 0) { 1359 xsltTransformError(NULL, NULL, NULL, 1360 "xsltCompileIdKeyPattern : NodeTest expected\n"); 1361 ctxt->error = 1; 1362 return; 1363 } 1364 NEXT; 1365 SKIP_BLANKS; 1366 lit = xsltScanLiteral(ctxt); 1367 if (ctxt->error) { 1368 xsltTransformError(NULL, NULL, NULL, 1369 "xsltCompileIdKeyPattern : Literal expected\n"); 1370 xmlFree(lit); 1371 return; 1372 } 1373 SKIP_BLANKS; 1374 if (CUR != ',') { 1375 xsltTransformError(NULL, NULL, NULL, 1376 "xsltCompileIdKeyPattern : , expected\n"); 1377 xmlFree(lit); 1378 ctxt->error = 1; 1379 return; 1380 } 1381 NEXT; 1382 SKIP_BLANKS; 1383 lit2 = xsltScanLiteral(ctxt); 1384 if (ctxt->error) { 1385 xsltTransformError(NULL, NULL, NULL, 1386 "xsltCompileIdKeyPattern : Literal expected\n"); 1387 xmlFree(lit); 1388 return; 1389 } 1390 SKIP_BLANKS; 1391 if (CUR != ')') { 1392 xsltTransformError(NULL, NULL, NULL, 1393 "xsltCompileIdKeyPattern : ) expected\n"); 1394 xmlFree(lit); 1395 xmlFree(lit2); 1396 ctxt->error = 1; 1397 return; 1398 } 1399 NEXT; 1400 /* URGENT TODO: support namespace in keys */ 1401 PUSH(XSLT_OP_KEY, lit, lit2, novar); 1402 lit = NULL; 1403 lit2 = NULL; 1404 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) { 1405 NEXT; 1406 SKIP_BLANKS; 1407 if (CUR != ')') { 1408 lit = xsltScanLiteral(ctxt); 1409 if (ctxt->error) { 1410 xsltTransformError(NULL, NULL, NULL, 1411 "xsltCompileIdKeyPattern : Literal expected\n"); 1412 xmlFree(lit); 1413 return; 1414 } 1415 SKIP_BLANKS; 1416 if (CUR != ')') { 1417 xsltTransformError(NULL, NULL, NULL, 1418 "xsltCompileIdKeyPattern : ) expected\n"); 1419 ctxt->error = 1; 1420 xmlFree(lit); 1421 return; 1422 } 1423 } 1424 NEXT; 1425 PUSH(XSLT_OP_PI, lit, NULL, novar); 1426 lit = NULL; 1427 } else if (xmlStrEqual(name, (const xmlChar *)"text")) { 1428 NEXT; 1429 SKIP_BLANKS; 1430 if (CUR != ')') { 1431 xsltTransformError(NULL, NULL, NULL, 1432 "xsltCompileIdKeyPattern : ) expected\n"); 1433 ctxt->error = 1; 1434 return; 1435 } 1436 NEXT; 1437 PUSH(XSLT_OP_TEXT, NULL, NULL, novar); 1438 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) { 1439 NEXT; 1440 SKIP_BLANKS; 1441 if (CUR != ')') { 1442 xsltTransformError(NULL, NULL, NULL, 1443 "xsltCompileIdKeyPattern : ) expected\n"); 1444 ctxt->error = 1; 1445 return; 1446 } 1447 NEXT; 1448 PUSH(XSLT_OP_COMMENT, NULL, NULL, novar); 1449 } else if (xmlStrEqual(name, (const xmlChar *)"node")) { 1450 NEXT; 1451 SKIP_BLANKS; 1452 if (CUR != ')') { 1453 xsltTransformError(NULL, NULL, NULL, 1454 "xsltCompileIdKeyPattern : ) expected\n"); 1455 ctxt->error = 1; 1456 return; 1457 } 1458 NEXT; 1459 if (axis == AXIS_ATTRIBUTE) { 1460 PUSH(XSLT_OP_ATTR, NULL, NULL, novar); 1461 } 1462 else { 1463 PUSH(XSLT_OP_NODE, NULL, NULL, novar); 1464 } 1465 } else if (aid) { 1466 xsltTransformError(NULL, NULL, NULL, 1467 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n"); 1468 ctxt->error = 1; 1469 return; 1470 } else { 1471 xsltTransformError(NULL, NULL, NULL, 1472 "xsltCompileIdKeyPattern : node type\n"); 1473 ctxt->error = 1; 1474 return; 1475 } 1476error: 1477 return; 1478} 1479 1480/** 1481 * xsltCompileStepPattern: 1482 * @ctxt: the compilation context 1483 * @token: a posible precompiled name 1484 * @novar: flag to prohibit xslt variables from pattern 1485 * 1486 * Compile the XSLT StepPattern and generates a precompiled 1487 * form suitable for fast matching. 1488 * 1489 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* 1490 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier 1491 * | ('child' | 'attribute') '::' 1492 * from XPath 1493 * [7] NodeTest ::= NameTest 1494 * | NodeType '(' ')' 1495 * | 'processing-instruction' '(' Literal ')' 1496 * [8] Predicate ::= '[' PredicateExpr ']' 1497 * [9] PredicateExpr ::= Expr 1498 * [13] AbbreviatedAxisSpecifier ::= '@'? 1499 * [37] NameTest ::= '*' | NCName ':' '*' | QName 1500 */ 1501 1502static void 1503xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) { 1504 xmlChar *name = NULL; 1505 const xmlChar *URI = NULL; 1506 xmlChar *URL = NULL; 1507 xmlChar *ret = NULL; 1508 int level; 1509 xsltAxis axis = 0; 1510 1511 SKIP_BLANKS; 1512 if ((token == NULL) && (CUR == '@')) { 1513 NEXT; 1514 axis = AXIS_ATTRIBUTE; 1515 } 1516parse_node_test: 1517 if (token == NULL) 1518 token = xsltScanNCName(ctxt); 1519 if (token == NULL) { 1520 if (CUR == '*') { 1521 NEXT; 1522 if (axis == AXIS_ATTRIBUTE) { 1523 PUSH(XSLT_OP_ATTR, NULL, NULL, novar); 1524 } 1525 else { 1526 PUSH(XSLT_OP_ALL, NULL, NULL, novar); 1527 } 1528 goto parse_predicate; 1529 } else { 1530 xsltTransformError(NULL, NULL, NULL, 1531 "xsltCompileStepPattern : Name expected\n"); 1532 ctxt->error = 1; 1533 goto error; 1534 } 1535 } 1536 1537 1538 SKIP_BLANKS; 1539 if (CUR == '(') { 1540 xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis); 1541 xmlFree(token); 1542 token = NULL; 1543 if (ctxt->error) 1544 goto error; 1545 } else if (CUR == ':') { 1546 NEXT; 1547 if (CUR != ':') { 1548 xmlChar *prefix = token; 1549 xmlNsPtr ns; 1550 1551 /* 1552 * This is a namespace match 1553 */ 1554 token = xsltScanNCName(ctxt); 1555 ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix); 1556 if (ns == NULL) { 1557 xsltTransformError(NULL, NULL, NULL, 1558 "xsltCompileStepPattern : no namespace bound to prefix %s\n", 1559 prefix); 1560 xmlFree(prefix); 1561 prefix=NULL; 1562 ctxt->error = 1; 1563 goto error; 1564 } else { 1565 URL = xmlStrdup(ns->href); 1566 } 1567 xmlFree(prefix); 1568 prefix=NULL; 1569 if (token == NULL) { 1570 if (CUR == '*') { 1571 NEXT; 1572 if (axis == AXIS_ATTRIBUTE) { 1573 PUSH(XSLT_OP_ATTR, NULL, URL, novar); 1574 URL = NULL; 1575 } 1576 else { 1577 PUSH(XSLT_OP_NS, URL, NULL, novar); 1578 URL = NULL; 1579 } 1580 } else { 1581 xsltTransformError(NULL, NULL, NULL, 1582 "xsltCompileStepPattern : Name expected\n"); 1583 ctxt->error = 1; 1584 goto error; 1585 } 1586 } else { 1587 if (axis == AXIS_ATTRIBUTE) { 1588 PUSH(XSLT_OP_ATTR, token, URL, novar); 1589 token = NULL; 1590 URL = NULL; 1591 } 1592 else { 1593 PUSH(XSLT_OP_ELEM, token, URL, novar); 1594 token = NULL; 1595 URL = NULL; 1596 } 1597 } 1598 } else { 1599 if (axis != 0) { 1600 xsltTransformError(NULL, NULL, NULL, 1601 "xsltCompileStepPattern : NodeTest expected\n"); 1602 ctxt->error = 1; 1603 goto error; 1604 } 1605 NEXT; 1606 if (xmlStrEqual(token, (const xmlChar *) "child")) { 1607 axis = AXIS_CHILD; 1608 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) { 1609 axis = AXIS_ATTRIBUTE; 1610 } else { 1611 xsltTransformError(NULL, NULL, NULL, 1612 "xsltCompileStepPattern : 'child' or 'attribute' expected\n"); 1613 ctxt->error = 1; 1614 goto error; 1615 } 1616 xmlFree(token); 1617 token = NULL; 1618 SKIP_BLANKS; 1619 token = xsltScanNCName(ctxt); 1620 goto parse_node_test; 1621 } 1622 } else { 1623 URI = xsltGetQNameURI(ctxt->elem, &token); 1624 if (token == NULL) { 1625 ctxt->error = 1; 1626 goto error; 1627 } 1628 if (URI != NULL) 1629 URL = xmlStrdup(URI); 1630 if (axis == AXIS_ATTRIBUTE) { 1631 PUSH(XSLT_OP_ATTR, token, URL, novar); 1632 token = NULL; 1633 URL = NULL; 1634 } 1635 else { 1636 PUSH(XSLT_OP_ELEM, token, URL, novar); 1637 token = NULL; 1638 URL = NULL; 1639 } 1640 } 1641parse_predicate: 1642 SKIP_BLANKS; 1643 level = 0; 1644 while (CUR == '[') { 1645 const xmlChar *q; 1646 1647 level++; 1648 NEXT; 1649 q = CUR_PTR; 1650 while (CUR != 0) { 1651 /* Skip over nested predicates */ 1652 if (CUR == '[') 1653 level++; 1654 else if (CUR == ']') { 1655 level--; 1656 if (level == 0) 1657 break; 1658 } else if (CUR == '"') { 1659 NEXT; 1660 while ((CUR != 0) && (CUR != '"')) 1661 NEXT; 1662 } else if (CUR == '\'') { 1663 NEXT; 1664 while ((CUR != 0) && (CUR != '\'')) 1665 NEXT; 1666 } 1667 NEXT; 1668 } 1669 if (CUR == 0) { 1670 xsltTransformError(NULL, NULL, NULL, 1671 "xsltCompileStepPattern : ']' expected\n"); 1672 ctxt->error = 1; 1673 return; 1674 } 1675 ret = xmlStrndup(q, CUR_PTR - q); 1676 PUSH(XSLT_OP_PREDICATE, ret, NULL, novar); 1677 ret = NULL; 1678 /* push the predicate lower than local test */ 1679 SWAP(); 1680 NEXT; 1681 SKIP_BLANKS; 1682 } 1683 return; 1684error: 1685 if (token != NULL) 1686 xmlFree(token); 1687 if (name != NULL) 1688 xmlFree(name); 1689 if (URL != NULL) 1690 xmlFree(URL); 1691 if (ret != NULL) 1692 xmlFree(ret); 1693} 1694 1695/** 1696 * xsltCompileRelativePathPattern: 1697 * @comp: the compilation context 1698 * @token: a posible precompiled name 1699 * @novar: flag to prohibit xslt variables 1700 * 1701 * Compile the XSLT RelativePathPattern and generates a precompiled 1702 * form suitable for fast matching. 1703 * 1704 * [4] RelativePathPattern ::= StepPattern 1705 * | RelativePathPattern '/' StepPattern 1706 * | RelativePathPattern '//' StepPattern 1707 */ 1708static void 1709xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) { 1710 xsltCompileStepPattern(ctxt, token, novar); 1711 if (ctxt->error) 1712 goto error; 1713 SKIP_BLANKS; 1714 while ((CUR != 0) && (CUR != '|')) { 1715 if ((CUR == '/') && (NXT(1) == '/')) { 1716 PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); 1717 NEXT; 1718 NEXT; 1719 SKIP_BLANKS; 1720 xsltCompileStepPattern(ctxt, NULL, novar); 1721 } else if (CUR == '/') { 1722 PUSH(XSLT_OP_PARENT, NULL, NULL, novar); 1723 NEXT; 1724 SKIP_BLANKS; 1725 xsltCompileStepPattern(ctxt, NULL, novar); 1726 } else { 1727 ctxt->error = 1; 1728 } 1729 if (ctxt->error) 1730 goto error; 1731 SKIP_BLANKS; 1732 } 1733error: 1734 return; 1735} 1736 1737/** 1738 * xsltCompileLocationPathPattern: 1739 * @ctxt: the compilation context 1740 * @novar: flag to prohibit xslt variables 1741 * 1742 * Compile the XSLT LocationPathPattern and generates a precompiled 1743 * form suitable for fast matching. 1744 * 1745 * [2] LocationPathPattern ::= '/' RelativePathPattern? 1746 * | IdKeyPattern (('/' | '//') RelativePathPattern)? 1747 * | '//'? RelativePathPattern 1748 */ 1749static void 1750xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) { 1751 SKIP_BLANKS; 1752 if ((CUR == '/') && (NXT(1) == '/')) { 1753 /* 1754 * since we reverse the query 1755 * a leading // can be safely ignored 1756 */ 1757 NEXT; 1758 NEXT; 1759 ctxt->comp->priority = 0.5; /* '//' means not 0 priority */ 1760 xsltCompileRelativePathPattern(ctxt, NULL, novar); 1761 } else if (CUR == '/') { 1762 /* 1763 * We need to find root as the parent 1764 */ 1765 NEXT; 1766 SKIP_BLANKS; 1767 PUSH(XSLT_OP_ROOT, NULL, NULL, novar); 1768 if ((CUR != 0) && (CUR != '|')) { 1769 PUSH(XSLT_OP_PARENT, NULL, NULL, novar); 1770 xsltCompileRelativePathPattern(ctxt, NULL, novar); 1771 } 1772 } else if (CUR == '*') { 1773 xsltCompileRelativePathPattern(ctxt, NULL, novar); 1774 } else if (CUR == '@') { 1775 xsltCompileRelativePathPattern(ctxt, NULL, novar); 1776 } else { 1777 xmlChar *name; 1778 name = xsltScanNCName(ctxt); 1779 if (name == NULL) { 1780 xsltTransformError(NULL, NULL, NULL, 1781 "xsltCompileLocationPathPattern : Name expected\n"); 1782 ctxt->error = 1; 1783 return; 1784 } 1785 SKIP_BLANKS; 1786 if ((CUR == '(') && !xmlXPathIsNodeType(name)) { 1787 xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0); 1788 xmlFree(name); 1789 name = NULL; 1790 if (ctxt->error) 1791 return; 1792 if ((CUR == '/') && (NXT(1) == '/')) { 1793 PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); 1794 NEXT; 1795 NEXT; 1796 SKIP_BLANKS; 1797 xsltCompileRelativePathPattern(ctxt, NULL, novar); 1798 } else if (CUR == '/') { 1799 PUSH(XSLT_OP_PARENT, NULL, NULL, novar); 1800 NEXT; 1801 SKIP_BLANKS; 1802 xsltCompileRelativePathPattern(ctxt, NULL, novar); 1803 } 1804 return; 1805 } 1806 xsltCompileRelativePathPattern(ctxt, name, novar); 1807 } 1808error: 1809 return; 1810} 1811 1812/** 1813 * xsltCompilePatternInternal: 1814 * @pattern: an XSLT pattern 1815 * @doc: the containing document 1816 * @node: the containing element 1817 * @style: the stylesheet 1818 * @runtime: the transformation context, if done at run-time 1819 * @novar: flag to prohibit xslt variables 1820 * 1821 * Compile the XSLT pattern and generates a list of precompiled form suitable 1822 * for fast matching. 1823 * 1824 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern 1825 * 1826 * Returns the generated pattern list or NULL in case of failure 1827 */ 1828 1829static xsltCompMatchPtr 1830xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc, 1831 xmlNodePtr node, xsltStylesheetPtr style, 1832 xsltTransformContextPtr runtime, int novar) { 1833 xsltParserContextPtr ctxt = NULL; 1834 xsltCompMatchPtr element, first = NULL, previous = NULL; 1835 int current, start, end, level, j; 1836 1837 if (pattern == NULL) { 1838 xsltTransformError(NULL, NULL, node, 1839 "xsltCompilePattern : NULL pattern\n"); 1840 return(NULL); 1841 } 1842 1843 ctxt = xsltNewParserContext(style, runtime); 1844 if (ctxt == NULL) 1845 return(NULL); 1846 ctxt->doc = doc; 1847 ctxt->elem = node; 1848 current = end = 0; 1849 while (pattern[current] != 0) { 1850 start = current; 1851 while (xmlIsBlank_ch(pattern[current])) 1852 current++; 1853 end = current; 1854 level = 0; 1855 while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) { 1856 if (pattern[end] == '[') 1857 level++; 1858 else if (pattern[end] == ']') 1859 level--; 1860 else if (pattern[end] == '\'') { 1861 end++; 1862 while ((pattern[end] != 0) && (pattern[end] != '\'')) 1863 end++; 1864 } else if (pattern[end] == '"') { 1865 end++; 1866 while ((pattern[end] != 0) && (pattern[end] != '"')) 1867 end++; 1868 } 1869 if (pattern[end] == 0) 1870 break; 1871 end++; 1872 } 1873 if (current == end) { 1874 xsltTransformError(NULL, NULL, node, 1875 "xsltCompilePattern : NULL pattern\n"); 1876 goto error; 1877 } 1878 element = xsltNewCompMatch(); 1879 if (element == NULL) { 1880 goto error; 1881 } 1882 if (first == NULL) 1883 first = element; 1884 else if (previous != NULL) 1885 previous->next = element; 1886 previous = element; 1887 1888 ctxt->comp = element; 1889 ctxt->base = xmlStrndup(&pattern[start], end - start); 1890 if (ctxt->base == NULL) 1891 goto error; 1892 ctxt->cur = &(ctxt->base)[current - start]; 1893 element->pattern = ctxt->base; 1894 element->node = node; 1895 element->nsList = xmlGetNsList(doc, node); 1896 j = 0; 1897 if (element->nsList != NULL) { 1898 while (element->nsList[j] != NULL) 1899 j++; 1900 } 1901 element->nsNr = j; 1902 1903 1904#ifdef WITH_XSLT_DEBUG_PATTERN 1905 xsltGenericDebug(xsltGenericDebugContext, 1906 "xsltCompilePattern : parsing '%s'\n", 1907 element->pattern); 1908#endif 1909 /* 1910 Preset default priority to be zero. 1911 This may be changed by xsltCompileLocationPathPattern. 1912 */ 1913 element->priority = 0; 1914 xsltCompileLocationPathPattern(ctxt, novar); 1915 if (ctxt->error) { 1916 xsltTransformError(NULL, style, node, 1917 "xsltCompilePattern : failed to compile '%s'\n", 1918 element->pattern); 1919 if (style != NULL) style->errors++; 1920 goto error; 1921 } 1922 1923 /* 1924 * Reverse for faster interpretation. 1925 */ 1926 xsltReverseCompMatch(ctxt, element); 1927 1928 /* 1929 * Set-up the priority 1930 */ 1931 if (element->priority == 0) { /* if not yet determined */ 1932 if (((element->steps[0].op == XSLT_OP_ELEM) || 1933 (element->steps[0].op == XSLT_OP_ATTR) || 1934 (element->steps[0].op == XSLT_OP_PI)) && 1935 (element->steps[0].value != NULL) && 1936 (element->steps[1].op == XSLT_OP_END)) { 1937 ; /* previously preset */ 1938 } else if ((element->steps[0].op == XSLT_OP_ATTR) && 1939 (element->steps[0].value2 != NULL) && 1940 (element->steps[1].op == XSLT_OP_END)) { 1941 element->priority = -0.25; 1942 } else if ((element->steps[0].op == XSLT_OP_NS) && 1943 (element->steps[0].value != NULL) && 1944 (element->steps[1].op == XSLT_OP_END)) { 1945 element->priority = -0.25; 1946 } else if ((element->steps[0].op == XSLT_OP_ATTR) && 1947 (element->steps[0].value == NULL) && 1948 (element->steps[0].value2 == NULL) && 1949 (element->steps[1].op == XSLT_OP_END)) { 1950 element->priority = -0.5; 1951 } else if (((element->steps[0].op == XSLT_OP_PI) || 1952 (element->steps[0].op == XSLT_OP_TEXT) || 1953 (element->steps[0].op == XSLT_OP_ALL) || 1954 (element->steps[0].op == XSLT_OP_NODE) || 1955 (element->steps[0].op == XSLT_OP_COMMENT)) && 1956 (element->steps[1].op == XSLT_OP_END)) { 1957 element->priority = -0.5; 1958 } else { 1959 element->priority = 0.5; 1960 } 1961 } 1962#ifdef WITH_XSLT_DEBUG_PATTERN 1963 xsltGenericDebug(xsltGenericDebugContext, 1964 "xsltCompilePattern : parsed %s, default priority %f\n", 1965 element->pattern, element->priority); 1966#endif 1967 if (pattern[end] == '|') 1968 end++; 1969 current = end; 1970 } 1971 if (end == 0) { 1972 xsltTransformError(NULL, style, node, 1973 "xsltCompilePattern : NULL pattern\n"); 1974 if (style != NULL) style->errors++; 1975 goto error; 1976 } 1977 1978 xsltFreeParserContext(ctxt); 1979 return(first); 1980 1981error: 1982 if (ctxt != NULL) 1983 xsltFreeParserContext(ctxt); 1984 if (first != NULL) 1985 xsltFreeCompMatchList(first); 1986 return(NULL); 1987} 1988 1989/** 1990 * xsltCompilePattern: 1991 * @pattern: an XSLT pattern 1992 * @doc: the containing document 1993 * @node: the containing element 1994 * @style: the stylesheet 1995 * @runtime: the transformation context, if done at run-time 1996 * 1997 * Compile the XSLT pattern and generates a list of precompiled form suitable 1998 * for fast matching. 1999 * 2000 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern 2001 * 2002 * Returns the generated pattern list or NULL in case of failure 2003 */ 2004 2005xsltCompMatchPtr 2006xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc, 2007 xmlNodePtr node, xsltStylesheetPtr style, 2008 xsltTransformContextPtr runtime) { 2009 return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0)); 2010} 2011 2012/************************************************************************ 2013 * * 2014 * Module interfaces * 2015 * * 2016 ************************************************************************/ 2017 2018/** 2019 * xsltAddTemplate: 2020 * @style: an XSLT stylesheet 2021 * @cur: an XSLT template 2022 * @mode: the mode name or NULL 2023 * @modeURI: the mode URI or NULL 2024 * 2025 * Register the XSLT pattern associated to @cur 2026 * 2027 * Returns -1 in case of error, 0 otherwise 2028 */ 2029int 2030xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur, 2031 const xmlChar *mode, const xmlChar *modeURI) { 2032 xsltCompMatchPtr pat, list, next; 2033 /* 2034 * 'top' will point to style->xxxMatch ptr - declaring as 'void' 2035 * avoids gcc 'type-punned pointer' warning. 2036 */ 2037 xsltCompMatchPtr *top = NULL; 2038 const xmlChar *name = NULL; 2039 float priority; /* the priority */ 2040 2041 if ((style == NULL) || (cur == NULL)) 2042 return(-1); 2043 2044 if (cur->next != NULL) 2045 cur->position = cur->next->position + 1; 2046 2047 /* Register named template */ 2048 if (cur->name != NULL) { 2049 if (style->namedTemplates == NULL) { 2050 style->namedTemplates = xmlHashCreate(10); 2051 if (style->namedTemplates == NULL) 2052 return(-1); 2053 } 2054 else { 2055 void *dup = xmlHashLookup2(style->namedTemplates, cur->name, 2056 cur->nameURI); 2057 if (dup != NULL) { 2058 xsltTransformError(NULL, style, cur->elem, 2059 "xsl:template: error duplicate name '%s'\n", 2060 cur->name); 2061 style->errors++; 2062 return(-1); 2063 } 2064 } 2065 2066 xmlHashAddEntry2(style->namedTemplates, cur->name, cur->nameURI, cur); 2067 } 2068 2069 if (cur->match == NULL) { 2070 if (cur->name == NULL) { 2071 xsltTransformError(NULL, style, cur->elem, 2072 "xsl:template: need to specify match or name attribute\n"); 2073 style->errors++; 2074 return(-1); 2075 } 2076 return(0); 2077 } 2078 2079 priority = cur->priority; 2080 pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem, 2081 style, NULL, 1); 2082 if (pat == NULL) 2083 return(-1); 2084 while (pat) { 2085 int success = 0; 2086 2087 next = pat->next; 2088 pat->next = NULL; 2089 name = NULL; 2090 2091 pat->template = cur; 2092 if (mode != NULL) 2093 pat->mode = xmlDictLookup(style->dict, mode, -1); 2094 if (modeURI != NULL) 2095 pat->modeURI = xmlDictLookup(style->dict, modeURI, -1); 2096 if (priority != XSLT_PAT_NO_PRIORITY) 2097 pat->priority = priority; 2098 2099 /* 2100 * insert it in the hash table list corresponding to its lookup name 2101 */ 2102 switch (pat->steps[0].op) { 2103 case XSLT_OP_ATTR: 2104 if (pat->steps[0].value != NULL) 2105 name = pat->steps[0].value; 2106 else 2107 top = &(style->attrMatch); 2108 break; 2109 case XSLT_OP_PARENT: 2110 case XSLT_OP_ANCESTOR: 2111 top = &(style->elemMatch); 2112 break; 2113 case XSLT_OP_ROOT: 2114 top = &(style->rootMatch); 2115 break; 2116 case XSLT_OP_KEY: 2117 top = &(style->keyMatch); 2118 break; 2119 case XSLT_OP_ID: 2120 /* TODO optimize ID !!! */ 2121 case XSLT_OP_NS: 2122 case XSLT_OP_ALL: 2123 top = &(style->elemMatch); 2124 break; 2125 case XSLT_OP_END: 2126 case XSLT_OP_PREDICATE: 2127 xsltTransformError(NULL, style, NULL, 2128 "xsltAddTemplate: invalid compiled pattern\n"); 2129 xsltFreeCompMatch(pat); 2130 return(-1); 2131 /* 2132 * TODO: some flags at the top level about type based patterns 2133 * would be faster than inclusion in the hash table. 2134 */ 2135 case XSLT_OP_PI: 2136 if (pat->steps[0].value != NULL) 2137 name = pat->steps[0].value; 2138 else 2139 top = &(style->piMatch); 2140 break; 2141 case XSLT_OP_COMMENT: 2142 top = &(style->commentMatch); 2143 break; 2144 case XSLT_OP_TEXT: 2145 top = &(style->textMatch); 2146 break; 2147 case XSLT_OP_ELEM: 2148 case XSLT_OP_NODE: 2149 if (pat->steps[0].value != NULL) 2150 name = pat->steps[0].value; 2151 else 2152 top = &(style->elemMatch); 2153 break; 2154 } 2155 if (name != NULL) { 2156 if (style->templatesHash == NULL) { 2157 style->templatesHash = xmlHashCreate(1024); 2158 success = (style->templatesHash != NULL) && 2159 (xmlHashAddEntry3(style->templatesHash, name, mode, 2160 modeURI, pat) >= 0); 2161 } else { 2162 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash, 2163 name, mode, modeURI); 2164 if (list == NULL) { 2165 success = (xmlHashAddEntry3(style->templatesHash, name, 2166 mode, modeURI, pat) >= 0); 2167 } else { 2168 /* 2169 * Note '<=' since one must choose among the matching 2170 * template rules that are left, the one that occurs 2171 * last in the stylesheet 2172 */ 2173 if (list->priority <= pat->priority) { 2174 pat->next = list; 2175 xmlHashUpdateEntry3(style->templatesHash, name, 2176 mode, modeURI, pat, NULL); 2177 } else { 2178 while (list->next != NULL) { 2179 if (list->next->priority <= pat->priority) 2180 break; 2181 list = list->next; 2182 } 2183 pat->next = list->next; 2184 list->next = pat; 2185 } 2186 success = 1; 2187 } 2188 } 2189 } else if (top != NULL) { 2190 list = *top; 2191 if (list == NULL) { 2192 *top = pat; 2193 pat->next = NULL; 2194 } else if (list->priority <= pat->priority) { 2195 pat->next = list; 2196 *top = pat; 2197 } else { 2198 while (list->next != NULL) { 2199 if (list->next->priority <= pat->priority) 2200 break; 2201 list = list->next; 2202 } 2203 pat->next = list->next; 2204 list->next = pat; 2205 } 2206 success = 1; 2207 } 2208 if (success == 0) { 2209 xsltTransformError(NULL, style, NULL, 2210 "xsltAddTemplate: invalid compiled pattern\n"); 2211 xsltFreeCompMatch(pat); 2212 xsltFreeCompMatchList(next); 2213 return(-1); 2214 } 2215#ifdef WITH_XSLT_DEBUG_PATTERN 2216 if (mode) 2217 xsltGenericDebug(xsltGenericDebugContext, 2218 "added pattern : '%s' mode '%s' priority %f\n", 2219 pat->pattern, pat->mode, pat->priority); 2220 else 2221 xsltGenericDebug(xsltGenericDebugContext, 2222 "added pattern : '%s' priority %f\n", 2223 pat->pattern, pat->priority); 2224#endif 2225 2226 pat = next; 2227 } 2228 return(0); 2229} 2230 2231static int 2232xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode) 2233{ 2234 if ((ctxt == NULL) || (contextNode == NULL)) { 2235 xsltTransformError(ctxt, NULL, ctxt->inst, 2236 "Internal error in xsltComputeAllKeys(): " 2237 "Bad arguments.\n"); 2238 return(-1); 2239 } 2240 2241 if (ctxt->document == NULL) { 2242 /* 2243 * The document info will only be NULL if we have a RTF. 2244 */ 2245 if (contextNode->doc->_private != NULL) 2246 goto doc_info_mismatch; 2247 /* 2248 * On-demand creation of the document info (needed for keys). 2249 */ 2250 ctxt->document = xsltNewDocument(ctxt, contextNode->doc); 2251 if (ctxt->document == NULL) 2252 return(-1); 2253 } 2254 return xsltInitAllDocKeys(ctxt); 2255 2256doc_info_mismatch: 2257 xsltTransformError(ctxt, NULL, ctxt->inst, 2258 "Internal error in xsltComputeAllKeys(): " 2259 "The context's document info doesn't match the " 2260 "document info of the current result tree.\n"); 2261 ctxt->state = XSLT_STATE_STOPPED; 2262 return(-1); 2263} 2264 2265/** 2266 * xsltGetTemplate: 2267 * @ctxt: a XSLT process context 2268 * @node: the node being processed 2269 * @style: the current style 2270 * 2271 * Finds the template applying to this node, if @style is non-NULL 2272 * it means one needs to look for the next imported template in scope. 2273 * 2274 * Returns the xsltTemplatePtr or NULL if not found 2275 */ 2276xsltTemplatePtr 2277xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, 2278 xsltStylesheetPtr style) 2279{ 2280 xsltStylesheetPtr curstyle; 2281 xsltTemplatePtr ret = NULL; 2282 const xmlChar *name = NULL; 2283 xsltCompMatchPtr list = NULL; 2284 float priority; 2285 2286 if ((ctxt == NULL) || (node == NULL)) 2287 return(NULL); 2288 2289 if (style == NULL) { 2290 curstyle = ctxt->style; 2291 } else { 2292 curstyle = xsltNextImport(style); 2293 } 2294 2295 while ((curstyle != NULL) && (curstyle != style)) { 2296 priority = XSLT_PAT_NO_PRIORITY; 2297 /* TODO : handle IDs/keys here ! */ 2298 if (curstyle->templatesHash != NULL) { 2299 /* 2300 * Use the top name as selector 2301 */ 2302 switch (node->type) { 2303 case XML_ELEMENT_NODE: 2304 if (node->name[0] == ' ') 2305 break; 2306 /* Intentional fall-through */ 2307 case XML_ATTRIBUTE_NODE: 2308 case XML_PI_NODE: 2309 name = node->name; 2310 break; 2311 case XML_DOCUMENT_NODE: 2312 case XML_HTML_DOCUMENT_NODE: 2313 case XML_TEXT_NODE: 2314 case XML_CDATA_SECTION_NODE: 2315 case XML_COMMENT_NODE: 2316 case XML_ENTITY_REF_NODE: 2317 case XML_ENTITY_NODE: 2318 case XML_DOCUMENT_TYPE_NODE: 2319 case XML_DOCUMENT_FRAG_NODE: 2320 case XML_NOTATION_NODE: 2321 case XML_DTD_NODE: 2322 case XML_ELEMENT_DECL: 2323 case XML_ATTRIBUTE_DECL: 2324 case XML_ENTITY_DECL: 2325 case XML_NAMESPACE_DECL: 2326 case XML_XINCLUDE_START: 2327 case XML_XINCLUDE_END: 2328 break; 2329 default: 2330 return(NULL); 2331 2332 } 2333 } 2334 if (name != NULL) { 2335 /* 2336 * find the list of applicable expressions based on the name 2337 */ 2338 list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash, 2339 name, ctxt->mode, ctxt->modeURI); 2340 } else 2341 list = NULL; 2342 while (list != NULL) { 2343 if (xsltTestCompMatch(ctxt, list, node, 2344 ctxt->mode, ctxt->modeURI) == 1) { 2345 ret = list->template; 2346 priority = list->priority; 2347 break; 2348 } 2349 list = list->next; 2350 } 2351 list = NULL; 2352 2353 /* 2354 * find alternate generic matches 2355 */ 2356 switch (node->type) { 2357 case XML_ELEMENT_NODE: 2358 if (node->name[0] == ' ') 2359 list = curstyle->rootMatch; 2360 else 2361 list = curstyle->elemMatch; 2362 break; 2363 case XML_ATTRIBUTE_NODE: { 2364 list = curstyle->attrMatch; 2365 break; 2366 } 2367 case XML_PI_NODE: 2368 list = curstyle->piMatch; 2369 break; 2370 case XML_DOCUMENT_NODE: 2371 case XML_HTML_DOCUMENT_NODE: { 2372 list = curstyle->rootMatch; 2373 break; 2374 } 2375 case XML_TEXT_NODE: 2376 case XML_CDATA_SECTION_NODE: 2377 list = curstyle->textMatch; 2378 break; 2379 case XML_COMMENT_NODE: 2380 list = curstyle->commentMatch; 2381 break; 2382 case XML_ENTITY_REF_NODE: 2383 case XML_ENTITY_NODE: 2384 case XML_DOCUMENT_TYPE_NODE: 2385 case XML_DOCUMENT_FRAG_NODE: 2386 case XML_NOTATION_NODE: 2387 case XML_DTD_NODE: 2388 case XML_ELEMENT_DECL: 2389 case XML_ATTRIBUTE_DECL: 2390 case XML_ENTITY_DECL: 2391 case XML_NAMESPACE_DECL: 2392 case XML_XINCLUDE_START: 2393 case XML_XINCLUDE_END: 2394 break; 2395 default: 2396 break; 2397 } 2398 while ((list != NULL) && 2399 ((ret == NULL) || 2400 (list->priority > priority) || 2401 ((list->priority == priority) && 2402 (list->template->position > ret->position)))) { 2403 if (xsltTestCompMatch(ctxt, list, node, 2404 ctxt->mode, ctxt->modeURI) == 1) { 2405 ret = list->template; 2406 priority = list->priority; 2407 break; 2408 } 2409 list = list->next; 2410 } 2411 /* 2412 * Some of the tests for elements can also apply to documents 2413 */ 2414 if ((node->type == XML_DOCUMENT_NODE) || 2415 (node->type == XML_HTML_DOCUMENT_NODE) || 2416 (node->type == XML_TEXT_NODE)) { 2417 list = curstyle->elemMatch; 2418 while ((list != NULL) && 2419 ((ret == NULL) || 2420 (list->priority > priority) || 2421 ((list->priority == priority) && 2422 (list->template->position > ret->position)))) { 2423 if (xsltTestCompMatch(ctxt, list, node, 2424 ctxt->mode, ctxt->modeURI) == 1) { 2425 ret = list->template; 2426 priority = list->priority; 2427 break; 2428 } 2429 list = list->next; 2430 } 2431 } else if ((node->type == XML_PI_NODE) || 2432 (node->type == XML_COMMENT_NODE)) { 2433 list = curstyle->elemMatch; 2434 while ((list != NULL) && 2435 ((ret == NULL) || 2436 (list->priority > priority) || 2437 ((list->priority == priority) && 2438 (list->template->position > ret->position)))) { 2439 if (xsltTestCompMatch(ctxt, list, node, 2440 ctxt->mode, ctxt->modeURI) == 1) { 2441 ret = list->template; 2442 priority = list->priority; 2443 break; 2444 } 2445 list = list->next; 2446 } 2447 } 2448 2449keyed_match: 2450 if (xsltGetSourceNodeFlags(node) & XSLT_SOURCE_NODE_HAS_KEY) { 2451 list = curstyle->keyMatch; 2452 while ((list != NULL) && 2453 ((ret == NULL) || 2454 (list->priority > priority) || 2455 ((list->priority == priority) && 2456 (list->template->position > ret->position)))) { 2457 if (xsltTestCompMatch(ctxt, list, node, 2458 ctxt->mode, ctxt->modeURI) == 1) { 2459 ret = list->template; 2460 priority = list->priority; 2461 break; 2462 } 2463 list = list->next; 2464 } 2465 } 2466 else if (ctxt->hasTemplKeyPatterns && 2467 ((ctxt->document == NULL) || 2468 (ctxt->document->nbKeysComputed < ctxt->nbKeys))) 2469 { 2470 /* 2471 * Compute all remaining keys for this document. 2472 * 2473 * REVISIT TODO: I think this could be further optimized. 2474 */ 2475 if (xsltComputeAllKeys(ctxt, node) == -1) 2476 goto error; 2477 2478 if (xsltGetSourceNodeFlags(node) & XSLT_SOURCE_NODE_HAS_KEY) 2479 goto keyed_match; 2480 } 2481 if (ret != NULL) 2482 return(ret); 2483 2484 /* 2485 * Cycle on next curstylesheet import. 2486 */ 2487 curstyle = xsltNextImport(curstyle); 2488 } 2489 2490error: 2491 return(NULL); 2492} 2493 2494/** 2495 * xsltCleanupTemplates: 2496 * @style: an XSLT stylesheet 2497 * 2498 * Cleanup the state of the templates used by the stylesheet and 2499 * the ones it imports. 2500 */ 2501void 2502xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) { 2503} 2504 2505/** 2506 * xsltFreeTemplateHashes: 2507 * @style: an XSLT stylesheet 2508 * 2509 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism 2510 */ 2511void 2512xsltFreeTemplateHashes(xsltStylesheetPtr style) { 2513 if (style->templatesHash != NULL) 2514 xmlHashFree(style->templatesHash, xsltFreeCompMatchListEntry); 2515 if (style->rootMatch != NULL) 2516 xsltFreeCompMatchList(style->rootMatch); 2517 if (style->keyMatch != NULL) 2518 xsltFreeCompMatchList(style->keyMatch); 2519 if (style->elemMatch != NULL) 2520 xsltFreeCompMatchList(style->elemMatch); 2521 if (style->attrMatch != NULL) 2522 xsltFreeCompMatchList(style->attrMatch); 2523 if (style->parentMatch != NULL) 2524 xsltFreeCompMatchList(style->parentMatch); 2525 if (style->textMatch != NULL) 2526 xsltFreeCompMatchList(style->textMatch); 2527 if (style->piMatch != NULL) 2528 xsltFreeCompMatchList(style->piMatch); 2529 if (style->commentMatch != NULL) 2530 xsltFreeCompMatchList(style->commentMatch); 2531 if (style->namedTemplates != NULL) 2532 xmlHashFree(style->namedTemplates, NULL); 2533} 2534