blob: 910e2cc39f0ab342917a4fe6ad08019c3881b77e [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 *
13 * Author: Daniel.Veillard@w3.org
14 *
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 */
63/* #define DEBUG_EXPR */
64
65void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
66double xmlXPathStringEvalNumber(const xmlChar *str);
Daniel Veillard5792e162001-04-30 17:44:45 +000067double xmlXPathDivideBy(double f, double fzero);
Owen Taylor3473f882001-02-23 17:55:21 +000068
Daniel Veillard9e7160d2001-03-18 23:17:47 +000069/************************************************************************
70 * *
71 * Floating point stuff *
72 * *
73 ************************************************************************/
74
Owen Taylor3473f882001-02-23 17:55:21 +000075/*
Owen Taylor3473f882001-02-23 17:55:21 +000076 * The lack of portability of this section of the libc is annoying !
77 */
78double xmlXPathNAN = 0;
79double xmlXPathPINF = 1;
80double xmlXPathNINF = -1;
81
82#ifndef isinf
83#ifndef HAVE_ISINF
84
85#if HAVE_FPCLASS
86
87int isinf(double d) {
88 fpclass_t type = fpclass(d);
89 switch (type) {
90 case FP_NINF:
91 return(-1);
92 case FP_PINF:
93 return(1);
94 }
95 return(0);
96}
97
98#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
99
100#if HAVE_FP_CLASS_H
101#include <fp_class.h>
102#endif
103
104int isinf(double d) {
105#if HAVE_FP_CLASS
106 int fpclass = fp_class(d);
107#else
108 int fpclass = fp_class_d(d);
109#endif
110 if (fpclass == FP_POS_INF)
111 return(1);
112 if (fpclass == FP_NEG_INF)
113 return(-1);
114 return(0);
115}
116
117#elif defined(HAVE_CLASS)
118
119int isinf(double d) {
120 int fpclass = class(d);
121 if (fpclass == FP_PLUS_INF)
122 return(1);
123 if (fpclass == FP_MINUS_INF)
124 return(-1);
125 return(0);
126}
127#elif defined(finite) || defined(HAVE_FINITE)
128int isinf(double x) { return !finite(x) && x==x; }
129#elif defined(HUGE_VAL)
130int isinf(double x)
131{
132 if (x == HUGE_VAL)
133 return(1);
134 if (x == -HUGE_VAL)
135 return(-1);
136 return(0);
137}
138#endif
139
140#endif /* ! HAVE_ISINF */
141#endif /* ! defined(isinf) */
142
143#ifndef isnan
144#ifndef HAVE_ISNAN
145
146#ifdef HAVE_ISNAND
147#define isnan(f) isnand(f)
148#endif /* HAVE_iSNAND */
149
150#endif /* ! HAVE_iSNAN */
151#endif /* ! defined(isnan) */
152
Daniel Veillard5792e162001-04-30 17:44:45 +0000153
154/**
155 * xmlXPathDivideBy:
156 *
157 * The best way found so far to generate the NAN, +-INF
158 * without hitting a compiler bug or optimization :-\
159 *
160 * Returns the double resulting from the division
161 */
162double
163xmlXPathDivideBy(double f, double fzero) {
164 float ret;
165#ifdef HAVE_SIGNAL
166#ifdef SIGFPE
167#ifdef SIG_IGN
168 void (*sighandler)(int);
169 sighandler = signal(SIGFPE, SIG_IGN);
170#endif
171#endif
172#endif
173 ret = f / fzero;
174#ifdef HAVE_SIGNAL
175#ifdef SIGFPE
176#ifdef SIG_IGN
177 signal(SIGFPE, sighandler);
178#endif
179#endif
180#endif
181 return(ret);
182}
183
Owen Taylor3473f882001-02-23 17:55:21 +0000184/**
185 * xmlXPathInit:
186 *
187 * Initialize the XPath environment
188 */
189void
190xmlXPathInit(void) {
191 static int initialized = 0;
192
193 if (initialized) return;
194
Daniel Veillard5792e162001-04-30 17:44:45 +0000195 xmlXPathNAN = xmlXPathDivideBy(0.0, 0.0);
196 xmlXPathPINF = xmlXPathDivideBy(1.0, 0.0);
197 xmlXPathPINF = xmlXPathDivideBy(-1.0, 0.0);
Owen Taylor3473f882001-02-23 17:55:21 +0000198
199 initialized = 1;
200}
201
202/************************************************************************
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000203 * *
204 * Parser Types *
205 * *
206 ************************************************************************/
207
208/*
209 * Types are private:
210 */
211
212typedef enum {
213 XPATH_OP_END=0,
214 XPATH_OP_AND,
215 XPATH_OP_OR,
216 XPATH_OP_EQUAL,
217 XPATH_OP_CMP,
218 XPATH_OP_PLUS,
219 XPATH_OP_MULT,
220 XPATH_OP_UNION,
221 XPATH_OP_ROOT,
222 XPATH_OP_NODE,
223 XPATH_OP_RESET,
224 XPATH_OP_COLLECT,
225 XPATH_OP_VALUE,
226 XPATH_OP_VARIABLE,
227 XPATH_OP_FUNCTION,
228 XPATH_OP_ARG,
229 XPATH_OP_PREDICATE,
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000230 XPATH_OP_FILTER,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000231 XPATH_OP_SORT
232#ifdef LIBXML_XPTR_ENABLED
233 ,XPATH_OP_RANGETO
234#endif
235} xmlXPathOp;
236
237typedef enum {
238 AXIS_ANCESTOR = 1,
239 AXIS_ANCESTOR_OR_SELF,
240 AXIS_ATTRIBUTE,
241 AXIS_CHILD,
242 AXIS_DESCENDANT,
243 AXIS_DESCENDANT_OR_SELF,
244 AXIS_FOLLOWING,
245 AXIS_FOLLOWING_SIBLING,
246 AXIS_NAMESPACE,
247 AXIS_PARENT,
248 AXIS_PRECEDING,
249 AXIS_PRECEDING_SIBLING,
250 AXIS_SELF
251} xmlXPathAxisVal;
252
253typedef enum {
254 NODE_TEST_NONE = 0,
255 NODE_TEST_TYPE = 1,
256 NODE_TEST_PI = 2,
257 NODE_TEST_ALL = 3,
258 NODE_TEST_NS = 4,
259 NODE_TEST_NAME = 5
260} xmlXPathTestVal;
261
262typedef enum {
263 NODE_TYPE_NODE = 0,
264 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
265 NODE_TYPE_TEXT = XML_TEXT_NODE,
266 NODE_TYPE_PI = XML_PI_NODE
267} xmlXPathTypeVal;
268
269
270typedef struct _xmlXPathStepOp xmlXPathStepOp;
271typedef xmlXPathStepOp *xmlXPathStepOpPtr;
272struct _xmlXPathStepOp {
273 xmlXPathOp op;
274 int ch1;
275 int ch2;
276 int value;
277 int value2;
278 int value3;
279 void *value4;
280 void *value5;
Daniel Veillarde39a93d2001-04-28 14:35:02 +0000281 void *cache;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000282};
283
284struct _xmlXPathCompExpr {
285 int nbStep;
286 int maxStep;
287 xmlXPathStepOp *steps; /* ops for computation */
288 int last;
289};
290
291/************************************************************************
292 * *
293 * Parser Type functions *
294 * *
295 ************************************************************************/
296
297/**
298 * xmlXPathNewCompExpr:
299 *
300 * Create a new Xpath component
301 *
302 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
303 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000304static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000305xmlXPathNewCompExpr(void) {
306 xmlXPathCompExprPtr cur;
307
308 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
309 if (cur == NULL) {
310 xmlGenericError(xmlGenericErrorContext,
311 "xmlXPathNewCompExpr : malloc failed\n");
312 return(NULL);
313 }
314 memset(cur, 0, sizeof(xmlXPathCompExpr));
315 cur->maxStep = 10;
316 cur->nbStep = 0;
317 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
318 sizeof(xmlXPathStepOp));
319 if (cur->steps == NULL) {
320 xmlGenericError(xmlGenericErrorContext,
321 "xmlXPathNewCompExpr : malloc failed\n");
322 xmlFree(cur);
323 return(NULL);
324 }
325 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
326 cur->last = -1;
327 return(cur);
328}
329
330/**
331 * xmlXPathFreeCompExpr:
332 * @comp: an XPATH comp
333 *
334 * Free up the memory allocated by @comp
335 */
336void
337xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) {
338 xmlXPathStepOpPtr op;
339 int i;
340
341 if (comp == NULL)
342 return;
343 for (i = 0;i < comp->nbStep;i++) {
344 op = &comp->steps[i];
345 if (op->value4 != NULL) {
346 if (op->op == XPATH_OP_VALUE)
347 xmlXPathFreeObject(op->value4);
348 else
349 xmlFree(op->value4);
350 }
351 if (op->value5 != NULL)
352 xmlFree(op->value5);
353 }
354 if (comp->steps != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000355 xmlFree(comp->steps);
356 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000357 xmlFree(comp);
358}
359
360/**
361 * xmlXPathCompExprAdd:
362 * @comp: the compiled expression
363 * @ch1: first child index
364 * @ch2: second child index
365 * @op: an op
366 * @value: the first int value
367 * @value2: the second int value
368 * @value3: the third int value
369 * @value4: the first string value
370 * @value5: the second string value
371 *
372 * Add an step to an XPath Compiled Expression
373 *
374 * Returns -1 in case of failure, the index otherwise
375 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000376static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000377xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
378 xmlXPathOp op, int value,
379 int value2, int value3, void *value4, void *value5) {
380 if (comp->nbStep >= comp->maxStep) {
381 xmlXPathStepOp *real;
382
383 comp->maxStep *= 2;
384 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
385 comp->maxStep * sizeof(xmlXPathStepOp));
386 if (real == NULL) {
387 comp->maxStep /= 2;
388 xmlGenericError(xmlGenericErrorContext,
389 "xmlXPathCompExprAdd : realloc failed\n");
390 return(-1);
391 }
392 comp->steps = real;
393 }
394 comp->last = comp->nbStep;
395 comp->steps[comp->nbStep].ch1 = ch1;
396 comp->steps[comp->nbStep].ch2 = ch2;
397 comp->steps[comp->nbStep].op = op;
398 comp->steps[comp->nbStep].value = value;
399 comp->steps[comp->nbStep].value2 = value2;
400 comp->steps[comp->nbStep].value3 = value3;
401 comp->steps[comp->nbStep].value4 = value4;
402 comp->steps[comp->nbStep].value5 = value5;
Daniel Veillarde39a93d2001-04-28 14:35:02 +0000403 comp->steps[comp->nbStep].cache = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000404 return(comp->nbStep++);
405}
406
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000407#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
408 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
409 (op), (val), (val2), (val3), (val4), (val5))
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000410#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
411 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
412 (op), (val), (val2), (val3), (val4), (val5))
413
414#define PUSH_LEAVE_EXPR(op, val, val2) \
415xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
416
417#define PUSH_UNARY_EXPR(op, ch, val, val2) \
418xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
419
420#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
421xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
422
423/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000424 * *
425 * Debugging related functions *
426 * *
427 ************************************************************************/
428
429#define TODO \
430 xmlGenericError(xmlGenericErrorContext, \
431 "Unimplemented block at %s:%d\n", \
432 __FILE__, __LINE__);
433
434#define STRANGE \
435 xmlGenericError(xmlGenericErrorContext, \
436 "Internal error at %s:%d\n", \
437 __FILE__, __LINE__);
438
439#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000440static void
441xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000442 int i;
443 char shift[100];
444
445 for (i = 0;((i < depth) && (i < 25));i++)
446 shift[2 * i] = shift[2 * i + 1] = ' ';
447 shift[2 * i] = shift[2 * i + 1] = 0;
448 if (cur == NULL) {
449 fprintf(output, shift);
450 fprintf(output, "Node is NULL !\n");
451 return;
452
453 }
454
455 if ((cur->type == XML_DOCUMENT_NODE) ||
456 (cur->type == XML_HTML_DOCUMENT_NODE)) {
457 fprintf(output, shift);
458 fprintf(output, " /\n");
459 } else if (cur->type == XML_ATTRIBUTE_NODE)
460 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
461 else
462 xmlDebugDumpOneNode(output, cur, depth);
463}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000464static void
465xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000466 xmlNodePtr tmp;
467 int i;
468 char shift[100];
469
470 for (i = 0;((i < depth) && (i < 25));i++)
471 shift[2 * i] = shift[2 * i + 1] = ' ';
472 shift[2 * i] = shift[2 * i + 1] = 0;
473 if (cur == NULL) {
474 fprintf(output, shift);
475 fprintf(output, "Node is NULL !\n");
476 return;
477
478 }
479
480 while (cur != NULL) {
481 tmp = cur;
482 cur = cur->next;
483 xmlDebugDumpOneNode(output, tmp, depth);
484 }
485}
Owen Taylor3473f882001-02-23 17:55:21 +0000486
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000487static void
488xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000489 int i;
490 char shift[100];
491
492 for (i = 0;((i < depth) && (i < 25));i++)
493 shift[2 * i] = shift[2 * i + 1] = ' ';
494 shift[2 * i] = shift[2 * i + 1] = 0;
495
496 if (cur == NULL) {
497 fprintf(output, shift);
498 fprintf(output, "NodeSet is NULL !\n");
499 return;
500
501 }
502
Daniel Veillard911f49a2001-04-07 15:39:35 +0000503 if (cur != NULL) {
504 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
505 for (i = 0;i < cur->nodeNr;i++) {
506 fprintf(output, shift);
507 fprintf(output, "%d", i + 1);
508 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
509 }
Owen Taylor3473f882001-02-23 17:55:21 +0000510 }
511}
512
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000513static void
514xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000515 int i;
516 char shift[100];
517
518 for (i = 0;((i < depth) && (i < 25));i++)
519 shift[2 * i] = shift[2 * i + 1] = ' ';
520 shift[2 * i] = shift[2 * i + 1] = 0;
521
522 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
523 fprintf(output, shift);
524 fprintf(output, "Value Tree is NULL !\n");
525 return;
526
527 }
528
529 fprintf(output, shift);
530 fprintf(output, "%d", i + 1);
531 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
532}
Owen Taylor3473f882001-02-23 17:55:21 +0000533#if defined(LIBXML_XPTR_ENABLED)
534void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000535static void
536xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000537 int i;
538 char shift[100];
539
540 for (i = 0;((i < depth) && (i < 25));i++)
541 shift[2 * i] = shift[2 * i + 1] = ' ';
542 shift[2 * i] = shift[2 * i + 1] = 0;
543
544 if (cur == NULL) {
545 fprintf(output, shift);
546 fprintf(output, "LocationSet is NULL !\n");
547 return;
548
549 }
550
551 for (i = 0;i < cur->locNr;i++) {
552 fprintf(output, shift);
553 fprintf(output, "%d : ", i + 1);
554 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
555 }
556}
557#endif
558
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000559/**
560 * xmlXPathDebugDumpObject:
561 * @output: the FILE * to dump the output
562 * @cur: the object to inspect
563 * @depth: indentation level
564 *
565 * Dump the content of the object for debugging purposes
566 */
567void
568xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000569 int i;
570 char shift[100];
571
572 for (i = 0;((i < depth) && (i < 25));i++)
573 shift[2 * i] = shift[2 * i + 1] = ' ';
574 shift[2 * i] = shift[2 * i + 1] = 0;
575
576 fprintf(output, shift);
577
578 if (cur == NULL) {
579 fprintf(output, "Object is empty (NULL)\n");
580 return;
581 }
582 switch(cur->type) {
583 case XPATH_UNDEFINED:
584 fprintf(output, "Object is uninitialized\n");
585 break;
586 case XPATH_NODESET:
587 fprintf(output, "Object is a Node Set :\n");
588 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
589 break;
590 case XPATH_XSLT_TREE:
591 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000592 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000593 break;
594 case XPATH_BOOLEAN:
595 fprintf(output, "Object is a Boolean : ");
596 if (cur->boolval) fprintf(output, "true\n");
597 else fprintf(output, "false\n");
598 break;
599 case XPATH_NUMBER:
Daniel Veillard357c9602001-05-03 10:49:20 +0000600 switch (isinf(cur->floatval)) {
601 case 1:
602 fprintf(output, "Object is a number : +Infinity\n");
603 break;
604 case -1:
605 fprintf(output, "Object is a number : -Infinity\n");
606 break;
607 default:
608 if (isnan(cur->floatval)) {
609 fprintf(output, "Object is a number : NaN\n");
610 } else {
611 fprintf(output, "Object is a number : %0g\n", cur->floatval);
612 }
613 }
Owen Taylor3473f882001-02-23 17:55:21 +0000614 break;
615 case XPATH_STRING:
616 fprintf(output, "Object is a string : ");
617 xmlDebugDumpString(output, cur->stringval);
618 fprintf(output, "\n");
619 break;
620 case XPATH_POINT:
621 fprintf(output, "Object is a point : index %d in node", cur->index);
622 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
623 fprintf(output, "\n");
624 break;
625 case XPATH_RANGE:
626 if ((cur->user2 == NULL) ||
627 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
628 fprintf(output, "Object is a collapsed range :\n");
629 fprintf(output, shift);
630 if (cur->index >= 0)
631 fprintf(output, "index %d in ", cur->index);
632 fprintf(output, "node\n");
633 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
634 depth + 1);
635 } else {
636 fprintf(output, "Object is a range :\n");
637 fprintf(output, shift);
638 fprintf(output, "From ");
639 if (cur->index >= 0)
640 fprintf(output, "index %d in ", cur->index);
641 fprintf(output, "node\n");
642 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
643 depth + 1);
644 fprintf(output, shift);
645 fprintf(output, "To ");
646 if (cur->index2 >= 0)
647 fprintf(output, "index %d in ", cur->index2);
648 fprintf(output, "node\n");
649 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
650 depth + 1);
651 fprintf(output, "\n");
652 }
653 break;
654 case XPATH_LOCATIONSET:
655#if defined(LIBXML_XPTR_ENABLED)
656 fprintf(output, "Object is a Location Set:\n");
657 xmlXPathDebugDumpLocationSet(output,
658 (xmlLocationSetPtr) cur->user, depth);
659#endif
660 break;
661 case XPATH_USERS:
662 fprintf(output, "Object is user defined\n");
663 break;
664 }
665}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000666
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000667static void
668xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000669 xmlXPathStepOpPtr op, int depth) {
670 int i;
671 char shift[100];
672
673 for (i = 0;((i < depth) && (i < 25));i++)
674 shift[2 * i] = shift[2 * i + 1] = ' ';
675 shift[2 * i] = shift[2 * i + 1] = 0;
676
677 fprintf(output, shift);
678 if (op == NULL) {
679 fprintf(output, "Step is NULL\n");
680 return;
681 }
682 switch (op->op) {
683 case XPATH_OP_END:
684 fprintf(output, "END"); break;
685 case XPATH_OP_AND:
686 fprintf(output, "AND"); break;
687 case XPATH_OP_OR:
688 fprintf(output, "OR"); break;
689 case XPATH_OP_EQUAL:
690 if (op->value)
691 fprintf(output, "EQUAL =");
692 else
693 fprintf(output, "EQUAL !=");
694 break;
695 case XPATH_OP_CMP:
696 if (op->value)
697 fprintf(output, "CMP <");
698 else
699 fprintf(output, "CMP >");
700 if (!op->value2)
701 fprintf(output, "=");
702 break;
703 case XPATH_OP_PLUS:
704 if (op->value == 0)
705 fprintf(output, "PLUS -");
706 else if (op->value == 1)
707 fprintf(output, "PLUS +");
708 else if (op->value == 2)
709 fprintf(output, "PLUS unary -");
710 else if (op->value == 3)
711 fprintf(output, "PLUS unary - -");
712 break;
713 case XPATH_OP_MULT:
714 if (op->value == 0)
715 fprintf(output, "MULT *");
716 else if (op->value == 1)
717 fprintf(output, "MULT div");
718 else
719 fprintf(output, "MULT mod");
720 break;
721 case XPATH_OP_UNION:
722 fprintf(output, "UNION"); break;
723 case XPATH_OP_ROOT:
724 fprintf(output, "ROOT"); break;
725 case XPATH_OP_NODE:
726 fprintf(output, "NODE"); break;
727 case XPATH_OP_RESET:
728 fprintf(output, "RESET"); break;
729 case XPATH_OP_SORT:
730 fprintf(output, "SORT"); break;
731 case XPATH_OP_COLLECT: {
732 xmlXPathAxisVal axis = op->value;
733 xmlXPathTestVal test = op->value2;
734 xmlXPathTypeVal type = op->value3;
735 const xmlChar *prefix = op->value4;
736 const xmlChar *name = op->value5;
737
738 fprintf(output, "COLLECT ");
739 switch (axis) {
740 case AXIS_ANCESTOR:
741 fprintf(output, " 'ancestors' "); break;
742 case AXIS_ANCESTOR_OR_SELF:
743 fprintf(output, " 'ancestors-or-self' "); break;
744 case AXIS_ATTRIBUTE:
745 fprintf(output, " 'attributes' "); break;
746 case AXIS_CHILD:
747 fprintf(output, " 'child' "); break;
748 case AXIS_DESCENDANT:
749 fprintf(output, " 'descendant' "); break;
750 case AXIS_DESCENDANT_OR_SELF:
751 fprintf(output, " 'descendant-or-self' "); break;
752 case AXIS_FOLLOWING:
753 fprintf(output, " 'following' "); break;
754 case AXIS_FOLLOWING_SIBLING:
755 fprintf(output, " 'following-siblings' "); break;
756 case AXIS_NAMESPACE:
757 fprintf(output, " 'namespace' "); break;
758 case AXIS_PARENT:
759 fprintf(output, " 'parent' "); break;
760 case AXIS_PRECEDING:
761 fprintf(output, " 'preceding' "); break;
762 case AXIS_PRECEDING_SIBLING:
763 fprintf(output, " 'preceding-sibling' "); break;
764 case AXIS_SELF:
765 fprintf(output, " 'self' "); break;
766 }
767 switch (test) {
768 case NODE_TEST_NONE:
769 fprintf(output, "'none' "); break;
770 case NODE_TEST_TYPE:
771 fprintf(output, "'type' "); break;
772 case NODE_TEST_PI:
773 fprintf(output, "'PI' "); break;
774 case NODE_TEST_ALL:
775 fprintf(output, "'all' "); break;
776 case NODE_TEST_NS:
777 fprintf(output, "'namespace' "); break;
778 case NODE_TEST_NAME:
779 fprintf(output, "'name' "); break;
780 }
781 switch (type) {
782 case NODE_TYPE_NODE:
783 fprintf(output, "'node' "); break;
784 case NODE_TYPE_COMMENT:
785 fprintf(output, "'comment' "); break;
786 case NODE_TYPE_TEXT:
787 fprintf(output, "'text' "); break;
788 case NODE_TYPE_PI:
789 fprintf(output, "'PI' "); break;
790 }
791 if (prefix != NULL)
792 fprintf(output, "%s:", prefix);
793 if (name != NULL)
794 fprintf(output, "%s", name);
795 break;
796
797 }
798 case XPATH_OP_VALUE: {
799 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
800
801 fprintf(output, "ELEM ");
802 xmlXPathDebugDumpObject(output, object, 0);
803 goto finish;
804 }
805 case XPATH_OP_VARIABLE: {
806 const xmlChar *prefix = op->value5;
807 const xmlChar *name = op->value4;
808
809 if (prefix != NULL)
810 fprintf(output, "VARIABLE %s:%s", prefix, name);
811 else
812 fprintf(output, "VARIABLE %s", name);
813 break;
814 }
815 case XPATH_OP_FUNCTION: {
816 int nbargs = op->value;
817 const xmlChar *prefix = op->value5;
818 const xmlChar *name = op->value4;
819
820 if (prefix != NULL)
821 fprintf(output, "FUNCTION %s:%s(%d args)",
822 prefix, name, nbargs);
823 else
824 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
825 break;
826 }
827 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
828 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000829 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +0000830#ifdef LIBXML_XPTR_ENABLED
831 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
832#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000833 default:
834 fprintf(output, "UNKNOWN %d\n", op->op); return;
835 }
836 fprintf(output, "\n");
837finish:
838 if (op->ch1 >= 0)
839 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
840 if (op->ch2 >= 0)
841 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
842}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000843
844void
845xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
846 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000847 int i;
848 char shift[100];
849
850 for (i = 0;((i < depth) && (i < 25));i++)
851 shift[2 * i] = shift[2 * i + 1] = ' ';
852 shift[2 * i] = shift[2 * i + 1] = 0;
853
854 fprintf(output, shift);
855
856 if (comp == NULL) {
857 fprintf(output, "Compiled Expression is NULL\n");
858 return;
859 }
860 fprintf(output, "Compiled Expression : %d elements\n",
861 comp->nbStep);
862 i = comp->last;
863 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
864}
Owen Taylor3473f882001-02-23 17:55:21 +0000865#endif
866
867/************************************************************************
868 * *
869 * Parser stacks related functions and macros *
870 * *
871 ************************************************************************/
872
873/*
874 * Generic function for accessing stacks in the Parser Context
875 */
876
877#define PUSH_AND_POP(type, name) \
878extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
879 if (ctxt->name##Nr >= ctxt->name##Max) { \
880 ctxt->name##Max *= 2; \
881 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
882 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
883 if (ctxt->name##Tab == NULL) { \
884 xmlGenericError(xmlGenericErrorContext, \
885 "realloc failed !\n"); \
886 return(0); \
887 } \
888 } \
889 ctxt->name##Tab[ctxt->name##Nr] = value; \
890 ctxt->name = value; \
891 return(ctxt->name##Nr++); \
892} \
893extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
894 type ret; \
895 if (ctxt->name##Nr <= 0) return(0); \
896 ctxt->name##Nr--; \
897 if (ctxt->name##Nr > 0) \
898 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
899 else \
900 ctxt->name = NULL; \
901 ret = ctxt->name##Tab[ctxt->name##Nr]; \
902 ctxt->name##Tab[ctxt->name##Nr] = 0; \
903 return(ret); \
904} \
905
906PUSH_AND_POP(xmlXPathObjectPtr, value)
907
908/*
909 * Macros for accessing the content. Those should be used only by the parser,
910 * and not exported.
911 *
912 * Dirty macros, i.e. one need to make assumption on the context to use them
913 *
914 * CUR_PTR return the current pointer to the xmlChar to be parsed.
915 * CUR returns the current xmlChar value, i.e. a 8 bit value
916 * in ISO-Latin or UTF-8.
917 * This should be used internally by the parser
918 * only to compare to ASCII values otherwise it would break when
919 * running with UTF-8 encoding.
920 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
921 * to compare on ASCII based substring.
922 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
923 * strings within the parser.
924 * CURRENT Returns the current char value, with the full decoding of
925 * UTF-8 if we are using this mode. It returns an int.
926 * NEXT Skip to the next character, this does the proper decoding
927 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
928 * It returns the pointer to the current xmlChar.
929 */
930
931#define CUR (*ctxt->cur)
932#define SKIP(val) ctxt->cur += (val)
933#define NXT(val) ctxt->cur[(val)]
934#define CUR_PTR ctxt->cur
Daniel Veillard61d80a22001-04-27 17:13:01 +0000935#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
936
937#define COPY_BUF(l,b,i,v) \
938 if (l == 1) b[i++] = (xmlChar) v; \
939 else i += xmlCopyChar(l,&b[i],v)
940
941#define NEXTL(l) ctxt->cur += l
Owen Taylor3473f882001-02-23 17:55:21 +0000942
943#define SKIP_BLANKS \
944 while (IS_BLANK(*(ctxt->cur))) NEXT
945
946#define CURRENT (*ctxt->cur)
947#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
948
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000949
950#ifndef DBL_DIG
951#define DBL_DIG 16
952#endif
953#ifndef DBL_EPSILON
954#define DBL_EPSILON 1E-9
955#endif
956
957#define UPPER_DOUBLE 1E9
958#define LOWER_DOUBLE 1E-5
959
960#define INTEGER_DIGITS DBL_DIG
961#define FRACTION_DIGITS (DBL_DIG + 1)
962#define EXPONENT_DIGITS (3 + 2)
963
964/**
965 * xmlXPathFormatNumber:
966 * @number: number to format
967 * @buffer: output buffer
968 * @buffersize: size of output buffer
969 *
970 * Convert the number into a string representation.
971 */
972static void
973xmlXPathFormatNumber(double number, char buffer[], int buffersize)
974{
975 switch (isinf(number)) {
976 case 1:
977 if (buffersize > (int)sizeof("+Infinity"))
978 sprintf(buffer, "+Infinity");
979 break;
980 case -1:
981 if (buffersize > (int)sizeof("-Infinity"))
982 sprintf(buffer, "-Infinity");
983 break;
984 default:
985 if (isnan(number)) {
986 if (buffersize > (int)sizeof("NaN"))
987 sprintf(buffer, "NaN");
988 } else {
Bjorn Reese70a9da52001-04-21 16:57:29 +0000989 /* 3 is sign, decimal point, and terminating zero */
990 char work[DBL_DIG + EXPONENT_DIGITS + 3];
991 int integer_place, fraction_place;
992 char *ptr;
993 char *after_fraction;
994 double absolute_value;
995 int size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000996
Bjorn Reese70a9da52001-04-21 16:57:29 +0000997 absolute_value = fabs(number);
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000998
Bjorn Reese70a9da52001-04-21 16:57:29 +0000999 /*
1000 * First choose format - scientific or regular floating point.
1001 * In either case, result is in work, and after_fraction points
1002 * just past the fractional part.
1003 */
1004 if ( ((absolute_value > UPPER_DOUBLE) ||
1005 (absolute_value < LOWER_DOUBLE)) &&
1006 (absolute_value != 0.0) ) {
1007 /* Use scientific notation */
1008 integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
1009 fraction_place = DBL_DIG - 1;
1010 snprintf(work, sizeof(work),"%*.*e",
1011 integer_place, fraction_place, number);
1012 after_fraction = strchr(work + DBL_DIG, 'e');
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001013 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00001014 else {
1015 /* Use regular notation */
1016 integer_place = 1 + (int)log10(absolute_value);
1017 fraction_place = (integer_place > 0)
1018 ? DBL_DIG - integer_place
1019 : DBL_DIG;
1020 size = snprintf(work, sizeof(work), "%0.*f",
1021 fraction_place, number);
1022 after_fraction = work + size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001023 }
1024
Bjorn Reese70a9da52001-04-21 16:57:29 +00001025 /* Remove fractional trailing zeroes */
1026 ptr = after_fraction;
1027 while (*(--ptr) == '0')
1028 ;
1029 if (*ptr != '.')
1030 ptr++;
1031 strcpy(ptr, after_fraction);
1032
1033 /* Finally copy result back to caller */
1034 size = strlen(work) + 1;
1035 if (size > buffersize) {
1036 work[buffersize - 1] = 0;
1037 size = buffersize;
1038 }
1039 memcpy(buffer, work, size);
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001040 }
1041 break;
1042 }
1043}
1044
Owen Taylor3473f882001-02-23 17:55:21 +00001045/************************************************************************
1046 * *
1047 * Error handling routines *
1048 * *
1049 ************************************************************************/
1050
1051
1052const char *xmlXPathErrorMessages[] = {
1053 "Ok",
1054 "Number encoding",
1055 "Unfinished litteral",
1056 "Start of litteral",
1057 "Expected $ for variable reference",
1058 "Undefined variable",
1059 "Invalid predicate",
1060 "Invalid expression",
1061 "Missing closing curly brace",
1062 "Unregistered function",
1063 "Invalid operand",
1064 "Invalid type",
1065 "Invalid number of arguments",
1066 "Invalid context size",
1067 "Invalid context position",
1068 "Memory allocation error",
1069 "Syntax error",
1070 "Resource error",
1071 "Sub resource error",
Daniel Veillard61d80a22001-04-27 17:13:01 +00001072 "Undefined namespace prefix",
1073 "Encoding error",
1074 "Char out of XML range"
Owen Taylor3473f882001-02-23 17:55:21 +00001075};
1076
1077/**
1078 * xmlXPathError:
1079 * @ctxt: the XPath Parser context
1080 * @file: the file name
1081 * @line: the line number
1082 * @no: the error number
1083 *
1084 * Create a new xmlNodeSetPtr of type double and of value @val
1085 *
1086 * Returns the newly created object.
1087 */
1088void
1089xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1090 int line, int no) {
1091 int n;
1092 const xmlChar *cur;
1093 const xmlChar *base;
1094
1095 xmlGenericError(xmlGenericErrorContext,
1096 "Error %s:%d: %s\n", file, line,
1097 xmlXPathErrorMessages[no]);
1098
1099 cur = ctxt->cur;
1100 base = ctxt->base;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001101 if ((cur == NULL) || (base == NULL))
1102 return;
1103
Owen Taylor3473f882001-02-23 17:55:21 +00001104 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1105 cur--;
1106 }
1107 n = 0;
1108 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1109 cur--;
1110 if ((*cur == '\n') || (*cur == '\r')) cur++;
1111 base = cur;
1112 n = 0;
1113 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1114 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1115 n++;
1116 }
1117 xmlGenericError(xmlGenericErrorContext, "\n");
1118 cur = ctxt->cur;
1119 while ((*cur == '\n') || (*cur == '\r'))
1120 cur--;
1121 n = 0;
1122 while ((cur != base) && (n++ < 80)) {
1123 xmlGenericError(xmlGenericErrorContext, " ");
1124 base++;
1125 }
1126 xmlGenericError(xmlGenericErrorContext,"^\n");
1127}
1128
1129
1130/************************************************************************
1131 * *
1132 * Routines to handle NodeSets *
1133 * *
1134 ************************************************************************/
1135
1136/**
1137 * xmlXPathCmpNodes:
1138 * @node1: the first node
1139 * @node2: the second node
1140 *
1141 * Compare two nodes w.r.t document order
1142 *
1143 * Returns -2 in case of error 1 if first point < second point, 0 if
1144 * that's the same node, -1 otherwise
1145 */
1146int
1147xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1148 int depth1, depth2;
1149 xmlNodePtr cur, root;
1150
1151 if ((node1 == NULL) || (node2 == NULL))
1152 return(-2);
1153 /*
1154 * a couple of optimizations which will avoid computations in most cases
1155 */
1156 if (node1 == node2)
1157 return(0);
Daniel Veillardb33c2012001-04-25 12:59:04 +00001158 if ((node1->type == XML_NAMESPACE_DECL) ||
1159 (node2->type == XML_NAMESPACE_DECL))
1160 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00001161 if (node1 == node2->prev)
1162 return(1);
1163 if (node1 == node2->next)
1164 return(-1);
1165
1166 /*
1167 * compute depth to root
1168 */
1169 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1170 if (cur == node1)
1171 return(1);
1172 depth2++;
1173 }
1174 root = cur;
1175 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1176 if (cur == node2)
1177 return(-1);
1178 depth1++;
1179 }
1180 /*
1181 * Distinct document (or distinct entities :-( ) case.
1182 */
1183 if (root != cur) {
1184 return(-2);
1185 }
1186 /*
1187 * get the nearest common ancestor.
1188 */
1189 while (depth1 > depth2) {
1190 depth1--;
1191 node1 = node1->parent;
1192 }
1193 while (depth2 > depth1) {
1194 depth2--;
1195 node2 = node2->parent;
1196 }
1197 while (node1->parent != node2->parent) {
1198 node1 = node1->parent;
1199 node2 = node2->parent;
1200 /* should not happen but just in case ... */
1201 if ((node1 == NULL) || (node2 == NULL))
1202 return(-2);
1203 }
1204 /*
1205 * Find who's first.
1206 */
1207 if (node1 == node2->next)
1208 return(-1);
1209 for (cur = node1->next;cur != NULL;cur = cur->next)
1210 if (cur == node2)
1211 return(1);
1212 return(-1); /* assume there is no sibling list corruption */
1213}
1214
1215/**
1216 * xmlXPathNodeSetSort:
1217 * @set: the node set
1218 *
1219 * Sort the node set in document order
1220 */
1221void
1222xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001223 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001224 xmlNodePtr tmp;
1225
1226 if (set == NULL)
1227 return;
1228
1229 /* Use Shell's sort to sort the node-set */
1230 len = set->nodeNr;
1231 for (incr = len / 2; incr > 0; incr /= 2) {
1232 for (i = incr; i < len; i++) {
1233 j = i - incr;
1234 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001235 if (xmlXPathCmpNodes(set->nodeTab[j],
1236 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001237 tmp = set->nodeTab[j];
1238 set->nodeTab[j] = set->nodeTab[j + incr];
1239 set->nodeTab[j + incr] = tmp;
1240 j -= incr;
1241 } else
1242 break;
1243 }
1244 }
1245 }
1246}
1247
1248#define XML_NODESET_DEFAULT 10
1249/**
1250 * xmlXPathNodeSetCreate:
1251 * @val: an initial xmlNodePtr, or NULL
1252 *
1253 * Create a new xmlNodeSetPtr of type double and of value @val
1254 *
1255 * Returns the newly created object.
1256 */
1257xmlNodeSetPtr
1258xmlXPathNodeSetCreate(xmlNodePtr val) {
1259 xmlNodeSetPtr ret;
1260
1261 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1262 if (ret == NULL) {
1263 xmlGenericError(xmlGenericErrorContext,
1264 "xmlXPathNewNodeSet: out of memory\n");
1265 return(NULL);
1266 }
1267 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1268 if (val != NULL) {
1269 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1270 sizeof(xmlNodePtr));
1271 if (ret->nodeTab == NULL) {
1272 xmlGenericError(xmlGenericErrorContext,
1273 "xmlXPathNewNodeSet: out of memory\n");
1274 return(NULL);
1275 }
1276 memset(ret->nodeTab, 0 ,
1277 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1278 ret->nodeMax = XML_NODESET_DEFAULT;
1279 ret->nodeTab[ret->nodeNr++] = val;
1280 }
1281 return(ret);
1282}
1283
1284/**
1285 * xmlXPathNodeSetAdd:
1286 * @cur: the initial node set
1287 * @val: a new xmlNodePtr
1288 *
1289 * add a new xmlNodePtr ot an existing NodeSet
1290 */
1291void
1292xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1293 int i;
1294
1295 if (val == NULL) return;
1296
1297 /*
1298 * check against doublons
1299 */
1300 for (i = 0;i < cur->nodeNr;i++)
1301 if (cur->nodeTab[i] == val) return;
1302
1303 /*
1304 * grow the nodeTab if needed
1305 */
1306 if (cur->nodeMax == 0) {
1307 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1308 sizeof(xmlNodePtr));
1309 if (cur->nodeTab == NULL) {
1310 xmlGenericError(xmlGenericErrorContext,
1311 "xmlXPathNodeSetAdd: out of memory\n");
1312 return;
1313 }
1314 memset(cur->nodeTab, 0 ,
1315 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1316 cur->nodeMax = XML_NODESET_DEFAULT;
1317 } else if (cur->nodeNr == cur->nodeMax) {
1318 xmlNodePtr *temp;
1319
1320 cur->nodeMax *= 2;
1321 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1322 sizeof(xmlNodePtr));
1323 if (temp == NULL) {
1324 xmlGenericError(xmlGenericErrorContext,
1325 "xmlXPathNodeSetAdd: out of memory\n");
1326 return;
1327 }
1328 cur->nodeTab = temp;
1329 }
1330 cur->nodeTab[cur->nodeNr++] = val;
1331}
1332
1333/**
1334 * xmlXPathNodeSetAddUnique:
1335 * @cur: the initial node set
1336 * @val: a new xmlNodePtr
1337 *
1338 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1339 * when we are sure the node is not already in the set.
1340 */
1341void
1342xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1343 if (val == NULL) return;
1344
1345 /*
1346 * grow the nodeTab if needed
1347 */
1348 if (cur->nodeMax == 0) {
1349 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1350 sizeof(xmlNodePtr));
1351 if (cur->nodeTab == NULL) {
1352 xmlGenericError(xmlGenericErrorContext,
1353 "xmlXPathNodeSetAddUnique: out of memory\n");
1354 return;
1355 }
1356 memset(cur->nodeTab, 0 ,
1357 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1358 cur->nodeMax = XML_NODESET_DEFAULT;
1359 } else if (cur->nodeNr == cur->nodeMax) {
1360 xmlNodePtr *temp;
1361
1362 cur->nodeMax *= 2;
1363 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1364 sizeof(xmlNodePtr));
1365 if (temp == NULL) {
1366 xmlGenericError(xmlGenericErrorContext,
1367 "xmlXPathNodeSetAddUnique: out of memory\n");
1368 return;
1369 }
1370 cur->nodeTab = temp;
1371 }
1372 cur->nodeTab[cur->nodeNr++] = val;
1373}
1374
1375/**
1376 * xmlXPathNodeSetMerge:
1377 * @val1: the first NodeSet or NULL
1378 * @val2: the second NodeSet
1379 *
1380 * Merges two nodesets, all nodes from @val2 are added to @val1
1381 * if @val1 is NULL, a new set is created and copied from @val2
1382 *
1383 * Returns val1 once extended or NULL in case of error.
1384 */
1385xmlNodeSetPtr
1386xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001387 int i, j, initNr, skip;
Owen Taylor3473f882001-02-23 17:55:21 +00001388
1389 if (val2 == NULL) return(val1);
1390 if (val1 == NULL) {
1391 val1 = xmlXPathNodeSetCreate(NULL);
1392 }
1393
1394 initNr = val1->nodeNr;
1395
1396 for (i = 0;i < val2->nodeNr;i++) {
1397 /*
1398 * check against doublons
1399 */
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001400 skip = 0;
1401 for (j = 0; j < initNr; j++) {
1402 if (val1->nodeTab[j] == val2->nodeTab[i]) {
1403 skip = 1;
1404 break;
1405 }
1406 }
1407 if (skip)
1408 continue;
Owen Taylor3473f882001-02-23 17:55:21 +00001409
1410 /*
1411 * grow the nodeTab if needed
1412 */
1413 if (val1->nodeMax == 0) {
1414 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1415 sizeof(xmlNodePtr));
1416 if (val1->nodeTab == NULL) {
1417 xmlGenericError(xmlGenericErrorContext,
1418 "xmlXPathNodeSetMerge: out of memory\n");
1419 return(NULL);
1420 }
1421 memset(val1->nodeTab, 0 ,
1422 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1423 val1->nodeMax = XML_NODESET_DEFAULT;
1424 } else if (val1->nodeNr == val1->nodeMax) {
1425 xmlNodePtr *temp;
1426
1427 val1->nodeMax *= 2;
1428 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1429 sizeof(xmlNodePtr));
1430 if (temp == NULL) {
1431 xmlGenericError(xmlGenericErrorContext,
1432 "xmlXPathNodeSetMerge: out of memory\n");
1433 return(NULL);
1434 }
1435 val1->nodeTab = temp;
1436 }
1437 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1438 }
1439
1440 return(val1);
1441}
1442
1443/**
1444 * xmlXPathNodeSetDel:
1445 * @cur: the initial node set
1446 * @val: an xmlNodePtr
1447 *
1448 * Removes an xmlNodePtr from an existing NodeSet
1449 */
1450void
1451xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1452 int i;
1453
1454 if (cur == NULL) return;
1455 if (val == NULL) return;
1456
1457 /*
1458 * check against doublons
1459 */
1460 for (i = 0;i < cur->nodeNr;i++)
1461 if (cur->nodeTab[i] == val) break;
1462
1463 if (i >= cur->nodeNr) {
1464#ifdef DEBUG
1465 xmlGenericError(xmlGenericErrorContext,
1466 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1467 val->name);
1468#endif
1469 return;
1470 }
1471 cur->nodeNr--;
1472 for (;i < cur->nodeNr;i++)
1473 cur->nodeTab[i] = cur->nodeTab[i + 1];
1474 cur->nodeTab[cur->nodeNr] = NULL;
1475}
1476
1477/**
1478 * xmlXPathNodeSetRemove:
1479 * @cur: the initial node set
1480 * @val: the index to remove
1481 *
1482 * Removes an entry from an existing NodeSet list.
1483 */
1484void
1485xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1486 if (cur == NULL) return;
1487 if (val >= cur->nodeNr) return;
1488 cur->nodeNr--;
1489 for (;val < cur->nodeNr;val++)
1490 cur->nodeTab[val] = cur->nodeTab[val + 1];
1491 cur->nodeTab[cur->nodeNr] = NULL;
1492}
1493
1494/**
1495 * xmlXPathFreeNodeSet:
1496 * @obj: the xmlNodeSetPtr to free
1497 *
1498 * Free the NodeSet compound (not the actual nodes !).
1499 */
1500void
1501xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1502 if (obj == NULL) return;
1503 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001504 xmlFree(obj->nodeTab);
1505 }
Owen Taylor3473f882001-02-23 17:55:21 +00001506 xmlFree(obj);
1507}
1508
1509/**
1510 * xmlXPathFreeValueTree:
1511 * @obj: the xmlNodeSetPtr to free
1512 *
1513 * Free the NodeSet compound and the actual tree, this is different
1514 * from xmlXPathFreeNodeSet()
1515 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001516static void
Owen Taylor3473f882001-02-23 17:55:21 +00001517xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1518 int i;
1519
1520 if (obj == NULL) return;
1521 for (i = 0;i < obj->nodeNr;i++)
1522 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001523 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001524
1525 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001526 xmlFree(obj->nodeTab);
1527 }
Owen Taylor3473f882001-02-23 17:55:21 +00001528 xmlFree(obj);
1529}
1530
1531#if defined(DEBUG) || defined(DEBUG_STEP)
1532/**
1533 * xmlGenericErrorContextNodeSet:
1534 * @output: a FILE * for the output
1535 * @obj: the xmlNodeSetPtr to free
1536 *
1537 * Quick display of a NodeSet
1538 */
1539void
1540xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1541 int i;
1542
1543 if (output == NULL) output = xmlGenericErrorContext;
1544 if (obj == NULL) {
1545 fprintf(output, "NodeSet == NULL !\n");
1546 return;
1547 }
1548 if (obj->nodeNr == 0) {
1549 fprintf(output, "NodeSet is empty\n");
1550 return;
1551 }
1552 if (obj->nodeTab == NULL) {
1553 fprintf(output, " nodeTab == NULL !\n");
1554 return;
1555 }
1556 for (i = 0; i < obj->nodeNr; i++) {
1557 if (obj->nodeTab[i] == NULL) {
1558 fprintf(output, " NULL !\n");
1559 return;
1560 }
1561 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1562 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1563 fprintf(output, " /");
1564 else if (obj->nodeTab[i]->name == NULL)
1565 fprintf(output, " noname!");
1566 else fprintf(output, " %s", obj->nodeTab[i]->name);
1567 }
1568 fprintf(output, "\n");
1569}
1570#endif
1571
1572/**
1573 * xmlXPathNewNodeSet:
1574 * @val: the NodePtr value
1575 *
1576 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1577 * it with the single Node @val
1578 *
1579 * Returns the newly created object.
1580 */
1581xmlXPathObjectPtr
1582xmlXPathNewNodeSet(xmlNodePtr val) {
1583 xmlXPathObjectPtr ret;
1584
1585 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1586 if (ret == NULL) {
1587 xmlGenericError(xmlGenericErrorContext,
1588 "xmlXPathNewNodeSet: out of memory\n");
1589 return(NULL);
1590 }
1591 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1592 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001593 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001594 ret->nodesetval = xmlXPathNodeSetCreate(val);
1595 return(ret);
1596}
1597
1598/**
1599 * xmlXPathNewValueTree:
1600 * @val: the NodePtr value
1601 *
1602 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1603 * it with the tree root @val
1604 *
1605 * Returns the newly created object.
1606 */
1607xmlXPathObjectPtr
1608xmlXPathNewValueTree(xmlNodePtr val) {
1609 xmlXPathObjectPtr ret;
1610
1611 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1612 if (ret == NULL) {
1613 xmlGenericError(xmlGenericErrorContext,
1614 "xmlXPathNewNodeSet: out of memory\n");
1615 return(NULL);
1616 }
1617 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1618 ret->type = XPATH_XSLT_TREE;
1619 ret->nodesetval = xmlXPathNodeSetCreate(val);
1620 return(ret);
1621}
1622
1623/**
1624 * xmlXPathNewNodeSetList:
1625 * @val: an existing NodeSet
1626 *
1627 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1628 * it with the Nodeset @val
1629 *
1630 * Returns the newly created object.
1631 */
1632xmlXPathObjectPtr
1633xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1634 xmlXPathObjectPtr ret;
1635 int i;
1636
1637 if (val == NULL)
1638 ret = NULL;
1639 else if (val->nodeTab == NULL)
1640 ret = xmlXPathNewNodeSet(NULL);
1641 else
1642 {
1643 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1644 for (i = 1; i < val->nodeNr; ++i)
1645 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1646 }
1647
1648 return(ret);
1649}
1650
1651/**
1652 * xmlXPathWrapNodeSet:
1653 * @val: the NodePtr value
1654 *
1655 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1656 *
1657 * Returns the newly created object.
1658 */
1659xmlXPathObjectPtr
1660xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1661 xmlXPathObjectPtr ret;
1662
1663 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1664 if (ret == NULL) {
1665 xmlGenericError(xmlGenericErrorContext,
1666 "xmlXPathWrapNodeSet: out of memory\n");
1667 return(NULL);
1668 }
1669 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1670 ret->type = XPATH_NODESET;
1671 ret->nodesetval = val;
1672 return(ret);
1673}
1674
1675/**
1676 * xmlXPathFreeNodeSetList:
1677 * @obj: an existing NodeSetList object
1678 *
1679 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1680 * the list contrary to xmlXPathFreeObject().
1681 */
1682void
1683xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1684 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001685 xmlFree(obj);
1686}
1687
1688/************************************************************************
1689 * *
1690 * Routines to handle extra functions *
1691 * *
1692 ************************************************************************/
1693
1694/**
1695 * xmlXPathRegisterFunc:
1696 * @ctxt: the XPath context
1697 * @name: the function name
1698 * @f: the function implementation or NULL
1699 *
1700 * Register a new function. If @f is NULL it unregisters the function
1701 *
1702 * Returns 0 in case of success, -1 in case of error
1703 */
1704int
1705xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
1706 xmlXPathFunction f) {
1707 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
1708}
1709
1710/**
1711 * xmlXPathRegisterFuncNS:
1712 * @ctxt: the XPath context
1713 * @name: the function name
1714 * @ns_uri: the function namespace URI
1715 * @f: the function implementation or NULL
1716 *
1717 * Register a new function. If @f is NULL it unregisters the function
1718 *
1719 * Returns 0 in case of success, -1 in case of error
1720 */
1721int
1722xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1723 const xmlChar *ns_uri, xmlXPathFunction f) {
1724 if (ctxt == NULL)
1725 return(-1);
1726 if (name == NULL)
1727 return(-1);
1728
1729 if (ctxt->funcHash == NULL)
1730 ctxt->funcHash = xmlHashCreate(0);
1731 if (ctxt->funcHash == NULL)
1732 return(-1);
1733 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
1734}
1735
1736/**
1737 * xmlXPathFunctionLookup:
1738 * @ctxt: the XPath context
1739 * @name: the function name
1740 *
1741 * Search in the Function array of the context for the given
1742 * function.
1743 *
1744 * Returns the xmlXPathFunction or NULL if not found
1745 */
1746xmlXPathFunction
1747xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1748 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
1749}
1750
1751/**
1752 * xmlXPathFunctionLookupNS:
1753 * @ctxt: the XPath context
1754 * @name: the function name
1755 * @ns_uri: the function namespace URI
1756 *
1757 * Search in the Function array of the context for the given
1758 * function.
1759 *
1760 * Returns the xmlXPathFunction or NULL if not found
1761 */
1762xmlXPathFunction
1763xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1764 const xmlChar *ns_uri) {
1765 if (ctxt == NULL)
1766 return(NULL);
1767 if (ctxt->funcHash == NULL)
1768 return(NULL);
1769 if (name == NULL)
1770 return(NULL);
1771
1772 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
1773}
1774
1775/**
1776 * xmlXPathRegisteredFuncsCleanup:
1777 * @ctxt: the XPath context
1778 *
1779 * Cleanup the XPath context data associated to registered functions
1780 */
1781void
1782xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
1783 if (ctxt == NULL)
1784 return;
1785
1786 xmlHashFree(ctxt->funcHash, NULL);
1787 ctxt->funcHash = NULL;
1788}
1789
1790/************************************************************************
1791 * *
1792 * Routines to handle Variable *
1793 * *
1794 ************************************************************************/
1795
1796/**
1797 * xmlXPathRegisterVariable:
1798 * @ctxt: the XPath context
1799 * @name: the variable name
1800 * @value: the variable value or NULL
1801 *
1802 * Register a new variable value. If @value is NULL it unregisters
1803 * the variable
1804 *
1805 * Returns 0 in case of success, -1 in case of error
1806 */
1807int
1808xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
1809 xmlXPathObjectPtr value) {
1810 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
1811}
1812
1813/**
1814 * xmlXPathRegisterVariableNS:
1815 * @ctxt: the XPath context
1816 * @name: the variable name
1817 * @ns_uri: the variable namespace URI
1818 * @value: the variable value or NULL
1819 *
1820 * Register a new variable value. If @value is NULL it unregisters
1821 * the variable
1822 *
1823 * Returns 0 in case of success, -1 in case of error
1824 */
1825int
1826xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1827 const xmlChar *ns_uri,
1828 xmlXPathObjectPtr value) {
1829 if (ctxt == NULL)
1830 return(-1);
1831 if (name == NULL)
1832 return(-1);
1833
1834 if (ctxt->varHash == NULL)
1835 ctxt->varHash = xmlHashCreate(0);
1836 if (ctxt->varHash == NULL)
1837 return(-1);
1838 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
1839 (void *) value,
1840 (xmlHashDeallocator)xmlXPathFreeObject));
1841}
1842
1843/**
1844 * xmlXPathRegisterVariableLookup:
1845 * @ctxt: the XPath context
1846 * @f: the lookup function
1847 * @data: the lookup data
1848 *
1849 * register an external mechanism to do variable lookup
1850 */
1851void
1852xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
1853 xmlXPathVariableLookupFunc f, void *data) {
1854 if (ctxt == NULL)
1855 return;
1856 ctxt->varLookupFunc = (void *) f;
1857 ctxt->varLookupData = data;
1858}
1859
1860/**
1861 * xmlXPathVariableLookup:
1862 * @ctxt: the XPath context
1863 * @name: the variable name
1864 *
1865 * Search in the Variable array of the context for the given
1866 * variable value.
1867 *
1868 * Returns the value or NULL if not found
1869 */
1870xmlXPathObjectPtr
1871xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1872 if (ctxt == NULL)
1873 return(NULL);
1874
1875 if (ctxt->varLookupFunc != NULL) {
1876 xmlXPathObjectPtr ret;
1877
1878 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1879 (ctxt->varLookupData, name, NULL);
1880 if (ret != NULL) return(ret);
1881 }
1882 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
1883}
1884
1885/**
1886 * xmlXPathVariableLookupNS:
1887 * @ctxt: the XPath context
1888 * @name: the variable name
1889 * @ns_uri: the variable namespace URI
1890 *
1891 * Search in the Variable array of the context for the given
1892 * variable value.
1893 *
1894 * Returns the value or NULL if not found
1895 */
1896xmlXPathObjectPtr
1897xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1898 const xmlChar *ns_uri) {
1899 if (ctxt == NULL)
1900 return(NULL);
1901
1902 if (ctxt->varLookupFunc != NULL) {
1903 xmlXPathObjectPtr ret;
1904
1905 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1906 (ctxt->varLookupData, name, ns_uri);
1907 if (ret != NULL) return(ret);
1908 }
1909
1910 if (ctxt->varHash == NULL)
1911 return(NULL);
1912 if (name == NULL)
1913 return(NULL);
1914
1915 return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
1916}
1917
1918/**
1919 * xmlXPathRegisteredVariablesCleanup:
1920 * @ctxt: the XPath context
1921 *
1922 * Cleanup the XPath context data associated to registered variables
1923 */
1924void
1925xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
1926 if (ctxt == NULL)
1927 return;
1928
1929 xmlHashFree(ctxt->varHash, NULL);
1930 ctxt->varHash = NULL;
1931}
1932
1933/**
1934 * xmlXPathRegisterNs:
1935 * @ctxt: the XPath context
1936 * @prefix: the namespace prefix
1937 * @ns_uri: the namespace name
1938 *
1939 * Register a new namespace. If @ns_uri is NULL it unregisters
1940 * the namespace
1941 *
1942 * Returns 0 in case of success, -1 in case of error
1943 */
1944int
1945xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
1946 const xmlChar *ns_uri) {
1947 if (ctxt == NULL)
1948 return(-1);
1949 if (prefix == NULL)
1950 return(-1);
1951
1952 if (ctxt->nsHash == NULL)
1953 ctxt->nsHash = xmlHashCreate(10);
1954 if (ctxt->nsHash == NULL)
1955 return(-1);
1956 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
1957 (xmlHashDeallocator)xmlFree));
1958}
1959
1960/**
1961 * xmlXPathNsLookup:
1962 * @ctxt: the XPath context
1963 * @prefix: the namespace prefix value
1964 *
1965 * Search in the namespace declaration array of the context for the given
1966 * namespace name associated to the given prefix
1967 *
1968 * Returns the value or NULL if not found
1969 */
1970const xmlChar *
1971xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
1972 if (ctxt == NULL)
1973 return(NULL);
1974 if (prefix == NULL)
1975 return(NULL);
1976
1977#ifdef XML_XML_NAMESPACE
1978 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
1979 return(XML_XML_NAMESPACE);
1980#endif
1981
Daniel Veillardc8f620b2001-04-30 20:31:33 +00001982 if (ctxt->namespaces != NULL) {
1983 int i;
1984
1985 for (i = 0;i < ctxt->nsNr;i++) {
1986 if ((ctxt->namespaces[i] != NULL) &&
1987 (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
1988 return(ctxt->namespaces[i]->href);
1989 }
1990 }
Owen Taylor3473f882001-02-23 17:55:21 +00001991
1992 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
1993}
1994
1995/**
1996 * xmlXPathRegisteredVariablesCleanup:
1997 * @ctxt: the XPath context
1998 *
1999 * Cleanup the XPath context data associated to registered variables
2000 */
2001void
2002xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
2003 if (ctxt == NULL)
2004 return;
2005
2006 xmlHashFree(ctxt->nsHash, NULL);
2007 ctxt->nsHash = NULL;
2008}
2009
2010/************************************************************************
2011 * *
2012 * Routines to handle Values *
2013 * *
2014 ************************************************************************/
2015
2016/* Allocations are terrible, one need to optimize all this !!! */
2017
2018/**
2019 * xmlXPathNewFloat:
2020 * @val: the double value
2021 *
2022 * Create a new xmlXPathObjectPtr of type double and of value @val
2023 *
2024 * Returns the newly created object.
2025 */
2026xmlXPathObjectPtr
2027xmlXPathNewFloat(double val) {
2028 xmlXPathObjectPtr ret;
2029
2030 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2031 if (ret == NULL) {
2032 xmlGenericError(xmlGenericErrorContext,
2033 "xmlXPathNewFloat: out of memory\n");
2034 return(NULL);
2035 }
2036 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2037 ret->type = XPATH_NUMBER;
2038 ret->floatval = val;
2039 return(ret);
2040}
2041
2042/**
2043 * xmlXPathNewBoolean:
2044 * @val: the boolean value
2045 *
2046 * Create a new xmlXPathObjectPtr of type boolean and of value @val
2047 *
2048 * Returns the newly created object.
2049 */
2050xmlXPathObjectPtr
2051xmlXPathNewBoolean(int val) {
2052 xmlXPathObjectPtr ret;
2053
2054 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2055 if (ret == NULL) {
2056 xmlGenericError(xmlGenericErrorContext,
2057 "xmlXPathNewBoolean: out of memory\n");
2058 return(NULL);
2059 }
2060 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2061 ret->type = XPATH_BOOLEAN;
2062 ret->boolval = (val != 0);
2063 return(ret);
2064}
2065
2066/**
2067 * xmlXPathNewString:
2068 * @val: the xmlChar * value
2069 *
2070 * Create a new xmlXPathObjectPtr of type string and of value @val
2071 *
2072 * Returns the newly created object.
2073 */
2074xmlXPathObjectPtr
2075xmlXPathNewString(const xmlChar *val) {
2076 xmlXPathObjectPtr ret;
2077
2078 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2079 if (ret == NULL) {
2080 xmlGenericError(xmlGenericErrorContext,
2081 "xmlXPathNewString: out of memory\n");
2082 return(NULL);
2083 }
2084 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2085 ret->type = XPATH_STRING;
2086 if (val != NULL)
2087 ret->stringval = xmlStrdup(val);
2088 else
2089 ret->stringval = xmlStrdup((const xmlChar *)"");
2090 return(ret);
2091}
2092
2093/**
2094 * xmlXPathNewCString:
2095 * @val: the char * value
2096 *
2097 * Create a new xmlXPathObjectPtr of type string and of value @val
2098 *
2099 * Returns the newly created object.
2100 */
2101xmlXPathObjectPtr
2102xmlXPathNewCString(const char *val) {
2103 xmlXPathObjectPtr ret;
2104
2105 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2106 if (ret == NULL) {
2107 xmlGenericError(xmlGenericErrorContext,
2108 "xmlXPathNewCString: out of memory\n");
2109 return(NULL);
2110 }
2111 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2112 ret->type = XPATH_STRING;
2113 ret->stringval = xmlStrdup(BAD_CAST val);
2114 return(ret);
2115}
2116
2117/**
2118 * xmlXPathObjectCopy:
2119 * @val: the original object
2120 *
2121 * allocate a new copy of a given object
2122 *
2123 * Returns the newly created object.
2124 */
2125xmlXPathObjectPtr
2126xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2127 xmlXPathObjectPtr ret;
2128
2129 if (val == NULL)
2130 return(NULL);
2131
2132 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2133 if (ret == NULL) {
2134 xmlGenericError(xmlGenericErrorContext,
2135 "xmlXPathObjectCopy: out of memory\n");
2136 return(NULL);
2137 }
2138 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2139 switch (val->type) {
2140 case XPATH_BOOLEAN:
2141 case XPATH_NUMBER:
2142 case XPATH_POINT:
2143 case XPATH_RANGE:
2144 break;
2145 case XPATH_STRING:
2146 ret->stringval = xmlStrdup(val->stringval);
2147 break;
2148 case XPATH_XSLT_TREE:
2149 if ((val->nodesetval != NULL) &&
2150 (val->nodesetval->nodeTab != NULL))
2151 ret->nodesetval = xmlXPathNodeSetCreate(
2152 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
2153 else
2154 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
2155 break;
2156 case XPATH_NODESET:
2157 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
2158 break;
2159 case XPATH_LOCATIONSET:
2160#ifdef LIBXML_XPTR_ENABLED
2161 {
2162 xmlLocationSetPtr loc = val->user;
2163 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2164 break;
2165 }
2166#endif
2167 case XPATH_UNDEFINED:
2168 case XPATH_USERS:
2169 xmlGenericError(xmlGenericErrorContext,
2170 "xmlXPathObjectCopy: unsupported type %d\n",
2171 val->type);
2172 break;
2173 }
2174 return(ret);
2175}
2176
2177/**
2178 * xmlXPathFreeObject:
2179 * @obj: the object to free
2180 *
2181 * Free up an xmlXPathObjectPtr object.
2182 */
2183void
2184xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2185 if (obj == NULL) return;
2186 if (obj->type == XPATH_NODESET) {
Daniel Veillard77851712001-02-27 21:54:07 +00002187 if (obj->boolval) {
2188 obj->type = XPATH_XSLT_TREE;
2189 if (obj->nodesetval != NULL)
2190 xmlXPathFreeValueTree(obj->nodesetval);
2191 } else {
2192 if (obj->nodesetval != NULL)
2193 xmlXPathFreeNodeSet(obj->nodesetval);
2194 }
Owen Taylor3473f882001-02-23 17:55:21 +00002195#ifdef LIBXML_XPTR_ENABLED
2196 } else if (obj->type == XPATH_LOCATIONSET) {
2197 if (obj->user != NULL)
2198 xmlXPtrFreeLocationSet(obj->user);
2199#endif
2200 } else if (obj->type == XPATH_STRING) {
2201 if (obj->stringval != NULL)
2202 xmlFree(obj->stringval);
2203 } else if (obj->type == XPATH_XSLT_TREE) {
2204 if (obj->nodesetval != NULL)
2205 xmlXPathFreeValueTree(obj->nodesetval);
2206 }
2207
Owen Taylor3473f882001-02-23 17:55:21 +00002208 xmlFree(obj);
2209}
2210
2211/************************************************************************
2212 * *
2213 * Routines to handle XPath contexts *
2214 * *
2215 ************************************************************************/
2216
2217/**
2218 * xmlXPathNewContext:
2219 * @doc: the XML document
2220 *
2221 * Create a new xmlXPathContext
2222 *
2223 * Returns the xmlXPathContext just allocated.
2224 */
2225xmlXPathContextPtr
2226xmlXPathNewContext(xmlDocPtr doc) {
2227 xmlXPathContextPtr ret;
2228
2229 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
2230 if (ret == NULL) {
2231 xmlGenericError(xmlGenericErrorContext,
2232 "xmlXPathNewContext: out of memory\n");
2233 return(NULL);
2234 }
2235 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
2236 ret->doc = doc;
2237 ret->node = NULL;
2238
2239 ret->varHash = NULL;
2240
2241 ret->nb_types = 0;
2242 ret->max_types = 0;
2243 ret->types = NULL;
2244
2245 ret->funcHash = xmlHashCreate(0);
2246
2247 ret->nb_axis = 0;
2248 ret->max_axis = 0;
2249 ret->axis = NULL;
2250
2251 ret->nsHash = NULL;
2252 ret->user = NULL;
2253
2254 ret->contextSize = -1;
2255 ret->proximityPosition = -1;
2256
2257 xmlXPathRegisterAllFunctions(ret);
2258
2259 return(ret);
2260}
2261
2262/**
2263 * xmlXPathFreeContext:
2264 * @ctxt: the context to free
2265 *
2266 * Free up an xmlXPathContext
2267 */
2268void
2269xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
2270 xmlXPathRegisteredNsCleanup(ctxt);
2271 xmlXPathRegisteredFuncsCleanup(ctxt);
2272 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00002273 xmlFree(ctxt);
2274}
2275
2276/************************************************************************
2277 * *
2278 * Routines to handle XPath parser contexts *
2279 * *
2280 ************************************************************************/
2281
2282#define CHECK_CTXT(ctxt) \
2283 if (ctxt == NULL) { \
2284 xmlGenericError(xmlGenericErrorContext, \
2285 "%s:%d Internal error: ctxt == NULL\n", \
2286 __FILE__, __LINE__); \
2287 } \
2288
2289
2290#define CHECK_CONTEXT(ctxt) \
2291 if (ctxt == NULL) { \
2292 xmlGenericError(xmlGenericErrorContext, \
2293 "%s:%d Internal error: no context\n", \
2294 __FILE__, __LINE__); \
2295 } \
2296 else if (ctxt->doc == NULL) { \
2297 xmlGenericError(xmlGenericErrorContext, \
2298 "%s:%d Internal error: no document\n", \
2299 __FILE__, __LINE__); \
2300 } \
2301 else if (ctxt->doc->children == NULL) { \
2302 xmlGenericError(xmlGenericErrorContext, \
2303 "%s:%d Internal error: document without root\n", \
2304 __FILE__, __LINE__); \
2305 } \
2306
2307
2308/**
2309 * xmlXPathNewParserContext:
2310 * @str: the XPath expression
2311 * @ctxt: the XPath context
2312 *
2313 * Create a new xmlXPathParserContext
2314 *
2315 * Returns the xmlXPathParserContext just allocated.
2316 */
2317xmlXPathParserContextPtr
2318xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
2319 xmlXPathParserContextPtr ret;
2320
2321 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2322 if (ret == NULL) {
2323 xmlGenericError(xmlGenericErrorContext,
2324 "xmlXPathNewParserContext: out of memory\n");
2325 return(NULL);
2326 }
2327 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2328 ret->cur = ret->base = str;
2329 ret->context = ctxt;
2330
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002331 ret->comp = xmlXPathNewCompExpr();
2332 if (ret->comp == NULL) {
2333 xmlFree(ret->valueTab);
2334 xmlFree(ret);
2335 return(NULL);
2336 }
2337
2338 return(ret);
2339}
2340
2341/**
2342 * xmlXPathCompParserContext:
2343 * @comp: the XPath compiled expression
2344 * @ctxt: the XPath context
2345 *
2346 * Create a new xmlXPathParserContext when processing a compiled expression
2347 *
2348 * Returns the xmlXPathParserContext just allocated.
2349 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002350static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002351xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
2352 xmlXPathParserContextPtr ret;
2353
2354 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2355 if (ret == NULL) {
2356 xmlGenericError(xmlGenericErrorContext,
2357 "xmlXPathNewParserContext: out of memory\n");
2358 return(NULL);
2359 }
2360 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2361
Owen Taylor3473f882001-02-23 17:55:21 +00002362 /* Allocate the value stack */
2363 ret->valueTab = (xmlXPathObjectPtr *)
2364 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002365 if (ret->valueTab == NULL) {
2366 xmlFree(ret);
2367 xmlGenericError(xmlGenericErrorContext,
2368 "xmlXPathNewParserContext: out of memory\n");
2369 return(NULL);
2370 }
Owen Taylor3473f882001-02-23 17:55:21 +00002371 ret->valueNr = 0;
2372 ret->valueMax = 10;
2373 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002374
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00002375 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002376 ret->comp = comp;
2377
Owen Taylor3473f882001-02-23 17:55:21 +00002378 return(ret);
2379}
2380
2381/**
2382 * xmlXPathFreeParserContext:
2383 * @ctxt: the context to free
2384 *
2385 * Free up an xmlXPathParserContext
2386 */
2387void
2388xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
2389 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002390 xmlFree(ctxt->valueTab);
2391 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002392 if (ctxt->comp)
2393 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00002394 xmlFree(ctxt);
2395}
2396
2397/************************************************************************
2398 * *
2399 * The implicit core function library *
2400 * *
2401 ************************************************************************/
2402
2403/*
2404 * Auto-pop and cast to a number
2405 */
2406void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
2407
2408
2409#define POP_FLOAT \
2410 arg = valuePop(ctxt); \
2411 if (arg == NULL) { \
2412 XP_ERROR(XPATH_INVALID_OPERAND); \
2413 } \
2414 if (arg->type != XPATH_NUMBER) { \
2415 valuePush(ctxt, arg); \
2416 xmlXPathNumberFunction(ctxt, 1); \
2417 arg = valuePop(ctxt); \
2418 }
2419
2420/**
2421 * xmlXPathCompareNodeSetFloat:
2422 * @ctxt: the XPath Parser context
2423 * @inf: less than (1) or greater than (0)
2424 * @strict: is the comparison strict
2425 * @arg: the node set
2426 * @f: the value
2427 *
2428 * Implement the compare operation between a nodeset and a number
2429 * @ns < @val (1, 1, ...
2430 * @ns <= @val (1, 0, ...
2431 * @ns > @val (0, 1, ...
2432 * @ns >= @val (0, 0, ...
2433 *
2434 * If one object to be compared is a node-set and the other is a number,
2435 * then the comparison will be true if and only if there is a node in the
2436 * node-set such that the result of performing the comparison on the number
2437 * to be compared and on the result of converting the string-value of that
2438 * node to a number using the number function is true.
2439 *
2440 * Returns 0 or 1 depending on the results of the test.
2441 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002442static int
Owen Taylor3473f882001-02-23 17:55:21 +00002443xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
2444 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
2445 int i, ret = 0;
2446 xmlNodeSetPtr ns;
2447 xmlChar *str2;
2448
2449 if ((f == NULL) || (arg == NULL) ||
2450 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2451 xmlXPathFreeObject(arg);
2452 xmlXPathFreeObject(f);
2453 return(0);
2454 }
2455 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002456 if (ns != NULL) {
2457 for (i = 0;i < ns->nodeNr;i++) {
2458 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2459 if (str2 != NULL) {
2460 valuePush(ctxt,
2461 xmlXPathNewString(str2));
2462 xmlFree(str2);
2463 xmlXPathNumberFunction(ctxt, 1);
2464 valuePush(ctxt, xmlXPathObjectCopy(f));
2465 ret = xmlXPathCompareValues(ctxt, inf, strict);
2466 if (ret)
2467 break;
2468 }
2469 }
Owen Taylor3473f882001-02-23 17:55:21 +00002470 }
2471 xmlXPathFreeObject(arg);
2472 xmlXPathFreeObject(f);
2473 return(ret);
2474}
2475
2476/**
2477 * xmlXPathCompareNodeSetString:
2478 * @ctxt: the XPath Parser context
2479 * @inf: less than (1) or greater than (0)
2480 * @strict: is the comparison strict
2481 * @arg: the node set
2482 * @s: the value
2483 *
2484 * Implement the compare operation between a nodeset and a string
2485 * @ns < @val (1, 1, ...
2486 * @ns <= @val (1, 0, ...
2487 * @ns > @val (0, 1, ...
2488 * @ns >= @val (0, 0, ...
2489 *
2490 * If one object to be compared is a node-set and the other is a string,
2491 * then the comparison will be true if and only if there is a node in
2492 * the node-set such that the result of performing the comparison on the
2493 * string-value of the node and the other string is true.
2494 *
2495 * Returns 0 or 1 depending on the results of the test.
2496 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002497static int
Owen Taylor3473f882001-02-23 17:55:21 +00002498xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
2499 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
2500 int i, ret = 0;
2501 xmlNodeSetPtr ns;
2502 xmlChar *str2;
2503
2504 if ((s == NULL) || (arg == NULL) ||
2505 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2506 xmlXPathFreeObject(arg);
2507 xmlXPathFreeObject(s);
2508 return(0);
2509 }
2510 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002511 if (ns != NULL) {
2512 for (i = 0;i < ns->nodeNr;i++) {
2513 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2514 if (str2 != NULL) {
2515 valuePush(ctxt,
2516 xmlXPathNewString(str2));
2517 xmlFree(str2);
2518 valuePush(ctxt, xmlXPathObjectCopy(s));
2519 ret = xmlXPathCompareValues(ctxt, inf, strict);
2520 if (ret)
2521 break;
2522 }
2523 }
Owen Taylor3473f882001-02-23 17:55:21 +00002524 }
2525 xmlXPathFreeObject(arg);
2526 xmlXPathFreeObject(s);
2527 return(ret);
2528}
2529
2530/**
2531 * xmlXPathCompareNodeSets:
Owen Taylor3473f882001-02-23 17:55:21 +00002532 * @op: less than (-1), equal (0) or greater than (1)
2533 * @strict: is the comparison strict
2534 * @arg1: the fist node set object
2535 * @arg2: the second node set object
2536 *
2537 * Implement the compare operation on nodesets:
2538 *
2539 * If both objects to be compared are node-sets, then the comparison
2540 * will be true if and only if there is a node in the first node-set
2541 * and a node in the second node-set such that the result of performing
2542 * the comparison on the string-values of the two nodes is true.
2543 * ....
2544 * When neither object to be compared is a node-set and the operator
2545 * is <=, <, >= or >, then the objects are compared by converting both
2546 * objects to numbers and comparing the numbers according to IEEE 754.
2547 * ....
2548 * The number function converts its argument to a number as follows:
2549 * - a string that consists of optional whitespace followed by an
2550 * optional minus sign followed by a Number followed by whitespace
2551 * is converted to the IEEE 754 number that is nearest (according
2552 * to the IEEE 754 round-to-nearest rule) to the mathematical value
2553 * represented by the string; any other string is converted to NaN
2554 *
2555 * Conclusion all nodes need to be converted first to their string value
2556 * and then the comparison must be done when possible
2557 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002558static int
2559xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00002560 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2561 int i, j, init = 0;
2562 double val1;
2563 double *values2;
2564 int ret = 0;
2565 xmlChar *str;
2566 xmlNodeSetPtr ns1;
2567 xmlNodeSetPtr ns2;
2568
2569 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002570 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
2571 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002572 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002573 }
Owen Taylor3473f882001-02-23 17:55:21 +00002574 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002575 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
2576 xmlXPathFreeObject(arg1);
2577 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002578 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002579 }
Owen Taylor3473f882001-02-23 17:55:21 +00002580
2581 ns1 = arg1->nodesetval;
2582 ns2 = arg2->nodesetval;
2583
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002584 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002585 xmlXPathFreeObject(arg1);
2586 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002587 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002588 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002589 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002590 xmlXPathFreeObject(arg1);
2591 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002592 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002593 }
Owen Taylor3473f882001-02-23 17:55:21 +00002594
2595 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
2596 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002597 xmlXPathFreeObject(arg1);
2598 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002599 return(0);
2600 }
2601 for (i = 0;i < ns1->nodeNr;i++) {
2602 str = xmlNodeGetContent(ns1->nodeTab[i]);
2603 if (str == NULL)
2604 continue;
2605 val1 = xmlXPathStringEvalNumber(str);
2606 xmlFree(str);
2607 if (isnan(val1))
2608 continue;
2609 for (j = 0;j < ns2->nodeNr;j++) {
2610 if (init == 0) {
2611 str = xmlNodeGetContent(ns2->nodeTab[j]);
2612 if (str == NULL) {
2613 values2[j] = xmlXPathNAN;
2614 } else {
2615 values2[j] = xmlXPathStringEvalNumber(str);
2616 xmlFree(str);
2617 }
2618 }
2619 if (isnan(values2[j]))
2620 continue;
2621 if (inf && strict)
2622 ret = (val1 < values2[j]);
2623 else if (inf && !strict)
2624 ret = (val1 <= values2[j]);
2625 else if (!inf && strict)
2626 ret = (val1 > values2[j]);
2627 else if (!inf && !strict)
2628 ret = (val1 >= values2[j]);
2629 if (ret)
2630 break;
2631 }
2632 if (ret)
2633 break;
2634 init = 1;
2635 }
2636 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002637 xmlXPathFreeObject(arg1);
2638 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002639 return(ret);
2640 return(0);
2641}
2642
2643/**
2644 * xmlXPathCompareNodeSetValue:
2645 * @ctxt: the XPath Parser context
2646 * @inf: less than (1) or greater than (0)
2647 * @strict: is the comparison strict
2648 * @arg: the node set
2649 * @val: the value
2650 *
2651 * Implement the compare operation between a nodeset and a value
2652 * @ns < @val (1, 1, ...
2653 * @ns <= @val (1, 0, ...
2654 * @ns > @val (0, 1, ...
2655 * @ns >= @val (0, 0, ...
2656 *
2657 * If one object to be compared is a node-set and the other is a boolean,
2658 * then the comparison will be true if and only if the result of performing
2659 * the comparison on the boolean and on the result of converting
2660 * the node-set to a boolean using the boolean function is true.
2661 *
2662 * Returns 0 or 1 depending on the results of the test.
2663 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002664static int
Owen Taylor3473f882001-02-23 17:55:21 +00002665xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
2666 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
2667 if ((val == NULL) || (arg == NULL) ||
2668 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2669 return(0);
2670
2671 switch(val->type) {
2672 case XPATH_NUMBER:
2673 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
2674 case XPATH_NODESET:
2675 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002676 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00002677 case XPATH_STRING:
2678 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
2679 case XPATH_BOOLEAN:
2680 valuePush(ctxt, arg);
2681 xmlXPathBooleanFunction(ctxt, 1);
2682 valuePush(ctxt, val);
2683 return(xmlXPathCompareValues(ctxt, inf, strict));
2684 default:
2685 TODO
2686 return(0);
2687 }
2688 return(0);
2689}
2690
2691/**
2692 * xmlXPathEqualNodeSetString
2693 * @arg: the nodeset object argument
2694 * @str: the string to compare to.
2695 *
2696 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2697 * If one object to be compared is a node-set and the other is a string,
2698 * then the comparison will be true if and only if there is a node in
2699 * the node-set such that the result of performing the comparison on the
2700 * string-value of the node and the other string is true.
2701 *
2702 * Returns 0 or 1 depending on the results of the test.
2703 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002704static int
Owen Taylor3473f882001-02-23 17:55:21 +00002705xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
2706 int i;
2707 xmlNodeSetPtr ns;
2708 xmlChar *str2;
2709
2710 if ((str == NULL) || (arg == NULL) ||
2711 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2712 return(0);
2713 ns = arg->nodesetval;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002714 if (ns == NULL)
2715 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002716 if (ns->nodeNr <= 0)
2717 return(0);
2718 for (i = 0;i < ns->nodeNr;i++) {
2719 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2720 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
2721 xmlFree(str2);
2722 return(1);
2723 }
2724 if (str2 != NULL)
2725 xmlFree(str2);
2726 }
2727 return(0);
2728}
2729
2730/**
2731 * xmlXPathEqualNodeSetFloat
2732 * @arg: the nodeset object argument
2733 * @f: the float to compare to
2734 *
2735 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2736 * If one object to be compared is a node-set and the other is a number,
2737 * then the comparison will be true if and only if there is a node in
2738 * the node-set such that the result of performing the comparison on the
2739 * number to be compared and on the result of converting the string-value
2740 * of that node to a number using the number function is true.
2741 *
2742 * Returns 0 or 1 depending on the results of the test.
2743 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002744static int
Owen Taylor3473f882001-02-23 17:55:21 +00002745xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
2746 char buf[100] = "";
2747
2748 if ((arg == NULL) ||
2749 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2750 return(0);
2751
Bjorn Reesee1dc0112001-03-03 12:09:03 +00002752 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00002753 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
2754}
2755
2756
2757/**
2758 * xmlXPathEqualNodeSets
2759 * @arg1: first nodeset object argument
2760 * @arg2: second nodeset object argument
2761 *
2762 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
2763 * If both objects to be compared are node-sets, then the comparison
2764 * will be true if and only if there is a node in the first node-set and
2765 * a node in the second node-set such that the result of performing the
2766 * comparison on the string-values of the two nodes is true.
2767 *
2768 * (needless to say, this is a costly operation)
2769 *
2770 * Returns 0 or 1 depending on the results of the test.
2771 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002772static int
Owen Taylor3473f882001-02-23 17:55:21 +00002773xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2774 int i, j;
2775 xmlChar **values1;
2776 xmlChar **values2;
2777 int ret = 0;
2778 xmlNodeSetPtr ns1;
2779 xmlNodeSetPtr ns2;
2780
2781 if ((arg1 == NULL) ||
2782 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2783 return(0);
2784 if ((arg2 == NULL) ||
2785 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2786 return(0);
2787
2788 ns1 = arg1->nodesetval;
2789 ns2 = arg2->nodesetval;
2790
Daniel Veillard911f49a2001-04-07 15:39:35 +00002791 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002792 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00002793 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002794 return(0);
2795
2796 /*
2797 * check if there is a node pertaining to both sets
2798 */
2799 for (i = 0;i < ns1->nodeNr;i++)
2800 for (j = 0;j < ns2->nodeNr;j++)
2801 if (ns1->nodeTab[i] == ns2->nodeTab[j])
2802 return(1);
2803
2804 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
2805 if (values1 == NULL)
2806 return(0);
2807 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
2808 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
2809 if (values2 == NULL) {
2810 xmlFree(values1);
2811 return(0);
2812 }
2813 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
2814 for (i = 0;i < ns1->nodeNr;i++) {
2815 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
2816 for (j = 0;j < ns2->nodeNr;j++) {
2817 if (i == 0)
2818 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
2819 ret = xmlStrEqual(values1[i], values2[j]);
2820 if (ret)
2821 break;
2822 }
2823 if (ret)
2824 break;
2825 }
2826 for (i = 0;i < ns1->nodeNr;i++)
2827 if (values1[i] != NULL)
2828 xmlFree(values1[i]);
2829 for (j = 0;j < ns2->nodeNr;j++)
2830 if (values2[j] != NULL)
2831 xmlFree(values2[j]);
2832 xmlFree(values1);
2833 xmlFree(values2);
2834 return(ret);
2835}
2836
2837/**
2838 * xmlXPathEqualValues:
2839 * @ctxt: the XPath Parser context
2840 *
2841 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2842 *
2843 * Returns 0 or 1 depending on the results of the test.
2844 */
2845int
2846xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
2847 xmlXPathObjectPtr arg1, arg2;
2848 int ret = 0;
2849
2850 arg1 = valuePop(ctxt);
2851 if (arg1 == NULL)
2852 XP_ERROR0(XPATH_INVALID_OPERAND);
2853
2854 arg2 = valuePop(ctxt);
2855 if (arg2 == NULL) {
2856 xmlXPathFreeObject(arg1);
2857 XP_ERROR0(XPATH_INVALID_OPERAND);
2858 }
2859
2860 if (arg1 == arg2) {
2861#ifdef DEBUG_EXPR
2862 xmlGenericError(xmlGenericErrorContext,
2863 "Equal: by pointer\n");
2864#endif
2865 return(1);
2866 }
2867
2868 switch (arg1->type) {
2869 case XPATH_UNDEFINED:
2870#ifdef DEBUG_EXPR
2871 xmlGenericError(xmlGenericErrorContext,
2872 "Equal: undefined\n");
2873#endif
2874 break;
2875 case XPATH_XSLT_TREE:
2876 case XPATH_NODESET:
2877 switch (arg2->type) {
2878 case XPATH_UNDEFINED:
2879#ifdef DEBUG_EXPR
2880 xmlGenericError(xmlGenericErrorContext,
2881 "Equal: undefined\n");
2882#endif
2883 break;
2884 case XPATH_XSLT_TREE:
2885 case XPATH_NODESET:
2886 ret = xmlXPathEqualNodeSets(arg1, arg2);
2887 break;
2888 case XPATH_BOOLEAN:
2889 if ((arg1->nodesetval == NULL) ||
2890 (arg1->nodesetval->nodeNr == 0)) ret = 0;
2891 else
2892 ret = 1;
2893 ret = (ret == arg2->boolval);
2894 break;
2895 case XPATH_NUMBER:
2896 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
2897 break;
2898 case XPATH_STRING:
2899 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
2900 break;
2901 case XPATH_USERS:
2902 case XPATH_POINT:
2903 case XPATH_RANGE:
2904 case XPATH_LOCATIONSET:
2905 TODO
2906 break;
2907 }
2908 break;
2909 case XPATH_BOOLEAN:
2910 switch (arg2->type) {
2911 case XPATH_UNDEFINED:
2912#ifdef DEBUG_EXPR
2913 xmlGenericError(xmlGenericErrorContext,
2914 "Equal: undefined\n");
2915#endif
2916 break;
2917 case XPATH_NODESET:
2918 case XPATH_XSLT_TREE:
2919 if ((arg2->nodesetval == NULL) ||
2920 (arg2->nodesetval->nodeNr == 0)) ret = 0;
2921 else
2922 ret = 1;
2923 break;
2924 case XPATH_BOOLEAN:
2925#ifdef DEBUG_EXPR
2926 xmlGenericError(xmlGenericErrorContext,
2927 "Equal: %d boolean %d \n",
2928 arg1->boolval, arg2->boolval);
2929#endif
2930 ret = (arg1->boolval == arg2->boolval);
2931 break;
2932 case XPATH_NUMBER:
2933 if (arg2->floatval) ret = 1;
2934 else ret = 0;
2935 ret = (arg1->boolval == ret);
2936 break;
2937 case XPATH_STRING:
2938 if ((arg2->stringval == NULL) ||
2939 (arg2->stringval[0] == 0)) ret = 0;
2940 else
2941 ret = 1;
2942 ret = (arg1->boolval == ret);
2943 break;
2944 case XPATH_USERS:
2945 case XPATH_POINT:
2946 case XPATH_RANGE:
2947 case XPATH_LOCATIONSET:
2948 TODO
2949 break;
2950 }
2951 break;
2952 case XPATH_NUMBER:
2953 switch (arg2->type) {
2954 case XPATH_UNDEFINED:
2955#ifdef DEBUG_EXPR
2956 xmlGenericError(xmlGenericErrorContext,
2957 "Equal: undefined\n");
2958#endif
2959 break;
2960 case XPATH_NODESET:
2961 case XPATH_XSLT_TREE:
2962 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
2963 break;
2964 case XPATH_BOOLEAN:
2965 if (arg1->floatval) ret = 1;
2966 else ret = 0;
2967 ret = (arg2->boolval == ret);
2968 break;
2969 case XPATH_STRING:
2970 valuePush(ctxt, arg2);
2971 xmlXPathNumberFunction(ctxt, 1);
2972 arg2 = valuePop(ctxt);
2973 /* no break on purpose */
2974 case XPATH_NUMBER:
2975 ret = (arg1->floatval == arg2->floatval);
2976 break;
2977 case XPATH_USERS:
2978 case XPATH_POINT:
2979 case XPATH_RANGE:
2980 case XPATH_LOCATIONSET:
2981 TODO
2982 break;
2983 }
2984 break;
2985 case XPATH_STRING:
2986 switch (arg2->type) {
2987 case XPATH_UNDEFINED:
2988#ifdef DEBUG_EXPR
2989 xmlGenericError(xmlGenericErrorContext,
2990 "Equal: undefined\n");
2991#endif
2992 break;
2993 case XPATH_NODESET:
2994 case XPATH_XSLT_TREE:
2995 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
2996 break;
2997 case XPATH_BOOLEAN:
2998 if ((arg1->stringval == NULL) ||
2999 (arg1->stringval[0] == 0)) ret = 0;
3000 else
3001 ret = 1;
3002 ret = (arg2->boolval == ret);
3003 break;
3004 case XPATH_STRING:
3005 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
3006 break;
3007 case XPATH_NUMBER:
3008 valuePush(ctxt, arg1);
3009 xmlXPathNumberFunction(ctxt, 1);
3010 arg1 = valuePop(ctxt);
3011 ret = (arg1->floatval == arg2->floatval);
3012 break;
3013 case XPATH_USERS:
3014 case XPATH_POINT:
3015 case XPATH_RANGE:
3016 case XPATH_LOCATIONSET:
3017 TODO
3018 break;
3019 }
3020 break;
3021 case XPATH_USERS:
3022 case XPATH_POINT:
3023 case XPATH_RANGE:
3024 case XPATH_LOCATIONSET:
3025 TODO
3026 break;
3027 }
3028 xmlXPathFreeObject(arg1);
3029 xmlXPathFreeObject(arg2);
3030 return(ret);
3031}
3032
3033
3034/**
3035 * xmlXPathCompareValues:
3036 * @ctxt: the XPath Parser context
3037 * @inf: less than (1) or greater than (0)
3038 * @strict: is the comparison strict
3039 *
3040 * Implement the compare operation on XPath objects:
3041 * @arg1 < @arg2 (1, 1, ...
3042 * @arg1 <= @arg2 (1, 0, ...
3043 * @arg1 > @arg2 (0, 1, ...
3044 * @arg1 >= @arg2 (0, 0, ...
3045 *
3046 * When neither object to be compared is a node-set and the operator is
3047 * <=, <, >=, >, then the objects are compared by converted both objects
3048 * to numbers and comparing the numbers according to IEEE 754. The <
3049 * comparison will be true if and only if the first number is less than the
3050 * second number. The <= comparison will be true if and only if the first
3051 * number is less than or equal to the second number. The > comparison
3052 * will be true if and only if the first number is greater than the second
3053 * number. The >= comparison will be true if and only if the first number
3054 * is greater than or equal to the second number.
3055 *
3056 * Returns 1 if the comparaison succeeded, 0 if it failed
3057 */
3058int
3059xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
3060 int ret = 0;
3061 xmlXPathObjectPtr arg1, arg2;
3062
3063 arg2 = valuePop(ctxt);
3064 if (arg2 == NULL) {
3065 XP_ERROR0(XPATH_INVALID_OPERAND);
3066 }
3067
3068 arg1 = valuePop(ctxt);
3069 if (arg1 == NULL) {
3070 xmlXPathFreeObject(arg2);
3071 XP_ERROR0(XPATH_INVALID_OPERAND);
3072 }
3073
3074 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
3075 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003076 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003077 } else {
3078 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003079 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
3080 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003081 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003082 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
3083 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00003084 }
3085 }
3086 return(ret);
3087 }
3088
3089 if (arg1->type != XPATH_NUMBER) {
3090 valuePush(ctxt, arg1);
3091 xmlXPathNumberFunction(ctxt, 1);
3092 arg1 = valuePop(ctxt);
3093 }
3094 if (arg1->type != XPATH_NUMBER) {
3095 xmlXPathFreeObject(arg1);
3096 xmlXPathFreeObject(arg2);
3097 XP_ERROR0(XPATH_INVALID_OPERAND);
3098 }
3099 if (arg2->type != XPATH_NUMBER) {
3100 valuePush(ctxt, arg2);
3101 xmlXPathNumberFunction(ctxt, 1);
3102 arg2 = valuePop(ctxt);
3103 }
3104 if (arg2->type != XPATH_NUMBER) {
3105 xmlXPathFreeObject(arg1);
3106 xmlXPathFreeObject(arg2);
3107 XP_ERROR0(XPATH_INVALID_OPERAND);
3108 }
3109 /*
3110 * Add tests for infinity and nan
3111 * => feedback on 3.4 for Inf and NaN
3112 */
3113 if (inf && strict)
3114 ret = (arg1->floatval < arg2->floatval);
3115 else if (inf && !strict)
3116 ret = (arg1->floatval <= arg2->floatval);
3117 else if (!inf && strict)
3118 ret = (arg1->floatval > arg2->floatval);
3119 else if (!inf && !strict)
3120 ret = (arg1->floatval >= arg2->floatval);
3121 xmlXPathFreeObject(arg1);
3122 xmlXPathFreeObject(arg2);
3123 return(ret);
3124}
3125
3126/**
3127 * xmlXPathValueFlipSign:
3128 * @ctxt: the XPath Parser context
3129 *
3130 * Implement the unary - operation on an XPath object
3131 * The numeric operators convert their operands to numbers as if
3132 * by calling the number function.
3133 */
3134void
3135xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
3136 xmlXPathObjectPtr arg;
3137
3138 POP_FLOAT
3139 arg->floatval = -arg->floatval;
3140 valuePush(ctxt, arg);
3141}
3142
3143/**
3144 * xmlXPathAddValues:
3145 * @ctxt: the XPath Parser context
3146 *
3147 * Implement the add operation on XPath objects:
3148 * The numeric operators convert their operands to numbers as if
3149 * by calling the number function.
3150 */
3151void
3152xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
3153 xmlXPathObjectPtr arg;
3154 double val;
3155
3156 POP_FLOAT
3157 val = arg->floatval;
3158 xmlXPathFreeObject(arg);
3159
3160 POP_FLOAT
3161 arg->floatval += val;
3162 valuePush(ctxt, arg);
3163}
3164
3165/**
3166 * xmlXPathSubValues:
3167 * @ctxt: the XPath Parser context
3168 *
3169 * Implement the substraction operation on XPath objects:
3170 * The numeric operators convert their operands to numbers as if
3171 * by calling the number function.
3172 */
3173void
3174xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
3175 xmlXPathObjectPtr arg;
3176 double val;
3177
3178 POP_FLOAT
3179 val = arg->floatval;
3180 xmlXPathFreeObject(arg);
3181
3182 POP_FLOAT
3183 arg->floatval -= val;
3184 valuePush(ctxt, arg);
3185}
3186
3187/**
3188 * xmlXPathMultValues:
3189 * @ctxt: the XPath Parser context
3190 *
3191 * Implement the multiply operation on XPath objects:
3192 * The numeric operators convert their operands to numbers as if
3193 * by calling the number function.
3194 */
3195void
3196xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
3197 xmlXPathObjectPtr arg;
3198 double val;
3199
3200 POP_FLOAT
3201 val = arg->floatval;
3202 xmlXPathFreeObject(arg);
3203
3204 POP_FLOAT
3205 arg->floatval *= val;
3206 valuePush(ctxt, arg);
3207}
3208
3209/**
3210 * xmlXPathDivValues:
3211 * @ctxt: the XPath Parser context
3212 *
3213 * Implement the div operation on XPath objects @arg1 / @arg2:
3214 * The numeric operators convert their operands to numbers as if
3215 * by calling the number function.
3216 */
3217void
3218xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
3219 xmlXPathObjectPtr arg;
3220 double val;
3221
3222 POP_FLOAT
3223 val = arg->floatval;
3224 xmlXPathFreeObject(arg);
3225
3226 POP_FLOAT
3227 arg->floatval /= val;
3228 valuePush(ctxt, arg);
3229}
3230
3231/**
3232 * xmlXPathModValues:
3233 * @ctxt: the XPath Parser context
3234 *
3235 * Implement the mod operation on XPath objects: @arg1 / @arg2
3236 * The numeric operators convert their operands to numbers as if
3237 * by calling the number function.
3238 */
3239void
3240xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
3241 xmlXPathObjectPtr arg;
3242 int arg1, arg2;
3243
3244 POP_FLOAT
3245 arg2 = (int) arg->floatval;
3246 xmlXPathFreeObject(arg);
3247
3248 POP_FLOAT
3249 arg1 = (int) arg->floatval;
3250 arg->floatval = arg1 % arg2;
3251 valuePush(ctxt, arg);
3252}
3253
3254/************************************************************************
3255 * *
3256 * The traversal functions *
3257 * *
3258 ************************************************************************/
3259
Owen Taylor3473f882001-02-23 17:55:21 +00003260/*
3261 * A traversal function enumerates nodes along an axis.
3262 * Initially it must be called with NULL, and it indicates
3263 * termination on the axis by returning NULL.
3264 */
3265typedef xmlNodePtr (*xmlXPathTraversalFunction)
3266 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
3267
3268/**
3269 * xmlXPathNextSelf:
3270 * @ctxt: the XPath Parser context
3271 * @cur: the current node in the traversal
3272 *
3273 * Traversal function for the "self" direction
3274 * The self axis contains just the context node itself
3275 *
3276 * Returns the next element following that axis
3277 */
3278xmlNodePtr
3279xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3280 if (cur == NULL)
3281 return(ctxt->context->node);
3282 return(NULL);
3283}
3284
3285/**
3286 * xmlXPathNextChild:
3287 * @ctxt: the XPath Parser context
3288 * @cur: the current node in the traversal
3289 *
3290 * Traversal function for the "child" direction
3291 * The child axis contains the children of the context node in document order.
3292 *
3293 * Returns the next element following that axis
3294 */
3295xmlNodePtr
3296xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3297 if (cur == NULL) {
3298 if (ctxt->context->node == NULL) return(NULL);
3299 switch (ctxt->context->node->type) {
3300 case XML_ELEMENT_NODE:
3301 case XML_TEXT_NODE:
3302 case XML_CDATA_SECTION_NODE:
3303 case XML_ENTITY_REF_NODE:
3304 case XML_ENTITY_NODE:
3305 case XML_PI_NODE:
3306 case XML_COMMENT_NODE:
3307 case XML_NOTATION_NODE:
3308 case XML_DTD_NODE:
3309 return(ctxt->context->node->children);
3310 case XML_DOCUMENT_NODE:
3311 case XML_DOCUMENT_TYPE_NODE:
3312 case XML_DOCUMENT_FRAG_NODE:
3313 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003314#ifdef LIBXML_DOCB_ENABLED
3315 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003316#endif
3317 return(((xmlDocPtr) ctxt->context->node)->children);
3318 case XML_ELEMENT_DECL:
3319 case XML_ATTRIBUTE_DECL:
3320 case XML_ENTITY_DECL:
3321 case XML_ATTRIBUTE_NODE:
3322 case XML_NAMESPACE_DECL:
3323 case XML_XINCLUDE_START:
3324 case XML_XINCLUDE_END:
3325 return(NULL);
3326 }
3327 return(NULL);
3328 }
3329 if ((cur->type == XML_DOCUMENT_NODE) ||
3330 (cur->type == XML_HTML_DOCUMENT_NODE))
3331 return(NULL);
3332 return(cur->next);
3333}
3334
3335/**
3336 * xmlXPathNextDescendant:
3337 * @ctxt: the XPath Parser context
3338 * @cur: the current node in the traversal
3339 *
3340 * Traversal function for the "descendant" direction
3341 * the descendant axis contains the descendants of the context node in document
3342 * order; a descendant is a child or a child of a child and so on.
3343 *
3344 * Returns the next element following that axis
3345 */
3346xmlNodePtr
3347xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3348 if (cur == NULL) {
3349 if (ctxt->context->node == NULL)
3350 return(NULL);
3351 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3352 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3353 return(NULL);
3354
3355 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3356 return(ctxt->context->doc->children);
3357 return(ctxt->context->node->children);
3358 }
3359
3360 if (cur->children != NULL)
3361 {
3362 if (cur->children->type != XML_ENTITY_DECL)
3363 return(cur->children);
3364 }
3365 if (cur->next != NULL) return(cur->next);
3366
3367 do {
3368 cur = cur->parent;
3369 if (cur == NULL) return(NULL);
3370 if (cur == ctxt->context->node) return(NULL);
3371 if (cur->next != NULL) {
3372 cur = cur->next;
3373 return(cur);
3374 }
3375 } while (cur != NULL);
3376 return(cur);
3377}
3378
3379/**
3380 * xmlXPathNextDescendantOrSelf:
3381 * @ctxt: the XPath Parser context
3382 * @cur: the current node in the traversal
3383 *
3384 * Traversal function for the "descendant-or-self" direction
3385 * the descendant-or-self axis contains the context node and the descendants
3386 * of the context node in document order; thus the context node is the first
3387 * node on the axis, and the first child of the context node is the second node
3388 * on the axis
3389 *
3390 * Returns the next element following that axis
3391 */
3392xmlNodePtr
3393xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3394 if (cur == NULL) {
3395 if (ctxt->context->node == NULL)
3396 return(NULL);
3397 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3398 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3399 return(NULL);
3400 return(ctxt->context->node);
3401 }
3402
3403 return(xmlXPathNextDescendant(ctxt, cur));
3404}
3405
3406/**
3407 * xmlXPathNextParent:
3408 * @ctxt: the XPath Parser context
3409 * @cur: the current node in the traversal
3410 *
3411 * Traversal function for the "parent" direction
3412 * The parent axis contains the parent of the context node, if there is one.
3413 *
3414 * Returns the next element following that axis
3415 */
3416xmlNodePtr
3417xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3418 /*
3419 * the parent of an attribute or namespace node is the element
3420 * to which the attribute or namespace node is attached
3421 * Namespace handling !!!
3422 */
3423 if (cur == NULL) {
3424 if (ctxt->context->node == NULL) return(NULL);
3425 switch (ctxt->context->node->type) {
3426 case XML_ELEMENT_NODE:
3427 case XML_TEXT_NODE:
3428 case XML_CDATA_SECTION_NODE:
3429 case XML_ENTITY_REF_NODE:
3430 case XML_ENTITY_NODE:
3431 case XML_PI_NODE:
3432 case XML_COMMENT_NODE:
3433 case XML_NOTATION_NODE:
3434 case XML_DTD_NODE:
3435 case XML_ELEMENT_DECL:
3436 case XML_ATTRIBUTE_DECL:
3437 case XML_XINCLUDE_START:
3438 case XML_XINCLUDE_END:
3439 case XML_ENTITY_DECL:
3440 if (ctxt->context->node->parent == NULL)
3441 return((xmlNodePtr) ctxt->context->doc);
3442 return(ctxt->context->node->parent);
3443 case XML_ATTRIBUTE_NODE: {
3444 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3445
3446 return(att->parent);
3447 }
3448 case XML_DOCUMENT_NODE:
3449 case XML_DOCUMENT_TYPE_NODE:
3450 case XML_DOCUMENT_FRAG_NODE:
3451 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003452#ifdef LIBXML_DOCB_ENABLED
3453 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003454#endif
3455 return(NULL);
3456 case XML_NAMESPACE_DECL:
3457 /*
3458 * TODO !!! may require extending struct _xmlNs with
3459 * parent field
3460 * C.f. Infoset case...
3461 */
3462 return(NULL);
3463 }
3464 }
3465 return(NULL);
3466}
3467
3468/**
3469 * xmlXPathNextAncestor:
3470 * @ctxt: the XPath Parser context
3471 * @cur: the current node in the traversal
3472 *
3473 * Traversal function for the "ancestor" direction
3474 * the ancestor axis contains the ancestors of the context node; the ancestors
3475 * of the context node consist of the parent of context node and the parent's
3476 * parent and so on; the nodes are ordered in reverse document order; thus the
3477 * parent is the first node on the axis, and the parent's parent is the second
3478 * node on the axis
3479 *
3480 * Returns the next element following that axis
3481 */
3482xmlNodePtr
3483xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3484 /*
3485 * the parent of an attribute or namespace node is the element
3486 * to which the attribute or namespace node is attached
3487 * !!!!!!!!!!!!!
3488 */
3489 if (cur == NULL) {
3490 if (ctxt->context->node == NULL) return(NULL);
3491 switch (ctxt->context->node->type) {
3492 case XML_ELEMENT_NODE:
3493 case XML_TEXT_NODE:
3494 case XML_CDATA_SECTION_NODE:
3495 case XML_ENTITY_REF_NODE:
3496 case XML_ENTITY_NODE:
3497 case XML_PI_NODE:
3498 case XML_COMMENT_NODE:
3499 case XML_DTD_NODE:
3500 case XML_ELEMENT_DECL:
3501 case XML_ATTRIBUTE_DECL:
3502 case XML_ENTITY_DECL:
3503 case XML_NOTATION_NODE:
3504 case XML_XINCLUDE_START:
3505 case XML_XINCLUDE_END:
3506 if (ctxt->context->node->parent == NULL)
3507 return((xmlNodePtr) ctxt->context->doc);
3508 return(ctxt->context->node->parent);
3509 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003510 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003511
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003512 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00003513 }
3514 case XML_DOCUMENT_NODE:
3515 case XML_DOCUMENT_TYPE_NODE:
3516 case XML_DOCUMENT_FRAG_NODE:
3517 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003518#ifdef LIBXML_DOCB_ENABLED
3519 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003520#endif
3521 return(NULL);
3522 case XML_NAMESPACE_DECL:
3523 /*
3524 * TODO !!! may require extending struct _xmlNs with
3525 * parent field
3526 * C.f. Infoset case...
3527 */
3528 return(NULL);
3529 }
3530 return(NULL);
3531 }
3532 if (cur == ctxt->context->doc->children)
3533 return((xmlNodePtr) ctxt->context->doc);
3534 if (cur == (xmlNodePtr) ctxt->context->doc)
3535 return(NULL);
3536 switch (cur->type) {
3537 case XML_ELEMENT_NODE:
3538 case XML_TEXT_NODE:
3539 case XML_CDATA_SECTION_NODE:
3540 case XML_ENTITY_REF_NODE:
3541 case XML_ENTITY_NODE:
3542 case XML_PI_NODE:
3543 case XML_COMMENT_NODE:
3544 case XML_NOTATION_NODE:
3545 case XML_DTD_NODE:
3546 case XML_ELEMENT_DECL:
3547 case XML_ATTRIBUTE_DECL:
3548 case XML_ENTITY_DECL:
3549 case XML_XINCLUDE_START:
3550 case XML_XINCLUDE_END:
3551 return(cur->parent);
3552 case XML_ATTRIBUTE_NODE: {
3553 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3554
3555 return(att->parent);
3556 }
3557 case XML_DOCUMENT_NODE:
3558 case XML_DOCUMENT_TYPE_NODE:
3559 case XML_DOCUMENT_FRAG_NODE:
3560 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003561#ifdef LIBXML_DOCB_ENABLED
3562 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003563#endif
3564 return(NULL);
3565 case XML_NAMESPACE_DECL:
3566 /*
3567 * TODO !!! may require extending struct _xmlNs with
3568 * parent field
3569 * C.f. Infoset case...
3570 */
3571 return(NULL);
3572 }
3573 return(NULL);
3574}
3575
3576/**
3577 * xmlXPathNextAncestorOrSelf:
3578 * @ctxt: the XPath Parser context
3579 * @cur: the current node in the traversal
3580 *
3581 * Traversal function for the "ancestor-or-self" direction
3582 * he ancestor-or-self axis contains the context node and ancestors of
3583 * the context node in reverse document order; thus the context node is
3584 * the first node on the axis, and the context node's parent the second;
3585 * parent here is defined the same as with the parent axis.
3586 *
3587 * Returns the next element following that axis
3588 */
3589xmlNodePtr
3590xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3591 if (cur == NULL)
3592 return(ctxt->context->node);
3593 return(xmlXPathNextAncestor(ctxt, cur));
3594}
3595
3596/**
3597 * xmlXPathNextFollowingSibling:
3598 * @ctxt: the XPath Parser context
3599 * @cur: the current node in the traversal
3600 *
3601 * Traversal function for the "following-sibling" direction
3602 * The following-sibling axis contains the following siblings of the context
3603 * node in document order.
3604 *
3605 * Returns the next element following that axis
3606 */
3607xmlNodePtr
3608xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3609 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3610 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3611 return(NULL);
3612 if (cur == (xmlNodePtr) ctxt->context->doc)
3613 return(NULL);
3614 if (cur == NULL)
3615 return(ctxt->context->node->next);
3616 return(cur->next);
3617}
3618
3619/**
3620 * xmlXPathNextPrecedingSibling:
3621 * @ctxt: the XPath Parser context
3622 * @cur: the current node in the traversal
3623 *
3624 * Traversal function for the "preceding-sibling" direction
3625 * The preceding-sibling axis contains the preceding siblings of the context
3626 * node in reverse document order; the first preceding sibling is first on the
3627 * axis; the sibling preceding that node is the second on the axis and so on.
3628 *
3629 * Returns the next element following that axis
3630 */
3631xmlNodePtr
3632xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3633 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3634 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3635 return(NULL);
3636 if (cur == (xmlNodePtr) ctxt->context->doc)
3637 return(NULL);
3638 if (cur == NULL)
3639 return(ctxt->context->node->prev);
3640 return(cur->prev);
3641}
3642
3643/**
3644 * xmlXPathNextFollowing:
3645 * @ctxt: the XPath Parser context
3646 * @cur: the current node in the traversal
3647 *
3648 * Traversal function for the "following" direction
3649 * The following axis contains all nodes in the same document as the context
3650 * node that are after the context node in document order, excluding any
3651 * descendants and excluding attribute nodes and namespace nodes; the nodes
3652 * are ordered in document order
3653 *
3654 * Returns the next element following that axis
3655 */
3656xmlNodePtr
3657xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3658 if (cur != NULL && cur->children != NULL)
3659 return cur->children ;
3660 if (cur == NULL) cur = ctxt->context->node;
3661 if (cur == NULL) return(NULL) ; /* ERROR */
3662 if (cur->next != NULL) return(cur->next) ;
3663 do {
3664 cur = cur->parent;
3665 if (cur == NULL) return(NULL);
3666 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3667 if (cur->next != NULL) return(cur->next);
3668 } while (cur != NULL);
3669 return(cur);
3670}
3671
3672/*
3673 * xmlXPathIsAncestor:
3674 * @ancestor: the ancestor node
3675 * @node: the current node
3676 *
3677 * Check that @ancestor is a @node's ancestor
3678 *
3679 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3680 */
3681static int
3682xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3683 if ((ancestor == NULL) || (node == NULL)) return(0);
3684 /* nodes need to be in the same document */
3685 if (ancestor->doc != node->doc) return(0);
3686 /* avoid searching if ancestor or node is the root node */
3687 if (ancestor == (xmlNodePtr) node->doc) return(1);
3688 if (node == (xmlNodePtr) ancestor->doc) return(0);
3689 while (node->parent != NULL) {
3690 if (node->parent == ancestor)
3691 return(1);
3692 node = node->parent;
3693 }
3694 return(0);
3695}
3696
3697/**
3698 * xmlXPathNextPreceding:
3699 * @ctxt: the XPath Parser context
3700 * @cur: the current node in the traversal
3701 *
3702 * Traversal function for the "preceding" direction
3703 * the preceding axis contains all nodes in the same document as the context
3704 * node that are before the context node in document order, excluding any
3705 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3706 * ordered in reverse document order
3707 *
3708 * Returns the next element following that axis
3709 */
3710xmlNodePtr
3711xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3712 if (cur == NULL)
3713 cur = ctxt->context->node ;
3714 do {
3715 if (cur->prev != NULL) {
3716 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3717 ;
3718 return(cur) ;
3719 }
3720
3721 cur = cur->parent;
3722 if (cur == NULL) return(NULL);
3723 if (cur == ctxt->context->doc->children) return(NULL);
3724 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3725 return(cur);
3726}
3727
3728/**
3729 * xmlXPathNextNamespace:
3730 * @ctxt: the XPath Parser context
3731 * @cur: the current attribute in the traversal
3732 *
3733 * Traversal function for the "namespace" direction
3734 * the namespace axis contains the namespace nodes of the context node;
3735 * the order of nodes on this axis is implementation-defined; the axis will
3736 * be empty unless the context node is an element
3737 *
3738 * Returns the next element following that axis
3739 */
3740xmlNodePtr
3741xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3742 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3743 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3744 if (ctxt->context->namespaces != NULL)
3745 xmlFree(ctxt->context->namespaces);
3746 ctxt->context->namespaces =
3747 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3748 if (ctxt->context->namespaces == NULL) return(NULL);
3749 ctxt->context->nsNr = 0;
3750 }
3751 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3752}
3753
3754/**
3755 * xmlXPathNextAttribute:
3756 * @ctxt: the XPath Parser context
3757 * @cur: the current attribute in the traversal
3758 *
3759 * Traversal function for the "attribute" direction
3760 * TODO: support DTD inherited default attributes
3761 *
3762 * Returns the next element following that axis
3763 */
3764xmlNodePtr
3765xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00003766 if (ctxt->context->node == NULL)
3767 return(NULL);
3768 if (ctxt->context->node->type != XML_ELEMENT_NODE)
3769 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003770 if (cur == NULL) {
3771 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3772 return(NULL);
3773 return((xmlNodePtr)ctxt->context->node->properties);
3774 }
3775 return((xmlNodePtr)cur->next);
3776}
3777
3778/************************************************************************
3779 * *
3780 * NodeTest Functions *
3781 * *
3782 ************************************************************************/
3783
Owen Taylor3473f882001-02-23 17:55:21 +00003784#define IS_FUNCTION 200
3785
Owen Taylor3473f882001-02-23 17:55:21 +00003786
3787/************************************************************************
3788 * *
3789 * Implicit tree core function library *
3790 * *
3791 ************************************************************************/
3792
3793/**
3794 * xmlXPathRoot:
3795 * @ctxt: the XPath Parser context
3796 *
3797 * Initialize the context to the root of the document
3798 */
3799void
3800xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
3801 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
3802 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3803}
3804
3805/************************************************************************
3806 * *
3807 * The explicit core function library *
3808 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
3809 * *
3810 ************************************************************************/
3811
3812
3813/**
3814 * xmlXPathLastFunction:
3815 * @ctxt: the XPath Parser context
3816 * @nargs: the number of arguments
3817 *
3818 * Implement the last() XPath function
3819 * number last()
3820 * The last function returns the number of nodes in the context node list.
3821 */
3822void
3823xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3824 CHECK_ARITY(0);
3825 if (ctxt->context->contextSize >= 0) {
3826 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
3827#ifdef DEBUG_EXPR
3828 xmlGenericError(xmlGenericErrorContext,
3829 "last() : %d\n", ctxt->context->contextSize);
3830#endif
3831 } else {
3832 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
3833 }
3834}
3835
3836/**
3837 * xmlXPathPositionFunction:
3838 * @ctxt: the XPath Parser context
3839 * @nargs: the number of arguments
3840 *
3841 * Implement the position() XPath function
3842 * number position()
3843 * The position function returns the position of the context node in the
3844 * context node list. The first position is 1, and so the last positionr
3845 * will be equal to last().
3846 */
3847void
3848xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3849 CHECK_ARITY(0);
3850 if (ctxt->context->proximityPosition >= 0) {
3851 valuePush(ctxt,
3852 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
3853#ifdef DEBUG_EXPR
3854 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
3855 ctxt->context->proximityPosition);
3856#endif
3857 } else {
3858 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
3859 }
3860}
3861
3862/**
3863 * xmlXPathCountFunction:
3864 * @ctxt: the XPath Parser context
3865 * @nargs: the number of arguments
3866 *
3867 * Implement the count() XPath function
3868 * number count(node-set)
3869 */
3870void
3871xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3872 xmlXPathObjectPtr cur;
3873
3874 CHECK_ARITY(1);
3875 if ((ctxt->value == NULL) ||
3876 ((ctxt->value->type != XPATH_NODESET) &&
3877 (ctxt->value->type != XPATH_XSLT_TREE)))
3878 XP_ERROR(XPATH_INVALID_TYPE);
3879 cur = valuePop(ctxt);
3880
Daniel Veillard911f49a2001-04-07 15:39:35 +00003881 if ((cur == NULL) || (cur->nodesetval == NULL))
3882 valuePush(ctxt, xmlXPathNewFloat((double) 0));
3883 else
3884 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Owen Taylor3473f882001-02-23 17:55:21 +00003885 xmlXPathFreeObject(cur);
3886}
3887
3888/**
3889 * xmlXPathIdFunction:
3890 * @ctxt: the XPath Parser context
3891 * @nargs: the number of arguments
3892 *
3893 * Implement the id() XPath function
3894 * node-set id(object)
3895 * The id function selects elements by their unique ID
3896 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
3897 * then the result is the union of the result of applying id to the
3898 * string value of each of the nodes in the argument node-set. When the
3899 * argument to id is of any other type, the argument is converted to a
3900 * string as if by a call to the string function; the string is split
3901 * into a whitespace-separated list of tokens (whitespace is any sequence
3902 * of characters matching the production S); the result is a node-set
3903 * containing the elements in the same document as the context node that
3904 * have a unique ID equal to any of the tokens in the list.
3905 */
3906void
3907xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3908 const xmlChar *tokens;
3909 const xmlChar *cur;
3910 xmlChar *ID;
3911 xmlAttrPtr attr;
3912 xmlNodePtr elem = NULL;
3913 xmlXPathObjectPtr ret, obj;
3914
3915 CHECK_ARITY(1);
3916 obj = valuePop(ctxt);
3917 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3918 if (obj->type == XPATH_NODESET) {
3919 xmlXPathObjectPtr newobj;
3920 int i;
3921
3922 ret = xmlXPathNewNodeSet(NULL);
3923
Daniel Veillard911f49a2001-04-07 15:39:35 +00003924 if (obj->nodesetval != NULL) {
3925 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3926 valuePush(ctxt,
3927 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
3928 xmlXPathStringFunction(ctxt, 1);
3929 xmlXPathIdFunction(ctxt, 1);
3930 newobj = valuePop(ctxt);
3931 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
3932 newobj->nodesetval);
3933 xmlXPathFreeObject(newobj);
3934 }
Owen Taylor3473f882001-02-23 17:55:21 +00003935 }
3936
3937 xmlXPathFreeObject(obj);
3938 valuePush(ctxt, ret);
3939 return;
3940 }
3941 if (obj->type != XPATH_STRING) {
3942 valuePush(ctxt, obj);
3943 xmlXPathStringFunction(ctxt, 1);
3944 obj = valuePop(ctxt);
3945 if (obj->type != XPATH_STRING) {
3946 xmlXPathFreeObject(obj);
3947 return;
3948 }
3949 }
3950 tokens = obj->stringval;
3951
3952 ret = xmlXPathNewNodeSet(NULL);
3953 valuePush(ctxt, ret);
3954 if (tokens == NULL) {
3955 xmlXPathFreeObject(obj);
3956 return;
3957 }
3958
3959 cur = tokens;
3960
3961 while (IS_BLANK(*cur)) cur++;
3962 while (*cur != 0) {
3963 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
3964 (*cur == '.') || (*cur == '-') ||
3965 (*cur == '_') || (*cur == ':') ||
3966 (IS_COMBINING(*cur)) ||
3967 (IS_EXTENDER(*cur)))
3968 cur++;
3969
3970 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
3971
3972 ID = xmlStrndup(tokens, cur - tokens);
3973 attr = xmlGetID(ctxt->context->doc, ID);
3974 if (attr != NULL) {
3975 elem = attr->parent;
3976 xmlXPathNodeSetAdd(ret->nodesetval, elem);
3977 }
3978 if (ID != NULL)
3979 xmlFree(ID);
3980
3981 while (IS_BLANK(*cur)) cur++;
3982 tokens = cur;
3983 }
3984 xmlXPathFreeObject(obj);
3985 return;
3986}
3987
3988/**
3989 * xmlXPathLocalNameFunction:
3990 * @ctxt: the XPath Parser context
3991 * @nargs: the number of arguments
3992 *
3993 * Implement the local-name() XPath function
3994 * string local-name(node-set?)
3995 * The local-name function returns a string containing the local part
3996 * of the name of the node in the argument node-set that is first in
3997 * document order. If the node-set is empty or the first node has no
3998 * name, an empty string is returned. If the argument is omitted it
3999 * defaults to the context node.
4000 */
4001void
4002xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4003 xmlXPathObjectPtr cur;
4004
4005 if (nargs == 0) {
4006 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4007 nargs = 1;
4008 }
4009
4010 CHECK_ARITY(1);
4011 if ((ctxt->value == NULL) ||
4012 ((ctxt->value->type != XPATH_NODESET) &&
4013 (ctxt->value->type != XPATH_XSLT_TREE)))
4014 XP_ERROR(XPATH_INVALID_TYPE);
4015 cur = valuePop(ctxt);
4016
Daniel Veillard911f49a2001-04-07 15:39:35 +00004017 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004018 valuePush(ctxt, xmlXPathNewCString(""));
4019 } else {
4020 int i = 0; /* Should be first in document order !!!!! */
4021 switch (cur->nodesetval->nodeTab[i]->type) {
4022 case XML_ELEMENT_NODE:
4023 case XML_ATTRIBUTE_NODE:
4024 case XML_PI_NODE:
4025 valuePush(ctxt,
4026 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
4027 break;
4028 case XML_NAMESPACE_DECL:
4029 valuePush(ctxt, xmlXPathNewString(
4030 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
4031 break;
4032 default:
4033 valuePush(ctxt, xmlXPathNewCString(""));
4034 }
4035 }
4036 xmlXPathFreeObject(cur);
4037}
4038
4039/**
4040 * xmlXPathNamespaceURIFunction:
4041 * @ctxt: the XPath Parser context
4042 * @nargs: the number of arguments
4043 *
4044 * Implement the namespace-uri() XPath function
4045 * string namespace-uri(node-set?)
4046 * The namespace-uri function returns a string containing the
4047 * namespace URI of the expanded name of the node in the argument
4048 * node-set that is first in document order. If the node-set is empty,
4049 * the first node has no name, or the expanded name has no namespace
4050 * URI, an empty string is returned. If the argument is omitted it
4051 * defaults to the context node.
4052 */
4053void
4054xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4055 xmlXPathObjectPtr cur;
4056
4057 if (nargs == 0) {
4058 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4059 nargs = 1;
4060 }
4061 CHECK_ARITY(1);
4062 if ((ctxt->value == NULL) ||
4063 ((ctxt->value->type != XPATH_NODESET) &&
4064 (ctxt->value->type != XPATH_XSLT_TREE)))
4065 XP_ERROR(XPATH_INVALID_TYPE);
4066 cur = valuePop(ctxt);
4067
Daniel Veillard911f49a2001-04-07 15:39:35 +00004068 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004069 valuePush(ctxt, xmlXPathNewCString(""));
4070 } else {
4071 int i = 0; /* Should be first in document order !!!!! */
4072 switch (cur->nodesetval->nodeTab[i]->type) {
4073 case XML_ELEMENT_NODE:
4074 case XML_ATTRIBUTE_NODE:
4075 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4076 valuePush(ctxt, xmlXPathNewCString(""));
4077 else
4078 valuePush(ctxt, xmlXPathNewString(
4079 cur->nodesetval->nodeTab[i]->ns->href));
4080 break;
4081 default:
4082 valuePush(ctxt, xmlXPathNewCString(""));
4083 }
4084 }
4085 xmlXPathFreeObject(cur);
4086}
4087
4088/**
4089 * xmlXPathNameFunction:
4090 * @ctxt: the XPath Parser context
4091 * @nargs: the number of arguments
4092 *
4093 * Implement the name() XPath function
4094 * string name(node-set?)
4095 * The name function returns a string containing a QName representing
4096 * the name of the node in the argument node-set that is first in documenti
4097 * order. The QName must represent the name with respect to the namespace
4098 * declarations in effect on the node whose name is being represented.
4099 * Typically, this will be the form in which the name occurred in the XML
4100 * source. This need not be the case if there are namespace declarations
4101 * in effect on the node that associate multiple prefixes with the same
4102 * namespace. However, an implementation may include information about
4103 * the original prefix in its representation of nodes; in this case, an
4104 * implementation can ensure that the returned string is always the same
4105 * as the QName used in the XML source. If the argument it omitted it
4106 * defaults to the context node.
4107 * Libxml keep the original prefix so the "real qualified name" used is
4108 * returned.
4109 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004110static void
Owen Taylor3473f882001-02-23 17:55:21 +00004111xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4112 xmlXPathObjectPtr cur;
4113
4114 if (nargs == 0) {
4115 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4116 nargs = 1;
4117 }
4118
4119 CHECK_ARITY(1);
4120 if ((ctxt->value == NULL) ||
4121 ((ctxt->value->type != XPATH_NODESET) &&
4122 (ctxt->value->type != XPATH_XSLT_TREE)))
4123 XP_ERROR(XPATH_INVALID_TYPE);
4124 cur = valuePop(ctxt);
4125
Daniel Veillard911f49a2001-04-07 15:39:35 +00004126 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004127 valuePush(ctxt, xmlXPathNewCString(""));
4128 } else {
4129 int i = 0; /* Should be first in document order !!!!! */
4130
4131 switch (cur->nodesetval->nodeTab[i]->type) {
4132 case XML_ELEMENT_NODE:
4133 case XML_ATTRIBUTE_NODE:
4134 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4135 valuePush(ctxt, xmlXPathNewString(
4136 cur->nodesetval->nodeTab[i]->name));
4137
4138 else {
4139 char name[2000];
Owen Taylor3473f882001-02-23 17:55:21 +00004140 snprintf(name, sizeof(name), "%s:%s",
4141 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4142 (char *) cur->nodesetval->nodeTab[i]->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004143 name[sizeof(name) - 1] = 0;
4144 valuePush(ctxt, xmlXPathNewCString(name));
4145 }
4146 break;
4147 default:
4148 valuePush(ctxt,
4149 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4150 xmlXPathLocalNameFunction(ctxt, 1);
4151 }
4152 }
4153 xmlXPathFreeObject(cur);
4154}
4155
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004156
4157/**
4158 * xmlXPathConvertString:
4159 * @val: an XPath object
4160 *
4161 * Converts an existing object to its string() equivalent
4162 *
4163 * Returns the new object, the old one is freed (or the operation
4164 * is done directly on @val)
4165 */
4166xmlXPathObjectPtr
4167xmlXPathConvertString(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004168 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004169
4170 if (val == NULL)
4171 return(xmlXPathNewCString(""));
4172 switch (val->type) {
4173 case XPATH_UNDEFINED:
4174#ifdef DEBUG_EXPR
4175 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4176#endif
4177 ret = xmlXPathNewCString("");
4178 break;
4179 case XPATH_XSLT_TREE:
4180 case XPATH_NODESET:
Daniel Veillard911f49a2001-04-07 15:39:35 +00004181 if ((val->nodesetval == NULL) || (val->nodesetval->nodeNr == 0)) {
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004182 ret = xmlXPathNewCString("");
4183 } else {
4184 xmlChar *res;
4185
4186 xmlXPathNodeSetSort(val->nodesetval);
4187 res = xmlNodeGetContent(val->nodesetval->nodeTab[0]);
4188 /* TODO: avoid allocating res to free it */
4189 ret = xmlXPathNewString(res);
4190 if (res != NULL)
4191 xmlFree(res);
4192 }
4193 break;
4194 case XPATH_STRING:
4195 return(val);
4196 case XPATH_BOOLEAN:
4197 if (val->boolval) ret = xmlXPathNewCString("true");
4198 else ret = xmlXPathNewCString("false");
4199 break;
4200 case XPATH_NUMBER: {
4201 char buf[100];
4202
4203 xmlXPathFormatNumber(val->floatval, buf, sizeof(buf));
4204 ret = xmlXPathNewCString(buf);
4205 break;
4206 }
4207 case XPATH_USERS:
4208 case XPATH_POINT:
4209 case XPATH_RANGE:
4210 case XPATH_LOCATIONSET:
4211 TODO
4212 ret = xmlXPathNewCString("");
4213 break;
4214 }
4215 xmlXPathFreeObject(val);
4216 return(ret);
4217}
4218
Owen Taylor3473f882001-02-23 17:55:21 +00004219/**
4220 * xmlXPathStringFunction:
4221 * @ctxt: the XPath Parser context
4222 * @nargs: the number of arguments
4223 *
4224 * Implement the string() XPath function
4225 * string string(object?)
4226 * he string function converts an object to a string as follows:
4227 * - A node-set is converted to a string by returning the value of
4228 * the node in the node-set that is first in document order.
4229 * If the node-set is empty, an empty string is returned.
4230 * - A number is converted to a string as follows
4231 * + NaN is converted to the string NaN
4232 * + positive zero is converted to the string 0
4233 * + negative zero is converted to the string 0
4234 * + positive infinity is converted to the string Infinity
4235 * + negative infinity is converted to the string -Infinity
4236 * + if the number is an integer, the number is represented in
4237 * decimal form as a Number with no decimal point and no leading
4238 * zeros, preceded by a minus sign (-) if the number is negative
4239 * + otherwise, the number is represented in decimal form as a
4240 * Number including a decimal point with at least one digit
4241 * before the decimal point and at least one digit after the
4242 * decimal point, preceded by a minus sign (-) if the number
4243 * is negative; there must be no leading zeros before the decimal
4244 * point apart possibly from the one required digit immediatelyi
4245 * before the decimal point; beyond the one required digit
4246 * after the decimal point there must be as many, but only as
4247 * many, more digits as are needed to uniquely distinguish the
4248 * number from all other IEEE 754 numeric values.
4249 * - The boolean false value is converted to the string false.
4250 * The boolean true value is converted to the string true.
4251 *
4252 * If the argument is omitted, it defaults to a node-set with the
4253 * context node as its only member.
4254 */
4255void
4256xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4257 xmlXPathObjectPtr cur;
4258
4259 if (nargs == 0) {
4260 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4261 nargs = 1;
4262 }
4263
4264 CHECK_ARITY(1);
4265 cur = valuePop(ctxt);
4266 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004267 cur = xmlXPathConvertString(cur);
4268 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004269}
4270
4271/**
4272 * xmlXPathStringLengthFunction:
4273 * @ctxt: the XPath Parser context
4274 * @nargs: the number of arguments
4275 *
4276 * Implement the string-length() XPath function
4277 * number string-length(string?)
4278 * The string-length returns the number of characters in the string
4279 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4280 * the context node converted to a string, in other words the value
4281 * of the context node.
4282 */
4283void
4284xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4285 xmlXPathObjectPtr cur;
4286
4287 if (nargs == 0) {
4288 if (ctxt->context->node == NULL) {
4289 valuePush(ctxt, xmlXPathNewFloat(0));
4290 } else {
4291 xmlChar *content;
4292
4293 content = xmlNodeGetContent(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004294 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00004295 xmlFree(content);
4296 }
4297 return;
4298 }
4299 CHECK_ARITY(1);
4300 CAST_TO_STRING;
4301 CHECK_TYPE(XPATH_STRING);
4302 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004303 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00004304 xmlXPathFreeObject(cur);
4305}
4306
4307/**
4308 * xmlXPathConcatFunction:
4309 * @ctxt: the XPath Parser context
4310 * @nargs: the number of arguments
4311 *
4312 * Implement the concat() XPath function
4313 * string concat(string, string, string*)
4314 * The concat function returns the concatenation of its arguments.
4315 */
4316void
4317xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4318 xmlXPathObjectPtr cur, newobj;
4319 xmlChar *tmp;
4320
4321 if (nargs < 2) {
4322 CHECK_ARITY(2);
4323 }
4324
4325 CAST_TO_STRING;
4326 cur = valuePop(ctxt);
4327 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4328 xmlXPathFreeObject(cur);
4329 return;
4330 }
4331 nargs--;
4332
4333 while (nargs > 0) {
4334 CAST_TO_STRING;
4335 newobj = valuePop(ctxt);
4336 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4337 xmlXPathFreeObject(newobj);
4338 xmlXPathFreeObject(cur);
4339 XP_ERROR(XPATH_INVALID_TYPE);
4340 }
4341 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4342 newobj->stringval = cur->stringval;
4343 cur->stringval = tmp;
4344
4345 xmlXPathFreeObject(newobj);
4346 nargs--;
4347 }
4348 valuePush(ctxt, cur);
4349}
4350
4351/**
4352 * xmlXPathContainsFunction:
4353 * @ctxt: the XPath Parser context
4354 * @nargs: the number of arguments
4355 *
4356 * Implement the contains() XPath function
4357 * boolean contains(string, string)
4358 * The contains function returns true if the first argument string
4359 * contains the second argument string, and otherwise returns false.
4360 */
4361void
4362xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4363 xmlXPathObjectPtr hay, needle;
4364
4365 CHECK_ARITY(2);
4366 CAST_TO_STRING;
4367 CHECK_TYPE(XPATH_STRING);
4368 needle = valuePop(ctxt);
4369 CAST_TO_STRING;
4370 hay = valuePop(ctxt);
4371 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4372 xmlXPathFreeObject(hay);
4373 xmlXPathFreeObject(needle);
4374 XP_ERROR(XPATH_INVALID_TYPE);
4375 }
4376 if (xmlStrstr(hay->stringval, needle->stringval))
4377 valuePush(ctxt, xmlXPathNewBoolean(1));
4378 else
4379 valuePush(ctxt, xmlXPathNewBoolean(0));
4380 xmlXPathFreeObject(hay);
4381 xmlXPathFreeObject(needle);
4382}
4383
4384/**
4385 * xmlXPathStartsWithFunction:
4386 * @ctxt: the XPath Parser context
4387 * @nargs: the number of arguments
4388 *
4389 * Implement the starts-with() XPath function
4390 * boolean starts-with(string, string)
4391 * The starts-with function returns true if the first argument string
4392 * starts with the second argument string, and otherwise returns false.
4393 */
4394void
4395xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4396 xmlXPathObjectPtr hay, needle;
4397 int n;
4398
4399 CHECK_ARITY(2);
4400 CAST_TO_STRING;
4401 CHECK_TYPE(XPATH_STRING);
4402 needle = valuePop(ctxt);
4403 CAST_TO_STRING;
4404 hay = valuePop(ctxt);
4405 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4406 xmlXPathFreeObject(hay);
4407 xmlXPathFreeObject(needle);
4408 XP_ERROR(XPATH_INVALID_TYPE);
4409 }
4410 n = xmlStrlen(needle->stringval);
4411 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4412 valuePush(ctxt, xmlXPathNewBoolean(0));
4413 else
4414 valuePush(ctxt, xmlXPathNewBoolean(1));
4415 xmlXPathFreeObject(hay);
4416 xmlXPathFreeObject(needle);
4417}
4418
4419/**
4420 * xmlXPathSubstringFunction:
4421 * @ctxt: the XPath Parser context
4422 * @nargs: the number of arguments
4423 *
4424 * Implement the substring() XPath function
4425 * string substring(string, number, number?)
4426 * The substring function returns the substring of the first argument
4427 * starting at the position specified in the second argument with
4428 * length specified in the third argument. For example,
4429 * substring("12345",2,3) returns "234". If the third argument is not
4430 * specified, it returns the substring starting at the position specified
4431 * in the second argument and continuing to the end of the string. For
4432 * example, substring("12345",2) returns "2345". More precisely, each
4433 * character in the string (see [3.6 Strings]) is considered to have a
4434 * numeric position: the position of the first character is 1, the position
4435 * of the second character is 2 and so on. The returned substring contains
4436 * those characters for which the position of the character is greater than
4437 * or equal to the second argument and, if the third argument is specified,
4438 * less than the sum of the second and third arguments; the comparisons
4439 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4440 * - substring("12345", 1.5, 2.6) returns "234"
4441 * - substring("12345", 0, 3) returns "12"
4442 * - substring("12345", 0 div 0, 3) returns ""
4443 * - substring("12345", 1, 0 div 0) returns ""
4444 * - substring("12345", -42, 1 div 0) returns "12345"
4445 * - substring("12345", -1 div 0, 1 div 0) returns ""
4446 */
4447void
4448xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4449 xmlXPathObjectPtr str, start, len;
4450 double le, in;
4451 int i, l;
4452 xmlChar *ret;
4453
4454 /*
Daniel Veillarde043ee12001-04-16 14:08:07 +00004455 * TODO: need to be converted to UTF8 strings
Owen Taylor3473f882001-02-23 17:55:21 +00004456 */
4457 if (nargs < 2) {
4458 CHECK_ARITY(2);
4459 }
4460 if (nargs > 3) {
4461 CHECK_ARITY(3);
4462 }
4463 if (nargs == 3) {
4464 CAST_TO_NUMBER;
4465 CHECK_TYPE(XPATH_NUMBER);
4466 len = valuePop(ctxt);
4467 le = len->floatval;
4468 xmlXPathFreeObject(len);
4469 } else {
4470 le = 2000000000;
4471 }
4472 CAST_TO_NUMBER;
4473 CHECK_TYPE(XPATH_NUMBER);
4474 start = valuePop(ctxt);
4475 in = start->floatval;
4476 xmlXPathFreeObject(start);
4477 CAST_TO_STRING;
4478 CHECK_TYPE(XPATH_STRING);
4479 str = valuePop(ctxt);
4480 le += in;
4481
4482 /* integer index of the first char */
4483 i = (int) in;
4484 if (((double)i) != in) i++;
4485
4486 /* integer index of the last char */
4487 l = (int) le;
4488 if (((double)l) != le) l++;
4489
4490 /* back to a zero based len */
4491 i--;
4492 l--;
4493
4494 /* check against the string len */
4495 if (l > 1024) {
4496 l = xmlStrlen(str->stringval);
4497 }
4498 if (i < 0) {
4499 i = 0;
4500 }
4501
4502 /* number of chars to copy */
4503 l -= i;
4504
4505 ret = xmlStrsub(str->stringval, i, l);
4506 if (ret == NULL)
4507 valuePush(ctxt, xmlXPathNewCString(""));
4508 else {
4509 valuePush(ctxt, xmlXPathNewString(ret));
4510 xmlFree(ret);
4511 }
4512 xmlXPathFreeObject(str);
4513}
4514
4515/**
4516 * xmlXPathSubstringBeforeFunction:
4517 * @ctxt: the XPath Parser context
4518 * @nargs: the number of arguments
4519 *
4520 * Implement the substring-before() XPath function
4521 * string substring-before(string, string)
4522 * The substring-before function returns the substring of the first
4523 * argument string that precedes the first occurrence of the second
4524 * argument string in the first argument string, or the empty string
4525 * if the first argument string does not contain the second argument
4526 * string. For example, substring-before("1999/04/01","/") returns 1999.
4527 */
4528void
4529xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4530 xmlXPathObjectPtr str;
4531 xmlXPathObjectPtr find;
4532 xmlBufferPtr target;
4533 const xmlChar *point;
4534 int offset;
4535
4536 CHECK_ARITY(2);
4537 CAST_TO_STRING;
4538 find = valuePop(ctxt);
4539 CAST_TO_STRING;
4540 str = valuePop(ctxt);
4541
4542 target = xmlBufferCreate();
4543 if (target) {
4544 point = xmlStrstr(str->stringval, find->stringval);
4545 if (point) {
4546 offset = (int)(point - str->stringval);
4547 xmlBufferAdd(target, str->stringval, offset);
4548 }
4549 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4550 xmlBufferFree(target);
4551 }
4552
4553 xmlXPathFreeObject(str);
4554 xmlXPathFreeObject(find);
4555}
4556
4557/**
4558 * xmlXPathSubstringAfterFunction:
4559 * @ctxt: the XPath Parser context
4560 * @nargs: the number of arguments
4561 *
4562 * Implement the substring-after() XPath function
4563 * string substring-after(string, string)
4564 * The substring-after function returns the substring of the first
4565 * argument string that follows the first occurrence of the second
4566 * argument string in the first argument string, or the empty stringi
4567 * if the first argument string does not contain the second argument
4568 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4569 * and substring-after("1999/04/01","19") returns 99/04/01.
4570 */
4571void
4572xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4573 xmlXPathObjectPtr str;
4574 xmlXPathObjectPtr find;
4575 xmlBufferPtr target;
4576 const xmlChar *point;
4577 int offset;
4578
4579 CHECK_ARITY(2);
4580 CAST_TO_STRING;
4581 find = valuePop(ctxt);
4582 CAST_TO_STRING;
4583 str = valuePop(ctxt);
4584
4585 target = xmlBufferCreate();
4586 if (target) {
4587 point = xmlStrstr(str->stringval, find->stringval);
4588 if (point) {
4589 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4590 xmlBufferAdd(target, &str->stringval[offset],
4591 xmlStrlen(str->stringval) - offset);
4592 }
4593 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4594 xmlBufferFree(target);
4595 }
4596
4597 xmlXPathFreeObject(str);
4598 xmlXPathFreeObject(find);
4599}
4600
4601/**
4602 * xmlXPathNormalizeFunction:
4603 * @ctxt: the XPath Parser context
4604 * @nargs: the number of arguments
4605 *
4606 * Implement the normalize-space() XPath function
4607 * string normalize-space(string?)
4608 * The normalize-space function returns the argument string with white
4609 * space normalized by stripping leading and trailing whitespace
4610 * and replacing sequences of whitespace characters by a single
4611 * space. Whitespace characters are the same allowed by the S production
4612 * in XML. If the argument is omitted, it defaults to the context
4613 * node converted to a string, in other words the value of the context node.
4614 */
4615void
4616xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4617 xmlXPathObjectPtr obj = NULL;
4618 xmlChar *source = NULL;
4619 xmlBufferPtr target;
4620 xmlChar blank;
4621
4622 if (nargs == 0) {
4623 /* Use current context node */
4624 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4625 xmlXPathStringFunction(ctxt, 1);
4626 nargs = 1;
4627 }
4628
4629 CHECK_ARITY(1);
4630 CAST_TO_STRING;
4631 CHECK_TYPE(XPATH_STRING);
4632 obj = valuePop(ctxt);
4633 source = obj->stringval;
4634
4635 target = xmlBufferCreate();
4636 if (target && source) {
4637
4638 /* Skip leading whitespaces */
4639 while (IS_BLANK(*source))
4640 source++;
4641
4642 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4643 blank = 0;
4644 while (*source) {
4645 if (IS_BLANK(*source)) {
4646 blank = *source;
4647 } else {
4648 if (blank) {
4649 xmlBufferAdd(target, &blank, 1);
4650 blank = 0;
4651 }
4652 xmlBufferAdd(target, source, 1);
4653 }
4654 source++;
4655 }
4656
4657 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4658 xmlBufferFree(target);
4659 }
4660 xmlXPathFreeObject(obj);
4661}
4662
4663/**
4664 * xmlXPathTranslateFunction:
4665 * @ctxt: the XPath Parser context
4666 * @nargs: the number of arguments
4667 *
4668 * Implement the translate() XPath function
4669 * string translate(string, string, string)
4670 * The translate function returns the first argument string with
4671 * occurrences of characters in the second argument string replaced
4672 * by the character at the corresponding position in the third argument
4673 * string. For example, translate("bar","abc","ABC") returns the string
4674 * BAr. If there is a character in the second argument string with no
4675 * character at a corresponding position in the third argument string
4676 * (because the second argument string is longer than the third argument
4677 * string), then occurrences of that character in the first argument
4678 * string are removed. For example, translate("--aaa--","abc-","ABC")
4679 * returns "AAA". If a character occurs more than once in second
4680 * argument string, then the first occurrence determines the replacement
4681 * character. If the third argument string is longer than the second
4682 * argument string, then excess characters are ignored.
4683 */
4684void
4685xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00004686 xmlXPathObjectPtr str;
4687 xmlXPathObjectPtr from;
4688 xmlXPathObjectPtr to;
4689 xmlBufferPtr target;
4690 int i, offset, max;
4691 xmlChar ch;
4692 const xmlChar *point;
Owen Taylor3473f882001-02-23 17:55:21 +00004693
Daniel Veillarde043ee12001-04-16 14:08:07 +00004694 /*
4695 * TODO: need to be converted to UTF8 strings
4696 */
4697 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00004698
Daniel Veillarde043ee12001-04-16 14:08:07 +00004699 CAST_TO_STRING;
4700 to = valuePop(ctxt);
4701 CAST_TO_STRING;
4702 from = valuePop(ctxt);
4703 CAST_TO_STRING;
4704 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00004705
Daniel Veillarde043ee12001-04-16 14:08:07 +00004706 target = xmlBufferCreate();
4707 if (target) {
4708 max = xmlStrlen(to->stringval);
4709 for (i = 0; (ch = str->stringval[i]); i++) {
4710 point = xmlStrchr(from->stringval, ch);
4711 if (point) {
4712 offset = (int)(point - from->stringval);
4713 if (offset < max)
4714 xmlBufferAdd(target, &to->stringval[offset], 1);
4715 } else
4716 xmlBufferAdd(target, &ch, 1);
4717 }
Owen Taylor3473f882001-02-23 17:55:21 +00004718 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00004719 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4720 xmlBufferFree(target);
4721 xmlXPathFreeObject(str);
4722 xmlXPathFreeObject(from);
4723 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00004724}
4725
4726/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004727 * xmlXPathConvertBoolean:
4728 * @val: an XPath object
4729 *
4730 * Converts an existing object to its boolean() equivalent
4731 *
4732 * Returns the new object, the old one is freed (or the operation
4733 * is done directly on @val)
4734 */
4735xmlXPathObjectPtr
4736xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
4737 int res = 0;
4738
4739 if (val == NULL)
4740 return(NULL);
4741 switch (val->type) {
4742 case XPATH_NODESET:
4743 case XPATH_XSLT_TREE:
4744 if ((val->nodesetval == NULL) ||
4745 (val->nodesetval->nodeNr == 0)) res = 0;
4746 else
4747 res = 1;
4748 break;
4749 case XPATH_STRING:
4750 if ((val->stringval == NULL) ||
4751 (val->stringval[0] == 0)) res = 0;
4752 else
4753 res = 1;
4754 break;
4755 case XPATH_BOOLEAN:
4756 return(val);
4757 case XPATH_NUMBER:
4758 if (val->floatval) res = 1;
4759 break;
4760 default:
4761 STRANGE
4762 }
4763 xmlXPathFreeObject(val);
4764 return(xmlXPathNewBoolean(res));
4765}
4766
4767/**
Owen Taylor3473f882001-02-23 17:55:21 +00004768 * xmlXPathBooleanFunction:
4769 * @ctxt: the XPath Parser context
4770 * @nargs: the number of arguments
4771 *
4772 * Implement the boolean() XPath function
4773 * boolean boolean(object)
4774 * he boolean function converts its argument to a boolean as follows:
4775 * - a number is true if and only if it is neither positive or
4776 * negative zero nor NaN
4777 * - a node-set is true if and only if it is non-empty
4778 * - a string is true if and only if its length is non-zero
4779 */
4780void
4781xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4782 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00004783
4784 CHECK_ARITY(1);
4785 cur = valuePop(ctxt);
4786 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004787 cur = xmlXPathConvertBoolean(cur);
4788 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004789}
4790
4791/**
4792 * xmlXPathNotFunction:
4793 * @ctxt: the XPath Parser context
4794 * @nargs: the number of arguments
4795 *
4796 * Implement the not() XPath function
4797 * boolean not(boolean)
4798 * The not function returns true if its argument is false,
4799 * and false otherwise.
4800 */
4801void
4802xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4803 CHECK_ARITY(1);
4804 CAST_TO_BOOLEAN;
4805 CHECK_TYPE(XPATH_BOOLEAN);
4806 ctxt->value->boolval = ! ctxt->value->boolval;
4807}
4808
4809/**
4810 * xmlXPathTrueFunction:
4811 * @ctxt: the XPath Parser context
4812 * @nargs: the number of arguments
4813 *
4814 * Implement the true() XPath function
4815 * boolean true()
4816 */
4817void
4818xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4819 CHECK_ARITY(0);
4820 valuePush(ctxt, xmlXPathNewBoolean(1));
4821}
4822
4823/**
4824 * xmlXPathFalseFunction:
4825 * @ctxt: the XPath Parser context
4826 * @nargs: the number of arguments
4827 *
4828 * Implement the false() XPath function
4829 * boolean false()
4830 */
4831void
4832xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4833 CHECK_ARITY(0);
4834 valuePush(ctxt, xmlXPathNewBoolean(0));
4835}
4836
4837/**
4838 * xmlXPathLangFunction:
4839 * @ctxt: the XPath Parser context
4840 * @nargs: the number of arguments
4841 *
4842 * Implement the lang() XPath function
4843 * boolean lang(string)
4844 * The lang function returns true or false depending on whether the
4845 * language of the context node as specified by xml:lang attributes
4846 * is the same as or is a sublanguage of the language specified by
4847 * the argument string. The language of the context node is determined
4848 * by the value of the xml:lang attribute on the context node, or, if
4849 * the context node has no xml:lang attribute, by the value of the
4850 * xml:lang attribute on the nearest ancestor of the context node that
4851 * has an xml:lang attribute. If there is no such attribute, then lang
4852 * returns false. If there is such an attribute, then lang returns
4853 * true if the attribute value is equal to the argument ignoring case,
4854 * or if there is some suffix starting with - such that the attribute
4855 * value is equal to the argument ignoring that suffix of the attribute
4856 * value and ignoring case.
4857 */
4858void
4859xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4860 xmlXPathObjectPtr val;
4861 const xmlChar *theLang;
4862 const xmlChar *lang;
4863 int ret = 0;
4864 int i;
4865
4866 CHECK_ARITY(1);
4867 CAST_TO_STRING;
4868 CHECK_TYPE(XPATH_STRING);
4869 val = valuePop(ctxt);
4870 lang = val->stringval;
4871 theLang = xmlNodeGetLang(ctxt->context->node);
4872 if ((theLang != NULL) && (lang != NULL)) {
4873 for (i = 0;lang[i] != 0;i++)
4874 if (toupper(lang[i]) != toupper(theLang[i]))
4875 goto not_equal;
4876 ret = 1;
4877 }
4878not_equal:
4879 xmlXPathFreeObject(val);
4880 valuePush(ctxt, xmlXPathNewBoolean(ret));
4881}
4882
4883/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004884 * xmlXPathConvertNumber:
4885 * @val: an XPath object
4886 *
4887 * Converts an existing object to its number() equivalent
4888 *
4889 * Returns the new object, the old one is freed (or the operation
4890 * is done directly on @val)
4891 */
4892xmlXPathObjectPtr
4893xmlXPathConvertNumber(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004894 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004895 double res;
4896
4897 if (val == NULL)
4898 return(xmlXPathNewFloat(0.0));
4899 switch (val->type) {
4900 case XPATH_UNDEFINED:
4901#ifdef DEBUG_EXPR
4902 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
4903#endif
4904 ret = xmlXPathNewFloat(0.0);
4905 break;
4906 case XPATH_XSLT_TREE:
4907 case XPATH_NODESET:
4908 val = xmlXPathConvertString(val);
4909 /* no break on purpose */
4910 case XPATH_STRING:
4911 res = xmlXPathStringEvalNumber(val->stringval);
4912 ret = xmlXPathNewFloat(res);
4913 break;
4914 case XPATH_BOOLEAN:
4915 if (val->boolval) ret = xmlXPathNewFloat(1.0);
4916 else ret = xmlXPathNewFloat(0.0);
4917 break;
4918 case XPATH_NUMBER:
4919 return(val);
4920 case XPATH_USERS:
4921 case XPATH_POINT:
4922 case XPATH_RANGE:
4923 case XPATH_LOCATIONSET:
4924 TODO
4925 ret = xmlXPathNewFloat(0.0);
4926 break;
4927 }
4928 xmlXPathFreeObject(val);
4929 return(ret);
4930}
4931
4932/**
Owen Taylor3473f882001-02-23 17:55:21 +00004933 * xmlXPathNumberFunction:
4934 * @ctxt: the XPath Parser context
4935 * @nargs: the number of arguments
4936 *
4937 * Implement the number() XPath function
4938 * number number(object?)
4939 */
4940void
4941xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4942 xmlXPathObjectPtr cur;
4943 double res;
4944
4945 if (nargs == 0) {
4946 if (ctxt->context->node == NULL) {
4947 valuePush(ctxt, xmlXPathNewFloat(0.0));
4948 } else {
4949 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
4950
4951 res = xmlXPathStringEvalNumber(content);
4952 valuePush(ctxt, xmlXPathNewFloat(res));
4953 xmlFree(content);
4954 }
4955 return;
4956 }
4957
4958 CHECK_ARITY(1);
4959 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004960 cur = xmlXPathConvertNumber(cur);
4961 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004962}
4963
4964/**
4965 * xmlXPathSumFunction:
4966 * @ctxt: the XPath Parser context
4967 * @nargs: the number of arguments
4968 *
4969 * Implement the sum() XPath function
4970 * number sum(node-set)
4971 * The sum function returns the sum of the values of the nodes in
4972 * the argument node-set.
4973 */
4974void
4975xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4976 xmlXPathObjectPtr cur;
4977 int i;
4978
4979 CHECK_ARITY(1);
4980 if ((ctxt->value == NULL) ||
4981 ((ctxt->value->type != XPATH_NODESET) &&
4982 (ctxt->value->type != XPATH_XSLT_TREE)))
4983 XP_ERROR(XPATH_INVALID_TYPE);
4984 cur = valuePop(ctxt);
4985
Daniel Veillardd8df6c02001-04-05 16:54:14 +00004986 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004987 valuePush(ctxt, xmlXPathNewFloat(0.0));
4988 } else {
4989 valuePush(ctxt,
4990 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
4991 xmlXPathNumberFunction(ctxt, 1);
4992 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
4993 valuePush(ctxt,
4994 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4995 xmlXPathAddValues(ctxt);
4996 }
4997 }
4998 xmlXPathFreeObject(cur);
4999}
5000
5001/**
5002 * xmlXPathFloorFunction:
5003 * @ctxt: the XPath Parser context
5004 * @nargs: the number of arguments
5005 *
5006 * Implement the floor() XPath function
5007 * number floor(number)
5008 * The floor function returns the largest (closest to positive infinity)
5009 * number that is not greater than the argument and that is an integer.
5010 */
5011void
5012xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5013 CHECK_ARITY(1);
5014 CAST_TO_NUMBER;
5015 CHECK_TYPE(XPATH_NUMBER);
5016#if 0
5017 ctxt->value->floatval = floor(ctxt->value->floatval);
5018#else
5019 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
5020 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
5021#endif
5022}
5023
5024/**
5025 * xmlXPathCeilingFunction:
5026 * @ctxt: the XPath Parser context
5027 * @nargs: the number of arguments
5028 *
5029 * Implement the ceiling() XPath function
5030 * number ceiling(number)
5031 * The ceiling function returns the smallest (closest to negative infinity)
5032 * number that is not less than the argument and that is an integer.
5033 */
5034void
5035xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5036 double f;
5037
5038 CHECK_ARITY(1);
5039 CAST_TO_NUMBER;
5040 CHECK_TYPE(XPATH_NUMBER);
5041
5042#if 0
5043 ctxt->value->floatval = ceil(ctxt->value->floatval);
5044#else
5045 f = (double)((int) ctxt->value->floatval);
5046 if (f != ctxt->value->floatval)
5047 ctxt->value->floatval = f + 1;
5048#endif
5049}
5050
5051/**
5052 * xmlXPathRoundFunction:
5053 * @ctxt: the XPath Parser context
5054 * @nargs: the number of arguments
5055 *
5056 * Implement the round() XPath function
5057 * number round(number)
5058 * The round function returns the number that is closest to the
5059 * argument and that is an integer. If there are two such numbers,
5060 * then the one that is even is returned.
5061 */
5062void
5063xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5064 double f;
5065
5066 CHECK_ARITY(1);
5067 CAST_TO_NUMBER;
5068 CHECK_TYPE(XPATH_NUMBER);
5069
5070 if ((ctxt->value->floatval == xmlXPathNAN) ||
5071 (ctxt->value->floatval == xmlXPathPINF) ||
5072 (ctxt->value->floatval == xmlXPathNINF) ||
5073 (ctxt->value->floatval == 0.0))
5074 return;
5075
5076#if 0
5077 f = floor(ctxt->value->floatval);
5078#else
5079 f = (double)((int) ctxt->value->floatval);
5080#endif
5081 if (ctxt->value->floatval < f + 0.5)
5082 ctxt->value->floatval = f;
5083 else
5084 ctxt->value->floatval = f + 1;
5085}
5086
5087/************************************************************************
5088 * *
5089 * The Parser *
5090 * *
5091 ************************************************************************/
5092
5093/*
5094 * a couple of forward declarations since we use a recursive call based
5095 * implementation.
5096 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005097static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005098static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005099static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005100#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005101static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
5102#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00005103#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005104static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005105#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00005106static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
5107 int qualified);
Owen Taylor3473f882001-02-23 17:55:21 +00005108
5109/**
Daniel Veillard61d80a22001-04-27 17:13:01 +00005110 * xmlXPathCurrentChar:
5111 * @ctxt: the XPath parser context
5112 * @cur: pointer to the beginning of the char
5113 * @len: pointer to the length of the char read
5114 *
5115 * The current char value, if using UTF-8 this may actaully span multiple
5116 * bytes in the input buffer.
5117 *
5118 * Returns the current char value and its lenght
5119 */
5120
5121static int
5122xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
5123 unsigned char c;
5124 unsigned int val;
5125 const xmlChar *cur;
5126
5127 if (ctxt == NULL)
5128 return(0);
5129 cur = ctxt->cur;
5130
5131 /*
5132 * We are supposed to handle UTF8, check it's valid
5133 * From rfc2044: encoding of the Unicode values on UTF-8:
5134 *
5135 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
5136 * 0000 0000-0000 007F 0xxxxxxx
5137 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
5138 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
5139 *
5140 * Check for the 0x110000 limit too
5141 */
5142 c = *cur;
5143 if (c & 0x80) {
5144 if ((cur[1] & 0xc0) != 0x80)
5145 goto encoding_error;
5146 if ((c & 0xe0) == 0xe0) {
5147
5148 if ((cur[2] & 0xc0) != 0x80)
5149 goto encoding_error;
5150 if ((c & 0xf0) == 0xf0) {
5151 if (((c & 0xf8) != 0xf0) ||
5152 ((cur[3] & 0xc0) != 0x80))
5153 goto encoding_error;
5154 /* 4-byte code */
5155 *len = 4;
5156 val = (cur[0] & 0x7) << 18;
5157 val |= (cur[1] & 0x3f) << 12;
5158 val |= (cur[2] & 0x3f) << 6;
5159 val |= cur[3] & 0x3f;
5160 } else {
5161 /* 3-byte code */
5162 *len = 3;
5163 val = (cur[0] & 0xf) << 12;
5164 val |= (cur[1] & 0x3f) << 6;
5165 val |= cur[2] & 0x3f;
5166 }
5167 } else {
5168 /* 2-byte code */
5169 *len = 2;
5170 val = (cur[0] & 0x1f) << 6;
5171 val |= cur[1] & 0x3f;
5172 }
5173 if (!IS_CHAR(val)) {
5174 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
5175 }
5176 return(val);
5177 } else {
5178 /* 1-byte code */
5179 *len = 1;
5180 return((int) *cur);
5181 }
5182encoding_error:
5183 /*
5184 * If we detect an UTF8 error that probably mean that the
5185 * input encoding didn't get properly advertized in the
5186 * declaration header. Report the error and switch the encoding
5187 * to ISO-Latin-1 (if you don't like this policy, just declare the
5188 * encoding !)
5189 */
5190 XP_ERROR0(XPATH_ENCODING_ERROR);
5191 *len = 1;
5192 return((int) *cur);
5193}
5194
5195/**
Owen Taylor3473f882001-02-23 17:55:21 +00005196 * xmlXPathParseNCName:
5197 * @ctxt: the XPath Parser context
5198 *
5199 * parse an XML namespace non qualified name.
5200 *
5201 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
5202 *
5203 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
5204 * CombiningChar | Extender
5205 *
5206 * Returns the namespace name or NULL
5207 */
5208
5209xmlChar *
5210xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard2156a562001-04-28 12:24:34 +00005211 const xmlChar *in;
5212 xmlChar *ret;
5213 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005214
Daniel Veillard2156a562001-04-28 12:24:34 +00005215 /*
5216 * Accelerator for simple ASCII names
5217 */
5218 in = ctxt->cur;
5219 if (((*in >= 0x61) && (*in <= 0x7A)) ||
5220 ((*in >= 0x41) && (*in <= 0x5A)) ||
5221 (*in == '_')) {
5222 in++;
5223 while (((*in >= 0x61) && (*in <= 0x7A)) ||
5224 ((*in >= 0x41) && (*in <= 0x5A)) ||
5225 ((*in >= 0x30) && (*in <= 0x39)) ||
5226 (*in == '_'))
5227 in++;
5228 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
5229 (*in == '[') || (*in == ']') || (*in == ':') ||
5230 (*in == '@') || (*in == '*')) {
5231 count = in - ctxt->cur;
5232 if (count == 0)
5233 return(NULL);
5234 ret = xmlStrndup(ctxt->cur, count);
5235 ctxt->cur = in;
5236 return(ret);
5237 }
5238 }
5239 return(xmlXPathParseNameComplex(ctxt, 0));
Owen Taylor3473f882001-02-23 17:55:21 +00005240}
5241
Daniel Veillard2156a562001-04-28 12:24:34 +00005242
Owen Taylor3473f882001-02-23 17:55:21 +00005243/**
5244 * xmlXPathParseQName:
5245 * @ctxt: the XPath Parser context
5246 * @prefix: a xmlChar **
5247 *
5248 * parse an XML qualified name
5249 *
5250 * [NS 5] QName ::= (Prefix ':')? LocalPart
5251 *
5252 * [NS 6] Prefix ::= NCName
5253 *
5254 * [NS 7] LocalPart ::= NCName
5255 *
5256 * Returns the function returns the local part, and prefix is updated
5257 * to get the Prefix if any.
5258 */
5259
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005260static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005261xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
5262 xmlChar *ret = NULL;
5263
5264 *prefix = NULL;
5265 ret = xmlXPathParseNCName(ctxt);
5266 if (CUR == ':') {
5267 *prefix = ret;
5268 NEXT;
5269 ret = xmlXPathParseNCName(ctxt);
5270 }
5271 return(ret);
5272}
5273
5274/**
5275 * xmlXPathParseName:
5276 * @ctxt: the XPath Parser context
5277 *
5278 * parse an XML name
5279 *
5280 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5281 * CombiningChar | Extender
5282 *
5283 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5284 *
5285 * Returns the namespace name or NULL
5286 */
5287
5288xmlChar *
5289xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00005290 const xmlChar *in;
5291 xmlChar *ret;
5292 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005293
Daniel Veillard61d80a22001-04-27 17:13:01 +00005294 /*
5295 * Accelerator for simple ASCII names
5296 */
5297 in = ctxt->cur;
5298 if (((*in >= 0x61) && (*in <= 0x7A)) ||
5299 ((*in >= 0x41) && (*in <= 0x5A)) ||
5300 (*in == '_') || (*in == ':')) {
5301 in++;
5302 while (((*in >= 0x61) && (*in <= 0x7A)) ||
5303 ((*in >= 0x41) && (*in <= 0x5A)) ||
5304 ((*in >= 0x30) && (*in <= 0x39)) ||
5305 (*in == '_') || (*in == ':'))
5306 in++;
5307 if ((*in == ' ') || (*in == '>') || (*in == '/')) {
5308 count = in - ctxt->cur;
5309 ret = xmlStrndup(ctxt->cur, count);
5310 ctxt->cur = in;
5311 return(ret);
5312 }
5313 }
Daniel Veillard2156a562001-04-28 12:24:34 +00005314 return(xmlXPathParseNameComplex(ctxt, 1));
Owen Taylor3473f882001-02-23 17:55:21 +00005315}
5316
Daniel Veillard61d80a22001-04-27 17:13:01 +00005317static xmlChar *
Daniel Veillard2156a562001-04-28 12:24:34 +00005318xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00005319 xmlChar buf[XML_MAX_NAMELEN + 5];
5320 int len = 0, l;
5321 int c;
5322
5323 /*
5324 * Handler for more complex cases
5325 */
5326 c = CUR_CHAR(l);
5327 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
Daniel Veillard2156a562001-04-28 12:24:34 +00005328 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
5329 (c == '*') || /* accelerators */
Daniel Veillard61d80a22001-04-27 17:13:01 +00005330 (!IS_LETTER(c) && (c != '_') &&
Daniel Veillard2156a562001-04-28 12:24:34 +00005331 ((qualified) && (c != ':')))) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00005332 return(NULL);
5333 }
5334
5335 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
5336 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
5337 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00005338 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00005339 (IS_COMBINING(c)) ||
5340 (IS_EXTENDER(c)))) {
5341 COPY_BUF(l,buf,len,c);
5342 NEXTL(l);
5343 c = CUR_CHAR(l);
5344 if (len >= XML_MAX_NAMELEN) {
5345 /*
5346 * Okay someone managed to make a huge name, so he's ready to pay
5347 * for the processing speed.
5348 */
5349 xmlChar *buffer;
5350 int max = len * 2;
5351
5352 buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
5353 if (buffer == NULL) {
5354 XP_ERROR0(XPATH_MEMORY_ERROR);
5355 }
5356 memcpy(buffer, buf, len);
5357 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
5358 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00005359 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00005360 (IS_COMBINING(c)) ||
5361 (IS_EXTENDER(c))) {
5362 if (len + 10 > max) {
5363 max *= 2;
5364 buffer = (xmlChar *) xmlRealloc(buffer,
5365 max * sizeof(xmlChar));
5366 XP_ERROR0(XPATH_MEMORY_ERROR);
5367 if (buffer == NULL) {
5368 XP_ERROR0(XPATH_MEMORY_ERROR);
5369 }
5370 }
5371 COPY_BUF(l,buffer,len,c);
5372 NEXTL(l);
5373 c = CUR_CHAR(l);
5374 }
5375 buffer[len] = 0;
5376 return(buffer);
5377 }
5378 }
Daniel Veillard2156a562001-04-28 12:24:34 +00005379 if (len == 0)
5380 return(NULL);
Daniel Veillard61d80a22001-04-27 17:13:01 +00005381 return(xmlStrndup(buf, len));
5382}
Owen Taylor3473f882001-02-23 17:55:21 +00005383/**
5384 * xmlXPathStringEvalNumber:
5385 * @str: A string to scan
5386 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00005387 * [30a] Float ::= Number ('e' Digits?)?
5388 *
Owen Taylor3473f882001-02-23 17:55:21 +00005389 * [30] Number ::= Digits ('.' Digits?)?
5390 * | '.' Digits
5391 * [31] Digits ::= [0-9]+
5392 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005393 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00005394 * In complement of the Number expression, this function also handles
5395 * negative values : '-' Number.
5396 *
5397 * Returns the double value.
5398 */
5399double
5400xmlXPathStringEvalNumber(const xmlChar *str) {
5401 const xmlChar *cur = str;
5402 double ret = 0.0;
5403 double mult = 1;
5404 int ok = 0;
5405 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005406 int exponent = 0;
5407 int is_exponent_negative = 0;
5408
Owen Taylor3473f882001-02-23 17:55:21 +00005409 while (IS_BLANK(*cur)) cur++;
5410 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5411 return(xmlXPathNAN);
5412 }
5413 if (*cur == '-') {
5414 isneg = 1;
5415 cur++;
5416 }
5417 while ((*cur >= '0') && (*cur <= '9')) {
5418 ret = ret * 10 + (*cur - '0');
5419 ok = 1;
5420 cur++;
5421 }
5422 if (*cur == '.') {
5423 cur++;
5424 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5425 return(xmlXPathNAN);
5426 }
5427 while ((*cur >= '0') && (*cur <= '9')) {
5428 mult /= 10;
5429 ret = ret + (*cur - '0') * mult;
5430 cur++;
5431 }
5432 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00005433 if ((*cur == 'e') || (*cur == 'E')) {
5434 cur++;
5435 if (*cur == '-') {
5436 is_exponent_negative = 1;
5437 cur++;
5438 }
5439 while ((*cur >= '0') && (*cur <= '9')) {
5440 exponent = exponent * 10 + (*cur - '0');
5441 cur++;
5442 }
5443 }
Owen Taylor3473f882001-02-23 17:55:21 +00005444 while (IS_BLANK(*cur)) cur++;
5445 if (*cur != 0) return(xmlXPathNAN);
5446 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005447 if (is_exponent_negative) exponent = -exponent;
5448 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00005449 return(ret);
5450}
5451
5452/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005453 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00005454 * @ctxt: the XPath Parser context
5455 *
5456 * [30] Number ::= Digits ('.' Digits?)?
5457 * | '.' Digits
5458 * [31] Digits ::= [0-9]+
5459 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005460 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00005461 *
5462 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005463static void
5464xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005465 double ret = 0.0;
5466 double mult = 1;
5467 int ok = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005468 int exponent = 0;
5469 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005470
5471 CHECK_ERROR;
5472 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5473 XP_ERROR(XPATH_NUMBER_ERROR);
5474 }
5475 while ((CUR >= '0') && (CUR <= '9')) {
5476 ret = ret * 10 + (CUR - '0');
5477 ok = 1;
5478 NEXT;
5479 }
5480 if (CUR == '.') {
5481 NEXT;
5482 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5483 XP_ERROR(XPATH_NUMBER_ERROR);
5484 }
5485 while ((CUR >= '0') && (CUR <= '9')) {
5486 mult /= 10;
5487 ret = ret + (CUR - '0') * mult;
5488 NEXT;
5489 }
5490 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00005491 if ((CUR == 'e') || (CUR == 'E')) {
5492 NEXT;
5493 if (CUR == '-') {
5494 is_exponent_negative = 1;
5495 NEXT;
5496 }
5497 while ((CUR >= '0') && (CUR <= '9')) {
5498 exponent = exponent * 10 + (CUR - '0');
5499 NEXT;
5500 }
5501 }
5502 if (is_exponent_negative)
5503 exponent = -exponent;
5504 ret *= pow(10.0, (double)exponent);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005505 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
5506 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005507}
5508
5509/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005510 * xmlXPathParseLiteral:
5511 * @ctxt: the XPath Parser context
5512 *
5513 * Parse a Literal
5514 *
5515 * [29] Literal ::= '"' [^"]* '"'
5516 * | "'" [^']* "'"
5517 *
5518 * Returns the value found or NULL in case of error
5519 */
5520static xmlChar *
5521xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
5522 const xmlChar *q;
5523 xmlChar *ret = NULL;
5524
5525 if (CUR == '"') {
5526 NEXT;
5527 q = CUR_PTR;
5528 while ((IS_CHAR(CUR)) && (CUR != '"'))
5529 NEXT;
5530 if (!IS_CHAR(CUR)) {
5531 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5532 } else {
5533 ret = xmlStrndup(q, CUR_PTR - q);
5534 NEXT;
5535 }
5536 } else if (CUR == '\'') {
5537 NEXT;
5538 q = CUR_PTR;
5539 while ((IS_CHAR(CUR)) && (CUR != '\''))
5540 NEXT;
5541 if (!IS_CHAR(CUR)) {
5542 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5543 } else {
5544 ret = xmlStrndup(q, CUR_PTR - q);
5545 NEXT;
5546 }
5547 } else {
5548 XP_ERROR0(XPATH_START_LITERAL_ERROR);
5549 }
5550 return(ret);
5551}
5552
5553/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005554 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00005555 * @ctxt: the XPath Parser context
5556 *
5557 * Parse a Literal and push it on the stack.
5558 *
5559 * [29] Literal ::= '"' [^"]* '"'
5560 * | "'" [^']* "'"
5561 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005562 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00005563 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005564static void
5565xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005566 const xmlChar *q;
5567 xmlChar *ret = NULL;
5568
5569 if (CUR == '"') {
5570 NEXT;
5571 q = CUR_PTR;
5572 while ((IS_CHAR(CUR)) && (CUR != '"'))
5573 NEXT;
5574 if (!IS_CHAR(CUR)) {
5575 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5576 } else {
5577 ret = xmlStrndup(q, CUR_PTR - q);
5578 NEXT;
5579 }
5580 } else if (CUR == '\'') {
5581 NEXT;
5582 q = CUR_PTR;
5583 while ((IS_CHAR(CUR)) && (CUR != '\''))
5584 NEXT;
5585 if (!IS_CHAR(CUR)) {
5586 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5587 } else {
5588 ret = xmlStrndup(q, CUR_PTR - q);
5589 NEXT;
5590 }
5591 } else {
5592 XP_ERROR(XPATH_START_LITERAL_ERROR);
5593 }
5594 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005595 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
5596 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005597 xmlFree(ret);
5598}
5599
5600/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005601 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00005602 * @ctxt: the XPath Parser context
5603 *
5604 * Parse a VariableReference, evaluate it and push it on the stack.
5605 *
5606 * The variable bindings consist of a mapping from variable names
5607 * to variable values. The value of a variable is an object, which
5608 * of any of the types that are possible for the value of an expression,
5609 * and may also be of additional types not specified here.
5610 *
5611 * Early evaluation is possible since:
5612 * The variable bindings [...] used to evaluate a subexpression are
5613 * always the same as those used to evaluate the containing expression.
5614 *
5615 * [36] VariableReference ::= '$' QName
5616 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005617static void
5618xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005619 xmlChar *name;
5620 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005621
5622 SKIP_BLANKS;
5623 if (CUR != '$') {
5624 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5625 }
5626 NEXT;
5627 name = xmlXPathParseQName(ctxt, &prefix);
5628 if (name == NULL) {
5629 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5630 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005631 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005632 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
5633 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005634 SKIP_BLANKS;
5635}
5636
5637/**
5638 * xmlXPathIsNodeType:
5639 * @ctxt: the XPath Parser context
5640 * @name: a name string
5641 *
5642 * Is the name given a NodeType one.
5643 *
5644 * [38] NodeType ::= 'comment'
5645 * | 'text'
5646 * | 'processing-instruction'
5647 * | 'node'
5648 *
5649 * Returns 1 if true 0 otherwise
5650 */
5651int
5652xmlXPathIsNodeType(const xmlChar *name) {
5653 if (name == NULL)
5654 return(0);
5655
5656 if (xmlStrEqual(name, BAD_CAST "comment"))
5657 return(1);
5658 if (xmlStrEqual(name, BAD_CAST "text"))
5659 return(1);
5660 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5661 return(1);
5662 if (xmlStrEqual(name, BAD_CAST "node"))
5663 return(1);
5664 return(0);
5665}
5666
5667/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005668 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00005669 * @ctxt: the XPath Parser context
5670 *
5671 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5672 * [17] Argument ::= Expr
5673 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005674 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00005675 * pushed on the stack
5676 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005677static void
5678xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005679 xmlChar *name;
5680 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005681 int nbargs = 0;
5682
5683 name = xmlXPathParseQName(ctxt, &prefix);
5684 if (name == NULL) {
5685 XP_ERROR(XPATH_EXPR_ERROR);
5686 }
5687 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005688#ifdef DEBUG_EXPR
5689 if (prefix == NULL)
5690 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5691 name);
5692 else
5693 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5694 prefix, name);
5695#endif
5696
Owen Taylor3473f882001-02-23 17:55:21 +00005697 if (CUR != '(') {
5698 XP_ERROR(XPATH_EXPR_ERROR);
5699 }
5700 NEXT;
5701 SKIP_BLANKS;
5702
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005703 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005704 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005705 int op1 = ctxt->comp->last;
5706 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005707 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005708 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005709 nbargs++;
5710 if (CUR == ')') break;
5711 if (CUR != ',') {
5712 XP_ERROR(XPATH_EXPR_ERROR);
5713 }
5714 NEXT;
5715 SKIP_BLANKS;
5716 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005717 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
5718 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005719 NEXT;
5720 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005721}
5722
5723/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005724 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005725 * @ctxt: the XPath Parser context
5726 *
5727 * [15] PrimaryExpr ::= VariableReference
5728 * | '(' Expr ')'
5729 * | Literal
5730 * | Number
5731 * | FunctionCall
5732 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005733 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005734 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005735static void
5736xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005737 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005738 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005739 else if (CUR == '(') {
5740 NEXT;
5741 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005742 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005743 if (CUR != ')') {
5744 XP_ERROR(XPATH_EXPR_ERROR);
5745 }
5746 NEXT;
5747 SKIP_BLANKS;
5748 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005749 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005750 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005751 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005752 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005753 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005754 }
5755 SKIP_BLANKS;
5756}
5757
5758/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005759 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005760 * @ctxt: the XPath Parser context
5761 *
5762 * [20] FilterExpr ::= PrimaryExpr
5763 * | FilterExpr Predicate
5764 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005765 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005766 * Square brackets are used to filter expressions in the same way that
5767 * they are used in location paths. It is an error if the expression to
5768 * be filtered does not evaluate to a node-set. The context node list
5769 * used for evaluating the expression in square brackets is the node-set
5770 * to be filtered listed in document order.
5771 */
5772
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005773static void
5774xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
5775 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005776 CHECK_ERROR;
5777 SKIP_BLANKS;
5778
5779 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005780 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00005781 SKIP_BLANKS;
5782 }
5783
5784
5785}
5786
5787/**
5788 * xmlXPathScanName:
5789 * @ctxt: the XPath Parser context
5790 *
5791 * Trickery: parse an XML name but without consuming the input flow
5792 * Needed to avoid insanity in the parser state.
5793 *
5794 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5795 * CombiningChar | Extender
5796 *
5797 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5798 *
5799 * [6] Names ::= Name (S Name)*
5800 *
5801 * Returns the Name parsed or NULL
5802 */
5803
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005804static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005805xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5806 xmlChar buf[XML_MAX_NAMELEN];
5807 int len = 0;
5808
5809 SKIP_BLANKS;
5810 if (!IS_LETTER(CUR) && (CUR != '_') &&
5811 (CUR != ':')) {
5812 return(NULL);
5813 }
5814
5815 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5816 (NXT(len) == '.') || (NXT(len) == '-') ||
5817 (NXT(len) == '_') || (NXT(len) == ':') ||
5818 (IS_COMBINING(NXT(len))) ||
5819 (IS_EXTENDER(NXT(len)))) {
5820 buf[len] = NXT(len);
5821 len++;
5822 if (len >= XML_MAX_NAMELEN) {
5823 xmlGenericError(xmlGenericErrorContext,
5824 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5825 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5826 (NXT(len) == '.') || (NXT(len) == '-') ||
5827 (NXT(len) == '_') || (NXT(len) == ':') ||
5828 (IS_COMBINING(NXT(len))) ||
5829 (IS_EXTENDER(NXT(len))))
5830 len++;
5831 break;
5832 }
5833 }
5834 return(xmlStrndup(buf, len));
5835}
5836
5837/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005838 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005839 * @ctxt: the XPath Parser context
5840 *
5841 * [19] PathExpr ::= LocationPath
5842 * | FilterExpr
5843 * | FilterExpr '/' RelativeLocationPath
5844 * | FilterExpr '//' RelativeLocationPath
5845 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005846 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005847 * The / operator and // operators combine an arbitrary expression
5848 * and a relative location path. It is an error if the expression
5849 * does not evaluate to a node-set.
5850 * The / operator does composition in the same way as when / is
5851 * used in a location path. As in location paths, // is short for
5852 * /descendant-or-self::node()/.
5853 */
5854
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005855static void
5856xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005857 int lc = 1; /* Should we branch to LocationPath ? */
5858 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5859
5860 SKIP_BLANKS;
5861 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5862 (CUR == '\'') || (CUR == '"')) {
5863 lc = 0;
5864 } else if (CUR == '*') {
5865 /* relative or absolute location path */
5866 lc = 1;
5867 } else if (CUR == '/') {
5868 /* relative or absolute location path */
5869 lc = 1;
5870 } else if (CUR == '@') {
5871 /* relative abbreviated attribute location path */
5872 lc = 1;
5873 } else if (CUR == '.') {
5874 /* relative abbreviated attribute location path */
5875 lc = 1;
5876 } else {
5877 /*
5878 * Problem is finding if we have a name here whether it's:
5879 * - a nodetype
5880 * - a function call in which case it's followed by '('
5881 * - an axis in which case it's followed by ':'
5882 * - a element name
5883 * We do an a priori analysis here rather than having to
5884 * maintain parsed token content through the recursive function
5885 * calls. This looks uglier but makes the code quite easier to
5886 * read/write/debug.
5887 */
5888 SKIP_BLANKS;
5889 name = xmlXPathScanName(ctxt);
5890 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5891#ifdef DEBUG_STEP
5892 xmlGenericError(xmlGenericErrorContext,
5893 "PathExpr: Axis\n");
5894#endif
5895 lc = 1;
5896 xmlFree(name);
5897 } else if (name != NULL) {
5898 int len =xmlStrlen(name);
5899 int blank = 0;
5900
5901
5902 while (NXT(len) != 0) {
5903 if (NXT(len) == '/') {
5904 /* element name */
5905#ifdef DEBUG_STEP
5906 xmlGenericError(xmlGenericErrorContext,
5907 "PathExpr: AbbrRelLocation\n");
5908#endif
5909 lc = 1;
5910 break;
5911 } else if (IS_BLANK(NXT(len))) {
5912 /* skip to next */
5913 blank = 1;
5914 } else if (NXT(len) == ':') {
5915#ifdef DEBUG_STEP
5916 xmlGenericError(xmlGenericErrorContext,
5917 "PathExpr: AbbrRelLocation\n");
5918#endif
5919 lc = 1;
5920 break;
5921 } else if ((NXT(len) == '(')) {
5922 /* Note Type or Function */
5923 if (xmlXPathIsNodeType(name)) {
5924#ifdef DEBUG_STEP
5925 xmlGenericError(xmlGenericErrorContext,
5926 "PathExpr: Type search\n");
5927#endif
5928 lc = 1;
5929 } else {
5930#ifdef DEBUG_STEP
5931 xmlGenericError(xmlGenericErrorContext,
5932 "PathExpr: function call\n");
5933#endif
5934 lc = 0;
5935 }
5936 break;
5937 } else if ((NXT(len) == '[')) {
5938 /* element name */
5939#ifdef DEBUG_STEP
5940 xmlGenericError(xmlGenericErrorContext,
5941 "PathExpr: AbbrRelLocation\n");
5942#endif
5943 lc = 1;
5944 break;
5945 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
5946 (NXT(len) == '=')) {
5947 lc = 1;
5948 break;
5949 } else {
5950 lc = 1;
5951 break;
5952 }
5953 len++;
5954 }
5955 if (NXT(len) == 0) {
5956#ifdef DEBUG_STEP
5957 xmlGenericError(xmlGenericErrorContext,
5958 "PathExpr: AbbrRelLocation\n");
5959#endif
5960 /* element name */
5961 lc = 1;
5962 }
5963 xmlFree(name);
5964 } else {
5965 /* make sure all cases are covered explicitely */
5966 XP_ERROR(XPATH_EXPR_ERROR);
5967 }
5968 }
5969
5970 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005971 if (CUR == '/') {
5972 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
5973 } else {
5974 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005975 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005976 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005977 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005978 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005979 CHECK_ERROR;
5980 if ((CUR == '/') && (NXT(1) == '/')) {
5981 SKIP(2);
5982 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005983
5984 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
5985 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
5986 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
5987
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005988 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005989 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005990 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005991 }
5992 }
5993 SKIP_BLANKS;
5994}
5995
5996/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005997 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005998 * @ctxt: the XPath Parser context
5999 *
6000 * [18] UnionExpr ::= PathExpr
6001 * | UnionExpr '|' PathExpr
6002 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006003 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006004 */
6005
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006006static void
6007xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
6008 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006009 CHECK_ERROR;
6010 SKIP_BLANKS;
6011 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006012 int op1 = ctxt->comp->last;
6013 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006014
6015 NEXT;
6016 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006017 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006018
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006019 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
6020
Owen Taylor3473f882001-02-23 17:55:21 +00006021 SKIP_BLANKS;
6022 }
Owen Taylor3473f882001-02-23 17:55:21 +00006023}
6024
6025/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006026 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006027 * @ctxt: the XPath Parser context
6028 *
6029 * [27] UnaryExpr ::= UnionExpr
6030 * | '-' UnaryExpr
6031 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006032 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006033 */
6034
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006035static void
6036xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006037 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006038 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006039
6040 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00006041 while (CUR == '-') {
6042 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006043 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006044 NEXT;
6045 SKIP_BLANKS;
6046 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006047
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006048 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006049 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006050 if (found) {
6051 if (minus)
6052 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
6053 else
6054 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006055 }
6056}
6057
6058/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006059 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006060 * @ctxt: the XPath Parser context
6061 *
6062 * [26] MultiplicativeExpr ::= UnaryExpr
6063 * | MultiplicativeExpr MultiplyOperator UnaryExpr
6064 * | MultiplicativeExpr 'div' UnaryExpr
6065 * | MultiplicativeExpr 'mod' UnaryExpr
6066 * [34] MultiplyOperator ::= '*'
6067 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006068 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006069 */
6070
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006071static void
6072xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
6073 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006074 CHECK_ERROR;
6075 SKIP_BLANKS;
6076 while ((CUR == '*') ||
6077 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
6078 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
6079 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006080 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006081
6082 if (CUR == '*') {
6083 op = 0;
6084 NEXT;
6085 } else if (CUR == 'd') {
6086 op = 1;
6087 SKIP(3);
6088 } else if (CUR == 'm') {
6089 op = 2;
6090 SKIP(3);
6091 }
6092 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006093 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006094 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006095 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006096 SKIP_BLANKS;
6097 }
6098}
6099
6100/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006101 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006102 * @ctxt: the XPath Parser context
6103 *
6104 * [25] AdditiveExpr ::= MultiplicativeExpr
6105 * | AdditiveExpr '+' MultiplicativeExpr
6106 * | AdditiveExpr '-' MultiplicativeExpr
6107 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006108 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006109 */
6110
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006111static void
6112xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006113
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006114 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006115 CHECK_ERROR;
6116 SKIP_BLANKS;
6117 while ((CUR == '+') || (CUR == '-')) {
6118 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006119 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006120
6121 if (CUR == '+') plus = 1;
6122 else plus = 0;
6123 NEXT;
6124 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006125 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006126 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006127 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006128 SKIP_BLANKS;
6129 }
6130}
6131
6132/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006133 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006134 * @ctxt: the XPath Parser context
6135 *
6136 * [24] RelationalExpr ::= AdditiveExpr
6137 * | RelationalExpr '<' AdditiveExpr
6138 * | RelationalExpr '>' AdditiveExpr
6139 * | RelationalExpr '<=' AdditiveExpr
6140 * | RelationalExpr '>=' AdditiveExpr
6141 *
6142 * A <= B > C is allowed ? Answer from James, yes with
6143 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
6144 * which is basically what got implemented.
6145 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006146 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00006147 * on the stack
6148 */
6149
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006150static void
6151xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
6152 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006153 CHECK_ERROR;
6154 SKIP_BLANKS;
6155 while ((CUR == '<') ||
6156 (CUR == '>') ||
6157 ((CUR == '<') && (NXT(1) == '=')) ||
6158 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006159 int inf, strict;
6160 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006161
6162 if (CUR == '<') inf = 1;
6163 else inf = 0;
6164 if (NXT(1) == '=') strict = 0;
6165 else strict = 1;
6166 NEXT;
6167 if (!strict) NEXT;
6168 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006169 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006170 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006171 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00006172 SKIP_BLANKS;
6173 }
6174}
6175
6176/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006177 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006178 * @ctxt: the XPath Parser context
6179 *
6180 * [23] EqualityExpr ::= RelationalExpr
6181 * | EqualityExpr '=' RelationalExpr
6182 * | EqualityExpr '!=' RelationalExpr
6183 *
6184 * A != B != C is allowed ? Answer from James, yes with
6185 * (RelationalExpr = RelationalExpr) = RelationalExpr
6186 * (RelationalExpr != RelationalExpr) != RelationalExpr
6187 * which is basically what got implemented.
6188 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006189 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006190 *
6191 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006192static void
6193xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
6194 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006195 CHECK_ERROR;
6196 SKIP_BLANKS;
6197 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006198 int eq;
6199 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006200
6201 if (CUR == '=') eq = 1;
6202 else eq = 0;
6203 NEXT;
6204 if (!eq) NEXT;
6205 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006206 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006207 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006208 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006209 SKIP_BLANKS;
6210 }
6211}
6212
6213/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006214 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006215 * @ctxt: the XPath Parser context
6216 *
6217 * [22] AndExpr ::= EqualityExpr
6218 * | AndExpr 'and' EqualityExpr
6219 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006220 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006221 *
6222 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006223static void
6224xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
6225 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006226 CHECK_ERROR;
6227 SKIP_BLANKS;
6228 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006229 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006230 SKIP(3);
6231 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006232 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006233 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006234 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006235 SKIP_BLANKS;
6236 }
6237}
6238
6239/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006240 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006241 * @ctxt: the XPath Parser context
6242 *
6243 * [14] Expr ::= OrExpr
6244 * [21] OrExpr ::= AndExpr
6245 * | OrExpr 'or' AndExpr
6246 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006247 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00006248 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006249static void
6250xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
6251 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006252 CHECK_ERROR;
6253 SKIP_BLANKS;
6254 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006255 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006256 SKIP(2);
6257 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006258 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006259 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006260 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
6261 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00006262 SKIP_BLANKS;
6263 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006264 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
6265 /* more ops could be optimized too */
6266 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
6267 }
Owen Taylor3473f882001-02-23 17:55:21 +00006268}
6269
6270/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006271 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00006272 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006273 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00006274 *
6275 * [8] Predicate ::= '[' PredicateExpr ']'
6276 * [9] PredicateExpr ::= Expr
6277 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006278 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00006279 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006280static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006281xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006282 int op1 = ctxt->comp->last;
6283
6284 SKIP_BLANKS;
6285 if (CUR != '[') {
6286 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6287 }
6288 NEXT;
6289 SKIP_BLANKS;
6290
6291 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006292 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006293 CHECK_ERROR;
6294
6295 if (CUR != ']') {
6296 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6297 }
6298
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006299 if (filter)
6300 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
6301 else
6302 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006303
6304 NEXT;
6305 SKIP_BLANKS;
6306}
6307
6308/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006309 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00006310 * @ctxt: the XPath Parser context
6311 * @test: pointer to a xmlXPathTestVal
6312 * @type: pointer to a xmlXPathTypeVal
6313 * @prefix: placeholder for a possible name prefix
6314 *
6315 * [7] NodeTest ::= NameTest
6316 * | NodeType '(' ')'
6317 * | 'processing-instruction' '(' Literal ')'
6318 *
6319 * [37] NameTest ::= '*'
6320 * | NCName ':' '*'
6321 * | QName
6322 * [38] NodeType ::= 'comment'
6323 * | 'text'
6324 * | 'processing-instruction'
6325 * | 'node'
6326 *
6327 * Returns the name found and update @test, @type and @prefix appropriately
6328 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006329static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006330xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6331 xmlXPathTypeVal *type, const xmlChar **prefix,
6332 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00006333 int blanks;
6334
6335 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6336 STRANGE;
6337 return(NULL);
6338 }
6339 *type = 0;
6340 *test = 0;
6341 *prefix = NULL;
6342 SKIP_BLANKS;
6343
6344 if ((name == NULL) && (CUR == '*')) {
6345 /*
6346 * All elements
6347 */
6348 NEXT;
6349 *test = NODE_TEST_ALL;
6350 return(NULL);
6351 }
6352
6353 if (name == NULL)
6354 name = xmlXPathParseNCName(ctxt);
6355 if (name == NULL) {
6356 XP_ERROR0(XPATH_EXPR_ERROR);
6357 }
6358
6359 blanks = IS_BLANK(CUR);
6360 SKIP_BLANKS;
6361 if (CUR == '(') {
6362 NEXT;
6363 /*
6364 * NodeType or PI search
6365 */
6366 if (xmlStrEqual(name, BAD_CAST "comment"))
6367 *type = NODE_TYPE_COMMENT;
6368 else if (xmlStrEqual(name, BAD_CAST "node"))
6369 *type = NODE_TYPE_NODE;
6370 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6371 *type = NODE_TYPE_PI;
6372 else if (xmlStrEqual(name, BAD_CAST "text"))
6373 *type = NODE_TYPE_TEXT;
6374 else {
6375 if (name != NULL)
6376 xmlFree(name);
6377 XP_ERROR0(XPATH_EXPR_ERROR);
6378 }
6379
6380 *test = NODE_TEST_TYPE;
6381
6382 SKIP_BLANKS;
6383 if (*type == NODE_TYPE_PI) {
6384 /*
6385 * Specific case: search a PI by name.
6386 */
Owen Taylor3473f882001-02-23 17:55:21 +00006387 if (name != NULL)
6388 xmlFree(name);
Daniel Veillard82e49712001-04-26 14:38:03 +00006389 name = NULL;
6390 if (CUR != ')') {
6391 name = xmlXPathParseLiteral(ctxt);
6392 CHECK_ERROR 0;
6393 SKIP_BLANKS;
6394 }
Owen Taylor3473f882001-02-23 17:55:21 +00006395 }
6396 if (CUR != ')') {
6397 if (name != NULL)
6398 xmlFree(name);
6399 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6400 }
6401 NEXT;
6402 return(name);
6403 }
6404 *test = NODE_TEST_NAME;
6405 if ((!blanks) && (CUR == ':')) {
6406 NEXT;
6407
6408 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006409 * Since currently the parser context don't have a
6410 * namespace list associated:
6411 * The namespace name for this prefix can be computed
6412 * only at evaluation time. The compilation is done
6413 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00006414 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006415#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00006416 *prefix = xmlXPathNsLookup(ctxt->context, name);
6417 if (name != NULL)
6418 xmlFree(name);
6419 if (*prefix == NULL) {
6420 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6421 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006422#else
6423 *prefix = name;
6424#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006425
6426 if (CUR == '*') {
6427 /*
6428 * All elements
6429 */
6430 NEXT;
6431 *test = NODE_TEST_ALL;
6432 return(NULL);
6433 }
6434
6435 name = xmlXPathParseNCName(ctxt);
6436 if (name == NULL) {
6437 XP_ERROR0(XPATH_EXPR_ERROR);
6438 }
6439 }
6440 return(name);
6441}
6442
6443/**
6444 * xmlXPathIsAxisName:
6445 * @name: a preparsed name token
6446 *
6447 * [6] AxisName ::= 'ancestor'
6448 * | 'ancestor-or-self'
6449 * | 'attribute'
6450 * | 'child'
6451 * | 'descendant'
6452 * | 'descendant-or-self'
6453 * | 'following'
6454 * | 'following-sibling'
6455 * | 'namespace'
6456 * | 'parent'
6457 * | 'preceding'
6458 * | 'preceding-sibling'
6459 * | 'self'
6460 *
6461 * Returns the axis or 0
6462 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006463static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00006464xmlXPathIsAxisName(const xmlChar *name) {
6465 xmlXPathAxisVal ret = 0;
6466 switch (name[0]) {
6467 case 'a':
6468 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6469 ret = AXIS_ANCESTOR;
6470 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6471 ret = AXIS_ANCESTOR_OR_SELF;
6472 if (xmlStrEqual(name, BAD_CAST "attribute"))
6473 ret = AXIS_ATTRIBUTE;
6474 break;
6475 case 'c':
6476 if (xmlStrEqual(name, BAD_CAST "child"))
6477 ret = AXIS_CHILD;
6478 break;
6479 case 'd':
6480 if (xmlStrEqual(name, BAD_CAST "descendant"))
6481 ret = AXIS_DESCENDANT;
6482 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6483 ret = AXIS_DESCENDANT_OR_SELF;
6484 break;
6485 case 'f':
6486 if (xmlStrEqual(name, BAD_CAST "following"))
6487 ret = AXIS_FOLLOWING;
6488 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6489 ret = AXIS_FOLLOWING_SIBLING;
6490 break;
6491 case 'n':
6492 if (xmlStrEqual(name, BAD_CAST "namespace"))
6493 ret = AXIS_NAMESPACE;
6494 break;
6495 case 'p':
6496 if (xmlStrEqual(name, BAD_CAST "parent"))
6497 ret = AXIS_PARENT;
6498 if (xmlStrEqual(name, BAD_CAST "preceding"))
6499 ret = AXIS_PRECEDING;
6500 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6501 ret = AXIS_PRECEDING_SIBLING;
6502 break;
6503 case 's':
6504 if (xmlStrEqual(name, BAD_CAST "self"))
6505 ret = AXIS_SELF;
6506 break;
6507 }
6508 return(ret);
6509}
6510
6511/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006512 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00006513 * @ctxt: the XPath Parser context
6514 *
6515 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6516 * | AbbreviatedStep
6517 *
6518 * [12] AbbreviatedStep ::= '.' | '..'
6519 *
6520 * [5] AxisSpecifier ::= AxisName '::'
6521 * | AbbreviatedAxisSpecifier
6522 *
6523 * [13] AbbreviatedAxisSpecifier ::= '@'?
6524 *
6525 * Modified for XPtr range support as:
6526 *
6527 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6528 * | AbbreviatedStep
6529 * | 'range-to' '(' Expr ')' Predicate*
6530 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006531 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00006532 * A location step of . is short for self::node(). This is
6533 * particularly useful in conjunction with //. For example, the
6534 * location path .//para is short for
6535 * self::node()/descendant-or-self::node()/child::para
6536 * and so will select all para descendant elements of the context
6537 * node.
6538 * Similarly, a location step of .. is short for parent::node().
6539 * For example, ../title is short for parent::node()/child::title
6540 * and so will select the title children of the parent of the context
6541 * node.
6542 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006543static void
6544xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006545#ifdef LIBXML_XPTR_ENABLED
6546 int rangeto = 0;
6547 int op2 = -1;
6548#endif
6549
Owen Taylor3473f882001-02-23 17:55:21 +00006550 SKIP_BLANKS;
6551 if ((CUR == '.') && (NXT(1) == '.')) {
6552 SKIP(2);
6553 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006554 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
6555 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006556 } else if (CUR == '.') {
6557 NEXT;
6558 SKIP_BLANKS;
6559 } else {
6560 xmlChar *name = NULL;
6561 const xmlChar *prefix = NULL;
6562 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006563 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006564 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006565 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00006566
6567 /*
6568 * The modification needed for XPointer change to the production
6569 */
6570#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006571 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00006572 name = xmlXPathParseNCName(ctxt);
6573 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006574 op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006575 xmlFree(name);
6576 SKIP_BLANKS;
6577 if (CUR != '(') {
6578 XP_ERROR(XPATH_EXPR_ERROR);
6579 }
6580 NEXT;
6581 SKIP_BLANKS;
6582
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006583 xmlXPathCompileExpr(ctxt);
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006584 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
Owen Taylor3473f882001-02-23 17:55:21 +00006585 CHECK_ERROR;
6586
6587 SKIP_BLANKS;
6588 if (CUR != ')') {
6589 XP_ERROR(XPATH_EXPR_ERROR);
6590 }
6591 NEXT;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006592 rangeto = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006593 goto eval_predicates;
6594 }
6595 }
6596#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00006597 if (CUR == '*') {
6598 axis = AXIS_CHILD;
6599 } else {
6600 if (name == NULL)
6601 name = xmlXPathParseNCName(ctxt);
6602 if (name != NULL) {
6603 axis = xmlXPathIsAxisName(name);
6604 if (axis != 0) {
6605 SKIP_BLANKS;
6606 if ((CUR == ':') && (NXT(1) == ':')) {
6607 SKIP(2);
6608 xmlFree(name);
6609 name = NULL;
6610 } else {
6611 /* an element name can conflict with an axis one :-\ */
6612 axis = AXIS_CHILD;
6613 }
Owen Taylor3473f882001-02-23 17:55:21 +00006614 } else {
Owen Taylor3473f882001-02-23 17:55:21 +00006615 axis = AXIS_CHILD;
6616 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006617 } else if (CUR == '@') {
6618 NEXT;
6619 axis = AXIS_ATTRIBUTE;
Owen Taylor3473f882001-02-23 17:55:21 +00006620 } else {
Daniel Veillard2156a562001-04-28 12:24:34 +00006621 axis = AXIS_CHILD;
Owen Taylor3473f882001-02-23 17:55:21 +00006622 }
Owen Taylor3473f882001-02-23 17:55:21 +00006623 }
6624
6625 CHECK_ERROR;
6626
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006627 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00006628 if (test == 0)
6629 return;
6630
6631#ifdef DEBUG_STEP
6632 xmlGenericError(xmlGenericErrorContext,
6633 "Basis : computing new set\n");
6634#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006635
Owen Taylor3473f882001-02-23 17:55:21 +00006636#ifdef DEBUG_STEP
6637 xmlGenericError(xmlGenericErrorContext, "Basis : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006638 if (ctxt->value == NULL)
6639 xmlGenericError(xmlGenericErrorContext, "no value\n");
6640 else if (ctxt->value->nodesetval == NULL)
6641 xmlGenericError(xmlGenericErrorContext, "Empty\n");
6642 else
6643 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00006644#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006645
6646eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006647 op1 = ctxt->comp->last;
6648 ctxt->comp->last = -1;
6649
Owen Taylor3473f882001-02-23 17:55:21 +00006650 SKIP_BLANKS;
6651 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006652 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006653 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006654
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006655#ifdef LIBXML_XPTR_ENABLED
6656 if (rangeto) {
6657 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
6658 } else
6659#endif
6660 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
6661 test, type, (void *)prefix, (void *)name);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006662
Owen Taylor3473f882001-02-23 17:55:21 +00006663 }
6664#ifdef DEBUG_STEP
6665 xmlGenericError(xmlGenericErrorContext, "Step : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006666 if (ctxt->value == NULL)
6667 xmlGenericError(xmlGenericErrorContext, "no value\n");
6668 else if (ctxt->value->nodesetval == NULL)
6669 xmlGenericError(xmlGenericErrorContext, "Empty\n");
6670 else
6671 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6672 ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00006673#endif
6674}
6675
6676/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006677 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006678 * @ctxt: the XPath Parser context
6679 *
6680 * [3] RelativeLocationPath ::= Step
6681 * | RelativeLocationPath '/' Step
6682 * | AbbreviatedRelativeLocationPath
6683 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6684 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006685 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00006686 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006687static void
Owen Taylor3473f882001-02-23 17:55:21 +00006688#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006689xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006690#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006691xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006692#endif
6693(xmlXPathParserContextPtr ctxt) {
6694 SKIP_BLANKS;
6695 if ((CUR == '/') && (NXT(1) == '/')) {
6696 SKIP(2);
6697 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006698 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6699 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006700 } else if (CUR == '/') {
6701 NEXT;
6702 SKIP_BLANKS;
6703 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006704 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006705 SKIP_BLANKS;
6706 while (CUR == '/') {
6707 if ((CUR == '/') && (NXT(1) == '/')) {
6708 SKIP(2);
6709 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006710 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00006711 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006712 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006713 } else if (CUR == '/') {
6714 NEXT;
6715 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006716 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006717 }
6718 SKIP_BLANKS;
6719 }
6720}
6721
6722/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006723 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006724 * @ctxt: the XPath Parser context
6725 *
6726 * [1] LocationPath ::= RelativeLocationPath
6727 * | AbsoluteLocationPath
6728 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6729 * | AbbreviatedAbsoluteLocationPath
6730 * [10] AbbreviatedAbsoluteLocationPath ::=
6731 * '//' RelativeLocationPath
6732 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006733 * Compile a location path
6734 *
Owen Taylor3473f882001-02-23 17:55:21 +00006735 * // is short for /descendant-or-self::node()/. For example,
6736 * //para is short for /descendant-or-self::node()/child::para and
6737 * so will select any para element in the document (even a para element
6738 * that is a document element will be selected by //para since the
6739 * document element node is a child of the root node); div//para is
6740 * short for div/descendant-or-self::node()/child::para and so will
6741 * select all para descendants of div children.
6742 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006743static void
6744xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006745 SKIP_BLANKS;
6746 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006747 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006748 } else {
6749 while (CUR == '/') {
6750 if ((CUR == '/') && (NXT(1) == '/')) {
6751 SKIP(2);
6752 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006753 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6754 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006755 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006756 } else if (CUR == '/') {
6757 NEXT;
6758 SKIP_BLANKS;
6759 if (CUR != 0)
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006760 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006761 }
6762 }
6763 }
6764}
6765
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006766/************************************************************************
6767 * *
6768 * XPath precompiled expression evaluation *
6769 * *
6770 ************************************************************************/
6771
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006772static void
6773xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
6774
6775/**
6776 * xmlXPathNodeCollectAndTest:
6777 * @ctxt: the XPath Parser context
6778 * @op: the XPath precompiled step operation
6779 *
6780 * This is the function implementing a step: based on the current list
6781 * of nodes, it builds up a new list, looking at all nodes under that
6782 * axis and selecting them it also do the predicate filtering
6783 *
6784 * Pushes the new NodeSet resulting from the search.
6785 */
6786static void
6787xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
6788 xmlXPathStepOpPtr op) {
6789 xmlXPathAxisVal axis = op->value;
6790 xmlXPathTestVal test = op->value2;
6791 xmlXPathTypeVal type = op->value3;
6792 const xmlChar *prefix = op->value4;
6793 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006794 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006795
6796#ifdef DEBUG_STEP
6797 int n = 0, t = 0;
6798#endif
6799 int i;
6800 xmlNodeSetPtr ret, list;
6801 xmlXPathTraversalFunction next = NULL;
6802 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
6803 xmlNodePtr cur = NULL;
6804 xmlXPathObjectPtr obj;
6805 xmlNodeSetPtr nodelist;
6806 xmlNodePtr tmp;
6807
6808 CHECK_TYPE(XPATH_NODESET);
6809 obj = valuePop(ctxt);
6810 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006811 if (prefix != NULL) {
6812 URI = xmlXPathNsLookup(ctxt->context, prefix);
6813 if (URI == NULL)
6814 XP_ERROR(XPATH_UNDEF_PREFIX_ERROR);
6815 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006816
6817#ifdef DEBUG_STEP
6818 xmlGenericError(xmlGenericErrorContext,
6819 "new step : ");
6820#endif
6821 switch (axis) {
6822 case AXIS_ANCESTOR:
6823#ifdef DEBUG_STEP
6824 xmlGenericError(xmlGenericErrorContext,
6825 "axis 'ancestors' ");
6826#endif
6827 next = xmlXPathNextAncestor; break;
6828 case AXIS_ANCESTOR_OR_SELF:
6829#ifdef DEBUG_STEP
6830 xmlGenericError(xmlGenericErrorContext,
6831 "axis 'ancestors-or-self' ");
6832#endif
6833 next = xmlXPathNextAncestorOrSelf; break;
6834 case AXIS_ATTRIBUTE:
6835#ifdef DEBUG_STEP
6836 xmlGenericError(xmlGenericErrorContext,
6837 "axis 'attributes' ");
6838#endif
6839 next = xmlXPathNextAttribute; break;
6840 break;
6841 case AXIS_CHILD:
6842#ifdef DEBUG_STEP
6843 xmlGenericError(xmlGenericErrorContext,
6844 "axis 'child' ");
6845#endif
6846 next = xmlXPathNextChild; break;
6847 case AXIS_DESCENDANT:
6848#ifdef DEBUG_STEP
6849 xmlGenericError(xmlGenericErrorContext,
6850 "axis 'descendant' ");
6851#endif
6852 next = xmlXPathNextDescendant; break;
6853 case AXIS_DESCENDANT_OR_SELF:
6854#ifdef DEBUG_STEP
6855 xmlGenericError(xmlGenericErrorContext,
6856 "axis 'descendant-or-self' ");
6857#endif
6858 next = xmlXPathNextDescendantOrSelf; break;
6859 case AXIS_FOLLOWING:
6860#ifdef DEBUG_STEP
6861 xmlGenericError(xmlGenericErrorContext,
6862 "axis 'following' ");
6863#endif
6864 next = xmlXPathNextFollowing; break;
6865 case AXIS_FOLLOWING_SIBLING:
6866#ifdef DEBUG_STEP
6867 xmlGenericError(xmlGenericErrorContext,
6868 "axis 'following-siblings' ");
6869#endif
6870 next = xmlXPathNextFollowingSibling; break;
6871 case AXIS_NAMESPACE:
6872#ifdef DEBUG_STEP
6873 xmlGenericError(xmlGenericErrorContext,
6874 "axis 'namespace' ");
6875#endif
6876 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
6877 break;
6878 case AXIS_PARENT:
6879#ifdef DEBUG_STEP
6880 xmlGenericError(xmlGenericErrorContext,
6881 "axis 'parent' ");
6882#endif
6883 next = xmlXPathNextParent; break;
6884 case AXIS_PRECEDING:
6885#ifdef DEBUG_STEP
6886 xmlGenericError(xmlGenericErrorContext,
6887 "axis 'preceding' ");
6888#endif
6889 next = xmlXPathNextPreceding; break;
6890 case AXIS_PRECEDING_SIBLING:
6891#ifdef DEBUG_STEP
6892 xmlGenericError(xmlGenericErrorContext,
6893 "axis 'preceding-sibling' ");
6894#endif
6895 next = xmlXPathNextPrecedingSibling; break;
6896 case AXIS_SELF:
6897#ifdef DEBUG_STEP
6898 xmlGenericError(xmlGenericErrorContext,
6899 "axis 'self' ");
6900#endif
6901 next = xmlXPathNextSelf; break;
6902 }
6903 if (next == NULL)
6904 return;
6905
6906 nodelist = obj->nodesetval;
6907 if (nodelist == NULL) {
6908 xmlXPathFreeObject(obj);
6909 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
6910 return;
6911 }
6912 addNode = xmlXPathNodeSetAddUnique;
6913 ret = NULL;
6914#ifdef DEBUG_STEP
6915 xmlGenericError(xmlGenericErrorContext,
6916 " context contains %d nodes\n",
6917 nodelist->nodeNr);
6918 switch (test) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006919 case NODE_TEST_NONE:
6920 xmlGenericError(xmlGenericErrorContext,
6921 " searching for none !!!\n");
6922 break;
6923 case NODE_TEST_TYPE:
6924 xmlGenericError(xmlGenericErrorContext,
6925 " searching for type %d\n", type);
6926 break;
6927 case NODE_TEST_PI:
6928 xmlGenericError(xmlGenericErrorContext,
6929 " searching for PI !!!\n");
6930 break;
6931 case NODE_TEST_ALL:
6932 xmlGenericError(xmlGenericErrorContext,
6933 " searching for *\n");
6934 break;
6935 case NODE_TEST_NS:
6936 xmlGenericError(xmlGenericErrorContext,
6937 " searching for namespace %s\n",
6938 prefix);
6939 break;
6940 case NODE_TEST_NAME:
6941 xmlGenericError(xmlGenericErrorContext,
6942 " searching for name %s\n", name);
6943 if (prefix != NULL)
6944 xmlGenericError(xmlGenericErrorContext,
6945 " with namespace %s\n",
6946 prefix);
6947 break;
6948 }
6949 xmlGenericError(xmlGenericErrorContext, "Testing : ");
6950#endif
6951 /*
6952 * 2.3 Node Tests
6953 * - For the attribute axis, the principal node type is attribute.
6954 * - For the namespace axis, the principal node type is namespace.
6955 * - For other axes, the principal node type is element.
6956 *
6957 * A node test * is true for any node of the
6958 * principal node type. For example, child::* willi
6959 * select all element children of the context node
6960 */
6961 tmp = ctxt->context->node;
6962 for (i = 0;i < nodelist->nodeNr; i++) {
6963 ctxt->context->node = nodelist->nodeTab[i];
6964
6965 cur = NULL;
6966 list = xmlXPathNodeSetCreate(NULL);
6967 do {
6968 cur = next(ctxt, cur);
6969 if (cur == NULL) break;
6970#ifdef DEBUG_STEP
6971 t++;
6972 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
6973#endif
6974 switch (test) {
6975 case NODE_TEST_NONE:
6976 ctxt->context->node = tmp;
6977 STRANGE
6978 return;
6979 case NODE_TEST_TYPE:
6980 if ((cur->type == type) ||
6981 ((type == NODE_TYPE_NODE) &&
6982 ((cur->type == XML_DOCUMENT_NODE) ||
6983 (cur->type == XML_HTML_DOCUMENT_NODE) ||
6984 (cur->type == XML_ELEMENT_NODE) ||
6985 (cur->type == XML_PI_NODE) ||
6986 (cur->type == XML_COMMENT_NODE) ||
6987 (cur->type == XML_CDATA_SECTION_NODE) ||
6988 (cur->type == XML_TEXT_NODE)))) {
6989#ifdef DEBUG_STEP
6990 n++;
6991#endif
6992 addNode(list, cur);
6993 }
6994 break;
6995 case NODE_TEST_PI:
6996 if (cur->type == XML_PI_NODE) {
6997 if ((name != NULL) &&
6998 (!xmlStrEqual(name, cur->name)))
6999 break;
7000#ifdef DEBUG_STEP
7001 n++;
7002#endif
7003 addNode(list, cur);
7004 }
7005 break;
7006 case NODE_TEST_ALL:
7007 if (axis == AXIS_ATTRIBUTE) {
7008 if (cur->type == XML_ATTRIBUTE_NODE) {
7009#ifdef DEBUG_STEP
7010 n++;
7011#endif
7012 addNode(list, cur);
7013 }
7014 } else if (axis == AXIS_NAMESPACE) {
7015 if (cur->type == XML_NAMESPACE_DECL) {
7016#ifdef DEBUG_STEP
7017 n++;
7018#endif
7019 addNode(list, cur);
7020 }
7021 } else {
7022 if ((cur->type == XML_ELEMENT_NODE) ||
7023 (cur->type == XML_DOCUMENT_NODE) ||
7024 (cur->type == XML_HTML_DOCUMENT_NODE)) {
7025 if (prefix == NULL) {
7026#ifdef DEBUG_STEP
7027 n++;
7028#endif
7029 addNode(list, cur);
7030 } else if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00007031 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007032 cur->ns->href))) {
7033#ifdef DEBUG_STEP
7034 n++;
7035#endif
7036 addNode(list, cur);
7037 }
7038 }
7039 }
7040 break;
7041 case NODE_TEST_NS: {
7042 TODO;
7043 break;
7044 }
7045 case NODE_TEST_NAME:
7046 switch (cur->type) {
7047 case XML_ELEMENT_NODE:
7048 if (xmlStrEqual(name, cur->name)) {
7049 if (prefix == NULL) {
7050 if ((cur->ns == NULL) ||
7051 (cur->ns->prefix == NULL)) {
7052#ifdef DEBUG_STEP
7053 n++;
7054#endif
7055 addNode(list, cur);
7056 }
7057 } else {
7058 if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00007059 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007060 cur->ns->href))) {
7061#ifdef DEBUG_STEP
7062 n++;
7063#endif
7064 addNode(list, cur);
7065 }
7066 }
7067 }
7068 break;
7069 case XML_ATTRIBUTE_NODE: {
7070 xmlAttrPtr attr = (xmlAttrPtr) cur;
7071 if (xmlStrEqual(name, attr->name)) {
7072 if (prefix == NULL) {
7073 if ((attr->ns == NULL) ||
7074 (attr->ns->prefix == NULL)) {
7075#ifdef DEBUG_STEP
7076 n++;
7077#endif
7078 addNode(list, (xmlNodePtr) attr);
7079 }
7080 } else {
7081 if ((attr->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00007082 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007083 attr->ns->href))) {
7084#ifdef DEBUG_STEP
7085 n++;
7086#endif
7087 addNode(list, (xmlNodePtr) attr);
7088 }
7089 }
7090 }
7091 break;
7092 }
7093 case XML_NAMESPACE_DECL: {
7094 TODO;
7095 break;
7096 }
7097 default:
7098 break;
7099 }
7100 break;
7101 }
7102 } while (cur != NULL);
7103
7104 /*
7105 * If there is some predicate filtering do it now
7106 */
7107 if (op->ch2 != -1) {
7108 xmlXPathObjectPtr obj2;
7109
7110 valuePush(ctxt, xmlXPathWrapNodeSet(list));
7111 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
7112 CHECK_TYPE(XPATH_NODESET);
7113 obj2 = valuePop(ctxt);
7114 list = obj2->nodesetval;
7115 obj2->nodesetval = NULL;
7116 xmlXPathFreeObject(obj2);
7117 }
7118 if (ret == NULL) {
7119 ret = list;
7120 } else {
7121 ret = xmlXPathNodeSetMerge(ret, list);
7122 xmlXPathFreeNodeSet(list);
7123 }
7124 }
7125 ctxt->context->node = tmp;
7126#ifdef DEBUG_STEP
7127 xmlGenericError(xmlGenericErrorContext,
7128 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
7129#endif
7130 xmlXPathFreeObject(obj);
7131 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
7132}
7133
Owen Taylor3473f882001-02-23 17:55:21 +00007134/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007135 * xmlXPathCompOpEval:
7136 * @ctxt: the XPath parser context with the compiled expression
7137 * @op: an XPath compiled operation
7138 *
7139 * Evaluate the Precompiled XPath operation
7140 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007141static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007142xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) {
7143 int equal, ret;
7144 xmlXPathCompExprPtr comp;
7145 xmlXPathObjectPtr arg1, arg2;
7146
7147 comp = ctxt->comp;
7148 switch (op->op) {
7149 case XPATH_OP_END:
7150 return;
7151 case XPATH_OP_AND:
7152 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7153 xmlXPathBooleanFunction(ctxt, 1);
7154 if (ctxt->value->boolval == 0)
7155 return;
7156 arg2 = valuePop(ctxt);
7157 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7158 xmlXPathBooleanFunction(ctxt, 1);
7159 arg1 = valuePop(ctxt);
7160 arg1->boolval &= arg2->boolval;
7161 valuePush(ctxt, arg1);
7162 xmlXPathFreeObject(arg2);
7163 return;
7164 case XPATH_OP_OR:
7165 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7166 xmlXPathBooleanFunction(ctxt, 1);
7167 if (ctxt->value->boolval == 1)
7168 return;
7169 arg2 = valuePop(ctxt);
7170 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7171 xmlXPathBooleanFunction(ctxt, 1);
7172 arg1 = valuePop(ctxt);
7173 arg1->boolval |= arg2->boolval;
7174 valuePush(ctxt, arg1);
7175 xmlXPathFreeObject(arg2);
7176 return;
7177 case XPATH_OP_EQUAL:
7178 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7179 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7180 equal = xmlXPathEqualValues(ctxt);
7181 if (op->value) valuePush(ctxt, xmlXPathNewBoolean(equal));
7182 else valuePush(ctxt, xmlXPathNewBoolean(!equal));
7183 return;
7184 case XPATH_OP_CMP:
7185 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7186 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7187 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
7188 valuePush(ctxt, xmlXPathNewBoolean(ret));
7189 return;
7190 case XPATH_OP_PLUS:
7191 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7192 if (op->ch2 != -1)
7193 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7194 if (op->value == 0) xmlXPathSubValues(ctxt);
7195 else if (op->value == 1) xmlXPathAddValues(ctxt);
7196 else if (op->value == 2) xmlXPathValueFlipSign(ctxt);
7197 else if (op->value == 3) {
7198 xmlXPathObjectPtr arg;
7199
7200 POP_FLOAT
7201 valuePush(ctxt, arg);
7202 }
7203 return;
7204 case XPATH_OP_MULT:
7205 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7206 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7207 if (op->value == 0) xmlXPathMultValues(ctxt);
7208 else if (op->value == 1) xmlXPathDivValues(ctxt);
7209 else if (op->value == 2) xmlXPathModValues(ctxt);
7210 return;
7211 case XPATH_OP_UNION:
7212 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7213 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7214 CHECK_TYPE(XPATH_NODESET);
7215 arg2 = valuePop(ctxt);
7216
7217 CHECK_TYPE(XPATH_NODESET);
7218 arg1 = valuePop(ctxt);
7219
7220 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
7221 arg2->nodesetval);
7222 valuePush(ctxt, arg1);
7223 xmlXPathFreeObject(arg2);
7224 return;
7225 case XPATH_OP_ROOT:
7226 xmlXPathRoot(ctxt);
7227 return;
7228 case XPATH_OP_NODE:
7229 if (op->ch1 != -1)
7230 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7231 if (op->ch2 != -1)
7232 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7233 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
7234 return;
7235 case XPATH_OP_RESET:
7236 if (op->ch1 != -1)
7237 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7238 if (op->ch2 != -1)
7239 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7240 ctxt->context->node = NULL;
7241 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007242 case XPATH_OP_COLLECT: {
7243 if (op->ch1 == -1)
7244 return;
7245
7246 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7247 xmlXPathNodeCollectAndTest(ctxt, op);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007248 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007249 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007250 case XPATH_OP_VALUE:
7251 valuePush(ctxt,
7252 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
7253 return;
7254 case XPATH_OP_VARIABLE: {
7255 if (op->ch1 != -1)
7256 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7257 if (op->value5 == NULL)
7258 valuePush(ctxt,
7259 xmlXPathVariableLookup(ctxt->context, op->value4));
7260 else {
7261 const xmlChar *URI;
7262 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7263 if (URI == NULL) {
7264 xmlGenericError(xmlGenericErrorContext,
7265 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
7266 op->value4, op->value5);
7267 return;
7268 }
7269 valuePush(ctxt,
7270 xmlXPathVariableLookupNS(ctxt->context,
7271 op->value4, URI));
7272 }
7273 return;
7274 }
7275 case XPATH_OP_FUNCTION: {
7276 xmlXPathFunction func;
7277
7278 if (op->ch1 != -1)
7279 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
Daniel Veillarde39a93d2001-04-28 14:35:02 +00007280 if (op->cache != NULL)
7281 func = (xmlXPathFunction) op->cache;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007282 else {
Daniel Veillarde39a93d2001-04-28 14:35:02 +00007283 if (op->value5 == NULL)
7284 func = xmlXPathFunctionLookup(ctxt->context, op->value4);
7285 else {
7286 const xmlChar *URI;
7287 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7288 if (URI == NULL) {
7289 xmlGenericError(xmlGenericErrorContext,
7290 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
7291 op->value4, op->value5);
7292 return;
7293 }
7294 func = xmlXPathFunctionLookupNS(ctxt->context,
7295 op->value4, URI);
7296 }
7297 if (func == NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007298 xmlGenericError(xmlGenericErrorContext,
Daniel Veillarde39a93d2001-04-28 14:35:02 +00007299 "xmlXPathRunEval: function %s not found\n",
7300 op->value4);
7301 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007302 return;
7303 }
Daniel Veillarde39a93d2001-04-28 14:35:02 +00007304 op->cache = (void *) func;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007305 }
7306 func(ctxt, op->value);
7307 return;
7308 }
7309 case XPATH_OP_ARG:
7310 if (op->ch1 != -1)
7311 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7312 if (op->ch2 != -1)
7313 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7314 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007315 case XPATH_OP_PREDICATE:
7316 case XPATH_OP_FILTER: {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007317 xmlXPathObjectPtr res;
7318 xmlXPathObjectPtr obj, tmp;
7319 xmlNodeSetPtr newset = NULL;
7320 xmlNodeSetPtr oldset;
7321 xmlNodePtr oldnode;
7322 int i;
7323
7324 if (op->ch1 != -1)
7325 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7326 if (op->ch2 == -1)
7327 return;
7328
7329 oldnode = ctxt->context->node;
7330
7331#ifdef LIBXML_XPTR_ENABLED
7332 /*
7333 * Hum are we filtering the result of an XPointer expression
7334 */
7335 if (ctxt->value->type == XPATH_LOCATIONSET) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007336 xmlLocationSetPtr newlocset = NULL;
7337 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007338
7339 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007340 * Extract the old locset, and then evaluate the result of the
7341 * expression for all the element in the locset. use it to grow
7342 * up a new locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007343 */
7344 CHECK_TYPE(XPATH_LOCATIONSET);
7345 obj = valuePop(ctxt);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007346 oldlocset = obj->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007347 ctxt->context->node = NULL;
7348
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007349 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007350 ctxt->context->contextSize = 0;
7351 ctxt->context->proximityPosition = 0;
7352 if (op->ch2 != -1)
7353 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7354 res = valuePop(ctxt);
7355 if (res != NULL)
7356 xmlXPathFreeObject(res);
7357 valuePush(ctxt, obj);
7358 CHECK_ERROR;
7359 return;
7360 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007361 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007362
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007363 for (i = 0; i < oldlocset->locNr; i++) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007364 /*
7365 * Run the evaluation with a node list made of a
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007366 * single item in the nodelocset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007367 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007368 ctxt->context->node = oldlocset->locTab[i]->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007369 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7370 valuePush(ctxt, tmp);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007371 ctxt->context->contextSize = oldlocset->locNr;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007372 ctxt->context->proximityPosition = i + 1;
7373
7374 if (op->ch2 != -1)
7375 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7376 CHECK_ERROR;
7377
7378 /*
7379 * The result of the evaluation need to be tested to
7380 * decided whether the filter succeeded or not
7381 */
7382 res = valuePop(ctxt);
7383 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007384 xmlXPtrLocationSetAdd(newlocset,
7385 xmlXPathObjectCopy(oldlocset->locTab[i]));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007386 }
7387
7388 /*
7389 * Cleanup
7390 */
7391 if (res != NULL)
7392 xmlXPathFreeObject(res);
7393 if (ctxt->value == tmp) {
7394 res = valuePop(ctxt);
7395 xmlXPathFreeObject(res);
7396 }
7397
7398 ctxt->context->node = NULL;
7399 }
7400
7401 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007402 * The result is used as the new evaluation locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007403 */
7404 xmlXPathFreeObject(obj);
7405 ctxt->context->node = NULL;
7406 ctxt->context->contextSize = -1;
7407 ctxt->context->proximityPosition = -1;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007408 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007409 ctxt->context->node = oldnode;
7410 return;
7411 }
7412#endif /* LIBXML_XPTR_ENABLED */
7413
7414 /*
7415 * Extract the old set, and then evaluate the result of the
7416 * expression for all the element in the set. use it to grow
7417 * up a new set.
7418 */
7419 CHECK_TYPE(XPATH_NODESET);
7420 obj = valuePop(ctxt);
7421 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00007422
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007423 oldnode = ctxt->context->node;
7424 ctxt->context->node = NULL;
7425
7426 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
7427 ctxt->context->contextSize = 0;
7428 ctxt->context->proximityPosition = 0;
7429 if (op->ch2 != -1)
7430 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7431 res = valuePop(ctxt);
7432 if (res != NULL)
7433 xmlXPathFreeObject(res);
7434 valuePush(ctxt, obj);
Daniel Veillard911f49a2001-04-07 15:39:35 +00007435 ctxt->context->node = oldnode;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007436 CHECK_ERROR;
7437 } else {
7438 /*
7439 * Initialize the new set.
7440 */
7441 newset = xmlXPathNodeSetCreate(NULL);
7442
7443 for (i = 0; i < oldset->nodeNr; i++) {
7444 /*
7445 * Run the evaluation with a node list made of
7446 * a single item in the nodeset.
7447 */
7448 ctxt->context->node = oldset->nodeTab[i];
7449 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7450 valuePush(ctxt, tmp);
7451 ctxt->context->contextSize = oldset->nodeNr;
7452 ctxt->context->proximityPosition = i + 1;
7453
7454 if (op->ch2 != -1)
7455 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7456 CHECK_ERROR;
7457
7458 /*
7459 * The result of the evaluation need to be tested to
7460 * decided whether the filter succeeded or not
7461 */
7462 res = valuePop(ctxt);
7463 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
7464 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
7465 }
7466
7467 /*
7468 * Cleanup
7469 */
7470 if (res != NULL)
7471 xmlXPathFreeObject(res);
7472 if (ctxt->value == tmp) {
7473 res = valuePop(ctxt);
7474 xmlXPathFreeObject(res);
7475 }
7476
7477 ctxt->context->node = NULL;
7478 }
7479
7480 /*
7481 * The result is used as the new evaluation set.
7482 */
7483 xmlXPathFreeObject(obj);
7484 ctxt->context->node = NULL;
7485 ctxt->context->contextSize = -1;
7486 ctxt->context->proximityPosition = -1;
7487 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
7488 }
7489 ctxt->context->node = oldnode;
7490 return;
7491 }
7492 case XPATH_OP_SORT:
7493 if (op->ch1 != -1)
7494 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7495 if ((ctxt->value != NULL) &&
7496 (ctxt->value->type == XPATH_NODESET) &&
7497 (ctxt->value->nodesetval != NULL))
7498 xmlXPathNodeSetSort(ctxt->value->nodesetval);
7499 return;
7500#ifdef LIBXML_XPTR_ENABLED
7501 case XPATH_OP_RANGETO: {
7502 xmlXPathObjectPtr range;
7503 xmlXPathObjectPtr res, obj;
7504 xmlXPathObjectPtr tmp;
7505 xmlLocationSetPtr newset = NULL;
7506 xmlNodeSetPtr oldset;
7507 int i;
7508
7509 if (op->ch1 != -1)
7510 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7511 if (op->ch2 == -1)
7512 return;
7513
7514 CHECK_TYPE(XPATH_NODESET);
7515 obj = valuePop(ctxt);
7516 oldset = obj->nodesetval;
7517 ctxt->context->node = NULL;
7518
7519 newset = xmlXPtrLocationSetCreate(NULL);
7520
Daniel Veillard911f49a2001-04-07 15:39:35 +00007521 if (oldset != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007522 for (i = 0; i < oldset->nodeNr; i++) {
7523 /*
7524 * Run the evaluation with a node list made of a single item
7525 * in the nodeset.
7526 */
7527 ctxt->context->node = oldset->nodeTab[i];
7528 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7529 valuePush(ctxt, tmp);
7530
7531 if (op->ch2 != -1)
7532 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7533 CHECK_ERROR;
7534
7535 /*
7536 * The result of the evaluation need to be tested to
7537 * decided whether the filter succeeded or not
7538 */
7539 res = valuePop(ctxt);
7540 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
7541 if (range != NULL) {
7542 xmlXPtrLocationSetAdd(newset, range);
7543 }
7544
7545 /*
7546 * Cleanup
7547 */
7548 if (res != NULL)
7549 xmlXPathFreeObject(res);
7550 if (ctxt->value == tmp) {
7551 res = valuePop(ctxt);
7552 xmlXPathFreeObject(res);
7553 }
7554
7555 ctxt->context->node = NULL;
7556 }
Daniel Veillard911f49a2001-04-07 15:39:35 +00007557 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007558
7559 /*
7560 * The result is used as the new evaluation set.
7561 */
7562 xmlXPathFreeObject(obj);
7563 ctxt->context->node = NULL;
7564 ctxt->context->contextSize = -1;
7565 ctxt->context->proximityPosition = -1;
7566 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
7567 return;
7568 }
7569#endif /* LIBXML_XPTR_ENABLED */
7570 }
7571 xmlGenericError(xmlGenericErrorContext,
7572 "XPath: unknown precompiled operation %d\n",
7573 op->op);
7574 return;
7575}
7576
7577/**
7578 * xmlXPathRunEval:
7579 * @ctxt: the XPath parser context with the compiled expression
7580 *
7581 * Evaluate the Precompiled XPath expression in the given context.
7582 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007583static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007584xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
7585 xmlXPathCompExprPtr comp;
7586
7587 if ((ctxt == NULL) || (ctxt->comp == NULL))
7588 return;
7589
7590 if (ctxt->valueTab == NULL) {
7591 /* Allocate the value stack */
7592 ctxt->valueTab = (xmlXPathObjectPtr *)
7593 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
7594 if (ctxt->valueTab == NULL) {
7595 xmlFree(ctxt);
7596 xmlGenericError(xmlGenericErrorContext,
7597 "xmlXPathRunEval: out of memory\n");
7598 return;
7599 }
7600 ctxt->valueNr = 0;
7601 ctxt->valueMax = 10;
7602 ctxt->value = NULL;
7603 }
7604 comp = ctxt->comp;
7605 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
7606}
7607
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007608/************************************************************************
7609 * *
7610 * Public interfaces *
7611 * *
7612 ************************************************************************/
7613
7614/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007615 * xmlXPathEvalPredicate:
7616 * @ctxt: the XPath context
7617 * @res: the Predicate Expression evaluation result
7618 *
7619 * Evaluate a predicate result for the current node.
7620 * A PredicateExpr is evaluated by evaluating the Expr and converting
7621 * the result to a boolean. If the result is a number, the result will
7622 * be converted to true if the number is equal to the position of the
7623 * context node in the context node list (as returned by the position
7624 * function) and will be converted to false otherwise; if the result
7625 * is not a number, then the result will be converted as if by a call
7626 * to the boolean function.
7627 *
7628 * Return 1 if predicate is true, 0 otherwise
7629 */
7630int
7631xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
7632 if (res == NULL) return(0);
7633 switch (res->type) {
7634 case XPATH_BOOLEAN:
7635 return(res->boolval);
7636 case XPATH_NUMBER:
7637 return(res->floatval == ctxt->proximityPosition);
7638 case XPATH_NODESET:
7639 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007640 if (res->nodesetval == NULL)
7641 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007642 return(res->nodesetval->nodeNr != 0);
7643 case XPATH_STRING:
7644 return((res->stringval != NULL) &&
7645 (xmlStrlen(res->stringval) != 0));
7646 default:
7647 STRANGE
7648 }
7649 return(0);
7650}
7651
7652/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007653 * xmlXPathEvaluatePredicateResult:
7654 * @ctxt: the XPath Parser context
7655 * @res: the Predicate Expression evaluation result
7656 *
7657 * Evaluate a predicate result for the current node.
7658 * A PredicateExpr is evaluated by evaluating the Expr and converting
7659 * the result to a boolean. If the result is a number, the result will
7660 * be converted to true if the number is equal to the position of the
7661 * context node in the context node list (as returned by the position
7662 * function) and will be converted to false otherwise; if the result
7663 * is not a number, then the result will be converted as if by a call
7664 * to the boolean function.
7665 *
7666 * Return 1 if predicate is true, 0 otherwise
7667 */
7668int
7669xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
7670 xmlXPathObjectPtr res) {
7671 if (res == NULL) return(0);
7672 switch (res->type) {
7673 case XPATH_BOOLEAN:
7674 return(res->boolval);
7675 case XPATH_NUMBER:
7676 return(res->floatval == ctxt->context->proximityPosition);
7677 case XPATH_NODESET:
7678 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00007679 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00007680 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007681 return(res->nodesetval->nodeNr != 0);
7682 case XPATH_STRING:
7683 return((res->stringval != NULL) &&
7684 (xmlStrlen(res->stringval) != 0));
7685 default:
7686 STRANGE
7687 }
7688 return(0);
7689}
7690
7691/**
7692 * xmlXPathCompile:
7693 * @str: the XPath expression
7694 *
7695 * Compile an XPath expression
7696 *
7697 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7698 * the caller has to free the object.
7699 */
7700xmlXPathCompExprPtr
7701xmlXPathCompile(const xmlChar *str) {
7702 xmlXPathParserContextPtr ctxt;
7703 xmlXPathCompExprPtr comp;
7704
7705 xmlXPathInit();
7706
7707 ctxt = xmlXPathNewParserContext(str, NULL);
7708 xmlXPathCompileExpr(ctxt);
7709
Daniel Veillard40af6492001-04-22 08:50:55 +00007710 if (*ctxt->cur != 0) {
7711 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7712 comp = NULL;
7713 } else {
7714 comp = ctxt->comp;
7715 ctxt->comp = NULL;
7716 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007717 xmlXPathFreeParserContext(ctxt);
7718 return(comp);
7719}
7720
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007721/**
7722 * xmlXPathCompiledEval:
7723 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00007724 * @ctx: the XPath context
7725 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007726 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00007727 *
7728 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7729 * the caller has to free the object.
7730 */
7731xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007732xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00007733 xmlXPathParserContextPtr ctxt;
7734 xmlXPathObjectPtr res, tmp, init = NULL;
7735 int stack = 0;
7736
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007737 if ((comp == NULL) || (ctx == NULL))
7738 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007739 xmlXPathInit();
7740
7741 CHECK_CONTEXT(ctx)
7742
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007743 ctxt = xmlXPathCompParserContext(comp, ctx);
7744 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007745
7746 if (ctxt->value == NULL) {
7747 xmlGenericError(xmlGenericErrorContext,
7748 "xmlXPathEval: evaluation failed\n");
7749 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00007750 } else {
7751 res = valuePop(ctxt);
7752 }
7753
7754 do {
7755 tmp = valuePop(ctxt);
7756 if (tmp != NULL) {
7757 if (tmp != init)
7758 stack++;
7759 xmlXPathFreeObject(tmp);
7760 }
7761 } while (tmp != NULL);
7762 if ((stack != 0) && (res != NULL)) {
7763 xmlGenericError(xmlGenericErrorContext,
7764 "xmlXPathEval: %d object left on the stack\n",
7765 stack);
7766 }
7767 if (ctxt->error != XPATH_EXPRESSION_OK) {
7768 xmlXPathFreeObject(res);
7769 res = NULL;
7770 }
7771
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007772
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007773 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007774 xmlXPathFreeParserContext(ctxt);
7775 return(res);
7776}
7777
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007778/**
7779 * xmlXPathEvalExpr:
7780 * @ctxt: the XPath Parser context
7781 *
7782 * Parse and evaluate an XPath expression in the given context,
7783 * then push the result on the context stack
7784 */
7785void
7786xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
7787 xmlXPathCompileExpr(ctxt);
7788 xmlXPathRunEval(ctxt);
7789}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007790
7791/**
7792 * xmlXPathEval:
7793 * @str: the XPath expression
7794 * @ctx: the XPath context
7795 *
7796 * Evaluate the XPath Location Path in the given context.
7797 *
7798 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7799 * the caller has to free the object.
7800 */
7801xmlXPathObjectPtr
7802xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
7803 xmlXPathParserContextPtr ctxt;
7804 xmlXPathObjectPtr res, tmp, init = NULL;
7805 int stack = 0;
7806
7807 xmlXPathInit();
7808
7809 CHECK_CONTEXT(ctx)
7810
7811 ctxt = xmlXPathNewParserContext(str, ctx);
7812 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007813
7814 if (ctxt->value == NULL) {
7815 xmlGenericError(xmlGenericErrorContext,
7816 "xmlXPathEval: evaluation failed\n");
7817 res = NULL;
7818 } else if (*ctxt->cur != 0) {
7819 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7820 res = NULL;
7821 } else {
7822 res = valuePop(ctxt);
7823 }
7824
7825 do {
7826 tmp = valuePop(ctxt);
7827 if (tmp != NULL) {
7828 if (tmp != init)
7829 stack++;
7830 xmlXPathFreeObject(tmp);
7831 }
7832 } while (tmp != NULL);
7833 if ((stack != 0) && (res != NULL)) {
7834 xmlGenericError(xmlGenericErrorContext,
7835 "xmlXPathEval: %d object left on the stack\n",
7836 stack);
7837 }
7838 if (ctxt->error != XPATH_EXPRESSION_OK) {
7839 xmlXPathFreeObject(res);
7840 res = NULL;
7841 }
7842
Owen Taylor3473f882001-02-23 17:55:21 +00007843 xmlXPathFreeParserContext(ctxt);
7844 return(res);
7845}
7846
7847/**
7848 * xmlXPathEvalExpression:
7849 * @str: the XPath expression
7850 * @ctxt: the XPath context
7851 *
7852 * Evaluate the XPath expression in the given context.
7853 *
7854 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
7855 * the caller has to free the object.
7856 */
7857xmlXPathObjectPtr
7858xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
7859 xmlXPathParserContextPtr pctxt;
7860 xmlXPathObjectPtr res, tmp;
7861 int stack = 0;
7862
7863 xmlXPathInit();
7864
7865 CHECK_CONTEXT(ctxt)
7866
7867 pctxt = xmlXPathNewParserContext(str, ctxt);
7868 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007869
7870 if (*pctxt->cur != 0) {
7871 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7872 res = NULL;
7873 } else {
7874 res = valuePop(pctxt);
7875 }
7876 do {
7877 tmp = valuePop(pctxt);
7878 if (tmp != NULL) {
7879 xmlXPathFreeObject(tmp);
7880 stack++;
7881 }
7882 } while (tmp != NULL);
7883 if ((stack != 0) && (res != NULL)) {
7884 xmlGenericError(xmlGenericErrorContext,
7885 "xmlXPathEvalExpression: %d object left on the stack\n",
7886 stack);
7887 }
7888 xmlXPathFreeParserContext(pctxt);
7889 return(res);
7890}
7891
7892/**
7893 * xmlXPathRegisterAllFunctions:
7894 * @ctxt: the XPath context
7895 *
7896 * Registers all default XPath functions in this context
7897 */
7898void
7899xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
7900{
7901 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
7902 xmlXPathBooleanFunction);
7903 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
7904 xmlXPathCeilingFunction);
7905 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
7906 xmlXPathCountFunction);
7907 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
7908 xmlXPathConcatFunction);
7909 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
7910 xmlXPathContainsFunction);
7911 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
7912 xmlXPathIdFunction);
7913 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
7914 xmlXPathFalseFunction);
7915 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
7916 xmlXPathFloorFunction);
7917 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
7918 xmlXPathLastFunction);
7919 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
7920 xmlXPathLangFunction);
7921 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
7922 xmlXPathLocalNameFunction);
7923 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
7924 xmlXPathNotFunction);
7925 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
7926 xmlXPathNameFunction);
7927 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
7928 xmlXPathNamespaceURIFunction);
7929 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
7930 xmlXPathNormalizeFunction);
7931 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
7932 xmlXPathNumberFunction);
7933 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
7934 xmlXPathPositionFunction);
7935 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
7936 xmlXPathRoundFunction);
7937 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
7938 xmlXPathStringFunction);
7939 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
7940 xmlXPathStringLengthFunction);
7941 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
7942 xmlXPathStartsWithFunction);
7943 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
7944 xmlXPathSubstringFunction);
7945 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
7946 xmlXPathSubstringBeforeFunction);
7947 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
7948 xmlXPathSubstringAfterFunction);
7949 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
7950 xmlXPathSumFunction);
7951 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
7952 xmlXPathTrueFunction);
7953 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
7954 xmlXPathTranslateFunction);
7955}
7956
7957#endif /* LIBXML_XPATH_ENABLED */