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