blob: 1c8c1a850b5778423d464b16c1a6e2f976f854ef [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
Daniel Veillard567e1b42001-08-01 15:53:47 +00004610 if (cur->children != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004611 if (cur->children->type != XML_ENTITY_DECL)
Daniel Veillard567e1b42001-08-01 15:53:47 +00004612 return(cur->children);
4613 }
4614
4615 if (cur == ctxt->context->node) return(NULL);
4616
Owen Taylor3473f882001-02-23 17:55:21 +00004617 if (cur->next != NULL) return(cur->next);
4618
4619 do {
4620 cur = cur->parent;
4621 if (cur == NULL) return(NULL);
4622 if (cur == ctxt->context->node) return(NULL);
4623 if (cur->next != NULL) {
4624 cur = cur->next;
4625 return(cur);
4626 }
4627 } while (cur != NULL);
4628 return(cur);
4629}
4630
4631/**
4632 * xmlXPathNextDescendantOrSelf:
4633 * @ctxt: the XPath Parser context
4634 * @cur: the current node in the traversal
4635 *
4636 * Traversal function for the "descendant-or-self" direction
4637 * the descendant-or-self axis contains the context node and the descendants
4638 * of the context node in document order; thus the context node is the first
4639 * node on the axis, and the first child of the context node is the second node
4640 * on the axis
4641 *
4642 * Returns the next element following that axis
4643 */
4644xmlNodePtr
4645xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4646 if (cur == NULL) {
4647 if (ctxt->context->node == NULL)
4648 return(NULL);
4649 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4650 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4651 return(NULL);
4652 return(ctxt->context->node);
4653 }
4654
4655 return(xmlXPathNextDescendant(ctxt, cur));
4656}
4657
4658/**
4659 * xmlXPathNextParent:
4660 * @ctxt: the XPath Parser context
4661 * @cur: the current node in the traversal
4662 *
4663 * Traversal function for the "parent" direction
4664 * The parent axis contains the parent of the context node, if there is one.
4665 *
4666 * Returns the next element following that axis
4667 */
4668xmlNodePtr
4669xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4670 /*
4671 * the parent of an attribute or namespace node is the element
4672 * to which the attribute or namespace node is attached
4673 * Namespace handling !!!
4674 */
4675 if (cur == NULL) {
4676 if (ctxt->context->node == NULL) return(NULL);
4677 switch (ctxt->context->node->type) {
4678 case XML_ELEMENT_NODE:
4679 case XML_TEXT_NODE:
4680 case XML_CDATA_SECTION_NODE:
4681 case XML_ENTITY_REF_NODE:
4682 case XML_ENTITY_NODE:
4683 case XML_PI_NODE:
4684 case XML_COMMENT_NODE:
4685 case XML_NOTATION_NODE:
4686 case XML_DTD_NODE:
4687 case XML_ELEMENT_DECL:
4688 case XML_ATTRIBUTE_DECL:
4689 case XML_XINCLUDE_START:
4690 case XML_XINCLUDE_END:
4691 case XML_ENTITY_DECL:
4692 if (ctxt->context->node->parent == NULL)
4693 return((xmlNodePtr) ctxt->context->doc);
4694 return(ctxt->context->node->parent);
4695 case XML_ATTRIBUTE_NODE: {
4696 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
4697
4698 return(att->parent);
4699 }
4700 case XML_DOCUMENT_NODE:
4701 case XML_DOCUMENT_TYPE_NODE:
4702 case XML_DOCUMENT_FRAG_NODE:
4703 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004704#ifdef LIBXML_DOCB_ENABLED
4705 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004706#endif
4707 return(NULL);
4708 case XML_NAMESPACE_DECL:
4709 /*
4710 * TODO !!! may require extending struct _xmlNs with
4711 * parent field
4712 * C.f. Infoset case...
4713 */
4714 return(NULL);
4715 }
4716 }
4717 return(NULL);
4718}
4719
4720/**
4721 * xmlXPathNextAncestor:
4722 * @ctxt: the XPath Parser context
4723 * @cur: the current node in the traversal
4724 *
4725 * Traversal function for the "ancestor" direction
4726 * the ancestor axis contains the ancestors of the context node; the ancestors
4727 * of the context node consist of the parent of context node and the parent's
4728 * parent and so on; the nodes are ordered in reverse document order; thus the
4729 * parent is the first node on the axis, and the parent's parent is the second
4730 * node on the axis
4731 *
4732 * Returns the next element following that axis
4733 */
4734xmlNodePtr
4735xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4736 /*
4737 * the parent of an attribute or namespace node is the element
4738 * to which the attribute or namespace node is attached
4739 * !!!!!!!!!!!!!
4740 */
4741 if (cur == NULL) {
4742 if (ctxt->context->node == NULL) return(NULL);
4743 switch (ctxt->context->node->type) {
4744 case XML_ELEMENT_NODE:
4745 case XML_TEXT_NODE:
4746 case XML_CDATA_SECTION_NODE:
4747 case XML_ENTITY_REF_NODE:
4748 case XML_ENTITY_NODE:
4749 case XML_PI_NODE:
4750 case XML_COMMENT_NODE:
4751 case XML_DTD_NODE:
4752 case XML_ELEMENT_DECL:
4753 case XML_ATTRIBUTE_DECL:
4754 case XML_ENTITY_DECL:
4755 case XML_NOTATION_NODE:
4756 case XML_XINCLUDE_START:
4757 case XML_XINCLUDE_END:
4758 if (ctxt->context->node->parent == NULL)
4759 return((xmlNodePtr) ctxt->context->doc);
4760 return(ctxt->context->node->parent);
4761 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004762 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00004763
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004764 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00004765 }
4766 case XML_DOCUMENT_NODE:
4767 case XML_DOCUMENT_TYPE_NODE:
4768 case XML_DOCUMENT_FRAG_NODE:
4769 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004770#ifdef LIBXML_DOCB_ENABLED
4771 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004772#endif
4773 return(NULL);
4774 case XML_NAMESPACE_DECL:
4775 /*
4776 * TODO !!! may require extending struct _xmlNs with
4777 * parent field
4778 * C.f. Infoset case...
4779 */
4780 return(NULL);
4781 }
4782 return(NULL);
4783 }
4784 if (cur == ctxt->context->doc->children)
4785 return((xmlNodePtr) ctxt->context->doc);
4786 if (cur == (xmlNodePtr) ctxt->context->doc)
4787 return(NULL);
4788 switch (cur->type) {
4789 case XML_ELEMENT_NODE:
4790 case XML_TEXT_NODE:
4791 case XML_CDATA_SECTION_NODE:
4792 case XML_ENTITY_REF_NODE:
4793 case XML_ENTITY_NODE:
4794 case XML_PI_NODE:
4795 case XML_COMMENT_NODE:
4796 case XML_NOTATION_NODE:
4797 case XML_DTD_NODE:
4798 case XML_ELEMENT_DECL:
4799 case XML_ATTRIBUTE_DECL:
4800 case XML_ENTITY_DECL:
4801 case XML_XINCLUDE_START:
4802 case XML_XINCLUDE_END:
4803 return(cur->parent);
4804 case XML_ATTRIBUTE_NODE: {
4805 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
4806
4807 return(att->parent);
4808 }
4809 case XML_DOCUMENT_NODE:
4810 case XML_DOCUMENT_TYPE_NODE:
4811 case XML_DOCUMENT_FRAG_NODE:
4812 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004813#ifdef LIBXML_DOCB_ENABLED
4814 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004815#endif
4816 return(NULL);
4817 case XML_NAMESPACE_DECL:
4818 /*
4819 * TODO !!! may require extending struct _xmlNs with
4820 * parent field
4821 * C.f. Infoset case...
4822 */
4823 return(NULL);
4824 }
4825 return(NULL);
4826}
4827
4828/**
4829 * xmlXPathNextAncestorOrSelf:
4830 * @ctxt: the XPath Parser context
4831 * @cur: the current node in the traversal
4832 *
4833 * Traversal function for the "ancestor-or-self" direction
4834 * he ancestor-or-self axis contains the context node and ancestors of
4835 * the context node in reverse document order; thus the context node is
4836 * the first node on the axis, and the context node's parent the second;
4837 * parent here is defined the same as with the parent axis.
4838 *
4839 * Returns the next element following that axis
4840 */
4841xmlNodePtr
4842xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4843 if (cur == NULL)
4844 return(ctxt->context->node);
4845 return(xmlXPathNextAncestor(ctxt, cur));
4846}
4847
4848/**
4849 * xmlXPathNextFollowingSibling:
4850 * @ctxt: the XPath Parser context
4851 * @cur: the current node in the traversal
4852 *
4853 * Traversal function for the "following-sibling" direction
4854 * The following-sibling axis contains the following siblings of the context
4855 * node in document order.
4856 *
4857 * Returns the next element following that axis
4858 */
4859xmlNodePtr
4860xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4861 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4862 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4863 return(NULL);
4864 if (cur == (xmlNodePtr) ctxt->context->doc)
4865 return(NULL);
4866 if (cur == NULL)
4867 return(ctxt->context->node->next);
4868 return(cur->next);
4869}
4870
4871/**
4872 * xmlXPathNextPrecedingSibling:
4873 * @ctxt: the XPath Parser context
4874 * @cur: the current node in the traversal
4875 *
4876 * Traversal function for the "preceding-sibling" direction
4877 * The preceding-sibling axis contains the preceding siblings of the context
4878 * node in reverse document order; the first preceding sibling is first on the
4879 * axis; the sibling preceding that node is the second on the axis and so on.
4880 *
4881 * Returns the next element following that axis
4882 */
4883xmlNodePtr
4884xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4885 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4886 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4887 return(NULL);
4888 if (cur == (xmlNodePtr) ctxt->context->doc)
4889 return(NULL);
4890 if (cur == NULL)
4891 return(ctxt->context->node->prev);
Daniel Veillardf06307e2001-07-03 10:35:50 +00004892 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
4893 cur = cur->prev;
4894 if (cur == NULL)
4895 return(ctxt->context->node->prev);
4896 }
Owen Taylor3473f882001-02-23 17:55:21 +00004897 return(cur->prev);
4898}
4899
4900/**
4901 * xmlXPathNextFollowing:
4902 * @ctxt: the XPath Parser context
4903 * @cur: the current node in the traversal
4904 *
4905 * Traversal function for the "following" direction
4906 * The following axis contains all nodes in the same document as the context
4907 * node that are after the context node in document order, excluding any
4908 * descendants and excluding attribute nodes and namespace nodes; the nodes
4909 * are ordered in document order
4910 *
4911 * Returns the next element following that axis
4912 */
4913xmlNodePtr
4914xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4915 if (cur != NULL && cur->children != NULL)
4916 return cur->children ;
4917 if (cur == NULL) cur = ctxt->context->node;
4918 if (cur == NULL) return(NULL) ; /* ERROR */
4919 if (cur->next != NULL) return(cur->next) ;
4920 do {
4921 cur = cur->parent;
4922 if (cur == NULL) return(NULL);
4923 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
4924 if (cur->next != NULL) return(cur->next);
4925 } while (cur != NULL);
4926 return(cur);
4927}
4928
4929/*
4930 * xmlXPathIsAncestor:
4931 * @ancestor: the ancestor node
4932 * @node: the current node
4933 *
4934 * Check that @ancestor is a @node's ancestor
4935 *
4936 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
4937 */
4938static int
4939xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
4940 if ((ancestor == NULL) || (node == NULL)) return(0);
4941 /* nodes need to be in the same document */
4942 if (ancestor->doc != node->doc) return(0);
4943 /* avoid searching if ancestor or node is the root node */
4944 if (ancestor == (xmlNodePtr) node->doc) return(1);
4945 if (node == (xmlNodePtr) ancestor->doc) return(0);
4946 while (node->parent != NULL) {
4947 if (node->parent == ancestor)
4948 return(1);
4949 node = node->parent;
4950 }
4951 return(0);
4952}
4953
4954/**
4955 * xmlXPathNextPreceding:
4956 * @ctxt: the XPath Parser context
4957 * @cur: the current node in the traversal
4958 *
4959 * Traversal function for the "preceding" direction
4960 * the preceding axis contains all nodes in the same document as the context
4961 * node that are before the context node in document order, excluding any
4962 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
4963 * ordered in reverse document order
4964 *
4965 * Returns the next element following that axis
4966 */
4967xmlNodePtr
Daniel Veillardf06307e2001-07-03 10:35:50 +00004968xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
4969{
Owen Taylor3473f882001-02-23 17:55:21 +00004970 if (cur == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00004971 cur = ctxt->context->node;
4972 if (cur == NULL)
4973 return (NULL);
4974 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
4975 cur = cur->prev;
Owen Taylor3473f882001-02-23 17:55:21 +00004976 do {
4977 if (cur->prev != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00004978 for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
4979 return (cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004980 }
4981
4982 cur = cur->parent;
Daniel Veillardf06307e2001-07-03 10:35:50 +00004983 if (cur == NULL)
4984 return (NULL);
4985 if (cur == ctxt->context->doc->children)
4986 return (NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004987 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
Daniel Veillardf06307e2001-07-03 10:35:50 +00004988 return (cur);
4989}
4990
4991/**
4992 * xmlXPathNextPrecedingInternal:
4993 * @ctxt: the XPath Parser context
4994 * @cur: the current node in the traversal
4995 *
4996 * Traversal function for the "preceding" direction
4997 * the preceding axis contains all nodes in the same document as the context
4998 * node that are before the context node in document order, excluding any
4999 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
5000 * ordered in reverse document order
5001 * This is a faster implementation but internal only since it requires a
5002 * state kept in the parser context: ctxt->ancestor.
5003 *
5004 * Returns the next element following that axis
5005 */
5006static xmlNodePtr
5007xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
5008 xmlNodePtr cur)
5009{
5010 if (cur == NULL) {
5011 cur = ctxt->context->node;
5012 if (cur == NULL)
5013 return (NULL);
5014 ctxt->ancestor = cur->parent;
5015 }
5016 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
5017 cur = cur->prev;
5018 while (cur->prev == NULL) {
5019 cur = cur->parent;
5020 if (cur == NULL)
5021 return (NULL);
5022 if (cur == ctxt->context->doc->children)
5023 return (NULL);
5024 if (cur != ctxt->ancestor)
5025 return (cur);
5026 ctxt->ancestor = cur->parent;
5027 }
5028 cur = cur->prev;
5029 while (cur->last != NULL)
5030 cur = cur->last;
5031 return (cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005032}
5033
5034/**
5035 * xmlXPathNextNamespace:
5036 * @ctxt: the XPath Parser context
5037 * @cur: the current attribute in the traversal
5038 *
5039 * Traversal function for the "namespace" direction
5040 * the namespace axis contains the namespace nodes of the context node;
5041 * the order of nodes on this axis is implementation-defined; the axis will
5042 * be empty unless the context node is an element
5043 *
5044 * Returns the next element following that axis
5045 */
5046xmlNodePtr
5047xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005048 xmlNodePtr ret;
5049
Owen Taylor3473f882001-02-23 17:55:21 +00005050 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005051 if ((cur == NULL) || (ctxt->context->tmpNsList == NULL)) {
5052 if (ctxt->context->tmpNsList != NULL)
5053 xmlFree(ctxt->context->tmpNsList);
5054 ctxt->context->tmpNsList =
Owen Taylor3473f882001-02-23 17:55:21 +00005055 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005056 if (ctxt->context->tmpNsList == NULL) return(NULL);
5057 ctxt->context->tmpNsNr = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005058 }
Daniel Veillard7d7e3792001-07-30 13:42:13 +00005059 ret = (xmlNodePtr)ctxt->context->tmpNsList[ctxt->context->tmpNsNr++];
5060 if (ret == NULL) {
5061 xmlFree(ctxt->context->tmpNsList);
5062 ctxt->context->tmpNsList = NULL;
5063 }
5064 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00005065}
5066
5067/**
5068 * xmlXPathNextAttribute:
5069 * @ctxt: the XPath Parser context
5070 * @cur: the current attribute in the traversal
5071 *
5072 * Traversal function for the "attribute" direction
5073 * TODO: support DTD inherited default attributes
5074 *
5075 * Returns the next element following that axis
5076 */
5077xmlNodePtr
5078xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00005079 if (ctxt->context->node == NULL)
5080 return(NULL);
5081 if (ctxt->context->node->type != XML_ELEMENT_NODE)
5082 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005083 if (cur == NULL) {
5084 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
5085 return(NULL);
5086 return((xmlNodePtr)ctxt->context->node->properties);
5087 }
5088 return((xmlNodePtr)cur->next);
5089}
5090
5091/************************************************************************
5092 * *
5093 * NodeTest Functions *
5094 * *
5095 ************************************************************************/
5096
Owen Taylor3473f882001-02-23 17:55:21 +00005097#define IS_FUNCTION 200
5098
Owen Taylor3473f882001-02-23 17:55:21 +00005099
5100/************************************************************************
5101 * *
5102 * Implicit tree core function library *
5103 * *
5104 ************************************************************************/
5105
5106/**
5107 * xmlXPathRoot:
5108 * @ctxt: the XPath Parser context
5109 *
5110 * Initialize the context to the root of the document
5111 */
5112void
5113xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
5114 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
5115 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5116}
5117
5118/************************************************************************
5119 * *
5120 * The explicit core function library *
5121 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
5122 * *
5123 ************************************************************************/
5124
5125
5126/**
5127 * xmlXPathLastFunction:
5128 * @ctxt: the XPath Parser context
5129 * @nargs: the number of arguments
5130 *
5131 * Implement the last() XPath function
5132 * number last()
5133 * The last function returns the number of nodes in the context node list.
5134 */
5135void
5136xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5137 CHECK_ARITY(0);
5138 if (ctxt->context->contextSize >= 0) {
5139 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
5140#ifdef DEBUG_EXPR
5141 xmlGenericError(xmlGenericErrorContext,
5142 "last() : %d\n", ctxt->context->contextSize);
5143#endif
5144 } else {
5145 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
5146 }
5147}
5148
5149/**
5150 * xmlXPathPositionFunction:
5151 * @ctxt: the XPath Parser context
5152 * @nargs: the number of arguments
5153 *
5154 * Implement the position() XPath function
5155 * number position()
5156 * The position function returns the position of the context node in the
5157 * context node list. The first position is 1, and so the last positionr
5158 * will be equal to last().
5159 */
5160void
5161xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5162 CHECK_ARITY(0);
5163 if (ctxt->context->proximityPosition >= 0) {
5164 valuePush(ctxt,
5165 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
5166#ifdef DEBUG_EXPR
5167 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
5168 ctxt->context->proximityPosition);
5169#endif
5170 } else {
5171 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
5172 }
5173}
5174
5175/**
5176 * xmlXPathCountFunction:
5177 * @ctxt: the XPath Parser context
5178 * @nargs: the number of arguments
5179 *
5180 * Implement the count() XPath function
5181 * number count(node-set)
5182 */
5183void
5184xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5185 xmlXPathObjectPtr cur;
5186
5187 CHECK_ARITY(1);
5188 if ((ctxt->value == NULL) ||
5189 ((ctxt->value->type != XPATH_NODESET) &&
5190 (ctxt->value->type != XPATH_XSLT_TREE)))
5191 XP_ERROR(XPATH_INVALID_TYPE);
5192 cur = valuePop(ctxt);
5193
Daniel Veillard911f49a2001-04-07 15:39:35 +00005194 if ((cur == NULL) || (cur->nodesetval == NULL))
5195 valuePush(ctxt, xmlXPathNewFloat((double) 0));
5196 else
5197 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Owen Taylor3473f882001-02-23 17:55:21 +00005198 xmlXPathFreeObject(cur);
5199}
5200
5201/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005202 * xmlXPathGetElementsByIds:
5203 * @doc: the document
5204 * @ids: a whitespace separated list of IDs
5205 *
5206 * Selects elements by their unique ID.
5207 *
5208 * Returns a node-set of selected elements.
5209 */
5210static xmlNodeSetPtr
5211xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
5212 xmlNodeSetPtr ret;
5213 const xmlChar *cur = ids;
5214 xmlChar *ID;
5215 xmlAttrPtr attr;
5216 xmlNodePtr elem = NULL;
5217
5218 ret = xmlXPathNodeSetCreate(NULL);
5219
5220 while (IS_BLANK(*cur)) cur++;
5221 while (*cur != 0) {
5222 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
5223 (*cur == '.') || (*cur == '-') ||
5224 (*cur == '_') || (*cur == ':') ||
5225 (IS_COMBINING(*cur)) ||
5226 (IS_EXTENDER(*cur)))
5227 cur++;
5228
5229 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
5230
5231 ID = xmlStrndup(ids, cur - ids);
5232 attr = xmlGetID(doc, ID);
5233 if (attr != NULL) {
5234 elem = attr->parent;
5235 xmlXPathNodeSetAdd(ret, elem);
5236 }
5237 if (ID != NULL)
5238 xmlFree(ID);
5239
5240 while (IS_BLANK(*cur)) cur++;
5241 ids = cur;
5242 }
5243 return(ret);
5244}
5245
5246/**
Owen Taylor3473f882001-02-23 17:55:21 +00005247 * xmlXPathIdFunction:
5248 * @ctxt: the XPath Parser context
5249 * @nargs: the number of arguments
5250 *
5251 * Implement the id() XPath function
5252 * node-set id(object)
5253 * The id function selects elements by their unique ID
5254 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
5255 * then the result is the union of the result of applying id to the
5256 * string value of each of the nodes in the argument node-set. When the
5257 * argument to id is of any other type, the argument is converted to a
5258 * string as if by a call to the string function; the string is split
5259 * into a whitespace-separated list of tokens (whitespace is any sequence
5260 * of characters matching the production S); the result is a node-set
5261 * containing the elements in the same document as the context node that
5262 * have a unique ID equal to any of the tokens in the list.
5263 */
5264void
5265xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005266 xmlChar *tokens;
5267 xmlNodeSetPtr ret;
5268 xmlXPathObjectPtr obj;
Owen Taylor3473f882001-02-23 17:55:21 +00005269
5270 CHECK_ARITY(1);
5271 obj = valuePop(ctxt);
5272 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
5273 if (obj->type == XPATH_NODESET) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005274 xmlNodeSetPtr ns;
Owen Taylor3473f882001-02-23 17:55:21 +00005275 int i;
5276
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005277 ret = xmlXPathNodeSetCreate(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005278
Daniel Veillard911f49a2001-04-07 15:39:35 +00005279 if (obj->nodesetval != NULL) {
5280 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005281 tokens =
5282 xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
5283 ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
5284 ret = xmlXPathNodeSetMerge(ret, ns);
5285 xmlXPathFreeNodeSet(ns);
5286 if (tokens != NULL)
5287 xmlFree(tokens);
Daniel Veillard911f49a2001-04-07 15:39:35 +00005288 }
Owen Taylor3473f882001-02-23 17:55:21 +00005289 }
5290
5291 xmlXPathFreeObject(obj);
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005292 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005293 return;
5294 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005295 obj = xmlXPathConvertString(obj);
Owen Taylor3473f882001-02-23 17:55:21 +00005296
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005297 ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
5298 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005299
Owen Taylor3473f882001-02-23 17:55:21 +00005300 xmlXPathFreeObject(obj);
5301 return;
5302}
5303
5304/**
5305 * xmlXPathLocalNameFunction:
5306 * @ctxt: the XPath Parser context
5307 * @nargs: the number of arguments
5308 *
5309 * Implement the local-name() XPath function
5310 * string local-name(node-set?)
5311 * The local-name function returns a string containing the local part
5312 * of the name of the node in the argument node-set that is first in
5313 * document order. If the node-set is empty or the first node has no
5314 * name, an empty string is returned. If the argument is omitted it
5315 * defaults to the context node.
5316 */
5317void
5318xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5319 xmlXPathObjectPtr cur;
5320
5321 if (nargs == 0) {
5322 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5323 nargs = 1;
5324 }
5325
5326 CHECK_ARITY(1);
5327 if ((ctxt->value == NULL) ||
5328 ((ctxt->value->type != XPATH_NODESET) &&
5329 (ctxt->value->type != XPATH_XSLT_TREE)))
5330 XP_ERROR(XPATH_INVALID_TYPE);
5331 cur = valuePop(ctxt);
5332
Daniel Veillard911f49a2001-04-07 15:39:35 +00005333 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005334 valuePush(ctxt, xmlXPathNewCString(""));
5335 } else {
5336 int i = 0; /* Should be first in document order !!!!! */
5337 switch (cur->nodesetval->nodeTab[i]->type) {
5338 case XML_ELEMENT_NODE:
5339 case XML_ATTRIBUTE_NODE:
5340 case XML_PI_NODE:
5341 valuePush(ctxt,
5342 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
5343 break;
5344 case XML_NAMESPACE_DECL:
5345 valuePush(ctxt, xmlXPathNewString(
5346 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
5347 break;
5348 default:
5349 valuePush(ctxt, xmlXPathNewCString(""));
5350 }
5351 }
5352 xmlXPathFreeObject(cur);
5353}
5354
5355/**
5356 * xmlXPathNamespaceURIFunction:
5357 * @ctxt: the XPath Parser context
5358 * @nargs: the number of arguments
5359 *
5360 * Implement the namespace-uri() XPath function
5361 * string namespace-uri(node-set?)
5362 * The namespace-uri function returns a string containing the
5363 * namespace URI of the expanded name of the node in the argument
5364 * node-set that is first in document order. If the node-set is empty,
5365 * the first node has no name, or the expanded name has no namespace
5366 * URI, an empty string is returned. If the argument is omitted it
5367 * defaults to the context node.
5368 */
5369void
5370xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5371 xmlXPathObjectPtr cur;
5372
5373 if (nargs == 0) {
5374 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5375 nargs = 1;
5376 }
5377 CHECK_ARITY(1);
5378 if ((ctxt->value == NULL) ||
5379 ((ctxt->value->type != XPATH_NODESET) &&
5380 (ctxt->value->type != XPATH_XSLT_TREE)))
5381 XP_ERROR(XPATH_INVALID_TYPE);
5382 cur = valuePop(ctxt);
5383
Daniel Veillard911f49a2001-04-07 15:39:35 +00005384 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005385 valuePush(ctxt, xmlXPathNewCString(""));
5386 } else {
5387 int i = 0; /* Should be first in document order !!!!! */
5388 switch (cur->nodesetval->nodeTab[i]->type) {
5389 case XML_ELEMENT_NODE:
5390 case XML_ATTRIBUTE_NODE:
5391 if (cur->nodesetval->nodeTab[i]->ns == NULL)
5392 valuePush(ctxt, xmlXPathNewCString(""));
5393 else
5394 valuePush(ctxt, xmlXPathNewString(
5395 cur->nodesetval->nodeTab[i]->ns->href));
5396 break;
5397 default:
5398 valuePush(ctxt, xmlXPathNewCString(""));
5399 }
5400 }
5401 xmlXPathFreeObject(cur);
5402}
5403
5404/**
5405 * xmlXPathNameFunction:
5406 * @ctxt: the XPath Parser context
5407 * @nargs: the number of arguments
5408 *
5409 * Implement the name() XPath function
5410 * string name(node-set?)
5411 * The name function returns a string containing a QName representing
5412 * the name of the node in the argument node-set that is first in documenti
5413 * order. The QName must represent the name with respect to the namespace
5414 * declarations in effect on the node whose name is being represented.
5415 * Typically, this will be the form in which the name occurred in the XML
5416 * source. This need not be the case if there are namespace declarations
5417 * in effect on the node that associate multiple prefixes with the same
5418 * namespace. However, an implementation may include information about
5419 * the original prefix in its representation of nodes; in this case, an
5420 * implementation can ensure that the returned string is always the same
5421 * as the QName used in the XML source. If the argument it omitted it
5422 * defaults to the context node.
5423 * Libxml keep the original prefix so the "real qualified name" used is
5424 * returned.
5425 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005426static void
Daniel Veillard04383752001-07-08 14:27:15 +00005427xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
5428{
Owen Taylor3473f882001-02-23 17:55:21 +00005429 xmlXPathObjectPtr cur;
5430
5431 if (nargs == 0) {
Daniel Veillard04383752001-07-08 14:27:15 +00005432 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5433 nargs = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005434 }
5435
5436 CHECK_ARITY(1);
Daniel Veillard04383752001-07-08 14:27:15 +00005437 if ((ctxt->value == NULL) ||
5438 ((ctxt->value->type != XPATH_NODESET) &&
5439 (ctxt->value->type != XPATH_XSLT_TREE)))
5440 XP_ERROR(XPATH_INVALID_TYPE);
Owen Taylor3473f882001-02-23 17:55:21 +00005441 cur = valuePop(ctxt);
5442
Daniel Veillard911f49a2001-04-07 15:39:35 +00005443 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Daniel Veillard04383752001-07-08 14:27:15 +00005444 valuePush(ctxt, xmlXPathNewCString(""));
Owen Taylor3473f882001-02-23 17:55:21 +00005445 } else {
Daniel Veillard04383752001-07-08 14:27:15 +00005446 int i = 0; /* Should be first in document order !!!!! */
Owen Taylor3473f882001-02-23 17:55:21 +00005447
Daniel Veillard04383752001-07-08 14:27:15 +00005448 switch (cur->nodesetval->nodeTab[i]->type) {
5449 case XML_ELEMENT_NODE:
5450 case XML_ATTRIBUTE_NODE:
5451 if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
5452 (cur->nodesetval->nodeTab[i]->ns->prefix == NULL))
5453 valuePush(ctxt,
5454 xmlXPathNewString(cur->nodesetval->
5455 nodeTab[i]->name));
5456
5457 else {
5458 char name[2000];
5459
5460 snprintf(name, sizeof(name), "%s:%s",
5461 (char *) cur->nodesetval->nodeTab[i]->ns->
5462 prefix,
5463 (char *) cur->nodesetval->nodeTab[i]->name);
5464 name[sizeof(name) - 1] = 0;
5465 valuePush(ctxt, xmlXPathNewCString(name));
5466 }
5467 break;
5468 default:
5469 valuePush(ctxt,
5470 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
5471 xmlXPathLocalNameFunction(ctxt, 1);
5472 }
Owen Taylor3473f882001-02-23 17:55:21 +00005473 }
5474 xmlXPathFreeObject(cur);
5475}
5476
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005477
5478/**
Owen Taylor3473f882001-02-23 17:55:21 +00005479 * xmlXPathStringFunction:
5480 * @ctxt: the XPath Parser context
5481 * @nargs: the number of arguments
5482 *
5483 * Implement the string() XPath function
5484 * string string(object?)
5485 * he string function converts an object to a string as follows:
5486 * - A node-set is converted to a string by returning the value of
5487 * the node in the node-set that is first in document order.
5488 * If the node-set is empty, an empty string is returned.
5489 * - A number is converted to a string as follows
5490 * + NaN is converted to the string NaN
5491 * + positive zero is converted to the string 0
5492 * + negative zero is converted to the string 0
5493 * + positive infinity is converted to the string Infinity
5494 * + negative infinity is converted to the string -Infinity
5495 * + if the number is an integer, the number is represented in
5496 * decimal form as a Number with no decimal point and no leading
5497 * zeros, preceded by a minus sign (-) if the number is negative
5498 * + otherwise, the number is represented in decimal form as a
5499 * Number including a decimal point with at least one digit
5500 * before the decimal point and at least one digit after the
5501 * decimal point, preceded by a minus sign (-) if the number
5502 * is negative; there must be no leading zeros before the decimal
5503 * point apart possibly from the one required digit immediatelyi
5504 * before the decimal point; beyond the one required digit
5505 * after the decimal point there must be as many, but only as
5506 * many, more digits as are needed to uniquely distinguish the
5507 * number from all other IEEE 754 numeric values.
5508 * - The boolean false value is converted to the string false.
5509 * The boolean true value is converted to the string true.
5510 *
5511 * If the argument is omitted, it defaults to a node-set with the
5512 * context node as its only member.
5513 */
5514void
5515xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5516 xmlXPathObjectPtr cur;
5517
5518 if (nargs == 0) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005519 valuePush(ctxt,
5520 xmlXPathWrapString(
5521 xmlXPathCastNodeToString(ctxt->context->node)));
5522 return;
Owen Taylor3473f882001-02-23 17:55:21 +00005523 }
5524
5525 CHECK_ARITY(1);
5526 cur = valuePop(ctxt);
5527 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005528 cur = xmlXPathConvertString(cur);
5529 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005530}
5531
5532/**
5533 * xmlXPathStringLengthFunction:
5534 * @ctxt: the XPath Parser context
5535 * @nargs: the number of arguments
5536 *
5537 * Implement the string-length() XPath function
5538 * number string-length(string?)
5539 * The string-length returns the number of characters in the string
5540 * (see [3.6 Strings]). If the argument is omitted, it defaults to
5541 * the context node converted to a string, in other words the value
5542 * of the context node.
5543 */
5544void
5545xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5546 xmlXPathObjectPtr cur;
5547
5548 if (nargs == 0) {
5549 if (ctxt->context->node == NULL) {
5550 valuePush(ctxt, xmlXPathNewFloat(0));
5551 } else {
5552 xmlChar *content;
5553
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005554 content = xmlXPathCastNodeToString(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005555 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00005556 xmlFree(content);
5557 }
5558 return;
5559 }
5560 CHECK_ARITY(1);
5561 CAST_TO_STRING;
5562 CHECK_TYPE(XPATH_STRING);
5563 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005564 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00005565 xmlXPathFreeObject(cur);
5566}
5567
5568/**
5569 * xmlXPathConcatFunction:
5570 * @ctxt: the XPath Parser context
5571 * @nargs: the number of arguments
5572 *
5573 * Implement the concat() XPath function
5574 * string concat(string, string, string*)
5575 * The concat function returns the concatenation of its arguments.
5576 */
5577void
5578xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5579 xmlXPathObjectPtr cur, newobj;
5580 xmlChar *tmp;
5581
5582 if (nargs < 2) {
5583 CHECK_ARITY(2);
5584 }
5585
5586 CAST_TO_STRING;
5587 cur = valuePop(ctxt);
5588 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
5589 xmlXPathFreeObject(cur);
5590 return;
5591 }
5592 nargs--;
5593
5594 while (nargs > 0) {
5595 CAST_TO_STRING;
5596 newobj = valuePop(ctxt);
5597 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
5598 xmlXPathFreeObject(newobj);
5599 xmlXPathFreeObject(cur);
5600 XP_ERROR(XPATH_INVALID_TYPE);
5601 }
5602 tmp = xmlStrcat(newobj->stringval, cur->stringval);
5603 newobj->stringval = cur->stringval;
5604 cur->stringval = tmp;
5605
5606 xmlXPathFreeObject(newobj);
5607 nargs--;
5608 }
5609 valuePush(ctxt, cur);
5610}
5611
5612/**
5613 * xmlXPathContainsFunction:
5614 * @ctxt: the XPath Parser context
5615 * @nargs: the number of arguments
5616 *
5617 * Implement the contains() XPath function
5618 * boolean contains(string, string)
5619 * The contains function returns true if the first argument string
5620 * contains the second argument string, and otherwise returns false.
5621 */
5622void
5623xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5624 xmlXPathObjectPtr hay, needle;
5625
5626 CHECK_ARITY(2);
5627 CAST_TO_STRING;
5628 CHECK_TYPE(XPATH_STRING);
5629 needle = valuePop(ctxt);
5630 CAST_TO_STRING;
5631 hay = valuePop(ctxt);
5632 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5633 xmlXPathFreeObject(hay);
5634 xmlXPathFreeObject(needle);
5635 XP_ERROR(XPATH_INVALID_TYPE);
5636 }
5637 if (xmlStrstr(hay->stringval, needle->stringval))
5638 valuePush(ctxt, xmlXPathNewBoolean(1));
5639 else
5640 valuePush(ctxt, xmlXPathNewBoolean(0));
5641 xmlXPathFreeObject(hay);
5642 xmlXPathFreeObject(needle);
5643}
5644
5645/**
5646 * xmlXPathStartsWithFunction:
5647 * @ctxt: the XPath Parser context
5648 * @nargs: the number of arguments
5649 *
5650 * Implement the starts-with() XPath function
5651 * boolean starts-with(string, string)
5652 * The starts-with function returns true if the first argument string
5653 * starts with the second argument string, and otherwise returns false.
5654 */
5655void
5656xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5657 xmlXPathObjectPtr hay, needle;
5658 int n;
5659
5660 CHECK_ARITY(2);
5661 CAST_TO_STRING;
5662 CHECK_TYPE(XPATH_STRING);
5663 needle = valuePop(ctxt);
5664 CAST_TO_STRING;
5665 hay = valuePop(ctxt);
5666 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5667 xmlXPathFreeObject(hay);
5668 xmlXPathFreeObject(needle);
5669 XP_ERROR(XPATH_INVALID_TYPE);
5670 }
5671 n = xmlStrlen(needle->stringval);
5672 if (xmlStrncmp(hay->stringval, needle->stringval, n))
5673 valuePush(ctxt, xmlXPathNewBoolean(0));
5674 else
5675 valuePush(ctxt, xmlXPathNewBoolean(1));
5676 xmlXPathFreeObject(hay);
5677 xmlXPathFreeObject(needle);
5678}
5679
5680/**
5681 * xmlXPathSubstringFunction:
5682 * @ctxt: the XPath Parser context
5683 * @nargs: the number of arguments
5684 *
5685 * Implement the substring() XPath function
5686 * string substring(string, number, number?)
5687 * The substring function returns the substring of the first argument
5688 * starting at the position specified in the second argument with
5689 * length specified in the third argument. For example,
5690 * substring("12345",2,3) returns "234". If the third argument is not
5691 * specified, it returns the substring starting at the position specified
5692 * in the second argument and continuing to the end of the string. For
5693 * example, substring("12345",2) returns "2345". More precisely, each
5694 * character in the string (see [3.6 Strings]) is considered to have a
5695 * numeric position: the position of the first character is 1, the position
5696 * of the second character is 2 and so on. The returned substring contains
5697 * those characters for which the position of the character is greater than
5698 * or equal to the second argument and, if the third argument is specified,
5699 * less than the sum of the second and third arguments; the comparisons
5700 * and addition used for the above follow the standard IEEE 754 rules. Thus:
5701 * - substring("12345", 1.5, 2.6) returns "234"
5702 * - substring("12345", 0, 3) returns "12"
5703 * - substring("12345", 0 div 0, 3) returns ""
5704 * - substring("12345", 1, 0 div 0) returns ""
5705 * - substring("12345", -42, 1 div 0) returns "12345"
5706 * - substring("12345", -1 div 0, 1 div 0) returns ""
5707 */
5708void
5709xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5710 xmlXPathObjectPtr str, start, len;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005711 double le=0, in;
5712 int i, l, m;
Owen Taylor3473f882001-02-23 17:55:21 +00005713 xmlChar *ret;
5714
Owen Taylor3473f882001-02-23 17:55:21 +00005715 if (nargs < 2) {
5716 CHECK_ARITY(2);
5717 }
5718 if (nargs > 3) {
5719 CHECK_ARITY(3);
5720 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005721 /*
5722 * take care of possible last (position) argument
5723 */
Owen Taylor3473f882001-02-23 17:55:21 +00005724 if (nargs == 3) {
5725 CAST_TO_NUMBER;
5726 CHECK_TYPE(XPATH_NUMBER);
5727 len = valuePop(ctxt);
5728 le = len->floatval;
5729 xmlXPathFreeObject(len);
Owen Taylor3473f882001-02-23 17:55:21 +00005730 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005731
Owen Taylor3473f882001-02-23 17:55:21 +00005732 CAST_TO_NUMBER;
5733 CHECK_TYPE(XPATH_NUMBER);
5734 start = valuePop(ctxt);
5735 in = start->floatval;
5736 xmlXPathFreeObject(start);
5737 CAST_TO_STRING;
5738 CHECK_TYPE(XPATH_STRING);
5739 str = valuePop(ctxt);
Daniel Veillard97ac1312001-05-30 19:14:17 +00005740 m = xmlUTF8Strlen((const unsigned char *)str->stringval);
Owen Taylor3473f882001-02-23 17:55:21 +00005741
Daniel Veillard97ac1312001-05-30 19:14:17 +00005742 /*
5743 * If last pos not present, calculate last position
5744 */
5745 if (nargs != 3)
5746 le = m;
5747
5748 /*
5749 * To meet our requirements, initial index calculations
5750 * must be done before we convert to integer format
5751 *
5752 * First we normalize indices
5753 */
5754 in -= 1.0;
5755 le += in;
5756 if (in < 0.0)
5757 in = 0.0;
5758 if (le > (double)m)
5759 le = (double)m;
5760
5761 /*
5762 * Now we go to integer form, rounding up
5763 */
Owen Taylor3473f882001-02-23 17:55:21 +00005764 i = (int) in;
5765 if (((double)i) != in) i++;
5766
Owen Taylor3473f882001-02-23 17:55:21 +00005767 l = (int) le;
5768 if (((double)l) != le) l++;
5769
Daniel Veillard97ac1312001-05-30 19:14:17 +00005770 if (l > m) l=m;
Owen Taylor3473f882001-02-23 17:55:21 +00005771
5772 /* number of chars to copy */
5773 l -= i;
5774
Daniel Veillard97ac1312001-05-30 19:14:17 +00005775 ret = xmlUTF8Strsub(str->stringval, i, l);
Owen Taylor3473f882001-02-23 17:55:21 +00005776 if (ret == NULL)
5777 valuePush(ctxt, xmlXPathNewCString(""));
5778 else {
5779 valuePush(ctxt, xmlXPathNewString(ret));
5780 xmlFree(ret);
5781 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005782
Owen Taylor3473f882001-02-23 17:55:21 +00005783 xmlXPathFreeObject(str);
5784}
5785
5786/**
5787 * xmlXPathSubstringBeforeFunction:
5788 * @ctxt: the XPath Parser context
5789 * @nargs: the number of arguments
5790 *
5791 * Implement the substring-before() XPath function
5792 * string substring-before(string, string)
5793 * The substring-before function returns the substring of the first
5794 * argument string that precedes the first occurrence of the second
5795 * argument string in the first argument string, or the empty string
5796 * if the first argument string does not contain the second argument
5797 * string. For example, substring-before("1999/04/01","/") returns 1999.
5798 */
5799void
5800xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5801 xmlXPathObjectPtr str;
5802 xmlXPathObjectPtr find;
5803 xmlBufferPtr target;
5804 const xmlChar *point;
5805 int offset;
5806
5807 CHECK_ARITY(2);
5808 CAST_TO_STRING;
5809 find = valuePop(ctxt);
5810 CAST_TO_STRING;
5811 str = valuePop(ctxt);
5812
5813 target = xmlBufferCreate();
5814 if (target) {
5815 point = xmlStrstr(str->stringval, find->stringval);
5816 if (point) {
5817 offset = (int)(point - str->stringval);
5818 xmlBufferAdd(target, str->stringval, offset);
5819 }
5820 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5821 xmlBufferFree(target);
5822 }
5823
5824 xmlXPathFreeObject(str);
5825 xmlXPathFreeObject(find);
5826}
5827
5828/**
5829 * xmlXPathSubstringAfterFunction:
5830 * @ctxt: the XPath Parser context
5831 * @nargs: the number of arguments
5832 *
5833 * Implement the substring-after() XPath function
5834 * string substring-after(string, string)
5835 * The substring-after function returns the substring of the first
5836 * argument string that follows the first occurrence of the second
5837 * argument string in the first argument string, or the empty stringi
5838 * if the first argument string does not contain the second argument
5839 * string. For example, substring-after("1999/04/01","/") returns 04/01,
5840 * and substring-after("1999/04/01","19") returns 99/04/01.
5841 */
5842void
5843xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5844 xmlXPathObjectPtr str;
5845 xmlXPathObjectPtr find;
5846 xmlBufferPtr target;
5847 const xmlChar *point;
5848 int offset;
5849
5850 CHECK_ARITY(2);
5851 CAST_TO_STRING;
5852 find = valuePop(ctxt);
5853 CAST_TO_STRING;
5854 str = valuePop(ctxt);
5855
5856 target = xmlBufferCreate();
5857 if (target) {
5858 point = xmlStrstr(str->stringval, find->stringval);
5859 if (point) {
5860 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
5861 xmlBufferAdd(target, &str->stringval[offset],
5862 xmlStrlen(str->stringval) - offset);
5863 }
5864 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5865 xmlBufferFree(target);
5866 }
5867
5868 xmlXPathFreeObject(str);
5869 xmlXPathFreeObject(find);
5870}
5871
5872/**
5873 * xmlXPathNormalizeFunction:
5874 * @ctxt: the XPath Parser context
5875 * @nargs: the number of arguments
5876 *
5877 * Implement the normalize-space() XPath function
5878 * string normalize-space(string?)
5879 * The normalize-space function returns the argument string with white
5880 * space normalized by stripping leading and trailing whitespace
5881 * and replacing sequences of whitespace characters by a single
5882 * space. Whitespace characters are the same allowed by the S production
5883 * in XML. If the argument is omitted, it defaults to the context
5884 * node converted to a string, in other words the value of the context node.
5885 */
5886void
5887xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5888 xmlXPathObjectPtr obj = NULL;
5889 xmlChar *source = NULL;
5890 xmlBufferPtr target;
5891 xmlChar blank;
5892
5893 if (nargs == 0) {
5894 /* Use current context node */
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005895 valuePush(ctxt,
5896 xmlXPathWrapString(
5897 xmlXPathCastNodeToString(ctxt->context->node)));
Owen Taylor3473f882001-02-23 17:55:21 +00005898 nargs = 1;
5899 }
5900
5901 CHECK_ARITY(1);
5902 CAST_TO_STRING;
5903 CHECK_TYPE(XPATH_STRING);
5904 obj = valuePop(ctxt);
5905 source = obj->stringval;
5906
5907 target = xmlBufferCreate();
5908 if (target && source) {
5909
5910 /* Skip leading whitespaces */
5911 while (IS_BLANK(*source))
5912 source++;
5913
5914 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
5915 blank = 0;
5916 while (*source) {
5917 if (IS_BLANK(*source)) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005918 blank = 0x20;
Owen Taylor3473f882001-02-23 17:55:21 +00005919 } else {
5920 if (blank) {
5921 xmlBufferAdd(target, &blank, 1);
5922 blank = 0;
5923 }
5924 xmlBufferAdd(target, source, 1);
5925 }
5926 source++;
5927 }
5928
5929 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5930 xmlBufferFree(target);
5931 }
5932 xmlXPathFreeObject(obj);
5933}
5934
5935/**
5936 * xmlXPathTranslateFunction:
5937 * @ctxt: the XPath Parser context
5938 * @nargs: the number of arguments
5939 *
5940 * Implement the translate() XPath function
5941 * string translate(string, string, string)
5942 * The translate function returns the first argument string with
5943 * occurrences of characters in the second argument string replaced
5944 * by the character at the corresponding position in the third argument
5945 * string. For example, translate("bar","abc","ABC") returns the string
5946 * BAr. If there is a character in the second argument string with no
5947 * character at a corresponding position in the third argument string
5948 * (because the second argument string is longer than the third argument
5949 * string), then occurrences of that character in the first argument
5950 * string are removed. For example, translate("--aaa--","abc-","ABC")
5951 * returns "AAA". If a character occurs more than once in second
5952 * argument string, then the first occurrence determines the replacement
5953 * character. If the third argument string is longer than the second
5954 * argument string, then excess characters are ignored.
5955 */
5956void
5957xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00005958 xmlXPathObjectPtr str;
5959 xmlXPathObjectPtr from;
5960 xmlXPathObjectPtr to;
5961 xmlBufferPtr target;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005962 int offset, max;
Daniel Veillarde043ee12001-04-16 14:08:07 +00005963 xmlChar ch;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005964 xmlChar *point;
5965 xmlChar *cptr;
Owen Taylor3473f882001-02-23 17:55:21 +00005966
Daniel Veillarde043ee12001-04-16 14:08:07 +00005967 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00005968
Daniel Veillarde043ee12001-04-16 14:08:07 +00005969 CAST_TO_STRING;
5970 to = valuePop(ctxt);
5971 CAST_TO_STRING;
5972 from = valuePop(ctxt);
5973 CAST_TO_STRING;
5974 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005975
Daniel Veillarde043ee12001-04-16 14:08:07 +00005976 target = xmlBufferCreate();
5977 if (target) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005978 max = xmlUTF8Strlen(to->stringval);
5979 for (cptr = str->stringval; (ch=*cptr); ) {
5980 offset = xmlUTF8Strloc(from->stringval, cptr);
5981 if (offset >= 0) {
5982 if (offset < max) {
5983 point = xmlUTF8Strpos(to->stringval, offset);
5984 if (point)
5985 xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1));
5986 }
5987 } else
5988 xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
5989
5990 /* Step to next character in input */
5991 cptr++;
5992 if ( ch & 0x80 ) {
5993 /* if not simple ascii, verify proper format */
5994 if ( (ch & 0xc0) != 0xc0 ) {
5995 xmlGenericError(xmlGenericErrorContext,
5996 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
5997 break;
5998 }
5999 /* then skip over remaining bytes for this char */
6000 while ( (ch <<= 1) & 0x80 )
6001 if ( (*cptr++ & 0xc0) != 0x80 ) {
6002 xmlGenericError(xmlGenericErrorContext,
6003 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
6004 break;
6005 }
6006 if (ch & 0x80) /* must have had error encountered */
6007 break;
6008 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00006009 }
Owen Taylor3473f882001-02-23 17:55:21 +00006010 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00006011 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
6012 xmlBufferFree(target);
6013 xmlXPathFreeObject(str);
6014 xmlXPathFreeObject(from);
6015 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00006016}
6017
6018/**
6019 * xmlXPathBooleanFunction:
6020 * @ctxt: the XPath Parser context
6021 * @nargs: the number of arguments
6022 *
6023 * Implement the boolean() XPath function
6024 * boolean boolean(object)
6025 * he boolean function converts its argument to a boolean as follows:
6026 * - a number is true if and only if it is neither positive or
6027 * negative zero nor NaN
6028 * - a node-set is true if and only if it is non-empty
6029 * - a string is true if and only if its length is non-zero
6030 */
6031void
6032xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6033 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00006034
6035 CHECK_ARITY(1);
6036 cur = valuePop(ctxt);
6037 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006038 cur = xmlXPathConvertBoolean(cur);
6039 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00006040}
6041
6042/**
6043 * xmlXPathNotFunction:
6044 * @ctxt: the XPath Parser context
6045 * @nargs: the number of arguments
6046 *
6047 * Implement the not() XPath function
6048 * boolean not(boolean)
6049 * The not function returns true if its argument is false,
6050 * and false otherwise.
6051 */
6052void
6053xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6054 CHECK_ARITY(1);
6055 CAST_TO_BOOLEAN;
6056 CHECK_TYPE(XPATH_BOOLEAN);
6057 ctxt->value->boolval = ! ctxt->value->boolval;
6058}
6059
6060/**
6061 * xmlXPathTrueFunction:
6062 * @ctxt: the XPath Parser context
6063 * @nargs: the number of arguments
6064 *
6065 * Implement the true() XPath function
6066 * boolean true()
6067 */
6068void
6069xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6070 CHECK_ARITY(0);
6071 valuePush(ctxt, xmlXPathNewBoolean(1));
6072}
6073
6074/**
6075 * xmlXPathFalseFunction:
6076 * @ctxt: the XPath Parser context
6077 * @nargs: the number of arguments
6078 *
6079 * Implement the false() XPath function
6080 * boolean false()
6081 */
6082void
6083xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6084 CHECK_ARITY(0);
6085 valuePush(ctxt, xmlXPathNewBoolean(0));
6086}
6087
6088/**
6089 * xmlXPathLangFunction:
6090 * @ctxt: the XPath Parser context
6091 * @nargs: the number of arguments
6092 *
6093 * Implement the lang() XPath function
6094 * boolean lang(string)
6095 * The lang function returns true or false depending on whether the
6096 * language of the context node as specified by xml:lang attributes
6097 * is the same as or is a sublanguage of the language specified by
6098 * the argument string. The language of the context node is determined
6099 * by the value of the xml:lang attribute on the context node, or, if
6100 * the context node has no xml:lang attribute, by the value of the
6101 * xml:lang attribute on the nearest ancestor of the context node that
6102 * has an xml:lang attribute. If there is no such attribute, then lang
6103 * returns false. If there is such an attribute, then lang returns
6104 * true if the attribute value is equal to the argument ignoring case,
6105 * or if there is some suffix starting with - such that the attribute
6106 * value is equal to the argument ignoring that suffix of the attribute
6107 * value and ignoring case.
6108 */
6109void
6110xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6111 xmlXPathObjectPtr val;
6112 const xmlChar *theLang;
6113 const xmlChar *lang;
6114 int ret = 0;
6115 int i;
6116
6117 CHECK_ARITY(1);
6118 CAST_TO_STRING;
6119 CHECK_TYPE(XPATH_STRING);
6120 val = valuePop(ctxt);
6121 lang = val->stringval;
6122 theLang = xmlNodeGetLang(ctxt->context->node);
6123 if ((theLang != NULL) && (lang != NULL)) {
6124 for (i = 0;lang[i] != 0;i++)
6125 if (toupper(lang[i]) != toupper(theLang[i]))
6126 goto not_equal;
6127 ret = 1;
6128 }
6129not_equal:
6130 xmlXPathFreeObject(val);
6131 valuePush(ctxt, xmlXPathNewBoolean(ret));
6132}
6133
6134/**
6135 * xmlXPathNumberFunction:
6136 * @ctxt: the XPath Parser context
6137 * @nargs: the number of arguments
6138 *
6139 * Implement the number() XPath function
6140 * number number(object?)
6141 */
6142void
6143xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6144 xmlXPathObjectPtr cur;
6145 double res;
6146
6147 if (nargs == 0) {
6148 if (ctxt->context->node == NULL) {
6149 valuePush(ctxt, xmlXPathNewFloat(0.0));
6150 } else {
6151 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
6152
6153 res = xmlXPathStringEvalNumber(content);
6154 valuePush(ctxt, xmlXPathNewFloat(res));
6155 xmlFree(content);
6156 }
6157 return;
6158 }
6159
6160 CHECK_ARITY(1);
6161 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006162 cur = xmlXPathConvertNumber(cur);
6163 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00006164}
6165
6166/**
6167 * xmlXPathSumFunction:
6168 * @ctxt: the XPath Parser context
6169 * @nargs: the number of arguments
6170 *
6171 * Implement the sum() XPath function
6172 * number sum(node-set)
6173 * The sum function returns the sum of the values of the nodes in
6174 * the argument node-set.
6175 */
6176void
6177xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6178 xmlXPathObjectPtr cur;
6179 int i;
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006180 double res = 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +00006181
6182 CHECK_ARITY(1);
6183 if ((ctxt->value == NULL) ||
6184 ((ctxt->value->type != XPATH_NODESET) &&
6185 (ctxt->value->type != XPATH_XSLT_TREE)))
6186 XP_ERROR(XPATH_INVALID_TYPE);
6187 cur = valuePop(ctxt);
6188
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006189 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006190 valuePush(ctxt, xmlXPathNewFloat(0.0));
6191 } else {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006192 for (i = 0; i < cur->nodesetval->nodeNr; i++) {
6193 res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00006194 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006195 valuePush(ctxt, xmlXPathNewFloat(res));
Owen Taylor3473f882001-02-23 17:55:21 +00006196 }
6197 xmlXPathFreeObject(cur);
6198}
6199
6200/**
6201 * xmlXPathFloorFunction:
6202 * @ctxt: the XPath Parser context
6203 * @nargs: the number of arguments
6204 *
6205 * Implement the floor() XPath function
6206 * number floor(number)
6207 * The floor function returns the largest (closest to positive infinity)
6208 * number that is not greater than the argument and that is an integer.
6209 */
6210void
6211xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6212 CHECK_ARITY(1);
6213 CAST_TO_NUMBER;
6214 CHECK_TYPE(XPATH_NUMBER);
6215#if 0
6216 ctxt->value->floatval = floor(ctxt->value->floatval);
6217#else
6218 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
6219 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
6220#endif
6221}
6222
6223/**
6224 * xmlXPathCeilingFunction:
6225 * @ctxt: the XPath Parser context
6226 * @nargs: the number of arguments
6227 *
6228 * Implement the ceiling() XPath function
6229 * number ceiling(number)
6230 * The ceiling function returns the smallest (closest to negative infinity)
6231 * number that is not less than the argument and that is an integer.
6232 */
6233void
6234xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6235 double f;
6236
6237 CHECK_ARITY(1);
6238 CAST_TO_NUMBER;
6239 CHECK_TYPE(XPATH_NUMBER);
6240
6241#if 0
6242 ctxt->value->floatval = ceil(ctxt->value->floatval);
6243#else
6244 f = (double)((int) ctxt->value->floatval);
6245 if (f != ctxt->value->floatval)
6246 ctxt->value->floatval = f + 1;
6247#endif
6248}
6249
6250/**
6251 * xmlXPathRoundFunction:
6252 * @ctxt: the XPath Parser context
6253 * @nargs: the number of arguments
6254 *
6255 * Implement the round() XPath function
6256 * number round(number)
6257 * The round function returns the number that is closest to the
6258 * argument and that is an integer. If there are two such numbers,
6259 * then the one that is even is returned.
6260 */
6261void
6262xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6263 double f;
6264
6265 CHECK_ARITY(1);
6266 CAST_TO_NUMBER;
6267 CHECK_TYPE(XPATH_NUMBER);
6268
6269 if ((ctxt->value->floatval == xmlXPathNAN) ||
6270 (ctxt->value->floatval == xmlXPathPINF) ||
6271 (ctxt->value->floatval == xmlXPathNINF) ||
6272 (ctxt->value->floatval == 0.0))
6273 return;
6274
6275#if 0
6276 f = floor(ctxt->value->floatval);
6277#else
6278 f = (double)((int) ctxt->value->floatval);
6279#endif
6280 if (ctxt->value->floatval < f + 0.5)
6281 ctxt->value->floatval = f;
6282 else
6283 ctxt->value->floatval = f + 1;
6284}
6285
6286/************************************************************************
6287 * *
6288 * The Parser *
6289 * *
6290 ************************************************************************/
6291
6292/*
6293 * a couple of forward declarations since we use a recursive call based
6294 * implementation.
6295 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006296static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006297static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006298static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006299#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006300static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
6301#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006302#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006303static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006304#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00006305static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
6306 int qualified);
Owen Taylor3473f882001-02-23 17:55:21 +00006307
6308/**
Daniel Veillard61d80a22001-04-27 17:13:01 +00006309 * xmlXPathCurrentChar:
6310 * @ctxt: the XPath parser context
6311 * @cur: pointer to the beginning of the char
6312 * @len: pointer to the length of the char read
6313 *
6314 * The current char value, if using UTF-8 this may actaully span multiple
6315 * bytes in the input buffer.
6316 *
6317 * Returns the current char value and its lenght
6318 */
6319
6320static int
6321xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
6322 unsigned char c;
6323 unsigned int val;
6324 const xmlChar *cur;
6325
6326 if (ctxt == NULL)
6327 return(0);
6328 cur = ctxt->cur;
6329
6330 /*
6331 * We are supposed to handle UTF8, check it's valid
6332 * From rfc2044: encoding of the Unicode values on UTF-8:
6333 *
6334 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
6335 * 0000 0000-0000 007F 0xxxxxxx
6336 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
6337 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
6338 *
6339 * Check for the 0x110000 limit too
6340 */
6341 c = *cur;
6342 if (c & 0x80) {
6343 if ((cur[1] & 0xc0) != 0x80)
6344 goto encoding_error;
6345 if ((c & 0xe0) == 0xe0) {
6346
6347 if ((cur[2] & 0xc0) != 0x80)
6348 goto encoding_error;
6349 if ((c & 0xf0) == 0xf0) {
6350 if (((c & 0xf8) != 0xf0) ||
6351 ((cur[3] & 0xc0) != 0x80))
6352 goto encoding_error;
6353 /* 4-byte code */
6354 *len = 4;
6355 val = (cur[0] & 0x7) << 18;
6356 val |= (cur[1] & 0x3f) << 12;
6357 val |= (cur[2] & 0x3f) << 6;
6358 val |= cur[3] & 0x3f;
6359 } else {
6360 /* 3-byte code */
6361 *len = 3;
6362 val = (cur[0] & 0xf) << 12;
6363 val |= (cur[1] & 0x3f) << 6;
6364 val |= cur[2] & 0x3f;
6365 }
6366 } else {
6367 /* 2-byte code */
6368 *len = 2;
6369 val = (cur[0] & 0x1f) << 6;
6370 val |= cur[1] & 0x3f;
6371 }
6372 if (!IS_CHAR(val)) {
6373 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
6374 }
6375 return(val);
6376 } else {
6377 /* 1-byte code */
6378 *len = 1;
6379 return((int) *cur);
6380 }
6381encoding_error:
6382 /*
6383 * If we detect an UTF8 error that probably mean that the
6384 * input encoding didn't get properly advertized in the
6385 * declaration header. Report the error and switch the encoding
6386 * to ISO-Latin-1 (if you don't like this policy, just declare the
6387 * encoding !)
6388 */
Daniel Veillard42596ad2001-05-22 16:57:14 +00006389 *len = 0;
Daniel Veillard61d80a22001-04-27 17:13:01 +00006390 XP_ERROR0(XPATH_ENCODING_ERROR);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006391}
6392
6393/**
Owen Taylor3473f882001-02-23 17:55:21 +00006394 * xmlXPathParseNCName:
6395 * @ctxt: the XPath Parser context
6396 *
6397 * parse an XML namespace non qualified name.
6398 *
6399 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
6400 *
6401 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
6402 * CombiningChar | Extender
6403 *
6404 * Returns the namespace name or NULL
6405 */
6406
6407xmlChar *
6408xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard2156a562001-04-28 12:24:34 +00006409 const xmlChar *in;
6410 xmlChar *ret;
6411 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006412
Daniel Veillard2156a562001-04-28 12:24:34 +00006413 /*
6414 * Accelerator for simple ASCII names
6415 */
6416 in = ctxt->cur;
6417 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6418 ((*in >= 0x41) && (*in <= 0x5A)) ||
6419 (*in == '_')) {
6420 in++;
6421 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6422 ((*in >= 0x41) && (*in <= 0x5A)) ||
6423 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard9a89a8a2001-06-27 11:13:35 +00006424 (*in == '_') || (*in == '.') ||
6425 (*in == '-'))
Daniel Veillard2156a562001-04-28 12:24:34 +00006426 in++;
6427 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
6428 (*in == '[') || (*in == ']') || (*in == ':') ||
6429 (*in == '@') || (*in == '*')) {
6430 count = in - ctxt->cur;
6431 if (count == 0)
6432 return(NULL);
6433 ret = xmlStrndup(ctxt->cur, count);
6434 ctxt->cur = in;
6435 return(ret);
6436 }
6437 }
6438 return(xmlXPathParseNameComplex(ctxt, 0));
Owen Taylor3473f882001-02-23 17:55:21 +00006439}
6440
Daniel Veillard2156a562001-04-28 12:24:34 +00006441
Owen Taylor3473f882001-02-23 17:55:21 +00006442/**
6443 * xmlXPathParseQName:
6444 * @ctxt: the XPath Parser context
6445 * @prefix: a xmlChar **
6446 *
6447 * parse an XML qualified name
6448 *
6449 * [NS 5] QName ::= (Prefix ':')? LocalPart
6450 *
6451 * [NS 6] Prefix ::= NCName
6452 *
6453 * [NS 7] LocalPart ::= NCName
6454 *
6455 * Returns the function returns the local part, and prefix is updated
6456 * to get the Prefix if any.
6457 */
6458
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006459static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00006460xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
6461 xmlChar *ret = NULL;
6462
6463 *prefix = NULL;
6464 ret = xmlXPathParseNCName(ctxt);
6465 if (CUR == ':') {
6466 *prefix = ret;
6467 NEXT;
6468 ret = xmlXPathParseNCName(ctxt);
6469 }
6470 return(ret);
6471}
6472
6473/**
6474 * xmlXPathParseName:
6475 * @ctxt: the XPath Parser context
6476 *
6477 * parse an XML name
6478 *
6479 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
6480 * CombiningChar | Extender
6481 *
6482 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
6483 *
6484 * Returns the namespace name or NULL
6485 */
6486
6487xmlChar *
6488xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006489 const xmlChar *in;
6490 xmlChar *ret;
6491 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006492
Daniel Veillard61d80a22001-04-27 17:13:01 +00006493 /*
6494 * Accelerator for simple ASCII names
6495 */
6496 in = ctxt->cur;
6497 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6498 ((*in >= 0x41) && (*in <= 0x5A)) ||
6499 (*in == '_') || (*in == ':')) {
6500 in++;
6501 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6502 ((*in >= 0x41) && (*in <= 0x5A)) ||
6503 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard76d66f42001-05-16 21:05:17 +00006504 (*in == '_') || (*in == '-') ||
6505 (*in == ':') || (*in == '.'))
Daniel Veillard61d80a22001-04-27 17:13:01 +00006506 in++;
Daniel Veillard76d66f42001-05-16 21:05:17 +00006507 if ((*in > 0) && (*in < 0x80)) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006508 count = in - ctxt->cur;
6509 ret = xmlStrndup(ctxt->cur, count);
6510 ctxt->cur = in;
6511 return(ret);
6512 }
6513 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006514 return(xmlXPathParseNameComplex(ctxt, 1));
Owen Taylor3473f882001-02-23 17:55:21 +00006515}
6516
Daniel Veillard61d80a22001-04-27 17:13:01 +00006517static xmlChar *
Daniel Veillard2156a562001-04-28 12:24:34 +00006518xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006519 xmlChar buf[XML_MAX_NAMELEN + 5];
6520 int len = 0, l;
6521 int c;
6522
6523 /*
6524 * Handler for more complex cases
6525 */
6526 c = CUR_CHAR(l);
6527 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
Daniel Veillard2156a562001-04-28 12:24:34 +00006528 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
6529 (c == '*') || /* accelerators */
Daniel Veillard61d80a22001-04-27 17:13:01 +00006530 (!IS_LETTER(c) && (c != '_') &&
Daniel Veillard2156a562001-04-28 12:24:34 +00006531 ((qualified) && (c != ':')))) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006532 return(NULL);
6533 }
6534
6535 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
6536 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
6537 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006538 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006539 (IS_COMBINING(c)) ||
6540 (IS_EXTENDER(c)))) {
6541 COPY_BUF(l,buf,len,c);
6542 NEXTL(l);
6543 c = CUR_CHAR(l);
6544 if (len >= XML_MAX_NAMELEN) {
6545 /*
6546 * Okay someone managed to make a huge name, so he's ready to pay
6547 * for the processing speed.
6548 */
6549 xmlChar *buffer;
6550 int max = len * 2;
6551
6552 buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
6553 if (buffer == NULL) {
6554 XP_ERROR0(XPATH_MEMORY_ERROR);
6555 }
6556 memcpy(buffer, buf, len);
6557 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
6558 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006559 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006560 (IS_COMBINING(c)) ||
6561 (IS_EXTENDER(c))) {
6562 if (len + 10 > max) {
6563 max *= 2;
6564 buffer = (xmlChar *) xmlRealloc(buffer,
6565 max * sizeof(xmlChar));
Daniel Veillard61d80a22001-04-27 17:13:01 +00006566 if (buffer == NULL) {
6567 XP_ERROR0(XPATH_MEMORY_ERROR);
6568 }
6569 }
6570 COPY_BUF(l,buffer,len,c);
6571 NEXTL(l);
6572 c = CUR_CHAR(l);
6573 }
6574 buffer[len] = 0;
6575 return(buffer);
6576 }
6577 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006578 if (len == 0)
6579 return(NULL);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006580 return(xmlStrndup(buf, len));
6581}
Owen Taylor3473f882001-02-23 17:55:21 +00006582/**
6583 * xmlXPathStringEvalNumber:
6584 * @str: A string to scan
6585 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00006586 * [30a] Float ::= Number ('e' Digits?)?
6587 *
Owen Taylor3473f882001-02-23 17:55:21 +00006588 * [30] Number ::= Digits ('.' Digits?)?
6589 * | '.' Digits
6590 * [31] Digits ::= [0-9]+
6591 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006592 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00006593 * In complement of the Number expression, this function also handles
6594 * negative values : '-' Number.
6595 *
6596 * Returns the double value.
6597 */
6598double
6599xmlXPathStringEvalNumber(const xmlChar *str) {
6600 const xmlChar *cur = str;
6601 double ret = 0.0;
6602 double mult = 1;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006603 int ok = 0, tmp = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006604 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006605 int exponent = 0;
6606 int is_exponent_negative = 0;
6607
Owen Taylor3473f882001-02-23 17:55:21 +00006608 while (IS_BLANK(*cur)) cur++;
6609 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
6610 return(xmlXPathNAN);
6611 }
6612 if (*cur == '-') {
6613 isneg = 1;
6614 cur++;
6615 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006616 /*
6617 * tmp is a workaroudn against a gcc compiler bug
6618 */
Owen Taylor3473f882001-02-23 17:55:21 +00006619 while ((*cur >= '0') && (*cur <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006620 tmp = tmp * 10 + (*cur - '0');
Owen Taylor3473f882001-02-23 17:55:21 +00006621 ok = 1;
6622 cur++;
6623 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006624 ret = (double) tmp;
6625
Owen Taylor3473f882001-02-23 17:55:21 +00006626 if (*cur == '.') {
6627 cur++;
6628 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
6629 return(xmlXPathNAN);
6630 }
6631 while ((*cur >= '0') && (*cur <= '9')) {
6632 mult /= 10;
6633 ret = ret + (*cur - '0') * mult;
6634 cur++;
6635 }
6636 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006637 if ((*cur == 'e') || (*cur == 'E')) {
6638 cur++;
6639 if (*cur == '-') {
6640 is_exponent_negative = 1;
6641 cur++;
6642 }
6643 while ((*cur >= '0') && (*cur <= '9')) {
6644 exponent = exponent * 10 + (*cur - '0');
6645 cur++;
6646 }
6647 }
Owen Taylor3473f882001-02-23 17:55:21 +00006648 while (IS_BLANK(*cur)) cur++;
6649 if (*cur != 0) return(xmlXPathNAN);
6650 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006651 if (is_exponent_negative) exponent = -exponent;
6652 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00006653 return(ret);
6654}
6655
6656/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006657 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00006658 * @ctxt: the XPath Parser context
6659 *
6660 * [30] Number ::= Digits ('.' Digits?)?
6661 * | '.' Digits
6662 * [31] Digits ::= [0-9]+
6663 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006664 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00006665 *
6666 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006667static void
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006668xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
6669{
Owen Taylor3473f882001-02-23 17:55:21 +00006670 double ret = 0.0;
6671 double mult = 1;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006672 int ok = 0, tmp = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006673 int exponent = 0;
6674 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006675
6676 CHECK_ERROR;
6677 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
6678 XP_ERROR(XPATH_NUMBER_ERROR);
6679 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006680 /*
6681 * Try to work around a gcc optimizer bug
6682 */
Owen Taylor3473f882001-02-23 17:55:21 +00006683 while ((CUR >= '0') && (CUR <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006684 tmp = tmp * 10 + (CUR - '0');
6685 ok = 1;
6686 NEXT;
Owen Taylor3473f882001-02-23 17:55:21 +00006687 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006688 ret = (double) tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00006689 if (CUR == '.') {
6690 NEXT;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006691 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
6692 XP_ERROR(XPATH_NUMBER_ERROR);
6693 }
6694 while ((CUR >= '0') && (CUR <= '9')) {
6695 mult /= 10;
6696 ret = ret + (CUR - '0') * mult;
6697 NEXT;
6698 }
Owen Taylor3473f882001-02-23 17:55:21 +00006699 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006700 if ((CUR == 'e') || (CUR == 'E')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006701 NEXT;
6702 if (CUR == '-') {
6703 is_exponent_negative = 1;
6704 NEXT;
6705 }
6706 while ((CUR >= '0') && (CUR <= '9')) {
6707 exponent = exponent * 10 + (CUR - '0');
6708 NEXT;
6709 }
6710 if (is_exponent_negative)
6711 exponent = -exponent;
6712 ret *= pow(10.0, (double) exponent);
Bjorn Reese70a9da52001-04-21 16:57:29 +00006713 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006714 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006715 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006716}
6717
6718/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006719 * xmlXPathParseLiteral:
6720 * @ctxt: the XPath Parser context
6721 *
6722 * Parse a Literal
6723 *
6724 * [29] Literal ::= '"' [^"]* '"'
6725 * | "'" [^']* "'"
6726 *
6727 * Returns the value found or NULL in case of error
6728 */
6729static xmlChar *
6730xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
6731 const xmlChar *q;
6732 xmlChar *ret = NULL;
6733
6734 if (CUR == '"') {
6735 NEXT;
6736 q = CUR_PTR;
6737 while ((IS_CHAR(CUR)) && (CUR != '"'))
6738 NEXT;
6739 if (!IS_CHAR(CUR)) {
6740 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6741 } else {
6742 ret = xmlStrndup(q, CUR_PTR - q);
6743 NEXT;
6744 }
6745 } else if (CUR == '\'') {
6746 NEXT;
6747 q = CUR_PTR;
6748 while ((IS_CHAR(CUR)) && (CUR != '\''))
6749 NEXT;
6750 if (!IS_CHAR(CUR)) {
6751 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6752 } else {
6753 ret = xmlStrndup(q, CUR_PTR - q);
6754 NEXT;
6755 }
6756 } else {
6757 XP_ERROR0(XPATH_START_LITERAL_ERROR);
6758 }
6759 return(ret);
6760}
6761
6762/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006763 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00006764 * @ctxt: the XPath Parser context
6765 *
6766 * Parse a Literal and push it on the stack.
6767 *
6768 * [29] Literal ::= '"' [^"]* '"'
6769 * | "'" [^']* "'"
6770 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006771 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00006772 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006773static void
6774xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006775 const xmlChar *q;
6776 xmlChar *ret = NULL;
6777
6778 if (CUR == '"') {
6779 NEXT;
6780 q = CUR_PTR;
6781 while ((IS_CHAR(CUR)) && (CUR != '"'))
6782 NEXT;
6783 if (!IS_CHAR(CUR)) {
6784 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6785 } else {
6786 ret = xmlStrndup(q, CUR_PTR - q);
6787 NEXT;
6788 }
6789 } else if (CUR == '\'') {
6790 NEXT;
6791 q = CUR_PTR;
6792 while ((IS_CHAR(CUR)) && (CUR != '\''))
6793 NEXT;
6794 if (!IS_CHAR(CUR)) {
6795 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6796 } else {
6797 ret = xmlStrndup(q, CUR_PTR - q);
6798 NEXT;
6799 }
6800 } else {
6801 XP_ERROR(XPATH_START_LITERAL_ERROR);
6802 }
6803 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006804 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
6805 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006806 xmlFree(ret);
6807}
6808
6809/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006810 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00006811 * @ctxt: the XPath Parser context
6812 *
6813 * Parse a VariableReference, evaluate it and push it on the stack.
6814 *
6815 * The variable bindings consist of a mapping from variable names
6816 * to variable values. The value of a variable is an object, which
6817 * of any of the types that are possible for the value of an expression,
6818 * and may also be of additional types not specified here.
6819 *
6820 * Early evaluation is possible since:
6821 * The variable bindings [...] used to evaluate a subexpression are
6822 * always the same as those used to evaluate the containing expression.
6823 *
6824 * [36] VariableReference ::= '$' QName
6825 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006826static void
6827xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006828 xmlChar *name;
6829 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006830
6831 SKIP_BLANKS;
6832 if (CUR != '$') {
6833 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6834 }
6835 NEXT;
6836 name = xmlXPathParseQName(ctxt, &prefix);
6837 if (name == NULL) {
6838 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6839 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006840 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006841 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
6842 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006843 SKIP_BLANKS;
6844}
6845
6846/**
6847 * xmlXPathIsNodeType:
6848 * @ctxt: the XPath Parser context
6849 * @name: a name string
6850 *
6851 * Is the name given a NodeType one.
6852 *
6853 * [38] NodeType ::= 'comment'
6854 * | 'text'
6855 * | 'processing-instruction'
6856 * | 'node'
6857 *
6858 * Returns 1 if true 0 otherwise
6859 */
6860int
6861xmlXPathIsNodeType(const xmlChar *name) {
6862 if (name == NULL)
6863 return(0);
6864
6865 if (xmlStrEqual(name, BAD_CAST "comment"))
6866 return(1);
6867 if (xmlStrEqual(name, BAD_CAST "text"))
6868 return(1);
6869 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6870 return(1);
6871 if (xmlStrEqual(name, BAD_CAST "node"))
6872 return(1);
6873 return(0);
6874}
6875
6876/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006877 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00006878 * @ctxt: the XPath Parser context
6879 *
6880 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
6881 * [17] Argument ::= Expr
6882 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006883 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00006884 * pushed on the stack
6885 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006886static void
6887xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006888 xmlChar *name;
6889 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006890 int nbargs = 0;
6891
6892 name = xmlXPathParseQName(ctxt, &prefix);
6893 if (name == NULL) {
6894 XP_ERROR(XPATH_EXPR_ERROR);
6895 }
6896 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006897#ifdef DEBUG_EXPR
6898 if (prefix == NULL)
6899 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
6900 name);
6901 else
6902 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
6903 prefix, name);
6904#endif
6905
Owen Taylor3473f882001-02-23 17:55:21 +00006906 if (CUR != '(') {
6907 XP_ERROR(XPATH_EXPR_ERROR);
6908 }
6909 NEXT;
6910 SKIP_BLANKS;
6911
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006912 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00006913 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006914 int op1 = ctxt->comp->last;
6915 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006916 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006917 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006918 nbargs++;
6919 if (CUR == ')') break;
6920 if (CUR != ',') {
6921 XP_ERROR(XPATH_EXPR_ERROR);
6922 }
6923 NEXT;
6924 SKIP_BLANKS;
6925 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006926 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
6927 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006928 NEXT;
6929 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006930}
6931
6932/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006933 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006934 * @ctxt: the XPath Parser context
6935 *
6936 * [15] PrimaryExpr ::= VariableReference
6937 * | '(' Expr ')'
6938 * | Literal
6939 * | Number
6940 * | FunctionCall
6941 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006942 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006943 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006944static void
6945xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006946 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006947 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006948 else if (CUR == '(') {
6949 NEXT;
6950 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006951 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006952 if (CUR != ')') {
6953 XP_ERROR(XPATH_EXPR_ERROR);
6954 }
6955 NEXT;
6956 SKIP_BLANKS;
6957 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006958 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006959 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006960 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006961 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006962 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006963 }
6964 SKIP_BLANKS;
6965}
6966
6967/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006968 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006969 * @ctxt: the XPath Parser context
6970 *
6971 * [20] FilterExpr ::= PrimaryExpr
6972 * | FilterExpr Predicate
6973 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006974 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006975 * Square brackets are used to filter expressions in the same way that
6976 * they are used in location paths. It is an error if the expression to
6977 * be filtered does not evaluate to a node-set. The context node list
6978 * used for evaluating the expression in square brackets is the node-set
6979 * to be filtered listed in document order.
6980 */
6981
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006982static void
6983xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
6984 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006985 CHECK_ERROR;
6986 SKIP_BLANKS;
6987
6988 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006989 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00006990 SKIP_BLANKS;
6991 }
6992
6993
6994}
6995
6996/**
6997 * xmlXPathScanName:
6998 * @ctxt: the XPath Parser context
6999 *
7000 * Trickery: parse an XML name but without consuming the input flow
7001 * Needed to avoid insanity in the parser state.
7002 *
7003 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
7004 * CombiningChar | Extender
7005 *
7006 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
7007 *
7008 * [6] Names ::= Name (S Name)*
7009 *
7010 * Returns the Name parsed or NULL
7011 */
7012
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007013static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00007014xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
7015 xmlChar buf[XML_MAX_NAMELEN];
7016 int len = 0;
7017
7018 SKIP_BLANKS;
7019 if (!IS_LETTER(CUR) && (CUR != '_') &&
7020 (CUR != ':')) {
7021 return(NULL);
7022 }
7023
7024 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
7025 (NXT(len) == '.') || (NXT(len) == '-') ||
7026 (NXT(len) == '_') || (NXT(len) == ':') ||
7027 (IS_COMBINING(NXT(len))) ||
7028 (IS_EXTENDER(NXT(len)))) {
7029 buf[len] = NXT(len);
7030 len++;
7031 if (len >= XML_MAX_NAMELEN) {
7032 xmlGenericError(xmlGenericErrorContext,
7033 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
7034 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
7035 (NXT(len) == '.') || (NXT(len) == '-') ||
7036 (NXT(len) == '_') || (NXT(len) == ':') ||
7037 (IS_COMBINING(NXT(len))) ||
7038 (IS_EXTENDER(NXT(len))))
7039 len++;
7040 break;
7041 }
7042 }
7043 return(xmlStrndup(buf, len));
7044}
7045
7046/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007047 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007048 * @ctxt: the XPath Parser context
7049 *
7050 * [19] PathExpr ::= LocationPath
7051 * | FilterExpr
7052 * | FilterExpr '/' RelativeLocationPath
7053 * | FilterExpr '//' RelativeLocationPath
7054 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007055 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007056 * The / operator and // operators combine an arbitrary expression
7057 * and a relative location path. It is an error if the expression
7058 * does not evaluate to a node-set.
7059 * The / operator does composition in the same way as when / is
7060 * used in a location path. As in location paths, // is short for
7061 * /descendant-or-self::node()/.
7062 */
7063
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007064static void
7065xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007066 int lc = 1; /* Should we branch to LocationPath ? */
7067 xmlChar *name = NULL; /* we may have to preparse a name to find out */
7068
7069 SKIP_BLANKS;
7070 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
7071 (CUR == '\'') || (CUR == '"')) {
7072 lc = 0;
7073 } else if (CUR == '*') {
7074 /* relative or absolute location path */
7075 lc = 1;
7076 } else if (CUR == '/') {
7077 /* relative or absolute location path */
7078 lc = 1;
7079 } else if (CUR == '@') {
7080 /* relative abbreviated attribute location path */
7081 lc = 1;
7082 } else if (CUR == '.') {
7083 /* relative abbreviated attribute location path */
7084 lc = 1;
7085 } else {
7086 /*
7087 * Problem is finding if we have a name here whether it's:
7088 * - a nodetype
7089 * - a function call in which case it's followed by '('
7090 * - an axis in which case it's followed by ':'
7091 * - a element name
7092 * We do an a priori analysis here rather than having to
7093 * maintain parsed token content through the recursive function
7094 * calls. This looks uglier but makes the code quite easier to
7095 * read/write/debug.
7096 */
7097 SKIP_BLANKS;
7098 name = xmlXPathScanName(ctxt);
7099 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
7100#ifdef DEBUG_STEP
7101 xmlGenericError(xmlGenericErrorContext,
7102 "PathExpr: Axis\n");
7103#endif
7104 lc = 1;
7105 xmlFree(name);
7106 } else if (name != NULL) {
7107 int len =xmlStrlen(name);
7108 int blank = 0;
7109
7110
7111 while (NXT(len) != 0) {
7112 if (NXT(len) == '/') {
7113 /* element name */
7114#ifdef DEBUG_STEP
7115 xmlGenericError(xmlGenericErrorContext,
7116 "PathExpr: AbbrRelLocation\n");
7117#endif
7118 lc = 1;
7119 break;
7120 } else if (IS_BLANK(NXT(len))) {
7121 /* skip to next */
7122 blank = 1;
7123 } else if (NXT(len) == ':') {
7124#ifdef DEBUG_STEP
7125 xmlGenericError(xmlGenericErrorContext,
7126 "PathExpr: AbbrRelLocation\n");
7127#endif
7128 lc = 1;
7129 break;
7130 } else if ((NXT(len) == '(')) {
7131 /* Note Type or Function */
7132 if (xmlXPathIsNodeType(name)) {
7133#ifdef DEBUG_STEP
7134 xmlGenericError(xmlGenericErrorContext,
7135 "PathExpr: Type search\n");
7136#endif
7137 lc = 1;
7138 } else {
7139#ifdef DEBUG_STEP
7140 xmlGenericError(xmlGenericErrorContext,
7141 "PathExpr: function call\n");
7142#endif
7143 lc = 0;
7144 }
7145 break;
7146 } else if ((NXT(len) == '[')) {
7147 /* element name */
7148#ifdef DEBUG_STEP
7149 xmlGenericError(xmlGenericErrorContext,
7150 "PathExpr: AbbrRelLocation\n");
7151#endif
7152 lc = 1;
7153 break;
7154 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
7155 (NXT(len) == '=')) {
7156 lc = 1;
7157 break;
7158 } else {
7159 lc = 1;
7160 break;
7161 }
7162 len++;
7163 }
7164 if (NXT(len) == 0) {
7165#ifdef DEBUG_STEP
7166 xmlGenericError(xmlGenericErrorContext,
7167 "PathExpr: AbbrRelLocation\n");
7168#endif
7169 /* element name */
7170 lc = 1;
7171 }
7172 xmlFree(name);
7173 } else {
7174 /* make sure all cases are covered explicitely */
7175 XP_ERROR(XPATH_EXPR_ERROR);
7176 }
7177 }
7178
7179 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007180 if (CUR == '/') {
7181 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
7182 } else {
7183 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007184 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007185 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007186 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007187 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007188 CHECK_ERROR;
7189 if ((CUR == '/') && (NXT(1) == '/')) {
7190 SKIP(2);
7191 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007192
7193 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7194 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
7195 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
7196
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007197 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007198 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007199 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007200 }
7201 }
7202 SKIP_BLANKS;
7203}
7204
7205/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007206 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007207 * @ctxt: the XPath Parser context
7208 *
7209 * [18] UnionExpr ::= PathExpr
7210 * | UnionExpr '|' PathExpr
7211 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007212 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007213 */
7214
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007215static void
7216xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
7217 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007218 CHECK_ERROR;
7219 SKIP_BLANKS;
7220 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007221 int op1 = ctxt->comp->last;
7222 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007223
7224 NEXT;
7225 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007226 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007227
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007228 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
7229
Owen Taylor3473f882001-02-23 17:55:21 +00007230 SKIP_BLANKS;
7231 }
Owen Taylor3473f882001-02-23 17:55:21 +00007232}
7233
7234/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007235 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007236 * @ctxt: the XPath Parser context
7237 *
7238 * [27] UnaryExpr ::= UnionExpr
7239 * | '-' UnaryExpr
7240 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007241 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007242 */
7243
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007244static void
7245xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007246 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007247 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007248
7249 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00007250 while (CUR == '-') {
7251 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007252 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007253 NEXT;
7254 SKIP_BLANKS;
7255 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007256
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007257 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007258 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007259 if (found) {
7260 if (minus)
7261 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
7262 else
7263 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007264 }
7265}
7266
7267/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007268 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007269 * @ctxt: the XPath Parser context
7270 *
7271 * [26] MultiplicativeExpr ::= UnaryExpr
7272 * | MultiplicativeExpr MultiplyOperator UnaryExpr
7273 * | MultiplicativeExpr 'div' UnaryExpr
7274 * | MultiplicativeExpr 'mod' UnaryExpr
7275 * [34] MultiplyOperator ::= '*'
7276 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007277 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007278 */
7279
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007280static void
7281xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
7282 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007283 CHECK_ERROR;
7284 SKIP_BLANKS;
7285 while ((CUR == '*') ||
7286 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
7287 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
7288 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007289 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007290
7291 if (CUR == '*') {
7292 op = 0;
7293 NEXT;
7294 } else if (CUR == 'd') {
7295 op = 1;
7296 SKIP(3);
7297 } else if (CUR == 'm') {
7298 op = 2;
7299 SKIP(3);
7300 }
7301 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007302 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007303 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007304 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007305 SKIP_BLANKS;
7306 }
7307}
7308
7309/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007310 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007311 * @ctxt: the XPath Parser context
7312 *
7313 * [25] AdditiveExpr ::= MultiplicativeExpr
7314 * | AdditiveExpr '+' MultiplicativeExpr
7315 * | AdditiveExpr '-' MultiplicativeExpr
7316 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007317 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007318 */
7319
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007320static void
7321xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007322
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007323 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007324 CHECK_ERROR;
7325 SKIP_BLANKS;
7326 while ((CUR == '+') || (CUR == '-')) {
7327 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007328 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007329
7330 if (CUR == '+') plus = 1;
7331 else plus = 0;
7332 NEXT;
7333 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007334 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007335 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007336 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007337 SKIP_BLANKS;
7338 }
7339}
7340
7341/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007342 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007343 * @ctxt: the XPath Parser context
7344 *
7345 * [24] RelationalExpr ::= AdditiveExpr
7346 * | RelationalExpr '<' AdditiveExpr
7347 * | RelationalExpr '>' AdditiveExpr
7348 * | RelationalExpr '<=' AdditiveExpr
7349 * | RelationalExpr '>=' AdditiveExpr
7350 *
7351 * A <= B > C is allowed ? Answer from James, yes with
7352 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
7353 * which is basically what got implemented.
7354 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007355 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00007356 * on the stack
7357 */
7358
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007359static void
7360xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
7361 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007362 CHECK_ERROR;
7363 SKIP_BLANKS;
7364 while ((CUR == '<') ||
7365 (CUR == '>') ||
7366 ((CUR == '<') && (NXT(1) == '=')) ||
7367 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007368 int inf, strict;
7369 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007370
7371 if (CUR == '<') inf = 1;
7372 else inf = 0;
7373 if (NXT(1) == '=') strict = 0;
7374 else strict = 1;
7375 NEXT;
7376 if (!strict) NEXT;
7377 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007378 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007379 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007380 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00007381 SKIP_BLANKS;
7382 }
7383}
7384
7385/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007386 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007387 * @ctxt: the XPath Parser context
7388 *
7389 * [23] EqualityExpr ::= RelationalExpr
7390 * | EqualityExpr '=' RelationalExpr
7391 * | EqualityExpr '!=' RelationalExpr
7392 *
7393 * A != B != C is allowed ? Answer from James, yes with
7394 * (RelationalExpr = RelationalExpr) = RelationalExpr
7395 * (RelationalExpr != RelationalExpr) != RelationalExpr
7396 * which is basically what got implemented.
7397 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007398 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007399 *
7400 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007401static void
7402xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
7403 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007404 CHECK_ERROR;
7405 SKIP_BLANKS;
7406 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007407 int eq;
7408 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007409
7410 if (CUR == '=') eq = 1;
7411 else eq = 0;
7412 NEXT;
7413 if (!eq) NEXT;
7414 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007415 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007416 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007417 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007418 SKIP_BLANKS;
7419 }
7420}
7421
7422/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007423 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007424 * @ctxt: the XPath Parser context
7425 *
7426 * [22] AndExpr ::= EqualityExpr
7427 * | AndExpr 'and' EqualityExpr
7428 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007429 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007430 *
7431 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007432static void
7433xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
7434 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007435 CHECK_ERROR;
7436 SKIP_BLANKS;
7437 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007438 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007439 SKIP(3);
7440 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007441 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007442 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007443 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007444 SKIP_BLANKS;
7445 }
7446}
7447
7448/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007449 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007450 * @ctxt: the XPath Parser context
7451 *
7452 * [14] Expr ::= OrExpr
7453 * [21] OrExpr ::= AndExpr
7454 * | OrExpr 'or' AndExpr
7455 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007456 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00007457 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007458static void
7459xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
7460 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007461 CHECK_ERROR;
7462 SKIP_BLANKS;
7463 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007464 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007465 SKIP(2);
7466 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007467 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007468 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007469 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
7470 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00007471 SKIP_BLANKS;
7472 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007473 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
7474 /* more ops could be optimized too */
7475 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
7476 }
Owen Taylor3473f882001-02-23 17:55:21 +00007477}
7478
7479/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007480 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00007481 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007482 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00007483 *
7484 * [8] Predicate ::= '[' PredicateExpr ']'
7485 * [9] PredicateExpr ::= Expr
7486 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007487 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00007488 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007489static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007490xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007491 int op1 = ctxt->comp->last;
7492
7493 SKIP_BLANKS;
7494 if (CUR != '[') {
7495 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7496 }
7497 NEXT;
7498 SKIP_BLANKS;
7499
7500 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007501 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007502 CHECK_ERROR;
7503
7504 if (CUR != ']') {
7505 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7506 }
7507
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007508 if (filter)
7509 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
7510 else
7511 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007512
7513 NEXT;
7514 SKIP_BLANKS;
7515}
7516
7517/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007518 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00007519 * @ctxt: the XPath Parser context
7520 * @test: pointer to a xmlXPathTestVal
7521 * @type: pointer to a xmlXPathTypeVal
7522 * @prefix: placeholder for a possible name prefix
7523 *
7524 * [7] NodeTest ::= NameTest
7525 * | NodeType '(' ')'
7526 * | 'processing-instruction' '(' Literal ')'
7527 *
7528 * [37] NameTest ::= '*'
7529 * | NCName ':' '*'
7530 * | QName
7531 * [38] NodeType ::= 'comment'
7532 * | 'text'
7533 * | 'processing-instruction'
7534 * | 'node'
7535 *
7536 * Returns the name found and update @test, @type and @prefix appropriately
7537 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007538static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007539xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
7540 xmlXPathTypeVal *type, const xmlChar **prefix,
7541 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00007542 int blanks;
7543
7544 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
7545 STRANGE;
7546 return(NULL);
7547 }
7548 *type = 0;
7549 *test = 0;
7550 *prefix = NULL;
7551 SKIP_BLANKS;
7552
7553 if ((name == NULL) && (CUR == '*')) {
7554 /*
7555 * All elements
7556 */
7557 NEXT;
7558 *test = NODE_TEST_ALL;
7559 return(NULL);
7560 }
7561
7562 if (name == NULL)
7563 name = xmlXPathParseNCName(ctxt);
7564 if (name == NULL) {
7565 XP_ERROR0(XPATH_EXPR_ERROR);
7566 }
7567
7568 blanks = IS_BLANK(CUR);
7569 SKIP_BLANKS;
7570 if (CUR == '(') {
7571 NEXT;
7572 /*
7573 * NodeType or PI search
7574 */
7575 if (xmlStrEqual(name, BAD_CAST "comment"))
7576 *type = NODE_TYPE_COMMENT;
7577 else if (xmlStrEqual(name, BAD_CAST "node"))
7578 *type = NODE_TYPE_NODE;
7579 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
7580 *type = NODE_TYPE_PI;
7581 else if (xmlStrEqual(name, BAD_CAST "text"))
7582 *type = NODE_TYPE_TEXT;
7583 else {
7584 if (name != NULL)
7585 xmlFree(name);
7586 XP_ERROR0(XPATH_EXPR_ERROR);
7587 }
7588
7589 *test = NODE_TEST_TYPE;
7590
7591 SKIP_BLANKS;
7592 if (*type == NODE_TYPE_PI) {
7593 /*
7594 * Specific case: search a PI by name.
7595 */
Owen Taylor3473f882001-02-23 17:55:21 +00007596 if (name != NULL)
7597 xmlFree(name);
Daniel Veillard82e49712001-04-26 14:38:03 +00007598 name = NULL;
7599 if (CUR != ')') {
7600 name = xmlXPathParseLiteral(ctxt);
7601 CHECK_ERROR 0;
7602 SKIP_BLANKS;
7603 }
Owen Taylor3473f882001-02-23 17:55:21 +00007604 }
7605 if (CUR != ')') {
7606 if (name != NULL)
7607 xmlFree(name);
7608 XP_ERROR0(XPATH_UNCLOSED_ERROR);
7609 }
7610 NEXT;
7611 return(name);
7612 }
7613 *test = NODE_TEST_NAME;
7614 if ((!blanks) && (CUR == ':')) {
7615 NEXT;
7616
7617 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007618 * Since currently the parser context don't have a
7619 * namespace list associated:
7620 * The namespace name for this prefix can be computed
7621 * only at evaluation time. The compilation is done
7622 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00007623 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007624#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00007625 *prefix = xmlXPathNsLookup(ctxt->context, name);
7626 if (name != NULL)
7627 xmlFree(name);
7628 if (*prefix == NULL) {
7629 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
7630 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007631#else
7632 *prefix = name;
7633#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007634
7635 if (CUR == '*') {
7636 /*
7637 * All elements
7638 */
7639 NEXT;
7640 *test = NODE_TEST_ALL;
7641 return(NULL);
7642 }
7643
7644 name = xmlXPathParseNCName(ctxt);
7645 if (name == NULL) {
7646 XP_ERROR0(XPATH_EXPR_ERROR);
7647 }
7648 }
7649 return(name);
7650}
7651
7652/**
7653 * xmlXPathIsAxisName:
7654 * @name: a preparsed name token
7655 *
7656 * [6] AxisName ::= 'ancestor'
7657 * | 'ancestor-or-self'
7658 * | 'attribute'
7659 * | 'child'
7660 * | 'descendant'
7661 * | 'descendant-or-self'
7662 * | 'following'
7663 * | 'following-sibling'
7664 * | 'namespace'
7665 * | 'parent'
7666 * | 'preceding'
7667 * | 'preceding-sibling'
7668 * | 'self'
7669 *
7670 * Returns the axis or 0
7671 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007672static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00007673xmlXPathIsAxisName(const xmlChar *name) {
7674 xmlXPathAxisVal ret = 0;
7675 switch (name[0]) {
7676 case 'a':
7677 if (xmlStrEqual(name, BAD_CAST "ancestor"))
7678 ret = AXIS_ANCESTOR;
7679 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
7680 ret = AXIS_ANCESTOR_OR_SELF;
7681 if (xmlStrEqual(name, BAD_CAST "attribute"))
7682 ret = AXIS_ATTRIBUTE;
7683 break;
7684 case 'c':
7685 if (xmlStrEqual(name, BAD_CAST "child"))
7686 ret = AXIS_CHILD;
7687 break;
7688 case 'd':
7689 if (xmlStrEqual(name, BAD_CAST "descendant"))
7690 ret = AXIS_DESCENDANT;
7691 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
7692 ret = AXIS_DESCENDANT_OR_SELF;
7693 break;
7694 case 'f':
7695 if (xmlStrEqual(name, BAD_CAST "following"))
7696 ret = AXIS_FOLLOWING;
7697 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
7698 ret = AXIS_FOLLOWING_SIBLING;
7699 break;
7700 case 'n':
7701 if (xmlStrEqual(name, BAD_CAST "namespace"))
7702 ret = AXIS_NAMESPACE;
7703 break;
7704 case 'p':
7705 if (xmlStrEqual(name, BAD_CAST "parent"))
7706 ret = AXIS_PARENT;
7707 if (xmlStrEqual(name, BAD_CAST "preceding"))
7708 ret = AXIS_PRECEDING;
7709 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
7710 ret = AXIS_PRECEDING_SIBLING;
7711 break;
7712 case 's':
7713 if (xmlStrEqual(name, BAD_CAST "self"))
7714 ret = AXIS_SELF;
7715 break;
7716 }
7717 return(ret);
7718}
7719
7720/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007721 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00007722 * @ctxt: the XPath Parser context
7723 *
7724 * [4] Step ::= AxisSpecifier NodeTest Predicate*
7725 * | AbbreviatedStep
7726 *
7727 * [12] AbbreviatedStep ::= '.' | '..'
7728 *
7729 * [5] AxisSpecifier ::= AxisName '::'
7730 * | AbbreviatedAxisSpecifier
7731 *
7732 * [13] AbbreviatedAxisSpecifier ::= '@'?
7733 *
7734 * Modified for XPtr range support as:
7735 *
7736 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
7737 * | AbbreviatedStep
7738 * | 'range-to' '(' Expr ')' Predicate*
7739 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007740 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00007741 * A location step of . is short for self::node(). This is
7742 * particularly useful in conjunction with //. For example, the
7743 * location path .//para is short for
7744 * self::node()/descendant-or-self::node()/child::para
7745 * and so will select all para descendant elements of the context
7746 * node.
7747 * Similarly, a location step of .. is short for parent::node().
7748 * For example, ../title is short for parent::node()/child::title
7749 * and so will select the title children of the parent of the context
7750 * node.
7751 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007752static void
7753xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007754#ifdef LIBXML_XPTR_ENABLED
7755 int rangeto = 0;
7756 int op2 = -1;
7757#endif
7758
Owen Taylor3473f882001-02-23 17:55:21 +00007759 SKIP_BLANKS;
7760 if ((CUR == '.') && (NXT(1) == '.')) {
7761 SKIP(2);
7762 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007763 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
7764 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007765 } else if (CUR == '.') {
7766 NEXT;
7767 SKIP_BLANKS;
7768 } else {
7769 xmlChar *name = NULL;
7770 const xmlChar *prefix = NULL;
7771 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007772 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007773 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007774 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00007775
7776 /*
7777 * The modification needed for XPointer change to the production
7778 */
7779#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007780 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00007781 name = xmlXPathParseNCName(ctxt);
7782 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007783 op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007784 xmlFree(name);
7785 SKIP_BLANKS;
7786 if (CUR != '(') {
7787 XP_ERROR(XPATH_EXPR_ERROR);
7788 }
7789 NEXT;
7790 SKIP_BLANKS;
7791
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007792 xmlXPathCompileExpr(ctxt);
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007793 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
Owen Taylor3473f882001-02-23 17:55:21 +00007794 CHECK_ERROR;
7795
7796 SKIP_BLANKS;
7797 if (CUR != ')') {
7798 XP_ERROR(XPATH_EXPR_ERROR);
7799 }
7800 NEXT;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007801 rangeto = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007802 goto eval_predicates;
7803 }
7804 }
7805#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00007806 if (CUR == '*') {
7807 axis = AXIS_CHILD;
7808 } else {
7809 if (name == NULL)
7810 name = xmlXPathParseNCName(ctxt);
7811 if (name != NULL) {
7812 axis = xmlXPathIsAxisName(name);
7813 if (axis != 0) {
7814 SKIP_BLANKS;
7815 if ((CUR == ':') && (NXT(1) == ':')) {
7816 SKIP(2);
7817 xmlFree(name);
7818 name = NULL;
7819 } else {
7820 /* an element name can conflict with an axis one :-\ */
7821 axis = AXIS_CHILD;
7822 }
Owen Taylor3473f882001-02-23 17:55:21 +00007823 } else {
Owen Taylor3473f882001-02-23 17:55:21 +00007824 axis = AXIS_CHILD;
7825 }
Daniel Veillard2156a562001-04-28 12:24:34 +00007826 } else if (CUR == '@') {
7827 NEXT;
7828 axis = AXIS_ATTRIBUTE;
Owen Taylor3473f882001-02-23 17:55:21 +00007829 } else {
Daniel Veillard2156a562001-04-28 12:24:34 +00007830 axis = AXIS_CHILD;
Owen Taylor3473f882001-02-23 17:55:21 +00007831 }
Owen Taylor3473f882001-02-23 17:55:21 +00007832 }
7833
7834 CHECK_ERROR;
7835
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007836 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00007837 if (test == 0)
7838 return;
7839
7840#ifdef DEBUG_STEP
7841 xmlGenericError(xmlGenericErrorContext,
7842 "Basis : computing new set\n");
7843#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007844
Owen Taylor3473f882001-02-23 17:55:21 +00007845#ifdef DEBUG_STEP
7846 xmlGenericError(xmlGenericErrorContext, "Basis : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007847 if (ctxt->value == NULL)
7848 xmlGenericError(xmlGenericErrorContext, "no value\n");
7849 else if (ctxt->value->nodesetval == NULL)
7850 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7851 else
7852 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007853#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007854
7855eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007856 op1 = ctxt->comp->last;
7857 ctxt->comp->last = -1;
7858
Owen Taylor3473f882001-02-23 17:55:21 +00007859 SKIP_BLANKS;
7860 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007861 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007862 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007863
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007864#ifdef LIBXML_XPTR_ENABLED
7865 if (rangeto) {
7866 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
7867 } else
7868#endif
7869 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
7870 test, type, (void *)prefix, (void *)name);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007871
Owen Taylor3473f882001-02-23 17:55:21 +00007872 }
7873#ifdef DEBUG_STEP
7874 xmlGenericError(xmlGenericErrorContext, "Step : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007875 if (ctxt->value == NULL)
7876 xmlGenericError(xmlGenericErrorContext, "no value\n");
7877 else if (ctxt->value->nodesetval == NULL)
7878 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7879 else
7880 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
7881 ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007882#endif
7883}
7884
7885/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007886 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007887 * @ctxt: the XPath Parser context
7888 *
7889 * [3] RelativeLocationPath ::= Step
7890 * | RelativeLocationPath '/' Step
7891 * | AbbreviatedRelativeLocationPath
7892 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
7893 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007894 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00007895 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007896static void
Owen Taylor3473f882001-02-23 17:55:21 +00007897#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007898xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007899#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007900xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007901#endif
7902(xmlXPathParserContextPtr ctxt) {
7903 SKIP_BLANKS;
7904 if ((CUR == '/') && (NXT(1) == '/')) {
7905 SKIP(2);
7906 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007907 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7908 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007909 } else if (CUR == '/') {
7910 NEXT;
7911 SKIP_BLANKS;
7912 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007913 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007914 SKIP_BLANKS;
7915 while (CUR == '/') {
7916 if ((CUR == '/') && (NXT(1) == '/')) {
7917 SKIP(2);
7918 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007919 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00007920 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007921 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007922 } else if (CUR == '/') {
7923 NEXT;
7924 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007925 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007926 }
7927 SKIP_BLANKS;
7928 }
7929}
7930
7931/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007932 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007933 * @ctxt: the XPath Parser context
7934 *
7935 * [1] LocationPath ::= RelativeLocationPath
7936 * | AbsoluteLocationPath
7937 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
7938 * | AbbreviatedAbsoluteLocationPath
7939 * [10] AbbreviatedAbsoluteLocationPath ::=
7940 * '//' RelativeLocationPath
7941 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007942 * Compile a location path
7943 *
Owen Taylor3473f882001-02-23 17:55:21 +00007944 * // is short for /descendant-or-self::node()/. For example,
7945 * //para is short for /descendant-or-self::node()/child::para and
7946 * so will select any para element in the document (even a para element
7947 * that is a document element will be selected by //para since the
7948 * document element node is a child of the root node); div//para is
7949 * short for div/descendant-or-self::node()/child::para and so will
7950 * select all para descendants of div children.
7951 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007952static void
7953xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007954 SKIP_BLANKS;
7955 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007956 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007957 } else {
7958 while (CUR == '/') {
7959 if ((CUR == '/') && (NXT(1) == '/')) {
7960 SKIP(2);
7961 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007962 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7963 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007964 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007965 } else if (CUR == '/') {
7966 NEXT;
Daniel Veillard608ad072001-06-14 08:32:28 +00007967 SKIP_BLANKS;
7968 if ((CUR != 0 ) &&
7969 ((IS_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
7970 (CUR == '@') || (CUR == '*')))
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007971 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007972 }
7973 }
7974 }
7975}
7976
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007977/************************************************************************
7978 * *
7979 * XPath precompiled expression evaluation *
7980 * *
7981 ************************************************************************/
7982
Daniel Veillardf06307e2001-07-03 10:35:50 +00007983static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007984xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
7985
7986/**
7987 * xmlXPathNodeCollectAndTest:
7988 * @ctxt: the XPath Parser context
7989 * @op: the XPath precompiled step operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00007990 * @first: pointer to the first element in document order
7991 * @last: pointer to the last element in document order
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007992 *
7993 * This is the function implementing a step: based on the current list
7994 * of nodes, it builds up a new list, looking at all nodes under that
7995 * axis and selecting them it also do the predicate filtering
7996 *
7997 * Pushes the new NodeSet resulting from the search.
Daniel Veillardf06307e2001-07-03 10:35:50 +00007998 *
7999 * Returns the number of node traversed
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008000 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00008001static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008002xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008003 xmlXPathStepOpPtr op,
8004 xmlNodePtr * first, xmlNodePtr * last)
8005{
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008006 xmlXPathAxisVal axis = op->value;
8007 xmlXPathTestVal test = op->value2;
8008 xmlXPathTypeVal type = op->value3;
8009 const xmlChar *prefix = op->value4;
8010 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00008011 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008012
8013#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008014 int n = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008015#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008016 int i, t = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008017 xmlNodeSetPtr ret, list;
8018 xmlXPathTraversalFunction next = NULL;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008019 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008020 xmlNodePtr cur = NULL;
8021 xmlXPathObjectPtr obj;
8022 xmlNodeSetPtr nodelist;
8023 xmlNodePtr tmp;
8024
Daniel Veillardf06307e2001-07-03 10:35:50 +00008025 CHECK_TYPE0(XPATH_NODESET);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008026 obj = valuePop(ctxt);
8027 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00008028 if (prefix != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008029 URI = xmlXPathNsLookup(ctxt->context, prefix);
8030 if (URI == NULL)
8031 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
Daniel Veillarde043ee12001-04-16 14:08:07 +00008032 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008033#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008034 xmlGenericError(xmlGenericErrorContext, "new step : ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008035#endif
8036 switch (axis) {
8037 case AXIS_ANCESTOR:
8038#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008039 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008040#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008041 first = NULL;
8042 next = xmlXPathNextAncestor;
8043 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008044 case AXIS_ANCESTOR_OR_SELF:
8045#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008046 xmlGenericError(xmlGenericErrorContext,
8047 "axis 'ancestors-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008048#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008049 first = NULL;
8050 next = xmlXPathNextAncestorOrSelf;
8051 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008052 case AXIS_ATTRIBUTE:
8053#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008054 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008055#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008056 first = NULL;
8057 last = NULL;
8058 next = xmlXPathNextAttribute;
8059 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008060 case AXIS_CHILD:
8061#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008062 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008063#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008064 last = NULL;
8065 next = xmlXPathNextChild;
8066 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008067 case AXIS_DESCENDANT:
8068#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008069 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008070#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008071 last = NULL;
8072 next = xmlXPathNextDescendant;
8073 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008074 case AXIS_DESCENDANT_OR_SELF:
8075#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008076 xmlGenericError(xmlGenericErrorContext,
8077 "axis 'descendant-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008078#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008079 last = NULL;
8080 next = xmlXPathNextDescendantOrSelf;
8081 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008082 case AXIS_FOLLOWING:
8083#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008084 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008085#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008086 last = NULL;
8087 next = xmlXPathNextFollowing;
8088 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008089 case AXIS_FOLLOWING_SIBLING:
8090#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008091 xmlGenericError(xmlGenericErrorContext,
8092 "axis 'following-siblings' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008093#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008094 last = NULL;
8095 next = xmlXPathNextFollowingSibling;
8096 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008097 case AXIS_NAMESPACE:
8098#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008099 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008100#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008101 first = NULL;
8102 last = NULL;
8103 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8104 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008105 case AXIS_PARENT:
8106#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008107 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008108#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008109 first = NULL;
8110 next = xmlXPathNextParent;
8111 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008112 case AXIS_PRECEDING:
8113#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008114 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008115#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008116 first = NULL;
8117 next = xmlXPathNextPrecedingInternal;
8118 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008119 case AXIS_PRECEDING_SIBLING:
8120#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008121 xmlGenericError(xmlGenericErrorContext,
8122 "axis 'preceding-sibling' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008123#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008124 first = NULL;
8125 next = xmlXPathNextPrecedingSibling;
8126 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008127 case AXIS_SELF:
8128#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008129 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008130#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008131 first = NULL;
8132 last = NULL;
8133 next = xmlXPathNextSelf;
8134 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008135 }
8136 if (next == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00008137 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008138
8139 nodelist = obj->nodesetval;
8140 if (nodelist == NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008141 xmlXPathFreeObject(obj);
8142 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8143 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008144 }
8145 addNode = xmlXPathNodeSetAddUnique;
8146 ret = NULL;
8147#ifdef DEBUG_STEP
8148 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008149 " context contains %d nodes\n", nodelist->nodeNr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008150 switch (test) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008151 case NODE_TEST_NONE:
8152 xmlGenericError(xmlGenericErrorContext,
8153 " searching for none !!!\n");
8154 break;
8155 case NODE_TEST_TYPE:
8156 xmlGenericError(xmlGenericErrorContext,
8157 " searching for type %d\n", type);
8158 break;
8159 case NODE_TEST_PI:
8160 xmlGenericError(xmlGenericErrorContext,
8161 " searching for PI !!!\n");
8162 break;
8163 case NODE_TEST_ALL:
8164 xmlGenericError(xmlGenericErrorContext,
8165 " searching for *\n");
8166 break;
8167 case NODE_TEST_NS:
8168 xmlGenericError(xmlGenericErrorContext,
8169 " searching for namespace %s\n",
8170 prefix);
8171 break;
8172 case NODE_TEST_NAME:
8173 xmlGenericError(xmlGenericErrorContext,
8174 " searching for name %s\n", name);
8175 if (prefix != NULL)
8176 xmlGenericError(xmlGenericErrorContext,
8177 " with namespace %s\n", prefix);
8178 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008179 }
8180 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8181#endif
8182 /*
8183 * 2.3 Node Tests
8184 * - For the attribute axis, the principal node type is attribute.
8185 * - For the namespace axis, the principal node type is namespace.
8186 * - For other axes, the principal node type is element.
8187 *
8188 * A node test * is true for any node of the
8189 * principal node type. For example, child::* willi
8190 * select all element children of the context node
8191 */
8192 tmp = ctxt->context->node;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008193 for (i = 0; i < nodelist->nodeNr; i++) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008194 ctxt->context->node = nodelist->nodeTab[i];
8195
Daniel Veillardf06307e2001-07-03 10:35:50 +00008196 cur = NULL;
8197 list = xmlXPathNodeSetCreate(NULL);
8198 do {
8199 cur = next(ctxt, cur);
8200 if (cur == NULL)
8201 break;
8202 if ((first != NULL) && (*first == cur))
8203 break;
8204 if (((t % 256) == 0) &&
8205 (first != NULL) && (*first != NULL) &&
8206 (xmlXPathCmpNodes(*first, cur) >= 0))
8207 break;
8208 if ((last != NULL) && (*last == cur))
8209 break;
8210 if (((t % 256) == 0) &&
8211 (last != NULL) && (*last != NULL) &&
8212 (xmlXPathCmpNodes(cur, *last) >= 0))
8213 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008214 t++;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008215#ifdef DEBUG_STEP
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008216 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
8217#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008218 switch (test) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008219 case NODE_TEST_NONE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008220 ctxt->context->node = tmp;
8221 STRANGE return(t);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008222 case NODE_TEST_TYPE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008223 if ((cur->type == type) ||
8224 ((type == NODE_TYPE_NODE) &&
8225 ((cur->type == XML_DOCUMENT_NODE) ||
8226 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8227 (cur->type == XML_ELEMENT_NODE) ||
8228 (cur->type == XML_PI_NODE) ||
8229 (cur->type == XML_COMMENT_NODE) ||
8230 (cur->type == XML_CDATA_SECTION_NODE) ||
Daniel Veillard7583a592001-07-08 13:15:55 +00008231 (cur->type == XML_TEXT_NODE))) ||
8232 ((type == NODE_TYPE_TEXT) &&
8233 (cur->type == XML_CDATA_SECTION_NODE))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008234#ifdef DEBUG_STEP
8235 n++;
8236#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008237 addNode(list, cur);
8238 }
8239 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008240 case NODE_TEST_PI:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008241 if (cur->type == XML_PI_NODE) {
8242 if ((name != NULL) &&
8243 (!xmlStrEqual(name, cur->name)))
8244 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008245#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008246 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008247#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008248 addNode(list, cur);
8249 }
8250 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008251 case NODE_TEST_ALL:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008252 if (axis == AXIS_ATTRIBUTE) {
8253 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008254#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008255 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008256#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008257 addNode(list, cur);
8258 }
8259 } else if (axis == AXIS_NAMESPACE) {
8260 if (cur->type == XML_NAMESPACE_DECL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008261#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008262 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008263#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008264 addNode(list, cur);
8265 }
8266 } else {
8267 if (cur->type == XML_ELEMENT_NODE) {
8268 if (prefix == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008269#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008270 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008271#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008272 addNode(list, cur);
8273 } else if ((cur->ns != NULL) &&
8274 (xmlStrEqual(URI, cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008275#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008276 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008277#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008278 addNode(list, cur);
8279 }
8280 }
8281 }
8282 break;
8283 case NODE_TEST_NS:{
8284 TODO;
8285 break;
8286 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008287 case NODE_TEST_NAME:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008288 switch (cur->type) {
8289 case XML_ELEMENT_NODE:
8290 if (xmlStrEqual(name, cur->name)) {
8291 if (prefix == NULL) {
8292 if (cur->ns == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008293#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008294 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008295#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008296 addNode(list, cur);
8297 }
8298 } else {
8299 if ((cur->ns != NULL) &&
8300 (xmlStrEqual(URI,
8301 cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008302#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008303 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008304#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008305 addNode(list, cur);
8306 }
8307 }
8308 }
8309 break;
8310 case XML_ATTRIBUTE_NODE:{
8311 xmlAttrPtr attr = (xmlAttrPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008312
Daniel Veillardf06307e2001-07-03 10:35:50 +00008313 if (xmlStrEqual(name, attr->name)) {
8314 if (prefix == NULL) {
8315 if ((attr->ns == NULL) ||
8316 (attr->ns->prefix == NULL)) {
8317#ifdef DEBUG_STEP
8318 n++;
8319#endif
8320 addNode(list,
8321 (xmlNodePtr) attr);
8322 }
8323 } else {
8324 if ((attr->ns != NULL) &&
8325 (xmlStrEqual(URI,
8326 attr->ns->
8327 href))) {
8328#ifdef DEBUG_STEP
8329 n++;
8330#endif
8331 addNode(list,
8332 (xmlNodePtr) attr);
8333 }
8334 }
8335 }
8336 break;
8337 }
8338 case XML_NAMESPACE_DECL:
8339 if (cur->type == XML_NAMESPACE_DECL) {
8340 xmlNsPtr ns = (xmlNsPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008341
Daniel Veillardf06307e2001-07-03 10:35:50 +00008342 if ((ns->prefix != NULL) && (name != NULL)
8343 && (xmlStrEqual(ns->prefix, name))) {
8344#ifdef DEBUG_STEP
8345 n++;
8346#endif
8347 addNode(list, cur);
8348 }
8349 }
8350 break;
8351 default:
8352 break;
8353 }
8354 break;
8355 break;
8356 }
8357 } while (cur != NULL);
8358
8359 /*
8360 * If there is some predicate filtering do it now
8361 */
8362 if (op->ch2 != -1) {
8363 xmlXPathObjectPtr obj2;
8364
8365 valuePush(ctxt, xmlXPathWrapNodeSet(list));
8366 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
8367 CHECK_TYPE0(XPATH_NODESET);
8368 obj2 = valuePop(ctxt);
8369 list = obj2->nodesetval;
8370 obj2->nodesetval = NULL;
8371 xmlXPathFreeObject(obj2);
8372 }
8373 if (ret == NULL) {
8374 ret = list;
8375 } else {
8376 ret = xmlXPathNodeSetMerge(ret, list);
8377 xmlXPathFreeNodeSet(list);
8378 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008379 }
8380 ctxt->context->node = tmp;
8381#ifdef DEBUG_STEP
8382 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008383 "\nExamined %d nodes, found %d nodes at that step\n",
8384 t, n);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008385#endif
8386 xmlXPathFreeObject(obj);
8387 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Daniel Veillardf06307e2001-07-03 10:35:50 +00008388 return(t);
8389}
8390
8391/**
8392 * xmlXPathNodeCollectAndTestNth:
8393 * @ctxt: the XPath Parser context
8394 * @op: the XPath precompiled step operation
8395 * @indx: the index to collect
8396 * @first: pointer to the first element in document order
8397 * @last: pointer to the last element in document order
8398 *
8399 * This is the function implementing a step: based on the current list
8400 * of nodes, it builds up a new list, looking at all nodes under that
8401 * axis and selecting them it also do the predicate filtering
8402 *
8403 * Pushes the new NodeSet resulting from the search.
8404 * Returns the number of node traversed
8405 */
8406static int
8407xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
8408 xmlXPathStepOpPtr op, int indx,
8409 xmlNodePtr * first, xmlNodePtr * last)
8410{
8411 xmlXPathAxisVal axis = op->value;
8412 xmlXPathTestVal test = op->value2;
8413 xmlXPathTypeVal type = op->value3;
8414 const xmlChar *prefix = op->value4;
8415 const xmlChar *name = op->value5;
8416 const xmlChar *URI = NULL;
8417 int n = 0, t = 0;
8418
8419 int i;
8420 xmlNodeSetPtr list;
8421 xmlXPathTraversalFunction next = NULL;
8422 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
8423 xmlNodePtr cur = NULL;
8424 xmlXPathObjectPtr obj;
8425 xmlNodeSetPtr nodelist;
8426 xmlNodePtr tmp;
8427
8428 CHECK_TYPE0(XPATH_NODESET);
8429 obj = valuePop(ctxt);
8430 addNode = xmlXPathNodeSetAdd;
8431 if (prefix != NULL) {
8432 URI = xmlXPathNsLookup(ctxt->context, prefix);
8433 if (URI == NULL)
8434 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
8435 }
8436#ifdef DEBUG_STEP_NTH
8437 xmlGenericError(xmlGenericErrorContext, "new step : ");
8438 if (first != NULL) {
8439 if (*first != NULL)
8440 xmlGenericError(xmlGenericErrorContext, "first = %s ",
8441 (*first)->name);
8442 else
8443 xmlGenericError(xmlGenericErrorContext, "first = NULL ");
8444 }
8445 if (last != NULL) {
8446 if (*last != NULL)
8447 xmlGenericError(xmlGenericErrorContext, "last = %s ",
8448 (*last)->name);
8449 else
8450 xmlGenericError(xmlGenericErrorContext, "last = NULL ");
8451 }
8452#endif
8453 switch (axis) {
8454 case AXIS_ANCESTOR:
8455#ifdef DEBUG_STEP_NTH
8456 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
8457#endif
8458 first = NULL;
8459 next = xmlXPathNextAncestor;
8460 break;
8461 case AXIS_ANCESTOR_OR_SELF:
8462#ifdef DEBUG_STEP_NTH
8463 xmlGenericError(xmlGenericErrorContext,
8464 "axis 'ancestors-or-self' ");
8465#endif
8466 first = NULL;
8467 next = xmlXPathNextAncestorOrSelf;
8468 break;
8469 case AXIS_ATTRIBUTE:
8470#ifdef DEBUG_STEP_NTH
8471 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
8472#endif
8473 first = NULL;
8474 last = NULL;
8475 next = xmlXPathNextAttribute;
8476 break;
8477 case AXIS_CHILD:
8478#ifdef DEBUG_STEP_NTH
8479 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
8480#endif
8481 last = NULL;
8482 next = xmlXPathNextChild;
8483 break;
8484 case AXIS_DESCENDANT:
8485#ifdef DEBUG_STEP_NTH
8486 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
8487#endif
8488 last = NULL;
8489 next = xmlXPathNextDescendant;
8490 break;
8491 case AXIS_DESCENDANT_OR_SELF:
8492#ifdef DEBUG_STEP_NTH
8493 xmlGenericError(xmlGenericErrorContext,
8494 "axis 'descendant-or-self' ");
8495#endif
8496 last = NULL;
8497 next = xmlXPathNextDescendantOrSelf;
8498 break;
8499 case AXIS_FOLLOWING:
8500#ifdef DEBUG_STEP_NTH
8501 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
8502#endif
8503 last = NULL;
8504 next = xmlXPathNextFollowing;
8505 break;
8506 case AXIS_FOLLOWING_SIBLING:
8507#ifdef DEBUG_STEP_NTH
8508 xmlGenericError(xmlGenericErrorContext,
8509 "axis 'following-siblings' ");
8510#endif
8511 last = NULL;
8512 next = xmlXPathNextFollowingSibling;
8513 break;
8514 case AXIS_NAMESPACE:
8515#ifdef DEBUG_STEP_NTH
8516 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
8517#endif
8518 last = NULL;
8519 first = NULL;
8520 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8521 break;
8522 case AXIS_PARENT:
8523#ifdef DEBUG_STEP_NTH
8524 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
8525#endif
8526 first = NULL;
8527 next = xmlXPathNextParent;
8528 break;
8529 case AXIS_PRECEDING:
8530#ifdef DEBUG_STEP_NTH
8531 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
8532#endif
8533 first = NULL;
8534 next = xmlXPathNextPrecedingInternal;
8535 break;
8536 case AXIS_PRECEDING_SIBLING:
8537#ifdef DEBUG_STEP_NTH
8538 xmlGenericError(xmlGenericErrorContext,
8539 "axis 'preceding-sibling' ");
8540#endif
8541 first = NULL;
8542 next = xmlXPathNextPrecedingSibling;
8543 break;
8544 case AXIS_SELF:
8545#ifdef DEBUG_STEP_NTH
8546 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
8547#endif
8548 first = NULL;
8549 last = NULL;
8550 next = xmlXPathNextSelf;
8551 break;
8552 }
8553 if (next == NULL)
8554 return(0);
8555
8556 nodelist = obj->nodesetval;
8557 if (nodelist == NULL) {
8558 xmlXPathFreeObject(obj);
8559 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8560 return(0);
8561 }
8562 addNode = xmlXPathNodeSetAddUnique;
8563#ifdef DEBUG_STEP_NTH
8564 xmlGenericError(xmlGenericErrorContext,
8565 " context contains %d nodes\n", nodelist->nodeNr);
8566 switch (test) {
8567 case NODE_TEST_NONE:
8568 xmlGenericError(xmlGenericErrorContext,
8569 " searching for none !!!\n");
8570 break;
8571 case NODE_TEST_TYPE:
8572 xmlGenericError(xmlGenericErrorContext,
8573 " searching for type %d\n", type);
8574 break;
8575 case NODE_TEST_PI:
8576 xmlGenericError(xmlGenericErrorContext,
8577 " searching for PI !!!\n");
8578 break;
8579 case NODE_TEST_ALL:
8580 xmlGenericError(xmlGenericErrorContext,
8581 " searching for *\n");
8582 break;
8583 case NODE_TEST_NS:
8584 xmlGenericError(xmlGenericErrorContext,
8585 " searching for namespace %s\n",
8586 prefix);
8587 break;
8588 case NODE_TEST_NAME:
8589 xmlGenericError(xmlGenericErrorContext,
8590 " searching for name %s\n", name);
8591 if (prefix != NULL)
8592 xmlGenericError(xmlGenericErrorContext,
8593 " with namespace %s\n", prefix);
8594 break;
8595 }
8596 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8597#endif
8598 /*
8599 * 2.3 Node Tests
8600 * - For the attribute axis, the principal node type is attribute.
8601 * - For the namespace axis, the principal node type is namespace.
8602 * - For other axes, the principal node type is element.
8603 *
8604 * A node test * is true for any node of the
8605 * principal node type. For example, child::* willi
8606 * select all element children of the context node
8607 */
8608 tmp = ctxt->context->node;
8609 list = xmlXPathNodeSetCreate(NULL);
8610 for (i = 0; i < nodelist->nodeNr; i++) {
8611 ctxt->context->node = nodelist->nodeTab[i];
8612
8613 cur = NULL;
8614 n = 0;
8615 do {
8616 cur = next(ctxt, cur);
8617 if (cur == NULL)
8618 break;
8619 if ((first != NULL) && (*first == cur))
8620 break;
8621 if (((t % 256) == 0) &&
8622 (first != NULL) && (*first != NULL) &&
8623 (xmlXPathCmpNodes(*first, cur) >= 0))
8624 break;
8625 if ((last != NULL) && (*last == cur))
8626 break;
8627 if (((t % 256) == 0) &&
8628 (last != NULL) && (*last != NULL) &&
8629 (xmlXPathCmpNodes(cur, *last) >= 0))
8630 break;
8631 t++;
8632 switch (test) {
8633 case NODE_TEST_NONE:
8634 ctxt->context->node = tmp;
8635 STRANGE return(0);
8636 case NODE_TEST_TYPE:
8637 if ((cur->type == type) ||
8638 ((type == NODE_TYPE_NODE) &&
8639 ((cur->type == XML_DOCUMENT_NODE) ||
8640 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8641 (cur->type == XML_ELEMENT_NODE) ||
8642 (cur->type == XML_PI_NODE) ||
8643 (cur->type == XML_COMMENT_NODE) ||
8644 (cur->type == XML_CDATA_SECTION_NODE) ||
8645 (cur->type == XML_TEXT_NODE)))) {
8646 n++;
8647 if (n == indx)
8648 addNode(list, cur);
8649 }
8650 break;
8651 case NODE_TEST_PI:
8652 if (cur->type == XML_PI_NODE) {
8653 if ((name != NULL) &&
8654 (!xmlStrEqual(name, cur->name)))
8655 break;
8656 n++;
8657 if (n == indx)
8658 addNode(list, cur);
8659 }
8660 break;
8661 case NODE_TEST_ALL:
8662 if (axis == AXIS_ATTRIBUTE) {
8663 if (cur->type == XML_ATTRIBUTE_NODE) {
8664 n++;
8665 if (n == indx)
8666 addNode(list, cur);
8667 }
8668 } else if (axis == AXIS_NAMESPACE) {
8669 if (cur->type == XML_NAMESPACE_DECL) {
8670 n++;
8671 if (n == indx)
8672 addNode(list, cur);
8673 }
8674 } else {
8675 if (cur->type == XML_ELEMENT_NODE) {
8676 if (prefix == NULL) {
8677 n++;
8678 if (n == indx)
8679 addNode(list, cur);
8680 } else if ((cur->ns != NULL) &&
8681 (xmlStrEqual(URI, cur->ns->href))) {
8682 n++;
8683 if (n == indx)
8684 addNode(list, cur);
8685 }
8686 }
8687 }
8688 break;
8689 case NODE_TEST_NS:{
8690 TODO;
8691 break;
8692 }
8693 case NODE_TEST_NAME:
8694 switch (cur->type) {
8695 case XML_ELEMENT_NODE:
8696 if (xmlStrEqual(name, cur->name)) {
8697 if (prefix == NULL) {
8698 if (cur->ns == NULL) {
8699 n++;
8700 if (n == indx)
8701 addNode(list, cur);
8702 }
8703 } else {
8704 if ((cur->ns != NULL) &&
8705 (xmlStrEqual(URI,
8706 cur->ns->href))) {
8707 n++;
8708 if (n == indx)
8709 addNode(list, cur);
8710 }
8711 }
8712 }
8713 break;
8714 case XML_ATTRIBUTE_NODE:{
8715 xmlAttrPtr attr = (xmlAttrPtr) cur;
8716
8717 if (xmlStrEqual(name, attr->name)) {
8718 if (prefix == NULL) {
8719 if ((attr->ns == NULL) ||
8720 (attr->ns->prefix == NULL)) {
8721 n++;
8722 if (n == indx)
8723 addNode(list, cur);
8724 }
8725 } else {
8726 if ((attr->ns != NULL) &&
8727 (xmlStrEqual(URI,
8728 attr->ns->
8729 href))) {
8730 n++;
8731 if (n == indx)
8732 addNode(list, cur);
8733 }
8734 }
8735 }
8736 break;
8737 }
8738 case XML_NAMESPACE_DECL:
8739 if (cur->type == XML_NAMESPACE_DECL) {
8740 xmlNsPtr ns = (xmlNsPtr) cur;
8741
8742 if ((ns->prefix != NULL) && (name != NULL)
8743 && (xmlStrEqual(ns->prefix, name))) {
8744 n++;
8745 if (n == indx)
8746 addNode(list, cur);
8747 }
8748 }
8749 break;
8750 default:
8751 break;
8752 }
8753 break;
8754 break;
8755 }
8756 } while (n < indx);
8757 }
8758 ctxt->context->node = tmp;
8759#ifdef DEBUG_STEP_NTH
8760 xmlGenericError(xmlGenericErrorContext,
8761 "\nExamined %d nodes, found %d nodes at that step\n",
8762 t, list->nodeNr);
8763#endif
8764 xmlXPathFreeObject(obj);
8765 valuePush(ctxt, xmlXPathWrapNodeSet(list));
8766 return(t);
8767}
8768
8769/**
8770 * xmlXPathCompOpEvalFirst:
8771 * @ctxt: the XPath parser context with the compiled expression
8772 * @op: an XPath compiled operation
8773 * @first: the first elem found so far
8774 *
8775 * Evaluate the Precompiled XPath operation searching only the first
8776 * element in document order
8777 *
8778 * Returns the number of examined objects.
8779 */
8780static int
8781xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
8782 xmlXPathStepOpPtr op, xmlNodePtr * first)
8783{
8784 int total = 0, cur;
8785 xmlXPathCompExprPtr comp;
8786 xmlXPathObjectPtr arg1, arg2;
8787
8788 comp = ctxt->comp;
8789 switch (op->op) {
8790 case XPATH_OP_END:
8791 return (0);
8792 case XPATH_OP_UNION:
8793 total =
8794 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8795 first);
8796 if ((ctxt->value != NULL)
8797 && (ctxt->value->type == XPATH_NODESET)
8798 && (ctxt->value->nodesetval != NULL)
8799 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8800 /*
8801 * limit tree traversing to first node in the result
8802 */
8803 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8804 *first = ctxt->value->nodesetval->nodeTab[0];
8805 }
8806 cur =
8807 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
8808 first);
8809 CHECK_TYPE0(XPATH_NODESET);
8810 arg2 = valuePop(ctxt);
8811
8812 CHECK_TYPE0(XPATH_NODESET);
8813 arg1 = valuePop(ctxt);
8814
8815 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8816 arg2->nodesetval);
8817 valuePush(ctxt, arg1);
8818 xmlXPathFreeObject(arg2);
8819 /* optimizer */
8820 if (total > cur)
8821 xmlXPathCompSwap(op);
8822 return (total + cur);
8823 case XPATH_OP_ROOT:
8824 xmlXPathRoot(ctxt);
8825 return (0);
8826 case XPATH_OP_NODE:
8827 if (op->ch1 != -1)
8828 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8829 if (op->ch2 != -1)
8830 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8831 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8832 return (total);
8833 case XPATH_OP_RESET:
8834 if (op->ch1 != -1)
8835 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8836 if (op->ch2 != -1)
8837 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8838 ctxt->context->node = NULL;
8839 return (total);
8840 case XPATH_OP_COLLECT:{
8841 if (op->ch1 == -1)
8842 return (total);
8843
8844 total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8845
8846 /*
8847 * Optimization for [n] selection where n is a number
8848 */
8849 if ((op->ch2 != -1) &&
8850 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8851 (comp->steps[op->ch2].ch1 == -1) &&
8852 (comp->steps[op->ch2].ch2 != -1) &&
8853 (comp->steps[comp->steps[op->ch2].ch2].op ==
8854 XPATH_OP_VALUE)) {
8855 xmlXPathObjectPtr val;
8856
8857 val = comp->steps[comp->steps[op->ch2].ch2].value4;
8858 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
8859 int indx = (int) val->floatval;
8860
8861 if (val->floatval == (float) indx) {
8862 xmlXPathNodeCollectAndTestNth(ctxt, op, indx,
8863 first, NULL);
8864 return (total);
8865 }
8866 }
8867 }
8868 total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL);
8869 return (total);
8870 }
8871 case XPATH_OP_VALUE:
8872 valuePush(ctxt,
8873 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
8874 return (0);
8875 case XPATH_OP_SORT:
8876 if (op->ch1 != -1)
8877 total +=
8878 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8879 first);
8880 if ((ctxt->value != NULL)
8881 && (ctxt->value->type == XPATH_NODESET)
8882 && (ctxt->value->nodesetval != NULL))
8883 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8884 return (total);
8885 default:
8886 return (xmlXPathCompOpEval(ctxt, op));
8887 }
8888}
8889
8890/**
8891 * xmlXPathCompOpEvalLast:
8892 * @ctxt: the XPath parser context with the compiled expression
8893 * @op: an XPath compiled operation
8894 * @last: the last elem found so far
8895 *
8896 * Evaluate the Precompiled XPath operation searching only the last
8897 * element in document order
8898 *
8899 * Returns the number of node traversed
8900 */
8901static int
8902xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
8903 xmlNodePtr * last)
8904{
8905 int total = 0, cur;
8906 xmlXPathCompExprPtr comp;
8907 xmlXPathObjectPtr arg1, arg2;
8908
8909 comp = ctxt->comp;
8910 switch (op->op) {
8911 case XPATH_OP_END:
8912 return (0);
8913 case XPATH_OP_UNION:
8914 total =
8915 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
8916 if ((ctxt->value != NULL)
8917 && (ctxt->value->type == XPATH_NODESET)
8918 && (ctxt->value->nodesetval != NULL)
8919 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8920 /*
8921 * limit tree traversing to first node in the result
8922 */
8923 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8924 *last =
8925 ctxt->value->nodesetval->nodeTab[ctxt->value->
8926 nodesetval->nodeNr -
8927 1];
8928 }
8929 cur =
8930 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
8931 if ((ctxt->value != NULL)
8932 && (ctxt->value->type == XPATH_NODESET)
8933 && (ctxt->value->nodesetval != NULL)
8934 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8935 }
8936 CHECK_TYPE0(XPATH_NODESET);
8937 arg2 = valuePop(ctxt);
8938
8939 CHECK_TYPE0(XPATH_NODESET);
8940 arg1 = valuePop(ctxt);
8941
8942 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8943 arg2->nodesetval);
8944 valuePush(ctxt, arg1);
8945 xmlXPathFreeObject(arg2);
8946 /* optimizer */
8947 if (total > cur)
8948 xmlXPathCompSwap(op);
8949 return (total + cur);
8950 case XPATH_OP_ROOT:
8951 xmlXPathRoot(ctxt);
8952 return (0);
8953 case XPATH_OP_NODE:
8954 if (op->ch1 != -1)
8955 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8956 if (op->ch2 != -1)
8957 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8958 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8959 return (total);
8960 case XPATH_OP_RESET:
8961 if (op->ch1 != -1)
8962 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8963 if (op->ch2 != -1)
8964 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8965 ctxt->context->node = NULL;
8966 return (total);
8967 case XPATH_OP_COLLECT:{
8968 if (op->ch1 == -1)
8969 return (0);
8970
8971 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8972
8973 /*
8974 * Optimization for [n] selection where n is a number
8975 */
8976 if ((op->ch2 != -1) &&
8977 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8978 (comp->steps[op->ch2].ch1 == -1) &&
8979 (comp->steps[op->ch2].ch2 != -1) &&
8980 (comp->steps[comp->steps[op->ch2].ch2].op ==
8981 XPATH_OP_VALUE)) {
8982 xmlXPathObjectPtr val;
8983
8984 val = comp->steps[comp->steps[op->ch2].ch2].value4;
8985 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
8986 int indx = (int) val->floatval;
8987
8988 if (val->floatval == (float) indx) {
8989 total +=
8990 xmlXPathNodeCollectAndTestNth(ctxt, op,
8991 indx, NULL,
8992 last);
8993 return (total);
8994 }
8995 }
8996 }
8997 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last);
8998 return (total);
8999 }
9000 case XPATH_OP_VALUE:
9001 valuePush(ctxt,
9002 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
9003 return (0);
9004 case XPATH_OP_SORT:
9005 if (op->ch1 != -1)
9006 total +=
9007 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
9008 last);
9009 if ((ctxt->value != NULL)
9010 && (ctxt->value->type == XPATH_NODESET)
9011 && (ctxt->value->nodesetval != NULL))
9012 xmlXPathNodeSetSort(ctxt->value->nodesetval);
9013 return (total);
9014 default:
9015 return (xmlXPathCompOpEval(ctxt, op));
9016 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009017}
9018
Owen Taylor3473f882001-02-23 17:55:21 +00009019/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009020 * xmlXPathCompOpEval:
9021 * @ctxt: the XPath parser context with the compiled expression
9022 * @op: an XPath compiled operation
9023 *
9024 * Evaluate the Precompiled XPath operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00009025 * Returns the number of node traversed
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009026 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00009027static int
9028xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
9029{
9030 int total = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009031 int equal, ret;
9032 xmlXPathCompExprPtr comp;
9033 xmlXPathObjectPtr arg1, arg2;
9034
9035 comp = ctxt->comp;
9036 switch (op->op) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00009037 case XPATH_OP_END:
9038 return (0);
9039 case XPATH_OP_AND:
9040 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9041 xmlXPathBooleanFunction(ctxt, 1);
9042 if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
9043 return (total);
9044 arg2 = valuePop(ctxt);
9045 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9046 xmlXPathBooleanFunction(ctxt, 1);
9047 arg1 = valuePop(ctxt);
9048 arg1->boolval &= arg2->boolval;
9049 valuePush(ctxt, arg1);
9050 xmlXPathFreeObject(arg2);
9051 return (total);
9052 case XPATH_OP_OR:
9053 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9054 xmlXPathBooleanFunction(ctxt, 1);
9055 if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
9056 return (total);
9057 arg2 = valuePop(ctxt);
9058 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9059 xmlXPathBooleanFunction(ctxt, 1);
9060 arg1 = valuePop(ctxt);
9061 arg1->boolval |= arg2->boolval;
9062 valuePush(ctxt, arg1);
9063 xmlXPathFreeObject(arg2);
9064 return (total);
9065 case XPATH_OP_EQUAL:
9066 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9067 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9068 equal = xmlXPathEqualValues(ctxt);
9069 if (op->value)
9070 valuePush(ctxt, xmlXPathNewBoolean(equal));
9071 else
9072 valuePush(ctxt, xmlXPathNewBoolean(!equal));
9073 return (total);
9074 case XPATH_OP_CMP:
9075 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9076 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9077 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
9078 valuePush(ctxt, xmlXPathNewBoolean(ret));
9079 return (total);
9080 case XPATH_OP_PLUS:
9081 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9082 if (op->ch2 != -1)
9083 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9084 if (op->value == 0)
9085 xmlXPathSubValues(ctxt);
9086 else if (op->value == 1)
9087 xmlXPathAddValues(ctxt);
9088 else if (op->value == 2)
9089 xmlXPathValueFlipSign(ctxt);
9090 else if (op->value == 3) {
9091 CAST_TO_NUMBER;
9092 CHECK_TYPE0(XPATH_NUMBER);
9093 }
9094 return (total);
9095 case XPATH_OP_MULT:
9096 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9097 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9098 if (op->value == 0)
9099 xmlXPathMultValues(ctxt);
9100 else if (op->value == 1)
9101 xmlXPathDivValues(ctxt);
9102 else if (op->value == 2)
9103 xmlXPathModValues(ctxt);
9104 return (total);
9105 case XPATH_OP_UNION:
9106 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9107 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9108 CHECK_TYPE0(XPATH_NODESET);
9109 arg2 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009110
Daniel Veillardf06307e2001-07-03 10:35:50 +00009111 CHECK_TYPE0(XPATH_NODESET);
9112 arg1 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009113
Daniel Veillardf06307e2001-07-03 10:35:50 +00009114 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
9115 arg2->nodesetval);
9116 valuePush(ctxt, arg1);
9117 xmlXPathFreeObject(arg2);
9118 return (total);
9119 case XPATH_OP_ROOT:
9120 xmlXPathRoot(ctxt);
9121 return (total);
9122 case XPATH_OP_NODE:
9123 if (op->ch1 != -1)
9124 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9125 if (op->ch2 != -1)
9126 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9127 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
9128 return (total);
9129 case XPATH_OP_RESET:
9130 if (op->ch1 != -1)
9131 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9132 if (op->ch2 != -1)
9133 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9134 ctxt->context->node = NULL;
9135 return (total);
9136 case XPATH_OP_COLLECT:{
9137 if (op->ch1 == -1)
9138 return (total);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009139
Daniel Veillardf06307e2001-07-03 10:35:50 +00009140 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009141
Daniel Veillardf06307e2001-07-03 10:35:50 +00009142 /*
9143 * Optimization for [n] selection where n is a number
9144 */
9145 if ((op->ch2 != -1) &&
9146 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
9147 (comp->steps[op->ch2].ch1 == -1) &&
9148 (comp->steps[op->ch2].ch2 != -1) &&
9149 (comp->steps[comp->steps[op->ch2].ch2].op ==
9150 XPATH_OP_VALUE)) {
9151 xmlXPathObjectPtr val;
Daniel Veillard42596ad2001-05-22 16:57:14 +00009152
Daniel Veillardf06307e2001-07-03 10:35:50 +00009153 val = comp->steps[comp->steps[op->ch2].ch2].value4;
9154 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
9155 int indx = (int) val->floatval;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009156
Daniel Veillardf06307e2001-07-03 10:35:50 +00009157 if (val->floatval == (float) indx) {
9158 total +=
9159 xmlXPathNodeCollectAndTestNth(ctxt, op,
9160 indx, NULL,
9161 NULL);
9162 return (total);
9163 }
9164 }
9165 }
9166 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL);
9167 return (total);
9168 }
9169 case XPATH_OP_VALUE:
9170 valuePush(ctxt,
9171 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
9172 return (total);
9173 case XPATH_OP_VARIABLE:{
9174 if (op->ch1 != -1)
9175 total +=
9176 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9177 if (op->value5 == NULL)
9178 valuePush(ctxt,
9179 xmlXPathVariableLookup(ctxt->context,
9180 op->value4));
9181 else {
9182 const xmlChar *URI;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009183
Daniel Veillardf06307e2001-07-03 10:35:50 +00009184 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9185 if (URI == NULL) {
9186 xmlGenericError(xmlGenericErrorContext,
9187 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
9188 op->value4, op->value5);
9189 return (total);
9190 }
9191 valuePush(ctxt,
9192 xmlXPathVariableLookupNS(ctxt->context,
9193 op->value4, URI));
9194 }
9195 return (total);
9196 }
9197 case XPATH_OP_FUNCTION:{
9198 xmlXPathFunction func;
9199 const xmlChar *oldFunc, *oldFuncURI;
9200
9201 if (op->ch1 != -1)
9202 total +=
9203 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9204 if (op->cache != NULL)
9205 func = (xmlXPathFunction) op->cache;
9206 else {
9207 const xmlChar *URI = NULL;
9208
9209 if (op->value5 == NULL)
9210 func =
9211 xmlXPathFunctionLookup(ctxt->context,
9212 op->value4);
9213 else {
9214 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9215 if (URI == NULL) {
9216 xmlGenericError(xmlGenericErrorContext,
9217 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
9218 op->value4, op->value5);
9219 return (total);
9220 }
9221 func = xmlXPathFunctionLookupNS(ctxt->context,
9222 op->value4, URI);
9223 }
9224 if (func == NULL) {
9225 xmlGenericError(xmlGenericErrorContext,
9226 "xmlXPathRunEval: function %s not found\n",
9227 op->value4);
9228 XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
9229 return (total);
9230 }
9231 op->cache = (void *) func;
9232 op->cacheURI = (void *) URI;
9233 }
9234 oldFunc = ctxt->context->function;
9235 oldFuncURI = ctxt->context->functionURI;
9236 ctxt->context->function = op->value4;
9237 ctxt->context->functionURI = op->cacheURI;
9238 func(ctxt, op->value);
9239 ctxt->context->function = oldFunc;
9240 ctxt->context->functionURI = oldFuncURI;
9241 return (total);
9242 }
9243 case XPATH_OP_ARG:
9244 if (op->ch1 != -1)
9245 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9246 if (op->ch2 != -1)
9247 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9248 return (total);
9249 case XPATH_OP_PREDICATE:
9250 case XPATH_OP_FILTER:{
9251 xmlXPathObjectPtr res;
9252 xmlXPathObjectPtr obj, tmp;
9253 xmlNodeSetPtr newset = NULL;
9254 xmlNodeSetPtr oldset;
9255 xmlNodePtr oldnode;
9256 int i;
9257
9258 /*
9259 * Optimization for ()[1] selection i.e. the first elem
9260 */
9261 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9262 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9263 (comp->steps[op->ch2].op == XPATH_OP_VALUE)) {
9264 xmlXPathObjectPtr val;
9265
9266 val = comp->steps[op->ch2].value4;
9267 if ((val != NULL) && (val->type == XPATH_NUMBER) &&
9268 (val->floatval == 1.0)) {
9269 xmlNodePtr first = NULL;
9270
9271 total +=
9272 xmlXPathCompOpEvalFirst(ctxt,
9273 &comp->steps[op->ch1],
9274 &first);
9275 /*
9276 * The nodeset should be in document order,
9277 * Keep only the first value
9278 */
9279 if ((ctxt->value != NULL) &&
9280 (ctxt->value->type == XPATH_NODESET) &&
9281 (ctxt->value->nodesetval != NULL) &&
9282 (ctxt->value->nodesetval->nodeNr > 1))
9283 ctxt->value->nodesetval->nodeNr = 1;
9284 return (total);
9285 }
9286 }
9287 /*
9288 * Optimization for ()[last()] selection i.e. the last elem
9289 */
9290 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9291 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9292 (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
9293 int f = comp->steps[op->ch2].ch1;
9294
9295 if ((f != -1) &&
9296 (comp->steps[f].op == XPATH_OP_FUNCTION) &&
9297 (comp->steps[f].value5 == NULL) &&
9298 (comp->steps[f].value == 0) &&
9299 (comp->steps[f].value4 != NULL) &&
9300 (xmlStrEqual
9301 (comp->steps[f].value4, BAD_CAST "last"))) {
9302 xmlNodePtr last = NULL;
9303
9304 total +=
9305 xmlXPathCompOpEvalLast(ctxt,
9306 &comp->steps[op->ch1],
9307 &last);
9308 /*
9309 * The nodeset should be in document order,
9310 * Keep only the last value
9311 */
9312 if ((ctxt->value != NULL) &&
9313 (ctxt->value->type == XPATH_NODESET) &&
9314 (ctxt->value->nodesetval != NULL) &&
9315 (ctxt->value->nodesetval->nodeTab != NULL) &&
9316 (ctxt->value->nodesetval->nodeNr > 1)) {
9317 ctxt->value->nodesetval->nodeTab[0] =
9318 ctxt->value->nodesetval->nodeTab[ctxt->
9319 value->
9320 nodesetval->
9321 nodeNr -
9322 1];
9323 ctxt->value->nodesetval->nodeNr = 1;
9324 }
9325 return (total);
9326 }
9327 }
9328
9329 if (op->ch1 != -1)
9330 total +=
9331 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9332 if (op->ch2 == -1)
9333 return (total);
9334 if (ctxt->value == NULL)
9335 return (total);
9336
9337 oldnode = ctxt->context->node;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009338
9339#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009340 /*
9341 * Hum are we filtering the result of an XPointer expression
9342 */
9343 if (ctxt->value->type == XPATH_LOCATIONSET) {
9344 xmlLocationSetPtr newlocset = NULL;
9345 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009346
Daniel Veillardf06307e2001-07-03 10:35:50 +00009347 /*
9348 * Extract the old locset, and then evaluate the result of the
9349 * expression for all the element in the locset. use it to grow
9350 * up a new locset.
9351 */
9352 CHECK_TYPE0(XPATH_LOCATIONSET);
9353 obj = valuePop(ctxt);
9354 oldlocset = obj->user;
9355 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009356
Daniel Veillardf06307e2001-07-03 10:35:50 +00009357 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
9358 ctxt->context->contextSize = 0;
9359 ctxt->context->proximityPosition = 0;
9360 if (op->ch2 != -1)
9361 total +=
9362 xmlXPathCompOpEval(ctxt,
9363 &comp->steps[op->ch2]);
9364 res = valuePop(ctxt);
9365 if (res != NULL)
9366 xmlXPathFreeObject(res);
9367 valuePush(ctxt, obj);
9368 CHECK_ERROR0;
9369 return (total);
9370 }
9371 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009372
Daniel Veillardf06307e2001-07-03 10:35:50 +00009373 for (i = 0; i < oldlocset->locNr; i++) {
9374 /*
9375 * Run the evaluation with a node list made of a
9376 * single item in the nodelocset.
9377 */
9378 ctxt->context->node = oldlocset->locTab[i]->user;
9379 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9380 valuePush(ctxt, tmp);
9381 ctxt->context->contextSize = oldlocset->locNr;
9382 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009383
Daniel Veillardf06307e2001-07-03 10:35:50 +00009384 if (op->ch2 != -1)
9385 total +=
9386 xmlXPathCompOpEval(ctxt,
9387 &comp->steps[op->ch2]);
9388 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009389
Daniel Veillardf06307e2001-07-03 10:35:50 +00009390 /*
9391 * The result of the evaluation need to be tested to
9392 * decided whether the filter succeeded or not
9393 */
9394 res = valuePop(ctxt);
9395 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9396 xmlXPtrLocationSetAdd(newlocset,
9397 xmlXPathObjectCopy
9398 (oldlocset->locTab[i]));
9399 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009400
Daniel Veillardf06307e2001-07-03 10:35:50 +00009401 /*
9402 * Cleanup
9403 */
9404 if (res != NULL)
9405 xmlXPathFreeObject(res);
9406 if (ctxt->value == tmp) {
9407 res = valuePop(ctxt);
9408 xmlXPathFreeObject(res);
9409 }
9410
9411 ctxt->context->node = NULL;
9412 }
9413
9414 /*
9415 * The result is used as the new evaluation locset.
9416 */
9417 xmlXPathFreeObject(obj);
9418 ctxt->context->node = NULL;
9419 ctxt->context->contextSize = -1;
9420 ctxt->context->proximityPosition = -1;
9421 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
9422 ctxt->context->node = oldnode;
9423 return (total);
9424 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009425#endif /* LIBXML_XPTR_ENABLED */
9426
Daniel Veillardf06307e2001-07-03 10:35:50 +00009427 /*
9428 * Extract the old set, and then evaluate the result of the
9429 * expression for all the element in the set. use it to grow
9430 * up a new set.
9431 */
9432 CHECK_TYPE0(XPATH_NODESET);
9433 obj = valuePop(ctxt);
9434 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00009435
Daniel Veillardf06307e2001-07-03 10:35:50 +00009436 oldnode = ctxt->context->node;
9437 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009438
Daniel Veillardf06307e2001-07-03 10:35:50 +00009439 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
9440 ctxt->context->contextSize = 0;
9441 ctxt->context->proximityPosition = 0;
9442 if (op->ch2 != -1)
9443 total +=
9444 xmlXPathCompOpEval(ctxt,
9445 &comp->steps[op->ch2]);
9446 res = valuePop(ctxt);
9447 if (res != NULL)
9448 xmlXPathFreeObject(res);
9449 valuePush(ctxt, obj);
9450 ctxt->context->node = oldnode;
9451 CHECK_ERROR0;
9452 } else {
9453 /*
9454 * Initialize the new set.
9455 */
9456 newset = xmlXPathNodeSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009457
Daniel Veillardf06307e2001-07-03 10:35:50 +00009458 for (i = 0; i < oldset->nodeNr; i++) {
9459 /*
9460 * Run the evaluation with a node list made of
9461 * a single item in the nodeset.
9462 */
9463 ctxt->context->node = oldset->nodeTab[i];
9464 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9465 valuePush(ctxt, tmp);
9466 ctxt->context->contextSize = oldset->nodeNr;
9467 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009468
Daniel Veillardf06307e2001-07-03 10:35:50 +00009469 if (op->ch2 != -1)
9470 total +=
9471 xmlXPathCompOpEval(ctxt,
9472 &comp->steps[op->ch2]);
9473 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009474
Daniel Veillardf06307e2001-07-03 10:35:50 +00009475 /*
9476 * The result of the evaluation need to be tested to
9477 * decided whether the filter succeeded or not
9478 */
9479 res = valuePop(ctxt);
9480 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9481 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
9482 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009483
Daniel Veillardf06307e2001-07-03 10:35:50 +00009484 /*
9485 * Cleanup
9486 */
9487 if (res != NULL)
9488 xmlXPathFreeObject(res);
9489 if (ctxt->value == tmp) {
9490 res = valuePop(ctxt);
9491 xmlXPathFreeObject(res);
9492 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009493
Daniel Veillardf06307e2001-07-03 10:35:50 +00009494 ctxt->context->node = NULL;
9495 }
9496
9497 /*
9498 * The result is used as the new evaluation set.
9499 */
9500 xmlXPathFreeObject(obj);
9501 ctxt->context->node = NULL;
9502 ctxt->context->contextSize = -1;
9503 ctxt->context->proximityPosition = -1;
9504 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
9505 }
9506 ctxt->context->node = oldnode;
9507 return (total);
9508 }
9509 case XPATH_OP_SORT:
9510 if (op->ch1 != -1)
9511 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9512 if ((ctxt->value != NULL) &&
9513 (ctxt->value->type == XPATH_NODESET) &&
9514 (ctxt->value->nodesetval != NULL))
9515 xmlXPathNodeSetSort(ctxt->value->nodesetval);
9516 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009517#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009518 case XPATH_OP_RANGETO:{
9519 xmlXPathObjectPtr range;
9520 xmlXPathObjectPtr res, obj;
9521 xmlXPathObjectPtr tmp;
9522 xmlLocationSetPtr newset = NULL;
9523 xmlNodeSetPtr oldset;
9524 int i;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009525
Daniel Veillardf06307e2001-07-03 10:35:50 +00009526 if (op->ch1 != -1)
9527 total +=
9528 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9529 if (op->ch2 == -1)
9530 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009531
Daniel Veillardf06307e2001-07-03 10:35:50 +00009532 CHECK_TYPE0(XPATH_NODESET);
9533 obj = valuePop(ctxt);
9534 oldset = obj->nodesetval;
9535 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009536
Daniel Veillardf06307e2001-07-03 10:35:50 +00009537 newset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009538
Daniel Veillardf06307e2001-07-03 10:35:50 +00009539 if (oldset != NULL) {
9540 for (i = 0; i < oldset->nodeNr; i++) {
9541 /*
9542 * Run the evaluation with a node list made of a single item
9543 * in the nodeset.
9544 */
9545 ctxt->context->node = oldset->nodeTab[i];
9546 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9547 valuePush(ctxt, tmp);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009548
Daniel Veillardf06307e2001-07-03 10:35:50 +00009549 if (op->ch2 != -1)
9550 total +=
9551 xmlXPathCompOpEval(ctxt,
9552 &comp->steps[op->ch2]);
9553 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009554
Daniel Veillardf06307e2001-07-03 10:35:50 +00009555 /*
9556 * The result of the evaluation need to be tested to
9557 * decided whether the filter succeeded or not
9558 */
9559 res = valuePop(ctxt);
9560 range =
9561 xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
9562 res);
9563 if (range != NULL) {
9564 xmlXPtrLocationSetAdd(newset, range);
9565 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009566
Daniel Veillardf06307e2001-07-03 10:35:50 +00009567 /*
9568 * Cleanup
9569 */
9570 if (res != NULL)
9571 xmlXPathFreeObject(res);
9572 if (ctxt->value == tmp) {
9573 res = valuePop(ctxt);
9574 xmlXPathFreeObject(res);
9575 }
9576
9577 ctxt->context->node = NULL;
9578 }
9579 }
9580
9581 /*
9582 * The result is used as the new evaluation set.
9583 */
9584 xmlXPathFreeObject(obj);
9585 ctxt->context->node = NULL;
9586 ctxt->context->contextSize = -1;
9587 ctxt->context->proximityPosition = -1;
9588 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
9589 return (total);
9590 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009591#endif /* LIBXML_XPTR_ENABLED */
9592 }
9593 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00009594 "XPath: unknown precompiled operation %d\n", op->op);
9595 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009596}
9597
9598/**
9599 * xmlXPathRunEval:
9600 * @ctxt: the XPath parser context with the compiled expression
9601 *
9602 * Evaluate the Precompiled XPath expression in the given context.
9603 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009604static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009605xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
9606 xmlXPathCompExprPtr comp;
9607
9608 if ((ctxt == NULL) || (ctxt->comp == NULL))
9609 return;
9610
9611 if (ctxt->valueTab == NULL) {
9612 /* Allocate the value stack */
9613 ctxt->valueTab = (xmlXPathObjectPtr *)
9614 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
9615 if (ctxt->valueTab == NULL) {
9616 xmlFree(ctxt);
9617 xmlGenericError(xmlGenericErrorContext,
9618 "xmlXPathRunEval: out of memory\n");
9619 return;
9620 }
9621 ctxt->valueNr = 0;
9622 ctxt->valueMax = 10;
9623 ctxt->value = NULL;
9624 }
9625 comp = ctxt->comp;
9626 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
9627}
9628
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009629/************************************************************************
9630 * *
9631 * Public interfaces *
9632 * *
9633 ************************************************************************/
9634
9635/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009636 * xmlXPathEvalPredicate:
9637 * @ctxt: the XPath context
9638 * @res: the Predicate Expression evaluation result
9639 *
9640 * Evaluate a predicate result for the current node.
9641 * A PredicateExpr is evaluated by evaluating the Expr and converting
9642 * the result to a boolean. If the result is a number, the result will
9643 * be converted to true if the number is equal to the position of the
9644 * context node in the context node list (as returned by the position
9645 * function) and will be converted to false otherwise; if the result
9646 * is not a number, then the result will be converted as if by a call
9647 * to the boolean function.
9648 *
9649 * Return 1 if predicate is true, 0 otherwise
9650 */
9651int
9652xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
9653 if (res == NULL) return(0);
9654 switch (res->type) {
9655 case XPATH_BOOLEAN:
9656 return(res->boolval);
9657 case XPATH_NUMBER:
9658 return(res->floatval == ctxt->proximityPosition);
9659 case XPATH_NODESET:
9660 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009661 if (res->nodesetval == NULL)
9662 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009663 return(res->nodesetval->nodeNr != 0);
9664 case XPATH_STRING:
9665 return((res->stringval != NULL) &&
9666 (xmlStrlen(res->stringval) != 0));
9667 default:
9668 STRANGE
9669 }
9670 return(0);
9671}
9672
9673/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009674 * xmlXPathEvaluatePredicateResult:
9675 * @ctxt: the XPath Parser context
9676 * @res: the Predicate Expression evaluation result
9677 *
9678 * Evaluate a predicate result for the current node.
9679 * A PredicateExpr is evaluated by evaluating the Expr and converting
9680 * the result to a boolean. If the result is a number, the result will
9681 * be converted to true if the number is equal to the position of the
9682 * context node in the context node list (as returned by the position
9683 * function) and will be converted to false otherwise; if the result
9684 * is not a number, then the result will be converted as if by a call
9685 * to the boolean function.
9686 *
9687 * Return 1 if predicate is true, 0 otherwise
9688 */
9689int
9690xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
9691 xmlXPathObjectPtr res) {
9692 if (res == NULL) return(0);
9693 switch (res->type) {
9694 case XPATH_BOOLEAN:
9695 return(res->boolval);
9696 case XPATH_NUMBER:
9697 return(res->floatval == ctxt->context->proximityPosition);
9698 case XPATH_NODESET:
9699 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00009700 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00009701 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009702 return(res->nodesetval->nodeNr != 0);
9703 case XPATH_STRING:
9704 return((res->stringval != NULL) &&
9705 (xmlStrlen(res->stringval) != 0));
9706 default:
9707 STRANGE
9708 }
9709 return(0);
9710}
9711
9712/**
9713 * xmlXPathCompile:
9714 * @str: the XPath expression
9715 *
9716 * Compile an XPath expression
9717 *
9718 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9719 * the caller has to free the object.
9720 */
9721xmlXPathCompExprPtr
9722xmlXPathCompile(const xmlChar *str) {
9723 xmlXPathParserContextPtr ctxt;
9724 xmlXPathCompExprPtr comp;
9725
9726 xmlXPathInit();
9727
9728 ctxt = xmlXPathNewParserContext(str, NULL);
9729 xmlXPathCompileExpr(ctxt);
9730
Daniel Veillard40af6492001-04-22 08:50:55 +00009731 if (*ctxt->cur != 0) {
9732 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9733 comp = NULL;
9734 } else {
9735 comp = ctxt->comp;
9736 ctxt->comp = NULL;
9737 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009738 xmlXPathFreeParserContext(ctxt);
Daniel Veillardf06307e2001-07-03 10:35:50 +00009739#ifdef DEBUG_EVAL_COUNTS
9740 if (comp != NULL) {
9741 comp->string = xmlStrdup(str);
9742 comp->nb = 0;
9743 }
9744#endif
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009745 return(comp);
9746}
9747
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009748/**
9749 * xmlXPathCompiledEval:
9750 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00009751 * @ctx: the XPath context
9752 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009753 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00009754 *
9755 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9756 * the caller has to free the object.
9757 */
9758xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009759xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00009760 xmlXPathParserContextPtr ctxt;
9761 xmlXPathObjectPtr res, tmp, init = NULL;
9762 int stack = 0;
9763
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009764 if ((comp == NULL) || (ctx == NULL))
9765 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00009766 xmlXPathInit();
9767
9768 CHECK_CONTEXT(ctx)
9769
Daniel Veillardf06307e2001-07-03 10:35:50 +00009770#ifdef DEBUG_EVAL_COUNTS
9771 comp->nb++;
9772 if ((comp->string != NULL) && (comp->nb > 100)) {
9773 fprintf(stderr, "100 x %s\n", comp->string);
9774 comp->nb = 0;
9775 }
9776#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009777 ctxt = xmlXPathCompParserContext(comp, ctx);
9778 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009779
9780 if (ctxt->value == NULL) {
9781 xmlGenericError(xmlGenericErrorContext,
9782 "xmlXPathEval: evaluation failed\n");
9783 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00009784 } else {
9785 res = valuePop(ctxt);
9786 }
9787
Daniel Veillardf06307e2001-07-03 10:35:50 +00009788
Owen Taylor3473f882001-02-23 17:55:21 +00009789 do {
9790 tmp = valuePop(ctxt);
9791 if (tmp != NULL) {
9792 if (tmp != init)
9793 stack++;
9794 xmlXPathFreeObject(tmp);
9795 }
9796 } while (tmp != NULL);
9797 if ((stack != 0) && (res != NULL)) {
9798 xmlGenericError(xmlGenericErrorContext,
9799 "xmlXPathEval: %d object left on the stack\n",
9800 stack);
9801 }
9802 if (ctxt->error != XPATH_EXPRESSION_OK) {
9803 xmlXPathFreeObject(res);
9804 res = NULL;
9805 }
9806
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009807
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009808 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009809 xmlXPathFreeParserContext(ctxt);
9810 return(res);
9811}
9812
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009813/**
9814 * xmlXPathEvalExpr:
9815 * @ctxt: the XPath Parser context
9816 *
9817 * Parse and evaluate an XPath expression in the given context,
9818 * then push the result on the context stack
9819 */
9820void
9821xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
9822 xmlXPathCompileExpr(ctxt);
9823 xmlXPathRunEval(ctxt);
9824}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009825
9826/**
9827 * xmlXPathEval:
9828 * @str: the XPath expression
9829 * @ctx: the XPath context
9830 *
9831 * Evaluate the XPath Location Path in the given context.
9832 *
9833 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9834 * the caller has to free the object.
9835 */
9836xmlXPathObjectPtr
9837xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
9838 xmlXPathParserContextPtr ctxt;
9839 xmlXPathObjectPtr res, tmp, init = NULL;
9840 int stack = 0;
9841
9842 xmlXPathInit();
9843
9844 CHECK_CONTEXT(ctx)
9845
9846 ctxt = xmlXPathNewParserContext(str, ctx);
9847 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009848
9849 if (ctxt->value == NULL) {
9850 xmlGenericError(xmlGenericErrorContext,
9851 "xmlXPathEval: evaluation failed\n");
9852 res = NULL;
9853 } else if (*ctxt->cur != 0) {
9854 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9855 res = NULL;
9856 } else {
9857 res = valuePop(ctxt);
9858 }
9859
9860 do {
9861 tmp = valuePop(ctxt);
9862 if (tmp != NULL) {
9863 if (tmp != init)
9864 stack++;
9865 xmlXPathFreeObject(tmp);
9866 }
9867 } while (tmp != NULL);
9868 if ((stack != 0) && (res != NULL)) {
9869 xmlGenericError(xmlGenericErrorContext,
9870 "xmlXPathEval: %d object left on the stack\n",
9871 stack);
9872 }
9873 if (ctxt->error != XPATH_EXPRESSION_OK) {
9874 xmlXPathFreeObject(res);
9875 res = NULL;
9876 }
9877
Owen Taylor3473f882001-02-23 17:55:21 +00009878 xmlXPathFreeParserContext(ctxt);
9879 return(res);
9880}
9881
9882/**
9883 * xmlXPathEvalExpression:
9884 * @str: the XPath expression
9885 * @ctxt: the XPath context
9886 *
9887 * Evaluate the XPath expression in the given context.
9888 *
9889 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
9890 * the caller has to free the object.
9891 */
9892xmlXPathObjectPtr
9893xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
9894 xmlXPathParserContextPtr pctxt;
9895 xmlXPathObjectPtr res, tmp;
9896 int stack = 0;
9897
9898 xmlXPathInit();
9899
9900 CHECK_CONTEXT(ctxt)
9901
9902 pctxt = xmlXPathNewParserContext(str, ctxt);
9903 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009904
9905 if (*pctxt->cur != 0) {
9906 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9907 res = NULL;
9908 } else {
9909 res = valuePop(pctxt);
9910 }
9911 do {
9912 tmp = valuePop(pctxt);
9913 if (tmp != NULL) {
9914 xmlXPathFreeObject(tmp);
9915 stack++;
9916 }
9917 } while (tmp != NULL);
9918 if ((stack != 0) && (res != NULL)) {
9919 xmlGenericError(xmlGenericErrorContext,
9920 "xmlXPathEvalExpression: %d object left on the stack\n",
9921 stack);
9922 }
9923 xmlXPathFreeParserContext(pctxt);
9924 return(res);
9925}
9926
9927/**
9928 * xmlXPathRegisterAllFunctions:
9929 * @ctxt: the XPath context
9930 *
9931 * Registers all default XPath functions in this context
9932 */
9933void
9934xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
9935{
9936 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
9937 xmlXPathBooleanFunction);
9938 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
9939 xmlXPathCeilingFunction);
9940 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
9941 xmlXPathCountFunction);
9942 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
9943 xmlXPathConcatFunction);
9944 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
9945 xmlXPathContainsFunction);
9946 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
9947 xmlXPathIdFunction);
9948 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
9949 xmlXPathFalseFunction);
9950 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
9951 xmlXPathFloorFunction);
9952 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
9953 xmlXPathLastFunction);
9954 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
9955 xmlXPathLangFunction);
9956 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
9957 xmlXPathLocalNameFunction);
9958 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
9959 xmlXPathNotFunction);
9960 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
9961 xmlXPathNameFunction);
9962 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
9963 xmlXPathNamespaceURIFunction);
9964 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
9965 xmlXPathNormalizeFunction);
9966 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
9967 xmlXPathNumberFunction);
9968 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
9969 xmlXPathPositionFunction);
9970 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
9971 xmlXPathRoundFunction);
9972 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
9973 xmlXPathStringFunction);
9974 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
9975 xmlXPathStringLengthFunction);
9976 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
9977 xmlXPathStartsWithFunction);
9978 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
9979 xmlXPathSubstringFunction);
9980 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
9981 xmlXPathSubstringBeforeFunction);
9982 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
9983 xmlXPathSubstringAfterFunction);
9984 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
9985 xmlXPathSumFunction);
9986 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
9987 xmlXPathTrueFunction);
9988 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
9989 xmlXPathTranslateFunction);
9990}
9991
9992#endif /* LIBXML_XPATH_ENABLED */