xpath.c revision e3d88efb8d24028a9893e12ae20633c8ee32fa49
1/* 2 * xpath.c: XML Path Language implementation 3 * XPath is a language for addressing parts of an XML document, 4 * designed to be used by both XSLT and XPointer. 5 * 6 * Reference: W3C Working Draft internal 5 July 1999 7 * http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html 8 * Public reference: 9 * http://www.w3.org/TR/WD-xpath/ 10 * 11 * See COPYRIGHT for the status of this software 12 * 13 * Author: Daniel.Veillard@w3.org 14 */ 15 16#ifdef WIN32 17#include "win32config.h" 18#else 19#include "config.h" 20#endif 21 22#include <stdio.h> 23#include <string.h> 24 25#ifdef HAVE_SYS_TYPES_H 26#include <sys/types.h> 27#endif 28#ifdef HAVE_MATH_H 29#include <math.h> 30#endif 31#ifdef HAVE_MATH_H 32#include <float.h> 33#endif 34#ifdef HAVE_IEEEFP_H 35#include <ieeefp.h> 36#endif 37#ifdef HAVE_NAN_H 38#include <nan.h> 39#endif 40#ifdef HAVE_CTYPE_H 41#include <ctype.h> 42#endif 43 44#include "xmlmemory.h" 45#include "tree.h" 46#include "valid.h" 47#include "xpath.h" 48#include "parserInternals.h" 49 50/* #define DEBUG */ 51/* #define DEBUG_STEP */ 52/* #define DEBUG_EXPR */ 53 54/* 55 * Setup stuff for floating point 56 * The lack of portability of this section of the libc is annoying ! 57 */ 58double xmlXPathNAN = 0; 59double xmlXPathPINF = 1; 60double xmlXPathMINF = -1; 61 62#ifndef isinf 63#ifndef HAVE_ISINF 64 65#if HAVE_FPCLASS 66 67int isinf(double d) { 68 fpclass_t type = fpclass(d); 69 switch (type) { 70 case FP_NINF: 71 return(-1); 72 case FP_PINF: 73 return(1); 74 default: 75 return(0); 76 } 77 return(0); 78} 79 80#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D) 81 82#if HAVE_FP_CLASS_H 83#include <fp_class.h> 84#endif 85 86int isinf(double d) { 87#if HAVE_FP_CLASS 88 int fpclass = fp_class(d); 89#else 90 int fpclass = fp_class_d(d); 91#endif 92 if (fpclass == FP_POS_INF) 93 return(1); 94 if (fpclass == FP_NEG_INF) 95 return(-1); 96 return(0); 97} 98 99#elif defined(HAVE_CLASS) 100 101int isinf(double d) { 102 int fpclass = class(d); 103 if (fpclass == FP_PLUS_INF) 104 return(1); 105 if (fpclass == FP_MINUS_INF) 106 return(-1); 107 return(0); 108} 109#elif defined(finite) || defined(HAVE_FINITE) 110int isinf(double x) { return !finite(x) && x==x; } 111#elif defined(HUGE_VAL) 112static int isinf(double x) 113{ 114 if (x == HUGE_VAL) 115 return(1); 116 if (x == -HUGE_VAL) 117 return(-1); 118 return(0); 119} 120#endif 121 122#endif /* ! HAVE_ISINF */ 123#endif /* ! defined(isinf) */ 124 125#ifndef isnan 126#ifndef HAVE_ISNAN 127 128#ifdef HAVE_ISNAND 129#define isnan(f) isnand(f) 130#endif /* HAVE_iSNAND */ 131 132#endif /* ! HAVE_iSNAN */ 133#endif /* ! defined(isnan) */ 134 135/** 136 * xmlXPathInit: 137 * 138 * Initialize the XPath environment 139 */ 140void 141xmlXPathInit(void) { 142 static int initialized = 0; 143 144 if (initialized) return; 145 146 xmlXPathNAN = 0; 147 xmlXPathNAN /= 0; 148 149 xmlXPathPINF = 1; 150 xmlXPathPINF /= 0; 151 152 xmlXPathMINF = -1; 153 xmlXPathMINF /= 0; 154 155 initialized = 1; 156} 157 158FILE *xmlXPathDebug = NULL; 159 160#define TODO \ 161 fprintf(xmlXPathDebug, "Unimplemented block at %s:%d\n", \ 162 __FILE__, __LINE__); 163 164#define STRANGE \ 165 fprintf(xmlXPathDebug, "Internal error at %s:%d\n", \ 166 __FILE__, __LINE__); 167 168double xmlXPathStringEvalNumber(const xmlChar *str); 169void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs); 170 171/************************************************************************ 172 * * 173 * Parser stacks related functions and macros * 174 * * 175 ************************************************************************/ 176 177/* 178 * Generic function for accessing stacks in the Parser Context 179 */ 180 181#define PUSH_AND_POP(type, name) \ 182extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \ 183 if (ctxt->name##Nr >= ctxt->name##Max) { \ 184 ctxt->name##Max *= 2; \ 185 ctxt->name##Tab = (void *) xmlRealloc(ctxt->name##Tab, \ 186 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \ 187 if (ctxt->name##Tab == NULL) { \ 188 fprintf(xmlXPathDebug, "realloc failed !\n"); \ 189 return(0); \ 190 } \ 191 } \ 192 ctxt->name##Tab[ctxt->name##Nr] = value; \ 193 ctxt->name = value; \ 194 return(ctxt->name##Nr++); \ 195} \ 196extern type name##Pop(xmlXPathParserContextPtr ctxt) { \ 197 type ret; \ 198 if (ctxt->name##Nr <= 0) return(0); \ 199 ctxt->name##Nr--; \ 200 if (ctxt->name##Nr > 0) \ 201 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \ 202 else \ 203 ctxt->name = NULL; \ 204 ret = ctxt->name##Tab[ctxt->name##Nr]; \ 205 ctxt->name##Tab[ctxt->name##Nr] = 0; \ 206 return(ret); \ 207} \ 208 209PUSH_AND_POP(xmlXPathObjectPtr, value) 210 211/* 212 * Macros for accessing the content. Those should be used only by the parser, 213 * and not exported. 214 * 215 * Dirty macros, i.e. one need to make assumption on the context to use them 216 * 217 * CUR_PTR return the current pointer to the xmlChar to be parsed. 218 * CUR returns the current xmlChar value, i.e. a 8 bit value if compiled 219 * in ISO-Latin or UTF-8, and the current 16 bit value if compiled 220 * in UNICODE mode. This should be used internally by the parser 221 * only to compare to ASCII values otherwise it would break when 222 * running with UTF-8 encoding. 223 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only 224 * to compare on ASCII based substring. 225 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined 226 * strings within the parser. 227 * CURRENT Returns the current char value, with the full decoding of 228 * UTF-8 if we are using this mode. It returns an int. 229 * NEXT Skip to the next character, this does the proper decoding 230 * in UTF-8 mode. It also pop-up unfinished entities on the fly. 231 * It returns the pointer to the current xmlChar. 232 */ 233 234#define CUR (*ctxt->cur) 235#define SKIP(val) ctxt->cur += (val) 236#define NXT(val) ctxt->cur[(val)] 237#define CUR_PTR ctxt->cur 238 239#define SKIP_BLANKS \ 240 while (IS_BLANK(*(ctxt->cur))) NEXT 241 242#ifndef USE_UTF_8 243#define CURRENT (*ctxt->cur) 244#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) 245#else 246#endif 247 248/************************************************************************ 249 * * 250 * Error handling routines * 251 * * 252 ************************************************************************/ 253 254#define XPATH_EXPRESSION_OK 0 255#define XPATH_NUMBER_ERROR 1 256#define XPATH_UNFINISHED_LITERAL_ERROR 2 257#define XPATH_START_LITERAL_ERROR 3 258#define XPATH_VARIABLE_REF_ERROR 4 259#define XPATH_UNDEF_VARIABLE_ERROR 5 260#define XPATH_INVALID_PREDICATE_ERROR 6 261#define XPATH_EXPR_ERROR 7 262#define XPATH_UNCLOSED_ERROR 8 263#define XPATH_UNKNOWN_FUNC_ERROR 9 264#define XPATH_INVALID_OPERAND 10 265#define XPATH_INVALID_TYPE 11 266#define XPATH_INVALID_ARITY 12 267 268const char *xmlXPathErrorMessages[] = { 269 "Ok", 270 "Number encoding", 271 "Unfinished litteral", 272 "Start of litteral", 273 "Expected $ for variable reference", 274 "Undefined variable", 275 "Invalid predicate", 276 "Invalid expression", 277 "Missing closing curly brace", 278 "Unregistered function", 279 "Invalid operand", 280 "Invalid type", 281 "Invalid number of arguments", 282}; 283 284/** 285 * xmlXPathError: 286 * @ctxt: the XPath Parser context 287 * @file: the file name 288 * @line: the line number 289 * @no: the error number 290 * 291 * Create a new xmlNodeSetPtr of type double and of value @val 292 * 293 * Returns the newly created object. 294 */ 295void 296xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file, 297 int line, int no) { 298 int n; 299 const xmlChar *cur; 300 const xmlChar *base; 301 302 fprintf(xmlXPathDebug, "Error %s:%d: %s\n", file, line, 303 xmlXPathErrorMessages[no]); 304 305 cur = ctxt->cur; 306 base = ctxt->base; 307 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) { 308 cur--; 309 } 310 n = 0; 311 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r')) 312 cur--; 313 if ((*cur == '\n') || (*cur == '\r')) cur++; 314 base = cur; 315 n = 0; 316 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) { 317 fprintf(xmlXPathDebug, "%c", (unsigned char) *cur++); 318 n++; 319 } 320 fprintf(xmlXPathDebug, "\n"); 321 cur = ctxt->cur; 322 while ((*cur == '\n') || (*cur == '\r')) 323 cur--; 324 n = 0; 325 while ((cur != base) && (n++ < 80)) { 326 fprintf(xmlXPathDebug, " "); 327 base++; 328 } 329 fprintf(xmlXPathDebug,"^\n"); 330} 331 332#define CHECK_ERROR \ 333 if (ctxt->error != XPATH_EXPRESSION_OK) return 334 335#define ERROR(X) \ 336 { xmlXPatherror(ctxt, __FILE__, __LINE__, X); \ 337 ctxt->error = (X); return; } 338 339#define ERROR0(X) \ 340 { xmlXPatherror(ctxt, __FILE__, __LINE__, X); \ 341 ctxt->error = (X); return(0); } 342 343#define CHECK_TYPE(typeval) \ 344 if ((ctxt->value == NULL) || (ctxt->value->type != typeval)) \ 345 ERROR(XPATH_INVALID_TYPE) \ 346 347 348/************************************************************************ 349 * * 350 * Routines to handle NodeSets * 351 * * 352 ************************************************************************/ 353 354#define XML_NODESET_DEFAULT 10 355/** 356 * xmlXPathNodeSetCreate: 357 * @val: an initial xmlNodePtr, or NULL 358 * 359 * Create a new xmlNodeSetPtr of type double and of value @val 360 * 361 * Returns the newly created object. 362 */ 363xmlNodeSetPtr 364xmlXPathNodeSetCreate(xmlNodePtr val) { 365 xmlNodeSetPtr ret; 366 367 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet)); 368 if (ret == NULL) { 369 fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n"); 370 return(NULL); 371 } 372 memset(ret, 0 , (size_t) sizeof(xmlNodeSet)); 373 if (val != NULL) { 374 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * 375 sizeof(xmlNodePtr)); 376 if (ret->nodeTab == NULL) { 377 fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n"); 378 return(NULL); 379 } 380 memset(ret->nodeTab, 0 , 381 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); 382 ret->nodeMax = XML_NODESET_DEFAULT; 383 ret->nodeTab[ret->nodeNr++] = val; 384 } 385 return(ret); 386} 387 388/** 389 * xmlXPathNodeSetAdd: 390 * @cur: the initial node set 391 * @val: a new xmlNodePtr 392 * 393 * add a new xmlNodePtr ot an existing NodeSet 394 */ 395void 396xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { 397 int i; 398 399 if (val == NULL) return; 400 401 /* 402 * check against doublons 403 */ 404 for (i = 0;i < cur->nodeNr;i++) 405 if (cur->nodeTab[i] == val) return; 406 407 /* 408 * grow the nodeTab if needed 409 */ 410 if (cur->nodeMax == 0) { 411 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * 412 sizeof(xmlNodePtr)); 413 if (cur->nodeTab == NULL) { 414 fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n"); 415 return; 416 } 417 memset(cur->nodeTab, 0 , 418 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); 419 cur->nodeMax = XML_NODESET_DEFAULT; 420 } else if (cur->nodeNr == cur->nodeMax) { 421 xmlNodePtr *temp; 422 423 cur->nodeMax *= 2; 424 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 425 sizeof(xmlNodePtr)); 426 if (temp == NULL) { 427 fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n"); 428 return; 429 } 430 cur->nodeTab = temp; 431 } 432 cur->nodeTab[cur->nodeNr++] = val; 433} 434 435/** 436 * xmlXPathNodeSetMerge: 437 * @val1: the first NodeSet 438 * @val2: the second NodeSet 439 * 440 * Merges two nodesets, all nodes from @val2 are added to @val1 441 * 442 * Returns val1 once extended or NULL in case of error. 443 */ 444xmlNodeSetPtr 445xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { 446 int i; 447 448 if (val1 == NULL) return(NULL); 449 if (val2 == NULL) return(val1); 450 451 /* 452 * !!!!! this can be optimized a lot, knowing that both 453 * val1 and val2 already have unicity of their values. 454 */ 455 456 for (i = 0;i < val2->nodeNr;i++) 457 xmlXPathNodeSetAdd(val1, val2->nodeTab[i]); 458 459 return(val1); 460} 461 462/** 463 * xmlXPathNodeSetDel: 464 * @cur: the initial node set 465 * @val: an xmlNodePtr 466 * 467 * Removes an xmlNodePtr from an existing NodeSet 468 */ 469void 470xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) { 471 int i; 472 473 if (cur == NULL) return; 474 if (val == NULL) return; 475 476 /* 477 * check against doublons 478 */ 479 for (i = 0;i < cur->nodeNr;i++) 480 if (cur->nodeTab[i] == val) break; 481 482 if (i >= cur->nodeNr) { 483#ifdef DEBUG 484 fprintf(xmlXPathDebug, 485 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n", 486 val->name); 487#endif 488 return; 489 } 490 cur->nodeNr--; 491 for (;i < cur->nodeNr;i++) 492 cur->nodeTab[i] = cur->nodeTab[i + 1]; 493 cur->nodeTab[cur->nodeNr] = NULL; 494} 495 496/** 497 * xmlXPathNodeSetRemove: 498 * @cur: the initial node set 499 * @val: the index to remove 500 * 501 * Removes an entry from an existing NodeSet list. 502 */ 503void 504xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) { 505 if (cur == NULL) return; 506 if (val >= cur->nodeNr) return; 507 cur->nodeNr--; 508 for (;val < cur->nodeNr;val++) 509 cur->nodeTab[val] = cur->nodeTab[val + 1]; 510 cur->nodeTab[cur->nodeNr] = NULL; 511} 512 513/** 514 * xmlXPathFreeNodeSet: 515 * @obj: the xmlNodeSetPtr to free 516 * 517 * Free the NodeSet compound (not the actual nodes !). 518 */ 519void 520xmlXPathFreeNodeSet(xmlNodeSetPtr obj) { 521 if (obj == NULL) return; 522 if (obj->nodeTab != NULL) { 523#ifdef DEBUG 524 memset(obj->nodeTab, 0xB , (size_t) sizeof(xmlNodePtr) * obj->nodeMax); 525#endif 526 xmlFree(obj->nodeTab); 527 } 528#ifdef DEBUG 529 memset(obj, 0xB , (size_t) sizeof(xmlNodeSet)); 530#endif 531 xmlFree(obj); 532} 533 534#if defined(DEBUG) || defined(DEBUG_STEP) 535/** 536 * xmlXPathDebugNodeSet: 537 * @output: a FILE * for the output 538 * @obj: the xmlNodeSetPtr to free 539 * 540 * Quick display of a NodeSet 541 */ 542void 543xmlXPathDebugNodeSet(FILE *output, xmlNodeSetPtr obj) { 544 int i; 545 546 if (output == NULL) output = xmlXPathDebug; 547 if (obj == NULL) { 548 fprintf(output, "NodeSet == NULL !\n"); 549 return; 550 } 551 if (obj->nodeNr == 0) { 552 fprintf(output, "NodeSet is empty\n"); 553 return; 554 } 555 if (obj->nodeTab == NULL) { 556 fprintf(output, " nodeTab == NULL !\n"); 557 return; 558 } 559 for (i = 0; i < obj->nodeNr; i++) { 560 if (obj->nodeTab[i] == NULL) { 561 fprintf(output, " NULL !\n"); 562 return; 563 } 564 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) || 565 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE)) 566 fprintf(output, " /"); 567 else if (obj->nodeTab[i]->name == NULL) 568 fprintf(output, " noname!"); 569 else fprintf(output, " %s", obj->nodeTab[i]->name); 570 } 571 fprintf(output, "\n"); 572} 573#endif 574 575/************************************************************************ 576 * * 577 * Routines to handle Variable * 578 * * 579 * UNIMPLEMENTED CURRENTLY * 580 * * 581 ************************************************************************/ 582 583/** 584 * xmlXPathVariablelookup: 585 * @ctxt: the XPath Parser context 586 * @prefix: the variable name namespace if any 587 * @name: the variable name 588 * 589 * Search in the Variable array of the context for the given 590 * variable value. 591 * 592 * UNIMPLEMENTED: always return NULL. 593 * 594 * Returns the value or NULL if not found 595 */ 596xmlXPathObjectPtr 597xmlXPathVariablelookup(xmlXPathParserContextPtr ctxt, 598 const xmlChar *prefix, const xmlChar *name) { 599 return(NULL); 600} 601 602/************************************************************************ 603 * * 604 * Routines to handle Values * 605 * * 606 ************************************************************************/ 607 608/* Allocations are terrible, one need to optimize all this !!! */ 609 610/** 611 * xmlXPathNewFloat: 612 * @val: the double value 613 * 614 * Create a new xmlXPathObjectPtr of type double and of value @val 615 * 616 * Returns the newly created object. 617 */ 618xmlXPathObjectPtr 619xmlXPathNewFloat(double val) { 620 xmlXPathObjectPtr ret; 621 622 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 623 if (ret == NULL) { 624 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n"); 625 return(NULL); 626 } 627 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 628 ret->type = XPATH_NUMBER; 629 ret->floatval = val; 630 return(ret); 631} 632 633/** 634 * xmlXPathNewBoolean: 635 * @val: the boolean value 636 * 637 * Create a new xmlXPathObjectPtr of type boolean and of value @val 638 * 639 * Returns the newly created object. 640 */ 641xmlXPathObjectPtr 642xmlXPathNewBoolean(int val) { 643 xmlXPathObjectPtr ret; 644 645 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 646 if (ret == NULL) { 647 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n"); 648 return(NULL); 649 } 650 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 651 ret->type = XPATH_BOOLEAN; 652 ret->boolval = (val != 0); 653 return(ret); 654} 655 656/** 657 * xmlXPathNewString: 658 * @val: the xmlChar * value 659 * 660 * Create a new xmlXPathObjectPtr of type string and of value @val 661 * 662 * Returns the newly created object. 663 */ 664xmlXPathObjectPtr 665xmlXPathNewString(const xmlChar *val) { 666 xmlXPathObjectPtr ret; 667 668 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 669 if (ret == NULL) { 670 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n"); 671 return(NULL); 672 } 673 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 674 ret->type = XPATH_STRING; 675 ret->stringval = xmlStrdup(val); 676 return(ret); 677} 678 679/** 680 * xmlXPathNewCString: 681 * @val: the char * value 682 * 683 * Create a new xmlXPathObjectPtr of type string and of value @val 684 * 685 * Returns the newly created object. 686 */ 687xmlXPathObjectPtr 688xmlXPathNewCString(const char *val) { 689 xmlXPathObjectPtr ret; 690 691 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 692 if (ret == NULL) { 693 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n"); 694 return(NULL); 695 } 696 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 697 ret->type = XPATH_STRING; 698 ret->stringval = xmlStrdup(BAD_CAST val); 699 return(ret); 700} 701 702/** 703 * xmlXPathNewNodeSet: 704 * @val: the NodePtr value 705 * 706 * Create a new xmlXPathObjectPtr of type NodeSet and initialize 707 * it with the single Node @val 708 * 709 * Returns the newly created object. 710 */ 711xmlXPathObjectPtr 712xmlXPathNewNodeSet(xmlNodePtr val) { 713 xmlXPathObjectPtr ret; 714 715 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 716 if (ret == NULL) { 717 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n"); 718 return(NULL); 719 } 720 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 721 ret->type = XPATH_NODESET; 722 ret->nodesetval = xmlXPathNodeSetCreate(val); 723 return(ret); 724} 725 726/** 727 * xmlXPathNewNodeSetList: 728 * @val: an existing NodeSet 729 * 730 * Create a new xmlXPathObjectPtr of type NodeSet and initialize 731 * it with the Nodeset @val 732 * 733 * Returns the newly created object. 734 */ 735xmlXPathObjectPtr 736xmlXPathNewNodeSetList(xmlNodeSetPtr val) { 737 xmlXPathObjectPtr ret; 738 739 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); 740 if (ret == NULL) { 741 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n"); 742 return(NULL); 743 } 744 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); 745 ret->type = XPATH_NODESET; 746 ret->nodesetval = val; 747 return(ret); 748} 749 750/** 751 * xmlXPathFreeNodeSetList: 752 * @obj: an existing NodeSetList object 753 * 754 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in 755 * the list contrary to xmlXPathFreeObject(). 756 */ 757void 758xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) { 759 if (obj == NULL) return; 760#ifdef DEBUG 761 memset(obj, 0xB , (size_t) sizeof(xmlXPathObject)); 762#endif 763 xmlFree(obj); 764} 765 766/** 767 * xmlXPathFreeObject: 768 * @obj: the object to free 769 * 770 * Free up an xmlXPathObjectPtr object. 771 */ 772void 773xmlXPathFreeObject(xmlXPathObjectPtr obj) { 774 if (obj == NULL) return; 775 if (obj->nodesetval != NULL) 776 xmlXPathFreeNodeSet(obj->nodesetval); 777 if (obj->stringval != NULL) 778 xmlFree(obj->stringval); 779#ifdef DEBUG 780 memset(obj, 0xB , (size_t) sizeof(xmlXPathObject)); 781#endif 782 xmlFree(obj); 783} 784 785/************************************************************************ 786 * * 787 * Routines to handle XPath contexts * 788 * * 789 ************************************************************************/ 790 791/** 792 * xmlXPathNewContext: 793 * @doc: the XML document 794 * 795 * Create a new xmlXPathContext 796 * 797 * Returns the xmlXPathContext just allocated. 798 */ 799xmlXPathContextPtr 800xmlXPathNewContext(xmlDocPtr doc) { 801 xmlXPathContextPtr ret; 802 803 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext)); 804 if (ret == NULL) { 805 fprintf(xmlXPathDebug, "xmlXPathNewContext: out of memory\n"); 806 return(NULL); 807 } 808 memset(ret, 0 , (size_t) sizeof(xmlXPathContext)); 809 ret->doc = doc; 810 /*********** 811 ret->node = (xmlNodePtr) doc; 812 ret->nodelist = xmlXPathNodeSetCreate(ret->node); 813 ***********/ 814 ret->node = NULL; 815 ret->nodelist = NULL; 816 817 ret->nb_variables = 0; 818 ret->max_variables = 0; 819 ret->variables = NULL; 820 821 ret->nb_types = 0; 822 ret->max_types = 0; 823 ret->types = NULL; 824 825 ret->nb_funcs = 0; 826 ret->max_funcs = 0; 827 ret->funcs = NULL; 828 829 ret->nb_axis = 0; 830 ret->max_axis = 0; 831 ret->axis = NULL; 832 833 ret->namespaces = NULL; 834 ret->user = NULL; 835 ret->nsNr = 0; 836 return(ret); 837} 838 839/** 840 * xmlXPathFreeContext: 841 * @ctxt: the context to free 842 * 843 * Free up an xmlXPathContext 844 */ 845void 846xmlXPathFreeContext(xmlXPathContextPtr ctxt) { 847 if (ctxt->namespaces != NULL) 848 xmlFree(ctxt->namespaces); 849 850 /*********** 851 if (ctxt->nodelist != NULL) 852 xmlXPathFreeNodeSet(ctxt->nodelist); 853 ***********/ 854#ifdef DEBUG 855 memset(ctxt, 0xB , (size_t) sizeof(xmlXPathContext)); 856#endif 857 xmlFree(ctxt); 858} 859 860/************************************************************************ 861 * * 862 * Routines to handle XPath parser contexts * 863 * * 864 ************************************************************************/ 865 866#define CHECK_CTXT \ 867 if (ctxt == NULL) { \ 868 fprintf(xmlXPathDebug, "%s:%d Internal error: ctxt == NULL\n", \ 869 __FILE__, __LINE__); \ 870 } \ 871 872 873#define CHECK_CONTEXT \ 874 if (ctxt == NULL) { \ 875 fprintf(xmlXPathDebug, "%s:%d Internal error: no context\n", \ 876 __FILE__, __LINE__); \ 877 } \ 878 if (ctxt->doc == NULL) { \ 879 fprintf(xmlXPathDebug, "%s:%d Internal error: no document\n", \ 880 __FILE__, __LINE__); \ 881 } \ 882 if (ctxt->doc->root == NULL) { \ 883 fprintf(xmlXPathDebug, \ 884 "%s:%d Internal error: document without root\n", \ 885 __FILE__, __LINE__); \ 886 } \ 887 888 889/** 890 * xmlXPathNewParserContext: 891 * @str: the XPath expression 892 * @ctxt: the XPath context 893 * 894 * Create a new xmlXPathParserContext 895 * 896 * Returns the xmlXPathParserContext just allocated. 897 */ 898xmlXPathParserContextPtr 899xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) { 900 xmlXPathParserContextPtr ret; 901 902 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext)); 903 if (ret == NULL) { 904 fprintf(xmlXPathDebug, "xmlXPathNewParserContext: out of memory\n"); 905 return(NULL); 906 } 907 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext)); 908 ret->cur = ret->base = str; 909 ret->context = ctxt; 910 911 /* Allocate the value stack */ 912 ret->valueTab = (xmlXPathObjectPtr *) 913 xmlMalloc(10 * sizeof(xmlXPathObjectPtr)); 914 ret->valueNr = 0; 915 ret->valueMax = 10; 916 ret->value = NULL; 917 return(ret); 918} 919 920/** 921 * xmlXPathFreeParserContext: 922 * @ctxt: the context to free 923 * 924 * Free up an xmlXPathParserContext 925 */ 926void 927xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) { 928 if (ctxt->valueTab != NULL) { 929#ifdef DEBUG 930 memset(ctxt->valueTab, 0xB , 10 * (size_t) sizeof(xmlXPathObjectPtr)); 931#endif 932 xmlFree(ctxt->valueTab); 933 } 934#ifdef DEBUG 935 memset(ctxt, 0xB , (size_t) sizeof(xmlXPathParserContext)); 936#endif 937 xmlFree(ctxt); 938} 939 940/************************************************************************ 941 * * 942 * The implicit core function library * 943 * * 944 ************************************************************************/ 945 946/* 947 * Auto-pop and cast to a number 948 */ 949void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs); 950 951#define CHECK_ARITY(x) \ 952 if (nargs != (x)) { \ 953 ERROR(XPATH_INVALID_ARITY); \ 954 } \ 955 956 957#define POP_FLOAT \ 958 arg = valuePop(ctxt); \ 959 if (arg == NULL) { \ 960 ERROR(XPATH_INVALID_OPERAND); \ 961 } \ 962 if (arg->type != XPATH_NUMBER) { \ 963 valuePush(ctxt, arg); \ 964 xmlXPathNumberFunction(ctxt, 1); \ 965 arg = valuePop(ctxt); \ 966 } 967 968/** 969 * xmlXPathEqualNodeSetString 970 * @arg: the nodeset object argument 971 * @str: the string to compare to. 972 * 973 * Implement the equal operation on XPath objects content: @arg1 == @arg2 974 * If one object to be compared is a node-set and the other is a string, 975 * then the comparison will be true if and only if there is a node in 976 * the node-set such that the result of performing the comparison on the 977 * string-value of the node and the other string is true. 978 * 979 * Returns 0 or 1 depending on the results of the test. 980 */ 981int 982xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) { 983 int i; 984 xmlNodeSetPtr ns; 985 xmlChar *str2; 986 987 if ((str == NULL) || (arg == NULL) || (arg->type != XPATH_NODESET)) 988 return(0); 989 ns = arg->nodesetval; 990 for (i = 0;i < ns->nodeNr;i++) { 991 str2 = xmlNodeGetContent(ns->nodeTab[i]); 992 if ((str2 != NULL) && (!xmlStrcmp(str, str2))) { 993 xmlFree(str2); 994 return(1); 995 } 996 xmlFree(str2); 997 } 998 return(0); 999} 1000 1001/** 1002 * xmlXPathEqualNodeSetFloat 1003 * @arg: the nodeset object argument 1004 * @f: the float to compare to 1005 * 1006 * Implement the equal operation on XPath objects content: @arg1 == @arg2 1007 * If one object to be compared is a node-set and the other is a number, 1008 * then the comparison will be true if and only if there is a node in 1009 * the node-set such that the result of performing the comparison on the 1010 * number to be compared and on the result of converting the string-value 1011 * of that node to a number using the number function is true. 1012 * 1013 * Returns 0 or 1 depending on the results of the test. 1014 */ 1015int 1016xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, float f) { 1017 char buf[100] = ""; 1018 1019 if ((arg == NULL) || (arg->type != XPATH_NODESET)) 1020 return(0); 1021 1022 if (isnan(f)) 1023 sprintf(buf, "NaN"); 1024 else if (isinf(f) > 0) 1025 sprintf(buf, "+Infinity"); 1026 else if (isinf(f) < 0) 1027 sprintf(buf, "-Infinity"); 1028 else 1029 sprintf(buf, "%0g", f); 1030 1031 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf)); 1032} 1033 1034 1035/** 1036 * xmlXPathEqualNodeSets 1037 * @arg1: first nodeset object argument 1038 * @arg2: second nodeset object argument 1039 * 1040 * Implement the equal operation on XPath nodesets: @arg1 == @arg2 1041 * If both objects to be compared are node-sets, then the comparison 1042 * will be true if and only if there is a node in the first node-set and 1043 * a node in the second node-set such that the result of performing the 1044 * comparison on the string-values of the two nodes is true. 1045 * 1046 * (needless to say, this is a costly operation) 1047 * 1048 * Returns 0 or 1 depending on the results of the test. 1049 */ 1050int 1051xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) { 1052 int i; 1053 xmlNodeSetPtr ns; 1054 xmlChar *str; 1055 1056 if ((arg1 == NULL) || (arg1->type != XPATH_NODESET)) 1057 return(0); 1058 if ((arg2 == NULL) || (arg2->type != XPATH_NODESET)) 1059 return(0); 1060 1061 ns = arg1->nodesetval; 1062 for (i = 0;i < ns->nodeNr;i++) { 1063 str = xmlNodeGetContent(ns->nodeTab[i]); 1064 if ((str != NULL) && (xmlXPathEqualNodeSetString(arg2, str))) { 1065 xmlFree(str); 1066 return(1); 1067 } 1068 xmlFree(str); 1069 } 1070 return(0); 1071} 1072 1073/** 1074 * xmlXPathEqualValues: 1075 * @ctxt: the XPath Parser context 1076 * 1077 * Implement the equal operation on XPath objects content: @arg1 == @arg2 1078 * 1079 * Returns 0 or 1 depending on the results of the test. 1080 */ 1081int 1082xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) { 1083 xmlXPathObjectPtr arg1, arg2; 1084 int ret = 0; 1085 1086 arg1 = valuePop(ctxt); 1087 if (arg1 == NULL) 1088 ERROR0(XPATH_INVALID_OPERAND); 1089 1090 arg2 = valuePop(ctxt); 1091 if (arg2 == NULL) { 1092 xmlXPathFreeObject(arg1); 1093 ERROR0(XPATH_INVALID_OPERAND); 1094 } 1095 1096 if (arg1 == arg2) { 1097#ifdef DEBUG_EXPR 1098 fprintf(xmlXPathDebug, "Equal: by pointer\n"); 1099#endif 1100 return(1); 1101 } 1102 1103 switch (arg1->type) { 1104 case XPATH_UNDEFINED: 1105#ifdef DEBUG_EXPR 1106 fprintf(xmlXPathDebug, "Equal: undefined\n"); 1107#endif 1108 break; 1109 case XPATH_NODESET: 1110 switch (arg2->type) { 1111 case XPATH_UNDEFINED: 1112#ifdef DEBUG_EXPR 1113 fprintf(xmlXPathDebug, "Equal: undefined\n"); 1114#endif 1115 break; 1116 case XPATH_NODESET: 1117 ret = xmlXPathEqualNodeSets(arg1, arg2); 1118 break; 1119 case XPATH_BOOLEAN: 1120 if ((arg1->nodesetval == NULL) || 1121 (arg1->nodesetval->nodeNr == 0)) ret = 0; 1122 else 1123 ret = 1; 1124 ret = (ret == arg2->boolval); 1125 break; 1126 case XPATH_NUMBER: 1127 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval); 1128 break; 1129 case XPATH_STRING: 1130 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval); 1131 break; 1132 } 1133 break; 1134 case XPATH_BOOLEAN: 1135 switch (arg2->type) { 1136 case XPATH_UNDEFINED: 1137#ifdef DEBUG_EXPR 1138 fprintf(xmlXPathDebug, "Equal: undefined\n"); 1139#endif 1140 break; 1141 case XPATH_NODESET: 1142 if ((arg2->nodesetval == NULL) || 1143 (arg2->nodesetval->nodeNr == 0)) ret = 0; 1144 else 1145 ret = 1; 1146 break; 1147 case XPATH_BOOLEAN: 1148#ifdef DEBUG_EXPR 1149 fprintf(xmlXPathDebug, "Equal: %d boolean %d \n", 1150 arg1->boolval, arg2->boolval); 1151#endif 1152 ret = (arg1->boolval == arg2->boolval); 1153 break; 1154 case XPATH_NUMBER: 1155 if (arg2->floatval) ret = 1; 1156 else ret = 0; 1157 ret = (arg1->boolval == ret); 1158 break; 1159 case XPATH_STRING: 1160 if ((arg2->stringval == NULL) || 1161 (arg2->stringval[0] == 0)) ret = 0; 1162 else 1163 ret = 1; 1164 ret = (arg1->boolval == ret); 1165 break; 1166 } 1167 break; 1168 case XPATH_NUMBER: 1169 switch (arg2->type) { 1170 case XPATH_UNDEFINED: 1171#ifdef DEBUG_EXPR 1172 fprintf(xmlXPathDebug, "Equal: undefined\n"); 1173#endif 1174 break; 1175 case XPATH_NODESET: 1176 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval); 1177 break; 1178 case XPATH_BOOLEAN: 1179 if (arg1->floatval) ret = 1; 1180 else ret = 0; 1181 ret = (arg2->boolval == ret); 1182 break; 1183 case XPATH_STRING: 1184 valuePush(ctxt, arg2); 1185 xmlXPathNumberFunction(ctxt, 1); 1186 arg2 = valuePop(ctxt); 1187 /* no break on purpose */ 1188 case XPATH_NUMBER: 1189 ret = (arg1->floatval == arg2->floatval); 1190 break; 1191 } 1192 break; 1193 case XPATH_STRING: 1194 switch (arg2->type) { 1195 case XPATH_UNDEFINED: 1196#ifdef DEBUG_EXPR 1197 fprintf(xmlXPathDebug, "Equal: undefined\n"); 1198#endif 1199 break; 1200 case XPATH_NODESET: 1201 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval); 1202 break; 1203 case XPATH_BOOLEAN: 1204 if ((arg1->stringval == NULL) || 1205 (arg1->stringval[0] == 0)) ret = 0; 1206 else 1207 ret = 1; 1208 ret = (arg2->boolval == ret); 1209 break; 1210 case XPATH_STRING: 1211 ret = !xmlStrcmp(arg1->stringval, arg2->stringval); 1212 break; 1213 case XPATH_NUMBER: 1214 valuePush(ctxt, arg1); 1215 xmlXPathNumberFunction(ctxt, 1); 1216 arg1 = valuePop(ctxt); 1217 ret = (arg1->floatval == arg2->floatval); 1218 break; 1219 } 1220 break; 1221 } 1222 xmlXPathFreeObject(arg1); 1223 xmlXPathFreeObject(arg2); 1224 return(ret); 1225} 1226 1227/** 1228 * xmlXPathCompareValues: 1229 * @ctxt: the XPath Parser context 1230 * @inf: less than (1) or greater than (2) 1231 * @strict: is the comparison strict 1232 * 1233 * Implement the compare operation on XPath objects: 1234 * @arg1 < @arg2 (1, 1, ... 1235 * @arg1 <= @arg2 (1, 0, ... 1236 * @arg1 > @arg2 (0, 1, ... 1237 * @arg1 >= @arg2 (0, 0, ... 1238 * 1239 * When neither object to be compared is a node-set and the operator is 1240 * <=, <, >=, >, then the objects are compared by converted both objects 1241 * to numbers and comparing the numbers according to IEEE 754. The < 1242 * comparison will be true if and only if the first number is less than the 1243 * second number. The <= comparison will be true if and only if the first 1244 * number is less than or equal to the second number. The > comparison 1245 * will be true if and only if the first number is greater than the second 1246 * number. The >= comparison will be true if and only if the first number 1247 * is greater than or equal to the second number. 1248 */ 1249int 1250xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) { 1251 int ret = 0; 1252 xmlXPathObjectPtr arg1, arg2; 1253 1254 arg2 = valuePop(ctxt); 1255 if ((arg2 == NULL) || (arg2->type == XPATH_NODESET)) { 1256 if (arg2 != NULL) 1257 xmlXPathFreeObject(arg2); 1258 ERROR0(XPATH_INVALID_OPERAND); 1259 } 1260 1261 arg1 = valuePop(ctxt); 1262 if ((arg1 == NULL) || (arg1->type == XPATH_NODESET)) { 1263 if (arg1 != NULL) 1264 xmlXPathFreeObject(arg1); 1265 xmlXPathFreeObject(arg2); 1266 ERROR0(XPATH_INVALID_OPERAND); 1267 } 1268 1269 if (arg1->type != XPATH_NUMBER) { 1270 valuePush(ctxt, arg1); 1271 xmlXPathNumberFunction(ctxt, 1); 1272 arg1 = valuePop(ctxt); 1273 } 1274 if (arg1->type != XPATH_NUMBER) { 1275 xmlXPathFreeObject(arg1); 1276 xmlXPathFreeObject(arg2); 1277 ERROR0(XPATH_INVALID_OPERAND); 1278 } 1279 if (arg2->type != XPATH_NUMBER) { 1280 valuePush(ctxt, arg2); 1281 xmlXPathNumberFunction(ctxt, 1); 1282 arg2 = valuePop(ctxt); 1283 } 1284 if (arg2->type != XPATH_NUMBER) { 1285 xmlXPathFreeObject(arg1); 1286 xmlXPathFreeObject(arg2); 1287 ERROR0(XPATH_INVALID_OPERAND); 1288 } 1289 /* 1290 * Add tests for infinity and nan 1291 * => feedback on 3.4 for Inf and NaN 1292 */ 1293 if (inf && strict) 1294 ret = (arg1->floatval < arg2->floatval); 1295 else if (inf && !strict) 1296 ret = (arg1->floatval <= arg2->floatval); 1297 else if (!inf && strict) 1298 ret = (arg1->floatval > arg2->floatval); 1299 else if (!inf && !strict) 1300 ret = (arg1->floatval >= arg2->floatval); 1301 xmlXPathFreeObject(arg1); 1302 xmlXPathFreeObject(arg2); 1303 return(ret); 1304} 1305 1306/** 1307 * xmlXPathValueFlipSign: 1308 * @ctxt: the XPath Parser context 1309 * 1310 * Implement the unary - operation on an XPath object 1311 * The numeric operators convert their operands to numbers as if 1312 * by calling the number function. 1313 */ 1314void 1315xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) { 1316 xmlXPathObjectPtr arg; 1317 1318 POP_FLOAT 1319 arg->floatval = -arg->floatval; 1320 valuePush(ctxt, arg); 1321} 1322 1323/** 1324 * xmlXPathAddValues: 1325 * @ctxt: the XPath Parser context 1326 * 1327 * Implement the add operation on XPath objects: 1328 * The numeric operators convert their operands to numbers as if 1329 * by calling the number function. 1330 */ 1331void 1332xmlXPathAddValues(xmlXPathParserContextPtr ctxt) { 1333 xmlXPathObjectPtr arg; 1334 double val; 1335 1336 POP_FLOAT 1337 val = arg->floatval; 1338 xmlXPathFreeObject(arg); 1339 1340 POP_FLOAT 1341 arg->floatval += val; 1342 valuePush(ctxt, arg); 1343} 1344 1345/** 1346 * xmlXPathSubValues: 1347 * @ctxt: the XPath Parser context 1348 * 1349 * Implement the substraction operation on XPath objects: 1350 * The numeric operators convert their operands to numbers as if 1351 * by calling the number function. 1352 */ 1353void 1354xmlXPathSubValues(xmlXPathParserContextPtr ctxt) { 1355 xmlXPathObjectPtr arg; 1356 double val; 1357 1358 POP_FLOAT 1359 val = arg->floatval; 1360 xmlXPathFreeObject(arg); 1361 1362 POP_FLOAT 1363 arg->floatval -= val; 1364 valuePush(ctxt, arg); 1365} 1366 1367/** 1368 * xmlXPathMultValues: 1369 * @ctxt: the XPath Parser context 1370 * 1371 * Implement the multiply operation on XPath objects: 1372 * The numeric operators convert their operands to numbers as if 1373 * by calling the number function. 1374 */ 1375void 1376xmlXPathMultValues(xmlXPathParserContextPtr ctxt) { 1377 xmlXPathObjectPtr arg; 1378 double val; 1379 1380 POP_FLOAT 1381 val = arg->floatval; 1382 xmlXPathFreeObject(arg); 1383 1384 POP_FLOAT 1385 arg->floatval *= val; 1386 valuePush(ctxt, arg); 1387} 1388 1389/** 1390 * xmlXPathDivValues: 1391 * @ctxt: the XPath Parser context 1392 * 1393 * Implement the div operation on XPath objects: 1394 * The numeric operators convert their operands to numbers as if 1395 * by calling the number function. 1396 */ 1397void 1398xmlXPathDivValues(xmlXPathParserContextPtr ctxt) { 1399 xmlXPathObjectPtr arg; 1400 double val; 1401 1402 POP_FLOAT 1403 val = arg->floatval; 1404 xmlXPathFreeObject(arg); 1405 1406 POP_FLOAT 1407 arg->floatval /= val; 1408 valuePush(ctxt, arg); 1409} 1410 1411/** 1412 * xmlXPathModValues: 1413 * @ctxt: the XPath Parser context 1414 * 1415 * Implement the div operation on XPath objects: @arg1 / @arg2 1416 * The numeric operators convert their operands to numbers as if 1417 * by calling the number function. 1418 */ 1419void 1420xmlXPathModValues(xmlXPathParserContextPtr ctxt) { 1421 xmlXPathObjectPtr arg; 1422 double val; 1423 1424 POP_FLOAT 1425 val = arg->floatval; 1426 xmlXPathFreeObject(arg); 1427 1428 POP_FLOAT 1429 arg->floatval /= val; 1430 valuePush(ctxt, arg); 1431} 1432 1433/************************************************************************ 1434 * * 1435 * The traversal functions * 1436 * * 1437 ************************************************************************/ 1438 1439#define AXIS_ANCESTOR 1 1440#define AXIS_ANCESTOR_OR_SELF 2 1441#define AXIS_ATTRIBUTE 3 1442#define AXIS_CHILD 4 1443#define AXIS_DESCENDANT 5 1444#define AXIS_DESCENDANT_OR_SELF 6 1445#define AXIS_FOLLOWING 7 1446#define AXIS_FOLLOWING_SIBLING 8 1447#define AXIS_NAMESPACE 9 1448#define AXIS_PARENT 10 1449#define AXIS_PRECEDING 11 1450#define AXIS_PRECEDING_SIBLING 12 1451#define AXIS_SELF 13 1452 1453/* 1454 * A traversal function enumerates nodes along an axis. 1455 * Initially it must be called with NULL, and it indicates 1456 * termination on the axis by returning NULL. 1457 */ 1458typedef xmlNodePtr (*xmlXPathTraversalFunction) 1459 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur); 1460 1461/** 1462 * mlXPathNextSelf: 1463 * @ctxt: the XPath Parser context 1464 * @cur: the current node in the traversal 1465 * 1466 * Traversal function for the "self" direction 1467 * he self axis contains just the context node itself 1468 * 1469 * Returns the next element following that axis 1470 */ 1471xmlNodePtr 1472xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1473 if (cur == NULL) 1474 return(ctxt->context->node); 1475 return(NULL); 1476} 1477 1478/** 1479 * mlXPathNextChild: 1480 * @ctxt: the XPath Parser context 1481 * @cur: the current node in the traversal 1482 * 1483 * Traversal function for the "child" direction 1484 * The child axis contains the children of the context node in document order. 1485 * 1486 * Returns the next element following that axis 1487 */ 1488xmlNodePtr 1489xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1490 if (cur == NULL) { 1491 if (ctxt->context->node == NULL) return(NULL); 1492 switch (ctxt->context->node->type) { 1493 case XML_ELEMENT_NODE: 1494 case XML_TEXT_NODE: 1495 case XML_CDATA_SECTION_NODE: 1496 case XML_ENTITY_REF_NODE: 1497 case XML_ENTITY_NODE: 1498 case XML_PI_NODE: 1499 case XML_COMMENT_NODE: 1500 case XML_NOTATION_NODE: 1501 return(ctxt->context->node->childs); 1502 case XML_ATTRIBUTE_NODE: 1503 return(NULL); 1504 case XML_DOCUMENT_NODE: 1505 case XML_DOCUMENT_TYPE_NODE: 1506 case XML_DOCUMENT_FRAG_NODE: 1507 case XML_HTML_DOCUMENT_NODE: 1508 return(((xmlDocPtr) ctxt->context->node)->root); 1509 } 1510 return(NULL); 1511 } 1512 if ((cur->type == XML_DOCUMENT_NODE) || 1513 (cur->type == XML_HTML_DOCUMENT_NODE)) 1514 return(NULL); 1515 return(cur->next); 1516} 1517 1518/** 1519 * mlXPathNextDescendant: 1520 * @ctxt: the XPath Parser context 1521 * @cur: the current node in the traversal 1522 * 1523 * Traversal function for the "descendant" direction 1524 * the descendant axis contains the descendants of the context node in document 1525 * order; a descendant is a child or a child of a child and so on. 1526 * 1527 * Returns the next element following that axis 1528 */ 1529xmlNodePtr 1530xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1531 if (cur == NULL) { 1532 if (ctxt->context->node == NULL) 1533 return(NULL); 1534 if (ctxt->context->node->type == XML_ATTRIBUTE_NODE) 1535 return(NULL); 1536 1537 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc) 1538 return(ctxt->context->doc->root); 1539 return(ctxt->context->node->childs); 1540 } 1541 1542 if (cur->childs != NULL) return(cur->childs); 1543 if (cur->next != NULL) return(cur->next); 1544 1545 do { 1546 cur = cur->parent; 1547 if (cur == NULL) return(NULL); 1548 if (cur == ctxt->context->node) return(NULL); 1549 if (cur->next != NULL) { 1550 cur = cur->next; 1551 return(cur); 1552 } 1553 } while (cur != NULL); 1554 return(cur); 1555} 1556 1557/** 1558 * mlXPathNextDescendantOrSelf: 1559 * @ctxt: the XPath Parser context 1560 * @cur: the current node in the traversal 1561 * 1562 * Traversal function for the "descendant-or-self" direction 1563 * the descendant-or-self axis contains the context node and the descendants 1564 * of the context node in document order; thus the context node is the first 1565 * node on the axis, and the first child of the context node is the second node 1566 * on the axis 1567 * 1568 * Returns the next element following that axis 1569 */ 1570xmlNodePtr 1571xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1572 if (cur == NULL) { 1573 if (ctxt->context->node == NULL) 1574 return(NULL); 1575 if (ctxt->context->node->type == XML_ATTRIBUTE_NODE) 1576 return(NULL); 1577 return(ctxt->context->node); 1578 } 1579 1580 return(xmlXPathNextDescendant(ctxt, cur)); 1581} 1582 1583/** 1584 * xmlXPathNextParent: 1585 * @ctxt: the XPath Parser context 1586 * @cur: the current node in the traversal 1587 * 1588 * Traversal function for the "parent" direction 1589 * The parent axis contains the parent of the context node, if there is one. 1590 * 1591 * Returns the next element following that axis 1592 */ 1593xmlNodePtr 1594xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1595 /* 1596 * the parent of an attribute or namespace node is the element 1597 * to which the attribute or namespace node is attached 1598 * Namespace handling !!! 1599 */ 1600 if (cur == NULL) { 1601 if (ctxt->context->node == NULL) return(NULL); 1602 switch (ctxt->context->node->type) { 1603 case XML_ELEMENT_NODE: 1604 case XML_TEXT_NODE: 1605 case XML_CDATA_SECTION_NODE: 1606 case XML_ENTITY_REF_NODE: 1607 case XML_ENTITY_NODE: 1608 case XML_PI_NODE: 1609 case XML_COMMENT_NODE: 1610 case XML_NOTATION_NODE: 1611 if (ctxt->context->node->parent == NULL) 1612 return((xmlNodePtr) ctxt->context->doc); 1613 return(ctxt->context->node->parent); 1614 case XML_ATTRIBUTE_NODE: { 1615 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node; 1616 1617 return(att->node); 1618 } 1619 case XML_DOCUMENT_NODE: 1620 case XML_DOCUMENT_TYPE_NODE: 1621 case XML_DOCUMENT_FRAG_NODE: 1622 case XML_HTML_DOCUMENT_NODE: 1623 return(NULL); 1624 } 1625 } 1626 return(NULL); 1627} 1628 1629/** 1630 * xmlXPathNextAncestor: 1631 * @ctxt: the XPath Parser context 1632 * @cur: the current node in the traversal 1633 * 1634 * Traversal function for the "ancestor" direction 1635 * the ancestor axis contains the ancestors of the context node; the ancestors 1636 * of the context node consist of the parent of context node and the parent's 1637 * parent and so on; the nodes are ordered in reverse document order; thus the 1638 * parent is the first node on the axis, and the parent's parent is the second 1639 * node on the axis 1640 * 1641 * Returns the next element following that axis 1642 */ 1643xmlNodePtr 1644xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1645 /* 1646 * the parent of an attribute or namespace node is the element 1647 * to which the attribute or namespace node is attached 1648 * !!!!!!!!!!!!! 1649 */ 1650 if (cur == NULL) { 1651 if (ctxt->context->node == NULL) return(NULL); 1652 switch (ctxt->context->node->type) { 1653 case XML_ELEMENT_NODE: 1654 case XML_TEXT_NODE: 1655 case XML_CDATA_SECTION_NODE: 1656 case XML_ENTITY_REF_NODE: 1657 case XML_ENTITY_NODE: 1658 case XML_PI_NODE: 1659 case XML_COMMENT_NODE: 1660 case XML_NOTATION_NODE: 1661 if (ctxt->context->node->parent == NULL) 1662 return((xmlNodePtr) ctxt->context->doc); 1663 return(ctxt->context->node->parent); 1664 case XML_ATTRIBUTE_NODE: { 1665 xmlAttrPtr cur = (xmlAttrPtr) ctxt->context->node; 1666 1667 return(cur->node); 1668 } 1669 case XML_DOCUMENT_NODE: 1670 case XML_DOCUMENT_TYPE_NODE: 1671 case XML_DOCUMENT_FRAG_NODE: 1672 case XML_HTML_DOCUMENT_NODE: 1673 return(NULL); 1674 } 1675 return(NULL); 1676 } 1677 if (cur == ctxt->context->doc->root) 1678 return((xmlNodePtr) ctxt->context->doc); 1679 if (cur == (xmlNodePtr) ctxt->context->doc) 1680 return(NULL); 1681 switch (cur->type) { 1682 case XML_ELEMENT_NODE: 1683 case XML_TEXT_NODE: 1684 case XML_CDATA_SECTION_NODE: 1685 case XML_ENTITY_REF_NODE: 1686 case XML_ENTITY_NODE: 1687 case XML_PI_NODE: 1688 case XML_COMMENT_NODE: 1689 case XML_NOTATION_NODE: 1690 return(cur->parent); 1691 case XML_ATTRIBUTE_NODE: { 1692 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node; 1693 1694 return(att->node); 1695 } 1696 case XML_DOCUMENT_NODE: 1697 case XML_DOCUMENT_TYPE_NODE: 1698 case XML_DOCUMENT_FRAG_NODE: 1699 case XML_HTML_DOCUMENT_NODE: 1700 return(NULL); 1701 } 1702 return(NULL); 1703} 1704 1705/** 1706 * xmlXPathNextAncestorOrSelf: 1707 * @ctxt: the XPath Parser context 1708 * @cur: the current node in the traversal 1709 * 1710 * Traversal function for the "ancestor-or-self" direction 1711 * he ancestor-or-self axis contains the context node and ancestors of 1712 * the context node in reverse document order; thus the context node is 1713 * the first node on the axis, and the context node's parent the second; 1714 * parent here is defined the same as with the parent axis. 1715 * 1716 * Returns the next element following that axis 1717 */ 1718xmlNodePtr 1719xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1720 if (cur == NULL) 1721 return(ctxt->context->node); 1722 return(xmlXPathNextAncestor(ctxt, cur)); 1723} 1724 1725/** 1726 * xmlXPathNextFollowingSibling: 1727 * @ctxt: the XPath Parser context 1728 * @cur: the current node in the traversal 1729 * 1730 * Traversal function for the "following-sibling" direction 1731 * The following-sibling axis contains the following siblings of the context 1732 * node in document order. 1733 * 1734 * Returns the next element following that axis 1735 */ 1736xmlNodePtr 1737xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1738 if (cur == (xmlNodePtr) ctxt->context->doc) 1739 return(NULL); 1740 if (cur == NULL) 1741 return(ctxt->context->node->next); 1742 return(cur->next); 1743} 1744 1745/** 1746 * xmlXPathNextPrecedingSibling: 1747 * @ctxt: the XPath Parser context 1748 * @cur: the current node in the traversal 1749 * 1750 * Traversal function for the "preceding-sibling" direction 1751 * The preceding-sibling axis contains the preceding siblings of the context 1752 * node in reverse document order; the first preceding sibling is first on the 1753 * axis; the sibling preceding that node is the second on the axis and so on. 1754 * 1755 * Returns the next element following that axis 1756 */ 1757xmlNodePtr 1758xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1759 if (cur == (xmlNodePtr) ctxt->context->doc) 1760 return(NULL); 1761 if (cur == NULL) 1762 return(ctxt->context->node->prev); 1763 return(cur->prev); 1764} 1765 1766/** 1767 * xmlXPathNextFollowing: 1768 * @ctxt: the XPath Parser context 1769 * @cur: the current node in the traversal 1770 * 1771 * Traversal function for the "following" direction 1772 * The following axis contains all nodes in the same document as the context 1773 * node that are after the context node in document order, excluding any 1774 * descendants and excluding attribute nodes and namespace nodes; the nodes 1775 * are ordered in document order 1776 * 1777 * Returns the next element following that axis 1778 */ 1779xmlNodePtr 1780xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1781 if (cur == (xmlNodePtr) ctxt->context->doc) 1782 return(NULL); 1783 if (cur == NULL) 1784 return(ctxt->context->node->next);; /* !!!!!!!!! */ 1785 if (cur->childs != NULL) return(cur->childs); 1786 if (cur->next != NULL) return(cur->next); 1787 1788 do { 1789 cur = cur->parent; 1790 if (cur == NULL) return(NULL); 1791 if (cur == ctxt->context->doc->root) return(NULL); 1792 if (cur->next != NULL) { 1793 cur = cur->next; 1794 return(cur); 1795 } 1796 } while (cur != NULL); 1797 return(cur); 1798} 1799 1800/** 1801 * xmlXPathNextPreceding: 1802 * @ctxt: the XPath Parser context 1803 * @cur: the current node in the traversal 1804 * 1805 * Traversal function for the "preceding" direction 1806 * the preceding axis contains all nodes in the same document as the context 1807 * node that are before the context node in document order, excluding any 1808 * ancestors and excluding attribute nodes and namespace nodes; the nodes are 1809 * ordered in reverse document order 1810 * 1811 * Returns the next element following that axis 1812 */ 1813xmlNodePtr 1814xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { 1815 if (cur == (xmlNodePtr) ctxt->context->doc) 1816 return(NULL); 1817 if (cur == NULL) 1818 return(ctxt->context->node->prev); /* !!!!!!!!! */ 1819 if (cur->last != NULL) return(cur->last); 1820 if (cur->prev != NULL) return(cur->prev); 1821 1822 do { 1823 cur = cur->parent; 1824 if (cur == NULL) return(NULL); 1825 if (cur == ctxt->context->doc->root) return(NULL); 1826 if (cur->prev != NULL) { 1827 cur = cur->prev; 1828 return(cur); 1829 } 1830 } while (cur != NULL); 1831 return(cur); 1832} 1833 1834/** 1835 * xmlXPathNextNamespace: 1836 * @ctxt: the XPath Parser context 1837 * @cur: the current attribute in the traversal 1838 * 1839 * Traversal function for the "namespace" direction 1840 * the namespace axis contains the namespace nodes of the context node; 1841 * the order of nodes on this axis is implementation-defined; the axis will 1842 * be empty unless the context node is an element 1843 * 1844 * Returns the next element following that axis 1845 */ 1846xmlNsPtr 1847xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) { 1848 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) { 1849 if (ctxt->context->namespaces != NULL) 1850 xmlFree(ctxt->context->namespaces); 1851 ctxt->context->namespaces = 1852 xmlGetNsList(ctxt->context->doc, ctxt->context->node); 1853 if (ctxt->context->namespaces == NULL) return(NULL); 1854 ctxt->context->nsNr = 0; 1855 } 1856 return(ctxt->context->namespaces[ctxt->context->nsNr++]); 1857} 1858 1859/** 1860 * xmlXPathNextAttribute: 1861 * @ctxt: the XPath Parser context 1862 * @cur: the current attribute in the traversal 1863 * 1864 * Traversal function for the "attribute" direction 1865 * TODO: support DTD inherited default attributes 1866 * 1867 * Returns the next element following that axis 1868 */ 1869xmlAttrPtr 1870xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) { 1871 if (cur == NULL) { 1872 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc) 1873 return(NULL); 1874 return(ctxt->context->node->properties); 1875 } 1876 return(cur->next); 1877} 1878 1879/************************************************************************ 1880 * * 1881 * NodeTest Functions * 1882 * * 1883 ************************************************************************/ 1884 1885#define NODE_TEST_NONE 0 1886#define NODE_TEST_TYPE 1 1887#define NODE_TEST_PI 2 1888#define NODE_TEST_ALL 3 1889#define NODE_TEST_NS 4 1890#define NODE_TEST_NAME 5 1891 1892#define NODE_TYPE_COMMENT 50 1893#define NODE_TYPE_TEXT 51 1894#define NODE_TYPE_PI 52 1895#define NODE_TYPE_NODE 53 1896 1897#define IS_FUNCTION 200 1898 1899/** 1900 * xmlXPathNodeCollectAndTest: 1901 * @ctxt: the XPath Parser context 1902 * @cur: the current node to test 1903 * 1904 * This is the function implementing a step: based on the current list 1905 * of nodes, it builds up a new list, looking at all nodes under that 1906 * axis and selecting them. 1907 * 1908 * Returns the new NodeSet resulting from the search. 1909 */ 1910xmlNodeSetPtr 1911xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, int axis, 1912 int test, int type, const xmlChar *prefix, const xmlChar *name) { 1913#ifdef DEBUG_STEP 1914 int n = 0, t = 0; 1915#endif 1916 int i; 1917 xmlNodeSetPtr ret; 1918 xmlXPathTraversalFunction next = NULL; 1919 xmlNodePtr cur = NULL; 1920 1921 if (ctxt->context->nodelist == NULL) { 1922 if (ctxt->context->node == NULL) { 1923 fprintf(xmlXPathDebug, 1924 "xmlXPathNodeCollectAndTest %s:%d : nodelist and node are NULL\n", 1925 __FILE__, __LINE__); 1926 return(NULL); 1927 } 1928 STRANGE 1929 return(NULL); 1930 } 1931#ifdef DEBUG_STEP 1932 fprintf(xmlXPathDebug, "new step : "); 1933#endif 1934 switch (axis) { 1935 case AXIS_ANCESTOR: 1936#ifdef DEBUG_STEP 1937 fprintf(xmlXPathDebug, "axis 'ancestors' "); 1938#endif 1939 next = xmlXPathNextAncestor; break; 1940 case AXIS_ANCESTOR_OR_SELF: 1941#ifdef DEBUG_STEP 1942 fprintf(xmlXPathDebug, "axis 'ancestors-or-self' "); 1943#endif 1944 next = xmlXPathNextAncestorOrSelf; break; 1945 case AXIS_ATTRIBUTE: 1946#ifdef DEBUG_STEP 1947 fprintf(xmlXPathDebug, "axis 'attributes' "); 1948#endif 1949 next = (xmlXPathTraversalFunction) xmlXPathNextAttribute; break; 1950 break; 1951 case AXIS_CHILD: 1952#ifdef DEBUG_STEP 1953 fprintf(xmlXPathDebug, "axis 'child' "); 1954#endif 1955 next = xmlXPathNextChild; break; 1956 case AXIS_DESCENDANT: 1957#ifdef DEBUG_STEP 1958 fprintf(xmlXPathDebug, "axis 'descendant' "); 1959#endif 1960 next = xmlXPathNextDescendant; break; 1961 case AXIS_DESCENDANT_OR_SELF: 1962#ifdef DEBUG_STEP 1963 fprintf(xmlXPathDebug, "axis 'descendant-or-self' "); 1964#endif 1965 next = xmlXPathNextDescendantOrSelf; break; 1966 case AXIS_FOLLOWING: 1967#ifdef DEBUG_STEP 1968 fprintf(xmlXPathDebug, "axis 'following' "); 1969#endif 1970 next = xmlXPathNextFollowing; break; 1971 case AXIS_FOLLOWING_SIBLING: 1972#ifdef DEBUG_STEP 1973 fprintf(xmlXPathDebug, "axis 'following-siblings' "); 1974#endif 1975 next = xmlXPathNextFollowingSibling; break; 1976 case AXIS_NAMESPACE: 1977#ifdef DEBUG_STEP 1978 fprintf(xmlXPathDebug, "axis 'namespace' "); 1979#endif 1980 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break; 1981 break; 1982 case AXIS_PARENT: 1983#ifdef DEBUG_STEP 1984 fprintf(xmlXPathDebug, "axis 'parent' "); 1985#endif 1986 next = xmlXPathNextParent; break; 1987 case AXIS_PRECEDING: 1988#ifdef DEBUG_STEP 1989 fprintf(xmlXPathDebug, "axis 'preceding' "); 1990#endif 1991 next = xmlXPathNextPreceding; break; 1992 case AXIS_PRECEDING_SIBLING: 1993#ifdef DEBUG_STEP 1994 fprintf(xmlXPathDebug, "axis 'preceding-sibling' "); 1995#endif 1996 next = xmlXPathNextPrecedingSibling; break; 1997 case AXIS_SELF: 1998#ifdef DEBUG_STEP 1999 fprintf(xmlXPathDebug, "axis 'self' "); 2000#endif 2001 next = xmlXPathNextSelf; break; 2002 } 2003 if (next == NULL) return(NULL); 2004 ret = xmlXPathNodeSetCreate(NULL); 2005#ifdef DEBUG_STEP 2006 fprintf(xmlXPathDebug, " context contains %d nodes\n", 2007 ctxt->context->nodelist->nodeNr); 2008 switch (test) { 2009 case NODE_TEST_NONE: 2010 fprintf(xmlXPathDebug, " searching for none !!!\n"); 2011 break; 2012 case NODE_TEST_TYPE: 2013 fprintf(xmlXPathDebug, " searching for type %d\n", type); 2014 break; 2015 case NODE_TEST_PI: 2016 fprintf(xmlXPathDebug, " searching for PI !!!\n"); 2017 break; 2018 case NODE_TEST_ALL: 2019 fprintf(xmlXPathDebug, " searching for *\n"); 2020 break; 2021 case NODE_TEST_NS: 2022 fprintf(xmlXPathDebug, " searching for namespace %s\n", 2023 prefix); 2024 break; 2025 case NODE_TEST_NAME: 2026 fprintf(xmlXPathDebug, " searching for name %s\n", name); 2027 if (prefix != NULL) 2028 fprintf(xmlXPathDebug, " with namespace %s\n", 2029 prefix); 2030 break; 2031 } 2032 fprintf(xmlXPathDebug, "Testing : "); 2033#endif 2034 for (i = 0;i < ctxt->context->nodelist->nodeNr; i++) { 2035 ctxt->context->node = ctxt->context->nodelist->nodeTab[i]; 2036 2037 cur = NULL; 2038 do { 2039 cur = next(ctxt, cur); 2040 if (cur == NULL) break; 2041#ifdef DEBUG_STEP 2042 t++; 2043 fprintf(xmlXPathDebug, " %s", cur->name); 2044#endif 2045 switch (test) { 2046 case NODE_TEST_NONE: 2047 STRANGE 2048 return(NULL); 2049 case NODE_TEST_TYPE: 2050 if ((cur->type == type) || 2051 ((type == XML_ELEMENT_NODE) && 2052 ((cur->type == XML_DOCUMENT_NODE) || 2053 (cur->type == XML_HTML_DOCUMENT_NODE)))) { 2054#ifdef DEBUG_STEP 2055 n++; 2056#endif 2057 xmlXPathNodeSetAdd(ret, cur); 2058 } 2059 break; 2060 case NODE_TEST_PI: 2061 if (cur->type == XML_PI_NODE) { 2062 if ((name != NULL) && 2063 (xmlStrcmp(name, cur->name))) 2064 break; 2065#ifdef DEBUG_STEP 2066 n++; 2067#endif 2068 xmlXPathNodeSetAdd(ret, cur); 2069 } 2070 break; 2071 case NODE_TEST_ALL: 2072 if ((cur->type == XML_ELEMENT_NODE) || 2073 (cur->type == XML_ATTRIBUTE_NODE)) { 2074 /* !!! || (cur->type == XML_TEXT_NODE)) { */ 2075#ifdef DEBUG_STEP 2076 n++; 2077#endif 2078 xmlXPathNodeSetAdd(ret, cur); 2079 } 2080 break; 2081 case NODE_TEST_NS: { 2082 TODO /* namespace search */ 2083 break; 2084 } 2085 case NODE_TEST_NAME: 2086 switch (cur->type) { 2087 case XML_ELEMENT_NODE: 2088 if (!xmlStrcmp(name, cur->name) && 2089 (((prefix == NULL) || 2090 ((cur->ns != NULL) && 2091 (!xmlStrcmp(prefix, cur->ns->href)))))) { 2092#ifdef DEBUG_STEP 2093 n++; 2094#endif 2095 xmlXPathNodeSetAdd(ret, cur); 2096 } 2097 break; 2098 case XML_ATTRIBUTE_NODE: { 2099 xmlAttrPtr attr = (xmlAttrPtr) cur; 2100 if (!xmlStrcmp(name, attr->name)) { 2101#ifdef DEBUG_STEP 2102 n++; 2103#endif 2104 xmlXPathNodeSetAdd(ret, cur); 2105 } 2106 break; 2107 } 2108 default: 2109 break; 2110 } 2111 break; 2112 2113 } 2114 } while (cur != NULL); 2115 } 2116#ifdef DEBUG_STEP 2117 fprintf(xmlXPathDebug, 2118 "\nExamined %d nodes, found %d nodes at that step\n", t, n); 2119#endif 2120 return(ret); 2121} 2122 2123 2124/************************************************************************ 2125 * * 2126 * Implicit tree core function library * 2127 * * 2128 ************************************************************************/ 2129 2130/** 2131 * xmlXPathRoot: 2132 * @ctxt: the XPath Parser context 2133 * 2134 * Initialize the context to the root of the document 2135 */ 2136void 2137xmlXPathRoot(xmlXPathParserContextPtr ctxt) { 2138 if (ctxt->context->nodelist != NULL) 2139 xmlXPathFreeNodeSet(ctxt->context->nodelist); 2140 ctxt->context->node = (xmlNodePtr) ctxt->context->doc; 2141 ctxt->context->nodelist = xmlXPathNodeSetCreate(ctxt->context->node); 2142} 2143 2144/************************************************************************ 2145 * * 2146 * The explicit core function library * 2147 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib * 2148 * * 2149 ************************************************************************/ 2150 2151 2152/** 2153 * xmlXPathLastFunction: 2154 * @ctxt: the XPath Parser context 2155 * 2156 * Implement the last() XPath function 2157 * The last function returns the number of nodes in the context node list. 2158 */ 2159void 2160xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2161 CHECK_ARITY(0); 2162 if ((ctxt->context->nodelist == NULL) || 2163 (ctxt->context->node == NULL) || 2164 (ctxt->context->nodelist->nodeNr == 0)) { 2165 valuePush(ctxt, xmlXPathNewFloat((double) 0)); 2166 } else { 2167 valuePush(ctxt, 2168 xmlXPathNewFloat((double) ctxt->context->nodelist->nodeNr)); 2169 } 2170} 2171 2172/** 2173 * xmlXPathPositionFunction: 2174 * @ctxt: the XPath Parser context 2175 * 2176 * Implement the position() XPath function 2177 * The position function returns the position of the context node in the 2178 * context node list. The first position is 1, and so the last positionr 2179 * will be equal to last(). 2180 */ 2181void 2182xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2183 int i; 2184 2185 CHECK_ARITY(0); 2186 if ((ctxt->context->nodelist == NULL) || 2187 (ctxt->context->node == NULL) || 2188 (ctxt->context->nodelist->nodeNr == 0)) { 2189 valuePush(ctxt, xmlXPathNewFloat((double) 0)); 2190 } 2191 for (i = 0; i < ctxt->context->nodelist->nodeNr;i++) { 2192 if (ctxt->context->node == ctxt->context->nodelist->nodeTab[i]) { 2193 valuePush(ctxt, xmlXPathNewFloat((double) i + 1)); 2194 return; 2195 } 2196 } 2197 valuePush(ctxt, xmlXPathNewFloat((double) 0)); 2198} 2199 2200/** 2201 * xmlXPathCountFunction: 2202 * @ctxt: the XPath Parser context 2203 * 2204 * Implement the count() XPath function 2205 */ 2206void 2207xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2208 xmlXPathObjectPtr cur; 2209 2210 CHECK_ARITY(1); 2211 CHECK_TYPE(XPATH_NODESET); 2212 cur = valuePop(ctxt); 2213 2214 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr)); 2215 xmlXPathFreeObject(cur); 2216} 2217 2218/** 2219 * xmlXPathIdFunction: 2220 * @ctxt: the XPath Parser context 2221 * 2222 * Implement the id() XPath function 2223 * The id function selects elements by their unique ID 2224 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set, 2225 * then the result is the union of the result of applying id to the 2226 * string value of each of the nodes in the argument node-set. When the 2227 * argument to id is of any other type, the argument is converted to a 2228 * string as if by a call to the string function; the string is split 2229 * into a whitespace-separated list of tokens (whitespace is any sequence 2230 * of characters matching the production S); the result is a node-set 2231 * containing the elements in the same document as the context node that 2232 * have a unique ID equal to any of the tokens in the list. 2233 */ 2234void 2235xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2236 const xmlChar *tokens; 2237 const xmlChar *cur; 2238 xmlChar *ID; 2239 xmlAttrPtr attr; 2240 xmlNodePtr elem = NULL; 2241 xmlXPathObjectPtr ret, obj; 2242 2243 CHECK_ARITY(1); 2244 obj = valuePop(ctxt); 2245 if (obj == NULL) ERROR(XPATH_INVALID_OPERAND); 2246 if (obj->type == XPATH_NODESET) { 2247 TODO /* ID function in case of NodeSet */ 2248 } 2249 if (obj->type != XPATH_STRING) { 2250 valuePush(ctxt, obj); 2251 xmlXPathStringFunction(ctxt, 1); 2252 obj = valuePop(ctxt); 2253 if (obj->type != XPATH_STRING) { 2254 xmlXPathFreeObject(obj); 2255 return; 2256 } 2257 } 2258 tokens = obj->stringval; 2259 2260 ret = xmlXPathNewNodeSet(NULL); 2261 valuePush(ctxt, ret); 2262 if (tokens == NULL) { 2263 xmlXPathFreeObject(obj); 2264 return; 2265 } 2266 2267 cur = tokens; 2268 2269 while (IS_BLANK(*cur)) cur++; 2270 while (*cur != 0) { 2271 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) || 2272 (*cur == '.') || (*cur == '-') || 2273 (*cur == '_') || (*cur == ':') || 2274 (IS_COMBINING(*cur)) || 2275 (IS_EXTENDER(*cur))) 2276 cur++; 2277 2278 if ((!IS_BLANK(*cur)) && (*cur != 0)) break; 2279 2280 ID = xmlStrndup(tokens, cur - tokens); 2281 attr = xmlGetID(ctxt->context->doc, ID); 2282 if (attr != NULL) { 2283 elem = attr->node; 2284 xmlXPathNodeSetAdd(ret->nodesetval, elem); 2285 } 2286 if (ID != NULL) 2287 xmlFree(ID); 2288 2289 while (IS_BLANK(*cur)) cur++; 2290 tokens = cur; 2291 } 2292 xmlXPathFreeObject(obj); 2293 return; 2294} 2295 2296/** 2297 * xmlXPathLocalPartFunction: 2298 * @ctxt: the XPath Parser context 2299 * 2300 * Implement the local-part() XPath function 2301 * The local-part function returns a string containing the local part 2302 * of the name of the node in the argument node-set that is first in 2303 * document order. If the node-set is empty or the first node has no 2304 * name, an empty string is returned. If the argument is omitted it 2305 * defaults to the context node. 2306 */ 2307void 2308xmlXPathLocalPartFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2309 xmlXPathObjectPtr cur; 2310 2311 CHECK_ARITY(1); 2312 CHECK_TYPE(XPATH_NODESET); 2313 cur = valuePop(ctxt); 2314 2315 if (cur->nodesetval->nodeNr == 0) { 2316 valuePush(ctxt, xmlXPathNewCString("")); 2317 } else { 2318 int i = 0; /* Should be first in document order !!!!! */ 2319 valuePush(ctxt, xmlXPathNewString(cur->nodesetval->nodeTab[i]->name)); 2320 } 2321 xmlXPathFreeObject(cur); 2322} 2323 2324/** 2325 * xmlXPathNamespaceFunction: 2326 * @ctxt: the XPath Parser context 2327 * 2328 * Implement the namespace() XPath function 2329 * The namespace function returns a string containing the namespace URI 2330 * of the expanded name of the node in the argument node-set that is 2331 * first in document order. If the node-set is empty, the first node has 2332 * no name, or the expanded name has no namespace URI, an empty string 2333 * is returned. If the argument is omitted it defaults to the context node. 2334 */ 2335void 2336xmlXPathNamespaceFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2337 xmlXPathObjectPtr cur; 2338 2339 if (nargs == 0) { 2340 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); 2341 nargs = 1; 2342 } 2343 CHECK_ARITY(1); 2344 CHECK_TYPE(XPATH_NODESET); 2345 cur = valuePop(ctxt); 2346 2347 if (cur->nodesetval->nodeNr == 0) { 2348 valuePush(ctxt, xmlXPathNewCString("")); 2349 } else { 2350 int i = 0; /* Should be first in document order !!!!! */ 2351 2352 if (cur->nodesetval->nodeTab[i]->ns == NULL) 2353 valuePush(ctxt, xmlXPathNewCString("")); 2354 else 2355 valuePush(ctxt, xmlXPathNewString( 2356 cur->nodesetval->nodeTab[i]->ns->href)); 2357 } 2358 xmlXPathFreeObject(cur); 2359} 2360 2361/** 2362 * xmlXPathNameFunction: 2363 * @ctxt: the XPath Parser context 2364 * 2365 * Implement the name() XPath function 2366 * The name function returns a string containing a QName representing 2367 * the name of the node in the argument node-set that is first in documenti 2368 * order. The QName must represent the name with respect to the namespace 2369 * declarations in effect on the node whose name is being represented. 2370 * Typically, this will be the form in which the name occurred in the XML 2371 * source. This need not be the case if there are namespace declarations 2372 * in effect on the node that associate multiple prefixes with the same 2373 * namespace. However, an implementation may include information about 2374 * the original prefix in its representation of nodes; in this case, an 2375 * implementation can ensure that the returned string is always the same 2376 * as the QName used in the XML source. If the argument it omitted it 2377 * defaults to the context node. 2378 * Libxml keep the original prefix so the "real qualified name" used is 2379 * returned. 2380 */ 2381void 2382xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2383 xmlXPathObjectPtr cur; 2384 2385 CHECK_ARITY(1); 2386 CHECK_TYPE(XPATH_NODESET); 2387 cur = valuePop(ctxt); 2388 2389 if (cur->nodesetval->nodeNr == 0) { 2390 valuePush(ctxt, xmlXPathNewCString("")); 2391 } else { 2392 int i = 0; /* Should be first in document order !!!!! */ 2393 2394 if (cur->nodesetval->nodeTab[i]->ns == NULL) 2395 valuePush(ctxt, xmlXPathNewString( 2396 cur->nodesetval->nodeTab[i]->name)); 2397 2398 else { 2399 char name[2000]; 2400 sprintf(name, "%s:%s", 2401 (char *) cur->nodesetval->nodeTab[i]->ns->prefix, 2402 (char *) cur->nodesetval->nodeTab[i]->name); 2403 valuePush(ctxt, xmlXPathNewCString(name)); 2404 } 2405 } 2406 xmlXPathFreeObject(cur); 2407} 2408 2409/** 2410 * xmlXPathStringFunction: 2411 * @ctxt: the XPath Parser context 2412 * 2413 * Implement the string() XPath function 2414 * he string function converts an object to a string as follows: 2415 * - A node-set is converted to a string by returning the value of 2416 * the node in the node-set that is first in document order. 2417 * If the node-set is empty, an empty string is returned. 2418 * - A number is converted to a string as follows 2419 * + NaN is converted to the string NaN 2420 * + positive zero is converted to the string 0 2421 * + negative zero is converted to the string 0 2422 * + positive infinity is converted to the string Infinity 2423 * + negative infinity is converted to the string -Infinity 2424 * + if the number is an integer, the number is represented in 2425 * decimal form as a Number with no decimal point and no leading 2426 * zeros, preceded by a minus sign (-) if the number is negative 2427 * + otherwise, the number is represented in decimal form as a 2428 * Number including a decimal point with at least one digit 2429 * before the decimal point and at least one digit after the 2430 * decimal point, preceded by a minus sign (-) if the number 2431 * is negative; there must be no leading zeros before the decimal 2432 * point apart possibly from the one required digit immediatelyi 2433 * before the decimal point; beyond the one required digit 2434 * after the decimal point there must be as many, but only as 2435 * many, more digits as are needed to uniquely distinguish the 2436 * number from all other IEEE 754 numeric values. 2437 * - The boolean false value is converted to the string false. 2438 * The boolean true value is converted to the string true. 2439 */ 2440void 2441xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2442 xmlXPathObjectPtr cur; 2443 2444 CHECK_ARITY(1); 2445 cur = valuePop(ctxt); 2446 if (cur == NULL) ERROR(XPATH_INVALID_OPERAND); 2447 switch (cur->type) { 2448 case XPATH_NODESET: 2449 if (cur->nodesetval->nodeNr == 0) { 2450 valuePush(ctxt, xmlXPathNewCString("")); 2451 } else { 2452 xmlChar *res; 2453 int i = 0; /* Should be first in document order !!!!! */ 2454 res = xmlNodeGetContent(cur->nodesetval->nodeTab[i]); 2455 valuePush(ctxt, xmlXPathNewString(res)); 2456 xmlFree(res); 2457 } 2458 xmlXPathFreeObject(cur); 2459 return; 2460 case XPATH_STRING: 2461 valuePush(ctxt, cur); 2462 return; 2463 case XPATH_BOOLEAN: 2464 if (cur->boolval) valuePush(ctxt, xmlXPathNewCString("true")); 2465 else valuePush(ctxt, xmlXPathNewCString("false")); 2466 xmlXPathFreeObject(cur); 2467 return; 2468 case XPATH_NUMBER: { 2469 char buf[100]; 2470 2471 if (isnan(cur->floatval)) 2472 sprintf(buf, "NaN"); 2473 else if (isinf(cur->floatval) > 0) 2474 sprintf(buf, "+Infinity"); 2475 else if (isinf(cur->floatval) < 0) 2476 sprintf(buf, "-Infinity"); 2477 else 2478 sprintf(buf, "%0g", cur->floatval); 2479 valuePush(ctxt, xmlXPathNewCString(buf)); 2480 xmlXPathFreeObject(cur); 2481 return; 2482 } 2483 } 2484 STRANGE 2485} 2486 2487/** 2488 * xmlXPathStringLengthFunction: 2489 * @ctxt: the XPath Parser context 2490 * 2491 * Implement the string-length() XPath function 2492 * The string-length returns the number of characters in the string 2493 * (see [3.6 Strings]). If the argument is omitted, it defaults to 2494 * the context node converted to a string, in other words the value 2495 * of the context node. 2496 */ 2497void 2498xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2499 xmlXPathObjectPtr cur; 2500 2501 if (nargs == 0) { 2502 if (ctxt->context->node == NULL) { 2503 valuePush(ctxt, xmlXPathNewFloat(0)); 2504 } else { 2505 xmlChar *content; 2506 2507 content = xmlNodeGetContent(ctxt->context->node); 2508 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content))); 2509 xmlFree(content); 2510 } 2511 return; 2512 } 2513 CHECK_ARITY(1); 2514 CHECK_TYPE(XPATH_STRING); 2515 cur = valuePop(ctxt); 2516 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval))); 2517 xmlXPathFreeObject(cur); 2518} 2519 2520/** 2521 * xmlXPathConcatFunction: 2522 * @ctxt: the XPath Parser context 2523 * 2524 * Implement the concat() XPath function 2525 * The concat function returns the concatenation of its arguments. 2526 */ 2527void 2528xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2529 xmlXPathObjectPtr cur, new; 2530 xmlChar *tmp; 2531 2532 if (nargs < 2) { 2533 CHECK_ARITY(2); 2534 } 2535 2536 cur = valuePop(ctxt); 2537 if ((cur == NULL) || (cur->type != XPATH_STRING)) { 2538 xmlXPathFreeObject(cur); 2539 return; 2540 } 2541 nargs--; 2542 2543 while (nargs > 0) { 2544 new = valuePop(ctxt); 2545 if ((new == NULL) || (new->type != XPATH_STRING)) { 2546 xmlXPathFreeObject(new); 2547 xmlXPathFreeObject(cur); 2548 ERROR(XPATH_INVALID_TYPE); 2549 } 2550 tmp = xmlStrcat(new->stringval, cur->stringval); 2551 new->stringval = cur->stringval; 2552 cur->stringval = tmp; 2553 2554 xmlXPathFreeObject(new); 2555 nargs--; 2556 } 2557 valuePush(ctxt, cur); 2558} 2559 2560/** 2561 * xmlXPathContainsFunction: 2562 * @ctxt: the XPath Parser context 2563 * 2564 * Implement the contains() XPath function 2565 * The contains function returns true if the first argument string 2566 * contains the second argument string, and otherwise returns false. 2567 */ 2568void 2569xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2570 xmlXPathObjectPtr hay, needle; 2571 2572 CHECK_ARITY(2); 2573 CHECK_TYPE(XPATH_STRING); 2574 needle = valuePop(ctxt); 2575 hay = valuePop(ctxt); 2576 if ((hay == NULL) || (hay->type != XPATH_STRING)) { 2577 xmlXPathFreeObject(hay); 2578 xmlXPathFreeObject(needle); 2579 ERROR(XPATH_INVALID_TYPE); 2580 } 2581 if (xmlStrstr(hay->stringval, needle->stringval)) 2582 valuePush(ctxt, xmlXPathNewBoolean(1)); 2583 else 2584 valuePush(ctxt, xmlXPathNewBoolean(0)); 2585 xmlXPathFreeObject(hay); 2586 xmlXPathFreeObject(needle); 2587} 2588 2589/** 2590 * xmlXPathStartsWithFunction: 2591 * @ctxt: the XPath Parser context 2592 * 2593 * Implement the starts-with() XPath function 2594 * The starts-with function returns true if the first argument string 2595 * starts with the second argument string, and otherwise returns false. 2596 */ 2597void 2598xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2599 xmlXPathObjectPtr hay, needle; 2600 int n; 2601 2602 CHECK_ARITY(2); 2603 CHECK_TYPE(XPATH_STRING); 2604 needle = valuePop(ctxt); 2605 hay = valuePop(ctxt); 2606 if ((hay == NULL) || (hay->type != XPATH_STRING)) { 2607 xmlXPathFreeObject(hay); 2608 xmlXPathFreeObject(needle); 2609 ERROR(XPATH_INVALID_TYPE); 2610 } 2611 n = xmlStrlen(needle->stringval); 2612 if (xmlStrncmp(hay->stringval, needle->stringval, n)) 2613 valuePush(ctxt, xmlXPathNewBoolean(0)); 2614 else 2615 valuePush(ctxt, xmlXPathNewBoolean(1)); 2616 xmlXPathFreeObject(hay); 2617 xmlXPathFreeObject(needle); 2618} 2619 2620/** 2621 * xmlXPathSubstringFunction: 2622 * @ctxt: the XPath Parser context 2623 * 2624 * Implement the substring() XPath function 2625 * The substring function returns the substring of the first argument 2626 * starting at the position specified in the second argument with 2627 * length specified in the third argument. For example, 2628 * substring("12345",2,3) returns "234". If the third argument is not 2629 * specified, it returns the substring starting at the position specified 2630 * in the second argument and continuing to the end of the string. For 2631 * example, substring("12345",2) returns "2345". More precisely, each 2632 * character in the string (see [3.6 Strings]) is considered to have a 2633 * numeric position: the position of the first character is 1, the position 2634 * of the second character is 2 and so on. The returned substring contains 2635 * those characters for which the position of the character is greater than 2636 * or equal to the second argument and, if the third argument is specified, 2637 * less than the sum of the second and third arguments; the comparisons 2638 * and addition used for the above follow the standard IEEE 754 rules. Thus: 2639 * - substring("12345", 1.5, 2.6) returns "234" 2640 * - substring("12345", 0, 3) returns "12" 2641 * - substring("12345", 0 div 0, 3) returns "" 2642 * - substring("12345", 1, 0 div 0) returns "" 2643 * - substring("12345", -42, 1 div 0) returns "12345" 2644 * - substring("12345", -1 div 0, 1 div 0) returns "" 2645 */ 2646void 2647xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2648 xmlXPathObjectPtr str, start, len; 2649 double le, in; 2650 int i, l; 2651 xmlChar *ret; 2652 2653 /* 2654 * Conformance needs to be checked !!!!! 2655 */ 2656 if (nargs < 2) { 2657 CHECK_ARITY(2); 2658 } 2659 if (nargs > 3) { 2660 CHECK_ARITY(3); 2661 } 2662 if (nargs == 3) { 2663 CHECK_TYPE(XPATH_NUMBER); 2664 len = valuePop(ctxt); 2665 le = len->floatval; 2666 xmlXPathFreeObject(len); 2667 } else { 2668 le = 2000000000; 2669 } 2670 CHECK_TYPE(XPATH_NUMBER); 2671 start = valuePop(ctxt); 2672 in = start->floatval; 2673 xmlXPathFreeObject(start); 2674 CHECK_TYPE(XPATH_STRING); 2675 str = valuePop(ctxt); 2676 le += in; 2677 2678 /* integer index of the first char */ 2679 i = in; 2680 if (((double)i) != in) i++; 2681 2682 /* integer index of the last char */ 2683 l = le; 2684 if (((double)l) != le) l++; 2685 2686 /* back to a zero based len */ 2687 i--; 2688 l--; 2689 2690 /* check against the string len */ 2691 if (l > 1024) { 2692 l = xmlStrlen(str->stringval); 2693 } 2694 if (i < 0) { 2695 i = 0; 2696 } 2697 2698 /* number of chars to copy */ 2699 l -= i; 2700 2701 ret = xmlStrsub(str->stringval, i, l); 2702 if (ret == NULL) 2703 valuePush(ctxt, xmlXPathNewCString("")); 2704 else { 2705 valuePush(ctxt, xmlXPathNewString(ret)); 2706 xmlFree(ret); 2707 } 2708 xmlXPathFreeObject(str); 2709} 2710 2711/** 2712 * xmlXPathSubstringBeforeFunction: 2713 * @ctxt: the XPath Parser context 2714 * 2715 * Implement the substring-before() XPath function 2716 * The substring-before function returns the substring of the first 2717 * argument string that precedes the first occurrence of the second 2718 * argument string in the first argument string, or the empty string 2719 * if the first argument string does not contain the second argument 2720 * string. For example, substring-before("1999/04/01","/") returns 1999. 2721 */ 2722void 2723xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2724 CHECK_ARITY(2); 2725 TODO /* substring before */ 2726} 2727 2728/** 2729 * xmlXPathSubstringAfterFunction: 2730 * @ctxt: the XPath Parser context 2731 * 2732 * Implement the substring-after() XPath function 2733 * The substring-after function returns the substring of the first 2734 * argument string that follows the first occurrence of the second 2735 * argument string in the first argument string, or the empty stringi 2736 * if the first argument string does not contain the second argument 2737 * string. For example, substring-after("1999/04/01","/") returns 04/01, 2738 * and substring-after("1999/04/01","19") returns 99/04/01. 2739 */ 2740void 2741xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2742 CHECK_ARITY(2); 2743 TODO /* substring after */ 2744} 2745 2746/** 2747 * xmlXPathNormalizeFunction: 2748 * @ctxt: the XPath Parser context 2749 * 2750 * Implement the normalize() XPath function 2751 * The normalize function returns the argument string with white 2752 * space normalized by stripping leading and trailing whitespace 2753 * and replacing sequences of whitespace characters by a single 2754 * space. Whitespace characters are the same allowed by the S production 2755 * in XML. If the argument is omitted, it defaults to the context 2756 * node converted to a string, in other words the value of the context node. 2757 */ 2758void 2759xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2760 CHECK_ARITY(1); 2761 TODO /* normalize isn't as boring as translate, but pretty much */ 2762} 2763 2764/** 2765 * xmlXPathTranslateFunction: 2766 * @ctxt: the XPath Parser context 2767 * 2768 * Implement the translate() XPath function 2769 * The translate function returns the first argument string with 2770 * occurrences of characters in the second argument string replaced 2771 * by the character at the corresponding position in the third argument 2772 * string. For example, translate("bar","abc","ABC") returns the string 2773 * BAr. If there is a character in the second argument string with no 2774 * character at a corresponding position in the third argument string 2775 * (because the second argument string is longer than the third argument 2776 * string), then occurrences of that character in the first argument 2777 * string are removed. For example, translate("--aaa--","abc-","ABC") 2778 * returns "AAA". If a character occurs more than once in second 2779 * argument string, then the first occurrence determines the replacement 2780 * character. If the third argument string is longer than the second 2781 * argument string, then excess characters are ignored. 2782 */ 2783void 2784xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2785 CHECK_ARITY(3); 2786 TODO /* translate is boring, waiting for UTF-8 representation too */ 2787} 2788 2789/** 2790 * xmlXPathBooleanFunction: 2791 * @ctxt: the XPath Parser context 2792 * 2793 * Implement the boolean() XPath function 2794 * he boolean function converts its argument to a boolean as follows: 2795 * - a number is true if and only if it is neither positive or 2796 * negative zero nor NaN 2797 * - a node-set is true if and only if it is non-empty 2798 * - a string is true if and only if its length is non-zero 2799 */ 2800void 2801xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2802 xmlXPathObjectPtr cur; 2803 int res = 0; 2804 2805 CHECK_ARITY(1); 2806 cur = valuePop(ctxt); 2807 if (cur == NULL) ERROR(XPATH_INVALID_OPERAND); 2808 switch (cur->type) { 2809 case XPATH_NODESET: 2810 if ((cur->nodesetval == NULL) || 2811 (cur->nodesetval->nodeNr == 0)) res = 0; 2812 else 2813 res = 1; 2814 break; 2815 case XPATH_STRING: 2816 if ((cur->stringval == NULL) || 2817 (cur->stringval[0] == 0)) res = 0; 2818 else 2819 res = 1; 2820 break; 2821 case XPATH_BOOLEAN: 2822 valuePush(ctxt, cur); 2823 return; 2824 case XPATH_NUMBER: 2825 if (cur->floatval) res = 1; 2826 break; 2827 default: 2828 STRANGE 2829 } 2830 xmlXPathFreeObject(cur); 2831 valuePush(ctxt, xmlXPathNewBoolean(res)); 2832} 2833 2834/** 2835 * xmlXPathNotFunction: 2836 * @ctxt: the XPath Parser context 2837 * 2838 * Implement the not() XPath function 2839 * The not function returns true if its argument is false, 2840 * and false otherwise. 2841 */ 2842void 2843xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2844 CHECK_ARITY(1); 2845 CHECK_TYPE(XPATH_BOOLEAN); 2846 ctxt->value->boolval = ! ctxt->value->boolval; 2847} 2848 2849/** 2850 * xmlXPathTrueFunction: 2851 * @ctxt: the XPath Parser context 2852 * 2853 * Implement the true() XPath function 2854 */ 2855void 2856xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2857 CHECK_ARITY(0); 2858 valuePush(ctxt, xmlXPathNewBoolean(1)); 2859} 2860 2861/** 2862 * xmlXPathFalseFunction: 2863 * @ctxt: the XPath Parser context 2864 * 2865 * Implement the false() XPath function 2866 */ 2867void 2868xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2869 CHECK_ARITY(0); 2870 valuePush(ctxt, xmlXPathNewBoolean(0)); 2871} 2872 2873/** 2874 * xmlXPathLangFunction: 2875 * @ctxt: the XPath Parser context 2876 * 2877 * Implement the lang() XPath function 2878 * The lang function returns true or false depending on whether the 2879 * language of the context node as specified by xml:lang attributes 2880 * is the same as or is a sublanguage of the language specified by 2881 * the argument string. The language of the context node is determined 2882 * by the value of the xml:lang attribute on the context node, or, if 2883 * the context node has no xml:lang attribute, by the value of the 2884 * xml:lang attribute on the nearest ancestor of the context node that 2885 * has an xml:lang attribute. If there is no such attribute, then lang 2886 * returns false. If there is such an attribute, then lang returns 2887 * true if the attribute value is equal to the argument ignoring case, 2888 * or if there is some suffix starting with - such that the attribute 2889 * value is equal to the argument ignoring that suffix of the attribute 2890 * value and ignoring case. 2891 */ 2892void 2893xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2894 xmlXPathObjectPtr val; 2895 const xmlChar *theLang; 2896 const xmlChar *lang; 2897 int ret = 0; 2898 int i; 2899 2900 CHECK_ARITY(1); 2901 CHECK_TYPE(XPATH_STRING); 2902 val = valuePop(ctxt); 2903 lang = val->stringval; 2904 theLang = xmlNodeGetLang(ctxt->context->node); 2905 if ((theLang != NULL) && (lang != NULL)) { 2906 for (i = 0;lang[i] != 0;i++) 2907 if (toupper(lang[i]) != toupper(theLang[i])) 2908 goto not_equal; 2909 ret = 1; 2910 } 2911not_equal: 2912 xmlXPathFreeObject(val); 2913 valuePush(ctxt, xmlXPathNewBoolean(ret)); 2914} 2915 2916/** 2917 * xmlXPathNumberFunction: 2918 * @ctxt: the XPath Parser context 2919 * 2920 * Implement the number() XPath function 2921 */ 2922void 2923xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2924 xmlXPathObjectPtr cur; 2925 double res; 2926 2927 CHECK_ARITY(1); 2928 cur = valuePop(ctxt); 2929 switch (cur->type) { 2930 case XPATH_NODESET: 2931 valuePush(ctxt, cur); 2932 xmlXPathStringFunction(ctxt, 1); 2933 cur = valuePop(ctxt); 2934 case XPATH_STRING: 2935 res = xmlXPathStringEvalNumber(cur->stringval); 2936 valuePush(ctxt, xmlXPathNewFloat(res)); 2937 xmlXPathFreeObject(cur); 2938 return; 2939 case XPATH_BOOLEAN: 2940 if (cur->boolval) valuePush(ctxt, xmlXPathNewFloat(1.0)); 2941 else valuePush(ctxt, xmlXPathNewFloat(0.0)); 2942 xmlXPathFreeObject(cur); 2943 return; 2944 case XPATH_NUMBER: 2945 valuePush(ctxt, cur); 2946 return; 2947 } 2948 STRANGE 2949} 2950 2951/** 2952 * xmlXPathSumFunction: 2953 * @ctxt: the XPath Parser context 2954 * 2955 * Implement the sum() XPath function 2956 * The sum function returns the sum of the values of the nodes in 2957 * the argument node-set. 2958 */ 2959void 2960xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2961 CHECK_ARITY(1); 2962 TODO /* BUG Sum : don't understand the definition */ 2963} 2964 2965/** 2966 * xmlXPathFloorFunction: 2967 * @ctxt: the XPath Parser context 2968 * 2969 * Implement the floor() XPath function 2970 * The floor function returns the largest (closest to positive infinity) 2971 * number that is not greater than the argument and that is an integer. 2972 */ 2973void 2974xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2975 CHECK_ARITY(1); 2976 CHECK_TYPE(XPATH_NUMBER); 2977 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */ 2978 ctxt->value->floatval = (double)((int) ctxt->value->floatval); 2979} 2980 2981/** 2982 * xmlXPathCeilingFunction: 2983 * @ctxt: the XPath Parser context 2984 * 2985 * Implement the ceiling() XPath function 2986 * The ceiling function returns the smallest (closest to negative infinity) 2987 * number that is not less than the argument and that is an integer. 2988 */ 2989void 2990xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) { 2991 double f; 2992 2993 CHECK_ARITY(1); 2994 CHECK_TYPE(XPATH_NUMBER); 2995 f = (double)((int) ctxt->value->floatval); 2996 if (f != ctxt->value->floatval) 2997 ctxt->value->floatval = f + 1; 2998} 2999 3000/** 3001 * xmlXPathRoundFunction: 3002 * @ctxt: the XPath Parser context 3003 * 3004 * Implement the round() XPath function 3005 * The round function returns the number that is closest to the 3006 * argument and that is an integer. If there are two such numbers, 3007 * then the one that is even is returned. 3008 */ 3009void 3010xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) { 3011 double f; 3012 3013 CHECK_ARITY(1); 3014 CHECK_TYPE(XPATH_NUMBER); 3015 /* round(0.50000001) => 0 !!!!! */ 3016 f = (double)((int) ctxt->value->floatval); 3017 if (ctxt->value->floatval < f + 0.5) 3018 ctxt->value->floatval = f; 3019 else if (ctxt->value->floatval == f + 0.5) 3020 ctxt->value->floatval = f; /* !!!! Not following the spec here */ 3021 else 3022 ctxt->value->floatval = f + 1; 3023} 3024 3025/************************************************************************ 3026 * * 3027 * The Parser * 3028 * * 3029 ************************************************************************/ 3030 3031/* 3032 * a couple of forward declarations since we use a recursive call based 3033 * implementation. 3034 */ 3035void xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt); 3036void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt); 3037void xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt); 3038void xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt); 3039 3040/** 3041 * xmlXPathParseNCName: 3042 * @ctxt: the XPath Parser context 3043 * 3044 * parse an XML namespace non qualified name. 3045 * 3046 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)* 3047 * 3048 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | 3049 * CombiningChar | Extender 3050 * 3051 * Returns the namespace name or NULL 3052 */ 3053 3054xmlChar * 3055xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) { 3056 const xmlChar *q; 3057 xmlChar *ret = NULL; 3058 3059 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL); 3060 q = NEXT; 3061 3062 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) || 3063 (CUR == '.') || (CUR == '-') || 3064 (CUR == '_') || 3065 (IS_COMBINING(CUR)) || 3066 (IS_EXTENDER(CUR))) 3067 NEXT; 3068 3069 ret = xmlStrndup(q, CUR_PTR - q); 3070 3071 return(ret); 3072} 3073 3074/** 3075 * xmlXPathParseQName: 3076 * @ctxt: the XPath Parser context 3077 * @prefix: a xmlChar ** 3078 * 3079 * parse an XML qualified name 3080 * 3081 * [NS 5] QName ::= (Prefix ':')? LocalPart 3082 * 3083 * [NS 6] Prefix ::= NCName 3084 * 3085 * [NS 7] LocalPart ::= NCName 3086 * 3087 * Returns the function returns the local part, and prefix is updated 3088 * to get the Prefix if any. 3089 */ 3090 3091xmlChar * 3092xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) { 3093 xmlChar *ret = NULL; 3094 3095 *prefix = NULL; 3096 ret = xmlXPathParseNCName(ctxt); 3097 if (CUR == ':') { 3098 *prefix = ret; 3099 NEXT; 3100 ret = xmlXPathParseNCName(ctxt); 3101 } 3102 return(ret); 3103} 3104 3105/** 3106 * xmlXPathStringEvalNumber: 3107 * @str: A string to scan 3108 * 3109 * [30] Number ::= Digits ('.' Digits)? 3110 * | '.' Digits 3111 * [31] Digits ::= [0-9]+ 3112 * 3113 * Parse and evaluate a Number in the string 3114 * 3115 * BUG: "1.' is not valid ... James promised correction 3116 * as Digits ('.' Digits?)? 3117 * 3118 * Returns the double value. 3119 */ 3120double 3121xmlXPathStringEvalNumber(const xmlChar *str) { 3122 const xmlChar *cur = str; 3123 double ret = 0.0; 3124 double mult = 1; 3125 int ok = 0; 3126 3127 while (*cur == ' ') cur++; 3128 if ((*cur != '.') && ((*cur < '0') || (*cur > '9'))) { 3129 return(xmlXPathNAN); 3130 } 3131 while ((*cur >= '0') && (*cur <= '9')) { 3132 ret = ret * 10 + (*cur - '0'); 3133 ok = 1; 3134 cur++; 3135 } 3136 if (*cur == '.') { 3137 cur++; 3138 if (((*cur < '0') || (*cur > '9')) && (!ok)) { 3139 return(xmlXPathNAN); 3140 } 3141 while ((*cur >= '0') && (*cur <= '9')) { 3142 mult /= 10; 3143 ret = ret + (*cur - '0') * mult; 3144 cur++; 3145 } 3146 } 3147 while (*cur == ' ') cur++; 3148 if (*cur != 0) return(xmlXPathNAN); 3149 return(ret); 3150} 3151 3152/** 3153 * xmlXPathEvalNumber: 3154 * @ctxt: the XPath Parser context 3155 * 3156 * [30] Number ::= Digits ('.' Digits)? 3157 * | '.' Digits 3158 * [31] Digits ::= [0-9]+ 3159 * 3160 * Parse and evaluate a Number, then push it on the stack 3161 * 3162 * BUG: "1.' is not valid ... James promised correction 3163 * as Digits ('.' Digits?)? 3164 */ 3165void 3166xmlXPathEvalNumber(xmlXPathParserContextPtr ctxt) { 3167 double ret = 0.0; 3168 double mult = 1; 3169 int ok = 0; 3170 3171 CHECK_ERROR; 3172 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) { 3173 ERROR(XPATH_NUMBER_ERROR); 3174 } 3175 while ((CUR >= '0') && (CUR <= '9')) { 3176 ret = ret * 10 + (CUR - '0'); 3177 ok = 1; 3178 NEXT; 3179 } 3180 if (CUR == '.') { 3181 NEXT; 3182 if (((CUR < '0') || (CUR > '9')) && (!ok)) { 3183 ERROR(XPATH_NUMBER_ERROR); 3184 } 3185 while ((CUR >= '0') && (CUR <= '9')) { 3186 mult /= 10; 3187 ret = ret + (CUR - '0') * mult; 3188 NEXT; 3189 } 3190 } 3191 valuePush(ctxt, xmlXPathNewFloat(ret)); 3192} 3193 3194/** 3195 * xmlXPathEvalLiteral: 3196 * @ctxt: the XPath Parser context 3197 * 3198 * Parse a Literal and push it on the stack. 3199 * 3200 * [29] Literal ::= '"' [^"]* '"' 3201 * | "'" [^']* "'" 3202 * 3203 * TODO: xmlXPathEvalLiteral memory allocation could be improved. 3204 */ 3205void 3206xmlXPathEvalLiteral(xmlXPathParserContextPtr ctxt) { 3207 const xmlChar *q; 3208 xmlChar *ret = NULL; 3209 3210 if (CUR == '"') { 3211 NEXT; 3212 q = CUR_PTR; 3213 while ((IS_CHAR(CUR)) && (CUR != '"')) 3214 NEXT; 3215 if (!IS_CHAR(CUR)) { 3216 ERROR(XPATH_UNFINISHED_LITERAL_ERROR); 3217 } else { 3218 ret = xmlStrndup(q, CUR_PTR - q); 3219 NEXT; 3220 } 3221 } else if (CUR == '\'') { 3222 NEXT; 3223 q = CUR_PTR; 3224 while ((IS_CHAR(CUR)) && (CUR != '\'')) 3225 NEXT; 3226 if (!IS_CHAR(CUR)) { 3227 ERROR(XPATH_UNFINISHED_LITERAL_ERROR); 3228 } else { 3229 ret = xmlStrndup(q, CUR_PTR - q); 3230 NEXT; 3231 } 3232 } else { 3233 ERROR(XPATH_START_LITERAL_ERROR); 3234 } 3235 if (ret == NULL) return; 3236 valuePush(ctxt, xmlXPathNewString(ret)); 3237 xmlFree(ret); 3238} 3239 3240/** 3241 * xmlXPathEvalVariableReference: 3242 * @ctxt: the XPath Parser context 3243 * 3244 * Parse a VariableReference, evaluate it and push it on the stack. 3245 * 3246 * The variable bindings consist of a mapping from variable names 3247 * to variable values. The value of a variable is an object, which 3248 * of any of the types that are possible for the value of an expression, 3249 * and may also be of additional types not specified here. 3250 * 3251 * Early evaluation is possible since: 3252 * The variable bindings [...] used to evaluate a subexpression are 3253 * always the same as those used to evaluate the containing expression. 3254 * 3255 * [36] VariableReference ::= '$' QName 3256 */ 3257void 3258xmlXPathEvalVariableReference(xmlXPathParserContextPtr ctxt) { 3259 xmlChar *name; 3260 xmlChar *prefix; 3261 xmlXPathObjectPtr value; 3262 3263 if (CUR != '$') { 3264 ERROR(XPATH_VARIABLE_REF_ERROR); 3265 } 3266 name = xmlXPathParseQName(ctxt, &prefix); 3267 if (name == NULL) { 3268 ERROR(XPATH_VARIABLE_REF_ERROR); 3269 } 3270 value = xmlXPathVariablelookup(ctxt, prefix, name); 3271 if (value == NULL) { 3272 ERROR(XPATH_UNDEF_VARIABLE_ERROR); 3273 } 3274 valuePush(ctxt, value); 3275 if (prefix != NULL) xmlFree(prefix); 3276 xmlFree(name); 3277} 3278 3279 3280/** 3281 * xmlXPathFunctionLookup: 3282 * @ctxt: the XPath Parser context 3283 * @name: a name string 3284 * 3285 * Search for a function of the given name 3286 * 3287 * [35] FunctionName ::= QName - NodeType 3288 * 3289 * TODO: for the moment the function list is hardcoded from the spec !!!! 3290 * 3291 * Returns the xmlXPathFunction if found, or NULL otherwise 3292 */ 3293xmlXPathFunction 3294xmlXPathIsFunction(xmlXPathParserContextPtr ctxt, const xmlChar *name) { 3295 switch (name[0]) { 3296 case 'b': 3297 if (!xmlStrcmp(name, BAD_CAST "boolean")) 3298 return(xmlXPathBooleanFunction); 3299 break; 3300 case 'c': 3301 if (!xmlStrcmp(name, BAD_CAST "ceiling")) 3302 return(xmlXPathCeilingFunction); 3303 if (!xmlStrcmp(name, BAD_CAST "count")) 3304 return(xmlXPathCountFunction); 3305 if (!xmlStrcmp(name, BAD_CAST "concat")) 3306 return(xmlXPathConcatFunction); 3307 if (!xmlStrcmp(name, BAD_CAST "contains")) 3308 return(xmlXPathContainsFunction); 3309 break; 3310 case 'i': 3311 if (!xmlStrcmp(name, BAD_CAST "id")) 3312 return(xmlXPathIdFunction); 3313 break; 3314 case 'f': 3315 if (!xmlStrcmp(name, BAD_CAST "false")) 3316 return(xmlXPathFalseFunction); 3317 if (!xmlStrcmp(name, BAD_CAST "floor")) 3318 return(xmlXPathFloorFunction); 3319 break; 3320 case 'l': 3321 if (!xmlStrcmp(name, BAD_CAST "last")) 3322 return(xmlXPathLastFunction); 3323 if (!xmlStrcmp(name, BAD_CAST "lang")) 3324 return(xmlXPathLangFunction); 3325 if (!xmlStrcmp(name, BAD_CAST "local-part")) 3326 return(xmlXPathLocalPartFunction); 3327 break; 3328 case 'n': 3329 if (!xmlStrcmp(name, BAD_CAST "not")) 3330 return(xmlXPathNotFunction); 3331 if (!xmlStrcmp(name, BAD_CAST "name")) 3332 return(xmlXPathNameFunction); 3333 if (!xmlStrcmp(name, BAD_CAST "namespace")) 3334 return(xmlXPathNamespaceFunction); 3335 if (!xmlStrcmp(name, BAD_CAST "normalize-space")) 3336 return(xmlXPathNormalizeFunction); 3337 if (!xmlStrcmp(name, BAD_CAST "normalize")) 3338 return(xmlXPathNormalizeFunction); 3339 if (!xmlStrcmp(name, BAD_CAST "number")) 3340 return(xmlXPathNumberFunction); 3341 break; 3342 case 'p': 3343 if (!xmlStrcmp(name, BAD_CAST "position")) 3344 return(xmlXPathPositionFunction); 3345 break; 3346 case 'r': 3347 if (!xmlStrcmp(name, BAD_CAST "round")) 3348 return(xmlXPathRoundFunction); 3349 break; 3350 case 's': 3351 if (!xmlStrcmp(name, BAD_CAST "string")) 3352 return(xmlXPathStringFunction); 3353 if (!xmlStrcmp(name, BAD_CAST "string-length")) 3354 return(xmlXPathStringLengthFunction); 3355 if (!xmlStrcmp(name, BAD_CAST "starts-with")) 3356 return(xmlXPathStartsWithFunction); 3357 if (!xmlStrcmp(name, BAD_CAST "substring")) 3358 return(xmlXPathSubstringFunction); 3359 if (!xmlStrcmp(name, BAD_CAST "substring-before")) 3360 return(xmlXPathSubstringBeforeFunction); 3361 if (!xmlStrcmp(name, BAD_CAST "substring-after")) 3362 return(xmlXPathSubstringAfterFunction); 3363 if (!xmlStrcmp(name, BAD_CAST "sum")) 3364 return(xmlXPathSumFunction); 3365 break; 3366 case 't': 3367 if (!xmlStrcmp(name, BAD_CAST "true")) 3368 return(xmlXPathTrueFunction); 3369 if (!xmlStrcmp(name, BAD_CAST "translate")) 3370 return(xmlXPathTranslateFunction); 3371 break; 3372 } 3373 return(NULL); 3374} 3375 3376/** 3377 * xmlXPathEvalLocationPathName: 3378 * @ctxt: the XPath Parser context 3379 * @name: a name string 3380 * 3381 * Various names in the beginning of a LocationPath expression 3382 * indicate whether that's an Axis, a node type, 3383 * 3384 * [6] AxisName ::= 'ancestor' 3385 * | 'ancestor-or-self' 3386 * | 'attribute' 3387 * | 'child' 3388 * | 'descendant' 3389 * | 'descendant-or-self' 3390 * | 'following' 3391 * | 'following-sibling' 3392 * | 'namespace' 3393 * | 'parent' 3394 * | 'preceding' 3395 * | 'preceding-sibling' 3396 * | 'self' 3397 * [38] NodeType ::= 'comment' 3398 * | 'text' 3399 * | 'processing-instruction' 3400 * | 'node' 3401 */ 3402int 3403xmlXPathGetNameType(xmlXPathParserContextPtr ctxt, const xmlChar *name) { 3404 switch (name[0]) { 3405 case 'a': 3406 if (!xmlStrcmp(name, BAD_CAST "ancestor")) return(AXIS_ANCESTOR); 3407 if (!xmlStrcmp(name, BAD_CAST "ancestor-or-self")) 3408 return(AXIS_ANCESTOR_OR_SELF); 3409 if (!xmlStrcmp(name, BAD_CAST "attribute")) return(AXIS_ATTRIBUTE); 3410 break; 3411 case 'c': 3412 if (!xmlStrcmp(name, BAD_CAST "child")) return(AXIS_CHILD); 3413 if (!xmlStrcmp(name, BAD_CAST "comment")) return(NODE_TYPE_COMMENT); 3414 break; 3415 case 'd': 3416 if (!xmlStrcmp(name, BAD_CAST "descendant")) 3417 return(AXIS_DESCENDANT); 3418 if (!xmlStrcmp(name, BAD_CAST "descendant-or-self")) 3419 return(AXIS_DESCENDANT_OR_SELF); 3420 break; 3421 case 'f': 3422 if (!xmlStrcmp(name, BAD_CAST "following")) return(AXIS_FOLLOWING); 3423 if (!xmlStrcmp(name, BAD_CAST "following-sibling")) 3424 return(AXIS_FOLLOWING_SIBLING); 3425 break; 3426 case 'n': 3427 if (!xmlStrcmp(name, BAD_CAST "namespace")) return(AXIS_NAMESPACE); 3428 if (!xmlStrcmp(name, BAD_CAST "node")) return(NODE_TYPE_NODE); 3429 break; 3430 case 'p': 3431 if (!xmlStrcmp(name, BAD_CAST "parent")) return(AXIS_PARENT); 3432 if (!xmlStrcmp(name, BAD_CAST "preceding")) return(AXIS_PRECEDING); 3433 if (!xmlStrcmp(name, BAD_CAST "preceding-sibling")) 3434 return(AXIS_PRECEDING_SIBLING); 3435 if (!xmlStrcmp(name, BAD_CAST "processing-instruction")) 3436 return(NODE_TYPE_PI); 3437 break; 3438 case 's': 3439 if (!xmlStrcmp(name, BAD_CAST "self")) return(AXIS_SELF); 3440 break; 3441 case 't': 3442 if (!xmlStrcmp(name, BAD_CAST "text")) return(NODE_TYPE_TEXT); 3443 break; 3444 } 3445 if (xmlXPathIsFunction(ctxt, name)) return(IS_FUNCTION); 3446 return(0); 3447} 3448 3449/** 3450 * xmlXPathEvalFunctionCall: 3451 * @ctxt: the XPath Parser context 3452 * 3453 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')' 3454 * [17] Argument ::= Expr 3455 * 3456 * Parse and evaluate a function call, the evaluation of all arguments are 3457 * pushed on the stack 3458 */ 3459void 3460xmlXPathEvalFunctionCall(xmlXPathParserContextPtr ctxt) { 3461 xmlChar *name; 3462 xmlChar *prefix; 3463 xmlXPathFunction func; 3464 int nbargs = 0; 3465 3466 name = xmlXPathParseQName(ctxt, &prefix); 3467 if (name == NULL) { 3468 ERROR(XPATH_EXPR_ERROR); 3469 } 3470 SKIP_BLANKS; 3471 func = xmlXPathIsFunction(ctxt, name); 3472 if (func == NULL) { 3473 xmlFree(name); 3474 ERROR(XPATH_UNKNOWN_FUNC_ERROR); 3475 } 3476#ifdef DEBUG_EXPR 3477 fprintf(xmlXPathDebug, "Calling function %s\n", name); 3478#endif 3479 3480 if (CUR != '(') { 3481 xmlFree(name); 3482 ERROR(XPATH_EXPR_ERROR); 3483 } 3484 NEXT; 3485 SKIP_BLANKS; 3486 3487 while (CUR != ')') { 3488 xmlXPathEvalExpr(ctxt); 3489 nbargs++; 3490 if (CUR == ')') break; 3491 if (CUR != ',') { 3492 xmlFree(name); 3493 ERROR(XPATH_EXPR_ERROR); 3494 } 3495 NEXT; 3496 SKIP_BLANKS; 3497 } 3498 NEXT; 3499 SKIP_BLANKS; 3500 xmlFree(name); 3501 func(ctxt, nbargs); 3502} 3503 3504/** 3505 * xmlXPathEvalPrimaryExpr: 3506 * @ctxt: the XPath Parser context 3507 * 3508 * [15] PrimaryExpr ::= VariableReference 3509 * | '(' Expr ')' 3510 * | Literal 3511 * | Number 3512 * | FunctionCall 3513 * 3514 * Parse and evaluate a primary expression, then push the result on the stack 3515 */ 3516void 3517xmlXPathEvalPrimaryExpr(xmlXPathParserContextPtr ctxt) { 3518 SKIP_BLANKS; 3519 if (CUR == '$') xmlXPathEvalVariableReference(ctxt); 3520 else if (CUR == '(') { 3521 NEXT; 3522 SKIP_BLANKS; 3523 xmlXPathEvalExpr(ctxt); 3524 if (CUR != ')') { 3525 ERROR(XPATH_EXPR_ERROR); 3526 } 3527 NEXT; 3528 SKIP_BLANKS; 3529 } else if (IS_DIGIT(CUR)) { 3530 xmlXPathEvalNumber(ctxt); 3531 } else if ((CUR == '\'') || (CUR == '"')) { 3532 xmlXPathEvalLiteral(ctxt); 3533 } else { 3534 xmlXPathEvalFunctionCall(ctxt); 3535 } 3536} 3537 3538/** 3539 * xmlXPathEvalFilterExpr: 3540 * @ctxt: the XPath Parser context 3541 * 3542 * [20] FilterExpr ::= PrimaryExpr 3543 * | FilterExpr Predicate 3544 * 3545 * Parse and evaluate a filter expression, then push the result on the stack 3546 * Square brackets are used to filter expressions in the same way that 3547 * they are used in location paths. It is an error if the expression to 3548 * be filtered does not evaluate to a node-set. The context node list 3549 * used for evaluating the expression in square brackets is the node-set 3550 * to be filtered listed in document order. 3551 */ 3552 3553void 3554xmlXPathEvalFilterExpr(xmlXPathParserContextPtr ctxt) { 3555 /**** 3556 xmlNodeSetPtr oldset = NULL; 3557 xmlXPathObjectPtr arg; 3558 ****/ 3559 3560 xmlXPathEvalPrimaryExpr(ctxt); 3561 CHECK_ERROR; 3562 SKIP_BLANKS; 3563 3564 if (CUR != '[') return; 3565 3566 CHECK_TYPE(XPATH_NODESET); 3567 3568 while (CUR == '[') { 3569 xmlXPathEvalPredicate(ctxt); 3570 SKIP_BLANKS; 3571 } 3572 3573 3574} 3575 3576/** 3577 * xmlXPathScanName: 3578 * @ctxt: the XPath Parser context 3579 * 3580 * Trickery: parse an XML name but without consuming the input flow 3581 * Needed for rollback cases. 3582 * 3583 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | 3584 * CombiningChar | Extender 3585 * 3586 * [5] Name ::= (Letter | '_' | ':') (NameChar)* 3587 * 3588 * [6] Names ::= Name (S Name)* 3589 * 3590 * Returns the Name parsed or NULL 3591 */ 3592 3593xmlChar * 3594xmlXPathScanName(xmlXPathParserContextPtr ctxt) { 3595 xmlChar buf[XML_MAX_NAMELEN]; 3596 int len = 0; 3597 3598 SKIP_BLANKS; 3599 if (!IS_LETTER(CUR) && (CUR != '_') && 3600 (CUR != ':')) { 3601 return(NULL); 3602 } 3603 3604 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) || 3605 (NXT(len) == '.') || (NXT(len) == '-') || 3606 (NXT(len) == '_') || (NXT(len) == ':') || 3607 (IS_COMBINING(NXT(len))) || 3608 (IS_EXTENDER(NXT(len)))) { 3609 buf[len] = NXT(len); 3610 len++; 3611 if (len >= XML_MAX_NAMELEN) { 3612 fprintf(stderr, 3613 "xmlScanName: reached XML_MAX_NAMELEN limit\n"); 3614 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) || 3615 (NXT(len) == '.') || (NXT(len) == '-') || 3616 (NXT(len) == '_') || (NXT(len) == ':') || 3617 (IS_COMBINING(NXT(len))) || 3618 (IS_EXTENDER(NXT(len)))) 3619 len++; 3620 break; 3621 } 3622 } 3623 return(xmlStrndup(buf, len)); 3624} 3625 3626/** 3627 * xmlXPathEvalPathExpr: 3628 * @ctxt: the XPath Parser context 3629 * 3630 * [19] PathExpr ::= LocationPath 3631 * | FilterExpr 3632 * | FilterExpr '/' RelativeLocationPath 3633 * | FilterExpr '//' RelativeLocationPath 3634 * 3635 * Parse and evaluate a path expression, then push the result on the stack 3636 * The / operator and // operators combine an arbitrary expression 3637 * and a relative location path. It is an error if the expression 3638 * does not evaluate to a node-set. 3639 * The / operator does composition in the same way as when / is 3640 * used in a location path. As in location paths, // is short for 3641 * /descendant-or-self::node()/. 3642 */ 3643 3644void 3645xmlXPathEvalPathExpr(xmlXPathParserContextPtr ctxt) { 3646 xmlNodeSetPtr newset = NULL; 3647 3648 SKIP_BLANKS; 3649 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) || 3650 (CUR == '\'') || (CUR == '"')) { 3651 xmlXPathEvalFilterExpr(ctxt); 3652 CHECK_ERROR; 3653 if ((CUR == '/') && (NXT(1) == '/')) { 3654 SKIP(2); 3655 SKIP_BLANKS; 3656 if (ctxt->context->nodelist == NULL) { 3657 STRANGE 3658 xmlXPathRoot(ctxt); 3659 } 3660 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF, 3661 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL); 3662 if (ctxt->context->nodelist != NULL) 3663 xmlXPathFreeNodeSet(ctxt->context->nodelist); 3664 ctxt->context->nodelist = newset; 3665 ctxt->context->node = NULL; 3666 xmlXPathEvalRelativeLocationPath(ctxt); 3667 } else if (CUR == '/') { 3668 xmlXPathEvalRelativeLocationPath(ctxt); 3669 } 3670 } else { 3671 /******* !!!!!!!!!! @attname */ 3672 xmlChar *name; 3673 3674 name = xmlXPathScanName(ctxt); 3675 if ((name == NULL) || (!xmlXPathIsFunction(ctxt, name))) 3676 xmlXPathEvalLocationPath(ctxt); 3677 else 3678 xmlXPathEvalFilterExpr(ctxt); 3679 if (name != NULL) 3680 xmlFree(name); 3681 } 3682} 3683 3684/** 3685 * xmlXPathEvalUnionExpr: 3686 * @ctxt: the XPath Parser context 3687 * 3688 * [18] UnionExpr ::= PathExpr 3689 * | UnionExpr '|' PathExpr 3690 * 3691 * Parse and evaluate an union expression, then push the result on the stack 3692 */ 3693 3694void 3695xmlXPathEvalUnionExpr(xmlXPathParserContextPtr ctxt) { 3696 xmlXPathEvalPathExpr(ctxt); 3697 CHECK_ERROR; 3698 SKIP_BLANKS; 3699 if (CUR == '|') { 3700 xmlNodeSetPtr old = ctxt->context->nodelist; 3701 3702 NEXT; 3703 SKIP_BLANKS; 3704 xmlXPathEvalPathExpr(ctxt); 3705 3706 if (ctxt->context->nodelist == NULL) 3707 ctxt->context->nodelist = old; 3708 else { 3709 ctxt->context->nodelist = 3710 xmlXPathNodeSetMerge(ctxt->context->nodelist, old); 3711 xmlXPathFreeNodeSet(old); 3712 } 3713 } 3714} 3715 3716/** 3717 * xmlXPathEvalUnaryExpr: 3718 * @ctxt: the XPath Parser context 3719 * 3720 * [27] UnaryExpr ::= UnionExpr 3721 * | '-' UnaryExpr 3722 * 3723 * Parse and evaluate an unary expression, then push the result on the stack 3724 */ 3725 3726void 3727xmlXPathEvalUnaryExpr(xmlXPathParserContextPtr ctxt) { 3728 int minus = 0; 3729 3730 SKIP_BLANKS; 3731 if (CUR == '-') { 3732 minus = 1; 3733 NEXT; 3734 SKIP_BLANKS; 3735 } 3736 xmlXPathEvalUnionExpr(ctxt); 3737 CHECK_ERROR; 3738 if (minus) { 3739 xmlXPathValueFlipSign(ctxt); 3740 } 3741} 3742 3743/** 3744 * xmlXPathEvalMultiplicativeExpr: 3745 * @ctxt: the XPath Parser context 3746 * 3747 * [26] MultiplicativeExpr ::= UnaryExpr 3748 * | MultiplicativeExpr MultiplyOperator UnaryExpr 3749 * | MultiplicativeExpr 'div' UnaryExpr 3750 * | MultiplicativeExpr 'mod' UnaryExpr 3751 * [34] MultiplyOperator ::= '*' 3752 * 3753 * Parse and evaluate an Additive expression, then push the result on the stack 3754 */ 3755 3756void 3757xmlXPathEvalMultiplicativeExpr(xmlXPathParserContextPtr ctxt) { 3758 xmlXPathEvalUnaryExpr(ctxt); 3759 CHECK_ERROR; 3760 SKIP_BLANKS; 3761 while ((CUR == '*') || 3762 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) || 3763 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) { 3764 int op = -1; 3765 3766 if (CUR == '*') { 3767 op = 0; 3768 NEXT; 3769 } else if (CUR == 'd') { 3770 op = 1; 3771 SKIP(3); 3772 } else if (CUR == 'm') { 3773 op = 2; 3774 SKIP(3); 3775 } 3776 SKIP_BLANKS; 3777 xmlXPathEvalUnaryExpr(ctxt); 3778 CHECK_ERROR; 3779 switch (op) { 3780 case 0: 3781 xmlXPathMultValues(ctxt); 3782 break; 3783 case 1: 3784 xmlXPathDivValues(ctxt); 3785 break; 3786 case 2: 3787 xmlXPathModValues(ctxt); 3788 break; 3789 } 3790 } 3791} 3792 3793/** 3794 * xmlXPathEvalAdditiveExpr: 3795 * @ctxt: the XPath Parser context 3796 * 3797 * [25] AdditiveExpr ::= MultiplicativeExpr 3798 * | AdditiveExpr '+' MultiplicativeExpr 3799 * | AdditiveExpr '-' MultiplicativeExpr 3800 * 3801 * Parse and evaluate an Additive expression, then push the result on the stack 3802 */ 3803 3804void 3805xmlXPathEvalAdditiveExpr(xmlXPathParserContextPtr ctxt) { 3806 xmlXPathEvalMultiplicativeExpr(ctxt); 3807 CHECK_ERROR; 3808 SKIP_BLANKS; 3809 while ((CUR == '+') || (CUR == '-')) { 3810 int plus; 3811 3812 if (CUR == '+') plus = 1; 3813 else plus = 0; 3814 NEXT; 3815 SKIP_BLANKS; 3816 xmlXPathEvalMultiplicativeExpr(ctxt); 3817 CHECK_ERROR; 3818 if (plus) xmlXPathAddValues(ctxt); 3819 else xmlXPathSubValues(ctxt); 3820 } 3821} 3822 3823/** 3824 * xmlXPathEvalRelationalExpr: 3825 * @ctxt: the XPath Parser context 3826 * 3827 * [24] RelationalExpr ::= AdditiveExpr 3828 * | RelationalExpr '<' AdditiveExpr 3829 * | RelationalExpr '>' AdditiveExpr 3830 * | RelationalExpr '<=' AdditiveExpr 3831 * | RelationalExpr '>=' AdditiveExpr 3832 * 3833 * A <= B > C is allowed ? Answer from James, yes with 3834 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr 3835 * which is basically what got implemented. 3836 * 3837 * Parse and evaluate a Relational expression, then push the result 3838 * on the stack 3839 */ 3840 3841void 3842xmlXPathEvalRelationalExpr(xmlXPathParserContextPtr ctxt) { 3843 xmlXPathEvalAdditiveExpr(ctxt); 3844 CHECK_ERROR; 3845 SKIP_BLANKS; 3846 while ((CUR == '<') || 3847 (CUR == '>') || 3848 ((CUR == '<') && (NXT(1) == '=')) || 3849 ((CUR == '>') && (NXT(1) == '='))) { 3850 int inf, strict, ret; 3851 3852 if (CUR == '<') inf = 1; 3853 else inf = 0; 3854 if (NXT(1) == '=') strict = 0; 3855 else strict = 1; 3856 NEXT; 3857 if (!strict) NEXT; 3858 SKIP_BLANKS; 3859 xmlXPathEvalAdditiveExpr(ctxt); 3860 CHECK_ERROR; 3861 ret = xmlXPathCompareValues(ctxt, inf, strict); 3862 valuePush(ctxt, xmlXPathNewBoolean(ret)); 3863 } 3864} 3865 3866/** 3867 * xmlXPathEvalEqualityExpr: 3868 * @ctxt: the XPath Parser context 3869 * 3870 * [23] EqualityExpr ::= RelationalExpr 3871 * | EqualityExpr '=' RelationalExpr 3872 * | EqualityExpr '!=' RelationalExpr 3873 * 3874 * A != B != C is allowed ? Answer from James, yes with 3875 * (RelationalExpr = RelationalExpr) = RelationalExpr 3876 * (RelationalExpr != RelationalExpr) != RelationalExpr 3877 * which is basically what got implemented. 3878 * 3879 * Parse and evaluate an Equality expression, then push the result on the stack 3880 * 3881 */ 3882void 3883xmlXPathEvalEqualityExpr(xmlXPathParserContextPtr ctxt) { 3884 xmlXPathEvalRelationalExpr(ctxt); 3885 CHECK_ERROR; 3886 SKIP_BLANKS; 3887 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) { 3888 xmlXPathObjectPtr res; 3889 int eq, equal; 3890 3891 if (CUR == '=') eq = 1; 3892 else eq = 0; 3893 NEXT; 3894 if (!eq) NEXT; 3895 SKIP_BLANKS; 3896 xmlXPathEvalRelationalExpr(ctxt); 3897 CHECK_ERROR; 3898 equal = xmlXPathEqualValues(ctxt); 3899 if (eq) res = xmlXPathNewBoolean(equal); 3900 else res = xmlXPathNewBoolean(!equal); 3901 valuePush(ctxt, res); 3902 } 3903} 3904 3905/** 3906 * xmlXPathEvalAndExpr: 3907 * @ctxt: the XPath Parser context 3908 * 3909 * [22] AndExpr ::= EqualityExpr 3910 * | AndExpr 'and' EqualityExpr 3911 * 3912 * Parse and evaluate an AND expression, then push the result on the stack 3913 * 3914 */ 3915void 3916xmlXPathEvalAndExpr(xmlXPathParserContextPtr ctxt) { 3917 xmlXPathEvalEqualityExpr(ctxt); 3918 CHECK_ERROR; 3919 SKIP_BLANKS; 3920 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'n')) { 3921 xmlXPathObjectPtr arg1, arg2; 3922 3923 SKIP(3); 3924 SKIP_BLANKS; 3925 xmlXPathEvalEqualityExpr(ctxt); 3926 CHECK_ERROR; 3927 arg2 = valuePop(ctxt); 3928 arg1 = valuePop(ctxt); 3929 arg1->boolval &= arg2->boolval; 3930 valuePush(ctxt, arg1); 3931 xmlXPathFreeObject(arg2); 3932 } 3933} 3934 3935/** 3936 * xmlXPathEvalExpr: 3937 * @ctxt: the XPath Parser context 3938 * 3939 * [14] Expr ::= OrExpr 3940 * [21] OrExpr ::= AndExpr 3941 * | OrExpr 'or' AndExpr 3942 * 3943 * Parse and evaluate an expression, then push the result on the stack 3944 * 3945 */ 3946void 3947xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) { 3948 xmlXPathEvalAndExpr(ctxt); 3949 CHECK_ERROR; 3950 SKIP_BLANKS; 3951 while ((CUR == 'o') && (NXT(1) == 'r')) { 3952 xmlXPathObjectPtr arg1, arg2; 3953 3954 SKIP(2); 3955 SKIP_BLANKS; 3956 xmlXPathEvalAndExpr(ctxt); 3957 CHECK_ERROR; 3958 arg2 = valuePop(ctxt); 3959 arg1 = valuePop(ctxt); 3960 arg1->boolval |= arg2->boolval; 3961 valuePush(ctxt, arg1); 3962 xmlXPathFreeObject(arg2); 3963 } 3964} 3965 3966/** 3967 * xmlXPathEvaluatePredicateResult: 3968 * @ctxt: the XPath Parser context 3969 * @res: the Predicate Expression evaluation result 3970 * @index: index of the current node in the current list 3971 * 3972 * Evaluate a predicate result for the current node. 3973 * A PredicateExpr is evaluated by evaluating the Expr and converting 3974 * the result to a boolean. If the result is a number, the result will 3975 * be converted to true if the number is equal to the position of the 3976 * context node in the context node list (as returned by the position 3977 * function) and will be converted to false otherwise; if the result 3978 * is not a number, then the result will be converted as if by a call 3979 * to the boolean function. 3980 */ 3981int 3982xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt, 3983 xmlXPathObjectPtr res, int index) { 3984 if (res == NULL) return(0); 3985 switch (res->type) { 3986 case XPATH_BOOLEAN: 3987 return(res->boolval); 3988 case XPATH_NUMBER: 3989 return(res->floatval == index); 3990 case XPATH_NODESET: 3991 return(res->nodesetval->nodeNr != 0); 3992 case XPATH_STRING: 3993 return((res->stringval != NULL) && 3994 (xmlStrlen(res->stringval) != 0)); 3995 default: 3996 STRANGE 3997 } 3998 return(0); 3999} 4000 4001/** 4002 * xmlXPathEvalPredicate: 4003 * @ctxt: the XPath Parser context 4004 * 4005 * [8] Predicate ::= '[' PredicateExpr ']' 4006 * [9] PredicateExpr ::= Expr 4007 * 4008 * Parse and evaluate a predicate for all the elements of the 4009 * current node list. Then refine the list by removing all 4010 * nodes where the predicate is false. 4011 */ 4012void 4013xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) { 4014 const xmlChar *cur; 4015 xmlXPathObjectPtr res; 4016 xmlNodeSetPtr newset = NULL; 4017 int i; 4018 4019 SKIP_BLANKS; 4020 if (CUR != '[') { 4021 ERROR(XPATH_INVALID_PREDICATE_ERROR); 4022 } 4023 NEXT; 4024 SKIP_BLANKS; 4025 if ((ctxt->context->nodelist == NULL) || 4026 (ctxt->context->nodelist->nodeNr == 0)) { 4027 ctxt->context->node = NULL; 4028 xmlXPathEvalExpr(ctxt); 4029 CHECK_ERROR; 4030 res = valuePop(ctxt); 4031 if (res != NULL) 4032 xmlXPathFreeObject(res); 4033 } else { 4034 cur = ctxt->cur; 4035 newset = xmlXPathNodeSetCreate(NULL); 4036 for (i = 0; i < ctxt->context->nodelist->nodeNr; i++) { 4037 ctxt->cur = cur; 4038 ctxt->context->node = ctxt->context->nodelist->nodeTab[i]; 4039 xmlXPathEvalExpr(ctxt); 4040 CHECK_ERROR; 4041 res = valuePop(ctxt); 4042 if (xmlXPathEvaluatePredicateResult(ctxt, res, i + 1)) 4043 xmlXPathNodeSetAdd(newset, 4044 ctxt->context->nodelist->nodeTab[i]); 4045 if (res != NULL) 4046 xmlXPathFreeObject(res); 4047 } 4048 if (ctxt->context->nodelist != NULL) 4049 xmlXPathFreeNodeSet(ctxt->context->nodelist); 4050 ctxt->context->nodelist = newset; 4051 ctxt->context->node = NULL; 4052 } 4053 if (CUR != ']') { 4054 ERROR(XPATH_INVALID_PREDICATE_ERROR); 4055 } 4056 NEXT; 4057 SKIP_BLANKS; 4058#ifdef DEBUG_STEP 4059 fprintf(xmlXPathDebug, "After predicate : "); 4060 xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist); 4061#endif 4062} 4063 4064/** 4065 * xmlXPathEvalBasis: 4066 * @ctxt: the XPath Parser context 4067 * 4068 * [5] Basis ::= AxisName '::' NodeTest 4069 * | AbbreviatedBasis 4070 * [13] AbbreviatedBasis ::= NodeTest 4071 * | '@' NodeTest 4072 * [7] NodeTest ::= WildcardName 4073 * | NodeType '(' ')' 4074 * | 'processing-instruction' '(' Literal ')' 4075 * [37] WildcardName ::= '*' 4076 * | NCName ':' '*' 4077 * | QName 4078 * 4079 * Evaluate one step in a Location Path 4080 */ 4081void 4082xmlXPathEvalBasis(xmlXPathParserContextPtr ctxt) { 4083 xmlChar *name = NULL; 4084 xmlChar *prefix = NULL; 4085 int type = 0; 4086 int axis = AXIS_CHILD; /* the default on abbreviated syntax */ 4087 int nodetest = NODE_TEST_NONE; 4088 int nodetype = 0; 4089 xmlNodeSetPtr newset = NULL; 4090 4091 if (CUR == '@') { 4092 NEXT; 4093 axis = AXIS_ATTRIBUTE; 4094 goto parse_NodeTest; 4095 } else if (CUR == '*') { 4096 NEXT; 4097 nodetest = NODE_TEST_ALL; 4098 } else { 4099 name = xmlXPathParseNCName(ctxt); 4100 if (name == NULL) { 4101 ERROR(XPATH_EXPR_ERROR); 4102 } 4103 type = xmlXPathGetNameType(ctxt, name); 4104 switch (type) { 4105 case IS_FUNCTION: { 4106 xmlXPathFunction func; 4107 int nbargs = 0; 4108 xmlXPathObjectPtr top; 4109 4110 top = ctxt->value; 4111 func = xmlXPathIsFunction(ctxt, name); 4112 if (func == NULL) { 4113 xmlFree(name); 4114 ERROR(XPATH_UNKNOWN_FUNC_ERROR); 4115 } 4116#ifdef DEBUG_EXPR 4117 fprintf(xmlXPathDebug, "Calling function %s\n", name); 4118#endif 4119 4120 if (CUR != '(') { 4121 xmlFree(name); 4122 ERROR(XPATH_EXPR_ERROR); 4123 } 4124 NEXT; 4125 4126 while (CUR != ')') { 4127 xmlXPathEvalExpr(ctxt); 4128 nbargs++; 4129 if (CUR == ')') break; 4130 if (CUR != ',') { 4131 xmlFree(name); 4132 ERROR(XPATH_EXPR_ERROR); 4133 } 4134 NEXT; 4135 } 4136 NEXT; 4137 xmlFree(name); 4138 func(ctxt, nbargs); 4139 if ((ctxt->value != top) && 4140 (ctxt->value != NULL) && 4141 (ctxt->value->type == XPATH_NODESET)) { 4142 xmlXPathObjectPtr cur; 4143 4144 cur = valuePop(ctxt); 4145 ctxt->context->nodelist = cur->nodesetval; 4146 ctxt->context->node = NULL; 4147 cur->nodesetval = NULL; 4148 xmlXPathFreeObject(cur); 4149 } 4150 return; 4151 } 4152 /* 4153 * Simple case: no axis seach all given node types. 4154 */ 4155 case NODE_TYPE_COMMENT: 4156 if ((CUR != '(') || (NXT(1) != ')')) break; 4157 SKIP(2); 4158 nodetest = NODE_TEST_TYPE; 4159 nodetype = XML_COMMENT_NODE; 4160 goto search_nodes; 4161 case NODE_TYPE_TEXT: 4162 if ((CUR != '(') || (NXT(1) != ')')) break; 4163 SKIP(2); 4164 nodetest = NODE_TEST_TYPE; 4165 nodetype = XML_TEXT_NODE; 4166 goto search_nodes; 4167 case NODE_TYPE_NODE: 4168 if ((CUR != '(') || (NXT(1) != ')')) { 4169 nodetest = NODE_TEST_NAME; 4170 break; 4171 } 4172 SKIP(2); 4173 nodetest = NODE_TEST_TYPE; 4174 nodetype = XML_ELEMENT_NODE; 4175 goto search_nodes; 4176 case NODE_TYPE_PI: 4177 if (CUR != '(') break; 4178 if (name != NULL) xmlFree(name); 4179 name = NULL; 4180 if (NXT(1) != ')') { 4181 xmlXPathObjectPtr cur; 4182 4183 /* 4184 * Specific case: search a PI by name. 4185 */ 4186 NEXT; 4187 nodetest = NODE_TEST_PI; 4188 xmlXPathEvalLiteral(ctxt); 4189 CHECK_ERROR; 4190 if (CUR != ')') 4191 ERROR(XPATH_UNCLOSED_ERROR); 4192 NEXT; 4193 xmlXPathStringFunction(ctxt, 1); 4194 CHECK_ERROR; 4195 cur = valuePop(ctxt); 4196 name = xmlStrdup(cur->stringval); 4197 xmlXPathFreeObject(cur); 4198 } else 4199 SKIP(2); 4200 nodetest = NODE_TEST_PI; 4201 goto search_nodes; 4202 4203 /* 4204 * Handling of the compund form: got the axis. 4205 */ 4206 case AXIS_ANCESTOR: 4207 case AXIS_ANCESTOR_OR_SELF: 4208 case AXIS_ATTRIBUTE: 4209 case AXIS_CHILD: 4210 case AXIS_DESCENDANT: 4211 case AXIS_DESCENDANT_OR_SELF: 4212 case AXIS_FOLLOWING: 4213 case AXIS_FOLLOWING_SIBLING: 4214 case AXIS_NAMESPACE: 4215 case AXIS_PARENT: 4216 case AXIS_PRECEDING: 4217 case AXIS_PRECEDING_SIBLING: 4218 case AXIS_SELF: 4219 if ((CUR != ':') || (NXT(1) != ':')) { 4220 nodetest = NODE_TEST_NAME; 4221 break; 4222 } 4223 SKIP(2); 4224 axis = type; 4225 break; 4226 4227 /* 4228 * Default: abbreviated syntax the axis is AXIS_CHILD 4229 */ 4230 default: 4231 nodetest = NODE_TEST_NAME; 4232 } 4233parse_NodeTest: 4234 if (nodetest == NODE_TEST_NONE) { 4235 if (CUR == '*') { 4236 NEXT; 4237 nodetest = NODE_TEST_ALL; 4238 } else { 4239 if (name != NULL) 4240 xmlFree(name); 4241 name = xmlXPathParseQName(ctxt, &prefix); 4242 if (name == NULL) { 4243 ERROR(XPATH_EXPR_ERROR); 4244 } 4245 type = xmlXPathGetNameType(ctxt, name); 4246 switch (type) { 4247 /* 4248 * Simple case: no axis seach all given node types. 4249 */ 4250 case NODE_TYPE_COMMENT: 4251 if ((CUR != '(') || (NXT(1) != ')')) break; 4252 SKIP(2); 4253 nodetest = NODE_TEST_TYPE; 4254 nodetype = XML_COMMENT_NODE; 4255 goto search_nodes; 4256 case NODE_TYPE_TEXT: 4257 if ((CUR != '(') || (NXT(1) != ')')) break; 4258 SKIP(2); 4259 nodetest = NODE_TEST_TYPE; 4260 nodetype = XML_TEXT_NODE; 4261 goto search_nodes; 4262 case NODE_TYPE_NODE: 4263 if ((CUR != '(') || (NXT(1) != ')')) { 4264 nodetest = NODE_TEST_NAME; 4265 break; 4266 } 4267 SKIP(2); 4268 nodetest = NODE_TEST_TYPE; 4269 nodetype = XML_ELEMENT_NODE; 4270 goto search_nodes; 4271 case NODE_TYPE_PI: 4272 if (CUR != '(') break; 4273 if (name != NULL) xmlFree(name); 4274 name = NULL; 4275 if (NXT(1) != ')') { 4276 xmlXPathObjectPtr cur; 4277 4278 /* 4279 * Specific case: search a PI by name. 4280 */ 4281 NEXT; 4282 nodetest = NODE_TEST_PI; 4283 xmlXPathEvalLiteral(ctxt); 4284 CHECK_ERROR; 4285 if (CUR != ')') 4286 ERROR(XPATH_UNCLOSED_ERROR); 4287 NEXT; 4288 xmlXPathStringFunction(ctxt, 1); 4289 CHECK_ERROR; 4290 cur = valuePop(ctxt); 4291 name = xmlStrdup(cur->stringval); 4292 xmlXPathFreeObject(cur); 4293 } else 4294 SKIP(2); 4295 nodetest = NODE_TEST_PI; 4296 goto search_nodes; 4297 } 4298 nodetest = NODE_TEST_NAME; 4299 } 4300 } else if ((CUR == ':') && (nodetest == NODE_TEST_NAME)) { 4301 NEXT; 4302 prefix = name; 4303 if (CUR == '*') { 4304 NEXT; 4305 nodetest = NODE_TEST_ALL; 4306 } else 4307 name = xmlXPathParseNCName(ctxt); 4308 } else if (name == NULL) 4309 ERROR(XPATH_EXPR_ERROR); 4310 } 4311 4312search_nodes: 4313 4314#ifdef DEBUG_STEP 4315 fprintf(xmlXPathDebug, "Basis : computing new set\n"); 4316#endif 4317 newset = xmlXPathNodeCollectAndTest(ctxt, axis, nodetest, nodetype, 4318 prefix, name); 4319 if (ctxt->context->nodelist != NULL) 4320 xmlXPathFreeNodeSet(ctxt->context->nodelist); 4321 ctxt->context->nodelist = newset; 4322 ctxt->context->node = NULL; 4323#ifdef DEBUG_STEP 4324 fprintf(xmlXPathDebug, "Basis : "); 4325 xmlXPathDebugNodeSet(stdout, ctxt->context->nodelist); 4326#endif 4327 if (name != NULL) xmlFree(name); 4328 if (prefix != NULL) xmlFree(prefix); 4329} 4330 4331/** 4332 * xmlXPathEvalStep: 4333 * @ctxt: the XPath Parser context 4334 * 4335 * [4] Step ::= Basis Predicate* 4336 * | AbbreviatedStep 4337 * [12] AbbreviatedStep ::= '.' 4338 * | '..' 4339 * 4340 * Evaluate one step in a Location Path 4341 * A location step of . is short for self::node(). This is 4342 * particularly useful in conjunction with //. For example, the 4343 * location path .//para is short for 4344 * self::node()/descendant-or-self::node()/child::para 4345 * and so will select all para descendant elements of the context 4346 * node. 4347 * Similarly, a location step of .. is short for parent::node(). 4348 * For example, ../title is short for parent::node()/child::title 4349 * and so will select the title children of the parent of the context 4350 * node. 4351 */ 4352void 4353xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) { 4354 xmlNodeSetPtr newset = NULL; 4355 4356 SKIP_BLANKS; 4357 if ((CUR == '.') && (NXT(1) == '.')) { 4358 SKIP(2); 4359 SKIP_BLANKS; 4360 if (ctxt->context->nodelist == NULL) { 4361 STRANGE 4362 xmlXPathRoot(ctxt); 4363 } 4364 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_PARENT, 4365 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL); 4366 if (ctxt->context->nodelist != NULL) 4367 xmlXPathFreeNodeSet(ctxt->context->nodelist); 4368 ctxt->context->nodelist = newset; 4369 ctxt->context->node = NULL; 4370 } else if (CUR == '.') { 4371 NEXT; 4372 SKIP_BLANKS; 4373 } else { 4374 xmlXPathEvalBasis(ctxt); 4375 SKIP_BLANKS; 4376 while (CUR == '[') { 4377 xmlXPathEvalPredicate(ctxt); 4378 } 4379 } 4380#ifdef DEBUG_STEP 4381 fprintf(xmlXPathDebug, "Step : "); 4382 xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist); 4383#endif 4384} 4385 4386/** 4387 * xmlXPathEvalRelativeLocationPath: 4388 * @ctxt: the XPath Parser context 4389 * 4390 * [3] RelativeLocationPath ::= Step 4391 * | RelativeLocationPath '/' Step 4392 * | AbbreviatedRelativeLocationPath 4393 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step 4394 * 4395 */ 4396void 4397xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt) { 4398 xmlNodeSetPtr newset = NULL; 4399 4400 SKIP_BLANKS; 4401 xmlXPathEvalStep(ctxt); 4402 SKIP_BLANKS; 4403 while (CUR == '/') { 4404 if ((CUR == '/') && (NXT(1) == '/')) { 4405 SKIP(2); 4406 SKIP_BLANKS; 4407 if (ctxt->context->nodelist == NULL) { 4408 STRANGE 4409 xmlXPathRoot(ctxt); 4410 } 4411 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF, 4412 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL); 4413 if (ctxt->context->nodelist != NULL) 4414 xmlXPathFreeNodeSet(ctxt->context->nodelist); 4415 ctxt->context->nodelist = newset; 4416 ctxt->context->node = NULL; 4417 xmlXPathEvalStep(ctxt); 4418 } else if (CUR == '/') { 4419 NEXT; 4420 SKIP_BLANKS; 4421 xmlXPathEvalStep(ctxt); 4422 } 4423 SKIP_BLANKS; 4424 } 4425} 4426 4427/** 4428 * xmlXPathEvalLocationPath: 4429 * @ctxt: the XPath Parser context 4430 * 4431 * [1] LocationPath ::= RelativeLocationPath 4432 * | AbsoluteLocationPath 4433 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath? 4434 * | AbbreviatedAbsoluteLocationPath 4435 * [10] AbbreviatedAbsoluteLocationPath ::= 4436 * '//' RelativeLocationPath 4437 * 4438 * // is short for /descendant-or-self::node()/. For example, 4439 * //para is short for /descendant-or-self::node()/child::para and 4440 * so will select any para element in the document (even a para element 4441 * that is a document element will be selected by //para since the 4442 * document element node is a child of the root node); div//para is 4443 * short for div/descendant-or-self::node()/child::para and so will 4444 * select all para descendants of div children. 4445 */ 4446void 4447xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt) { 4448 xmlNodeSetPtr newset = NULL; 4449 4450 SKIP_BLANKS; 4451 if (CUR != '/') { 4452 xmlXPathEvalRelativeLocationPath(ctxt); 4453 } else { 4454 while (CUR == '/') { 4455 if ((CUR == '/') && (NXT(1) == '/')) { 4456 SKIP(2); 4457 SKIP_BLANKS; 4458 if (ctxt->context->nodelist == NULL) 4459 xmlXPathRoot(ctxt); 4460 newset = xmlXPathNodeCollectAndTest(ctxt, 4461 AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE, 4462 XML_ELEMENT_NODE, NULL, NULL); 4463 if (ctxt->context->nodelist != NULL) 4464 xmlXPathFreeNodeSet(ctxt->context->nodelist); 4465 ctxt->context->nodelist = newset; 4466 ctxt->context->node = NULL; 4467 xmlXPathEvalRelativeLocationPath(ctxt); 4468 } else if (CUR == '/') { 4469 NEXT; 4470 SKIP_BLANKS; 4471 xmlXPathRoot(ctxt); 4472 if (CUR != 0) 4473 xmlXPathEvalRelativeLocationPath(ctxt); 4474 } else { 4475 xmlXPathEvalRelativeLocationPath(ctxt); 4476 } 4477 } 4478 } 4479} 4480 4481/** 4482 * xmlXPathEval: 4483 * @str: the XPath expression 4484 * @ctxt: the XPath context 4485 * 4486 * Evaluate the XPath Location Path in the given context. 4487 * 4488 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL. 4489 * the caller has to free the object. 4490 */ 4491xmlXPathObjectPtr 4492xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctxt) { 4493 xmlXPathParserContextPtr pctxt; 4494 xmlXPathObjectPtr res = NULL, tmp; 4495 int stack = 0; 4496 4497 xmlXPathInit(); 4498 4499 CHECK_CONTEXT 4500 4501 if (xmlXPathDebug == NULL) 4502 xmlXPathDebug = stderr; 4503 pctxt = xmlXPathNewParserContext(str, ctxt); 4504 if (str[0] == '/') 4505 xmlXPathRoot(pctxt); 4506 xmlXPathEvalLocationPath(pctxt); 4507 4508 /* TODO: cleanup nodelist, res = valuePop(pctxt); */ 4509 do { 4510 tmp = valuePop(pctxt); 4511 if (tmp != NULL) { 4512 xmlXPathFreeObject(tmp); 4513 stack++; 4514 } 4515 } while (tmp != NULL); 4516 if (stack != 0) { 4517 fprintf(xmlXPathDebug, "xmlXPathEval: %d object left on the stack\n", 4518 stack); 4519 } 4520 if (pctxt->error == XPATH_EXPRESSION_OK) 4521 res = xmlXPathNewNodeSetList(pctxt->context->nodelist); 4522 else 4523 res = NULL; 4524 xmlXPathFreeParserContext(pctxt); 4525 return(res); 4526} 4527 4528/** 4529 * xmlXPathEvalExpression: 4530 * @str: the XPath expression 4531 * @ctxt: the XPath context 4532 * 4533 * Evaluate the XPath expression in the given context. 4534 * 4535 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. 4536 * the caller has to free the object. 4537 */ 4538xmlXPathObjectPtr 4539xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) { 4540 xmlXPathParserContextPtr pctxt; 4541 xmlXPathObjectPtr res, tmp; 4542 int stack = 0; 4543 4544 xmlXPathInit(); 4545 4546 CHECK_CONTEXT 4547 4548 if (xmlXPathDebug == NULL) 4549 xmlXPathDebug = stderr; 4550 pctxt = xmlXPathNewParserContext(str, ctxt); 4551 xmlXPathEvalExpr(pctxt); 4552 4553 res = valuePop(pctxt); 4554 do { 4555 tmp = valuePop(pctxt); 4556 if (tmp != NULL) { 4557 xmlXPathFreeObject(tmp); 4558 stack++; 4559 } 4560 } while (tmp != NULL); 4561 if (stack != 0) { 4562 fprintf(xmlXPathDebug, "xmlXPathEval: %d object left on the stack\n", 4563 stack); 4564 } 4565 xmlXPathFreeParserContext(pctxt); 4566 return(res); 4567} 4568 4569