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