blob: d533a59fc80341210551f70e36831d49b99d377e [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
19#ifdef WIN32
20#include "win32config.h"
21#else
22#include "config.h"
23#endif
24
25#include <libxml/xmlversion.h>
26#ifdef LIBXML_XPATH_ENABLED
27
28#include <stdio.h>
29#include <string.h>
30
31#ifdef HAVE_SYS_TYPES_H
32#include <sys/types.h>
33#endif
34#ifdef HAVE_MATH_H
35#include <math.h>
36#endif
37#ifdef HAVE_FLOAT_H
38#include <float.h>
39#endif
40#ifdef HAVE_IEEEFP_H
41#include <ieeefp.h>
42#endif
43#ifdef HAVE_NAN_H
44#include <nan.h>
45#endif
46#ifdef HAVE_CTYPE_H
47#include <ctype.h>
48#endif
49
50#include <libxml/xmlmemory.h>
51#include <libxml/tree.h>
52#include <libxml/valid.h>
53#include <libxml/xpath.h>
54#include <libxml/xpathInternals.h>
55#include <libxml/parserInternals.h>
56#include <libxml/hash.h>
57#ifdef LIBXML_XPTR_ENABLED
58#include <libxml/xpointer.h>
59#endif
60#ifdef LIBXML_DEBUG_ENABLED
61#include <libxml/debugXML.h>
62#endif
63#include <libxml/xmlerror.h>
64
65/* #define DEBUG */
66/* #define DEBUG_STEP */
67/* #define DEBUG_EXPR */
68
69void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
70double xmlXPathStringEvalNumber(const xmlChar *str);
71
Daniel Veillard9e7160d2001-03-18 23:17:47 +000072/************************************************************************
73 * *
74 * Floating point stuff *
75 * *
76 ************************************************************************/
77
Owen Taylor3473f882001-02-23 17:55:21 +000078/*
Owen Taylor3473f882001-02-23 17:55:21 +000079 * The lack of portability of this section of the libc is annoying !
80 */
81double xmlXPathNAN = 0;
82double xmlXPathPINF = 1;
83double xmlXPathNINF = -1;
84
85#ifndef isinf
86#ifndef HAVE_ISINF
87
88#if HAVE_FPCLASS
89
90int isinf(double d) {
91 fpclass_t type = fpclass(d);
92 switch (type) {
93 case FP_NINF:
94 return(-1);
95 case FP_PINF:
96 return(1);
97 }
98 return(0);
99}
100
101#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
102
103#if HAVE_FP_CLASS_H
104#include <fp_class.h>
105#endif
106
107int isinf(double d) {
108#if HAVE_FP_CLASS
109 int fpclass = fp_class(d);
110#else
111 int fpclass = fp_class_d(d);
112#endif
113 if (fpclass == FP_POS_INF)
114 return(1);
115 if (fpclass == FP_NEG_INF)
116 return(-1);
117 return(0);
118}
119
120#elif defined(HAVE_CLASS)
121
122int isinf(double d) {
123 int fpclass = class(d);
124 if (fpclass == FP_PLUS_INF)
125 return(1);
126 if (fpclass == FP_MINUS_INF)
127 return(-1);
128 return(0);
129}
130#elif defined(finite) || defined(HAVE_FINITE)
131int isinf(double x) { return !finite(x) && x==x; }
132#elif defined(HUGE_VAL)
133int isinf(double x)
134{
135 if (x == HUGE_VAL)
136 return(1);
137 if (x == -HUGE_VAL)
138 return(-1);
139 return(0);
140}
141#endif
142
143#endif /* ! HAVE_ISINF */
144#endif /* ! defined(isinf) */
145
146#ifndef isnan
147#ifndef HAVE_ISNAN
148
149#ifdef HAVE_ISNAND
150#define isnan(f) isnand(f)
151#endif /* HAVE_iSNAND */
152
153#endif /* ! HAVE_iSNAN */
154#endif /* ! defined(isnan) */
155
156/**
157 * xmlXPathInit:
158 *
159 * Initialize the XPath environment
160 */
161void
162xmlXPathInit(void) {
163 static int initialized = 0;
164
165 if (initialized) return;
166
167 xmlXPathNAN = 0;
168 xmlXPathNAN /= 0;
169
170 xmlXPathPINF = 1;
171 xmlXPathPINF /= 0;
172
173 xmlXPathNINF = -1;
174 xmlXPathNINF /= 0;
175
176 initialized = 1;
177}
178
179/************************************************************************
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000180 * *
181 * Parser Types *
182 * *
183 ************************************************************************/
184
185/*
186 * Types are private:
187 */
188
189typedef enum {
190 XPATH_OP_END=0,
191 XPATH_OP_AND,
192 XPATH_OP_OR,
193 XPATH_OP_EQUAL,
194 XPATH_OP_CMP,
195 XPATH_OP_PLUS,
196 XPATH_OP_MULT,
197 XPATH_OP_UNION,
198 XPATH_OP_ROOT,
199 XPATH_OP_NODE,
200 XPATH_OP_RESET,
201 XPATH_OP_COLLECT,
202 XPATH_OP_VALUE,
203 XPATH_OP_VARIABLE,
204 XPATH_OP_FUNCTION,
205 XPATH_OP_ARG,
206 XPATH_OP_PREDICATE,
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000207 XPATH_OP_FILTER,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000208 XPATH_OP_SORT
209#ifdef LIBXML_XPTR_ENABLED
210 ,XPATH_OP_RANGETO
211#endif
212} xmlXPathOp;
213
214typedef enum {
215 AXIS_ANCESTOR = 1,
216 AXIS_ANCESTOR_OR_SELF,
217 AXIS_ATTRIBUTE,
218 AXIS_CHILD,
219 AXIS_DESCENDANT,
220 AXIS_DESCENDANT_OR_SELF,
221 AXIS_FOLLOWING,
222 AXIS_FOLLOWING_SIBLING,
223 AXIS_NAMESPACE,
224 AXIS_PARENT,
225 AXIS_PRECEDING,
226 AXIS_PRECEDING_SIBLING,
227 AXIS_SELF
228} xmlXPathAxisVal;
229
230typedef enum {
231 NODE_TEST_NONE = 0,
232 NODE_TEST_TYPE = 1,
233 NODE_TEST_PI = 2,
234 NODE_TEST_ALL = 3,
235 NODE_TEST_NS = 4,
236 NODE_TEST_NAME = 5
237} xmlXPathTestVal;
238
239typedef enum {
240 NODE_TYPE_NODE = 0,
241 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
242 NODE_TYPE_TEXT = XML_TEXT_NODE,
243 NODE_TYPE_PI = XML_PI_NODE
244} xmlXPathTypeVal;
245
246
247typedef struct _xmlXPathStepOp xmlXPathStepOp;
248typedef xmlXPathStepOp *xmlXPathStepOpPtr;
249struct _xmlXPathStepOp {
250 xmlXPathOp op;
251 int ch1;
252 int ch2;
253 int value;
254 int value2;
255 int value3;
256 void *value4;
257 void *value5;
258};
259
260struct _xmlXPathCompExpr {
261 int nbStep;
262 int maxStep;
263 xmlXPathStepOp *steps; /* ops for computation */
264 int last;
265};
266
267/************************************************************************
268 * *
269 * Parser Type functions *
270 * *
271 ************************************************************************/
272
273/**
274 * xmlXPathNewCompExpr:
275 *
276 * Create a new Xpath component
277 *
278 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
279 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000280static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000281xmlXPathNewCompExpr(void) {
282 xmlXPathCompExprPtr cur;
283
284 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
285 if (cur == NULL) {
286 xmlGenericError(xmlGenericErrorContext,
287 "xmlXPathNewCompExpr : malloc failed\n");
288 return(NULL);
289 }
290 memset(cur, 0, sizeof(xmlXPathCompExpr));
291 cur->maxStep = 10;
292 cur->nbStep = 0;
293 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
294 sizeof(xmlXPathStepOp));
295 if (cur->steps == NULL) {
296 xmlGenericError(xmlGenericErrorContext,
297 "xmlXPathNewCompExpr : malloc failed\n");
298 xmlFree(cur);
299 return(NULL);
300 }
301 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
302 cur->last = -1;
303 return(cur);
304}
305
306/**
307 * xmlXPathFreeCompExpr:
308 * @comp: an XPATH comp
309 *
310 * Free up the memory allocated by @comp
311 */
312void
313xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) {
314 xmlXPathStepOpPtr op;
315 int i;
316
317 if (comp == NULL)
318 return;
319 for (i = 0;i < comp->nbStep;i++) {
320 op = &comp->steps[i];
321 if (op->value4 != NULL) {
322 if (op->op == XPATH_OP_VALUE)
323 xmlXPathFreeObject(op->value4);
324 else
325 xmlFree(op->value4);
326 }
327 if (op->value5 != NULL)
328 xmlFree(op->value5);
329 }
330 if (comp->steps != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000331 xmlFree(comp->steps);
332 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000333 xmlFree(comp);
334}
335
336/**
337 * xmlXPathCompExprAdd:
338 * @comp: the compiled expression
339 * @ch1: first child index
340 * @ch2: second child index
341 * @op: an op
342 * @value: the first int value
343 * @value2: the second int value
344 * @value3: the third int value
345 * @value4: the first string value
346 * @value5: the second string value
347 *
348 * Add an step to an XPath Compiled Expression
349 *
350 * Returns -1 in case of failure, the index otherwise
351 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000352static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000353xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
354 xmlXPathOp op, int value,
355 int value2, int value3, void *value4, void *value5) {
356 if (comp->nbStep >= comp->maxStep) {
357 xmlXPathStepOp *real;
358
359 comp->maxStep *= 2;
360 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
361 comp->maxStep * sizeof(xmlXPathStepOp));
362 if (real == NULL) {
363 comp->maxStep /= 2;
364 xmlGenericError(xmlGenericErrorContext,
365 "xmlXPathCompExprAdd : realloc failed\n");
366 return(-1);
367 }
368 comp->steps = real;
369 }
370 comp->last = comp->nbStep;
371 comp->steps[comp->nbStep].ch1 = ch1;
372 comp->steps[comp->nbStep].ch2 = ch2;
373 comp->steps[comp->nbStep].op = op;
374 comp->steps[comp->nbStep].value = value;
375 comp->steps[comp->nbStep].value2 = value2;
376 comp->steps[comp->nbStep].value3 = value3;
377 comp->steps[comp->nbStep].value4 = value4;
378 comp->steps[comp->nbStep].value5 = value5;
379 return(comp->nbStep++);
380}
381
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000382#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
383 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
384 (op), (val), (val2), (val3), (val4), (val5))
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000385#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
386 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
387 (op), (val), (val2), (val3), (val4), (val5))
388
389#define PUSH_LEAVE_EXPR(op, val, val2) \
390xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
391
392#define PUSH_UNARY_EXPR(op, ch, val, val2) \
393xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
394
395#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
396xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
397
398/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000399 * *
400 * Debugging related functions *
401 * *
402 ************************************************************************/
403
404#define TODO \
405 xmlGenericError(xmlGenericErrorContext, \
406 "Unimplemented block at %s:%d\n", \
407 __FILE__, __LINE__);
408
409#define STRANGE \
410 xmlGenericError(xmlGenericErrorContext, \
411 "Internal error at %s:%d\n", \
412 __FILE__, __LINE__);
413
414#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000415static void
416xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000417 int i;
418 char shift[100];
419
420 for (i = 0;((i < depth) && (i < 25));i++)
421 shift[2 * i] = shift[2 * i + 1] = ' ';
422 shift[2 * i] = shift[2 * i + 1] = 0;
423 if (cur == NULL) {
424 fprintf(output, shift);
425 fprintf(output, "Node is NULL !\n");
426 return;
427
428 }
429
430 if ((cur->type == XML_DOCUMENT_NODE) ||
431 (cur->type == XML_HTML_DOCUMENT_NODE)) {
432 fprintf(output, shift);
433 fprintf(output, " /\n");
434 } else if (cur->type == XML_ATTRIBUTE_NODE)
435 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
436 else
437 xmlDebugDumpOneNode(output, cur, depth);
438}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000439static void
440xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000441 xmlNodePtr tmp;
442 int i;
443 char shift[100];
444
445 for (i = 0;((i < depth) && (i < 25));i++)
446 shift[2 * i] = shift[2 * i + 1] = ' ';
447 shift[2 * i] = shift[2 * i + 1] = 0;
448 if (cur == NULL) {
449 fprintf(output, shift);
450 fprintf(output, "Node is NULL !\n");
451 return;
452
453 }
454
455 while (cur != NULL) {
456 tmp = cur;
457 cur = cur->next;
458 xmlDebugDumpOneNode(output, tmp, depth);
459 }
460}
Owen Taylor3473f882001-02-23 17:55:21 +0000461
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000462static void
463xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000464 int i;
465 char shift[100];
466
467 for (i = 0;((i < depth) && (i < 25));i++)
468 shift[2 * i] = shift[2 * i + 1] = ' ';
469 shift[2 * i] = shift[2 * i + 1] = 0;
470
471 if (cur == NULL) {
472 fprintf(output, shift);
473 fprintf(output, "NodeSet is NULL !\n");
474 return;
475
476 }
477
Daniel Veillard911f49a2001-04-07 15:39:35 +0000478 if (cur != NULL) {
479 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
480 for (i = 0;i < cur->nodeNr;i++) {
481 fprintf(output, shift);
482 fprintf(output, "%d", i + 1);
483 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
484 }
Owen Taylor3473f882001-02-23 17:55:21 +0000485 }
486}
487
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000488static void
489xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000490 int i;
491 char shift[100];
492
493 for (i = 0;((i < depth) && (i < 25));i++)
494 shift[2 * i] = shift[2 * i + 1] = ' ';
495 shift[2 * i] = shift[2 * i + 1] = 0;
496
497 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
498 fprintf(output, shift);
499 fprintf(output, "Value Tree is NULL !\n");
500 return;
501
502 }
503
504 fprintf(output, shift);
505 fprintf(output, "%d", i + 1);
506 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
507}
Owen Taylor3473f882001-02-23 17:55:21 +0000508#if defined(LIBXML_XPTR_ENABLED)
509void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000510static void
511xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000512 int i;
513 char shift[100];
514
515 for (i = 0;((i < depth) && (i < 25));i++)
516 shift[2 * i] = shift[2 * i + 1] = ' ';
517 shift[2 * i] = shift[2 * i + 1] = 0;
518
519 if (cur == NULL) {
520 fprintf(output, shift);
521 fprintf(output, "LocationSet is NULL !\n");
522 return;
523
524 }
525
526 for (i = 0;i < cur->locNr;i++) {
527 fprintf(output, shift);
528 fprintf(output, "%d : ", i + 1);
529 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
530 }
531}
532#endif
533
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000534/**
535 * xmlXPathDebugDumpObject:
536 * @output: the FILE * to dump the output
537 * @cur: the object to inspect
538 * @depth: indentation level
539 *
540 * Dump the content of the object for debugging purposes
541 */
542void
543xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000544 int i;
545 char shift[100];
546
547 for (i = 0;((i < depth) && (i < 25));i++)
548 shift[2 * i] = shift[2 * i + 1] = ' ';
549 shift[2 * i] = shift[2 * i + 1] = 0;
550
551 fprintf(output, shift);
552
553 if (cur == NULL) {
554 fprintf(output, "Object is empty (NULL)\n");
555 return;
556 }
557 switch(cur->type) {
558 case XPATH_UNDEFINED:
559 fprintf(output, "Object is uninitialized\n");
560 break;
561 case XPATH_NODESET:
562 fprintf(output, "Object is a Node Set :\n");
563 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
564 break;
565 case XPATH_XSLT_TREE:
566 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000567 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000568 break;
569 case XPATH_BOOLEAN:
570 fprintf(output, "Object is a Boolean : ");
571 if (cur->boolval) fprintf(output, "true\n");
572 else fprintf(output, "false\n");
573 break;
574 case XPATH_NUMBER:
575 fprintf(output, "Object is a number : %0g\n", cur->floatval);
576 break;
577 case XPATH_STRING:
578 fprintf(output, "Object is a string : ");
579 xmlDebugDumpString(output, cur->stringval);
580 fprintf(output, "\n");
581 break;
582 case XPATH_POINT:
583 fprintf(output, "Object is a point : index %d in node", cur->index);
584 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
585 fprintf(output, "\n");
586 break;
587 case XPATH_RANGE:
588 if ((cur->user2 == NULL) ||
589 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
590 fprintf(output, "Object is a collapsed range :\n");
591 fprintf(output, shift);
592 if (cur->index >= 0)
593 fprintf(output, "index %d in ", cur->index);
594 fprintf(output, "node\n");
595 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
596 depth + 1);
597 } else {
598 fprintf(output, "Object is a range :\n");
599 fprintf(output, shift);
600 fprintf(output, "From ");
601 if (cur->index >= 0)
602 fprintf(output, "index %d in ", cur->index);
603 fprintf(output, "node\n");
604 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
605 depth + 1);
606 fprintf(output, shift);
607 fprintf(output, "To ");
608 if (cur->index2 >= 0)
609 fprintf(output, "index %d in ", cur->index2);
610 fprintf(output, "node\n");
611 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
612 depth + 1);
613 fprintf(output, "\n");
614 }
615 break;
616 case XPATH_LOCATIONSET:
617#if defined(LIBXML_XPTR_ENABLED)
618 fprintf(output, "Object is a Location Set:\n");
619 xmlXPathDebugDumpLocationSet(output,
620 (xmlLocationSetPtr) cur->user, depth);
621#endif
622 break;
623 case XPATH_USERS:
624 fprintf(output, "Object is user defined\n");
625 break;
626 }
627}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000628
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000629static void
630xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000631 xmlXPathStepOpPtr op, int depth) {
632 int i;
633 char shift[100];
634
635 for (i = 0;((i < depth) && (i < 25));i++)
636 shift[2 * i] = shift[2 * i + 1] = ' ';
637 shift[2 * i] = shift[2 * i + 1] = 0;
638
639 fprintf(output, shift);
640 if (op == NULL) {
641 fprintf(output, "Step is NULL\n");
642 return;
643 }
644 switch (op->op) {
645 case XPATH_OP_END:
646 fprintf(output, "END"); break;
647 case XPATH_OP_AND:
648 fprintf(output, "AND"); break;
649 case XPATH_OP_OR:
650 fprintf(output, "OR"); break;
651 case XPATH_OP_EQUAL:
652 if (op->value)
653 fprintf(output, "EQUAL =");
654 else
655 fprintf(output, "EQUAL !=");
656 break;
657 case XPATH_OP_CMP:
658 if (op->value)
659 fprintf(output, "CMP <");
660 else
661 fprintf(output, "CMP >");
662 if (!op->value2)
663 fprintf(output, "=");
664 break;
665 case XPATH_OP_PLUS:
666 if (op->value == 0)
667 fprintf(output, "PLUS -");
668 else if (op->value == 1)
669 fprintf(output, "PLUS +");
670 else if (op->value == 2)
671 fprintf(output, "PLUS unary -");
672 else if (op->value == 3)
673 fprintf(output, "PLUS unary - -");
674 break;
675 case XPATH_OP_MULT:
676 if (op->value == 0)
677 fprintf(output, "MULT *");
678 else if (op->value == 1)
679 fprintf(output, "MULT div");
680 else
681 fprintf(output, "MULT mod");
682 break;
683 case XPATH_OP_UNION:
684 fprintf(output, "UNION"); break;
685 case XPATH_OP_ROOT:
686 fprintf(output, "ROOT"); break;
687 case XPATH_OP_NODE:
688 fprintf(output, "NODE"); break;
689 case XPATH_OP_RESET:
690 fprintf(output, "RESET"); break;
691 case XPATH_OP_SORT:
692 fprintf(output, "SORT"); break;
693 case XPATH_OP_COLLECT: {
694 xmlXPathAxisVal axis = op->value;
695 xmlXPathTestVal test = op->value2;
696 xmlXPathTypeVal type = op->value3;
697 const xmlChar *prefix = op->value4;
698 const xmlChar *name = op->value5;
699
700 fprintf(output, "COLLECT ");
701 switch (axis) {
702 case AXIS_ANCESTOR:
703 fprintf(output, " 'ancestors' "); break;
704 case AXIS_ANCESTOR_OR_SELF:
705 fprintf(output, " 'ancestors-or-self' "); break;
706 case AXIS_ATTRIBUTE:
707 fprintf(output, " 'attributes' "); break;
708 case AXIS_CHILD:
709 fprintf(output, " 'child' "); break;
710 case AXIS_DESCENDANT:
711 fprintf(output, " 'descendant' "); break;
712 case AXIS_DESCENDANT_OR_SELF:
713 fprintf(output, " 'descendant-or-self' "); break;
714 case AXIS_FOLLOWING:
715 fprintf(output, " 'following' "); break;
716 case AXIS_FOLLOWING_SIBLING:
717 fprintf(output, " 'following-siblings' "); break;
718 case AXIS_NAMESPACE:
719 fprintf(output, " 'namespace' "); break;
720 case AXIS_PARENT:
721 fprintf(output, " 'parent' "); break;
722 case AXIS_PRECEDING:
723 fprintf(output, " 'preceding' "); break;
724 case AXIS_PRECEDING_SIBLING:
725 fprintf(output, " 'preceding-sibling' "); break;
726 case AXIS_SELF:
727 fprintf(output, " 'self' "); break;
728 }
729 switch (test) {
730 case NODE_TEST_NONE:
731 fprintf(output, "'none' "); break;
732 case NODE_TEST_TYPE:
733 fprintf(output, "'type' "); break;
734 case NODE_TEST_PI:
735 fprintf(output, "'PI' "); break;
736 case NODE_TEST_ALL:
737 fprintf(output, "'all' "); break;
738 case NODE_TEST_NS:
739 fprintf(output, "'namespace' "); break;
740 case NODE_TEST_NAME:
741 fprintf(output, "'name' "); break;
742 }
743 switch (type) {
744 case NODE_TYPE_NODE:
745 fprintf(output, "'node' "); break;
746 case NODE_TYPE_COMMENT:
747 fprintf(output, "'comment' "); break;
748 case NODE_TYPE_TEXT:
749 fprintf(output, "'text' "); break;
750 case NODE_TYPE_PI:
751 fprintf(output, "'PI' "); break;
752 }
753 if (prefix != NULL)
754 fprintf(output, "%s:", prefix);
755 if (name != NULL)
756 fprintf(output, "%s", name);
757 break;
758
759 }
760 case XPATH_OP_VALUE: {
761 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
762
763 fprintf(output, "ELEM ");
764 xmlXPathDebugDumpObject(output, object, 0);
765 goto finish;
766 }
767 case XPATH_OP_VARIABLE: {
768 const xmlChar *prefix = op->value5;
769 const xmlChar *name = op->value4;
770
771 if (prefix != NULL)
772 fprintf(output, "VARIABLE %s:%s", prefix, name);
773 else
774 fprintf(output, "VARIABLE %s", name);
775 break;
776 }
777 case XPATH_OP_FUNCTION: {
778 int nbargs = op->value;
779 const xmlChar *prefix = op->value5;
780 const xmlChar *name = op->value4;
781
782 if (prefix != NULL)
783 fprintf(output, "FUNCTION %s:%s(%d args)",
784 prefix, name, nbargs);
785 else
786 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
787 break;
788 }
789 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
790 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000791 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
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
894
895#define SKIP_BLANKS \
896 while (IS_BLANK(*(ctxt->cur))) NEXT
897
898#define CURRENT (*ctxt->cur)
899#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
900
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000901
902#ifndef DBL_DIG
903#define DBL_DIG 16
904#endif
905#ifndef DBL_EPSILON
906#define DBL_EPSILON 1E-9
907#endif
908
909#define UPPER_DOUBLE 1E9
910#define LOWER_DOUBLE 1E-5
911
912#define INTEGER_DIGITS DBL_DIG
913#define FRACTION_DIGITS (DBL_DIG + 1)
914#define EXPONENT_DIGITS (3 + 2)
915
916/**
917 * xmlXPathFormatNumber:
918 * @number: number to format
919 * @buffer: output buffer
920 * @buffersize: size of output buffer
921 *
922 * Convert the number into a string representation.
923 */
924static void
925xmlXPathFormatNumber(double number, char buffer[], int buffersize)
926{
927 switch (isinf(number)) {
928 case 1:
929 if (buffersize > (int)sizeof("+Infinity"))
930 sprintf(buffer, "+Infinity");
931 break;
932 case -1:
933 if (buffersize > (int)sizeof("-Infinity"))
934 sprintf(buffer, "-Infinity");
935 break;
936 default:
937 if (isnan(number)) {
938 if (buffersize > (int)sizeof("NaN"))
939 sprintf(buffer, "NaN");
940 } else {
941 char work[INTEGER_DIGITS + FRACTION_DIGITS + EXPONENT_DIGITS + 1];
942 char *pointer;
943 char *start;
944 int i;
945 int digits;
946 int is_negative;
947 int use_scientific;
948 int exponent;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000949 int indx;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000950 int count;
951 double n;
952
953 i = digits = 0;
954 is_negative = (number < 0.0);
955 if (is_negative)
956 number = -number;
957
958 /* Scale number */
959 n = log10(number);
960 exponent = (isinf(n) == -1) ? 0 : (int)n;
961 use_scientific = (((number <= LOWER_DOUBLE) ||
962 (number > UPPER_DOUBLE)) &&
963 (number != 0));
964 if (use_scientific) {
965 number /= pow(10.0, (double)exponent);
966 while (number < 1.0) {
967 number *= 10.0;
968 exponent--;
969 }
970 }
971
972 /* Integer part is build from back */
973 pointer = &work[INTEGER_DIGITS + 1];
974 if (number < 1.0) {
975 *(--pointer) = '0';
976 digits++;
977 } else {
978 n = number;
979 for (i = 1; i < INTEGER_DIGITS - 1; i++) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000980 indx = (int)n % 10;
981 *(--pointer) = "0123456789"[indx];
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000982 n /= 10.0;
983 if (n < 1.0)
984 break;
985 }
986 digits += i;
987 }
988 if (is_negative) {
989 *(--pointer) = '-';
990 digits++;
991 }
992 start = pointer;
993
994 /* Fraction part is build from front */
995 i = 0;
996 pointer = &work[INTEGER_DIGITS + 1];
997 if (number - floor(number) > DBL_EPSILON) {
998 *(pointer++) = '.';
999 i++;
1000 n = number;
1001 count = 0;
1002 while (i < FRACTION_DIGITS) {
1003 n -= floor(n);
1004 n *= 10.0;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001005 indx = (int)n % 10;
1006 *(pointer++) = "0123456789"[indx];
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001007 i++;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001008 if ((indx != 0) || (count > 0))
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001009 count++;
1010 if ((n > 10.0) || (count > FRACTION_DIGITS / 2))
1011 break;
1012 }
1013 }
1014 /* Remove trailing zeroes */
1015 while ((pointer[-1] == '0') && (i > 0)) {
1016 pointer--;
1017 i--;
1018 }
1019 digits += i;
1020
1021 if (use_scientific) {
1022 *(pointer++) = 'e';
1023 digits++;
1024 if (exponent < 0) {
1025 *(pointer++) = '-';
1026 exponent = -exponent;
1027 } else {
1028 *(pointer++) = '+';
1029 }
1030 digits++;
1031 if (exponent >= 100)
1032 pointer += 2;
1033 else if (exponent >= 10)
1034 pointer += 1;
1035 while (exponent >= 1) {
1036 *(pointer--) = "0123456789"[exponent % 10];
1037 exponent /= 10;
1038 digits++;
1039 }
1040 }
1041
1042 if (digits >= buffersize)
1043 digits = buffersize - 1;
1044
1045 memcpy(buffer, start, digits);
1046 buffer[digits] = 0;
1047 }
1048 break;
1049 }
1050}
1051
Owen Taylor3473f882001-02-23 17:55:21 +00001052/************************************************************************
1053 * *
1054 * Error handling routines *
1055 * *
1056 ************************************************************************/
1057
1058
1059const char *xmlXPathErrorMessages[] = {
1060 "Ok",
1061 "Number encoding",
1062 "Unfinished litteral",
1063 "Start of litteral",
1064 "Expected $ for variable reference",
1065 "Undefined variable",
1066 "Invalid predicate",
1067 "Invalid expression",
1068 "Missing closing curly brace",
1069 "Unregistered function",
1070 "Invalid operand",
1071 "Invalid type",
1072 "Invalid number of arguments",
1073 "Invalid context size",
1074 "Invalid context position",
1075 "Memory allocation error",
1076 "Syntax error",
1077 "Resource error",
1078 "Sub resource error",
1079 "Undefined namespace prefix"
1080};
1081
1082/**
1083 * xmlXPathError:
1084 * @ctxt: the XPath Parser context
1085 * @file: the file name
1086 * @line: the line number
1087 * @no: the error number
1088 *
1089 * Create a new xmlNodeSetPtr of type double and of value @val
1090 *
1091 * Returns the newly created object.
1092 */
1093void
1094xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1095 int line, int no) {
1096 int n;
1097 const xmlChar *cur;
1098 const xmlChar *base;
1099
1100 xmlGenericError(xmlGenericErrorContext,
1101 "Error %s:%d: %s\n", file, line,
1102 xmlXPathErrorMessages[no]);
1103
1104 cur = ctxt->cur;
1105 base = ctxt->base;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001106 if ((cur == NULL) || (base == NULL))
1107 return;
1108
Owen Taylor3473f882001-02-23 17:55:21 +00001109 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1110 cur--;
1111 }
1112 n = 0;
1113 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1114 cur--;
1115 if ((*cur == '\n') || (*cur == '\r')) cur++;
1116 base = cur;
1117 n = 0;
1118 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1119 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1120 n++;
1121 }
1122 xmlGenericError(xmlGenericErrorContext, "\n");
1123 cur = ctxt->cur;
1124 while ((*cur == '\n') || (*cur == '\r'))
1125 cur--;
1126 n = 0;
1127 while ((cur != base) && (n++ < 80)) {
1128 xmlGenericError(xmlGenericErrorContext, " ");
1129 base++;
1130 }
1131 xmlGenericError(xmlGenericErrorContext,"^\n");
1132}
1133
1134
1135/************************************************************************
1136 * *
1137 * Routines to handle NodeSets *
1138 * *
1139 ************************************************************************/
1140
1141/**
1142 * xmlXPathCmpNodes:
1143 * @node1: the first node
1144 * @node2: the second node
1145 *
1146 * Compare two nodes w.r.t document order
1147 *
1148 * Returns -2 in case of error 1 if first point < second point, 0 if
1149 * that's the same node, -1 otherwise
1150 */
1151int
1152xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1153 int depth1, depth2;
1154 xmlNodePtr cur, root;
1155
1156 if ((node1 == NULL) || (node2 == NULL))
1157 return(-2);
1158 /*
1159 * a couple of optimizations which will avoid computations in most cases
1160 */
1161 if (node1 == node2)
1162 return(0);
1163 if (node1 == node2->prev)
1164 return(1);
1165 if (node1 == node2->next)
1166 return(-1);
1167
1168 /*
1169 * compute depth to root
1170 */
1171 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1172 if (cur == node1)
1173 return(1);
1174 depth2++;
1175 }
1176 root = cur;
1177 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1178 if (cur == node2)
1179 return(-1);
1180 depth1++;
1181 }
1182 /*
1183 * Distinct document (or distinct entities :-( ) case.
1184 */
1185 if (root != cur) {
1186 return(-2);
1187 }
1188 /*
1189 * get the nearest common ancestor.
1190 */
1191 while (depth1 > depth2) {
1192 depth1--;
1193 node1 = node1->parent;
1194 }
1195 while (depth2 > depth1) {
1196 depth2--;
1197 node2 = node2->parent;
1198 }
1199 while (node1->parent != node2->parent) {
1200 node1 = node1->parent;
1201 node2 = node2->parent;
1202 /* should not happen but just in case ... */
1203 if ((node1 == NULL) || (node2 == NULL))
1204 return(-2);
1205 }
1206 /*
1207 * Find who's first.
1208 */
1209 if (node1 == node2->next)
1210 return(-1);
1211 for (cur = node1->next;cur != NULL;cur = cur->next)
1212 if (cur == node2)
1213 return(1);
1214 return(-1); /* assume there is no sibling list corruption */
1215}
1216
1217/**
1218 * xmlXPathNodeSetSort:
1219 * @set: the node set
1220 *
1221 * Sort the node set in document order
1222 */
1223void
1224xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001225 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001226 xmlNodePtr tmp;
1227
1228 if (set == NULL)
1229 return;
1230
1231 /* Use Shell's sort to sort the node-set */
1232 len = set->nodeNr;
1233 for (incr = len / 2; incr > 0; incr /= 2) {
1234 for (i = incr; i < len; i++) {
1235 j = i - incr;
1236 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001237 if (xmlXPathCmpNodes(set->nodeTab[j],
1238 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001239 tmp = set->nodeTab[j];
1240 set->nodeTab[j] = set->nodeTab[j + incr];
1241 set->nodeTab[j + incr] = tmp;
1242 j -= incr;
1243 } else
1244 break;
1245 }
1246 }
1247 }
1248}
1249
1250#define XML_NODESET_DEFAULT 10
1251/**
1252 * xmlXPathNodeSetCreate:
1253 * @val: an initial xmlNodePtr, or NULL
1254 *
1255 * Create a new xmlNodeSetPtr of type double and of value @val
1256 *
1257 * Returns the newly created object.
1258 */
1259xmlNodeSetPtr
1260xmlXPathNodeSetCreate(xmlNodePtr val) {
1261 xmlNodeSetPtr ret;
1262
1263 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1264 if (ret == NULL) {
1265 xmlGenericError(xmlGenericErrorContext,
1266 "xmlXPathNewNodeSet: out of memory\n");
1267 return(NULL);
1268 }
1269 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1270 if (val != NULL) {
1271 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1272 sizeof(xmlNodePtr));
1273 if (ret->nodeTab == NULL) {
1274 xmlGenericError(xmlGenericErrorContext,
1275 "xmlXPathNewNodeSet: out of memory\n");
1276 return(NULL);
1277 }
1278 memset(ret->nodeTab, 0 ,
1279 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1280 ret->nodeMax = XML_NODESET_DEFAULT;
1281 ret->nodeTab[ret->nodeNr++] = val;
1282 }
1283 return(ret);
1284}
1285
1286/**
1287 * xmlXPathNodeSetAdd:
1288 * @cur: the initial node set
1289 * @val: a new xmlNodePtr
1290 *
1291 * add a new xmlNodePtr ot an existing NodeSet
1292 */
1293void
1294xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1295 int i;
1296
1297 if (val == NULL) return;
1298
1299 /*
1300 * check against doublons
1301 */
1302 for (i = 0;i < cur->nodeNr;i++)
1303 if (cur->nodeTab[i] == val) return;
1304
1305 /*
1306 * grow the nodeTab if needed
1307 */
1308 if (cur->nodeMax == 0) {
1309 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1310 sizeof(xmlNodePtr));
1311 if (cur->nodeTab == NULL) {
1312 xmlGenericError(xmlGenericErrorContext,
1313 "xmlXPathNodeSetAdd: out of memory\n");
1314 return;
1315 }
1316 memset(cur->nodeTab, 0 ,
1317 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1318 cur->nodeMax = XML_NODESET_DEFAULT;
1319 } else if (cur->nodeNr == cur->nodeMax) {
1320 xmlNodePtr *temp;
1321
1322 cur->nodeMax *= 2;
1323 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1324 sizeof(xmlNodePtr));
1325 if (temp == NULL) {
1326 xmlGenericError(xmlGenericErrorContext,
1327 "xmlXPathNodeSetAdd: out of memory\n");
1328 return;
1329 }
1330 cur->nodeTab = temp;
1331 }
1332 cur->nodeTab[cur->nodeNr++] = val;
1333}
1334
1335/**
1336 * xmlXPathNodeSetAddUnique:
1337 * @cur: the initial node set
1338 * @val: a new xmlNodePtr
1339 *
1340 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1341 * when we are sure the node is not already in the set.
1342 */
1343void
1344xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1345 if (val == NULL) return;
1346
1347 /*
1348 * grow the nodeTab if needed
1349 */
1350 if (cur->nodeMax == 0) {
1351 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1352 sizeof(xmlNodePtr));
1353 if (cur->nodeTab == NULL) {
1354 xmlGenericError(xmlGenericErrorContext,
1355 "xmlXPathNodeSetAddUnique: out of memory\n");
1356 return;
1357 }
1358 memset(cur->nodeTab, 0 ,
1359 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1360 cur->nodeMax = XML_NODESET_DEFAULT;
1361 } else if (cur->nodeNr == cur->nodeMax) {
1362 xmlNodePtr *temp;
1363
1364 cur->nodeMax *= 2;
1365 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1366 sizeof(xmlNodePtr));
1367 if (temp == NULL) {
1368 xmlGenericError(xmlGenericErrorContext,
1369 "xmlXPathNodeSetAddUnique: out of memory\n");
1370 return;
1371 }
1372 cur->nodeTab = temp;
1373 }
1374 cur->nodeTab[cur->nodeNr++] = val;
1375}
1376
1377/**
1378 * xmlXPathNodeSetMerge:
1379 * @val1: the first NodeSet or NULL
1380 * @val2: the second NodeSet
1381 *
1382 * Merges two nodesets, all nodes from @val2 are added to @val1
1383 * if @val1 is NULL, a new set is created and copied from @val2
1384 *
1385 * Returns val1 once extended or NULL in case of error.
1386 */
1387xmlNodeSetPtr
1388xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001389 int i, j, initNr, skip;
Owen Taylor3473f882001-02-23 17:55:21 +00001390
1391 if (val2 == NULL) return(val1);
1392 if (val1 == NULL) {
1393 val1 = xmlXPathNodeSetCreate(NULL);
1394 }
1395
1396 initNr = val1->nodeNr;
1397
1398 for (i = 0;i < val2->nodeNr;i++) {
1399 /*
1400 * check against doublons
1401 */
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001402 skip = 0;
1403 for (j = 0; j < initNr; j++) {
1404 if (val1->nodeTab[j] == val2->nodeTab[i]) {
1405 skip = 1;
1406 break;
1407 }
1408 }
1409 if (skip)
1410 continue;
Owen Taylor3473f882001-02-23 17:55:21 +00001411
1412 /*
1413 * grow the nodeTab if needed
1414 */
1415 if (val1->nodeMax == 0) {
1416 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1417 sizeof(xmlNodePtr));
1418 if (val1->nodeTab == NULL) {
1419 xmlGenericError(xmlGenericErrorContext,
1420 "xmlXPathNodeSetMerge: out of memory\n");
1421 return(NULL);
1422 }
1423 memset(val1->nodeTab, 0 ,
1424 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1425 val1->nodeMax = XML_NODESET_DEFAULT;
1426 } else if (val1->nodeNr == val1->nodeMax) {
1427 xmlNodePtr *temp;
1428
1429 val1->nodeMax *= 2;
1430 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1431 sizeof(xmlNodePtr));
1432 if (temp == NULL) {
1433 xmlGenericError(xmlGenericErrorContext,
1434 "xmlXPathNodeSetMerge: out of memory\n");
1435 return(NULL);
1436 }
1437 val1->nodeTab = temp;
1438 }
1439 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1440 }
1441
1442 return(val1);
1443}
1444
1445/**
1446 * xmlXPathNodeSetDel:
1447 * @cur: the initial node set
1448 * @val: an xmlNodePtr
1449 *
1450 * Removes an xmlNodePtr from an existing NodeSet
1451 */
1452void
1453xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1454 int i;
1455
1456 if (cur == NULL) return;
1457 if (val == NULL) return;
1458
1459 /*
1460 * check against doublons
1461 */
1462 for (i = 0;i < cur->nodeNr;i++)
1463 if (cur->nodeTab[i] == val) break;
1464
1465 if (i >= cur->nodeNr) {
1466#ifdef DEBUG
1467 xmlGenericError(xmlGenericErrorContext,
1468 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1469 val->name);
1470#endif
1471 return;
1472 }
1473 cur->nodeNr--;
1474 for (;i < cur->nodeNr;i++)
1475 cur->nodeTab[i] = cur->nodeTab[i + 1];
1476 cur->nodeTab[cur->nodeNr] = NULL;
1477}
1478
1479/**
1480 * xmlXPathNodeSetRemove:
1481 * @cur: the initial node set
1482 * @val: the index to remove
1483 *
1484 * Removes an entry from an existing NodeSet list.
1485 */
1486void
1487xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1488 if (cur == NULL) return;
1489 if (val >= cur->nodeNr) return;
1490 cur->nodeNr--;
1491 for (;val < cur->nodeNr;val++)
1492 cur->nodeTab[val] = cur->nodeTab[val + 1];
1493 cur->nodeTab[cur->nodeNr] = NULL;
1494}
1495
1496/**
1497 * xmlXPathFreeNodeSet:
1498 * @obj: the xmlNodeSetPtr to free
1499 *
1500 * Free the NodeSet compound (not the actual nodes !).
1501 */
1502void
1503xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1504 if (obj == NULL) return;
1505 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001506 xmlFree(obj->nodeTab);
1507 }
Owen Taylor3473f882001-02-23 17:55:21 +00001508 xmlFree(obj);
1509}
1510
1511/**
1512 * xmlXPathFreeValueTree:
1513 * @obj: the xmlNodeSetPtr to free
1514 *
1515 * Free the NodeSet compound and the actual tree, this is different
1516 * from xmlXPathFreeNodeSet()
1517 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001518static void
Owen Taylor3473f882001-02-23 17:55:21 +00001519xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1520 int i;
1521
1522 if (obj == NULL) return;
1523 for (i = 0;i < obj->nodeNr;i++)
1524 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001525 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001526
1527 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001528 xmlFree(obj->nodeTab);
1529 }
Owen Taylor3473f882001-02-23 17:55:21 +00001530 xmlFree(obj);
1531}
1532
1533#if defined(DEBUG) || defined(DEBUG_STEP)
1534/**
1535 * xmlGenericErrorContextNodeSet:
1536 * @output: a FILE * for the output
1537 * @obj: the xmlNodeSetPtr to free
1538 *
1539 * Quick display of a NodeSet
1540 */
1541void
1542xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1543 int i;
1544
1545 if (output == NULL) output = xmlGenericErrorContext;
1546 if (obj == NULL) {
1547 fprintf(output, "NodeSet == NULL !\n");
1548 return;
1549 }
1550 if (obj->nodeNr == 0) {
1551 fprintf(output, "NodeSet is empty\n");
1552 return;
1553 }
1554 if (obj->nodeTab == NULL) {
1555 fprintf(output, " nodeTab == NULL !\n");
1556 return;
1557 }
1558 for (i = 0; i < obj->nodeNr; i++) {
1559 if (obj->nodeTab[i] == NULL) {
1560 fprintf(output, " NULL !\n");
1561 return;
1562 }
1563 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1564 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1565 fprintf(output, " /");
1566 else if (obj->nodeTab[i]->name == NULL)
1567 fprintf(output, " noname!");
1568 else fprintf(output, " %s", obj->nodeTab[i]->name);
1569 }
1570 fprintf(output, "\n");
1571}
1572#endif
1573
1574/**
1575 * xmlXPathNewNodeSet:
1576 * @val: the NodePtr value
1577 *
1578 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1579 * it with the single Node @val
1580 *
1581 * Returns the newly created object.
1582 */
1583xmlXPathObjectPtr
1584xmlXPathNewNodeSet(xmlNodePtr val) {
1585 xmlXPathObjectPtr ret;
1586
1587 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1588 if (ret == NULL) {
1589 xmlGenericError(xmlGenericErrorContext,
1590 "xmlXPathNewNodeSet: out of memory\n");
1591 return(NULL);
1592 }
1593 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1594 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001595 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001596 ret->nodesetval = xmlXPathNodeSetCreate(val);
1597 return(ret);
1598}
1599
1600/**
1601 * xmlXPathNewValueTree:
1602 * @val: the NodePtr value
1603 *
1604 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1605 * it with the tree root @val
1606 *
1607 * Returns the newly created object.
1608 */
1609xmlXPathObjectPtr
1610xmlXPathNewValueTree(xmlNodePtr val) {
1611 xmlXPathObjectPtr ret;
1612
1613 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1614 if (ret == NULL) {
1615 xmlGenericError(xmlGenericErrorContext,
1616 "xmlXPathNewNodeSet: out of memory\n");
1617 return(NULL);
1618 }
1619 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1620 ret->type = XPATH_XSLT_TREE;
1621 ret->nodesetval = xmlXPathNodeSetCreate(val);
1622 return(ret);
1623}
1624
1625/**
1626 * xmlXPathNewNodeSetList:
1627 * @val: an existing NodeSet
1628 *
1629 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1630 * it with the Nodeset @val
1631 *
1632 * Returns the newly created object.
1633 */
1634xmlXPathObjectPtr
1635xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1636 xmlXPathObjectPtr ret;
1637 int i;
1638
1639 if (val == NULL)
1640 ret = NULL;
1641 else if (val->nodeTab == NULL)
1642 ret = xmlXPathNewNodeSet(NULL);
1643 else
1644 {
1645 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1646 for (i = 1; i < val->nodeNr; ++i)
1647 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1648 }
1649
1650 return(ret);
1651}
1652
1653/**
1654 * xmlXPathWrapNodeSet:
1655 * @val: the NodePtr value
1656 *
1657 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1658 *
1659 * Returns the newly created object.
1660 */
1661xmlXPathObjectPtr
1662xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1663 xmlXPathObjectPtr ret;
1664
1665 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1666 if (ret == NULL) {
1667 xmlGenericError(xmlGenericErrorContext,
1668 "xmlXPathWrapNodeSet: out of memory\n");
1669 return(NULL);
1670 }
1671 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1672 ret->type = XPATH_NODESET;
1673 ret->nodesetval = val;
1674 return(ret);
1675}
1676
1677/**
1678 * xmlXPathFreeNodeSetList:
1679 * @obj: an existing NodeSetList object
1680 *
1681 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1682 * the list contrary to xmlXPathFreeObject().
1683 */
1684void
1685xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1686 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001687 xmlFree(obj);
1688}
1689
1690/************************************************************************
1691 * *
1692 * Routines to handle extra functions *
1693 * *
1694 ************************************************************************/
1695
1696/**
1697 * xmlXPathRegisterFunc:
1698 * @ctxt: the XPath context
1699 * @name: the function name
1700 * @f: the function implementation or NULL
1701 *
1702 * Register a new function. If @f is NULL it unregisters the function
1703 *
1704 * Returns 0 in case of success, -1 in case of error
1705 */
1706int
1707xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
1708 xmlXPathFunction f) {
1709 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
1710}
1711
1712/**
1713 * xmlXPathRegisterFuncNS:
1714 * @ctxt: the XPath context
1715 * @name: the function name
1716 * @ns_uri: the function namespace URI
1717 * @f: the function implementation or NULL
1718 *
1719 * Register a new function. If @f is NULL it unregisters the function
1720 *
1721 * Returns 0 in case of success, -1 in case of error
1722 */
1723int
1724xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1725 const xmlChar *ns_uri, xmlXPathFunction f) {
1726 if (ctxt == NULL)
1727 return(-1);
1728 if (name == NULL)
1729 return(-1);
1730
1731 if (ctxt->funcHash == NULL)
1732 ctxt->funcHash = xmlHashCreate(0);
1733 if (ctxt->funcHash == NULL)
1734 return(-1);
1735 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
1736}
1737
1738/**
1739 * xmlXPathFunctionLookup:
1740 * @ctxt: the XPath context
1741 * @name: the function name
1742 *
1743 * Search in the Function array of the context for the given
1744 * function.
1745 *
1746 * Returns the xmlXPathFunction or NULL if not found
1747 */
1748xmlXPathFunction
1749xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1750 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
1751}
1752
1753/**
1754 * xmlXPathFunctionLookupNS:
1755 * @ctxt: the XPath context
1756 * @name: the function name
1757 * @ns_uri: the function namespace URI
1758 *
1759 * Search in the Function array of the context for the given
1760 * function.
1761 *
1762 * Returns the xmlXPathFunction or NULL if not found
1763 */
1764xmlXPathFunction
1765xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1766 const xmlChar *ns_uri) {
1767 if (ctxt == NULL)
1768 return(NULL);
1769 if (ctxt->funcHash == NULL)
1770 return(NULL);
1771 if (name == NULL)
1772 return(NULL);
1773
1774 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
1775}
1776
1777/**
1778 * xmlXPathRegisteredFuncsCleanup:
1779 * @ctxt: the XPath context
1780 *
1781 * Cleanup the XPath context data associated to registered functions
1782 */
1783void
1784xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
1785 if (ctxt == NULL)
1786 return;
1787
1788 xmlHashFree(ctxt->funcHash, NULL);
1789 ctxt->funcHash = NULL;
1790}
1791
1792/************************************************************************
1793 * *
1794 * Routines to handle Variable *
1795 * *
1796 ************************************************************************/
1797
1798/**
1799 * xmlXPathRegisterVariable:
1800 * @ctxt: the XPath context
1801 * @name: the variable name
1802 * @value: the variable value or NULL
1803 *
1804 * Register a new variable value. If @value is NULL it unregisters
1805 * the variable
1806 *
1807 * Returns 0 in case of success, -1 in case of error
1808 */
1809int
1810xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
1811 xmlXPathObjectPtr value) {
1812 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
1813}
1814
1815/**
1816 * xmlXPathRegisterVariableNS:
1817 * @ctxt: the XPath context
1818 * @name: the variable name
1819 * @ns_uri: the variable namespace URI
1820 * @value: the variable value or NULL
1821 *
1822 * Register a new variable value. If @value is NULL it unregisters
1823 * the variable
1824 *
1825 * Returns 0 in case of success, -1 in case of error
1826 */
1827int
1828xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1829 const xmlChar *ns_uri,
1830 xmlXPathObjectPtr value) {
1831 if (ctxt == NULL)
1832 return(-1);
1833 if (name == NULL)
1834 return(-1);
1835
1836 if (ctxt->varHash == NULL)
1837 ctxt->varHash = xmlHashCreate(0);
1838 if (ctxt->varHash == NULL)
1839 return(-1);
1840 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
1841 (void *) value,
1842 (xmlHashDeallocator)xmlXPathFreeObject));
1843}
1844
1845/**
1846 * xmlXPathRegisterVariableLookup:
1847 * @ctxt: the XPath context
1848 * @f: the lookup function
1849 * @data: the lookup data
1850 *
1851 * register an external mechanism to do variable lookup
1852 */
1853void
1854xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
1855 xmlXPathVariableLookupFunc f, void *data) {
1856 if (ctxt == NULL)
1857 return;
1858 ctxt->varLookupFunc = (void *) f;
1859 ctxt->varLookupData = data;
1860}
1861
1862/**
1863 * xmlXPathVariableLookup:
1864 * @ctxt: the XPath context
1865 * @name: the variable name
1866 *
1867 * Search in the Variable array of the context for the given
1868 * variable value.
1869 *
1870 * Returns the value or NULL if not found
1871 */
1872xmlXPathObjectPtr
1873xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1874 if (ctxt == NULL)
1875 return(NULL);
1876
1877 if (ctxt->varLookupFunc != NULL) {
1878 xmlXPathObjectPtr ret;
1879
1880 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1881 (ctxt->varLookupData, name, NULL);
1882 if (ret != NULL) return(ret);
1883 }
1884 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
1885}
1886
1887/**
1888 * xmlXPathVariableLookupNS:
1889 * @ctxt: the XPath context
1890 * @name: the variable name
1891 * @ns_uri: the variable namespace URI
1892 *
1893 * Search in the Variable array of the context for the given
1894 * variable value.
1895 *
1896 * Returns the value or NULL if not found
1897 */
1898xmlXPathObjectPtr
1899xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1900 const xmlChar *ns_uri) {
1901 if (ctxt == NULL)
1902 return(NULL);
1903
1904 if (ctxt->varLookupFunc != NULL) {
1905 xmlXPathObjectPtr ret;
1906
1907 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1908 (ctxt->varLookupData, name, ns_uri);
1909 if (ret != NULL) return(ret);
1910 }
1911
1912 if (ctxt->varHash == NULL)
1913 return(NULL);
1914 if (name == NULL)
1915 return(NULL);
1916
1917 return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
1918}
1919
1920/**
1921 * xmlXPathRegisteredVariablesCleanup:
1922 * @ctxt: the XPath context
1923 *
1924 * Cleanup the XPath context data associated to registered variables
1925 */
1926void
1927xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
1928 if (ctxt == NULL)
1929 return;
1930
1931 xmlHashFree(ctxt->varHash, NULL);
1932 ctxt->varHash = NULL;
1933}
1934
1935/**
1936 * xmlXPathRegisterNs:
1937 * @ctxt: the XPath context
1938 * @prefix: the namespace prefix
1939 * @ns_uri: the namespace name
1940 *
1941 * Register a new namespace. If @ns_uri is NULL it unregisters
1942 * the namespace
1943 *
1944 * Returns 0 in case of success, -1 in case of error
1945 */
1946int
1947xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
1948 const xmlChar *ns_uri) {
1949 if (ctxt == NULL)
1950 return(-1);
1951 if (prefix == NULL)
1952 return(-1);
1953
1954 if (ctxt->nsHash == NULL)
1955 ctxt->nsHash = xmlHashCreate(10);
1956 if (ctxt->nsHash == NULL)
1957 return(-1);
1958 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
1959 (xmlHashDeallocator)xmlFree));
1960}
1961
1962/**
1963 * xmlXPathNsLookup:
1964 * @ctxt: the XPath context
1965 * @prefix: the namespace prefix value
1966 *
1967 * Search in the namespace declaration array of the context for the given
1968 * namespace name associated to the given prefix
1969 *
1970 * Returns the value or NULL if not found
1971 */
1972const xmlChar *
1973xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
1974 if (ctxt == NULL)
1975 return(NULL);
1976 if (prefix == NULL)
1977 return(NULL);
1978
1979#ifdef XML_XML_NAMESPACE
1980 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
1981 return(XML_XML_NAMESPACE);
1982#endif
1983
1984 if (ctxt->nsHash == NULL)
1985 return(NULL);
1986
1987 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
1988}
1989
1990/**
1991 * xmlXPathRegisteredVariablesCleanup:
1992 * @ctxt: the XPath context
1993 *
1994 * Cleanup the XPath context data associated to registered variables
1995 */
1996void
1997xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
1998 if (ctxt == NULL)
1999 return;
2000
2001 xmlHashFree(ctxt->nsHash, NULL);
2002 ctxt->nsHash = NULL;
2003}
2004
2005/************************************************************************
2006 * *
2007 * Routines to handle Values *
2008 * *
2009 ************************************************************************/
2010
2011/* Allocations are terrible, one need to optimize all this !!! */
2012
2013/**
2014 * xmlXPathNewFloat:
2015 * @val: the double value
2016 *
2017 * Create a new xmlXPathObjectPtr of type double and of value @val
2018 *
2019 * Returns the newly created object.
2020 */
2021xmlXPathObjectPtr
2022xmlXPathNewFloat(double val) {
2023 xmlXPathObjectPtr ret;
2024
2025 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2026 if (ret == NULL) {
2027 xmlGenericError(xmlGenericErrorContext,
2028 "xmlXPathNewFloat: out of memory\n");
2029 return(NULL);
2030 }
2031 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2032 ret->type = XPATH_NUMBER;
2033 ret->floatval = val;
2034 return(ret);
2035}
2036
2037/**
2038 * xmlXPathNewBoolean:
2039 * @val: the boolean value
2040 *
2041 * Create a new xmlXPathObjectPtr of type boolean and of value @val
2042 *
2043 * Returns the newly created object.
2044 */
2045xmlXPathObjectPtr
2046xmlXPathNewBoolean(int val) {
2047 xmlXPathObjectPtr ret;
2048
2049 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2050 if (ret == NULL) {
2051 xmlGenericError(xmlGenericErrorContext,
2052 "xmlXPathNewBoolean: out of memory\n");
2053 return(NULL);
2054 }
2055 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2056 ret->type = XPATH_BOOLEAN;
2057 ret->boolval = (val != 0);
2058 return(ret);
2059}
2060
2061/**
2062 * xmlXPathNewString:
2063 * @val: the xmlChar * value
2064 *
2065 * Create a new xmlXPathObjectPtr of type string and of value @val
2066 *
2067 * Returns the newly created object.
2068 */
2069xmlXPathObjectPtr
2070xmlXPathNewString(const xmlChar *val) {
2071 xmlXPathObjectPtr ret;
2072
2073 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2074 if (ret == NULL) {
2075 xmlGenericError(xmlGenericErrorContext,
2076 "xmlXPathNewString: out of memory\n");
2077 return(NULL);
2078 }
2079 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2080 ret->type = XPATH_STRING;
2081 if (val != NULL)
2082 ret->stringval = xmlStrdup(val);
2083 else
2084 ret->stringval = xmlStrdup((const xmlChar *)"");
2085 return(ret);
2086}
2087
2088/**
2089 * xmlXPathNewCString:
2090 * @val: the char * value
2091 *
2092 * Create a new xmlXPathObjectPtr of type string and of value @val
2093 *
2094 * Returns the newly created object.
2095 */
2096xmlXPathObjectPtr
2097xmlXPathNewCString(const char *val) {
2098 xmlXPathObjectPtr ret;
2099
2100 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2101 if (ret == NULL) {
2102 xmlGenericError(xmlGenericErrorContext,
2103 "xmlXPathNewCString: out of memory\n");
2104 return(NULL);
2105 }
2106 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2107 ret->type = XPATH_STRING;
2108 ret->stringval = xmlStrdup(BAD_CAST val);
2109 return(ret);
2110}
2111
2112/**
2113 * xmlXPathObjectCopy:
2114 * @val: the original object
2115 *
2116 * allocate a new copy of a given object
2117 *
2118 * Returns the newly created object.
2119 */
2120xmlXPathObjectPtr
2121xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2122 xmlXPathObjectPtr ret;
2123
2124 if (val == NULL)
2125 return(NULL);
2126
2127 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2128 if (ret == NULL) {
2129 xmlGenericError(xmlGenericErrorContext,
2130 "xmlXPathObjectCopy: out of memory\n");
2131 return(NULL);
2132 }
2133 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2134 switch (val->type) {
2135 case XPATH_BOOLEAN:
2136 case XPATH_NUMBER:
2137 case XPATH_POINT:
2138 case XPATH_RANGE:
2139 break;
2140 case XPATH_STRING:
2141 ret->stringval = xmlStrdup(val->stringval);
2142 break;
2143 case XPATH_XSLT_TREE:
2144 if ((val->nodesetval != NULL) &&
2145 (val->nodesetval->nodeTab != NULL))
2146 ret->nodesetval = xmlXPathNodeSetCreate(
2147 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
2148 else
2149 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
2150 break;
2151 case XPATH_NODESET:
2152 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
2153 break;
2154 case XPATH_LOCATIONSET:
2155#ifdef LIBXML_XPTR_ENABLED
2156 {
2157 xmlLocationSetPtr loc = val->user;
2158 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2159 break;
2160 }
2161#endif
2162 case XPATH_UNDEFINED:
2163 case XPATH_USERS:
2164 xmlGenericError(xmlGenericErrorContext,
2165 "xmlXPathObjectCopy: unsupported type %d\n",
2166 val->type);
2167 break;
2168 }
2169 return(ret);
2170}
2171
2172/**
2173 * xmlXPathFreeObject:
2174 * @obj: the object to free
2175 *
2176 * Free up an xmlXPathObjectPtr object.
2177 */
2178void
2179xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2180 if (obj == NULL) return;
2181 if (obj->type == XPATH_NODESET) {
Daniel Veillard77851712001-02-27 21:54:07 +00002182 if (obj->boolval) {
2183 obj->type = XPATH_XSLT_TREE;
2184 if (obj->nodesetval != NULL)
2185 xmlXPathFreeValueTree(obj->nodesetval);
2186 } else {
2187 if (obj->nodesetval != NULL)
2188 xmlXPathFreeNodeSet(obj->nodesetval);
2189 }
Owen Taylor3473f882001-02-23 17:55:21 +00002190#ifdef LIBXML_XPTR_ENABLED
2191 } else if (obj->type == XPATH_LOCATIONSET) {
2192 if (obj->user != NULL)
2193 xmlXPtrFreeLocationSet(obj->user);
2194#endif
2195 } else if (obj->type == XPATH_STRING) {
2196 if (obj->stringval != NULL)
2197 xmlFree(obj->stringval);
2198 } else if (obj->type == XPATH_XSLT_TREE) {
2199 if (obj->nodesetval != NULL)
2200 xmlXPathFreeValueTree(obj->nodesetval);
2201 }
2202
Owen Taylor3473f882001-02-23 17:55:21 +00002203 xmlFree(obj);
2204}
2205
2206/************************************************************************
2207 * *
2208 * Routines to handle XPath contexts *
2209 * *
2210 ************************************************************************/
2211
2212/**
2213 * xmlXPathNewContext:
2214 * @doc: the XML document
2215 *
2216 * Create a new xmlXPathContext
2217 *
2218 * Returns the xmlXPathContext just allocated.
2219 */
2220xmlXPathContextPtr
2221xmlXPathNewContext(xmlDocPtr doc) {
2222 xmlXPathContextPtr ret;
2223
2224 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
2225 if (ret == NULL) {
2226 xmlGenericError(xmlGenericErrorContext,
2227 "xmlXPathNewContext: out of memory\n");
2228 return(NULL);
2229 }
2230 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
2231 ret->doc = doc;
2232 ret->node = NULL;
2233
2234 ret->varHash = NULL;
2235
2236 ret->nb_types = 0;
2237 ret->max_types = 0;
2238 ret->types = NULL;
2239
2240 ret->funcHash = xmlHashCreate(0);
2241
2242 ret->nb_axis = 0;
2243 ret->max_axis = 0;
2244 ret->axis = NULL;
2245
2246 ret->nsHash = NULL;
2247 ret->user = NULL;
2248
2249 ret->contextSize = -1;
2250 ret->proximityPosition = -1;
2251
2252 xmlXPathRegisterAllFunctions(ret);
2253
2254 return(ret);
2255}
2256
2257/**
2258 * xmlXPathFreeContext:
2259 * @ctxt: the context to free
2260 *
2261 * Free up an xmlXPathContext
2262 */
2263void
2264xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
2265 xmlXPathRegisteredNsCleanup(ctxt);
2266 xmlXPathRegisteredFuncsCleanup(ctxt);
2267 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00002268 xmlFree(ctxt);
2269}
2270
2271/************************************************************************
2272 * *
2273 * Routines to handle XPath parser contexts *
2274 * *
2275 ************************************************************************/
2276
2277#define CHECK_CTXT(ctxt) \
2278 if (ctxt == NULL) { \
2279 xmlGenericError(xmlGenericErrorContext, \
2280 "%s:%d Internal error: ctxt == NULL\n", \
2281 __FILE__, __LINE__); \
2282 } \
2283
2284
2285#define CHECK_CONTEXT(ctxt) \
2286 if (ctxt == NULL) { \
2287 xmlGenericError(xmlGenericErrorContext, \
2288 "%s:%d Internal error: no context\n", \
2289 __FILE__, __LINE__); \
2290 } \
2291 else if (ctxt->doc == NULL) { \
2292 xmlGenericError(xmlGenericErrorContext, \
2293 "%s:%d Internal error: no document\n", \
2294 __FILE__, __LINE__); \
2295 } \
2296 else if (ctxt->doc->children == NULL) { \
2297 xmlGenericError(xmlGenericErrorContext, \
2298 "%s:%d Internal error: document without root\n", \
2299 __FILE__, __LINE__); \
2300 } \
2301
2302
2303/**
2304 * xmlXPathNewParserContext:
2305 * @str: the XPath expression
2306 * @ctxt: the XPath context
2307 *
2308 * Create a new xmlXPathParserContext
2309 *
2310 * Returns the xmlXPathParserContext just allocated.
2311 */
2312xmlXPathParserContextPtr
2313xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
2314 xmlXPathParserContextPtr ret;
2315
2316 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2317 if (ret == NULL) {
2318 xmlGenericError(xmlGenericErrorContext,
2319 "xmlXPathNewParserContext: out of memory\n");
2320 return(NULL);
2321 }
2322 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2323 ret->cur = ret->base = str;
2324 ret->context = ctxt;
2325
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002326 ret->comp = xmlXPathNewCompExpr();
2327 if (ret->comp == NULL) {
2328 xmlFree(ret->valueTab);
2329 xmlFree(ret);
2330 return(NULL);
2331 }
2332
2333 return(ret);
2334}
2335
2336/**
2337 * xmlXPathCompParserContext:
2338 * @comp: the XPath compiled expression
2339 * @ctxt: the XPath context
2340 *
2341 * Create a new xmlXPathParserContext when processing a compiled expression
2342 *
2343 * Returns the xmlXPathParserContext just allocated.
2344 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002345static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002346xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
2347 xmlXPathParserContextPtr ret;
2348
2349 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2350 if (ret == NULL) {
2351 xmlGenericError(xmlGenericErrorContext,
2352 "xmlXPathNewParserContext: out of memory\n");
2353 return(NULL);
2354 }
2355 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2356
Owen Taylor3473f882001-02-23 17:55:21 +00002357 /* Allocate the value stack */
2358 ret->valueTab = (xmlXPathObjectPtr *)
2359 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002360 if (ret->valueTab == NULL) {
2361 xmlFree(ret);
2362 xmlGenericError(xmlGenericErrorContext,
2363 "xmlXPathNewParserContext: out of memory\n");
2364 return(NULL);
2365 }
Owen Taylor3473f882001-02-23 17:55:21 +00002366 ret->valueNr = 0;
2367 ret->valueMax = 10;
2368 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002369
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00002370 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002371 ret->comp = comp;
2372
Owen Taylor3473f882001-02-23 17:55:21 +00002373 return(ret);
2374}
2375
2376/**
2377 * xmlXPathFreeParserContext:
2378 * @ctxt: the context to free
2379 *
2380 * Free up an xmlXPathParserContext
2381 */
2382void
2383xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
2384 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002385 xmlFree(ctxt->valueTab);
2386 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002387 if (ctxt->comp)
2388 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00002389 xmlFree(ctxt);
2390}
2391
2392/************************************************************************
2393 * *
2394 * The implicit core function library *
2395 * *
2396 ************************************************************************/
2397
2398/*
2399 * Auto-pop and cast to a number
2400 */
2401void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
2402
2403
2404#define POP_FLOAT \
2405 arg = valuePop(ctxt); \
2406 if (arg == NULL) { \
2407 XP_ERROR(XPATH_INVALID_OPERAND); \
2408 } \
2409 if (arg->type != XPATH_NUMBER) { \
2410 valuePush(ctxt, arg); \
2411 xmlXPathNumberFunction(ctxt, 1); \
2412 arg = valuePop(ctxt); \
2413 }
2414
2415/**
2416 * xmlXPathCompareNodeSetFloat:
2417 * @ctxt: the XPath Parser context
2418 * @inf: less than (1) or greater than (0)
2419 * @strict: is the comparison strict
2420 * @arg: the node set
2421 * @f: the value
2422 *
2423 * Implement the compare operation between a nodeset and a number
2424 * @ns < @val (1, 1, ...
2425 * @ns <= @val (1, 0, ...
2426 * @ns > @val (0, 1, ...
2427 * @ns >= @val (0, 0, ...
2428 *
2429 * If one object to be compared is a node-set and the other is a number,
2430 * then the comparison will be true if and only if there is a node in the
2431 * node-set such that the result of performing the comparison on the number
2432 * to be compared and on the result of converting the string-value of that
2433 * node to a number using the number function is true.
2434 *
2435 * Returns 0 or 1 depending on the results of the test.
2436 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002437static int
Owen Taylor3473f882001-02-23 17:55:21 +00002438xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
2439 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
2440 int i, ret = 0;
2441 xmlNodeSetPtr ns;
2442 xmlChar *str2;
2443
2444 if ((f == NULL) || (arg == NULL) ||
2445 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2446 xmlXPathFreeObject(arg);
2447 xmlXPathFreeObject(f);
2448 return(0);
2449 }
2450 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002451 if (ns != NULL) {
2452 for (i = 0;i < ns->nodeNr;i++) {
2453 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2454 if (str2 != NULL) {
2455 valuePush(ctxt,
2456 xmlXPathNewString(str2));
2457 xmlFree(str2);
2458 xmlXPathNumberFunction(ctxt, 1);
2459 valuePush(ctxt, xmlXPathObjectCopy(f));
2460 ret = xmlXPathCompareValues(ctxt, inf, strict);
2461 if (ret)
2462 break;
2463 }
2464 }
Owen Taylor3473f882001-02-23 17:55:21 +00002465 }
2466 xmlXPathFreeObject(arg);
2467 xmlXPathFreeObject(f);
2468 return(ret);
2469}
2470
2471/**
2472 * xmlXPathCompareNodeSetString:
2473 * @ctxt: the XPath Parser context
2474 * @inf: less than (1) or greater than (0)
2475 * @strict: is the comparison strict
2476 * @arg: the node set
2477 * @s: the value
2478 *
2479 * Implement the compare operation between a nodeset and a string
2480 * @ns < @val (1, 1, ...
2481 * @ns <= @val (1, 0, ...
2482 * @ns > @val (0, 1, ...
2483 * @ns >= @val (0, 0, ...
2484 *
2485 * If one object to be compared is a node-set and the other is a string,
2486 * then the comparison will be true if and only if there is a node in
2487 * the node-set such that the result of performing the comparison on the
2488 * string-value of the node and the other string is true.
2489 *
2490 * Returns 0 or 1 depending on the results of the test.
2491 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002492static int
Owen Taylor3473f882001-02-23 17:55:21 +00002493xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
2494 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
2495 int i, ret = 0;
2496 xmlNodeSetPtr ns;
2497 xmlChar *str2;
2498
2499 if ((s == NULL) || (arg == NULL) ||
2500 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2501 xmlXPathFreeObject(arg);
2502 xmlXPathFreeObject(s);
2503 return(0);
2504 }
2505 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002506 if (ns != NULL) {
2507 for (i = 0;i < ns->nodeNr;i++) {
2508 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2509 if (str2 != NULL) {
2510 valuePush(ctxt,
2511 xmlXPathNewString(str2));
2512 xmlFree(str2);
2513 valuePush(ctxt, xmlXPathObjectCopy(s));
2514 ret = xmlXPathCompareValues(ctxt, inf, strict);
2515 if (ret)
2516 break;
2517 }
2518 }
Owen Taylor3473f882001-02-23 17:55:21 +00002519 }
2520 xmlXPathFreeObject(arg);
2521 xmlXPathFreeObject(s);
2522 return(ret);
2523}
2524
2525/**
2526 * xmlXPathCompareNodeSets:
Owen Taylor3473f882001-02-23 17:55:21 +00002527 * @op: less than (-1), equal (0) or greater than (1)
2528 * @strict: is the comparison strict
2529 * @arg1: the fist node set object
2530 * @arg2: the second node set object
2531 *
2532 * Implement the compare operation on nodesets:
2533 *
2534 * If both objects to be compared are node-sets, then the comparison
2535 * will be true if and only if there is a node in the first node-set
2536 * and a node in the second node-set such that the result of performing
2537 * the comparison on the string-values of the two nodes is true.
2538 * ....
2539 * When neither object to be compared is a node-set and the operator
2540 * is <=, <, >= or >, then the objects are compared by converting both
2541 * objects to numbers and comparing the numbers according to IEEE 754.
2542 * ....
2543 * The number function converts its argument to a number as follows:
2544 * - a string that consists of optional whitespace followed by an
2545 * optional minus sign followed by a Number followed by whitespace
2546 * is converted to the IEEE 754 number that is nearest (according
2547 * to the IEEE 754 round-to-nearest rule) to the mathematical value
2548 * represented by the string; any other string is converted to NaN
2549 *
2550 * Conclusion all nodes need to be converted first to their string value
2551 * and then the comparison must be done when possible
2552 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002553static int
2554xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00002555 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2556 int i, j, init = 0;
2557 double val1;
2558 double *values2;
2559 int ret = 0;
2560 xmlChar *str;
2561 xmlNodeSetPtr ns1;
2562 xmlNodeSetPtr ns2;
2563
2564 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002565 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
2566 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002567 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002568 }
Owen Taylor3473f882001-02-23 17:55:21 +00002569 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002570 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
2571 xmlXPathFreeObject(arg1);
2572 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002573 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002574 }
Owen Taylor3473f882001-02-23 17:55:21 +00002575
2576 ns1 = arg1->nodesetval;
2577 ns2 = arg2->nodesetval;
2578
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002579 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002580 xmlXPathFreeObject(arg1);
2581 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002582 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002583 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002584 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002585 xmlXPathFreeObject(arg1);
2586 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002587 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002588 }
Owen Taylor3473f882001-02-23 17:55:21 +00002589
2590 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
2591 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002592 xmlXPathFreeObject(arg1);
2593 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002594 return(0);
2595 }
2596 for (i = 0;i < ns1->nodeNr;i++) {
2597 str = xmlNodeGetContent(ns1->nodeTab[i]);
2598 if (str == NULL)
2599 continue;
2600 val1 = xmlXPathStringEvalNumber(str);
2601 xmlFree(str);
2602 if (isnan(val1))
2603 continue;
2604 for (j = 0;j < ns2->nodeNr;j++) {
2605 if (init == 0) {
2606 str = xmlNodeGetContent(ns2->nodeTab[j]);
2607 if (str == NULL) {
2608 values2[j] = xmlXPathNAN;
2609 } else {
2610 values2[j] = xmlXPathStringEvalNumber(str);
2611 xmlFree(str);
2612 }
2613 }
2614 if (isnan(values2[j]))
2615 continue;
2616 if (inf && strict)
2617 ret = (val1 < values2[j]);
2618 else if (inf && !strict)
2619 ret = (val1 <= values2[j]);
2620 else if (!inf && strict)
2621 ret = (val1 > values2[j]);
2622 else if (!inf && !strict)
2623 ret = (val1 >= values2[j]);
2624 if (ret)
2625 break;
2626 }
2627 if (ret)
2628 break;
2629 init = 1;
2630 }
2631 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002632 xmlXPathFreeObject(arg1);
2633 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002634 return(ret);
2635 return(0);
2636}
2637
2638/**
2639 * xmlXPathCompareNodeSetValue:
2640 * @ctxt: the XPath Parser context
2641 * @inf: less than (1) or greater than (0)
2642 * @strict: is the comparison strict
2643 * @arg: the node set
2644 * @val: the value
2645 *
2646 * Implement the compare operation between a nodeset and a value
2647 * @ns < @val (1, 1, ...
2648 * @ns <= @val (1, 0, ...
2649 * @ns > @val (0, 1, ...
2650 * @ns >= @val (0, 0, ...
2651 *
2652 * If one object to be compared is a node-set and the other is a boolean,
2653 * then the comparison will be true if and only if the result of performing
2654 * the comparison on the boolean and on the result of converting
2655 * the node-set to a boolean using the boolean function is true.
2656 *
2657 * Returns 0 or 1 depending on the results of the test.
2658 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002659static int
Owen Taylor3473f882001-02-23 17:55:21 +00002660xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
2661 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
2662 if ((val == NULL) || (arg == NULL) ||
2663 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2664 return(0);
2665
2666 switch(val->type) {
2667 case XPATH_NUMBER:
2668 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
2669 case XPATH_NODESET:
2670 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002671 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00002672 case XPATH_STRING:
2673 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
2674 case XPATH_BOOLEAN:
2675 valuePush(ctxt, arg);
2676 xmlXPathBooleanFunction(ctxt, 1);
2677 valuePush(ctxt, val);
2678 return(xmlXPathCompareValues(ctxt, inf, strict));
2679 default:
2680 TODO
2681 return(0);
2682 }
2683 return(0);
2684}
2685
2686/**
2687 * xmlXPathEqualNodeSetString
2688 * @arg: the nodeset object argument
2689 * @str: the string to compare to.
2690 *
2691 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2692 * If one object to be compared is a node-set and the other is a string,
2693 * then the comparison will be true if and only if there is a node in
2694 * the node-set such that the result of performing the comparison on the
2695 * string-value of the node and the other string is true.
2696 *
2697 * Returns 0 or 1 depending on the results of the test.
2698 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002699static int
Owen Taylor3473f882001-02-23 17:55:21 +00002700xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
2701 int i;
2702 xmlNodeSetPtr ns;
2703 xmlChar *str2;
2704
2705 if ((str == NULL) || (arg == NULL) ||
2706 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2707 return(0);
2708 ns = arg->nodesetval;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002709 if (ns == NULL)
2710 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002711 if (ns->nodeNr <= 0)
2712 return(0);
2713 for (i = 0;i < ns->nodeNr;i++) {
2714 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2715 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
2716 xmlFree(str2);
2717 return(1);
2718 }
2719 if (str2 != NULL)
2720 xmlFree(str2);
2721 }
2722 return(0);
2723}
2724
2725/**
2726 * xmlXPathEqualNodeSetFloat
2727 * @arg: the nodeset object argument
2728 * @f: the float to compare to
2729 *
2730 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2731 * If one object to be compared is a node-set and the other is a number,
2732 * then the comparison will be true if and only if there is a node in
2733 * the node-set such that the result of performing the comparison on the
2734 * number to be compared and on the result of converting the string-value
2735 * of that node to a number using the number function is true.
2736 *
2737 * Returns 0 or 1 depending on the results of the test.
2738 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002739static int
Owen Taylor3473f882001-02-23 17:55:21 +00002740xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
2741 char buf[100] = "";
2742
2743 if ((arg == NULL) ||
2744 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2745 return(0);
2746
Bjorn Reesee1dc0112001-03-03 12:09:03 +00002747 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00002748 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
2749}
2750
2751
2752/**
2753 * xmlXPathEqualNodeSets
2754 * @arg1: first nodeset object argument
2755 * @arg2: second nodeset object argument
2756 *
2757 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
2758 * If both objects to be compared are node-sets, then the comparison
2759 * will be true if and only if there is a node in the first node-set and
2760 * a node in the second node-set such that the result of performing the
2761 * comparison on the string-values of the two nodes is true.
2762 *
2763 * (needless to say, this is a costly operation)
2764 *
2765 * Returns 0 or 1 depending on the results of the test.
2766 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002767static int
Owen Taylor3473f882001-02-23 17:55:21 +00002768xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2769 int i, j;
2770 xmlChar **values1;
2771 xmlChar **values2;
2772 int ret = 0;
2773 xmlNodeSetPtr ns1;
2774 xmlNodeSetPtr ns2;
2775
2776 if ((arg1 == NULL) ||
2777 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2778 return(0);
2779 if ((arg2 == NULL) ||
2780 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2781 return(0);
2782
2783 ns1 = arg1->nodesetval;
2784 ns2 = arg2->nodesetval;
2785
Daniel Veillard911f49a2001-04-07 15:39:35 +00002786 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002787 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00002788 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002789 return(0);
2790
2791 /*
2792 * check if there is a node pertaining to both sets
2793 */
2794 for (i = 0;i < ns1->nodeNr;i++)
2795 for (j = 0;j < ns2->nodeNr;j++)
2796 if (ns1->nodeTab[i] == ns2->nodeTab[j])
2797 return(1);
2798
2799 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
2800 if (values1 == NULL)
2801 return(0);
2802 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
2803 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
2804 if (values2 == NULL) {
2805 xmlFree(values1);
2806 return(0);
2807 }
2808 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
2809 for (i = 0;i < ns1->nodeNr;i++) {
2810 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
2811 for (j = 0;j < ns2->nodeNr;j++) {
2812 if (i == 0)
2813 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
2814 ret = xmlStrEqual(values1[i], values2[j]);
2815 if (ret)
2816 break;
2817 }
2818 if (ret)
2819 break;
2820 }
2821 for (i = 0;i < ns1->nodeNr;i++)
2822 if (values1[i] != NULL)
2823 xmlFree(values1[i]);
2824 for (j = 0;j < ns2->nodeNr;j++)
2825 if (values2[j] != NULL)
2826 xmlFree(values2[j]);
2827 xmlFree(values1);
2828 xmlFree(values2);
2829 return(ret);
2830}
2831
2832/**
2833 * xmlXPathEqualValues:
2834 * @ctxt: the XPath Parser context
2835 *
2836 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2837 *
2838 * Returns 0 or 1 depending on the results of the test.
2839 */
2840int
2841xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
2842 xmlXPathObjectPtr arg1, arg2;
2843 int ret = 0;
2844
2845 arg1 = valuePop(ctxt);
2846 if (arg1 == NULL)
2847 XP_ERROR0(XPATH_INVALID_OPERAND);
2848
2849 arg2 = valuePop(ctxt);
2850 if (arg2 == NULL) {
2851 xmlXPathFreeObject(arg1);
2852 XP_ERROR0(XPATH_INVALID_OPERAND);
2853 }
2854
2855 if (arg1 == arg2) {
2856#ifdef DEBUG_EXPR
2857 xmlGenericError(xmlGenericErrorContext,
2858 "Equal: by pointer\n");
2859#endif
2860 return(1);
2861 }
2862
2863 switch (arg1->type) {
2864 case XPATH_UNDEFINED:
2865#ifdef DEBUG_EXPR
2866 xmlGenericError(xmlGenericErrorContext,
2867 "Equal: undefined\n");
2868#endif
2869 break;
2870 case XPATH_XSLT_TREE:
2871 case XPATH_NODESET:
2872 switch (arg2->type) {
2873 case XPATH_UNDEFINED:
2874#ifdef DEBUG_EXPR
2875 xmlGenericError(xmlGenericErrorContext,
2876 "Equal: undefined\n");
2877#endif
2878 break;
2879 case XPATH_XSLT_TREE:
2880 case XPATH_NODESET:
2881 ret = xmlXPathEqualNodeSets(arg1, arg2);
2882 break;
2883 case XPATH_BOOLEAN:
2884 if ((arg1->nodesetval == NULL) ||
2885 (arg1->nodesetval->nodeNr == 0)) ret = 0;
2886 else
2887 ret = 1;
2888 ret = (ret == arg2->boolval);
2889 break;
2890 case XPATH_NUMBER:
2891 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
2892 break;
2893 case XPATH_STRING:
2894 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
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_BOOLEAN:
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 if ((arg2->nodesetval == NULL) ||
2915 (arg2->nodesetval->nodeNr == 0)) ret = 0;
2916 else
2917 ret = 1;
2918 break;
2919 case XPATH_BOOLEAN:
2920#ifdef DEBUG_EXPR
2921 xmlGenericError(xmlGenericErrorContext,
2922 "Equal: %d boolean %d \n",
2923 arg1->boolval, arg2->boolval);
2924#endif
2925 ret = (arg1->boolval == arg2->boolval);
2926 break;
2927 case XPATH_NUMBER:
2928 if (arg2->floatval) ret = 1;
2929 else ret = 0;
2930 ret = (arg1->boolval == ret);
2931 break;
2932 case XPATH_STRING:
2933 if ((arg2->stringval == NULL) ||
2934 (arg2->stringval[0] == 0)) ret = 0;
2935 else
2936 ret = 1;
2937 ret = (arg1->boolval == ret);
2938 break;
2939 case XPATH_USERS:
2940 case XPATH_POINT:
2941 case XPATH_RANGE:
2942 case XPATH_LOCATIONSET:
2943 TODO
2944 break;
2945 }
2946 break;
2947 case XPATH_NUMBER:
2948 switch (arg2->type) {
2949 case XPATH_UNDEFINED:
2950#ifdef DEBUG_EXPR
2951 xmlGenericError(xmlGenericErrorContext,
2952 "Equal: undefined\n");
2953#endif
2954 break;
2955 case XPATH_NODESET:
2956 case XPATH_XSLT_TREE:
2957 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
2958 break;
2959 case XPATH_BOOLEAN:
2960 if (arg1->floatval) ret = 1;
2961 else ret = 0;
2962 ret = (arg2->boolval == ret);
2963 break;
2964 case XPATH_STRING:
2965 valuePush(ctxt, arg2);
2966 xmlXPathNumberFunction(ctxt, 1);
2967 arg2 = valuePop(ctxt);
2968 /* no break on purpose */
2969 case XPATH_NUMBER:
2970 ret = (arg1->floatval == arg2->floatval);
2971 break;
2972 case XPATH_USERS:
2973 case XPATH_POINT:
2974 case XPATH_RANGE:
2975 case XPATH_LOCATIONSET:
2976 TODO
2977 break;
2978 }
2979 break;
2980 case XPATH_STRING:
2981 switch (arg2->type) {
2982 case XPATH_UNDEFINED:
2983#ifdef DEBUG_EXPR
2984 xmlGenericError(xmlGenericErrorContext,
2985 "Equal: undefined\n");
2986#endif
2987 break;
2988 case XPATH_NODESET:
2989 case XPATH_XSLT_TREE:
2990 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
2991 break;
2992 case XPATH_BOOLEAN:
2993 if ((arg1->stringval == NULL) ||
2994 (arg1->stringval[0] == 0)) ret = 0;
2995 else
2996 ret = 1;
2997 ret = (arg2->boolval == ret);
2998 break;
2999 case XPATH_STRING:
3000 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
3001 break;
3002 case XPATH_NUMBER:
3003 valuePush(ctxt, arg1);
3004 xmlXPathNumberFunction(ctxt, 1);
3005 arg1 = valuePop(ctxt);
3006 ret = (arg1->floatval == arg2->floatval);
3007 break;
3008 case XPATH_USERS:
3009 case XPATH_POINT:
3010 case XPATH_RANGE:
3011 case XPATH_LOCATIONSET:
3012 TODO
3013 break;
3014 }
3015 break;
3016 case XPATH_USERS:
3017 case XPATH_POINT:
3018 case XPATH_RANGE:
3019 case XPATH_LOCATIONSET:
3020 TODO
3021 break;
3022 }
3023 xmlXPathFreeObject(arg1);
3024 xmlXPathFreeObject(arg2);
3025 return(ret);
3026}
3027
3028
3029/**
3030 * xmlXPathCompareValues:
3031 * @ctxt: the XPath Parser context
3032 * @inf: less than (1) or greater than (0)
3033 * @strict: is the comparison strict
3034 *
3035 * Implement the compare operation on XPath objects:
3036 * @arg1 < @arg2 (1, 1, ...
3037 * @arg1 <= @arg2 (1, 0, ...
3038 * @arg1 > @arg2 (0, 1, ...
3039 * @arg1 >= @arg2 (0, 0, ...
3040 *
3041 * When neither object to be compared is a node-set and the operator is
3042 * <=, <, >=, >, then the objects are compared by converted both objects
3043 * to numbers and comparing the numbers according to IEEE 754. The <
3044 * comparison will be true if and only if the first number is less than the
3045 * second number. The <= comparison will be true if and only if the first
3046 * number is less than or equal to the second number. The > comparison
3047 * will be true if and only if the first number is greater than the second
3048 * number. The >= comparison will be true if and only if the first number
3049 * is greater than or equal to the second number.
3050 *
3051 * Returns 1 if the comparaison succeeded, 0 if it failed
3052 */
3053int
3054xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
3055 int ret = 0;
3056 xmlXPathObjectPtr arg1, arg2;
3057
3058 arg2 = valuePop(ctxt);
3059 if (arg2 == NULL) {
3060 XP_ERROR0(XPATH_INVALID_OPERAND);
3061 }
3062
3063 arg1 = valuePop(ctxt);
3064 if (arg1 == NULL) {
3065 xmlXPathFreeObject(arg2);
3066 XP_ERROR0(XPATH_INVALID_OPERAND);
3067 }
3068
3069 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
3070 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003071 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003072 } else {
3073 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003074 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
3075 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003076 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003077 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
3078 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00003079 }
3080 }
3081 return(ret);
3082 }
3083
3084 if (arg1->type != XPATH_NUMBER) {
3085 valuePush(ctxt, arg1);
3086 xmlXPathNumberFunction(ctxt, 1);
3087 arg1 = valuePop(ctxt);
3088 }
3089 if (arg1->type != XPATH_NUMBER) {
3090 xmlXPathFreeObject(arg1);
3091 xmlXPathFreeObject(arg2);
3092 XP_ERROR0(XPATH_INVALID_OPERAND);
3093 }
3094 if (arg2->type != XPATH_NUMBER) {
3095 valuePush(ctxt, arg2);
3096 xmlXPathNumberFunction(ctxt, 1);
3097 arg2 = valuePop(ctxt);
3098 }
3099 if (arg2->type != XPATH_NUMBER) {
3100 xmlXPathFreeObject(arg1);
3101 xmlXPathFreeObject(arg2);
3102 XP_ERROR0(XPATH_INVALID_OPERAND);
3103 }
3104 /*
3105 * Add tests for infinity and nan
3106 * => feedback on 3.4 for Inf and NaN
3107 */
3108 if (inf && strict)
3109 ret = (arg1->floatval < arg2->floatval);
3110 else if (inf && !strict)
3111 ret = (arg1->floatval <= arg2->floatval);
3112 else if (!inf && strict)
3113 ret = (arg1->floatval > arg2->floatval);
3114 else if (!inf && !strict)
3115 ret = (arg1->floatval >= arg2->floatval);
3116 xmlXPathFreeObject(arg1);
3117 xmlXPathFreeObject(arg2);
3118 return(ret);
3119}
3120
3121/**
3122 * xmlXPathValueFlipSign:
3123 * @ctxt: the XPath Parser context
3124 *
3125 * Implement the unary - operation on an XPath object
3126 * The numeric operators convert their operands to numbers as if
3127 * by calling the number function.
3128 */
3129void
3130xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
3131 xmlXPathObjectPtr arg;
3132
3133 POP_FLOAT
3134 arg->floatval = -arg->floatval;
3135 valuePush(ctxt, arg);
3136}
3137
3138/**
3139 * xmlXPathAddValues:
3140 * @ctxt: the XPath Parser context
3141 *
3142 * Implement the add operation on XPath objects:
3143 * The numeric operators convert their operands to numbers as if
3144 * by calling the number function.
3145 */
3146void
3147xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
3148 xmlXPathObjectPtr arg;
3149 double val;
3150
3151 POP_FLOAT
3152 val = arg->floatval;
3153 xmlXPathFreeObject(arg);
3154
3155 POP_FLOAT
3156 arg->floatval += val;
3157 valuePush(ctxt, arg);
3158}
3159
3160/**
3161 * xmlXPathSubValues:
3162 * @ctxt: the XPath Parser context
3163 *
3164 * Implement the substraction operation on XPath objects:
3165 * The numeric operators convert their operands to numbers as if
3166 * by calling the number function.
3167 */
3168void
3169xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
3170 xmlXPathObjectPtr arg;
3171 double val;
3172
3173 POP_FLOAT
3174 val = arg->floatval;
3175 xmlXPathFreeObject(arg);
3176
3177 POP_FLOAT
3178 arg->floatval -= val;
3179 valuePush(ctxt, arg);
3180}
3181
3182/**
3183 * xmlXPathMultValues:
3184 * @ctxt: the XPath Parser context
3185 *
3186 * Implement the multiply operation on XPath objects:
3187 * The numeric operators convert their operands to numbers as if
3188 * by calling the number function.
3189 */
3190void
3191xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
3192 xmlXPathObjectPtr arg;
3193 double val;
3194
3195 POP_FLOAT
3196 val = arg->floatval;
3197 xmlXPathFreeObject(arg);
3198
3199 POP_FLOAT
3200 arg->floatval *= val;
3201 valuePush(ctxt, arg);
3202}
3203
3204/**
3205 * xmlXPathDivValues:
3206 * @ctxt: the XPath Parser context
3207 *
3208 * Implement the div operation on XPath objects @arg1 / @arg2:
3209 * The numeric operators convert their operands to numbers as if
3210 * by calling the number function.
3211 */
3212void
3213xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
3214 xmlXPathObjectPtr arg;
3215 double val;
3216
3217 POP_FLOAT
3218 val = arg->floatval;
3219 xmlXPathFreeObject(arg);
3220
3221 POP_FLOAT
3222 arg->floatval /= val;
3223 valuePush(ctxt, arg);
3224}
3225
3226/**
3227 * xmlXPathModValues:
3228 * @ctxt: the XPath Parser context
3229 *
3230 * Implement the mod operation on XPath objects: @arg1 / @arg2
3231 * The numeric operators convert their operands to numbers as if
3232 * by calling the number function.
3233 */
3234void
3235xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
3236 xmlXPathObjectPtr arg;
3237 int arg1, arg2;
3238
3239 POP_FLOAT
3240 arg2 = (int) arg->floatval;
3241 xmlXPathFreeObject(arg);
3242
3243 POP_FLOAT
3244 arg1 = (int) arg->floatval;
3245 arg->floatval = arg1 % arg2;
3246 valuePush(ctxt, arg);
3247}
3248
3249/************************************************************************
3250 * *
3251 * The traversal functions *
3252 * *
3253 ************************************************************************/
3254
Owen Taylor3473f882001-02-23 17:55:21 +00003255/*
3256 * A traversal function enumerates nodes along an axis.
3257 * Initially it must be called with NULL, and it indicates
3258 * termination on the axis by returning NULL.
3259 */
3260typedef xmlNodePtr (*xmlXPathTraversalFunction)
3261 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
3262
3263/**
3264 * xmlXPathNextSelf:
3265 * @ctxt: the XPath Parser context
3266 * @cur: the current node in the traversal
3267 *
3268 * Traversal function for the "self" direction
3269 * The self axis contains just the context node itself
3270 *
3271 * Returns the next element following that axis
3272 */
3273xmlNodePtr
3274xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3275 if (cur == NULL)
3276 return(ctxt->context->node);
3277 return(NULL);
3278}
3279
3280/**
3281 * xmlXPathNextChild:
3282 * @ctxt: the XPath Parser context
3283 * @cur: the current node in the traversal
3284 *
3285 * Traversal function for the "child" direction
3286 * The child axis contains the children of the context node in document order.
3287 *
3288 * Returns the next element following that axis
3289 */
3290xmlNodePtr
3291xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3292 if (cur == NULL) {
3293 if (ctxt->context->node == NULL) return(NULL);
3294 switch (ctxt->context->node->type) {
3295 case XML_ELEMENT_NODE:
3296 case XML_TEXT_NODE:
3297 case XML_CDATA_SECTION_NODE:
3298 case XML_ENTITY_REF_NODE:
3299 case XML_ENTITY_NODE:
3300 case XML_PI_NODE:
3301 case XML_COMMENT_NODE:
3302 case XML_NOTATION_NODE:
3303 case XML_DTD_NODE:
3304 return(ctxt->context->node->children);
3305 case XML_DOCUMENT_NODE:
3306 case XML_DOCUMENT_TYPE_NODE:
3307 case XML_DOCUMENT_FRAG_NODE:
3308 case XML_HTML_DOCUMENT_NODE:
3309#ifdef LIBXML_SGML_ENABLED
3310 case XML_SGML_DOCUMENT_NODE:
3311#endif
3312 return(((xmlDocPtr) ctxt->context->node)->children);
3313 case XML_ELEMENT_DECL:
3314 case XML_ATTRIBUTE_DECL:
3315 case XML_ENTITY_DECL:
3316 case XML_ATTRIBUTE_NODE:
3317 case XML_NAMESPACE_DECL:
3318 case XML_XINCLUDE_START:
3319 case XML_XINCLUDE_END:
3320 return(NULL);
3321 }
3322 return(NULL);
3323 }
3324 if ((cur->type == XML_DOCUMENT_NODE) ||
3325 (cur->type == XML_HTML_DOCUMENT_NODE))
3326 return(NULL);
3327 return(cur->next);
3328}
3329
3330/**
3331 * xmlXPathNextDescendant:
3332 * @ctxt: the XPath Parser context
3333 * @cur: the current node in the traversal
3334 *
3335 * Traversal function for the "descendant" direction
3336 * the descendant axis contains the descendants of the context node in document
3337 * order; a descendant is a child or a child of a child and so on.
3338 *
3339 * Returns the next element following that axis
3340 */
3341xmlNodePtr
3342xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3343 if (cur == NULL) {
3344 if (ctxt->context->node == NULL)
3345 return(NULL);
3346 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3347 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3348 return(NULL);
3349
3350 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3351 return(ctxt->context->doc->children);
3352 return(ctxt->context->node->children);
3353 }
3354
3355 if (cur->children != NULL)
3356 {
3357 if (cur->children->type != XML_ENTITY_DECL)
3358 return(cur->children);
3359 }
3360 if (cur->next != NULL) return(cur->next);
3361
3362 do {
3363 cur = cur->parent;
3364 if (cur == NULL) return(NULL);
3365 if (cur == ctxt->context->node) return(NULL);
3366 if (cur->next != NULL) {
3367 cur = cur->next;
3368 return(cur);
3369 }
3370 } while (cur != NULL);
3371 return(cur);
3372}
3373
3374/**
3375 * xmlXPathNextDescendantOrSelf:
3376 * @ctxt: the XPath Parser context
3377 * @cur: the current node in the traversal
3378 *
3379 * Traversal function for the "descendant-or-self" direction
3380 * the descendant-or-self axis contains the context node and the descendants
3381 * of the context node in document order; thus the context node is the first
3382 * node on the axis, and the first child of the context node is the second node
3383 * on the axis
3384 *
3385 * Returns the next element following that axis
3386 */
3387xmlNodePtr
3388xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3389 if (cur == NULL) {
3390 if (ctxt->context->node == NULL)
3391 return(NULL);
3392 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3393 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3394 return(NULL);
3395 return(ctxt->context->node);
3396 }
3397
3398 return(xmlXPathNextDescendant(ctxt, cur));
3399}
3400
3401/**
3402 * xmlXPathNextParent:
3403 * @ctxt: the XPath Parser context
3404 * @cur: the current node in the traversal
3405 *
3406 * Traversal function for the "parent" direction
3407 * The parent axis contains the parent of the context node, if there is one.
3408 *
3409 * Returns the next element following that axis
3410 */
3411xmlNodePtr
3412xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3413 /*
3414 * the parent of an attribute or namespace node is the element
3415 * to which the attribute or namespace node is attached
3416 * Namespace handling !!!
3417 */
3418 if (cur == NULL) {
3419 if (ctxt->context->node == NULL) return(NULL);
3420 switch (ctxt->context->node->type) {
3421 case XML_ELEMENT_NODE:
3422 case XML_TEXT_NODE:
3423 case XML_CDATA_SECTION_NODE:
3424 case XML_ENTITY_REF_NODE:
3425 case XML_ENTITY_NODE:
3426 case XML_PI_NODE:
3427 case XML_COMMENT_NODE:
3428 case XML_NOTATION_NODE:
3429 case XML_DTD_NODE:
3430 case XML_ELEMENT_DECL:
3431 case XML_ATTRIBUTE_DECL:
3432 case XML_XINCLUDE_START:
3433 case XML_XINCLUDE_END:
3434 case XML_ENTITY_DECL:
3435 if (ctxt->context->node->parent == NULL)
3436 return((xmlNodePtr) ctxt->context->doc);
3437 return(ctxt->context->node->parent);
3438 case XML_ATTRIBUTE_NODE: {
3439 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3440
3441 return(att->parent);
3442 }
3443 case XML_DOCUMENT_NODE:
3444 case XML_DOCUMENT_TYPE_NODE:
3445 case XML_DOCUMENT_FRAG_NODE:
3446 case XML_HTML_DOCUMENT_NODE:
3447#ifdef LIBXML_SGML_ENABLED
3448 case XML_SGML_DOCUMENT_NODE:
3449#endif
3450 return(NULL);
3451 case XML_NAMESPACE_DECL:
3452 /*
3453 * TODO !!! may require extending struct _xmlNs with
3454 * parent field
3455 * C.f. Infoset case...
3456 */
3457 return(NULL);
3458 }
3459 }
3460 return(NULL);
3461}
3462
3463/**
3464 * xmlXPathNextAncestor:
3465 * @ctxt: the XPath Parser context
3466 * @cur: the current node in the traversal
3467 *
3468 * Traversal function for the "ancestor" direction
3469 * the ancestor axis contains the ancestors of the context node; the ancestors
3470 * of the context node consist of the parent of context node and the parent's
3471 * parent and so on; the nodes are ordered in reverse document order; thus the
3472 * parent is the first node on the axis, and the parent's parent is the second
3473 * node on the axis
3474 *
3475 * Returns the next element following that axis
3476 */
3477xmlNodePtr
3478xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3479 /*
3480 * the parent of an attribute or namespace node is the element
3481 * to which the attribute or namespace node is attached
3482 * !!!!!!!!!!!!!
3483 */
3484 if (cur == NULL) {
3485 if (ctxt->context->node == NULL) return(NULL);
3486 switch (ctxt->context->node->type) {
3487 case XML_ELEMENT_NODE:
3488 case XML_TEXT_NODE:
3489 case XML_CDATA_SECTION_NODE:
3490 case XML_ENTITY_REF_NODE:
3491 case XML_ENTITY_NODE:
3492 case XML_PI_NODE:
3493 case XML_COMMENT_NODE:
3494 case XML_DTD_NODE:
3495 case XML_ELEMENT_DECL:
3496 case XML_ATTRIBUTE_DECL:
3497 case XML_ENTITY_DECL:
3498 case XML_NOTATION_NODE:
3499 case XML_XINCLUDE_START:
3500 case XML_XINCLUDE_END:
3501 if (ctxt->context->node->parent == NULL)
3502 return((xmlNodePtr) ctxt->context->doc);
3503 return(ctxt->context->node->parent);
3504 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003505 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003506
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003507 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00003508 }
3509 case XML_DOCUMENT_NODE:
3510 case XML_DOCUMENT_TYPE_NODE:
3511 case XML_DOCUMENT_FRAG_NODE:
3512 case XML_HTML_DOCUMENT_NODE:
3513#ifdef LIBXML_SGML_ENABLED
3514 case XML_SGML_DOCUMENT_NODE:
3515#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 if (cur == ctxt->context->doc->children)
3528 return((xmlNodePtr) ctxt->context->doc);
3529 if (cur == (xmlNodePtr) ctxt->context->doc)
3530 return(NULL);
3531 switch (cur->type) {
3532 case XML_ELEMENT_NODE:
3533 case XML_TEXT_NODE:
3534 case XML_CDATA_SECTION_NODE:
3535 case XML_ENTITY_REF_NODE:
3536 case XML_ENTITY_NODE:
3537 case XML_PI_NODE:
3538 case XML_COMMENT_NODE:
3539 case XML_NOTATION_NODE:
3540 case XML_DTD_NODE:
3541 case XML_ELEMENT_DECL:
3542 case XML_ATTRIBUTE_DECL:
3543 case XML_ENTITY_DECL:
3544 case XML_XINCLUDE_START:
3545 case XML_XINCLUDE_END:
3546 return(cur->parent);
3547 case XML_ATTRIBUTE_NODE: {
3548 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3549
3550 return(att->parent);
3551 }
3552 case XML_DOCUMENT_NODE:
3553 case XML_DOCUMENT_TYPE_NODE:
3554 case XML_DOCUMENT_FRAG_NODE:
3555 case XML_HTML_DOCUMENT_NODE:
3556#ifdef LIBXML_SGML_ENABLED
3557 case XML_SGML_DOCUMENT_NODE:
3558#endif
3559 return(NULL);
3560 case XML_NAMESPACE_DECL:
3561 /*
3562 * TODO !!! may require extending struct _xmlNs with
3563 * parent field
3564 * C.f. Infoset case...
3565 */
3566 return(NULL);
3567 }
3568 return(NULL);
3569}
3570
3571/**
3572 * xmlXPathNextAncestorOrSelf:
3573 * @ctxt: the XPath Parser context
3574 * @cur: the current node in the traversal
3575 *
3576 * Traversal function for the "ancestor-or-self" direction
3577 * he ancestor-or-self axis contains the context node and ancestors of
3578 * the context node in reverse document order; thus the context node is
3579 * the first node on the axis, and the context node's parent the second;
3580 * parent here is defined the same as with the parent axis.
3581 *
3582 * Returns the next element following that axis
3583 */
3584xmlNodePtr
3585xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3586 if (cur == NULL)
3587 return(ctxt->context->node);
3588 return(xmlXPathNextAncestor(ctxt, cur));
3589}
3590
3591/**
3592 * xmlXPathNextFollowingSibling:
3593 * @ctxt: the XPath Parser context
3594 * @cur: the current node in the traversal
3595 *
3596 * Traversal function for the "following-sibling" direction
3597 * The following-sibling axis contains the following siblings of the context
3598 * node in document order.
3599 *
3600 * Returns the next element following that axis
3601 */
3602xmlNodePtr
3603xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3604 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3605 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3606 return(NULL);
3607 if (cur == (xmlNodePtr) ctxt->context->doc)
3608 return(NULL);
3609 if (cur == NULL)
3610 return(ctxt->context->node->next);
3611 return(cur->next);
3612}
3613
3614/**
3615 * xmlXPathNextPrecedingSibling:
3616 * @ctxt: the XPath Parser context
3617 * @cur: the current node in the traversal
3618 *
3619 * Traversal function for the "preceding-sibling" direction
3620 * The preceding-sibling axis contains the preceding siblings of the context
3621 * node in reverse document order; the first preceding sibling is first on the
3622 * axis; the sibling preceding that node is the second on the axis and so on.
3623 *
3624 * Returns the next element following that axis
3625 */
3626xmlNodePtr
3627xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3628 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3629 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3630 return(NULL);
3631 if (cur == (xmlNodePtr) ctxt->context->doc)
3632 return(NULL);
3633 if (cur == NULL)
3634 return(ctxt->context->node->prev);
3635 return(cur->prev);
3636}
3637
3638/**
3639 * xmlXPathNextFollowing:
3640 * @ctxt: the XPath Parser context
3641 * @cur: the current node in the traversal
3642 *
3643 * Traversal function for the "following" direction
3644 * The following axis contains all nodes in the same document as the context
3645 * node that are after the context node in document order, excluding any
3646 * descendants and excluding attribute nodes and namespace nodes; the nodes
3647 * are ordered in document order
3648 *
3649 * Returns the next element following that axis
3650 */
3651xmlNodePtr
3652xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3653 if (cur != NULL && cur->children != NULL)
3654 return cur->children ;
3655 if (cur == NULL) cur = ctxt->context->node;
3656 if (cur == NULL) return(NULL) ; /* ERROR */
3657 if (cur->next != NULL) return(cur->next) ;
3658 do {
3659 cur = cur->parent;
3660 if (cur == NULL) return(NULL);
3661 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3662 if (cur->next != NULL) return(cur->next);
3663 } while (cur != NULL);
3664 return(cur);
3665}
3666
3667/*
3668 * xmlXPathIsAncestor:
3669 * @ancestor: the ancestor node
3670 * @node: the current node
3671 *
3672 * Check that @ancestor is a @node's ancestor
3673 *
3674 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3675 */
3676static int
3677xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3678 if ((ancestor == NULL) || (node == NULL)) return(0);
3679 /* nodes need to be in the same document */
3680 if (ancestor->doc != node->doc) return(0);
3681 /* avoid searching if ancestor or node is the root node */
3682 if (ancestor == (xmlNodePtr) node->doc) return(1);
3683 if (node == (xmlNodePtr) ancestor->doc) return(0);
3684 while (node->parent != NULL) {
3685 if (node->parent == ancestor)
3686 return(1);
3687 node = node->parent;
3688 }
3689 return(0);
3690}
3691
3692/**
3693 * xmlXPathNextPreceding:
3694 * @ctxt: the XPath Parser context
3695 * @cur: the current node in the traversal
3696 *
3697 * Traversal function for the "preceding" direction
3698 * the preceding axis contains all nodes in the same document as the context
3699 * node that are before the context node in document order, excluding any
3700 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3701 * ordered in reverse document order
3702 *
3703 * Returns the next element following that axis
3704 */
3705xmlNodePtr
3706xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3707 if (cur == NULL)
3708 cur = ctxt->context->node ;
3709 do {
3710 if (cur->prev != NULL) {
3711 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3712 ;
3713 return(cur) ;
3714 }
3715
3716 cur = cur->parent;
3717 if (cur == NULL) return(NULL);
3718 if (cur == ctxt->context->doc->children) return(NULL);
3719 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3720 return(cur);
3721}
3722
3723/**
3724 * xmlXPathNextNamespace:
3725 * @ctxt: the XPath Parser context
3726 * @cur: the current attribute in the traversal
3727 *
3728 * Traversal function for the "namespace" direction
3729 * the namespace axis contains the namespace nodes of the context node;
3730 * the order of nodes on this axis is implementation-defined; the axis will
3731 * be empty unless the context node is an element
3732 *
3733 * Returns the next element following that axis
3734 */
3735xmlNodePtr
3736xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3737 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3738 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3739 if (ctxt->context->namespaces != NULL)
3740 xmlFree(ctxt->context->namespaces);
3741 ctxt->context->namespaces =
3742 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3743 if (ctxt->context->namespaces == NULL) return(NULL);
3744 ctxt->context->nsNr = 0;
3745 }
3746 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3747}
3748
3749/**
3750 * xmlXPathNextAttribute:
3751 * @ctxt: the XPath Parser context
3752 * @cur: the current attribute in the traversal
3753 *
3754 * Traversal function for the "attribute" direction
3755 * TODO: support DTD inherited default attributes
3756 *
3757 * Returns the next element following that axis
3758 */
3759xmlNodePtr
3760xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3761 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3762 if (cur == NULL) {
3763 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3764 return(NULL);
3765 return((xmlNodePtr)ctxt->context->node->properties);
3766 }
3767 return((xmlNodePtr)cur->next);
3768}
3769
3770/************************************************************************
3771 * *
3772 * NodeTest Functions *
3773 * *
3774 ************************************************************************/
3775
Owen Taylor3473f882001-02-23 17:55:21 +00003776#define IS_FUNCTION 200
3777
Owen Taylor3473f882001-02-23 17:55:21 +00003778
3779/************************************************************************
3780 * *
3781 * Implicit tree core function library *
3782 * *
3783 ************************************************************************/
3784
3785/**
3786 * xmlXPathRoot:
3787 * @ctxt: the XPath Parser context
3788 *
3789 * Initialize the context to the root of the document
3790 */
3791void
3792xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
3793 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
3794 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3795}
3796
3797/************************************************************************
3798 * *
3799 * The explicit core function library *
3800 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
3801 * *
3802 ************************************************************************/
3803
3804
3805/**
3806 * xmlXPathLastFunction:
3807 * @ctxt: the XPath Parser context
3808 * @nargs: the number of arguments
3809 *
3810 * Implement the last() XPath function
3811 * number last()
3812 * The last function returns the number of nodes in the context node list.
3813 */
3814void
3815xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3816 CHECK_ARITY(0);
3817 if (ctxt->context->contextSize >= 0) {
3818 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
3819#ifdef DEBUG_EXPR
3820 xmlGenericError(xmlGenericErrorContext,
3821 "last() : %d\n", ctxt->context->contextSize);
3822#endif
3823 } else {
3824 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
3825 }
3826}
3827
3828/**
3829 * xmlXPathPositionFunction:
3830 * @ctxt: the XPath Parser context
3831 * @nargs: the number of arguments
3832 *
3833 * Implement the position() XPath function
3834 * number position()
3835 * The position function returns the position of the context node in the
3836 * context node list. The first position is 1, and so the last positionr
3837 * will be equal to last().
3838 */
3839void
3840xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3841 CHECK_ARITY(0);
3842 if (ctxt->context->proximityPosition >= 0) {
3843 valuePush(ctxt,
3844 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
3845#ifdef DEBUG_EXPR
3846 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
3847 ctxt->context->proximityPosition);
3848#endif
3849 } else {
3850 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
3851 }
3852}
3853
3854/**
3855 * xmlXPathCountFunction:
3856 * @ctxt: the XPath Parser context
3857 * @nargs: the number of arguments
3858 *
3859 * Implement the count() XPath function
3860 * number count(node-set)
3861 */
3862void
3863xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3864 xmlXPathObjectPtr cur;
3865
3866 CHECK_ARITY(1);
3867 if ((ctxt->value == NULL) ||
3868 ((ctxt->value->type != XPATH_NODESET) &&
3869 (ctxt->value->type != XPATH_XSLT_TREE)))
3870 XP_ERROR(XPATH_INVALID_TYPE);
3871 cur = valuePop(ctxt);
3872
Daniel Veillard911f49a2001-04-07 15:39:35 +00003873 if ((cur == NULL) || (cur->nodesetval == NULL))
3874 valuePush(ctxt, xmlXPathNewFloat((double) 0));
3875 else
3876 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Owen Taylor3473f882001-02-23 17:55:21 +00003877 xmlXPathFreeObject(cur);
3878}
3879
3880/**
3881 * xmlXPathIdFunction:
3882 * @ctxt: the XPath Parser context
3883 * @nargs: the number of arguments
3884 *
3885 * Implement the id() XPath function
3886 * node-set id(object)
3887 * The id function selects elements by their unique ID
3888 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
3889 * then the result is the union of the result of applying id to the
3890 * string value of each of the nodes in the argument node-set. When the
3891 * argument to id is of any other type, the argument is converted to a
3892 * string as if by a call to the string function; the string is split
3893 * into a whitespace-separated list of tokens (whitespace is any sequence
3894 * of characters matching the production S); the result is a node-set
3895 * containing the elements in the same document as the context node that
3896 * have a unique ID equal to any of the tokens in the list.
3897 */
3898void
3899xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3900 const xmlChar *tokens;
3901 const xmlChar *cur;
3902 xmlChar *ID;
3903 xmlAttrPtr attr;
3904 xmlNodePtr elem = NULL;
3905 xmlXPathObjectPtr ret, obj;
3906
3907 CHECK_ARITY(1);
3908 obj = valuePop(ctxt);
3909 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3910 if (obj->type == XPATH_NODESET) {
3911 xmlXPathObjectPtr newobj;
3912 int i;
3913
3914 ret = xmlXPathNewNodeSet(NULL);
3915
Daniel Veillard911f49a2001-04-07 15:39:35 +00003916 if (obj->nodesetval != NULL) {
3917 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3918 valuePush(ctxt,
3919 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
3920 xmlXPathStringFunction(ctxt, 1);
3921 xmlXPathIdFunction(ctxt, 1);
3922 newobj = valuePop(ctxt);
3923 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
3924 newobj->nodesetval);
3925 xmlXPathFreeObject(newobj);
3926 }
Owen Taylor3473f882001-02-23 17:55:21 +00003927 }
3928
3929 xmlXPathFreeObject(obj);
3930 valuePush(ctxt, ret);
3931 return;
3932 }
3933 if (obj->type != XPATH_STRING) {
3934 valuePush(ctxt, obj);
3935 xmlXPathStringFunction(ctxt, 1);
3936 obj = valuePop(ctxt);
3937 if (obj->type != XPATH_STRING) {
3938 xmlXPathFreeObject(obj);
3939 return;
3940 }
3941 }
3942 tokens = obj->stringval;
3943
3944 ret = xmlXPathNewNodeSet(NULL);
3945 valuePush(ctxt, ret);
3946 if (tokens == NULL) {
3947 xmlXPathFreeObject(obj);
3948 return;
3949 }
3950
3951 cur = tokens;
3952
3953 while (IS_BLANK(*cur)) cur++;
3954 while (*cur != 0) {
3955 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
3956 (*cur == '.') || (*cur == '-') ||
3957 (*cur == '_') || (*cur == ':') ||
3958 (IS_COMBINING(*cur)) ||
3959 (IS_EXTENDER(*cur)))
3960 cur++;
3961
3962 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
3963
3964 ID = xmlStrndup(tokens, cur - tokens);
3965 attr = xmlGetID(ctxt->context->doc, ID);
3966 if (attr != NULL) {
3967 elem = attr->parent;
3968 xmlXPathNodeSetAdd(ret->nodesetval, elem);
3969 }
3970 if (ID != NULL)
3971 xmlFree(ID);
3972
3973 while (IS_BLANK(*cur)) cur++;
3974 tokens = cur;
3975 }
3976 xmlXPathFreeObject(obj);
3977 return;
3978}
3979
3980/**
3981 * xmlXPathLocalNameFunction:
3982 * @ctxt: the XPath Parser context
3983 * @nargs: the number of arguments
3984 *
3985 * Implement the local-name() XPath function
3986 * string local-name(node-set?)
3987 * The local-name function returns a string containing the local part
3988 * of the name of the node in the argument node-set that is first in
3989 * document order. If the node-set is empty or the first node has no
3990 * name, an empty string is returned. If the argument is omitted it
3991 * defaults to the context node.
3992 */
3993void
3994xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3995 xmlXPathObjectPtr cur;
3996
3997 if (nargs == 0) {
3998 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3999 nargs = 1;
4000 }
4001
4002 CHECK_ARITY(1);
4003 if ((ctxt->value == NULL) ||
4004 ((ctxt->value->type != XPATH_NODESET) &&
4005 (ctxt->value->type != XPATH_XSLT_TREE)))
4006 XP_ERROR(XPATH_INVALID_TYPE);
4007 cur = valuePop(ctxt);
4008
Daniel Veillard911f49a2001-04-07 15:39:35 +00004009 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004010 valuePush(ctxt, xmlXPathNewCString(""));
4011 } else {
4012 int i = 0; /* Should be first in document order !!!!! */
4013 switch (cur->nodesetval->nodeTab[i]->type) {
4014 case XML_ELEMENT_NODE:
4015 case XML_ATTRIBUTE_NODE:
4016 case XML_PI_NODE:
4017 valuePush(ctxt,
4018 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
4019 break;
4020 case XML_NAMESPACE_DECL:
4021 valuePush(ctxt, xmlXPathNewString(
4022 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
4023 break;
4024 default:
4025 valuePush(ctxt, xmlXPathNewCString(""));
4026 }
4027 }
4028 xmlXPathFreeObject(cur);
4029}
4030
4031/**
4032 * xmlXPathNamespaceURIFunction:
4033 * @ctxt: the XPath Parser context
4034 * @nargs: the number of arguments
4035 *
4036 * Implement the namespace-uri() XPath function
4037 * string namespace-uri(node-set?)
4038 * The namespace-uri function returns a string containing the
4039 * namespace URI of the expanded name of the node in the argument
4040 * node-set that is first in document order. If the node-set is empty,
4041 * the first node has no name, or the expanded name has no namespace
4042 * URI, an empty string is returned. If the argument is omitted it
4043 * defaults to the context node.
4044 */
4045void
4046xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4047 xmlXPathObjectPtr cur;
4048
4049 if (nargs == 0) {
4050 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4051 nargs = 1;
4052 }
4053 CHECK_ARITY(1);
4054 if ((ctxt->value == NULL) ||
4055 ((ctxt->value->type != XPATH_NODESET) &&
4056 (ctxt->value->type != XPATH_XSLT_TREE)))
4057 XP_ERROR(XPATH_INVALID_TYPE);
4058 cur = valuePop(ctxt);
4059
Daniel Veillard911f49a2001-04-07 15:39:35 +00004060 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004061 valuePush(ctxt, xmlXPathNewCString(""));
4062 } else {
4063 int i = 0; /* Should be first in document order !!!!! */
4064 switch (cur->nodesetval->nodeTab[i]->type) {
4065 case XML_ELEMENT_NODE:
4066 case XML_ATTRIBUTE_NODE:
4067 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4068 valuePush(ctxt, xmlXPathNewCString(""));
4069 else
4070 valuePush(ctxt, xmlXPathNewString(
4071 cur->nodesetval->nodeTab[i]->ns->href));
4072 break;
4073 default:
4074 valuePush(ctxt, xmlXPathNewCString(""));
4075 }
4076 }
4077 xmlXPathFreeObject(cur);
4078}
4079
4080/**
4081 * xmlXPathNameFunction:
4082 * @ctxt: the XPath Parser context
4083 * @nargs: the number of arguments
4084 *
4085 * Implement the name() XPath function
4086 * string name(node-set?)
4087 * The name function returns a string containing a QName representing
4088 * the name of the node in the argument node-set that is first in documenti
4089 * order. The QName must represent the name with respect to the namespace
4090 * declarations in effect on the node whose name is being represented.
4091 * Typically, this will be the form in which the name occurred in the XML
4092 * source. This need not be the case if there are namespace declarations
4093 * in effect on the node that associate multiple prefixes with the same
4094 * namespace. However, an implementation may include information about
4095 * the original prefix in its representation of nodes; in this case, an
4096 * implementation can ensure that the returned string is always the same
4097 * as the QName used in the XML source. If the argument it omitted it
4098 * defaults to the context node.
4099 * Libxml keep the original prefix so the "real qualified name" used is
4100 * returned.
4101 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004102static void
Owen Taylor3473f882001-02-23 17:55:21 +00004103xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4104 xmlXPathObjectPtr cur;
4105
4106 if (nargs == 0) {
4107 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4108 nargs = 1;
4109 }
4110
4111 CHECK_ARITY(1);
4112 if ((ctxt->value == NULL) ||
4113 ((ctxt->value->type != XPATH_NODESET) &&
4114 (ctxt->value->type != XPATH_XSLT_TREE)))
4115 XP_ERROR(XPATH_INVALID_TYPE);
4116 cur = valuePop(ctxt);
4117
Daniel Veillard911f49a2001-04-07 15:39:35 +00004118 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004119 valuePush(ctxt, xmlXPathNewCString(""));
4120 } else {
4121 int i = 0; /* Should be first in document order !!!!! */
4122
4123 switch (cur->nodesetval->nodeTab[i]->type) {
4124 case XML_ELEMENT_NODE:
4125 case XML_ATTRIBUTE_NODE:
4126 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4127 valuePush(ctxt, xmlXPathNewString(
4128 cur->nodesetval->nodeTab[i]->name));
4129
4130 else {
4131 char name[2000];
Owen Taylor3473f882001-02-23 17:55:21 +00004132 snprintf(name, sizeof(name), "%s:%s",
4133 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4134 (char *) cur->nodesetval->nodeTab[i]->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004135 name[sizeof(name) - 1] = 0;
4136 valuePush(ctxt, xmlXPathNewCString(name));
4137 }
4138 break;
4139 default:
4140 valuePush(ctxt,
4141 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4142 xmlXPathLocalNameFunction(ctxt, 1);
4143 }
4144 }
4145 xmlXPathFreeObject(cur);
4146}
4147
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004148
4149/**
4150 * xmlXPathConvertString:
4151 * @val: an XPath object
4152 *
4153 * Converts an existing object to its string() equivalent
4154 *
4155 * Returns the new object, the old one is freed (or the operation
4156 * is done directly on @val)
4157 */
4158xmlXPathObjectPtr
4159xmlXPathConvertString(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004160 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004161
4162 if (val == NULL)
4163 return(xmlXPathNewCString(""));
4164 switch (val->type) {
4165 case XPATH_UNDEFINED:
4166#ifdef DEBUG_EXPR
4167 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4168#endif
4169 ret = xmlXPathNewCString("");
4170 break;
4171 case XPATH_XSLT_TREE:
4172 case XPATH_NODESET:
Daniel Veillard911f49a2001-04-07 15:39:35 +00004173 if ((val->nodesetval == NULL) || (val->nodesetval->nodeNr == 0)) {
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004174 ret = xmlXPathNewCString("");
4175 } else {
4176 xmlChar *res;
4177
4178 xmlXPathNodeSetSort(val->nodesetval);
4179 res = xmlNodeGetContent(val->nodesetval->nodeTab[0]);
4180 /* TODO: avoid allocating res to free it */
4181 ret = xmlXPathNewString(res);
4182 if (res != NULL)
4183 xmlFree(res);
4184 }
4185 break;
4186 case XPATH_STRING:
4187 return(val);
4188 case XPATH_BOOLEAN:
4189 if (val->boolval) ret = xmlXPathNewCString("true");
4190 else ret = xmlXPathNewCString("false");
4191 break;
4192 case XPATH_NUMBER: {
4193 char buf[100];
4194
4195 xmlXPathFormatNumber(val->floatval, buf, sizeof(buf));
4196 ret = xmlXPathNewCString(buf);
4197 break;
4198 }
4199 case XPATH_USERS:
4200 case XPATH_POINT:
4201 case XPATH_RANGE:
4202 case XPATH_LOCATIONSET:
4203 TODO
4204 ret = xmlXPathNewCString("");
4205 break;
4206 }
4207 xmlXPathFreeObject(val);
4208 return(ret);
4209}
4210
Owen Taylor3473f882001-02-23 17:55:21 +00004211/**
4212 * xmlXPathStringFunction:
4213 * @ctxt: the XPath Parser context
4214 * @nargs: the number of arguments
4215 *
4216 * Implement the string() XPath function
4217 * string string(object?)
4218 * he string function converts an object to a string as follows:
4219 * - A node-set is converted to a string by returning the value of
4220 * the node in the node-set that is first in document order.
4221 * If the node-set is empty, an empty string is returned.
4222 * - A number is converted to a string as follows
4223 * + NaN is converted to the string NaN
4224 * + positive zero is converted to the string 0
4225 * + negative zero is converted to the string 0
4226 * + positive infinity is converted to the string Infinity
4227 * + negative infinity is converted to the string -Infinity
4228 * + if the number is an integer, the number is represented in
4229 * decimal form as a Number with no decimal point and no leading
4230 * zeros, preceded by a minus sign (-) if the number is negative
4231 * + otherwise, the number is represented in decimal form as a
4232 * Number including a decimal point with at least one digit
4233 * before the decimal point and at least one digit after the
4234 * decimal point, preceded by a minus sign (-) if the number
4235 * is negative; there must be no leading zeros before the decimal
4236 * point apart possibly from the one required digit immediatelyi
4237 * before the decimal point; beyond the one required digit
4238 * after the decimal point there must be as many, but only as
4239 * many, more digits as are needed to uniquely distinguish the
4240 * number from all other IEEE 754 numeric values.
4241 * - The boolean false value is converted to the string false.
4242 * The boolean true value is converted to the string true.
4243 *
4244 * If the argument is omitted, it defaults to a node-set with the
4245 * context node as its only member.
4246 */
4247void
4248xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4249 xmlXPathObjectPtr cur;
4250
4251 if (nargs == 0) {
4252 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4253 nargs = 1;
4254 }
4255
4256 CHECK_ARITY(1);
4257 cur = valuePop(ctxt);
4258 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004259 cur = xmlXPathConvertString(cur);
4260 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004261}
4262
4263/**
4264 * xmlXPathStringLengthFunction:
4265 * @ctxt: the XPath Parser context
4266 * @nargs: the number of arguments
4267 *
4268 * Implement the string-length() XPath function
4269 * number string-length(string?)
4270 * The string-length returns the number of characters in the string
4271 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4272 * the context node converted to a string, in other words the value
4273 * of the context node.
4274 */
4275void
4276xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4277 xmlXPathObjectPtr cur;
4278
4279 if (nargs == 0) {
4280 if (ctxt->context->node == NULL) {
4281 valuePush(ctxt, xmlXPathNewFloat(0));
4282 } else {
4283 xmlChar *content;
4284
4285 content = xmlNodeGetContent(ctxt->context->node);
4286 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content)));
4287 xmlFree(content);
4288 }
4289 return;
4290 }
4291 CHECK_ARITY(1);
4292 CAST_TO_STRING;
4293 CHECK_TYPE(XPATH_STRING);
4294 cur = valuePop(ctxt);
4295 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval)));
4296 xmlXPathFreeObject(cur);
4297}
4298
4299/**
4300 * xmlXPathConcatFunction:
4301 * @ctxt: the XPath Parser context
4302 * @nargs: the number of arguments
4303 *
4304 * Implement the concat() XPath function
4305 * string concat(string, string, string*)
4306 * The concat function returns the concatenation of its arguments.
4307 */
4308void
4309xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4310 xmlXPathObjectPtr cur, newobj;
4311 xmlChar *tmp;
4312
4313 if (nargs < 2) {
4314 CHECK_ARITY(2);
4315 }
4316
4317 CAST_TO_STRING;
4318 cur = valuePop(ctxt);
4319 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4320 xmlXPathFreeObject(cur);
4321 return;
4322 }
4323 nargs--;
4324
4325 while (nargs > 0) {
4326 CAST_TO_STRING;
4327 newobj = valuePop(ctxt);
4328 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4329 xmlXPathFreeObject(newobj);
4330 xmlXPathFreeObject(cur);
4331 XP_ERROR(XPATH_INVALID_TYPE);
4332 }
4333 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4334 newobj->stringval = cur->stringval;
4335 cur->stringval = tmp;
4336
4337 xmlXPathFreeObject(newobj);
4338 nargs--;
4339 }
4340 valuePush(ctxt, cur);
4341}
4342
4343/**
4344 * xmlXPathContainsFunction:
4345 * @ctxt: the XPath Parser context
4346 * @nargs: the number of arguments
4347 *
4348 * Implement the contains() XPath function
4349 * boolean contains(string, string)
4350 * The contains function returns true if the first argument string
4351 * contains the second argument string, and otherwise returns false.
4352 */
4353void
4354xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4355 xmlXPathObjectPtr hay, needle;
4356
4357 CHECK_ARITY(2);
4358 CAST_TO_STRING;
4359 CHECK_TYPE(XPATH_STRING);
4360 needle = valuePop(ctxt);
4361 CAST_TO_STRING;
4362 hay = valuePop(ctxt);
4363 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4364 xmlXPathFreeObject(hay);
4365 xmlXPathFreeObject(needle);
4366 XP_ERROR(XPATH_INVALID_TYPE);
4367 }
4368 if (xmlStrstr(hay->stringval, needle->stringval))
4369 valuePush(ctxt, xmlXPathNewBoolean(1));
4370 else
4371 valuePush(ctxt, xmlXPathNewBoolean(0));
4372 xmlXPathFreeObject(hay);
4373 xmlXPathFreeObject(needle);
4374}
4375
4376/**
4377 * xmlXPathStartsWithFunction:
4378 * @ctxt: the XPath Parser context
4379 * @nargs: the number of arguments
4380 *
4381 * Implement the starts-with() XPath function
4382 * boolean starts-with(string, string)
4383 * The starts-with function returns true if the first argument string
4384 * starts with the second argument string, and otherwise returns false.
4385 */
4386void
4387xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4388 xmlXPathObjectPtr hay, needle;
4389 int n;
4390
4391 CHECK_ARITY(2);
4392 CAST_TO_STRING;
4393 CHECK_TYPE(XPATH_STRING);
4394 needle = valuePop(ctxt);
4395 CAST_TO_STRING;
4396 hay = valuePop(ctxt);
4397 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4398 xmlXPathFreeObject(hay);
4399 xmlXPathFreeObject(needle);
4400 XP_ERROR(XPATH_INVALID_TYPE);
4401 }
4402 n = xmlStrlen(needle->stringval);
4403 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4404 valuePush(ctxt, xmlXPathNewBoolean(0));
4405 else
4406 valuePush(ctxt, xmlXPathNewBoolean(1));
4407 xmlXPathFreeObject(hay);
4408 xmlXPathFreeObject(needle);
4409}
4410
4411/**
4412 * xmlXPathSubstringFunction:
4413 * @ctxt: the XPath Parser context
4414 * @nargs: the number of arguments
4415 *
4416 * Implement the substring() XPath function
4417 * string substring(string, number, number?)
4418 * The substring function returns the substring of the first argument
4419 * starting at the position specified in the second argument with
4420 * length specified in the third argument. For example,
4421 * substring("12345",2,3) returns "234". If the third argument is not
4422 * specified, it returns the substring starting at the position specified
4423 * in the second argument and continuing to the end of the string. For
4424 * example, substring("12345",2) returns "2345". More precisely, each
4425 * character in the string (see [3.6 Strings]) is considered to have a
4426 * numeric position: the position of the first character is 1, the position
4427 * of the second character is 2 and so on. The returned substring contains
4428 * those characters for which the position of the character is greater than
4429 * or equal to the second argument and, if the third argument is specified,
4430 * less than the sum of the second and third arguments; the comparisons
4431 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4432 * - substring("12345", 1.5, 2.6) returns "234"
4433 * - substring("12345", 0, 3) returns "12"
4434 * - substring("12345", 0 div 0, 3) returns ""
4435 * - substring("12345", 1, 0 div 0) returns ""
4436 * - substring("12345", -42, 1 div 0) returns "12345"
4437 * - substring("12345", -1 div 0, 1 div 0) returns ""
4438 */
4439void
4440xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4441 xmlXPathObjectPtr str, start, len;
4442 double le, in;
4443 int i, l;
4444 xmlChar *ret;
4445
4446 /*
4447 * Conformance needs to be checked !!!!!
4448 */
4449 if (nargs < 2) {
4450 CHECK_ARITY(2);
4451 }
4452 if (nargs > 3) {
4453 CHECK_ARITY(3);
4454 }
4455 if (nargs == 3) {
4456 CAST_TO_NUMBER;
4457 CHECK_TYPE(XPATH_NUMBER);
4458 len = valuePop(ctxt);
4459 le = len->floatval;
4460 xmlXPathFreeObject(len);
4461 } else {
4462 le = 2000000000;
4463 }
4464 CAST_TO_NUMBER;
4465 CHECK_TYPE(XPATH_NUMBER);
4466 start = valuePop(ctxt);
4467 in = start->floatval;
4468 xmlXPathFreeObject(start);
4469 CAST_TO_STRING;
4470 CHECK_TYPE(XPATH_STRING);
4471 str = valuePop(ctxt);
4472 le += in;
4473
4474 /* integer index of the first char */
4475 i = (int) in;
4476 if (((double)i) != in) i++;
4477
4478 /* integer index of the last char */
4479 l = (int) le;
4480 if (((double)l) != le) l++;
4481
4482 /* back to a zero based len */
4483 i--;
4484 l--;
4485
4486 /* check against the string len */
4487 if (l > 1024) {
4488 l = xmlStrlen(str->stringval);
4489 }
4490 if (i < 0) {
4491 i = 0;
4492 }
4493
4494 /* number of chars to copy */
4495 l -= i;
4496
4497 ret = xmlStrsub(str->stringval, i, l);
4498 if (ret == NULL)
4499 valuePush(ctxt, xmlXPathNewCString(""));
4500 else {
4501 valuePush(ctxt, xmlXPathNewString(ret));
4502 xmlFree(ret);
4503 }
4504 xmlXPathFreeObject(str);
4505}
4506
4507/**
4508 * xmlXPathSubstringBeforeFunction:
4509 * @ctxt: the XPath Parser context
4510 * @nargs: the number of arguments
4511 *
4512 * Implement the substring-before() XPath function
4513 * string substring-before(string, string)
4514 * The substring-before function returns the substring of the first
4515 * argument string that precedes the first occurrence of the second
4516 * argument string in the first argument string, or the empty string
4517 * if the first argument string does not contain the second argument
4518 * string. For example, substring-before("1999/04/01","/") returns 1999.
4519 */
4520void
4521xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4522 xmlXPathObjectPtr str;
4523 xmlXPathObjectPtr find;
4524 xmlBufferPtr target;
4525 const xmlChar *point;
4526 int offset;
4527
4528 CHECK_ARITY(2);
4529 CAST_TO_STRING;
4530 find = valuePop(ctxt);
4531 CAST_TO_STRING;
4532 str = valuePop(ctxt);
4533
4534 target = xmlBufferCreate();
4535 if (target) {
4536 point = xmlStrstr(str->stringval, find->stringval);
4537 if (point) {
4538 offset = (int)(point - str->stringval);
4539 xmlBufferAdd(target, str->stringval, offset);
4540 }
4541 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4542 xmlBufferFree(target);
4543 }
4544
4545 xmlXPathFreeObject(str);
4546 xmlXPathFreeObject(find);
4547}
4548
4549/**
4550 * xmlXPathSubstringAfterFunction:
4551 * @ctxt: the XPath Parser context
4552 * @nargs: the number of arguments
4553 *
4554 * Implement the substring-after() XPath function
4555 * string substring-after(string, string)
4556 * The substring-after function returns the substring of the first
4557 * argument string that follows the first occurrence of the second
4558 * argument string in the first argument string, or the empty stringi
4559 * if the first argument string does not contain the second argument
4560 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4561 * and substring-after("1999/04/01","19") returns 99/04/01.
4562 */
4563void
4564xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4565 xmlXPathObjectPtr str;
4566 xmlXPathObjectPtr find;
4567 xmlBufferPtr target;
4568 const xmlChar *point;
4569 int offset;
4570
4571 CHECK_ARITY(2);
4572 CAST_TO_STRING;
4573 find = valuePop(ctxt);
4574 CAST_TO_STRING;
4575 str = valuePop(ctxt);
4576
4577 target = xmlBufferCreate();
4578 if (target) {
4579 point = xmlStrstr(str->stringval, find->stringval);
4580 if (point) {
4581 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4582 xmlBufferAdd(target, &str->stringval[offset],
4583 xmlStrlen(str->stringval) - offset);
4584 }
4585 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4586 xmlBufferFree(target);
4587 }
4588
4589 xmlXPathFreeObject(str);
4590 xmlXPathFreeObject(find);
4591}
4592
4593/**
4594 * xmlXPathNormalizeFunction:
4595 * @ctxt: the XPath Parser context
4596 * @nargs: the number of arguments
4597 *
4598 * Implement the normalize-space() XPath function
4599 * string normalize-space(string?)
4600 * The normalize-space function returns the argument string with white
4601 * space normalized by stripping leading and trailing whitespace
4602 * and replacing sequences of whitespace characters by a single
4603 * space. Whitespace characters are the same allowed by the S production
4604 * in XML. If the argument is omitted, it defaults to the context
4605 * node converted to a string, in other words the value of the context node.
4606 */
4607void
4608xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4609 xmlXPathObjectPtr obj = NULL;
4610 xmlChar *source = NULL;
4611 xmlBufferPtr target;
4612 xmlChar blank;
4613
4614 if (nargs == 0) {
4615 /* Use current context node */
4616 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4617 xmlXPathStringFunction(ctxt, 1);
4618 nargs = 1;
4619 }
4620
4621 CHECK_ARITY(1);
4622 CAST_TO_STRING;
4623 CHECK_TYPE(XPATH_STRING);
4624 obj = valuePop(ctxt);
4625 source = obj->stringval;
4626
4627 target = xmlBufferCreate();
4628 if (target && source) {
4629
4630 /* Skip leading whitespaces */
4631 while (IS_BLANK(*source))
4632 source++;
4633
4634 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4635 blank = 0;
4636 while (*source) {
4637 if (IS_BLANK(*source)) {
4638 blank = *source;
4639 } else {
4640 if (blank) {
4641 xmlBufferAdd(target, &blank, 1);
4642 blank = 0;
4643 }
4644 xmlBufferAdd(target, source, 1);
4645 }
4646 source++;
4647 }
4648
4649 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4650 xmlBufferFree(target);
4651 }
4652 xmlXPathFreeObject(obj);
4653}
4654
4655/**
4656 * xmlXPathTranslateFunction:
4657 * @ctxt: the XPath Parser context
4658 * @nargs: the number of arguments
4659 *
4660 * Implement the translate() XPath function
4661 * string translate(string, string, string)
4662 * The translate function returns the first argument string with
4663 * occurrences of characters in the second argument string replaced
4664 * by the character at the corresponding position in the third argument
4665 * string. For example, translate("bar","abc","ABC") returns the string
4666 * BAr. If there is a character in the second argument string with no
4667 * character at a corresponding position in the third argument string
4668 * (because the second argument string is longer than the third argument
4669 * string), then occurrences of that character in the first argument
4670 * string are removed. For example, translate("--aaa--","abc-","ABC")
4671 * returns "AAA". If a character occurs more than once in second
4672 * argument string, then the first occurrence determines the replacement
4673 * character. If the third argument string is longer than the second
4674 * argument string, then excess characters are ignored.
4675 */
4676void
4677xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4678 xmlXPathObjectPtr str;
4679 xmlXPathObjectPtr from;
4680 xmlXPathObjectPtr to;
4681 xmlBufferPtr target;
4682 int i, offset, max;
4683 xmlChar ch;
4684 const xmlChar *point;
4685
4686 CHECK_ARITY(3);
4687
4688 CAST_TO_STRING;
4689 to = valuePop(ctxt);
4690 CAST_TO_STRING;
4691 from = valuePop(ctxt);
4692 CAST_TO_STRING;
4693 str = valuePop(ctxt);
4694
4695 target = xmlBufferCreate();
4696 if (target) {
4697 max = xmlStrlen(to->stringval);
4698 for (i = 0; (ch = str->stringval[i]); i++) {
4699 point = xmlStrchr(from->stringval, ch);
4700 if (point) {
4701 /* Warning: This may not work with UTF-8 */
4702 offset = (int)(point - from->stringval);
4703 if (offset < max)
4704 xmlBufferAdd(target, &to->stringval[offset], 1);
4705 } else
4706 xmlBufferAdd(target, &ch, 1);
4707 }
4708 }
4709 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4710 xmlBufferFree(target);
4711 xmlXPathFreeObject(str);
4712 xmlXPathFreeObject(from);
4713 xmlXPathFreeObject(to);
4714}
4715
4716/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004717 * xmlXPathConvertBoolean:
4718 * @val: an XPath object
4719 *
4720 * Converts an existing object to its boolean() equivalent
4721 *
4722 * Returns the new object, the old one is freed (or the operation
4723 * is done directly on @val)
4724 */
4725xmlXPathObjectPtr
4726xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
4727 int res = 0;
4728
4729 if (val == NULL)
4730 return(NULL);
4731 switch (val->type) {
4732 case XPATH_NODESET:
4733 case XPATH_XSLT_TREE:
4734 if ((val->nodesetval == NULL) ||
4735 (val->nodesetval->nodeNr == 0)) res = 0;
4736 else
4737 res = 1;
4738 break;
4739 case XPATH_STRING:
4740 if ((val->stringval == NULL) ||
4741 (val->stringval[0] == 0)) res = 0;
4742 else
4743 res = 1;
4744 break;
4745 case XPATH_BOOLEAN:
4746 return(val);
4747 case XPATH_NUMBER:
4748 if (val->floatval) res = 1;
4749 break;
4750 default:
4751 STRANGE
4752 }
4753 xmlXPathFreeObject(val);
4754 return(xmlXPathNewBoolean(res));
4755}
4756
4757/**
Owen Taylor3473f882001-02-23 17:55:21 +00004758 * xmlXPathBooleanFunction:
4759 * @ctxt: the XPath Parser context
4760 * @nargs: the number of arguments
4761 *
4762 * Implement the boolean() XPath function
4763 * boolean boolean(object)
4764 * he boolean function converts its argument to a boolean as follows:
4765 * - a number is true if and only if it is neither positive or
4766 * negative zero nor NaN
4767 * - a node-set is true if and only if it is non-empty
4768 * - a string is true if and only if its length is non-zero
4769 */
4770void
4771xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4772 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00004773
4774 CHECK_ARITY(1);
4775 cur = valuePop(ctxt);
4776 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004777 cur = xmlXPathConvertBoolean(cur);
4778 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004779}
4780
4781/**
4782 * xmlXPathNotFunction:
4783 * @ctxt: the XPath Parser context
4784 * @nargs: the number of arguments
4785 *
4786 * Implement the not() XPath function
4787 * boolean not(boolean)
4788 * The not function returns true if its argument is false,
4789 * and false otherwise.
4790 */
4791void
4792xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4793 CHECK_ARITY(1);
4794 CAST_TO_BOOLEAN;
4795 CHECK_TYPE(XPATH_BOOLEAN);
4796 ctxt->value->boolval = ! ctxt->value->boolval;
4797}
4798
4799/**
4800 * xmlXPathTrueFunction:
4801 * @ctxt: the XPath Parser context
4802 * @nargs: the number of arguments
4803 *
4804 * Implement the true() XPath function
4805 * boolean true()
4806 */
4807void
4808xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4809 CHECK_ARITY(0);
4810 valuePush(ctxt, xmlXPathNewBoolean(1));
4811}
4812
4813/**
4814 * xmlXPathFalseFunction:
4815 * @ctxt: the XPath Parser context
4816 * @nargs: the number of arguments
4817 *
4818 * Implement the false() XPath function
4819 * boolean false()
4820 */
4821void
4822xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4823 CHECK_ARITY(0);
4824 valuePush(ctxt, xmlXPathNewBoolean(0));
4825}
4826
4827/**
4828 * xmlXPathLangFunction:
4829 * @ctxt: the XPath Parser context
4830 * @nargs: the number of arguments
4831 *
4832 * Implement the lang() XPath function
4833 * boolean lang(string)
4834 * The lang function returns true or false depending on whether the
4835 * language of the context node as specified by xml:lang attributes
4836 * is the same as or is a sublanguage of the language specified by
4837 * the argument string. The language of the context node is determined
4838 * by the value of the xml:lang attribute on the context node, or, if
4839 * the context node has no xml:lang attribute, by the value of the
4840 * xml:lang attribute on the nearest ancestor of the context node that
4841 * has an xml:lang attribute. If there is no such attribute, then lang
4842 * returns false. If there is such an attribute, then lang returns
4843 * true if the attribute value is equal to the argument ignoring case,
4844 * or if there is some suffix starting with - such that the attribute
4845 * value is equal to the argument ignoring that suffix of the attribute
4846 * value and ignoring case.
4847 */
4848void
4849xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4850 xmlXPathObjectPtr val;
4851 const xmlChar *theLang;
4852 const xmlChar *lang;
4853 int ret = 0;
4854 int i;
4855
4856 CHECK_ARITY(1);
4857 CAST_TO_STRING;
4858 CHECK_TYPE(XPATH_STRING);
4859 val = valuePop(ctxt);
4860 lang = val->stringval;
4861 theLang = xmlNodeGetLang(ctxt->context->node);
4862 if ((theLang != NULL) && (lang != NULL)) {
4863 for (i = 0;lang[i] != 0;i++)
4864 if (toupper(lang[i]) != toupper(theLang[i]))
4865 goto not_equal;
4866 ret = 1;
4867 }
4868not_equal:
4869 xmlXPathFreeObject(val);
4870 valuePush(ctxt, xmlXPathNewBoolean(ret));
4871}
4872
4873/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004874 * xmlXPathConvertNumber:
4875 * @val: an XPath object
4876 *
4877 * Converts an existing object to its number() equivalent
4878 *
4879 * Returns the new object, the old one is freed (or the operation
4880 * is done directly on @val)
4881 */
4882xmlXPathObjectPtr
4883xmlXPathConvertNumber(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004884 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004885 double res;
4886
4887 if (val == NULL)
4888 return(xmlXPathNewFloat(0.0));
4889 switch (val->type) {
4890 case XPATH_UNDEFINED:
4891#ifdef DEBUG_EXPR
4892 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
4893#endif
4894 ret = xmlXPathNewFloat(0.0);
4895 break;
4896 case XPATH_XSLT_TREE:
4897 case XPATH_NODESET:
4898 val = xmlXPathConvertString(val);
4899 /* no break on purpose */
4900 case XPATH_STRING:
4901 res = xmlXPathStringEvalNumber(val->stringval);
4902 ret = xmlXPathNewFloat(res);
4903 break;
4904 case XPATH_BOOLEAN:
4905 if (val->boolval) ret = xmlXPathNewFloat(1.0);
4906 else ret = xmlXPathNewFloat(0.0);
4907 break;
4908 case XPATH_NUMBER:
4909 return(val);
4910 case XPATH_USERS:
4911 case XPATH_POINT:
4912 case XPATH_RANGE:
4913 case XPATH_LOCATIONSET:
4914 TODO
4915 ret = xmlXPathNewFloat(0.0);
4916 break;
4917 }
4918 xmlXPathFreeObject(val);
4919 return(ret);
4920}
4921
4922/**
Owen Taylor3473f882001-02-23 17:55:21 +00004923 * xmlXPathNumberFunction:
4924 * @ctxt: the XPath Parser context
4925 * @nargs: the number of arguments
4926 *
4927 * Implement the number() XPath function
4928 * number number(object?)
4929 */
4930void
4931xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4932 xmlXPathObjectPtr cur;
4933 double res;
4934
4935 if (nargs == 0) {
4936 if (ctxt->context->node == NULL) {
4937 valuePush(ctxt, xmlXPathNewFloat(0.0));
4938 } else {
4939 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
4940
4941 res = xmlXPathStringEvalNumber(content);
4942 valuePush(ctxt, xmlXPathNewFloat(res));
4943 xmlFree(content);
4944 }
4945 return;
4946 }
4947
4948 CHECK_ARITY(1);
4949 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004950 cur = xmlXPathConvertNumber(cur);
4951 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004952}
4953
4954/**
4955 * xmlXPathSumFunction:
4956 * @ctxt: the XPath Parser context
4957 * @nargs: the number of arguments
4958 *
4959 * Implement the sum() XPath function
4960 * number sum(node-set)
4961 * The sum function returns the sum of the values of the nodes in
4962 * the argument node-set.
4963 */
4964void
4965xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4966 xmlXPathObjectPtr cur;
4967 int i;
4968
4969 CHECK_ARITY(1);
4970 if ((ctxt->value == NULL) ||
4971 ((ctxt->value->type != XPATH_NODESET) &&
4972 (ctxt->value->type != XPATH_XSLT_TREE)))
4973 XP_ERROR(XPATH_INVALID_TYPE);
4974 cur = valuePop(ctxt);
4975
Daniel Veillardd8df6c02001-04-05 16:54:14 +00004976 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004977 valuePush(ctxt, xmlXPathNewFloat(0.0));
4978 } else {
4979 valuePush(ctxt,
4980 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
4981 xmlXPathNumberFunction(ctxt, 1);
4982 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
4983 valuePush(ctxt,
4984 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4985 xmlXPathAddValues(ctxt);
4986 }
4987 }
4988 xmlXPathFreeObject(cur);
4989}
4990
4991/**
4992 * xmlXPathFloorFunction:
4993 * @ctxt: the XPath Parser context
4994 * @nargs: the number of arguments
4995 *
4996 * Implement the floor() XPath function
4997 * number floor(number)
4998 * The floor function returns the largest (closest to positive infinity)
4999 * number that is not greater than the argument and that is an integer.
5000 */
5001void
5002xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5003 CHECK_ARITY(1);
5004 CAST_TO_NUMBER;
5005 CHECK_TYPE(XPATH_NUMBER);
5006#if 0
5007 ctxt->value->floatval = floor(ctxt->value->floatval);
5008#else
5009 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
5010 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
5011#endif
5012}
5013
5014/**
5015 * xmlXPathCeilingFunction:
5016 * @ctxt: the XPath Parser context
5017 * @nargs: the number of arguments
5018 *
5019 * Implement the ceiling() XPath function
5020 * number ceiling(number)
5021 * The ceiling function returns the smallest (closest to negative infinity)
5022 * number that is not less than the argument and that is an integer.
5023 */
5024void
5025xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5026 double f;
5027
5028 CHECK_ARITY(1);
5029 CAST_TO_NUMBER;
5030 CHECK_TYPE(XPATH_NUMBER);
5031
5032#if 0
5033 ctxt->value->floatval = ceil(ctxt->value->floatval);
5034#else
5035 f = (double)((int) ctxt->value->floatval);
5036 if (f != ctxt->value->floatval)
5037 ctxt->value->floatval = f + 1;
5038#endif
5039}
5040
5041/**
5042 * xmlXPathRoundFunction:
5043 * @ctxt: the XPath Parser context
5044 * @nargs: the number of arguments
5045 *
5046 * Implement the round() XPath function
5047 * number round(number)
5048 * The round function returns the number that is closest to the
5049 * argument and that is an integer. If there are two such numbers,
5050 * then the one that is even is returned.
5051 */
5052void
5053xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5054 double f;
5055
5056 CHECK_ARITY(1);
5057 CAST_TO_NUMBER;
5058 CHECK_TYPE(XPATH_NUMBER);
5059
5060 if ((ctxt->value->floatval == xmlXPathNAN) ||
5061 (ctxt->value->floatval == xmlXPathPINF) ||
5062 (ctxt->value->floatval == xmlXPathNINF) ||
5063 (ctxt->value->floatval == 0.0))
5064 return;
5065
5066#if 0
5067 f = floor(ctxt->value->floatval);
5068#else
5069 f = (double)((int) ctxt->value->floatval);
5070#endif
5071 if (ctxt->value->floatval < f + 0.5)
5072 ctxt->value->floatval = f;
5073 else
5074 ctxt->value->floatval = f + 1;
5075}
5076
5077/************************************************************************
5078 * *
5079 * The Parser *
5080 * *
5081 ************************************************************************/
5082
5083/*
5084 * a couple of forward declarations since we use a recursive call based
5085 * implementation.
5086 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005087static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005088static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005089static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005090#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005091static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
5092#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00005093#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005094static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005095#endif
5096
5097/**
5098 * xmlXPathParseNCName:
5099 * @ctxt: the XPath Parser context
5100 *
5101 * parse an XML namespace non qualified name.
5102 *
5103 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
5104 *
5105 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
5106 * CombiningChar | Extender
5107 *
5108 * Returns the namespace name or NULL
5109 */
5110
5111xmlChar *
5112xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
5113 const xmlChar *q;
5114 xmlChar *ret = NULL;
5115
5116 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5117 q = NEXT;
5118
5119 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5120 (CUR == '.') || (CUR == '-') ||
5121 (CUR == '_') ||
5122 (IS_COMBINING(CUR)) ||
5123 (IS_EXTENDER(CUR)))
5124 NEXT;
5125
5126 ret = xmlStrndup(q, CUR_PTR - q);
5127
5128 return(ret);
5129}
5130
5131/**
5132 * xmlXPathParseQName:
5133 * @ctxt: the XPath Parser context
5134 * @prefix: a xmlChar **
5135 *
5136 * parse an XML qualified name
5137 *
5138 * [NS 5] QName ::= (Prefix ':')? LocalPart
5139 *
5140 * [NS 6] Prefix ::= NCName
5141 *
5142 * [NS 7] LocalPart ::= NCName
5143 *
5144 * Returns the function returns the local part, and prefix is updated
5145 * to get the Prefix if any.
5146 */
5147
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005148static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005149xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
5150 xmlChar *ret = NULL;
5151
5152 *prefix = NULL;
5153 ret = xmlXPathParseNCName(ctxt);
5154 if (CUR == ':') {
5155 *prefix = ret;
5156 NEXT;
5157 ret = xmlXPathParseNCName(ctxt);
5158 }
5159 return(ret);
5160}
5161
5162/**
5163 * xmlXPathParseName:
5164 * @ctxt: the XPath Parser context
5165 *
5166 * parse an XML name
5167 *
5168 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5169 * CombiningChar | Extender
5170 *
5171 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5172 *
5173 * Returns the namespace name or NULL
5174 */
5175
5176xmlChar *
5177xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
5178 const xmlChar *q;
5179 xmlChar *ret = NULL;
5180
5181 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5182 q = NEXT;
5183
5184 /* TODO Make this UTF8 compliant !!! */
5185 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5186 (CUR == '.') || (CUR == '-') ||
5187 (CUR == '_') || (CUR == ':') ||
5188 (IS_COMBINING(CUR)) ||
5189 (IS_EXTENDER(CUR)))
5190 NEXT;
5191
5192 ret = xmlStrndup(q, CUR_PTR - q);
5193
5194 return(ret);
5195}
5196
5197/**
5198 * xmlXPathStringEvalNumber:
5199 * @str: A string to scan
5200 *
5201 * [30] Number ::= Digits ('.' Digits?)?
5202 * | '.' Digits
5203 * [31] Digits ::= [0-9]+
5204 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005205 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00005206 * In complement of the Number expression, this function also handles
5207 * negative values : '-' Number.
5208 *
5209 * Returns the double value.
5210 */
5211double
5212xmlXPathStringEvalNumber(const xmlChar *str) {
5213 const xmlChar *cur = str;
5214 double ret = 0.0;
5215 double mult = 1;
5216 int ok = 0;
5217 int isneg = 0;
5218
5219 while (IS_BLANK(*cur)) cur++;
5220 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5221 return(xmlXPathNAN);
5222 }
5223 if (*cur == '-') {
5224 isneg = 1;
5225 cur++;
5226 }
5227 while ((*cur >= '0') && (*cur <= '9')) {
5228 ret = ret * 10 + (*cur - '0');
5229 ok = 1;
5230 cur++;
5231 }
5232 if (*cur == '.') {
5233 cur++;
5234 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5235 return(xmlXPathNAN);
5236 }
5237 while ((*cur >= '0') && (*cur <= '9')) {
5238 mult /= 10;
5239 ret = ret + (*cur - '0') * mult;
5240 cur++;
5241 }
5242 }
5243 while (IS_BLANK(*cur)) cur++;
5244 if (*cur != 0) return(xmlXPathNAN);
5245 if (isneg) ret = -ret;
5246 return(ret);
5247}
5248
5249/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005250 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00005251 * @ctxt: the XPath Parser context
5252 *
5253 * [30] Number ::= Digits ('.' Digits?)?
5254 * | '.' Digits
5255 * [31] Digits ::= [0-9]+
5256 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005257 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00005258 *
5259 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005260static void
5261xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005262 double ret = 0.0;
5263 double mult = 1;
5264 int ok = 0;
5265
5266 CHECK_ERROR;
5267 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5268 XP_ERROR(XPATH_NUMBER_ERROR);
5269 }
5270 while ((CUR >= '0') && (CUR <= '9')) {
5271 ret = ret * 10 + (CUR - '0');
5272 ok = 1;
5273 NEXT;
5274 }
5275 if (CUR == '.') {
5276 NEXT;
5277 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5278 XP_ERROR(XPATH_NUMBER_ERROR);
5279 }
5280 while ((CUR >= '0') && (CUR <= '9')) {
5281 mult /= 10;
5282 ret = ret + (CUR - '0') * mult;
5283 NEXT;
5284 }
5285 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005286 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
5287 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005288}
5289
5290/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005291 * xmlXPathParseLiteral:
5292 * @ctxt: the XPath Parser context
5293 *
5294 * Parse a Literal
5295 *
5296 * [29] Literal ::= '"' [^"]* '"'
5297 * | "'" [^']* "'"
5298 *
5299 * Returns the value found or NULL in case of error
5300 */
5301static xmlChar *
5302xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
5303 const xmlChar *q;
5304 xmlChar *ret = NULL;
5305
5306 if (CUR == '"') {
5307 NEXT;
5308 q = CUR_PTR;
5309 while ((IS_CHAR(CUR)) && (CUR != '"'))
5310 NEXT;
5311 if (!IS_CHAR(CUR)) {
5312 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5313 } else {
5314 ret = xmlStrndup(q, CUR_PTR - q);
5315 NEXT;
5316 }
5317 } else if (CUR == '\'') {
5318 NEXT;
5319 q = CUR_PTR;
5320 while ((IS_CHAR(CUR)) && (CUR != '\''))
5321 NEXT;
5322 if (!IS_CHAR(CUR)) {
5323 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5324 } else {
5325 ret = xmlStrndup(q, CUR_PTR - q);
5326 NEXT;
5327 }
5328 } else {
5329 XP_ERROR0(XPATH_START_LITERAL_ERROR);
5330 }
5331 return(ret);
5332}
5333
5334/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005335 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00005336 * @ctxt: the XPath Parser context
5337 *
5338 * Parse a Literal and push it on the stack.
5339 *
5340 * [29] Literal ::= '"' [^"]* '"'
5341 * | "'" [^']* "'"
5342 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005343 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00005344 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005345static void
5346xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005347 const xmlChar *q;
5348 xmlChar *ret = NULL;
5349
5350 if (CUR == '"') {
5351 NEXT;
5352 q = CUR_PTR;
5353 while ((IS_CHAR(CUR)) && (CUR != '"'))
5354 NEXT;
5355 if (!IS_CHAR(CUR)) {
5356 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5357 } else {
5358 ret = xmlStrndup(q, CUR_PTR - q);
5359 NEXT;
5360 }
5361 } else if (CUR == '\'') {
5362 NEXT;
5363 q = CUR_PTR;
5364 while ((IS_CHAR(CUR)) && (CUR != '\''))
5365 NEXT;
5366 if (!IS_CHAR(CUR)) {
5367 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5368 } else {
5369 ret = xmlStrndup(q, CUR_PTR - q);
5370 NEXT;
5371 }
5372 } else {
5373 XP_ERROR(XPATH_START_LITERAL_ERROR);
5374 }
5375 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005376 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
5377 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005378 xmlFree(ret);
5379}
5380
5381/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005382 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00005383 * @ctxt: the XPath Parser context
5384 *
5385 * Parse a VariableReference, evaluate it and push it on the stack.
5386 *
5387 * The variable bindings consist of a mapping from variable names
5388 * to variable values. The value of a variable is an object, which
5389 * of any of the types that are possible for the value of an expression,
5390 * and may also be of additional types not specified here.
5391 *
5392 * Early evaluation is possible since:
5393 * The variable bindings [...] used to evaluate a subexpression are
5394 * always the same as those used to evaluate the containing expression.
5395 *
5396 * [36] VariableReference ::= '$' QName
5397 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005398static void
5399xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005400 xmlChar *name;
5401 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005402
5403 SKIP_BLANKS;
5404 if (CUR != '$') {
5405 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5406 }
5407 NEXT;
5408 name = xmlXPathParseQName(ctxt, &prefix);
5409 if (name == NULL) {
5410 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5411 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005412 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005413 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
5414 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005415 SKIP_BLANKS;
5416}
5417
5418/**
5419 * xmlXPathIsNodeType:
5420 * @ctxt: the XPath Parser context
5421 * @name: a name string
5422 *
5423 * Is the name given a NodeType one.
5424 *
5425 * [38] NodeType ::= 'comment'
5426 * | 'text'
5427 * | 'processing-instruction'
5428 * | 'node'
5429 *
5430 * Returns 1 if true 0 otherwise
5431 */
5432int
5433xmlXPathIsNodeType(const xmlChar *name) {
5434 if (name == NULL)
5435 return(0);
5436
5437 if (xmlStrEqual(name, BAD_CAST "comment"))
5438 return(1);
5439 if (xmlStrEqual(name, BAD_CAST "text"))
5440 return(1);
5441 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5442 return(1);
5443 if (xmlStrEqual(name, BAD_CAST "node"))
5444 return(1);
5445 return(0);
5446}
5447
5448/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005449 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00005450 * @ctxt: the XPath Parser context
5451 *
5452 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5453 * [17] Argument ::= Expr
5454 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005455 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00005456 * pushed on the stack
5457 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005458static void
5459xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005460 xmlChar *name;
5461 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005462 int nbargs = 0;
5463
5464 name = xmlXPathParseQName(ctxt, &prefix);
5465 if (name == NULL) {
5466 XP_ERROR(XPATH_EXPR_ERROR);
5467 }
5468 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005469#ifdef DEBUG_EXPR
5470 if (prefix == NULL)
5471 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5472 name);
5473 else
5474 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5475 prefix, name);
5476#endif
5477
Owen Taylor3473f882001-02-23 17:55:21 +00005478 if (CUR != '(') {
5479 XP_ERROR(XPATH_EXPR_ERROR);
5480 }
5481 NEXT;
5482 SKIP_BLANKS;
5483
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005484 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005485 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005486 int op1 = ctxt->comp->last;
5487 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005488 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005489 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005490 nbargs++;
5491 if (CUR == ')') break;
5492 if (CUR != ',') {
5493 XP_ERROR(XPATH_EXPR_ERROR);
5494 }
5495 NEXT;
5496 SKIP_BLANKS;
5497 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005498 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
5499 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005500 NEXT;
5501 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005502}
5503
5504/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005505 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005506 * @ctxt: the XPath Parser context
5507 *
5508 * [15] PrimaryExpr ::= VariableReference
5509 * | '(' Expr ')'
5510 * | Literal
5511 * | Number
5512 * | FunctionCall
5513 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005514 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005515 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005516static void
5517xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005518 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005519 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005520 else if (CUR == '(') {
5521 NEXT;
5522 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005523 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005524 if (CUR != ')') {
5525 XP_ERROR(XPATH_EXPR_ERROR);
5526 }
5527 NEXT;
5528 SKIP_BLANKS;
5529 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005530 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005531 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005532 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005533 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005534 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005535 }
5536 SKIP_BLANKS;
5537}
5538
5539/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005540 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005541 * @ctxt: the XPath Parser context
5542 *
5543 * [20] FilterExpr ::= PrimaryExpr
5544 * | FilterExpr Predicate
5545 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005546 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005547 * Square brackets are used to filter expressions in the same way that
5548 * they are used in location paths. It is an error if the expression to
5549 * be filtered does not evaluate to a node-set. The context node list
5550 * used for evaluating the expression in square brackets is the node-set
5551 * to be filtered listed in document order.
5552 */
5553
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005554static void
5555xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
5556 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005557 CHECK_ERROR;
5558 SKIP_BLANKS;
5559
5560 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005561 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00005562 SKIP_BLANKS;
5563 }
5564
5565
5566}
5567
5568/**
5569 * xmlXPathScanName:
5570 * @ctxt: the XPath Parser context
5571 *
5572 * Trickery: parse an XML name but without consuming the input flow
5573 * Needed to avoid insanity in the parser state.
5574 *
5575 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5576 * CombiningChar | Extender
5577 *
5578 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5579 *
5580 * [6] Names ::= Name (S Name)*
5581 *
5582 * Returns the Name parsed or NULL
5583 */
5584
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005585static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005586xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5587 xmlChar buf[XML_MAX_NAMELEN];
5588 int len = 0;
5589
5590 SKIP_BLANKS;
5591 if (!IS_LETTER(CUR) && (CUR != '_') &&
5592 (CUR != ':')) {
5593 return(NULL);
5594 }
5595
5596 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5597 (NXT(len) == '.') || (NXT(len) == '-') ||
5598 (NXT(len) == '_') || (NXT(len) == ':') ||
5599 (IS_COMBINING(NXT(len))) ||
5600 (IS_EXTENDER(NXT(len)))) {
5601 buf[len] = NXT(len);
5602 len++;
5603 if (len >= XML_MAX_NAMELEN) {
5604 xmlGenericError(xmlGenericErrorContext,
5605 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5606 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5607 (NXT(len) == '.') || (NXT(len) == '-') ||
5608 (NXT(len) == '_') || (NXT(len) == ':') ||
5609 (IS_COMBINING(NXT(len))) ||
5610 (IS_EXTENDER(NXT(len))))
5611 len++;
5612 break;
5613 }
5614 }
5615 return(xmlStrndup(buf, len));
5616}
5617
5618/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005619 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005620 * @ctxt: the XPath Parser context
5621 *
5622 * [19] PathExpr ::= LocationPath
5623 * | FilterExpr
5624 * | FilterExpr '/' RelativeLocationPath
5625 * | FilterExpr '//' RelativeLocationPath
5626 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005627 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005628 * The / operator and // operators combine an arbitrary expression
5629 * and a relative location path. It is an error if the expression
5630 * does not evaluate to a node-set.
5631 * The / operator does composition in the same way as when / is
5632 * used in a location path. As in location paths, // is short for
5633 * /descendant-or-self::node()/.
5634 */
5635
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005636static void
5637xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005638 int lc = 1; /* Should we branch to LocationPath ? */
5639 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5640
5641 SKIP_BLANKS;
5642 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5643 (CUR == '\'') || (CUR == '"')) {
5644 lc = 0;
5645 } else if (CUR == '*') {
5646 /* relative or absolute location path */
5647 lc = 1;
5648 } else if (CUR == '/') {
5649 /* relative or absolute location path */
5650 lc = 1;
5651 } else if (CUR == '@') {
5652 /* relative abbreviated attribute location path */
5653 lc = 1;
5654 } else if (CUR == '.') {
5655 /* relative abbreviated attribute location path */
5656 lc = 1;
5657 } else {
5658 /*
5659 * Problem is finding if we have a name here whether it's:
5660 * - a nodetype
5661 * - a function call in which case it's followed by '('
5662 * - an axis in which case it's followed by ':'
5663 * - a element name
5664 * We do an a priori analysis here rather than having to
5665 * maintain parsed token content through the recursive function
5666 * calls. This looks uglier but makes the code quite easier to
5667 * read/write/debug.
5668 */
5669 SKIP_BLANKS;
5670 name = xmlXPathScanName(ctxt);
5671 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5672#ifdef DEBUG_STEP
5673 xmlGenericError(xmlGenericErrorContext,
5674 "PathExpr: Axis\n");
5675#endif
5676 lc = 1;
5677 xmlFree(name);
5678 } else if (name != NULL) {
5679 int len =xmlStrlen(name);
5680 int blank = 0;
5681
5682
5683 while (NXT(len) != 0) {
5684 if (NXT(len) == '/') {
5685 /* element name */
5686#ifdef DEBUG_STEP
5687 xmlGenericError(xmlGenericErrorContext,
5688 "PathExpr: AbbrRelLocation\n");
5689#endif
5690 lc = 1;
5691 break;
5692 } else if (IS_BLANK(NXT(len))) {
5693 /* skip to next */
5694 blank = 1;
5695 } else if (NXT(len) == ':') {
5696#ifdef DEBUG_STEP
5697 xmlGenericError(xmlGenericErrorContext,
5698 "PathExpr: AbbrRelLocation\n");
5699#endif
5700 lc = 1;
5701 break;
5702 } else if ((NXT(len) == '(')) {
5703 /* Note Type or Function */
5704 if (xmlXPathIsNodeType(name)) {
5705#ifdef DEBUG_STEP
5706 xmlGenericError(xmlGenericErrorContext,
5707 "PathExpr: Type search\n");
5708#endif
5709 lc = 1;
5710 } else {
5711#ifdef DEBUG_STEP
5712 xmlGenericError(xmlGenericErrorContext,
5713 "PathExpr: function call\n");
5714#endif
5715 lc = 0;
5716 }
5717 break;
5718 } else if ((NXT(len) == '[')) {
5719 /* element name */
5720#ifdef DEBUG_STEP
5721 xmlGenericError(xmlGenericErrorContext,
5722 "PathExpr: AbbrRelLocation\n");
5723#endif
5724 lc = 1;
5725 break;
5726 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
5727 (NXT(len) == '=')) {
5728 lc = 1;
5729 break;
5730 } else {
5731 lc = 1;
5732 break;
5733 }
5734 len++;
5735 }
5736 if (NXT(len) == 0) {
5737#ifdef DEBUG_STEP
5738 xmlGenericError(xmlGenericErrorContext,
5739 "PathExpr: AbbrRelLocation\n");
5740#endif
5741 /* element name */
5742 lc = 1;
5743 }
5744 xmlFree(name);
5745 } else {
5746 /* make sure all cases are covered explicitely */
5747 XP_ERROR(XPATH_EXPR_ERROR);
5748 }
5749 }
5750
5751 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005752 if (CUR == '/') {
5753 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
5754 } else {
5755 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005756 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005757 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005758 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005759 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005760 CHECK_ERROR;
5761 if ((CUR == '/') && (NXT(1) == '/')) {
5762 SKIP(2);
5763 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005764
5765 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
5766 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
5767 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
5768
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005769 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005770 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005771 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005772 }
5773 }
5774 SKIP_BLANKS;
5775}
5776
5777/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005778 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005779 * @ctxt: the XPath Parser context
5780 *
5781 * [18] UnionExpr ::= PathExpr
5782 * | UnionExpr '|' PathExpr
5783 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005784 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005785 */
5786
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005787static void
5788xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
5789 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005790 CHECK_ERROR;
5791 SKIP_BLANKS;
5792 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005793 int op1 = ctxt->comp->last;
5794 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005795
5796 NEXT;
5797 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005798 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005799
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005800 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
5801
Owen Taylor3473f882001-02-23 17:55:21 +00005802 SKIP_BLANKS;
5803 }
Owen Taylor3473f882001-02-23 17:55:21 +00005804}
5805
5806/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005807 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005808 * @ctxt: the XPath Parser context
5809 *
5810 * [27] UnaryExpr ::= UnionExpr
5811 * | '-' UnaryExpr
5812 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005813 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005814 */
5815
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005816static void
5817xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005818 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005819 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005820
5821 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00005822 while (CUR == '-') {
5823 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005824 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005825 NEXT;
5826 SKIP_BLANKS;
5827 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005828
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005829 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005830 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005831 if (found) {
5832 if (minus)
5833 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
5834 else
5835 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005836 }
5837}
5838
5839/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005840 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005841 * @ctxt: the XPath Parser context
5842 *
5843 * [26] MultiplicativeExpr ::= UnaryExpr
5844 * | MultiplicativeExpr MultiplyOperator UnaryExpr
5845 * | MultiplicativeExpr 'div' UnaryExpr
5846 * | MultiplicativeExpr 'mod' UnaryExpr
5847 * [34] MultiplyOperator ::= '*'
5848 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005849 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005850 */
5851
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005852static void
5853xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
5854 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005855 CHECK_ERROR;
5856 SKIP_BLANKS;
5857 while ((CUR == '*') ||
5858 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
5859 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
5860 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005861 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005862
5863 if (CUR == '*') {
5864 op = 0;
5865 NEXT;
5866 } else if (CUR == 'd') {
5867 op = 1;
5868 SKIP(3);
5869 } else if (CUR == 'm') {
5870 op = 2;
5871 SKIP(3);
5872 }
5873 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005874 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005875 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005876 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005877 SKIP_BLANKS;
5878 }
5879}
5880
5881/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005882 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005883 * @ctxt: the XPath Parser context
5884 *
5885 * [25] AdditiveExpr ::= MultiplicativeExpr
5886 * | AdditiveExpr '+' MultiplicativeExpr
5887 * | AdditiveExpr '-' MultiplicativeExpr
5888 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005889 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005890 */
5891
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005892static void
5893xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005894
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005895 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005896 CHECK_ERROR;
5897 SKIP_BLANKS;
5898 while ((CUR == '+') || (CUR == '-')) {
5899 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005900 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005901
5902 if (CUR == '+') plus = 1;
5903 else plus = 0;
5904 NEXT;
5905 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005906 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005907 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005908 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005909 SKIP_BLANKS;
5910 }
5911}
5912
5913/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005914 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005915 * @ctxt: the XPath Parser context
5916 *
5917 * [24] RelationalExpr ::= AdditiveExpr
5918 * | RelationalExpr '<' AdditiveExpr
5919 * | RelationalExpr '>' AdditiveExpr
5920 * | RelationalExpr '<=' AdditiveExpr
5921 * | RelationalExpr '>=' AdditiveExpr
5922 *
5923 * A <= B > C is allowed ? Answer from James, yes with
5924 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
5925 * which is basically what got implemented.
5926 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005927 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00005928 * on the stack
5929 */
5930
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005931static void
5932xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
5933 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005934 CHECK_ERROR;
5935 SKIP_BLANKS;
5936 while ((CUR == '<') ||
5937 (CUR == '>') ||
5938 ((CUR == '<') && (NXT(1) == '=')) ||
5939 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005940 int inf, strict;
5941 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005942
5943 if (CUR == '<') inf = 1;
5944 else inf = 0;
5945 if (NXT(1) == '=') strict = 0;
5946 else strict = 1;
5947 NEXT;
5948 if (!strict) NEXT;
5949 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005950 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005951 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005952 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00005953 SKIP_BLANKS;
5954 }
5955}
5956
5957/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005958 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005959 * @ctxt: the XPath Parser context
5960 *
5961 * [23] EqualityExpr ::= RelationalExpr
5962 * | EqualityExpr '=' RelationalExpr
5963 * | EqualityExpr '!=' RelationalExpr
5964 *
5965 * A != B != C is allowed ? Answer from James, yes with
5966 * (RelationalExpr = RelationalExpr) = RelationalExpr
5967 * (RelationalExpr != RelationalExpr) != RelationalExpr
5968 * which is basically what got implemented.
5969 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005970 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005971 *
5972 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005973static void
5974xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
5975 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005976 CHECK_ERROR;
5977 SKIP_BLANKS;
5978 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005979 int eq;
5980 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005981
5982 if (CUR == '=') eq = 1;
5983 else eq = 0;
5984 NEXT;
5985 if (!eq) NEXT;
5986 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005987 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005988 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005989 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005990 SKIP_BLANKS;
5991 }
5992}
5993
5994/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005995 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005996 * @ctxt: the XPath Parser context
5997 *
5998 * [22] AndExpr ::= EqualityExpr
5999 * | AndExpr 'and' EqualityExpr
6000 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006001 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006002 *
6003 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006004static void
6005xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
6006 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006007 CHECK_ERROR;
6008 SKIP_BLANKS;
6009 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006010 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006011 SKIP(3);
6012 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006013 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006014 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006015 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006016 SKIP_BLANKS;
6017 }
6018}
6019
6020/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006021 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006022 * @ctxt: the XPath Parser context
6023 *
6024 * [14] Expr ::= OrExpr
6025 * [21] OrExpr ::= AndExpr
6026 * | OrExpr 'or' AndExpr
6027 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006028 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00006029 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006030static void
6031xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
6032 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006033 CHECK_ERROR;
6034 SKIP_BLANKS;
6035 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006036 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006037 SKIP(2);
6038 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006039 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006040 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006041 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
6042 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00006043 SKIP_BLANKS;
6044 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006045 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
6046 /* more ops could be optimized too */
6047 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
6048 }
Owen Taylor3473f882001-02-23 17:55:21 +00006049}
6050
6051/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006052 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00006053 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006054 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00006055 *
6056 * [8] Predicate ::= '[' PredicateExpr ']'
6057 * [9] PredicateExpr ::= Expr
6058 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006059 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00006060 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006061static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006062xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006063 int op1 = ctxt->comp->last;
6064
6065 SKIP_BLANKS;
6066 if (CUR != '[') {
6067 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6068 }
6069 NEXT;
6070 SKIP_BLANKS;
6071
6072 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006073 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006074 CHECK_ERROR;
6075
6076 if (CUR != ']') {
6077 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6078 }
6079
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006080 if (filter)
6081 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
6082 else
6083 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006084
6085 NEXT;
6086 SKIP_BLANKS;
6087}
6088
6089/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006090 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00006091 * @ctxt: the XPath Parser context
6092 * @test: pointer to a xmlXPathTestVal
6093 * @type: pointer to a xmlXPathTypeVal
6094 * @prefix: placeholder for a possible name prefix
6095 *
6096 * [7] NodeTest ::= NameTest
6097 * | NodeType '(' ')'
6098 * | 'processing-instruction' '(' Literal ')'
6099 *
6100 * [37] NameTest ::= '*'
6101 * | NCName ':' '*'
6102 * | QName
6103 * [38] NodeType ::= 'comment'
6104 * | 'text'
6105 * | 'processing-instruction'
6106 * | 'node'
6107 *
6108 * Returns the name found and update @test, @type and @prefix appropriately
6109 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006110static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006111xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6112 xmlXPathTypeVal *type, const xmlChar **prefix,
6113 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00006114 int blanks;
6115
6116 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6117 STRANGE;
6118 return(NULL);
6119 }
6120 *type = 0;
6121 *test = 0;
6122 *prefix = NULL;
6123 SKIP_BLANKS;
6124
6125 if ((name == NULL) && (CUR == '*')) {
6126 /*
6127 * All elements
6128 */
6129 NEXT;
6130 *test = NODE_TEST_ALL;
6131 return(NULL);
6132 }
6133
6134 if (name == NULL)
6135 name = xmlXPathParseNCName(ctxt);
6136 if (name == NULL) {
6137 XP_ERROR0(XPATH_EXPR_ERROR);
6138 }
6139
6140 blanks = IS_BLANK(CUR);
6141 SKIP_BLANKS;
6142 if (CUR == '(') {
6143 NEXT;
6144 /*
6145 * NodeType or PI search
6146 */
6147 if (xmlStrEqual(name, BAD_CAST "comment"))
6148 *type = NODE_TYPE_COMMENT;
6149 else if (xmlStrEqual(name, BAD_CAST "node"))
6150 *type = NODE_TYPE_NODE;
6151 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6152 *type = NODE_TYPE_PI;
6153 else if (xmlStrEqual(name, BAD_CAST "text"))
6154 *type = NODE_TYPE_TEXT;
6155 else {
6156 if (name != NULL)
6157 xmlFree(name);
6158 XP_ERROR0(XPATH_EXPR_ERROR);
6159 }
6160
6161 *test = NODE_TEST_TYPE;
6162
6163 SKIP_BLANKS;
6164 if (*type == NODE_TYPE_PI) {
6165 /*
6166 * Specific case: search a PI by name.
6167 */
Owen Taylor3473f882001-02-23 17:55:21 +00006168 if (name != NULL)
6169 xmlFree(name);
6170
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006171 name = xmlXPathParseLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006172 CHECK_ERROR 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006173 SKIP_BLANKS;
6174 }
6175 if (CUR != ')') {
6176 if (name != NULL)
6177 xmlFree(name);
6178 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6179 }
6180 NEXT;
6181 return(name);
6182 }
6183 *test = NODE_TEST_NAME;
6184 if ((!blanks) && (CUR == ':')) {
6185 NEXT;
6186
6187 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006188 * Since currently the parser context don't have a
6189 * namespace list associated:
6190 * The namespace name for this prefix can be computed
6191 * only at evaluation time. The compilation is done
6192 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00006193 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006194#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00006195 *prefix = xmlXPathNsLookup(ctxt->context, name);
6196 if (name != NULL)
6197 xmlFree(name);
6198 if (*prefix == NULL) {
6199 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6200 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006201#else
6202 *prefix = name;
6203#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006204
6205 if (CUR == '*') {
6206 /*
6207 * All elements
6208 */
6209 NEXT;
6210 *test = NODE_TEST_ALL;
6211 return(NULL);
6212 }
6213
6214 name = xmlXPathParseNCName(ctxt);
6215 if (name == NULL) {
6216 XP_ERROR0(XPATH_EXPR_ERROR);
6217 }
6218 }
6219 return(name);
6220}
6221
6222/**
6223 * xmlXPathIsAxisName:
6224 * @name: a preparsed name token
6225 *
6226 * [6] AxisName ::= 'ancestor'
6227 * | 'ancestor-or-self'
6228 * | 'attribute'
6229 * | 'child'
6230 * | 'descendant'
6231 * | 'descendant-or-self'
6232 * | 'following'
6233 * | 'following-sibling'
6234 * | 'namespace'
6235 * | 'parent'
6236 * | 'preceding'
6237 * | 'preceding-sibling'
6238 * | 'self'
6239 *
6240 * Returns the axis or 0
6241 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006242static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00006243xmlXPathIsAxisName(const xmlChar *name) {
6244 xmlXPathAxisVal ret = 0;
6245 switch (name[0]) {
6246 case 'a':
6247 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6248 ret = AXIS_ANCESTOR;
6249 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6250 ret = AXIS_ANCESTOR_OR_SELF;
6251 if (xmlStrEqual(name, BAD_CAST "attribute"))
6252 ret = AXIS_ATTRIBUTE;
6253 break;
6254 case 'c':
6255 if (xmlStrEqual(name, BAD_CAST "child"))
6256 ret = AXIS_CHILD;
6257 break;
6258 case 'd':
6259 if (xmlStrEqual(name, BAD_CAST "descendant"))
6260 ret = AXIS_DESCENDANT;
6261 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6262 ret = AXIS_DESCENDANT_OR_SELF;
6263 break;
6264 case 'f':
6265 if (xmlStrEqual(name, BAD_CAST "following"))
6266 ret = AXIS_FOLLOWING;
6267 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6268 ret = AXIS_FOLLOWING_SIBLING;
6269 break;
6270 case 'n':
6271 if (xmlStrEqual(name, BAD_CAST "namespace"))
6272 ret = AXIS_NAMESPACE;
6273 break;
6274 case 'p':
6275 if (xmlStrEqual(name, BAD_CAST "parent"))
6276 ret = AXIS_PARENT;
6277 if (xmlStrEqual(name, BAD_CAST "preceding"))
6278 ret = AXIS_PRECEDING;
6279 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6280 ret = AXIS_PRECEDING_SIBLING;
6281 break;
6282 case 's':
6283 if (xmlStrEqual(name, BAD_CAST "self"))
6284 ret = AXIS_SELF;
6285 break;
6286 }
6287 return(ret);
6288}
6289
6290/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006291 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00006292 * @ctxt: the XPath Parser context
6293 *
6294 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6295 * | AbbreviatedStep
6296 *
6297 * [12] AbbreviatedStep ::= '.' | '..'
6298 *
6299 * [5] AxisSpecifier ::= AxisName '::'
6300 * | AbbreviatedAxisSpecifier
6301 *
6302 * [13] AbbreviatedAxisSpecifier ::= '@'?
6303 *
6304 * Modified for XPtr range support as:
6305 *
6306 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6307 * | AbbreviatedStep
6308 * | 'range-to' '(' Expr ')' Predicate*
6309 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006310 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00006311 * A location step of . is short for self::node(). This is
6312 * particularly useful in conjunction with //. For example, the
6313 * location path .//para is short for
6314 * self::node()/descendant-or-self::node()/child::para
6315 * and so will select all para descendant elements of the context
6316 * node.
6317 * Similarly, a location step of .. is short for parent::node().
6318 * For example, ../title is short for parent::node()/child::title
6319 * and so will select the title children of the parent of the context
6320 * node.
6321 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006322static void
6323xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006324 SKIP_BLANKS;
6325 if ((CUR == '.') && (NXT(1) == '.')) {
6326 SKIP(2);
6327 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006328 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
6329 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006330 } else if (CUR == '.') {
6331 NEXT;
6332 SKIP_BLANKS;
6333 } else {
6334 xmlChar *name = NULL;
6335 const xmlChar *prefix = NULL;
6336 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006337 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006338 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006339 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00006340
6341 /*
6342 * The modification needed for XPointer change to the production
6343 */
6344#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006345 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00006346 name = xmlXPathParseNCName(ctxt);
6347 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006348 int op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006349 xmlFree(name);
6350 SKIP_BLANKS;
6351 if (CUR != '(') {
6352 XP_ERROR(XPATH_EXPR_ERROR);
6353 }
6354 NEXT;
6355 SKIP_BLANKS;
6356
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006357 xmlXPathCompileExpr(ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006358 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006359 CHECK_ERROR;
6360
6361 SKIP_BLANKS;
6362 if (CUR != ')') {
6363 XP_ERROR(XPATH_EXPR_ERROR);
6364 }
6365 NEXT;
6366 goto eval_predicates;
6367 }
6368 }
6369#endif
6370 if (name == NULL)
6371 name = xmlXPathParseNCName(ctxt);
6372 if (name != NULL) {
6373 axis = xmlXPathIsAxisName(name);
6374 if (axis != 0) {
6375 SKIP_BLANKS;
6376 if ((CUR == ':') && (NXT(1) == ':')) {
6377 SKIP(2);
6378 xmlFree(name);
6379 name = NULL;
6380 } else {
6381 /* an element name can conflict with an axis one :-\ */
6382 axis = AXIS_CHILD;
6383 }
6384 } else {
6385 axis = AXIS_CHILD;
6386 }
6387 } else if (CUR == '@') {
6388 NEXT;
6389 axis = AXIS_ATTRIBUTE;
6390 } else {
6391 axis = AXIS_CHILD;
6392 }
6393
6394 CHECK_ERROR;
6395
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006396 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00006397 if (test == 0)
6398 return;
6399
6400#ifdef DEBUG_STEP
6401 xmlGenericError(xmlGenericErrorContext,
6402 "Basis : computing new set\n");
6403#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006404
Owen Taylor3473f882001-02-23 17:55:21 +00006405#ifdef DEBUG_STEP
6406 xmlGenericError(xmlGenericErrorContext, "Basis : ");
6407 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
6408#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006409
6410eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006411 op1 = ctxt->comp->last;
6412 ctxt->comp->last = -1;
6413
Owen Taylor3473f882001-02-23 17:55:21 +00006414 SKIP_BLANKS;
6415 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006416 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006417 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006418
6419 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
6420 test, type, (void *)prefix, (void *)name);
6421
Owen Taylor3473f882001-02-23 17:55:21 +00006422 }
6423#ifdef DEBUG_STEP
6424 xmlGenericError(xmlGenericErrorContext, "Step : ");
6425 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6426 ctxt->value->nodesetval);
6427#endif
6428}
6429
6430/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006431 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006432 * @ctxt: the XPath Parser context
6433 *
6434 * [3] RelativeLocationPath ::= Step
6435 * | RelativeLocationPath '/' Step
6436 * | AbbreviatedRelativeLocationPath
6437 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6438 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006439 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00006440 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006441static void
Owen Taylor3473f882001-02-23 17:55:21 +00006442#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006443xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006444#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006445xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006446#endif
6447(xmlXPathParserContextPtr ctxt) {
6448 SKIP_BLANKS;
6449 if ((CUR == '/') && (NXT(1) == '/')) {
6450 SKIP(2);
6451 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006452 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6453 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006454 } else if (CUR == '/') {
6455 NEXT;
6456 SKIP_BLANKS;
6457 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006458 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006459 SKIP_BLANKS;
6460 while (CUR == '/') {
6461 if ((CUR == '/') && (NXT(1) == '/')) {
6462 SKIP(2);
6463 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006464 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00006465 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006466 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006467 } else if (CUR == '/') {
6468 NEXT;
6469 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006470 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006471 }
6472 SKIP_BLANKS;
6473 }
6474}
6475
6476/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006477 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006478 * @ctxt: the XPath Parser context
6479 *
6480 * [1] LocationPath ::= RelativeLocationPath
6481 * | AbsoluteLocationPath
6482 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6483 * | AbbreviatedAbsoluteLocationPath
6484 * [10] AbbreviatedAbsoluteLocationPath ::=
6485 * '//' RelativeLocationPath
6486 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006487 * Compile a location path
6488 *
Owen Taylor3473f882001-02-23 17:55:21 +00006489 * // is short for /descendant-or-self::node()/. For example,
6490 * //para is short for /descendant-or-self::node()/child::para and
6491 * so will select any para element in the document (even a para element
6492 * that is a document element will be selected by //para since the
6493 * document element node is a child of the root node); div//para is
6494 * short for div/descendant-or-self::node()/child::para and so will
6495 * select all para descendants of div children.
6496 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006497static void
6498xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006499 SKIP_BLANKS;
6500 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006501 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006502 } else {
6503 while (CUR == '/') {
6504 if ((CUR == '/') && (NXT(1) == '/')) {
6505 SKIP(2);
6506 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006507 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6508 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006509 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006510 } else if (CUR == '/') {
6511 NEXT;
6512 SKIP_BLANKS;
6513 if (CUR != 0)
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006514 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006515 }
6516 }
6517 }
6518}
6519
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006520/************************************************************************
6521 * *
6522 * XPath precompiled expression evaluation *
6523 * *
6524 ************************************************************************/
6525
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006526static void
6527xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
6528
6529/**
6530 * xmlXPathNodeCollectAndTest:
6531 * @ctxt: the XPath Parser context
6532 * @op: the XPath precompiled step operation
6533 *
6534 * This is the function implementing a step: based on the current list
6535 * of nodes, it builds up a new list, looking at all nodes under that
6536 * axis and selecting them it also do the predicate filtering
6537 *
6538 * Pushes the new NodeSet resulting from the search.
6539 */
6540static void
6541xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
6542 xmlXPathStepOpPtr op) {
6543 xmlXPathAxisVal axis = op->value;
6544 xmlXPathTestVal test = op->value2;
6545 xmlXPathTypeVal type = op->value3;
6546 const xmlChar *prefix = op->value4;
6547 const xmlChar *name = op->value5;
6548
6549#ifdef DEBUG_STEP
6550 int n = 0, t = 0;
6551#endif
6552 int i;
6553 xmlNodeSetPtr ret, list;
6554 xmlXPathTraversalFunction next = NULL;
6555 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
6556 xmlNodePtr cur = NULL;
6557 xmlXPathObjectPtr obj;
6558 xmlNodeSetPtr nodelist;
6559 xmlNodePtr tmp;
6560
6561 CHECK_TYPE(XPATH_NODESET);
6562 obj = valuePop(ctxt);
6563 addNode = xmlXPathNodeSetAdd;
6564
6565#ifdef DEBUG_STEP
6566 xmlGenericError(xmlGenericErrorContext,
6567 "new step : ");
6568#endif
6569 switch (axis) {
6570 case AXIS_ANCESTOR:
6571#ifdef DEBUG_STEP
6572 xmlGenericError(xmlGenericErrorContext,
6573 "axis 'ancestors' ");
6574#endif
6575 next = xmlXPathNextAncestor; break;
6576 case AXIS_ANCESTOR_OR_SELF:
6577#ifdef DEBUG_STEP
6578 xmlGenericError(xmlGenericErrorContext,
6579 "axis 'ancestors-or-self' ");
6580#endif
6581 next = xmlXPathNextAncestorOrSelf; break;
6582 case AXIS_ATTRIBUTE:
6583#ifdef DEBUG_STEP
6584 xmlGenericError(xmlGenericErrorContext,
6585 "axis 'attributes' ");
6586#endif
6587 next = xmlXPathNextAttribute; break;
6588 break;
6589 case AXIS_CHILD:
6590#ifdef DEBUG_STEP
6591 xmlGenericError(xmlGenericErrorContext,
6592 "axis 'child' ");
6593#endif
6594 next = xmlXPathNextChild; break;
6595 case AXIS_DESCENDANT:
6596#ifdef DEBUG_STEP
6597 xmlGenericError(xmlGenericErrorContext,
6598 "axis 'descendant' ");
6599#endif
6600 next = xmlXPathNextDescendant; break;
6601 case AXIS_DESCENDANT_OR_SELF:
6602#ifdef DEBUG_STEP
6603 xmlGenericError(xmlGenericErrorContext,
6604 "axis 'descendant-or-self' ");
6605#endif
6606 next = xmlXPathNextDescendantOrSelf; break;
6607 case AXIS_FOLLOWING:
6608#ifdef DEBUG_STEP
6609 xmlGenericError(xmlGenericErrorContext,
6610 "axis 'following' ");
6611#endif
6612 next = xmlXPathNextFollowing; break;
6613 case AXIS_FOLLOWING_SIBLING:
6614#ifdef DEBUG_STEP
6615 xmlGenericError(xmlGenericErrorContext,
6616 "axis 'following-siblings' ");
6617#endif
6618 next = xmlXPathNextFollowingSibling; break;
6619 case AXIS_NAMESPACE:
6620#ifdef DEBUG_STEP
6621 xmlGenericError(xmlGenericErrorContext,
6622 "axis 'namespace' ");
6623#endif
6624 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
6625 break;
6626 case AXIS_PARENT:
6627#ifdef DEBUG_STEP
6628 xmlGenericError(xmlGenericErrorContext,
6629 "axis 'parent' ");
6630#endif
6631 next = xmlXPathNextParent; break;
6632 case AXIS_PRECEDING:
6633#ifdef DEBUG_STEP
6634 xmlGenericError(xmlGenericErrorContext,
6635 "axis 'preceding' ");
6636#endif
6637 next = xmlXPathNextPreceding; break;
6638 case AXIS_PRECEDING_SIBLING:
6639#ifdef DEBUG_STEP
6640 xmlGenericError(xmlGenericErrorContext,
6641 "axis 'preceding-sibling' ");
6642#endif
6643 next = xmlXPathNextPrecedingSibling; break;
6644 case AXIS_SELF:
6645#ifdef DEBUG_STEP
6646 xmlGenericError(xmlGenericErrorContext,
6647 "axis 'self' ");
6648#endif
6649 next = xmlXPathNextSelf; break;
6650 }
6651 if (next == NULL)
6652 return;
6653
6654 nodelist = obj->nodesetval;
6655 if (nodelist == NULL) {
6656 xmlXPathFreeObject(obj);
6657 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
6658 return;
6659 }
6660 addNode = xmlXPathNodeSetAddUnique;
6661 ret = NULL;
6662#ifdef DEBUG_STEP
6663 xmlGenericError(xmlGenericErrorContext,
6664 " context contains %d nodes\n",
6665 nodelist->nodeNr);
6666 switch (test) {
6667 case NODE_TEST_NODE:
6668 xmlGenericError(xmlGenericErrorContext,
6669 " searching all nodes\n");
6670 break;
6671 case NODE_TEST_NONE:
6672 xmlGenericError(xmlGenericErrorContext,
6673 " searching for none !!!\n");
6674 break;
6675 case NODE_TEST_TYPE:
6676 xmlGenericError(xmlGenericErrorContext,
6677 " searching for type %d\n", type);
6678 break;
6679 case NODE_TEST_PI:
6680 xmlGenericError(xmlGenericErrorContext,
6681 " searching for PI !!!\n");
6682 break;
6683 case NODE_TEST_ALL:
6684 xmlGenericError(xmlGenericErrorContext,
6685 " searching for *\n");
6686 break;
6687 case NODE_TEST_NS:
6688 xmlGenericError(xmlGenericErrorContext,
6689 " searching for namespace %s\n",
6690 prefix);
6691 break;
6692 case NODE_TEST_NAME:
6693 xmlGenericError(xmlGenericErrorContext,
6694 " searching for name %s\n", name);
6695 if (prefix != NULL)
6696 xmlGenericError(xmlGenericErrorContext,
6697 " with namespace %s\n",
6698 prefix);
6699 break;
6700 }
6701 xmlGenericError(xmlGenericErrorContext, "Testing : ");
6702#endif
6703 /*
6704 * 2.3 Node Tests
6705 * - For the attribute axis, the principal node type is attribute.
6706 * - For the namespace axis, the principal node type is namespace.
6707 * - For other axes, the principal node type is element.
6708 *
6709 * A node test * is true for any node of the
6710 * principal node type. For example, child::* willi
6711 * select all element children of the context node
6712 */
6713 tmp = ctxt->context->node;
6714 for (i = 0;i < nodelist->nodeNr; i++) {
6715 ctxt->context->node = nodelist->nodeTab[i];
6716
6717 cur = NULL;
6718 list = xmlXPathNodeSetCreate(NULL);
6719 do {
6720 cur = next(ctxt, cur);
6721 if (cur == NULL) break;
6722#ifdef DEBUG_STEP
6723 t++;
6724 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
6725#endif
6726 switch (test) {
6727 case NODE_TEST_NONE:
6728 ctxt->context->node = tmp;
6729 STRANGE
6730 return;
6731 case NODE_TEST_TYPE:
6732 if ((cur->type == type) ||
6733 ((type == NODE_TYPE_NODE) &&
6734 ((cur->type == XML_DOCUMENT_NODE) ||
6735 (cur->type == XML_HTML_DOCUMENT_NODE) ||
6736 (cur->type == XML_ELEMENT_NODE) ||
6737 (cur->type == XML_PI_NODE) ||
6738 (cur->type == XML_COMMENT_NODE) ||
6739 (cur->type == XML_CDATA_SECTION_NODE) ||
6740 (cur->type == XML_TEXT_NODE)))) {
6741#ifdef DEBUG_STEP
6742 n++;
6743#endif
6744 addNode(list, cur);
6745 }
6746 break;
6747 case NODE_TEST_PI:
6748 if (cur->type == XML_PI_NODE) {
6749 if ((name != NULL) &&
6750 (!xmlStrEqual(name, cur->name)))
6751 break;
6752#ifdef DEBUG_STEP
6753 n++;
6754#endif
6755 addNode(list, cur);
6756 }
6757 break;
6758 case NODE_TEST_ALL:
6759 if (axis == AXIS_ATTRIBUTE) {
6760 if (cur->type == XML_ATTRIBUTE_NODE) {
6761#ifdef DEBUG_STEP
6762 n++;
6763#endif
6764 addNode(list, cur);
6765 }
6766 } else if (axis == AXIS_NAMESPACE) {
6767 if (cur->type == XML_NAMESPACE_DECL) {
6768#ifdef DEBUG_STEP
6769 n++;
6770#endif
6771 addNode(list, cur);
6772 }
6773 } else {
6774 if ((cur->type == XML_ELEMENT_NODE) ||
6775 (cur->type == XML_DOCUMENT_NODE) ||
6776 (cur->type == XML_HTML_DOCUMENT_NODE)) {
6777 if (prefix == NULL) {
6778#ifdef DEBUG_STEP
6779 n++;
6780#endif
6781 addNode(list, cur);
6782 } else if ((cur->ns != NULL) &&
6783 (xmlStrEqual(prefix,
6784 cur->ns->href))) {
6785#ifdef DEBUG_STEP
6786 n++;
6787#endif
6788 addNode(list, cur);
6789 }
6790 }
6791 }
6792 break;
6793 case NODE_TEST_NS: {
6794 TODO;
6795 break;
6796 }
6797 case NODE_TEST_NAME:
6798 switch (cur->type) {
6799 case XML_ELEMENT_NODE:
6800 if (xmlStrEqual(name, cur->name)) {
6801 if (prefix == NULL) {
6802 if ((cur->ns == NULL) ||
6803 (cur->ns->prefix == NULL)) {
6804#ifdef DEBUG_STEP
6805 n++;
6806#endif
6807 addNode(list, cur);
6808 }
6809 } else {
6810 if ((cur->ns != NULL) &&
6811 (xmlStrEqual(prefix,
6812 cur->ns->href))) {
6813#ifdef DEBUG_STEP
6814 n++;
6815#endif
6816 addNode(list, cur);
6817 }
6818 }
6819 }
6820 break;
6821 case XML_ATTRIBUTE_NODE: {
6822 xmlAttrPtr attr = (xmlAttrPtr) cur;
6823 if (xmlStrEqual(name, attr->name)) {
6824 if (prefix == NULL) {
6825 if ((attr->ns == NULL) ||
6826 (attr->ns->prefix == NULL)) {
6827#ifdef DEBUG_STEP
6828 n++;
6829#endif
6830 addNode(list, (xmlNodePtr) attr);
6831 }
6832 } else {
6833 if ((attr->ns != NULL) &&
6834 (xmlStrEqual(prefix,
6835 attr->ns->href))) {
6836#ifdef DEBUG_STEP
6837 n++;
6838#endif
6839 addNode(list, (xmlNodePtr) attr);
6840 }
6841 }
6842 }
6843 break;
6844 }
6845 case XML_NAMESPACE_DECL: {
6846 TODO;
6847 break;
6848 }
6849 default:
6850 break;
6851 }
6852 break;
6853 }
6854 } while (cur != NULL);
6855
6856 /*
6857 * If there is some predicate filtering do it now
6858 */
6859 if (op->ch2 != -1) {
6860 xmlXPathObjectPtr obj2;
6861
6862 valuePush(ctxt, xmlXPathWrapNodeSet(list));
6863 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
6864 CHECK_TYPE(XPATH_NODESET);
6865 obj2 = valuePop(ctxt);
6866 list = obj2->nodesetval;
6867 obj2->nodesetval = NULL;
6868 xmlXPathFreeObject(obj2);
6869 }
6870 if (ret == NULL) {
6871 ret = list;
6872 } else {
6873 ret = xmlXPathNodeSetMerge(ret, list);
6874 xmlXPathFreeNodeSet(list);
6875 }
6876 }
6877 ctxt->context->node = tmp;
6878#ifdef DEBUG_STEP
6879 xmlGenericError(xmlGenericErrorContext,
6880 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
6881#endif
6882 xmlXPathFreeObject(obj);
6883 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
6884}
6885
Owen Taylor3473f882001-02-23 17:55:21 +00006886/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006887 * xmlXPathCompOpEval:
6888 * @ctxt: the XPath parser context with the compiled expression
6889 * @op: an XPath compiled operation
6890 *
6891 * Evaluate the Precompiled XPath operation
6892 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006893static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006894xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) {
6895 int equal, ret;
6896 xmlXPathCompExprPtr comp;
6897 xmlXPathObjectPtr arg1, arg2;
6898
6899 comp = ctxt->comp;
6900 switch (op->op) {
6901 case XPATH_OP_END:
6902 return;
6903 case XPATH_OP_AND:
6904 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6905 xmlXPathBooleanFunction(ctxt, 1);
6906 if (ctxt->value->boolval == 0)
6907 return;
6908 arg2 = valuePop(ctxt);
6909 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6910 xmlXPathBooleanFunction(ctxt, 1);
6911 arg1 = valuePop(ctxt);
6912 arg1->boolval &= arg2->boolval;
6913 valuePush(ctxt, arg1);
6914 xmlXPathFreeObject(arg2);
6915 return;
6916 case XPATH_OP_OR:
6917 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6918 xmlXPathBooleanFunction(ctxt, 1);
6919 if (ctxt->value->boolval == 1)
6920 return;
6921 arg2 = valuePop(ctxt);
6922 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6923 xmlXPathBooleanFunction(ctxt, 1);
6924 arg1 = valuePop(ctxt);
6925 arg1->boolval |= arg2->boolval;
6926 valuePush(ctxt, arg1);
6927 xmlXPathFreeObject(arg2);
6928 return;
6929 case XPATH_OP_EQUAL:
6930 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6931 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6932 equal = xmlXPathEqualValues(ctxt);
6933 if (op->value) valuePush(ctxt, xmlXPathNewBoolean(equal));
6934 else valuePush(ctxt, xmlXPathNewBoolean(!equal));
6935 return;
6936 case XPATH_OP_CMP:
6937 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6938 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6939 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
6940 valuePush(ctxt, xmlXPathNewBoolean(ret));
6941 return;
6942 case XPATH_OP_PLUS:
6943 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6944 if (op->ch2 != -1)
6945 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6946 if (op->value == 0) xmlXPathSubValues(ctxt);
6947 else if (op->value == 1) xmlXPathAddValues(ctxt);
6948 else if (op->value == 2) xmlXPathValueFlipSign(ctxt);
6949 else if (op->value == 3) {
6950 xmlXPathObjectPtr arg;
6951
6952 POP_FLOAT
6953 valuePush(ctxt, arg);
6954 }
6955 return;
6956 case XPATH_OP_MULT:
6957 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6958 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6959 if (op->value == 0) xmlXPathMultValues(ctxt);
6960 else if (op->value == 1) xmlXPathDivValues(ctxt);
6961 else if (op->value == 2) xmlXPathModValues(ctxt);
6962 return;
6963 case XPATH_OP_UNION:
6964 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6965 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6966 CHECK_TYPE(XPATH_NODESET);
6967 arg2 = valuePop(ctxt);
6968
6969 CHECK_TYPE(XPATH_NODESET);
6970 arg1 = valuePop(ctxt);
6971
6972 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
6973 arg2->nodesetval);
6974 valuePush(ctxt, arg1);
6975 xmlXPathFreeObject(arg2);
6976 return;
6977 case XPATH_OP_ROOT:
6978 xmlXPathRoot(ctxt);
6979 return;
6980 case XPATH_OP_NODE:
6981 if (op->ch1 != -1)
6982 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6983 if (op->ch2 != -1)
6984 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6985 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
6986 return;
6987 case XPATH_OP_RESET:
6988 if (op->ch1 != -1)
6989 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6990 if (op->ch2 != -1)
6991 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6992 ctxt->context->node = NULL;
6993 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006994 case XPATH_OP_COLLECT: {
6995 if (op->ch1 == -1)
6996 return;
6997
6998 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6999 xmlXPathNodeCollectAndTest(ctxt, op);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007000 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007001 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007002 case XPATH_OP_VALUE:
7003 valuePush(ctxt,
7004 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
7005 return;
7006 case XPATH_OP_VARIABLE: {
7007 if (op->ch1 != -1)
7008 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7009 if (op->value5 == NULL)
7010 valuePush(ctxt,
7011 xmlXPathVariableLookup(ctxt->context, op->value4));
7012 else {
7013 const xmlChar *URI;
7014 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7015 if (URI == NULL) {
7016 xmlGenericError(xmlGenericErrorContext,
7017 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
7018 op->value4, op->value5);
7019 return;
7020 }
7021 valuePush(ctxt,
7022 xmlXPathVariableLookupNS(ctxt->context,
7023 op->value4, URI));
7024 }
7025 return;
7026 }
7027 case XPATH_OP_FUNCTION: {
7028 xmlXPathFunction func;
7029
7030 if (op->ch1 != -1)
7031 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7032 if (op->value5 == NULL)
7033 func = xmlXPathFunctionLookup(ctxt->context, op->value4);
7034 else {
7035 const xmlChar *URI;
7036 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7037 if (URI == NULL) {
7038 xmlGenericError(xmlGenericErrorContext,
7039 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
7040 op->value4, op->value5);
7041 return;
7042 }
7043 func = xmlXPathFunctionLookupNS(ctxt->context,
7044 op->value4, URI);
7045 }
7046 if (func == NULL) {
7047 xmlGenericError(xmlGenericErrorContext,
7048 "xmlXPathRunEval: function %s not found\n",
7049 op->value4);
7050 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
7051 return;
7052 }
7053 func(ctxt, op->value);
7054 return;
7055 }
7056 case XPATH_OP_ARG:
7057 if (op->ch1 != -1)
7058 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7059 if (op->ch2 != -1)
7060 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7061 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007062 case XPATH_OP_PREDICATE:
7063 case XPATH_OP_FILTER: {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007064 xmlXPathObjectPtr res;
7065 xmlXPathObjectPtr obj, tmp;
7066 xmlNodeSetPtr newset = NULL;
7067 xmlNodeSetPtr oldset;
7068 xmlNodePtr oldnode;
7069 int i;
7070
7071 if (op->ch1 != -1)
7072 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7073 if (op->ch2 == -1)
7074 return;
7075
7076 oldnode = ctxt->context->node;
7077
7078#ifdef LIBXML_XPTR_ENABLED
7079 /*
7080 * Hum are we filtering the result of an XPointer expression
7081 */
7082 if (ctxt->value->type == XPATH_LOCATIONSET) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007083 xmlLocationSetPtr newlocset = NULL;
7084 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007085
7086 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007087 * Extract the old locset, and then evaluate the result of the
7088 * expression for all the element in the locset. use it to grow
7089 * up a new locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007090 */
7091 CHECK_TYPE(XPATH_LOCATIONSET);
7092 obj = valuePop(ctxt);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007093 oldlocset = obj->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007094 ctxt->context->node = NULL;
7095
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007096 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007097 ctxt->context->contextSize = 0;
7098 ctxt->context->proximityPosition = 0;
7099 if (op->ch2 != -1)
7100 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7101 res = valuePop(ctxt);
7102 if (res != NULL)
7103 xmlXPathFreeObject(res);
7104 valuePush(ctxt, obj);
7105 CHECK_ERROR;
7106 return;
7107 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007108 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007109
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007110 for (i = 0; i < oldlocset->locNr; i++) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007111 /*
7112 * Run the evaluation with a node list made of a
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007113 * single item in the nodelocset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007114 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007115 ctxt->context->node = oldlocset->locTab[i]->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007116 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7117 valuePush(ctxt, tmp);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007118 ctxt->context->contextSize = oldlocset->locNr;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007119 ctxt->context->proximityPosition = i + 1;
7120
7121 if (op->ch2 != -1)
7122 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7123 CHECK_ERROR;
7124
7125 /*
7126 * The result of the evaluation need to be tested to
7127 * decided whether the filter succeeded or not
7128 */
7129 res = valuePop(ctxt);
7130 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007131 xmlXPtrLocationSetAdd(newlocset,
7132 xmlXPathObjectCopy(oldlocset->locTab[i]));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007133 }
7134
7135 /*
7136 * Cleanup
7137 */
7138 if (res != NULL)
7139 xmlXPathFreeObject(res);
7140 if (ctxt->value == tmp) {
7141 res = valuePop(ctxt);
7142 xmlXPathFreeObject(res);
7143 }
7144
7145 ctxt->context->node = NULL;
7146 }
7147
7148 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007149 * The result is used as the new evaluation locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007150 */
7151 xmlXPathFreeObject(obj);
7152 ctxt->context->node = NULL;
7153 ctxt->context->contextSize = -1;
7154 ctxt->context->proximityPosition = -1;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007155 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007156 ctxt->context->node = oldnode;
7157 return;
7158 }
7159#endif /* LIBXML_XPTR_ENABLED */
7160
7161 /*
7162 * Extract the old set, and then evaluate the result of the
7163 * expression for all the element in the set. use it to grow
7164 * up a new set.
7165 */
7166 CHECK_TYPE(XPATH_NODESET);
7167 obj = valuePop(ctxt);
7168 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00007169
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007170 oldnode = ctxt->context->node;
7171 ctxt->context->node = NULL;
7172
7173 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
7174 ctxt->context->contextSize = 0;
7175 ctxt->context->proximityPosition = 0;
7176 if (op->ch2 != -1)
7177 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7178 res = valuePop(ctxt);
7179 if (res != NULL)
7180 xmlXPathFreeObject(res);
7181 valuePush(ctxt, obj);
Daniel Veillard911f49a2001-04-07 15:39:35 +00007182 ctxt->context->node = oldnode;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007183 CHECK_ERROR;
7184 } else {
7185 /*
7186 * Initialize the new set.
7187 */
7188 newset = xmlXPathNodeSetCreate(NULL);
7189
7190 for (i = 0; i < oldset->nodeNr; i++) {
7191 /*
7192 * Run the evaluation with a node list made of
7193 * a single item in the nodeset.
7194 */
7195 ctxt->context->node = oldset->nodeTab[i];
7196 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7197 valuePush(ctxt, tmp);
7198 ctxt->context->contextSize = oldset->nodeNr;
7199 ctxt->context->proximityPosition = i + 1;
7200
7201 if (op->ch2 != -1)
7202 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7203 CHECK_ERROR;
7204
7205 /*
7206 * The result of the evaluation need to be tested to
7207 * decided whether the filter succeeded or not
7208 */
7209 res = valuePop(ctxt);
7210 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
7211 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
7212 }
7213
7214 /*
7215 * Cleanup
7216 */
7217 if (res != NULL)
7218 xmlXPathFreeObject(res);
7219 if (ctxt->value == tmp) {
7220 res = valuePop(ctxt);
7221 xmlXPathFreeObject(res);
7222 }
7223
7224 ctxt->context->node = NULL;
7225 }
7226
7227 /*
7228 * The result is used as the new evaluation set.
7229 */
7230 xmlXPathFreeObject(obj);
7231 ctxt->context->node = NULL;
7232 ctxt->context->contextSize = -1;
7233 ctxt->context->proximityPosition = -1;
7234 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
7235 }
7236 ctxt->context->node = oldnode;
7237 return;
7238 }
7239 case XPATH_OP_SORT:
7240 if (op->ch1 != -1)
7241 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7242 if ((ctxt->value != NULL) &&
7243 (ctxt->value->type == XPATH_NODESET) &&
7244 (ctxt->value->nodesetval != NULL))
7245 xmlXPathNodeSetSort(ctxt->value->nodesetval);
7246 return;
7247#ifdef LIBXML_XPTR_ENABLED
7248 case XPATH_OP_RANGETO: {
7249 xmlXPathObjectPtr range;
7250 xmlXPathObjectPtr res, obj;
7251 xmlXPathObjectPtr tmp;
7252 xmlLocationSetPtr newset = NULL;
7253 xmlNodeSetPtr oldset;
7254 int i;
7255
7256 if (op->ch1 != -1)
7257 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7258 if (op->ch2 == -1)
7259 return;
7260
7261 CHECK_TYPE(XPATH_NODESET);
7262 obj = valuePop(ctxt);
7263 oldset = obj->nodesetval;
7264 ctxt->context->node = NULL;
7265
7266 newset = xmlXPtrLocationSetCreate(NULL);
7267
Daniel Veillard911f49a2001-04-07 15:39:35 +00007268 if (oldset != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007269 for (i = 0; i < oldset->nodeNr; i++) {
7270 /*
7271 * Run the evaluation with a node list made of a single item
7272 * in the nodeset.
7273 */
7274 ctxt->context->node = oldset->nodeTab[i];
7275 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7276 valuePush(ctxt, tmp);
7277
7278 if (op->ch2 != -1)
7279 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7280 CHECK_ERROR;
7281
7282 /*
7283 * The result of the evaluation need to be tested to
7284 * decided whether the filter succeeded or not
7285 */
7286 res = valuePop(ctxt);
7287 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
7288 if (range != NULL) {
7289 xmlXPtrLocationSetAdd(newset, range);
7290 }
7291
7292 /*
7293 * Cleanup
7294 */
7295 if (res != NULL)
7296 xmlXPathFreeObject(res);
7297 if (ctxt->value == tmp) {
7298 res = valuePop(ctxt);
7299 xmlXPathFreeObject(res);
7300 }
7301
7302 ctxt->context->node = NULL;
7303 }
Daniel Veillard911f49a2001-04-07 15:39:35 +00007304 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007305
7306 /*
7307 * The result is used as the new evaluation set.
7308 */
7309 xmlXPathFreeObject(obj);
7310 ctxt->context->node = NULL;
7311 ctxt->context->contextSize = -1;
7312 ctxt->context->proximityPosition = -1;
7313 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
7314 return;
7315 }
7316#endif /* LIBXML_XPTR_ENABLED */
7317 }
7318 xmlGenericError(xmlGenericErrorContext,
7319 "XPath: unknown precompiled operation %d\n",
7320 op->op);
7321 return;
7322}
7323
7324/**
7325 * xmlXPathRunEval:
7326 * @ctxt: the XPath parser context with the compiled expression
7327 *
7328 * Evaluate the Precompiled XPath expression in the given context.
7329 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007330static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007331xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
7332 xmlXPathCompExprPtr comp;
7333
7334 if ((ctxt == NULL) || (ctxt->comp == NULL))
7335 return;
7336
7337 if (ctxt->valueTab == NULL) {
7338 /* Allocate the value stack */
7339 ctxt->valueTab = (xmlXPathObjectPtr *)
7340 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
7341 if (ctxt->valueTab == NULL) {
7342 xmlFree(ctxt);
7343 xmlGenericError(xmlGenericErrorContext,
7344 "xmlXPathRunEval: out of memory\n");
7345 return;
7346 }
7347 ctxt->valueNr = 0;
7348 ctxt->valueMax = 10;
7349 ctxt->value = NULL;
7350 }
7351 comp = ctxt->comp;
7352 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
7353}
7354
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007355/************************************************************************
7356 * *
7357 * Public interfaces *
7358 * *
7359 ************************************************************************/
7360
7361/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007362 * xmlXPathEvalPredicate:
7363 * @ctxt: the XPath context
7364 * @res: the Predicate Expression evaluation result
7365 *
7366 * Evaluate a predicate result for the current node.
7367 * A PredicateExpr is evaluated by evaluating the Expr and converting
7368 * the result to a boolean. If the result is a number, the result will
7369 * be converted to true if the number is equal to the position of the
7370 * context node in the context node list (as returned by the position
7371 * function) and will be converted to false otherwise; if the result
7372 * is not a number, then the result will be converted as if by a call
7373 * to the boolean function.
7374 *
7375 * Return 1 if predicate is true, 0 otherwise
7376 */
7377int
7378xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
7379 if (res == NULL) return(0);
7380 switch (res->type) {
7381 case XPATH_BOOLEAN:
7382 return(res->boolval);
7383 case XPATH_NUMBER:
7384 return(res->floatval == ctxt->proximityPosition);
7385 case XPATH_NODESET:
7386 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007387 if (res->nodesetval == NULL)
7388 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007389 return(res->nodesetval->nodeNr != 0);
7390 case XPATH_STRING:
7391 return((res->stringval != NULL) &&
7392 (xmlStrlen(res->stringval) != 0));
7393 default:
7394 STRANGE
7395 }
7396 return(0);
7397}
7398
7399/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007400 * xmlXPathEvaluatePredicateResult:
7401 * @ctxt: the XPath Parser context
7402 * @res: the Predicate Expression evaluation result
7403 *
7404 * Evaluate a predicate result for the current node.
7405 * A PredicateExpr is evaluated by evaluating the Expr and converting
7406 * the result to a boolean. If the result is a number, the result will
7407 * be converted to true if the number is equal to the position of the
7408 * context node in the context node list (as returned by the position
7409 * function) and will be converted to false otherwise; if the result
7410 * is not a number, then the result will be converted as if by a call
7411 * to the boolean function.
7412 *
7413 * Return 1 if predicate is true, 0 otherwise
7414 */
7415int
7416xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
7417 xmlXPathObjectPtr res) {
7418 if (res == NULL) return(0);
7419 switch (res->type) {
7420 case XPATH_BOOLEAN:
7421 return(res->boolval);
7422 case XPATH_NUMBER:
7423 return(res->floatval == ctxt->context->proximityPosition);
7424 case XPATH_NODESET:
7425 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00007426 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00007427 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007428 return(res->nodesetval->nodeNr != 0);
7429 case XPATH_STRING:
7430 return((res->stringval != NULL) &&
7431 (xmlStrlen(res->stringval) != 0));
7432 default:
7433 STRANGE
7434 }
7435 return(0);
7436}
7437
7438/**
7439 * xmlXPathCompile:
7440 * @str: the XPath expression
7441 *
7442 * Compile an XPath expression
7443 *
7444 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7445 * the caller has to free the object.
7446 */
7447xmlXPathCompExprPtr
7448xmlXPathCompile(const xmlChar *str) {
7449 xmlXPathParserContextPtr ctxt;
7450 xmlXPathCompExprPtr comp;
7451
7452 xmlXPathInit();
7453
7454 ctxt = xmlXPathNewParserContext(str, NULL);
7455 xmlXPathCompileExpr(ctxt);
7456
7457 comp = ctxt->comp;
7458 ctxt->comp = NULL;
7459 xmlXPathFreeParserContext(ctxt);
7460 return(comp);
7461}
7462
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007463/**
7464 * xmlXPathCompiledEval:
7465 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00007466 * @ctx: the XPath context
7467 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007468 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00007469 *
7470 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7471 * the caller has to free the object.
7472 */
7473xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007474xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00007475 xmlXPathParserContextPtr ctxt;
7476 xmlXPathObjectPtr res, tmp, init = NULL;
7477 int stack = 0;
7478
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007479 if ((comp == NULL) || (ctx == NULL))
7480 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007481 xmlXPathInit();
7482
7483 CHECK_CONTEXT(ctx)
7484
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007485 ctxt = xmlXPathCompParserContext(comp, ctx);
7486 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007487
7488 if (ctxt->value == NULL) {
7489 xmlGenericError(xmlGenericErrorContext,
7490 "xmlXPathEval: evaluation failed\n");
7491 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00007492 } else {
7493 res = valuePop(ctxt);
7494 }
7495
7496 do {
7497 tmp = valuePop(ctxt);
7498 if (tmp != NULL) {
7499 if (tmp != init)
7500 stack++;
7501 xmlXPathFreeObject(tmp);
7502 }
7503 } while (tmp != NULL);
7504 if ((stack != 0) && (res != NULL)) {
7505 xmlGenericError(xmlGenericErrorContext,
7506 "xmlXPathEval: %d object left on the stack\n",
7507 stack);
7508 }
7509 if (ctxt->error != XPATH_EXPRESSION_OK) {
7510 xmlXPathFreeObject(res);
7511 res = NULL;
7512 }
7513
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007514
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007515 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007516 xmlXPathFreeParserContext(ctxt);
7517 return(res);
7518}
7519
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007520/**
7521 * xmlXPathEvalExpr:
7522 * @ctxt: the XPath Parser context
7523 *
7524 * Parse and evaluate an XPath expression in the given context,
7525 * then push the result on the context stack
7526 */
7527void
7528xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
7529 xmlXPathCompileExpr(ctxt);
7530 xmlXPathRunEval(ctxt);
7531}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007532
7533/**
7534 * xmlXPathEval:
7535 * @str: the XPath expression
7536 * @ctx: the XPath context
7537 *
7538 * Evaluate the XPath Location Path in the given context.
7539 *
7540 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7541 * the caller has to free the object.
7542 */
7543xmlXPathObjectPtr
7544xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
7545 xmlXPathParserContextPtr ctxt;
7546 xmlXPathObjectPtr res, tmp, init = NULL;
7547 int stack = 0;
7548
7549 xmlXPathInit();
7550
7551 CHECK_CONTEXT(ctx)
7552
7553 ctxt = xmlXPathNewParserContext(str, ctx);
7554 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007555
7556 if (ctxt->value == NULL) {
7557 xmlGenericError(xmlGenericErrorContext,
7558 "xmlXPathEval: evaluation failed\n");
7559 res = NULL;
7560 } else if (*ctxt->cur != 0) {
7561 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7562 res = NULL;
7563 } else {
7564 res = valuePop(ctxt);
7565 }
7566
7567 do {
7568 tmp = valuePop(ctxt);
7569 if (tmp != NULL) {
7570 if (tmp != init)
7571 stack++;
7572 xmlXPathFreeObject(tmp);
7573 }
7574 } while (tmp != NULL);
7575 if ((stack != 0) && (res != NULL)) {
7576 xmlGenericError(xmlGenericErrorContext,
7577 "xmlXPathEval: %d object left on the stack\n",
7578 stack);
7579 }
7580 if (ctxt->error != XPATH_EXPRESSION_OK) {
7581 xmlXPathFreeObject(res);
7582 res = NULL;
7583 }
7584
Owen Taylor3473f882001-02-23 17:55:21 +00007585 xmlXPathFreeParserContext(ctxt);
7586 return(res);
7587}
7588
7589/**
7590 * xmlXPathEvalExpression:
7591 * @str: the XPath expression
7592 * @ctxt: the XPath context
7593 *
7594 * Evaluate the XPath expression in the given context.
7595 *
7596 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
7597 * the caller has to free the object.
7598 */
7599xmlXPathObjectPtr
7600xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
7601 xmlXPathParserContextPtr pctxt;
7602 xmlXPathObjectPtr res, tmp;
7603 int stack = 0;
7604
7605 xmlXPathInit();
7606
7607 CHECK_CONTEXT(ctxt)
7608
7609 pctxt = xmlXPathNewParserContext(str, ctxt);
7610 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007611
7612 if (*pctxt->cur != 0) {
7613 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7614 res = NULL;
7615 } else {
7616 res = valuePop(pctxt);
7617 }
7618 do {
7619 tmp = valuePop(pctxt);
7620 if (tmp != NULL) {
7621 xmlXPathFreeObject(tmp);
7622 stack++;
7623 }
7624 } while (tmp != NULL);
7625 if ((stack != 0) && (res != NULL)) {
7626 xmlGenericError(xmlGenericErrorContext,
7627 "xmlXPathEvalExpression: %d object left on the stack\n",
7628 stack);
7629 }
7630 xmlXPathFreeParserContext(pctxt);
7631 return(res);
7632}
7633
7634/**
7635 * xmlXPathRegisterAllFunctions:
7636 * @ctxt: the XPath context
7637 *
7638 * Registers all default XPath functions in this context
7639 */
7640void
7641xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
7642{
7643 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
7644 xmlXPathBooleanFunction);
7645 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
7646 xmlXPathCeilingFunction);
7647 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
7648 xmlXPathCountFunction);
7649 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
7650 xmlXPathConcatFunction);
7651 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
7652 xmlXPathContainsFunction);
7653 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
7654 xmlXPathIdFunction);
7655 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
7656 xmlXPathFalseFunction);
7657 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
7658 xmlXPathFloorFunction);
7659 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
7660 xmlXPathLastFunction);
7661 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
7662 xmlXPathLangFunction);
7663 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
7664 xmlXPathLocalNameFunction);
7665 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
7666 xmlXPathNotFunction);
7667 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
7668 xmlXPathNameFunction);
7669 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
7670 xmlXPathNamespaceURIFunction);
7671 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
7672 xmlXPathNormalizeFunction);
7673 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
7674 xmlXPathNumberFunction);
7675 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
7676 xmlXPathPositionFunction);
7677 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
7678 xmlXPathRoundFunction);
7679 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
7680 xmlXPathStringFunction);
7681 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
7682 xmlXPathStringLengthFunction);
7683 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
7684 xmlXPathStartsWithFunction);
7685 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
7686 xmlXPathSubstringFunction);
7687 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
7688 xmlXPathSubstringBeforeFunction);
7689 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
7690 xmlXPathSubstringAfterFunction);
7691 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
7692 xmlXPathSumFunction);
7693 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
7694 xmlXPathTrueFunction);
7695 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
7696 xmlXPathTranslateFunction);
7697}
7698
7699#endif /* LIBXML_XPATH_ENABLED */