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