xpointer.c revision 9e8bfae59a087a938a810a5527023b1e7b0e15b3
1/* 2 * xpointer.c : Code to handle XML Pointer 3 * 4 * World Wide Web Consortium Working Draft 03-March-1998 5 * http://www.w3.org/TR/2000/CR-xptr-20000607 6 * 7 * See Copyright for the status of this software. 8 * 9 * Daniel.Veillard@w3.org 10 */ 11 12#ifdef WIN32 13#include "win32config.h" 14#else 15#include "config.h" 16#endif 17 18/** 19 * TODO: better handling of error cases, the full expression should 20 * be parsed beforehand instead of a progressive evaluation 21 * TODO: Access into entities references are not supported now ... 22 * need a start to be able to pop out of entities refs since 23 * parent is the endity declaration, not the ref. 24 * TODO: some functions are still missing ! 25 */ 26 27#include <stdio.h> 28#include <string.h> 29#include <libxml/xpointer.h> 30#include <libxml/xmlmemory.h> 31#include <libxml/parserInternals.h> 32#include <libxml/xpath.h> 33#include <libxml/xpathInternals.h> 34#ifdef LIBXML_DEBUG_ENABLED 35#include <libxml/debugXML.h> 36#endif 37#include <libxml/xmlerror.h> 38 39#ifdef LIBXML_XPTR_ENABLED 40 41/* #define DEBUG_RANGES */ 42 43#define TODO \ 44 xmlGenericError(xmlGenericErrorContext, \ 45 "Unimplemented block at %s:%d\n", \ 46 __FILE__, __LINE__); 47 48#define STRANGE \ 49 xmlGenericError(xmlGenericErrorContext, \ 50 "Internal error at %s:%d\n", \ 51 __FILE__, __LINE__); 52 53/************************************************************************ 54 * * 55 * A few helper functions for child sequences * 56 * * 57 ************************************************************************/ 58 59/** 60 * xmlXPtrGetArity: 61 * @cur: the node 62 * 63 * Returns the number of child for an element, -1 in case of error 64 */ 65int 66xmlXPtrGetArity(xmlNodePtr cur) { 67 int i; 68 if (cur == NULL) 69 return(-1); 70 cur = cur->children; 71 for (i = 0;cur != NULL;cur = cur->next) { 72 if ((cur->type == XML_ELEMENT_NODE) || 73 (cur->type == XML_DOCUMENT_NODE) || 74 (cur->type == XML_HTML_DOCUMENT_NODE)) { 75 i++; 76 } 77 } 78 return(i); 79} 80 81/** 82 * xmlXPtrGetIndex: 83 * @cur: the node 84 * 85 * Returns the index of the node in its parent children list, -1 86 * in case of error 87 */ 88int 89xmlXPtrGetIndex(xmlNodePtr cur) { 90 int i; 91 if (cur == NULL) 92 return(-1); 93 for (i = 1;cur != NULL;cur = cur->prev) { 94 if ((cur->type == XML_ELEMENT_NODE) || 95 (cur->type == XML_DOCUMENT_NODE) || 96 (cur->type == XML_HTML_DOCUMENT_NODE)) { 97 i++; 98 } 99 } 100 return(i); 101} 102 103/** 104 * xmlXPtrGetNthChild: 105 * @cur: the node 106 * @no: the child number 107 * 108 * Returns the @no'th element child of @cur or NULL 109 */ 110xmlNodePtr 111xmlXPtrGetNthChild(xmlNodePtr cur, int no) { 112 int i; 113 if (cur == NULL) 114 return(cur); 115 cur = cur->children; 116 for (i = 0;i <= no;cur = cur->next) { 117 if (cur == NULL) 118 return(cur); 119 if ((cur->type == XML_ELEMENT_NODE) || 120 (cur->type == XML_DOCUMENT_NODE) || 121 (cur->type == XML_HTML_DOCUMENT_NODE)) { 122 i++; 123 if (i == no) 124 break; 125 } 126 } 127 return(cur); 128} 129 130/************************************************************************ 131 * * 132 * Handling of XPointer specific types * 133 * * 134 ************************************************************************/ 135 136/** 137 * xmlXPtrCmpPoints: 138 * @node1: the first node 139 * @index1: the first index 140 * @node2: the second node 141 * @index2: the second index 142 * 143 * Compare two points w.r.t document order 144 * 145 * Returns -2 in case of error 1 if first point < second point, 0 if 146 * that's the same point, -1 otherwise 147 */ 148int 149xmlXPtrCmpPoints(xmlNodePtr node1, int index1, xmlNodePtr node2, int index2) { 150 int depth1, depth2; 151 xmlNodePtr cur, root; 152 153 if ((node1 == NULL) || (node2 == NULL)) 154 return(-2); 155 /* 156 * a couple of optimizations which will avoid computations in most cases 157 */ 158 if (node1 == node2) { 159 if (index1 < index2) 160 return(1); 161 if (index1 > index2) 162 return(-1); 163 return(0); 164 } 165 if (node1 == node2->prev) 166 return(1); 167 if (node1 == node2->next) 168 return(-1); 169 170 /* 171 * compute depth to root 172 */ 173 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) { 174 if (cur == node1) 175 return(1); 176 depth2++; 177 } 178 root = cur; 179 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) { 180 if (cur == node2) 181 return(-1); 182 depth1++; 183 } 184 /* 185 * Distinct document (or distinct entities :-( ) case. 186 */ 187 if (root != cur) { 188 return(-2); 189 } 190 /* 191 * get the nearest common ancestor. 192 */ 193 while (depth1 > depth2) { 194 depth1--; 195 node1 = node1->parent; 196 } 197 while (depth2 > depth1) { 198 depth2--; 199 node2 = node2->parent; 200 } 201 while (node1->parent != node2->parent) { 202 node1 = node1->parent; 203 node2 = node2->parent; 204 /* should not happen but just in case ... */ 205 if ((node1 == NULL) || (node2 == NULL)) 206 return(-2); 207 } 208 /* 209 * Find who's first. 210 */ 211 if (node1 == node2->next) 212 return(-1); 213 for (cur = node1->next;cur != NULL;cur = cur->next) 214 if (cur == node2) 215 return(1); 216 return(-1); /* assume there is no sibling list corruption */ 217} 218 219/** 220 * xmlXPtrNewPoint: 221 * @node: the xmlNodePtr 222 * @index: the index within the node 223 * 224 * Create a new xmlXPathObjectPtr of type point 225 * 226 * Returns the newly created object. 227 */ 228xmlXPathObjectPtr 229xmlXPtrNewPoint(xmlNodePtr node, int index) { 230 xmlXPathObjectPtr ret; 231 232 if (node == NULL) 233 return(NULL); 234 if (index < 0) 235 return(NULL); 236 237 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 238 if (ret == NULL) { 239 xmlGenericError(xmlGenericErrorContext, 240 "xmlXPtrNewPoint: out of memory\n"); 241 return(NULL); 242 } 243 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 244 ret->type = XPATH_POINT; 245 ret->user = (void *) node; 246 ret->index = index; 247 return(ret); 248} 249 250/** 251 * xmlXPtrRangeCheckOrder: 252 * @range: an object range 253 * 254 * Make sure the points in the range are in the right order 255 */ 256void 257xmlXPtrRangeCheckOrder(xmlXPathObjectPtr range) { 258 int tmp; 259 xmlNodePtr tmp2; 260 if (range == NULL) 261 return; 262 if (range->type != XPATH_RANGE) 263 return; 264 if (range->user2 == NULL) 265 return; 266 tmp = xmlXPtrCmpPoints(range->user, range->index, 267 range->user2, range->index2); 268 if (tmp == -1) { 269 tmp2 = range->user; 270 range->user = range->user2; 271 range->user2 = tmp2; 272 tmp = range->index; 273 range->index = range->index2; 274 range->index2 = tmp; 275 } 276} 277 278/** 279 * xmlXPtrNewRange: 280 * @start: the starting node 281 * @startindex: the start index 282 * @end: the ending point 283 * @endindex: the ending index 284 * 285 * Create a new xmlXPathObjectPtr of type range 286 * 287 * Returns the newly created object. 288 */ 289xmlXPathObjectPtr 290xmlXPtrNewRange(xmlNodePtr start, int startindex, 291 xmlNodePtr end, int endindex) { 292 xmlXPathObjectPtr ret; 293 294 if (start == NULL) 295 return(NULL); 296 if (end == NULL) 297 return(NULL); 298 if (startindex < 0) 299 return(NULL); 300 if (endindex < 0) 301 return(NULL); 302 303 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 304 if (ret == NULL) { 305 xmlGenericError(xmlGenericErrorContext, 306 "xmlXPtrNewRangePoints: out of memory\n"); 307 return(NULL); 308 } 309 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 310 ret->type = XPATH_RANGE; 311 ret->user = start; 312 ret->index = startindex; 313 ret->user2 = end; 314 ret->index2 = endindex; 315 xmlXPtrRangeCheckOrder(ret); 316 return(ret); 317} 318 319/** 320 * xmlXPtrNewRangePoints: 321 * @start: the starting point 322 * @end: the ending point 323 * 324 * Create a new xmlXPathObjectPtr of type range using 2 Points 325 * 326 * Returns the newly created object. 327 */ 328xmlXPathObjectPtr 329xmlXPtrNewRangePoints(xmlXPathObjectPtr start, xmlXPathObjectPtr end) { 330 xmlXPathObjectPtr ret; 331 332 if (start == NULL) 333 return(NULL); 334 if (end == NULL) 335 return(NULL); 336 if (start->type != XPATH_POINT) 337 return(NULL); 338 if (end->type != XPATH_POINT) 339 return(NULL); 340 341 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 342 if (ret == NULL) { 343 xmlGenericError(xmlGenericErrorContext, 344 "xmlXPtrNewRangePoints: out of memory\n"); 345 return(NULL); 346 } 347 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 348 ret->type = XPATH_RANGE; 349 ret->user = start->user; 350 ret->index = start->index; 351 ret->user2 = end->user; 352 ret->index2 = end->index; 353 xmlXPtrRangeCheckOrder(ret); 354 return(ret); 355} 356 357/** 358 * xmlXPtrNewRangePointNode: 359 * @start: the starting point 360 * @end: the ending node 361 * 362 * Create a new xmlXPathObjectPtr of type range from a point to a node 363 * 364 * Returns the newly created object. 365 */ 366xmlXPathObjectPtr 367xmlXPtrNewRangePointNode(xmlXPathObjectPtr start, xmlNodePtr end) { 368 xmlXPathObjectPtr ret; 369 370 if (start == NULL) 371 return(NULL); 372 if (end == NULL) 373 return(NULL); 374 if (start->type != XPATH_POINT) 375 return(NULL); 376 377 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 378 if (ret == NULL) { 379 xmlGenericError(xmlGenericErrorContext, 380 "xmlXPtrNewRangePointNode: out of memory\n"); 381 return(NULL); 382 } 383 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 384 ret->type = XPATH_RANGE; 385 ret->user = start->user; 386 ret->index = start->index; 387 ret->user2 = end; 388 ret->index2 = -1; 389 xmlXPtrRangeCheckOrder(ret); 390 return(ret); 391} 392 393/** 394 * xmlXPtrNewRangeNodePoint: 395 * @start: the starting node 396 * @end: the ending point 397 * 398 * Create a new xmlXPathObjectPtr of type range from a node to a point 399 * 400 * Returns the newly created object. 401 */ 402xmlXPathObjectPtr 403xmlXPtrNewRangeNodePoint(xmlNodePtr start, xmlXPathObjectPtr end) { 404 xmlXPathObjectPtr ret; 405 406 if (start == NULL) 407 return(NULL); 408 if (end == NULL) 409 return(NULL); 410 if (start->type != XPATH_POINT) 411 return(NULL); 412 if (end->type != XPATH_POINT) 413 return(NULL); 414 415 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 416 if (ret == NULL) { 417 xmlGenericError(xmlGenericErrorContext, 418 "xmlXPtrNewRangeNodePoint: out of memory\n"); 419 return(NULL); 420 } 421 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 422 ret->type = XPATH_RANGE; 423 ret->user = start; 424 ret->index = -1; 425 ret->user2 = end->user; 426 ret->index2 = end->index; 427 xmlXPtrRangeCheckOrder(ret); 428 return(ret); 429} 430 431/** 432 * xmlXPtrNewRangeNodes: 433 * @start: the starting node 434 * @end: the ending node 435 * 436 * Create a new xmlXPathObjectPtr of type range using 2 nodes 437 * 438 * Returns the newly created object. 439 */ 440xmlXPathObjectPtr 441xmlXPtrNewRangeNodes(xmlNodePtr start, xmlNodePtr end) { 442 xmlXPathObjectPtr ret; 443 444 if (start == NULL) 445 return(NULL); 446 if (end == NULL) 447 return(NULL); 448 449 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 450 if (ret == NULL) { 451 xmlGenericError(xmlGenericErrorContext, 452 "xmlXPtrNewRangeNodes: out of memory\n"); 453 return(NULL); 454 } 455 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 456 ret->type = XPATH_RANGE; 457 ret->user = start; 458 ret->index = -1; 459 ret->user2 = end; 460 ret->index2 = -1; 461 xmlXPtrRangeCheckOrder(ret); 462 return(ret); 463} 464 465/** 466 * xmlXPtrNewCollapsedRange: 467 * @start: the starting and ending node 468 * 469 * Create a new xmlXPathObjectPtr of type range using a single nodes 470 * 471 * Returns the newly created object. 472 */ 473xmlXPathObjectPtr 474xmlXPtrNewCollapsedRange(xmlNodePtr start) { 475 xmlXPathObjectPtr ret; 476 477 if (start == NULL) 478 return(NULL); 479 480 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 481 if (ret == NULL) { 482 xmlGenericError(xmlGenericErrorContext, 483 "xmlXPtrNewRangeNodes: out of memory\n"); 484 return(NULL); 485 } 486 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 487 ret->type = XPATH_RANGE; 488 ret->user = start; 489 ret->index = -1; 490 ret->user2 = NULL; 491 ret->index2 = -1; 492 return(ret); 493} 494 495/** 496 * xmlXPtrNewRangeNodeObject: 497 * @start: the starting node 498 * @end: the ending object 499 * 500 * Create a new xmlXPathObjectPtr of type range from a not to an object 501 * 502 * Returns the newly created object. 503 */ 504xmlXPathObjectPtr 505xmlXPtrNewRangeNodeObject(xmlNodePtr start, xmlXPathObjectPtr end) { 506 xmlXPathObjectPtr ret; 507 508 if (start == NULL) 509 return(NULL); 510 if (end == NULL) 511 return(NULL); 512 switch (end->type) { 513 case XPATH_POINT: 514 break; 515 case XPATH_NODESET: 516 /* 517 * Empty set ... 518 */ 519 if (end->nodesetval->nodeNr <= 0) 520 return(NULL); 521 break; 522 default: 523 TODO 524 return(NULL); 525 } 526 527 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 528 if (ret == NULL) { 529 xmlGenericError(xmlGenericErrorContext, 530 "xmlXPtrNewRangeNodeObject: out of memory\n"); 531 return(NULL); 532 } 533 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 534 ret->type = XPATH_RANGE; 535 ret->user = start; 536 ret->index = -1; 537 switch (end->type) { 538 case XPATH_POINT: 539 ret->user2 = end->user; 540 ret->index2 = end->index; 541 case XPATH_NODESET: { 542 ret->user2 = end->nodesetval->nodeTab[end->nodesetval->nodeNr - 1]; 543 ret->index2 = -1; 544 break; 545 } 546 default: 547 STRANGE 548 return(NULL); 549 } 550 xmlXPtrRangeCheckOrder(ret); 551 return(ret); 552} 553 554#define XML_RANGESET_DEFAULT 10 555 556/** 557 * xmlXPtrLocationSetCreate: 558 * @val: an initial xmlXPathObjectPtr, or NULL 559 * 560 * Create a new xmlLocationSetPtr of type double and of value @val 561 * 562 * Returns the newly created object. 563 */ 564xmlLocationSetPtr 565xmlXPtrLocationSetCreate(xmlXPathObjectPtr val) { 566 xmlLocationSetPtr ret; 567 568 ret = (xmlLocationSetPtr) xmlMalloc(sizeof(xmlLocationSet)); 569 if (ret == NULL) { 570 xmlGenericError(xmlGenericErrorContext, 571 "xmlXPtrLocationSetCreate: out of memory\n"); 572 return(NULL); 573 } 574 memset(ret, 0 , (size_t) sizeof(xmlLocationSet)); 575 if (val != NULL) { 576 ret->locTab = (xmlXPathObjectPtr *) xmlMalloc(XML_RANGESET_DEFAULT * 577 sizeof(xmlXPathObjectPtr)); 578 if (ret->locTab == NULL) { 579 xmlGenericError(xmlGenericErrorContext, 580 "xmlXPtrLocationSetCreate: out of memory\n"); 581 return(NULL); 582 } 583 memset(ret->locTab, 0 , 584 XML_RANGESET_DEFAULT * (size_t) sizeof(xmlXPathObjectPtr)); 585 ret->locMax = XML_RANGESET_DEFAULT; 586 ret->locTab[ret->locNr++] = val; 587 } 588 return(ret); 589} 590 591/** 592 * xmlXPtrLocationSetAdd: 593 * @cur: the initial range set 594 * @val: a new xmlXPathObjectPtr 595 * 596 * add a new xmlXPathObjectPtr ot an existing LocationSet 597 */ 598void 599xmlXPtrLocationSetAdd(xmlLocationSetPtr cur, xmlXPathObjectPtr val) { 600 int i; 601 602 if (val == NULL) return; 603 604 /* 605 * check against doublons 606 */ 607 for (i = 0;i < cur->locNr;i++) 608 if (cur->locTab[i] == val) return; 609 610 /* 611 * grow the locTab if needed 612 */ 613 if (cur->locMax == 0) { 614 cur->locTab = (xmlXPathObjectPtr *) xmlMalloc(XML_RANGESET_DEFAULT * 615 sizeof(xmlXPathObjectPtr)); 616 if (cur->locTab == NULL) { 617 xmlGenericError(xmlGenericErrorContext, 618 "xmlXPtrLocationSetAdd: out of memory\n"); 619 return; 620 } 621 memset(cur->locTab, 0 , 622 XML_RANGESET_DEFAULT * (size_t) sizeof(xmlXPathObjectPtr)); 623 cur->locMax = XML_RANGESET_DEFAULT; 624 } else if (cur->locNr == cur->locMax) { 625 xmlXPathObjectPtr *temp; 626 627 cur->locMax *= 2; 628 temp = (xmlXPathObjectPtr *) xmlRealloc(cur->locTab, cur->locMax * 629 sizeof(xmlXPathObjectPtr)); 630 if (temp == NULL) { 631 xmlGenericError(xmlGenericErrorContext, 632 "xmlXPtrLocationSetAdd: out of memory\n"); 633 return; 634 } 635 cur->locTab = temp; 636 } 637 cur->locTab[cur->locNr++] = val; 638} 639 640/** 641 * xmlXPtrLocationSetMerge: 642 * @val1: the first LocationSet 643 * @val2: the second LocationSet 644 * 645 * Merges two rangesets, all ranges from @val2 are added to @val1 646 * 647 * Returns val1 once extended or NULL in case of error. 648 */ 649xmlLocationSetPtr 650xmlXPtrLocationSetMerge(xmlLocationSetPtr val1, xmlLocationSetPtr val2) { 651 int i; 652 653 if (val1 == NULL) return(NULL); 654 if (val2 == NULL) return(val1); 655 656 /* 657 * !!!!! this can be optimized a lot, knowing that both 658 * val1 and val2 already have unicity of their values. 659 */ 660 661 for (i = 0;i < val2->locNr;i++) 662 xmlXPtrLocationSetAdd(val1, val2->locTab[i]); 663 664 return(val1); 665} 666 667/** 668 * xmlXPtrLocationSetDel: 669 * @cur: the initial range set 670 * @val: an xmlXPathObjectPtr 671 * 672 * Removes an xmlXPathObjectPtr from an existing LocationSet 673 */ 674void 675xmlXPtrLocationSetDel(xmlLocationSetPtr cur, xmlXPathObjectPtr val) { 676 int i; 677 678 if (cur == NULL) return; 679 if (val == NULL) return; 680 681 /* 682 * check against doublons 683 */ 684 for (i = 0;i < cur->locNr;i++) 685 if (cur->locTab[i] == val) break; 686 687 if (i >= cur->locNr) { 688#ifdef DEBUG 689 xmlGenericError(xmlGenericErrorContext, 690 "xmlXPtrLocationSetDel: Range %s wasn't found in RangeList\n", 691 val->name); 692#endif 693 return; 694 } 695 cur->locNr--; 696 for (;i < cur->locNr;i++) 697 cur->locTab[i] = cur->locTab[i + 1]; 698 cur->locTab[cur->locNr] = NULL; 699} 700 701/** 702 * xmlXPtrLocationSetRemove: 703 * @cur: the initial range set 704 * @val: the index to remove 705 * 706 * Removes an entry from an existing LocationSet list. 707 */ 708void 709xmlXPtrLocationSetRemove(xmlLocationSetPtr cur, int val) { 710 if (cur == NULL) return; 711 if (val >= cur->locNr) return; 712 cur->locNr--; 713 for (;val < cur->locNr;val++) 714 cur->locTab[val] = cur->locTab[val + 1]; 715 cur->locTab[cur->locNr] = NULL; 716} 717 718/** 719 * xmlXPtrFreeLocationSet: 720 * @obj: the xmlLocationSetPtr to free 721 * 722 * Free the LocationSet compound (not the actual ranges !). 723 */ 724void 725xmlXPtrFreeLocationSet(xmlLocationSetPtr obj) { 726 int i; 727 728 if (obj == NULL) return; 729 if (obj->locTab != NULL) { 730 for (i = 0;i < obj->locNr; i++) { 731 xmlXPathFreeObject(obj->locTab[i]); 732 } 733#ifdef DEBUG 734 memset(obj->locTab, 0xB , 735 (size_t) sizeof(xmlXPathObjectPtr) * obj->locMax); 736#endif 737 xmlFree(obj->locTab); 738 } 739#ifdef DEBUG 740 memset(obj, 0xB , (size_t) sizeof(xmlLocationSet)); 741#endif 742 xmlFree(obj); 743} 744 745/** 746 * xmlXPtrNewLocationSetNodes: 747 * @start: the start NodePtr value 748 * @end: the end NodePtr value or NULL 749 * 750 * Create a new xmlXPathObjectPtr of type LocationSet and initialize 751 * it with the single range made of the two nodes @start and @end 752 * 753 * Returns the newly created object. 754 */ 755xmlXPathObjectPtr 756xmlXPtrNewLocationSetNodes(xmlNodePtr start, xmlNodePtr end) { 757 xmlXPathObjectPtr ret; 758 759 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 760 if (ret == NULL) { 761 xmlGenericError(xmlGenericErrorContext, 762 "xmlXPtrNewLocationSetNodes: out of memory\n"); 763 return(NULL); 764 } 765 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 766 ret->type = XPATH_LOCATIONSET; 767 if (end == NULL) 768 ret->user = xmlXPtrLocationSetCreate(xmlXPtrNewCollapsedRange(start)); 769 else 770 ret->user = xmlXPtrLocationSetCreate(xmlXPtrNewRangeNodes(start,end)); 771 return(ret); 772} 773 774/** 775 * xmlXPtrNewLocationSetNodeSet: 776 * @set: a node set 777 * 778 * Create a new xmlXPathObjectPtr of type LocationSet and initialize 779 * it with all the nodes from @set 780 * 781 * Returns the newly created object. 782 */ 783xmlXPathObjectPtr 784xmlXPtrNewLocationSetNodeSet(xmlNodeSetPtr set) { 785 xmlXPathObjectPtr ret; 786 787 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 788 if (ret == NULL) { 789 xmlGenericError(xmlGenericErrorContext, 790 "xmlXPtrNewLocationSetNodes: out of memory\n"); 791 return(NULL); 792 } 793 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 794 ret->type = XPATH_LOCATIONSET; 795 if (set != NULL) { 796 int i; 797 xmlLocationSetPtr newset; 798 799 newset = xmlXPtrLocationSetCreate(NULL); 800 if (newset == NULL) 801 return(ret); 802 803 for (i = 0;i < set->nodeNr;i++) 804 xmlXPtrLocationSetAdd(newset, 805 xmlXPtrNewCollapsedRange(set->nodeTab[i])); 806 807 ret->user = (void *) newset; 808 } 809 return(ret); 810} 811 812/** 813 * xmlXPtrWrapLocationSet: 814 * @val: the LocationSet value 815 * 816 * Wrap the LocationSet @val in a new xmlXPathObjectPtr 817 * 818 * Returns the newly created object. 819 */ 820xmlXPathObjectPtr 821xmlXPtrWrapLocationSet(xmlLocationSetPtr val) { 822 xmlXPathObjectPtr ret; 823 824 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 825 if (ret == NULL) { 826 xmlGenericError(xmlGenericErrorContext, 827 "xmlXPtrWrapLocationSet: out of memory\n"); 828 return(NULL); 829 } 830 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 831 ret->type = XPATH_LOCATIONSET; 832 ret->user = (void *) val; 833 return(ret); 834} 835 836/************************************************************************ 837 * * 838 * The parser * 839 * * 840 ************************************************************************/ 841 842/* 843 * Macros for accessing the content. Those should be used only by the parser, 844 * and not exported. 845 * 846 * Dirty macros, i.e. one need to make assumption on the context to use them 847 * 848 * CUR_PTR return the current pointer to the xmlChar to be parsed. 849 * CUR returns the current xmlChar value, i.e. a 8 bit value 850 * in ISO-Latin or UTF-8. 851 * This should be used internally by the parser 852 * only to compare to ASCII values otherwise it would break when 853 * running with UTF-8 encoding. 854 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only 855 * to compare on ASCII based substring. 856 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined 857 * strings within the parser. 858 * CURRENT Returns the current char value, with the full decoding of 859 * UTF-8 if we are using this mode. It returns an int. 860 * NEXT Skip to the next character, this does the proper decoding 861 * in UTF-8 mode. It also pop-up unfinished entities on the fly. 862 * It returns the pointer to the current xmlChar. 863 */ 864 865#define CUR (*ctxt->cur) 866#define SKIP(val) ctxt->cur += (val) 867#define NXT(val) ctxt->cur[(val)] 868#define CUR_PTR ctxt->cur 869 870#define SKIP_BLANKS \ 871 while (IS_BLANK(*(ctxt->cur))) NEXT 872 873#define CURRENT (*ctxt->cur) 874#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) 875 876/* 877 * xmlXPtrGetChildNo: 878 * @ctxt: the XPointer Parser context 879 * @index: the child number 880 * 881 * Move the current node of the nodeset on the stack to the 882 * given child if found 883 */ 884void 885xmlXPtrGetChildNo(xmlXPathParserContextPtr ctxt, int index) { 886 xmlNodePtr cur = NULL; 887 xmlXPathObjectPtr obj; 888 xmlNodeSetPtr oldset; 889 890 CHECK_TYPE(XPATH_NODESET); 891 obj = valuePop(ctxt); 892 oldset = obj->nodesetval; 893 if ((index <= 0) || (oldset == NULL) || (oldset->nodeNr != 1)) { 894 xmlXPathFreeObject(obj); 895 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 896 return; 897 } 898 cur = xmlXPtrGetNthChild(oldset->nodeTab[0], index); 899 if (cur == NULL) { 900 xmlXPathFreeObject(obj); 901 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); 902 return; 903 } 904 oldset->nodeTab[0] = cur; 905 valuePush(ctxt, obj); 906} 907 908/** 909 * xmlXPtrEvalXPtrPart: 910 * @ctxt: the XPointer Parser context 911 * @name: the preparsed Scheme for the XPtrPart 912 * 913 * XPtrPart ::= 'xpointer' '(' XPtrExpr ')' 914 * | Scheme '(' SchemeSpecificExpr ')' 915 * 916 * Scheme ::= NCName - 'xpointer' [VC: Non-XPointer schemes] 917 * 918 * SchemeSpecificExpr ::= StringWithBalancedParens 919 * 920 * StringWithBalancedParens ::= 921 * [^()]* ('(' StringWithBalancedParens ')' [^()]*)* 922 * [VC: Parenthesis escaping] 923 * 924 * XPtrExpr ::= Expr [VC: Parenthesis escaping] 925 * 926 * VC: Parenthesis escaping: 927 * The end of an XPointer part is signaled by the right parenthesis ")" 928 * character that is balanced with the left parenthesis "(" character 929 * that began the part. Any unbalanced parenthesis character inside the 930 * expression, even within literals, must be escaped with a circumflex (^) 931 * character preceding it. If the expression contains any literal 932 * occurrences of the circumflex, each must be escaped with an additional 933 * circumflex (that is, ^^). If the unescaped parentheses in the expression 934 * are not balanced, a syntax error results. 935 * 936 * Parse and evaluate an XPtrPart. Basically it generates the unescaped 937 * string and if the scheme is 'xpointer' it will call the XPath interprter. 938 * 939 * TODO: there is no new scheme registration mechanism 940 */ 941 942void 943xmlXPtrEvalXPtrPart(xmlXPathParserContextPtr ctxt, xmlChar *name) { 944 xmlChar *buffer, *cur; 945 int len; 946 int level; 947 948 if (name == NULL) 949 name = xmlXPathParseName(ctxt); 950 if (name == NULL) 951 XP_ERROR(XPATH_EXPR_ERROR); 952 953 if (CUR != '(') 954 XP_ERROR(XPATH_EXPR_ERROR); 955 NEXT; 956 level = 1; 957 958 len = xmlStrlen(ctxt->cur); 959 len++; 960 buffer = (xmlChar *) xmlMalloc(len * sizeof (xmlChar)); 961 if (buffer == NULL) { 962 xmlGenericError(xmlGenericErrorContext, 963 "xmlXPtrEvalXPtrPart: out of memory\n"); 964 return; 965 } 966 967 cur = buffer; 968 while (CUR != 0) { 969 if (CUR == ')') { 970 level--; 971 if (level == 0) { 972 NEXT; 973 break; 974 } 975 *cur++ = CUR; 976 } else if (CUR == '(') { 977 level++; 978 *cur++ = CUR; 979 } else if (CUR == '^') { 980 NEXT; 981 if ((CUR == ')') || (CUR == '(') || (CUR == '^')) { 982 *cur++ = CUR; 983 } else { 984 *cur++ = '^'; 985 *cur++ = CUR; 986 } 987 } else { 988 *cur++ = CUR; 989 } 990 NEXT; 991 } 992 *cur = 0; 993 994 if ((level != 0) && (CUR == 0)) { 995 xmlFree(buffer); 996 XP_ERROR(XPTR_SYNTAX_ERROR); 997 } 998 999 if (xmlStrEqual(name, (xmlChar *) "xpointer")) { 1000 const xmlChar *left = CUR_PTR; 1001 1002 CUR_PTR = buffer; 1003 xmlXPathRoot(ctxt); 1004 xmlXPathEvalExpr(ctxt); 1005 CUR_PTR=left; 1006 } else { 1007 xmlGenericError(xmlGenericErrorContext, 1008 "unsupported scheme '%s'\n", name); 1009 } 1010 xmlFree(buffer); 1011 xmlFree(name); 1012} 1013 1014/** 1015 * xmlXPtrEvalFullXPtr: 1016 * @ctxt: the XPointer Parser context 1017 * @name: the preparsed Scheme for the first XPtrPart 1018 * 1019 * FullXPtr ::= XPtrPart (S? XPtrPart)* 1020 * 1021 * As the specs says: 1022 * ----------- 1023 * When multiple XPtrParts are provided, they must be evaluated in 1024 * left-to-right order. If evaluation of one part fails, the nexti 1025 * is evaluated. The following conditions cause XPointer part failure: 1026 * 1027 * - An unknown scheme 1028 * - A scheme that does not locate any sub-resource present in the resource 1029 * - A scheme that is not applicable to the media type of the resource 1030 * 1031 * The XPointer application must consume a failed XPointer part and 1032 * attempt to evaluate the next one, if any. The result of the first 1033 * XPointer part whose evaluation succeeds is taken to be the fragment 1034 * located by the XPointer as a whole. If all the parts fail, the result 1035 * for the XPointer as a whole is a sub-resource error. 1036 * ----------- 1037 * 1038 * Parse and evaluate a Full XPtr i.e. possibly a cascade of XPath based 1039 * expressions or other shemes. 1040 */ 1041void 1042xmlXPtrEvalFullXPtr(xmlXPathParserContextPtr ctxt, xmlChar *name) { 1043 if (name == NULL) 1044 name = xmlXPathParseName(ctxt); 1045 if (name == NULL) 1046 XP_ERROR(XPATH_EXPR_ERROR); 1047 while (name != NULL) { 1048 xmlXPtrEvalXPtrPart(ctxt, name); 1049 1050 /* in case of syntax error, break here */ 1051 if (ctxt->error != XPATH_EXPRESSION_OK) 1052 return; 1053 1054 /* 1055 * If the returned value is a non-empty nodeset 1056 * or location set, return here. 1057 */ 1058 if (ctxt->value != NULL) { 1059 xmlXPathObjectPtr obj = ctxt->value; 1060 1061 switch (obj->type) { 1062 case XPATH_LOCATIONSET: { 1063 xmlLocationSetPtr loc = ctxt->value->user; 1064 if ((loc != NULL) && (loc->locNr > 0)) 1065 return; 1066 break; 1067 } 1068 case XPATH_NODESET: { 1069 xmlNodeSetPtr loc = ctxt->value->nodesetval; 1070 if ((loc != NULL) && (loc->nodeNr > 0)) 1071 return; 1072 break; 1073 } 1074 default: 1075 break; 1076 } 1077 1078 /* 1079 * Evaluating to improper values is equivalent to 1080 * a sub-resource error, clean-up the stack 1081 */ 1082 do { 1083 obj = valuePop(ctxt); 1084 if (obj != NULL) { 1085 xmlXPathFreeObject(obj); 1086 } 1087 } while (obj != NULL); 1088 } 1089 1090 /* 1091 * Is there another XPoointer part. 1092 */ 1093 SKIP_BLANKS; 1094 name = xmlXPathParseName(ctxt); 1095 } 1096} 1097 1098/** 1099 * xmlXPtrEvalChildSeq: 1100 * @ctxt: the XPointer Parser context 1101 * @name: a possible ID name of the child sequence 1102 * 1103 * ChildSeq ::= '/1' ('/' [0-9]*)* 1104 * | Name ('/' [0-9]*)+ 1105 * 1106 * Parse and evaluate a Child Sequence. This routine also handle the 1107 * case of a Bare Name used to get a document ID. 1108 */ 1109void 1110xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt, xmlChar *name) { 1111 /* 1112 * XPointer don't allow by syntax to adress in mutirooted trees 1113 * this might prove useful in some cases, warn about it. 1114 */ 1115 if ((name == NULL) && (CUR == '/') && (NXT(1) != '1')) { 1116 xmlGenericError(xmlGenericErrorContext, 1117 "warning: ChildSeq not starting by /1\n"); 1118 } 1119 1120 if (name != NULL) { 1121 valuePush(ctxt, xmlXPathNewString(name)); 1122 xmlFree(name); 1123 xmlXPathIdFunction(ctxt, 1); 1124 CHECK_ERROR; 1125 } 1126 1127 while (CUR == '/') { 1128 int child = 0; 1129 NEXT; 1130 1131 while ((CUR >= '0') && (CUR <= '9')) { 1132 child = child * 10 + (CUR - '0'); 1133 NEXT; 1134 } 1135 xmlXPtrGetChildNo(ctxt, child); 1136 } 1137} 1138 1139 1140/** 1141 * xmlXPtrEvalXPointer: 1142 * @ctxt: the XPointer Parser context 1143 * 1144 * XPointer ::= Name 1145 * | ChildSeq 1146 * | FullXPtr 1147 * 1148 * Parse and evaluate an XPointer 1149 */ 1150void 1151xmlXPtrEvalXPointer(xmlXPathParserContextPtr ctxt) { 1152 SKIP_BLANKS; 1153 if (CUR == '/') { 1154 xmlXPathRoot(ctxt); 1155 xmlXPtrEvalChildSeq(ctxt, NULL); 1156 } else { 1157 xmlChar *name; 1158 1159 name = xmlXPathParseName(ctxt); 1160 if (name == NULL) 1161 XP_ERROR(XPATH_EXPR_ERROR); 1162 if (CUR == '(') { 1163 xmlXPtrEvalFullXPtr(ctxt, name); 1164 /* Short evaluation */ 1165 return; 1166 } else { 1167 /* this handle both Bare Names and Child Sequences */ 1168 xmlXPtrEvalChildSeq(ctxt, name); 1169 } 1170 } 1171 SKIP_BLANKS; 1172 if (CUR != 0) 1173 XP_ERROR(XPATH_EXPR_ERROR); 1174} 1175 1176 1177/************************************************************************ 1178 * * 1179 * General routines * 1180 * * 1181 ************************************************************************/ 1182 1183void xmlXPtrRangeToFunction(xmlXPathParserContextPtr ctxt, int nargs); 1184void xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs); 1185void xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs); 1186void xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs); 1187void xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs); 1188void xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs); 1189void xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs); 1190void xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt, int nargs); 1191 1192/** 1193 * xmlXPtrNewContext: 1194 * @doc: the XML document 1195 * @here: the node that directly contains the XPointer being evaluated or NULL 1196 * @origin: the element from which a user or program initiated traversal of 1197 * the link, or NULL. 1198 * 1199 * Create a new XPointer context 1200 * 1201 * Returns the xmlXPathContext just allocated. 1202 */ 1203xmlXPathContextPtr 1204xmlXPtrNewContext(xmlDocPtr doc, xmlNodePtr here, xmlNodePtr origin) { 1205 xmlXPathContextPtr ret; 1206 1207 ret = xmlXPathNewContext(doc); 1208 if (ret == NULL) 1209 return(ret); 1210 ret->xptr = 1; 1211 ret->here = here; 1212 ret->origin = origin; 1213 1214 xmlXPathRegisterFunc(ret, (xmlChar *)"range-to", 1215 xmlXPtrRangeToFunction); 1216 xmlXPathRegisterFunc(ret, (xmlChar *)"range", 1217 xmlXPtrRangeFunction); 1218 xmlXPathRegisterFunc(ret, (xmlChar *)"range-inside", 1219 xmlXPtrRangeInsideFunction); 1220 xmlXPathRegisterFunc(ret, (xmlChar *)"string-range", 1221 xmlXPtrStringRangeFunction); 1222 xmlXPathRegisterFunc(ret, (xmlChar *)"start-point", 1223 xmlXPtrStartPointFunction); 1224 xmlXPathRegisterFunc(ret, (xmlChar *)"end-point", 1225 xmlXPtrEndPointFunction); 1226 xmlXPathRegisterFunc(ret, (xmlChar *)"here", 1227 xmlXPtrHereFunction); 1228 xmlXPathRegisterFunc(ret, (xmlChar *)" origin", 1229 xmlXPtrOriginFunction); 1230 1231 return(ret); 1232} 1233 1234/** 1235 * xmlXPtrEval: 1236 * @str: the XPointer expression 1237 * @ctx: the XPointer context 1238 * 1239 * Evaluate the XPath Location Path in the given context. 1240 * 1241 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL. 1242 * the caller has to free the object. 1243 */ 1244xmlXPathObjectPtr 1245xmlXPtrEval(const xmlChar *str, xmlXPathContextPtr ctx) { 1246 xmlXPathParserContextPtr ctxt; 1247 xmlXPathObjectPtr res = NULL, tmp; 1248 xmlXPathObjectPtr init = NULL; 1249 int stack = 0; 1250 1251 xmlXPathInit(); 1252 1253 if ((ctx == NULL) || (str == NULL)) 1254 return(NULL); 1255 1256 ctxt = xmlXPathNewParserContext(str, ctx); 1257 if (ctx->node != NULL) { 1258 init = xmlXPathNewNodeSet(ctx->node); 1259 valuePush(ctxt, init); 1260 } 1261 xmlXPtrEvalXPointer(ctxt); 1262 1263 if ((ctxt->value != NULL) && 1264 (ctxt->value->type != XPATH_NODESET) && 1265 (ctxt->value->type != XPATH_LOCATIONSET)) { 1266 xmlGenericError(xmlGenericErrorContext, 1267 "xmlXPtrEval: evaluation failed to return a node set\n"); 1268 } else { 1269 res = valuePop(ctxt); 1270 } 1271 1272 do { 1273 tmp = valuePop(ctxt); 1274 if (tmp != NULL) { 1275 if (tmp != init) { 1276 if (tmp->type == XPATH_NODESET) { 1277 /* 1278 * Evaluation may push a root nodeset which is unused 1279 */ 1280 xmlNodeSetPtr set; 1281 set = tmp->nodesetval; 1282 if ((set->nodeNr != 1) || 1283 (set->nodeTab[0] != (xmlNodePtr) ctx->doc)) 1284 stack++; 1285 } else 1286 stack++; 1287 } 1288 xmlXPathFreeObject(tmp); 1289 } 1290 } while (tmp != NULL); 1291 if (stack != 0) { 1292 xmlGenericError(xmlGenericErrorContext, 1293 "xmlXPtrEval: %d object left on the stack\n", 1294 stack); 1295 } 1296 if (ctxt->error != XPATH_EXPRESSION_OK) { 1297 xmlXPathFreeObject(res); 1298 res = NULL; 1299 } 1300 1301 xmlXPathFreeParserContext(ctxt); 1302 return(res); 1303} 1304 1305 1306/************************************************************************ 1307 * * 1308 * XPointer functions * 1309 * * 1310 ************************************************************************/ 1311 1312/** 1313 * xmlXPtrNbLocChildren: 1314 * @node: an xmlNodePtr 1315 * 1316 * Count the number of location children of @node or the lenght of the 1317 * string value in case of text/PI/Comments nodes 1318 * 1319 * Returns the number of location children 1320 */ 1321int 1322xmlXPtrNbLocChildren(xmlNodePtr node) { 1323 int ret = 0; 1324 if (node == NULL) 1325 return(-1); 1326 switch (node->type) { 1327 case XML_HTML_DOCUMENT_NODE: 1328 case XML_DOCUMENT_NODE: 1329 case XML_ELEMENT_NODE: 1330 node = node->children; 1331 while (node != NULL) { 1332 if (node->type == XML_ELEMENT_NODE) 1333 ret++; 1334 node = node->next; 1335 } 1336 break; 1337 case XML_ATTRIBUTE_NODE: 1338 return(-1); 1339 1340 case XML_PI_NODE: 1341 case XML_COMMENT_NODE: 1342 case XML_TEXT_NODE: 1343 case XML_CDATA_SECTION_NODE: 1344 case XML_ENTITY_REF_NODE: 1345#ifndef XML_USE_BUFFER_CONTENT 1346 ret = xmlStrlen(node->content); 1347#else 1348 ret = xmlBufferLength(node->content); 1349#endif 1350 break; 1351 default: 1352 return(-1); 1353 } 1354 return(ret); 1355} 1356 1357/** 1358 * xmlXPtrHereFunction: 1359 * @ctxt: the XPointer Parser context 1360 * 1361 * Function implementing here() operation 1362 * as described in 5.4.3 1363 */ 1364void 1365xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs) { 1366 if (ctxt->context->here == NULL) 1367 XP_ERROR(XPTR_SYNTAX_ERROR); 1368 1369 valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->here, NULL)); 1370} 1371 1372/** 1373 * xmlXPtrOriginFunction: 1374 * @ctxt: the XPointer Parser context 1375 * 1376 * Function implementing origin() operation 1377 * as described in 5.4.3 1378 */ 1379void 1380xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs) { 1381 if (ctxt->context->origin == NULL) 1382 XP_ERROR(XPTR_SYNTAX_ERROR); 1383 1384 valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->origin, NULL)); 1385} 1386 1387/** 1388 * xmlXPtrStartPointFunction: 1389 * @ctxt: the XPointer Parser context 1390 * 1391 * Function implementing start-point() operation 1392 * as described in 5.4.3 1393 * ---------------- 1394 * location-set start-point(location-set) 1395 * 1396 * For each location x in the argument location-set, start-point adds a 1397 * location of type point to the result location-set. That point represents 1398 * the start point of location x and is determined by the following rules: 1399 * 1400 * - If x is of type point, the start point is x. 1401 * - If x is of type range, the start point is the start point of x. 1402 * - If x is of type root, element, text, comment, or processing instruction, 1403 * - the container node of the start point is x and the index is 0. 1404 * - If x is of type attribute or namespace, the function must signal a 1405 * syntax error. 1406 * ---------------- 1407 * 1408 */ 1409void 1410xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs) { 1411 xmlXPathObjectPtr tmp, obj, point; 1412 xmlLocationSetPtr newset = NULL; 1413 xmlLocationSetPtr oldset = NULL; 1414 1415 CHECK_ARITY(1); 1416 if ((ctxt->value == NULL) || 1417 ((ctxt->value->type != XPATH_LOCATIONSET) && 1418 (ctxt->value->type != XPATH_NODESET))) 1419 XP_ERROR(XPATH_INVALID_TYPE) 1420 1421 obj = valuePop(ctxt); 1422 if (obj->type == XPATH_NODESET) { 1423 /* 1424 * First convert to a location set 1425 */ 1426 tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval); 1427 xmlXPathFreeObject(obj); 1428 obj = tmp; 1429 } 1430 1431 newset = xmlXPtrLocationSetCreate(NULL); 1432 oldset = (xmlLocationSetPtr) obj->user; 1433 if (oldset != NULL) { 1434 int i; 1435 1436 for (i = 0; i < oldset->locNr; i++) { 1437 tmp = oldset->locTab[i]; 1438 if (tmp == NULL) 1439 continue; 1440 point = NULL; 1441 switch (tmp->type) { 1442 case XPATH_POINT: 1443 point = xmlXPtrNewPoint(tmp->user, tmp->index); 1444 break; 1445 case XPATH_RANGE: { 1446 xmlNodePtr node = tmp->user; 1447 if (node != NULL) { 1448 if (node->type == XML_ATTRIBUTE_NODE) { 1449 /* TODO: Namespace Nodes ??? */ 1450 xmlXPathFreeObject(obj); 1451 xmlXPtrFreeLocationSet(newset); 1452 XP_ERROR(XPTR_SYNTAX_ERROR); 1453 } 1454 point = xmlXPtrNewPoint(node, tmp->index); 1455 } 1456 if (tmp->user2 == NULL) { 1457 point = xmlXPtrNewPoint(node, 0); 1458 } else 1459 point = xmlXPtrNewPoint(node, tmp->index); 1460 break; 1461 } 1462 default: 1463 /*** Should we raise an error ? 1464 xmlXPathFreeObject(obj); 1465 xmlXPathFreeObject(newset); 1466 XP_ERROR(XPATH_INVALID_TYPE) 1467 ***/ 1468 break; 1469 } 1470 if (point != NULL) 1471 xmlXPtrLocationSetAdd(newset, point); 1472 } 1473 } 1474 xmlXPathFreeObject(obj); 1475} 1476 1477/** 1478 * xmlXPtrEndPointFunction: 1479 * @ctxt: the XPointer Parser context 1480 * 1481 * Function implementing end-point() operation 1482 * as described in 5.4.3 1483 * ---------------------------- 1484 * location-set end-point(location-set) 1485 * 1486 * For each location x in the argument location-set, end-point adds a 1487 * location of type point to the result location-set. That point representsi 1488 * the end point of location x and is determined by the following rules: 1489 * 1490 * - If x is of type point, the resulting point is x. 1491 * - If x is of type range, the resulting point is the end point of x. 1492 * - If x is of type root or element, the container node of the resulting 1493 * point is x and the index is the number of location children of x. 1494 * - If x is of type text, comment, or processing instruction, the container 1495 * node of the resulting point is x and the index is the length of thei 1496 * string-value of x. 1497 * - If x is of type attribute or namespace, the function must signal a 1498 * syntax error. 1499 * ---------------------------- 1500 */ 1501void 1502xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs) { 1503 xmlXPathObjectPtr tmp, obj, point; 1504 xmlLocationSetPtr newset = NULL; 1505 xmlLocationSetPtr oldset = NULL; 1506 1507 CHECK_ARITY(1); 1508 if ((ctxt->value == NULL) || 1509 ((ctxt->value->type != XPATH_LOCATIONSET) && 1510 (ctxt->value->type != XPATH_NODESET))) 1511 XP_ERROR(XPATH_INVALID_TYPE) 1512 1513 obj = valuePop(ctxt); 1514 if (obj->type == XPATH_NODESET) { 1515 /* 1516 * First convert to a location set 1517 */ 1518 tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval); 1519 xmlXPathFreeObject(obj); 1520 obj = tmp; 1521 } 1522 1523 newset = xmlXPtrLocationSetCreate(NULL); 1524 oldset = (xmlLocationSetPtr) obj->user; 1525 if (oldset != NULL) { 1526 int i; 1527 1528 for (i = 0; i < oldset->locNr; i++) { 1529 tmp = oldset->locTab[i]; 1530 if (tmp == NULL) 1531 continue; 1532 point = NULL; 1533 switch (tmp->type) { 1534 case XPATH_POINT: 1535 point = xmlXPtrNewPoint(tmp->user, tmp->index); 1536 break; 1537 case XPATH_RANGE: { 1538 xmlNodePtr node = tmp->user; 1539 if (node != NULL) { 1540 if (node->type == XML_ATTRIBUTE_NODE) { 1541 /* TODO: Namespace Nodes ??? */ 1542 xmlXPathFreeObject(obj); 1543 xmlXPtrFreeLocationSet(newset); 1544 XP_ERROR(XPTR_SYNTAX_ERROR); 1545 } 1546 point = xmlXPtrNewPoint(node, tmp->index); 1547 } 1548 if (tmp->user2 == NULL) { 1549 point = xmlXPtrNewPoint(node, 1550 xmlXPtrNbLocChildren(node)); 1551 } else 1552 point = xmlXPtrNewPoint(node, tmp->index); 1553 break; 1554 } 1555 default: 1556 /*** Should we raise an error ? 1557 xmlXPathFreeObject(obj); 1558 xmlXPathFreeObject(newset); 1559 XP_ERROR(XPATH_INVALID_TYPE) 1560 ***/ 1561 break; 1562 } 1563 if (point != NULL) 1564 xmlXPtrLocationSetAdd(newset, point); 1565 } 1566 } 1567 xmlXPathFreeObject(obj); 1568} 1569 1570 1571/** 1572 * xmlXPtrCoveringRange: 1573 * @ctxt: the XPointer Parser context 1574 * @loc: the location for which the covering range must be computed 1575 * 1576 * A covering range is a range that wholly encompasses a location 1577 * Section 5.3.3. Covering Ranges for All Location Types 1578 * http://www.w3.org/TR/xptr#N2267 1579 * 1580 * Returns a new location or NULL in case of error 1581 */ 1582xmlXPathObjectPtr 1583xmlXPtrCoveringRange(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr loc) { 1584 if (loc == NULL) 1585 return(NULL); 1586 if ((ctxt == NULL) || (ctxt->context == NULL) || 1587 (ctxt->context->doc == NULL)) 1588 return(NULL); 1589 switch (loc->type) { 1590 case XPATH_POINT: 1591 return(xmlXPtrNewRange(loc->user, loc->index, 1592 loc->user, loc->index)); 1593 case XPATH_RANGE: 1594 if (loc->user2 != NULL) { 1595 return(xmlXPtrNewRange(loc->user, loc->index, 1596 loc->user2, loc->index2)); 1597 } else { 1598 xmlNodePtr node = (xmlNodePtr) loc->user; 1599 if (node == (xmlNodePtr) ctxt->context->doc) { 1600 return(xmlXPtrNewRange(node, 0, node, 1601 xmlXPtrGetArity(node))); 1602 } else { 1603 switch (node->type) { 1604 case XML_ATTRIBUTE_NODE: 1605 /* !!! our model is slightly different than XPath */ 1606 return(xmlXPtrNewRange(node, 0, node, 1607 xmlXPtrGetArity(node))); 1608 case XML_ELEMENT_NODE: 1609 case XML_TEXT_NODE: 1610 case XML_CDATA_SECTION_NODE: 1611 case XML_ENTITY_REF_NODE: 1612 case XML_PI_NODE: 1613 case XML_COMMENT_NODE: 1614 case XML_DOCUMENT_NODE: 1615 case XML_NOTATION_NODE: 1616 case XML_HTML_DOCUMENT_NODE: { 1617 int index = xmlXPtrGetIndex(node); 1618 1619 node = node->parent; 1620 return(xmlXPtrNewRange(node, index - 1, 1621 node, index + 1)); 1622 } 1623 default: 1624 return(NULL); 1625 } 1626 } 1627 } 1628 default: 1629 TODO /* missed one case ??? */ 1630 } 1631 return(NULL); 1632} 1633 1634/** 1635 * xmlXPtrRangeFunction: 1636 * @ctxt: the XPointer Parser context 1637 * 1638 * Function implementing the range() function 5.4.3 1639 * location-set range(location-set ) 1640 * 1641 * The range function returns ranges covering the locations in 1642 * the argument location-set. For each location x in the argument 1643 * location-set, a range location representing the covering range of 1644 * x is added to the result location-set. 1645 */ 1646void 1647xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) { 1648 int i; 1649 xmlXPathObjectPtr set; 1650 xmlLocationSetPtr oldset; 1651 xmlLocationSetPtr newset; 1652 1653 CHECK_ARITY(1); 1654 if ((ctxt->value == NULL) || 1655 ((ctxt->value->type != XPATH_LOCATIONSET) && 1656 (ctxt->value->type != XPATH_NODESET))) 1657 XP_ERROR(XPATH_INVALID_TYPE) 1658 1659 set = valuePop(ctxt); 1660 if (set->type == XPATH_NODESET) { 1661 xmlXPathObjectPtr tmp; 1662 1663 /* 1664 * First convert to a location set 1665 */ 1666 tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval); 1667 xmlXPathFreeObject(set); 1668 set = tmp; 1669 } 1670 oldset = (xmlLocationSetPtr) set->user; 1671 1672 /* 1673 * The loop is to compute the covering range for each item and add it 1674 */ 1675 newset = xmlXPtrLocationSetCreate(NULL); 1676 for (i = 0;i < oldset->locNr;i++) { 1677 xmlXPtrLocationSetAdd(newset, 1678 xmlXPtrCoveringRange(ctxt, oldset->locTab[i])); 1679 } 1680 1681 /* 1682 * Save the new value and cleanup 1683 */ 1684 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); 1685 xmlXPathFreeObject(set); 1686} 1687 1688/** 1689 * xmlXPtrInsideRange: 1690 * @ctxt: the XPointer Parser context 1691 * @loc: the location for which the inside range must be computed 1692 * 1693 * A inside range is a range described in the range-inside() description 1694 * 1695 * Returns a new location or NULL in case of error 1696 */ 1697xmlXPathObjectPtr 1698xmlXPtrInsideRange(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr loc) { 1699 if (loc == NULL) 1700 return(NULL); 1701 if ((ctxt == NULL) || (ctxt->context == NULL) || 1702 (ctxt->context->doc == NULL)) 1703 return(NULL); 1704 switch (loc->type) { 1705 case XPATH_POINT: { 1706 xmlNodePtr node = (xmlNodePtr) loc->user; 1707 switch (node->type) { 1708 case XML_PI_NODE: 1709 case XML_COMMENT_NODE: 1710 case XML_TEXT_NODE: 1711 case XML_CDATA_SECTION_NODE: { 1712 if (node->content == NULL) { 1713 return(xmlXPtrNewRange(node, 0, node, 0)); 1714 } else { 1715#ifndef XML_USE_BUFFER_CONTENT 1716 return(xmlXPtrNewRange(node, 0, node, 1717 xmlStrlen(node->content))); 1718#else 1719 return(xmlXPtrNewRange(node, 0, node, 1720 xmlBufferLength(node->content))); 1721#endif 1722 } 1723 } 1724 case XML_ATTRIBUTE_NODE: 1725 case XML_ELEMENT_NODE: 1726 case XML_ENTITY_REF_NODE: 1727 case XML_DOCUMENT_NODE: 1728 case XML_NOTATION_NODE: 1729 case XML_HTML_DOCUMENT_NODE: { 1730 return(xmlXPtrNewRange(node, 0, node, 1731 xmlXPtrGetArity(node))); 1732 } 1733 default: 1734 return(NULL); 1735 } 1736 return(NULL); 1737 } 1738 case XPATH_RANGE: { 1739 xmlNodePtr node = (xmlNodePtr) loc->user; 1740 if (loc->user2 != NULL) { 1741 return(xmlXPtrNewRange(node, loc->index, 1742 loc->user2, loc->index2)); 1743 } else { 1744 switch (node->type) { 1745 case XML_PI_NODE: 1746 case XML_COMMENT_NODE: 1747 case XML_TEXT_NODE: 1748 case XML_CDATA_SECTION_NODE: { 1749 if (node->content == NULL) { 1750 return(xmlXPtrNewRange(node, 0, node, 0)); 1751 } else { 1752#ifndef XML_USE_BUFFER_CONTENT 1753 return(xmlXPtrNewRange(node, 0, node, 1754 xmlStrlen(node->content))); 1755#else 1756 return(xmlXPtrNewRange(node, 0, node, 1757 xmlBufferLength(node->content))); 1758#endif 1759 } 1760 } 1761 case XML_ATTRIBUTE_NODE: 1762 case XML_ELEMENT_NODE: 1763 case XML_ENTITY_REF_NODE: 1764 case XML_DOCUMENT_NODE: 1765 case XML_NOTATION_NODE: 1766 case XML_HTML_DOCUMENT_NODE: { 1767 return(xmlXPtrNewRange(node, 0, node, 1768 xmlXPtrGetArity(node))); 1769 } 1770 default: 1771 return(NULL); 1772 } 1773 return(NULL); 1774 } 1775 } 1776 default: 1777 TODO /* missed one case ??? */ 1778 } 1779 return(NULL); 1780} 1781 1782/** 1783 * xmlXPtrRangeInsideFunction: 1784 * @ctxt: the XPointer Parser context 1785 * 1786 * Function implementing the range-inside() function 5.4.3 1787 * location-set range-inside(location-set ) 1788 * 1789 * The range-inside function returns ranges covering the contents of 1790 * the locations in the argument location-set. For each location x in 1791 * the argument location-set, a range location is added to the result 1792 * location-set. If x is a range location, then x is added to the 1793 * result location-set. If x is not a range location, then x is used 1794 * as the container location of the start and end points of the range 1795 * location to be added; the index of the start point of the range is 1796 * zero; if the end point is a character point then its index is the 1797 * length of the string-value of x, and otherwise is the number of 1798 * location children of x. 1799 * 1800 */ 1801void 1802xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs) { 1803 int i; 1804 xmlXPathObjectPtr set; 1805 xmlLocationSetPtr oldset; 1806 xmlLocationSetPtr newset; 1807 1808 CHECK_ARITY(1); 1809 if ((ctxt->value == NULL) || 1810 ((ctxt->value->type != XPATH_LOCATIONSET) && 1811 (ctxt->value->type != XPATH_NODESET))) 1812 XP_ERROR(XPATH_INVALID_TYPE) 1813 1814 set = valuePop(ctxt); 1815 if (set->type == XPATH_NODESET) { 1816 xmlXPathObjectPtr tmp; 1817 1818 /* 1819 * First convert to a location set 1820 */ 1821 tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval); 1822 xmlXPathFreeObject(set); 1823 set = tmp; 1824 } 1825 oldset = (xmlLocationSetPtr) set->user; 1826 1827 /* 1828 * The loop is to compute the covering range for each item and add it 1829 */ 1830 newset = xmlXPtrLocationSetCreate(NULL); 1831 for (i = 0;i < oldset->locNr;i++) { 1832 xmlXPtrLocationSetAdd(newset, 1833 xmlXPtrInsideRange(ctxt, oldset->locTab[i])); 1834 } 1835 1836 /* 1837 * Save the new value and cleanup 1838 */ 1839 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); 1840 xmlXPathFreeObject(set); 1841} 1842 1843/** 1844 * xmlXPtrRangeToFunction: 1845 * @ctxt: the XPointer Parser context 1846 * 1847 * Implement the range-to() XPointer function 1848 */ 1849void 1850xmlXPtrRangeToFunction(xmlXPathParserContextPtr ctxt, int nargs) { 1851 xmlXPathObjectPtr range; 1852 const xmlChar *cur; 1853 xmlXPathObjectPtr res, obj; 1854 xmlXPathObjectPtr tmp; 1855 xmlLocationSetPtr newset = NULL; 1856 xmlNodeSetPtr oldset; 1857 int i; 1858 1859 CHECK_ARITY(1); 1860 /* 1861 * Save the expression pointer since we will have to evaluate 1862 * it multiple times. Initialize the new set. 1863 */ 1864 CHECK_TYPE(XPATH_NODESET); 1865 obj = valuePop(ctxt); 1866 oldset = obj->nodesetval; 1867 ctxt->context->node = NULL; 1868 1869 cur = ctxt->cur; 1870 newset = xmlXPtrLocationSetCreate(NULL); 1871 1872 for (i = 0; i < oldset->nodeNr; i++) { 1873 ctxt->cur = cur; 1874 1875 /* 1876 * Run the evaluation with a node list made of a single item 1877 * in the nodeset. 1878 */ 1879 ctxt->context->node = oldset->nodeTab[i]; 1880 tmp = xmlXPathNewNodeSet(ctxt->context->node); 1881 valuePush(ctxt, tmp); 1882 1883 xmlXPathEvalExpr(ctxt); 1884 CHECK_ERROR; 1885 1886 /* 1887 * The result of the evaluation need to be tested to 1888 * decided whether the filter succeeded or not 1889 */ 1890 res = valuePop(ctxt); 1891 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res); 1892 if (range != NULL) { 1893 xmlXPtrLocationSetAdd(newset, range); 1894 } 1895 1896 /* 1897 * Cleanup 1898 */ 1899 if (res != NULL) 1900 xmlXPathFreeObject(res); 1901 if (ctxt->value == tmp) { 1902 res = valuePop(ctxt); 1903 xmlXPathFreeObject(res); 1904 } 1905 1906 ctxt->context->node = NULL; 1907 } 1908 1909 /* 1910 * The result is used as the new evaluation set. 1911 */ 1912 xmlXPathFreeObject(obj); 1913 ctxt->context->node = NULL; 1914 ctxt->context->contextSize = -1; 1915 ctxt->context->proximityPosition = -1; 1916 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); 1917} 1918 1919/** 1920 * xmlXPtrAdvanceNode: 1921 * @cur: the node 1922 * 1923 * Advance to the next element or text node in document order 1924 * TODO: add a stack for entering/exiting entities 1925 * 1926 * Returns -1 in case of failure, 0 otherwise 1927 */ 1928xmlNodePtr 1929xmlXPtrAdvanceNode(xmlNodePtr cur) { 1930next: 1931 if (cur == NULL) 1932 return(NULL); 1933 if (cur->children != NULL) { 1934 cur = cur->children ; 1935 goto found; 1936 } 1937 if (cur->next != NULL) { 1938 cur = cur->next; 1939 goto found; 1940 } 1941 do { 1942 cur = cur->parent; 1943 if (cur == NULL) return(NULL); 1944 if (cur->next != NULL) { 1945 cur = cur->next; 1946 goto found; 1947 } 1948 } while (cur != NULL); 1949 1950found: 1951 if ((cur->type != XML_ELEMENT_NODE) && 1952 (cur->type != XML_TEXT_NODE) && 1953 (cur->type != XML_DOCUMENT_NODE) && 1954 (cur->type != XML_HTML_DOCUMENT_NODE) && 1955 (cur->type != XML_CDATA_SECTION_NODE)) 1956 goto next; 1957 if (cur->type == XML_ENTITY_REF_NODE) { 1958 TODO 1959 } 1960 return(cur); 1961} 1962 1963/** 1964 * xmlXPtrAdvanceChar: 1965 * @node: the node 1966 * @index: the index 1967 * @bytes: the number of bytes 1968 * 1969 * Advance a point of the associated number of bytes (not UTF8 chars) 1970 * 1971 * Returns -1 in case of failure, 0 otherwise 1972 */ 1973int 1974xmlXPtrAdvanceChar(xmlNodePtr *node, int *index, int bytes) { 1975 xmlNodePtr cur; 1976 int pos; 1977 int len; 1978 1979 if ((node == NULL) || (index == NULL)) 1980 return(-1); 1981 cur = *node; 1982 if (cur == NULL) 1983 return(-1); 1984 pos = *index; 1985 1986 while (bytes >= 0) { 1987 /* 1988 * First position to the beginning of the first text node 1989 * corresponding to this point 1990 */ 1991 while ((cur != NULL) && 1992 ((cur->type == XML_ELEMENT_NODE) || 1993 (cur->type == XML_DOCUMENT_NODE) || 1994 (cur->type == XML_HTML_DOCUMENT_NODE))) { 1995 if (pos > 0) { 1996 cur = xmlXPtrGetNthChild(cur, pos); 1997 pos = 0; 1998 } else { 1999 cur = xmlXPtrAdvanceNode(cur); 2000 pos = 0; 2001 } 2002 } 2003 2004 if (cur == NULL) { 2005 *node = NULL; 2006 *index = 0; 2007 return(-1); 2008 } 2009 2010 /* 2011 * if there is no move needed return the current value. 2012 */ 2013 if (pos == 0) pos = 1; 2014 if (bytes == 0) { 2015 *node = cur; 2016 *index = pos; 2017 return(0); 2018 } 2019 /* 2020 * We should have a text (or cdata) node ... 2021 */ 2022 len = 0; 2023 if (cur->content != NULL) { 2024#ifndef XML_USE_BUFFER_CONTENT 2025 len = xmlStrlen(cur->content); 2026#else 2027 len = xmlBufferLength(cur->content); 2028#endif 2029 } 2030 if (pos > len) { 2031 /* Strange, the index in the text node is greater than it's len */ 2032 STRANGE 2033 pos = len; 2034 } 2035 if (pos + bytes >= len) { 2036 bytes -= (len - pos); 2037 cur = xmlXPtrAdvanceNode(cur); 2038 cur = 0; 2039 } else if (pos + bytes < len) { 2040 pos += bytes; 2041 *node = cur; 2042 *index = pos; 2043 return(0); 2044 } 2045 } 2046 return(-1); 2047} 2048 2049/** 2050 * xmlXPtrMatchString: 2051 * @string: the string to search 2052 * @start: the start textnode 2053 * @startindex: the start index 2054 * @end: the end textnode IN/OUT 2055 * @endindex: the end index IN/OUT 2056 * 2057 * Check whether the document contains @string at the position 2058 * (@start, @startindex) and limited by the (@end, @endindex) point 2059 * 2060 * Returns -1 in case of failure, 0 if not found, 1 if found in which case 2061 * (@start, @startindex) will indicate the position of the beginning 2062 * of the range and (@end, @endindex) will endicate the end 2063 * of the range 2064 */ 2065int 2066xmlXPtrMatchString(const xmlChar *string, xmlNodePtr start, int startindex, 2067 xmlNodePtr *end, int *endindex) { 2068 xmlNodePtr cur; 2069 int pos; /* 0 based */ 2070 int len; /* in bytes */ 2071 int stringlen; /* in bytes */ 2072 int match; 2073 2074 if (string == NULL) 2075 return(-1); 2076 if (start == NULL) 2077 return(-1); 2078 if ((end == NULL) || (endindex == NULL)) 2079 return(-1); 2080 cur = start; 2081 if (cur == NULL) 2082 return(-1); 2083 pos = startindex - 1; 2084 stringlen = xmlStrlen(string); 2085 2086 while (stringlen > 0) { 2087 if ((cur == *end) && (pos + stringlen > *endindex)) 2088 return(0); 2089 if (cur->content != NULL) { 2090#ifndef XML_USE_BUFFER_CONTENT 2091 len = xmlStrlen(cur->content); 2092#else 2093 len = xmlBufferLength(cur->content); 2094#endif 2095 if (len >= pos + stringlen) { 2096#ifndef XML_USE_BUFFER_CONTENT 2097 match = (!xmlStrncmp(&cur->content[pos], string, stringlen)); 2098#else 2099 len = (!xmlStrncmp(&xmlBufferContent(cur->content)[pos], 2100 string, stringlen)); 2101#endif 2102 if (match) { 2103#ifdef DEBUG_RANGES 2104 xmlGenericError(xmlGenericErrorContext, 2105 "found range %d bytes at index %d of ->", 2106 stringlen, pos + 1); 2107 xmlDebugDumpString(stdout, cur->content); 2108 xmlGenericError(xmlGenericErrorContext, "\n"); 2109#endif 2110 *end = cur; 2111 *endindex = pos + stringlen; 2112 return(1); 2113 } else { 2114 return(0); 2115 } 2116 } else { 2117 int sub = len - pos; 2118#ifndef XML_USE_BUFFER_CONTENT 2119 match = (!xmlStrncmp(&cur->content[pos], string, sub)); 2120#else 2121 len = (!xmlStrncmp(&xmlBufferContent(cur->content)[pos], 2122 string, sub)); 2123#endif 2124 if (match) { 2125#ifdef DEBUG_RANGES 2126 xmlGenericError(xmlGenericErrorContext, 2127 "found subrange %d bytes at index %d of ->", 2128 sub, pos + 1); 2129 xmlDebugDumpString(stdout, cur->content); 2130 xmlGenericError(xmlGenericErrorContext, "\n"); 2131#endif 2132 string = &string[sub]; 2133 stringlen -= sub; 2134 } else { 2135 return(0); 2136 } 2137 } 2138 } 2139 cur = xmlXPtrAdvanceNode(cur); 2140 if (cur == NULL) 2141 return(0); 2142 pos = 0; 2143 } 2144 return(1); 2145} 2146 2147/** 2148 * xmlXPtrSearchString: 2149 * @string: the string to search 2150 * @start: the start textnode IN/OUT 2151 * @startindex: the start index IN/OUT 2152 * @end: the end textnode 2153 * @endindex: the end index 2154 * 2155 * Search the next occurence of @string within the document content 2156 * until the (@end, @endindex) point is reached 2157 * 2158 * Returns -1 in case of failure, 0 if not found, 1 if found in which case 2159 * (@start, @startindex) will indicate the position of the beginning 2160 * of the range and (@end, @endindex) will endicate the end 2161 * of the range 2162 */ 2163int 2164xmlXPtrSearchString(const xmlChar *string, xmlNodePtr *start, int *startindex, 2165 xmlNodePtr *end, int *endindex) { 2166 xmlNodePtr cur; 2167 const xmlChar *str; 2168 int pos; /* 0 based */ 2169 int len; /* in bytes */ 2170 int stringlen; /* in bytes */ 2171 xmlChar first; 2172 2173 if (string == NULL) 2174 return(-1); 2175 if ((start == NULL) || (startindex == NULL)) 2176 return(-1); 2177 if ((end == NULL) || (endindex == NULL)) 2178 return(-1); 2179 cur = *start; 2180 if (cur == NULL) 2181 return(-1); 2182 pos = *startindex - 1; 2183 first = string[0]; 2184 stringlen = xmlStrlen(string); 2185 2186 while (cur != NULL) { 2187 if (cur->content != NULL) { 2188#ifndef XML_USE_BUFFER_CONTENT 2189 len = xmlStrlen(cur->content); 2190#else 2191 len = xmlBufferLength(cur->content); 2192#endif 2193 while (pos <= len) { 2194 if (first != 0) { 2195#ifndef XML_USE_BUFFER_CONTENT 2196 str = xmlStrchr(&cur->content[pos], first); 2197#else 2198 str = xmlStrchr(&xmlBufferContent(cur->content)[pos], 2199 first); 2200#endif 2201 if (str != NULL) { 2202 pos = (str - (xmlChar *)(cur->content)); 2203#ifdef DEBUG_RANGES 2204 xmlGenericError(xmlGenericErrorContext, 2205 "found '%c' at index %d of ->", 2206 first, pos + 1); 2207 xmlDebugDumpString(stdout, cur->content); 2208 xmlGenericError(xmlGenericErrorContext, "\n"); 2209#endif 2210 if (xmlXPtrMatchString(string, cur, pos + 1, 2211 end, endindex)) { 2212 *start = cur; 2213 *startindex = pos + 1; 2214 return(1); 2215 } 2216 pos++; 2217 } else { 2218 pos = len + 1; 2219 } 2220 } else { 2221 /* 2222 * An empty string is considered to match before each 2223 * character of the string-value and after the final 2224 * character. 2225 */ 2226#ifdef DEBUG_RANGES 2227 xmlGenericError(xmlGenericErrorContext, 2228 "found '' at index %d of ->", 2229 pos + 1); 2230 xmlDebugDumpString(stdout, cur->content); 2231 xmlGenericError(xmlGenericErrorContext, "\n"); 2232#endif 2233 *start = cur; 2234 *startindex = pos + 1; 2235 *end = cur; 2236 *endindex = pos + 1; 2237 return(1); 2238 } 2239 } 2240 } 2241 if ((cur == *end) && (pos >= *endindex)) 2242 return(0); 2243 cur = xmlXPtrAdvanceNode(cur); 2244 if (cur == NULL) 2245 return(0); 2246 pos = 1; 2247 } 2248 return(0); 2249} 2250 2251/** 2252 * xmlXPtrGetLastChar: 2253 * @node: the node 2254 * @index: the index 2255 * 2256 * Computes the point coordinates of the last char of this point 2257 * 2258 * Returns -1 in case of failure, 0 otherwise 2259 */ 2260int 2261xmlXPtrGetLastChar(xmlNodePtr *node, int *index) { 2262 xmlNodePtr cur; 2263 int pos, len = 0; 2264 2265 if ((node == NULL) || (index == NULL)) 2266 return(-1); 2267 cur = *node; 2268 pos = *index; 2269 2270 if (cur == NULL) 2271 return(-1); 2272 2273 if ((cur->type == XML_ELEMENT_NODE) || 2274 (cur->type == XML_DOCUMENT_NODE) || 2275 (cur->type == XML_HTML_DOCUMENT_NODE)) { 2276 if (pos > 0) { 2277 cur = xmlXPtrGetNthChild(cur, pos); 2278 pos = 0; 2279 } 2280 } 2281 while (cur != NULL) { 2282 if (cur->last != NULL) 2283 cur = cur->last; 2284 else if (cur->content != NULL) { 2285#ifndef XML_USE_BUFFER_CONTENT 2286 len = xmlStrlen(cur->content); 2287#else 2288 len = xmlBufferLength(cur->content); 2289#endif 2290 break; 2291 } 2292 } 2293 if (cur == NULL) 2294 return(-1); 2295 *node = cur; 2296 *index = len; 2297 return(0); 2298} 2299 2300/** 2301 * xmlXPtrGetStartPoint: 2302 * @obj: an range 2303 * @node: the resulting node 2304 * @index: the resulting index 2305 * 2306 * read the object and return the start point coordinates. 2307 * 2308 * Returns -1 in case of failure, 0 otherwise 2309 */ 2310int 2311xmlXPtrGetStartPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *index) { 2312 if ((obj == NULL) || (node == NULL) || (index == NULL)) 2313 return(-1); 2314 2315 switch (obj->type) { 2316 case XPATH_POINT: 2317 *node = obj->user; 2318 if (obj->index <= 0) 2319 *index = 0; 2320 else 2321 *index = obj->index; 2322 return(0); 2323 case XPATH_RANGE: 2324 *node = obj->user; 2325 if (obj->index <= 0) 2326 *index = 0; 2327 else 2328 *index = obj->index; 2329 return(0); 2330 default: 2331 return(-1); 2332 } 2333 return(-1); 2334} 2335 2336/** 2337 * xmlXPtrGetEndPoint: 2338 * @obj: an range 2339 * @node: the resulting node 2340 * @index: the resulting index 2341 * 2342 * read the object and return the end point coordinates. 2343 * 2344 * Returns -1 in case of failure, 0 otherwise 2345 */ 2346int 2347xmlXPtrGetEndPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *index) { 2348 if ((obj == NULL) || (node == NULL) || (index == NULL)) 2349 return(-1); 2350 2351 switch (obj->type) { 2352 case XPATH_POINT: 2353 *node = obj->user; 2354 if (obj->index <= 0) 2355 *index = 0; 2356 else 2357 *index = obj->index; 2358 return(0); 2359 case XPATH_RANGE: 2360 *node = obj->user; 2361 if (obj->index <= 0) 2362 *index = 0; 2363 else 2364 *index = obj->index; 2365 return(0); 2366 default: 2367 return(-1); 2368 } 2369 return(-1); 2370} 2371 2372/** 2373 * xmlXPtrStringRangeFunction: 2374 * @ctxt: the XPointer Parser context 2375 * 2376 * Function implementing the string-range() function 2377 * range as described in 5.4.2 2378 * 2379 * ------------------------------ 2380 * [Definition: For each location in the location-set argument, 2381 * string-range returns a set of string ranges, a set of substrings in a 2382 * string. Specifically, the string-value of the location is searched for 2383 * substrings that match the string argument, and the resulting location-set 2384 * will contain a range location for each non-overlapping match.] 2385 * An empty string is considered to match before each character of the 2386 * string-value and after the final character. Whitespace in a string 2387 * is matched literally, with no normalization except that provided by 2388 * XML for line ends. The third argument gives the position of the first 2389 * character to be in the resulting range, relative to the start of the 2390 * match. The default value is 1, which makes the range start immediately 2391 * before the first character of the matched string. The fourth argument 2392 * gives the number of characters in the range; the default is that the 2393 * range extends to the end of the matched string. 2394 * 2395 * Element boundaries, as well as entire embedded nodes such as processing 2396 * instructions and comments, are ignored as defined in [XPath]. 2397 * 2398 * If the string in the second argument is not found in the string-value 2399 * of the location, or if a value in the third or fourth argument indicates 2400 * a string that is beyond the beginning or end of the document, the 2401 * expression fails. 2402 * 2403 * The points of the range-locations in the returned location-set will 2404 * all be character points. 2405 * ------------------------------ 2406 */ 2407void 2408xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2409 int i, startindex, endindex, fendindex; 2410 xmlNodePtr start, end, fend; 2411 xmlXPathObjectPtr set; 2412 xmlLocationSetPtr oldset; 2413 xmlLocationSetPtr newset; 2414 xmlXPathObjectPtr string; 2415 xmlXPathObjectPtr position = NULL; 2416 xmlXPathObjectPtr number = NULL; 2417 int found, pos, num; 2418 2419 /* 2420 * Grab the arguments 2421 */ 2422 if ((nargs < 2) || (nargs > 4)) 2423 XP_ERROR(XPATH_INVALID_ARITY); 2424 2425 if (nargs >= 4) { 2426 CHECK_TYPE(XPATH_NUMBER); 2427 number = valuePop(ctxt); 2428 if (number != NULL) 2429 num = number->floatval; 2430 } 2431 if (nargs >= 3) { 2432 CHECK_TYPE(XPATH_NUMBER); 2433 position = valuePop(ctxt); 2434 if (position != NULL) 2435 pos = position->floatval; 2436 } 2437 CHECK_TYPE(XPATH_STRING); 2438 string = valuePop(ctxt); 2439 if ((ctxt->value == NULL) || 2440 ((ctxt->value->type != XPATH_LOCATIONSET) && 2441 (ctxt->value->type != XPATH_NODESET))) 2442 XP_ERROR(XPATH_INVALID_TYPE) 2443 2444 set = valuePop(ctxt); 2445 if (set->type == XPATH_NODESET) { 2446 xmlXPathObjectPtr tmp; 2447 2448 /* 2449 * First convert to a location set 2450 */ 2451 tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval); 2452 xmlXPathFreeObject(set); 2453 set = tmp; 2454 } 2455 oldset = (xmlLocationSetPtr) set->user; 2456 2457 /* 2458 * The loop is to search for each element in the location set 2459 * the list of location set corresponding to that search 2460 */ 2461 newset = xmlXPtrLocationSetCreate(NULL); 2462 for (i = 0;i < oldset->locNr;i++) { 2463#ifdef DEBUG_RANGES 2464 xmlXPathDebugDumpObject(stdout, oldset->locTab[i], 0); 2465#endif 2466 2467 xmlXPtrGetStartPoint(oldset->locTab[i], &start, &startindex); 2468 xmlXPtrGetEndPoint(oldset->locTab[i], &end, &endindex); 2469 xmlXPtrAdvanceChar(&start, &startindex, 0); 2470 xmlXPtrGetLastChar(&end, &endindex); 2471 2472#ifdef DEBUG_RANGES 2473 xmlGenericError(xmlGenericErrorContext, 2474 "from index %d of ->", startindex); 2475 xmlDebugDumpString(stdout, start->content); 2476 xmlGenericError(xmlGenericErrorContext, "\n"); 2477 xmlGenericError(xmlGenericErrorContext, 2478 "to index %d of ->", endindex); 2479 xmlDebugDumpString(stdout, end->content); 2480 xmlGenericError(xmlGenericErrorContext, "\n"); 2481#endif 2482 do { 2483 fend = end; 2484 fendindex = endindex; 2485 found = xmlXPtrSearchString(string->stringval, &start, &startindex, 2486 &fend, &fendindex); 2487 if (found == 1) { 2488 if (position == NULL) { 2489 xmlXPtrLocationSetAdd(newset, 2490 xmlXPtrNewRange(start, startindex, fend, fendindex)); 2491 } else if (xmlXPtrAdvanceChar(&start, &startindex, 2492 pos - 1) == 0) { 2493 if ((number != NULL) && (num > 0)) { 2494 int rindex; 2495 xmlNodePtr rend; 2496 rend = start; 2497 rindex = startindex - 1; 2498 if (xmlXPtrAdvanceChar(&rend, &rindex, 2499 num) == 0) { 2500 xmlXPtrLocationSetAdd(newset, 2501 xmlXPtrNewRange(start, startindex, 2502 rend, rindex)); 2503 } 2504 } else if ((number != NULL) && (num <= 0)) { 2505 xmlXPtrLocationSetAdd(newset, 2506 xmlXPtrNewRange(start, startindex, 2507 start, startindex)); 2508 } else { 2509 xmlXPtrLocationSetAdd(newset, 2510 xmlXPtrNewRange(start, startindex, 2511 fend, fendindex)); 2512 } 2513 } 2514 start = fend; 2515 startindex = fendindex; 2516 if (string->stringval[0] == 0) 2517 startindex++; 2518 } 2519 } while (found == 1); 2520 } 2521 2522 /* 2523 * Save the new value and cleanup 2524 */ 2525 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); 2526 xmlXPathFreeObject(set); 2527 xmlXPathFreeObject(string); 2528 if (position) xmlXPathFreeObject(position); 2529 if (number) xmlXPathFreeObject(number); 2530} 2531 2532#else 2533#endif 2534 2535