Annotation of embedaddon/libxml2/xpath.c, revision 1.1
1.1 ! misho 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: *f
! 6: * Reference: W3C Recommendation 16 November 1999
! 7: * http://www.w3.org/TR/1999/REC-xpath-19991116
! 8: * Public reference:
! 9: * http://www.w3.org/TR/xpath
! 10: *
! 11: * See Copyright for the status of this software
! 12: *
! 13: * Author: daniel@veillard.com
! 14: *
! 15: */
! 16:
! 17: #define IN_LIBXML
! 18: #include "libxml.h"
! 19:
! 20: #include <string.h>
! 21:
! 22: #ifdef HAVE_SYS_TYPES_H
! 23: #include <sys/types.h>
! 24: #endif
! 25: #ifdef HAVE_MATH_H
! 26: #include <math.h>
! 27: #endif
! 28: #ifdef HAVE_FLOAT_H
! 29: #include <float.h>
! 30: #endif
! 31: #ifdef HAVE_CTYPE_H
! 32: #include <ctype.h>
! 33: #endif
! 34: #ifdef HAVE_SIGNAL_H
! 35: #include <signal.h>
! 36: #endif
! 37:
! 38: #include <libxml/xmlmemory.h>
! 39: #include <libxml/tree.h>
! 40: #include <libxml/valid.h>
! 41: #include <libxml/xpath.h>
! 42: #include <libxml/xpathInternals.h>
! 43: #include <libxml/parserInternals.h>
! 44: #include <libxml/hash.h>
! 45: #ifdef LIBXML_XPTR_ENABLED
! 46: #include <libxml/xpointer.h>
! 47: #endif
! 48: #ifdef LIBXML_DEBUG_ENABLED
! 49: #include <libxml/debugXML.h>
! 50: #endif
! 51: #include <libxml/xmlerror.h>
! 52: #include <libxml/threads.h>
! 53: #include <libxml/globals.h>
! 54: #ifdef LIBXML_PATTERN_ENABLED
! 55: #include <libxml/pattern.h>
! 56: #endif
! 57:
! 58: #ifdef LIBXML_PATTERN_ENABLED
! 59: #define XPATH_STREAMING
! 60: #endif
! 61:
! 62: #define TODO \
! 63: xmlGenericError(xmlGenericErrorContext, \
! 64: "Unimplemented block at %s:%d\n", \
! 65: __FILE__, __LINE__);
! 66:
! 67: /*
! 68: * XP_OPTIMIZED_NON_ELEM_COMPARISON:
! 69: * If defined, this will use xmlXPathCmpNodesExt() instead of
! 70: * xmlXPathCmpNodes(). The new function is optimized comparison of
! 71: * non-element nodes; actually it will speed up comparison only if
! 72: * xmlXPathOrderDocElems() was called in order to index the elements of
! 73: * a tree in document order; Libxslt does such an indexing, thus it will
! 74: * benefit from this optimization.
! 75: */
! 76: #define XP_OPTIMIZED_NON_ELEM_COMPARISON
! 77:
! 78: /*
! 79: * XP_OPTIMIZED_FILTER_FIRST:
! 80: * If defined, this will optimize expressions like "key('foo', 'val')[b][1]"
! 81: * in a way, that it stop evaluation at the first node.
! 82: */
! 83: #define XP_OPTIMIZED_FILTER_FIRST
! 84:
! 85: /*
! 86: * XP_DEBUG_OBJ_USAGE:
! 87: * Internal flag to enable tracking of how much XPath objects have been
! 88: * created.
! 89: */
! 90: /* #define XP_DEBUG_OBJ_USAGE */
! 91:
! 92: /*
! 93: * TODO:
! 94: * There are a few spots where some tests are done which depend upon ascii
! 95: * data. These should be enhanced for full UTF8 support (see particularly
! 96: * any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT)
! 97: */
! 98:
! 99: #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
! 100:
! 101: /************************************************************************
! 102: * *
! 103: * Floating point stuff *
! 104: * *
! 105: ************************************************************************/
! 106:
! 107: #ifndef TRIO_REPLACE_STDIO
! 108: #define TRIO_PUBLIC static
! 109: #endif
! 110: #include "trionan.c"
! 111:
! 112: /*
! 113: * The lack of portability of this section of the libc is annoying !
! 114: */
! 115: double xmlXPathNAN = 0;
! 116: double xmlXPathPINF = 1;
! 117: double xmlXPathNINF = -1;
! 118: static double xmlXPathNZERO = 0; /* not exported from headers */
! 119: static int xmlXPathInitialized = 0;
! 120:
! 121: /**
! 122: * xmlXPathInit:
! 123: *
! 124: * Initialize the XPath environment
! 125: */
! 126: void
! 127: xmlXPathInit(void) {
! 128: if (xmlXPathInitialized) return;
! 129:
! 130: xmlXPathPINF = trio_pinf();
! 131: xmlXPathNINF = trio_ninf();
! 132: xmlXPathNAN = trio_nan();
! 133: xmlXPathNZERO = trio_nzero();
! 134:
! 135: xmlXPathInitialized = 1;
! 136: }
! 137:
! 138: /**
! 139: * xmlXPathIsNaN:
! 140: * @val: a double value
! 141: *
! 142: * Provides a portable isnan() function to detect whether a double
! 143: * is a NotaNumber. Based on trio code
! 144: * http://sourceforge.net/projects/ctrio/
! 145: *
! 146: * Returns 1 if the value is a NaN, 0 otherwise
! 147: */
! 148: int
! 149: xmlXPathIsNaN(double val) {
! 150: return(trio_isnan(val));
! 151: }
! 152:
! 153: /**
! 154: * xmlXPathIsInf:
! 155: * @val: a double value
! 156: *
! 157: * Provides a portable isinf() function to detect whether a double
! 158: * is a +Infinite or -Infinite. Based on trio code
! 159: * http://sourceforge.net/projects/ctrio/
! 160: *
! 161: * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise
! 162: */
! 163: int
! 164: xmlXPathIsInf(double val) {
! 165: return(trio_isinf(val));
! 166: }
! 167:
! 168: #endif /* SCHEMAS or XPATH */
! 169: #ifdef LIBXML_XPATH_ENABLED
! 170: /**
! 171: * xmlXPathGetSign:
! 172: * @val: a double value
! 173: *
! 174: * Provides a portable function to detect the sign of a double
! 175: * Modified from trio code
! 176: * http://sourceforge.net/projects/ctrio/
! 177: *
! 178: * Returns 1 if the value is Negative, 0 if positive
! 179: */
! 180: static int
! 181: xmlXPathGetSign(double val) {
! 182: return(trio_signbit(val));
! 183: }
! 184:
! 185:
! 186: /*
! 187: * TODO: when compatibility allows remove all "fake node libxslt" strings
! 188: * the test should just be name[0] = ' '
! 189: */
! 190: #ifdef DEBUG_XPATH_EXPRESSION
! 191: #define DEBUG_STEP
! 192: #define DEBUG_EXPR
! 193: #define DEBUG_EVAL_COUNTS
! 194: #endif
! 195:
! 196: static xmlNs xmlXPathXMLNamespaceStruct = {
! 197: NULL,
! 198: XML_NAMESPACE_DECL,
! 199: XML_XML_NAMESPACE,
! 200: BAD_CAST "xml",
! 201: NULL,
! 202: NULL
! 203: };
! 204: static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct;
! 205: #ifndef LIBXML_THREAD_ENABLED
! 206: /*
! 207: * Optimizer is disabled only when threaded apps are detected while
! 208: * the library ain't compiled for thread safety.
! 209: */
! 210: static int xmlXPathDisableOptimizer = 0;
! 211: #endif
! 212:
! 213: /************************************************************************
! 214: * *
! 215: * Error handling routines *
! 216: * *
! 217: ************************************************************************/
! 218:
! 219: /**
! 220: * XP_ERRORNULL:
! 221: * @X: the error code
! 222: *
! 223: * Macro to raise an XPath error and return NULL.
! 224: */
! 225: #define XP_ERRORNULL(X) \
! 226: { xmlXPathErr(ctxt, X); return(NULL); }
! 227:
! 228: /*
! 229: * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError
! 230: */
! 231: static const char *xmlXPathErrorMessages[] = {
! 232: "Ok\n",
! 233: "Number encoding\n",
! 234: "Unfinished literal\n",
! 235: "Start of literal\n",
! 236: "Expected $ for variable reference\n",
! 237: "Undefined variable\n",
! 238: "Invalid predicate\n",
! 239: "Invalid expression\n",
! 240: "Missing closing curly brace\n",
! 241: "Unregistered function\n",
! 242: "Invalid operand\n",
! 243: "Invalid type\n",
! 244: "Invalid number of arguments\n",
! 245: "Invalid context size\n",
! 246: "Invalid context position\n",
! 247: "Memory allocation error\n",
! 248: "Syntax error\n",
! 249: "Resource error\n",
! 250: "Sub resource error\n",
! 251: "Undefined namespace prefix\n",
! 252: "Encoding error\n",
! 253: "Char out of XML range\n",
! 254: "Invalid or incomplete context\n",
! 255: "?? Unknown error ??\n" /* Must be last in the list! */
! 256: };
! 257: #define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \
! 258: sizeof(xmlXPathErrorMessages[0])) - 1)
! 259: /**
! 260: * xmlXPathErrMemory:
! 261: * @ctxt: an XPath context
! 262: * @extra: extra informations
! 263: *
! 264: * Handle a redefinition of attribute error
! 265: */
! 266: static void
! 267: xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra)
! 268: {
! 269: if (ctxt != NULL) {
! 270: if (extra) {
! 271: xmlChar buf[200];
! 272:
! 273: xmlStrPrintf(buf, 200,
! 274: BAD_CAST "Memory allocation failed : %s\n",
! 275: extra);
! 276: ctxt->lastError.message = (char *) xmlStrdup(buf);
! 277: } else {
! 278: ctxt->lastError.message = (char *)
! 279: xmlStrdup(BAD_CAST "Memory allocation failed\n");
! 280: }
! 281: ctxt->lastError.domain = XML_FROM_XPATH;
! 282: ctxt->lastError.code = XML_ERR_NO_MEMORY;
! 283: if (ctxt->error != NULL)
! 284: ctxt->error(ctxt->userData, &ctxt->lastError);
! 285: } else {
! 286: if (extra)
! 287: __xmlRaiseError(NULL, NULL, NULL,
! 288: NULL, NULL, XML_FROM_XPATH,
! 289: XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
! 290: extra, NULL, NULL, 0, 0,
! 291: "Memory allocation failed : %s\n", extra);
! 292: else
! 293: __xmlRaiseError(NULL, NULL, NULL,
! 294: NULL, NULL, XML_FROM_XPATH,
! 295: XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
! 296: NULL, NULL, NULL, 0, 0,
! 297: "Memory allocation failed\n");
! 298: }
! 299: }
! 300:
! 301: /**
! 302: * xmlXPathPErrMemory:
! 303: * @ctxt: an XPath parser context
! 304: * @extra: extra informations
! 305: *
! 306: * Handle a redefinition of attribute error
! 307: */
! 308: static void
! 309: xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra)
! 310: {
! 311: if (ctxt == NULL)
! 312: xmlXPathErrMemory(NULL, extra);
! 313: else {
! 314: ctxt->error = XPATH_MEMORY_ERROR;
! 315: xmlXPathErrMemory(ctxt->context, extra);
! 316: }
! 317: }
! 318:
! 319: /**
! 320: * xmlXPathErr:
! 321: * @ctxt: a XPath parser context
! 322: * @error: the error code
! 323: *
! 324: * Handle an XPath error
! 325: */
! 326: void
! 327: xmlXPathErr(xmlXPathParserContextPtr ctxt, int error)
! 328: {
! 329: if ((error < 0) || (error > MAXERRNO))
! 330: error = MAXERRNO;
! 331: if (ctxt == NULL) {
! 332: __xmlRaiseError(NULL, NULL, NULL,
! 333: NULL, NULL, XML_FROM_XPATH,
! 334: error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
! 335: XML_ERR_ERROR, NULL, 0,
! 336: NULL, NULL, NULL, 0, 0,
! 337: "%s", xmlXPathErrorMessages[error]);
! 338: return;
! 339: }
! 340: ctxt->error = error;
! 341: if (ctxt->context == NULL) {
! 342: __xmlRaiseError(NULL, NULL, NULL,
! 343: NULL, NULL, XML_FROM_XPATH,
! 344: error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
! 345: XML_ERR_ERROR, NULL, 0,
! 346: (const char *) ctxt->base, NULL, NULL,
! 347: ctxt->cur - ctxt->base, 0,
! 348: "%s", xmlXPathErrorMessages[error]);
! 349: return;
! 350: }
! 351:
! 352: /* cleanup current last error */
! 353: xmlResetError(&ctxt->context->lastError);
! 354:
! 355: ctxt->context->lastError.domain = XML_FROM_XPATH;
! 356: ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK -
! 357: XPATH_EXPRESSION_OK;
! 358: ctxt->context->lastError.level = XML_ERR_ERROR;
! 359: ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base);
! 360: ctxt->context->lastError.int1 = ctxt->cur - ctxt->base;
! 361: ctxt->context->lastError.node = ctxt->context->debugNode;
! 362: if (ctxt->context->error != NULL) {
! 363: ctxt->context->error(ctxt->context->userData,
! 364: &ctxt->context->lastError);
! 365: } else {
! 366: __xmlRaiseError(NULL, NULL, NULL,
! 367: NULL, ctxt->context->debugNode, XML_FROM_XPATH,
! 368: error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
! 369: XML_ERR_ERROR, NULL, 0,
! 370: (const char *) ctxt->base, NULL, NULL,
! 371: ctxt->cur - ctxt->base, 0,
! 372: "%s", xmlXPathErrorMessages[error]);
! 373: }
! 374:
! 375: }
! 376:
! 377: /**
! 378: * xmlXPatherror:
! 379: * @ctxt: the XPath Parser context
! 380: * @file: the file name
! 381: * @line: the line number
! 382: * @no: the error number
! 383: *
! 384: * Formats an error message.
! 385: */
! 386: void
! 387: xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED,
! 388: int line ATTRIBUTE_UNUSED, int no) {
! 389: xmlXPathErr(ctxt, no);
! 390: }
! 391:
! 392: /************************************************************************
! 393: * *
! 394: * Utilities *
! 395: * *
! 396: ************************************************************************/
! 397:
! 398: /**
! 399: * xsltPointerList:
! 400: *
! 401: * Pointer-list for various purposes.
! 402: */
! 403: typedef struct _xmlPointerList xmlPointerList;
! 404: typedef xmlPointerList *xmlPointerListPtr;
! 405: struct _xmlPointerList {
! 406: void **items;
! 407: int number;
! 408: int size;
! 409: };
! 410: /*
! 411: * TODO: Since such a list-handling is used in xmlschemas.c and libxslt
! 412: * and here, we should make the functions public.
! 413: */
! 414: static int
! 415: xmlPointerListAddSize(xmlPointerListPtr list,
! 416: void *item,
! 417: int initialSize)
! 418: {
! 419: if (list->items == NULL) {
! 420: if (initialSize <= 0)
! 421: initialSize = 1;
! 422: list->items = (void **) xmlMalloc(
! 423: initialSize * sizeof(void *));
! 424: if (list->items == NULL) {
! 425: xmlXPathErrMemory(NULL,
! 426: "xmlPointerListCreate: allocating item\n");
! 427: return(-1);
! 428: }
! 429: list->number = 0;
! 430: list->size = initialSize;
! 431: } else if (list->size <= list->number) {
! 432: list->size *= 2;
! 433: list->items = (void **) xmlRealloc(list->items,
! 434: list->size * sizeof(void *));
! 435: if (list->items == NULL) {
! 436: xmlXPathErrMemory(NULL,
! 437: "xmlPointerListCreate: re-allocating item\n");
! 438: list->size = 0;
! 439: return(-1);
! 440: }
! 441: }
! 442: list->items[list->number++] = item;
! 443: return(0);
! 444: }
! 445:
! 446: /**
! 447: * xsltPointerListCreate:
! 448: *
! 449: * Creates an xsltPointerList structure.
! 450: *
! 451: * Returns a xsltPointerList structure or NULL in case of an error.
! 452: */
! 453: static xmlPointerListPtr
! 454: xmlPointerListCreate(int initialSize)
! 455: {
! 456: xmlPointerListPtr ret;
! 457:
! 458: ret = xmlMalloc(sizeof(xmlPointerList));
! 459: if (ret == NULL) {
! 460: xmlXPathErrMemory(NULL,
! 461: "xmlPointerListCreate: allocating item\n");
! 462: return (NULL);
! 463: }
! 464: memset(ret, 0, sizeof(xmlPointerList));
! 465: if (initialSize > 0) {
! 466: xmlPointerListAddSize(ret, NULL, initialSize);
! 467: ret->number = 0;
! 468: }
! 469: return (ret);
! 470: }
! 471:
! 472: /**
! 473: * xsltPointerListFree:
! 474: *
! 475: * Frees the xsltPointerList structure. This does not free
! 476: * the content of the list.
! 477: */
! 478: static void
! 479: xmlPointerListFree(xmlPointerListPtr list)
! 480: {
! 481: if (list == NULL)
! 482: return;
! 483: if (list->items != NULL)
! 484: xmlFree(list->items);
! 485: xmlFree(list);
! 486: }
! 487:
! 488: /************************************************************************
! 489: * *
! 490: * Parser Types *
! 491: * *
! 492: ************************************************************************/
! 493:
! 494: /*
! 495: * Types are private:
! 496: */
! 497:
! 498: typedef enum {
! 499: XPATH_OP_END=0,
! 500: XPATH_OP_AND,
! 501: XPATH_OP_OR,
! 502: XPATH_OP_EQUAL,
! 503: XPATH_OP_CMP,
! 504: XPATH_OP_PLUS,
! 505: XPATH_OP_MULT,
! 506: XPATH_OP_UNION,
! 507: XPATH_OP_ROOT,
! 508: XPATH_OP_NODE,
! 509: XPATH_OP_RESET, /* 10 */
! 510: XPATH_OP_COLLECT,
! 511: XPATH_OP_VALUE, /* 12 */
! 512: XPATH_OP_VARIABLE,
! 513: XPATH_OP_FUNCTION,
! 514: XPATH_OP_ARG,
! 515: XPATH_OP_PREDICATE,
! 516: XPATH_OP_FILTER, /* 17 */
! 517: XPATH_OP_SORT /* 18 */
! 518: #ifdef LIBXML_XPTR_ENABLED
! 519: ,XPATH_OP_RANGETO
! 520: #endif
! 521: } xmlXPathOp;
! 522:
! 523: typedef enum {
! 524: AXIS_ANCESTOR = 1,
! 525: AXIS_ANCESTOR_OR_SELF,
! 526: AXIS_ATTRIBUTE,
! 527: AXIS_CHILD,
! 528: AXIS_DESCENDANT,
! 529: AXIS_DESCENDANT_OR_SELF,
! 530: AXIS_FOLLOWING,
! 531: AXIS_FOLLOWING_SIBLING,
! 532: AXIS_NAMESPACE,
! 533: AXIS_PARENT,
! 534: AXIS_PRECEDING,
! 535: AXIS_PRECEDING_SIBLING,
! 536: AXIS_SELF
! 537: } xmlXPathAxisVal;
! 538:
! 539: typedef enum {
! 540: NODE_TEST_NONE = 0,
! 541: NODE_TEST_TYPE = 1,
! 542: NODE_TEST_PI = 2,
! 543: NODE_TEST_ALL = 3,
! 544: NODE_TEST_NS = 4,
! 545: NODE_TEST_NAME = 5
! 546: } xmlXPathTestVal;
! 547:
! 548: typedef enum {
! 549: NODE_TYPE_NODE = 0,
! 550: NODE_TYPE_COMMENT = XML_COMMENT_NODE,
! 551: NODE_TYPE_TEXT = XML_TEXT_NODE,
! 552: NODE_TYPE_PI = XML_PI_NODE
! 553: } xmlXPathTypeVal;
! 554:
! 555: #define XP_REWRITE_DOS_CHILD_ELEM 1
! 556:
! 557: typedef struct _xmlXPathStepOp xmlXPathStepOp;
! 558: typedef xmlXPathStepOp *xmlXPathStepOpPtr;
! 559: struct _xmlXPathStepOp {
! 560: xmlXPathOp op; /* The identifier of the operation */
! 561: int ch1; /* First child */
! 562: int ch2; /* Second child */
! 563: int value;
! 564: int value2;
! 565: int value3;
! 566: void *value4;
! 567: void *value5;
! 568: void *cache;
! 569: void *cacheURI;
! 570: int rewriteType;
! 571: };
! 572:
! 573: struct _xmlXPathCompExpr {
! 574: int nbStep; /* Number of steps in this expression */
! 575: int maxStep; /* Maximum number of steps allocated */
! 576: xmlXPathStepOp *steps; /* ops for computation of this expression */
! 577: int last; /* index of last step in expression */
! 578: xmlChar *expr; /* the expression being computed */
! 579: xmlDictPtr dict; /* the dictionnary to use if any */
! 580: #ifdef DEBUG_EVAL_COUNTS
! 581: int nb;
! 582: xmlChar *string;
! 583: #endif
! 584: #ifdef XPATH_STREAMING
! 585: xmlPatternPtr stream;
! 586: #endif
! 587: };
! 588:
! 589: /************************************************************************
! 590: * *
! 591: * Forward declarations *
! 592: * *
! 593: ************************************************************************/
! 594: static void
! 595: xmlXPathFreeValueTree(xmlNodeSetPtr obj);
! 596: static void
! 597: xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj);
! 598: static int
! 599: xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
! 600: xmlXPathStepOpPtr op, xmlNodePtr *first);
! 601: static int
! 602: xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
! 603: xmlXPathStepOpPtr op,
! 604: int isPredicate);
! 605:
! 606: /************************************************************************
! 607: * *
! 608: * Parser Type functions *
! 609: * *
! 610: ************************************************************************/
! 611:
! 612: /**
! 613: * xmlXPathNewCompExpr:
! 614: *
! 615: * Create a new Xpath component
! 616: *
! 617: * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
! 618: */
! 619: static xmlXPathCompExprPtr
! 620: xmlXPathNewCompExpr(void) {
! 621: xmlXPathCompExprPtr cur;
! 622:
! 623: cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
! 624: if (cur == NULL) {
! 625: xmlXPathErrMemory(NULL, "allocating component\n");
! 626: return(NULL);
! 627: }
! 628: memset(cur, 0, sizeof(xmlXPathCompExpr));
! 629: cur->maxStep = 10;
! 630: cur->nbStep = 0;
! 631: cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
! 632: sizeof(xmlXPathStepOp));
! 633: if (cur->steps == NULL) {
! 634: xmlXPathErrMemory(NULL, "allocating steps\n");
! 635: xmlFree(cur);
! 636: return(NULL);
! 637: }
! 638: memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
! 639: cur->last = -1;
! 640: #ifdef DEBUG_EVAL_COUNTS
! 641: cur->nb = 0;
! 642: #endif
! 643: return(cur);
! 644: }
! 645:
! 646: /**
! 647: * xmlXPathFreeCompExpr:
! 648: * @comp: an XPATH comp
! 649: *
! 650: * Free up the memory allocated by @comp
! 651: */
! 652: void
! 653: xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
! 654: {
! 655: xmlXPathStepOpPtr op;
! 656: int i;
! 657:
! 658: if (comp == NULL)
! 659: return;
! 660: if (comp->dict == NULL) {
! 661: for (i = 0; i < comp->nbStep; i++) {
! 662: op = &comp->steps[i];
! 663: if (op->value4 != NULL) {
! 664: if (op->op == XPATH_OP_VALUE)
! 665: xmlXPathFreeObject(op->value4);
! 666: else
! 667: xmlFree(op->value4);
! 668: }
! 669: if (op->value5 != NULL)
! 670: xmlFree(op->value5);
! 671: }
! 672: } else {
! 673: for (i = 0; i < comp->nbStep; i++) {
! 674: op = &comp->steps[i];
! 675: if (op->value4 != NULL) {
! 676: if (op->op == XPATH_OP_VALUE)
! 677: xmlXPathFreeObject(op->value4);
! 678: }
! 679: }
! 680: xmlDictFree(comp->dict);
! 681: }
! 682: if (comp->steps != NULL) {
! 683: xmlFree(comp->steps);
! 684: }
! 685: #ifdef DEBUG_EVAL_COUNTS
! 686: if (comp->string != NULL) {
! 687: xmlFree(comp->string);
! 688: }
! 689: #endif
! 690: #ifdef XPATH_STREAMING
! 691: if (comp->stream != NULL) {
! 692: xmlFreePatternList(comp->stream);
! 693: }
! 694: #endif
! 695: if (comp->expr != NULL) {
! 696: xmlFree(comp->expr);
! 697: }
! 698:
! 699: xmlFree(comp);
! 700: }
! 701:
! 702: /**
! 703: * xmlXPathCompExprAdd:
! 704: * @comp: the compiled expression
! 705: * @ch1: first child index
! 706: * @ch2: second child index
! 707: * @op: an op
! 708: * @value: the first int value
! 709: * @value2: the second int value
! 710: * @value3: the third int value
! 711: * @value4: the first string value
! 712: * @value5: the second string value
! 713: *
! 714: * Add a step to an XPath Compiled Expression
! 715: *
! 716: * Returns -1 in case of failure, the index otherwise
! 717: */
! 718: static int
! 719: xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
! 720: xmlXPathOp op, int value,
! 721: int value2, int value3, void *value4, void *value5) {
! 722: if (comp->nbStep >= comp->maxStep) {
! 723: xmlXPathStepOp *real;
! 724:
! 725: comp->maxStep *= 2;
! 726: real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
! 727: comp->maxStep * sizeof(xmlXPathStepOp));
! 728: if (real == NULL) {
! 729: comp->maxStep /= 2;
! 730: xmlXPathErrMemory(NULL, "adding step\n");
! 731: return(-1);
! 732: }
! 733: comp->steps = real;
! 734: }
! 735: comp->last = comp->nbStep;
! 736: comp->steps[comp->nbStep].rewriteType = 0;
! 737: comp->steps[comp->nbStep].ch1 = ch1;
! 738: comp->steps[comp->nbStep].ch2 = ch2;
! 739: comp->steps[comp->nbStep].op = op;
! 740: comp->steps[comp->nbStep].value = value;
! 741: comp->steps[comp->nbStep].value2 = value2;
! 742: comp->steps[comp->nbStep].value3 = value3;
! 743: if ((comp->dict != NULL) &&
! 744: ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) ||
! 745: (op == XPATH_OP_COLLECT))) {
! 746: if (value4 != NULL) {
! 747: comp->steps[comp->nbStep].value4 = (xmlChar *)
! 748: (void *)xmlDictLookup(comp->dict, value4, -1);
! 749: xmlFree(value4);
! 750: } else
! 751: comp->steps[comp->nbStep].value4 = NULL;
! 752: if (value5 != NULL) {
! 753: comp->steps[comp->nbStep].value5 = (xmlChar *)
! 754: (void *)xmlDictLookup(comp->dict, value5, -1);
! 755: xmlFree(value5);
! 756: } else
! 757: comp->steps[comp->nbStep].value5 = NULL;
! 758: } else {
! 759: comp->steps[comp->nbStep].value4 = value4;
! 760: comp->steps[comp->nbStep].value5 = value5;
! 761: }
! 762: comp->steps[comp->nbStep].cache = NULL;
! 763: return(comp->nbStep++);
! 764: }
! 765:
! 766: /**
! 767: * xmlXPathCompSwap:
! 768: * @comp: the compiled expression
! 769: * @op: operation index
! 770: *
! 771: * Swaps 2 operations in the compiled expression
! 772: */
! 773: static void
! 774: xmlXPathCompSwap(xmlXPathStepOpPtr op) {
! 775: int tmp;
! 776:
! 777: #ifndef LIBXML_THREAD_ENABLED
! 778: /*
! 779: * Since this manipulates possibly shared variables, this is
! 780: * disabled if one detects that the library is used in a multithreaded
! 781: * application
! 782: */
! 783: if (xmlXPathDisableOptimizer)
! 784: return;
! 785: #endif
! 786:
! 787: tmp = op->ch1;
! 788: op->ch1 = op->ch2;
! 789: op->ch2 = tmp;
! 790: }
! 791:
! 792: #define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
! 793: xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
! 794: (op), (val), (val2), (val3), (val4), (val5))
! 795: #define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
! 796: xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
! 797: (op), (val), (val2), (val3), (val4), (val5))
! 798:
! 799: #define PUSH_LEAVE_EXPR(op, val, val2) \
! 800: xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
! 801:
! 802: #define PUSH_UNARY_EXPR(op, ch, val, val2) \
! 803: xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
! 804:
! 805: #define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
! 806: xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), \
! 807: (val), (val2), 0 ,NULL ,NULL)
! 808:
! 809: /************************************************************************
! 810: * *
! 811: * XPath object cache structures *
! 812: * *
! 813: ************************************************************************/
! 814:
! 815: /* #define XP_DEFAULT_CACHE_ON */
! 816:
! 817: #define XP_HAS_CACHE(c) ((c != NULL) && ((c)->cache != NULL))
! 818:
! 819: typedef struct _xmlXPathContextCache xmlXPathContextCache;
! 820: typedef xmlXPathContextCache *xmlXPathContextCachePtr;
! 821: struct _xmlXPathContextCache {
! 822: xmlPointerListPtr nodesetObjs; /* contains xmlXPathObjectPtr */
! 823: xmlPointerListPtr stringObjs; /* contains xmlXPathObjectPtr */
! 824: xmlPointerListPtr booleanObjs; /* contains xmlXPathObjectPtr */
! 825: xmlPointerListPtr numberObjs; /* contains xmlXPathObjectPtr */
! 826: xmlPointerListPtr miscObjs; /* contains xmlXPathObjectPtr */
! 827: int maxNodeset;
! 828: int maxString;
! 829: int maxBoolean;
! 830: int maxNumber;
! 831: int maxMisc;
! 832: #ifdef XP_DEBUG_OBJ_USAGE
! 833: int dbgCachedAll;
! 834: int dbgCachedNodeset;
! 835: int dbgCachedString;
! 836: int dbgCachedBool;
! 837: int dbgCachedNumber;
! 838: int dbgCachedPoint;
! 839: int dbgCachedRange;
! 840: int dbgCachedLocset;
! 841: int dbgCachedUsers;
! 842: int dbgCachedXSLTTree;
! 843: int dbgCachedUndefined;
! 844:
! 845:
! 846: int dbgReusedAll;
! 847: int dbgReusedNodeset;
! 848: int dbgReusedString;
! 849: int dbgReusedBool;
! 850: int dbgReusedNumber;
! 851: int dbgReusedPoint;
! 852: int dbgReusedRange;
! 853: int dbgReusedLocset;
! 854: int dbgReusedUsers;
! 855: int dbgReusedXSLTTree;
! 856: int dbgReusedUndefined;
! 857:
! 858: #endif
! 859: };
! 860:
! 861: /************************************************************************
! 862: * *
! 863: * Debugging related functions *
! 864: * *
! 865: ************************************************************************/
! 866:
! 867: #define STRANGE \
! 868: xmlGenericError(xmlGenericErrorContext, \
! 869: "Internal error at %s:%d\n", \
! 870: __FILE__, __LINE__);
! 871:
! 872: #ifdef LIBXML_DEBUG_ENABLED
! 873: static void
! 874: xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
! 875: int i;
! 876: char shift[100];
! 877:
! 878: for (i = 0;((i < depth) && (i < 25));i++)
! 879: shift[2 * i] = shift[2 * i + 1] = ' ';
! 880: shift[2 * i] = shift[2 * i + 1] = 0;
! 881: if (cur == NULL) {
! 882: fprintf(output, "%s", shift);
! 883: fprintf(output, "Node is NULL !\n");
! 884: return;
! 885:
! 886: }
! 887:
! 888: if ((cur->type == XML_DOCUMENT_NODE) ||
! 889: (cur->type == XML_HTML_DOCUMENT_NODE)) {
! 890: fprintf(output, "%s", shift);
! 891: fprintf(output, " /\n");
! 892: } else if (cur->type == XML_ATTRIBUTE_NODE)
! 893: xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
! 894: else
! 895: xmlDebugDumpOneNode(output, cur, depth);
! 896: }
! 897: static void
! 898: xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
! 899: xmlNodePtr tmp;
! 900: int i;
! 901: char shift[100];
! 902:
! 903: for (i = 0;((i < depth) && (i < 25));i++)
! 904: shift[2 * i] = shift[2 * i + 1] = ' ';
! 905: shift[2 * i] = shift[2 * i + 1] = 0;
! 906: if (cur == NULL) {
! 907: fprintf(output, "%s", shift);
! 908: fprintf(output, "Node is NULL !\n");
! 909: return;
! 910:
! 911: }
! 912:
! 913: while (cur != NULL) {
! 914: tmp = cur;
! 915: cur = cur->next;
! 916: xmlDebugDumpOneNode(output, tmp, depth);
! 917: }
! 918: }
! 919:
! 920: static void
! 921: xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
! 922: int i;
! 923: char shift[100];
! 924:
! 925: for (i = 0;((i < depth) && (i < 25));i++)
! 926: shift[2 * i] = shift[2 * i + 1] = ' ';
! 927: shift[2 * i] = shift[2 * i + 1] = 0;
! 928:
! 929: if (cur == NULL) {
! 930: fprintf(output, "%s", shift);
! 931: fprintf(output, "NodeSet is NULL !\n");
! 932: return;
! 933:
! 934: }
! 935:
! 936: if (cur != NULL) {
! 937: fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
! 938: for (i = 0;i < cur->nodeNr;i++) {
! 939: fprintf(output, "%s", shift);
! 940: fprintf(output, "%d", i + 1);
! 941: xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
! 942: }
! 943: }
! 944: }
! 945:
! 946: static void
! 947: xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
! 948: int i;
! 949: char shift[100];
! 950:
! 951: for (i = 0;((i < depth) && (i < 25));i++)
! 952: shift[2 * i] = shift[2 * i + 1] = ' ';
! 953: shift[2 * i] = shift[2 * i + 1] = 0;
! 954:
! 955: if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
! 956: fprintf(output, "%s", shift);
! 957: fprintf(output, "Value Tree is NULL !\n");
! 958: return;
! 959:
! 960: }
! 961:
! 962: fprintf(output, "%s", shift);
! 963: fprintf(output, "%d", i + 1);
! 964: xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
! 965: }
! 966: #if defined(LIBXML_XPTR_ENABLED)
! 967: static void
! 968: xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
! 969: int i;
! 970: char shift[100];
! 971:
! 972: for (i = 0;((i < depth) && (i < 25));i++)
! 973: shift[2 * i] = shift[2 * i + 1] = ' ';
! 974: shift[2 * i] = shift[2 * i + 1] = 0;
! 975:
! 976: if (cur == NULL) {
! 977: fprintf(output, "%s", shift);
! 978: fprintf(output, "LocationSet is NULL !\n");
! 979: return;
! 980:
! 981: }
! 982:
! 983: for (i = 0;i < cur->locNr;i++) {
! 984: fprintf(output, "%s", shift);
! 985: fprintf(output, "%d : ", i + 1);
! 986: xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
! 987: }
! 988: }
! 989: #endif /* LIBXML_XPTR_ENABLED */
! 990:
! 991: /**
! 992: * xmlXPathDebugDumpObject:
! 993: * @output: the FILE * to dump the output
! 994: * @cur: the object to inspect
! 995: * @depth: indentation level
! 996: *
! 997: * Dump the content of the object for debugging purposes
! 998: */
! 999: void
! 1000: xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
! 1001: int i;
! 1002: char shift[100];
! 1003:
! 1004: if (output == NULL) return;
! 1005:
! 1006: for (i = 0;((i < depth) && (i < 25));i++)
! 1007: shift[2 * i] = shift[2 * i + 1] = ' ';
! 1008: shift[2 * i] = shift[2 * i + 1] = 0;
! 1009:
! 1010:
! 1011: fprintf(output, "%s", shift);
! 1012:
! 1013: if (cur == NULL) {
! 1014: fprintf(output, "Object is empty (NULL)\n");
! 1015: return;
! 1016: }
! 1017: switch(cur->type) {
! 1018: case XPATH_UNDEFINED:
! 1019: fprintf(output, "Object is uninitialized\n");
! 1020: break;
! 1021: case XPATH_NODESET:
! 1022: fprintf(output, "Object is a Node Set :\n");
! 1023: xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
! 1024: break;
! 1025: case XPATH_XSLT_TREE:
! 1026: fprintf(output, "Object is an XSLT value tree :\n");
! 1027: xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
! 1028: break;
! 1029: case XPATH_BOOLEAN:
! 1030: fprintf(output, "Object is a Boolean : ");
! 1031: if (cur->boolval) fprintf(output, "true\n");
! 1032: else fprintf(output, "false\n");
! 1033: break;
! 1034: case XPATH_NUMBER:
! 1035: switch (xmlXPathIsInf(cur->floatval)) {
! 1036: case 1:
! 1037: fprintf(output, "Object is a number : Infinity\n");
! 1038: break;
! 1039: case -1:
! 1040: fprintf(output, "Object is a number : -Infinity\n");
! 1041: break;
! 1042: default:
! 1043: if (xmlXPathIsNaN(cur->floatval)) {
! 1044: fprintf(output, "Object is a number : NaN\n");
! 1045: } else if (cur->floatval == 0 && xmlXPathGetSign(cur->floatval) != 0) {
! 1046: fprintf(output, "Object is a number : 0\n");
! 1047: } else {
! 1048: fprintf(output, "Object is a number : %0g\n", cur->floatval);
! 1049: }
! 1050: }
! 1051: break;
! 1052: case XPATH_STRING:
! 1053: fprintf(output, "Object is a string : ");
! 1054: xmlDebugDumpString(output, cur->stringval);
! 1055: fprintf(output, "\n");
! 1056: break;
! 1057: case XPATH_POINT:
! 1058: fprintf(output, "Object is a point : index %d in node", cur->index);
! 1059: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
! 1060: fprintf(output, "\n");
! 1061: break;
! 1062: case XPATH_RANGE:
! 1063: if ((cur->user2 == NULL) ||
! 1064: ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
! 1065: fprintf(output, "Object is a collapsed range :\n");
! 1066: fprintf(output, "%s", shift);
! 1067: if (cur->index >= 0)
! 1068: fprintf(output, "index %d in ", cur->index);
! 1069: fprintf(output, "node\n");
! 1070: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
! 1071: depth + 1);
! 1072: } else {
! 1073: fprintf(output, "Object is a range :\n");
! 1074: fprintf(output, "%s", shift);
! 1075: fprintf(output, "From ");
! 1076: if (cur->index >= 0)
! 1077: fprintf(output, "index %d in ", cur->index);
! 1078: fprintf(output, "node\n");
! 1079: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
! 1080: depth + 1);
! 1081: fprintf(output, "%s", shift);
! 1082: fprintf(output, "To ");
! 1083: if (cur->index2 >= 0)
! 1084: fprintf(output, "index %d in ", cur->index2);
! 1085: fprintf(output, "node\n");
! 1086: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
! 1087: depth + 1);
! 1088: fprintf(output, "\n");
! 1089: }
! 1090: break;
! 1091: case XPATH_LOCATIONSET:
! 1092: #if defined(LIBXML_XPTR_ENABLED)
! 1093: fprintf(output, "Object is a Location Set:\n");
! 1094: xmlXPathDebugDumpLocationSet(output,
! 1095: (xmlLocationSetPtr) cur->user, depth);
! 1096: #endif
! 1097: break;
! 1098: case XPATH_USERS:
! 1099: fprintf(output, "Object is user defined\n");
! 1100: break;
! 1101: }
! 1102: }
! 1103:
! 1104: static void
! 1105: xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
! 1106: xmlXPathStepOpPtr op, int depth) {
! 1107: int i;
! 1108: char shift[100];
! 1109:
! 1110: for (i = 0;((i < depth) && (i < 25));i++)
! 1111: shift[2 * i] = shift[2 * i + 1] = ' ';
! 1112: shift[2 * i] = shift[2 * i + 1] = 0;
! 1113:
! 1114: fprintf(output, "%s", shift);
! 1115: if (op == NULL) {
! 1116: fprintf(output, "Step is NULL\n");
! 1117: return;
! 1118: }
! 1119: switch (op->op) {
! 1120: case XPATH_OP_END:
! 1121: fprintf(output, "END"); break;
! 1122: case XPATH_OP_AND:
! 1123: fprintf(output, "AND"); break;
! 1124: case XPATH_OP_OR:
! 1125: fprintf(output, "OR"); break;
! 1126: case XPATH_OP_EQUAL:
! 1127: if (op->value)
! 1128: fprintf(output, "EQUAL =");
! 1129: else
! 1130: fprintf(output, "EQUAL !=");
! 1131: break;
! 1132: case XPATH_OP_CMP:
! 1133: if (op->value)
! 1134: fprintf(output, "CMP <");
! 1135: else
! 1136: fprintf(output, "CMP >");
! 1137: if (!op->value2)
! 1138: fprintf(output, "=");
! 1139: break;
! 1140: case XPATH_OP_PLUS:
! 1141: if (op->value == 0)
! 1142: fprintf(output, "PLUS -");
! 1143: else if (op->value == 1)
! 1144: fprintf(output, "PLUS +");
! 1145: else if (op->value == 2)
! 1146: fprintf(output, "PLUS unary -");
! 1147: else if (op->value == 3)
! 1148: fprintf(output, "PLUS unary - -");
! 1149: break;
! 1150: case XPATH_OP_MULT:
! 1151: if (op->value == 0)
! 1152: fprintf(output, "MULT *");
! 1153: else if (op->value == 1)
! 1154: fprintf(output, "MULT div");
! 1155: else
! 1156: fprintf(output, "MULT mod");
! 1157: break;
! 1158: case XPATH_OP_UNION:
! 1159: fprintf(output, "UNION"); break;
! 1160: case XPATH_OP_ROOT:
! 1161: fprintf(output, "ROOT"); break;
! 1162: case XPATH_OP_NODE:
! 1163: fprintf(output, "NODE"); break;
! 1164: case XPATH_OP_RESET:
! 1165: fprintf(output, "RESET"); break;
! 1166: case XPATH_OP_SORT:
! 1167: fprintf(output, "SORT"); break;
! 1168: case XPATH_OP_COLLECT: {
! 1169: xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value;
! 1170: xmlXPathTestVal test = (xmlXPathTestVal)op->value2;
! 1171: xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3;
! 1172: const xmlChar *prefix = op->value4;
! 1173: const xmlChar *name = op->value5;
! 1174:
! 1175: fprintf(output, "COLLECT ");
! 1176: switch (axis) {
! 1177: case AXIS_ANCESTOR:
! 1178: fprintf(output, " 'ancestors' "); break;
! 1179: case AXIS_ANCESTOR_OR_SELF:
! 1180: fprintf(output, " 'ancestors-or-self' "); break;
! 1181: case AXIS_ATTRIBUTE:
! 1182: fprintf(output, " 'attributes' "); break;
! 1183: case AXIS_CHILD:
! 1184: fprintf(output, " 'child' "); break;
! 1185: case AXIS_DESCENDANT:
! 1186: fprintf(output, " 'descendant' "); break;
! 1187: case AXIS_DESCENDANT_OR_SELF:
! 1188: fprintf(output, " 'descendant-or-self' "); break;
! 1189: case AXIS_FOLLOWING:
! 1190: fprintf(output, " 'following' "); break;
! 1191: case AXIS_FOLLOWING_SIBLING:
! 1192: fprintf(output, " 'following-siblings' "); break;
! 1193: case AXIS_NAMESPACE:
! 1194: fprintf(output, " 'namespace' "); break;
! 1195: case AXIS_PARENT:
! 1196: fprintf(output, " 'parent' "); break;
! 1197: case AXIS_PRECEDING:
! 1198: fprintf(output, " 'preceding' "); break;
! 1199: case AXIS_PRECEDING_SIBLING:
! 1200: fprintf(output, " 'preceding-sibling' "); break;
! 1201: case AXIS_SELF:
! 1202: fprintf(output, " 'self' "); break;
! 1203: }
! 1204: switch (test) {
! 1205: case NODE_TEST_NONE:
! 1206: fprintf(output, "'none' "); break;
! 1207: case NODE_TEST_TYPE:
! 1208: fprintf(output, "'type' "); break;
! 1209: case NODE_TEST_PI:
! 1210: fprintf(output, "'PI' "); break;
! 1211: case NODE_TEST_ALL:
! 1212: fprintf(output, "'all' "); break;
! 1213: case NODE_TEST_NS:
! 1214: fprintf(output, "'namespace' "); break;
! 1215: case NODE_TEST_NAME:
! 1216: fprintf(output, "'name' "); break;
! 1217: }
! 1218: switch (type) {
! 1219: case NODE_TYPE_NODE:
! 1220: fprintf(output, "'node' "); break;
! 1221: case NODE_TYPE_COMMENT:
! 1222: fprintf(output, "'comment' "); break;
! 1223: case NODE_TYPE_TEXT:
! 1224: fprintf(output, "'text' "); break;
! 1225: case NODE_TYPE_PI:
! 1226: fprintf(output, "'PI' "); break;
! 1227: }
! 1228: if (prefix != NULL)
! 1229: fprintf(output, "%s:", prefix);
! 1230: if (name != NULL)
! 1231: fprintf(output, "%s", (const char *) name);
! 1232: break;
! 1233:
! 1234: }
! 1235: case XPATH_OP_VALUE: {
! 1236: xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
! 1237:
! 1238: fprintf(output, "ELEM ");
! 1239: xmlXPathDebugDumpObject(output, object, 0);
! 1240: goto finish;
! 1241: }
! 1242: case XPATH_OP_VARIABLE: {
! 1243: const xmlChar *prefix = op->value5;
! 1244: const xmlChar *name = op->value4;
! 1245:
! 1246: if (prefix != NULL)
! 1247: fprintf(output, "VARIABLE %s:%s", prefix, name);
! 1248: else
! 1249: fprintf(output, "VARIABLE %s", name);
! 1250: break;
! 1251: }
! 1252: case XPATH_OP_FUNCTION: {
! 1253: int nbargs = op->value;
! 1254: const xmlChar *prefix = op->value5;
! 1255: const xmlChar *name = op->value4;
! 1256:
! 1257: if (prefix != NULL)
! 1258: fprintf(output, "FUNCTION %s:%s(%d args)",
! 1259: prefix, name, nbargs);
! 1260: else
! 1261: fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
! 1262: break;
! 1263: }
! 1264: case XPATH_OP_ARG: fprintf(output, "ARG"); break;
! 1265: case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
! 1266: case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
! 1267: #ifdef LIBXML_XPTR_ENABLED
! 1268: case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
! 1269: #endif
! 1270: default:
! 1271: fprintf(output, "UNKNOWN %d\n", op->op); return;
! 1272: }
! 1273: fprintf(output, "\n");
! 1274: finish:
! 1275: if (op->ch1 >= 0)
! 1276: xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
! 1277: if (op->ch2 >= 0)
! 1278: xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
! 1279: }
! 1280:
! 1281: /**
! 1282: * xmlXPathDebugDumpCompExpr:
! 1283: * @output: the FILE * for the output
! 1284: * @comp: the precompiled XPath expression
! 1285: * @depth: the indentation level.
! 1286: *
! 1287: * Dumps the tree of the compiled XPath expression.
! 1288: */
! 1289: void
! 1290: xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
! 1291: int depth) {
! 1292: int i;
! 1293: char shift[100];
! 1294:
! 1295: if ((output == NULL) || (comp == NULL)) return;
! 1296:
! 1297: for (i = 0;((i < depth) && (i < 25));i++)
! 1298: shift[2 * i] = shift[2 * i + 1] = ' ';
! 1299: shift[2 * i] = shift[2 * i + 1] = 0;
! 1300:
! 1301: fprintf(output, "%s", shift);
! 1302:
! 1303: fprintf(output, "Compiled Expression : %d elements\n",
! 1304: comp->nbStep);
! 1305: i = comp->last;
! 1306: xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
! 1307: }
! 1308:
! 1309: #ifdef XP_DEBUG_OBJ_USAGE
! 1310:
! 1311: /*
! 1312: * XPath object usage related debugging variables.
! 1313: */
! 1314: static int xmlXPathDebugObjCounterUndefined = 0;
! 1315: static int xmlXPathDebugObjCounterNodeset = 0;
! 1316: static int xmlXPathDebugObjCounterBool = 0;
! 1317: static int xmlXPathDebugObjCounterNumber = 0;
! 1318: static int xmlXPathDebugObjCounterString = 0;
! 1319: static int xmlXPathDebugObjCounterPoint = 0;
! 1320: static int xmlXPathDebugObjCounterRange = 0;
! 1321: static int xmlXPathDebugObjCounterLocset = 0;
! 1322: static int xmlXPathDebugObjCounterUsers = 0;
! 1323: static int xmlXPathDebugObjCounterXSLTTree = 0;
! 1324: static int xmlXPathDebugObjCounterAll = 0;
! 1325:
! 1326: static int xmlXPathDebugObjTotalUndefined = 0;
! 1327: static int xmlXPathDebugObjTotalNodeset = 0;
! 1328: static int xmlXPathDebugObjTotalBool = 0;
! 1329: static int xmlXPathDebugObjTotalNumber = 0;
! 1330: static int xmlXPathDebugObjTotalString = 0;
! 1331: static int xmlXPathDebugObjTotalPoint = 0;
! 1332: static int xmlXPathDebugObjTotalRange = 0;
! 1333: static int xmlXPathDebugObjTotalLocset = 0;
! 1334: static int xmlXPathDebugObjTotalUsers = 0;
! 1335: static int xmlXPathDebugObjTotalXSLTTree = 0;
! 1336: static int xmlXPathDebugObjTotalAll = 0;
! 1337:
! 1338: static int xmlXPathDebugObjMaxUndefined = 0;
! 1339: static int xmlXPathDebugObjMaxNodeset = 0;
! 1340: static int xmlXPathDebugObjMaxBool = 0;
! 1341: static int xmlXPathDebugObjMaxNumber = 0;
! 1342: static int xmlXPathDebugObjMaxString = 0;
! 1343: static int xmlXPathDebugObjMaxPoint = 0;
! 1344: static int xmlXPathDebugObjMaxRange = 0;
! 1345: static int xmlXPathDebugObjMaxLocset = 0;
! 1346: static int xmlXPathDebugObjMaxUsers = 0;
! 1347: static int xmlXPathDebugObjMaxXSLTTree = 0;
! 1348: static int xmlXPathDebugObjMaxAll = 0;
! 1349:
! 1350: /* REVISIT TODO: Make this static when committing */
! 1351: static void
! 1352: xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt)
! 1353: {
! 1354: if (ctxt != NULL) {
! 1355: if (ctxt->cache != NULL) {
! 1356: xmlXPathContextCachePtr cache =
! 1357: (xmlXPathContextCachePtr) ctxt->cache;
! 1358:
! 1359: cache->dbgCachedAll = 0;
! 1360: cache->dbgCachedNodeset = 0;
! 1361: cache->dbgCachedString = 0;
! 1362: cache->dbgCachedBool = 0;
! 1363: cache->dbgCachedNumber = 0;
! 1364: cache->dbgCachedPoint = 0;
! 1365: cache->dbgCachedRange = 0;
! 1366: cache->dbgCachedLocset = 0;
! 1367: cache->dbgCachedUsers = 0;
! 1368: cache->dbgCachedXSLTTree = 0;
! 1369: cache->dbgCachedUndefined = 0;
! 1370:
! 1371: cache->dbgReusedAll = 0;
! 1372: cache->dbgReusedNodeset = 0;
! 1373: cache->dbgReusedString = 0;
! 1374: cache->dbgReusedBool = 0;
! 1375: cache->dbgReusedNumber = 0;
! 1376: cache->dbgReusedPoint = 0;
! 1377: cache->dbgReusedRange = 0;
! 1378: cache->dbgReusedLocset = 0;
! 1379: cache->dbgReusedUsers = 0;
! 1380: cache->dbgReusedXSLTTree = 0;
! 1381: cache->dbgReusedUndefined = 0;
! 1382: }
! 1383: }
! 1384:
! 1385: xmlXPathDebugObjCounterUndefined = 0;
! 1386: xmlXPathDebugObjCounterNodeset = 0;
! 1387: xmlXPathDebugObjCounterBool = 0;
! 1388: xmlXPathDebugObjCounterNumber = 0;
! 1389: xmlXPathDebugObjCounterString = 0;
! 1390: xmlXPathDebugObjCounterPoint = 0;
! 1391: xmlXPathDebugObjCounterRange = 0;
! 1392: xmlXPathDebugObjCounterLocset = 0;
! 1393: xmlXPathDebugObjCounterUsers = 0;
! 1394: xmlXPathDebugObjCounterXSLTTree = 0;
! 1395: xmlXPathDebugObjCounterAll = 0;
! 1396:
! 1397: xmlXPathDebugObjTotalUndefined = 0;
! 1398: xmlXPathDebugObjTotalNodeset = 0;
! 1399: xmlXPathDebugObjTotalBool = 0;
! 1400: xmlXPathDebugObjTotalNumber = 0;
! 1401: xmlXPathDebugObjTotalString = 0;
! 1402: xmlXPathDebugObjTotalPoint = 0;
! 1403: xmlXPathDebugObjTotalRange = 0;
! 1404: xmlXPathDebugObjTotalLocset = 0;
! 1405: xmlXPathDebugObjTotalUsers = 0;
! 1406: xmlXPathDebugObjTotalXSLTTree = 0;
! 1407: xmlXPathDebugObjTotalAll = 0;
! 1408:
! 1409: xmlXPathDebugObjMaxUndefined = 0;
! 1410: xmlXPathDebugObjMaxNodeset = 0;
! 1411: xmlXPathDebugObjMaxBool = 0;
! 1412: xmlXPathDebugObjMaxNumber = 0;
! 1413: xmlXPathDebugObjMaxString = 0;
! 1414: xmlXPathDebugObjMaxPoint = 0;
! 1415: xmlXPathDebugObjMaxRange = 0;
! 1416: xmlXPathDebugObjMaxLocset = 0;
! 1417: xmlXPathDebugObjMaxUsers = 0;
! 1418: xmlXPathDebugObjMaxXSLTTree = 0;
! 1419: xmlXPathDebugObjMaxAll = 0;
! 1420:
! 1421: }
! 1422:
! 1423: static void
! 1424: xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt,
! 1425: xmlXPathObjectType objType)
! 1426: {
! 1427: int isCached = 0;
! 1428:
! 1429: if (ctxt != NULL) {
! 1430: if (ctxt->cache != NULL) {
! 1431: xmlXPathContextCachePtr cache =
! 1432: (xmlXPathContextCachePtr) ctxt->cache;
! 1433:
! 1434: isCached = 1;
! 1435:
! 1436: cache->dbgReusedAll++;
! 1437: switch (objType) {
! 1438: case XPATH_UNDEFINED:
! 1439: cache->dbgReusedUndefined++;
! 1440: break;
! 1441: case XPATH_NODESET:
! 1442: cache->dbgReusedNodeset++;
! 1443: break;
! 1444: case XPATH_BOOLEAN:
! 1445: cache->dbgReusedBool++;
! 1446: break;
! 1447: case XPATH_NUMBER:
! 1448: cache->dbgReusedNumber++;
! 1449: break;
! 1450: case XPATH_STRING:
! 1451: cache->dbgReusedString++;
! 1452: break;
! 1453: case XPATH_POINT:
! 1454: cache->dbgReusedPoint++;
! 1455: break;
! 1456: case XPATH_RANGE:
! 1457: cache->dbgReusedRange++;
! 1458: break;
! 1459: case XPATH_LOCATIONSET:
! 1460: cache->dbgReusedLocset++;
! 1461: break;
! 1462: case XPATH_USERS:
! 1463: cache->dbgReusedUsers++;
! 1464: break;
! 1465: case XPATH_XSLT_TREE:
! 1466: cache->dbgReusedXSLTTree++;
! 1467: break;
! 1468: default:
! 1469: break;
! 1470: }
! 1471: }
! 1472: }
! 1473:
! 1474: switch (objType) {
! 1475: case XPATH_UNDEFINED:
! 1476: if (! isCached)
! 1477: xmlXPathDebugObjTotalUndefined++;
! 1478: xmlXPathDebugObjCounterUndefined++;
! 1479: if (xmlXPathDebugObjCounterUndefined >
! 1480: xmlXPathDebugObjMaxUndefined)
! 1481: xmlXPathDebugObjMaxUndefined =
! 1482: xmlXPathDebugObjCounterUndefined;
! 1483: break;
! 1484: case XPATH_NODESET:
! 1485: if (! isCached)
! 1486: xmlXPathDebugObjTotalNodeset++;
! 1487: xmlXPathDebugObjCounterNodeset++;
! 1488: if (xmlXPathDebugObjCounterNodeset >
! 1489: xmlXPathDebugObjMaxNodeset)
! 1490: xmlXPathDebugObjMaxNodeset =
! 1491: xmlXPathDebugObjCounterNodeset;
! 1492: break;
! 1493: case XPATH_BOOLEAN:
! 1494: if (! isCached)
! 1495: xmlXPathDebugObjTotalBool++;
! 1496: xmlXPathDebugObjCounterBool++;
! 1497: if (xmlXPathDebugObjCounterBool >
! 1498: xmlXPathDebugObjMaxBool)
! 1499: xmlXPathDebugObjMaxBool =
! 1500: xmlXPathDebugObjCounterBool;
! 1501: break;
! 1502: case XPATH_NUMBER:
! 1503: if (! isCached)
! 1504: xmlXPathDebugObjTotalNumber++;
! 1505: xmlXPathDebugObjCounterNumber++;
! 1506: if (xmlXPathDebugObjCounterNumber >
! 1507: xmlXPathDebugObjMaxNumber)
! 1508: xmlXPathDebugObjMaxNumber =
! 1509: xmlXPathDebugObjCounterNumber;
! 1510: break;
! 1511: case XPATH_STRING:
! 1512: if (! isCached)
! 1513: xmlXPathDebugObjTotalString++;
! 1514: xmlXPathDebugObjCounterString++;
! 1515: if (xmlXPathDebugObjCounterString >
! 1516: xmlXPathDebugObjMaxString)
! 1517: xmlXPathDebugObjMaxString =
! 1518: xmlXPathDebugObjCounterString;
! 1519: break;
! 1520: case XPATH_POINT:
! 1521: if (! isCached)
! 1522: xmlXPathDebugObjTotalPoint++;
! 1523: xmlXPathDebugObjCounterPoint++;
! 1524: if (xmlXPathDebugObjCounterPoint >
! 1525: xmlXPathDebugObjMaxPoint)
! 1526: xmlXPathDebugObjMaxPoint =
! 1527: xmlXPathDebugObjCounterPoint;
! 1528: break;
! 1529: case XPATH_RANGE:
! 1530: if (! isCached)
! 1531: xmlXPathDebugObjTotalRange++;
! 1532: xmlXPathDebugObjCounterRange++;
! 1533: if (xmlXPathDebugObjCounterRange >
! 1534: xmlXPathDebugObjMaxRange)
! 1535: xmlXPathDebugObjMaxRange =
! 1536: xmlXPathDebugObjCounterRange;
! 1537: break;
! 1538: case XPATH_LOCATIONSET:
! 1539: if (! isCached)
! 1540: xmlXPathDebugObjTotalLocset++;
! 1541: xmlXPathDebugObjCounterLocset++;
! 1542: if (xmlXPathDebugObjCounterLocset >
! 1543: xmlXPathDebugObjMaxLocset)
! 1544: xmlXPathDebugObjMaxLocset =
! 1545: xmlXPathDebugObjCounterLocset;
! 1546: break;
! 1547: case XPATH_USERS:
! 1548: if (! isCached)
! 1549: xmlXPathDebugObjTotalUsers++;
! 1550: xmlXPathDebugObjCounterUsers++;
! 1551: if (xmlXPathDebugObjCounterUsers >
! 1552: xmlXPathDebugObjMaxUsers)
! 1553: xmlXPathDebugObjMaxUsers =
! 1554: xmlXPathDebugObjCounterUsers;
! 1555: break;
! 1556: case XPATH_XSLT_TREE:
! 1557: if (! isCached)
! 1558: xmlXPathDebugObjTotalXSLTTree++;
! 1559: xmlXPathDebugObjCounterXSLTTree++;
! 1560: if (xmlXPathDebugObjCounterXSLTTree >
! 1561: xmlXPathDebugObjMaxXSLTTree)
! 1562: xmlXPathDebugObjMaxXSLTTree =
! 1563: xmlXPathDebugObjCounterXSLTTree;
! 1564: break;
! 1565: default:
! 1566: break;
! 1567: }
! 1568: if (! isCached)
! 1569: xmlXPathDebugObjTotalAll++;
! 1570: xmlXPathDebugObjCounterAll++;
! 1571: if (xmlXPathDebugObjCounterAll >
! 1572: xmlXPathDebugObjMaxAll)
! 1573: xmlXPathDebugObjMaxAll =
! 1574: xmlXPathDebugObjCounterAll;
! 1575: }
! 1576:
! 1577: static void
! 1578: xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt,
! 1579: xmlXPathObjectType objType)
! 1580: {
! 1581: int isCached = 0;
! 1582:
! 1583: if (ctxt != NULL) {
! 1584: if (ctxt->cache != NULL) {
! 1585: xmlXPathContextCachePtr cache =
! 1586: (xmlXPathContextCachePtr) ctxt->cache;
! 1587:
! 1588: isCached = 1;
! 1589:
! 1590: cache->dbgCachedAll++;
! 1591: switch (objType) {
! 1592: case XPATH_UNDEFINED:
! 1593: cache->dbgCachedUndefined++;
! 1594: break;
! 1595: case XPATH_NODESET:
! 1596: cache->dbgCachedNodeset++;
! 1597: break;
! 1598: case XPATH_BOOLEAN:
! 1599: cache->dbgCachedBool++;
! 1600: break;
! 1601: case XPATH_NUMBER:
! 1602: cache->dbgCachedNumber++;
! 1603: break;
! 1604: case XPATH_STRING:
! 1605: cache->dbgCachedString++;
! 1606: break;
! 1607: case XPATH_POINT:
! 1608: cache->dbgCachedPoint++;
! 1609: break;
! 1610: case XPATH_RANGE:
! 1611: cache->dbgCachedRange++;
! 1612: break;
! 1613: case XPATH_LOCATIONSET:
! 1614: cache->dbgCachedLocset++;
! 1615: break;
! 1616: case XPATH_USERS:
! 1617: cache->dbgCachedUsers++;
! 1618: break;
! 1619: case XPATH_XSLT_TREE:
! 1620: cache->dbgCachedXSLTTree++;
! 1621: break;
! 1622: default:
! 1623: break;
! 1624: }
! 1625:
! 1626: }
! 1627: }
! 1628: switch (objType) {
! 1629: case XPATH_UNDEFINED:
! 1630: xmlXPathDebugObjCounterUndefined--;
! 1631: break;
! 1632: case XPATH_NODESET:
! 1633: xmlXPathDebugObjCounterNodeset--;
! 1634: break;
! 1635: case XPATH_BOOLEAN:
! 1636: xmlXPathDebugObjCounterBool--;
! 1637: break;
! 1638: case XPATH_NUMBER:
! 1639: xmlXPathDebugObjCounterNumber--;
! 1640: break;
! 1641: case XPATH_STRING:
! 1642: xmlXPathDebugObjCounterString--;
! 1643: break;
! 1644: case XPATH_POINT:
! 1645: xmlXPathDebugObjCounterPoint--;
! 1646: break;
! 1647: case XPATH_RANGE:
! 1648: xmlXPathDebugObjCounterRange--;
! 1649: break;
! 1650: case XPATH_LOCATIONSET:
! 1651: xmlXPathDebugObjCounterLocset--;
! 1652: break;
! 1653: case XPATH_USERS:
! 1654: xmlXPathDebugObjCounterUsers--;
! 1655: break;
! 1656: case XPATH_XSLT_TREE:
! 1657: xmlXPathDebugObjCounterXSLTTree--;
! 1658: break;
! 1659: default:
! 1660: break;
! 1661: }
! 1662: xmlXPathDebugObjCounterAll--;
! 1663: }
! 1664:
! 1665: /* REVISIT TODO: Make this static when committing */
! 1666: static void
! 1667: xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt)
! 1668: {
! 1669: int reqAll, reqNodeset, reqString, reqBool, reqNumber,
! 1670: reqXSLTTree, reqUndefined;
! 1671: int caAll = 0, caNodeset = 0, caString = 0, caBool = 0,
! 1672: caNumber = 0, caXSLTTree = 0, caUndefined = 0;
! 1673: int reAll = 0, reNodeset = 0, reString = 0, reBool = 0,
! 1674: reNumber = 0, reXSLTTree = 0, reUndefined = 0;
! 1675: int leftObjs = xmlXPathDebugObjCounterAll;
! 1676:
! 1677: reqAll = xmlXPathDebugObjTotalAll;
! 1678: reqNodeset = xmlXPathDebugObjTotalNodeset;
! 1679: reqString = xmlXPathDebugObjTotalString;
! 1680: reqBool = xmlXPathDebugObjTotalBool;
! 1681: reqNumber = xmlXPathDebugObjTotalNumber;
! 1682: reqXSLTTree = xmlXPathDebugObjTotalXSLTTree;
! 1683: reqUndefined = xmlXPathDebugObjTotalUndefined;
! 1684:
! 1685: printf("# XPath object usage:\n");
! 1686:
! 1687: if (ctxt != NULL) {
! 1688: if (ctxt->cache != NULL) {
! 1689: xmlXPathContextCachePtr cache =
! 1690: (xmlXPathContextCachePtr) ctxt->cache;
! 1691:
! 1692: reAll = cache->dbgReusedAll;
! 1693: reqAll += reAll;
! 1694: reNodeset = cache->dbgReusedNodeset;
! 1695: reqNodeset += reNodeset;
! 1696: reString = cache->dbgReusedString;
! 1697: reqString += reString;
! 1698: reBool = cache->dbgReusedBool;
! 1699: reqBool += reBool;
! 1700: reNumber = cache->dbgReusedNumber;
! 1701: reqNumber += reNumber;
! 1702: reXSLTTree = cache->dbgReusedXSLTTree;
! 1703: reqXSLTTree += reXSLTTree;
! 1704: reUndefined = cache->dbgReusedUndefined;
! 1705: reqUndefined += reUndefined;
! 1706:
! 1707: caAll = cache->dbgCachedAll;
! 1708: caBool = cache->dbgCachedBool;
! 1709: caNodeset = cache->dbgCachedNodeset;
! 1710: caString = cache->dbgCachedString;
! 1711: caNumber = cache->dbgCachedNumber;
! 1712: caXSLTTree = cache->dbgCachedXSLTTree;
! 1713: caUndefined = cache->dbgCachedUndefined;
! 1714:
! 1715: if (cache->nodesetObjs)
! 1716: leftObjs -= cache->nodesetObjs->number;
! 1717: if (cache->stringObjs)
! 1718: leftObjs -= cache->stringObjs->number;
! 1719: if (cache->booleanObjs)
! 1720: leftObjs -= cache->booleanObjs->number;
! 1721: if (cache->numberObjs)
! 1722: leftObjs -= cache->numberObjs->number;
! 1723: if (cache->miscObjs)
! 1724: leftObjs -= cache->miscObjs->number;
! 1725: }
! 1726: }
! 1727:
! 1728: printf("# all\n");
! 1729: printf("# total : %d\n", reqAll);
! 1730: printf("# left : %d\n", leftObjs);
! 1731: printf("# created: %d\n", xmlXPathDebugObjTotalAll);
! 1732: printf("# reused : %d\n", reAll);
! 1733: printf("# max : %d\n", xmlXPathDebugObjMaxAll);
! 1734:
! 1735: printf("# node-sets\n");
! 1736: printf("# total : %d\n", reqNodeset);
! 1737: printf("# created: %d\n", xmlXPathDebugObjTotalNodeset);
! 1738: printf("# reused : %d\n", reNodeset);
! 1739: printf("# max : %d\n", xmlXPathDebugObjMaxNodeset);
! 1740:
! 1741: printf("# strings\n");
! 1742: printf("# total : %d\n", reqString);
! 1743: printf("# created: %d\n", xmlXPathDebugObjTotalString);
! 1744: printf("# reused : %d\n", reString);
! 1745: printf("# max : %d\n", xmlXPathDebugObjMaxString);
! 1746:
! 1747: printf("# booleans\n");
! 1748: printf("# total : %d\n", reqBool);
! 1749: printf("# created: %d\n", xmlXPathDebugObjTotalBool);
! 1750: printf("# reused : %d\n", reBool);
! 1751: printf("# max : %d\n", xmlXPathDebugObjMaxBool);
! 1752:
! 1753: printf("# numbers\n");
! 1754: printf("# total : %d\n", reqNumber);
! 1755: printf("# created: %d\n", xmlXPathDebugObjTotalNumber);
! 1756: printf("# reused : %d\n", reNumber);
! 1757: printf("# max : %d\n", xmlXPathDebugObjMaxNumber);
! 1758:
! 1759: printf("# XSLT result tree fragments\n");
! 1760: printf("# total : %d\n", reqXSLTTree);
! 1761: printf("# created: %d\n", xmlXPathDebugObjTotalXSLTTree);
! 1762: printf("# reused : %d\n", reXSLTTree);
! 1763: printf("# max : %d\n", xmlXPathDebugObjMaxXSLTTree);
! 1764:
! 1765: printf("# undefined\n");
! 1766: printf("# total : %d\n", reqUndefined);
! 1767: printf("# created: %d\n", xmlXPathDebugObjTotalUndefined);
! 1768: printf("# reused : %d\n", reUndefined);
! 1769: printf("# max : %d\n", xmlXPathDebugObjMaxUndefined);
! 1770:
! 1771: }
! 1772:
! 1773: #endif /* XP_DEBUG_OBJ_USAGE */
! 1774:
! 1775: #endif /* LIBXML_DEBUG_ENABLED */
! 1776:
! 1777: /************************************************************************
! 1778: * *
! 1779: * XPath object caching *
! 1780: * *
! 1781: ************************************************************************/
! 1782:
! 1783: /**
! 1784: * xmlXPathNewCache:
! 1785: *
! 1786: * Create a new object cache
! 1787: *
! 1788: * Returns the xmlXPathCache just allocated.
! 1789: */
! 1790: static xmlXPathContextCachePtr
! 1791: xmlXPathNewCache(void)
! 1792: {
! 1793: xmlXPathContextCachePtr ret;
! 1794:
! 1795: ret = (xmlXPathContextCachePtr) xmlMalloc(sizeof(xmlXPathContextCache));
! 1796: if (ret == NULL) {
! 1797: xmlXPathErrMemory(NULL, "creating object cache\n");
! 1798: return(NULL);
! 1799: }
! 1800: memset(ret, 0 , (size_t) sizeof(xmlXPathContextCache));
! 1801: ret->maxNodeset = 100;
! 1802: ret->maxString = 100;
! 1803: ret->maxBoolean = 100;
! 1804: ret->maxNumber = 100;
! 1805: ret->maxMisc = 100;
! 1806: return(ret);
! 1807: }
! 1808:
! 1809: static void
! 1810: xmlXPathCacheFreeObjectList(xmlPointerListPtr list)
! 1811: {
! 1812: int i;
! 1813: xmlXPathObjectPtr obj;
! 1814:
! 1815: if (list == NULL)
! 1816: return;
! 1817:
! 1818: for (i = 0; i < list->number; i++) {
! 1819: obj = list->items[i];
! 1820: /*
! 1821: * Note that it is already assured that we don't need to
! 1822: * look out for namespace nodes in the node-set.
! 1823: */
! 1824: if (obj->nodesetval != NULL) {
! 1825: if (obj->nodesetval->nodeTab != NULL)
! 1826: xmlFree(obj->nodesetval->nodeTab);
! 1827: xmlFree(obj->nodesetval);
! 1828: }
! 1829: xmlFree(obj);
! 1830: #ifdef XP_DEBUG_OBJ_USAGE
! 1831: xmlXPathDebugObjCounterAll--;
! 1832: #endif
! 1833: }
! 1834: xmlPointerListFree(list);
! 1835: }
! 1836:
! 1837: static void
! 1838: xmlXPathFreeCache(xmlXPathContextCachePtr cache)
! 1839: {
! 1840: if (cache == NULL)
! 1841: return;
! 1842: if (cache->nodesetObjs)
! 1843: xmlXPathCacheFreeObjectList(cache->nodesetObjs);
! 1844: if (cache->stringObjs)
! 1845: xmlXPathCacheFreeObjectList(cache->stringObjs);
! 1846: if (cache->booleanObjs)
! 1847: xmlXPathCacheFreeObjectList(cache->booleanObjs);
! 1848: if (cache->numberObjs)
! 1849: xmlXPathCacheFreeObjectList(cache->numberObjs);
! 1850: if (cache->miscObjs)
! 1851: xmlXPathCacheFreeObjectList(cache->miscObjs);
! 1852: xmlFree(cache);
! 1853: }
! 1854:
! 1855: /**
! 1856: * xmlXPathContextSetCache:
! 1857: *
! 1858: * @ctxt: the XPath context
! 1859: * @active: enables/disables (creates/frees) the cache
! 1860: * @value: a value with semantics dependant on @options
! 1861: * @options: options (currently only the value 0 is used)
! 1862: *
! 1863: * Creates/frees an object cache on the XPath context.
! 1864: * If activates XPath objects (xmlXPathObject) will be cached internally
! 1865: * to be reused.
! 1866: * @options:
! 1867: * 0: This will set the XPath object caching:
! 1868: * @value:
! 1869: * This will set the maximum number of XPath objects
! 1870: * to be cached per slot
! 1871: * There are 5 slots for: node-set, string, number, boolean, and
! 1872: * misc objects. Use <0 for the default number (100).
! 1873: * Other values for @options have currently no effect.
! 1874: *
! 1875: * Returns 0 if the setting succeeded, and -1 on API or internal errors.
! 1876: */
! 1877: int
! 1878: xmlXPathContextSetCache(xmlXPathContextPtr ctxt,
! 1879: int active,
! 1880: int value,
! 1881: int options)
! 1882: {
! 1883: if (ctxt == NULL)
! 1884: return(-1);
! 1885: if (active) {
! 1886: xmlXPathContextCachePtr cache;
! 1887:
! 1888: if (ctxt->cache == NULL) {
! 1889: ctxt->cache = xmlXPathNewCache();
! 1890: if (ctxt->cache == NULL)
! 1891: return(-1);
! 1892: }
! 1893: cache = (xmlXPathContextCachePtr) ctxt->cache;
! 1894: if (options == 0) {
! 1895: if (value < 0)
! 1896: value = 100;
! 1897: cache->maxNodeset = value;
! 1898: cache->maxString = value;
! 1899: cache->maxNumber = value;
! 1900: cache->maxBoolean = value;
! 1901: cache->maxMisc = value;
! 1902: }
! 1903: } else if (ctxt->cache != NULL) {
! 1904: xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
! 1905: ctxt->cache = NULL;
! 1906: }
! 1907: return(0);
! 1908: }
! 1909:
! 1910: /**
! 1911: * xmlXPathCacheWrapNodeSet:
! 1912: * @ctxt: the XPath context
! 1913: * @val: the NodePtr value
! 1914: *
! 1915: * This is the cached version of xmlXPathWrapNodeSet().
! 1916: * Wrap the Nodeset @val in a new xmlXPathObjectPtr
! 1917: *
! 1918: * Returns the created or reused object.
! 1919: */
! 1920: static xmlXPathObjectPtr
! 1921: xmlXPathCacheWrapNodeSet(xmlXPathContextPtr ctxt, xmlNodeSetPtr val)
! 1922: {
! 1923: if ((ctxt != NULL) && (ctxt->cache != NULL)) {
! 1924: xmlXPathContextCachePtr cache =
! 1925: (xmlXPathContextCachePtr) ctxt->cache;
! 1926:
! 1927: if ((cache->miscObjs != NULL) &&
! 1928: (cache->miscObjs->number != 0))
! 1929: {
! 1930: xmlXPathObjectPtr ret;
! 1931:
! 1932: ret = (xmlXPathObjectPtr)
! 1933: cache->miscObjs->items[--cache->miscObjs->number];
! 1934: ret->type = XPATH_NODESET;
! 1935: ret->nodesetval = val;
! 1936: #ifdef XP_DEBUG_OBJ_USAGE
! 1937: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
! 1938: #endif
! 1939: return(ret);
! 1940: }
! 1941: }
! 1942:
! 1943: return(xmlXPathWrapNodeSet(val));
! 1944:
! 1945: }
! 1946:
! 1947: /**
! 1948: * xmlXPathCacheWrapString:
! 1949: * @ctxt: the XPath context
! 1950: * @val: the xmlChar * value
! 1951: *
! 1952: * This is the cached version of xmlXPathWrapString().
! 1953: * Wraps the @val string into an XPath object.
! 1954: *
! 1955: * Returns the created or reused object.
! 1956: */
! 1957: static xmlXPathObjectPtr
! 1958: xmlXPathCacheWrapString(xmlXPathContextPtr ctxt, xmlChar *val)
! 1959: {
! 1960: if ((ctxt != NULL) && (ctxt->cache != NULL)) {
! 1961: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
! 1962:
! 1963: if ((cache->stringObjs != NULL) &&
! 1964: (cache->stringObjs->number != 0))
! 1965: {
! 1966:
! 1967: xmlXPathObjectPtr ret;
! 1968:
! 1969: ret = (xmlXPathObjectPtr)
! 1970: cache->stringObjs->items[--cache->stringObjs->number];
! 1971: ret->type = XPATH_STRING;
! 1972: ret->stringval = val;
! 1973: #ifdef XP_DEBUG_OBJ_USAGE
! 1974: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
! 1975: #endif
! 1976: return(ret);
! 1977: } else if ((cache->miscObjs != NULL) &&
! 1978: (cache->miscObjs->number != 0))
! 1979: {
! 1980: xmlXPathObjectPtr ret;
! 1981: /*
! 1982: * Fallback to misc-cache.
! 1983: */
! 1984: ret = (xmlXPathObjectPtr)
! 1985: cache->miscObjs->items[--cache->miscObjs->number];
! 1986:
! 1987: ret->type = XPATH_STRING;
! 1988: ret->stringval = val;
! 1989: #ifdef XP_DEBUG_OBJ_USAGE
! 1990: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
! 1991: #endif
! 1992: return(ret);
! 1993: }
! 1994: }
! 1995: return(xmlXPathWrapString(val));
! 1996: }
! 1997:
! 1998: /**
! 1999: * xmlXPathCacheNewNodeSet:
! 2000: * @ctxt: the XPath context
! 2001: * @val: the NodePtr value
! 2002: *
! 2003: * This is the cached version of xmlXPathNewNodeSet().
! 2004: * Acquire an xmlXPathObjectPtr of type NodeSet and initialize
! 2005: * it with the single Node @val
! 2006: *
! 2007: * Returns the created or reused object.
! 2008: */
! 2009: static xmlXPathObjectPtr
! 2010: xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val)
! 2011: {
! 2012: if ((ctxt != NULL) && (ctxt->cache)) {
! 2013: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
! 2014:
! 2015: if ((cache->nodesetObjs != NULL) &&
! 2016: (cache->nodesetObjs->number != 0))
! 2017: {
! 2018: xmlXPathObjectPtr ret;
! 2019: /*
! 2020: * Use the nodset-cache.
! 2021: */
! 2022: ret = (xmlXPathObjectPtr)
! 2023: cache->nodesetObjs->items[--cache->nodesetObjs->number];
! 2024: ret->type = XPATH_NODESET;
! 2025: ret->boolval = 0;
! 2026: if (val) {
! 2027: if ((ret->nodesetval->nodeMax == 0) ||
! 2028: (val->type == XML_NAMESPACE_DECL))
! 2029: {
! 2030: xmlXPathNodeSetAddUnique(ret->nodesetval, val);
! 2031: } else {
! 2032: ret->nodesetval->nodeTab[0] = val;
! 2033: ret->nodesetval->nodeNr = 1;
! 2034: }
! 2035: }
! 2036: #ifdef XP_DEBUG_OBJ_USAGE
! 2037: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
! 2038: #endif
! 2039: return(ret);
! 2040: } else if ((cache->miscObjs != NULL) &&
! 2041: (cache->miscObjs->number != 0))
! 2042: {
! 2043: xmlXPathObjectPtr ret;
! 2044: /*
! 2045: * Fallback to misc-cache.
! 2046: */
! 2047:
! 2048: ret = (xmlXPathObjectPtr)
! 2049: cache->miscObjs->items[--cache->miscObjs->number];
! 2050:
! 2051: ret->type = XPATH_NODESET;
! 2052: ret->boolval = 0;
! 2053: ret->nodesetval = xmlXPathNodeSetCreate(val);
! 2054: #ifdef XP_DEBUG_OBJ_USAGE
! 2055: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
! 2056: #endif
! 2057: return(ret);
! 2058: }
! 2059: }
! 2060: return(xmlXPathNewNodeSet(val));
! 2061: }
! 2062:
! 2063: /**
! 2064: * xmlXPathCacheNewCString:
! 2065: * @ctxt: the XPath context
! 2066: * @val: the char * value
! 2067: *
! 2068: * This is the cached version of xmlXPathNewCString().
! 2069: * Acquire an xmlXPathObjectPtr of type string and of value @val
! 2070: *
! 2071: * Returns the created or reused object.
! 2072: */
! 2073: static xmlXPathObjectPtr
! 2074: xmlXPathCacheNewCString(xmlXPathContextPtr ctxt, const char *val)
! 2075: {
! 2076: if ((ctxt != NULL) && (ctxt->cache)) {
! 2077: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
! 2078:
! 2079: if ((cache->stringObjs != NULL) &&
! 2080: (cache->stringObjs->number != 0))
! 2081: {
! 2082: xmlXPathObjectPtr ret;
! 2083:
! 2084: ret = (xmlXPathObjectPtr)
! 2085: cache->stringObjs->items[--cache->stringObjs->number];
! 2086:
! 2087: ret->type = XPATH_STRING;
! 2088: ret->stringval = xmlStrdup(BAD_CAST val);
! 2089: #ifdef XP_DEBUG_OBJ_USAGE
! 2090: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
! 2091: #endif
! 2092: return(ret);
! 2093: } else if ((cache->miscObjs != NULL) &&
! 2094: (cache->miscObjs->number != 0))
! 2095: {
! 2096: xmlXPathObjectPtr ret;
! 2097:
! 2098: ret = (xmlXPathObjectPtr)
! 2099: cache->miscObjs->items[--cache->miscObjs->number];
! 2100:
! 2101: ret->type = XPATH_STRING;
! 2102: ret->stringval = xmlStrdup(BAD_CAST val);
! 2103: #ifdef XP_DEBUG_OBJ_USAGE
! 2104: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
! 2105: #endif
! 2106: return(ret);
! 2107: }
! 2108: }
! 2109: return(xmlXPathNewCString(val));
! 2110: }
! 2111:
! 2112: /**
! 2113: * xmlXPathCacheNewString:
! 2114: * @ctxt: the XPath context
! 2115: * @val: the xmlChar * value
! 2116: *
! 2117: * This is the cached version of xmlXPathNewString().
! 2118: * Acquire an xmlXPathObjectPtr of type string and of value @val
! 2119: *
! 2120: * Returns the created or reused object.
! 2121: */
! 2122: static xmlXPathObjectPtr
! 2123: xmlXPathCacheNewString(xmlXPathContextPtr ctxt, const xmlChar *val)
! 2124: {
! 2125: if ((ctxt != NULL) && (ctxt->cache)) {
! 2126: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
! 2127:
! 2128: if ((cache->stringObjs != NULL) &&
! 2129: (cache->stringObjs->number != 0))
! 2130: {
! 2131: xmlXPathObjectPtr ret;
! 2132:
! 2133: ret = (xmlXPathObjectPtr)
! 2134: cache->stringObjs->items[--cache->stringObjs->number];
! 2135: ret->type = XPATH_STRING;
! 2136: if (val != NULL)
! 2137: ret->stringval = xmlStrdup(val);
! 2138: else
! 2139: ret->stringval = xmlStrdup((const xmlChar *)"");
! 2140: #ifdef XP_DEBUG_OBJ_USAGE
! 2141: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
! 2142: #endif
! 2143: return(ret);
! 2144: } else if ((cache->miscObjs != NULL) &&
! 2145: (cache->miscObjs->number != 0))
! 2146: {
! 2147: xmlXPathObjectPtr ret;
! 2148:
! 2149: ret = (xmlXPathObjectPtr)
! 2150: cache->miscObjs->items[--cache->miscObjs->number];
! 2151:
! 2152: ret->type = XPATH_STRING;
! 2153: if (val != NULL)
! 2154: ret->stringval = xmlStrdup(val);
! 2155: else
! 2156: ret->stringval = xmlStrdup((const xmlChar *)"");
! 2157: #ifdef XP_DEBUG_OBJ_USAGE
! 2158: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
! 2159: #endif
! 2160: return(ret);
! 2161: }
! 2162: }
! 2163: return(xmlXPathNewString(val));
! 2164: }
! 2165:
! 2166: /**
! 2167: * xmlXPathCacheNewBoolean:
! 2168: * @ctxt: the XPath context
! 2169: * @val: the boolean value
! 2170: *
! 2171: * This is the cached version of xmlXPathNewBoolean().
! 2172: * Acquires an xmlXPathObjectPtr of type boolean and of value @val
! 2173: *
! 2174: * Returns the created or reused object.
! 2175: */
! 2176: static xmlXPathObjectPtr
! 2177: xmlXPathCacheNewBoolean(xmlXPathContextPtr ctxt, int val)
! 2178: {
! 2179: if ((ctxt != NULL) && (ctxt->cache)) {
! 2180: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
! 2181:
! 2182: if ((cache->booleanObjs != NULL) &&
! 2183: (cache->booleanObjs->number != 0))
! 2184: {
! 2185: xmlXPathObjectPtr ret;
! 2186:
! 2187: ret = (xmlXPathObjectPtr)
! 2188: cache->booleanObjs->items[--cache->booleanObjs->number];
! 2189: ret->type = XPATH_BOOLEAN;
! 2190: ret->boolval = (val != 0);
! 2191: #ifdef XP_DEBUG_OBJ_USAGE
! 2192: xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
! 2193: #endif
! 2194: return(ret);
! 2195: } else if ((cache->miscObjs != NULL) &&
! 2196: (cache->miscObjs->number != 0))
! 2197: {
! 2198: xmlXPathObjectPtr ret;
! 2199:
! 2200: ret = (xmlXPathObjectPtr)
! 2201: cache->miscObjs->items[--cache->miscObjs->number];
! 2202:
! 2203: ret->type = XPATH_BOOLEAN;
! 2204: ret->boolval = (val != 0);
! 2205: #ifdef XP_DEBUG_OBJ_USAGE
! 2206: xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
! 2207: #endif
! 2208: return(ret);
! 2209: }
! 2210: }
! 2211: return(xmlXPathNewBoolean(val));
! 2212: }
! 2213:
! 2214: /**
! 2215: * xmlXPathCacheNewFloat:
! 2216: * @ctxt: the XPath context
! 2217: * @val: the double value
! 2218: *
! 2219: * This is the cached version of xmlXPathNewFloat().
! 2220: * Acquires an xmlXPathObjectPtr of type double and of value @val
! 2221: *
! 2222: * Returns the created or reused object.
! 2223: */
! 2224: static xmlXPathObjectPtr
! 2225: xmlXPathCacheNewFloat(xmlXPathContextPtr ctxt, double val)
! 2226: {
! 2227: if ((ctxt != NULL) && (ctxt->cache)) {
! 2228: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
! 2229:
! 2230: if ((cache->numberObjs != NULL) &&
! 2231: (cache->numberObjs->number != 0))
! 2232: {
! 2233: xmlXPathObjectPtr ret;
! 2234:
! 2235: ret = (xmlXPathObjectPtr)
! 2236: cache->numberObjs->items[--cache->numberObjs->number];
! 2237: ret->type = XPATH_NUMBER;
! 2238: ret->floatval = val;
! 2239: #ifdef XP_DEBUG_OBJ_USAGE
! 2240: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
! 2241: #endif
! 2242: return(ret);
! 2243: } else if ((cache->miscObjs != NULL) &&
! 2244: (cache->miscObjs->number != 0))
! 2245: {
! 2246: xmlXPathObjectPtr ret;
! 2247:
! 2248: ret = (xmlXPathObjectPtr)
! 2249: cache->miscObjs->items[--cache->miscObjs->number];
! 2250:
! 2251: ret->type = XPATH_NUMBER;
! 2252: ret->floatval = val;
! 2253: #ifdef XP_DEBUG_OBJ_USAGE
! 2254: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
! 2255: #endif
! 2256: return(ret);
! 2257: }
! 2258: }
! 2259: return(xmlXPathNewFloat(val));
! 2260: }
! 2261:
! 2262: /**
! 2263: * xmlXPathCacheConvertString:
! 2264: * @ctxt: the XPath context
! 2265: * @val: an XPath object
! 2266: *
! 2267: * This is the cached version of xmlXPathConvertString().
! 2268: * Converts an existing object to its string() equivalent
! 2269: *
! 2270: * Returns a created or reused object, the old one is freed (cached)
! 2271: * (or the operation is done directly on @val)
! 2272: */
! 2273:
! 2274: static xmlXPathObjectPtr
! 2275: xmlXPathCacheConvertString(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
! 2276: xmlChar *res = NULL;
! 2277:
! 2278: if (val == NULL)
! 2279: return(xmlXPathCacheNewCString(ctxt, ""));
! 2280:
! 2281: switch (val->type) {
! 2282: case XPATH_UNDEFINED:
! 2283: #ifdef DEBUG_EXPR
! 2284: xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
! 2285: #endif
! 2286: break;
! 2287: case XPATH_NODESET:
! 2288: case XPATH_XSLT_TREE:
! 2289: res = xmlXPathCastNodeSetToString(val->nodesetval);
! 2290: break;
! 2291: case XPATH_STRING:
! 2292: return(val);
! 2293: case XPATH_BOOLEAN:
! 2294: res = xmlXPathCastBooleanToString(val->boolval);
! 2295: break;
! 2296: case XPATH_NUMBER:
! 2297: res = xmlXPathCastNumberToString(val->floatval);
! 2298: break;
! 2299: case XPATH_USERS:
! 2300: case XPATH_POINT:
! 2301: case XPATH_RANGE:
! 2302: case XPATH_LOCATIONSET:
! 2303: TODO;
! 2304: break;
! 2305: }
! 2306: xmlXPathReleaseObject(ctxt, val);
! 2307: if (res == NULL)
! 2308: return(xmlXPathCacheNewCString(ctxt, ""));
! 2309: return(xmlXPathCacheWrapString(ctxt, res));
! 2310: }
! 2311:
! 2312: /**
! 2313: * xmlXPathCacheObjectCopy:
! 2314: * @ctxt: the XPath context
! 2315: * @val: the original object
! 2316: *
! 2317: * This is the cached version of xmlXPathObjectCopy().
! 2318: * Acquire a copy of a given object
! 2319: *
! 2320: * Returns a created or reused created object.
! 2321: */
! 2322: static xmlXPathObjectPtr
! 2323: xmlXPathCacheObjectCopy(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val)
! 2324: {
! 2325: if (val == NULL)
! 2326: return(NULL);
! 2327:
! 2328: if (XP_HAS_CACHE(ctxt)) {
! 2329: switch (val->type) {
! 2330: case XPATH_NODESET:
! 2331: return(xmlXPathCacheWrapNodeSet(ctxt,
! 2332: xmlXPathNodeSetMerge(NULL, val->nodesetval)));
! 2333: case XPATH_STRING:
! 2334: return(xmlXPathCacheNewString(ctxt, val->stringval));
! 2335: case XPATH_BOOLEAN:
! 2336: return(xmlXPathCacheNewBoolean(ctxt, val->boolval));
! 2337: case XPATH_NUMBER:
! 2338: return(xmlXPathCacheNewFloat(ctxt, val->floatval));
! 2339: default:
! 2340: break;
! 2341: }
! 2342: }
! 2343: return(xmlXPathObjectCopy(val));
! 2344: }
! 2345:
! 2346: /**
! 2347: * xmlXPathCacheConvertBoolean:
! 2348: * @ctxt: the XPath context
! 2349: * @val: an XPath object
! 2350: *
! 2351: * This is the cached version of xmlXPathConvertBoolean().
! 2352: * Converts an existing object to its boolean() equivalent
! 2353: *
! 2354: * Returns a created or reused object, the old one is freed (or the operation
! 2355: * is done directly on @val)
! 2356: */
! 2357: static xmlXPathObjectPtr
! 2358: xmlXPathCacheConvertBoolean(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
! 2359: xmlXPathObjectPtr ret;
! 2360:
! 2361: if (val == NULL)
! 2362: return(xmlXPathCacheNewBoolean(ctxt, 0));
! 2363: if (val->type == XPATH_BOOLEAN)
! 2364: return(val);
! 2365: ret = xmlXPathCacheNewBoolean(ctxt, xmlXPathCastToBoolean(val));
! 2366: xmlXPathReleaseObject(ctxt, val);
! 2367: return(ret);
! 2368: }
! 2369:
! 2370: /**
! 2371: * xmlXPathCacheConvertNumber:
! 2372: * @ctxt: the XPath context
! 2373: * @val: an XPath object
! 2374: *
! 2375: * This is the cached version of xmlXPathConvertNumber().
! 2376: * Converts an existing object to its number() equivalent
! 2377: *
! 2378: * Returns a created or reused object, the old one is freed (or the operation
! 2379: * is done directly on @val)
! 2380: */
! 2381: static xmlXPathObjectPtr
! 2382: xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
! 2383: xmlXPathObjectPtr ret;
! 2384:
! 2385: if (val == NULL)
! 2386: return(xmlXPathCacheNewFloat(ctxt, 0.0));
! 2387: if (val->type == XPATH_NUMBER)
! 2388: return(val);
! 2389: ret = xmlXPathCacheNewFloat(ctxt, xmlXPathCastToNumber(val));
! 2390: xmlXPathReleaseObject(ctxt, val);
! 2391: return(ret);
! 2392: }
! 2393:
! 2394: /************************************************************************
! 2395: * *
! 2396: * Parser stacks related functions and macros *
! 2397: * *
! 2398: ************************************************************************/
! 2399:
! 2400: /**
! 2401: * valuePop:
! 2402: * @ctxt: an XPath evaluation context
! 2403: *
! 2404: * Pops the top XPath object from the value stack
! 2405: *
! 2406: * Returns the XPath object just removed
! 2407: */
! 2408: xmlXPathObjectPtr
! 2409: valuePop(xmlXPathParserContextPtr ctxt)
! 2410: {
! 2411: xmlXPathObjectPtr ret;
! 2412:
! 2413: if ((ctxt == NULL) || (ctxt->valueNr <= 0))
! 2414: return (NULL);
! 2415: ctxt->valueNr--;
! 2416: if (ctxt->valueNr > 0)
! 2417: ctxt->value = ctxt->valueTab[ctxt->valueNr - 1];
! 2418: else
! 2419: ctxt->value = NULL;
! 2420: ret = ctxt->valueTab[ctxt->valueNr];
! 2421: ctxt->valueTab[ctxt->valueNr] = NULL;
! 2422: return (ret);
! 2423: }
! 2424: /**
! 2425: * valuePush:
! 2426: * @ctxt: an XPath evaluation context
! 2427: * @value: the XPath object
! 2428: *
! 2429: * Pushes a new XPath object on top of the value stack
! 2430: *
! 2431: * returns the number of items on the value stack
! 2432: */
! 2433: int
! 2434: valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
! 2435: {
! 2436: if ((ctxt == NULL) || (value == NULL)) return(-1);
! 2437: if (ctxt->valueNr >= ctxt->valueMax) {
! 2438: xmlXPathObjectPtr *tmp;
! 2439:
! 2440: tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab,
! 2441: 2 * ctxt->valueMax *
! 2442: sizeof(ctxt->valueTab[0]));
! 2443: if (tmp == NULL) {
! 2444: xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
! 2445: return (0);
! 2446: }
! 2447: ctxt->valueMax *= 2;
! 2448: ctxt->valueTab = tmp;
! 2449: }
! 2450: ctxt->valueTab[ctxt->valueNr] = value;
! 2451: ctxt->value = value;
! 2452: return (ctxt->valueNr++);
! 2453: }
! 2454:
! 2455: /**
! 2456: * xmlXPathPopBoolean:
! 2457: * @ctxt: an XPath parser context
! 2458: *
! 2459: * Pops a boolean from the stack, handling conversion if needed.
! 2460: * Check error with #xmlXPathCheckError.
! 2461: *
! 2462: * Returns the boolean
! 2463: */
! 2464: int
! 2465: xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
! 2466: xmlXPathObjectPtr obj;
! 2467: int ret;
! 2468:
! 2469: obj = valuePop(ctxt);
! 2470: if (obj == NULL) {
! 2471: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
! 2472: return(0);
! 2473: }
! 2474: if (obj->type != XPATH_BOOLEAN)
! 2475: ret = xmlXPathCastToBoolean(obj);
! 2476: else
! 2477: ret = obj->boolval;
! 2478: xmlXPathReleaseObject(ctxt->context, obj);
! 2479: return(ret);
! 2480: }
! 2481:
! 2482: /**
! 2483: * xmlXPathPopNumber:
! 2484: * @ctxt: an XPath parser context
! 2485: *
! 2486: * Pops a number from the stack, handling conversion if needed.
! 2487: * Check error with #xmlXPathCheckError.
! 2488: *
! 2489: * Returns the number
! 2490: */
! 2491: double
! 2492: xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
! 2493: xmlXPathObjectPtr obj;
! 2494: double ret;
! 2495:
! 2496: obj = valuePop(ctxt);
! 2497: if (obj == NULL) {
! 2498: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
! 2499: return(0);
! 2500: }
! 2501: if (obj->type != XPATH_NUMBER)
! 2502: ret = xmlXPathCastToNumber(obj);
! 2503: else
! 2504: ret = obj->floatval;
! 2505: xmlXPathReleaseObject(ctxt->context, obj);
! 2506: return(ret);
! 2507: }
! 2508:
! 2509: /**
! 2510: * xmlXPathPopString:
! 2511: * @ctxt: an XPath parser context
! 2512: *
! 2513: * Pops a string from the stack, handling conversion if needed.
! 2514: * Check error with #xmlXPathCheckError.
! 2515: *
! 2516: * Returns the string
! 2517: */
! 2518: xmlChar *
! 2519: xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
! 2520: xmlXPathObjectPtr obj;
! 2521: xmlChar * ret;
! 2522:
! 2523: obj = valuePop(ctxt);
! 2524: if (obj == NULL) {
! 2525: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
! 2526: return(NULL);
! 2527: }
! 2528: ret = xmlXPathCastToString(obj); /* this does required strdup */
! 2529: /* TODO: needs refactoring somewhere else */
! 2530: if (obj->stringval == ret)
! 2531: obj->stringval = NULL;
! 2532: xmlXPathReleaseObject(ctxt->context, obj);
! 2533: return(ret);
! 2534: }
! 2535:
! 2536: /**
! 2537: * xmlXPathPopNodeSet:
! 2538: * @ctxt: an XPath parser context
! 2539: *
! 2540: * Pops a node-set from the stack, handling conversion if needed.
! 2541: * Check error with #xmlXPathCheckError.
! 2542: *
! 2543: * Returns the node-set
! 2544: */
! 2545: xmlNodeSetPtr
! 2546: xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
! 2547: xmlXPathObjectPtr obj;
! 2548: xmlNodeSetPtr ret;
! 2549:
! 2550: if (ctxt == NULL) return(NULL);
! 2551: if (ctxt->value == NULL) {
! 2552: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
! 2553: return(NULL);
! 2554: }
! 2555: if (!xmlXPathStackIsNodeSet(ctxt)) {
! 2556: xmlXPathSetTypeError(ctxt);
! 2557: return(NULL);
! 2558: }
! 2559: obj = valuePop(ctxt);
! 2560: ret = obj->nodesetval;
! 2561: #if 0
! 2562: /* to fix memory leak of not clearing obj->user */
! 2563: if (obj->boolval && obj->user != NULL)
! 2564: xmlFreeNodeList((xmlNodePtr) obj->user);
! 2565: #endif
! 2566: obj->nodesetval = NULL;
! 2567: xmlXPathReleaseObject(ctxt->context, obj);
! 2568: return(ret);
! 2569: }
! 2570:
! 2571: /**
! 2572: * xmlXPathPopExternal:
! 2573: * @ctxt: an XPath parser context
! 2574: *
! 2575: * Pops an external object from the stack, handling conversion if needed.
! 2576: * Check error with #xmlXPathCheckError.
! 2577: *
! 2578: * Returns the object
! 2579: */
! 2580: void *
! 2581: xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
! 2582: xmlXPathObjectPtr obj;
! 2583: void * ret;
! 2584:
! 2585: if ((ctxt == NULL) || (ctxt->value == NULL)) {
! 2586: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
! 2587: return(NULL);
! 2588: }
! 2589: if (ctxt->value->type != XPATH_USERS) {
! 2590: xmlXPathSetTypeError(ctxt);
! 2591: return(NULL);
! 2592: }
! 2593: obj = valuePop(ctxt);
! 2594: ret = obj->user;
! 2595: obj->user = NULL;
! 2596: xmlXPathReleaseObject(ctxt->context, obj);
! 2597: return(ret);
! 2598: }
! 2599:
! 2600: /*
! 2601: * Macros for accessing the content. Those should be used only by the parser,
! 2602: * and not exported.
! 2603: *
! 2604: * Dirty macros, i.e. one need to make assumption on the context to use them
! 2605: *
! 2606: * CUR_PTR return the current pointer to the xmlChar to be parsed.
! 2607: * CUR returns the current xmlChar value, i.e. a 8 bit value
! 2608: * in ISO-Latin or UTF-8.
! 2609: * This should be used internally by the parser
! 2610: * only to compare to ASCII values otherwise it would break when
! 2611: * running with UTF-8 encoding.
! 2612: * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
! 2613: * to compare on ASCII based substring.
! 2614: * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
! 2615: * strings within the parser.
! 2616: * CURRENT Returns the current char value, with the full decoding of
! 2617: * UTF-8 if we are using this mode. It returns an int.
! 2618: * NEXT Skip to the next character, this does the proper decoding
! 2619: * in UTF-8 mode. It also pop-up unfinished entities on the fly.
! 2620: * It returns the pointer to the current xmlChar.
! 2621: */
! 2622:
! 2623: #define CUR (*ctxt->cur)
! 2624: #define SKIP(val) ctxt->cur += (val)
! 2625: #define NXT(val) ctxt->cur[(val)]
! 2626: #define CUR_PTR ctxt->cur
! 2627: #define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
! 2628:
! 2629: #define COPY_BUF(l,b,i,v) \
! 2630: if (l == 1) b[i++] = (xmlChar) v; \
! 2631: else i += xmlCopyChar(l,&b[i],v)
! 2632:
! 2633: #define NEXTL(l) ctxt->cur += l
! 2634:
! 2635: #define SKIP_BLANKS \
! 2636: while (IS_BLANK_CH(*(ctxt->cur))) NEXT
! 2637:
! 2638: #define CURRENT (*ctxt->cur)
! 2639: #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
! 2640:
! 2641:
! 2642: #ifndef DBL_DIG
! 2643: #define DBL_DIG 16
! 2644: #endif
! 2645: #ifndef DBL_EPSILON
! 2646: #define DBL_EPSILON 1E-9
! 2647: #endif
! 2648:
! 2649: #define UPPER_DOUBLE 1E9
! 2650: #define LOWER_DOUBLE 1E-5
! 2651: #define LOWER_DOUBLE_EXP 5
! 2652:
! 2653: #define INTEGER_DIGITS DBL_DIG
! 2654: #define FRACTION_DIGITS (DBL_DIG + 1 + (LOWER_DOUBLE_EXP))
! 2655: #define EXPONENT_DIGITS (3 + 2)
! 2656:
! 2657: /**
! 2658: * xmlXPathFormatNumber:
! 2659: * @number: number to format
! 2660: * @buffer: output buffer
! 2661: * @buffersize: size of output buffer
! 2662: *
! 2663: * Convert the number into a string representation.
! 2664: */
! 2665: static void
! 2666: xmlXPathFormatNumber(double number, char buffer[], int buffersize)
! 2667: {
! 2668: switch (xmlXPathIsInf(number)) {
! 2669: case 1:
! 2670: if (buffersize > (int)sizeof("Infinity"))
! 2671: snprintf(buffer, buffersize, "Infinity");
! 2672: break;
! 2673: case -1:
! 2674: if (buffersize > (int)sizeof("-Infinity"))
! 2675: snprintf(buffer, buffersize, "-Infinity");
! 2676: break;
! 2677: default:
! 2678: if (xmlXPathIsNaN(number)) {
! 2679: if (buffersize > (int)sizeof("NaN"))
! 2680: snprintf(buffer, buffersize, "NaN");
! 2681: } else if (number == 0 && xmlXPathGetSign(number) != 0) {
! 2682: snprintf(buffer, buffersize, "0");
! 2683: } else if (number == ((int) number)) {
! 2684: char work[30];
! 2685: char *ptr, *cur;
! 2686: int value = (int) number;
! 2687:
! 2688: ptr = &buffer[0];
! 2689: if (value == 0) {
! 2690: *ptr++ = '0';
! 2691: } else {
! 2692: snprintf(work, 29, "%d", value);
! 2693: cur = &work[0];
! 2694: while ((*cur) && (ptr - buffer < buffersize)) {
! 2695: *ptr++ = *cur++;
! 2696: }
! 2697: }
! 2698: if (ptr - buffer < buffersize) {
! 2699: *ptr = 0;
! 2700: } else if (buffersize > 0) {
! 2701: ptr--;
! 2702: *ptr = 0;
! 2703: }
! 2704: } else {
! 2705: /*
! 2706: For the dimension of work,
! 2707: DBL_DIG is number of significant digits
! 2708: EXPONENT is only needed for "scientific notation"
! 2709: 3 is sign, decimal point, and terminating zero
! 2710: LOWER_DOUBLE_EXP is max number of leading zeroes in fraction
! 2711: Note that this dimension is slightly (a few characters)
! 2712: larger than actually necessary.
! 2713: */
! 2714: char work[DBL_DIG + EXPONENT_DIGITS + 3 + LOWER_DOUBLE_EXP];
! 2715: int integer_place, fraction_place;
! 2716: char *ptr;
! 2717: char *after_fraction;
! 2718: double absolute_value;
! 2719: int size;
! 2720:
! 2721: absolute_value = fabs(number);
! 2722:
! 2723: /*
! 2724: * First choose format - scientific or regular floating point.
! 2725: * In either case, result is in work, and after_fraction points
! 2726: * just past the fractional part.
! 2727: */
! 2728: if ( ((absolute_value > UPPER_DOUBLE) ||
! 2729: (absolute_value < LOWER_DOUBLE)) &&
! 2730: (absolute_value != 0.0) ) {
! 2731: /* Use scientific notation */
! 2732: integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
! 2733: fraction_place = DBL_DIG - 1;
! 2734: size = snprintf(work, sizeof(work),"%*.*e",
! 2735: integer_place, fraction_place, number);
! 2736: while ((size > 0) && (work[size] != 'e')) size--;
! 2737:
! 2738: }
! 2739: else {
! 2740: /* Use regular notation */
! 2741: if (absolute_value > 0.0) {
! 2742: integer_place = (int)log10(absolute_value);
! 2743: if (integer_place > 0)
! 2744: fraction_place = DBL_DIG - integer_place - 1;
! 2745: else
! 2746: fraction_place = DBL_DIG - integer_place;
! 2747: } else {
! 2748: fraction_place = 1;
! 2749: }
! 2750: size = snprintf(work, sizeof(work), "%0.*f",
! 2751: fraction_place, number);
! 2752: }
! 2753:
! 2754: /* Remove fractional trailing zeroes */
! 2755: after_fraction = work + size;
! 2756: ptr = after_fraction;
! 2757: while (*(--ptr) == '0')
! 2758: ;
! 2759: if (*ptr != '.')
! 2760: ptr++;
! 2761: while ((*ptr++ = *after_fraction++) != 0);
! 2762:
! 2763: /* Finally copy result back to caller */
! 2764: size = strlen(work) + 1;
! 2765: if (size > buffersize) {
! 2766: work[buffersize - 1] = 0;
! 2767: size = buffersize;
! 2768: }
! 2769: memmove(buffer, work, size);
! 2770: }
! 2771: break;
! 2772: }
! 2773: }
! 2774:
! 2775:
! 2776: /************************************************************************
! 2777: * *
! 2778: * Routines to handle NodeSets *
! 2779: * *
! 2780: ************************************************************************/
! 2781:
! 2782: /**
! 2783: * xmlXPathOrderDocElems:
! 2784: * @doc: an input document
! 2785: *
! 2786: * Call this routine to speed up XPath computation on static documents.
! 2787: * This stamps all the element nodes with the document order
! 2788: * Like for line information, the order is kept in the element->content
! 2789: * field, the value stored is actually - the node number (starting at -1)
! 2790: * to be able to differentiate from line numbers.
! 2791: *
! 2792: * Returns the number of elements found in the document or -1 in case
! 2793: * of error.
! 2794: */
! 2795: long
! 2796: xmlXPathOrderDocElems(xmlDocPtr doc) {
! 2797: long count = 0;
! 2798: xmlNodePtr cur;
! 2799:
! 2800: if (doc == NULL)
! 2801: return(-1);
! 2802: cur = doc->children;
! 2803: while (cur != NULL) {
! 2804: if (cur->type == XML_ELEMENT_NODE) {
! 2805: cur->content = (void *) (-(++count));
! 2806: if (cur->children != NULL) {
! 2807: cur = cur->children;
! 2808: continue;
! 2809: }
! 2810: }
! 2811: if (cur->next != NULL) {
! 2812: cur = cur->next;
! 2813: continue;
! 2814: }
! 2815: do {
! 2816: cur = cur->parent;
! 2817: if (cur == NULL)
! 2818: break;
! 2819: if (cur == (xmlNodePtr) doc) {
! 2820: cur = NULL;
! 2821: break;
! 2822: }
! 2823: if (cur->next != NULL) {
! 2824: cur = cur->next;
! 2825: break;
! 2826: }
! 2827: } while (cur != NULL);
! 2828: }
! 2829: return(count);
! 2830: }
! 2831:
! 2832: /**
! 2833: * xmlXPathCmpNodes:
! 2834: * @node1: the first node
! 2835: * @node2: the second node
! 2836: *
! 2837: * Compare two nodes w.r.t document order
! 2838: *
! 2839: * Returns -2 in case of error 1 if first point < second point, 0 if
! 2840: * it's the same node, -1 otherwise
! 2841: */
! 2842: int
! 2843: xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
! 2844: int depth1, depth2;
! 2845: int attr1 = 0, attr2 = 0;
! 2846: xmlNodePtr attrNode1 = NULL, attrNode2 = NULL;
! 2847: xmlNodePtr cur, root;
! 2848:
! 2849: if ((node1 == NULL) || (node2 == NULL))
! 2850: return(-2);
! 2851: /*
! 2852: * a couple of optimizations which will avoid computations in most cases
! 2853: */
! 2854: if (node1 == node2) /* trivial case */
! 2855: return(0);
! 2856: if (node1->type == XML_ATTRIBUTE_NODE) {
! 2857: attr1 = 1;
! 2858: attrNode1 = node1;
! 2859: node1 = node1->parent;
! 2860: }
! 2861: if (node2->type == XML_ATTRIBUTE_NODE) {
! 2862: attr2 = 1;
! 2863: attrNode2 = node2;
! 2864: node2 = node2->parent;
! 2865: }
! 2866: if (node1 == node2) {
! 2867: if (attr1 == attr2) {
! 2868: /* not required, but we keep attributes in order */
! 2869: if (attr1 != 0) {
! 2870: cur = attrNode2->prev;
! 2871: while (cur != NULL) {
! 2872: if (cur == attrNode1)
! 2873: return (1);
! 2874: cur = cur->prev;
! 2875: }
! 2876: return (-1);
! 2877: }
! 2878: return(0);
! 2879: }
! 2880: if (attr2 == 1)
! 2881: return(1);
! 2882: return(-1);
! 2883: }
! 2884: if ((node1->type == XML_NAMESPACE_DECL) ||
! 2885: (node2->type == XML_NAMESPACE_DECL))
! 2886: return(1);
! 2887: if (node1 == node2->prev)
! 2888: return(1);
! 2889: if (node1 == node2->next)
! 2890: return(-1);
! 2891:
! 2892: /*
! 2893: * Speedup using document order if availble.
! 2894: */
! 2895: if ((node1->type == XML_ELEMENT_NODE) &&
! 2896: (node2->type == XML_ELEMENT_NODE) &&
! 2897: (0 > (long) node1->content) &&
! 2898: (0 > (long) node2->content) &&
! 2899: (node1->doc == node2->doc)) {
! 2900: long l1, l2;
! 2901:
! 2902: l1 = -((long) node1->content);
! 2903: l2 = -((long) node2->content);
! 2904: if (l1 < l2)
! 2905: return(1);
! 2906: if (l1 > l2)
! 2907: return(-1);
! 2908: }
! 2909:
! 2910: /*
! 2911: * compute depth to root
! 2912: */
! 2913: for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
! 2914: if (cur == node1)
! 2915: return(1);
! 2916: depth2++;
! 2917: }
! 2918: root = cur;
! 2919: for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
! 2920: if (cur == node2)
! 2921: return(-1);
! 2922: depth1++;
! 2923: }
! 2924: /*
! 2925: * Distinct document (or distinct entities :-( ) case.
! 2926: */
! 2927: if (root != cur) {
! 2928: return(-2);
! 2929: }
! 2930: /*
! 2931: * get the nearest common ancestor.
! 2932: */
! 2933: while (depth1 > depth2) {
! 2934: depth1--;
! 2935: node1 = node1->parent;
! 2936: }
! 2937: while (depth2 > depth1) {
! 2938: depth2--;
! 2939: node2 = node2->parent;
! 2940: }
! 2941: while (node1->parent != node2->parent) {
! 2942: node1 = node1->parent;
! 2943: node2 = node2->parent;
! 2944: /* should not happen but just in case ... */
! 2945: if ((node1 == NULL) || (node2 == NULL))
! 2946: return(-2);
! 2947: }
! 2948: /*
! 2949: * Find who's first.
! 2950: */
! 2951: if (node1 == node2->prev)
! 2952: return(1);
! 2953: if (node1 == node2->next)
! 2954: return(-1);
! 2955: /*
! 2956: * Speedup using document order if availble.
! 2957: */
! 2958: if ((node1->type == XML_ELEMENT_NODE) &&
! 2959: (node2->type == XML_ELEMENT_NODE) &&
! 2960: (0 > (long) node1->content) &&
! 2961: (0 > (long) node2->content) &&
! 2962: (node1->doc == node2->doc)) {
! 2963: long l1, l2;
! 2964:
! 2965: l1 = -((long) node1->content);
! 2966: l2 = -((long) node2->content);
! 2967: if (l1 < l2)
! 2968: return(1);
! 2969: if (l1 > l2)
! 2970: return(-1);
! 2971: }
! 2972:
! 2973: for (cur = node1->next;cur != NULL;cur = cur->next)
! 2974: if (cur == node2)
! 2975: return(1);
! 2976: return(-1); /* assume there is no sibling list corruption */
! 2977: }
! 2978:
! 2979: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
! 2980: /**
! 2981: * xmlXPathCmpNodesExt:
! 2982: * @node1: the first node
! 2983: * @node2: the second node
! 2984: *
! 2985: * Compare two nodes w.r.t document order.
! 2986: * This one is optimized for handling of non-element nodes.
! 2987: *
! 2988: * Returns -2 in case of error 1 if first point < second point, 0 if
! 2989: * it's the same node, -1 otherwise
! 2990: */
! 2991: static int
! 2992: xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) {
! 2993: int depth1, depth2;
! 2994: int misc = 0, precedence1 = 0, precedence2 = 0;
! 2995: xmlNodePtr miscNode1 = NULL, miscNode2 = NULL;
! 2996: xmlNodePtr cur, root;
! 2997: long l1, l2;
! 2998:
! 2999: if ((node1 == NULL) || (node2 == NULL))
! 3000: return(-2);
! 3001:
! 3002: if (node1 == node2)
! 3003: return(0);
! 3004:
! 3005: /*
! 3006: * a couple of optimizations which will avoid computations in most cases
! 3007: */
! 3008: switch (node1->type) {
! 3009: case XML_ELEMENT_NODE:
! 3010: if (node2->type == XML_ELEMENT_NODE) {
! 3011: if ((0 > (long) node1->content) && /* TODO: Would a != 0 suffice here? */
! 3012: (0 > (long) node2->content) &&
! 3013: (node1->doc == node2->doc))
! 3014: {
! 3015: l1 = -((long) node1->content);
! 3016: l2 = -((long) node2->content);
! 3017: if (l1 < l2)
! 3018: return(1);
! 3019: if (l1 > l2)
! 3020: return(-1);
! 3021: } else
! 3022: goto turtle_comparison;
! 3023: }
! 3024: break;
! 3025: case XML_ATTRIBUTE_NODE:
! 3026: precedence1 = 1; /* element is owner */
! 3027: miscNode1 = node1;
! 3028: node1 = node1->parent;
! 3029: misc = 1;
! 3030: break;
! 3031: case XML_TEXT_NODE:
! 3032: case XML_CDATA_SECTION_NODE:
! 3033: case XML_COMMENT_NODE:
! 3034: case XML_PI_NODE: {
! 3035: miscNode1 = node1;
! 3036: /*
! 3037: * Find nearest element node.
! 3038: */
! 3039: if (node1->prev != NULL) {
! 3040: do {
! 3041: node1 = node1->prev;
! 3042: if (node1->type == XML_ELEMENT_NODE) {
! 3043: precedence1 = 3; /* element in prev-sibl axis */
! 3044: break;
! 3045: }
! 3046: if (node1->prev == NULL) {
! 3047: precedence1 = 2; /* element is parent */
! 3048: /*
! 3049: * URGENT TODO: Are there any cases, where the
! 3050: * parent of such a node is not an element node?
! 3051: */
! 3052: node1 = node1->parent;
! 3053: break;
! 3054: }
! 3055: } while (1);
! 3056: } else {
! 3057: precedence1 = 2; /* element is parent */
! 3058: node1 = node1->parent;
! 3059: }
! 3060: if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE) ||
! 3061: (0 <= (long) node1->content)) {
! 3062: /*
! 3063: * Fallback for whatever case.
! 3064: */
! 3065: node1 = miscNode1;
! 3066: precedence1 = 0;
! 3067: } else
! 3068: misc = 1;
! 3069: }
! 3070: break;
! 3071: case XML_NAMESPACE_DECL:
! 3072: /*
! 3073: * TODO: why do we return 1 for namespace nodes?
! 3074: */
! 3075: return(1);
! 3076: default:
! 3077: break;
! 3078: }
! 3079: switch (node2->type) {
! 3080: case XML_ELEMENT_NODE:
! 3081: break;
! 3082: case XML_ATTRIBUTE_NODE:
! 3083: precedence2 = 1; /* element is owner */
! 3084: miscNode2 = node2;
! 3085: node2 = node2->parent;
! 3086: misc = 1;
! 3087: break;
! 3088: case XML_TEXT_NODE:
! 3089: case XML_CDATA_SECTION_NODE:
! 3090: case XML_COMMENT_NODE:
! 3091: case XML_PI_NODE: {
! 3092: miscNode2 = node2;
! 3093: if (node2->prev != NULL) {
! 3094: do {
! 3095: node2 = node2->prev;
! 3096: if (node2->type == XML_ELEMENT_NODE) {
! 3097: precedence2 = 3; /* element in prev-sibl axis */
! 3098: break;
! 3099: }
! 3100: if (node2->prev == NULL) {
! 3101: precedence2 = 2; /* element is parent */
! 3102: node2 = node2->parent;
! 3103: break;
! 3104: }
! 3105: } while (1);
! 3106: } else {
! 3107: precedence2 = 2; /* element is parent */
! 3108: node2 = node2->parent;
! 3109: }
! 3110: if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) ||
! 3111: (0 <= (long) node1->content))
! 3112: {
! 3113: node2 = miscNode2;
! 3114: precedence2 = 0;
! 3115: } else
! 3116: misc = 1;
! 3117: }
! 3118: break;
! 3119: case XML_NAMESPACE_DECL:
! 3120: return(1);
! 3121: default:
! 3122: break;
! 3123: }
! 3124: if (misc) {
! 3125: if (node1 == node2) {
! 3126: if (precedence1 == precedence2) {
! 3127: /*
! 3128: * The ugly case; but normally there aren't many
! 3129: * adjacent non-element nodes around.
! 3130: */
! 3131: cur = miscNode2->prev;
! 3132: while (cur != NULL) {
! 3133: if (cur == miscNode1)
! 3134: return(1);
! 3135: if (cur->type == XML_ELEMENT_NODE)
! 3136: return(-1);
! 3137: cur = cur->prev;
! 3138: }
! 3139: return (-1);
! 3140: } else {
! 3141: /*
! 3142: * Evaluate based on higher precedence wrt to the element.
! 3143: * TODO: This assumes attributes are sorted before content.
! 3144: * Is this 100% correct?
! 3145: */
! 3146: if (precedence1 < precedence2)
! 3147: return(1);
! 3148: else
! 3149: return(-1);
! 3150: }
! 3151: }
! 3152: /*
! 3153: * Special case: One of the helper-elements is contained by the other.
! 3154: * <foo>
! 3155: * <node2>
! 3156: * <node1>Text-1(precedence1 == 2)</node1>
! 3157: * </node2>
! 3158: * Text-6(precedence2 == 3)
! 3159: * </foo>
! 3160: */
! 3161: if ((precedence2 == 3) && (precedence1 > 1)) {
! 3162: cur = node1->parent;
! 3163: while (cur) {
! 3164: if (cur == node2)
! 3165: return(1);
! 3166: cur = cur->parent;
! 3167: }
! 3168: }
! 3169: if ((precedence1 == 3) && (precedence2 > 1)) {
! 3170: cur = node2->parent;
! 3171: while (cur) {
! 3172: if (cur == node1)
! 3173: return(-1);
! 3174: cur = cur->parent;
! 3175: }
! 3176: }
! 3177: }
! 3178:
! 3179: /*
! 3180: * Speedup using document order if availble.
! 3181: */
! 3182: if ((node1->type == XML_ELEMENT_NODE) &&
! 3183: (node2->type == XML_ELEMENT_NODE) &&
! 3184: (0 > (long) node1->content) &&
! 3185: (0 > (long) node2->content) &&
! 3186: (node1->doc == node2->doc)) {
! 3187:
! 3188: l1 = -((long) node1->content);
! 3189: l2 = -((long) node2->content);
! 3190: if (l1 < l2)
! 3191: return(1);
! 3192: if (l1 > l2)
! 3193: return(-1);
! 3194: }
! 3195:
! 3196: turtle_comparison:
! 3197:
! 3198: if (node1 == node2->prev)
! 3199: return(1);
! 3200: if (node1 == node2->next)
! 3201: return(-1);
! 3202: /*
! 3203: * compute depth to root
! 3204: */
! 3205: for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
! 3206: if (cur == node1)
! 3207: return(1);
! 3208: depth2++;
! 3209: }
! 3210: root = cur;
! 3211: for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
! 3212: if (cur == node2)
! 3213: return(-1);
! 3214: depth1++;
! 3215: }
! 3216: /*
! 3217: * Distinct document (or distinct entities :-( ) case.
! 3218: */
! 3219: if (root != cur) {
! 3220: return(-2);
! 3221: }
! 3222: /*
! 3223: * get the nearest common ancestor.
! 3224: */
! 3225: while (depth1 > depth2) {
! 3226: depth1--;
! 3227: node1 = node1->parent;
! 3228: }
! 3229: while (depth2 > depth1) {
! 3230: depth2--;
! 3231: node2 = node2->parent;
! 3232: }
! 3233: while (node1->parent != node2->parent) {
! 3234: node1 = node1->parent;
! 3235: node2 = node2->parent;
! 3236: /* should not happen but just in case ... */
! 3237: if ((node1 == NULL) || (node2 == NULL))
! 3238: return(-2);
! 3239: }
! 3240: /*
! 3241: * Find who's first.
! 3242: */
! 3243: if (node1 == node2->prev)
! 3244: return(1);
! 3245: if (node1 == node2->next)
! 3246: return(-1);
! 3247: /*
! 3248: * Speedup using document order if availble.
! 3249: */
! 3250: if ((node1->type == XML_ELEMENT_NODE) &&
! 3251: (node2->type == XML_ELEMENT_NODE) &&
! 3252: (0 > (long) node1->content) &&
! 3253: (0 > (long) node2->content) &&
! 3254: (node1->doc == node2->doc)) {
! 3255:
! 3256: l1 = -((long) node1->content);
! 3257: l2 = -((long) node2->content);
! 3258: if (l1 < l2)
! 3259: return(1);
! 3260: if (l1 > l2)
! 3261: return(-1);
! 3262: }
! 3263:
! 3264: for (cur = node1->next;cur != NULL;cur = cur->next)
! 3265: if (cur == node2)
! 3266: return(1);
! 3267: return(-1); /* assume there is no sibling list corruption */
! 3268: }
! 3269: #endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */
! 3270:
! 3271: /**
! 3272: * xmlXPathNodeSetSort:
! 3273: * @set: the node set
! 3274: *
! 3275: * Sort the node set in document order
! 3276: */
! 3277: void
! 3278: xmlXPathNodeSetSort(xmlNodeSetPtr set) {
! 3279: int i, j, incr, len;
! 3280: xmlNodePtr tmp;
! 3281:
! 3282: if (set == NULL)
! 3283: return;
! 3284:
! 3285: /* Use Shell's sort to sort the node-set */
! 3286: len = set->nodeNr;
! 3287: for (incr = len / 2; incr > 0; incr /= 2) {
! 3288: for (i = incr; i < len; i++) {
! 3289: j = i - incr;
! 3290: while (j >= 0) {
! 3291: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
! 3292: if (xmlXPathCmpNodesExt(set->nodeTab[j],
! 3293: set->nodeTab[j + incr]) == -1)
! 3294: #else
! 3295: if (xmlXPathCmpNodes(set->nodeTab[j],
! 3296: set->nodeTab[j + incr]) == -1)
! 3297: #endif
! 3298: {
! 3299: tmp = set->nodeTab[j];
! 3300: set->nodeTab[j] = set->nodeTab[j + incr];
! 3301: set->nodeTab[j + incr] = tmp;
! 3302: j -= incr;
! 3303: } else
! 3304: break;
! 3305: }
! 3306: }
! 3307: }
! 3308: }
! 3309:
! 3310: #define XML_NODESET_DEFAULT 10
! 3311: /**
! 3312: * xmlXPathNodeSetDupNs:
! 3313: * @node: the parent node of the namespace XPath node
! 3314: * @ns: the libxml namespace declaration node.
! 3315: *
! 3316: * Namespace node in libxml don't match the XPath semantic. In a node set
! 3317: * the namespace nodes are duplicated and the next pointer is set to the
! 3318: * parent node in the XPath semantic.
! 3319: *
! 3320: * Returns the newly created object.
! 3321: */
! 3322: static xmlNodePtr
! 3323: xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) {
! 3324: xmlNsPtr cur;
! 3325:
! 3326: if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
! 3327: return(NULL);
! 3328: if ((node == NULL) || (node->type == XML_NAMESPACE_DECL))
! 3329: return((xmlNodePtr) ns);
! 3330:
! 3331: /*
! 3332: * Allocate a new Namespace and fill the fields.
! 3333: */
! 3334: cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
! 3335: if (cur == NULL) {
! 3336: xmlXPathErrMemory(NULL, "duplicating namespace\n");
! 3337: return(NULL);
! 3338: }
! 3339: memset(cur, 0, sizeof(xmlNs));
! 3340: cur->type = XML_NAMESPACE_DECL;
! 3341: if (ns->href != NULL)
! 3342: cur->href = xmlStrdup(ns->href);
! 3343: if (ns->prefix != NULL)
! 3344: cur->prefix = xmlStrdup(ns->prefix);
! 3345: cur->next = (xmlNsPtr) node;
! 3346: return((xmlNodePtr) cur);
! 3347: }
! 3348:
! 3349: /**
! 3350: * xmlXPathNodeSetFreeNs:
! 3351: * @ns: the XPath namespace node found in a nodeset.
! 3352: *
! 3353: * Namespace nodes in libxml don't match the XPath semantic. In a node set
! 3354: * the namespace nodes are duplicated and the next pointer is set to the
! 3355: * parent node in the XPath semantic. Check if such a node needs to be freed
! 3356: */
! 3357: void
! 3358: xmlXPathNodeSetFreeNs(xmlNsPtr ns) {
! 3359: if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
! 3360: return;
! 3361:
! 3362: if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) {
! 3363: if (ns->href != NULL)
! 3364: xmlFree((xmlChar *)ns->href);
! 3365: if (ns->prefix != NULL)
! 3366: xmlFree((xmlChar *)ns->prefix);
! 3367: xmlFree(ns);
! 3368: }
! 3369: }
! 3370:
! 3371: /**
! 3372: * xmlXPathNodeSetCreate:
! 3373: * @val: an initial xmlNodePtr, or NULL
! 3374: *
! 3375: * Create a new xmlNodeSetPtr of type double and of value @val
! 3376: *
! 3377: * Returns the newly created object.
! 3378: */
! 3379: xmlNodeSetPtr
! 3380: xmlXPathNodeSetCreate(xmlNodePtr val) {
! 3381: xmlNodeSetPtr ret;
! 3382:
! 3383: ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
! 3384: if (ret == NULL) {
! 3385: xmlXPathErrMemory(NULL, "creating nodeset\n");
! 3386: return(NULL);
! 3387: }
! 3388: memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
! 3389: if (val != NULL) {
! 3390: ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
! 3391: sizeof(xmlNodePtr));
! 3392: if (ret->nodeTab == NULL) {
! 3393: xmlXPathErrMemory(NULL, "creating nodeset\n");
! 3394: xmlFree(ret);
! 3395: return(NULL);
! 3396: }
! 3397: memset(ret->nodeTab, 0 ,
! 3398: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
! 3399: ret->nodeMax = XML_NODESET_DEFAULT;
! 3400: if (val->type == XML_NAMESPACE_DECL) {
! 3401: xmlNsPtr ns = (xmlNsPtr) val;
! 3402:
! 3403: ret->nodeTab[ret->nodeNr++] =
! 3404: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
! 3405: } else
! 3406: ret->nodeTab[ret->nodeNr++] = val;
! 3407: }
! 3408: return(ret);
! 3409: }
! 3410:
! 3411: /**
! 3412: * xmlXPathNodeSetCreateSize:
! 3413: * @size: the initial size of the set
! 3414: *
! 3415: * Create a new xmlNodeSetPtr of type double and of value @val
! 3416: *
! 3417: * Returns the newly created object.
! 3418: */
! 3419: static xmlNodeSetPtr
! 3420: xmlXPathNodeSetCreateSize(int size) {
! 3421: xmlNodeSetPtr ret;
! 3422:
! 3423: ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
! 3424: if (ret == NULL) {
! 3425: xmlXPathErrMemory(NULL, "creating nodeset\n");
! 3426: return(NULL);
! 3427: }
! 3428: memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
! 3429: if (size < XML_NODESET_DEFAULT)
! 3430: size = XML_NODESET_DEFAULT;
! 3431: ret->nodeTab = (xmlNodePtr *) xmlMalloc(size * sizeof(xmlNodePtr));
! 3432: if (ret->nodeTab == NULL) {
! 3433: xmlXPathErrMemory(NULL, "creating nodeset\n");
! 3434: xmlFree(ret);
! 3435: return(NULL);
! 3436: }
! 3437: memset(ret->nodeTab, 0 , size * (size_t) sizeof(xmlNodePtr));
! 3438: ret->nodeMax = size;
! 3439: return(ret);
! 3440: }
! 3441:
! 3442: /**
! 3443: * xmlXPathNodeSetContains:
! 3444: * @cur: the node-set
! 3445: * @val: the node
! 3446: *
! 3447: * checks whether @cur contains @val
! 3448: *
! 3449: * Returns true (1) if @cur contains @val, false (0) otherwise
! 3450: */
! 3451: int
! 3452: xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
! 3453: int i;
! 3454:
! 3455: if ((cur == NULL) || (val == NULL)) return(0);
! 3456: if (val->type == XML_NAMESPACE_DECL) {
! 3457: for (i = 0; i < cur->nodeNr; i++) {
! 3458: if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) {
! 3459: xmlNsPtr ns1, ns2;
! 3460:
! 3461: ns1 = (xmlNsPtr) val;
! 3462: ns2 = (xmlNsPtr) cur->nodeTab[i];
! 3463: if (ns1 == ns2)
! 3464: return(1);
! 3465: if ((ns1->next != NULL) && (ns2->next == ns1->next) &&
! 3466: (xmlStrEqual(ns1->prefix, ns2->prefix)))
! 3467: return(1);
! 3468: }
! 3469: }
! 3470: } else {
! 3471: for (i = 0; i < cur->nodeNr; i++) {
! 3472: if (cur->nodeTab[i] == val)
! 3473: return(1);
! 3474: }
! 3475: }
! 3476: return(0);
! 3477: }
! 3478:
! 3479: /**
! 3480: * xmlXPathNodeSetAddNs:
! 3481: * @cur: the initial node set
! 3482: * @node: the hosting node
! 3483: * @ns: a the namespace node
! 3484: *
! 3485: * add a new namespace node to an existing NodeSet
! 3486: */
! 3487: void
! 3488: xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) {
! 3489: int i;
! 3490:
! 3491:
! 3492: if ((cur == NULL) || (ns == NULL) || (node == NULL) ||
! 3493: (ns->type != XML_NAMESPACE_DECL) ||
! 3494: (node->type != XML_ELEMENT_NODE))
! 3495: return;
! 3496:
! 3497: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
! 3498: /*
! 3499: * prevent duplicates
! 3500: */
! 3501: for (i = 0;i < cur->nodeNr;i++) {
! 3502: if ((cur->nodeTab[i] != NULL) &&
! 3503: (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) &&
! 3504: (((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) &&
! 3505: (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix)))
! 3506: return;
! 3507: }
! 3508:
! 3509: /*
! 3510: * grow the nodeTab if needed
! 3511: */
! 3512: if (cur->nodeMax == 0) {
! 3513: cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
! 3514: sizeof(xmlNodePtr));
! 3515: if (cur->nodeTab == NULL) {
! 3516: xmlXPathErrMemory(NULL, "growing nodeset\n");
! 3517: return;
! 3518: }
! 3519: memset(cur->nodeTab, 0 ,
! 3520: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
! 3521: cur->nodeMax = XML_NODESET_DEFAULT;
! 3522: } else if (cur->nodeNr == cur->nodeMax) {
! 3523: xmlNodePtr *temp;
! 3524:
! 3525: cur->nodeMax *= 2;
! 3526: temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
! 3527: sizeof(xmlNodePtr));
! 3528: if (temp == NULL) {
! 3529: xmlXPathErrMemory(NULL, "growing nodeset\n");
! 3530: return;
! 3531: }
! 3532: cur->nodeTab = temp;
! 3533: }
! 3534: cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns);
! 3535: }
! 3536:
! 3537: /**
! 3538: * xmlXPathNodeSetAdd:
! 3539: * @cur: the initial node set
! 3540: * @val: a new xmlNodePtr
! 3541: *
! 3542: * add a new xmlNodePtr to an existing NodeSet
! 3543: */
! 3544: void
! 3545: xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
! 3546: int i;
! 3547:
! 3548: if ((cur == NULL) || (val == NULL)) return;
! 3549:
! 3550: #if 0
! 3551: if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' '))
! 3552: return; /* an XSLT fake node */
! 3553: #endif
! 3554:
! 3555: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
! 3556: /*
! 3557: * prevent duplcates
! 3558: */
! 3559: for (i = 0;i < cur->nodeNr;i++)
! 3560: if (cur->nodeTab[i] == val) return;
! 3561:
! 3562: /*
! 3563: * grow the nodeTab if needed
! 3564: */
! 3565: if (cur->nodeMax == 0) {
! 3566: cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
! 3567: sizeof(xmlNodePtr));
! 3568: if (cur->nodeTab == NULL) {
! 3569: xmlXPathErrMemory(NULL, "growing nodeset\n");
! 3570: return;
! 3571: }
! 3572: memset(cur->nodeTab, 0 ,
! 3573: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
! 3574: cur->nodeMax = XML_NODESET_DEFAULT;
! 3575: } else if (cur->nodeNr == cur->nodeMax) {
! 3576: xmlNodePtr *temp;
! 3577:
! 3578: cur->nodeMax *= 2;
! 3579: temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
! 3580: sizeof(xmlNodePtr));
! 3581: if (temp == NULL) {
! 3582: xmlXPathErrMemory(NULL, "growing nodeset\n");
! 3583: return;
! 3584: }
! 3585: cur->nodeTab = temp;
! 3586: }
! 3587: if (val->type == XML_NAMESPACE_DECL) {
! 3588: xmlNsPtr ns = (xmlNsPtr) val;
! 3589:
! 3590: cur->nodeTab[cur->nodeNr++] =
! 3591: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
! 3592: } else
! 3593: cur->nodeTab[cur->nodeNr++] = val;
! 3594: }
! 3595:
! 3596: /**
! 3597: * xmlXPathNodeSetAddUnique:
! 3598: * @cur: the initial node set
! 3599: * @val: a new xmlNodePtr
! 3600: *
! 3601: * add a new xmlNodePtr to an existing NodeSet, optimized version
! 3602: * when we are sure the node is not already in the set.
! 3603: */
! 3604: void
! 3605: xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
! 3606: if ((cur == NULL) || (val == NULL)) return;
! 3607:
! 3608: #if 0
! 3609: if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' '))
! 3610: return; /* an XSLT fake node */
! 3611: #endif
! 3612:
! 3613: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
! 3614: /*
! 3615: * grow the nodeTab if needed
! 3616: */
! 3617: if (cur->nodeMax == 0) {
! 3618: cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
! 3619: sizeof(xmlNodePtr));
! 3620: if (cur->nodeTab == NULL) {
! 3621: xmlXPathErrMemory(NULL, "growing nodeset\n");
! 3622: return;
! 3623: }
! 3624: memset(cur->nodeTab, 0 ,
! 3625: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
! 3626: cur->nodeMax = XML_NODESET_DEFAULT;
! 3627: } else if (cur->nodeNr == cur->nodeMax) {
! 3628: xmlNodePtr *temp;
! 3629:
! 3630: cur->nodeMax *= 2;
! 3631: temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
! 3632: sizeof(xmlNodePtr));
! 3633: if (temp == NULL) {
! 3634: xmlXPathErrMemory(NULL, "growing nodeset\n");
! 3635: return;
! 3636: }
! 3637: cur->nodeTab = temp;
! 3638: }
! 3639: if (val->type == XML_NAMESPACE_DECL) {
! 3640: xmlNsPtr ns = (xmlNsPtr) val;
! 3641:
! 3642: cur->nodeTab[cur->nodeNr++] =
! 3643: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
! 3644: } else
! 3645: cur->nodeTab[cur->nodeNr++] = val;
! 3646: }
! 3647:
! 3648: /**
! 3649: * xmlXPathNodeSetMerge:
! 3650: * @val1: the first NodeSet or NULL
! 3651: * @val2: the second NodeSet
! 3652: *
! 3653: * Merges two nodesets, all nodes from @val2 are added to @val1
! 3654: * if @val1 is NULL, a new set is created and copied from @val2
! 3655: *
! 3656: * Returns @val1 once extended or NULL in case of error.
! 3657: */
! 3658: xmlNodeSetPtr
! 3659: xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
! 3660: int i, j, initNr, skip;
! 3661: xmlNodePtr n1, n2;
! 3662:
! 3663: if (val2 == NULL) return(val1);
! 3664: if (val1 == NULL) {
! 3665: val1 = xmlXPathNodeSetCreate(NULL);
! 3666: if (val1 == NULL)
! 3667: return (NULL);
! 3668: #if 0
! 3669: /*
! 3670: * TODO: The optimization won't work in every case, since
! 3671: * those nasty namespace nodes need to be added with
! 3672: * xmlXPathNodeSetDupNs() to the set; thus a pure
! 3673: * memcpy is not possible.
! 3674: * If there was a flag on the nodesetval, indicating that
! 3675: * some temporary nodes are in, that would be helpfull.
! 3676: */
! 3677: /*
! 3678: * Optimization: Create an equally sized node-set
! 3679: * and memcpy the content.
! 3680: */
! 3681: val1 = xmlXPathNodeSetCreateSize(val2->nodeNr);
! 3682: if (val1 == NULL)
! 3683: return(NULL);
! 3684: if (val2->nodeNr != 0) {
! 3685: if (val2->nodeNr == 1)
! 3686: *(val1->nodeTab) = *(val2->nodeTab);
! 3687: else {
! 3688: memcpy(val1->nodeTab, val2->nodeTab,
! 3689: val2->nodeNr * sizeof(xmlNodePtr));
! 3690: }
! 3691: val1->nodeNr = val2->nodeNr;
! 3692: }
! 3693: return(val1);
! 3694: #endif
! 3695: }
! 3696:
! 3697: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
! 3698: initNr = val1->nodeNr;
! 3699:
! 3700: for (i = 0;i < val2->nodeNr;i++) {
! 3701: n2 = val2->nodeTab[i];
! 3702: /*
! 3703: * check against duplicates
! 3704: */
! 3705: skip = 0;
! 3706: for (j = 0; j < initNr; j++) {
! 3707: n1 = val1->nodeTab[j];
! 3708: if (n1 == n2) {
! 3709: skip = 1;
! 3710: break;
! 3711: } else if ((n1->type == XML_NAMESPACE_DECL) &&
! 3712: (n2->type == XML_NAMESPACE_DECL)) {
! 3713: if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
! 3714: (xmlStrEqual(((xmlNsPtr) n1)->prefix,
! 3715: ((xmlNsPtr) n2)->prefix)))
! 3716: {
! 3717: skip = 1;
! 3718: break;
! 3719: }
! 3720: }
! 3721: }
! 3722: if (skip)
! 3723: continue;
! 3724:
! 3725: /*
! 3726: * grow the nodeTab if needed
! 3727: */
! 3728: if (val1->nodeMax == 0) {
! 3729: val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
! 3730: sizeof(xmlNodePtr));
! 3731: if (val1->nodeTab == NULL) {
! 3732: xmlXPathErrMemory(NULL, "merging nodeset\n");
! 3733: return(NULL);
! 3734: }
! 3735: memset(val1->nodeTab, 0 ,
! 3736: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
! 3737: val1->nodeMax = XML_NODESET_DEFAULT;
! 3738: } else if (val1->nodeNr == val1->nodeMax) {
! 3739: xmlNodePtr *temp;
! 3740:
! 3741: val1->nodeMax *= 2;
! 3742: temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
! 3743: sizeof(xmlNodePtr));
! 3744: if (temp == NULL) {
! 3745: xmlXPathErrMemory(NULL, "merging nodeset\n");
! 3746: return(NULL);
! 3747: }
! 3748: val1->nodeTab = temp;
! 3749: }
! 3750: if (n2->type == XML_NAMESPACE_DECL) {
! 3751: xmlNsPtr ns = (xmlNsPtr) n2;
! 3752:
! 3753: val1->nodeTab[val1->nodeNr++] =
! 3754: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
! 3755: } else
! 3756: val1->nodeTab[val1->nodeNr++] = n2;
! 3757: }
! 3758:
! 3759: return(val1);
! 3760: }
! 3761:
! 3762: #if 0 /* xmlXPathNodeSetMergeUnique() is currently not used anymore */
! 3763: /**
! 3764: * xmlXPathNodeSetMergeUnique:
! 3765: * @val1: the first NodeSet or NULL
! 3766: * @val2: the second NodeSet
! 3767: *
! 3768: * Merges two nodesets, all nodes from @val2 are added to @val1
! 3769: * if @val1 is NULL, a new set is created and copied from @val2
! 3770: *
! 3771: * Returns @val1 once extended or NULL in case of error.
! 3772: */
! 3773: static xmlNodeSetPtr
! 3774: xmlXPathNodeSetMergeUnique(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
! 3775: int i;
! 3776:
! 3777: if (val2 == NULL) return(val1);
! 3778: if (val1 == NULL) {
! 3779: val1 = xmlXPathNodeSetCreate(NULL);
! 3780: }
! 3781: if (val1 == NULL)
! 3782: return (NULL);
! 3783:
! 3784: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
! 3785:
! 3786: for (i = 0;i < val2->nodeNr;i++) {
! 3787: /*
! 3788: * grow the nodeTab if needed
! 3789: */
! 3790: if (val1->nodeMax == 0) {
! 3791: val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
! 3792: sizeof(xmlNodePtr));
! 3793: if (val1->nodeTab == NULL) {
! 3794: xmlXPathErrMemory(NULL, "merging nodeset\n");
! 3795: return(NULL);
! 3796: }
! 3797: memset(val1->nodeTab, 0 ,
! 3798: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
! 3799: val1->nodeMax = XML_NODESET_DEFAULT;
! 3800: } else if (val1->nodeNr == val1->nodeMax) {
! 3801: xmlNodePtr *temp;
! 3802:
! 3803: val1->nodeMax *= 2;
! 3804: temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
! 3805: sizeof(xmlNodePtr));
! 3806: if (temp == NULL) {
! 3807: xmlXPathErrMemory(NULL, "merging nodeset\n");
! 3808: return(NULL);
! 3809: }
! 3810: val1->nodeTab = temp;
! 3811: }
! 3812: if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) {
! 3813: xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i];
! 3814:
! 3815: val1->nodeTab[val1->nodeNr++] =
! 3816: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
! 3817: } else
! 3818: val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
! 3819: }
! 3820:
! 3821: return(val1);
! 3822: }
! 3823: #endif /* xmlXPathNodeSetMergeUnique() is currently not used anymore */
! 3824:
! 3825: /**
! 3826: * xmlXPathNodeSetMergeAndClear:
! 3827: * @set1: the first NodeSet or NULL
! 3828: * @set2: the second NodeSet
! 3829: * @hasSet2NsNodes: 1 if set2 contains namespaces nodes
! 3830: *
! 3831: * Merges two nodesets, all nodes from @set2 are added to @set1
! 3832: * if @set1 is NULL, a new set is created and copied from @set2.
! 3833: * Checks for duplicate nodes. Clears set2.
! 3834: *
! 3835: * Returns @set1 once extended or NULL in case of error.
! 3836: */
! 3837: static xmlNodeSetPtr
! 3838: xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2,
! 3839: int hasNullEntries)
! 3840: {
! 3841: if ((set1 == NULL) && (hasNullEntries == 0)) {
! 3842: /*
! 3843: * Note that doing a memcpy of the list, namespace nodes are
! 3844: * just assigned to set1, since set2 is cleared anyway.
! 3845: */
! 3846: set1 = xmlXPathNodeSetCreateSize(set2->nodeNr);
! 3847: if (set1 == NULL)
! 3848: return(NULL);
! 3849: if (set2->nodeNr != 0) {
! 3850: memcpy(set1->nodeTab, set2->nodeTab,
! 3851: set2->nodeNr * sizeof(xmlNodePtr));
! 3852: set1->nodeNr = set2->nodeNr;
! 3853: }
! 3854: } else {
! 3855: int i, j, initNbSet1;
! 3856: xmlNodePtr n1, n2;
! 3857:
! 3858: if (set1 == NULL)
! 3859: set1 = xmlXPathNodeSetCreate(NULL);
! 3860: if (set1 == NULL)
! 3861: return (NULL);
! 3862:
! 3863: initNbSet1 = set1->nodeNr;
! 3864: for (i = 0;i < set2->nodeNr;i++) {
! 3865: n2 = set2->nodeTab[i];
! 3866: /*
! 3867: * Skip NULLed entries.
! 3868: */
! 3869: if (n2 == NULL)
! 3870: continue;
! 3871: /*
! 3872: * Skip duplicates.
! 3873: */
! 3874: for (j = 0; j < initNbSet1; j++) {
! 3875: n1 = set1->nodeTab[j];
! 3876: if (n1 == n2) {
! 3877: goto skip_node;
! 3878: } else if ((n1->type == XML_NAMESPACE_DECL) &&
! 3879: (n2->type == XML_NAMESPACE_DECL))
! 3880: {
! 3881: if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
! 3882: (xmlStrEqual(((xmlNsPtr) n1)->prefix,
! 3883: ((xmlNsPtr) n2)->prefix)))
! 3884: {
! 3885: /*
! 3886: * Free the namespace node.
! 3887: */
! 3888: set2->nodeTab[i] = NULL;
! 3889: xmlXPathNodeSetFreeNs((xmlNsPtr) n2);
! 3890: goto skip_node;
! 3891: }
! 3892: }
! 3893: }
! 3894: /*
! 3895: * grow the nodeTab if needed
! 3896: */
! 3897: if (set1->nodeMax == 0) {
! 3898: set1->nodeTab = (xmlNodePtr *) xmlMalloc(
! 3899: XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
! 3900: if (set1->nodeTab == NULL) {
! 3901: xmlXPathErrMemory(NULL, "merging nodeset\n");
! 3902: return(NULL);
! 3903: }
! 3904: memset(set1->nodeTab, 0,
! 3905: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
! 3906: set1->nodeMax = XML_NODESET_DEFAULT;
! 3907: } else if (set1->nodeNr >= set1->nodeMax) {
! 3908: xmlNodePtr *temp;
! 3909:
! 3910: set1->nodeMax *= 2;
! 3911: temp = (xmlNodePtr *) xmlRealloc(
! 3912: set1->nodeTab, set1->nodeMax * sizeof(xmlNodePtr));
! 3913: if (temp == NULL) {
! 3914: xmlXPathErrMemory(NULL, "merging nodeset\n");
! 3915: return(NULL);
! 3916: }
! 3917: set1->nodeTab = temp;
! 3918: }
! 3919: if (n2->type == XML_NAMESPACE_DECL) {
! 3920: xmlNsPtr ns = (xmlNsPtr) n2;
! 3921:
! 3922: set1->nodeTab[set1->nodeNr++] =
! 3923: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
! 3924: } else
! 3925: set1->nodeTab[set1->nodeNr++] = n2;
! 3926: skip_node:
! 3927: {}
! 3928: }
! 3929: }
! 3930: set2->nodeNr = 0;
! 3931: return(set1);
! 3932: }
! 3933:
! 3934: /**
! 3935: * xmlXPathNodeSetMergeAndClearNoDupls:
! 3936: * @set1: the first NodeSet or NULL
! 3937: * @set2: the second NodeSet
! 3938: * @hasSet2NsNodes: 1 if set2 contains namespaces nodes
! 3939: *
! 3940: * Merges two nodesets, all nodes from @set2 are added to @set1
! 3941: * if @set1 is NULL, a new set is created and copied from @set2.
! 3942: * Doesn't chack for duplicate nodes. Clears set2.
! 3943: *
! 3944: * Returns @set1 once extended or NULL in case of error.
! 3945: */
! 3946: static xmlNodeSetPtr
! 3947: xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2,
! 3948: int hasNullEntries)
! 3949: {
! 3950: if (set2 == NULL)
! 3951: return(set1);
! 3952: if ((set1 == NULL) && (hasNullEntries == 0)) {
! 3953: /*
! 3954: * Note that doing a memcpy of the list, namespace nodes are
! 3955: * just assigned to set1, since set2 is cleared anyway.
! 3956: */
! 3957: set1 = xmlXPathNodeSetCreateSize(set2->nodeNr);
! 3958: if (set1 == NULL)
! 3959: return(NULL);
! 3960: if (set2->nodeNr != 0) {
! 3961: memcpy(set1->nodeTab, set2->nodeTab,
! 3962: set2->nodeNr * sizeof(xmlNodePtr));
! 3963: set1->nodeNr = set2->nodeNr;
! 3964: }
! 3965: } else {
! 3966: int i;
! 3967: xmlNodePtr n2;
! 3968:
! 3969: if (set1 == NULL)
! 3970: set1 = xmlXPathNodeSetCreate(NULL);
! 3971: if (set1 == NULL)
! 3972: return (NULL);
! 3973:
! 3974: for (i = 0;i < set2->nodeNr;i++) {
! 3975: n2 = set2->nodeTab[i];
! 3976: /*
! 3977: * Skip NULLed entries.
! 3978: */
! 3979: if (n2 == NULL)
! 3980: continue;
! 3981: if (set1->nodeMax == 0) {
! 3982: set1->nodeTab = (xmlNodePtr *) xmlMalloc(
! 3983: XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
! 3984: if (set1->nodeTab == NULL) {
! 3985: xmlXPathErrMemory(NULL, "merging nodeset\n");
! 3986: return(NULL);
! 3987: }
! 3988: memset(set1->nodeTab, 0,
! 3989: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
! 3990: set1->nodeMax = XML_NODESET_DEFAULT;
! 3991: } else if (set1->nodeNr >= set1->nodeMax) {
! 3992: xmlNodePtr *temp;
! 3993:
! 3994: set1->nodeMax *= 2;
! 3995: temp = (xmlNodePtr *) xmlRealloc(
! 3996: set1->nodeTab, set1->nodeMax * sizeof(xmlNodePtr));
! 3997: if (temp == NULL) {
! 3998: xmlXPathErrMemory(NULL, "merging nodeset\n");
! 3999: return(NULL);
! 4000: }
! 4001: set1->nodeTab = temp;
! 4002: }
! 4003: set1->nodeTab[set1->nodeNr++] = n2;
! 4004: }
! 4005: }
! 4006: set2->nodeNr = 0;
! 4007: return(set1);
! 4008: }
! 4009:
! 4010: /**
! 4011: * xmlXPathNodeSetDel:
! 4012: * @cur: the initial node set
! 4013: * @val: an xmlNodePtr
! 4014: *
! 4015: * Removes an xmlNodePtr from an existing NodeSet
! 4016: */
! 4017: void
! 4018: xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
! 4019: int i;
! 4020:
! 4021: if (cur == NULL) return;
! 4022: if (val == NULL) return;
! 4023:
! 4024: /*
! 4025: * find node in nodeTab
! 4026: */
! 4027: for (i = 0;i < cur->nodeNr;i++)
! 4028: if (cur->nodeTab[i] == val) break;
! 4029:
! 4030: if (i >= cur->nodeNr) { /* not found */
! 4031: #ifdef DEBUG
! 4032: xmlGenericError(xmlGenericErrorContext,
! 4033: "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
! 4034: val->name);
! 4035: #endif
! 4036: return;
! 4037: }
! 4038: if ((cur->nodeTab[i] != NULL) &&
! 4039: (cur->nodeTab[i]->type == XML_NAMESPACE_DECL))
! 4040: xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]);
! 4041: cur->nodeNr--;
! 4042: for (;i < cur->nodeNr;i++)
! 4043: cur->nodeTab[i] = cur->nodeTab[i + 1];
! 4044: cur->nodeTab[cur->nodeNr] = NULL;
! 4045: }
! 4046:
! 4047: /**
! 4048: * xmlXPathNodeSetRemove:
! 4049: * @cur: the initial node set
! 4050: * @val: the index to remove
! 4051: *
! 4052: * Removes an entry from an existing NodeSet list.
! 4053: */
! 4054: void
! 4055: xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
! 4056: if (cur == NULL) return;
! 4057: if (val >= cur->nodeNr) return;
! 4058: if ((cur->nodeTab[val] != NULL) &&
! 4059: (cur->nodeTab[val]->type == XML_NAMESPACE_DECL))
! 4060: xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]);
! 4061: cur->nodeNr--;
! 4062: for (;val < cur->nodeNr;val++)
! 4063: cur->nodeTab[val] = cur->nodeTab[val + 1];
! 4064: cur->nodeTab[cur->nodeNr] = NULL;
! 4065: }
! 4066:
! 4067: /**
! 4068: * xmlXPathFreeNodeSet:
! 4069: * @obj: the xmlNodeSetPtr to free
! 4070: *
! 4071: * Free the NodeSet compound (not the actual nodes !).
! 4072: */
! 4073: void
! 4074: xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
! 4075: if (obj == NULL) return;
! 4076: if (obj->nodeTab != NULL) {
! 4077: int i;
! 4078:
! 4079: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
! 4080: for (i = 0;i < obj->nodeNr;i++)
! 4081: if ((obj->nodeTab[i] != NULL) &&
! 4082: (obj->nodeTab[i]->type == XML_NAMESPACE_DECL))
! 4083: xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
! 4084: xmlFree(obj->nodeTab);
! 4085: }
! 4086: xmlFree(obj);
! 4087: }
! 4088:
! 4089: /**
! 4090: * xmlXPathNodeSetClear:
! 4091: * @set: the node set to clear
! 4092: *
! 4093: * Clears the list from all temporary XPath objects (e.g. namespace nodes
! 4094: * are feed), but does *not* free the list itself. Sets the length of the
! 4095: * list to 0.
! 4096: */
! 4097: static void
! 4098: xmlXPathNodeSetClear(xmlNodeSetPtr set, int hasNsNodes)
! 4099: {
! 4100: if ((set == NULL) || (set->nodeNr <= 0))
! 4101: return;
! 4102: else if (hasNsNodes) {
! 4103: int i;
! 4104: xmlNodePtr node;
! 4105:
! 4106: for (i = 0; i < set->nodeNr; i++) {
! 4107: node = set->nodeTab[i];
! 4108: if ((node != NULL) &&
! 4109: (node->type == XML_NAMESPACE_DECL))
! 4110: xmlXPathNodeSetFreeNs((xmlNsPtr) node);
! 4111: }
! 4112: }
! 4113: set->nodeNr = 0;
! 4114: }
! 4115:
! 4116: /**
! 4117: * xmlXPathNodeSetClearFromPos:
! 4118: * @set: the node set to be cleared
! 4119: * @pos: the start position to clear from
! 4120: *
! 4121: * Clears the list from temporary XPath objects (e.g. namespace nodes
! 4122: * are feed) starting with the entry at @pos, but does *not* free the list
! 4123: * itself. Sets the length of the list to @pos.
! 4124: */
! 4125: static void
! 4126: xmlXPathNodeSetClearFromPos(xmlNodeSetPtr set, int pos, int hasNsNodes)
! 4127: {
! 4128: if ((set == NULL) || (set->nodeNr <= 0) || (pos >= set->nodeNr))
! 4129: return;
! 4130: else if ((hasNsNodes)) {
! 4131: int i;
! 4132: xmlNodePtr node;
! 4133:
! 4134: for (i = pos; i < set->nodeNr; i++) {
! 4135: node = set->nodeTab[i];
! 4136: if ((node != NULL) &&
! 4137: (node->type == XML_NAMESPACE_DECL))
! 4138: xmlXPathNodeSetFreeNs((xmlNsPtr) node);
! 4139: }
! 4140: }
! 4141: set->nodeNr = pos;
! 4142: }
! 4143:
! 4144: /**
! 4145: * xmlXPathFreeValueTree:
! 4146: * @obj: the xmlNodeSetPtr to free
! 4147: *
! 4148: * Free the NodeSet compound and the actual tree, this is different
! 4149: * from xmlXPathFreeNodeSet()
! 4150: */
! 4151: static void
! 4152: xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
! 4153: int i;
! 4154:
! 4155: if (obj == NULL) return;
! 4156:
! 4157: if (obj->nodeTab != NULL) {
! 4158: for (i = 0;i < obj->nodeNr;i++) {
! 4159: if (obj->nodeTab[i] != NULL) {
! 4160: if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) {
! 4161: xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
! 4162: } else {
! 4163: xmlFreeNodeList(obj->nodeTab[i]);
! 4164: }
! 4165: }
! 4166: }
! 4167: xmlFree(obj->nodeTab);
! 4168: }
! 4169: xmlFree(obj);
! 4170: }
! 4171:
! 4172: #if defined(DEBUG) || defined(DEBUG_STEP)
! 4173: /**
! 4174: * xmlGenericErrorContextNodeSet:
! 4175: * @output: a FILE * for the output
! 4176: * @obj: the xmlNodeSetPtr to display
! 4177: *
! 4178: * Quick display of a NodeSet
! 4179: */
! 4180: void
! 4181: xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
! 4182: int i;
! 4183:
! 4184: if (output == NULL) output = xmlGenericErrorContext;
! 4185: if (obj == NULL) {
! 4186: fprintf(output, "NodeSet == NULL !\n");
! 4187: return;
! 4188: }
! 4189: if (obj->nodeNr == 0) {
! 4190: fprintf(output, "NodeSet is empty\n");
! 4191: return;
! 4192: }
! 4193: if (obj->nodeTab == NULL) {
! 4194: fprintf(output, " nodeTab == NULL !\n");
! 4195: return;
! 4196: }
! 4197: for (i = 0; i < obj->nodeNr; i++) {
! 4198: if (obj->nodeTab[i] == NULL) {
! 4199: fprintf(output, " NULL !\n");
! 4200: return;
! 4201: }
! 4202: if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
! 4203: (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
! 4204: fprintf(output, " /");
! 4205: else if (obj->nodeTab[i]->name == NULL)
! 4206: fprintf(output, " noname!");
! 4207: else fprintf(output, " %s", obj->nodeTab[i]->name);
! 4208: }
! 4209: fprintf(output, "\n");
! 4210: }
! 4211: #endif
! 4212:
! 4213: /**
! 4214: * xmlXPathNewNodeSet:
! 4215: * @val: the NodePtr value
! 4216: *
! 4217: * Create a new xmlXPathObjectPtr of type NodeSet and initialize
! 4218: * it with the single Node @val
! 4219: *
! 4220: * Returns the newly created object.
! 4221: */
! 4222: xmlXPathObjectPtr
! 4223: xmlXPathNewNodeSet(xmlNodePtr val) {
! 4224: xmlXPathObjectPtr ret;
! 4225:
! 4226: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
! 4227: if (ret == NULL) {
! 4228: xmlXPathErrMemory(NULL, "creating nodeset\n");
! 4229: return(NULL);
! 4230: }
! 4231: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
! 4232: ret->type = XPATH_NODESET;
! 4233: ret->boolval = 0;
! 4234: ret->nodesetval = xmlXPathNodeSetCreate(val);
! 4235: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
! 4236: #ifdef XP_DEBUG_OBJ_USAGE
! 4237: xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
! 4238: #endif
! 4239: return(ret);
! 4240: }
! 4241:
! 4242: /**
! 4243: * xmlXPathNewValueTree:
! 4244: * @val: the NodePtr value
! 4245: *
! 4246: * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
! 4247: * it with the tree root @val
! 4248: *
! 4249: * Returns the newly created object.
! 4250: */
! 4251: xmlXPathObjectPtr
! 4252: xmlXPathNewValueTree(xmlNodePtr val) {
! 4253: xmlXPathObjectPtr ret;
! 4254:
! 4255: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
! 4256: if (ret == NULL) {
! 4257: xmlXPathErrMemory(NULL, "creating result value tree\n");
! 4258: return(NULL);
! 4259: }
! 4260: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
! 4261: ret->type = XPATH_XSLT_TREE;
! 4262: ret->boolval = 1;
! 4263: ret->user = (void *) val;
! 4264: ret->nodesetval = xmlXPathNodeSetCreate(val);
! 4265: #ifdef XP_DEBUG_OBJ_USAGE
! 4266: xmlXPathDebugObjUsageRequested(NULL, XPATH_XSLT_TREE);
! 4267: #endif
! 4268: return(ret);
! 4269: }
! 4270:
! 4271: /**
! 4272: * xmlXPathNewNodeSetList:
! 4273: * @val: an existing NodeSet
! 4274: *
! 4275: * Create a new xmlXPathObjectPtr of type NodeSet and initialize
! 4276: * it with the Nodeset @val
! 4277: *
! 4278: * Returns the newly created object.
! 4279: */
! 4280: xmlXPathObjectPtr
! 4281: xmlXPathNewNodeSetList(xmlNodeSetPtr val)
! 4282: {
! 4283: xmlXPathObjectPtr ret;
! 4284: int i;
! 4285:
! 4286: if (val == NULL)
! 4287: ret = NULL;
! 4288: else if (val->nodeTab == NULL)
! 4289: ret = xmlXPathNewNodeSet(NULL);
! 4290: else {
! 4291: ret = xmlXPathNewNodeSet(val->nodeTab[0]);
! 4292: if (ret)
! 4293: for (i = 1; i < val->nodeNr; ++i)
! 4294: xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
! 4295: }
! 4296:
! 4297: return (ret);
! 4298: }
! 4299:
! 4300: /**
! 4301: * xmlXPathWrapNodeSet:
! 4302: * @val: the NodePtr value
! 4303: *
! 4304: * Wrap the Nodeset @val in a new xmlXPathObjectPtr
! 4305: *
! 4306: * Returns the newly created object.
! 4307: */
! 4308: xmlXPathObjectPtr
! 4309: xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
! 4310: xmlXPathObjectPtr ret;
! 4311:
! 4312: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
! 4313: if (ret == NULL) {
! 4314: xmlXPathErrMemory(NULL, "creating node set object\n");
! 4315: return(NULL);
! 4316: }
! 4317: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
! 4318: ret->type = XPATH_NODESET;
! 4319: ret->nodesetval = val;
! 4320: #ifdef XP_DEBUG_OBJ_USAGE
! 4321: xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
! 4322: #endif
! 4323: return(ret);
! 4324: }
! 4325:
! 4326: /**
! 4327: * xmlXPathFreeNodeSetList:
! 4328: * @obj: an existing NodeSetList object
! 4329: *
! 4330: * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
! 4331: * the list contrary to xmlXPathFreeObject().
! 4332: */
! 4333: void
! 4334: xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
! 4335: if (obj == NULL) return;
! 4336: #ifdef XP_DEBUG_OBJ_USAGE
! 4337: xmlXPathDebugObjUsageReleased(NULL, obj->type);
! 4338: #endif
! 4339: xmlFree(obj);
! 4340: }
! 4341:
! 4342: /**
! 4343: * xmlXPathDifference:
! 4344: * @nodes1: a node-set
! 4345: * @nodes2: a node-set
! 4346: *
! 4347: * Implements the EXSLT - Sets difference() function:
! 4348: * node-set set:difference (node-set, node-set)
! 4349: *
! 4350: * Returns the difference between the two node sets, or nodes1 if
! 4351: * nodes2 is empty
! 4352: */
! 4353: xmlNodeSetPtr
! 4354: xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
! 4355: xmlNodeSetPtr ret;
! 4356: int i, l1;
! 4357: xmlNodePtr cur;
! 4358:
! 4359: if (xmlXPathNodeSetIsEmpty(nodes2))
! 4360: return(nodes1);
! 4361:
! 4362: ret = xmlXPathNodeSetCreate(NULL);
! 4363: if (xmlXPathNodeSetIsEmpty(nodes1))
! 4364: return(ret);
! 4365:
! 4366: l1 = xmlXPathNodeSetGetLength(nodes1);
! 4367:
! 4368: for (i = 0; i < l1; i++) {
! 4369: cur = xmlXPathNodeSetItem(nodes1, i);
! 4370: if (!xmlXPathNodeSetContains(nodes2, cur))
! 4371: xmlXPathNodeSetAddUnique(ret, cur);
! 4372: }
! 4373: return(ret);
! 4374: }
! 4375:
! 4376: /**
! 4377: * xmlXPathIntersection:
! 4378: * @nodes1: a node-set
! 4379: * @nodes2: a node-set
! 4380: *
! 4381: * Implements the EXSLT - Sets intersection() function:
! 4382: * node-set set:intersection (node-set, node-set)
! 4383: *
! 4384: * Returns a node set comprising the nodes that are within both the
! 4385: * node sets passed as arguments
! 4386: */
! 4387: xmlNodeSetPtr
! 4388: xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
! 4389: xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
! 4390: int i, l1;
! 4391: xmlNodePtr cur;
! 4392:
! 4393: if (ret == NULL)
! 4394: return(ret);
! 4395: if (xmlXPathNodeSetIsEmpty(nodes1))
! 4396: return(ret);
! 4397: if (xmlXPathNodeSetIsEmpty(nodes2))
! 4398: return(ret);
! 4399:
! 4400: l1 = xmlXPathNodeSetGetLength(nodes1);
! 4401:
! 4402: for (i = 0; i < l1; i++) {
! 4403: cur = xmlXPathNodeSetItem(nodes1, i);
! 4404: if (xmlXPathNodeSetContains(nodes2, cur))
! 4405: xmlXPathNodeSetAddUnique(ret, cur);
! 4406: }
! 4407: return(ret);
! 4408: }
! 4409:
! 4410: /**
! 4411: * xmlXPathDistinctSorted:
! 4412: * @nodes: a node-set, sorted by document order
! 4413: *
! 4414: * Implements the EXSLT - Sets distinct() function:
! 4415: * node-set set:distinct (node-set)
! 4416: *
! 4417: * Returns a subset of the nodes contained in @nodes, or @nodes if
! 4418: * it is empty
! 4419: */
! 4420: xmlNodeSetPtr
! 4421: xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
! 4422: xmlNodeSetPtr ret;
! 4423: xmlHashTablePtr hash;
! 4424: int i, l;
! 4425: xmlChar * strval;
! 4426: xmlNodePtr cur;
! 4427:
! 4428: if (xmlXPathNodeSetIsEmpty(nodes))
! 4429: return(nodes);
! 4430:
! 4431: ret = xmlXPathNodeSetCreate(NULL);
! 4432: if (ret == NULL)
! 4433: return(ret);
! 4434: l = xmlXPathNodeSetGetLength(nodes);
! 4435: hash = xmlHashCreate (l);
! 4436: for (i = 0; i < l; i++) {
! 4437: cur = xmlXPathNodeSetItem(nodes, i);
! 4438: strval = xmlXPathCastNodeToString(cur);
! 4439: if (xmlHashLookup(hash, strval) == NULL) {
! 4440: xmlHashAddEntry(hash, strval, strval);
! 4441: xmlXPathNodeSetAddUnique(ret, cur);
! 4442: } else {
! 4443: xmlFree(strval);
! 4444: }
! 4445: }
! 4446: xmlHashFree(hash, (xmlHashDeallocator) xmlFree);
! 4447: return(ret);
! 4448: }
! 4449:
! 4450: /**
! 4451: * xmlXPathDistinct:
! 4452: * @nodes: a node-set
! 4453: *
! 4454: * Implements the EXSLT - Sets distinct() function:
! 4455: * node-set set:distinct (node-set)
! 4456: * @nodes is sorted by document order, then #exslSetsDistinctSorted
! 4457: * is called with the sorted node-set
! 4458: *
! 4459: * Returns a subset of the nodes contained in @nodes, or @nodes if
! 4460: * it is empty
! 4461: */
! 4462: xmlNodeSetPtr
! 4463: xmlXPathDistinct (xmlNodeSetPtr nodes) {
! 4464: if (xmlXPathNodeSetIsEmpty(nodes))
! 4465: return(nodes);
! 4466:
! 4467: xmlXPathNodeSetSort(nodes);
! 4468: return(xmlXPathDistinctSorted(nodes));
! 4469: }
! 4470:
! 4471: /**
! 4472: * xmlXPathHasSameNodes:
! 4473: * @nodes1: a node-set
! 4474: * @nodes2: a node-set
! 4475: *
! 4476: * Implements the EXSLT - Sets has-same-nodes function:
! 4477: * boolean set:has-same-node(node-set, node-set)
! 4478: *
! 4479: * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
! 4480: * otherwise
! 4481: */
! 4482: int
! 4483: xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
! 4484: int i, l;
! 4485: xmlNodePtr cur;
! 4486:
! 4487: if (xmlXPathNodeSetIsEmpty(nodes1) ||
! 4488: xmlXPathNodeSetIsEmpty(nodes2))
! 4489: return(0);
! 4490:
! 4491: l = xmlXPathNodeSetGetLength(nodes1);
! 4492: for (i = 0; i < l; i++) {
! 4493: cur = xmlXPathNodeSetItem(nodes1, i);
! 4494: if (xmlXPathNodeSetContains(nodes2, cur))
! 4495: return(1);
! 4496: }
! 4497: return(0);
! 4498: }
! 4499:
! 4500: /**
! 4501: * xmlXPathNodeLeadingSorted:
! 4502: * @nodes: a node-set, sorted by document order
! 4503: * @node: a node
! 4504: *
! 4505: * Implements the EXSLT - Sets leading() function:
! 4506: * node-set set:leading (node-set, node-set)
! 4507: *
! 4508: * Returns the nodes in @nodes that precede @node in document order,
! 4509: * @nodes if @node is NULL or an empty node-set if @nodes
! 4510: * doesn't contain @node
! 4511: */
! 4512: xmlNodeSetPtr
! 4513: xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
! 4514: int i, l;
! 4515: xmlNodePtr cur;
! 4516: xmlNodeSetPtr ret;
! 4517:
! 4518: if (node == NULL)
! 4519: return(nodes);
! 4520:
! 4521: ret = xmlXPathNodeSetCreate(NULL);
! 4522: if (ret == NULL)
! 4523: return(ret);
! 4524: if (xmlXPathNodeSetIsEmpty(nodes) ||
! 4525: (!xmlXPathNodeSetContains(nodes, node)))
! 4526: return(ret);
! 4527:
! 4528: l = xmlXPathNodeSetGetLength(nodes);
! 4529: for (i = 0; i < l; i++) {
! 4530: cur = xmlXPathNodeSetItem(nodes, i);
! 4531: if (cur == node)
! 4532: break;
! 4533: xmlXPathNodeSetAddUnique(ret, cur);
! 4534: }
! 4535: return(ret);
! 4536: }
! 4537:
! 4538: /**
! 4539: * xmlXPathNodeLeading:
! 4540: * @nodes: a node-set
! 4541: * @node: a node
! 4542: *
! 4543: * Implements the EXSLT - Sets leading() function:
! 4544: * node-set set:leading (node-set, node-set)
! 4545: * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
! 4546: * is called.
! 4547: *
! 4548: * Returns the nodes in @nodes that precede @node in document order,
! 4549: * @nodes if @node is NULL or an empty node-set if @nodes
! 4550: * doesn't contain @node
! 4551: */
! 4552: xmlNodeSetPtr
! 4553: xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
! 4554: xmlXPathNodeSetSort(nodes);
! 4555: return(xmlXPathNodeLeadingSorted(nodes, node));
! 4556: }
! 4557:
! 4558: /**
! 4559: * xmlXPathLeadingSorted:
! 4560: * @nodes1: a node-set, sorted by document order
! 4561: * @nodes2: a node-set, sorted by document order
! 4562: *
! 4563: * Implements the EXSLT - Sets leading() function:
! 4564: * node-set set:leading (node-set, node-set)
! 4565: *
! 4566: * Returns the nodes in @nodes1 that precede the first node in @nodes2
! 4567: * in document order, @nodes1 if @nodes2 is NULL or empty or
! 4568: * an empty node-set if @nodes1 doesn't contain @nodes2
! 4569: */
! 4570: xmlNodeSetPtr
! 4571: xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
! 4572: if (xmlXPathNodeSetIsEmpty(nodes2))
! 4573: return(nodes1);
! 4574: return(xmlXPathNodeLeadingSorted(nodes1,
! 4575: xmlXPathNodeSetItem(nodes2, 1)));
! 4576: }
! 4577:
! 4578: /**
! 4579: * xmlXPathLeading:
! 4580: * @nodes1: a node-set
! 4581: * @nodes2: a node-set
! 4582: *
! 4583: * Implements the EXSLT - Sets leading() function:
! 4584: * node-set set:leading (node-set, node-set)
! 4585: * @nodes1 and @nodes2 are sorted by document order, then
! 4586: * #exslSetsLeadingSorted is called.
! 4587: *
! 4588: * Returns the nodes in @nodes1 that precede the first node in @nodes2
! 4589: * in document order, @nodes1 if @nodes2 is NULL or empty or
! 4590: * an empty node-set if @nodes1 doesn't contain @nodes2
! 4591: */
! 4592: xmlNodeSetPtr
! 4593: xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
! 4594: if (xmlXPathNodeSetIsEmpty(nodes2))
! 4595: return(nodes1);
! 4596: if (xmlXPathNodeSetIsEmpty(nodes1))
! 4597: return(xmlXPathNodeSetCreate(NULL));
! 4598: xmlXPathNodeSetSort(nodes1);
! 4599: xmlXPathNodeSetSort(nodes2);
! 4600: return(xmlXPathNodeLeadingSorted(nodes1,
! 4601: xmlXPathNodeSetItem(nodes2, 1)));
! 4602: }
! 4603:
! 4604: /**
! 4605: * xmlXPathNodeTrailingSorted:
! 4606: * @nodes: a node-set, sorted by document order
! 4607: * @node: a node
! 4608: *
! 4609: * Implements the EXSLT - Sets trailing() function:
! 4610: * node-set set:trailing (node-set, node-set)
! 4611: *
! 4612: * Returns the nodes in @nodes that follow @node in document order,
! 4613: * @nodes if @node is NULL or an empty node-set if @nodes
! 4614: * doesn't contain @node
! 4615: */
! 4616: xmlNodeSetPtr
! 4617: xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
! 4618: int i, l;
! 4619: xmlNodePtr cur;
! 4620: xmlNodeSetPtr ret;
! 4621:
! 4622: if (node == NULL)
! 4623: return(nodes);
! 4624:
! 4625: ret = xmlXPathNodeSetCreate(NULL);
! 4626: if (ret == NULL)
! 4627: return(ret);
! 4628: if (xmlXPathNodeSetIsEmpty(nodes) ||
! 4629: (!xmlXPathNodeSetContains(nodes, node)))
! 4630: return(ret);
! 4631:
! 4632: l = xmlXPathNodeSetGetLength(nodes);
! 4633: for (i = l - 1; i >= 0; i--) {
! 4634: cur = xmlXPathNodeSetItem(nodes, i);
! 4635: if (cur == node)
! 4636: break;
! 4637: xmlXPathNodeSetAddUnique(ret, cur);
! 4638: }
! 4639: xmlXPathNodeSetSort(ret); /* bug 413451 */
! 4640: return(ret);
! 4641: }
! 4642:
! 4643: /**
! 4644: * xmlXPathNodeTrailing:
! 4645: * @nodes: a node-set
! 4646: * @node: a node
! 4647: *
! 4648: * Implements the EXSLT - Sets trailing() function:
! 4649: * node-set set:trailing (node-set, node-set)
! 4650: * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
! 4651: * is called.
! 4652: *
! 4653: * Returns the nodes in @nodes that follow @node in document order,
! 4654: * @nodes if @node is NULL or an empty node-set if @nodes
! 4655: * doesn't contain @node
! 4656: */
! 4657: xmlNodeSetPtr
! 4658: xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
! 4659: xmlXPathNodeSetSort(nodes);
! 4660: return(xmlXPathNodeTrailingSorted(nodes, node));
! 4661: }
! 4662:
! 4663: /**
! 4664: * xmlXPathTrailingSorted:
! 4665: * @nodes1: a node-set, sorted by document order
! 4666: * @nodes2: a node-set, sorted by document order
! 4667: *
! 4668: * Implements the EXSLT - Sets trailing() function:
! 4669: * node-set set:trailing (node-set, node-set)
! 4670: *
! 4671: * Returns the nodes in @nodes1 that follow the first node in @nodes2
! 4672: * in document order, @nodes1 if @nodes2 is NULL or empty or
! 4673: * an empty node-set if @nodes1 doesn't contain @nodes2
! 4674: */
! 4675: xmlNodeSetPtr
! 4676: xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
! 4677: if (xmlXPathNodeSetIsEmpty(nodes2))
! 4678: return(nodes1);
! 4679: return(xmlXPathNodeTrailingSorted(nodes1,
! 4680: xmlXPathNodeSetItem(nodes2, 0)));
! 4681: }
! 4682:
! 4683: /**
! 4684: * xmlXPathTrailing:
! 4685: * @nodes1: a node-set
! 4686: * @nodes2: a node-set
! 4687: *
! 4688: * Implements the EXSLT - Sets trailing() function:
! 4689: * node-set set:trailing (node-set, node-set)
! 4690: * @nodes1 and @nodes2 are sorted by document order, then
! 4691: * #xmlXPathTrailingSorted is called.
! 4692: *
! 4693: * Returns the nodes in @nodes1 that follow the first node in @nodes2
! 4694: * in document order, @nodes1 if @nodes2 is NULL or empty or
! 4695: * an empty node-set if @nodes1 doesn't contain @nodes2
! 4696: */
! 4697: xmlNodeSetPtr
! 4698: xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
! 4699: if (xmlXPathNodeSetIsEmpty(nodes2))
! 4700: return(nodes1);
! 4701: if (xmlXPathNodeSetIsEmpty(nodes1))
! 4702: return(xmlXPathNodeSetCreate(NULL));
! 4703: xmlXPathNodeSetSort(nodes1);
! 4704: xmlXPathNodeSetSort(nodes2);
! 4705: return(xmlXPathNodeTrailingSorted(nodes1,
! 4706: xmlXPathNodeSetItem(nodes2, 0)));
! 4707: }
! 4708:
! 4709: /************************************************************************
! 4710: * *
! 4711: * Routines to handle extra functions *
! 4712: * *
! 4713: ************************************************************************/
! 4714:
! 4715: /**
! 4716: * xmlXPathRegisterFunc:
! 4717: * @ctxt: the XPath context
! 4718: * @name: the function name
! 4719: * @f: the function implementation or NULL
! 4720: *
! 4721: * Register a new function. If @f is NULL it unregisters the function
! 4722: *
! 4723: * Returns 0 in case of success, -1 in case of error
! 4724: */
! 4725: int
! 4726: xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
! 4727: xmlXPathFunction f) {
! 4728: return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
! 4729: }
! 4730:
! 4731: /**
! 4732: * xmlXPathRegisterFuncNS:
! 4733: * @ctxt: the XPath context
! 4734: * @name: the function name
! 4735: * @ns_uri: the function namespace URI
! 4736: * @f: the function implementation or NULL
! 4737: *
! 4738: * Register a new function. If @f is NULL it unregisters the function
! 4739: *
! 4740: * Returns 0 in case of success, -1 in case of error
! 4741: */
! 4742: int
! 4743: xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
! 4744: const xmlChar *ns_uri, xmlXPathFunction f) {
! 4745: if (ctxt == NULL)
! 4746: return(-1);
! 4747: if (name == NULL)
! 4748: return(-1);
! 4749:
! 4750: if (ctxt->funcHash == NULL)
! 4751: ctxt->funcHash = xmlHashCreate(0);
! 4752: if (ctxt->funcHash == NULL)
! 4753: return(-1);
! 4754: if (f == NULL)
! 4755: return(xmlHashRemoveEntry2(ctxt->funcHash, name, ns_uri, NULL));
! 4756: return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, XML_CAST_FPTR(f)));
! 4757: }
! 4758:
! 4759: /**
! 4760: * xmlXPathRegisterFuncLookup:
! 4761: * @ctxt: the XPath context
! 4762: * @f: the lookup function
! 4763: * @funcCtxt: the lookup data
! 4764: *
! 4765: * Registers an external mechanism to do function lookup.
! 4766: */
! 4767: void
! 4768: xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
! 4769: xmlXPathFuncLookupFunc f,
! 4770: void *funcCtxt) {
! 4771: if (ctxt == NULL)
! 4772: return;
! 4773: ctxt->funcLookupFunc = f;
! 4774: ctxt->funcLookupData = funcCtxt;
! 4775: }
! 4776:
! 4777: /**
! 4778: * xmlXPathFunctionLookup:
! 4779: * @ctxt: the XPath context
! 4780: * @name: the function name
! 4781: *
! 4782: * Search in the Function array of the context for the given
! 4783: * function.
! 4784: *
! 4785: * Returns the xmlXPathFunction or NULL if not found
! 4786: */
! 4787: xmlXPathFunction
! 4788: xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
! 4789: if (ctxt == NULL)
! 4790: return (NULL);
! 4791:
! 4792: if (ctxt->funcLookupFunc != NULL) {
! 4793: xmlXPathFunction ret;
! 4794: xmlXPathFuncLookupFunc f;
! 4795:
! 4796: f = ctxt->funcLookupFunc;
! 4797: ret = f(ctxt->funcLookupData, name, NULL);
! 4798: if (ret != NULL)
! 4799: return(ret);
! 4800: }
! 4801: return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
! 4802: }
! 4803:
! 4804: /**
! 4805: * xmlXPathFunctionLookupNS:
! 4806: * @ctxt: the XPath context
! 4807: * @name: the function name
! 4808: * @ns_uri: the function namespace URI
! 4809: *
! 4810: * Search in the Function array of the context for the given
! 4811: * function.
! 4812: *
! 4813: * Returns the xmlXPathFunction or NULL if not found
! 4814: */
! 4815: xmlXPathFunction
! 4816: xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
! 4817: const xmlChar *ns_uri) {
! 4818: xmlXPathFunction ret;
! 4819:
! 4820: if (ctxt == NULL)
! 4821: return(NULL);
! 4822: if (name == NULL)
! 4823: return(NULL);
! 4824:
! 4825: if (ctxt->funcLookupFunc != NULL) {
! 4826: xmlXPathFuncLookupFunc f;
! 4827:
! 4828: f = ctxt->funcLookupFunc;
! 4829: ret = f(ctxt->funcLookupData, name, ns_uri);
! 4830: if (ret != NULL)
! 4831: return(ret);
! 4832: }
! 4833:
! 4834: if (ctxt->funcHash == NULL)
! 4835: return(NULL);
! 4836:
! 4837: XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri);
! 4838: return(ret);
! 4839: }
! 4840:
! 4841: /**
! 4842: * xmlXPathRegisteredFuncsCleanup:
! 4843: * @ctxt: the XPath context
! 4844: *
! 4845: * Cleanup the XPath context data associated to registered functions
! 4846: */
! 4847: void
! 4848: xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
! 4849: if (ctxt == NULL)
! 4850: return;
! 4851:
! 4852: xmlHashFree(ctxt->funcHash, NULL);
! 4853: ctxt->funcHash = NULL;
! 4854: }
! 4855:
! 4856: /************************************************************************
! 4857: * *
! 4858: * Routines to handle Variables *
! 4859: * *
! 4860: ************************************************************************/
! 4861:
! 4862: /**
! 4863: * xmlXPathRegisterVariable:
! 4864: * @ctxt: the XPath context
! 4865: * @name: the variable name
! 4866: * @value: the variable value or NULL
! 4867: *
! 4868: * Register a new variable value. If @value is NULL it unregisters
! 4869: * the variable
! 4870: *
! 4871: * Returns 0 in case of success, -1 in case of error
! 4872: */
! 4873: int
! 4874: xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
! 4875: xmlXPathObjectPtr value) {
! 4876: return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
! 4877: }
! 4878:
! 4879: /**
! 4880: * xmlXPathRegisterVariableNS:
! 4881: * @ctxt: the XPath context
! 4882: * @name: the variable name
! 4883: * @ns_uri: the variable namespace URI
! 4884: * @value: the variable value or NULL
! 4885: *
! 4886: * Register a new variable value. If @value is NULL it unregisters
! 4887: * the variable
! 4888: *
! 4889: * Returns 0 in case of success, -1 in case of error
! 4890: */
! 4891: int
! 4892: xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
! 4893: const xmlChar *ns_uri,
! 4894: xmlXPathObjectPtr value) {
! 4895: if (ctxt == NULL)
! 4896: return(-1);
! 4897: if (name == NULL)
! 4898: return(-1);
! 4899:
! 4900: if (ctxt->varHash == NULL)
! 4901: ctxt->varHash = xmlHashCreate(0);
! 4902: if (ctxt->varHash == NULL)
! 4903: return(-1);
! 4904: if (value == NULL)
! 4905: return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri,
! 4906: (xmlHashDeallocator)xmlXPathFreeObject));
! 4907: return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
! 4908: (void *) value,
! 4909: (xmlHashDeallocator)xmlXPathFreeObject));
! 4910: }
! 4911:
! 4912: /**
! 4913: * xmlXPathRegisterVariableLookup:
! 4914: * @ctxt: the XPath context
! 4915: * @f: the lookup function
! 4916: * @data: the lookup data
! 4917: *
! 4918: * register an external mechanism to do variable lookup
! 4919: */
! 4920: void
! 4921: xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
! 4922: xmlXPathVariableLookupFunc f, void *data) {
! 4923: if (ctxt == NULL)
! 4924: return;
! 4925: ctxt->varLookupFunc = f;
! 4926: ctxt->varLookupData = data;
! 4927: }
! 4928:
! 4929: /**
! 4930: * xmlXPathVariableLookup:
! 4931: * @ctxt: the XPath context
! 4932: * @name: the variable name
! 4933: *
! 4934: * Search in the Variable array of the context for the given
! 4935: * variable value.
! 4936: *
! 4937: * Returns a copy of the value or NULL if not found
! 4938: */
! 4939: xmlXPathObjectPtr
! 4940: xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
! 4941: if (ctxt == NULL)
! 4942: return(NULL);
! 4943:
! 4944: if (ctxt->varLookupFunc != NULL) {
! 4945: xmlXPathObjectPtr ret;
! 4946:
! 4947: ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
! 4948: (ctxt->varLookupData, name, NULL);
! 4949: return(ret);
! 4950: }
! 4951: return(xmlXPathVariableLookupNS(ctxt, name, NULL));
! 4952: }
! 4953:
! 4954: /**
! 4955: * xmlXPathVariableLookupNS:
! 4956: * @ctxt: the XPath context
! 4957: * @name: the variable name
! 4958: * @ns_uri: the variable namespace URI
! 4959: *
! 4960: * Search in the Variable array of the context for the given
! 4961: * variable value.
! 4962: *
! 4963: * Returns the a copy of the value or NULL if not found
! 4964: */
! 4965: xmlXPathObjectPtr
! 4966: xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
! 4967: const xmlChar *ns_uri) {
! 4968: if (ctxt == NULL)
! 4969: return(NULL);
! 4970:
! 4971: if (ctxt->varLookupFunc != NULL) {
! 4972: xmlXPathObjectPtr ret;
! 4973:
! 4974: ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
! 4975: (ctxt->varLookupData, name, ns_uri);
! 4976: if (ret != NULL) return(ret);
! 4977: }
! 4978:
! 4979: if (ctxt->varHash == NULL)
! 4980: return(NULL);
! 4981: if (name == NULL)
! 4982: return(NULL);
! 4983:
! 4984: return(xmlXPathCacheObjectCopy(ctxt, (xmlXPathObjectPtr)
! 4985: xmlHashLookup2(ctxt->varHash, name, ns_uri)));
! 4986: }
! 4987:
! 4988: /**
! 4989: * xmlXPathRegisteredVariablesCleanup:
! 4990: * @ctxt: the XPath context
! 4991: *
! 4992: * Cleanup the XPath context data associated to registered variables
! 4993: */
! 4994: void
! 4995: xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
! 4996: if (ctxt == NULL)
! 4997: return;
! 4998:
! 4999: xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject);
! 5000: ctxt->varHash = NULL;
! 5001: }
! 5002:
! 5003: /**
! 5004: * xmlXPathRegisterNs:
! 5005: * @ctxt: the XPath context
! 5006: * @prefix: the namespace prefix cannot be NULL or empty string
! 5007: * @ns_uri: the namespace name
! 5008: *
! 5009: * Register a new namespace. If @ns_uri is NULL it unregisters
! 5010: * the namespace
! 5011: *
! 5012: * Returns 0 in case of success, -1 in case of error
! 5013: */
! 5014: int
! 5015: xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
! 5016: const xmlChar *ns_uri) {
! 5017: if (ctxt == NULL)
! 5018: return(-1);
! 5019: if (prefix == NULL)
! 5020: return(-1);
! 5021: if (prefix[0] == 0)
! 5022: return(-1);
! 5023:
! 5024: if (ctxt->nsHash == NULL)
! 5025: ctxt->nsHash = xmlHashCreate(10);
! 5026: if (ctxt->nsHash == NULL)
! 5027: return(-1);
! 5028: if (ns_uri == NULL)
! 5029: return(xmlHashRemoveEntry(ctxt->nsHash, prefix,
! 5030: (xmlHashDeallocator)xmlFree));
! 5031: return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) xmlStrdup(ns_uri),
! 5032: (xmlHashDeallocator)xmlFree));
! 5033: }
! 5034:
! 5035: /**
! 5036: * xmlXPathNsLookup:
! 5037: * @ctxt: the XPath context
! 5038: * @prefix: the namespace prefix value
! 5039: *
! 5040: * Search in the namespace declaration array of the context for the given
! 5041: * namespace name associated to the given prefix
! 5042: *
! 5043: * Returns the value or NULL if not found
! 5044: */
! 5045: const xmlChar *
! 5046: xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
! 5047: if (ctxt == NULL)
! 5048: return(NULL);
! 5049: if (prefix == NULL)
! 5050: return(NULL);
! 5051:
! 5052: #ifdef XML_XML_NAMESPACE
! 5053: if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
! 5054: return(XML_XML_NAMESPACE);
! 5055: #endif
! 5056:
! 5057: if (ctxt->namespaces != NULL) {
! 5058: int i;
! 5059:
! 5060: for (i = 0;i < ctxt->nsNr;i++) {
! 5061: if ((ctxt->namespaces[i] != NULL) &&
! 5062: (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
! 5063: return(ctxt->namespaces[i]->href);
! 5064: }
! 5065: }
! 5066:
! 5067: return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
! 5068: }
! 5069:
! 5070: /**
! 5071: * xmlXPathRegisteredNsCleanup:
! 5072: * @ctxt: the XPath context
! 5073: *
! 5074: * Cleanup the XPath context data associated to registered variables
! 5075: */
! 5076: void
! 5077: xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
! 5078: if (ctxt == NULL)
! 5079: return;
! 5080:
! 5081: xmlHashFree(ctxt->nsHash, (xmlHashDeallocator)xmlFree);
! 5082: ctxt->nsHash = NULL;
! 5083: }
! 5084:
! 5085: /************************************************************************
! 5086: * *
! 5087: * Routines to handle Values *
! 5088: * *
! 5089: ************************************************************************/
! 5090:
! 5091: /* Allocations are terrible, one needs to optimize all this !!! */
! 5092:
! 5093: /**
! 5094: * xmlXPathNewFloat:
! 5095: * @val: the double value
! 5096: *
! 5097: * Create a new xmlXPathObjectPtr of type double and of value @val
! 5098: *
! 5099: * Returns the newly created object.
! 5100: */
! 5101: xmlXPathObjectPtr
! 5102: xmlXPathNewFloat(double val) {
! 5103: xmlXPathObjectPtr ret;
! 5104:
! 5105: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
! 5106: if (ret == NULL) {
! 5107: xmlXPathErrMemory(NULL, "creating float object\n");
! 5108: return(NULL);
! 5109: }
! 5110: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
! 5111: ret->type = XPATH_NUMBER;
! 5112: ret->floatval = val;
! 5113: #ifdef XP_DEBUG_OBJ_USAGE
! 5114: xmlXPathDebugObjUsageRequested(NULL, XPATH_NUMBER);
! 5115: #endif
! 5116: return(ret);
! 5117: }
! 5118:
! 5119: /**
! 5120: * xmlXPathNewBoolean:
! 5121: * @val: the boolean value
! 5122: *
! 5123: * Create a new xmlXPathObjectPtr of type boolean and of value @val
! 5124: *
! 5125: * Returns the newly created object.
! 5126: */
! 5127: xmlXPathObjectPtr
! 5128: xmlXPathNewBoolean(int val) {
! 5129: xmlXPathObjectPtr ret;
! 5130:
! 5131: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
! 5132: if (ret == NULL) {
! 5133: xmlXPathErrMemory(NULL, "creating boolean object\n");
! 5134: return(NULL);
! 5135: }
! 5136: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
! 5137: ret->type = XPATH_BOOLEAN;
! 5138: ret->boolval = (val != 0);
! 5139: #ifdef XP_DEBUG_OBJ_USAGE
! 5140: xmlXPathDebugObjUsageRequested(NULL, XPATH_BOOLEAN);
! 5141: #endif
! 5142: return(ret);
! 5143: }
! 5144:
! 5145: /**
! 5146: * xmlXPathNewString:
! 5147: * @val: the xmlChar * value
! 5148: *
! 5149: * Create a new xmlXPathObjectPtr of type string and of value @val
! 5150: *
! 5151: * Returns the newly created object.
! 5152: */
! 5153: xmlXPathObjectPtr
! 5154: xmlXPathNewString(const xmlChar *val) {
! 5155: xmlXPathObjectPtr ret;
! 5156:
! 5157: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
! 5158: if (ret == NULL) {
! 5159: xmlXPathErrMemory(NULL, "creating string object\n");
! 5160: return(NULL);
! 5161: }
! 5162: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
! 5163: ret->type = XPATH_STRING;
! 5164: if (val != NULL)
! 5165: ret->stringval = xmlStrdup(val);
! 5166: else
! 5167: ret->stringval = xmlStrdup((const xmlChar *)"");
! 5168: #ifdef XP_DEBUG_OBJ_USAGE
! 5169: xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
! 5170: #endif
! 5171: return(ret);
! 5172: }
! 5173:
! 5174: /**
! 5175: * xmlXPathWrapString:
! 5176: * @val: the xmlChar * value
! 5177: *
! 5178: * Wraps the @val string into an XPath object.
! 5179: *
! 5180: * Returns the newly created object.
! 5181: */
! 5182: xmlXPathObjectPtr
! 5183: xmlXPathWrapString (xmlChar *val) {
! 5184: xmlXPathObjectPtr ret;
! 5185:
! 5186: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
! 5187: if (ret == NULL) {
! 5188: xmlXPathErrMemory(NULL, "creating string object\n");
! 5189: return(NULL);
! 5190: }
! 5191: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
! 5192: ret->type = XPATH_STRING;
! 5193: ret->stringval = val;
! 5194: #ifdef XP_DEBUG_OBJ_USAGE
! 5195: xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
! 5196: #endif
! 5197: return(ret);
! 5198: }
! 5199:
! 5200: /**
! 5201: * xmlXPathNewCString:
! 5202: * @val: the char * value
! 5203: *
! 5204: * Create a new xmlXPathObjectPtr of type string and of value @val
! 5205: *
! 5206: * Returns the newly created object.
! 5207: */
! 5208: xmlXPathObjectPtr
! 5209: xmlXPathNewCString(const char *val) {
! 5210: xmlXPathObjectPtr ret;
! 5211:
! 5212: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
! 5213: if (ret == NULL) {
! 5214: xmlXPathErrMemory(NULL, "creating string object\n");
! 5215: return(NULL);
! 5216: }
! 5217: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
! 5218: ret->type = XPATH_STRING;
! 5219: ret->stringval = xmlStrdup(BAD_CAST val);
! 5220: #ifdef XP_DEBUG_OBJ_USAGE
! 5221: xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
! 5222: #endif
! 5223: return(ret);
! 5224: }
! 5225:
! 5226: /**
! 5227: * xmlXPathWrapCString:
! 5228: * @val: the char * value
! 5229: *
! 5230: * Wraps a string into an XPath object.
! 5231: *
! 5232: * Returns the newly created object.
! 5233: */
! 5234: xmlXPathObjectPtr
! 5235: xmlXPathWrapCString (char * val) {
! 5236: return(xmlXPathWrapString((xmlChar *)(val)));
! 5237: }
! 5238:
! 5239: /**
! 5240: * xmlXPathWrapExternal:
! 5241: * @val: the user data
! 5242: *
! 5243: * Wraps the @val data into an XPath object.
! 5244: *
! 5245: * Returns the newly created object.
! 5246: */
! 5247: xmlXPathObjectPtr
! 5248: xmlXPathWrapExternal (void *val) {
! 5249: xmlXPathObjectPtr ret;
! 5250:
! 5251: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
! 5252: if (ret == NULL) {
! 5253: xmlXPathErrMemory(NULL, "creating user object\n");
! 5254: return(NULL);
! 5255: }
! 5256: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
! 5257: ret->type = XPATH_USERS;
! 5258: ret->user = val;
! 5259: #ifdef XP_DEBUG_OBJ_USAGE
! 5260: xmlXPathDebugObjUsageRequested(NULL, XPATH_USERS);
! 5261: #endif
! 5262: return(ret);
! 5263: }
! 5264:
! 5265: /**
! 5266: * xmlXPathObjectCopy:
! 5267: * @val: the original object
! 5268: *
! 5269: * allocate a new copy of a given object
! 5270: *
! 5271: * Returns the newly created object.
! 5272: */
! 5273: xmlXPathObjectPtr
! 5274: xmlXPathObjectCopy(xmlXPathObjectPtr val) {
! 5275: xmlXPathObjectPtr ret;
! 5276:
! 5277: if (val == NULL)
! 5278: return(NULL);
! 5279:
! 5280: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
! 5281: if (ret == NULL) {
! 5282: xmlXPathErrMemory(NULL, "copying object\n");
! 5283: return(NULL);
! 5284: }
! 5285: memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
! 5286: #ifdef XP_DEBUG_OBJ_USAGE
! 5287: xmlXPathDebugObjUsageRequested(NULL, val->type);
! 5288: #endif
! 5289: switch (val->type) {
! 5290: case XPATH_BOOLEAN:
! 5291: case XPATH_NUMBER:
! 5292: case XPATH_POINT:
! 5293: case XPATH_RANGE:
! 5294: break;
! 5295: case XPATH_STRING:
! 5296: ret->stringval = xmlStrdup(val->stringval);
! 5297: break;
! 5298: case XPATH_XSLT_TREE:
! 5299: #if 0
! 5300: /*
! 5301: Removed 11 July 2004 - the current handling of xslt tmpRVT nodes means that
! 5302: this previous handling is no longer correct, and can cause some serious
! 5303: problems (ref. bug 145547)
! 5304: */
! 5305: if ((val->nodesetval != NULL) &&
! 5306: (val->nodesetval->nodeTab != NULL)) {
! 5307: xmlNodePtr cur, tmp;
! 5308: xmlDocPtr top;
! 5309:
! 5310: ret->boolval = 1;
! 5311: top = xmlNewDoc(NULL);
! 5312: top->name = (char *)
! 5313: xmlStrdup(val->nodesetval->nodeTab[0]->name);
! 5314: ret->user = top;
! 5315: if (top != NULL) {
! 5316: top->doc = top;
! 5317: cur = val->nodesetval->nodeTab[0]->children;
! 5318: while (cur != NULL) {
! 5319: tmp = xmlDocCopyNode(cur, top, 1);
! 5320: xmlAddChild((xmlNodePtr) top, tmp);
! 5321: cur = cur->next;
! 5322: }
! 5323: }
! 5324:
! 5325: ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top);
! 5326: } else
! 5327: ret->nodesetval = xmlXPathNodeSetCreate(NULL);
! 5328: /* Deallocate the copied tree value */
! 5329: break;
! 5330: #endif
! 5331: case XPATH_NODESET:
! 5332: ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
! 5333: /* Do not deallocate the copied tree value */
! 5334: ret->boolval = 0;
! 5335: break;
! 5336: case XPATH_LOCATIONSET:
! 5337: #ifdef LIBXML_XPTR_ENABLED
! 5338: {
! 5339: xmlLocationSetPtr loc = val->user;
! 5340: ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
! 5341: break;
! 5342: }
! 5343: #endif
! 5344: case XPATH_USERS:
! 5345: ret->user = val->user;
! 5346: break;
! 5347: case XPATH_UNDEFINED:
! 5348: xmlGenericError(xmlGenericErrorContext,
! 5349: "xmlXPathObjectCopy: unsupported type %d\n",
! 5350: val->type);
! 5351: break;
! 5352: }
! 5353: return(ret);
! 5354: }
! 5355:
! 5356: /**
! 5357: * xmlXPathFreeObject:
! 5358: * @obj: the object to free
! 5359: *
! 5360: * Free up an xmlXPathObjectPtr object.
! 5361: */
! 5362: void
! 5363: xmlXPathFreeObject(xmlXPathObjectPtr obj) {
! 5364: if (obj == NULL) return;
! 5365: if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
! 5366: if (obj->boolval) {
! 5367: #if 0
! 5368: if (obj->user != NULL) {
! 5369: xmlXPathFreeNodeSet(obj->nodesetval);
! 5370: xmlFreeNodeList((xmlNodePtr) obj->user);
! 5371: } else
! 5372: #endif
! 5373: obj->type = XPATH_XSLT_TREE; /* TODO: Just for debugging. */
! 5374: if (obj->nodesetval != NULL)
! 5375: xmlXPathFreeValueTree(obj->nodesetval);
! 5376: } else {
! 5377: if (obj->nodesetval != NULL)
! 5378: xmlXPathFreeNodeSet(obj->nodesetval);
! 5379: }
! 5380: #ifdef LIBXML_XPTR_ENABLED
! 5381: } else if (obj->type == XPATH_LOCATIONSET) {
! 5382: if (obj->user != NULL)
! 5383: xmlXPtrFreeLocationSet(obj->user);
! 5384: #endif
! 5385: } else if (obj->type == XPATH_STRING) {
! 5386: if (obj->stringval != NULL)
! 5387: xmlFree(obj->stringval);
! 5388: }
! 5389: #ifdef XP_DEBUG_OBJ_USAGE
! 5390: xmlXPathDebugObjUsageReleased(NULL, obj->type);
! 5391: #endif
! 5392: xmlFree(obj);
! 5393: }
! 5394:
! 5395: /**
! 5396: * xmlXPathReleaseObject:
! 5397: * @obj: the xmlXPathObjectPtr to free or to cache
! 5398: *
! 5399: * Depending on the state of the cache this frees the given
! 5400: * XPath object or stores it in the cache.
! 5401: */
! 5402: static void
! 5403: xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj)
! 5404: {
! 5405: #define XP_CACHE_ADD(sl, o) if (sl == NULL) { \
! 5406: sl = xmlPointerListCreate(10); if (sl == NULL) goto free_obj; } \
! 5407: if (xmlPointerListAddSize(sl, obj, 0) == -1) goto free_obj;
! 5408:
! 5409: #define XP_CACHE_WANTS(sl, n) ((sl == NULL) || ((sl)->number < n))
! 5410:
! 5411: if (obj == NULL)
! 5412: return;
! 5413: if ((ctxt == NULL) || (ctxt->cache == NULL)) {
! 5414: xmlXPathFreeObject(obj);
! 5415: } else {
! 5416: xmlXPathContextCachePtr cache =
! 5417: (xmlXPathContextCachePtr) ctxt->cache;
! 5418:
! 5419: switch (obj->type) {
! 5420: case XPATH_NODESET:
! 5421: case XPATH_XSLT_TREE:
! 5422: if (obj->nodesetval != NULL) {
! 5423: if (obj->boolval) {
! 5424: /*
! 5425: * It looks like the @boolval is used for
! 5426: * evaluation if this an XSLT Result Tree Fragment.
! 5427: * TODO: Check if this assumption is correct.
! 5428: */
! 5429: obj->type = XPATH_XSLT_TREE; /* just for debugging */
! 5430: xmlXPathFreeValueTree(obj->nodesetval);
! 5431: obj->nodesetval = NULL;
! 5432: } else if ((obj->nodesetval->nodeMax <= 40) &&
! 5433: (XP_CACHE_WANTS(cache->nodesetObjs,
! 5434: cache->maxNodeset)))
! 5435: {
! 5436: XP_CACHE_ADD(cache->nodesetObjs, obj);
! 5437: goto obj_cached;
! 5438: } else {
! 5439: xmlXPathFreeNodeSet(obj->nodesetval);
! 5440: obj->nodesetval = NULL;
! 5441: }
! 5442: }
! 5443: break;
! 5444: case XPATH_STRING:
! 5445: if (obj->stringval != NULL)
! 5446: xmlFree(obj->stringval);
! 5447:
! 5448: if (XP_CACHE_WANTS(cache->stringObjs, cache->maxString)) {
! 5449: XP_CACHE_ADD(cache->stringObjs, obj);
! 5450: goto obj_cached;
! 5451: }
! 5452: break;
! 5453: case XPATH_BOOLEAN:
! 5454: if (XP_CACHE_WANTS(cache->booleanObjs, cache->maxBoolean)) {
! 5455: XP_CACHE_ADD(cache->booleanObjs, obj);
! 5456: goto obj_cached;
! 5457: }
! 5458: break;
! 5459: case XPATH_NUMBER:
! 5460: if (XP_CACHE_WANTS(cache->numberObjs, cache->maxNumber)) {
! 5461: XP_CACHE_ADD(cache->numberObjs, obj);
! 5462: goto obj_cached;
! 5463: }
! 5464: break;
! 5465: #ifdef LIBXML_XPTR_ENABLED
! 5466: case XPATH_LOCATIONSET:
! 5467: if (obj->user != NULL) {
! 5468: xmlXPtrFreeLocationSet(obj->user);
! 5469: }
! 5470: goto free_obj;
! 5471: #endif
! 5472: default:
! 5473: goto free_obj;
! 5474: }
! 5475:
! 5476: /*
! 5477: * Fallback to adding to the misc-objects slot.
! 5478: */
! 5479: if (XP_CACHE_WANTS(cache->miscObjs, cache->maxMisc)) {
! 5480: XP_CACHE_ADD(cache->miscObjs, obj);
! 5481: } else
! 5482: goto free_obj;
! 5483:
! 5484: obj_cached:
! 5485:
! 5486: #ifdef XP_DEBUG_OBJ_USAGE
! 5487: xmlXPathDebugObjUsageReleased(ctxt, obj->type);
! 5488: #endif
! 5489:
! 5490: if (obj->nodesetval != NULL) {
! 5491: xmlNodeSetPtr tmpset = obj->nodesetval;
! 5492:
! 5493: /*
! 5494: * TODO: Due to those nasty ns-nodes, we need to traverse
! 5495: * the list and free the ns-nodes.
! 5496: * URGENT TODO: Check if it's actually slowing things down.
! 5497: * Maybe we shouldn't try to preserve the list.
! 5498: */
! 5499: if (tmpset->nodeNr > 1) {
! 5500: int i;
! 5501: xmlNodePtr node;
! 5502:
! 5503: for (i = 0; i < tmpset->nodeNr; i++) {
! 5504: node = tmpset->nodeTab[i];
! 5505: if ((node != NULL) &&
! 5506: (node->type == XML_NAMESPACE_DECL))
! 5507: {
! 5508: xmlXPathNodeSetFreeNs((xmlNsPtr) node);
! 5509: }
! 5510: }
! 5511: } else if (tmpset->nodeNr == 1) {
! 5512: if ((tmpset->nodeTab[0] != NULL) &&
! 5513: (tmpset->nodeTab[0]->type == XML_NAMESPACE_DECL))
! 5514: xmlXPathNodeSetFreeNs((xmlNsPtr) tmpset->nodeTab[0]);
! 5515: }
! 5516: tmpset->nodeNr = 0;
! 5517: memset(obj, 0, sizeof(xmlXPathObject));
! 5518: obj->nodesetval = tmpset;
! 5519: } else
! 5520: memset(obj, 0, sizeof(xmlXPathObject));
! 5521:
! 5522: return;
! 5523:
! 5524: free_obj:
! 5525: /*
! 5526: * Cache is full; free the object.
! 5527: */
! 5528: if (obj->nodesetval != NULL)
! 5529: xmlXPathFreeNodeSet(obj->nodesetval);
! 5530: #ifdef XP_DEBUG_OBJ_USAGE
! 5531: xmlXPathDebugObjUsageReleased(NULL, obj->type);
! 5532: #endif
! 5533: xmlFree(obj);
! 5534: }
! 5535: return;
! 5536: }
! 5537:
! 5538:
! 5539: /************************************************************************
! 5540: * *
! 5541: * Type Casting Routines *
! 5542: * *
! 5543: ************************************************************************/
! 5544:
! 5545: /**
! 5546: * xmlXPathCastBooleanToString:
! 5547: * @val: a boolean
! 5548: *
! 5549: * Converts a boolean to its string value.
! 5550: *
! 5551: * Returns a newly allocated string.
! 5552: */
! 5553: xmlChar *
! 5554: xmlXPathCastBooleanToString (int val) {
! 5555: xmlChar *ret;
! 5556: if (val)
! 5557: ret = xmlStrdup((const xmlChar *) "true");
! 5558: else
! 5559: ret = xmlStrdup((const xmlChar *) "false");
! 5560: return(ret);
! 5561: }
! 5562:
! 5563: /**
! 5564: * xmlXPathCastNumberToString:
! 5565: * @val: a number
! 5566: *
! 5567: * Converts a number to its string value.
! 5568: *
! 5569: * Returns a newly allocated string.
! 5570: */
! 5571: xmlChar *
! 5572: xmlXPathCastNumberToString (double val) {
! 5573: xmlChar *ret;
! 5574: switch (xmlXPathIsInf(val)) {
! 5575: case 1:
! 5576: ret = xmlStrdup((const xmlChar *) "Infinity");
! 5577: break;
! 5578: case -1:
! 5579: ret = xmlStrdup((const xmlChar *) "-Infinity");
! 5580: break;
! 5581: default:
! 5582: if (xmlXPathIsNaN(val)) {
! 5583: ret = xmlStrdup((const xmlChar *) "NaN");
! 5584: } else if (val == 0 && xmlXPathGetSign(val) != 0) {
! 5585: ret = xmlStrdup((const xmlChar *) "0");
! 5586: } else {
! 5587: /* could be improved */
! 5588: char buf[100];
! 5589: xmlXPathFormatNumber(val, buf, 99);
! 5590: buf[99] = 0;
! 5591: ret = xmlStrdup((const xmlChar *) buf);
! 5592: }
! 5593: }
! 5594: return(ret);
! 5595: }
! 5596:
! 5597: /**
! 5598: * xmlXPathCastNodeToString:
! 5599: * @node: a node
! 5600: *
! 5601: * Converts a node to its string value.
! 5602: *
! 5603: * Returns a newly allocated string.
! 5604: */
! 5605: xmlChar *
! 5606: xmlXPathCastNodeToString (xmlNodePtr node) {
! 5607: xmlChar *ret;
! 5608: if ((ret = xmlNodeGetContent(node)) == NULL)
! 5609: ret = xmlStrdup((const xmlChar *) "");
! 5610: return(ret);
! 5611: }
! 5612:
! 5613: /**
! 5614: * xmlXPathCastNodeSetToString:
! 5615: * @ns: a node-set
! 5616: *
! 5617: * Converts a node-set to its string value.
! 5618: *
! 5619: * Returns a newly allocated string.
! 5620: */
! 5621: xmlChar *
! 5622: xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
! 5623: if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
! 5624: return(xmlStrdup((const xmlChar *) ""));
! 5625:
! 5626: if (ns->nodeNr > 1)
! 5627: xmlXPathNodeSetSort(ns);
! 5628: return(xmlXPathCastNodeToString(ns->nodeTab[0]));
! 5629: }
! 5630:
! 5631: /**
! 5632: * xmlXPathCastToString:
! 5633: * @val: an XPath object
! 5634: *
! 5635: * Converts an existing object to its string() equivalent
! 5636: *
! 5637: * Returns the allocated string value of the object, NULL in case of error.
! 5638: * It's up to the caller to free the string memory with xmlFree().
! 5639: */
! 5640: xmlChar *
! 5641: xmlXPathCastToString(xmlXPathObjectPtr val) {
! 5642: xmlChar *ret = NULL;
! 5643:
! 5644: if (val == NULL)
! 5645: return(xmlStrdup((const xmlChar *) ""));
! 5646: switch (val->type) {
! 5647: case XPATH_UNDEFINED:
! 5648: #ifdef DEBUG_EXPR
! 5649: xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
! 5650: #endif
! 5651: ret = xmlStrdup((const xmlChar *) "");
! 5652: break;
! 5653: case XPATH_NODESET:
! 5654: case XPATH_XSLT_TREE:
! 5655: ret = xmlXPathCastNodeSetToString(val->nodesetval);
! 5656: break;
! 5657: case XPATH_STRING:
! 5658: return(xmlStrdup(val->stringval));
! 5659: case XPATH_BOOLEAN:
! 5660: ret = xmlXPathCastBooleanToString(val->boolval);
! 5661: break;
! 5662: case XPATH_NUMBER: {
! 5663: ret = xmlXPathCastNumberToString(val->floatval);
! 5664: break;
! 5665: }
! 5666: case XPATH_USERS:
! 5667: case XPATH_POINT:
! 5668: case XPATH_RANGE:
! 5669: case XPATH_LOCATIONSET:
! 5670: TODO
! 5671: ret = xmlStrdup((const xmlChar *) "");
! 5672: break;
! 5673: }
! 5674: return(ret);
! 5675: }
! 5676:
! 5677: /**
! 5678: * xmlXPathConvertString:
! 5679: * @val: an XPath object
! 5680: *
! 5681: * Converts an existing object to its string() equivalent
! 5682: *
! 5683: * Returns the new object, the old one is freed (or the operation
! 5684: * is done directly on @val)
! 5685: */
! 5686: xmlXPathObjectPtr
! 5687: xmlXPathConvertString(xmlXPathObjectPtr val) {
! 5688: xmlChar *res = NULL;
! 5689:
! 5690: if (val == NULL)
! 5691: return(xmlXPathNewCString(""));
! 5692:
! 5693: switch (val->type) {
! 5694: case XPATH_UNDEFINED:
! 5695: #ifdef DEBUG_EXPR
! 5696: xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
! 5697: #endif
! 5698: break;
! 5699: case XPATH_NODESET:
! 5700: case XPATH_XSLT_TREE:
! 5701: res = xmlXPathCastNodeSetToString(val->nodesetval);
! 5702: break;
! 5703: case XPATH_STRING:
! 5704: return(val);
! 5705: case XPATH_BOOLEAN:
! 5706: res = xmlXPathCastBooleanToString(val->boolval);
! 5707: break;
! 5708: case XPATH_NUMBER:
! 5709: res = xmlXPathCastNumberToString(val->floatval);
! 5710: break;
! 5711: case XPATH_USERS:
! 5712: case XPATH_POINT:
! 5713: case XPATH_RANGE:
! 5714: case XPATH_LOCATIONSET:
! 5715: TODO;
! 5716: break;
! 5717: }
! 5718: xmlXPathFreeObject(val);
! 5719: if (res == NULL)
! 5720: return(xmlXPathNewCString(""));
! 5721: return(xmlXPathWrapString(res));
! 5722: }
! 5723:
! 5724: /**
! 5725: * xmlXPathCastBooleanToNumber:
! 5726: * @val: a boolean
! 5727: *
! 5728: * Converts a boolean to its number value
! 5729: *
! 5730: * Returns the number value
! 5731: */
! 5732: double
! 5733: xmlXPathCastBooleanToNumber(int val) {
! 5734: if (val)
! 5735: return(1.0);
! 5736: return(0.0);
! 5737: }
! 5738:
! 5739: /**
! 5740: * xmlXPathCastStringToNumber:
! 5741: * @val: a string
! 5742: *
! 5743: * Converts a string to its number value
! 5744: *
! 5745: * Returns the number value
! 5746: */
! 5747: double
! 5748: xmlXPathCastStringToNumber(const xmlChar * val) {
! 5749: return(xmlXPathStringEvalNumber(val));
! 5750: }
! 5751:
! 5752: /**
! 5753: * xmlXPathCastNodeToNumber:
! 5754: * @node: a node
! 5755: *
! 5756: * Converts a node to its number value
! 5757: *
! 5758: * Returns the number value
! 5759: */
! 5760: double
! 5761: xmlXPathCastNodeToNumber (xmlNodePtr node) {
! 5762: xmlChar *strval;
! 5763: double ret;
! 5764:
! 5765: if (node == NULL)
! 5766: return(xmlXPathNAN);
! 5767: strval = xmlXPathCastNodeToString(node);
! 5768: if (strval == NULL)
! 5769: return(xmlXPathNAN);
! 5770: ret = xmlXPathCastStringToNumber(strval);
! 5771: xmlFree(strval);
! 5772:
! 5773: return(ret);
! 5774: }
! 5775:
! 5776: /**
! 5777: * xmlXPathCastNodeSetToNumber:
! 5778: * @ns: a node-set
! 5779: *
! 5780: * Converts a node-set to its number value
! 5781: *
! 5782: * Returns the number value
! 5783: */
! 5784: double
! 5785: xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
! 5786: xmlChar *str;
! 5787: double ret;
! 5788:
! 5789: if (ns == NULL)
! 5790: return(xmlXPathNAN);
! 5791: str = xmlXPathCastNodeSetToString(ns);
! 5792: ret = xmlXPathCastStringToNumber(str);
! 5793: xmlFree(str);
! 5794: return(ret);
! 5795: }
! 5796:
! 5797: /**
! 5798: * xmlXPathCastToNumber:
! 5799: * @val: an XPath object
! 5800: *
! 5801: * Converts an XPath object to its number value
! 5802: *
! 5803: * Returns the number value
! 5804: */
! 5805: double
! 5806: xmlXPathCastToNumber(xmlXPathObjectPtr val) {
! 5807: double ret = 0.0;
! 5808:
! 5809: if (val == NULL)
! 5810: return(xmlXPathNAN);
! 5811: switch (val->type) {
! 5812: case XPATH_UNDEFINED:
! 5813: #ifdef DEGUB_EXPR
! 5814: xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
! 5815: #endif
! 5816: ret = xmlXPathNAN;
! 5817: break;
! 5818: case XPATH_NODESET:
! 5819: case XPATH_XSLT_TREE:
! 5820: ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
! 5821: break;
! 5822: case XPATH_STRING:
! 5823: ret = xmlXPathCastStringToNumber(val->stringval);
! 5824: break;
! 5825: case XPATH_NUMBER:
! 5826: ret = val->floatval;
! 5827: break;
! 5828: case XPATH_BOOLEAN:
! 5829: ret = xmlXPathCastBooleanToNumber(val->boolval);
! 5830: break;
! 5831: case XPATH_USERS:
! 5832: case XPATH_POINT:
! 5833: case XPATH_RANGE:
! 5834: case XPATH_LOCATIONSET:
! 5835: TODO;
! 5836: ret = xmlXPathNAN;
! 5837: break;
! 5838: }
! 5839: return(ret);
! 5840: }
! 5841:
! 5842: /**
! 5843: * xmlXPathConvertNumber:
! 5844: * @val: an XPath object
! 5845: *
! 5846: * Converts an existing object to its number() equivalent
! 5847: *
! 5848: * Returns the new object, the old one is freed (or the operation
! 5849: * is done directly on @val)
! 5850: */
! 5851: xmlXPathObjectPtr
! 5852: xmlXPathConvertNumber(xmlXPathObjectPtr val) {
! 5853: xmlXPathObjectPtr ret;
! 5854:
! 5855: if (val == NULL)
! 5856: return(xmlXPathNewFloat(0.0));
! 5857: if (val->type == XPATH_NUMBER)
! 5858: return(val);
! 5859: ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
! 5860: xmlXPathFreeObject(val);
! 5861: return(ret);
! 5862: }
! 5863:
! 5864: /**
! 5865: * xmlXPathCastNumberToBoolean:
! 5866: * @val: a number
! 5867: *
! 5868: * Converts a number to its boolean value
! 5869: *
! 5870: * Returns the boolean value
! 5871: */
! 5872: int
! 5873: xmlXPathCastNumberToBoolean (double val) {
! 5874: if (xmlXPathIsNaN(val) || (val == 0.0))
! 5875: return(0);
! 5876: return(1);
! 5877: }
! 5878:
! 5879: /**
! 5880: * xmlXPathCastStringToBoolean:
! 5881: * @val: a string
! 5882: *
! 5883: * Converts a string to its boolean value
! 5884: *
! 5885: * Returns the boolean value
! 5886: */
! 5887: int
! 5888: xmlXPathCastStringToBoolean (const xmlChar *val) {
! 5889: if ((val == NULL) || (xmlStrlen(val) == 0))
! 5890: return(0);
! 5891: return(1);
! 5892: }
! 5893:
! 5894: /**
! 5895: * xmlXPathCastNodeSetToBoolean:
! 5896: * @ns: a node-set
! 5897: *
! 5898: * Converts a node-set to its boolean value
! 5899: *
! 5900: * Returns the boolean value
! 5901: */
! 5902: int
! 5903: xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
! 5904: if ((ns == NULL) || (ns->nodeNr == 0))
! 5905: return(0);
! 5906: return(1);
! 5907: }
! 5908:
! 5909: /**
! 5910: * xmlXPathCastToBoolean:
! 5911: * @val: an XPath object
! 5912: *
! 5913: * Converts an XPath object to its boolean value
! 5914: *
! 5915: * Returns the boolean value
! 5916: */
! 5917: int
! 5918: xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
! 5919: int ret = 0;
! 5920:
! 5921: if (val == NULL)
! 5922: return(0);
! 5923: switch (val->type) {
! 5924: case XPATH_UNDEFINED:
! 5925: #ifdef DEBUG_EXPR
! 5926: xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
! 5927: #endif
! 5928: ret = 0;
! 5929: break;
! 5930: case XPATH_NODESET:
! 5931: case XPATH_XSLT_TREE:
! 5932: ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
! 5933: break;
! 5934: case XPATH_STRING:
! 5935: ret = xmlXPathCastStringToBoolean(val->stringval);
! 5936: break;
! 5937: case XPATH_NUMBER:
! 5938: ret = xmlXPathCastNumberToBoolean(val->floatval);
! 5939: break;
! 5940: case XPATH_BOOLEAN:
! 5941: ret = val->boolval;
! 5942: break;
! 5943: case XPATH_USERS:
! 5944: case XPATH_POINT:
! 5945: case XPATH_RANGE:
! 5946: case XPATH_LOCATIONSET:
! 5947: TODO;
! 5948: ret = 0;
! 5949: break;
! 5950: }
! 5951: return(ret);
! 5952: }
! 5953:
! 5954:
! 5955: /**
! 5956: * xmlXPathConvertBoolean:
! 5957: * @val: an XPath object
! 5958: *
! 5959: * Converts an existing object to its boolean() equivalent
! 5960: *
! 5961: * Returns the new object, the old one is freed (or the operation
! 5962: * is done directly on @val)
! 5963: */
! 5964: xmlXPathObjectPtr
! 5965: xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
! 5966: xmlXPathObjectPtr ret;
! 5967:
! 5968: if (val == NULL)
! 5969: return(xmlXPathNewBoolean(0));
! 5970: if (val->type == XPATH_BOOLEAN)
! 5971: return(val);
! 5972: ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
! 5973: xmlXPathFreeObject(val);
! 5974: return(ret);
! 5975: }
! 5976:
! 5977: /************************************************************************
! 5978: * *
! 5979: * Routines to handle XPath contexts *
! 5980: * *
! 5981: ************************************************************************/
! 5982:
! 5983: /**
! 5984: * xmlXPathNewContext:
! 5985: * @doc: the XML document
! 5986: *
! 5987: * Create a new xmlXPathContext
! 5988: *
! 5989: * Returns the xmlXPathContext just allocated. The caller will need to free it.
! 5990: */
! 5991: xmlXPathContextPtr
! 5992: xmlXPathNewContext(xmlDocPtr doc) {
! 5993: xmlXPathContextPtr ret;
! 5994:
! 5995: ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
! 5996: if (ret == NULL) {
! 5997: xmlXPathErrMemory(NULL, "creating context\n");
! 5998: return(NULL);
! 5999: }
! 6000: memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
! 6001: ret->doc = doc;
! 6002: ret->node = NULL;
! 6003:
! 6004: ret->varHash = NULL;
! 6005:
! 6006: ret->nb_types = 0;
! 6007: ret->max_types = 0;
! 6008: ret->types = NULL;
! 6009:
! 6010: ret->funcHash = xmlHashCreate(0);
! 6011:
! 6012: ret->nb_axis = 0;
! 6013: ret->max_axis = 0;
! 6014: ret->axis = NULL;
! 6015:
! 6016: ret->nsHash = NULL;
! 6017: ret->user = NULL;
! 6018:
! 6019: ret->contextSize = -1;
! 6020: ret->proximityPosition = -1;
! 6021:
! 6022: #ifdef XP_DEFAULT_CACHE_ON
! 6023: if (xmlXPathContextSetCache(ret, 1, -1, 0) == -1) {
! 6024: xmlXPathFreeContext(ret);
! 6025: return(NULL);
! 6026: }
! 6027: #endif
! 6028:
! 6029: xmlXPathRegisterAllFunctions(ret);
! 6030:
! 6031: return(ret);
! 6032: }
! 6033:
! 6034: /**
! 6035: * xmlXPathFreeContext:
! 6036: * @ctxt: the context to free
! 6037: *
! 6038: * Free up an xmlXPathContext
! 6039: */
! 6040: void
! 6041: xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
! 6042: if (ctxt == NULL) return;
! 6043:
! 6044: if (ctxt->cache != NULL)
! 6045: xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
! 6046: xmlXPathRegisteredNsCleanup(ctxt);
! 6047: xmlXPathRegisteredFuncsCleanup(ctxt);
! 6048: xmlXPathRegisteredVariablesCleanup(ctxt);
! 6049: xmlResetError(&ctxt->lastError);
! 6050: xmlFree(ctxt);
! 6051: }
! 6052:
! 6053: /************************************************************************
! 6054: * *
! 6055: * Routines to handle XPath parser contexts *
! 6056: * *
! 6057: ************************************************************************/
! 6058:
! 6059: #define CHECK_CTXT(ctxt) \
! 6060: if (ctxt == NULL) { \
! 6061: __xmlRaiseError(NULL, NULL, NULL, \
! 6062: NULL, NULL, XML_FROM_XPATH, \
! 6063: XML_ERR_INTERNAL_ERROR, XML_ERR_FATAL, \
! 6064: __FILE__, __LINE__, \
! 6065: NULL, NULL, NULL, 0, 0, \
! 6066: "NULL context pointer\n"); \
! 6067: return(NULL); \
! 6068: } \
! 6069:
! 6070: #define CHECK_CTXT_NEG(ctxt) \
! 6071: if (ctxt == NULL) { \
! 6072: __xmlRaiseError(NULL, NULL, NULL, \
! 6073: NULL, NULL, XML_FROM_XPATH, \
! 6074: XML_ERR_INTERNAL_ERROR, XML_ERR_FATAL, \
! 6075: __FILE__, __LINE__, \
! 6076: NULL, NULL, NULL, 0, 0, \
! 6077: "NULL context pointer\n"); \
! 6078: return(-1); \
! 6079: } \
! 6080:
! 6081:
! 6082: #define CHECK_CONTEXT(ctxt) \
! 6083: if ((ctxt == NULL) || (ctxt->doc == NULL) || \
! 6084: (ctxt->doc->children == NULL)) { \
! 6085: xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_INVALID_CTXT); \
! 6086: return(NULL); \
! 6087: }
! 6088:
! 6089:
! 6090: /**
! 6091: * xmlXPathNewParserContext:
! 6092: * @str: the XPath expression
! 6093: * @ctxt: the XPath context
! 6094: *
! 6095: * Create a new xmlXPathParserContext
! 6096: *
! 6097: * Returns the xmlXPathParserContext just allocated.
! 6098: */
! 6099: xmlXPathParserContextPtr
! 6100: xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
! 6101: xmlXPathParserContextPtr ret;
! 6102:
! 6103: ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
! 6104: if (ret == NULL) {
! 6105: xmlXPathErrMemory(ctxt, "creating parser context\n");
! 6106: return(NULL);
! 6107: }
! 6108: memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
! 6109: ret->cur = ret->base = str;
! 6110: ret->context = ctxt;
! 6111:
! 6112: ret->comp = xmlXPathNewCompExpr();
! 6113: if (ret->comp == NULL) {
! 6114: xmlFree(ret->valueTab);
! 6115: xmlFree(ret);
! 6116: return(NULL);
! 6117: }
! 6118: if ((ctxt != NULL) && (ctxt->dict != NULL)) {
! 6119: ret->comp->dict = ctxt->dict;
! 6120: xmlDictReference(ret->comp->dict);
! 6121: }
! 6122:
! 6123: return(ret);
! 6124: }
! 6125:
! 6126: /**
! 6127: * xmlXPathCompParserContext:
! 6128: * @comp: the XPath compiled expression
! 6129: * @ctxt: the XPath context
! 6130: *
! 6131: * Create a new xmlXPathParserContext when processing a compiled expression
! 6132: *
! 6133: * Returns the xmlXPathParserContext just allocated.
! 6134: */
! 6135: static xmlXPathParserContextPtr
! 6136: xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
! 6137: xmlXPathParserContextPtr ret;
! 6138:
! 6139: ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
! 6140: if (ret == NULL) {
! 6141: xmlXPathErrMemory(ctxt, "creating evaluation context\n");
! 6142: return(NULL);
! 6143: }
! 6144: memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
! 6145:
! 6146: /* Allocate the value stack */
! 6147: ret->valueTab = (xmlXPathObjectPtr *)
! 6148: xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
! 6149: if (ret->valueTab == NULL) {
! 6150: xmlFree(ret);
! 6151: xmlXPathErrMemory(ctxt, "creating evaluation context\n");
! 6152: return(NULL);
! 6153: }
! 6154: ret->valueNr = 0;
! 6155: ret->valueMax = 10;
! 6156: ret->value = NULL;
! 6157:
! 6158: ret->context = ctxt;
! 6159: ret->comp = comp;
! 6160:
! 6161: return(ret);
! 6162: }
! 6163:
! 6164: /**
! 6165: * xmlXPathFreeParserContext:
! 6166: * @ctxt: the context to free
! 6167: *
! 6168: * Free up an xmlXPathParserContext
! 6169: */
! 6170: void
! 6171: xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
! 6172: if (ctxt->valueTab != NULL) {
! 6173: xmlFree(ctxt->valueTab);
! 6174: }
! 6175: if (ctxt->comp != NULL) {
! 6176: #ifdef XPATH_STREAMING
! 6177: if (ctxt->comp->stream != NULL) {
! 6178: xmlFreePatternList(ctxt->comp->stream);
! 6179: ctxt->comp->stream = NULL;
! 6180: }
! 6181: #endif
! 6182: xmlXPathFreeCompExpr(ctxt->comp);
! 6183: }
! 6184: xmlFree(ctxt);
! 6185: }
! 6186:
! 6187: /************************************************************************
! 6188: * *
! 6189: * The implicit core function library *
! 6190: * *
! 6191: ************************************************************************/
! 6192:
! 6193: /**
! 6194: * xmlXPathNodeValHash:
! 6195: * @node: a node pointer
! 6196: *
! 6197: * Function computing the beginning of the string value of the node,
! 6198: * used to speed up comparisons
! 6199: *
! 6200: * Returns an int usable as a hash
! 6201: */
! 6202: static unsigned int
! 6203: xmlXPathNodeValHash(xmlNodePtr node) {
! 6204: int len = 2;
! 6205: const xmlChar * string = NULL;
! 6206: xmlNodePtr tmp = NULL;
! 6207: unsigned int ret = 0;
! 6208:
! 6209: if (node == NULL)
! 6210: return(0);
! 6211:
! 6212: if (node->type == XML_DOCUMENT_NODE) {
! 6213: tmp = xmlDocGetRootElement((xmlDocPtr) node);
! 6214: if (tmp == NULL)
! 6215: node = node->children;
! 6216: else
! 6217: node = tmp;
! 6218:
! 6219: if (node == NULL)
! 6220: return(0);
! 6221: }
! 6222:
! 6223: switch (node->type) {
! 6224: case XML_COMMENT_NODE:
! 6225: case XML_PI_NODE:
! 6226: case XML_CDATA_SECTION_NODE:
! 6227: case XML_TEXT_NODE:
! 6228: string = node->content;
! 6229: if (string == NULL)
! 6230: return(0);
! 6231: if (string[0] == 0)
! 6232: return(0);
! 6233: return(((unsigned int) string[0]) +
! 6234: (((unsigned int) string[1]) << 8));
! 6235: case XML_NAMESPACE_DECL:
! 6236: string = ((xmlNsPtr)node)->href;
! 6237: if (string == NULL)
! 6238: return(0);
! 6239: if (string[0] == 0)
! 6240: return(0);
! 6241: return(((unsigned int) string[0]) +
! 6242: (((unsigned int) string[1]) << 8));
! 6243: case XML_ATTRIBUTE_NODE:
! 6244: tmp = ((xmlAttrPtr) node)->children;
! 6245: break;
! 6246: case XML_ELEMENT_NODE:
! 6247: tmp = node->children;
! 6248: break;
! 6249: default:
! 6250: return(0);
! 6251: }
! 6252: while (tmp != NULL) {
! 6253: switch (tmp->type) {
! 6254: case XML_COMMENT_NODE:
! 6255: case XML_PI_NODE:
! 6256: case XML_CDATA_SECTION_NODE:
! 6257: case XML_TEXT_NODE:
! 6258: string = tmp->content;
! 6259: break;
! 6260: case XML_NAMESPACE_DECL:
! 6261: string = ((xmlNsPtr)tmp)->href;
! 6262: break;
! 6263: default:
! 6264: break;
! 6265: }
! 6266: if ((string != NULL) && (string[0] != 0)) {
! 6267: if (len == 1) {
! 6268: return(ret + (((unsigned int) string[0]) << 8));
! 6269: }
! 6270: if (string[1] == 0) {
! 6271: len = 1;
! 6272: ret = (unsigned int) string[0];
! 6273: } else {
! 6274: return(((unsigned int) string[0]) +
! 6275: (((unsigned int) string[1]) << 8));
! 6276: }
! 6277: }
! 6278: /*
! 6279: * Skip to next node
! 6280: */
! 6281: if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
! 6282: if (tmp->children->type != XML_ENTITY_DECL) {
! 6283: tmp = tmp->children;
! 6284: continue;
! 6285: }
! 6286: }
! 6287: if (tmp == node)
! 6288: break;
! 6289:
! 6290: if (tmp->next != NULL) {
! 6291: tmp = tmp->next;
! 6292: continue;
! 6293: }
! 6294:
! 6295: do {
! 6296: tmp = tmp->parent;
! 6297: if (tmp == NULL)
! 6298: break;
! 6299: if (tmp == node) {
! 6300: tmp = NULL;
! 6301: break;
! 6302: }
! 6303: if (tmp->next != NULL) {
! 6304: tmp = tmp->next;
! 6305: break;
! 6306: }
! 6307: } while (tmp != NULL);
! 6308: }
! 6309: return(ret);
! 6310: }
! 6311:
! 6312: /**
! 6313: * xmlXPathStringHash:
! 6314: * @string: a string
! 6315: *
! 6316: * Function computing the beginning of the string value of the node,
! 6317: * used to speed up comparisons
! 6318: *
! 6319: * Returns an int usable as a hash
! 6320: */
! 6321: static unsigned int
! 6322: xmlXPathStringHash(const xmlChar * string) {
! 6323: if (string == NULL)
! 6324: return((unsigned int) 0);
! 6325: if (string[0] == 0)
! 6326: return(0);
! 6327: return(((unsigned int) string[0]) +
! 6328: (((unsigned int) string[1]) << 8));
! 6329: }
! 6330:
! 6331: /**
! 6332: * xmlXPathCompareNodeSetFloat:
! 6333: * @ctxt: the XPath Parser context
! 6334: * @inf: less than (1) or greater than (0)
! 6335: * @strict: is the comparison strict
! 6336: * @arg: the node set
! 6337: * @f: the value
! 6338: *
! 6339: * Implement the compare operation between a nodeset and a number
! 6340: * @ns < @val (1, 1, ...
! 6341: * @ns <= @val (1, 0, ...
! 6342: * @ns > @val (0, 1, ...
! 6343: * @ns >= @val (0, 0, ...
! 6344: *
! 6345: * If one object to be compared is a node-set and the other is a number,
! 6346: * then the comparison will be true if and only if there is a node in the
! 6347: * node-set such that the result of performing the comparison on the number
! 6348: * to be compared and on the result of converting the string-value of that
! 6349: * node to a number using the number function is true.
! 6350: *
! 6351: * Returns 0 or 1 depending on the results of the test.
! 6352: */
! 6353: static int
! 6354: xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
! 6355: xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
! 6356: int i, ret = 0;
! 6357: xmlNodeSetPtr ns;
! 6358: xmlChar *str2;
! 6359:
! 6360: if ((f == NULL) || (arg == NULL) ||
! 6361: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
! 6362: xmlXPathReleaseObject(ctxt->context, arg);
! 6363: xmlXPathReleaseObject(ctxt->context, f);
! 6364: return(0);
! 6365: }
! 6366: ns = arg->nodesetval;
! 6367: if (ns != NULL) {
! 6368: for (i = 0;i < ns->nodeNr;i++) {
! 6369: str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
! 6370: if (str2 != NULL) {
! 6371: valuePush(ctxt,
! 6372: xmlXPathCacheNewString(ctxt->context, str2));
! 6373: xmlFree(str2);
! 6374: xmlXPathNumberFunction(ctxt, 1);
! 6375: valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, f));
! 6376: ret = xmlXPathCompareValues(ctxt, inf, strict);
! 6377: if (ret)
! 6378: break;
! 6379: }
! 6380: }
! 6381: }
! 6382: xmlXPathReleaseObject(ctxt->context, arg);
! 6383: xmlXPathReleaseObject(ctxt->context, f);
! 6384: return(ret);
! 6385: }
! 6386:
! 6387: /**
! 6388: * xmlXPathCompareNodeSetString:
! 6389: * @ctxt: the XPath Parser context
! 6390: * @inf: less than (1) or greater than (0)
! 6391: * @strict: is the comparison strict
! 6392: * @arg: the node set
! 6393: * @s: the value
! 6394: *
! 6395: * Implement the compare operation between a nodeset and a string
! 6396: * @ns < @val (1, 1, ...
! 6397: * @ns <= @val (1, 0, ...
! 6398: * @ns > @val (0, 1, ...
! 6399: * @ns >= @val (0, 0, ...
! 6400: *
! 6401: * If one object to be compared is a node-set and the other is a string,
! 6402: * then the comparison will be true if and only if there is a node in
! 6403: * the node-set such that the result of performing the comparison on the
! 6404: * string-value of the node and the other string is true.
! 6405: *
! 6406: * Returns 0 or 1 depending on the results of the test.
! 6407: */
! 6408: static int
! 6409: xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
! 6410: xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
! 6411: int i, ret = 0;
! 6412: xmlNodeSetPtr ns;
! 6413: xmlChar *str2;
! 6414:
! 6415: if ((s == NULL) || (arg == NULL) ||
! 6416: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
! 6417: xmlXPathReleaseObject(ctxt->context, arg);
! 6418: xmlXPathReleaseObject(ctxt->context, s);
! 6419: return(0);
! 6420: }
! 6421: ns = arg->nodesetval;
! 6422: if (ns != NULL) {
! 6423: for (i = 0;i < ns->nodeNr;i++) {
! 6424: str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
! 6425: if (str2 != NULL) {
! 6426: valuePush(ctxt,
! 6427: xmlXPathCacheNewString(ctxt->context, str2));
! 6428: xmlFree(str2);
! 6429: valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, s));
! 6430: ret = xmlXPathCompareValues(ctxt, inf, strict);
! 6431: if (ret)
! 6432: break;
! 6433: }
! 6434: }
! 6435: }
! 6436: xmlXPathReleaseObject(ctxt->context, arg);
! 6437: xmlXPathReleaseObject(ctxt->context, s);
! 6438: return(ret);
! 6439: }
! 6440:
! 6441: /**
! 6442: * xmlXPathCompareNodeSets:
! 6443: * @inf: less than (1) or greater than (0)
! 6444: * @strict: is the comparison strict
! 6445: * @arg1: the first node set object
! 6446: * @arg2: the second node set object
! 6447: *
! 6448: * Implement the compare operation on nodesets:
! 6449: *
! 6450: * If both objects to be compared are node-sets, then the comparison
! 6451: * will be true if and only if there is a node in the first node-set
! 6452: * and a node in the second node-set such that the result of performing
! 6453: * the comparison on the string-values of the two nodes is true.
! 6454: * ....
! 6455: * When neither object to be compared is a node-set and the operator
! 6456: * is <=, <, >= or >, then the objects are compared by converting both
! 6457: * objects to numbers and comparing the numbers according to IEEE 754.
! 6458: * ....
! 6459: * The number function converts its argument to a number as follows:
! 6460: * - a string that consists of optional whitespace followed by an
! 6461: * optional minus sign followed by a Number followed by whitespace
! 6462: * is converted to the IEEE 754 number that is nearest (according
! 6463: * to the IEEE 754 round-to-nearest rule) to the mathematical value
! 6464: * represented by the string; any other string is converted to NaN
! 6465: *
! 6466: * Conclusion all nodes need to be converted first to their string value
! 6467: * and then the comparison must be done when possible
! 6468: */
! 6469: static int
! 6470: xmlXPathCompareNodeSets(int inf, int strict,
! 6471: xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
! 6472: int i, j, init = 0;
! 6473: double val1;
! 6474: double *values2;
! 6475: int ret = 0;
! 6476: xmlNodeSetPtr ns1;
! 6477: xmlNodeSetPtr ns2;
! 6478:
! 6479: if ((arg1 == NULL) ||
! 6480: ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
! 6481: xmlXPathFreeObject(arg2);
! 6482: return(0);
! 6483: }
! 6484: if ((arg2 == NULL) ||
! 6485: ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
! 6486: xmlXPathFreeObject(arg1);
! 6487: xmlXPathFreeObject(arg2);
! 6488: return(0);
! 6489: }
! 6490:
! 6491: ns1 = arg1->nodesetval;
! 6492: ns2 = arg2->nodesetval;
! 6493:
! 6494: if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
! 6495: xmlXPathFreeObject(arg1);
! 6496: xmlXPathFreeObject(arg2);
! 6497: return(0);
! 6498: }
! 6499: if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
! 6500: xmlXPathFreeObject(arg1);
! 6501: xmlXPathFreeObject(arg2);
! 6502: return(0);
! 6503: }
! 6504:
! 6505: values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
! 6506: if (values2 == NULL) {
! 6507: xmlXPathErrMemory(NULL, "comparing nodesets\n");
! 6508: xmlXPathFreeObject(arg1);
! 6509: xmlXPathFreeObject(arg2);
! 6510: return(0);
! 6511: }
! 6512: for (i = 0;i < ns1->nodeNr;i++) {
! 6513: val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
! 6514: if (xmlXPathIsNaN(val1))
! 6515: continue;
! 6516: for (j = 0;j < ns2->nodeNr;j++) {
! 6517: if (init == 0) {
! 6518: values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
! 6519: }
! 6520: if (xmlXPathIsNaN(values2[j]))
! 6521: continue;
! 6522: if (inf && strict)
! 6523: ret = (val1 < values2[j]);
! 6524: else if (inf && !strict)
! 6525: ret = (val1 <= values2[j]);
! 6526: else if (!inf && strict)
! 6527: ret = (val1 > values2[j]);
! 6528: else if (!inf && !strict)
! 6529: ret = (val1 >= values2[j]);
! 6530: if (ret)
! 6531: break;
! 6532: }
! 6533: if (ret)
! 6534: break;
! 6535: init = 1;
! 6536: }
! 6537: xmlFree(values2);
! 6538: xmlXPathFreeObject(arg1);
! 6539: xmlXPathFreeObject(arg2);
! 6540: return(ret);
! 6541: }
! 6542:
! 6543: /**
! 6544: * xmlXPathCompareNodeSetValue:
! 6545: * @ctxt: the XPath Parser context
! 6546: * @inf: less than (1) or greater than (0)
! 6547: * @strict: is the comparison strict
! 6548: * @arg: the node set
! 6549: * @val: the value
! 6550: *
! 6551: * Implement the compare operation between a nodeset and a value
! 6552: * @ns < @val (1, 1, ...
! 6553: * @ns <= @val (1, 0, ...
! 6554: * @ns > @val (0, 1, ...
! 6555: * @ns >= @val (0, 0, ...
! 6556: *
! 6557: * If one object to be compared is a node-set and the other is a boolean,
! 6558: * then the comparison will be true if and only if the result of performing
! 6559: * the comparison on the boolean and on the result of converting
! 6560: * the node-set to a boolean using the boolean function is true.
! 6561: *
! 6562: * Returns 0 or 1 depending on the results of the test.
! 6563: */
! 6564: static int
! 6565: xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
! 6566: xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
! 6567: if ((val == NULL) || (arg == NULL) ||
! 6568: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
! 6569: return(0);
! 6570:
! 6571: switch(val->type) {
! 6572: case XPATH_NUMBER:
! 6573: return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
! 6574: case XPATH_NODESET:
! 6575: case XPATH_XSLT_TREE:
! 6576: return(xmlXPathCompareNodeSets(inf, strict, arg, val));
! 6577: case XPATH_STRING:
! 6578: return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
! 6579: case XPATH_BOOLEAN:
! 6580: valuePush(ctxt, arg);
! 6581: xmlXPathBooleanFunction(ctxt, 1);
! 6582: valuePush(ctxt, val);
! 6583: return(xmlXPathCompareValues(ctxt, inf, strict));
! 6584: default:
! 6585: TODO
! 6586: }
! 6587: return(0);
! 6588: }
! 6589:
! 6590: /**
! 6591: * xmlXPathEqualNodeSetString:
! 6592: * @arg: the nodeset object argument
! 6593: * @str: the string to compare to.
! 6594: * @neq: flag to show whether for '=' (0) or '!=' (1)
! 6595: *
! 6596: * Implement the equal operation on XPath objects content: @arg1 == @arg2
! 6597: * If one object to be compared is a node-set and the other is a string,
! 6598: * then the comparison will be true if and only if there is a node in
! 6599: * the node-set such that the result of performing the comparison on the
! 6600: * string-value of the node and the other string is true.
! 6601: *
! 6602: * Returns 0 or 1 depending on the results of the test.
! 6603: */
! 6604: static int
! 6605: xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str, int neq)
! 6606: {
! 6607: int i;
! 6608: xmlNodeSetPtr ns;
! 6609: xmlChar *str2;
! 6610: unsigned int hash;
! 6611:
! 6612: if ((str == NULL) || (arg == NULL) ||
! 6613: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
! 6614: return (0);
! 6615: ns = arg->nodesetval;
! 6616: /*
! 6617: * A NULL nodeset compared with a string is always false
! 6618: * (since there is no node equal, and no node not equal)
! 6619: */
! 6620: if ((ns == NULL) || (ns->nodeNr <= 0) )
! 6621: return (0);
! 6622: hash = xmlXPathStringHash(str);
! 6623: for (i = 0; i < ns->nodeNr; i++) {
! 6624: if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
! 6625: str2 = xmlNodeGetContent(ns->nodeTab[i]);
! 6626: if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
! 6627: xmlFree(str2);
! 6628: if (neq)
! 6629: continue;
! 6630: return (1);
! 6631: } else if ((str2 == NULL) && (xmlStrEqual(str, BAD_CAST ""))) {
! 6632: if (neq)
! 6633: continue;
! 6634: return (1);
! 6635: } else if (neq) {
! 6636: if (str2 != NULL)
! 6637: xmlFree(str2);
! 6638: return (1);
! 6639: }
! 6640: if (str2 != NULL)
! 6641: xmlFree(str2);
! 6642: } else if (neq)
! 6643: return (1);
! 6644: }
! 6645: return (0);
! 6646: }
! 6647:
! 6648: /**
! 6649: * xmlXPathEqualNodeSetFloat:
! 6650: * @arg: the nodeset object argument
! 6651: * @f: the float to compare to
! 6652: * @neq: flag to show whether to compare '=' (0) or '!=' (1)
! 6653: *
! 6654: * Implement the equal operation on XPath objects content: @arg1 == @arg2
! 6655: * If one object to be compared is a node-set and the other is a number,
! 6656: * then the comparison will be true if and only if there is a node in
! 6657: * the node-set such that the result of performing the comparison on the
! 6658: * number to be compared and on the result of converting the string-value
! 6659: * of that node to a number using the number function is true.
! 6660: *
! 6661: * Returns 0 or 1 depending on the results of the test.
! 6662: */
! 6663: static int
! 6664: xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt,
! 6665: xmlXPathObjectPtr arg, double f, int neq) {
! 6666: int i, ret=0;
! 6667: xmlNodeSetPtr ns;
! 6668: xmlChar *str2;
! 6669: xmlXPathObjectPtr val;
! 6670: double v;
! 6671:
! 6672: if ((arg == NULL) ||
! 6673: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
! 6674: return(0);
! 6675:
! 6676: ns = arg->nodesetval;
! 6677: if (ns != NULL) {
! 6678: for (i=0;i<ns->nodeNr;i++) {
! 6679: str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
! 6680: if (str2 != NULL) {
! 6681: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, str2));
! 6682: xmlFree(str2);
! 6683: xmlXPathNumberFunction(ctxt, 1);
! 6684: val = valuePop(ctxt);
! 6685: v = val->floatval;
! 6686: xmlXPathReleaseObject(ctxt->context, val);
! 6687: if (!xmlXPathIsNaN(v)) {
! 6688: if ((!neq) && (v==f)) {
! 6689: ret = 1;
! 6690: break;
! 6691: } else if ((neq) && (v!=f)) {
! 6692: ret = 1;
! 6693: break;
! 6694: }
! 6695: } else { /* NaN is unequal to any value */
! 6696: if (neq)
! 6697: ret = 1;
! 6698: }
! 6699: }
! 6700: }
! 6701: }
! 6702:
! 6703: return(ret);
! 6704: }
! 6705:
! 6706:
! 6707: /**
! 6708: * xmlXPathEqualNodeSets:
! 6709: * @arg1: first nodeset object argument
! 6710: * @arg2: second nodeset object argument
! 6711: * @neq: flag to show whether to test '=' (0) or '!=' (1)
! 6712: *
! 6713: * Implement the equal / not equal operation on XPath nodesets:
! 6714: * @arg1 == @arg2 or @arg1 != @arg2
! 6715: * If both objects to be compared are node-sets, then the comparison
! 6716: * will be true if and only if there is a node in the first node-set and
! 6717: * a node in the second node-set such that the result of performing the
! 6718: * comparison on the string-values of the two nodes is true.
! 6719: *
! 6720: * (needless to say, this is a costly operation)
! 6721: *
! 6722: * Returns 0 or 1 depending on the results of the test.
! 6723: */
! 6724: static int
! 6725: xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2, int neq) {
! 6726: int i, j;
! 6727: unsigned int *hashs1;
! 6728: unsigned int *hashs2;
! 6729: xmlChar **values1;
! 6730: xmlChar **values2;
! 6731: int ret = 0;
! 6732: xmlNodeSetPtr ns1;
! 6733: xmlNodeSetPtr ns2;
! 6734:
! 6735: if ((arg1 == NULL) ||
! 6736: ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
! 6737: return(0);
! 6738: if ((arg2 == NULL) ||
! 6739: ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
! 6740: return(0);
! 6741:
! 6742: ns1 = arg1->nodesetval;
! 6743: ns2 = arg2->nodesetval;
! 6744:
! 6745: if ((ns1 == NULL) || (ns1->nodeNr <= 0))
! 6746: return(0);
! 6747: if ((ns2 == NULL) || (ns2->nodeNr <= 0))
! 6748: return(0);
! 6749:
! 6750: /*
! 6751: * for equal, check if there is a node pertaining to both sets
! 6752: */
! 6753: if (neq == 0)
! 6754: for (i = 0;i < ns1->nodeNr;i++)
! 6755: for (j = 0;j < ns2->nodeNr;j++)
! 6756: if (ns1->nodeTab[i] == ns2->nodeTab[j])
! 6757: return(1);
! 6758:
! 6759: values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
! 6760: if (values1 == NULL) {
! 6761: xmlXPathErrMemory(NULL, "comparing nodesets\n");
! 6762: return(0);
! 6763: }
! 6764: hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
! 6765: if (hashs1 == NULL) {
! 6766: xmlXPathErrMemory(NULL, "comparing nodesets\n");
! 6767: xmlFree(values1);
! 6768: return(0);
! 6769: }
! 6770: memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
! 6771: values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
! 6772: if (values2 == NULL) {
! 6773: xmlXPathErrMemory(NULL, "comparing nodesets\n");
! 6774: xmlFree(hashs1);
! 6775: xmlFree(values1);
! 6776: return(0);
! 6777: }
! 6778: hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
! 6779: if (hashs2 == NULL) {
! 6780: xmlXPathErrMemory(NULL, "comparing nodesets\n");
! 6781: xmlFree(hashs1);
! 6782: xmlFree(values1);
! 6783: xmlFree(values2);
! 6784: return(0);
! 6785: }
! 6786: memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
! 6787: for (i = 0;i < ns1->nodeNr;i++) {
! 6788: hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
! 6789: for (j = 0;j < ns2->nodeNr;j++) {
! 6790: if (i == 0)
! 6791: hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
! 6792: if (hashs1[i] != hashs2[j]) {
! 6793: if (neq) {
! 6794: ret = 1;
! 6795: break;
! 6796: }
! 6797: }
! 6798: else {
! 6799: if (values1[i] == NULL)
! 6800: values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
! 6801: if (values2[j] == NULL)
! 6802: values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
! 6803: ret = xmlStrEqual(values1[i], values2[j]) ^ neq;
! 6804: if (ret)
! 6805: break;
! 6806: }
! 6807: }
! 6808: if (ret)
! 6809: break;
! 6810: }
! 6811: for (i = 0;i < ns1->nodeNr;i++)
! 6812: if (values1[i] != NULL)
! 6813: xmlFree(values1[i]);
! 6814: for (j = 0;j < ns2->nodeNr;j++)
! 6815: if (values2[j] != NULL)
! 6816: xmlFree(values2[j]);
! 6817: xmlFree(values1);
! 6818: xmlFree(values2);
! 6819: xmlFree(hashs1);
! 6820: xmlFree(hashs2);
! 6821: return(ret);
! 6822: }
! 6823:
! 6824: static int
! 6825: xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt,
! 6826: xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
! 6827: int ret = 0;
! 6828: /*
! 6829: *At this point we are assured neither arg1 nor arg2
! 6830: *is a nodeset, so we can just pick the appropriate routine.
! 6831: */
! 6832: switch (arg1->type) {
! 6833: case XPATH_UNDEFINED:
! 6834: #ifdef DEBUG_EXPR
! 6835: xmlGenericError(xmlGenericErrorContext,
! 6836: "Equal: undefined\n");
! 6837: #endif
! 6838: break;
! 6839: case XPATH_BOOLEAN:
! 6840: switch (arg2->type) {
! 6841: case XPATH_UNDEFINED:
! 6842: #ifdef DEBUG_EXPR
! 6843: xmlGenericError(xmlGenericErrorContext,
! 6844: "Equal: undefined\n");
! 6845: #endif
! 6846: break;
! 6847: case XPATH_BOOLEAN:
! 6848: #ifdef DEBUG_EXPR
! 6849: xmlGenericError(xmlGenericErrorContext,
! 6850: "Equal: %d boolean %d \n",
! 6851: arg1->boolval, arg2->boolval);
! 6852: #endif
! 6853: ret = (arg1->boolval == arg2->boolval);
! 6854: break;
! 6855: case XPATH_NUMBER:
! 6856: ret = (arg1->boolval ==
! 6857: xmlXPathCastNumberToBoolean(arg2->floatval));
! 6858: break;
! 6859: case XPATH_STRING:
! 6860: if ((arg2->stringval == NULL) ||
! 6861: (arg2->stringval[0] == 0)) ret = 0;
! 6862: else
! 6863: ret = 1;
! 6864: ret = (arg1->boolval == ret);
! 6865: break;
! 6866: case XPATH_USERS:
! 6867: case XPATH_POINT:
! 6868: case XPATH_RANGE:
! 6869: case XPATH_LOCATIONSET:
! 6870: TODO
! 6871: break;
! 6872: case XPATH_NODESET:
! 6873: case XPATH_XSLT_TREE:
! 6874: break;
! 6875: }
! 6876: break;
! 6877: case XPATH_NUMBER:
! 6878: switch (arg2->type) {
! 6879: case XPATH_UNDEFINED:
! 6880: #ifdef DEBUG_EXPR
! 6881: xmlGenericError(xmlGenericErrorContext,
! 6882: "Equal: undefined\n");
! 6883: #endif
! 6884: break;
! 6885: case XPATH_BOOLEAN:
! 6886: ret = (arg2->boolval==
! 6887: xmlXPathCastNumberToBoolean(arg1->floatval));
! 6888: break;
! 6889: case XPATH_STRING:
! 6890: valuePush(ctxt, arg2);
! 6891: xmlXPathNumberFunction(ctxt, 1);
! 6892: arg2 = valuePop(ctxt);
! 6893: /* no break on purpose */
! 6894: case XPATH_NUMBER:
! 6895: /* Hand check NaN and Infinity equalities */
! 6896: if (xmlXPathIsNaN(arg1->floatval) ||
! 6897: xmlXPathIsNaN(arg2->floatval)) {
! 6898: ret = 0;
! 6899: } else if (xmlXPathIsInf(arg1->floatval) == 1) {
! 6900: if (xmlXPathIsInf(arg2->floatval) == 1)
! 6901: ret = 1;
! 6902: else
! 6903: ret = 0;
! 6904: } else if (xmlXPathIsInf(arg1->floatval) == -1) {
! 6905: if (xmlXPathIsInf(arg2->floatval) == -1)
! 6906: ret = 1;
! 6907: else
! 6908: ret = 0;
! 6909: } else if (xmlXPathIsInf(arg2->floatval) == 1) {
! 6910: if (xmlXPathIsInf(arg1->floatval) == 1)
! 6911: ret = 1;
! 6912: else
! 6913: ret = 0;
! 6914: } else if (xmlXPathIsInf(arg2->floatval) == -1) {
! 6915: if (xmlXPathIsInf(arg1->floatval) == -1)
! 6916: ret = 1;
! 6917: else
! 6918: ret = 0;
! 6919: } else {
! 6920: ret = (arg1->floatval == arg2->floatval);
! 6921: }
! 6922: break;
! 6923: case XPATH_USERS:
! 6924: case XPATH_POINT:
! 6925: case XPATH_RANGE:
! 6926: case XPATH_LOCATIONSET:
! 6927: TODO
! 6928: break;
! 6929: case XPATH_NODESET:
! 6930: case XPATH_XSLT_TREE:
! 6931: break;
! 6932: }
! 6933: break;
! 6934: case XPATH_STRING:
! 6935: switch (arg2->type) {
! 6936: case XPATH_UNDEFINED:
! 6937: #ifdef DEBUG_EXPR
! 6938: xmlGenericError(xmlGenericErrorContext,
! 6939: "Equal: undefined\n");
! 6940: #endif
! 6941: break;
! 6942: case XPATH_BOOLEAN:
! 6943: if ((arg1->stringval == NULL) ||
! 6944: (arg1->stringval[0] == 0)) ret = 0;
! 6945: else
! 6946: ret = 1;
! 6947: ret = (arg2->boolval == ret);
! 6948: break;
! 6949: case XPATH_STRING:
! 6950: ret = xmlStrEqual(arg1->stringval, arg2->stringval);
! 6951: break;
! 6952: case XPATH_NUMBER:
! 6953: valuePush(ctxt, arg1);
! 6954: xmlXPathNumberFunction(ctxt, 1);
! 6955: arg1 = valuePop(ctxt);
! 6956: /* Hand check NaN and Infinity equalities */
! 6957: if (xmlXPathIsNaN(arg1->floatval) ||
! 6958: xmlXPathIsNaN(arg2->floatval)) {
! 6959: ret = 0;
! 6960: } else if (xmlXPathIsInf(arg1->floatval) == 1) {
! 6961: if (xmlXPathIsInf(arg2->floatval) == 1)
! 6962: ret = 1;
! 6963: else
! 6964: ret = 0;
! 6965: } else if (xmlXPathIsInf(arg1->floatval) == -1) {
! 6966: if (xmlXPathIsInf(arg2->floatval) == -1)
! 6967: ret = 1;
! 6968: else
! 6969: ret = 0;
! 6970: } else if (xmlXPathIsInf(arg2->floatval) == 1) {
! 6971: if (xmlXPathIsInf(arg1->floatval) == 1)
! 6972: ret = 1;
! 6973: else
! 6974: ret = 0;
! 6975: } else if (xmlXPathIsInf(arg2->floatval) == -1) {
! 6976: if (xmlXPathIsInf(arg1->floatval) == -1)
! 6977: ret = 1;
! 6978: else
! 6979: ret = 0;
! 6980: } else {
! 6981: ret = (arg1->floatval == arg2->floatval);
! 6982: }
! 6983: break;
! 6984: case XPATH_USERS:
! 6985: case XPATH_POINT:
! 6986: case XPATH_RANGE:
! 6987: case XPATH_LOCATIONSET:
! 6988: TODO
! 6989: break;
! 6990: case XPATH_NODESET:
! 6991: case XPATH_XSLT_TREE:
! 6992: break;
! 6993: }
! 6994: break;
! 6995: case XPATH_USERS:
! 6996: case XPATH_POINT:
! 6997: case XPATH_RANGE:
! 6998: case XPATH_LOCATIONSET:
! 6999: TODO
! 7000: break;
! 7001: case XPATH_NODESET:
! 7002: case XPATH_XSLT_TREE:
! 7003: break;
! 7004: }
! 7005: xmlXPathReleaseObject(ctxt->context, arg1);
! 7006: xmlXPathReleaseObject(ctxt->context, arg2);
! 7007: return(ret);
! 7008: }
! 7009:
! 7010: /**
! 7011: * xmlXPathEqualValues:
! 7012: * @ctxt: the XPath Parser context
! 7013: *
! 7014: * Implement the equal operation on XPath objects content: @arg1 == @arg2
! 7015: *
! 7016: * Returns 0 or 1 depending on the results of the test.
! 7017: */
! 7018: int
! 7019: xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
! 7020: xmlXPathObjectPtr arg1, arg2, argtmp;
! 7021: int ret = 0;
! 7022:
! 7023: if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
! 7024: arg2 = valuePop(ctxt);
! 7025: arg1 = valuePop(ctxt);
! 7026: if ((arg1 == NULL) || (arg2 == NULL)) {
! 7027: if (arg1 != NULL)
! 7028: xmlXPathReleaseObject(ctxt->context, arg1);
! 7029: else
! 7030: xmlXPathReleaseObject(ctxt->context, arg2);
! 7031: XP_ERROR0(XPATH_INVALID_OPERAND);
! 7032: }
! 7033:
! 7034: if (arg1 == arg2) {
! 7035: #ifdef DEBUG_EXPR
! 7036: xmlGenericError(xmlGenericErrorContext,
! 7037: "Equal: by pointer\n");
! 7038: #endif
! 7039: xmlXPathFreeObject(arg1);
! 7040: return(1);
! 7041: }
! 7042:
! 7043: /*
! 7044: *If either argument is a nodeset, it's a 'special case'
! 7045: */
! 7046: if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
! 7047: (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
! 7048: /*
! 7049: *Hack it to assure arg1 is the nodeset
! 7050: */
! 7051: if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
! 7052: argtmp = arg2;
! 7053: arg2 = arg1;
! 7054: arg1 = argtmp;
! 7055: }
! 7056: switch (arg2->type) {
! 7057: case XPATH_UNDEFINED:
! 7058: #ifdef DEBUG_EXPR
! 7059: xmlGenericError(xmlGenericErrorContext,
! 7060: "Equal: undefined\n");
! 7061: #endif
! 7062: break;
! 7063: case XPATH_NODESET:
! 7064: case XPATH_XSLT_TREE:
! 7065: ret = xmlXPathEqualNodeSets(arg1, arg2, 0);
! 7066: break;
! 7067: case XPATH_BOOLEAN:
! 7068: if ((arg1->nodesetval == NULL) ||
! 7069: (arg1->nodesetval->nodeNr == 0)) ret = 0;
! 7070: else
! 7071: ret = 1;
! 7072: ret = (ret == arg2->boolval);
! 7073: break;
! 7074: case XPATH_NUMBER:
! 7075: ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 0);
! 7076: break;
! 7077: case XPATH_STRING:
! 7078: ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0);
! 7079: break;
! 7080: case XPATH_USERS:
! 7081: case XPATH_POINT:
! 7082: case XPATH_RANGE:
! 7083: case XPATH_LOCATIONSET:
! 7084: TODO
! 7085: break;
! 7086: }
! 7087: xmlXPathReleaseObject(ctxt->context, arg1);
! 7088: xmlXPathReleaseObject(ctxt->context, arg2);
! 7089: return(ret);
! 7090: }
! 7091:
! 7092: return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
! 7093: }
! 7094:
! 7095: /**
! 7096: * xmlXPathNotEqualValues:
! 7097: * @ctxt: the XPath Parser context
! 7098: *
! 7099: * Implement the equal operation on XPath objects content: @arg1 == @arg2
! 7100: *
! 7101: * Returns 0 or 1 depending on the results of the test.
! 7102: */
! 7103: int
! 7104: xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
! 7105: xmlXPathObjectPtr arg1, arg2, argtmp;
! 7106: int ret = 0;
! 7107:
! 7108: if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
! 7109: arg2 = valuePop(ctxt);
! 7110: arg1 = valuePop(ctxt);
! 7111: if ((arg1 == NULL) || (arg2 == NULL)) {
! 7112: if (arg1 != NULL)
! 7113: xmlXPathReleaseObject(ctxt->context, arg1);
! 7114: else
! 7115: xmlXPathReleaseObject(ctxt->context, arg2);
! 7116: XP_ERROR0(XPATH_INVALID_OPERAND);
! 7117: }
! 7118:
! 7119: if (arg1 == arg2) {
! 7120: #ifdef DEBUG_EXPR
! 7121: xmlGenericError(xmlGenericErrorContext,
! 7122: "NotEqual: by pointer\n");
! 7123: #endif
! 7124: xmlXPathReleaseObject(ctxt->context, arg1);
! 7125: return(0);
! 7126: }
! 7127:
! 7128: /*
! 7129: *If either argument is a nodeset, it's a 'special case'
! 7130: */
! 7131: if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
! 7132: (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
! 7133: /*
! 7134: *Hack it to assure arg1 is the nodeset
! 7135: */
! 7136: if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
! 7137: argtmp = arg2;
! 7138: arg2 = arg1;
! 7139: arg1 = argtmp;
! 7140: }
! 7141: switch (arg2->type) {
! 7142: case XPATH_UNDEFINED:
! 7143: #ifdef DEBUG_EXPR
! 7144: xmlGenericError(xmlGenericErrorContext,
! 7145: "NotEqual: undefined\n");
! 7146: #endif
! 7147: break;
! 7148: case XPATH_NODESET:
! 7149: case XPATH_XSLT_TREE:
! 7150: ret = xmlXPathEqualNodeSets(arg1, arg2, 1);
! 7151: break;
! 7152: case XPATH_BOOLEAN:
! 7153: if ((arg1->nodesetval == NULL) ||
! 7154: (arg1->nodesetval->nodeNr == 0)) ret = 0;
! 7155: else
! 7156: ret = 1;
! 7157: ret = (ret != arg2->boolval);
! 7158: break;
! 7159: case XPATH_NUMBER:
! 7160: ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 1);
! 7161: break;
! 7162: case XPATH_STRING:
! 7163: ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1);
! 7164: break;
! 7165: case XPATH_USERS:
! 7166: case XPATH_POINT:
! 7167: case XPATH_RANGE:
! 7168: case XPATH_LOCATIONSET:
! 7169: TODO
! 7170: break;
! 7171: }
! 7172: xmlXPathReleaseObject(ctxt->context, arg1);
! 7173: xmlXPathReleaseObject(ctxt->context, arg2);
! 7174: return(ret);
! 7175: }
! 7176:
! 7177: return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
! 7178: }
! 7179:
! 7180: /**
! 7181: * xmlXPathCompareValues:
! 7182: * @ctxt: the XPath Parser context
! 7183: * @inf: less than (1) or greater than (0)
! 7184: * @strict: is the comparison strict
! 7185: *
! 7186: * Implement the compare operation on XPath objects:
! 7187: * @arg1 < @arg2 (1, 1, ...
! 7188: * @arg1 <= @arg2 (1, 0, ...
! 7189: * @arg1 > @arg2 (0, 1, ...
! 7190: * @arg1 >= @arg2 (0, 0, ...
! 7191: *
! 7192: * When neither object to be compared is a node-set and the operator is
! 7193: * <=, <, >=, >, then the objects are compared by converted both objects
! 7194: * to numbers and comparing the numbers according to IEEE 754. The <
! 7195: * comparison will be true if and only if the first number is less than the
! 7196: * second number. The <= comparison will be true if and only if the first
! 7197: * number is less than or equal to the second number. The > comparison
! 7198: * will be true if and only if the first number is greater than the second
! 7199: * number. The >= comparison will be true if and only if the first number
! 7200: * is greater than or equal to the second number.
! 7201: *
! 7202: * Returns 1 if the comparison succeeded, 0 if it failed
! 7203: */
! 7204: int
! 7205: xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
! 7206: int ret = 0, arg1i = 0, arg2i = 0;
! 7207: xmlXPathObjectPtr arg1, arg2;
! 7208:
! 7209: if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
! 7210: arg2 = valuePop(ctxt);
! 7211: arg1 = valuePop(ctxt);
! 7212: if ((arg1 == NULL) || (arg2 == NULL)) {
! 7213: if (arg1 != NULL)
! 7214: xmlXPathReleaseObject(ctxt->context, arg1);
! 7215: else
! 7216: xmlXPathReleaseObject(ctxt->context, arg2);
! 7217: XP_ERROR0(XPATH_INVALID_OPERAND);
! 7218: }
! 7219:
! 7220: if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
! 7221: (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
! 7222: /*
! 7223: * If either argument is a XPATH_NODESET or XPATH_XSLT_TREE the two arguments
! 7224: * are not freed from within this routine; they will be freed from the
! 7225: * called routine, e.g. xmlXPathCompareNodeSets or xmlXPathCompareNodeSetValue
! 7226: */
! 7227: if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) &&
! 7228: ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){
! 7229: ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
! 7230: } else {
! 7231: if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
! 7232: ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
! 7233: arg1, arg2);
! 7234: } else {
! 7235: ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
! 7236: arg2, arg1);
! 7237: }
! 7238: }
! 7239: return(ret);
! 7240: }
! 7241:
! 7242: if (arg1->type != XPATH_NUMBER) {
! 7243: valuePush(ctxt, arg1);
! 7244: xmlXPathNumberFunction(ctxt, 1);
! 7245: arg1 = valuePop(ctxt);
! 7246: }
! 7247: if (arg1->type != XPATH_NUMBER) {
! 7248: xmlXPathFreeObject(arg1);
! 7249: xmlXPathFreeObject(arg2);
! 7250: XP_ERROR0(XPATH_INVALID_OPERAND);
! 7251: }
! 7252: if (arg2->type != XPATH_NUMBER) {
! 7253: valuePush(ctxt, arg2);
! 7254: xmlXPathNumberFunction(ctxt, 1);
! 7255: arg2 = valuePop(ctxt);
! 7256: }
! 7257: if (arg2->type != XPATH_NUMBER) {
! 7258: xmlXPathReleaseObject(ctxt->context, arg1);
! 7259: xmlXPathReleaseObject(ctxt->context, arg2);
! 7260: XP_ERROR0(XPATH_INVALID_OPERAND);
! 7261: }
! 7262: /*
! 7263: * Add tests for infinity and nan
! 7264: * => feedback on 3.4 for Inf and NaN
! 7265: */
! 7266: /* Hand check NaN and Infinity comparisons */
! 7267: if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) {
! 7268: ret=0;
! 7269: } else {
! 7270: arg1i=xmlXPathIsInf(arg1->floatval);
! 7271: arg2i=xmlXPathIsInf(arg2->floatval);
! 7272: if (inf && strict) {
! 7273: if ((arg1i == -1 && arg2i != -1) ||
! 7274: (arg2i == 1 && arg1i != 1)) {
! 7275: ret = 1;
! 7276: } else if (arg1i == 0 && arg2i == 0) {
! 7277: ret = (arg1->floatval < arg2->floatval);
! 7278: } else {
! 7279: ret = 0;
! 7280: }
! 7281: }
! 7282: else if (inf && !strict) {
! 7283: if (arg1i == -1 || arg2i == 1) {
! 7284: ret = 1;
! 7285: } else if (arg1i == 0 && arg2i == 0) {
! 7286: ret = (arg1->floatval <= arg2->floatval);
! 7287: } else {
! 7288: ret = 0;
! 7289: }
! 7290: }
! 7291: else if (!inf && strict) {
! 7292: if ((arg1i == 1 && arg2i != 1) ||
! 7293: (arg2i == -1 && arg1i != -1)) {
! 7294: ret = 1;
! 7295: } else if (arg1i == 0 && arg2i == 0) {
! 7296: ret = (arg1->floatval > arg2->floatval);
! 7297: } else {
! 7298: ret = 0;
! 7299: }
! 7300: }
! 7301: else if (!inf && !strict) {
! 7302: if (arg1i == 1 || arg2i == -1) {
! 7303: ret = 1;
! 7304: } else if (arg1i == 0 && arg2i == 0) {
! 7305: ret = (arg1->floatval >= arg2->floatval);
! 7306: } else {
! 7307: ret = 0;
! 7308: }
! 7309: }
! 7310: }
! 7311: xmlXPathReleaseObject(ctxt->context, arg1);
! 7312: xmlXPathReleaseObject(ctxt->context, arg2);
! 7313: return(ret);
! 7314: }
! 7315:
! 7316: /**
! 7317: * xmlXPathValueFlipSign:
! 7318: * @ctxt: the XPath Parser context
! 7319: *
! 7320: * Implement the unary - operation on an XPath object
! 7321: * The numeric operators convert their operands to numbers as if
! 7322: * by calling the number function.
! 7323: */
! 7324: void
! 7325: xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
! 7326: if ((ctxt == NULL) || (ctxt->context == NULL)) return;
! 7327: CAST_TO_NUMBER;
! 7328: CHECK_TYPE(XPATH_NUMBER);
! 7329: if (xmlXPathIsNaN(ctxt->value->floatval))
! 7330: ctxt->value->floatval=xmlXPathNAN;
! 7331: else if (xmlXPathIsInf(ctxt->value->floatval) == 1)
! 7332: ctxt->value->floatval=xmlXPathNINF;
! 7333: else if (xmlXPathIsInf(ctxt->value->floatval) == -1)
! 7334: ctxt->value->floatval=xmlXPathPINF;
! 7335: else if (ctxt->value->floatval == 0) {
! 7336: if (xmlXPathGetSign(ctxt->value->floatval) == 0)
! 7337: ctxt->value->floatval = xmlXPathNZERO;
! 7338: else
! 7339: ctxt->value->floatval = 0;
! 7340: }
! 7341: else
! 7342: ctxt->value->floatval = - ctxt->value->floatval;
! 7343: }
! 7344:
! 7345: /**
! 7346: * xmlXPathAddValues:
! 7347: * @ctxt: the XPath Parser context
! 7348: *
! 7349: * Implement the add operation on XPath objects:
! 7350: * The numeric operators convert their operands to numbers as if
! 7351: * by calling the number function.
! 7352: */
! 7353: void
! 7354: xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
! 7355: xmlXPathObjectPtr arg;
! 7356: double val;
! 7357:
! 7358: arg = valuePop(ctxt);
! 7359: if (arg == NULL)
! 7360: XP_ERROR(XPATH_INVALID_OPERAND);
! 7361: val = xmlXPathCastToNumber(arg);
! 7362: xmlXPathReleaseObject(ctxt->context, arg);
! 7363: CAST_TO_NUMBER;
! 7364: CHECK_TYPE(XPATH_NUMBER);
! 7365: ctxt->value->floatval += val;
! 7366: }
! 7367:
! 7368: /**
! 7369: * xmlXPathSubValues:
! 7370: * @ctxt: the XPath Parser context
! 7371: *
! 7372: * Implement the subtraction operation on XPath objects:
! 7373: * The numeric operators convert their operands to numbers as if
! 7374: * by calling the number function.
! 7375: */
! 7376: void
! 7377: xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
! 7378: xmlXPathObjectPtr arg;
! 7379: double val;
! 7380:
! 7381: arg = valuePop(ctxt);
! 7382: if (arg == NULL)
! 7383: XP_ERROR(XPATH_INVALID_OPERAND);
! 7384: val = xmlXPathCastToNumber(arg);
! 7385: xmlXPathReleaseObject(ctxt->context, arg);
! 7386: CAST_TO_NUMBER;
! 7387: CHECK_TYPE(XPATH_NUMBER);
! 7388: ctxt->value->floatval -= val;
! 7389: }
! 7390:
! 7391: /**
! 7392: * xmlXPathMultValues:
! 7393: * @ctxt: the XPath Parser context
! 7394: *
! 7395: * Implement the multiply operation on XPath objects:
! 7396: * The numeric operators convert their operands to numbers as if
! 7397: * by calling the number function.
! 7398: */
! 7399: void
! 7400: xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
! 7401: xmlXPathObjectPtr arg;
! 7402: double val;
! 7403:
! 7404: arg = valuePop(ctxt);
! 7405: if (arg == NULL)
! 7406: XP_ERROR(XPATH_INVALID_OPERAND);
! 7407: val = xmlXPathCastToNumber(arg);
! 7408: xmlXPathReleaseObject(ctxt->context, arg);
! 7409: CAST_TO_NUMBER;
! 7410: CHECK_TYPE(XPATH_NUMBER);
! 7411: ctxt->value->floatval *= val;
! 7412: }
! 7413:
! 7414: /**
! 7415: * xmlXPathDivValues:
! 7416: * @ctxt: the XPath Parser context
! 7417: *
! 7418: * Implement the div operation on XPath objects @arg1 / @arg2:
! 7419: * The numeric operators convert their operands to numbers as if
! 7420: * by calling the number function.
! 7421: */
! 7422: void
! 7423: xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
! 7424: xmlXPathObjectPtr arg;
! 7425: double val;
! 7426:
! 7427: arg = valuePop(ctxt);
! 7428: if (arg == NULL)
! 7429: XP_ERROR(XPATH_INVALID_OPERAND);
! 7430: val = xmlXPathCastToNumber(arg);
! 7431: xmlXPathReleaseObject(ctxt->context, arg);
! 7432: CAST_TO_NUMBER;
! 7433: CHECK_TYPE(XPATH_NUMBER);
! 7434: if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval))
! 7435: ctxt->value->floatval = xmlXPathNAN;
! 7436: else if (val == 0 && xmlXPathGetSign(val) != 0) {
! 7437: if (ctxt->value->floatval == 0)
! 7438: ctxt->value->floatval = xmlXPathNAN;
! 7439: else if (ctxt->value->floatval > 0)
! 7440: ctxt->value->floatval = xmlXPathNINF;
! 7441: else if (ctxt->value->floatval < 0)
! 7442: ctxt->value->floatval = xmlXPathPINF;
! 7443: }
! 7444: else if (val == 0) {
! 7445: if (ctxt->value->floatval == 0)
! 7446: ctxt->value->floatval = xmlXPathNAN;
! 7447: else if (ctxt->value->floatval > 0)
! 7448: ctxt->value->floatval = xmlXPathPINF;
! 7449: else if (ctxt->value->floatval < 0)
! 7450: ctxt->value->floatval = xmlXPathNINF;
! 7451: } else
! 7452: ctxt->value->floatval /= val;
! 7453: }
! 7454:
! 7455: /**
! 7456: * xmlXPathModValues:
! 7457: * @ctxt: the XPath Parser context
! 7458: *
! 7459: * Implement the mod operation on XPath objects: @arg1 / @arg2
! 7460: * The numeric operators convert their operands to numbers as if
! 7461: * by calling the number function.
! 7462: */
! 7463: void
! 7464: xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
! 7465: xmlXPathObjectPtr arg;
! 7466: double arg1, arg2;
! 7467:
! 7468: arg = valuePop(ctxt);
! 7469: if (arg == NULL)
! 7470: XP_ERROR(XPATH_INVALID_OPERAND);
! 7471: arg2 = xmlXPathCastToNumber(arg);
! 7472: xmlXPathReleaseObject(ctxt->context, arg);
! 7473: CAST_TO_NUMBER;
! 7474: CHECK_TYPE(XPATH_NUMBER);
! 7475: arg1 = ctxt->value->floatval;
! 7476: if (arg2 == 0)
! 7477: ctxt->value->floatval = xmlXPathNAN;
! 7478: else {
! 7479: ctxt->value->floatval = fmod(arg1, arg2);
! 7480: }
! 7481: }
! 7482:
! 7483: /************************************************************************
! 7484: * *
! 7485: * The traversal functions *
! 7486: * *
! 7487: ************************************************************************/
! 7488:
! 7489: /*
! 7490: * A traversal function enumerates nodes along an axis.
! 7491: * Initially it must be called with NULL, and it indicates
! 7492: * termination on the axis by returning NULL.
! 7493: */
! 7494: typedef xmlNodePtr (*xmlXPathTraversalFunction)
! 7495: (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
! 7496:
! 7497: /*
! 7498: * xmlXPathTraversalFunctionExt:
! 7499: * A traversal function enumerates nodes along an axis.
! 7500: * Initially it must be called with NULL, and it indicates
! 7501: * termination on the axis by returning NULL.
! 7502: * The context node of the traversal is specified via @contextNode.
! 7503: */
! 7504: typedef xmlNodePtr (*xmlXPathTraversalFunctionExt)
! 7505: (xmlNodePtr cur, xmlNodePtr contextNode);
! 7506:
! 7507: /*
! 7508: * xmlXPathNodeSetMergeFunction:
! 7509: * Used for merging node sets in xmlXPathCollectAndTest().
! 7510: */
! 7511: typedef xmlNodeSetPtr (*xmlXPathNodeSetMergeFunction)
! 7512: (xmlNodeSetPtr, xmlNodeSetPtr, int);
! 7513:
! 7514:
! 7515: /**
! 7516: * xmlXPathNextSelf:
! 7517: * @ctxt: the XPath Parser context
! 7518: * @cur: the current node in the traversal
! 7519: *
! 7520: * Traversal function for the "self" direction
! 7521: * The self axis contains just the context node itself
! 7522: *
! 7523: * Returns the next element following that axis
! 7524: */
! 7525: xmlNodePtr
! 7526: xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 7527: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 7528: if (cur == NULL)
! 7529: return(ctxt->context->node);
! 7530: return(NULL);
! 7531: }
! 7532:
! 7533: /**
! 7534: * xmlXPathNextChild:
! 7535: * @ctxt: the XPath Parser context
! 7536: * @cur: the current node in the traversal
! 7537: *
! 7538: * Traversal function for the "child" direction
! 7539: * The child axis contains the children of the context node in document order.
! 7540: *
! 7541: * Returns the next element following that axis
! 7542: */
! 7543: xmlNodePtr
! 7544: xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 7545: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 7546: if (cur == NULL) {
! 7547: if (ctxt->context->node == NULL) return(NULL);
! 7548: switch (ctxt->context->node->type) {
! 7549: case XML_ELEMENT_NODE:
! 7550: case XML_TEXT_NODE:
! 7551: case XML_CDATA_SECTION_NODE:
! 7552: case XML_ENTITY_REF_NODE:
! 7553: case XML_ENTITY_NODE:
! 7554: case XML_PI_NODE:
! 7555: case XML_COMMENT_NODE:
! 7556: case XML_NOTATION_NODE:
! 7557: case XML_DTD_NODE:
! 7558: return(ctxt->context->node->children);
! 7559: case XML_DOCUMENT_NODE:
! 7560: case XML_DOCUMENT_TYPE_NODE:
! 7561: case XML_DOCUMENT_FRAG_NODE:
! 7562: case XML_HTML_DOCUMENT_NODE:
! 7563: #ifdef LIBXML_DOCB_ENABLED
! 7564: case XML_DOCB_DOCUMENT_NODE:
! 7565: #endif
! 7566: return(((xmlDocPtr) ctxt->context->node)->children);
! 7567: case XML_ELEMENT_DECL:
! 7568: case XML_ATTRIBUTE_DECL:
! 7569: case XML_ENTITY_DECL:
! 7570: case XML_ATTRIBUTE_NODE:
! 7571: case XML_NAMESPACE_DECL:
! 7572: case XML_XINCLUDE_START:
! 7573: case XML_XINCLUDE_END:
! 7574: return(NULL);
! 7575: }
! 7576: return(NULL);
! 7577: }
! 7578: if ((cur->type == XML_DOCUMENT_NODE) ||
! 7579: (cur->type == XML_HTML_DOCUMENT_NODE))
! 7580: return(NULL);
! 7581: return(cur->next);
! 7582: }
! 7583:
! 7584: /**
! 7585: * xmlXPathNextChildElement:
! 7586: * @ctxt: the XPath Parser context
! 7587: * @cur: the current node in the traversal
! 7588: *
! 7589: * Traversal function for the "child" direction and nodes of type element.
! 7590: * The child axis contains the children of the context node in document order.
! 7591: *
! 7592: * Returns the next element following that axis
! 7593: */
! 7594: static xmlNodePtr
! 7595: xmlXPathNextChildElement(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 7596: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 7597: if (cur == NULL) {
! 7598: cur = ctxt->context->node;
! 7599: if (cur == NULL) return(NULL);
! 7600: /*
! 7601: * Get the first element child.
! 7602: */
! 7603: switch (cur->type) {
! 7604: case XML_ELEMENT_NODE:
! 7605: case XML_DOCUMENT_FRAG_NODE:
! 7606: case XML_ENTITY_REF_NODE: /* URGENT TODO: entify-refs as well? */
! 7607: case XML_ENTITY_NODE:
! 7608: cur = cur->children;
! 7609: if (cur != NULL) {
! 7610: if (cur->type == XML_ELEMENT_NODE)
! 7611: return(cur);
! 7612: do {
! 7613: cur = cur->next;
! 7614: } while ((cur != NULL) &&
! 7615: (cur->type != XML_ELEMENT_NODE));
! 7616: return(cur);
! 7617: }
! 7618: return(NULL);
! 7619: case XML_DOCUMENT_NODE:
! 7620: case XML_HTML_DOCUMENT_NODE:
! 7621: #ifdef LIBXML_DOCB_ENABLED
! 7622: case XML_DOCB_DOCUMENT_NODE:
! 7623: #endif
! 7624: return(xmlDocGetRootElement((xmlDocPtr) cur));
! 7625: default:
! 7626: return(NULL);
! 7627: }
! 7628: return(NULL);
! 7629: }
! 7630: /*
! 7631: * Get the next sibling element node.
! 7632: */
! 7633: switch (cur->type) {
! 7634: case XML_ELEMENT_NODE:
! 7635: case XML_TEXT_NODE:
! 7636: case XML_ENTITY_REF_NODE:
! 7637: case XML_ENTITY_NODE:
! 7638: case XML_CDATA_SECTION_NODE:
! 7639: case XML_PI_NODE:
! 7640: case XML_COMMENT_NODE:
! 7641: case XML_XINCLUDE_END:
! 7642: break;
! 7643: /* case XML_DTD_NODE: */ /* URGENT TODO: DTD-node as well? */
! 7644: default:
! 7645: return(NULL);
! 7646: }
! 7647: if (cur->next != NULL) {
! 7648: if (cur->next->type == XML_ELEMENT_NODE)
! 7649: return(cur->next);
! 7650: cur = cur->next;
! 7651: do {
! 7652: cur = cur->next;
! 7653: } while ((cur != NULL) && (cur->type != XML_ELEMENT_NODE));
! 7654: return(cur);
! 7655: }
! 7656: return(NULL);
! 7657: }
! 7658:
! 7659: /**
! 7660: * xmlXPathNextDescendantOrSelfElemParent:
! 7661: * @ctxt: the XPath Parser context
! 7662: * @cur: the current node in the traversal
! 7663: *
! 7664: * Traversal function for the "descendant-or-self" axis.
! 7665: * Additionally it returns only nodes which can be parents of
! 7666: * element nodes.
! 7667: *
! 7668: *
! 7669: * Returns the next element following that axis
! 7670: */
! 7671: static xmlNodePtr
! 7672: xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur,
! 7673: xmlNodePtr contextNode)
! 7674: {
! 7675: if (cur == NULL) {
! 7676: if (contextNode == NULL)
! 7677: return(NULL);
! 7678: switch (contextNode->type) {
! 7679: case XML_ELEMENT_NODE:
! 7680: case XML_XINCLUDE_START:
! 7681: case XML_DOCUMENT_FRAG_NODE:
! 7682: case XML_DOCUMENT_NODE:
! 7683: #ifdef LIBXML_DOCB_ENABLED
! 7684: case XML_DOCB_DOCUMENT_NODE:
! 7685: #endif
! 7686: case XML_HTML_DOCUMENT_NODE:
! 7687: return(contextNode);
! 7688: default:
! 7689: return(NULL);
! 7690: }
! 7691: return(NULL);
! 7692: } else {
! 7693: xmlNodePtr start = cur;
! 7694:
! 7695: while (cur != NULL) {
! 7696: switch (cur->type) {
! 7697: case XML_ELEMENT_NODE:
! 7698: /* TODO: OK to have XInclude here? */
! 7699: case XML_XINCLUDE_START:
! 7700: case XML_DOCUMENT_FRAG_NODE:
! 7701: if (cur != start)
! 7702: return(cur);
! 7703: if (cur->children != NULL) {
! 7704: cur = cur->children;
! 7705: continue;
! 7706: }
! 7707: break;
! 7708: /* Not sure if we need those here. */
! 7709: case XML_DOCUMENT_NODE:
! 7710: #ifdef LIBXML_DOCB_ENABLED
! 7711: case XML_DOCB_DOCUMENT_NODE:
! 7712: #endif
! 7713: case XML_HTML_DOCUMENT_NODE:
! 7714: if (cur != start)
! 7715: return(cur);
! 7716: return(xmlDocGetRootElement((xmlDocPtr) cur));
! 7717: default:
! 7718: break;
! 7719: }
! 7720:
! 7721: next_sibling:
! 7722: if ((cur == NULL) || (cur == contextNode))
! 7723: return(NULL);
! 7724: if (cur->next != NULL) {
! 7725: cur = cur->next;
! 7726: } else {
! 7727: cur = cur->parent;
! 7728: goto next_sibling;
! 7729: }
! 7730: }
! 7731: }
! 7732: return(NULL);
! 7733: }
! 7734:
! 7735: /**
! 7736: * xmlXPathNextDescendant:
! 7737: * @ctxt: the XPath Parser context
! 7738: * @cur: the current node in the traversal
! 7739: *
! 7740: * Traversal function for the "descendant" direction
! 7741: * the descendant axis contains the descendants of the context node in document
! 7742: * order; a descendant is a child or a child of a child and so on.
! 7743: *
! 7744: * Returns the next element following that axis
! 7745: */
! 7746: xmlNodePtr
! 7747: xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 7748: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 7749: if (cur == NULL) {
! 7750: if (ctxt->context->node == NULL)
! 7751: return(NULL);
! 7752: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
! 7753: (ctxt->context->node->type == XML_NAMESPACE_DECL))
! 7754: return(NULL);
! 7755:
! 7756: if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
! 7757: return(ctxt->context->doc->children);
! 7758: return(ctxt->context->node->children);
! 7759: }
! 7760:
! 7761: if (cur->children != NULL) {
! 7762: /*
! 7763: * Do not descend on entities declarations
! 7764: */
! 7765: if (cur->children->type != XML_ENTITY_DECL) {
! 7766: cur = cur->children;
! 7767: /*
! 7768: * Skip DTDs
! 7769: */
! 7770: if (cur->type != XML_DTD_NODE)
! 7771: return(cur);
! 7772: }
! 7773: }
! 7774:
! 7775: if (cur == ctxt->context->node) return(NULL);
! 7776:
! 7777: while (cur->next != NULL) {
! 7778: cur = cur->next;
! 7779: if ((cur->type != XML_ENTITY_DECL) &&
! 7780: (cur->type != XML_DTD_NODE))
! 7781: return(cur);
! 7782: }
! 7783:
! 7784: do {
! 7785: cur = cur->parent;
! 7786: if (cur == NULL) break;
! 7787: if (cur == ctxt->context->node) return(NULL);
! 7788: if (cur->next != NULL) {
! 7789: cur = cur->next;
! 7790: return(cur);
! 7791: }
! 7792: } while (cur != NULL);
! 7793: return(cur);
! 7794: }
! 7795:
! 7796: /**
! 7797: * xmlXPathNextDescendantOrSelf:
! 7798: * @ctxt: the XPath Parser context
! 7799: * @cur: the current node in the traversal
! 7800: *
! 7801: * Traversal function for the "descendant-or-self" direction
! 7802: * the descendant-or-self axis contains the context node and the descendants
! 7803: * of the context node in document order; thus the context node is the first
! 7804: * node on the axis, and the first child of the context node is the second node
! 7805: * on the axis
! 7806: *
! 7807: * Returns the next element following that axis
! 7808: */
! 7809: xmlNodePtr
! 7810: xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 7811: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 7812: if (cur == NULL) {
! 7813: if (ctxt->context->node == NULL)
! 7814: return(NULL);
! 7815: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
! 7816: (ctxt->context->node->type == XML_NAMESPACE_DECL))
! 7817: return(NULL);
! 7818: return(ctxt->context->node);
! 7819: }
! 7820:
! 7821: return(xmlXPathNextDescendant(ctxt, cur));
! 7822: }
! 7823:
! 7824: /**
! 7825: * xmlXPathNextParent:
! 7826: * @ctxt: the XPath Parser context
! 7827: * @cur: the current node in the traversal
! 7828: *
! 7829: * Traversal function for the "parent" direction
! 7830: * The parent axis contains the parent of the context node, if there is one.
! 7831: *
! 7832: * Returns the next element following that axis
! 7833: */
! 7834: xmlNodePtr
! 7835: xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 7836: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 7837: /*
! 7838: * the parent of an attribute or namespace node is the element
! 7839: * to which the attribute or namespace node is attached
! 7840: * Namespace handling !!!
! 7841: */
! 7842: if (cur == NULL) {
! 7843: if (ctxt->context->node == NULL) return(NULL);
! 7844: switch (ctxt->context->node->type) {
! 7845: case XML_ELEMENT_NODE:
! 7846: case XML_TEXT_NODE:
! 7847: case XML_CDATA_SECTION_NODE:
! 7848: case XML_ENTITY_REF_NODE:
! 7849: case XML_ENTITY_NODE:
! 7850: case XML_PI_NODE:
! 7851: case XML_COMMENT_NODE:
! 7852: case XML_NOTATION_NODE:
! 7853: case XML_DTD_NODE:
! 7854: case XML_ELEMENT_DECL:
! 7855: case XML_ATTRIBUTE_DECL:
! 7856: case XML_XINCLUDE_START:
! 7857: case XML_XINCLUDE_END:
! 7858: case XML_ENTITY_DECL:
! 7859: if (ctxt->context->node->parent == NULL)
! 7860: return((xmlNodePtr) ctxt->context->doc);
! 7861: if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
! 7862: ((ctxt->context->node->parent->name[0] == ' ') ||
! 7863: (xmlStrEqual(ctxt->context->node->parent->name,
! 7864: BAD_CAST "fake node libxslt"))))
! 7865: return(NULL);
! 7866: return(ctxt->context->node->parent);
! 7867: case XML_ATTRIBUTE_NODE: {
! 7868: xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
! 7869:
! 7870: return(att->parent);
! 7871: }
! 7872: case XML_DOCUMENT_NODE:
! 7873: case XML_DOCUMENT_TYPE_NODE:
! 7874: case XML_DOCUMENT_FRAG_NODE:
! 7875: case XML_HTML_DOCUMENT_NODE:
! 7876: #ifdef LIBXML_DOCB_ENABLED
! 7877: case XML_DOCB_DOCUMENT_NODE:
! 7878: #endif
! 7879: return(NULL);
! 7880: case XML_NAMESPACE_DECL: {
! 7881: xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
! 7882:
! 7883: if ((ns->next != NULL) &&
! 7884: (ns->next->type != XML_NAMESPACE_DECL))
! 7885: return((xmlNodePtr) ns->next);
! 7886: return(NULL);
! 7887: }
! 7888: }
! 7889: }
! 7890: return(NULL);
! 7891: }
! 7892:
! 7893: /**
! 7894: * xmlXPathNextAncestor:
! 7895: * @ctxt: the XPath Parser context
! 7896: * @cur: the current node in the traversal
! 7897: *
! 7898: * Traversal function for the "ancestor" direction
! 7899: * the ancestor axis contains the ancestors of the context node; the ancestors
! 7900: * of the context node consist of the parent of context node and the parent's
! 7901: * parent and so on; the nodes are ordered in reverse document order; thus the
! 7902: * parent is the first node on the axis, and the parent's parent is the second
! 7903: * node on the axis
! 7904: *
! 7905: * Returns the next element following that axis
! 7906: */
! 7907: xmlNodePtr
! 7908: xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 7909: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 7910: /*
! 7911: * the parent of an attribute or namespace node is the element
! 7912: * to which the attribute or namespace node is attached
! 7913: * !!!!!!!!!!!!!
! 7914: */
! 7915: if (cur == NULL) {
! 7916: if (ctxt->context->node == NULL) return(NULL);
! 7917: switch (ctxt->context->node->type) {
! 7918: case XML_ELEMENT_NODE:
! 7919: case XML_TEXT_NODE:
! 7920: case XML_CDATA_SECTION_NODE:
! 7921: case XML_ENTITY_REF_NODE:
! 7922: case XML_ENTITY_NODE:
! 7923: case XML_PI_NODE:
! 7924: case XML_COMMENT_NODE:
! 7925: case XML_DTD_NODE:
! 7926: case XML_ELEMENT_DECL:
! 7927: case XML_ATTRIBUTE_DECL:
! 7928: case XML_ENTITY_DECL:
! 7929: case XML_NOTATION_NODE:
! 7930: case XML_XINCLUDE_START:
! 7931: case XML_XINCLUDE_END:
! 7932: if (ctxt->context->node->parent == NULL)
! 7933: return((xmlNodePtr) ctxt->context->doc);
! 7934: if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
! 7935: ((ctxt->context->node->parent->name[0] == ' ') ||
! 7936: (xmlStrEqual(ctxt->context->node->parent->name,
! 7937: BAD_CAST "fake node libxslt"))))
! 7938: return(NULL);
! 7939: return(ctxt->context->node->parent);
! 7940: case XML_ATTRIBUTE_NODE: {
! 7941: xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
! 7942:
! 7943: return(tmp->parent);
! 7944: }
! 7945: case XML_DOCUMENT_NODE:
! 7946: case XML_DOCUMENT_TYPE_NODE:
! 7947: case XML_DOCUMENT_FRAG_NODE:
! 7948: case XML_HTML_DOCUMENT_NODE:
! 7949: #ifdef LIBXML_DOCB_ENABLED
! 7950: case XML_DOCB_DOCUMENT_NODE:
! 7951: #endif
! 7952: return(NULL);
! 7953: case XML_NAMESPACE_DECL: {
! 7954: xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
! 7955:
! 7956: if ((ns->next != NULL) &&
! 7957: (ns->next->type != XML_NAMESPACE_DECL))
! 7958: return((xmlNodePtr) ns->next);
! 7959: /* Bad, how did that namespace end up here ? */
! 7960: return(NULL);
! 7961: }
! 7962: }
! 7963: return(NULL);
! 7964: }
! 7965: if (cur == ctxt->context->doc->children)
! 7966: return((xmlNodePtr) ctxt->context->doc);
! 7967: if (cur == (xmlNodePtr) ctxt->context->doc)
! 7968: return(NULL);
! 7969: switch (cur->type) {
! 7970: case XML_ELEMENT_NODE:
! 7971: case XML_TEXT_NODE:
! 7972: case XML_CDATA_SECTION_NODE:
! 7973: case XML_ENTITY_REF_NODE:
! 7974: case XML_ENTITY_NODE:
! 7975: case XML_PI_NODE:
! 7976: case XML_COMMENT_NODE:
! 7977: case XML_NOTATION_NODE:
! 7978: case XML_DTD_NODE:
! 7979: case XML_ELEMENT_DECL:
! 7980: case XML_ATTRIBUTE_DECL:
! 7981: case XML_ENTITY_DECL:
! 7982: case XML_XINCLUDE_START:
! 7983: case XML_XINCLUDE_END:
! 7984: if (cur->parent == NULL)
! 7985: return(NULL);
! 7986: if ((cur->parent->type == XML_ELEMENT_NODE) &&
! 7987: ((cur->parent->name[0] == ' ') ||
! 7988: (xmlStrEqual(cur->parent->name,
! 7989: BAD_CAST "fake node libxslt"))))
! 7990: return(NULL);
! 7991: return(cur->parent);
! 7992: case XML_ATTRIBUTE_NODE: {
! 7993: xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
! 7994:
! 7995: return(att->parent);
! 7996: }
! 7997: case XML_NAMESPACE_DECL: {
! 7998: xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
! 7999:
! 8000: if ((ns->next != NULL) &&
! 8001: (ns->next->type != XML_NAMESPACE_DECL))
! 8002: return((xmlNodePtr) ns->next);
! 8003: /* Bad, how did that namespace end up here ? */
! 8004: return(NULL);
! 8005: }
! 8006: case XML_DOCUMENT_NODE:
! 8007: case XML_DOCUMENT_TYPE_NODE:
! 8008: case XML_DOCUMENT_FRAG_NODE:
! 8009: case XML_HTML_DOCUMENT_NODE:
! 8010: #ifdef LIBXML_DOCB_ENABLED
! 8011: case XML_DOCB_DOCUMENT_NODE:
! 8012: #endif
! 8013: return(NULL);
! 8014: }
! 8015: return(NULL);
! 8016: }
! 8017:
! 8018: /**
! 8019: * xmlXPathNextAncestorOrSelf:
! 8020: * @ctxt: the XPath Parser context
! 8021: * @cur: the current node in the traversal
! 8022: *
! 8023: * Traversal function for the "ancestor-or-self" direction
! 8024: * he ancestor-or-self axis contains the context node and ancestors of
! 8025: * the context node in reverse document order; thus the context node is
! 8026: * the first node on the axis, and the context node's parent the second;
! 8027: * parent here is defined the same as with the parent axis.
! 8028: *
! 8029: * Returns the next element following that axis
! 8030: */
! 8031: xmlNodePtr
! 8032: xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 8033: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 8034: if (cur == NULL)
! 8035: return(ctxt->context->node);
! 8036: return(xmlXPathNextAncestor(ctxt, cur));
! 8037: }
! 8038:
! 8039: /**
! 8040: * xmlXPathNextFollowingSibling:
! 8041: * @ctxt: the XPath Parser context
! 8042: * @cur: the current node in the traversal
! 8043: *
! 8044: * Traversal function for the "following-sibling" direction
! 8045: * The following-sibling axis contains the following siblings of the context
! 8046: * node in document order.
! 8047: *
! 8048: * Returns the next element following that axis
! 8049: */
! 8050: xmlNodePtr
! 8051: xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 8052: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 8053: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
! 8054: (ctxt->context->node->type == XML_NAMESPACE_DECL))
! 8055: return(NULL);
! 8056: if (cur == (xmlNodePtr) ctxt->context->doc)
! 8057: return(NULL);
! 8058: if (cur == NULL)
! 8059: return(ctxt->context->node->next);
! 8060: return(cur->next);
! 8061: }
! 8062:
! 8063: /**
! 8064: * xmlXPathNextPrecedingSibling:
! 8065: * @ctxt: the XPath Parser context
! 8066: * @cur: the current node in the traversal
! 8067: *
! 8068: * Traversal function for the "preceding-sibling" direction
! 8069: * The preceding-sibling axis contains the preceding siblings of the context
! 8070: * node in reverse document order; the first preceding sibling is first on the
! 8071: * axis; the sibling preceding that node is the second on the axis and so on.
! 8072: *
! 8073: * Returns the next element following that axis
! 8074: */
! 8075: xmlNodePtr
! 8076: xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 8077: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 8078: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
! 8079: (ctxt->context->node->type == XML_NAMESPACE_DECL))
! 8080: return(NULL);
! 8081: if (cur == (xmlNodePtr) ctxt->context->doc)
! 8082: return(NULL);
! 8083: if (cur == NULL)
! 8084: return(ctxt->context->node->prev);
! 8085: if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
! 8086: cur = cur->prev;
! 8087: if (cur == NULL)
! 8088: return(ctxt->context->node->prev);
! 8089: }
! 8090: return(cur->prev);
! 8091: }
! 8092:
! 8093: /**
! 8094: * xmlXPathNextFollowing:
! 8095: * @ctxt: the XPath Parser context
! 8096: * @cur: the current node in the traversal
! 8097: *
! 8098: * Traversal function for the "following" direction
! 8099: * The following axis contains all nodes in the same document as the context
! 8100: * node that are after the context node in document order, excluding any
! 8101: * descendants and excluding attribute nodes and namespace nodes; the nodes
! 8102: * are ordered in document order
! 8103: *
! 8104: * Returns the next element following that axis
! 8105: */
! 8106: xmlNodePtr
! 8107: xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 8108: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 8109: if ((cur != NULL) && (cur->type != XML_ATTRIBUTE_NODE) &&
! 8110: (cur->type != XML_NAMESPACE_DECL) && (cur->children != NULL))
! 8111: return(cur->children);
! 8112:
! 8113: if (cur == NULL) {
! 8114: cur = ctxt->context->node;
! 8115: if (cur->type == XML_NAMESPACE_DECL)
! 8116: return(NULL);
! 8117: if (cur->type == XML_ATTRIBUTE_NODE)
! 8118: cur = cur->parent;
! 8119: }
! 8120: if (cur == NULL) return(NULL) ; /* ERROR */
! 8121: if (cur->next != NULL) return(cur->next) ;
! 8122: do {
! 8123: cur = cur->parent;
! 8124: if (cur == NULL) break;
! 8125: if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
! 8126: if (cur->next != NULL) return(cur->next);
! 8127: } while (cur != NULL);
! 8128: return(cur);
! 8129: }
! 8130:
! 8131: /*
! 8132: * xmlXPathIsAncestor:
! 8133: * @ancestor: the ancestor node
! 8134: * @node: the current node
! 8135: *
! 8136: * Check that @ancestor is a @node's ancestor
! 8137: *
! 8138: * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
! 8139: */
! 8140: static int
! 8141: xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
! 8142: if ((ancestor == NULL) || (node == NULL)) return(0);
! 8143: /* nodes need to be in the same document */
! 8144: if (ancestor->doc != node->doc) return(0);
! 8145: /* avoid searching if ancestor or node is the root node */
! 8146: if (ancestor == (xmlNodePtr) node->doc) return(1);
! 8147: if (node == (xmlNodePtr) ancestor->doc) return(0);
! 8148: while (node->parent != NULL) {
! 8149: if (node->parent == ancestor)
! 8150: return(1);
! 8151: node = node->parent;
! 8152: }
! 8153: return(0);
! 8154: }
! 8155:
! 8156: /**
! 8157: * xmlXPathNextPreceding:
! 8158: * @ctxt: the XPath Parser context
! 8159: * @cur: the current node in the traversal
! 8160: *
! 8161: * Traversal function for the "preceding" direction
! 8162: * the preceding axis contains all nodes in the same document as the context
! 8163: * node that are before the context node in document order, excluding any
! 8164: * ancestors and excluding attribute nodes and namespace nodes; the nodes are
! 8165: * ordered in reverse document order
! 8166: *
! 8167: * Returns the next element following that axis
! 8168: */
! 8169: xmlNodePtr
! 8170: xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
! 8171: {
! 8172: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 8173: if (cur == NULL) {
! 8174: cur = ctxt->context->node;
! 8175: if (cur->type == XML_NAMESPACE_DECL)
! 8176: return(NULL);
! 8177: if (cur->type == XML_ATTRIBUTE_NODE)
! 8178: return(cur->parent);
! 8179: }
! 8180: if (cur == NULL)
! 8181: return (NULL);
! 8182: if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
! 8183: cur = cur->prev;
! 8184: do {
! 8185: if (cur->prev != NULL) {
! 8186: for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
! 8187: return (cur);
! 8188: }
! 8189:
! 8190: cur = cur->parent;
! 8191: if (cur == NULL)
! 8192: return (NULL);
! 8193: if (cur == ctxt->context->doc->children)
! 8194: return (NULL);
! 8195: } while (xmlXPathIsAncestor(cur, ctxt->context->node));
! 8196: return (cur);
! 8197: }
! 8198:
! 8199: /**
! 8200: * xmlXPathNextPrecedingInternal:
! 8201: * @ctxt: the XPath Parser context
! 8202: * @cur: the current node in the traversal
! 8203: *
! 8204: * Traversal function for the "preceding" direction
! 8205: * the preceding axis contains all nodes in the same document as the context
! 8206: * node that are before the context node in document order, excluding any
! 8207: * ancestors and excluding attribute nodes and namespace nodes; the nodes are
! 8208: * ordered in reverse document order
! 8209: * This is a faster implementation but internal only since it requires a
! 8210: * state kept in the parser context: ctxt->ancestor.
! 8211: *
! 8212: * Returns the next element following that axis
! 8213: */
! 8214: static xmlNodePtr
! 8215: xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
! 8216: xmlNodePtr cur)
! 8217: {
! 8218: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 8219: if (cur == NULL) {
! 8220: cur = ctxt->context->node;
! 8221: if (cur == NULL)
! 8222: return (NULL);
! 8223: if (cur->type == XML_NAMESPACE_DECL)
! 8224: return (NULL);
! 8225: ctxt->ancestor = cur->parent;
! 8226: }
! 8227: if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
! 8228: cur = cur->prev;
! 8229: while (cur->prev == NULL) {
! 8230: cur = cur->parent;
! 8231: if (cur == NULL)
! 8232: return (NULL);
! 8233: if (cur == ctxt->context->doc->children)
! 8234: return (NULL);
! 8235: if (cur != ctxt->ancestor)
! 8236: return (cur);
! 8237: ctxt->ancestor = cur->parent;
! 8238: }
! 8239: cur = cur->prev;
! 8240: while (cur->last != NULL)
! 8241: cur = cur->last;
! 8242: return (cur);
! 8243: }
! 8244:
! 8245: /**
! 8246: * xmlXPathNextNamespace:
! 8247: * @ctxt: the XPath Parser context
! 8248: * @cur: the current attribute in the traversal
! 8249: *
! 8250: * Traversal function for the "namespace" direction
! 8251: * the namespace axis contains the namespace nodes of the context node;
! 8252: * the order of nodes on this axis is implementation-defined; the axis will
! 8253: * be empty unless the context node is an element
! 8254: *
! 8255: * We keep the XML namespace node at the end of the list.
! 8256: *
! 8257: * Returns the next element following that axis
! 8258: */
! 8259: xmlNodePtr
! 8260: xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 8261: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 8262: if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
! 8263: if (ctxt->context->tmpNsList == NULL && cur != (xmlNodePtr) xmlXPathXMLNamespace) {
! 8264: if (ctxt->context->tmpNsList != NULL)
! 8265: xmlFree(ctxt->context->tmpNsList);
! 8266: ctxt->context->tmpNsList =
! 8267: xmlGetNsList(ctxt->context->doc, ctxt->context->node);
! 8268: ctxt->context->tmpNsNr = 0;
! 8269: if (ctxt->context->tmpNsList != NULL) {
! 8270: while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) {
! 8271: ctxt->context->tmpNsNr++;
! 8272: }
! 8273: }
! 8274: return((xmlNodePtr) xmlXPathXMLNamespace);
! 8275: }
! 8276: if (ctxt->context->tmpNsNr > 0) {
! 8277: return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr];
! 8278: } else {
! 8279: if (ctxt->context->tmpNsList != NULL)
! 8280: xmlFree(ctxt->context->tmpNsList);
! 8281: ctxt->context->tmpNsList = NULL;
! 8282: return(NULL);
! 8283: }
! 8284: }
! 8285:
! 8286: /**
! 8287: * xmlXPathNextAttribute:
! 8288: * @ctxt: the XPath Parser context
! 8289: * @cur: the current attribute in the traversal
! 8290: *
! 8291: * Traversal function for the "attribute" direction
! 8292: * TODO: support DTD inherited default attributes
! 8293: *
! 8294: * Returns the next element following that axis
! 8295: */
! 8296: xmlNodePtr
! 8297: xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
! 8298: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
! 8299: if (ctxt->context->node == NULL)
! 8300: return(NULL);
! 8301: if (ctxt->context->node->type != XML_ELEMENT_NODE)
! 8302: return(NULL);
! 8303: if (cur == NULL) {
! 8304: if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
! 8305: return(NULL);
! 8306: return((xmlNodePtr)ctxt->context->node->properties);
! 8307: }
! 8308: return((xmlNodePtr)cur->next);
! 8309: }
! 8310:
! 8311: /************************************************************************
! 8312: * *
! 8313: * NodeTest Functions *
! 8314: * *
! 8315: ************************************************************************/
! 8316:
! 8317: #define IS_FUNCTION 200
! 8318:
! 8319:
! 8320: /************************************************************************
! 8321: * *
! 8322: * Implicit tree core function library *
! 8323: * *
! 8324: ************************************************************************/
! 8325:
! 8326: /**
! 8327: * xmlXPathRoot:
! 8328: * @ctxt: the XPath Parser context
! 8329: *
! 8330: * Initialize the context to the root of the document
! 8331: */
! 8332: void
! 8333: xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
! 8334: if ((ctxt == NULL) || (ctxt->context == NULL))
! 8335: return;
! 8336: ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
! 8337: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
! 8338: ctxt->context->node));
! 8339: }
! 8340:
! 8341: /************************************************************************
! 8342: * *
! 8343: * The explicit core function library *
! 8344: *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
! 8345: * *
! 8346: ************************************************************************/
! 8347:
! 8348:
! 8349: /**
! 8350: * xmlXPathLastFunction:
! 8351: * @ctxt: the XPath Parser context
! 8352: * @nargs: the number of arguments
! 8353: *
! 8354: * Implement the last() XPath function
! 8355: * number last()
! 8356: * The last function returns the number of nodes in the context node list.
! 8357: */
! 8358: void
! 8359: xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8360: CHECK_ARITY(0);
! 8361: if (ctxt->context->contextSize >= 0) {
! 8362: valuePush(ctxt,
! 8363: xmlXPathCacheNewFloat(ctxt->context,
! 8364: (double) ctxt->context->contextSize));
! 8365: #ifdef DEBUG_EXPR
! 8366: xmlGenericError(xmlGenericErrorContext,
! 8367: "last() : %d\n", ctxt->context->contextSize);
! 8368: #endif
! 8369: } else {
! 8370: XP_ERROR(XPATH_INVALID_CTXT_SIZE);
! 8371: }
! 8372: }
! 8373:
! 8374: /**
! 8375: * xmlXPathPositionFunction:
! 8376: * @ctxt: the XPath Parser context
! 8377: * @nargs: the number of arguments
! 8378: *
! 8379: * Implement the position() XPath function
! 8380: * number position()
! 8381: * The position function returns the position of the context node in the
! 8382: * context node list. The first position is 1, and so the last position
! 8383: * will be equal to last().
! 8384: */
! 8385: void
! 8386: xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8387: CHECK_ARITY(0);
! 8388: if (ctxt->context->proximityPosition >= 0) {
! 8389: valuePush(ctxt,
! 8390: xmlXPathCacheNewFloat(ctxt->context,
! 8391: (double) ctxt->context->proximityPosition));
! 8392: #ifdef DEBUG_EXPR
! 8393: xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
! 8394: ctxt->context->proximityPosition);
! 8395: #endif
! 8396: } else {
! 8397: XP_ERROR(XPATH_INVALID_CTXT_POSITION);
! 8398: }
! 8399: }
! 8400:
! 8401: /**
! 8402: * xmlXPathCountFunction:
! 8403: * @ctxt: the XPath Parser context
! 8404: * @nargs: the number of arguments
! 8405: *
! 8406: * Implement the count() XPath function
! 8407: * number count(node-set)
! 8408: */
! 8409: void
! 8410: xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8411: xmlXPathObjectPtr cur;
! 8412:
! 8413: CHECK_ARITY(1);
! 8414: if ((ctxt->value == NULL) ||
! 8415: ((ctxt->value->type != XPATH_NODESET) &&
! 8416: (ctxt->value->type != XPATH_XSLT_TREE)))
! 8417: XP_ERROR(XPATH_INVALID_TYPE);
! 8418: cur = valuePop(ctxt);
! 8419:
! 8420: if ((cur == NULL) || (cur->nodesetval == NULL))
! 8421: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
! 8422: else if ((cur->type == XPATH_NODESET) || (cur->type == XPATH_XSLT_TREE)) {
! 8423: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
! 8424: (double) cur->nodesetval->nodeNr));
! 8425: } else {
! 8426: if ((cur->nodesetval->nodeNr != 1) ||
! 8427: (cur->nodesetval->nodeTab == NULL)) {
! 8428: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
! 8429: } else {
! 8430: xmlNodePtr tmp;
! 8431: int i = 0;
! 8432:
! 8433: tmp = cur->nodesetval->nodeTab[0];
! 8434: if (tmp != NULL) {
! 8435: tmp = tmp->children;
! 8436: while (tmp != NULL) {
! 8437: tmp = tmp->next;
! 8438: i++;
! 8439: }
! 8440: }
! 8441: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) i));
! 8442: }
! 8443: }
! 8444: xmlXPathReleaseObject(ctxt->context, cur);
! 8445: }
! 8446:
! 8447: /**
! 8448: * xmlXPathGetElementsByIds:
! 8449: * @doc: the document
! 8450: * @ids: a whitespace separated list of IDs
! 8451: *
! 8452: * Selects elements by their unique ID.
! 8453: *
! 8454: * Returns a node-set of selected elements.
! 8455: */
! 8456: static xmlNodeSetPtr
! 8457: xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
! 8458: xmlNodeSetPtr ret;
! 8459: const xmlChar *cur = ids;
! 8460: xmlChar *ID;
! 8461: xmlAttrPtr attr;
! 8462: xmlNodePtr elem = NULL;
! 8463:
! 8464: if (ids == NULL) return(NULL);
! 8465:
! 8466: ret = xmlXPathNodeSetCreate(NULL);
! 8467: if (ret == NULL)
! 8468: return(ret);
! 8469:
! 8470: while (IS_BLANK_CH(*cur)) cur++;
! 8471: while (*cur != 0) {
! 8472: while ((!IS_BLANK_CH(*cur)) && (*cur != 0))
! 8473: cur++;
! 8474:
! 8475: ID = xmlStrndup(ids, cur - ids);
! 8476: if (ID != NULL) {
! 8477: /*
! 8478: * We used to check the fact that the value passed
! 8479: * was an NCName, but this generated much troubles for
! 8480: * me and Aleksey Sanin, people blatantly violated that
! 8481: * constaint, like Visa3D spec.
! 8482: * if (xmlValidateNCName(ID, 1) == 0)
! 8483: */
! 8484: attr = xmlGetID(doc, ID);
! 8485: if (attr != NULL) {
! 8486: if (attr->type == XML_ATTRIBUTE_NODE)
! 8487: elem = attr->parent;
! 8488: else if (attr->type == XML_ELEMENT_NODE)
! 8489: elem = (xmlNodePtr) attr;
! 8490: else
! 8491: elem = NULL;
! 8492: if (elem != NULL)
! 8493: xmlXPathNodeSetAdd(ret, elem);
! 8494: }
! 8495: xmlFree(ID);
! 8496: }
! 8497:
! 8498: while (IS_BLANK_CH(*cur)) cur++;
! 8499: ids = cur;
! 8500: }
! 8501: return(ret);
! 8502: }
! 8503:
! 8504: /**
! 8505: * xmlXPathIdFunction:
! 8506: * @ctxt: the XPath Parser context
! 8507: * @nargs: the number of arguments
! 8508: *
! 8509: * Implement the id() XPath function
! 8510: * node-set id(object)
! 8511: * The id function selects elements by their unique ID
! 8512: * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
! 8513: * then the result is the union of the result of applying id to the
! 8514: * string value of each of the nodes in the argument node-set. When the
! 8515: * argument to id is of any other type, the argument is converted to a
! 8516: * string as if by a call to the string function; the string is split
! 8517: * into a whitespace-separated list of tokens (whitespace is any sequence
! 8518: * of characters matching the production S); the result is a node-set
! 8519: * containing the elements in the same document as the context node that
! 8520: * have a unique ID equal to any of the tokens in the list.
! 8521: */
! 8522: void
! 8523: xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8524: xmlChar *tokens;
! 8525: xmlNodeSetPtr ret;
! 8526: xmlXPathObjectPtr obj;
! 8527:
! 8528: CHECK_ARITY(1);
! 8529: obj = valuePop(ctxt);
! 8530: if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
! 8531: if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
! 8532: xmlNodeSetPtr ns;
! 8533: int i;
! 8534:
! 8535: ret = xmlXPathNodeSetCreate(NULL);
! 8536: /*
! 8537: * FIXME -- in an out-of-memory condition this will behave badly.
! 8538: * The solution is not clear -- we already popped an item from
! 8539: * ctxt, so the object is in a corrupt state.
! 8540: */
! 8541:
! 8542: if (obj->nodesetval != NULL) {
! 8543: for (i = 0; i < obj->nodesetval->nodeNr; i++) {
! 8544: tokens =
! 8545: xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
! 8546: ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
! 8547: ret = xmlXPathNodeSetMerge(ret, ns);
! 8548: xmlXPathFreeNodeSet(ns);
! 8549: if (tokens != NULL)
! 8550: xmlFree(tokens);
! 8551: }
! 8552: }
! 8553: xmlXPathReleaseObject(ctxt->context, obj);
! 8554: valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
! 8555: return;
! 8556: }
! 8557: obj = xmlXPathCacheConvertString(ctxt->context, obj);
! 8558: ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
! 8559: valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
! 8560: xmlXPathReleaseObject(ctxt->context, obj);
! 8561: return;
! 8562: }
! 8563:
! 8564: /**
! 8565: * xmlXPathLocalNameFunction:
! 8566: * @ctxt: the XPath Parser context
! 8567: * @nargs: the number of arguments
! 8568: *
! 8569: * Implement the local-name() XPath function
! 8570: * string local-name(node-set?)
! 8571: * The local-name function returns a string containing the local part
! 8572: * of the name of the node in the argument node-set that is first in
! 8573: * document order. If the node-set is empty or the first node has no
! 8574: * name, an empty string is returned. If the argument is omitted it
! 8575: * defaults to the context node.
! 8576: */
! 8577: void
! 8578: xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8579: xmlXPathObjectPtr cur;
! 8580:
! 8581: if (ctxt == NULL) return;
! 8582:
! 8583: if (nargs == 0) {
! 8584: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
! 8585: ctxt->context->node));
! 8586: nargs = 1;
! 8587: }
! 8588:
! 8589: CHECK_ARITY(1);
! 8590: if ((ctxt->value == NULL) ||
! 8591: ((ctxt->value->type != XPATH_NODESET) &&
! 8592: (ctxt->value->type != XPATH_XSLT_TREE)))
! 8593: XP_ERROR(XPATH_INVALID_TYPE);
! 8594: cur = valuePop(ctxt);
! 8595:
! 8596: if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
! 8597: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
! 8598: } else {
! 8599: int i = 0; /* Should be first in document order !!!!! */
! 8600: switch (cur->nodesetval->nodeTab[i]->type) {
! 8601: case XML_ELEMENT_NODE:
! 8602: case XML_ATTRIBUTE_NODE:
! 8603: case XML_PI_NODE:
! 8604: if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
! 8605: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
! 8606: else
! 8607: valuePush(ctxt,
! 8608: xmlXPathCacheNewString(ctxt->context,
! 8609: cur->nodesetval->nodeTab[i]->name));
! 8610: break;
! 8611: case XML_NAMESPACE_DECL:
! 8612: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
! 8613: ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
! 8614: break;
! 8615: default:
! 8616: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
! 8617: }
! 8618: }
! 8619: xmlXPathReleaseObject(ctxt->context, cur);
! 8620: }
! 8621:
! 8622: /**
! 8623: * xmlXPathNamespaceURIFunction:
! 8624: * @ctxt: the XPath Parser context
! 8625: * @nargs: the number of arguments
! 8626: *
! 8627: * Implement the namespace-uri() XPath function
! 8628: * string namespace-uri(node-set?)
! 8629: * The namespace-uri function returns a string containing the
! 8630: * namespace URI of the expanded name of the node in the argument
! 8631: * node-set that is first in document order. If the node-set is empty,
! 8632: * the first node has no name, or the expanded name has no namespace
! 8633: * URI, an empty string is returned. If the argument is omitted it
! 8634: * defaults to the context node.
! 8635: */
! 8636: void
! 8637: xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8638: xmlXPathObjectPtr cur;
! 8639:
! 8640: if (ctxt == NULL) return;
! 8641:
! 8642: if (nargs == 0) {
! 8643: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
! 8644: ctxt->context->node));
! 8645: nargs = 1;
! 8646: }
! 8647: CHECK_ARITY(1);
! 8648: if ((ctxt->value == NULL) ||
! 8649: ((ctxt->value->type != XPATH_NODESET) &&
! 8650: (ctxt->value->type != XPATH_XSLT_TREE)))
! 8651: XP_ERROR(XPATH_INVALID_TYPE);
! 8652: cur = valuePop(ctxt);
! 8653:
! 8654: if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
! 8655: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
! 8656: } else {
! 8657: int i = 0; /* Should be first in document order !!!!! */
! 8658: switch (cur->nodesetval->nodeTab[i]->type) {
! 8659: case XML_ELEMENT_NODE:
! 8660: case XML_ATTRIBUTE_NODE:
! 8661: if (cur->nodesetval->nodeTab[i]->ns == NULL)
! 8662: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
! 8663: else
! 8664: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
! 8665: cur->nodesetval->nodeTab[i]->ns->href));
! 8666: break;
! 8667: default:
! 8668: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
! 8669: }
! 8670: }
! 8671: xmlXPathReleaseObject(ctxt->context, cur);
! 8672: }
! 8673:
! 8674: /**
! 8675: * xmlXPathNameFunction:
! 8676: * @ctxt: the XPath Parser context
! 8677: * @nargs: the number of arguments
! 8678: *
! 8679: * Implement the name() XPath function
! 8680: * string name(node-set?)
! 8681: * The name function returns a string containing a QName representing
! 8682: * the name of the node in the argument node-set that is first in document
! 8683: * order. The QName must represent the name with respect to the namespace
! 8684: * declarations in effect on the node whose name is being represented.
! 8685: * Typically, this will be the form in which the name occurred in the XML
! 8686: * source. This need not be the case if there are namespace declarations
! 8687: * in effect on the node that associate multiple prefixes with the same
! 8688: * namespace. However, an implementation may include information about
! 8689: * the original prefix in its representation of nodes; in this case, an
! 8690: * implementation can ensure that the returned string is always the same
! 8691: * as the QName used in the XML source. If the argument it omitted it
! 8692: * defaults to the context node.
! 8693: * Libxml keep the original prefix so the "real qualified name" used is
! 8694: * returned.
! 8695: */
! 8696: static void
! 8697: xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
! 8698: {
! 8699: xmlXPathObjectPtr cur;
! 8700:
! 8701: if (nargs == 0) {
! 8702: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
! 8703: ctxt->context->node));
! 8704: nargs = 1;
! 8705: }
! 8706:
! 8707: CHECK_ARITY(1);
! 8708: if ((ctxt->value == NULL) ||
! 8709: ((ctxt->value->type != XPATH_NODESET) &&
! 8710: (ctxt->value->type != XPATH_XSLT_TREE)))
! 8711: XP_ERROR(XPATH_INVALID_TYPE);
! 8712: cur = valuePop(ctxt);
! 8713:
! 8714: if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
! 8715: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
! 8716: } else {
! 8717: int i = 0; /* Should be first in document order !!!!! */
! 8718:
! 8719: switch (cur->nodesetval->nodeTab[i]->type) {
! 8720: case XML_ELEMENT_NODE:
! 8721: case XML_ATTRIBUTE_NODE:
! 8722: if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
! 8723: valuePush(ctxt,
! 8724: xmlXPathCacheNewCString(ctxt->context, ""));
! 8725: else if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
! 8726: (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) {
! 8727: valuePush(ctxt,
! 8728: xmlXPathCacheNewString(ctxt->context,
! 8729: cur->nodesetval->nodeTab[i]->name));
! 8730: } else {
! 8731: xmlChar *fullname;
! 8732:
! 8733: fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name,
! 8734: cur->nodesetval->nodeTab[i]->ns->prefix,
! 8735: NULL, 0);
! 8736: if (fullname == cur->nodesetval->nodeTab[i]->name)
! 8737: fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name);
! 8738: if (fullname == NULL) {
! 8739: XP_ERROR(XPATH_MEMORY_ERROR);
! 8740: }
! 8741: valuePush(ctxt, xmlXPathCacheWrapString(
! 8742: ctxt->context, fullname));
! 8743: }
! 8744: break;
! 8745: default:
! 8746: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
! 8747: cur->nodesetval->nodeTab[i]));
! 8748: xmlXPathLocalNameFunction(ctxt, 1);
! 8749: }
! 8750: }
! 8751: xmlXPathReleaseObject(ctxt->context, cur);
! 8752: }
! 8753:
! 8754:
! 8755: /**
! 8756: * xmlXPathStringFunction:
! 8757: * @ctxt: the XPath Parser context
! 8758: * @nargs: the number of arguments
! 8759: *
! 8760: * Implement the string() XPath function
! 8761: * string string(object?)
! 8762: * The string function converts an object to a string as follows:
! 8763: * - A node-set is converted to a string by returning the value of
! 8764: * the node in the node-set that is first in document order.
! 8765: * If the node-set is empty, an empty string is returned.
! 8766: * - A number is converted to a string as follows
! 8767: * + NaN is converted to the string NaN
! 8768: * + positive zero is converted to the string 0
! 8769: * + negative zero is converted to the string 0
! 8770: * + positive infinity is converted to the string Infinity
! 8771: * + negative infinity is converted to the string -Infinity
! 8772: * + if the number is an integer, the number is represented in
! 8773: * decimal form as a Number with no decimal point and no leading
! 8774: * zeros, preceded by a minus sign (-) if the number is negative
! 8775: * + otherwise, the number is represented in decimal form as a
! 8776: * Number including a decimal point with at least one digit
! 8777: * before the decimal point and at least one digit after the
! 8778: * decimal point, preceded by a minus sign (-) if the number
! 8779: * is negative; there must be no leading zeros before the decimal
! 8780: * point apart possibly from the one required digit immediately
! 8781: * before the decimal point; beyond the one required digit
! 8782: * after the decimal point there must be as many, but only as
! 8783: * many, more digits as are needed to uniquely distinguish the
! 8784: * number from all other IEEE 754 numeric values.
! 8785: * - The boolean false value is converted to the string false.
! 8786: * The boolean true value is converted to the string true.
! 8787: *
! 8788: * If the argument is omitted, it defaults to a node-set with the
! 8789: * context node as its only member.
! 8790: */
! 8791: void
! 8792: xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8793: xmlXPathObjectPtr cur;
! 8794:
! 8795: if (ctxt == NULL) return;
! 8796: if (nargs == 0) {
! 8797: valuePush(ctxt,
! 8798: xmlXPathCacheWrapString(ctxt->context,
! 8799: xmlXPathCastNodeToString(ctxt->context->node)));
! 8800: return;
! 8801: }
! 8802:
! 8803: CHECK_ARITY(1);
! 8804: cur = valuePop(ctxt);
! 8805: if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
! 8806: valuePush(ctxt, xmlXPathCacheConvertString(ctxt->context, cur));
! 8807: }
! 8808:
! 8809: /**
! 8810: * xmlXPathStringLengthFunction:
! 8811: * @ctxt: the XPath Parser context
! 8812: * @nargs: the number of arguments
! 8813: *
! 8814: * Implement the string-length() XPath function
! 8815: * number string-length(string?)
! 8816: * The string-length returns the number of characters in the string
! 8817: * (see [3.6 Strings]). If the argument is omitted, it defaults to
! 8818: * the context node converted to a string, in other words the value
! 8819: * of the context node.
! 8820: */
! 8821: void
! 8822: xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8823: xmlXPathObjectPtr cur;
! 8824:
! 8825: if (nargs == 0) {
! 8826: if ((ctxt == NULL) || (ctxt->context == NULL))
! 8827: return;
! 8828: if (ctxt->context->node == NULL) {
! 8829: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0));
! 8830: } else {
! 8831: xmlChar *content;
! 8832:
! 8833: content = xmlXPathCastNodeToString(ctxt->context->node);
! 8834: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
! 8835: xmlUTF8Strlen(content)));
! 8836: xmlFree(content);
! 8837: }
! 8838: return;
! 8839: }
! 8840: CHECK_ARITY(1);
! 8841: CAST_TO_STRING;
! 8842: CHECK_TYPE(XPATH_STRING);
! 8843: cur = valuePop(ctxt);
! 8844: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
! 8845: xmlUTF8Strlen(cur->stringval)));
! 8846: xmlXPathReleaseObject(ctxt->context, cur);
! 8847: }
! 8848:
! 8849: /**
! 8850: * xmlXPathConcatFunction:
! 8851: * @ctxt: the XPath Parser context
! 8852: * @nargs: the number of arguments
! 8853: *
! 8854: * Implement the concat() XPath function
! 8855: * string concat(string, string, string*)
! 8856: * The concat function returns the concatenation of its arguments.
! 8857: */
! 8858: void
! 8859: xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8860: xmlXPathObjectPtr cur, newobj;
! 8861: xmlChar *tmp;
! 8862:
! 8863: if (ctxt == NULL) return;
! 8864: if (nargs < 2) {
! 8865: CHECK_ARITY(2);
! 8866: }
! 8867:
! 8868: CAST_TO_STRING;
! 8869: cur = valuePop(ctxt);
! 8870: if ((cur == NULL) || (cur->type != XPATH_STRING)) {
! 8871: xmlXPathReleaseObject(ctxt->context, cur);
! 8872: return;
! 8873: }
! 8874: nargs--;
! 8875:
! 8876: while (nargs > 0) {
! 8877: CAST_TO_STRING;
! 8878: newobj = valuePop(ctxt);
! 8879: if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
! 8880: xmlXPathReleaseObject(ctxt->context, newobj);
! 8881: xmlXPathReleaseObject(ctxt->context, cur);
! 8882: XP_ERROR(XPATH_INVALID_TYPE);
! 8883: }
! 8884: tmp = xmlStrcat(newobj->stringval, cur->stringval);
! 8885: newobj->stringval = cur->stringval;
! 8886: cur->stringval = tmp;
! 8887: xmlXPathReleaseObject(ctxt->context, newobj);
! 8888: nargs--;
! 8889: }
! 8890: valuePush(ctxt, cur);
! 8891: }
! 8892:
! 8893: /**
! 8894: * xmlXPathContainsFunction:
! 8895: * @ctxt: the XPath Parser context
! 8896: * @nargs: the number of arguments
! 8897: *
! 8898: * Implement the contains() XPath function
! 8899: * boolean contains(string, string)
! 8900: * The contains function returns true if the first argument string
! 8901: * contains the second argument string, and otherwise returns false.
! 8902: */
! 8903: void
! 8904: xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8905: xmlXPathObjectPtr hay, needle;
! 8906:
! 8907: CHECK_ARITY(2);
! 8908: CAST_TO_STRING;
! 8909: CHECK_TYPE(XPATH_STRING);
! 8910: needle = valuePop(ctxt);
! 8911: CAST_TO_STRING;
! 8912: hay = valuePop(ctxt);
! 8913:
! 8914: if ((hay == NULL) || (hay->type != XPATH_STRING)) {
! 8915: xmlXPathReleaseObject(ctxt->context, hay);
! 8916: xmlXPathReleaseObject(ctxt->context, needle);
! 8917: XP_ERROR(XPATH_INVALID_TYPE);
! 8918: }
! 8919: if (xmlStrstr(hay->stringval, needle->stringval))
! 8920: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
! 8921: else
! 8922: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
! 8923: xmlXPathReleaseObject(ctxt->context, hay);
! 8924: xmlXPathReleaseObject(ctxt->context, needle);
! 8925: }
! 8926:
! 8927: /**
! 8928: * xmlXPathStartsWithFunction:
! 8929: * @ctxt: the XPath Parser context
! 8930: * @nargs: the number of arguments
! 8931: *
! 8932: * Implement the starts-with() XPath function
! 8933: * boolean starts-with(string, string)
! 8934: * The starts-with function returns true if the first argument string
! 8935: * starts with the second argument string, and otherwise returns false.
! 8936: */
! 8937: void
! 8938: xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8939: xmlXPathObjectPtr hay, needle;
! 8940: int n;
! 8941:
! 8942: CHECK_ARITY(2);
! 8943: CAST_TO_STRING;
! 8944: CHECK_TYPE(XPATH_STRING);
! 8945: needle = valuePop(ctxt);
! 8946: CAST_TO_STRING;
! 8947: hay = valuePop(ctxt);
! 8948:
! 8949: if ((hay == NULL) || (hay->type != XPATH_STRING)) {
! 8950: xmlXPathReleaseObject(ctxt->context, hay);
! 8951: xmlXPathReleaseObject(ctxt->context, needle);
! 8952: XP_ERROR(XPATH_INVALID_TYPE);
! 8953: }
! 8954: n = xmlStrlen(needle->stringval);
! 8955: if (xmlStrncmp(hay->stringval, needle->stringval, n))
! 8956: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
! 8957: else
! 8958: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
! 8959: xmlXPathReleaseObject(ctxt->context, hay);
! 8960: xmlXPathReleaseObject(ctxt->context, needle);
! 8961: }
! 8962:
! 8963: /**
! 8964: * xmlXPathSubstringFunction:
! 8965: * @ctxt: the XPath Parser context
! 8966: * @nargs: the number of arguments
! 8967: *
! 8968: * Implement the substring() XPath function
! 8969: * string substring(string, number, number?)
! 8970: * The substring function returns the substring of the first argument
! 8971: * starting at the position specified in the second argument with
! 8972: * length specified in the third argument. For example,
! 8973: * substring("12345",2,3) returns "234". If the third argument is not
! 8974: * specified, it returns the substring starting at the position specified
! 8975: * in the second argument and continuing to the end of the string. For
! 8976: * example, substring("12345",2) returns "2345". More precisely, each
! 8977: * character in the string (see [3.6 Strings]) is considered to have a
! 8978: * numeric position: the position of the first character is 1, the position
! 8979: * of the second character is 2 and so on. The returned substring contains
! 8980: * those characters for which the position of the character is greater than
! 8981: * or equal to the second argument and, if the third argument is specified,
! 8982: * less than the sum of the second and third arguments; the comparisons
! 8983: * and addition used for the above follow the standard IEEE 754 rules. Thus:
! 8984: * - substring("12345", 1.5, 2.6) returns "234"
! 8985: * - substring("12345", 0, 3) returns "12"
! 8986: * - substring("12345", 0 div 0, 3) returns ""
! 8987: * - substring("12345", 1, 0 div 0) returns ""
! 8988: * - substring("12345", -42, 1 div 0) returns "12345"
! 8989: * - substring("12345", -1 div 0, 1 div 0) returns ""
! 8990: */
! 8991: void
! 8992: xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 8993: xmlXPathObjectPtr str, start, len;
! 8994: double le=0, in;
! 8995: int i, l, m;
! 8996: xmlChar *ret;
! 8997:
! 8998: if (nargs < 2) {
! 8999: CHECK_ARITY(2);
! 9000: }
! 9001: if (nargs > 3) {
! 9002: CHECK_ARITY(3);
! 9003: }
! 9004: /*
! 9005: * take care of possible last (position) argument
! 9006: */
! 9007: if (nargs == 3) {
! 9008: CAST_TO_NUMBER;
! 9009: CHECK_TYPE(XPATH_NUMBER);
! 9010: len = valuePop(ctxt);
! 9011: le = len->floatval;
! 9012: xmlXPathReleaseObject(ctxt->context, len);
! 9013: }
! 9014:
! 9015: CAST_TO_NUMBER;
! 9016: CHECK_TYPE(XPATH_NUMBER);
! 9017: start = valuePop(ctxt);
! 9018: in = start->floatval;
! 9019: xmlXPathReleaseObject(ctxt->context, start);
! 9020: CAST_TO_STRING;
! 9021: CHECK_TYPE(XPATH_STRING);
! 9022: str = valuePop(ctxt);
! 9023: m = xmlUTF8Strlen((const unsigned char *)str->stringval);
! 9024:
! 9025: /*
! 9026: * If last pos not present, calculate last position
! 9027: */
! 9028: if (nargs != 3) {
! 9029: le = (double)m;
! 9030: if (in < 1.0)
! 9031: in = 1.0;
! 9032: }
! 9033:
! 9034: /* Need to check for the special cases where either
! 9035: * the index is NaN, the length is NaN, or both
! 9036: * arguments are infinity (relying on Inf + -Inf = NaN)
! 9037: */
! 9038: if (!xmlXPathIsInf(in) && !xmlXPathIsNaN(in + le)) {
! 9039: /*
! 9040: * To meet the requirements of the spec, the arguments
! 9041: * must be converted to integer format before
! 9042: * initial index calculations are done
! 9043: *
! 9044: * First we go to integer form, rounding up
! 9045: * and checking for special cases
! 9046: */
! 9047: i = (int) in;
! 9048: if (((double)i)+0.5 <= in) i++;
! 9049:
! 9050: if (xmlXPathIsInf(le) == 1) {
! 9051: l = m;
! 9052: if (i < 1)
! 9053: i = 1;
! 9054: }
! 9055: else if (xmlXPathIsInf(le) == -1 || le < 0.0)
! 9056: l = 0;
! 9057: else {
! 9058: l = (int) le;
! 9059: if (((double)l)+0.5 <= le) l++;
! 9060: }
! 9061:
! 9062: /* Now we normalize inidices */
! 9063: i -= 1;
! 9064: l += i;
! 9065: if (i < 0)
! 9066: i = 0;
! 9067: if (l > m)
! 9068: l = m;
! 9069:
! 9070: /* number of chars to copy */
! 9071: l -= i;
! 9072:
! 9073: ret = xmlUTF8Strsub(str->stringval, i, l);
! 9074: }
! 9075: else {
! 9076: ret = NULL;
! 9077: }
! 9078: if (ret == NULL)
! 9079: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
! 9080: else {
! 9081: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, ret));
! 9082: xmlFree(ret);
! 9083: }
! 9084: xmlXPathReleaseObject(ctxt->context, str);
! 9085: }
! 9086:
! 9087: /**
! 9088: * xmlXPathSubstringBeforeFunction:
! 9089: * @ctxt: the XPath Parser context
! 9090: * @nargs: the number of arguments
! 9091: *
! 9092: * Implement the substring-before() XPath function
! 9093: * string substring-before(string, string)
! 9094: * The substring-before function returns the substring of the first
! 9095: * argument string that precedes the first occurrence of the second
! 9096: * argument string in the first argument string, or the empty string
! 9097: * if the first argument string does not contain the second argument
! 9098: * string. For example, substring-before("1999/04/01","/") returns 1999.
! 9099: */
! 9100: void
! 9101: xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9102: xmlXPathObjectPtr str;
! 9103: xmlXPathObjectPtr find;
! 9104: xmlBufferPtr target;
! 9105: const xmlChar *point;
! 9106: int offset;
! 9107:
! 9108: CHECK_ARITY(2);
! 9109: CAST_TO_STRING;
! 9110: find = valuePop(ctxt);
! 9111: CAST_TO_STRING;
! 9112: str = valuePop(ctxt);
! 9113:
! 9114: target = xmlBufferCreate();
! 9115: if (target) {
! 9116: point = xmlStrstr(str->stringval, find->stringval);
! 9117: if (point) {
! 9118: offset = (int)(point - str->stringval);
! 9119: xmlBufferAdd(target, str->stringval, offset);
! 9120: }
! 9121: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
! 9122: xmlBufferContent(target)));
! 9123: xmlBufferFree(target);
! 9124: }
! 9125: xmlXPathReleaseObject(ctxt->context, str);
! 9126: xmlXPathReleaseObject(ctxt->context, find);
! 9127: }
! 9128:
! 9129: /**
! 9130: * xmlXPathSubstringAfterFunction:
! 9131: * @ctxt: the XPath Parser context
! 9132: * @nargs: the number of arguments
! 9133: *
! 9134: * Implement the substring-after() XPath function
! 9135: * string substring-after(string, string)
! 9136: * The substring-after function returns the substring of the first
! 9137: * argument string that follows the first occurrence of the second
! 9138: * argument string in the first argument string, or the empty stringi
! 9139: * if the first argument string does not contain the second argument
! 9140: * string. For example, substring-after("1999/04/01","/") returns 04/01,
! 9141: * and substring-after("1999/04/01","19") returns 99/04/01.
! 9142: */
! 9143: void
! 9144: xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9145: xmlXPathObjectPtr str;
! 9146: xmlXPathObjectPtr find;
! 9147: xmlBufferPtr target;
! 9148: const xmlChar *point;
! 9149: int offset;
! 9150:
! 9151: CHECK_ARITY(2);
! 9152: CAST_TO_STRING;
! 9153: find = valuePop(ctxt);
! 9154: CAST_TO_STRING;
! 9155: str = valuePop(ctxt);
! 9156:
! 9157: target = xmlBufferCreate();
! 9158: if (target) {
! 9159: point = xmlStrstr(str->stringval, find->stringval);
! 9160: if (point) {
! 9161: offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
! 9162: xmlBufferAdd(target, &str->stringval[offset],
! 9163: xmlStrlen(str->stringval) - offset);
! 9164: }
! 9165: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
! 9166: xmlBufferContent(target)));
! 9167: xmlBufferFree(target);
! 9168: }
! 9169: xmlXPathReleaseObject(ctxt->context, str);
! 9170: xmlXPathReleaseObject(ctxt->context, find);
! 9171: }
! 9172:
! 9173: /**
! 9174: * xmlXPathNormalizeFunction:
! 9175: * @ctxt: the XPath Parser context
! 9176: * @nargs: the number of arguments
! 9177: *
! 9178: * Implement the normalize-space() XPath function
! 9179: * string normalize-space(string?)
! 9180: * The normalize-space function returns the argument string with white
! 9181: * space normalized by stripping leading and trailing whitespace
! 9182: * and replacing sequences of whitespace characters by a single
! 9183: * space. Whitespace characters are the same allowed by the S production
! 9184: * in XML. If the argument is omitted, it defaults to the context
! 9185: * node converted to a string, in other words the value of the context node.
! 9186: */
! 9187: void
! 9188: xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9189: xmlXPathObjectPtr obj = NULL;
! 9190: xmlChar *source = NULL;
! 9191: xmlBufferPtr target;
! 9192: xmlChar blank;
! 9193:
! 9194: if (ctxt == NULL) return;
! 9195: if (nargs == 0) {
! 9196: /* Use current context node */
! 9197: valuePush(ctxt,
! 9198: xmlXPathCacheWrapString(ctxt->context,
! 9199: xmlXPathCastNodeToString(ctxt->context->node)));
! 9200: nargs = 1;
! 9201: }
! 9202:
! 9203: CHECK_ARITY(1);
! 9204: CAST_TO_STRING;
! 9205: CHECK_TYPE(XPATH_STRING);
! 9206: obj = valuePop(ctxt);
! 9207: source = obj->stringval;
! 9208:
! 9209: target = xmlBufferCreate();
! 9210: if (target && source) {
! 9211:
! 9212: /* Skip leading whitespaces */
! 9213: while (IS_BLANK_CH(*source))
! 9214: source++;
! 9215:
! 9216: /* Collapse intermediate whitespaces, and skip trailing whitespaces */
! 9217: blank = 0;
! 9218: while (*source) {
! 9219: if (IS_BLANK_CH(*source)) {
! 9220: blank = 0x20;
! 9221: } else {
! 9222: if (blank) {
! 9223: xmlBufferAdd(target, &blank, 1);
! 9224: blank = 0;
! 9225: }
! 9226: xmlBufferAdd(target, source, 1);
! 9227: }
! 9228: source++;
! 9229: }
! 9230: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
! 9231: xmlBufferContent(target)));
! 9232: xmlBufferFree(target);
! 9233: }
! 9234: xmlXPathReleaseObject(ctxt->context, obj);
! 9235: }
! 9236:
! 9237: /**
! 9238: * xmlXPathTranslateFunction:
! 9239: * @ctxt: the XPath Parser context
! 9240: * @nargs: the number of arguments
! 9241: *
! 9242: * Implement the translate() XPath function
! 9243: * string translate(string, string, string)
! 9244: * The translate function returns the first argument string with
! 9245: * occurrences of characters in the second argument string replaced
! 9246: * by the character at the corresponding position in the third argument
! 9247: * string. For example, translate("bar","abc","ABC") returns the string
! 9248: * BAr. If there is a character in the second argument string with no
! 9249: * character at a corresponding position in the third argument string
! 9250: * (because the second argument string is longer than the third argument
! 9251: * string), then occurrences of that character in the first argument
! 9252: * string are removed. For example, translate("--aaa--","abc-","ABC")
! 9253: * returns "AAA". If a character occurs more than once in second
! 9254: * argument string, then the first occurrence determines the replacement
! 9255: * character. If the third argument string is longer than the second
! 9256: * argument string, then excess characters are ignored.
! 9257: */
! 9258: void
! 9259: xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9260: xmlXPathObjectPtr str;
! 9261: xmlXPathObjectPtr from;
! 9262: xmlXPathObjectPtr to;
! 9263: xmlBufferPtr target;
! 9264: int offset, max;
! 9265: xmlChar ch;
! 9266: const xmlChar *point;
! 9267: xmlChar *cptr;
! 9268:
! 9269: CHECK_ARITY(3);
! 9270:
! 9271: CAST_TO_STRING;
! 9272: to = valuePop(ctxt);
! 9273: CAST_TO_STRING;
! 9274: from = valuePop(ctxt);
! 9275: CAST_TO_STRING;
! 9276: str = valuePop(ctxt);
! 9277:
! 9278: target = xmlBufferCreate();
! 9279: if (target) {
! 9280: max = xmlUTF8Strlen(to->stringval);
! 9281: for (cptr = str->stringval; (ch=*cptr); ) {
! 9282: offset = xmlUTF8Strloc(from->stringval, cptr);
! 9283: if (offset >= 0) {
! 9284: if (offset < max) {
! 9285: point = xmlUTF8Strpos(to->stringval, offset);
! 9286: if (point)
! 9287: xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1));
! 9288: }
! 9289: } else
! 9290: xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
! 9291:
! 9292: /* Step to next character in input */
! 9293: cptr++;
! 9294: if ( ch & 0x80 ) {
! 9295: /* if not simple ascii, verify proper format */
! 9296: if ( (ch & 0xc0) != 0xc0 ) {
! 9297: xmlGenericError(xmlGenericErrorContext,
! 9298: "xmlXPathTranslateFunction: Invalid UTF8 string\n");
! 9299: break;
! 9300: }
! 9301: /* then skip over remaining bytes for this char */
! 9302: while ( (ch <<= 1) & 0x80 )
! 9303: if ( (*cptr++ & 0xc0) != 0x80 ) {
! 9304: xmlGenericError(xmlGenericErrorContext,
! 9305: "xmlXPathTranslateFunction: Invalid UTF8 string\n");
! 9306: break;
! 9307: }
! 9308: if (ch & 0x80) /* must have had error encountered */
! 9309: break;
! 9310: }
! 9311: }
! 9312: }
! 9313: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
! 9314: xmlBufferContent(target)));
! 9315: xmlBufferFree(target);
! 9316: xmlXPathReleaseObject(ctxt->context, str);
! 9317: xmlXPathReleaseObject(ctxt->context, from);
! 9318: xmlXPathReleaseObject(ctxt->context, to);
! 9319: }
! 9320:
! 9321: /**
! 9322: * xmlXPathBooleanFunction:
! 9323: * @ctxt: the XPath Parser context
! 9324: * @nargs: the number of arguments
! 9325: *
! 9326: * Implement the boolean() XPath function
! 9327: * boolean boolean(object)
! 9328: * The boolean function converts its argument to a boolean as follows:
! 9329: * - a number is true if and only if it is neither positive or
! 9330: * negative zero nor NaN
! 9331: * - a node-set is true if and only if it is non-empty
! 9332: * - a string is true if and only if its length is non-zero
! 9333: */
! 9334: void
! 9335: xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9336: xmlXPathObjectPtr cur;
! 9337:
! 9338: CHECK_ARITY(1);
! 9339: cur = valuePop(ctxt);
! 9340: if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
! 9341: cur = xmlXPathCacheConvertBoolean(ctxt->context, cur);
! 9342: valuePush(ctxt, cur);
! 9343: }
! 9344:
! 9345: /**
! 9346: * xmlXPathNotFunction:
! 9347: * @ctxt: the XPath Parser context
! 9348: * @nargs: the number of arguments
! 9349: *
! 9350: * Implement the not() XPath function
! 9351: * boolean not(boolean)
! 9352: * The not function returns true if its argument is false,
! 9353: * and false otherwise.
! 9354: */
! 9355: void
! 9356: xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9357: CHECK_ARITY(1);
! 9358: CAST_TO_BOOLEAN;
! 9359: CHECK_TYPE(XPATH_BOOLEAN);
! 9360: ctxt->value->boolval = ! ctxt->value->boolval;
! 9361: }
! 9362:
! 9363: /**
! 9364: * xmlXPathTrueFunction:
! 9365: * @ctxt: the XPath Parser context
! 9366: * @nargs: the number of arguments
! 9367: *
! 9368: * Implement the true() XPath function
! 9369: * boolean true()
! 9370: */
! 9371: void
! 9372: xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9373: CHECK_ARITY(0);
! 9374: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
! 9375: }
! 9376:
! 9377: /**
! 9378: * xmlXPathFalseFunction:
! 9379: * @ctxt: the XPath Parser context
! 9380: * @nargs: the number of arguments
! 9381: *
! 9382: * Implement the false() XPath function
! 9383: * boolean false()
! 9384: */
! 9385: void
! 9386: xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9387: CHECK_ARITY(0);
! 9388: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
! 9389: }
! 9390:
! 9391: /**
! 9392: * xmlXPathLangFunction:
! 9393: * @ctxt: the XPath Parser context
! 9394: * @nargs: the number of arguments
! 9395: *
! 9396: * Implement the lang() XPath function
! 9397: * boolean lang(string)
! 9398: * The lang function returns true or false depending on whether the
! 9399: * language of the context node as specified by xml:lang attributes
! 9400: * is the same as or is a sublanguage of the language specified by
! 9401: * the argument string. The language of the context node is determined
! 9402: * by the value of the xml:lang attribute on the context node, or, if
! 9403: * the context node has no xml:lang attribute, by the value of the
! 9404: * xml:lang attribute on the nearest ancestor of the context node that
! 9405: * has an xml:lang attribute. If there is no such attribute, then lang
! 9406: * returns false. If there is such an attribute, then lang returns
! 9407: * true if the attribute value is equal to the argument ignoring case,
! 9408: * or if there is some suffix starting with - such that the attribute
! 9409: * value is equal to the argument ignoring that suffix of the attribute
! 9410: * value and ignoring case.
! 9411: */
! 9412: void
! 9413: xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9414: xmlXPathObjectPtr val = NULL;
! 9415: const xmlChar *theLang = NULL;
! 9416: const xmlChar *lang;
! 9417: int ret = 0;
! 9418: int i;
! 9419:
! 9420: CHECK_ARITY(1);
! 9421: CAST_TO_STRING;
! 9422: CHECK_TYPE(XPATH_STRING);
! 9423: val = valuePop(ctxt);
! 9424: lang = val->stringval;
! 9425: theLang = xmlNodeGetLang(ctxt->context->node);
! 9426: if ((theLang != NULL) && (lang != NULL)) {
! 9427: for (i = 0;lang[i] != 0;i++)
! 9428: if (toupper(lang[i]) != toupper(theLang[i]))
! 9429: goto not_equal;
! 9430: if ((theLang[i] == 0) || (theLang[i] == '-'))
! 9431: ret = 1;
! 9432: }
! 9433: not_equal:
! 9434: if (theLang != NULL)
! 9435: xmlFree((void *)theLang);
! 9436:
! 9437: xmlXPathReleaseObject(ctxt->context, val);
! 9438: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
! 9439: }
! 9440:
! 9441: /**
! 9442: * xmlXPathNumberFunction:
! 9443: * @ctxt: the XPath Parser context
! 9444: * @nargs: the number of arguments
! 9445: *
! 9446: * Implement the number() XPath function
! 9447: * number number(object?)
! 9448: */
! 9449: void
! 9450: xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9451: xmlXPathObjectPtr cur;
! 9452: double res;
! 9453:
! 9454: if (ctxt == NULL) return;
! 9455: if (nargs == 0) {
! 9456: if (ctxt->context->node == NULL) {
! 9457: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0.0));
! 9458: } else {
! 9459: xmlChar* content = xmlNodeGetContent(ctxt->context->node);
! 9460:
! 9461: res = xmlXPathStringEvalNumber(content);
! 9462: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
! 9463: xmlFree(content);
! 9464: }
! 9465: return;
! 9466: }
! 9467:
! 9468: CHECK_ARITY(1);
! 9469: cur = valuePop(ctxt);
! 9470: valuePush(ctxt, xmlXPathCacheConvertNumber(ctxt->context, cur));
! 9471: }
! 9472:
! 9473: /**
! 9474: * xmlXPathSumFunction:
! 9475: * @ctxt: the XPath Parser context
! 9476: * @nargs: the number of arguments
! 9477: *
! 9478: * Implement the sum() XPath function
! 9479: * number sum(node-set)
! 9480: * The sum function returns the sum of the values of the nodes in
! 9481: * the argument node-set.
! 9482: */
! 9483: void
! 9484: xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9485: xmlXPathObjectPtr cur;
! 9486: int i;
! 9487: double res = 0.0;
! 9488:
! 9489: CHECK_ARITY(1);
! 9490: if ((ctxt->value == NULL) ||
! 9491: ((ctxt->value->type != XPATH_NODESET) &&
! 9492: (ctxt->value->type != XPATH_XSLT_TREE)))
! 9493: XP_ERROR(XPATH_INVALID_TYPE);
! 9494: cur = valuePop(ctxt);
! 9495:
! 9496: if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) {
! 9497: for (i = 0; i < cur->nodesetval->nodeNr; i++) {
! 9498: res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
! 9499: }
! 9500: }
! 9501: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
! 9502: xmlXPathReleaseObject(ctxt->context, cur);
! 9503: }
! 9504:
! 9505: /*
! 9506: * To assure working code on multiple platforms, we want to only depend
! 9507: * upon the characteristic truncation of converting a floating point value
! 9508: * to an integer. Unfortunately, because of the different storage sizes
! 9509: * of our internal floating point value (double) and integer (int), we
! 9510: * can't directly convert (see bug 301162). This macro is a messy
! 9511: * 'workaround'
! 9512: */
! 9513: #define XTRUNC(f, v) \
! 9514: f = fmod((v), INT_MAX); \
! 9515: f = (v) - (f) + (double)((int)(f));
! 9516:
! 9517: /**
! 9518: * xmlXPathFloorFunction:
! 9519: * @ctxt: the XPath Parser context
! 9520: * @nargs: the number of arguments
! 9521: *
! 9522: * Implement the floor() XPath function
! 9523: * number floor(number)
! 9524: * The floor function returns the largest (closest to positive infinity)
! 9525: * number that is not greater than the argument and that is an integer.
! 9526: */
! 9527: void
! 9528: xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9529: double f;
! 9530:
! 9531: CHECK_ARITY(1);
! 9532: CAST_TO_NUMBER;
! 9533: CHECK_TYPE(XPATH_NUMBER);
! 9534:
! 9535: XTRUNC(f, ctxt->value->floatval);
! 9536: if (f != ctxt->value->floatval) {
! 9537: if (ctxt->value->floatval > 0)
! 9538: ctxt->value->floatval = f;
! 9539: else
! 9540: ctxt->value->floatval = f - 1;
! 9541: }
! 9542: }
! 9543:
! 9544: /**
! 9545: * xmlXPathCeilingFunction:
! 9546: * @ctxt: the XPath Parser context
! 9547: * @nargs: the number of arguments
! 9548: *
! 9549: * Implement the ceiling() XPath function
! 9550: * number ceiling(number)
! 9551: * The ceiling function returns the smallest (closest to negative infinity)
! 9552: * number that is not less than the argument and that is an integer.
! 9553: */
! 9554: void
! 9555: xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9556: double f;
! 9557:
! 9558: CHECK_ARITY(1);
! 9559: CAST_TO_NUMBER;
! 9560: CHECK_TYPE(XPATH_NUMBER);
! 9561:
! 9562: #if 0
! 9563: ctxt->value->floatval = ceil(ctxt->value->floatval);
! 9564: #else
! 9565: XTRUNC(f, ctxt->value->floatval);
! 9566: if (f != ctxt->value->floatval) {
! 9567: if (ctxt->value->floatval > 0)
! 9568: ctxt->value->floatval = f + 1;
! 9569: else {
! 9570: if (ctxt->value->floatval < 0 && f == 0)
! 9571: ctxt->value->floatval = xmlXPathNZERO;
! 9572: else
! 9573: ctxt->value->floatval = f;
! 9574: }
! 9575:
! 9576: }
! 9577: #endif
! 9578: }
! 9579:
! 9580: /**
! 9581: * xmlXPathRoundFunction:
! 9582: * @ctxt: the XPath Parser context
! 9583: * @nargs: the number of arguments
! 9584: *
! 9585: * Implement the round() XPath function
! 9586: * number round(number)
! 9587: * The round function returns the number that is closest to the
! 9588: * argument and that is an integer. If there are two such numbers,
! 9589: * then the one that is even is returned.
! 9590: */
! 9591: void
! 9592: xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 9593: double f;
! 9594:
! 9595: CHECK_ARITY(1);
! 9596: CAST_TO_NUMBER;
! 9597: CHECK_TYPE(XPATH_NUMBER);
! 9598:
! 9599: if ((xmlXPathIsNaN(ctxt->value->floatval)) ||
! 9600: (xmlXPathIsInf(ctxt->value->floatval) == 1) ||
! 9601: (xmlXPathIsInf(ctxt->value->floatval) == -1) ||
! 9602: (ctxt->value->floatval == 0.0))
! 9603: return;
! 9604:
! 9605: XTRUNC(f, ctxt->value->floatval);
! 9606: if (ctxt->value->floatval < 0) {
! 9607: if (ctxt->value->floatval < f - 0.5)
! 9608: ctxt->value->floatval = f - 1;
! 9609: else
! 9610: ctxt->value->floatval = f;
! 9611: if (ctxt->value->floatval == 0)
! 9612: ctxt->value->floatval = xmlXPathNZERO;
! 9613: } else {
! 9614: if (ctxt->value->floatval < f + 0.5)
! 9615: ctxt->value->floatval = f;
! 9616: else
! 9617: ctxt->value->floatval = f + 1;
! 9618: }
! 9619: }
! 9620:
! 9621: /************************************************************************
! 9622: * *
! 9623: * The Parser *
! 9624: * *
! 9625: ************************************************************************/
! 9626:
! 9627: /*
! 9628: * a few forward declarations since we use a recursive call based
! 9629: * implementation.
! 9630: */
! 9631: static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort);
! 9632: static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
! 9633: static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
! 9634: static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
! 9635: static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
! 9636: int qualified);
! 9637:
! 9638: /**
! 9639: * xmlXPathCurrentChar:
! 9640: * @ctxt: the XPath parser context
! 9641: * @cur: pointer to the beginning of the char
! 9642: * @len: pointer to the length of the char read
! 9643: *
! 9644: * The current char value, if using UTF-8 this may actually span multiple
! 9645: * bytes in the input buffer.
! 9646: *
! 9647: * Returns the current char value and its length
! 9648: */
! 9649:
! 9650: static int
! 9651: xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
! 9652: unsigned char c;
! 9653: unsigned int val;
! 9654: const xmlChar *cur;
! 9655:
! 9656: if (ctxt == NULL)
! 9657: return(0);
! 9658: cur = ctxt->cur;
! 9659:
! 9660: /*
! 9661: * We are supposed to handle UTF8, check it's valid
! 9662: * From rfc2044: encoding of the Unicode values on UTF-8:
! 9663: *
! 9664: * UCS-4 range (hex.) UTF-8 octet sequence (binary)
! 9665: * 0000 0000-0000 007F 0xxxxxxx
! 9666: * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
! 9667: * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
! 9668: *
! 9669: * Check for the 0x110000 limit too
! 9670: */
! 9671: c = *cur;
! 9672: if (c & 0x80) {
! 9673: if ((cur[1] & 0xc0) != 0x80)
! 9674: goto encoding_error;
! 9675: if ((c & 0xe0) == 0xe0) {
! 9676:
! 9677: if ((cur[2] & 0xc0) != 0x80)
! 9678: goto encoding_error;
! 9679: if ((c & 0xf0) == 0xf0) {
! 9680: if (((c & 0xf8) != 0xf0) ||
! 9681: ((cur[3] & 0xc0) != 0x80))
! 9682: goto encoding_error;
! 9683: /* 4-byte code */
! 9684: *len = 4;
! 9685: val = (cur[0] & 0x7) << 18;
! 9686: val |= (cur[1] & 0x3f) << 12;
! 9687: val |= (cur[2] & 0x3f) << 6;
! 9688: val |= cur[3] & 0x3f;
! 9689: } else {
! 9690: /* 3-byte code */
! 9691: *len = 3;
! 9692: val = (cur[0] & 0xf) << 12;
! 9693: val |= (cur[1] & 0x3f) << 6;
! 9694: val |= cur[2] & 0x3f;
! 9695: }
! 9696: } else {
! 9697: /* 2-byte code */
! 9698: *len = 2;
! 9699: val = (cur[0] & 0x1f) << 6;
! 9700: val |= cur[1] & 0x3f;
! 9701: }
! 9702: if (!IS_CHAR(val)) {
! 9703: XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
! 9704: }
! 9705: return(val);
! 9706: } else {
! 9707: /* 1-byte code */
! 9708: *len = 1;
! 9709: return((int) *cur);
! 9710: }
! 9711: encoding_error:
! 9712: /*
! 9713: * If we detect an UTF8 error that probably means that the
! 9714: * input encoding didn't get properly advertised in the
! 9715: * declaration header. Report the error and switch the encoding
! 9716: * to ISO-Latin-1 (if you don't like this policy, just declare the
! 9717: * encoding !)
! 9718: */
! 9719: *len = 0;
! 9720: XP_ERROR0(XPATH_ENCODING_ERROR);
! 9721: }
! 9722:
! 9723: /**
! 9724: * xmlXPathParseNCName:
! 9725: * @ctxt: the XPath Parser context
! 9726: *
! 9727: * parse an XML namespace non qualified name.
! 9728: *
! 9729: * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
! 9730: *
! 9731: * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
! 9732: * CombiningChar | Extender
! 9733: *
! 9734: * Returns the namespace name or NULL
! 9735: */
! 9736:
! 9737: xmlChar *
! 9738: xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
! 9739: const xmlChar *in;
! 9740: xmlChar *ret;
! 9741: int count = 0;
! 9742:
! 9743: if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
! 9744: /*
! 9745: * Accelerator for simple ASCII names
! 9746: */
! 9747: in = ctxt->cur;
! 9748: if (((*in >= 0x61) && (*in <= 0x7A)) ||
! 9749: ((*in >= 0x41) && (*in <= 0x5A)) ||
! 9750: (*in == '_')) {
! 9751: in++;
! 9752: while (((*in >= 0x61) && (*in <= 0x7A)) ||
! 9753: ((*in >= 0x41) && (*in <= 0x5A)) ||
! 9754: ((*in >= 0x30) && (*in <= 0x39)) ||
! 9755: (*in == '_') || (*in == '.') ||
! 9756: (*in == '-'))
! 9757: in++;
! 9758: if ((*in == ' ') || (*in == '>') || (*in == '/') ||
! 9759: (*in == '[') || (*in == ']') || (*in == ':') ||
! 9760: (*in == '@') || (*in == '*')) {
! 9761: count = in - ctxt->cur;
! 9762: if (count == 0)
! 9763: return(NULL);
! 9764: ret = xmlStrndup(ctxt->cur, count);
! 9765: ctxt->cur = in;
! 9766: return(ret);
! 9767: }
! 9768: }
! 9769: return(xmlXPathParseNameComplex(ctxt, 0));
! 9770: }
! 9771:
! 9772:
! 9773: /**
! 9774: * xmlXPathParseQName:
! 9775: * @ctxt: the XPath Parser context
! 9776: * @prefix: a xmlChar **
! 9777: *
! 9778: * parse an XML qualified name
! 9779: *
! 9780: * [NS 5] QName ::= (Prefix ':')? LocalPart
! 9781: *
! 9782: * [NS 6] Prefix ::= NCName
! 9783: *
! 9784: * [NS 7] LocalPart ::= NCName
! 9785: *
! 9786: * Returns the function returns the local part, and prefix is updated
! 9787: * to get the Prefix if any.
! 9788: */
! 9789:
! 9790: static xmlChar *
! 9791: xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
! 9792: xmlChar *ret = NULL;
! 9793:
! 9794: *prefix = NULL;
! 9795: ret = xmlXPathParseNCName(ctxt);
! 9796: if (ret && CUR == ':') {
! 9797: *prefix = ret;
! 9798: NEXT;
! 9799: ret = xmlXPathParseNCName(ctxt);
! 9800: }
! 9801: return(ret);
! 9802: }
! 9803:
! 9804: /**
! 9805: * xmlXPathParseName:
! 9806: * @ctxt: the XPath Parser context
! 9807: *
! 9808: * parse an XML name
! 9809: *
! 9810: * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
! 9811: * CombiningChar | Extender
! 9812: *
! 9813: * [5] Name ::= (Letter | '_' | ':') (NameChar)*
! 9814: *
! 9815: * Returns the namespace name or NULL
! 9816: */
! 9817:
! 9818: xmlChar *
! 9819: xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
! 9820: const xmlChar *in;
! 9821: xmlChar *ret;
! 9822: int count = 0;
! 9823:
! 9824: if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
! 9825: /*
! 9826: * Accelerator for simple ASCII names
! 9827: */
! 9828: in = ctxt->cur;
! 9829: if (((*in >= 0x61) && (*in <= 0x7A)) ||
! 9830: ((*in >= 0x41) && (*in <= 0x5A)) ||
! 9831: (*in == '_') || (*in == ':')) {
! 9832: in++;
! 9833: while (((*in >= 0x61) && (*in <= 0x7A)) ||
! 9834: ((*in >= 0x41) && (*in <= 0x5A)) ||
! 9835: ((*in >= 0x30) && (*in <= 0x39)) ||
! 9836: (*in == '_') || (*in == '-') ||
! 9837: (*in == ':') || (*in == '.'))
! 9838: in++;
! 9839: if ((*in > 0) && (*in < 0x80)) {
! 9840: count = in - ctxt->cur;
! 9841: ret = xmlStrndup(ctxt->cur, count);
! 9842: ctxt->cur = in;
! 9843: return(ret);
! 9844: }
! 9845: }
! 9846: return(xmlXPathParseNameComplex(ctxt, 1));
! 9847: }
! 9848:
! 9849: static xmlChar *
! 9850: xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
! 9851: xmlChar buf[XML_MAX_NAMELEN + 5];
! 9852: int len = 0, l;
! 9853: int c;
! 9854:
! 9855: /*
! 9856: * Handler for more complex cases
! 9857: */
! 9858: c = CUR_CHAR(l);
! 9859: if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
! 9860: (c == '[') || (c == ']') || (c == '@') || /* accelerators */
! 9861: (c == '*') || /* accelerators */
! 9862: (!IS_LETTER(c) && (c != '_') &&
! 9863: ((qualified) && (c != ':')))) {
! 9864: return(NULL);
! 9865: }
! 9866:
! 9867: while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
! 9868: ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
! 9869: (c == '.') || (c == '-') ||
! 9870: (c == '_') || ((qualified) && (c == ':')) ||
! 9871: (IS_COMBINING(c)) ||
! 9872: (IS_EXTENDER(c)))) {
! 9873: COPY_BUF(l,buf,len,c);
! 9874: NEXTL(l);
! 9875: c = CUR_CHAR(l);
! 9876: if (len >= XML_MAX_NAMELEN) {
! 9877: /*
! 9878: * Okay someone managed to make a huge name, so he's ready to pay
! 9879: * for the processing speed.
! 9880: */
! 9881: xmlChar *buffer;
! 9882: int max = len * 2;
! 9883:
! 9884: buffer = (xmlChar *) xmlMallocAtomic(max * sizeof(xmlChar));
! 9885: if (buffer == NULL) {
! 9886: XP_ERRORNULL(XPATH_MEMORY_ERROR);
! 9887: }
! 9888: memcpy(buffer, buf, len);
! 9889: while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
! 9890: (c == '.') || (c == '-') ||
! 9891: (c == '_') || ((qualified) && (c == ':')) ||
! 9892: (IS_COMBINING(c)) ||
! 9893: (IS_EXTENDER(c))) {
! 9894: if (len + 10 > max) {
! 9895: max *= 2;
! 9896: buffer = (xmlChar *) xmlRealloc(buffer,
! 9897: max * sizeof(xmlChar));
! 9898: if (buffer == NULL) {
! 9899: XP_ERRORNULL(XPATH_MEMORY_ERROR);
! 9900: }
! 9901: }
! 9902: COPY_BUF(l,buffer,len,c);
! 9903: NEXTL(l);
! 9904: c = CUR_CHAR(l);
! 9905: }
! 9906: buffer[len] = 0;
! 9907: return(buffer);
! 9908: }
! 9909: }
! 9910: if (len == 0)
! 9911: return(NULL);
! 9912: return(xmlStrndup(buf, len));
! 9913: }
! 9914:
! 9915: #define MAX_FRAC 20
! 9916:
! 9917: /*
! 9918: * These are used as divisors for the fractional part of a number.
! 9919: * Since the table includes 1.0 (representing '0' fractional digits),
! 9920: * it must be dimensioned at MAX_FRAC+1 (bug 133921)
! 9921: */
! 9922: static double my_pow10[MAX_FRAC+1] = {
! 9923: 1.0, 10.0, 100.0, 1000.0, 10000.0,
! 9924: 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0,
! 9925: 10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0,
! 9926: 100000000000000.0,
! 9927: 1000000000000000.0, 10000000000000000.0, 100000000000000000.0,
! 9928: 1000000000000000000.0, 10000000000000000000.0, 100000000000000000000.0
! 9929: };
! 9930:
! 9931: /**
! 9932: * xmlXPathStringEvalNumber:
! 9933: * @str: A string to scan
! 9934: *
! 9935: * [30a] Float ::= Number ('e' Digits?)?
! 9936: *
! 9937: * [30] Number ::= Digits ('.' Digits?)?
! 9938: * | '.' Digits
! 9939: * [31] Digits ::= [0-9]+
! 9940: *
! 9941: * Compile a Number in the string
! 9942: * In complement of the Number expression, this function also handles
! 9943: * negative values : '-' Number.
! 9944: *
! 9945: * Returns the double value.
! 9946: */
! 9947: double
! 9948: xmlXPathStringEvalNumber(const xmlChar *str) {
! 9949: const xmlChar *cur = str;
! 9950: double ret;
! 9951: int ok = 0;
! 9952: int isneg = 0;
! 9953: int exponent = 0;
! 9954: int is_exponent_negative = 0;
! 9955: #ifdef __GNUC__
! 9956: unsigned long tmp = 0;
! 9957: double temp;
! 9958: #endif
! 9959: if (cur == NULL) return(0);
! 9960: while (IS_BLANK_CH(*cur)) cur++;
! 9961: if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
! 9962: return(xmlXPathNAN);
! 9963: }
! 9964: if (*cur == '-') {
! 9965: isneg = 1;
! 9966: cur++;
! 9967: }
! 9968:
! 9969: #ifdef __GNUC__
! 9970: /*
! 9971: * tmp/temp is a workaround against a gcc compiler bug
! 9972: * http://veillard.com/gcc.bug
! 9973: */
! 9974: ret = 0;
! 9975: while ((*cur >= '0') && (*cur <= '9')) {
! 9976: ret = ret * 10;
! 9977: tmp = (*cur - '0');
! 9978: ok = 1;
! 9979: cur++;
! 9980: temp = (double) tmp;
! 9981: ret = ret + temp;
! 9982: }
! 9983: #else
! 9984: ret = 0;
! 9985: while ((*cur >= '0') && (*cur <= '9')) {
! 9986: ret = ret * 10 + (*cur - '0');
! 9987: ok = 1;
! 9988: cur++;
! 9989: }
! 9990: #endif
! 9991:
! 9992: if (*cur == '.') {
! 9993: int v, frac = 0;
! 9994: double fraction = 0;
! 9995:
! 9996: cur++;
! 9997: if (((*cur < '0') || (*cur > '9')) && (!ok)) {
! 9998: return(xmlXPathNAN);
! 9999: }
! 10000: while (((*cur >= '0') && (*cur <= '9')) && (frac < MAX_FRAC)) {
! 10001: v = (*cur - '0');
! 10002: fraction = fraction * 10 + v;
! 10003: frac = frac + 1;
! 10004: cur++;
! 10005: }
! 10006: fraction /= my_pow10[frac];
! 10007: ret = ret + fraction;
! 10008: while ((*cur >= '0') && (*cur <= '9'))
! 10009: cur++;
! 10010: }
! 10011: if ((*cur == 'e') || (*cur == 'E')) {
! 10012: cur++;
! 10013: if (*cur == '-') {
! 10014: is_exponent_negative = 1;
! 10015: cur++;
! 10016: } else if (*cur == '+') {
! 10017: cur++;
! 10018: }
! 10019: while ((*cur >= '0') && (*cur <= '9')) {
! 10020: exponent = exponent * 10 + (*cur - '0');
! 10021: cur++;
! 10022: }
! 10023: }
! 10024: while (IS_BLANK_CH(*cur)) cur++;
! 10025: if (*cur != 0) return(xmlXPathNAN);
! 10026: if (isneg) ret = -ret;
! 10027: if (is_exponent_negative) exponent = -exponent;
! 10028: ret *= pow(10.0, (double)exponent);
! 10029: return(ret);
! 10030: }
! 10031:
! 10032: /**
! 10033: * xmlXPathCompNumber:
! 10034: * @ctxt: the XPath Parser context
! 10035: *
! 10036: * [30] Number ::= Digits ('.' Digits?)?
! 10037: * | '.' Digits
! 10038: * [31] Digits ::= [0-9]+
! 10039: *
! 10040: * Compile a Number, then push it on the stack
! 10041: *
! 10042: */
! 10043: static void
! 10044: xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
! 10045: {
! 10046: double ret = 0.0;
! 10047: double mult = 1;
! 10048: int ok = 0;
! 10049: int exponent = 0;
! 10050: int is_exponent_negative = 0;
! 10051: #ifdef __GNUC__
! 10052: unsigned long tmp = 0;
! 10053: double temp;
! 10054: #endif
! 10055:
! 10056: CHECK_ERROR;
! 10057: if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
! 10058: XP_ERROR(XPATH_NUMBER_ERROR);
! 10059: }
! 10060: #ifdef __GNUC__
! 10061: /*
! 10062: * tmp/temp is a workaround against a gcc compiler bug
! 10063: * http://veillard.com/gcc.bug
! 10064: */
! 10065: ret = 0;
! 10066: while ((CUR >= '0') && (CUR <= '9')) {
! 10067: ret = ret * 10;
! 10068: tmp = (CUR - '0');
! 10069: ok = 1;
! 10070: NEXT;
! 10071: temp = (double) tmp;
! 10072: ret = ret + temp;
! 10073: }
! 10074: #else
! 10075: ret = 0;
! 10076: while ((CUR >= '0') && (CUR <= '9')) {
! 10077: ret = ret * 10 + (CUR - '0');
! 10078: ok = 1;
! 10079: NEXT;
! 10080: }
! 10081: #endif
! 10082: if (CUR == '.') {
! 10083: int v, frac = 0;
! 10084: double fraction = 0;
! 10085:
! 10086: NEXT;
! 10087: if (((CUR < '0') || (CUR > '9')) && (!ok)) {
! 10088: XP_ERROR(XPATH_NUMBER_ERROR);
! 10089: }
! 10090: while ((CUR >= '0') && (CUR <= '9') && (frac < MAX_FRAC)) {
! 10091: v = (CUR - '0');
! 10092: fraction = fraction * 10 + v;
! 10093: frac = frac + 1;
! 10094: NEXT;
! 10095: }
! 10096: fraction /= my_pow10[frac];
! 10097: ret = ret + fraction;
! 10098: while ((CUR >= '0') && (CUR <= '9'))
! 10099: NEXT;
! 10100: }
! 10101: if ((CUR == 'e') || (CUR == 'E')) {
! 10102: NEXT;
! 10103: if (CUR == '-') {
! 10104: is_exponent_negative = 1;
! 10105: NEXT;
! 10106: } else if (CUR == '+') {
! 10107: NEXT;
! 10108: }
! 10109: while ((CUR >= '0') && (CUR <= '9')) {
! 10110: exponent = exponent * 10 + (CUR - '0');
! 10111: NEXT;
! 10112: }
! 10113: if (is_exponent_negative)
! 10114: exponent = -exponent;
! 10115: ret *= pow(10.0, (double) exponent);
! 10116: }
! 10117: PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
! 10118: xmlXPathCacheNewFloat(ctxt->context, ret), NULL);
! 10119: }
! 10120:
! 10121: /**
! 10122: * xmlXPathParseLiteral:
! 10123: * @ctxt: the XPath Parser context
! 10124: *
! 10125: * Parse a Literal
! 10126: *
! 10127: * [29] Literal ::= '"' [^"]* '"'
! 10128: * | "'" [^']* "'"
! 10129: *
! 10130: * Returns the value found or NULL in case of error
! 10131: */
! 10132: static xmlChar *
! 10133: xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
! 10134: const xmlChar *q;
! 10135: xmlChar *ret = NULL;
! 10136:
! 10137: if (CUR == '"') {
! 10138: NEXT;
! 10139: q = CUR_PTR;
! 10140: while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
! 10141: NEXT;
! 10142: if (!IS_CHAR_CH(CUR)) {
! 10143: XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
! 10144: } else {
! 10145: ret = xmlStrndup(q, CUR_PTR - q);
! 10146: NEXT;
! 10147: }
! 10148: } else if (CUR == '\'') {
! 10149: NEXT;
! 10150: q = CUR_PTR;
! 10151: while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
! 10152: NEXT;
! 10153: if (!IS_CHAR_CH(CUR)) {
! 10154: XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
! 10155: } else {
! 10156: ret = xmlStrndup(q, CUR_PTR - q);
! 10157: NEXT;
! 10158: }
! 10159: } else {
! 10160: XP_ERRORNULL(XPATH_START_LITERAL_ERROR);
! 10161: }
! 10162: return(ret);
! 10163: }
! 10164:
! 10165: /**
! 10166: * xmlXPathCompLiteral:
! 10167: * @ctxt: the XPath Parser context
! 10168: *
! 10169: * Parse a Literal and push it on the stack.
! 10170: *
! 10171: * [29] Literal ::= '"' [^"]* '"'
! 10172: * | "'" [^']* "'"
! 10173: *
! 10174: * TODO: xmlXPathCompLiteral memory allocation could be improved.
! 10175: */
! 10176: static void
! 10177: xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
! 10178: const xmlChar *q;
! 10179: xmlChar *ret = NULL;
! 10180:
! 10181: if (CUR == '"') {
! 10182: NEXT;
! 10183: q = CUR_PTR;
! 10184: while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
! 10185: NEXT;
! 10186: if (!IS_CHAR_CH(CUR)) {
! 10187: XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
! 10188: } else {
! 10189: ret = xmlStrndup(q, CUR_PTR - q);
! 10190: NEXT;
! 10191: }
! 10192: } else if (CUR == '\'') {
! 10193: NEXT;
! 10194: q = CUR_PTR;
! 10195: while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
! 10196: NEXT;
! 10197: if (!IS_CHAR_CH(CUR)) {
! 10198: XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
! 10199: } else {
! 10200: ret = xmlStrndup(q, CUR_PTR - q);
! 10201: NEXT;
! 10202: }
! 10203: } else {
! 10204: XP_ERROR(XPATH_START_LITERAL_ERROR);
! 10205: }
! 10206: if (ret == NULL) return;
! 10207: PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
! 10208: xmlXPathCacheNewString(ctxt->context, ret), NULL);
! 10209: xmlFree(ret);
! 10210: }
! 10211:
! 10212: /**
! 10213: * xmlXPathCompVariableReference:
! 10214: * @ctxt: the XPath Parser context
! 10215: *
! 10216: * Parse a VariableReference, evaluate it and push it on the stack.
! 10217: *
! 10218: * The variable bindings consist of a mapping from variable names
! 10219: * to variable values. The value of a variable is an object, which can be
! 10220: * of any of the types that are possible for the value of an expression,
! 10221: * and may also be of additional types not specified here.
! 10222: *
! 10223: * Early evaluation is possible since:
! 10224: * The variable bindings [...] used to evaluate a subexpression are
! 10225: * always the same as those used to evaluate the containing expression.
! 10226: *
! 10227: * [36] VariableReference ::= '$' QName
! 10228: */
! 10229: static void
! 10230: xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
! 10231: xmlChar *name;
! 10232: xmlChar *prefix;
! 10233:
! 10234: SKIP_BLANKS;
! 10235: if (CUR != '$') {
! 10236: XP_ERROR(XPATH_VARIABLE_REF_ERROR);
! 10237: }
! 10238: NEXT;
! 10239: name = xmlXPathParseQName(ctxt, &prefix);
! 10240: if (name == NULL) {
! 10241: XP_ERROR(XPATH_VARIABLE_REF_ERROR);
! 10242: }
! 10243: ctxt->comp->last = -1;
! 10244: PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
! 10245: name, prefix);
! 10246: SKIP_BLANKS;
! 10247: if ((ctxt->context != NULL) && (ctxt->context->flags & XML_XPATH_NOVAR)) {
! 10248: XP_ERROR(XPATH_UNDEF_VARIABLE_ERROR);
! 10249: }
! 10250: }
! 10251:
! 10252: /**
! 10253: * xmlXPathIsNodeType:
! 10254: * @name: a name string
! 10255: *
! 10256: * Is the name given a NodeType one.
! 10257: *
! 10258: * [38] NodeType ::= 'comment'
! 10259: * | 'text'
! 10260: * | 'processing-instruction'
! 10261: * | 'node'
! 10262: *
! 10263: * Returns 1 if true 0 otherwise
! 10264: */
! 10265: int
! 10266: xmlXPathIsNodeType(const xmlChar *name) {
! 10267: if (name == NULL)
! 10268: return(0);
! 10269:
! 10270: if (xmlStrEqual(name, BAD_CAST "node"))
! 10271: return(1);
! 10272: if (xmlStrEqual(name, BAD_CAST "text"))
! 10273: return(1);
! 10274: if (xmlStrEqual(name, BAD_CAST "comment"))
! 10275: return(1);
! 10276: if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
! 10277: return(1);
! 10278: return(0);
! 10279: }
! 10280:
! 10281: /**
! 10282: * xmlXPathCompFunctionCall:
! 10283: * @ctxt: the XPath Parser context
! 10284: *
! 10285: * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
! 10286: * [17] Argument ::= Expr
! 10287: *
! 10288: * Compile a function call, the evaluation of all arguments are
! 10289: * pushed on the stack
! 10290: */
! 10291: static void
! 10292: xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
! 10293: xmlChar *name;
! 10294: xmlChar *prefix;
! 10295: int nbargs = 0;
! 10296: int sort = 1;
! 10297:
! 10298: name = xmlXPathParseQName(ctxt, &prefix);
! 10299: if (name == NULL) {
! 10300: xmlFree(prefix);
! 10301: XP_ERROR(XPATH_EXPR_ERROR);
! 10302: }
! 10303: SKIP_BLANKS;
! 10304: #ifdef DEBUG_EXPR
! 10305: if (prefix == NULL)
! 10306: xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
! 10307: name);
! 10308: else
! 10309: xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
! 10310: prefix, name);
! 10311: #endif
! 10312:
! 10313: if (CUR != '(') {
! 10314: XP_ERROR(XPATH_EXPR_ERROR);
! 10315: }
! 10316: NEXT;
! 10317: SKIP_BLANKS;
! 10318:
! 10319: /*
! 10320: * Optimization for count(): we don't need the node-set to be sorted.
! 10321: */
! 10322: if ((prefix == NULL) && (name[0] == 'c') &&
! 10323: xmlStrEqual(name, BAD_CAST "count"))
! 10324: {
! 10325: sort = 0;
! 10326: }
! 10327: ctxt->comp->last = -1;
! 10328: if (CUR != ')') {
! 10329: while (CUR != 0) {
! 10330: int op1 = ctxt->comp->last;
! 10331: ctxt->comp->last = -1;
! 10332: xmlXPathCompileExpr(ctxt, sort);
! 10333: if (ctxt->error != XPATH_EXPRESSION_OK) {
! 10334: xmlFree(name);
! 10335: xmlFree(prefix);
! 10336: return;
! 10337: }
! 10338: PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
! 10339: nbargs++;
! 10340: if (CUR == ')') break;
! 10341: if (CUR != ',') {
! 10342: XP_ERROR(XPATH_EXPR_ERROR);
! 10343: }
! 10344: NEXT;
! 10345: SKIP_BLANKS;
! 10346: }
! 10347: }
! 10348: PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
! 10349: name, prefix);
! 10350: NEXT;
! 10351: SKIP_BLANKS;
! 10352: }
! 10353:
! 10354: /**
! 10355: * xmlXPathCompPrimaryExpr:
! 10356: * @ctxt: the XPath Parser context
! 10357: *
! 10358: * [15] PrimaryExpr ::= VariableReference
! 10359: * | '(' Expr ')'
! 10360: * | Literal
! 10361: * | Number
! 10362: * | FunctionCall
! 10363: *
! 10364: * Compile a primary expression.
! 10365: */
! 10366: static void
! 10367: xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
! 10368: SKIP_BLANKS;
! 10369: if (CUR == '$') xmlXPathCompVariableReference(ctxt);
! 10370: else if (CUR == '(') {
! 10371: NEXT;
! 10372: SKIP_BLANKS;
! 10373: xmlXPathCompileExpr(ctxt, 1);
! 10374: CHECK_ERROR;
! 10375: if (CUR != ')') {
! 10376: XP_ERROR(XPATH_EXPR_ERROR);
! 10377: }
! 10378: NEXT;
! 10379: SKIP_BLANKS;
! 10380: } else if (IS_ASCII_DIGIT(CUR) || (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
! 10381: xmlXPathCompNumber(ctxt);
! 10382: } else if ((CUR == '\'') || (CUR == '"')) {
! 10383: xmlXPathCompLiteral(ctxt);
! 10384: } else {
! 10385: xmlXPathCompFunctionCall(ctxt);
! 10386: }
! 10387: SKIP_BLANKS;
! 10388: }
! 10389:
! 10390: /**
! 10391: * xmlXPathCompFilterExpr:
! 10392: * @ctxt: the XPath Parser context
! 10393: *
! 10394: * [20] FilterExpr ::= PrimaryExpr
! 10395: * | FilterExpr Predicate
! 10396: *
! 10397: * Compile a filter expression.
! 10398: * Square brackets are used to filter expressions in the same way that
! 10399: * they are used in location paths. It is an error if the expression to
! 10400: * be filtered does not evaluate to a node-set. The context node list
! 10401: * used for evaluating the expression in square brackets is the node-set
! 10402: * to be filtered listed in document order.
! 10403: */
! 10404:
! 10405: static void
! 10406: xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
! 10407: xmlXPathCompPrimaryExpr(ctxt);
! 10408: CHECK_ERROR;
! 10409: SKIP_BLANKS;
! 10410:
! 10411: while (CUR == '[') {
! 10412: xmlXPathCompPredicate(ctxt, 1);
! 10413: SKIP_BLANKS;
! 10414: }
! 10415:
! 10416:
! 10417: }
! 10418:
! 10419: /**
! 10420: * xmlXPathScanName:
! 10421: * @ctxt: the XPath Parser context
! 10422: *
! 10423: * Trickery: parse an XML name but without consuming the input flow
! 10424: * Needed to avoid insanity in the parser state.
! 10425: *
! 10426: * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
! 10427: * CombiningChar | Extender
! 10428: *
! 10429: * [5] Name ::= (Letter | '_' | ':') (NameChar)*
! 10430: *
! 10431: * [6] Names ::= Name (S Name)*
! 10432: *
! 10433: * Returns the Name parsed or NULL
! 10434: */
! 10435:
! 10436: static xmlChar *
! 10437: xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
! 10438: int len = 0, l;
! 10439: int c;
! 10440: const xmlChar *cur;
! 10441: xmlChar *ret;
! 10442:
! 10443: cur = ctxt->cur;
! 10444:
! 10445: c = CUR_CHAR(l);
! 10446: if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
! 10447: (!IS_LETTER(c) && (c != '_') &&
! 10448: (c != ':'))) {
! 10449: return(NULL);
! 10450: }
! 10451:
! 10452: while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
! 10453: ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
! 10454: (c == '.') || (c == '-') ||
! 10455: (c == '_') || (c == ':') ||
! 10456: (IS_COMBINING(c)) ||
! 10457: (IS_EXTENDER(c)))) {
! 10458: len += l;
! 10459: NEXTL(l);
! 10460: c = CUR_CHAR(l);
! 10461: }
! 10462: ret = xmlStrndup(cur, ctxt->cur - cur);
! 10463: ctxt->cur = cur;
! 10464: return(ret);
! 10465: }
! 10466:
! 10467: /**
! 10468: * xmlXPathCompPathExpr:
! 10469: * @ctxt: the XPath Parser context
! 10470: *
! 10471: * [19] PathExpr ::= LocationPath
! 10472: * | FilterExpr
! 10473: * | FilterExpr '/' RelativeLocationPath
! 10474: * | FilterExpr '//' RelativeLocationPath
! 10475: *
! 10476: * Compile a path expression.
! 10477: * The / operator and // operators combine an arbitrary expression
! 10478: * and a relative location path. It is an error if the expression
! 10479: * does not evaluate to a node-set.
! 10480: * The / operator does composition in the same way as when / is
! 10481: * used in a location path. As in location paths, // is short for
! 10482: * /descendant-or-self::node()/.
! 10483: */
! 10484:
! 10485: static void
! 10486: xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
! 10487: int lc = 1; /* Should we branch to LocationPath ? */
! 10488: xmlChar *name = NULL; /* we may have to preparse a name to find out */
! 10489:
! 10490: SKIP_BLANKS;
! 10491: if ((CUR == '$') || (CUR == '(') ||
! 10492: (IS_ASCII_DIGIT(CUR)) ||
! 10493: (CUR == '\'') || (CUR == '"') ||
! 10494: (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
! 10495: lc = 0;
! 10496: } else if (CUR == '*') {
! 10497: /* relative or absolute location path */
! 10498: lc = 1;
! 10499: } else if (CUR == '/') {
! 10500: /* relative or absolute location path */
! 10501: lc = 1;
! 10502: } else if (CUR == '@') {
! 10503: /* relative abbreviated attribute location path */
! 10504: lc = 1;
! 10505: } else if (CUR == '.') {
! 10506: /* relative abbreviated attribute location path */
! 10507: lc = 1;
! 10508: } else {
! 10509: /*
! 10510: * Problem is finding if we have a name here whether it's:
! 10511: * - a nodetype
! 10512: * - a function call in which case it's followed by '('
! 10513: * - an axis in which case it's followed by ':'
! 10514: * - a element name
! 10515: * We do an a priori analysis here rather than having to
! 10516: * maintain parsed token content through the recursive function
! 10517: * calls. This looks uglier but makes the code easier to
! 10518: * read/write/debug.
! 10519: */
! 10520: SKIP_BLANKS;
! 10521: name = xmlXPathScanName(ctxt);
! 10522: if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
! 10523: #ifdef DEBUG_STEP
! 10524: xmlGenericError(xmlGenericErrorContext,
! 10525: "PathExpr: Axis\n");
! 10526: #endif
! 10527: lc = 1;
! 10528: xmlFree(name);
! 10529: } else if (name != NULL) {
! 10530: int len =xmlStrlen(name);
! 10531:
! 10532:
! 10533: while (NXT(len) != 0) {
! 10534: if (NXT(len) == '/') {
! 10535: /* element name */
! 10536: #ifdef DEBUG_STEP
! 10537: xmlGenericError(xmlGenericErrorContext,
! 10538: "PathExpr: AbbrRelLocation\n");
! 10539: #endif
! 10540: lc = 1;
! 10541: break;
! 10542: } else if (IS_BLANK_CH(NXT(len))) {
! 10543: /* ignore blanks */
! 10544: ;
! 10545: } else if (NXT(len) == ':') {
! 10546: #ifdef DEBUG_STEP
! 10547: xmlGenericError(xmlGenericErrorContext,
! 10548: "PathExpr: AbbrRelLocation\n");
! 10549: #endif
! 10550: lc = 1;
! 10551: break;
! 10552: } else if ((NXT(len) == '(')) {
! 10553: /* Note Type or Function */
! 10554: if (xmlXPathIsNodeType(name)) {
! 10555: #ifdef DEBUG_STEP
! 10556: xmlGenericError(xmlGenericErrorContext,
! 10557: "PathExpr: Type search\n");
! 10558: #endif
! 10559: lc = 1;
! 10560: } else {
! 10561: #ifdef DEBUG_STEP
! 10562: xmlGenericError(xmlGenericErrorContext,
! 10563: "PathExpr: function call\n");
! 10564: #endif
! 10565: lc = 0;
! 10566: }
! 10567: break;
! 10568: } else if ((NXT(len) == '[')) {
! 10569: /* element name */
! 10570: #ifdef DEBUG_STEP
! 10571: xmlGenericError(xmlGenericErrorContext,
! 10572: "PathExpr: AbbrRelLocation\n");
! 10573: #endif
! 10574: lc = 1;
! 10575: break;
! 10576: } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
! 10577: (NXT(len) == '=')) {
! 10578: lc = 1;
! 10579: break;
! 10580: } else {
! 10581: lc = 1;
! 10582: break;
! 10583: }
! 10584: len++;
! 10585: }
! 10586: if (NXT(len) == 0) {
! 10587: #ifdef DEBUG_STEP
! 10588: xmlGenericError(xmlGenericErrorContext,
! 10589: "PathExpr: AbbrRelLocation\n");
! 10590: #endif
! 10591: /* element name */
! 10592: lc = 1;
! 10593: }
! 10594: xmlFree(name);
! 10595: } else {
! 10596: /* make sure all cases are covered explicitly */
! 10597: XP_ERROR(XPATH_EXPR_ERROR);
! 10598: }
! 10599: }
! 10600:
! 10601: if (lc) {
! 10602: if (CUR == '/') {
! 10603: PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
! 10604: } else {
! 10605: PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
! 10606: }
! 10607: xmlXPathCompLocationPath(ctxt);
! 10608: } else {
! 10609: xmlXPathCompFilterExpr(ctxt);
! 10610: CHECK_ERROR;
! 10611: if ((CUR == '/') && (NXT(1) == '/')) {
! 10612: SKIP(2);
! 10613: SKIP_BLANKS;
! 10614:
! 10615: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
! 10616: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
! 10617: PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
! 10618:
! 10619: xmlXPathCompRelativeLocationPath(ctxt);
! 10620: } else if (CUR == '/') {
! 10621: xmlXPathCompRelativeLocationPath(ctxt);
! 10622: }
! 10623: }
! 10624: SKIP_BLANKS;
! 10625: }
! 10626:
! 10627: /**
! 10628: * xmlXPathCompUnionExpr:
! 10629: * @ctxt: the XPath Parser context
! 10630: *
! 10631: * [18] UnionExpr ::= PathExpr
! 10632: * | UnionExpr '|' PathExpr
! 10633: *
! 10634: * Compile an union expression.
! 10635: */
! 10636:
! 10637: static void
! 10638: xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
! 10639: xmlXPathCompPathExpr(ctxt);
! 10640: CHECK_ERROR;
! 10641: SKIP_BLANKS;
! 10642: while (CUR == '|') {
! 10643: int op1 = ctxt->comp->last;
! 10644: PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
! 10645:
! 10646: NEXT;
! 10647: SKIP_BLANKS;
! 10648: xmlXPathCompPathExpr(ctxt);
! 10649:
! 10650: PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
! 10651:
! 10652: SKIP_BLANKS;
! 10653: }
! 10654: }
! 10655:
! 10656: /**
! 10657: * xmlXPathCompUnaryExpr:
! 10658: * @ctxt: the XPath Parser context
! 10659: *
! 10660: * [27] UnaryExpr ::= UnionExpr
! 10661: * | '-' UnaryExpr
! 10662: *
! 10663: * Compile an unary expression.
! 10664: */
! 10665:
! 10666: static void
! 10667: xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
! 10668: int minus = 0;
! 10669: int found = 0;
! 10670:
! 10671: SKIP_BLANKS;
! 10672: while (CUR == '-') {
! 10673: minus = 1 - minus;
! 10674: found = 1;
! 10675: NEXT;
! 10676: SKIP_BLANKS;
! 10677: }
! 10678:
! 10679: xmlXPathCompUnionExpr(ctxt);
! 10680: CHECK_ERROR;
! 10681: if (found) {
! 10682: if (minus)
! 10683: PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
! 10684: else
! 10685: PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
! 10686: }
! 10687: }
! 10688:
! 10689: /**
! 10690: * xmlXPathCompMultiplicativeExpr:
! 10691: * @ctxt: the XPath Parser context
! 10692: *
! 10693: * [26] MultiplicativeExpr ::= UnaryExpr
! 10694: * | MultiplicativeExpr MultiplyOperator UnaryExpr
! 10695: * | MultiplicativeExpr 'div' UnaryExpr
! 10696: * | MultiplicativeExpr 'mod' UnaryExpr
! 10697: * [34] MultiplyOperator ::= '*'
! 10698: *
! 10699: * Compile an Additive expression.
! 10700: */
! 10701:
! 10702: static void
! 10703: xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
! 10704: xmlXPathCompUnaryExpr(ctxt);
! 10705: CHECK_ERROR;
! 10706: SKIP_BLANKS;
! 10707: while ((CUR == '*') ||
! 10708: ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
! 10709: ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
! 10710: int op = -1;
! 10711: int op1 = ctxt->comp->last;
! 10712:
! 10713: if (CUR == '*') {
! 10714: op = 0;
! 10715: NEXT;
! 10716: } else if (CUR == 'd') {
! 10717: op = 1;
! 10718: SKIP(3);
! 10719: } else if (CUR == 'm') {
! 10720: op = 2;
! 10721: SKIP(3);
! 10722: }
! 10723: SKIP_BLANKS;
! 10724: xmlXPathCompUnaryExpr(ctxt);
! 10725: CHECK_ERROR;
! 10726: PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
! 10727: SKIP_BLANKS;
! 10728: }
! 10729: }
! 10730:
! 10731: /**
! 10732: * xmlXPathCompAdditiveExpr:
! 10733: * @ctxt: the XPath Parser context
! 10734: *
! 10735: * [25] AdditiveExpr ::= MultiplicativeExpr
! 10736: * | AdditiveExpr '+' MultiplicativeExpr
! 10737: * | AdditiveExpr '-' MultiplicativeExpr
! 10738: *
! 10739: * Compile an Additive expression.
! 10740: */
! 10741:
! 10742: static void
! 10743: xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
! 10744:
! 10745: xmlXPathCompMultiplicativeExpr(ctxt);
! 10746: CHECK_ERROR;
! 10747: SKIP_BLANKS;
! 10748: while ((CUR == '+') || (CUR == '-')) {
! 10749: int plus;
! 10750: int op1 = ctxt->comp->last;
! 10751:
! 10752: if (CUR == '+') plus = 1;
! 10753: else plus = 0;
! 10754: NEXT;
! 10755: SKIP_BLANKS;
! 10756: xmlXPathCompMultiplicativeExpr(ctxt);
! 10757: CHECK_ERROR;
! 10758: PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
! 10759: SKIP_BLANKS;
! 10760: }
! 10761: }
! 10762:
! 10763: /**
! 10764: * xmlXPathCompRelationalExpr:
! 10765: * @ctxt: the XPath Parser context
! 10766: *
! 10767: * [24] RelationalExpr ::= AdditiveExpr
! 10768: * | RelationalExpr '<' AdditiveExpr
! 10769: * | RelationalExpr '>' AdditiveExpr
! 10770: * | RelationalExpr '<=' AdditiveExpr
! 10771: * | RelationalExpr '>=' AdditiveExpr
! 10772: *
! 10773: * A <= B > C is allowed ? Answer from James, yes with
! 10774: * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
! 10775: * which is basically what got implemented.
! 10776: *
! 10777: * Compile a Relational expression, then push the result
! 10778: * on the stack
! 10779: */
! 10780:
! 10781: static void
! 10782: xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
! 10783: xmlXPathCompAdditiveExpr(ctxt);
! 10784: CHECK_ERROR;
! 10785: SKIP_BLANKS;
! 10786: while ((CUR == '<') ||
! 10787: (CUR == '>') ||
! 10788: ((CUR == '<') && (NXT(1) == '=')) ||
! 10789: ((CUR == '>') && (NXT(1) == '='))) {
! 10790: int inf, strict;
! 10791: int op1 = ctxt->comp->last;
! 10792:
! 10793: if (CUR == '<') inf = 1;
! 10794: else inf = 0;
! 10795: if (NXT(1) == '=') strict = 0;
! 10796: else strict = 1;
! 10797: NEXT;
! 10798: if (!strict) NEXT;
! 10799: SKIP_BLANKS;
! 10800: xmlXPathCompAdditiveExpr(ctxt);
! 10801: CHECK_ERROR;
! 10802: PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
! 10803: SKIP_BLANKS;
! 10804: }
! 10805: }
! 10806:
! 10807: /**
! 10808: * xmlXPathCompEqualityExpr:
! 10809: * @ctxt: the XPath Parser context
! 10810: *
! 10811: * [23] EqualityExpr ::= RelationalExpr
! 10812: * | EqualityExpr '=' RelationalExpr
! 10813: * | EqualityExpr '!=' RelationalExpr
! 10814: *
! 10815: * A != B != C is allowed ? Answer from James, yes with
! 10816: * (RelationalExpr = RelationalExpr) = RelationalExpr
! 10817: * (RelationalExpr != RelationalExpr) != RelationalExpr
! 10818: * which is basically what got implemented.
! 10819: *
! 10820: * Compile an Equality expression.
! 10821: *
! 10822: */
! 10823: static void
! 10824: xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
! 10825: xmlXPathCompRelationalExpr(ctxt);
! 10826: CHECK_ERROR;
! 10827: SKIP_BLANKS;
! 10828: while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
! 10829: int eq;
! 10830: int op1 = ctxt->comp->last;
! 10831:
! 10832: if (CUR == '=') eq = 1;
! 10833: else eq = 0;
! 10834: NEXT;
! 10835: if (!eq) NEXT;
! 10836: SKIP_BLANKS;
! 10837: xmlXPathCompRelationalExpr(ctxt);
! 10838: CHECK_ERROR;
! 10839: PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
! 10840: SKIP_BLANKS;
! 10841: }
! 10842: }
! 10843:
! 10844: /**
! 10845: * xmlXPathCompAndExpr:
! 10846: * @ctxt: the XPath Parser context
! 10847: *
! 10848: * [22] AndExpr ::= EqualityExpr
! 10849: * | AndExpr 'and' EqualityExpr
! 10850: *
! 10851: * Compile an AND expression.
! 10852: *
! 10853: */
! 10854: static void
! 10855: xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
! 10856: xmlXPathCompEqualityExpr(ctxt);
! 10857: CHECK_ERROR;
! 10858: SKIP_BLANKS;
! 10859: while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
! 10860: int op1 = ctxt->comp->last;
! 10861: SKIP(3);
! 10862: SKIP_BLANKS;
! 10863: xmlXPathCompEqualityExpr(ctxt);
! 10864: CHECK_ERROR;
! 10865: PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
! 10866: SKIP_BLANKS;
! 10867: }
! 10868: }
! 10869:
! 10870: /**
! 10871: * xmlXPathCompileExpr:
! 10872: * @ctxt: the XPath Parser context
! 10873: *
! 10874: * [14] Expr ::= OrExpr
! 10875: * [21] OrExpr ::= AndExpr
! 10876: * | OrExpr 'or' AndExpr
! 10877: *
! 10878: * Parse and compile an expression
! 10879: */
! 10880: static void
! 10881: xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
! 10882: xmlXPathCompAndExpr(ctxt);
! 10883: CHECK_ERROR;
! 10884: SKIP_BLANKS;
! 10885: while ((CUR == 'o') && (NXT(1) == 'r')) {
! 10886: int op1 = ctxt->comp->last;
! 10887: SKIP(2);
! 10888: SKIP_BLANKS;
! 10889: xmlXPathCompAndExpr(ctxt);
! 10890: CHECK_ERROR;
! 10891: PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
! 10892: SKIP_BLANKS;
! 10893: }
! 10894: if ((sort) && (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE)) {
! 10895: /* more ops could be optimized too */
! 10896: /*
! 10897: * This is the main place to eliminate sorting for
! 10898: * operations which don't require a sorted node-set.
! 10899: * E.g. count().
! 10900: */
! 10901: PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
! 10902: }
! 10903: }
! 10904:
! 10905: /**
! 10906: * xmlXPathCompPredicate:
! 10907: * @ctxt: the XPath Parser context
! 10908: * @filter: act as a filter
! 10909: *
! 10910: * [8] Predicate ::= '[' PredicateExpr ']'
! 10911: * [9] PredicateExpr ::= Expr
! 10912: *
! 10913: * Compile a predicate expression
! 10914: */
! 10915: static void
! 10916: xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
! 10917: int op1 = ctxt->comp->last;
! 10918:
! 10919: SKIP_BLANKS;
! 10920: if (CUR != '[') {
! 10921: XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
! 10922: }
! 10923: NEXT;
! 10924: SKIP_BLANKS;
! 10925:
! 10926: ctxt->comp->last = -1;
! 10927: /*
! 10928: * This call to xmlXPathCompileExpr() will deactivate sorting
! 10929: * of the predicate result.
! 10930: * TODO: Sorting is still activated for filters, since I'm not
! 10931: * sure if needed. Normally sorting should not be needed, since
! 10932: * a filter can only diminish the number of items in a sequence,
! 10933: * but won't change its order; so if the initial sequence is sorted,
! 10934: * subsequent sorting is not needed.
! 10935: */
! 10936: if (! filter)
! 10937: xmlXPathCompileExpr(ctxt, 0);
! 10938: else
! 10939: xmlXPathCompileExpr(ctxt, 1);
! 10940: CHECK_ERROR;
! 10941:
! 10942: if (CUR != ']') {
! 10943: XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
! 10944: }
! 10945:
! 10946: if (filter)
! 10947: PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
! 10948: else
! 10949: PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
! 10950:
! 10951: NEXT;
! 10952: SKIP_BLANKS;
! 10953: }
! 10954:
! 10955: /**
! 10956: * xmlXPathCompNodeTest:
! 10957: * @ctxt: the XPath Parser context
! 10958: * @test: pointer to a xmlXPathTestVal
! 10959: * @type: pointer to a xmlXPathTypeVal
! 10960: * @prefix: placeholder for a possible name prefix
! 10961: *
! 10962: * [7] NodeTest ::= NameTest
! 10963: * | NodeType '(' ')'
! 10964: * | 'processing-instruction' '(' Literal ')'
! 10965: *
! 10966: * [37] NameTest ::= '*'
! 10967: * | NCName ':' '*'
! 10968: * | QName
! 10969: * [38] NodeType ::= 'comment'
! 10970: * | 'text'
! 10971: * | 'processing-instruction'
! 10972: * | 'node'
! 10973: *
! 10974: * Returns the name found and updates @test, @type and @prefix appropriately
! 10975: */
! 10976: static xmlChar *
! 10977: xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
! 10978: xmlXPathTypeVal *type, const xmlChar **prefix,
! 10979: xmlChar *name) {
! 10980: int blanks;
! 10981:
! 10982: if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
! 10983: STRANGE;
! 10984: return(NULL);
! 10985: }
! 10986: *type = (xmlXPathTypeVal) 0;
! 10987: *test = (xmlXPathTestVal) 0;
! 10988: *prefix = NULL;
! 10989: SKIP_BLANKS;
! 10990:
! 10991: if ((name == NULL) && (CUR == '*')) {
! 10992: /*
! 10993: * All elements
! 10994: */
! 10995: NEXT;
! 10996: *test = NODE_TEST_ALL;
! 10997: return(NULL);
! 10998: }
! 10999:
! 11000: if (name == NULL)
! 11001: name = xmlXPathParseNCName(ctxt);
! 11002: if (name == NULL) {
! 11003: XP_ERRORNULL(XPATH_EXPR_ERROR);
! 11004: }
! 11005:
! 11006: blanks = IS_BLANK_CH(CUR);
! 11007: SKIP_BLANKS;
! 11008: if (CUR == '(') {
! 11009: NEXT;
! 11010: /*
! 11011: * NodeType or PI search
! 11012: */
! 11013: if (xmlStrEqual(name, BAD_CAST "comment"))
! 11014: *type = NODE_TYPE_COMMENT;
! 11015: else if (xmlStrEqual(name, BAD_CAST "node"))
! 11016: *type = NODE_TYPE_NODE;
! 11017: else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
! 11018: *type = NODE_TYPE_PI;
! 11019: else if (xmlStrEqual(name, BAD_CAST "text"))
! 11020: *type = NODE_TYPE_TEXT;
! 11021: else {
! 11022: if (name != NULL)
! 11023: xmlFree(name);
! 11024: XP_ERRORNULL(XPATH_EXPR_ERROR);
! 11025: }
! 11026:
! 11027: *test = NODE_TEST_TYPE;
! 11028:
! 11029: SKIP_BLANKS;
! 11030: if (*type == NODE_TYPE_PI) {
! 11031: /*
! 11032: * Specific case: search a PI by name.
! 11033: */
! 11034: if (name != NULL)
! 11035: xmlFree(name);
! 11036: name = NULL;
! 11037: if (CUR != ')') {
! 11038: name = xmlXPathParseLiteral(ctxt);
! 11039: CHECK_ERROR NULL;
! 11040: *test = NODE_TEST_PI;
! 11041: SKIP_BLANKS;
! 11042: }
! 11043: }
! 11044: if (CUR != ')') {
! 11045: if (name != NULL)
! 11046: xmlFree(name);
! 11047: XP_ERRORNULL(XPATH_UNCLOSED_ERROR);
! 11048: }
! 11049: NEXT;
! 11050: return(name);
! 11051: }
! 11052: *test = NODE_TEST_NAME;
! 11053: if ((!blanks) && (CUR == ':')) {
! 11054: NEXT;
! 11055:
! 11056: /*
! 11057: * Since currently the parser context don't have a
! 11058: * namespace list associated:
! 11059: * The namespace name for this prefix can be computed
! 11060: * only at evaluation time. The compilation is done
! 11061: * outside of any context.
! 11062: */
! 11063: #if 0
! 11064: *prefix = xmlXPathNsLookup(ctxt->context, name);
! 11065: if (name != NULL)
! 11066: xmlFree(name);
! 11067: if (*prefix == NULL) {
! 11068: XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
! 11069: }
! 11070: #else
! 11071: *prefix = name;
! 11072: #endif
! 11073:
! 11074: if (CUR == '*') {
! 11075: /*
! 11076: * All elements
! 11077: */
! 11078: NEXT;
! 11079: *test = NODE_TEST_ALL;
! 11080: return(NULL);
! 11081: }
! 11082:
! 11083: name = xmlXPathParseNCName(ctxt);
! 11084: if (name == NULL) {
! 11085: XP_ERRORNULL(XPATH_EXPR_ERROR);
! 11086: }
! 11087: }
! 11088: return(name);
! 11089: }
! 11090:
! 11091: /**
! 11092: * xmlXPathIsAxisName:
! 11093: * @name: a preparsed name token
! 11094: *
! 11095: * [6] AxisName ::= 'ancestor'
! 11096: * | 'ancestor-or-self'
! 11097: * | 'attribute'
! 11098: * | 'child'
! 11099: * | 'descendant'
! 11100: * | 'descendant-or-self'
! 11101: * | 'following'
! 11102: * | 'following-sibling'
! 11103: * | 'namespace'
! 11104: * | 'parent'
! 11105: * | 'preceding'
! 11106: * | 'preceding-sibling'
! 11107: * | 'self'
! 11108: *
! 11109: * Returns the axis or 0
! 11110: */
! 11111: static xmlXPathAxisVal
! 11112: xmlXPathIsAxisName(const xmlChar *name) {
! 11113: xmlXPathAxisVal ret = (xmlXPathAxisVal) 0;
! 11114: switch (name[0]) {
! 11115: case 'a':
! 11116: if (xmlStrEqual(name, BAD_CAST "ancestor"))
! 11117: ret = AXIS_ANCESTOR;
! 11118: if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
! 11119: ret = AXIS_ANCESTOR_OR_SELF;
! 11120: if (xmlStrEqual(name, BAD_CAST "attribute"))
! 11121: ret = AXIS_ATTRIBUTE;
! 11122: break;
! 11123: case 'c':
! 11124: if (xmlStrEqual(name, BAD_CAST "child"))
! 11125: ret = AXIS_CHILD;
! 11126: break;
! 11127: case 'd':
! 11128: if (xmlStrEqual(name, BAD_CAST "descendant"))
! 11129: ret = AXIS_DESCENDANT;
! 11130: if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
! 11131: ret = AXIS_DESCENDANT_OR_SELF;
! 11132: break;
! 11133: case 'f':
! 11134: if (xmlStrEqual(name, BAD_CAST "following"))
! 11135: ret = AXIS_FOLLOWING;
! 11136: if (xmlStrEqual(name, BAD_CAST "following-sibling"))
! 11137: ret = AXIS_FOLLOWING_SIBLING;
! 11138: break;
! 11139: case 'n':
! 11140: if (xmlStrEqual(name, BAD_CAST "namespace"))
! 11141: ret = AXIS_NAMESPACE;
! 11142: break;
! 11143: case 'p':
! 11144: if (xmlStrEqual(name, BAD_CAST "parent"))
! 11145: ret = AXIS_PARENT;
! 11146: if (xmlStrEqual(name, BAD_CAST "preceding"))
! 11147: ret = AXIS_PRECEDING;
! 11148: if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
! 11149: ret = AXIS_PRECEDING_SIBLING;
! 11150: break;
! 11151: case 's':
! 11152: if (xmlStrEqual(name, BAD_CAST "self"))
! 11153: ret = AXIS_SELF;
! 11154: break;
! 11155: }
! 11156: return(ret);
! 11157: }
! 11158:
! 11159: /**
! 11160: * xmlXPathCompStep:
! 11161: * @ctxt: the XPath Parser context
! 11162: *
! 11163: * [4] Step ::= AxisSpecifier NodeTest Predicate*
! 11164: * | AbbreviatedStep
! 11165: *
! 11166: * [12] AbbreviatedStep ::= '.' | '..'
! 11167: *
! 11168: * [5] AxisSpecifier ::= AxisName '::'
! 11169: * | AbbreviatedAxisSpecifier
! 11170: *
! 11171: * [13] AbbreviatedAxisSpecifier ::= '@'?
! 11172: *
! 11173: * Modified for XPtr range support as:
! 11174: *
! 11175: * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
! 11176: * | AbbreviatedStep
! 11177: * | 'range-to' '(' Expr ')' Predicate*
! 11178: *
! 11179: * Compile one step in a Location Path
! 11180: * A location step of . is short for self::node(). This is
! 11181: * particularly useful in conjunction with //. For example, the
! 11182: * location path .//para is short for
! 11183: * self::node()/descendant-or-self::node()/child::para
! 11184: * and so will select all para descendant elements of the context
! 11185: * node.
! 11186: * Similarly, a location step of .. is short for parent::node().
! 11187: * For example, ../title is short for parent::node()/child::title
! 11188: * and so will select the title children of the parent of the context
! 11189: * node.
! 11190: */
! 11191: static void
! 11192: xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
! 11193: #ifdef LIBXML_XPTR_ENABLED
! 11194: int rangeto = 0;
! 11195: int op2 = -1;
! 11196: #endif
! 11197:
! 11198: SKIP_BLANKS;
! 11199: if ((CUR == '.') && (NXT(1) == '.')) {
! 11200: SKIP(2);
! 11201: SKIP_BLANKS;
! 11202: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
! 11203: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
! 11204: } else if (CUR == '.') {
! 11205: NEXT;
! 11206: SKIP_BLANKS;
! 11207: } else {
! 11208: xmlChar *name = NULL;
! 11209: const xmlChar *prefix = NULL;
! 11210: xmlXPathTestVal test = (xmlXPathTestVal) 0;
! 11211: xmlXPathAxisVal axis = (xmlXPathAxisVal) 0;
! 11212: xmlXPathTypeVal type = (xmlXPathTypeVal) 0;
! 11213: int op1;
! 11214:
! 11215: /*
! 11216: * The modification needed for XPointer change to the production
! 11217: */
! 11218: #ifdef LIBXML_XPTR_ENABLED
! 11219: if (ctxt->xptr) {
! 11220: name = xmlXPathParseNCName(ctxt);
! 11221: if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
! 11222: op2 = ctxt->comp->last;
! 11223: xmlFree(name);
! 11224: SKIP_BLANKS;
! 11225: if (CUR != '(') {
! 11226: XP_ERROR(XPATH_EXPR_ERROR);
! 11227: }
! 11228: NEXT;
! 11229: SKIP_BLANKS;
! 11230:
! 11231: xmlXPathCompileExpr(ctxt, 1);
! 11232: /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
! 11233: CHECK_ERROR;
! 11234:
! 11235: SKIP_BLANKS;
! 11236: if (CUR != ')') {
! 11237: XP_ERROR(XPATH_EXPR_ERROR);
! 11238: }
! 11239: NEXT;
! 11240: rangeto = 1;
! 11241: goto eval_predicates;
! 11242: }
! 11243: }
! 11244: #endif
! 11245: if (CUR == '*') {
! 11246: axis = AXIS_CHILD;
! 11247: } else {
! 11248: if (name == NULL)
! 11249: name = xmlXPathParseNCName(ctxt);
! 11250: if (name != NULL) {
! 11251: axis = xmlXPathIsAxisName(name);
! 11252: if (axis != 0) {
! 11253: SKIP_BLANKS;
! 11254: if ((CUR == ':') && (NXT(1) == ':')) {
! 11255: SKIP(2);
! 11256: xmlFree(name);
! 11257: name = NULL;
! 11258: } else {
! 11259: /* an element name can conflict with an axis one :-\ */
! 11260: axis = AXIS_CHILD;
! 11261: }
! 11262: } else {
! 11263: axis = AXIS_CHILD;
! 11264: }
! 11265: } else if (CUR == '@') {
! 11266: NEXT;
! 11267: axis = AXIS_ATTRIBUTE;
! 11268: } else {
! 11269: axis = AXIS_CHILD;
! 11270: }
! 11271: }
! 11272:
! 11273: if (ctxt->error != XPATH_EXPRESSION_OK) {
! 11274: xmlFree(name);
! 11275: return;
! 11276: }
! 11277:
! 11278: name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
! 11279: if (test == 0)
! 11280: return;
! 11281:
! 11282: if ((prefix != NULL) && (ctxt->context != NULL) &&
! 11283: (ctxt->context->flags & XML_XPATH_CHECKNS)) {
! 11284: if (xmlXPathNsLookup(ctxt->context, prefix) == NULL) {
! 11285: xmlXPathErr(ctxt, XPATH_UNDEF_PREFIX_ERROR);
! 11286: }
! 11287: }
! 11288: #ifdef DEBUG_STEP
! 11289: xmlGenericError(xmlGenericErrorContext,
! 11290: "Basis : computing new set\n");
! 11291: #endif
! 11292:
! 11293: #ifdef DEBUG_STEP
! 11294: xmlGenericError(xmlGenericErrorContext, "Basis : ");
! 11295: if (ctxt->value == NULL)
! 11296: xmlGenericError(xmlGenericErrorContext, "no value\n");
! 11297: else if (ctxt->value->nodesetval == NULL)
! 11298: xmlGenericError(xmlGenericErrorContext, "Empty\n");
! 11299: else
! 11300: xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
! 11301: #endif
! 11302:
! 11303: #ifdef LIBXML_XPTR_ENABLED
! 11304: eval_predicates:
! 11305: #endif
! 11306: op1 = ctxt->comp->last;
! 11307: ctxt->comp->last = -1;
! 11308:
! 11309: SKIP_BLANKS;
! 11310: while (CUR == '[') {
! 11311: xmlXPathCompPredicate(ctxt, 0);
! 11312: }
! 11313:
! 11314: #ifdef LIBXML_XPTR_ENABLED
! 11315: if (rangeto) {
! 11316: PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
! 11317: } else
! 11318: #endif
! 11319: PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
! 11320: test, type, (void *)prefix, (void *)name);
! 11321:
! 11322: }
! 11323: #ifdef DEBUG_STEP
! 11324: xmlGenericError(xmlGenericErrorContext, "Step : ");
! 11325: if (ctxt->value == NULL)
! 11326: xmlGenericError(xmlGenericErrorContext, "no value\n");
! 11327: else if (ctxt->value->nodesetval == NULL)
! 11328: xmlGenericError(xmlGenericErrorContext, "Empty\n");
! 11329: else
! 11330: xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
! 11331: ctxt->value->nodesetval);
! 11332: #endif
! 11333: }
! 11334:
! 11335: /**
! 11336: * xmlXPathCompRelativeLocationPath:
! 11337: * @ctxt: the XPath Parser context
! 11338: *
! 11339: * [3] RelativeLocationPath ::= Step
! 11340: * | RelativeLocationPath '/' Step
! 11341: * | AbbreviatedRelativeLocationPath
! 11342: * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
! 11343: *
! 11344: * Compile a relative location path.
! 11345: */
! 11346: static void
! 11347: xmlXPathCompRelativeLocationPath
! 11348: (xmlXPathParserContextPtr ctxt) {
! 11349: SKIP_BLANKS;
! 11350: if ((CUR == '/') && (NXT(1) == '/')) {
! 11351: SKIP(2);
! 11352: SKIP_BLANKS;
! 11353: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
! 11354: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
! 11355: } else if (CUR == '/') {
! 11356: NEXT;
! 11357: SKIP_BLANKS;
! 11358: }
! 11359: xmlXPathCompStep(ctxt);
! 11360: CHECK_ERROR;
! 11361: SKIP_BLANKS;
! 11362: while (CUR == '/') {
! 11363: if ((CUR == '/') && (NXT(1) == '/')) {
! 11364: SKIP(2);
! 11365: SKIP_BLANKS;
! 11366: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
! 11367: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
! 11368: xmlXPathCompStep(ctxt);
! 11369: } else if (CUR == '/') {
! 11370: NEXT;
! 11371: SKIP_BLANKS;
! 11372: xmlXPathCompStep(ctxt);
! 11373: }
! 11374: SKIP_BLANKS;
! 11375: }
! 11376: }
! 11377:
! 11378: /**
! 11379: * xmlXPathCompLocationPath:
! 11380: * @ctxt: the XPath Parser context
! 11381: *
! 11382: * [1] LocationPath ::= RelativeLocationPath
! 11383: * | AbsoluteLocationPath
! 11384: * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
! 11385: * | AbbreviatedAbsoluteLocationPath
! 11386: * [10] AbbreviatedAbsoluteLocationPath ::=
! 11387: * '//' RelativeLocationPath
! 11388: *
! 11389: * Compile a location path
! 11390: *
! 11391: * // is short for /descendant-or-self::node()/. For example,
! 11392: * //para is short for /descendant-or-self::node()/child::para and
! 11393: * so will select any para element in the document (even a para element
! 11394: * that is a document element will be selected by //para since the
! 11395: * document element node is a child of the root node); div//para is
! 11396: * short for div/descendant-or-self::node()/child::para and so will
! 11397: * select all para descendants of div children.
! 11398: */
! 11399: static void
! 11400: xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
! 11401: SKIP_BLANKS;
! 11402: if (CUR != '/') {
! 11403: xmlXPathCompRelativeLocationPath(ctxt);
! 11404: } else {
! 11405: while (CUR == '/') {
! 11406: if ((CUR == '/') && (NXT(1) == '/')) {
! 11407: SKIP(2);
! 11408: SKIP_BLANKS;
! 11409: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
! 11410: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
! 11411: xmlXPathCompRelativeLocationPath(ctxt);
! 11412: } else if (CUR == '/') {
! 11413: NEXT;
! 11414: SKIP_BLANKS;
! 11415: if ((CUR != 0 ) &&
! 11416: ((IS_ASCII_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
! 11417: (CUR == '@') || (CUR == '*')))
! 11418: xmlXPathCompRelativeLocationPath(ctxt);
! 11419: }
! 11420: CHECK_ERROR;
! 11421: }
! 11422: }
! 11423: }
! 11424:
! 11425: /************************************************************************
! 11426: * *
! 11427: * XPath precompiled expression evaluation *
! 11428: * *
! 11429: ************************************************************************/
! 11430:
! 11431: static int
! 11432: xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
! 11433:
! 11434: #ifdef DEBUG_STEP
! 11435: static void
! 11436: xmlXPathDebugDumpStepAxis(xmlXPathStepOpPtr op,
! 11437: int nbNodes)
! 11438: {
! 11439: xmlGenericError(xmlGenericErrorContext, "new step : ");
! 11440: switch (op->value) {
! 11441: case AXIS_ANCESTOR:
! 11442: xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
! 11443: break;
! 11444: case AXIS_ANCESTOR_OR_SELF:
! 11445: xmlGenericError(xmlGenericErrorContext,
! 11446: "axis 'ancestors-or-self' ");
! 11447: break;
! 11448: case AXIS_ATTRIBUTE:
! 11449: xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
! 11450: break;
! 11451: case AXIS_CHILD:
! 11452: xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
! 11453: break;
! 11454: case AXIS_DESCENDANT:
! 11455: xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
! 11456: break;
! 11457: case AXIS_DESCENDANT_OR_SELF:
! 11458: xmlGenericError(xmlGenericErrorContext,
! 11459: "axis 'descendant-or-self' ");
! 11460: break;
! 11461: case AXIS_FOLLOWING:
! 11462: xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
! 11463: break;
! 11464: case AXIS_FOLLOWING_SIBLING:
! 11465: xmlGenericError(xmlGenericErrorContext,
! 11466: "axis 'following-siblings' ");
! 11467: break;
! 11468: case AXIS_NAMESPACE:
! 11469: xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
! 11470: break;
! 11471: case AXIS_PARENT:
! 11472: xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
! 11473: break;
! 11474: case AXIS_PRECEDING:
! 11475: xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
! 11476: break;
! 11477: case AXIS_PRECEDING_SIBLING:
! 11478: xmlGenericError(xmlGenericErrorContext,
! 11479: "axis 'preceding-sibling' ");
! 11480: break;
! 11481: case AXIS_SELF:
! 11482: xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
! 11483: break;
! 11484: }
! 11485: xmlGenericError(xmlGenericErrorContext,
! 11486: " context contains %d nodes\n", nbNodes);
! 11487: switch (op->value2) {
! 11488: case NODE_TEST_NONE:
! 11489: xmlGenericError(xmlGenericErrorContext,
! 11490: " searching for none !!!\n");
! 11491: break;
! 11492: case NODE_TEST_TYPE:
! 11493: xmlGenericError(xmlGenericErrorContext,
! 11494: " searching for type %d\n", op->value3);
! 11495: break;
! 11496: case NODE_TEST_PI:
! 11497: xmlGenericError(xmlGenericErrorContext,
! 11498: " searching for PI !!!\n");
! 11499: break;
! 11500: case NODE_TEST_ALL:
! 11501: xmlGenericError(xmlGenericErrorContext,
! 11502: " searching for *\n");
! 11503: break;
! 11504: case NODE_TEST_NS:
! 11505: xmlGenericError(xmlGenericErrorContext,
! 11506: " searching for namespace %s\n",
! 11507: op->value5);
! 11508: break;
! 11509: case NODE_TEST_NAME:
! 11510: xmlGenericError(xmlGenericErrorContext,
! 11511: " searching for name %s\n", op->value5);
! 11512: if (op->value4)
! 11513: xmlGenericError(xmlGenericErrorContext,
! 11514: " with namespace %s\n", op->value4);
! 11515: break;
! 11516: }
! 11517: xmlGenericError(xmlGenericErrorContext, "Testing : ");
! 11518: }
! 11519: #endif /* DEBUG_STEP */
! 11520:
! 11521: static int
! 11522: xmlXPathCompOpEvalPredicate(xmlXPathParserContextPtr ctxt,
! 11523: xmlXPathStepOpPtr op,
! 11524: xmlNodeSetPtr set,
! 11525: int contextSize,
! 11526: int hasNsNodes)
! 11527: {
! 11528: if (op->ch1 != -1) {
! 11529: xmlXPathCompExprPtr comp = ctxt->comp;
! 11530: /*
! 11531: * Process inner predicates first.
! 11532: */
! 11533: if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
! 11534: /*
! 11535: * TODO: raise an internal error.
! 11536: */
! 11537: }
! 11538: contextSize = xmlXPathCompOpEvalPredicate(ctxt,
! 11539: &comp->steps[op->ch1], set, contextSize, hasNsNodes);
! 11540: CHECK_ERROR0;
! 11541: if (contextSize <= 0)
! 11542: return(0);
! 11543: }
! 11544: if (op->ch2 != -1) {
! 11545: xmlXPathContextPtr xpctxt = ctxt->context;
! 11546: xmlNodePtr contextNode, oldContextNode;
! 11547: xmlDocPtr oldContextDoc;
! 11548: int i, res, contextPos = 0, newContextSize;
! 11549: xmlXPathStepOpPtr exprOp;
! 11550: xmlXPathObjectPtr contextObj = NULL, exprRes = NULL;
! 11551:
! 11552: #ifdef LIBXML_XPTR_ENABLED
! 11553: /*
! 11554: * URGENT TODO: Check the following:
! 11555: * We don't expect location sets if evaluating prediates, right?
! 11556: * Only filters should expect location sets, right?
! 11557: */
! 11558: #endif
! 11559: /*
! 11560: * SPEC XPath 1.0:
! 11561: * "For each node in the node-set to be filtered, the
! 11562: * PredicateExpr is evaluated with that node as the
! 11563: * context node, with the number of nodes in the
! 11564: * node-set as the context size, and with the proximity
! 11565: * position of the node in the node-set with respect to
! 11566: * the axis as the context position;"
! 11567: * @oldset is the node-set" to be filtered.
! 11568: *
! 11569: * SPEC XPath 1.0:
! 11570: * "only predicates change the context position and
! 11571: * context size (see [2.4 Predicates])."
! 11572: * Example:
! 11573: * node-set context pos
! 11574: * nA 1
! 11575: * nB 2
! 11576: * nC 3
! 11577: * After applying predicate [position() > 1] :
! 11578: * node-set context pos
! 11579: * nB 1
! 11580: * nC 2
! 11581: */
! 11582: oldContextNode = xpctxt->node;
! 11583: oldContextDoc = xpctxt->doc;
! 11584: /*
! 11585: * Get the expression of this predicate.
! 11586: */
! 11587: exprOp = &ctxt->comp->steps[op->ch2];
! 11588: newContextSize = 0;
! 11589: for (i = 0; i < set->nodeNr; i++) {
! 11590: if (set->nodeTab[i] == NULL)
! 11591: continue;
! 11592:
! 11593: contextNode = set->nodeTab[i];
! 11594: xpctxt->node = contextNode;
! 11595: xpctxt->contextSize = contextSize;
! 11596: xpctxt->proximityPosition = ++contextPos;
! 11597:
! 11598: /*
! 11599: * Also set the xpath document in case things like
! 11600: * key() are evaluated in the predicate.
! 11601: */
! 11602: if ((contextNode->type != XML_NAMESPACE_DECL) &&
! 11603: (contextNode->doc != NULL))
! 11604: xpctxt->doc = contextNode->doc;
! 11605: /*
! 11606: * Evaluate the predicate expression with 1 context node
! 11607: * at a time; this node is packaged into a node set; this
! 11608: * node set is handed over to the evaluation mechanism.
! 11609: */
! 11610: if (contextObj == NULL)
! 11611: contextObj = xmlXPathCacheNewNodeSet(xpctxt, contextNode);
! 11612: else
! 11613: xmlXPathNodeSetAddUnique(contextObj->nodesetval,
! 11614: contextNode);
! 11615:
! 11616: valuePush(ctxt, contextObj);
! 11617:
! 11618: res = xmlXPathCompOpEvalToBoolean(ctxt, exprOp, 1);
! 11619:
! 11620: if ((ctxt->error != XPATH_EXPRESSION_OK) || (res == -1)) {
! 11621: xmlXPathNodeSetClear(set, hasNsNodes);
! 11622: newContextSize = 0;
! 11623: goto evaluation_exit;
! 11624: }
! 11625:
! 11626: if (res != 0) {
! 11627: newContextSize++;
! 11628: } else {
! 11629: /*
! 11630: * Remove the entry from the initial node set.
! 11631: */
! 11632: set->nodeTab[i] = NULL;
! 11633: if (contextNode->type == XML_NAMESPACE_DECL)
! 11634: xmlXPathNodeSetFreeNs((xmlNsPtr) contextNode);
! 11635: }
! 11636: if (ctxt->value == contextObj) {
! 11637: /*
! 11638: * Don't free the temporary XPath object holding the
! 11639: * context node, in order to avoid massive recreation
! 11640: * inside this loop.
! 11641: */
! 11642: valuePop(ctxt);
! 11643: xmlXPathNodeSetClear(contextObj->nodesetval, hasNsNodes);
! 11644: } else {
! 11645: /*
! 11646: * TODO: The object was lost in the evaluation machinery.
! 11647: * Can this happen? Maybe in internal-error cases.
! 11648: */
! 11649: contextObj = NULL;
! 11650: }
! 11651: }
! 11652:
! 11653: if (contextObj != NULL) {
! 11654: if (ctxt->value == contextObj)
! 11655: valuePop(ctxt);
! 11656: xmlXPathReleaseObject(xpctxt, contextObj);
! 11657: }
! 11658: evaluation_exit:
! 11659: if (exprRes != NULL)
! 11660: xmlXPathReleaseObject(ctxt->context, exprRes);
! 11661: /*
! 11662: * Reset/invalidate the context.
! 11663: */
! 11664: xpctxt->node = oldContextNode;
! 11665: xpctxt->doc = oldContextDoc;
! 11666: xpctxt->contextSize = -1;
! 11667: xpctxt->proximityPosition = -1;
! 11668: return(newContextSize);
! 11669: }
! 11670: return(contextSize);
! 11671: }
! 11672:
! 11673: static int
! 11674: xmlXPathCompOpEvalPositionalPredicate(xmlXPathParserContextPtr ctxt,
! 11675: xmlXPathStepOpPtr op,
! 11676: xmlNodeSetPtr set,
! 11677: int contextSize,
! 11678: int minPos,
! 11679: int maxPos,
! 11680: int hasNsNodes)
! 11681: {
! 11682: if (op->ch1 != -1) {
! 11683: xmlXPathCompExprPtr comp = ctxt->comp;
! 11684: if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
! 11685: /*
! 11686: * TODO: raise an internal error.
! 11687: */
! 11688: }
! 11689: contextSize = xmlXPathCompOpEvalPredicate(ctxt,
! 11690: &comp->steps[op->ch1], set, contextSize, hasNsNodes);
! 11691: CHECK_ERROR0;
! 11692: if (contextSize <= 0)
! 11693: return(0);
! 11694: }
! 11695: /*
! 11696: * Check if the node set contains a sufficient number of nodes for
! 11697: * the requested range.
! 11698: */
! 11699: if (contextSize < minPos) {
! 11700: xmlXPathNodeSetClear(set, hasNsNodes);
! 11701: return(0);
! 11702: }
! 11703: if (op->ch2 == -1) {
! 11704: /*
! 11705: * TODO: Can this ever happen?
! 11706: */
! 11707: return (contextSize);
! 11708: } else {
! 11709: xmlDocPtr oldContextDoc;
! 11710: int i, pos = 0, newContextSize = 0, contextPos = 0, res;
! 11711: xmlXPathStepOpPtr exprOp;
! 11712: xmlXPathObjectPtr contextObj = NULL, exprRes = NULL;
! 11713: xmlNodePtr oldContextNode, contextNode = NULL;
! 11714: xmlXPathContextPtr xpctxt = ctxt->context;
! 11715:
! 11716: #ifdef LIBXML_XPTR_ENABLED
! 11717: /*
! 11718: * URGENT TODO: Check the following:
! 11719: * We don't expect location sets if evaluating prediates, right?
! 11720: * Only filters should expect location sets, right?
! 11721: */
! 11722: #endif /* LIBXML_XPTR_ENABLED */
! 11723:
! 11724: /*
! 11725: * Save old context.
! 11726: */
! 11727: oldContextNode = xpctxt->node;
! 11728: oldContextDoc = xpctxt->doc;
! 11729: /*
! 11730: * Get the expression of this predicate.
! 11731: */
! 11732: exprOp = &ctxt->comp->steps[op->ch2];
! 11733: for (i = 0; i < set->nodeNr; i++) {
! 11734: if (set->nodeTab[i] == NULL)
! 11735: continue;
! 11736:
! 11737: contextNode = set->nodeTab[i];
! 11738: xpctxt->node = contextNode;
! 11739: xpctxt->contextSize = contextSize;
! 11740: xpctxt->proximityPosition = ++contextPos;
! 11741:
! 11742: /*
! 11743: * Initialize the new set.
! 11744: * Also set the xpath document in case things like
! 11745: * key() evaluation are attempted on the predicate
! 11746: */
! 11747: if ((contextNode->type != XML_NAMESPACE_DECL) &&
! 11748: (contextNode->doc != NULL))
! 11749: xpctxt->doc = contextNode->doc;
! 11750: /*
! 11751: * Evaluate the predicate expression with 1 context node
! 11752: * at a time; this node is packaged into a node set; this
! 11753: * node set is handed over to the evaluation mechanism.
! 11754: */
! 11755: if (contextObj == NULL)
! 11756: contextObj = xmlXPathCacheNewNodeSet(xpctxt, contextNode);
! 11757: else
! 11758: xmlXPathNodeSetAddUnique(contextObj->nodesetval,
! 11759: contextNode);
! 11760:
! 11761: valuePush(ctxt, contextObj);
! 11762: res = xmlXPathCompOpEvalToBoolean(ctxt, exprOp, 1);
! 11763:
! 11764: if ((ctxt->error != XPATH_EXPRESSION_OK) || (res == -1)) {
! 11765: xmlXPathObjectPtr tmp;
! 11766: /* pop the result */
! 11767: tmp = valuePop(ctxt);
! 11768: xmlXPathReleaseObject(xpctxt, tmp);
! 11769: /* then pop off contextObj, which will be freed later */
! 11770: valuePop(ctxt);
! 11771: goto evaluation_error;
! 11772: }
! 11773:
! 11774: if (res)
! 11775: pos++;
! 11776:
! 11777: if (res && (pos >= minPos) && (pos <= maxPos)) {
! 11778: /*
! 11779: * Fits in the requested range.
! 11780: */
! 11781: newContextSize++;
! 11782: if (minPos == maxPos) {
! 11783: /*
! 11784: * Only 1 node was requested.
! 11785: */
! 11786: if (contextNode->type == XML_NAMESPACE_DECL) {
! 11787: /*
! 11788: * As always: take care of those nasty
! 11789: * namespace nodes.
! 11790: */
! 11791: set->nodeTab[i] = NULL;
! 11792: }
! 11793: xmlXPathNodeSetClear(set, hasNsNodes);
! 11794: set->nodeNr = 1;
! 11795: set->nodeTab[0] = contextNode;
! 11796: goto evaluation_exit;
! 11797: }
! 11798: if (pos == maxPos) {
! 11799: /*
! 11800: * We are done.
! 11801: */
! 11802: xmlXPathNodeSetClearFromPos(set, i +1, hasNsNodes);
! 11803: goto evaluation_exit;
! 11804: }
! 11805: } else {
! 11806: /*
! 11807: * Remove the entry from the initial node set.
! 11808: */
! 11809: set->nodeTab[i] = NULL;
! 11810: if (contextNode->type == XML_NAMESPACE_DECL)
! 11811: xmlXPathNodeSetFreeNs((xmlNsPtr) contextNode);
! 11812: }
! 11813: if (exprRes != NULL) {
! 11814: xmlXPathReleaseObject(ctxt->context, exprRes);
! 11815: exprRes = NULL;
! 11816: }
! 11817: if (ctxt->value == contextObj) {
! 11818: /*
! 11819: * Don't free the temporary XPath object holding the
! 11820: * context node, in order to avoid massive recreation
! 11821: * inside this loop.
! 11822: */
! 11823: valuePop(ctxt);
! 11824: xmlXPathNodeSetClear(contextObj->nodesetval, hasNsNodes);
! 11825: } else {
! 11826: /*
! 11827: * The object was lost in the evaluation machinery.
! 11828: * Can this happen? Maybe in case of internal-errors.
! 11829: */
! 11830: contextObj = NULL;
! 11831: }
! 11832: }
! 11833: goto evaluation_exit;
! 11834:
! 11835: evaluation_error:
! 11836: xmlXPathNodeSetClear(set, hasNsNodes);
! 11837: newContextSize = 0;
! 11838:
! 11839: evaluation_exit:
! 11840: if (contextObj != NULL) {
! 11841: if (ctxt->value == contextObj)
! 11842: valuePop(ctxt);
! 11843: xmlXPathReleaseObject(xpctxt, contextObj);
! 11844: }
! 11845: if (exprRes != NULL)
! 11846: xmlXPathReleaseObject(ctxt->context, exprRes);
! 11847: /*
! 11848: * Reset/invalidate the context.
! 11849: */
! 11850: xpctxt->node = oldContextNode;
! 11851: xpctxt->doc = oldContextDoc;
! 11852: xpctxt->contextSize = -1;
! 11853: xpctxt->proximityPosition = -1;
! 11854: return(newContextSize);
! 11855: }
! 11856: return(contextSize);
! 11857: }
! 11858:
! 11859: static int
! 11860: xmlXPathIsPositionalPredicate(xmlXPathParserContextPtr ctxt,
! 11861: xmlXPathStepOpPtr op,
! 11862: int *maxPos)
! 11863: {
! 11864:
! 11865: xmlXPathStepOpPtr exprOp;
! 11866:
! 11867: /*
! 11868: * BIG NOTE: This is not intended for XPATH_OP_FILTER yet!
! 11869: */
! 11870:
! 11871: /*
! 11872: * If not -1, then ch1 will point to:
! 11873: * 1) For predicates (XPATH_OP_PREDICATE):
! 11874: * - an inner predicate operator
! 11875: * 2) For filters (XPATH_OP_FILTER):
! 11876: * - an inner filter operater OR
! 11877: * - an expression selecting the node set.
! 11878: * E.g. "key('a', 'b')" or "(//foo | //bar)".
! 11879: */
! 11880: if ((op->op != XPATH_OP_PREDICATE) && (op->op != XPATH_OP_FILTER))
! 11881: return(0);
! 11882:
! 11883: if (op->ch2 != -1) {
! 11884: exprOp = &ctxt->comp->steps[op->ch2];
! 11885: } else
! 11886: return(0);
! 11887:
! 11888: if ((exprOp != NULL) &&
! 11889: (exprOp->op == XPATH_OP_VALUE) &&
! 11890: (exprOp->value4 != NULL) &&
! 11891: (((xmlXPathObjectPtr) exprOp->value4)->type == XPATH_NUMBER))
! 11892: {
! 11893: /*
! 11894: * We have a "[n]" predicate here.
! 11895: * TODO: Unfortunately this simplistic test here is not
! 11896: * able to detect a position() predicate in compound
! 11897: * expressions like "[@attr = 'a" and position() = 1],
! 11898: * and even not the usage of position() in
! 11899: * "[position() = 1]"; thus - obviously - a position-range,
! 11900: * like it "[position() < 5]", is also not detected.
! 11901: * Maybe we could rewrite the AST to ease the optimization.
! 11902: */
! 11903: *maxPos = (int) ((xmlXPathObjectPtr) exprOp->value4)->floatval;
! 11904:
! 11905: if (((xmlXPathObjectPtr) exprOp->value4)->floatval ==
! 11906: (float) *maxPos)
! 11907: {
! 11908: return(1);
! 11909: }
! 11910: }
! 11911: return(0);
! 11912: }
! 11913:
! 11914: static int
! 11915: xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
! 11916: xmlXPathStepOpPtr op,
! 11917: xmlNodePtr * first, xmlNodePtr * last,
! 11918: int toBool)
! 11919: {
! 11920:
! 11921: #define XP_TEST_HIT \
! 11922: if (hasAxisRange != 0) { \
! 11923: if (++pos == maxPos) { \
! 11924: addNode(seq, cur); \
! 11925: goto axis_range_end; } \
! 11926: } else { \
! 11927: addNode(seq, cur); \
! 11928: if (breakOnFirstHit) goto first_hit; }
! 11929:
! 11930: #define XP_TEST_HIT_NS \
! 11931: if (hasAxisRange != 0) { \
! 11932: if (++pos == maxPos) { \
! 11933: hasNsNodes = 1; \
! 11934: xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur); \
! 11935: goto axis_range_end; } \
! 11936: } else { \
! 11937: hasNsNodes = 1; \
! 11938: xmlXPathNodeSetAddNs(seq, \
! 11939: xpctxt->node, (xmlNsPtr) cur); \
! 11940: if (breakOnFirstHit) goto first_hit; }
! 11941:
! 11942: xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
! 11943: xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
! 11944: xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;
! 11945: const xmlChar *prefix = op->value4;
! 11946: const xmlChar *name = op->value5;
! 11947: const xmlChar *URI = NULL;
! 11948:
! 11949: #ifdef DEBUG_STEP
! 11950: int nbMatches = 0, prevMatches = 0;
! 11951: #endif
! 11952: int total = 0, hasNsNodes = 0;
! 11953: /* The popped object holding the context nodes */
! 11954: xmlXPathObjectPtr obj;
! 11955: /* The set of context nodes for the node tests */
! 11956: xmlNodeSetPtr contextSeq;
! 11957: int contextIdx;
! 11958: xmlNodePtr contextNode;
! 11959: /* The context node for a compound traversal */
! 11960: xmlNodePtr outerContextNode;
! 11961: /* The final resulting node set wrt to all context nodes */
! 11962: xmlNodeSetPtr outSeq;
! 11963: /*
! 11964: * The temporary resulting node set wrt 1 context node.
! 11965: * Used to feed predicate evaluation.
! 11966: */
! 11967: xmlNodeSetPtr seq;
! 11968: xmlNodePtr cur;
! 11969: /* First predicate operator */
! 11970: xmlXPathStepOpPtr predOp;
! 11971: int maxPos; /* The requested position() (when a "[n]" predicate) */
! 11972: int hasPredicateRange, hasAxisRange, pos, size, newSize;
! 11973: int breakOnFirstHit;
! 11974:
! 11975: xmlXPathTraversalFunction next = NULL;
! 11976: /* compound axis traversal */
! 11977: xmlXPathTraversalFunctionExt outerNext = NULL;
! 11978: void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
! 11979: xmlXPathNodeSetMergeFunction mergeAndClear;
! 11980: xmlNodePtr oldContextNode;
! 11981: xmlXPathContextPtr xpctxt = ctxt->context;
! 11982:
! 11983:
! 11984: CHECK_TYPE0(XPATH_NODESET);
! 11985: obj = valuePop(ctxt);
! 11986: /*
! 11987: * Setup namespaces.
! 11988: */
! 11989: if (prefix != NULL) {
! 11990: URI = xmlXPathNsLookup(xpctxt, prefix);
! 11991: if (URI == NULL) {
! 11992: xmlXPathReleaseObject(xpctxt, obj);
! 11993: XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
! 11994: }
! 11995: }
! 11996: /*
! 11997: * Setup axis.
! 11998: *
! 11999: * MAYBE FUTURE TODO: merging optimizations:
! 12000: * - If the nodes to be traversed wrt to the initial nodes and
! 12001: * the current axis cannot overlap, then we could avoid searching
! 12002: * for duplicates during the merge.
! 12003: * But the question is how/when to evaluate if they cannot overlap.
! 12004: * Example: if we know that for two initial nodes, the one is
! 12005: * not in the ancestor-or-self axis of the other, then we could safely
! 12006: * avoid a duplicate-aware merge, if the axis to be traversed is e.g.
! 12007: * the descendant-or-self axis.
! 12008: */
! 12009: mergeAndClear = xmlXPathNodeSetMergeAndClear;
! 12010: switch (axis) {
! 12011: case AXIS_ANCESTOR:
! 12012: first = NULL;
! 12013: next = xmlXPathNextAncestor;
! 12014: break;
! 12015: case AXIS_ANCESTOR_OR_SELF:
! 12016: first = NULL;
! 12017: next = xmlXPathNextAncestorOrSelf;
! 12018: break;
! 12019: case AXIS_ATTRIBUTE:
! 12020: first = NULL;
! 12021: last = NULL;
! 12022: next = xmlXPathNextAttribute;
! 12023: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
! 12024: break;
! 12025: case AXIS_CHILD:
! 12026: last = NULL;
! 12027: if (op->rewriteType == XP_REWRITE_DOS_CHILD_ELEM) {
! 12028: /*
! 12029: * This iterator will give us only nodes which can
! 12030: * hold element nodes.
! 12031: */
! 12032: outerNext = xmlXPathNextDescendantOrSelfElemParent;
! 12033: }
! 12034: if (((test == NODE_TEST_NAME) || (test == NODE_TEST_ALL)) &&
! 12035: (type == NODE_TYPE_NODE))
! 12036: {
! 12037: /*
! 12038: * Optimization if an element node type is 'element'.
! 12039: */
! 12040: next = xmlXPathNextChildElement;
! 12041: } else
! 12042: next = xmlXPathNextChild;
! 12043: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
! 12044: break;
! 12045: case AXIS_DESCENDANT:
! 12046: last = NULL;
! 12047: next = xmlXPathNextDescendant;
! 12048: break;
! 12049: case AXIS_DESCENDANT_OR_SELF:
! 12050: last = NULL;
! 12051: next = xmlXPathNextDescendantOrSelf;
! 12052: break;
! 12053: case AXIS_FOLLOWING:
! 12054: last = NULL;
! 12055: next = xmlXPathNextFollowing;
! 12056: break;
! 12057: case AXIS_FOLLOWING_SIBLING:
! 12058: last = NULL;
! 12059: next = xmlXPathNextFollowingSibling;
! 12060: break;
! 12061: case AXIS_NAMESPACE:
! 12062: first = NULL;
! 12063: last = NULL;
! 12064: next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
! 12065: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
! 12066: break;
! 12067: case AXIS_PARENT:
! 12068: first = NULL;
! 12069: next = xmlXPathNextParent;
! 12070: break;
! 12071: case AXIS_PRECEDING:
! 12072: first = NULL;
! 12073: next = xmlXPathNextPrecedingInternal;
! 12074: break;
! 12075: case AXIS_PRECEDING_SIBLING:
! 12076: first = NULL;
! 12077: next = xmlXPathNextPrecedingSibling;
! 12078: break;
! 12079: case AXIS_SELF:
! 12080: first = NULL;
! 12081: last = NULL;
! 12082: next = xmlXPathNextSelf;
! 12083: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
! 12084: break;
! 12085: }
! 12086:
! 12087: #ifdef DEBUG_STEP
! 12088: xmlXPathDebugDumpStepAxis(op,
! 12089: (obj->nodesetval != NULL) ? obj->nodesetval->nodeNr : 0);
! 12090: #endif
! 12091:
! 12092: if (next == NULL) {
! 12093: xmlXPathReleaseObject(xpctxt, obj);
! 12094: return(0);
! 12095: }
! 12096: contextSeq = obj->nodesetval;
! 12097: if ((contextSeq == NULL) || (contextSeq->nodeNr <= 0)) {
! 12098: xmlXPathReleaseObject(xpctxt, obj);
! 12099: valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL));
! 12100: return(0);
! 12101: }
! 12102: /*
! 12103: * Predicate optimization ---------------------------------------------
! 12104: * If this step has a last predicate, which contains a position(),
! 12105: * then we'll optimize (although not exactly "position()", but only
! 12106: * the short-hand form, i.e., "[n]".
! 12107: *
! 12108: * Example - expression "/foo[parent::bar][1]":
! 12109: *
! 12110: * COLLECT 'child' 'name' 'node' foo -- op (we are here)
! 12111: * ROOT -- op->ch1
! 12112: * PREDICATE -- op->ch2 (predOp)
! 12113: * PREDICATE -- predOp->ch1 = [parent::bar]
! 12114: * SORT
! 12115: * COLLECT 'parent' 'name' 'node' bar
! 12116: * NODE
! 12117: * ELEM Object is a number : 1 -- predOp->ch2 = [1]
! 12118: *
! 12119: */
! 12120: maxPos = 0;
! 12121: predOp = NULL;
! 12122: hasPredicateRange = 0;
! 12123: hasAxisRange = 0;
! 12124: if (op->ch2 != -1) {
! 12125: /*
! 12126: * There's at least one predicate. 16 == XPATH_OP_PREDICATE
! 12127: */
! 12128: predOp = &ctxt->comp->steps[op->ch2];
! 12129: if (xmlXPathIsPositionalPredicate(ctxt, predOp, &maxPos)) {
! 12130: if (predOp->ch1 != -1) {
! 12131: /*
! 12132: * Use the next inner predicate operator.
! 12133: */
! 12134: predOp = &ctxt->comp->steps[predOp->ch1];
! 12135: hasPredicateRange = 1;
! 12136: } else {
! 12137: /*
! 12138: * There's no other predicate than the [n] predicate.
! 12139: */
! 12140: predOp = NULL;
! 12141: hasAxisRange = 1;
! 12142: }
! 12143: }
! 12144: }
! 12145: breakOnFirstHit = ((toBool) && (predOp == NULL)) ? 1 : 0;
! 12146: /*
! 12147: * Axis traversal -----------------------------------------------------
! 12148: */
! 12149: /*
! 12150: * 2.3 Node Tests
! 12151: * - For the attribute axis, the principal node type is attribute.
! 12152: * - For the namespace axis, the principal node type is namespace.
! 12153: * - For other axes, the principal node type is element.
! 12154: *
! 12155: * A node test * is true for any node of the
! 12156: * principal node type. For example, child::* will
! 12157: * select all element children of the context node
! 12158: */
! 12159: oldContextNode = xpctxt->node;
! 12160: addNode = xmlXPathNodeSetAddUnique;
! 12161: outSeq = NULL;
! 12162: seq = NULL;
! 12163: outerContextNode = NULL;
! 12164: contextNode = NULL;
! 12165: contextIdx = 0;
! 12166:
! 12167:
! 12168: while ((contextIdx < contextSeq->nodeNr) || (contextNode != NULL)) {
! 12169: if (outerNext != NULL) {
! 12170: /*
! 12171: * This is a compound traversal.
! 12172: */
! 12173: if (contextNode == NULL) {
! 12174: /*
! 12175: * Set the context for the outer traversal.
! 12176: */
! 12177: outerContextNode = contextSeq->nodeTab[contextIdx++];
! 12178: contextNode = outerNext(NULL, outerContextNode);
! 12179: } else
! 12180: contextNode = outerNext(contextNode, outerContextNode);
! 12181: if (contextNode == NULL)
! 12182: continue;
! 12183: /*
! 12184: * Set the context for the main traversal.
! 12185: */
! 12186: xpctxt->node = contextNode;
! 12187: } else
! 12188: xpctxt->node = contextSeq->nodeTab[contextIdx++];
! 12189:
! 12190: if (seq == NULL) {
! 12191: seq = xmlXPathNodeSetCreate(NULL);
! 12192: if (seq == NULL) {
! 12193: total = 0;
! 12194: goto error;
! 12195: }
! 12196: }
! 12197: /*
! 12198: * Traverse the axis and test the nodes.
! 12199: */
! 12200: pos = 0;
! 12201: cur = NULL;
! 12202: hasNsNodes = 0;
! 12203: do {
! 12204: cur = next(ctxt, cur);
! 12205: if (cur == NULL)
! 12206: break;
! 12207:
! 12208: /*
! 12209: * QUESTION TODO: What does the "first" and "last" stuff do?
! 12210: */
! 12211: if ((first != NULL) && (*first != NULL)) {
! 12212: if (*first == cur)
! 12213: break;
! 12214: if (((total % 256) == 0) &&
! 12215: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
! 12216: (xmlXPathCmpNodesExt(*first, cur) >= 0))
! 12217: #else
! 12218: (xmlXPathCmpNodes(*first, cur) >= 0))
! 12219: #endif
! 12220: {
! 12221: break;
! 12222: }
! 12223: }
! 12224: if ((last != NULL) && (*last != NULL)) {
! 12225: if (*last == cur)
! 12226: break;
! 12227: if (((total % 256) == 0) &&
! 12228: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
! 12229: (xmlXPathCmpNodesExt(cur, *last) >= 0))
! 12230: #else
! 12231: (xmlXPathCmpNodes(cur, *last) >= 0))
! 12232: #endif
! 12233: {
! 12234: break;
! 12235: }
! 12236: }
! 12237:
! 12238: total++;
! 12239:
! 12240: #ifdef DEBUG_STEP
! 12241: xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
! 12242: #endif
! 12243:
! 12244: switch (test) {
! 12245: case NODE_TEST_NONE:
! 12246: total = 0;
! 12247: STRANGE
! 12248: goto error;
! 12249: case NODE_TEST_TYPE:
! 12250: /*
! 12251: * TODO: Don't we need to use
! 12252: * xmlXPathNodeSetAddNs() for namespace nodes here?
! 12253: * Surprisingly, some c14n tests fail, if we do this.
! 12254: */
! 12255: if (type == NODE_TYPE_NODE) {
! 12256: switch (cur->type) {
! 12257: case XML_DOCUMENT_NODE:
! 12258: case XML_HTML_DOCUMENT_NODE:
! 12259: #ifdef LIBXML_DOCB_ENABLED
! 12260: case XML_DOCB_DOCUMENT_NODE:
! 12261: #endif
! 12262: case XML_ELEMENT_NODE:
! 12263: case XML_ATTRIBUTE_NODE:
! 12264: case XML_PI_NODE:
! 12265: case XML_COMMENT_NODE:
! 12266: case XML_CDATA_SECTION_NODE:
! 12267: case XML_TEXT_NODE:
! 12268: case XML_NAMESPACE_DECL:
! 12269: XP_TEST_HIT
! 12270: break;
! 12271: default:
! 12272: break;
! 12273: }
! 12274: } else if (cur->type == type) {
! 12275: if (type == XML_NAMESPACE_DECL)
! 12276: XP_TEST_HIT_NS
! 12277: else
! 12278: XP_TEST_HIT
! 12279: } else if ((type == NODE_TYPE_TEXT) &&
! 12280: (cur->type == XML_CDATA_SECTION_NODE))
! 12281: {
! 12282: XP_TEST_HIT
! 12283: }
! 12284: break;
! 12285: case NODE_TEST_PI:
! 12286: if ((cur->type == XML_PI_NODE) &&
! 12287: ((name == NULL) || xmlStrEqual(name, cur->name)))
! 12288: {
! 12289: XP_TEST_HIT
! 12290: }
! 12291: break;
! 12292: case NODE_TEST_ALL:
! 12293: if (axis == AXIS_ATTRIBUTE) {
! 12294: if (cur->type == XML_ATTRIBUTE_NODE)
! 12295: {
! 12296: XP_TEST_HIT
! 12297: }
! 12298: } else if (axis == AXIS_NAMESPACE) {
! 12299: if (cur->type == XML_NAMESPACE_DECL)
! 12300: {
! 12301: XP_TEST_HIT_NS
! 12302: }
! 12303: } else {
! 12304: if (cur->type == XML_ELEMENT_NODE) {
! 12305: if (prefix == NULL)
! 12306: {
! 12307: XP_TEST_HIT
! 12308:
! 12309: } else if ((cur->ns != NULL) &&
! 12310: (xmlStrEqual(URI, cur->ns->href)))
! 12311: {
! 12312: XP_TEST_HIT
! 12313: }
! 12314: }
! 12315: }
! 12316: break;
! 12317: case NODE_TEST_NS:{
! 12318: TODO;
! 12319: break;
! 12320: }
! 12321: case NODE_TEST_NAME:
! 12322: if (axis == AXIS_ATTRIBUTE) {
! 12323: if (cur->type != XML_ATTRIBUTE_NODE)
! 12324: break;
! 12325: } else if (axis == AXIS_NAMESPACE) {
! 12326: if (cur->type != XML_NAMESPACE_DECL)
! 12327: break;
! 12328: } else {
! 12329: if (cur->type != XML_ELEMENT_NODE)
! 12330: break;
! 12331: }
! 12332: switch (cur->type) {
! 12333: case XML_ELEMENT_NODE:
! 12334: if (xmlStrEqual(name, cur->name)) {
! 12335: if (prefix == NULL) {
! 12336: if (cur->ns == NULL)
! 12337: {
! 12338: XP_TEST_HIT
! 12339: }
! 12340: } else {
! 12341: if ((cur->ns != NULL) &&
! 12342: (xmlStrEqual(URI, cur->ns->href)))
! 12343: {
! 12344: XP_TEST_HIT
! 12345: }
! 12346: }
! 12347: }
! 12348: break;
! 12349: case XML_ATTRIBUTE_NODE:{
! 12350: xmlAttrPtr attr = (xmlAttrPtr) cur;
! 12351:
! 12352: if (xmlStrEqual(name, attr->name)) {
! 12353: if (prefix == NULL) {
! 12354: if ((attr->ns == NULL) ||
! 12355: (attr->ns->prefix == NULL))
! 12356: {
! 12357: XP_TEST_HIT
! 12358: }
! 12359: } else {
! 12360: if ((attr->ns != NULL) &&
! 12361: (xmlStrEqual(URI,
! 12362: attr->ns->href)))
! 12363: {
! 12364: XP_TEST_HIT
! 12365: }
! 12366: }
! 12367: }
! 12368: break;
! 12369: }
! 12370: case XML_NAMESPACE_DECL:
! 12371: if (cur->type == XML_NAMESPACE_DECL) {
! 12372: xmlNsPtr ns = (xmlNsPtr) cur;
! 12373:
! 12374: if ((ns->prefix != NULL) && (name != NULL)
! 12375: && (xmlStrEqual(ns->prefix, name)))
! 12376: {
! 12377: XP_TEST_HIT_NS
! 12378: }
! 12379: }
! 12380: break;
! 12381: default:
! 12382: break;
! 12383: }
! 12384: break;
! 12385: } /* switch(test) */
! 12386: } while (cur != NULL);
! 12387:
! 12388: goto apply_predicates;
! 12389:
! 12390: axis_range_end: /* ----------------------------------------------------- */
! 12391: /*
! 12392: * We have a "/foo[n]", and position() = n was reached.
! 12393: * Note that we can have as well "/foo/::parent::foo[1]", so
! 12394: * a duplicate-aware merge is still needed.
! 12395: * Merge with the result.
! 12396: */
! 12397: if (outSeq == NULL) {
! 12398: outSeq = seq;
! 12399: seq = NULL;
! 12400: } else
! 12401: outSeq = mergeAndClear(outSeq, seq, 0);
! 12402: /*
! 12403: * Break if only a true/false result was requested.
! 12404: */
! 12405: if (toBool)
! 12406: break;
! 12407: continue;
! 12408:
! 12409: first_hit: /* ---------------------------------------------------------- */
! 12410: /*
! 12411: * Break if only a true/false result was requested and
! 12412: * no predicates existed and a node test succeeded.
! 12413: */
! 12414: if (outSeq == NULL) {
! 12415: outSeq = seq;
! 12416: seq = NULL;
! 12417: } else
! 12418: outSeq = mergeAndClear(outSeq, seq, 0);
! 12419: break;
! 12420:
! 12421: #ifdef DEBUG_STEP
! 12422: if (seq != NULL)
! 12423: nbMatches += seq->nodeNr;
! 12424: #endif
! 12425:
! 12426: apply_predicates: /* --------------------------------------------------- */
! 12427: /*
! 12428: * Apply predicates.
! 12429: */
! 12430: if ((predOp != NULL) && (seq->nodeNr > 0)) {
! 12431: /*
! 12432: * E.g. when we have a "/foo[some expression][n]".
! 12433: */
! 12434: /*
! 12435: * QUESTION TODO: The old predicate evaluation took into
! 12436: * account location-sets.
! 12437: * (E.g. ctxt->value->type == XPATH_LOCATIONSET)
! 12438: * Do we expect such a set here?
! 12439: * All what I learned now from the evaluation semantics
! 12440: * does not indicate that a location-set will be processed
! 12441: * here, so this looks OK.
! 12442: */
! 12443: /*
! 12444: * Iterate over all predicates, starting with the outermost
! 12445: * predicate.
! 12446: * TODO: Problem: we cannot execute the inner predicates first
! 12447: * since we cannot go back *up* the operator tree!
! 12448: * Options we have:
! 12449: * 1) Use of recursive functions (like is it currently done
! 12450: * via xmlXPathCompOpEval())
! 12451: * 2) Add a predicate evaluation information stack to the
! 12452: * context struct
! 12453: * 3) Change the way the operators are linked; we need a
! 12454: * "parent" field on xmlXPathStepOp
! 12455: *
! 12456: * For the moment, I'll try to solve this with a recursive
! 12457: * function: xmlXPathCompOpEvalPredicate().
! 12458: */
! 12459: size = seq->nodeNr;
! 12460: if (hasPredicateRange != 0)
! 12461: newSize = xmlXPathCompOpEvalPositionalPredicate(ctxt,
! 12462: predOp, seq, size, maxPos, maxPos, hasNsNodes);
! 12463: else
! 12464: newSize = xmlXPathCompOpEvalPredicate(ctxt,
! 12465: predOp, seq, size, hasNsNodes);
! 12466:
! 12467: if (ctxt->error != XPATH_EXPRESSION_OK) {
! 12468: total = 0;
! 12469: goto error;
! 12470: }
! 12471: /*
! 12472: * Add the filtered set of nodes to the result node set.
! 12473: */
! 12474: if (newSize == 0) {
! 12475: /*
! 12476: * The predicates filtered all nodes out.
! 12477: */
! 12478: xmlXPathNodeSetClear(seq, hasNsNodes);
! 12479: } else if (seq->nodeNr > 0) {
! 12480: /*
! 12481: * Add to result set.
! 12482: */
! 12483: if (outSeq == NULL) {
! 12484: if (size != newSize) {
! 12485: /*
! 12486: * We need to merge and clear here, since
! 12487: * the sequence will contained NULLed entries.
! 12488: */
! 12489: outSeq = mergeAndClear(NULL, seq, 1);
! 12490: } else {
! 12491: outSeq = seq;
! 12492: seq = NULL;
! 12493: }
! 12494: } else
! 12495: outSeq = mergeAndClear(outSeq, seq,
! 12496: (size != newSize) ? 1: 0);
! 12497: /*
! 12498: * Break if only a true/false result was requested.
! 12499: */
! 12500: if (toBool)
! 12501: break;
! 12502: }
! 12503: } else if (seq->nodeNr > 0) {
! 12504: /*
! 12505: * Add to result set.
! 12506: */
! 12507: if (outSeq == NULL) {
! 12508: outSeq = seq;
! 12509: seq = NULL;
! 12510: } else {
! 12511: outSeq = mergeAndClear(outSeq, seq, 0);
! 12512: }
! 12513: }
! 12514: }
! 12515:
! 12516: error:
! 12517: if ((obj->boolval) && (obj->user != NULL)) {
! 12518: /*
! 12519: * QUESTION TODO: What does this do and why?
! 12520: * TODO: Do we have to do this also for the "error"
! 12521: * cleanup further down?
! 12522: */
! 12523: ctxt->value->boolval = 1;
! 12524: ctxt->value->user = obj->user;
! 12525: obj->user = NULL;
! 12526: obj->boolval = 0;
! 12527: }
! 12528: xmlXPathReleaseObject(xpctxt, obj);
! 12529:
! 12530: /*
! 12531: * Ensure we return at least an emtpy set.
! 12532: */
! 12533: if (outSeq == NULL) {
! 12534: if ((seq != NULL) && (seq->nodeNr == 0))
! 12535: outSeq = seq;
! 12536: else
! 12537: outSeq = xmlXPathNodeSetCreate(NULL);
! 12538: /* XXX what if xmlXPathNodeSetCreate returned NULL here? */
! 12539: }
! 12540: if ((seq != NULL) && (seq != outSeq)) {
! 12541: xmlXPathFreeNodeSet(seq);
! 12542: }
! 12543: /*
! 12544: * Hand over the result. Better to push the set also in
! 12545: * case of errors.
! 12546: */
! 12547: valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, outSeq));
! 12548: /*
! 12549: * Reset the context node.
! 12550: */
! 12551: xpctxt->node = oldContextNode;
! 12552:
! 12553: #ifdef DEBUG_STEP
! 12554: xmlGenericError(xmlGenericErrorContext,
! 12555: "\nExamined %d nodes, found %d nodes at that step\n",
! 12556: total, nbMatches);
! 12557: #endif
! 12558:
! 12559: return(total);
! 12560: }
! 12561:
! 12562: static int
! 12563: xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
! 12564: xmlXPathStepOpPtr op, xmlNodePtr * first);
! 12565:
! 12566: /**
! 12567: * xmlXPathCompOpEvalFirst:
! 12568: * @ctxt: the XPath parser context with the compiled expression
! 12569: * @op: an XPath compiled operation
! 12570: * @first: the first elem found so far
! 12571: *
! 12572: * Evaluate the Precompiled XPath operation searching only the first
! 12573: * element in document order
! 12574: *
! 12575: * Returns the number of examined objects.
! 12576: */
! 12577: static int
! 12578: xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
! 12579: xmlXPathStepOpPtr op, xmlNodePtr * first)
! 12580: {
! 12581: int total = 0, cur;
! 12582: xmlXPathCompExprPtr comp;
! 12583: xmlXPathObjectPtr arg1, arg2;
! 12584:
! 12585: CHECK_ERROR0;
! 12586: comp = ctxt->comp;
! 12587: switch (op->op) {
! 12588: case XPATH_OP_END:
! 12589: return (0);
! 12590: case XPATH_OP_UNION:
! 12591: total =
! 12592: xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
! 12593: first);
! 12594: CHECK_ERROR0;
! 12595: if ((ctxt->value != NULL)
! 12596: && (ctxt->value->type == XPATH_NODESET)
! 12597: && (ctxt->value->nodesetval != NULL)
! 12598: && (ctxt->value->nodesetval->nodeNr >= 1)) {
! 12599: /*
! 12600: * limit tree traversing to first node in the result
! 12601: */
! 12602: /*
! 12603: * OPTIMIZE TODO: This implicitely sorts
! 12604: * the result, even if not needed. E.g. if the argument
! 12605: * of the count() function, no sorting is needed.
! 12606: * OPTIMIZE TODO: How do we know if the node-list wasn't
! 12607: * aready sorted?
! 12608: */
! 12609: if (ctxt->value->nodesetval->nodeNr > 1)
! 12610: xmlXPathNodeSetSort(ctxt->value->nodesetval);
! 12611: *first = ctxt->value->nodesetval->nodeTab[0];
! 12612: }
! 12613: cur =
! 12614: xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
! 12615: first);
! 12616: CHECK_ERROR0;
! 12617: CHECK_TYPE0(XPATH_NODESET);
! 12618: arg2 = valuePop(ctxt);
! 12619:
! 12620: CHECK_TYPE0(XPATH_NODESET);
! 12621: arg1 = valuePop(ctxt);
! 12622:
! 12623: arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
! 12624: arg2->nodesetval);
! 12625: valuePush(ctxt, arg1);
! 12626: xmlXPathReleaseObject(ctxt->context, arg2);
! 12627: /* optimizer */
! 12628: if (total > cur)
! 12629: xmlXPathCompSwap(op);
! 12630: return (total + cur);
! 12631: case XPATH_OP_ROOT:
! 12632: xmlXPathRoot(ctxt);
! 12633: return (0);
! 12634: case XPATH_OP_NODE:
! 12635: if (op->ch1 != -1)
! 12636: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 12637: CHECK_ERROR0;
! 12638: if (op->ch2 != -1)
! 12639: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 12640: CHECK_ERROR0;
! 12641: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
! 12642: ctxt->context->node));
! 12643: return (total);
! 12644: case XPATH_OP_RESET:
! 12645: if (op->ch1 != -1)
! 12646: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 12647: CHECK_ERROR0;
! 12648: if (op->ch2 != -1)
! 12649: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 12650: CHECK_ERROR0;
! 12651: ctxt->context->node = NULL;
! 12652: return (total);
! 12653: case XPATH_OP_COLLECT:{
! 12654: if (op->ch1 == -1)
! 12655: return (total);
! 12656:
! 12657: total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 12658: CHECK_ERROR0;
! 12659:
! 12660: total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL, 0);
! 12661: return (total);
! 12662: }
! 12663: case XPATH_OP_VALUE:
! 12664: valuePush(ctxt,
! 12665: xmlXPathCacheObjectCopy(ctxt->context,
! 12666: (xmlXPathObjectPtr) op->value4));
! 12667: return (0);
! 12668: case XPATH_OP_SORT:
! 12669: if (op->ch1 != -1)
! 12670: total +=
! 12671: xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
! 12672: first);
! 12673: CHECK_ERROR0;
! 12674: if ((ctxt->value != NULL)
! 12675: && (ctxt->value->type == XPATH_NODESET)
! 12676: && (ctxt->value->nodesetval != NULL)
! 12677: && (ctxt->value->nodesetval->nodeNr > 1))
! 12678: xmlXPathNodeSetSort(ctxt->value->nodesetval);
! 12679: return (total);
! 12680: #ifdef XP_OPTIMIZED_FILTER_FIRST
! 12681: case XPATH_OP_FILTER:
! 12682: total += xmlXPathCompOpEvalFilterFirst(ctxt, op, first);
! 12683: return (total);
! 12684: #endif
! 12685: default:
! 12686: return (xmlXPathCompOpEval(ctxt, op));
! 12687: }
! 12688: }
! 12689:
! 12690: /**
! 12691: * xmlXPathCompOpEvalLast:
! 12692: * @ctxt: the XPath parser context with the compiled expression
! 12693: * @op: an XPath compiled operation
! 12694: * @last: the last elem found so far
! 12695: *
! 12696: * Evaluate the Precompiled XPath operation searching only the last
! 12697: * element in document order
! 12698: *
! 12699: * Returns the number of nodes traversed
! 12700: */
! 12701: static int
! 12702: xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
! 12703: xmlNodePtr * last)
! 12704: {
! 12705: int total = 0, cur;
! 12706: xmlXPathCompExprPtr comp;
! 12707: xmlXPathObjectPtr arg1, arg2;
! 12708: xmlNodePtr bak;
! 12709: xmlDocPtr bakd;
! 12710: int pp;
! 12711: int cs;
! 12712:
! 12713: CHECK_ERROR0;
! 12714: comp = ctxt->comp;
! 12715: switch (op->op) {
! 12716: case XPATH_OP_END:
! 12717: return (0);
! 12718: case XPATH_OP_UNION:
! 12719: bakd = ctxt->context->doc;
! 12720: bak = ctxt->context->node;
! 12721: pp = ctxt->context->proximityPosition;
! 12722: cs = ctxt->context->contextSize;
! 12723: total =
! 12724: xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
! 12725: CHECK_ERROR0;
! 12726: if ((ctxt->value != NULL)
! 12727: && (ctxt->value->type == XPATH_NODESET)
! 12728: && (ctxt->value->nodesetval != NULL)
! 12729: && (ctxt->value->nodesetval->nodeNr >= 1)) {
! 12730: /*
! 12731: * limit tree traversing to first node in the result
! 12732: */
! 12733: if (ctxt->value->nodesetval->nodeNr > 1)
! 12734: xmlXPathNodeSetSort(ctxt->value->nodesetval);
! 12735: *last =
! 12736: ctxt->value->nodesetval->nodeTab[ctxt->value->
! 12737: nodesetval->nodeNr -
! 12738: 1];
! 12739: }
! 12740: ctxt->context->doc = bakd;
! 12741: ctxt->context->node = bak;
! 12742: ctxt->context->proximityPosition = pp;
! 12743: ctxt->context->contextSize = cs;
! 12744: cur =
! 12745: xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
! 12746: CHECK_ERROR0;
! 12747: if ((ctxt->value != NULL)
! 12748: && (ctxt->value->type == XPATH_NODESET)
! 12749: && (ctxt->value->nodesetval != NULL)
! 12750: && (ctxt->value->nodesetval->nodeNr >= 1)) { /* TODO: NOP ? */
! 12751: }
! 12752: CHECK_TYPE0(XPATH_NODESET);
! 12753: arg2 = valuePop(ctxt);
! 12754:
! 12755: CHECK_TYPE0(XPATH_NODESET);
! 12756: arg1 = valuePop(ctxt);
! 12757:
! 12758: arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
! 12759: arg2->nodesetval);
! 12760: valuePush(ctxt, arg1);
! 12761: xmlXPathReleaseObject(ctxt->context, arg2);
! 12762: /* optimizer */
! 12763: if (total > cur)
! 12764: xmlXPathCompSwap(op);
! 12765: return (total + cur);
! 12766: case XPATH_OP_ROOT:
! 12767: xmlXPathRoot(ctxt);
! 12768: return (0);
! 12769: case XPATH_OP_NODE:
! 12770: if (op->ch1 != -1)
! 12771: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 12772: CHECK_ERROR0;
! 12773: if (op->ch2 != -1)
! 12774: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 12775: CHECK_ERROR0;
! 12776: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
! 12777: ctxt->context->node));
! 12778: return (total);
! 12779: case XPATH_OP_RESET:
! 12780: if (op->ch1 != -1)
! 12781: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 12782: CHECK_ERROR0;
! 12783: if (op->ch2 != -1)
! 12784: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 12785: CHECK_ERROR0;
! 12786: ctxt->context->node = NULL;
! 12787: return (total);
! 12788: case XPATH_OP_COLLECT:{
! 12789: if (op->ch1 == -1)
! 12790: return (0);
! 12791:
! 12792: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 12793: CHECK_ERROR0;
! 12794:
! 12795: total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last, 0);
! 12796: return (total);
! 12797: }
! 12798: case XPATH_OP_VALUE:
! 12799: valuePush(ctxt,
! 12800: xmlXPathCacheObjectCopy(ctxt->context,
! 12801: (xmlXPathObjectPtr) op->value4));
! 12802: return (0);
! 12803: case XPATH_OP_SORT:
! 12804: if (op->ch1 != -1)
! 12805: total +=
! 12806: xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
! 12807: last);
! 12808: CHECK_ERROR0;
! 12809: if ((ctxt->value != NULL)
! 12810: && (ctxt->value->type == XPATH_NODESET)
! 12811: && (ctxt->value->nodesetval != NULL)
! 12812: && (ctxt->value->nodesetval->nodeNr > 1))
! 12813: xmlXPathNodeSetSort(ctxt->value->nodesetval);
! 12814: return (total);
! 12815: default:
! 12816: return (xmlXPathCompOpEval(ctxt, op));
! 12817: }
! 12818: }
! 12819:
! 12820: #ifdef XP_OPTIMIZED_FILTER_FIRST
! 12821: static int
! 12822: xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
! 12823: xmlXPathStepOpPtr op, xmlNodePtr * first)
! 12824: {
! 12825: int total = 0;
! 12826: xmlXPathCompExprPtr comp;
! 12827: xmlXPathObjectPtr res;
! 12828: xmlXPathObjectPtr obj;
! 12829: xmlNodeSetPtr oldset;
! 12830: xmlNodePtr oldnode;
! 12831: xmlDocPtr oldDoc;
! 12832: int i;
! 12833:
! 12834: CHECK_ERROR0;
! 12835: comp = ctxt->comp;
! 12836: /*
! 12837: * Optimization for ()[last()] selection i.e. the last elem
! 12838: */
! 12839: if ((op->ch1 != -1) && (op->ch2 != -1) &&
! 12840: (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
! 12841: (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
! 12842: int f = comp->steps[op->ch2].ch1;
! 12843:
! 12844: if ((f != -1) &&
! 12845: (comp->steps[f].op == XPATH_OP_FUNCTION) &&
! 12846: (comp->steps[f].value5 == NULL) &&
! 12847: (comp->steps[f].value == 0) &&
! 12848: (comp->steps[f].value4 != NULL) &&
! 12849: (xmlStrEqual
! 12850: (comp->steps[f].value4, BAD_CAST "last"))) {
! 12851: xmlNodePtr last = NULL;
! 12852:
! 12853: total +=
! 12854: xmlXPathCompOpEvalLast(ctxt,
! 12855: &comp->steps[op->ch1],
! 12856: &last);
! 12857: CHECK_ERROR0;
! 12858: /*
! 12859: * The nodeset should be in document order,
! 12860: * Keep only the last value
! 12861: */
! 12862: if ((ctxt->value != NULL) &&
! 12863: (ctxt->value->type == XPATH_NODESET) &&
! 12864: (ctxt->value->nodesetval != NULL) &&
! 12865: (ctxt->value->nodesetval->nodeTab != NULL) &&
! 12866: (ctxt->value->nodesetval->nodeNr > 1)) {
! 12867: ctxt->value->nodesetval->nodeTab[0] =
! 12868: ctxt->value->nodesetval->nodeTab[ctxt->
! 12869: value->
! 12870: nodesetval->
! 12871: nodeNr -
! 12872: 1];
! 12873: ctxt->value->nodesetval->nodeNr = 1;
! 12874: *first = *(ctxt->value->nodesetval->nodeTab);
! 12875: }
! 12876: return (total);
! 12877: }
! 12878: }
! 12879:
! 12880: if (op->ch1 != -1)
! 12881: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 12882: CHECK_ERROR0;
! 12883: if (op->ch2 == -1)
! 12884: return (total);
! 12885: if (ctxt->value == NULL)
! 12886: return (total);
! 12887:
! 12888: #ifdef LIBXML_XPTR_ENABLED
! 12889: oldnode = ctxt->context->node;
! 12890: /*
! 12891: * Hum are we filtering the result of an XPointer expression
! 12892: */
! 12893: if (ctxt->value->type == XPATH_LOCATIONSET) {
! 12894: xmlXPathObjectPtr tmp = NULL;
! 12895: xmlLocationSetPtr newlocset = NULL;
! 12896: xmlLocationSetPtr oldlocset;
! 12897:
! 12898: /*
! 12899: * Extract the old locset, and then evaluate the result of the
! 12900: * expression for all the element in the locset. use it to grow
! 12901: * up a new locset.
! 12902: */
! 12903: CHECK_TYPE0(XPATH_LOCATIONSET);
! 12904: obj = valuePop(ctxt);
! 12905: oldlocset = obj->user;
! 12906: ctxt->context->node = NULL;
! 12907:
! 12908: if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
! 12909: ctxt->context->contextSize = 0;
! 12910: ctxt->context->proximityPosition = 0;
! 12911: if (op->ch2 != -1)
! 12912: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 12913: res = valuePop(ctxt);
! 12914: if (res != NULL) {
! 12915: xmlXPathReleaseObject(ctxt->context, res);
! 12916: }
! 12917: valuePush(ctxt, obj);
! 12918: CHECK_ERROR0;
! 12919: return (total);
! 12920: }
! 12921: newlocset = xmlXPtrLocationSetCreate(NULL);
! 12922:
! 12923: for (i = 0; i < oldlocset->locNr; i++) {
! 12924: /*
! 12925: * Run the evaluation with a node list made of a
! 12926: * single item in the nodelocset.
! 12927: */
! 12928: ctxt->context->node = oldlocset->locTab[i]->user;
! 12929: ctxt->context->contextSize = oldlocset->locNr;
! 12930: ctxt->context->proximityPosition = i + 1;
! 12931: if (tmp == NULL) {
! 12932: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
! 12933: ctxt->context->node);
! 12934: } else {
! 12935: xmlXPathNodeSetAddUnique(tmp->nodesetval,
! 12936: ctxt->context->node);
! 12937: }
! 12938: valuePush(ctxt, tmp);
! 12939: if (op->ch2 != -1)
! 12940: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 12941: if (ctxt->error != XPATH_EXPRESSION_OK) {
! 12942: xmlXPathFreeObject(obj);
! 12943: return(0);
! 12944: }
! 12945: /*
! 12946: * The result of the evaluation need to be tested to
! 12947: * decided whether the filter succeeded or not
! 12948: */
! 12949: res = valuePop(ctxt);
! 12950: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
! 12951: xmlXPtrLocationSetAdd(newlocset,
! 12952: xmlXPathCacheObjectCopy(ctxt->context,
! 12953: oldlocset->locTab[i]));
! 12954: }
! 12955: /*
! 12956: * Cleanup
! 12957: */
! 12958: if (res != NULL) {
! 12959: xmlXPathReleaseObject(ctxt->context, res);
! 12960: }
! 12961: if (ctxt->value == tmp) {
! 12962: valuePop(ctxt);
! 12963: xmlXPathNodeSetClear(tmp->nodesetval, 1);
! 12964: /*
! 12965: * REVISIT TODO: Don't create a temporary nodeset
! 12966: * for everly iteration.
! 12967: */
! 12968: /* OLD: xmlXPathFreeObject(res); */
! 12969: } else
! 12970: tmp = NULL;
! 12971: ctxt->context->node = NULL;
! 12972: /*
! 12973: * Only put the first node in the result, then leave.
! 12974: */
! 12975: if (newlocset->locNr > 0) {
! 12976: *first = (xmlNodePtr) oldlocset->locTab[i]->user;
! 12977: break;
! 12978: }
! 12979: }
! 12980: if (tmp != NULL) {
! 12981: xmlXPathReleaseObject(ctxt->context, tmp);
! 12982: }
! 12983: /*
! 12984: * The result is used as the new evaluation locset.
! 12985: */
! 12986: xmlXPathReleaseObject(ctxt->context, obj);
! 12987: ctxt->context->node = NULL;
! 12988: ctxt->context->contextSize = -1;
! 12989: ctxt->context->proximityPosition = -1;
! 12990: valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
! 12991: ctxt->context->node = oldnode;
! 12992: return (total);
! 12993: }
! 12994: #endif /* LIBXML_XPTR_ENABLED */
! 12995:
! 12996: /*
! 12997: * Extract the old set, and then evaluate the result of the
! 12998: * expression for all the element in the set. use it to grow
! 12999: * up a new set.
! 13000: */
! 13001: CHECK_TYPE0(XPATH_NODESET);
! 13002: obj = valuePop(ctxt);
! 13003: oldset = obj->nodesetval;
! 13004:
! 13005: oldnode = ctxt->context->node;
! 13006: oldDoc = ctxt->context->doc;
! 13007: ctxt->context->node = NULL;
! 13008:
! 13009: if ((oldset == NULL) || (oldset->nodeNr == 0)) {
! 13010: ctxt->context->contextSize = 0;
! 13011: ctxt->context->proximityPosition = 0;
! 13012: /* QUESTION TODO: Why was this code commented out?
! 13013: if (op->ch2 != -1)
! 13014: total +=
! 13015: xmlXPathCompOpEval(ctxt,
! 13016: &comp->steps[op->ch2]);
! 13017: CHECK_ERROR0;
! 13018: res = valuePop(ctxt);
! 13019: if (res != NULL)
! 13020: xmlXPathFreeObject(res);
! 13021: */
! 13022: valuePush(ctxt, obj);
! 13023: ctxt->context->node = oldnode;
! 13024: CHECK_ERROR0;
! 13025: } else {
! 13026: xmlNodeSetPtr newset;
! 13027: xmlXPathObjectPtr tmp = NULL;
! 13028: /*
! 13029: * Initialize the new set.
! 13030: * Also set the xpath document in case things like
! 13031: * key() evaluation are attempted on the predicate
! 13032: */
! 13033: newset = xmlXPathNodeSetCreate(NULL);
! 13034: /* XXX what if xmlXPathNodeSetCreate returned NULL? */
! 13035:
! 13036: for (i = 0; i < oldset->nodeNr; i++) {
! 13037: /*
! 13038: * Run the evaluation with a node list made of
! 13039: * a single item in the nodeset.
! 13040: */
! 13041: ctxt->context->node = oldset->nodeTab[i];
! 13042: if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
! 13043: (oldset->nodeTab[i]->doc != NULL))
! 13044: ctxt->context->doc = oldset->nodeTab[i]->doc;
! 13045: if (tmp == NULL) {
! 13046: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
! 13047: ctxt->context->node);
! 13048: } else {
! 13049: xmlXPathNodeSetAddUnique(tmp->nodesetval,
! 13050: ctxt->context->node);
! 13051: }
! 13052: valuePush(ctxt, tmp);
! 13053: ctxt->context->contextSize = oldset->nodeNr;
! 13054: ctxt->context->proximityPosition = i + 1;
! 13055: if (op->ch2 != -1)
! 13056: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13057: if (ctxt->error != XPATH_EXPRESSION_OK) {
! 13058: xmlXPathFreeNodeSet(newset);
! 13059: xmlXPathFreeObject(obj);
! 13060: return(0);
! 13061: }
! 13062: /*
! 13063: * The result of the evaluation needs to be tested to
! 13064: * decide whether the filter succeeded or not
! 13065: */
! 13066: res = valuePop(ctxt);
! 13067: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
! 13068: xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
! 13069: }
! 13070: /*
! 13071: * Cleanup
! 13072: */
! 13073: if (res != NULL) {
! 13074: xmlXPathReleaseObject(ctxt->context, res);
! 13075: }
! 13076: if (ctxt->value == tmp) {
! 13077: valuePop(ctxt);
! 13078: /*
! 13079: * Don't free the temporary nodeset
! 13080: * in order to avoid massive recreation inside this
! 13081: * loop.
! 13082: */
! 13083: xmlXPathNodeSetClear(tmp->nodesetval, 1);
! 13084: } else
! 13085: tmp = NULL;
! 13086: ctxt->context->node = NULL;
! 13087: /*
! 13088: * Only put the first node in the result, then leave.
! 13089: */
! 13090: if (newset->nodeNr > 0) {
! 13091: *first = *(newset->nodeTab);
! 13092: break;
! 13093: }
! 13094: }
! 13095: if (tmp != NULL) {
! 13096: xmlXPathReleaseObject(ctxt->context, tmp);
! 13097: }
! 13098: /*
! 13099: * The result is used as the new evaluation set.
! 13100: */
! 13101: xmlXPathReleaseObject(ctxt->context, obj);
! 13102: ctxt->context->node = NULL;
! 13103: ctxt->context->contextSize = -1;
! 13104: ctxt->context->proximityPosition = -1;
! 13105: /* may want to move this past the '}' later */
! 13106: ctxt->context->doc = oldDoc;
! 13107: valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, newset));
! 13108: }
! 13109: ctxt->context->node = oldnode;
! 13110: return(total);
! 13111: }
! 13112: #endif /* XP_OPTIMIZED_FILTER_FIRST */
! 13113:
! 13114: /**
! 13115: * xmlXPathCompOpEval:
! 13116: * @ctxt: the XPath parser context with the compiled expression
! 13117: * @op: an XPath compiled operation
! 13118: *
! 13119: * Evaluate the Precompiled XPath operation
! 13120: * Returns the number of nodes traversed
! 13121: */
! 13122: static int
! 13123: xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
! 13124: {
! 13125: int total = 0;
! 13126: int equal, ret;
! 13127: xmlXPathCompExprPtr comp;
! 13128: xmlXPathObjectPtr arg1, arg2;
! 13129: xmlNodePtr bak;
! 13130: xmlDocPtr bakd;
! 13131: int pp;
! 13132: int cs;
! 13133:
! 13134: CHECK_ERROR0;
! 13135: comp = ctxt->comp;
! 13136: switch (op->op) {
! 13137: case XPATH_OP_END:
! 13138: return (0);
! 13139: case XPATH_OP_AND:
! 13140: bakd = ctxt->context->doc;
! 13141: bak = ctxt->context->node;
! 13142: pp = ctxt->context->proximityPosition;
! 13143: cs = ctxt->context->contextSize;
! 13144: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13145: CHECK_ERROR0;
! 13146: xmlXPathBooleanFunction(ctxt, 1);
! 13147: if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
! 13148: return (total);
! 13149: arg2 = valuePop(ctxt);
! 13150: ctxt->context->doc = bakd;
! 13151: ctxt->context->node = bak;
! 13152: ctxt->context->proximityPosition = pp;
! 13153: ctxt->context->contextSize = cs;
! 13154: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13155: if (ctxt->error) {
! 13156: xmlXPathFreeObject(arg2);
! 13157: return(0);
! 13158: }
! 13159: xmlXPathBooleanFunction(ctxt, 1);
! 13160: arg1 = valuePop(ctxt);
! 13161: arg1->boolval &= arg2->boolval;
! 13162: valuePush(ctxt, arg1);
! 13163: xmlXPathReleaseObject(ctxt->context, arg2);
! 13164: return (total);
! 13165: case XPATH_OP_OR:
! 13166: bakd = ctxt->context->doc;
! 13167: bak = ctxt->context->node;
! 13168: pp = ctxt->context->proximityPosition;
! 13169: cs = ctxt->context->contextSize;
! 13170: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13171: CHECK_ERROR0;
! 13172: xmlXPathBooleanFunction(ctxt, 1);
! 13173: if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
! 13174: return (total);
! 13175: arg2 = valuePop(ctxt);
! 13176: ctxt->context->doc = bakd;
! 13177: ctxt->context->node = bak;
! 13178: ctxt->context->proximityPosition = pp;
! 13179: ctxt->context->contextSize = cs;
! 13180: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13181: if (ctxt->error) {
! 13182: xmlXPathFreeObject(arg2);
! 13183: return(0);
! 13184: }
! 13185: xmlXPathBooleanFunction(ctxt, 1);
! 13186: arg1 = valuePop(ctxt);
! 13187: arg1->boolval |= arg2->boolval;
! 13188: valuePush(ctxt, arg1);
! 13189: xmlXPathReleaseObject(ctxt->context, arg2);
! 13190: return (total);
! 13191: case XPATH_OP_EQUAL:
! 13192: bakd = ctxt->context->doc;
! 13193: bak = ctxt->context->node;
! 13194: pp = ctxt->context->proximityPosition;
! 13195: cs = ctxt->context->contextSize;
! 13196: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13197: CHECK_ERROR0;
! 13198: ctxt->context->doc = bakd;
! 13199: ctxt->context->node = bak;
! 13200: ctxt->context->proximityPosition = pp;
! 13201: ctxt->context->contextSize = cs;
! 13202: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13203: CHECK_ERROR0;
! 13204: if (op->value)
! 13205: equal = xmlXPathEqualValues(ctxt);
! 13206: else
! 13207: equal = xmlXPathNotEqualValues(ctxt);
! 13208: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal));
! 13209: return (total);
! 13210: case XPATH_OP_CMP:
! 13211: bakd = ctxt->context->doc;
! 13212: bak = ctxt->context->node;
! 13213: pp = ctxt->context->proximityPosition;
! 13214: cs = ctxt->context->contextSize;
! 13215: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13216: CHECK_ERROR0;
! 13217: ctxt->context->doc = bakd;
! 13218: ctxt->context->node = bak;
! 13219: ctxt->context->proximityPosition = pp;
! 13220: ctxt->context->contextSize = cs;
! 13221: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13222: CHECK_ERROR0;
! 13223: ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
! 13224: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
! 13225: return (total);
! 13226: case XPATH_OP_PLUS:
! 13227: bakd = ctxt->context->doc;
! 13228: bak = ctxt->context->node;
! 13229: pp = ctxt->context->proximityPosition;
! 13230: cs = ctxt->context->contextSize;
! 13231: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13232: CHECK_ERROR0;
! 13233: if (op->ch2 != -1) {
! 13234: ctxt->context->doc = bakd;
! 13235: ctxt->context->node = bak;
! 13236: ctxt->context->proximityPosition = pp;
! 13237: ctxt->context->contextSize = cs;
! 13238: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13239: }
! 13240: CHECK_ERROR0;
! 13241: if (op->value == 0)
! 13242: xmlXPathSubValues(ctxt);
! 13243: else if (op->value == 1)
! 13244: xmlXPathAddValues(ctxt);
! 13245: else if (op->value == 2)
! 13246: xmlXPathValueFlipSign(ctxt);
! 13247: else if (op->value == 3) {
! 13248: CAST_TO_NUMBER;
! 13249: CHECK_TYPE0(XPATH_NUMBER);
! 13250: }
! 13251: return (total);
! 13252: case XPATH_OP_MULT:
! 13253: bakd = ctxt->context->doc;
! 13254: bak = ctxt->context->node;
! 13255: pp = ctxt->context->proximityPosition;
! 13256: cs = ctxt->context->contextSize;
! 13257: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13258: CHECK_ERROR0;
! 13259: ctxt->context->doc = bakd;
! 13260: ctxt->context->node = bak;
! 13261: ctxt->context->proximityPosition = pp;
! 13262: ctxt->context->contextSize = cs;
! 13263: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13264: CHECK_ERROR0;
! 13265: if (op->value == 0)
! 13266: xmlXPathMultValues(ctxt);
! 13267: else if (op->value == 1)
! 13268: xmlXPathDivValues(ctxt);
! 13269: else if (op->value == 2)
! 13270: xmlXPathModValues(ctxt);
! 13271: return (total);
! 13272: case XPATH_OP_UNION:
! 13273: bakd = ctxt->context->doc;
! 13274: bak = ctxt->context->node;
! 13275: pp = ctxt->context->proximityPosition;
! 13276: cs = ctxt->context->contextSize;
! 13277: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13278: CHECK_ERROR0;
! 13279: ctxt->context->doc = bakd;
! 13280: ctxt->context->node = bak;
! 13281: ctxt->context->proximityPosition = pp;
! 13282: ctxt->context->contextSize = cs;
! 13283: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13284: CHECK_ERROR0;
! 13285: CHECK_TYPE0(XPATH_NODESET);
! 13286: arg2 = valuePop(ctxt);
! 13287:
! 13288: CHECK_TYPE0(XPATH_NODESET);
! 13289: arg1 = valuePop(ctxt);
! 13290:
! 13291: if ((arg1->nodesetval == NULL) ||
! 13292: ((arg2->nodesetval != NULL) &&
! 13293: (arg2->nodesetval->nodeNr != 0)))
! 13294: {
! 13295: arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
! 13296: arg2->nodesetval);
! 13297: }
! 13298:
! 13299: valuePush(ctxt, arg1);
! 13300: xmlXPathReleaseObject(ctxt->context, arg2);
! 13301: return (total);
! 13302: case XPATH_OP_ROOT:
! 13303: xmlXPathRoot(ctxt);
! 13304: return (total);
! 13305: case XPATH_OP_NODE:
! 13306: if (op->ch1 != -1)
! 13307: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13308: CHECK_ERROR0;
! 13309: if (op->ch2 != -1)
! 13310: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13311: CHECK_ERROR0;
! 13312: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
! 13313: ctxt->context->node));
! 13314: return (total);
! 13315: case XPATH_OP_RESET:
! 13316: if (op->ch1 != -1)
! 13317: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13318: CHECK_ERROR0;
! 13319: if (op->ch2 != -1)
! 13320: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13321: CHECK_ERROR0;
! 13322: ctxt->context->node = NULL;
! 13323: return (total);
! 13324: case XPATH_OP_COLLECT:{
! 13325: if (op->ch1 == -1)
! 13326: return (total);
! 13327:
! 13328: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13329: CHECK_ERROR0;
! 13330:
! 13331: total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 0);
! 13332: return (total);
! 13333: }
! 13334: case XPATH_OP_VALUE:
! 13335: valuePush(ctxt,
! 13336: xmlXPathCacheObjectCopy(ctxt->context,
! 13337: (xmlXPathObjectPtr) op->value4));
! 13338: return (total);
! 13339: case XPATH_OP_VARIABLE:{
! 13340: xmlXPathObjectPtr val;
! 13341:
! 13342: if (op->ch1 != -1)
! 13343: total +=
! 13344: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13345: if (op->value5 == NULL) {
! 13346: val = xmlXPathVariableLookup(ctxt->context, op->value4);
! 13347: if (val == NULL) {
! 13348: ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
! 13349: return(0);
! 13350: }
! 13351: valuePush(ctxt, val);
! 13352: } else {
! 13353: const xmlChar *URI;
! 13354:
! 13355: URI = xmlXPathNsLookup(ctxt->context, op->value5);
! 13356: if (URI == NULL) {
! 13357: xmlGenericError(xmlGenericErrorContext,
! 13358: "xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n",
! 13359: (char *) op->value4, (char *)op->value5);
! 13360: return (total);
! 13361: }
! 13362: val = xmlXPathVariableLookupNS(ctxt->context,
! 13363: op->value4, URI);
! 13364: if (val == NULL) {
! 13365: ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
! 13366: return(0);
! 13367: }
! 13368: valuePush(ctxt, val);
! 13369: }
! 13370: return (total);
! 13371: }
! 13372: case XPATH_OP_FUNCTION:{
! 13373: xmlXPathFunction func;
! 13374: const xmlChar *oldFunc, *oldFuncURI;
! 13375: int i;
! 13376:
! 13377: if (op->ch1 != -1)
! 13378: total +=
! 13379: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13380: if (ctxt->valueNr < op->value) {
! 13381: xmlGenericError(xmlGenericErrorContext,
! 13382: "xmlXPathCompOpEval: parameter error\n");
! 13383: ctxt->error = XPATH_INVALID_OPERAND;
! 13384: return (total);
! 13385: }
! 13386: for (i = 0; i < op->value; i++)
! 13387: if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) {
! 13388: xmlGenericError(xmlGenericErrorContext,
! 13389: "xmlXPathCompOpEval: parameter error\n");
! 13390: ctxt->error = XPATH_INVALID_OPERAND;
! 13391: return (total);
! 13392: }
! 13393: if (op->cache != NULL)
! 13394: XML_CAST_FPTR(func) = op->cache;
! 13395: else {
! 13396: const xmlChar *URI = NULL;
! 13397:
! 13398: if (op->value5 == NULL)
! 13399: func =
! 13400: xmlXPathFunctionLookup(ctxt->context,
! 13401: op->value4);
! 13402: else {
! 13403: URI = xmlXPathNsLookup(ctxt->context, op->value5);
! 13404: if (URI == NULL) {
! 13405: xmlGenericError(xmlGenericErrorContext,
! 13406: "xmlXPathCompOpEval: function %s bound to undefined prefix %s\n",
! 13407: (char *)op->value4, (char *)op->value5);
! 13408: return (total);
! 13409: }
! 13410: func = xmlXPathFunctionLookupNS(ctxt->context,
! 13411: op->value4, URI);
! 13412: }
! 13413: if (func == NULL) {
! 13414: xmlGenericError(xmlGenericErrorContext,
! 13415: "xmlXPathCompOpEval: function %s not found\n",
! 13416: (char *)op->value4);
! 13417: XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
! 13418: }
! 13419: op->cache = XML_CAST_FPTR(func);
! 13420: op->cacheURI = (void *) URI;
! 13421: }
! 13422: oldFunc = ctxt->context->function;
! 13423: oldFuncURI = ctxt->context->functionURI;
! 13424: ctxt->context->function = op->value4;
! 13425: ctxt->context->functionURI = op->cacheURI;
! 13426: func(ctxt, op->value);
! 13427: ctxt->context->function = oldFunc;
! 13428: ctxt->context->functionURI = oldFuncURI;
! 13429: return (total);
! 13430: }
! 13431: case XPATH_OP_ARG:
! 13432: bakd = ctxt->context->doc;
! 13433: bak = ctxt->context->node;
! 13434: pp = ctxt->context->proximityPosition;
! 13435: cs = ctxt->context->contextSize;
! 13436: if (op->ch1 != -1)
! 13437: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13438: ctxt->context->contextSize = cs;
! 13439: ctxt->context->proximityPosition = pp;
! 13440: ctxt->context->node = bak;
! 13441: ctxt->context->doc = bakd;
! 13442: CHECK_ERROR0;
! 13443: if (op->ch2 != -1) {
! 13444: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
! 13445: ctxt->context->doc = bakd;
! 13446: ctxt->context->node = bak;
! 13447: CHECK_ERROR0;
! 13448: }
! 13449: return (total);
! 13450: case XPATH_OP_PREDICATE:
! 13451: case XPATH_OP_FILTER:{
! 13452: xmlXPathObjectPtr res;
! 13453: xmlXPathObjectPtr obj, tmp;
! 13454: xmlNodeSetPtr newset = NULL;
! 13455: xmlNodeSetPtr oldset;
! 13456: xmlNodePtr oldnode;
! 13457: xmlDocPtr oldDoc;
! 13458: int i;
! 13459:
! 13460: /*
! 13461: * Optimization for ()[1] selection i.e. the first elem
! 13462: */
! 13463: if ((op->ch1 != -1) && (op->ch2 != -1) &&
! 13464: #ifdef XP_OPTIMIZED_FILTER_FIRST
! 13465: /*
! 13466: * FILTER TODO: Can we assume that the inner processing
! 13467: * will result in an ordered list if we have an
! 13468: * XPATH_OP_FILTER?
! 13469: * What about an additional field or flag on
! 13470: * xmlXPathObject like @sorted ? This way we wouln'd need
! 13471: * to assume anything, so it would be more robust and
! 13472: * easier to optimize.
! 13473: */
! 13474: ((comp->steps[op->ch1].op == XPATH_OP_SORT) || /* 18 */
! 13475: (comp->steps[op->ch1].op == XPATH_OP_FILTER)) && /* 17 */
! 13476: #else
! 13477: (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
! 13478: #endif
! 13479: (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */
! 13480: xmlXPathObjectPtr val;
! 13481:
! 13482: val = comp->steps[op->ch2].value4;
! 13483: if ((val != NULL) && (val->type == XPATH_NUMBER) &&
! 13484: (val->floatval == 1.0)) {
! 13485: xmlNodePtr first = NULL;
! 13486:
! 13487: total +=
! 13488: xmlXPathCompOpEvalFirst(ctxt,
! 13489: &comp->steps[op->ch1],
! 13490: &first);
! 13491: CHECK_ERROR0;
! 13492: /*
! 13493: * The nodeset should be in document order,
! 13494: * Keep only the first value
! 13495: */
! 13496: if ((ctxt->value != NULL) &&
! 13497: (ctxt->value->type == XPATH_NODESET) &&
! 13498: (ctxt->value->nodesetval != NULL) &&
! 13499: (ctxt->value->nodesetval->nodeNr > 1))
! 13500: ctxt->value->nodesetval->nodeNr = 1;
! 13501: return (total);
! 13502: }
! 13503: }
! 13504: /*
! 13505: * Optimization for ()[last()] selection i.e. the last elem
! 13506: */
! 13507: if ((op->ch1 != -1) && (op->ch2 != -1) &&
! 13508: (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
! 13509: (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
! 13510: int f = comp->steps[op->ch2].ch1;
! 13511:
! 13512: if ((f != -1) &&
! 13513: (comp->steps[f].op == XPATH_OP_FUNCTION) &&
! 13514: (comp->steps[f].value5 == NULL) &&
! 13515: (comp->steps[f].value == 0) &&
! 13516: (comp->steps[f].value4 != NULL) &&
! 13517: (xmlStrEqual
! 13518: (comp->steps[f].value4, BAD_CAST "last"))) {
! 13519: xmlNodePtr last = NULL;
! 13520:
! 13521: total +=
! 13522: xmlXPathCompOpEvalLast(ctxt,
! 13523: &comp->steps[op->ch1],
! 13524: &last);
! 13525: CHECK_ERROR0;
! 13526: /*
! 13527: * The nodeset should be in document order,
! 13528: * Keep only the last value
! 13529: */
! 13530: if ((ctxt->value != NULL) &&
! 13531: (ctxt->value->type == XPATH_NODESET) &&
! 13532: (ctxt->value->nodesetval != NULL) &&
! 13533: (ctxt->value->nodesetval->nodeTab != NULL) &&
! 13534: (ctxt->value->nodesetval->nodeNr > 1)) {
! 13535: ctxt->value->nodesetval->nodeTab[0] =
! 13536: ctxt->value->nodesetval->nodeTab[ctxt->
! 13537: value->
! 13538: nodesetval->
! 13539: nodeNr -
! 13540: 1];
! 13541: ctxt->value->nodesetval->nodeNr = 1;
! 13542: }
! 13543: return (total);
! 13544: }
! 13545: }
! 13546: /*
! 13547: * Process inner predicates first.
! 13548: * Example "index[parent::book][1]":
! 13549: * ...
! 13550: * PREDICATE <-- we are here "[1]"
! 13551: * PREDICATE <-- process "[parent::book]" first
! 13552: * SORT
! 13553: * COLLECT 'parent' 'name' 'node' book
! 13554: * NODE
! 13555: * ELEM Object is a number : 1
! 13556: */
! 13557: if (op->ch1 != -1)
! 13558: total +=
! 13559: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13560: CHECK_ERROR0;
! 13561: if (op->ch2 == -1)
! 13562: return (total);
! 13563: if (ctxt->value == NULL)
! 13564: return (total);
! 13565:
! 13566: oldnode = ctxt->context->node;
! 13567:
! 13568: #ifdef LIBXML_XPTR_ENABLED
! 13569: /*
! 13570: * Hum are we filtering the result of an XPointer expression
! 13571: */
! 13572: if (ctxt->value->type == XPATH_LOCATIONSET) {
! 13573: xmlLocationSetPtr newlocset = NULL;
! 13574: xmlLocationSetPtr oldlocset;
! 13575:
! 13576: /*
! 13577: * Extract the old locset, and then evaluate the result of the
! 13578: * expression for all the element in the locset. use it to grow
! 13579: * up a new locset.
! 13580: */
! 13581: CHECK_TYPE0(XPATH_LOCATIONSET);
! 13582: obj = valuePop(ctxt);
! 13583: oldlocset = obj->user;
! 13584: ctxt->context->node = NULL;
! 13585:
! 13586: if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
! 13587: ctxt->context->contextSize = 0;
! 13588: ctxt->context->proximityPosition = 0;
! 13589: if (op->ch2 != -1)
! 13590: total +=
! 13591: xmlXPathCompOpEval(ctxt,
! 13592: &comp->steps[op->ch2]);
! 13593: res = valuePop(ctxt);
! 13594: if (res != NULL) {
! 13595: xmlXPathReleaseObject(ctxt->context, res);
! 13596: }
! 13597: valuePush(ctxt, obj);
! 13598: CHECK_ERROR0;
! 13599: return (total);
! 13600: }
! 13601: newlocset = xmlXPtrLocationSetCreate(NULL);
! 13602:
! 13603: for (i = 0; i < oldlocset->locNr; i++) {
! 13604: /*
! 13605: * Run the evaluation with a node list made of a
! 13606: * single item in the nodelocset.
! 13607: */
! 13608: ctxt->context->node = oldlocset->locTab[i]->user;
! 13609: ctxt->context->contextSize = oldlocset->locNr;
! 13610: ctxt->context->proximityPosition = i + 1;
! 13611: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
! 13612: ctxt->context->node);
! 13613: valuePush(ctxt, tmp);
! 13614:
! 13615: if (op->ch2 != -1)
! 13616: total +=
! 13617: xmlXPathCompOpEval(ctxt,
! 13618: &comp->steps[op->ch2]);
! 13619: if (ctxt->error != XPATH_EXPRESSION_OK) {
! 13620: xmlXPathFreeObject(obj);
! 13621: return(0);
! 13622: }
! 13623:
! 13624: /*
! 13625: * The result of the evaluation need to be tested to
! 13626: * decided whether the filter succeeded or not
! 13627: */
! 13628: res = valuePop(ctxt);
! 13629: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
! 13630: xmlXPtrLocationSetAdd(newlocset,
! 13631: xmlXPathObjectCopy
! 13632: (oldlocset->locTab[i]));
! 13633: }
! 13634:
! 13635: /*
! 13636: * Cleanup
! 13637: */
! 13638: if (res != NULL) {
! 13639: xmlXPathReleaseObject(ctxt->context, res);
! 13640: }
! 13641: if (ctxt->value == tmp) {
! 13642: res = valuePop(ctxt);
! 13643: xmlXPathReleaseObject(ctxt->context, res);
! 13644: }
! 13645:
! 13646: ctxt->context->node = NULL;
! 13647: }
! 13648:
! 13649: /*
! 13650: * The result is used as the new evaluation locset.
! 13651: */
! 13652: xmlXPathReleaseObject(ctxt->context, obj);
! 13653: ctxt->context->node = NULL;
! 13654: ctxt->context->contextSize = -1;
! 13655: ctxt->context->proximityPosition = -1;
! 13656: valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
! 13657: ctxt->context->node = oldnode;
! 13658: return (total);
! 13659: }
! 13660: #endif /* LIBXML_XPTR_ENABLED */
! 13661:
! 13662: /*
! 13663: * Extract the old set, and then evaluate the result of the
! 13664: * expression for all the element in the set. use it to grow
! 13665: * up a new set.
! 13666: */
! 13667: CHECK_TYPE0(XPATH_NODESET);
! 13668: obj = valuePop(ctxt);
! 13669: oldset = obj->nodesetval;
! 13670:
! 13671: oldnode = ctxt->context->node;
! 13672: oldDoc = ctxt->context->doc;
! 13673: ctxt->context->node = NULL;
! 13674:
! 13675: if ((oldset == NULL) || (oldset->nodeNr == 0)) {
! 13676: ctxt->context->contextSize = 0;
! 13677: ctxt->context->proximityPosition = 0;
! 13678: /*
! 13679: if (op->ch2 != -1)
! 13680: total +=
! 13681: xmlXPathCompOpEval(ctxt,
! 13682: &comp->steps[op->ch2]);
! 13683: CHECK_ERROR0;
! 13684: res = valuePop(ctxt);
! 13685: if (res != NULL)
! 13686: xmlXPathFreeObject(res);
! 13687: */
! 13688: valuePush(ctxt, obj);
! 13689: ctxt->context->node = oldnode;
! 13690: CHECK_ERROR0;
! 13691: } else {
! 13692: tmp = NULL;
! 13693: /*
! 13694: * Initialize the new set.
! 13695: * Also set the xpath document in case things like
! 13696: * key() evaluation are attempted on the predicate
! 13697: */
! 13698: newset = xmlXPathNodeSetCreate(NULL);
! 13699: /*
! 13700: * SPEC XPath 1.0:
! 13701: * "For each node in the node-set to be filtered, the
! 13702: * PredicateExpr is evaluated with that node as the
! 13703: * context node, with the number of nodes in the
! 13704: * node-set as the context size, and with the proximity
! 13705: * position of the node in the node-set with respect to
! 13706: * the axis as the context position;"
! 13707: * @oldset is the node-set" to be filtered.
! 13708: *
! 13709: * SPEC XPath 1.0:
! 13710: * "only predicates change the context position and
! 13711: * context size (see [2.4 Predicates])."
! 13712: * Example:
! 13713: * node-set context pos
! 13714: * nA 1
! 13715: * nB 2
! 13716: * nC 3
! 13717: * After applying predicate [position() > 1] :
! 13718: * node-set context pos
! 13719: * nB 1
! 13720: * nC 2
! 13721: *
! 13722: * removed the first node in the node-set, then
! 13723: * the context position of the
! 13724: */
! 13725: for (i = 0; i < oldset->nodeNr; i++) {
! 13726: /*
! 13727: * Run the evaluation with a node list made of
! 13728: * a single item in the nodeset.
! 13729: */
! 13730: ctxt->context->node = oldset->nodeTab[i];
! 13731: if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
! 13732: (oldset->nodeTab[i]->doc != NULL))
! 13733: ctxt->context->doc = oldset->nodeTab[i]->doc;
! 13734: if (tmp == NULL) {
! 13735: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
! 13736: ctxt->context->node);
! 13737: } else {
! 13738: xmlXPathNodeSetAddUnique(tmp->nodesetval,
! 13739: ctxt->context->node);
! 13740: }
! 13741: valuePush(ctxt, tmp);
! 13742: ctxt->context->contextSize = oldset->nodeNr;
! 13743: ctxt->context->proximityPosition = i + 1;
! 13744: /*
! 13745: * Evaluate the predicate against the context node.
! 13746: * Can/should we optimize position() predicates
! 13747: * here (e.g. "[1]")?
! 13748: */
! 13749: if (op->ch2 != -1)
! 13750: total +=
! 13751: xmlXPathCompOpEval(ctxt,
! 13752: &comp->steps[op->ch2]);
! 13753: if (ctxt->error != XPATH_EXPRESSION_OK) {
! 13754: xmlXPathFreeNodeSet(newset);
! 13755: xmlXPathFreeObject(obj);
! 13756: return(0);
! 13757: }
! 13758:
! 13759: /*
! 13760: * The result of the evaluation needs to be tested to
! 13761: * decide whether the filter succeeded or not
! 13762: */
! 13763: /*
! 13764: * OPTIMIZE TODO: Can we use
! 13765: * xmlXPathNodeSetAdd*Unique()* instead?
! 13766: */
! 13767: res = valuePop(ctxt);
! 13768: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
! 13769: xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
! 13770: }
! 13771:
! 13772: /*
! 13773: * Cleanup
! 13774: */
! 13775: if (res != NULL) {
! 13776: xmlXPathReleaseObject(ctxt->context, res);
! 13777: }
! 13778: if (ctxt->value == tmp) {
! 13779: valuePop(ctxt);
! 13780: xmlXPathNodeSetClear(tmp->nodesetval, 1);
! 13781: /*
! 13782: * Don't free the temporary nodeset
! 13783: * in order to avoid massive recreation inside this
! 13784: * loop.
! 13785: */
! 13786: } else
! 13787: tmp = NULL;
! 13788: ctxt->context->node = NULL;
! 13789: }
! 13790: if (tmp != NULL)
! 13791: xmlXPathReleaseObject(ctxt->context, tmp);
! 13792: /*
! 13793: * The result is used as the new evaluation set.
! 13794: */
! 13795: xmlXPathReleaseObject(ctxt->context, obj);
! 13796: ctxt->context->node = NULL;
! 13797: ctxt->context->contextSize = -1;
! 13798: ctxt->context->proximityPosition = -1;
! 13799: /* may want to move this past the '}' later */
! 13800: ctxt->context->doc = oldDoc;
! 13801: valuePush(ctxt,
! 13802: xmlXPathCacheWrapNodeSet(ctxt->context, newset));
! 13803: }
! 13804: ctxt->context->node = oldnode;
! 13805: return (total);
! 13806: }
! 13807: case XPATH_OP_SORT:
! 13808: if (op->ch1 != -1)
! 13809: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13810: CHECK_ERROR0;
! 13811: if ((ctxt->value != NULL) &&
! 13812: (ctxt->value->type == XPATH_NODESET) &&
! 13813: (ctxt->value->nodesetval != NULL) &&
! 13814: (ctxt->value->nodesetval->nodeNr > 1))
! 13815: {
! 13816: xmlXPathNodeSetSort(ctxt->value->nodesetval);
! 13817: }
! 13818: return (total);
! 13819: #ifdef LIBXML_XPTR_ENABLED
! 13820: case XPATH_OP_RANGETO:{
! 13821: xmlXPathObjectPtr range;
! 13822: xmlXPathObjectPtr res, obj;
! 13823: xmlXPathObjectPtr tmp;
! 13824: xmlLocationSetPtr newlocset = NULL;
! 13825: xmlLocationSetPtr oldlocset;
! 13826: xmlNodeSetPtr oldset;
! 13827: int i, j;
! 13828:
! 13829: if (op->ch1 != -1)
! 13830: total +=
! 13831: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
! 13832: if (op->ch2 == -1)
! 13833: return (total);
! 13834:
! 13835: if (ctxt->value->type == XPATH_LOCATIONSET) {
! 13836: /*
! 13837: * Extract the old locset, and then evaluate the result of the
! 13838: * expression for all the element in the locset. use it to grow
! 13839: * up a new locset.
! 13840: */
! 13841: CHECK_TYPE0(XPATH_LOCATIONSET);
! 13842: obj = valuePop(ctxt);
! 13843: oldlocset = obj->user;
! 13844:
! 13845: if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
! 13846: ctxt->context->node = NULL;
! 13847: ctxt->context->contextSize = 0;
! 13848: ctxt->context->proximityPosition = 0;
! 13849: total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]);
! 13850: res = valuePop(ctxt);
! 13851: if (res != NULL) {
! 13852: xmlXPathReleaseObject(ctxt->context, res);
! 13853: }
! 13854: valuePush(ctxt, obj);
! 13855: CHECK_ERROR0;
! 13856: return (total);
! 13857: }
! 13858: newlocset = xmlXPtrLocationSetCreate(NULL);
! 13859:
! 13860: for (i = 0; i < oldlocset->locNr; i++) {
! 13861: /*
! 13862: * Run the evaluation with a node list made of a
! 13863: * single item in the nodelocset.
! 13864: */
! 13865: ctxt->context->node = oldlocset->locTab[i]->user;
! 13866: ctxt->context->contextSize = oldlocset->locNr;
! 13867: ctxt->context->proximityPosition = i + 1;
! 13868: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
! 13869: ctxt->context->node);
! 13870: valuePush(ctxt, tmp);
! 13871:
! 13872: if (op->ch2 != -1)
! 13873: total +=
! 13874: xmlXPathCompOpEval(ctxt,
! 13875: &comp->steps[op->ch2]);
! 13876: if (ctxt->error != XPATH_EXPRESSION_OK) {
! 13877: xmlXPathFreeObject(obj);
! 13878: return(0);
! 13879: }
! 13880:
! 13881: res = valuePop(ctxt);
! 13882: if (res->type == XPATH_LOCATIONSET) {
! 13883: xmlLocationSetPtr rloc =
! 13884: (xmlLocationSetPtr)res->user;
! 13885: for (j=0; j<rloc->locNr; j++) {
! 13886: range = xmlXPtrNewRange(
! 13887: oldlocset->locTab[i]->user,
! 13888: oldlocset->locTab[i]->index,
! 13889: rloc->locTab[j]->user2,
! 13890: rloc->locTab[j]->index2);
! 13891: if (range != NULL) {
! 13892: xmlXPtrLocationSetAdd(newlocset, range);
! 13893: }
! 13894: }
! 13895: } else {
! 13896: range = xmlXPtrNewRangeNodeObject(
! 13897: (xmlNodePtr)oldlocset->locTab[i]->user, res);
! 13898: if (range != NULL) {
! 13899: xmlXPtrLocationSetAdd(newlocset,range);
! 13900: }
! 13901: }
! 13902:
! 13903: /*
! 13904: * Cleanup
! 13905: */
! 13906: if (res != NULL) {
! 13907: xmlXPathReleaseObject(ctxt->context, res);
! 13908: }
! 13909: if (ctxt->value == tmp) {
! 13910: res = valuePop(ctxt);
! 13911: xmlXPathReleaseObject(ctxt->context, res);
! 13912: }
! 13913:
! 13914: ctxt->context->node = NULL;
! 13915: }
! 13916: } else { /* Not a location set */
! 13917: CHECK_TYPE0(XPATH_NODESET);
! 13918: obj = valuePop(ctxt);
! 13919: oldset = obj->nodesetval;
! 13920: ctxt->context->node = NULL;
! 13921:
! 13922: newlocset = xmlXPtrLocationSetCreate(NULL);
! 13923:
! 13924: if (oldset != NULL) {
! 13925: for (i = 0; i < oldset->nodeNr; i++) {
! 13926: /*
! 13927: * Run the evaluation with a node list made of a single item
! 13928: * in the nodeset.
! 13929: */
! 13930: ctxt->context->node = oldset->nodeTab[i];
! 13931: /*
! 13932: * OPTIMIZE TODO: Avoid recreation for every iteration.
! 13933: */
! 13934: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
! 13935: ctxt->context->node);
! 13936: valuePush(ctxt, tmp);
! 13937:
! 13938: if (op->ch2 != -1)
! 13939: total +=
! 13940: xmlXPathCompOpEval(ctxt,
! 13941: &comp->steps[op->ch2]);
! 13942: if (ctxt->error != XPATH_EXPRESSION_OK) {
! 13943: xmlXPathFreeObject(obj);
! 13944: return(0);
! 13945: }
! 13946:
! 13947: res = valuePop(ctxt);
! 13948: range =
! 13949: xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
! 13950: res);
! 13951: if (range != NULL) {
! 13952: xmlXPtrLocationSetAdd(newlocset, range);
! 13953: }
! 13954:
! 13955: /*
! 13956: * Cleanup
! 13957: */
! 13958: if (res != NULL) {
! 13959: xmlXPathReleaseObject(ctxt->context, res);
! 13960: }
! 13961: if (ctxt->value == tmp) {
! 13962: res = valuePop(ctxt);
! 13963: xmlXPathReleaseObject(ctxt->context, res);
! 13964: }
! 13965:
! 13966: ctxt->context->node = NULL;
! 13967: }
! 13968: }
! 13969: }
! 13970:
! 13971: /*
! 13972: * The result is used as the new evaluation set.
! 13973: */
! 13974: xmlXPathReleaseObject(ctxt->context, obj);
! 13975: ctxt->context->node = NULL;
! 13976: ctxt->context->contextSize = -1;
! 13977: ctxt->context->proximityPosition = -1;
! 13978: valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
! 13979: return (total);
! 13980: }
! 13981: #endif /* LIBXML_XPTR_ENABLED */
! 13982: }
! 13983: xmlGenericError(xmlGenericErrorContext,
! 13984: "XPath: unknown precompiled operation %d\n", op->op);
! 13985: return (total);
! 13986: }
! 13987:
! 13988: /**
! 13989: * xmlXPathCompOpEvalToBoolean:
! 13990: * @ctxt: the XPath parser context
! 13991: *
! 13992: * Evaluates if the expression evaluates to true.
! 13993: *
! 13994: * Returns 1 if true, 0 if false and -1 on API or internal errors.
! 13995: */
! 13996: static int
! 13997: xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
! 13998: xmlXPathStepOpPtr op,
! 13999: int isPredicate)
! 14000: {
! 14001: xmlXPathObjectPtr resObj = NULL;
! 14002:
! 14003: start:
! 14004: /* comp = ctxt->comp; */
! 14005: switch (op->op) {
! 14006: case XPATH_OP_END:
! 14007: return (0);
! 14008: case XPATH_OP_VALUE:
! 14009: resObj = (xmlXPathObjectPtr) op->value4;
! 14010: if (isPredicate)
! 14011: return(xmlXPathEvaluatePredicateResult(ctxt, resObj));
! 14012: return(xmlXPathCastToBoolean(resObj));
! 14013: case XPATH_OP_SORT:
! 14014: /*
! 14015: * We don't need sorting for boolean results. Skip this one.
! 14016: */
! 14017: if (op->ch1 != -1) {
! 14018: op = &ctxt->comp->steps[op->ch1];
! 14019: goto start;
! 14020: }
! 14021: return(0);
! 14022: case XPATH_OP_COLLECT:
! 14023: if (op->ch1 == -1)
! 14024: return(0);
! 14025:
! 14026: xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch1]);
! 14027: if (ctxt->error != XPATH_EXPRESSION_OK)
! 14028: return(-1);
! 14029:
! 14030: xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 1);
! 14031: if (ctxt->error != XPATH_EXPRESSION_OK)
! 14032: return(-1);
! 14033:
! 14034: resObj = valuePop(ctxt);
! 14035: if (resObj == NULL)
! 14036: return(-1);
! 14037: break;
! 14038: default:
! 14039: /*
! 14040: * Fallback to call xmlXPathCompOpEval().
! 14041: */
! 14042: xmlXPathCompOpEval(ctxt, op);
! 14043: if (ctxt->error != XPATH_EXPRESSION_OK)
! 14044: return(-1);
! 14045:
! 14046: resObj = valuePop(ctxt);
! 14047: if (resObj == NULL)
! 14048: return(-1);
! 14049: break;
! 14050: }
! 14051:
! 14052: if (resObj) {
! 14053: int res;
! 14054:
! 14055: if (resObj->type == XPATH_BOOLEAN) {
! 14056: res = resObj->boolval;
! 14057: } else if (isPredicate) {
! 14058: /*
! 14059: * For predicates a result of type "number" is handled
! 14060: * differently:
! 14061: * SPEC XPath 1.0:
! 14062: * "If the result is a number, the result will be converted
! 14063: * to true if the number is equal to the context position
! 14064: * and will be converted to false otherwise;"
! 14065: */
! 14066: res = xmlXPathEvaluatePredicateResult(ctxt, resObj);
! 14067: } else {
! 14068: res = xmlXPathCastToBoolean(resObj);
! 14069: }
! 14070: xmlXPathReleaseObject(ctxt->context, resObj);
! 14071: return(res);
! 14072: }
! 14073:
! 14074: return(0);
! 14075: }
! 14076:
! 14077: #ifdef XPATH_STREAMING
! 14078: /**
! 14079: * xmlXPathRunStreamEval:
! 14080: * @ctxt: the XPath parser context with the compiled expression
! 14081: *
! 14082: * Evaluate the Precompiled Streamable XPath expression in the given context.
! 14083: */
! 14084: static int
! 14085: xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp,
! 14086: xmlXPathObjectPtr *resultSeq, int toBool)
! 14087: {
! 14088: int max_depth, min_depth;
! 14089: int from_root;
! 14090: int ret, depth;
! 14091: int eval_all_nodes;
! 14092: xmlNodePtr cur = NULL, limit = NULL;
! 14093: xmlStreamCtxtPtr patstream = NULL;
! 14094:
! 14095: int nb_nodes = 0;
! 14096:
! 14097: if ((ctxt == NULL) || (comp == NULL))
! 14098: return(-1);
! 14099: max_depth = xmlPatternMaxDepth(comp);
! 14100: if (max_depth == -1)
! 14101: return(-1);
! 14102: if (max_depth == -2)
! 14103: max_depth = 10000;
! 14104: min_depth = xmlPatternMinDepth(comp);
! 14105: if (min_depth == -1)
! 14106: return(-1);
! 14107: from_root = xmlPatternFromRoot(comp);
! 14108: if (from_root < 0)
! 14109: return(-1);
! 14110: #if 0
! 14111: printf("stream eval: depth %d from root %d\n", max_depth, from_root);
! 14112: #endif
! 14113:
! 14114: if (! toBool) {
! 14115: if (resultSeq == NULL)
! 14116: return(-1);
! 14117: *resultSeq = xmlXPathCacheNewNodeSet(ctxt, NULL);
! 14118: if (*resultSeq == NULL)
! 14119: return(-1);
! 14120: }
! 14121:
! 14122: /*
! 14123: * handle the special cases of "/" amd "." being matched
! 14124: */
! 14125: if (min_depth == 0) {
! 14126: if (from_root) {
! 14127: /* Select "/" */
! 14128: if (toBool)
! 14129: return(1);
! 14130: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval,
! 14131: (xmlNodePtr) ctxt->doc);
! 14132: } else {
! 14133: /* Select "self::node()" */
! 14134: if (toBool)
! 14135: return(1);
! 14136: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, ctxt->node);
! 14137: }
! 14138: }
! 14139: if (max_depth == 0) {
! 14140: return(0);
! 14141: }
! 14142:
! 14143: if (from_root) {
! 14144: cur = (xmlNodePtr)ctxt->doc;
! 14145: } else if (ctxt->node != NULL) {
! 14146: switch (ctxt->node->type) {
! 14147: case XML_ELEMENT_NODE:
! 14148: case XML_DOCUMENT_NODE:
! 14149: case XML_DOCUMENT_FRAG_NODE:
! 14150: case XML_HTML_DOCUMENT_NODE:
! 14151: #ifdef LIBXML_DOCB_ENABLED
! 14152: case XML_DOCB_DOCUMENT_NODE:
! 14153: #endif
! 14154: cur = ctxt->node;
! 14155: break;
! 14156: case XML_ATTRIBUTE_NODE:
! 14157: case XML_TEXT_NODE:
! 14158: case XML_CDATA_SECTION_NODE:
! 14159: case XML_ENTITY_REF_NODE:
! 14160: case XML_ENTITY_NODE:
! 14161: case XML_PI_NODE:
! 14162: case XML_COMMENT_NODE:
! 14163: case XML_NOTATION_NODE:
! 14164: case XML_DTD_NODE:
! 14165: case XML_DOCUMENT_TYPE_NODE:
! 14166: case XML_ELEMENT_DECL:
! 14167: case XML_ATTRIBUTE_DECL:
! 14168: case XML_ENTITY_DECL:
! 14169: case XML_NAMESPACE_DECL:
! 14170: case XML_XINCLUDE_START:
! 14171: case XML_XINCLUDE_END:
! 14172: break;
! 14173: }
! 14174: limit = cur;
! 14175: }
! 14176: if (cur == NULL) {
! 14177: return(0);
! 14178: }
! 14179:
! 14180: patstream = xmlPatternGetStreamCtxt(comp);
! 14181: if (patstream == NULL) {
! 14182: /*
! 14183: * QUESTION TODO: Is this an error?
! 14184: */
! 14185: return(0);
! 14186: }
! 14187:
! 14188: eval_all_nodes = xmlStreamWantsAnyNode(patstream);
! 14189:
! 14190: if (from_root) {
! 14191: ret = xmlStreamPush(patstream, NULL, NULL);
! 14192: if (ret < 0) {
! 14193: } else if (ret == 1) {
! 14194: if (toBool)
! 14195: goto return_1;
! 14196: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur);
! 14197: }
! 14198: }
! 14199: depth = 0;
! 14200: goto scan_children;
! 14201: next_node:
! 14202: do {
! 14203: nb_nodes++;
! 14204:
! 14205: switch (cur->type) {
! 14206: case XML_ELEMENT_NODE:
! 14207: case XML_TEXT_NODE:
! 14208: case XML_CDATA_SECTION_NODE:
! 14209: case XML_COMMENT_NODE:
! 14210: case XML_PI_NODE:
! 14211: if (cur->type == XML_ELEMENT_NODE) {
! 14212: ret = xmlStreamPush(patstream, cur->name,
! 14213: (cur->ns ? cur->ns->href : NULL));
! 14214: } else if (eval_all_nodes)
! 14215: ret = xmlStreamPushNode(patstream, NULL, NULL, cur->type);
! 14216: else
! 14217: break;
! 14218:
! 14219: if (ret < 0) {
! 14220: /* NOP. */
! 14221: } else if (ret == 1) {
! 14222: if (toBool)
! 14223: goto return_1;
! 14224: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur);
! 14225: }
! 14226: if ((cur->children == NULL) || (depth >= max_depth)) {
! 14227: ret = xmlStreamPop(patstream);
! 14228: while (cur->next != NULL) {
! 14229: cur = cur->next;
! 14230: if ((cur->type != XML_ENTITY_DECL) &&
! 14231: (cur->type != XML_DTD_NODE))
! 14232: goto next_node;
! 14233: }
! 14234: }
! 14235: default:
! 14236: break;
! 14237: }
! 14238:
! 14239: scan_children:
! 14240: if ((cur->children != NULL) && (depth < max_depth)) {
! 14241: /*
! 14242: * Do not descend on entities declarations
! 14243: */
! 14244: if (cur->children->type != XML_ENTITY_DECL) {
! 14245: cur = cur->children;
! 14246: depth++;
! 14247: /*
! 14248: * Skip DTDs
! 14249: */
! 14250: if (cur->type != XML_DTD_NODE)
! 14251: continue;
! 14252: }
! 14253: }
! 14254:
! 14255: if (cur == limit)
! 14256: break;
! 14257:
! 14258: while (cur->next != NULL) {
! 14259: cur = cur->next;
! 14260: if ((cur->type != XML_ENTITY_DECL) &&
! 14261: (cur->type != XML_DTD_NODE))
! 14262: goto next_node;
! 14263: }
! 14264:
! 14265: do {
! 14266: cur = cur->parent;
! 14267: depth--;
! 14268: if ((cur == NULL) || (cur == limit))
! 14269: goto done;
! 14270: if (cur->type == XML_ELEMENT_NODE) {
! 14271: ret = xmlStreamPop(patstream);
! 14272: } else if ((eval_all_nodes) &&
! 14273: ((cur->type == XML_TEXT_NODE) ||
! 14274: (cur->type == XML_CDATA_SECTION_NODE) ||
! 14275: (cur->type == XML_COMMENT_NODE) ||
! 14276: (cur->type == XML_PI_NODE)))
! 14277: {
! 14278: ret = xmlStreamPop(patstream);
! 14279: }
! 14280: if (cur->next != NULL) {
! 14281: cur = cur->next;
! 14282: break;
! 14283: }
! 14284: } while (cur != NULL);
! 14285:
! 14286: } while ((cur != NULL) && (depth >= 0));
! 14287:
! 14288: done:
! 14289:
! 14290: #if 0
! 14291: printf("stream eval: checked %d nodes selected %d\n",
! 14292: nb_nodes, retObj->nodesetval->nodeNr);
! 14293: #endif
! 14294:
! 14295: if (patstream)
! 14296: xmlFreeStreamCtxt(patstream);
! 14297: return(0);
! 14298:
! 14299: return_1:
! 14300: if (patstream)
! 14301: xmlFreeStreamCtxt(patstream);
! 14302: return(1);
! 14303: }
! 14304: #endif /* XPATH_STREAMING */
! 14305:
! 14306: /**
! 14307: * xmlXPathRunEval:
! 14308: * @ctxt: the XPath parser context with the compiled expression
! 14309: * @toBool: evaluate to a boolean result
! 14310: *
! 14311: * Evaluate the Precompiled XPath expression in the given context.
! 14312: */
! 14313: static int
! 14314: xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
! 14315: {
! 14316: xmlXPathCompExprPtr comp;
! 14317:
! 14318: if ((ctxt == NULL) || (ctxt->comp == NULL))
! 14319: return(-1);
! 14320:
! 14321: if (ctxt->valueTab == NULL) {
! 14322: /* Allocate the value stack */
! 14323: ctxt->valueTab = (xmlXPathObjectPtr *)
! 14324: xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
! 14325: if (ctxt->valueTab == NULL) {
! 14326: xmlXPathPErrMemory(ctxt, "creating evaluation context\n");
! 14327: xmlFree(ctxt);
! 14328: }
! 14329: ctxt->valueNr = 0;
! 14330: ctxt->valueMax = 10;
! 14331: ctxt->value = NULL;
! 14332: }
! 14333: #ifdef XPATH_STREAMING
! 14334: if (ctxt->comp->stream) {
! 14335: int res;
! 14336:
! 14337: if (toBool) {
! 14338: /*
! 14339: * Evaluation to boolean result.
! 14340: */
! 14341: res = xmlXPathRunStreamEval(ctxt->context,
! 14342: ctxt->comp->stream, NULL, 1);
! 14343: if (res != -1)
! 14344: return(res);
! 14345: } else {
! 14346: xmlXPathObjectPtr resObj = NULL;
! 14347:
! 14348: /*
! 14349: * Evaluation to a sequence.
! 14350: */
! 14351: res = xmlXPathRunStreamEval(ctxt->context,
! 14352: ctxt->comp->stream, &resObj, 0);
! 14353:
! 14354: if ((res != -1) && (resObj != NULL)) {
! 14355: valuePush(ctxt, resObj);
! 14356: return(0);
! 14357: }
! 14358: if (resObj != NULL)
! 14359: xmlXPathReleaseObject(ctxt->context, resObj);
! 14360: }
! 14361: /*
! 14362: * QUESTION TODO: This falls back to normal XPath evaluation
! 14363: * if res == -1. Is this intended?
! 14364: */
! 14365: }
! 14366: #endif
! 14367: comp = ctxt->comp;
! 14368: if (comp->last < 0) {
! 14369: xmlGenericError(xmlGenericErrorContext,
! 14370: "xmlXPathRunEval: last is less than zero\n");
! 14371: return(-1);
! 14372: }
! 14373: if (toBool)
! 14374: return(xmlXPathCompOpEvalToBoolean(ctxt,
! 14375: &comp->steps[comp->last], 0));
! 14376: else
! 14377: xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
! 14378:
! 14379: return(0);
! 14380: }
! 14381:
! 14382: /************************************************************************
! 14383: * *
! 14384: * Public interfaces *
! 14385: * *
! 14386: ************************************************************************/
! 14387:
! 14388: /**
! 14389: * xmlXPathEvalPredicate:
! 14390: * @ctxt: the XPath context
! 14391: * @res: the Predicate Expression evaluation result
! 14392: *
! 14393: * Evaluate a predicate result for the current node.
! 14394: * A PredicateExpr is evaluated by evaluating the Expr and converting
! 14395: * the result to a boolean. If the result is a number, the result will
! 14396: * be converted to true if the number is equal to the position of the
! 14397: * context node in the context node list (as returned by the position
! 14398: * function) and will be converted to false otherwise; if the result
! 14399: * is not a number, then the result will be converted as if by a call
! 14400: * to the boolean function.
! 14401: *
! 14402: * Returns 1 if predicate is true, 0 otherwise
! 14403: */
! 14404: int
! 14405: xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
! 14406: if ((ctxt == NULL) || (res == NULL)) return(0);
! 14407: switch (res->type) {
! 14408: case XPATH_BOOLEAN:
! 14409: return(res->boolval);
! 14410: case XPATH_NUMBER:
! 14411: return(res->floatval == ctxt->proximityPosition);
! 14412: case XPATH_NODESET:
! 14413: case XPATH_XSLT_TREE:
! 14414: if (res->nodesetval == NULL)
! 14415: return(0);
! 14416: return(res->nodesetval->nodeNr != 0);
! 14417: case XPATH_STRING:
! 14418: return((res->stringval != NULL) &&
! 14419: (xmlStrlen(res->stringval) != 0));
! 14420: default:
! 14421: STRANGE
! 14422: }
! 14423: return(0);
! 14424: }
! 14425:
! 14426: /**
! 14427: * xmlXPathEvaluatePredicateResult:
! 14428: * @ctxt: the XPath Parser context
! 14429: * @res: the Predicate Expression evaluation result
! 14430: *
! 14431: * Evaluate a predicate result for the current node.
! 14432: * A PredicateExpr is evaluated by evaluating the Expr and converting
! 14433: * the result to a boolean. If the result is a number, the result will
! 14434: * be converted to true if the number is equal to the position of the
! 14435: * context node in the context node list (as returned by the position
! 14436: * function) and will be converted to false otherwise; if the result
! 14437: * is not a number, then the result will be converted as if by a call
! 14438: * to the boolean function.
! 14439: *
! 14440: * Returns 1 if predicate is true, 0 otherwise
! 14441: */
! 14442: int
! 14443: xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
! 14444: xmlXPathObjectPtr res) {
! 14445: if ((ctxt == NULL) || (res == NULL)) return(0);
! 14446: switch (res->type) {
! 14447: case XPATH_BOOLEAN:
! 14448: return(res->boolval);
! 14449: case XPATH_NUMBER:
! 14450: #if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200))
! 14451: return((res->floatval == ctxt->context->proximityPosition) &&
! 14452: (!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/
! 14453: #else
! 14454: return(res->floatval == ctxt->context->proximityPosition);
! 14455: #endif
! 14456: case XPATH_NODESET:
! 14457: case XPATH_XSLT_TREE:
! 14458: if (res->nodesetval == NULL)
! 14459: return(0);
! 14460: return(res->nodesetval->nodeNr != 0);
! 14461: case XPATH_STRING:
! 14462: return((res->stringval != NULL) && (res->stringval[0] != 0));
! 14463: #ifdef LIBXML_XPTR_ENABLED
! 14464: case XPATH_LOCATIONSET:{
! 14465: xmlLocationSetPtr ptr = res->user;
! 14466: if (ptr == NULL)
! 14467: return(0);
! 14468: return (ptr->locNr != 0);
! 14469: }
! 14470: #endif
! 14471: default:
! 14472: STRANGE
! 14473: }
! 14474: return(0);
! 14475: }
! 14476:
! 14477: #ifdef XPATH_STREAMING
! 14478: /**
! 14479: * xmlXPathTryStreamCompile:
! 14480: * @ctxt: an XPath context
! 14481: * @str: the XPath expression
! 14482: *
! 14483: * Try to compile the XPath expression as a streamable subset.
! 14484: *
! 14485: * Returns the compiled expression or NULL if failed to compile.
! 14486: */
! 14487: static xmlXPathCompExprPtr
! 14488: xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
! 14489: /*
! 14490: * Optimization: use streaming patterns when the XPath expression can
! 14491: * be compiled to a stream lookup
! 14492: */
! 14493: xmlPatternPtr stream;
! 14494: xmlXPathCompExprPtr comp;
! 14495: xmlDictPtr dict = NULL;
! 14496: const xmlChar **namespaces = NULL;
! 14497: xmlNsPtr ns;
! 14498: int i, j;
! 14499:
! 14500: if ((!xmlStrchr(str, '[')) && (!xmlStrchr(str, '(')) &&
! 14501: (!xmlStrchr(str, '@'))) {
! 14502: const xmlChar *tmp;
! 14503:
! 14504: /*
! 14505: * We don't try to handle expressions using the verbose axis
! 14506: * specifiers ("::"), just the simplied form at this point.
! 14507: * Additionally, if there is no list of namespaces available and
! 14508: * there's a ":" in the expression, indicating a prefixed QName,
! 14509: * then we won't try to compile either. xmlPatterncompile() needs
! 14510: * to have a list of namespaces at compilation time in order to
! 14511: * compile prefixed name tests.
! 14512: */
! 14513: tmp = xmlStrchr(str, ':');
! 14514: if ((tmp != NULL) &&
! 14515: ((ctxt == NULL) || (ctxt->nsNr == 0) || (tmp[1] == ':')))
! 14516: return(NULL);
! 14517:
! 14518: if (ctxt != NULL) {
! 14519: dict = ctxt->dict;
! 14520: if (ctxt->nsNr > 0) {
! 14521: namespaces = xmlMalloc(2 * (ctxt->nsNr + 1) * sizeof(xmlChar*));
! 14522: if (namespaces == NULL) {
! 14523: xmlXPathErrMemory(ctxt, "allocating namespaces array\n");
! 14524: return(NULL);
! 14525: }
! 14526: for (i = 0, j = 0; (j < ctxt->nsNr); j++) {
! 14527: ns = ctxt->namespaces[j];
! 14528: namespaces[i++] = ns->href;
! 14529: namespaces[i++] = ns->prefix;
! 14530: }
! 14531: namespaces[i++] = NULL;
! 14532: namespaces[i] = NULL;
! 14533: }
! 14534: }
! 14535:
! 14536: stream = xmlPatterncompile(str, dict, XML_PATTERN_XPATH,
! 14537: &namespaces[0]);
! 14538: if (namespaces != NULL) {
! 14539: xmlFree((xmlChar **)namespaces);
! 14540: }
! 14541: if ((stream != NULL) && (xmlPatternStreamable(stream) == 1)) {
! 14542: comp = xmlXPathNewCompExpr();
! 14543: if (comp == NULL) {
! 14544: xmlXPathErrMemory(ctxt, "allocating streamable expression\n");
! 14545: return(NULL);
! 14546: }
! 14547: comp->stream = stream;
! 14548: comp->dict = dict;
! 14549: if (comp->dict)
! 14550: xmlDictReference(comp->dict);
! 14551: return(comp);
! 14552: }
! 14553: xmlFreePattern(stream);
! 14554: }
! 14555: return(NULL);
! 14556: }
! 14557: #endif /* XPATH_STREAMING */
! 14558:
! 14559: static int
! 14560: xmlXPathCanRewriteDosExpression(xmlChar *expr)
! 14561: {
! 14562: if (expr == NULL)
! 14563: return(0);
! 14564: do {
! 14565: if ((*expr == '/') && (*(++expr) == '/'))
! 14566: return(1);
! 14567: } while (*expr++);
! 14568: return(0);
! 14569: }
! 14570: static void
! 14571: xmlXPathRewriteDOSExpression(xmlXPathCompExprPtr comp, xmlXPathStepOpPtr op)
! 14572: {
! 14573: /*
! 14574: * Try to rewrite "descendant-or-self::node()/foo" to an optimized
! 14575: * internal representation.
! 14576: */
! 14577: if (op->ch1 != -1) {
! 14578: if ((op->op == XPATH_OP_COLLECT /* 11 */) &&
! 14579: ((xmlXPathAxisVal) op->value == AXIS_CHILD /* 4 */) &&
! 14580: ((xmlXPathTestVal) op->value2 == NODE_TEST_NAME /* 5 */) &&
! 14581: ((xmlXPathTypeVal) op->value3 == NODE_TYPE_NODE /* 0 */))
! 14582: {
! 14583: /*
! 14584: * This is a "child::foo"
! 14585: */
! 14586: xmlXPathStepOpPtr prevop = &comp->steps[op->ch1];
! 14587:
! 14588: if ((prevop->op == XPATH_OP_COLLECT /* 11 */) &&
! 14589: (prevop->ch1 != -1) &&
! 14590: ((xmlXPathAxisVal) prevop->value ==
! 14591: AXIS_DESCENDANT_OR_SELF) &&
! 14592: (prevop->ch2 == -1) &&
! 14593: ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) &&
! 14594: ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE) &&
! 14595: (comp->steps[prevop->ch1].op == XPATH_OP_ROOT))
! 14596: {
! 14597: /*
! 14598: * This is a "/descendant-or-self::node()" without predicates.
! 14599: * Eliminate it.
! 14600: */
! 14601: op->ch1 = prevop->ch1;
! 14602: op->rewriteType = XP_REWRITE_DOS_CHILD_ELEM;
! 14603: }
! 14604: }
! 14605: if (op->ch1 != -1)
! 14606: xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch1]);
! 14607: }
! 14608: if (op->ch2 != -1)
! 14609: xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch2]);
! 14610: }
! 14611:
! 14612: /**
! 14613: * xmlXPathCtxtCompile:
! 14614: * @ctxt: an XPath context
! 14615: * @str: the XPath expression
! 14616: *
! 14617: * Compile an XPath expression
! 14618: *
! 14619: * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
! 14620: * the caller has to free the object.
! 14621: */
! 14622: xmlXPathCompExprPtr
! 14623: xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
! 14624: xmlXPathParserContextPtr pctxt;
! 14625: xmlXPathCompExprPtr comp;
! 14626:
! 14627: #ifdef XPATH_STREAMING
! 14628: comp = xmlXPathTryStreamCompile(ctxt, str);
! 14629: if (comp != NULL)
! 14630: return(comp);
! 14631: #endif
! 14632:
! 14633: xmlXPathInit();
! 14634:
! 14635: pctxt = xmlXPathNewParserContext(str, ctxt);
! 14636: if (pctxt == NULL)
! 14637: return NULL;
! 14638: xmlXPathCompileExpr(pctxt, 1);
! 14639:
! 14640: if( pctxt->error != XPATH_EXPRESSION_OK )
! 14641: {
! 14642: xmlXPathFreeParserContext(pctxt);
! 14643: return(NULL);
! 14644: }
! 14645:
! 14646: if (*pctxt->cur != 0) {
! 14647: /*
! 14648: * aleksey: in some cases this line prints *second* error message
! 14649: * (see bug #78858) and probably this should be fixed.
! 14650: * However, we are not sure that all error messages are printed
! 14651: * out in other places. It's not critical so we leave it as-is for now
! 14652: */
! 14653: xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
! 14654: comp = NULL;
! 14655: } else {
! 14656: comp = pctxt->comp;
! 14657: pctxt->comp = NULL;
! 14658: }
! 14659: xmlXPathFreeParserContext(pctxt);
! 14660:
! 14661: if (comp != NULL) {
! 14662: comp->expr = xmlStrdup(str);
! 14663: #ifdef DEBUG_EVAL_COUNTS
! 14664: comp->string = xmlStrdup(str);
! 14665: comp->nb = 0;
! 14666: #endif
! 14667: if ((comp->expr != NULL) &&
! 14668: (comp->nbStep > 2) &&
! 14669: (comp->last >= 0) &&
! 14670: (xmlXPathCanRewriteDosExpression(comp->expr) == 1))
! 14671: {
! 14672: xmlXPathRewriteDOSExpression(comp, &comp->steps[comp->last]);
! 14673: }
! 14674: }
! 14675: return(comp);
! 14676: }
! 14677:
! 14678: /**
! 14679: * xmlXPathCompile:
! 14680: * @str: the XPath expression
! 14681: *
! 14682: * Compile an XPath expression
! 14683: *
! 14684: * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
! 14685: * the caller has to free the object.
! 14686: */
! 14687: xmlXPathCompExprPtr
! 14688: xmlXPathCompile(const xmlChar *str) {
! 14689: return(xmlXPathCtxtCompile(NULL, str));
! 14690: }
! 14691:
! 14692: /**
! 14693: * xmlXPathCompiledEvalInternal:
! 14694: * @comp: the compiled XPath expression
! 14695: * @ctxt: the XPath context
! 14696: * @resObj: the resulting XPath object or NULL
! 14697: * @toBool: 1 if only a boolean result is requested
! 14698: *
! 14699: * Evaluate the Precompiled XPath expression in the given context.
! 14700: * The caller has to free @resObj.
! 14701: *
! 14702: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
! 14703: * the caller has to free the object.
! 14704: */
! 14705: static int
! 14706: xmlXPathCompiledEvalInternal(xmlXPathCompExprPtr comp,
! 14707: xmlXPathContextPtr ctxt,
! 14708: xmlXPathObjectPtr *resObj,
! 14709: int toBool)
! 14710: {
! 14711: xmlXPathParserContextPtr pctxt;
! 14712: #ifndef LIBXML_THREAD_ENABLED
! 14713: static int reentance = 0;
! 14714: #endif
! 14715: int res;
! 14716:
! 14717: CHECK_CTXT_NEG(ctxt)
! 14718:
! 14719: if (comp == NULL)
! 14720: return(-1);
! 14721: xmlXPathInit();
! 14722:
! 14723: #ifndef LIBXML_THREAD_ENABLED
! 14724: reentance++;
! 14725: if (reentance > 1)
! 14726: xmlXPathDisableOptimizer = 1;
! 14727: #endif
! 14728:
! 14729: #ifdef DEBUG_EVAL_COUNTS
! 14730: comp->nb++;
! 14731: if ((comp->string != NULL) && (comp->nb > 100)) {
! 14732: fprintf(stderr, "100 x %s\n", comp->string);
! 14733: comp->nb = 0;
! 14734: }
! 14735: #endif
! 14736: pctxt = xmlXPathCompParserContext(comp, ctxt);
! 14737: res = xmlXPathRunEval(pctxt, toBool);
! 14738:
! 14739: if (resObj) {
! 14740: if (pctxt->value == NULL) {
! 14741: xmlGenericError(xmlGenericErrorContext,
! 14742: "xmlXPathCompiledEval: evaluation failed\n");
! 14743: *resObj = NULL;
! 14744: } else {
! 14745: *resObj = valuePop(pctxt);
! 14746: }
! 14747: }
! 14748:
! 14749: /*
! 14750: * Pop all remaining objects from the stack.
! 14751: */
! 14752: if (pctxt->valueNr > 0) {
! 14753: xmlXPathObjectPtr tmp;
! 14754: int stack = 0;
! 14755:
! 14756: do {
! 14757: tmp = valuePop(pctxt);
! 14758: if (tmp != NULL) {
! 14759: stack++;
! 14760: xmlXPathReleaseObject(ctxt, tmp);
! 14761: }
! 14762: } while (tmp != NULL);
! 14763: if ((stack != 0) &&
! 14764: ((toBool) || ((resObj) && (*resObj))))
! 14765: {
! 14766: xmlGenericError(xmlGenericErrorContext,
! 14767: "xmlXPathCompiledEval: %d objects left on the stack.\n",
! 14768: stack);
! 14769: }
! 14770: }
! 14771:
! 14772: if ((pctxt->error != XPATH_EXPRESSION_OK) && (resObj) && (*resObj)) {
! 14773: xmlXPathFreeObject(*resObj);
! 14774: *resObj = NULL;
! 14775: }
! 14776: pctxt->comp = NULL;
! 14777: xmlXPathFreeParserContext(pctxt);
! 14778: #ifndef LIBXML_THREAD_ENABLED
! 14779: reentance--;
! 14780: #endif
! 14781:
! 14782: return(res);
! 14783: }
! 14784:
! 14785: /**
! 14786: * xmlXPathCompiledEval:
! 14787: * @comp: the compiled XPath expression
! 14788: * @ctx: the XPath context
! 14789: *
! 14790: * Evaluate the Precompiled XPath expression in the given context.
! 14791: *
! 14792: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
! 14793: * the caller has to free the object.
! 14794: */
! 14795: xmlXPathObjectPtr
! 14796: xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx)
! 14797: {
! 14798: xmlXPathObjectPtr res = NULL;
! 14799:
! 14800: xmlXPathCompiledEvalInternal(comp, ctx, &res, 0);
! 14801: return(res);
! 14802: }
! 14803:
! 14804: /**
! 14805: * xmlXPathCompiledEvalToBoolean:
! 14806: * @comp: the compiled XPath expression
! 14807: * @ctxt: the XPath context
! 14808: *
! 14809: * Applies the XPath boolean() function on the result of the given
! 14810: * compiled expression.
! 14811: *
! 14812: * Returns 1 if the expression evaluated to true, 0 if to false and
! 14813: * -1 in API and internal errors.
! 14814: */
! 14815: int
! 14816: xmlXPathCompiledEvalToBoolean(xmlXPathCompExprPtr comp,
! 14817: xmlXPathContextPtr ctxt)
! 14818: {
! 14819: return(xmlXPathCompiledEvalInternal(comp, ctxt, NULL, 1));
! 14820: }
! 14821:
! 14822: /**
! 14823: * xmlXPathEvalExpr:
! 14824: * @ctxt: the XPath Parser context
! 14825: *
! 14826: * Parse and evaluate an XPath expression in the given context,
! 14827: * then push the result on the context stack
! 14828: */
! 14829: void
! 14830: xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
! 14831: #ifdef XPATH_STREAMING
! 14832: xmlXPathCompExprPtr comp;
! 14833: #endif
! 14834:
! 14835: if (ctxt == NULL) return;
! 14836:
! 14837: #ifdef XPATH_STREAMING
! 14838: comp = xmlXPathTryStreamCompile(ctxt->context, ctxt->base);
! 14839: if (comp != NULL) {
! 14840: if (ctxt->comp != NULL)
! 14841: xmlXPathFreeCompExpr(ctxt->comp);
! 14842: ctxt->comp = comp;
! 14843: if (ctxt->cur != NULL)
! 14844: while (*ctxt->cur != 0) ctxt->cur++;
! 14845: } else
! 14846: #endif
! 14847: {
! 14848: xmlXPathCompileExpr(ctxt, 1);
! 14849: /*
! 14850: * In this scenario the expression string will sit in ctxt->base.
! 14851: */
! 14852: if ((ctxt->error == XPATH_EXPRESSION_OK) &&
! 14853: (ctxt->comp != NULL) &&
! 14854: (ctxt->base != NULL) &&
! 14855: (ctxt->comp->nbStep > 2) &&
! 14856: (ctxt->comp->last >= 0) &&
! 14857: (xmlXPathCanRewriteDosExpression((xmlChar *) ctxt->base) == 1))
! 14858: {
! 14859: xmlXPathRewriteDOSExpression(ctxt->comp,
! 14860: &ctxt->comp->steps[ctxt->comp->last]);
! 14861: }
! 14862: }
! 14863: CHECK_ERROR;
! 14864: xmlXPathRunEval(ctxt, 0);
! 14865: }
! 14866:
! 14867: /**
! 14868: * xmlXPathEval:
! 14869: * @str: the XPath expression
! 14870: * @ctx: the XPath context
! 14871: *
! 14872: * Evaluate the XPath Location Path in the given context.
! 14873: *
! 14874: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
! 14875: * the caller has to free the object.
! 14876: */
! 14877: xmlXPathObjectPtr
! 14878: xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
! 14879: xmlXPathParserContextPtr ctxt;
! 14880: xmlXPathObjectPtr res, tmp, init = NULL;
! 14881: int stack = 0;
! 14882:
! 14883: CHECK_CTXT(ctx)
! 14884:
! 14885: xmlXPathInit();
! 14886:
! 14887: ctxt = xmlXPathNewParserContext(str, ctx);
! 14888: if (ctxt == NULL)
! 14889: return NULL;
! 14890: xmlXPathEvalExpr(ctxt);
! 14891:
! 14892: if (ctxt->value == NULL) {
! 14893: xmlGenericError(xmlGenericErrorContext,
! 14894: "xmlXPathEval: evaluation failed\n");
! 14895: res = NULL;
! 14896: } else if ((*ctxt->cur != 0) && (ctxt->comp != NULL)
! 14897: #ifdef XPATH_STREAMING
! 14898: && (ctxt->comp->stream == NULL)
! 14899: #endif
! 14900: ) {
! 14901: xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
! 14902: res = NULL;
! 14903: } else {
! 14904: res = valuePop(ctxt);
! 14905: }
! 14906:
! 14907: do {
! 14908: tmp = valuePop(ctxt);
! 14909: if (tmp != NULL) {
! 14910: if (tmp != init)
! 14911: stack++;
! 14912: xmlXPathReleaseObject(ctx, tmp);
! 14913: }
! 14914: } while (tmp != NULL);
! 14915: if ((stack != 0) && (res != NULL)) {
! 14916: xmlGenericError(xmlGenericErrorContext,
! 14917: "xmlXPathEval: %d object left on the stack\n",
! 14918: stack);
! 14919: }
! 14920: if (ctxt->error != XPATH_EXPRESSION_OK) {
! 14921: xmlXPathFreeObject(res);
! 14922: res = NULL;
! 14923: }
! 14924:
! 14925: xmlXPathFreeParserContext(ctxt);
! 14926: return(res);
! 14927: }
! 14928:
! 14929: /**
! 14930: * xmlXPathEvalExpression:
! 14931: * @str: the XPath expression
! 14932: * @ctxt: the XPath context
! 14933: *
! 14934: * Evaluate the XPath expression in the given context.
! 14935: *
! 14936: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
! 14937: * the caller has to free the object.
! 14938: */
! 14939: xmlXPathObjectPtr
! 14940: xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
! 14941: xmlXPathParserContextPtr pctxt;
! 14942: xmlXPathObjectPtr res, tmp;
! 14943: int stack = 0;
! 14944:
! 14945: CHECK_CTXT(ctxt)
! 14946:
! 14947: xmlXPathInit();
! 14948:
! 14949: pctxt = xmlXPathNewParserContext(str, ctxt);
! 14950: if (pctxt == NULL)
! 14951: return NULL;
! 14952: xmlXPathEvalExpr(pctxt);
! 14953:
! 14954: if ((*pctxt->cur != 0) || (pctxt->error != XPATH_EXPRESSION_OK)) {
! 14955: xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
! 14956: res = NULL;
! 14957: } else {
! 14958: res = valuePop(pctxt);
! 14959: }
! 14960: do {
! 14961: tmp = valuePop(pctxt);
! 14962: if (tmp != NULL) {
! 14963: xmlXPathReleaseObject(ctxt, tmp);
! 14964: stack++;
! 14965: }
! 14966: } while (tmp != NULL);
! 14967: if ((stack != 0) && (res != NULL)) {
! 14968: xmlGenericError(xmlGenericErrorContext,
! 14969: "xmlXPathEvalExpression: %d object left on the stack\n",
! 14970: stack);
! 14971: }
! 14972: xmlXPathFreeParserContext(pctxt);
! 14973: return(res);
! 14974: }
! 14975:
! 14976: /************************************************************************
! 14977: * *
! 14978: * Extra functions not pertaining to the XPath spec *
! 14979: * *
! 14980: ************************************************************************/
! 14981: /**
! 14982: * xmlXPathEscapeUriFunction:
! 14983: * @ctxt: the XPath Parser context
! 14984: * @nargs: the number of arguments
! 14985: *
! 14986: * Implement the escape-uri() XPath function
! 14987: * string escape-uri(string $str, bool $escape-reserved)
! 14988: *
! 14989: * This function applies the URI escaping rules defined in section 2 of [RFC
! 14990: * 2396] to the string supplied as $uri-part, which typically represents all
! 14991: * or part of a URI. The effect of the function is to replace any special
! 14992: * character in the string by an escape sequence of the form %xx%yy...,
! 14993: * where xxyy... is the hexadecimal representation of the octets used to
! 14994: * represent the character in UTF-8.
! 14995: *
! 14996: * The set of characters that are escaped depends on the setting of the
! 14997: * boolean argument $escape-reserved.
! 14998: *
! 14999: * If $escape-reserved is true, all characters are escaped other than lower
! 15000: * case letters a-z, upper case letters A-Z, digits 0-9, and the characters
! 15001: * referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!"
! 15002: * | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only
! 15003: * if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and
! 15004: * A-F).
! 15005: *
! 15006: * If $escape-reserved is false, the behavior differs in that characters
! 15007: * referred to in [RFC 2396] as reserved characters are not escaped. These
! 15008: * characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",".
! 15009: *
! 15010: * [RFC 2396] does not define whether escaped URIs should use lower case or
! 15011: * upper case for hexadecimal digits. To ensure that escaped URIs can be
! 15012: * compared using string comparison functions, this function must always use
! 15013: * the upper-case letters A-F.
! 15014: *
! 15015: * Generally, $escape-reserved should be set to true when escaping a string
! 15016: * that is to form a single part of a URI, and to false when escaping an
! 15017: * entire URI or URI reference.
! 15018: *
! 15019: * In the case of non-ascii characters, the string is encoded according to
! 15020: * utf-8 and then converted according to RFC 2396.
! 15021: *
! 15022: * Examples
! 15023: * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true())
! 15024: * returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean"
! 15025: * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false())
! 15026: * returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean"
! 15027: *
! 15028: */
! 15029: static void
! 15030: xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) {
! 15031: xmlXPathObjectPtr str;
! 15032: int escape_reserved;
! 15033: xmlBufferPtr target;
! 15034: xmlChar *cptr;
! 15035: xmlChar escape[4];
! 15036:
! 15037: CHECK_ARITY(2);
! 15038:
! 15039: escape_reserved = xmlXPathPopBoolean(ctxt);
! 15040:
! 15041: CAST_TO_STRING;
! 15042: str = valuePop(ctxt);
! 15043:
! 15044: target = xmlBufferCreate();
! 15045:
! 15046: escape[0] = '%';
! 15047: escape[3] = 0;
! 15048:
! 15049: if (target) {
! 15050: for (cptr = str->stringval; *cptr; cptr++) {
! 15051: if ((*cptr >= 'A' && *cptr <= 'Z') ||
! 15052: (*cptr >= 'a' && *cptr <= 'z') ||
! 15053: (*cptr >= '0' && *cptr <= '9') ||
! 15054: *cptr == '-' || *cptr == '_' || *cptr == '.' ||
! 15055: *cptr == '!' || *cptr == '~' || *cptr == '*' ||
! 15056: *cptr == '\''|| *cptr == '(' || *cptr == ')' ||
! 15057: (*cptr == '%' &&
! 15058: ((cptr[1] >= 'A' && cptr[1] <= 'F') ||
! 15059: (cptr[1] >= 'a' && cptr[1] <= 'f') ||
! 15060: (cptr[1] >= '0' && cptr[1] <= '9')) &&
! 15061: ((cptr[2] >= 'A' && cptr[2] <= 'F') ||
! 15062: (cptr[2] >= 'a' && cptr[2] <= 'f') ||
! 15063: (cptr[2] >= '0' && cptr[2] <= '9'))) ||
! 15064: (!escape_reserved &&
! 15065: (*cptr == ';' || *cptr == '/' || *cptr == '?' ||
! 15066: *cptr == ':' || *cptr == '@' || *cptr == '&' ||
! 15067: *cptr == '=' || *cptr == '+' || *cptr == '$' ||
! 15068: *cptr == ','))) {
! 15069: xmlBufferAdd(target, cptr, 1);
! 15070: } else {
! 15071: if ((*cptr >> 4) < 10)
! 15072: escape[1] = '0' + (*cptr >> 4);
! 15073: else
! 15074: escape[1] = 'A' - 10 + (*cptr >> 4);
! 15075: if ((*cptr & 0xF) < 10)
! 15076: escape[2] = '0' + (*cptr & 0xF);
! 15077: else
! 15078: escape[2] = 'A' - 10 + (*cptr & 0xF);
! 15079:
! 15080: xmlBufferAdd(target, &escape[0], 3);
! 15081: }
! 15082: }
! 15083: }
! 15084: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
! 15085: xmlBufferContent(target)));
! 15086: xmlBufferFree(target);
! 15087: xmlXPathReleaseObject(ctxt->context, str);
! 15088: }
! 15089:
! 15090: /**
! 15091: * xmlXPathRegisterAllFunctions:
! 15092: * @ctxt: the XPath context
! 15093: *
! 15094: * Registers all default XPath functions in this context
! 15095: */
! 15096: void
! 15097: xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
! 15098: {
! 15099: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
! 15100: xmlXPathBooleanFunction);
! 15101: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
! 15102: xmlXPathCeilingFunction);
! 15103: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
! 15104: xmlXPathCountFunction);
! 15105: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
! 15106: xmlXPathConcatFunction);
! 15107: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
! 15108: xmlXPathContainsFunction);
! 15109: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
! 15110: xmlXPathIdFunction);
! 15111: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
! 15112: xmlXPathFalseFunction);
! 15113: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
! 15114: xmlXPathFloorFunction);
! 15115: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
! 15116: xmlXPathLastFunction);
! 15117: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
! 15118: xmlXPathLangFunction);
! 15119: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
! 15120: xmlXPathLocalNameFunction);
! 15121: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
! 15122: xmlXPathNotFunction);
! 15123: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
! 15124: xmlXPathNameFunction);
! 15125: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
! 15126: xmlXPathNamespaceURIFunction);
! 15127: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
! 15128: xmlXPathNormalizeFunction);
! 15129: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
! 15130: xmlXPathNumberFunction);
! 15131: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
! 15132: xmlXPathPositionFunction);
! 15133: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
! 15134: xmlXPathRoundFunction);
! 15135: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
! 15136: xmlXPathStringFunction);
! 15137: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
! 15138: xmlXPathStringLengthFunction);
! 15139: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
! 15140: xmlXPathStartsWithFunction);
! 15141: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
! 15142: xmlXPathSubstringFunction);
! 15143: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
! 15144: xmlXPathSubstringBeforeFunction);
! 15145: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
! 15146: xmlXPathSubstringAfterFunction);
! 15147: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
! 15148: xmlXPathSumFunction);
! 15149: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
! 15150: xmlXPathTrueFunction);
! 15151: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
! 15152: xmlXPathTranslateFunction);
! 15153:
! 15154: xmlXPathRegisterFuncNS(ctxt, (const xmlChar *)"escape-uri",
! 15155: (const xmlChar *)"http://www.w3.org/2002/08/xquery-functions",
! 15156: xmlXPathEscapeUriFunction);
! 15157: }
! 15158:
! 15159: #endif /* LIBXML_XPATH_ENABLED */
! 15160: #define bottom_xpath
! 15161: #include "elfgcchack.h"
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>