1 /* 2 * pattern.c: Implementation of selectors for nodes 3 * 4 * Reference: 5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/ 6 * to some extent 7 * http://www.w3.org/TR/1999/REC-xml-19991116 8 * 9 * See Copyright for the status of this software. 10 * 11 * daniel@veillard.com 12 */ 13 14 /* 15 * TODO: 16 * - compilation flags to check for specific syntaxes 17 * using flags of xmlPatterncompile() 18 * - making clear how pattern starting with / or . need to be handled, 19 * currently push(NULL, NULL) means a reset of the streaming context 20 * and indicating we are on / (the document node), probably need 21 * something similar for . 22 * - get rid of the "compile" starting with lowercase 23 * - DONE (2006-05-16): get rid of the Strdup/Strndup in case of dictionary 24 */ 25 26 #define IN_LIBXML 27 #include "libxml.h" 28 29 #include <string.h> 30 #include <libxml/xmlmemory.h> 31 #include <libxml/tree.h> 32 #include <libxml/hash.h> 33 #include <libxml/dict.h> 34 #include <libxml/xmlerror.h> 35 #include <libxml/parserInternals.h> 36 #include <libxml/pattern.h> 37 38 #ifdef LIBXML_PATTERN_ENABLED 39 40 /* #define DEBUG_STREAMING */ 41 42 #ifdef ERROR 43 #undef ERROR 44 #endif 45 #define ERROR(a, b, c, d) 46 #define ERROR5(a, b, c, d, e) 47 48 #define XML_STREAM_STEP_DESC 1 49 #define XML_STREAM_STEP_FINAL 2 50 #define XML_STREAM_STEP_ROOT 4 51 #define XML_STREAM_STEP_ATTR 8 52 #define XML_STREAM_STEP_NODE 16 53 #define XML_STREAM_STEP_IN_SET 32 54 55 /* 56 * NOTE: Those private flags (XML_STREAM_xxx) are used 57 * in _xmlStreamCtxt->flag. They extend the public 58 * xmlPatternFlags, so be careful not to interfere with the 59 * reserved values for xmlPatternFlags. 60 */ 61 #define XML_STREAM_FINAL_IS_ANY_NODE 1<<14 62 #define XML_STREAM_FROM_ROOT 1<<15 63 #define XML_STREAM_DESC 1<<16 64 65 /* 66 * XML_STREAM_ANY_NODE is used for comparison against 67 * xmlElementType enums, to indicate a node of any type. 68 */ 69 #define XML_STREAM_ANY_NODE 100 70 71 #define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \ 72 XML_PATTERN_XSSEL | \ 73 XML_PATTERN_XSFIELD) 74 75 #define XML_STREAM_XS_IDC(c) ((c)->flags & \ 76 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD)) 77 78 #define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL) 79 80 #define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD) 81 82 #define XML_PAT_COPY_NSNAME(c, r, nsname) \ 83 if ((c)->comp->dict) \ 84 r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \ 85 else r = xmlStrdup(BAD_CAST nsname); 86 87 #define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r); 88 89 typedef struct _xmlStreamStep xmlStreamStep; 90 typedef xmlStreamStep *xmlStreamStepPtr; 91 struct _xmlStreamStep { 92 int flags; /* properties of that step */ 93 const xmlChar *name; /* first string value if NULL accept all */ 94 const xmlChar *ns; /* second string value */ 95 int nodeType; /* type of node */ 96 }; 97 98 typedef struct _xmlStreamComp xmlStreamComp; 99 typedef xmlStreamComp *xmlStreamCompPtr; 100 struct _xmlStreamComp { 101 xmlDict *dict; /* the dictionary if any */ 102 int nbStep; /* number of steps in the automata */ 103 int maxStep; /* allocated number of steps */ 104 xmlStreamStepPtr steps; /* the array of steps */ 105 int flags; 106 }; 107 108 struct _xmlStreamCtxt { 109 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */ 110 xmlStreamCompPtr comp; /* the compiled stream */ 111 int nbState; /* number of states in the automata */ 112 int maxState; /* allocated number of states */ 113 int level; /* how deep are we ? */ 114 int *states; /* the array of step indexes */ 115 int flags; /* validation options */ 116 int blockLevel; 117 }; 118 119 static void xmlFreeStreamComp(xmlStreamCompPtr comp); 120 121 /* 122 * Types are private: 123 */ 124 125 typedef enum { 126 XML_OP_END=0, 127 XML_OP_ROOT, 128 XML_OP_ELEM, 129 XML_OP_CHILD, 130 XML_OP_ATTR, 131 XML_OP_PARENT, 132 XML_OP_ANCESTOR, 133 XML_OP_NS, 134 XML_OP_ALL 135 } xmlPatOp; 136 137 138 typedef struct _xmlStepState xmlStepState; 139 typedef xmlStepState *xmlStepStatePtr; 140 struct _xmlStepState { 141 int step; 142 xmlNodePtr node; 143 }; 144 145 typedef struct _xmlStepStates xmlStepStates; 146 typedef xmlStepStates *xmlStepStatesPtr; 147 struct _xmlStepStates { 148 int nbstates; 149 int maxstates; 150 xmlStepStatePtr states; 151 }; 152 153 typedef struct _xmlStepOp xmlStepOp; 154 typedef xmlStepOp *xmlStepOpPtr; 155 struct _xmlStepOp { 156 xmlPatOp op; 157 const xmlChar *value; 158 const xmlChar *value2; /* The namespace name */ 159 }; 160 161 #define PAT_FROM_ROOT (1<<8) 162 #define PAT_FROM_CUR (1<<9) 163 164 struct _xmlPattern { 165 void *data; /* the associated template */ 166 xmlDictPtr dict; /* the optional dictionary */ 167 struct _xmlPattern *next; /* next pattern if | is used */ 168 const xmlChar *pattern; /* the pattern */ 169 int flags; /* flags */ 170 int nbStep; 171 int maxStep; 172 xmlStepOpPtr steps; /* ops for computation */ 173 xmlStreamCompPtr stream; /* the streaming data if any */ 174 }; 175 176 typedef struct _xmlPatParserContext xmlPatParserContext; 177 typedef xmlPatParserContext *xmlPatParserContextPtr; 178 struct _xmlPatParserContext { 179 const xmlChar *cur; /* the current char being parsed */ 180 const xmlChar *base; /* the full expression */ 181 int error; /* error code */ 182 xmlDictPtr dict; /* the dictionary if any */ 183 xmlPatternPtr comp; /* the result */ 184 xmlNodePtr elem; /* the current node if any */ 185 const xmlChar **namespaces; /* the namespaces definitions */ 186 int nb_namespaces; /* the number of namespaces */ 187 }; 188 189 /************************************************************************ 190 * * 191 * Type functions * 192 * * 193 ************************************************************************/ 194 195 /** 196 * xmlNewPattern: 197 * 198 * Create a new XSLT Pattern 199 * 200 * Returns the newly allocated xmlPatternPtr or NULL in case of error 201 */ 202 static xmlPatternPtr 203 xmlNewPattern(void) { 204 xmlPatternPtr cur; 205 206 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern)); 207 if (cur == NULL) { 208 ERROR(NULL, NULL, NULL, 209 "xmlNewPattern : malloc failed\n"); 210 return(NULL); 211 } 212 memset(cur, 0, sizeof(xmlPattern)); 213 cur->maxStep = 10; 214 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp)); 215 if (cur->steps == NULL) { 216 xmlFree(cur); 217 ERROR(NULL, NULL, NULL, 218 "xmlNewPattern : malloc failed\n"); 219 return(NULL); 220 } 221 return(cur); 222 } 223 224 /** 225 * xmlFreePattern: 226 * @comp: an XSLT comp 227 * 228 * Free up the memory allocated by @comp 229 */ 230 void 231 xmlFreePattern(xmlPatternPtr comp) { 232 xmlFreePatternList(comp); 233 } 234 235 static void 236 xmlFreePatternInternal(xmlPatternPtr comp) { 237 xmlStepOpPtr op; 238 int i; 239 240 if (comp == NULL) 241 return; 242 if (comp->stream != NULL) 243 xmlFreeStreamComp(comp->stream); 244 if (comp->pattern != NULL) 245 xmlFree((xmlChar *)comp->pattern); 246 if (comp->steps != NULL) { 247 if (comp->dict == NULL) { 248 for (i = 0;i < comp->nbStep;i++) { 249 op = &comp->steps[i]; 250 if (op->value != NULL) 251 xmlFree((xmlChar *) op->value); 252 if (op->value2 != NULL) 253 xmlFree((xmlChar *) op->value2); 254 } 255 } 256 xmlFree(comp->steps); 257 } 258 if (comp->dict != NULL) 259 xmlDictFree(comp->dict); 260 261 memset(comp, -1, sizeof(xmlPattern)); 262 xmlFree(comp); 263 } 264 265 /** 266 * xmlFreePatternList: 267 * @comp: an XSLT comp list 268 * 269 * Free up the memory allocated by all the elements of @comp 270 */ 271 void 272 xmlFreePatternList(xmlPatternPtr comp) { 273 xmlPatternPtr cur; 274 275 while (comp != NULL) { 276 cur = comp; 277 comp = comp->next; 278 cur->next = NULL; 279 xmlFreePatternInternal(cur); 280 } 281 } 282 283 /** 284 * xmlNewPatParserContext: 285 * @pattern: the pattern context 286 * @dict: the inherited dictionary or NULL 287 * @namespaces: the prefix definitions, array of [URI, prefix] terminated 288 * with [NULL, NULL] or NULL if no namespace is used 289 * 290 * Create a new XML pattern parser context 291 * 292 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error 293 */ 294 static xmlPatParserContextPtr 295 xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict, 296 const xmlChar **namespaces) { 297 xmlPatParserContextPtr cur; 298 299 if (pattern == NULL) 300 return(NULL); 301 302 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext)); 303 if (cur == NULL) { 304 ERROR(NULL, NULL, NULL, 305 "xmlNewPatParserContext : malloc failed\n"); 306 return(NULL); 307 } 308 memset(cur, 0, sizeof(xmlPatParserContext)); 309 cur->dict = dict; 310 cur->cur = pattern; 311 cur->base = pattern; 312 if (namespaces != NULL) { 313 int i; 314 for (i = 0;namespaces[2 * i] != NULL;i++) 315 ; 316 cur->nb_namespaces = i; 317 } else { 318 cur->nb_namespaces = 0; 319 } 320 cur->namespaces = namespaces; 321 return(cur); 322 } 323 324 /** 325 * xmlFreePatParserContext: 326 * @ctxt: an XSLT parser context 327 * 328 * Free up the memory allocated by @ctxt 329 */ 330 static void 331 xmlFreePatParserContext(xmlPatParserContextPtr ctxt) { 332 if (ctxt == NULL) 333 return; 334 memset(ctxt, -1, sizeof(xmlPatParserContext)); 335 xmlFree(ctxt); 336 } 337 338 /** 339 * xmlPatternAdd: 340 * @comp: the compiled match expression 341 * @op: an op 342 * @value: the first value 343 * @value2: the second value 344 * 345 * Add a step to an XSLT Compiled Match 346 * 347 * Returns -1 in case of failure, 0 otherwise. 348 */ 349 static int 350 xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED, 351 xmlPatternPtr comp, 352 xmlPatOp op, xmlChar * value, xmlChar * value2) 353 { 354 if (comp->nbStep >= comp->maxStep) { 355 xmlStepOpPtr temp; 356 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 * 357 sizeof(xmlStepOp)); 358 if (temp == NULL) { 359 ERROR(ctxt, NULL, NULL, 360 "xmlPatternAdd: realloc failed\n"); 361 return (-1); 362 } 363 comp->steps = temp; 364 comp->maxStep *= 2; 365 } 366 comp->steps[comp->nbStep].op = op; 367 comp->steps[comp->nbStep].value = value; 368 comp->steps[comp->nbStep].value2 = value2; 369 comp->nbStep++; 370 return (0); 371 } 372 373 #if 0 374 /** 375 * xsltSwapTopPattern: 376 * @comp: the compiled match expression 377 * 378 * reverse the two top steps. 379 */ 380 static void 381 xsltSwapTopPattern(xmlPatternPtr comp) { 382 int i; 383 int j = comp->nbStep - 1; 384 385 if (j > 0) { 386 register const xmlChar *tmp; 387 register xmlPatOp op; 388 i = j - 1; 389 tmp = comp->steps[i].value; 390 comp->steps[i].value = comp->steps[j].value; 391 comp->steps[j].value = tmp; 392 tmp = comp->steps[i].value2; 393 comp->steps[i].value2 = comp->steps[j].value2; 394 comp->steps[j].value2 = tmp; 395 op = comp->steps[i].op; 396 comp->steps[i].op = comp->steps[j].op; 397 comp->steps[j].op = op; 398 } 399 } 400 #endif 401 402 /** 403 * xmlReversePattern: 404 * @comp: the compiled match expression 405 * 406 * reverse all the stack of expressions 407 * 408 * returns 0 in case of success and -1 in case of error. 409 */ 410 static int 411 xmlReversePattern(xmlPatternPtr comp) { 412 int i, j; 413 414 /* 415 * remove the leading // for //a or .//a 416 */ 417 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) { 418 for (i = 0, j = 1;j < comp->nbStep;i++,j++) { 419 comp->steps[i].value = comp->steps[j].value; 420 comp->steps[i].value2 = comp->steps[j].value2; 421 comp->steps[i].op = comp->steps[j].op; 422 } 423 comp->nbStep--; 424 } 425 if (comp->nbStep >= comp->maxStep) { 426 xmlStepOpPtr temp; 427 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 * 428 sizeof(xmlStepOp)); 429 if (temp == NULL) { 430 ERROR(ctxt, NULL, NULL, 431 "xmlReversePattern: realloc failed\n"); 432 return (-1); 433 } 434 comp->steps = temp; 435 comp->maxStep *= 2; 436 } 437 i = 0; 438 j = comp->nbStep - 1; 439 while (j > i) { 440 register const xmlChar *tmp; 441 register xmlPatOp op; 442 tmp = comp->steps[i].value; 443 comp->steps[i].value = comp->steps[j].value; 444 comp->steps[j].value = tmp; 445 tmp = comp->steps[i].value2; 446 comp->steps[i].value2 = comp->steps[j].value2; 447 comp->steps[j].value2 = tmp; 448 op = comp->steps[i].op; 449 comp->steps[i].op = comp->steps[j].op; 450 comp->steps[j].op = op; 451 j--; 452 i++; 453 } 454 comp->steps[comp->nbStep].value = NULL; 455 comp->steps[comp->nbStep].value2 = NULL; 456 comp->steps[comp->nbStep++].op = XML_OP_END; 457 return(0); 458 } 459 460 /************************************************************************ 461 * * 462 * The interpreter for the precompiled patterns * 463 * * 464 ************************************************************************/ 465 466 static int 467 xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) { 468 if ((states->states == NULL) || (states->maxstates <= 0)) { 469 states->maxstates = 4; 470 states->nbstates = 0; 471 states->states = xmlMalloc(4 * sizeof(xmlStepState)); 472 } 473 else if (states->maxstates <= states->nbstates) { 474 xmlStepState *tmp; 475 476 tmp = (xmlStepStatePtr) xmlRealloc(states->states, 477 2 * states->maxstates * sizeof(xmlStepState)); 478 if (tmp == NULL) 479 return(-1); 480 states->states = tmp; 481 states->maxstates *= 2; 482 } 483 states->states[states->nbstates].step = step; 484 states->states[states->nbstates++].node = node; 485 #if 0 486 fprintf(stderr, "Push: %d, %s\n", step, node->name); 487 #endif 488 return(0); 489 } 490 491 /** 492 * xmlPatMatch: 493 * @comp: the precompiled pattern 494 * @node: a node 495 * 496 * Test whether the node matches the pattern 497 * 498 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure 499 */ 500 static int 501 xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) { 502 int i; 503 xmlStepOpPtr step; 504 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */ 505 506 if ((comp == NULL) || (node == NULL)) return(-1); 507 i = 0; 508 restart: 509 for (;i < comp->nbStep;i++) { 510 step = &comp->steps[i]; 511 switch (step->op) { 512 case XML_OP_END: 513 goto found; 514 case XML_OP_ROOT: 515 if (node->type == XML_NAMESPACE_DECL) 516 goto rollback; 517 node = node->parent; 518 if ((node->type == XML_DOCUMENT_NODE) || 519 #ifdef LIBXML_DOCB_ENABLED 520 (node->type == XML_DOCB_DOCUMENT_NODE) || 521 #endif 522 (node->type == XML_HTML_DOCUMENT_NODE)) 523 continue; 524 goto rollback; 525 case XML_OP_ELEM: 526 if (node->type != XML_ELEMENT_NODE) 527 goto rollback; 528 if (step->value == NULL) 529 continue; 530 if (step->value[0] != node->name[0]) 531 goto rollback; 532 if (!xmlStrEqual(step->value, node->name)) 533 goto rollback; 534 535 /* Namespace test */ 536 if (node->ns == NULL) { 537 if (step->value2 != NULL) 538 goto rollback; 539 } else if (node->ns->href != NULL) { 540 if (step->value2 == NULL) 541 goto rollback; 542 if (!xmlStrEqual(step->value2, node->ns->href)) 543 goto rollback; 544 } 545 continue; 546 case XML_OP_CHILD: { 547 xmlNodePtr lst; 548 549 if ((node->type != XML_ELEMENT_NODE) && 550 (node->type != XML_DOCUMENT_NODE) && 551 #ifdef LIBXML_DOCB_ENABLED 552 (node->type != XML_DOCB_DOCUMENT_NODE) && 553 #endif 554 (node->type != XML_HTML_DOCUMENT_NODE)) 555 goto rollback; 556 557 lst = node->children; 558 559 if (step->value != NULL) { 560 while (lst != NULL) { 561 if ((lst->type == XML_ELEMENT_NODE) && 562 (step->value[0] == lst->name[0]) && 563 (xmlStrEqual(step->value, lst->name))) 564 break; 565 lst = lst->next; 566 } 567 if (lst != NULL) 568 continue; 569 } 570 goto rollback; 571 } 572 case XML_OP_ATTR: 573 if (node->type != XML_ATTRIBUTE_NODE) 574 goto rollback; 575 if (step->value != NULL) { 576 if (step->value[0] != node->name[0]) 577 goto rollback; 578 if (!xmlStrEqual(step->value, node->name)) 579 goto rollback; 580 } 581 /* Namespace test */ 582 if (node->ns == NULL) { 583 if (step->value2 != NULL) 584 goto rollback; 585 } else if (step->value2 != NULL) { 586 if (!xmlStrEqual(step->value2, node->ns->href)) 587 goto rollback; 588 } 589 continue; 590 case XML_OP_PARENT: 591 if ((node->type == XML_DOCUMENT_NODE) || 592 (node->type == XML_HTML_DOCUMENT_NODE) || 593 #ifdef LIBXML_DOCB_ENABLED 594 (node->type == XML_DOCB_DOCUMENT_NODE) || 595 #endif 596 (node->type == XML_NAMESPACE_DECL)) 597 goto rollback; 598 node = node->parent; 599 if (node == NULL) 600 goto rollback; 601 if (step->value == NULL) 602 continue; 603 if (step->value[0] != node->name[0]) 604 goto rollback; 605 if (!xmlStrEqual(step->value, node->name)) 606 goto rollback; 607 /* Namespace test */ 608 if (node->ns == NULL) { 609 if (step->value2 != NULL) 610 goto rollback; 611 } else if (node->ns->href != NULL) { 612 if (step->value2 == NULL) 613 goto rollback; 614 if (!xmlStrEqual(step->value2, node->ns->href)) 615 goto rollback; 616 } 617 continue; 618 case XML_OP_ANCESTOR: 619 /* TODO: implement coalescing of ANCESTOR/NODE ops */ 620 if (step->value == NULL) { 621 i++; 622 step = &comp->steps[i]; 623 if (step->op == XML_OP_ROOT) 624 goto found; 625 if (step->op != XML_OP_ELEM) 626 goto rollback; 627 if (step->value == NULL) 628 return(-1); 629 } 630 if (node == NULL) 631 goto rollback; 632 if ((node->type == XML_DOCUMENT_NODE) || 633 (node->type == XML_HTML_DOCUMENT_NODE) || 634 #ifdef LIBXML_DOCB_ENABLED 635 (node->type == XML_DOCB_DOCUMENT_NODE) || 636 #endif 637 (node->type == XML_NAMESPACE_DECL)) 638 goto rollback; 639 node = node->parent; 640 while (node != NULL) { 641 if ((node->type == XML_ELEMENT_NODE) && 642 (step->value[0] == node->name[0]) && 643 (xmlStrEqual(step->value, node->name))) { 644 /* Namespace test */ 645 if (node->ns == NULL) { 646 if (step->value2 == NULL) 647 break; 648 } else if (node->ns->href != NULL) { 649 if ((step->value2 != NULL) && 650 (xmlStrEqual(step->value2, node->ns->href))) 651 break; 652 } 653 } 654 node = node->parent; 655 } 656 if (node == NULL) 657 goto rollback; 658 /* 659 * prepare a potential rollback from here 660 * for ancestors of that node. 661 */ 662 if (step->op == XML_OP_ANCESTOR) 663 xmlPatPushState(&states, i, node); 664 else 665 xmlPatPushState(&states, i - 1, node); 666 continue; 667 case XML_OP_NS: 668 if (node->type != XML_ELEMENT_NODE) 669 goto rollback; 670 if (node->ns == NULL) { 671 if (step->value != NULL) 672 goto rollback; 673 } else if (node->ns->href != NULL) { 674 if (step->value == NULL) 675 goto rollback; 676 if (!xmlStrEqual(step->value, node->ns->href)) 677 goto rollback; 678 } 679 break; 680 case XML_OP_ALL: 681 if (node->type != XML_ELEMENT_NODE) 682 goto rollback; 683 break; 684 } 685 } 686 found: 687 if (states.states != NULL) { 688 /* Free the rollback states */ 689 xmlFree(states.states); 690 } 691 return(1); 692 rollback: 693 /* got an error try to rollback */ 694 if (states.states == NULL) 695 return(0); 696 if (states.nbstates <= 0) { 697 xmlFree(states.states); 698 return(0); 699 } 700 states.nbstates--; 701 i = states.states[states.nbstates].step; 702 node = states.states[states.nbstates].node; 703 #if 0 704 fprintf(stderr, "Pop: %d, %s\n", i, node->name); 705 #endif 706 goto restart; 707 } 708 709 /************************************************************************ 710 * * 711 * Dedicated parser for templates * 712 * * 713 ************************************************************************/ 714 715 #define TODO \ 716 xmlGenericError(xmlGenericErrorContext, \ 717 "Unimplemented block at %s:%d\n", \ 718 __FILE__, __LINE__); 719 #define CUR (*ctxt->cur) 720 #define SKIP(val) ctxt->cur += (val) 721 #define NXT(val) ctxt->cur[(val)] 722 #define PEEKPREV(val) ctxt->cur[-(val)] 723 #define CUR_PTR ctxt->cur 724 725 #define SKIP_BLANKS \ 726 while (IS_BLANK_CH(CUR)) NEXT 727 728 #define CURRENT (*ctxt->cur) 729 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) 730 731 732 #define PUSH(op, val, val2) \ 733 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error; 734 735 #define XSLT_ERROR(X) \ 736 { xsltError(ctxt, __FILE__, __LINE__, X); \ 737 ctxt->error = (X); return; } 738 739 #define XSLT_ERROR0(X) \ 740 { xsltError(ctxt, __FILE__, __LINE__, X); \ 741 ctxt->error = (X); return(0); } 742 743 #if 0 744 /** 745 * xmlPatScanLiteral: 746 * @ctxt: the XPath Parser context 747 * 748 * Parse an XPath Literal: 749 * 750 * [29] Literal ::= '"' [^"]* '"' 751 * | "'" [^']* "'" 752 * 753 * Returns the Literal parsed or NULL 754 */ 755 756 static xmlChar * 757 xmlPatScanLiteral(xmlPatParserContextPtr ctxt) { 758 const xmlChar *q, *cur; 759 xmlChar *ret = NULL; 760 int val, len; 761 762 SKIP_BLANKS; 763 if (CUR == '"') { 764 NEXT; 765 cur = q = CUR_PTR; 766 val = xmlStringCurrentChar(NULL, cur, &len); 767 while ((IS_CHAR(val)) && (val != '"')) { 768 cur += len; 769 val = xmlStringCurrentChar(NULL, cur, &len); 770 } 771 if (!IS_CHAR(val)) { 772 ctxt->error = 1; 773 return(NULL); 774 } else { 775 if (ctxt->dict) 776 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); 777 else 778 ret = xmlStrndup(q, cur - q); 779 } 780 cur += len; 781 CUR_PTR = cur; 782 } else if (CUR == '\'') { 783 NEXT; 784 cur = q = CUR_PTR; 785 val = xmlStringCurrentChar(NULL, cur, &len); 786 while ((IS_CHAR(val)) && (val != '\'')) { 787 cur += len; 788 val = xmlStringCurrentChar(NULL, cur, &len); 789 } 790 if (!IS_CHAR(val)) { 791 ctxt->error = 1; 792 return(NULL); 793 } else { 794 if (ctxt->dict) 795 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); 796 else 797 ret = xmlStrndup(q, cur - q); 798 } 799 cur += len; 800 CUR_PTR = cur; 801 } else { 802 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */ 803 ctxt->error = 1; 804 return(NULL); 805 } 806 return(ret); 807 } 808 #endif 809 810 /** 811 * xmlPatScanName: 812 * @ctxt: the XPath Parser context 813 * 814 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | 815 * CombiningChar | Extender 816 * 817 * [5] Name ::= (Letter | '_' | ':') (NameChar)* 818 * 819 * [6] Names ::= Name (S Name)* 820 * 821 * Returns the Name parsed or NULL 822 */ 823 824 static xmlChar * 825 xmlPatScanName(xmlPatParserContextPtr ctxt) { 826 const xmlChar *q, *cur; 827 xmlChar *ret = NULL; 828 int val, len; 829 830 SKIP_BLANKS; 831 832 cur = q = CUR_PTR; 833 val = xmlStringCurrentChar(NULL, cur, &len); 834 if (!IS_LETTER(val) && (val != '_') && (val != ':')) 835 return(NULL); 836 837 while ((IS_LETTER(val)) || (IS_DIGIT(val)) || 838 (val == '.') || (val == '-') || 839 (val == '_') || 840 (IS_COMBINING(val)) || 841 (IS_EXTENDER(val))) { 842 cur += len; 843 val = xmlStringCurrentChar(NULL, cur, &len); 844 } 845 if (ctxt->dict) 846 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); 847 else 848 ret = xmlStrndup(q, cur - q); 849 CUR_PTR = cur; 850 return(ret); 851 } 852 853 /** 854 * xmlPatScanNCName: 855 * @ctxt: the XPath Parser context 856 * 857 * Parses a non qualified name 858 * 859 * Returns the Name parsed or NULL 860 */ 861 862 static xmlChar * 863 xmlPatScanNCName(xmlPatParserContextPtr ctxt) { 864 const xmlChar *q, *cur; 865 xmlChar *ret = NULL; 866 int val, len; 867 868 SKIP_BLANKS; 869 870 cur = q = CUR_PTR; 871 val = xmlStringCurrentChar(NULL, cur, &len); 872 if (!IS_LETTER(val) && (val != '_')) 873 return(NULL); 874 875 while ((IS_LETTER(val)) || (IS_DIGIT(val)) || 876 (val == '.') || (val == '-') || 877 (val == '_') || 878 (IS_COMBINING(val)) || 879 (IS_EXTENDER(val))) { 880 cur += len; 881 val = xmlStringCurrentChar(NULL, cur, &len); 882 } 883 if (ctxt->dict) 884 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); 885 else 886 ret = xmlStrndup(q, cur - q); 887 CUR_PTR = cur; 888 return(ret); 889 } 890 891 #if 0 892 /** 893 * xmlPatScanQName: 894 * @ctxt: the XPath Parser context 895 * @prefix: the place to store the prefix 896 * 897 * Parse a qualified name 898 * 899 * Returns the Name parsed or NULL 900 */ 901 902 static xmlChar * 903 xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) { 904 xmlChar *ret = NULL; 905 906 *prefix = NULL; 907 ret = xmlPatScanNCName(ctxt); 908 if (CUR == ':') { 909 *prefix = ret; 910 NEXT; 911 ret = xmlPatScanNCName(ctxt); 912 } 913 return(ret); 914 } 915 #endif 916 917 /** 918 * xmlCompileAttributeTest: 919 * @ctxt: the compilation context 920 * 921 * Compile an attribute test. 922 */ 923 static void 924 xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) { 925 xmlChar *token = NULL; 926 xmlChar *name = NULL; 927 xmlChar *URL = NULL; 928 929 SKIP_BLANKS; 930 name = xmlPatScanNCName(ctxt); 931 if (name == NULL) { 932 if (CUR == '*') { 933 PUSH(XML_OP_ATTR, NULL, NULL); 934 NEXT; 935 } else { 936 ERROR(NULL, NULL, NULL, 937 "xmlCompileAttributeTest : Name expected\n"); 938 ctxt->error = 1; 939 } 940 return; 941 } 942 if (CUR == ':') { 943 int i; 944 xmlChar *prefix = name; 945 946 NEXT; 947 948 if (IS_BLANK_CH(CUR)) { 949 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); 950 XML_PAT_FREE_STRING(ctxt, prefix); 951 ctxt->error = 1; 952 goto error; 953 } 954 /* 955 * This is a namespace match 956 */ 957 token = xmlPatScanName(ctxt); 958 if ((prefix[0] == 'x') && 959 (prefix[1] == 'm') && 960 (prefix[2] == 'l') && 961 (prefix[3] == 0)) 962 { 963 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE); 964 } else { 965 for (i = 0;i < ctxt->nb_namespaces;i++) { 966 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { 967 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) 968 break; 969 } 970 } 971 if (i >= ctxt->nb_namespaces) { 972 ERROR5(NULL, NULL, NULL, 973 "xmlCompileAttributeTest : no namespace bound to prefix %s\n", 974 prefix); 975 XML_PAT_FREE_STRING(ctxt, prefix); 976 ctxt->error = 1; 977 goto error; 978 } 979 } 980 XML_PAT_FREE_STRING(ctxt, prefix); 981 if (token == NULL) { 982 if (CUR == '*') { 983 NEXT; 984 PUSH(XML_OP_ATTR, NULL, URL); 985 } else { 986 ERROR(NULL, NULL, NULL, 987 "xmlCompileAttributeTest : Name expected\n"); 988 ctxt->error = 1; 989 goto error; 990 } 991 } else { 992 PUSH(XML_OP_ATTR, token, URL); 993 } 994 } else { 995 PUSH(XML_OP_ATTR, name, NULL); 996 } 997 return; 998 error: 999 if (URL != NULL) 1000 XML_PAT_FREE_STRING(ctxt, URL) 1001 if (token != NULL) 1002 XML_PAT_FREE_STRING(ctxt, token); 1003 } 1004 1005 /** 1006 * xmlCompileStepPattern: 1007 * @ctxt: the compilation context 1008 * 1009 * Compile the Step Pattern and generates a precompiled 1010 * form suitable for fast matching. 1011 * 1012 * [3] Step ::= '.' | NameTest 1013 * [4] NameTest ::= QName | '*' | NCName ':' '*' 1014 */ 1015 1016 static void 1017 xmlCompileStepPattern(xmlPatParserContextPtr ctxt) { 1018 xmlChar *token = NULL; 1019 xmlChar *name = NULL; 1020 xmlChar *URL = NULL; 1021 int hasBlanks = 0; 1022 1023 SKIP_BLANKS; 1024 if (CUR == '.') { 1025 /* 1026 * Context node. 1027 */ 1028 NEXT; 1029 PUSH(XML_OP_ELEM, NULL, NULL); 1030 return; 1031 } 1032 if (CUR == '@') { 1033 /* 1034 * Attribute test. 1035 */ 1036 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) { 1037 ERROR5(NULL, NULL, NULL, 1038 "Unexpected attribute axis in '%s'.\n", ctxt->base); 1039 ctxt->error = 1; 1040 return; 1041 } 1042 NEXT; 1043 xmlCompileAttributeTest(ctxt); 1044 if (ctxt->error != 0) 1045 goto error; 1046 return; 1047 } 1048 name = xmlPatScanNCName(ctxt); 1049 if (name == NULL) { 1050 if (CUR == '*') { 1051 NEXT; 1052 PUSH(XML_OP_ALL, NULL, NULL); 1053 return; 1054 } else { 1055 ERROR(NULL, NULL, NULL, 1056 "xmlCompileStepPattern : Name expected\n"); 1057 ctxt->error = 1; 1058 return; 1059 } 1060 } 1061 if (IS_BLANK_CH(CUR)) { 1062 hasBlanks = 1; 1063 SKIP_BLANKS; 1064 } 1065 if (CUR == ':') { 1066 NEXT; 1067 if (CUR != ':') { 1068 xmlChar *prefix = name; 1069 int i; 1070 1071 if (hasBlanks || IS_BLANK_CH(CUR)) { 1072 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); 1073 ctxt->error = 1; 1074 goto error; 1075 } 1076 /* 1077 * This is a namespace match 1078 */ 1079 token = xmlPatScanName(ctxt); 1080 if ((prefix[0] == 'x') && 1081 (prefix[1] == 'm') && 1082 (prefix[2] == 'l') && 1083 (prefix[3] == 0)) 1084 { 1085 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE) 1086 } else { 1087 for (i = 0;i < ctxt->nb_namespaces;i++) { 1088 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { 1089 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) 1090 break; 1091 } 1092 } 1093 if (i >= ctxt->nb_namespaces) { 1094 ERROR5(NULL, NULL, NULL, 1095 "xmlCompileStepPattern : no namespace bound to prefix %s\n", 1096 prefix); 1097 ctxt->error = 1; 1098 goto error; 1099 } 1100 } 1101 XML_PAT_FREE_STRING(ctxt, prefix); 1102 name = NULL; 1103 if (token == NULL) { 1104 if (CUR == '*') { 1105 NEXT; 1106 PUSH(XML_OP_NS, URL, NULL); 1107 } else { 1108 ERROR(NULL, NULL, NULL, 1109 "xmlCompileStepPattern : Name expected\n"); 1110 ctxt->error = 1; 1111 goto error; 1112 } 1113 } else { 1114 PUSH(XML_OP_ELEM, token, URL); 1115 } 1116 } else { 1117 NEXT; 1118 if (xmlStrEqual(name, (const xmlChar *) "child")) { 1119 XML_PAT_FREE_STRING(ctxt, name); 1120 name = xmlPatScanName(ctxt); 1121 if (name == NULL) { 1122 if (CUR == '*') { 1123 NEXT; 1124 PUSH(XML_OP_ALL, NULL, NULL); 1125 return; 1126 } else { 1127 ERROR(NULL, NULL, NULL, 1128 "xmlCompileStepPattern : QName expected\n"); 1129 ctxt->error = 1; 1130 goto error; 1131 } 1132 } 1133 if (CUR == ':') { 1134 xmlChar *prefix = name; 1135 int i; 1136 1137 NEXT; 1138 if (IS_BLANK_CH(CUR)) { 1139 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); 1140 ctxt->error = 1; 1141 goto error; 1142 } 1143 /* 1144 * This is a namespace match 1145 */ 1146 token = xmlPatScanName(ctxt); 1147 if ((prefix[0] == 'x') && 1148 (prefix[1] == 'm') && 1149 (prefix[2] == 'l') && 1150 (prefix[3] == 0)) 1151 { 1152 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE) 1153 } else { 1154 for (i = 0;i < ctxt->nb_namespaces;i++) { 1155 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { 1156 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) 1157 break; 1158 } 1159 } 1160 if (i >= ctxt->nb_namespaces) { 1161 ERROR5(NULL, NULL, NULL, 1162 "xmlCompileStepPattern : no namespace bound " 1163 "to prefix %s\n", prefix); 1164 ctxt->error = 1; 1165 goto error; 1166 } 1167 } 1168 XML_PAT_FREE_STRING(ctxt, prefix); 1169 name = NULL; 1170 if (token == NULL) { 1171 if (CUR == '*') { 1172 NEXT; 1173 PUSH(XML_OP_NS, URL, NULL); 1174 } else { 1175 ERROR(NULL, NULL, NULL, 1176 "xmlCompileStepPattern : Name expected\n"); 1177 ctxt->error = 1; 1178 goto error; 1179 } 1180 } else { 1181 PUSH(XML_OP_CHILD, token, URL); 1182 } 1183 } else 1184 PUSH(XML_OP_CHILD, name, NULL); 1185 return; 1186 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) { 1187 XML_PAT_FREE_STRING(ctxt, name) 1188 name = NULL; 1189 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) { 1190 ERROR5(NULL, NULL, NULL, 1191 "Unexpected attribute axis in '%s'.\n", ctxt->base); 1192 ctxt->error = 1; 1193 goto error; 1194 } 1195 xmlCompileAttributeTest(ctxt); 1196 if (ctxt->error != 0) 1197 goto error; 1198 return; 1199 } else { 1200 ERROR5(NULL, NULL, NULL, 1201 "The 'element' or 'attribute' axis is expected.\n", NULL); 1202 ctxt->error = 1; 1203 goto error; 1204 } 1205 } 1206 } else if (CUR == '*') { 1207 if (name != NULL) { 1208 ctxt->error = 1; 1209 goto error; 1210 } 1211 NEXT; 1212 PUSH(XML_OP_ALL, token, NULL); 1213 } else { 1214 PUSH(XML_OP_ELEM, name, NULL); 1215 } 1216 return; 1217 error: 1218 if (URL != NULL) 1219 XML_PAT_FREE_STRING(ctxt, URL) 1220 if (token != NULL) 1221 XML_PAT_FREE_STRING(ctxt, token) 1222 if (name != NULL) 1223 XML_PAT_FREE_STRING(ctxt, name) 1224 } 1225 1226 /** 1227 * xmlCompilePathPattern: 1228 * @ctxt: the compilation context 1229 * 1230 * Compile the Path Pattern and generates a precompiled 1231 * form suitable for fast matching. 1232 * 1233 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) 1234 */ 1235 static void 1236 xmlCompilePathPattern(xmlPatParserContextPtr ctxt) { 1237 SKIP_BLANKS; 1238 if (CUR == '/') { 1239 ctxt->comp->flags |= PAT_FROM_ROOT; 1240 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) { 1241 ctxt->comp->flags |= PAT_FROM_CUR; 1242 } 1243 1244 if ((CUR == '/') && (NXT(1) == '/')) { 1245 PUSH(XML_OP_ANCESTOR, NULL, NULL); 1246 NEXT; 1247 NEXT; 1248 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) { 1249 PUSH(XML_OP_ANCESTOR, NULL, NULL); 1250 NEXT; 1251 NEXT; 1252 NEXT; 1253 /* Check for incompleteness. */ 1254 SKIP_BLANKS; 1255 if (CUR == 0) { 1256 ERROR5(NULL, NULL, NULL, 1257 "Incomplete expression '%s'.\n", ctxt->base); 1258 ctxt->error = 1; 1259 goto error; 1260 } 1261 } 1262 if (CUR == '@') { 1263 NEXT; 1264 xmlCompileAttributeTest(ctxt); 1265 SKIP_BLANKS; 1266 /* TODO: check for incompleteness */ 1267 if (CUR != 0) { 1268 xmlCompileStepPattern(ctxt); 1269 if (ctxt->error != 0) 1270 goto error; 1271 } 1272 } else { 1273 if (CUR == '/') { 1274 PUSH(XML_OP_ROOT, NULL, NULL); 1275 NEXT; 1276 /* Check for incompleteness. */ 1277 SKIP_BLANKS; 1278 if (CUR == 0) { 1279 ERROR5(NULL, NULL, NULL, 1280 "Incomplete expression '%s'.\n", ctxt->base); 1281 ctxt->error = 1; 1282 goto error; 1283 } 1284 } 1285 xmlCompileStepPattern(ctxt); 1286 if (ctxt->error != 0) 1287 goto error; 1288 SKIP_BLANKS; 1289 while (CUR == '/') { 1290 if (NXT(1) == '/') { 1291 PUSH(XML_OP_ANCESTOR, NULL, NULL); 1292 NEXT; 1293 NEXT; 1294 SKIP_BLANKS; 1295 xmlCompileStepPattern(ctxt); 1296 if (ctxt->error != 0) 1297 goto error; 1298 } else { 1299 PUSH(XML_OP_PARENT, NULL, NULL); 1300 NEXT; 1301 SKIP_BLANKS; 1302 if (CUR == 0) { 1303 ERROR5(NULL, NULL, NULL, 1304 "Incomplete expression '%s'.\n", ctxt->base); 1305 ctxt->error = 1; 1306 goto error; 1307 } 1308 xmlCompileStepPattern(ctxt); 1309 if (ctxt->error != 0) 1310 goto error; 1311 } 1312 } 1313 } 1314 if (CUR != 0) { 1315 ERROR5(NULL, NULL, NULL, 1316 "Failed to compile pattern %s\n", ctxt->base); 1317 ctxt->error = 1; 1318 } 1319 error: 1320 return; 1321 } 1322 1323 /** 1324 * xmlCompileIDCXPathPath: 1325 * @ctxt: the compilation context 1326 * 1327 * Compile the Path Pattern and generates a precompiled 1328 * form suitable for fast matching. 1329 * 1330 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) 1331 */ 1332 static void 1333 xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) { 1334 SKIP_BLANKS; 1335 if (CUR == '/') { 1336 ERROR5(NULL, NULL, NULL, 1337 "Unexpected selection of the document root in '%s'.\n", 1338 ctxt->base); 1339 goto error; 1340 } 1341 ctxt->comp->flags |= PAT_FROM_CUR; 1342 1343 if (CUR == '.') { 1344 /* "." - "self::node()" */ 1345 NEXT; 1346 SKIP_BLANKS; 1347 if (CUR == 0) { 1348 /* 1349 * Selection of the context node. 1350 */ 1351 PUSH(XML_OP_ELEM, NULL, NULL); 1352 return; 1353 } 1354 if (CUR != '/') { 1355 /* TODO: A more meaningful error message. */ 1356 ERROR5(NULL, NULL, NULL, 1357 "Unexpected token after '.' in '%s'.\n", ctxt->base); 1358 goto error; 1359 } 1360 /* "./" - "self::node()/" */ 1361 NEXT; 1362 SKIP_BLANKS; 1363 if (CUR == '/') { 1364 if (IS_BLANK_CH(PEEKPREV(1))) { 1365 /* 1366 * Disallow "./ /" 1367 */ 1368 ERROR5(NULL, NULL, NULL, 1369 "Unexpected '/' token in '%s'.\n", ctxt->base); 1370 goto error; 1371 } 1372 /* ".//" - "self:node()/descendant-or-self::node()/" */ 1373 PUSH(XML_OP_ANCESTOR, NULL, NULL); 1374 NEXT; 1375 SKIP_BLANKS; 1376 } 1377 if (CUR == 0) 1378 goto error_unfinished; 1379 } 1380 /* 1381 * Process steps. 1382 */ 1383 do { 1384 xmlCompileStepPattern(ctxt); 1385 if (ctxt->error != 0) 1386 goto error; 1387 SKIP_BLANKS; 1388 if (CUR != '/') 1389 break; 1390 PUSH(XML_OP_PARENT, NULL, NULL); 1391 NEXT; 1392 SKIP_BLANKS; 1393 if (CUR == '/') { 1394 /* 1395 * Disallow subsequent '//'. 1396 */ 1397 ERROR5(NULL, NULL, NULL, 1398 "Unexpected subsequent '//' in '%s'.\n", 1399 ctxt->base); 1400 goto error; 1401 } 1402 if (CUR == 0) 1403 goto error_unfinished; 1404 1405 } while (CUR != 0); 1406 1407 if (CUR != 0) { 1408 ERROR5(NULL, NULL, NULL, 1409 "Failed to compile expression '%s'.\n", ctxt->base); 1410 ctxt->error = 1; 1411 } 1412 return; 1413 error: 1414 ctxt->error = 1; 1415 return; 1416 1417 error_unfinished: 1418 ctxt->error = 1; 1419 ERROR5(NULL, NULL, NULL, 1420 "Unfinished expression '%s'.\n", ctxt->base); 1421 return; 1422 } 1423 1424 /************************************************************************ 1425 * * 1426 * The streaming code * 1427 * * 1428 ************************************************************************/ 1429 1430 #ifdef DEBUG_STREAMING 1431 static void 1432 xmlDebugStreamComp(xmlStreamCompPtr stream) { 1433 int i; 1434 1435 if (stream == NULL) { 1436 printf("Stream: NULL\n"); 1437 return; 1438 } 1439 printf("Stream: %d steps\n", stream->nbStep); 1440 for (i = 0;i < stream->nbStep;i++) { 1441 if (stream->steps[i].ns != NULL) { 1442 printf("{%s}", stream->steps[i].ns); 1443 } 1444 if (stream->steps[i].name == NULL) { 1445 printf("* "); 1446 } else { 1447 printf("%s ", stream->steps[i].name); 1448 } 1449 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT) 1450 printf("root "); 1451 if (stream->steps[i].flags & XML_STREAM_STEP_DESC) 1452 printf("// "); 1453 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL) 1454 printf("final "); 1455 printf("\n"); 1456 } 1457 } 1458 static void 1459 xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) { 1460 int i; 1461 1462 if (ctxt == NULL) { 1463 printf("Stream: NULL\n"); 1464 return; 1465 } 1466 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState); 1467 if (match) 1468 printf("matches\n"); 1469 else 1470 printf("\n"); 1471 for (i = 0;i < ctxt->nbState;i++) { 1472 if (ctxt->states[2 * i] < 0) 1473 printf(" %d: free\n", i); 1474 else { 1475 printf(" %d: step %d, level %d", i, ctxt->states[2 * i], 1476 ctxt->states[(2 * i) + 1]); 1477 if (ctxt->comp->steps[ctxt->states[2 * i]].flags & 1478 XML_STREAM_STEP_DESC) 1479 printf(" //\n"); 1480 else 1481 printf("\n"); 1482 } 1483 } 1484 } 1485 #endif 1486 /** 1487 * xmlNewStreamComp: 1488 * @size: the number of expected steps 1489 * 1490 * build a new compiled pattern for streaming 1491 * 1492 * Returns the new structure or NULL in case of error. 1493 */ 1494 static xmlStreamCompPtr 1495 xmlNewStreamComp(int size) { 1496 xmlStreamCompPtr cur; 1497 1498 if (size < 4) 1499 size = 4; 1500 1501 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp)); 1502 if (cur == NULL) { 1503 ERROR(NULL, NULL, NULL, 1504 "xmlNewStreamComp: malloc failed\n"); 1505 return(NULL); 1506 } 1507 memset(cur, 0, sizeof(xmlStreamComp)); 1508 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep)); 1509 if (cur->steps == NULL) { 1510 xmlFree(cur); 1511 ERROR(NULL, NULL, NULL, 1512 "xmlNewStreamComp: malloc failed\n"); 1513 return(NULL); 1514 } 1515 cur->nbStep = 0; 1516 cur->maxStep = size; 1517 return(cur); 1518 } 1519 1520 /** 1521 * xmlFreeStreamComp: 1522 * @comp: the compiled pattern for streaming 1523 * 1524 * Free the compiled pattern for streaming 1525 */ 1526 static void 1527 xmlFreeStreamComp(xmlStreamCompPtr comp) { 1528 if (comp != NULL) { 1529 if (comp->steps != NULL) 1530 xmlFree(comp->steps); 1531 if (comp->dict != NULL) 1532 xmlDictFree(comp->dict); 1533 xmlFree(comp); 1534 } 1535 } 1536 1537 /** 1538 * xmlStreamCompAddStep: 1539 * @comp: the compiled pattern for streaming 1540 * @name: the first string, the name, or NULL for * 1541 * @ns: the second step, the namespace name 1542 * @flags: the flags for that step 1543 * 1544 * Add a new step to the compiled pattern 1545 * 1546 * Returns -1 in case of error or the step index if successful 1547 */ 1548 static int 1549 xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name, 1550 const xmlChar *ns, int nodeType, int flags) { 1551 xmlStreamStepPtr cur; 1552 1553 if (comp->nbStep >= comp->maxStep) { 1554 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps, 1555 comp->maxStep * 2 * sizeof(xmlStreamStep)); 1556 if (cur == NULL) { 1557 ERROR(NULL, NULL, NULL, 1558 "xmlNewStreamComp: malloc failed\n"); 1559 return(-1); 1560 } 1561 comp->steps = cur; 1562 comp->maxStep *= 2; 1563 } 1564 cur = &comp->steps[comp->nbStep++]; 1565 cur->flags = flags; 1566 cur->name = name; 1567 cur->ns = ns; 1568 cur->nodeType = nodeType; 1569 return(comp->nbStep - 1); 1570 } 1571 1572 /** 1573 * xmlStreamCompile: 1574 * @comp: the precompiled pattern 1575 * 1576 * Tries to stream compile a pattern 1577 * 1578 * Returns -1 in case of failure and 0 in case of success. 1579 */ 1580 static int 1581 xmlStreamCompile(xmlPatternPtr comp) { 1582 xmlStreamCompPtr stream; 1583 int i, s = 0, root = 0, flags = 0, prevs = -1; 1584 xmlStepOp step; 1585 1586 if ((comp == NULL) || (comp->steps == NULL)) 1587 return(-1); 1588 /* 1589 * special case for . 1590 */ 1591 if ((comp->nbStep == 1) && 1592 (comp->steps[0].op == XML_OP_ELEM) && 1593 (comp->steps[0].value == NULL) && 1594 (comp->steps[0].value2 == NULL)) { 1595 stream = xmlNewStreamComp(0); 1596 if (stream == NULL) 1597 return(-1); 1598 /* Note that the stream will have no steps in this case. */ 1599 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE; 1600 comp->stream = stream; 1601 return(0); 1602 } 1603 1604 stream = xmlNewStreamComp((comp->nbStep / 2) + 1); 1605 if (stream == NULL) 1606 return(-1); 1607 if (comp->dict != NULL) { 1608 stream->dict = comp->dict; 1609 xmlDictReference(stream->dict); 1610 } 1611 1612 i = 0; 1613 if (comp->flags & PAT_FROM_ROOT) 1614 stream->flags |= XML_STREAM_FROM_ROOT; 1615 1616 for (;i < comp->nbStep;i++) { 1617 step = comp->steps[i]; 1618 switch (step.op) { 1619 case XML_OP_END: 1620 break; 1621 case XML_OP_ROOT: 1622 if (i != 0) 1623 goto error; 1624 root = 1; 1625 break; 1626 case XML_OP_NS: 1627 s = xmlStreamCompAddStep(stream, NULL, step.value, 1628 XML_ELEMENT_NODE, flags); 1629 if (s < 0) 1630 goto error; 1631 prevs = s; 1632 flags = 0; 1633 break; 1634 case XML_OP_ATTR: 1635 flags |= XML_STREAM_STEP_ATTR; 1636 prevs = -1; 1637 s = xmlStreamCompAddStep(stream, 1638 step.value, step.value2, XML_ATTRIBUTE_NODE, flags); 1639 flags = 0; 1640 if (s < 0) 1641 goto error; 1642 break; 1643 case XML_OP_ELEM: 1644 if ((step.value == NULL) && (step.value2 == NULL)) { 1645 /* 1646 * We have a "." or "self::node()" here. 1647 * Eliminate redundant self::node() tests like in "/./." 1648 * or "//./" 1649 * The only case we won't eliminate is "//.", i.e. if 1650 * self::node() is the last node test and we had 1651 * continuation somewhere beforehand. 1652 */ 1653 if ((comp->nbStep == i + 1) && 1654 (flags & XML_STREAM_STEP_DESC)) { 1655 /* 1656 * Mark the special case where the expression resolves 1657 * to any type of node. 1658 */ 1659 if (comp->nbStep == i + 1) { 1660 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE; 1661 } 1662 flags |= XML_STREAM_STEP_NODE; 1663 s = xmlStreamCompAddStep(stream, NULL, NULL, 1664 XML_STREAM_ANY_NODE, flags); 1665 if (s < 0) 1666 goto error; 1667 flags = 0; 1668 /* 1669 * If there was a previous step, mark it to be added to 1670 * the result node-set; this is needed since only 1671 * the last step will be marked as "final" and only 1672 * "final" nodes are added to the resulting set. 1673 */ 1674 if (prevs != -1) { 1675 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET; 1676 prevs = -1; 1677 } 1678 break; 1679 1680 } else { 1681 /* Just skip this one. */ 1682 continue; 1683 } 1684 } 1685 /* An element node. */ 1686 s = xmlStreamCompAddStep(stream, step.value, step.value2, 1687 XML_ELEMENT_NODE, flags); 1688 if (s < 0) 1689 goto error; 1690 prevs = s; 1691 flags = 0; 1692 break; 1693 case XML_OP_CHILD: 1694 /* An element node child. */ 1695 s = xmlStreamCompAddStep(stream, step.value, step.value2, 1696 XML_ELEMENT_NODE, flags); 1697 if (s < 0) 1698 goto error; 1699 prevs = s; 1700 flags = 0; 1701 break; 1702 case XML_OP_ALL: 1703 s = xmlStreamCompAddStep(stream, NULL, NULL, 1704 XML_ELEMENT_NODE, flags); 1705 if (s < 0) 1706 goto error; 1707 prevs = s; 1708 flags = 0; 1709 break; 1710 case XML_OP_PARENT: 1711 break; 1712 case XML_OP_ANCESTOR: 1713 /* Skip redundant continuations. */ 1714 if (flags & XML_STREAM_STEP_DESC) 1715 break; 1716 flags |= XML_STREAM_STEP_DESC; 1717 /* 1718 * Mark the expression as having "//". 1719 */ 1720 if ((stream->flags & XML_STREAM_DESC) == 0) 1721 stream->flags |= XML_STREAM_DESC; 1722 break; 1723 } 1724 } 1725 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) { 1726 /* 1727 * If this should behave like a real pattern, we will mark 1728 * the first step as having "//", to be reentrant on every 1729 * tree level. 1730 */ 1731 if ((stream->flags & XML_STREAM_DESC) == 0) 1732 stream->flags |= XML_STREAM_DESC; 1733 1734 if (stream->nbStep > 0) { 1735 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0) 1736 stream->steps[0].flags |= XML_STREAM_STEP_DESC; 1737 } 1738 } 1739 if (stream->nbStep <= s) 1740 goto error; 1741 stream->steps[s].flags |= XML_STREAM_STEP_FINAL; 1742 if (root) 1743 stream->steps[0].flags |= XML_STREAM_STEP_ROOT; 1744 #ifdef DEBUG_STREAMING 1745 xmlDebugStreamComp(stream); 1746 #endif 1747 comp->stream = stream; 1748 return(0); 1749 error: 1750 xmlFreeStreamComp(stream); 1751 return(0); 1752 } 1753 1754 /** 1755 * xmlNewStreamCtxt: 1756 * @size: the number of expected states 1757 * 1758 * build a new stream context 1759 * 1760 * Returns the new structure or NULL in case of error. 1761 */ 1762 static xmlStreamCtxtPtr 1763 xmlNewStreamCtxt(xmlStreamCompPtr stream) { 1764 xmlStreamCtxtPtr cur; 1765 1766 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt)); 1767 if (cur == NULL) { 1768 ERROR(NULL, NULL, NULL, 1769 "xmlNewStreamCtxt: malloc failed\n"); 1770 return(NULL); 1771 } 1772 memset(cur, 0, sizeof(xmlStreamCtxt)); 1773 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int)); 1774 if (cur->states == NULL) { 1775 xmlFree(cur); 1776 ERROR(NULL, NULL, NULL, 1777 "xmlNewStreamCtxt: malloc failed\n"); 1778 return(NULL); 1779 } 1780 cur->nbState = 0; 1781 cur->maxState = 4; 1782 cur->level = 0; 1783 cur->comp = stream; 1784 cur->blockLevel = -1; 1785 return(cur); 1786 } 1787 1788 /** 1789 * xmlFreeStreamCtxt: 1790 * @stream: the stream context 1791 * 1792 * Free the stream context 1793 */ 1794 void 1795 xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) { 1796 xmlStreamCtxtPtr next; 1797 1798 while (stream != NULL) { 1799 next = stream->next; 1800 if (stream->states != NULL) 1801 xmlFree(stream->states); 1802 xmlFree(stream); 1803 stream = next; 1804 } 1805 } 1806 1807 /** 1808 * xmlStreamCtxtAddState: 1809 * @comp: the stream context 1810 * @idx: the step index for that streaming state 1811 * 1812 * Add a new state to the stream context 1813 * 1814 * Returns -1 in case of error or the state index if successful 1815 */ 1816 static int 1817 xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) { 1818 int i; 1819 for (i = 0;i < comp->nbState;i++) { 1820 if (comp->states[2 * i] < 0) { 1821 comp->states[2 * i] = idx; 1822 comp->states[2 * i + 1] = level; 1823 return(i); 1824 } 1825 } 1826 if (comp->nbState >= comp->maxState) { 1827 int *cur; 1828 1829 cur = (int *) xmlRealloc(comp->states, 1830 comp->maxState * 4 * sizeof(int)); 1831 if (cur == NULL) { 1832 ERROR(NULL, NULL, NULL, 1833 "xmlNewStreamCtxt: malloc failed\n"); 1834 return(-1); 1835 } 1836 comp->states = cur; 1837 comp->maxState *= 2; 1838 } 1839 comp->states[2 * comp->nbState] = idx; 1840 comp->states[2 * comp->nbState++ + 1] = level; 1841 return(comp->nbState - 1); 1842 } 1843 1844 /** 1845 * xmlStreamPushInternal: 1846 * @stream: the stream context 1847 * @name: the current name 1848 * @ns: the namespace name 1849 * @nodeType: the type of the node 1850 * 1851 * Push new data onto the stream. NOTE: if the call xmlPatterncompile() 1852 * indicated a dictionary, then strings for name and ns will be expected 1853 * to come from the dictionary. 1854 * Both @name and @ns being NULL means the / i.e. the root of the document. 1855 * This can also act as a reset. 1856 * 1857 * Returns: -1 in case of error, 1 if the current state in the stream is a 1858 * match and 0 otherwise. 1859 */ 1860 static int 1861 xmlStreamPushInternal(xmlStreamCtxtPtr stream, 1862 const xmlChar *name, const xmlChar *ns, 1863 int nodeType) { 1864 int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc; 1865 xmlStreamCompPtr comp; 1866 xmlStreamStep step; 1867 #ifdef DEBUG_STREAMING 1868 xmlStreamCtxtPtr orig = stream; 1869 #endif 1870 1871 if ((stream == NULL) || (stream->nbState < 0)) 1872 return(-1); 1873 1874 while (stream != NULL) { 1875 comp = stream->comp; 1876 1877 if ((nodeType == XML_ELEMENT_NODE) && 1878 (name == NULL) && (ns == NULL)) { 1879 /* We have a document node here (or a reset). */ 1880 stream->nbState = 0; 1881 stream->level = 0; 1882 stream->blockLevel = -1; 1883 if (comp->flags & XML_STREAM_FROM_ROOT) { 1884 if (comp->nbStep == 0) { 1885 /* TODO: We have a "/." here? */ 1886 ret = 1; 1887 } else { 1888 if ((comp->nbStep == 1) && 1889 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) && 1890 (comp->steps[0].flags & XML_STREAM_STEP_DESC)) 1891 { 1892 /* 1893 * In the case of "//." the document node will match 1894 * as well. 1895 */ 1896 ret = 1; 1897 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) { 1898 /* TODO: Do we need this ? */ 1899 tmp = xmlStreamCtxtAddState(stream, 0, 0); 1900 if (tmp < 0) 1901 err++; 1902 } 1903 } 1904 } 1905 stream = stream->next; 1906 continue; /* while */ 1907 } 1908 1909 /* 1910 * Fast check for ".". 1911 */ 1912 if (comp->nbStep == 0) { 1913 /* 1914 * / and . are handled at the XPath node set creation 1915 * level by checking min depth 1916 */ 1917 if (stream->flags & XML_PATTERN_XPATH) { 1918 stream = stream->next; 1919 continue; /* while */ 1920 } 1921 /* 1922 * For non-pattern like evaluation like XML Schema IDCs 1923 * or traditional XPath expressions, this will match if 1924 * we are at the first level only, otherwise on every level. 1925 */ 1926 if ((nodeType != XML_ATTRIBUTE_NODE) && 1927 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) || 1928 (stream->level == 0))) { 1929 ret = 1; 1930 } 1931 stream->level++; 1932 goto stream_next; 1933 } 1934 if (stream->blockLevel != -1) { 1935 /* 1936 * Skip blocked expressions. 1937 */ 1938 stream->level++; 1939 goto stream_next; 1940 } 1941 1942 if ((nodeType != XML_ELEMENT_NODE) && 1943 (nodeType != XML_ATTRIBUTE_NODE) && 1944 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) { 1945 /* 1946 * No need to process nodes of other types if we don't 1947 * resolve to those types. 1948 * TODO: Do we need to block the context here? 1949 */ 1950 stream->level++; 1951 goto stream_next; 1952 } 1953 1954 /* 1955 * Check evolution of existing states 1956 */ 1957 i = 0; 1958 m = stream->nbState; 1959 while (i < m) { 1960 if ((comp->flags & XML_STREAM_DESC) == 0) { 1961 /* 1962 * If there is no "//", then only the last 1963 * added state is of interest. 1964 */ 1965 stepNr = stream->states[2 * (stream->nbState -1)]; 1966 /* 1967 * TODO: Security check, should not happen, remove it. 1968 */ 1969 if (stream->states[(2 * (stream->nbState -1)) + 1] < 1970 stream->level) { 1971 return (-1); 1972 } 1973 desc = 0; 1974 /* loop-stopper */ 1975 i = m; 1976 } else { 1977 /* 1978 * If there are "//", then we need to process every "//" 1979 * occurring in the states, plus any other state for this 1980 * level. 1981 */ 1982 stepNr = stream->states[2 * i]; 1983 1984 /* TODO: should not happen anymore: dead states */ 1985 if (stepNr < 0) 1986 goto next_state; 1987 1988 tmp = stream->states[(2 * i) + 1]; 1989 1990 /* skip new states just added */ 1991 if (tmp > stream->level) 1992 goto next_state; 1993 1994 /* skip states at ancestor levels, except if "//" */ 1995 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC; 1996 if ((tmp < stream->level) && (!desc)) 1997 goto next_state; 1998 } 1999 /* 2000 * Check for correct node-type. 2001 */ 2002 step = comp->steps[stepNr]; 2003 if (step.nodeType != nodeType) { 2004 if (step.nodeType == XML_ATTRIBUTE_NODE) { 2005 /* 2006 * Block this expression for deeper evaluation. 2007 */ 2008 if ((comp->flags & XML_STREAM_DESC) == 0) 2009 stream->blockLevel = stream->level +1; 2010 goto next_state; 2011 } else if (step.nodeType != XML_STREAM_ANY_NODE) 2012 goto next_state; 2013 } 2014 /* 2015 * Compare local/namespace-name. 2016 */ 2017 match = 0; 2018 if (step.nodeType == XML_STREAM_ANY_NODE) { 2019 match = 1; 2020 } else if (step.name == NULL) { 2021 if (step.ns == NULL) { 2022 /* 2023 * This lets through all elements/attributes. 2024 */ 2025 match = 1; 2026 } else if (ns != NULL) 2027 match = xmlStrEqual(step.ns, ns); 2028 } else if (((step.ns != NULL) == (ns != NULL)) && 2029 (name != NULL) && 2030 (step.name[0] == name[0]) && 2031 xmlStrEqual(step.name, name) && 2032 ((step.ns == ns) || xmlStrEqual(step.ns, ns))) 2033 { 2034 match = 1; 2035 } 2036 #if 0 2037 /* 2038 * TODO: Pointer comparison won't work, since not guaranteed that the given 2039 * values are in the same dict; especially if it's the namespace name, 2040 * normally coming from ns->href. We need a namespace dict mechanism ! 2041 */ 2042 } else if (comp->dict) { 2043 if (step.name == NULL) { 2044 if (step.ns == NULL) 2045 match = 1; 2046 else 2047 match = (step.ns == ns); 2048 } else { 2049 match = ((step.name == name) && (step.ns == ns)); 2050 } 2051 #endif /* if 0 ------------------------------------------------------- */ 2052 if (match) { 2053 final = step.flags & XML_STREAM_STEP_FINAL; 2054 if (desc) { 2055 if (final) { 2056 ret = 1; 2057 } else { 2058 /* descending match create a new state */ 2059 xmlStreamCtxtAddState(stream, stepNr + 1, 2060 stream->level + 1); 2061 } 2062 } else { 2063 if (final) { 2064 ret = 1; 2065 } else { 2066 xmlStreamCtxtAddState(stream, stepNr + 1, 2067 stream->level + 1); 2068 } 2069 } 2070 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) { 2071 /* 2072 * Check if we have a special case like "foo/bar//.", where 2073 * "foo" is selected as well. 2074 */ 2075 ret = 1; 2076 } 2077 } 2078 if (((comp->flags & XML_STREAM_DESC) == 0) && 2079 ((! match) || final)) { 2080 /* 2081 * Mark this expression as blocked for any evaluation at 2082 * deeper levels. Note that this includes "/foo" 2083 * expressions if the *pattern* behaviour is used. 2084 */ 2085 stream->blockLevel = stream->level +1; 2086 } 2087 next_state: 2088 i++; 2089 } 2090 2091 stream->level++; 2092 2093 /* 2094 * Re/enter the expression. 2095 * Don't reenter if it's an absolute expression like "/foo", 2096 * except "//foo". 2097 */ 2098 step = comp->steps[0]; 2099 if (step.flags & XML_STREAM_STEP_ROOT) 2100 goto stream_next; 2101 2102 desc = step.flags & XML_STREAM_STEP_DESC; 2103 if (stream->flags & XML_PATTERN_NOTPATTERN) { 2104 /* 2105 * Re/enter the expression if it is a "descendant" one, 2106 * or if we are at the 1st level of evaluation. 2107 */ 2108 2109 if (stream->level == 1) { 2110 if (XML_STREAM_XS_IDC(stream)) { 2111 /* 2112 * XS-IDC: The missing "self::node()" will always 2113 * match the first given node. 2114 */ 2115 goto stream_next; 2116 } else 2117 goto compare; 2118 } 2119 /* 2120 * A "//" is always reentrant. 2121 */ 2122 if (desc) 2123 goto compare; 2124 2125 /* 2126 * XS-IDC: Process the 2nd level, since the missing 2127 * "self::node()" is responsible for the 2nd level being 2128 * the real start level. 2129 */ 2130 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream)) 2131 goto compare; 2132 2133 goto stream_next; 2134 } 2135 2136 compare: 2137 /* 2138 * Check expected node-type. 2139 */ 2140 if (step.nodeType != nodeType) { 2141 if (nodeType == XML_ATTRIBUTE_NODE) 2142 goto stream_next; 2143 else if (step.nodeType != XML_STREAM_ANY_NODE) 2144 goto stream_next; 2145 } 2146 /* 2147 * Compare local/namespace-name. 2148 */ 2149 match = 0; 2150 if (step.nodeType == XML_STREAM_ANY_NODE) { 2151 match = 1; 2152 } else if (step.name == NULL) { 2153 if (step.ns == NULL) { 2154 /* 2155 * This lets through all elements/attributes. 2156 */ 2157 match = 1; 2158 } else if (ns != NULL) 2159 match = xmlStrEqual(step.ns, ns); 2160 } else if (((step.ns != NULL) == (ns != NULL)) && 2161 (name != NULL) && 2162 (step.name[0] == name[0]) && 2163 xmlStrEqual(step.name, name) && 2164 ((step.ns == ns) || xmlStrEqual(step.ns, ns))) 2165 { 2166 match = 1; 2167 } 2168 final = step.flags & XML_STREAM_STEP_FINAL; 2169 if (match) { 2170 if (final) 2171 ret = 1; 2172 else 2173 xmlStreamCtxtAddState(stream, 1, stream->level); 2174 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) { 2175 /* 2176 * Check if we have a special case like "foo//.", where 2177 * "foo" is selected as well. 2178 */ 2179 ret = 1; 2180 } 2181 } 2182 if (((comp->flags & XML_STREAM_DESC) == 0) && 2183 ((! match) || final)) { 2184 /* 2185 * Mark this expression as blocked for any evaluation at 2186 * deeper levels. 2187 */ 2188 stream->blockLevel = stream->level; 2189 } 2190 2191 stream_next: 2192 stream = stream->next; 2193 } /* while stream != NULL */ 2194 2195 if (err > 0) 2196 ret = -1; 2197 #ifdef DEBUG_STREAMING 2198 xmlDebugStreamCtxt(orig, ret); 2199 #endif 2200 return(ret); 2201 } 2202 2203 /** 2204 * xmlStreamPush: 2205 * @stream: the stream context 2206 * @name: the current name 2207 * @ns: the namespace name 2208 * 2209 * Push new data onto the stream. NOTE: if the call xmlPatterncompile() 2210 * indicated a dictionary, then strings for name and ns will be expected 2211 * to come from the dictionary. 2212 * Both @name and @ns being NULL means the / i.e. the root of the document. 2213 * This can also act as a reset. 2214 * Otherwise the function will act as if it has been given an element-node. 2215 * 2216 * Returns: -1 in case of error, 1 if the current state in the stream is a 2217 * match and 0 otherwise. 2218 */ 2219 int 2220 xmlStreamPush(xmlStreamCtxtPtr stream, 2221 const xmlChar *name, const xmlChar *ns) { 2222 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE)); 2223 } 2224 2225 /** 2226 * xmlStreamPushNode: 2227 * @stream: the stream context 2228 * @name: the current name 2229 * @ns: the namespace name 2230 * @nodeType: the type of the node being pushed 2231 * 2232 * Push new data onto the stream. NOTE: if the call xmlPatterncompile() 2233 * indicated a dictionary, then strings for name and ns will be expected 2234 * to come from the dictionary. 2235 * Both @name and @ns being NULL means the / i.e. the root of the document. 2236 * This can also act as a reset. 2237 * Different from xmlStreamPush() this function can be fed with nodes of type: 2238 * element-, attribute-, text-, cdata-section-, comment- and 2239 * processing-instruction-node. 2240 * 2241 * Returns: -1 in case of error, 1 if the current state in the stream is a 2242 * match and 0 otherwise. 2243 */ 2244 int 2245 xmlStreamPushNode(xmlStreamCtxtPtr stream, 2246 const xmlChar *name, const xmlChar *ns, 2247 int nodeType) 2248 { 2249 return (xmlStreamPushInternal(stream, name, ns, 2250 nodeType)); 2251 } 2252 2253 /** 2254 * xmlStreamPushAttr: 2255 * @stream: the stream context 2256 * @name: the current name 2257 * @ns: the namespace name 2258 * 2259 * Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile() 2260 * indicated a dictionary, then strings for name and ns will be expected 2261 * to come from the dictionary. 2262 * Both @name and @ns being NULL means the / i.e. the root of the document. 2263 * This can also act as a reset. 2264 * Otherwise the function will act as if it has been given an attribute-node. 2265 * 2266 * Returns: -1 in case of error, 1 if the current state in the stream is a 2267 * match and 0 otherwise. 2268 */ 2269 int 2270 xmlStreamPushAttr(xmlStreamCtxtPtr stream, 2271 const xmlChar *name, const xmlChar *ns) { 2272 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE)); 2273 } 2274 2275 /** 2276 * xmlStreamPop: 2277 * @stream: the stream context 2278 * 2279 * push one level from the stream. 2280 * 2281 * Returns: -1 in case of error, 0 otherwise. 2282 */ 2283 int 2284 xmlStreamPop(xmlStreamCtxtPtr stream) { 2285 int i, lev; 2286 2287 if (stream == NULL) 2288 return(-1); 2289 while (stream != NULL) { 2290 /* 2291 * Reset block-level. 2292 */ 2293 if (stream->blockLevel == stream->level) 2294 stream->blockLevel = -1; 2295 2296 /* 2297 * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set 2298 * (see the thread at 2299 * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html) 2300 */ 2301 if (stream->level) 2302 stream->level--; 2303 /* 2304 * Check evolution of existing states 2305 */ 2306 for (i = stream->nbState -1; i >= 0; i--) { 2307 /* discard obsoleted states */ 2308 lev = stream->states[(2 * i) + 1]; 2309 if (lev > stream->level) 2310 stream->nbState--; 2311 if (lev <= stream->level) 2312 break; 2313 } 2314 stream = stream->next; 2315 } 2316 return(0); 2317 } 2318 2319 /** 2320 * xmlStreamWantsAnyNode: 2321 * @streamCtxt: the stream context 2322 * 2323 * Query if the streaming pattern additionally needs to be fed with 2324 * text-, cdata-section-, comment- and processing-instruction-nodes. 2325 * If the result is 0 then only element-nodes and attribute-nodes 2326 * need to be pushed. 2327 * 2328 * Returns: 1 in case of need of nodes of the above described types, 2329 * 0 otherwise. -1 on API errors. 2330 */ 2331 int 2332 xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt) 2333 { 2334 if (streamCtxt == NULL) 2335 return(-1); 2336 while (streamCtxt != NULL) { 2337 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) 2338 return(1); 2339 streamCtxt = streamCtxt->next; 2340 } 2341 return(0); 2342 } 2343 2344 /************************************************************************ 2345 * * 2346 * The public interfaces * 2347 * * 2348 ************************************************************************/ 2349 2350 /** 2351 * xmlPatterncompile: 2352 * @pattern: the pattern to compile 2353 * @dict: an optional dictionary for interned strings 2354 * @flags: compilation flags, see xmlPatternFlags 2355 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL 2356 * 2357 * Compile a pattern. 2358 * 2359 * Returns the compiled form of the pattern or NULL in case of error 2360 */ 2361 xmlPatternPtr 2362 xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags, 2363 const xmlChar **namespaces) { 2364 xmlPatternPtr ret = NULL, cur; 2365 xmlPatParserContextPtr ctxt = NULL; 2366 const xmlChar *or, *start; 2367 xmlChar *tmp = NULL; 2368 int type = 0; 2369 int streamable = 1; 2370 2371 if (pattern == NULL) 2372 return(NULL); 2373 2374 start = pattern; 2375 or = start; 2376 while (*or != 0) { 2377 tmp = NULL; 2378 while ((*or != 0) && (*or != '|')) or++; 2379 if (*or == 0) 2380 ctxt = xmlNewPatParserContext(start, dict, namespaces); 2381 else { 2382 tmp = xmlStrndup(start, or - start); 2383 if (tmp != NULL) { 2384 ctxt = xmlNewPatParserContext(tmp, dict, namespaces); 2385 } 2386 or++; 2387 } 2388 if (ctxt == NULL) goto error; 2389 cur = xmlNewPattern(); 2390 if (cur == NULL) goto error; 2391 /* 2392 * Assign string dict. 2393 */ 2394 if (dict) { 2395 cur->dict = dict; 2396 xmlDictReference(dict); 2397 } 2398 if (ret == NULL) 2399 ret = cur; 2400 else { 2401 cur->next = ret->next; 2402 ret->next = cur; 2403 } 2404 cur->flags = flags; 2405 ctxt->comp = cur; 2406 2407 if (XML_STREAM_XS_IDC(cur)) 2408 xmlCompileIDCXPathPath(ctxt); 2409 else 2410 xmlCompilePathPattern(ctxt); 2411 if (ctxt->error != 0) 2412 goto error; 2413 xmlFreePatParserContext(ctxt); 2414 ctxt = NULL; 2415 2416 2417 if (streamable) { 2418 if (type == 0) { 2419 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR); 2420 } else if (type == PAT_FROM_ROOT) { 2421 if (cur->flags & PAT_FROM_CUR) 2422 streamable = 0; 2423 } else if (type == PAT_FROM_CUR) { 2424 if (cur->flags & PAT_FROM_ROOT) 2425 streamable = 0; 2426 } 2427 } 2428 if (streamable) 2429 xmlStreamCompile(cur); 2430 if (xmlReversePattern(cur) < 0) 2431 goto error; 2432 if (tmp != NULL) { 2433 xmlFree(tmp); 2434 tmp = NULL; 2435 } 2436 start = or; 2437 } 2438 if (streamable == 0) { 2439 cur = ret; 2440 while (cur != NULL) { 2441 if (cur->stream != NULL) { 2442 xmlFreeStreamComp(cur->stream); 2443 cur->stream = NULL; 2444 } 2445 cur = cur->next; 2446 } 2447 } 2448 2449 return(ret); 2450 error: 2451 if (ctxt != NULL) xmlFreePatParserContext(ctxt); 2452 if (ret != NULL) xmlFreePattern(ret); 2453 if (tmp != NULL) xmlFree(tmp); 2454 return(NULL); 2455 } 2456 2457 /** 2458 * xmlPatternMatch: 2459 * @comp: the precompiled pattern 2460 * @node: a node 2461 * 2462 * Test whether the node matches the pattern 2463 * 2464 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure 2465 */ 2466 int 2467 xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node) 2468 { 2469 int ret = 0; 2470 2471 if ((comp == NULL) || (node == NULL)) 2472 return(-1); 2473 2474 while (comp != NULL) { 2475 ret = xmlPatMatch(comp, node); 2476 if (ret != 0) 2477 return(ret); 2478 comp = comp->next; 2479 } 2480 return(ret); 2481 } 2482 2483 /** 2484 * xmlPatternGetStreamCtxt: 2485 * @comp: the precompiled pattern 2486 * 2487 * Get a streaming context for that pattern 2488 * Use xmlFreeStreamCtxt to free the context. 2489 * 2490 * Returns a pointer to the context or NULL in case of failure 2491 */ 2492 xmlStreamCtxtPtr 2493 xmlPatternGetStreamCtxt(xmlPatternPtr comp) 2494 { 2495 xmlStreamCtxtPtr ret = NULL, cur; 2496 2497 if ((comp == NULL) || (comp->stream == NULL)) 2498 return(NULL); 2499 2500 while (comp != NULL) { 2501 if (comp->stream == NULL) 2502 goto failed; 2503 cur = xmlNewStreamCtxt(comp->stream); 2504 if (cur == NULL) 2505 goto failed; 2506 if (ret == NULL) 2507 ret = cur; 2508 else { 2509 cur->next = ret->next; 2510 ret->next = cur; 2511 } 2512 cur->flags = comp->flags; 2513 comp = comp->next; 2514 } 2515 return(ret); 2516 failed: 2517 xmlFreeStreamCtxt(ret); 2518 return(NULL); 2519 } 2520 2521 /** 2522 * xmlPatternStreamable: 2523 * @comp: the precompiled pattern 2524 * 2525 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt() 2526 * should work. 2527 * 2528 * Returns 1 if streamable, 0 if not and -1 in case of error. 2529 */ 2530 int 2531 xmlPatternStreamable(xmlPatternPtr comp) { 2532 if (comp == NULL) 2533 return(-1); 2534 while (comp != NULL) { 2535 if (comp->stream == NULL) 2536 return(0); 2537 comp = comp->next; 2538 } 2539 return(1); 2540 } 2541 2542 /** 2543 * xmlPatternMaxDepth: 2544 * @comp: the precompiled pattern 2545 * 2546 * Check the maximum depth reachable by a pattern 2547 * 2548 * Returns -2 if no limit (using //), otherwise the depth, 2549 * and -1 in case of error 2550 */ 2551 int 2552 xmlPatternMaxDepth(xmlPatternPtr comp) { 2553 int ret = 0, i; 2554 if (comp == NULL) 2555 return(-1); 2556 while (comp != NULL) { 2557 if (comp->stream == NULL) 2558 return(-1); 2559 for (i = 0;i < comp->stream->nbStep;i++) 2560 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC) 2561 return(-2); 2562 if (comp->stream->nbStep > ret) 2563 ret = comp->stream->nbStep; 2564 comp = comp->next; 2565 } 2566 return(ret); 2567 } 2568 2569 /** 2570 * xmlPatternMinDepth: 2571 * @comp: the precompiled pattern 2572 * 2573 * Check the minimum depth reachable by a pattern, 0 mean the / or . are 2574 * part of the set. 2575 * 2576 * Returns -1 in case of error otherwise the depth, 2577 * 2578 */ 2579 int 2580 xmlPatternMinDepth(xmlPatternPtr comp) { 2581 int ret = 12345678; 2582 if (comp == NULL) 2583 return(-1); 2584 while (comp != NULL) { 2585 if (comp->stream == NULL) 2586 return(-1); 2587 if (comp->stream->nbStep < ret) 2588 ret = comp->stream->nbStep; 2589 if (ret == 0) 2590 return(0); 2591 comp = comp->next; 2592 } 2593 return(ret); 2594 } 2595 2596 /** 2597 * xmlPatternFromRoot: 2598 * @comp: the precompiled pattern 2599 * 2600 * Check if the pattern must be looked at from the root. 2601 * 2602 * Returns 1 if true, 0 if false and -1 in case of error 2603 */ 2604 int 2605 xmlPatternFromRoot(xmlPatternPtr comp) { 2606 if (comp == NULL) 2607 return(-1); 2608 while (comp != NULL) { 2609 if (comp->stream == NULL) 2610 return(-1); 2611 if (comp->flags & PAT_FROM_ROOT) 2612 return(1); 2613 comp = comp->next; 2614 } 2615 return(0); 2616 2617 } 2618 2619 #define bottom_pattern 2620 #include "elfgcchack.h" 2621 #endif /* LIBXML_PATTERN_ENABLED */ 2622