blob: a579ff0a211646486b2e255cab3ef2fb52f2376e [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
42
43#include <libxml/xmlmemory.h>
44#include <libxml/tree.h>
45#include <libxml/valid.h>
46#include <libxml/xpath.h>
47#include <libxml/xpathInternals.h>
48#include <libxml/parserInternals.h>
49#include <libxml/hash.h>
50#ifdef LIBXML_XPTR_ENABLED
51#include <libxml/xpointer.h>
52#endif
53#ifdef LIBXML_DEBUG_ENABLED
54#include <libxml/debugXML.h>
55#endif
56#include <libxml/xmlerror.h>
57
58/* #define DEBUG */
59/* #define DEBUG_STEP */
60/* #define DEBUG_EXPR */
61
62void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
63double xmlXPathStringEvalNumber(const xmlChar *str);
64
Daniel Veillard9e7160d2001-03-18 23:17:47 +000065/************************************************************************
66 * *
67 * Floating point stuff *
68 * *
69 ************************************************************************/
70
Owen Taylor3473f882001-02-23 17:55:21 +000071/*
Owen Taylor3473f882001-02-23 17:55:21 +000072 * The lack of portability of this section of the libc is annoying !
73 */
74double xmlXPathNAN = 0;
75double xmlXPathPINF = 1;
76double xmlXPathNINF = -1;
77
78#ifndef isinf
79#ifndef HAVE_ISINF
80
81#if HAVE_FPCLASS
82
83int isinf(double d) {
84 fpclass_t type = fpclass(d);
85 switch (type) {
86 case FP_NINF:
87 return(-1);
88 case FP_PINF:
89 return(1);
90 }
91 return(0);
92}
93
94#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
95
96#if HAVE_FP_CLASS_H
97#include <fp_class.h>
98#endif
99
100int isinf(double d) {
101#if HAVE_FP_CLASS
102 int fpclass = fp_class(d);
103#else
104 int fpclass = fp_class_d(d);
105#endif
106 if (fpclass == FP_POS_INF)
107 return(1);
108 if (fpclass == FP_NEG_INF)
109 return(-1);
110 return(0);
111}
112
113#elif defined(HAVE_CLASS)
114
115int isinf(double d) {
116 int fpclass = class(d);
117 if (fpclass == FP_PLUS_INF)
118 return(1);
119 if (fpclass == FP_MINUS_INF)
120 return(-1);
121 return(0);
122}
123#elif defined(finite) || defined(HAVE_FINITE)
124int isinf(double x) { return !finite(x) && x==x; }
125#elif defined(HUGE_VAL)
126int isinf(double x)
127{
128 if (x == HUGE_VAL)
129 return(1);
130 if (x == -HUGE_VAL)
131 return(-1);
132 return(0);
133}
134#endif
135
136#endif /* ! HAVE_ISINF */
137#endif /* ! defined(isinf) */
138
139#ifndef isnan
140#ifndef HAVE_ISNAN
141
142#ifdef HAVE_ISNAND
143#define isnan(f) isnand(f)
144#endif /* HAVE_iSNAND */
145
146#endif /* ! HAVE_iSNAN */
147#endif /* ! defined(isnan) */
148
149/**
150 * xmlXPathInit:
151 *
152 * Initialize the XPath environment
153 */
154void
155xmlXPathInit(void) {
156 static int initialized = 0;
157
158 if (initialized) return;
159
Daniel Veillard61d80a22001-04-27 17:13:01 +0000160#ifdef XPATH_USE_DIVISION_SHORTCUTS
161 xmlXPathNAN = 0;
162 xmlXPathNAN /= 0.0;
163 xmlXPathPINF = 1;
164 xmlXPathPINF /= 0.0;
165 xmlXPathNINF = -1;
166 xmlXPathNINF /= 0.0;
167#else
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000168 xmlXPathNAN = 0.0 / 0.0;
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000169 xmlXPathPINF = 1 / 0.0;
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000170 xmlXPathNINF = -1 / 0.0;
Daniel Veillard61d80a22001-04-27 17:13:01 +0000171#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000172
173 initialized = 1;
174}
175
176/************************************************************************
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000177 * *
178 * Parser Types *
179 * *
180 ************************************************************************/
181
182/*
183 * Types are private:
184 */
185
186typedef enum {
187 XPATH_OP_END=0,
188 XPATH_OP_AND,
189 XPATH_OP_OR,
190 XPATH_OP_EQUAL,
191 XPATH_OP_CMP,
192 XPATH_OP_PLUS,
193 XPATH_OP_MULT,
194 XPATH_OP_UNION,
195 XPATH_OP_ROOT,
196 XPATH_OP_NODE,
197 XPATH_OP_RESET,
198 XPATH_OP_COLLECT,
199 XPATH_OP_VALUE,
200 XPATH_OP_VARIABLE,
201 XPATH_OP_FUNCTION,
202 XPATH_OP_ARG,
203 XPATH_OP_PREDICATE,
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000204 XPATH_OP_FILTER,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000205 XPATH_OP_SORT
206#ifdef LIBXML_XPTR_ENABLED
207 ,XPATH_OP_RANGETO
208#endif
209} xmlXPathOp;
210
211typedef enum {
212 AXIS_ANCESTOR = 1,
213 AXIS_ANCESTOR_OR_SELF,
214 AXIS_ATTRIBUTE,
215 AXIS_CHILD,
216 AXIS_DESCENDANT,
217 AXIS_DESCENDANT_OR_SELF,
218 AXIS_FOLLOWING,
219 AXIS_FOLLOWING_SIBLING,
220 AXIS_NAMESPACE,
221 AXIS_PARENT,
222 AXIS_PRECEDING,
223 AXIS_PRECEDING_SIBLING,
224 AXIS_SELF
225} xmlXPathAxisVal;
226
227typedef enum {
228 NODE_TEST_NONE = 0,
229 NODE_TEST_TYPE = 1,
230 NODE_TEST_PI = 2,
231 NODE_TEST_ALL = 3,
232 NODE_TEST_NS = 4,
233 NODE_TEST_NAME = 5
234} xmlXPathTestVal;
235
236typedef enum {
237 NODE_TYPE_NODE = 0,
238 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
239 NODE_TYPE_TEXT = XML_TEXT_NODE,
240 NODE_TYPE_PI = XML_PI_NODE
241} xmlXPathTypeVal;
242
243
244typedef struct _xmlXPathStepOp xmlXPathStepOp;
245typedef xmlXPathStepOp *xmlXPathStepOpPtr;
246struct _xmlXPathStepOp {
247 xmlXPathOp op;
248 int ch1;
249 int ch2;
250 int value;
251 int value2;
252 int value3;
253 void *value4;
254 void *value5;
255};
256
257struct _xmlXPathCompExpr {
258 int nbStep;
259 int maxStep;
260 xmlXPathStepOp *steps; /* ops for computation */
261 int last;
262};
263
264/************************************************************************
265 * *
266 * Parser Type functions *
267 * *
268 ************************************************************************/
269
270/**
271 * xmlXPathNewCompExpr:
272 *
273 * Create a new Xpath component
274 *
275 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
276 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000277static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000278xmlXPathNewCompExpr(void) {
279 xmlXPathCompExprPtr cur;
280
281 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
282 if (cur == NULL) {
283 xmlGenericError(xmlGenericErrorContext,
284 "xmlXPathNewCompExpr : malloc failed\n");
285 return(NULL);
286 }
287 memset(cur, 0, sizeof(xmlXPathCompExpr));
288 cur->maxStep = 10;
289 cur->nbStep = 0;
290 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
291 sizeof(xmlXPathStepOp));
292 if (cur->steps == NULL) {
293 xmlGenericError(xmlGenericErrorContext,
294 "xmlXPathNewCompExpr : malloc failed\n");
295 xmlFree(cur);
296 return(NULL);
297 }
298 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
299 cur->last = -1;
300 return(cur);
301}
302
303/**
304 * xmlXPathFreeCompExpr:
305 * @comp: an XPATH comp
306 *
307 * Free up the memory allocated by @comp
308 */
309void
310xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) {
311 xmlXPathStepOpPtr op;
312 int i;
313
314 if (comp == NULL)
315 return;
316 for (i = 0;i < comp->nbStep;i++) {
317 op = &comp->steps[i];
318 if (op->value4 != NULL) {
319 if (op->op == XPATH_OP_VALUE)
320 xmlXPathFreeObject(op->value4);
321 else
322 xmlFree(op->value4);
323 }
324 if (op->value5 != NULL)
325 xmlFree(op->value5);
326 }
327 if (comp->steps != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000328 xmlFree(comp->steps);
329 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000330 xmlFree(comp);
331}
332
333/**
334 * xmlXPathCompExprAdd:
335 * @comp: the compiled expression
336 * @ch1: first child index
337 * @ch2: second child index
338 * @op: an op
339 * @value: the first int value
340 * @value2: the second int value
341 * @value3: the third int value
342 * @value4: the first string value
343 * @value5: the second string value
344 *
345 * Add an step to an XPath Compiled Expression
346 *
347 * Returns -1 in case of failure, the index otherwise
348 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000349static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000350xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
351 xmlXPathOp op, int value,
352 int value2, int value3, void *value4, void *value5) {
353 if (comp->nbStep >= comp->maxStep) {
354 xmlXPathStepOp *real;
355
356 comp->maxStep *= 2;
357 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
358 comp->maxStep * sizeof(xmlXPathStepOp));
359 if (real == NULL) {
360 comp->maxStep /= 2;
361 xmlGenericError(xmlGenericErrorContext,
362 "xmlXPathCompExprAdd : realloc failed\n");
363 return(-1);
364 }
365 comp->steps = real;
366 }
367 comp->last = comp->nbStep;
368 comp->steps[comp->nbStep].ch1 = ch1;
369 comp->steps[comp->nbStep].ch2 = ch2;
370 comp->steps[comp->nbStep].op = op;
371 comp->steps[comp->nbStep].value = value;
372 comp->steps[comp->nbStep].value2 = value2;
373 comp->steps[comp->nbStep].value3 = value3;
374 comp->steps[comp->nbStep].value4 = value4;
375 comp->steps[comp->nbStep].value5 = value5;
376 return(comp->nbStep++);
377}
378
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000379#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
380 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
381 (op), (val), (val2), (val3), (val4), (val5))
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000382#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
383 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
384 (op), (val), (val2), (val3), (val4), (val5))
385
386#define PUSH_LEAVE_EXPR(op, val, val2) \
387xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
388
389#define PUSH_UNARY_EXPR(op, ch, val, val2) \
390xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
391
392#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
393xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
394
395/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000396 * *
397 * Debugging related functions *
398 * *
399 ************************************************************************/
400
401#define TODO \
402 xmlGenericError(xmlGenericErrorContext, \
403 "Unimplemented block at %s:%d\n", \
404 __FILE__, __LINE__);
405
406#define STRANGE \
407 xmlGenericError(xmlGenericErrorContext, \
408 "Internal error at %s:%d\n", \
409 __FILE__, __LINE__);
410
411#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000412static void
413xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000414 int i;
415 char shift[100];
416
417 for (i = 0;((i < depth) && (i < 25));i++)
418 shift[2 * i] = shift[2 * i + 1] = ' ';
419 shift[2 * i] = shift[2 * i + 1] = 0;
420 if (cur == NULL) {
421 fprintf(output, shift);
422 fprintf(output, "Node is NULL !\n");
423 return;
424
425 }
426
427 if ((cur->type == XML_DOCUMENT_NODE) ||
428 (cur->type == XML_HTML_DOCUMENT_NODE)) {
429 fprintf(output, shift);
430 fprintf(output, " /\n");
431 } else if (cur->type == XML_ATTRIBUTE_NODE)
432 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
433 else
434 xmlDebugDumpOneNode(output, cur, depth);
435}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000436static void
437xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000438 xmlNodePtr tmp;
439 int i;
440 char shift[100];
441
442 for (i = 0;((i < depth) && (i < 25));i++)
443 shift[2 * i] = shift[2 * i + 1] = ' ';
444 shift[2 * i] = shift[2 * i + 1] = 0;
445 if (cur == NULL) {
446 fprintf(output, shift);
447 fprintf(output, "Node is NULL !\n");
448 return;
449
450 }
451
452 while (cur != NULL) {
453 tmp = cur;
454 cur = cur->next;
455 xmlDebugDumpOneNode(output, tmp, depth);
456 }
457}
Owen Taylor3473f882001-02-23 17:55:21 +0000458
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000459static void
460xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000461 int i;
462 char shift[100];
463
464 for (i = 0;((i < depth) && (i < 25));i++)
465 shift[2 * i] = shift[2 * i + 1] = ' ';
466 shift[2 * i] = shift[2 * i + 1] = 0;
467
468 if (cur == NULL) {
469 fprintf(output, shift);
470 fprintf(output, "NodeSet is NULL !\n");
471 return;
472
473 }
474
Daniel Veillard911f49a2001-04-07 15:39:35 +0000475 if (cur != NULL) {
476 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
477 for (i = 0;i < cur->nodeNr;i++) {
478 fprintf(output, shift);
479 fprintf(output, "%d", i + 1);
480 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
481 }
Owen Taylor3473f882001-02-23 17:55:21 +0000482 }
483}
484
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000485static void
486xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000487 int i;
488 char shift[100];
489
490 for (i = 0;((i < depth) && (i < 25));i++)
491 shift[2 * i] = shift[2 * i + 1] = ' ';
492 shift[2 * i] = shift[2 * i + 1] = 0;
493
494 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
495 fprintf(output, shift);
496 fprintf(output, "Value Tree is NULL !\n");
497 return;
498
499 }
500
501 fprintf(output, shift);
502 fprintf(output, "%d", i + 1);
503 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
504}
Owen Taylor3473f882001-02-23 17:55:21 +0000505#if defined(LIBXML_XPTR_ENABLED)
506void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000507static void
508xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000509 int i;
510 char shift[100];
511
512 for (i = 0;((i < depth) && (i < 25));i++)
513 shift[2 * i] = shift[2 * i + 1] = ' ';
514 shift[2 * i] = shift[2 * i + 1] = 0;
515
516 if (cur == NULL) {
517 fprintf(output, shift);
518 fprintf(output, "LocationSet is NULL !\n");
519 return;
520
521 }
522
523 for (i = 0;i < cur->locNr;i++) {
524 fprintf(output, shift);
525 fprintf(output, "%d : ", i + 1);
526 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
527 }
528}
529#endif
530
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000531/**
532 * xmlXPathDebugDumpObject:
533 * @output: the FILE * to dump the output
534 * @cur: the object to inspect
535 * @depth: indentation level
536 *
537 * Dump the content of the object for debugging purposes
538 */
539void
540xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000541 int i;
542 char shift[100];
543
544 for (i = 0;((i < depth) && (i < 25));i++)
545 shift[2 * i] = shift[2 * i + 1] = ' ';
546 shift[2 * i] = shift[2 * i + 1] = 0;
547
548 fprintf(output, shift);
549
550 if (cur == NULL) {
551 fprintf(output, "Object is empty (NULL)\n");
552 return;
553 }
554 switch(cur->type) {
555 case XPATH_UNDEFINED:
556 fprintf(output, "Object is uninitialized\n");
557 break;
558 case XPATH_NODESET:
559 fprintf(output, "Object is a Node Set :\n");
560 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
561 break;
562 case XPATH_XSLT_TREE:
563 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000564 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000565 break;
566 case XPATH_BOOLEAN:
567 fprintf(output, "Object is a Boolean : ");
568 if (cur->boolval) fprintf(output, "true\n");
569 else fprintf(output, "false\n");
570 break;
571 case XPATH_NUMBER:
572 fprintf(output, "Object is a number : %0g\n", cur->floatval);
573 break;
574 case XPATH_STRING:
575 fprintf(output, "Object is a string : ");
576 xmlDebugDumpString(output, cur->stringval);
577 fprintf(output, "\n");
578 break;
579 case XPATH_POINT:
580 fprintf(output, "Object is a point : index %d in node", cur->index);
581 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
582 fprintf(output, "\n");
583 break;
584 case XPATH_RANGE:
585 if ((cur->user2 == NULL) ||
586 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
587 fprintf(output, "Object is a collapsed range :\n");
588 fprintf(output, shift);
589 if (cur->index >= 0)
590 fprintf(output, "index %d in ", cur->index);
591 fprintf(output, "node\n");
592 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
593 depth + 1);
594 } else {
595 fprintf(output, "Object is a range :\n");
596 fprintf(output, shift);
597 fprintf(output, "From ");
598 if (cur->index >= 0)
599 fprintf(output, "index %d in ", cur->index);
600 fprintf(output, "node\n");
601 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
602 depth + 1);
603 fprintf(output, shift);
604 fprintf(output, "To ");
605 if (cur->index2 >= 0)
606 fprintf(output, "index %d in ", cur->index2);
607 fprintf(output, "node\n");
608 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
609 depth + 1);
610 fprintf(output, "\n");
611 }
612 break;
613 case XPATH_LOCATIONSET:
614#if defined(LIBXML_XPTR_ENABLED)
615 fprintf(output, "Object is a Location Set:\n");
616 xmlXPathDebugDumpLocationSet(output,
617 (xmlLocationSetPtr) cur->user, depth);
618#endif
619 break;
620 case XPATH_USERS:
621 fprintf(output, "Object is user defined\n");
622 break;
623 }
624}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000625
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000626static void
627xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000628 xmlXPathStepOpPtr op, int depth) {
629 int i;
630 char shift[100];
631
632 for (i = 0;((i < depth) && (i < 25));i++)
633 shift[2 * i] = shift[2 * i + 1] = ' ';
634 shift[2 * i] = shift[2 * i + 1] = 0;
635
636 fprintf(output, shift);
637 if (op == NULL) {
638 fprintf(output, "Step is NULL\n");
639 return;
640 }
641 switch (op->op) {
642 case XPATH_OP_END:
643 fprintf(output, "END"); break;
644 case XPATH_OP_AND:
645 fprintf(output, "AND"); break;
646 case XPATH_OP_OR:
647 fprintf(output, "OR"); break;
648 case XPATH_OP_EQUAL:
649 if (op->value)
650 fprintf(output, "EQUAL =");
651 else
652 fprintf(output, "EQUAL !=");
653 break;
654 case XPATH_OP_CMP:
655 if (op->value)
656 fprintf(output, "CMP <");
657 else
658 fprintf(output, "CMP >");
659 if (!op->value2)
660 fprintf(output, "=");
661 break;
662 case XPATH_OP_PLUS:
663 if (op->value == 0)
664 fprintf(output, "PLUS -");
665 else if (op->value == 1)
666 fprintf(output, "PLUS +");
667 else if (op->value == 2)
668 fprintf(output, "PLUS unary -");
669 else if (op->value == 3)
670 fprintf(output, "PLUS unary - -");
671 break;
672 case XPATH_OP_MULT:
673 if (op->value == 0)
674 fprintf(output, "MULT *");
675 else if (op->value == 1)
676 fprintf(output, "MULT div");
677 else
678 fprintf(output, "MULT mod");
679 break;
680 case XPATH_OP_UNION:
681 fprintf(output, "UNION"); break;
682 case XPATH_OP_ROOT:
683 fprintf(output, "ROOT"); break;
684 case XPATH_OP_NODE:
685 fprintf(output, "NODE"); break;
686 case XPATH_OP_RESET:
687 fprintf(output, "RESET"); break;
688 case XPATH_OP_SORT:
689 fprintf(output, "SORT"); break;
690 case XPATH_OP_COLLECT: {
691 xmlXPathAxisVal axis = op->value;
692 xmlXPathTestVal test = op->value2;
693 xmlXPathTypeVal type = op->value3;
694 const xmlChar *prefix = op->value4;
695 const xmlChar *name = op->value5;
696
697 fprintf(output, "COLLECT ");
698 switch (axis) {
699 case AXIS_ANCESTOR:
700 fprintf(output, " 'ancestors' "); break;
701 case AXIS_ANCESTOR_OR_SELF:
702 fprintf(output, " 'ancestors-or-self' "); break;
703 case AXIS_ATTRIBUTE:
704 fprintf(output, " 'attributes' "); break;
705 case AXIS_CHILD:
706 fprintf(output, " 'child' "); break;
707 case AXIS_DESCENDANT:
708 fprintf(output, " 'descendant' "); break;
709 case AXIS_DESCENDANT_OR_SELF:
710 fprintf(output, " 'descendant-or-self' "); break;
711 case AXIS_FOLLOWING:
712 fprintf(output, " 'following' "); break;
713 case AXIS_FOLLOWING_SIBLING:
714 fprintf(output, " 'following-siblings' "); break;
715 case AXIS_NAMESPACE:
716 fprintf(output, " 'namespace' "); break;
717 case AXIS_PARENT:
718 fprintf(output, " 'parent' "); break;
719 case AXIS_PRECEDING:
720 fprintf(output, " 'preceding' "); break;
721 case AXIS_PRECEDING_SIBLING:
722 fprintf(output, " 'preceding-sibling' "); break;
723 case AXIS_SELF:
724 fprintf(output, " 'self' "); break;
725 }
726 switch (test) {
727 case NODE_TEST_NONE:
728 fprintf(output, "'none' "); break;
729 case NODE_TEST_TYPE:
730 fprintf(output, "'type' "); break;
731 case NODE_TEST_PI:
732 fprintf(output, "'PI' "); break;
733 case NODE_TEST_ALL:
734 fprintf(output, "'all' "); break;
735 case NODE_TEST_NS:
736 fprintf(output, "'namespace' "); break;
737 case NODE_TEST_NAME:
738 fprintf(output, "'name' "); break;
739 }
740 switch (type) {
741 case NODE_TYPE_NODE:
742 fprintf(output, "'node' "); break;
743 case NODE_TYPE_COMMENT:
744 fprintf(output, "'comment' "); break;
745 case NODE_TYPE_TEXT:
746 fprintf(output, "'text' "); break;
747 case NODE_TYPE_PI:
748 fprintf(output, "'PI' "); break;
749 }
750 if (prefix != NULL)
751 fprintf(output, "%s:", prefix);
752 if (name != NULL)
753 fprintf(output, "%s", name);
754 break;
755
756 }
757 case XPATH_OP_VALUE: {
758 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
759
760 fprintf(output, "ELEM ");
761 xmlXPathDebugDumpObject(output, object, 0);
762 goto finish;
763 }
764 case XPATH_OP_VARIABLE: {
765 const xmlChar *prefix = op->value5;
766 const xmlChar *name = op->value4;
767
768 if (prefix != NULL)
769 fprintf(output, "VARIABLE %s:%s", prefix, name);
770 else
771 fprintf(output, "VARIABLE %s", name);
772 break;
773 }
774 case XPATH_OP_FUNCTION: {
775 int nbargs = op->value;
776 const xmlChar *prefix = op->value5;
777 const xmlChar *name = op->value4;
778
779 if (prefix != NULL)
780 fprintf(output, "FUNCTION %s:%s(%d args)",
781 prefix, name, nbargs);
782 else
783 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
784 break;
785 }
786 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
787 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000788 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +0000789#ifdef LIBXML_XPTR_ENABLED
790 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
791#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000792 default:
793 fprintf(output, "UNKNOWN %d\n", op->op); return;
794 }
795 fprintf(output, "\n");
796finish:
797 if (op->ch1 >= 0)
798 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
799 if (op->ch2 >= 0)
800 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
801}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000802
803void
804xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
805 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000806 int i;
807 char shift[100];
808
809 for (i = 0;((i < depth) && (i < 25));i++)
810 shift[2 * i] = shift[2 * i + 1] = ' ';
811 shift[2 * i] = shift[2 * i + 1] = 0;
812
813 fprintf(output, shift);
814
815 if (comp == NULL) {
816 fprintf(output, "Compiled Expression is NULL\n");
817 return;
818 }
819 fprintf(output, "Compiled Expression : %d elements\n",
820 comp->nbStep);
821 i = comp->last;
822 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
823}
Owen Taylor3473f882001-02-23 17:55:21 +0000824#endif
825
826/************************************************************************
827 * *
828 * Parser stacks related functions and macros *
829 * *
830 ************************************************************************/
831
832/*
833 * Generic function for accessing stacks in the Parser Context
834 */
835
836#define PUSH_AND_POP(type, name) \
837extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
838 if (ctxt->name##Nr >= ctxt->name##Max) { \
839 ctxt->name##Max *= 2; \
840 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
841 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
842 if (ctxt->name##Tab == NULL) { \
843 xmlGenericError(xmlGenericErrorContext, \
844 "realloc failed !\n"); \
845 return(0); \
846 } \
847 } \
848 ctxt->name##Tab[ctxt->name##Nr] = value; \
849 ctxt->name = value; \
850 return(ctxt->name##Nr++); \
851} \
852extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
853 type ret; \
854 if (ctxt->name##Nr <= 0) return(0); \
855 ctxt->name##Nr--; \
856 if (ctxt->name##Nr > 0) \
857 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
858 else \
859 ctxt->name = NULL; \
860 ret = ctxt->name##Tab[ctxt->name##Nr]; \
861 ctxt->name##Tab[ctxt->name##Nr] = 0; \
862 return(ret); \
863} \
864
865PUSH_AND_POP(xmlXPathObjectPtr, value)
866
867/*
868 * Macros for accessing the content. Those should be used only by the parser,
869 * and not exported.
870 *
871 * Dirty macros, i.e. one need to make assumption on the context to use them
872 *
873 * CUR_PTR return the current pointer to the xmlChar to be parsed.
874 * CUR returns the current xmlChar value, i.e. a 8 bit value
875 * in ISO-Latin or UTF-8.
876 * This should be used internally by the parser
877 * only to compare to ASCII values otherwise it would break when
878 * running with UTF-8 encoding.
879 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
880 * to compare on ASCII based substring.
881 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
882 * strings within the parser.
883 * CURRENT Returns the current char value, with the full decoding of
884 * UTF-8 if we are using this mode. It returns an int.
885 * NEXT Skip to the next character, this does the proper decoding
886 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
887 * It returns the pointer to the current xmlChar.
888 */
889
890#define CUR (*ctxt->cur)
891#define SKIP(val) ctxt->cur += (val)
892#define NXT(val) ctxt->cur[(val)]
893#define CUR_PTR ctxt->cur
Daniel Veillard61d80a22001-04-27 17:13:01 +0000894#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
895
896#define COPY_BUF(l,b,i,v) \
897 if (l == 1) b[i++] = (xmlChar) v; \
898 else i += xmlCopyChar(l,&b[i],v)
899
900#define NEXTL(l) ctxt->cur += l
Owen Taylor3473f882001-02-23 17:55:21 +0000901
902#define SKIP_BLANKS \
903 while (IS_BLANK(*(ctxt->cur))) NEXT
904
905#define CURRENT (*ctxt->cur)
906#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
907
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000908
909#ifndef DBL_DIG
910#define DBL_DIG 16
911#endif
912#ifndef DBL_EPSILON
913#define DBL_EPSILON 1E-9
914#endif
915
916#define UPPER_DOUBLE 1E9
917#define LOWER_DOUBLE 1E-5
918
919#define INTEGER_DIGITS DBL_DIG
920#define FRACTION_DIGITS (DBL_DIG + 1)
921#define EXPONENT_DIGITS (3 + 2)
922
923/**
924 * xmlXPathFormatNumber:
925 * @number: number to format
926 * @buffer: output buffer
927 * @buffersize: size of output buffer
928 *
929 * Convert the number into a string representation.
930 */
931static void
932xmlXPathFormatNumber(double number, char buffer[], int buffersize)
933{
934 switch (isinf(number)) {
935 case 1:
936 if (buffersize > (int)sizeof("+Infinity"))
937 sprintf(buffer, "+Infinity");
938 break;
939 case -1:
940 if (buffersize > (int)sizeof("-Infinity"))
941 sprintf(buffer, "-Infinity");
942 break;
943 default:
944 if (isnan(number)) {
945 if (buffersize > (int)sizeof("NaN"))
946 sprintf(buffer, "NaN");
947 } else {
Bjorn Reese70a9da52001-04-21 16:57:29 +0000948 /* 3 is sign, decimal point, and terminating zero */
949 char work[DBL_DIG + EXPONENT_DIGITS + 3];
950 int integer_place, fraction_place;
951 char *ptr;
952 char *after_fraction;
953 double absolute_value;
954 int size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000955
Bjorn Reese70a9da52001-04-21 16:57:29 +0000956 absolute_value = fabs(number);
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000957
Bjorn Reese70a9da52001-04-21 16:57:29 +0000958 /*
959 * First choose format - scientific or regular floating point.
960 * In either case, result is in work, and after_fraction points
961 * just past the fractional part.
962 */
963 if ( ((absolute_value > UPPER_DOUBLE) ||
964 (absolute_value < LOWER_DOUBLE)) &&
965 (absolute_value != 0.0) ) {
966 /* Use scientific notation */
967 integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
968 fraction_place = DBL_DIG - 1;
969 snprintf(work, sizeof(work),"%*.*e",
970 integer_place, fraction_place, number);
971 after_fraction = strchr(work + DBL_DIG, 'e');
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000972 }
Bjorn Reese70a9da52001-04-21 16:57:29 +0000973 else {
974 /* Use regular notation */
975 integer_place = 1 + (int)log10(absolute_value);
976 fraction_place = (integer_place > 0)
977 ? DBL_DIG - integer_place
978 : DBL_DIG;
979 size = snprintf(work, sizeof(work), "%0.*f",
980 fraction_place, number);
981 after_fraction = work + size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000982 }
983
Bjorn Reese70a9da52001-04-21 16:57:29 +0000984 /* Remove fractional trailing zeroes */
985 ptr = after_fraction;
986 while (*(--ptr) == '0')
987 ;
988 if (*ptr != '.')
989 ptr++;
990 strcpy(ptr, after_fraction);
991
992 /* Finally copy result back to caller */
993 size = strlen(work) + 1;
994 if (size > buffersize) {
995 work[buffersize - 1] = 0;
996 size = buffersize;
997 }
998 memcpy(buffer, work, size);
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000999 }
1000 break;
1001 }
1002}
1003
Owen Taylor3473f882001-02-23 17:55:21 +00001004/************************************************************************
1005 * *
1006 * Error handling routines *
1007 * *
1008 ************************************************************************/
1009
1010
1011const char *xmlXPathErrorMessages[] = {
1012 "Ok",
1013 "Number encoding",
1014 "Unfinished litteral",
1015 "Start of litteral",
1016 "Expected $ for variable reference",
1017 "Undefined variable",
1018 "Invalid predicate",
1019 "Invalid expression",
1020 "Missing closing curly brace",
1021 "Unregistered function",
1022 "Invalid operand",
1023 "Invalid type",
1024 "Invalid number of arguments",
1025 "Invalid context size",
1026 "Invalid context position",
1027 "Memory allocation error",
1028 "Syntax error",
1029 "Resource error",
1030 "Sub resource error",
Daniel Veillard61d80a22001-04-27 17:13:01 +00001031 "Undefined namespace prefix",
1032 "Encoding error",
1033 "Char out of XML range"
Owen Taylor3473f882001-02-23 17:55:21 +00001034};
1035
1036/**
1037 * xmlXPathError:
1038 * @ctxt: the XPath Parser context
1039 * @file: the file name
1040 * @line: the line number
1041 * @no: the error number
1042 *
1043 * Create a new xmlNodeSetPtr of type double and of value @val
1044 *
1045 * Returns the newly created object.
1046 */
1047void
1048xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1049 int line, int no) {
1050 int n;
1051 const xmlChar *cur;
1052 const xmlChar *base;
1053
1054 xmlGenericError(xmlGenericErrorContext,
1055 "Error %s:%d: %s\n", file, line,
1056 xmlXPathErrorMessages[no]);
1057
1058 cur = ctxt->cur;
1059 base = ctxt->base;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001060 if ((cur == NULL) || (base == NULL))
1061 return;
1062
Owen Taylor3473f882001-02-23 17:55:21 +00001063 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1064 cur--;
1065 }
1066 n = 0;
1067 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1068 cur--;
1069 if ((*cur == '\n') || (*cur == '\r')) cur++;
1070 base = cur;
1071 n = 0;
1072 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1073 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1074 n++;
1075 }
1076 xmlGenericError(xmlGenericErrorContext, "\n");
1077 cur = ctxt->cur;
1078 while ((*cur == '\n') || (*cur == '\r'))
1079 cur--;
1080 n = 0;
1081 while ((cur != base) && (n++ < 80)) {
1082 xmlGenericError(xmlGenericErrorContext, " ");
1083 base++;
1084 }
1085 xmlGenericError(xmlGenericErrorContext,"^\n");
1086}
1087
1088
1089/************************************************************************
1090 * *
1091 * Routines to handle NodeSets *
1092 * *
1093 ************************************************************************/
1094
1095/**
1096 * xmlXPathCmpNodes:
1097 * @node1: the first node
1098 * @node2: the second node
1099 *
1100 * Compare two nodes w.r.t document order
1101 *
1102 * Returns -2 in case of error 1 if first point < second point, 0 if
1103 * that's the same node, -1 otherwise
1104 */
1105int
1106xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1107 int depth1, depth2;
1108 xmlNodePtr cur, root;
1109
1110 if ((node1 == NULL) || (node2 == NULL))
1111 return(-2);
1112 /*
1113 * a couple of optimizations which will avoid computations in most cases
1114 */
1115 if (node1 == node2)
1116 return(0);
Daniel Veillardb33c2012001-04-25 12:59:04 +00001117 if ((node1->type == XML_NAMESPACE_DECL) ||
1118 (node2->type == XML_NAMESPACE_DECL))
1119 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00001120 if (node1 == node2->prev)
1121 return(1);
1122 if (node1 == node2->next)
1123 return(-1);
1124
1125 /*
1126 * compute depth to root
1127 */
1128 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1129 if (cur == node1)
1130 return(1);
1131 depth2++;
1132 }
1133 root = cur;
1134 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1135 if (cur == node2)
1136 return(-1);
1137 depth1++;
1138 }
1139 /*
1140 * Distinct document (or distinct entities :-( ) case.
1141 */
1142 if (root != cur) {
1143 return(-2);
1144 }
1145 /*
1146 * get the nearest common ancestor.
1147 */
1148 while (depth1 > depth2) {
1149 depth1--;
1150 node1 = node1->parent;
1151 }
1152 while (depth2 > depth1) {
1153 depth2--;
1154 node2 = node2->parent;
1155 }
1156 while (node1->parent != node2->parent) {
1157 node1 = node1->parent;
1158 node2 = node2->parent;
1159 /* should not happen but just in case ... */
1160 if ((node1 == NULL) || (node2 == NULL))
1161 return(-2);
1162 }
1163 /*
1164 * Find who's first.
1165 */
1166 if (node1 == node2->next)
1167 return(-1);
1168 for (cur = node1->next;cur != NULL;cur = cur->next)
1169 if (cur == node2)
1170 return(1);
1171 return(-1); /* assume there is no sibling list corruption */
1172}
1173
1174/**
1175 * xmlXPathNodeSetSort:
1176 * @set: the node set
1177 *
1178 * Sort the node set in document order
1179 */
1180void
1181xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001182 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001183 xmlNodePtr tmp;
1184
1185 if (set == NULL)
1186 return;
1187
1188 /* Use Shell's sort to sort the node-set */
1189 len = set->nodeNr;
1190 for (incr = len / 2; incr > 0; incr /= 2) {
1191 for (i = incr; i < len; i++) {
1192 j = i - incr;
1193 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001194 if (xmlXPathCmpNodes(set->nodeTab[j],
1195 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001196 tmp = set->nodeTab[j];
1197 set->nodeTab[j] = set->nodeTab[j + incr];
1198 set->nodeTab[j + incr] = tmp;
1199 j -= incr;
1200 } else
1201 break;
1202 }
1203 }
1204 }
1205}
1206
1207#define XML_NODESET_DEFAULT 10
1208/**
1209 * xmlXPathNodeSetCreate:
1210 * @val: an initial xmlNodePtr, or NULL
1211 *
1212 * Create a new xmlNodeSetPtr of type double and of value @val
1213 *
1214 * Returns the newly created object.
1215 */
1216xmlNodeSetPtr
1217xmlXPathNodeSetCreate(xmlNodePtr val) {
1218 xmlNodeSetPtr ret;
1219
1220 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1221 if (ret == NULL) {
1222 xmlGenericError(xmlGenericErrorContext,
1223 "xmlXPathNewNodeSet: out of memory\n");
1224 return(NULL);
1225 }
1226 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1227 if (val != NULL) {
1228 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1229 sizeof(xmlNodePtr));
1230 if (ret->nodeTab == NULL) {
1231 xmlGenericError(xmlGenericErrorContext,
1232 "xmlXPathNewNodeSet: out of memory\n");
1233 return(NULL);
1234 }
1235 memset(ret->nodeTab, 0 ,
1236 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1237 ret->nodeMax = XML_NODESET_DEFAULT;
1238 ret->nodeTab[ret->nodeNr++] = val;
1239 }
1240 return(ret);
1241}
1242
1243/**
1244 * xmlXPathNodeSetAdd:
1245 * @cur: the initial node set
1246 * @val: a new xmlNodePtr
1247 *
1248 * add a new xmlNodePtr ot an existing NodeSet
1249 */
1250void
1251xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1252 int i;
1253
1254 if (val == NULL) return;
1255
1256 /*
1257 * check against doublons
1258 */
1259 for (i = 0;i < cur->nodeNr;i++)
1260 if (cur->nodeTab[i] == val) return;
1261
1262 /*
1263 * grow the nodeTab if needed
1264 */
1265 if (cur->nodeMax == 0) {
1266 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1267 sizeof(xmlNodePtr));
1268 if (cur->nodeTab == NULL) {
1269 xmlGenericError(xmlGenericErrorContext,
1270 "xmlXPathNodeSetAdd: out of memory\n");
1271 return;
1272 }
1273 memset(cur->nodeTab, 0 ,
1274 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1275 cur->nodeMax = XML_NODESET_DEFAULT;
1276 } else if (cur->nodeNr == cur->nodeMax) {
1277 xmlNodePtr *temp;
1278
1279 cur->nodeMax *= 2;
1280 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1281 sizeof(xmlNodePtr));
1282 if (temp == NULL) {
1283 xmlGenericError(xmlGenericErrorContext,
1284 "xmlXPathNodeSetAdd: out of memory\n");
1285 return;
1286 }
1287 cur->nodeTab = temp;
1288 }
1289 cur->nodeTab[cur->nodeNr++] = val;
1290}
1291
1292/**
1293 * xmlXPathNodeSetAddUnique:
1294 * @cur: the initial node set
1295 * @val: a new xmlNodePtr
1296 *
1297 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1298 * when we are sure the node is not already in the set.
1299 */
1300void
1301xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1302 if (val == NULL) return;
1303
1304 /*
1305 * grow the nodeTab if needed
1306 */
1307 if (cur->nodeMax == 0) {
1308 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1309 sizeof(xmlNodePtr));
1310 if (cur->nodeTab == NULL) {
1311 xmlGenericError(xmlGenericErrorContext,
1312 "xmlXPathNodeSetAddUnique: out of memory\n");
1313 return;
1314 }
1315 memset(cur->nodeTab, 0 ,
1316 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1317 cur->nodeMax = XML_NODESET_DEFAULT;
1318 } else if (cur->nodeNr == cur->nodeMax) {
1319 xmlNodePtr *temp;
1320
1321 cur->nodeMax *= 2;
1322 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1323 sizeof(xmlNodePtr));
1324 if (temp == NULL) {
1325 xmlGenericError(xmlGenericErrorContext,
1326 "xmlXPathNodeSetAddUnique: out of memory\n");
1327 return;
1328 }
1329 cur->nodeTab = temp;
1330 }
1331 cur->nodeTab[cur->nodeNr++] = val;
1332}
1333
1334/**
1335 * xmlXPathNodeSetMerge:
1336 * @val1: the first NodeSet or NULL
1337 * @val2: the second NodeSet
1338 *
1339 * Merges two nodesets, all nodes from @val2 are added to @val1
1340 * if @val1 is NULL, a new set is created and copied from @val2
1341 *
1342 * Returns val1 once extended or NULL in case of error.
1343 */
1344xmlNodeSetPtr
1345xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001346 int i, j, initNr, skip;
Owen Taylor3473f882001-02-23 17:55:21 +00001347
1348 if (val2 == NULL) return(val1);
1349 if (val1 == NULL) {
1350 val1 = xmlXPathNodeSetCreate(NULL);
1351 }
1352
1353 initNr = val1->nodeNr;
1354
1355 for (i = 0;i < val2->nodeNr;i++) {
1356 /*
1357 * check against doublons
1358 */
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001359 skip = 0;
1360 for (j = 0; j < initNr; j++) {
1361 if (val1->nodeTab[j] == val2->nodeTab[i]) {
1362 skip = 1;
1363 break;
1364 }
1365 }
1366 if (skip)
1367 continue;
Owen Taylor3473f882001-02-23 17:55:21 +00001368
1369 /*
1370 * grow the nodeTab if needed
1371 */
1372 if (val1->nodeMax == 0) {
1373 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1374 sizeof(xmlNodePtr));
1375 if (val1->nodeTab == NULL) {
1376 xmlGenericError(xmlGenericErrorContext,
1377 "xmlXPathNodeSetMerge: out of memory\n");
1378 return(NULL);
1379 }
1380 memset(val1->nodeTab, 0 ,
1381 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1382 val1->nodeMax = XML_NODESET_DEFAULT;
1383 } else if (val1->nodeNr == val1->nodeMax) {
1384 xmlNodePtr *temp;
1385
1386 val1->nodeMax *= 2;
1387 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1388 sizeof(xmlNodePtr));
1389 if (temp == NULL) {
1390 xmlGenericError(xmlGenericErrorContext,
1391 "xmlXPathNodeSetMerge: out of memory\n");
1392 return(NULL);
1393 }
1394 val1->nodeTab = temp;
1395 }
1396 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1397 }
1398
1399 return(val1);
1400}
1401
1402/**
1403 * xmlXPathNodeSetDel:
1404 * @cur: the initial node set
1405 * @val: an xmlNodePtr
1406 *
1407 * Removes an xmlNodePtr from an existing NodeSet
1408 */
1409void
1410xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1411 int i;
1412
1413 if (cur == NULL) return;
1414 if (val == NULL) return;
1415
1416 /*
1417 * check against doublons
1418 */
1419 for (i = 0;i < cur->nodeNr;i++)
1420 if (cur->nodeTab[i] == val) break;
1421
1422 if (i >= cur->nodeNr) {
1423#ifdef DEBUG
1424 xmlGenericError(xmlGenericErrorContext,
1425 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1426 val->name);
1427#endif
1428 return;
1429 }
1430 cur->nodeNr--;
1431 for (;i < cur->nodeNr;i++)
1432 cur->nodeTab[i] = cur->nodeTab[i + 1];
1433 cur->nodeTab[cur->nodeNr] = NULL;
1434}
1435
1436/**
1437 * xmlXPathNodeSetRemove:
1438 * @cur: the initial node set
1439 * @val: the index to remove
1440 *
1441 * Removes an entry from an existing NodeSet list.
1442 */
1443void
1444xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1445 if (cur == NULL) return;
1446 if (val >= cur->nodeNr) return;
1447 cur->nodeNr--;
1448 for (;val < cur->nodeNr;val++)
1449 cur->nodeTab[val] = cur->nodeTab[val + 1];
1450 cur->nodeTab[cur->nodeNr] = NULL;
1451}
1452
1453/**
1454 * xmlXPathFreeNodeSet:
1455 * @obj: the xmlNodeSetPtr to free
1456 *
1457 * Free the NodeSet compound (not the actual nodes !).
1458 */
1459void
1460xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1461 if (obj == NULL) return;
1462 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001463 xmlFree(obj->nodeTab);
1464 }
Owen Taylor3473f882001-02-23 17:55:21 +00001465 xmlFree(obj);
1466}
1467
1468/**
1469 * xmlXPathFreeValueTree:
1470 * @obj: the xmlNodeSetPtr to free
1471 *
1472 * Free the NodeSet compound and the actual tree, this is different
1473 * from xmlXPathFreeNodeSet()
1474 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001475static void
Owen Taylor3473f882001-02-23 17:55:21 +00001476xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1477 int i;
1478
1479 if (obj == NULL) return;
1480 for (i = 0;i < obj->nodeNr;i++)
1481 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001482 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001483
1484 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001485 xmlFree(obj->nodeTab);
1486 }
Owen Taylor3473f882001-02-23 17:55:21 +00001487 xmlFree(obj);
1488}
1489
1490#if defined(DEBUG) || defined(DEBUG_STEP)
1491/**
1492 * xmlGenericErrorContextNodeSet:
1493 * @output: a FILE * for the output
1494 * @obj: the xmlNodeSetPtr to free
1495 *
1496 * Quick display of a NodeSet
1497 */
1498void
1499xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1500 int i;
1501
1502 if (output == NULL) output = xmlGenericErrorContext;
1503 if (obj == NULL) {
1504 fprintf(output, "NodeSet == NULL !\n");
1505 return;
1506 }
1507 if (obj->nodeNr == 0) {
1508 fprintf(output, "NodeSet is empty\n");
1509 return;
1510 }
1511 if (obj->nodeTab == NULL) {
1512 fprintf(output, " nodeTab == NULL !\n");
1513 return;
1514 }
1515 for (i = 0; i < obj->nodeNr; i++) {
1516 if (obj->nodeTab[i] == NULL) {
1517 fprintf(output, " NULL !\n");
1518 return;
1519 }
1520 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1521 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1522 fprintf(output, " /");
1523 else if (obj->nodeTab[i]->name == NULL)
1524 fprintf(output, " noname!");
1525 else fprintf(output, " %s", obj->nodeTab[i]->name);
1526 }
1527 fprintf(output, "\n");
1528}
1529#endif
1530
1531/**
1532 * xmlXPathNewNodeSet:
1533 * @val: the NodePtr value
1534 *
1535 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1536 * it with the single Node @val
1537 *
1538 * Returns the newly created object.
1539 */
1540xmlXPathObjectPtr
1541xmlXPathNewNodeSet(xmlNodePtr val) {
1542 xmlXPathObjectPtr ret;
1543
1544 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1545 if (ret == NULL) {
1546 xmlGenericError(xmlGenericErrorContext,
1547 "xmlXPathNewNodeSet: out of memory\n");
1548 return(NULL);
1549 }
1550 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1551 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001552 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001553 ret->nodesetval = xmlXPathNodeSetCreate(val);
1554 return(ret);
1555}
1556
1557/**
1558 * xmlXPathNewValueTree:
1559 * @val: the NodePtr value
1560 *
1561 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1562 * it with the tree root @val
1563 *
1564 * Returns the newly created object.
1565 */
1566xmlXPathObjectPtr
1567xmlXPathNewValueTree(xmlNodePtr val) {
1568 xmlXPathObjectPtr ret;
1569
1570 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1571 if (ret == NULL) {
1572 xmlGenericError(xmlGenericErrorContext,
1573 "xmlXPathNewNodeSet: out of memory\n");
1574 return(NULL);
1575 }
1576 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1577 ret->type = XPATH_XSLT_TREE;
1578 ret->nodesetval = xmlXPathNodeSetCreate(val);
1579 return(ret);
1580}
1581
1582/**
1583 * xmlXPathNewNodeSetList:
1584 * @val: an existing NodeSet
1585 *
1586 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1587 * it with the Nodeset @val
1588 *
1589 * Returns the newly created object.
1590 */
1591xmlXPathObjectPtr
1592xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1593 xmlXPathObjectPtr ret;
1594 int i;
1595
1596 if (val == NULL)
1597 ret = NULL;
1598 else if (val->nodeTab == NULL)
1599 ret = xmlXPathNewNodeSet(NULL);
1600 else
1601 {
1602 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1603 for (i = 1; i < val->nodeNr; ++i)
1604 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1605 }
1606
1607 return(ret);
1608}
1609
1610/**
1611 * xmlXPathWrapNodeSet:
1612 * @val: the NodePtr value
1613 *
1614 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1615 *
1616 * Returns the newly created object.
1617 */
1618xmlXPathObjectPtr
1619xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1620 xmlXPathObjectPtr ret;
1621
1622 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1623 if (ret == NULL) {
1624 xmlGenericError(xmlGenericErrorContext,
1625 "xmlXPathWrapNodeSet: out of memory\n");
1626 return(NULL);
1627 }
1628 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1629 ret->type = XPATH_NODESET;
1630 ret->nodesetval = val;
1631 return(ret);
1632}
1633
1634/**
1635 * xmlXPathFreeNodeSetList:
1636 * @obj: an existing NodeSetList object
1637 *
1638 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1639 * the list contrary to xmlXPathFreeObject().
1640 */
1641void
1642xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1643 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001644 xmlFree(obj);
1645}
1646
1647/************************************************************************
1648 * *
1649 * Routines to handle extra functions *
1650 * *
1651 ************************************************************************/
1652
1653/**
1654 * xmlXPathRegisterFunc:
1655 * @ctxt: the XPath context
1656 * @name: the function name
1657 * @f: the function implementation or NULL
1658 *
1659 * Register a new function. If @f is NULL it unregisters the function
1660 *
1661 * Returns 0 in case of success, -1 in case of error
1662 */
1663int
1664xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
1665 xmlXPathFunction f) {
1666 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
1667}
1668
1669/**
1670 * xmlXPathRegisterFuncNS:
1671 * @ctxt: the XPath context
1672 * @name: the function name
1673 * @ns_uri: the function namespace URI
1674 * @f: the function implementation or NULL
1675 *
1676 * Register a new function. If @f is NULL it unregisters the function
1677 *
1678 * Returns 0 in case of success, -1 in case of error
1679 */
1680int
1681xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1682 const xmlChar *ns_uri, xmlXPathFunction f) {
1683 if (ctxt == NULL)
1684 return(-1);
1685 if (name == NULL)
1686 return(-1);
1687
1688 if (ctxt->funcHash == NULL)
1689 ctxt->funcHash = xmlHashCreate(0);
1690 if (ctxt->funcHash == NULL)
1691 return(-1);
1692 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
1693}
1694
1695/**
1696 * xmlXPathFunctionLookup:
1697 * @ctxt: the XPath context
1698 * @name: the function name
1699 *
1700 * Search in the Function array of the context for the given
1701 * function.
1702 *
1703 * Returns the xmlXPathFunction or NULL if not found
1704 */
1705xmlXPathFunction
1706xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1707 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
1708}
1709
1710/**
1711 * xmlXPathFunctionLookupNS:
1712 * @ctxt: the XPath context
1713 * @name: the function name
1714 * @ns_uri: the function namespace URI
1715 *
1716 * Search in the Function array of the context for the given
1717 * function.
1718 *
1719 * Returns the xmlXPathFunction or NULL if not found
1720 */
1721xmlXPathFunction
1722xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1723 const xmlChar *ns_uri) {
1724 if (ctxt == NULL)
1725 return(NULL);
1726 if (ctxt->funcHash == NULL)
1727 return(NULL);
1728 if (name == NULL)
1729 return(NULL);
1730
1731 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
1732}
1733
1734/**
1735 * xmlXPathRegisteredFuncsCleanup:
1736 * @ctxt: the XPath context
1737 *
1738 * Cleanup the XPath context data associated to registered functions
1739 */
1740void
1741xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
1742 if (ctxt == NULL)
1743 return;
1744
1745 xmlHashFree(ctxt->funcHash, NULL);
1746 ctxt->funcHash = NULL;
1747}
1748
1749/************************************************************************
1750 * *
1751 * Routines to handle Variable *
1752 * *
1753 ************************************************************************/
1754
1755/**
1756 * xmlXPathRegisterVariable:
1757 * @ctxt: the XPath context
1758 * @name: the variable name
1759 * @value: the variable value or NULL
1760 *
1761 * Register a new variable value. If @value is NULL it unregisters
1762 * the variable
1763 *
1764 * Returns 0 in case of success, -1 in case of error
1765 */
1766int
1767xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
1768 xmlXPathObjectPtr value) {
1769 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
1770}
1771
1772/**
1773 * xmlXPathRegisterVariableNS:
1774 * @ctxt: the XPath context
1775 * @name: the variable name
1776 * @ns_uri: the variable namespace URI
1777 * @value: the variable value or NULL
1778 *
1779 * Register a new variable value. If @value is NULL it unregisters
1780 * the variable
1781 *
1782 * Returns 0 in case of success, -1 in case of error
1783 */
1784int
1785xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1786 const xmlChar *ns_uri,
1787 xmlXPathObjectPtr value) {
1788 if (ctxt == NULL)
1789 return(-1);
1790 if (name == NULL)
1791 return(-1);
1792
1793 if (ctxt->varHash == NULL)
1794 ctxt->varHash = xmlHashCreate(0);
1795 if (ctxt->varHash == NULL)
1796 return(-1);
1797 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
1798 (void *) value,
1799 (xmlHashDeallocator)xmlXPathFreeObject));
1800}
1801
1802/**
1803 * xmlXPathRegisterVariableLookup:
1804 * @ctxt: the XPath context
1805 * @f: the lookup function
1806 * @data: the lookup data
1807 *
1808 * register an external mechanism to do variable lookup
1809 */
1810void
1811xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
1812 xmlXPathVariableLookupFunc f, void *data) {
1813 if (ctxt == NULL)
1814 return;
1815 ctxt->varLookupFunc = (void *) f;
1816 ctxt->varLookupData = data;
1817}
1818
1819/**
1820 * xmlXPathVariableLookup:
1821 * @ctxt: the XPath context
1822 * @name: the variable name
1823 *
1824 * Search in the Variable array of the context for the given
1825 * variable value.
1826 *
1827 * Returns the value or NULL if not found
1828 */
1829xmlXPathObjectPtr
1830xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1831 if (ctxt == NULL)
1832 return(NULL);
1833
1834 if (ctxt->varLookupFunc != NULL) {
1835 xmlXPathObjectPtr ret;
1836
1837 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1838 (ctxt->varLookupData, name, NULL);
1839 if (ret != NULL) return(ret);
1840 }
1841 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
1842}
1843
1844/**
1845 * xmlXPathVariableLookupNS:
1846 * @ctxt: the XPath context
1847 * @name: the variable name
1848 * @ns_uri: the variable namespace URI
1849 *
1850 * Search in the Variable array of the context for the given
1851 * variable value.
1852 *
1853 * Returns the value or NULL if not found
1854 */
1855xmlXPathObjectPtr
1856xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1857 const xmlChar *ns_uri) {
1858 if (ctxt == NULL)
1859 return(NULL);
1860
1861 if (ctxt->varLookupFunc != NULL) {
1862 xmlXPathObjectPtr ret;
1863
1864 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1865 (ctxt->varLookupData, name, ns_uri);
1866 if (ret != NULL) return(ret);
1867 }
1868
1869 if (ctxt->varHash == NULL)
1870 return(NULL);
1871 if (name == NULL)
1872 return(NULL);
1873
1874 return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
1875}
1876
1877/**
1878 * xmlXPathRegisteredVariablesCleanup:
1879 * @ctxt: the XPath context
1880 *
1881 * Cleanup the XPath context data associated to registered variables
1882 */
1883void
1884xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
1885 if (ctxt == NULL)
1886 return;
1887
1888 xmlHashFree(ctxt->varHash, NULL);
1889 ctxt->varHash = NULL;
1890}
1891
1892/**
1893 * xmlXPathRegisterNs:
1894 * @ctxt: the XPath context
1895 * @prefix: the namespace prefix
1896 * @ns_uri: the namespace name
1897 *
1898 * Register a new namespace. If @ns_uri is NULL it unregisters
1899 * the namespace
1900 *
1901 * Returns 0 in case of success, -1 in case of error
1902 */
1903int
1904xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
1905 const xmlChar *ns_uri) {
1906 if (ctxt == NULL)
1907 return(-1);
1908 if (prefix == NULL)
1909 return(-1);
1910
1911 if (ctxt->nsHash == NULL)
1912 ctxt->nsHash = xmlHashCreate(10);
1913 if (ctxt->nsHash == NULL)
1914 return(-1);
1915 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
1916 (xmlHashDeallocator)xmlFree));
1917}
1918
1919/**
1920 * xmlXPathNsLookup:
1921 * @ctxt: the XPath context
1922 * @prefix: the namespace prefix value
1923 *
1924 * Search in the namespace declaration array of the context for the given
1925 * namespace name associated to the given prefix
1926 *
1927 * Returns the value or NULL if not found
1928 */
1929const xmlChar *
1930xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
1931 if (ctxt == NULL)
1932 return(NULL);
1933 if (prefix == NULL)
1934 return(NULL);
1935
1936#ifdef XML_XML_NAMESPACE
1937 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
1938 return(XML_XML_NAMESPACE);
1939#endif
1940
1941 if (ctxt->nsHash == NULL)
1942 return(NULL);
1943
1944 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
1945}
1946
1947/**
1948 * xmlXPathRegisteredVariablesCleanup:
1949 * @ctxt: the XPath context
1950 *
1951 * Cleanup the XPath context data associated to registered variables
1952 */
1953void
1954xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
1955 if (ctxt == NULL)
1956 return;
1957
1958 xmlHashFree(ctxt->nsHash, NULL);
1959 ctxt->nsHash = NULL;
1960}
1961
1962/************************************************************************
1963 * *
1964 * Routines to handle Values *
1965 * *
1966 ************************************************************************/
1967
1968/* Allocations are terrible, one need to optimize all this !!! */
1969
1970/**
1971 * xmlXPathNewFloat:
1972 * @val: the double value
1973 *
1974 * Create a new xmlXPathObjectPtr of type double and of value @val
1975 *
1976 * Returns the newly created object.
1977 */
1978xmlXPathObjectPtr
1979xmlXPathNewFloat(double val) {
1980 xmlXPathObjectPtr ret;
1981
1982 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1983 if (ret == NULL) {
1984 xmlGenericError(xmlGenericErrorContext,
1985 "xmlXPathNewFloat: out of memory\n");
1986 return(NULL);
1987 }
1988 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1989 ret->type = XPATH_NUMBER;
1990 ret->floatval = val;
1991 return(ret);
1992}
1993
1994/**
1995 * xmlXPathNewBoolean:
1996 * @val: the boolean value
1997 *
1998 * Create a new xmlXPathObjectPtr of type boolean and of value @val
1999 *
2000 * Returns the newly created object.
2001 */
2002xmlXPathObjectPtr
2003xmlXPathNewBoolean(int val) {
2004 xmlXPathObjectPtr ret;
2005
2006 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2007 if (ret == NULL) {
2008 xmlGenericError(xmlGenericErrorContext,
2009 "xmlXPathNewBoolean: out of memory\n");
2010 return(NULL);
2011 }
2012 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2013 ret->type = XPATH_BOOLEAN;
2014 ret->boolval = (val != 0);
2015 return(ret);
2016}
2017
2018/**
2019 * xmlXPathNewString:
2020 * @val: the xmlChar * value
2021 *
2022 * Create a new xmlXPathObjectPtr of type string and of value @val
2023 *
2024 * Returns the newly created object.
2025 */
2026xmlXPathObjectPtr
2027xmlXPathNewString(const xmlChar *val) {
2028 xmlXPathObjectPtr ret;
2029
2030 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2031 if (ret == NULL) {
2032 xmlGenericError(xmlGenericErrorContext,
2033 "xmlXPathNewString: out of memory\n");
2034 return(NULL);
2035 }
2036 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2037 ret->type = XPATH_STRING;
2038 if (val != NULL)
2039 ret->stringval = xmlStrdup(val);
2040 else
2041 ret->stringval = xmlStrdup((const xmlChar *)"");
2042 return(ret);
2043}
2044
2045/**
2046 * xmlXPathNewCString:
2047 * @val: the char * value
2048 *
2049 * Create a new xmlXPathObjectPtr of type string and of value @val
2050 *
2051 * Returns the newly created object.
2052 */
2053xmlXPathObjectPtr
2054xmlXPathNewCString(const char *val) {
2055 xmlXPathObjectPtr ret;
2056
2057 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2058 if (ret == NULL) {
2059 xmlGenericError(xmlGenericErrorContext,
2060 "xmlXPathNewCString: out of memory\n");
2061 return(NULL);
2062 }
2063 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2064 ret->type = XPATH_STRING;
2065 ret->stringval = xmlStrdup(BAD_CAST val);
2066 return(ret);
2067}
2068
2069/**
2070 * xmlXPathObjectCopy:
2071 * @val: the original object
2072 *
2073 * allocate a new copy of a given object
2074 *
2075 * Returns the newly created object.
2076 */
2077xmlXPathObjectPtr
2078xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2079 xmlXPathObjectPtr ret;
2080
2081 if (val == NULL)
2082 return(NULL);
2083
2084 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2085 if (ret == NULL) {
2086 xmlGenericError(xmlGenericErrorContext,
2087 "xmlXPathObjectCopy: out of memory\n");
2088 return(NULL);
2089 }
2090 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2091 switch (val->type) {
2092 case XPATH_BOOLEAN:
2093 case XPATH_NUMBER:
2094 case XPATH_POINT:
2095 case XPATH_RANGE:
2096 break;
2097 case XPATH_STRING:
2098 ret->stringval = xmlStrdup(val->stringval);
2099 break;
2100 case XPATH_XSLT_TREE:
2101 if ((val->nodesetval != NULL) &&
2102 (val->nodesetval->nodeTab != NULL))
2103 ret->nodesetval = xmlXPathNodeSetCreate(
2104 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
2105 else
2106 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
2107 break;
2108 case XPATH_NODESET:
2109 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
2110 break;
2111 case XPATH_LOCATIONSET:
2112#ifdef LIBXML_XPTR_ENABLED
2113 {
2114 xmlLocationSetPtr loc = val->user;
2115 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2116 break;
2117 }
2118#endif
2119 case XPATH_UNDEFINED:
2120 case XPATH_USERS:
2121 xmlGenericError(xmlGenericErrorContext,
2122 "xmlXPathObjectCopy: unsupported type %d\n",
2123 val->type);
2124 break;
2125 }
2126 return(ret);
2127}
2128
2129/**
2130 * xmlXPathFreeObject:
2131 * @obj: the object to free
2132 *
2133 * Free up an xmlXPathObjectPtr object.
2134 */
2135void
2136xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2137 if (obj == NULL) return;
2138 if (obj->type == XPATH_NODESET) {
Daniel Veillard77851712001-02-27 21:54:07 +00002139 if (obj->boolval) {
2140 obj->type = XPATH_XSLT_TREE;
2141 if (obj->nodesetval != NULL)
2142 xmlXPathFreeValueTree(obj->nodesetval);
2143 } else {
2144 if (obj->nodesetval != NULL)
2145 xmlXPathFreeNodeSet(obj->nodesetval);
2146 }
Owen Taylor3473f882001-02-23 17:55:21 +00002147#ifdef LIBXML_XPTR_ENABLED
2148 } else if (obj->type == XPATH_LOCATIONSET) {
2149 if (obj->user != NULL)
2150 xmlXPtrFreeLocationSet(obj->user);
2151#endif
2152 } else if (obj->type == XPATH_STRING) {
2153 if (obj->stringval != NULL)
2154 xmlFree(obj->stringval);
2155 } else if (obj->type == XPATH_XSLT_TREE) {
2156 if (obj->nodesetval != NULL)
2157 xmlXPathFreeValueTree(obj->nodesetval);
2158 }
2159
Owen Taylor3473f882001-02-23 17:55:21 +00002160 xmlFree(obj);
2161}
2162
2163/************************************************************************
2164 * *
2165 * Routines to handle XPath contexts *
2166 * *
2167 ************************************************************************/
2168
2169/**
2170 * xmlXPathNewContext:
2171 * @doc: the XML document
2172 *
2173 * Create a new xmlXPathContext
2174 *
2175 * Returns the xmlXPathContext just allocated.
2176 */
2177xmlXPathContextPtr
2178xmlXPathNewContext(xmlDocPtr doc) {
2179 xmlXPathContextPtr ret;
2180
2181 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
2182 if (ret == NULL) {
2183 xmlGenericError(xmlGenericErrorContext,
2184 "xmlXPathNewContext: out of memory\n");
2185 return(NULL);
2186 }
2187 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
2188 ret->doc = doc;
2189 ret->node = NULL;
2190
2191 ret->varHash = NULL;
2192
2193 ret->nb_types = 0;
2194 ret->max_types = 0;
2195 ret->types = NULL;
2196
2197 ret->funcHash = xmlHashCreate(0);
2198
2199 ret->nb_axis = 0;
2200 ret->max_axis = 0;
2201 ret->axis = NULL;
2202
2203 ret->nsHash = NULL;
2204 ret->user = NULL;
2205
2206 ret->contextSize = -1;
2207 ret->proximityPosition = -1;
2208
2209 xmlXPathRegisterAllFunctions(ret);
2210
2211 return(ret);
2212}
2213
2214/**
2215 * xmlXPathFreeContext:
2216 * @ctxt: the context to free
2217 *
2218 * Free up an xmlXPathContext
2219 */
2220void
2221xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
2222 xmlXPathRegisteredNsCleanup(ctxt);
2223 xmlXPathRegisteredFuncsCleanup(ctxt);
2224 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00002225 xmlFree(ctxt);
2226}
2227
2228/************************************************************************
2229 * *
2230 * Routines to handle XPath parser contexts *
2231 * *
2232 ************************************************************************/
2233
2234#define CHECK_CTXT(ctxt) \
2235 if (ctxt == NULL) { \
2236 xmlGenericError(xmlGenericErrorContext, \
2237 "%s:%d Internal error: ctxt == NULL\n", \
2238 __FILE__, __LINE__); \
2239 } \
2240
2241
2242#define CHECK_CONTEXT(ctxt) \
2243 if (ctxt == NULL) { \
2244 xmlGenericError(xmlGenericErrorContext, \
2245 "%s:%d Internal error: no context\n", \
2246 __FILE__, __LINE__); \
2247 } \
2248 else if (ctxt->doc == NULL) { \
2249 xmlGenericError(xmlGenericErrorContext, \
2250 "%s:%d Internal error: no document\n", \
2251 __FILE__, __LINE__); \
2252 } \
2253 else if (ctxt->doc->children == NULL) { \
2254 xmlGenericError(xmlGenericErrorContext, \
2255 "%s:%d Internal error: document without root\n", \
2256 __FILE__, __LINE__); \
2257 } \
2258
2259
2260/**
2261 * xmlXPathNewParserContext:
2262 * @str: the XPath expression
2263 * @ctxt: the XPath context
2264 *
2265 * Create a new xmlXPathParserContext
2266 *
2267 * Returns the xmlXPathParserContext just allocated.
2268 */
2269xmlXPathParserContextPtr
2270xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
2271 xmlXPathParserContextPtr ret;
2272
2273 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2274 if (ret == NULL) {
2275 xmlGenericError(xmlGenericErrorContext,
2276 "xmlXPathNewParserContext: out of memory\n");
2277 return(NULL);
2278 }
2279 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2280 ret->cur = ret->base = str;
2281 ret->context = ctxt;
2282
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002283 ret->comp = xmlXPathNewCompExpr();
2284 if (ret->comp == NULL) {
2285 xmlFree(ret->valueTab);
2286 xmlFree(ret);
2287 return(NULL);
2288 }
2289
2290 return(ret);
2291}
2292
2293/**
2294 * xmlXPathCompParserContext:
2295 * @comp: the XPath compiled expression
2296 * @ctxt: the XPath context
2297 *
2298 * Create a new xmlXPathParserContext when processing a compiled expression
2299 *
2300 * Returns the xmlXPathParserContext just allocated.
2301 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002302static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002303xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
2304 xmlXPathParserContextPtr ret;
2305
2306 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2307 if (ret == NULL) {
2308 xmlGenericError(xmlGenericErrorContext,
2309 "xmlXPathNewParserContext: out of memory\n");
2310 return(NULL);
2311 }
2312 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2313
Owen Taylor3473f882001-02-23 17:55:21 +00002314 /* Allocate the value stack */
2315 ret->valueTab = (xmlXPathObjectPtr *)
2316 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002317 if (ret->valueTab == NULL) {
2318 xmlFree(ret);
2319 xmlGenericError(xmlGenericErrorContext,
2320 "xmlXPathNewParserContext: out of memory\n");
2321 return(NULL);
2322 }
Owen Taylor3473f882001-02-23 17:55:21 +00002323 ret->valueNr = 0;
2324 ret->valueMax = 10;
2325 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002326
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00002327 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002328 ret->comp = comp;
2329
Owen Taylor3473f882001-02-23 17:55:21 +00002330 return(ret);
2331}
2332
2333/**
2334 * xmlXPathFreeParserContext:
2335 * @ctxt: the context to free
2336 *
2337 * Free up an xmlXPathParserContext
2338 */
2339void
2340xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
2341 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002342 xmlFree(ctxt->valueTab);
2343 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002344 if (ctxt->comp)
2345 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00002346 xmlFree(ctxt);
2347}
2348
2349/************************************************************************
2350 * *
2351 * The implicit core function library *
2352 * *
2353 ************************************************************************/
2354
2355/*
2356 * Auto-pop and cast to a number
2357 */
2358void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
2359
2360
2361#define POP_FLOAT \
2362 arg = valuePop(ctxt); \
2363 if (arg == NULL) { \
2364 XP_ERROR(XPATH_INVALID_OPERAND); \
2365 } \
2366 if (arg->type != XPATH_NUMBER) { \
2367 valuePush(ctxt, arg); \
2368 xmlXPathNumberFunction(ctxt, 1); \
2369 arg = valuePop(ctxt); \
2370 }
2371
2372/**
2373 * xmlXPathCompareNodeSetFloat:
2374 * @ctxt: the XPath Parser context
2375 * @inf: less than (1) or greater than (0)
2376 * @strict: is the comparison strict
2377 * @arg: the node set
2378 * @f: the value
2379 *
2380 * Implement the compare operation between a nodeset and a number
2381 * @ns < @val (1, 1, ...
2382 * @ns <= @val (1, 0, ...
2383 * @ns > @val (0, 1, ...
2384 * @ns >= @val (0, 0, ...
2385 *
2386 * If one object to be compared is a node-set and the other is a number,
2387 * then the comparison will be true if and only if there is a node in the
2388 * node-set such that the result of performing the comparison on the number
2389 * to be compared and on the result of converting the string-value of that
2390 * node to a number using the number function is true.
2391 *
2392 * Returns 0 or 1 depending on the results of the test.
2393 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002394static int
Owen Taylor3473f882001-02-23 17:55:21 +00002395xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
2396 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
2397 int i, ret = 0;
2398 xmlNodeSetPtr ns;
2399 xmlChar *str2;
2400
2401 if ((f == NULL) || (arg == NULL) ||
2402 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2403 xmlXPathFreeObject(arg);
2404 xmlXPathFreeObject(f);
2405 return(0);
2406 }
2407 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002408 if (ns != NULL) {
2409 for (i = 0;i < ns->nodeNr;i++) {
2410 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2411 if (str2 != NULL) {
2412 valuePush(ctxt,
2413 xmlXPathNewString(str2));
2414 xmlFree(str2);
2415 xmlXPathNumberFunction(ctxt, 1);
2416 valuePush(ctxt, xmlXPathObjectCopy(f));
2417 ret = xmlXPathCompareValues(ctxt, inf, strict);
2418 if (ret)
2419 break;
2420 }
2421 }
Owen Taylor3473f882001-02-23 17:55:21 +00002422 }
2423 xmlXPathFreeObject(arg);
2424 xmlXPathFreeObject(f);
2425 return(ret);
2426}
2427
2428/**
2429 * xmlXPathCompareNodeSetString:
2430 * @ctxt: the XPath Parser context
2431 * @inf: less than (1) or greater than (0)
2432 * @strict: is the comparison strict
2433 * @arg: the node set
2434 * @s: the value
2435 *
2436 * Implement the compare operation between a nodeset and a string
2437 * @ns < @val (1, 1, ...
2438 * @ns <= @val (1, 0, ...
2439 * @ns > @val (0, 1, ...
2440 * @ns >= @val (0, 0, ...
2441 *
2442 * If one object to be compared is a node-set and the other is a string,
2443 * then the comparison will be true if and only if there is a node in
2444 * the node-set such that the result of performing the comparison on the
2445 * string-value of the node and the other string is true.
2446 *
2447 * Returns 0 or 1 depending on the results of the test.
2448 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002449static int
Owen Taylor3473f882001-02-23 17:55:21 +00002450xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
2451 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
2452 int i, ret = 0;
2453 xmlNodeSetPtr ns;
2454 xmlChar *str2;
2455
2456 if ((s == NULL) || (arg == NULL) ||
2457 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2458 xmlXPathFreeObject(arg);
2459 xmlXPathFreeObject(s);
2460 return(0);
2461 }
2462 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002463 if (ns != NULL) {
2464 for (i = 0;i < ns->nodeNr;i++) {
2465 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2466 if (str2 != NULL) {
2467 valuePush(ctxt,
2468 xmlXPathNewString(str2));
2469 xmlFree(str2);
2470 valuePush(ctxt, xmlXPathObjectCopy(s));
2471 ret = xmlXPathCompareValues(ctxt, inf, strict);
2472 if (ret)
2473 break;
2474 }
2475 }
Owen Taylor3473f882001-02-23 17:55:21 +00002476 }
2477 xmlXPathFreeObject(arg);
2478 xmlXPathFreeObject(s);
2479 return(ret);
2480}
2481
2482/**
2483 * xmlXPathCompareNodeSets:
Owen Taylor3473f882001-02-23 17:55:21 +00002484 * @op: less than (-1), equal (0) or greater than (1)
2485 * @strict: is the comparison strict
2486 * @arg1: the fist node set object
2487 * @arg2: the second node set object
2488 *
2489 * Implement the compare operation on nodesets:
2490 *
2491 * If both objects to be compared are node-sets, then the comparison
2492 * will be true if and only if there is a node in the first node-set
2493 * and a node in the second node-set such that the result of performing
2494 * the comparison on the string-values of the two nodes is true.
2495 * ....
2496 * When neither object to be compared is a node-set and the operator
2497 * is <=, <, >= or >, then the objects are compared by converting both
2498 * objects to numbers and comparing the numbers according to IEEE 754.
2499 * ....
2500 * The number function converts its argument to a number as follows:
2501 * - a string that consists of optional whitespace followed by an
2502 * optional minus sign followed by a Number followed by whitespace
2503 * is converted to the IEEE 754 number that is nearest (according
2504 * to the IEEE 754 round-to-nearest rule) to the mathematical value
2505 * represented by the string; any other string is converted to NaN
2506 *
2507 * Conclusion all nodes need to be converted first to their string value
2508 * and then the comparison must be done when possible
2509 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002510static int
2511xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00002512 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2513 int i, j, init = 0;
2514 double val1;
2515 double *values2;
2516 int ret = 0;
2517 xmlChar *str;
2518 xmlNodeSetPtr ns1;
2519 xmlNodeSetPtr ns2;
2520
2521 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002522 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
2523 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002524 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002525 }
Owen Taylor3473f882001-02-23 17:55:21 +00002526 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002527 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
2528 xmlXPathFreeObject(arg1);
2529 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002530 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002531 }
Owen Taylor3473f882001-02-23 17:55:21 +00002532
2533 ns1 = arg1->nodesetval;
2534 ns2 = arg2->nodesetval;
2535
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002536 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002537 xmlXPathFreeObject(arg1);
2538 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002539 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002540 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002541 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002542 xmlXPathFreeObject(arg1);
2543 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002544 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002545 }
Owen Taylor3473f882001-02-23 17:55:21 +00002546
2547 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
2548 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002549 xmlXPathFreeObject(arg1);
2550 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002551 return(0);
2552 }
2553 for (i = 0;i < ns1->nodeNr;i++) {
2554 str = xmlNodeGetContent(ns1->nodeTab[i]);
2555 if (str == NULL)
2556 continue;
2557 val1 = xmlXPathStringEvalNumber(str);
2558 xmlFree(str);
2559 if (isnan(val1))
2560 continue;
2561 for (j = 0;j < ns2->nodeNr;j++) {
2562 if (init == 0) {
2563 str = xmlNodeGetContent(ns2->nodeTab[j]);
2564 if (str == NULL) {
2565 values2[j] = xmlXPathNAN;
2566 } else {
2567 values2[j] = xmlXPathStringEvalNumber(str);
2568 xmlFree(str);
2569 }
2570 }
2571 if (isnan(values2[j]))
2572 continue;
2573 if (inf && strict)
2574 ret = (val1 < values2[j]);
2575 else if (inf && !strict)
2576 ret = (val1 <= values2[j]);
2577 else if (!inf && strict)
2578 ret = (val1 > values2[j]);
2579 else if (!inf && !strict)
2580 ret = (val1 >= values2[j]);
2581 if (ret)
2582 break;
2583 }
2584 if (ret)
2585 break;
2586 init = 1;
2587 }
2588 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002589 xmlXPathFreeObject(arg1);
2590 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002591 return(ret);
2592 return(0);
2593}
2594
2595/**
2596 * xmlXPathCompareNodeSetValue:
2597 * @ctxt: the XPath Parser context
2598 * @inf: less than (1) or greater than (0)
2599 * @strict: is the comparison strict
2600 * @arg: the node set
2601 * @val: the value
2602 *
2603 * Implement the compare operation between a nodeset and a value
2604 * @ns < @val (1, 1, ...
2605 * @ns <= @val (1, 0, ...
2606 * @ns > @val (0, 1, ...
2607 * @ns >= @val (0, 0, ...
2608 *
2609 * If one object to be compared is a node-set and the other is a boolean,
2610 * then the comparison will be true if and only if the result of performing
2611 * the comparison on the boolean and on the result of converting
2612 * the node-set to a boolean using the boolean function is true.
2613 *
2614 * Returns 0 or 1 depending on the results of the test.
2615 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002616static int
Owen Taylor3473f882001-02-23 17:55:21 +00002617xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
2618 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
2619 if ((val == NULL) || (arg == NULL) ||
2620 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2621 return(0);
2622
2623 switch(val->type) {
2624 case XPATH_NUMBER:
2625 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
2626 case XPATH_NODESET:
2627 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002628 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00002629 case XPATH_STRING:
2630 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
2631 case XPATH_BOOLEAN:
2632 valuePush(ctxt, arg);
2633 xmlXPathBooleanFunction(ctxt, 1);
2634 valuePush(ctxt, val);
2635 return(xmlXPathCompareValues(ctxt, inf, strict));
2636 default:
2637 TODO
2638 return(0);
2639 }
2640 return(0);
2641}
2642
2643/**
2644 * xmlXPathEqualNodeSetString
2645 * @arg: the nodeset object argument
2646 * @str: the string to compare to.
2647 *
2648 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2649 * If one object to be compared is a node-set and the other is a string,
2650 * then the comparison will be true if and only if there is a node in
2651 * the node-set such that the result of performing the comparison on the
2652 * string-value of the node and the other string is true.
2653 *
2654 * Returns 0 or 1 depending on the results of the test.
2655 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002656static int
Owen Taylor3473f882001-02-23 17:55:21 +00002657xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
2658 int i;
2659 xmlNodeSetPtr ns;
2660 xmlChar *str2;
2661
2662 if ((str == NULL) || (arg == NULL) ||
2663 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2664 return(0);
2665 ns = arg->nodesetval;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002666 if (ns == NULL)
2667 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002668 if (ns->nodeNr <= 0)
2669 return(0);
2670 for (i = 0;i < ns->nodeNr;i++) {
2671 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2672 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
2673 xmlFree(str2);
2674 return(1);
2675 }
2676 if (str2 != NULL)
2677 xmlFree(str2);
2678 }
2679 return(0);
2680}
2681
2682/**
2683 * xmlXPathEqualNodeSetFloat
2684 * @arg: the nodeset object argument
2685 * @f: the float to compare to
2686 *
2687 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2688 * If one object to be compared is a node-set and the other is a number,
2689 * then the comparison will be true if and only if there is a node in
2690 * the node-set such that the result of performing the comparison on the
2691 * number to be compared and on the result of converting the string-value
2692 * of that node to a number using the number function is true.
2693 *
2694 * Returns 0 or 1 depending on the results of the test.
2695 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002696static int
Owen Taylor3473f882001-02-23 17:55:21 +00002697xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
2698 char buf[100] = "";
2699
2700 if ((arg == NULL) ||
2701 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2702 return(0);
2703
Bjorn Reesee1dc0112001-03-03 12:09:03 +00002704 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00002705 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
2706}
2707
2708
2709/**
2710 * xmlXPathEqualNodeSets
2711 * @arg1: first nodeset object argument
2712 * @arg2: second nodeset object argument
2713 *
2714 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
2715 * If both objects to be compared are node-sets, then the comparison
2716 * will be true if and only if there is a node in the first node-set and
2717 * a node in the second node-set such that the result of performing the
2718 * comparison on the string-values of the two nodes is true.
2719 *
2720 * (needless to say, this is a costly operation)
2721 *
2722 * Returns 0 or 1 depending on the results of the test.
2723 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002724static int
Owen Taylor3473f882001-02-23 17:55:21 +00002725xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2726 int i, j;
2727 xmlChar **values1;
2728 xmlChar **values2;
2729 int ret = 0;
2730 xmlNodeSetPtr ns1;
2731 xmlNodeSetPtr ns2;
2732
2733 if ((arg1 == NULL) ||
2734 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2735 return(0);
2736 if ((arg2 == NULL) ||
2737 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2738 return(0);
2739
2740 ns1 = arg1->nodesetval;
2741 ns2 = arg2->nodesetval;
2742
Daniel Veillard911f49a2001-04-07 15:39:35 +00002743 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002744 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00002745 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002746 return(0);
2747
2748 /*
2749 * check if there is a node pertaining to both sets
2750 */
2751 for (i = 0;i < ns1->nodeNr;i++)
2752 for (j = 0;j < ns2->nodeNr;j++)
2753 if (ns1->nodeTab[i] == ns2->nodeTab[j])
2754 return(1);
2755
2756 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
2757 if (values1 == NULL)
2758 return(0);
2759 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
2760 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
2761 if (values2 == NULL) {
2762 xmlFree(values1);
2763 return(0);
2764 }
2765 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
2766 for (i = 0;i < ns1->nodeNr;i++) {
2767 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
2768 for (j = 0;j < ns2->nodeNr;j++) {
2769 if (i == 0)
2770 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
2771 ret = xmlStrEqual(values1[i], values2[j]);
2772 if (ret)
2773 break;
2774 }
2775 if (ret)
2776 break;
2777 }
2778 for (i = 0;i < ns1->nodeNr;i++)
2779 if (values1[i] != NULL)
2780 xmlFree(values1[i]);
2781 for (j = 0;j < ns2->nodeNr;j++)
2782 if (values2[j] != NULL)
2783 xmlFree(values2[j]);
2784 xmlFree(values1);
2785 xmlFree(values2);
2786 return(ret);
2787}
2788
2789/**
2790 * xmlXPathEqualValues:
2791 * @ctxt: the XPath Parser context
2792 *
2793 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2794 *
2795 * Returns 0 or 1 depending on the results of the test.
2796 */
2797int
2798xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
2799 xmlXPathObjectPtr arg1, arg2;
2800 int ret = 0;
2801
2802 arg1 = valuePop(ctxt);
2803 if (arg1 == NULL)
2804 XP_ERROR0(XPATH_INVALID_OPERAND);
2805
2806 arg2 = valuePop(ctxt);
2807 if (arg2 == NULL) {
2808 xmlXPathFreeObject(arg1);
2809 XP_ERROR0(XPATH_INVALID_OPERAND);
2810 }
2811
2812 if (arg1 == arg2) {
2813#ifdef DEBUG_EXPR
2814 xmlGenericError(xmlGenericErrorContext,
2815 "Equal: by pointer\n");
2816#endif
2817 return(1);
2818 }
2819
2820 switch (arg1->type) {
2821 case XPATH_UNDEFINED:
2822#ifdef DEBUG_EXPR
2823 xmlGenericError(xmlGenericErrorContext,
2824 "Equal: undefined\n");
2825#endif
2826 break;
2827 case XPATH_XSLT_TREE:
2828 case XPATH_NODESET:
2829 switch (arg2->type) {
2830 case XPATH_UNDEFINED:
2831#ifdef DEBUG_EXPR
2832 xmlGenericError(xmlGenericErrorContext,
2833 "Equal: undefined\n");
2834#endif
2835 break;
2836 case XPATH_XSLT_TREE:
2837 case XPATH_NODESET:
2838 ret = xmlXPathEqualNodeSets(arg1, arg2);
2839 break;
2840 case XPATH_BOOLEAN:
2841 if ((arg1->nodesetval == NULL) ||
2842 (arg1->nodesetval->nodeNr == 0)) ret = 0;
2843 else
2844 ret = 1;
2845 ret = (ret == arg2->boolval);
2846 break;
2847 case XPATH_NUMBER:
2848 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
2849 break;
2850 case XPATH_STRING:
2851 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
2852 break;
2853 case XPATH_USERS:
2854 case XPATH_POINT:
2855 case XPATH_RANGE:
2856 case XPATH_LOCATIONSET:
2857 TODO
2858 break;
2859 }
2860 break;
2861 case XPATH_BOOLEAN:
2862 switch (arg2->type) {
2863 case XPATH_UNDEFINED:
2864#ifdef DEBUG_EXPR
2865 xmlGenericError(xmlGenericErrorContext,
2866 "Equal: undefined\n");
2867#endif
2868 break;
2869 case XPATH_NODESET:
2870 case XPATH_XSLT_TREE:
2871 if ((arg2->nodesetval == NULL) ||
2872 (arg2->nodesetval->nodeNr == 0)) ret = 0;
2873 else
2874 ret = 1;
2875 break;
2876 case XPATH_BOOLEAN:
2877#ifdef DEBUG_EXPR
2878 xmlGenericError(xmlGenericErrorContext,
2879 "Equal: %d boolean %d \n",
2880 arg1->boolval, arg2->boolval);
2881#endif
2882 ret = (arg1->boolval == arg2->boolval);
2883 break;
2884 case XPATH_NUMBER:
2885 if (arg2->floatval) ret = 1;
2886 else ret = 0;
2887 ret = (arg1->boolval == ret);
2888 break;
2889 case XPATH_STRING:
2890 if ((arg2->stringval == NULL) ||
2891 (arg2->stringval[0] == 0)) ret = 0;
2892 else
2893 ret = 1;
2894 ret = (arg1->boolval == ret);
2895 break;
2896 case XPATH_USERS:
2897 case XPATH_POINT:
2898 case XPATH_RANGE:
2899 case XPATH_LOCATIONSET:
2900 TODO
2901 break;
2902 }
2903 break;
2904 case XPATH_NUMBER:
2905 switch (arg2->type) {
2906 case XPATH_UNDEFINED:
2907#ifdef DEBUG_EXPR
2908 xmlGenericError(xmlGenericErrorContext,
2909 "Equal: undefined\n");
2910#endif
2911 break;
2912 case XPATH_NODESET:
2913 case XPATH_XSLT_TREE:
2914 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
2915 break;
2916 case XPATH_BOOLEAN:
2917 if (arg1->floatval) ret = 1;
2918 else ret = 0;
2919 ret = (arg2->boolval == ret);
2920 break;
2921 case XPATH_STRING:
2922 valuePush(ctxt, arg2);
2923 xmlXPathNumberFunction(ctxt, 1);
2924 arg2 = valuePop(ctxt);
2925 /* no break on purpose */
2926 case XPATH_NUMBER:
2927 ret = (arg1->floatval == arg2->floatval);
2928 break;
2929 case XPATH_USERS:
2930 case XPATH_POINT:
2931 case XPATH_RANGE:
2932 case XPATH_LOCATIONSET:
2933 TODO
2934 break;
2935 }
2936 break;
2937 case XPATH_STRING:
2938 switch (arg2->type) {
2939 case XPATH_UNDEFINED:
2940#ifdef DEBUG_EXPR
2941 xmlGenericError(xmlGenericErrorContext,
2942 "Equal: undefined\n");
2943#endif
2944 break;
2945 case XPATH_NODESET:
2946 case XPATH_XSLT_TREE:
2947 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
2948 break;
2949 case XPATH_BOOLEAN:
2950 if ((arg1->stringval == NULL) ||
2951 (arg1->stringval[0] == 0)) ret = 0;
2952 else
2953 ret = 1;
2954 ret = (arg2->boolval == ret);
2955 break;
2956 case XPATH_STRING:
2957 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
2958 break;
2959 case XPATH_NUMBER:
2960 valuePush(ctxt, arg1);
2961 xmlXPathNumberFunction(ctxt, 1);
2962 arg1 = valuePop(ctxt);
2963 ret = (arg1->floatval == arg2->floatval);
2964 break;
2965 case XPATH_USERS:
2966 case XPATH_POINT:
2967 case XPATH_RANGE:
2968 case XPATH_LOCATIONSET:
2969 TODO
2970 break;
2971 }
2972 break;
2973 case XPATH_USERS:
2974 case XPATH_POINT:
2975 case XPATH_RANGE:
2976 case XPATH_LOCATIONSET:
2977 TODO
2978 break;
2979 }
2980 xmlXPathFreeObject(arg1);
2981 xmlXPathFreeObject(arg2);
2982 return(ret);
2983}
2984
2985
2986/**
2987 * xmlXPathCompareValues:
2988 * @ctxt: the XPath Parser context
2989 * @inf: less than (1) or greater than (0)
2990 * @strict: is the comparison strict
2991 *
2992 * Implement the compare operation on XPath objects:
2993 * @arg1 < @arg2 (1, 1, ...
2994 * @arg1 <= @arg2 (1, 0, ...
2995 * @arg1 > @arg2 (0, 1, ...
2996 * @arg1 >= @arg2 (0, 0, ...
2997 *
2998 * When neither object to be compared is a node-set and the operator is
2999 * <=, <, >=, >, then the objects are compared by converted both objects
3000 * to numbers and comparing the numbers according to IEEE 754. The <
3001 * comparison will be true if and only if the first number is less than the
3002 * second number. The <= comparison will be true if and only if the first
3003 * number is less than or equal to the second number. The > comparison
3004 * will be true if and only if the first number is greater than the second
3005 * number. The >= comparison will be true if and only if the first number
3006 * is greater than or equal to the second number.
3007 *
3008 * Returns 1 if the comparaison succeeded, 0 if it failed
3009 */
3010int
3011xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
3012 int ret = 0;
3013 xmlXPathObjectPtr arg1, arg2;
3014
3015 arg2 = valuePop(ctxt);
3016 if (arg2 == NULL) {
3017 XP_ERROR0(XPATH_INVALID_OPERAND);
3018 }
3019
3020 arg1 = valuePop(ctxt);
3021 if (arg1 == NULL) {
3022 xmlXPathFreeObject(arg2);
3023 XP_ERROR0(XPATH_INVALID_OPERAND);
3024 }
3025
3026 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
3027 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003028 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003029 } else {
3030 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003031 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
3032 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003033 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003034 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
3035 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00003036 }
3037 }
3038 return(ret);
3039 }
3040
3041 if (arg1->type != XPATH_NUMBER) {
3042 valuePush(ctxt, arg1);
3043 xmlXPathNumberFunction(ctxt, 1);
3044 arg1 = valuePop(ctxt);
3045 }
3046 if (arg1->type != XPATH_NUMBER) {
3047 xmlXPathFreeObject(arg1);
3048 xmlXPathFreeObject(arg2);
3049 XP_ERROR0(XPATH_INVALID_OPERAND);
3050 }
3051 if (arg2->type != XPATH_NUMBER) {
3052 valuePush(ctxt, arg2);
3053 xmlXPathNumberFunction(ctxt, 1);
3054 arg2 = valuePop(ctxt);
3055 }
3056 if (arg2->type != XPATH_NUMBER) {
3057 xmlXPathFreeObject(arg1);
3058 xmlXPathFreeObject(arg2);
3059 XP_ERROR0(XPATH_INVALID_OPERAND);
3060 }
3061 /*
3062 * Add tests for infinity and nan
3063 * => feedback on 3.4 for Inf and NaN
3064 */
3065 if (inf && strict)
3066 ret = (arg1->floatval < arg2->floatval);
3067 else if (inf && !strict)
3068 ret = (arg1->floatval <= arg2->floatval);
3069 else if (!inf && strict)
3070 ret = (arg1->floatval > arg2->floatval);
3071 else if (!inf && !strict)
3072 ret = (arg1->floatval >= arg2->floatval);
3073 xmlXPathFreeObject(arg1);
3074 xmlXPathFreeObject(arg2);
3075 return(ret);
3076}
3077
3078/**
3079 * xmlXPathValueFlipSign:
3080 * @ctxt: the XPath Parser context
3081 *
3082 * Implement the unary - operation on an XPath object
3083 * The numeric operators convert their operands to numbers as if
3084 * by calling the number function.
3085 */
3086void
3087xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
3088 xmlXPathObjectPtr arg;
3089
3090 POP_FLOAT
3091 arg->floatval = -arg->floatval;
3092 valuePush(ctxt, arg);
3093}
3094
3095/**
3096 * xmlXPathAddValues:
3097 * @ctxt: the XPath Parser context
3098 *
3099 * Implement the add operation on XPath objects:
3100 * The numeric operators convert their operands to numbers as if
3101 * by calling the number function.
3102 */
3103void
3104xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
3105 xmlXPathObjectPtr arg;
3106 double val;
3107
3108 POP_FLOAT
3109 val = arg->floatval;
3110 xmlXPathFreeObject(arg);
3111
3112 POP_FLOAT
3113 arg->floatval += val;
3114 valuePush(ctxt, arg);
3115}
3116
3117/**
3118 * xmlXPathSubValues:
3119 * @ctxt: the XPath Parser context
3120 *
3121 * Implement the substraction operation on XPath objects:
3122 * The numeric operators convert their operands to numbers as if
3123 * by calling the number function.
3124 */
3125void
3126xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
3127 xmlXPathObjectPtr arg;
3128 double val;
3129
3130 POP_FLOAT
3131 val = arg->floatval;
3132 xmlXPathFreeObject(arg);
3133
3134 POP_FLOAT
3135 arg->floatval -= val;
3136 valuePush(ctxt, arg);
3137}
3138
3139/**
3140 * xmlXPathMultValues:
3141 * @ctxt: the XPath Parser context
3142 *
3143 * Implement the multiply operation on XPath objects:
3144 * The numeric operators convert their operands to numbers as if
3145 * by calling the number function.
3146 */
3147void
3148xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
3149 xmlXPathObjectPtr arg;
3150 double val;
3151
3152 POP_FLOAT
3153 val = arg->floatval;
3154 xmlXPathFreeObject(arg);
3155
3156 POP_FLOAT
3157 arg->floatval *= val;
3158 valuePush(ctxt, arg);
3159}
3160
3161/**
3162 * xmlXPathDivValues:
3163 * @ctxt: the XPath Parser context
3164 *
3165 * Implement the div operation on XPath objects @arg1 / @arg2:
3166 * The numeric operators convert their operands to numbers as if
3167 * by calling the number function.
3168 */
3169void
3170xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
3171 xmlXPathObjectPtr arg;
3172 double val;
3173
3174 POP_FLOAT
3175 val = arg->floatval;
3176 xmlXPathFreeObject(arg);
3177
3178 POP_FLOAT
3179 arg->floatval /= val;
3180 valuePush(ctxt, arg);
3181}
3182
3183/**
3184 * xmlXPathModValues:
3185 * @ctxt: the XPath Parser context
3186 *
3187 * Implement the mod operation on XPath objects: @arg1 / @arg2
3188 * The numeric operators convert their operands to numbers as if
3189 * by calling the number function.
3190 */
3191void
3192xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
3193 xmlXPathObjectPtr arg;
3194 int arg1, arg2;
3195
3196 POP_FLOAT
3197 arg2 = (int) arg->floatval;
3198 xmlXPathFreeObject(arg);
3199
3200 POP_FLOAT
3201 arg1 = (int) arg->floatval;
3202 arg->floatval = arg1 % arg2;
3203 valuePush(ctxt, arg);
3204}
3205
3206/************************************************************************
3207 * *
3208 * The traversal functions *
3209 * *
3210 ************************************************************************/
3211
Owen Taylor3473f882001-02-23 17:55:21 +00003212/*
3213 * A traversal function enumerates nodes along an axis.
3214 * Initially it must be called with NULL, and it indicates
3215 * termination on the axis by returning NULL.
3216 */
3217typedef xmlNodePtr (*xmlXPathTraversalFunction)
3218 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
3219
3220/**
3221 * xmlXPathNextSelf:
3222 * @ctxt: the XPath Parser context
3223 * @cur: the current node in the traversal
3224 *
3225 * Traversal function for the "self" direction
3226 * The self axis contains just the context node itself
3227 *
3228 * Returns the next element following that axis
3229 */
3230xmlNodePtr
3231xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3232 if (cur == NULL)
3233 return(ctxt->context->node);
3234 return(NULL);
3235}
3236
3237/**
3238 * xmlXPathNextChild:
3239 * @ctxt: the XPath Parser context
3240 * @cur: the current node in the traversal
3241 *
3242 * Traversal function for the "child" direction
3243 * The child axis contains the children of the context node in document order.
3244 *
3245 * Returns the next element following that axis
3246 */
3247xmlNodePtr
3248xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3249 if (cur == NULL) {
3250 if (ctxt->context->node == NULL) return(NULL);
3251 switch (ctxt->context->node->type) {
3252 case XML_ELEMENT_NODE:
3253 case XML_TEXT_NODE:
3254 case XML_CDATA_SECTION_NODE:
3255 case XML_ENTITY_REF_NODE:
3256 case XML_ENTITY_NODE:
3257 case XML_PI_NODE:
3258 case XML_COMMENT_NODE:
3259 case XML_NOTATION_NODE:
3260 case XML_DTD_NODE:
3261 return(ctxt->context->node->children);
3262 case XML_DOCUMENT_NODE:
3263 case XML_DOCUMENT_TYPE_NODE:
3264 case XML_DOCUMENT_FRAG_NODE:
3265 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003266#ifdef LIBXML_DOCB_ENABLED
3267 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003268#endif
3269 return(((xmlDocPtr) ctxt->context->node)->children);
3270 case XML_ELEMENT_DECL:
3271 case XML_ATTRIBUTE_DECL:
3272 case XML_ENTITY_DECL:
3273 case XML_ATTRIBUTE_NODE:
3274 case XML_NAMESPACE_DECL:
3275 case XML_XINCLUDE_START:
3276 case XML_XINCLUDE_END:
3277 return(NULL);
3278 }
3279 return(NULL);
3280 }
3281 if ((cur->type == XML_DOCUMENT_NODE) ||
3282 (cur->type == XML_HTML_DOCUMENT_NODE))
3283 return(NULL);
3284 return(cur->next);
3285}
3286
3287/**
3288 * xmlXPathNextDescendant:
3289 * @ctxt: the XPath Parser context
3290 * @cur: the current node in the traversal
3291 *
3292 * Traversal function for the "descendant" direction
3293 * the descendant axis contains the descendants of the context node in document
3294 * order; a descendant is a child or a child of a child and so on.
3295 *
3296 * Returns the next element following that axis
3297 */
3298xmlNodePtr
3299xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3300 if (cur == NULL) {
3301 if (ctxt->context->node == NULL)
3302 return(NULL);
3303 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3304 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3305 return(NULL);
3306
3307 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3308 return(ctxt->context->doc->children);
3309 return(ctxt->context->node->children);
3310 }
3311
3312 if (cur->children != NULL)
3313 {
3314 if (cur->children->type != XML_ENTITY_DECL)
3315 return(cur->children);
3316 }
3317 if (cur->next != NULL) return(cur->next);
3318
3319 do {
3320 cur = cur->parent;
3321 if (cur == NULL) return(NULL);
3322 if (cur == ctxt->context->node) return(NULL);
3323 if (cur->next != NULL) {
3324 cur = cur->next;
3325 return(cur);
3326 }
3327 } while (cur != NULL);
3328 return(cur);
3329}
3330
3331/**
3332 * xmlXPathNextDescendantOrSelf:
3333 * @ctxt: the XPath Parser context
3334 * @cur: the current node in the traversal
3335 *
3336 * Traversal function for the "descendant-or-self" direction
3337 * the descendant-or-self axis contains the context node and the descendants
3338 * of the context node in document order; thus the context node is the first
3339 * node on the axis, and the first child of the context node is the second node
3340 * on the axis
3341 *
3342 * Returns the next element following that axis
3343 */
3344xmlNodePtr
3345xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3346 if (cur == NULL) {
3347 if (ctxt->context->node == NULL)
3348 return(NULL);
3349 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3350 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3351 return(NULL);
3352 return(ctxt->context->node);
3353 }
3354
3355 return(xmlXPathNextDescendant(ctxt, cur));
3356}
3357
3358/**
3359 * xmlXPathNextParent:
3360 * @ctxt: the XPath Parser context
3361 * @cur: the current node in the traversal
3362 *
3363 * Traversal function for the "parent" direction
3364 * The parent axis contains the parent of the context node, if there is one.
3365 *
3366 * Returns the next element following that axis
3367 */
3368xmlNodePtr
3369xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3370 /*
3371 * the parent of an attribute or namespace node is the element
3372 * to which the attribute or namespace node is attached
3373 * Namespace handling !!!
3374 */
3375 if (cur == NULL) {
3376 if (ctxt->context->node == NULL) return(NULL);
3377 switch (ctxt->context->node->type) {
3378 case XML_ELEMENT_NODE:
3379 case XML_TEXT_NODE:
3380 case XML_CDATA_SECTION_NODE:
3381 case XML_ENTITY_REF_NODE:
3382 case XML_ENTITY_NODE:
3383 case XML_PI_NODE:
3384 case XML_COMMENT_NODE:
3385 case XML_NOTATION_NODE:
3386 case XML_DTD_NODE:
3387 case XML_ELEMENT_DECL:
3388 case XML_ATTRIBUTE_DECL:
3389 case XML_XINCLUDE_START:
3390 case XML_XINCLUDE_END:
3391 case XML_ENTITY_DECL:
3392 if (ctxt->context->node->parent == NULL)
3393 return((xmlNodePtr) ctxt->context->doc);
3394 return(ctxt->context->node->parent);
3395 case XML_ATTRIBUTE_NODE: {
3396 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3397
3398 return(att->parent);
3399 }
3400 case XML_DOCUMENT_NODE:
3401 case XML_DOCUMENT_TYPE_NODE:
3402 case XML_DOCUMENT_FRAG_NODE:
3403 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003404#ifdef LIBXML_DOCB_ENABLED
3405 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003406#endif
3407 return(NULL);
3408 case XML_NAMESPACE_DECL:
3409 /*
3410 * TODO !!! may require extending struct _xmlNs with
3411 * parent field
3412 * C.f. Infoset case...
3413 */
3414 return(NULL);
3415 }
3416 }
3417 return(NULL);
3418}
3419
3420/**
3421 * xmlXPathNextAncestor:
3422 * @ctxt: the XPath Parser context
3423 * @cur: the current node in the traversal
3424 *
3425 * Traversal function for the "ancestor" direction
3426 * the ancestor axis contains the ancestors of the context node; the ancestors
3427 * of the context node consist of the parent of context node and the parent's
3428 * parent and so on; the nodes are ordered in reverse document order; thus the
3429 * parent is the first node on the axis, and the parent's parent is the second
3430 * node on the axis
3431 *
3432 * Returns the next element following that axis
3433 */
3434xmlNodePtr
3435xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3436 /*
3437 * the parent of an attribute or namespace node is the element
3438 * to which the attribute or namespace node is attached
3439 * !!!!!!!!!!!!!
3440 */
3441 if (cur == NULL) {
3442 if (ctxt->context->node == NULL) return(NULL);
3443 switch (ctxt->context->node->type) {
3444 case XML_ELEMENT_NODE:
3445 case XML_TEXT_NODE:
3446 case XML_CDATA_SECTION_NODE:
3447 case XML_ENTITY_REF_NODE:
3448 case XML_ENTITY_NODE:
3449 case XML_PI_NODE:
3450 case XML_COMMENT_NODE:
3451 case XML_DTD_NODE:
3452 case XML_ELEMENT_DECL:
3453 case XML_ATTRIBUTE_DECL:
3454 case XML_ENTITY_DECL:
3455 case XML_NOTATION_NODE:
3456 case XML_XINCLUDE_START:
3457 case XML_XINCLUDE_END:
3458 if (ctxt->context->node->parent == NULL)
3459 return((xmlNodePtr) ctxt->context->doc);
3460 return(ctxt->context->node->parent);
3461 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003462 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003463
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003464 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00003465 }
3466 case XML_DOCUMENT_NODE:
3467 case XML_DOCUMENT_TYPE_NODE:
3468 case XML_DOCUMENT_FRAG_NODE:
3469 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003470#ifdef LIBXML_DOCB_ENABLED
3471 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003472#endif
3473 return(NULL);
3474 case XML_NAMESPACE_DECL:
3475 /*
3476 * TODO !!! may require extending struct _xmlNs with
3477 * parent field
3478 * C.f. Infoset case...
3479 */
3480 return(NULL);
3481 }
3482 return(NULL);
3483 }
3484 if (cur == ctxt->context->doc->children)
3485 return((xmlNodePtr) ctxt->context->doc);
3486 if (cur == (xmlNodePtr) ctxt->context->doc)
3487 return(NULL);
3488 switch (cur->type) {
3489 case XML_ELEMENT_NODE:
3490 case XML_TEXT_NODE:
3491 case XML_CDATA_SECTION_NODE:
3492 case XML_ENTITY_REF_NODE:
3493 case XML_ENTITY_NODE:
3494 case XML_PI_NODE:
3495 case XML_COMMENT_NODE:
3496 case XML_NOTATION_NODE:
3497 case XML_DTD_NODE:
3498 case XML_ELEMENT_DECL:
3499 case XML_ATTRIBUTE_DECL:
3500 case XML_ENTITY_DECL:
3501 case XML_XINCLUDE_START:
3502 case XML_XINCLUDE_END:
3503 return(cur->parent);
3504 case XML_ATTRIBUTE_NODE: {
3505 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3506
3507 return(att->parent);
3508 }
3509 case XML_DOCUMENT_NODE:
3510 case XML_DOCUMENT_TYPE_NODE:
3511 case XML_DOCUMENT_FRAG_NODE:
3512 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003513#ifdef LIBXML_DOCB_ENABLED
3514 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003515#endif
3516 return(NULL);
3517 case XML_NAMESPACE_DECL:
3518 /*
3519 * TODO !!! may require extending struct _xmlNs with
3520 * parent field
3521 * C.f. Infoset case...
3522 */
3523 return(NULL);
3524 }
3525 return(NULL);
3526}
3527
3528/**
3529 * xmlXPathNextAncestorOrSelf:
3530 * @ctxt: the XPath Parser context
3531 * @cur: the current node in the traversal
3532 *
3533 * Traversal function for the "ancestor-or-self" direction
3534 * he ancestor-or-self axis contains the context node and ancestors of
3535 * the context node in reverse document order; thus the context node is
3536 * the first node on the axis, and the context node's parent the second;
3537 * parent here is defined the same as with the parent axis.
3538 *
3539 * Returns the next element following that axis
3540 */
3541xmlNodePtr
3542xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3543 if (cur == NULL)
3544 return(ctxt->context->node);
3545 return(xmlXPathNextAncestor(ctxt, cur));
3546}
3547
3548/**
3549 * xmlXPathNextFollowingSibling:
3550 * @ctxt: the XPath Parser context
3551 * @cur: the current node in the traversal
3552 *
3553 * Traversal function for the "following-sibling" direction
3554 * The following-sibling axis contains the following siblings of the context
3555 * node in document order.
3556 *
3557 * Returns the next element following that axis
3558 */
3559xmlNodePtr
3560xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3561 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3562 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3563 return(NULL);
3564 if (cur == (xmlNodePtr) ctxt->context->doc)
3565 return(NULL);
3566 if (cur == NULL)
3567 return(ctxt->context->node->next);
3568 return(cur->next);
3569}
3570
3571/**
3572 * xmlXPathNextPrecedingSibling:
3573 * @ctxt: the XPath Parser context
3574 * @cur: the current node in the traversal
3575 *
3576 * Traversal function for the "preceding-sibling" direction
3577 * The preceding-sibling axis contains the preceding siblings of the context
3578 * node in reverse document order; the first preceding sibling is first on the
3579 * axis; the sibling preceding that node is the second on the axis and so on.
3580 *
3581 * Returns the next element following that axis
3582 */
3583xmlNodePtr
3584xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3585 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3586 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3587 return(NULL);
3588 if (cur == (xmlNodePtr) ctxt->context->doc)
3589 return(NULL);
3590 if (cur == NULL)
3591 return(ctxt->context->node->prev);
3592 return(cur->prev);
3593}
3594
3595/**
3596 * xmlXPathNextFollowing:
3597 * @ctxt: the XPath Parser context
3598 * @cur: the current node in the traversal
3599 *
3600 * Traversal function for the "following" direction
3601 * The following axis contains all nodes in the same document as the context
3602 * node that are after the context node in document order, excluding any
3603 * descendants and excluding attribute nodes and namespace nodes; the nodes
3604 * are ordered in document order
3605 *
3606 * Returns the next element following that axis
3607 */
3608xmlNodePtr
3609xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3610 if (cur != NULL && cur->children != NULL)
3611 return cur->children ;
3612 if (cur == NULL) cur = ctxt->context->node;
3613 if (cur == NULL) return(NULL) ; /* ERROR */
3614 if (cur->next != NULL) return(cur->next) ;
3615 do {
3616 cur = cur->parent;
3617 if (cur == NULL) return(NULL);
3618 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3619 if (cur->next != NULL) return(cur->next);
3620 } while (cur != NULL);
3621 return(cur);
3622}
3623
3624/*
3625 * xmlXPathIsAncestor:
3626 * @ancestor: the ancestor node
3627 * @node: the current node
3628 *
3629 * Check that @ancestor is a @node's ancestor
3630 *
3631 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3632 */
3633static int
3634xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3635 if ((ancestor == NULL) || (node == NULL)) return(0);
3636 /* nodes need to be in the same document */
3637 if (ancestor->doc != node->doc) return(0);
3638 /* avoid searching if ancestor or node is the root node */
3639 if (ancestor == (xmlNodePtr) node->doc) return(1);
3640 if (node == (xmlNodePtr) ancestor->doc) return(0);
3641 while (node->parent != NULL) {
3642 if (node->parent == ancestor)
3643 return(1);
3644 node = node->parent;
3645 }
3646 return(0);
3647}
3648
3649/**
3650 * xmlXPathNextPreceding:
3651 * @ctxt: the XPath Parser context
3652 * @cur: the current node in the traversal
3653 *
3654 * Traversal function for the "preceding" direction
3655 * the preceding axis contains all nodes in the same document as the context
3656 * node that are before the context node in document order, excluding any
3657 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3658 * ordered in reverse document order
3659 *
3660 * Returns the next element following that axis
3661 */
3662xmlNodePtr
3663xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3664 if (cur == NULL)
3665 cur = ctxt->context->node ;
3666 do {
3667 if (cur->prev != NULL) {
3668 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3669 ;
3670 return(cur) ;
3671 }
3672
3673 cur = cur->parent;
3674 if (cur == NULL) return(NULL);
3675 if (cur == ctxt->context->doc->children) return(NULL);
3676 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3677 return(cur);
3678}
3679
3680/**
3681 * xmlXPathNextNamespace:
3682 * @ctxt: the XPath Parser context
3683 * @cur: the current attribute in the traversal
3684 *
3685 * Traversal function for the "namespace" direction
3686 * the namespace axis contains the namespace nodes of the context node;
3687 * the order of nodes on this axis is implementation-defined; the axis will
3688 * be empty unless the context node is an element
3689 *
3690 * Returns the next element following that axis
3691 */
3692xmlNodePtr
3693xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3694 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3695 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3696 if (ctxt->context->namespaces != NULL)
3697 xmlFree(ctxt->context->namespaces);
3698 ctxt->context->namespaces =
3699 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3700 if (ctxt->context->namespaces == NULL) return(NULL);
3701 ctxt->context->nsNr = 0;
3702 }
3703 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3704}
3705
3706/**
3707 * xmlXPathNextAttribute:
3708 * @ctxt: the XPath Parser context
3709 * @cur: the current attribute in the traversal
3710 *
3711 * Traversal function for the "attribute" direction
3712 * TODO: support DTD inherited default attributes
3713 *
3714 * Returns the next element following that axis
3715 */
3716xmlNodePtr
3717xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00003718 if (ctxt->context->node == NULL)
3719 return(NULL);
3720 if (ctxt->context->node->type != XML_ELEMENT_NODE)
3721 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003722 if (cur == NULL) {
3723 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3724 return(NULL);
3725 return((xmlNodePtr)ctxt->context->node->properties);
3726 }
3727 return((xmlNodePtr)cur->next);
3728}
3729
3730/************************************************************************
3731 * *
3732 * NodeTest Functions *
3733 * *
3734 ************************************************************************/
3735
Owen Taylor3473f882001-02-23 17:55:21 +00003736#define IS_FUNCTION 200
3737
Owen Taylor3473f882001-02-23 17:55:21 +00003738
3739/************************************************************************
3740 * *
3741 * Implicit tree core function library *
3742 * *
3743 ************************************************************************/
3744
3745/**
3746 * xmlXPathRoot:
3747 * @ctxt: the XPath Parser context
3748 *
3749 * Initialize the context to the root of the document
3750 */
3751void
3752xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
3753 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
3754 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3755}
3756
3757/************************************************************************
3758 * *
3759 * The explicit core function library *
3760 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
3761 * *
3762 ************************************************************************/
3763
3764
3765/**
3766 * xmlXPathLastFunction:
3767 * @ctxt: the XPath Parser context
3768 * @nargs: the number of arguments
3769 *
3770 * Implement the last() XPath function
3771 * number last()
3772 * The last function returns the number of nodes in the context node list.
3773 */
3774void
3775xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3776 CHECK_ARITY(0);
3777 if (ctxt->context->contextSize >= 0) {
3778 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
3779#ifdef DEBUG_EXPR
3780 xmlGenericError(xmlGenericErrorContext,
3781 "last() : %d\n", ctxt->context->contextSize);
3782#endif
3783 } else {
3784 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
3785 }
3786}
3787
3788/**
3789 * xmlXPathPositionFunction:
3790 * @ctxt: the XPath Parser context
3791 * @nargs: the number of arguments
3792 *
3793 * Implement the position() XPath function
3794 * number position()
3795 * The position function returns the position of the context node in the
3796 * context node list. The first position is 1, and so the last positionr
3797 * will be equal to last().
3798 */
3799void
3800xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3801 CHECK_ARITY(0);
3802 if (ctxt->context->proximityPosition >= 0) {
3803 valuePush(ctxt,
3804 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
3805#ifdef DEBUG_EXPR
3806 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
3807 ctxt->context->proximityPosition);
3808#endif
3809 } else {
3810 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
3811 }
3812}
3813
3814/**
3815 * xmlXPathCountFunction:
3816 * @ctxt: the XPath Parser context
3817 * @nargs: the number of arguments
3818 *
3819 * Implement the count() XPath function
3820 * number count(node-set)
3821 */
3822void
3823xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3824 xmlXPathObjectPtr cur;
3825
3826 CHECK_ARITY(1);
3827 if ((ctxt->value == NULL) ||
3828 ((ctxt->value->type != XPATH_NODESET) &&
3829 (ctxt->value->type != XPATH_XSLT_TREE)))
3830 XP_ERROR(XPATH_INVALID_TYPE);
3831 cur = valuePop(ctxt);
3832
Daniel Veillard911f49a2001-04-07 15:39:35 +00003833 if ((cur == NULL) || (cur->nodesetval == NULL))
3834 valuePush(ctxt, xmlXPathNewFloat((double) 0));
3835 else
3836 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Owen Taylor3473f882001-02-23 17:55:21 +00003837 xmlXPathFreeObject(cur);
3838}
3839
3840/**
3841 * xmlXPathIdFunction:
3842 * @ctxt: the XPath Parser context
3843 * @nargs: the number of arguments
3844 *
3845 * Implement the id() XPath function
3846 * node-set id(object)
3847 * The id function selects elements by their unique ID
3848 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
3849 * then the result is the union of the result of applying id to the
3850 * string value of each of the nodes in the argument node-set. When the
3851 * argument to id is of any other type, the argument is converted to a
3852 * string as if by a call to the string function; the string is split
3853 * into a whitespace-separated list of tokens (whitespace is any sequence
3854 * of characters matching the production S); the result is a node-set
3855 * containing the elements in the same document as the context node that
3856 * have a unique ID equal to any of the tokens in the list.
3857 */
3858void
3859xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3860 const xmlChar *tokens;
3861 const xmlChar *cur;
3862 xmlChar *ID;
3863 xmlAttrPtr attr;
3864 xmlNodePtr elem = NULL;
3865 xmlXPathObjectPtr ret, obj;
3866
3867 CHECK_ARITY(1);
3868 obj = valuePop(ctxt);
3869 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3870 if (obj->type == XPATH_NODESET) {
3871 xmlXPathObjectPtr newobj;
3872 int i;
3873
3874 ret = xmlXPathNewNodeSet(NULL);
3875
Daniel Veillard911f49a2001-04-07 15:39:35 +00003876 if (obj->nodesetval != NULL) {
3877 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3878 valuePush(ctxt,
3879 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
3880 xmlXPathStringFunction(ctxt, 1);
3881 xmlXPathIdFunction(ctxt, 1);
3882 newobj = valuePop(ctxt);
3883 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
3884 newobj->nodesetval);
3885 xmlXPathFreeObject(newobj);
3886 }
Owen Taylor3473f882001-02-23 17:55:21 +00003887 }
3888
3889 xmlXPathFreeObject(obj);
3890 valuePush(ctxt, ret);
3891 return;
3892 }
3893 if (obj->type != XPATH_STRING) {
3894 valuePush(ctxt, obj);
3895 xmlXPathStringFunction(ctxt, 1);
3896 obj = valuePop(ctxt);
3897 if (obj->type != XPATH_STRING) {
3898 xmlXPathFreeObject(obj);
3899 return;
3900 }
3901 }
3902 tokens = obj->stringval;
3903
3904 ret = xmlXPathNewNodeSet(NULL);
3905 valuePush(ctxt, ret);
3906 if (tokens == NULL) {
3907 xmlXPathFreeObject(obj);
3908 return;
3909 }
3910
3911 cur = tokens;
3912
3913 while (IS_BLANK(*cur)) cur++;
3914 while (*cur != 0) {
3915 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
3916 (*cur == '.') || (*cur == '-') ||
3917 (*cur == '_') || (*cur == ':') ||
3918 (IS_COMBINING(*cur)) ||
3919 (IS_EXTENDER(*cur)))
3920 cur++;
3921
3922 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
3923
3924 ID = xmlStrndup(tokens, cur - tokens);
3925 attr = xmlGetID(ctxt->context->doc, ID);
3926 if (attr != NULL) {
3927 elem = attr->parent;
3928 xmlXPathNodeSetAdd(ret->nodesetval, elem);
3929 }
3930 if (ID != NULL)
3931 xmlFree(ID);
3932
3933 while (IS_BLANK(*cur)) cur++;
3934 tokens = cur;
3935 }
3936 xmlXPathFreeObject(obj);
3937 return;
3938}
3939
3940/**
3941 * xmlXPathLocalNameFunction:
3942 * @ctxt: the XPath Parser context
3943 * @nargs: the number of arguments
3944 *
3945 * Implement the local-name() XPath function
3946 * string local-name(node-set?)
3947 * The local-name function returns a string containing the local part
3948 * of the name of the node in the argument node-set that is first in
3949 * document order. If the node-set is empty or the first node has no
3950 * name, an empty string is returned. If the argument is omitted it
3951 * defaults to the context node.
3952 */
3953void
3954xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3955 xmlXPathObjectPtr cur;
3956
3957 if (nargs == 0) {
3958 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3959 nargs = 1;
3960 }
3961
3962 CHECK_ARITY(1);
3963 if ((ctxt->value == NULL) ||
3964 ((ctxt->value->type != XPATH_NODESET) &&
3965 (ctxt->value->type != XPATH_XSLT_TREE)))
3966 XP_ERROR(XPATH_INVALID_TYPE);
3967 cur = valuePop(ctxt);
3968
Daniel Veillard911f49a2001-04-07 15:39:35 +00003969 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003970 valuePush(ctxt, xmlXPathNewCString(""));
3971 } else {
3972 int i = 0; /* Should be first in document order !!!!! */
3973 switch (cur->nodesetval->nodeTab[i]->type) {
3974 case XML_ELEMENT_NODE:
3975 case XML_ATTRIBUTE_NODE:
3976 case XML_PI_NODE:
3977 valuePush(ctxt,
3978 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
3979 break;
3980 case XML_NAMESPACE_DECL:
3981 valuePush(ctxt, xmlXPathNewString(
3982 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
3983 break;
3984 default:
3985 valuePush(ctxt, xmlXPathNewCString(""));
3986 }
3987 }
3988 xmlXPathFreeObject(cur);
3989}
3990
3991/**
3992 * xmlXPathNamespaceURIFunction:
3993 * @ctxt: the XPath Parser context
3994 * @nargs: the number of arguments
3995 *
3996 * Implement the namespace-uri() XPath function
3997 * string namespace-uri(node-set?)
3998 * The namespace-uri function returns a string containing the
3999 * namespace URI of the expanded name of the node in the argument
4000 * node-set that is first in document order. If the node-set is empty,
4001 * the first node has no name, or the expanded name has no namespace
4002 * URI, an empty string is returned. If the argument is omitted it
4003 * defaults to the context node.
4004 */
4005void
4006xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4007 xmlXPathObjectPtr cur;
4008
4009 if (nargs == 0) {
4010 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4011 nargs = 1;
4012 }
4013 CHECK_ARITY(1);
4014 if ((ctxt->value == NULL) ||
4015 ((ctxt->value->type != XPATH_NODESET) &&
4016 (ctxt->value->type != XPATH_XSLT_TREE)))
4017 XP_ERROR(XPATH_INVALID_TYPE);
4018 cur = valuePop(ctxt);
4019
Daniel Veillard911f49a2001-04-07 15:39:35 +00004020 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004021 valuePush(ctxt, xmlXPathNewCString(""));
4022 } else {
4023 int i = 0; /* Should be first in document order !!!!! */
4024 switch (cur->nodesetval->nodeTab[i]->type) {
4025 case XML_ELEMENT_NODE:
4026 case XML_ATTRIBUTE_NODE:
4027 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4028 valuePush(ctxt, xmlXPathNewCString(""));
4029 else
4030 valuePush(ctxt, xmlXPathNewString(
4031 cur->nodesetval->nodeTab[i]->ns->href));
4032 break;
4033 default:
4034 valuePush(ctxt, xmlXPathNewCString(""));
4035 }
4036 }
4037 xmlXPathFreeObject(cur);
4038}
4039
4040/**
4041 * xmlXPathNameFunction:
4042 * @ctxt: the XPath Parser context
4043 * @nargs: the number of arguments
4044 *
4045 * Implement the name() XPath function
4046 * string name(node-set?)
4047 * The name function returns a string containing a QName representing
4048 * the name of the node in the argument node-set that is first in documenti
4049 * order. The QName must represent the name with respect to the namespace
4050 * declarations in effect on the node whose name is being represented.
4051 * Typically, this will be the form in which the name occurred in the XML
4052 * source. This need not be the case if there are namespace declarations
4053 * in effect on the node that associate multiple prefixes with the same
4054 * namespace. However, an implementation may include information about
4055 * the original prefix in its representation of nodes; in this case, an
4056 * implementation can ensure that the returned string is always the same
4057 * as the QName used in the XML source. If the argument it omitted it
4058 * defaults to the context node.
4059 * Libxml keep the original prefix so the "real qualified name" used is
4060 * returned.
4061 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004062static void
Owen Taylor3473f882001-02-23 17:55:21 +00004063xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4064 xmlXPathObjectPtr cur;
4065
4066 if (nargs == 0) {
4067 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4068 nargs = 1;
4069 }
4070
4071 CHECK_ARITY(1);
4072 if ((ctxt->value == NULL) ||
4073 ((ctxt->value->type != XPATH_NODESET) &&
4074 (ctxt->value->type != XPATH_XSLT_TREE)))
4075 XP_ERROR(XPATH_INVALID_TYPE);
4076 cur = valuePop(ctxt);
4077
Daniel Veillard911f49a2001-04-07 15:39:35 +00004078 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004079 valuePush(ctxt, xmlXPathNewCString(""));
4080 } else {
4081 int i = 0; /* Should be first in document order !!!!! */
4082
4083 switch (cur->nodesetval->nodeTab[i]->type) {
4084 case XML_ELEMENT_NODE:
4085 case XML_ATTRIBUTE_NODE:
4086 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4087 valuePush(ctxt, xmlXPathNewString(
4088 cur->nodesetval->nodeTab[i]->name));
4089
4090 else {
4091 char name[2000];
Owen Taylor3473f882001-02-23 17:55:21 +00004092 snprintf(name, sizeof(name), "%s:%s",
4093 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4094 (char *) cur->nodesetval->nodeTab[i]->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004095 name[sizeof(name) - 1] = 0;
4096 valuePush(ctxt, xmlXPathNewCString(name));
4097 }
4098 break;
4099 default:
4100 valuePush(ctxt,
4101 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4102 xmlXPathLocalNameFunction(ctxt, 1);
4103 }
4104 }
4105 xmlXPathFreeObject(cur);
4106}
4107
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004108
4109/**
4110 * xmlXPathConvertString:
4111 * @val: an XPath object
4112 *
4113 * Converts an existing object to its string() equivalent
4114 *
4115 * Returns the new object, the old one is freed (or the operation
4116 * is done directly on @val)
4117 */
4118xmlXPathObjectPtr
4119xmlXPathConvertString(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004120 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004121
4122 if (val == NULL)
4123 return(xmlXPathNewCString(""));
4124 switch (val->type) {
4125 case XPATH_UNDEFINED:
4126#ifdef DEBUG_EXPR
4127 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4128#endif
4129 ret = xmlXPathNewCString("");
4130 break;
4131 case XPATH_XSLT_TREE:
4132 case XPATH_NODESET:
Daniel Veillard911f49a2001-04-07 15:39:35 +00004133 if ((val->nodesetval == NULL) || (val->nodesetval->nodeNr == 0)) {
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004134 ret = xmlXPathNewCString("");
4135 } else {
4136 xmlChar *res;
4137
4138 xmlXPathNodeSetSort(val->nodesetval);
4139 res = xmlNodeGetContent(val->nodesetval->nodeTab[0]);
4140 /* TODO: avoid allocating res to free it */
4141 ret = xmlXPathNewString(res);
4142 if (res != NULL)
4143 xmlFree(res);
4144 }
4145 break;
4146 case XPATH_STRING:
4147 return(val);
4148 case XPATH_BOOLEAN:
4149 if (val->boolval) ret = xmlXPathNewCString("true");
4150 else ret = xmlXPathNewCString("false");
4151 break;
4152 case XPATH_NUMBER: {
4153 char buf[100];
4154
4155 xmlXPathFormatNumber(val->floatval, buf, sizeof(buf));
4156 ret = xmlXPathNewCString(buf);
4157 break;
4158 }
4159 case XPATH_USERS:
4160 case XPATH_POINT:
4161 case XPATH_RANGE:
4162 case XPATH_LOCATIONSET:
4163 TODO
4164 ret = xmlXPathNewCString("");
4165 break;
4166 }
4167 xmlXPathFreeObject(val);
4168 return(ret);
4169}
4170
Owen Taylor3473f882001-02-23 17:55:21 +00004171/**
4172 * xmlXPathStringFunction:
4173 * @ctxt: the XPath Parser context
4174 * @nargs: the number of arguments
4175 *
4176 * Implement the string() XPath function
4177 * string string(object?)
4178 * he string function converts an object to a string as follows:
4179 * - A node-set is converted to a string by returning the value of
4180 * the node in the node-set that is first in document order.
4181 * If the node-set is empty, an empty string is returned.
4182 * - A number is converted to a string as follows
4183 * + NaN is converted to the string NaN
4184 * + positive zero is converted to the string 0
4185 * + negative zero is converted to the string 0
4186 * + positive infinity is converted to the string Infinity
4187 * + negative infinity is converted to the string -Infinity
4188 * + if the number is an integer, the number is represented in
4189 * decimal form as a Number with no decimal point and no leading
4190 * zeros, preceded by a minus sign (-) if the number is negative
4191 * + otherwise, the number is represented in decimal form as a
4192 * Number including a decimal point with at least one digit
4193 * before the decimal point and at least one digit after the
4194 * decimal point, preceded by a minus sign (-) if the number
4195 * is negative; there must be no leading zeros before the decimal
4196 * point apart possibly from the one required digit immediatelyi
4197 * before the decimal point; beyond the one required digit
4198 * after the decimal point there must be as many, but only as
4199 * many, more digits as are needed to uniquely distinguish the
4200 * number from all other IEEE 754 numeric values.
4201 * - The boolean false value is converted to the string false.
4202 * The boolean true value is converted to the string true.
4203 *
4204 * If the argument is omitted, it defaults to a node-set with the
4205 * context node as its only member.
4206 */
4207void
4208xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4209 xmlXPathObjectPtr cur;
4210
4211 if (nargs == 0) {
4212 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4213 nargs = 1;
4214 }
4215
4216 CHECK_ARITY(1);
4217 cur = valuePop(ctxt);
4218 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004219 cur = xmlXPathConvertString(cur);
4220 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004221}
4222
4223/**
4224 * xmlXPathStringLengthFunction:
4225 * @ctxt: the XPath Parser context
4226 * @nargs: the number of arguments
4227 *
4228 * Implement the string-length() XPath function
4229 * number string-length(string?)
4230 * The string-length returns the number of characters in the string
4231 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4232 * the context node converted to a string, in other words the value
4233 * of the context node.
4234 */
4235void
4236xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4237 xmlXPathObjectPtr cur;
4238
4239 if (nargs == 0) {
4240 if (ctxt->context->node == NULL) {
4241 valuePush(ctxt, xmlXPathNewFloat(0));
4242 } else {
4243 xmlChar *content;
4244
4245 content = xmlNodeGetContent(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004246 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00004247 xmlFree(content);
4248 }
4249 return;
4250 }
4251 CHECK_ARITY(1);
4252 CAST_TO_STRING;
4253 CHECK_TYPE(XPATH_STRING);
4254 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004255 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00004256 xmlXPathFreeObject(cur);
4257}
4258
4259/**
4260 * xmlXPathConcatFunction:
4261 * @ctxt: the XPath Parser context
4262 * @nargs: the number of arguments
4263 *
4264 * Implement the concat() XPath function
4265 * string concat(string, string, string*)
4266 * The concat function returns the concatenation of its arguments.
4267 */
4268void
4269xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4270 xmlXPathObjectPtr cur, newobj;
4271 xmlChar *tmp;
4272
4273 if (nargs < 2) {
4274 CHECK_ARITY(2);
4275 }
4276
4277 CAST_TO_STRING;
4278 cur = valuePop(ctxt);
4279 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4280 xmlXPathFreeObject(cur);
4281 return;
4282 }
4283 nargs--;
4284
4285 while (nargs > 0) {
4286 CAST_TO_STRING;
4287 newobj = valuePop(ctxt);
4288 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4289 xmlXPathFreeObject(newobj);
4290 xmlXPathFreeObject(cur);
4291 XP_ERROR(XPATH_INVALID_TYPE);
4292 }
4293 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4294 newobj->stringval = cur->stringval;
4295 cur->stringval = tmp;
4296
4297 xmlXPathFreeObject(newobj);
4298 nargs--;
4299 }
4300 valuePush(ctxt, cur);
4301}
4302
4303/**
4304 * xmlXPathContainsFunction:
4305 * @ctxt: the XPath Parser context
4306 * @nargs: the number of arguments
4307 *
4308 * Implement the contains() XPath function
4309 * boolean contains(string, string)
4310 * The contains function returns true if the first argument string
4311 * contains the second argument string, and otherwise returns false.
4312 */
4313void
4314xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4315 xmlXPathObjectPtr hay, needle;
4316
4317 CHECK_ARITY(2);
4318 CAST_TO_STRING;
4319 CHECK_TYPE(XPATH_STRING);
4320 needle = valuePop(ctxt);
4321 CAST_TO_STRING;
4322 hay = valuePop(ctxt);
4323 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4324 xmlXPathFreeObject(hay);
4325 xmlXPathFreeObject(needle);
4326 XP_ERROR(XPATH_INVALID_TYPE);
4327 }
4328 if (xmlStrstr(hay->stringval, needle->stringval))
4329 valuePush(ctxt, xmlXPathNewBoolean(1));
4330 else
4331 valuePush(ctxt, xmlXPathNewBoolean(0));
4332 xmlXPathFreeObject(hay);
4333 xmlXPathFreeObject(needle);
4334}
4335
4336/**
4337 * xmlXPathStartsWithFunction:
4338 * @ctxt: the XPath Parser context
4339 * @nargs: the number of arguments
4340 *
4341 * Implement the starts-with() XPath function
4342 * boolean starts-with(string, string)
4343 * The starts-with function returns true if the first argument string
4344 * starts with the second argument string, and otherwise returns false.
4345 */
4346void
4347xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4348 xmlXPathObjectPtr hay, needle;
4349 int n;
4350
4351 CHECK_ARITY(2);
4352 CAST_TO_STRING;
4353 CHECK_TYPE(XPATH_STRING);
4354 needle = valuePop(ctxt);
4355 CAST_TO_STRING;
4356 hay = valuePop(ctxt);
4357 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4358 xmlXPathFreeObject(hay);
4359 xmlXPathFreeObject(needle);
4360 XP_ERROR(XPATH_INVALID_TYPE);
4361 }
4362 n = xmlStrlen(needle->stringval);
4363 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4364 valuePush(ctxt, xmlXPathNewBoolean(0));
4365 else
4366 valuePush(ctxt, xmlXPathNewBoolean(1));
4367 xmlXPathFreeObject(hay);
4368 xmlXPathFreeObject(needle);
4369}
4370
4371/**
4372 * xmlXPathSubstringFunction:
4373 * @ctxt: the XPath Parser context
4374 * @nargs: the number of arguments
4375 *
4376 * Implement the substring() XPath function
4377 * string substring(string, number, number?)
4378 * The substring function returns the substring of the first argument
4379 * starting at the position specified in the second argument with
4380 * length specified in the third argument. For example,
4381 * substring("12345",2,3) returns "234". If the third argument is not
4382 * specified, it returns the substring starting at the position specified
4383 * in the second argument and continuing to the end of the string. For
4384 * example, substring("12345",2) returns "2345". More precisely, each
4385 * character in the string (see [3.6 Strings]) is considered to have a
4386 * numeric position: the position of the first character is 1, the position
4387 * of the second character is 2 and so on. The returned substring contains
4388 * those characters for which the position of the character is greater than
4389 * or equal to the second argument and, if the third argument is specified,
4390 * less than the sum of the second and third arguments; the comparisons
4391 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4392 * - substring("12345", 1.5, 2.6) returns "234"
4393 * - substring("12345", 0, 3) returns "12"
4394 * - substring("12345", 0 div 0, 3) returns ""
4395 * - substring("12345", 1, 0 div 0) returns ""
4396 * - substring("12345", -42, 1 div 0) returns "12345"
4397 * - substring("12345", -1 div 0, 1 div 0) returns ""
4398 */
4399void
4400xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4401 xmlXPathObjectPtr str, start, len;
4402 double le, in;
4403 int i, l;
4404 xmlChar *ret;
4405
4406 /*
Daniel Veillarde043ee12001-04-16 14:08:07 +00004407 * TODO: need to be converted to UTF8 strings
Owen Taylor3473f882001-02-23 17:55:21 +00004408 */
4409 if (nargs < 2) {
4410 CHECK_ARITY(2);
4411 }
4412 if (nargs > 3) {
4413 CHECK_ARITY(3);
4414 }
4415 if (nargs == 3) {
4416 CAST_TO_NUMBER;
4417 CHECK_TYPE(XPATH_NUMBER);
4418 len = valuePop(ctxt);
4419 le = len->floatval;
4420 xmlXPathFreeObject(len);
4421 } else {
4422 le = 2000000000;
4423 }
4424 CAST_TO_NUMBER;
4425 CHECK_TYPE(XPATH_NUMBER);
4426 start = valuePop(ctxt);
4427 in = start->floatval;
4428 xmlXPathFreeObject(start);
4429 CAST_TO_STRING;
4430 CHECK_TYPE(XPATH_STRING);
4431 str = valuePop(ctxt);
4432 le += in;
4433
4434 /* integer index of the first char */
4435 i = (int) in;
4436 if (((double)i) != in) i++;
4437
4438 /* integer index of the last char */
4439 l = (int) le;
4440 if (((double)l) != le) l++;
4441
4442 /* back to a zero based len */
4443 i--;
4444 l--;
4445
4446 /* check against the string len */
4447 if (l > 1024) {
4448 l = xmlStrlen(str->stringval);
4449 }
4450 if (i < 0) {
4451 i = 0;
4452 }
4453
4454 /* number of chars to copy */
4455 l -= i;
4456
4457 ret = xmlStrsub(str->stringval, i, l);
4458 if (ret == NULL)
4459 valuePush(ctxt, xmlXPathNewCString(""));
4460 else {
4461 valuePush(ctxt, xmlXPathNewString(ret));
4462 xmlFree(ret);
4463 }
4464 xmlXPathFreeObject(str);
4465}
4466
4467/**
4468 * xmlXPathSubstringBeforeFunction:
4469 * @ctxt: the XPath Parser context
4470 * @nargs: the number of arguments
4471 *
4472 * Implement the substring-before() XPath function
4473 * string substring-before(string, string)
4474 * The substring-before function returns the substring of the first
4475 * argument string that precedes the first occurrence of the second
4476 * argument string in the first argument string, or the empty string
4477 * if the first argument string does not contain the second argument
4478 * string. For example, substring-before("1999/04/01","/") returns 1999.
4479 */
4480void
4481xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4482 xmlXPathObjectPtr str;
4483 xmlXPathObjectPtr find;
4484 xmlBufferPtr target;
4485 const xmlChar *point;
4486 int offset;
4487
4488 CHECK_ARITY(2);
4489 CAST_TO_STRING;
4490 find = valuePop(ctxt);
4491 CAST_TO_STRING;
4492 str = valuePop(ctxt);
4493
4494 target = xmlBufferCreate();
4495 if (target) {
4496 point = xmlStrstr(str->stringval, find->stringval);
4497 if (point) {
4498 offset = (int)(point - str->stringval);
4499 xmlBufferAdd(target, str->stringval, offset);
4500 }
4501 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4502 xmlBufferFree(target);
4503 }
4504
4505 xmlXPathFreeObject(str);
4506 xmlXPathFreeObject(find);
4507}
4508
4509/**
4510 * xmlXPathSubstringAfterFunction:
4511 * @ctxt: the XPath Parser context
4512 * @nargs: the number of arguments
4513 *
4514 * Implement the substring-after() XPath function
4515 * string substring-after(string, string)
4516 * The substring-after function returns the substring of the first
4517 * argument string that follows the first occurrence of the second
4518 * argument string in the first argument string, or the empty stringi
4519 * if the first argument string does not contain the second argument
4520 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4521 * and substring-after("1999/04/01","19") returns 99/04/01.
4522 */
4523void
4524xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4525 xmlXPathObjectPtr str;
4526 xmlXPathObjectPtr find;
4527 xmlBufferPtr target;
4528 const xmlChar *point;
4529 int offset;
4530
4531 CHECK_ARITY(2);
4532 CAST_TO_STRING;
4533 find = valuePop(ctxt);
4534 CAST_TO_STRING;
4535 str = valuePop(ctxt);
4536
4537 target = xmlBufferCreate();
4538 if (target) {
4539 point = xmlStrstr(str->stringval, find->stringval);
4540 if (point) {
4541 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4542 xmlBufferAdd(target, &str->stringval[offset],
4543 xmlStrlen(str->stringval) - offset);
4544 }
4545 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4546 xmlBufferFree(target);
4547 }
4548
4549 xmlXPathFreeObject(str);
4550 xmlXPathFreeObject(find);
4551}
4552
4553/**
4554 * xmlXPathNormalizeFunction:
4555 * @ctxt: the XPath Parser context
4556 * @nargs: the number of arguments
4557 *
4558 * Implement the normalize-space() XPath function
4559 * string normalize-space(string?)
4560 * The normalize-space function returns the argument string with white
4561 * space normalized by stripping leading and trailing whitespace
4562 * and replacing sequences of whitespace characters by a single
4563 * space. Whitespace characters are the same allowed by the S production
4564 * in XML. If the argument is omitted, it defaults to the context
4565 * node converted to a string, in other words the value of the context node.
4566 */
4567void
4568xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4569 xmlXPathObjectPtr obj = NULL;
4570 xmlChar *source = NULL;
4571 xmlBufferPtr target;
4572 xmlChar blank;
4573
4574 if (nargs == 0) {
4575 /* Use current context node */
4576 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4577 xmlXPathStringFunction(ctxt, 1);
4578 nargs = 1;
4579 }
4580
4581 CHECK_ARITY(1);
4582 CAST_TO_STRING;
4583 CHECK_TYPE(XPATH_STRING);
4584 obj = valuePop(ctxt);
4585 source = obj->stringval;
4586
4587 target = xmlBufferCreate();
4588 if (target && source) {
4589
4590 /* Skip leading whitespaces */
4591 while (IS_BLANK(*source))
4592 source++;
4593
4594 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4595 blank = 0;
4596 while (*source) {
4597 if (IS_BLANK(*source)) {
4598 blank = *source;
4599 } else {
4600 if (blank) {
4601 xmlBufferAdd(target, &blank, 1);
4602 blank = 0;
4603 }
4604 xmlBufferAdd(target, source, 1);
4605 }
4606 source++;
4607 }
4608
4609 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4610 xmlBufferFree(target);
4611 }
4612 xmlXPathFreeObject(obj);
4613}
4614
4615/**
4616 * xmlXPathTranslateFunction:
4617 * @ctxt: the XPath Parser context
4618 * @nargs: the number of arguments
4619 *
4620 * Implement the translate() XPath function
4621 * string translate(string, string, string)
4622 * The translate function returns the first argument string with
4623 * occurrences of characters in the second argument string replaced
4624 * by the character at the corresponding position in the third argument
4625 * string. For example, translate("bar","abc","ABC") returns the string
4626 * BAr. If there is a character in the second argument string with no
4627 * character at a corresponding position in the third argument string
4628 * (because the second argument string is longer than the third argument
4629 * string), then occurrences of that character in the first argument
4630 * string are removed. For example, translate("--aaa--","abc-","ABC")
4631 * returns "AAA". If a character occurs more than once in second
4632 * argument string, then the first occurrence determines the replacement
4633 * character. If the third argument string is longer than the second
4634 * argument string, then excess characters are ignored.
4635 */
4636void
4637xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00004638 xmlXPathObjectPtr str;
4639 xmlXPathObjectPtr from;
4640 xmlXPathObjectPtr to;
4641 xmlBufferPtr target;
4642 int i, offset, max;
4643 xmlChar ch;
4644 const xmlChar *point;
Owen Taylor3473f882001-02-23 17:55:21 +00004645
Daniel Veillarde043ee12001-04-16 14:08:07 +00004646 /*
4647 * TODO: need to be converted to UTF8 strings
4648 */
4649 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00004650
Daniel Veillarde043ee12001-04-16 14:08:07 +00004651 CAST_TO_STRING;
4652 to = valuePop(ctxt);
4653 CAST_TO_STRING;
4654 from = valuePop(ctxt);
4655 CAST_TO_STRING;
4656 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00004657
Daniel Veillarde043ee12001-04-16 14:08:07 +00004658 target = xmlBufferCreate();
4659 if (target) {
4660 max = xmlStrlen(to->stringval);
4661 for (i = 0; (ch = str->stringval[i]); i++) {
4662 point = xmlStrchr(from->stringval, ch);
4663 if (point) {
4664 offset = (int)(point - from->stringval);
4665 if (offset < max)
4666 xmlBufferAdd(target, &to->stringval[offset], 1);
4667 } else
4668 xmlBufferAdd(target, &ch, 1);
4669 }
Owen Taylor3473f882001-02-23 17:55:21 +00004670 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00004671 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4672 xmlBufferFree(target);
4673 xmlXPathFreeObject(str);
4674 xmlXPathFreeObject(from);
4675 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00004676}
4677
4678/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004679 * xmlXPathConvertBoolean:
4680 * @val: an XPath object
4681 *
4682 * Converts an existing object to its boolean() equivalent
4683 *
4684 * Returns the new object, the old one is freed (or the operation
4685 * is done directly on @val)
4686 */
4687xmlXPathObjectPtr
4688xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
4689 int res = 0;
4690
4691 if (val == NULL)
4692 return(NULL);
4693 switch (val->type) {
4694 case XPATH_NODESET:
4695 case XPATH_XSLT_TREE:
4696 if ((val->nodesetval == NULL) ||
4697 (val->nodesetval->nodeNr == 0)) res = 0;
4698 else
4699 res = 1;
4700 break;
4701 case XPATH_STRING:
4702 if ((val->stringval == NULL) ||
4703 (val->stringval[0] == 0)) res = 0;
4704 else
4705 res = 1;
4706 break;
4707 case XPATH_BOOLEAN:
4708 return(val);
4709 case XPATH_NUMBER:
4710 if (val->floatval) res = 1;
4711 break;
4712 default:
4713 STRANGE
4714 }
4715 xmlXPathFreeObject(val);
4716 return(xmlXPathNewBoolean(res));
4717}
4718
4719/**
Owen Taylor3473f882001-02-23 17:55:21 +00004720 * xmlXPathBooleanFunction:
4721 * @ctxt: the XPath Parser context
4722 * @nargs: the number of arguments
4723 *
4724 * Implement the boolean() XPath function
4725 * boolean boolean(object)
4726 * he boolean function converts its argument to a boolean as follows:
4727 * - a number is true if and only if it is neither positive or
4728 * negative zero nor NaN
4729 * - a node-set is true if and only if it is non-empty
4730 * - a string is true if and only if its length is non-zero
4731 */
4732void
4733xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4734 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00004735
4736 CHECK_ARITY(1);
4737 cur = valuePop(ctxt);
4738 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004739 cur = xmlXPathConvertBoolean(cur);
4740 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004741}
4742
4743/**
4744 * xmlXPathNotFunction:
4745 * @ctxt: the XPath Parser context
4746 * @nargs: the number of arguments
4747 *
4748 * Implement the not() XPath function
4749 * boolean not(boolean)
4750 * The not function returns true if its argument is false,
4751 * and false otherwise.
4752 */
4753void
4754xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4755 CHECK_ARITY(1);
4756 CAST_TO_BOOLEAN;
4757 CHECK_TYPE(XPATH_BOOLEAN);
4758 ctxt->value->boolval = ! ctxt->value->boolval;
4759}
4760
4761/**
4762 * xmlXPathTrueFunction:
4763 * @ctxt: the XPath Parser context
4764 * @nargs: the number of arguments
4765 *
4766 * Implement the true() XPath function
4767 * boolean true()
4768 */
4769void
4770xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4771 CHECK_ARITY(0);
4772 valuePush(ctxt, xmlXPathNewBoolean(1));
4773}
4774
4775/**
4776 * xmlXPathFalseFunction:
4777 * @ctxt: the XPath Parser context
4778 * @nargs: the number of arguments
4779 *
4780 * Implement the false() XPath function
4781 * boolean false()
4782 */
4783void
4784xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4785 CHECK_ARITY(0);
4786 valuePush(ctxt, xmlXPathNewBoolean(0));
4787}
4788
4789/**
4790 * xmlXPathLangFunction:
4791 * @ctxt: the XPath Parser context
4792 * @nargs: the number of arguments
4793 *
4794 * Implement the lang() XPath function
4795 * boolean lang(string)
4796 * The lang function returns true or false depending on whether the
4797 * language of the context node as specified by xml:lang attributes
4798 * is the same as or is a sublanguage of the language specified by
4799 * the argument string. The language of the context node is determined
4800 * by the value of the xml:lang attribute on the context node, or, if
4801 * the context node has no xml:lang attribute, by the value of the
4802 * xml:lang attribute on the nearest ancestor of the context node that
4803 * has an xml:lang attribute. If there is no such attribute, then lang
4804 * returns false. If there is such an attribute, then lang returns
4805 * true if the attribute value is equal to the argument ignoring case,
4806 * or if there is some suffix starting with - such that the attribute
4807 * value is equal to the argument ignoring that suffix of the attribute
4808 * value and ignoring case.
4809 */
4810void
4811xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4812 xmlXPathObjectPtr val;
4813 const xmlChar *theLang;
4814 const xmlChar *lang;
4815 int ret = 0;
4816 int i;
4817
4818 CHECK_ARITY(1);
4819 CAST_TO_STRING;
4820 CHECK_TYPE(XPATH_STRING);
4821 val = valuePop(ctxt);
4822 lang = val->stringval;
4823 theLang = xmlNodeGetLang(ctxt->context->node);
4824 if ((theLang != NULL) && (lang != NULL)) {
4825 for (i = 0;lang[i] != 0;i++)
4826 if (toupper(lang[i]) != toupper(theLang[i]))
4827 goto not_equal;
4828 ret = 1;
4829 }
4830not_equal:
4831 xmlXPathFreeObject(val);
4832 valuePush(ctxt, xmlXPathNewBoolean(ret));
4833}
4834
4835/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004836 * xmlXPathConvertNumber:
4837 * @val: an XPath object
4838 *
4839 * Converts an existing object to its number() equivalent
4840 *
4841 * Returns the new object, the old one is freed (or the operation
4842 * is done directly on @val)
4843 */
4844xmlXPathObjectPtr
4845xmlXPathConvertNumber(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004846 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004847 double res;
4848
4849 if (val == NULL)
4850 return(xmlXPathNewFloat(0.0));
4851 switch (val->type) {
4852 case XPATH_UNDEFINED:
4853#ifdef DEBUG_EXPR
4854 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
4855#endif
4856 ret = xmlXPathNewFloat(0.0);
4857 break;
4858 case XPATH_XSLT_TREE:
4859 case XPATH_NODESET:
4860 val = xmlXPathConvertString(val);
4861 /* no break on purpose */
4862 case XPATH_STRING:
4863 res = xmlXPathStringEvalNumber(val->stringval);
4864 ret = xmlXPathNewFloat(res);
4865 break;
4866 case XPATH_BOOLEAN:
4867 if (val->boolval) ret = xmlXPathNewFloat(1.0);
4868 else ret = xmlXPathNewFloat(0.0);
4869 break;
4870 case XPATH_NUMBER:
4871 return(val);
4872 case XPATH_USERS:
4873 case XPATH_POINT:
4874 case XPATH_RANGE:
4875 case XPATH_LOCATIONSET:
4876 TODO
4877 ret = xmlXPathNewFloat(0.0);
4878 break;
4879 }
4880 xmlXPathFreeObject(val);
4881 return(ret);
4882}
4883
4884/**
Owen Taylor3473f882001-02-23 17:55:21 +00004885 * xmlXPathNumberFunction:
4886 * @ctxt: the XPath Parser context
4887 * @nargs: the number of arguments
4888 *
4889 * Implement the number() XPath function
4890 * number number(object?)
4891 */
4892void
4893xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4894 xmlXPathObjectPtr cur;
4895 double res;
4896
4897 if (nargs == 0) {
4898 if (ctxt->context->node == NULL) {
4899 valuePush(ctxt, xmlXPathNewFloat(0.0));
4900 } else {
4901 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
4902
4903 res = xmlXPathStringEvalNumber(content);
4904 valuePush(ctxt, xmlXPathNewFloat(res));
4905 xmlFree(content);
4906 }
4907 return;
4908 }
4909
4910 CHECK_ARITY(1);
4911 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004912 cur = xmlXPathConvertNumber(cur);
4913 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004914}
4915
4916/**
4917 * xmlXPathSumFunction:
4918 * @ctxt: the XPath Parser context
4919 * @nargs: the number of arguments
4920 *
4921 * Implement the sum() XPath function
4922 * number sum(node-set)
4923 * The sum function returns the sum of the values of the nodes in
4924 * the argument node-set.
4925 */
4926void
4927xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4928 xmlXPathObjectPtr cur;
4929 int i;
4930
4931 CHECK_ARITY(1);
4932 if ((ctxt->value == NULL) ||
4933 ((ctxt->value->type != XPATH_NODESET) &&
4934 (ctxt->value->type != XPATH_XSLT_TREE)))
4935 XP_ERROR(XPATH_INVALID_TYPE);
4936 cur = valuePop(ctxt);
4937
Daniel Veillardd8df6c02001-04-05 16:54:14 +00004938 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004939 valuePush(ctxt, xmlXPathNewFloat(0.0));
4940 } else {
4941 valuePush(ctxt,
4942 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
4943 xmlXPathNumberFunction(ctxt, 1);
4944 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
4945 valuePush(ctxt,
4946 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4947 xmlXPathAddValues(ctxt);
4948 }
4949 }
4950 xmlXPathFreeObject(cur);
4951}
4952
4953/**
4954 * xmlXPathFloorFunction:
4955 * @ctxt: the XPath Parser context
4956 * @nargs: the number of arguments
4957 *
4958 * Implement the floor() XPath function
4959 * number floor(number)
4960 * The floor function returns the largest (closest to positive infinity)
4961 * number that is not greater than the argument and that is an integer.
4962 */
4963void
4964xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4965 CHECK_ARITY(1);
4966 CAST_TO_NUMBER;
4967 CHECK_TYPE(XPATH_NUMBER);
4968#if 0
4969 ctxt->value->floatval = floor(ctxt->value->floatval);
4970#else
4971 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
4972 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
4973#endif
4974}
4975
4976/**
4977 * xmlXPathCeilingFunction:
4978 * @ctxt: the XPath Parser context
4979 * @nargs: the number of arguments
4980 *
4981 * Implement the ceiling() XPath function
4982 * number ceiling(number)
4983 * The ceiling function returns the smallest (closest to negative infinity)
4984 * number that is not less than the argument and that is an integer.
4985 */
4986void
4987xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4988 double f;
4989
4990 CHECK_ARITY(1);
4991 CAST_TO_NUMBER;
4992 CHECK_TYPE(XPATH_NUMBER);
4993
4994#if 0
4995 ctxt->value->floatval = ceil(ctxt->value->floatval);
4996#else
4997 f = (double)((int) ctxt->value->floatval);
4998 if (f != ctxt->value->floatval)
4999 ctxt->value->floatval = f + 1;
5000#endif
5001}
5002
5003/**
5004 * xmlXPathRoundFunction:
5005 * @ctxt: the XPath Parser context
5006 * @nargs: the number of arguments
5007 *
5008 * Implement the round() XPath function
5009 * number round(number)
5010 * The round function returns the number that is closest to the
5011 * argument and that is an integer. If there are two such numbers,
5012 * then the one that is even is returned.
5013 */
5014void
5015xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5016 double f;
5017
5018 CHECK_ARITY(1);
5019 CAST_TO_NUMBER;
5020 CHECK_TYPE(XPATH_NUMBER);
5021
5022 if ((ctxt->value->floatval == xmlXPathNAN) ||
5023 (ctxt->value->floatval == xmlXPathPINF) ||
5024 (ctxt->value->floatval == xmlXPathNINF) ||
5025 (ctxt->value->floatval == 0.0))
5026 return;
5027
5028#if 0
5029 f = floor(ctxt->value->floatval);
5030#else
5031 f = (double)((int) ctxt->value->floatval);
5032#endif
5033 if (ctxt->value->floatval < f + 0.5)
5034 ctxt->value->floatval = f;
5035 else
5036 ctxt->value->floatval = f + 1;
5037}
5038
5039/************************************************************************
5040 * *
5041 * The Parser *
5042 * *
5043 ************************************************************************/
5044
5045/*
5046 * a couple of forward declarations since we use a recursive call based
5047 * implementation.
5048 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005049static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005050static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005051static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005052#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005053static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
5054#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00005055#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005056static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005057#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00005058static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
5059 int qualified);
Owen Taylor3473f882001-02-23 17:55:21 +00005060
5061/**
Daniel Veillard61d80a22001-04-27 17:13:01 +00005062 * xmlXPathCurrentChar:
5063 * @ctxt: the XPath parser context
5064 * @cur: pointer to the beginning of the char
5065 * @len: pointer to the length of the char read
5066 *
5067 * The current char value, if using UTF-8 this may actaully span multiple
5068 * bytes in the input buffer.
5069 *
5070 * Returns the current char value and its lenght
5071 */
5072
5073static int
5074xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
5075 unsigned char c;
5076 unsigned int val;
5077 const xmlChar *cur;
5078
5079 if (ctxt == NULL)
5080 return(0);
5081 cur = ctxt->cur;
5082
5083 /*
5084 * We are supposed to handle UTF8, check it's valid
5085 * From rfc2044: encoding of the Unicode values on UTF-8:
5086 *
5087 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
5088 * 0000 0000-0000 007F 0xxxxxxx
5089 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
5090 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
5091 *
5092 * Check for the 0x110000 limit too
5093 */
5094 c = *cur;
5095 if (c & 0x80) {
5096 if ((cur[1] & 0xc0) != 0x80)
5097 goto encoding_error;
5098 if ((c & 0xe0) == 0xe0) {
5099
5100 if ((cur[2] & 0xc0) != 0x80)
5101 goto encoding_error;
5102 if ((c & 0xf0) == 0xf0) {
5103 if (((c & 0xf8) != 0xf0) ||
5104 ((cur[3] & 0xc0) != 0x80))
5105 goto encoding_error;
5106 /* 4-byte code */
5107 *len = 4;
5108 val = (cur[0] & 0x7) << 18;
5109 val |= (cur[1] & 0x3f) << 12;
5110 val |= (cur[2] & 0x3f) << 6;
5111 val |= cur[3] & 0x3f;
5112 } else {
5113 /* 3-byte code */
5114 *len = 3;
5115 val = (cur[0] & 0xf) << 12;
5116 val |= (cur[1] & 0x3f) << 6;
5117 val |= cur[2] & 0x3f;
5118 }
5119 } else {
5120 /* 2-byte code */
5121 *len = 2;
5122 val = (cur[0] & 0x1f) << 6;
5123 val |= cur[1] & 0x3f;
5124 }
5125 if (!IS_CHAR(val)) {
5126 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
5127 }
5128 return(val);
5129 } else {
5130 /* 1-byte code */
5131 *len = 1;
5132 return((int) *cur);
5133 }
5134encoding_error:
5135 /*
5136 * If we detect an UTF8 error that probably mean that the
5137 * input encoding didn't get properly advertized in the
5138 * declaration header. Report the error and switch the encoding
5139 * to ISO-Latin-1 (if you don't like this policy, just declare the
5140 * encoding !)
5141 */
5142 XP_ERROR0(XPATH_ENCODING_ERROR);
5143 *len = 1;
5144 return((int) *cur);
5145}
5146
5147/**
Owen Taylor3473f882001-02-23 17:55:21 +00005148 * xmlXPathParseNCName:
5149 * @ctxt: the XPath Parser context
5150 *
5151 * parse an XML namespace non qualified name.
5152 *
5153 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
5154 *
5155 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
5156 * CombiningChar | Extender
5157 *
5158 * Returns the namespace name or NULL
5159 */
5160
5161xmlChar *
5162xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard2156a562001-04-28 12:24:34 +00005163 const xmlChar *in;
5164 xmlChar *ret;
5165 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005166
Daniel Veillard2156a562001-04-28 12:24:34 +00005167 /*
5168 * Accelerator for simple ASCII names
5169 */
5170 in = ctxt->cur;
5171 if (((*in >= 0x61) && (*in <= 0x7A)) ||
5172 ((*in >= 0x41) && (*in <= 0x5A)) ||
5173 (*in == '_')) {
5174 in++;
5175 while (((*in >= 0x61) && (*in <= 0x7A)) ||
5176 ((*in >= 0x41) && (*in <= 0x5A)) ||
5177 ((*in >= 0x30) && (*in <= 0x39)) ||
5178 (*in == '_'))
5179 in++;
5180 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
5181 (*in == '[') || (*in == ']') || (*in == ':') ||
5182 (*in == '@') || (*in == '*')) {
5183 count = in - ctxt->cur;
5184 if (count == 0)
5185 return(NULL);
5186 ret = xmlStrndup(ctxt->cur, count);
5187 ctxt->cur = in;
5188 return(ret);
5189 }
5190 }
5191 return(xmlXPathParseNameComplex(ctxt, 0));
Owen Taylor3473f882001-02-23 17:55:21 +00005192}
5193
Daniel Veillard2156a562001-04-28 12:24:34 +00005194
Owen Taylor3473f882001-02-23 17:55:21 +00005195/**
5196 * xmlXPathParseQName:
5197 * @ctxt: the XPath Parser context
5198 * @prefix: a xmlChar **
5199 *
5200 * parse an XML qualified name
5201 *
5202 * [NS 5] QName ::= (Prefix ':')? LocalPart
5203 *
5204 * [NS 6] Prefix ::= NCName
5205 *
5206 * [NS 7] LocalPart ::= NCName
5207 *
5208 * Returns the function returns the local part, and prefix is updated
5209 * to get the Prefix if any.
5210 */
5211
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005212static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005213xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
5214 xmlChar *ret = NULL;
5215
5216 *prefix = NULL;
5217 ret = xmlXPathParseNCName(ctxt);
5218 if (CUR == ':') {
5219 *prefix = ret;
5220 NEXT;
5221 ret = xmlXPathParseNCName(ctxt);
5222 }
5223 return(ret);
5224}
5225
5226/**
5227 * xmlXPathParseName:
5228 * @ctxt: the XPath Parser context
5229 *
5230 * parse an XML name
5231 *
5232 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5233 * CombiningChar | Extender
5234 *
5235 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5236 *
5237 * Returns the namespace name or NULL
5238 */
5239
5240xmlChar *
5241xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00005242 const xmlChar *in;
5243 xmlChar *ret;
5244 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005245
Daniel Veillard61d80a22001-04-27 17:13:01 +00005246 /*
5247 * Accelerator for simple ASCII names
5248 */
5249 in = ctxt->cur;
5250 if (((*in >= 0x61) && (*in <= 0x7A)) ||
5251 ((*in >= 0x41) && (*in <= 0x5A)) ||
5252 (*in == '_') || (*in == ':')) {
5253 in++;
5254 while (((*in >= 0x61) && (*in <= 0x7A)) ||
5255 ((*in >= 0x41) && (*in <= 0x5A)) ||
5256 ((*in >= 0x30) && (*in <= 0x39)) ||
5257 (*in == '_') || (*in == ':'))
5258 in++;
5259 if ((*in == ' ') || (*in == '>') || (*in == '/')) {
5260 count = in - ctxt->cur;
5261 ret = xmlStrndup(ctxt->cur, count);
5262 ctxt->cur = in;
5263 return(ret);
5264 }
5265 }
Daniel Veillard2156a562001-04-28 12:24:34 +00005266 return(xmlXPathParseNameComplex(ctxt, 1));
Owen Taylor3473f882001-02-23 17:55:21 +00005267}
5268
Daniel Veillard61d80a22001-04-27 17:13:01 +00005269static xmlChar *
Daniel Veillard2156a562001-04-28 12:24:34 +00005270xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00005271 xmlChar buf[XML_MAX_NAMELEN + 5];
5272 int len = 0, l;
5273 int c;
5274
5275 /*
5276 * Handler for more complex cases
5277 */
5278 c = CUR_CHAR(l);
5279 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
Daniel Veillard2156a562001-04-28 12:24:34 +00005280 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
5281 (c == '*') || /* accelerators */
Daniel Veillard61d80a22001-04-27 17:13:01 +00005282 (!IS_LETTER(c) && (c != '_') &&
Daniel Veillard2156a562001-04-28 12:24:34 +00005283 ((qualified) && (c != ':')))) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00005284 return(NULL);
5285 }
5286
5287 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
5288 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
5289 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00005290 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00005291 (IS_COMBINING(c)) ||
5292 (IS_EXTENDER(c)))) {
5293 COPY_BUF(l,buf,len,c);
5294 NEXTL(l);
5295 c = CUR_CHAR(l);
5296 if (len >= XML_MAX_NAMELEN) {
5297 /*
5298 * Okay someone managed to make a huge name, so he's ready to pay
5299 * for the processing speed.
5300 */
5301 xmlChar *buffer;
5302 int max = len * 2;
5303
5304 buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
5305 if (buffer == NULL) {
5306 XP_ERROR0(XPATH_MEMORY_ERROR);
5307 }
5308 memcpy(buffer, buf, len);
5309 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
5310 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00005311 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00005312 (IS_COMBINING(c)) ||
5313 (IS_EXTENDER(c))) {
5314 if (len + 10 > max) {
5315 max *= 2;
5316 buffer = (xmlChar *) xmlRealloc(buffer,
5317 max * sizeof(xmlChar));
5318 XP_ERROR0(XPATH_MEMORY_ERROR);
5319 if (buffer == NULL) {
5320 XP_ERROR0(XPATH_MEMORY_ERROR);
5321 }
5322 }
5323 COPY_BUF(l,buffer,len,c);
5324 NEXTL(l);
5325 c = CUR_CHAR(l);
5326 }
5327 buffer[len] = 0;
5328 return(buffer);
5329 }
5330 }
Daniel Veillard2156a562001-04-28 12:24:34 +00005331 if (len == 0)
5332 return(NULL);
Daniel Veillard61d80a22001-04-27 17:13:01 +00005333 return(xmlStrndup(buf, len));
5334}
Owen Taylor3473f882001-02-23 17:55:21 +00005335/**
5336 * xmlXPathStringEvalNumber:
5337 * @str: A string to scan
5338 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00005339 * [30a] Float ::= Number ('e' Digits?)?
5340 *
Owen Taylor3473f882001-02-23 17:55:21 +00005341 * [30] Number ::= Digits ('.' Digits?)?
5342 * | '.' Digits
5343 * [31] Digits ::= [0-9]+
5344 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005345 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00005346 * In complement of the Number expression, this function also handles
5347 * negative values : '-' Number.
5348 *
5349 * Returns the double value.
5350 */
5351double
5352xmlXPathStringEvalNumber(const xmlChar *str) {
5353 const xmlChar *cur = str;
5354 double ret = 0.0;
5355 double mult = 1;
5356 int ok = 0;
5357 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005358 int exponent = 0;
5359 int is_exponent_negative = 0;
5360
Owen Taylor3473f882001-02-23 17:55:21 +00005361 while (IS_BLANK(*cur)) cur++;
5362 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5363 return(xmlXPathNAN);
5364 }
5365 if (*cur == '-') {
5366 isneg = 1;
5367 cur++;
5368 }
5369 while ((*cur >= '0') && (*cur <= '9')) {
5370 ret = ret * 10 + (*cur - '0');
5371 ok = 1;
5372 cur++;
5373 }
5374 if (*cur == '.') {
5375 cur++;
5376 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5377 return(xmlXPathNAN);
5378 }
5379 while ((*cur >= '0') && (*cur <= '9')) {
5380 mult /= 10;
5381 ret = ret + (*cur - '0') * mult;
5382 cur++;
5383 }
5384 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00005385 if ((*cur == 'e') || (*cur == 'E')) {
5386 cur++;
5387 if (*cur == '-') {
5388 is_exponent_negative = 1;
5389 cur++;
5390 }
5391 while ((*cur >= '0') && (*cur <= '9')) {
5392 exponent = exponent * 10 + (*cur - '0');
5393 cur++;
5394 }
5395 }
Owen Taylor3473f882001-02-23 17:55:21 +00005396 while (IS_BLANK(*cur)) cur++;
5397 if (*cur != 0) return(xmlXPathNAN);
5398 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005399 if (is_exponent_negative) exponent = -exponent;
5400 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00005401 return(ret);
5402}
5403
5404/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005405 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00005406 * @ctxt: the XPath Parser context
5407 *
5408 * [30] Number ::= Digits ('.' Digits?)?
5409 * | '.' Digits
5410 * [31] Digits ::= [0-9]+
5411 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005412 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00005413 *
5414 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005415static void
5416xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005417 double ret = 0.0;
5418 double mult = 1;
5419 int ok = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005420 int exponent = 0;
5421 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005422
5423 CHECK_ERROR;
5424 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5425 XP_ERROR(XPATH_NUMBER_ERROR);
5426 }
5427 while ((CUR >= '0') && (CUR <= '9')) {
5428 ret = ret * 10 + (CUR - '0');
5429 ok = 1;
5430 NEXT;
5431 }
5432 if (CUR == '.') {
5433 NEXT;
5434 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5435 XP_ERROR(XPATH_NUMBER_ERROR);
5436 }
5437 while ((CUR >= '0') && (CUR <= '9')) {
5438 mult /= 10;
5439 ret = ret + (CUR - '0') * mult;
5440 NEXT;
5441 }
5442 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00005443 if ((CUR == 'e') || (CUR == 'E')) {
5444 NEXT;
5445 if (CUR == '-') {
5446 is_exponent_negative = 1;
5447 NEXT;
5448 }
5449 while ((CUR >= '0') && (CUR <= '9')) {
5450 exponent = exponent * 10 + (CUR - '0');
5451 NEXT;
5452 }
5453 }
5454 if (is_exponent_negative)
5455 exponent = -exponent;
5456 ret *= pow(10.0, (double)exponent);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005457 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
5458 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005459}
5460
5461/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005462 * xmlXPathParseLiteral:
5463 * @ctxt: the XPath Parser context
5464 *
5465 * Parse a Literal
5466 *
5467 * [29] Literal ::= '"' [^"]* '"'
5468 * | "'" [^']* "'"
5469 *
5470 * Returns the value found or NULL in case of error
5471 */
5472static xmlChar *
5473xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
5474 const xmlChar *q;
5475 xmlChar *ret = NULL;
5476
5477 if (CUR == '"') {
5478 NEXT;
5479 q = CUR_PTR;
5480 while ((IS_CHAR(CUR)) && (CUR != '"'))
5481 NEXT;
5482 if (!IS_CHAR(CUR)) {
5483 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5484 } else {
5485 ret = xmlStrndup(q, CUR_PTR - q);
5486 NEXT;
5487 }
5488 } else if (CUR == '\'') {
5489 NEXT;
5490 q = CUR_PTR;
5491 while ((IS_CHAR(CUR)) && (CUR != '\''))
5492 NEXT;
5493 if (!IS_CHAR(CUR)) {
5494 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5495 } else {
5496 ret = xmlStrndup(q, CUR_PTR - q);
5497 NEXT;
5498 }
5499 } else {
5500 XP_ERROR0(XPATH_START_LITERAL_ERROR);
5501 }
5502 return(ret);
5503}
5504
5505/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005506 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00005507 * @ctxt: the XPath Parser context
5508 *
5509 * Parse a Literal and push it on the stack.
5510 *
5511 * [29] Literal ::= '"' [^"]* '"'
5512 * | "'" [^']* "'"
5513 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005514 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00005515 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005516static void
5517xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005518 const xmlChar *q;
5519 xmlChar *ret = NULL;
5520
5521 if (CUR == '"') {
5522 NEXT;
5523 q = CUR_PTR;
5524 while ((IS_CHAR(CUR)) && (CUR != '"'))
5525 NEXT;
5526 if (!IS_CHAR(CUR)) {
5527 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5528 } else {
5529 ret = xmlStrndup(q, CUR_PTR - q);
5530 NEXT;
5531 }
5532 } else if (CUR == '\'') {
5533 NEXT;
5534 q = CUR_PTR;
5535 while ((IS_CHAR(CUR)) && (CUR != '\''))
5536 NEXT;
5537 if (!IS_CHAR(CUR)) {
5538 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5539 } else {
5540 ret = xmlStrndup(q, CUR_PTR - q);
5541 NEXT;
5542 }
5543 } else {
5544 XP_ERROR(XPATH_START_LITERAL_ERROR);
5545 }
5546 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005547 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
5548 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005549 xmlFree(ret);
5550}
5551
5552/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005553 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00005554 * @ctxt: the XPath Parser context
5555 *
5556 * Parse a VariableReference, evaluate it and push it on the stack.
5557 *
5558 * The variable bindings consist of a mapping from variable names
5559 * to variable values. The value of a variable is an object, which
5560 * of any of the types that are possible for the value of an expression,
5561 * and may also be of additional types not specified here.
5562 *
5563 * Early evaluation is possible since:
5564 * The variable bindings [...] used to evaluate a subexpression are
5565 * always the same as those used to evaluate the containing expression.
5566 *
5567 * [36] VariableReference ::= '$' QName
5568 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005569static void
5570xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005571 xmlChar *name;
5572 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005573
5574 SKIP_BLANKS;
5575 if (CUR != '$') {
5576 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5577 }
5578 NEXT;
5579 name = xmlXPathParseQName(ctxt, &prefix);
5580 if (name == NULL) {
5581 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5582 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005583 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005584 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
5585 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005586 SKIP_BLANKS;
5587}
5588
5589/**
5590 * xmlXPathIsNodeType:
5591 * @ctxt: the XPath Parser context
5592 * @name: a name string
5593 *
5594 * Is the name given a NodeType one.
5595 *
5596 * [38] NodeType ::= 'comment'
5597 * | 'text'
5598 * | 'processing-instruction'
5599 * | 'node'
5600 *
5601 * Returns 1 if true 0 otherwise
5602 */
5603int
5604xmlXPathIsNodeType(const xmlChar *name) {
5605 if (name == NULL)
5606 return(0);
5607
5608 if (xmlStrEqual(name, BAD_CAST "comment"))
5609 return(1);
5610 if (xmlStrEqual(name, BAD_CAST "text"))
5611 return(1);
5612 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5613 return(1);
5614 if (xmlStrEqual(name, BAD_CAST "node"))
5615 return(1);
5616 return(0);
5617}
5618
5619/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005620 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00005621 * @ctxt: the XPath Parser context
5622 *
5623 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5624 * [17] Argument ::= Expr
5625 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005626 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00005627 * pushed on the stack
5628 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005629static void
5630xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005631 xmlChar *name;
5632 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005633 int nbargs = 0;
5634
5635 name = xmlXPathParseQName(ctxt, &prefix);
5636 if (name == NULL) {
5637 XP_ERROR(XPATH_EXPR_ERROR);
5638 }
5639 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005640#ifdef DEBUG_EXPR
5641 if (prefix == NULL)
5642 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5643 name);
5644 else
5645 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5646 prefix, name);
5647#endif
5648
Owen Taylor3473f882001-02-23 17:55:21 +00005649 if (CUR != '(') {
5650 XP_ERROR(XPATH_EXPR_ERROR);
5651 }
5652 NEXT;
5653 SKIP_BLANKS;
5654
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005655 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005656 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005657 int op1 = ctxt->comp->last;
5658 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005659 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005660 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005661 nbargs++;
5662 if (CUR == ')') break;
5663 if (CUR != ',') {
5664 XP_ERROR(XPATH_EXPR_ERROR);
5665 }
5666 NEXT;
5667 SKIP_BLANKS;
5668 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005669 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
5670 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005671 NEXT;
5672 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005673}
5674
5675/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005676 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005677 * @ctxt: the XPath Parser context
5678 *
5679 * [15] PrimaryExpr ::= VariableReference
5680 * | '(' Expr ')'
5681 * | Literal
5682 * | Number
5683 * | FunctionCall
5684 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005685 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005686 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005687static void
5688xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005689 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005690 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005691 else if (CUR == '(') {
5692 NEXT;
5693 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005694 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005695 if (CUR != ')') {
5696 XP_ERROR(XPATH_EXPR_ERROR);
5697 }
5698 NEXT;
5699 SKIP_BLANKS;
5700 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005701 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005702 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005703 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005704 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005705 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005706 }
5707 SKIP_BLANKS;
5708}
5709
5710/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005711 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005712 * @ctxt: the XPath Parser context
5713 *
5714 * [20] FilterExpr ::= PrimaryExpr
5715 * | FilterExpr Predicate
5716 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005717 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005718 * Square brackets are used to filter expressions in the same way that
5719 * they are used in location paths. It is an error if the expression to
5720 * be filtered does not evaluate to a node-set. The context node list
5721 * used for evaluating the expression in square brackets is the node-set
5722 * to be filtered listed in document order.
5723 */
5724
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005725static void
5726xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
5727 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005728 CHECK_ERROR;
5729 SKIP_BLANKS;
5730
5731 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005732 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00005733 SKIP_BLANKS;
5734 }
5735
5736
5737}
5738
5739/**
5740 * xmlXPathScanName:
5741 * @ctxt: the XPath Parser context
5742 *
5743 * Trickery: parse an XML name but without consuming the input flow
5744 * Needed to avoid insanity in the parser state.
5745 *
5746 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5747 * CombiningChar | Extender
5748 *
5749 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5750 *
5751 * [6] Names ::= Name (S Name)*
5752 *
5753 * Returns the Name parsed or NULL
5754 */
5755
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005756static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005757xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5758 xmlChar buf[XML_MAX_NAMELEN];
5759 int len = 0;
5760
5761 SKIP_BLANKS;
5762 if (!IS_LETTER(CUR) && (CUR != '_') &&
5763 (CUR != ':')) {
5764 return(NULL);
5765 }
5766
5767 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5768 (NXT(len) == '.') || (NXT(len) == '-') ||
5769 (NXT(len) == '_') || (NXT(len) == ':') ||
5770 (IS_COMBINING(NXT(len))) ||
5771 (IS_EXTENDER(NXT(len)))) {
5772 buf[len] = NXT(len);
5773 len++;
5774 if (len >= XML_MAX_NAMELEN) {
5775 xmlGenericError(xmlGenericErrorContext,
5776 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5777 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5778 (NXT(len) == '.') || (NXT(len) == '-') ||
5779 (NXT(len) == '_') || (NXT(len) == ':') ||
5780 (IS_COMBINING(NXT(len))) ||
5781 (IS_EXTENDER(NXT(len))))
5782 len++;
5783 break;
5784 }
5785 }
5786 return(xmlStrndup(buf, len));
5787}
5788
5789/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005790 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005791 * @ctxt: the XPath Parser context
5792 *
5793 * [19] PathExpr ::= LocationPath
5794 * | FilterExpr
5795 * | FilterExpr '/' RelativeLocationPath
5796 * | FilterExpr '//' RelativeLocationPath
5797 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005798 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005799 * The / operator and // operators combine an arbitrary expression
5800 * and a relative location path. It is an error if the expression
5801 * does not evaluate to a node-set.
5802 * The / operator does composition in the same way as when / is
5803 * used in a location path. As in location paths, // is short for
5804 * /descendant-or-self::node()/.
5805 */
5806
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005807static void
5808xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005809 int lc = 1; /* Should we branch to LocationPath ? */
5810 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5811
5812 SKIP_BLANKS;
5813 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5814 (CUR == '\'') || (CUR == '"')) {
5815 lc = 0;
5816 } else if (CUR == '*') {
5817 /* relative or absolute location path */
5818 lc = 1;
5819 } else if (CUR == '/') {
5820 /* relative or absolute location path */
5821 lc = 1;
5822 } else if (CUR == '@') {
5823 /* relative abbreviated attribute location path */
5824 lc = 1;
5825 } else if (CUR == '.') {
5826 /* relative abbreviated attribute location path */
5827 lc = 1;
5828 } else {
5829 /*
5830 * Problem is finding if we have a name here whether it's:
5831 * - a nodetype
5832 * - a function call in which case it's followed by '('
5833 * - an axis in which case it's followed by ':'
5834 * - a element name
5835 * We do an a priori analysis here rather than having to
5836 * maintain parsed token content through the recursive function
5837 * calls. This looks uglier but makes the code quite easier to
5838 * read/write/debug.
5839 */
5840 SKIP_BLANKS;
5841 name = xmlXPathScanName(ctxt);
5842 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5843#ifdef DEBUG_STEP
5844 xmlGenericError(xmlGenericErrorContext,
5845 "PathExpr: Axis\n");
5846#endif
5847 lc = 1;
5848 xmlFree(name);
5849 } else if (name != NULL) {
5850 int len =xmlStrlen(name);
5851 int blank = 0;
5852
5853
5854 while (NXT(len) != 0) {
5855 if (NXT(len) == '/') {
5856 /* element name */
5857#ifdef DEBUG_STEP
5858 xmlGenericError(xmlGenericErrorContext,
5859 "PathExpr: AbbrRelLocation\n");
5860#endif
5861 lc = 1;
5862 break;
5863 } else if (IS_BLANK(NXT(len))) {
5864 /* skip to next */
5865 blank = 1;
5866 } else if (NXT(len) == ':') {
5867#ifdef DEBUG_STEP
5868 xmlGenericError(xmlGenericErrorContext,
5869 "PathExpr: AbbrRelLocation\n");
5870#endif
5871 lc = 1;
5872 break;
5873 } else if ((NXT(len) == '(')) {
5874 /* Note Type or Function */
5875 if (xmlXPathIsNodeType(name)) {
5876#ifdef DEBUG_STEP
5877 xmlGenericError(xmlGenericErrorContext,
5878 "PathExpr: Type search\n");
5879#endif
5880 lc = 1;
5881 } else {
5882#ifdef DEBUG_STEP
5883 xmlGenericError(xmlGenericErrorContext,
5884 "PathExpr: function call\n");
5885#endif
5886 lc = 0;
5887 }
5888 break;
5889 } else if ((NXT(len) == '[')) {
5890 /* element name */
5891#ifdef DEBUG_STEP
5892 xmlGenericError(xmlGenericErrorContext,
5893 "PathExpr: AbbrRelLocation\n");
5894#endif
5895 lc = 1;
5896 break;
5897 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
5898 (NXT(len) == '=')) {
5899 lc = 1;
5900 break;
5901 } else {
5902 lc = 1;
5903 break;
5904 }
5905 len++;
5906 }
5907 if (NXT(len) == 0) {
5908#ifdef DEBUG_STEP
5909 xmlGenericError(xmlGenericErrorContext,
5910 "PathExpr: AbbrRelLocation\n");
5911#endif
5912 /* element name */
5913 lc = 1;
5914 }
5915 xmlFree(name);
5916 } else {
5917 /* make sure all cases are covered explicitely */
5918 XP_ERROR(XPATH_EXPR_ERROR);
5919 }
5920 }
5921
5922 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005923 if (CUR == '/') {
5924 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
5925 } else {
5926 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005927 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005928 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005929 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005930 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005931 CHECK_ERROR;
5932 if ((CUR == '/') && (NXT(1) == '/')) {
5933 SKIP(2);
5934 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005935
5936 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
5937 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
5938 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
5939
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005940 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005941 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005942 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005943 }
5944 }
5945 SKIP_BLANKS;
5946}
5947
5948/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005949 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005950 * @ctxt: the XPath Parser context
5951 *
5952 * [18] UnionExpr ::= PathExpr
5953 * | UnionExpr '|' PathExpr
5954 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005955 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005956 */
5957
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005958static void
5959xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
5960 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005961 CHECK_ERROR;
5962 SKIP_BLANKS;
5963 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005964 int op1 = ctxt->comp->last;
5965 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005966
5967 NEXT;
5968 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005969 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005970
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005971 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
5972
Owen Taylor3473f882001-02-23 17:55:21 +00005973 SKIP_BLANKS;
5974 }
Owen Taylor3473f882001-02-23 17:55:21 +00005975}
5976
5977/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005978 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005979 * @ctxt: the XPath Parser context
5980 *
5981 * [27] UnaryExpr ::= UnionExpr
5982 * | '-' UnaryExpr
5983 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005984 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005985 */
5986
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005987static void
5988xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005989 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005990 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005991
5992 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00005993 while (CUR == '-') {
5994 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005995 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005996 NEXT;
5997 SKIP_BLANKS;
5998 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005999
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006000 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006001 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006002 if (found) {
6003 if (minus)
6004 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
6005 else
6006 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006007 }
6008}
6009
6010/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006011 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006012 * @ctxt: the XPath Parser context
6013 *
6014 * [26] MultiplicativeExpr ::= UnaryExpr
6015 * | MultiplicativeExpr MultiplyOperator UnaryExpr
6016 * | MultiplicativeExpr 'div' UnaryExpr
6017 * | MultiplicativeExpr 'mod' UnaryExpr
6018 * [34] MultiplyOperator ::= '*'
6019 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006020 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006021 */
6022
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006023static void
6024xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
6025 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006026 CHECK_ERROR;
6027 SKIP_BLANKS;
6028 while ((CUR == '*') ||
6029 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
6030 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
6031 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006032 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006033
6034 if (CUR == '*') {
6035 op = 0;
6036 NEXT;
6037 } else if (CUR == 'd') {
6038 op = 1;
6039 SKIP(3);
6040 } else if (CUR == 'm') {
6041 op = 2;
6042 SKIP(3);
6043 }
6044 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006045 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006046 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006047 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006048 SKIP_BLANKS;
6049 }
6050}
6051
6052/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006053 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006054 * @ctxt: the XPath Parser context
6055 *
6056 * [25] AdditiveExpr ::= MultiplicativeExpr
6057 * | AdditiveExpr '+' MultiplicativeExpr
6058 * | AdditiveExpr '-' MultiplicativeExpr
6059 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006060 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006061 */
6062
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006063static void
6064xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006065
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006066 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006067 CHECK_ERROR;
6068 SKIP_BLANKS;
6069 while ((CUR == '+') || (CUR == '-')) {
6070 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006071 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006072
6073 if (CUR == '+') plus = 1;
6074 else plus = 0;
6075 NEXT;
6076 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006077 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006078 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006079 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006080 SKIP_BLANKS;
6081 }
6082}
6083
6084/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006085 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006086 * @ctxt: the XPath Parser context
6087 *
6088 * [24] RelationalExpr ::= AdditiveExpr
6089 * | RelationalExpr '<' AdditiveExpr
6090 * | RelationalExpr '>' AdditiveExpr
6091 * | RelationalExpr '<=' AdditiveExpr
6092 * | RelationalExpr '>=' AdditiveExpr
6093 *
6094 * A <= B > C is allowed ? Answer from James, yes with
6095 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
6096 * which is basically what got implemented.
6097 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006098 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00006099 * on the stack
6100 */
6101
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006102static void
6103xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
6104 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006105 CHECK_ERROR;
6106 SKIP_BLANKS;
6107 while ((CUR == '<') ||
6108 (CUR == '>') ||
6109 ((CUR == '<') && (NXT(1) == '=')) ||
6110 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006111 int inf, strict;
6112 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006113
6114 if (CUR == '<') inf = 1;
6115 else inf = 0;
6116 if (NXT(1) == '=') strict = 0;
6117 else strict = 1;
6118 NEXT;
6119 if (!strict) NEXT;
6120 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006121 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006122 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006123 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00006124 SKIP_BLANKS;
6125 }
6126}
6127
6128/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006129 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006130 * @ctxt: the XPath Parser context
6131 *
6132 * [23] EqualityExpr ::= RelationalExpr
6133 * | EqualityExpr '=' RelationalExpr
6134 * | EqualityExpr '!=' RelationalExpr
6135 *
6136 * A != B != C is allowed ? Answer from James, yes with
6137 * (RelationalExpr = RelationalExpr) = RelationalExpr
6138 * (RelationalExpr != RelationalExpr) != RelationalExpr
6139 * which is basically what got implemented.
6140 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006141 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006142 *
6143 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006144static void
6145xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
6146 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006147 CHECK_ERROR;
6148 SKIP_BLANKS;
6149 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006150 int eq;
6151 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006152
6153 if (CUR == '=') eq = 1;
6154 else eq = 0;
6155 NEXT;
6156 if (!eq) NEXT;
6157 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006158 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006159 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006160 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006161 SKIP_BLANKS;
6162 }
6163}
6164
6165/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006166 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006167 * @ctxt: the XPath Parser context
6168 *
6169 * [22] AndExpr ::= EqualityExpr
6170 * | AndExpr 'and' EqualityExpr
6171 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006172 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006173 *
6174 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006175static void
6176xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
6177 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006178 CHECK_ERROR;
6179 SKIP_BLANKS;
6180 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006181 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006182 SKIP(3);
6183 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006184 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006185 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006186 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006187 SKIP_BLANKS;
6188 }
6189}
6190
6191/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006192 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006193 * @ctxt: the XPath Parser context
6194 *
6195 * [14] Expr ::= OrExpr
6196 * [21] OrExpr ::= AndExpr
6197 * | OrExpr 'or' AndExpr
6198 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006199 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00006200 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006201static void
6202xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
6203 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006204 CHECK_ERROR;
6205 SKIP_BLANKS;
6206 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006207 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006208 SKIP(2);
6209 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006210 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006211 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006212 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
6213 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00006214 SKIP_BLANKS;
6215 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006216 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
6217 /* more ops could be optimized too */
6218 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
6219 }
Owen Taylor3473f882001-02-23 17:55:21 +00006220}
6221
6222/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006223 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00006224 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006225 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00006226 *
6227 * [8] Predicate ::= '[' PredicateExpr ']'
6228 * [9] PredicateExpr ::= Expr
6229 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006230 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00006231 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006232static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006233xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006234 int op1 = ctxt->comp->last;
6235
6236 SKIP_BLANKS;
6237 if (CUR != '[') {
6238 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6239 }
6240 NEXT;
6241 SKIP_BLANKS;
6242
6243 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006244 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006245 CHECK_ERROR;
6246
6247 if (CUR != ']') {
6248 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6249 }
6250
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006251 if (filter)
6252 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
6253 else
6254 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006255
6256 NEXT;
6257 SKIP_BLANKS;
6258}
6259
6260/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006261 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00006262 * @ctxt: the XPath Parser context
6263 * @test: pointer to a xmlXPathTestVal
6264 * @type: pointer to a xmlXPathTypeVal
6265 * @prefix: placeholder for a possible name prefix
6266 *
6267 * [7] NodeTest ::= NameTest
6268 * | NodeType '(' ')'
6269 * | 'processing-instruction' '(' Literal ')'
6270 *
6271 * [37] NameTest ::= '*'
6272 * | NCName ':' '*'
6273 * | QName
6274 * [38] NodeType ::= 'comment'
6275 * | 'text'
6276 * | 'processing-instruction'
6277 * | 'node'
6278 *
6279 * Returns the name found and update @test, @type and @prefix appropriately
6280 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006281static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006282xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6283 xmlXPathTypeVal *type, const xmlChar **prefix,
6284 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00006285 int blanks;
6286
6287 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6288 STRANGE;
6289 return(NULL);
6290 }
6291 *type = 0;
6292 *test = 0;
6293 *prefix = NULL;
6294 SKIP_BLANKS;
6295
6296 if ((name == NULL) && (CUR == '*')) {
6297 /*
6298 * All elements
6299 */
6300 NEXT;
6301 *test = NODE_TEST_ALL;
6302 return(NULL);
6303 }
6304
6305 if (name == NULL)
6306 name = xmlXPathParseNCName(ctxt);
6307 if (name == NULL) {
6308 XP_ERROR0(XPATH_EXPR_ERROR);
6309 }
6310
6311 blanks = IS_BLANK(CUR);
6312 SKIP_BLANKS;
6313 if (CUR == '(') {
6314 NEXT;
6315 /*
6316 * NodeType or PI search
6317 */
6318 if (xmlStrEqual(name, BAD_CAST "comment"))
6319 *type = NODE_TYPE_COMMENT;
6320 else if (xmlStrEqual(name, BAD_CAST "node"))
6321 *type = NODE_TYPE_NODE;
6322 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6323 *type = NODE_TYPE_PI;
6324 else if (xmlStrEqual(name, BAD_CAST "text"))
6325 *type = NODE_TYPE_TEXT;
6326 else {
6327 if (name != NULL)
6328 xmlFree(name);
6329 XP_ERROR0(XPATH_EXPR_ERROR);
6330 }
6331
6332 *test = NODE_TEST_TYPE;
6333
6334 SKIP_BLANKS;
6335 if (*type == NODE_TYPE_PI) {
6336 /*
6337 * Specific case: search a PI by name.
6338 */
Owen Taylor3473f882001-02-23 17:55:21 +00006339 if (name != NULL)
6340 xmlFree(name);
Daniel Veillard82e49712001-04-26 14:38:03 +00006341 name = NULL;
6342 if (CUR != ')') {
6343 name = xmlXPathParseLiteral(ctxt);
6344 CHECK_ERROR 0;
6345 SKIP_BLANKS;
6346 }
Owen Taylor3473f882001-02-23 17:55:21 +00006347 }
6348 if (CUR != ')') {
6349 if (name != NULL)
6350 xmlFree(name);
6351 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6352 }
6353 NEXT;
6354 return(name);
6355 }
6356 *test = NODE_TEST_NAME;
6357 if ((!blanks) && (CUR == ':')) {
6358 NEXT;
6359
6360 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006361 * Since currently the parser context don't have a
6362 * namespace list associated:
6363 * The namespace name for this prefix can be computed
6364 * only at evaluation time. The compilation is done
6365 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00006366 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006367#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00006368 *prefix = xmlXPathNsLookup(ctxt->context, name);
6369 if (name != NULL)
6370 xmlFree(name);
6371 if (*prefix == NULL) {
6372 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6373 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006374#else
6375 *prefix = name;
6376#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006377
6378 if (CUR == '*') {
6379 /*
6380 * All elements
6381 */
6382 NEXT;
6383 *test = NODE_TEST_ALL;
6384 return(NULL);
6385 }
6386
6387 name = xmlXPathParseNCName(ctxt);
6388 if (name == NULL) {
6389 XP_ERROR0(XPATH_EXPR_ERROR);
6390 }
6391 }
6392 return(name);
6393}
6394
6395/**
6396 * xmlXPathIsAxisName:
6397 * @name: a preparsed name token
6398 *
6399 * [6] AxisName ::= 'ancestor'
6400 * | 'ancestor-or-self'
6401 * | 'attribute'
6402 * | 'child'
6403 * | 'descendant'
6404 * | 'descendant-or-self'
6405 * | 'following'
6406 * | 'following-sibling'
6407 * | 'namespace'
6408 * | 'parent'
6409 * | 'preceding'
6410 * | 'preceding-sibling'
6411 * | 'self'
6412 *
6413 * Returns the axis or 0
6414 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006415static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00006416xmlXPathIsAxisName(const xmlChar *name) {
6417 xmlXPathAxisVal ret = 0;
6418 switch (name[0]) {
6419 case 'a':
6420 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6421 ret = AXIS_ANCESTOR;
6422 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6423 ret = AXIS_ANCESTOR_OR_SELF;
6424 if (xmlStrEqual(name, BAD_CAST "attribute"))
6425 ret = AXIS_ATTRIBUTE;
6426 break;
6427 case 'c':
6428 if (xmlStrEqual(name, BAD_CAST "child"))
6429 ret = AXIS_CHILD;
6430 break;
6431 case 'd':
6432 if (xmlStrEqual(name, BAD_CAST "descendant"))
6433 ret = AXIS_DESCENDANT;
6434 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6435 ret = AXIS_DESCENDANT_OR_SELF;
6436 break;
6437 case 'f':
6438 if (xmlStrEqual(name, BAD_CAST "following"))
6439 ret = AXIS_FOLLOWING;
6440 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6441 ret = AXIS_FOLLOWING_SIBLING;
6442 break;
6443 case 'n':
6444 if (xmlStrEqual(name, BAD_CAST "namespace"))
6445 ret = AXIS_NAMESPACE;
6446 break;
6447 case 'p':
6448 if (xmlStrEqual(name, BAD_CAST "parent"))
6449 ret = AXIS_PARENT;
6450 if (xmlStrEqual(name, BAD_CAST "preceding"))
6451 ret = AXIS_PRECEDING;
6452 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6453 ret = AXIS_PRECEDING_SIBLING;
6454 break;
6455 case 's':
6456 if (xmlStrEqual(name, BAD_CAST "self"))
6457 ret = AXIS_SELF;
6458 break;
6459 }
6460 return(ret);
6461}
6462
6463/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006464 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00006465 * @ctxt: the XPath Parser context
6466 *
6467 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6468 * | AbbreviatedStep
6469 *
6470 * [12] AbbreviatedStep ::= '.' | '..'
6471 *
6472 * [5] AxisSpecifier ::= AxisName '::'
6473 * | AbbreviatedAxisSpecifier
6474 *
6475 * [13] AbbreviatedAxisSpecifier ::= '@'?
6476 *
6477 * Modified for XPtr range support as:
6478 *
6479 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6480 * | AbbreviatedStep
6481 * | 'range-to' '(' Expr ')' Predicate*
6482 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006483 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00006484 * A location step of . is short for self::node(). This is
6485 * particularly useful in conjunction with //. For example, the
6486 * location path .//para is short for
6487 * self::node()/descendant-or-self::node()/child::para
6488 * and so will select all para descendant elements of the context
6489 * node.
6490 * Similarly, a location step of .. is short for parent::node().
6491 * For example, ../title is short for parent::node()/child::title
6492 * and so will select the title children of the parent of the context
6493 * node.
6494 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006495static void
6496xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006497#ifdef LIBXML_XPTR_ENABLED
6498 int rangeto = 0;
6499 int op2 = -1;
6500#endif
6501
Owen Taylor3473f882001-02-23 17:55:21 +00006502 SKIP_BLANKS;
6503 if ((CUR == '.') && (NXT(1) == '.')) {
6504 SKIP(2);
6505 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006506 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
6507 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006508 } else if (CUR == '.') {
6509 NEXT;
6510 SKIP_BLANKS;
6511 } else {
6512 xmlChar *name = NULL;
6513 const xmlChar *prefix = NULL;
6514 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006515 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006516 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006517 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00006518
6519 /*
6520 * The modification needed for XPointer change to the production
6521 */
6522#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006523 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00006524 name = xmlXPathParseNCName(ctxt);
6525 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006526 op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006527 xmlFree(name);
6528 SKIP_BLANKS;
6529 if (CUR != '(') {
6530 XP_ERROR(XPATH_EXPR_ERROR);
6531 }
6532 NEXT;
6533 SKIP_BLANKS;
6534
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006535 xmlXPathCompileExpr(ctxt);
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006536 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
Owen Taylor3473f882001-02-23 17:55:21 +00006537 CHECK_ERROR;
6538
6539 SKIP_BLANKS;
6540 if (CUR != ')') {
6541 XP_ERROR(XPATH_EXPR_ERROR);
6542 }
6543 NEXT;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006544 rangeto = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006545 goto eval_predicates;
6546 }
6547 }
6548#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00006549 if (CUR == '*') {
6550 axis = AXIS_CHILD;
6551 } else {
6552 if (name == NULL)
6553 name = xmlXPathParseNCName(ctxt);
6554 if (name != NULL) {
6555 axis = xmlXPathIsAxisName(name);
6556 if (axis != 0) {
6557 SKIP_BLANKS;
6558 if ((CUR == ':') && (NXT(1) == ':')) {
6559 SKIP(2);
6560 xmlFree(name);
6561 name = NULL;
6562 } else {
6563 /* an element name can conflict with an axis one :-\ */
6564 axis = AXIS_CHILD;
6565 }
Owen Taylor3473f882001-02-23 17:55:21 +00006566 } else {
Owen Taylor3473f882001-02-23 17:55:21 +00006567 axis = AXIS_CHILD;
6568 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006569 } else if (CUR == '@') {
6570 NEXT;
6571 axis = AXIS_ATTRIBUTE;
Owen Taylor3473f882001-02-23 17:55:21 +00006572 } else {
Daniel Veillard2156a562001-04-28 12:24:34 +00006573 axis = AXIS_CHILD;
Owen Taylor3473f882001-02-23 17:55:21 +00006574 }
Owen Taylor3473f882001-02-23 17:55:21 +00006575 }
6576
6577 CHECK_ERROR;
6578
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006579 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00006580 if (test == 0)
6581 return;
6582
6583#ifdef DEBUG_STEP
6584 xmlGenericError(xmlGenericErrorContext,
6585 "Basis : computing new set\n");
6586#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006587
Owen Taylor3473f882001-02-23 17:55:21 +00006588#ifdef DEBUG_STEP
6589 xmlGenericError(xmlGenericErrorContext, "Basis : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006590 if (ctxt->value == NULL)
6591 xmlGenericError(xmlGenericErrorContext, "no value\n");
6592 else if (ctxt->value->nodesetval == NULL)
6593 xmlGenericError(xmlGenericErrorContext, "Empty\n");
6594 else
6595 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00006596#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006597
6598eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006599 op1 = ctxt->comp->last;
6600 ctxt->comp->last = -1;
6601
Owen Taylor3473f882001-02-23 17:55:21 +00006602 SKIP_BLANKS;
6603 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006604 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006605 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006606
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006607#ifdef LIBXML_XPTR_ENABLED
6608 if (rangeto) {
6609 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
6610 } else
6611#endif
6612 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
6613 test, type, (void *)prefix, (void *)name);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006614
Owen Taylor3473f882001-02-23 17:55:21 +00006615 }
6616#ifdef DEBUG_STEP
6617 xmlGenericError(xmlGenericErrorContext, "Step : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006618 if (ctxt->value == NULL)
6619 xmlGenericError(xmlGenericErrorContext, "no value\n");
6620 else if (ctxt->value->nodesetval == NULL)
6621 xmlGenericError(xmlGenericErrorContext, "Empty\n");
6622 else
6623 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6624 ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00006625#endif
6626}
6627
6628/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006629 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006630 * @ctxt: the XPath Parser context
6631 *
6632 * [3] RelativeLocationPath ::= Step
6633 * | RelativeLocationPath '/' Step
6634 * | AbbreviatedRelativeLocationPath
6635 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6636 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006637 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00006638 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006639static void
Owen Taylor3473f882001-02-23 17:55:21 +00006640#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006641xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006642#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006643xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006644#endif
6645(xmlXPathParserContextPtr ctxt) {
6646 SKIP_BLANKS;
6647 if ((CUR == '/') && (NXT(1) == '/')) {
6648 SKIP(2);
6649 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006650 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6651 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006652 } else if (CUR == '/') {
6653 NEXT;
6654 SKIP_BLANKS;
6655 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006656 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006657 SKIP_BLANKS;
6658 while (CUR == '/') {
6659 if ((CUR == '/') && (NXT(1) == '/')) {
6660 SKIP(2);
6661 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006662 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00006663 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006664 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006665 } else if (CUR == '/') {
6666 NEXT;
6667 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006668 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006669 }
6670 SKIP_BLANKS;
6671 }
6672}
6673
6674/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006675 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006676 * @ctxt: the XPath Parser context
6677 *
6678 * [1] LocationPath ::= RelativeLocationPath
6679 * | AbsoluteLocationPath
6680 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6681 * | AbbreviatedAbsoluteLocationPath
6682 * [10] AbbreviatedAbsoluteLocationPath ::=
6683 * '//' RelativeLocationPath
6684 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006685 * Compile a location path
6686 *
Owen Taylor3473f882001-02-23 17:55:21 +00006687 * // is short for /descendant-or-self::node()/. For example,
6688 * //para is short for /descendant-or-self::node()/child::para and
6689 * so will select any para element in the document (even a para element
6690 * that is a document element will be selected by //para since the
6691 * document element node is a child of the root node); div//para is
6692 * short for div/descendant-or-self::node()/child::para and so will
6693 * select all para descendants of div children.
6694 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006695static void
6696xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006697 SKIP_BLANKS;
6698 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006699 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006700 } else {
6701 while (CUR == '/') {
6702 if ((CUR == '/') && (NXT(1) == '/')) {
6703 SKIP(2);
6704 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006705 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6706 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006707 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006708 } else if (CUR == '/') {
6709 NEXT;
6710 SKIP_BLANKS;
6711 if (CUR != 0)
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006712 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006713 }
6714 }
6715 }
6716}
6717
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006718/************************************************************************
6719 * *
6720 * XPath precompiled expression evaluation *
6721 * *
6722 ************************************************************************/
6723
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006724static void
6725xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
6726
6727/**
6728 * xmlXPathNodeCollectAndTest:
6729 * @ctxt: the XPath Parser context
6730 * @op: the XPath precompiled step operation
6731 *
6732 * This is the function implementing a step: based on the current list
6733 * of nodes, it builds up a new list, looking at all nodes under that
6734 * axis and selecting them it also do the predicate filtering
6735 *
6736 * Pushes the new NodeSet resulting from the search.
6737 */
6738static void
6739xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
6740 xmlXPathStepOpPtr op) {
6741 xmlXPathAxisVal axis = op->value;
6742 xmlXPathTestVal test = op->value2;
6743 xmlXPathTypeVal type = op->value3;
6744 const xmlChar *prefix = op->value4;
6745 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006746 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006747
6748#ifdef DEBUG_STEP
6749 int n = 0, t = 0;
6750#endif
6751 int i;
6752 xmlNodeSetPtr ret, list;
6753 xmlXPathTraversalFunction next = NULL;
6754 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
6755 xmlNodePtr cur = NULL;
6756 xmlXPathObjectPtr obj;
6757 xmlNodeSetPtr nodelist;
6758 xmlNodePtr tmp;
6759
6760 CHECK_TYPE(XPATH_NODESET);
6761 obj = valuePop(ctxt);
6762 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006763 if (prefix != NULL) {
6764 URI = xmlXPathNsLookup(ctxt->context, prefix);
6765 if (URI == NULL)
6766 XP_ERROR(XPATH_UNDEF_PREFIX_ERROR);
6767 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006768
6769#ifdef DEBUG_STEP
6770 xmlGenericError(xmlGenericErrorContext,
6771 "new step : ");
6772#endif
6773 switch (axis) {
6774 case AXIS_ANCESTOR:
6775#ifdef DEBUG_STEP
6776 xmlGenericError(xmlGenericErrorContext,
6777 "axis 'ancestors' ");
6778#endif
6779 next = xmlXPathNextAncestor; break;
6780 case AXIS_ANCESTOR_OR_SELF:
6781#ifdef DEBUG_STEP
6782 xmlGenericError(xmlGenericErrorContext,
6783 "axis 'ancestors-or-self' ");
6784#endif
6785 next = xmlXPathNextAncestorOrSelf; break;
6786 case AXIS_ATTRIBUTE:
6787#ifdef DEBUG_STEP
6788 xmlGenericError(xmlGenericErrorContext,
6789 "axis 'attributes' ");
6790#endif
6791 next = xmlXPathNextAttribute; break;
6792 break;
6793 case AXIS_CHILD:
6794#ifdef DEBUG_STEP
6795 xmlGenericError(xmlGenericErrorContext,
6796 "axis 'child' ");
6797#endif
6798 next = xmlXPathNextChild; break;
6799 case AXIS_DESCENDANT:
6800#ifdef DEBUG_STEP
6801 xmlGenericError(xmlGenericErrorContext,
6802 "axis 'descendant' ");
6803#endif
6804 next = xmlXPathNextDescendant; break;
6805 case AXIS_DESCENDANT_OR_SELF:
6806#ifdef DEBUG_STEP
6807 xmlGenericError(xmlGenericErrorContext,
6808 "axis 'descendant-or-self' ");
6809#endif
6810 next = xmlXPathNextDescendantOrSelf; break;
6811 case AXIS_FOLLOWING:
6812#ifdef DEBUG_STEP
6813 xmlGenericError(xmlGenericErrorContext,
6814 "axis 'following' ");
6815#endif
6816 next = xmlXPathNextFollowing; break;
6817 case AXIS_FOLLOWING_SIBLING:
6818#ifdef DEBUG_STEP
6819 xmlGenericError(xmlGenericErrorContext,
6820 "axis 'following-siblings' ");
6821#endif
6822 next = xmlXPathNextFollowingSibling; break;
6823 case AXIS_NAMESPACE:
6824#ifdef DEBUG_STEP
6825 xmlGenericError(xmlGenericErrorContext,
6826 "axis 'namespace' ");
6827#endif
6828 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
6829 break;
6830 case AXIS_PARENT:
6831#ifdef DEBUG_STEP
6832 xmlGenericError(xmlGenericErrorContext,
6833 "axis 'parent' ");
6834#endif
6835 next = xmlXPathNextParent; break;
6836 case AXIS_PRECEDING:
6837#ifdef DEBUG_STEP
6838 xmlGenericError(xmlGenericErrorContext,
6839 "axis 'preceding' ");
6840#endif
6841 next = xmlXPathNextPreceding; break;
6842 case AXIS_PRECEDING_SIBLING:
6843#ifdef DEBUG_STEP
6844 xmlGenericError(xmlGenericErrorContext,
6845 "axis 'preceding-sibling' ");
6846#endif
6847 next = xmlXPathNextPrecedingSibling; break;
6848 case AXIS_SELF:
6849#ifdef DEBUG_STEP
6850 xmlGenericError(xmlGenericErrorContext,
6851 "axis 'self' ");
6852#endif
6853 next = xmlXPathNextSelf; break;
6854 }
6855 if (next == NULL)
6856 return;
6857
6858 nodelist = obj->nodesetval;
6859 if (nodelist == NULL) {
6860 xmlXPathFreeObject(obj);
6861 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
6862 return;
6863 }
6864 addNode = xmlXPathNodeSetAddUnique;
6865 ret = NULL;
6866#ifdef DEBUG_STEP
6867 xmlGenericError(xmlGenericErrorContext,
6868 " context contains %d nodes\n",
6869 nodelist->nodeNr);
6870 switch (test) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006871 case NODE_TEST_NONE:
6872 xmlGenericError(xmlGenericErrorContext,
6873 " searching for none !!!\n");
6874 break;
6875 case NODE_TEST_TYPE:
6876 xmlGenericError(xmlGenericErrorContext,
6877 " searching for type %d\n", type);
6878 break;
6879 case NODE_TEST_PI:
6880 xmlGenericError(xmlGenericErrorContext,
6881 " searching for PI !!!\n");
6882 break;
6883 case NODE_TEST_ALL:
6884 xmlGenericError(xmlGenericErrorContext,
6885 " searching for *\n");
6886 break;
6887 case NODE_TEST_NS:
6888 xmlGenericError(xmlGenericErrorContext,
6889 " searching for namespace %s\n",
6890 prefix);
6891 break;
6892 case NODE_TEST_NAME:
6893 xmlGenericError(xmlGenericErrorContext,
6894 " searching for name %s\n", name);
6895 if (prefix != NULL)
6896 xmlGenericError(xmlGenericErrorContext,
6897 " with namespace %s\n",
6898 prefix);
6899 break;
6900 }
6901 xmlGenericError(xmlGenericErrorContext, "Testing : ");
6902#endif
6903 /*
6904 * 2.3 Node Tests
6905 * - For the attribute axis, the principal node type is attribute.
6906 * - For the namespace axis, the principal node type is namespace.
6907 * - For other axes, the principal node type is element.
6908 *
6909 * A node test * is true for any node of the
6910 * principal node type. For example, child::* willi
6911 * select all element children of the context node
6912 */
6913 tmp = ctxt->context->node;
6914 for (i = 0;i < nodelist->nodeNr; i++) {
6915 ctxt->context->node = nodelist->nodeTab[i];
6916
6917 cur = NULL;
6918 list = xmlXPathNodeSetCreate(NULL);
6919 do {
6920 cur = next(ctxt, cur);
6921 if (cur == NULL) break;
6922#ifdef DEBUG_STEP
6923 t++;
6924 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
6925#endif
6926 switch (test) {
6927 case NODE_TEST_NONE:
6928 ctxt->context->node = tmp;
6929 STRANGE
6930 return;
6931 case NODE_TEST_TYPE:
6932 if ((cur->type == type) ||
6933 ((type == NODE_TYPE_NODE) &&
6934 ((cur->type == XML_DOCUMENT_NODE) ||
6935 (cur->type == XML_HTML_DOCUMENT_NODE) ||
6936 (cur->type == XML_ELEMENT_NODE) ||
6937 (cur->type == XML_PI_NODE) ||
6938 (cur->type == XML_COMMENT_NODE) ||
6939 (cur->type == XML_CDATA_SECTION_NODE) ||
6940 (cur->type == XML_TEXT_NODE)))) {
6941#ifdef DEBUG_STEP
6942 n++;
6943#endif
6944 addNode(list, cur);
6945 }
6946 break;
6947 case NODE_TEST_PI:
6948 if (cur->type == XML_PI_NODE) {
6949 if ((name != NULL) &&
6950 (!xmlStrEqual(name, cur->name)))
6951 break;
6952#ifdef DEBUG_STEP
6953 n++;
6954#endif
6955 addNode(list, cur);
6956 }
6957 break;
6958 case NODE_TEST_ALL:
6959 if (axis == AXIS_ATTRIBUTE) {
6960 if (cur->type == XML_ATTRIBUTE_NODE) {
6961#ifdef DEBUG_STEP
6962 n++;
6963#endif
6964 addNode(list, cur);
6965 }
6966 } else if (axis == AXIS_NAMESPACE) {
6967 if (cur->type == XML_NAMESPACE_DECL) {
6968#ifdef DEBUG_STEP
6969 n++;
6970#endif
6971 addNode(list, cur);
6972 }
6973 } else {
6974 if ((cur->type == XML_ELEMENT_NODE) ||
6975 (cur->type == XML_DOCUMENT_NODE) ||
6976 (cur->type == XML_HTML_DOCUMENT_NODE)) {
6977 if (prefix == NULL) {
6978#ifdef DEBUG_STEP
6979 n++;
6980#endif
6981 addNode(list, cur);
6982 } else if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006983 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006984 cur->ns->href))) {
6985#ifdef DEBUG_STEP
6986 n++;
6987#endif
6988 addNode(list, cur);
6989 }
6990 }
6991 }
6992 break;
6993 case NODE_TEST_NS: {
6994 TODO;
6995 break;
6996 }
6997 case NODE_TEST_NAME:
6998 switch (cur->type) {
6999 case XML_ELEMENT_NODE:
7000 if (xmlStrEqual(name, cur->name)) {
7001 if (prefix == NULL) {
7002 if ((cur->ns == NULL) ||
7003 (cur->ns->prefix == NULL)) {
7004#ifdef DEBUG_STEP
7005 n++;
7006#endif
7007 addNode(list, cur);
7008 }
7009 } else {
7010 if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00007011 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007012 cur->ns->href))) {
7013#ifdef DEBUG_STEP
7014 n++;
7015#endif
7016 addNode(list, cur);
7017 }
7018 }
7019 }
7020 break;
7021 case XML_ATTRIBUTE_NODE: {
7022 xmlAttrPtr attr = (xmlAttrPtr) cur;
7023 if (xmlStrEqual(name, attr->name)) {
7024 if (prefix == NULL) {
7025 if ((attr->ns == NULL) ||
7026 (attr->ns->prefix == NULL)) {
7027#ifdef DEBUG_STEP
7028 n++;
7029#endif
7030 addNode(list, (xmlNodePtr) attr);
7031 }
7032 } else {
7033 if ((attr->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00007034 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007035 attr->ns->href))) {
7036#ifdef DEBUG_STEP
7037 n++;
7038#endif
7039 addNode(list, (xmlNodePtr) attr);
7040 }
7041 }
7042 }
7043 break;
7044 }
7045 case XML_NAMESPACE_DECL: {
7046 TODO;
7047 break;
7048 }
7049 default:
7050 break;
7051 }
7052 break;
7053 }
7054 } while (cur != NULL);
7055
7056 /*
7057 * If there is some predicate filtering do it now
7058 */
7059 if (op->ch2 != -1) {
7060 xmlXPathObjectPtr obj2;
7061
7062 valuePush(ctxt, xmlXPathWrapNodeSet(list));
7063 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
7064 CHECK_TYPE(XPATH_NODESET);
7065 obj2 = valuePop(ctxt);
7066 list = obj2->nodesetval;
7067 obj2->nodesetval = NULL;
7068 xmlXPathFreeObject(obj2);
7069 }
7070 if (ret == NULL) {
7071 ret = list;
7072 } else {
7073 ret = xmlXPathNodeSetMerge(ret, list);
7074 xmlXPathFreeNodeSet(list);
7075 }
7076 }
7077 ctxt->context->node = tmp;
7078#ifdef DEBUG_STEP
7079 xmlGenericError(xmlGenericErrorContext,
7080 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
7081#endif
7082 xmlXPathFreeObject(obj);
7083 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
7084}
7085
Owen Taylor3473f882001-02-23 17:55:21 +00007086/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007087 * xmlXPathCompOpEval:
7088 * @ctxt: the XPath parser context with the compiled expression
7089 * @op: an XPath compiled operation
7090 *
7091 * Evaluate the Precompiled XPath operation
7092 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007093static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007094xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) {
7095 int equal, ret;
7096 xmlXPathCompExprPtr comp;
7097 xmlXPathObjectPtr arg1, arg2;
7098
7099 comp = ctxt->comp;
7100 switch (op->op) {
7101 case XPATH_OP_END:
7102 return;
7103 case XPATH_OP_AND:
7104 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7105 xmlXPathBooleanFunction(ctxt, 1);
7106 if (ctxt->value->boolval == 0)
7107 return;
7108 arg2 = valuePop(ctxt);
7109 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7110 xmlXPathBooleanFunction(ctxt, 1);
7111 arg1 = valuePop(ctxt);
7112 arg1->boolval &= arg2->boolval;
7113 valuePush(ctxt, arg1);
7114 xmlXPathFreeObject(arg2);
7115 return;
7116 case XPATH_OP_OR:
7117 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7118 xmlXPathBooleanFunction(ctxt, 1);
7119 if (ctxt->value->boolval == 1)
7120 return;
7121 arg2 = valuePop(ctxt);
7122 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7123 xmlXPathBooleanFunction(ctxt, 1);
7124 arg1 = valuePop(ctxt);
7125 arg1->boolval |= arg2->boolval;
7126 valuePush(ctxt, arg1);
7127 xmlXPathFreeObject(arg2);
7128 return;
7129 case XPATH_OP_EQUAL:
7130 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7131 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7132 equal = xmlXPathEqualValues(ctxt);
7133 if (op->value) valuePush(ctxt, xmlXPathNewBoolean(equal));
7134 else valuePush(ctxt, xmlXPathNewBoolean(!equal));
7135 return;
7136 case XPATH_OP_CMP:
7137 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7138 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7139 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
7140 valuePush(ctxt, xmlXPathNewBoolean(ret));
7141 return;
7142 case XPATH_OP_PLUS:
7143 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7144 if (op->ch2 != -1)
7145 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7146 if (op->value == 0) xmlXPathSubValues(ctxt);
7147 else if (op->value == 1) xmlXPathAddValues(ctxt);
7148 else if (op->value == 2) xmlXPathValueFlipSign(ctxt);
7149 else if (op->value == 3) {
7150 xmlXPathObjectPtr arg;
7151
7152 POP_FLOAT
7153 valuePush(ctxt, arg);
7154 }
7155 return;
7156 case XPATH_OP_MULT:
7157 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7158 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7159 if (op->value == 0) xmlXPathMultValues(ctxt);
7160 else if (op->value == 1) xmlXPathDivValues(ctxt);
7161 else if (op->value == 2) xmlXPathModValues(ctxt);
7162 return;
7163 case XPATH_OP_UNION:
7164 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7165 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7166 CHECK_TYPE(XPATH_NODESET);
7167 arg2 = valuePop(ctxt);
7168
7169 CHECK_TYPE(XPATH_NODESET);
7170 arg1 = valuePop(ctxt);
7171
7172 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
7173 arg2->nodesetval);
7174 valuePush(ctxt, arg1);
7175 xmlXPathFreeObject(arg2);
7176 return;
7177 case XPATH_OP_ROOT:
7178 xmlXPathRoot(ctxt);
7179 return;
7180 case XPATH_OP_NODE:
7181 if (op->ch1 != -1)
7182 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7183 if (op->ch2 != -1)
7184 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7185 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
7186 return;
7187 case XPATH_OP_RESET:
7188 if (op->ch1 != -1)
7189 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7190 if (op->ch2 != -1)
7191 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7192 ctxt->context->node = NULL;
7193 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007194 case XPATH_OP_COLLECT: {
7195 if (op->ch1 == -1)
7196 return;
7197
7198 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7199 xmlXPathNodeCollectAndTest(ctxt, op);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007200 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007201 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007202 case XPATH_OP_VALUE:
7203 valuePush(ctxt,
7204 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
7205 return;
7206 case XPATH_OP_VARIABLE: {
7207 if (op->ch1 != -1)
7208 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7209 if (op->value5 == NULL)
7210 valuePush(ctxt,
7211 xmlXPathVariableLookup(ctxt->context, op->value4));
7212 else {
7213 const xmlChar *URI;
7214 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7215 if (URI == NULL) {
7216 xmlGenericError(xmlGenericErrorContext,
7217 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
7218 op->value4, op->value5);
7219 return;
7220 }
7221 valuePush(ctxt,
7222 xmlXPathVariableLookupNS(ctxt->context,
7223 op->value4, URI));
7224 }
7225 return;
7226 }
7227 case XPATH_OP_FUNCTION: {
7228 xmlXPathFunction func;
7229
7230 if (op->ch1 != -1)
7231 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7232 if (op->value5 == NULL)
7233 func = xmlXPathFunctionLookup(ctxt->context, op->value4);
7234 else {
7235 const xmlChar *URI;
7236 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7237 if (URI == NULL) {
7238 xmlGenericError(xmlGenericErrorContext,
7239 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
7240 op->value4, op->value5);
7241 return;
7242 }
7243 func = xmlXPathFunctionLookupNS(ctxt->context,
7244 op->value4, URI);
7245 }
7246 if (func == NULL) {
7247 xmlGenericError(xmlGenericErrorContext,
7248 "xmlXPathRunEval: function %s not found\n",
7249 op->value4);
7250 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
7251 return;
7252 }
7253 func(ctxt, op->value);
7254 return;
7255 }
7256 case XPATH_OP_ARG:
7257 if (op->ch1 != -1)
7258 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7259 if (op->ch2 != -1)
7260 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7261 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007262 case XPATH_OP_PREDICATE:
7263 case XPATH_OP_FILTER: {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007264 xmlXPathObjectPtr res;
7265 xmlXPathObjectPtr obj, tmp;
7266 xmlNodeSetPtr newset = NULL;
7267 xmlNodeSetPtr oldset;
7268 xmlNodePtr oldnode;
7269 int i;
7270
7271 if (op->ch1 != -1)
7272 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7273 if (op->ch2 == -1)
7274 return;
7275
7276 oldnode = ctxt->context->node;
7277
7278#ifdef LIBXML_XPTR_ENABLED
7279 /*
7280 * Hum are we filtering the result of an XPointer expression
7281 */
7282 if (ctxt->value->type == XPATH_LOCATIONSET) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007283 xmlLocationSetPtr newlocset = NULL;
7284 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007285
7286 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007287 * Extract the old locset, and then evaluate the result of the
7288 * expression for all the element in the locset. use it to grow
7289 * up a new locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007290 */
7291 CHECK_TYPE(XPATH_LOCATIONSET);
7292 obj = valuePop(ctxt);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007293 oldlocset = obj->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007294 ctxt->context->node = NULL;
7295
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007296 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007297 ctxt->context->contextSize = 0;
7298 ctxt->context->proximityPosition = 0;
7299 if (op->ch2 != -1)
7300 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7301 res = valuePop(ctxt);
7302 if (res != NULL)
7303 xmlXPathFreeObject(res);
7304 valuePush(ctxt, obj);
7305 CHECK_ERROR;
7306 return;
7307 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007308 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007309
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007310 for (i = 0; i < oldlocset->locNr; i++) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007311 /*
7312 * Run the evaluation with a node list made of a
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007313 * single item in the nodelocset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007314 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007315 ctxt->context->node = oldlocset->locTab[i]->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007316 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7317 valuePush(ctxt, tmp);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007318 ctxt->context->contextSize = oldlocset->locNr;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007319 ctxt->context->proximityPosition = i + 1;
7320
7321 if (op->ch2 != -1)
7322 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7323 CHECK_ERROR;
7324
7325 /*
7326 * The result of the evaluation need to be tested to
7327 * decided whether the filter succeeded or not
7328 */
7329 res = valuePop(ctxt);
7330 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007331 xmlXPtrLocationSetAdd(newlocset,
7332 xmlXPathObjectCopy(oldlocset->locTab[i]));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007333 }
7334
7335 /*
7336 * Cleanup
7337 */
7338 if (res != NULL)
7339 xmlXPathFreeObject(res);
7340 if (ctxt->value == tmp) {
7341 res = valuePop(ctxt);
7342 xmlXPathFreeObject(res);
7343 }
7344
7345 ctxt->context->node = NULL;
7346 }
7347
7348 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007349 * The result is used as the new evaluation locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007350 */
7351 xmlXPathFreeObject(obj);
7352 ctxt->context->node = NULL;
7353 ctxt->context->contextSize = -1;
7354 ctxt->context->proximityPosition = -1;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007355 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007356 ctxt->context->node = oldnode;
7357 return;
7358 }
7359#endif /* LIBXML_XPTR_ENABLED */
7360
7361 /*
7362 * Extract the old set, and then evaluate the result of the
7363 * expression for all the element in the set. use it to grow
7364 * up a new set.
7365 */
7366 CHECK_TYPE(XPATH_NODESET);
7367 obj = valuePop(ctxt);
7368 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00007369
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007370 oldnode = ctxt->context->node;
7371 ctxt->context->node = NULL;
7372
7373 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
7374 ctxt->context->contextSize = 0;
7375 ctxt->context->proximityPosition = 0;
7376 if (op->ch2 != -1)
7377 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7378 res = valuePop(ctxt);
7379 if (res != NULL)
7380 xmlXPathFreeObject(res);
7381 valuePush(ctxt, obj);
Daniel Veillard911f49a2001-04-07 15:39:35 +00007382 ctxt->context->node = oldnode;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007383 CHECK_ERROR;
7384 } else {
7385 /*
7386 * Initialize the new set.
7387 */
7388 newset = xmlXPathNodeSetCreate(NULL);
7389
7390 for (i = 0; i < oldset->nodeNr; i++) {
7391 /*
7392 * Run the evaluation with a node list made of
7393 * a single item in the nodeset.
7394 */
7395 ctxt->context->node = oldset->nodeTab[i];
7396 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7397 valuePush(ctxt, tmp);
7398 ctxt->context->contextSize = oldset->nodeNr;
7399 ctxt->context->proximityPosition = i + 1;
7400
7401 if (op->ch2 != -1)
7402 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7403 CHECK_ERROR;
7404
7405 /*
7406 * The result of the evaluation need to be tested to
7407 * decided whether the filter succeeded or not
7408 */
7409 res = valuePop(ctxt);
7410 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
7411 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
7412 }
7413
7414 /*
7415 * Cleanup
7416 */
7417 if (res != NULL)
7418 xmlXPathFreeObject(res);
7419 if (ctxt->value == tmp) {
7420 res = valuePop(ctxt);
7421 xmlXPathFreeObject(res);
7422 }
7423
7424 ctxt->context->node = NULL;
7425 }
7426
7427 /*
7428 * The result is used as the new evaluation set.
7429 */
7430 xmlXPathFreeObject(obj);
7431 ctxt->context->node = NULL;
7432 ctxt->context->contextSize = -1;
7433 ctxt->context->proximityPosition = -1;
7434 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
7435 }
7436 ctxt->context->node = oldnode;
7437 return;
7438 }
7439 case XPATH_OP_SORT:
7440 if (op->ch1 != -1)
7441 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7442 if ((ctxt->value != NULL) &&
7443 (ctxt->value->type == XPATH_NODESET) &&
7444 (ctxt->value->nodesetval != NULL))
7445 xmlXPathNodeSetSort(ctxt->value->nodesetval);
7446 return;
7447#ifdef LIBXML_XPTR_ENABLED
7448 case XPATH_OP_RANGETO: {
7449 xmlXPathObjectPtr range;
7450 xmlXPathObjectPtr res, obj;
7451 xmlXPathObjectPtr tmp;
7452 xmlLocationSetPtr newset = NULL;
7453 xmlNodeSetPtr oldset;
7454 int i;
7455
7456 if (op->ch1 != -1)
7457 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7458 if (op->ch2 == -1)
7459 return;
7460
7461 CHECK_TYPE(XPATH_NODESET);
7462 obj = valuePop(ctxt);
7463 oldset = obj->nodesetval;
7464 ctxt->context->node = NULL;
7465
7466 newset = xmlXPtrLocationSetCreate(NULL);
7467
Daniel Veillard911f49a2001-04-07 15:39:35 +00007468 if (oldset != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007469 for (i = 0; i < oldset->nodeNr; i++) {
7470 /*
7471 * Run the evaluation with a node list made of a single item
7472 * in the nodeset.
7473 */
7474 ctxt->context->node = oldset->nodeTab[i];
7475 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7476 valuePush(ctxt, tmp);
7477
7478 if (op->ch2 != -1)
7479 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7480 CHECK_ERROR;
7481
7482 /*
7483 * The result of the evaluation need to be tested to
7484 * decided whether the filter succeeded or not
7485 */
7486 res = valuePop(ctxt);
7487 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
7488 if (range != NULL) {
7489 xmlXPtrLocationSetAdd(newset, range);
7490 }
7491
7492 /*
7493 * Cleanup
7494 */
7495 if (res != NULL)
7496 xmlXPathFreeObject(res);
7497 if (ctxt->value == tmp) {
7498 res = valuePop(ctxt);
7499 xmlXPathFreeObject(res);
7500 }
7501
7502 ctxt->context->node = NULL;
7503 }
Daniel Veillard911f49a2001-04-07 15:39:35 +00007504 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007505
7506 /*
7507 * The result is used as the new evaluation set.
7508 */
7509 xmlXPathFreeObject(obj);
7510 ctxt->context->node = NULL;
7511 ctxt->context->contextSize = -1;
7512 ctxt->context->proximityPosition = -1;
7513 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
7514 return;
7515 }
7516#endif /* LIBXML_XPTR_ENABLED */
7517 }
7518 xmlGenericError(xmlGenericErrorContext,
7519 "XPath: unknown precompiled operation %d\n",
7520 op->op);
7521 return;
7522}
7523
7524/**
7525 * xmlXPathRunEval:
7526 * @ctxt: the XPath parser context with the compiled expression
7527 *
7528 * Evaluate the Precompiled XPath expression in the given context.
7529 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007530static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007531xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
7532 xmlXPathCompExprPtr comp;
7533
7534 if ((ctxt == NULL) || (ctxt->comp == NULL))
7535 return;
7536
7537 if (ctxt->valueTab == NULL) {
7538 /* Allocate the value stack */
7539 ctxt->valueTab = (xmlXPathObjectPtr *)
7540 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
7541 if (ctxt->valueTab == NULL) {
7542 xmlFree(ctxt);
7543 xmlGenericError(xmlGenericErrorContext,
7544 "xmlXPathRunEval: out of memory\n");
7545 return;
7546 }
7547 ctxt->valueNr = 0;
7548 ctxt->valueMax = 10;
7549 ctxt->value = NULL;
7550 }
7551 comp = ctxt->comp;
7552 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
7553}
7554
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007555/************************************************************************
7556 * *
7557 * Public interfaces *
7558 * *
7559 ************************************************************************/
7560
7561/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007562 * xmlXPathEvalPredicate:
7563 * @ctxt: the XPath context
7564 * @res: the Predicate Expression evaluation result
7565 *
7566 * Evaluate a predicate result for the current node.
7567 * A PredicateExpr is evaluated by evaluating the Expr and converting
7568 * the result to a boolean. If the result is a number, the result will
7569 * be converted to true if the number is equal to the position of the
7570 * context node in the context node list (as returned by the position
7571 * function) and will be converted to false otherwise; if the result
7572 * is not a number, then the result will be converted as if by a call
7573 * to the boolean function.
7574 *
7575 * Return 1 if predicate is true, 0 otherwise
7576 */
7577int
7578xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
7579 if (res == NULL) return(0);
7580 switch (res->type) {
7581 case XPATH_BOOLEAN:
7582 return(res->boolval);
7583 case XPATH_NUMBER:
7584 return(res->floatval == ctxt->proximityPosition);
7585 case XPATH_NODESET:
7586 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007587 if (res->nodesetval == NULL)
7588 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007589 return(res->nodesetval->nodeNr != 0);
7590 case XPATH_STRING:
7591 return((res->stringval != NULL) &&
7592 (xmlStrlen(res->stringval) != 0));
7593 default:
7594 STRANGE
7595 }
7596 return(0);
7597}
7598
7599/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007600 * xmlXPathEvaluatePredicateResult:
7601 * @ctxt: the XPath Parser context
7602 * @res: the Predicate Expression evaluation result
7603 *
7604 * Evaluate a predicate result for the current node.
7605 * A PredicateExpr is evaluated by evaluating the Expr and converting
7606 * the result to a boolean. If the result is a number, the result will
7607 * be converted to true if the number is equal to the position of the
7608 * context node in the context node list (as returned by the position
7609 * function) and will be converted to false otherwise; if the result
7610 * is not a number, then the result will be converted as if by a call
7611 * to the boolean function.
7612 *
7613 * Return 1 if predicate is true, 0 otherwise
7614 */
7615int
7616xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
7617 xmlXPathObjectPtr res) {
7618 if (res == NULL) return(0);
7619 switch (res->type) {
7620 case XPATH_BOOLEAN:
7621 return(res->boolval);
7622 case XPATH_NUMBER:
7623 return(res->floatval == ctxt->context->proximityPosition);
7624 case XPATH_NODESET:
7625 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00007626 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00007627 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007628 return(res->nodesetval->nodeNr != 0);
7629 case XPATH_STRING:
7630 return((res->stringval != NULL) &&
7631 (xmlStrlen(res->stringval) != 0));
7632 default:
7633 STRANGE
7634 }
7635 return(0);
7636}
7637
7638/**
7639 * xmlXPathCompile:
7640 * @str: the XPath expression
7641 *
7642 * Compile an XPath expression
7643 *
7644 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7645 * the caller has to free the object.
7646 */
7647xmlXPathCompExprPtr
7648xmlXPathCompile(const xmlChar *str) {
7649 xmlXPathParserContextPtr ctxt;
7650 xmlXPathCompExprPtr comp;
7651
7652 xmlXPathInit();
7653
7654 ctxt = xmlXPathNewParserContext(str, NULL);
7655 xmlXPathCompileExpr(ctxt);
7656
Daniel Veillard40af6492001-04-22 08:50:55 +00007657 if (*ctxt->cur != 0) {
7658 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7659 comp = NULL;
7660 } else {
7661 comp = ctxt->comp;
7662 ctxt->comp = NULL;
7663 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007664 xmlXPathFreeParserContext(ctxt);
7665 return(comp);
7666}
7667
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007668/**
7669 * xmlXPathCompiledEval:
7670 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00007671 * @ctx: the XPath context
7672 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007673 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00007674 *
7675 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7676 * the caller has to free the object.
7677 */
7678xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007679xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00007680 xmlXPathParserContextPtr ctxt;
7681 xmlXPathObjectPtr res, tmp, init = NULL;
7682 int stack = 0;
7683
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007684 if ((comp == NULL) || (ctx == NULL))
7685 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007686 xmlXPathInit();
7687
7688 CHECK_CONTEXT(ctx)
7689
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007690 ctxt = xmlXPathCompParserContext(comp, ctx);
7691 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007692
7693 if (ctxt->value == NULL) {
7694 xmlGenericError(xmlGenericErrorContext,
7695 "xmlXPathEval: evaluation failed\n");
7696 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00007697 } else {
7698 res = valuePop(ctxt);
7699 }
7700
7701 do {
7702 tmp = valuePop(ctxt);
7703 if (tmp != NULL) {
7704 if (tmp != init)
7705 stack++;
7706 xmlXPathFreeObject(tmp);
7707 }
7708 } while (tmp != NULL);
7709 if ((stack != 0) && (res != NULL)) {
7710 xmlGenericError(xmlGenericErrorContext,
7711 "xmlXPathEval: %d object left on the stack\n",
7712 stack);
7713 }
7714 if (ctxt->error != XPATH_EXPRESSION_OK) {
7715 xmlXPathFreeObject(res);
7716 res = NULL;
7717 }
7718
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007719
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007720 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007721 xmlXPathFreeParserContext(ctxt);
7722 return(res);
7723}
7724
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007725/**
7726 * xmlXPathEvalExpr:
7727 * @ctxt: the XPath Parser context
7728 *
7729 * Parse and evaluate an XPath expression in the given context,
7730 * then push the result on the context stack
7731 */
7732void
7733xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
7734 xmlXPathCompileExpr(ctxt);
7735 xmlXPathRunEval(ctxt);
7736}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007737
7738/**
7739 * xmlXPathEval:
7740 * @str: the XPath expression
7741 * @ctx: the XPath context
7742 *
7743 * Evaluate the XPath Location Path in the given context.
7744 *
7745 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7746 * the caller has to free the object.
7747 */
7748xmlXPathObjectPtr
7749xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
7750 xmlXPathParserContextPtr ctxt;
7751 xmlXPathObjectPtr res, tmp, init = NULL;
7752 int stack = 0;
7753
7754 xmlXPathInit();
7755
7756 CHECK_CONTEXT(ctx)
7757
7758 ctxt = xmlXPathNewParserContext(str, ctx);
7759 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007760
7761 if (ctxt->value == NULL) {
7762 xmlGenericError(xmlGenericErrorContext,
7763 "xmlXPathEval: evaluation failed\n");
7764 res = NULL;
7765 } else if (*ctxt->cur != 0) {
7766 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7767 res = NULL;
7768 } else {
7769 res = valuePop(ctxt);
7770 }
7771
7772 do {
7773 tmp = valuePop(ctxt);
7774 if (tmp != NULL) {
7775 if (tmp != init)
7776 stack++;
7777 xmlXPathFreeObject(tmp);
7778 }
7779 } while (tmp != NULL);
7780 if ((stack != 0) && (res != NULL)) {
7781 xmlGenericError(xmlGenericErrorContext,
7782 "xmlXPathEval: %d object left on the stack\n",
7783 stack);
7784 }
7785 if (ctxt->error != XPATH_EXPRESSION_OK) {
7786 xmlXPathFreeObject(res);
7787 res = NULL;
7788 }
7789
Owen Taylor3473f882001-02-23 17:55:21 +00007790 xmlXPathFreeParserContext(ctxt);
7791 return(res);
7792}
7793
7794/**
7795 * xmlXPathEvalExpression:
7796 * @str: the XPath expression
7797 * @ctxt: the XPath context
7798 *
7799 * Evaluate the XPath expression in the given context.
7800 *
7801 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
7802 * the caller has to free the object.
7803 */
7804xmlXPathObjectPtr
7805xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
7806 xmlXPathParserContextPtr pctxt;
7807 xmlXPathObjectPtr res, tmp;
7808 int stack = 0;
7809
7810 xmlXPathInit();
7811
7812 CHECK_CONTEXT(ctxt)
7813
7814 pctxt = xmlXPathNewParserContext(str, ctxt);
7815 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007816
7817 if (*pctxt->cur != 0) {
7818 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7819 res = NULL;
7820 } else {
7821 res = valuePop(pctxt);
7822 }
7823 do {
7824 tmp = valuePop(pctxt);
7825 if (tmp != NULL) {
7826 xmlXPathFreeObject(tmp);
7827 stack++;
7828 }
7829 } while (tmp != NULL);
7830 if ((stack != 0) && (res != NULL)) {
7831 xmlGenericError(xmlGenericErrorContext,
7832 "xmlXPathEvalExpression: %d object left on the stack\n",
7833 stack);
7834 }
7835 xmlXPathFreeParserContext(pctxt);
7836 return(res);
7837}
7838
7839/**
7840 * xmlXPathRegisterAllFunctions:
7841 * @ctxt: the XPath context
7842 *
7843 * Registers all default XPath functions in this context
7844 */
7845void
7846xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
7847{
7848 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
7849 xmlXPathBooleanFunction);
7850 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
7851 xmlXPathCeilingFunction);
7852 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
7853 xmlXPathCountFunction);
7854 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
7855 xmlXPathConcatFunction);
7856 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
7857 xmlXPathContainsFunction);
7858 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
7859 xmlXPathIdFunction);
7860 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
7861 xmlXPathFalseFunction);
7862 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
7863 xmlXPathFloorFunction);
7864 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
7865 xmlXPathLastFunction);
7866 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
7867 xmlXPathLangFunction);
7868 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
7869 xmlXPathLocalNameFunction);
7870 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
7871 xmlXPathNotFunction);
7872 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
7873 xmlXPathNameFunction);
7874 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
7875 xmlXPathNamespaceURIFunction);
7876 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
7877 xmlXPathNormalizeFunction);
7878 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
7879 xmlXPathNumberFunction);
7880 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
7881 xmlXPathPositionFunction);
7882 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
7883 xmlXPathRoundFunction);
7884 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
7885 xmlXPathStringFunction);
7886 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
7887 xmlXPathStringLengthFunction);
7888 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
7889 xmlXPathStartsWithFunction);
7890 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
7891 xmlXPathSubstringFunction);
7892 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
7893 xmlXPathSubstringBeforeFunction);
7894 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
7895 xmlXPathSubstringAfterFunction);
7896 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
7897 xmlXPathSumFunction);
7898 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
7899 xmlXPathTrueFunction);
7900 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
7901 xmlXPathTranslateFunction);
7902}
7903
7904#endif /* LIBXML_XPATH_ENABLED */