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