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