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