blob: 51899ea3c8b2b500a27497cd288319e80c5826bb [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++)
1740 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;
1837 ret->nodesetval = xmlXPathNodeSetCreate(val);
1838 return(ret);
1839}
1840
1841/**
1842 * xmlXPathNewNodeSetList:
1843 * @val: an existing NodeSet
1844 *
1845 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1846 * it with the Nodeset @val
1847 *
1848 * Returns the newly created object.
1849 */
1850xmlXPathObjectPtr
1851xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1852 xmlXPathObjectPtr ret;
1853 int i;
1854
1855 if (val == NULL)
1856 ret = NULL;
1857 else if (val->nodeTab == NULL)
1858 ret = xmlXPathNewNodeSet(NULL);
1859 else
1860 {
1861 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1862 for (i = 1; i < val->nodeNr; ++i)
1863 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1864 }
1865
1866 return(ret);
1867}
1868
1869/**
1870 * xmlXPathWrapNodeSet:
1871 * @val: the NodePtr value
1872 *
1873 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1874 *
1875 * Returns the newly created object.
1876 */
1877xmlXPathObjectPtr
1878xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1879 xmlXPathObjectPtr ret;
1880
1881 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1882 if (ret == NULL) {
1883 xmlGenericError(xmlGenericErrorContext,
1884 "xmlXPathWrapNodeSet: out of memory\n");
1885 return(NULL);
1886 }
1887 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1888 ret->type = XPATH_NODESET;
1889 ret->nodesetval = val;
1890 return(ret);
1891}
1892
1893/**
1894 * xmlXPathFreeNodeSetList:
1895 * @obj: an existing NodeSetList object
1896 *
1897 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1898 * the list contrary to xmlXPathFreeObject().
1899 */
1900void
1901xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1902 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001903 xmlFree(obj);
1904}
1905
Thomas Broyerf06a3d82001-07-16 04:52:57 +00001906/**
1907 * xmlXPathDifference:
1908 * @nodes1: a node-set
1909 * @nodes2: a node-set
1910 *
1911 * Implements the EXSLT - Sets difference() function:
1912 * node-set set:difference (node-set, node-set)
1913 *
1914 * Returns the difference between the two node sets, or nodes1 if
1915 * nodes2 is empty
1916 */
1917xmlNodeSetPtr
1918xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
1919 xmlNodeSetPtr ret;
1920 int i, l1;
1921 xmlNodePtr cur;
1922
1923 if (xmlXPathNodeSetIsEmpty(nodes2))
1924 return(nodes1);
1925
1926 ret = xmlXPathNodeSetCreate(NULL);
1927 if (xmlXPathNodeSetIsEmpty(nodes1))
1928 return(ret);
1929
1930 l1 = xmlXPathNodeSetGetLength(nodes1);
1931
1932 for (i = 0; i < l1; i++) {
1933 cur = xmlXPathNodeSetItem(nodes1, i);
1934 if (!xmlXPathNodeSetContains(nodes2, cur))
1935 xmlXPathNodeSetAddUnique(ret, cur);
1936 }
1937 return(ret);
1938}
1939
1940/**
1941 * xmlXPathIntersection:
1942 * @nodes1: a node-set
1943 * @nodes2: a node-set
1944 *
1945 * Implements the EXSLT - Sets intersection() function:
1946 * node-set set:intersection (node-set, node-set)
1947 *
1948 * Returns a node set comprising the nodes that are within both the
1949 * node sets passed as arguments
1950 */
1951xmlNodeSetPtr
1952xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
1953 xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
1954 int i, l1;
1955 xmlNodePtr cur;
1956
1957 if (xmlXPathNodeSetIsEmpty(nodes1))
1958 return(ret);
1959 if (xmlXPathNodeSetIsEmpty(nodes2))
1960 return(ret);
1961
1962 l1 = xmlXPathNodeSetGetLength(nodes1);
1963
1964 for (i = 0; i < l1; i++) {
1965 cur = xmlXPathNodeSetItem(nodes1, i);
1966 if (xmlXPathNodeSetContains(nodes2, cur))
1967 xmlXPathNodeSetAddUnique(ret, cur);
1968 }
1969 return(ret);
1970}
1971
1972/**
1973 * xmlXPathDistinctSorted:
1974 * @nodes: a node-set, sorted by document order
1975 *
1976 * Implements the EXSLT - Sets distinct() function:
1977 * node-set set:distinct (node-set)
1978 *
1979 * Returns a subset of the nodes contained in @nodes, or @nodes if
1980 * it is empty
1981 */
1982xmlNodeSetPtr
1983xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
1984 xmlNodeSetPtr ret;
1985 xmlHashTablePtr hash;
1986 int i, l;
1987 xmlChar * strval;
1988 xmlNodePtr cur;
1989
1990 if (xmlXPathNodeSetIsEmpty(nodes))
1991 return(nodes);
1992
1993 ret = xmlXPathNodeSetCreate(NULL);
1994 l = xmlXPathNodeSetGetLength(nodes);
1995 hash = xmlHashCreate (l);
1996 for (i = 0; i < l; i++) {
1997 cur = xmlXPathNodeSetItem(nodes, i);
1998 strval = xmlXPathCastNodeToString(cur);
1999 if (xmlHashLookup(hash, strval) == NULL) {
2000 xmlHashAddEntry(hash, strval, strval);
2001 xmlXPathNodeSetAddUnique(ret, cur);
2002 } else {
2003 xmlFree(strval);
2004 }
2005 }
2006 xmlHashFree(hash, (xmlHashDeallocator) xmlFree);
2007 return(ret);
2008}
2009
2010/**
2011 * xmlXPathDistinct:
2012 * @nodes: a node-set
2013 *
2014 * Implements the EXSLT - Sets distinct() function:
2015 * node-set set:distinct (node-set)
2016 * @nodes is sorted by document order, then #exslSetsDistinctSorted
2017 * is called with the sorted node-set
2018 *
2019 * Returns a subset of the nodes contained in @nodes, or @nodes if
2020 * it is empty
2021 */
2022xmlNodeSetPtr
2023xmlXPathDistinct (xmlNodeSetPtr nodes) {
2024 if (xmlXPathNodeSetIsEmpty(nodes))
2025 return(nodes);
2026
2027 xmlXPathNodeSetSort(nodes);
2028 return(xmlXPathDistinctSorted(nodes));
2029}
2030
2031/**
2032 * xmlXPathHasSameNodes:
2033 * @nodes1: a node-set
2034 * @nodes2: a node-set
2035 *
2036 * Implements the EXSLT - Sets has-same-nodes function:
2037 * boolean set:has-same-node(node-set, node-set)
2038 *
2039 * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
2040 * otherwise
2041 */
2042int
2043xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2044 int i, l;
2045 xmlNodePtr cur;
2046
2047 if (xmlXPathNodeSetIsEmpty(nodes1) ||
2048 xmlXPathNodeSetIsEmpty(nodes2))
2049 return(0);
2050
2051 l = xmlXPathNodeSetGetLength(nodes1);
2052 for (i = 0; i < l; i++) {
2053 cur = xmlXPathNodeSetItem(nodes1, i);
2054 if (xmlXPathNodeSetContains(nodes2, cur))
2055 return(1);
2056 }
2057 return(0);
2058}
2059
2060/**
2061 * xmlXPathNodeLeadingSorted:
2062 * @nodes: a node-set, sorted by document order
2063 * @node: a node
2064 *
2065 * Implements the EXSLT - Sets leading() function:
2066 * node-set set:leading (node-set, node-set)
2067 *
2068 * Returns the nodes in @nodes that precede @node in document order,
2069 * @nodes if @node is NULL or an empty node-set if @nodes
2070 * doesn't contain @node
2071 */
2072xmlNodeSetPtr
2073xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
2074 int i, l;
2075 xmlNodePtr cur;
2076 xmlNodeSetPtr ret;
2077
2078 if (node == NULL)
2079 return(nodes);
2080
2081 ret = xmlXPathNodeSetCreate(NULL);
2082 if (xmlXPathNodeSetIsEmpty(nodes) ||
2083 (!xmlXPathNodeSetContains(nodes, node)))
2084 return(ret);
2085
2086 l = xmlXPathNodeSetGetLength(nodes);
2087 for (i = 0; i < l; i++) {
2088 cur = xmlXPathNodeSetItem(nodes, i);
2089 if (cur == node)
2090 break;
2091 xmlXPathNodeSetAddUnique(ret, cur);
2092 }
2093 return(ret);
2094}
2095
2096/**
2097 * xmlXPathNodeLeading:
2098 * @nodes: a node-set
2099 * @node: a node
2100 *
2101 * Implements the EXSLT - Sets leading() function:
2102 * node-set set:leading (node-set, node-set)
2103 * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
2104 * is called.
2105 *
2106 * Returns the nodes in @nodes that precede @node in document order,
2107 * @nodes if @node is NULL or an empty node-set if @nodes
2108 * doesn't contain @node
2109 */
2110xmlNodeSetPtr
2111xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
2112 xmlXPathNodeSetSort(nodes);
2113 return(xmlXPathNodeLeadingSorted(nodes, node));
2114}
2115
2116/**
2117 * xmlXPathLeadingSorted:
2118 * @nodes1: a node-set, sorted by document order
2119 * @nodes2: a node-set, sorted by document order
2120 *
2121 * Implements the EXSLT - Sets leading() function:
2122 * node-set set:leading (node-set, node-set)
2123 *
2124 * Returns the nodes in @nodes1 that precede the first node in @nodes2
2125 * in document order, @nodes1 if @nodes2 is NULL or empty or
2126 * an empty node-set if @nodes1 doesn't contain @nodes2
2127 */
2128xmlNodeSetPtr
2129xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2130 if (xmlXPathNodeSetIsEmpty(nodes2))
2131 return(nodes1);
2132 return(xmlXPathNodeLeadingSorted(nodes1,
2133 xmlXPathNodeSetItem(nodes2, 1)));
2134}
2135
2136/**
2137 * xmlXPathLeading:
2138 * @nodes1: a node-set
2139 * @nodes2: a node-set
2140 *
2141 * Implements the EXSLT - Sets leading() function:
2142 * node-set set:leading (node-set, node-set)
2143 * @nodes1 and @nodes2 are sorted by document order, then
2144 * #exslSetsLeadingSorted is called.
2145 *
2146 * Returns the nodes in @nodes1 that precede the first node in @nodes2
2147 * in document order, @nodes1 if @nodes2 is NULL or empty or
2148 * an empty node-set if @nodes1 doesn't contain @nodes2
2149 */
2150xmlNodeSetPtr
2151xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2152 if (xmlXPathNodeSetIsEmpty(nodes2))
2153 return(nodes1);
2154 if (xmlXPathNodeSetIsEmpty(nodes1))
2155 return(xmlXPathNodeSetCreate(NULL));
2156 xmlXPathNodeSetSort(nodes1);
2157 xmlXPathNodeSetSort(nodes2);
2158 return(xmlXPathNodeLeadingSorted(nodes1,
2159 xmlXPathNodeSetItem(nodes2, 1)));
2160}
2161
2162/**
2163 * xmlXPathNodeTrailingSorted:
2164 * @nodes: a node-set, sorted by document order
2165 * @node: a node
2166 *
2167 * Implements the EXSLT - Sets trailing() function:
2168 * node-set set:trailing (node-set, node-set)
2169 *
2170 * Returns the nodes in @nodes that follow @node in document order,
2171 * @nodes if @node is NULL or an empty node-set if @nodes
2172 * doesn't contain @node
2173 */
2174xmlNodeSetPtr
2175xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
2176 int i, l;
2177 xmlNodePtr cur;
2178 xmlNodeSetPtr ret;
2179
2180 if (node == NULL)
2181 return(nodes);
2182
2183 ret = xmlXPathNodeSetCreate(NULL);
2184 if (xmlXPathNodeSetIsEmpty(nodes) ||
2185 (!xmlXPathNodeSetContains(nodes, node)))
2186 return(ret);
2187
2188 l = xmlXPathNodeSetGetLength(nodes);
Thomas Broyerf186c822001-07-31 23:30:37 +00002189 for (i = l; i > 0; i--) {
Thomas Broyerf06a3d82001-07-16 04:52:57 +00002190 cur = xmlXPathNodeSetItem(nodes, i);
2191 if (cur == node)
2192 break;
2193 xmlXPathNodeSetAddUnique(ret, cur);
2194 }
2195 return(ret);
2196}
2197
2198/**
2199 * xmlXPathNodeTrailing:
2200 * @nodes: a node-set
2201 * @node: a node
2202 *
2203 * Implements the EXSLT - Sets trailing() function:
2204 * node-set set:trailing (node-set, node-set)
2205 * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
2206 * is called.
2207 *
2208 * Returns the nodes in @nodes that follow @node in document order,
2209 * @nodes if @node is NULL or an empty node-set if @nodes
2210 * doesn't contain @node
2211 */
2212xmlNodeSetPtr
2213xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
2214 xmlXPathNodeSetSort(nodes);
2215 return(xmlXPathNodeTrailingSorted(nodes, node));
2216}
2217
2218/**
2219 * xmlXPathTrailingSorted:
2220 * @nodes1: a node-set, sorted by document order
2221 * @nodes2: a node-set, sorted by document order
2222 *
2223 * Implements the EXSLT - Sets trailing() function:
2224 * node-set set:trailing (node-set, node-set)
2225 *
2226 * Returns the nodes in @nodes1 that follow the first node in @nodes2
2227 * in document order, @nodes1 if @nodes2 is NULL or empty or
2228 * an empty node-set if @nodes1 doesn't contain @nodes2
2229 */
2230xmlNodeSetPtr
2231xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2232 if (xmlXPathNodeSetIsEmpty(nodes2))
2233 return(nodes1);
2234 return(xmlXPathNodeTrailingSorted(nodes1,
2235 xmlXPathNodeSetItem(nodes2, 0)));
2236}
2237
2238/**
2239 * xmlXPathTrailing:
2240 * @nodes1: a node-set
2241 * @nodes2: a node-set
2242 *
2243 * Implements the EXSLT - Sets trailing() function:
2244 * node-set set:trailing (node-set, node-set)
2245 * @nodes1 and @nodes2 are sorted by document order, then
2246 * #xmlXPathTrailingSorted is called.
2247 *
2248 * Returns the nodes in @nodes1 that follow the first node in @nodes2
2249 * in document order, @nodes1 if @nodes2 is NULL or empty or
2250 * an empty node-set if @nodes1 doesn't contain @nodes2
2251 */
2252xmlNodeSetPtr
2253xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2254 if (xmlXPathNodeSetIsEmpty(nodes2))
2255 return(nodes1);
2256 if (xmlXPathNodeSetIsEmpty(nodes1))
2257 return(xmlXPathNodeSetCreate(NULL));
2258 xmlXPathNodeSetSort(nodes1);
2259 xmlXPathNodeSetSort(nodes2);
2260 return(xmlXPathNodeTrailingSorted(nodes1,
2261 xmlXPathNodeSetItem(nodes2, 0)));
2262}
2263
Owen Taylor3473f882001-02-23 17:55:21 +00002264/************************************************************************
2265 * *
2266 * Routines to handle extra functions *
2267 * *
2268 ************************************************************************/
2269
2270/**
2271 * xmlXPathRegisterFunc:
2272 * @ctxt: the XPath context
2273 * @name: the function name
2274 * @f: the function implementation or NULL
2275 *
2276 * Register a new function. If @f is NULL it unregisters the function
2277 *
2278 * Returns 0 in case of success, -1 in case of error
2279 */
2280int
2281xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
2282 xmlXPathFunction f) {
2283 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
2284}
2285
2286/**
2287 * xmlXPathRegisterFuncNS:
2288 * @ctxt: the XPath context
2289 * @name: the function name
2290 * @ns_uri: the function namespace URI
2291 * @f: the function implementation or NULL
2292 *
2293 * Register a new function. If @f is NULL it unregisters the function
2294 *
2295 * Returns 0 in case of success, -1 in case of error
2296 */
2297int
2298xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2299 const xmlChar *ns_uri, xmlXPathFunction f) {
2300 if (ctxt == NULL)
2301 return(-1);
2302 if (name == NULL)
2303 return(-1);
2304
2305 if (ctxt->funcHash == NULL)
2306 ctxt->funcHash = xmlHashCreate(0);
2307 if (ctxt->funcHash == NULL)
2308 return(-1);
2309 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
2310}
2311
2312/**
Thomas Broyerba4ad322001-07-26 16:55:21 +00002313 * xmlXPathRegisterFuncLookup:
2314 * @ctxt: the XPath context
2315 * @f: the lookup function
2316 * @data: the lookup data
2317 *
2318 * Registers an external mecanism to do function lookup.
2319 */
2320void
2321xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
2322 xmlXPathFuncLookupFunc f,
2323 void *funcCtxt) {
2324 if (ctxt == NULL)
2325 return;
2326 ctxt->funcLookupFunc = (void *) f;
2327 ctxt->funcLookupData = funcCtxt;
2328}
2329
2330/**
Owen Taylor3473f882001-02-23 17:55:21 +00002331 * xmlXPathFunctionLookup:
2332 * @ctxt: the XPath context
2333 * @name: the function name
2334 *
2335 * Search in the Function array of the context for the given
2336 * function.
2337 *
2338 * Returns the xmlXPathFunction or NULL if not found
2339 */
2340xmlXPathFunction
2341xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
Thomas Broyerba4ad322001-07-26 16:55:21 +00002342 if (ctxt == NULL)
2343 return (NULL);
2344
2345 if (ctxt->funcLookupFunc != NULL) {
2346 xmlXPathFunction ret;
2347
2348 ret = ((xmlXPathFuncLookupFunc) ctxt->funcLookupFunc)
2349 (ctxt->funcLookupData, name, NULL);
2350 if (ret != NULL)
2351 return(ret);
2352 }
Owen Taylor3473f882001-02-23 17:55:21 +00002353 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
2354}
2355
2356/**
2357 * xmlXPathFunctionLookupNS:
2358 * @ctxt: the XPath context
2359 * @name: the function name
2360 * @ns_uri: the function namespace URI
2361 *
2362 * Search in the Function array of the context for the given
2363 * function.
2364 *
2365 * Returns the xmlXPathFunction or NULL if not found
2366 */
2367xmlXPathFunction
2368xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2369 const xmlChar *ns_uri) {
2370 if (ctxt == NULL)
2371 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002372 if (name == NULL)
2373 return(NULL);
2374
Thomas Broyerba4ad322001-07-26 16:55:21 +00002375 if (ctxt->funcLookupFunc != NULL) {
2376 xmlXPathFunction ret;
2377
2378 ret = ((xmlXPathFuncLookupFunc) ctxt->funcLookupFunc)
2379 (ctxt->funcLookupData, name, ns_uri);
2380 if (ret != NULL)
2381 return(ret);
2382 }
2383
2384 if (ctxt->funcHash == NULL)
2385 return(NULL);
2386
Owen Taylor3473f882001-02-23 17:55:21 +00002387 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
2388}
2389
2390/**
2391 * xmlXPathRegisteredFuncsCleanup:
2392 * @ctxt: the XPath context
2393 *
2394 * Cleanup the XPath context data associated to registered functions
2395 */
2396void
2397xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
2398 if (ctxt == NULL)
2399 return;
2400
2401 xmlHashFree(ctxt->funcHash, NULL);
2402 ctxt->funcHash = NULL;
2403}
2404
2405/************************************************************************
2406 * *
2407 * Routines to handle Variable *
2408 * *
2409 ************************************************************************/
2410
2411/**
2412 * xmlXPathRegisterVariable:
2413 * @ctxt: the XPath context
2414 * @name: the variable name
2415 * @value: the variable value or NULL
2416 *
2417 * Register a new variable value. If @value is NULL it unregisters
2418 * the variable
2419 *
2420 * Returns 0 in case of success, -1 in case of error
2421 */
2422int
2423xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
2424 xmlXPathObjectPtr value) {
2425 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
2426}
2427
2428/**
2429 * xmlXPathRegisterVariableNS:
2430 * @ctxt: the XPath context
2431 * @name: the variable name
2432 * @ns_uri: the variable namespace URI
2433 * @value: the variable value or NULL
2434 *
2435 * Register a new variable value. If @value is NULL it unregisters
2436 * the variable
2437 *
2438 * Returns 0 in case of success, -1 in case of error
2439 */
2440int
2441xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2442 const xmlChar *ns_uri,
2443 xmlXPathObjectPtr value) {
2444 if (ctxt == NULL)
2445 return(-1);
2446 if (name == NULL)
2447 return(-1);
2448
2449 if (ctxt->varHash == NULL)
2450 ctxt->varHash = xmlHashCreate(0);
2451 if (ctxt->varHash == NULL)
2452 return(-1);
2453 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
2454 (void *) value,
2455 (xmlHashDeallocator)xmlXPathFreeObject));
2456}
2457
2458/**
2459 * xmlXPathRegisterVariableLookup:
2460 * @ctxt: the XPath context
2461 * @f: the lookup function
2462 * @data: the lookup data
2463 *
2464 * register an external mechanism to do variable lookup
2465 */
2466void
2467xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
2468 xmlXPathVariableLookupFunc f, void *data) {
2469 if (ctxt == NULL)
2470 return;
2471 ctxt->varLookupFunc = (void *) f;
2472 ctxt->varLookupData = data;
2473}
2474
2475/**
2476 * xmlXPathVariableLookup:
2477 * @ctxt: the XPath context
2478 * @name: the variable name
2479 *
2480 * Search in the Variable array of the context for the given
2481 * variable value.
2482 *
Daniel Veillard73c9c042001-07-05 20:02:54 +00002483 * Returns a copy of the value or NULL if not found
Owen Taylor3473f882001-02-23 17:55:21 +00002484 */
2485xmlXPathObjectPtr
2486xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
2487 if (ctxt == NULL)
2488 return(NULL);
2489
2490 if (ctxt->varLookupFunc != NULL) {
2491 xmlXPathObjectPtr ret;
2492
2493 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
2494 (ctxt->varLookupData, name, NULL);
2495 if (ret != NULL) return(ret);
2496 }
2497 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
2498}
2499
2500/**
2501 * xmlXPathVariableLookupNS:
2502 * @ctxt: the XPath context
2503 * @name: the variable name
2504 * @ns_uri: the variable namespace URI
2505 *
2506 * Search in the Variable array of the context for the given
Daniel Veillard73c9c042001-07-05 20:02:54 +00002507 * variable value.
Owen Taylor3473f882001-02-23 17:55:21 +00002508 *
Daniel Veillard73c9c042001-07-05 20:02:54 +00002509 * Returns the a copy of the value or NULL if not found
Owen Taylor3473f882001-02-23 17:55:21 +00002510 */
2511xmlXPathObjectPtr
2512xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2513 const xmlChar *ns_uri) {
2514 if (ctxt == NULL)
2515 return(NULL);
2516
2517 if (ctxt->varLookupFunc != NULL) {
2518 xmlXPathObjectPtr ret;
2519
2520 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
2521 (ctxt->varLookupData, name, ns_uri);
2522 if (ret != NULL) return(ret);
2523 }
2524
2525 if (ctxt->varHash == NULL)
2526 return(NULL);
2527 if (name == NULL)
2528 return(NULL);
2529
Daniel Veillard8c357d52001-07-03 23:43:33 +00002530 return(xmlXPathObjectCopy((xmlXPathObjectPtr)
2531 xmlHashLookup2(ctxt->varHash, name, ns_uri)));
Owen Taylor3473f882001-02-23 17:55:21 +00002532}
2533
2534/**
2535 * xmlXPathRegisteredVariablesCleanup:
2536 * @ctxt: the XPath context
2537 *
2538 * Cleanup the XPath context data associated to registered variables
2539 */
2540void
2541xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
2542 if (ctxt == NULL)
2543 return;
2544
Daniel Veillard76d66f42001-05-16 21:05:17 +00002545 xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject);
Owen Taylor3473f882001-02-23 17:55:21 +00002546 ctxt->varHash = NULL;
2547}
2548
2549/**
2550 * xmlXPathRegisterNs:
2551 * @ctxt: the XPath context
2552 * @prefix: the namespace prefix
2553 * @ns_uri: the namespace name
2554 *
2555 * Register a new namespace. If @ns_uri is NULL it unregisters
2556 * the namespace
2557 *
2558 * Returns 0 in case of success, -1 in case of error
2559 */
2560int
2561xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
2562 const xmlChar *ns_uri) {
2563 if (ctxt == NULL)
2564 return(-1);
2565 if (prefix == NULL)
2566 return(-1);
2567
2568 if (ctxt->nsHash == NULL)
2569 ctxt->nsHash = xmlHashCreate(10);
2570 if (ctxt->nsHash == NULL)
2571 return(-1);
2572 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
2573 (xmlHashDeallocator)xmlFree));
2574}
2575
2576/**
2577 * xmlXPathNsLookup:
2578 * @ctxt: the XPath context
2579 * @prefix: the namespace prefix value
2580 *
2581 * Search in the namespace declaration array of the context for the given
2582 * namespace name associated to the given prefix
2583 *
2584 * Returns the value or NULL if not found
2585 */
2586const xmlChar *
2587xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
2588 if (ctxt == NULL)
2589 return(NULL);
2590 if (prefix == NULL)
2591 return(NULL);
2592
2593#ifdef XML_XML_NAMESPACE
2594 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
2595 return(XML_XML_NAMESPACE);
2596#endif
2597
Daniel Veillardc8f620b2001-04-30 20:31:33 +00002598 if (ctxt->namespaces != NULL) {
2599 int i;
2600
2601 for (i = 0;i < ctxt->nsNr;i++) {
2602 if ((ctxt->namespaces[i] != NULL) &&
2603 (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
2604 return(ctxt->namespaces[i]->href);
2605 }
2606 }
Owen Taylor3473f882001-02-23 17:55:21 +00002607
2608 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
2609}
2610
2611/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002612 * xmlXPathRegisteredNsCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +00002613 * @ctxt: the XPath context
2614 *
2615 * Cleanup the XPath context data associated to registered variables
2616 */
2617void
2618xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
2619 if (ctxt == NULL)
2620 return;
2621
2622 xmlHashFree(ctxt->nsHash, NULL);
2623 ctxt->nsHash = NULL;
2624}
2625
2626/************************************************************************
2627 * *
2628 * Routines to handle Values *
2629 * *
2630 ************************************************************************/
2631
2632/* Allocations are terrible, one need to optimize all this !!! */
2633
2634/**
2635 * xmlXPathNewFloat:
2636 * @val: the double value
2637 *
2638 * Create a new xmlXPathObjectPtr of type double and of value @val
2639 *
2640 * Returns the newly created object.
2641 */
2642xmlXPathObjectPtr
2643xmlXPathNewFloat(double val) {
2644 xmlXPathObjectPtr ret;
2645
2646 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2647 if (ret == NULL) {
2648 xmlGenericError(xmlGenericErrorContext,
2649 "xmlXPathNewFloat: out of memory\n");
2650 return(NULL);
2651 }
2652 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2653 ret->type = XPATH_NUMBER;
2654 ret->floatval = val;
2655 return(ret);
2656}
2657
2658/**
2659 * xmlXPathNewBoolean:
2660 * @val: the boolean value
2661 *
2662 * Create a new xmlXPathObjectPtr of type boolean and of value @val
2663 *
2664 * Returns the newly created object.
2665 */
2666xmlXPathObjectPtr
2667xmlXPathNewBoolean(int val) {
2668 xmlXPathObjectPtr ret;
2669
2670 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2671 if (ret == NULL) {
2672 xmlGenericError(xmlGenericErrorContext,
2673 "xmlXPathNewBoolean: out of memory\n");
2674 return(NULL);
2675 }
2676 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2677 ret->type = XPATH_BOOLEAN;
2678 ret->boolval = (val != 0);
2679 return(ret);
2680}
2681
2682/**
2683 * xmlXPathNewString:
2684 * @val: the xmlChar * value
2685 *
2686 * Create a new xmlXPathObjectPtr of type string and of value @val
2687 *
2688 * Returns the newly created object.
2689 */
2690xmlXPathObjectPtr
2691xmlXPathNewString(const xmlChar *val) {
2692 xmlXPathObjectPtr ret;
2693
2694 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2695 if (ret == NULL) {
2696 xmlGenericError(xmlGenericErrorContext,
2697 "xmlXPathNewString: out of memory\n");
2698 return(NULL);
2699 }
2700 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2701 ret->type = XPATH_STRING;
2702 if (val != NULL)
2703 ret->stringval = xmlStrdup(val);
2704 else
2705 ret->stringval = xmlStrdup((const xmlChar *)"");
2706 return(ret);
2707}
2708
2709/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002710 * xmlXPathWrapString:
2711 * @val: the xmlChar * value
2712 *
2713 * Wraps the @val string into an XPath object.
2714 *
2715 * Returns the newly created object.
2716 */
2717xmlXPathObjectPtr
2718xmlXPathWrapString (xmlChar *val) {
2719 xmlXPathObjectPtr ret;
2720
2721 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2722 if (ret == NULL) {
2723 xmlGenericError(xmlGenericErrorContext,
2724 "xmlXPathWrapString: out of memory\n");
2725 return(NULL);
2726 }
2727 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2728 ret->type = XPATH_STRING;
2729 ret->stringval = val;
2730 return(ret);
2731}
2732
2733/**
Owen Taylor3473f882001-02-23 17:55:21 +00002734 * xmlXPathNewCString:
2735 * @val: the char * value
2736 *
2737 * Create a new xmlXPathObjectPtr of type string and of value @val
2738 *
2739 * Returns the newly created object.
2740 */
2741xmlXPathObjectPtr
2742xmlXPathNewCString(const char *val) {
2743 xmlXPathObjectPtr ret;
2744
2745 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2746 if (ret == NULL) {
2747 xmlGenericError(xmlGenericErrorContext,
2748 "xmlXPathNewCString: out of memory\n");
2749 return(NULL);
2750 }
2751 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2752 ret->type = XPATH_STRING;
2753 ret->stringval = xmlStrdup(BAD_CAST val);
2754 return(ret);
2755}
2756
2757/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002758 * xmlXPathWrapCString:
2759 * @val: the char * value
2760 *
2761 * Wraps a string into an XPath object.
2762 *
2763 * Returns the newly created object.
2764 */
2765xmlXPathObjectPtr
2766xmlXPathWrapCString (char * val) {
2767 return(xmlXPathWrapString((xmlChar *)(val)));
2768}
2769
2770/**
Thomas Broyerf06a3d82001-07-16 04:52:57 +00002771 * xmlXPathWrapExternal:
2772 * @val: the user data
2773 *
2774 * Wraps the @val data into an XPath object.
2775 *
2776 * Returns the newly created object.
2777 */
2778xmlXPathObjectPtr
2779xmlXPathWrapExternal (void *val) {
2780 xmlXPathObjectPtr ret;
2781
2782 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2783 if (ret == NULL) {
2784 xmlGenericError(xmlGenericErrorContext,
2785 "xmlXPathWrapString: out of memory\n");
2786 return(NULL);
2787 }
2788 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2789 ret->type = XPATH_USERS;
2790 ret->user = val;
2791 return(ret);
2792}
2793
2794/**
Owen Taylor3473f882001-02-23 17:55:21 +00002795 * xmlXPathObjectCopy:
2796 * @val: the original object
2797 *
2798 * allocate a new copy of a given object
2799 *
2800 * Returns the newly created object.
2801 */
2802xmlXPathObjectPtr
2803xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2804 xmlXPathObjectPtr ret;
2805
2806 if (val == NULL)
2807 return(NULL);
2808
2809 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2810 if (ret == NULL) {
2811 xmlGenericError(xmlGenericErrorContext,
2812 "xmlXPathObjectCopy: out of memory\n");
2813 return(NULL);
2814 }
2815 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2816 switch (val->type) {
2817 case XPATH_BOOLEAN:
2818 case XPATH_NUMBER:
2819 case XPATH_POINT:
2820 case XPATH_RANGE:
2821 break;
2822 case XPATH_STRING:
2823 ret->stringval = xmlStrdup(val->stringval);
2824 break;
2825 case XPATH_XSLT_TREE:
2826 if ((val->nodesetval != NULL) &&
2827 (val->nodesetval->nodeTab != NULL))
2828 ret->nodesetval = xmlXPathNodeSetCreate(
2829 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
2830 else
2831 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
2832 break;
2833 case XPATH_NODESET:
2834 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
2835 break;
2836 case XPATH_LOCATIONSET:
2837#ifdef LIBXML_XPTR_ENABLED
2838 {
2839 xmlLocationSetPtr loc = val->user;
2840 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2841 break;
2842 }
2843#endif
2844 case XPATH_UNDEFINED:
2845 case XPATH_USERS:
2846 xmlGenericError(xmlGenericErrorContext,
2847 "xmlXPathObjectCopy: unsupported type %d\n",
2848 val->type);
2849 break;
2850 }
2851 return(ret);
2852}
2853
2854/**
2855 * xmlXPathFreeObject:
2856 * @obj: the object to free
2857 *
2858 * Free up an xmlXPathObjectPtr object.
2859 */
2860void
2861xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2862 if (obj == NULL) return;
2863 if (obj->type == XPATH_NODESET) {
Daniel Veillard77851712001-02-27 21:54:07 +00002864 if (obj->boolval) {
2865 obj->type = XPATH_XSLT_TREE;
2866 if (obj->nodesetval != NULL)
2867 xmlXPathFreeValueTree(obj->nodesetval);
2868 } else {
2869 if (obj->nodesetval != NULL)
2870 xmlXPathFreeNodeSet(obj->nodesetval);
2871 }
Owen Taylor3473f882001-02-23 17:55:21 +00002872#ifdef LIBXML_XPTR_ENABLED
2873 } else if (obj->type == XPATH_LOCATIONSET) {
2874 if (obj->user != NULL)
2875 xmlXPtrFreeLocationSet(obj->user);
2876#endif
2877 } else if (obj->type == XPATH_STRING) {
2878 if (obj->stringval != NULL)
2879 xmlFree(obj->stringval);
2880 } else if (obj->type == XPATH_XSLT_TREE) {
2881 if (obj->nodesetval != NULL)
2882 xmlXPathFreeValueTree(obj->nodesetval);
2883 }
2884
Owen Taylor3473f882001-02-23 17:55:21 +00002885 xmlFree(obj);
2886}
2887
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002888
2889/************************************************************************
2890 * *
2891 * Type Casting Routines *
2892 * *
2893 ************************************************************************/
2894
2895/**
2896 * xmlXPathCastBooleanToString:
2897 * @val: a boolean
2898 *
2899 * Converts a boolean to its string value.
2900 *
2901 * Returns a newly allocated string.
2902 */
2903xmlChar *
2904xmlXPathCastBooleanToString (int val) {
2905 xmlChar *ret;
2906 if (val)
2907 ret = xmlStrdup((const xmlChar *) "true");
2908 else
2909 ret = xmlStrdup((const xmlChar *) "false");
2910 return(ret);
2911}
2912
2913/**
2914 * xmlXPathCastNumberToString:
2915 * @val: a number
2916 *
2917 * Converts a number to its string value.
2918 *
2919 * Returns a newly allocated string.
2920 */
2921xmlChar *
2922xmlXPathCastNumberToString (double val) {
2923 xmlChar *ret;
2924 switch (isinf(val)) {
2925 case 1:
2926 ret = xmlStrdup((const xmlChar *) "+Infinity");
2927 break;
2928 case -1:
2929 ret = xmlStrdup((const xmlChar *) "-Infinity");
2930 break;
2931 default:
2932 if (isnan(val)) {
2933 ret = xmlStrdup((const xmlChar *) "NaN");
2934 } else {
2935 /* could be improved */
2936 char buf[100];
2937 xmlXPathFormatNumber(val, buf, 100);
2938 ret = xmlStrdup((const xmlChar *) buf);
2939 }
2940 }
2941 return(ret);
2942}
2943
2944/**
2945 * xmlXPathCastNodeToString:
2946 * @node: a node
2947 *
2948 * Converts a node to its string value.
2949 *
2950 * Returns a newly allocated string.
2951 */
2952xmlChar *
2953xmlXPathCastNodeToString (xmlNodePtr node) {
2954 return(xmlNodeGetContent(node));
2955}
2956
2957/**
2958 * xmlXPathCastNodeSetToString:
2959 * @ns: a node-set
2960 *
2961 * Converts a node-set to its string value.
2962 *
2963 * Returns a newly allocated string.
2964 */
2965xmlChar *
2966xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
2967 if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
2968 return(xmlStrdup((const xmlChar *) ""));
2969
2970 xmlXPathNodeSetSort(ns);
2971 return(xmlXPathCastNodeToString(ns->nodeTab[0]));
2972}
2973
2974/**
2975 * xmlXPathCastToString:
2976 * @val: an XPath object
2977 *
2978 * Converts an existing object to its string() equivalent
2979 *
2980 * Returns the string value of the object, NULL in case of error.
2981 * A new string is allocated only if needed (val isn't a
2982 * string object).
2983 */
2984xmlChar *
2985xmlXPathCastToString(xmlXPathObjectPtr val) {
2986 xmlChar *ret = NULL;
2987
2988 if (val == NULL)
2989 return(xmlStrdup((const xmlChar *) ""));
2990 switch (val->type) {
2991 case XPATH_UNDEFINED:
2992#ifdef DEBUG_EXPR
2993 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
2994#endif
2995 ret = xmlStrdup((const xmlChar *) "");
2996 break;
2997 case XPATH_XSLT_TREE:
2998 case XPATH_NODESET:
2999 ret = xmlXPathCastNodeSetToString(val->nodesetval);
3000 break;
3001 case XPATH_STRING:
3002 return(val->stringval);
3003 case XPATH_BOOLEAN:
3004 ret = xmlXPathCastBooleanToString(val->boolval);
3005 break;
3006 case XPATH_NUMBER: {
3007 ret = xmlXPathCastNumberToString(val->floatval);
3008 break;
3009 }
3010 case XPATH_USERS:
3011 case XPATH_POINT:
3012 case XPATH_RANGE:
3013 case XPATH_LOCATIONSET:
3014 TODO
3015 ret = xmlStrdup((const xmlChar *) "");
3016 break;
3017 }
3018 return(ret);
3019}
3020
3021/**
3022 * xmlXPathConvertString:
3023 * @val: an XPath object
3024 *
3025 * Converts an existing object to its string() equivalent
3026 *
3027 * Returns the new object, the old one is freed (or the operation
3028 * is done directly on @val)
3029 */
3030xmlXPathObjectPtr
3031xmlXPathConvertString(xmlXPathObjectPtr val) {
3032 xmlChar *res = NULL;
3033
3034 if (val == NULL)
3035 return(xmlXPathNewCString(""));
3036
3037 switch (val->type) {
3038 case XPATH_UNDEFINED:
3039#ifdef DEBUG_EXPR
3040 xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
3041#endif
3042 break;
3043 case XPATH_XSLT_TREE:
3044 case XPATH_NODESET:
3045 res = xmlXPathCastNodeSetToString(val->nodesetval);
3046 break;
3047 case XPATH_STRING:
3048 return(val);
3049 case XPATH_BOOLEAN:
3050 res = xmlXPathCastBooleanToString(val->boolval);
3051 break;
3052 case XPATH_NUMBER:
3053 res = xmlXPathCastNumberToString(val->floatval);
3054 break;
3055 case XPATH_USERS:
3056 case XPATH_POINT:
3057 case XPATH_RANGE:
3058 case XPATH_LOCATIONSET:
3059 TODO;
3060 break;
3061 }
3062 xmlXPathFreeObject(val);
3063 if (res == NULL)
3064 return(xmlXPathNewCString(""));
3065 return(xmlXPathWrapString(res));
3066}
3067
3068/**
3069 * xmlXPathCastBooleanToNumber:
3070 * @val: a boolean
3071 *
3072 * Converts a boolean to its number value
3073 *
3074 * Returns the number value
3075 */
3076double
3077xmlXPathCastBooleanToNumber(int val) {
3078 if (val)
3079 return(1.0);
3080 return(0.0);
3081}
3082
3083/**
3084 * xmlXPathCastStringToNumber:
3085 * @val: a string
3086 *
3087 * Converts a string to its number value
3088 *
3089 * Returns the number value
3090 */
3091double
3092xmlXPathCastStringToNumber(const xmlChar * val) {
3093 return(xmlXPathStringEvalNumber(val));
3094}
3095
3096/**
3097 * xmlXPathCastNodeToNumber:
3098 * @node: a node
3099 *
3100 * Converts a node to its number value
3101 *
3102 * Returns the number value
3103 */
3104double
3105xmlXPathCastNodeToNumber (xmlNodePtr node) {
3106 xmlChar *strval;
3107 double ret;
3108
3109 if (node == NULL)
3110 return(xmlXPathNAN);
3111 strval = xmlXPathCastNodeToString(node);
3112 if (strval == NULL)
3113 return(xmlXPathNAN);
3114 ret = xmlXPathCastStringToNumber(strval);
3115 xmlFree(strval);
3116
3117 return(ret);
3118}
3119
3120/**
3121 * xmlXPathCastNodeSetToNumber:
3122 * @ns: a node-set
3123 *
3124 * Converts a node-set to its number value
3125 *
3126 * Returns the number value
3127 */
3128double
3129xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
3130 xmlChar *str;
3131 double ret;
3132
3133 if (ns == NULL)
3134 return(xmlXPathNAN);
3135 str = xmlXPathCastNodeSetToString(ns);
3136 ret = xmlXPathCastStringToNumber(str);
3137 xmlFree(str);
3138 return(ret);
3139}
3140
3141/**
3142 * xmlXPathCastToNumber:
3143 * @val: an XPath object
3144 *
3145 * Converts an XPath object to its number value
3146 *
3147 * Returns the number value
3148 */
3149double
3150xmlXPathCastToNumber(xmlXPathObjectPtr val) {
3151 double ret = 0.0;
3152
3153 if (val == NULL)
3154 return(xmlXPathNAN);
3155 switch (val->type) {
3156 case XPATH_UNDEFINED:
3157#ifdef DEGUB_EXPR
3158 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
3159#endif
3160 ret = xmlXPathNAN;
3161 break;
3162 case XPATH_XSLT_TREE:
3163 case XPATH_NODESET:
3164 ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
3165 break;
3166 case XPATH_STRING:
3167 ret = xmlXPathCastStringToNumber(val->stringval);
3168 break;
3169 case XPATH_NUMBER:
3170 ret = val->floatval;
3171 break;
3172 case XPATH_BOOLEAN:
3173 ret = xmlXPathCastBooleanToNumber(val->boolval);
3174 break;
3175 case XPATH_USERS:
3176 case XPATH_POINT:
3177 case XPATH_RANGE:
3178 case XPATH_LOCATIONSET:
3179 TODO;
3180 ret = xmlXPathNAN;
3181 break;
3182 }
3183 return(ret);
3184}
3185
3186/**
3187 * xmlXPathConvertNumber:
3188 * @val: an XPath object
3189 *
3190 * Converts an existing object to its number() equivalent
3191 *
3192 * Returns the new object, the old one is freed (or the operation
3193 * is done directly on @val)
3194 */
3195xmlXPathObjectPtr
3196xmlXPathConvertNumber(xmlXPathObjectPtr val) {
3197 xmlXPathObjectPtr ret;
3198
3199 if (val == NULL)
3200 return(xmlXPathNewFloat(0.0));
3201 if (val->type == XPATH_NUMBER)
3202 return(val);
3203 ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
3204 xmlXPathFreeObject(val);
3205 return(ret);
3206}
3207
3208/**
3209 * xmlXPathCastNumberToBoolean:
3210 * @val: a number
3211 *
3212 * Converts a number to its boolean value
3213 *
3214 * Returns the boolean value
3215 */
3216int
3217xmlXPathCastNumberToBoolean (double val) {
3218 if (isnan(val) || (val == 0.0))
3219 return(0);
3220 return(1);
3221}
3222
3223/**
3224 * xmlXPathCastStringToBoolean:
3225 * @val: a string
3226 *
3227 * Converts a string to its boolean value
3228 *
3229 * Returns the boolean value
3230 */
3231int
3232xmlXPathCastStringToBoolean (const xmlChar *val) {
3233 if ((val == NULL) || (xmlStrlen(val) == 0))
3234 return(0);
3235 return(1);
3236}
3237
3238/**
3239 * xmlXPathCastNodeSetToBoolean:
3240 * @ns: a node-set
3241 *
3242 * Converts a node-set to its boolean value
3243 *
3244 * Returns the boolean value
3245 */
3246int
3247xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
3248 if ((ns == NULL) || (ns->nodeNr == 0))
3249 return(0);
3250 return(1);
3251}
3252
3253/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00003254 * xmlXPathCastToBoolean:
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003255 * @val: an XPath object
3256 *
3257 * Converts an XPath object to its boolean value
3258 *
3259 * Returns the boolean value
3260 */
3261int
3262xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
3263 int ret = 0;
3264
3265 if (val == NULL)
3266 return(0);
3267 switch (val->type) {
3268 case XPATH_UNDEFINED:
3269#ifdef DEBUG_EXPR
3270 xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
3271#endif
3272 ret = 0;
3273 break;
3274 case XPATH_XSLT_TREE:
3275 case XPATH_NODESET:
3276 ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
3277 break;
3278 case XPATH_STRING:
3279 ret = xmlXPathCastStringToBoolean(val->stringval);
3280 break;
3281 case XPATH_NUMBER:
3282 ret = xmlXPathCastNumberToBoolean(val->floatval);
3283 break;
3284 case XPATH_BOOLEAN:
3285 ret = val->boolval;
3286 break;
3287 case XPATH_USERS:
3288 case XPATH_POINT:
3289 case XPATH_RANGE:
3290 case XPATH_LOCATIONSET:
3291 TODO;
3292 ret = 0;
3293 break;
3294 }
3295 return(ret);
3296}
3297
3298
3299/**
3300 * xmlXPathConvertBoolean:
3301 * @val: an XPath object
3302 *
3303 * Converts an existing object to its boolean() equivalent
3304 *
3305 * Returns the new object, the old one is freed (or the operation
3306 * is done directly on @val)
3307 */
3308xmlXPathObjectPtr
3309xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
3310 xmlXPathObjectPtr ret;
3311
3312 if (val == NULL)
3313 return(xmlXPathNewBoolean(0));
3314 if (val->type == XPATH_BOOLEAN)
3315 return(val);
3316 ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
3317 xmlXPathFreeObject(val);
3318 return(ret);
3319}
3320
Owen Taylor3473f882001-02-23 17:55:21 +00003321/************************************************************************
3322 * *
3323 * Routines to handle XPath contexts *
3324 * *
3325 ************************************************************************/
3326
3327/**
3328 * xmlXPathNewContext:
3329 * @doc: the XML document
3330 *
3331 * Create a new xmlXPathContext
3332 *
3333 * Returns the xmlXPathContext just allocated.
3334 */
3335xmlXPathContextPtr
3336xmlXPathNewContext(xmlDocPtr doc) {
3337 xmlXPathContextPtr ret;
3338
3339 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
3340 if (ret == NULL) {
3341 xmlGenericError(xmlGenericErrorContext,
3342 "xmlXPathNewContext: out of memory\n");
3343 return(NULL);
3344 }
3345 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
3346 ret->doc = doc;
3347 ret->node = NULL;
3348
3349 ret->varHash = NULL;
3350
3351 ret->nb_types = 0;
3352 ret->max_types = 0;
3353 ret->types = NULL;
3354
3355 ret->funcHash = xmlHashCreate(0);
3356
3357 ret->nb_axis = 0;
3358 ret->max_axis = 0;
3359 ret->axis = NULL;
3360
3361 ret->nsHash = NULL;
3362 ret->user = NULL;
3363
3364 ret->contextSize = -1;
3365 ret->proximityPosition = -1;
3366
3367 xmlXPathRegisterAllFunctions(ret);
3368
3369 return(ret);
3370}
3371
3372/**
3373 * xmlXPathFreeContext:
3374 * @ctxt: the context to free
3375 *
3376 * Free up an xmlXPathContext
3377 */
3378void
3379xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
3380 xmlXPathRegisteredNsCleanup(ctxt);
3381 xmlXPathRegisteredFuncsCleanup(ctxt);
3382 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00003383 xmlFree(ctxt);
3384}
3385
3386/************************************************************************
3387 * *
3388 * Routines to handle XPath parser contexts *
3389 * *
3390 ************************************************************************/
3391
3392#define CHECK_CTXT(ctxt) \
3393 if (ctxt == NULL) { \
3394 xmlGenericError(xmlGenericErrorContext, \
3395 "%s:%d Internal error: ctxt == NULL\n", \
3396 __FILE__, __LINE__); \
3397 } \
3398
3399
3400#define CHECK_CONTEXT(ctxt) \
3401 if (ctxt == NULL) { \
3402 xmlGenericError(xmlGenericErrorContext, \
3403 "%s:%d Internal error: no context\n", \
3404 __FILE__, __LINE__); \
3405 } \
3406 else if (ctxt->doc == NULL) { \
3407 xmlGenericError(xmlGenericErrorContext, \
3408 "%s:%d Internal error: no document\n", \
3409 __FILE__, __LINE__); \
3410 } \
3411 else if (ctxt->doc->children == NULL) { \
3412 xmlGenericError(xmlGenericErrorContext, \
3413 "%s:%d Internal error: document without root\n", \
3414 __FILE__, __LINE__); \
3415 } \
3416
3417
3418/**
3419 * xmlXPathNewParserContext:
3420 * @str: the XPath expression
3421 * @ctxt: the XPath context
3422 *
3423 * Create a new xmlXPathParserContext
3424 *
3425 * Returns the xmlXPathParserContext just allocated.
3426 */
3427xmlXPathParserContextPtr
3428xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
3429 xmlXPathParserContextPtr ret;
3430
3431 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
3432 if (ret == NULL) {
3433 xmlGenericError(xmlGenericErrorContext,
3434 "xmlXPathNewParserContext: out of memory\n");
3435 return(NULL);
3436 }
3437 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
3438 ret->cur = ret->base = str;
3439 ret->context = ctxt;
3440
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003441 ret->comp = xmlXPathNewCompExpr();
3442 if (ret->comp == NULL) {
3443 xmlFree(ret->valueTab);
3444 xmlFree(ret);
3445 return(NULL);
3446 }
3447
3448 return(ret);
3449}
3450
3451/**
3452 * xmlXPathCompParserContext:
3453 * @comp: the XPath compiled expression
3454 * @ctxt: the XPath context
3455 *
3456 * Create a new xmlXPathParserContext when processing a compiled expression
3457 *
3458 * Returns the xmlXPathParserContext just allocated.
3459 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003460static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003461xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
3462 xmlXPathParserContextPtr ret;
3463
3464 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
3465 if (ret == NULL) {
3466 xmlGenericError(xmlGenericErrorContext,
3467 "xmlXPathNewParserContext: out of memory\n");
3468 return(NULL);
3469 }
3470 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
3471
Owen Taylor3473f882001-02-23 17:55:21 +00003472 /* Allocate the value stack */
3473 ret->valueTab = (xmlXPathObjectPtr *)
3474 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003475 if (ret->valueTab == NULL) {
3476 xmlFree(ret);
3477 xmlGenericError(xmlGenericErrorContext,
3478 "xmlXPathNewParserContext: out of memory\n");
3479 return(NULL);
3480 }
Owen Taylor3473f882001-02-23 17:55:21 +00003481 ret->valueNr = 0;
3482 ret->valueMax = 10;
3483 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003484
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00003485 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003486 ret->comp = comp;
3487
Owen Taylor3473f882001-02-23 17:55:21 +00003488 return(ret);
3489}
3490
3491/**
3492 * xmlXPathFreeParserContext:
3493 * @ctxt: the context to free
3494 *
3495 * Free up an xmlXPathParserContext
3496 */
3497void
3498xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
3499 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00003500 xmlFree(ctxt->valueTab);
3501 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003502 if (ctxt->comp)
3503 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00003504 xmlFree(ctxt);
3505}
3506
3507/************************************************************************
3508 * *
3509 * The implicit core function library *
3510 * *
3511 ************************************************************************/
3512
Owen Taylor3473f882001-02-23 17:55:21 +00003513/**
Daniel Veillardf06307e2001-07-03 10:35:50 +00003514 * xmlXPathNodeStringHash:
3515 * @node: a node pointer
3516 *
3517 * Function computing the beginning of the string value of the node,
3518 * used to speed up comparisons
3519 *
3520 * Returns an int usable as a hash
3521 */
3522static unsigned int
3523xmlXPathNodeValHash(xmlNodePtr node) {
3524 int len = 2;
3525 const xmlChar * string = NULL;
3526 xmlNodePtr tmp = NULL;
3527 unsigned int ret = 0;
3528
3529 if (node == NULL)
3530 return(0);
3531
3532
3533 switch (node->type) {
3534 case XML_COMMENT_NODE:
3535 case XML_PI_NODE:
3536 case XML_CDATA_SECTION_NODE:
3537 case XML_TEXT_NODE:
3538 string = node->content;
3539 if (string == NULL)
3540 return(0);
3541 if (string[0] == 0)
3542 return(0);
3543 return(((unsigned int) string[0]) +
3544 (((unsigned int) string[1]) << 8));
3545 case XML_NAMESPACE_DECL:
3546 string = ((xmlNsPtr)node)->href;
3547 if (string == NULL)
3548 return(0);
3549 if (string[0] == 0)
3550 return(0);
3551 return(((unsigned int) string[0]) +
3552 (((unsigned int) string[1]) << 8));
3553 case XML_ATTRIBUTE_NODE:
3554 tmp = ((xmlAttrPtr) node)->children;
3555 break;
3556 case XML_ELEMENT_NODE:
3557 tmp = node->children;
3558 break;
3559 default:
3560 return(0);
3561 }
3562 while (tmp != NULL) {
3563 switch (tmp->type) {
3564 case XML_COMMENT_NODE:
3565 case XML_PI_NODE:
3566 case XML_CDATA_SECTION_NODE:
3567 case XML_TEXT_NODE:
3568 string = tmp->content;
3569 break;
3570 case XML_NAMESPACE_DECL:
3571 string = ((xmlNsPtr)tmp)->href;
3572 break;
3573 default:
3574 break;
3575 }
3576 if ((string != NULL) && (string[0] != 0)) {
3577 if (string[0] == 0)
3578 return(0);
3579 if (len == 1) {
3580 return(ret + (((unsigned int) string[0]) << 8));
3581 }
3582 if (string[1] == 0) {
3583 len = 1;
3584 ret = (unsigned int) string[0];
3585 } else {
3586 return(((unsigned int) string[0]) +
3587 (((unsigned int) string[1]) << 8));
3588 }
3589 }
3590 /*
3591 * Skip to next node
3592 */
3593 if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
3594 if (tmp->children->type != XML_ENTITY_DECL) {
3595 tmp = tmp->children;
3596 continue;
3597 }
3598 }
3599 if (tmp == node)
3600 break;
3601
3602 if (tmp->next != NULL) {
3603 tmp = tmp->next;
3604 continue;
3605 }
3606
3607 do {
3608 tmp = tmp->parent;
3609 if (tmp == NULL)
3610 break;
3611 if (tmp == node) {
3612 tmp = NULL;
3613 break;
3614 }
3615 if (tmp->next != NULL) {
3616 tmp = tmp->next;
3617 break;
3618 }
3619 } while (tmp != NULL);
3620 }
3621 return(ret);
3622}
3623
3624/**
3625 * xmlXPathStringHash:
3626 * @string: a string
3627 *
3628 * Function computing the beginning of the string value of the node,
3629 * used to speed up comparisons
3630 *
3631 * Returns an int usable as a hash
3632 */
3633static unsigned int
3634xmlXPathStringHash(const xmlChar * string) {
3635 if (string == NULL)
3636 return((unsigned int) 0);
3637 if (string[0] == 0)
3638 return(0);
3639 return(((unsigned int) string[0]) +
3640 (((unsigned int) string[1]) << 8));
3641}
3642
3643/**
Owen Taylor3473f882001-02-23 17:55:21 +00003644 * xmlXPathCompareNodeSetFloat:
3645 * @ctxt: the XPath Parser context
3646 * @inf: less than (1) or greater than (0)
3647 * @strict: is the comparison strict
3648 * @arg: the node set
3649 * @f: the value
3650 *
3651 * Implement the compare operation between a nodeset and a number
3652 * @ns < @val (1, 1, ...
3653 * @ns <= @val (1, 0, ...
3654 * @ns > @val (0, 1, ...
3655 * @ns >= @val (0, 0, ...
3656 *
3657 * If one object to be compared is a node-set and the other is a number,
3658 * then the comparison will be true if and only if there is a node in the
3659 * node-set such that the result of performing the comparison on the number
3660 * to be compared and on the result of converting the string-value of that
3661 * node to a number using the number function is true.
3662 *
3663 * Returns 0 or 1 depending on the results of the test.
3664 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003665static int
Owen Taylor3473f882001-02-23 17:55:21 +00003666xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
3667 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
3668 int i, ret = 0;
3669 xmlNodeSetPtr ns;
3670 xmlChar *str2;
3671
3672 if ((f == NULL) || (arg == NULL) ||
3673 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
3674 xmlXPathFreeObject(arg);
3675 xmlXPathFreeObject(f);
3676 return(0);
3677 }
3678 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00003679 if (ns != NULL) {
3680 for (i = 0;i < ns->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003681 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
Daniel Veillard911f49a2001-04-07 15:39:35 +00003682 if (str2 != NULL) {
3683 valuePush(ctxt,
3684 xmlXPathNewString(str2));
3685 xmlFree(str2);
3686 xmlXPathNumberFunction(ctxt, 1);
3687 valuePush(ctxt, xmlXPathObjectCopy(f));
3688 ret = xmlXPathCompareValues(ctxt, inf, strict);
3689 if (ret)
3690 break;
3691 }
3692 }
Owen Taylor3473f882001-02-23 17:55:21 +00003693 }
3694 xmlXPathFreeObject(arg);
3695 xmlXPathFreeObject(f);
3696 return(ret);
3697}
3698
3699/**
3700 * xmlXPathCompareNodeSetString:
3701 * @ctxt: the XPath Parser context
3702 * @inf: less than (1) or greater than (0)
3703 * @strict: is the comparison strict
3704 * @arg: the node set
3705 * @s: the value
3706 *
3707 * Implement the compare operation between a nodeset and a string
3708 * @ns < @val (1, 1, ...
3709 * @ns <= @val (1, 0, ...
3710 * @ns > @val (0, 1, ...
3711 * @ns >= @val (0, 0, ...
3712 *
3713 * If one object to be compared is a node-set and the other is a string,
3714 * then the comparison will be true if and only if there is a node in
3715 * the node-set such that the result of performing the comparison on the
3716 * string-value of the node and the other string is true.
3717 *
3718 * Returns 0 or 1 depending on the results of the test.
3719 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003720static int
Owen Taylor3473f882001-02-23 17:55:21 +00003721xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
3722 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
3723 int i, ret = 0;
3724 xmlNodeSetPtr ns;
3725 xmlChar *str2;
3726
3727 if ((s == NULL) || (arg == NULL) ||
3728 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
3729 xmlXPathFreeObject(arg);
3730 xmlXPathFreeObject(s);
3731 return(0);
3732 }
3733 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00003734 if (ns != NULL) {
3735 for (i = 0;i < ns->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003736 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
Daniel Veillard911f49a2001-04-07 15:39:35 +00003737 if (str2 != NULL) {
3738 valuePush(ctxt,
3739 xmlXPathNewString(str2));
3740 xmlFree(str2);
3741 valuePush(ctxt, xmlXPathObjectCopy(s));
3742 ret = xmlXPathCompareValues(ctxt, inf, strict);
3743 if (ret)
3744 break;
3745 }
3746 }
Owen Taylor3473f882001-02-23 17:55:21 +00003747 }
3748 xmlXPathFreeObject(arg);
3749 xmlXPathFreeObject(s);
3750 return(ret);
3751}
3752
3753/**
3754 * xmlXPathCompareNodeSets:
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003755 * @inf: less than (1) or greater than (0)
Owen Taylor3473f882001-02-23 17:55:21 +00003756 * @strict: is the comparison strict
3757 * @arg1: the fist node set object
3758 * @arg2: the second node set object
3759 *
3760 * Implement the compare operation on nodesets:
3761 *
3762 * If both objects to be compared are node-sets, then the comparison
3763 * will be true if and only if there is a node in the first node-set
3764 * and a node in the second node-set such that the result of performing
3765 * the comparison on the string-values of the two nodes is true.
3766 * ....
3767 * When neither object to be compared is a node-set and the operator
3768 * is <=, <, >= or >, then the objects are compared by converting both
3769 * objects to numbers and comparing the numbers according to IEEE 754.
3770 * ....
3771 * The number function converts its argument to a number as follows:
3772 * - a string that consists of optional whitespace followed by an
3773 * optional minus sign followed by a Number followed by whitespace
3774 * is converted to the IEEE 754 number that is nearest (according
3775 * to the IEEE 754 round-to-nearest rule) to the mathematical value
3776 * represented by the string; any other string is converted to NaN
3777 *
3778 * Conclusion all nodes need to be converted first to their string value
3779 * and then the comparison must be done when possible
3780 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003781static int
3782xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00003783 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
3784 int i, j, init = 0;
3785 double val1;
3786 double *values2;
3787 int ret = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00003788 xmlNodeSetPtr ns1;
3789 xmlNodeSetPtr ns2;
3790
3791 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00003792 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
3793 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003794 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003795 }
Owen Taylor3473f882001-02-23 17:55:21 +00003796 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00003797 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
3798 xmlXPathFreeObject(arg1);
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
3803 ns1 = arg1->nodesetval;
3804 ns2 = arg2->nodesetval;
3805
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003806 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003807 xmlXPathFreeObject(arg1);
3808 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003809 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003810 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003811 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003812 xmlXPathFreeObject(arg1);
3813 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003814 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003815 }
Owen Taylor3473f882001-02-23 17:55:21 +00003816
3817 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
3818 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003819 xmlXPathFreeObject(arg1);
3820 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003821 return(0);
3822 }
3823 for (i = 0;i < ns1->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003824 val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00003825 if (isnan(val1))
3826 continue;
3827 for (j = 0;j < ns2->nodeNr;j++) {
3828 if (init == 0) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003829 values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
Owen Taylor3473f882001-02-23 17:55:21 +00003830 }
3831 if (isnan(values2[j]))
3832 continue;
3833 if (inf && strict)
3834 ret = (val1 < values2[j]);
3835 else if (inf && !strict)
3836 ret = (val1 <= values2[j]);
3837 else if (!inf && strict)
3838 ret = (val1 > values2[j]);
3839 else if (!inf && !strict)
3840 ret = (val1 >= values2[j]);
3841 if (ret)
3842 break;
3843 }
3844 if (ret)
3845 break;
3846 init = 1;
3847 }
3848 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003849 xmlXPathFreeObject(arg1);
3850 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003851 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00003852}
3853
3854/**
3855 * xmlXPathCompareNodeSetValue:
3856 * @ctxt: the XPath Parser context
3857 * @inf: less than (1) or greater than (0)
3858 * @strict: is the comparison strict
3859 * @arg: the node set
3860 * @val: the value
3861 *
3862 * Implement the compare operation between a nodeset and a value
3863 * @ns < @val (1, 1, ...
3864 * @ns <= @val (1, 0, ...
3865 * @ns > @val (0, 1, ...
3866 * @ns >= @val (0, 0, ...
3867 *
3868 * If one object to be compared is a node-set and the other is a boolean,
3869 * then the comparison will be true if and only if the result of performing
3870 * the comparison on the boolean and on the result of converting
3871 * the node-set to a boolean using the boolean function is true.
3872 *
3873 * Returns 0 or 1 depending on the results of the test.
3874 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003875static int
Owen Taylor3473f882001-02-23 17:55:21 +00003876xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
3877 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
3878 if ((val == NULL) || (arg == NULL) ||
3879 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3880 return(0);
3881
3882 switch(val->type) {
3883 case XPATH_NUMBER:
3884 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
3885 case XPATH_NODESET:
3886 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003887 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00003888 case XPATH_STRING:
3889 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
3890 case XPATH_BOOLEAN:
3891 valuePush(ctxt, arg);
3892 xmlXPathBooleanFunction(ctxt, 1);
3893 valuePush(ctxt, val);
3894 return(xmlXPathCompareValues(ctxt, inf, strict));
3895 default:
3896 TODO
3897 return(0);
3898 }
3899 return(0);
3900}
3901
3902/**
3903 * xmlXPathEqualNodeSetString
3904 * @arg: the nodeset object argument
3905 * @str: the string to compare to.
3906 *
3907 * Implement the equal operation on XPath objects content: @arg1 == @arg2
3908 * If one object to be compared is a node-set and the other is a string,
3909 * then the comparison will be true if and only if there is a node in
3910 * the node-set such that the result of performing the comparison on the
3911 * string-value of the node and the other string is true.
3912 *
3913 * Returns 0 or 1 depending on the results of the test.
3914 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003915static int
Daniel Veillardf06307e2001-07-03 10:35:50 +00003916xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str)
3917{
Owen Taylor3473f882001-02-23 17:55:21 +00003918 int i;
3919 xmlNodeSetPtr ns;
3920 xmlChar *str2;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003921 unsigned int hash;
Owen Taylor3473f882001-02-23 17:55:21 +00003922
3923 if ((str == NULL) || (arg == NULL) ||
Daniel Veillardf06307e2001-07-03 10:35:50 +00003924 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3925 return (0);
Owen Taylor3473f882001-02-23 17:55:21 +00003926 ns = arg->nodesetval;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003927 hash = xmlXPathStringHash(str);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003928 if (ns == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00003929 return (0);
Daniel Veillard73c9c042001-07-05 20:02:54 +00003930 if (ns->nodeNr <= 0) {
3931 if (hash == 0)
3932 return(1);
3933 return(0);
3934 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00003935 for (i = 0; i < ns->nodeNr; i++) {
3936 if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
3937 str2 = xmlNodeGetContent(ns->nodeTab[i]);
3938 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
3939 xmlFree(str2);
3940 return (1);
3941 }
3942 if (str2 != NULL)
3943 xmlFree(str2);
3944 }
Owen Taylor3473f882001-02-23 17:55:21 +00003945 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00003946 return (0);
Owen Taylor3473f882001-02-23 17:55:21 +00003947}
3948
3949/**
3950 * xmlXPathEqualNodeSetFloat
3951 * @arg: the nodeset object argument
3952 * @f: the float to compare to
3953 *
3954 * Implement the equal operation on XPath objects content: @arg1 == @arg2
3955 * If one object to be compared is a node-set and the other is a number,
3956 * then the comparison will be true if and only if there is a node in
3957 * the node-set such that the result of performing the comparison on the
3958 * number to be compared and on the result of converting the string-value
3959 * of that node to a number using the number function is true.
3960 *
3961 * Returns 0 or 1 depending on the results of the test.
3962 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003963static int
Owen Taylor3473f882001-02-23 17:55:21 +00003964xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
3965 char buf[100] = "";
3966
3967 if ((arg == NULL) ||
3968 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3969 return(0);
3970
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003971 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00003972 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
3973}
3974
3975
3976/**
3977 * xmlXPathEqualNodeSets
3978 * @arg1: first nodeset object argument
3979 * @arg2: second nodeset object argument
3980 *
3981 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
3982 * If both objects to be compared are node-sets, then the comparison
3983 * will be true if and only if there is a node in the first node-set and
3984 * a node in the second node-set such that the result of performing the
3985 * comparison on the string-values of the two nodes is true.
3986 *
3987 * (needless to say, this is a costly operation)
3988 *
3989 * Returns 0 or 1 depending on the results of the test.
3990 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003991static int
Owen Taylor3473f882001-02-23 17:55:21 +00003992xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
3993 int i, j;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003994 unsigned int *hashs1;
3995 unsigned int *hashs2;
Owen Taylor3473f882001-02-23 17:55:21 +00003996 xmlChar **values1;
3997 xmlChar **values2;
3998 int ret = 0;
3999 xmlNodeSetPtr ns1;
4000 xmlNodeSetPtr ns2;
4001
4002 if ((arg1 == NULL) ||
4003 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
4004 return(0);
4005 if ((arg2 == NULL) ||
4006 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
4007 return(0);
4008
4009 ns1 = arg1->nodesetval;
4010 ns2 = arg2->nodesetval;
4011
Daniel Veillard911f49a2001-04-07 15:39:35 +00004012 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00004013 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00004014 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00004015 return(0);
4016
4017 /*
4018 * check if there is a node pertaining to both sets
4019 */
4020 for (i = 0;i < ns1->nodeNr;i++)
4021 for (j = 0;j < ns2->nodeNr;j++)
4022 if (ns1->nodeTab[i] == ns2->nodeTab[j])
4023 return(1);
4024
4025 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
4026 if (values1 == NULL)
4027 return(0);
Daniel Veillardf06307e2001-07-03 10:35:50 +00004028 hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
4029 if (hashs1 == NULL) {
4030 xmlFree(values1);
4031 return(0);
4032 }
Owen Taylor3473f882001-02-23 17:55:21 +00004033 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
4034 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
4035 if (values2 == NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00004036 xmlFree(hashs1);
Owen Taylor3473f882001-02-23 17:55:21 +00004037 xmlFree(values1);
4038 return(0);
4039 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00004040 hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
4041 if (hashs2 == NULL) {
4042 xmlFree(hashs1);
4043 xmlFree(values1);
4044 xmlFree(values2);
4045 return(0);
4046 }
Owen Taylor3473f882001-02-23 17:55:21 +00004047 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
4048 for (i = 0;i < ns1->nodeNr;i++) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00004049 hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00004050 for (j = 0;j < ns2->nodeNr;j++) {
4051 if (i == 0)
Daniel Veillardf06307e2001-07-03 10:35:50 +00004052 hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
4053 if (hashs1[i] == hashs2[j]) {
4054 if (values1[i] == NULL)
4055 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
4056 if (values2[j] == NULL)
4057 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
4058 ret = xmlStrEqual(values1[i], values2[j]);
4059 if (ret)
4060 break;
4061 }
Owen Taylor3473f882001-02-23 17:55:21 +00004062 }
4063 if (ret)
4064 break;
4065 }
4066 for (i = 0;i < ns1->nodeNr;i++)
4067 if (values1[i] != NULL)
4068 xmlFree(values1[i]);
4069 for (j = 0;j < ns2->nodeNr;j++)
4070 if (values2[j] != NULL)
4071 xmlFree(values2[j]);
4072 xmlFree(values1);
4073 xmlFree(values2);
Daniel Veillardf06307e2001-07-03 10:35:50 +00004074 xmlFree(hashs1);
4075 xmlFree(hashs2);
Owen Taylor3473f882001-02-23 17:55:21 +00004076 return(ret);
4077}
4078
4079/**
4080 * xmlXPathEqualValues:
4081 * @ctxt: the XPath Parser context
4082 *
4083 * Implement the equal operation on XPath objects content: @arg1 == @arg2
4084 *
4085 * Returns 0 or 1 depending on the results of the test.
4086 */
4087int
4088xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
4089 xmlXPathObjectPtr arg1, arg2;
4090 int ret = 0;
4091
4092 arg1 = valuePop(ctxt);
4093 if (arg1 == NULL)
4094 XP_ERROR0(XPATH_INVALID_OPERAND);
4095
4096 arg2 = valuePop(ctxt);
4097 if (arg2 == NULL) {
4098 xmlXPathFreeObject(arg1);
4099 XP_ERROR0(XPATH_INVALID_OPERAND);
4100 }
4101
4102 if (arg1 == arg2) {
4103#ifdef DEBUG_EXPR
4104 xmlGenericError(xmlGenericErrorContext,
4105 "Equal: by pointer\n");
4106#endif
4107 return(1);
4108 }
4109
4110 switch (arg1->type) {
4111 case XPATH_UNDEFINED:
4112#ifdef DEBUG_EXPR
4113 xmlGenericError(xmlGenericErrorContext,
4114 "Equal: undefined\n");
4115#endif
4116 break;
4117 case XPATH_XSLT_TREE:
4118 case XPATH_NODESET:
4119 switch (arg2->type) {
4120 case XPATH_UNDEFINED:
4121#ifdef DEBUG_EXPR
4122 xmlGenericError(xmlGenericErrorContext,
4123 "Equal: undefined\n");
4124#endif
4125 break;
4126 case XPATH_XSLT_TREE:
4127 case XPATH_NODESET:
4128 ret = xmlXPathEqualNodeSets(arg1, arg2);
4129 break;
4130 case XPATH_BOOLEAN:
4131 if ((arg1->nodesetval == NULL) ||
4132 (arg1->nodesetval->nodeNr == 0)) ret = 0;
4133 else
4134 ret = 1;
4135 ret = (ret == arg2->boolval);
4136 break;
4137 case XPATH_NUMBER:
4138 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
4139 break;
4140 case XPATH_STRING:
4141 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
4142 break;
4143 case XPATH_USERS:
4144 case XPATH_POINT:
4145 case XPATH_RANGE:
4146 case XPATH_LOCATIONSET:
4147 TODO
4148 break;
4149 }
4150 break;
4151 case XPATH_BOOLEAN:
4152 switch (arg2->type) {
4153 case XPATH_UNDEFINED:
4154#ifdef DEBUG_EXPR
4155 xmlGenericError(xmlGenericErrorContext,
4156 "Equal: undefined\n");
4157#endif
4158 break;
4159 case XPATH_NODESET:
4160 case XPATH_XSLT_TREE:
4161 if ((arg2->nodesetval == NULL) ||
4162 (arg2->nodesetval->nodeNr == 0)) ret = 0;
4163 else
4164 ret = 1;
4165 break;
4166 case XPATH_BOOLEAN:
4167#ifdef DEBUG_EXPR
4168 xmlGenericError(xmlGenericErrorContext,
4169 "Equal: %d boolean %d \n",
4170 arg1->boolval, arg2->boolval);
4171#endif
4172 ret = (arg1->boolval == arg2->boolval);
4173 break;
4174 case XPATH_NUMBER:
4175 if (arg2->floatval) ret = 1;
4176 else ret = 0;
4177 ret = (arg1->boolval == ret);
4178 break;
4179 case XPATH_STRING:
4180 if ((arg2->stringval == NULL) ||
4181 (arg2->stringval[0] == 0)) ret = 0;
4182 else
4183 ret = 1;
4184 ret = (arg1->boolval == ret);
4185 break;
4186 case XPATH_USERS:
4187 case XPATH_POINT:
4188 case XPATH_RANGE:
4189 case XPATH_LOCATIONSET:
4190 TODO
4191 break;
4192 }
4193 break;
4194 case XPATH_NUMBER:
4195 switch (arg2->type) {
4196 case XPATH_UNDEFINED:
4197#ifdef DEBUG_EXPR
4198 xmlGenericError(xmlGenericErrorContext,
4199 "Equal: undefined\n");
4200#endif
4201 break;
4202 case XPATH_NODESET:
4203 case XPATH_XSLT_TREE:
4204 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
4205 break;
4206 case XPATH_BOOLEAN:
4207 if (arg1->floatval) ret = 1;
4208 else ret = 0;
4209 ret = (arg2->boolval == ret);
4210 break;
4211 case XPATH_STRING:
4212 valuePush(ctxt, arg2);
4213 xmlXPathNumberFunction(ctxt, 1);
4214 arg2 = valuePop(ctxt);
4215 /* no break on purpose */
4216 case XPATH_NUMBER:
4217 ret = (arg1->floatval == arg2->floatval);
4218 break;
4219 case XPATH_USERS:
4220 case XPATH_POINT:
4221 case XPATH_RANGE:
4222 case XPATH_LOCATIONSET:
4223 TODO
4224 break;
4225 }
4226 break;
4227 case XPATH_STRING:
4228 switch (arg2->type) {
4229 case XPATH_UNDEFINED:
4230#ifdef DEBUG_EXPR
4231 xmlGenericError(xmlGenericErrorContext,
4232 "Equal: undefined\n");
4233#endif
4234 break;
4235 case XPATH_NODESET:
4236 case XPATH_XSLT_TREE:
4237 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
4238 break;
4239 case XPATH_BOOLEAN:
4240 if ((arg1->stringval == NULL) ||
4241 (arg1->stringval[0] == 0)) ret = 0;
4242 else
4243 ret = 1;
4244 ret = (arg2->boolval == ret);
4245 break;
4246 case XPATH_STRING:
4247 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
4248 break;
4249 case XPATH_NUMBER:
4250 valuePush(ctxt, arg1);
4251 xmlXPathNumberFunction(ctxt, 1);
4252 arg1 = valuePop(ctxt);
4253 ret = (arg1->floatval == arg2->floatval);
4254 break;
4255 case XPATH_USERS:
4256 case XPATH_POINT:
4257 case XPATH_RANGE:
4258 case XPATH_LOCATIONSET:
4259 TODO
4260 break;
4261 }
4262 break;
4263 case XPATH_USERS:
4264 case XPATH_POINT:
4265 case XPATH_RANGE:
4266 case XPATH_LOCATIONSET:
4267 TODO
4268 break;
4269 }
4270 xmlXPathFreeObject(arg1);
4271 xmlXPathFreeObject(arg2);
4272 return(ret);
4273}
4274
4275
4276/**
4277 * xmlXPathCompareValues:
4278 * @ctxt: the XPath Parser context
4279 * @inf: less than (1) or greater than (0)
4280 * @strict: is the comparison strict
4281 *
4282 * Implement the compare operation on XPath objects:
4283 * @arg1 < @arg2 (1, 1, ...
4284 * @arg1 <= @arg2 (1, 0, ...
4285 * @arg1 > @arg2 (0, 1, ...
4286 * @arg1 >= @arg2 (0, 0, ...
4287 *
4288 * When neither object to be compared is a node-set and the operator is
4289 * <=, <, >=, >, then the objects are compared by converted both objects
4290 * to numbers and comparing the numbers according to IEEE 754. The <
4291 * comparison will be true if and only if the first number is less than the
4292 * second number. The <= comparison will be true if and only if the first
4293 * number is less than or equal to the second number. The > comparison
4294 * will be true if and only if the first number is greater than the second
4295 * number. The >= comparison will be true if and only if the first number
4296 * is greater than or equal to the second number.
4297 *
4298 * Returns 1 if the comparaison succeeded, 0 if it failed
4299 */
4300int
4301xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
4302 int ret = 0;
4303 xmlXPathObjectPtr arg1, arg2;
4304
4305 arg2 = valuePop(ctxt);
4306 if (arg2 == NULL) {
4307 XP_ERROR0(XPATH_INVALID_OPERAND);
4308 }
4309
4310 arg1 = valuePop(ctxt);
4311 if (arg1 == NULL) {
4312 xmlXPathFreeObject(arg2);
4313 XP_ERROR0(XPATH_INVALID_OPERAND);
4314 }
4315
4316 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
4317 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004318 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00004319 } else {
4320 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00004321 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
4322 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00004323 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00004324 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
4325 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00004326 }
4327 }
4328 return(ret);
4329 }
4330
4331 if (arg1->type != XPATH_NUMBER) {
4332 valuePush(ctxt, arg1);
4333 xmlXPathNumberFunction(ctxt, 1);
4334 arg1 = valuePop(ctxt);
4335 }
4336 if (arg1->type != XPATH_NUMBER) {
4337 xmlXPathFreeObject(arg1);
4338 xmlXPathFreeObject(arg2);
4339 XP_ERROR0(XPATH_INVALID_OPERAND);
4340 }
4341 if (arg2->type != XPATH_NUMBER) {
4342 valuePush(ctxt, arg2);
4343 xmlXPathNumberFunction(ctxt, 1);
4344 arg2 = valuePop(ctxt);
4345 }
4346 if (arg2->type != XPATH_NUMBER) {
4347 xmlXPathFreeObject(arg1);
4348 xmlXPathFreeObject(arg2);
4349 XP_ERROR0(XPATH_INVALID_OPERAND);
4350 }
4351 /*
4352 * Add tests for infinity and nan
4353 * => feedback on 3.4 for Inf and NaN
4354 */
4355 if (inf && strict)
4356 ret = (arg1->floatval < arg2->floatval);
4357 else if (inf && !strict)
4358 ret = (arg1->floatval <= arg2->floatval);
4359 else if (!inf && strict)
4360 ret = (arg1->floatval > arg2->floatval);
4361 else if (!inf && !strict)
4362 ret = (arg1->floatval >= arg2->floatval);
4363 xmlXPathFreeObject(arg1);
4364 xmlXPathFreeObject(arg2);
4365 return(ret);
4366}
4367
4368/**
4369 * xmlXPathValueFlipSign:
4370 * @ctxt: the XPath Parser context
4371 *
4372 * Implement the unary - operation on an XPath object
4373 * The numeric operators convert their operands to numbers as if
4374 * by calling the number function.
4375 */
4376void
4377xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004378 CAST_TO_NUMBER;
4379 CHECK_TYPE(XPATH_NUMBER);
4380 ctxt->value->floatval = - ctxt->value->floatval;
Owen Taylor3473f882001-02-23 17:55:21 +00004381}
4382
4383/**
4384 * xmlXPathAddValues:
4385 * @ctxt: the XPath Parser context
4386 *
4387 * Implement the add operation on XPath objects:
4388 * The numeric operators convert their operands to numbers as if
4389 * by calling the number function.
4390 */
4391void
4392xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
4393 xmlXPathObjectPtr arg;
4394 double val;
4395
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004396 arg = valuePop(ctxt);
4397 if (arg == NULL)
4398 XP_ERROR(XPATH_INVALID_OPERAND);
4399 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004400 xmlXPathFreeObject(arg);
4401
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004402 CAST_TO_NUMBER;
4403 CHECK_TYPE(XPATH_NUMBER);
4404 ctxt->value->floatval += val;
Owen Taylor3473f882001-02-23 17:55:21 +00004405}
4406
4407/**
4408 * xmlXPathSubValues:
4409 * @ctxt: the XPath Parser context
4410 *
4411 * Implement the substraction operation on XPath objects:
4412 * The numeric operators convert their operands to numbers as if
4413 * by calling the number function.
4414 */
4415void
4416xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
4417 xmlXPathObjectPtr arg;
4418 double val;
4419
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004420 arg = valuePop(ctxt);
4421 if (arg == NULL)
4422 XP_ERROR(XPATH_INVALID_OPERAND);
4423 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004424 xmlXPathFreeObject(arg);
4425
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004426 CAST_TO_NUMBER;
4427 CHECK_TYPE(XPATH_NUMBER);
4428 ctxt->value->floatval -= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004429}
4430
4431/**
4432 * xmlXPathMultValues:
4433 * @ctxt: the XPath Parser context
4434 *
4435 * Implement the multiply operation on XPath objects:
4436 * The numeric operators convert their operands to numbers as if
4437 * by calling the number function.
4438 */
4439void
4440xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
4441 xmlXPathObjectPtr arg;
4442 double val;
4443
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004444 arg = valuePop(ctxt);
4445 if (arg == NULL)
4446 XP_ERROR(XPATH_INVALID_OPERAND);
4447 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004448 xmlXPathFreeObject(arg);
4449
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004450 CAST_TO_NUMBER;
4451 CHECK_TYPE(XPATH_NUMBER);
4452 ctxt->value->floatval *= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004453}
4454
4455/**
4456 * xmlXPathDivValues:
4457 * @ctxt: the XPath Parser context
4458 *
4459 * Implement the div operation on XPath objects @arg1 / @arg2:
4460 * The numeric operators convert their operands to numbers as if
4461 * by calling the number function.
4462 */
4463void
4464xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
4465 xmlXPathObjectPtr arg;
4466 double val;
4467
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004468 arg = valuePop(ctxt);
4469 if (arg == NULL)
4470 XP_ERROR(XPATH_INVALID_OPERAND);
4471 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004472 xmlXPathFreeObject(arg);
4473
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004474 CAST_TO_NUMBER;
4475 CHECK_TYPE(XPATH_NUMBER);
4476 ctxt->value->floatval /= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004477}
4478
4479/**
4480 * xmlXPathModValues:
4481 * @ctxt: the XPath Parser context
4482 *
4483 * Implement the mod operation on XPath objects: @arg1 / @arg2
4484 * The numeric operators convert their operands to numbers as if
4485 * by calling the number function.
4486 */
4487void
4488xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
4489 xmlXPathObjectPtr arg;
4490 int arg1, arg2;
4491
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004492 arg = valuePop(ctxt);
4493 if (arg == NULL)
4494 XP_ERROR(XPATH_INVALID_OPERAND);
4495 arg2 = (int) xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004496 xmlXPathFreeObject(arg);
4497
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004498 CAST_TO_NUMBER;
4499 CHECK_TYPE(XPATH_NUMBER);
4500 arg1 = (int) ctxt->value->floatval;
4501 ctxt->value->floatval = arg1 % arg2;
Owen Taylor3473f882001-02-23 17:55:21 +00004502}
4503
4504/************************************************************************
4505 * *
4506 * The traversal functions *
4507 * *
4508 ************************************************************************/
4509
Owen Taylor3473f882001-02-23 17:55:21 +00004510/*
4511 * A traversal function enumerates nodes along an axis.
4512 * Initially it must be called with NULL, and it indicates
4513 * termination on the axis by returning NULL.
4514 */
4515typedef xmlNodePtr (*xmlXPathTraversalFunction)
4516 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
4517
4518/**
4519 * xmlXPathNextSelf:
4520 * @ctxt: the XPath Parser context
4521 * @cur: the current node in the traversal
4522 *
4523 * Traversal function for the "self" direction
4524 * The self axis contains just the context node itself
4525 *
4526 * Returns the next element following that axis
4527 */
4528xmlNodePtr
4529xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4530 if (cur == NULL)
4531 return(ctxt->context->node);
4532 return(NULL);
4533}
4534
4535/**
4536 * xmlXPathNextChild:
4537 * @ctxt: the XPath Parser context
4538 * @cur: the current node in the traversal
4539 *
4540 * Traversal function for the "child" direction
4541 * The child axis contains the children of the context node in document order.
4542 *
4543 * Returns the next element following that axis
4544 */
4545xmlNodePtr
4546xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4547 if (cur == NULL) {
4548 if (ctxt->context->node == NULL) return(NULL);
4549 switch (ctxt->context->node->type) {
4550 case XML_ELEMENT_NODE:
4551 case XML_TEXT_NODE:
4552 case XML_CDATA_SECTION_NODE:
4553 case XML_ENTITY_REF_NODE:
4554 case XML_ENTITY_NODE:
4555 case XML_PI_NODE:
4556 case XML_COMMENT_NODE:
4557 case XML_NOTATION_NODE:
4558 case XML_DTD_NODE:
4559 return(ctxt->context->node->children);
4560 case XML_DOCUMENT_NODE:
4561 case XML_DOCUMENT_TYPE_NODE:
4562 case XML_DOCUMENT_FRAG_NODE:
4563 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004564#ifdef LIBXML_DOCB_ENABLED
4565 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004566#endif
4567 return(((xmlDocPtr) ctxt->context->node)->children);
4568 case XML_ELEMENT_DECL:
4569 case XML_ATTRIBUTE_DECL:
4570 case XML_ENTITY_DECL:
4571 case XML_ATTRIBUTE_NODE:
4572 case XML_NAMESPACE_DECL:
4573 case XML_XINCLUDE_START:
4574 case XML_XINCLUDE_END:
4575 return(NULL);
4576 }
4577 return(NULL);
4578 }
4579 if ((cur->type == XML_DOCUMENT_NODE) ||
4580 (cur->type == XML_HTML_DOCUMENT_NODE))
4581 return(NULL);
4582 return(cur->next);
4583}
4584
4585/**
4586 * xmlXPathNextDescendant:
4587 * @ctxt: the XPath Parser context
4588 * @cur: the current node in the traversal
4589 *
4590 * Traversal function for the "descendant" direction
4591 * the descendant axis contains the descendants of the context node in document
4592 * order; a descendant is a child or a child of a child and so on.
4593 *
4594 * Returns the next element following that axis
4595 */
4596xmlNodePtr
4597xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4598 if (cur == NULL) {
4599 if (ctxt->context->node == NULL)
4600 return(NULL);
4601 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4602 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4603 return(NULL);
4604
4605 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
4606 return(ctxt->context->doc->children);
4607 return(ctxt->context->node->children);
4608 }
4609
4610 if (cur->children != NULL)
4611 {
4612 if (cur->children->type != XML_ENTITY_DECL)
4613 return(cur->children);
4614 }
4615 if (cur->next != NULL) return(cur->next);
4616
4617 do {
4618 cur = cur->parent;
4619 if (cur == NULL) return(NULL);
4620 if (cur == ctxt->context->node) return(NULL);
4621 if (cur->next != NULL) {
4622 cur = cur->next;
4623 return(cur);
4624 }
4625 } while (cur != NULL);
4626 return(cur);
4627}
4628
4629/**
4630 * xmlXPathNextDescendantOrSelf:
4631 * @ctxt: the XPath Parser context
4632 * @cur: the current node in the traversal
4633 *
4634 * Traversal function for the "descendant-or-self" direction
4635 * the descendant-or-self axis contains the context node and the descendants
4636 * of the context node in document order; thus the context node is the first
4637 * node on the axis, and the first child of the context node is the second node
4638 * on the axis
4639 *
4640 * Returns the next element following that axis
4641 */
4642xmlNodePtr
4643xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4644 if (cur == NULL) {
4645 if (ctxt->context->node == NULL)
4646 return(NULL);
4647 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4648 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4649 return(NULL);
4650 return(ctxt->context->node);
4651 }
4652
4653 return(xmlXPathNextDescendant(ctxt, cur));
4654}
4655
4656/**
4657 * xmlXPathNextParent:
4658 * @ctxt: the XPath Parser context
4659 * @cur: the current node in the traversal
4660 *
4661 * Traversal function for the "parent" direction
4662 * The parent axis contains the parent of the context node, if there is one.
4663 *
4664 * Returns the next element following that axis
4665 */
4666xmlNodePtr
4667xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4668 /*
4669 * the parent of an attribute or namespace node is the element
4670 * to which the attribute or namespace node is attached
4671 * Namespace handling !!!
4672 */
4673 if (cur == NULL) {
4674 if (ctxt->context->node == NULL) return(NULL);
4675 switch (ctxt->context->node->type) {
4676 case XML_ELEMENT_NODE:
4677 case XML_TEXT_NODE:
4678 case XML_CDATA_SECTION_NODE:
4679 case XML_ENTITY_REF_NODE:
4680 case XML_ENTITY_NODE:
4681 case XML_PI_NODE:
4682 case XML_COMMENT_NODE:
4683 case XML_NOTATION_NODE:
4684 case XML_DTD_NODE:
4685 case XML_ELEMENT_DECL:
4686 case XML_ATTRIBUTE_DECL:
4687 case XML_XINCLUDE_START:
4688 case XML_XINCLUDE_END:
4689 case XML_ENTITY_DECL:
4690 if (ctxt->context->node->parent == NULL)
4691 return((xmlNodePtr) ctxt->context->doc);
4692 return(ctxt->context->node->parent);
4693 case XML_ATTRIBUTE_NODE: {
4694 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
4695
4696 return(att->parent);
4697 }
4698 case XML_DOCUMENT_NODE:
4699 case XML_DOCUMENT_TYPE_NODE:
4700 case XML_DOCUMENT_FRAG_NODE:
4701 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004702#ifdef LIBXML_DOCB_ENABLED
4703 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004704#endif
4705 return(NULL);
4706 case XML_NAMESPACE_DECL:
4707 /*
4708 * TODO !!! may require extending struct _xmlNs with
4709 * parent field
4710 * C.f. Infoset case...
4711 */
4712 return(NULL);
4713 }
4714 }
4715 return(NULL);
4716}
4717
4718/**
4719 * xmlXPathNextAncestor:
4720 * @ctxt: the XPath Parser context
4721 * @cur: the current node in the traversal
4722 *
4723 * Traversal function for the "ancestor" direction
4724 * the ancestor axis contains the ancestors of the context node; the ancestors
4725 * of the context node consist of the parent of context node and the parent's
4726 * parent and so on; the nodes are ordered in reverse document order; thus the
4727 * parent is the first node on the axis, and the parent's parent is the second
4728 * node on the axis
4729 *
4730 * Returns the next element following that axis
4731 */
4732xmlNodePtr
4733xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4734 /*
4735 * the parent of an attribute or namespace node is the element
4736 * to which the attribute or namespace node is attached
4737 * !!!!!!!!!!!!!
4738 */
4739 if (cur == NULL) {
4740 if (ctxt->context->node == NULL) return(NULL);
4741 switch (ctxt->context->node->type) {
4742 case XML_ELEMENT_NODE:
4743 case XML_TEXT_NODE:
4744 case XML_CDATA_SECTION_NODE:
4745 case XML_ENTITY_REF_NODE:
4746 case XML_ENTITY_NODE:
4747 case XML_PI_NODE:
4748 case XML_COMMENT_NODE:
4749 case XML_DTD_NODE:
4750 case XML_ELEMENT_DECL:
4751 case XML_ATTRIBUTE_DECL:
4752 case XML_ENTITY_DECL:
4753 case XML_NOTATION_NODE:
4754 case XML_XINCLUDE_START:
4755 case XML_XINCLUDE_END:
4756 if (ctxt->context->node->parent == NULL)
4757 return((xmlNodePtr) ctxt->context->doc);
4758 return(ctxt->context->node->parent);
4759 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004760 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00004761
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004762 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00004763 }
4764 case XML_DOCUMENT_NODE:
4765 case XML_DOCUMENT_TYPE_NODE:
4766 case XML_DOCUMENT_FRAG_NODE:
4767 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004768#ifdef LIBXML_DOCB_ENABLED
4769 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004770#endif
4771 return(NULL);
4772 case XML_NAMESPACE_DECL:
4773 /*
4774 * TODO !!! may require extending struct _xmlNs with
4775 * parent field
4776 * C.f. Infoset case...
4777 */
4778 return(NULL);
4779 }
4780 return(NULL);
4781 }
4782 if (cur == ctxt->context->doc->children)
4783 return((xmlNodePtr) ctxt->context->doc);
4784 if (cur == (xmlNodePtr) ctxt->context->doc)
4785 return(NULL);
4786 switch (cur->type) {
4787 case XML_ELEMENT_NODE:
4788 case XML_TEXT_NODE:
4789 case XML_CDATA_SECTION_NODE:
4790 case XML_ENTITY_REF_NODE:
4791 case XML_ENTITY_NODE:
4792 case XML_PI_NODE:
4793 case XML_COMMENT_NODE:
4794 case XML_NOTATION_NODE:
4795 case XML_DTD_NODE:
4796 case XML_ELEMENT_DECL:
4797 case XML_ATTRIBUTE_DECL:
4798 case XML_ENTITY_DECL:
4799 case XML_XINCLUDE_START:
4800 case XML_XINCLUDE_END:
4801 return(cur->parent);
4802 case XML_ATTRIBUTE_NODE: {
4803 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
4804
4805 return(att->parent);
4806 }
4807 case XML_DOCUMENT_NODE:
4808 case XML_DOCUMENT_TYPE_NODE:
4809 case XML_DOCUMENT_FRAG_NODE:
4810 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004811#ifdef LIBXML_DOCB_ENABLED
4812 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004813#endif
4814 return(NULL);
4815 case XML_NAMESPACE_DECL:
4816 /*
4817 * TODO !!! may require extending struct _xmlNs with
4818 * parent field
4819 * C.f. Infoset case...
4820 */
4821 return(NULL);
4822 }
4823 return(NULL);
4824}
4825
4826/**
4827 * xmlXPathNextAncestorOrSelf:
4828 * @ctxt: the XPath Parser context
4829 * @cur: the current node in the traversal
4830 *
4831 * Traversal function for the "ancestor-or-self" direction
4832 * he ancestor-or-self axis contains the context node and ancestors of
4833 * the context node in reverse document order; thus the context node is
4834 * the first node on the axis, and the context node's parent the second;
4835 * parent here is defined the same as with the parent axis.
4836 *
4837 * Returns the next element following that axis
4838 */
4839xmlNodePtr
4840xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4841 if (cur == NULL)
4842 return(ctxt->context->node);
4843 return(xmlXPathNextAncestor(ctxt, cur));
4844}
4845
4846/**
4847 * xmlXPathNextFollowingSibling:
4848 * @ctxt: the XPath Parser context
4849 * @cur: the current node in the traversal
4850 *
4851 * Traversal function for the "following-sibling" direction
4852 * The following-sibling axis contains the following siblings of the context
4853 * node in document order.
4854 *
4855 * Returns the next element following that axis
4856 */
4857xmlNodePtr
4858xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4859 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4860 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4861 return(NULL);
4862 if (cur == (xmlNodePtr) ctxt->context->doc)
4863 return(NULL);
4864 if (cur == NULL)
4865 return(ctxt->context->node->next);
4866 return(cur->next);
4867}
4868
4869/**
4870 * xmlXPathNextPrecedingSibling:
4871 * @ctxt: the XPath Parser context
4872 * @cur: the current node in the traversal
4873 *
4874 * Traversal function for the "preceding-sibling" direction
4875 * The preceding-sibling axis contains the preceding siblings of the context
4876 * node in reverse document order; the first preceding sibling is first on the
4877 * axis; the sibling preceding that node is the second on the axis and so on.
4878 *
4879 * Returns the next element following that axis
4880 */
4881xmlNodePtr
4882xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4883 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4884 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4885 return(NULL);
4886 if (cur == (xmlNodePtr) ctxt->context->doc)
4887 return(NULL);
4888 if (cur == NULL)
4889 return(ctxt->context->node->prev);
Daniel Veillardf06307e2001-07-03 10:35:50 +00004890 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
4891 cur = cur->prev;
4892 if (cur == NULL)
4893 return(ctxt->context->node->prev);
4894 }
Owen Taylor3473f882001-02-23 17:55:21 +00004895 return(cur->prev);
4896}
4897
4898/**
4899 * xmlXPathNextFollowing:
4900 * @ctxt: the XPath Parser context
4901 * @cur: the current node in the traversal
4902 *
4903 * Traversal function for the "following" direction
4904 * The following axis contains all nodes in the same document as the context
4905 * node that are after the context node in document order, excluding any
4906 * descendants and excluding attribute nodes and namespace nodes; the nodes
4907 * are ordered in document order
4908 *
4909 * Returns the next element following that axis
4910 */
4911xmlNodePtr
4912xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4913 if (cur != NULL && cur->children != NULL)
4914 return cur->children ;
4915 if (cur == NULL) cur = ctxt->context->node;
4916 if (cur == NULL) return(NULL) ; /* ERROR */
4917 if (cur->next != NULL) return(cur->next) ;
4918 do {
4919 cur = cur->parent;
4920 if (cur == NULL) return(NULL);
4921 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
4922 if (cur->next != NULL) return(cur->next);
4923 } while (cur != NULL);
4924 return(cur);
4925}
4926
4927/*
4928 * xmlXPathIsAncestor:
4929 * @ancestor: the ancestor node
4930 * @node: the current node
4931 *
4932 * Check that @ancestor is a @node's ancestor
4933 *
4934 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
4935 */
4936static int
4937xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
4938 if ((ancestor == NULL) || (node == NULL)) return(0);
4939 /* nodes need to be in the same document */
4940 if (ancestor->doc != node->doc) return(0);
4941 /* avoid searching if ancestor or node is the root node */
4942 if (ancestor == (xmlNodePtr) node->doc) return(1);
4943 if (node == (xmlNodePtr) ancestor->doc) return(0);
4944 while (node->parent != NULL) {
4945 if (node->parent == ancestor)
4946 return(1);
4947 node = node->parent;
4948 }
4949 return(0);
4950}
4951
4952/**
4953 * xmlXPathNextPreceding:
4954 * @ctxt: the XPath Parser context
4955 * @cur: the current node in the traversal
4956 *
4957 * Traversal function for the "preceding" direction
4958 * the preceding axis contains all nodes in the same document as the context
4959 * node that are before the context node in document order, excluding any
4960 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
4961 * ordered in reverse document order
4962 *
4963 * Returns the next element following that axis
4964 */
4965xmlNodePtr
Daniel Veillardf06307e2001-07-03 10:35:50 +00004966xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
4967{
Owen Taylor3473f882001-02-23 17:55:21 +00004968 if (cur == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00004969 cur = ctxt->context->node;
4970 if (cur == NULL)
4971 return (NULL);
4972 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
4973 cur = cur->prev;
Owen Taylor3473f882001-02-23 17:55:21 +00004974 do {
4975 if (cur->prev != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00004976 for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
4977 return (cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004978 }
4979
4980 cur = cur->parent;
Daniel Veillardf06307e2001-07-03 10:35:50 +00004981 if (cur == NULL)
4982 return (NULL);
4983 if (cur == ctxt->context->doc->children)
4984 return (NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004985 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
Daniel Veillardf06307e2001-07-03 10:35:50 +00004986 return (cur);
4987}
4988
4989/**
4990 * xmlXPathNextPrecedingInternal:
4991 * @ctxt: the XPath Parser context
4992 * @cur: the current node in the traversal
4993 *
4994 * Traversal function for the "preceding" direction
4995 * the preceding axis contains all nodes in the same document as the context
4996 * node that are before the context node in document order, excluding any
4997 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
4998 * ordered in reverse document order
4999 * This is a faster implementation but internal only since it requires a
5000 * state kept in the parser context: ctxt->ancestor.
5001 *
5002 * Returns the next element following that axis
5003 */
5004static xmlNodePtr
5005xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
5006 xmlNodePtr cur)
5007{
5008 if (cur == NULL) {
5009 cur = ctxt->context->node;
5010 if (cur == NULL)
5011 return (NULL);
5012 ctxt->ancestor = cur->parent;
5013 }
5014 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
5015 cur = cur->prev;
5016 while (cur->prev == NULL) {
5017 cur = cur->parent;
5018 if (cur == NULL)
5019 return (NULL);
5020 if (cur == ctxt->context->doc->children)
5021 return (NULL);
5022 if (cur != ctxt->ancestor)
5023 return (cur);
5024 ctxt->ancestor = cur->parent;
5025 }
5026 cur = cur->prev;
5027 while (cur->last != NULL)
5028 cur = cur->last;
5029 return (cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005030}
5031
5032/**
5033 * xmlXPathNextNamespace:
5034 * @ctxt: the XPath Parser context
5035 * @cur: the current attribute in the traversal
5036 *
5037 * Traversal function for the "namespace" direction
5038 * the namespace axis contains the namespace nodes of the context node;
5039 * the order of nodes on this axis is implementation-defined; the axis will
5040 * be empty unless the context node is an element
5041 *
5042 * Returns the next element following that axis
5043 */
5044xmlNodePtr
5045xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005046 xmlNodePtr ret;
5047
Owen Taylor3473f882001-02-23 17:55:21 +00005048 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005049 if ((cur == NULL) || (ctxt->context->tmpNsList == NULL)) {
5050 if (ctxt->context->tmpNsList != NULL)
5051 xmlFree(ctxt->context->tmpNsList);
5052 ctxt->context->tmpNsList =
Owen Taylor3473f882001-02-23 17:55:21 +00005053 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005054 if (ctxt->context->tmpNsList == NULL) return(NULL);
5055 ctxt->context->tmpNsNr = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005056 }
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005057 ret = (xmlNodePtr)ctxt->context->tmpNsList[ctxt->context->tmpNsNr++];
5058 if (ret == NULL) {
5059 xmlFree(ctxt->context->tmpNsList);
5060 ctxt->context->tmpNsList = NULL;
5061 }
5062 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00005063}
5064
5065/**
5066 * xmlXPathNextAttribute:
5067 * @ctxt: the XPath Parser context
5068 * @cur: the current attribute in the traversal
5069 *
5070 * Traversal function for the "attribute" direction
5071 * TODO: support DTD inherited default attributes
5072 *
5073 * Returns the next element following that axis
5074 */
5075xmlNodePtr
5076xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00005077 if (ctxt->context->node == NULL)
5078 return(NULL);
5079 if (ctxt->context->node->type != XML_ELEMENT_NODE)
5080 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005081 if (cur == NULL) {
5082 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
5083 return(NULL);
5084 return((xmlNodePtr)ctxt->context->node->properties);
5085 }
5086 return((xmlNodePtr)cur->next);
5087}
5088
5089/************************************************************************
5090 * *
5091 * NodeTest Functions *
5092 * *
5093 ************************************************************************/
5094
Owen Taylor3473f882001-02-23 17:55:21 +00005095#define IS_FUNCTION 200
5096
Owen Taylor3473f882001-02-23 17:55:21 +00005097
5098/************************************************************************
5099 * *
5100 * Implicit tree core function library *
5101 * *
5102 ************************************************************************/
5103
5104/**
5105 * xmlXPathRoot:
5106 * @ctxt: the XPath Parser context
5107 *
5108 * Initialize the context to the root of the document
5109 */
5110void
5111xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
5112 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
5113 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5114}
5115
5116/************************************************************************
5117 * *
5118 * The explicit core function library *
5119 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
5120 * *
5121 ************************************************************************/
5122
5123
5124/**
5125 * xmlXPathLastFunction:
5126 * @ctxt: the XPath Parser context
5127 * @nargs: the number of arguments
5128 *
5129 * Implement the last() XPath function
5130 * number last()
5131 * The last function returns the number of nodes in the context node list.
5132 */
5133void
5134xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5135 CHECK_ARITY(0);
5136 if (ctxt->context->contextSize >= 0) {
5137 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
5138#ifdef DEBUG_EXPR
5139 xmlGenericError(xmlGenericErrorContext,
5140 "last() : %d\n", ctxt->context->contextSize);
5141#endif
5142 } else {
5143 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
5144 }
5145}
5146
5147/**
5148 * xmlXPathPositionFunction:
5149 * @ctxt: the XPath Parser context
5150 * @nargs: the number of arguments
5151 *
5152 * Implement the position() XPath function
5153 * number position()
5154 * The position function returns the position of the context node in the
5155 * context node list. The first position is 1, and so the last positionr
5156 * will be equal to last().
5157 */
5158void
5159xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5160 CHECK_ARITY(0);
5161 if (ctxt->context->proximityPosition >= 0) {
5162 valuePush(ctxt,
5163 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
5164#ifdef DEBUG_EXPR
5165 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
5166 ctxt->context->proximityPosition);
5167#endif
5168 } else {
5169 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
5170 }
5171}
5172
5173/**
5174 * xmlXPathCountFunction:
5175 * @ctxt: the XPath Parser context
5176 * @nargs: the number of arguments
5177 *
5178 * Implement the count() XPath function
5179 * number count(node-set)
5180 */
5181void
5182xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5183 xmlXPathObjectPtr cur;
5184
5185 CHECK_ARITY(1);
5186 if ((ctxt->value == NULL) ||
5187 ((ctxt->value->type != XPATH_NODESET) &&
5188 (ctxt->value->type != XPATH_XSLT_TREE)))
5189 XP_ERROR(XPATH_INVALID_TYPE);
5190 cur = valuePop(ctxt);
5191
Daniel Veillard911f49a2001-04-07 15:39:35 +00005192 if ((cur == NULL) || (cur->nodesetval == NULL))
5193 valuePush(ctxt, xmlXPathNewFloat((double) 0));
5194 else
5195 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Owen Taylor3473f882001-02-23 17:55:21 +00005196 xmlXPathFreeObject(cur);
5197}
5198
5199/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005200 * xmlXPathGetElementsByIds:
5201 * @doc: the document
5202 * @ids: a whitespace separated list of IDs
5203 *
5204 * Selects elements by their unique ID.
5205 *
5206 * Returns a node-set of selected elements.
5207 */
5208static xmlNodeSetPtr
5209xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
5210 xmlNodeSetPtr ret;
5211 const xmlChar *cur = ids;
5212 xmlChar *ID;
5213 xmlAttrPtr attr;
5214 xmlNodePtr elem = NULL;
5215
5216 ret = xmlXPathNodeSetCreate(NULL);
5217
5218 while (IS_BLANK(*cur)) cur++;
5219 while (*cur != 0) {
5220 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
5221 (*cur == '.') || (*cur == '-') ||
5222 (*cur == '_') || (*cur == ':') ||
5223 (IS_COMBINING(*cur)) ||
5224 (IS_EXTENDER(*cur)))
5225 cur++;
5226
5227 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
5228
5229 ID = xmlStrndup(ids, cur - ids);
5230 attr = xmlGetID(doc, ID);
5231 if (attr != NULL) {
5232 elem = attr->parent;
5233 xmlXPathNodeSetAdd(ret, elem);
5234 }
5235 if (ID != NULL)
5236 xmlFree(ID);
5237
5238 while (IS_BLANK(*cur)) cur++;
5239 ids = cur;
5240 }
5241 return(ret);
5242}
5243
5244/**
Owen Taylor3473f882001-02-23 17:55:21 +00005245 * xmlXPathIdFunction:
5246 * @ctxt: the XPath Parser context
5247 * @nargs: the number of arguments
5248 *
5249 * Implement the id() XPath function
5250 * node-set id(object)
5251 * The id function selects elements by their unique ID
5252 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
5253 * then the result is the union of the result of applying id to the
5254 * string value of each of the nodes in the argument node-set. When the
5255 * argument to id is of any other type, the argument is converted to a
5256 * string as if by a call to the string function; the string is split
5257 * into a whitespace-separated list of tokens (whitespace is any sequence
5258 * of characters matching the production S); the result is a node-set
5259 * containing the elements in the same document as the context node that
5260 * have a unique ID equal to any of the tokens in the list.
5261 */
5262void
5263xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005264 xmlChar *tokens;
5265 xmlNodeSetPtr ret;
5266 xmlXPathObjectPtr obj;
Owen Taylor3473f882001-02-23 17:55:21 +00005267
5268 CHECK_ARITY(1);
5269 obj = valuePop(ctxt);
5270 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
5271 if (obj->type == XPATH_NODESET) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005272 xmlNodeSetPtr ns;
Owen Taylor3473f882001-02-23 17:55:21 +00005273 int i;
5274
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005275 ret = xmlXPathNodeSetCreate(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005276
Daniel Veillard911f49a2001-04-07 15:39:35 +00005277 if (obj->nodesetval != NULL) {
5278 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005279 tokens =
5280 xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
5281 ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
5282 ret = xmlXPathNodeSetMerge(ret, ns);
5283 xmlXPathFreeNodeSet(ns);
5284 if (tokens != NULL)
5285 xmlFree(tokens);
Daniel Veillard911f49a2001-04-07 15:39:35 +00005286 }
Owen Taylor3473f882001-02-23 17:55:21 +00005287 }
5288
5289 xmlXPathFreeObject(obj);
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005290 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005291 return;
5292 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005293 obj = xmlXPathConvertString(obj);
Owen Taylor3473f882001-02-23 17:55:21 +00005294
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005295 ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
5296 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005297
Owen Taylor3473f882001-02-23 17:55:21 +00005298 xmlXPathFreeObject(obj);
5299 return;
5300}
5301
5302/**
5303 * xmlXPathLocalNameFunction:
5304 * @ctxt: the XPath Parser context
5305 * @nargs: the number of arguments
5306 *
5307 * Implement the local-name() XPath function
5308 * string local-name(node-set?)
5309 * The local-name function returns a string containing the local part
5310 * of the name of the node in the argument node-set that is first in
5311 * document order. If the node-set is empty or the first node has no
5312 * name, an empty string is returned. If the argument is omitted it
5313 * defaults to the context node.
5314 */
5315void
5316xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5317 xmlXPathObjectPtr cur;
5318
5319 if (nargs == 0) {
5320 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5321 nargs = 1;
5322 }
5323
5324 CHECK_ARITY(1);
5325 if ((ctxt->value == NULL) ||
5326 ((ctxt->value->type != XPATH_NODESET) &&
5327 (ctxt->value->type != XPATH_XSLT_TREE)))
5328 XP_ERROR(XPATH_INVALID_TYPE);
5329 cur = valuePop(ctxt);
5330
Daniel Veillard911f49a2001-04-07 15:39:35 +00005331 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005332 valuePush(ctxt, xmlXPathNewCString(""));
5333 } else {
5334 int i = 0; /* Should be first in document order !!!!! */
5335 switch (cur->nodesetval->nodeTab[i]->type) {
5336 case XML_ELEMENT_NODE:
5337 case XML_ATTRIBUTE_NODE:
5338 case XML_PI_NODE:
5339 valuePush(ctxt,
5340 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
5341 break;
5342 case XML_NAMESPACE_DECL:
5343 valuePush(ctxt, xmlXPathNewString(
5344 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
5345 break;
5346 default:
5347 valuePush(ctxt, xmlXPathNewCString(""));
5348 }
5349 }
5350 xmlXPathFreeObject(cur);
5351}
5352
5353/**
5354 * xmlXPathNamespaceURIFunction:
5355 * @ctxt: the XPath Parser context
5356 * @nargs: the number of arguments
5357 *
5358 * Implement the namespace-uri() XPath function
5359 * string namespace-uri(node-set?)
5360 * The namespace-uri function returns a string containing the
5361 * namespace URI of the expanded name of the node in the argument
5362 * node-set that is first in document order. If the node-set is empty,
5363 * the first node has no name, or the expanded name has no namespace
5364 * URI, an empty string is returned. If the argument is omitted it
5365 * defaults to the context node.
5366 */
5367void
5368xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5369 xmlXPathObjectPtr cur;
5370
5371 if (nargs == 0) {
5372 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5373 nargs = 1;
5374 }
5375 CHECK_ARITY(1);
5376 if ((ctxt->value == NULL) ||
5377 ((ctxt->value->type != XPATH_NODESET) &&
5378 (ctxt->value->type != XPATH_XSLT_TREE)))
5379 XP_ERROR(XPATH_INVALID_TYPE);
5380 cur = valuePop(ctxt);
5381
Daniel Veillard911f49a2001-04-07 15:39:35 +00005382 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005383 valuePush(ctxt, xmlXPathNewCString(""));
5384 } else {
5385 int i = 0; /* Should be first in document order !!!!! */
5386 switch (cur->nodesetval->nodeTab[i]->type) {
5387 case XML_ELEMENT_NODE:
5388 case XML_ATTRIBUTE_NODE:
5389 if (cur->nodesetval->nodeTab[i]->ns == NULL)
5390 valuePush(ctxt, xmlXPathNewCString(""));
5391 else
5392 valuePush(ctxt, xmlXPathNewString(
5393 cur->nodesetval->nodeTab[i]->ns->href));
5394 break;
5395 default:
5396 valuePush(ctxt, xmlXPathNewCString(""));
5397 }
5398 }
5399 xmlXPathFreeObject(cur);
5400}
5401
5402/**
5403 * xmlXPathNameFunction:
5404 * @ctxt: the XPath Parser context
5405 * @nargs: the number of arguments
5406 *
5407 * Implement the name() XPath function
5408 * string name(node-set?)
5409 * The name function returns a string containing a QName representing
5410 * the name of the node in the argument node-set that is first in documenti
5411 * order. The QName must represent the name with respect to the namespace
5412 * declarations in effect on the node whose name is being represented.
5413 * Typically, this will be the form in which the name occurred in the XML
5414 * source. This need not be the case if there are namespace declarations
5415 * in effect on the node that associate multiple prefixes with the same
5416 * namespace. However, an implementation may include information about
5417 * the original prefix in its representation of nodes; in this case, an
5418 * implementation can ensure that the returned string is always the same
5419 * as the QName used in the XML source. If the argument it omitted it
5420 * defaults to the context node.
5421 * Libxml keep the original prefix so the "real qualified name" used is
5422 * returned.
5423 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005424static void
Daniel Veillard04383752001-07-08 14:27:15 +00005425xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
5426{
Owen Taylor3473f882001-02-23 17:55:21 +00005427 xmlXPathObjectPtr cur;
5428
5429 if (nargs == 0) {
Daniel Veillard04383752001-07-08 14:27:15 +00005430 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5431 nargs = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005432 }
5433
5434 CHECK_ARITY(1);
Daniel Veillard04383752001-07-08 14:27:15 +00005435 if ((ctxt->value == NULL) ||
5436 ((ctxt->value->type != XPATH_NODESET) &&
5437 (ctxt->value->type != XPATH_XSLT_TREE)))
5438 XP_ERROR(XPATH_INVALID_TYPE);
Owen Taylor3473f882001-02-23 17:55:21 +00005439 cur = valuePop(ctxt);
5440
Daniel Veillard911f49a2001-04-07 15:39:35 +00005441 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Daniel Veillard04383752001-07-08 14:27:15 +00005442 valuePush(ctxt, xmlXPathNewCString(""));
Owen Taylor3473f882001-02-23 17:55:21 +00005443 } else {
Daniel Veillard04383752001-07-08 14:27:15 +00005444 int i = 0; /* Should be first in document order !!!!! */
Owen Taylor3473f882001-02-23 17:55:21 +00005445
Daniel Veillard04383752001-07-08 14:27:15 +00005446 switch (cur->nodesetval->nodeTab[i]->type) {
5447 case XML_ELEMENT_NODE:
5448 case XML_ATTRIBUTE_NODE:
5449 if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
5450 (cur->nodesetval->nodeTab[i]->ns->prefix == NULL))
5451 valuePush(ctxt,
5452 xmlXPathNewString(cur->nodesetval->
5453 nodeTab[i]->name));
5454
5455 else {
5456 char name[2000];
5457
5458 snprintf(name, sizeof(name), "%s:%s",
5459 (char *) cur->nodesetval->nodeTab[i]->ns->
5460 prefix,
5461 (char *) cur->nodesetval->nodeTab[i]->name);
5462 name[sizeof(name) - 1] = 0;
5463 valuePush(ctxt, xmlXPathNewCString(name));
5464 }
5465 break;
5466 default:
5467 valuePush(ctxt,
5468 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
5469 xmlXPathLocalNameFunction(ctxt, 1);
5470 }
Owen Taylor3473f882001-02-23 17:55:21 +00005471 }
5472 xmlXPathFreeObject(cur);
5473}
5474
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005475
5476/**
Owen Taylor3473f882001-02-23 17:55:21 +00005477 * xmlXPathStringFunction:
5478 * @ctxt: the XPath Parser context
5479 * @nargs: the number of arguments
5480 *
5481 * Implement the string() XPath function
5482 * string string(object?)
5483 * he string function converts an object to a string as follows:
5484 * - A node-set is converted to a string by returning the value of
5485 * the node in the node-set that is first in document order.
5486 * If the node-set is empty, an empty string is returned.
5487 * - A number is converted to a string as follows
5488 * + NaN is converted to the string NaN
5489 * + positive zero is converted to the string 0
5490 * + negative zero is converted to the string 0
5491 * + positive infinity is converted to the string Infinity
5492 * + negative infinity is converted to the string -Infinity
5493 * + if the number is an integer, the number is represented in
5494 * decimal form as a Number with no decimal point and no leading
5495 * zeros, preceded by a minus sign (-) if the number is negative
5496 * + otherwise, the number is represented in decimal form as a
5497 * Number including a decimal point with at least one digit
5498 * before the decimal point and at least one digit after the
5499 * decimal point, preceded by a minus sign (-) if the number
5500 * is negative; there must be no leading zeros before the decimal
5501 * point apart possibly from the one required digit immediatelyi
5502 * before the decimal point; beyond the one required digit
5503 * after the decimal point there must be as many, but only as
5504 * many, more digits as are needed to uniquely distinguish the
5505 * number from all other IEEE 754 numeric values.
5506 * - The boolean false value is converted to the string false.
5507 * The boolean true value is converted to the string true.
5508 *
5509 * If the argument is omitted, it defaults to a node-set with the
5510 * context node as its only member.
5511 */
5512void
5513xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5514 xmlXPathObjectPtr cur;
5515
5516 if (nargs == 0) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005517 valuePush(ctxt,
5518 xmlXPathWrapString(
5519 xmlXPathCastNodeToString(ctxt->context->node)));
5520 return;
Owen Taylor3473f882001-02-23 17:55:21 +00005521 }
5522
5523 CHECK_ARITY(1);
5524 cur = valuePop(ctxt);
5525 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005526 cur = xmlXPathConvertString(cur);
5527 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005528}
5529
5530/**
5531 * xmlXPathStringLengthFunction:
5532 * @ctxt: the XPath Parser context
5533 * @nargs: the number of arguments
5534 *
5535 * Implement the string-length() XPath function
5536 * number string-length(string?)
5537 * The string-length returns the number of characters in the string
5538 * (see [3.6 Strings]). If the argument is omitted, it defaults to
5539 * the context node converted to a string, in other words the value
5540 * of the context node.
5541 */
5542void
5543xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5544 xmlXPathObjectPtr cur;
5545
5546 if (nargs == 0) {
5547 if (ctxt->context->node == NULL) {
5548 valuePush(ctxt, xmlXPathNewFloat(0));
5549 } else {
5550 xmlChar *content;
5551
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005552 content = xmlXPathCastNodeToString(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005553 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00005554 xmlFree(content);
5555 }
5556 return;
5557 }
5558 CHECK_ARITY(1);
5559 CAST_TO_STRING;
5560 CHECK_TYPE(XPATH_STRING);
5561 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005562 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00005563 xmlXPathFreeObject(cur);
5564}
5565
5566/**
5567 * xmlXPathConcatFunction:
5568 * @ctxt: the XPath Parser context
5569 * @nargs: the number of arguments
5570 *
5571 * Implement the concat() XPath function
5572 * string concat(string, string, string*)
5573 * The concat function returns the concatenation of its arguments.
5574 */
5575void
5576xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5577 xmlXPathObjectPtr cur, newobj;
5578 xmlChar *tmp;
5579
5580 if (nargs < 2) {
5581 CHECK_ARITY(2);
5582 }
5583
5584 CAST_TO_STRING;
5585 cur = valuePop(ctxt);
5586 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
5587 xmlXPathFreeObject(cur);
5588 return;
5589 }
5590 nargs--;
5591
5592 while (nargs > 0) {
5593 CAST_TO_STRING;
5594 newobj = valuePop(ctxt);
5595 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
5596 xmlXPathFreeObject(newobj);
5597 xmlXPathFreeObject(cur);
5598 XP_ERROR(XPATH_INVALID_TYPE);
5599 }
5600 tmp = xmlStrcat(newobj->stringval, cur->stringval);
5601 newobj->stringval = cur->stringval;
5602 cur->stringval = tmp;
5603
5604 xmlXPathFreeObject(newobj);
5605 nargs--;
5606 }
5607 valuePush(ctxt, cur);
5608}
5609
5610/**
5611 * xmlXPathContainsFunction:
5612 * @ctxt: the XPath Parser context
5613 * @nargs: the number of arguments
5614 *
5615 * Implement the contains() XPath function
5616 * boolean contains(string, string)
5617 * The contains function returns true if the first argument string
5618 * contains the second argument string, and otherwise returns false.
5619 */
5620void
5621xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5622 xmlXPathObjectPtr hay, needle;
5623
5624 CHECK_ARITY(2);
5625 CAST_TO_STRING;
5626 CHECK_TYPE(XPATH_STRING);
5627 needle = valuePop(ctxt);
5628 CAST_TO_STRING;
5629 hay = valuePop(ctxt);
5630 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5631 xmlXPathFreeObject(hay);
5632 xmlXPathFreeObject(needle);
5633 XP_ERROR(XPATH_INVALID_TYPE);
5634 }
5635 if (xmlStrstr(hay->stringval, needle->stringval))
5636 valuePush(ctxt, xmlXPathNewBoolean(1));
5637 else
5638 valuePush(ctxt, xmlXPathNewBoolean(0));
5639 xmlXPathFreeObject(hay);
5640 xmlXPathFreeObject(needle);
5641}
5642
5643/**
5644 * xmlXPathStartsWithFunction:
5645 * @ctxt: the XPath Parser context
5646 * @nargs: the number of arguments
5647 *
5648 * Implement the starts-with() XPath function
5649 * boolean starts-with(string, string)
5650 * The starts-with function returns true if the first argument string
5651 * starts with the second argument string, and otherwise returns false.
5652 */
5653void
5654xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5655 xmlXPathObjectPtr hay, needle;
5656 int n;
5657
5658 CHECK_ARITY(2);
5659 CAST_TO_STRING;
5660 CHECK_TYPE(XPATH_STRING);
5661 needle = valuePop(ctxt);
5662 CAST_TO_STRING;
5663 hay = valuePop(ctxt);
5664 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5665 xmlXPathFreeObject(hay);
5666 xmlXPathFreeObject(needle);
5667 XP_ERROR(XPATH_INVALID_TYPE);
5668 }
5669 n = xmlStrlen(needle->stringval);
5670 if (xmlStrncmp(hay->stringval, needle->stringval, n))
5671 valuePush(ctxt, xmlXPathNewBoolean(0));
5672 else
5673 valuePush(ctxt, xmlXPathNewBoolean(1));
5674 xmlXPathFreeObject(hay);
5675 xmlXPathFreeObject(needle);
5676}
5677
5678/**
5679 * xmlXPathSubstringFunction:
5680 * @ctxt: the XPath Parser context
5681 * @nargs: the number of arguments
5682 *
5683 * Implement the substring() XPath function
5684 * string substring(string, number, number?)
5685 * The substring function returns the substring of the first argument
5686 * starting at the position specified in the second argument with
5687 * length specified in the third argument. For example,
5688 * substring("12345",2,3) returns "234". If the third argument is not
5689 * specified, it returns the substring starting at the position specified
5690 * in the second argument and continuing to the end of the string. For
5691 * example, substring("12345",2) returns "2345". More precisely, each
5692 * character in the string (see [3.6 Strings]) is considered to have a
5693 * numeric position: the position of the first character is 1, the position
5694 * of the second character is 2 and so on. The returned substring contains
5695 * those characters for which the position of the character is greater than
5696 * or equal to the second argument and, if the third argument is specified,
5697 * less than the sum of the second and third arguments; the comparisons
5698 * and addition used for the above follow the standard IEEE 754 rules. Thus:
5699 * - substring("12345", 1.5, 2.6) returns "234"
5700 * - substring("12345", 0, 3) returns "12"
5701 * - substring("12345", 0 div 0, 3) returns ""
5702 * - substring("12345", 1, 0 div 0) returns ""
5703 * - substring("12345", -42, 1 div 0) returns "12345"
5704 * - substring("12345", -1 div 0, 1 div 0) returns ""
5705 */
5706void
5707xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5708 xmlXPathObjectPtr str, start, len;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005709 double le=0, in;
5710 int i, l, m;
Owen Taylor3473f882001-02-23 17:55:21 +00005711 xmlChar *ret;
5712
Owen Taylor3473f882001-02-23 17:55:21 +00005713 if (nargs < 2) {
5714 CHECK_ARITY(2);
5715 }
5716 if (nargs > 3) {
5717 CHECK_ARITY(3);
5718 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005719 /*
5720 * take care of possible last (position) argument
5721 */
Owen Taylor3473f882001-02-23 17:55:21 +00005722 if (nargs == 3) {
5723 CAST_TO_NUMBER;
5724 CHECK_TYPE(XPATH_NUMBER);
5725 len = valuePop(ctxt);
5726 le = len->floatval;
5727 xmlXPathFreeObject(len);
Owen Taylor3473f882001-02-23 17:55:21 +00005728 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005729
Owen Taylor3473f882001-02-23 17:55:21 +00005730 CAST_TO_NUMBER;
5731 CHECK_TYPE(XPATH_NUMBER);
5732 start = valuePop(ctxt);
5733 in = start->floatval;
5734 xmlXPathFreeObject(start);
5735 CAST_TO_STRING;
5736 CHECK_TYPE(XPATH_STRING);
5737 str = valuePop(ctxt);
Daniel Veillard97ac1312001-05-30 19:14:17 +00005738 m = xmlUTF8Strlen((const unsigned char *)str->stringval);
Owen Taylor3473f882001-02-23 17:55:21 +00005739
Daniel Veillard97ac1312001-05-30 19:14:17 +00005740 /*
5741 * If last pos not present, calculate last position
5742 */
5743 if (nargs != 3)
5744 le = m;
5745
5746 /*
5747 * To meet our requirements, initial index calculations
5748 * must be done before we convert to integer format
5749 *
5750 * First we normalize indices
5751 */
5752 in -= 1.0;
5753 le += in;
5754 if (in < 0.0)
5755 in = 0.0;
5756 if (le > (double)m)
5757 le = (double)m;
5758
5759 /*
5760 * Now we go to integer form, rounding up
5761 */
Owen Taylor3473f882001-02-23 17:55:21 +00005762 i = (int) in;
5763 if (((double)i) != in) i++;
5764
Owen Taylor3473f882001-02-23 17:55:21 +00005765 l = (int) le;
5766 if (((double)l) != le) l++;
5767
Daniel Veillard97ac1312001-05-30 19:14:17 +00005768 if (l > m) l=m;
Owen Taylor3473f882001-02-23 17:55:21 +00005769
5770 /* number of chars to copy */
5771 l -= i;
5772
Daniel Veillard97ac1312001-05-30 19:14:17 +00005773 ret = xmlUTF8Strsub(str->stringval, i, l);
Owen Taylor3473f882001-02-23 17:55:21 +00005774 if (ret == NULL)
5775 valuePush(ctxt, xmlXPathNewCString(""));
5776 else {
5777 valuePush(ctxt, xmlXPathNewString(ret));
5778 xmlFree(ret);
5779 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005780
Owen Taylor3473f882001-02-23 17:55:21 +00005781 xmlXPathFreeObject(str);
5782}
5783
5784/**
5785 * xmlXPathSubstringBeforeFunction:
5786 * @ctxt: the XPath Parser context
5787 * @nargs: the number of arguments
5788 *
5789 * Implement the substring-before() XPath function
5790 * string substring-before(string, string)
5791 * The substring-before function returns the substring of the first
5792 * argument string that precedes the first occurrence of the second
5793 * argument string in the first argument string, or the empty string
5794 * if the first argument string does not contain the second argument
5795 * string. For example, substring-before("1999/04/01","/") returns 1999.
5796 */
5797void
5798xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5799 xmlXPathObjectPtr str;
5800 xmlXPathObjectPtr find;
5801 xmlBufferPtr target;
5802 const xmlChar *point;
5803 int offset;
5804
5805 CHECK_ARITY(2);
5806 CAST_TO_STRING;
5807 find = valuePop(ctxt);
5808 CAST_TO_STRING;
5809 str = valuePop(ctxt);
5810
5811 target = xmlBufferCreate();
5812 if (target) {
5813 point = xmlStrstr(str->stringval, find->stringval);
5814 if (point) {
5815 offset = (int)(point - str->stringval);
5816 xmlBufferAdd(target, str->stringval, offset);
5817 }
5818 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5819 xmlBufferFree(target);
5820 }
5821
5822 xmlXPathFreeObject(str);
5823 xmlXPathFreeObject(find);
5824}
5825
5826/**
5827 * xmlXPathSubstringAfterFunction:
5828 * @ctxt: the XPath Parser context
5829 * @nargs: the number of arguments
5830 *
5831 * Implement the substring-after() XPath function
5832 * string substring-after(string, string)
5833 * The substring-after function returns the substring of the first
5834 * argument string that follows the first occurrence of the second
5835 * argument string in the first argument string, or the empty stringi
5836 * if the first argument string does not contain the second argument
5837 * string. For example, substring-after("1999/04/01","/") returns 04/01,
5838 * and substring-after("1999/04/01","19") returns 99/04/01.
5839 */
5840void
5841xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5842 xmlXPathObjectPtr str;
5843 xmlXPathObjectPtr find;
5844 xmlBufferPtr target;
5845 const xmlChar *point;
5846 int offset;
5847
5848 CHECK_ARITY(2);
5849 CAST_TO_STRING;
5850 find = valuePop(ctxt);
5851 CAST_TO_STRING;
5852 str = valuePop(ctxt);
5853
5854 target = xmlBufferCreate();
5855 if (target) {
5856 point = xmlStrstr(str->stringval, find->stringval);
5857 if (point) {
5858 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
5859 xmlBufferAdd(target, &str->stringval[offset],
5860 xmlStrlen(str->stringval) - offset);
5861 }
5862 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5863 xmlBufferFree(target);
5864 }
5865
5866 xmlXPathFreeObject(str);
5867 xmlXPathFreeObject(find);
5868}
5869
5870/**
5871 * xmlXPathNormalizeFunction:
5872 * @ctxt: the XPath Parser context
5873 * @nargs: the number of arguments
5874 *
5875 * Implement the normalize-space() XPath function
5876 * string normalize-space(string?)
5877 * The normalize-space function returns the argument string with white
5878 * space normalized by stripping leading and trailing whitespace
5879 * and replacing sequences of whitespace characters by a single
5880 * space. Whitespace characters are the same allowed by the S production
5881 * in XML. If the argument is omitted, it defaults to the context
5882 * node converted to a string, in other words the value of the context node.
5883 */
5884void
5885xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5886 xmlXPathObjectPtr obj = NULL;
5887 xmlChar *source = NULL;
5888 xmlBufferPtr target;
5889 xmlChar blank;
5890
5891 if (nargs == 0) {
5892 /* Use current context node */
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005893 valuePush(ctxt,
5894 xmlXPathWrapString(
5895 xmlXPathCastNodeToString(ctxt->context->node)));
Owen Taylor3473f882001-02-23 17:55:21 +00005896 nargs = 1;
5897 }
5898
5899 CHECK_ARITY(1);
5900 CAST_TO_STRING;
5901 CHECK_TYPE(XPATH_STRING);
5902 obj = valuePop(ctxt);
5903 source = obj->stringval;
5904
5905 target = xmlBufferCreate();
5906 if (target && source) {
5907
5908 /* Skip leading whitespaces */
5909 while (IS_BLANK(*source))
5910 source++;
5911
5912 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
5913 blank = 0;
5914 while (*source) {
5915 if (IS_BLANK(*source)) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005916 blank = 0x20;
Owen Taylor3473f882001-02-23 17:55:21 +00005917 } else {
5918 if (blank) {
5919 xmlBufferAdd(target, &blank, 1);
5920 blank = 0;
5921 }
5922 xmlBufferAdd(target, source, 1);
5923 }
5924 source++;
5925 }
5926
5927 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5928 xmlBufferFree(target);
5929 }
5930 xmlXPathFreeObject(obj);
5931}
5932
5933/**
5934 * xmlXPathTranslateFunction:
5935 * @ctxt: the XPath Parser context
5936 * @nargs: the number of arguments
5937 *
5938 * Implement the translate() XPath function
5939 * string translate(string, string, string)
5940 * The translate function returns the first argument string with
5941 * occurrences of characters in the second argument string replaced
5942 * by the character at the corresponding position in the third argument
5943 * string. For example, translate("bar","abc","ABC") returns the string
5944 * BAr. If there is a character in the second argument string with no
5945 * character at a corresponding position in the third argument string
5946 * (because the second argument string is longer than the third argument
5947 * string), then occurrences of that character in the first argument
5948 * string are removed. For example, translate("--aaa--","abc-","ABC")
5949 * returns "AAA". If a character occurs more than once in second
5950 * argument string, then the first occurrence determines the replacement
5951 * character. If the third argument string is longer than the second
5952 * argument string, then excess characters are ignored.
5953 */
5954void
5955xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00005956 xmlXPathObjectPtr str;
5957 xmlXPathObjectPtr from;
5958 xmlXPathObjectPtr to;
5959 xmlBufferPtr target;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005960 int offset, max;
Daniel Veillarde043ee12001-04-16 14:08:07 +00005961 xmlChar ch;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005962 xmlChar *point;
5963 xmlChar *cptr;
Owen Taylor3473f882001-02-23 17:55:21 +00005964
Daniel Veillarde043ee12001-04-16 14:08:07 +00005965 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00005966
Daniel Veillarde043ee12001-04-16 14:08:07 +00005967 CAST_TO_STRING;
5968 to = valuePop(ctxt);
5969 CAST_TO_STRING;
5970 from = valuePop(ctxt);
5971 CAST_TO_STRING;
5972 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005973
Daniel Veillarde043ee12001-04-16 14:08:07 +00005974 target = xmlBufferCreate();
5975 if (target) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005976 max = xmlUTF8Strlen(to->stringval);
5977 for (cptr = str->stringval; (ch=*cptr); ) {
5978 offset = xmlUTF8Strloc(from->stringval, cptr);
5979 if (offset >= 0) {
5980 if (offset < max) {
5981 point = xmlUTF8Strpos(to->stringval, offset);
5982 if (point)
5983 xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1));
5984 }
5985 } else
5986 xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
5987
5988 /* Step to next character in input */
5989 cptr++;
5990 if ( ch & 0x80 ) {
5991 /* if not simple ascii, verify proper format */
5992 if ( (ch & 0xc0) != 0xc0 ) {
5993 xmlGenericError(xmlGenericErrorContext,
5994 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
5995 break;
5996 }
5997 /* then skip over remaining bytes for this char */
5998 while ( (ch <<= 1) & 0x80 )
5999 if ( (*cptr++ & 0xc0) != 0x80 ) {
6000 xmlGenericError(xmlGenericErrorContext,
6001 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
6002 break;
6003 }
6004 if (ch & 0x80) /* must have had error encountered */
6005 break;
6006 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00006007 }
Owen Taylor3473f882001-02-23 17:55:21 +00006008 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00006009 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
6010 xmlBufferFree(target);
6011 xmlXPathFreeObject(str);
6012 xmlXPathFreeObject(from);
6013 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00006014}
6015
6016/**
6017 * xmlXPathBooleanFunction:
6018 * @ctxt: the XPath Parser context
6019 * @nargs: the number of arguments
6020 *
6021 * Implement the boolean() XPath function
6022 * boolean boolean(object)
6023 * he boolean function converts its argument to a boolean as follows:
6024 * - a number is true if and only if it is neither positive or
6025 * negative zero nor NaN
6026 * - a node-set is true if and only if it is non-empty
6027 * - a string is true if and only if its length is non-zero
6028 */
6029void
6030xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6031 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00006032
6033 CHECK_ARITY(1);
6034 cur = valuePop(ctxt);
6035 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006036 cur = xmlXPathConvertBoolean(cur);
6037 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00006038}
6039
6040/**
6041 * xmlXPathNotFunction:
6042 * @ctxt: the XPath Parser context
6043 * @nargs: the number of arguments
6044 *
6045 * Implement the not() XPath function
6046 * boolean not(boolean)
6047 * The not function returns true if its argument is false,
6048 * and false otherwise.
6049 */
6050void
6051xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6052 CHECK_ARITY(1);
6053 CAST_TO_BOOLEAN;
6054 CHECK_TYPE(XPATH_BOOLEAN);
6055 ctxt->value->boolval = ! ctxt->value->boolval;
6056}
6057
6058/**
6059 * xmlXPathTrueFunction:
6060 * @ctxt: the XPath Parser context
6061 * @nargs: the number of arguments
6062 *
6063 * Implement the true() XPath function
6064 * boolean true()
6065 */
6066void
6067xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6068 CHECK_ARITY(0);
6069 valuePush(ctxt, xmlXPathNewBoolean(1));
6070}
6071
6072/**
6073 * xmlXPathFalseFunction:
6074 * @ctxt: the XPath Parser context
6075 * @nargs: the number of arguments
6076 *
6077 * Implement the false() XPath function
6078 * boolean false()
6079 */
6080void
6081xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6082 CHECK_ARITY(0);
6083 valuePush(ctxt, xmlXPathNewBoolean(0));
6084}
6085
6086/**
6087 * xmlXPathLangFunction:
6088 * @ctxt: the XPath Parser context
6089 * @nargs: the number of arguments
6090 *
6091 * Implement the lang() XPath function
6092 * boolean lang(string)
6093 * The lang function returns true or false depending on whether the
6094 * language of the context node as specified by xml:lang attributes
6095 * is the same as or is a sublanguage of the language specified by
6096 * the argument string. The language of the context node is determined
6097 * by the value of the xml:lang attribute on the context node, or, if
6098 * the context node has no xml:lang attribute, by the value of the
6099 * xml:lang attribute on the nearest ancestor of the context node that
6100 * has an xml:lang attribute. If there is no such attribute, then lang
6101 * returns false. If there is such an attribute, then lang returns
6102 * true if the attribute value is equal to the argument ignoring case,
6103 * or if there is some suffix starting with - such that the attribute
6104 * value is equal to the argument ignoring that suffix of the attribute
6105 * value and ignoring case.
6106 */
6107void
6108xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6109 xmlXPathObjectPtr val;
6110 const xmlChar *theLang;
6111 const xmlChar *lang;
6112 int ret = 0;
6113 int i;
6114
6115 CHECK_ARITY(1);
6116 CAST_TO_STRING;
6117 CHECK_TYPE(XPATH_STRING);
6118 val = valuePop(ctxt);
6119 lang = val->stringval;
6120 theLang = xmlNodeGetLang(ctxt->context->node);
6121 if ((theLang != NULL) && (lang != NULL)) {
6122 for (i = 0;lang[i] != 0;i++)
6123 if (toupper(lang[i]) != toupper(theLang[i]))
6124 goto not_equal;
6125 ret = 1;
6126 }
6127not_equal:
6128 xmlXPathFreeObject(val);
6129 valuePush(ctxt, xmlXPathNewBoolean(ret));
6130}
6131
6132/**
6133 * xmlXPathNumberFunction:
6134 * @ctxt: the XPath Parser context
6135 * @nargs: the number of arguments
6136 *
6137 * Implement the number() XPath function
6138 * number number(object?)
6139 */
6140void
6141xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6142 xmlXPathObjectPtr cur;
6143 double res;
6144
6145 if (nargs == 0) {
6146 if (ctxt->context->node == NULL) {
6147 valuePush(ctxt, xmlXPathNewFloat(0.0));
6148 } else {
6149 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
6150
6151 res = xmlXPathStringEvalNumber(content);
6152 valuePush(ctxt, xmlXPathNewFloat(res));
6153 xmlFree(content);
6154 }
6155 return;
6156 }
6157
6158 CHECK_ARITY(1);
6159 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006160 cur = xmlXPathConvertNumber(cur);
6161 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00006162}
6163
6164/**
6165 * xmlXPathSumFunction:
6166 * @ctxt: the XPath Parser context
6167 * @nargs: the number of arguments
6168 *
6169 * Implement the sum() XPath function
6170 * number sum(node-set)
6171 * The sum function returns the sum of the values of the nodes in
6172 * the argument node-set.
6173 */
6174void
6175xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6176 xmlXPathObjectPtr cur;
6177 int i;
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006178 double res = 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +00006179
6180 CHECK_ARITY(1);
6181 if ((ctxt->value == NULL) ||
6182 ((ctxt->value->type != XPATH_NODESET) &&
6183 (ctxt->value->type != XPATH_XSLT_TREE)))
6184 XP_ERROR(XPATH_INVALID_TYPE);
6185 cur = valuePop(ctxt);
6186
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006187 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006188 valuePush(ctxt, xmlXPathNewFloat(0.0));
6189 } else {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006190 for (i = 0; i < cur->nodesetval->nodeNr; i++) {
6191 res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00006192 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006193 valuePush(ctxt, xmlXPathNewFloat(res));
Owen Taylor3473f882001-02-23 17:55:21 +00006194 }
6195 xmlXPathFreeObject(cur);
6196}
6197
6198/**
6199 * xmlXPathFloorFunction:
6200 * @ctxt: the XPath Parser context
6201 * @nargs: the number of arguments
6202 *
6203 * Implement the floor() XPath function
6204 * number floor(number)
6205 * The floor function returns the largest (closest to positive infinity)
6206 * number that is not greater than the argument and that is an integer.
6207 */
6208void
6209xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6210 CHECK_ARITY(1);
6211 CAST_TO_NUMBER;
6212 CHECK_TYPE(XPATH_NUMBER);
6213#if 0
6214 ctxt->value->floatval = floor(ctxt->value->floatval);
6215#else
6216 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
6217 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
6218#endif
6219}
6220
6221/**
6222 * xmlXPathCeilingFunction:
6223 * @ctxt: the XPath Parser context
6224 * @nargs: the number of arguments
6225 *
6226 * Implement the ceiling() XPath function
6227 * number ceiling(number)
6228 * The ceiling function returns the smallest (closest to negative infinity)
6229 * number that is not less than the argument and that is an integer.
6230 */
6231void
6232xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6233 double f;
6234
6235 CHECK_ARITY(1);
6236 CAST_TO_NUMBER;
6237 CHECK_TYPE(XPATH_NUMBER);
6238
6239#if 0
6240 ctxt->value->floatval = ceil(ctxt->value->floatval);
6241#else
6242 f = (double)((int) ctxt->value->floatval);
6243 if (f != ctxt->value->floatval)
6244 ctxt->value->floatval = f + 1;
6245#endif
6246}
6247
6248/**
6249 * xmlXPathRoundFunction:
6250 * @ctxt: the XPath Parser context
6251 * @nargs: the number of arguments
6252 *
6253 * Implement the round() XPath function
6254 * number round(number)
6255 * The round function returns the number that is closest to the
6256 * argument and that is an integer. If there are two such numbers,
6257 * then the one that is even is returned.
6258 */
6259void
6260xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6261 double f;
6262
6263 CHECK_ARITY(1);
6264 CAST_TO_NUMBER;
6265 CHECK_TYPE(XPATH_NUMBER);
6266
6267 if ((ctxt->value->floatval == xmlXPathNAN) ||
6268 (ctxt->value->floatval == xmlXPathPINF) ||
6269 (ctxt->value->floatval == xmlXPathNINF) ||
6270 (ctxt->value->floatval == 0.0))
6271 return;
6272
6273#if 0
6274 f = floor(ctxt->value->floatval);
6275#else
6276 f = (double)((int) ctxt->value->floatval);
6277#endif
6278 if (ctxt->value->floatval < f + 0.5)
6279 ctxt->value->floatval = f;
6280 else
6281 ctxt->value->floatval = f + 1;
6282}
6283
6284/************************************************************************
6285 * *
6286 * The Parser *
6287 * *
6288 ************************************************************************/
6289
6290/*
6291 * a couple of forward declarations since we use a recursive call based
6292 * implementation.
6293 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006294static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006295static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006296static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006297#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006298static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
6299#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006300#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006301static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006302#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00006303static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
6304 int qualified);
Owen Taylor3473f882001-02-23 17:55:21 +00006305
6306/**
Daniel Veillard61d80a22001-04-27 17:13:01 +00006307 * xmlXPathCurrentChar:
6308 * @ctxt: the XPath parser context
6309 * @cur: pointer to the beginning of the char
6310 * @len: pointer to the length of the char read
6311 *
6312 * The current char value, if using UTF-8 this may actaully span multiple
6313 * bytes in the input buffer.
6314 *
6315 * Returns the current char value and its lenght
6316 */
6317
6318static int
6319xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
6320 unsigned char c;
6321 unsigned int val;
6322 const xmlChar *cur;
6323
6324 if (ctxt == NULL)
6325 return(0);
6326 cur = ctxt->cur;
6327
6328 /*
6329 * We are supposed to handle UTF8, check it's valid
6330 * From rfc2044: encoding of the Unicode values on UTF-8:
6331 *
6332 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
6333 * 0000 0000-0000 007F 0xxxxxxx
6334 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
6335 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
6336 *
6337 * Check for the 0x110000 limit too
6338 */
6339 c = *cur;
6340 if (c & 0x80) {
6341 if ((cur[1] & 0xc0) != 0x80)
6342 goto encoding_error;
6343 if ((c & 0xe0) == 0xe0) {
6344
6345 if ((cur[2] & 0xc0) != 0x80)
6346 goto encoding_error;
6347 if ((c & 0xf0) == 0xf0) {
6348 if (((c & 0xf8) != 0xf0) ||
6349 ((cur[3] & 0xc0) != 0x80))
6350 goto encoding_error;
6351 /* 4-byte code */
6352 *len = 4;
6353 val = (cur[0] & 0x7) << 18;
6354 val |= (cur[1] & 0x3f) << 12;
6355 val |= (cur[2] & 0x3f) << 6;
6356 val |= cur[3] & 0x3f;
6357 } else {
6358 /* 3-byte code */
6359 *len = 3;
6360 val = (cur[0] & 0xf) << 12;
6361 val |= (cur[1] & 0x3f) << 6;
6362 val |= cur[2] & 0x3f;
6363 }
6364 } else {
6365 /* 2-byte code */
6366 *len = 2;
6367 val = (cur[0] & 0x1f) << 6;
6368 val |= cur[1] & 0x3f;
6369 }
6370 if (!IS_CHAR(val)) {
6371 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
6372 }
6373 return(val);
6374 } else {
6375 /* 1-byte code */
6376 *len = 1;
6377 return((int) *cur);
6378 }
6379encoding_error:
6380 /*
6381 * If we detect an UTF8 error that probably mean that the
6382 * input encoding didn't get properly advertized in the
6383 * declaration header. Report the error and switch the encoding
6384 * to ISO-Latin-1 (if you don't like this policy, just declare the
6385 * encoding !)
6386 */
Daniel Veillard42596ad2001-05-22 16:57:14 +00006387 *len = 0;
Daniel Veillard61d80a22001-04-27 17:13:01 +00006388 XP_ERROR0(XPATH_ENCODING_ERROR);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006389}
6390
6391/**
Owen Taylor3473f882001-02-23 17:55:21 +00006392 * xmlXPathParseNCName:
6393 * @ctxt: the XPath Parser context
6394 *
6395 * parse an XML namespace non qualified name.
6396 *
6397 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
6398 *
6399 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
6400 * CombiningChar | Extender
6401 *
6402 * Returns the namespace name or NULL
6403 */
6404
6405xmlChar *
6406xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard2156a562001-04-28 12:24:34 +00006407 const xmlChar *in;
6408 xmlChar *ret;
6409 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006410
Daniel Veillard2156a562001-04-28 12:24:34 +00006411 /*
6412 * Accelerator for simple ASCII names
6413 */
6414 in = ctxt->cur;
6415 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6416 ((*in >= 0x41) && (*in <= 0x5A)) ||
6417 (*in == '_')) {
6418 in++;
6419 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6420 ((*in >= 0x41) && (*in <= 0x5A)) ||
6421 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard9a89a8a2001-06-27 11:13:35 +00006422 (*in == '_') || (*in == '.') ||
6423 (*in == '-'))
Daniel Veillard2156a562001-04-28 12:24:34 +00006424 in++;
6425 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
6426 (*in == '[') || (*in == ']') || (*in == ':') ||
6427 (*in == '@') || (*in == '*')) {
6428 count = in - ctxt->cur;
6429 if (count == 0)
6430 return(NULL);
6431 ret = xmlStrndup(ctxt->cur, count);
6432 ctxt->cur = in;
6433 return(ret);
6434 }
6435 }
6436 return(xmlXPathParseNameComplex(ctxt, 0));
Owen Taylor3473f882001-02-23 17:55:21 +00006437}
6438
Daniel Veillard2156a562001-04-28 12:24:34 +00006439
Owen Taylor3473f882001-02-23 17:55:21 +00006440/**
6441 * xmlXPathParseQName:
6442 * @ctxt: the XPath Parser context
6443 * @prefix: a xmlChar **
6444 *
6445 * parse an XML qualified name
6446 *
6447 * [NS 5] QName ::= (Prefix ':')? LocalPart
6448 *
6449 * [NS 6] Prefix ::= NCName
6450 *
6451 * [NS 7] LocalPart ::= NCName
6452 *
6453 * Returns the function returns the local part, and prefix is updated
6454 * to get the Prefix if any.
6455 */
6456
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006457static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00006458xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
6459 xmlChar *ret = NULL;
6460
6461 *prefix = NULL;
6462 ret = xmlXPathParseNCName(ctxt);
6463 if (CUR == ':') {
6464 *prefix = ret;
6465 NEXT;
6466 ret = xmlXPathParseNCName(ctxt);
6467 }
6468 return(ret);
6469}
6470
6471/**
6472 * xmlXPathParseName:
6473 * @ctxt: the XPath Parser context
6474 *
6475 * parse an XML name
6476 *
6477 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
6478 * CombiningChar | Extender
6479 *
6480 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
6481 *
6482 * Returns the namespace name or NULL
6483 */
6484
6485xmlChar *
6486xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006487 const xmlChar *in;
6488 xmlChar *ret;
6489 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006490
Daniel Veillard61d80a22001-04-27 17:13:01 +00006491 /*
6492 * Accelerator for simple ASCII names
6493 */
6494 in = ctxt->cur;
6495 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6496 ((*in >= 0x41) && (*in <= 0x5A)) ||
6497 (*in == '_') || (*in == ':')) {
6498 in++;
6499 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6500 ((*in >= 0x41) && (*in <= 0x5A)) ||
6501 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard76d66f42001-05-16 21:05:17 +00006502 (*in == '_') || (*in == '-') ||
6503 (*in == ':') || (*in == '.'))
Daniel Veillard61d80a22001-04-27 17:13:01 +00006504 in++;
Daniel Veillard76d66f42001-05-16 21:05:17 +00006505 if ((*in > 0) && (*in < 0x80)) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006506 count = in - ctxt->cur;
6507 ret = xmlStrndup(ctxt->cur, count);
6508 ctxt->cur = in;
6509 return(ret);
6510 }
6511 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006512 return(xmlXPathParseNameComplex(ctxt, 1));
Owen Taylor3473f882001-02-23 17:55:21 +00006513}
6514
Daniel Veillard61d80a22001-04-27 17:13:01 +00006515static xmlChar *
Daniel Veillard2156a562001-04-28 12:24:34 +00006516xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006517 xmlChar buf[XML_MAX_NAMELEN + 5];
6518 int len = 0, l;
6519 int c;
6520
6521 /*
6522 * Handler for more complex cases
6523 */
6524 c = CUR_CHAR(l);
6525 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
Daniel Veillard2156a562001-04-28 12:24:34 +00006526 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
6527 (c == '*') || /* accelerators */
Daniel Veillard61d80a22001-04-27 17:13:01 +00006528 (!IS_LETTER(c) && (c != '_') &&
Daniel Veillard2156a562001-04-28 12:24:34 +00006529 ((qualified) && (c != ':')))) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006530 return(NULL);
6531 }
6532
6533 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
6534 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
6535 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006536 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006537 (IS_COMBINING(c)) ||
6538 (IS_EXTENDER(c)))) {
6539 COPY_BUF(l,buf,len,c);
6540 NEXTL(l);
6541 c = CUR_CHAR(l);
6542 if (len >= XML_MAX_NAMELEN) {
6543 /*
6544 * Okay someone managed to make a huge name, so he's ready to pay
6545 * for the processing speed.
6546 */
6547 xmlChar *buffer;
6548 int max = len * 2;
6549
6550 buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
6551 if (buffer == NULL) {
6552 XP_ERROR0(XPATH_MEMORY_ERROR);
6553 }
6554 memcpy(buffer, buf, len);
6555 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
6556 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006557 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006558 (IS_COMBINING(c)) ||
6559 (IS_EXTENDER(c))) {
6560 if (len + 10 > max) {
6561 max *= 2;
6562 buffer = (xmlChar *) xmlRealloc(buffer,
6563 max * sizeof(xmlChar));
Daniel Veillard61d80a22001-04-27 17:13:01 +00006564 if (buffer == NULL) {
6565 XP_ERROR0(XPATH_MEMORY_ERROR);
6566 }
6567 }
6568 COPY_BUF(l,buffer,len,c);
6569 NEXTL(l);
6570 c = CUR_CHAR(l);
6571 }
6572 buffer[len] = 0;
6573 return(buffer);
6574 }
6575 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006576 if (len == 0)
6577 return(NULL);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006578 return(xmlStrndup(buf, len));
6579}
Owen Taylor3473f882001-02-23 17:55:21 +00006580/**
6581 * xmlXPathStringEvalNumber:
6582 * @str: A string to scan
6583 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00006584 * [30a] Float ::= Number ('e' Digits?)?
6585 *
Owen Taylor3473f882001-02-23 17:55:21 +00006586 * [30] Number ::= Digits ('.' Digits?)?
6587 * | '.' Digits
6588 * [31] Digits ::= [0-9]+
6589 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006590 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00006591 * In complement of the Number expression, this function also handles
6592 * negative values : '-' Number.
6593 *
6594 * Returns the double value.
6595 */
6596double
6597xmlXPathStringEvalNumber(const xmlChar *str) {
6598 const xmlChar *cur = str;
6599 double ret = 0.0;
6600 double mult = 1;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006601 int ok = 0, tmp = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006602 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006603 int exponent = 0;
6604 int is_exponent_negative = 0;
6605
Owen Taylor3473f882001-02-23 17:55:21 +00006606 while (IS_BLANK(*cur)) cur++;
6607 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
6608 return(xmlXPathNAN);
6609 }
6610 if (*cur == '-') {
6611 isneg = 1;
6612 cur++;
6613 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006614 /*
6615 * tmp is a workaroudn against a gcc compiler bug
6616 */
Owen Taylor3473f882001-02-23 17:55:21 +00006617 while ((*cur >= '0') && (*cur <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006618 tmp = tmp * 10 + (*cur - '0');
Owen Taylor3473f882001-02-23 17:55:21 +00006619 ok = 1;
6620 cur++;
6621 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006622 ret = (double) tmp;
6623
Owen Taylor3473f882001-02-23 17:55:21 +00006624 if (*cur == '.') {
6625 cur++;
6626 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
6627 return(xmlXPathNAN);
6628 }
6629 while ((*cur >= '0') && (*cur <= '9')) {
6630 mult /= 10;
6631 ret = ret + (*cur - '0') * mult;
6632 cur++;
6633 }
6634 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006635 if ((*cur == 'e') || (*cur == 'E')) {
6636 cur++;
6637 if (*cur == '-') {
6638 is_exponent_negative = 1;
6639 cur++;
6640 }
6641 while ((*cur >= '0') && (*cur <= '9')) {
6642 exponent = exponent * 10 + (*cur - '0');
6643 cur++;
6644 }
6645 }
Owen Taylor3473f882001-02-23 17:55:21 +00006646 while (IS_BLANK(*cur)) cur++;
6647 if (*cur != 0) return(xmlXPathNAN);
6648 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006649 if (is_exponent_negative) exponent = -exponent;
6650 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00006651 return(ret);
6652}
6653
6654/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006655 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00006656 * @ctxt: the XPath Parser context
6657 *
6658 * [30] Number ::= Digits ('.' Digits?)?
6659 * | '.' Digits
6660 * [31] Digits ::= [0-9]+
6661 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006662 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00006663 *
6664 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006665static void
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006666xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
6667{
Owen Taylor3473f882001-02-23 17:55:21 +00006668 double ret = 0.0;
6669 double mult = 1;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006670 int ok = 0, tmp = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006671 int exponent = 0;
6672 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006673
6674 CHECK_ERROR;
6675 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
6676 XP_ERROR(XPATH_NUMBER_ERROR);
6677 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006678 /*
6679 * Try to work around a gcc optimizer bug
6680 */
Owen Taylor3473f882001-02-23 17:55:21 +00006681 while ((CUR >= '0') && (CUR <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006682 tmp = tmp * 10 + (CUR - '0');
6683 ok = 1;
6684 NEXT;
Owen Taylor3473f882001-02-23 17:55:21 +00006685 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006686 ret = (double) tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00006687 if (CUR == '.') {
6688 NEXT;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006689 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
6690 XP_ERROR(XPATH_NUMBER_ERROR);
6691 }
6692 while ((CUR >= '0') && (CUR <= '9')) {
6693 mult /= 10;
6694 ret = ret + (CUR - '0') * mult;
6695 NEXT;
6696 }
Owen Taylor3473f882001-02-23 17:55:21 +00006697 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006698 if ((CUR == 'e') || (CUR == 'E')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006699 NEXT;
6700 if (CUR == '-') {
6701 is_exponent_negative = 1;
6702 NEXT;
6703 }
6704 while ((CUR >= '0') && (CUR <= '9')) {
6705 exponent = exponent * 10 + (CUR - '0');
6706 NEXT;
6707 }
6708 if (is_exponent_negative)
6709 exponent = -exponent;
6710 ret *= pow(10.0, (double) exponent);
Bjorn Reese70a9da52001-04-21 16:57:29 +00006711 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006712 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006713 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006714}
6715
6716/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006717 * xmlXPathParseLiteral:
6718 * @ctxt: the XPath Parser context
6719 *
6720 * Parse a Literal
6721 *
6722 * [29] Literal ::= '"' [^"]* '"'
6723 * | "'" [^']* "'"
6724 *
6725 * Returns the value found or NULL in case of error
6726 */
6727static xmlChar *
6728xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
6729 const xmlChar *q;
6730 xmlChar *ret = NULL;
6731
6732 if (CUR == '"') {
6733 NEXT;
6734 q = CUR_PTR;
6735 while ((IS_CHAR(CUR)) && (CUR != '"'))
6736 NEXT;
6737 if (!IS_CHAR(CUR)) {
6738 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6739 } else {
6740 ret = xmlStrndup(q, CUR_PTR - q);
6741 NEXT;
6742 }
6743 } else if (CUR == '\'') {
6744 NEXT;
6745 q = CUR_PTR;
6746 while ((IS_CHAR(CUR)) && (CUR != '\''))
6747 NEXT;
6748 if (!IS_CHAR(CUR)) {
6749 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6750 } else {
6751 ret = xmlStrndup(q, CUR_PTR - q);
6752 NEXT;
6753 }
6754 } else {
6755 XP_ERROR0(XPATH_START_LITERAL_ERROR);
6756 }
6757 return(ret);
6758}
6759
6760/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006761 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00006762 * @ctxt: the XPath Parser context
6763 *
6764 * Parse a Literal and push it on the stack.
6765 *
6766 * [29] Literal ::= '"' [^"]* '"'
6767 * | "'" [^']* "'"
6768 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006769 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00006770 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006771static void
6772xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006773 const xmlChar *q;
6774 xmlChar *ret = NULL;
6775
6776 if (CUR == '"') {
6777 NEXT;
6778 q = CUR_PTR;
6779 while ((IS_CHAR(CUR)) && (CUR != '"'))
6780 NEXT;
6781 if (!IS_CHAR(CUR)) {
6782 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6783 } else {
6784 ret = xmlStrndup(q, CUR_PTR - q);
6785 NEXT;
6786 }
6787 } else if (CUR == '\'') {
6788 NEXT;
6789 q = CUR_PTR;
6790 while ((IS_CHAR(CUR)) && (CUR != '\''))
6791 NEXT;
6792 if (!IS_CHAR(CUR)) {
6793 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6794 } else {
6795 ret = xmlStrndup(q, CUR_PTR - q);
6796 NEXT;
6797 }
6798 } else {
6799 XP_ERROR(XPATH_START_LITERAL_ERROR);
6800 }
6801 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006802 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
6803 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006804 xmlFree(ret);
6805}
6806
6807/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006808 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00006809 * @ctxt: the XPath Parser context
6810 *
6811 * Parse a VariableReference, evaluate it and push it on the stack.
6812 *
6813 * The variable bindings consist of a mapping from variable names
6814 * to variable values. The value of a variable is an object, which
6815 * of any of the types that are possible for the value of an expression,
6816 * and may also be of additional types not specified here.
6817 *
6818 * Early evaluation is possible since:
6819 * The variable bindings [...] used to evaluate a subexpression are
6820 * always the same as those used to evaluate the containing expression.
6821 *
6822 * [36] VariableReference ::= '$' QName
6823 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006824static void
6825xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006826 xmlChar *name;
6827 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006828
6829 SKIP_BLANKS;
6830 if (CUR != '$') {
6831 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6832 }
6833 NEXT;
6834 name = xmlXPathParseQName(ctxt, &prefix);
6835 if (name == NULL) {
6836 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6837 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006838 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006839 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
6840 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006841 SKIP_BLANKS;
6842}
6843
6844/**
6845 * xmlXPathIsNodeType:
6846 * @ctxt: the XPath Parser context
6847 * @name: a name string
6848 *
6849 * Is the name given a NodeType one.
6850 *
6851 * [38] NodeType ::= 'comment'
6852 * | 'text'
6853 * | 'processing-instruction'
6854 * | 'node'
6855 *
6856 * Returns 1 if true 0 otherwise
6857 */
6858int
6859xmlXPathIsNodeType(const xmlChar *name) {
6860 if (name == NULL)
6861 return(0);
6862
6863 if (xmlStrEqual(name, BAD_CAST "comment"))
6864 return(1);
6865 if (xmlStrEqual(name, BAD_CAST "text"))
6866 return(1);
6867 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6868 return(1);
6869 if (xmlStrEqual(name, BAD_CAST "node"))
6870 return(1);
6871 return(0);
6872}
6873
6874/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006875 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00006876 * @ctxt: the XPath Parser context
6877 *
6878 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
6879 * [17] Argument ::= Expr
6880 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006881 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00006882 * pushed on the stack
6883 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006884static void
6885xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006886 xmlChar *name;
6887 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006888 int nbargs = 0;
6889
6890 name = xmlXPathParseQName(ctxt, &prefix);
6891 if (name == NULL) {
6892 XP_ERROR(XPATH_EXPR_ERROR);
6893 }
6894 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006895#ifdef DEBUG_EXPR
6896 if (prefix == NULL)
6897 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
6898 name);
6899 else
6900 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
6901 prefix, name);
6902#endif
6903
Owen Taylor3473f882001-02-23 17:55:21 +00006904 if (CUR != '(') {
6905 XP_ERROR(XPATH_EXPR_ERROR);
6906 }
6907 NEXT;
6908 SKIP_BLANKS;
6909
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006910 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00006911 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006912 int op1 = ctxt->comp->last;
6913 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006914 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006915 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006916 nbargs++;
6917 if (CUR == ')') break;
6918 if (CUR != ',') {
6919 XP_ERROR(XPATH_EXPR_ERROR);
6920 }
6921 NEXT;
6922 SKIP_BLANKS;
6923 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006924 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
6925 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006926 NEXT;
6927 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006928}
6929
6930/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006931 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006932 * @ctxt: the XPath Parser context
6933 *
6934 * [15] PrimaryExpr ::= VariableReference
6935 * | '(' Expr ')'
6936 * | Literal
6937 * | Number
6938 * | FunctionCall
6939 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006940 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006941 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006942static void
6943xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006944 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006945 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006946 else if (CUR == '(') {
6947 NEXT;
6948 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006949 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006950 if (CUR != ')') {
6951 XP_ERROR(XPATH_EXPR_ERROR);
6952 }
6953 NEXT;
6954 SKIP_BLANKS;
6955 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006956 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006957 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006958 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006959 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006960 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006961 }
6962 SKIP_BLANKS;
6963}
6964
6965/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006966 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006967 * @ctxt: the XPath Parser context
6968 *
6969 * [20] FilterExpr ::= PrimaryExpr
6970 * | FilterExpr Predicate
6971 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006972 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006973 * Square brackets are used to filter expressions in the same way that
6974 * they are used in location paths. It is an error if the expression to
6975 * be filtered does not evaluate to a node-set. The context node list
6976 * used for evaluating the expression in square brackets is the node-set
6977 * to be filtered listed in document order.
6978 */
6979
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006980static void
6981xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
6982 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006983 CHECK_ERROR;
6984 SKIP_BLANKS;
6985
6986 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006987 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00006988 SKIP_BLANKS;
6989 }
6990
6991
6992}
6993
6994/**
6995 * xmlXPathScanName:
6996 * @ctxt: the XPath Parser context
6997 *
6998 * Trickery: parse an XML name but without consuming the input flow
6999 * Needed to avoid insanity in the parser state.
7000 *
7001 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
7002 * CombiningChar | Extender
7003 *
7004 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
7005 *
7006 * [6] Names ::= Name (S Name)*
7007 *
7008 * Returns the Name parsed or NULL
7009 */
7010
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007011static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00007012xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
7013 xmlChar buf[XML_MAX_NAMELEN];
7014 int len = 0;
7015
7016 SKIP_BLANKS;
7017 if (!IS_LETTER(CUR) && (CUR != '_') &&
7018 (CUR != ':')) {
7019 return(NULL);
7020 }
7021
7022 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
7023 (NXT(len) == '.') || (NXT(len) == '-') ||
7024 (NXT(len) == '_') || (NXT(len) == ':') ||
7025 (IS_COMBINING(NXT(len))) ||
7026 (IS_EXTENDER(NXT(len)))) {
7027 buf[len] = NXT(len);
7028 len++;
7029 if (len >= XML_MAX_NAMELEN) {
7030 xmlGenericError(xmlGenericErrorContext,
7031 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
7032 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
7033 (NXT(len) == '.') || (NXT(len) == '-') ||
7034 (NXT(len) == '_') || (NXT(len) == ':') ||
7035 (IS_COMBINING(NXT(len))) ||
7036 (IS_EXTENDER(NXT(len))))
7037 len++;
7038 break;
7039 }
7040 }
7041 return(xmlStrndup(buf, len));
7042}
7043
7044/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007045 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007046 * @ctxt: the XPath Parser context
7047 *
7048 * [19] PathExpr ::= LocationPath
7049 * | FilterExpr
7050 * | FilterExpr '/' RelativeLocationPath
7051 * | FilterExpr '//' RelativeLocationPath
7052 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007053 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007054 * The / operator and // operators combine an arbitrary expression
7055 * and a relative location path. It is an error if the expression
7056 * does not evaluate to a node-set.
7057 * The / operator does composition in the same way as when / is
7058 * used in a location path. As in location paths, // is short for
7059 * /descendant-or-self::node()/.
7060 */
7061
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007062static void
7063xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007064 int lc = 1; /* Should we branch to LocationPath ? */
7065 xmlChar *name = NULL; /* we may have to preparse a name to find out */
7066
7067 SKIP_BLANKS;
7068 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
7069 (CUR == '\'') || (CUR == '"')) {
7070 lc = 0;
7071 } else if (CUR == '*') {
7072 /* relative or absolute location path */
7073 lc = 1;
7074 } else if (CUR == '/') {
7075 /* relative or absolute location path */
7076 lc = 1;
7077 } else if (CUR == '@') {
7078 /* relative abbreviated attribute location path */
7079 lc = 1;
7080 } else if (CUR == '.') {
7081 /* relative abbreviated attribute location path */
7082 lc = 1;
7083 } else {
7084 /*
7085 * Problem is finding if we have a name here whether it's:
7086 * - a nodetype
7087 * - a function call in which case it's followed by '('
7088 * - an axis in which case it's followed by ':'
7089 * - a element name
7090 * We do an a priori analysis here rather than having to
7091 * maintain parsed token content through the recursive function
7092 * calls. This looks uglier but makes the code quite easier to
7093 * read/write/debug.
7094 */
7095 SKIP_BLANKS;
7096 name = xmlXPathScanName(ctxt);
7097 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
7098#ifdef DEBUG_STEP
7099 xmlGenericError(xmlGenericErrorContext,
7100 "PathExpr: Axis\n");
7101#endif
7102 lc = 1;
7103 xmlFree(name);
7104 } else if (name != NULL) {
7105 int len =xmlStrlen(name);
7106 int blank = 0;
7107
7108
7109 while (NXT(len) != 0) {
7110 if (NXT(len) == '/') {
7111 /* element name */
7112#ifdef DEBUG_STEP
7113 xmlGenericError(xmlGenericErrorContext,
7114 "PathExpr: AbbrRelLocation\n");
7115#endif
7116 lc = 1;
7117 break;
7118 } else if (IS_BLANK(NXT(len))) {
7119 /* skip to next */
7120 blank = 1;
7121 } else if (NXT(len) == ':') {
7122#ifdef DEBUG_STEP
7123 xmlGenericError(xmlGenericErrorContext,
7124 "PathExpr: AbbrRelLocation\n");
7125#endif
7126 lc = 1;
7127 break;
7128 } else if ((NXT(len) == '(')) {
7129 /* Note Type or Function */
7130 if (xmlXPathIsNodeType(name)) {
7131#ifdef DEBUG_STEP
7132 xmlGenericError(xmlGenericErrorContext,
7133 "PathExpr: Type search\n");
7134#endif
7135 lc = 1;
7136 } else {
7137#ifdef DEBUG_STEP
7138 xmlGenericError(xmlGenericErrorContext,
7139 "PathExpr: function call\n");
7140#endif
7141 lc = 0;
7142 }
7143 break;
7144 } else if ((NXT(len) == '[')) {
7145 /* element name */
7146#ifdef DEBUG_STEP
7147 xmlGenericError(xmlGenericErrorContext,
7148 "PathExpr: AbbrRelLocation\n");
7149#endif
7150 lc = 1;
7151 break;
7152 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
7153 (NXT(len) == '=')) {
7154 lc = 1;
7155 break;
7156 } else {
7157 lc = 1;
7158 break;
7159 }
7160 len++;
7161 }
7162 if (NXT(len) == 0) {
7163#ifdef DEBUG_STEP
7164 xmlGenericError(xmlGenericErrorContext,
7165 "PathExpr: AbbrRelLocation\n");
7166#endif
7167 /* element name */
7168 lc = 1;
7169 }
7170 xmlFree(name);
7171 } else {
7172 /* make sure all cases are covered explicitely */
7173 XP_ERROR(XPATH_EXPR_ERROR);
7174 }
7175 }
7176
7177 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007178 if (CUR == '/') {
7179 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
7180 } else {
7181 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007182 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007183 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007184 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007185 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007186 CHECK_ERROR;
7187 if ((CUR == '/') && (NXT(1) == '/')) {
7188 SKIP(2);
7189 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007190
7191 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7192 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
7193 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
7194
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007195 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007196 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007197 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007198 }
7199 }
7200 SKIP_BLANKS;
7201}
7202
7203/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007204 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007205 * @ctxt: the XPath Parser context
7206 *
7207 * [18] UnionExpr ::= PathExpr
7208 * | UnionExpr '|' PathExpr
7209 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007210 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007211 */
7212
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007213static void
7214xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
7215 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007216 CHECK_ERROR;
7217 SKIP_BLANKS;
7218 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007219 int op1 = ctxt->comp->last;
7220 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007221
7222 NEXT;
7223 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007224 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007225
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007226 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
7227
Owen Taylor3473f882001-02-23 17:55:21 +00007228 SKIP_BLANKS;
7229 }
Owen Taylor3473f882001-02-23 17:55:21 +00007230}
7231
7232/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007233 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007234 * @ctxt: the XPath Parser context
7235 *
7236 * [27] UnaryExpr ::= UnionExpr
7237 * | '-' UnaryExpr
7238 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007239 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007240 */
7241
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007242static void
7243xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007244 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007245 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007246
7247 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00007248 while (CUR == '-') {
7249 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007250 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007251 NEXT;
7252 SKIP_BLANKS;
7253 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007254
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007255 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007256 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007257 if (found) {
7258 if (minus)
7259 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
7260 else
7261 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007262 }
7263}
7264
7265/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007266 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007267 * @ctxt: the XPath Parser context
7268 *
7269 * [26] MultiplicativeExpr ::= UnaryExpr
7270 * | MultiplicativeExpr MultiplyOperator UnaryExpr
7271 * | MultiplicativeExpr 'div' UnaryExpr
7272 * | MultiplicativeExpr 'mod' UnaryExpr
7273 * [34] MultiplyOperator ::= '*'
7274 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007275 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007276 */
7277
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007278static void
7279xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
7280 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007281 CHECK_ERROR;
7282 SKIP_BLANKS;
7283 while ((CUR == '*') ||
7284 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
7285 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
7286 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007287 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007288
7289 if (CUR == '*') {
7290 op = 0;
7291 NEXT;
7292 } else if (CUR == 'd') {
7293 op = 1;
7294 SKIP(3);
7295 } else if (CUR == 'm') {
7296 op = 2;
7297 SKIP(3);
7298 }
7299 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007300 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007301 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007302 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007303 SKIP_BLANKS;
7304 }
7305}
7306
7307/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007308 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007309 * @ctxt: the XPath Parser context
7310 *
7311 * [25] AdditiveExpr ::= MultiplicativeExpr
7312 * | AdditiveExpr '+' MultiplicativeExpr
7313 * | AdditiveExpr '-' MultiplicativeExpr
7314 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007315 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007316 */
7317
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007318static void
7319xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007320
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007321 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007322 CHECK_ERROR;
7323 SKIP_BLANKS;
7324 while ((CUR == '+') || (CUR == '-')) {
7325 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007326 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007327
7328 if (CUR == '+') plus = 1;
7329 else plus = 0;
7330 NEXT;
7331 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007332 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007333 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007334 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007335 SKIP_BLANKS;
7336 }
7337}
7338
7339/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007340 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007341 * @ctxt: the XPath Parser context
7342 *
7343 * [24] RelationalExpr ::= AdditiveExpr
7344 * | RelationalExpr '<' AdditiveExpr
7345 * | RelationalExpr '>' AdditiveExpr
7346 * | RelationalExpr '<=' AdditiveExpr
7347 * | RelationalExpr '>=' AdditiveExpr
7348 *
7349 * A <= B > C is allowed ? Answer from James, yes with
7350 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
7351 * which is basically what got implemented.
7352 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007353 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00007354 * on the stack
7355 */
7356
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007357static void
7358xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
7359 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007360 CHECK_ERROR;
7361 SKIP_BLANKS;
7362 while ((CUR == '<') ||
7363 (CUR == '>') ||
7364 ((CUR == '<') && (NXT(1) == '=')) ||
7365 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007366 int inf, strict;
7367 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007368
7369 if (CUR == '<') inf = 1;
7370 else inf = 0;
7371 if (NXT(1) == '=') strict = 0;
7372 else strict = 1;
7373 NEXT;
7374 if (!strict) NEXT;
7375 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007376 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007377 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007378 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00007379 SKIP_BLANKS;
7380 }
7381}
7382
7383/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007384 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007385 * @ctxt: the XPath Parser context
7386 *
7387 * [23] EqualityExpr ::= RelationalExpr
7388 * | EqualityExpr '=' RelationalExpr
7389 * | EqualityExpr '!=' RelationalExpr
7390 *
7391 * A != B != C is allowed ? Answer from James, yes with
7392 * (RelationalExpr = RelationalExpr) = RelationalExpr
7393 * (RelationalExpr != RelationalExpr) != RelationalExpr
7394 * which is basically what got implemented.
7395 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007396 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007397 *
7398 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007399static void
7400xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
7401 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007402 CHECK_ERROR;
7403 SKIP_BLANKS;
7404 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007405 int eq;
7406 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007407
7408 if (CUR == '=') eq = 1;
7409 else eq = 0;
7410 NEXT;
7411 if (!eq) NEXT;
7412 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007413 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007414 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007415 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007416 SKIP_BLANKS;
7417 }
7418}
7419
7420/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007421 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007422 * @ctxt: the XPath Parser context
7423 *
7424 * [22] AndExpr ::= EqualityExpr
7425 * | AndExpr 'and' EqualityExpr
7426 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007427 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007428 *
7429 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007430static void
7431xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
7432 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007433 CHECK_ERROR;
7434 SKIP_BLANKS;
7435 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007436 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007437 SKIP(3);
7438 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007439 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007440 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007441 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007442 SKIP_BLANKS;
7443 }
7444}
7445
7446/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007447 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007448 * @ctxt: the XPath Parser context
7449 *
7450 * [14] Expr ::= OrExpr
7451 * [21] OrExpr ::= AndExpr
7452 * | OrExpr 'or' AndExpr
7453 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007454 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00007455 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007456static void
7457xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
7458 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007459 CHECK_ERROR;
7460 SKIP_BLANKS;
7461 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007462 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007463 SKIP(2);
7464 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007465 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007466 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007467 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
7468 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00007469 SKIP_BLANKS;
7470 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007471 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
7472 /* more ops could be optimized too */
7473 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
7474 }
Owen Taylor3473f882001-02-23 17:55:21 +00007475}
7476
7477/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007478 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00007479 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007480 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00007481 *
7482 * [8] Predicate ::= '[' PredicateExpr ']'
7483 * [9] PredicateExpr ::= Expr
7484 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007485 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00007486 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007487static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007488xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007489 int op1 = ctxt->comp->last;
7490
7491 SKIP_BLANKS;
7492 if (CUR != '[') {
7493 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7494 }
7495 NEXT;
7496 SKIP_BLANKS;
7497
7498 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007499 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007500 CHECK_ERROR;
7501
7502 if (CUR != ']') {
7503 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7504 }
7505
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007506 if (filter)
7507 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
7508 else
7509 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007510
7511 NEXT;
7512 SKIP_BLANKS;
7513}
7514
7515/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007516 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00007517 * @ctxt: the XPath Parser context
7518 * @test: pointer to a xmlXPathTestVal
7519 * @type: pointer to a xmlXPathTypeVal
7520 * @prefix: placeholder for a possible name prefix
7521 *
7522 * [7] NodeTest ::= NameTest
7523 * | NodeType '(' ')'
7524 * | 'processing-instruction' '(' Literal ')'
7525 *
7526 * [37] NameTest ::= '*'
7527 * | NCName ':' '*'
7528 * | QName
7529 * [38] NodeType ::= 'comment'
7530 * | 'text'
7531 * | 'processing-instruction'
7532 * | 'node'
7533 *
7534 * Returns the name found and update @test, @type and @prefix appropriately
7535 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007536static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007537xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
7538 xmlXPathTypeVal *type, const xmlChar **prefix,
7539 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00007540 int blanks;
7541
7542 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
7543 STRANGE;
7544 return(NULL);
7545 }
7546 *type = 0;
7547 *test = 0;
7548 *prefix = NULL;
7549 SKIP_BLANKS;
7550
7551 if ((name == NULL) && (CUR == '*')) {
7552 /*
7553 * All elements
7554 */
7555 NEXT;
7556 *test = NODE_TEST_ALL;
7557 return(NULL);
7558 }
7559
7560 if (name == NULL)
7561 name = xmlXPathParseNCName(ctxt);
7562 if (name == NULL) {
7563 XP_ERROR0(XPATH_EXPR_ERROR);
7564 }
7565
7566 blanks = IS_BLANK(CUR);
7567 SKIP_BLANKS;
7568 if (CUR == '(') {
7569 NEXT;
7570 /*
7571 * NodeType or PI search
7572 */
7573 if (xmlStrEqual(name, BAD_CAST "comment"))
7574 *type = NODE_TYPE_COMMENT;
7575 else if (xmlStrEqual(name, BAD_CAST "node"))
7576 *type = NODE_TYPE_NODE;
7577 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
7578 *type = NODE_TYPE_PI;
7579 else if (xmlStrEqual(name, BAD_CAST "text"))
7580 *type = NODE_TYPE_TEXT;
7581 else {
7582 if (name != NULL)
7583 xmlFree(name);
7584 XP_ERROR0(XPATH_EXPR_ERROR);
7585 }
7586
7587 *test = NODE_TEST_TYPE;
7588
7589 SKIP_BLANKS;
7590 if (*type == NODE_TYPE_PI) {
7591 /*
7592 * Specific case: search a PI by name.
7593 */
Owen Taylor3473f882001-02-23 17:55:21 +00007594 if (name != NULL)
7595 xmlFree(name);
Daniel Veillard82e49712001-04-26 14:38:03 +00007596 name = NULL;
7597 if (CUR != ')') {
7598 name = xmlXPathParseLiteral(ctxt);
7599 CHECK_ERROR 0;
7600 SKIP_BLANKS;
7601 }
Owen Taylor3473f882001-02-23 17:55:21 +00007602 }
7603 if (CUR != ')') {
7604 if (name != NULL)
7605 xmlFree(name);
7606 XP_ERROR0(XPATH_UNCLOSED_ERROR);
7607 }
7608 NEXT;
7609 return(name);
7610 }
7611 *test = NODE_TEST_NAME;
7612 if ((!blanks) && (CUR == ':')) {
7613 NEXT;
7614
7615 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007616 * Since currently the parser context don't have a
7617 * namespace list associated:
7618 * The namespace name for this prefix can be computed
7619 * only at evaluation time. The compilation is done
7620 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00007621 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007622#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00007623 *prefix = xmlXPathNsLookup(ctxt->context, name);
7624 if (name != NULL)
7625 xmlFree(name);
7626 if (*prefix == NULL) {
7627 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
7628 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007629#else
7630 *prefix = name;
7631#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007632
7633 if (CUR == '*') {
7634 /*
7635 * All elements
7636 */
7637 NEXT;
7638 *test = NODE_TEST_ALL;
7639 return(NULL);
7640 }
7641
7642 name = xmlXPathParseNCName(ctxt);
7643 if (name == NULL) {
7644 XP_ERROR0(XPATH_EXPR_ERROR);
7645 }
7646 }
7647 return(name);
7648}
7649
7650/**
7651 * xmlXPathIsAxisName:
7652 * @name: a preparsed name token
7653 *
7654 * [6] AxisName ::= 'ancestor'
7655 * | 'ancestor-or-self'
7656 * | 'attribute'
7657 * | 'child'
7658 * | 'descendant'
7659 * | 'descendant-or-self'
7660 * | 'following'
7661 * | 'following-sibling'
7662 * | 'namespace'
7663 * | 'parent'
7664 * | 'preceding'
7665 * | 'preceding-sibling'
7666 * | 'self'
7667 *
7668 * Returns the axis or 0
7669 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007670static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00007671xmlXPathIsAxisName(const xmlChar *name) {
7672 xmlXPathAxisVal ret = 0;
7673 switch (name[0]) {
7674 case 'a':
7675 if (xmlStrEqual(name, BAD_CAST "ancestor"))
7676 ret = AXIS_ANCESTOR;
7677 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
7678 ret = AXIS_ANCESTOR_OR_SELF;
7679 if (xmlStrEqual(name, BAD_CAST "attribute"))
7680 ret = AXIS_ATTRIBUTE;
7681 break;
7682 case 'c':
7683 if (xmlStrEqual(name, BAD_CAST "child"))
7684 ret = AXIS_CHILD;
7685 break;
7686 case 'd':
7687 if (xmlStrEqual(name, BAD_CAST "descendant"))
7688 ret = AXIS_DESCENDANT;
7689 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
7690 ret = AXIS_DESCENDANT_OR_SELF;
7691 break;
7692 case 'f':
7693 if (xmlStrEqual(name, BAD_CAST "following"))
7694 ret = AXIS_FOLLOWING;
7695 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
7696 ret = AXIS_FOLLOWING_SIBLING;
7697 break;
7698 case 'n':
7699 if (xmlStrEqual(name, BAD_CAST "namespace"))
7700 ret = AXIS_NAMESPACE;
7701 break;
7702 case 'p':
7703 if (xmlStrEqual(name, BAD_CAST "parent"))
7704 ret = AXIS_PARENT;
7705 if (xmlStrEqual(name, BAD_CAST "preceding"))
7706 ret = AXIS_PRECEDING;
7707 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
7708 ret = AXIS_PRECEDING_SIBLING;
7709 break;
7710 case 's':
7711 if (xmlStrEqual(name, BAD_CAST "self"))
7712 ret = AXIS_SELF;
7713 break;
7714 }
7715 return(ret);
7716}
7717
7718/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007719 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00007720 * @ctxt: the XPath Parser context
7721 *
7722 * [4] Step ::= AxisSpecifier NodeTest Predicate*
7723 * | AbbreviatedStep
7724 *
7725 * [12] AbbreviatedStep ::= '.' | '..'
7726 *
7727 * [5] AxisSpecifier ::= AxisName '::'
7728 * | AbbreviatedAxisSpecifier
7729 *
7730 * [13] AbbreviatedAxisSpecifier ::= '@'?
7731 *
7732 * Modified for XPtr range support as:
7733 *
7734 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
7735 * | AbbreviatedStep
7736 * | 'range-to' '(' Expr ')' Predicate*
7737 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007738 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00007739 * A location step of . is short for self::node(). This is
7740 * particularly useful in conjunction with //. For example, the
7741 * location path .//para is short for
7742 * self::node()/descendant-or-self::node()/child::para
7743 * and so will select all para descendant elements of the context
7744 * node.
7745 * Similarly, a location step of .. is short for parent::node().
7746 * For example, ../title is short for parent::node()/child::title
7747 * and so will select the title children of the parent of the context
7748 * node.
7749 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007750static void
7751xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007752#ifdef LIBXML_XPTR_ENABLED
7753 int rangeto = 0;
7754 int op2 = -1;
7755#endif
7756
Owen Taylor3473f882001-02-23 17:55:21 +00007757 SKIP_BLANKS;
7758 if ((CUR == '.') && (NXT(1) == '.')) {
7759 SKIP(2);
7760 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007761 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
7762 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007763 } else if (CUR == '.') {
7764 NEXT;
7765 SKIP_BLANKS;
7766 } else {
7767 xmlChar *name = NULL;
7768 const xmlChar *prefix = NULL;
7769 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007770 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007771 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007772 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00007773
7774 /*
7775 * The modification needed for XPointer change to the production
7776 */
7777#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007778 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00007779 name = xmlXPathParseNCName(ctxt);
7780 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007781 op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007782 xmlFree(name);
7783 SKIP_BLANKS;
7784 if (CUR != '(') {
7785 XP_ERROR(XPATH_EXPR_ERROR);
7786 }
7787 NEXT;
7788 SKIP_BLANKS;
7789
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007790 xmlXPathCompileExpr(ctxt);
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007791 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
Owen Taylor3473f882001-02-23 17:55:21 +00007792 CHECK_ERROR;
7793
7794 SKIP_BLANKS;
7795 if (CUR != ')') {
7796 XP_ERROR(XPATH_EXPR_ERROR);
7797 }
7798 NEXT;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007799 rangeto = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007800 goto eval_predicates;
7801 }
7802 }
7803#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00007804 if (CUR == '*') {
7805 axis = AXIS_CHILD;
7806 } else {
7807 if (name == NULL)
7808 name = xmlXPathParseNCName(ctxt);
7809 if (name != NULL) {
7810 axis = xmlXPathIsAxisName(name);
7811 if (axis != 0) {
7812 SKIP_BLANKS;
7813 if ((CUR == ':') && (NXT(1) == ':')) {
7814 SKIP(2);
7815 xmlFree(name);
7816 name = NULL;
7817 } else {
7818 /* an element name can conflict with an axis one :-\ */
7819 axis = AXIS_CHILD;
7820 }
Owen Taylor3473f882001-02-23 17:55:21 +00007821 } else {
Owen Taylor3473f882001-02-23 17:55:21 +00007822 axis = AXIS_CHILD;
7823 }
Daniel Veillard2156a562001-04-28 12:24:34 +00007824 } else if (CUR == '@') {
7825 NEXT;
7826 axis = AXIS_ATTRIBUTE;
Owen Taylor3473f882001-02-23 17:55:21 +00007827 } else {
Daniel Veillard2156a562001-04-28 12:24:34 +00007828 axis = AXIS_CHILD;
Owen Taylor3473f882001-02-23 17:55:21 +00007829 }
Owen Taylor3473f882001-02-23 17:55:21 +00007830 }
7831
7832 CHECK_ERROR;
7833
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007834 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00007835 if (test == 0)
7836 return;
7837
7838#ifdef DEBUG_STEP
7839 xmlGenericError(xmlGenericErrorContext,
7840 "Basis : computing new set\n");
7841#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007842
Owen Taylor3473f882001-02-23 17:55:21 +00007843#ifdef DEBUG_STEP
7844 xmlGenericError(xmlGenericErrorContext, "Basis : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007845 if (ctxt->value == NULL)
7846 xmlGenericError(xmlGenericErrorContext, "no value\n");
7847 else if (ctxt->value->nodesetval == NULL)
7848 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7849 else
7850 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007851#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007852
7853eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007854 op1 = ctxt->comp->last;
7855 ctxt->comp->last = -1;
7856
Owen Taylor3473f882001-02-23 17:55:21 +00007857 SKIP_BLANKS;
7858 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007859 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007860 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007861
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007862#ifdef LIBXML_XPTR_ENABLED
7863 if (rangeto) {
7864 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
7865 } else
7866#endif
7867 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
7868 test, type, (void *)prefix, (void *)name);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007869
Owen Taylor3473f882001-02-23 17:55:21 +00007870 }
7871#ifdef DEBUG_STEP
7872 xmlGenericError(xmlGenericErrorContext, "Step : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007873 if (ctxt->value == NULL)
7874 xmlGenericError(xmlGenericErrorContext, "no value\n");
7875 else if (ctxt->value->nodesetval == NULL)
7876 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7877 else
7878 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
7879 ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007880#endif
7881}
7882
7883/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007884 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007885 * @ctxt: the XPath Parser context
7886 *
7887 * [3] RelativeLocationPath ::= Step
7888 * | RelativeLocationPath '/' Step
7889 * | AbbreviatedRelativeLocationPath
7890 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
7891 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007892 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00007893 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007894static void
Owen Taylor3473f882001-02-23 17:55:21 +00007895#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007896xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007897#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007898xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007899#endif
7900(xmlXPathParserContextPtr ctxt) {
7901 SKIP_BLANKS;
7902 if ((CUR == '/') && (NXT(1) == '/')) {
7903 SKIP(2);
7904 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007905 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7906 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007907 } else if (CUR == '/') {
7908 NEXT;
7909 SKIP_BLANKS;
7910 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007911 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007912 SKIP_BLANKS;
7913 while (CUR == '/') {
7914 if ((CUR == '/') && (NXT(1) == '/')) {
7915 SKIP(2);
7916 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007917 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00007918 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007919 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007920 } else if (CUR == '/') {
7921 NEXT;
7922 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007923 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007924 }
7925 SKIP_BLANKS;
7926 }
7927}
7928
7929/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007930 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007931 * @ctxt: the XPath Parser context
7932 *
7933 * [1] LocationPath ::= RelativeLocationPath
7934 * | AbsoluteLocationPath
7935 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
7936 * | AbbreviatedAbsoluteLocationPath
7937 * [10] AbbreviatedAbsoluteLocationPath ::=
7938 * '//' RelativeLocationPath
7939 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007940 * Compile a location path
7941 *
Owen Taylor3473f882001-02-23 17:55:21 +00007942 * // is short for /descendant-or-self::node()/. For example,
7943 * //para is short for /descendant-or-self::node()/child::para and
7944 * so will select any para element in the document (even a para element
7945 * that is a document element will be selected by //para since the
7946 * document element node is a child of the root node); div//para is
7947 * short for div/descendant-or-self::node()/child::para and so will
7948 * select all para descendants of div children.
7949 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007950static void
7951xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007952 SKIP_BLANKS;
7953 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007954 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007955 } else {
7956 while (CUR == '/') {
7957 if ((CUR == '/') && (NXT(1) == '/')) {
7958 SKIP(2);
7959 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007960 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7961 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007962 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007963 } else if (CUR == '/') {
7964 NEXT;
Daniel Veillard608ad072001-06-14 08:32:28 +00007965 SKIP_BLANKS;
7966 if ((CUR != 0 ) &&
7967 ((IS_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
7968 (CUR == '@') || (CUR == '*')))
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007969 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007970 }
7971 }
7972 }
7973}
7974
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007975/************************************************************************
7976 * *
7977 * XPath precompiled expression evaluation *
7978 * *
7979 ************************************************************************/
7980
Daniel Veillardf06307e2001-07-03 10:35:50 +00007981static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007982xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
7983
7984/**
7985 * xmlXPathNodeCollectAndTest:
7986 * @ctxt: the XPath Parser context
7987 * @op: the XPath precompiled step operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00007988 * @first: pointer to the first element in document order
7989 * @last: pointer to the last element in document order
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007990 *
7991 * This is the function implementing a step: based on the current list
7992 * of nodes, it builds up a new list, looking at all nodes under that
7993 * axis and selecting them it also do the predicate filtering
7994 *
7995 * Pushes the new NodeSet resulting from the search.
Daniel Veillardf06307e2001-07-03 10:35:50 +00007996 *
7997 * Returns the number of node traversed
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007998 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00007999static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008000xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008001 xmlXPathStepOpPtr op,
8002 xmlNodePtr * first, xmlNodePtr * last)
8003{
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008004 xmlXPathAxisVal axis = op->value;
8005 xmlXPathTestVal test = op->value2;
8006 xmlXPathTypeVal type = op->value3;
8007 const xmlChar *prefix = op->value4;
8008 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00008009 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008010
8011#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008012 int n = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008013#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008014 int i, t = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008015 xmlNodeSetPtr ret, list;
8016 xmlXPathTraversalFunction next = NULL;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008017 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008018 xmlNodePtr cur = NULL;
8019 xmlXPathObjectPtr obj;
8020 xmlNodeSetPtr nodelist;
8021 xmlNodePtr tmp;
8022
Daniel Veillardf06307e2001-07-03 10:35:50 +00008023 CHECK_TYPE0(XPATH_NODESET);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008024 obj = valuePop(ctxt);
8025 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00008026 if (prefix != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008027 URI = xmlXPathNsLookup(ctxt->context, prefix);
8028 if (URI == NULL)
8029 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
Daniel Veillarde043ee12001-04-16 14:08:07 +00008030 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008031#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008032 xmlGenericError(xmlGenericErrorContext, "new step : ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008033#endif
8034 switch (axis) {
8035 case AXIS_ANCESTOR:
8036#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008037 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008038#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008039 first = NULL;
8040 next = xmlXPathNextAncestor;
8041 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008042 case AXIS_ANCESTOR_OR_SELF:
8043#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008044 xmlGenericError(xmlGenericErrorContext,
8045 "axis 'ancestors-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008046#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008047 first = NULL;
8048 next = xmlXPathNextAncestorOrSelf;
8049 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008050 case AXIS_ATTRIBUTE:
8051#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008052 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008053#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008054 first = NULL;
8055 last = NULL;
8056 next = xmlXPathNextAttribute;
8057 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008058 case AXIS_CHILD:
8059#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008060 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008061#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008062 last = NULL;
8063 next = xmlXPathNextChild;
8064 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008065 case AXIS_DESCENDANT:
8066#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008067 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008068#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008069 last = NULL;
8070 next = xmlXPathNextDescendant;
8071 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008072 case AXIS_DESCENDANT_OR_SELF:
8073#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008074 xmlGenericError(xmlGenericErrorContext,
8075 "axis 'descendant-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008076#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008077 last = NULL;
8078 next = xmlXPathNextDescendantOrSelf;
8079 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008080 case AXIS_FOLLOWING:
8081#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008082 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008083#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008084 last = NULL;
8085 next = xmlXPathNextFollowing;
8086 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008087 case AXIS_FOLLOWING_SIBLING:
8088#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008089 xmlGenericError(xmlGenericErrorContext,
8090 "axis 'following-siblings' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008091#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008092 last = NULL;
8093 next = xmlXPathNextFollowingSibling;
8094 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008095 case AXIS_NAMESPACE:
8096#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008097 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008098#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008099 first = NULL;
8100 last = NULL;
8101 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8102 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008103 case AXIS_PARENT:
8104#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008105 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008106#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008107 first = NULL;
8108 next = xmlXPathNextParent;
8109 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008110 case AXIS_PRECEDING:
8111#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008112 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008113#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008114 first = NULL;
8115 next = xmlXPathNextPrecedingInternal;
8116 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008117 case AXIS_PRECEDING_SIBLING:
8118#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008119 xmlGenericError(xmlGenericErrorContext,
8120 "axis 'preceding-sibling' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008121#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008122 first = NULL;
8123 next = xmlXPathNextPrecedingSibling;
8124 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008125 case AXIS_SELF:
8126#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008127 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008128#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008129 first = NULL;
8130 last = NULL;
8131 next = xmlXPathNextSelf;
8132 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008133 }
8134 if (next == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00008135 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008136
8137 nodelist = obj->nodesetval;
8138 if (nodelist == NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008139 xmlXPathFreeObject(obj);
8140 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8141 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008142 }
8143 addNode = xmlXPathNodeSetAddUnique;
8144 ret = NULL;
8145#ifdef DEBUG_STEP
8146 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008147 " context contains %d nodes\n", nodelist->nodeNr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008148 switch (test) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008149 case NODE_TEST_NONE:
8150 xmlGenericError(xmlGenericErrorContext,
8151 " searching for none !!!\n");
8152 break;
8153 case NODE_TEST_TYPE:
8154 xmlGenericError(xmlGenericErrorContext,
8155 " searching for type %d\n", type);
8156 break;
8157 case NODE_TEST_PI:
8158 xmlGenericError(xmlGenericErrorContext,
8159 " searching for PI !!!\n");
8160 break;
8161 case NODE_TEST_ALL:
8162 xmlGenericError(xmlGenericErrorContext,
8163 " searching for *\n");
8164 break;
8165 case NODE_TEST_NS:
8166 xmlGenericError(xmlGenericErrorContext,
8167 " searching for namespace %s\n",
8168 prefix);
8169 break;
8170 case NODE_TEST_NAME:
8171 xmlGenericError(xmlGenericErrorContext,
8172 " searching for name %s\n", name);
8173 if (prefix != NULL)
8174 xmlGenericError(xmlGenericErrorContext,
8175 " with namespace %s\n", prefix);
8176 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008177 }
8178 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8179#endif
8180 /*
8181 * 2.3 Node Tests
8182 * - For the attribute axis, the principal node type is attribute.
8183 * - For the namespace axis, the principal node type is namespace.
8184 * - For other axes, the principal node type is element.
8185 *
8186 * A node test * is true for any node of the
8187 * principal node type. For example, child::* willi
8188 * select all element children of the context node
8189 */
8190 tmp = ctxt->context->node;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008191 for (i = 0; i < nodelist->nodeNr; i++) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008192 ctxt->context->node = nodelist->nodeTab[i];
8193
Daniel Veillardf06307e2001-07-03 10:35:50 +00008194 cur = NULL;
8195 list = xmlXPathNodeSetCreate(NULL);
8196 do {
8197 cur = next(ctxt, cur);
8198 if (cur == NULL)
8199 break;
8200 if ((first != NULL) && (*first == cur))
8201 break;
8202 if (((t % 256) == 0) &&
8203 (first != NULL) && (*first != NULL) &&
8204 (xmlXPathCmpNodes(*first, cur) >= 0))
8205 break;
8206 if ((last != NULL) && (*last == cur))
8207 break;
8208 if (((t % 256) == 0) &&
8209 (last != NULL) && (*last != NULL) &&
8210 (xmlXPathCmpNodes(cur, *last) >= 0))
8211 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008212 t++;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008213#ifdef DEBUG_STEP
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008214 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
8215#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008216 switch (test) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008217 case NODE_TEST_NONE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008218 ctxt->context->node = tmp;
8219 STRANGE return(t);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008220 case NODE_TEST_TYPE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008221 if ((cur->type == type) ||
8222 ((type == NODE_TYPE_NODE) &&
8223 ((cur->type == XML_DOCUMENT_NODE) ||
8224 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8225 (cur->type == XML_ELEMENT_NODE) ||
8226 (cur->type == XML_PI_NODE) ||
8227 (cur->type == XML_COMMENT_NODE) ||
8228 (cur->type == XML_CDATA_SECTION_NODE) ||
Daniel Veillard7583a592001-07-08 13:15:55 +00008229 (cur->type == XML_TEXT_NODE))) ||
8230 ((type == NODE_TYPE_TEXT) &&
8231 (cur->type == XML_CDATA_SECTION_NODE))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008232#ifdef DEBUG_STEP
8233 n++;
8234#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008235 addNode(list, cur);
8236 }
8237 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008238 case NODE_TEST_PI:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008239 if (cur->type == XML_PI_NODE) {
8240 if ((name != NULL) &&
8241 (!xmlStrEqual(name, cur->name)))
8242 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008243#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008244 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008245#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008246 addNode(list, cur);
8247 }
8248 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008249 case NODE_TEST_ALL:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008250 if (axis == AXIS_ATTRIBUTE) {
8251 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008252#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008253 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008254#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008255 addNode(list, cur);
8256 }
8257 } else if (axis == AXIS_NAMESPACE) {
8258 if (cur->type == XML_NAMESPACE_DECL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008259#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008260 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008261#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008262 addNode(list, cur);
8263 }
8264 } else {
8265 if (cur->type == XML_ELEMENT_NODE) {
8266 if (prefix == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008267#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008268 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008269#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008270 addNode(list, cur);
8271 } else if ((cur->ns != NULL) &&
8272 (xmlStrEqual(URI, cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008273#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008274 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008275#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008276 addNode(list, cur);
8277 }
8278 }
8279 }
8280 break;
8281 case NODE_TEST_NS:{
8282 TODO;
8283 break;
8284 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008285 case NODE_TEST_NAME:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008286 switch (cur->type) {
8287 case XML_ELEMENT_NODE:
8288 if (xmlStrEqual(name, cur->name)) {
8289 if (prefix == NULL) {
8290 if (cur->ns == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008291#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008292 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008293#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008294 addNode(list, cur);
8295 }
8296 } else {
8297 if ((cur->ns != NULL) &&
8298 (xmlStrEqual(URI,
8299 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 XML_ATTRIBUTE_NODE:{
8309 xmlAttrPtr attr = (xmlAttrPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008310
Daniel Veillardf06307e2001-07-03 10:35:50 +00008311 if (xmlStrEqual(name, attr->name)) {
8312 if (prefix == NULL) {
8313 if ((attr->ns == NULL) ||
8314 (attr->ns->prefix == NULL)) {
8315#ifdef DEBUG_STEP
8316 n++;
8317#endif
8318 addNode(list,
8319 (xmlNodePtr) attr);
8320 }
8321 } else {
8322 if ((attr->ns != NULL) &&
8323 (xmlStrEqual(URI,
8324 attr->ns->
8325 href))) {
8326#ifdef DEBUG_STEP
8327 n++;
8328#endif
8329 addNode(list,
8330 (xmlNodePtr) attr);
8331 }
8332 }
8333 }
8334 break;
8335 }
8336 case XML_NAMESPACE_DECL:
8337 if (cur->type == XML_NAMESPACE_DECL) {
8338 xmlNsPtr ns = (xmlNsPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008339
Daniel Veillardf06307e2001-07-03 10:35:50 +00008340 if ((ns->prefix != NULL) && (name != NULL)
8341 && (xmlStrEqual(ns->prefix, name))) {
8342#ifdef DEBUG_STEP
8343 n++;
8344#endif
8345 addNode(list, cur);
8346 }
8347 }
8348 break;
8349 default:
8350 break;
8351 }
8352 break;
8353 break;
8354 }
8355 } while (cur != NULL);
8356
8357 /*
8358 * If there is some predicate filtering do it now
8359 */
8360 if (op->ch2 != -1) {
8361 xmlXPathObjectPtr obj2;
8362
8363 valuePush(ctxt, xmlXPathWrapNodeSet(list));
8364 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
8365 CHECK_TYPE0(XPATH_NODESET);
8366 obj2 = valuePop(ctxt);
8367 list = obj2->nodesetval;
8368 obj2->nodesetval = NULL;
8369 xmlXPathFreeObject(obj2);
8370 }
8371 if (ret == NULL) {
8372 ret = list;
8373 } else {
8374 ret = xmlXPathNodeSetMerge(ret, list);
8375 xmlXPathFreeNodeSet(list);
8376 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008377 }
8378 ctxt->context->node = tmp;
8379#ifdef DEBUG_STEP
8380 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008381 "\nExamined %d nodes, found %d nodes at that step\n",
8382 t, n);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008383#endif
8384 xmlXPathFreeObject(obj);
8385 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Daniel Veillardf06307e2001-07-03 10:35:50 +00008386 return(t);
8387}
8388
8389/**
8390 * xmlXPathNodeCollectAndTestNth:
8391 * @ctxt: the XPath Parser context
8392 * @op: the XPath precompiled step operation
8393 * @indx: the index to collect
8394 * @first: pointer to the first element in document order
8395 * @last: pointer to the last element in document order
8396 *
8397 * This is the function implementing a step: based on the current list
8398 * of nodes, it builds up a new list, looking at all nodes under that
8399 * axis and selecting them it also do the predicate filtering
8400 *
8401 * Pushes the new NodeSet resulting from the search.
8402 * Returns the number of node traversed
8403 */
8404static int
8405xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
8406 xmlXPathStepOpPtr op, int indx,
8407 xmlNodePtr * first, xmlNodePtr * last)
8408{
8409 xmlXPathAxisVal axis = op->value;
8410 xmlXPathTestVal test = op->value2;
8411 xmlXPathTypeVal type = op->value3;
8412 const xmlChar *prefix = op->value4;
8413 const xmlChar *name = op->value5;
8414 const xmlChar *URI = NULL;
8415 int n = 0, t = 0;
8416
8417 int i;
8418 xmlNodeSetPtr list;
8419 xmlXPathTraversalFunction next = NULL;
8420 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
8421 xmlNodePtr cur = NULL;
8422 xmlXPathObjectPtr obj;
8423 xmlNodeSetPtr nodelist;
8424 xmlNodePtr tmp;
8425
8426 CHECK_TYPE0(XPATH_NODESET);
8427 obj = valuePop(ctxt);
8428 addNode = xmlXPathNodeSetAdd;
8429 if (prefix != NULL) {
8430 URI = xmlXPathNsLookup(ctxt->context, prefix);
8431 if (URI == NULL)
8432 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
8433 }
8434#ifdef DEBUG_STEP_NTH
8435 xmlGenericError(xmlGenericErrorContext, "new step : ");
8436 if (first != NULL) {
8437 if (*first != NULL)
8438 xmlGenericError(xmlGenericErrorContext, "first = %s ",
8439 (*first)->name);
8440 else
8441 xmlGenericError(xmlGenericErrorContext, "first = NULL ");
8442 }
8443 if (last != NULL) {
8444 if (*last != NULL)
8445 xmlGenericError(xmlGenericErrorContext, "last = %s ",
8446 (*last)->name);
8447 else
8448 xmlGenericError(xmlGenericErrorContext, "last = NULL ");
8449 }
8450#endif
8451 switch (axis) {
8452 case AXIS_ANCESTOR:
8453#ifdef DEBUG_STEP_NTH
8454 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
8455#endif
8456 first = NULL;
8457 next = xmlXPathNextAncestor;
8458 break;
8459 case AXIS_ANCESTOR_OR_SELF:
8460#ifdef DEBUG_STEP_NTH
8461 xmlGenericError(xmlGenericErrorContext,
8462 "axis 'ancestors-or-self' ");
8463#endif
8464 first = NULL;
8465 next = xmlXPathNextAncestorOrSelf;
8466 break;
8467 case AXIS_ATTRIBUTE:
8468#ifdef DEBUG_STEP_NTH
8469 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
8470#endif
8471 first = NULL;
8472 last = NULL;
8473 next = xmlXPathNextAttribute;
8474 break;
8475 case AXIS_CHILD:
8476#ifdef DEBUG_STEP_NTH
8477 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
8478#endif
8479 last = NULL;
8480 next = xmlXPathNextChild;
8481 break;
8482 case AXIS_DESCENDANT:
8483#ifdef DEBUG_STEP_NTH
8484 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
8485#endif
8486 last = NULL;
8487 next = xmlXPathNextDescendant;
8488 break;
8489 case AXIS_DESCENDANT_OR_SELF:
8490#ifdef DEBUG_STEP_NTH
8491 xmlGenericError(xmlGenericErrorContext,
8492 "axis 'descendant-or-self' ");
8493#endif
8494 last = NULL;
8495 next = xmlXPathNextDescendantOrSelf;
8496 break;
8497 case AXIS_FOLLOWING:
8498#ifdef DEBUG_STEP_NTH
8499 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
8500#endif
8501 last = NULL;
8502 next = xmlXPathNextFollowing;
8503 break;
8504 case AXIS_FOLLOWING_SIBLING:
8505#ifdef DEBUG_STEP_NTH
8506 xmlGenericError(xmlGenericErrorContext,
8507 "axis 'following-siblings' ");
8508#endif
8509 last = NULL;
8510 next = xmlXPathNextFollowingSibling;
8511 break;
8512 case AXIS_NAMESPACE:
8513#ifdef DEBUG_STEP_NTH
8514 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
8515#endif
8516 last = NULL;
8517 first = NULL;
8518 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8519 break;
8520 case AXIS_PARENT:
8521#ifdef DEBUG_STEP_NTH
8522 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
8523#endif
8524 first = NULL;
8525 next = xmlXPathNextParent;
8526 break;
8527 case AXIS_PRECEDING:
8528#ifdef DEBUG_STEP_NTH
8529 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
8530#endif
8531 first = NULL;
8532 next = xmlXPathNextPrecedingInternal;
8533 break;
8534 case AXIS_PRECEDING_SIBLING:
8535#ifdef DEBUG_STEP_NTH
8536 xmlGenericError(xmlGenericErrorContext,
8537 "axis 'preceding-sibling' ");
8538#endif
8539 first = NULL;
8540 next = xmlXPathNextPrecedingSibling;
8541 break;
8542 case AXIS_SELF:
8543#ifdef DEBUG_STEP_NTH
8544 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
8545#endif
8546 first = NULL;
8547 last = NULL;
8548 next = xmlXPathNextSelf;
8549 break;
8550 }
8551 if (next == NULL)
8552 return(0);
8553
8554 nodelist = obj->nodesetval;
8555 if (nodelist == NULL) {
8556 xmlXPathFreeObject(obj);
8557 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8558 return(0);
8559 }
8560 addNode = xmlXPathNodeSetAddUnique;
8561#ifdef DEBUG_STEP_NTH
8562 xmlGenericError(xmlGenericErrorContext,
8563 " context contains %d nodes\n", nodelist->nodeNr);
8564 switch (test) {
8565 case NODE_TEST_NONE:
8566 xmlGenericError(xmlGenericErrorContext,
8567 " searching for none !!!\n");
8568 break;
8569 case NODE_TEST_TYPE:
8570 xmlGenericError(xmlGenericErrorContext,
8571 " searching for type %d\n", type);
8572 break;
8573 case NODE_TEST_PI:
8574 xmlGenericError(xmlGenericErrorContext,
8575 " searching for PI !!!\n");
8576 break;
8577 case NODE_TEST_ALL:
8578 xmlGenericError(xmlGenericErrorContext,
8579 " searching for *\n");
8580 break;
8581 case NODE_TEST_NS:
8582 xmlGenericError(xmlGenericErrorContext,
8583 " searching for namespace %s\n",
8584 prefix);
8585 break;
8586 case NODE_TEST_NAME:
8587 xmlGenericError(xmlGenericErrorContext,
8588 " searching for name %s\n", name);
8589 if (prefix != NULL)
8590 xmlGenericError(xmlGenericErrorContext,
8591 " with namespace %s\n", prefix);
8592 break;
8593 }
8594 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8595#endif
8596 /*
8597 * 2.3 Node Tests
8598 * - For the attribute axis, the principal node type is attribute.
8599 * - For the namespace axis, the principal node type is namespace.
8600 * - For other axes, the principal node type is element.
8601 *
8602 * A node test * is true for any node of the
8603 * principal node type. For example, child::* willi
8604 * select all element children of the context node
8605 */
8606 tmp = ctxt->context->node;
8607 list = xmlXPathNodeSetCreate(NULL);
8608 for (i = 0; i < nodelist->nodeNr; i++) {
8609 ctxt->context->node = nodelist->nodeTab[i];
8610
8611 cur = NULL;
8612 n = 0;
8613 do {
8614 cur = next(ctxt, cur);
8615 if (cur == NULL)
8616 break;
8617 if ((first != NULL) && (*first == cur))
8618 break;
8619 if (((t % 256) == 0) &&
8620 (first != NULL) && (*first != NULL) &&
8621 (xmlXPathCmpNodes(*first, cur) >= 0))
8622 break;
8623 if ((last != NULL) && (*last == cur))
8624 break;
8625 if (((t % 256) == 0) &&
8626 (last != NULL) && (*last != NULL) &&
8627 (xmlXPathCmpNodes(cur, *last) >= 0))
8628 break;
8629 t++;
8630 switch (test) {
8631 case NODE_TEST_NONE:
8632 ctxt->context->node = tmp;
8633 STRANGE return(0);
8634 case NODE_TEST_TYPE:
8635 if ((cur->type == type) ||
8636 ((type == NODE_TYPE_NODE) &&
8637 ((cur->type == XML_DOCUMENT_NODE) ||
8638 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8639 (cur->type == XML_ELEMENT_NODE) ||
8640 (cur->type == XML_PI_NODE) ||
8641 (cur->type == XML_COMMENT_NODE) ||
8642 (cur->type == XML_CDATA_SECTION_NODE) ||
8643 (cur->type == XML_TEXT_NODE)))) {
8644 n++;
8645 if (n == indx)
8646 addNode(list, cur);
8647 }
8648 break;
8649 case NODE_TEST_PI:
8650 if (cur->type == XML_PI_NODE) {
8651 if ((name != NULL) &&
8652 (!xmlStrEqual(name, cur->name)))
8653 break;
8654 n++;
8655 if (n == indx)
8656 addNode(list, cur);
8657 }
8658 break;
8659 case NODE_TEST_ALL:
8660 if (axis == AXIS_ATTRIBUTE) {
8661 if (cur->type == XML_ATTRIBUTE_NODE) {
8662 n++;
8663 if (n == indx)
8664 addNode(list, cur);
8665 }
8666 } else if (axis == AXIS_NAMESPACE) {
8667 if (cur->type == XML_NAMESPACE_DECL) {
8668 n++;
8669 if (n == indx)
8670 addNode(list, cur);
8671 }
8672 } else {
8673 if (cur->type == XML_ELEMENT_NODE) {
8674 if (prefix == NULL) {
8675 n++;
8676 if (n == indx)
8677 addNode(list, cur);
8678 } else if ((cur->ns != NULL) &&
8679 (xmlStrEqual(URI, cur->ns->href))) {
8680 n++;
8681 if (n == indx)
8682 addNode(list, cur);
8683 }
8684 }
8685 }
8686 break;
8687 case NODE_TEST_NS:{
8688 TODO;
8689 break;
8690 }
8691 case NODE_TEST_NAME:
8692 switch (cur->type) {
8693 case XML_ELEMENT_NODE:
8694 if (xmlStrEqual(name, cur->name)) {
8695 if (prefix == NULL) {
8696 if (cur->ns == NULL) {
8697 n++;
8698 if (n == indx)
8699 addNode(list, cur);
8700 }
8701 } else {
8702 if ((cur->ns != NULL) &&
8703 (xmlStrEqual(URI,
8704 cur->ns->href))) {
8705 n++;
8706 if (n == indx)
8707 addNode(list, cur);
8708 }
8709 }
8710 }
8711 break;
8712 case XML_ATTRIBUTE_NODE:{
8713 xmlAttrPtr attr = (xmlAttrPtr) cur;
8714
8715 if (xmlStrEqual(name, attr->name)) {
8716 if (prefix == NULL) {
8717 if ((attr->ns == NULL) ||
8718 (attr->ns->prefix == NULL)) {
8719 n++;
8720 if (n == indx)
8721 addNode(list, cur);
8722 }
8723 } else {
8724 if ((attr->ns != NULL) &&
8725 (xmlStrEqual(URI,
8726 attr->ns->
8727 href))) {
8728 n++;
8729 if (n == indx)
8730 addNode(list, cur);
8731 }
8732 }
8733 }
8734 break;
8735 }
8736 case XML_NAMESPACE_DECL:
8737 if (cur->type == XML_NAMESPACE_DECL) {
8738 xmlNsPtr ns = (xmlNsPtr) cur;
8739
8740 if ((ns->prefix != NULL) && (name != NULL)
8741 && (xmlStrEqual(ns->prefix, name))) {
8742 n++;
8743 if (n == indx)
8744 addNode(list, cur);
8745 }
8746 }
8747 break;
8748 default:
8749 break;
8750 }
8751 break;
8752 break;
8753 }
8754 } while (n < indx);
8755 }
8756 ctxt->context->node = tmp;
8757#ifdef DEBUG_STEP_NTH
8758 xmlGenericError(xmlGenericErrorContext,
8759 "\nExamined %d nodes, found %d nodes at that step\n",
8760 t, list->nodeNr);
8761#endif
8762 xmlXPathFreeObject(obj);
8763 valuePush(ctxt, xmlXPathWrapNodeSet(list));
8764 return(t);
8765}
8766
8767/**
8768 * xmlXPathCompOpEvalFirst:
8769 * @ctxt: the XPath parser context with the compiled expression
8770 * @op: an XPath compiled operation
8771 * @first: the first elem found so far
8772 *
8773 * Evaluate the Precompiled XPath operation searching only the first
8774 * element in document order
8775 *
8776 * Returns the number of examined objects.
8777 */
8778static int
8779xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
8780 xmlXPathStepOpPtr op, xmlNodePtr * first)
8781{
8782 int total = 0, cur;
8783 xmlXPathCompExprPtr comp;
8784 xmlXPathObjectPtr arg1, arg2;
8785
8786 comp = ctxt->comp;
8787 switch (op->op) {
8788 case XPATH_OP_END:
8789 return (0);
8790 case XPATH_OP_UNION:
8791 total =
8792 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8793 first);
8794 if ((ctxt->value != NULL)
8795 && (ctxt->value->type == XPATH_NODESET)
8796 && (ctxt->value->nodesetval != NULL)
8797 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8798 /*
8799 * limit tree traversing to first node in the result
8800 */
8801 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8802 *first = ctxt->value->nodesetval->nodeTab[0];
8803 }
8804 cur =
8805 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
8806 first);
8807 CHECK_TYPE0(XPATH_NODESET);
8808 arg2 = valuePop(ctxt);
8809
8810 CHECK_TYPE0(XPATH_NODESET);
8811 arg1 = valuePop(ctxt);
8812
8813 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8814 arg2->nodesetval);
8815 valuePush(ctxt, arg1);
8816 xmlXPathFreeObject(arg2);
8817 /* optimizer */
8818 if (total > cur)
8819 xmlXPathCompSwap(op);
8820 return (total + cur);
8821 case XPATH_OP_ROOT:
8822 xmlXPathRoot(ctxt);
8823 return (0);
8824 case XPATH_OP_NODE:
8825 if (op->ch1 != -1)
8826 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8827 if (op->ch2 != -1)
8828 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8829 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8830 return (total);
8831 case XPATH_OP_RESET:
8832 if (op->ch1 != -1)
8833 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8834 if (op->ch2 != -1)
8835 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8836 ctxt->context->node = NULL;
8837 return (total);
8838 case XPATH_OP_COLLECT:{
8839 if (op->ch1 == -1)
8840 return (total);
8841
8842 total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8843
8844 /*
8845 * Optimization for [n] selection where n is a number
8846 */
8847 if ((op->ch2 != -1) &&
8848 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8849 (comp->steps[op->ch2].ch1 == -1) &&
8850 (comp->steps[op->ch2].ch2 != -1) &&
8851 (comp->steps[comp->steps[op->ch2].ch2].op ==
8852 XPATH_OP_VALUE)) {
8853 xmlXPathObjectPtr val;
8854
8855 val = comp->steps[comp->steps[op->ch2].ch2].value4;
8856 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
8857 int indx = (int) val->floatval;
8858
8859 if (val->floatval == (float) indx) {
8860 xmlXPathNodeCollectAndTestNth(ctxt, op, indx,
8861 first, NULL);
8862 return (total);
8863 }
8864 }
8865 }
8866 total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL);
8867 return (total);
8868 }
8869 case XPATH_OP_VALUE:
8870 valuePush(ctxt,
8871 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
8872 return (0);
8873 case XPATH_OP_SORT:
8874 if (op->ch1 != -1)
8875 total +=
8876 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8877 first);
8878 if ((ctxt->value != NULL)
8879 && (ctxt->value->type == XPATH_NODESET)
8880 && (ctxt->value->nodesetval != NULL))
8881 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8882 return (total);
8883 default:
8884 return (xmlXPathCompOpEval(ctxt, op));
8885 }
8886}
8887
8888/**
8889 * xmlXPathCompOpEvalLast:
8890 * @ctxt: the XPath parser context with the compiled expression
8891 * @op: an XPath compiled operation
8892 * @last: the last elem found so far
8893 *
8894 * Evaluate the Precompiled XPath operation searching only the last
8895 * element in document order
8896 *
8897 * Returns the number of node traversed
8898 */
8899static int
8900xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
8901 xmlNodePtr * last)
8902{
8903 int total = 0, cur;
8904 xmlXPathCompExprPtr comp;
8905 xmlXPathObjectPtr arg1, arg2;
8906
8907 comp = ctxt->comp;
8908 switch (op->op) {
8909 case XPATH_OP_END:
8910 return (0);
8911 case XPATH_OP_UNION:
8912 total =
8913 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
8914 if ((ctxt->value != NULL)
8915 && (ctxt->value->type == XPATH_NODESET)
8916 && (ctxt->value->nodesetval != NULL)
8917 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8918 /*
8919 * limit tree traversing to first node in the result
8920 */
8921 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8922 *last =
8923 ctxt->value->nodesetval->nodeTab[ctxt->value->
8924 nodesetval->nodeNr -
8925 1];
8926 }
8927 cur =
8928 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
8929 if ((ctxt->value != NULL)
8930 && (ctxt->value->type == XPATH_NODESET)
8931 && (ctxt->value->nodesetval != NULL)
8932 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8933 }
8934 CHECK_TYPE0(XPATH_NODESET);
8935 arg2 = valuePop(ctxt);
8936
8937 CHECK_TYPE0(XPATH_NODESET);
8938 arg1 = valuePop(ctxt);
8939
8940 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8941 arg2->nodesetval);
8942 valuePush(ctxt, arg1);
8943 xmlXPathFreeObject(arg2);
8944 /* optimizer */
8945 if (total > cur)
8946 xmlXPathCompSwap(op);
8947 return (total + cur);
8948 case XPATH_OP_ROOT:
8949 xmlXPathRoot(ctxt);
8950 return (0);
8951 case XPATH_OP_NODE:
8952 if (op->ch1 != -1)
8953 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8954 if (op->ch2 != -1)
8955 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8956 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8957 return (total);
8958 case XPATH_OP_RESET:
8959 if (op->ch1 != -1)
8960 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8961 if (op->ch2 != -1)
8962 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8963 ctxt->context->node = NULL;
8964 return (total);
8965 case XPATH_OP_COLLECT:{
8966 if (op->ch1 == -1)
8967 return (0);
8968
8969 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8970
8971 /*
8972 * Optimization for [n] selection where n is a number
8973 */
8974 if ((op->ch2 != -1) &&
8975 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8976 (comp->steps[op->ch2].ch1 == -1) &&
8977 (comp->steps[op->ch2].ch2 != -1) &&
8978 (comp->steps[comp->steps[op->ch2].ch2].op ==
8979 XPATH_OP_VALUE)) {
8980 xmlXPathObjectPtr val;
8981
8982 val = comp->steps[comp->steps[op->ch2].ch2].value4;
8983 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
8984 int indx = (int) val->floatval;
8985
8986 if (val->floatval == (float) indx) {
8987 total +=
8988 xmlXPathNodeCollectAndTestNth(ctxt, op,
8989 indx, NULL,
8990 last);
8991 return (total);
8992 }
8993 }
8994 }
8995 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last);
8996 return (total);
8997 }
8998 case XPATH_OP_VALUE:
8999 valuePush(ctxt,
9000 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
9001 return (0);
9002 case XPATH_OP_SORT:
9003 if (op->ch1 != -1)
9004 total +=
9005 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
9006 last);
9007 if ((ctxt->value != NULL)
9008 && (ctxt->value->type == XPATH_NODESET)
9009 && (ctxt->value->nodesetval != NULL))
9010 xmlXPathNodeSetSort(ctxt->value->nodesetval);
9011 return (total);
9012 default:
9013 return (xmlXPathCompOpEval(ctxt, op));
9014 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009015}
9016
Owen Taylor3473f882001-02-23 17:55:21 +00009017/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009018 * xmlXPathCompOpEval:
9019 * @ctxt: the XPath parser context with the compiled expression
9020 * @op: an XPath compiled operation
9021 *
9022 * Evaluate the Precompiled XPath operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00009023 * Returns the number of node traversed
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009024 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00009025static int
9026xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
9027{
9028 int total = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009029 int equal, ret;
9030 xmlXPathCompExprPtr comp;
9031 xmlXPathObjectPtr arg1, arg2;
9032
9033 comp = ctxt->comp;
9034 switch (op->op) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00009035 case XPATH_OP_END:
9036 return (0);
9037 case XPATH_OP_AND:
9038 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9039 xmlXPathBooleanFunction(ctxt, 1);
9040 if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
9041 return (total);
9042 arg2 = valuePop(ctxt);
9043 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9044 xmlXPathBooleanFunction(ctxt, 1);
9045 arg1 = valuePop(ctxt);
9046 arg1->boolval &= arg2->boolval;
9047 valuePush(ctxt, arg1);
9048 xmlXPathFreeObject(arg2);
9049 return (total);
9050 case XPATH_OP_OR:
9051 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9052 xmlXPathBooleanFunction(ctxt, 1);
9053 if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
9054 return (total);
9055 arg2 = valuePop(ctxt);
9056 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9057 xmlXPathBooleanFunction(ctxt, 1);
9058 arg1 = valuePop(ctxt);
9059 arg1->boolval |= arg2->boolval;
9060 valuePush(ctxt, arg1);
9061 xmlXPathFreeObject(arg2);
9062 return (total);
9063 case XPATH_OP_EQUAL:
9064 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9065 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9066 equal = xmlXPathEqualValues(ctxt);
9067 if (op->value)
9068 valuePush(ctxt, xmlXPathNewBoolean(equal));
9069 else
9070 valuePush(ctxt, xmlXPathNewBoolean(!equal));
9071 return (total);
9072 case XPATH_OP_CMP:
9073 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9074 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9075 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
9076 valuePush(ctxt, xmlXPathNewBoolean(ret));
9077 return (total);
9078 case XPATH_OP_PLUS:
9079 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9080 if (op->ch2 != -1)
9081 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9082 if (op->value == 0)
9083 xmlXPathSubValues(ctxt);
9084 else if (op->value == 1)
9085 xmlXPathAddValues(ctxt);
9086 else if (op->value == 2)
9087 xmlXPathValueFlipSign(ctxt);
9088 else if (op->value == 3) {
9089 CAST_TO_NUMBER;
9090 CHECK_TYPE0(XPATH_NUMBER);
9091 }
9092 return (total);
9093 case XPATH_OP_MULT:
9094 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9095 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9096 if (op->value == 0)
9097 xmlXPathMultValues(ctxt);
9098 else if (op->value == 1)
9099 xmlXPathDivValues(ctxt);
9100 else if (op->value == 2)
9101 xmlXPathModValues(ctxt);
9102 return (total);
9103 case XPATH_OP_UNION:
9104 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9105 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9106 CHECK_TYPE0(XPATH_NODESET);
9107 arg2 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009108
Daniel Veillardf06307e2001-07-03 10:35:50 +00009109 CHECK_TYPE0(XPATH_NODESET);
9110 arg1 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009111
Daniel Veillardf06307e2001-07-03 10:35:50 +00009112 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
9113 arg2->nodesetval);
9114 valuePush(ctxt, arg1);
9115 xmlXPathFreeObject(arg2);
9116 return (total);
9117 case XPATH_OP_ROOT:
9118 xmlXPathRoot(ctxt);
9119 return (total);
9120 case XPATH_OP_NODE:
9121 if (op->ch1 != -1)
9122 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9123 if (op->ch2 != -1)
9124 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9125 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
9126 return (total);
9127 case XPATH_OP_RESET:
9128 if (op->ch1 != -1)
9129 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9130 if (op->ch2 != -1)
9131 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9132 ctxt->context->node = NULL;
9133 return (total);
9134 case XPATH_OP_COLLECT:{
9135 if (op->ch1 == -1)
9136 return (total);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009137
Daniel Veillardf06307e2001-07-03 10:35:50 +00009138 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009139
Daniel Veillardf06307e2001-07-03 10:35:50 +00009140 /*
9141 * Optimization for [n] selection where n is a number
9142 */
9143 if ((op->ch2 != -1) &&
9144 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
9145 (comp->steps[op->ch2].ch1 == -1) &&
9146 (comp->steps[op->ch2].ch2 != -1) &&
9147 (comp->steps[comp->steps[op->ch2].ch2].op ==
9148 XPATH_OP_VALUE)) {
9149 xmlXPathObjectPtr val;
Daniel Veillard42596ad2001-05-22 16:57:14 +00009150
Daniel Veillardf06307e2001-07-03 10:35:50 +00009151 val = comp->steps[comp->steps[op->ch2].ch2].value4;
9152 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
9153 int indx = (int) val->floatval;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009154
Daniel Veillardf06307e2001-07-03 10:35:50 +00009155 if (val->floatval == (float) indx) {
9156 total +=
9157 xmlXPathNodeCollectAndTestNth(ctxt, op,
9158 indx, NULL,
9159 NULL);
9160 return (total);
9161 }
9162 }
9163 }
9164 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL);
9165 return (total);
9166 }
9167 case XPATH_OP_VALUE:
9168 valuePush(ctxt,
9169 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
9170 return (total);
9171 case XPATH_OP_VARIABLE:{
9172 if (op->ch1 != -1)
9173 total +=
9174 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9175 if (op->value5 == NULL)
9176 valuePush(ctxt,
9177 xmlXPathVariableLookup(ctxt->context,
9178 op->value4));
9179 else {
9180 const xmlChar *URI;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009181
Daniel Veillardf06307e2001-07-03 10:35:50 +00009182 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9183 if (URI == NULL) {
9184 xmlGenericError(xmlGenericErrorContext,
9185 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
9186 op->value4, op->value5);
9187 return (total);
9188 }
9189 valuePush(ctxt,
9190 xmlXPathVariableLookupNS(ctxt->context,
9191 op->value4, URI));
9192 }
9193 return (total);
9194 }
9195 case XPATH_OP_FUNCTION:{
9196 xmlXPathFunction func;
9197 const xmlChar *oldFunc, *oldFuncURI;
9198
9199 if (op->ch1 != -1)
9200 total +=
9201 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9202 if (op->cache != NULL)
9203 func = (xmlXPathFunction) op->cache;
9204 else {
9205 const xmlChar *URI = NULL;
9206
9207 if (op->value5 == NULL)
9208 func =
9209 xmlXPathFunctionLookup(ctxt->context,
9210 op->value4);
9211 else {
9212 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9213 if (URI == NULL) {
9214 xmlGenericError(xmlGenericErrorContext,
9215 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
9216 op->value4, op->value5);
9217 return (total);
9218 }
9219 func = xmlXPathFunctionLookupNS(ctxt->context,
9220 op->value4, URI);
9221 }
9222 if (func == NULL) {
9223 xmlGenericError(xmlGenericErrorContext,
9224 "xmlXPathRunEval: function %s not found\n",
9225 op->value4);
9226 XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
9227 return (total);
9228 }
9229 op->cache = (void *) func;
9230 op->cacheURI = (void *) URI;
9231 }
9232 oldFunc = ctxt->context->function;
9233 oldFuncURI = ctxt->context->functionURI;
9234 ctxt->context->function = op->value4;
9235 ctxt->context->functionURI = op->cacheURI;
9236 func(ctxt, op->value);
9237 ctxt->context->function = oldFunc;
9238 ctxt->context->functionURI = oldFuncURI;
9239 return (total);
9240 }
9241 case XPATH_OP_ARG:
9242 if (op->ch1 != -1)
9243 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9244 if (op->ch2 != -1)
9245 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9246 return (total);
9247 case XPATH_OP_PREDICATE:
9248 case XPATH_OP_FILTER:{
9249 xmlXPathObjectPtr res;
9250 xmlXPathObjectPtr obj, tmp;
9251 xmlNodeSetPtr newset = NULL;
9252 xmlNodeSetPtr oldset;
9253 xmlNodePtr oldnode;
9254 int i;
9255
9256 /*
9257 * Optimization for ()[1] selection i.e. the first elem
9258 */
9259 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9260 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9261 (comp->steps[op->ch2].op == XPATH_OP_VALUE)) {
9262 xmlXPathObjectPtr val;
9263
9264 val = comp->steps[op->ch2].value4;
9265 if ((val != NULL) && (val->type == XPATH_NUMBER) &&
9266 (val->floatval == 1.0)) {
9267 xmlNodePtr first = NULL;
9268
9269 total +=
9270 xmlXPathCompOpEvalFirst(ctxt,
9271 &comp->steps[op->ch1],
9272 &first);
9273 /*
9274 * The nodeset should be in document order,
9275 * Keep only the first value
9276 */
9277 if ((ctxt->value != NULL) &&
9278 (ctxt->value->type == XPATH_NODESET) &&
9279 (ctxt->value->nodesetval != NULL) &&
9280 (ctxt->value->nodesetval->nodeNr > 1))
9281 ctxt->value->nodesetval->nodeNr = 1;
9282 return (total);
9283 }
9284 }
9285 /*
9286 * Optimization for ()[last()] selection i.e. the last elem
9287 */
9288 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9289 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9290 (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
9291 int f = comp->steps[op->ch2].ch1;
9292
9293 if ((f != -1) &&
9294 (comp->steps[f].op == XPATH_OP_FUNCTION) &&
9295 (comp->steps[f].value5 == NULL) &&
9296 (comp->steps[f].value == 0) &&
9297 (comp->steps[f].value4 != NULL) &&
9298 (xmlStrEqual
9299 (comp->steps[f].value4, BAD_CAST "last"))) {
9300 xmlNodePtr last = NULL;
9301
9302 total +=
9303 xmlXPathCompOpEvalLast(ctxt,
9304 &comp->steps[op->ch1],
9305 &last);
9306 /*
9307 * The nodeset should be in document order,
9308 * Keep only the last value
9309 */
9310 if ((ctxt->value != NULL) &&
9311 (ctxt->value->type == XPATH_NODESET) &&
9312 (ctxt->value->nodesetval != NULL) &&
9313 (ctxt->value->nodesetval->nodeTab != NULL) &&
9314 (ctxt->value->nodesetval->nodeNr > 1)) {
9315 ctxt->value->nodesetval->nodeTab[0] =
9316 ctxt->value->nodesetval->nodeTab[ctxt->
9317 value->
9318 nodesetval->
9319 nodeNr -
9320 1];
9321 ctxt->value->nodesetval->nodeNr = 1;
9322 }
9323 return (total);
9324 }
9325 }
9326
9327 if (op->ch1 != -1)
9328 total +=
9329 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9330 if (op->ch2 == -1)
9331 return (total);
9332 if (ctxt->value == NULL)
9333 return (total);
9334
9335 oldnode = ctxt->context->node;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009336
9337#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009338 /*
9339 * Hum are we filtering the result of an XPointer expression
9340 */
9341 if (ctxt->value->type == XPATH_LOCATIONSET) {
9342 xmlLocationSetPtr newlocset = NULL;
9343 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009344
Daniel Veillardf06307e2001-07-03 10:35:50 +00009345 /*
9346 * Extract the old locset, and then evaluate the result of the
9347 * expression for all the element in the locset. use it to grow
9348 * up a new locset.
9349 */
9350 CHECK_TYPE0(XPATH_LOCATIONSET);
9351 obj = valuePop(ctxt);
9352 oldlocset = obj->user;
9353 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009354
Daniel Veillardf06307e2001-07-03 10:35:50 +00009355 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
9356 ctxt->context->contextSize = 0;
9357 ctxt->context->proximityPosition = 0;
9358 if (op->ch2 != -1)
9359 total +=
9360 xmlXPathCompOpEval(ctxt,
9361 &comp->steps[op->ch2]);
9362 res = valuePop(ctxt);
9363 if (res != NULL)
9364 xmlXPathFreeObject(res);
9365 valuePush(ctxt, obj);
9366 CHECK_ERROR0;
9367 return (total);
9368 }
9369 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009370
Daniel Veillardf06307e2001-07-03 10:35:50 +00009371 for (i = 0; i < oldlocset->locNr; i++) {
9372 /*
9373 * Run the evaluation with a node list made of a
9374 * single item in the nodelocset.
9375 */
9376 ctxt->context->node = oldlocset->locTab[i]->user;
9377 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9378 valuePush(ctxt, tmp);
9379 ctxt->context->contextSize = oldlocset->locNr;
9380 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009381
Daniel Veillardf06307e2001-07-03 10:35:50 +00009382 if (op->ch2 != -1)
9383 total +=
9384 xmlXPathCompOpEval(ctxt,
9385 &comp->steps[op->ch2]);
9386 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009387
Daniel Veillardf06307e2001-07-03 10:35:50 +00009388 /*
9389 * The result of the evaluation need to be tested to
9390 * decided whether the filter succeeded or not
9391 */
9392 res = valuePop(ctxt);
9393 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9394 xmlXPtrLocationSetAdd(newlocset,
9395 xmlXPathObjectCopy
9396 (oldlocset->locTab[i]));
9397 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009398
Daniel Veillardf06307e2001-07-03 10:35:50 +00009399 /*
9400 * Cleanup
9401 */
9402 if (res != NULL)
9403 xmlXPathFreeObject(res);
9404 if (ctxt->value == tmp) {
9405 res = valuePop(ctxt);
9406 xmlXPathFreeObject(res);
9407 }
9408
9409 ctxt->context->node = NULL;
9410 }
9411
9412 /*
9413 * The result is used as the new evaluation locset.
9414 */
9415 xmlXPathFreeObject(obj);
9416 ctxt->context->node = NULL;
9417 ctxt->context->contextSize = -1;
9418 ctxt->context->proximityPosition = -1;
9419 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
9420 ctxt->context->node = oldnode;
9421 return (total);
9422 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009423#endif /* LIBXML_XPTR_ENABLED */
9424
Daniel Veillardf06307e2001-07-03 10:35:50 +00009425 /*
9426 * Extract the old set, and then evaluate the result of the
9427 * expression for all the element in the set. use it to grow
9428 * up a new set.
9429 */
9430 CHECK_TYPE0(XPATH_NODESET);
9431 obj = valuePop(ctxt);
9432 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00009433
Daniel Veillardf06307e2001-07-03 10:35:50 +00009434 oldnode = ctxt->context->node;
9435 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009436
Daniel Veillardf06307e2001-07-03 10:35:50 +00009437 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
9438 ctxt->context->contextSize = 0;
9439 ctxt->context->proximityPosition = 0;
9440 if (op->ch2 != -1)
9441 total +=
9442 xmlXPathCompOpEval(ctxt,
9443 &comp->steps[op->ch2]);
9444 res = valuePop(ctxt);
9445 if (res != NULL)
9446 xmlXPathFreeObject(res);
9447 valuePush(ctxt, obj);
9448 ctxt->context->node = oldnode;
9449 CHECK_ERROR0;
9450 } else {
9451 /*
9452 * Initialize the new set.
9453 */
9454 newset = xmlXPathNodeSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009455
Daniel Veillardf06307e2001-07-03 10:35:50 +00009456 for (i = 0; i < oldset->nodeNr; i++) {
9457 /*
9458 * Run the evaluation with a node list made of
9459 * a single item in the nodeset.
9460 */
9461 ctxt->context->node = oldset->nodeTab[i];
9462 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9463 valuePush(ctxt, tmp);
9464 ctxt->context->contextSize = oldset->nodeNr;
9465 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009466
Daniel Veillardf06307e2001-07-03 10:35:50 +00009467 if (op->ch2 != -1)
9468 total +=
9469 xmlXPathCompOpEval(ctxt,
9470 &comp->steps[op->ch2]);
9471 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009472
Daniel Veillardf06307e2001-07-03 10:35:50 +00009473 /*
9474 * The result of the evaluation need to be tested to
9475 * decided whether the filter succeeded or not
9476 */
9477 res = valuePop(ctxt);
9478 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9479 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
9480 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009481
Daniel Veillardf06307e2001-07-03 10:35:50 +00009482 /*
9483 * Cleanup
9484 */
9485 if (res != NULL)
9486 xmlXPathFreeObject(res);
9487 if (ctxt->value == tmp) {
9488 res = valuePop(ctxt);
9489 xmlXPathFreeObject(res);
9490 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009491
Daniel Veillardf06307e2001-07-03 10:35:50 +00009492 ctxt->context->node = NULL;
9493 }
9494
9495 /*
9496 * The result is used as the new evaluation set.
9497 */
9498 xmlXPathFreeObject(obj);
9499 ctxt->context->node = NULL;
9500 ctxt->context->contextSize = -1;
9501 ctxt->context->proximityPosition = -1;
9502 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
9503 }
9504 ctxt->context->node = oldnode;
9505 return (total);
9506 }
9507 case XPATH_OP_SORT:
9508 if (op->ch1 != -1)
9509 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9510 if ((ctxt->value != NULL) &&
9511 (ctxt->value->type == XPATH_NODESET) &&
9512 (ctxt->value->nodesetval != NULL))
9513 xmlXPathNodeSetSort(ctxt->value->nodesetval);
9514 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009515#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009516 case XPATH_OP_RANGETO:{
9517 xmlXPathObjectPtr range;
9518 xmlXPathObjectPtr res, obj;
9519 xmlXPathObjectPtr tmp;
9520 xmlLocationSetPtr newset = NULL;
9521 xmlNodeSetPtr oldset;
9522 int i;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009523
Daniel Veillardf06307e2001-07-03 10:35:50 +00009524 if (op->ch1 != -1)
9525 total +=
9526 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9527 if (op->ch2 == -1)
9528 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009529
Daniel Veillardf06307e2001-07-03 10:35:50 +00009530 CHECK_TYPE0(XPATH_NODESET);
9531 obj = valuePop(ctxt);
9532 oldset = obj->nodesetval;
9533 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009534
Daniel Veillardf06307e2001-07-03 10:35:50 +00009535 newset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009536
Daniel Veillardf06307e2001-07-03 10:35:50 +00009537 if (oldset != NULL) {
9538 for (i = 0; i < oldset->nodeNr; i++) {
9539 /*
9540 * Run the evaluation with a node list made of a single item
9541 * in the nodeset.
9542 */
9543 ctxt->context->node = oldset->nodeTab[i];
9544 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9545 valuePush(ctxt, tmp);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009546
Daniel Veillardf06307e2001-07-03 10:35:50 +00009547 if (op->ch2 != -1)
9548 total +=
9549 xmlXPathCompOpEval(ctxt,
9550 &comp->steps[op->ch2]);
9551 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009552
Daniel Veillardf06307e2001-07-03 10:35:50 +00009553 /*
9554 * The result of the evaluation need to be tested to
9555 * decided whether the filter succeeded or not
9556 */
9557 res = valuePop(ctxt);
9558 range =
9559 xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
9560 res);
9561 if (range != NULL) {
9562 xmlXPtrLocationSetAdd(newset, range);
9563 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009564
Daniel Veillardf06307e2001-07-03 10:35:50 +00009565 /*
9566 * Cleanup
9567 */
9568 if (res != NULL)
9569 xmlXPathFreeObject(res);
9570 if (ctxt->value == tmp) {
9571 res = valuePop(ctxt);
9572 xmlXPathFreeObject(res);
9573 }
9574
9575 ctxt->context->node = NULL;
9576 }
9577 }
9578
9579 /*
9580 * The result is used as the new evaluation set.
9581 */
9582 xmlXPathFreeObject(obj);
9583 ctxt->context->node = NULL;
9584 ctxt->context->contextSize = -1;
9585 ctxt->context->proximityPosition = -1;
9586 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
9587 return (total);
9588 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009589#endif /* LIBXML_XPTR_ENABLED */
9590 }
9591 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00009592 "XPath: unknown precompiled operation %d\n", op->op);
9593 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009594}
9595
9596/**
9597 * xmlXPathRunEval:
9598 * @ctxt: the XPath parser context with the compiled expression
9599 *
9600 * Evaluate the Precompiled XPath expression in the given context.
9601 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009602static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009603xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
9604 xmlXPathCompExprPtr comp;
9605
9606 if ((ctxt == NULL) || (ctxt->comp == NULL))
9607 return;
9608
9609 if (ctxt->valueTab == NULL) {
9610 /* Allocate the value stack */
9611 ctxt->valueTab = (xmlXPathObjectPtr *)
9612 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
9613 if (ctxt->valueTab == NULL) {
9614 xmlFree(ctxt);
9615 xmlGenericError(xmlGenericErrorContext,
9616 "xmlXPathRunEval: out of memory\n");
9617 return;
9618 }
9619 ctxt->valueNr = 0;
9620 ctxt->valueMax = 10;
9621 ctxt->value = NULL;
9622 }
9623 comp = ctxt->comp;
9624 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
9625}
9626
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009627/************************************************************************
9628 * *
9629 * Public interfaces *
9630 * *
9631 ************************************************************************/
9632
9633/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009634 * xmlXPathEvalPredicate:
9635 * @ctxt: the XPath context
9636 * @res: the Predicate Expression evaluation result
9637 *
9638 * Evaluate a predicate result for the current node.
9639 * A PredicateExpr is evaluated by evaluating the Expr and converting
9640 * the result to a boolean. If the result is a number, the result will
9641 * be converted to true if the number is equal to the position of the
9642 * context node in the context node list (as returned by the position
9643 * function) and will be converted to false otherwise; if the result
9644 * is not a number, then the result will be converted as if by a call
9645 * to the boolean function.
9646 *
9647 * Return 1 if predicate is true, 0 otherwise
9648 */
9649int
9650xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
9651 if (res == NULL) return(0);
9652 switch (res->type) {
9653 case XPATH_BOOLEAN:
9654 return(res->boolval);
9655 case XPATH_NUMBER:
9656 return(res->floatval == ctxt->proximityPosition);
9657 case XPATH_NODESET:
9658 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009659 if (res->nodesetval == NULL)
9660 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009661 return(res->nodesetval->nodeNr != 0);
9662 case XPATH_STRING:
9663 return((res->stringval != NULL) &&
9664 (xmlStrlen(res->stringval) != 0));
9665 default:
9666 STRANGE
9667 }
9668 return(0);
9669}
9670
9671/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009672 * xmlXPathEvaluatePredicateResult:
9673 * @ctxt: the XPath Parser context
9674 * @res: the Predicate Expression evaluation result
9675 *
9676 * Evaluate a predicate result for the current node.
9677 * A PredicateExpr is evaluated by evaluating the Expr and converting
9678 * the result to a boolean. If the result is a number, the result will
9679 * be converted to true if the number is equal to the position of the
9680 * context node in the context node list (as returned by the position
9681 * function) and will be converted to false otherwise; if the result
9682 * is not a number, then the result will be converted as if by a call
9683 * to the boolean function.
9684 *
9685 * Return 1 if predicate is true, 0 otherwise
9686 */
9687int
9688xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
9689 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->context->proximityPosition);
9696 case XPATH_NODESET:
9697 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00009698 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00009699 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +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/**
9711 * xmlXPathCompile:
9712 * @str: the XPath expression
9713 *
9714 * Compile an XPath expression
9715 *
9716 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9717 * the caller has to free the object.
9718 */
9719xmlXPathCompExprPtr
9720xmlXPathCompile(const xmlChar *str) {
9721 xmlXPathParserContextPtr ctxt;
9722 xmlXPathCompExprPtr comp;
9723
9724 xmlXPathInit();
9725
9726 ctxt = xmlXPathNewParserContext(str, NULL);
9727 xmlXPathCompileExpr(ctxt);
9728
Daniel Veillard40af6492001-04-22 08:50:55 +00009729 if (*ctxt->cur != 0) {
9730 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9731 comp = NULL;
9732 } else {
9733 comp = ctxt->comp;
9734 ctxt->comp = NULL;
9735 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009736 xmlXPathFreeParserContext(ctxt);
Daniel Veillardf06307e2001-07-03 10:35:50 +00009737#ifdef DEBUG_EVAL_COUNTS
9738 if (comp != NULL) {
9739 comp->string = xmlStrdup(str);
9740 comp->nb = 0;
9741 }
9742#endif
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009743 return(comp);
9744}
9745
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009746/**
9747 * xmlXPathCompiledEval:
9748 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00009749 * @ctx: the XPath context
9750 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009751 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00009752 *
9753 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9754 * the caller has to free the object.
9755 */
9756xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009757xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00009758 xmlXPathParserContextPtr ctxt;
9759 xmlXPathObjectPtr res, tmp, init = NULL;
9760 int stack = 0;
9761
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009762 if ((comp == NULL) || (ctx == NULL))
9763 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00009764 xmlXPathInit();
9765
9766 CHECK_CONTEXT(ctx)
9767
Daniel Veillardf06307e2001-07-03 10:35:50 +00009768#ifdef DEBUG_EVAL_COUNTS
9769 comp->nb++;
9770 if ((comp->string != NULL) && (comp->nb > 100)) {
9771 fprintf(stderr, "100 x %s\n", comp->string);
9772 comp->nb = 0;
9773 }
9774#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009775 ctxt = xmlXPathCompParserContext(comp, ctx);
9776 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009777
9778 if (ctxt->value == NULL) {
9779 xmlGenericError(xmlGenericErrorContext,
9780 "xmlXPathEval: evaluation failed\n");
9781 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00009782 } else {
9783 res = valuePop(ctxt);
9784 }
9785
Daniel Veillardf06307e2001-07-03 10:35:50 +00009786
Owen Taylor3473f882001-02-23 17:55:21 +00009787 do {
9788 tmp = valuePop(ctxt);
9789 if (tmp != NULL) {
9790 if (tmp != init)
9791 stack++;
9792 xmlXPathFreeObject(tmp);
9793 }
9794 } while (tmp != NULL);
9795 if ((stack != 0) && (res != NULL)) {
9796 xmlGenericError(xmlGenericErrorContext,
9797 "xmlXPathEval: %d object left on the stack\n",
9798 stack);
9799 }
9800 if (ctxt->error != XPATH_EXPRESSION_OK) {
9801 xmlXPathFreeObject(res);
9802 res = NULL;
9803 }
9804
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009805
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009806 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009807 xmlXPathFreeParserContext(ctxt);
9808 return(res);
9809}
9810
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009811/**
9812 * xmlXPathEvalExpr:
9813 * @ctxt: the XPath Parser context
9814 *
9815 * Parse and evaluate an XPath expression in the given context,
9816 * then push the result on the context stack
9817 */
9818void
9819xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
9820 xmlXPathCompileExpr(ctxt);
9821 xmlXPathRunEval(ctxt);
9822}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009823
9824/**
9825 * xmlXPathEval:
9826 * @str: the XPath expression
9827 * @ctx: the XPath context
9828 *
9829 * Evaluate the XPath Location Path in the given context.
9830 *
9831 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9832 * the caller has to free the object.
9833 */
9834xmlXPathObjectPtr
9835xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
9836 xmlXPathParserContextPtr ctxt;
9837 xmlXPathObjectPtr res, tmp, init = NULL;
9838 int stack = 0;
9839
9840 xmlXPathInit();
9841
9842 CHECK_CONTEXT(ctx)
9843
9844 ctxt = xmlXPathNewParserContext(str, ctx);
9845 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009846
9847 if (ctxt->value == NULL) {
9848 xmlGenericError(xmlGenericErrorContext,
9849 "xmlXPathEval: evaluation failed\n");
9850 res = NULL;
9851 } else if (*ctxt->cur != 0) {
9852 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9853 res = NULL;
9854 } else {
9855 res = valuePop(ctxt);
9856 }
9857
9858 do {
9859 tmp = valuePop(ctxt);
9860 if (tmp != NULL) {
9861 if (tmp != init)
9862 stack++;
9863 xmlXPathFreeObject(tmp);
9864 }
9865 } while (tmp != NULL);
9866 if ((stack != 0) && (res != NULL)) {
9867 xmlGenericError(xmlGenericErrorContext,
9868 "xmlXPathEval: %d object left on the stack\n",
9869 stack);
9870 }
9871 if (ctxt->error != XPATH_EXPRESSION_OK) {
9872 xmlXPathFreeObject(res);
9873 res = NULL;
9874 }
9875
Owen Taylor3473f882001-02-23 17:55:21 +00009876 xmlXPathFreeParserContext(ctxt);
9877 return(res);
9878}
9879
9880/**
9881 * xmlXPathEvalExpression:
9882 * @str: the XPath expression
9883 * @ctxt: the XPath context
9884 *
9885 * Evaluate the XPath expression in the given context.
9886 *
9887 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
9888 * the caller has to free the object.
9889 */
9890xmlXPathObjectPtr
9891xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
9892 xmlXPathParserContextPtr pctxt;
9893 xmlXPathObjectPtr res, tmp;
9894 int stack = 0;
9895
9896 xmlXPathInit();
9897
9898 CHECK_CONTEXT(ctxt)
9899
9900 pctxt = xmlXPathNewParserContext(str, ctxt);
9901 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009902
9903 if (*pctxt->cur != 0) {
9904 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9905 res = NULL;
9906 } else {
9907 res = valuePop(pctxt);
9908 }
9909 do {
9910 tmp = valuePop(pctxt);
9911 if (tmp != NULL) {
9912 xmlXPathFreeObject(tmp);
9913 stack++;
9914 }
9915 } while (tmp != NULL);
9916 if ((stack != 0) && (res != NULL)) {
9917 xmlGenericError(xmlGenericErrorContext,
9918 "xmlXPathEvalExpression: %d object left on the stack\n",
9919 stack);
9920 }
9921 xmlXPathFreeParserContext(pctxt);
9922 return(res);
9923}
9924
9925/**
9926 * xmlXPathRegisterAllFunctions:
9927 * @ctxt: the XPath context
9928 *
9929 * Registers all default XPath functions in this context
9930 */
9931void
9932xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
9933{
9934 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
9935 xmlXPathBooleanFunction);
9936 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
9937 xmlXPathCeilingFunction);
9938 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
9939 xmlXPathCountFunction);
9940 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
9941 xmlXPathConcatFunction);
9942 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
9943 xmlXPathContainsFunction);
9944 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
9945 xmlXPathIdFunction);
9946 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
9947 xmlXPathFalseFunction);
9948 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
9949 xmlXPathFloorFunction);
9950 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
9951 xmlXPathLastFunction);
9952 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
9953 xmlXPathLangFunction);
9954 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
9955 xmlXPathLocalNameFunction);
9956 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
9957 xmlXPathNotFunction);
9958 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
9959 xmlXPathNameFunction);
9960 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
9961 xmlXPathNamespaceURIFunction);
9962 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
9963 xmlXPathNormalizeFunction);
9964 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
9965 xmlXPathNumberFunction);
9966 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
9967 xmlXPathPositionFunction);
9968 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
9969 xmlXPathRoundFunction);
9970 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
9971 xmlXPathStringFunction);
9972 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
9973 xmlXPathStringLengthFunction);
9974 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
9975 xmlXPathStartsWithFunction);
9976 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
9977 xmlXPathSubstringFunction);
9978 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
9979 xmlXPathSubstringBeforeFunction);
9980 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
9981 xmlXPathSubstringAfterFunction);
9982 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
9983 xmlXPathSumFunction);
9984 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
9985 xmlXPathTrueFunction);
9986 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
9987 xmlXPathTranslateFunction);
9988}
9989
9990#endif /* LIBXML_XPATH_ENABLED */