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 (at) 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 ctxt->error = 1; 973 goto error; 974 } 975 } 976 XML_PAT_FREE_STRING(ctxt, prefix); 977 if (token == NULL) { 978 if (CUR == '*') { 979 NEXT; 980 PUSH(XML_OP_ATTR, NULL, URL); 981 } else { 982 ERROR(NULL, NULL, NULL, 983 "xmlCompileAttributeTest : Name expected\n"); 984 ctxt->error = 1; 985 goto error; 986 } 987 } else { 988 PUSH(XML_OP_ATTR, token, URL); 989 } 990 } else { 991 PUSH(XML_OP_ATTR, name, NULL); 992 } 993 return; 994 error: 995 if (URL != NULL) 996 XML_PAT_FREE_STRING(ctxt, URL) 997 if (token != NULL) 998 XML_PAT_FREE_STRING(ctxt, token); 999 } 1000 1001 /** 1002 * xmlCompileStepPattern: 1003 * @ctxt: the compilation context 1004 * 1005 * Compile the Step Pattern and generates a precompiled 1006 * form suitable for fast matching. 1007 * 1008 * [3] Step ::= '.' | NameTest 1009 * [4] NameTest ::= QName | '*' | NCName ':' '*' 1010 */ 1011 1012 static void 1013 xmlCompileStepPattern(xmlPatParserContextPtr ctxt) { 1014 xmlChar *token = NULL; 1015 xmlChar *name = NULL; 1016 xmlChar *URL = NULL; 1017 int hasBlanks = 0; 1018 1019 SKIP_BLANKS; 1020 if (CUR == '.') { 1021 /* 1022 * Context node. 1023 */ 1024 NEXT; 1025 PUSH(XML_OP_ELEM, NULL, NULL); 1026 return; 1027 } 1028 if (CUR == '@') { 1029 /* 1030 * Attribute test. 1031 */ 1032 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) { 1033 ERROR5(NULL, NULL, NULL, 1034 "Unexpected attribute axis in '%s'.\n", ctxt->base); 1035 ctxt->error = 1; 1036 return; 1037 } 1038 NEXT; 1039 xmlCompileAttributeTest(ctxt); 1040 if (ctxt->error != 0) 1041 goto error; 1042 return; 1043 } 1044 name = xmlPatScanNCName(ctxt); 1045 if (name == NULL) { 1046 if (CUR == '*') { 1047 NEXT; 1048 PUSH(XML_OP_ALL, NULL, NULL); 1049 return; 1050 } else { 1051 ERROR(NULL, NULL, NULL, 1052 "xmlCompileStepPattern : Name expected\n"); 1053 ctxt->error = 1; 1054 return; 1055 } 1056 } 1057 if (IS_BLANK_CH(CUR)) { 1058 hasBlanks = 1; 1059 SKIP_BLANKS; 1060 } 1061 if (CUR == ':') { 1062 NEXT; 1063 if (CUR != ':') { 1064 xmlChar *prefix = name; 1065 int i; 1066 1067 if (hasBlanks || IS_BLANK_CH(CUR)) { 1068 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); 1069 ctxt->error = 1; 1070 goto error; 1071 } 1072 /* 1073 * This is a namespace match 1074 */ 1075 token = xmlPatScanName(ctxt); 1076 if ((prefix[0] == 'x') && 1077 (prefix[1] == 'm') && 1078 (prefix[2] == 'l') && 1079 (prefix[3] == 0)) 1080 { 1081 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE) 1082 } else { 1083 for (i = 0;i < ctxt->nb_namespaces;i++) { 1084 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { 1085 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) 1086 break; 1087 } 1088 } 1089 if (i >= ctxt->nb_namespaces) { 1090 ERROR5(NULL, NULL, NULL, 1091 "xmlCompileStepPattern : no namespace bound to prefix %s\n", 1092 prefix); 1093 ctxt->error = 1; 1094 goto error; 1095 } 1096 } 1097 XML_PAT_FREE_STRING(ctxt, prefix); 1098 name = NULL; 1099 if (token == NULL) { 1100 if (CUR == '*') { 1101 NEXT; 1102 PUSH(XML_OP_NS, URL, NULL); 1103 } else { 1104 ERROR(NULL, NULL, NULL, 1105 "xmlCompileStepPattern : Name expected\n"); 1106 ctxt->error = 1; 1107 goto error; 1108 } 1109 } else { 1110 PUSH(XML_OP_ELEM, token, URL); 1111 } 1112 } else { 1113 NEXT; 1114 if (xmlStrEqual(name, (const xmlChar *) "child")) { 1115 XML_PAT_FREE_STRING(ctxt, name); 1116 name = xmlPatScanName(ctxt); 1117 if (name == NULL) { 1118 if (CUR == '*') { 1119 NEXT; 1120 PUSH(XML_OP_ALL, NULL, NULL); 1121 return; 1122 } else { 1123 ERROR(NULL, NULL, NULL, 1124 "xmlCompileStepPattern : QName expected\n"); 1125 ctxt->error = 1; 1126 goto error; 1127 } 1128 } 1129 if (CUR == ':') { 1130 xmlChar *prefix = name; 1131 int i; 1132 1133 NEXT; 1134 if (IS_BLANK_CH(CUR)) { 1135 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); 1136 ctxt->error = 1; 1137 goto error; 1138 } 1139 /* 1140 * This is a namespace match 1141 */ 1142 token = xmlPatScanName(ctxt); 1143 if ((prefix[0] == 'x') && 1144 (prefix[1] == 'm') && 1145 (prefix[2] == 'l') && 1146 (prefix[3] == 0)) 1147 { 1148 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE) 1149 } else { 1150 for (i = 0;i < ctxt->nb_namespaces;i++) { 1151 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { 1152 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) 1153 break; 1154 } 1155 } 1156 if (i >= ctxt->nb_namespaces) { 1157 ERROR5(NULL, NULL, NULL, 1158 "xmlCompileStepPattern : no namespace bound " 1159 "to prefix %s\n", prefix); 1160 ctxt->error = 1; 1161 goto error; 1162 } 1163 } 1164 XML_PAT_FREE_STRING(ctxt, prefix); 1165 name = NULL; 1166 if (token == NULL) { 1167 if (CUR == '*') { 1168 NEXT; 1169 PUSH(XML_OP_NS, URL, NULL); 1170 } else { 1171 ERROR(NULL, NULL, NULL, 1172 "xmlCompileStepPattern : Name expected\n"); 1173 ctxt->error = 1; 1174 goto error; 1175 } 1176 } else { 1177 PUSH(XML_OP_CHILD, token, URL); 1178 } 1179 } else 1180 PUSH(XML_OP_CHILD, name, NULL); 1181 return; 1182 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) { 1183 XML_PAT_FREE_STRING(ctxt, name) 1184 name = NULL; 1185 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) { 1186 ERROR5(NULL, NULL, NULL, 1187 "Unexpected attribute axis in '%s'.\n", ctxt->base); 1188 ctxt->error = 1; 1189 goto error; 1190 } 1191 xmlCompileAttributeTest(ctxt); 1192 if (ctxt->error != 0) 1193 goto error; 1194 return; 1195 } else { 1196 ERROR5(NULL, NULL, NULL, 1197 "The 'element' or 'attribute' axis is expected.\n", NULL); 1198 ctxt->error = 1; 1199 goto error; 1200 } 1201 } 1202 } else if (CUR == '*') { 1203 if (name != NULL) { 1204 ctxt->error = 1; 1205 goto error; 1206 } 1207 NEXT; 1208 PUSH(XML_OP_ALL, token, NULL); 1209 } else { 1210 PUSH(XML_OP_ELEM, name, NULL); 1211 } 1212 return; 1213 error: 1214 if (URL != NULL) 1215 XML_PAT_FREE_STRING(ctxt, URL) 1216 if (token != NULL) 1217 XML_PAT_FREE_STRING(ctxt, token) 1218 if (name != NULL) 1219 XML_PAT_FREE_STRING(ctxt, name) 1220 } 1221 1222 /** 1223 * xmlCompilePathPattern: 1224 * @ctxt: the compilation context 1225 * 1226 * Compile the Path Pattern and generates a precompiled 1227 * form suitable for fast matching. 1228 * 1229 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) 1230 */ 1231 static void 1232 xmlCompilePathPattern(xmlPatParserContextPtr ctxt) { 1233 SKIP_BLANKS; 1234 if (CUR == '/') { 1235 ctxt->comp->flags |= PAT_FROM_ROOT; 1236 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) { 1237 ctxt->comp->flags |= PAT_FROM_CUR; 1238 } 1239 1240 if ((CUR == '/') && (NXT(1) == '/')) { 1241 PUSH(XML_OP_ANCESTOR, NULL, NULL); 1242 NEXT; 1243 NEXT; 1244 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) { 1245 PUSH(XML_OP_ANCESTOR, NULL, NULL); 1246 NEXT; 1247 NEXT; 1248 NEXT; 1249 /* Check for incompleteness. */ 1250 SKIP_BLANKS; 1251 if (CUR == 0) { 1252 ERROR5(NULL, NULL, NULL, 1253 "Incomplete expression '%s'.\n", ctxt->base); 1254 ctxt->error = 1; 1255 goto error; 1256 } 1257 } 1258 if (CUR == '@') { 1259 NEXT; 1260 xmlCompileAttributeTest(ctxt); 1261 SKIP_BLANKS; 1262 /* TODO: check for incompleteness */ 1263 if (CUR != 0) { 1264 xmlCompileStepPattern(ctxt); 1265 if (ctxt->error != 0) 1266 goto error; 1267 } 1268 } else { 1269 if (CUR == '/') { 1270 PUSH(XML_OP_ROOT, NULL, NULL); 1271 NEXT; 1272 /* Check for incompleteness. */ 1273 SKIP_BLANKS; 1274 if (CUR == 0) { 1275 ERROR5(NULL, NULL, NULL, 1276 "Incomplete expression '%s'.\n", ctxt->base); 1277 ctxt->error = 1; 1278 goto error; 1279 } 1280 } 1281 xmlCompileStepPattern(ctxt); 1282 if (ctxt->error != 0) 1283 goto error; 1284 SKIP_BLANKS; 1285 while (CUR == '/') { 1286 if (NXT(1) == '/') { 1287 PUSH(XML_OP_ANCESTOR, NULL, NULL); 1288 NEXT; 1289 NEXT; 1290 SKIP_BLANKS; 1291 xmlCompileStepPattern(ctxt); 1292 if (ctxt->error != 0) 1293 goto error; 1294 } else { 1295 PUSH(XML_OP_PARENT, NULL, NULL); 1296 NEXT; 1297 SKIP_BLANKS; 1298 if (CUR == 0) { 1299 ERROR5(NULL, NULL, NULL, 1300 "Incomplete expression '%s'.\n", ctxt->base); 1301 ctxt->error = 1; 1302 goto error; 1303 } 1304 xmlCompileStepPattern(ctxt); 1305 if (ctxt->error != 0) 1306 goto error; 1307 } 1308 } 1309 } 1310 if (CUR != 0) { 1311 ERROR5(NULL, NULL, NULL, 1312 "Failed to compile pattern %s\n", ctxt->base); 1313 ctxt->error = 1; 1314 } 1315 error: 1316 return; 1317 } 1318 1319 /** 1320 * xmlCompileIDCXPathPath: 1321 * @ctxt: the compilation context 1322 * 1323 * Compile the Path Pattern and generates a precompiled 1324 * form suitable for fast matching. 1325 * 1326 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) 1327 */ 1328 static void 1329 xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) { 1330 SKIP_BLANKS; 1331 if (CUR == '/') { 1332 ERROR5(NULL, NULL, NULL, 1333 "Unexpected selection of the document root in '%s'.\n", 1334 ctxt->base); 1335 goto error; 1336 } 1337 ctxt->comp->flags |= PAT_FROM_CUR; 1338 1339 if (CUR == '.') { 1340 /* "." - "self::node()" */ 1341 NEXT; 1342 SKIP_BLANKS; 1343 if (CUR == 0) { 1344 /* 1345 * Selection of the context node. 1346 */ 1347 PUSH(XML_OP_ELEM, NULL, NULL); 1348 return; 1349 } 1350 if (CUR != '/') { 1351 /* TODO: A more meaningful error message. */ 1352 ERROR5(NULL, NULL, NULL, 1353 "Unexpected token after '.' in '%s'.\n", ctxt->base); 1354 goto error; 1355 } 1356 /* "./" - "self::node()/" */ 1357 NEXT; 1358 SKIP_BLANKS; 1359 if (CUR == '/') { 1360 if (IS_BLANK_CH(PEEKPREV(1))) { 1361 /* 1362 * Disallow "./ /" 1363 */ 1364 ERROR5(NULL, NULL, NULL, 1365 "Unexpected '/' token in '%s'.\n", ctxt->base); 1366 goto error; 1367 } 1368 /* ".//" - "self:node()/descendant-or-self::node()/" */ 1369 PUSH(XML_OP_ANCESTOR, NULL, NULL); 1370 NEXT; 1371 SKIP_BLANKS; 1372 } 1373 if (CUR == 0) 1374 goto error_unfinished; 1375 } 1376 /* 1377 * Process steps. 1378 */ 1379 do { 1380 xmlCompileStepPattern(ctxt); 1381 if (ctxt->error != 0) 1382 goto error; 1383 SKIP_BLANKS; 1384 if (CUR != '/') 1385 break; 1386 PUSH(XML_OP_PARENT, NULL, NULL); 1387 NEXT; 1388 SKIP_BLANKS; 1389 if (CUR == '/') { 1390 /* 1391 * Disallow subsequent '//'. 1392 */ 1393 ERROR5(NULL, NULL, NULL, 1394 "Unexpected subsequent '//' in '%s'.\n", 1395 ctxt->base); 1396 goto error; 1397 } 1398 if (CUR == 0) 1399 goto error_unfinished; 1400 1401 } while (CUR != 0); 1402 1403 if (CUR != 0) { 1404 ERROR5(NULL, NULL, NULL, 1405 "Failed to compile expression '%s'.\n", ctxt->base); 1406 ctxt->error = 1; 1407 } 1408 return; 1409 error: 1410 ctxt->error = 1; 1411 return; 1412 1413 error_unfinished: 1414 ctxt->error = 1; 1415 ERROR5(NULL, NULL, NULL, 1416 "Unfinished expression '%s'.\n", ctxt->base); 1417 return; 1418 } 1419 1420 /************************************************************************ 1421 * * 1422 * The streaming code * 1423 * * 1424 ************************************************************************/ 1425 1426 #ifdef DEBUG_STREAMING 1427 static void 1428 xmlDebugStreamComp(xmlStreamCompPtr stream) { 1429 int i; 1430 1431 if (stream == NULL) { 1432 printf("Stream: NULL\n"); 1433 return; 1434 } 1435 printf("Stream: %d steps\n", stream->nbStep); 1436 for (i = 0;i < stream->nbStep;i++) { 1437 if (stream->steps[i].ns != NULL) { 1438 printf("{%s}", stream->steps[i].ns); 1439 } 1440 if (stream->steps[i].name == NULL) { 1441 printf("* "); 1442 } else { 1443 printf("%s ", stream->steps[i].name); 1444 } 1445 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT) 1446 printf("root "); 1447 if (stream->steps[i].flags & XML_STREAM_STEP_DESC) 1448 printf("// "); 1449 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL) 1450 printf("final "); 1451 printf("\n"); 1452 } 1453 } 1454 static void 1455 xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) { 1456 int i; 1457 1458 if (ctxt == NULL) { 1459 printf("Stream: NULL\n"); 1460 return; 1461 } 1462 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState); 1463 if (match) 1464 printf("matches\n"); 1465 else 1466 printf("\n"); 1467 for (i = 0;i < ctxt->nbState;i++) { 1468 if (ctxt->states[2 * i] < 0) 1469 printf(" %d: free\n", i); 1470 else { 1471 printf(" %d: step %d, level %d", i, ctxt->states[2 * i], 1472 ctxt->states[(2 * i) + 1]); 1473 if (ctxt->comp->steps[ctxt->states[2 * i]].flags & 1474 XML_STREAM_STEP_DESC) 1475 printf(" //\n"); 1476 else 1477 printf("\n"); 1478 } 1479 } 1480 } 1481 #endif 1482 /** 1483 * xmlNewStreamComp: 1484 * @size: the number of expected steps 1485 * 1486 * build a new compiled pattern for streaming 1487 * 1488 * Returns the new structure or NULL in case of error. 1489 */ 1490 static xmlStreamCompPtr 1491 xmlNewStreamComp(int size) { 1492 xmlStreamCompPtr cur; 1493 1494 if (size < 4) 1495 size = 4; 1496 1497 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp)); 1498 if (cur == NULL) { 1499 ERROR(NULL, NULL, NULL, 1500 "xmlNewStreamComp: malloc failed\n"); 1501 return(NULL); 1502 } 1503 memset(cur, 0, sizeof(xmlStreamComp)); 1504 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep)); 1505 if (cur->steps == NULL) { 1506 xmlFree(cur); 1507 ERROR(NULL, NULL, NULL, 1508 "xmlNewStreamComp: malloc failed\n"); 1509 return(NULL); 1510 } 1511 cur->nbStep = 0; 1512 cur->maxStep = size; 1513 return(cur); 1514 } 1515 1516 /** 1517 * xmlFreeStreamComp: 1518 * @comp: the compiled pattern for streaming 1519 * 1520 * Free the compiled pattern for streaming 1521 */ 1522 static void 1523 xmlFreeStreamComp(xmlStreamCompPtr comp) { 1524 if (comp != NULL) { 1525 if (comp->steps != NULL) 1526 xmlFree(comp->steps); 1527 if (comp->dict != NULL) 1528 xmlDictFree(comp->dict); 1529 xmlFree(comp); 1530 } 1531 } 1532 1533 /** 1534 * xmlStreamCompAddStep: 1535 * @comp: the compiled pattern for streaming 1536 * @name: the first string, the name, or NULL for * 1537 * @ns: the second step, the namespace name 1538 * @flags: the flags for that step 1539 * 1540 * Add a new step to the compiled pattern 1541 * 1542 * Returns -1 in case of error or the step index if successful 1543 */ 1544 static int 1545 xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name, 1546 const xmlChar *ns, int nodeType, int flags) { 1547 xmlStreamStepPtr cur; 1548 1549 if (comp->nbStep >= comp->maxStep) { 1550 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps, 1551 comp->maxStep * 2 * sizeof(xmlStreamStep)); 1552 if (cur == NULL) { 1553 ERROR(NULL, NULL, NULL, 1554 "xmlNewStreamComp: malloc failed\n"); 1555 return(-1); 1556 } 1557 comp->steps = cur; 1558 comp->maxStep *= 2; 1559 } 1560 cur = &comp->steps[comp->nbStep++]; 1561 cur->flags = flags; 1562 cur->name = name; 1563 cur->ns = ns; 1564 cur->nodeType = nodeType; 1565 return(comp->nbStep - 1); 1566 } 1567 1568 /** 1569 * xmlStreamCompile: 1570 * @comp: the precompiled pattern 1571 * 1572 * Tries to stream compile a pattern 1573 * 1574 * Returns -1 in case of failure and 0 in case of success. 1575 */ 1576 static int 1577 xmlStreamCompile(xmlPatternPtr comp) { 1578 xmlStreamCompPtr stream; 1579 int i, s = 0, root = 0, flags = 0, prevs = -1; 1580 xmlStepOp step; 1581 1582 if ((comp == NULL) || (comp->steps == NULL)) 1583 return(-1); 1584 /* 1585 * special case for . 1586 */ 1587 if ((comp->nbStep == 1) && 1588 (comp->steps[0].op == XML_OP_ELEM) && 1589 (comp->steps[0].value == NULL) && 1590 (comp->steps[0].value2 == NULL)) { 1591 stream = xmlNewStreamComp(0); 1592 if (stream == NULL) 1593 return(-1); 1594 /* Note that the stream will have no steps in this case. */ 1595 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE; 1596 comp->stream = stream; 1597 return(0); 1598 } 1599 1600 stream = xmlNewStreamComp((comp->nbStep / 2) + 1); 1601 if (stream == NULL) 1602 return(-1); 1603 if (comp->dict != NULL) { 1604 stream->dict = comp->dict; 1605 xmlDictReference(stream->dict); 1606 } 1607 1608 i = 0; 1609 if (comp->flags & PAT_FROM_ROOT) 1610 stream->flags |= XML_STREAM_FROM_ROOT; 1611 1612 for (;i < comp->nbStep;i++) { 1613 step = comp->steps[i]; 1614 switch (step.op) { 1615 case XML_OP_END: 1616 break; 1617 case XML_OP_ROOT: 1618 if (i != 0) 1619 goto error; 1620 root = 1; 1621 break; 1622 case XML_OP_NS: 1623 s = xmlStreamCompAddStep(stream, NULL, step.value, 1624 XML_ELEMENT_NODE, flags); 1625 if (s < 0) 1626 goto error; 1627 prevs = s; 1628 flags = 0; 1629 break; 1630 case XML_OP_ATTR: 1631 flags |= XML_STREAM_STEP_ATTR; 1632 prevs = -1; 1633 s = xmlStreamCompAddStep(stream, 1634 step.value, step.value2, XML_ATTRIBUTE_NODE, flags); 1635 flags = 0; 1636 if (s < 0) 1637 goto error; 1638 break; 1639 case XML_OP_ELEM: 1640 if ((step.value == NULL) && (step.value2 == NULL)) { 1641 /* 1642 * We have a "." or "self::node()" here. 1643 * Eliminate redundant self::node() tests like in "/./." 1644 * or "//./" 1645 * The only case we won't eliminate is "//.", i.e. if 1646 * self::node() is the last node test and we had 1647 * continuation somewhere beforehand. 1648 */ 1649 if ((comp->nbStep == i + 1) && 1650 (flags & XML_STREAM_STEP_DESC)) { 1651 /* 1652 * Mark the special case where the expression resolves 1653 * to any type of node. 1654 */ 1655 if (comp->nbStep == i + 1) { 1656 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE; 1657 } 1658 flags |= XML_STREAM_STEP_NODE; 1659 s = xmlStreamCompAddStep(stream, NULL, NULL, 1660 XML_STREAM_ANY_NODE, flags); 1661 if (s < 0) 1662 goto error; 1663 flags = 0; 1664 /* 1665 * If there was a previous step, mark it to be added to 1666 * the result node-set; this is needed since only 1667 * the last step will be marked as "final" and only 1668 * "final" nodes are added to the resulting set. 1669 */ 1670 if (prevs != -1) { 1671 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET; 1672 prevs = -1; 1673 } 1674 break; 1675 1676 } else { 1677 /* Just skip this one. */ 1678 continue; 1679 } 1680 } 1681 /* An element node. */ 1682 s = xmlStreamCompAddStep(stream, step.value, step.value2, 1683 XML_ELEMENT_NODE, flags); 1684 if (s < 0) 1685 goto error; 1686 prevs = s; 1687 flags = 0; 1688 break; 1689 case XML_OP_CHILD: 1690 /* An element node child. */ 1691 s = xmlStreamCompAddStep(stream, step.value, step.value2, 1692 XML_ELEMENT_NODE, flags); 1693 if (s < 0) 1694 goto error; 1695 prevs = s; 1696 flags = 0; 1697 break; 1698 case XML_OP_ALL: 1699 s = xmlStreamCompAddStep(stream, NULL, NULL, 1700 XML_ELEMENT_NODE, flags); 1701 if (s < 0) 1702 goto error; 1703 prevs = s; 1704 flags = 0; 1705 break; 1706 case XML_OP_PARENT: 1707 break; 1708 case XML_OP_ANCESTOR: 1709 /* Skip redundant continuations. */ 1710 if (flags & XML_STREAM_STEP_DESC) 1711 break; 1712 flags |= XML_STREAM_STEP_DESC; 1713 /* 1714 * Mark the expression as having "//". 1715 */ 1716 if ((stream->flags & XML_STREAM_DESC) == 0) 1717 stream->flags |= XML_STREAM_DESC; 1718 break; 1719 } 1720 } 1721 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) { 1722 /* 1723 * If this should behave like a real pattern, we will mark 1724 * the first step as having "//", to be reentrant on every 1725 * tree level. 1726 */ 1727 if ((stream->flags & XML_STREAM_DESC) == 0) 1728 stream->flags |= XML_STREAM_DESC; 1729 1730 if (stream->nbStep > 0) { 1731 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0) 1732 stream->steps[0].flags |= XML_STREAM_STEP_DESC; 1733 } 1734 } 1735 if (stream->nbStep <= s) 1736 goto error; 1737 stream->steps[s].flags |= XML_STREAM_STEP_FINAL; 1738 if (root) 1739 stream->steps[0].flags |= XML_STREAM_STEP_ROOT; 1740 #ifdef DEBUG_STREAMING 1741 xmlDebugStreamComp(stream); 1742 #endif 1743 comp->stream = stream; 1744 return(0); 1745 error: 1746 xmlFreeStreamComp(stream); 1747 return(0); 1748 } 1749 1750 /** 1751 * xmlNewStreamCtxt: 1752 * @size: the number of expected states 1753 * 1754 * build a new stream context 1755 * 1756 * Returns the new structure or NULL in case of error. 1757 */ 1758 static xmlStreamCtxtPtr 1759 xmlNewStreamCtxt(xmlStreamCompPtr stream) { 1760 xmlStreamCtxtPtr cur; 1761 1762 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt)); 1763 if (cur == NULL) { 1764 ERROR(NULL, NULL, NULL, 1765 "xmlNewStreamCtxt: malloc failed\n"); 1766 return(NULL); 1767 } 1768 memset(cur, 0, sizeof(xmlStreamCtxt)); 1769 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int)); 1770 if (cur->states == NULL) { 1771 xmlFree(cur); 1772 ERROR(NULL, NULL, NULL, 1773 "xmlNewStreamCtxt: malloc failed\n"); 1774 return(NULL); 1775 } 1776 cur->nbState = 0; 1777 cur->maxState = 4; 1778 cur->level = 0; 1779 cur->comp = stream; 1780 cur->blockLevel = -1; 1781 return(cur); 1782 } 1783 1784 /** 1785 * xmlFreeStreamCtxt: 1786 * @stream: the stream context 1787 * 1788 * Free the stream context 1789 */ 1790 void 1791 xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) { 1792 xmlStreamCtxtPtr next; 1793 1794 while (stream != NULL) { 1795 next = stream->next; 1796 if (stream->states != NULL) 1797 xmlFree(stream->states); 1798 xmlFree(stream); 1799 stream = next; 1800 } 1801 } 1802 1803 /** 1804 * xmlStreamCtxtAddState: 1805 * @comp: the stream context 1806 * @idx: the step index for that streaming state 1807 * 1808 * Add a new state to the stream context 1809 * 1810 * Returns -1 in case of error or the state index if successful 1811 */ 1812 static int 1813 xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) { 1814 int i; 1815 for (i = 0;i < comp->nbState;i++) { 1816 if (comp->states[2 * i] < 0) { 1817 comp->states[2 * i] = idx; 1818 comp->states[2 * i + 1] = level; 1819 return(i); 1820 } 1821 } 1822 if (comp->nbState >= comp->maxState) { 1823 int *cur; 1824 1825 cur = (int *) xmlRealloc(comp->states, 1826 comp->maxState * 4 * sizeof(int)); 1827 if (cur == NULL) { 1828 ERROR(NULL, NULL, NULL, 1829 "xmlNewStreamCtxt: malloc failed\n"); 1830 return(-1); 1831 } 1832 comp->states = cur; 1833 comp->maxState *= 2; 1834 } 1835 comp->states[2 * comp->nbState] = idx; 1836 comp->states[2 * comp->nbState++ + 1] = level; 1837 return(comp->nbState - 1); 1838 } 1839 1840 /** 1841 * xmlStreamPushInternal: 1842 * @stream: the stream context 1843 * @name: the current name 1844 * @ns: the namespace name 1845 * @nodeType: the type of the node 1846 * 1847 * Push new data onto the stream. NOTE: if the call xmlPatterncompile() 1848 * indicated a dictionary, then strings for name and ns will be expected 1849 * to come from the dictionary. 1850 * Both @name and @ns being NULL means the / i.e. the root of the document. 1851 * This can also act as a reset. 1852 * 1853 * Returns: -1 in case of error, 1 if the current state in the stream is a 1854 * match and 0 otherwise. 1855 */ 1856 static int 1857 xmlStreamPushInternal(xmlStreamCtxtPtr stream, 1858 const xmlChar *name, const xmlChar *ns, 1859 int nodeType) { 1860 int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc; 1861 xmlStreamCompPtr comp; 1862 xmlStreamStep step; 1863 #ifdef DEBUG_STREAMING 1864 xmlStreamCtxtPtr orig = stream; 1865 #endif 1866 1867 if ((stream == NULL) || (stream->nbState < 0)) 1868 return(-1); 1869 1870 while (stream != NULL) { 1871 comp = stream->comp; 1872 1873 if ((nodeType == XML_ELEMENT_NODE) && 1874 (name == NULL) && (ns == NULL)) { 1875 /* We have a document node here (or a reset). */ 1876 stream->nbState = 0; 1877 stream->level = 0; 1878 stream->blockLevel = -1; 1879 if (comp->flags & XML_STREAM_FROM_ROOT) { 1880 if (comp->nbStep == 0) { 1881 /* TODO: We have a "/." here? */ 1882 ret = 1; 1883 } else { 1884 if ((comp->nbStep == 1) && 1885 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) && 1886 (comp->steps[0].flags & XML_STREAM_STEP_DESC)) 1887 { 1888 /* 1889 * In the case of "//." the document node will match 1890 * as well. 1891 */ 1892 ret = 1; 1893 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) { 1894 /* TODO: Do we need this ? */ 1895 tmp = xmlStreamCtxtAddState(stream, 0, 0); 1896 if (tmp < 0) 1897 err++; 1898 } 1899 } 1900 } 1901 stream = stream->next; 1902 continue; /* while */ 1903 } 1904 1905 /* 1906 * Fast check for ".". 1907 */ 1908 if (comp->nbStep == 0) { 1909 /* 1910 * / and . are handled at the XPath node set creation 1911 * level by checking min depth 1912 */ 1913 if (stream->flags & XML_PATTERN_XPATH) { 1914 stream = stream->next; 1915 continue; /* while */ 1916 } 1917 /* 1918 * For non-pattern like evaluation like XML Schema IDCs 1919 * or traditional XPath expressions, this will match if 1920 * we are at the first level only, otherwise on every level. 1921 */ 1922 if ((nodeType != XML_ATTRIBUTE_NODE) && 1923 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) || 1924 (stream->level == 0))) { 1925 ret = 1; 1926 } 1927 stream->level++; 1928 goto stream_next; 1929 } 1930 if (stream->blockLevel != -1) { 1931 /* 1932 * Skip blocked expressions. 1933 */ 1934 stream->level++; 1935 goto stream_next; 1936 } 1937 1938 if ((nodeType != XML_ELEMENT_NODE) && 1939 (nodeType != XML_ATTRIBUTE_NODE) && 1940 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) { 1941 /* 1942 * No need to process nodes of other types if we don't 1943 * resolve to those types. 1944 * TODO: Do we need to block the context here? 1945 */ 1946 stream->level++; 1947 goto stream_next; 1948 } 1949 1950 /* 1951 * Check evolution of existing states 1952 */ 1953 i = 0; 1954 m = stream->nbState; 1955 while (i < m) { 1956 if ((comp->flags & XML_STREAM_DESC) == 0) { 1957 /* 1958 * If there is no "//", then only the last 1959 * added state is of interest. 1960 */ 1961 stepNr = stream->states[2 * (stream->nbState -1)]; 1962 /* 1963 * TODO: Security check, should not happen, remove it. 1964 */ 1965 if (stream->states[(2 * (stream->nbState -1)) + 1] < 1966 stream->level) { 1967 return (-1); 1968 } 1969 desc = 0; 1970 /* loop-stopper */ 1971 i = m; 1972 } else { 1973 /* 1974 * If there are "//", then we need to process every "//" 1975 * occuring in the states, plus any other state for this 1976 * level. 1977 */ 1978 stepNr = stream->states[2 * i]; 1979 1980 /* TODO: should not happen anymore: dead states */ 1981 if (stepNr < 0) 1982 goto next_state; 1983 1984 tmp = stream->states[(2 * i) + 1]; 1985 1986 /* skip new states just added */ 1987 if (tmp > stream->level) 1988 goto next_state; 1989 1990 /* skip states at ancestor levels, except if "//" */ 1991 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC; 1992 if ((tmp < stream->level) && (!desc)) 1993 goto next_state; 1994 } 1995 /* 1996 * Check for correct node-type. 1997 */ 1998 step = comp->steps[stepNr]; 1999 if (step.nodeType != nodeType) { 2000 if (step.nodeType == XML_ATTRIBUTE_NODE) { 2001 /* 2002 * Block this expression for deeper evaluation. 2003 */ 2004 if ((comp->flags & XML_STREAM_DESC) == 0) 2005 stream->blockLevel = stream->level +1; 2006 goto next_state; 2007 } else if (step.nodeType != XML_STREAM_ANY_NODE) 2008 goto next_state; 2009 } 2010 /* 2011 * Compare local/namespace-name. 2012 */ 2013 match = 0; 2014 if (step.nodeType == XML_STREAM_ANY_NODE) { 2015 match = 1; 2016 } else if (step.name == NULL) { 2017 if (step.ns == NULL) { 2018 /* 2019 * This lets through all elements/attributes. 2020 */ 2021 match = 1; 2022 } else if (ns != NULL) 2023 match = xmlStrEqual(step.ns, ns); 2024 } else if (((step.ns != NULL) == (ns != NULL)) && 2025 (name != NULL) && 2026 (step.name[0] == name[0]) && 2027 xmlStrEqual(step.name, name) && 2028 ((step.ns == ns) || xmlStrEqual(step.ns, ns))) 2029 { 2030 match = 1; 2031 } 2032 #if 0 2033 /* 2034 * TODO: Pointer comparison won't work, since not guaranteed that the given 2035 * values are in the same dict; especially if it's the namespace name, 2036 * normally coming from ns->href. We need a namespace dict mechanism ! 2037 */ 2038 } else if (comp->dict) { 2039 if (step.name == NULL) { 2040 if (step.ns == NULL) 2041 match = 1; 2042 else 2043 match = (step.ns == ns); 2044 } else { 2045 match = ((step.name == name) && (step.ns == ns)); 2046 } 2047 #endif /* if 0 ------------------------------------------------------- */ 2048 if (match) { 2049 final = step.flags & XML_STREAM_STEP_FINAL; 2050 if (desc) { 2051 if (final) { 2052 ret = 1; 2053 } else { 2054 /* descending match create a new state */ 2055 xmlStreamCtxtAddState(stream, stepNr + 1, 2056 stream->level + 1); 2057 } 2058 } else { 2059 if (final) { 2060 ret = 1; 2061 } else { 2062 xmlStreamCtxtAddState(stream, stepNr + 1, 2063 stream->level + 1); 2064 } 2065 } 2066 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) { 2067 /* 2068 * Check if we have a special case like "foo/bar//.", where 2069 * "foo" is selected as well. 2070 */ 2071 ret = 1; 2072 } 2073 } 2074 if (((comp->flags & XML_STREAM_DESC) == 0) && 2075 ((! match) || final)) { 2076 /* 2077 * Mark this expression as blocked for any evaluation at 2078 * deeper levels. Note that this includes "/foo" 2079 * expressions if the *pattern* behaviour is used. 2080 */ 2081 stream->blockLevel = stream->level +1; 2082 } 2083 next_state: 2084 i++; 2085 } 2086 2087 stream->level++; 2088 2089 /* 2090 * Re/enter the expression. 2091 * Don't reenter if it's an absolute expression like "/foo", 2092 * except "//foo". 2093 */ 2094 step = comp->steps[0]; 2095 if (step.flags & XML_STREAM_STEP_ROOT) 2096 goto stream_next; 2097 2098 desc = step.flags & XML_STREAM_STEP_DESC; 2099 if (stream->flags & XML_PATTERN_NOTPATTERN) { 2100 /* 2101 * Re/enter the expression if it is a "descendant" one, 2102 * or if we are at the 1st level of evaluation. 2103 */ 2104 2105 if (stream->level == 1) { 2106 if (XML_STREAM_XS_IDC(stream)) { 2107 /* 2108 * XS-IDC: The missing "self::node()" will always 2109 * match the first given node. 2110 */ 2111 goto stream_next; 2112 } else 2113 goto compare; 2114 } 2115 /* 2116 * A "//" is always reentrant. 2117 */ 2118 if (desc) 2119 goto compare; 2120 2121 /* 2122 * XS-IDC: Process the 2nd level, since the missing 2123 * "self::node()" is responsible for the 2nd level being 2124 * the real start level. 2125 */ 2126 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream)) 2127 goto compare; 2128 2129 goto stream_next; 2130 } 2131 2132 compare: 2133 /* 2134 * Check expected node-type. 2135 */ 2136 if (step.nodeType != nodeType) { 2137 if (nodeType == XML_ATTRIBUTE_NODE) 2138 goto stream_next; 2139 else if (step.nodeType != XML_STREAM_ANY_NODE) 2140 goto stream_next; 2141 } 2142 /* 2143 * Compare local/namespace-name. 2144 */ 2145 match = 0; 2146 if (step.nodeType == XML_STREAM_ANY_NODE) { 2147 match = 1; 2148 } else if (step.name == NULL) { 2149 if (step.ns == NULL) { 2150 /* 2151 * This lets through all elements/attributes. 2152 */ 2153 match = 1; 2154 } else if (ns != NULL) 2155 match = xmlStrEqual(step.ns, ns); 2156 } else if (((step.ns != NULL) == (ns != NULL)) && 2157 (name != NULL) && 2158 (step.name[0] == name[0]) && 2159 xmlStrEqual(step.name, name) && 2160 ((step.ns == ns) || xmlStrEqual(step.ns, ns))) 2161 { 2162 match = 1; 2163 } 2164 final = step.flags & XML_STREAM_STEP_FINAL; 2165 if (match) { 2166 if (final) 2167 ret = 1; 2168 else 2169 xmlStreamCtxtAddState(stream, 1, stream->level); 2170 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) { 2171 /* 2172 * Check if we have a special case like "foo//.", where 2173 * "foo" is selected as well. 2174 */ 2175 ret = 1; 2176 } 2177 } 2178 if (((comp->flags & XML_STREAM_DESC) == 0) && 2179 ((! match) || final)) { 2180 /* 2181 * Mark this expression as blocked for any evaluation at 2182 * deeper levels. 2183 */ 2184 stream->blockLevel = stream->level; 2185 } 2186 2187 stream_next: 2188 stream = stream->next; 2189 } /* while stream != NULL */ 2190 2191 if (err > 0) 2192 ret = -1; 2193 #ifdef DEBUG_STREAMING 2194 xmlDebugStreamCtxt(orig, ret); 2195 #endif 2196 return(ret); 2197 } 2198 2199 /** 2200 * xmlStreamPush: 2201 * @stream: the stream context 2202 * @name: the current name 2203 * @ns: the namespace name 2204 * 2205 * Push new data onto the stream. NOTE: if the call xmlPatterncompile() 2206 * indicated a dictionary, then strings for name and ns will be expected 2207 * to come from the dictionary. 2208 * Both @name and @ns being NULL means the / i.e. the root of the document. 2209 * This can also act as a reset. 2210 * Otherwise the function will act as if it has been given an element-node. 2211 * 2212 * Returns: -1 in case of error, 1 if the current state in the stream is a 2213 * match and 0 otherwise. 2214 */ 2215 int 2216 xmlStreamPush(xmlStreamCtxtPtr stream, 2217 const xmlChar *name, const xmlChar *ns) { 2218 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE)); 2219 } 2220 2221 /** 2222 * xmlStreamPushNode: 2223 * @stream: the stream context 2224 * @name: the current name 2225 * @ns: the namespace name 2226 * @nodeType: the type of the node being pushed 2227 * 2228 * Push new data onto the stream. NOTE: if the call xmlPatterncompile() 2229 * indicated a dictionary, then strings for name and ns will be expected 2230 * to come from the dictionary. 2231 * Both @name and @ns being NULL means the / i.e. the root of the document. 2232 * This can also act as a reset. 2233 * Different from xmlStreamPush() this function can be fed with nodes of type: 2234 * element-, attribute-, text-, cdata-section-, comment- and 2235 * processing-instruction-node. 2236 * 2237 * Returns: -1 in case of error, 1 if the current state in the stream is a 2238 * match and 0 otherwise. 2239 */ 2240 int 2241 xmlStreamPushNode(xmlStreamCtxtPtr stream, 2242 const xmlChar *name, const xmlChar *ns, 2243 int nodeType) 2244 { 2245 return (xmlStreamPushInternal(stream, name, ns, 2246 nodeType)); 2247 } 2248 2249 /** 2250 * xmlStreamPushAttr: 2251 * @stream: the stream context 2252 * @name: the current name 2253 * @ns: the namespace name 2254 * 2255 * Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile() 2256 * indicated a dictionary, then strings for name and ns will be expected 2257 * to come from the dictionary. 2258 * Both @name and @ns being NULL means the / i.e. the root of the document. 2259 * This can also act as a reset. 2260 * Otherwise the function will act as if it has been given an attribute-node. 2261 * 2262 * Returns: -1 in case of error, 1 if the current state in the stream is a 2263 * match and 0 otherwise. 2264 */ 2265 int 2266 xmlStreamPushAttr(xmlStreamCtxtPtr stream, 2267 const xmlChar *name, const xmlChar *ns) { 2268 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE)); 2269 } 2270 2271 /** 2272 * xmlStreamPop: 2273 * @stream: the stream context 2274 * 2275 * push one level from the stream. 2276 * 2277 * Returns: -1 in case of error, 0 otherwise. 2278 */ 2279 int 2280 xmlStreamPop(xmlStreamCtxtPtr stream) { 2281 int i, lev; 2282 2283 if (stream == NULL) 2284 return(-1); 2285 while (stream != NULL) { 2286 /* 2287 * Reset block-level. 2288 */ 2289 if (stream->blockLevel == stream->level) 2290 stream->blockLevel = -1; 2291 2292 /* 2293 * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set 2294 * (see the thread at 2295 * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html) 2296 */ 2297 if (stream->level) 2298 stream->level--; 2299 /* 2300 * Check evolution of existing states 2301 */ 2302 for (i = stream->nbState -1; i >= 0; i--) { 2303 /* discard obsoleted states */ 2304 lev = stream->states[(2 * i) + 1]; 2305 if (lev > stream->level) 2306 stream->nbState--; 2307 if (lev <= stream->level) 2308 break; 2309 } 2310 stream = stream->next; 2311 } 2312 return(0); 2313 } 2314 2315 /** 2316 * xmlStreamWantsAnyNode: 2317 * @streamCtxt: the stream context 2318 * 2319 * Query if the streaming pattern additionally needs to be fed with 2320 * text-, cdata-section-, comment- and processing-instruction-nodes. 2321 * If the result is 0 then only element-nodes and attribute-nodes 2322 * need to be pushed. 2323 * 2324 * Returns: 1 in case of need of nodes of the above described types, 2325 * 0 otherwise. -1 on API errors. 2326 */ 2327 int 2328 xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt) 2329 { 2330 if (streamCtxt == NULL) 2331 return(-1); 2332 while (streamCtxt != NULL) { 2333 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) 2334 return(1); 2335 streamCtxt = streamCtxt->next; 2336 } 2337 return(0); 2338 } 2339 2340 /************************************************************************ 2341 * * 2342 * The public interfaces * 2343 * * 2344 ************************************************************************/ 2345 2346 /** 2347 * xmlPatterncompile: 2348 * @pattern: the pattern to compile 2349 * @dict: an optional dictionary for interned strings 2350 * @flags: compilation flags, see xmlPatternFlags 2351 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL 2352 * 2353 * Compile a pattern. 2354 * 2355 * Returns the compiled form of the pattern or NULL in case of error 2356 */ 2357 xmlPatternPtr 2358 xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags, 2359 const xmlChar **namespaces) { 2360 xmlPatternPtr ret = NULL, cur; 2361 xmlPatParserContextPtr ctxt = NULL; 2362 const xmlChar *or, *start; 2363 xmlChar *tmp = NULL; 2364 int type = 0; 2365 int streamable = 1; 2366 2367 if (pattern == NULL) 2368 return(NULL); 2369 2370 start = pattern; 2371 or = start; 2372 while (*or != 0) { 2373 tmp = NULL; 2374 while ((*or != 0) && (*or != '|')) or++; 2375 if (*or == 0) 2376 ctxt = xmlNewPatParserContext(start, dict, namespaces); 2377 else { 2378 tmp = xmlStrndup(start, or - start); 2379 if (tmp != NULL) { 2380 ctxt = xmlNewPatParserContext(tmp, dict, namespaces); 2381 } 2382 or++; 2383 } 2384 if (ctxt == NULL) goto error; 2385 cur = xmlNewPattern(); 2386 if (cur == NULL) goto error; 2387 /* 2388 * Assign string dict. 2389 */ 2390 if (dict) { 2391 cur->dict = dict; 2392 xmlDictReference(dict); 2393 } 2394 if (ret == NULL) 2395 ret = cur; 2396 else { 2397 cur->next = ret->next; 2398 ret->next = cur; 2399 } 2400 cur->flags = flags; 2401 ctxt->comp = cur; 2402 2403 if (XML_STREAM_XS_IDC(cur)) 2404 xmlCompileIDCXPathPath(ctxt); 2405 else 2406 xmlCompilePathPattern(ctxt); 2407 if (ctxt->error != 0) 2408 goto error; 2409 xmlFreePatParserContext(ctxt); 2410 ctxt = NULL; 2411 2412 2413 if (streamable) { 2414 if (type == 0) { 2415 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR); 2416 } else if (type == PAT_FROM_ROOT) { 2417 if (cur->flags & PAT_FROM_CUR) 2418 streamable = 0; 2419 } else if (type == PAT_FROM_CUR) { 2420 if (cur->flags & PAT_FROM_ROOT) 2421 streamable = 0; 2422 } 2423 } 2424 if (streamable) 2425 xmlStreamCompile(cur); 2426 if (xmlReversePattern(cur) < 0) 2427 goto error; 2428 if (tmp != NULL) { 2429 xmlFree(tmp); 2430 tmp = NULL; 2431 } 2432 start = or; 2433 } 2434 if (streamable == 0) { 2435 cur = ret; 2436 while (cur != NULL) { 2437 if (cur->stream != NULL) { 2438 xmlFreeStreamComp(cur->stream); 2439 cur->stream = NULL; 2440 } 2441 cur = cur->next; 2442 } 2443 } 2444 2445 return(ret); 2446 error: 2447 if (ctxt != NULL) xmlFreePatParserContext(ctxt); 2448 if (ret != NULL) xmlFreePattern(ret); 2449 if (tmp != NULL) xmlFree(tmp); 2450 return(NULL); 2451 } 2452 2453 /** 2454 * xmlPatternMatch: 2455 * @comp: the precompiled pattern 2456 * @node: a node 2457 * 2458 * Test whether the node matches the pattern 2459 * 2460 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure 2461 */ 2462 int 2463 xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node) 2464 { 2465 int ret = 0; 2466 2467 if ((comp == NULL) || (node == NULL)) 2468 return(-1); 2469 2470 while (comp != NULL) { 2471 ret = xmlPatMatch(comp, node); 2472 if (ret != 0) 2473 return(ret); 2474 comp = comp->next; 2475 } 2476 return(ret); 2477 } 2478 2479 /** 2480 * xmlPatternGetStreamCtxt: 2481 * @comp: the precompiled pattern 2482 * 2483 * Get a streaming context for that pattern 2484 * Use xmlFreeStreamCtxt to free the context. 2485 * 2486 * Returns a pointer to the context or NULL in case of failure 2487 */ 2488 xmlStreamCtxtPtr 2489 xmlPatternGetStreamCtxt(xmlPatternPtr comp) 2490 { 2491 xmlStreamCtxtPtr ret = NULL, cur; 2492 2493 if ((comp == NULL) || (comp->stream == NULL)) 2494 return(NULL); 2495 2496 while (comp != NULL) { 2497 if (comp->stream == NULL) 2498 goto failed; 2499 cur = xmlNewStreamCtxt(comp->stream); 2500 if (cur == NULL) 2501 goto failed; 2502 if (ret == NULL) 2503 ret = cur; 2504 else { 2505 cur->next = ret->next; 2506 ret->next = cur; 2507 } 2508 cur->flags = comp->flags; 2509 comp = comp->next; 2510 } 2511 return(ret); 2512 failed: 2513 xmlFreeStreamCtxt(ret); 2514 return(NULL); 2515 } 2516 2517 /** 2518 * xmlPatternStreamable: 2519 * @comp: the precompiled pattern 2520 * 2521 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt() 2522 * should work. 2523 * 2524 * Returns 1 if streamable, 0 if not and -1 in case of error. 2525 */ 2526 int 2527 xmlPatternStreamable(xmlPatternPtr comp) { 2528 if (comp == NULL) 2529 return(-1); 2530 while (comp != NULL) { 2531 if (comp->stream == NULL) 2532 return(0); 2533 comp = comp->next; 2534 } 2535 return(1); 2536 } 2537 2538 /** 2539 * xmlPatternMaxDepth: 2540 * @comp: the precompiled pattern 2541 * 2542 * Check the maximum depth reachable by a pattern 2543 * 2544 * Returns -2 if no limit (using //), otherwise the depth, 2545 * and -1 in case of error 2546 */ 2547 int 2548 xmlPatternMaxDepth(xmlPatternPtr comp) { 2549 int ret = 0, i; 2550 if (comp == NULL) 2551 return(-1); 2552 while (comp != NULL) { 2553 if (comp->stream == NULL) 2554 return(-1); 2555 for (i = 0;i < comp->stream->nbStep;i++) 2556 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC) 2557 return(-2); 2558 if (comp->stream->nbStep > ret) 2559 ret = comp->stream->nbStep; 2560 comp = comp->next; 2561 } 2562 return(ret); 2563 } 2564 2565 /** 2566 * xmlPatternMinDepth: 2567 * @comp: the precompiled pattern 2568 * 2569 * Check the minimum depth reachable by a pattern, 0 mean the / or . are 2570 * part of the set. 2571 * 2572 * Returns -1 in case of error otherwise the depth, 2573 * 2574 */ 2575 int 2576 xmlPatternMinDepth(xmlPatternPtr comp) { 2577 int ret = 12345678; 2578 if (comp == NULL) 2579 return(-1); 2580 while (comp != NULL) { 2581 if (comp->stream == NULL) 2582 return(-1); 2583 if (comp->stream->nbStep < ret) 2584 ret = comp->stream->nbStep; 2585 if (ret == 0) 2586 return(0); 2587 comp = comp->next; 2588 } 2589 return(ret); 2590 } 2591 2592 /** 2593 * xmlPatternFromRoot: 2594 * @comp: the precompiled pattern 2595 * 2596 * Check if the pattern must be looked at from the root. 2597 * 2598 * Returns 1 if true, 0 if false and -1 in case of error 2599 */ 2600 int 2601 xmlPatternFromRoot(xmlPatternPtr comp) { 2602 if (comp == NULL) 2603 return(-1); 2604 while (comp != NULL) { 2605 if (comp->stream == NULL) 2606 return(-1); 2607 if (comp->flags & PAT_FROM_ROOT) 2608 return(1); 2609 comp = comp->next; 2610 } 2611 return(0); 2612 2613 } 2614 2615 #define bottom_pattern 2616 #include "elfgcchack.h" 2617 #endif /* LIBXML_PATTERN_ENABLED */ 2618