blob: 2228ff5706a66f09e88352f75740c3aeeeca9250 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * xpath.c: XML Path Language implementation
3 * XPath is a language for addressing parts of an XML document,
4 * designed to be used by both XSLT and XPointer
5 *
6 * Reference: W3C 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 *
Daniel Veillardc5d64342001-06-24 12:13:24 +000013 * Author: daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +000014 *
15 * 14 Nov 2000 ht - truncated declaration of xmlXPathEvalRelativeLocationPath
16 * for VMS
17 */
18
Bjorn Reese70a9da52001-04-21 16:57:29 +000019#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000020#ifdef LIBXML_XPATH_ENABLED
21
Owen Taylor3473f882001-02-23 17:55:21 +000022#include <string.h>
23
24#ifdef HAVE_SYS_TYPES_H
25#include <sys/types.h>
26#endif
27#ifdef HAVE_MATH_H
28#include <math.h>
29#endif
30#ifdef HAVE_FLOAT_H
31#include <float.h>
32#endif
33#ifdef HAVE_IEEEFP_H
34#include <ieeefp.h>
35#endif
36#ifdef HAVE_NAN_H
37#include <nan.h>
38#endif
39#ifdef HAVE_CTYPE_H
40#include <ctype.h>
41#endif
Daniel Veillard5792e162001-04-30 17:44:45 +000042#ifdef HAVE_SIGNAL_H
Daniel Veillardb45c43b2001-04-28 17:02:11 +000043#include <signal.h>
Daniel Veillardb45c43b2001-04-28 17:02:11 +000044#endif
Owen Taylor3473f882001-02-23 17:55:21 +000045
46#include <libxml/xmlmemory.h>
47#include <libxml/tree.h>
48#include <libxml/valid.h>
49#include <libxml/xpath.h>
50#include <libxml/xpathInternals.h>
51#include <libxml/parserInternals.h>
52#include <libxml/hash.h>
53#ifdef LIBXML_XPTR_ENABLED
54#include <libxml/xpointer.h>
55#endif
56#ifdef LIBXML_DEBUG_ENABLED
57#include <libxml/debugXML.h>
58#endif
59#include <libxml/xmlerror.h>
60
61/* #define DEBUG */
62/* #define DEBUG_STEP */
Daniel Veillardf06307e2001-07-03 10:35:50 +000063/* #define DEBUG_STEP_NTH */
Owen Taylor3473f882001-02-23 17:55:21 +000064/* #define DEBUG_EXPR */
Daniel Veillardf06307e2001-07-03 10:35:50 +000065/* #define DEBUG_EVAL_COUNTS */
Owen Taylor3473f882001-02-23 17:55:21 +000066
67void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
68double xmlXPathStringEvalNumber(const xmlChar *str);
Daniel Veillard5792e162001-04-30 17:44:45 +000069double xmlXPathDivideBy(double f, double fzero);
Owen Taylor3473f882001-02-23 17:55:21 +000070
Daniel Veillard9e7160d2001-03-18 23:17:47 +000071/************************************************************************
72 * *
73 * Floating point stuff *
74 * *
75 ************************************************************************/
76
Owen Taylor3473f882001-02-23 17:55:21 +000077/*
Owen Taylor3473f882001-02-23 17:55:21 +000078 * The lack of portability of this section of the libc is annoying !
79 */
80double xmlXPathNAN = 0;
81double xmlXPathPINF = 1;
82double xmlXPathNINF = -1;
83
84#ifndef isinf
85#ifndef HAVE_ISINF
86
87#if HAVE_FPCLASS
88
89int isinf(double d) {
90 fpclass_t type = fpclass(d);
91 switch (type) {
92 case FP_NINF:
93 return(-1);
94 case FP_PINF:
95 return(1);
96 }
97 return(0);
98}
99
100#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
101
102#if HAVE_FP_CLASS_H
103#include <fp_class.h>
104#endif
105
106int isinf(double d) {
107#if HAVE_FP_CLASS
108 int fpclass = fp_class(d);
109#else
110 int fpclass = fp_class_d(d);
111#endif
112 if (fpclass == FP_POS_INF)
113 return(1);
114 if (fpclass == FP_NEG_INF)
115 return(-1);
116 return(0);
117}
118
119#elif defined(HAVE_CLASS)
120
121int isinf(double d) {
122 int fpclass = class(d);
123 if (fpclass == FP_PLUS_INF)
124 return(1);
125 if (fpclass == FP_MINUS_INF)
126 return(-1);
127 return(0);
128}
129#elif defined(finite) || defined(HAVE_FINITE)
130int isinf(double x) { return !finite(x) && x==x; }
131#elif defined(HUGE_VAL)
132int isinf(double x)
133{
134 if (x == HUGE_VAL)
135 return(1);
136 if (x == -HUGE_VAL)
137 return(-1);
138 return(0);
139}
140#endif
141
142#endif /* ! HAVE_ISINF */
143#endif /* ! defined(isinf) */
144
145#ifndef isnan
146#ifndef HAVE_ISNAN
147
148#ifdef HAVE_ISNAND
149#define isnan(f) isnand(f)
150#endif /* HAVE_iSNAND */
151
152#endif /* ! HAVE_iSNAN */
153#endif /* ! defined(isnan) */
154
Daniel Veillard5792e162001-04-30 17:44:45 +0000155
156/**
157 * xmlXPathDivideBy:
158 *
159 * The best way found so far to generate the NAN, +-INF
160 * without hitting a compiler bug or optimization :-\
161 *
162 * Returns the double resulting from the division
163 */
164double
165xmlXPathDivideBy(double f, double fzero) {
Daniel Veillard81418e32001-05-22 15:08:55 +0000166 double ret;
Daniel Veillard5792e162001-04-30 17:44:45 +0000167#ifdef HAVE_SIGNAL
168#ifdef SIGFPE
169#ifdef SIG_IGN
170 void (*sighandler)(int);
171 sighandler = signal(SIGFPE, SIG_IGN);
172#endif
173#endif
174#endif
175 ret = f / fzero;
176#ifdef HAVE_SIGNAL
177#ifdef SIGFPE
178#ifdef SIG_IGN
179 signal(SIGFPE, sighandler);
180#endif
181#endif
182#endif
183 return(ret);
184}
185
Owen Taylor3473f882001-02-23 17:55:21 +0000186/**
187 * xmlXPathInit:
188 *
189 * Initialize the XPath environment
190 */
191void
192xmlXPathInit(void) {
193 static int initialized = 0;
194
195 if (initialized) return;
196
Daniel Veillarda53c6882001-07-25 17:18:57 +0000197#if defined(HUGE_VAL) && defined(DBL_MAX)
198 xmlXPathPINF = (HUGE_VAL == DBL_MAX) ?
199 xmlXPathDivideBy(1.0, 0.0) : HUGE_VAL;
200 xmlXPathNINF = -xmlXPathPINF;
201 xmlXPathNAN = xmlXPathDivideBy(xmlXPathPINF, xmlXPathPINF);
202#else
Daniel Veillard5792e162001-04-30 17:44:45 +0000203 xmlXPathNAN = xmlXPathDivideBy(0.0, 0.0);
204 xmlXPathPINF = xmlXPathDivideBy(1.0, 0.0);
Daniel Veillard541d6552001-06-07 14:20:01 +0000205 xmlXPathNINF = xmlXPathDivideBy(-1.0, 0.0);
Daniel Veillarda53c6882001-07-25 17:18:57 +0000206#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000207
208 initialized = 1;
209}
210
211/************************************************************************
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000212 * *
213 * Parser Types *
214 * *
215 ************************************************************************/
216
217/*
218 * Types are private:
219 */
220
221typedef enum {
222 XPATH_OP_END=0,
223 XPATH_OP_AND,
224 XPATH_OP_OR,
225 XPATH_OP_EQUAL,
226 XPATH_OP_CMP,
227 XPATH_OP_PLUS,
228 XPATH_OP_MULT,
229 XPATH_OP_UNION,
230 XPATH_OP_ROOT,
231 XPATH_OP_NODE,
232 XPATH_OP_RESET,
233 XPATH_OP_COLLECT,
234 XPATH_OP_VALUE,
235 XPATH_OP_VARIABLE,
236 XPATH_OP_FUNCTION,
237 XPATH_OP_ARG,
238 XPATH_OP_PREDICATE,
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000239 XPATH_OP_FILTER,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000240 XPATH_OP_SORT
241#ifdef LIBXML_XPTR_ENABLED
242 ,XPATH_OP_RANGETO
243#endif
244} xmlXPathOp;
245
246typedef enum {
247 AXIS_ANCESTOR = 1,
248 AXIS_ANCESTOR_OR_SELF,
249 AXIS_ATTRIBUTE,
250 AXIS_CHILD,
251 AXIS_DESCENDANT,
252 AXIS_DESCENDANT_OR_SELF,
253 AXIS_FOLLOWING,
254 AXIS_FOLLOWING_SIBLING,
255 AXIS_NAMESPACE,
256 AXIS_PARENT,
257 AXIS_PRECEDING,
258 AXIS_PRECEDING_SIBLING,
259 AXIS_SELF
260} xmlXPathAxisVal;
261
262typedef enum {
263 NODE_TEST_NONE = 0,
264 NODE_TEST_TYPE = 1,
265 NODE_TEST_PI = 2,
266 NODE_TEST_ALL = 3,
267 NODE_TEST_NS = 4,
268 NODE_TEST_NAME = 5
269} xmlXPathTestVal;
270
271typedef enum {
272 NODE_TYPE_NODE = 0,
273 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
274 NODE_TYPE_TEXT = XML_TEXT_NODE,
275 NODE_TYPE_PI = XML_PI_NODE
276} xmlXPathTypeVal;
277
278
279typedef struct _xmlXPathStepOp xmlXPathStepOp;
280typedef xmlXPathStepOp *xmlXPathStepOpPtr;
281struct _xmlXPathStepOp {
282 xmlXPathOp op;
283 int ch1;
284 int ch2;
285 int value;
286 int value2;
287 int value3;
288 void *value4;
289 void *value5;
Daniel Veillarde39a93d2001-04-28 14:35:02 +0000290 void *cache;
Daniel Veillard42596ad2001-05-22 16:57:14 +0000291 void *cacheURI;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000292};
293
294struct _xmlXPathCompExpr {
295 int nbStep;
296 int maxStep;
297 xmlXPathStepOp *steps; /* ops for computation */
298 int last;
Daniel Veillardf06307e2001-07-03 10:35:50 +0000299#ifdef DEBUG_EVAL_COUNTS
300 int nb;
301 xmlChar *string;
302#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000303};
304
305/************************************************************************
306 * *
307 * Parser Type functions *
308 * *
309 ************************************************************************/
310
311/**
312 * xmlXPathNewCompExpr:
313 *
314 * Create a new Xpath component
315 *
316 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
317 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000318static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000319xmlXPathNewCompExpr(void) {
320 xmlXPathCompExprPtr cur;
321
322 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
323 if (cur == NULL) {
324 xmlGenericError(xmlGenericErrorContext,
325 "xmlXPathNewCompExpr : malloc failed\n");
326 return(NULL);
327 }
328 memset(cur, 0, sizeof(xmlXPathCompExpr));
329 cur->maxStep = 10;
330 cur->nbStep = 0;
331 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
332 sizeof(xmlXPathStepOp));
333 if (cur->steps == NULL) {
334 xmlGenericError(xmlGenericErrorContext,
335 "xmlXPathNewCompExpr : malloc failed\n");
336 xmlFree(cur);
337 return(NULL);
338 }
339 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
340 cur->last = -1;
Daniel Veillardf06307e2001-07-03 10:35:50 +0000341#ifdef DEBUG_EVAL_COUNTS
342 cur->nb = 0;
343#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000344 return(cur);
345}
346
347/**
348 * xmlXPathFreeCompExpr:
349 * @comp: an XPATH comp
350 *
351 * Free up the memory allocated by @comp
352 */
353void
Daniel Veillardf06307e2001-07-03 10:35:50 +0000354xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
355{
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000356 xmlXPathStepOpPtr op;
357 int i;
358
359 if (comp == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +0000360 return;
361 for (i = 0; i < comp->nbStep; i++) {
362 op = &comp->steps[i];
363 if (op->value4 != NULL) {
364 if (op->op == XPATH_OP_VALUE)
365 xmlXPathFreeObject(op->value4);
366 else
367 xmlFree(op->value4);
368 }
369 if (op->value5 != NULL)
370 xmlFree(op->value5);
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000371 }
372 if (comp->steps != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +0000373 xmlFree(comp->steps);
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000374 }
Daniel Veillardf06307e2001-07-03 10:35:50 +0000375#ifdef DEBUG_EVAL_COUNTS
376 if (comp->string != NULL) {
377 xmlFree(comp->string);
378 }
379#endif
380
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000381 xmlFree(comp);
382}
383
384/**
385 * xmlXPathCompExprAdd:
386 * @comp: the compiled expression
387 * @ch1: first child index
388 * @ch2: second child index
389 * @op: an op
390 * @value: the first int value
391 * @value2: the second int value
392 * @value3: the third int value
393 * @value4: the first string value
394 * @value5: the second string value
395 *
396 * Add an step to an XPath Compiled Expression
397 *
398 * Returns -1 in case of failure, the index otherwise
399 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000400static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000401xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
402 xmlXPathOp op, int value,
403 int value2, int value3, void *value4, void *value5) {
404 if (comp->nbStep >= comp->maxStep) {
405 xmlXPathStepOp *real;
406
407 comp->maxStep *= 2;
408 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
409 comp->maxStep * sizeof(xmlXPathStepOp));
410 if (real == NULL) {
411 comp->maxStep /= 2;
412 xmlGenericError(xmlGenericErrorContext,
413 "xmlXPathCompExprAdd : realloc failed\n");
414 return(-1);
415 }
416 comp->steps = real;
417 }
418 comp->last = comp->nbStep;
419 comp->steps[comp->nbStep].ch1 = ch1;
420 comp->steps[comp->nbStep].ch2 = ch2;
421 comp->steps[comp->nbStep].op = op;
422 comp->steps[comp->nbStep].value = value;
423 comp->steps[comp->nbStep].value2 = value2;
424 comp->steps[comp->nbStep].value3 = value3;
425 comp->steps[comp->nbStep].value4 = value4;
426 comp->steps[comp->nbStep].value5 = value5;
Daniel Veillarde39a93d2001-04-28 14:35:02 +0000427 comp->steps[comp->nbStep].cache = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000428 return(comp->nbStep++);
429}
430
Daniel Veillardf06307e2001-07-03 10:35:50 +0000431/**
432 * xmlXPathCompSwap:
433 * @comp: the compiled expression
434 * @op: operation index
435 *
436 * Swaps 2 operations in the compiled expression
437 * TODO: not thread safe, disable for multi-thread operations
438 *
439 * Returns -1 in case of failure, the index otherwise
440 */
441static void
442xmlXPathCompSwap(xmlXPathStepOpPtr op) {
443 int tmp;
444
445 tmp = op->ch1;
446 op->ch1 = op->ch2;
447 op->ch2 = tmp;
448}
449
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000450#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
451 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
452 (op), (val), (val2), (val3), (val4), (val5))
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000453#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
454 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
455 (op), (val), (val2), (val3), (val4), (val5))
456
457#define PUSH_LEAVE_EXPR(op, val, val2) \
458xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
459
460#define PUSH_UNARY_EXPR(op, ch, val, val2) \
461xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
462
463#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
464xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
465
466/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000467 * *
468 * Debugging related functions *
469 * *
470 ************************************************************************/
471
472#define TODO \
473 xmlGenericError(xmlGenericErrorContext, \
474 "Unimplemented block at %s:%d\n", \
475 __FILE__, __LINE__);
476
477#define STRANGE \
478 xmlGenericError(xmlGenericErrorContext, \
479 "Internal error at %s:%d\n", \
480 __FILE__, __LINE__);
481
482#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000483static void
484xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000485 int i;
486 char shift[100];
487
488 for (i = 0;((i < depth) && (i < 25));i++)
489 shift[2 * i] = shift[2 * i + 1] = ' ';
490 shift[2 * i] = shift[2 * i + 1] = 0;
491 if (cur == NULL) {
492 fprintf(output, shift);
493 fprintf(output, "Node is NULL !\n");
494 return;
495
496 }
497
498 if ((cur->type == XML_DOCUMENT_NODE) ||
499 (cur->type == XML_HTML_DOCUMENT_NODE)) {
500 fprintf(output, shift);
501 fprintf(output, " /\n");
502 } else if (cur->type == XML_ATTRIBUTE_NODE)
503 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
504 else
505 xmlDebugDumpOneNode(output, cur, depth);
506}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000507static void
508xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000509 xmlNodePtr tmp;
510 int i;
511 char shift[100];
512
513 for (i = 0;((i < depth) && (i < 25));i++)
514 shift[2 * i] = shift[2 * i + 1] = ' ';
515 shift[2 * i] = shift[2 * i + 1] = 0;
516 if (cur == NULL) {
517 fprintf(output, shift);
518 fprintf(output, "Node is NULL !\n");
519 return;
520
521 }
522
523 while (cur != NULL) {
524 tmp = cur;
525 cur = cur->next;
526 xmlDebugDumpOneNode(output, tmp, depth);
527 }
528}
Owen Taylor3473f882001-02-23 17:55:21 +0000529
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000530static void
531xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000532 int i;
533 char shift[100];
534
535 for (i = 0;((i < depth) && (i < 25));i++)
536 shift[2 * i] = shift[2 * i + 1] = ' ';
537 shift[2 * i] = shift[2 * i + 1] = 0;
538
539 if (cur == NULL) {
540 fprintf(output, shift);
541 fprintf(output, "NodeSet is NULL !\n");
542 return;
543
544 }
545
Daniel Veillard911f49a2001-04-07 15:39:35 +0000546 if (cur != NULL) {
547 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
548 for (i = 0;i < cur->nodeNr;i++) {
549 fprintf(output, shift);
550 fprintf(output, "%d", i + 1);
551 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
552 }
Owen Taylor3473f882001-02-23 17:55:21 +0000553 }
554}
555
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000556static void
557xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000558 int i;
559 char shift[100];
560
561 for (i = 0;((i < depth) && (i < 25));i++)
562 shift[2 * i] = shift[2 * i + 1] = ' ';
563 shift[2 * i] = shift[2 * i + 1] = 0;
564
565 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
566 fprintf(output, shift);
567 fprintf(output, "Value Tree is NULL !\n");
568 return;
569
570 }
571
572 fprintf(output, shift);
573 fprintf(output, "%d", i + 1);
574 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
575}
Owen Taylor3473f882001-02-23 17:55:21 +0000576#if defined(LIBXML_XPTR_ENABLED)
577void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000578static void
579xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000580 int i;
581 char shift[100];
582
583 for (i = 0;((i < depth) && (i < 25));i++)
584 shift[2 * i] = shift[2 * i + 1] = ' ';
585 shift[2 * i] = shift[2 * i + 1] = 0;
586
587 if (cur == NULL) {
588 fprintf(output, shift);
589 fprintf(output, "LocationSet is NULL !\n");
590 return;
591
592 }
593
594 for (i = 0;i < cur->locNr;i++) {
595 fprintf(output, shift);
596 fprintf(output, "%d : ", i + 1);
597 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
598 }
599}
Daniel Veillard017b1082001-06-21 11:20:21 +0000600#endif /* LIBXML_XPTR_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +0000601
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000602/**
603 * xmlXPathDebugDumpObject:
604 * @output: the FILE * to dump the output
605 * @cur: the object to inspect
606 * @depth: indentation level
607 *
608 * Dump the content of the object for debugging purposes
609 */
610void
611xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000612 int i;
613 char shift[100];
614
615 for (i = 0;((i < depth) && (i < 25));i++)
616 shift[2 * i] = shift[2 * i + 1] = ' ';
617 shift[2 * i] = shift[2 * i + 1] = 0;
618
619 fprintf(output, shift);
620
621 if (cur == NULL) {
622 fprintf(output, "Object is empty (NULL)\n");
623 return;
624 }
625 switch(cur->type) {
626 case XPATH_UNDEFINED:
627 fprintf(output, "Object is uninitialized\n");
628 break;
629 case XPATH_NODESET:
630 fprintf(output, "Object is a Node Set :\n");
631 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
632 break;
633 case XPATH_XSLT_TREE:
634 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000635 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000636 break;
637 case XPATH_BOOLEAN:
638 fprintf(output, "Object is a Boolean : ");
639 if (cur->boolval) fprintf(output, "true\n");
640 else fprintf(output, "false\n");
641 break;
642 case XPATH_NUMBER:
Daniel Veillard357c9602001-05-03 10:49:20 +0000643 switch (isinf(cur->floatval)) {
644 case 1:
645 fprintf(output, "Object is a number : +Infinity\n");
646 break;
647 case -1:
648 fprintf(output, "Object is a number : -Infinity\n");
649 break;
650 default:
651 if (isnan(cur->floatval)) {
652 fprintf(output, "Object is a number : NaN\n");
653 } else {
654 fprintf(output, "Object is a number : %0g\n", cur->floatval);
655 }
656 }
Owen Taylor3473f882001-02-23 17:55:21 +0000657 break;
658 case XPATH_STRING:
659 fprintf(output, "Object is a string : ");
660 xmlDebugDumpString(output, cur->stringval);
661 fprintf(output, "\n");
662 break;
663 case XPATH_POINT:
664 fprintf(output, "Object is a point : index %d in node", cur->index);
665 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
666 fprintf(output, "\n");
667 break;
668 case XPATH_RANGE:
669 if ((cur->user2 == NULL) ||
670 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
671 fprintf(output, "Object is a collapsed range :\n");
672 fprintf(output, shift);
673 if (cur->index >= 0)
674 fprintf(output, "index %d in ", cur->index);
675 fprintf(output, "node\n");
676 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
677 depth + 1);
678 } else {
679 fprintf(output, "Object is a range :\n");
680 fprintf(output, shift);
681 fprintf(output, "From ");
682 if (cur->index >= 0)
683 fprintf(output, "index %d in ", cur->index);
684 fprintf(output, "node\n");
685 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
686 depth + 1);
687 fprintf(output, shift);
688 fprintf(output, "To ");
689 if (cur->index2 >= 0)
690 fprintf(output, "index %d in ", cur->index2);
691 fprintf(output, "node\n");
692 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
693 depth + 1);
694 fprintf(output, "\n");
695 }
696 break;
697 case XPATH_LOCATIONSET:
698#if defined(LIBXML_XPTR_ENABLED)
699 fprintf(output, "Object is a Location Set:\n");
700 xmlXPathDebugDumpLocationSet(output,
701 (xmlLocationSetPtr) cur->user, depth);
702#endif
703 break;
704 case XPATH_USERS:
705 fprintf(output, "Object is user defined\n");
706 break;
707 }
708}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000709
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000710static void
711xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000712 xmlXPathStepOpPtr op, int depth) {
713 int i;
714 char shift[100];
715
716 for (i = 0;((i < depth) && (i < 25));i++)
717 shift[2 * i] = shift[2 * i + 1] = ' ';
718 shift[2 * i] = shift[2 * i + 1] = 0;
719
720 fprintf(output, shift);
721 if (op == NULL) {
722 fprintf(output, "Step is NULL\n");
723 return;
724 }
725 switch (op->op) {
726 case XPATH_OP_END:
727 fprintf(output, "END"); break;
728 case XPATH_OP_AND:
729 fprintf(output, "AND"); break;
730 case XPATH_OP_OR:
731 fprintf(output, "OR"); break;
732 case XPATH_OP_EQUAL:
733 if (op->value)
734 fprintf(output, "EQUAL =");
735 else
736 fprintf(output, "EQUAL !=");
737 break;
738 case XPATH_OP_CMP:
739 if (op->value)
740 fprintf(output, "CMP <");
741 else
742 fprintf(output, "CMP >");
743 if (!op->value2)
744 fprintf(output, "=");
745 break;
746 case XPATH_OP_PLUS:
747 if (op->value == 0)
748 fprintf(output, "PLUS -");
749 else if (op->value == 1)
750 fprintf(output, "PLUS +");
751 else if (op->value == 2)
752 fprintf(output, "PLUS unary -");
753 else if (op->value == 3)
754 fprintf(output, "PLUS unary - -");
755 break;
756 case XPATH_OP_MULT:
757 if (op->value == 0)
758 fprintf(output, "MULT *");
759 else if (op->value == 1)
760 fprintf(output, "MULT div");
761 else
762 fprintf(output, "MULT mod");
763 break;
764 case XPATH_OP_UNION:
765 fprintf(output, "UNION"); break;
766 case XPATH_OP_ROOT:
767 fprintf(output, "ROOT"); break;
768 case XPATH_OP_NODE:
769 fprintf(output, "NODE"); break;
770 case XPATH_OP_RESET:
771 fprintf(output, "RESET"); break;
772 case XPATH_OP_SORT:
773 fprintf(output, "SORT"); break;
774 case XPATH_OP_COLLECT: {
775 xmlXPathAxisVal axis = op->value;
776 xmlXPathTestVal test = op->value2;
777 xmlXPathTypeVal type = op->value3;
778 const xmlChar *prefix = op->value4;
779 const xmlChar *name = op->value5;
780
781 fprintf(output, "COLLECT ");
782 switch (axis) {
783 case AXIS_ANCESTOR:
784 fprintf(output, " 'ancestors' "); break;
785 case AXIS_ANCESTOR_OR_SELF:
786 fprintf(output, " 'ancestors-or-self' "); break;
787 case AXIS_ATTRIBUTE:
788 fprintf(output, " 'attributes' "); break;
789 case AXIS_CHILD:
790 fprintf(output, " 'child' "); break;
791 case AXIS_DESCENDANT:
792 fprintf(output, " 'descendant' "); break;
793 case AXIS_DESCENDANT_OR_SELF:
794 fprintf(output, " 'descendant-or-self' "); break;
795 case AXIS_FOLLOWING:
796 fprintf(output, " 'following' "); break;
797 case AXIS_FOLLOWING_SIBLING:
798 fprintf(output, " 'following-siblings' "); break;
799 case AXIS_NAMESPACE:
800 fprintf(output, " 'namespace' "); break;
801 case AXIS_PARENT:
802 fprintf(output, " 'parent' "); break;
803 case AXIS_PRECEDING:
804 fprintf(output, " 'preceding' "); break;
805 case AXIS_PRECEDING_SIBLING:
806 fprintf(output, " 'preceding-sibling' "); break;
807 case AXIS_SELF:
808 fprintf(output, " 'self' "); break;
809 }
810 switch (test) {
811 case NODE_TEST_NONE:
812 fprintf(output, "'none' "); break;
813 case NODE_TEST_TYPE:
814 fprintf(output, "'type' "); break;
815 case NODE_TEST_PI:
816 fprintf(output, "'PI' "); break;
817 case NODE_TEST_ALL:
818 fprintf(output, "'all' "); break;
819 case NODE_TEST_NS:
820 fprintf(output, "'namespace' "); break;
821 case NODE_TEST_NAME:
822 fprintf(output, "'name' "); break;
823 }
824 switch (type) {
825 case NODE_TYPE_NODE:
826 fprintf(output, "'node' "); break;
827 case NODE_TYPE_COMMENT:
828 fprintf(output, "'comment' "); break;
829 case NODE_TYPE_TEXT:
830 fprintf(output, "'text' "); break;
831 case NODE_TYPE_PI:
832 fprintf(output, "'PI' "); break;
833 }
834 if (prefix != NULL)
835 fprintf(output, "%s:", prefix);
836 if (name != NULL)
837 fprintf(output, "%s", name);
838 break;
839
840 }
841 case XPATH_OP_VALUE: {
842 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
843
844 fprintf(output, "ELEM ");
845 xmlXPathDebugDumpObject(output, object, 0);
846 goto finish;
847 }
848 case XPATH_OP_VARIABLE: {
849 const xmlChar *prefix = op->value5;
850 const xmlChar *name = op->value4;
851
852 if (prefix != NULL)
853 fprintf(output, "VARIABLE %s:%s", prefix, name);
854 else
855 fprintf(output, "VARIABLE %s", name);
856 break;
857 }
858 case XPATH_OP_FUNCTION: {
859 int nbargs = op->value;
860 const xmlChar *prefix = op->value5;
861 const xmlChar *name = op->value4;
862
863 if (prefix != NULL)
864 fprintf(output, "FUNCTION %s:%s(%d args)",
865 prefix, name, nbargs);
866 else
867 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
868 break;
869 }
870 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
871 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000872 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +0000873#ifdef LIBXML_XPTR_ENABLED
874 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
875#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000876 default:
877 fprintf(output, "UNKNOWN %d\n", op->op); return;
878 }
879 fprintf(output, "\n");
880finish:
881 if (op->ch1 >= 0)
882 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
883 if (op->ch2 >= 0)
884 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
885}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000886
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000887/**
888 * xmlXPathDebugDumpCompExpr:
889 * @output: the FILE * for the output
890 * @comp: the precompiled XPath expression
891 * @depth: the indentation level.
892 *
893 * Dumps the tree of the compiled XPath expression.
894 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000895void
896xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
897 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000898 int i;
899 char shift[100];
900
901 for (i = 0;((i < depth) && (i < 25));i++)
902 shift[2 * i] = shift[2 * i + 1] = ' ';
903 shift[2 * i] = shift[2 * i + 1] = 0;
904
905 fprintf(output, shift);
906
907 if (comp == NULL) {
908 fprintf(output, "Compiled Expression is NULL\n");
909 return;
910 }
911 fprintf(output, "Compiled Expression : %d elements\n",
912 comp->nbStep);
913 i = comp->last;
914 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
915}
Daniel Veillard017b1082001-06-21 11:20:21 +0000916#endif /* LIBXML_DEBUG_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +0000917
918/************************************************************************
919 * *
920 * Parser stacks related functions and macros *
921 * *
922 ************************************************************************/
923
924/*
925 * Generic function for accessing stacks in the Parser Context
926 */
927
928#define PUSH_AND_POP(type, name) \
929extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
930 if (ctxt->name##Nr >= ctxt->name##Max) { \
931 ctxt->name##Max *= 2; \
932 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
933 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
934 if (ctxt->name##Tab == NULL) { \
935 xmlGenericError(xmlGenericErrorContext, \
936 "realloc failed !\n"); \
937 return(0); \
938 } \
939 } \
940 ctxt->name##Tab[ctxt->name##Nr] = value; \
941 ctxt->name = value; \
942 return(ctxt->name##Nr++); \
943} \
944extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
945 type ret; \
946 if (ctxt->name##Nr <= 0) return(0); \
947 ctxt->name##Nr--; \
948 if (ctxt->name##Nr > 0) \
949 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
950 else \
951 ctxt->name = NULL; \
952 ret = ctxt->name##Tab[ctxt->name##Nr]; \
953 ctxt->name##Tab[ctxt->name##Nr] = 0; \
954 return(ret); \
955} \
956
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000957/**
958 * valuePop:
959 * @ctxt: an XPath evaluation context
960 *
961 * Pops the top XPath object from the value stack
962 *
963 * Returns the XPath object just removed
964 */
965/**
966 * valuePush:
967 * @ctxt: an XPath evaluation context
968 * @value: the XPath object
969 *
970 * Pushes a new XPath object on top of the value stack
971 */
Owen Taylor3473f882001-02-23 17:55:21 +0000972PUSH_AND_POP(xmlXPathObjectPtr, value)
973
Thomas Broyerf06a3d82001-07-16 04:52:57 +0000974/**
975 * xmlXPathPopBoolean:
976 * @ctxt: an XPath parser context
977 *
978 * Pops a boolean from the stack, handling conversion if needed.
979 * Check error with #xmlXPathCheckError.
980 *
981 * Returns the boolean
982 */
983int
984xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
985 xmlXPathObjectPtr obj;
986 int ret;
987
988 obj = valuePop(ctxt);
989 if (obj == NULL) {
990 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
991 return(0);
992 }
993 ret = xmlXPathCastToBoolean(obj);
994 xmlXPathFreeObject(obj);
995 return(ret);
996}
997
998/**
999 * xmlXPathPopNumber:
1000 * @ctxt: an XPath parser context
1001 *
1002 * Pops a number from the stack, handling conversion if needed.
1003 * Check error with #xmlXPathCheckError.
1004 *
1005 * Returns the number
1006 */
1007double
1008xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
1009 xmlXPathObjectPtr obj;
1010 double ret;
1011
1012 obj = valuePop(ctxt);
1013 if (obj == NULL) {
1014 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
1015 return(0);
1016 }
1017 ret = xmlXPathCastToNumber(obj);
1018 xmlXPathFreeObject(obj);
1019 return(ret);
1020}
1021
1022/**
1023 * xmlXPathPopString:
1024 * @ctxt: an XPath parser context
1025 *
1026 * Pops a string from the stack, handling conversion if needed.
1027 * Check error with #xmlXPathCheckError.
1028 *
1029 * Returns the string
1030 */
1031xmlChar *
1032xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
1033 xmlXPathObjectPtr obj;
1034 xmlChar * ret;
1035
1036 obj = valuePop(ctxt);
1037 if (obj == NULL) {
1038 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
1039 return(NULL);
1040 }
1041 ret = xmlXPathCastToString(obj);
1042 /* TODO: needs refactoring somewhere else */
1043 if (obj->stringval == ret)
1044 obj->stringval = NULL;
1045 xmlXPathFreeObject(obj);
1046 return(ret);
1047}
1048
1049/**
1050 * xmlXPathPopNodeSet:
1051 * @ctxt: an XPath parser context
1052 *
1053 * Pops a node-set from the stack, handling conversion if needed.
1054 * Check error with #xmlXPathCheckError.
1055 *
1056 * Returns the node-set
1057 */
1058xmlNodeSetPtr
1059xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
1060 xmlXPathObjectPtr obj;
1061 xmlNodeSetPtr ret;
1062
1063 if (ctxt->value == NULL) {
1064 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
1065 return(NULL);
1066 }
1067 if (!xmlXPathStackIsNodeSet(ctxt)) {
1068 xmlXPathSetTypeError(ctxt);
1069 return(NULL);
1070 }
1071 obj = valuePop(ctxt);
1072 ret = obj->nodesetval;
1073 xmlXPathFreeNodeSetList(obj);
1074 return(ret);
1075}
1076
1077/**
1078 * xmlXPathPopExternal:
1079 * @ctxt: an XPath parser context
1080 *
1081 * Pops an external oject from the stack, handling conversion if needed.
1082 * Check error with #xmlXPathCheckError.
1083 *
1084 * Returns the object
1085 */
1086void *
1087xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
1088 xmlXPathObjectPtr obj;
1089 void * ret;
1090
1091 if (ctxt->value == NULL) {
1092 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
1093 return(NULL);
1094 }
1095 if (ctxt->value->type != XPATH_USERS) {
1096 xmlXPathSetTypeError(ctxt);
1097 return(NULL);
1098 }
1099 obj = valuePop(ctxt);
1100 ret = obj->user;
1101 xmlXPathFreeObject(obj);
1102 return(ret);
1103}
1104
Owen Taylor3473f882001-02-23 17:55:21 +00001105/*
1106 * Macros for accessing the content. Those should be used only by the parser,
1107 * and not exported.
1108 *
1109 * Dirty macros, i.e. one need to make assumption on the context to use them
1110 *
1111 * CUR_PTR return the current pointer to the xmlChar to be parsed.
1112 * CUR returns the current xmlChar value, i.e. a 8 bit value
1113 * in ISO-Latin or UTF-8.
1114 * This should be used internally by the parser
1115 * only to compare to ASCII values otherwise it would break when
1116 * running with UTF-8 encoding.
1117 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
1118 * to compare on ASCII based substring.
1119 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
1120 * strings within the parser.
1121 * CURRENT Returns the current char value, with the full decoding of
1122 * UTF-8 if we are using this mode. It returns an int.
1123 * NEXT Skip to the next character, this does the proper decoding
1124 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
1125 * It returns the pointer to the current xmlChar.
1126 */
1127
1128#define CUR (*ctxt->cur)
1129#define SKIP(val) ctxt->cur += (val)
1130#define NXT(val) ctxt->cur[(val)]
1131#define CUR_PTR ctxt->cur
Daniel Veillard61d80a22001-04-27 17:13:01 +00001132#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
1133
1134#define COPY_BUF(l,b,i,v) \
1135 if (l == 1) b[i++] = (xmlChar) v; \
1136 else i += xmlCopyChar(l,&b[i],v)
1137
1138#define NEXTL(l) ctxt->cur += l
Owen Taylor3473f882001-02-23 17:55:21 +00001139
1140#define SKIP_BLANKS \
1141 while (IS_BLANK(*(ctxt->cur))) NEXT
1142
1143#define CURRENT (*ctxt->cur)
1144#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
1145
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001146
1147#ifndef DBL_DIG
1148#define DBL_DIG 16
1149#endif
1150#ifndef DBL_EPSILON
1151#define DBL_EPSILON 1E-9
1152#endif
1153
1154#define UPPER_DOUBLE 1E9
1155#define LOWER_DOUBLE 1E-5
1156
1157#define INTEGER_DIGITS DBL_DIG
1158#define FRACTION_DIGITS (DBL_DIG + 1)
1159#define EXPONENT_DIGITS (3 + 2)
1160
1161/**
1162 * xmlXPathFormatNumber:
1163 * @number: number to format
1164 * @buffer: output buffer
1165 * @buffersize: size of output buffer
1166 *
1167 * Convert the number into a string representation.
1168 */
1169static void
1170xmlXPathFormatNumber(double number, char buffer[], int buffersize)
1171{
1172 switch (isinf(number)) {
1173 case 1:
1174 if (buffersize > (int)sizeof("+Infinity"))
1175 sprintf(buffer, "+Infinity");
1176 break;
1177 case -1:
1178 if (buffersize > (int)sizeof("-Infinity"))
1179 sprintf(buffer, "-Infinity");
1180 break;
1181 default:
1182 if (isnan(number)) {
1183 if (buffersize > (int)sizeof("NaN"))
1184 sprintf(buffer, "NaN");
1185 } else {
Bjorn Reese70a9da52001-04-21 16:57:29 +00001186 /* 3 is sign, decimal point, and terminating zero */
1187 char work[DBL_DIG + EXPONENT_DIGITS + 3];
1188 int integer_place, fraction_place;
1189 char *ptr;
1190 char *after_fraction;
1191 double absolute_value;
1192 int size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001193
Bjorn Reese70a9da52001-04-21 16:57:29 +00001194 absolute_value = fabs(number);
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001195
Bjorn Reese70a9da52001-04-21 16:57:29 +00001196 /*
1197 * First choose format - scientific or regular floating point.
1198 * In either case, result is in work, and after_fraction points
1199 * just past the fractional part.
1200 */
1201 if ( ((absolute_value > UPPER_DOUBLE) ||
1202 (absolute_value < LOWER_DOUBLE)) &&
1203 (absolute_value != 0.0) ) {
1204 /* Use scientific notation */
1205 integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
1206 fraction_place = DBL_DIG - 1;
1207 snprintf(work, sizeof(work),"%*.*e",
1208 integer_place, fraction_place, number);
1209 after_fraction = strchr(work + DBL_DIG, 'e');
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001210 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00001211 else {
1212 /* Use regular notation */
Daniel Veillard56f06462001-06-24 21:34:03 +00001213 if (absolute_value > 0.0)
1214 integer_place = 1 + (int)log10(absolute_value);
1215 else
Daniel Veillarda3067d12001-06-24 21:39:39 +00001216 integer_place = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00001217 fraction_place = (integer_place > 0)
1218 ? DBL_DIG - integer_place
1219 : DBL_DIG;
1220 size = snprintf(work, sizeof(work), "%0.*f",
1221 fraction_place, number);
1222 after_fraction = work + size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001223 }
1224
Bjorn Reese70a9da52001-04-21 16:57:29 +00001225 /* Remove fractional trailing zeroes */
1226 ptr = after_fraction;
1227 while (*(--ptr) == '0')
1228 ;
1229 if (*ptr != '.')
1230 ptr++;
1231 strcpy(ptr, after_fraction);
1232
1233 /* Finally copy result back to caller */
1234 size = strlen(work) + 1;
1235 if (size > buffersize) {
1236 work[buffersize - 1] = 0;
1237 size = buffersize;
1238 }
1239 memcpy(buffer, work, size);
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001240 }
1241 break;
1242 }
1243}
1244
Owen Taylor3473f882001-02-23 17:55:21 +00001245/************************************************************************
1246 * *
1247 * Error handling routines *
1248 * *
1249 ************************************************************************/
1250
1251
1252const char *xmlXPathErrorMessages[] = {
1253 "Ok",
1254 "Number encoding",
1255 "Unfinished litteral",
1256 "Start of litteral",
1257 "Expected $ for variable reference",
1258 "Undefined variable",
1259 "Invalid predicate",
1260 "Invalid expression",
1261 "Missing closing curly brace",
1262 "Unregistered function",
1263 "Invalid operand",
1264 "Invalid type",
1265 "Invalid number of arguments",
1266 "Invalid context size",
1267 "Invalid context position",
1268 "Memory allocation error",
1269 "Syntax error",
1270 "Resource error",
1271 "Sub resource error",
Daniel Veillard61d80a22001-04-27 17:13:01 +00001272 "Undefined namespace prefix",
1273 "Encoding error",
1274 "Char out of XML range"
Owen Taylor3473f882001-02-23 17:55:21 +00001275};
1276
1277/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001278 * xmlXPatherror:
Owen Taylor3473f882001-02-23 17:55:21 +00001279 * @ctxt: the XPath Parser context
1280 * @file: the file name
1281 * @line: the line number
1282 * @no: the error number
1283 *
Thomas Broyerf06a3d82001-07-16 04:52:57 +00001284 * Formats an error message.
Owen Taylor3473f882001-02-23 17:55:21 +00001285 */
1286void
1287xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1288 int line, int no) {
1289 int n;
1290 const xmlChar *cur;
1291 const xmlChar *base;
1292
1293 xmlGenericError(xmlGenericErrorContext,
1294 "Error %s:%d: %s\n", file, line,
1295 xmlXPathErrorMessages[no]);
1296
1297 cur = ctxt->cur;
1298 base = ctxt->base;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001299 if ((cur == NULL) || (base == NULL))
1300 return;
1301
Owen Taylor3473f882001-02-23 17:55:21 +00001302 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1303 cur--;
1304 }
1305 n = 0;
1306 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1307 cur--;
1308 if ((*cur == '\n') || (*cur == '\r')) cur++;
1309 base = cur;
1310 n = 0;
1311 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1312 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1313 n++;
1314 }
1315 xmlGenericError(xmlGenericErrorContext, "\n");
1316 cur = ctxt->cur;
1317 while ((*cur == '\n') || (*cur == '\r'))
1318 cur--;
1319 n = 0;
1320 while ((cur != base) && (n++ < 80)) {
1321 xmlGenericError(xmlGenericErrorContext, " ");
1322 base++;
1323 }
1324 xmlGenericError(xmlGenericErrorContext,"^\n");
1325}
1326
1327
1328/************************************************************************
1329 * *
1330 * Routines to handle NodeSets *
1331 * *
1332 ************************************************************************/
1333
1334/**
1335 * xmlXPathCmpNodes:
1336 * @node1: the first node
1337 * @node2: the second node
1338 *
1339 * Compare two nodes w.r.t document order
1340 *
1341 * Returns -2 in case of error 1 if first point < second point, 0 if
1342 * that's the same node, -1 otherwise
1343 */
1344int
1345xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1346 int depth1, depth2;
1347 xmlNodePtr cur, root;
1348
1349 if ((node1 == NULL) || (node2 == NULL))
1350 return(-2);
1351 /*
1352 * a couple of optimizations which will avoid computations in most cases
1353 */
1354 if (node1 == node2)
1355 return(0);
Daniel Veillardb33c2012001-04-25 12:59:04 +00001356 if ((node1->type == XML_NAMESPACE_DECL) ||
1357 (node2->type == XML_NAMESPACE_DECL))
1358 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00001359 if (node1 == node2->prev)
1360 return(1);
1361 if (node1 == node2->next)
1362 return(-1);
1363
1364 /*
1365 * compute depth to root
1366 */
1367 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1368 if (cur == node1)
1369 return(1);
1370 depth2++;
1371 }
1372 root = cur;
1373 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1374 if (cur == node2)
1375 return(-1);
1376 depth1++;
1377 }
1378 /*
1379 * Distinct document (or distinct entities :-( ) case.
1380 */
1381 if (root != cur) {
1382 return(-2);
1383 }
1384 /*
1385 * get the nearest common ancestor.
1386 */
1387 while (depth1 > depth2) {
1388 depth1--;
1389 node1 = node1->parent;
1390 }
1391 while (depth2 > depth1) {
1392 depth2--;
1393 node2 = node2->parent;
1394 }
1395 while (node1->parent != node2->parent) {
1396 node1 = node1->parent;
1397 node2 = node2->parent;
1398 /* should not happen but just in case ... */
1399 if ((node1 == NULL) || (node2 == NULL))
1400 return(-2);
1401 }
1402 /*
1403 * Find who's first.
1404 */
1405 if (node1 == node2->next)
1406 return(-1);
1407 for (cur = node1->next;cur != NULL;cur = cur->next)
1408 if (cur == node2)
1409 return(1);
1410 return(-1); /* assume there is no sibling list corruption */
1411}
1412
1413/**
1414 * xmlXPathNodeSetSort:
1415 * @set: the node set
1416 *
1417 * Sort the node set in document order
1418 */
1419void
1420xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001421 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001422 xmlNodePtr tmp;
1423
1424 if (set == NULL)
1425 return;
1426
1427 /* Use Shell's sort to sort the node-set */
1428 len = set->nodeNr;
1429 for (incr = len / 2; incr > 0; incr /= 2) {
1430 for (i = incr; i < len; i++) {
1431 j = i - incr;
1432 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001433 if (xmlXPathCmpNodes(set->nodeTab[j],
1434 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001435 tmp = set->nodeTab[j];
1436 set->nodeTab[j] = set->nodeTab[j + incr];
1437 set->nodeTab[j + incr] = tmp;
1438 j -= incr;
1439 } else
1440 break;
1441 }
1442 }
1443 }
1444}
1445
1446#define XML_NODESET_DEFAULT 10
1447/**
1448 * xmlXPathNodeSetCreate:
1449 * @val: an initial xmlNodePtr, or NULL
1450 *
1451 * Create a new xmlNodeSetPtr of type double and of value @val
1452 *
1453 * Returns the newly created object.
1454 */
1455xmlNodeSetPtr
1456xmlXPathNodeSetCreate(xmlNodePtr val) {
1457 xmlNodeSetPtr ret;
1458
1459 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1460 if (ret == NULL) {
1461 xmlGenericError(xmlGenericErrorContext,
1462 "xmlXPathNewNodeSet: out of memory\n");
1463 return(NULL);
1464 }
1465 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1466 if (val != NULL) {
1467 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1468 sizeof(xmlNodePtr));
1469 if (ret->nodeTab == NULL) {
1470 xmlGenericError(xmlGenericErrorContext,
1471 "xmlXPathNewNodeSet: out of memory\n");
1472 return(NULL);
1473 }
1474 memset(ret->nodeTab, 0 ,
1475 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1476 ret->nodeMax = XML_NODESET_DEFAULT;
1477 ret->nodeTab[ret->nodeNr++] = val;
1478 }
1479 return(ret);
1480}
1481
1482/**
Thomas Broyerf06a3d82001-07-16 04:52:57 +00001483 * xmlXPathNodeSetContains:
1484 * @cur: the node-set
1485 * @val: the node
1486 *
1487 * checks whether @cur contains @val
1488 *
1489 * Returns true (1) if @cur contains @val, false (0) otherwise
1490 */
1491int
1492xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
1493 int i;
1494
1495 for (i = 0; i < cur->nodeNr; i++) {
1496 if (cur->nodeTab[i] == val)
1497 return(1);
1498 }
1499 return(0);
1500}
1501
1502/**
Owen Taylor3473f882001-02-23 17:55:21 +00001503 * xmlXPathNodeSetAdd:
1504 * @cur: the initial node set
1505 * @val: a new xmlNodePtr
1506 *
1507 * add a new xmlNodePtr ot an existing NodeSet
1508 */
1509void
1510xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1511 int i;
1512
1513 if (val == NULL) return;
1514
1515 /*
1516 * check against doublons
1517 */
1518 for (i = 0;i < cur->nodeNr;i++)
1519 if (cur->nodeTab[i] == val) return;
1520
1521 /*
1522 * grow the nodeTab if needed
1523 */
1524 if (cur->nodeMax == 0) {
1525 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1526 sizeof(xmlNodePtr));
1527 if (cur->nodeTab == NULL) {
1528 xmlGenericError(xmlGenericErrorContext,
1529 "xmlXPathNodeSetAdd: out of memory\n");
1530 return;
1531 }
1532 memset(cur->nodeTab, 0 ,
1533 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1534 cur->nodeMax = XML_NODESET_DEFAULT;
1535 } else if (cur->nodeNr == cur->nodeMax) {
1536 xmlNodePtr *temp;
1537
1538 cur->nodeMax *= 2;
1539 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1540 sizeof(xmlNodePtr));
1541 if (temp == NULL) {
1542 xmlGenericError(xmlGenericErrorContext,
1543 "xmlXPathNodeSetAdd: out of memory\n");
1544 return;
1545 }
1546 cur->nodeTab = temp;
1547 }
1548 cur->nodeTab[cur->nodeNr++] = val;
1549}
1550
1551/**
1552 * xmlXPathNodeSetAddUnique:
1553 * @cur: the initial node set
1554 * @val: a new xmlNodePtr
1555 *
1556 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1557 * when we are sure the node is not already in the set.
1558 */
1559void
1560xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1561 if (val == NULL) return;
1562
1563 /*
1564 * grow the nodeTab if needed
1565 */
1566 if (cur->nodeMax == 0) {
1567 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1568 sizeof(xmlNodePtr));
1569 if (cur->nodeTab == NULL) {
1570 xmlGenericError(xmlGenericErrorContext,
1571 "xmlXPathNodeSetAddUnique: out of memory\n");
1572 return;
1573 }
1574 memset(cur->nodeTab, 0 ,
1575 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1576 cur->nodeMax = XML_NODESET_DEFAULT;
1577 } else if (cur->nodeNr == cur->nodeMax) {
1578 xmlNodePtr *temp;
1579
1580 cur->nodeMax *= 2;
1581 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1582 sizeof(xmlNodePtr));
1583 if (temp == NULL) {
1584 xmlGenericError(xmlGenericErrorContext,
1585 "xmlXPathNodeSetAddUnique: out of memory\n");
1586 return;
1587 }
1588 cur->nodeTab = temp;
1589 }
1590 cur->nodeTab[cur->nodeNr++] = val;
1591}
1592
1593/**
1594 * xmlXPathNodeSetMerge:
1595 * @val1: the first NodeSet or NULL
1596 * @val2: the second NodeSet
1597 *
1598 * Merges two nodesets, all nodes from @val2 are added to @val1
1599 * if @val1 is NULL, a new set is created and copied from @val2
1600 *
1601 * Returns val1 once extended or NULL in case of error.
1602 */
1603xmlNodeSetPtr
1604xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001605 int i, j, initNr, skip;
Owen Taylor3473f882001-02-23 17:55:21 +00001606
1607 if (val2 == NULL) return(val1);
1608 if (val1 == NULL) {
1609 val1 = xmlXPathNodeSetCreate(NULL);
1610 }
1611
1612 initNr = val1->nodeNr;
1613
1614 for (i = 0;i < val2->nodeNr;i++) {
1615 /*
1616 * check against doublons
1617 */
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001618 skip = 0;
1619 for (j = 0; j < initNr; j++) {
1620 if (val1->nodeTab[j] == val2->nodeTab[i]) {
1621 skip = 1;
1622 break;
1623 }
1624 }
1625 if (skip)
1626 continue;
Owen Taylor3473f882001-02-23 17:55:21 +00001627
1628 /*
1629 * grow the nodeTab if needed
1630 */
1631 if (val1->nodeMax == 0) {
1632 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1633 sizeof(xmlNodePtr));
1634 if (val1->nodeTab == NULL) {
1635 xmlGenericError(xmlGenericErrorContext,
1636 "xmlXPathNodeSetMerge: out of memory\n");
1637 return(NULL);
1638 }
1639 memset(val1->nodeTab, 0 ,
1640 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1641 val1->nodeMax = XML_NODESET_DEFAULT;
1642 } else if (val1->nodeNr == val1->nodeMax) {
1643 xmlNodePtr *temp;
1644
1645 val1->nodeMax *= 2;
1646 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1647 sizeof(xmlNodePtr));
1648 if (temp == NULL) {
1649 xmlGenericError(xmlGenericErrorContext,
1650 "xmlXPathNodeSetMerge: out of memory\n");
1651 return(NULL);
1652 }
1653 val1->nodeTab = temp;
1654 }
1655 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1656 }
1657
1658 return(val1);
1659}
1660
1661/**
1662 * xmlXPathNodeSetDel:
1663 * @cur: the initial node set
1664 * @val: an xmlNodePtr
1665 *
1666 * Removes an xmlNodePtr from an existing NodeSet
1667 */
1668void
1669xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1670 int i;
1671
1672 if (cur == NULL) return;
1673 if (val == NULL) return;
1674
1675 /*
1676 * check against doublons
1677 */
1678 for (i = 0;i < cur->nodeNr;i++)
1679 if (cur->nodeTab[i] == val) break;
1680
1681 if (i >= cur->nodeNr) {
1682#ifdef DEBUG
1683 xmlGenericError(xmlGenericErrorContext,
1684 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1685 val->name);
1686#endif
1687 return;
1688 }
1689 cur->nodeNr--;
1690 for (;i < cur->nodeNr;i++)
1691 cur->nodeTab[i] = cur->nodeTab[i + 1];
1692 cur->nodeTab[cur->nodeNr] = NULL;
1693}
1694
1695/**
1696 * xmlXPathNodeSetRemove:
1697 * @cur: the initial node set
1698 * @val: the index to remove
1699 *
1700 * Removes an entry from an existing NodeSet list.
1701 */
1702void
1703xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1704 if (cur == NULL) return;
1705 if (val >= cur->nodeNr) return;
1706 cur->nodeNr--;
1707 for (;val < cur->nodeNr;val++)
1708 cur->nodeTab[val] = cur->nodeTab[val + 1];
1709 cur->nodeTab[cur->nodeNr] = NULL;
1710}
1711
1712/**
1713 * xmlXPathFreeNodeSet:
1714 * @obj: the xmlNodeSetPtr to free
1715 *
1716 * Free the NodeSet compound (not the actual nodes !).
1717 */
1718void
1719xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1720 if (obj == NULL) return;
1721 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001722 xmlFree(obj->nodeTab);
1723 }
Owen Taylor3473f882001-02-23 17:55:21 +00001724 xmlFree(obj);
1725}
1726
1727/**
1728 * xmlXPathFreeValueTree:
1729 * @obj: the xmlNodeSetPtr to free
1730 *
1731 * Free the NodeSet compound and the actual tree, this is different
1732 * from xmlXPathFreeNodeSet()
1733 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001734static void
Owen Taylor3473f882001-02-23 17:55:21 +00001735xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1736 int i;
1737
1738 if (obj == NULL) return;
1739 for (i = 0;i < obj->nodeNr;i++)
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00001740 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001741 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001742
1743 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001744 xmlFree(obj->nodeTab);
1745 }
Owen Taylor3473f882001-02-23 17:55:21 +00001746 xmlFree(obj);
1747}
1748
1749#if defined(DEBUG) || defined(DEBUG_STEP)
1750/**
1751 * xmlGenericErrorContextNodeSet:
1752 * @output: a FILE * for the output
1753 * @obj: the xmlNodeSetPtr to free
1754 *
1755 * Quick display of a NodeSet
1756 */
1757void
1758xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1759 int i;
1760
1761 if (output == NULL) output = xmlGenericErrorContext;
1762 if (obj == NULL) {
1763 fprintf(output, "NodeSet == NULL !\n");
1764 return;
1765 }
1766 if (obj->nodeNr == 0) {
1767 fprintf(output, "NodeSet is empty\n");
1768 return;
1769 }
1770 if (obj->nodeTab == NULL) {
1771 fprintf(output, " nodeTab == NULL !\n");
1772 return;
1773 }
1774 for (i = 0; i < obj->nodeNr; i++) {
1775 if (obj->nodeTab[i] == NULL) {
1776 fprintf(output, " NULL !\n");
1777 return;
1778 }
1779 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1780 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1781 fprintf(output, " /");
1782 else if (obj->nodeTab[i]->name == NULL)
1783 fprintf(output, " noname!");
1784 else fprintf(output, " %s", obj->nodeTab[i]->name);
1785 }
1786 fprintf(output, "\n");
1787}
1788#endif
1789
1790/**
1791 * xmlXPathNewNodeSet:
1792 * @val: the NodePtr value
1793 *
1794 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1795 * it with the single Node @val
1796 *
1797 * Returns the newly created object.
1798 */
1799xmlXPathObjectPtr
1800xmlXPathNewNodeSet(xmlNodePtr val) {
1801 xmlXPathObjectPtr ret;
1802
1803 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1804 if (ret == NULL) {
1805 xmlGenericError(xmlGenericErrorContext,
1806 "xmlXPathNewNodeSet: out of memory\n");
1807 return(NULL);
1808 }
1809 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1810 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001811 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001812 ret->nodesetval = xmlXPathNodeSetCreate(val);
1813 return(ret);
1814}
1815
1816/**
1817 * xmlXPathNewValueTree:
1818 * @val: the NodePtr value
1819 *
1820 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1821 * it with the tree root @val
1822 *
1823 * Returns the newly created object.
1824 */
1825xmlXPathObjectPtr
1826xmlXPathNewValueTree(xmlNodePtr val) {
1827 xmlXPathObjectPtr ret;
1828
1829 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1830 if (ret == NULL) {
1831 xmlGenericError(xmlGenericErrorContext,
1832 "xmlXPathNewNodeSet: out of memory\n");
1833 return(NULL);
1834 }
1835 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1836 ret->type = XPATH_XSLT_TREE;
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00001837 ret->boolval = 1;
1838 ret->user = (void *) val;
Owen Taylor3473f882001-02-23 17:55:21 +00001839 ret->nodesetval = xmlXPathNodeSetCreate(val);
1840 return(ret);
1841}
1842
1843/**
1844 * xmlXPathNewNodeSetList:
1845 * @val: an existing NodeSet
1846 *
1847 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1848 * it with the Nodeset @val
1849 *
1850 * Returns the newly created object.
1851 */
1852xmlXPathObjectPtr
1853xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1854 xmlXPathObjectPtr ret;
1855 int i;
1856
1857 if (val == NULL)
1858 ret = NULL;
1859 else if (val->nodeTab == NULL)
1860 ret = xmlXPathNewNodeSet(NULL);
1861 else
1862 {
1863 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1864 for (i = 1; i < val->nodeNr; ++i)
1865 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1866 }
1867
1868 return(ret);
1869}
1870
1871/**
1872 * xmlXPathWrapNodeSet:
1873 * @val: the NodePtr value
1874 *
1875 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1876 *
1877 * Returns the newly created object.
1878 */
1879xmlXPathObjectPtr
1880xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1881 xmlXPathObjectPtr ret;
1882
1883 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1884 if (ret == NULL) {
1885 xmlGenericError(xmlGenericErrorContext,
1886 "xmlXPathWrapNodeSet: out of memory\n");
1887 return(NULL);
1888 }
1889 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1890 ret->type = XPATH_NODESET;
1891 ret->nodesetval = val;
1892 return(ret);
1893}
1894
1895/**
1896 * xmlXPathFreeNodeSetList:
1897 * @obj: an existing NodeSetList object
1898 *
1899 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1900 * the list contrary to xmlXPathFreeObject().
1901 */
1902void
1903xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1904 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001905 xmlFree(obj);
1906}
1907
Thomas Broyerf06a3d82001-07-16 04:52:57 +00001908/**
1909 * xmlXPathDifference:
1910 * @nodes1: a node-set
1911 * @nodes2: a node-set
1912 *
1913 * Implements the EXSLT - Sets difference() function:
1914 * node-set set:difference (node-set, node-set)
1915 *
1916 * Returns the difference between the two node sets, or nodes1 if
1917 * nodes2 is empty
1918 */
1919xmlNodeSetPtr
1920xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
1921 xmlNodeSetPtr ret;
1922 int i, l1;
1923 xmlNodePtr cur;
1924
1925 if (xmlXPathNodeSetIsEmpty(nodes2))
1926 return(nodes1);
1927
1928 ret = xmlXPathNodeSetCreate(NULL);
1929 if (xmlXPathNodeSetIsEmpty(nodes1))
1930 return(ret);
1931
1932 l1 = xmlXPathNodeSetGetLength(nodes1);
1933
1934 for (i = 0; i < l1; i++) {
1935 cur = xmlXPathNodeSetItem(nodes1, i);
1936 if (!xmlXPathNodeSetContains(nodes2, cur))
1937 xmlXPathNodeSetAddUnique(ret, cur);
1938 }
1939 return(ret);
1940}
1941
1942/**
1943 * xmlXPathIntersection:
1944 * @nodes1: a node-set
1945 * @nodes2: a node-set
1946 *
1947 * Implements the EXSLT - Sets intersection() function:
1948 * node-set set:intersection (node-set, node-set)
1949 *
1950 * Returns a node set comprising the nodes that are within both the
1951 * node sets passed as arguments
1952 */
1953xmlNodeSetPtr
1954xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
1955 xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
1956 int i, l1;
1957 xmlNodePtr cur;
1958
1959 if (xmlXPathNodeSetIsEmpty(nodes1))
1960 return(ret);
1961 if (xmlXPathNodeSetIsEmpty(nodes2))
1962 return(ret);
1963
1964 l1 = xmlXPathNodeSetGetLength(nodes1);
1965
1966 for (i = 0; i < l1; i++) {
1967 cur = xmlXPathNodeSetItem(nodes1, i);
1968 if (xmlXPathNodeSetContains(nodes2, cur))
1969 xmlXPathNodeSetAddUnique(ret, cur);
1970 }
1971 return(ret);
1972}
1973
1974/**
1975 * xmlXPathDistinctSorted:
1976 * @nodes: a node-set, sorted by document order
1977 *
1978 * Implements the EXSLT - Sets distinct() function:
1979 * node-set set:distinct (node-set)
1980 *
1981 * Returns a subset of the nodes contained in @nodes, or @nodes if
1982 * it is empty
1983 */
1984xmlNodeSetPtr
1985xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
1986 xmlNodeSetPtr ret;
1987 xmlHashTablePtr hash;
1988 int i, l;
1989 xmlChar * strval;
1990 xmlNodePtr cur;
1991
1992 if (xmlXPathNodeSetIsEmpty(nodes))
1993 return(nodes);
1994
1995 ret = xmlXPathNodeSetCreate(NULL);
1996 l = xmlXPathNodeSetGetLength(nodes);
1997 hash = xmlHashCreate (l);
1998 for (i = 0; i < l; i++) {
1999 cur = xmlXPathNodeSetItem(nodes, i);
2000 strval = xmlXPathCastNodeToString(cur);
2001 if (xmlHashLookup(hash, strval) == NULL) {
2002 xmlHashAddEntry(hash, strval, strval);
2003 xmlXPathNodeSetAddUnique(ret, cur);
2004 } else {
2005 xmlFree(strval);
2006 }
2007 }
2008 xmlHashFree(hash, (xmlHashDeallocator) xmlFree);
2009 return(ret);
2010}
2011
2012/**
2013 * xmlXPathDistinct:
2014 * @nodes: a node-set
2015 *
2016 * Implements the EXSLT - Sets distinct() function:
2017 * node-set set:distinct (node-set)
2018 * @nodes is sorted by document order, then #exslSetsDistinctSorted
2019 * is called with the sorted node-set
2020 *
2021 * Returns a subset of the nodes contained in @nodes, or @nodes if
2022 * it is empty
2023 */
2024xmlNodeSetPtr
2025xmlXPathDistinct (xmlNodeSetPtr nodes) {
2026 if (xmlXPathNodeSetIsEmpty(nodes))
2027 return(nodes);
2028
2029 xmlXPathNodeSetSort(nodes);
2030 return(xmlXPathDistinctSorted(nodes));
2031}
2032
2033/**
2034 * xmlXPathHasSameNodes:
2035 * @nodes1: a node-set
2036 * @nodes2: a node-set
2037 *
2038 * Implements the EXSLT - Sets has-same-nodes function:
2039 * boolean set:has-same-node(node-set, node-set)
2040 *
2041 * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
2042 * otherwise
2043 */
2044int
2045xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2046 int i, l;
2047 xmlNodePtr cur;
2048
2049 if (xmlXPathNodeSetIsEmpty(nodes1) ||
2050 xmlXPathNodeSetIsEmpty(nodes2))
2051 return(0);
2052
2053 l = xmlXPathNodeSetGetLength(nodes1);
2054 for (i = 0; i < l; i++) {
2055 cur = xmlXPathNodeSetItem(nodes1, i);
2056 if (xmlXPathNodeSetContains(nodes2, cur))
2057 return(1);
2058 }
2059 return(0);
2060}
2061
2062/**
2063 * xmlXPathNodeLeadingSorted:
2064 * @nodes: a node-set, sorted by document order
2065 * @node: a node
2066 *
2067 * Implements the EXSLT - Sets leading() function:
2068 * node-set set:leading (node-set, node-set)
2069 *
2070 * Returns the nodes in @nodes that precede @node in document order,
2071 * @nodes if @node is NULL or an empty node-set if @nodes
2072 * doesn't contain @node
2073 */
2074xmlNodeSetPtr
2075xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
2076 int i, l;
2077 xmlNodePtr cur;
2078 xmlNodeSetPtr ret;
2079
2080 if (node == NULL)
2081 return(nodes);
2082
2083 ret = xmlXPathNodeSetCreate(NULL);
2084 if (xmlXPathNodeSetIsEmpty(nodes) ||
2085 (!xmlXPathNodeSetContains(nodes, node)))
2086 return(ret);
2087
2088 l = xmlXPathNodeSetGetLength(nodes);
2089 for (i = 0; i < l; i++) {
2090 cur = xmlXPathNodeSetItem(nodes, i);
2091 if (cur == node)
2092 break;
2093 xmlXPathNodeSetAddUnique(ret, cur);
2094 }
2095 return(ret);
2096}
2097
2098/**
2099 * xmlXPathNodeLeading:
2100 * @nodes: a node-set
2101 * @node: a node
2102 *
2103 * Implements the EXSLT - Sets leading() function:
2104 * node-set set:leading (node-set, node-set)
2105 * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
2106 * is called.
2107 *
2108 * Returns the nodes in @nodes that precede @node in document order,
2109 * @nodes if @node is NULL or an empty node-set if @nodes
2110 * doesn't contain @node
2111 */
2112xmlNodeSetPtr
2113xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
2114 xmlXPathNodeSetSort(nodes);
2115 return(xmlXPathNodeLeadingSorted(nodes, node));
2116}
2117
2118/**
2119 * xmlXPathLeadingSorted:
2120 * @nodes1: a node-set, sorted by document order
2121 * @nodes2: a node-set, sorted by document order
2122 *
2123 * Implements the EXSLT - Sets leading() function:
2124 * node-set set:leading (node-set, node-set)
2125 *
2126 * Returns the nodes in @nodes1 that precede the first node in @nodes2
2127 * in document order, @nodes1 if @nodes2 is NULL or empty or
2128 * an empty node-set if @nodes1 doesn't contain @nodes2
2129 */
2130xmlNodeSetPtr
2131xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2132 if (xmlXPathNodeSetIsEmpty(nodes2))
2133 return(nodes1);
2134 return(xmlXPathNodeLeadingSorted(nodes1,
2135 xmlXPathNodeSetItem(nodes2, 1)));
2136}
2137
2138/**
2139 * xmlXPathLeading:
2140 * @nodes1: a node-set
2141 * @nodes2: a node-set
2142 *
2143 * Implements the EXSLT - Sets leading() function:
2144 * node-set set:leading (node-set, node-set)
2145 * @nodes1 and @nodes2 are sorted by document order, then
2146 * #exslSetsLeadingSorted is called.
2147 *
2148 * Returns the nodes in @nodes1 that precede the first node in @nodes2
2149 * in document order, @nodes1 if @nodes2 is NULL or empty or
2150 * an empty node-set if @nodes1 doesn't contain @nodes2
2151 */
2152xmlNodeSetPtr
2153xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2154 if (xmlXPathNodeSetIsEmpty(nodes2))
2155 return(nodes1);
2156 if (xmlXPathNodeSetIsEmpty(nodes1))
2157 return(xmlXPathNodeSetCreate(NULL));
2158 xmlXPathNodeSetSort(nodes1);
2159 xmlXPathNodeSetSort(nodes2);
2160 return(xmlXPathNodeLeadingSorted(nodes1,
2161 xmlXPathNodeSetItem(nodes2, 1)));
2162}
2163
2164/**
2165 * xmlXPathNodeTrailingSorted:
2166 * @nodes: a node-set, sorted by document order
2167 * @node: a node
2168 *
2169 * Implements the EXSLT - Sets trailing() function:
2170 * node-set set:trailing (node-set, node-set)
2171 *
2172 * Returns the nodes in @nodes that follow @node in document order,
2173 * @nodes if @node is NULL or an empty node-set if @nodes
2174 * doesn't contain @node
2175 */
2176xmlNodeSetPtr
2177xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
2178 int i, l;
2179 xmlNodePtr cur;
2180 xmlNodeSetPtr ret;
2181
2182 if (node == NULL)
2183 return(nodes);
2184
2185 ret = xmlXPathNodeSetCreate(NULL);
2186 if (xmlXPathNodeSetIsEmpty(nodes) ||
2187 (!xmlXPathNodeSetContains(nodes, node)))
2188 return(ret);
2189
2190 l = xmlXPathNodeSetGetLength(nodes);
Thomas Broyerf186c822001-07-31 23:30:37 +00002191 for (i = l; i > 0; i--) {
Thomas Broyerf06a3d82001-07-16 04:52:57 +00002192 cur = xmlXPathNodeSetItem(nodes, i);
2193 if (cur == node)
2194 break;
2195 xmlXPathNodeSetAddUnique(ret, cur);
2196 }
2197 return(ret);
2198}
2199
2200/**
2201 * xmlXPathNodeTrailing:
2202 * @nodes: a node-set
2203 * @node: a node
2204 *
2205 * Implements the EXSLT - Sets trailing() function:
2206 * node-set set:trailing (node-set, node-set)
2207 * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
2208 * is called.
2209 *
2210 * Returns the nodes in @nodes that follow @node in document order,
2211 * @nodes if @node is NULL or an empty node-set if @nodes
2212 * doesn't contain @node
2213 */
2214xmlNodeSetPtr
2215xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
2216 xmlXPathNodeSetSort(nodes);
2217 return(xmlXPathNodeTrailingSorted(nodes, node));
2218}
2219
2220/**
2221 * xmlXPathTrailingSorted:
2222 * @nodes1: a node-set, sorted by document order
2223 * @nodes2: a node-set, sorted by document order
2224 *
2225 * Implements the EXSLT - Sets trailing() function:
2226 * node-set set:trailing (node-set, node-set)
2227 *
2228 * Returns the nodes in @nodes1 that follow the first node in @nodes2
2229 * in document order, @nodes1 if @nodes2 is NULL or empty or
2230 * an empty node-set if @nodes1 doesn't contain @nodes2
2231 */
2232xmlNodeSetPtr
2233xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2234 if (xmlXPathNodeSetIsEmpty(nodes2))
2235 return(nodes1);
2236 return(xmlXPathNodeTrailingSorted(nodes1,
2237 xmlXPathNodeSetItem(nodes2, 0)));
2238}
2239
2240/**
2241 * xmlXPathTrailing:
2242 * @nodes1: a node-set
2243 * @nodes2: a node-set
2244 *
2245 * Implements the EXSLT - Sets trailing() function:
2246 * node-set set:trailing (node-set, node-set)
2247 * @nodes1 and @nodes2 are sorted by document order, then
2248 * #xmlXPathTrailingSorted is called.
2249 *
2250 * Returns the nodes in @nodes1 that follow the first node in @nodes2
2251 * in document order, @nodes1 if @nodes2 is NULL or empty or
2252 * an empty node-set if @nodes1 doesn't contain @nodes2
2253 */
2254xmlNodeSetPtr
2255xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2256 if (xmlXPathNodeSetIsEmpty(nodes2))
2257 return(nodes1);
2258 if (xmlXPathNodeSetIsEmpty(nodes1))
2259 return(xmlXPathNodeSetCreate(NULL));
2260 xmlXPathNodeSetSort(nodes1);
2261 xmlXPathNodeSetSort(nodes2);
2262 return(xmlXPathNodeTrailingSorted(nodes1,
2263 xmlXPathNodeSetItem(nodes2, 0)));
2264}
2265
Owen Taylor3473f882001-02-23 17:55:21 +00002266/************************************************************************
2267 * *
2268 * Routines to handle extra functions *
2269 * *
2270 ************************************************************************/
2271
2272/**
2273 * xmlXPathRegisterFunc:
2274 * @ctxt: the XPath context
2275 * @name: the function name
2276 * @f: the function implementation or NULL
2277 *
2278 * Register a new function. If @f is NULL it unregisters the function
2279 *
2280 * Returns 0 in case of success, -1 in case of error
2281 */
2282int
2283xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
2284 xmlXPathFunction f) {
2285 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
2286}
2287
2288/**
2289 * xmlXPathRegisterFuncNS:
2290 * @ctxt: the XPath context
2291 * @name: the function name
2292 * @ns_uri: the function namespace URI
2293 * @f: the function implementation or NULL
2294 *
2295 * Register a new function. If @f is NULL it unregisters the function
2296 *
2297 * Returns 0 in case of success, -1 in case of error
2298 */
2299int
2300xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2301 const xmlChar *ns_uri, xmlXPathFunction f) {
2302 if (ctxt == NULL)
2303 return(-1);
2304 if (name == NULL)
2305 return(-1);
2306
2307 if (ctxt->funcHash == NULL)
2308 ctxt->funcHash = xmlHashCreate(0);
2309 if (ctxt->funcHash == NULL)
2310 return(-1);
2311 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
2312}
2313
2314/**
Thomas Broyerba4ad322001-07-26 16:55:21 +00002315 * xmlXPathRegisterFuncLookup:
2316 * @ctxt: the XPath context
2317 * @f: the lookup function
2318 * @data: the lookup data
2319 *
2320 * Registers an external mecanism to do function lookup.
2321 */
2322void
2323xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
2324 xmlXPathFuncLookupFunc f,
2325 void *funcCtxt) {
2326 if (ctxt == NULL)
2327 return;
2328 ctxt->funcLookupFunc = (void *) f;
2329 ctxt->funcLookupData = funcCtxt;
2330}
2331
2332/**
Owen Taylor3473f882001-02-23 17:55:21 +00002333 * xmlXPathFunctionLookup:
2334 * @ctxt: the XPath context
2335 * @name: the function name
2336 *
2337 * Search in the Function array of the context for the given
2338 * function.
2339 *
2340 * Returns the xmlXPathFunction or NULL if not found
2341 */
2342xmlXPathFunction
2343xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
Thomas Broyerba4ad322001-07-26 16:55:21 +00002344 if (ctxt == NULL)
2345 return (NULL);
2346
2347 if (ctxt->funcLookupFunc != NULL) {
2348 xmlXPathFunction ret;
2349
2350 ret = ((xmlXPathFuncLookupFunc) ctxt->funcLookupFunc)
2351 (ctxt->funcLookupData, name, NULL);
2352 if (ret != NULL)
2353 return(ret);
2354 }
Owen Taylor3473f882001-02-23 17:55:21 +00002355 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
2356}
2357
2358/**
2359 * xmlXPathFunctionLookupNS:
2360 * @ctxt: the XPath context
2361 * @name: the function name
2362 * @ns_uri: the function namespace URI
2363 *
2364 * Search in the Function array of the context for the given
2365 * function.
2366 *
2367 * Returns the xmlXPathFunction or NULL if not found
2368 */
2369xmlXPathFunction
2370xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2371 const xmlChar *ns_uri) {
2372 if (ctxt == NULL)
2373 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002374 if (name == NULL)
2375 return(NULL);
2376
Thomas Broyerba4ad322001-07-26 16:55:21 +00002377 if (ctxt->funcLookupFunc != NULL) {
2378 xmlXPathFunction ret;
2379
2380 ret = ((xmlXPathFuncLookupFunc) ctxt->funcLookupFunc)
2381 (ctxt->funcLookupData, name, ns_uri);
2382 if (ret != NULL)
2383 return(ret);
2384 }
2385
2386 if (ctxt->funcHash == NULL)
2387 return(NULL);
2388
Owen Taylor3473f882001-02-23 17:55:21 +00002389 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
2390}
2391
2392/**
2393 * xmlXPathRegisteredFuncsCleanup:
2394 * @ctxt: the XPath context
2395 *
2396 * Cleanup the XPath context data associated to registered functions
2397 */
2398void
2399xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
2400 if (ctxt == NULL)
2401 return;
2402
2403 xmlHashFree(ctxt->funcHash, NULL);
2404 ctxt->funcHash = NULL;
2405}
2406
2407/************************************************************************
2408 * *
2409 * Routines to handle Variable *
2410 * *
2411 ************************************************************************/
2412
2413/**
2414 * xmlXPathRegisterVariable:
2415 * @ctxt: the XPath context
2416 * @name: the variable name
2417 * @value: the variable value or NULL
2418 *
2419 * Register a new variable value. If @value is NULL it unregisters
2420 * the variable
2421 *
2422 * Returns 0 in case of success, -1 in case of error
2423 */
2424int
2425xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
2426 xmlXPathObjectPtr value) {
2427 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
2428}
2429
2430/**
2431 * xmlXPathRegisterVariableNS:
2432 * @ctxt: the XPath context
2433 * @name: the variable name
2434 * @ns_uri: the variable namespace URI
2435 * @value: the variable value or NULL
2436 *
2437 * Register a new variable value. If @value is NULL it unregisters
2438 * the variable
2439 *
2440 * Returns 0 in case of success, -1 in case of error
2441 */
2442int
2443xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2444 const xmlChar *ns_uri,
2445 xmlXPathObjectPtr value) {
2446 if (ctxt == NULL)
2447 return(-1);
2448 if (name == NULL)
2449 return(-1);
2450
2451 if (ctxt->varHash == NULL)
2452 ctxt->varHash = xmlHashCreate(0);
2453 if (ctxt->varHash == NULL)
2454 return(-1);
2455 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
2456 (void *) value,
2457 (xmlHashDeallocator)xmlXPathFreeObject));
2458}
2459
2460/**
2461 * xmlXPathRegisterVariableLookup:
2462 * @ctxt: the XPath context
2463 * @f: the lookup function
2464 * @data: the lookup data
2465 *
2466 * register an external mechanism to do variable lookup
2467 */
2468void
2469xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
2470 xmlXPathVariableLookupFunc f, void *data) {
2471 if (ctxt == NULL)
2472 return;
2473 ctxt->varLookupFunc = (void *) f;
2474 ctxt->varLookupData = data;
2475}
2476
2477/**
2478 * xmlXPathVariableLookup:
2479 * @ctxt: the XPath context
2480 * @name: the variable name
2481 *
2482 * Search in the Variable array of the context for the given
2483 * variable value.
2484 *
Daniel Veillard73c9c042001-07-05 20:02:54 +00002485 * Returns a copy of the value or NULL if not found
Owen Taylor3473f882001-02-23 17:55:21 +00002486 */
2487xmlXPathObjectPtr
2488xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
2489 if (ctxt == NULL)
2490 return(NULL);
2491
2492 if (ctxt->varLookupFunc != NULL) {
2493 xmlXPathObjectPtr ret;
2494
2495 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
2496 (ctxt->varLookupData, name, NULL);
2497 if (ret != NULL) return(ret);
2498 }
2499 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
2500}
2501
2502/**
2503 * xmlXPathVariableLookupNS:
2504 * @ctxt: the XPath context
2505 * @name: the variable name
2506 * @ns_uri: the variable namespace URI
2507 *
2508 * Search in the Variable array of the context for the given
Daniel Veillard73c9c042001-07-05 20:02:54 +00002509 * variable value.
Owen Taylor3473f882001-02-23 17:55:21 +00002510 *
Daniel Veillard73c9c042001-07-05 20:02:54 +00002511 * Returns the a copy of the value or NULL if not found
Owen Taylor3473f882001-02-23 17:55:21 +00002512 */
2513xmlXPathObjectPtr
2514xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2515 const xmlChar *ns_uri) {
2516 if (ctxt == NULL)
2517 return(NULL);
2518
2519 if (ctxt->varLookupFunc != NULL) {
2520 xmlXPathObjectPtr ret;
2521
2522 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
2523 (ctxt->varLookupData, name, ns_uri);
2524 if (ret != NULL) return(ret);
2525 }
2526
2527 if (ctxt->varHash == NULL)
2528 return(NULL);
2529 if (name == NULL)
2530 return(NULL);
2531
Daniel Veillard8c357d52001-07-03 23:43:33 +00002532 return(xmlXPathObjectCopy((xmlXPathObjectPtr)
2533 xmlHashLookup2(ctxt->varHash, name, ns_uri)));
Owen Taylor3473f882001-02-23 17:55:21 +00002534}
2535
2536/**
2537 * xmlXPathRegisteredVariablesCleanup:
2538 * @ctxt: the XPath context
2539 *
2540 * Cleanup the XPath context data associated to registered variables
2541 */
2542void
2543xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
2544 if (ctxt == NULL)
2545 return;
2546
Daniel Veillard76d66f42001-05-16 21:05:17 +00002547 xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject);
Owen Taylor3473f882001-02-23 17:55:21 +00002548 ctxt->varHash = NULL;
2549}
2550
2551/**
2552 * xmlXPathRegisterNs:
2553 * @ctxt: the XPath context
2554 * @prefix: the namespace prefix
2555 * @ns_uri: the namespace name
2556 *
2557 * Register a new namespace. If @ns_uri is NULL it unregisters
2558 * the namespace
2559 *
2560 * Returns 0 in case of success, -1 in case of error
2561 */
2562int
2563xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
2564 const xmlChar *ns_uri) {
2565 if (ctxt == NULL)
2566 return(-1);
2567 if (prefix == NULL)
2568 return(-1);
2569
2570 if (ctxt->nsHash == NULL)
2571 ctxt->nsHash = xmlHashCreate(10);
2572 if (ctxt->nsHash == NULL)
2573 return(-1);
2574 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
2575 (xmlHashDeallocator)xmlFree));
2576}
2577
2578/**
2579 * xmlXPathNsLookup:
2580 * @ctxt: the XPath context
2581 * @prefix: the namespace prefix value
2582 *
2583 * Search in the namespace declaration array of the context for the given
2584 * namespace name associated to the given prefix
2585 *
2586 * Returns the value or NULL if not found
2587 */
2588const xmlChar *
2589xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
2590 if (ctxt == NULL)
2591 return(NULL);
2592 if (prefix == NULL)
2593 return(NULL);
2594
2595#ifdef XML_XML_NAMESPACE
2596 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
2597 return(XML_XML_NAMESPACE);
2598#endif
2599
Daniel Veillardc8f620b2001-04-30 20:31:33 +00002600 if (ctxt->namespaces != NULL) {
2601 int i;
2602
2603 for (i = 0;i < ctxt->nsNr;i++) {
2604 if ((ctxt->namespaces[i] != NULL) &&
2605 (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
2606 return(ctxt->namespaces[i]->href);
2607 }
2608 }
Owen Taylor3473f882001-02-23 17:55:21 +00002609
2610 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
2611}
2612
2613/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002614 * xmlXPathRegisteredNsCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +00002615 * @ctxt: the XPath context
2616 *
2617 * Cleanup the XPath context data associated to registered variables
2618 */
2619void
2620xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
2621 if (ctxt == NULL)
2622 return;
2623
2624 xmlHashFree(ctxt->nsHash, NULL);
2625 ctxt->nsHash = NULL;
2626}
2627
2628/************************************************************************
2629 * *
2630 * Routines to handle Values *
2631 * *
2632 ************************************************************************/
2633
2634/* Allocations are terrible, one need to optimize all this !!! */
2635
2636/**
2637 * xmlXPathNewFloat:
2638 * @val: the double value
2639 *
2640 * Create a new xmlXPathObjectPtr of type double and of value @val
2641 *
2642 * Returns the newly created object.
2643 */
2644xmlXPathObjectPtr
2645xmlXPathNewFloat(double val) {
2646 xmlXPathObjectPtr ret;
2647
2648 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2649 if (ret == NULL) {
2650 xmlGenericError(xmlGenericErrorContext,
2651 "xmlXPathNewFloat: out of memory\n");
2652 return(NULL);
2653 }
2654 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2655 ret->type = XPATH_NUMBER;
2656 ret->floatval = val;
2657 return(ret);
2658}
2659
2660/**
2661 * xmlXPathNewBoolean:
2662 * @val: the boolean value
2663 *
2664 * Create a new xmlXPathObjectPtr of type boolean and of value @val
2665 *
2666 * Returns the newly created object.
2667 */
2668xmlXPathObjectPtr
2669xmlXPathNewBoolean(int val) {
2670 xmlXPathObjectPtr ret;
2671
2672 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2673 if (ret == NULL) {
2674 xmlGenericError(xmlGenericErrorContext,
2675 "xmlXPathNewBoolean: out of memory\n");
2676 return(NULL);
2677 }
2678 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2679 ret->type = XPATH_BOOLEAN;
2680 ret->boolval = (val != 0);
2681 return(ret);
2682}
2683
2684/**
2685 * xmlXPathNewString:
2686 * @val: the xmlChar * value
2687 *
2688 * Create a new xmlXPathObjectPtr of type string and of value @val
2689 *
2690 * Returns the newly created object.
2691 */
2692xmlXPathObjectPtr
2693xmlXPathNewString(const xmlChar *val) {
2694 xmlXPathObjectPtr ret;
2695
2696 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2697 if (ret == NULL) {
2698 xmlGenericError(xmlGenericErrorContext,
2699 "xmlXPathNewString: out of memory\n");
2700 return(NULL);
2701 }
2702 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2703 ret->type = XPATH_STRING;
2704 if (val != NULL)
2705 ret->stringval = xmlStrdup(val);
2706 else
2707 ret->stringval = xmlStrdup((const xmlChar *)"");
2708 return(ret);
2709}
2710
2711/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002712 * xmlXPathWrapString:
2713 * @val: the xmlChar * value
2714 *
2715 * Wraps the @val string into an XPath object.
2716 *
2717 * Returns the newly created object.
2718 */
2719xmlXPathObjectPtr
2720xmlXPathWrapString (xmlChar *val) {
2721 xmlXPathObjectPtr ret;
2722
2723 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2724 if (ret == NULL) {
2725 xmlGenericError(xmlGenericErrorContext,
2726 "xmlXPathWrapString: out of memory\n");
2727 return(NULL);
2728 }
2729 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2730 ret->type = XPATH_STRING;
2731 ret->stringval = val;
2732 return(ret);
2733}
2734
2735/**
Owen Taylor3473f882001-02-23 17:55:21 +00002736 * xmlXPathNewCString:
2737 * @val: the char * value
2738 *
2739 * Create a new xmlXPathObjectPtr of type string and of value @val
2740 *
2741 * Returns the newly created object.
2742 */
2743xmlXPathObjectPtr
2744xmlXPathNewCString(const char *val) {
2745 xmlXPathObjectPtr ret;
2746
2747 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2748 if (ret == NULL) {
2749 xmlGenericError(xmlGenericErrorContext,
2750 "xmlXPathNewCString: out of memory\n");
2751 return(NULL);
2752 }
2753 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2754 ret->type = XPATH_STRING;
2755 ret->stringval = xmlStrdup(BAD_CAST val);
2756 return(ret);
2757}
2758
2759/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002760 * xmlXPathWrapCString:
2761 * @val: the char * value
2762 *
2763 * Wraps a string into an XPath object.
2764 *
2765 * Returns the newly created object.
2766 */
2767xmlXPathObjectPtr
2768xmlXPathWrapCString (char * val) {
2769 return(xmlXPathWrapString((xmlChar *)(val)));
2770}
2771
2772/**
Thomas Broyerf06a3d82001-07-16 04:52:57 +00002773 * xmlXPathWrapExternal:
2774 * @val: the user data
2775 *
2776 * Wraps the @val data into an XPath object.
2777 *
2778 * Returns the newly created object.
2779 */
2780xmlXPathObjectPtr
2781xmlXPathWrapExternal (void *val) {
2782 xmlXPathObjectPtr ret;
2783
2784 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2785 if (ret == NULL) {
2786 xmlGenericError(xmlGenericErrorContext,
2787 "xmlXPathWrapString: out of memory\n");
2788 return(NULL);
2789 }
2790 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2791 ret->type = XPATH_USERS;
2792 ret->user = val;
2793 return(ret);
2794}
2795
2796/**
Owen Taylor3473f882001-02-23 17:55:21 +00002797 * xmlXPathObjectCopy:
2798 * @val: the original object
2799 *
2800 * allocate a new copy of a given object
2801 *
2802 * Returns the newly created object.
2803 */
2804xmlXPathObjectPtr
2805xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2806 xmlXPathObjectPtr ret;
2807
2808 if (val == NULL)
2809 return(NULL);
2810
2811 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2812 if (ret == NULL) {
2813 xmlGenericError(xmlGenericErrorContext,
2814 "xmlXPathObjectCopy: out of memory\n");
2815 return(NULL);
2816 }
2817 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2818 switch (val->type) {
2819 case XPATH_BOOLEAN:
2820 case XPATH_NUMBER:
2821 case XPATH_POINT:
2822 case XPATH_RANGE:
2823 break;
2824 case XPATH_STRING:
2825 ret->stringval = xmlStrdup(val->stringval);
2826 break;
2827 case XPATH_XSLT_TREE:
2828 if ((val->nodesetval != NULL) &&
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002829 (val->nodesetval->nodeTab != NULL)) {
2830 ret->boolval = 1;
2831 ret->user = xmlCopyNode(val->nodesetval->nodeTab[0], 1);
Owen Taylor3473f882001-02-23 17:55:21 +00002832 ret->nodesetval = xmlXPathNodeSetCreate(
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002833 (xmlNodePtr) ret->user);
2834 } else
Owen Taylor3473f882001-02-23 17:55:21 +00002835 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002836 /* Deallocate the copied tree value */
Owen Taylor3473f882001-02-23 17:55:21 +00002837 break;
2838 case XPATH_NODESET:
2839 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002840 /* Do not deallocate the copied tree value */
2841 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002842 break;
2843 case XPATH_LOCATIONSET:
2844#ifdef LIBXML_XPTR_ENABLED
2845 {
2846 xmlLocationSetPtr loc = val->user;
2847 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2848 break;
2849 }
2850#endif
2851 case XPATH_UNDEFINED:
2852 case XPATH_USERS:
2853 xmlGenericError(xmlGenericErrorContext,
2854 "xmlXPathObjectCopy: unsupported type %d\n",
2855 val->type);
2856 break;
2857 }
2858 return(ret);
2859}
2860
2861/**
2862 * xmlXPathFreeObject:
2863 * @obj: the object to free
2864 *
2865 * Free up an xmlXPathObjectPtr object.
2866 */
2867void
2868xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2869 if (obj == NULL) return;
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002870 if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
Daniel Veillard77851712001-02-27 21:54:07 +00002871 if (obj->boolval) {
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002872 if (obj->user != NULL) {
2873 xmlFreeNodeList((xmlNodePtr) obj->user);
2874 xmlXPathFreeNodeSet(obj->nodesetval);
2875 } else if (obj->nodesetval != NULL)
Daniel Veillard77851712001-02-27 21:54:07 +00002876 xmlXPathFreeValueTree(obj->nodesetval);
2877 } else {
2878 if (obj->nodesetval != NULL)
2879 xmlXPathFreeNodeSet(obj->nodesetval);
2880 }
Owen Taylor3473f882001-02-23 17:55:21 +00002881#ifdef LIBXML_XPTR_ENABLED
2882 } else if (obj->type == XPATH_LOCATIONSET) {
2883 if (obj->user != NULL)
2884 xmlXPtrFreeLocationSet(obj->user);
2885#endif
2886 } else if (obj->type == XPATH_STRING) {
2887 if (obj->stringval != NULL)
2888 xmlFree(obj->stringval);
Owen Taylor3473f882001-02-23 17:55:21 +00002889 }
2890
Owen Taylor3473f882001-02-23 17:55:21 +00002891 xmlFree(obj);
2892}
2893
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002894
2895/************************************************************************
2896 * *
2897 * Type Casting Routines *
2898 * *
2899 ************************************************************************/
2900
2901/**
2902 * xmlXPathCastBooleanToString:
2903 * @val: a boolean
2904 *
2905 * Converts a boolean to its string value.
2906 *
2907 * Returns a newly allocated string.
2908 */
2909xmlChar *
2910xmlXPathCastBooleanToString (int val) {
2911 xmlChar *ret;
2912 if (val)
2913 ret = xmlStrdup((const xmlChar *) "true");
2914 else
2915 ret = xmlStrdup((const xmlChar *) "false");
2916 return(ret);
2917}
2918
2919/**
2920 * xmlXPathCastNumberToString:
2921 * @val: a number
2922 *
2923 * Converts a number to its string value.
2924 *
2925 * Returns a newly allocated string.
2926 */
2927xmlChar *
2928xmlXPathCastNumberToString (double val) {
2929 xmlChar *ret;
2930 switch (isinf(val)) {
2931 case 1:
2932 ret = xmlStrdup((const xmlChar *) "+Infinity");
2933 break;
2934 case -1:
2935 ret = xmlStrdup((const xmlChar *) "-Infinity");
2936 break;
2937 default:
2938 if (isnan(val)) {
2939 ret = xmlStrdup((const xmlChar *) "NaN");
2940 } else {
2941 /* could be improved */
2942 char buf[100];
2943 xmlXPathFormatNumber(val, buf, 100);
2944 ret = xmlStrdup((const xmlChar *) buf);
2945 }
2946 }
2947 return(ret);
2948}
2949
2950/**
2951 * xmlXPathCastNodeToString:
2952 * @node: a node
2953 *
2954 * Converts a node to its string value.
2955 *
2956 * Returns a newly allocated string.
2957 */
2958xmlChar *
2959xmlXPathCastNodeToString (xmlNodePtr node) {
2960 return(xmlNodeGetContent(node));
2961}
2962
2963/**
2964 * xmlXPathCastNodeSetToString:
2965 * @ns: a node-set
2966 *
2967 * Converts a node-set to its string value.
2968 *
2969 * Returns a newly allocated string.
2970 */
2971xmlChar *
2972xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
2973 if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
2974 return(xmlStrdup((const xmlChar *) ""));
2975
2976 xmlXPathNodeSetSort(ns);
2977 return(xmlXPathCastNodeToString(ns->nodeTab[0]));
2978}
2979
2980/**
2981 * xmlXPathCastToString:
2982 * @val: an XPath object
2983 *
2984 * Converts an existing object to its string() equivalent
2985 *
2986 * Returns the string value of the object, NULL in case of error.
2987 * A new string is allocated only if needed (val isn't a
2988 * string object).
2989 */
2990xmlChar *
2991xmlXPathCastToString(xmlXPathObjectPtr val) {
2992 xmlChar *ret = NULL;
2993
2994 if (val == NULL)
2995 return(xmlStrdup((const xmlChar *) ""));
2996 switch (val->type) {
2997 case XPATH_UNDEFINED:
2998#ifdef DEBUG_EXPR
2999 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
3000#endif
3001 ret = xmlStrdup((const xmlChar *) "");
3002 break;
3003 case XPATH_XSLT_TREE:
3004 case XPATH_NODESET:
3005 ret = xmlXPathCastNodeSetToString(val->nodesetval);
3006 break;
3007 case XPATH_STRING:
3008 return(val->stringval);
3009 case XPATH_BOOLEAN:
3010 ret = xmlXPathCastBooleanToString(val->boolval);
3011 break;
3012 case XPATH_NUMBER: {
3013 ret = xmlXPathCastNumberToString(val->floatval);
3014 break;
3015 }
3016 case XPATH_USERS:
3017 case XPATH_POINT:
3018 case XPATH_RANGE:
3019 case XPATH_LOCATIONSET:
3020 TODO
3021 ret = xmlStrdup((const xmlChar *) "");
3022 break;
3023 }
3024 return(ret);
3025}
3026
3027/**
3028 * xmlXPathConvertString:
3029 * @val: an XPath object
3030 *
3031 * Converts an existing object to its string() equivalent
3032 *
3033 * Returns the new object, the old one is freed (or the operation
3034 * is done directly on @val)
3035 */
3036xmlXPathObjectPtr
3037xmlXPathConvertString(xmlXPathObjectPtr val) {
3038 xmlChar *res = NULL;
3039
3040 if (val == NULL)
3041 return(xmlXPathNewCString(""));
3042
3043 switch (val->type) {
3044 case XPATH_UNDEFINED:
3045#ifdef DEBUG_EXPR
3046 xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
3047#endif
3048 break;
3049 case XPATH_XSLT_TREE:
3050 case XPATH_NODESET:
3051 res = xmlXPathCastNodeSetToString(val->nodesetval);
3052 break;
3053 case XPATH_STRING:
3054 return(val);
3055 case XPATH_BOOLEAN:
3056 res = xmlXPathCastBooleanToString(val->boolval);
3057 break;
3058 case XPATH_NUMBER:
3059 res = xmlXPathCastNumberToString(val->floatval);
3060 break;
3061 case XPATH_USERS:
3062 case XPATH_POINT:
3063 case XPATH_RANGE:
3064 case XPATH_LOCATIONSET:
3065 TODO;
3066 break;
3067 }
3068 xmlXPathFreeObject(val);
3069 if (res == NULL)
3070 return(xmlXPathNewCString(""));
3071 return(xmlXPathWrapString(res));
3072}
3073
3074/**
3075 * xmlXPathCastBooleanToNumber:
3076 * @val: a boolean
3077 *
3078 * Converts a boolean to its number value
3079 *
3080 * Returns the number value
3081 */
3082double
3083xmlXPathCastBooleanToNumber(int val) {
3084 if (val)
3085 return(1.0);
3086 return(0.0);
3087}
3088
3089/**
3090 * xmlXPathCastStringToNumber:
3091 * @val: a string
3092 *
3093 * Converts a string to its number value
3094 *
3095 * Returns the number value
3096 */
3097double
3098xmlXPathCastStringToNumber(const xmlChar * val) {
3099 return(xmlXPathStringEvalNumber(val));
3100}
3101
3102/**
3103 * xmlXPathCastNodeToNumber:
3104 * @node: a node
3105 *
3106 * Converts a node to its number value
3107 *
3108 * Returns the number value
3109 */
3110double
3111xmlXPathCastNodeToNumber (xmlNodePtr node) {
3112 xmlChar *strval;
3113 double ret;
3114
3115 if (node == NULL)
3116 return(xmlXPathNAN);
3117 strval = xmlXPathCastNodeToString(node);
3118 if (strval == NULL)
3119 return(xmlXPathNAN);
3120 ret = xmlXPathCastStringToNumber(strval);
3121 xmlFree(strval);
3122
3123 return(ret);
3124}
3125
3126/**
3127 * xmlXPathCastNodeSetToNumber:
3128 * @ns: a node-set
3129 *
3130 * Converts a node-set to its number value
3131 *
3132 * Returns the number value
3133 */
3134double
3135xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
3136 xmlChar *str;
3137 double ret;
3138
3139 if (ns == NULL)
3140 return(xmlXPathNAN);
3141 str = xmlXPathCastNodeSetToString(ns);
3142 ret = xmlXPathCastStringToNumber(str);
3143 xmlFree(str);
3144 return(ret);
3145}
3146
3147/**
3148 * xmlXPathCastToNumber:
3149 * @val: an XPath object
3150 *
3151 * Converts an XPath object to its number value
3152 *
3153 * Returns the number value
3154 */
3155double
3156xmlXPathCastToNumber(xmlXPathObjectPtr val) {
3157 double ret = 0.0;
3158
3159 if (val == NULL)
3160 return(xmlXPathNAN);
3161 switch (val->type) {
3162 case XPATH_UNDEFINED:
3163#ifdef DEGUB_EXPR
3164 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
3165#endif
3166 ret = xmlXPathNAN;
3167 break;
3168 case XPATH_XSLT_TREE:
3169 case XPATH_NODESET:
3170 ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
3171 break;
3172 case XPATH_STRING:
3173 ret = xmlXPathCastStringToNumber(val->stringval);
3174 break;
3175 case XPATH_NUMBER:
3176 ret = val->floatval;
3177 break;
3178 case XPATH_BOOLEAN:
3179 ret = xmlXPathCastBooleanToNumber(val->boolval);
3180 break;
3181 case XPATH_USERS:
3182 case XPATH_POINT:
3183 case XPATH_RANGE:
3184 case XPATH_LOCATIONSET:
3185 TODO;
3186 ret = xmlXPathNAN;
3187 break;
3188 }
3189 return(ret);
3190}
3191
3192/**
3193 * xmlXPathConvertNumber:
3194 * @val: an XPath object
3195 *
3196 * Converts an existing object to its number() equivalent
3197 *
3198 * Returns the new object, the old one is freed (or the operation
3199 * is done directly on @val)
3200 */
3201xmlXPathObjectPtr
3202xmlXPathConvertNumber(xmlXPathObjectPtr val) {
3203 xmlXPathObjectPtr ret;
3204
3205 if (val == NULL)
3206 return(xmlXPathNewFloat(0.0));
3207 if (val->type == XPATH_NUMBER)
3208 return(val);
3209 ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
3210 xmlXPathFreeObject(val);
3211 return(ret);
3212}
3213
3214/**
3215 * xmlXPathCastNumberToBoolean:
3216 * @val: a number
3217 *
3218 * Converts a number to its boolean value
3219 *
3220 * Returns the boolean value
3221 */
3222int
3223xmlXPathCastNumberToBoolean (double val) {
3224 if (isnan(val) || (val == 0.0))
3225 return(0);
3226 return(1);
3227}
3228
3229/**
3230 * xmlXPathCastStringToBoolean:
3231 * @val: a string
3232 *
3233 * Converts a string to its boolean value
3234 *
3235 * Returns the boolean value
3236 */
3237int
3238xmlXPathCastStringToBoolean (const xmlChar *val) {
3239 if ((val == NULL) || (xmlStrlen(val) == 0))
3240 return(0);
3241 return(1);
3242}
3243
3244/**
3245 * xmlXPathCastNodeSetToBoolean:
3246 * @ns: a node-set
3247 *
3248 * Converts a node-set to its boolean value
3249 *
3250 * Returns the boolean value
3251 */
3252int
3253xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
3254 if ((ns == NULL) || (ns->nodeNr == 0))
3255 return(0);
3256 return(1);
3257}
3258
3259/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00003260 * xmlXPathCastToBoolean:
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003261 * @val: an XPath object
3262 *
3263 * Converts an XPath object to its boolean value
3264 *
3265 * Returns the boolean value
3266 */
3267int
3268xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
3269 int ret = 0;
3270
3271 if (val == NULL)
3272 return(0);
3273 switch (val->type) {
3274 case XPATH_UNDEFINED:
3275#ifdef DEBUG_EXPR
3276 xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
3277#endif
3278 ret = 0;
3279 break;
3280 case XPATH_XSLT_TREE:
3281 case XPATH_NODESET:
3282 ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
3283 break;
3284 case XPATH_STRING:
3285 ret = xmlXPathCastStringToBoolean(val->stringval);
3286 break;
3287 case XPATH_NUMBER:
3288 ret = xmlXPathCastNumberToBoolean(val->floatval);
3289 break;
3290 case XPATH_BOOLEAN:
3291 ret = val->boolval;
3292 break;
3293 case XPATH_USERS:
3294 case XPATH_POINT:
3295 case XPATH_RANGE:
3296 case XPATH_LOCATIONSET:
3297 TODO;
3298 ret = 0;
3299 break;
3300 }
3301 return(ret);
3302}
3303
3304
3305/**
3306 * xmlXPathConvertBoolean:
3307 * @val: an XPath object
3308 *
3309 * Converts an existing object to its boolean() equivalent
3310 *
3311 * Returns the new object, the old one is freed (or the operation
3312 * is done directly on @val)
3313 */
3314xmlXPathObjectPtr
3315xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
3316 xmlXPathObjectPtr ret;
3317
3318 if (val == NULL)
3319 return(xmlXPathNewBoolean(0));
3320 if (val->type == XPATH_BOOLEAN)
3321 return(val);
3322 ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
3323 xmlXPathFreeObject(val);
3324 return(ret);
3325}
3326
Owen Taylor3473f882001-02-23 17:55:21 +00003327/************************************************************************
3328 * *
3329 * Routines to handle XPath contexts *
3330 * *
3331 ************************************************************************/
3332
3333/**
3334 * xmlXPathNewContext:
3335 * @doc: the XML document
3336 *
3337 * Create a new xmlXPathContext
3338 *
3339 * Returns the xmlXPathContext just allocated.
3340 */
3341xmlXPathContextPtr
3342xmlXPathNewContext(xmlDocPtr doc) {
3343 xmlXPathContextPtr ret;
3344
3345 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
3346 if (ret == NULL) {
3347 xmlGenericError(xmlGenericErrorContext,
3348 "xmlXPathNewContext: out of memory\n");
3349 return(NULL);
3350 }
3351 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
3352 ret->doc = doc;
3353 ret->node = NULL;
3354
3355 ret->varHash = NULL;
3356
3357 ret->nb_types = 0;
3358 ret->max_types = 0;
3359 ret->types = NULL;
3360
3361 ret->funcHash = xmlHashCreate(0);
3362
3363 ret->nb_axis = 0;
3364 ret->max_axis = 0;
3365 ret->axis = NULL;
3366
3367 ret->nsHash = NULL;
3368 ret->user = NULL;
3369
3370 ret->contextSize = -1;
3371 ret->proximityPosition = -1;
3372
3373 xmlXPathRegisterAllFunctions(ret);
3374
3375 return(ret);
3376}
3377
3378/**
3379 * xmlXPathFreeContext:
3380 * @ctxt: the context to free
3381 *
3382 * Free up an xmlXPathContext
3383 */
3384void
3385xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
3386 xmlXPathRegisteredNsCleanup(ctxt);
3387 xmlXPathRegisteredFuncsCleanup(ctxt);
3388 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00003389 xmlFree(ctxt);
3390}
3391
3392/************************************************************************
3393 * *
3394 * Routines to handle XPath parser contexts *
3395 * *
3396 ************************************************************************/
3397
3398#define CHECK_CTXT(ctxt) \
3399 if (ctxt == NULL) { \
3400 xmlGenericError(xmlGenericErrorContext, \
3401 "%s:%d Internal error: ctxt == NULL\n", \
3402 __FILE__, __LINE__); \
3403 } \
3404
3405
3406#define CHECK_CONTEXT(ctxt) \
3407 if (ctxt == NULL) { \
3408 xmlGenericError(xmlGenericErrorContext, \
3409 "%s:%d Internal error: no context\n", \
3410 __FILE__, __LINE__); \
3411 } \
3412 else if (ctxt->doc == NULL) { \
3413 xmlGenericError(xmlGenericErrorContext, \
3414 "%s:%d Internal error: no document\n", \
3415 __FILE__, __LINE__); \
3416 } \
3417 else if (ctxt->doc->children == NULL) { \
3418 xmlGenericError(xmlGenericErrorContext, \
3419 "%s:%d Internal error: document without root\n", \
3420 __FILE__, __LINE__); \
3421 } \
3422
3423
3424/**
3425 * xmlXPathNewParserContext:
3426 * @str: the XPath expression
3427 * @ctxt: the XPath context
3428 *
3429 * Create a new xmlXPathParserContext
3430 *
3431 * Returns the xmlXPathParserContext just allocated.
3432 */
3433xmlXPathParserContextPtr
3434xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
3435 xmlXPathParserContextPtr ret;
3436
3437 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
3438 if (ret == NULL) {
3439 xmlGenericError(xmlGenericErrorContext,
3440 "xmlXPathNewParserContext: out of memory\n");
3441 return(NULL);
3442 }
3443 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
3444 ret->cur = ret->base = str;
3445 ret->context = ctxt;
3446
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003447 ret->comp = xmlXPathNewCompExpr();
3448 if (ret->comp == NULL) {
3449 xmlFree(ret->valueTab);
3450 xmlFree(ret);
3451 return(NULL);
3452 }
3453
3454 return(ret);
3455}
3456
3457/**
3458 * xmlXPathCompParserContext:
3459 * @comp: the XPath compiled expression
3460 * @ctxt: the XPath context
3461 *
3462 * Create a new xmlXPathParserContext when processing a compiled expression
3463 *
3464 * Returns the xmlXPathParserContext just allocated.
3465 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003466static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003467xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
3468 xmlXPathParserContextPtr ret;
3469
3470 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
3471 if (ret == NULL) {
3472 xmlGenericError(xmlGenericErrorContext,
3473 "xmlXPathNewParserContext: out of memory\n");
3474 return(NULL);
3475 }
3476 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
3477
Owen Taylor3473f882001-02-23 17:55:21 +00003478 /* Allocate the value stack */
3479 ret->valueTab = (xmlXPathObjectPtr *)
3480 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003481 if (ret->valueTab == NULL) {
3482 xmlFree(ret);
3483 xmlGenericError(xmlGenericErrorContext,
3484 "xmlXPathNewParserContext: out of memory\n");
3485 return(NULL);
3486 }
Owen Taylor3473f882001-02-23 17:55:21 +00003487 ret->valueNr = 0;
3488 ret->valueMax = 10;
3489 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003490
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00003491 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003492 ret->comp = comp;
3493
Owen Taylor3473f882001-02-23 17:55:21 +00003494 return(ret);
3495}
3496
3497/**
3498 * xmlXPathFreeParserContext:
3499 * @ctxt: the context to free
3500 *
3501 * Free up an xmlXPathParserContext
3502 */
3503void
3504xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
3505 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00003506 xmlFree(ctxt->valueTab);
3507 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003508 if (ctxt->comp)
3509 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00003510 xmlFree(ctxt);
3511}
3512
3513/************************************************************************
3514 * *
3515 * The implicit core function library *
3516 * *
3517 ************************************************************************/
3518
Owen Taylor3473f882001-02-23 17:55:21 +00003519/**
Daniel Veillardf06307e2001-07-03 10:35:50 +00003520 * xmlXPathNodeStringHash:
3521 * @node: a node pointer
3522 *
3523 * Function computing the beginning of the string value of the node,
3524 * used to speed up comparisons
3525 *
3526 * Returns an int usable as a hash
3527 */
3528static unsigned int
3529xmlXPathNodeValHash(xmlNodePtr node) {
3530 int len = 2;
3531 const xmlChar * string = NULL;
3532 xmlNodePtr tmp = NULL;
3533 unsigned int ret = 0;
3534
3535 if (node == NULL)
3536 return(0);
3537
3538
3539 switch (node->type) {
3540 case XML_COMMENT_NODE:
3541 case XML_PI_NODE:
3542 case XML_CDATA_SECTION_NODE:
3543 case XML_TEXT_NODE:
3544 string = node->content;
3545 if (string == NULL)
3546 return(0);
3547 if (string[0] == 0)
3548 return(0);
3549 return(((unsigned int) string[0]) +
3550 (((unsigned int) string[1]) << 8));
3551 case XML_NAMESPACE_DECL:
3552 string = ((xmlNsPtr)node)->href;
3553 if (string == NULL)
3554 return(0);
3555 if (string[0] == 0)
3556 return(0);
3557 return(((unsigned int) string[0]) +
3558 (((unsigned int) string[1]) << 8));
3559 case XML_ATTRIBUTE_NODE:
3560 tmp = ((xmlAttrPtr) node)->children;
3561 break;
3562 case XML_ELEMENT_NODE:
3563 tmp = node->children;
3564 break;
3565 default:
3566 return(0);
3567 }
3568 while (tmp != NULL) {
3569 switch (tmp->type) {
3570 case XML_COMMENT_NODE:
3571 case XML_PI_NODE:
3572 case XML_CDATA_SECTION_NODE:
3573 case XML_TEXT_NODE:
3574 string = tmp->content;
3575 break;
3576 case XML_NAMESPACE_DECL:
3577 string = ((xmlNsPtr)tmp)->href;
3578 break;
3579 default:
3580 break;
3581 }
3582 if ((string != NULL) && (string[0] != 0)) {
3583 if (string[0] == 0)
3584 return(0);
3585 if (len == 1) {
3586 return(ret + (((unsigned int) string[0]) << 8));
3587 }
3588 if (string[1] == 0) {
3589 len = 1;
3590 ret = (unsigned int) string[0];
3591 } else {
3592 return(((unsigned int) string[0]) +
3593 (((unsigned int) string[1]) << 8));
3594 }
3595 }
3596 /*
3597 * Skip to next node
3598 */
3599 if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
3600 if (tmp->children->type != XML_ENTITY_DECL) {
3601 tmp = tmp->children;
3602 continue;
3603 }
3604 }
3605 if (tmp == node)
3606 break;
3607
3608 if (tmp->next != NULL) {
3609 tmp = tmp->next;
3610 continue;
3611 }
3612
3613 do {
3614 tmp = tmp->parent;
3615 if (tmp == NULL)
3616 break;
3617 if (tmp == node) {
3618 tmp = NULL;
3619 break;
3620 }
3621 if (tmp->next != NULL) {
3622 tmp = tmp->next;
3623 break;
3624 }
3625 } while (tmp != NULL);
3626 }
3627 return(ret);
3628}
3629
3630/**
3631 * xmlXPathStringHash:
3632 * @string: a string
3633 *
3634 * Function computing the beginning of the string value of the node,
3635 * used to speed up comparisons
3636 *
3637 * Returns an int usable as a hash
3638 */
3639static unsigned int
3640xmlXPathStringHash(const xmlChar * string) {
3641 if (string == NULL)
3642 return((unsigned int) 0);
3643 if (string[0] == 0)
3644 return(0);
3645 return(((unsigned int) string[0]) +
3646 (((unsigned int) string[1]) << 8));
3647}
3648
3649/**
Owen Taylor3473f882001-02-23 17:55:21 +00003650 * xmlXPathCompareNodeSetFloat:
3651 * @ctxt: the XPath Parser context
3652 * @inf: less than (1) or greater than (0)
3653 * @strict: is the comparison strict
3654 * @arg: the node set
3655 * @f: the value
3656 *
3657 * Implement the compare operation between a nodeset and a number
3658 * @ns < @val (1, 1, ...
3659 * @ns <= @val (1, 0, ...
3660 * @ns > @val (0, 1, ...
3661 * @ns >= @val (0, 0, ...
3662 *
3663 * If one object to be compared is a node-set and the other is a number,
3664 * then the comparison will be true if and only if there is a node in the
3665 * node-set such that the result of performing the comparison on the number
3666 * to be compared and on the result of converting the string-value of that
3667 * node to a number using the number function is true.
3668 *
3669 * Returns 0 or 1 depending on the results of the test.
3670 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003671static int
Owen Taylor3473f882001-02-23 17:55:21 +00003672xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
3673 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
3674 int i, ret = 0;
3675 xmlNodeSetPtr ns;
3676 xmlChar *str2;
3677
3678 if ((f == NULL) || (arg == NULL) ||
3679 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
3680 xmlXPathFreeObject(arg);
3681 xmlXPathFreeObject(f);
3682 return(0);
3683 }
3684 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00003685 if (ns != NULL) {
3686 for (i = 0;i < ns->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003687 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
Daniel Veillard911f49a2001-04-07 15:39:35 +00003688 if (str2 != NULL) {
3689 valuePush(ctxt,
3690 xmlXPathNewString(str2));
3691 xmlFree(str2);
3692 xmlXPathNumberFunction(ctxt, 1);
3693 valuePush(ctxt, xmlXPathObjectCopy(f));
3694 ret = xmlXPathCompareValues(ctxt, inf, strict);
3695 if (ret)
3696 break;
3697 }
3698 }
Owen Taylor3473f882001-02-23 17:55:21 +00003699 }
3700 xmlXPathFreeObject(arg);
3701 xmlXPathFreeObject(f);
3702 return(ret);
3703}
3704
3705/**
3706 * xmlXPathCompareNodeSetString:
3707 * @ctxt: the XPath Parser context
3708 * @inf: less than (1) or greater than (0)
3709 * @strict: is the comparison strict
3710 * @arg: the node set
3711 * @s: the value
3712 *
3713 * Implement the compare operation between a nodeset and a string
3714 * @ns < @val (1, 1, ...
3715 * @ns <= @val (1, 0, ...
3716 * @ns > @val (0, 1, ...
3717 * @ns >= @val (0, 0, ...
3718 *
3719 * If one object to be compared is a node-set and the other is a string,
3720 * then the comparison will be true if and only if there is a node in
3721 * the node-set such that the result of performing the comparison on the
3722 * string-value of the node and the other string is true.
3723 *
3724 * Returns 0 or 1 depending on the results of the test.
3725 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003726static int
Owen Taylor3473f882001-02-23 17:55:21 +00003727xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
3728 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
3729 int i, ret = 0;
3730 xmlNodeSetPtr ns;
3731 xmlChar *str2;
3732
3733 if ((s == NULL) || (arg == NULL) ||
3734 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
3735 xmlXPathFreeObject(arg);
3736 xmlXPathFreeObject(s);
3737 return(0);
3738 }
3739 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00003740 if (ns != NULL) {
3741 for (i = 0;i < ns->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003742 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
Daniel Veillard911f49a2001-04-07 15:39:35 +00003743 if (str2 != NULL) {
3744 valuePush(ctxt,
3745 xmlXPathNewString(str2));
3746 xmlFree(str2);
3747 valuePush(ctxt, xmlXPathObjectCopy(s));
3748 ret = xmlXPathCompareValues(ctxt, inf, strict);
3749 if (ret)
3750 break;
3751 }
3752 }
Owen Taylor3473f882001-02-23 17:55:21 +00003753 }
3754 xmlXPathFreeObject(arg);
3755 xmlXPathFreeObject(s);
3756 return(ret);
3757}
3758
3759/**
3760 * xmlXPathCompareNodeSets:
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003761 * @inf: less than (1) or greater than (0)
Owen Taylor3473f882001-02-23 17:55:21 +00003762 * @strict: is the comparison strict
3763 * @arg1: the fist node set object
3764 * @arg2: the second node set object
3765 *
3766 * Implement the compare operation on nodesets:
3767 *
3768 * If both objects to be compared are node-sets, then the comparison
3769 * will be true if and only if there is a node in the first node-set
3770 * and a node in the second node-set such that the result of performing
3771 * the comparison on the string-values of the two nodes is true.
3772 * ....
3773 * When neither object to be compared is a node-set and the operator
3774 * is <=, <, >= or >, then the objects are compared by converting both
3775 * objects to numbers and comparing the numbers according to IEEE 754.
3776 * ....
3777 * The number function converts its argument to a number as follows:
3778 * - a string that consists of optional whitespace followed by an
3779 * optional minus sign followed by a Number followed by whitespace
3780 * is converted to the IEEE 754 number that is nearest (according
3781 * to the IEEE 754 round-to-nearest rule) to the mathematical value
3782 * represented by the string; any other string is converted to NaN
3783 *
3784 * Conclusion all nodes need to be converted first to their string value
3785 * and then the comparison must be done when possible
3786 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003787static int
3788xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00003789 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
3790 int i, j, init = 0;
3791 double val1;
3792 double *values2;
3793 int ret = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00003794 xmlNodeSetPtr ns1;
3795 xmlNodeSetPtr ns2;
3796
3797 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00003798 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
3799 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003800 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003801 }
Owen Taylor3473f882001-02-23 17:55:21 +00003802 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00003803 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
3804 xmlXPathFreeObject(arg1);
3805 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003806 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003807 }
Owen Taylor3473f882001-02-23 17:55:21 +00003808
3809 ns1 = arg1->nodesetval;
3810 ns2 = arg2->nodesetval;
3811
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003812 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003813 xmlXPathFreeObject(arg1);
3814 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003815 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003816 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003817 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003818 xmlXPathFreeObject(arg1);
3819 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003820 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003821 }
Owen Taylor3473f882001-02-23 17:55:21 +00003822
3823 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
3824 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003825 xmlXPathFreeObject(arg1);
3826 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003827 return(0);
3828 }
3829 for (i = 0;i < ns1->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003830 val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00003831 if (isnan(val1))
3832 continue;
3833 for (j = 0;j < ns2->nodeNr;j++) {
3834 if (init == 0) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003835 values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
Owen Taylor3473f882001-02-23 17:55:21 +00003836 }
3837 if (isnan(values2[j]))
3838 continue;
3839 if (inf && strict)
3840 ret = (val1 < values2[j]);
3841 else if (inf && !strict)
3842 ret = (val1 <= values2[j]);
3843 else if (!inf && strict)
3844 ret = (val1 > values2[j]);
3845 else if (!inf && !strict)
3846 ret = (val1 >= values2[j]);
3847 if (ret)
3848 break;
3849 }
3850 if (ret)
3851 break;
3852 init = 1;
3853 }
3854 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003855 xmlXPathFreeObject(arg1);
3856 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003857 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00003858}
3859
3860/**
3861 * xmlXPathCompareNodeSetValue:
3862 * @ctxt: the XPath Parser context
3863 * @inf: less than (1) or greater than (0)
3864 * @strict: is the comparison strict
3865 * @arg: the node set
3866 * @val: the value
3867 *
3868 * Implement the compare operation between a nodeset and a value
3869 * @ns < @val (1, 1, ...
3870 * @ns <= @val (1, 0, ...
3871 * @ns > @val (0, 1, ...
3872 * @ns >= @val (0, 0, ...
3873 *
3874 * If one object to be compared is a node-set and the other is a boolean,
3875 * then the comparison will be true if and only if the result of performing
3876 * the comparison on the boolean and on the result of converting
3877 * the node-set to a boolean using the boolean function is true.
3878 *
3879 * Returns 0 or 1 depending on the results of the test.
3880 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003881static int
Owen Taylor3473f882001-02-23 17:55:21 +00003882xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
3883 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
3884 if ((val == NULL) || (arg == NULL) ||
3885 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3886 return(0);
3887
3888 switch(val->type) {
3889 case XPATH_NUMBER:
3890 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
3891 case XPATH_NODESET:
3892 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003893 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00003894 case XPATH_STRING:
3895 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
3896 case XPATH_BOOLEAN:
3897 valuePush(ctxt, arg);
3898 xmlXPathBooleanFunction(ctxt, 1);
3899 valuePush(ctxt, val);
3900 return(xmlXPathCompareValues(ctxt, inf, strict));
3901 default:
3902 TODO
3903 return(0);
3904 }
3905 return(0);
3906}
3907
3908/**
3909 * xmlXPathEqualNodeSetString
3910 * @arg: the nodeset object argument
3911 * @str: the string to compare to.
3912 *
3913 * Implement the equal operation on XPath objects content: @arg1 == @arg2
3914 * If one object to be compared is a node-set and the other is a string,
3915 * then the comparison will be true if and only if there is a node in
3916 * the node-set such that the result of performing the comparison on the
3917 * string-value of the node and the other string is true.
3918 *
3919 * Returns 0 or 1 depending on the results of the test.
3920 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003921static int
Daniel Veillardf06307e2001-07-03 10:35:50 +00003922xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str)
3923{
Owen Taylor3473f882001-02-23 17:55:21 +00003924 int i;
3925 xmlNodeSetPtr ns;
3926 xmlChar *str2;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003927 unsigned int hash;
Owen Taylor3473f882001-02-23 17:55:21 +00003928
3929 if ((str == NULL) || (arg == NULL) ||
Daniel Veillardf06307e2001-07-03 10:35:50 +00003930 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3931 return (0);
Owen Taylor3473f882001-02-23 17:55:21 +00003932 ns = arg->nodesetval;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003933 hash = xmlXPathStringHash(str);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003934 if (ns == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00003935 return (0);
Daniel Veillard73c9c042001-07-05 20:02:54 +00003936 if (ns->nodeNr <= 0) {
3937 if (hash == 0)
3938 return(1);
3939 return(0);
3940 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00003941 for (i = 0; i < ns->nodeNr; i++) {
3942 if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
3943 str2 = xmlNodeGetContent(ns->nodeTab[i]);
3944 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
3945 xmlFree(str2);
3946 return (1);
3947 }
3948 if (str2 != NULL)
3949 xmlFree(str2);
3950 }
Owen Taylor3473f882001-02-23 17:55:21 +00003951 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00003952 return (0);
Owen Taylor3473f882001-02-23 17:55:21 +00003953}
3954
3955/**
3956 * xmlXPathEqualNodeSetFloat
3957 * @arg: the nodeset object argument
3958 * @f: the float to compare to
3959 *
3960 * Implement the equal operation on XPath objects content: @arg1 == @arg2
3961 * If one object to be compared is a node-set and the other is a number,
3962 * then the comparison will be true if and only if there is a node in
3963 * the node-set such that the result of performing the comparison on the
3964 * number to be compared and on the result of converting the string-value
3965 * of that node to a number using the number function is true.
3966 *
3967 * Returns 0 or 1 depending on the results of the test.
3968 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003969static int
Owen Taylor3473f882001-02-23 17:55:21 +00003970xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
3971 char buf[100] = "";
3972
3973 if ((arg == NULL) ||
3974 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3975 return(0);
3976
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003977 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00003978 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
3979}
3980
3981
3982/**
3983 * xmlXPathEqualNodeSets
3984 * @arg1: first nodeset object argument
3985 * @arg2: second nodeset object argument
3986 *
3987 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
3988 * If both objects to be compared are node-sets, then the comparison
3989 * will be true if and only if there is a node in the first node-set and
3990 * a node in the second node-set such that the result of performing the
3991 * comparison on the string-values of the two nodes is true.
3992 *
3993 * (needless to say, this is a costly operation)
3994 *
3995 * Returns 0 or 1 depending on the results of the test.
3996 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003997static int
Owen Taylor3473f882001-02-23 17:55:21 +00003998xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
3999 int i, j;
Daniel Veillardf06307e2001-07-03 10:35:50 +00004000 unsigned int *hashs1;
4001 unsigned int *hashs2;
Owen Taylor3473f882001-02-23 17:55:21 +00004002 xmlChar **values1;
4003 xmlChar **values2;
4004 int ret = 0;
4005 xmlNodeSetPtr ns1;
4006 xmlNodeSetPtr ns2;
4007
4008 if ((arg1 == NULL) ||
4009 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
4010 return(0);
4011 if ((arg2 == NULL) ||
4012 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
4013 return(0);
4014
4015 ns1 = arg1->nodesetval;
4016 ns2 = arg2->nodesetval;
4017
Daniel Veillard911f49a2001-04-07 15:39:35 +00004018 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00004019 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00004020 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00004021 return(0);
4022
4023 /*
4024 * check if there is a node pertaining to both sets
4025 */
4026 for (i = 0;i < ns1->nodeNr;i++)
4027 for (j = 0;j < ns2->nodeNr;j++)
4028 if (ns1->nodeTab[i] == ns2->nodeTab[j])
4029 return(1);
4030
4031 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
4032 if (values1 == NULL)
4033 return(0);
Daniel Veillardf06307e2001-07-03 10:35:50 +00004034 hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
4035 if (hashs1 == NULL) {
4036 xmlFree(values1);
4037 return(0);
4038 }
Owen Taylor3473f882001-02-23 17:55:21 +00004039 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
4040 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
4041 if (values2 == NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00004042 xmlFree(hashs1);
Owen Taylor3473f882001-02-23 17:55:21 +00004043 xmlFree(values1);
4044 return(0);
4045 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00004046 hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
4047 if (hashs2 == NULL) {
4048 xmlFree(hashs1);
4049 xmlFree(values1);
4050 xmlFree(values2);
4051 return(0);
4052 }
Owen Taylor3473f882001-02-23 17:55:21 +00004053 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
4054 for (i = 0;i < ns1->nodeNr;i++) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00004055 hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00004056 for (j = 0;j < ns2->nodeNr;j++) {
4057 if (i == 0)
Daniel Veillardf06307e2001-07-03 10:35:50 +00004058 hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
4059 if (hashs1[i] == hashs2[j]) {
4060 if (values1[i] == NULL)
4061 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
4062 if (values2[j] == NULL)
4063 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
4064 ret = xmlStrEqual(values1[i], values2[j]);
4065 if (ret)
4066 break;
4067 }
Owen Taylor3473f882001-02-23 17:55:21 +00004068 }
4069 if (ret)
4070 break;
4071 }
4072 for (i = 0;i < ns1->nodeNr;i++)
4073 if (values1[i] != NULL)
4074 xmlFree(values1[i]);
4075 for (j = 0;j < ns2->nodeNr;j++)
4076 if (values2[j] != NULL)
4077 xmlFree(values2[j]);
4078 xmlFree(values1);
4079 xmlFree(values2);
Daniel Veillardf06307e2001-07-03 10:35:50 +00004080 xmlFree(hashs1);
4081 xmlFree(hashs2);
Owen Taylor3473f882001-02-23 17:55:21 +00004082 return(ret);
4083}
4084
4085/**
4086 * xmlXPathEqualValues:
4087 * @ctxt: the XPath Parser context
4088 *
4089 * Implement the equal operation on XPath objects content: @arg1 == @arg2
4090 *
4091 * Returns 0 or 1 depending on the results of the test.
4092 */
4093int
4094xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
4095 xmlXPathObjectPtr arg1, arg2;
4096 int ret = 0;
4097
4098 arg1 = valuePop(ctxt);
4099 if (arg1 == NULL)
4100 XP_ERROR0(XPATH_INVALID_OPERAND);
4101
4102 arg2 = valuePop(ctxt);
4103 if (arg2 == NULL) {
4104 xmlXPathFreeObject(arg1);
4105 XP_ERROR0(XPATH_INVALID_OPERAND);
4106 }
4107
4108 if (arg1 == arg2) {
4109#ifdef DEBUG_EXPR
4110 xmlGenericError(xmlGenericErrorContext,
4111 "Equal: by pointer\n");
4112#endif
4113 return(1);
4114 }
4115
4116 switch (arg1->type) {
4117 case XPATH_UNDEFINED:
4118#ifdef DEBUG_EXPR
4119 xmlGenericError(xmlGenericErrorContext,
4120 "Equal: undefined\n");
4121#endif
4122 break;
4123 case XPATH_XSLT_TREE:
4124 case XPATH_NODESET:
4125 switch (arg2->type) {
4126 case XPATH_UNDEFINED:
4127#ifdef DEBUG_EXPR
4128 xmlGenericError(xmlGenericErrorContext,
4129 "Equal: undefined\n");
4130#endif
4131 break;
4132 case XPATH_XSLT_TREE:
4133 case XPATH_NODESET:
4134 ret = xmlXPathEqualNodeSets(arg1, arg2);
4135 break;
4136 case XPATH_BOOLEAN:
4137 if ((arg1->nodesetval == NULL) ||
4138 (arg1->nodesetval->nodeNr == 0)) ret = 0;
4139 else
4140 ret = 1;
4141 ret = (ret == arg2->boolval);
4142 break;
4143 case XPATH_NUMBER:
4144 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
4145 break;
4146 case XPATH_STRING:
4147 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
4148 break;
4149 case XPATH_USERS:
4150 case XPATH_POINT:
4151 case XPATH_RANGE:
4152 case XPATH_LOCATIONSET:
4153 TODO
4154 break;
4155 }
4156 break;
4157 case XPATH_BOOLEAN:
4158 switch (arg2->type) {
4159 case XPATH_UNDEFINED:
4160#ifdef DEBUG_EXPR
4161 xmlGenericError(xmlGenericErrorContext,
4162 "Equal: undefined\n");
4163#endif
4164 break;
4165 case XPATH_NODESET:
4166 case XPATH_XSLT_TREE:
4167 if ((arg2->nodesetval == NULL) ||
4168 (arg2->nodesetval->nodeNr == 0)) ret = 0;
4169 else
4170 ret = 1;
4171 break;
4172 case XPATH_BOOLEAN:
4173#ifdef DEBUG_EXPR
4174 xmlGenericError(xmlGenericErrorContext,
4175 "Equal: %d boolean %d \n",
4176 arg1->boolval, arg2->boolval);
4177#endif
4178 ret = (arg1->boolval == arg2->boolval);
4179 break;
4180 case XPATH_NUMBER:
4181 if (arg2->floatval) ret = 1;
4182 else ret = 0;
4183 ret = (arg1->boolval == ret);
4184 break;
4185 case XPATH_STRING:
4186 if ((arg2->stringval == NULL) ||
4187 (arg2->stringval[0] == 0)) ret = 0;
4188 else
4189 ret = 1;
4190 ret = (arg1->boolval == ret);
4191 break;
4192 case XPATH_USERS:
4193 case XPATH_POINT:
4194 case XPATH_RANGE:
4195 case XPATH_LOCATIONSET:
4196 TODO
4197 break;
4198 }
4199 break;
4200 case XPATH_NUMBER:
4201 switch (arg2->type) {
4202 case XPATH_UNDEFINED:
4203#ifdef DEBUG_EXPR
4204 xmlGenericError(xmlGenericErrorContext,
4205 "Equal: undefined\n");
4206#endif
4207 break;
4208 case XPATH_NODESET:
4209 case XPATH_XSLT_TREE:
4210 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
4211 break;
4212 case XPATH_BOOLEAN:
4213 if (arg1->floatval) ret = 1;
4214 else ret = 0;
4215 ret = (arg2->boolval == ret);
4216 break;
4217 case XPATH_STRING:
4218 valuePush(ctxt, arg2);
4219 xmlXPathNumberFunction(ctxt, 1);
4220 arg2 = valuePop(ctxt);
4221 /* no break on purpose */
4222 case XPATH_NUMBER:
4223 ret = (arg1->floatval == arg2->floatval);
4224 break;
4225 case XPATH_USERS:
4226 case XPATH_POINT:
4227 case XPATH_RANGE:
4228 case XPATH_LOCATIONSET:
4229 TODO
4230 break;
4231 }
4232 break;
4233 case XPATH_STRING:
4234 switch (arg2->type) {
4235 case XPATH_UNDEFINED:
4236#ifdef DEBUG_EXPR
4237 xmlGenericError(xmlGenericErrorContext,
4238 "Equal: undefined\n");
4239#endif
4240 break;
4241 case XPATH_NODESET:
4242 case XPATH_XSLT_TREE:
4243 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
4244 break;
4245 case XPATH_BOOLEAN:
4246 if ((arg1->stringval == NULL) ||
4247 (arg1->stringval[0] == 0)) ret = 0;
4248 else
4249 ret = 1;
4250 ret = (arg2->boolval == ret);
4251 break;
4252 case XPATH_STRING:
4253 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
4254 break;
4255 case XPATH_NUMBER:
4256 valuePush(ctxt, arg1);
4257 xmlXPathNumberFunction(ctxt, 1);
4258 arg1 = valuePop(ctxt);
4259 ret = (arg1->floatval == arg2->floatval);
4260 break;
4261 case XPATH_USERS:
4262 case XPATH_POINT:
4263 case XPATH_RANGE:
4264 case XPATH_LOCATIONSET:
4265 TODO
4266 break;
4267 }
4268 break;
4269 case XPATH_USERS:
4270 case XPATH_POINT:
4271 case XPATH_RANGE:
4272 case XPATH_LOCATIONSET:
4273 TODO
4274 break;
4275 }
4276 xmlXPathFreeObject(arg1);
4277 xmlXPathFreeObject(arg2);
4278 return(ret);
4279}
4280
4281
4282/**
4283 * xmlXPathCompareValues:
4284 * @ctxt: the XPath Parser context
4285 * @inf: less than (1) or greater than (0)
4286 * @strict: is the comparison strict
4287 *
4288 * Implement the compare operation on XPath objects:
4289 * @arg1 < @arg2 (1, 1, ...
4290 * @arg1 <= @arg2 (1, 0, ...
4291 * @arg1 > @arg2 (0, 1, ...
4292 * @arg1 >= @arg2 (0, 0, ...
4293 *
4294 * When neither object to be compared is a node-set and the operator is
4295 * <=, <, >=, >, then the objects are compared by converted both objects
4296 * to numbers and comparing the numbers according to IEEE 754. The <
4297 * comparison will be true if and only if the first number is less than the
4298 * second number. The <= comparison will be true if and only if the first
4299 * number is less than or equal to the second number. The > comparison
4300 * will be true if and only if the first number is greater than the second
4301 * number. The >= comparison will be true if and only if the first number
4302 * is greater than or equal to the second number.
4303 *
4304 * Returns 1 if the comparaison succeeded, 0 if it failed
4305 */
4306int
4307xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
4308 int ret = 0;
4309 xmlXPathObjectPtr arg1, arg2;
4310
4311 arg2 = valuePop(ctxt);
4312 if (arg2 == NULL) {
4313 XP_ERROR0(XPATH_INVALID_OPERAND);
4314 }
4315
4316 arg1 = valuePop(ctxt);
4317 if (arg1 == NULL) {
4318 xmlXPathFreeObject(arg2);
4319 XP_ERROR0(XPATH_INVALID_OPERAND);
4320 }
4321
4322 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
4323 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004324 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00004325 } else {
4326 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00004327 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
4328 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00004329 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00004330 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
4331 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00004332 }
4333 }
4334 return(ret);
4335 }
4336
4337 if (arg1->type != XPATH_NUMBER) {
4338 valuePush(ctxt, arg1);
4339 xmlXPathNumberFunction(ctxt, 1);
4340 arg1 = valuePop(ctxt);
4341 }
4342 if (arg1->type != XPATH_NUMBER) {
4343 xmlXPathFreeObject(arg1);
4344 xmlXPathFreeObject(arg2);
4345 XP_ERROR0(XPATH_INVALID_OPERAND);
4346 }
4347 if (arg2->type != XPATH_NUMBER) {
4348 valuePush(ctxt, arg2);
4349 xmlXPathNumberFunction(ctxt, 1);
4350 arg2 = valuePop(ctxt);
4351 }
4352 if (arg2->type != XPATH_NUMBER) {
4353 xmlXPathFreeObject(arg1);
4354 xmlXPathFreeObject(arg2);
4355 XP_ERROR0(XPATH_INVALID_OPERAND);
4356 }
4357 /*
4358 * Add tests for infinity and nan
4359 * => feedback on 3.4 for Inf and NaN
4360 */
4361 if (inf && strict)
4362 ret = (arg1->floatval < arg2->floatval);
4363 else if (inf && !strict)
4364 ret = (arg1->floatval <= arg2->floatval);
4365 else if (!inf && strict)
4366 ret = (arg1->floatval > arg2->floatval);
4367 else if (!inf && !strict)
4368 ret = (arg1->floatval >= arg2->floatval);
4369 xmlXPathFreeObject(arg1);
4370 xmlXPathFreeObject(arg2);
4371 return(ret);
4372}
4373
4374/**
4375 * xmlXPathValueFlipSign:
4376 * @ctxt: the XPath Parser context
4377 *
4378 * Implement the unary - operation on an XPath object
4379 * The numeric operators convert their operands to numbers as if
4380 * by calling the number function.
4381 */
4382void
4383xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004384 CAST_TO_NUMBER;
4385 CHECK_TYPE(XPATH_NUMBER);
4386 ctxt->value->floatval = - ctxt->value->floatval;
Owen Taylor3473f882001-02-23 17:55:21 +00004387}
4388
4389/**
4390 * xmlXPathAddValues:
4391 * @ctxt: the XPath Parser context
4392 *
4393 * Implement the add operation on XPath objects:
4394 * The numeric operators convert their operands to numbers as if
4395 * by calling the number function.
4396 */
4397void
4398xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
4399 xmlXPathObjectPtr arg;
4400 double val;
4401
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004402 arg = valuePop(ctxt);
4403 if (arg == NULL)
4404 XP_ERROR(XPATH_INVALID_OPERAND);
4405 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004406 xmlXPathFreeObject(arg);
4407
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004408 CAST_TO_NUMBER;
4409 CHECK_TYPE(XPATH_NUMBER);
4410 ctxt->value->floatval += val;
Owen Taylor3473f882001-02-23 17:55:21 +00004411}
4412
4413/**
4414 * xmlXPathSubValues:
4415 * @ctxt: the XPath Parser context
4416 *
4417 * Implement the substraction operation on XPath objects:
4418 * The numeric operators convert their operands to numbers as if
4419 * by calling the number function.
4420 */
4421void
4422xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
4423 xmlXPathObjectPtr arg;
4424 double val;
4425
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004426 arg = valuePop(ctxt);
4427 if (arg == NULL)
4428 XP_ERROR(XPATH_INVALID_OPERAND);
4429 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004430 xmlXPathFreeObject(arg);
4431
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004432 CAST_TO_NUMBER;
4433 CHECK_TYPE(XPATH_NUMBER);
4434 ctxt->value->floatval -= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004435}
4436
4437/**
4438 * xmlXPathMultValues:
4439 * @ctxt: the XPath Parser context
4440 *
4441 * Implement the multiply operation on XPath objects:
4442 * The numeric operators convert their operands to numbers as if
4443 * by calling the number function.
4444 */
4445void
4446xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
4447 xmlXPathObjectPtr arg;
4448 double val;
4449
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004450 arg = valuePop(ctxt);
4451 if (arg == NULL)
4452 XP_ERROR(XPATH_INVALID_OPERAND);
4453 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004454 xmlXPathFreeObject(arg);
4455
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004456 CAST_TO_NUMBER;
4457 CHECK_TYPE(XPATH_NUMBER);
4458 ctxt->value->floatval *= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004459}
4460
4461/**
4462 * xmlXPathDivValues:
4463 * @ctxt: the XPath Parser context
4464 *
4465 * Implement the div operation on XPath objects @arg1 / @arg2:
4466 * The numeric operators convert their operands to numbers as if
4467 * by calling the number function.
4468 */
4469void
4470xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
4471 xmlXPathObjectPtr arg;
4472 double val;
4473
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004474 arg = valuePop(ctxt);
4475 if (arg == NULL)
4476 XP_ERROR(XPATH_INVALID_OPERAND);
4477 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004478 xmlXPathFreeObject(arg);
4479
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004480 CAST_TO_NUMBER;
4481 CHECK_TYPE(XPATH_NUMBER);
4482 ctxt->value->floatval /= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004483}
4484
4485/**
4486 * xmlXPathModValues:
4487 * @ctxt: the XPath Parser context
4488 *
4489 * Implement the mod operation on XPath objects: @arg1 / @arg2
4490 * The numeric operators convert their operands to numbers as if
4491 * by calling the number function.
4492 */
4493void
4494xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
4495 xmlXPathObjectPtr arg;
4496 int arg1, arg2;
4497
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004498 arg = valuePop(ctxt);
4499 if (arg == NULL)
4500 XP_ERROR(XPATH_INVALID_OPERAND);
4501 arg2 = (int) xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004502 xmlXPathFreeObject(arg);
4503
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004504 CAST_TO_NUMBER;
4505 CHECK_TYPE(XPATH_NUMBER);
4506 arg1 = (int) ctxt->value->floatval;
4507 ctxt->value->floatval = arg1 % arg2;
Owen Taylor3473f882001-02-23 17:55:21 +00004508}
4509
4510/************************************************************************
4511 * *
4512 * The traversal functions *
4513 * *
4514 ************************************************************************/
4515
Owen Taylor3473f882001-02-23 17:55:21 +00004516/*
4517 * A traversal function enumerates nodes along an axis.
4518 * Initially it must be called with NULL, and it indicates
4519 * termination on the axis by returning NULL.
4520 */
4521typedef xmlNodePtr (*xmlXPathTraversalFunction)
4522 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
4523
4524/**
4525 * xmlXPathNextSelf:
4526 * @ctxt: the XPath Parser context
4527 * @cur: the current node in the traversal
4528 *
4529 * Traversal function for the "self" direction
4530 * The self axis contains just the context node itself
4531 *
4532 * Returns the next element following that axis
4533 */
4534xmlNodePtr
4535xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4536 if (cur == NULL)
4537 return(ctxt->context->node);
4538 return(NULL);
4539}
4540
4541/**
4542 * xmlXPathNextChild:
4543 * @ctxt: the XPath Parser context
4544 * @cur: the current node in the traversal
4545 *
4546 * Traversal function for the "child" direction
4547 * The child axis contains the children of the context node in document order.
4548 *
4549 * Returns the next element following that axis
4550 */
4551xmlNodePtr
4552xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4553 if (cur == NULL) {
4554 if (ctxt->context->node == NULL) return(NULL);
4555 switch (ctxt->context->node->type) {
4556 case XML_ELEMENT_NODE:
4557 case XML_TEXT_NODE:
4558 case XML_CDATA_SECTION_NODE:
4559 case XML_ENTITY_REF_NODE:
4560 case XML_ENTITY_NODE:
4561 case XML_PI_NODE:
4562 case XML_COMMENT_NODE:
4563 case XML_NOTATION_NODE:
4564 case XML_DTD_NODE:
4565 return(ctxt->context->node->children);
4566 case XML_DOCUMENT_NODE:
4567 case XML_DOCUMENT_TYPE_NODE:
4568 case XML_DOCUMENT_FRAG_NODE:
4569 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004570#ifdef LIBXML_DOCB_ENABLED
4571 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004572#endif
4573 return(((xmlDocPtr) ctxt->context->node)->children);
4574 case XML_ELEMENT_DECL:
4575 case XML_ATTRIBUTE_DECL:
4576 case XML_ENTITY_DECL:
4577 case XML_ATTRIBUTE_NODE:
4578 case XML_NAMESPACE_DECL:
4579 case XML_XINCLUDE_START:
4580 case XML_XINCLUDE_END:
4581 return(NULL);
4582 }
4583 return(NULL);
4584 }
4585 if ((cur->type == XML_DOCUMENT_NODE) ||
4586 (cur->type == XML_HTML_DOCUMENT_NODE))
4587 return(NULL);
4588 return(cur->next);
4589}
4590
4591/**
4592 * xmlXPathNextDescendant:
4593 * @ctxt: the XPath Parser context
4594 * @cur: the current node in the traversal
4595 *
4596 * Traversal function for the "descendant" direction
4597 * the descendant axis contains the descendants of the context node in document
4598 * order; a descendant is a child or a child of a child and so on.
4599 *
4600 * Returns the next element following that axis
4601 */
4602xmlNodePtr
4603xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4604 if (cur == NULL) {
4605 if (ctxt->context->node == NULL)
4606 return(NULL);
4607 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4608 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4609 return(NULL);
4610
4611 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
4612 return(ctxt->context->doc->children);
4613 return(ctxt->context->node->children);
4614 }
4615
Daniel Veillard567e1b42001-08-01 15:53:47 +00004616 if (cur->children != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004617 if (cur->children->type != XML_ENTITY_DECL)
Daniel Veillard567e1b42001-08-01 15:53:47 +00004618 return(cur->children);
4619 }
4620
4621 if (cur == ctxt->context->node) return(NULL);
4622
Owen Taylor3473f882001-02-23 17:55:21 +00004623 if (cur->next != NULL) return(cur->next);
4624
4625 do {
4626 cur = cur->parent;
4627 if (cur == NULL) return(NULL);
4628 if (cur == ctxt->context->node) return(NULL);
4629 if (cur->next != NULL) {
4630 cur = cur->next;
4631 return(cur);
4632 }
4633 } while (cur != NULL);
4634 return(cur);
4635}
4636
4637/**
4638 * xmlXPathNextDescendantOrSelf:
4639 * @ctxt: the XPath Parser context
4640 * @cur: the current node in the traversal
4641 *
4642 * Traversal function for the "descendant-or-self" direction
4643 * the descendant-or-self axis contains the context node and the descendants
4644 * of the context node in document order; thus the context node is the first
4645 * node on the axis, and the first child of the context node is the second node
4646 * on the axis
4647 *
4648 * Returns the next element following that axis
4649 */
4650xmlNodePtr
4651xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4652 if (cur == NULL) {
4653 if (ctxt->context->node == NULL)
4654 return(NULL);
4655 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4656 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4657 return(NULL);
4658 return(ctxt->context->node);
4659 }
4660
4661 return(xmlXPathNextDescendant(ctxt, cur));
4662}
4663
4664/**
4665 * xmlXPathNextParent:
4666 * @ctxt: the XPath Parser context
4667 * @cur: the current node in the traversal
4668 *
4669 * Traversal function for the "parent" direction
4670 * The parent axis contains the parent of the context node, if there is one.
4671 *
4672 * Returns the next element following that axis
4673 */
4674xmlNodePtr
4675xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4676 /*
4677 * the parent of an attribute or namespace node is the element
4678 * to which the attribute or namespace node is attached
4679 * Namespace handling !!!
4680 */
4681 if (cur == NULL) {
4682 if (ctxt->context->node == NULL) return(NULL);
4683 switch (ctxt->context->node->type) {
4684 case XML_ELEMENT_NODE:
4685 case XML_TEXT_NODE:
4686 case XML_CDATA_SECTION_NODE:
4687 case XML_ENTITY_REF_NODE:
4688 case XML_ENTITY_NODE:
4689 case XML_PI_NODE:
4690 case XML_COMMENT_NODE:
4691 case XML_NOTATION_NODE:
4692 case XML_DTD_NODE:
4693 case XML_ELEMENT_DECL:
4694 case XML_ATTRIBUTE_DECL:
4695 case XML_XINCLUDE_START:
4696 case XML_XINCLUDE_END:
4697 case XML_ENTITY_DECL:
4698 if (ctxt->context->node->parent == NULL)
4699 return((xmlNodePtr) ctxt->context->doc);
4700 return(ctxt->context->node->parent);
4701 case XML_ATTRIBUTE_NODE: {
4702 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
4703
4704 return(att->parent);
4705 }
4706 case XML_DOCUMENT_NODE:
4707 case XML_DOCUMENT_TYPE_NODE:
4708 case XML_DOCUMENT_FRAG_NODE:
4709 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004710#ifdef LIBXML_DOCB_ENABLED
4711 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004712#endif
4713 return(NULL);
4714 case XML_NAMESPACE_DECL:
4715 /*
4716 * TODO !!! may require extending struct _xmlNs with
4717 * parent field
4718 * C.f. Infoset case...
4719 */
4720 return(NULL);
4721 }
4722 }
4723 return(NULL);
4724}
4725
4726/**
4727 * xmlXPathNextAncestor:
4728 * @ctxt: the XPath Parser context
4729 * @cur: the current node in the traversal
4730 *
4731 * Traversal function for the "ancestor" direction
4732 * the ancestor axis contains the ancestors of the context node; the ancestors
4733 * of the context node consist of the parent of context node and the parent's
4734 * parent and so on; the nodes are ordered in reverse document order; thus the
4735 * parent is the first node on the axis, and the parent's parent is the second
4736 * node on the axis
4737 *
4738 * Returns the next element following that axis
4739 */
4740xmlNodePtr
4741xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4742 /*
4743 * the parent of an attribute or namespace node is the element
4744 * to which the attribute or namespace node is attached
4745 * !!!!!!!!!!!!!
4746 */
4747 if (cur == NULL) {
4748 if (ctxt->context->node == NULL) return(NULL);
4749 switch (ctxt->context->node->type) {
4750 case XML_ELEMENT_NODE:
4751 case XML_TEXT_NODE:
4752 case XML_CDATA_SECTION_NODE:
4753 case XML_ENTITY_REF_NODE:
4754 case XML_ENTITY_NODE:
4755 case XML_PI_NODE:
4756 case XML_COMMENT_NODE:
4757 case XML_DTD_NODE:
4758 case XML_ELEMENT_DECL:
4759 case XML_ATTRIBUTE_DECL:
4760 case XML_ENTITY_DECL:
4761 case XML_NOTATION_NODE:
4762 case XML_XINCLUDE_START:
4763 case XML_XINCLUDE_END:
4764 if (ctxt->context->node->parent == NULL)
4765 return((xmlNodePtr) ctxt->context->doc);
4766 return(ctxt->context->node->parent);
4767 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004768 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00004769
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004770 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00004771 }
4772 case XML_DOCUMENT_NODE:
4773 case XML_DOCUMENT_TYPE_NODE:
4774 case XML_DOCUMENT_FRAG_NODE:
4775 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004776#ifdef LIBXML_DOCB_ENABLED
4777 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004778#endif
4779 return(NULL);
4780 case XML_NAMESPACE_DECL:
4781 /*
4782 * TODO !!! may require extending struct _xmlNs with
4783 * parent field
4784 * C.f. Infoset case...
4785 */
4786 return(NULL);
4787 }
4788 return(NULL);
4789 }
4790 if (cur == ctxt->context->doc->children)
4791 return((xmlNodePtr) ctxt->context->doc);
4792 if (cur == (xmlNodePtr) ctxt->context->doc)
4793 return(NULL);
4794 switch (cur->type) {
4795 case XML_ELEMENT_NODE:
4796 case XML_TEXT_NODE:
4797 case XML_CDATA_SECTION_NODE:
4798 case XML_ENTITY_REF_NODE:
4799 case XML_ENTITY_NODE:
4800 case XML_PI_NODE:
4801 case XML_COMMENT_NODE:
4802 case XML_NOTATION_NODE:
4803 case XML_DTD_NODE:
4804 case XML_ELEMENT_DECL:
4805 case XML_ATTRIBUTE_DECL:
4806 case XML_ENTITY_DECL:
4807 case XML_XINCLUDE_START:
4808 case XML_XINCLUDE_END:
4809 return(cur->parent);
4810 case XML_ATTRIBUTE_NODE: {
4811 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
4812
4813 return(att->parent);
4814 }
4815 case XML_DOCUMENT_NODE:
4816 case XML_DOCUMENT_TYPE_NODE:
4817 case XML_DOCUMENT_FRAG_NODE:
4818 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004819#ifdef LIBXML_DOCB_ENABLED
4820 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004821#endif
4822 return(NULL);
4823 case XML_NAMESPACE_DECL:
4824 /*
4825 * TODO !!! may require extending struct _xmlNs with
4826 * parent field
4827 * C.f. Infoset case...
4828 */
4829 return(NULL);
4830 }
4831 return(NULL);
4832}
4833
4834/**
4835 * xmlXPathNextAncestorOrSelf:
4836 * @ctxt: the XPath Parser context
4837 * @cur: the current node in the traversal
4838 *
4839 * Traversal function for the "ancestor-or-self" direction
4840 * he ancestor-or-self axis contains the context node and ancestors of
4841 * the context node in reverse document order; thus the context node is
4842 * the first node on the axis, and the context node's parent the second;
4843 * parent here is defined the same as with the parent axis.
4844 *
4845 * Returns the next element following that axis
4846 */
4847xmlNodePtr
4848xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4849 if (cur == NULL)
4850 return(ctxt->context->node);
4851 return(xmlXPathNextAncestor(ctxt, cur));
4852}
4853
4854/**
4855 * xmlXPathNextFollowingSibling:
4856 * @ctxt: the XPath Parser context
4857 * @cur: the current node in the traversal
4858 *
4859 * Traversal function for the "following-sibling" direction
4860 * The following-sibling axis contains the following siblings of the context
4861 * node in document order.
4862 *
4863 * Returns the next element following that axis
4864 */
4865xmlNodePtr
4866xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4867 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4868 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4869 return(NULL);
4870 if (cur == (xmlNodePtr) ctxt->context->doc)
4871 return(NULL);
4872 if (cur == NULL)
4873 return(ctxt->context->node->next);
4874 return(cur->next);
4875}
4876
4877/**
4878 * xmlXPathNextPrecedingSibling:
4879 * @ctxt: the XPath Parser context
4880 * @cur: the current node in the traversal
4881 *
4882 * Traversal function for the "preceding-sibling" direction
4883 * The preceding-sibling axis contains the preceding siblings of the context
4884 * node in reverse document order; the first preceding sibling is first on the
4885 * axis; the sibling preceding that node is the second on the axis and so on.
4886 *
4887 * Returns the next element following that axis
4888 */
4889xmlNodePtr
4890xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4891 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4892 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4893 return(NULL);
4894 if (cur == (xmlNodePtr) ctxt->context->doc)
4895 return(NULL);
4896 if (cur == NULL)
4897 return(ctxt->context->node->prev);
Daniel Veillardf06307e2001-07-03 10:35:50 +00004898 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
4899 cur = cur->prev;
4900 if (cur == NULL)
4901 return(ctxt->context->node->prev);
4902 }
Owen Taylor3473f882001-02-23 17:55:21 +00004903 return(cur->prev);
4904}
4905
4906/**
4907 * xmlXPathNextFollowing:
4908 * @ctxt: the XPath Parser context
4909 * @cur: the current node in the traversal
4910 *
4911 * Traversal function for the "following" direction
4912 * The following axis contains all nodes in the same document as the context
4913 * node that are after the context node in document order, excluding any
4914 * descendants and excluding attribute nodes and namespace nodes; the nodes
4915 * are ordered in document order
4916 *
4917 * Returns the next element following that axis
4918 */
4919xmlNodePtr
4920xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4921 if (cur != NULL && cur->children != NULL)
4922 return cur->children ;
4923 if (cur == NULL) cur = ctxt->context->node;
4924 if (cur == NULL) return(NULL) ; /* ERROR */
4925 if (cur->next != NULL) return(cur->next) ;
4926 do {
4927 cur = cur->parent;
4928 if (cur == NULL) return(NULL);
4929 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
4930 if (cur->next != NULL) return(cur->next);
4931 } while (cur != NULL);
4932 return(cur);
4933}
4934
4935/*
4936 * xmlXPathIsAncestor:
4937 * @ancestor: the ancestor node
4938 * @node: the current node
4939 *
4940 * Check that @ancestor is a @node's ancestor
4941 *
4942 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
4943 */
4944static int
4945xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
4946 if ((ancestor == NULL) || (node == NULL)) return(0);
4947 /* nodes need to be in the same document */
4948 if (ancestor->doc != node->doc) return(0);
4949 /* avoid searching if ancestor or node is the root node */
4950 if (ancestor == (xmlNodePtr) node->doc) return(1);
4951 if (node == (xmlNodePtr) ancestor->doc) return(0);
4952 while (node->parent != NULL) {
4953 if (node->parent == ancestor)
4954 return(1);
4955 node = node->parent;
4956 }
4957 return(0);
4958}
4959
4960/**
4961 * xmlXPathNextPreceding:
4962 * @ctxt: the XPath Parser context
4963 * @cur: the current node in the traversal
4964 *
4965 * Traversal function for the "preceding" direction
4966 * the preceding axis contains all nodes in the same document as the context
4967 * node that are before the context node in document order, excluding any
4968 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
4969 * ordered in reverse document order
4970 *
4971 * Returns the next element following that axis
4972 */
4973xmlNodePtr
Daniel Veillardf06307e2001-07-03 10:35:50 +00004974xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
4975{
Owen Taylor3473f882001-02-23 17:55:21 +00004976 if (cur == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00004977 cur = ctxt->context->node;
4978 if (cur == NULL)
4979 return (NULL);
4980 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
4981 cur = cur->prev;
Owen Taylor3473f882001-02-23 17:55:21 +00004982 do {
4983 if (cur->prev != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00004984 for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
4985 return (cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004986 }
4987
4988 cur = cur->parent;
Daniel Veillardf06307e2001-07-03 10:35:50 +00004989 if (cur == NULL)
4990 return (NULL);
4991 if (cur == ctxt->context->doc->children)
4992 return (NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004993 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
Daniel Veillardf06307e2001-07-03 10:35:50 +00004994 return (cur);
4995}
4996
4997/**
4998 * xmlXPathNextPrecedingInternal:
4999 * @ctxt: the XPath Parser context
5000 * @cur: the current node in the traversal
5001 *
5002 * Traversal function for the "preceding" direction
5003 * the preceding axis contains all nodes in the same document as the context
5004 * node that are before the context node in document order, excluding any
5005 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
5006 * ordered in reverse document order
5007 * This is a faster implementation but internal only since it requires a
5008 * state kept in the parser context: ctxt->ancestor.
5009 *
5010 * Returns the next element following that axis
5011 */
5012static xmlNodePtr
5013xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
5014 xmlNodePtr cur)
5015{
5016 if (cur == NULL) {
5017 cur = ctxt->context->node;
5018 if (cur == NULL)
5019 return (NULL);
5020 ctxt->ancestor = cur->parent;
5021 }
5022 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
5023 cur = cur->prev;
5024 while (cur->prev == NULL) {
5025 cur = cur->parent;
5026 if (cur == NULL)
5027 return (NULL);
5028 if (cur == ctxt->context->doc->children)
5029 return (NULL);
5030 if (cur != ctxt->ancestor)
5031 return (cur);
5032 ctxt->ancestor = cur->parent;
5033 }
5034 cur = cur->prev;
5035 while (cur->last != NULL)
5036 cur = cur->last;
5037 return (cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005038}
5039
5040/**
5041 * xmlXPathNextNamespace:
5042 * @ctxt: the XPath Parser context
5043 * @cur: the current attribute in the traversal
5044 *
5045 * Traversal function for the "namespace" direction
5046 * the namespace axis contains the namespace nodes of the context node;
5047 * the order of nodes on this axis is implementation-defined; the axis will
5048 * be empty unless the context node is an element
5049 *
5050 * Returns the next element following that axis
5051 */
5052xmlNodePtr
5053xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005054 xmlNodePtr ret;
5055
Owen Taylor3473f882001-02-23 17:55:21 +00005056 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005057 if ((cur == NULL) || (ctxt->context->tmpNsList == NULL)) {
5058 if (ctxt->context->tmpNsList != NULL)
5059 xmlFree(ctxt->context->tmpNsList);
5060 ctxt->context->tmpNsList =
Owen Taylor3473f882001-02-23 17:55:21 +00005061 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005062 if (ctxt->context->tmpNsList == NULL) return(NULL);
5063 ctxt->context->tmpNsNr = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005064 }
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005065 ret = (xmlNodePtr)ctxt->context->tmpNsList[ctxt->context->tmpNsNr++];
5066 if (ret == NULL) {
5067 xmlFree(ctxt->context->tmpNsList);
5068 ctxt->context->tmpNsList = NULL;
5069 }
5070 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00005071}
5072
5073/**
5074 * xmlXPathNextAttribute:
5075 * @ctxt: the XPath Parser context
5076 * @cur: the current attribute in the traversal
5077 *
5078 * Traversal function for the "attribute" direction
5079 * TODO: support DTD inherited default attributes
5080 *
5081 * Returns the next element following that axis
5082 */
5083xmlNodePtr
5084xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00005085 if (ctxt->context->node == NULL)
5086 return(NULL);
5087 if (ctxt->context->node->type != XML_ELEMENT_NODE)
5088 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005089 if (cur == NULL) {
5090 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
5091 return(NULL);
5092 return((xmlNodePtr)ctxt->context->node->properties);
5093 }
5094 return((xmlNodePtr)cur->next);
5095}
5096
5097/************************************************************************
5098 * *
5099 * NodeTest Functions *
5100 * *
5101 ************************************************************************/
5102
Owen Taylor3473f882001-02-23 17:55:21 +00005103#define IS_FUNCTION 200
5104
Owen Taylor3473f882001-02-23 17:55:21 +00005105
5106/************************************************************************
5107 * *
5108 * Implicit tree core function library *
5109 * *
5110 ************************************************************************/
5111
5112/**
5113 * xmlXPathRoot:
5114 * @ctxt: the XPath Parser context
5115 *
5116 * Initialize the context to the root of the document
5117 */
5118void
5119xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
5120 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
5121 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5122}
5123
5124/************************************************************************
5125 * *
5126 * The explicit core function library *
5127 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
5128 * *
5129 ************************************************************************/
5130
5131
5132/**
5133 * xmlXPathLastFunction:
5134 * @ctxt: the XPath Parser context
5135 * @nargs: the number of arguments
5136 *
5137 * Implement the last() XPath function
5138 * number last()
5139 * The last function returns the number of nodes in the context node list.
5140 */
5141void
5142xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5143 CHECK_ARITY(0);
5144 if (ctxt->context->contextSize >= 0) {
5145 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
5146#ifdef DEBUG_EXPR
5147 xmlGenericError(xmlGenericErrorContext,
5148 "last() : %d\n", ctxt->context->contextSize);
5149#endif
5150 } else {
5151 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
5152 }
5153}
5154
5155/**
5156 * xmlXPathPositionFunction:
5157 * @ctxt: the XPath Parser context
5158 * @nargs: the number of arguments
5159 *
5160 * Implement the position() XPath function
5161 * number position()
5162 * The position function returns the position of the context node in the
5163 * context node list. The first position is 1, and so the last positionr
5164 * will be equal to last().
5165 */
5166void
5167xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5168 CHECK_ARITY(0);
5169 if (ctxt->context->proximityPosition >= 0) {
5170 valuePush(ctxt,
5171 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
5172#ifdef DEBUG_EXPR
5173 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
5174 ctxt->context->proximityPosition);
5175#endif
5176 } else {
5177 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
5178 }
5179}
5180
5181/**
5182 * xmlXPathCountFunction:
5183 * @ctxt: the XPath Parser context
5184 * @nargs: the number of arguments
5185 *
5186 * Implement the count() XPath function
5187 * number count(node-set)
5188 */
5189void
5190xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5191 xmlXPathObjectPtr cur;
5192
5193 CHECK_ARITY(1);
5194 if ((ctxt->value == NULL) ||
5195 ((ctxt->value->type != XPATH_NODESET) &&
5196 (ctxt->value->type != XPATH_XSLT_TREE)))
5197 XP_ERROR(XPATH_INVALID_TYPE);
5198 cur = valuePop(ctxt);
5199
Daniel Veillard911f49a2001-04-07 15:39:35 +00005200 if ((cur == NULL) || (cur->nodesetval == NULL))
5201 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillardfe703322001-08-14 12:18:09 +00005202 else if (cur->type == XPATH_NODESET) {
Daniel Veillard911f49a2001-04-07 15:39:35 +00005203 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Daniel Veillardfe703322001-08-14 12:18:09 +00005204 } else {
5205 if ((cur->nodesetval->nodeNr != 1) ||
5206 (cur->nodesetval->nodeTab == NULL)) {
5207 valuePush(ctxt, xmlXPathNewFloat((double) 0));
5208 } else {
5209 xmlNodePtr tmp;
5210 int i = 0;
5211
5212 tmp = cur->nodesetval->nodeTab[0];
5213 if (tmp != NULL) {
5214 tmp = tmp->children;
5215 while (tmp != NULL) {
5216 tmp = tmp->next;
5217 i++;
5218 }
5219 }
5220 valuePush(ctxt, xmlXPathNewFloat((double) i));
5221 }
5222 }
Owen Taylor3473f882001-02-23 17:55:21 +00005223 xmlXPathFreeObject(cur);
5224}
5225
5226/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005227 * xmlXPathGetElementsByIds:
5228 * @doc: the document
5229 * @ids: a whitespace separated list of IDs
5230 *
5231 * Selects elements by their unique ID.
5232 *
5233 * Returns a node-set of selected elements.
5234 */
5235static xmlNodeSetPtr
5236xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
5237 xmlNodeSetPtr ret;
5238 const xmlChar *cur = ids;
5239 xmlChar *ID;
5240 xmlAttrPtr attr;
5241 xmlNodePtr elem = NULL;
5242
5243 ret = xmlXPathNodeSetCreate(NULL);
5244
5245 while (IS_BLANK(*cur)) cur++;
5246 while (*cur != 0) {
5247 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
5248 (*cur == '.') || (*cur == '-') ||
5249 (*cur == '_') || (*cur == ':') ||
5250 (IS_COMBINING(*cur)) ||
5251 (IS_EXTENDER(*cur)))
5252 cur++;
5253
5254 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
5255
5256 ID = xmlStrndup(ids, cur - ids);
5257 attr = xmlGetID(doc, ID);
5258 if (attr != NULL) {
5259 elem = attr->parent;
5260 xmlXPathNodeSetAdd(ret, elem);
5261 }
5262 if (ID != NULL)
5263 xmlFree(ID);
5264
5265 while (IS_BLANK(*cur)) cur++;
5266 ids = cur;
5267 }
5268 return(ret);
5269}
5270
5271/**
Owen Taylor3473f882001-02-23 17:55:21 +00005272 * xmlXPathIdFunction:
5273 * @ctxt: the XPath Parser context
5274 * @nargs: the number of arguments
5275 *
5276 * Implement the id() XPath function
5277 * node-set id(object)
5278 * The id function selects elements by their unique ID
5279 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
5280 * then the result is the union of the result of applying id to the
5281 * string value of each of the nodes in the argument node-set. When the
5282 * argument to id is of any other type, the argument is converted to a
5283 * string as if by a call to the string function; the string is split
5284 * into a whitespace-separated list of tokens (whitespace is any sequence
5285 * of characters matching the production S); the result is a node-set
5286 * containing the elements in the same document as the context node that
5287 * have a unique ID equal to any of the tokens in the list.
5288 */
5289void
5290xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005291 xmlChar *tokens;
5292 xmlNodeSetPtr ret;
5293 xmlXPathObjectPtr obj;
Owen Taylor3473f882001-02-23 17:55:21 +00005294
5295 CHECK_ARITY(1);
5296 obj = valuePop(ctxt);
5297 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
5298 if (obj->type == XPATH_NODESET) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005299 xmlNodeSetPtr ns;
Owen Taylor3473f882001-02-23 17:55:21 +00005300 int i;
5301
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005302 ret = xmlXPathNodeSetCreate(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005303
Daniel Veillard911f49a2001-04-07 15:39:35 +00005304 if (obj->nodesetval != NULL) {
5305 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005306 tokens =
5307 xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
5308 ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
5309 ret = xmlXPathNodeSetMerge(ret, ns);
5310 xmlXPathFreeNodeSet(ns);
5311 if (tokens != NULL)
5312 xmlFree(tokens);
Daniel Veillard911f49a2001-04-07 15:39:35 +00005313 }
Owen Taylor3473f882001-02-23 17:55:21 +00005314 }
5315
5316 xmlXPathFreeObject(obj);
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005317 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005318 return;
5319 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005320 obj = xmlXPathConvertString(obj);
Owen Taylor3473f882001-02-23 17:55:21 +00005321
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005322 ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
5323 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005324
Owen Taylor3473f882001-02-23 17:55:21 +00005325 xmlXPathFreeObject(obj);
5326 return;
5327}
5328
5329/**
5330 * xmlXPathLocalNameFunction:
5331 * @ctxt: the XPath Parser context
5332 * @nargs: the number of arguments
5333 *
5334 * Implement the local-name() XPath function
5335 * string local-name(node-set?)
5336 * The local-name function returns a string containing the local part
5337 * of the name of the node in the argument node-set that is first in
5338 * document order. If the node-set is empty or the first node has no
5339 * name, an empty string is returned. If the argument is omitted it
5340 * defaults to the context node.
5341 */
5342void
5343xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5344 xmlXPathObjectPtr cur;
5345
5346 if (nargs == 0) {
5347 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5348 nargs = 1;
5349 }
5350
5351 CHECK_ARITY(1);
5352 if ((ctxt->value == NULL) ||
5353 ((ctxt->value->type != XPATH_NODESET) &&
5354 (ctxt->value->type != XPATH_XSLT_TREE)))
5355 XP_ERROR(XPATH_INVALID_TYPE);
5356 cur = valuePop(ctxt);
5357
Daniel Veillard911f49a2001-04-07 15:39:35 +00005358 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005359 valuePush(ctxt, xmlXPathNewCString(""));
5360 } else {
5361 int i = 0; /* Should be first in document order !!!!! */
5362 switch (cur->nodesetval->nodeTab[i]->type) {
5363 case XML_ELEMENT_NODE:
5364 case XML_ATTRIBUTE_NODE:
5365 case XML_PI_NODE:
5366 valuePush(ctxt,
5367 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
5368 break;
5369 case XML_NAMESPACE_DECL:
5370 valuePush(ctxt, xmlXPathNewString(
5371 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
5372 break;
5373 default:
5374 valuePush(ctxt, xmlXPathNewCString(""));
5375 }
5376 }
5377 xmlXPathFreeObject(cur);
5378}
5379
5380/**
5381 * xmlXPathNamespaceURIFunction:
5382 * @ctxt: the XPath Parser context
5383 * @nargs: the number of arguments
5384 *
5385 * Implement the namespace-uri() XPath function
5386 * string namespace-uri(node-set?)
5387 * The namespace-uri function returns a string containing the
5388 * namespace URI of the expanded name of the node in the argument
5389 * node-set that is first in document order. If the node-set is empty,
5390 * the first node has no name, or the expanded name has no namespace
5391 * URI, an empty string is returned. If the argument is omitted it
5392 * defaults to the context node.
5393 */
5394void
5395xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5396 xmlXPathObjectPtr cur;
5397
5398 if (nargs == 0) {
5399 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5400 nargs = 1;
5401 }
5402 CHECK_ARITY(1);
5403 if ((ctxt->value == NULL) ||
5404 ((ctxt->value->type != XPATH_NODESET) &&
5405 (ctxt->value->type != XPATH_XSLT_TREE)))
5406 XP_ERROR(XPATH_INVALID_TYPE);
5407 cur = valuePop(ctxt);
5408
Daniel Veillard911f49a2001-04-07 15:39:35 +00005409 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005410 valuePush(ctxt, xmlXPathNewCString(""));
5411 } else {
5412 int i = 0; /* Should be first in document order !!!!! */
5413 switch (cur->nodesetval->nodeTab[i]->type) {
5414 case XML_ELEMENT_NODE:
5415 case XML_ATTRIBUTE_NODE:
5416 if (cur->nodesetval->nodeTab[i]->ns == NULL)
5417 valuePush(ctxt, xmlXPathNewCString(""));
5418 else
5419 valuePush(ctxt, xmlXPathNewString(
5420 cur->nodesetval->nodeTab[i]->ns->href));
5421 break;
5422 default:
5423 valuePush(ctxt, xmlXPathNewCString(""));
5424 }
5425 }
5426 xmlXPathFreeObject(cur);
5427}
5428
5429/**
5430 * xmlXPathNameFunction:
5431 * @ctxt: the XPath Parser context
5432 * @nargs: the number of arguments
5433 *
5434 * Implement the name() XPath function
5435 * string name(node-set?)
5436 * The name function returns a string containing a QName representing
5437 * the name of the node in the argument node-set that is first in documenti
5438 * order. The QName must represent the name with respect to the namespace
5439 * declarations in effect on the node whose name is being represented.
5440 * Typically, this will be the form in which the name occurred in the XML
5441 * source. This need not be the case if there are namespace declarations
5442 * in effect on the node that associate multiple prefixes with the same
5443 * namespace. However, an implementation may include information about
5444 * the original prefix in its representation of nodes; in this case, an
5445 * implementation can ensure that the returned string is always the same
5446 * as the QName used in the XML source. If the argument it omitted it
5447 * defaults to the context node.
5448 * Libxml keep the original prefix so the "real qualified name" used is
5449 * returned.
5450 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005451static void
Daniel Veillard04383752001-07-08 14:27:15 +00005452xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
5453{
Owen Taylor3473f882001-02-23 17:55:21 +00005454 xmlXPathObjectPtr cur;
5455
5456 if (nargs == 0) {
Daniel Veillard04383752001-07-08 14:27:15 +00005457 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5458 nargs = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005459 }
5460
5461 CHECK_ARITY(1);
Daniel Veillard04383752001-07-08 14:27:15 +00005462 if ((ctxt->value == NULL) ||
5463 ((ctxt->value->type != XPATH_NODESET) &&
5464 (ctxt->value->type != XPATH_XSLT_TREE)))
5465 XP_ERROR(XPATH_INVALID_TYPE);
Owen Taylor3473f882001-02-23 17:55:21 +00005466 cur = valuePop(ctxt);
5467
Daniel Veillard911f49a2001-04-07 15:39:35 +00005468 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Daniel Veillard04383752001-07-08 14:27:15 +00005469 valuePush(ctxt, xmlXPathNewCString(""));
Owen Taylor3473f882001-02-23 17:55:21 +00005470 } else {
Daniel Veillard04383752001-07-08 14:27:15 +00005471 int i = 0; /* Should be first in document order !!!!! */
Owen Taylor3473f882001-02-23 17:55:21 +00005472
Daniel Veillard04383752001-07-08 14:27:15 +00005473 switch (cur->nodesetval->nodeTab[i]->type) {
5474 case XML_ELEMENT_NODE:
5475 case XML_ATTRIBUTE_NODE:
5476 if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
5477 (cur->nodesetval->nodeTab[i]->ns->prefix == NULL))
5478 valuePush(ctxt,
5479 xmlXPathNewString(cur->nodesetval->
5480 nodeTab[i]->name));
5481
5482 else {
5483 char name[2000];
5484
5485 snprintf(name, sizeof(name), "%s:%s",
5486 (char *) cur->nodesetval->nodeTab[i]->ns->
5487 prefix,
5488 (char *) cur->nodesetval->nodeTab[i]->name);
5489 name[sizeof(name) - 1] = 0;
5490 valuePush(ctxt, xmlXPathNewCString(name));
5491 }
5492 break;
5493 default:
5494 valuePush(ctxt,
5495 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
5496 xmlXPathLocalNameFunction(ctxt, 1);
5497 }
Owen Taylor3473f882001-02-23 17:55:21 +00005498 }
5499 xmlXPathFreeObject(cur);
5500}
5501
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005502
5503/**
Owen Taylor3473f882001-02-23 17:55:21 +00005504 * xmlXPathStringFunction:
5505 * @ctxt: the XPath Parser context
5506 * @nargs: the number of arguments
5507 *
5508 * Implement the string() XPath function
5509 * string string(object?)
5510 * he string function converts an object to a string as follows:
5511 * - A node-set is converted to a string by returning the value of
5512 * the node in the node-set that is first in document order.
5513 * If the node-set is empty, an empty string is returned.
5514 * - A number is converted to a string as follows
5515 * + NaN is converted to the string NaN
5516 * + positive zero is converted to the string 0
5517 * + negative zero is converted to the string 0
5518 * + positive infinity is converted to the string Infinity
5519 * + negative infinity is converted to the string -Infinity
5520 * + if the number is an integer, the number is represented in
5521 * decimal form as a Number with no decimal point and no leading
5522 * zeros, preceded by a minus sign (-) if the number is negative
5523 * + otherwise, the number is represented in decimal form as a
5524 * Number including a decimal point with at least one digit
5525 * before the decimal point and at least one digit after the
5526 * decimal point, preceded by a minus sign (-) if the number
5527 * is negative; there must be no leading zeros before the decimal
5528 * point apart possibly from the one required digit immediatelyi
5529 * before the decimal point; beyond the one required digit
5530 * after the decimal point there must be as many, but only as
5531 * many, more digits as are needed to uniquely distinguish the
5532 * number from all other IEEE 754 numeric values.
5533 * - The boolean false value is converted to the string false.
5534 * The boolean true value is converted to the string true.
5535 *
5536 * If the argument is omitted, it defaults to a node-set with the
5537 * context node as its only member.
5538 */
5539void
5540xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5541 xmlXPathObjectPtr cur;
5542
5543 if (nargs == 0) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005544 valuePush(ctxt,
5545 xmlXPathWrapString(
5546 xmlXPathCastNodeToString(ctxt->context->node)));
5547 return;
Owen Taylor3473f882001-02-23 17:55:21 +00005548 }
5549
5550 CHECK_ARITY(1);
5551 cur = valuePop(ctxt);
5552 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005553 cur = xmlXPathConvertString(cur);
5554 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005555}
5556
5557/**
5558 * xmlXPathStringLengthFunction:
5559 * @ctxt: the XPath Parser context
5560 * @nargs: the number of arguments
5561 *
5562 * Implement the string-length() XPath function
5563 * number string-length(string?)
5564 * The string-length returns the number of characters in the string
5565 * (see [3.6 Strings]). If the argument is omitted, it defaults to
5566 * the context node converted to a string, in other words the value
5567 * of the context node.
5568 */
5569void
5570xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5571 xmlXPathObjectPtr cur;
5572
5573 if (nargs == 0) {
5574 if (ctxt->context->node == NULL) {
5575 valuePush(ctxt, xmlXPathNewFloat(0));
5576 } else {
5577 xmlChar *content;
5578
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005579 content = xmlXPathCastNodeToString(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005580 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00005581 xmlFree(content);
5582 }
5583 return;
5584 }
5585 CHECK_ARITY(1);
5586 CAST_TO_STRING;
5587 CHECK_TYPE(XPATH_STRING);
5588 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005589 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00005590 xmlXPathFreeObject(cur);
5591}
5592
5593/**
5594 * xmlXPathConcatFunction:
5595 * @ctxt: the XPath Parser context
5596 * @nargs: the number of arguments
5597 *
5598 * Implement the concat() XPath function
5599 * string concat(string, string, string*)
5600 * The concat function returns the concatenation of its arguments.
5601 */
5602void
5603xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5604 xmlXPathObjectPtr cur, newobj;
5605 xmlChar *tmp;
5606
5607 if (nargs < 2) {
5608 CHECK_ARITY(2);
5609 }
5610
5611 CAST_TO_STRING;
5612 cur = valuePop(ctxt);
5613 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
5614 xmlXPathFreeObject(cur);
5615 return;
5616 }
5617 nargs--;
5618
5619 while (nargs > 0) {
5620 CAST_TO_STRING;
5621 newobj = valuePop(ctxt);
5622 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
5623 xmlXPathFreeObject(newobj);
5624 xmlXPathFreeObject(cur);
5625 XP_ERROR(XPATH_INVALID_TYPE);
5626 }
5627 tmp = xmlStrcat(newobj->stringval, cur->stringval);
5628 newobj->stringval = cur->stringval;
5629 cur->stringval = tmp;
5630
5631 xmlXPathFreeObject(newobj);
5632 nargs--;
5633 }
5634 valuePush(ctxt, cur);
5635}
5636
5637/**
5638 * xmlXPathContainsFunction:
5639 * @ctxt: the XPath Parser context
5640 * @nargs: the number of arguments
5641 *
5642 * Implement the contains() XPath function
5643 * boolean contains(string, string)
5644 * The contains function returns true if the first argument string
5645 * contains the second argument string, and otherwise returns false.
5646 */
5647void
5648xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5649 xmlXPathObjectPtr hay, needle;
5650
5651 CHECK_ARITY(2);
5652 CAST_TO_STRING;
5653 CHECK_TYPE(XPATH_STRING);
5654 needle = valuePop(ctxt);
5655 CAST_TO_STRING;
5656 hay = valuePop(ctxt);
5657 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5658 xmlXPathFreeObject(hay);
5659 xmlXPathFreeObject(needle);
5660 XP_ERROR(XPATH_INVALID_TYPE);
5661 }
5662 if (xmlStrstr(hay->stringval, needle->stringval))
5663 valuePush(ctxt, xmlXPathNewBoolean(1));
5664 else
5665 valuePush(ctxt, xmlXPathNewBoolean(0));
5666 xmlXPathFreeObject(hay);
5667 xmlXPathFreeObject(needle);
5668}
5669
5670/**
5671 * xmlXPathStartsWithFunction:
5672 * @ctxt: the XPath Parser context
5673 * @nargs: the number of arguments
5674 *
5675 * Implement the starts-with() XPath function
5676 * boolean starts-with(string, string)
5677 * The starts-with function returns true if the first argument string
5678 * starts with the second argument string, and otherwise returns false.
5679 */
5680void
5681xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5682 xmlXPathObjectPtr hay, needle;
5683 int n;
5684
5685 CHECK_ARITY(2);
5686 CAST_TO_STRING;
5687 CHECK_TYPE(XPATH_STRING);
5688 needle = valuePop(ctxt);
5689 CAST_TO_STRING;
5690 hay = valuePop(ctxt);
5691 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5692 xmlXPathFreeObject(hay);
5693 xmlXPathFreeObject(needle);
5694 XP_ERROR(XPATH_INVALID_TYPE);
5695 }
5696 n = xmlStrlen(needle->stringval);
5697 if (xmlStrncmp(hay->stringval, needle->stringval, n))
5698 valuePush(ctxt, xmlXPathNewBoolean(0));
5699 else
5700 valuePush(ctxt, xmlXPathNewBoolean(1));
5701 xmlXPathFreeObject(hay);
5702 xmlXPathFreeObject(needle);
5703}
5704
5705/**
5706 * xmlXPathSubstringFunction:
5707 * @ctxt: the XPath Parser context
5708 * @nargs: the number of arguments
5709 *
5710 * Implement the substring() XPath function
5711 * string substring(string, number, number?)
5712 * The substring function returns the substring of the first argument
5713 * starting at the position specified in the second argument with
5714 * length specified in the third argument. For example,
5715 * substring("12345",2,3) returns "234". If the third argument is not
5716 * specified, it returns the substring starting at the position specified
5717 * in the second argument and continuing to the end of the string. For
5718 * example, substring("12345",2) returns "2345". More precisely, each
5719 * character in the string (see [3.6 Strings]) is considered to have a
5720 * numeric position: the position of the first character is 1, the position
5721 * of the second character is 2 and so on. The returned substring contains
5722 * those characters for which the position of the character is greater than
5723 * or equal to the second argument and, if the third argument is specified,
5724 * less than the sum of the second and third arguments; the comparisons
5725 * and addition used for the above follow the standard IEEE 754 rules. Thus:
5726 * - substring("12345", 1.5, 2.6) returns "234"
5727 * - substring("12345", 0, 3) returns "12"
5728 * - substring("12345", 0 div 0, 3) returns ""
5729 * - substring("12345", 1, 0 div 0) returns ""
5730 * - substring("12345", -42, 1 div 0) returns "12345"
5731 * - substring("12345", -1 div 0, 1 div 0) returns ""
5732 */
5733void
5734xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5735 xmlXPathObjectPtr str, start, len;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005736 double le=0, in;
5737 int i, l, m;
Owen Taylor3473f882001-02-23 17:55:21 +00005738 xmlChar *ret;
5739
Owen Taylor3473f882001-02-23 17:55:21 +00005740 if (nargs < 2) {
5741 CHECK_ARITY(2);
5742 }
5743 if (nargs > 3) {
5744 CHECK_ARITY(3);
5745 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005746 /*
5747 * take care of possible last (position) argument
5748 */
Owen Taylor3473f882001-02-23 17:55:21 +00005749 if (nargs == 3) {
5750 CAST_TO_NUMBER;
5751 CHECK_TYPE(XPATH_NUMBER);
5752 len = valuePop(ctxt);
5753 le = len->floatval;
5754 xmlXPathFreeObject(len);
Owen Taylor3473f882001-02-23 17:55:21 +00005755 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005756
Owen Taylor3473f882001-02-23 17:55:21 +00005757 CAST_TO_NUMBER;
5758 CHECK_TYPE(XPATH_NUMBER);
5759 start = valuePop(ctxt);
5760 in = start->floatval;
5761 xmlXPathFreeObject(start);
5762 CAST_TO_STRING;
5763 CHECK_TYPE(XPATH_STRING);
5764 str = valuePop(ctxt);
Daniel Veillard97ac1312001-05-30 19:14:17 +00005765 m = xmlUTF8Strlen((const unsigned char *)str->stringval);
Owen Taylor3473f882001-02-23 17:55:21 +00005766
Daniel Veillard97ac1312001-05-30 19:14:17 +00005767 /*
5768 * If last pos not present, calculate last position
5769 */
5770 if (nargs != 3)
5771 le = m;
5772
5773 /*
5774 * To meet our requirements, initial index calculations
5775 * must be done before we convert to integer format
5776 *
5777 * First we normalize indices
5778 */
5779 in -= 1.0;
5780 le += in;
5781 if (in < 0.0)
5782 in = 0.0;
5783 if (le > (double)m)
5784 le = (double)m;
5785
5786 /*
5787 * Now we go to integer form, rounding up
5788 */
Owen Taylor3473f882001-02-23 17:55:21 +00005789 i = (int) in;
5790 if (((double)i) != in) i++;
5791
Owen Taylor3473f882001-02-23 17:55:21 +00005792 l = (int) le;
5793 if (((double)l) != le) l++;
5794
Daniel Veillard97ac1312001-05-30 19:14:17 +00005795 if (l > m) l=m;
Owen Taylor3473f882001-02-23 17:55:21 +00005796
5797 /* number of chars to copy */
5798 l -= i;
5799
Daniel Veillard97ac1312001-05-30 19:14:17 +00005800 ret = xmlUTF8Strsub(str->stringval, i, l);
Owen Taylor3473f882001-02-23 17:55:21 +00005801 if (ret == NULL)
5802 valuePush(ctxt, xmlXPathNewCString(""));
5803 else {
5804 valuePush(ctxt, xmlXPathNewString(ret));
5805 xmlFree(ret);
5806 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005807
Owen Taylor3473f882001-02-23 17:55:21 +00005808 xmlXPathFreeObject(str);
5809}
5810
5811/**
5812 * xmlXPathSubstringBeforeFunction:
5813 * @ctxt: the XPath Parser context
5814 * @nargs: the number of arguments
5815 *
5816 * Implement the substring-before() XPath function
5817 * string substring-before(string, string)
5818 * The substring-before function returns the substring of the first
5819 * argument string that precedes the first occurrence of the second
5820 * argument string in the first argument string, or the empty string
5821 * if the first argument string does not contain the second argument
5822 * string. For example, substring-before("1999/04/01","/") returns 1999.
5823 */
5824void
5825xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5826 xmlXPathObjectPtr str;
5827 xmlXPathObjectPtr find;
5828 xmlBufferPtr target;
5829 const xmlChar *point;
5830 int offset;
5831
5832 CHECK_ARITY(2);
5833 CAST_TO_STRING;
5834 find = valuePop(ctxt);
5835 CAST_TO_STRING;
5836 str = valuePop(ctxt);
5837
5838 target = xmlBufferCreate();
5839 if (target) {
5840 point = xmlStrstr(str->stringval, find->stringval);
5841 if (point) {
5842 offset = (int)(point - str->stringval);
5843 xmlBufferAdd(target, str->stringval, offset);
5844 }
5845 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5846 xmlBufferFree(target);
5847 }
5848
5849 xmlXPathFreeObject(str);
5850 xmlXPathFreeObject(find);
5851}
5852
5853/**
5854 * xmlXPathSubstringAfterFunction:
5855 * @ctxt: the XPath Parser context
5856 * @nargs: the number of arguments
5857 *
5858 * Implement the substring-after() XPath function
5859 * string substring-after(string, string)
5860 * The substring-after function returns the substring of the first
5861 * argument string that follows the first occurrence of the second
5862 * argument string in the first argument string, or the empty stringi
5863 * if the first argument string does not contain the second argument
5864 * string. For example, substring-after("1999/04/01","/") returns 04/01,
5865 * and substring-after("1999/04/01","19") returns 99/04/01.
5866 */
5867void
5868xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5869 xmlXPathObjectPtr str;
5870 xmlXPathObjectPtr find;
5871 xmlBufferPtr target;
5872 const xmlChar *point;
5873 int offset;
5874
5875 CHECK_ARITY(2);
5876 CAST_TO_STRING;
5877 find = valuePop(ctxt);
5878 CAST_TO_STRING;
5879 str = valuePop(ctxt);
5880
5881 target = xmlBufferCreate();
5882 if (target) {
5883 point = xmlStrstr(str->stringval, find->stringval);
5884 if (point) {
5885 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
5886 xmlBufferAdd(target, &str->stringval[offset],
5887 xmlStrlen(str->stringval) - offset);
5888 }
5889 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5890 xmlBufferFree(target);
5891 }
5892
5893 xmlXPathFreeObject(str);
5894 xmlXPathFreeObject(find);
5895}
5896
5897/**
5898 * xmlXPathNormalizeFunction:
5899 * @ctxt: the XPath Parser context
5900 * @nargs: the number of arguments
5901 *
5902 * Implement the normalize-space() XPath function
5903 * string normalize-space(string?)
5904 * The normalize-space function returns the argument string with white
5905 * space normalized by stripping leading and trailing whitespace
5906 * and replacing sequences of whitespace characters by a single
5907 * space. Whitespace characters are the same allowed by the S production
5908 * in XML. If the argument is omitted, it defaults to the context
5909 * node converted to a string, in other words the value of the context node.
5910 */
5911void
5912xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5913 xmlXPathObjectPtr obj = NULL;
5914 xmlChar *source = NULL;
5915 xmlBufferPtr target;
5916 xmlChar blank;
5917
5918 if (nargs == 0) {
5919 /* Use current context node */
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005920 valuePush(ctxt,
5921 xmlXPathWrapString(
5922 xmlXPathCastNodeToString(ctxt->context->node)));
Owen Taylor3473f882001-02-23 17:55:21 +00005923 nargs = 1;
5924 }
5925
5926 CHECK_ARITY(1);
5927 CAST_TO_STRING;
5928 CHECK_TYPE(XPATH_STRING);
5929 obj = valuePop(ctxt);
5930 source = obj->stringval;
5931
5932 target = xmlBufferCreate();
5933 if (target && source) {
5934
5935 /* Skip leading whitespaces */
5936 while (IS_BLANK(*source))
5937 source++;
5938
5939 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
5940 blank = 0;
5941 while (*source) {
5942 if (IS_BLANK(*source)) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005943 blank = 0x20;
Owen Taylor3473f882001-02-23 17:55:21 +00005944 } else {
5945 if (blank) {
5946 xmlBufferAdd(target, &blank, 1);
5947 blank = 0;
5948 }
5949 xmlBufferAdd(target, source, 1);
5950 }
5951 source++;
5952 }
5953
5954 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5955 xmlBufferFree(target);
5956 }
5957 xmlXPathFreeObject(obj);
5958}
5959
5960/**
5961 * xmlXPathTranslateFunction:
5962 * @ctxt: the XPath Parser context
5963 * @nargs: the number of arguments
5964 *
5965 * Implement the translate() XPath function
5966 * string translate(string, string, string)
5967 * The translate function returns the first argument string with
5968 * occurrences of characters in the second argument string replaced
5969 * by the character at the corresponding position in the third argument
5970 * string. For example, translate("bar","abc","ABC") returns the string
5971 * BAr. If there is a character in the second argument string with no
5972 * character at a corresponding position in the third argument string
5973 * (because the second argument string is longer than the third argument
5974 * string), then occurrences of that character in the first argument
5975 * string are removed. For example, translate("--aaa--","abc-","ABC")
5976 * returns "AAA". If a character occurs more than once in second
5977 * argument string, then the first occurrence determines the replacement
5978 * character. If the third argument string is longer than the second
5979 * argument string, then excess characters are ignored.
5980 */
5981void
5982xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00005983 xmlXPathObjectPtr str;
5984 xmlXPathObjectPtr from;
5985 xmlXPathObjectPtr to;
5986 xmlBufferPtr target;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005987 int offset, max;
Daniel Veillarde043ee12001-04-16 14:08:07 +00005988 xmlChar ch;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005989 xmlChar *point;
5990 xmlChar *cptr;
Owen Taylor3473f882001-02-23 17:55:21 +00005991
Daniel Veillarde043ee12001-04-16 14:08:07 +00005992 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00005993
Daniel Veillarde043ee12001-04-16 14:08:07 +00005994 CAST_TO_STRING;
5995 to = valuePop(ctxt);
5996 CAST_TO_STRING;
5997 from = valuePop(ctxt);
5998 CAST_TO_STRING;
5999 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006000
Daniel Veillarde043ee12001-04-16 14:08:07 +00006001 target = xmlBufferCreate();
6002 if (target) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00006003 max = xmlUTF8Strlen(to->stringval);
6004 for (cptr = str->stringval; (ch=*cptr); ) {
6005 offset = xmlUTF8Strloc(from->stringval, cptr);
6006 if (offset >= 0) {
6007 if (offset < max) {
6008 point = xmlUTF8Strpos(to->stringval, offset);
6009 if (point)
6010 xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1));
6011 }
6012 } else
6013 xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
6014
6015 /* Step to next character in input */
6016 cptr++;
6017 if ( ch & 0x80 ) {
6018 /* if not simple ascii, verify proper format */
6019 if ( (ch & 0xc0) != 0xc0 ) {
6020 xmlGenericError(xmlGenericErrorContext,
6021 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
6022 break;
6023 }
6024 /* then skip over remaining bytes for this char */
6025 while ( (ch <<= 1) & 0x80 )
6026 if ( (*cptr++ & 0xc0) != 0x80 ) {
6027 xmlGenericError(xmlGenericErrorContext,
6028 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
6029 break;
6030 }
6031 if (ch & 0x80) /* must have had error encountered */
6032 break;
6033 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00006034 }
Owen Taylor3473f882001-02-23 17:55:21 +00006035 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00006036 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
6037 xmlBufferFree(target);
6038 xmlXPathFreeObject(str);
6039 xmlXPathFreeObject(from);
6040 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00006041}
6042
6043/**
6044 * xmlXPathBooleanFunction:
6045 * @ctxt: the XPath Parser context
6046 * @nargs: the number of arguments
6047 *
6048 * Implement the boolean() XPath function
6049 * boolean boolean(object)
6050 * he boolean function converts its argument to a boolean as follows:
6051 * - a number is true if and only if it is neither positive or
6052 * negative zero nor NaN
6053 * - a node-set is true if and only if it is non-empty
6054 * - a string is true if and only if its length is non-zero
6055 */
6056void
6057xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6058 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00006059
6060 CHECK_ARITY(1);
6061 cur = valuePop(ctxt);
6062 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006063 cur = xmlXPathConvertBoolean(cur);
6064 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00006065}
6066
6067/**
6068 * xmlXPathNotFunction:
6069 * @ctxt: the XPath Parser context
6070 * @nargs: the number of arguments
6071 *
6072 * Implement the not() XPath function
6073 * boolean not(boolean)
6074 * The not function returns true if its argument is false,
6075 * and false otherwise.
6076 */
6077void
6078xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6079 CHECK_ARITY(1);
6080 CAST_TO_BOOLEAN;
6081 CHECK_TYPE(XPATH_BOOLEAN);
6082 ctxt->value->boolval = ! ctxt->value->boolval;
6083}
6084
6085/**
6086 * xmlXPathTrueFunction:
6087 * @ctxt: the XPath Parser context
6088 * @nargs: the number of arguments
6089 *
6090 * Implement the true() XPath function
6091 * boolean true()
6092 */
6093void
6094xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6095 CHECK_ARITY(0);
6096 valuePush(ctxt, xmlXPathNewBoolean(1));
6097}
6098
6099/**
6100 * xmlXPathFalseFunction:
6101 * @ctxt: the XPath Parser context
6102 * @nargs: the number of arguments
6103 *
6104 * Implement the false() XPath function
6105 * boolean false()
6106 */
6107void
6108xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6109 CHECK_ARITY(0);
6110 valuePush(ctxt, xmlXPathNewBoolean(0));
6111}
6112
6113/**
6114 * xmlXPathLangFunction:
6115 * @ctxt: the XPath Parser context
6116 * @nargs: the number of arguments
6117 *
6118 * Implement the lang() XPath function
6119 * boolean lang(string)
6120 * The lang function returns true or false depending on whether the
6121 * language of the context node as specified by xml:lang attributes
6122 * is the same as or is a sublanguage of the language specified by
6123 * the argument string. The language of the context node is determined
6124 * by the value of the xml:lang attribute on the context node, or, if
6125 * the context node has no xml:lang attribute, by the value of the
6126 * xml:lang attribute on the nearest ancestor of the context node that
6127 * has an xml:lang attribute. If there is no such attribute, then lang
6128 * returns false. If there is such an attribute, then lang returns
6129 * true if the attribute value is equal to the argument ignoring case,
6130 * or if there is some suffix starting with - such that the attribute
6131 * value is equal to the argument ignoring that suffix of the attribute
6132 * value and ignoring case.
6133 */
6134void
6135xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6136 xmlXPathObjectPtr val;
6137 const xmlChar *theLang;
6138 const xmlChar *lang;
6139 int ret = 0;
6140 int i;
6141
6142 CHECK_ARITY(1);
6143 CAST_TO_STRING;
6144 CHECK_TYPE(XPATH_STRING);
6145 val = valuePop(ctxt);
6146 lang = val->stringval;
6147 theLang = xmlNodeGetLang(ctxt->context->node);
6148 if ((theLang != NULL) && (lang != NULL)) {
6149 for (i = 0;lang[i] != 0;i++)
6150 if (toupper(lang[i]) != toupper(theLang[i]))
6151 goto not_equal;
6152 ret = 1;
6153 }
6154not_equal:
6155 xmlXPathFreeObject(val);
6156 valuePush(ctxt, xmlXPathNewBoolean(ret));
6157}
6158
6159/**
6160 * xmlXPathNumberFunction:
6161 * @ctxt: the XPath Parser context
6162 * @nargs: the number of arguments
6163 *
6164 * Implement the number() XPath function
6165 * number number(object?)
6166 */
6167void
6168xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6169 xmlXPathObjectPtr cur;
6170 double res;
6171
6172 if (nargs == 0) {
6173 if (ctxt->context->node == NULL) {
6174 valuePush(ctxt, xmlXPathNewFloat(0.0));
6175 } else {
6176 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
6177
6178 res = xmlXPathStringEvalNumber(content);
6179 valuePush(ctxt, xmlXPathNewFloat(res));
6180 xmlFree(content);
6181 }
6182 return;
6183 }
6184
6185 CHECK_ARITY(1);
6186 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006187 cur = xmlXPathConvertNumber(cur);
6188 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00006189}
6190
6191/**
6192 * xmlXPathSumFunction:
6193 * @ctxt: the XPath Parser context
6194 * @nargs: the number of arguments
6195 *
6196 * Implement the sum() XPath function
6197 * number sum(node-set)
6198 * The sum function returns the sum of the values of the nodes in
6199 * the argument node-set.
6200 */
6201void
6202xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6203 xmlXPathObjectPtr cur;
6204 int i;
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006205 double res = 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +00006206
6207 CHECK_ARITY(1);
6208 if ((ctxt->value == NULL) ||
6209 ((ctxt->value->type != XPATH_NODESET) &&
6210 (ctxt->value->type != XPATH_XSLT_TREE)))
6211 XP_ERROR(XPATH_INVALID_TYPE);
6212 cur = valuePop(ctxt);
6213
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006214 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006215 valuePush(ctxt, xmlXPathNewFloat(0.0));
6216 } else {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006217 for (i = 0; i < cur->nodesetval->nodeNr; i++) {
6218 res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00006219 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006220 valuePush(ctxt, xmlXPathNewFloat(res));
Owen Taylor3473f882001-02-23 17:55:21 +00006221 }
6222 xmlXPathFreeObject(cur);
6223}
6224
6225/**
6226 * xmlXPathFloorFunction:
6227 * @ctxt: the XPath Parser context
6228 * @nargs: the number of arguments
6229 *
6230 * Implement the floor() XPath function
6231 * number floor(number)
6232 * The floor function returns the largest (closest to positive infinity)
6233 * number that is not greater than the argument and that is an integer.
6234 */
6235void
6236xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6237 CHECK_ARITY(1);
6238 CAST_TO_NUMBER;
6239 CHECK_TYPE(XPATH_NUMBER);
6240#if 0
6241 ctxt->value->floatval = floor(ctxt->value->floatval);
6242#else
6243 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
6244 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
6245#endif
6246}
6247
6248/**
6249 * xmlXPathCeilingFunction:
6250 * @ctxt: the XPath Parser context
6251 * @nargs: the number of arguments
6252 *
6253 * Implement the ceiling() XPath function
6254 * number ceiling(number)
6255 * The ceiling function returns the smallest (closest to negative infinity)
6256 * number that is not less than the argument and that is an integer.
6257 */
6258void
6259xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6260 double f;
6261
6262 CHECK_ARITY(1);
6263 CAST_TO_NUMBER;
6264 CHECK_TYPE(XPATH_NUMBER);
6265
6266#if 0
6267 ctxt->value->floatval = ceil(ctxt->value->floatval);
6268#else
6269 f = (double)((int) ctxt->value->floatval);
6270 if (f != ctxt->value->floatval)
6271 ctxt->value->floatval = f + 1;
6272#endif
6273}
6274
6275/**
6276 * xmlXPathRoundFunction:
6277 * @ctxt: the XPath Parser context
6278 * @nargs: the number of arguments
6279 *
6280 * Implement the round() XPath function
6281 * number round(number)
6282 * The round function returns the number that is closest to the
6283 * argument and that is an integer. If there are two such numbers,
6284 * then the one that is even is returned.
6285 */
6286void
6287xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6288 double f;
6289
6290 CHECK_ARITY(1);
6291 CAST_TO_NUMBER;
6292 CHECK_TYPE(XPATH_NUMBER);
6293
6294 if ((ctxt->value->floatval == xmlXPathNAN) ||
6295 (ctxt->value->floatval == xmlXPathPINF) ||
6296 (ctxt->value->floatval == xmlXPathNINF) ||
6297 (ctxt->value->floatval == 0.0))
6298 return;
6299
6300#if 0
6301 f = floor(ctxt->value->floatval);
6302#else
6303 f = (double)((int) ctxt->value->floatval);
6304#endif
6305 if (ctxt->value->floatval < f + 0.5)
6306 ctxt->value->floatval = f;
6307 else
6308 ctxt->value->floatval = f + 1;
6309}
6310
6311/************************************************************************
6312 * *
6313 * The Parser *
6314 * *
6315 ************************************************************************/
6316
6317/*
6318 * a couple of forward declarations since we use a recursive call based
6319 * implementation.
6320 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006321static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006322static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006323static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006324#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006325static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
6326#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006327#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006328static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006329#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00006330static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
6331 int qualified);
Owen Taylor3473f882001-02-23 17:55:21 +00006332
6333/**
Daniel Veillard61d80a22001-04-27 17:13:01 +00006334 * xmlXPathCurrentChar:
6335 * @ctxt: the XPath parser context
6336 * @cur: pointer to the beginning of the char
6337 * @len: pointer to the length of the char read
6338 *
6339 * The current char value, if using UTF-8 this may actaully span multiple
6340 * bytes in the input buffer.
6341 *
6342 * Returns the current char value and its lenght
6343 */
6344
6345static int
6346xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
6347 unsigned char c;
6348 unsigned int val;
6349 const xmlChar *cur;
6350
6351 if (ctxt == NULL)
6352 return(0);
6353 cur = ctxt->cur;
6354
6355 /*
6356 * We are supposed to handle UTF8, check it's valid
6357 * From rfc2044: encoding of the Unicode values on UTF-8:
6358 *
6359 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
6360 * 0000 0000-0000 007F 0xxxxxxx
6361 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
6362 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
6363 *
6364 * Check for the 0x110000 limit too
6365 */
6366 c = *cur;
6367 if (c & 0x80) {
6368 if ((cur[1] & 0xc0) != 0x80)
6369 goto encoding_error;
6370 if ((c & 0xe0) == 0xe0) {
6371
6372 if ((cur[2] & 0xc0) != 0x80)
6373 goto encoding_error;
6374 if ((c & 0xf0) == 0xf0) {
6375 if (((c & 0xf8) != 0xf0) ||
6376 ((cur[3] & 0xc0) != 0x80))
6377 goto encoding_error;
6378 /* 4-byte code */
6379 *len = 4;
6380 val = (cur[0] & 0x7) << 18;
6381 val |= (cur[1] & 0x3f) << 12;
6382 val |= (cur[2] & 0x3f) << 6;
6383 val |= cur[3] & 0x3f;
6384 } else {
6385 /* 3-byte code */
6386 *len = 3;
6387 val = (cur[0] & 0xf) << 12;
6388 val |= (cur[1] & 0x3f) << 6;
6389 val |= cur[2] & 0x3f;
6390 }
6391 } else {
6392 /* 2-byte code */
6393 *len = 2;
6394 val = (cur[0] & 0x1f) << 6;
6395 val |= cur[1] & 0x3f;
6396 }
6397 if (!IS_CHAR(val)) {
6398 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
6399 }
6400 return(val);
6401 } else {
6402 /* 1-byte code */
6403 *len = 1;
6404 return((int) *cur);
6405 }
6406encoding_error:
6407 /*
6408 * If we detect an UTF8 error that probably mean that the
6409 * input encoding didn't get properly advertized in the
6410 * declaration header. Report the error and switch the encoding
6411 * to ISO-Latin-1 (if you don't like this policy, just declare the
6412 * encoding !)
6413 */
Daniel Veillard42596ad2001-05-22 16:57:14 +00006414 *len = 0;
Daniel Veillard61d80a22001-04-27 17:13:01 +00006415 XP_ERROR0(XPATH_ENCODING_ERROR);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006416}
6417
6418/**
Owen Taylor3473f882001-02-23 17:55:21 +00006419 * xmlXPathParseNCName:
6420 * @ctxt: the XPath Parser context
6421 *
6422 * parse an XML namespace non qualified name.
6423 *
6424 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
6425 *
6426 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
6427 * CombiningChar | Extender
6428 *
6429 * Returns the namespace name or NULL
6430 */
6431
6432xmlChar *
6433xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard2156a562001-04-28 12:24:34 +00006434 const xmlChar *in;
6435 xmlChar *ret;
6436 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006437
Daniel Veillard2156a562001-04-28 12:24:34 +00006438 /*
6439 * Accelerator for simple ASCII names
6440 */
6441 in = ctxt->cur;
6442 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6443 ((*in >= 0x41) && (*in <= 0x5A)) ||
6444 (*in == '_')) {
6445 in++;
6446 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6447 ((*in >= 0x41) && (*in <= 0x5A)) ||
6448 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard9a89a8a2001-06-27 11:13:35 +00006449 (*in == '_') || (*in == '.') ||
6450 (*in == '-'))
Daniel Veillard2156a562001-04-28 12:24:34 +00006451 in++;
6452 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
6453 (*in == '[') || (*in == ']') || (*in == ':') ||
6454 (*in == '@') || (*in == '*')) {
6455 count = in - ctxt->cur;
6456 if (count == 0)
6457 return(NULL);
6458 ret = xmlStrndup(ctxt->cur, count);
6459 ctxt->cur = in;
6460 return(ret);
6461 }
6462 }
6463 return(xmlXPathParseNameComplex(ctxt, 0));
Owen Taylor3473f882001-02-23 17:55:21 +00006464}
6465
Daniel Veillard2156a562001-04-28 12:24:34 +00006466
Owen Taylor3473f882001-02-23 17:55:21 +00006467/**
6468 * xmlXPathParseQName:
6469 * @ctxt: the XPath Parser context
6470 * @prefix: a xmlChar **
6471 *
6472 * parse an XML qualified name
6473 *
6474 * [NS 5] QName ::= (Prefix ':')? LocalPart
6475 *
6476 * [NS 6] Prefix ::= NCName
6477 *
6478 * [NS 7] LocalPart ::= NCName
6479 *
6480 * Returns the function returns the local part, and prefix is updated
6481 * to get the Prefix if any.
6482 */
6483
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006484static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00006485xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
6486 xmlChar *ret = NULL;
6487
6488 *prefix = NULL;
6489 ret = xmlXPathParseNCName(ctxt);
6490 if (CUR == ':') {
6491 *prefix = ret;
6492 NEXT;
6493 ret = xmlXPathParseNCName(ctxt);
6494 }
6495 return(ret);
6496}
6497
6498/**
6499 * xmlXPathParseName:
6500 * @ctxt: the XPath Parser context
6501 *
6502 * parse an XML name
6503 *
6504 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
6505 * CombiningChar | Extender
6506 *
6507 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
6508 *
6509 * Returns the namespace name or NULL
6510 */
6511
6512xmlChar *
6513xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006514 const xmlChar *in;
6515 xmlChar *ret;
6516 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006517
Daniel Veillard61d80a22001-04-27 17:13:01 +00006518 /*
6519 * Accelerator for simple ASCII names
6520 */
6521 in = ctxt->cur;
6522 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6523 ((*in >= 0x41) && (*in <= 0x5A)) ||
6524 (*in == '_') || (*in == ':')) {
6525 in++;
6526 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6527 ((*in >= 0x41) && (*in <= 0x5A)) ||
6528 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard76d66f42001-05-16 21:05:17 +00006529 (*in == '_') || (*in == '-') ||
6530 (*in == ':') || (*in == '.'))
Daniel Veillard61d80a22001-04-27 17:13:01 +00006531 in++;
Daniel Veillard76d66f42001-05-16 21:05:17 +00006532 if ((*in > 0) && (*in < 0x80)) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006533 count = in - ctxt->cur;
6534 ret = xmlStrndup(ctxt->cur, count);
6535 ctxt->cur = in;
6536 return(ret);
6537 }
6538 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006539 return(xmlXPathParseNameComplex(ctxt, 1));
Owen Taylor3473f882001-02-23 17:55:21 +00006540}
6541
Daniel Veillard61d80a22001-04-27 17:13:01 +00006542static xmlChar *
Daniel Veillard2156a562001-04-28 12:24:34 +00006543xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006544 xmlChar buf[XML_MAX_NAMELEN + 5];
6545 int len = 0, l;
6546 int c;
6547
6548 /*
6549 * Handler for more complex cases
6550 */
6551 c = CUR_CHAR(l);
6552 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
Daniel Veillard2156a562001-04-28 12:24:34 +00006553 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
6554 (c == '*') || /* accelerators */
Daniel Veillard61d80a22001-04-27 17:13:01 +00006555 (!IS_LETTER(c) && (c != '_') &&
Daniel Veillard2156a562001-04-28 12:24:34 +00006556 ((qualified) && (c != ':')))) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006557 return(NULL);
6558 }
6559
6560 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
6561 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
6562 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006563 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006564 (IS_COMBINING(c)) ||
6565 (IS_EXTENDER(c)))) {
6566 COPY_BUF(l,buf,len,c);
6567 NEXTL(l);
6568 c = CUR_CHAR(l);
6569 if (len >= XML_MAX_NAMELEN) {
6570 /*
6571 * Okay someone managed to make a huge name, so he's ready to pay
6572 * for the processing speed.
6573 */
6574 xmlChar *buffer;
6575 int max = len * 2;
6576
6577 buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
6578 if (buffer == NULL) {
6579 XP_ERROR0(XPATH_MEMORY_ERROR);
6580 }
6581 memcpy(buffer, buf, len);
6582 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
6583 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006584 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006585 (IS_COMBINING(c)) ||
6586 (IS_EXTENDER(c))) {
6587 if (len + 10 > max) {
6588 max *= 2;
6589 buffer = (xmlChar *) xmlRealloc(buffer,
6590 max * sizeof(xmlChar));
Daniel Veillard61d80a22001-04-27 17:13:01 +00006591 if (buffer == NULL) {
6592 XP_ERROR0(XPATH_MEMORY_ERROR);
6593 }
6594 }
6595 COPY_BUF(l,buffer,len,c);
6596 NEXTL(l);
6597 c = CUR_CHAR(l);
6598 }
6599 buffer[len] = 0;
6600 return(buffer);
6601 }
6602 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006603 if (len == 0)
6604 return(NULL);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006605 return(xmlStrndup(buf, len));
6606}
Owen Taylor3473f882001-02-23 17:55:21 +00006607/**
6608 * xmlXPathStringEvalNumber:
6609 * @str: A string to scan
6610 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00006611 * [30a] Float ::= Number ('e' Digits?)?
6612 *
Owen Taylor3473f882001-02-23 17:55:21 +00006613 * [30] Number ::= Digits ('.' Digits?)?
6614 * | '.' Digits
6615 * [31] Digits ::= [0-9]+
6616 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006617 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00006618 * In complement of the Number expression, this function also handles
6619 * negative values : '-' Number.
6620 *
6621 * Returns the double value.
6622 */
6623double
6624xmlXPathStringEvalNumber(const xmlChar *str) {
6625 const xmlChar *cur = str;
6626 double ret = 0.0;
6627 double mult = 1;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006628 int ok = 0, tmp = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006629 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006630 int exponent = 0;
6631 int is_exponent_negative = 0;
6632
Owen Taylor3473f882001-02-23 17:55:21 +00006633 while (IS_BLANK(*cur)) cur++;
6634 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
6635 return(xmlXPathNAN);
6636 }
6637 if (*cur == '-') {
6638 isneg = 1;
6639 cur++;
6640 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006641 /*
6642 * tmp is a workaroudn against a gcc compiler bug
6643 */
Owen Taylor3473f882001-02-23 17:55:21 +00006644 while ((*cur >= '0') && (*cur <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006645 tmp = tmp * 10 + (*cur - '0');
Owen Taylor3473f882001-02-23 17:55:21 +00006646 ok = 1;
6647 cur++;
6648 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006649 ret = (double) tmp;
6650
Owen Taylor3473f882001-02-23 17:55:21 +00006651 if (*cur == '.') {
6652 cur++;
6653 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
6654 return(xmlXPathNAN);
6655 }
6656 while ((*cur >= '0') && (*cur <= '9')) {
6657 mult /= 10;
6658 ret = ret + (*cur - '0') * mult;
6659 cur++;
6660 }
6661 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006662 if ((*cur == 'e') || (*cur == 'E')) {
6663 cur++;
6664 if (*cur == '-') {
6665 is_exponent_negative = 1;
6666 cur++;
6667 }
6668 while ((*cur >= '0') && (*cur <= '9')) {
6669 exponent = exponent * 10 + (*cur - '0');
6670 cur++;
6671 }
6672 }
Owen Taylor3473f882001-02-23 17:55:21 +00006673 while (IS_BLANK(*cur)) cur++;
6674 if (*cur != 0) return(xmlXPathNAN);
6675 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006676 if (is_exponent_negative) exponent = -exponent;
6677 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00006678 return(ret);
6679}
6680
6681/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006682 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00006683 * @ctxt: the XPath Parser context
6684 *
6685 * [30] Number ::= Digits ('.' Digits?)?
6686 * | '.' Digits
6687 * [31] Digits ::= [0-9]+
6688 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006689 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00006690 *
6691 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006692static void
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006693xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
6694{
Owen Taylor3473f882001-02-23 17:55:21 +00006695 double ret = 0.0;
6696 double mult = 1;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006697 int ok = 0, tmp = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006698 int exponent = 0;
6699 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006700
6701 CHECK_ERROR;
6702 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
6703 XP_ERROR(XPATH_NUMBER_ERROR);
6704 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006705 /*
6706 * Try to work around a gcc optimizer bug
6707 */
Owen Taylor3473f882001-02-23 17:55:21 +00006708 while ((CUR >= '0') && (CUR <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006709 tmp = tmp * 10 + (CUR - '0');
6710 ok = 1;
6711 NEXT;
Owen Taylor3473f882001-02-23 17:55:21 +00006712 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006713 ret = (double) tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00006714 if (CUR == '.') {
6715 NEXT;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006716 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
6717 XP_ERROR(XPATH_NUMBER_ERROR);
6718 }
6719 while ((CUR >= '0') && (CUR <= '9')) {
6720 mult /= 10;
6721 ret = ret + (CUR - '0') * mult;
6722 NEXT;
6723 }
Owen Taylor3473f882001-02-23 17:55:21 +00006724 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006725 if ((CUR == 'e') || (CUR == 'E')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006726 NEXT;
6727 if (CUR == '-') {
6728 is_exponent_negative = 1;
6729 NEXT;
6730 }
6731 while ((CUR >= '0') && (CUR <= '9')) {
6732 exponent = exponent * 10 + (CUR - '0');
6733 NEXT;
6734 }
6735 if (is_exponent_negative)
6736 exponent = -exponent;
6737 ret *= pow(10.0, (double) exponent);
Bjorn Reese70a9da52001-04-21 16:57:29 +00006738 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006739 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006740 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006741}
6742
6743/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006744 * xmlXPathParseLiteral:
6745 * @ctxt: the XPath Parser context
6746 *
6747 * Parse a Literal
6748 *
6749 * [29] Literal ::= '"' [^"]* '"'
6750 * | "'" [^']* "'"
6751 *
6752 * Returns the value found or NULL in case of error
6753 */
6754static xmlChar *
6755xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
6756 const xmlChar *q;
6757 xmlChar *ret = NULL;
6758
6759 if (CUR == '"') {
6760 NEXT;
6761 q = CUR_PTR;
6762 while ((IS_CHAR(CUR)) && (CUR != '"'))
6763 NEXT;
6764 if (!IS_CHAR(CUR)) {
6765 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6766 } else {
6767 ret = xmlStrndup(q, CUR_PTR - q);
6768 NEXT;
6769 }
6770 } else if (CUR == '\'') {
6771 NEXT;
6772 q = CUR_PTR;
6773 while ((IS_CHAR(CUR)) && (CUR != '\''))
6774 NEXT;
6775 if (!IS_CHAR(CUR)) {
6776 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6777 } else {
6778 ret = xmlStrndup(q, CUR_PTR - q);
6779 NEXT;
6780 }
6781 } else {
6782 XP_ERROR0(XPATH_START_LITERAL_ERROR);
6783 }
6784 return(ret);
6785}
6786
6787/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006788 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00006789 * @ctxt: the XPath Parser context
6790 *
6791 * Parse a Literal and push it on the stack.
6792 *
6793 * [29] Literal ::= '"' [^"]* '"'
6794 * | "'" [^']* "'"
6795 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006796 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00006797 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006798static void
6799xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006800 const xmlChar *q;
6801 xmlChar *ret = NULL;
6802
6803 if (CUR == '"') {
6804 NEXT;
6805 q = CUR_PTR;
6806 while ((IS_CHAR(CUR)) && (CUR != '"'))
6807 NEXT;
6808 if (!IS_CHAR(CUR)) {
6809 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6810 } else {
6811 ret = xmlStrndup(q, CUR_PTR - q);
6812 NEXT;
6813 }
6814 } else if (CUR == '\'') {
6815 NEXT;
6816 q = CUR_PTR;
6817 while ((IS_CHAR(CUR)) && (CUR != '\''))
6818 NEXT;
6819 if (!IS_CHAR(CUR)) {
6820 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6821 } else {
6822 ret = xmlStrndup(q, CUR_PTR - q);
6823 NEXT;
6824 }
6825 } else {
6826 XP_ERROR(XPATH_START_LITERAL_ERROR);
6827 }
6828 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006829 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
6830 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006831 xmlFree(ret);
6832}
6833
6834/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006835 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00006836 * @ctxt: the XPath Parser context
6837 *
6838 * Parse a VariableReference, evaluate it and push it on the stack.
6839 *
6840 * The variable bindings consist of a mapping from variable names
6841 * to variable values. The value of a variable is an object, which
6842 * of any of the types that are possible for the value of an expression,
6843 * and may also be of additional types not specified here.
6844 *
6845 * Early evaluation is possible since:
6846 * The variable bindings [...] used to evaluate a subexpression are
6847 * always the same as those used to evaluate the containing expression.
6848 *
6849 * [36] VariableReference ::= '$' QName
6850 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006851static void
6852xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006853 xmlChar *name;
6854 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006855
6856 SKIP_BLANKS;
6857 if (CUR != '$') {
6858 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6859 }
6860 NEXT;
6861 name = xmlXPathParseQName(ctxt, &prefix);
6862 if (name == NULL) {
6863 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6864 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006865 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006866 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
6867 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006868 SKIP_BLANKS;
6869}
6870
6871/**
6872 * xmlXPathIsNodeType:
6873 * @ctxt: the XPath Parser context
6874 * @name: a name string
6875 *
6876 * Is the name given a NodeType one.
6877 *
6878 * [38] NodeType ::= 'comment'
6879 * | 'text'
6880 * | 'processing-instruction'
6881 * | 'node'
6882 *
6883 * Returns 1 if true 0 otherwise
6884 */
6885int
6886xmlXPathIsNodeType(const xmlChar *name) {
6887 if (name == NULL)
6888 return(0);
6889
6890 if (xmlStrEqual(name, BAD_CAST "comment"))
6891 return(1);
6892 if (xmlStrEqual(name, BAD_CAST "text"))
6893 return(1);
6894 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6895 return(1);
6896 if (xmlStrEqual(name, BAD_CAST "node"))
6897 return(1);
6898 return(0);
6899}
6900
6901/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006902 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00006903 * @ctxt: the XPath Parser context
6904 *
6905 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
6906 * [17] Argument ::= Expr
6907 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006908 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00006909 * pushed on the stack
6910 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006911static void
6912xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006913 xmlChar *name;
6914 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006915 int nbargs = 0;
6916
6917 name = xmlXPathParseQName(ctxt, &prefix);
6918 if (name == NULL) {
6919 XP_ERROR(XPATH_EXPR_ERROR);
6920 }
6921 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006922#ifdef DEBUG_EXPR
6923 if (prefix == NULL)
6924 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
6925 name);
6926 else
6927 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
6928 prefix, name);
6929#endif
6930
Owen Taylor3473f882001-02-23 17:55:21 +00006931 if (CUR != '(') {
6932 XP_ERROR(XPATH_EXPR_ERROR);
6933 }
6934 NEXT;
6935 SKIP_BLANKS;
6936
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006937 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00006938 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006939 int op1 = ctxt->comp->last;
6940 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006941 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006942 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006943 nbargs++;
6944 if (CUR == ')') break;
6945 if (CUR != ',') {
6946 XP_ERROR(XPATH_EXPR_ERROR);
6947 }
6948 NEXT;
6949 SKIP_BLANKS;
6950 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006951 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
6952 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006953 NEXT;
6954 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006955}
6956
6957/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006958 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006959 * @ctxt: the XPath Parser context
6960 *
6961 * [15] PrimaryExpr ::= VariableReference
6962 * | '(' Expr ')'
6963 * | Literal
6964 * | Number
6965 * | FunctionCall
6966 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006967 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006968 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006969static void
6970xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006971 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006972 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006973 else if (CUR == '(') {
6974 NEXT;
6975 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006976 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006977 if (CUR != ')') {
6978 XP_ERROR(XPATH_EXPR_ERROR);
6979 }
6980 NEXT;
6981 SKIP_BLANKS;
6982 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006983 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006984 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006985 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006986 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006987 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006988 }
6989 SKIP_BLANKS;
6990}
6991
6992/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006993 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006994 * @ctxt: the XPath Parser context
6995 *
6996 * [20] FilterExpr ::= PrimaryExpr
6997 * | FilterExpr Predicate
6998 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006999 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007000 * Square brackets are used to filter expressions in the same way that
7001 * they are used in location paths. It is an error if the expression to
7002 * be filtered does not evaluate to a node-set. The context node list
7003 * used for evaluating the expression in square brackets is the node-set
7004 * to be filtered listed in document order.
7005 */
7006
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007007static void
7008xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
7009 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007010 CHECK_ERROR;
7011 SKIP_BLANKS;
7012
7013 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007014 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00007015 SKIP_BLANKS;
7016 }
7017
7018
7019}
7020
7021/**
7022 * xmlXPathScanName:
7023 * @ctxt: the XPath Parser context
7024 *
7025 * Trickery: parse an XML name but without consuming the input flow
7026 * Needed to avoid insanity in the parser state.
7027 *
7028 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
7029 * CombiningChar | Extender
7030 *
7031 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
7032 *
7033 * [6] Names ::= Name (S Name)*
7034 *
7035 * Returns the Name parsed or NULL
7036 */
7037
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007038static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00007039xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
7040 xmlChar buf[XML_MAX_NAMELEN];
7041 int len = 0;
7042
7043 SKIP_BLANKS;
7044 if (!IS_LETTER(CUR) && (CUR != '_') &&
7045 (CUR != ':')) {
7046 return(NULL);
7047 }
7048
7049 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
7050 (NXT(len) == '.') || (NXT(len) == '-') ||
7051 (NXT(len) == '_') || (NXT(len) == ':') ||
7052 (IS_COMBINING(NXT(len))) ||
7053 (IS_EXTENDER(NXT(len)))) {
7054 buf[len] = NXT(len);
7055 len++;
7056 if (len >= XML_MAX_NAMELEN) {
7057 xmlGenericError(xmlGenericErrorContext,
7058 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
7059 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
7060 (NXT(len) == '.') || (NXT(len) == '-') ||
7061 (NXT(len) == '_') || (NXT(len) == ':') ||
7062 (IS_COMBINING(NXT(len))) ||
7063 (IS_EXTENDER(NXT(len))))
7064 len++;
7065 break;
7066 }
7067 }
7068 return(xmlStrndup(buf, len));
7069}
7070
7071/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007072 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007073 * @ctxt: the XPath Parser context
7074 *
7075 * [19] PathExpr ::= LocationPath
7076 * | FilterExpr
7077 * | FilterExpr '/' RelativeLocationPath
7078 * | FilterExpr '//' RelativeLocationPath
7079 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007080 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007081 * The / operator and // operators combine an arbitrary expression
7082 * and a relative location path. It is an error if the expression
7083 * does not evaluate to a node-set.
7084 * The / operator does composition in the same way as when / is
7085 * used in a location path. As in location paths, // is short for
7086 * /descendant-or-self::node()/.
7087 */
7088
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007089static void
7090xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007091 int lc = 1; /* Should we branch to LocationPath ? */
7092 xmlChar *name = NULL; /* we may have to preparse a name to find out */
7093
7094 SKIP_BLANKS;
7095 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
7096 (CUR == '\'') || (CUR == '"')) {
7097 lc = 0;
7098 } else if (CUR == '*') {
7099 /* relative or absolute location path */
7100 lc = 1;
7101 } else if (CUR == '/') {
7102 /* relative or absolute location path */
7103 lc = 1;
7104 } else if (CUR == '@') {
7105 /* relative abbreviated attribute location path */
7106 lc = 1;
7107 } else if (CUR == '.') {
7108 /* relative abbreviated attribute location path */
7109 lc = 1;
7110 } else {
7111 /*
7112 * Problem is finding if we have a name here whether it's:
7113 * - a nodetype
7114 * - a function call in which case it's followed by '('
7115 * - an axis in which case it's followed by ':'
7116 * - a element name
7117 * We do an a priori analysis here rather than having to
7118 * maintain parsed token content through the recursive function
7119 * calls. This looks uglier but makes the code quite easier to
7120 * read/write/debug.
7121 */
7122 SKIP_BLANKS;
7123 name = xmlXPathScanName(ctxt);
7124 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
7125#ifdef DEBUG_STEP
7126 xmlGenericError(xmlGenericErrorContext,
7127 "PathExpr: Axis\n");
7128#endif
7129 lc = 1;
7130 xmlFree(name);
7131 } else if (name != NULL) {
7132 int len =xmlStrlen(name);
7133 int blank = 0;
7134
7135
7136 while (NXT(len) != 0) {
7137 if (NXT(len) == '/') {
7138 /* element name */
7139#ifdef DEBUG_STEP
7140 xmlGenericError(xmlGenericErrorContext,
7141 "PathExpr: AbbrRelLocation\n");
7142#endif
7143 lc = 1;
7144 break;
7145 } else if (IS_BLANK(NXT(len))) {
7146 /* skip to next */
7147 blank = 1;
7148 } else if (NXT(len) == ':') {
7149#ifdef DEBUG_STEP
7150 xmlGenericError(xmlGenericErrorContext,
7151 "PathExpr: AbbrRelLocation\n");
7152#endif
7153 lc = 1;
7154 break;
7155 } else if ((NXT(len) == '(')) {
7156 /* Note Type or Function */
7157 if (xmlXPathIsNodeType(name)) {
7158#ifdef DEBUG_STEP
7159 xmlGenericError(xmlGenericErrorContext,
7160 "PathExpr: Type search\n");
7161#endif
7162 lc = 1;
7163 } else {
7164#ifdef DEBUG_STEP
7165 xmlGenericError(xmlGenericErrorContext,
7166 "PathExpr: function call\n");
7167#endif
7168 lc = 0;
7169 }
7170 break;
7171 } else if ((NXT(len) == '[')) {
7172 /* element name */
7173#ifdef DEBUG_STEP
7174 xmlGenericError(xmlGenericErrorContext,
7175 "PathExpr: AbbrRelLocation\n");
7176#endif
7177 lc = 1;
7178 break;
7179 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
7180 (NXT(len) == '=')) {
7181 lc = 1;
7182 break;
7183 } else {
7184 lc = 1;
7185 break;
7186 }
7187 len++;
7188 }
7189 if (NXT(len) == 0) {
7190#ifdef DEBUG_STEP
7191 xmlGenericError(xmlGenericErrorContext,
7192 "PathExpr: AbbrRelLocation\n");
7193#endif
7194 /* element name */
7195 lc = 1;
7196 }
7197 xmlFree(name);
7198 } else {
7199 /* make sure all cases are covered explicitely */
7200 XP_ERROR(XPATH_EXPR_ERROR);
7201 }
7202 }
7203
7204 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007205 if (CUR == '/') {
7206 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
7207 } else {
7208 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007209 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007210 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007211 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007212 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007213 CHECK_ERROR;
7214 if ((CUR == '/') && (NXT(1) == '/')) {
7215 SKIP(2);
7216 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007217
7218 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7219 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
7220 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
7221
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007222 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007223 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007224 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007225 }
7226 }
7227 SKIP_BLANKS;
7228}
7229
7230/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007231 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007232 * @ctxt: the XPath Parser context
7233 *
7234 * [18] UnionExpr ::= PathExpr
7235 * | UnionExpr '|' PathExpr
7236 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007237 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007238 */
7239
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007240static void
7241xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
7242 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007243 CHECK_ERROR;
7244 SKIP_BLANKS;
7245 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007246 int op1 = ctxt->comp->last;
7247 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007248
7249 NEXT;
7250 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007251 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007252
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007253 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
7254
Owen Taylor3473f882001-02-23 17:55:21 +00007255 SKIP_BLANKS;
7256 }
Owen Taylor3473f882001-02-23 17:55:21 +00007257}
7258
7259/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007260 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007261 * @ctxt: the XPath Parser context
7262 *
7263 * [27] UnaryExpr ::= UnionExpr
7264 * | '-' UnaryExpr
7265 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007266 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007267 */
7268
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007269static void
7270xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007271 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007272 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007273
7274 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00007275 while (CUR == '-') {
7276 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007277 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007278 NEXT;
7279 SKIP_BLANKS;
7280 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007281
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007282 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007283 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007284 if (found) {
7285 if (minus)
7286 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
7287 else
7288 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007289 }
7290}
7291
7292/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007293 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007294 * @ctxt: the XPath Parser context
7295 *
7296 * [26] MultiplicativeExpr ::= UnaryExpr
7297 * | MultiplicativeExpr MultiplyOperator UnaryExpr
7298 * | MultiplicativeExpr 'div' UnaryExpr
7299 * | MultiplicativeExpr 'mod' UnaryExpr
7300 * [34] MultiplyOperator ::= '*'
7301 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007302 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007303 */
7304
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007305static void
7306xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
7307 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007308 CHECK_ERROR;
7309 SKIP_BLANKS;
7310 while ((CUR == '*') ||
7311 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
7312 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
7313 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007314 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007315
7316 if (CUR == '*') {
7317 op = 0;
7318 NEXT;
7319 } else if (CUR == 'd') {
7320 op = 1;
7321 SKIP(3);
7322 } else if (CUR == 'm') {
7323 op = 2;
7324 SKIP(3);
7325 }
7326 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007327 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007328 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007329 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007330 SKIP_BLANKS;
7331 }
7332}
7333
7334/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007335 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007336 * @ctxt: the XPath Parser context
7337 *
7338 * [25] AdditiveExpr ::= MultiplicativeExpr
7339 * | AdditiveExpr '+' MultiplicativeExpr
7340 * | AdditiveExpr '-' MultiplicativeExpr
7341 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007342 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007343 */
7344
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007345static void
7346xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007347
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007348 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007349 CHECK_ERROR;
7350 SKIP_BLANKS;
7351 while ((CUR == '+') || (CUR == '-')) {
7352 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007353 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007354
7355 if (CUR == '+') plus = 1;
7356 else plus = 0;
7357 NEXT;
7358 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007359 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007360 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007361 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007362 SKIP_BLANKS;
7363 }
7364}
7365
7366/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007367 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007368 * @ctxt: the XPath Parser context
7369 *
7370 * [24] RelationalExpr ::= AdditiveExpr
7371 * | RelationalExpr '<' AdditiveExpr
7372 * | RelationalExpr '>' AdditiveExpr
7373 * | RelationalExpr '<=' AdditiveExpr
7374 * | RelationalExpr '>=' AdditiveExpr
7375 *
7376 * A <= B > C is allowed ? Answer from James, yes with
7377 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
7378 * which is basically what got implemented.
7379 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007380 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00007381 * on the stack
7382 */
7383
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007384static void
7385xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
7386 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007387 CHECK_ERROR;
7388 SKIP_BLANKS;
7389 while ((CUR == '<') ||
7390 (CUR == '>') ||
7391 ((CUR == '<') && (NXT(1) == '=')) ||
7392 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007393 int inf, strict;
7394 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007395
7396 if (CUR == '<') inf = 1;
7397 else inf = 0;
7398 if (NXT(1) == '=') strict = 0;
7399 else strict = 1;
7400 NEXT;
7401 if (!strict) NEXT;
7402 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007403 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007404 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007405 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00007406 SKIP_BLANKS;
7407 }
7408}
7409
7410/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007411 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007412 * @ctxt: the XPath Parser context
7413 *
7414 * [23] EqualityExpr ::= RelationalExpr
7415 * | EqualityExpr '=' RelationalExpr
7416 * | EqualityExpr '!=' RelationalExpr
7417 *
7418 * A != B != C is allowed ? Answer from James, yes with
7419 * (RelationalExpr = RelationalExpr) = RelationalExpr
7420 * (RelationalExpr != RelationalExpr) != RelationalExpr
7421 * which is basically what got implemented.
7422 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007423 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007424 *
7425 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007426static void
7427xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
7428 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007429 CHECK_ERROR;
7430 SKIP_BLANKS;
7431 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007432 int eq;
7433 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007434
7435 if (CUR == '=') eq = 1;
7436 else eq = 0;
7437 NEXT;
7438 if (!eq) NEXT;
7439 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007440 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007441 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007442 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007443 SKIP_BLANKS;
7444 }
7445}
7446
7447/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007448 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007449 * @ctxt: the XPath Parser context
7450 *
7451 * [22] AndExpr ::= EqualityExpr
7452 * | AndExpr 'and' EqualityExpr
7453 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007454 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007455 *
7456 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007457static void
7458xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
7459 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007460 CHECK_ERROR;
7461 SKIP_BLANKS;
7462 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007463 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007464 SKIP(3);
7465 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007466 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007467 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007468 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007469 SKIP_BLANKS;
7470 }
7471}
7472
7473/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007474 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007475 * @ctxt: the XPath Parser context
7476 *
7477 * [14] Expr ::= OrExpr
7478 * [21] OrExpr ::= AndExpr
7479 * | OrExpr 'or' AndExpr
7480 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007481 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00007482 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007483static void
7484xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
7485 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007486 CHECK_ERROR;
7487 SKIP_BLANKS;
7488 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007489 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007490 SKIP(2);
7491 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007492 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007493 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007494 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
7495 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00007496 SKIP_BLANKS;
7497 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007498 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
7499 /* more ops could be optimized too */
7500 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
7501 }
Owen Taylor3473f882001-02-23 17:55:21 +00007502}
7503
7504/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007505 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00007506 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007507 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00007508 *
7509 * [8] Predicate ::= '[' PredicateExpr ']'
7510 * [9] PredicateExpr ::= Expr
7511 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007512 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00007513 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007514static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007515xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007516 int op1 = ctxt->comp->last;
7517
7518 SKIP_BLANKS;
7519 if (CUR != '[') {
7520 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7521 }
7522 NEXT;
7523 SKIP_BLANKS;
7524
7525 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007526 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007527 CHECK_ERROR;
7528
7529 if (CUR != ']') {
7530 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7531 }
7532
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007533 if (filter)
7534 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
7535 else
7536 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007537
7538 NEXT;
7539 SKIP_BLANKS;
7540}
7541
7542/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007543 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00007544 * @ctxt: the XPath Parser context
7545 * @test: pointer to a xmlXPathTestVal
7546 * @type: pointer to a xmlXPathTypeVal
7547 * @prefix: placeholder for a possible name prefix
7548 *
7549 * [7] NodeTest ::= NameTest
7550 * | NodeType '(' ')'
7551 * | 'processing-instruction' '(' Literal ')'
7552 *
7553 * [37] NameTest ::= '*'
7554 * | NCName ':' '*'
7555 * | QName
7556 * [38] NodeType ::= 'comment'
7557 * | 'text'
7558 * | 'processing-instruction'
7559 * | 'node'
7560 *
7561 * Returns the name found and update @test, @type and @prefix appropriately
7562 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007563static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007564xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
7565 xmlXPathTypeVal *type, const xmlChar **prefix,
7566 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00007567 int blanks;
7568
7569 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
7570 STRANGE;
7571 return(NULL);
7572 }
7573 *type = 0;
7574 *test = 0;
7575 *prefix = NULL;
7576 SKIP_BLANKS;
7577
7578 if ((name == NULL) && (CUR == '*')) {
7579 /*
7580 * All elements
7581 */
7582 NEXT;
7583 *test = NODE_TEST_ALL;
7584 return(NULL);
7585 }
7586
7587 if (name == NULL)
7588 name = xmlXPathParseNCName(ctxt);
7589 if (name == NULL) {
7590 XP_ERROR0(XPATH_EXPR_ERROR);
7591 }
7592
7593 blanks = IS_BLANK(CUR);
7594 SKIP_BLANKS;
7595 if (CUR == '(') {
7596 NEXT;
7597 /*
7598 * NodeType or PI search
7599 */
7600 if (xmlStrEqual(name, BAD_CAST "comment"))
7601 *type = NODE_TYPE_COMMENT;
7602 else if (xmlStrEqual(name, BAD_CAST "node"))
7603 *type = NODE_TYPE_NODE;
7604 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
7605 *type = NODE_TYPE_PI;
7606 else if (xmlStrEqual(name, BAD_CAST "text"))
7607 *type = NODE_TYPE_TEXT;
7608 else {
7609 if (name != NULL)
7610 xmlFree(name);
7611 XP_ERROR0(XPATH_EXPR_ERROR);
7612 }
7613
7614 *test = NODE_TEST_TYPE;
7615
7616 SKIP_BLANKS;
7617 if (*type == NODE_TYPE_PI) {
7618 /*
7619 * Specific case: search a PI by name.
7620 */
Owen Taylor3473f882001-02-23 17:55:21 +00007621 if (name != NULL)
7622 xmlFree(name);
Daniel Veillard82e49712001-04-26 14:38:03 +00007623 name = NULL;
7624 if (CUR != ')') {
7625 name = xmlXPathParseLiteral(ctxt);
7626 CHECK_ERROR 0;
7627 SKIP_BLANKS;
7628 }
Owen Taylor3473f882001-02-23 17:55:21 +00007629 }
7630 if (CUR != ')') {
7631 if (name != NULL)
7632 xmlFree(name);
7633 XP_ERROR0(XPATH_UNCLOSED_ERROR);
7634 }
7635 NEXT;
7636 return(name);
7637 }
7638 *test = NODE_TEST_NAME;
7639 if ((!blanks) && (CUR == ':')) {
7640 NEXT;
7641
7642 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007643 * Since currently the parser context don't have a
7644 * namespace list associated:
7645 * The namespace name for this prefix can be computed
7646 * only at evaluation time. The compilation is done
7647 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00007648 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007649#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00007650 *prefix = xmlXPathNsLookup(ctxt->context, name);
7651 if (name != NULL)
7652 xmlFree(name);
7653 if (*prefix == NULL) {
7654 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
7655 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007656#else
7657 *prefix = name;
7658#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007659
7660 if (CUR == '*') {
7661 /*
7662 * All elements
7663 */
7664 NEXT;
7665 *test = NODE_TEST_ALL;
7666 return(NULL);
7667 }
7668
7669 name = xmlXPathParseNCName(ctxt);
7670 if (name == NULL) {
7671 XP_ERROR0(XPATH_EXPR_ERROR);
7672 }
7673 }
7674 return(name);
7675}
7676
7677/**
7678 * xmlXPathIsAxisName:
7679 * @name: a preparsed name token
7680 *
7681 * [6] AxisName ::= 'ancestor'
7682 * | 'ancestor-or-self'
7683 * | 'attribute'
7684 * | 'child'
7685 * | 'descendant'
7686 * | 'descendant-or-self'
7687 * | 'following'
7688 * | 'following-sibling'
7689 * | 'namespace'
7690 * | 'parent'
7691 * | 'preceding'
7692 * | 'preceding-sibling'
7693 * | 'self'
7694 *
7695 * Returns the axis or 0
7696 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007697static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00007698xmlXPathIsAxisName(const xmlChar *name) {
7699 xmlXPathAxisVal ret = 0;
7700 switch (name[0]) {
7701 case 'a':
7702 if (xmlStrEqual(name, BAD_CAST "ancestor"))
7703 ret = AXIS_ANCESTOR;
7704 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
7705 ret = AXIS_ANCESTOR_OR_SELF;
7706 if (xmlStrEqual(name, BAD_CAST "attribute"))
7707 ret = AXIS_ATTRIBUTE;
7708 break;
7709 case 'c':
7710 if (xmlStrEqual(name, BAD_CAST "child"))
7711 ret = AXIS_CHILD;
7712 break;
7713 case 'd':
7714 if (xmlStrEqual(name, BAD_CAST "descendant"))
7715 ret = AXIS_DESCENDANT;
7716 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
7717 ret = AXIS_DESCENDANT_OR_SELF;
7718 break;
7719 case 'f':
7720 if (xmlStrEqual(name, BAD_CAST "following"))
7721 ret = AXIS_FOLLOWING;
7722 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
7723 ret = AXIS_FOLLOWING_SIBLING;
7724 break;
7725 case 'n':
7726 if (xmlStrEqual(name, BAD_CAST "namespace"))
7727 ret = AXIS_NAMESPACE;
7728 break;
7729 case 'p':
7730 if (xmlStrEqual(name, BAD_CAST "parent"))
7731 ret = AXIS_PARENT;
7732 if (xmlStrEqual(name, BAD_CAST "preceding"))
7733 ret = AXIS_PRECEDING;
7734 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
7735 ret = AXIS_PRECEDING_SIBLING;
7736 break;
7737 case 's':
7738 if (xmlStrEqual(name, BAD_CAST "self"))
7739 ret = AXIS_SELF;
7740 break;
7741 }
7742 return(ret);
7743}
7744
7745/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007746 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00007747 * @ctxt: the XPath Parser context
7748 *
7749 * [4] Step ::= AxisSpecifier NodeTest Predicate*
7750 * | AbbreviatedStep
7751 *
7752 * [12] AbbreviatedStep ::= '.' | '..'
7753 *
7754 * [5] AxisSpecifier ::= AxisName '::'
7755 * | AbbreviatedAxisSpecifier
7756 *
7757 * [13] AbbreviatedAxisSpecifier ::= '@'?
7758 *
7759 * Modified for XPtr range support as:
7760 *
7761 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
7762 * | AbbreviatedStep
7763 * | 'range-to' '(' Expr ')' Predicate*
7764 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007765 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00007766 * A location step of . is short for self::node(). This is
7767 * particularly useful in conjunction with //. For example, the
7768 * location path .//para is short for
7769 * self::node()/descendant-or-self::node()/child::para
7770 * and so will select all para descendant elements of the context
7771 * node.
7772 * Similarly, a location step of .. is short for parent::node().
7773 * For example, ../title is short for parent::node()/child::title
7774 * and so will select the title children of the parent of the context
7775 * node.
7776 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007777static void
7778xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007779#ifdef LIBXML_XPTR_ENABLED
7780 int rangeto = 0;
7781 int op2 = -1;
7782#endif
7783
Owen Taylor3473f882001-02-23 17:55:21 +00007784 SKIP_BLANKS;
7785 if ((CUR == '.') && (NXT(1) == '.')) {
7786 SKIP(2);
7787 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007788 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
7789 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007790 } else if (CUR == '.') {
7791 NEXT;
7792 SKIP_BLANKS;
7793 } else {
7794 xmlChar *name = NULL;
7795 const xmlChar *prefix = NULL;
7796 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007797 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007798 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007799 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00007800
7801 /*
7802 * The modification needed for XPointer change to the production
7803 */
7804#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007805 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00007806 name = xmlXPathParseNCName(ctxt);
7807 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007808 op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007809 xmlFree(name);
7810 SKIP_BLANKS;
7811 if (CUR != '(') {
7812 XP_ERROR(XPATH_EXPR_ERROR);
7813 }
7814 NEXT;
7815 SKIP_BLANKS;
7816
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007817 xmlXPathCompileExpr(ctxt);
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007818 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
Owen Taylor3473f882001-02-23 17:55:21 +00007819 CHECK_ERROR;
7820
7821 SKIP_BLANKS;
7822 if (CUR != ')') {
7823 XP_ERROR(XPATH_EXPR_ERROR);
7824 }
7825 NEXT;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007826 rangeto = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007827 goto eval_predicates;
7828 }
7829 }
7830#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00007831 if (CUR == '*') {
7832 axis = AXIS_CHILD;
7833 } else {
7834 if (name == NULL)
7835 name = xmlXPathParseNCName(ctxt);
7836 if (name != NULL) {
7837 axis = xmlXPathIsAxisName(name);
7838 if (axis != 0) {
7839 SKIP_BLANKS;
7840 if ((CUR == ':') && (NXT(1) == ':')) {
7841 SKIP(2);
7842 xmlFree(name);
7843 name = NULL;
7844 } else {
7845 /* an element name can conflict with an axis one :-\ */
7846 axis = AXIS_CHILD;
7847 }
Owen Taylor3473f882001-02-23 17:55:21 +00007848 } else {
Owen Taylor3473f882001-02-23 17:55:21 +00007849 axis = AXIS_CHILD;
7850 }
Daniel Veillard2156a562001-04-28 12:24:34 +00007851 } else if (CUR == '@') {
7852 NEXT;
7853 axis = AXIS_ATTRIBUTE;
Owen Taylor3473f882001-02-23 17:55:21 +00007854 } else {
Daniel Veillard2156a562001-04-28 12:24:34 +00007855 axis = AXIS_CHILD;
Owen Taylor3473f882001-02-23 17:55:21 +00007856 }
Owen Taylor3473f882001-02-23 17:55:21 +00007857 }
7858
7859 CHECK_ERROR;
7860
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007861 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00007862 if (test == 0)
7863 return;
7864
7865#ifdef DEBUG_STEP
7866 xmlGenericError(xmlGenericErrorContext,
7867 "Basis : computing new set\n");
7868#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007869
Owen Taylor3473f882001-02-23 17:55:21 +00007870#ifdef DEBUG_STEP
7871 xmlGenericError(xmlGenericErrorContext, "Basis : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007872 if (ctxt->value == NULL)
7873 xmlGenericError(xmlGenericErrorContext, "no value\n");
7874 else if (ctxt->value->nodesetval == NULL)
7875 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7876 else
7877 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007878#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007879
7880eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007881 op1 = ctxt->comp->last;
7882 ctxt->comp->last = -1;
7883
Owen Taylor3473f882001-02-23 17:55:21 +00007884 SKIP_BLANKS;
7885 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007886 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007887 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007888
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007889#ifdef LIBXML_XPTR_ENABLED
7890 if (rangeto) {
7891 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
7892 } else
7893#endif
7894 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
7895 test, type, (void *)prefix, (void *)name);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007896
Owen Taylor3473f882001-02-23 17:55:21 +00007897 }
7898#ifdef DEBUG_STEP
7899 xmlGenericError(xmlGenericErrorContext, "Step : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007900 if (ctxt->value == NULL)
7901 xmlGenericError(xmlGenericErrorContext, "no value\n");
7902 else if (ctxt->value->nodesetval == NULL)
7903 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7904 else
7905 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
7906 ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007907#endif
7908}
7909
7910/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007911 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007912 * @ctxt: the XPath Parser context
7913 *
7914 * [3] RelativeLocationPath ::= Step
7915 * | RelativeLocationPath '/' Step
7916 * | AbbreviatedRelativeLocationPath
7917 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
7918 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007919 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00007920 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007921static void
Owen Taylor3473f882001-02-23 17:55:21 +00007922#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007923xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007924#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007925xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007926#endif
7927(xmlXPathParserContextPtr ctxt) {
7928 SKIP_BLANKS;
7929 if ((CUR == '/') && (NXT(1) == '/')) {
7930 SKIP(2);
7931 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007932 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7933 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007934 } else if (CUR == '/') {
7935 NEXT;
7936 SKIP_BLANKS;
7937 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007938 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007939 SKIP_BLANKS;
7940 while (CUR == '/') {
7941 if ((CUR == '/') && (NXT(1) == '/')) {
7942 SKIP(2);
7943 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007944 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00007945 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007946 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007947 } else if (CUR == '/') {
7948 NEXT;
7949 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007950 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007951 }
7952 SKIP_BLANKS;
7953 }
7954}
7955
7956/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007957 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007958 * @ctxt: the XPath Parser context
7959 *
7960 * [1] LocationPath ::= RelativeLocationPath
7961 * | AbsoluteLocationPath
7962 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
7963 * | AbbreviatedAbsoluteLocationPath
7964 * [10] AbbreviatedAbsoluteLocationPath ::=
7965 * '//' RelativeLocationPath
7966 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007967 * Compile a location path
7968 *
Owen Taylor3473f882001-02-23 17:55:21 +00007969 * // is short for /descendant-or-self::node()/. For example,
7970 * //para is short for /descendant-or-self::node()/child::para and
7971 * so will select any para element in the document (even a para element
7972 * that is a document element will be selected by //para since the
7973 * document element node is a child of the root node); div//para is
7974 * short for div/descendant-or-self::node()/child::para and so will
7975 * select all para descendants of div children.
7976 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007977static void
7978xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007979 SKIP_BLANKS;
7980 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007981 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007982 } else {
7983 while (CUR == '/') {
7984 if ((CUR == '/') && (NXT(1) == '/')) {
7985 SKIP(2);
7986 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007987 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7988 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007989 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007990 } else if (CUR == '/') {
7991 NEXT;
Daniel Veillard608ad072001-06-14 08:32:28 +00007992 SKIP_BLANKS;
7993 if ((CUR != 0 ) &&
7994 ((IS_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
7995 (CUR == '@') || (CUR == '*')))
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007996 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007997 }
7998 }
7999 }
8000}
8001
Daniel Veillard9e7160d2001-03-18 23:17:47 +00008002/************************************************************************
8003 * *
8004 * XPath precompiled expression evaluation *
8005 * *
8006 ************************************************************************/
8007
Daniel Veillardf06307e2001-07-03 10:35:50 +00008008static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008009xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
8010
8011/**
8012 * xmlXPathNodeCollectAndTest:
8013 * @ctxt: the XPath Parser context
8014 * @op: the XPath precompiled step operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00008015 * @first: pointer to the first element in document order
8016 * @last: pointer to the last element in document order
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008017 *
8018 * This is the function implementing a step: based on the current list
8019 * of nodes, it builds up a new list, looking at all nodes under that
8020 * axis and selecting them it also do the predicate filtering
8021 *
8022 * Pushes the new NodeSet resulting from the search.
Daniel Veillardf06307e2001-07-03 10:35:50 +00008023 *
8024 * Returns the number of node traversed
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008025 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00008026static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008027xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008028 xmlXPathStepOpPtr op,
8029 xmlNodePtr * first, xmlNodePtr * last)
8030{
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008031 xmlXPathAxisVal axis = op->value;
8032 xmlXPathTestVal test = op->value2;
8033 xmlXPathTypeVal type = op->value3;
8034 const xmlChar *prefix = op->value4;
8035 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00008036 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008037
8038#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008039 int n = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008040#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008041 int i, t = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008042 xmlNodeSetPtr ret, list;
8043 xmlXPathTraversalFunction next = NULL;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008044 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008045 xmlNodePtr cur = NULL;
8046 xmlXPathObjectPtr obj;
8047 xmlNodeSetPtr nodelist;
8048 xmlNodePtr tmp;
8049
Daniel Veillardf06307e2001-07-03 10:35:50 +00008050 CHECK_TYPE0(XPATH_NODESET);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008051 obj = valuePop(ctxt);
8052 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00008053 if (prefix != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008054 URI = xmlXPathNsLookup(ctxt->context, prefix);
8055 if (URI == NULL)
8056 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
Daniel Veillarde043ee12001-04-16 14:08:07 +00008057 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008058#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008059 xmlGenericError(xmlGenericErrorContext, "new step : ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008060#endif
8061 switch (axis) {
8062 case AXIS_ANCESTOR:
8063#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008064 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008065#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008066 first = NULL;
8067 next = xmlXPathNextAncestor;
8068 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008069 case AXIS_ANCESTOR_OR_SELF:
8070#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008071 xmlGenericError(xmlGenericErrorContext,
8072 "axis 'ancestors-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008073#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008074 first = NULL;
8075 next = xmlXPathNextAncestorOrSelf;
8076 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008077 case AXIS_ATTRIBUTE:
8078#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008079 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008080#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008081 first = NULL;
8082 last = NULL;
8083 next = xmlXPathNextAttribute;
8084 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008085 case AXIS_CHILD:
8086#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008087 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008088#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008089 last = NULL;
8090 next = xmlXPathNextChild;
8091 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008092 case AXIS_DESCENDANT:
8093#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008094 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008095#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008096 last = NULL;
8097 next = xmlXPathNextDescendant;
8098 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008099 case AXIS_DESCENDANT_OR_SELF:
8100#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008101 xmlGenericError(xmlGenericErrorContext,
8102 "axis 'descendant-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008103#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008104 last = NULL;
8105 next = xmlXPathNextDescendantOrSelf;
8106 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008107 case AXIS_FOLLOWING:
8108#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008109 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008110#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008111 last = NULL;
8112 next = xmlXPathNextFollowing;
8113 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008114 case AXIS_FOLLOWING_SIBLING:
8115#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008116 xmlGenericError(xmlGenericErrorContext,
8117 "axis 'following-siblings' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008118#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008119 last = NULL;
8120 next = xmlXPathNextFollowingSibling;
8121 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008122 case AXIS_NAMESPACE:
8123#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008124 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008125#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008126 first = NULL;
8127 last = NULL;
8128 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8129 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008130 case AXIS_PARENT:
8131#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008132 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008133#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008134 first = NULL;
8135 next = xmlXPathNextParent;
8136 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008137 case AXIS_PRECEDING:
8138#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008139 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008140#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008141 first = NULL;
8142 next = xmlXPathNextPrecedingInternal;
8143 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008144 case AXIS_PRECEDING_SIBLING:
8145#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008146 xmlGenericError(xmlGenericErrorContext,
8147 "axis 'preceding-sibling' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008148#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008149 first = NULL;
8150 next = xmlXPathNextPrecedingSibling;
8151 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008152 case AXIS_SELF:
8153#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008154 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008155#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008156 first = NULL;
8157 last = NULL;
8158 next = xmlXPathNextSelf;
8159 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008160 }
8161 if (next == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00008162 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008163
8164 nodelist = obj->nodesetval;
8165 if (nodelist == NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008166 xmlXPathFreeObject(obj);
8167 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8168 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008169 }
8170 addNode = xmlXPathNodeSetAddUnique;
8171 ret = NULL;
8172#ifdef DEBUG_STEP
8173 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008174 " context contains %d nodes\n", nodelist->nodeNr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008175 switch (test) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008176 case NODE_TEST_NONE:
8177 xmlGenericError(xmlGenericErrorContext,
8178 " searching for none !!!\n");
8179 break;
8180 case NODE_TEST_TYPE:
8181 xmlGenericError(xmlGenericErrorContext,
8182 " searching for type %d\n", type);
8183 break;
8184 case NODE_TEST_PI:
8185 xmlGenericError(xmlGenericErrorContext,
8186 " searching for PI !!!\n");
8187 break;
8188 case NODE_TEST_ALL:
8189 xmlGenericError(xmlGenericErrorContext,
8190 " searching for *\n");
8191 break;
8192 case NODE_TEST_NS:
8193 xmlGenericError(xmlGenericErrorContext,
8194 " searching for namespace %s\n",
8195 prefix);
8196 break;
8197 case NODE_TEST_NAME:
8198 xmlGenericError(xmlGenericErrorContext,
8199 " searching for name %s\n", name);
8200 if (prefix != NULL)
8201 xmlGenericError(xmlGenericErrorContext,
8202 " with namespace %s\n", prefix);
8203 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008204 }
8205 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8206#endif
8207 /*
8208 * 2.3 Node Tests
8209 * - For the attribute axis, the principal node type is attribute.
8210 * - For the namespace axis, the principal node type is namespace.
8211 * - For other axes, the principal node type is element.
8212 *
8213 * A node test * is true for any node of the
8214 * principal node type. For example, child::* willi
8215 * select all element children of the context node
8216 */
8217 tmp = ctxt->context->node;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008218 for (i = 0; i < nodelist->nodeNr; i++) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008219 ctxt->context->node = nodelist->nodeTab[i];
8220
Daniel Veillardf06307e2001-07-03 10:35:50 +00008221 cur = NULL;
8222 list = xmlXPathNodeSetCreate(NULL);
8223 do {
8224 cur = next(ctxt, cur);
8225 if (cur == NULL)
8226 break;
8227 if ((first != NULL) && (*first == cur))
8228 break;
8229 if (((t % 256) == 0) &&
8230 (first != NULL) && (*first != NULL) &&
8231 (xmlXPathCmpNodes(*first, cur) >= 0))
8232 break;
8233 if ((last != NULL) && (*last == cur))
8234 break;
8235 if (((t % 256) == 0) &&
8236 (last != NULL) && (*last != NULL) &&
8237 (xmlXPathCmpNodes(cur, *last) >= 0))
8238 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008239 t++;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008240#ifdef DEBUG_STEP
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008241 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
8242#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008243 switch (test) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008244 case NODE_TEST_NONE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008245 ctxt->context->node = tmp;
8246 STRANGE return(t);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008247 case NODE_TEST_TYPE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008248 if ((cur->type == type) ||
8249 ((type == NODE_TYPE_NODE) &&
8250 ((cur->type == XML_DOCUMENT_NODE) ||
8251 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8252 (cur->type == XML_ELEMENT_NODE) ||
8253 (cur->type == XML_PI_NODE) ||
8254 (cur->type == XML_COMMENT_NODE) ||
8255 (cur->type == XML_CDATA_SECTION_NODE) ||
Daniel Veillard7583a592001-07-08 13:15:55 +00008256 (cur->type == XML_TEXT_NODE))) ||
8257 ((type == NODE_TYPE_TEXT) &&
8258 (cur->type == XML_CDATA_SECTION_NODE))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008259#ifdef DEBUG_STEP
8260 n++;
8261#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008262 addNode(list, cur);
8263 }
8264 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008265 case NODE_TEST_PI:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008266 if (cur->type == XML_PI_NODE) {
8267 if ((name != NULL) &&
8268 (!xmlStrEqual(name, cur->name)))
8269 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008270#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008271 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008272#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008273 addNode(list, cur);
8274 }
8275 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008276 case NODE_TEST_ALL:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008277 if (axis == AXIS_ATTRIBUTE) {
8278 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008279#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008280 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008281#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008282 addNode(list, cur);
8283 }
8284 } else if (axis == AXIS_NAMESPACE) {
8285 if (cur->type == XML_NAMESPACE_DECL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008286#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008287 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008288#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008289 addNode(list, cur);
8290 }
8291 } else {
8292 if (cur->type == XML_ELEMENT_NODE) {
8293 if (prefix == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008294#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008295 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008296#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008297 addNode(list, cur);
8298 } else if ((cur->ns != NULL) &&
8299 (xmlStrEqual(URI, cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008300#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008301 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008302#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008303 addNode(list, cur);
8304 }
8305 }
8306 }
8307 break;
8308 case NODE_TEST_NS:{
8309 TODO;
8310 break;
8311 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008312 case NODE_TEST_NAME:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008313 switch (cur->type) {
8314 case XML_ELEMENT_NODE:
8315 if (xmlStrEqual(name, cur->name)) {
8316 if (prefix == NULL) {
8317 if (cur->ns == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008318#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008319 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008320#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008321 addNode(list, cur);
8322 }
8323 } else {
8324 if ((cur->ns != NULL) &&
8325 (xmlStrEqual(URI,
8326 cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008327#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008328 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008329#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008330 addNode(list, cur);
8331 }
8332 }
8333 }
8334 break;
8335 case XML_ATTRIBUTE_NODE:{
8336 xmlAttrPtr attr = (xmlAttrPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008337
Daniel Veillardf06307e2001-07-03 10:35:50 +00008338 if (xmlStrEqual(name, attr->name)) {
8339 if (prefix == NULL) {
8340 if ((attr->ns == NULL) ||
8341 (attr->ns->prefix == NULL)) {
8342#ifdef DEBUG_STEP
8343 n++;
8344#endif
8345 addNode(list,
8346 (xmlNodePtr) attr);
8347 }
8348 } else {
8349 if ((attr->ns != NULL) &&
8350 (xmlStrEqual(URI,
8351 attr->ns->
8352 href))) {
8353#ifdef DEBUG_STEP
8354 n++;
8355#endif
8356 addNode(list,
8357 (xmlNodePtr) attr);
8358 }
8359 }
8360 }
8361 break;
8362 }
8363 case XML_NAMESPACE_DECL:
8364 if (cur->type == XML_NAMESPACE_DECL) {
8365 xmlNsPtr ns = (xmlNsPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008366
Daniel Veillardf06307e2001-07-03 10:35:50 +00008367 if ((ns->prefix != NULL) && (name != NULL)
8368 && (xmlStrEqual(ns->prefix, name))) {
8369#ifdef DEBUG_STEP
8370 n++;
8371#endif
8372 addNode(list, cur);
8373 }
8374 }
8375 break;
8376 default:
8377 break;
8378 }
8379 break;
8380 break;
8381 }
8382 } while (cur != NULL);
8383
8384 /*
8385 * If there is some predicate filtering do it now
8386 */
8387 if (op->ch2 != -1) {
8388 xmlXPathObjectPtr obj2;
8389
8390 valuePush(ctxt, xmlXPathWrapNodeSet(list));
8391 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
8392 CHECK_TYPE0(XPATH_NODESET);
8393 obj2 = valuePop(ctxt);
8394 list = obj2->nodesetval;
8395 obj2->nodesetval = NULL;
8396 xmlXPathFreeObject(obj2);
8397 }
8398 if (ret == NULL) {
8399 ret = list;
8400 } else {
8401 ret = xmlXPathNodeSetMerge(ret, list);
8402 xmlXPathFreeNodeSet(list);
8403 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008404 }
8405 ctxt->context->node = tmp;
8406#ifdef DEBUG_STEP
8407 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008408 "\nExamined %d nodes, found %d nodes at that step\n",
8409 t, n);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008410#endif
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008411 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00008412 if ((obj->boolval) && (obj->user != NULL)) {
8413 ctxt->value->boolval = 1;
8414 ctxt->value->user = obj->user;
8415 obj->user = NULL;
8416 obj->boolval = 0;
8417 }
8418 xmlXPathFreeObject(obj);
Daniel Veillardf06307e2001-07-03 10:35:50 +00008419 return(t);
8420}
8421
8422/**
8423 * xmlXPathNodeCollectAndTestNth:
8424 * @ctxt: the XPath Parser context
8425 * @op: the XPath precompiled step operation
8426 * @indx: the index to collect
8427 * @first: pointer to the first element in document order
8428 * @last: pointer to the last element in document order
8429 *
8430 * This is the function implementing a step: based on the current list
8431 * of nodes, it builds up a new list, looking at all nodes under that
8432 * axis and selecting them it also do the predicate filtering
8433 *
8434 * Pushes the new NodeSet resulting from the search.
8435 * Returns the number of node traversed
8436 */
8437static int
8438xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
8439 xmlXPathStepOpPtr op, int indx,
8440 xmlNodePtr * first, xmlNodePtr * last)
8441{
8442 xmlXPathAxisVal axis = op->value;
8443 xmlXPathTestVal test = op->value2;
8444 xmlXPathTypeVal type = op->value3;
8445 const xmlChar *prefix = op->value4;
8446 const xmlChar *name = op->value5;
8447 const xmlChar *URI = NULL;
8448 int n = 0, t = 0;
8449
8450 int i;
8451 xmlNodeSetPtr list;
8452 xmlXPathTraversalFunction next = NULL;
8453 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
8454 xmlNodePtr cur = NULL;
8455 xmlXPathObjectPtr obj;
8456 xmlNodeSetPtr nodelist;
8457 xmlNodePtr tmp;
8458
8459 CHECK_TYPE0(XPATH_NODESET);
8460 obj = valuePop(ctxt);
8461 addNode = xmlXPathNodeSetAdd;
8462 if (prefix != NULL) {
8463 URI = xmlXPathNsLookup(ctxt->context, prefix);
8464 if (URI == NULL)
8465 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
8466 }
8467#ifdef DEBUG_STEP_NTH
8468 xmlGenericError(xmlGenericErrorContext, "new step : ");
8469 if (first != NULL) {
8470 if (*first != NULL)
8471 xmlGenericError(xmlGenericErrorContext, "first = %s ",
8472 (*first)->name);
8473 else
8474 xmlGenericError(xmlGenericErrorContext, "first = NULL ");
8475 }
8476 if (last != NULL) {
8477 if (*last != NULL)
8478 xmlGenericError(xmlGenericErrorContext, "last = %s ",
8479 (*last)->name);
8480 else
8481 xmlGenericError(xmlGenericErrorContext, "last = NULL ");
8482 }
8483#endif
8484 switch (axis) {
8485 case AXIS_ANCESTOR:
8486#ifdef DEBUG_STEP_NTH
8487 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
8488#endif
8489 first = NULL;
8490 next = xmlXPathNextAncestor;
8491 break;
8492 case AXIS_ANCESTOR_OR_SELF:
8493#ifdef DEBUG_STEP_NTH
8494 xmlGenericError(xmlGenericErrorContext,
8495 "axis 'ancestors-or-self' ");
8496#endif
8497 first = NULL;
8498 next = xmlXPathNextAncestorOrSelf;
8499 break;
8500 case AXIS_ATTRIBUTE:
8501#ifdef DEBUG_STEP_NTH
8502 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
8503#endif
8504 first = NULL;
8505 last = NULL;
8506 next = xmlXPathNextAttribute;
8507 break;
8508 case AXIS_CHILD:
8509#ifdef DEBUG_STEP_NTH
8510 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
8511#endif
8512 last = NULL;
8513 next = xmlXPathNextChild;
8514 break;
8515 case AXIS_DESCENDANT:
8516#ifdef DEBUG_STEP_NTH
8517 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
8518#endif
8519 last = NULL;
8520 next = xmlXPathNextDescendant;
8521 break;
8522 case AXIS_DESCENDANT_OR_SELF:
8523#ifdef DEBUG_STEP_NTH
8524 xmlGenericError(xmlGenericErrorContext,
8525 "axis 'descendant-or-self' ");
8526#endif
8527 last = NULL;
8528 next = xmlXPathNextDescendantOrSelf;
8529 break;
8530 case AXIS_FOLLOWING:
8531#ifdef DEBUG_STEP_NTH
8532 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
8533#endif
8534 last = NULL;
8535 next = xmlXPathNextFollowing;
8536 break;
8537 case AXIS_FOLLOWING_SIBLING:
8538#ifdef DEBUG_STEP_NTH
8539 xmlGenericError(xmlGenericErrorContext,
8540 "axis 'following-siblings' ");
8541#endif
8542 last = NULL;
8543 next = xmlXPathNextFollowingSibling;
8544 break;
8545 case AXIS_NAMESPACE:
8546#ifdef DEBUG_STEP_NTH
8547 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
8548#endif
8549 last = NULL;
8550 first = NULL;
8551 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8552 break;
8553 case AXIS_PARENT:
8554#ifdef DEBUG_STEP_NTH
8555 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
8556#endif
8557 first = NULL;
8558 next = xmlXPathNextParent;
8559 break;
8560 case AXIS_PRECEDING:
8561#ifdef DEBUG_STEP_NTH
8562 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
8563#endif
8564 first = NULL;
8565 next = xmlXPathNextPrecedingInternal;
8566 break;
8567 case AXIS_PRECEDING_SIBLING:
8568#ifdef DEBUG_STEP_NTH
8569 xmlGenericError(xmlGenericErrorContext,
8570 "axis 'preceding-sibling' ");
8571#endif
8572 first = NULL;
8573 next = xmlXPathNextPrecedingSibling;
8574 break;
8575 case AXIS_SELF:
8576#ifdef DEBUG_STEP_NTH
8577 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
8578#endif
8579 first = NULL;
8580 last = NULL;
8581 next = xmlXPathNextSelf;
8582 break;
8583 }
8584 if (next == NULL)
8585 return(0);
8586
8587 nodelist = obj->nodesetval;
8588 if (nodelist == NULL) {
8589 xmlXPathFreeObject(obj);
8590 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8591 return(0);
8592 }
8593 addNode = xmlXPathNodeSetAddUnique;
8594#ifdef DEBUG_STEP_NTH
8595 xmlGenericError(xmlGenericErrorContext,
8596 " context contains %d nodes\n", nodelist->nodeNr);
8597 switch (test) {
8598 case NODE_TEST_NONE:
8599 xmlGenericError(xmlGenericErrorContext,
8600 " searching for none !!!\n");
8601 break;
8602 case NODE_TEST_TYPE:
8603 xmlGenericError(xmlGenericErrorContext,
8604 " searching for type %d\n", type);
8605 break;
8606 case NODE_TEST_PI:
8607 xmlGenericError(xmlGenericErrorContext,
8608 " searching for PI !!!\n");
8609 break;
8610 case NODE_TEST_ALL:
8611 xmlGenericError(xmlGenericErrorContext,
8612 " searching for *\n");
8613 break;
8614 case NODE_TEST_NS:
8615 xmlGenericError(xmlGenericErrorContext,
8616 " searching for namespace %s\n",
8617 prefix);
8618 break;
8619 case NODE_TEST_NAME:
8620 xmlGenericError(xmlGenericErrorContext,
8621 " searching for name %s\n", name);
8622 if (prefix != NULL)
8623 xmlGenericError(xmlGenericErrorContext,
8624 " with namespace %s\n", prefix);
8625 break;
8626 }
8627 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8628#endif
8629 /*
8630 * 2.3 Node Tests
8631 * - For the attribute axis, the principal node type is attribute.
8632 * - For the namespace axis, the principal node type is namespace.
8633 * - For other axes, the principal node type is element.
8634 *
8635 * A node test * is true for any node of the
8636 * principal node type. For example, child::* willi
8637 * select all element children of the context node
8638 */
8639 tmp = ctxt->context->node;
8640 list = xmlXPathNodeSetCreate(NULL);
8641 for (i = 0; i < nodelist->nodeNr; i++) {
8642 ctxt->context->node = nodelist->nodeTab[i];
8643
8644 cur = NULL;
8645 n = 0;
8646 do {
8647 cur = next(ctxt, cur);
8648 if (cur == NULL)
8649 break;
8650 if ((first != NULL) && (*first == cur))
8651 break;
8652 if (((t % 256) == 0) &&
8653 (first != NULL) && (*first != NULL) &&
8654 (xmlXPathCmpNodes(*first, cur) >= 0))
8655 break;
8656 if ((last != NULL) && (*last == cur))
8657 break;
8658 if (((t % 256) == 0) &&
8659 (last != NULL) && (*last != NULL) &&
8660 (xmlXPathCmpNodes(cur, *last) >= 0))
8661 break;
8662 t++;
8663 switch (test) {
8664 case NODE_TEST_NONE:
8665 ctxt->context->node = tmp;
8666 STRANGE return(0);
8667 case NODE_TEST_TYPE:
8668 if ((cur->type == type) ||
8669 ((type == NODE_TYPE_NODE) &&
8670 ((cur->type == XML_DOCUMENT_NODE) ||
8671 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8672 (cur->type == XML_ELEMENT_NODE) ||
8673 (cur->type == XML_PI_NODE) ||
8674 (cur->type == XML_COMMENT_NODE) ||
8675 (cur->type == XML_CDATA_SECTION_NODE) ||
8676 (cur->type == XML_TEXT_NODE)))) {
8677 n++;
8678 if (n == indx)
8679 addNode(list, cur);
8680 }
8681 break;
8682 case NODE_TEST_PI:
8683 if (cur->type == XML_PI_NODE) {
8684 if ((name != NULL) &&
8685 (!xmlStrEqual(name, cur->name)))
8686 break;
8687 n++;
8688 if (n == indx)
8689 addNode(list, cur);
8690 }
8691 break;
8692 case NODE_TEST_ALL:
8693 if (axis == AXIS_ATTRIBUTE) {
8694 if (cur->type == XML_ATTRIBUTE_NODE) {
8695 n++;
8696 if (n == indx)
8697 addNode(list, cur);
8698 }
8699 } else if (axis == AXIS_NAMESPACE) {
8700 if (cur->type == XML_NAMESPACE_DECL) {
8701 n++;
8702 if (n == indx)
8703 addNode(list, cur);
8704 }
8705 } else {
8706 if (cur->type == XML_ELEMENT_NODE) {
8707 if (prefix == NULL) {
8708 n++;
8709 if (n == indx)
8710 addNode(list, cur);
8711 } else if ((cur->ns != NULL) &&
8712 (xmlStrEqual(URI, cur->ns->href))) {
8713 n++;
8714 if (n == indx)
8715 addNode(list, cur);
8716 }
8717 }
8718 }
8719 break;
8720 case NODE_TEST_NS:{
8721 TODO;
8722 break;
8723 }
8724 case NODE_TEST_NAME:
8725 switch (cur->type) {
8726 case XML_ELEMENT_NODE:
8727 if (xmlStrEqual(name, cur->name)) {
8728 if (prefix == NULL) {
8729 if (cur->ns == NULL) {
8730 n++;
8731 if (n == indx)
8732 addNode(list, cur);
8733 }
8734 } else {
8735 if ((cur->ns != NULL) &&
8736 (xmlStrEqual(URI,
8737 cur->ns->href))) {
8738 n++;
8739 if (n == indx)
8740 addNode(list, cur);
8741 }
8742 }
8743 }
8744 break;
8745 case XML_ATTRIBUTE_NODE:{
8746 xmlAttrPtr attr = (xmlAttrPtr) cur;
8747
8748 if (xmlStrEqual(name, attr->name)) {
8749 if (prefix == NULL) {
8750 if ((attr->ns == NULL) ||
8751 (attr->ns->prefix == NULL)) {
8752 n++;
8753 if (n == indx)
8754 addNode(list, cur);
8755 }
8756 } else {
8757 if ((attr->ns != NULL) &&
8758 (xmlStrEqual(URI,
8759 attr->ns->
8760 href))) {
8761 n++;
8762 if (n == indx)
8763 addNode(list, cur);
8764 }
8765 }
8766 }
8767 break;
8768 }
8769 case XML_NAMESPACE_DECL:
8770 if (cur->type == XML_NAMESPACE_DECL) {
8771 xmlNsPtr ns = (xmlNsPtr) cur;
8772
8773 if ((ns->prefix != NULL) && (name != NULL)
8774 && (xmlStrEqual(ns->prefix, name))) {
8775 n++;
8776 if (n == indx)
8777 addNode(list, cur);
8778 }
8779 }
8780 break;
8781 default:
8782 break;
8783 }
8784 break;
8785 break;
8786 }
8787 } while (n < indx);
8788 }
8789 ctxt->context->node = tmp;
8790#ifdef DEBUG_STEP_NTH
8791 xmlGenericError(xmlGenericErrorContext,
8792 "\nExamined %d nodes, found %d nodes at that step\n",
8793 t, list->nodeNr);
8794#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008795 valuePush(ctxt, xmlXPathWrapNodeSet(list));
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00008796 if ((obj->boolval) && (obj->user != NULL)) {
8797 ctxt->value->boolval = 1;
8798 ctxt->value->user = obj->user;
8799 obj->user = NULL;
8800 obj->boolval = 0;
8801 }
8802 xmlXPathFreeObject(obj);
Daniel Veillardf06307e2001-07-03 10:35:50 +00008803 return(t);
8804}
8805
8806/**
8807 * xmlXPathCompOpEvalFirst:
8808 * @ctxt: the XPath parser context with the compiled expression
8809 * @op: an XPath compiled operation
8810 * @first: the first elem found so far
8811 *
8812 * Evaluate the Precompiled XPath operation searching only the first
8813 * element in document order
8814 *
8815 * Returns the number of examined objects.
8816 */
8817static int
8818xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
8819 xmlXPathStepOpPtr op, xmlNodePtr * first)
8820{
8821 int total = 0, cur;
8822 xmlXPathCompExprPtr comp;
8823 xmlXPathObjectPtr arg1, arg2;
8824
8825 comp = ctxt->comp;
8826 switch (op->op) {
8827 case XPATH_OP_END:
8828 return (0);
8829 case XPATH_OP_UNION:
8830 total =
8831 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8832 first);
8833 if ((ctxt->value != NULL)
8834 && (ctxt->value->type == XPATH_NODESET)
8835 && (ctxt->value->nodesetval != NULL)
8836 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8837 /*
8838 * limit tree traversing to first node in the result
8839 */
8840 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8841 *first = ctxt->value->nodesetval->nodeTab[0];
8842 }
8843 cur =
8844 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
8845 first);
8846 CHECK_TYPE0(XPATH_NODESET);
8847 arg2 = valuePop(ctxt);
8848
8849 CHECK_TYPE0(XPATH_NODESET);
8850 arg1 = valuePop(ctxt);
8851
8852 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8853 arg2->nodesetval);
8854 valuePush(ctxt, arg1);
8855 xmlXPathFreeObject(arg2);
8856 /* optimizer */
8857 if (total > cur)
8858 xmlXPathCompSwap(op);
8859 return (total + cur);
8860 case XPATH_OP_ROOT:
8861 xmlXPathRoot(ctxt);
8862 return (0);
8863 case XPATH_OP_NODE:
8864 if (op->ch1 != -1)
8865 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8866 if (op->ch2 != -1)
8867 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8868 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8869 return (total);
8870 case XPATH_OP_RESET:
8871 if (op->ch1 != -1)
8872 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8873 if (op->ch2 != -1)
8874 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8875 ctxt->context->node = NULL;
8876 return (total);
8877 case XPATH_OP_COLLECT:{
8878 if (op->ch1 == -1)
8879 return (total);
8880
8881 total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8882
8883 /*
8884 * Optimization for [n] selection where n is a number
8885 */
8886 if ((op->ch2 != -1) &&
8887 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8888 (comp->steps[op->ch2].ch1 == -1) &&
8889 (comp->steps[op->ch2].ch2 != -1) &&
8890 (comp->steps[comp->steps[op->ch2].ch2].op ==
8891 XPATH_OP_VALUE)) {
8892 xmlXPathObjectPtr val;
8893
8894 val = comp->steps[comp->steps[op->ch2].ch2].value4;
8895 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
8896 int indx = (int) val->floatval;
8897
8898 if (val->floatval == (float) indx) {
8899 xmlXPathNodeCollectAndTestNth(ctxt, op, indx,
8900 first, NULL);
8901 return (total);
8902 }
8903 }
8904 }
8905 total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL);
8906 return (total);
8907 }
8908 case XPATH_OP_VALUE:
8909 valuePush(ctxt,
8910 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
8911 return (0);
8912 case XPATH_OP_SORT:
8913 if (op->ch1 != -1)
8914 total +=
8915 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8916 first);
8917 if ((ctxt->value != NULL)
8918 && (ctxt->value->type == XPATH_NODESET)
8919 && (ctxt->value->nodesetval != NULL))
8920 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8921 return (total);
8922 default:
8923 return (xmlXPathCompOpEval(ctxt, op));
8924 }
8925}
8926
8927/**
8928 * xmlXPathCompOpEvalLast:
8929 * @ctxt: the XPath parser context with the compiled expression
8930 * @op: an XPath compiled operation
8931 * @last: the last elem found so far
8932 *
8933 * Evaluate the Precompiled XPath operation searching only the last
8934 * element in document order
8935 *
8936 * Returns the number of node traversed
8937 */
8938static int
8939xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
8940 xmlNodePtr * last)
8941{
8942 int total = 0, cur;
8943 xmlXPathCompExprPtr comp;
8944 xmlXPathObjectPtr arg1, arg2;
8945
8946 comp = ctxt->comp;
8947 switch (op->op) {
8948 case XPATH_OP_END:
8949 return (0);
8950 case XPATH_OP_UNION:
8951 total =
8952 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
8953 if ((ctxt->value != NULL)
8954 && (ctxt->value->type == XPATH_NODESET)
8955 && (ctxt->value->nodesetval != NULL)
8956 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8957 /*
8958 * limit tree traversing to first node in the result
8959 */
8960 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8961 *last =
8962 ctxt->value->nodesetval->nodeTab[ctxt->value->
8963 nodesetval->nodeNr -
8964 1];
8965 }
8966 cur =
8967 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
8968 if ((ctxt->value != NULL)
8969 && (ctxt->value->type == XPATH_NODESET)
8970 && (ctxt->value->nodesetval != NULL)
8971 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8972 }
8973 CHECK_TYPE0(XPATH_NODESET);
8974 arg2 = valuePop(ctxt);
8975
8976 CHECK_TYPE0(XPATH_NODESET);
8977 arg1 = valuePop(ctxt);
8978
8979 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8980 arg2->nodesetval);
8981 valuePush(ctxt, arg1);
8982 xmlXPathFreeObject(arg2);
8983 /* optimizer */
8984 if (total > cur)
8985 xmlXPathCompSwap(op);
8986 return (total + cur);
8987 case XPATH_OP_ROOT:
8988 xmlXPathRoot(ctxt);
8989 return (0);
8990 case XPATH_OP_NODE:
8991 if (op->ch1 != -1)
8992 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8993 if (op->ch2 != -1)
8994 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8995 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8996 return (total);
8997 case XPATH_OP_RESET:
8998 if (op->ch1 != -1)
8999 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9000 if (op->ch2 != -1)
9001 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9002 ctxt->context->node = NULL;
9003 return (total);
9004 case XPATH_OP_COLLECT:{
9005 if (op->ch1 == -1)
9006 return (0);
9007
9008 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9009
9010 /*
9011 * Optimization for [n] selection where n is a number
9012 */
9013 if ((op->ch2 != -1) &&
9014 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
9015 (comp->steps[op->ch2].ch1 == -1) &&
9016 (comp->steps[op->ch2].ch2 != -1) &&
9017 (comp->steps[comp->steps[op->ch2].ch2].op ==
9018 XPATH_OP_VALUE)) {
9019 xmlXPathObjectPtr val;
9020
9021 val = comp->steps[comp->steps[op->ch2].ch2].value4;
9022 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
9023 int indx = (int) val->floatval;
9024
9025 if (val->floatval == (float) indx) {
9026 total +=
9027 xmlXPathNodeCollectAndTestNth(ctxt, op,
9028 indx, NULL,
9029 last);
9030 return (total);
9031 }
9032 }
9033 }
9034 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last);
9035 return (total);
9036 }
9037 case XPATH_OP_VALUE:
9038 valuePush(ctxt,
9039 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
9040 return (0);
9041 case XPATH_OP_SORT:
9042 if (op->ch1 != -1)
9043 total +=
9044 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
9045 last);
9046 if ((ctxt->value != NULL)
9047 && (ctxt->value->type == XPATH_NODESET)
9048 && (ctxt->value->nodesetval != NULL))
9049 xmlXPathNodeSetSort(ctxt->value->nodesetval);
9050 return (total);
9051 default:
9052 return (xmlXPathCompOpEval(ctxt, op));
9053 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009054}
9055
Owen Taylor3473f882001-02-23 17:55:21 +00009056/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009057 * xmlXPathCompOpEval:
9058 * @ctxt: the XPath parser context with the compiled expression
9059 * @op: an XPath compiled operation
9060 *
9061 * Evaluate the Precompiled XPath operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00009062 * Returns the number of node traversed
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009063 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00009064static int
9065xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
9066{
9067 int total = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009068 int equal, ret;
9069 xmlXPathCompExprPtr comp;
9070 xmlXPathObjectPtr arg1, arg2;
9071
9072 comp = ctxt->comp;
9073 switch (op->op) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00009074 case XPATH_OP_END:
9075 return (0);
9076 case XPATH_OP_AND:
9077 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9078 xmlXPathBooleanFunction(ctxt, 1);
9079 if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
9080 return (total);
9081 arg2 = valuePop(ctxt);
9082 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9083 xmlXPathBooleanFunction(ctxt, 1);
9084 arg1 = valuePop(ctxt);
9085 arg1->boolval &= arg2->boolval;
9086 valuePush(ctxt, arg1);
9087 xmlXPathFreeObject(arg2);
9088 return (total);
9089 case XPATH_OP_OR:
9090 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9091 xmlXPathBooleanFunction(ctxt, 1);
9092 if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
9093 return (total);
9094 arg2 = valuePop(ctxt);
9095 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9096 xmlXPathBooleanFunction(ctxt, 1);
9097 arg1 = valuePop(ctxt);
9098 arg1->boolval |= arg2->boolval;
9099 valuePush(ctxt, arg1);
9100 xmlXPathFreeObject(arg2);
9101 return (total);
9102 case XPATH_OP_EQUAL:
9103 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9104 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9105 equal = xmlXPathEqualValues(ctxt);
9106 if (op->value)
9107 valuePush(ctxt, xmlXPathNewBoolean(equal));
9108 else
9109 valuePush(ctxt, xmlXPathNewBoolean(!equal));
9110 return (total);
9111 case XPATH_OP_CMP:
9112 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9113 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9114 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
9115 valuePush(ctxt, xmlXPathNewBoolean(ret));
9116 return (total);
9117 case XPATH_OP_PLUS:
9118 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9119 if (op->ch2 != -1)
9120 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9121 if (op->value == 0)
9122 xmlXPathSubValues(ctxt);
9123 else if (op->value == 1)
9124 xmlXPathAddValues(ctxt);
9125 else if (op->value == 2)
9126 xmlXPathValueFlipSign(ctxt);
9127 else if (op->value == 3) {
9128 CAST_TO_NUMBER;
9129 CHECK_TYPE0(XPATH_NUMBER);
9130 }
9131 return (total);
9132 case XPATH_OP_MULT:
9133 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9134 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9135 if (op->value == 0)
9136 xmlXPathMultValues(ctxt);
9137 else if (op->value == 1)
9138 xmlXPathDivValues(ctxt);
9139 else if (op->value == 2)
9140 xmlXPathModValues(ctxt);
9141 return (total);
9142 case XPATH_OP_UNION:
9143 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9144 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9145 CHECK_TYPE0(XPATH_NODESET);
9146 arg2 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009147
Daniel Veillardf06307e2001-07-03 10:35:50 +00009148 CHECK_TYPE0(XPATH_NODESET);
9149 arg1 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009150
Daniel Veillardf06307e2001-07-03 10:35:50 +00009151 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
9152 arg2->nodesetval);
9153 valuePush(ctxt, arg1);
9154 xmlXPathFreeObject(arg2);
9155 return (total);
9156 case XPATH_OP_ROOT:
9157 xmlXPathRoot(ctxt);
9158 return (total);
9159 case XPATH_OP_NODE:
9160 if (op->ch1 != -1)
9161 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9162 if (op->ch2 != -1)
9163 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9164 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
9165 return (total);
9166 case XPATH_OP_RESET:
9167 if (op->ch1 != -1)
9168 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9169 if (op->ch2 != -1)
9170 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9171 ctxt->context->node = NULL;
9172 return (total);
9173 case XPATH_OP_COLLECT:{
9174 if (op->ch1 == -1)
9175 return (total);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009176
Daniel Veillardf06307e2001-07-03 10:35:50 +00009177 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009178
Daniel Veillardf06307e2001-07-03 10:35:50 +00009179 /*
9180 * Optimization for [n] selection where n is a number
9181 */
9182 if ((op->ch2 != -1) &&
9183 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
9184 (comp->steps[op->ch2].ch1 == -1) &&
9185 (comp->steps[op->ch2].ch2 != -1) &&
9186 (comp->steps[comp->steps[op->ch2].ch2].op ==
9187 XPATH_OP_VALUE)) {
9188 xmlXPathObjectPtr val;
Daniel Veillard42596ad2001-05-22 16:57:14 +00009189
Daniel Veillardf06307e2001-07-03 10:35:50 +00009190 val = comp->steps[comp->steps[op->ch2].ch2].value4;
9191 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
9192 int indx = (int) val->floatval;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009193
Daniel Veillardf06307e2001-07-03 10:35:50 +00009194 if (val->floatval == (float) indx) {
9195 total +=
9196 xmlXPathNodeCollectAndTestNth(ctxt, op,
9197 indx, NULL,
9198 NULL);
9199 return (total);
9200 }
9201 }
9202 }
9203 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL);
9204 return (total);
9205 }
9206 case XPATH_OP_VALUE:
9207 valuePush(ctxt,
9208 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
9209 return (total);
9210 case XPATH_OP_VARIABLE:{
9211 if (op->ch1 != -1)
9212 total +=
9213 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9214 if (op->value5 == NULL)
9215 valuePush(ctxt,
9216 xmlXPathVariableLookup(ctxt->context,
9217 op->value4));
9218 else {
9219 const xmlChar *URI;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009220
Daniel Veillardf06307e2001-07-03 10:35:50 +00009221 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9222 if (URI == NULL) {
9223 xmlGenericError(xmlGenericErrorContext,
9224 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
9225 op->value4, op->value5);
9226 return (total);
9227 }
9228 valuePush(ctxt,
9229 xmlXPathVariableLookupNS(ctxt->context,
9230 op->value4, URI));
9231 }
9232 return (total);
9233 }
9234 case XPATH_OP_FUNCTION:{
9235 xmlXPathFunction func;
9236 const xmlChar *oldFunc, *oldFuncURI;
9237
9238 if (op->ch1 != -1)
9239 total +=
9240 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9241 if (op->cache != NULL)
9242 func = (xmlXPathFunction) op->cache;
9243 else {
9244 const xmlChar *URI = NULL;
9245
9246 if (op->value5 == NULL)
9247 func =
9248 xmlXPathFunctionLookup(ctxt->context,
9249 op->value4);
9250 else {
9251 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9252 if (URI == NULL) {
9253 xmlGenericError(xmlGenericErrorContext,
9254 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
9255 op->value4, op->value5);
9256 return (total);
9257 }
9258 func = xmlXPathFunctionLookupNS(ctxt->context,
9259 op->value4, URI);
9260 }
9261 if (func == NULL) {
9262 xmlGenericError(xmlGenericErrorContext,
9263 "xmlXPathRunEval: function %s not found\n",
9264 op->value4);
9265 XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
9266 return (total);
9267 }
9268 op->cache = (void *) func;
9269 op->cacheURI = (void *) URI;
9270 }
9271 oldFunc = ctxt->context->function;
9272 oldFuncURI = ctxt->context->functionURI;
9273 ctxt->context->function = op->value4;
9274 ctxt->context->functionURI = op->cacheURI;
9275 func(ctxt, op->value);
9276 ctxt->context->function = oldFunc;
9277 ctxt->context->functionURI = oldFuncURI;
9278 return (total);
9279 }
9280 case XPATH_OP_ARG:
9281 if (op->ch1 != -1)
9282 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9283 if (op->ch2 != -1)
9284 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9285 return (total);
9286 case XPATH_OP_PREDICATE:
9287 case XPATH_OP_FILTER:{
9288 xmlXPathObjectPtr res;
9289 xmlXPathObjectPtr obj, tmp;
9290 xmlNodeSetPtr newset = NULL;
9291 xmlNodeSetPtr oldset;
9292 xmlNodePtr oldnode;
9293 int i;
9294
9295 /*
9296 * Optimization for ()[1] selection i.e. the first elem
9297 */
9298 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9299 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9300 (comp->steps[op->ch2].op == XPATH_OP_VALUE)) {
9301 xmlXPathObjectPtr val;
9302
9303 val = comp->steps[op->ch2].value4;
9304 if ((val != NULL) && (val->type == XPATH_NUMBER) &&
9305 (val->floatval == 1.0)) {
9306 xmlNodePtr first = NULL;
9307
9308 total +=
9309 xmlXPathCompOpEvalFirst(ctxt,
9310 &comp->steps[op->ch1],
9311 &first);
9312 /*
9313 * The nodeset should be in document order,
9314 * Keep only the first value
9315 */
9316 if ((ctxt->value != NULL) &&
9317 (ctxt->value->type == XPATH_NODESET) &&
9318 (ctxt->value->nodesetval != NULL) &&
9319 (ctxt->value->nodesetval->nodeNr > 1))
9320 ctxt->value->nodesetval->nodeNr = 1;
9321 return (total);
9322 }
9323 }
9324 /*
9325 * Optimization for ()[last()] selection i.e. the last elem
9326 */
9327 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9328 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9329 (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
9330 int f = comp->steps[op->ch2].ch1;
9331
9332 if ((f != -1) &&
9333 (comp->steps[f].op == XPATH_OP_FUNCTION) &&
9334 (comp->steps[f].value5 == NULL) &&
9335 (comp->steps[f].value == 0) &&
9336 (comp->steps[f].value4 != NULL) &&
9337 (xmlStrEqual
9338 (comp->steps[f].value4, BAD_CAST "last"))) {
9339 xmlNodePtr last = NULL;
9340
9341 total +=
9342 xmlXPathCompOpEvalLast(ctxt,
9343 &comp->steps[op->ch1],
9344 &last);
9345 /*
9346 * The nodeset should be in document order,
9347 * Keep only the last value
9348 */
9349 if ((ctxt->value != NULL) &&
9350 (ctxt->value->type == XPATH_NODESET) &&
9351 (ctxt->value->nodesetval != NULL) &&
9352 (ctxt->value->nodesetval->nodeTab != NULL) &&
9353 (ctxt->value->nodesetval->nodeNr > 1)) {
9354 ctxt->value->nodesetval->nodeTab[0] =
9355 ctxt->value->nodesetval->nodeTab[ctxt->
9356 value->
9357 nodesetval->
9358 nodeNr -
9359 1];
9360 ctxt->value->nodesetval->nodeNr = 1;
9361 }
9362 return (total);
9363 }
9364 }
9365
9366 if (op->ch1 != -1)
9367 total +=
9368 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9369 if (op->ch2 == -1)
9370 return (total);
9371 if (ctxt->value == NULL)
9372 return (total);
9373
9374 oldnode = ctxt->context->node;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009375
9376#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009377 /*
9378 * Hum are we filtering the result of an XPointer expression
9379 */
9380 if (ctxt->value->type == XPATH_LOCATIONSET) {
9381 xmlLocationSetPtr newlocset = NULL;
9382 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009383
Daniel Veillardf06307e2001-07-03 10:35:50 +00009384 /*
9385 * Extract the old locset, and then evaluate the result of the
9386 * expression for all the element in the locset. use it to grow
9387 * up a new locset.
9388 */
9389 CHECK_TYPE0(XPATH_LOCATIONSET);
9390 obj = valuePop(ctxt);
9391 oldlocset = obj->user;
9392 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009393
Daniel Veillardf06307e2001-07-03 10:35:50 +00009394 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
9395 ctxt->context->contextSize = 0;
9396 ctxt->context->proximityPosition = 0;
9397 if (op->ch2 != -1)
9398 total +=
9399 xmlXPathCompOpEval(ctxt,
9400 &comp->steps[op->ch2]);
9401 res = valuePop(ctxt);
9402 if (res != NULL)
9403 xmlXPathFreeObject(res);
9404 valuePush(ctxt, obj);
9405 CHECK_ERROR0;
9406 return (total);
9407 }
9408 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009409
Daniel Veillardf06307e2001-07-03 10:35:50 +00009410 for (i = 0; i < oldlocset->locNr; i++) {
9411 /*
9412 * Run the evaluation with a node list made of a
9413 * single item in the nodelocset.
9414 */
9415 ctxt->context->node = oldlocset->locTab[i]->user;
9416 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9417 valuePush(ctxt, tmp);
9418 ctxt->context->contextSize = oldlocset->locNr;
9419 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009420
Daniel Veillardf06307e2001-07-03 10:35:50 +00009421 if (op->ch2 != -1)
9422 total +=
9423 xmlXPathCompOpEval(ctxt,
9424 &comp->steps[op->ch2]);
9425 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009426
Daniel Veillardf06307e2001-07-03 10:35:50 +00009427 /*
9428 * The result of the evaluation need to be tested to
9429 * decided whether the filter succeeded or not
9430 */
9431 res = valuePop(ctxt);
9432 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9433 xmlXPtrLocationSetAdd(newlocset,
9434 xmlXPathObjectCopy
9435 (oldlocset->locTab[i]));
9436 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009437
Daniel Veillardf06307e2001-07-03 10:35:50 +00009438 /*
9439 * Cleanup
9440 */
9441 if (res != NULL)
9442 xmlXPathFreeObject(res);
9443 if (ctxt->value == tmp) {
9444 res = valuePop(ctxt);
9445 xmlXPathFreeObject(res);
9446 }
9447
9448 ctxt->context->node = NULL;
9449 }
9450
9451 /*
9452 * The result is used as the new evaluation locset.
9453 */
9454 xmlXPathFreeObject(obj);
9455 ctxt->context->node = NULL;
9456 ctxt->context->contextSize = -1;
9457 ctxt->context->proximityPosition = -1;
9458 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
9459 ctxt->context->node = oldnode;
9460 return (total);
9461 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009462#endif /* LIBXML_XPTR_ENABLED */
9463
Daniel Veillardf06307e2001-07-03 10:35:50 +00009464 /*
9465 * Extract the old set, and then evaluate the result of the
9466 * expression for all the element in the set. use it to grow
9467 * up a new set.
9468 */
9469 CHECK_TYPE0(XPATH_NODESET);
9470 obj = valuePop(ctxt);
9471 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00009472
Daniel Veillardf06307e2001-07-03 10:35:50 +00009473 oldnode = ctxt->context->node;
9474 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009475
Daniel Veillardf06307e2001-07-03 10:35:50 +00009476 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
9477 ctxt->context->contextSize = 0;
9478 ctxt->context->proximityPosition = 0;
9479 if (op->ch2 != -1)
9480 total +=
9481 xmlXPathCompOpEval(ctxt,
9482 &comp->steps[op->ch2]);
9483 res = valuePop(ctxt);
9484 if (res != NULL)
9485 xmlXPathFreeObject(res);
9486 valuePush(ctxt, obj);
9487 ctxt->context->node = oldnode;
9488 CHECK_ERROR0;
9489 } else {
9490 /*
9491 * Initialize the new set.
9492 */
9493 newset = xmlXPathNodeSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009494
Daniel Veillardf06307e2001-07-03 10:35:50 +00009495 for (i = 0; i < oldset->nodeNr; i++) {
9496 /*
9497 * Run the evaluation with a node list made of
9498 * a single item in the nodeset.
9499 */
9500 ctxt->context->node = oldset->nodeTab[i];
9501 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9502 valuePush(ctxt, tmp);
9503 ctxt->context->contextSize = oldset->nodeNr;
9504 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009505
Daniel Veillardf06307e2001-07-03 10:35:50 +00009506 if (op->ch2 != -1)
9507 total +=
9508 xmlXPathCompOpEval(ctxt,
9509 &comp->steps[op->ch2]);
9510 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009511
Daniel Veillardf06307e2001-07-03 10:35:50 +00009512 /*
9513 * The result of the evaluation need to be tested to
9514 * decided whether the filter succeeded or not
9515 */
9516 res = valuePop(ctxt);
9517 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9518 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
9519 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009520
Daniel Veillardf06307e2001-07-03 10:35:50 +00009521 /*
9522 * Cleanup
9523 */
9524 if (res != NULL)
9525 xmlXPathFreeObject(res);
9526 if (ctxt->value == tmp) {
9527 res = valuePop(ctxt);
9528 xmlXPathFreeObject(res);
9529 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009530
Daniel Veillardf06307e2001-07-03 10:35:50 +00009531 ctxt->context->node = NULL;
9532 }
9533
9534 /*
9535 * The result is used as the new evaluation set.
9536 */
9537 xmlXPathFreeObject(obj);
9538 ctxt->context->node = NULL;
9539 ctxt->context->contextSize = -1;
9540 ctxt->context->proximityPosition = -1;
9541 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
9542 }
9543 ctxt->context->node = oldnode;
9544 return (total);
9545 }
9546 case XPATH_OP_SORT:
9547 if (op->ch1 != -1)
9548 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9549 if ((ctxt->value != NULL) &&
9550 (ctxt->value->type == XPATH_NODESET) &&
9551 (ctxt->value->nodesetval != NULL))
9552 xmlXPathNodeSetSort(ctxt->value->nodesetval);
9553 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009554#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009555 case XPATH_OP_RANGETO:{
9556 xmlXPathObjectPtr range;
9557 xmlXPathObjectPtr res, obj;
9558 xmlXPathObjectPtr tmp;
9559 xmlLocationSetPtr newset = NULL;
9560 xmlNodeSetPtr oldset;
9561 int i;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009562
Daniel Veillardf06307e2001-07-03 10:35:50 +00009563 if (op->ch1 != -1)
9564 total +=
9565 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9566 if (op->ch2 == -1)
9567 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009568
Daniel Veillardf06307e2001-07-03 10:35:50 +00009569 CHECK_TYPE0(XPATH_NODESET);
9570 obj = valuePop(ctxt);
9571 oldset = obj->nodesetval;
9572 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009573
Daniel Veillardf06307e2001-07-03 10:35:50 +00009574 newset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009575
Daniel Veillardf06307e2001-07-03 10:35:50 +00009576 if (oldset != NULL) {
9577 for (i = 0; i < oldset->nodeNr; i++) {
9578 /*
9579 * Run the evaluation with a node list made of a single item
9580 * in the nodeset.
9581 */
9582 ctxt->context->node = oldset->nodeTab[i];
9583 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9584 valuePush(ctxt, tmp);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009585
Daniel Veillardf06307e2001-07-03 10:35:50 +00009586 if (op->ch2 != -1)
9587 total +=
9588 xmlXPathCompOpEval(ctxt,
9589 &comp->steps[op->ch2]);
9590 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009591
Daniel Veillardf06307e2001-07-03 10:35:50 +00009592 /*
9593 * The result of the evaluation need to be tested to
9594 * decided whether the filter succeeded or not
9595 */
9596 res = valuePop(ctxt);
9597 range =
9598 xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
9599 res);
9600 if (range != NULL) {
9601 xmlXPtrLocationSetAdd(newset, range);
9602 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009603
Daniel Veillardf06307e2001-07-03 10:35:50 +00009604 /*
9605 * Cleanup
9606 */
9607 if (res != NULL)
9608 xmlXPathFreeObject(res);
9609 if (ctxt->value == tmp) {
9610 res = valuePop(ctxt);
9611 xmlXPathFreeObject(res);
9612 }
9613
9614 ctxt->context->node = NULL;
9615 }
9616 }
9617
9618 /*
9619 * The result is used as the new evaluation set.
9620 */
9621 xmlXPathFreeObject(obj);
9622 ctxt->context->node = NULL;
9623 ctxt->context->contextSize = -1;
9624 ctxt->context->proximityPosition = -1;
9625 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
9626 return (total);
9627 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009628#endif /* LIBXML_XPTR_ENABLED */
9629 }
9630 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00009631 "XPath: unknown precompiled operation %d\n", op->op);
9632 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009633}
9634
9635/**
9636 * xmlXPathRunEval:
9637 * @ctxt: the XPath parser context with the compiled expression
9638 *
9639 * Evaluate the Precompiled XPath expression in the given context.
9640 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009641static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009642xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
9643 xmlXPathCompExprPtr comp;
9644
9645 if ((ctxt == NULL) || (ctxt->comp == NULL))
9646 return;
9647
9648 if (ctxt->valueTab == NULL) {
9649 /* Allocate the value stack */
9650 ctxt->valueTab = (xmlXPathObjectPtr *)
9651 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
9652 if (ctxt->valueTab == NULL) {
9653 xmlFree(ctxt);
9654 xmlGenericError(xmlGenericErrorContext,
9655 "xmlXPathRunEval: out of memory\n");
9656 return;
9657 }
9658 ctxt->valueNr = 0;
9659 ctxt->valueMax = 10;
9660 ctxt->value = NULL;
9661 }
9662 comp = ctxt->comp;
9663 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
9664}
9665
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009666/************************************************************************
9667 * *
9668 * Public interfaces *
9669 * *
9670 ************************************************************************/
9671
9672/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009673 * xmlXPathEvalPredicate:
9674 * @ctxt: the XPath context
9675 * @res: the Predicate Expression evaluation result
9676 *
9677 * Evaluate a predicate result for the current node.
9678 * A PredicateExpr is evaluated by evaluating the Expr and converting
9679 * the result to a boolean. If the result is a number, the result will
9680 * be converted to true if the number is equal to the position of the
9681 * context node in the context node list (as returned by the position
9682 * function) and will be converted to false otherwise; if the result
9683 * is not a number, then the result will be converted as if by a call
9684 * to the boolean function.
9685 *
9686 * Return 1 if predicate is true, 0 otherwise
9687 */
9688int
9689xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
9690 if (res == NULL) return(0);
9691 switch (res->type) {
9692 case XPATH_BOOLEAN:
9693 return(res->boolval);
9694 case XPATH_NUMBER:
9695 return(res->floatval == ctxt->proximityPosition);
9696 case XPATH_NODESET:
9697 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009698 if (res->nodesetval == NULL)
9699 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009700 return(res->nodesetval->nodeNr != 0);
9701 case XPATH_STRING:
9702 return((res->stringval != NULL) &&
9703 (xmlStrlen(res->stringval) != 0));
9704 default:
9705 STRANGE
9706 }
9707 return(0);
9708}
9709
9710/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009711 * xmlXPathEvaluatePredicateResult:
9712 * @ctxt: the XPath Parser context
9713 * @res: the Predicate Expression evaluation result
9714 *
9715 * Evaluate a predicate result for the current node.
9716 * A PredicateExpr is evaluated by evaluating the Expr and converting
9717 * the result to a boolean. If the result is a number, the result will
9718 * be converted to true if the number is equal to the position of the
9719 * context node in the context node list (as returned by the position
9720 * function) and will be converted to false otherwise; if the result
9721 * is not a number, then the result will be converted as if by a call
9722 * to the boolean function.
9723 *
9724 * Return 1 if predicate is true, 0 otherwise
9725 */
9726int
9727xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
9728 xmlXPathObjectPtr res) {
9729 if (res == NULL) return(0);
9730 switch (res->type) {
9731 case XPATH_BOOLEAN:
9732 return(res->boolval);
9733 case XPATH_NUMBER:
9734 return(res->floatval == ctxt->context->proximityPosition);
9735 case XPATH_NODESET:
9736 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00009737 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00009738 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009739 return(res->nodesetval->nodeNr != 0);
9740 case XPATH_STRING:
9741 return((res->stringval != NULL) &&
9742 (xmlStrlen(res->stringval) != 0));
9743 default:
9744 STRANGE
9745 }
9746 return(0);
9747}
9748
9749/**
9750 * xmlXPathCompile:
9751 * @str: the XPath expression
9752 *
9753 * Compile an XPath expression
9754 *
9755 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9756 * the caller has to free the object.
9757 */
9758xmlXPathCompExprPtr
9759xmlXPathCompile(const xmlChar *str) {
9760 xmlXPathParserContextPtr ctxt;
9761 xmlXPathCompExprPtr comp;
9762
9763 xmlXPathInit();
9764
9765 ctxt = xmlXPathNewParserContext(str, NULL);
9766 xmlXPathCompileExpr(ctxt);
9767
Daniel Veillard40af6492001-04-22 08:50:55 +00009768 if (*ctxt->cur != 0) {
9769 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9770 comp = NULL;
9771 } else {
9772 comp = ctxt->comp;
9773 ctxt->comp = NULL;
9774 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009775 xmlXPathFreeParserContext(ctxt);
Daniel Veillardf06307e2001-07-03 10:35:50 +00009776#ifdef DEBUG_EVAL_COUNTS
9777 if (comp != NULL) {
9778 comp->string = xmlStrdup(str);
9779 comp->nb = 0;
9780 }
9781#endif
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009782 return(comp);
9783}
9784
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009785/**
9786 * xmlXPathCompiledEval:
9787 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00009788 * @ctx: the XPath context
9789 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009790 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00009791 *
9792 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9793 * the caller has to free the object.
9794 */
9795xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009796xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00009797 xmlXPathParserContextPtr ctxt;
9798 xmlXPathObjectPtr res, tmp, init = NULL;
9799 int stack = 0;
9800
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009801 if ((comp == NULL) || (ctx == NULL))
9802 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00009803 xmlXPathInit();
9804
9805 CHECK_CONTEXT(ctx)
9806
Daniel Veillardf06307e2001-07-03 10:35:50 +00009807#ifdef DEBUG_EVAL_COUNTS
9808 comp->nb++;
9809 if ((comp->string != NULL) && (comp->nb > 100)) {
9810 fprintf(stderr, "100 x %s\n", comp->string);
9811 comp->nb = 0;
9812 }
9813#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009814 ctxt = xmlXPathCompParserContext(comp, ctx);
9815 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009816
9817 if (ctxt->value == NULL) {
9818 xmlGenericError(xmlGenericErrorContext,
9819 "xmlXPathEval: evaluation failed\n");
9820 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00009821 } else {
9822 res = valuePop(ctxt);
9823 }
9824
Daniel Veillardf06307e2001-07-03 10:35:50 +00009825
Owen Taylor3473f882001-02-23 17:55:21 +00009826 do {
9827 tmp = valuePop(ctxt);
9828 if (tmp != NULL) {
9829 if (tmp != init)
9830 stack++;
9831 xmlXPathFreeObject(tmp);
9832 }
9833 } while (tmp != NULL);
9834 if ((stack != 0) && (res != NULL)) {
9835 xmlGenericError(xmlGenericErrorContext,
9836 "xmlXPathEval: %d object left on the stack\n",
9837 stack);
9838 }
9839 if (ctxt->error != XPATH_EXPRESSION_OK) {
9840 xmlXPathFreeObject(res);
9841 res = NULL;
9842 }
9843
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009844
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009845 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009846 xmlXPathFreeParserContext(ctxt);
9847 return(res);
9848}
9849
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009850/**
9851 * xmlXPathEvalExpr:
9852 * @ctxt: the XPath Parser context
9853 *
9854 * Parse and evaluate an XPath expression in the given context,
9855 * then push the result on the context stack
9856 */
9857void
9858xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
9859 xmlXPathCompileExpr(ctxt);
9860 xmlXPathRunEval(ctxt);
9861}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009862
9863/**
9864 * xmlXPathEval:
9865 * @str: the XPath expression
9866 * @ctx: the XPath context
9867 *
9868 * Evaluate the XPath Location Path in the given context.
9869 *
9870 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9871 * the caller has to free the object.
9872 */
9873xmlXPathObjectPtr
9874xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
9875 xmlXPathParserContextPtr ctxt;
9876 xmlXPathObjectPtr res, tmp, init = NULL;
9877 int stack = 0;
9878
9879 xmlXPathInit();
9880
9881 CHECK_CONTEXT(ctx)
9882
9883 ctxt = xmlXPathNewParserContext(str, ctx);
9884 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009885
9886 if (ctxt->value == NULL) {
9887 xmlGenericError(xmlGenericErrorContext,
9888 "xmlXPathEval: evaluation failed\n");
9889 res = NULL;
9890 } else if (*ctxt->cur != 0) {
9891 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9892 res = NULL;
9893 } else {
9894 res = valuePop(ctxt);
9895 }
9896
9897 do {
9898 tmp = valuePop(ctxt);
9899 if (tmp != NULL) {
9900 if (tmp != init)
9901 stack++;
9902 xmlXPathFreeObject(tmp);
9903 }
9904 } while (tmp != NULL);
9905 if ((stack != 0) && (res != NULL)) {
9906 xmlGenericError(xmlGenericErrorContext,
9907 "xmlXPathEval: %d object left on the stack\n",
9908 stack);
9909 }
9910 if (ctxt->error != XPATH_EXPRESSION_OK) {
9911 xmlXPathFreeObject(res);
9912 res = NULL;
9913 }
9914
Owen Taylor3473f882001-02-23 17:55:21 +00009915 xmlXPathFreeParserContext(ctxt);
9916 return(res);
9917}
9918
9919/**
9920 * xmlXPathEvalExpression:
9921 * @str: the XPath expression
9922 * @ctxt: the XPath context
9923 *
9924 * Evaluate the XPath expression in the given context.
9925 *
9926 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
9927 * the caller has to free the object.
9928 */
9929xmlXPathObjectPtr
9930xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
9931 xmlXPathParserContextPtr pctxt;
9932 xmlXPathObjectPtr res, tmp;
9933 int stack = 0;
9934
9935 xmlXPathInit();
9936
9937 CHECK_CONTEXT(ctxt)
9938
9939 pctxt = xmlXPathNewParserContext(str, ctxt);
9940 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009941
9942 if (*pctxt->cur != 0) {
9943 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9944 res = NULL;
9945 } else {
9946 res = valuePop(pctxt);
9947 }
9948 do {
9949 tmp = valuePop(pctxt);
9950 if (tmp != NULL) {
9951 xmlXPathFreeObject(tmp);
9952 stack++;
9953 }
9954 } while (tmp != NULL);
9955 if ((stack != 0) && (res != NULL)) {
9956 xmlGenericError(xmlGenericErrorContext,
9957 "xmlXPathEvalExpression: %d object left on the stack\n",
9958 stack);
9959 }
9960 xmlXPathFreeParserContext(pctxt);
9961 return(res);
9962}
9963
9964/**
9965 * xmlXPathRegisterAllFunctions:
9966 * @ctxt: the XPath context
9967 *
9968 * Registers all default XPath functions in this context
9969 */
9970void
9971xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
9972{
9973 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
9974 xmlXPathBooleanFunction);
9975 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
9976 xmlXPathCeilingFunction);
9977 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
9978 xmlXPathCountFunction);
9979 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
9980 xmlXPathConcatFunction);
9981 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
9982 xmlXPathContainsFunction);
9983 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
9984 xmlXPathIdFunction);
9985 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
9986 xmlXPathFalseFunction);
9987 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
9988 xmlXPathFloorFunction);
9989 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
9990 xmlXPathLastFunction);
9991 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
9992 xmlXPathLangFunction);
9993 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
9994 xmlXPathLocalNameFunction);
9995 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
9996 xmlXPathNotFunction);
9997 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
9998 xmlXPathNameFunction);
9999 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
10000 xmlXPathNamespaceURIFunction);
10001 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
10002 xmlXPathNormalizeFunction);
10003 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
10004 xmlXPathNumberFunction);
10005 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
10006 xmlXPathPositionFunction);
10007 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
10008 xmlXPathRoundFunction);
10009 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
10010 xmlXPathStringFunction);
10011 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
10012 xmlXPathStringLengthFunction);
10013 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
10014 xmlXPathStartsWithFunction);
10015 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
10016 xmlXPathSubstringFunction);
10017 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
10018 xmlXPathSubstringBeforeFunction);
10019 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
10020 xmlXPathSubstringAfterFunction);
10021 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
10022 xmlXPathSumFunction);
10023 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
10024 xmlXPathTrueFunction);
10025 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
10026 xmlXPathTranslateFunction);
10027}
10028
10029#endif /* LIBXML_XPATH_ENABLED */