blob: 2de4e1d5680d669be24c7f922e8ed74725bebc4d [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));
Daniel Veillardfe703322001-08-14 12:18:09 +00005196 else if (cur->type == XPATH_NODESET) {
Daniel Veillard911f49a2001-04-07 15:39:35 +00005197 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Daniel Veillardfe703322001-08-14 12:18:09 +00005198 } else {
5199 if ((cur->nodesetval->nodeNr != 1) ||
5200 (cur->nodesetval->nodeTab == NULL)) {
5201 valuePush(ctxt, xmlXPathNewFloat((double) 0));
5202 } else {
5203 xmlNodePtr tmp;
5204 int i = 0;
5205
5206 tmp = cur->nodesetval->nodeTab[0];
5207 if (tmp != NULL) {
5208 tmp = tmp->children;
5209 while (tmp != NULL) {
5210 tmp = tmp->next;
5211 i++;
5212 }
5213 }
5214 valuePush(ctxt, xmlXPathNewFloat((double) i));
5215 }
5216 }
Owen Taylor3473f882001-02-23 17:55:21 +00005217 xmlXPathFreeObject(cur);
5218}
5219
5220/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005221 * xmlXPathGetElementsByIds:
5222 * @doc: the document
5223 * @ids: a whitespace separated list of IDs
5224 *
5225 * Selects elements by their unique ID.
5226 *
5227 * Returns a node-set of selected elements.
5228 */
5229static xmlNodeSetPtr
5230xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
5231 xmlNodeSetPtr ret;
5232 const xmlChar *cur = ids;
5233 xmlChar *ID;
5234 xmlAttrPtr attr;
5235 xmlNodePtr elem = NULL;
5236
5237 ret = xmlXPathNodeSetCreate(NULL);
5238
5239 while (IS_BLANK(*cur)) cur++;
5240 while (*cur != 0) {
5241 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
5242 (*cur == '.') || (*cur == '-') ||
5243 (*cur == '_') || (*cur == ':') ||
5244 (IS_COMBINING(*cur)) ||
5245 (IS_EXTENDER(*cur)))
5246 cur++;
5247
5248 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
5249
5250 ID = xmlStrndup(ids, cur - ids);
5251 attr = xmlGetID(doc, ID);
5252 if (attr != NULL) {
5253 elem = attr->parent;
5254 xmlXPathNodeSetAdd(ret, elem);
5255 }
5256 if (ID != NULL)
5257 xmlFree(ID);
5258
5259 while (IS_BLANK(*cur)) cur++;
5260 ids = cur;
5261 }
5262 return(ret);
5263}
5264
5265/**
Owen Taylor3473f882001-02-23 17:55:21 +00005266 * xmlXPathIdFunction:
5267 * @ctxt: the XPath Parser context
5268 * @nargs: the number of arguments
5269 *
5270 * Implement the id() XPath function
5271 * node-set id(object)
5272 * The id function selects elements by their unique ID
5273 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
5274 * then the result is the union of the result of applying id to the
5275 * string value of each of the nodes in the argument node-set. When the
5276 * argument to id is of any other type, the argument is converted to a
5277 * string as if by a call to the string function; the string is split
5278 * into a whitespace-separated list of tokens (whitespace is any sequence
5279 * of characters matching the production S); the result is a node-set
5280 * containing the elements in the same document as the context node that
5281 * have a unique ID equal to any of the tokens in the list.
5282 */
5283void
5284xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005285 xmlChar *tokens;
5286 xmlNodeSetPtr ret;
5287 xmlXPathObjectPtr obj;
Owen Taylor3473f882001-02-23 17:55:21 +00005288
5289 CHECK_ARITY(1);
5290 obj = valuePop(ctxt);
5291 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
5292 if (obj->type == XPATH_NODESET) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005293 xmlNodeSetPtr ns;
Owen Taylor3473f882001-02-23 17:55:21 +00005294 int i;
5295
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005296 ret = xmlXPathNodeSetCreate(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005297
Daniel Veillard911f49a2001-04-07 15:39:35 +00005298 if (obj->nodesetval != NULL) {
5299 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005300 tokens =
5301 xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
5302 ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
5303 ret = xmlXPathNodeSetMerge(ret, ns);
5304 xmlXPathFreeNodeSet(ns);
5305 if (tokens != NULL)
5306 xmlFree(tokens);
Daniel Veillard911f49a2001-04-07 15:39:35 +00005307 }
Owen Taylor3473f882001-02-23 17:55:21 +00005308 }
5309
5310 xmlXPathFreeObject(obj);
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005311 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005312 return;
5313 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005314 obj = xmlXPathConvertString(obj);
Owen Taylor3473f882001-02-23 17:55:21 +00005315
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005316 ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
5317 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005318
Owen Taylor3473f882001-02-23 17:55:21 +00005319 xmlXPathFreeObject(obj);
5320 return;
5321}
5322
5323/**
5324 * xmlXPathLocalNameFunction:
5325 * @ctxt: the XPath Parser context
5326 * @nargs: the number of arguments
5327 *
5328 * Implement the local-name() XPath function
5329 * string local-name(node-set?)
5330 * The local-name function returns a string containing the local part
5331 * of the name of the node in the argument node-set that is first in
5332 * document order. If the node-set is empty or the first node has no
5333 * name, an empty string is returned. If the argument is omitted it
5334 * defaults to the context node.
5335 */
5336void
5337xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5338 xmlXPathObjectPtr cur;
5339
5340 if (nargs == 0) {
5341 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5342 nargs = 1;
5343 }
5344
5345 CHECK_ARITY(1);
5346 if ((ctxt->value == NULL) ||
5347 ((ctxt->value->type != XPATH_NODESET) &&
5348 (ctxt->value->type != XPATH_XSLT_TREE)))
5349 XP_ERROR(XPATH_INVALID_TYPE);
5350 cur = valuePop(ctxt);
5351
Daniel Veillard911f49a2001-04-07 15:39:35 +00005352 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005353 valuePush(ctxt, xmlXPathNewCString(""));
5354 } else {
5355 int i = 0; /* Should be first in document order !!!!! */
5356 switch (cur->nodesetval->nodeTab[i]->type) {
5357 case XML_ELEMENT_NODE:
5358 case XML_ATTRIBUTE_NODE:
5359 case XML_PI_NODE:
5360 valuePush(ctxt,
5361 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
5362 break;
5363 case XML_NAMESPACE_DECL:
5364 valuePush(ctxt, xmlXPathNewString(
5365 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
5366 break;
5367 default:
5368 valuePush(ctxt, xmlXPathNewCString(""));
5369 }
5370 }
5371 xmlXPathFreeObject(cur);
5372}
5373
5374/**
5375 * xmlXPathNamespaceURIFunction:
5376 * @ctxt: the XPath Parser context
5377 * @nargs: the number of arguments
5378 *
5379 * Implement the namespace-uri() XPath function
5380 * string namespace-uri(node-set?)
5381 * The namespace-uri function returns a string containing the
5382 * namespace URI of the expanded name of the node in the argument
5383 * node-set that is first in document order. If the node-set is empty,
5384 * the first node has no name, or the expanded name has no namespace
5385 * URI, an empty string is returned. If the argument is omitted it
5386 * defaults to the context node.
5387 */
5388void
5389xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5390 xmlXPathObjectPtr cur;
5391
5392 if (nargs == 0) {
5393 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5394 nargs = 1;
5395 }
5396 CHECK_ARITY(1);
5397 if ((ctxt->value == NULL) ||
5398 ((ctxt->value->type != XPATH_NODESET) &&
5399 (ctxt->value->type != XPATH_XSLT_TREE)))
5400 XP_ERROR(XPATH_INVALID_TYPE);
5401 cur = valuePop(ctxt);
5402
Daniel Veillard911f49a2001-04-07 15:39:35 +00005403 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005404 valuePush(ctxt, xmlXPathNewCString(""));
5405 } else {
5406 int i = 0; /* Should be first in document order !!!!! */
5407 switch (cur->nodesetval->nodeTab[i]->type) {
5408 case XML_ELEMENT_NODE:
5409 case XML_ATTRIBUTE_NODE:
5410 if (cur->nodesetval->nodeTab[i]->ns == NULL)
5411 valuePush(ctxt, xmlXPathNewCString(""));
5412 else
5413 valuePush(ctxt, xmlXPathNewString(
5414 cur->nodesetval->nodeTab[i]->ns->href));
5415 break;
5416 default:
5417 valuePush(ctxt, xmlXPathNewCString(""));
5418 }
5419 }
5420 xmlXPathFreeObject(cur);
5421}
5422
5423/**
5424 * xmlXPathNameFunction:
5425 * @ctxt: the XPath Parser context
5426 * @nargs: the number of arguments
5427 *
5428 * Implement the name() XPath function
5429 * string name(node-set?)
5430 * The name function returns a string containing a QName representing
5431 * the name of the node in the argument node-set that is first in documenti
5432 * order. The QName must represent the name with respect to the namespace
5433 * declarations in effect on the node whose name is being represented.
5434 * Typically, this will be the form in which the name occurred in the XML
5435 * source. This need not be the case if there are namespace declarations
5436 * in effect on the node that associate multiple prefixes with the same
5437 * namespace. However, an implementation may include information about
5438 * the original prefix in its representation of nodes; in this case, an
5439 * implementation can ensure that the returned string is always the same
5440 * as the QName used in the XML source. If the argument it omitted it
5441 * defaults to the context node.
5442 * Libxml keep the original prefix so the "real qualified name" used is
5443 * returned.
5444 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005445static void
Daniel Veillard04383752001-07-08 14:27:15 +00005446xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
5447{
Owen Taylor3473f882001-02-23 17:55:21 +00005448 xmlXPathObjectPtr cur;
5449
5450 if (nargs == 0) {
Daniel Veillard04383752001-07-08 14:27:15 +00005451 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5452 nargs = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005453 }
5454
5455 CHECK_ARITY(1);
Daniel Veillard04383752001-07-08 14:27:15 +00005456 if ((ctxt->value == NULL) ||
5457 ((ctxt->value->type != XPATH_NODESET) &&
5458 (ctxt->value->type != XPATH_XSLT_TREE)))
5459 XP_ERROR(XPATH_INVALID_TYPE);
Owen Taylor3473f882001-02-23 17:55:21 +00005460 cur = valuePop(ctxt);
5461
Daniel Veillard911f49a2001-04-07 15:39:35 +00005462 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Daniel Veillard04383752001-07-08 14:27:15 +00005463 valuePush(ctxt, xmlXPathNewCString(""));
Owen Taylor3473f882001-02-23 17:55:21 +00005464 } else {
Daniel Veillard04383752001-07-08 14:27:15 +00005465 int i = 0; /* Should be first in document order !!!!! */
Owen Taylor3473f882001-02-23 17:55:21 +00005466
Daniel Veillard04383752001-07-08 14:27:15 +00005467 switch (cur->nodesetval->nodeTab[i]->type) {
5468 case XML_ELEMENT_NODE:
5469 case XML_ATTRIBUTE_NODE:
5470 if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
5471 (cur->nodesetval->nodeTab[i]->ns->prefix == NULL))
5472 valuePush(ctxt,
5473 xmlXPathNewString(cur->nodesetval->
5474 nodeTab[i]->name));
5475
5476 else {
5477 char name[2000];
5478
5479 snprintf(name, sizeof(name), "%s:%s",
5480 (char *) cur->nodesetval->nodeTab[i]->ns->
5481 prefix,
5482 (char *) cur->nodesetval->nodeTab[i]->name);
5483 name[sizeof(name) - 1] = 0;
5484 valuePush(ctxt, xmlXPathNewCString(name));
5485 }
5486 break;
5487 default:
5488 valuePush(ctxt,
5489 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
5490 xmlXPathLocalNameFunction(ctxt, 1);
5491 }
Owen Taylor3473f882001-02-23 17:55:21 +00005492 }
5493 xmlXPathFreeObject(cur);
5494}
5495
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005496
5497/**
Owen Taylor3473f882001-02-23 17:55:21 +00005498 * xmlXPathStringFunction:
5499 * @ctxt: the XPath Parser context
5500 * @nargs: the number of arguments
5501 *
5502 * Implement the string() XPath function
5503 * string string(object?)
5504 * he string function converts an object to a string as follows:
5505 * - A node-set is converted to a string by returning the value of
5506 * the node in the node-set that is first in document order.
5507 * If the node-set is empty, an empty string is returned.
5508 * - A number is converted to a string as follows
5509 * + NaN is converted to the string NaN
5510 * + positive zero is converted to the string 0
5511 * + negative zero is converted to the string 0
5512 * + positive infinity is converted to the string Infinity
5513 * + negative infinity is converted to the string -Infinity
5514 * + if the number is an integer, the number is represented in
5515 * decimal form as a Number with no decimal point and no leading
5516 * zeros, preceded by a minus sign (-) if the number is negative
5517 * + otherwise, the number is represented in decimal form as a
5518 * Number including a decimal point with at least one digit
5519 * before the decimal point and at least one digit after the
5520 * decimal point, preceded by a minus sign (-) if the number
5521 * is negative; there must be no leading zeros before the decimal
5522 * point apart possibly from the one required digit immediatelyi
5523 * before the decimal point; beyond the one required digit
5524 * after the decimal point there must be as many, but only as
5525 * many, more digits as are needed to uniquely distinguish the
5526 * number from all other IEEE 754 numeric values.
5527 * - The boolean false value is converted to the string false.
5528 * The boolean true value is converted to the string true.
5529 *
5530 * If the argument is omitted, it defaults to a node-set with the
5531 * context node as its only member.
5532 */
5533void
5534xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5535 xmlXPathObjectPtr cur;
5536
5537 if (nargs == 0) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005538 valuePush(ctxt,
5539 xmlXPathWrapString(
5540 xmlXPathCastNodeToString(ctxt->context->node)));
5541 return;
Owen Taylor3473f882001-02-23 17:55:21 +00005542 }
5543
5544 CHECK_ARITY(1);
5545 cur = valuePop(ctxt);
5546 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005547 cur = xmlXPathConvertString(cur);
5548 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005549}
5550
5551/**
5552 * xmlXPathStringLengthFunction:
5553 * @ctxt: the XPath Parser context
5554 * @nargs: the number of arguments
5555 *
5556 * Implement the string-length() XPath function
5557 * number string-length(string?)
5558 * The string-length returns the number of characters in the string
5559 * (see [3.6 Strings]). If the argument is omitted, it defaults to
5560 * the context node converted to a string, in other words the value
5561 * of the context node.
5562 */
5563void
5564xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5565 xmlXPathObjectPtr cur;
5566
5567 if (nargs == 0) {
5568 if (ctxt->context->node == NULL) {
5569 valuePush(ctxt, xmlXPathNewFloat(0));
5570 } else {
5571 xmlChar *content;
5572
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005573 content = xmlXPathCastNodeToString(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005574 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00005575 xmlFree(content);
5576 }
5577 return;
5578 }
5579 CHECK_ARITY(1);
5580 CAST_TO_STRING;
5581 CHECK_TYPE(XPATH_STRING);
5582 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005583 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00005584 xmlXPathFreeObject(cur);
5585}
5586
5587/**
5588 * xmlXPathConcatFunction:
5589 * @ctxt: the XPath Parser context
5590 * @nargs: the number of arguments
5591 *
5592 * Implement the concat() XPath function
5593 * string concat(string, string, string*)
5594 * The concat function returns the concatenation of its arguments.
5595 */
5596void
5597xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5598 xmlXPathObjectPtr cur, newobj;
5599 xmlChar *tmp;
5600
5601 if (nargs < 2) {
5602 CHECK_ARITY(2);
5603 }
5604
5605 CAST_TO_STRING;
5606 cur = valuePop(ctxt);
5607 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
5608 xmlXPathFreeObject(cur);
5609 return;
5610 }
5611 nargs--;
5612
5613 while (nargs > 0) {
5614 CAST_TO_STRING;
5615 newobj = valuePop(ctxt);
5616 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
5617 xmlXPathFreeObject(newobj);
5618 xmlXPathFreeObject(cur);
5619 XP_ERROR(XPATH_INVALID_TYPE);
5620 }
5621 tmp = xmlStrcat(newobj->stringval, cur->stringval);
5622 newobj->stringval = cur->stringval;
5623 cur->stringval = tmp;
5624
5625 xmlXPathFreeObject(newobj);
5626 nargs--;
5627 }
5628 valuePush(ctxt, cur);
5629}
5630
5631/**
5632 * xmlXPathContainsFunction:
5633 * @ctxt: the XPath Parser context
5634 * @nargs: the number of arguments
5635 *
5636 * Implement the contains() XPath function
5637 * boolean contains(string, string)
5638 * The contains function returns true if the first argument string
5639 * contains the second argument string, and otherwise returns false.
5640 */
5641void
5642xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5643 xmlXPathObjectPtr hay, needle;
5644
5645 CHECK_ARITY(2);
5646 CAST_TO_STRING;
5647 CHECK_TYPE(XPATH_STRING);
5648 needle = valuePop(ctxt);
5649 CAST_TO_STRING;
5650 hay = valuePop(ctxt);
5651 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5652 xmlXPathFreeObject(hay);
5653 xmlXPathFreeObject(needle);
5654 XP_ERROR(XPATH_INVALID_TYPE);
5655 }
5656 if (xmlStrstr(hay->stringval, needle->stringval))
5657 valuePush(ctxt, xmlXPathNewBoolean(1));
5658 else
5659 valuePush(ctxt, xmlXPathNewBoolean(0));
5660 xmlXPathFreeObject(hay);
5661 xmlXPathFreeObject(needle);
5662}
5663
5664/**
5665 * xmlXPathStartsWithFunction:
5666 * @ctxt: the XPath Parser context
5667 * @nargs: the number of arguments
5668 *
5669 * Implement the starts-with() XPath function
5670 * boolean starts-with(string, string)
5671 * The starts-with function returns true if the first argument string
5672 * starts with the second argument string, and otherwise returns false.
5673 */
5674void
5675xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5676 xmlXPathObjectPtr hay, needle;
5677 int n;
5678
5679 CHECK_ARITY(2);
5680 CAST_TO_STRING;
5681 CHECK_TYPE(XPATH_STRING);
5682 needle = valuePop(ctxt);
5683 CAST_TO_STRING;
5684 hay = valuePop(ctxt);
5685 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5686 xmlXPathFreeObject(hay);
5687 xmlXPathFreeObject(needle);
5688 XP_ERROR(XPATH_INVALID_TYPE);
5689 }
5690 n = xmlStrlen(needle->stringval);
5691 if (xmlStrncmp(hay->stringval, needle->stringval, n))
5692 valuePush(ctxt, xmlXPathNewBoolean(0));
5693 else
5694 valuePush(ctxt, xmlXPathNewBoolean(1));
5695 xmlXPathFreeObject(hay);
5696 xmlXPathFreeObject(needle);
5697}
5698
5699/**
5700 * xmlXPathSubstringFunction:
5701 * @ctxt: the XPath Parser context
5702 * @nargs: the number of arguments
5703 *
5704 * Implement the substring() XPath function
5705 * string substring(string, number, number?)
5706 * The substring function returns the substring of the first argument
5707 * starting at the position specified in the second argument with
5708 * length specified in the third argument. For example,
5709 * substring("12345",2,3) returns "234". If the third argument is not
5710 * specified, it returns the substring starting at the position specified
5711 * in the second argument and continuing to the end of the string. For
5712 * example, substring("12345",2) returns "2345". More precisely, each
5713 * character in the string (see [3.6 Strings]) is considered to have a
5714 * numeric position: the position of the first character is 1, the position
5715 * of the second character is 2 and so on. The returned substring contains
5716 * those characters for which the position of the character is greater than
5717 * or equal to the second argument and, if the third argument is specified,
5718 * less than the sum of the second and third arguments; the comparisons
5719 * and addition used for the above follow the standard IEEE 754 rules. Thus:
5720 * - substring("12345", 1.5, 2.6) returns "234"
5721 * - substring("12345", 0, 3) returns "12"
5722 * - substring("12345", 0 div 0, 3) returns ""
5723 * - substring("12345", 1, 0 div 0) returns ""
5724 * - substring("12345", -42, 1 div 0) returns "12345"
5725 * - substring("12345", -1 div 0, 1 div 0) returns ""
5726 */
5727void
5728xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5729 xmlXPathObjectPtr str, start, len;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005730 double le=0, in;
5731 int i, l, m;
Owen Taylor3473f882001-02-23 17:55:21 +00005732 xmlChar *ret;
5733
Owen Taylor3473f882001-02-23 17:55:21 +00005734 if (nargs < 2) {
5735 CHECK_ARITY(2);
5736 }
5737 if (nargs > 3) {
5738 CHECK_ARITY(3);
5739 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005740 /*
5741 * take care of possible last (position) argument
5742 */
Owen Taylor3473f882001-02-23 17:55:21 +00005743 if (nargs == 3) {
5744 CAST_TO_NUMBER;
5745 CHECK_TYPE(XPATH_NUMBER);
5746 len = valuePop(ctxt);
5747 le = len->floatval;
5748 xmlXPathFreeObject(len);
Owen Taylor3473f882001-02-23 17:55:21 +00005749 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005750
Owen Taylor3473f882001-02-23 17:55:21 +00005751 CAST_TO_NUMBER;
5752 CHECK_TYPE(XPATH_NUMBER);
5753 start = valuePop(ctxt);
5754 in = start->floatval;
5755 xmlXPathFreeObject(start);
5756 CAST_TO_STRING;
5757 CHECK_TYPE(XPATH_STRING);
5758 str = valuePop(ctxt);
Daniel Veillard97ac1312001-05-30 19:14:17 +00005759 m = xmlUTF8Strlen((const unsigned char *)str->stringval);
Owen Taylor3473f882001-02-23 17:55:21 +00005760
Daniel Veillard97ac1312001-05-30 19:14:17 +00005761 /*
5762 * If last pos not present, calculate last position
5763 */
5764 if (nargs != 3)
5765 le = m;
5766
5767 /*
5768 * To meet our requirements, initial index calculations
5769 * must be done before we convert to integer format
5770 *
5771 * First we normalize indices
5772 */
5773 in -= 1.0;
5774 le += in;
5775 if (in < 0.0)
5776 in = 0.0;
5777 if (le > (double)m)
5778 le = (double)m;
5779
5780 /*
5781 * Now we go to integer form, rounding up
5782 */
Owen Taylor3473f882001-02-23 17:55:21 +00005783 i = (int) in;
5784 if (((double)i) != in) i++;
5785
Owen Taylor3473f882001-02-23 17:55:21 +00005786 l = (int) le;
5787 if (((double)l) != le) l++;
5788
Daniel Veillard97ac1312001-05-30 19:14:17 +00005789 if (l > m) l=m;
Owen Taylor3473f882001-02-23 17:55:21 +00005790
5791 /* number of chars to copy */
5792 l -= i;
5793
Daniel Veillard97ac1312001-05-30 19:14:17 +00005794 ret = xmlUTF8Strsub(str->stringval, i, l);
Owen Taylor3473f882001-02-23 17:55:21 +00005795 if (ret == NULL)
5796 valuePush(ctxt, xmlXPathNewCString(""));
5797 else {
5798 valuePush(ctxt, xmlXPathNewString(ret));
5799 xmlFree(ret);
5800 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005801
Owen Taylor3473f882001-02-23 17:55:21 +00005802 xmlXPathFreeObject(str);
5803}
5804
5805/**
5806 * xmlXPathSubstringBeforeFunction:
5807 * @ctxt: the XPath Parser context
5808 * @nargs: the number of arguments
5809 *
5810 * Implement the substring-before() XPath function
5811 * string substring-before(string, string)
5812 * The substring-before function returns the substring of the first
5813 * argument string that precedes the first occurrence of the second
5814 * argument string in the first argument string, or the empty string
5815 * if the first argument string does not contain the second argument
5816 * string. For example, substring-before("1999/04/01","/") returns 1999.
5817 */
5818void
5819xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5820 xmlXPathObjectPtr str;
5821 xmlXPathObjectPtr find;
5822 xmlBufferPtr target;
5823 const xmlChar *point;
5824 int offset;
5825
5826 CHECK_ARITY(2);
5827 CAST_TO_STRING;
5828 find = valuePop(ctxt);
5829 CAST_TO_STRING;
5830 str = valuePop(ctxt);
5831
5832 target = xmlBufferCreate();
5833 if (target) {
5834 point = xmlStrstr(str->stringval, find->stringval);
5835 if (point) {
5836 offset = (int)(point - str->stringval);
5837 xmlBufferAdd(target, str->stringval, offset);
5838 }
5839 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5840 xmlBufferFree(target);
5841 }
5842
5843 xmlXPathFreeObject(str);
5844 xmlXPathFreeObject(find);
5845}
5846
5847/**
5848 * xmlXPathSubstringAfterFunction:
5849 * @ctxt: the XPath Parser context
5850 * @nargs: the number of arguments
5851 *
5852 * Implement the substring-after() XPath function
5853 * string substring-after(string, string)
5854 * The substring-after function returns the substring of the first
5855 * argument string that follows the first occurrence of the second
5856 * argument string in the first argument string, or the empty stringi
5857 * if the first argument string does not contain the second argument
5858 * string. For example, substring-after("1999/04/01","/") returns 04/01,
5859 * and substring-after("1999/04/01","19") returns 99/04/01.
5860 */
5861void
5862xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5863 xmlXPathObjectPtr str;
5864 xmlXPathObjectPtr find;
5865 xmlBufferPtr target;
5866 const xmlChar *point;
5867 int offset;
5868
5869 CHECK_ARITY(2);
5870 CAST_TO_STRING;
5871 find = valuePop(ctxt);
5872 CAST_TO_STRING;
5873 str = valuePop(ctxt);
5874
5875 target = xmlBufferCreate();
5876 if (target) {
5877 point = xmlStrstr(str->stringval, find->stringval);
5878 if (point) {
5879 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
5880 xmlBufferAdd(target, &str->stringval[offset],
5881 xmlStrlen(str->stringval) - offset);
5882 }
5883 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5884 xmlBufferFree(target);
5885 }
5886
5887 xmlXPathFreeObject(str);
5888 xmlXPathFreeObject(find);
5889}
5890
5891/**
5892 * xmlXPathNormalizeFunction:
5893 * @ctxt: the XPath Parser context
5894 * @nargs: the number of arguments
5895 *
5896 * Implement the normalize-space() XPath function
5897 * string normalize-space(string?)
5898 * The normalize-space function returns the argument string with white
5899 * space normalized by stripping leading and trailing whitespace
5900 * and replacing sequences of whitespace characters by a single
5901 * space. Whitespace characters are the same allowed by the S production
5902 * in XML. If the argument is omitted, it defaults to the context
5903 * node converted to a string, in other words the value of the context node.
5904 */
5905void
5906xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5907 xmlXPathObjectPtr obj = NULL;
5908 xmlChar *source = NULL;
5909 xmlBufferPtr target;
5910 xmlChar blank;
5911
5912 if (nargs == 0) {
5913 /* Use current context node */
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005914 valuePush(ctxt,
5915 xmlXPathWrapString(
5916 xmlXPathCastNodeToString(ctxt->context->node)));
Owen Taylor3473f882001-02-23 17:55:21 +00005917 nargs = 1;
5918 }
5919
5920 CHECK_ARITY(1);
5921 CAST_TO_STRING;
5922 CHECK_TYPE(XPATH_STRING);
5923 obj = valuePop(ctxt);
5924 source = obj->stringval;
5925
5926 target = xmlBufferCreate();
5927 if (target && source) {
5928
5929 /* Skip leading whitespaces */
5930 while (IS_BLANK(*source))
5931 source++;
5932
5933 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
5934 blank = 0;
5935 while (*source) {
5936 if (IS_BLANK(*source)) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005937 blank = 0x20;
Owen Taylor3473f882001-02-23 17:55:21 +00005938 } else {
5939 if (blank) {
5940 xmlBufferAdd(target, &blank, 1);
5941 blank = 0;
5942 }
5943 xmlBufferAdd(target, source, 1);
5944 }
5945 source++;
5946 }
5947
5948 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5949 xmlBufferFree(target);
5950 }
5951 xmlXPathFreeObject(obj);
5952}
5953
5954/**
5955 * xmlXPathTranslateFunction:
5956 * @ctxt: the XPath Parser context
5957 * @nargs: the number of arguments
5958 *
5959 * Implement the translate() XPath function
5960 * string translate(string, string, string)
5961 * The translate function returns the first argument string with
5962 * occurrences of characters in the second argument string replaced
5963 * by the character at the corresponding position in the third argument
5964 * string. For example, translate("bar","abc","ABC") returns the string
5965 * BAr. If there is a character in the second argument string with no
5966 * character at a corresponding position in the third argument string
5967 * (because the second argument string is longer than the third argument
5968 * string), then occurrences of that character in the first argument
5969 * string are removed. For example, translate("--aaa--","abc-","ABC")
5970 * returns "AAA". If a character occurs more than once in second
5971 * argument string, then the first occurrence determines the replacement
5972 * character. If the third argument string is longer than the second
5973 * argument string, then excess characters are ignored.
5974 */
5975void
5976xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00005977 xmlXPathObjectPtr str;
5978 xmlXPathObjectPtr from;
5979 xmlXPathObjectPtr to;
5980 xmlBufferPtr target;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005981 int offset, max;
Daniel Veillarde043ee12001-04-16 14:08:07 +00005982 xmlChar ch;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005983 xmlChar *point;
5984 xmlChar *cptr;
Owen Taylor3473f882001-02-23 17:55:21 +00005985
Daniel Veillarde043ee12001-04-16 14:08:07 +00005986 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00005987
Daniel Veillarde043ee12001-04-16 14:08:07 +00005988 CAST_TO_STRING;
5989 to = valuePop(ctxt);
5990 CAST_TO_STRING;
5991 from = valuePop(ctxt);
5992 CAST_TO_STRING;
5993 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005994
Daniel Veillarde043ee12001-04-16 14:08:07 +00005995 target = xmlBufferCreate();
5996 if (target) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005997 max = xmlUTF8Strlen(to->stringval);
5998 for (cptr = str->stringval; (ch=*cptr); ) {
5999 offset = xmlUTF8Strloc(from->stringval, cptr);
6000 if (offset >= 0) {
6001 if (offset < max) {
6002 point = xmlUTF8Strpos(to->stringval, offset);
6003 if (point)
6004 xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1));
6005 }
6006 } else
6007 xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
6008
6009 /* Step to next character in input */
6010 cptr++;
6011 if ( ch & 0x80 ) {
6012 /* if not simple ascii, verify proper format */
6013 if ( (ch & 0xc0) != 0xc0 ) {
6014 xmlGenericError(xmlGenericErrorContext,
6015 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
6016 break;
6017 }
6018 /* then skip over remaining bytes for this char */
6019 while ( (ch <<= 1) & 0x80 )
6020 if ( (*cptr++ & 0xc0) != 0x80 ) {
6021 xmlGenericError(xmlGenericErrorContext,
6022 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
6023 break;
6024 }
6025 if (ch & 0x80) /* must have had error encountered */
6026 break;
6027 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00006028 }
Owen Taylor3473f882001-02-23 17:55:21 +00006029 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00006030 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
6031 xmlBufferFree(target);
6032 xmlXPathFreeObject(str);
6033 xmlXPathFreeObject(from);
6034 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00006035}
6036
6037/**
6038 * xmlXPathBooleanFunction:
6039 * @ctxt: the XPath Parser context
6040 * @nargs: the number of arguments
6041 *
6042 * Implement the boolean() XPath function
6043 * boolean boolean(object)
6044 * he boolean function converts its argument to a boolean as follows:
6045 * - a number is true if and only if it is neither positive or
6046 * negative zero nor NaN
6047 * - a node-set is true if and only if it is non-empty
6048 * - a string is true if and only if its length is non-zero
6049 */
6050void
6051xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6052 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00006053
6054 CHECK_ARITY(1);
6055 cur = valuePop(ctxt);
6056 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006057 cur = xmlXPathConvertBoolean(cur);
6058 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00006059}
6060
6061/**
6062 * xmlXPathNotFunction:
6063 * @ctxt: the XPath Parser context
6064 * @nargs: the number of arguments
6065 *
6066 * Implement the not() XPath function
6067 * boolean not(boolean)
6068 * The not function returns true if its argument is false,
6069 * and false otherwise.
6070 */
6071void
6072xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6073 CHECK_ARITY(1);
6074 CAST_TO_BOOLEAN;
6075 CHECK_TYPE(XPATH_BOOLEAN);
6076 ctxt->value->boolval = ! ctxt->value->boolval;
6077}
6078
6079/**
6080 * xmlXPathTrueFunction:
6081 * @ctxt: the XPath Parser context
6082 * @nargs: the number of arguments
6083 *
6084 * Implement the true() XPath function
6085 * boolean true()
6086 */
6087void
6088xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6089 CHECK_ARITY(0);
6090 valuePush(ctxt, xmlXPathNewBoolean(1));
6091}
6092
6093/**
6094 * xmlXPathFalseFunction:
6095 * @ctxt: the XPath Parser context
6096 * @nargs: the number of arguments
6097 *
6098 * Implement the false() XPath function
6099 * boolean false()
6100 */
6101void
6102xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6103 CHECK_ARITY(0);
6104 valuePush(ctxt, xmlXPathNewBoolean(0));
6105}
6106
6107/**
6108 * xmlXPathLangFunction:
6109 * @ctxt: the XPath Parser context
6110 * @nargs: the number of arguments
6111 *
6112 * Implement the lang() XPath function
6113 * boolean lang(string)
6114 * The lang function returns true or false depending on whether the
6115 * language of the context node as specified by xml:lang attributes
6116 * is the same as or is a sublanguage of the language specified by
6117 * the argument string. The language of the context node is determined
6118 * by the value of the xml:lang attribute on the context node, or, if
6119 * the context node has no xml:lang attribute, by the value of the
6120 * xml:lang attribute on the nearest ancestor of the context node that
6121 * has an xml:lang attribute. If there is no such attribute, then lang
6122 * returns false. If there is such an attribute, then lang returns
6123 * true if the attribute value is equal to the argument ignoring case,
6124 * or if there is some suffix starting with - such that the attribute
6125 * value is equal to the argument ignoring that suffix of the attribute
6126 * value and ignoring case.
6127 */
6128void
6129xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6130 xmlXPathObjectPtr val;
6131 const xmlChar *theLang;
6132 const xmlChar *lang;
6133 int ret = 0;
6134 int i;
6135
6136 CHECK_ARITY(1);
6137 CAST_TO_STRING;
6138 CHECK_TYPE(XPATH_STRING);
6139 val = valuePop(ctxt);
6140 lang = val->stringval;
6141 theLang = xmlNodeGetLang(ctxt->context->node);
6142 if ((theLang != NULL) && (lang != NULL)) {
6143 for (i = 0;lang[i] != 0;i++)
6144 if (toupper(lang[i]) != toupper(theLang[i]))
6145 goto not_equal;
6146 ret = 1;
6147 }
6148not_equal:
6149 xmlXPathFreeObject(val);
6150 valuePush(ctxt, xmlXPathNewBoolean(ret));
6151}
6152
6153/**
6154 * xmlXPathNumberFunction:
6155 * @ctxt: the XPath Parser context
6156 * @nargs: the number of arguments
6157 *
6158 * Implement the number() XPath function
6159 * number number(object?)
6160 */
6161void
6162xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6163 xmlXPathObjectPtr cur;
6164 double res;
6165
6166 if (nargs == 0) {
6167 if (ctxt->context->node == NULL) {
6168 valuePush(ctxt, xmlXPathNewFloat(0.0));
6169 } else {
6170 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
6171
6172 res = xmlXPathStringEvalNumber(content);
6173 valuePush(ctxt, xmlXPathNewFloat(res));
6174 xmlFree(content);
6175 }
6176 return;
6177 }
6178
6179 CHECK_ARITY(1);
6180 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006181 cur = xmlXPathConvertNumber(cur);
6182 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00006183}
6184
6185/**
6186 * xmlXPathSumFunction:
6187 * @ctxt: the XPath Parser context
6188 * @nargs: the number of arguments
6189 *
6190 * Implement the sum() XPath function
6191 * number sum(node-set)
6192 * The sum function returns the sum of the values of the nodes in
6193 * the argument node-set.
6194 */
6195void
6196xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6197 xmlXPathObjectPtr cur;
6198 int i;
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006199 double res = 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +00006200
6201 CHECK_ARITY(1);
6202 if ((ctxt->value == NULL) ||
6203 ((ctxt->value->type != XPATH_NODESET) &&
6204 (ctxt->value->type != XPATH_XSLT_TREE)))
6205 XP_ERROR(XPATH_INVALID_TYPE);
6206 cur = valuePop(ctxt);
6207
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006208 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006209 valuePush(ctxt, xmlXPathNewFloat(0.0));
6210 } else {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006211 for (i = 0; i < cur->nodesetval->nodeNr; i++) {
6212 res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00006213 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006214 valuePush(ctxt, xmlXPathNewFloat(res));
Owen Taylor3473f882001-02-23 17:55:21 +00006215 }
6216 xmlXPathFreeObject(cur);
6217}
6218
6219/**
6220 * xmlXPathFloorFunction:
6221 * @ctxt: the XPath Parser context
6222 * @nargs: the number of arguments
6223 *
6224 * Implement the floor() XPath function
6225 * number floor(number)
6226 * The floor function returns the largest (closest to positive infinity)
6227 * number that is not greater than the argument and that is an integer.
6228 */
6229void
6230xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6231 CHECK_ARITY(1);
6232 CAST_TO_NUMBER;
6233 CHECK_TYPE(XPATH_NUMBER);
6234#if 0
6235 ctxt->value->floatval = floor(ctxt->value->floatval);
6236#else
6237 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
6238 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
6239#endif
6240}
6241
6242/**
6243 * xmlXPathCeilingFunction:
6244 * @ctxt: the XPath Parser context
6245 * @nargs: the number of arguments
6246 *
6247 * Implement the ceiling() XPath function
6248 * number ceiling(number)
6249 * The ceiling function returns the smallest (closest to negative infinity)
6250 * number that is not less than the argument and that is an integer.
6251 */
6252void
6253xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6254 double f;
6255
6256 CHECK_ARITY(1);
6257 CAST_TO_NUMBER;
6258 CHECK_TYPE(XPATH_NUMBER);
6259
6260#if 0
6261 ctxt->value->floatval = ceil(ctxt->value->floatval);
6262#else
6263 f = (double)((int) ctxt->value->floatval);
6264 if (f != ctxt->value->floatval)
6265 ctxt->value->floatval = f + 1;
6266#endif
6267}
6268
6269/**
6270 * xmlXPathRoundFunction:
6271 * @ctxt: the XPath Parser context
6272 * @nargs: the number of arguments
6273 *
6274 * Implement the round() XPath function
6275 * number round(number)
6276 * The round function returns the number that is closest to the
6277 * argument and that is an integer. If there are two such numbers,
6278 * then the one that is even is returned.
6279 */
6280void
6281xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6282 double f;
6283
6284 CHECK_ARITY(1);
6285 CAST_TO_NUMBER;
6286 CHECK_TYPE(XPATH_NUMBER);
6287
6288 if ((ctxt->value->floatval == xmlXPathNAN) ||
6289 (ctxt->value->floatval == xmlXPathPINF) ||
6290 (ctxt->value->floatval == xmlXPathNINF) ||
6291 (ctxt->value->floatval == 0.0))
6292 return;
6293
6294#if 0
6295 f = floor(ctxt->value->floatval);
6296#else
6297 f = (double)((int) ctxt->value->floatval);
6298#endif
6299 if (ctxt->value->floatval < f + 0.5)
6300 ctxt->value->floatval = f;
6301 else
6302 ctxt->value->floatval = f + 1;
6303}
6304
6305/************************************************************************
6306 * *
6307 * The Parser *
6308 * *
6309 ************************************************************************/
6310
6311/*
6312 * a couple of forward declarations since we use a recursive call based
6313 * implementation.
6314 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006315static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006316static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006317static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006318#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006319static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
6320#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006321#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006322static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006323#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00006324static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
6325 int qualified);
Owen Taylor3473f882001-02-23 17:55:21 +00006326
6327/**
Daniel Veillard61d80a22001-04-27 17:13:01 +00006328 * xmlXPathCurrentChar:
6329 * @ctxt: the XPath parser context
6330 * @cur: pointer to the beginning of the char
6331 * @len: pointer to the length of the char read
6332 *
6333 * The current char value, if using UTF-8 this may actaully span multiple
6334 * bytes in the input buffer.
6335 *
6336 * Returns the current char value and its lenght
6337 */
6338
6339static int
6340xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
6341 unsigned char c;
6342 unsigned int val;
6343 const xmlChar *cur;
6344
6345 if (ctxt == NULL)
6346 return(0);
6347 cur = ctxt->cur;
6348
6349 /*
6350 * We are supposed to handle UTF8, check it's valid
6351 * From rfc2044: encoding of the Unicode values on UTF-8:
6352 *
6353 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
6354 * 0000 0000-0000 007F 0xxxxxxx
6355 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
6356 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
6357 *
6358 * Check for the 0x110000 limit too
6359 */
6360 c = *cur;
6361 if (c & 0x80) {
6362 if ((cur[1] & 0xc0) != 0x80)
6363 goto encoding_error;
6364 if ((c & 0xe0) == 0xe0) {
6365
6366 if ((cur[2] & 0xc0) != 0x80)
6367 goto encoding_error;
6368 if ((c & 0xf0) == 0xf0) {
6369 if (((c & 0xf8) != 0xf0) ||
6370 ((cur[3] & 0xc0) != 0x80))
6371 goto encoding_error;
6372 /* 4-byte code */
6373 *len = 4;
6374 val = (cur[0] & 0x7) << 18;
6375 val |= (cur[1] & 0x3f) << 12;
6376 val |= (cur[2] & 0x3f) << 6;
6377 val |= cur[3] & 0x3f;
6378 } else {
6379 /* 3-byte code */
6380 *len = 3;
6381 val = (cur[0] & 0xf) << 12;
6382 val |= (cur[1] & 0x3f) << 6;
6383 val |= cur[2] & 0x3f;
6384 }
6385 } else {
6386 /* 2-byte code */
6387 *len = 2;
6388 val = (cur[0] & 0x1f) << 6;
6389 val |= cur[1] & 0x3f;
6390 }
6391 if (!IS_CHAR(val)) {
6392 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
6393 }
6394 return(val);
6395 } else {
6396 /* 1-byte code */
6397 *len = 1;
6398 return((int) *cur);
6399 }
6400encoding_error:
6401 /*
6402 * If we detect an UTF8 error that probably mean that the
6403 * input encoding didn't get properly advertized in the
6404 * declaration header. Report the error and switch the encoding
6405 * to ISO-Latin-1 (if you don't like this policy, just declare the
6406 * encoding !)
6407 */
Daniel Veillard42596ad2001-05-22 16:57:14 +00006408 *len = 0;
Daniel Veillard61d80a22001-04-27 17:13:01 +00006409 XP_ERROR0(XPATH_ENCODING_ERROR);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006410}
6411
6412/**
Owen Taylor3473f882001-02-23 17:55:21 +00006413 * xmlXPathParseNCName:
6414 * @ctxt: the XPath Parser context
6415 *
6416 * parse an XML namespace non qualified name.
6417 *
6418 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
6419 *
6420 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
6421 * CombiningChar | Extender
6422 *
6423 * Returns the namespace name or NULL
6424 */
6425
6426xmlChar *
6427xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard2156a562001-04-28 12:24:34 +00006428 const xmlChar *in;
6429 xmlChar *ret;
6430 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006431
Daniel Veillard2156a562001-04-28 12:24:34 +00006432 /*
6433 * Accelerator for simple ASCII names
6434 */
6435 in = ctxt->cur;
6436 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6437 ((*in >= 0x41) && (*in <= 0x5A)) ||
6438 (*in == '_')) {
6439 in++;
6440 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6441 ((*in >= 0x41) && (*in <= 0x5A)) ||
6442 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard9a89a8a2001-06-27 11:13:35 +00006443 (*in == '_') || (*in == '.') ||
6444 (*in == '-'))
Daniel Veillard2156a562001-04-28 12:24:34 +00006445 in++;
6446 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
6447 (*in == '[') || (*in == ']') || (*in == ':') ||
6448 (*in == '@') || (*in == '*')) {
6449 count = in - ctxt->cur;
6450 if (count == 0)
6451 return(NULL);
6452 ret = xmlStrndup(ctxt->cur, count);
6453 ctxt->cur = in;
6454 return(ret);
6455 }
6456 }
6457 return(xmlXPathParseNameComplex(ctxt, 0));
Owen Taylor3473f882001-02-23 17:55:21 +00006458}
6459
Daniel Veillard2156a562001-04-28 12:24:34 +00006460
Owen Taylor3473f882001-02-23 17:55:21 +00006461/**
6462 * xmlXPathParseQName:
6463 * @ctxt: the XPath Parser context
6464 * @prefix: a xmlChar **
6465 *
6466 * parse an XML qualified name
6467 *
6468 * [NS 5] QName ::= (Prefix ':')? LocalPart
6469 *
6470 * [NS 6] Prefix ::= NCName
6471 *
6472 * [NS 7] LocalPart ::= NCName
6473 *
6474 * Returns the function returns the local part, and prefix is updated
6475 * to get the Prefix if any.
6476 */
6477
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006478static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00006479xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
6480 xmlChar *ret = NULL;
6481
6482 *prefix = NULL;
6483 ret = xmlXPathParseNCName(ctxt);
6484 if (CUR == ':') {
6485 *prefix = ret;
6486 NEXT;
6487 ret = xmlXPathParseNCName(ctxt);
6488 }
6489 return(ret);
6490}
6491
6492/**
6493 * xmlXPathParseName:
6494 * @ctxt: the XPath Parser context
6495 *
6496 * parse an XML name
6497 *
6498 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
6499 * CombiningChar | Extender
6500 *
6501 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
6502 *
6503 * Returns the namespace name or NULL
6504 */
6505
6506xmlChar *
6507xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006508 const xmlChar *in;
6509 xmlChar *ret;
6510 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006511
Daniel Veillard61d80a22001-04-27 17:13:01 +00006512 /*
6513 * Accelerator for simple ASCII names
6514 */
6515 in = ctxt->cur;
6516 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6517 ((*in >= 0x41) && (*in <= 0x5A)) ||
6518 (*in == '_') || (*in == ':')) {
6519 in++;
6520 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6521 ((*in >= 0x41) && (*in <= 0x5A)) ||
6522 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard76d66f42001-05-16 21:05:17 +00006523 (*in == '_') || (*in == '-') ||
6524 (*in == ':') || (*in == '.'))
Daniel Veillard61d80a22001-04-27 17:13:01 +00006525 in++;
Daniel Veillard76d66f42001-05-16 21:05:17 +00006526 if ((*in > 0) && (*in < 0x80)) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006527 count = in - ctxt->cur;
6528 ret = xmlStrndup(ctxt->cur, count);
6529 ctxt->cur = in;
6530 return(ret);
6531 }
6532 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006533 return(xmlXPathParseNameComplex(ctxt, 1));
Owen Taylor3473f882001-02-23 17:55:21 +00006534}
6535
Daniel Veillard61d80a22001-04-27 17:13:01 +00006536static xmlChar *
Daniel Veillard2156a562001-04-28 12:24:34 +00006537xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006538 xmlChar buf[XML_MAX_NAMELEN + 5];
6539 int len = 0, l;
6540 int c;
6541
6542 /*
6543 * Handler for more complex cases
6544 */
6545 c = CUR_CHAR(l);
6546 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
Daniel Veillard2156a562001-04-28 12:24:34 +00006547 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
6548 (c == '*') || /* accelerators */
Daniel Veillard61d80a22001-04-27 17:13:01 +00006549 (!IS_LETTER(c) && (c != '_') &&
Daniel Veillard2156a562001-04-28 12:24:34 +00006550 ((qualified) && (c != ':')))) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006551 return(NULL);
6552 }
6553
6554 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
6555 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
6556 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006557 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006558 (IS_COMBINING(c)) ||
6559 (IS_EXTENDER(c)))) {
6560 COPY_BUF(l,buf,len,c);
6561 NEXTL(l);
6562 c = CUR_CHAR(l);
6563 if (len >= XML_MAX_NAMELEN) {
6564 /*
6565 * Okay someone managed to make a huge name, so he's ready to pay
6566 * for the processing speed.
6567 */
6568 xmlChar *buffer;
6569 int max = len * 2;
6570
6571 buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
6572 if (buffer == NULL) {
6573 XP_ERROR0(XPATH_MEMORY_ERROR);
6574 }
6575 memcpy(buffer, buf, len);
6576 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
6577 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006578 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006579 (IS_COMBINING(c)) ||
6580 (IS_EXTENDER(c))) {
6581 if (len + 10 > max) {
6582 max *= 2;
6583 buffer = (xmlChar *) xmlRealloc(buffer,
6584 max * sizeof(xmlChar));
Daniel Veillard61d80a22001-04-27 17:13:01 +00006585 if (buffer == NULL) {
6586 XP_ERROR0(XPATH_MEMORY_ERROR);
6587 }
6588 }
6589 COPY_BUF(l,buffer,len,c);
6590 NEXTL(l);
6591 c = CUR_CHAR(l);
6592 }
6593 buffer[len] = 0;
6594 return(buffer);
6595 }
6596 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006597 if (len == 0)
6598 return(NULL);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006599 return(xmlStrndup(buf, len));
6600}
Owen Taylor3473f882001-02-23 17:55:21 +00006601/**
6602 * xmlXPathStringEvalNumber:
6603 * @str: A string to scan
6604 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00006605 * [30a] Float ::= Number ('e' Digits?)?
6606 *
Owen Taylor3473f882001-02-23 17:55:21 +00006607 * [30] Number ::= Digits ('.' Digits?)?
6608 * | '.' Digits
6609 * [31] Digits ::= [0-9]+
6610 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006611 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00006612 * In complement of the Number expression, this function also handles
6613 * negative values : '-' Number.
6614 *
6615 * Returns the double value.
6616 */
6617double
6618xmlXPathStringEvalNumber(const xmlChar *str) {
6619 const xmlChar *cur = str;
6620 double ret = 0.0;
6621 double mult = 1;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006622 int ok = 0, tmp = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006623 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006624 int exponent = 0;
6625 int is_exponent_negative = 0;
6626
Owen Taylor3473f882001-02-23 17:55:21 +00006627 while (IS_BLANK(*cur)) cur++;
6628 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
6629 return(xmlXPathNAN);
6630 }
6631 if (*cur == '-') {
6632 isneg = 1;
6633 cur++;
6634 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006635 /*
6636 * tmp is a workaroudn against a gcc compiler bug
6637 */
Owen Taylor3473f882001-02-23 17:55:21 +00006638 while ((*cur >= '0') && (*cur <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006639 tmp = tmp * 10 + (*cur - '0');
Owen Taylor3473f882001-02-23 17:55:21 +00006640 ok = 1;
6641 cur++;
6642 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006643 ret = (double) tmp;
6644
Owen Taylor3473f882001-02-23 17:55:21 +00006645 if (*cur == '.') {
6646 cur++;
6647 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
6648 return(xmlXPathNAN);
6649 }
6650 while ((*cur >= '0') && (*cur <= '9')) {
6651 mult /= 10;
6652 ret = ret + (*cur - '0') * mult;
6653 cur++;
6654 }
6655 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006656 if ((*cur == 'e') || (*cur == 'E')) {
6657 cur++;
6658 if (*cur == '-') {
6659 is_exponent_negative = 1;
6660 cur++;
6661 }
6662 while ((*cur >= '0') && (*cur <= '9')) {
6663 exponent = exponent * 10 + (*cur - '0');
6664 cur++;
6665 }
6666 }
Owen Taylor3473f882001-02-23 17:55:21 +00006667 while (IS_BLANK(*cur)) cur++;
6668 if (*cur != 0) return(xmlXPathNAN);
6669 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006670 if (is_exponent_negative) exponent = -exponent;
6671 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00006672 return(ret);
6673}
6674
6675/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006676 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00006677 * @ctxt: the XPath Parser context
6678 *
6679 * [30] Number ::= Digits ('.' Digits?)?
6680 * | '.' Digits
6681 * [31] Digits ::= [0-9]+
6682 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006683 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00006684 *
6685 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006686static void
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006687xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
6688{
Owen Taylor3473f882001-02-23 17:55:21 +00006689 double ret = 0.0;
6690 double mult = 1;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006691 int ok = 0, tmp = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006692 int exponent = 0;
6693 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006694
6695 CHECK_ERROR;
6696 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
6697 XP_ERROR(XPATH_NUMBER_ERROR);
6698 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006699 /*
6700 * Try to work around a gcc optimizer bug
6701 */
Owen Taylor3473f882001-02-23 17:55:21 +00006702 while ((CUR >= '0') && (CUR <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006703 tmp = tmp * 10 + (CUR - '0');
6704 ok = 1;
6705 NEXT;
Owen Taylor3473f882001-02-23 17:55:21 +00006706 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006707 ret = (double) tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00006708 if (CUR == '.') {
6709 NEXT;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006710 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
6711 XP_ERROR(XPATH_NUMBER_ERROR);
6712 }
6713 while ((CUR >= '0') && (CUR <= '9')) {
6714 mult /= 10;
6715 ret = ret + (CUR - '0') * mult;
6716 NEXT;
6717 }
Owen Taylor3473f882001-02-23 17:55:21 +00006718 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006719 if ((CUR == 'e') || (CUR == 'E')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006720 NEXT;
6721 if (CUR == '-') {
6722 is_exponent_negative = 1;
6723 NEXT;
6724 }
6725 while ((CUR >= '0') && (CUR <= '9')) {
6726 exponent = exponent * 10 + (CUR - '0');
6727 NEXT;
6728 }
6729 if (is_exponent_negative)
6730 exponent = -exponent;
6731 ret *= pow(10.0, (double) exponent);
Bjorn Reese70a9da52001-04-21 16:57:29 +00006732 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006733 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006734 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006735}
6736
6737/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006738 * xmlXPathParseLiteral:
6739 * @ctxt: the XPath Parser context
6740 *
6741 * Parse a Literal
6742 *
6743 * [29] Literal ::= '"' [^"]* '"'
6744 * | "'" [^']* "'"
6745 *
6746 * Returns the value found or NULL in case of error
6747 */
6748static xmlChar *
6749xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
6750 const xmlChar *q;
6751 xmlChar *ret = NULL;
6752
6753 if (CUR == '"') {
6754 NEXT;
6755 q = CUR_PTR;
6756 while ((IS_CHAR(CUR)) && (CUR != '"'))
6757 NEXT;
6758 if (!IS_CHAR(CUR)) {
6759 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6760 } else {
6761 ret = xmlStrndup(q, CUR_PTR - q);
6762 NEXT;
6763 }
6764 } else if (CUR == '\'') {
6765 NEXT;
6766 q = CUR_PTR;
6767 while ((IS_CHAR(CUR)) && (CUR != '\''))
6768 NEXT;
6769 if (!IS_CHAR(CUR)) {
6770 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6771 } else {
6772 ret = xmlStrndup(q, CUR_PTR - q);
6773 NEXT;
6774 }
6775 } else {
6776 XP_ERROR0(XPATH_START_LITERAL_ERROR);
6777 }
6778 return(ret);
6779}
6780
6781/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006782 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00006783 * @ctxt: the XPath Parser context
6784 *
6785 * Parse a Literal and push it on the stack.
6786 *
6787 * [29] Literal ::= '"' [^"]* '"'
6788 * | "'" [^']* "'"
6789 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006790 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00006791 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006792static void
6793xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006794 const xmlChar *q;
6795 xmlChar *ret = NULL;
6796
6797 if (CUR == '"') {
6798 NEXT;
6799 q = CUR_PTR;
6800 while ((IS_CHAR(CUR)) && (CUR != '"'))
6801 NEXT;
6802 if (!IS_CHAR(CUR)) {
6803 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6804 } else {
6805 ret = xmlStrndup(q, CUR_PTR - q);
6806 NEXT;
6807 }
6808 } else if (CUR == '\'') {
6809 NEXT;
6810 q = CUR_PTR;
6811 while ((IS_CHAR(CUR)) && (CUR != '\''))
6812 NEXT;
6813 if (!IS_CHAR(CUR)) {
6814 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6815 } else {
6816 ret = xmlStrndup(q, CUR_PTR - q);
6817 NEXT;
6818 }
6819 } else {
6820 XP_ERROR(XPATH_START_LITERAL_ERROR);
6821 }
6822 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006823 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
6824 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006825 xmlFree(ret);
6826}
6827
6828/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006829 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00006830 * @ctxt: the XPath Parser context
6831 *
6832 * Parse a VariableReference, evaluate it and push it on the stack.
6833 *
6834 * The variable bindings consist of a mapping from variable names
6835 * to variable values. The value of a variable is an object, which
6836 * of any of the types that are possible for the value of an expression,
6837 * and may also be of additional types not specified here.
6838 *
6839 * Early evaluation is possible since:
6840 * The variable bindings [...] used to evaluate a subexpression are
6841 * always the same as those used to evaluate the containing expression.
6842 *
6843 * [36] VariableReference ::= '$' QName
6844 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006845static void
6846xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006847 xmlChar *name;
6848 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006849
6850 SKIP_BLANKS;
6851 if (CUR != '$') {
6852 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6853 }
6854 NEXT;
6855 name = xmlXPathParseQName(ctxt, &prefix);
6856 if (name == NULL) {
6857 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6858 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006859 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006860 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
6861 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006862 SKIP_BLANKS;
6863}
6864
6865/**
6866 * xmlXPathIsNodeType:
6867 * @ctxt: the XPath Parser context
6868 * @name: a name string
6869 *
6870 * Is the name given a NodeType one.
6871 *
6872 * [38] NodeType ::= 'comment'
6873 * | 'text'
6874 * | 'processing-instruction'
6875 * | 'node'
6876 *
6877 * Returns 1 if true 0 otherwise
6878 */
6879int
6880xmlXPathIsNodeType(const xmlChar *name) {
6881 if (name == NULL)
6882 return(0);
6883
6884 if (xmlStrEqual(name, BAD_CAST "comment"))
6885 return(1);
6886 if (xmlStrEqual(name, BAD_CAST "text"))
6887 return(1);
6888 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6889 return(1);
6890 if (xmlStrEqual(name, BAD_CAST "node"))
6891 return(1);
6892 return(0);
6893}
6894
6895/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006896 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00006897 * @ctxt: the XPath Parser context
6898 *
6899 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
6900 * [17] Argument ::= Expr
6901 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006902 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00006903 * pushed on the stack
6904 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006905static void
6906xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006907 xmlChar *name;
6908 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006909 int nbargs = 0;
6910
6911 name = xmlXPathParseQName(ctxt, &prefix);
6912 if (name == NULL) {
6913 XP_ERROR(XPATH_EXPR_ERROR);
6914 }
6915 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006916#ifdef DEBUG_EXPR
6917 if (prefix == NULL)
6918 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
6919 name);
6920 else
6921 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
6922 prefix, name);
6923#endif
6924
Owen Taylor3473f882001-02-23 17:55:21 +00006925 if (CUR != '(') {
6926 XP_ERROR(XPATH_EXPR_ERROR);
6927 }
6928 NEXT;
6929 SKIP_BLANKS;
6930
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006931 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00006932 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006933 int op1 = ctxt->comp->last;
6934 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006935 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006936 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006937 nbargs++;
6938 if (CUR == ')') break;
6939 if (CUR != ',') {
6940 XP_ERROR(XPATH_EXPR_ERROR);
6941 }
6942 NEXT;
6943 SKIP_BLANKS;
6944 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006945 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
6946 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006947 NEXT;
6948 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006949}
6950
6951/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006952 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006953 * @ctxt: the XPath Parser context
6954 *
6955 * [15] PrimaryExpr ::= VariableReference
6956 * | '(' Expr ')'
6957 * | Literal
6958 * | Number
6959 * | FunctionCall
6960 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006961 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006962 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006963static void
6964xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006965 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006966 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006967 else if (CUR == '(') {
6968 NEXT;
6969 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006970 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006971 if (CUR != ')') {
6972 XP_ERROR(XPATH_EXPR_ERROR);
6973 }
6974 NEXT;
6975 SKIP_BLANKS;
6976 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006977 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006978 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006979 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006980 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006981 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006982 }
6983 SKIP_BLANKS;
6984}
6985
6986/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006987 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006988 * @ctxt: the XPath Parser context
6989 *
6990 * [20] FilterExpr ::= PrimaryExpr
6991 * | FilterExpr Predicate
6992 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006993 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006994 * Square brackets are used to filter expressions in the same way that
6995 * they are used in location paths. It is an error if the expression to
6996 * be filtered does not evaluate to a node-set. The context node list
6997 * used for evaluating the expression in square brackets is the node-set
6998 * to be filtered listed in document order.
6999 */
7000
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007001static void
7002xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
7003 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007004 CHECK_ERROR;
7005 SKIP_BLANKS;
7006
7007 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007008 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00007009 SKIP_BLANKS;
7010 }
7011
7012
7013}
7014
7015/**
7016 * xmlXPathScanName:
7017 * @ctxt: the XPath Parser context
7018 *
7019 * Trickery: parse an XML name but without consuming the input flow
7020 * Needed to avoid insanity in the parser state.
7021 *
7022 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
7023 * CombiningChar | Extender
7024 *
7025 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
7026 *
7027 * [6] Names ::= Name (S Name)*
7028 *
7029 * Returns the Name parsed or NULL
7030 */
7031
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007032static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00007033xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
7034 xmlChar buf[XML_MAX_NAMELEN];
7035 int len = 0;
7036
7037 SKIP_BLANKS;
7038 if (!IS_LETTER(CUR) && (CUR != '_') &&
7039 (CUR != ':')) {
7040 return(NULL);
7041 }
7042
7043 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
7044 (NXT(len) == '.') || (NXT(len) == '-') ||
7045 (NXT(len) == '_') || (NXT(len) == ':') ||
7046 (IS_COMBINING(NXT(len))) ||
7047 (IS_EXTENDER(NXT(len)))) {
7048 buf[len] = NXT(len);
7049 len++;
7050 if (len >= XML_MAX_NAMELEN) {
7051 xmlGenericError(xmlGenericErrorContext,
7052 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
7053 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
7054 (NXT(len) == '.') || (NXT(len) == '-') ||
7055 (NXT(len) == '_') || (NXT(len) == ':') ||
7056 (IS_COMBINING(NXT(len))) ||
7057 (IS_EXTENDER(NXT(len))))
7058 len++;
7059 break;
7060 }
7061 }
7062 return(xmlStrndup(buf, len));
7063}
7064
7065/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007066 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007067 * @ctxt: the XPath Parser context
7068 *
7069 * [19] PathExpr ::= LocationPath
7070 * | FilterExpr
7071 * | FilterExpr '/' RelativeLocationPath
7072 * | FilterExpr '//' RelativeLocationPath
7073 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007074 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007075 * The / operator and // operators combine an arbitrary expression
7076 * and a relative location path. It is an error if the expression
7077 * does not evaluate to a node-set.
7078 * The / operator does composition in the same way as when / is
7079 * used in a location path. As in location paths, // is short for
7080 * /descendant-or-self::node()/.
7081 */
7082
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007083static void
7084xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007085 int lc = 1; /* Should we branch to LocationPath ? */
7086 xmlChar *name = NULL; /* we may have to preparse a name to find out */
7087
7088 SKIP_BLANKS;
7089 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
7090 (CUR == '\'') || (CUR == '"')) {
7091 lc = 0;
7092 } else if (CUR == '*') {
7093 /* relative or absolute location path */
7094 lc = 1;
7095 } else if (CUR == '/') {
7096 /* relative or absolute location path */
7097 lc = 1;
7098 } else if (CUR == '@') {
7099 /* relative abbreviated attribute location path */
7100 lc = 1;
7101 } else if (CUR == '.') {
7102 /* relative abbreviated attribute location path */
7103 lc = 1;
7104 } else {
7105 /*
7106 * Problem is finding if we have a name here whether it's:
7107 * - a nodetype
7108 * - a function call in which case it's followed by '('
7109 * - an axis in which case it's followed by ':'
7110 * - a element name
7111 * We do an a priori analysis here rather than having to
7112 * maintain parsed token content through the recursive function
7113 * calls. This looks uglier but makes the code quite easier to
7114 * read/write/debug.
7115 */
7116 SKIP_BLANKS;
7117 name = xmlXPathScanName(ctxt);
7118 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
7119#ifdef DEBUG_STEP
7120 xmlGenericError(xmlGenericErrorContext,
7121 "PathExpr: Axis\n");
7122#endif
7123 lc = 1;
7124 xmlFree(name);
7125 } else if (name != NULL) {
7126 int len =xmlStrlen(name);
7127 int blank = 0;
7128
7129
7130 while (NXT(len) != 0) {
7131 if (NXT(len) == '/') {
7132 /* element name */
7133#ifdef DEBUG_STEP
7134 xmlGenericError(xmlGenericErrorContext,
7135 "PathExpr: AbbrRelLocation\n");
7136#endif
7137 lc = 1;
7138 break;
7139 } else if (IS_BLANK(NXT(len))) {
7140 /* skip to next */
7141 blank = 1;
7142 } else if (NXT(len) == ':') {
7143#ifdef DEBUG_STEP
7144 xmlGenericError(xmlGenericErrorContext,
7145 "PathExpr: AbbrRelLocation\n");
7146#endif
7147 lc = 1;
7148 break;
7149 } else if ((NXT(len) == '(')) {
7150 /* Note Type or Function */
7151 if (xmlXPathIsNodeType(name)) {
7152#ifdef DEBUG_STEP
7153 xmlGenericError(xmlGenericErrorContext,
7154 "PathExpr: Type search\n");
7155#endif
7156 lc = 1;
7157 } else {
7158#ifdef DEBUG_STEP
7159 xmlGenericError(xmlGenericErrorContext,
7160 "PathExpr: function call\n");
7161#endif
7162 lc = 0;
7163 }
7164 break;
7165 } else if ((NXT(len) == '[')) {
7166 /* element name */
7167#ifdef DEBUG_STEP
7168 xmlGenericError(xmlGenericErrorContext,
7169 "PathExpr: AbbrRelLocation\n");
7170#endif
7171 lc = 1;
7172 break;
7173 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
7174 (NXT(len) == '=')) {
7175 lc = 1;
7176 break;
7177 } else {
7178 lc = 1;
7179 break;
7180 }
7181 len++;
7182 }
7183 if (NXT(len) == 0) {
7184#ifdef DEBUG_STEP
7185 xmlGenericError(xmlGenericErrorContext,
7186 "PathExpr: AbbrRelLocation\n");
7187#endif
7188 /* element name */
7189 lc = 1;
7190 }
7191 xmlFree(name);
7192 } else {
7193 /* make sure all cases are covered explicitely */
7194 XP_ERROR(XPATH_EXPR_ERROR);
7195 }
7196 }
7197
7198 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007199 if (CUR == '/') {
7200 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
7201 } else {
7202 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007203 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007204 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007205 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007206 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007207 CHECK_ERROR;
7208 if ((CUR == '/') && (NXT(1) == '/')) {
7209 SKIP(2);
7210 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007211
7212 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7213 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
7214 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
7215
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007216 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007217 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007218 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007219 }
7220 }
7221 SKIP_BLANKS;
7222}
7223
7224/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007225 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007226 * @ctxt: the XPath Parser context
7227 *
7228 * [18] UnionExpr ::= PathExpr
7229 * | UnionExpr '|' PathExpr
7230 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007231 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007232 */
7233
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007234static void
7235xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
7236 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007237 CHECK_ERROR;
7238 SKIP_BLANKS;
7239 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007240 int op1 = ctxt->comp->last;
7241 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007242
7243 NEXT;
7244 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007245 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007246
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007247 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
7248
Owen Taylor3473f882001-02-23 17:55:21 +00007249 SKIP_BLANKS;
7250 }
Owen Taylor3473f882001-02-23 17:55:21 +00007251}
7252
7253/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007254 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007255 * @ctxt: the XPath Parser context
7256 *
7257 * [27] UnaryExpr ::= UnionExpr
7258 * | '-' UnaryExpr
7259 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007260 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007261 */
7262
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007263static void
7264xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007265 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007266 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007267
7268 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00007269 while (CUR == '-') {
7270 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007271 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007272 NEXT;
7273 SKIP_BLANKS;
7274 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007275
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007276 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007277 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007278 if (found) {
7279 if (minus)
7280 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
7281 else
7282 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007283 }
7284}
7285
7286/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007287 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007288 * @ctxt: the XPath Parser context
7289 *
7290 * [26] MultiplicativeExpr ::= UnaryExpr
7291 * | MultiplicativeExpr MultiplyOperator UnaryExpr
7292 * | MultiplicativeExpr 'div' UnaryExpr
7293 * | MultiplicativeExpr 'mod' UnaryExpr
7294 * [34] MultiplyOperator ::= '*'
7295 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007296 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007297 */
7298
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007299static void
7300xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
7301 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007302 CHECK_ERROR;
7303 SKIP_BLANKS;
7304 while ((CUR == '*') ||
7305 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
7306 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
7307 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007308 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007309
7310 if (CUR == '*') {
7311 op = 0;
7312 NEXT;
7313 } else if (CUR == 'd') {
7314 op = 1;
7315 SKIP(3);
7316 } else if (CUR == 'm') {
7317 op = 2;
7318 SKIP(3);
7319 }
7320 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007321 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007322 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007323 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007324 SKIP_BLANKS;
7325 }
7326}
7327
7328/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007329 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007330 * @ctxt: the XPath Parser context
7331 *
7332 * [25] AdditiveExpr ::= MultiplicativeExpr
7333 * | AdditiveExpr '+' MultiplicativeExpr
7334 * | AdditiveExpr '-' MultiplicativeExpr
7335 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007336 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007337 */
7338
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007339static void
7340xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007341
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007342 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007343 CHECK_ERROR;
7344 SKIP_BLANKS;
7345 while ((CUR == '+') || (CUR == '-')) {
7346 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007347 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007348
7349 if (CUR == '+') plus = 1;
7350 else plus = 0;
7351 NEXT;
7352 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007353 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007354 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007355 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007356 SKIP_BLANKS;
7357 }
7358}
7359
7360/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007361 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007362 * @ctxt: the XPath Parser context
7363 *
7364 * [24] RelationalExpr ::= AdditiveExpr
7365 * | RelationalExpr '<' AdditiveExpr
7366 * | RelationalExpr '>' AdditiveExpr
7367 * | RelationalExpr '<=' AdditiveExpr
7368 * | RelationalExpr '>=' AdditiveExpr
7369 *
7370 * A <= B > C is allowed ? Answer from James, yes with
7371 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
7372 * which is basically what got implemented.
7373 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007374 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00007375 * on the stack
7376 */
7377
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007378static void
7379xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
7380 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007381 CHECK_ERROR;
7382 SKIP_BLANKS;
7383 while ((CUR == '<') ||
7384 (CUR == '>') ||
7385 ((CUR == '<') && (NXT(1) == '=')) ||
7386 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007387 int inf, strict;
7388 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007389
7390 if (CUR == '<') inf = 1;
7391 else inf = 0;
7392 if (NXT(1) == '=') strict = 0;
7393 else strict = 1;
7394 NEXT;
7395 if (!strict) NEXT;
7396 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007397 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007398 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007399 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00007400 SKIP_BLANKS;
7401 }
7402}
7403
7404/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007405 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007406 * @ctxt: the XPath Parser context
7407 *
7408 * [23] EqualityExpr ::= RelationalExpr
7409 * | EqualityExpr '=' RelationalExpr
7410 * | EqualityExpr '!=' RelationalExpr
7411 *
7412 * A != B != C is allowed ? Answer from James, yes with
7413 * (RelationalExpr = RelationalExpr) = RelationalExpr
7414 * (RelationalExpr != RelationalExpr) != RelationalExpr
7415 * which is basically what got implemented.
7416 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007417 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007418 *
7419 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007420static void
7421xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
7422 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007423 CHECK_ERROR;
7424 SKIP_BLANKS;
7425 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007426 int eq;
7427 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007428
7429 if (CUR == '=') eq = 1;
7430 else eq = 0;
7431 NEXT;
7432 if (!eq) NEXT;
7433 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007434 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007435 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007436 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007437 SKIP_BLANKS;
7438 }
7439}
7440
7441/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007442 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007443 * @ctxt: the XPath Parser context
7444 *
7445 * [22] AndExpr ::= EqualityExpr
7446 * | AndExpr 'and' EqualityExpr
7447 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007448 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007449 *
7450 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007451static void
7452xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
7453 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007454 CHECK_ERROR;
7455 SKIP_BLANKS;
7456 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007457 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007458 SKIP(3);
7459 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007460 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007461 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007462 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007463 SKIP_BLANKS;
7464 }
7465}
7466
7467/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007468 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007469 * @ctxt: the XPath Parser context
7470 *
7471 * [14] Expr ::= OrExpr
7472 * [21] OrExpr ::= AndExpr
7473 * | OrExpr 'or' AndExpr
7474 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007475 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00007476 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007477static void
7478xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
7479 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007480 CHECK_ERROR;
7481 SKIP_BLANKS;
7482 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007483 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007484 SKIP(2);
7485 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007486 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007487 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007488 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
7489 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00007490 SKIP_BLANKS;
7491 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007492 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
7493 /* more ops could be optimized too */
7494 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
7495 }
Owen Taylor3473f882001-02-23 17:55:21 +00007496}
7497
7498/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007499 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00007500 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007501 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00007502 *
7503 * [8] Predicate ::= '[' PredicateExpr ']'
7504 * [9] PredicateExpr ::= Expr
7505 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007506 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00007507 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007508static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007509xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007510 int op1 = ctxt->comp->last;
7511
7512 SKIP_BLANKS;
7513 if (CUR != '[') {
7514 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7515 }
7516 NEXT;
7517 SKIP_BLANKS;
7518
7519 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007520 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007521 CHECK_ERROR;
7522
7523 if (CUR != ']') {
7524 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7525 }
7526
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007527 if (filter)
7528 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
7529 else
7530 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007531
7532 NEXT;
7533 SKIP_BLANKS;
7534}
7535
7536/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007537 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00007538 * @ctxt: the XPath Parser context
7539 * @test: pointer to a xmlXPathTestVal
7540 * @type: pointer to a xmlXPathTypeVal
7541 * @prefix: placeholder for a possible name prefix
7542 *
7543 * [7] NodeTest ::= NameTest
7544 * | NodeType '(' ')'
7545 * | 'processing-instruction' '(' Literal ')'
7546 *
7547 * [37] NameTest ::= '*'
7548 * | NCName ':' '*'
7549 * | QName
7550 * [38] NodeType ::= 'comment'
7551 * | 'text'
7552 * | 'processing-instruction'
7553 * | 'node'
7554 *
7555 * Returns the name found and update @test, @type and @prefix appropriately
7556 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007557static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007558xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
7559 xmlXPathTypeVal *type, const xmlChar **prefix,
7560 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00007561 int blanks;
7562
7563 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
7564 STRANGE;
7565 return(NULL);
7566 }
7567 *type = 0;
7568 *test = 0;
7569 *prefix = NULL;
7570 SKIP_BLANKS;
7571
7572 if ((name == NULL) && (CUR == '*')) {
7573 /*
7574 * All elements
7575 */
7576 NEXT;
7577 *test = NODE_TEST_ALL;
7578 return(NULL);
7579 }
7580
7581 if (name == NULL)
7582 name = xmlXPathParseNCName(ctxt);
7583 if (name == NULL) {
7584 XP_ERROR0(XPATH_EXPR_ERROR);
7585 }
7586
7587 blanks = IS_BLANK(CUR);
7588 SKIP_BLANKS;
7589 if (CUR == '(') {
7590 NEXT;
7591 /*
7592 * NodeType or PI search
7593 */
7594 if (xmlStrEqual(name, BAD_CAST "comment"))
7595 *type = NODE_TYPE_COMMENT;
7596 else if (xmlStrEqual(name, BAD_CAST "node"))
7597 *type = NODE_TYPE_NODE;
7598 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
7599 *type = NODE_TYPE_PI;
7600 else if (xmlStrEqual(name, BAD_CAST "text"))
7601 *type = NODE_TYPE_TEXT;
7602 else {
7603 if (name != NULL)
7604 xmlFree(name);
7605 XP_ERROR0(XPATH_EXPR_ERROR);
7606 }
7607
7608 *test = NODE_TEST_TYPE;
7609
7610 SKIP_BLANKS;
7611 if (*type == NODE_TYPE_PI) {
7612 /*
7613 * Specific case: search a PI by name.
7614 */
Owen Taylor3473f882001-02-23 17:55:21 +00007615 if (name != NULL)
7616 xmlFree(name);
Daniel Veillard82e49712001-04-26 14:38:03 +00007617 name = NULL;
7618 if (CUR != ')') {
7619 name = xmlXPathParseLiteral(ctxt);
7620 CHECK_ERROR 0;
7621 SKIP_BLANKS;
7622 }
Owen Taylor3473f882001-02-23 17:55:21 +00007623 }
7624 if (CUR != ')') {
7625 if (name != NULL)
7626 xmlFree(name);
7627 XP_ERROR0(XPATH_UNCLOSED_ERROR);
7628 }
7629 NEXT;
7630 return(name);
7631 }
7632 *test = NODE_TEST_NAME;
7633 if ((!blanks) && (CUR == ':')) {
7634 NEXT;
7635
7636 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007637 * Since currently the parser context don't have a
7638 * namespace list associated:
7639 * The namespace name for this prefix can be computed
7640 * only at evaluation time. The compilation is done
7641 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00007642 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007643#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00007644 *prefix = xmlXPathNsLookup(ctxt->context, name);
7645 if (name != NULL)
7646 xmlFree(name);
7647 if (*prefix == NULL) {
7648 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
7649 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007650#else
7651 *prefix = name;
7652#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007653
7654 if (CUR == '*') {
7655 /*
7656 * All elements
7657 */
7658 NEXT;
7659 *test = NODE_TEST_ALL;
7660 return(NULL);
7661 }
7662
7663 name = xmlXPathParseNCName(ctxt);
7664 if (name == NULL) {
7665 XP_ERROR0(XPATH_EXPR_ERROR);
7666 }
7667 }
7668 return(name);
7669}
7670
7671/**
7672 * xmlXPathIsAxisName:
7673 * @name: a preparsed name token
7674 *
7675 * [6] AxisName ::= 'ancestor'
7676 * | 'ancestor-or-self'
7677 * | 'attribute'
7678 * | 'child'
7679 * | 'descendant'
7680 * | 'descendant-or-self'
7681 * | 'following'
7682 * | 'following-sibling'
7683 * | 'namespace'
7684 * | 'parent'
7685 * | 'preceding'
7686 * | 'preceding-sibling'
7687 * | 'self'
7688 *
7689 * Returns the axis or 0
7690 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007691static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00007692xmlXPathIsAxisName(const xmlChar *name) {
7693 xmlXPathAxisVal ret = 0;
7694 switch (name[0]) {
7695 case 'a':
7696 if (xmlStrEqual(name, BAD_CAST "ancestor"))
7697 ret = AXIS_ANCESTOR;
7698 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
7699 ret = AXIS_ANCESTOR_OR_SELF;
7700 if (xmlStrEqual(name, BAD_CAST "attribute"))
7701 ret = AXIS_ATTRIBUTE;
7702 break;
7703 case 'c':
7704 if (xmlStrEqual(name, BAD_CAST "child"))
7705 ret = AXIS_CHILD;
7706 break;
7707 case 'd':
7708 if (xmlStrEqual(name, BAD_CAST "descendant"))
7709 ret = AXIS_DESCENDANT;
7710 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
7711 ret = AXIS_DESCENDANT_OR_SELF;
7712 break;
7713 case 'f':
7714 if (xmlStrEqual(name, BAD_CAST "following"))
7715 ret = AXIS_FOLLOWING;
7716 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
7717 ret = AXIS_FOLLOWING_SIBLING;
7718 break;
7719 case 'n':
7720 if (xmlStrEqual(name, BAD_CAST "namespace"))
7721 ret = AXIS_NAMESPACE;
7722 break;
7723 case 'p':
7724 if (xmlStrEqual(name, BAD_CAST "parent"))
7725 ret = AXIS_PARENT;
7726 if (xmlStrEqual(name, BAD_CAST "preceding"))
7727 ret = AXIS_PRECEDING;
7728 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
7729 ret = AXIS_PRECEDING_SIBLING;
7730 break;
7731 case 's':
7732 if (xmlStrEqual(name, BAD_CAST "self"))
7733 ret = AXIS_SELF;
7734 break;
7735 }
7736 return(ret);
7737}
7738
7739/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007740 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00007741 * @ctxt: the XPath Parser context
7742 *
7743 * [4] Step ::= AxisSpecifier NodeTest Predicate*
7744 * | AbbreviatedStep
7745 *
7746 * [12] AbbreviatedStep ::= '.' | '..'
7747 *
7748 * [5] AxisSpecifier ::= AxisName '::'
7749 * | AbbreviatedAxisSpecifier
7750 *
7751 * [13] AbbreviatedAxisSpecifier ::= '@'?
7752 *
7753 * Modified for XPtr range support as:
7754 *
7755 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
7756 * | AbbreviatedStep
7757 * | 'range-to' '(' Expr ')' Predicate*
7758 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007759 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00007760 * A location step of . is short for self::node(). This is
7761 * particularly useful in conjunction with //. For example, the
7762 * location path .//para is short for
7763 * self::node()/descendant-or-self::node()/child::para
7764 * and so will select all para descendant elements of the context
7765 * node.
7766 * Similarly, a location step of .. is short for parent::node().
7767 * For example, ../title is short for parent::node()/child::title
7768 * and so will select the title children of the parent of the context
7769 * node.
7770 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007771static void
7772xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007773#ifdef LIBXML_XPTR_ENABLED
7774 int rangeto = 0;
7775 int op2 = -1;
7776#endif
7777
Owen Taylor3473f882001-02-23 17:55:21 +00007778 SKIP_BLANKS;
7779 if ((CUR == '.') && (NXT(1) == '.')) {
7780 SKIP(2);
7781 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007782 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
7783 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007784 } else if (CUR == '.') {
7785 NEXT;
7786 SKIP_BLANKS;
7787 } else {
7788 xmlChar *name = NULL;
7789 const xmlChar *prefix = NULL;
7790 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007791 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007792 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007793 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00007794
7795 /*
7796 * The modification needed for XPointer change to the production
7797 */
7798#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007799 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00007800 name = xmlXPathParseNCName(ctxt);
7801 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007802 op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007803 xmlFree(name);
7804 SKIP_BLANKS;
7805 if (CUR != '(') {
7806 XP_ERROR(XPATH_EXPR_ERROR);
7807 }
7808 NEXT;
7809 SKIP_BLANKS;
7810
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007811 xmlXPathCompileExpr(ctxt);
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007812 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
Owen Taylor3473f882001-02-23 17:55:21 +00007813 CHECK_ERROR;
7814
7815 SKIP_BLANKS;
7816 if (CUR != ')') {
7817 XP_ERROR(XPATH_EXPR_ERROR);
7818 }
7819 NEXT;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007820 rangeto = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007821 goto eval_predicates;
7822 }
7823 }
7824#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00007825 if (CUR == '*') {
7826 axis = AXIS_CHILD;
7827 } else {
7828 if (name == NULL)
7829 name = xmlXPathParseNCName(ctxt);
7830 if (name != NULL) {
7831 axis = xmlXPathIsAxisName(name);
7832 if (axis != 0) {
7833 SKIP_BLANKS;
7834 if ((CUR == ':') && (NXT(1) == ':')) {
7835 SKIP(2);
7836 xmlFree(name);
7837 name = NULL;
7838 } else {
7839 /* an element name can conflict with an axis one :-\ */
7840 axis = AXIS_CHILD;
7841 }
Owen Taylor3473f882001-02-23 17:55:21 +00007842 } else {
Owen Taylor3473f882001-02-23 17:55:21 +00007843 axis = AXIS_CHILD;
7844 }
Daniel Veillard2156a562001-04-28 12:24:34 +00007845 } else if (CUR == '@') {
7846 NEXT;
7847 axis = AXIS_ATTRIBUTE;
Owen Taylor3473f882001-02-23 17:55:21 +00007848 } else {
Daniel Veillard2156a562001-04-28 12:24:34 +00007849 axis = AXIS_CHILD;
Owen Taylor3473f882001-02-23 17:55:21 +00007850 }
Owen Taylor3473f882001-02-23 17:55:21 +00007851 }
7852
7853 CHECK_ERROR;
7854
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007855 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00007856 if (test == 0)
7857 return;
7858
7859#ifdef DEBUG_STEP
7860 xmlGenericError(xmlGenericErrorContext,
7861 "Basis : computing new set\n");
7862#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007863
Owen Taylor3473f882001-02-23 17:55:21 +00007864#ifdef DEBUG_STEP
7865 xmlGenericError(xmlGenericErrorContext, "Basis : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007866 if (ctxt->value == NULL)
7867 xmlGenericError(xmlGenericErrorContext, "no value\n");
7868 else if (ctxt->value->nodesetval == NULL)
7869 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7870 else
7871 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007872#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007873
7874eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007875 op1 = ctxt->comp->last;
7876 ctxt->comp->last = -1;
7877
Owen Taylor3473f882001-02-23 17:55:21 +00007878 SKIP_BLANKS;
7879 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007880 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007881 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007882
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007883#ifdef LIBXML_XPTR_ENABLED
7884 if (rangeto) {
7885 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
7886 } else
7887#endif
7888 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
7889 test, type, (void *)prefix, (void *)name);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007890
Owen Taylor3473f882001-02-23 17:55:21 +00007891 }
7892#ifdef DEBUG_STEP
7893 xmlGenericError(xmlGenericErrorContext, "Step : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007894 if (ctxt->value == NULL)
7895 xmlGenericError(xmlGenericErrorContext, "no value\n");
7896 else if (ctxt->value->nodesetval == NULL)
7897 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7898 else
7899 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
7900 ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007901#endif
7902}
7903
7904/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007905 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007906 * @ctxt: the XPath Parser context
7907 *
7908 * [3] RelativeLocationPath ::= Step
7909 * | RelativeLocationPath '/' Step
7910 * | AbbreviatedRelativeLocationPath
7911 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
7912 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007913 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00007914 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007915static void
Owen Taylor3473f882001-02-23 17:55:21 +00007916#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007917xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007918#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007919xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007920#endif
7921(xmlXPathParserContextPtr ctxt) {
7922 SKIP_BLANKS;
7923 if ((CUR == '/') && (NXT(1) == '/')) {
7924 SKIP(2);
7925 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007926 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7927 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007928 } else if (CUR == '/') {
7929 NEXT;
7930 SKIP_BLANKS;
7931 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007932 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007933 SKIP_BLANKS;
7934 while (CUR == '/') {
7935 if ((CUR == '/') && (NXT(1) == '/')) {
7936 SKIP(2);
7937 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007938 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00007939 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007940 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007941 } else if (CUR == '/') {
7942 NEXT;
7943 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007944 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007945 }
7946 SKIP_BLANKS;
7947 }
7948}
7949
7950/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007951 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007952 * @ctxt: the XPath Parser context
7953 *
7954 * [1] LocationPath ::= RelativeLocationPath
7955 * | AbsoluteLocationPath
7956 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
7957 * | AbbreviatedAbsoluteLocationPath
7958 * [10] AbbreviatedAbsoluteLocationPath ::=
7959 * '//' RelativeLocationPath
7960 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007961 * Compile a location path
7962 *
Owen Taylor3473f882001-02-23 17:55:21 +00007963 * // is short for /descendant-or-self::node()/. For example,
7964 * //para is short for /descendant-or-self::node()/child::para and
7965 * so will select any para element in the document (even a para element
7966 * that is a document element will be selected by //para since the
7967 * document element node is a child of the root node); div//para is
7968 * short for div/descendant-or-self::node()/child::para and so will
7969 * select all para descendants of div children.
7970 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007971static void
7972xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007973 SKIP_BLANKS;
7974 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007975 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007976 } else {
7977 while (CUR == '/') {
7978 if ((CUR == '/') && (NXT(1) == '/')) {
7979 SKIP(2);
7980 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007981 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7982 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007983 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007984 } else if (CUR == '/') {
7985 NEXT;
Daniel Veillard608ad072001-06-14 08:32:28 +00007986 SKIP_BLANKS;
7987 if ((CUR != 0 ) &&
7988 ((IS_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
7989 (CUR == '@') || (CUR == '*')))
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007990 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007991 }
7992 }
7993 }
7994}
7995
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007996/************************************************************************
7997 * *
7998 * XPath precompiled expression evaluation *
7999 * *
8000 ************************************************************************/
8001
Daniel Veillardf06307e2001-07-03 10:35:50 +00008002static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008003xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
8004
8005/**
8006 * xmlXPathNodeCollectAndTest:
8007 * @ctxt: the XPath Parser context
8008 * @op: the XPath precompiled step operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00008009 * @first: pointer to the first element in document order
8010 * @last: pointer to the last element in document order
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008011 *
8012 * This is the function implementing a step: based on the current list
8013 * of nodes, it builds up a new list, looking at all nodes under that
8014 * axis and selecting them it also do the predicate filtering
8015 *
8016 * Pushes the new NodeSet resulting from the search.
Daniel Veillardf06307e2001-07-03 10:35:50 +00008017 *
8018 * Returns the number of node traversed
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008019 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00008020static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008021xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008022 xmlXPathStepOpPtr op,
8023 xmlNodePtr * first, xmlNodePtr * last)
8024{
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008025 xmlXPathAxisVal axis = op->value;
8026 xmlXPathTestVal test = op->value2;
8027 xmlXPathTypeVal type = op->value3;
8028 const xmlChar *prefix = op->value4;
8029 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00008030 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008031
8032#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008033 int n = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008034#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008035 int i, t = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008036 xmlNodeSetPtr ret, list;
8037 xmlXPathTraversalFunction next = NULL;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008038 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008039 xmlNodePtr cur = NULL;
8040 xmlXPathObjectPtr obj;
8041 xmlNodeSetPtr nodelist;
8042 xmlNodePtr tmp;
8043
Daniel Veillardf06307e2001-07-03 10:35:50 +00008044 CHECK_TYPE0(XPATH_NODESET);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008045 obj = valuePop(ctxt);
8046 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00008047 if (prefix != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008048 URI = xmlXPathNsLookup(ctxt->context, prefix);
8049 if (URI == NULL)
8050 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
Daniel Veillarde043ee12001-04-16 14:08:07 +00008051 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008052#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008053 xmlGenericError(xmlGenericErrorContext, "new step : ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008054#endif
8055 switch (axis) {
8056 case AXIS_ANCESTOR:
8057#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008058 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008059#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008060 first = NULL;
8061 next = xmlXPathNextAncestor;
8062 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008063 case AXIS_ANCESTOR_OR_SELF:
8064#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008065 xmlGenericError(xmlGenericErrorContext,
8066 "axis 'ancestors-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008067#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008068 first = NULL;
8069 next = xmlXPathNextAncestorOrSelf;
8070 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008071 case AXIS_ATTRIBUTE:
8072#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008073 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008074#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008075 first = NULL;
8076 last = NULL;
8077 next = xmlXPathNextAttribute;
8078 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008079 case AXIS_CHILD:
8080#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008081 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008082#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008083 last = NULL;
8084 next = xmlXPathNextChild;
8085 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008086 case AXIS_DESCENDANT:
8087#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008088 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008089#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008090 last = NULL;
8091 next = xmlXPathNextDescendant;
8092 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008093 case AXIS_DESCENDANT_OR_SELF:
8094#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008095 xmlGenericError(xmlGenericErrorContext,
8096 "axis 'descendant-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008097#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008098 last = NULL;
8099 next = xmlXPathNextDescendantOrSelf;
8100 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008101 case AXIS_FOLLOWING:
8102#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008103 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008104#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008105 last = NULL;
8106 next = xmlXPathNextFollowing;
8107 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008108 case AXIS_FOLLOWING_SIBLING:
8109#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008110 xmlGenericError(xmlGenericErrorContext,
8111 "axis 'following-siblings' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008112#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008113 last = NULL;
8114 next = xmlXPathNextFollowingSibling;
8115 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008116 case AXIS_NAMESPACE:
8117#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008118 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008119#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008120 first = NULL;
8121 last = NULL;
8122 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8123 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008124 case AXIS_PARENT:
8125#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008126 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008127#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008128 first = NULL;
8129 next = xmlXPathNextParent;
8130 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008131 case AXIS_PRECEDING:
8132#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008133 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008134#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008135 first = NULL;
8136 next = xmlXPathNextPrecedingInternal;
8137 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008138 case AXIS_PRECEDING_SIBLING:
8139#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008140 xmlGenericError(xmlGenericErrorContext,
8141 "axis 'preceding-sibling' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008142#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008143 first = NULL;
8144 next = xmlXPathNextPrecedingSibling;
8145 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008146 case AXIS_SELF:
8147#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008148 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008149#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008150 first = NULL;
8151 last = NULL;
8152 next = xmlXPathNextSelf;
8153 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008154 }
8155 if (next == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00008156 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008157
8158 nodelist = obj->nodesetval;
8159 if (nodelist == NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008160 xmlXPathFreeObject(obj);
8161 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8162 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008163 }
8164 addNode = xmlXPathNodeSetAddUnique;
8165 ret = NULL;
8166#ifdef DEBUG_STEP
8167 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008168 " context contains %d nodes\n", nodelist->nodeNr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008169 switch (test) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008170 case NODE_TEST_NONE:
8171 xmlGenericError(xmlGenericErrorContext,
8172 " searching for none !!!\n");
8173 break;
8174 case NODE_TEST_TYPE:
8175 xmlGenericError(xmlGenericErrorContext,
8176 " searching for type %d\n", type);
8177 break;
8178 case NODE_TEST_PI:
8179 xmlGenericError(xmlGenericErrorContext,
8180 " searching for PI !!!\n");
8181 break;
8182 case NODE_TEST_ALL:
8183 xmlGenericError(xmlGenericErrorContext,
8184 " searching for *\n");
8185 break;
8186 case NODE_TEST_NS:
8187 xmlGenericError(xmlGenericErrorContext,
8188 " searching for namespace %s\n",
8189 prefix);
8190 break;
8191 case NODE_TEST_NAME:
8192 xmlGenericError(xmlGenericErrorContext,
8193 " searching for name %s\n", name);
8194 if (prefix != NULL)
8195 xmlGenericError(xmlGenericErrorContext,
8196 " with namespace %s\n", prefix);
8197 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008198 }
8199 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8200#endif
8201 /*
8202 * 2.3 Node Tests
8203 * - For the attribute axis, the principal node type is attribute.
8204 * - For the namespace axis, the principal node type is namespace.
8205 * - For other axes, the principal node type is element.
8206 *
8207 * A node test * is true for any node of the
8208 * principal node type. For example, child::* willi
8209 * select all element children of the context node
8210 */
8211 tmp = ctxt->context->node;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008212 for (i = 0; i < nodelist->nodeNr; i++) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008213 ctxt->context->node = nodelist->nodeTab[i];
8214
Daniel Veillardf06307e2001-07-03 10:35:50 +00008215 cur = NULL;
8216 list = xmlXPathNodeSetCreate(NULL);
8217 do {
8218 cur = next(ctxt, cur);
8219 if (cur == NULL)
8220 break;
8221 if ((first != NULL) && (*first == cur))
8222 break;
8223 if (((t % 256) == 0) &&
8224 (first != NULL) && (*first != NULL) &&
8225 (xmlXPathCmpNodes(*first, cur) >= 0))
8226 break;
8227 if ((last != NULL) && (*last == cur))
8228 break;
8229 if (((t % 256) == 0) &&
8230 (last != NULL) && (*last != NULL) &&
8231 (xmlXPathCmpNodes(cur, *last) >= 0))
8232 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008233 t++;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008234#ifdef DEBUG_STEP
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008235 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
8236#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008237 switch (test) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008238 case NODE_TEST_NONE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008239 ctxt->context->node = tmp;
8240 STRANGE return(t);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008241 case NODE_TEST_TYPE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008242 if ((cur->type == type) ||
8243 ((type == NODE_TYPE_NODE) &&
8244 ((cur->type == XML_DOCUMENT_NODE) ||
8245 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8246 (cur->type == XML_ELEMENT_NODE) ||
8247 (cur->type == XML_PI_NODE) ||
8248 (cur->type == XML_COMMENT_NODE) ||
8249 (cur->type == XML_CDATA_SECTION_NODE) ||
Daniel Veillard7583a592001-07-08 13:15:55 +00008250 (cur->type == XML_TEXT_NODE))) ||
8251 ((type == NODE_TYPE_TEXT) &&
8252 (cur->type == XML_CDATA_SECTION_NODE))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008253#ifdef DEBUG_STEP
8254 n++;
8255#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008256 addNode(list, cur);
8257 }
8258 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008259 case NODE_TEST_PI:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008260 if (cur->type == XML_PI_NODE) {
8261 if ((name != NULL) &&
8262 (!xmlStrEqual(name, cur->name)))
8263 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008264#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008265 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008266#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008267 addNode(list, cur);
8268 }
8269 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008270 case NODE_TEST_ALL:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008271 if (axis == AXIS_ATTRIBUTE) {
8272 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008273#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008274 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008275#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008276 addNode(list, cur);
8277 }
8278 } else if (axis == AXIS_NAMESPACE) {
8279 if (cur->type == XML_NAMESPACE_DECL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008280#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008281 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008282#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008283 addNode(list, cur);
8284 }
8285 } else {
8286 if (cur->type == XML_ELEMENT_NODE) {
8287 if (prefix == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008288#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008289 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008290#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008291 addNode(list, cur);
8292 } else if ((cur->ns != NULL) &&
8293 (xmlStrEqual(URI, cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008294#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008295 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008296#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008297 addNode(list, cur);
8298 }
8299 }
8300 }
8301 break;
8302 case NODE_TEST_NS:{
8303 TODO;
8304 break;
8305 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008306 case NODE_TEST_NAME:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008307 switch (cur->type) {
8308 case XML_ELEMENT_NODE:
8309 if (xmlStrEqual(name, cur->name)) {
8310 if (prefix == NULL) {
8311 if (cur->ns == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008312#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008313 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008314#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008315 addNode(list, cur);
8316 }
8317 } else {
8318 if ((cur->ns != NULL) &&
8319 (xmlStrEqual(URI,
8320 cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008321#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008322 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008323#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008324 addNode(list, cur);
8325 }
8326 }
8327 }
8328 break;
8329 case XML_ATTRIBUTE_NODE:{
8330 xmlAttrPtr attr = (xmlAttrPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008331
Daniel Veillardf06307e2001-07-03 10:35:50 +00008332 if (xmlStrEqual(name, attr->name)) {
8333 if (prefix == NULL) {
8334 if ((attr->ns == NULL) ||
8335 (attr->ns->prefix == NULL)) {
8336#ifdef DEBUG_STEP
8337 n++;
8338#endif
8339 addNode(list,
8340 (xmlNodePtr) attr);
8341 }
8342 } else {
8343 if ((attr->ns != NULL) &&
8344 (xmlStrEqual(URI,
8345 attr->ns->
8346 href))) {
8347#ifdef DEBUG_STEP
8348 n++;
8349#endif
8350 addNode(list,
8351 (xmlNodePtr) attr);
8352 }
8353 }
8354 }
8355 break;
8356 }
8357 case XML_NAMESPACE_DECL:
8358 if (cur->type == XML_NAMESPACE_DECL) {
8359 xmlNsPtr ns = (xmlNsPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008360
Daniel Veillardf06307e2001-07-03 10:35:50 +00008361 if ((ns->prefix != NULL) && (name != NULL)
8362 && (xmlStrEqual(ns->prefix, name))) {
8363#ifdef DEBUG_STEP
8364 n++;
8365#endif
8366 addNode(list, cur);
8367 }
8368 }
8369 break;
8370 default:
8371 break;
8372 }
8373 break;
8374 break;
8375 }
8376 } while (cur != NULL);
8377
8378 /*
8379 * If there is some predicate filtering do it now
8380 */
8381 if (op->ch2 != -1) {
8382 xmlXPathObjectPtr obj2;
8383
8384 valuePush(ctxt, xmlXPathWrapNodeSet(list));
8385 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
8386 CHECK_TYPE0(XPATH_NODESET);
8387 obj2 = valuePop(ctxt);
8388 list = obj2->nodesetval;
8389 obj2->nodesetval = NULL;
8390 xmlXPathFreeObject(obj2);
8391 }
8392 if (ret == NULL) {
8393 ret = list;
8394 } else {
8395 ret = xmlXPathNodeSetMerge(ret, list);
8396 xmlXPathFreeNodeSet(list);
8397 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008398 }
8399 ctxt->context->node = tmp;
8400#ifdef DEBUG_STEP
8401 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008402 "\nExamined %d nodes, found %d nodes at that step\n",
8403 t, n);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008404#endif
8405 xmlXPathFreeObject(obj);
8406 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Daniel Veillardf06307e2001-07-03 10:35:50 +00008407 return(t);
8408}
8409
8410/**
8411 * xmlXPathNodeCollectAndTestNth:
8412 * @ctxt: the XPath Parser context
8413 * @op: the XPath precompiled step operation
8414 * @indx: the index to collect
8415 * @first: pointer to the first element in document order
8416 * @last: pointer to the last element in document order
8417 *
8418 * This is the function implementing a step: based on the current list
8419 * of nodes, it builds up a new list, looking at all nodes under that
8420 * axis and selecting them it also do the predicate filtering
8421 *
8422 * Pushes the new NodeSet resulting from the search.
8423 * Returns the number of node traversed
8424 */
8425static int
8426xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
8427 xmlXPathStepOpPtr op, int indx,
8428 xmlNodePtr * first, xmlNodePtr * last)
8429{
8430 xmlXPathAxisVal axis = op->value;
8431 xmlXPathTestVal test = op->value2;
8432 xmlXPathTypeVal type = op->value3;
8433 const xmlChar *prefix = op->value4;
8434 const xmlChar *name = op->value5;
8435 const xmlChar *URI = NULL;
8436 int n = 0, t = 0;
8437
8438 int i;
8439 xmlNodeSetPtr list;
8440 xmlXPathTraversalFunction next = NULL;
8441 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
8442 xmlNodePtr cur = NULL;
8443 xmlXPathObjectPtr obj;
8444 xmlNodeSetPtr nodelist;
8445 xmlNodePtr tmp;
8446
8447 CHECK_TYPE0(XPATH_NODESET);
8448 obj = valuePop(ctxt);
8449 addNode = xmlXPathNodeSetAdd;
8450 if (prefix != NULL) {
8451 URI = xmlXPathNsLookup(ctxt->context, prefix);
8452 if (URI == NULL)
8453 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
8454 }
8455#ifdef DEBUG_STEP_NTH
8456 xmlGenericError(xmlGenericErrorContext, "new step : ");
8457 if (first != NULL) {
8458 if (*first != NULL)
8459 xmlGenericError(xmlGenericErrorContext, "first = %s ",
8460 (*first)->name);
8461 else
8462 xmlGenericError(xmlGenericErrorContext, "first = NULL ");
8463 }
8464 if (last != NULL) {
8465 if (*last != NULL)
8466 xmlGenericError(xmlGenericErrorContext, "last = %s ",
8467 (*last)->name);
8468 else
8469 xmlGenericError(xmlGenericErrorContext, "last = NULL ");
8470 }
8471#endif
8472 switch (axis) {
8473 case AXIS_ANCESTOR:
8474#ifdef DEBUG_STEP_NTH
8475 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
8476#endif
8477 first = NULL;
8478 next = xmlXPathNextAncestor;
8479 break;
8480 case AXIS_ANCESTOR_OR_SELF:
8481#ifdef DEBUG_STEP_NTH
8482 xmlGenericError(xmlGenericErrorContext,
8483 "axis 'ancestors-or-self' ");
8484#endif
8485 first = NULL;
8486 next = xmlXPathNextAncestorOrSelf;
8487 break;
8488 case AXIS_ATTRIBUTE:
8489#ifdef DEBUG_STEP_NTH
8490 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
8491#endif
8492 first = NULL;
8493 last = NULL;
8494 next = xmlXPathNextAttribute;
8495 break;
8496 case AXIS_CHILD:
8497#ifdef DEBUG_STEP_NTH
8498 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
8499#endif
8500 last = NULL;
8501 next = xmlXPathNextChild;
8502 break;
8503 case AXIS_DESCENDANT:
8504#ifdef DEBUG_STEP_NTH
8505 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
8506#endif
8507 last = NULL;
8508 next = xmlXPathNextDescendant;
8509 break;
8510 case AXIS_DESCENDANT_OR_SELF:
8511#ifdef DEBUG_STEP_NTH
8512 xmlGenericError(xmlGenericErrorContext,
8513 "axis 'descendant-or-self' ");
8514#endif
8515 last = NULL;
8516 next = xmlXPathNextDescendantOrSelf;
8517 break;
8518 case AXIS_FOLLOWING:
8519#ifdef DEBUG_STEP_NTH
8520 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
8521#endif
8522 last = NULL;
8523 next = xmlXPathNextFollowing;
8524 break;
8525 case AXIS_FOLLOWING_SIBLING:
8526#ifdef DEBUG_STEP_NTH
8527 xmlGenericError(xmlGenericErrorContext,
8528 "axis 'following-siblings' ");
8529#endif
8530 last = NULL;
8531 next = xmlXPathNextFollowingSibling;
8532 break;
8533 case AXIS_NAMESPACE:
8534#ifdef DEBUG_STEP_NTH
8535 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
8536#endif
8537 last = NULL;
8538 first = NULL;
8539 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8540 break;
8541 case AXIS_PARENT:
8542#ifdef DEBUG_STEP_NTH
8543 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
8544#endif
8545 first = NULL;
8546 next = xmlXPathNextParent;
8547 break;
8548 case AXIS_PRECEDING:
8549#ifdef DEBUG_STEP_NTH
8550 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
8551#endif
8552 first = NULL;
8553 next = xmlXPathNextPrecedingInternal;
8554 break;
8555 case AXIS_PRECEDING_SIBLING:
8556#ifdef DEBUG_STEP_NTH
8557 xmlGenericError(xmlGenericErrorContext,
8558 "axis 'preceding-sibling' ");
8559#endif
8560 first = NULL;
8561 next = xmlXPathNextPrecedingSibling;
8562 break;
8563 case AXIS_SELF:
8564#ifdef DEBUG_STEP_NTH
8565 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
8566#endif
8567 first = NULL;
8568 last = NULL;
8569 next = xmlXPathNextSelf;
8570 break;
8571 }
8572 if (next == NULL)
8573 return(0);
8574
8575 nodelist = obj->nodesetval;
8576 if (nodelist == NULL) {
8577 xmlXPathFreeObject(obj);
8578 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8579 return(0);
8580 }
8581 addNode = xmlXPathNodeSetAddUnique;
8582#ifdef DEBUG_STEP_NTH
8583 xmlGenericError(xmlGenericErrorContext,
8584 " context contains %d nodes\n", nodelist->nodeNr);
8585 switch (test) {
8586 case NODE_TEST_NONE:
8587 xmlGenericError(xmlGenericErrorContext,
8588 " searching for none !!!\n");
8589 break;
8590 case NODE_TEST_TYPE:
8591 xmlGenericError(xmlGenericErrorContext,
8592 " searching for type %d\n", type);
8593 break;
8594 case NODE_TEST_PI:
8595 xmlGenericError(xmlGenericErrorContext,
8596 " searching for PI !!!\n");
8597 break;
8598 case NODE_TEST_ALL:
8599 xmlGenericError(xmlGenericErrorContext,
8600 " searching for *\n");
8601 break;
8602 case NODE_TEST_NS:
8603 xmlGenericError(xmlGenericErrorContext,
8604 " searching for namespace %s\n",
8605 prefix);
8606 break;
8607 case NODE_TEST_NAME:
8608 xmlGenericError(xmlGenericErrorContext,
8609 " searching for name %s\n", name);
8610 if (prefix != NULL)
8611 xmlGenericError(xmlGenericErrorContext,
8612 " with namespace %s\n", prefix);
8613 break;
8614 }
8615 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8616#endif
8617 /*
8618 * 2.3 Node Tests
8619 * - For the attribute axis, the principal node type is attribute.
8620 * - For the namespace axis, the principal node type is namespace.
8621 * - For other axes, the principal node type is element.
8622 *
8623 * A node test * is true for any node of the
8624 * principal node type. For example, child::* willi
8625 * select all element children of the context node
8626 */
8627 tmp = ctxt->context->node;
8628 list = xmlXPathNodeSetCreate(NULL);
8629 for (i = 0; i < nodelist->nodeNr; i++) {
8630 ctxt->context->node = nodelist->nodeTab[i];
8631
8632 cur = NULL;
8633 n = 0;
8634 do {
8635 cur = next(ctxt, cur);
8636 if (cur == NULL)
8637 break;
8638 if ((first != NULL) && (*first == cur))
8639 break;
8640 if (((t % 256) == 0) &&
8641 (first != NULL) && (*first != NULL) &&
8642 (xmlXPathCmpNodes(*first, cur) >= 0))
8643 break;
8644 if ((last != NULL) && (*last == cur))
8645 break;
8646 if (((t % 256) == 0) &&
8647 (last != NULL) && (*last != NULL) &&
8648 (xmlXPathCmpNodes(cur, *last) >= 0))
8649 break;
8650 t++;
8651 switch (test) {
8652 case NODE_TEST_NONE:
8653 ctxt->context->node = tmp;
8654 STRANGE return(0);
8655 case NODE_TEST_TYPE:
8656 if ((cur->type == type) ||
8657 ((type == NODE_TYPE_NODE) &&
8658 ((cur->type == XML_DOCUMENT_NODE) ||
8659 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8660 (cur->type == XML_ELEMENT_NODE) ||
8661 (cur->type == XML_PI_NODE) ||
8662 (cur->type == XML_COMMENT_NODE) ||
8663 (cur->type == XML_CDATA_SECTION_NODE) ||
8664 (cur->type == XML_TEXT_NODE)))) {
8665 n++;
8666 if (n == indx)
8667 addNode(list, cur);
8668 }
8669 break;
8670 case NODE_TEST_PI:
8671 if (cur->type == XML_PI_NODE) {
8672 if ((name != NULL) &&
8673 (!xmlStrEqual(name, cur->name)))
8674 break;
8675 n++;
8676 if (n == indx)
8677 addNode(list, cur);
8678 }
8679 break;
8680 case NODE_TEST_ALL:
8681 if (axis == AXIS_ATTRIBUTE) {
8682 if (cur->type == XML_ATTRIBUTE_NODE) {
8683 n++;
8684 if (n == indx)
8685 addNode(list, cur);
8686 }
8687 } else if (axis == AXIS_NAMESPACE) {
8688 if (cur->type == XML_NAMESPACE_DECL) {
8689 n++;
8690 if (n == indx)
8691 addNode(list, cur);
8692 }
8693 } else {
8694 if (cur->type == XML_ELEMENT_NODE) {
8695 if (prefix == NULL) {
8696 n++;
8697 if (n == indx)
8698 addNode(list, cur);
8699 } else if ((cur->ns != NULL) &&
8700 (xmlStrEqual(URI, cur->ns->href))) {
8701 n++;
8702 if (n == indx)
8703 addNode(list, cur);
8704 }
8705 }
8706 }
8707 break;
8708 case NODE_TEST_NS:{
8709 TODO;
8710 break;
8711 }
8712 case NODE_TEST_NAME:
8713 switch (cur->type) {
8714 case XML_ELEMENT_NODE:
8715 if (xmlStrEqual(name, cur->name)) {
8716 if (prefix == NULL) {
8717 if (cur->ns == NULL) {
8718 n++;
8719 if (n == indx)
8720 addNode(list, cur);
8721 }
8722 } else {
8723 if ((cur->ns != NULL) &&
8724 (xmlStrEqual(URI,
8725 cur->ns->href))) {
8726 n++;
8727 if (n == indx)
8728 addNode(list, cur);
8729 }
8730 }
8731 }
8732 break;
8733 case XML_ATTRIBUTE_NODE:{
8734 xmlAttrPtr attr = (xmlAttrPtr) cur;
8735
8736 if (xmlStrEqual(name, attr->name)) {
8737 if (prefix == NULL) {
8738 if ((attr->ns == NULL) ||
8739 (attr->ns->prefix == NULL)) {
8740 n++;
8741 if (n == indx)
8742 addNode(list, cur);
8743 }
8744 } else {
8745 if ((attr->ns != NULL) &&
8746 (xmlStrEqual(URI,
8747 attr->ns->
8748 href))) {
8749 n++;
8750 if (n == indx)
8751 addNode(list, cur);
8752 }
8753 }
8754 }
8755 break;
8756 }
8757 case XML_NAMESPACE_DECL:
8758 if (cur->type == XML_NAMESPACE_DECL) {
8759 xmlNsPtr ns = (xmlNsPtr) cur;
8760
8761 if ((ns->prefix != NULL) && (name != NULL)
8762 && (xmlStrEqual(ns->prefix, name))) {
8763 n++;
8764 if (n == indx)
8765 addNode(list, cur);
8766 }
8767 }
8768 break;
8769 default:
8770 break;
8771 }
8772 break;
8773 break;
8774 }
8775 } while (n < indx);
8776 }
8777 ctxt->context->node = tmp;
8778#ifdef DEBUG_STEP_NTH
8779 xmlGenericError(xmlGenericErrorContext,
8780 "\nExamined %d nodes, found %d nodes at that step\n",
8781 t, list->nodeNr);
8782#endif
8783 xmlXPathFreeObject(obj);
8784 valuePush(ctxt, xmlXPathWrapNodeSet(list));
8785 return(t);
8786}
8787
8788/**
8789 * xmlXPathCompOpEvalFirst:
8790 * @ctxt: the XPath parser context with the compiled expression
8791 * @op: an XPath compiled operation
8792 * @first: the first elem found so far
8793 *
8794 * Evaluate the Precompiled XPath operation searching only the first
8795 * element in document order
8796 *
8797 * Returns the number of examined objects.
8798 */
8799static int
8800xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
8801 xmlXPathStepOpPtr op, xmlNodePtr * first)
8802{
8803 int total = 0, cur;
8804 xmlXPathCompExprPtr comp;
8805 xmlXPathObjectPtr arg1, arg2;
8806
8807 comp = ctxt->comp;
8808 switch (op->op) {
8809 case XPATH_OP_END:
8810 return (0);
8811 case XPATH_OP_UNION:
8812 total =
8813 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8814 first);
8815 if ((ctxt->value != NULL)
8816 && (ctxt->value->type == XPATH_NODESET)
8817 && (ctxt->value->nodesetval != NULL)
8818 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8819 /*
8820 * limit tree traversing to first node in the result
8821 */
8822 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8823 *first = ctxt->value->nodesetval->nodeTab[0];
8824 }
8825 cur =
8826 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
8827 first);
8828 CHECK_TYPE0(XPATH_NODESET);
8829 arg2 = valuePop(ctxt);
8830
8831 CHECK_TYPE0(XPATH_NODESET);
8832 arg1 = valuePop(ctxt);
8833
8834 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8835 arg2->nodesetval);
8836 valuePush(ctxt, arg1);
8837 xmlXPathFreeObject(arg2);
8838 /* optimizer */
8839 if (total > cur)
8840 xmlXPathCompSwap(op);
8841 return (total + cur);
8842 case XPATH_OP_ROOT:
8843 xmlXPathRoot(ctxt);
8844 return (0);
8845 case XPATH_OP_NODE:
8846 if (op->ch1 != -1)
8847 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8848 if (op->ch2 != -1)
8849 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8850 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8851 return (total);
8852 case XPATH_OP_RESET:
8853 if (op->ch1 != -1)
8854 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8855 if (op->ch2 != -1)
8856 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8857 ctxt->context->node = NULL;
8858 return (total);
8859 case XPATH_OP_COLLECT:{
8860 if (op->ch1 == -1)
8861 return (total);
8862
8863 total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8864
8865 /*
8866 * Optimization for [n] selection where n is a number
8867 */
8868 if ((op->ch2 != -1) &&
8869 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8870 (comp->steps[op->ch2].ch1 == -1) &&
8871 (comp->steps[op->ch2].ch2 != -1) &&
8872 (comp->steps[comp->steps[op->ch2].ch2].op ==
8873 XPATH_OP_VALUE)) {
8874 xmlXPathObjectPtr val;
8875
8876 val = comp->steps[comp->steps[op->ch2].ch2].value4;
8877 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
8878 int indx = (int) val->floatval;
8879
8880 if (val->floatval == (float) indx) {
8881 xmlXPathNodeCollectAndTestNth(ctxt, op, indx,
8882 first, NULL);
8883 return (total);
8884 }
8885 }
8886 }
8887 total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL);
8888 return (total);
8889 }
8890 case XPATH_OP_VALUE:
8891 valuePush(ctxt,
8892 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
8893 return (0);
8894 case XPATH_OP_SORT:
8895 if (op->ch1 != -1)
8896 total +=
8897 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8898 first);
8899 if ((ctxt->value != NULL)
8900 && (ctxt->value->type == XPATH_NODESET)
8901 && (ctxt->value->nodesetval != NULL))
8902 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8903 return (total);
8904 default:
8905 return (xmlXPathCompOpEval(ctxt, op));
8906 }
8907}
8908
8909/**
8910 * xmlXPathCompOpEvalLast:
8911 * @ctxt: the XPath parser context with the compiled expression
8912 * @op: an XPath compiled operation
8913 * @last: the last elem found so far
8914 *
8915 * Evaluate the Precompiled XPath operation searching only the last
8916 * element in document order
8917 *
8918 * Returns the number of node traversed
8919 */
8920static int
8921xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
8922 xmlNodePtr * last)
8923{
8924 int total = 0, cur;
8925 xmlXPathCompExprPtr comp;
8926 xmlXPathObjectPtr arg1, arg2;
8927
8928 comp = ctxt->comp;
8929 switch (op->op) {
8930 case XPATH_OP_END:
8931 return (0);
8932 case XPATH_OP_UNION:
8933 total =
8934 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
8935 if ((ctxt->value != NULL)
8936 && (ctxt->value->type == XPATH_NODESET)
8937 && (ctxt->value->nodesetval != NULL)
8938 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8939 /*
8940 * limit tree traversing to first node in the result
8941 */
8942 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8943 *last =
8944 ctxt->value->nodesetval->nodeTab[ctxt->value->
8945 nodesetval->nodeNr -
8946 1];
8947 }
8948 cur =
8949 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
8950 if ((ctxt->value != NULL)
8951 && (ctxt->value->type == XPATH_NODESET)
8952 && (ctxt->value->nodesetval != NULL)
8953 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8954 }
8955 CHECK_TYPE0(XPATH_NODESET);
8956 arg2 = valuePop(ctxt);
8957
8958 CHECK_TYPE0(XPATH_NODESET);
8959 arg1 = valuePop(ctxt);
8960
8961 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8962 arg2->nodesetval);
8963 valuePush(ctxt, arg1);
8964 xmlXPathFreeObject(arg2);
8965 /* optimizer */
8966 if (total > cur)
8967 xmlXPathCompSwap(op);
8968 return (total + cur);
8969 case XPATH_OP_ROOT:
8970 xmlXPathRoot(ctxt);
8971 return (0);
8972 case XPATH_OP_NODE:
8973 if (op->ch1 != -1)
8974 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8975 if (op->ch2 != -1)
8976 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8977 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8978 return (total);
8979 case XPATH_OP_RESET:
8980 if (op->ch1 != -1)
8981 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8982 if (op->ch2 != -1)
8983 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8984 ctxt->context->node = NULL;
8985 return (total);
8986 case XPATH_OP_COLLECT:{
8987 if (op->ch1 == -1)
8988 return (0);
8989
8990 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8991
8992 /*
8993 * Optimization for [n] selection where n is a number
8994 */
8995 if ((op->ch2 != -1) &&
8996 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8997 (comp->steps[op->ch2].ch1 == -1) &&
8998 (comp->steps[op->ch2].ch2 != -1) &&
8999 (comp->steps[comp->steps[op->ch2].ch2].op ==
9000 XPATH_OP_VALUE)) {
9001 xmlXPathObjectPtr val;
9002
9003 val = comp->steps[comp->steps[op->ch2].ch2].value4;
9004 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
9005 int indx = (int) val->floatval;
9006
9007 if (val->floatval == (float) indx) {
9008 total +=
9009 xmlXPathNodeCollectAndTestNth(ctxt, op,
9010 indx, NULL,
9011 last);
9012 return (total);
9013 }
9014 }
9015 }
9016 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last);
9017 return (total);
9018 }
9019 case XPATH_OP_VALUE:
9020 valuePush(ctxt,
9021 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
9022 return (0);
9023 case XPATH_OP_SORT:
9024 if (op->ch1 != -1)
9025 total +=
9026 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
9027 last);
9028 if ((ctxt->value != NULL)
9029 && (ctxt->value->type == XPATH_NODESET)
9030 && (ctxt->value->nodesetval != NULL))
9031 xmlXPathNodeSetSort(ctxt->value->nodesetval);
9032 return (total);
9033 default:
9034 return (xmlXPathCompOpEval(ctxt, op));
9035 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009036}
9037
Owen Taylor3473f882001-02-23 17:55:21 +00009038/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009039 * xmlXPathCompOpEval:
9040 * @ctxt: the XPath parser context with the compiled expression
9041 * @op: an XPath compiled operation
9042 *
9043 * Evaluate the Precompiled XPath operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00009044 * Returns the number of node traversed
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009045 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00009046static int
9047xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
9048{
9049 int total = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009050 int equal, ret;
9051 xmlXPathCompExprPtr comp;
9052 xmlXPathObjectPtr arg1, arg2;
9053
9054 comp = ctxt->comp;
9055 switch (op->op) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00009056 case XPATH_OP_END:
9057 return (0);
9058 case XPATH_OP_AND:
9059 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9060 xmlXPathBooleanFunction(ctxt, 1);
9061 if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
9062 return (total);
9063 arg2 = valuePop(ctxt);
9064 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9065 xmlXPathBooleanFunction(ctxt, 1);
9066 arg1 = valuePop(ctxt);
9067 arg1->boolval &= arg2->boolval;
9068 valuePush(ctxt, arg1);
9069 xmlXPathFreeObject(arg2);
9070 return (total);
9071 case XPATH_OP_OR:
9072 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9073 xmlXPathBooleanFunction(ctxt, 1);
9074 if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
9075 return (total);
9076 arg2 = valuePop(ctxt);
9077 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9078 xmlXPathBooleanFunction(ctxt, 1);
9079 arg1 = valuePop(ctxt);
9080 arg1->boolval |= arg2->boolval;
9081 valuePush(ctxt, arg1);
9082 xmlXPathFreeObject(arg2);
9083 return (total);
9084 case XPATH_OP_EQUAL:
9085 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9086 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9087 equal = xmlXPathEqualValues(ctxt);
9088 if (op->value)
9089 valuePush(ctxt, xmlXPathNewBoolean(equal));
9090 else
9091 valuePush(ctxt, xmlXPathNewBoolean(!equal));
9092 return (total);
9093 case XPATH_OP_CMP:
9094 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9095 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9096 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
9097 valuePush(ctxt, xmlXPathNewBoolean(ret));
9098 return (total);
9099 case XPATH_OP_PLUS:
9100 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9101 if (op->ch2 != -1)
9102 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9103 if (op->value == 0)
9104 xmlXPathSubValues(ctxt);
9105 else if (op->value == 1)
9106 xmlXPathAddValues(ctxt);
9107 else if (op->value == 2)
9108 xmlXPathValueFlipSign(ctxt);
9109 else if (op->value == 3) {
9110 CAST_TO_NUMBER;
9111 CHECK_TYPE0(XPATH_NUMBER);
9112 }
9113 return (total);
9114 case XPATH_OP_MULT:
9115 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9116 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9117 if (op->value == 0)
9118 xmlXPathMultValues(ctxt);
9119 else if (op->value == 1)
9120 xmlXPathDivValues(ctxt);
9121 else if (op->value == 2)
9122 xmlXPathModValues(ctxt);
9123 return (total);
9124 case XPATH_OP_UNION:
9125 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9126 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9127 CHECK_TYPE0(XPATH_NODESET);
9128 arg2 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009129
Daniel Veillardf06307e2001-07-03 10:35:50 +00009130 CHECK_TYPE0(XPATH_NODESET);
9131 arg1 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009132
Daniel Veillardf06307e2001-07-03 10:35:50 +00009133 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
9134 arg2->nodesetval);
9135 valuePush(ctxt, arg1);
9136 xmlXPathFreeObject(arg2);
9137 return (total);
9138 case XPATH_OP_ROOT:
9139 xmlXPathRoot(ctxt);
9140 return (total);
9141 case XPATH_OP_NODE:
9142 if (op->ch1 != -1)
9143 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9144 if (op->ch2 != -1)
9145 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9146 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
9147 return (total);
9148 case XPATH_OP_RESET:
9149 if (op->ch1 != -1)
9150 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9151 if (op->ch2 != -1)
9152 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9153 ctxt->context->node = NULL;
9154 return (total);
9155 case XPATH_OP_COLLECT:{
9156 if (op->ch1 == -1)
9157 return (total);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009158
Daniel Veillardf06307e2001-07-03 10:35:50 +00009159 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009160
Daniel Veillardf06307e2001-07-03 10:35:50 +00009161 /*
9162 * Optimization for [n] selection where n is a number
9163 */
9164 if ((op->ch2 != -1) &&
9165 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
9166 (comp->steps[op->ch2].ch1 == -1) &&
9167 (comp->steps[op->ch2].ch2 != -1) &&
9168 (comp->steps[comp->steps[op->ch2].ch2].op ==
9169 XPATH_OP_VALUE)) {
9170 xmlXPathObjectPtr val;
Daniel Veillard42596ad2001-05-22 16:57:14 +00009171
Daniel Veillardf06307e2001-07-03 10:35:50 +00009172 val = comp->steps[comp->steps[op->ch2].ch2].value4;
9173 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
9174 int indx = (int) val->floatval;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009175
Daniel Veillardf06307e2001-07-03 10:35:50 +00009176 if (val->floatval == (float) indx) {
9177 total +=
9178 xmlXPathNodeCollectAndTestNth(ctxt, op,
9179 indx, NULL,
9180 NULL);
9181 return (total);
9182 }
9183 }
9184 }
9185 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL);
9186 return (total);
9187 }
9188 case XPATH_OP_VALUE:
9189 valuePush(ctxt,
9190 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
9191 return (total);
9192 case XPATH_OP_VARIABLE:{
9193 if (op->ch1 != -1)
9194 total +=
9195 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9196 if (op->value5 == NULL)
9197 valuePush(ctxt,
9198 xmlXPathVariableLookup(ctxt->context,
9199 op->value4));
9200 else {
9201 const xmlChar *URI;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009202
Daniel Veillardf06307e2001-07-03 10:35:50 +00009203 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9204 if (URI == NULL) {
9205 xmlGenericError(xmlGenericErrorContext,
9206 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
9207 op->value4, op->value5);
9208 return (total);
9209 }
9210 valuePush(ctxt,
9211 xmlXPathVariableLookupNS(ctxt->context,
9212 op->value4, URI));
9213 }
9214 return (total);
9215 }
9216 case XPATH_OP_FUNCTION:{
9217 xmlXPathFunction func;
9218 const xmlChar *oldFunc, *oldFuncURI;
9219
9220 if (op->ch1 != -1)
9221 total +=
9222 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9223 if (op->cache != NULL)
9224 func = (xmlXPathFunction) op->cache;
9225 else {
9226 const xmlChar *URI = NULL;
9227
9228 if (op->value5 == NULL)
9229 func =
9230 xmlXPathFunctionLookup(ctxt->context,
9231 op->value4);
9232 else {
9233 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9234 if (URI == NULL) {
9235 xmlGenericError(xmlGenericErrorContext,
9236 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
9237 op->value4, op->value5);
9238 return (total);
9239 }
9240 func = xmlXPathFunctionLookupNS(ctxt->context,
9241 op->value4, URI);
9242 }
9243 if (func == NULL) {
9244 xmlGenericError(xmlGenericErrorContext,
9245 "xmlXPathRunEval: function %s not found\n",
9246 op->value4);
9247 XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
9248 return (total);
9249 }
9250 op->cache = (void *) func;
9251 op->cacheURI = (void *) URI;
9252 }
9253 oldFunc = ctxt->context->function;
9254 oldFuncURI = ctxt->context->functionURI;
9255 ctxt->context->function = op->value4;
9256 ctxt->context->functionURI = op->cacheURI;
9257 func(ctxt, op->value);
9258 ctxt->context->function = oldFunc;
9259 ctxt->context->functionURI = oldFuncURI;
9260 return (total);
9261 }
9262 case XPATH_OP_ARG:
9263 if (op->ch1 != -1)
9264 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9265 if (op->ch2 != -1)
9266 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9267 return (total);
9268 case XPATH_OP_PREDICATE:
9269 case XPATH_OP_FILTER:{
9270 xmlXPathObjectPtr res;
9271 xmlXPathObjectPtr obj, tmp;
9272 xmlNodeSetPtr newset = NULL;
9273 xmlNodeSetPtr oldset;
9274 xmlNodePtr oldnode;
9275 int i;
9276
9277 /*
9278 * Optimization for ()[1] selection i.e. the first elem
9279 */
9280 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9281 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9282 (comp->steps[op->ch2].op == XPATH_OP_VALUE)) {
9283 xmlXPathObjectPtr val;
9284
9285 val = comp->steps[op->ch2].value4;
9286 if ((val != NULL) && (val->type == XPATH_NUMBER) &&
9287 (val->floatval == 1.0)) {
9288 xmlNodePtr first = NULL;
9289
9290 total +=
9291 xmlXPathCompOpEvalFirst(ctxt,
9292 &comp->steps[op->ch1],
9293 &first);
9294 /*
9295 * The nodeset should be in document order,
9296 * Keep only the first value
9297 */
9298 if ((ctxt->value != NULL) &&
9299 (ctxt->value->type == XPATH_NODESET) &&
9300 (ctxt->value->nodesetval != NULL) &&
9301 (ctxt->value->nodesetval->nodeNr > 1))
9302 ctxt->value->nodesetval->nodeNr = 1;
9303 return (total);
9304 }
9305 }
9306 /*
9307 * Optimization for ()[last()] selection i.e. the last elem
9308 */
9309 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9310 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9311 (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
9312 int f = comp->steps[op->ch2].ch1;
9313
9314 if ((f != -1) &&
9315 (comp->steps[f].op == XPATH_OP_FUNCTION) &&
9316 (comp->steps[f].value5 == NULL) &&
9317 (comp->steps[f].value == 0) &&
9318 (comp->steps[f].value4 != NULL) &&
9319 (xmlStrEqual
9320 (comp->steps[f].value4, BAD_CAST "last"))) {
9321 xmlNodePtr last = NULL;
9322
9323 total +=
9324 xmlXPathCompOpEvalLast(ctxt,
9325 &comp->steps[op->ch1],
9326 &last);
9327 /*
9328 * The nodeset should be in document order,
9329 * Keep only the last value
9330 */
9331 if ((ctxt->value != NULL) &&
9332 (ctxt->value->type == XPATH_NODESET) &&
9333 (ctxt->value->nodesetval != NULL) &&
9334 (ctxt->value->nodesetval->nodeTab != NULL) &&
9335 (ctxt->value->nodesetval->nodeNr > 1)) {
9336 ctxt->value->nodesetval->nodeTab[0] =
9337 ctxt->value->nodesetval->nodeTab[ctxt->
9338 value->
9339 nodesetval->
9340 nodeNr -
9341 1];
9342 ctxt->value->nodesetval->nodeNr = 1;
9343 }
9344 return (total);
9345 }
9346 }
9347
9348 if (op->ch1 != -1)
9349 total +=
9350 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9351 if (op->ch2 == -1)
9352 return (total);
9353 if (ctxt->value == NULL)
9354 return (total);
9355
9356 oldnode = ctxt->context->node;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009357
9358#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009359 /*
9360 * Hum are we filtering the result of an XPointer expression
9361 */
9362 if (ctxt->value->type == XPATH_LOCATIONSET) {
9363 xmlLocationSetPtr newlocset = NULL;
9364 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009365
Daniel Veillardf06307e2001-07-03 10:35:50 +00009366 /*
9367 * Extract the old locset, and then evaluate the result of the
9368 * expression for all the element in the locset. use it to grow
9369 * up a new locset.
9370 */
9371 CHECK_TYPE0(XPATH_LOCATIONSET);
9372 obj = valuePop(ctxt);
9373 oldlocset = obj->user;
9374 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009375
Daniel Veillardf06307e2001-07-03 10:35:50 +00009376 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
9377 ctxt->context->contextSize = 0;
9378 ctxt->context->proximityPosition = 0;
9379 if (op->ch2 != -1)
9380 total +=
9381 xmlXPathCompOpEval(ctxt,
9382 &comp->steps[op->ch2]);
9383 res = valuePop(ctxt);
9384 if (res != NULL)
9385 xmlXPathFreeObject(res);
9386 valuePush(ctxt, obj);
9387 CHECK_ERROR0;
9388 return (total);
9389 }
9390 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009391
Daniel Veillardf06307e2001-07-03 10:35:50 +00009392 for (i = 0; i < oldlocset->locNr; i++) {
9393 /*
9394 * Run the evaluation with a node list made of a
9395 * single item in the nodelocset.
9396 */
9397 ctxt->context->node = oldlocset->locTab[i]->user;
9398 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9399 valuePush(ctxt, tmp);
9400 ctxt->context->contextSize = oldlocset->locNr;
9401 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009402
Daniel Veillardf06307e2001-07-03 10:35:50 +00009403 if (op->ch2 != -1)
9404 total +=
9405 xmlXPathCompOpEval(ctxt,
9406 &comp->steps[op->ch2]);
9407 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009408
Daniel Veillardf06307e2001-07-03 10:35:50 +00009409 /*
9410 * The result of the evaluation need to be tested to
9411 * decided whether the filter succeeded or not
9412 */
9413 res = valuePop(ctxt);
9414 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9415 xmlXPtrLocationSetAdd(newlocset,
9416 xmlXPathObjectCopy
9417 (oldlocset->locTab[i]));
9418 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009419
Daniel Veillardf06307e2001-07-03 10:35:50 +00009420 /*
9421 * Cleanup
9422 */
9423 if (res != NULL)
9424 xmlXPathFreeObject(res);
9425 if (ctxt->value == tmp) {
9426 res = valuePop(ctxt);
9427 xmlXPathFreeObject(res);
9428 }
9429
9430 ctxt->context->node = NULL;
9431 }
9432
9433 /*
9434 * The result is used as the new evaluation locset.
9435 */
9436 xmlXPathFreeObject(obj);
9437 ctxt->context->node = NULL;
9438 ctxt->context->contextSize = -1;
9439 ctxt->context->proximityPosition = -1;
9440 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
9441 ctxt->context->node = oldnode;
9442 return (total);
9443 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009444#endif /* LIBXML_XPTR_ENABLED */
9445
Daniel Veillardf06307e2001-07-03 10:35:50 +00009446 /*
9447 * Extract the old set, and then evaluate the result of the
9448 * expression for all the element in the set. use it to grow
9449 * up a new set.
9450 */
9451 CHECK_TYPE0(XPATH_NODESET);
9452 obj = valuePop(ctxt);
9453 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00009454
Daniel Veillardf06307e2001-07-03 10:35:50 +00009455 oldnode = ctxt->context->node;
9456 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009457
Daniel Veillardf06307e2001-07-03 10:35:50 +00009458 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
9459 ctxt->context->contextSize = 0;
9460 ctxt->context->proximityPosition = 0;
9461 if (op->ch2 != -1)
9462 total +=
9463 xmlXPathCompOpEval(ctxt,
9464 &comp->steps[op->ch2]);
9465 res = valuePop(ctxt);
9466 if (res != NULL)
9467 xmlXPathFreeObject(res);
9468 valuePush(ctxt, obj);
9469 ctxt->context->node = oldnode;
9470 CHECK_ERROR0;
9471 } else {
9472 /*
9473 * Initialize the new set.
9474 */
9475 newset = xmlXPathNodeSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009476
Daniel Veillardf06307e2001-07-03 10:35:50 +00009477 for (i = 0; i < oldset->nodeNr; i++) {
9478 /*
9479 * Run the evaluation with a node list made of
9480 * a single item in the nodeset.
9481 */
9482 ctxt->context->node = oldset->nodeTab[i];
9483 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9484 valuePush(ctxt, tmp);
9485 ctxt->context->contextSize = oldset->nodeNr;
9486 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009487
Daniel Veillardf06307e2001-07-03 10:35:50 +00009488 if (op->ch2 != -1)
9489 total +=
9490 xmlXPathCompOpEval(ctxt,
9491 &comp->steps[op->ch2]);
9492 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009493
Daniel Veillardf06307e2001-07-03 10:35:50 +00009494 /*
9495 * The result of the evaluation need to be tested to
9496 * decided whether the filter succeeded or not
9497 */
9498 res = valuePop(ctxt);
9499 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9500 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
9501 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009502
Daniel Veillardf06307e2001-07-03 10:35:50 +00009503 /*
9504 * Cleanup
9505 */
9506 if (res != NULL)
9507 xmlXPathFreeObject(res);
9508 if (ctxt->value == tmp) {
9509 res = valuePop(ctxt);
9510 xmlXPathFreeObject(res);
9511 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009512
Daniel Veillardf06307e2001-07-03 10:35:50 +00009513 ctxt->context->node = NULL;
9514 }
9515
9516 /*
9517 * The result is used as the new evaluation set.
9518 */
9519 xmlXPathFreeObject(obj);
9520 ctxt->context->node = NULL;
9521 ctxt->context->contextSize = -1;
9522 ctxt->context->proximityPosition = -1;
9523 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
9524 }
9525 ctxt->context->node = oldnode;
9526 return (total);
9527 }
9528 case XPATH_OP_SORT:
9529 if (op->ch1 != -1)
9530 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9531 if ((ctxt->value != NULL) &&
9532 (ctxt->value->type == XPATH_NODESET) &&
9533 (ctxt->value->nodesetval != NULL))
9534 xmlXPathNodeSetSort(ctxt->value->nodesetval);
9535 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009536#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009537 case XPATH_OP_RANGETO:{
9538 xmlXPathObjectPtr range;
9539 xmlXPathObjectPtr res, obj;
9540 xmlXPathObjectPtr tmp;
9541 xmlLocationSetPtr newset = NULL;
9542 xmlNodeSetPtr oldset;
9543 int i;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009544
Daniel Veillardf06307e2001-07-03 10:35:50 +00009545 if (op->ch1 != -1)
9546 total +=
9547 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9548 if (op->ch2 == -1)
9549 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009550
Daniel Veillardf06307e2001-07-03 10:35:50 +00009551 CHECK_TYPE0(XPATH_NODESET);
9552 obj = valuePop(ctxt);
9553 oldset = obj->nodesetval;
9554 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009555
Daniel Veillardf06307e2001-07-03 10:35:50 +00009556 newset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009557
Daniel Veillardf06307e2001-07-03 10:35:50 +00009558 if (oldset != NULL) {
9559 for (i = 0; i < oldset->nodeNr; i++) {
9560 /*
9561 * Run the evaluation with a node list made of a single item
9562 * in the nodeset.
9563 */
9564 ctxt->context->node = oldset->nodeTab[i];
9565 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9566 valuePush(ctxt, tmp);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009567
Daniel Veillardf06307e2001-07-03 10:35:50 +00009568 if (op->ch2 != -1)
9569 total +=
9570 xmlXPathCompOpEval(ctxt,
9571 &comp->steps[op->ch2]);
9572 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009573
Daniel Veillardf06307e2001-07-03 10:35:50 +00009574 /*
9575 * The result of the evaluation need to be tested to
9576 * decided whether the filter succeeded or not
9577 */
9578 res = valuePop(ctxt);
9579 range =
9580 xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
9581 res);
9582 if (range != NULL) {
9583 xmlXPtrLocationSetAdd(newset, range);
9584 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009585
Daniel Veillardf06307e2001-07-03 10:35:50 +00009586 /*
9587 * Cleanup
9588 */
9589 if (res != NULL)
9590 xmlXPathFreeObject(res);
9591 if (ctxt->value == tmp) {
9592 res = valuePop(ctxt);
9593 xmlXPathFreeObject(res);
9594 }
9595
9596 ctxt->context->node = NULL;
9597 }
9598 }
9599
9600 /*
9601 * The result is used as the new evaluation set.
9602 */
9603 xmlXPathFreeObject(obj);
9604 ctxt->context->node = NULL;
9605 ctxt->context->contextSize = -1;
9606 ctxt->context->proximityPosition = -1;
9607 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
9608 return (total);
9609 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009610#endif /* LIBXML_XPTR_ENABLED */
9611 }
9612 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00009613 "XPath: unknown precompiled operation %d\n", op->op);
9614 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009615}
9616
9617/**
9618 * xmlXPathRunEval:
9619 * @ctxt: the XPath parser context with the compiled expression
9620 *
9621 * Evaluate the Precompiled XPath expression in the given context.
9622 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009623static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009624xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
9625 xmlXPathCompExprPtr comp;
9626
9627 if ((ctxt == NULL) || (ctxt->comp == NULL))
9628 return;
9629
9630 if (ctxt->valueTab == NULL) {
9631 /* Allocate the value stack */
9632 ctxt->valueTab = (xmlXPathObjectPtr *)
9633 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
9634 if (ctxt->valueTab == NULL) {
9635 xmlFree(ctxt);
9636 xmlGenericError(xmlGenericErrorContext,
9637 "xmlXPathRunEval: out of memory\n");
9638 return;
9639 }
9640 ctxt->valueNr = 0;
9641 ctxt->valueMax = 10;
9642 ctxt->value = NULL;
9643 }
9644 comp = ctxt->comp;
9645 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
9646}
9647
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009648/************************************************************************
9649 * *
9650 * Public interfaces *
9651 * *
9652 ************************************************************************/
9653
9654/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009655 * xmlXPathEvalPredicate:
9656 * @ctxt: the XPath context
9657 * @res: the Predicate Expression evaluation result
9658 *
9659 * Evaluate a predicate result for the current node.
9660 * A PredicateExpr is evaluated by evaluating the Expr and converting
9661 * the result to a boolean. If the result is a number, the result will
9662 * be converted to true if the number is equal to the position of the
9663 * context node in the context node list (as returned by the position
9664 * function) and will be converted to false otherwise; if the result
9665 * is not a number, then the result will be converted as if by a call
9666 * to the boolean function.
9667 *
9668 * Return 1 if predicate is true, 0 otherwise
9669 */
9670int
9671xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
9672 if (res == NULL) return(0);
9673 switch (res->type) {
9674 case XPATH_BOOLEAN:
9675 return(res->boolval);
9676 case XPATH_NUMBER:
9677 return(res->floatval == ctxt->proximityPosition);
9678 case XPATH_NODESET:
9679 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009680 if (res->nodesetval == NULL)
9681 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009682 return(res->nodesetval->nodeNr != 0);
9683 case XPATH_STRING:
9684 return((res->stringval != NULL) &&
9685 (xmlStrlen(res->stringval) != 0));
9686 default:
9687 STRANGE
9688 }
9689 return(0);
9690}
9691
9692/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009693 * xmlXPathEvaluatePredicateResult:
9694 * @ctxt: the XPath Parser context
9695 * @res: the Predicate Expression evaluation result
9696 *
9697 * Evaluate a predicate result for the current node.
9698 * A PredicateExpr is evaluated by evaluating the Expr and converting
9699 * the result to a boolean. If the result is a number, the result will
9700 * be converted to true if the number is equal to the position of the
9701 * context node in the context node list (as returned by the position
9702 * function) and will be converted to false otherwise; if the result
9703 * is not a number, then the result will be converted as if by a call
9704 * to the boolean function.
9705 *
9706 * Return 1 if predicate is true, 0 otherwise
9707 */
9708int
9709xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
9710 xmlXPathObjectPtr res) {
9711 if (res == NULL) return(0);
9712 switch (res->type) {
9713 case XPATH_BOOLEAN:
9714 return(res->boolval);
9715 case XPATH_NUMBER:
9716 return(res->floatval == ctxt->context->proximityPosition);
9717 case XPATH_NODESET:
9718 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00009719 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00009720 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009721 return(res->nodesetval->nodeNr != 0);
9722 case XPATH_STRING:
9723 return((res->stringval != NULL) &&
9724 (xmlStrlen(res->stringval) != 0));
9725 default:
9726 STRANGE
9727 }
9728 return(0);
9729}
9730
9731/**
9732 * xmlXPathCompile:
9733 * @str: the XPath expression
9734 *
9735 * Compile an XPath expression
9736 *
9737 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9738 * the caller has to free the object.
9739 */
9740xmlXPathCompExprPtr
9741xmlXPathCompile(const xmlChar *str) {
9742 xmlXPathParserContextPtr ctxt;
9743 xmlXPathCompExprPtr comp;
9744
9745 xmlXPathInit();
9746
9747 ctxt = xmlXPathNewParserContext(str, NULL);
9748 xmlXPathCompileExpr(ctxt);
9749
Daniel Veillard40af6492001-04-22 08:50:55 +00009750 if (*ctxt->cur != 0) {
9751 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9752 comp = NULL;
9753 } else {
9754 comp = ctxt->comp;
9755 ctxt->comp = NULL;
9756 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009757 xmlXPathFreeParserContext(ctxt);
Daniel Veillardf06307e2001-07-03 10:35:50 +00009758#ifdef DEBUG_EVAL_COUNTS
9759 if (comp != NULL) {
9760 comp->string = xmlStrdup(str);
9761 comp->nb = 0;
9762 }
9763#endif
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009764 return(comp);
9765}
9766
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009767/**
9768 * xmlXPathCompiledEval:
9769 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00009770 * @ctx: the XPath context
9771 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009772 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00009773 *
9774 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9775 * the caller has to free the object.
9776 */
9777xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009778xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00009779 xmlXPathParserContextPtr ctxt;
9780 xmlXPathObjectPtr res, tmp, init = NULL;
9781 int stack = 0;
9782
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009783 if ((comp == NULL) || (ctx == NULL))
9784 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00009785 xmlXPathInit();
9786
9787 CHECK_CONTEXT(ctx)
9788
Daniel Veillardf06307e2001-07-03 10:35:50 +00009789#ifdef DEBUG_EVAL_COUNTS
9790 comp->nb++;
9791 if ((comp->string != NULL) && (comp->nb > 100)) {
9792 fprintf(stderr, "100 x %s\n", comp->string);
9793 comp->nb = 0;
9794 }
9795#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009796 ctxt = xmlXPathCompParserContext(comp, ctx);
9797 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009798
9799 if (ctxt->value == NULL) {
9800 xmlGenericError(xmlGenericErrorContext,
9801 "xmlXPathEval: evaluation failed\n");
9802 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00009803 } else {
9804 res = valuePop(ctxt);
9805 }
9806
Daniel Veillardf06307e2001-07-03 10:35:50 +00009807
Owen Taylor3473f882001-02-23 17:55:21 +00009808 do {
9809 tmp = valuePop(ctxt);
9810 if (tmp != NULL) {
9811 if (tmp != init)
9812 stack++;
9813 xmlXPathFreeObject(tmp);
9814 }
9815 } while (tmp != NULL);
9816 if ((stack != 0) && (res != NULL)) {
9817 xmlGenericError(xmlGenericErrorContext,
9818 "xmlXPathEval: %d object left on the stack\n",
9819 stack);
9820 }
9821 if (ctxt->error != XPATH_EXPRESSION_OK) {
9822 xmlXPathFreeObject(res);
9823 res = NULL;
9824 }
9825
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009826
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009827 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009828 xmlXPathFreeParserContext(ctxt);
9829 return(res);
9830}
9831
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009832/**
9833 * xmlXPathEvalExpr:
9834 * @ctxt: the XPath Parser context
9835 *
9836 * Parse and evaluate an XPath expression in the given context,
9837 * then push the result on the context stack
9838 */
9839void
9840xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
9841 xmlXPathCompileExpr(ctxt);
9842 xmlXPathRunEval(ctxt);
9843}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009844
9845/**
9846 * xmlXPathEval:
9847 * @str: the XPath expression
9848 * @ctx: the XPath context
9849 *
9850 * Evaluate the XPath Location Path in the given context.
9851 *
9852 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9853 * the caller has to free the object.
9854 */
9855xmlXPathObjectPtr
9856xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
9857 xmlXPathParserContextPtr ctxt;
9858 xmlXPathObjectPtr res, tmp, init = NULL;
9859 int stack = 0;
9860
9861 xmlXPathInit();
9862
9863 CHECK_CONTEXT(ctx)
9864
9865 ctxt = xmlXPathNewParserContext(str, ctx);
9866 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009867
9868 if (ctxt->value == NULL) {
9869 xmlGenericError(xmlGenericErrorContext,
9870 "xmlXPathEval: evaluation failed\n");
9871 res = NULL;
9872 } else if (*ctxt->cur != 0) {
9873 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9874 res = NULL;
9875 } else {
9876 res = valuePop(ctxt);
9877 }
9878
9879 do {
9880 tmp = valuePop(ctxt);
9881 if (tmp != NULL) {
9882 if (tmp != init)
9883 stack++;
9884 xmlXPathFreeObject(tmp);
9885 }
9886 } while (tmp != NULL);
9887 if ((stack != 0) && (res != NULL)) {
9888 xmlGenericError(xmlGenericErrorContext,
9889 "xmlXPathEval: %d object left on the stack\n",
9890 stack);
9891 }
9892 if (ctxt->error != XPATH_EXPRESSION_OK) {
9893 xmlXPathFreeObject(res);
9894 res = NULL;
9895 }
9896
Owen Taylor3473f882001-02-23 17:55:21 +00009897 xmlXPathFreeParserContext(ctxt);
9898 return(res);
9899}
9900
9901/**
9902 * xmlXPathEvalExpression:
9903 * @str: the XPath expression
9904 * @ctxt: the XPath context
9905 *
9906 * Evaluate the XPath expression in the given context.
9907 *
9908 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
9909 * the caller has to free the object.
9910 */
9911xmlXPathObjectPtr
9912xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
9913 xmlXPathParserContextPtr pctxt;
9914 xmlXPathObjectPtr res, tmp;
9915 int stack = 0;
9916
9917 xmlXPathInit();
9918
9919 CHECK_CONTEXT(ctxt)
9920
9921 pctxt = xmlXPathNewParserContext(str, ctxt);
9922 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009923
9924 if (*pctxt->cur != 0) {
9925 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9926 res = NULL;
9927 } else {
9928 res = valuePop(pctxt);
9929 }
9930 do {
9931 tmp = valuePop(pctxt);
9932 if (tmp != NULL) {
9933 xmlXPathFreeObject(tmp);
9934 stack++;
9935 }
9936 } while (tmp != NULL);
9937 if ((stack != 0) && (res != NULL)) {
9938 xmlGenericError(xmlGenericErrorContext,
9939 "xmlXPathEvalExpression: %d object left on the stack\n",
9940 stack);
9941 }
9942 xmlXPathFreeParserContext(pctxt);
9943 return(res);
9944}
9945
9946/**
9947 * xmlXPathRegisterAllFunctions:
9948 * @ctxt: the XPath context
9949 *
9950 * Registers all default XPath functions in this context
9951 */
9952void
9953xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
9954{
9955 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
9956 xmlXPathBooleanFunction);
9957 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
9958 xmlXPathCeilingFunction);
9959 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
9960 xmlXPathCountFunction);
9961 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
9962 xmlXPathConcatFunction);
9963 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
9964 xmlXPathContainsFunction);
9965 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
9966 xmlXPathIdFunction);
9967 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
9968 xmlXPathFalseFunction);
9969 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
9970 xmlXPathFloorFunction);
9971 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
9972 xmlXPathLastFunction);
9973 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
9974 xmlXPathLangFunction);
9975 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
9976 xmlXPathLocalNameFunction);
9977 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
9978 xmlXPathNotFunction);
9979 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
9980 xmlXPathNameFunction);
9981 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
9982 xmlXPathNamespaceURIFunction);
9983 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
9984 xmlXPathNormalizeFunction);
9985 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
9986 xmlXPathNumberFunction);
9987 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
9988 xmlXPathPositionFunction);
9989 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
9990 xmlXPathRoundFunction);
9991 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
9992 xmlXPathStringFunction);
9993 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
9994 xmlXPathStringLengthFunction);
9995 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
9996 xmlXPathStartsWithFunction);
9997 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
9998 xmlXPathSubstringFunction);
9999 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
10000 xmlXPathSubstringBeforeFunction);
10001 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
10002 xmlXPathSubstringAfterFunction);
10003 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
10004 xmlXPathSumFunction);
10005 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
10006 xmlXPathTrueFunction);
10007 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
10008 xmlXPathTranslateFunction);
10009}
10010
10011#endif /* LIBXML_XPATH_ENABLED */