blob: fd760e5a8fda800a1100f4decaa7ec8cbbfb0b29 [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,
207 XPATH_OP_SORT
208#ifdef LIBXML_XPTR_ENABLED
209 ,XPATH_OP_RANGETO
210#endif
211} xmlXPathOp;
212
213typedef enum {
214 AXIS_ANCESTOR = 1,
215 AXIS_ANCESTOR_OR_SELF,
216 AXIS_ATTRIBUTE,
217 AXIS_CHILD,
218 AXIS_DESCENDANT,
219 AXIS_DESCENDANT_OR_SELF,
220 AXIS_FOLLOWING,
221 AXIS_FOLLOWING_SIBLING,
222 AXIS_NAMESPACE,
223 AXIS_PARENT,
224 AXIS_PRECEDING,
225 AXIS_PRECEDING_SIBLING,
226 AXIS_SELF
227} xmlXPathAxisVal;
228
229typedef enum {
230 NODE_TEST_NONE = 0,
231 NODE_TEST_TYPE = 1,
232 NODE_TEST_PI = 2,
233 NODE_TEST_ALL = 3,
234 NODE_TEST_NS = 4,
235 NODE_TEST_NAME = 5
236} xmlXPathTestVal;
237
238typedef enum {
239 NODE_TYPE_NODE = 0,
240 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
241 NODE_TYPE_TEXT = XML_TEXT_NODE,
242 NODE_TYPE_PI = XML_PI_NODE
243} xmlXPathTypeVal;
244
245
246typedef struct _xmlXPathStepOp xmlXPathStepOp;
247typedef xmlXPathStepOp *xmlXPathStepOpPtr;
248struct _xmlXPathStepOp {
249 xmlXPathOp op;
250 int ch1;
251 int ch2;
252 int value;
253 int value2;
254 int value3;
255 void *value4;
256 void *value5;
257};
258
259struct _xmlXPathCompExpr {
260 int nbStep;
261 int maxStep;
262 xmlXPathStepOp *steps; /* ops for computation */
263 int last;
264};
265
266/************************************************************************
267 * *
268 * Parser Type functions *
269 * *
270 ************************************************************************/
271
272/**
273 * xmlXPathNewCompExpr:
274 *
275 * Create a new Xpath component
276 *
277 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
278 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000279static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000280xmlXPathNewCompExpr(void) {
281 xmlXPathCompExprPtr cur;
282
283 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
284 if (cur == NULL) {
285 xmlGenericError(xmlGenericErrorContext,
286 "xmlXPathNewCompExpr : malloc failed\n");
287 return(NULL);
288 }
289 memset(cur, 0, sizeof(xmlXPathCompExpr));
290 cur->maxStep = 10;
291 cur->nbStep = 0;
292 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
293 sizeof(xmlXPathStepOp));
294 if (cur->steps == NULL) {
295 xmlGenericError(xmlGenericErrorContext,
296 "xmlXPathNewCompExpr : malloc failed\n");
297 xmlFree(cur);
298 return(NULL);
299 }
300 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
301 cur->last = -1;
302 return(cur);
303}
304
305/**
306 * xmlXPathFreeCompExpr:
307 * @comp: an XPATH comp
308 *
309 * Free up the memory allocated by @comp
310 */
311void
312xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) {
313 xmlXPathStepOpPtr op;
314 int i;
315
316 if (comp == NULL)
317 return;
318 for (i = 0;i < comp->nbStep;i++) {
319 op = &comp->steps[i];
320 if (op->value4 != NULL) {
321 if (op->op == XPATH_OP_VALUE)
322 xmlXPathFreeObject(op->value4);
323 else
324 xmlFree(op->value4);
325 }
326 if (op->value5 != NULL)
327 xmlFree(op->value5);
328 }
329 if (comp->steps != NULL) {
330 MEM_CLEANUP(comp->steps, sizeof(xmlXPathStepOp));
331 xmlFree(comp->steps);
332 }
333 MEM_CLEANUP(comp, sizeof(xmlXPathCompExpr));
334 xmlFree(comp);
335}
336
337/**
338 * xmlXPathCompExprAdd:
339 * @comp: the compiled expression
340 * @ch1: first child index
341 * @ch2: second child index
342 * @op: an op
343 * @value: the first int value
344 * @value2: the second int value
345 * @value3: the third int value
346 * @value4: the first string value
347 * @value5: the second string value
348 *
349 * Add an step to an XPath Compiled Expression
350 *
351 * Returns -1 in case of failure, the index otherwise
352 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000353static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000354xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
355 xmlXPathOp op, int value,
356 int value2, int value3, void *value4, void *value5) {
357 if (comp->nbStep >= comp->maxStep) {
358 xmlXPathStepOp *real;
359
360 comp->maxStep *= 2;
361 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
362 comp->maxStep * sizeof(xmlXPathStepOp));
363 if (real == NULL) {
364 comp->maxStep /= 2;
365 xmlGenericError(xmlGenericErrorContext,
366 "xmlXPathCompExprAdd : realloc failed\n");
367 return(-1);
368 }
369 comp->steps = real;
370 }
371 comp->last = comp->nbStep;
372 comp->steps[comp->nbStep].ch1 = ch1;
373 comp->steps[comp->nbStep].ch2 = ch2;
374 comp->steps[comp->nbStep].op = op;
375 comp->steps[comp->nbStep].value = value;
376 comp->steps[comp->nbStep].value2 = value2;
377 comp->steps[comp->nbStep].value3 = value3;
378 comp->steps[comp->nbStep].value4 = value4;
379 comp->steps[comp->nbStep].value5 = value5;
380 return(comp->nbStep++);
381}
382
383#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
384 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
385 (op), (val), (val2), (val3), (val4), (val5))
386
387#define PUSH_LEAVE_EXPR(op, val, val2) \
388xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
389
390#define PUSH_UNARY_EXPR(op, ch, val, val2) \
391xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
392
393#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
394xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
395
396/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000397 * *
398 * Debugging related functions *
399 * *
400 ************************************************************************/
401
402#define TODO \
403 xmlGenericError(xmlGenericErrorContext, \
404 "Unimplemented block at %s:%d\n", \
405 __FILE__, __LINE__);
406
407#define STRANGE \
408 xmlGenericError(xmlGenericErrorContext, \
409 "Internal error at %s:%d\n", \
410 __FILE__, __LINE__);
411
412#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000413static void
414xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000415 int i;
416 char shift[100];
417
418 for (i = 0;((i < depth) && (i < 25));i++)
419 shift[2 * i] = shift[2 * i + 1] = ' ';
420 shift[2 * i] = shift[2 * i + 1] = 0;
421 if (cur == NULL) {
422 fprintf(output, shift);
423 fprintf(output, "Node is NULL !\n");
424 return;
425
426 }
427
428 if ((cur->type == XML_DOCUMENT_NODE) ||
429 (cur->type == XML_HTML_DOCUMENT_NODE)) {
430 fprintf(output, shift);
431 fprintf(output, " /\n");
432 } else if (cur->type == XML_ATTRIBUTE_NODE)
433 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
434 else
435 xmlDebugDumpOneNode(output, cur, depth);
436}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000437static void
438xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000439 xmlNodePtr tmp;
440 int i;
441 char shift[100];
442
443 for (i = 0;((i < depth) && (i < 25));i++)
444 shift[2 * i] = shift[2 * i + 1] = ' ';
445 shift[2 * i] = shift[2 * i + 1] = 0;
446 if (cur == NULL) {
447 fprintf(output, shift);
448 fprintf(output, "Node is NULL !\n");
449 return;
450
451 }
452
453 while (cur != NULL) {
454 tmp = cur;
455 cur = cur->next;
456 xmlDebugDumpOneNode(output, tmp, depth);
457 }
458}
Owen Taylor3473f882001-02-23 17:55:21 +0000459
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000460static void
461xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000462 int i;
463 char shift[100];
464
465 for (i = 0;((i < depth) && (i < 25));i++)
466 shift[2 * i] = shift[2 * i + 1] = ' ';
467 shift[2 * i] = shift[2 * i + 1] = 0;
468
469 if (cur == NULL) {
470 fprintf(output, shift);
471 fprintf(output, "NodeSet is NULL !\n");
472 return;
473
474 }
475
476 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
477 for (i = 0;i < cur->nodeNr;i++) {
478 fprintf(output, shift);
479 fprintf(output, "%d", i + 1);
480 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
481 }
482}
483
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000484static void
485xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000486 int i;
487 char shift[100];
488
489 for (i = 0;((i < depth) && (i < 25));i++)
490 shift[2 * i] = shift[2 * i + 1] = ' ';
491 shift[2 * i] = shift[2 * i + 1] = 0;
492
493 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
494 fprintf(output, shift);
495 fprintf(output, "Value Tree is NULL !\n");
496 return;
497
498 }
499
500 fprintf(output, shift);
501 fprintf(output, "%d", i + 1);
502 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
503}
Owen Taylor3473f882001-02-23 17:55:21 +0000504#if defined(LIBXML_XPTR_ENABLED)
505void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000506static void
507xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000508 int i;
509 char shift[100];
510
511 for (i = 0;((i < depth) && (i < 25));i++)
512 shift[2 * i] = shift[2 * i + 1] = ' ';
513 shift[2 * i] = shift[2 * i + 1] = 0;
514
515 if (cur == NULL) {
516 fprintf(output, shift);
517 fprintf(output, "LocationSet is NULL !\n");
518 return;
519
520 }
521
522 for (i = 0;i < cur->locNr;i++) {
523 fprintf(output, shift);
524 fprintf(output, "%d : ", i + 1);
525 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
526 }
527}
528#endif
529
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000530/**
531 * xmlXPathDebugDumpObject:
532 * @output: the FILE * to dump the output
533 * @cur: the object to inspect
534 * @depth: indentation level
535 *
536 * Dump the content of the object for debugging purposes
537 */
538void
539xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000540 int i;
541 char shift[100];
542
543 for (i = 0;((i < depth) && (i < 25));i++)
544 shift[2 * i] = shift[2 * i + 1] = ' ';
545 shift[2 * i] = shift[2 * i + 1] = 0;
546
547 fprintf(output, shift);
548
549 if (cur == NULL) {
550 fprintf(output, "Object is empty (NULL)\n");
551 return;
552 }
553 switch(cur->type) {
554 case XPATH_UNDEFINED:
555 fprintf(output, "Object is uninitialized\n");
556 break;
557 case XPATH_NODESET:
558 fprintf(output, "Object is a Node Set :\n");
559 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
560 break;
561 case XPATH_XSLT_TREE:
562 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000563 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000564 break;
565 case XPATH_BOOLEAN:
566 fprintf(output, "Object is a Boolean : ");
567 if (cur->boolval) fprintf(output, "true\n");
568 else fprintf(output, "false\n");
569 break;
570 case XPATH_NUMBER:
571 fprintf(output, "Object is a number : %0g\n", cur->floatval);
572 break;
573 case XPATH_STRING:
574 fprintf(output, "Object is a string : ");
575 xmlDebugDumpString(output, cur->stringval);
576 fprintf(output, "\n");
577 break;
578 case XPATH_POINT:
579 fprintf(output, "Object is a point : index %d in node", cur->index);
580 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
581 fprintf(output, "\n");
582 break;
583 case XPATH_RANGE:
584 if ((cur->user2 == NULL) ||
585 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
586 fprintf(output, "Object is a collapsed range :\n");
587 fprintf(output, shift);
588 if (cur->index >= 0)
589 fprintf(output, "index %d in ", cur->index);
590 fprintf(output, "node\n");
591 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
592 depth + 1);
593 } else {
594 fprintf(output, "Object is a range :\n");
595 fprintf(output, shift);
596 fprintf(output, "From ");
597 if (cur->index >= 0)
598 fprintf(output, "index %d in ", cur->index);
599 fprintf(output, "node\n");
600 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
601 depth + 1);
602 fprintf(output, shift);
603 fprintf(output, "To ");
604 if (cur->index2 >= 0)
605 fprintf(output, "index %d in ", cur->index2);
606 fprintf(output, "node\n");
607 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
608 depth + 1);
609 fprintf(output, "\n");
610 }
611 break;
612 case XPATH_LOCATIONSET:
613#if defined(LIBXML_XPTR_ENABLED)
614 fprintf(output, "Object is a Location Set:\n");
615 xmlXPathDebugDumpLocationSet(output,
616 (xmlLocationSetPtr) cur->user, depth);
617#endif
618 break;
619 case XPATH_USERS:
620 fprintf(output, "Object is user defined\n");
621 break;
622 }
623}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000624
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000625static void
626xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000627 xmlXPathStepOpPtr op, int depth) {
628 int i;
629 char shift[100];
630
631 for (i = 0;((i < depth) && (i < 25));i++)
632 shift[2 * i] = shift[2 * i + 1] = ' ';
633 shift[2 * i] = shift[2 * i + 1] = 0;
634
635 fprintf(output, shift);
636 if (op == NULL) {
637 fprintf(output, "Step is NULL\n");
638 return;
639 }
640 switch (op->op) {
641 case XPATH_OP_END:
642 fprintf(output, "END"); break;
643 case XPATH_OP_AND:
644 fprintf(output, "AND"); break;
645 case XPATH_OP_OR:
646 fprintf(output, "OR"); break;
647 case XPATH_OP_EQUAL:
648 if (op->value)
649 fprintf(output, "EQUAL =");
650 else
651 fprintf(output, "EQUAL !=");
652 break;
653 case XPATH_OP_CMP:
654 if (op->value)
655 fprintf(output, "CMP <");
656 else
657 fprintf(output, "CMP >");
658 if (!op->value2)
659 fprintf(output, "=");
660 break;
661 case XPATH_OP_PLUS:
662 if (op->value == 0)
663 fprintf(output, "PLUS -");
664 else if (op->value == 1)
665 fprintf(output, "PLUS +");
666 else if (op->value == 2)
667 fprintf(output, "PLUS unary -");
668 else if (op->value == 3)
669 fprintf(output, "PLUS unary - -");
670 break;
671 case XPATH_OP_MULT:
672 if (op->value == 0)
673 fprintf(output, "MULT *");
674 else if (op->value == 1)
675 fprintf(output, "MULT div");
676 else
677 fprintf(output, "MULT mod");
678 break;
679 case XPATH_OP_UNION:
680 fprintf(output, "UNION"); break;
681 case XPATH_OP_ROOT:
682 fprintf(output, "ROOT"); break;
683 case XPATH_OP_NODE:
684 fprintf(output, "NODE"); break;
685 case XPATH_OP_RESET:
686 fprintf(output, "RESET"); break;
687 case XPATH_OP_SORT:
688 fprintf(output, "SORT"); break;
689 case XPATH_OP_COLLECT: {
690 xmlXPathAxisVal axis = op->value;
691 xmlXPathTestVal test = op->value2;
692 xmlXPathTypeVal type = op->value3;
693 const xmlChar *prefix = op->value4;
694 const xmlChar *name = op->value5;
695
696 fprintf(output, "COLLECT ");
697 switch (axis) {
698 case AXIS_ANCESTOR:
699 fprintf(output, " 'ancestors' "); break;
700 case AXIS_ANCESTOR_OR_SELF:
701 fprintf(output, " 'ancestors-or-self' "); break;
702 case AXIS_ATTRIBUTE:
703 fprintf(output, " 'attributes' "); break;
704 case AXIS_CHILD:
705 fprintf(output, " 'child' "); break;
706 case AXIS_DESCENDANT:
707 fprintf(output, " 'descendant' "); break;
708 case AXIS_DESCENDANT_OR_SELF:
709 fprintf(output, " 'descendant-or-self' "); break;
710 case AXIS_FOLLOWING:
711 fprintf(output, " 'following' "); break;
712 case AXIS_FOLLOWING_SIBLING:
713 fprintf(output, " 'following-siblings' "); break;
714 case AXIS_NAMESPACE:
715 fprintf(output, " 'namespace' "); break;
716 case AXIS_PARENT:
717 fprintf(output, " 'parent' "); break;
718 case AXIS_PRECEDING:
719 fprintf(output, " 'preceding' "); break;
720 case AXIS_PRECEDING_SIBLING:
721 fprintf(output, " 'preceding-sibling' "); break;
722 case AXIS_SELF:
723 fprintf(output, " 'self' "); break;
724 }
725 switch (test) {
726 case NODE_TEST_NONE:
727 fprintf(output, "'none' "); break;
728 case NODE_TEST_TYPE:
729 fprintf(output, "'type' "); break;
730 case NODE_TEST_PI:
731 fprintf(output, "'PI' "); break;
732 case NODE_TEST_ALL:
733 fprintf(output, "'all' "); break;
734 case NODE_TEST_NS:
735 fprintf(output, "'namespace' "); break;
736 case NODE_TEST_NAME:
737 fprintf(output, "'name' "); break;
738 }
739 switch (type) {
740 case NODE_TYPE_NODE:
741 fprintf(output, "'node' "); break;
742 case NODE_TYPE_COMMENT:
743 fprintf(output, "'comment' "); break;
744 case NODE_TYPE_TEXT:
745 fprintf(output, "'text' "); break;
746 case NODE_TYPE_PI:
747 fprintf(output, "'PI' "); break;
748 }
749 if (prefix != NULL)
750 fprintf(output, "%s:", prefix);
751 if (name != NULL)
752 fprintf(output, "%s", name);
753 break;
754
755 }
756 case XPATH_OP_VALUE: {
757 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
758
759 fprintf(output, "ELEM ");
760 xmlXPathDebugDumpObject(output, object, 0);
761 goto finish;
762 }
763 case XPATH_OP_VARIABLE: {
764 const xmlChar *prefix = op->value5;
765 const xmlChar *name = op->value4;
766
767 if (prefix != NULL)
768 fprintf(output, "VARIABLE %s:%s", prefix, name);
769 else
770 fprintf(output, "VARIABLE %s", name);
771 break;
772 }
773 case XPATH_OP_FUNCTION: {
774 int nbargs = op->value;
775 const xmlChar *prefix = op->value5;
776 const xmlChar *name = op->value4;
777
778 if (prefix != NULL)
779 fprintf(output, "FUNCTION %s:%s(%d args)",
780 prefix, name, nbargs);
781 else
782 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
783 break;
784 }
785 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
786 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
787 default:
788 fprintf(output, "UNKNOWN %d\n", op->op); return;
789 }
790 fprintf(output, "\n");
791finish:
792 if (op->ch1 >= 0)
793 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
794 if (op->ch2 >= 0)
795 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
796}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000797
798void
799xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
800 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000801 int i;
802 char shift[100];
803
804 for (i = 0;((i < depth) && (i < 25));i++)
805 shift[2 * i] = shift[2 * i + 1] = ' ';
806 shift[2 * i] = shift[2 * i + 1] = 0;
807
808 fprintf(output, shift);
809
810 if (comp == NULL) {
811 fprintf(output, "Compiled Expression is NULL\n");
812 return;
813 }
814 fprintf(output, "Compiled Expression : %d elements\n",
815 comp->nbStep);
816 i = comp->last;
817 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
818}
Owen Taylor3473f882001-02-23 17:55:21 +0000819#endif
820
821/************************************************************************
822 * *
823 * Parser stacks related functions and macros *
824 * *
825 ************************************************************************/
826
827/*
828 * Generic function for accessing stacks in the Parser Context
829 */
830
831#define PUSH_AND_POP(type, name) \
832extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
833 if (ctxt->name##Nr >= ctxt->name##Max) { \
834 ctxt->name##Max *= 2; \
835 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
836 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
837 if (ctxt->name##Tab == NULL) { \
838 xmlGenericError(xmlGenericErrorContext, \
839 "realloc failed !\n"); \
840 return(0); \
841 } \
842 } \
843 ctxt->name##Tab[ctxt->name##Nr] = value; \
844 ctxt->name = value; \
845 return(ctxt->name##Nr++); \
846} \
847extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
848 type ret; \
849 if (ctxt->name##Nr <= 0) return(0); \
850 ctxt->name##Nr--; \
851 if (ctxt->name##Nr > 0) \
852 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
853 else \
854 ctxt->name = NULL; \
855 ret = ctxt->name##Tab[ctxt->name##Nr]; \
856 ctxt->name##Tab[ctxt->name##Nr] = 0; \
857 return(ret); \
858} \
859
860PUSH_AND_POP(xmlXPathObjectPtr, value)
861
862/*
863 * Macros for accessing the content. Those should be used only by the parser,
864 * and not exported.
865 *
866 * Dirty macros, i.e. one need to make assumption on the context to use them
867 *
868 * CUR_PTR return the current pointer to the xmlChar to be parsed.
869 * CUR returns the current xmlChar value, i.e. a 8 bit value
870 * in ISO-Latin or UTF-8.
871 * This should be used internally by the parser
872 * only to compare to ASCII values otherwise it would break when
873 * running with UTF-8 encoding.
874 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
875 * to compare on ASCII based substring.
876 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
877 * strings within the parser.
878 * CURRENT Returns the current char value, with the full decoding of
879 * UTF-8 if we are using this mode. It returns an int.
880 * NEXT Skip to the next character, this does the proper decoding
881 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
882 * It returns the pointer to the current xmlChar.
883 */
884
885#define CUR (*ctxt->cur)
886#define SKIP(val) ctxt->cur += (val)
887#define NXT(val) ctxt->cur[(val)]
888#define CUR_PTR ctxt->cur
889
890#define SKIP_BLANKS \
891 while (IS_BLANK(*(ctxt->cur))) NEXT
892
893#define CURRENT (*ctxt->cur)
894#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
895
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000896
897#ifndef DBL_DIG
898#define DBL_DIG 16
899#endif
900#ifndef DBL_EPSILON
901#define DBL_EPSILON 1E-9
902#endif
903
904#define UPPER_DOUBLE 1E9
905#define LOWER_DOUBLE 1E-5
906
907#define INTEGER_DIGITS DBL_DIG
908#define FRACTION_DIGITS (DBL_DIG + 1)
909#define EXPONENT_DIGITS (3 + 2)
910
911/**
912 * xmlXPathFormatNumber:
913 * @number: number to format
914 * @buffer: output buffer
915 * @buffersize: size of output buffer
916 *
917 * Convert the number into a string representation.
918 */
919static void
920xmlXPathFormatNumber(double number, char buffer[], int buffersize)
921{
922 switch (isinf(number)) {
923 case 1:
924 if (buffersize > (int)sizeof("+Infinity"))
925 sprintf(buffer, "+Infinity");
926 break;
927 case -1:
928 if (buffersize > (int)sizeof("-Infinity"))
929 sprintf(buffer, "-Infinity");
930 break;
931 default:
932 if (isnan(number)) {
933 if (buffersize > (int)sizeof("NaN"))
934 sprintf(buffer, "NaN");
935 } else {
936 char work[INTEGER_DIGITS + FRACTION_DIGITS + EXPONENT_DIGITS + 1];
937 char *pointer;
938 char *start;
939 int i;
940 int digits;
941 int is_negative;
942 int use_scientific;
943 int exponent;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000944 int indx;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000945 int count;
946 double n;
947
948 i = digits = 0;
949 is_negative = (number < 0.0);
950 if (is_negative)
951 number = -number;
952
953 /* Scale number */
954 n = log10(number);
955 exponent = (isinf(n) == -1) ? 0 : (int)n;
956 use_scientific = (((number <= LOWER_DOUBLE) ||
957 (number > UPPER_DOUBLE)) &&
958 (number != 0));
959 if (use_scientific) {
960 number /= pow(10.0, (double)exponent);
961 while (number < 1.0) {
962 number *= 10.0;
963 exponent--;
964 }
965 }
966
967 /* Integer part is build from back */
968 pointer = &work[INTEGER_DIGITS + 1];
969 if (number < 1.0) {
970 *(--pointer) = '0';
971 digits++;
972 } else {
973 n = number;
974 for (i = 1; i < INTEGER_DIGITS - 1; i++) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000975 indx = (int)n % 10;
976 *(--pointer) = "0123456789"[indx];
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000977 n /= 10.0;
978 if (n < 1.0)
979 break;
980 }
981 digits += i;
982 }
983 if (is_negative) {
984 *(--pointer) = '-';
985 digits++;
986 }
987 start = pointer;
988
989 /* Fraction part is build from front */
990 i = 0;
991 pointer = &work[INTEGER_DIGITS + 1];
992 if (number - floor(number) > DBL_EPSILON) {
993 *(pointer++) = '.';
994 i++;
995 n = number;
996 count = 0;
997 while (i < FRACTION_DIGITS) {
998 n -= floor(n);
999 n *= 10.0;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001000 indx = (int)n % 10;
1001 *(pointer++) = "0123456789"[indx];
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001002 i++;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001003 if ((indx != 0) || (count > 0))
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001004 count++;
1005 if ((n > 10.0) || (count > FRACTION_DIGITS / 2))
1006 break;
1007 }
1008 }
1009 /* Remove trailing zeroes */
1010 while ((pointer[-1] == '0') && (i > 0)) {
1011 pointer--;
1012 i--;
1013 }
1014 digits += i;
1015
1016 if (use_scientific) {
1017 *(pointer++) = 'e';
1018 digits++;
1019 if (exponent < 0) {
1020 *(pointer++) = '-';
1021 exponent = -exponent;
1022 } else {
1023 *(pointer++) = '+';
1024 }
1025 digits++;
1026 if (exponent >= 100)
1027 pointer += 2;
1028 else if (exponent >= 10)
1029 pointer += 1;
1030 while (exponent >= 1) {
1031 *(pointer--) = "0123456789"[exponent % 10];
1032 exponent /= 10;
1033 digits++;
1034 }
1035 }
1036
1037 if (digits >= buffersize)
1038 digits = buffersize - 1;
1039
1040 memcpy(buffer, start, digits);
1041 buffer[digits] = 0;
1042 }
1043 break;
1044 }
1045}
1046
Owen Taylor3473f882001-02-23 17:55:21 +00001047/************************************************************************
1048 * *
1049 * Error handling routines *
1050 * *
1051 ************************************************************************/
1052
1053
1054const char *xmlXPathErrorMessages[] = {
1055 "Ok",
1056 "Number encoding",
1057 "Unfinished litteral",
1058 "Start of litteral",
1059 "Expected $ for variable reference",
1060 "Undefined variable",
1061 "Invalid predicate",
1062 "Invalid expression",
1063 "Missing closing curly brace",
1064 "Unregistered function",
1065 "Invalid operand",
1066 "Invalid type",
1067 "Invalid number of arguments",
1068 "Invalid context size",
1069 "Invalid context position",
1070 "Memory allocation error",
1071 "Syntax error",
1072 "Resource error",
1073 "Sub resource error",
1074 "Undefined namespace prefix"
1075};
1076
1077/**
1078 * xmlXPathError:
1079 * @ctxt: the XPath Parser context
1080 * @file: the file name
1081 * @line: the line number
1082 * @no: the error number
1083 *
1084 * Create a new xmlNodeSetPtr of type double and of value @val
1085 *
1086 * Returns the newly created object.
1087 */
1088void
1089xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1090 int line, int no) {
1091 int n;
1092 const xmlChar *cur;
1093 const xmlChar *base;
1094
1095 xmlGenericError(xmlGenericErrorContext,
1096 "Error %s:%d: %s\n", file, line,
1097 xmlXPathErrorMessages[no]);
1098
1099 cur = ctxt->cur;
1100 base = ctxt->base;
1101 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1102 cur--;
1103 }
1104 n = 0;
1105 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1106 cur--;
1107 if ((*cur == '\n') || (*cur == '\r')) cur++;
1108 base = cur;
1109 n = 0;
1110 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1111 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1112 n++;
1113 }
1114 xmlGenericError(xmlGenericErrorContext, "\n");
1115 cur = ctxt->cur;
1116 while ((*cur == '\n') || (*cur == '\r'))
1117 cur--;
1118 n = 0;
1119 while ((cur != base) && (n++ < 80)) {
1120 xmlGenericError(xmlGenericErrorContext, " ");
1121 base++;
1122 }
1123 xmlGenericError(xmlGenericErrorContext,"^\n");
1124}
1125
1126
1127/************************************************************************
1128 * *
1129 * Routines to handle NodeSets *
1130 * *
1131 ************************************************************************/
1132
1133/**
1134 * xmlXPathCmpNodes:
1135 * @node1: the first node
1136 * @node2: the second node
1137 *
1138 * Compare two nodes w.r.t document order
1139 *
1140 * Returns -2 in case of error 1 if first point < second point, 0 if
1141 * that's the same node, -1 otherwise
1142 */
1143int
1144xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1145 int depth1, depth2;
1146 xmlNodePtr cur, root;
1147
1148 if ((node1 == NULL) || (node2 == NULL))
1149 return(-2);
1150 /*
1151 * a couple of optimizations which will avoid computations in most cases
1152 */
1153 if (node1 == node2)
1154 return(0);
1155 if (node1 == node2->prev)
1156 return(1);
1157 if (node1 == node2->next)
1158 return(-1);
1159
1160 /*
1161 * compute depth to root
1162 */
1163 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1164 if (cur == node1)
1165 return(1);
1166 depth2++;
1167 }
1168 root = cur;
1169 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1170 if (cur == node2)
1171 return(-1);
1172 depth1++;
1173 }
1174 /*
1175 * Distinct document (or distinct entities :-( ) case.
1176 */
1177 if (root != cur) {
1178 return(-2);
1179 }
1180 /*
1181 * get the nearest common ancestor.
1182 */
1183 while (depth1 > depth2) {
1184 depth1--;
1185 node1 = node1->parent;
1186 }
1187 while (depth2 > depth1) {
1188 depth2--;
1189 node2 = node2->parent;
1190 }
1191 while (node1->parent != node2->parent) {
1192 node1 = node1->parent;
1193 node2 = node2->parent;
1194 /* should not happen but just in case ... */
1195 if ((node1 == NULL) || (node2 == NULL))
1196 return(-2);
1197 }
1198 /*
1199 * Find who's first.
1200 */
1201 if (node1 == node2->next)
1202 return(-1);
1203 for (cur = node1->next;cur != NULL;cur = cur->next)
1204 if (cur == node2)
1205 return(1);
1206 return(-1); /* assume there is no sibling list corruption */
1207}
1208
1209/**
1210 * xmlXPathNodeSetSort:
1211 * @set: the node set
1212 *
1213 * Sort the node set in document order
1214 */
1215void
1216xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001217 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001218 xmlNodePtr tmp;
1219
1220 if (set == NULL)
1221 return;
1222
1223 /* Use Shell's sort to sort the node-set */
1224 len = set->nodeNr;
1225 for (incr = len / 2; incr > 0; incr /= 2) {
1226 for (i = incr; i < len; i++) {
1227 j = i - incr;
1228 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001229 if (xmlXPathCmpNodes(set->nodeTab[j],
1230 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001231 tmp = set->nodeTab[j];
1232 set->nodeTab[j] = set->nodeTab[j + incr];
1233 set->nodeTab[j + incr] = tmp;
1234 j -= incr;
1235 } else
1236 break;
1237 }
1238 }
1239 }
1240}
1241
1242#define XML_NODESET_DEFAULT 10
1243/**
1244 * xmlXPathNodeSetCreate:
1245 * @val: an initial xmlNodePtr, or NULL
1246 *
1247 * Create a new xmlNodeSetPtr of type double and of value @val
1248 *
1249 * Returns the newly created object.
1250 */
1251xmlNodeSetPtr
1252xmlXPathNodeSetCreate(xmlNodePtr val) {
1253 xmlNodeSetPtr ret;
1254
1255 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1256 if (ret == NULL) {
1257 xmlGenericError(xmlGenericErrorContext,
1258 "xmlXPathNewNodeSet: out of memory\n");
1259 return(NULL);
1260 }
1261 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1262 if (val != NULL) {
1263 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1264 sizeof(xmlNodePtr));
1265 if (ret->nodeTab == NULL) {
1266 xmlGenericError(xmlGenericErrorContext,
1267 "xmlXPathNewNodeSet: out of memory\n");
1268 return(NULL);
1269 }
1270 memset(ret->nodeTab, 0 ,
1271 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1272 ret->nodeMax = XML_NODESET_DEFAULT;
1273 ret->nodeTab[ret->nodeNr++] = val;
1274 }
1275 return(ret);
1276}
1277
1278/**
1279 * xmlXPathNodeSetAdd:
1280 * @cur: the initial node set
1281 * @val: a new xmlNodePtr
1282 *
1283 * add a new xmlNodePtr ot an existing NodeSet
1284 */
1285void
1286xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1287 int i;
1288
1289 if (val == NULL) return;
1290
1291 /*
1292 * check against doublons
1293 */
1294 for (i = 0;i < cur->nodeNr;i++)
1295 if (cur->nodeTab[i] == val) return;
1296
1297 /*
1298 * grow the nodeTab if needed
1299 */
1300 if (cur->nodeMax == 0) {
1301 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1302 sizeof(xmlNodePtr));
1303 if (cur->nodeTab == NULL) {
1304 xmlGenericError(xmlGenericErrorContext,
1305 "xmlXPathNodeSetAdd: out of memory\n");
1306 return;
1307 }
1308 memset(cur->nodeTab, 0 ,
1309 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1310 cur->nodeMax = XML_NODESET_DEFAULT;
1311 } else if (cur->nodeNr == cur->nodeMax) {
1312 xmlNodePtr *temp;
1313
1314 cur->nodeMax *= 2;
1315 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1316 sizeof(xmlNodePtr));
1317 if (temp == NULL) {
1318 xmlGenericError(xmlGenericErrorContext,
1319 "xmlXPathNodeSetAdd: out of memory\n");
1320 return;
1321 }
1322 cur->nodeTab = temp;
1323 }
1324 cur->nodeTab[cur->nodeNr++] = val;
1325}
1326
1327/**
1328 * xmlXPathNodeSetAddUnique:
1329 * @cur: the initial node set
1330 * @val: a new xmlNodePtr
1331 *
1332 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1333 * when we are sure the node is not already in the set.
1334 */
1335void
1336xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1337 if (val == NULL) return;
1338
1339 /*
1340 * grow the nodeTab if needed
1341 */
1342 if (cur->nodeMax == 0) {
1343 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1344 sizeof(xmlNodePtr));
1345 if (cur->nodeTab == NULL) {
1346 xmlGenericError(xmlGenericErrorContext,
1347 "xmlXPathNodeSetAddUnique: out of memory\n");
1348 return;
1349 }
1350 memset(cur->nodeTab, 0 ,
1351 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1352 cur->nodeMax = XML_NODESET_DEFAULT;
1353 } else if (cur->nodeNr == cur->nodeMax) {
1354 xmlNodePtr *temp;
1355
1356 cur->nodeMax *= 2;
1357 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1358 sizeof(xmlNodePtr));
1359 if (temp == NULL) {
1360 xmlGenericError(xmlGenericErrorContext,
1361 "xmlXPathNodeSetAddUnique: out of memory\n");
1362 return;
1363 }
1364 cur->nodeTab = temp;
1365 }
1366 cur->nodeTab[cur->nodeNr++] = val;
1367}
1368
1369/**
1370 * xmlXPathNodeSetMerge:
1371 * @val1: the first NodeSet or NULL
1372 * @val2: the second NodeSet
1373 *
1374 * Merges two nodesets, all nodes from @val2 are added to @val1
1375 * if @val1 is NULL, a new set is created and copied from @val2
1376 *
1377 * Returns val1 once extended or NULL in case of error.
1378 */
1379xmlNodeSetPtr
1380xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
1381 int i, j, initNr;
1382
1383 if (val2 == NULL) return(val1);
1384 if (val1 == NULL) {
1385 val1 = xmlXPathNodeSetCreate(NULL);
1386 }
1387
1388 initNr = val1->nodeNr;
1389
1390 for (i = 0;i < val2->nodeNr;i++) {
1391 /*
1392 * check against doublons
1393 */
1394 for (j = 0; j < initNr; j++)
1395 if (val1->nodeTab[j] == val2->nodeTab[i]) continue;
1396
1397 /*
1398 * grow the nodeTab if needed
1399 */
1400 if (val1->nodeMax == 0) {
1401 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1402 sizeof(xmlNodePtr));
1403 if (val1->nodeTab == NULL) {
1404 xmlGenericError(xmlGenericErrorContext,
1405 "xmlXPathNodeSetMerge: out of memory\n");
1406 return(NULL);
1407 }
1408 memset(val1->nodeTab, 0 ,
1409 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1410 val1->nodeMax = XML_NODESET_DEFAULT;
1411 } else if (val1->nodeNr == val1->nodeMax) {
1412 xmlNodePtr *temp;
1413
1414 val1->nodeMax *= 2;
1415 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1416 sizeof(xmlNodePtr));
1417 if (temp == NULL) {
1418 xmlGenericError(xmlGenericErrorContext,
1419 "xmlXPathNodeSetMerge: out of memory\n");
1420 return(NULL);
1421 }
1422 val1->nodeTab = temp;
1423 }
1424 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1425 }
1426
1427 return(val1);
1428}
1429
1430/**
1431 * xmlXPathNodeSetDel:
1432 * @cur: the initial node set
1433 * @val: an xmlNodePtr
1434 *
1435 * Removes an xmlNodePtr from an existing NodeSet
1436 */
1437void
1438xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1439 int i;
1440
1441 if (cur == NULL) return;
1442 if (val == NULL) return;
1443
1444 /*
1445 * check against doublons
1446 */
1447 for (i = 0;i < cur->nodeNr;i++)
1448 if (cur->nodeTab[i] == val) break;
1449
1450 if (i >= cur->nodeNr) {
1451#ifdef DEBUG
1452 xmlGenericError(xmlGenericErrorContext,
1453 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1454 val->name);
1455#endif
1456 return;
1457 }
1458 cur->nodeNr--;
1459 for (;i < cur->nodeNr;i++)
1460 cur->nodeTab[i] = cur->nodeTab[i + 1];
1461 cur->nodeTab[cur->nodeNr] = NULL;
1462}
1463
1464/**
1465 * xmlXPathNodeSetRemove:
1466 * @cur: the initial node set
1467 * @val: the index to remove
1468 *
1469 * Removes an entry from an existing NodeSet list.
1470 */
1471void
1472xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1473 if (cur == NULL) return;
1474 if (val >= cur->nodeNr) return;
1475 cur->nodeNr--;
1476 for (;val < cur->nodeNr;val++)
1477 cur->nodeTab[val] = cur->nodeTab[val + 1];
1478 cur->nodeTab[cur->nodeNr] = NULL;
1479}
1480
1481/**
1482 * xmlXPathFreeNodeSet:
1483 * @obj: the xmlNodeSetPtr to free
1484 *
1485 * Free the NodeSet compound (not the actual nodes !).
1486 */
1487void
1488xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1489 if (obj == NULL) return;
1490 if (obj->nodeTab != NULL) {
Daniel Veillard48b2f892001-02-25 16:11:03 +00001491 MEM_CLEANUP(obj->nodeTab, (size_t) sizeof(xmlNodePtr) * obj->nodeMax);
Owen Taylor3473f882001-02-23 17:55:21 +00001492 xmlFree(obj->nodeTab);
1493 }
Daniel Veillard48b2f892001-02-25 16:11:03 +00001494 MEM_CLEANUP(obj, (size_t) sizeof(xmlNodeSet));
Owen Taylor3473f882001-02-23 17:55:21 +00001495 xmlFree(obj);
1496}
1497
1498/**
1499 * xmlXPathFreeValueTree:
1500 * @obj: the xmlNodeSetPtr to free
1501 *
1502 * Free the NodeSet compound and the actual tree, this is different
1503 * from xmlXPathFreeNodeSet()
1504 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001505static void
Owen Taylor3473f882001-02-23 17:55:21 +00001506xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1507 int i;
1508
1509 if (obj == NULL) return;
1510 for (i = 0;i < obj->nodeNr;i++)
1511 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001512 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001513
1514 if (obj->nodeTab != NULL) {
Daniel Veillard48b2f892001-02-25 16:11:03 +00001515 MEM_CLEANUP(obj->nodeTab, (size_t) sizeof(xmlNodePtr) * obj->nodeMax);
Owen Taylor3473f882001-02-23 17:55:21 +00001516 xmlFree(obj->nodeTab);
1517 }
Daniel Veillard48b2f892001-02-25 16:11:03 +00001518 MEM_CLEANUP(obj, (size_t) sizeof(xmlNodeSet));
Owen Taylor3473f882001-02-23 17:55:21 +00001519 xmlFree(obj);
1520}
1521
1522#if defined(DEBUG) || defined(DEBUG_STEP)
1523/**
1524 * xmlGenericErrorContextNodeSet:
1525 * @output: a FILE * for the output
1526 * @obj: the xmlNodeSetPtr to free
1527 *
1528 * Quick display of a NodeSet
1529 */
1530void
1531xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1532 int i;
1533
1534 if (output == NULL) output = xmlGenericErrorContext;
1535 if (obj == NULL) {
1536 fprintf(output, "NodeSet == NULL !\n");
1537 return;
1538 }
1539 if (obj->nodeNr == 0) {
1540 fprintf(output, "NodeSet is empty\n");
1541 return;
1542 }
1543 if (obj->nodeTab == NULL) {
1544 fprintf(output, " nodeTab == NULL !\n");
1545 return;
1546 }
1547 for (i = 0; i < obj->nodeNr; i++) {
1548 if (obj->nodeTab[i] == NULL) {
1549 fprintf(output, " NULL !\n");
1550 return;
1551 }
1552 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1553 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1554 fprintf(output, " /");
1555 else if (obj->nodeTab[i]->name == NULL)
1556 fprintf(output, " noname!");
1557 else fprintf(output, " %s", obj->nodeTab[i]->name);
1558 }
1559 fprintf(output, "\n");
1560}
1561#endif
1562
1563/**
1564 * xmlXPathNewNodeSet:
1565 * @val: the NodePtr value
1566 *
1567 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1568 * it with the single Node @val
1569 *
1570 * Returns the newly created object.
1571 */
1572xmlXPathObjectPtr
1573xmlXPathNewNodeSet(xmlNodePtr val) {
1574 xmlXPathObjectPtr ret;
1575
1576 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1577 if (ret == NULL) {
1578 xmlGenericError(xmlGenericErrorContext,
1579 "xmlXPathNewNodeSet: out of memory\n");
1580 return(NULL);
1581 }
1582 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1583 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001584 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001585 ret->nodesetval = xmlXPathNodeSetCreate(val);
1586 return(ret);
1587}
1588
1589/**
1590 * xmlXPathNewValueTree:
1591 * @val: the NodePtr value
1592 *
1593 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1594 * it with the tree root @val
1595 *
1596 * Returns the newly created object.
1597 */
1598xmlXPathObjectPtr
1599xmlXPathNewValueTree(xmlNodePtr val) {
1600 xmlXPathObjectPtr ret;
1601
1602 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1603 if (ret == NULL) {
1604 xmlGenericError(xmlGenericErrorContext,
1605 "xmlXPathNewNodeSet: out of memory\n");
1606 return(NULL);
1607 }
1608 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1609 ret->type = XPATH_XSLT_TREE;
1610 ret->nodesetval = xmlXPathNodeSetCreate(val);
1611 return(ret);
1612}
1613
1614/**
1615 * xmlXPathNewNodeSetList:
1616 * @val: an existing NodeSet
1617 *
1618 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1619 * it with the Nodeset @val
1620 *
1621 * Returns the newly created object.
1622 */
1623xmlXPathObjectPtr
1624xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1625 xmlXPathObjectPtr ret;
1626 int i;
1627
1628 if (val == NULL)
1629 ret = NULL;
1630 else if (val->nodeTab == NULL)
1631 ret = xmlXPathNewNodeSet(NULL);
1632 else
1633 {
1634 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1635 for (i = 1; i < val->nodeNr; ++i)
1636 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1637 }
1638
1639 return(ret);
1640}
1641
1642/**
1643 * xmlXPathWrapNodeSet:
1644 * @val: the NodePtr value
1645 *
1646 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1647 *
1648 * Returns the newly created object.
1649 */
1650xmlXPathObjectPtr
1651xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1652 xmlXPathObjectPtr ret;
1653
1654 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1655 if (ret == NULL) {
1656 xmlGenericError(xmlGenericErrorContext,
1657 "xmlXPathWrapNodeSet: out of memory\n");
1658 return(NULL);
1659 }
1660 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1661 ret->type = XPATH_NODESET;
1662 ret->nodesetval = val;
1663 return(ret);
1664}
1665
1666/**
1667 * xmlXPathFreeNodeSetList:
1668 * @obj: an existing NodeSetList object
1669 *
1670 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1671 * the list contrary to xmlXPathFreeObject().
1672 */
1673void
1674xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1675 if (obj == NULL) return;
Daniel Veillard48b2f892001-02-25 16:11:03 +00001676 MEM_CLEANUP(obj, (size_t) sizeof(xmlXPathObject));
Owen Taylor3473f882001-02-23 17:55:21 +00001677 xmlFree(obj);
1678}
1679
1680/************************************************************************
1681 * *
1682 * Routines to handle extra functions *
1683 * *
1684 ************************************************************************/
1685
1686/**
1687 * xmlXPathRegisterFunc:
1688 * @ctxt: the XPath context
1689 * @name: the function name
1690 * @f: the function implementation or NULL
1691 *
1692 * Register a new function. If @f is NULL it unregisters the function
1693 *
1694 * Returns 0 in case of success, -1 in case of error
1695 */
1696int
1697xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
1698 xmlXPathFunction f) {
1699 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
1700}
1701
1702/**
1703 * xmlXPathRegisterFuncNS:
1704 * @ctxt: the XPath context
1705 * @name: the function name
1706 * @ns_uri: the function namespace URI
1707 * @f: the function implementation or NULL
1708 *
1709 * Register a new function. If @f is NULL it unregisters the function
1710 *
1711 * Returns 0 in case of success, -1 in case of error
1712 */
1713int
1714xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1715 const xmlChar *ns_uri, xmlXPathFunction f) {
1716 if (ctxt == NULL)
1717 return(-1);
1718 if (name == NULL)
1719 return(-1);
1720
1721 if (ctxt->funcHash == NULL)
1722 ctxt->funcHash = xmlHashCreate(0);
1723 if (ctxt->funcHash == NULL)
1724 return(-1);
1725 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
1726}
1727
1728/**
1729 * xmlXPathFunctionLookup:
1730 * @ctxt: the XPath context
1731 * @name: the function name
1732 *
1733 * Search in the Function array of the context for the given
1734 * function.
1735 *
1736 * Returns the xmlXPathFunction or NULL if not found
1737 */
1738xmlXPathFunction
1739xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1740 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
1741}
1742
1743/**
1744 * xmlXPathFunctionLookupNS:
1745 * @ctxt: the XPath context
1746 * @name: the function name
1747 * @ns_uri: the function namespace URI
1748 *
1749 * Search in the Function array of the context for the given
1750 * function.
1751 *
1752 * Returns the xmlXPathFunction or NULL if not found
1753 */
1754xmlXPathFunction
1755xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1756 const xmlChar *ns_uri) {
1757 if (ctxt == NULL)
1758 return(NULL);
1759 if (ctxt->funcHash == NULL)
1760 return(NULL);
1761 if (name == NULL)
1762 return(NULL);
1763
1764 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
1765}
1766
1767/**
1768 * xmlXPathRegisteredFuncsCleanup:
1769 * @ctxt: the XPath context
1770 *
1771 * Cleanup the XPath context data associated to registered functions
1772 */
1773void
1774xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
1775 if (ctxt == NULL)
1776 return;
1777
1778 xmlHashFree(ctxt->funcHash, NULL);
1779 ctxt->funcHash = NULL;
1780}
1781
1782/************************************************************************
1783 * *
1784 * Routines to handle Variable *
1785 * *
1786 ************************************************************************/
1787
1788/**
1789 * xmlXPathRegisterVariable:
1790 * @ctxt: the XPath context
1791 * @name: the variable name
1792 * @value: the variable value or NULL
1793 *
1794 * Register a new variable value. If @value is NULL it unregisters
1795 * the variable
1796 *
1797 * Returns 0 in case of success, -1 in case of error
1798 */
1799int
1800xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
1801 xmlXPathObjectPtr value) {
1802 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
1803}
1804
1805/**
1806 * xmlXPathRegisterVariableNS:
1807 * @ctxt: the XPath context
1808 * @name: the variable name
1809 * @ns_uri: the variable namespace URI
1810 * @value: the variable value or NULL
1811 *
1812 * Register a new variable value. If @value is NULL it unregisters
1813 * the variable
1814 *
1815 * Returns 0 in case of success, -1 in case of error
1816 */
1817int
1818xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1819 const xmlChar *ns_uri,
1820 xmlXPathObjectPtr value) {
1821 if (ctxt == NULL)
1822 return(-1);
1823 if (name == NULL)
1824 return(-1);
1825
1826 if (ctxt->varHash == NULL)
1827 ctxt->varHash = xmlHashCreate(0);
1828 if (ctxt->varHash == NULL)
1829 return(-1);
1830 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
1831 (void *) value,
1832 (xmlHashDeallocator)xmlXPathFreeObject));
1833}
1834
1835/**
1836 * xmlXPathRegisterVariableLookup:
1837 * @ctxt: the XPath context
1838 * @f: the lookup function
1839 * @data: the lookup data
1840 *
1841 * register an external mechanism to do variable lookup
1842 */
1843void
1844xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
1845 xmlXPathVariableLookupFunc f, void *data) {
1846 if (ctxt == NULL)
1847 return;
1848 ctxt->varLookupFunc = (void *) f;
1849 ctxt->varLookupData = data;
1850}
1851
1852/**
1853 * xmlXPathVariableLookup:
1854 * @ctxt: the XPath context
1855 * @name: the variable name
1856 *
1857 * Search in the Variable array of the context for the given
1858 * variable value.
1859 *
1860 * Returns the value or NULL if not found
1861 */
1862xmlXPathObjectPtr
1863xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1864 if (ctxt == NULL)
1865 return(NULL);
1866
1867 if (ctxt->varLookupFunc != NULL) {
1868 xmlXPathObjectPtr ret;
1869
1870 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1871 (ctxt->varLookupData, name, NULL);
1872 if (ret != NULL) return(ret);
1873 }
1874 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
1875}
1876
1877/**
1878 * xmlXPathVariableLookupNS:
1879 * @ctxt: the XPath context
1880 * @name: the variable name
1881 * @ns_uri: the variable namespace URI
1882 *
1883 * Search in the Variable array of the context for the given
1884 * variable value.
1885 *
1886 * Returns the value or NULL if not found
1887 */
1888xmlXPathObjectPtr
1889xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1890 const xmlChar *ns_uri) {
1891 if (ctxt == NULL)
1892 return(NULL);
1893
1894 if (ctxt->varLookupFunc != NULL) {
1895 xmlXPathObjectPtr ret;
1896
1897 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1898 (ctxt->varLookupData, name, ns_uri);
1899 if (ret != NULL) return(ret);
1900 }
1901
1902 if (ctxt->varHash == NULL)
1903 return(NULL);
1904 if (name == NULL)
1905 return(NULL);
1906
1907 return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
1908}
1909
1910/**
1911 * xmlXPathRegisteredVariablesCleanup:
1912 * @ctxt: the XPath context
1913 *
1914 * Cleanup the XPath context data associated to registered variables
1915 */
1916void
1917xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
1918 if (ctxt == NULL)
1919 return;
1920
1921 xmlHashFree(ctxt->varHash, NULL);
1922 ctxt->varHash = NULL;
1923}
1924
1925/**
1926 * xmlXPathRegisterNs:
1927 * @ctxt: the XPath context
1928 * @prefix: the namespace prefix
1929 * @ns_uri: the namespace name
1930 *
1931 * Register a new namespace. If @ns_uri is NULL it unregisters
1932 * the namespace
1933 *
1934 * Returns 0 in case of success, -1 in case of error
1935 */
1936int
1937xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
1938 const xmlChar *ns_uri) {
1939 if (ctxt == NULL)
1940 return(-1);
1941 if (prefix == NULL)
1942 return(-1);
1943
1944 if (ctxt->nsHash == NULL)
1945 ctxt->nsHash = xmlHashCreate(10);
1946 if (ctxt->nsHash == NULL)
1947 return(-1);
1948 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
1949 (xmlHashDeallocator)xmlFree));
1950}
1951
1952/**
1953 * xmlXPathNsLookup:
1954 * @ctxt: the XPath context
1955 * @prefix: the namespace prefix value
1956 *
1957 * Search in the namespace declaration array of the context for the given
1958 * namespace name associated to the given prefix
1959 *
1960 * Returns the value or NULL if not found
1961 */
1962const xmlChar *
1963xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
1964 if (ctxt == NULL)
1965 return(NULL);
1966 if (prefix == NULL)
1967 return(NULL);
1968
1969#ifdef XML_XML_NAMESPACE
1970 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
1971 return(XML_XML_NAMESPACE);
1972#endif
1973
1974 if (ctxt->nsHash == NULL)
1975 return(NULL);
1976
1977 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
1978}
1979
1980/**
1981 * xmlXPathRegisteredVariablesCleanup:
1982 * @ctxt: the XPath context
1983 *
1984 * Cleanup the XPath context data associated to registered variables
1985 */
1986void
1987xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
1988 if (ctxt == NULL)
1989 return;
1990
1991 xmlHashFree(ctxt->nsHash, NULL);
1992 ctxt->nsHash = NULL;
1993}
1994
1995/************************************************************************
1996 * *
1997 * Routines to handle Values *
1998 * *
1999 ************************************************************************/
2000
2001/* Allocations are terrible, one need to optimize all this !!! */
2002
2003/**
2004 * xmlXPathNewFloat:
2005 * @val: the double value
2006 *
2007 * Create a new xmlXPathObjectPtr of type double and of value @val
2008 *
2009 * Returns the newly created object.
2010 */
2011xmlXPathObjectPtr
2012xmlXPathNewFloat(double val) {
2013 xmlXPathObjectPtr ret;
2014
2015 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2016 if (ret == NULL) {
2017 xmlGenericError(xmlGenericErrorContext,
2018 "xmlXPathNewFloat: out of memory\n");
2019 return(NULL);
2020 }
2021 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2022 ret->type = XPATH_NUMBER;
2023 ret->floatval = val;
2024 return(ret);
2025}
2026
2027/**
2028 * xmlXPathNewBoolean:
2029 * @val: the boolean value
2030 *
2031 * Create a new xmlXPathObjectPtr of type boolean and of value @val
2032 *
2033 * Returns the newly created object.
2034 */
2035xmlXPathObjectPtr
2036xmlXPathNewBoolean(int val) {
2037 xmlXPathObjectPtr ret;
2038
2039 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2040 if (ret == NULL) {
2041 xmlGenericError(xmlGenericErrorContext,
2042 "xmlXPathNewBoolean: out of memory\n");
2043 return(NULL);
2044 }
2045 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2046 ret->type = XPATH_BOOLEAN;
2047 ret->boolval = (val != 0);
2048 return(ret);
2049}
2050
2051/**
2052 * xmlXPathNewString:
2053 * @val: the xmlChar * value
2054 *
2055 * Create a new xmlXPathObjectPtr of type string and of value @val
2056 *
2057 * Returns the newly created object.
2058 */
2059xmlXPathObjectPtr
2060xmlXPathNewString(const xmlChar *val) {
2061 xmlXPathObjectPtr ret;
2062
2063 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2064 if (ret == NULL) {
2065 xmlGenericError(xmlGenericErrorContext,
2066 "xmlXPathNewString: out of memory\n");
2067 return(NULL);
2068 }
2069 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2070 ret->type = XPATH_STRING;
2071 if (val != NULL)
2072 ret->stringval = xmlStrdup(val);
2073 else
2074 ret->stringval = xmlStrdup((const xmlChar *)"");
2075 return(ret);
2076}
2077
2078/**
2079 * xmlXPathNewCString:
2080 * @val: the char * value
2081 *
2082 * Create a new xmlXPathObjectPtr of type string and of value @val
2083 *
2084 * Returns the newly created object.
2085 */
2086xmlXPathObjectPtr
2087xmlXPathNewCString(const char *val) {
2088 xmlXPathObjectPtr ret;
2089
2090 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2091 if (ret == NULL) {
2092 xmlGenericError(xmlGenericErrorContext,
2093 "xmlXPathNewCString: out of memory\n");
2094 return(NULL);
2095 }
2096 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2097 ret->type = XPATH_STRING;
2098 ret->stringval = xmlStrdup(BAD_CAST val);
2099 return(ret);
2100}
2101
2102/**
2103 * xmlXPathObjectCopy:
2104 * @val: the original object
2105 *
2106 * allocate a new copy of a given object
2107 *
2108 * Returns the newly created object.
2109 */
2110xmlXPathObjectPtr
2111xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2112 xmlXPathObjectPtr ret;
2113
2114 if (val == NULL)
2115 return(NULL);
2116
2117 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2118 if (ret == NULL) {
2119 xmlGenericError(xmlGenericErrorContext,
2120 "xmlXPathObjectCopy: out of memory\n");
2121 return(NULL);
2122 }
2123 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2124 switch (val->type) {
2125 case XPATH_BOOLEAN:
2126 case XPATH_NUMBER:
2127 case XPATH_POINT:
2128 case XPATH_RANGE:
2129 break;
2130 case XPATH_STRING:
2131 ret->stringval = xmlStrdup(val->stringval);
2132 break;
2133 case XPATH_XSLT_TREE:
2134 if ((val->nodesetval != NULL) &&
2135 (val->nodesetval->nodeTab != NULL))
2136 ret->nodesetval = xmlXPathNodeSetCreate(
2137 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
2138 else
2139 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
2140 break;
2141 case XPATH_NODESET:
2142 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
2143 break;
2144 case XPATH_LOCATIONSET:
2145#ifdef LIBXML_XPTR_ENABLED
2146 {
2147 xmlLocationSetPtr loc = val->user;
2148 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2149 break;
2150 }
2151#endif
2152 case XPATH_UNDEFINED:
2153 case XPATH_USERS:
2154 xmlGenericError(xmlGenericErrorContext,
2155 "xmlXPathObjectCopy: unsupported type %d\n",
2156 val->type);
2157 break;
2158 }
2159 return(ret);
2160}
2161
2162/**
2163 * xmlXPathFreeObject:
2164 * @obj: the object to free
2165 *
2166 * Free up an xmlXPathObjectPtr object.
2167 */
2168void
2169xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2170 if (obj == NULL) return;
2171 if (obj->type == XPATH_NODESET) {
Daniel Veillard77851712001-02-27 21:54:07 +00002172 if (obj->boolval) {
2173 obj->type = XPATH_XSLT_TREE;
2174 if (obj->nodesetval != NULL)
2175 xmlXPathFreeValueTree(obj->nodesetval);
2176 } else {
2177 if (obj->nodesetval != NULL)
2178 xmlXPathFreeNodeSet(obj->nodesetval);
2179 }
Owen Taylor3473f882001-02-23 17:55:21 +00002180#ifdef LIBXML_XPTR_ENABLED
2181 } else if (obj->type == XPATH_LOCATIONSET) {
2182 if (obj->user != NULL)
2183 xmlXPtrFreeLocationSet(obj->user);
2184#endif
2185 } else if (obj->type == XPATH_STRING) {
2186 if (obj->stringval != NULL)
2187 xmlFree(obj->stringval);
2188 } else if (obj->type == XPATH_XSLT_TREE) {
2189 if (obj->nodesetval != NULL)
2190 xmlXPathFreeValueTree(obj->nodesetval);
2191 }
2192
Daniel Veillard48b2f892001-02-25 16:11:03 +00002193 MEM_CLEANUP(obj, (size_t) sizeof(xmlXPathObject));
Owen Taylor3473f882001-02-23 17:55:21 +00002194 xmlFree(obj);
2195}
2196
2197/************************************************************************
2198 * *
2199 * Routines to handle XPath contexts *
2200 * *
2201 ************************************************************************/
2202
2203/**
2204 * xmlXPathNewContext:
2205 * @doc: the XML document
2206 *
2207 * Create a new xmlXPathContext
2208 *
2209 * Returns the xmlXPathContext just allocated.
2210 */
2211xmlXPathContextPtr
2212xmlXPathNewContext(xmlDocPtr doc) {
2213 xmlXPathContextPtr ret;
2214
2215 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
2216 if (ret == NULL) {
2217 xmlGenericError(xmlGenericErrorContext,
2218 "xmlXPathNewContext: out of memory\n");
2219 return(NULL);
2220 }
2221 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
2222 ret->doc = doc;
2223 ret->node = NULL;
2224
2225 ret->varHash = NULL;
2226
2227 ret->nb_types = 0;
2228 ret->max_types = 0;
2229 ret->types = NULL;
2230
2231 ret->funcHash = xmlHashCreate(0);
2232
2233 ret->nb_axis = 0;
2234 ret->max_axis = 0;
2235 ret->axis = NULL;
2236
2237 ret->nsHash = NULL;
2238 ret->user = NULL;
2239
2240 ret->contextSize = -1;
2241 ret->proximityPosition = -1;
2242
2243 xmlXPathRegisterAllFunctions(ret);
2244
2245 return(ret);
2246}
2247
2248/**
2249 * xmlXPathFreeContext:
2250 * @ctxt: the context to free
2251 *
2252 * Free up an xmlXPathContext
2253 */
2254void
2255xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
2256 xmlXPathRegisteredNsCleanup(ctxt);
2257 xmlXPathRegisteredFuncsCleanup(ctxt);
2258 xmlXPathRegisteredVariablesCleanup(ctxt);
Daniel Veillard48b2f892001-02-25 16:11:03 +00002259 MEM_CLEANUP(ctxt, (size_t) sizeof(xmlXPathContext));
Owen Taylor3473f882001-02-23 17:55:21 +00002260 xmlFree(ctxt);
2261}
2262
2263/************************************************************************
2264 * *
2265 * Routines to handle XPath parser contexts *
2266 * *
2267 ************************************************************************/
2268
2269#define CHECK_CTXT(ctxt) \
2270 if (ctxt == NULL) { \
2271 xmlGenericError(xmlGenericErrorContext, \
2272 "%s:%d Internal error: ctxt == NULL\n", \
2273 __FILE__, __LINE__); \
2274 } \
2275
2276
2277#define CHECK_CONTEXT(ctxt) \
2278 if (ctxt == NULL) { \
2279 xmlGenericError(xmlGenericErrorContext, \
2280 "%s:%d Internal error: no context\n", \
2281 __FILE__, __LINE__); \
2282 } \
2283 else if (ctxt->doc == NULL) { \
2284 xmlGenericError(xmlGenericErrorContext, \
2285 "%s:%d Internal error: no document\n", \
2286 __FILE__, __LINE__); \
2287 } \
2288 else if (ctxt->doc->children == NULL) { \
2289 xmlGenericError(xmlGenericErrorContext, \
2290 "%s:%d Internal error: document without root\n", \
2291 __FILE__, __LINE__); \
2292 } \
2293
2294
2295/**
2296 * xmlXPathNewParserContext:
2297 * @str: the XPath expression
2298 * @ctxt: the XPath context
2299 *
2300 * Create a new xmlXPathParserContext
2301 *
2302 * Returns the xmlXPathParserContext just allocated.
2303 */
2304xmlXPathParserContextPtr
2305xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
2306 xmlXPathParserContextPtr ret;
2307
2308 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2309 if (ret == NULL) {
2310 xmlGenericError(xmlGenericErrorContext,
2311 "xmlXPathNewParserContext: out of memory\n");
2312 return(NULL);
2313 }
2314 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2315 ret->cur = ret->base = str;
2316 ret->context = ctxt;
2317
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002318 ret->comp = xmlXPathNewCompExpr();
2319 if (ret->comp == NULL) {
2320 xmlFree(ret->valueTab);
2321 xmlFree(ret);
2322 return(NULL);
2323 }
2324
2325 return(ret);
2326}
2327
2328/**
2329 * xmlXPathCompParserContext:
2330 * @comp: the XPath compiled expression
2331 * @ctxt: the XPath context
2332 *
2333 * Create a new xmlXPathParserContext when processing a compiled expression
2334 *
2335 * Returns the xmlXPathParserContext just allocated.
2336 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002337static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002338xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
2339 xmlXPathParserContextPtr ret;
2340
2341 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2342 if (ret == NULL) {
2343 xmlGenericError(xmlGenericErrorContext,
2344 "xmlXPathNewParserContext: out of memory\n");
2345 return(NULL);
2346 }
2347 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2348
Owen Taylor3473f882001-02-23 17:55:21 +00002349 /* Allocate the value stack */
2350 ret->valueTab = (xmlXPathObjectPtr *)
2351 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002352 if (ret->valueTab == NULL) {
2353 xmlFree(ret);
2354 xmlGenericError(xmlGenericErrorContext,
2355 "xmlXPathNewParserContext: out of memory\n");
2356 return(NULL);
2357 }
Owen Taylor3473f882001-02-23 17:55:21 +00002358 ret->valueNr = 0;
2359 ret->valueMax = 10;
2360 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002361
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00002362 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002363 ret->comp = comp;
2364
Owen Taylor3473f882001-02-23 17:55:21 +00002365 return(ret);
2366}
2367
2368/**
2369 * xmlXPathFreeParserContext:
2370 * @ctxt: the context to free
2371 *
2372 * Free up an xmlXPathParserContext
2373 */
2374void
2375xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
2376 if (ctxt->valueTab != NULL) {
Daniel Veillard48b2f892001-02-25 16:11:03 +00002377 MEM_CLEANUP(ctxt->valueTab, 10 * (size_t) sizeof(xmlXPathObjectPtr));
Owen Taylor3473f882001-02-23 17:55:21 +00002378 xmlFree(ctxt->valueTab);
2379 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002380 if (ctxt->comp)
2381 xmlXPathFreeCompExpr(ctxt->comp);
Daniel Veillard48b2f892001-02-25 16:11:03 +00002382 MEM_CLEANUP(ctxt, (size_t) sizeof(xmlXPathParserContext));
Owen Taylor3473f882001-02-23 17:55:21 +00002383 xmlFree(ctxt);
2384}
2385
2386/************************************************************************
2387 * *
2388 * The implicit core function library *
2389 * *
2390 ************************************************************************/
2391
2392/*
2393 * Auto-pop and cast to a number
2394 */
2395void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
2396
2397
2398#define POP_FLOAT \
2399 arg = valuePop(ctxt); \
2400 if (arg == NULL) { \
2401 XP_ERROR(XPATH_INVALID_OPERAND); \
2402 } \
2403 if (arg->type != XPATH_NUMBER) { \
2404 valuePush(ctxt, arg); \
2405 xmlXPathNumberFunction(ctxt, 1); \
2406 arg = valuePop(ctxt); \
2407 }
2408
2409/**
2410 * xmlXPathCompareNodeSetFloat:
2411 * @ctxt: the XPath Parser context
2412 * @inf: less than (1) or greater than (0)
2413 * @strict: is the comparison strict
2414 * @arg: the node set
2415 * @f: the value
2416 *
2417 * Implement the compare operation between a nodeset and a number
2418 * @ns < @val (1, 1, ...
2419 * @ns <= @val (1, 0, ...
2420 * @ns > @val (0, 1, ...
2421 * @ns >= @val (0, 0, ...
2422 *
2423 * If one object to be compared is a node-set and the other is a number,
2424 * then the comparison will be true if and only if there is a node in the
2425 * node-set such that the result of performing the comparison on the number
2426 * to be compared and on the result of converting the string-value of that
2427 * node to a number using the number function is true.
2428 *
2429 * Returns 0 or 1 depending on the results of the test.
2430 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002431static int
Owen Taylor3473f882001-02-23 17:55:21 +00002432xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
2433 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
2434 int i, ret = 0;
2435 xmlNodeSetPtr ns;
2436 xmlChar *str2;
2437
2438 if ((f == NULL) || (arg == NULL) ||
2439 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2440 xmlXPathFreeObject(arg);
2441 xmlXPathFreeObject(f);
2442 return(0);
2443 }
2444 ns = arg->nodesetval;
2445 for (i = 0;i < ns->nodeNr;i++) {
2446 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2447 if (str2 != NULL) {
2448 valuePush(ctxt,
2449 xmlXPathNewString(str2));
2450 xmlFree(str2);
2451 xmlXPathNumberFunction(ctxt, 1);
2452 valuePush(ctxt, xmlXPathObjectCopy(f));
2453 ret = xmlXPathCompareValues(ctxt, inf, strict);
2454 if (ret)
2455 break;
2456 }
2457 }
2458 xmlXPathFreeObject(arg);
2459 xmlXPathFreeObject(f);
2460 return(ret);
2461}
2462
2463/**
2464 * xmlXPathCompareNodeSetString:
2465 * @ctxt: the XPath Parser context
2466 * @inf: less than (1) or greater than (0)
2467 * @strict: is the comparison strict
2468 * @arg: the node set
2469 * @s: the value
2470 *
2471 * Implement the compare operation between a nodeset and a string
2472 * @ns < @val (1, 1, ...
2473 * @ns <= @val (1, 0, ...
2474 * @ns > @val (0, 1, ...
2475 * @ns >= @val (0, 0, ...
2476 *
2477 * If one object to be compared is a node-set and the other is a string,
2478 * then the comparison will be true if and only if there is a node in
2479 * the node-set such that the result of performing the comparison on the
2480 * string-value of the node and the other string is true.
2481 *
2482 * Returns 0 or 1 depending on the results of the test.
2483 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002484static int
Owen Taylor3473f882001-02-23 17:55:21 +00002485xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
2486 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
2487 int i, ret = 0;
2488 xmlNodeSetPtr ns;
2489 xmlChar *str2;
2490
2491 if ((s == NULL) || (arg == NULL) ||
2492 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2493 xmlXPathFreeObject(arg);
2494 xmlXPathFreeObject(s);
2495 return(0);
2496 }
2497 ns = arg->nodesetval;
2498 for (i = 0;i < ns->nodeNr;i++) {
2499 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2500 if (str2 != NULL) {
2501 valuePush(ctxt,
2502 xmlXPathNewString(str2));
2503 xmlFree(str2);
2504 valuePush(ctxt, xmlXPathObjectCopy(s));
2505 ret = xmlXPathCompareValues(ctxt, inf, strict);
2506 if (ret)
2507 break;
2508 }
2509 }
2510 xmlXPathFreeObject(arg);
2511 xmlXPathFreeObject(s);
2512 return(ret);
2513}
2514
2515/**
2516 * xmlXPathCompareNodeSets:
Owen Taylor3473f882001-02-23 17:55:21 +00002517 * @op: less than (-1), equal (0) or greater than (1)
2518 * @strict: is the comparison strict
2519 * @arg1: the fist node set object
2520 * @arg2: the second node set object
2521 *
2522 * Implement the compare operation on nodesets:
2523 *
2524 * If both objects to be compared are node-sets, then the comparison
2525 * will be true if and only if there is a node in the first node-set
2526 * and a node in the second node-set such that the result of performing
2527 * the comparison on the string-values of the two nodes is true.
2528 * ....
2529 * When neither object to be compared is a node-set and the operator
2530 * is <=, <, >= or >, then the objects are compared by converting both
2531 * objects to numbers and comparing the numbers according to IEEE 754.
2532 * ....
2533 * The number function converts its argument to a number as follows:
2534 * - a string that consists of optional whitespace followed by an
2535 * optional minus sign followed by a Number followed by whitespace
2536 * is converted to the IEEE 754 number that is nearest (according
2537 * to the IEEE 754 round-to-nearest rule) to the mathematical value
2538 * represented by the string; any other string is converted to NaN
2539 *
2540 * Conclusion all nodes need to be converted first to their string value
2541 * and then the comparison must be done when possible
2542 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002543static int
2544xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00002545 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2546 int i, j, init = 0;
2547 double val1;
2548 double *values2;
2549 int ret = 0;
2550 xmlChar *str;
2551 xmlNodeSetPtr ns1;
2552 xmlNodeSetPtr ns2;
2553
2554 if ((arg1 == NULL) ||
2555 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2556 return(0);
2557 if ((arg2 == NULL) ||
2558 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2559 return(0);
2560
2561 ns1 = arg1->nodesetval;
2562 ns2 = arg2->nodesetval;
2563
2564 if (ns1->nodeNr <= 0)
2565 return(0);
2566 if (ns2->nodeNr <= 0)
2567 return(0);
2568
2569 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
2570 if (values2 == NULL) {
2571 return(0);
2572 }
2573 for (i = 0;i < ns1->nodeNr;i++) {
2574 str = xmlNodeGetContent(ns1->nodeTab[i]);
2575 if (str == NULL)
2576 continue;
2577 val1 = xmlXPathStringEvalNumber(str);
2578 xmlFree(str);
2579 if (isnan(val1))
2580 continue;
2581 for (j = 0;j < ns2->nodeNr;j++) {
2582 if (init == 0) {
2583 str = xmlNodeGetContent(ns2->nodeTab[j]);
2584 if (str == NULL) {
2585 values2[j] = xmlXPathNAN;
2586 } else {
2587 values2[j] = xmlXPathStringEvalNumber(str);
2588 xmlFree(str);
2589 }
2590 }
2591 if (isnan(values2[j]))
2592 continue;
2593 if (inf && strict)
2594 ret = (val1 < values2[j]);
2595 else if (inf && !strict)
2596 ret = (val1 <= values2[j]);
2597 else if (!inf && strict)
2598 ret = (val1 > values2[j]);
2599 else if (!inf && !strict)
2600 ret = (val1 >= values2[j]);
2601 if (ret)
2602 break;
2603 }
2604 if (ret)
2605 break;
2606 init = 1;
2607 }
2608 xmlFree(values2);
2609 return(ret);
2610 return(0);
2611}
2612
2613/**
2614 * xmlXPathCompareNodeSetValue:
2615 * @ctxt: the XPath Parser context
2616 * @inf: less than (1) or greater than (0)
2617 * @strict: is the comparison strict
2618 * @arg: the node set
2619 * @val: the value
2620 *
2621 * Implement the compare operation between a nodeset and a value
2622 * @ns < @val (1, 1, ...
2623 * @ns <= @val (1, 0, ...
2624 * @ns > @val (0, 1, ...
2625 * @ns >= @val (0, 0, ...
2626 *
2627 * If one object to be compared is a node-set and the other is a boolean,
2628 * then the comparison will be true if and only if the result of performing
2629 * the comparison on the boolean and on the result of converting
2630 * the node-set to a boolean using the boolean function is true.
2631 *
2632 * Returns 0 or 1 depending on the results of the test.
2633 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002634static int
Owen Taylor3473f882001-02-23 17:55:21 +00002635xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
2636 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
2637 if ((val == NULL) || (arg == NULL) ||
2638 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2639 return(0);
2640
2641 switch(val->type) {
2642 case XPATH_NUMBER:
2643 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
2644 case XPATH_NODESET:
2645 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002646 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00002647 case XPATH_STRING:
2648 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
2649 case XPATH_BOOLEAN:
2650 valuePush(ctxt, arg);
2651 xmlXPathBooleanFunction(ctxt, 1);
2652 valuePush(ctxt, val);
2653 return(xmlXPathCompareValues(ctxt, inf, strict));
2654 default:
2655 TODO
2656 return(0);
2657 }
2658 return(0);
2659}
2660
2661/**
2662 * xmlXPathEqualNodeSetString
2663 * @arg: the nodeset object argument
2664 * @str: the string to compare to.
2665 *
2666 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2667 * If one object to be compared is a node-set and the other is a string,
2668 * then the comparison will be true if and only if there is a node in
2669 * the node-set such that the result of performing the comparison on the
2670 * string-value of the node and the other string is true.
2671 *
2672 * Returns 0 or 1 depending on the results of the test.
2673 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002674static int
Owen Taylor3473f882001-02-23 17:55:21 +00002675xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
2676 int i;
2677 xmlNodeSetPtr ns;
2678 xmlChar *str2;
2679
2680 if ((str == NULL) || (arg == NULL) ||
2681 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2682 return(0);
2683 ns = arg->nodesetval;
2684 if (ns->nodeNr <= 0)
2685 return(0);
2686 for (i = 0;i < ns->nodeNr;i++) {
2687 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2688 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
2689 xmlFree(str2);
2690 return(1);
2691 }
2692 if (str2 != NULL)
2693 xmlFree(str2);
2694 }
2695 return(0);
2696}
2697
2698/**
2699 * xmlXPathEqualNodeSetFloat
2700 * @arg: the nodeset object argument
2701 * @f: the float to compare to
2702 *
2703 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2704 * If one object to be compared is a node-set and the other is a number,
2705 * then the comparison will be true if and only if there is a node in
2706 * the node-set such that the result of performing the comparison on the
2707 * number to be compared and on the result of converting the string-value
2708 * of that node to a number using the number function is true.
2709 *
2710 * Returns 0 or 1 depending on the results of the test.
2711 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002712static int
Owen Taylor3473f882001-02-23 17:55:21 +00002713xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
2714 char buf[100] = "";
2715
2716 if ((arg == NULL) ||
2717 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2718 return(0);
2719
Bjorn Reesee1dc0112001-03-03 12:09:03 +00002720 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00002721 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
2722}
2723
2724
2725/**
2726 * xmlXPathEqualNodeSets
2727 * @arg1: first nodeset object argument
2728 * @arg2: second nodeset object argument
2729 *
2730 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
2731 * If both objects to be compared are node-sets, then the comparison
2732 * will be true if and only if there is a node in the first node-set and
2733 * a node in the second node-set such that the result of performing the
2734 * comparison on the string-values of the two nodes is true.
2735 *
2736 * (needless to say, this is a costly operation)
2737 *
2738 * Returns 0 or 1 depending on the results of the test.
2739 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002740static int
Owen Taylor3473f882001-02-23 17:55:21 +00002741xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2742 int i, j;
2743 xmlChar **values1;
2744 xmlChar **values2;
2745 int ret = 0;
2746 xmlNodeSetPtr ns1;
2747 xmlNodeSetPtr ns2;
2748
2749 if ((arg1 == NULL) ||
2750 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2751 return(0);
2752 if ((arg2 == NULL) ||
2753 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2754 return(0);
2755
2756 ns1 = arg1->nodesetval;
2757 ns2 = arg2->nodesetval;
2758
2759 if (ns1->nodeNr <= 0)
2760 return(0);
2761 if (ns2->nodeNr <= 0)
2762 return(0);
2763
2764 /*
2765 * check if there is a node pertaining to both sets
2766 */
2767 for (i = 0;i < ns1->nodeNr;i++)
2768 for (j = 0;j < ns2->nodeNr;j++)
2769 if (ns1->nodeTab[i] == ns2->nodeTab[j])
2770 return(1);
2771
2772 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
2773 if (values1 == NULL)
2774 return(0);
2775 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
2776 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
2777 if (values2 == NULL) {
2778 xmlFree(values1);
2779 return(0);
2780 }
2781 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
2782 for (i = 0;i < ns1->nodeNr;i++) {
2783 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
2784 for (j = 0;j < ns2->nodeNr;j++) {
2785 if (i == 0)
2786 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
2787 ret = xmlStrEqual(values1[i], values2[j]);
2788 if (ret)
2789 break;
2790 }
2791 if (ret)
2792 break;
2793 }
2794 for (i = 0;i < ns1->nodeNr;i++)
2795 if (values1[i] != NULL)
2796 xmlFree(values1[i]);
2797 for (j = 0;j < ns2->nodeNr;j++)
2798 if (values2[j] != NULL)
2799 xmlFree(values2[j]);
2800 xmlFree(values1);
2801 xmlFree(values2);
2802 return(ret);
2803}
2804
2805/**
2806 * xmlXPathEqualValues:
2807 * @ctxt: the XPath Parser context
2808 *
2809 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2810 *
2811 * Returns 0 or 1 depending on the results of the test.
2812 */
2813int
2814xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
2815 xmlXPathObjectPtr arg1, arg2;
2816 int ret = 0;
2817
2818 arg1 = valuePop(ctxt);
2819 if (arg1 == NULL)
2820 XP_ERROR0(XPATH_INVALID_OPERAND);
2821
2822 arg2 = valuePop(ctxt);
2823 if (arg2 == NULL) {
2824 xmlXPathFreeObject(arg1);
2825 XP_ERROR0(XPATH_INVALID_OPERAND);
2826 }
2827
2828 if (arg1 == arg2) {
2829#ifdef DEBUG_EXPR
2830 xmlGenericError(xmlGenericErrorContext,
2831 "Equal: by pointer\n");
2832#endif
2833 return(1);
2834 }
2835
2836 switch (arg1->type) {
2837 case XPATH_UNDEFINED:
2838#ifdef DEBUG_EXPR
2839 xmlGenericError(xmlGenericErrorContext,
2840 "Equal: undefined\n");
2841#endif
2842 break;
2843 case XPATH_XSLT_TREE:
2844 case XPATH_NODESET:
2845 switch (arg2->type) {
2846 case XPATH_UNDEFINED:
2847#ifdef DEBUG_EXPR
2848 xmlGenericError(xmlGenericErrorContext,
2849 "Equal: undefined\n");
2850#endif
2851 break;
2852 case XPATH_XSLT_TREE:
2853 case XPATH_NODESET:
2854 ret = xmlXPathEqualNodeSets(arg1, arg2);
2855 break;
2856 case XPATH_BOOLEAN:
2857 if ((arg1->nodesetval == NULL) ||
2858 (arg1->nodesetval->nodeNr == 0)) ret = 0;
2859 else
2860 ret = 1;
2861 ret = (ret == arg2->boolval);
2862 break;
2863 case XPATH_NUMBER:
2864 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
2865 break;
2866 case XPATH_STRING:
2867 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
2868 break;
2869 case XPATH_USERS:
2870 case XPATH_POINT:
2871 case XPATH_RANGE:
2872 case XPATH_LOCATIONSET:
2873 TODO
2874 break;
2875 }
2876 break;
2877 case XPATH_BOOLEAN:
2878 switch (arg2->type) {
2879 case XPATH_UNDEFINED:
2880#ifdef DEBUG_EXPR
2881 xmlGenericError(xmlGenericErrorContext,
2882 "Equal: undefined\n");
2883#endif
2884 break;
2885 case XPATH_NODESET:
2886 case XPATH_XSLT_TREE:
2887 if ((arg2->nodesetval == NULL) ||
2888 (arg2->nodesetval->nodeNr == 0)) ret = 0;
2889 else
2890 ret = 1;
2891 break;
2892 case XPATH_BOOLEAN:
2893#ifdef DEBUG_EXPR
2894 xmlGenericError(xmlGenericErrorContext,
2895 "Equal: %d boolean %d \n",
2896 arg1->boolval, arg2->boolval);
2897#endif
2898 ret = (arg1->boolval == arg2->boolval);
2899 break;
2900 case XPATH_NUMBER:
2901 if (arg2->floatval) ret = 1;
2902 else ret = 0;
2903 ret = (arg1->boolval == ret);
2904 break;
2905 case XPATH_STRING:
2906 if ((arg2->stringval == NULL) ||
2907 (arg2->stringval[0] == 0)) ret = 0;
2908 else
2909 ret = 1;
2910 ret = (arg1->boolval == ret);
2911 break;
2912 case XPATH_USERS:
2913 case XPATH_POINT:
2914 case XPATH_RANGE:
2915 case XPATH_LOCATIONSET:
2916 TODO
2917 break;
2918 }
2919 break;
2920 case XPATH_NUMBER:
2921 switch (arg2->type) {
2922 case XPATH_UNDEFINED:
2923#ifdef DEBUG_EXPR
2924 xmlGenericError(xmlGenericErrorContext,
2925 "Equal: undefined\n");
2926#endif
2927 break;
2928 case XPATH_NODESET:
2929 case XPATH_XSLT_TREE:
2930 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
2931 break;
2932 case XPATH_BOOLEAN:
2933 if (arg1->floatval) ret = 1;
2934 else ret = 0;
2935 ret = (arg2->boolval == ret);
2936 break;
2937 case XPATH_STRING:
2938 valuePush(ctxt, arg2);
2939 xmlXPathNumberFunction(ctxt, 1);
2940 arg2 = valuePop(ctxt);
2941 /* no break on purpose */
2942 case XPATH_NUMBER:
2943 ret = (arg1->floatval == arg2->floatval);
2944 break;
2945 case XPATH_USERS:
2946 case XPATH_POINT:
2947 case XPATH_RANGE:
2948 case XPATH_LOCATIONSET:
2949 TODO
2950 break;
2951 }
2952 break;
2953 case XPATH_STRING:
2954 switch (arg2->type) {
2955 case XPATH_UNDEFINED:
2956#ifdef DEBUG_EXPR
2957 xmlGenericError(xmlGenericErrorContext,
2958 "Equal: undefined\n");
2959#endif
2960 break;
2961 case XPATH_NODESET:
2962 case XPATH_XSLT_TREE:
2963 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
2964 break;
2965 case XPATH_BOOLEAN:
2966 if ((arg1->stringval == NULL) ||
2967 (arg1->stringval[0] == 0)) ret = 0;
2968 else
2969 ret = 1;
2970 ret = (arg2->boolval == ret);
2971 break;
2972 case XPATH_STRING:
2973 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
2974 break;
2975 case XPATH_NUMBER:
2976 valuePush(ctxt, arg1);
2977 xmlXPathNumberFunction(ctxt, 1);
2978 arg1 = valuePop(ctxt);
2979 ret = (arg1->floatval == arg2->floatval);
2980 break;
2981 case XPATH_USERS:
2982 case XPATH_POINT:
2983 case XPATH_RANGE:
2984 case XPATH_LOCATIONSET:
2985 TODO
2986 break;
2987 }
2988 break;
2989 case XPATH_USERS:
2990 case XPATH_POINT:
2991 case XPATH_RANGE:
2992 case XPATH_LOCATIONSET:
2993 TODO
2994 break;
2995 }
2996 xmlXPathFreeObject(arg1);
2997 xmlXPathFreeObject(arg2);
2998 return(ret);
2999}
3000
3001
3002/**
3003 * xmlXPathCompareValues:
3004 * @ctxt: the XPath Parser context
3005 * @inf: less than (1) or greater than (0)
3006 * @strict: is the comparison strict
3007 *
3008 * Implement the compare operation on XPath objects:
3009 * @arg1 < @arg2 (1, 1, ...
3010 * @arg1 <= @arg2 (1, 0, ...
3011 * @arg1 > @arg2 (0, 1, ...
3012 * @arg1 >= @arg2 (0, 0, ...
3013 *
3014 * When neither object to be compared is a node-set and the operator is
3015 * <=, <, >=, >, then the objects are compared by converted both objects
3016 * to numbers and comparing the numbers according to IEEE 754. The <
3017 * comparison will be true if and only if the first number is less than the
3018 * second number. The <= comparison will be true if and only if the first
3019 * number is less than or equal to the second number. The > comparison
3020 * will be true if and only if the first number is greater than the second
3021 * number. The >= comparison will be true if and only if the first number
3022 * is greater than or equal to the second number.
3023 *
3024 * Returns 1 if the comparaison succeeded, 0 if it failed
3025 */
3026int
3027xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
3028 int ret = 0;
3029 xmlXPathObjectPtr arg1, arg2;
3030
3031 arg2 = valuePop(ctxt);
3032 if (arg2 == NULL) {
3033 XP_ERROR0(XPATH_INVALID_OPERAND);
3034 }
3035
3036 arg1 = valuePop(ctxt);
3037 if (arg1 == NULL) {
3038 xmlXPathFreeObject(arg2);
3039 XP_ERROR0(XPATH_INVALID_OPERAND);
3040 }
3041
3042 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
3043 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003044 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003045 } else {
3046 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003047 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
3048 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003049 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003050 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
3051 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00003052 }
3053 }
3054 return(ret);
3055 }
3056
3057 if (arg1->type != XPATH_NUMBER) {
3058 valuePush(ctxt, arg1);
3059 xmlXPathNumberFunction(ctxt, 1);
3060 arg1 = valuePop(ctxt);
3061 }
3062 if (arg1->type != XPATH_NUMBER) {
3063 xmlXPathFreeObject(arg1);
3064 xmlXPathFreeObject(arg2);
3065 XP_ERROR0(XPATH_INVALID_OPERAND);
3066 }
3067 if (arg2->type != XPATH_NUMBER) {
3068 valuePush(ctxt, arg2);
3069 xmlXPathNumberFunction(ctxt, 1);
3070 arg2 = valuePop(ctxt);
3071 }
3072 if (arg2->type != XPATH_NUMBER) {
3073 xmlXPathFreeObject(arg1);
3074 xmlXPathFreeObject(arg2);
3075 XP_ERROR0(XPATH_INVALID_OPERAND);
3076 }
3077 /*
3078 * Add tests for infinity and nan
3079 * => feedback on 3.4 for Inf and NaN
3080 */
3081 if (inf && strict)
3082 ret = (arg1->floatval < arg2->floatval);
3083 else if (inf && !strict)
3084 ret = (arg1->floatval <= arg2->floatval);
3085 else if (!inf && strict)
3086 ret = (arg1->floatval > arg2->floatval);
3087 else if (!inf && !strict)
3088 ret = (arg1->floatval >= arg2->floatval);
3089 xmlXPathFreeObject(arg1);
3090 xmlXPathFreeObject(arg2);
3091 return(ret);
3092}
3093
3094/**
3095 * xmlXPathValueFlipSign:
3096 * @ctxt: the XPath Parser context
3097 *
3098 * Implement the unary - operation on an XPath object
3099 * The numeric operators convert their operands to numbers as if
3100 * by calling the number function.
3101 */
3102void
3103xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
3104 xmlXPathObjectPtr arg;
3105
3106 POP_FLOAT
3107 arg->floatval = -arg->floatval;
3108 valuePush(ctxt, arg);
3109}
3110
3111/**
3112 * xmlXPathAddValues:
3113 * @ctxt: the XPath Parser context
3114 *
3115 * Implement the add operation on XPath objects:
3116 * The numeric operators convert their operands to numbers as if
3117 * by calling the number function.
3118 */
3119void
3120xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
3121 xmlXPathObjectPtr arg;
3122 double val;
3123
3124 POP_FLOAT
3125 val = arg->floatval;
3126 xmlXPathFreeObject(arg);
3127
3128 POP_FLOAT
3129 arg->floatval += val;
3130 valuePush(ctxt, arg);
3131}
3132
3133/**
3134 * xmlXPathSubValues:
3135 * @ctxt: the XPath Parser context
3136 *
3137 * Implement the substraction operation on XPath objects:
3138 * The numeric operators convert their operands to numbers as if
3139 * by calling the number function.
3140 */
3141void
3142xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
3143 xmlXPathObjectPtr arg;
3144 double val;
3145
3146 POP_FLOAT
3147 val = arg->floatval;
3148 xmlXPathFreeObject(arg);
3149
3150 POP_FLOAT
3151 arg->floatval -= val;
3152 valuePush(ctxt, arg);
3153}
3154
3155/**
3156 * xmlXPathMultValues:
3157 * @ctxt: the XPath Parser context
3158 *
3159 * Implement the multiply operation on XPath objects:
3160 * The numeric operators convert their operands to numbers as if
3161 * by calling the number function.
3162 */
3163void
3164xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
3165 xmlXPathObjectPtr arg;
3166 double val;
3167
3168 POP_FLOAT
3169 val = arg->floatval;
3170 xmlXPathFreeObject(arg);
3171
3172 POP_FLOAT
3173 arg->floatval *= val;
3174 valuePush(ctxt, arg);
3175}
3176
3177/**
3178 * xmlXPathDivValues:
3179 * @ctxt: the XPath Parser context
3180 *
3181 * Implement the div operation on XPath objects @arg1 / @arg2:
3182 * The numeric operators convert their operands to numbers as if
3183 * by calling the number function.
3184 */
3185void
3186xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
3187 xmlXPathObjectPtr arg;
3188 double val;
3189
3190 POP_FLOAT
3191 val = arg->floatval;
3192 xmlXPathFreeObject(arg);
3193
3194 POP_FLOAT
3195 arg->floatval /= val;
3196 valuePush(ctxt, arg);
3197}
3198
3199/**
3200 * xmlXPathModValues:
3201 * @ctxt: the XPath Parser context
3202 *
3203 * Implement the mod operation on XPath objects: @arg1 / @arg2
3204 * The numeric operators convert their operands to numbers as if
3205 * by calling the number function.
3206 */
3207void
3208xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
3209 xmlXPathObjectPtr arg;
3210 int arg1, arg2;
3211
3212 POP_FLOAT
3213 arg2 = (int) arg->floatval;
3214 xmlXPathFreeObject(arg);
3215
3216 POP_FLOAT
3217 arg1 = (int) arg->floatval;
3218 arg->floatval = arg1 % arg2;
3219 valuePush(ctxt, arg);
3220}
3221
3222/************************************************************************
3223 * *
3224 * The traversal functions *
3225 * *
3226 ************************************************************************/
3227
Owen Taylor3473f882001-02-23 17:55:21 +00003228/*
3229 * A traversal function enumerates nodes along an axis.
3230 * Initially it must be called with NULL, and it indicates
3231 * termination on the axis by returning NULL.
3232 */
3233typedef xmlNodePtr (*xmlXPathTraversalFunction)
3234 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
3235
3236/**
3237 * xmlXPathNextSelf:
3238 * @ctxt: the XPath Parser context
3239 * @cur: the current node in the traversal
3240 *
3241 * Traversal function for the "self" direction
3242 * The self axis contains just the context node itself
3243 *
3244 * Returns the next element following that axis
3245 */
3246xmlNodePtr
3247xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3248 if (cur == NULL)
3249 return(ctxt->context->node);
3250 return(NULL);
3251}
3252
3253/**
3254 * xmlXPathNextChild:
3255 * @ctxt: the XPath Parser context
3256 * @cur: the current node in the traversal
3257 *
3258 * Traversal function for the "child" direction
3259 * The child axis contains the children of the context node in document order.
3260 *
3261 * Returns the next element following that axis
3262 */
3263xmlNodePtr
3264xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3265 if (cur == NULL) {
3266 if (ctxt->context->node == NULL) return(NULL);
3267 switch (ctxt->context->node->type) {
3268 case XML_ELEMENT_NODE:
3269 case XML_TEXT_NODE:
3270 case XML_CDATA_SECTION_NODE:
3271 case XML_ENTITY_REF_NODE:
3272 case XML_ENTITY_NODE:
3273 case XML_PI_NODE:
3274 case XML_COMMENT_NODE:
3275 case XML_NOTATION_NODE:
3276 case XML_DTD_NODE:
3277 return(ctxt->context->node->children);
3278 case XML_DOCUMENT_NODE:
3279 case XML_DOCUMENT_TYPE_NODE:
3280 case XML_DOCUMENT_FRAG_NODE:
3281 case XML_HTML_DOCUMENT_NODE:
3282#ifdef LIBXML_SGML_ENABLED
3283 case XML_SGML_DOCUMENT_NODE:
3284#endif
3285 return(((xmlDocPtr) ctxt->context->node)->children);
3286 case XML_ELEMENT_DECL:
3287 case XML_ATTRIBUTE_DECL:
3288 case XML_ENTITY_DECL:
3289 case XML_ATTRIBUTE_NODE:
3290 case XML_NAMESPACE_DECL:
3291 case XML_XINCLUDE_START:
3292 case XML_XINCLUDE_END:
3293 return(NULL);
3294 }
3295 return(NULL);
3296 }
3297 if ((cur->type == XML_DOCUMENT_NODE) ||
3298 (cur->type == XML_HTML_DOCUMENT_NODE))
3299 return(NULL);
3300 return(cur->next);
3301}
3302
3303/**
3304 * xmlXPathNextDescendant:
3305 * @ctxt: the XPath Parser context
3306 * @cur: the current node in the traversal
3307 *
3308 * Traversal function for the "descendant" direction
3309 * the descendant axis contains the descendants of the context node in document
3310 * order; a descendant is a child or a child of a child and so on.
3311 *
3312 * Returns the next element following that axis
3313 */
3314xmlNodePtr
3315xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3316 if (cur == NULL) {
3317 if (ctxt->context->node == NULL)
3318 return(NULL);
3319 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3320 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3321 return(NULL);
3322
3323 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3324 return(ctxt->context->doc->children);
3325 return(ctxt->context->node->children);
3326 }
3327
3328 if (cur->children != NULL)
3329 {
3330 if (cur->children->type != XML_ENTITY_DECL)
3331 return(cur->children);
3332 }
3333 if (cur->next != NULL) return(cur->next);
3334
3335 do {
3336 cur = cur->parent;
3337 if (cur == NULL) return(NULL);
3338 if (cur == ctxt->context->node) return(NULL);
3339 if (cur->next != NULL) {
3340 cur = cur->next;
3341 return(cur);
3342 }
3343 } while (cur != NULL);
3344 return(cur);
3345}
3346
3347/**
3348 * xmlXPathNextDescendantOrSelf:
3349 * @ctxt: the XPath Parser context
3350 * @cur: the current node in the traversal
3351 *
3352 * Traversal function for the "descendant-or-self" direction
3353 * the descendant-or-self axis contains the context node and the descendants
3354 * of the context node in document order; thus the context node is the first
3355 * node on the axis, and the first child of the context node is the second node
3356 * on the axis
3357 *
3358 * Returns the next element following that axis
3359 */
3360xmlNodePtr
3361xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3362 if (cur == NULL) {
3363 if (ctxt->context->node == NULL)
3364 return(NULL);
3365 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3366 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3367 return(NULL);
3368 return(ctxt->context->node);
3369 }
3370
3371 return(xmlXPathNextDescendant(ctxt, cur));
3372}
3373
3374/**
3375 * xmlXPathNextParent:
3376 * @ctxt: the XPath Parser context
3377 * @cur: the current node in the traversal
3378 *
3379 * Traversal function for the "parent" direction
3380 * The parent axis contains the parent of the context node, if there is one.
3381 *
3382 * Returns the next element following that axis
3383 */
3384xmlNodePtr
3385xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3386 /*
3387 * the parent of an attribute or namespace node is the element
3388 * to which the attribute or namespace node is attached
3389 * Namespace handling !!!
3390 */
3391 if (cur == NULL) {
3392 if (ctxt->context->node == NULL) return(NULL);
3393 switch (ctxt->context->node->type) {
3394 case XML_ELEMENT_NODE:
3395 case XML_TEXT_NODE:
3396 case XML_CDATA_SECTION_NODE:
3397 case XML_ENTITY_REF_NODE:
3398 case XML_ENTITY_NODE:
3399 case XML_PI_NODE:
3400 case XML_COMMENT_NODE:
3401 case XML_NOTATION_NODE:
3402 case XML_DTD_NODE:
3403 case XML_ELEMENT_DECL:
3404 case XML_ATTRIBUTE_DECL:
3405 case XML_XINCLUDE_START:
3406 case XML_XINCLUDE_END:
3407 case XML_ENTITY_DECL:
3408 if (ctxt->context->node->parent == NULL)
3409 return((xmlNodePtr) ctxt->context->doc);
3410 return(ctxt->context->node->parent);
3411 case XML_ATTRIBUTE_NODE: {
3412 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3413
3414 return(att->parent);
3415 }
3416 case XML_DOCUMENT_NODE:
3417 case XML_DOCUMENT_TYPE_NODE:
3418 case XML_DOCUMENT_FRAG_NODE:
3419 case XML_HTML_DOCUMENT_NODE:
3420#ifdef LIBXML_SGML_ENABLED
3421 case XML_SGML_DOCUMENT_NODE:
3422#endif
3423 return(NULL);
3424 case XML_NAMESPACE_DECL:
3425 /*
3426 * TODO !!! may require extending struct _xmlNs with
3427 * parent field
3428 * C.f. Infoset case...
3429 */
3430 return(NULL);
3431 }
3432 }
3433 return(NULL);
3434}
3435
3436/**
3437 * xmlXPathNextAncestor:
3438 * @ctxt: the XPath Parser context
3439 * @cur: the current node in the traversal
3440 *
3441 * Traversal function for the "ancestor" direction
3442 * the ancestor axis contains the ancestors of the context node; the ancestors
3443 * of the context node consist of the parent of context node and the parent's
3444 * parent and so on; the nodes are ordered in reverse document order; thus the
3445 * parent is the first node on the axis, and the parent's parent is the second
3446 * node on the axis
3447 *
3448 * Returns the next element following that axis
3449 */
3450xmlNodePtr
3451xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3452 /*
3453 * the parent of an attribute or namespace node is the element
3454 * to which the attribute or namespace node is attached
3455 * !!!!!!!!!!!!!
3456 */
3457 if (cur == NULL) {
3458 if (ctxt->context->node == NULL) return(NULL);
3459 switch (ctxt->context->node->type) {
3460 case XML_ELEMENT_NODE:
3461 case XML_TEXT_NODE:
3462 case XML_CDATA_SECTION_NODE:
3463 case XML_ENTITY_REF_NODE:
3464 case XML_ENTITY_NODE:
3465 case XML_PI_NODE:
3466 case XML_COMMENT_NODE:
3467 case XML_DTD_NODE:
3468 case XML_ELEMENT_DECL:
3469 case XML_ATTRIBUTE_DECL:
3470 case XML_ENTITY_DECL:
3471 case XML_NOTATION_NODE:
3472 case XML_XINCLUDE_START:
3473 case XML_XINCLUDE_END:
3474 if (ctxt->context->node->parent == NULL)
3475 return((xmlNodePtr) ctxt->context->doc);
3476 return(ctxt->context->node->parent);
3477 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003478 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003479
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003480 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00003481 }
3482 case XML_DOCUMENT_NODE:
3483 case XML_DOCUMENT_TYPE_NODE:
3484 case XML_DOCUMENT_FRAG_NODE:
3485 case XML_HTML_DOCUMENT_NODE:
3486#ifdef LIBXML_SGML_ENABLED
3487 case XML_SGML_DOCUMENT_NODE:
3488#endif
3489 return(NULL);
3490 case XML_NAMESPACE_DECL:
3491 /*
3492 * TODO !!! may require extending struct _xmlNs with
3493 * parent field
3494 * C.f. Infoset case...
3495 */
3496 return(NULL);
3497 }
3498 return(NULL);
3499 }
3500 if (cur == ctxt->context->doc->children)
3501 return((xmlNodePtr) ctxt->context->doc);
3502 if (cur == (xmlNodePtr) ctxt->context->doc)
3503 return(NULL);
3504 switch (cur->type) {
3505 case XML_ELEMENT_NODE:
3506 case XML_TEXT_NODE:
3507 case XML_CDATA_SECTION_NODE:
3508 case XML_ENTITY_REF_NODE:
3509 case XML_ENTITY_NODE:
3510 case XML_PI_NODE:
3511 case XML_COMMENT_NODE:
3512 case XML_NOTATION_NODE:
3513 case XML_DTD_NODE:
3514 case XML_ELEMENT_DECL:
3515 case XML_ATTRIBUTE_DECL:
3516 case XML_ENTITY_DECL:
3517 case XML_XINCLUDE_START:
3518 case XML_XINCLUDE_END:
3519 return(cur->parent);
3520 case XML_ATTRIBUTE_NODE: {
3521 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3522
3523 return(att->parent);
3524 }
3525 case XML_DOCUMENT_NODE:
3526 case XML_DOCUMENT_TYPE_NODE:
3527 case XML_DOCUMENT_FRAG_NODE:
3528 case XML_HTML_DOCUMENT_NODE:
3529#ifdef LIBXML_SGML_ENABLED
3530 case XML_SGML_DOCUMENT_NODE:
3531#endif
3532 return(NULL);
3533 case XML_NAMESPACE_DECL:
3534 /*
3535 * TODO !!! may require extending struct _xmlNs with
3536 * parent field
3537 * C.f. Infoset case...
3538 */
3539 return(NULL);
3540 }
3541 return(NULL);
3542}
3543
3544/**
3545 * xmlXPathNextAncestorOrSelf:
3546 * @ctxt: the XPath Parser context
3547 * @cur: the current node in the traversal
3548 *
3549 * Traversal function for the "ancestor-or-self" direction
3550 * he ancestor-or-self axis contains the context node and ancestors of
3551 * the context node in reverse document order; thus the context node is
3552 * the first node on the axis, and the context node's parent the second;
3553 * parent here is defined the same as with the parent axis.
3554 *
3555 * Returns the next element following that axis
3556 */
3557xmlNodePtr
3558xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3559 if (cur == NULL)
3560 return(ctxt->context->node);
3561 return(xmlXPathNextAncestor(ctxt, cur));
3562}
3563
3564/**
3565 * xmlXPathNextFollowingSibling:
3566 * @ctxt: the XPath Parser context
3567 * @cur: the current node in the traversal
3568 *
3569 * Traversal function for the "following-sibling" direction
3570 * The following-sibling axis contains the following siblings of the context
3571 * node in document order.
3572 *
3573 * Returns the next element following that axis
3574 */
3575xmlNodePtr
3576xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3577 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3578 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3579 return(NULL);
3580 if (cur == (xmlNodePtr) ctxt->context->doc)
3581 return(NULL);
3582 if (cur == NULL)
3583 return(ctxt->context->node->next);
3584 return(cur->next);
3585}
3586
3587/**
3588 * xmlXPathNextPrecedingSibling:
3589 * @ctxt: the XPath Parser context
3590 * @cur: the current node in the traversal
3591 *
3592 * Traversal function for the "preceding-sibling" direction
3593 * The preceding-sibling axis contains the preceding siblings of the context
3594 * node in reverse document order; the first preceding sibling is first on the
3595 * axis; the sibling preceding that node is the second on the axis and so on.
3596 *
3597 * Returns the next element following that axis
3598 */
3599xmlNodePtr
3600xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3601 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3602 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3603 return(NULL);
3604 if (cur == (xmlNodePtr) ctxt->context->doc)
3605 return(NULL);
3606 if (cur == NULL)
3607 return(ctxt->context->node->prev);
3608 return(cur->prev);
3609}
3610
3611/**
3612 * xmlXPathNextFollowing:
3613 * @ctxt: the XPath Parser context
3614 * @cur: the current node in the traversal
3615 *
3616 * Traversal function for the "following" direction
3617 * The following axis contains all nodes in the same document as the context
3618 * node that are after the context node in document order, excluding any
3619 * descendants and excluding attribute nodes and namespace nodes; the nodes
3620 * are ordered in document order
3621 *
3622 * Returns the next element following that axis
3623 */
3624xmlNodePtr
3625xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3626 if (cur != NULL && cur->children != NULL)
3627 return cur->children ;
3628 if (cur == NULL) cur = ctxt->context->node;
3629 if (cur == NULL) return(NULL) ; /* ERROR */
3630 if (cur->next != NULL) return(cur->next) ;
3631 do {
3632 cur = cur->parent;
3633 if (cur == NULL) return(NULL);
3634 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3635 if (cur->next != NULL) return(cur->next);
3636 } while (cur != NULL);
3637 return(cur);
3638}
3639
3640/*
3641 * xmlXPathIsAncestor:
3642 * @ancestor: the ancestor node
3643 * @node: the current node
3644 *
3645 * Check that @ancestor is a @node's ancestor
3646 *
3647 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3648 */
3649static int
3650xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3651 if ((ancestor == NULL) || (node == NULL)) return(0);
3652 /* nodes need to be in the same document */
3653 if (ancestor->doc != node->doc) return(0);
3654 /* avoid searching if ancestor or node is the root node */
3655 if (ancestor == (xmlNodePtr) node->doc) return(1);
3656 if (node == (xmlNodePtr) ancestor->doc) return(0);
3657 while (node->parent != NULL) {
3658 if (node->parent == ancestor)
3659 return(1);
3660 node = node->parent;
3661 }
3662 return(0);
3663}
3664
3665/**
3666 * xmlXPathNextPreceding:
3667 * @ctxt: the XPath Parser context
3668 * @cur: the current node in the traversal
3669 *
3670 * Traversal function for the "preceding" direction
3671 * the preceding axis contains all nodes in the same document as the context
3672 * node that are before the context node in document order, excluding any
3673 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3674 * ordered in reverse document order
3675 *
3676 * Returns the next element following that axis
3677 */
3678xmlNodePtr
3679xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3680 if (cur == NULL)
3681 cur = ctxt->context->node ;
3682 do {
3683 if (cur->prev != NULL) {
3684 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3685 ;
3686 return(cur) ;
3687 }
3688
3689 cur = cur->parent;
3690 if (cur == NULL) return(NULL);
3691 if (cur == ctxt->context->doc->children) return(NULL);
3692 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3693 return(cur);
3694}
3695
3696/**
3697 * xmlXPathNextNamespace:
3698 * @ctxt: the XPath Parser context
3699 * @cur: the current attribute in the traversal
3700 *
3701 * Traversal function for the "namespace" direction
3702 * the namespace axis contains the namespace nodes of the context node;
3703 * the order of nodes on this axis is implementation-defined; the axis will
3704 * be empty unless the context node is an element
3705 *
3706 * Returns the next element following that axis
3707 */
3708xmlNodePtr
3709xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3710 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3711 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3712 if (ctxt->context->namespaces != NULL)
3713 xmlFree(ctxt->context->namespaces);
3714 ctxt->context->namespaces =
3715 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3716 if (ctxt->context->namespaces == NULL) return(NULL);
3717 ctxt->context->nsNr = 0;
3718 }
3719 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3720}
3721
3722/**
3723 * xmlXPathNextAttribute:
3724 * @ctxt: the XPath Parser context
3725 * @cur: the current attribute in the traversal
3726 *
3727 * Traversal function for the "attribute" direction
3728 * TODO: support DTD inherited default attributes
3729 *
3730 * Returns the next element following that axis
3731 */
3732xmlNodePtr
3733xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3734 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3735 if (cur == NULL) {
3736 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3737 return(NULL);
3738 return((xmlNodePtr)ctxt->context->node->properties);
3739 }
3740 return((xmlNodePtr)cur->next);
3741}
3742
3743/************************************************************************
3744 * *
3745 * NodeTest Functions *
3746 * *
3747 ************************************************************************/
3748
Owen Taylor3473f882001-02-23 17:55:21 +00003749#define IS_FUNCTION 200
3750
3751/**
3752 * xmlXPathNodeCollectAndTest:
3753 * @ctxt: the XPath Parser context
3754 * @axis: the XPath axis
3755 * @test: the XPath test
3756 * @type: the XPath type
3757 * @prefix: the namesapce prefix if any
3758 * @name: the name used in the search if any
3759 *
3760 * This is the function implementing a step: based on the current list
3761 * of nodes, it builds up a new list, looking at all nodes under that
3762 * axis and selecting them.
3763 *
3764 * Returns the new NodeSet resulting from the search.
3765 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003766static void
Owen Taylor3473f882001-02-23 17:55:21 +00003767xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, xmlXPathAxisVal axis,
3768 xmlXPathTestVal test, xmlXPathTypeVal type,
3769 const xmlChar *prefix, const xmlChar *name) {
3770#ifdef DEBUG_STEP
3771 int n = 0, t = 0;
3772#endif
3773 int i;
3774 xmlNodeSetPtr ret;
3775 xmlXPathTraversalFunction next = NULL;
3776 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
3777 xmlNodePtr cur = NULL;
3778 xmlXPathObjectPtr obj;
3779 xmlNodeSetPtr nodelist;
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003780 xmlNodePtr tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00003781
3782 CHECK_TYPE(XPATH_NODESET);
3783 obj = valuePop(ctxt);
3784 addNode = xmlXPathNodeSetAdd;
3785
3786#ifdef DEBUG_STEP
3787 xmlGenericError(xmlGenericErrorContext,
3788 "new step : ");
3789#endif
3790 switch (axis) {
3791 case AXIS_ANCESTOR:
3792#ifdef DEBUG_STEP
3793 xmlGenericError(xmlGenericErrorContext,
3794 "axis 'ancestors' ");
3795#endif
3796 next = xmlXPathNextAncestor; break;
3797 case AXIS_ANCESTOR_OR_SELF:
3798#ifdef DEBUG_STEP
3799 xmlGenericError(xmlGenericErrorContext,
3800 "axis 'ancestors-or-self' ");
3801#endif
3802 next = xmlXPathNextAncestorOrSelf; break;
3803 case AXIS_ATTRIBUTE:
3804#ifdef DEBUG_STEP
3805 xmlGenericError(xmlGenericErrorContext,
3806 "axis 'attributes' ");
3807#endif
3808 next = xmlXPathNextAttribute; break;
3809 break;
3810 case AXIS_CHILD:
3811#ifdef DEBUG_STEP
3812 xmlGenericError(xmlGenericErrorContext,
3813 "axis 'child' ");
3814#endif
3815 next = xmlXPathNextChild; break;
3816 case AXIS_DESCENDANT:
3817#ifdef DEBUG_STEP
3818 xmlGenericError(xmlGenericErrorContext,
3819 "axis 'descendant' ");
3820#endif
3821 next = xmlXPathNextDescendant; break;
3822 case AXIS_DESCENDANT_OR_SELF:
3823#ifdef DEBUG_STEP
3824 xmlGenericError(xmlGenericErrorContext,
3825 "axis 'descendant-or-self' ");
3826#endif
3827 next = xmlXPathNextDescendantOrSelf; break;
3828 case AXIS_FOLLOWING:
3829#ifdef DEBUG_STEP
3830 xmlGenericError(xmlGenericErrorContext,
3831 "axis 'following' ");
3832#endif
3833 next = xmlXPathNextFollowing; break;
3834 case AXIS_FOLLOWING_SIBLING:
3835#ifdef DEBUG_STEP
3836 xmlGenericError(xmlGenericErrorContext,
3837 "axis 'following-siblings' ");
3838#endif
3839 next = xmlXPathNextFollowingSibling; break;
3840 case AXIS_NAMESPACE:
3841#ifdef DEBUG_STEP
3842 xmlGenericError(xmlGenericErrorContext,
3843 "axis 'namespace' ");
3844#endif
3845 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
3846 break;
3847 case AXIS_PARENT:
3848#ifdef DEBUG_STEP
3849 xmlGenericError(xmlGenericErrorContext,
3850 "axis 'parent' ");
3851#endif
3852 next = xmlXPathNextParent; break;
3853 case AXIS_PRECEDING:
3854#ifdef DEBUG_STEP
3855 xmlGenericError(xmlGenericErrorContext,
3856 "axis 'preceding' ");
3857#endif
3858 next = xmlXPathNextPreceding; break;
3859 case AXIS_PRECEDING_SIBLING:
3860#ifdef DEBUG_STEP
3861 xmlGenericError(xmlGenericErrorContext,
3862 "axis 'preceding-sibling' ");
3863#endif
3864 next = xmlXPathNextPrecedingSibling; break;
3865 case AXIS_SELF:
3866#ifdef DEBUG_STEP
3867 xmlGenericError(xmlGenericErrorContext,
3868 "axis 'self' ");
3869#endif
3870 next = xmlXPathNextSelf; break;
3871 }
3872 if (next == NULL)
3873 return;
3874
3875 nodelist = obj->nodesetval;
3876 if ((nodelist != NULL) &&
3877 (nodelist->nodeNr <= 1))
3878 addNode = xmlXPathNodeSetAddUnique;
3879 else
3880 addNode = xmlXPathNodeSetAdd;
3881 ret = xmlXPathNodeSetCreate(NULL);
3882#ifdef DEBUG_STEP
3883 xmlGenericError(xmlGenericErrorContext,
3884 " context contains %d nodes\n",
3885 nodelist->nodeNr);
3886 switch (test) {
3887 case NODE_TEST_NODE:
3888 xmlGenericError(xmlGenericErrorContext,
3889 " searching all nodes\n");
3890 break;
3891 case NODE_TEST_NONE:
3892 xmlGenericError(xmlGenericErrorContext,
3893 " searching for none !!!\n");
3894 break;
3895 case NODE_TEST_TYPE:
3896 xmlGenericError(xmlGenericErrorContext,
3897 " searching for type %d\n", type);
3898 break;
3899 case NODE_TEST_PI:
3900 xmlGenericError(xmlGenericErrorContext,
3901 " searching for PI !!!\n");
3902 break;
3903 case NODE_TEST_ALL:
3904 xmlGenericError(xmlGenericErrorContext,
3905 " searching for *\n");
3906 break;
3907 case NODE_TEST_NS:
3908 xmlGenericError(xmlGenericErrorContext,
3909 " searching for namespace %s\n",
3910 prefix);
3911 break;
3912 case NODE_TEST_NAME:
3913 xmlGenericError(xmlGenericErrorContext,
3914 " searching for name %s\n", name);
3915 if (prefix != NULL)
3916 xmlGenericError(xmlGenericErrorContext,
3917 " with namespace %s\n",
3918 prefix);
3919 break;
3920 }
3921 xmlGenericError(xmlGenericErrorContext, "Testing : ");
3922#endif
3923 /*
3924 * 2.3 Node Tests
3925 * - For the attribute axis, the principal node type is attribute.
3926 * - For the namespace axis, the principal node type is namespace.
3927 * - For other axes, the principal node type is element.
3928 *
3929 * A node test * is true for any node of the
3930 * principal node type. For example, child::* willi
3931 * select all element children of the context node
3932 */
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003933 tmp = ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003934 for (i = 0;i < nodelist->nodeNr; i++) {
3935 ctxt->context->node = nodelist->nodeTab[i];
3936
3937 cur = NULL;
3938 do {
3939 cur = next(ctxt, cur);
3940 if (cur == NULL) break;
3941#ifdef DEBUG_STEP
3942 t++;
3943 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
3944#endif
3945 switch (test) {
3946 case NODE_TEST_NONE:
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003947 ctxt->context->node = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00003948 STRANGE
3949 return;
3950 case NODE_TEST_TYPE:
3951 if ((cur->type == type) ||
3952 ((type == NODE_TYPE_NODE) &&
3953 ((cur->type == XML_DOCUMENT_NODE) ||
3954 (cur->type == XML_HTML_DOCUMENT_NODE) ||
3955 (cur->type == XML_ELEMENT_NODE) ||
3956 (cur->type == XML_PI_NODE) ||
3957 (cur->type == XML_COMMENT_NODE) ||
3958 (cur->type == XML_CDATA_SECTION_NODE) ||
3959 (cur->type == XML_TEXT_NODE)))) {
3960#ifdef DEBUG_STEP
3961 n++;
3962#endif
3963 addNode(ret, cur);
3964 }
3965 break;
3966 case NODE_TEST_PI:
3967 if (cur->type == XML_PI_NODE) {
3968 if ((name != NULL) &&
3969 (!xmlStrEqual(name, cur->name)))
3970 break;
3971#ifdef DEBUG_STEP
3972 n++;
3973#endif
3974 addNode(ret, cur);
3975 }
3976 break;
3977 case NODE_TEST_ALL:
3978 if (axis == AXIS_ATTRIBUTE) {
3979 if (cur->type == XML_ATTRIBUTE_NODE) {
3980#ifdef DEBUG_STEP
3981 n++;
3982#endif
3983 addNode(ret, cur);
3984 }
3985 } else if (axis == AXIS_NAMESPACE) {
3986 if (cur->type == XML_NAMESPACE_DECL) {
3987#ifdef DEBUG_STEP
3988 n++;
3989#endif
3990 addNode(ret, cur);
3991 }
3992 } else {
3993 if ((cur->type == XML_ELEMENT_NODE) ||
3994 (cur->type == XML_DOCUMENT_NODE) ||
3995 (cur->type == XML_HTML_DOCUMENT_NODE)) {
3996 if (prefix == NULL) {
3997#ifdef DEBUG_STEP
3998 n++;
3999#endif
4000 addNode(ret, cur);
4001 } else if ((cur->ns != NULL) &&
4002 (xmlStrEqual(prefix,
4003 cur->ns->href))) {
4004#ifdef DEBUG_STEP
4005 n++;
4006#endif
4007 addNode(ret, cur);
4008 }
4009 }
4010 }
4011 break;
4012 case NODE_TEST_NS: {
4013 TODO;
4014 break;
4015 }
4016 case NODE_TEST_NAME:
4017 switch (cur->type) {
4018 case XML_ELEMENT_NODE:
4019 if (xmlStrEqual(name, cur->name)) {
4020 if (prefix == NULL) {
4021 if ((cur->ns == NULL) ||
4022 (cur->ns->prefix == NULL)) {
4023#ifdef DEBUG_STEP
4024 n++;
4025#endif
4026 addNode(ret, cur);
4027 }
4028 } else {
4029 if ((cur->ns != NULL) &&
4030 (xmlStrEqual(prefix,
4031 cur->ns->href))) {
4032#ifdef DEBUG_STEP
4033 n++;
4034#endif
4035 addNode(ret, cur);
4036 }
4037 }
4038 }
4039 break;
4040 case XML_ATTRIBUTE_NODE: {
4041 xmlAttrPtr attr = (xmlAttrPtr) cur;
4042 if (xmlStrEqual(name, attr->name)) {
4043 if (prefix == NULL) {
4044 if ((attr->ns == NULL) ||
4045 (attr->ns->prefix == NULL)) {
4046#ifdef DEBUG_STEP
4047 n++;
4048#endif
4049 addNode(ret, (xmlNodePtr) attr);
4050 }
4051 } else {
4052 if ((attr->ns != NULL) &&
4053 (xmlStrEqual(prefix,
4054 attr->ns->href))) {
4055#ifdef DEBUG_STEP
4056 n++;
4057#endif
4058 addNode(ret, (xmlNodePtr) attr);
4059 }
4060 }
4061 }
4062 break;
4063 }
4064 case XML_NAMESPACE_DECL: {
4065 TODO;
4066 break;
4067 }
4068 default:
4069 break;
4070 }
4071 break;
4072 }
4073 } while (cur != NULL);
4074 }
Bjorn Reesee1dc0112001-03-03 12:09:03 +00004075 ctxt->context->node = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004076#ifdef DEBUG_STEP
4077 xmlGenericError(xmlGenericErrorContext,
4078 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
4079#endif
4080 xmlXPathFreeObject(obj);
4081 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
4082}
4083
4084
4085/************************************************************************
4086 * *
4087 * Implicit tree core function library *
4088 * *
4089 ************************************************************************/
4090
4091/**
4092 * xmlXPathRoot:
4093 * @ctxt: the XPath Parser context
4094 *
4095 * Initialize the context to the root of the document
4096 */
4097void
4098xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
4099 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
4100 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4101}
4102
4103/************************************************************************
4104 * *
4105 * The explicit core function library *
4106 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
4107 * *
4108 ************************************************************************/
4109
4110
4111/**
4112 * xmlXPathLastFunction:
4113 * @ctxt: the XPath Parser context
4114 * @nargs: the number of arguments
4115 *
4116 * Implement the last() XPath function
4117 * number last()
4118 * The last function returns the number of nodes in the context node list.
4119 */
4120void
4121xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4122 CHECK_ARITY(0);
4123 if (ctxt->context->contextSize >= 0) {
4124 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
4125#ifdef DEBUG_EXPR
4126 xmlGenericError(xmlGenericErrorContext,
4127 "last() : %d\n", ctxt->context->contextSize);
4128#endif
4129 } else {
4130 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
4131 }
4132}
4133
4134/**
4135 * xmlXPathPositionFunction:
4136 * @ctxt: the XPath Parser context
4137 * @nargs: the number of arguments
4138 *
4139 * Implement the position() XPath function
4140 * number position()
4141 * The position function returns the position of the context node in the
4142 * context node list. The first position is 1, and so the last positionr
4143 * will be equal to last().
4144 */
4145void
4146xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4147 CHECK_ARITY(0);
4148 if (ctxt->context->proximityPosition >= 0) {
4149 valuePush(ctxt,
4150 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
4151#ifdef DEBUG_EXPR
4152 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
4153 ctxt->context->proximityPosition);
4154#endif
4155 } else {
4156 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
4157 }
4158}
4159
4160/**
4161 * xmlXPathCountFunction:
4162 * @ctxt: the XPath Parser context
4163 * @nargs: the number of arguments
4164 *
4165 * Implement the count() XPath function
4166 * number count(node-set)
4167 */
4168void
4169xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4170 xmlXPathObjectPtr cur;
4171
4172 CHECK_ARITY(1);
4173 if ((ctxt->value == NULL) ||
4174 ((ctxt->value->type != XPATH_NODESET) &&
4175 (ctxt->value->type != XPATH_XSLT_TREE)))
4176 XP_ERROR(XPATH_INVALID_TYPE);
4177 cur = valuePop(ctxt);
4178
4179 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
4180 xmlXPathFreeObject(cur);
4181}
4182
4183/**
4184 * xmlXPathIdFunction:
4185 * @ctxt: the XPath Parser context
4186 * @nargs: the number of arguments
4187 *
4188 * Implement the id() XPath function
4189 * node-set id(object)
4190 * The id function selects elements by their unique ID
4191 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
4192 * then the result is the union of the result of applying id to the
4193 * string value of each of the nodes in the argument node-set. When the
4194 * argument to id is of any other type, the argument is converted to a
4195 * string as if by a call to the string function; the string is split
4196 * into a whitespace-separated list of tokens (whitespace is any sequence
4197 * of characters matching the production S); the result is a node-set
4198 * containing the elements in the same document as the context node that
4199 * have a unique ID equal to any of the tokens in the list.
4200 */
4201void
4202xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4203 const xmlChar *tokens;
4204 const xmlChar *cur;
4205 xmlChar *ID;
4206 xmlAttrPtr attr;
4207 xmlNodePtr elem = NULL;
4208 xmlXPathObjectPtr ret, obj;
4209
4210 CHECK_ARITY(1);
4211 obj = valuePop(ctxt);
4212 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
4213 if (obj->type == XPATH_NODESET) {
4214 xmlXPathObjectPtr newobj;
4215 int i;
4216
4217 ret = xmlXPathNewNodeSet(NULL);
4218
4219 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
4220 valuePush(ctxt,
4221 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
4222 xmlXPathStringFunction(ctxt, 1);
4223 xmlXPathIdFunction(ctxt, 1);
4224 newobj = valuePop(ctxt);
4225 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
4226 newobj->nodesetval);
4227 xmlXPathFreeObject(newobj);
4228 }
4229
4230 xmlXPathFreeObject(obj);
4231 valuePush(ctxt, ret);
4232 return;
4233 }
4234 if (obj->type != XPATH_STRING) {
4235 valuePush(ctxt, obj);
4236 xmlXPathStringFunction(ctxt, 1);
4237 obj = valuePop(ctxt);
4238 if (obj->type != XPATH_STRING) {
4239 xmlXPathFreeObject(obj);
4240 return;
4241 }
4242 }
4243 tokens = obj->stringval;
4244
4245 ret = xmlXPathNewNodeSet(NULL);
4246 valuePush(ctxt, ret);
4247 if (tokens == NULL) {
4248 xmlXPathFreeObject(obj);
4249 return;
4250 }
4251
4252 cur = tokens;
4253
4254 while (IS_BLANK(*cur)) cur++;
4255 while (*cur != 0) {
4256 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
4257 (*cur == '.') || (*cur == '-') ||
4258 (*cur == '_') || (*cur == ':') ||
4259 (IS_COMBINING(*cur)) ||
4260 (IS_EXTENDER(*cur)))
4261 cur++;
4262
4263 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
4264
4265 ID = xmlStrndup(tokens, cur - tokens);
4266 attr = xmlGetID(ctxt->context->doc, ID);
4267 if (attr != NULL) {
4268 elem = attr->parent;
4269 xmlXPathNodeSetAdd(ret->nodesetval, elem);
4270 }
4271 if (ID != NULL)
4272 xmlFree(ID);
4273
4274 while (IS_BLANK(*cur)) cur++;
4275 tokens = cur;
4276 }
4277 xmlXPathFreeObject(obj);
4278 return;
4279}
4280
4281/**
4282 * xmlXPathLocalNameFunction:
4283 * @ctxt: the XPath Parser context
4284 * @nargs: the number of arguments
4285 *
4286 * Implement the local-name() XPath function
4287 * string local-name(node-set?)
4288 * The local-name function returns a string containing the local part
4289 * of the name of the node in the argument node-set that is first in
4290 * document order. If the node-set is empty or the first node has no
4291 * name, an empty string is returned. If the argument is omitted it
4292 * defaults to the context node.
4293 */
4294void
4295xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4296 xmlXPathObjectPtr cur;
4297
4298 if (nargs == 0) {
4299 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4300 nargs = 1;
4301 }
4302
4303 CHECK_ARITY(1);
4304 if ((ctxt->value == NULL) ||
4305 ((ctxt->value->type != XPATH_NODESET) &&
4306 (ctxt->value->type != XPATH_XSLT_TREE)))
4307 XP_ERROR(XPATH_INVALID_TYPE);
4308 cur = valuePop(ctxt);
4309
4310 if (cur->nodesetval->nodeNr == 0) {
4311 valuePush(ctxt, xmlXPathNewCString(""));
4312 } else {
4313 int i = 0; /* Should be first in document order !!!!! */
4314 switch (cur->nodesetval->nodeTab[i]->type) {
4315 case XML_ELEMENT_NODE:
4316 case XML_ATTRIBUTE_NODE:
4317 case XML_PI_NODE:
4318 valuePush(ctxt,
4319 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
4320 break;
4321 case XML_NAMESPACE_DECL:
4322 valuePush(ctxt, xmlXPathNewString(
4323 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
4324 break;
4325 default:
4326 valuePush(ctxt, xmlXPathNewCString(""));
4327 }
4328 }
4329 xmlXPathFreeObject(cur);
4330}
4331
4332/**
4333 * xmlXPathNamespaceURIFunction:
4334 * @ctxt: the XPath Parser context
4335 * @nargs: the number of arguments
4336 *
4337 * Implement the namespace-uri() XPath function
4338 * string namespace-uri(node-set?)
4339 * The namespace-uri function returns a string containing the
4340 * namespace URI of the expanded name of the node in the argument
4341 * node-set that is first in document order. If the node-set is empty,
4342 * the first node has no name, or the expanded name has no namespace
4343 * URI, an empty string is returned. If the argument is omitted it
4344 * defaults to the context node.
4345 */
4346void
4347xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4348 xmlXPathObjectPtr cur;
4349
4350 if (nargs == 0) {
4351 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4352 nargs = 1;
4353 }
4354 CHECK_ARITY(1);
4355 if ((ctxt->value == NULL) ||
4356 ((ctxt->value->type != XPATH_NODESET) &&
4357 (ctxt->value->type != XPATH_XSLT_TREE)))
4358 XP_ERROR(XPATH_INVALID_TYPE);
4359 cur = valuePop(ctxt);
4360
4361 if (cur->nodesetval->nodeNr == 0) {
4362 valuePush(ctxt, xmlXPathNewCString(""));
4363 } else {
4364 int i = 0; /* Should be first in document order !!!!! */
4365 switch (cur->nodesetval->nodeTab[i]->type) {
4366 case XML_ELEMENT_NODE:
4367 case XML_ATTRIBUTE_NODE:
4368 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4369 valuePush(ctxt, xmlXPathNewCString(""));
4370 else
4371 valuePush(ctxt, xmlXPathNewString(
4372 cur->nodesetval->nodeTab[i]->ns->href));
4373 break;
4374 default:
4375 valuePush(ctxt, xmlXPathNewCString(""));
4376 }
4377 }
4378 xmlXPathFreeObject(cur);
4379}
4380
4381/**
4382 * xmlXPathNameFunction:
4383 * @ctxt: the XPath Parser context
4384 * @nargs: the number of arguments
4385 *
4386 * Implement the name() XPath function
4387 * string name(node-set?)
4388 * The name function returns a string containing a QName representing
4389 * the name of the node in the argument node-set that is first in documenti
4390 * order. The QName must represent the name with respect to the namespace
4391 * declarations in effect on the node whose name is being represented.
4392 * Typically, this will be the form in which the name occurred in the XML
4393 * source. This need not be the case if there are namespace declarations
4394 * in effect on the node that associate multiple prefixes with the same
4395 * namespace. However, an implementation may include information about
4396 * the original prefix in its representation of nodes; in this case, an
4397 * implementation can ensure that the returned string is always the same
4398 * as the QName used in the XML source. If the argument it omitted it
4399 * defaults to the context node.
4400 * Libxml keep the original prefix so the "real qualified name" used is
4401 * returned.
4402 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004403static void
Owen Taylor3473f882001-02-23 17:55:21 +00004404xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4405 xmlXPathObjectPtr cur;
4406
4407 if (nargs == 0) {
4408 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4409 nargs = 1;
4410 }
4411
4412 CHECK_ARITY(1);
4413 if ((ctxt->value == NULL) ||
4414 ((ctxt->value->type != XPATH_NODESET) &&
4415 (ctxt->value->type != XPATH_XSLT_TREE)))
4416 XP_ERROR(XPATH_INVALID_TYPE);
4417 cur = valuePop(ctxt);
4418
4419 if (cur->nodesetval->nodeNr == 0) {
4420 valuePush(ctxt, xmlXPathNewCString(""));
4421 } else {
4422 int i = 0; /* Should be first in document order !!!!! */
4423
4424 switch (cur->nodesetval->nodeTab[i]->type) {
4425 case XML_ELEMENT_NODE:
4426 case XML_ATTRIBUTE_NODE:
4427 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4428 valuePush(ctxt, xmlXPathNewString(
4429 cur->nodesetval->nodeTab[i]->name));
4430
4431 else {
4432 char name[2000];
4433#ifdef HAVE_SNPRINTF
4434 snprintf(name, sizeof(name), "%s:%s",
4435 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4436 (char *) cur->nodesetval->nodeTab[i]->name);
4437#else
4438 sprintf(name, "%s:%s",
4439 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4440 (char *) cur->nodesetval->nodeTab[i]->name);
4441#endif
4442 name[sizeof(name) - 1] = 0;
4443 valuePush(ctxt, xmlXPathNewCString(name));
4444 }
4445 break;
4446 default:
4447 valuePush(ctxt,
4448 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4449 xmlXPathLocalNameFunction(ctxt, 1);
4450 }
4451 }
4452 xmlXPathFreeObject(cur);
4453}
4454
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004455
4456/**
4457 * xmlXPathConvertString:
4458 * @val: an XPath object
4459 *
4460 * Converts an existing object to its string() equivalent
4461 *
4462 * Returns the new object, the old one is freed (or the operation
4463 * is done directly on @val)
4464 */
4465xmlXPathObjectPtr
4466xmlXPathConvertString(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004467 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004468
4469 if (val == NULL)
4470 return(xmlXPathNewCString(""));
4471 switch (val->type) {
4472 case XPATH_UNDEFINED:
4473#ifdef DEBUG_EXPR
4474 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4475#endif
4476 ret = xmlXPathNewCString("");
4477 break;
4478 case XPATH_XSLT_TREE:
4479 case XPATH_NODESET:
4480 if (val->nodesetval == NULL)
4481 ret = xmlXPathNewCString("");
4482 else if (val->nodesetval->nodeNr == 0) {
4483 ret = xmlXPathNewCString("");
4484 } else {
4485 xmlChar *res;
4486
4487 xmlXPathNodeSetSort(val->nodesetval);
4488 res = xmlNodeGetContent(val->nodesetval->nodeTab[0]);
4489 /* TODO: avoid allocating res to free it */
4490 ret = xmlXPathNewString(res);
4491 if (res != NULL)
4492 xmlFree(res);
4493 }
4494 break;
4495 case XPATH_STRING:
4496 return(val);
4497 case XPATH_BOOLEAN:
4498 if (val->boolval) ret = xmlXPathNewCString("true");
4499 else ret = xmlXPathNewCString("false");
4500 break;
4501 case XPATH_NUMBER: {
4502 char buf[100];
4503
4504 xmlXPathFormatNumber(val->floatval, buf, sizeof(buf));
4505 ret = xmlXPathNewCString(buf);
4506 break;
4507 }
4508 case XPATH_USERS:
4509 case XPATH_POINT:
4510 case XPATH_RANGE:
4511 case XPATH_LOCATIONSET:
4512 TODO
4513 ret = xmlXPathNewCString("");
4514 break;
4515 }
4516 xmlXPathFreeObject(val);
4517 return(ret);
4518}
4519
Owen Taylor3473f882001-02-23 17:55:21 +00004520/**
4521 * xmlXPathStringFunction:
4522 * @ctxt: the XPath Parser context
4523 * @nargs: the number of arguments
4524 *
4525 * Implement the string() XPath function
4526 * string string(object?)
4527 * he string function converts an object to a string as follows:
4528 * - A node-set is converted to a string by returning the value of
4529 * the node in the node-set that is first in document order.
4530 * If the node-set is empty, an empty string is returned.
4531 * - A number is converted to a string as follows
4532 * + NaN is converted to the string NaN
4533 * + positive zero is converted to the string 0
4534 * + negative zero is converted to the string 0
4535 * + positive infinity is converted to the string Infinity
4536 * + negative infinity is converted to the string -Infinity
4537 * + if the number is an integer, the number is represented in
4538 * decimal form as a Number with no decimal point and no leading
4539 * zeros, preceded by a minus sign (-) if the number is negative
4540 * + otherwise, the number is represented in decimal form as a
4541 * Number including a decimal point with at least one digit
4542 * before the decimal point and at least one digit after the
4543 * decimal point, preceded by a minus sign (-) if the number
4544 * is negative; there must be no leading zeros before the decimal
4545 * point apart possibly from the one required digit immediatelyi
4546 * before the decimal point; beyond the one required digit
4547 * after the decimal point there must be as many, but only as
4548 * many, more digits as are needed to uniquely distinguish the
4549 * number from all other IEEE 754 numeric values.
4550 * - The boolean false value is converted to the string false.
4551 * The boolean true value is converted to the string true.
4552 *
4553 * If the argument is omitted, it defaults to a node-set with the
4554 * context node as its only member.
4555 */
4556void
4557xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4558 xmlXPathObjectPtr cur;
4559
4560 if (nargs == 0) {
4561 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4562 nargs = 1;
4563 }
4564
4565 CHECK_ARITY(1);
4566 cur = valuePop(ctxt);
4567 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004568 cur = xmlXPathConvertString(cur);
4569 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004570}
4571
4572/**
4573 * xmlXPathStringLengthFunction:
4574 * @ctxt: the XPath Parser context
4575 * @nargs: the number of arguments
4576 *
4577 * Implement the string-length() XPath function
4578 * number string-length(string?)
4579 * The string-length returns the number of characters in the string
4580 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4581 * the context node converted to a string, in other words the value
4582 * of the context node.
4583 */
4584void
4585xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4586 xmlXPathObjectPtr cur;
4587
4588 if (nargs == 0) {
4589 if (ctxt->context->node == NULL) {
4590 valuePush(ctxt, xmlXPathNewFloat(0));
4591 } else {
4592 xmlChar *content;
4593
4594 content = xmlNodeGetContent(ctxt->context->node);
4595 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content)));
4596 xmlFree(content);
4597 }
4598 return;
4599 }
4600 CHECK_ARITY(1);
4601 CAST_TO_STRING;
4602 CHECK_TYPE(XPATH_STRING);
4603 cur = valuePop(ctxt);
4604 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval)));
4605 xmlXPathFreeObject(cur);
4606}
4607
4608/**
4609 * xmlXPathConcatFunction:
4610 * @ctxt: the XPath Parser context
4611 * @nargs: the number of arguments
4612 *
4613 * Implement the concat() XPath function
4614 * string concat(string, string, string*)
4615 * The concat function returns the concatenation of its arguments.
4616 */
4617void
4618xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4619 xmlXPathObjectPtr cur, newobj;
4620 xmlChar *tmp;
4621
4622 if (nargs < 2) {
4623 CHECK_ARITY(2);
4624 }
4625
4626 CAST_TO_STRING;
4627 cur = valuePop(ctxt);
4628 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4629 xmlXPathFreeObject(cur);
4630 return;
4631 }
4632 nargs--;
4633
4634 while (nargs > 0) {
4635 CAST_TO_STRING;
4636 newobj = valuePop(ctxt);
4637 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4638 xmlXPathFreeObject(newobj);
4639 xmlXPathFreeObject(cur);
4640 XP_ERROR(XPATH_INVALID_TYPE);
4641 }
4642 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4643 newobj->stringval = cur->stringval;
4644 cur->stringval = tmp;
4645
4646 xmlXPathFreeObject(newobj);
4647 nargs--;
4648 }
4649 valuePush(ctxt, cur);
4650}
4651
4652/**
4653 * xmlXPathContainsFunction:
4654 * @ctxt: the XPath Parser context
4655 * @nargs: the number of arguments
4656 *
4657 * Implement the contains() XPath function
4658 * boolean contains(string, string)
4659 * The contains function returns true if the first argument string
4660 * contains the second argument string, and otherwise returns false.
4661 */
4662void
4663xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4664 xmlXPathObjectPtr hay, needle;
4665
4666 CHECK_ARITY(2);
4667 CAST_TO_STRING;
4668 CHECK_TYPE(XPATH_STRING);
4669 needle = valuePop(ctxt);
4670 CAST_TO_STRING;
4671 hay = valuePop(ctxt);
4672 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4673 xmlXPathFreeObject(hay);
4674 xmlXPathFreeObject(needle);
4675 XP_ERROR(XPATH_INVALID_TYPE);
4676 }
4677 if (xmlStrstr(hay->stringval, needle->stringval))
4678 valuePush(ctxt, xmlXPathNewBoolean(1));
4679 else
4680 valuePush(ctxt, xmlXPathNewBoolean(0));
4681 xmlXPathFreeObject(hay);
4682 xmlXPathFreeObject(needle);
4683}
4684
4685/**
4686 * xmlXPathStartsWithFunction:
4687 * @ctxt: the XPath Parser context
4688 * @nargs: the number of arguments
4689 *
4690 * Implement the starts-with() XPath function
4691 * boolean starts-with(string, string)
4692 * The starts-with function returns true if the first argument string
4693 * starts with the second argument string, and otherwise returns false.
4694 */
4695void
4696xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4697 xmlXPathObjectPtr hay, needle;
4698 int n;
4699
4700 CHECK_ARITY(2);
4701 CAST_TO_STRING;
4702 CHECK_TYPE(XPATH_STRING);
4703 needle = valuePop(ctxt);
4704 CAST_TO_STRING;
4705 hay = valuePop(ctxt);
4706 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4707 xmlXPathFreeObject(hay);
4708 xmlXPathFreeObject(needle);
4709 XP_ERROR(XPATH_INVALID_TYPE);
4710 }
4711 n = xmlStrlen(needle->stringval);
4712 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4713 valuePush(ctxt, xmlXPathNewBoolean(0));
4714 else
4715 valuePush(ctxt, xmlXPathNewBoolean(1));
4716 xmlXPathFreeObject(hay);
4717 xmlXPathFreeObject(needle);
4718}
4719
4720/**
4721 * xmlXPathSubstringFunction:
4722 * @ctxt: the XPath Parser context
4723 * @nargs: the number of arguments
4724 *
4725 * Implement the substring() XPath function
4726 * string substring(string, number, number?)
4727 * The substring function returns the substring of the first argument
4728 * starting at the position specified in the second argument with
4729 * length specified in the third argument. For example,
4730 * substring("12345",2,3) returns "234". If the third argument is not
4731 * specified, it returns the substring starting at the position specified
4732 * in the second argument and continuing to the end of the string. For
4733 * example, substring("12345",2) returns "2345". More precisely, each
4734 * character in the string (see [3.6 Strings]) is considered to have a
4735 * numeric position: the position of the first character is 1, the position
4736 * of the second character is 2 and so on. The returned substring contains
4737 * those characters for which the position of the character is greater than
4738 * or equal to the second argument and, if the third argument is specified,
4739 * less than the sum of the second and third arguments; the comparisons
4740 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4741 * - substring("12345", 1.5, 2.6) returns "234"
4742 * - substring("12345", 0, 3) returns "12"
4743 * - substring("12345", 0 div 0, 3) returns ""
4744 * - substring("12345", 1, 0 div 0) returns ""
4745 * - substring("12345", -42, 1 div 0) returns "12345"
4746 * - substring("12345", -1 div 0, 1 div 0) returns ""
4747 */
4748void
4749xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4750 xmlXPathObjectPtr str, start, len;
4751 double le, in;
4752 int i, l;
4753 xmlChar *ret;
4754
4755 /*
4756 * Conformance needs to be checked !!!!!
4757 */
4758 if (nargs < 2) {
4759 CHECK_ARITY(2);
4760 }
4761 if (nargs > 3) {
4762 CHECK_ARITY(3);
4763 }
4764 if (nargs == 3) {
4765 CAST_TO_NUMBER;
4766 CHECK_TYPE(XPATH_NUMBER);
4767 len = valuePop(ctxt);
4768 le = len->floatval;
4769 xmlXPathFreeObject(len);
4770 } else {
4771 le = 2000000000;
4772 }
4773 CAST_TO_NUMBER;
4774 CHECK_TYPE(XPATH_NUMBER);
4775 start = valuePop(ctxt);
4776 in = start->floatval;
4777 xmlXPathFreeObject(start);
4778 CAST_TO_STRING;
4779 CHECK_TYPE(XPATH_STRING);
4780 str = valuePop(ctxt);
4781 le += in;
4782
4783 /* integer index of the first char */
4784 i = (int) in;
4785 if (((double)i) != in) i++;
4786
4787 /* integer index of the last char */
4788 l = (int) le;
4789 if (((double)l) != le) l++;
4790
4791 /* back to a zero based len */
4792 i--;
4793 l--;
4794
4795 /* check against the string len */
4796 if (l > 1024) {
4797 l = xmlStrlen(str->stringval);
4798 }
4799 if (i < 0) {
4800 i = 0;
4801 }
4802
4803 /* number of chars to copy */
4804 l -= i;
4805
4806 ret = xmlStrsub(str->stringval, i, l);
4807 if (ret == NULL)
4808 valuePush(ctxt, xmlXPathNewCString(""));
4809 else {
4810 valuePush(ctxt, xmlXPathNewString(ret));
4811 xmlFree(ret);
4812 }
4813 xmlXPathFreeObject(str);
4814}
4815
4816/**
4817 * xmlXPathSubstringBeforeFunction:
4818 * @ctxt: the XPath Parser context
4819 * @nargs: the number of arguments
4820 *
4821 * Implement the substring-before() XPath function
4822 * string substring-before(string, string)
4823 * The substring-before function returns the substring of the first
4824 * argument string that precedes the first occurrence of the second
4825 * argument string in the first argument string, or the empty string
4826 * if the first argument string does not contain the second argument
4827 * string. For example, substring-before("1999/04/01","/") returns 1999.
4828 */
4829void
4830xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4831 xmlXPathObjectPtr str;
4832 xmlXPathObjectPtr find;
4833 xmlBufferPtr target;
4834 const xmlChar *point;
4835 int offset;
4836
4837 CHECK_ARITY(2);
4838 CAST_TO_STRING;
4839 find = valuePop(ctxt);
4840 CAST_TO_STRING;
4841 str = valuePop(ctxt);
4842
4843 target = xmlBufferCreate();
4844 if (target) {
4845 point = xmlStrstr(str->stringval, find->stringval);
4846 if (point) {
4847 offset = (int)(point - str->stringval);
4848 xmlBufferAdd(target, str->stringval, offset);
4849 }
4850 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4851 xmlBufferFree(target);
4852 }
4853
4854 xmlXPathFreeObject(str);
4855 xmlXPathFreeObject(find);
4856}
4857
4858/**
4859 * xmlXPathSubstringAfterFunction:
4860 * @ctxt: the XPath Parser context
4861 * @nargs: the number of arguments
4862 *
4863 * Implement the substring-after() XPath function
4864 * string substring-after(string, string)
4865 * The substring-after function returns the substring of the first
4866 * argument string that follows the first occurrence of the second
4867 * argument string in the first argument string, or the empty stringi
4868 * if the first argument string does not contain the second argument
4869 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4870 * and substring-after("1999/04/01","19") returns 99/04/01.
4871 */
4872void
4873xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4874 xmlXPathObjectPtr str;
4875 xmlXPathObjectPtr find;
4876 xmlBufferPtr target;
4877 const xmlChar *point;
4878 int offset;
4879
4880 CHECK_ARITY(2);
4881 CAST_TO_STRING;
4882 find = valuePop(ctxt);
4883 CAST_TO_STRING;
4884 str = valuePop(ctxt);
4885
4886 target = xmlBufferCreate();
4887 if (target) {
4888 point = xmlStrstr(str->stringval, find->stringval);
4889 if (point) {
4890 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4891 xmlBufferAdd(target, &str->stringval[offset],
4892 xmlStrlen(str->stringval) - offset);
4893 }
4894 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4895 xmlBufferFree(target);
4896 }
4897
4898 xmlXPathFreeObject(str);
4899 xmlXPathFreeObject(find);
4900}
4901
4902/**
4903 * xmlXPathNormalizeFunction:
4904 * @ctxt: the XPath Parser context
4905 * @nargs: the number of arguments
4906 *
4907 * Implement the normalize-space() XPath function
4908 * string normalize-space(string?)
4909 * The normalize-space function returns the argument string with white
4910 * space normalized by stripping leading and trailing whitespace
4911 * and replacing sequences of whitespace characters by a single
4912 * space. Whitespace characters are the same allowed by the S production
4913 * in XML. If the argument is omitted, it defaults to the context
4914 * node converted to a string, in other words the value of the context node.
4915 */
4916void
4917xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4918 xmlXPathObjectPtr obj = NULL;
4919 xmlChar *source = NULL;
4920 xmlBufferPtr target;
4921 xmlChar blank;
4922
4923 if (nargs == 0) {
4924 /* Use current context node */
4925 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4926 xmlXPathStringFunction(ctxt, 1);
4927 nargs = 1;
4928 }
4929
4930 CHECK_ARITY(1);
4931 CAST_TO_STRING;
4932 CHECK_TYPE(XPATH_STRING);
4933 obj = valuePop(ctxt);
4934 source = obj->stringval;
4935
4936 target = xmlBufferCreate();
4937 if (target && source) {
4938
4939 /* Skip leading whitespaces */
4940 while (IS_BLANK(*source))
4941 source++;
4942
4943 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4944 blank = 0;
4945 while (*source) {
4946 if (IS_BLANK(*source)) {
4947 blank = *source;
4948 } else {
4949 if (blank) {
4950 xmlBufferAdd(target, &blank, 1);
4951 blank = 0;
4952 }
4953 xmlBufferAdd(target, source, 1);
4954 }
4955 source++;
4956 }
4957
4958 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4959 xmlBufferFree(target);
4960 }
4961 xmlXPathFreeObject(obj);
4962}
4963
4964/**
4965 * xmlXPathTranslateFunction:
4966 * @ctxt: the XPath Parser context
4967 * @nargs: the number of arguments
4968 *
4969 * Implement the translate() XPath function
4970 * string translate(string, string, string)
4971 * The translate function returns the first argument string with
4972 * occurrences of characters in the second argument string replaced
4973 * by the character at the corresponding position in the third argument
4974 * string. For example, translate("bar","abc","ABC") returns the string
4975 * BAr. If there is a character in the second argument string with no
4976 * character at a corresponding position in the third argument string
4977 * (because the second argument string is longer than the third argument
4978 * string), then occurrences of that character in the first argument
4979 * string are removed. For example, translate("--aaa--","abc-","ABC")
4980 * returns "AAA". If a character occurs more than once in second
4981 * argument string, then the first occurrence determines the replacement
4982 * character. If the third argument string is longer than the second
4983 * argument string, then excess characters are ignored.
4984 */
4985void
4986xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4987 xmlXPathObjectPtr str;
4988 xmlXPathObjectPtr from;
4989 xmlXPathObjectPtr to;
4990 xmlBufferPtr target;
4991 int i, offset, max;
4992 xmlChar ch;
4993 const xmlChar *point;
4994
4995 CHECK_ARITY(3);
4996
4997 CAST_TO_STRING;
4998 to = valuePop(ctxt);
4999 CAST_TO_STRING;
5000 from = valuePop(ctxt);
5001 CAST_TO_STRING;
5002 str = valuePop(ctxt);
5003
5004 target = xmlBufferCreate();
5005 if (target) {
5006 max = xmlStrlen(to->stringval);
5007 for (i = 0; (ch = str->stringval[i]); i++) {
5008 point = xmlStrchr(from->stringval, ch);
5009 if (point) {
5010 /* Warning: This may not work with UTF-8 */
5011 offset = (int)(point - from->stringval);
5012 if (offset < max)
5013 xmlBufferAdd(target, &to->stringval[offset], 1);
5014 } else
5015 xmlBufferAdd(target, &ch, 1);
5016 }
5017 }
5018 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5019 xmlBufferFree(target);
5020 xmlXPathFreeObject(str);
5021 xmlXPathFreeObject(from);
5022 xmlXPathFreeObject(to);
5023}
5024
5025/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005026 * xmlXPathConvertBoolean:
5027 * @val: an XPath object
5028 *
5029 * Converts an existing object to its boolean() equivalent
5030 *
5031 * Returns the new object, the old one is freed (or the operation
5032 * is done directly on @val)
5033 */
5034xmlXPathObjectPtr
5035xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
5036 int res = 0;
5037
5038 if (val == NULL)
5039 return(NULL);
5040 switch (val->type) {
5041 case XPATH_NODESET:
5042 case XPATH_XSLT_TREE:
5043 if ((val->nodesetval == NULL) ||
5044 (val->nodesetval->nodeNr == 0)) res = 0;
5045 else
5046 res = 1;
5047 break;
5048 case XPATH_STRING:
5049 if ((val->stringval == NULL) ||
5050 (val->stringval[0] == 0)) res = 0;
5051 else
5052 res = 1;
5053 break;
5054 case XPATH_BOOLEAN:
5055 return(val);
5056 case XPATH_NUMBER:
5057 if (val->floatval) res = 1;
5058 break;
5059 default:
5060 STRANGE
5061 }
5062 xmlXPathFreeObject(val);
5063 return(xmlXPathNewBoolean(res));
5064}
5065
5066/**
Owen Taylor3473f882001-02-23 17:55:21 +00005067 * xmlXPathBooleanFunction:
5068 * @ctxt: the XPath Parser context
5069 * @nargs: the number of arguments
5070 *
5071 * Implement the boolean() XPath function
5072 * boolean boolean(object)
5073 * he boolean function converts its argument to a boolean as follows:
5074 * - a number is true if and only if it is neither positive or
5075 * negative zero nor NaN
5076 * - a node-set is true if and only if it is non-empty
5077 * - a string is true if and only if its length is non-zero
5078 */
5079void
5080xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5081 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00005082
5083 CHECK_ARITY(1);
5084 cur = valuePop(ctxt);
5085 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005086 cur = xmlXPathConvertBoolean(cur);
5087 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005088}
5089
5090/**
5091 * xmlXPathNotFunction:
5092 * @ctxt: the XPath Parser context
5093 * @nargs: the number of arguments
5094 *
5095 * Implement the not() XPath function
5096 * boolean not(boolean)
5097 * The not function returns true if its argument is false,
5098 * and false otherwise.
5099 */
5100void
5101xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5102 CHECK_ARITY(1);
5103 CAST_TO_BOOLEAN;
5104 CHECK_TYPE(XPATH_BOOLEAN);
5105 ctxt->value->boolval = ! ctxt->value->boolval;
5106}
5107
5108/**
5109 * xmlXPathTrueFunction:
5110 * @ctxt: the XPath Parser context
5111 * @nargs: the number of arguments
5112 *
5113 * Implement the true() XPath function
5114 * boolean true()
5115 */
5116void
5117xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5118 CHECK_ARITY(0);
5119 valuePush(ctxt, xmlXPathNewBoolean(1));
5120}
5121
5122/**
5123 * xmlXPathFalseFunction:
5124 * @ctxt: the XPath Parser context
5125 * @nargs: the number of arguments
5126 *
5127 * Implement the false() XPath function
5128 * boolean false()
5129 */
5130void
5131xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5132 CHECK_ARITY(0);
5133 valuePush(ctxt, xmlXPathNewBoolean(0));
5134}
5135
5136/**
5137 * xmlXPathLangFunction:
5138 * @ctxt: the XPath Parser context
5139 * @nargs: the number of arguments
5140 *
5141 * Implement the lang() XPath function
5142 * boolean lang(string)
5143 * The lang function returns true or false depending on whether the
5144 * language of the context node as specified by xml:lang attributes
5145 * is the same as or is a sublanguage of the language specified by
5146 * the argument string. The language of the context node is determined
5147 * by the value of the xml:lang attribute on the context node, or, if
5148 * the context node has no xml:lang attribute, by the value of the
5149 * xml:lang attribute on the nearest ancestor of the context node that
5150 * has an xml:lang attribute. If there is no such attribute, then lang
5151 * returns false. If there is such an attribute, then lang returns
5152 * true if the attribute value is equal to the argument ignoring case,
5153 * or if there is some suffix starting with - such that the attribute
5154 * value is equal to the argument ignoring that suffix of the attribute
5155 * value and ignoring case.
5156 */
5157void
5158xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5159 xmlXPathObjectPtr val;
5160 const xmlChar *theLang;
5161 const xmlChar *lang;
5162 int ret = 0;
5163 int i;
5164
5165 CHECK_ARITY(1);
5166 CAST_TO_STRING;
5167 CHECK_TYPE(XPATH_STRING);
5168 val = valuePop(ctxt);
5169 lang = val->stringval;
5170 theLang = xmlNodeGetLang(ctxt->context->node);
5171 if ((theLang != NULL) && (lang != NULL)) {
5172 for (i = 0;lang[i] != 0;i++)
5173 if (toupper(lang[i]) != toupper(theLang[i]))
5174 goto not_equal;
5175 ret = 1;
5176 }
5177not_equal:
5178 xmlXPathFreeObject(val);
5179 valuePush(ctxt, xmlXPathNewBoolean(ret));
5180}
5181
5182/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005183 * xmlXPathConvertNumber:
5184 * @val: an XPath object
5185 *
5186 * Converts an existing object to its number() equivalent
5187 *
5188 * Returns the new object, the old one is freed (or the operation
5189 * is done directly on @val)
5190 */
5191xmlXPathObjectPtr
5192xmlXPathConvertNumber(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005193 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005194 double res;
5195
5196 if (val == NULL)
5197 return(xmlXPathNewFloat(0.0));
5198 switch (val->type) {
5199 case XPATH_UNDEFINED:
5200#ifdef DEBUG_EXPR
5201 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
5202#endif
5203 ret = xmlXPathNewFloat(0.0);
5204 break;
5205 case XPATH_XSLT_TREE:
5206 case XPATH_NODESET:
5207 val = xmlXPathConvertString(val);
5208 /* no break on purpose */
5209 case XPATH_STRING:
5210 res = xmlXPathStringEvalNumber(val->stringval);
5211 ret = xmlXPathNewFloat(res);
5212 break;
5213 case XPATH_BOOLEAN:
5214 if (val->boolval) ret = xmlXPathNewFloat(1.0);
5215 else ret = xmlXPathNewFloat(0.0);
5216 break;
5217 case XPATH_NUMBER:
5218 return(val);
5219 case XPATH_USERS:
5220 case XPATH_POINT:
5221 case XPATH_RANGE:
5222 case XPATH_LOCATIONSET:
5223 TODO
5224 ret = xmlXPathNewFloat(0.0);
5225 break;
5226 }
5227 xmlXPathFreeObject(val);
5228 return(ret);
5229}
5230
5231/**
Owen Taylor3473f882001-02-23 17:55:21 +00005232 * xmlXPathNumberFunction:
5233 * @ctxt: the XPath Parser context
5234 * @nargs: the number of arguments
5235 *
5236 * Implement the number() XPath function
5237 * number number(object?)
5238 */
5239void
5240xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5241 xmlXPathObjectPtr cur;
5242 double res;
5243
5244 if (nargs == 0) {
5245 if (ctxt->context->node == NULL) {
5246 valuePush(ctxt, xmlXPathNewFloat(0.0));
5247 } else {
5248 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
5249
5250 res = xmlXPathStringEvalNumber(content);
5251 valuePush(ctxt, xmlXPathNewFloat(res));
5252 xmlFree(content);
5253 }
5254 return;
5255 }
5256
5257 CHECK_ARITY(1);
5258 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005259 cur = xmlXPathConvertNumber(cur);
5260 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005261}
5262
5263/**
5264 * xmlXPathSumFunction:
5265 * @ctxt: the XPath Parser context
5266 * @nargs: the number of arguments
5267 *
5268 * Implement the sum() XPath function
5269 * number sum(node-set)
5270 * The sum function returns the sum of the values of the nodes in
5271 * the argument node-set.
5272 */
5273void
5274xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5275 xmlXPathObjectPtr cur;
5276 int i;
5277
5278 CHECK_ARITY(1);
5279 if ((ctxt->value == NULL) ||
5280 ((ctxt->value->type != XPATH_NODESET) &&
5281 (ctxt->value->type != XPATH_XSLT_TREE)))
5282 XP_ERROR(XPATH_INVALID_TYPE);
5283 cur = valuePop(ctxt);
5284
5285 if (cur->nodesetval->nodeNr == 0) {
5286 valuePush(ctxt, xmlXPathNewFloat(0.0));
5287 } else {
5288 valuePush(ctxt,
5289 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
5290 xmlXPathNumberFunction(ctxt, 1);
5291 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
5292 valuePush(ctxt,
5293 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
5294 xmlXPathAddValues(ctxt);
5295 }
5296 }
5297 xmlXPathFreeObject(cur);
5298}
5299
5300/**
5301 * xmlXPathFloorFunction:
5302 * @ctxt: the XPath Parser context
5303 * @nargs: the number of arguments
5304 *
5305 * Implement the floor() XPath function
5306 * number floor(number)
5307 * The floor function returns the largest (closest to positive infinity)
5308 * number that is not greater than the argument and that is an integer.
5309 */
5310void
5311xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5312 CHECK_ARITY(1);
5313 CAST_TO_NUMBER;
5314 CHECK_TYPE(XPATH_NUMBER);
5315#if 0
5316 ctxt->value->floatval = floor(ctxt->value->floatval);
5317#else
5318 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
5319 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
5320#endif
5321}
5322
5323/**
5324 * xmlXPathCeilingFunction:
5325 * @ctxt: the XPath Parser context
5326 * @nargs: the number of arguments
5327 *
5328 * Implement the ceiling() XPath function
5329 * number ceiling(number)
5330 * The ceiling function returns the smallest (closest to negative infinity)
5331 * number that is not less than the argument and that is an integer.
5332 */
5333void
5334xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5335 double f;
5336
5337 CHECK_ARITY(1);
5338 CAST_TO_NUMBER;
5339 CHECK_TYPE(XPATH_NUMBER);
5340
5341#if 0
5342 ctxt->value->floatval = ceil(ctxt->value->floatval);
5343#else
5344 f = (double)((int) ctxt->value->floatval);
5345 if (f != ctxt->value->floatval)
5346 ctxt->value->floatval = f + 1;
5347#endif
5348}
5349
5350/**
5351 * xmlXPathRoundFunction:
5352 * @ctxt: the XPath Parser context
5353 * @nargs: the number of arguments
5354 *
5355 * Implement the round() XPath function
5356 * number round(number)
5357 * The round function returns the number that is closest to the
5358 * argument and that is an integer. If there are two such numbers,
5359 * then the one that is even is returned.
5360 */
5361void
5362xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5363 double f;
5364
5365 CHECK_ARITY(1);
5366 CAST_TO_NUMBER;
5367 CHECK_TYPE(XPATH_NUMBER);
5368
5369 if ((ctxt->value->floatval == xmlXPathNAN) ||
5370 (ctxt->value->floatval == xmlXPathPINF) ||
5371 (ctxt->value->floatval == xmlXPathNINF) ||
5372 (ctxt->value->floatval == 0.0))
5373 return;
5374
5375#if 0
5376 f = floor(ctxt->value->floatval);
5377#else
5378 f = (double)((int) ctxt->value->floatval);
5379#endif
5380 if (ctxt->value->floatval < f + 0.5)
5381 ctxt->value->floatval = f;
5382 else
5383 ctxt->value->floatval = f + 1;
5384}
5385
5386/************************************************************************
5387 * *
5388 * The Parser *
5389 * *
5390 ************************************************************************/
5391
5392/*
5393 * a couple of forward declarations since we use a recursive call based
5394 * implementation.
5395 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005396static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
5397static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt);
5398static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005399#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005400static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
5401#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00005402#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005403static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005404#endif
5405
5406/**
5407 * xmlXPathParseNCName:
5408 * @ctxt: the XPath Parser context
5409 *
5410 * parse an XML namespace non qualified name.
5411 *
5412 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
5413 *
5414 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
5415 * CombiningChar | Extender
5416 *
5417 * Returns the namespace name or NULL
5418 */
5419
5420xmlChar *
5421xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
5422 const xmlChar *q;
5423 xmlChar *ret = NULL;
5424
5425 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5426 q = NEXT;
5427
5428 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5429 (CUR == '.') || (CUR == '-') ||
5430 (CUR == '_') ||
5431 (IS_COMBINING(CUR)) ||
5432 (IS_EXTENDER(CUR)))
5433 NEXT;
5434
5435 ret = xmlStrndup(q, CUR_PTR - q);
5436
5437 return(ret);
5438}
5439
5440/**
5441 * xmlXPathParseQName:
5442 * @ctxt: the XPath Parser context
5443 * @prefix: a xmlChar **
5444 *
5445 * parse an XML qualified name
5446 *
5447 * [NS 5] QName ::= (Prefix ':')? LocalPart
5448 *
5449 * [NS 6] Prefix ::= NCName
5450 *
5451 * [NS 7] LocalPart ::= NCName
5452 *
5453 * Returns the function returns the local part, and prefix is updated
5454 * to get the Prefix if any.
5455 */
5456
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005457static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005458xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
5459 xmlChar *ret = NULL;
5460
5461 *prefix = NULL;
5462 ret = xmlXPathParseNCName(ctxt);
5463 if (CUR == ':') {
5464 *prefix = ret;
5465 NEXT;
5466 ret = xmlXPathParseNCName(ctxt);
5467 }
5468 return(ret);
5469}
5470
5471/**
5472 * xmlXPathParseName:
5473 * @ctxt: the XPath Parser context
5474 *
5475 * parse an XML name
5476 *
5477 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5478 * CombiningChar | Extender
5479 *
5480 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5481 *
5482 * Returns the namespace name or NULL
5483 */
5484
5485xmlChar *
5486xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
5487 const xmlChar *q;
5488 xmlChar *ret = NULL;
5489
5490 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5491 q = NEXT;
5492
5493 /* TODO Make this UTF8 compliant !!! */
5494 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5495 (CUR == '.') || (CUR == '-') ||
5496 (CUR == '_') || (CUR == ':') ||
5497 (IS_COMBINING(CUR)) ||
5498 (IS_EXTENDER(CUR)))
5499 NEXT;
5500
5501 ret = xmlStrndup(q, CUR_PTR - q);
5502
5503 return(ret);
5504}
5505
5506/**
5507 * xmlXPathStringEvalNumber:
5508 * @str: A string to scan
5509 *
5510 * [30] Number ::= Digits ('.' Digits?)?
5511 * | '.' Digits
5512 * [31] Digits ::= [0-9]+
5513 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005514 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00005515 * In complement of the Number expression, this function also handles
5516 * negative values : '-' Number.
5517 *
5518 * Returns the double value.
5519 */
5520double
5521xmlXPathStringEvalNumber(const xmlChar *str) {
5522 const xmlChar *cur = str;
5523 double ret = 0.0;
5524 double mult = 1;
5525 int ok = 0;
5526 int isneg = 0;
5527
5528 while (IS_BLANK(*cur)) cur++;
5529 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5530 return(xmlXPathNAN);
5531 }
5532 if (*cur == '-') {
5533 isneg = 1;
5534 cur++;
5535 }
5536 while ((*cur >= '0') && (*cur <= '9')) {
5537 ret = ret * 10 + (*cur - '0');
5538 ok = 1;
5539 cur++;
5540 }
5541 if (*cur == '.') {
5542 cur++;
5543 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5544 return(xmlXPathNAN);
5545 }
5546 while ((*cur >= '0') && (*cur <= '9')) {
5547 mult /= 10;
5548 ret = ret + (*cur - '0') * mult;
5549 cur++;
5550 }
5551 }
5552 while (IS_BLANK(*cur)) cur++;
5553 if (*cur != 0) return(xmlXPathNAN);
5554 if (isneg) ret = -ret;
5555 return(ret);
5556}
5557
5558/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005559 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00005560 * @ctxt: the XPath Parser context
5561 *
5562 * [30] Number ::= Digits ('.' Digits?)?
5563 * | '.' Digits
5564 * [31] Digits ::= [0-9]+
5565 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005566 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00005567 *
5568 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005569static void
5570xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005571 double ret = 0.0;
5572 double mult = 1;
5573 int ok = 0;
5574
5575 CHECK_ERROR;
5576 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5577 XP_ERROR(XPATH_NUMBER_ERROR);
5578 }
5579 while ((CUR >= '0') && (CUR <= '9')) {
5580 ret = ret * 10 + (CUR - '0');
5581 ok = 1;
5582 NEXT;
5583 }
5584 if (CUR == '.') {
5585 NEXT;
5586 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5587 XP_ERROR(XPATH_NUMBER_ERROR);
5588 }
5589 while ((CUR >= '0') && (CUR <= '9')) {
5590 mult /= 10;
5591 ret = ret + (CUR - '0') * mult;
5592 NEXT;
5593 }
5594 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005595 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
5596 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005597}
5598
5599/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005600 * xmlXPathParseLiteral:
5601 * @ctxt: the XPath Parser context
5602 *
5603 * Parse a Literal
5604 *
5605 * [29] Literal ::= '"' [^"]* '"'
5606 * | "'" [^']* "'"
5607 *
5608 * Returns the value found or NULL in case of error
5609 */
5610static xmlChar *
5611xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
5612 const xmlChar *q;
5613 xmlChar *ret = NULL;
5614
5615 if (CUR == '"') {
5616 NEXT;
5617 q = CUR_PTR;
5618 while ((IS_CHAR(CUR)) && (CUR != '"'))
5619 NEXT;
5620 if (!IS_CHAR(CUR)) {
5621 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5622 } else {
5623 ret = xmlStrndup(q, CUR_PTR - q);
5624 NEXT;
5625 }
5626 } else if (CUR == '\'') {
5627 NEXT;
5628 q = CUR_PTR;
5629 while ((IS_CHAR(CUR)) && (CUR != '\''))
5630 NEXT;
5631 if (!IS_CHAR(CUR)) {
5632 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5633 } else {
5634 ret = xmlStrndup(q, CUR_PTR - q);
5635 NEXT;
5636 }
5637 } else {
5638 XP_ERROR0(XPATH_START_LITERAL_ERROR);
5639 }
5640 return(ret);
5641}
5642
5643/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005644 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00005645 * @ctxt: the XPath Parser context
5646 *
5647 * Parse a Literal and push it on the stack.
5648 *
5649 * [29] Literal ::= '"' [^"]* '"'
5650 * | "'" [^']* "'"
5651 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005652 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00005653 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005654static void
5655xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005656 const xmlChar *q;
5657 xmlChar *ret = NULL;
5658
5659 if (CUR == '"') {
5660 NEXT;
5661 q = CUR_PTR;
5662 while ((IS_CHAR(CUR)) && (CUR != '"'))
5663 NEXT;
5664 if (!IS_CHAR(CUR)) {
5665 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5666 } else {
5667 ret = xmlStrndup(q, CUR_PTR - q);
5668 NEXT;
5669 }
5670 } else if (CUR == '\'') {
5671 NEXT;
5672 q = CUR_PTR;
5673 while ((IS_CHAR(CUR)) && (CUR != '\''))
5674 NEXT;
5675 if (!IS_CHAR(CUR)) {
5676 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5677 } else {
5678 ret = xmlStrndup(q, CUR_PTR - q);
5679 NEXT;
5680 }
5681 } else {
5682 XP_ERROR(XPATH_START_LITERAL_ERROR);
5683 }
5684 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005685 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
5686 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005687 xmlFree(ret);
5688}
5689
5690/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005691 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00005692 * @ctxt: the XPath Parser context
5693 *
5694 * Parse a VariableReference, evaluate it and push it on the stack.
5695 *
5696 * The variable bindings consist of a mapping from variable names
5697 * to variable values. The value of a variable is an object, which
5698 * of any of the types that are possible for the value of an expression,
5699 * and may also be of additional types not specified here.
5700 *
5701 * Early evaluation is possible since:
5702 * The variable bindings [...] used to evaluate a subexpression are
5703 * always the same as those used to evaluate the containing expression.
5704 *
5705 * [36] VariableReference ::= '$' QName
5706 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005707static void
5708xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005709 xmlChar *name;
5710 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005711
5712 SKIP_BLANKS;
5713 if (CUR != '$') {
5714 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5715 }
5716 NEXT;
5717 name = xmlXPathParseQName(ctxt, &prefix);
5718 if (name == NULL) {
5719 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5720 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005721 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005722 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
5723 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005724 SKIP_BLANKS;
5725}
5726
5727/**
5728 * xmlXPathIsNodeType:
5729 * @ctxt: the XPath Parser context
5730 * @name: a name string
5731 *
5732 * Is the name given a NodeType one.
5733 *
5734 * [38] NodeType ::= 'comment'
5735 * | 'text'
5736 * | 'processing-instruction'
5737 * | 'node'
5738 *
5739 * Returns 1 if true 0 otherwise
5740 */
5741int
5742xmlXPathIsNodeType(const xmlChar *name) {
5743 if (name == NULL)
5744 return(0);
5745
5746 if (xmlStrEqual(name, BAD_CAST "comment"))
5747 return(1);
5748 if (xmlStrEqual(name, BAD_CAST "text"))
5749 return(1);
5750 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5751 return(1);
5752 if (xmlStrEqual(name, BAD_CAST "node"))
5753 return(1);
5754 return(0);
5755}
5756
5757/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005758 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00005759 * @ctxt: the XPath Parser context
5760 *
5761 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5762 * [17] Argument ::= Expr
5763 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005764 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00005765 * pushed on the stack
5766 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005767static void
5768xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005769 xmlChar *name;
5770 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005771 int nbargs = 0;
5772
5773 name = xmlXPathParseQName(ctxt, &prefix);
5774 if (name == NULL) {
5775 XP_ERROR(XPATH_EXPR_ERROR);
5776 }
5777 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005778#ifdef DEBUG_EXPR
5779 if (prefix == NULL)
5780 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5781 name);
5782 else
5783 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5784 prefix, name);
5785#endif
5786
Owen Taylor3473f882001-02-23 17:55:21 +00005787 if (CUR != '(') {
5788 XP_ERROR(XPATH_EXPR_ERROR);
5789 }
5790 NEXT;
5791 SKIP_BLANKS;
5792
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005793 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005794 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005795 int op1 = ctxt->comp->last;
5796 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005797 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005798 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005799 nbargs++;
5800 if (CUR == ')') break;
5801 if (CUR != ',') {
5802 XP_ERROR(XPATH_EXPR_ERROR);
5803 }
5804 NEXT;
5805 SKIP_BLANKS;
5806 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005807 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
5808 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005809 NEXT;
5810 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005811}
5812
5813/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005814 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005815 * @ctxt: the XPath Parser context
5816 *
5817 * [15] PrimaryExpr ::= VariableReference
5818 * | '(' Expr ')'
5819 * | Literal
5820 * | Number
5821 * | FunctionCall
5822 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005823 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005824 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005825static void
5826xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005827 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005828 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005829 else if (CUR == '(') {
5830 NEXT;
5831 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005832 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005833 if (CUR != ')') {
5834 XP_ERROR(XPATH_EXPR_ERROR);
5835 }
5836 NEXT;
5837 SKIP_BLANKS;
5838 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005839 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005840 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005841 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005842 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005843 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005844 }
5845 SKIP_BLANKS;
5846}
5847
5848/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005849 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005850 * @ctxt: the XPath Parser context
5851 *
5852 * [20] FilterExpr ::= PrimaryExpr
5853 * | FilterExpr Predicate
5854 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005855 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005856 * Square brackets are used to filter expressions in the same way that
5857 * they are used in location paths. It is an error if the expression to
5858 * be filtered does not evaluate to a node-set. The context node list
5859 * used for evaluating the expression in square brackets is the node-set
5860 * to be filtered listed in document order.
5861 */
5862
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005863static void
5864xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
5865 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005866 CHECK_ERROR;
5867 SKIP_BLANKS;
5868
5869 while (CUR == '[') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005870 xmlXPathCompPredicate(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005871 SKIP_BLANKS;
5872 }
5873
5874
5875}
5876
5877/**
5878 * xmlXPathScanName:
5879 * @ctxt: the XPath Parser context
5880 *
5881 * Trickery: parse an XML name but without consuming the input flow
5882 * Needed to avoid insanity in the parser state.
5883 *
5884 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5885 * CombiningChar | Extender
5886 *
5887 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5888 *
5889 * [6] Names ::= Name (S Name)*
5890 *
5891 * Returns the Name parsed or NULL
5892 */
5893
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005894static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005895xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5896 xmlChar buf[XML_MAX_NAMELEN];
5897 int len = 0;
5898
5899 SKIP_BLANKS;
5900 if (!IS_LETTER(CUR) && (CUR != '_') &&
5901 (CUR != ':')) {
5902 return(NULL);
5903 }
5904
5905 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5906 (NXT(len) == '.') || (NXT(len) == '-') ||
5907 (NXT(len) == '_') || (NXT(len) == ':') ||
5908 (IS_COMBINING(NXT(len))) ||
5909 (IS_EXTENDER(NXT(len)))) {
5910 buf[len] = NXT(len);
5911 len++;
5912 if (len >= XML_MAX_NAMELEN) {
5913 xmlGenericError(xmlGenericErrorContext,
5914 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5915 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5916 (NXT(len) == '.') || (NXT(len) == '-') ||
5917 (NXT(len) == '_') || (NXT(len) == ':') ||
5918 (IS_COMBINING(NXT(len))) ||
5919 (IS_EXTENDER(NXT(len))))
5920 len++;
5921 break;
5922 }
5923 }
5924 return(xmlStrndup(buf, len));
5925}
5926
5927/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005928 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005929 * @ctxt: the XPath Parser context
5930 *
5931 * [19] PathExpr ::= LocationPath
5932 * | FilterExpr
5933 * | FilterExpr '/' RelativeLocationPath
5934 * | FilterExpr '//' RelativeLocationPath
5935 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005936 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005937 * The / operator and // operators combine an arbitrary expression
5938 * and a relative location path. It is an error if the expression
5939 * does not evaluate to a node-set.
5940 * The / operator does composition in the same way as when / is
5941 * used in a location path. As in location paths, // is short for
5942 * /descendant-or-self::node()/.
5943 */
5944
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005945static void
5946xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005947 int lc = 1; /* Should we branch to LocationPath ? */
5948 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5949
5950 SKIP_BLANKS;
5951 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5952 (CUR == '\'') || (CUR == '"')) {
5953 lc = 0;
5954 } else if (CUR == '*') {
5955 /* relative or absolute location path */
5956 lc = 1;
5957 } else if (CUR == '/') {
5958 /* relative or absolute location path */
5959 lc = 1;
5960 } else if (CUR == '@') {
5961 /* relative abbreviated attribute location path */
5962 lc = 1;
5963 } else if (CUR == '.') {
5964 /* relative abbreviated attribute location path */
5965 lc = 1;
5966 } else {
5967 /*
5968 * Problem is finding if we have a name here whether it's:
5969 * - a nodetype
5970 * - a function call in which case it's followed by '('
5971 * - an axis in which case it's followed by ':'
5972 * - a element name
5973 * We do an a priori analysis here rather than having to
5974 * maintain parsed token content through the recursive function
5975 * calls. This looks uglier but makes the code quite easier to
5976 * read/write/debug.
5977 */
5978 SKIP_BLANKS;
5979 name = xmlXPathScanName(ctxt);
5980 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5981#ifdef DEBUG_STEP
5982 xmlGenericError(xmlGenericErrorContext,
5983 "PathExpr: Axis\n");
5984#endif
5985 lc = 1;
5986 xmlFree(name);
5987 } else if (name != NULL) {
5988 int len =xmlStrlen(name);
5989 int blank = 0;
5990
5991
5992 while (NXT(len) != 0) {
5993 if (NXT(len) == '/') {
5994 /* element name */
5995#ifdef DEBUG_STEP
5996 xmlGenericError(xmlGenericErrorContext,
5997 "PathExpr: AbbrRelLocation\n");
5998#endif
5999 lc = 1;
6000 break;
6001 } else if (IS_BLANK(NXT(len))) {
6002 /* skip to next */
6003 blank = 1;
6004 } else if (NXT(len) == ':') {
6005#ifdef DEBUG_STEP
6006 xmlGenericError(xmlGenericErrorContext,
6007 "PathExpr: AbbrRelLocation\n");
6008#endif
6009 lc = 1;
6010 break;
6011 } else if ((NXT(len) == '(')) {
6012 /* Note Type or Function */
6013 if (xmlXPathIsNodeType(name)) {
6014#ifdef DEBUG_STEP
6015 xmlGenericError(xmlGenericErrorContext,
6016 "PathExpr: Type search\n");
6017#endif
6018 lc = 1;
6019 } else {
6020#ifdef DEBUG_STEP
6021 xmlGenericError(xmlGenericErrorContext,
6022 "PathExpr: function call\n");
6023#endif
6024 lc = 0;
6025 }
6026 break;
6027 } else if ((NXT(len) == '[')) {
6028 /* element name */
6029#ifdef DEBUG_STEP
6030 xmlGenericError(xmlGenericErrorContext,
6031 "PathExpr: AbbrRelLocation\n");
6032#endif
6033 lc = 1;
6034 break;
6035 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
6036 (NXT(len) == '=')) {
6037 lc = 1;
6038 break;
6039 } else {
6040 lc = 1;
6041 break;
6042 }
6043 len++;
6044 }
6045 if (NXT(len) == 0) {
6046#ifdef DEBUG_STEP
6047 xmlGenericError(xmlGenericErrorContext,
6048 "PathExpr: AbbrRelLocation\n");
6049#endif
6050 /* element name */
6051 lc = 1;
6052 }
6053 xmlFree(name);
6054 } else {
6055 /* make sure all cases are covered explicitely */
6056 XP_ERROR(XPATH_EXPR_ERROR);
6057 }
6058 }
6059
6060 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006061 if (CUR == '/') {
6062 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
6063 } else {
6064 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006065 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006066 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006067 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006068 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006069 CHECK_ERROR;
6070 if ((CUR == '/') && (NXT(1) == '/')) {
6071 SKIP(2);
6072 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006073
6074 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6075 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
6076 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
6077
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006078 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006079 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006080 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006081 }
6082 }
6083 SKIP_BLANKS;
6084}
6085
6086/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006087 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006088 * @ctxt: the XPath Parser context
6089 *
6090 * [18] UnionExpr ::= PathExpr
6091 * | UnionExpr '|' PathExpr
6092 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006093 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006094 */
6095
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006096static void
6097xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
6098 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006099 CHECK_ERROR;
6100 SKIP_BLANKS;
6101 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006102 int op1 = ctxt->comp->last;
6103 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006104
6105 NEXT;
6106 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006107 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006108
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006109 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
6110
Owen Taylor3473f882001-02-23 17:55:21 +00006111 SKIP_BLANKS;
6112 }
Owen Taylor3473f882001-02-23 17:55:21 +00006113}
6114
6115/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006116 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006117 * @ctxt: the XPath Parser context
6118 *
6119 * [27] UnaryExpr ::= UnionExpr
6120 * | '-' UnaryExpr
6121 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006122 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006123 */
6124
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006125static void
6126xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006127 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006128 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006129
6130 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00006131 while (CUR == '-') {
6132 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006133 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006134 NEXT;
6135 SKIP_BLANKS;
6136 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006137
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006138 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006139 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006140 if (found) {
6141 if (minus)
6142 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
6143 else
6144 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006145 }
6146}
6147
6148/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006149 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006150 * @ctxt: the XPath Parser context
6151 *
6152 * [26] MultiplicativeExpr ::= UnaryExpr
6153 * | MultiplicativeExpr MultiplyOperator UnaryExpr
6154 * | MultiplicativeExpr 'div' UnaryExpr
6155 * | MultiplicativeExpr 'mod' UnaryExpr
6156 * [34] MultiplyOperator ::= '*'
6157 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006158 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006159 */
6160
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006161static void
6162xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
6163 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006164 CHECK_ERROR;
6165 SKIP_BLANKS;
6166 while ((CUR == '*') ||
6167 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
6168 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
6169 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006170 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006171
6172 if (CUR == '*') {
6173 op = 0;
6174 NEXT;
6175 } else if (CUR == 'd') {
6176 op = 1;
6177 SKIP(3);
6178 } else if (CUR == 'm') {
6179 op = 2;
6180 SKIP(3);
6181 }
6182 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006183 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006184 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006185 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006186 SKIP_BLANKS;
6187 }
6188}
6189
6190/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006191 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006192 * @ctxt: the XPath Parser context
6193 *
6194 * [25] AdditiveExpr ::= MultiplicativeExpr
6195 * | AdditiveExpr '+' MultiplicativeExpr
6196 * | AdditiveExpr '-' MultiplicativeExpr
6197 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006198 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006199 */
6200
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006201static void
6202xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006203
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006204 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006205 CHECK_ERROR;
6206 SKIP_BLANKS;
6207 while ((CUR == '+') || (CUR == '-')) {
6208 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006209 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006210
6211 if (CUR == '+') plus = 1;
6212 else plus = 0;
6213 NEXT;
6214 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006215 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006216 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006217 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006218 SKIP_BLANKS;
6219 }
6220}
6221
6222/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006223 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006224 * @ctxt: the XPath Parser context
6225 *
6226 * [24] RelationalExpr ::= AdditiveExpr
6227 * | RelationalExpr '<' AdditiveExpr
6228 * | RelationalExpr '>' AdditiveExpr
6229 * | RelationalExpr '<=' AdditiveExpr
6230 * | RelationalExpr '>=' AdditiveExpr
6231 *
6232 * A <= B > C is allowed ? Answer from James, yes with
6233 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
6234 * which is basically what got implemented.
6235 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006236 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00006237 * on the stack
6238 */
6239
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006240static void
6241xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
6242 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006243 CHECK_ERROR;
6244 SKIP_BLANKS;
6245 while ((CUR == '<') ||
6246 (CUR == '>') ||
6247 ((CUR == '<') && (NXT(1) == '=')) ||
6248 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006249 int inf, strict;
6250 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006251
6252 if (CUR == '<') inf = 1;
6253 else inf = 0;
6254 if (NXT(1) == '=') strict = 0;
6255 else strict = 1;
6256 NEXT;
6257 if (!strict) NEXT;
6258 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006259 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006260 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006261 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00006262 SKIP_BLANKS;
6263 }
6264}
6265
6266/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006267 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006268 * @ctxt: the XPath Parser context
6269 *
6270 * [23] EqualityExpr ::= RelationalExpr
6271 * | EqualityExpr '=' RelationalExpr
6272 * | EqualityExpr '!=' RelationalExpr
6273 *
6274 * A != B != C is allowed ? Answer from James, yes with
6275 * (RelationalExpr = RelationalExpr) = RelationalExpr
6276 * (RelationalExpr != RelationalExpr) != RelationalExpr
6277 * which is basically what got implemented.
6278 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006279 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006280 *
6281 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006282static void
6283xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
6284 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006285 CHECK_ERROR;
6286 SKIP_BLANKS;
6287 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006288 int eq;
6289 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006290
6291 if (CUR == '=') eq = 1;
6292 else eq = 0;
6293 NEXT;
6294 if (!eq) NEXT;
6295 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006296 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006297 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006298 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006299 SKIP_BLANKS;
6300 }
6301}
6302
6303/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006304 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006305 * @ctxt: the XPath Parser context
6306 *
6307 * [22] AndExpr ::= EqualityExpr
6308 * | AndExpr 'and' EqualityExpr
6309 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006310 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006311 *
6312 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006313static void
6314xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
6315 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006316 CHECK_ERROR;
6317 SKIP_BLANKS;
6318 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006319 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006320 SKIP(3);
6321 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006322 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006323 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006324 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006325 SKIP_BLANKS;
6326 }
6327}
6328
6329/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006330 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006331 * @ctxt: the XPath Parser context
6332 *
6333 * [14] Expr ::= OrExpr
6334 * [21] OrExpr ::= AndExpr
6335 * | OrExpr 'or' AndExpr
6336 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006337 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00006338 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006339static void
6340xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
6341 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006342 CHECK_ERROR;
6343 SKIP_BLANKS;
6344 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006345 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006346 SKIP(2);
6347 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006348 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006349 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006350 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
6351 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00006352 SKIP_BLANKS;
6353 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006354 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
6355 /* more ops could be optimized too */
6356 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
6357 }
Owen Taylor3473f882001-02-23 17:55:21 +00006358}
6359
6360/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006361 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00006362 * @ctxt: the XPath Parser context
6363 *
6364 * [8] Predicate ::= '[' PredicateExpr ']'
6365 * [9] PredicateExpr ::= Expr
6366 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006367 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00006368 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006369static void
6370xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006371 int op1 = ctxt->comp->last;
6372
6373 SKIP_BLANKS;
6374 if (CUR != '[') {
6375 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6376 }
6377 NEXT;
6378 SKIP_BLANKS;
6379
6380 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006381 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006382 CHECK_ERROR;
6383
6384 if (CUR != ']') {
6385 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6386 }
6387
6388 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
6389
6390 NEXT;
6391 SKIP_BLANKS;
6392}
6393
6394/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006395 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00006396 * @ctxt: the XPath Parser context
6397 * @test: pointer to a xmlXPathTestVal
6398 * @type: pointer to a xmlXPathTypeVal
6399 * @prefix: placeholder for a possible name prefix
6400 *
6401 * [7] NodeTest ::= NameTest
6402 * | NodeType '(' ')'
6403 * | 'processing-instruction' '(' Literal ')'
6404 *
6405 * [37] NameTest ::= '*'
6406 * | NCName ':' '*'
6407 * | QName
6408 * [38] NodeType ::= 'comment'
6409 * | 'text'
6410 * | 'processing-instruction'
6411 * | 'node'
6412 *
6413 * Returns the name found and update @test, @type and @prefix appropriately
6414 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006415static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006416xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6417 xmlXPathTypeVal *type, const xmlChar **prefix,
6418 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00006419 int blanks;
6420
6421 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6422 STRANGE;
6423 return(NULL);
6424 }
6425 *type = 0;
6426 *test = 0;
6427 *prefix = NULL;
6428 SKIP_BLANKS;
6429
6430 if ((name == NULL) && (CUR == '*')) {
6431 /*
6432 * All elements
6433 */
6434 NEXT;
6435 *test = NODE_TEST_ALL;
6436 return(NULL);
6437 }
6438
6439 if (name == NULL)
6440 name = xmlXPathParseNCName(ctxt);
6441 if (name == NULL) {
6442 XP_ERROR0(XPATH_EXPR_ERROR);
6443 }
6444
6445 blanks = IS_BLANK(CUR);
6446 SKIP_BLANKS;
6447 if (CUR == '(') {
6448 NEXT;
6449 /*
6450 * NodeType or PI search
6451 */
6452 if (xmlStrEqual(name, BAD_CAST "comment"))
6453 *type = NODE_TYPE_COMMENT;
6454 else if (xmlStrEqual(name, BAD_CAST "node"))
6455 *type = NODE_TYPE_NODE;
6456 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6457 *type = NODE_TYPE_PI;
6458 else if (xmlStrEqual(name, BAD_CAST "text"))
6459 *type = NODE_TYPE_TEXT;
6460 else {
6461 if (name != NULL)
6462 xmlFree(name);
6463 XP_ERROR0(XPATH_EXPR_ERROR);
6464 }
6465
6466 *test = NODE_TEST_TYPE;
6467
6468 SKIP_BLANKS;
6469 if (*type == NODE_TYPE_PI) {
6470 /*
6471 * Specific case: search a PI by name.
6472 */
Owen Taylor3473f882001-02-23 17:55:21 +00006473 if (name != NULL)
6474 xmlFree(name);
6475
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006476 name = xmlXPathParseLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006477 CHECK_ERROR 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006478 SKIP_BLANKS;
6479 }
6480 if (CUR != ')') {
6481 if (name != NULL)
6482 xmlFree(name);
6483 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6484 }
6485 NEXT;
6486 return(name);
6487 }
6488 *test = NODE_TEST_NAME;
6489 if ((!blanks) && (CUR == ':')) {
6490 NEXT;
6491
6492 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006493 * Since currently the parser context don't have a
6494 * namespace list associated:
6495 * The namespace name for this prefix can be computed
6496 * only at evaluation time. The compilation is done
6497 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00006498 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006499#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00006500 *prefix = xmlXPathNsLookup(ctxt->context, name);
6501 if (name != NULL)
6502 xmlFree(name);
6503 if (*prefix == NULL) {
6504 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6505 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006506#else
6507 *prefix = name;
6508#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006509
6510 if (CUR == '*') {
6511 /*
6512 * All elements
6513 */
6514 NEXT;
6515 *test = NODE_TEST_ALL;
6516 return(NULL);
6517 }
6518
6519 name = xmlXPathParseNCName(ctxt);
6520 if (name == NULL) {
6521 XP_ERROR0(XPATH_EXPR_ERROR);
6522 }
6523 }
6524 return(name);
6525}
6526
6527/**
6528 * xmlXPathIsAxisName:
6529 * @name: a preparsed name token
6530 *
6531 * [6] AxisName ::= 'ancestor'
6532 * | 'ancestor-or-self'
6533 * | 'attribute'
6534 * | 'child'
6535 * | 'descendant'
6536 * | 'descendant-or-self'
6537 * | 'following'
6538 * | 'following-sibling'
6539 * | 'namespace'
6540 * | 'parent'
6541 * | 'preceding'
6542 * | 'preceding-sibling'
6543 * | 'self'
6544 *
6545 * Returns the axis or 0
6546 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006547static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00006548xmlXPathIsAxisName(const xmlChar *name) {
6549 xmlXPathAxisVal ret = 0;
6550 switch (name[0]) {
6551 case 'a':
6552 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6553 ret = AXIS_ANCESTOR;
6554 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6555 ret = AXIS_ANCESTOR_OR_SELF;
6556 if (xmlStrEqual(name, BAD_CAST "attribute"))
6557 ret = AXIS_ATTRIBUTE;
6558 break;
6559 case 'c':
6560 if (xmlStrEqual(name, BAD_CAST "child"))
6561 ret = AXIS_CHILD;
6562 break;
6563 case 'd':
6564 if (xmlStrEqual(name, BAD_CAST "descendant"))
6565 ret = AXIS_DESCENDANT;
6566 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6567 ret = AXIS_DESCENDANT_OR_SELF;
6568 break;
6569 case 'f':
6570 if (xmlStrEqual(name, BAD_CAST "following"))
6571 ret = AXIS_FOLLOWING;
6572 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6573 ret = AXIS_FOLLOWING_SIBLING;
6574 break;
6575 case 'n':
6576 if (xmlStrEqual(name, BAD_CAST "namespace"))
6577 ret = AXIS_NAMESPACE;
6578 break;
6579 case 'p':
6580 if (xmlStrEqual(name, BAD_CAST "parent"))
6581 ret = AXIS_PARENT;
6582 if (xmlStrEqual(name, BAD_CAST "preceding"))
6583 ret = AXIS_PRECEDING;
6584 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6585 ret = AXIS_PRECEDING_SIBLING;
6586 break;
6587 case 's':
6588 if (xmlStrEqual(name, BAD_CAST "self"))
6589 ret = AXIS_SELF;
6590 break;
6591 }
6592 return(ret);
6593}
6594
6595/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006596 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00006597 * @ctxt: the XPath Parser context
6598 *
6599 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6600 * | AbbreviatedStep
6601 *
6602 * [12] AbbreviatedStep ::= '.' | '..'
6603 *
6604 * [5] AxisSpecifier ::= AxisName '::'
6605 * | AbbreviatedAxisSpecifier
6606 *
6607 * [13] AbbreviatedAxisSpecifier ::= '@'?
6608 *
6609 * Modified for XPtr range support as:
6610 *
6611 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6612 * | AbbreviatedStep
6613 * | 'range-to' '(' Expr ')' Predicate*
6614 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006615 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00006616 * A location step of . is short for self::node(). This is
6617 * particularly useful in conjunction with //. For example, the
6618 * location path .//para is short for
6619 * self::node()/descendant-or-self::node()/child::para
6620 * and so will select all para descendant elements of the context
6621 * node.
6622 * Similarly, a location step of .. is short for parent::node().
6623 * For example, ../title is short for parent::node()/child::title
6624 * and so will select the title children of the parent of the context
6625 * node.
6626 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006627static void
6628xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006629 SKIP_BLANKS;
6630 if ((CUR == '.') && (NXT(1) == '.')) {
6631 SKIP(2);
6632 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006633 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
6634 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006635 } else if (CUR == '.') {
6636 NEXT;
6637 SKIP_BLANKS;
6638 } else {
6639 xmlChar *name = NULL;
6640 const xmlChar *prefix = NULL;
6641 xmlXPathTestVal test;
6642 xmlXPathAxisVal axis;
6643 xmlXPathTypeVal type;
6644
6645 /*
6646 * The modification needed for XPointer change to the production
6647 */
6648#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006649 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00006650 name = xmlXPathParseNCName(ctxt);
6651 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006652 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006653 xmlFree(name);
6654 SKIP_BLANKS;
6655 if (CUR != '(') {
6656 XP_ERROR(XPATH_EXPR_ERROR);
6657 }
6658 NEXT;
6659 SKIP_BLANKS;
6660
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006661 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006662 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006663 CHECK_ERROR;
6664
6665 SKIP_BLANKS;
6666 if (CUR != ')') {
6667 XP_ERROR(XPATH_EXPR_ERROR);
6668 }
6669 NEXT;
6670 goto eval_predicates;
6671 }
6672 }
6673#endif
6674 if (name == NULL)
6675 name = xmlXPathParseNCName(ctxt);
6676 if (name != NULL) {
6677 axis = xmlXPathIsAxisName(name);
6678 if (axis != 0) {
6679 SKIP_BLANKS;
6680 if ((CUR == ':') && (NXT(1) == ':')) {
6681 SKIP(2);
6682 xmlFree(name);
6683 name = NULL;
6684 } else {
6685 /* an element name can conflict with an axis one :-\ */
6686 axis = AXIS_CHILD;
6687 }
6688 } else {
6689 axis = AXIS_CHILD;
6690 }
6691 } else if (CUR == '@') {
6692 NEXT;
6693 axis = AXIS_ATTRIBUTE;
6694 } else {
6695 axis = AXIS_CHILD;
6696 }
6697
6698 CHECK_ERROR;
6699
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006700 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00006701 if (test == 0)
6702 return;
6703
6704#ifdef DEBUG_STEP
6705 xmlGenericError(xmlGenericErrorContext,
6706 "Basis : computing new set\n");
6707#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006708
6709 PUSH_LONG_EXPR(XPATH_OP_COLLECT, axis, test, type,
6710 (void *)prefix, (void *)name);
6711
Owen Taylor3473f882001-02-23 17:55:21 +00006712#ifdef DEBUG_STEP
6713 xmlGenericError(xmlGenericErrorContext, "Basis : ");
6714 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
6715#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006716
6717eval_predicates:
6718 SKIP_BLANKS;
6719 while (CUR == '[') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006720 xmlXPathCompPredicate(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006721 }
6722 }
6723#ifdef DEBUG_STEP
6724 xmlGenericError(xmlGenericErrorContext, "Step : ");
6725 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6726 ctxt->value->nodesetval);
6727#endif
6728}
6729
6730/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006731 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006732 * @ctxt: the XPath Parser context
6733 *
6734 * [3] RelativeLocationPath ::= Step
6735 * | RelativeLocationPath '/' Step
6736 * | AbbreviatedRelativeLocationPath
6737 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6738 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006739 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00006740 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006741static void
Owen Taylor3473f882001-02-23 17:55:21 +00006742#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006743xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006744#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006745xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006746#endif
6747(xmlXPathParserContextPtr ctxt) {
6748 SKIP_BLANKS;
6749 if ((CUR == '/') && (NXT(1) == '/')) {
6750 SKIP(2);
6751 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006752 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6753 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006754 } else if (CUR == '/') {
6755 NEXT;
6756 SKIP_BLANKS;
6757 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006758 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006759 SKIP_BLANKS;
6760 while (CUR == '/') {
6761 if ((CUR == '/') && (NXT(1) == '/')) {
6762 SKIP(2);
6763 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006764 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00006765 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006766 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006767 } else if (CUR == '/') {
6768 NEXT;
6769 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006770 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006771 }
6772 SKIP_BLANKS;
6773 }
6774}
6775
6776/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006777 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006778 * @ctxt: the XPath Parser context
6779 *
6780 * [1] LocationPath ::= RelativeLocationPath
6781 * | AbsoluteLocationPath
6782 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6783 * | AbbreviatedAbsoluteLocationPath
6784 * [10] AbbreviatedAbsoluteLocationPath ::=
6785 * '//' RelativeLocationPath
6786 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006787 * Compile a location path
6788 *
Owen Taylor3473f882001-02-23 17:55:21 +00006789 * // is short for /descendant-or-self::node()/. For example,
6790 * //para is short for /descendant-or-self::node()/child::para and
6791 * so will select any para element in the document (even a para element
6792 * that is a document element will be selected by //para since the
6793 * document element node is a child of the root node); div//para is
6794 * short for div/descendant-or-self::node()/child::para and so will
6795 * select all para descendants of div children.
6796 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006797static void
6798xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006799 SKIP_BLANKS;
6800 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006801 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006802 } else {
6803 while (CUR == '/') {
6804 if ((CUR == '/') && (NXT(1) == '/')) {
6805 SKIP(2);
6806 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006807 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6808 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006809 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006810 } else if (CUR == '/') {
6811 NEXT;
6812 SKIP_BLANKS;
6813 if (CUR != 0)
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006814 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006815 }
6816 }
6817 }
6818}
6819
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006820/************************************************************************
6821 * *
6822 * XPath precompiled expression evaluation *
6823 * *
6824 ************************************************************************/
6825
Owen Taylor3473f882001-02-23 17:55:21 +00006826/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006827 * xmlXPathCompOpEval:
6828 * @ctxt: the XPath parser context with the compiled expression
6829 * @op: an XPath compiled operation
6830 *
6831 * Evaluate the Precompiled XPath operation
6832 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006833static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006834xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) {
6835 int equal, ret;
6836 xmlXPathCompExprPtr comp;
6837 xmlXPathObjectPtr arg1, arg2;
6838
6839 comp = ctxt->comp;
6840 switch (op->op) {
6841 case XPATH_OP_END:
6842 return;
6843 case XPATH_OP_AND:
6844 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6845 xmlXPathBooleanFunction(ctxt, 1);
6846 if (ctxt->value->boolval == 0)
6847 return;
6848 arg2 = valuePop(ctxt);
6849 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6850 xmlXPathBooleanFunction(ctxt, 1);
6851 arg1 = valuePop(ctxt);
6852 arg1->boolval &= arg2->boolval;
6853 valuePush(ctxt, arg1);
6854 xmlXPathFreeObject(arg2);
6855 return;
6856 case XPATH_OP_OR:
6857 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6858 xmlXPathBooleanFunction(ctxt, 1);
6859 if (ctxt->value->boolval == 1)
6860 return;
6861 arg2 = valuePop(ctxt);
6862 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6863 xmlXPathBooleanFunction(ctxt, 1);
6864 arg1 = valuePop(ctxt);
6865 arg1->boolval |= arg2->boolval;
6866 valuePush(ctxt, arg1);
6867 xmlXPathFreeObject(arg2);
6868 return;
6869 case XPATH_OP_EQUAL:
6870 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6871 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6872 equal = xmlXPathEqualValues(ctxt);
6873 if (op->value) valuePush(ctxt, xmlXPathNewBoolean(equal));
6874 else valuePush(ctxt, xmlXPathNewBoolean(!equal));
6875 return;
6876 case XPATH_OP_CMP:
6877 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6878 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6879 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
6880 valuePush(ctxt, xmlXPathNewBoolean(ret));
6881 return;
6882 case XPATH_OP_PLUS:
6883 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6884 if (op->ch2 != -1)
6885 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6886 if (op->value == 0) xmlXPathSubValues(ctxt);
6887 else if (op->value == 1) xmlXPathAddValues(ctxt);
6888 else if (op->value == 2) xmlXPathValueFlipSign(ctxt);
6889 else if (op->value == 3) {
6890 xmlXPathObjectPtr arg;
6891
6892 POP_FLOAT
6893 valuePush(ctxt, arg);
6894 }
6895 return;
6896 case XPATH_OP_MULT:
6897 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6898 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6899 if (op->value == 0) xmlXPathMultValues(ctxt);
6900 else if (op->value == 1) xmlXPathDivValues(ctxt);
6901 else if (op->value == 2) xmlXPathModValues(ctxt);
6902 return;
6903 case XPATH_OP_UNION:
6904 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6905 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6906 CHECK_TYPE(XPATH_NODESET);
6907 arg2 = valuePop(ctxt);
6908
6909 CHECK_TYPE(XPATH_NODESET);
6910 arg1 = valuePop(ctxt);
6911
6912 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
6913 arg2->nodesetval);
6914 valuePush(ctxt, arg1);
6915 xmlXPathFreeObject(arg2);
6916 return;
6917 case XPATH_OP_ROOT:
6918 xmlXPathRoot(ctxt);
6919 return;
6920 case XPATH_OP_NODE:
6921 if (op->ch1 != -1)
6922 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6923 if (op->ch2 != -1)
6924 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6925 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
6926 return;
6927 case XPATH_OP_RESET:
6928 if (op->ch1 != -1)
6929 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6930 if (op->ch2 != -1)
6931 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6932 ctxt->context->node = NULL;
6933 return;
6934 case XPATH_OP_COLLECT:
6935 if (op->ch1 != -1)
6936 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6937 xmlXPathNodeCollectAndTest(ctxt, op->value, op->value2,
6938 op->value3, op->value4, op->value5);
6939 return;
6940 case XPATH_OP_VALUE:
6941 valuePush(ctxt,
6942 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
6943 return;
6944 case XPATH_OP_VARIABLE: {
6945 if (op->ch1 != -1)
6946 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6947 if (op->value5 == NULL)
6948 valuePush(ctxt,
6949 xmlXPathVariableLookup(ctxt->context, op->value4));
6950 else {
6951 const xmlChar *URI;
6952 URI = xmlXPathNsLookup(ctxt->context, op->value5);
6953 if (URI == NULL) {
6954 xmlGenericError(xmlGenericErrorContext,
6955 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
6956 op->value4, op->value5);
6957 return;
6958 }
6959 valuePush(ctxt,
6960 xmlXPathVariableLookupNS(ctxt->context,
6961 op->value4, URI));
6962 }
6963 return;
6964 }
6965 case XPATH_OP_FUNCTION: {
6966 xmlXPathFunction func;
6967
6968 if (op->ch1 != -1)
6969 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6970 if (op->value5 == NULL)
6971 func = xmlXPathFunctionLookup(ctxt->context, op->value4);
6972 else {
6973 const xmlChar *URI;
6974 URI = xmlXPathNsLookup(ctxt->context, op->value5);
6975 if (URI == NULL) {
6976 xmlGenericError(xmlGenericErrorContext,
6977 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
6978 op->value4, op->value5);
6979 return;
6980 }
6981 func = xmlXPathFunctionLookupNS(ctxt->context,
6982 op->value4, URI);
6983 }
6984 if (func == NULL) {
6985 xmlGenericError(xmlGenericErrorContext,
6986 "xmlXPathRunEval: function %s not found\n",
6987 op->value4);
6988 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
6989 return;
6990 }
6991 func(ctxt, op->value);
6992 return;
6993 }
6994 case XPATH_OP_ARG:
6995 if (op->ch1 != -1)
6996 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6997 if (op->ch2 != -1)
6998 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6999 return;
7000 case XPATH_OP_PREDICATE: {
7001 xmlXPathObjectPtr res;
7002 xmlXPathObjectPtr obj, tmp;
7003 xmlNodeSetPtr newset = NULL;
7004 xmlNodeSetPtr oldset;
7005 xmlNodePtr oldnode;
7006 int i;
7007
7008 if (op->ch1 != -1)
7009 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7010 if (op->ch2 == -1)
7011 return;
7012
7013 oldnode = ctxt->context->node;
7014
7015#ifdef LIBXML_XPTR_ENABLED
7016 /*
7017 * Hum are we filtering the result of an XPointer expression
7018 */
7019 if (ctxt->value->type == XPATH_LOCATIONSET) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007020 xmlLocationSetPtr newlocset = NULL;
7021 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007022
7023 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007024 * Extract the old locset, and then evaluate the result of the
7025 * expression for all the element in the locset. use it to grow
7026 * up a new locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007027 */
7028 CHECK_TYPE(XPATH_LOCATIONSET);
7029 obj = valuePop(ctxt);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007030 oldlocset = obj->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007031 ctxt->context->node = NULL;
7032
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007033 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007034 ctxt->context->contextSize = 0;
7035 ctxt->context->proximityPosition = 0;
7036 if (op->ch2 != -1)
7037 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7038 res = valuePop(ctxt);
7039 if (res != NULL)
7040 xmlXPathFreeObject(res);
7041 valuePush(ctxt, obj);
7042 CHECK_ERROR;
7043 return;
7044 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007045 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007046
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007047 for (i = 0; i < oldlocset->locNr; i++) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007048 /*
7049 * Run the evaluation with a node list made of a
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007050 * single item in the nodelocset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007051 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007052 ctxt->context->node = oldlocset->locTab[i]->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007053 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7054 valuePush(ctxt, tmp);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007055 ctxt->context->contextSize = oldlocset->locNr;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007056 ctxt->context->proximityPosition = i + 1;
7057
7058 if (op->ch2 != -1)
7059 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7060 CHECK_ERROR;
7061
7062 /*
7063 * The result of the evaluation need to be tested to
7064 * decided whether the filter succeeded or not
7065 */
7066 res = valuePop(ctxt);
7067 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007068 xmlXPtrLocationSetAdd(newlocset,
7069 xmlXPathObjectCopy(oldlocset->locTab[i]));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007070 }
7071
7072 /*
7073 * Cleanup
7074 */
7075 if (res != NULL)
7076 xmlXPathFreeObject(res);
7077 if (ctxt->value == tmp) {
7078 res = valuePop(ctxt);
7079 xmlXPathFreeObject(res);
7080 }
7081
7082 ctxt->context->node = NULL;
7083 }
7084
7085 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007086 * The result is used as the new evaluation locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007087 */
7088 xmlXPathFreeObject(obj);
7089 ctxt->context->node = NULL;
7090 ctxt->context->contextSize = -1;
7091 ctxt->context->proximityPosition = -1;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007092 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007093 ctxt->context->node = oldnode;
7094 return;
7095 }
7096#endif /* LIBXML_XPTR_ENABLED */
7097
7098 /*
7099 * Extract the old set, and then evaluate the result of the
7100 * expression for all the element in the set. use it to grow
7101 * up a new set.
7102 */
7103 CHECK_TYPE(XPATH_NODESET);
7104 obj = valuePop(ctxt);
7105 oldset = obj->nodesetval;
7106 oldnode = ctxt->context->node;
7107 ctxt->context->node = NULL;
7108
7109 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
7110 ctxt->context->contextSize = 0;
7111 ctxt->context->proximityPosition = 0;
7112 if (op->ch2 != -1)
7113 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7114 res = valuePop(ctxt);
7115 if (res != NULL)
7116 xmlXPathFreeObject(res);
7117 valuePush(ctxt, obj);
7118 CHECK_ERROR;
7119 } else {
7120 /*
7121 * Initialize the new set.
7122 */
7123 newset = xmlXPathNodeSetCreate(NULL);
7124
7125 for (i = 0; i < oldset->nodeNr; i++) {
7126 /*
7127 * Run the evaluation with a node list made of
7128 * a single item in the nodeset.
7129 */
7130 ctxt->context->node = oldset->nodeTab[i];
7131 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7132 valuePush(ctxt, tmp);
7133 ctxt->context->contextSize = oldset->nodeNr;
7134 ctxt->context->proximityPosition = i + 1;
7135
7136 if (op->ch2 != -1)
7137 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7138 CHECK_ERROR;
7139
7140 /*
7141 * The result of the evaluation need to be tested to
7142 * decided whether the filter succeeded or not
7143 */
7144 res = valuePop(ctxt);
7145 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
7146 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
7147 }
7148
7149 /*
7150 * Cleanup
7151 */
7152 if (res != NULL)
7153 xmlXPathFreeObject(res);
7154 if (ctxt->value == tmp) {
7155 res = valuePop(ctxt);
7156 xmlXPathFreeObject(res);
7157 }
7158
7159 ctxt->context->node = NULL;
7160 }
7161
7162 /*
7163 * The result is used as the new evaluation set.
7164 */
7165 xmlXPathFreeObject(obj);
7166 ctxt->context->node = NULL;
7167 ctxt->context->contextSize = -1;
7168 ctxt->context->proximityPosition = -1;
7169 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
7170 }
7171 ctxt->context->node = oldnode;
7172 return;
7173 }
7174 case XPATH_OP_SORT:
7175 if (op->ch1 != -1)
7176 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7177 if ((ctxt->value != NULL) &&
7178 (ctxt->value->type == XPATH_NODESET) &&
7179 (ctxt->value->nodesetval != NULL))
7180 xmlXPathNodeSetSort(ctxt->value->nodesetval);
7181 return;
7182#ifdef LIBXML_XPTR_ENABLED
7183 case XPATH_OP_RANGETO: {
7184 xmlXPathObjectPtr range;
7185 xmlXPathObjectPtr res, obj;
7186 xmlXPathObjectPtr tmp;
7187 xmlLocationSetPtr newset = NULL;
7188 xmlNodeSetPtr oldset;
7189 int i;
7190
7191 if (op->ch1 != -1)
7192 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7193 if (op->ch2 == -1)
7194 return;
7195
7196 CHECK_TYPE(XPATH_NODESET);
7197 obj = valuePop(ctxt);
7198 oldset = obj->nodesetval;
7199 ctxt->context->node = NULL;
7200
7201 newset = xmlXPtrLocationSetCreate(NULL);
7202
7203 for (i = 0; i < oldset->nodeNr; i++) {
7204 /*
7205 * Run the evaluation with a node list made of a single item
7206 * in the nodeset.
7207 */
7208 ctxt->context->node = oldset->nodeTab[i];
7209 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7210 valuePush(ctxt, tmp);
7211
7212 if (op->ch2 != -1)
7213 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7214 CHECK_ERROR;
7215
7216 /*
7217 * The result of the evaluation need to be tested to
7218 * decided whether the filter succeeded or not
7219 */
7220 res = valuePop(ctxt);
7221 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
7222 if (range != NULL) {
7223 xmlXPtrLocationSetAdd(newset, range);
7224 }
7225
7226 /*
7227 * Cleanup
7228 */
7229 if (res != NULL)
7230 xmlXPathFreeObject(res);
7231 if (ctxt->value == tmp) {
7232 res = valuePop(ctxt);
7233 xmlXPathFreeObject(res);
7234 }
7235
7236 ctxt->context->node = NULL;
7237 }
7238
7239 /*
7240 * The result is used as the new evaluation set.
7241 */
7242 xmlXPathFreeObject(obj);
7243 ctxt->context->node = NULL;
7244 ctxt->context->contextSize = -1;
7245 ctxt->context->proximityPosition = -1;
7246 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
7247 return;
7248 }
7249#endif /* LIBXML_XPTR_ENABLED */
7250 }
7251 xmlGenericError(xmlGenericErrorContext,
7252 "XPath: unknown precompiled operation %d\n",
7253 op->op);
7254 return;
7255}
7256
7257/**
7258 * xmlXPathRunEval:
7259 * @ctxt: the XPath parser context with the compiled expression
7260 *
7261 * Evaluate the Precompiled XPath expression in the given context.
7262 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007263static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007264xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
7265 xmlXPathCompExprPtr comp;
7266
7267 if ((ctxt == NULL) || (ctxt->comp == NULL))
7268 return;
7269
7270 if (ctxt->valueTab == NULL) {
7271 /* Allocate the value stack */
7272 ctxt->valueTab = (xmlXPathObjectPtr *)
7273 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
7274 if (ctxt->valueTab == NULL) {
7275 xmlFree(ctxt);
7276 xmlGenericError(xmlGenericErrorContext,
7277 "xmlXPathRunEval: out of memory\n");
7278 return;
7279 }
7280 ctxt->valueNr = 0;
7281 ctxt->valueMax = 10;
7282 ctxt->value = NULL;
7283 }
7284 comp = ctxt->comp;
7285 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
7286}
7287
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007288/************************************************************************
7289 * *
7290 * Public interfaces *
7291 * *
7292 ************************************************************************/
7293
7294/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007295 * xmlXPathEvalPredicate:
7296 * @ctxt: the XPath context
7297 * @res: the Predicate Expression evaluation result
7298 *
7299 * Evaluate a predicate result for the current node.
7300 * A PredicateExpr is evaluated by evaluating the Expr and converting
7301 * the result to a boolean. If the result is a number, the result will
7302 * be converted to true if the number is equal to the position of the
7303 * context node in the context node list (as returned by the position
7304 * function) and will be converted to false otherwise; if the result
7305 * is not a number, then the result will be converted as if by a call
7306 * to the boolean function.
7307 *
7308 * Return 1 if predicate is true, 0 otherwise
7309 */
7310int
7311xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
7312 if (res == NULL) return(0);
7313 switch (res->type) {
7314 case XPATH_BOOLEAN:
7315 return(res->boolval);
7316 case XPATH_NUMBER:
7317 return(res->floatval == ctxt->proximityPosition);
7318 case XPATH_NODESET:
7319 case XPATH_XSLT_TREE:
7320 return(res->nodesetval->nodeNr != 0);
7321 case XPATH_STRING:
7322 return((res->stringval != NULL) &&
7323 (xmlStrlen(res->stringval) != 0));
7324 default:
7325 STRANGE
7326 }
7327 return(0);
7328}
7329
7330/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007331 * xmlXPathEvaluatePredicateResult:
7332 * @ctxt: the XPath Parser context
7333 * @res: the Predicate Expression evaluation result
7334 *
7335 * Evaluate a predicate result for the current node.
7336 * A PredicateExpr is evaluated by evaluating the Expr and converting
7337 * the result to a boolean. If the result is a number, the result will
7338 * be converted to true if the number is equal to the position of the
7339 * context node in the context node list (as returned by the position
7340 * function) and will be converted to false otherwise; if the result
7341 * is not a number, then the result will be converted as if by a call
7342 * to the boolean function.
7343 *
7344 * Return 1 if predicate is true, 0 otherwise
7345 */
7346int
7347xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
7348 xmlXPathObjectPtr res) {
7349 if (res == NULL) return(0);
7350 switch (res->type) {
7351 case XPATH_BOOLEAN:
7352 return(res->boolval);
7353 case XPATH_NUMBER:
7354 return(res->floatval == ctxt->context->proximityPosition);
7355 case XPATH_NODESET:
7356 case XPATH_XSLT_TREE:
7357 return(res->nodesetval->nodeNr != 0);
7358 case XPATH_STRING:
7359 return((res->stringval != NULL) &&
7360 (xmlStrlen(res->stringval) != 0));
7361 default:
7362 STRANGE
7363 }
7364 return(0);
7365}
7366
7367/**
7368 * xmlXPathCompile:
7369 * @str: the XPath expression
7370 *
7371 * Compile an XPath expression
7372 *
7373 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7374 * the caller has to free the object.
7375 */
7376xmlXPathCompExprPtr
7377xmlXPathCompile(const xmlChar *str) {
7378 xmlXPathParserContextPtr ctxt;
7379 xmlXPathCompExprPtr comp;
7380
7381 xmlXPathInit();
7382
7383 ctxt = xmlXPathNewParserContext(str, NULL);
7384 xmlXPathCompileExpr(ctxt);
7385
7386 comp = ctxt->comp;
7387 ctxt->comp = NULL;
7388 xmlXPathFreeParserContext(ctxt);
7389 return(comp);
7390}
7391
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007392/**
7393 * xmlXPathCompiledEval:
7394 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00007395 * @ctx: the XPath context
7396 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007397 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00007398 *
7399 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7400 * the caller has to free the object.
7401 */
7402xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007403xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00007404 xmlXPathParserContextPtr ctxt;
7405 xmlXPathObjectPtr res, tmp, init = NULL;
7406 int stack = 0;
7407
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007408 if ((comp == NULL) || (ctx == NULL))
7409 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007410 xmlXPathInit();
7411
7412 CHECK_CONTEXT(ctx)
7413
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007414 ctxt = xmlXPathCompParserContext(comp, ctx);
7415 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007416
7417 if (ctxt->value == NULL) {
7418 xmlGenericError(xmlGenericErrorContext,
7419 "xmlXPathEval: evaluation failed\n");
7420 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00007421 } else {
7422 res = valuePop(ctxt);
7423 }
7424
7425 do {
7426 tmp = valuePop(ctxt);
7427 if (tmp != NULL) {
7428 if (tmp != init)
7429 stack++;
7430 xmlXPathFreeObject(tmp);
7431 }
7432 } while (tmp != NULL);
7433 if ((stack != 0) && (res != NULL)) {
7434 xmlGenericError(xmlGenericErrorContext,
7435 "xmlXPathEval: %d object left on the stack\n",
7436 stack);
7437 }
7438 if (ctxt->error != XPATH_EXPRESSION_OK) {
7439 xmlXPathFreeObject(res);
7440 res = NULL;
7441 }
7442
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007443
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007444 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007445 xmlXPathFreeParserContext(ctxt);
7446 return(res);
7447}
7448
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007449/**
7450 * xmlXPathEvalExpr:
7451 * @ctxt: the XPath Parser context
7452 *
7453 * Parse and evaluate an XPath expression in the given context,
7454 * then push the result on the context stack
7455 */
7456void
7457xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
7458 xmlXPathCompileExpr(ctxt);
7459 xmlXPathRunEval(ctxt);
7460}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007461
7462/**
7463 * xmlXPathEval:
7464 * @str: the XPath expression
7465 * @ctx: the XPath context
7466 *
7467 * Evaluate the XPath Location Path in the given context.
7468 *
7469 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7470 * the caller has to free the object.
7471 */
7472xmlXPathObjectPtr
7473xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
7474 xmlXPathParserContextPtr ctxt;
7475 xmlXPathObjectPtr res, tmp, init = NULL;
7476 int stack = 0;
7477
7478 xmlXPathInit();
7479
7480 CHECK_CONTEXT(ctx)
7481
7482 ctxt = xmlXPathNewParserContext(str, ctx);
7483 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007484
7485 if (ctxt->value == NULL) {
7486 xmlGenericError(xmlGenericErrorContext,
7487 "xmlXPathEval: evaluation failed\n");
7488 res = NULL;
7489 } else if (*ctxt->cur != 0) {
7490 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7491 res = NULL;
7492 } 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
Owen Taylor3473f882001-02-23 17:55:21 +00007514 xmlXPathFreeParserContext(ctxt);
7515 return(res);
7516}
7517
7518/**
7519 * xmlXPathEvalExpression:
7520 * @str: the XPath expression
7521 * @ctxt: the XPath context
7522 *
7523 * Evaluate the XPath expression in the given context.
7524 *
7525 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
7526 * the caller has to free the object.
7527 */
7528xmlXPathObjectPtr
7529xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
7530 xmlXPathParserContextPtr pctxt;
7531 xmlXPathObjectPtr res, tmp;
7532 int stack = 0;
7533
7534 xmlXPathInit();
7535
7536 CHECK_CONTEXT(ctxt)
7537
7538 pctxt = xmlXPathNewParserContext(str, ctxt);
7539 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007540
7541 if (*pctxt->cur != 0) {
7542 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7543 res = NULL;
7544 } else {
7545 res = valuePop(pctxt);
7546 }
7547 do {
7548 tmp = valuePop(pctxt);
7549 if (tmp != NULL) {
7550 xmlXPathFreeObject(tmp);
7551 stack++;
7552 }
7553 } while (tmp != NULL);
7554 if ((stack != 0) && (res != NULL)) {
7555 xmlGenericError(xmlGenericErrorContext,
7556 "xmlXPathEvalExpression: %d object left on the stack\n",
7557 stack);
7558 }
7559 xmlXPathFreeParserContext(pctxt);
7560 return(res);
7561}
7562
7563/**
7564 * xmlXPathRegisterAllFunctions:
7565 * @ctxt: the XPath context
7566 *
7567 * Registers all default XPath functions in this context
7568 */
7569void
7570xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
7571{
7572 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
7573 xmlXPathBooleanFunction);
7574 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
7575 xmlXPathCeilingFunction);
7576 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
7577 xmlXPathCountFunction);
7578 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
7579 xmlXPathConcatFunction);
7580 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
7581 xmlXPathContainsFunction);
7582 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
7583 xmlXPathIdFunction);
7584 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
7585 xmlXPathFalseFunction);
7586 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
7587 xmlXPathFloorFunction);
7588 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
7589 xmlXPathLastFunction);
7590 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
7591 xmlXPathLangFunction);
7592 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
7593 xmlXPathLocalNameFunction);
7594 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
7595 xmlXPathNotFunction);
7596 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
7597 xmlXPathNameFunction);
7598 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
7599 xmlXPathNamespaceURIFunction);
7600 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
7601 xmlXPathNormalizeFunction);
7602 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
7603 xmlXPathNumberFunction);
7604 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
7605 xmlXPathPositionFunction);
7606 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
7607 xmlXPathRoundFunction);
7608 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
7609 xmlXPathStringFunction);
7610 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
7611 xmlXPathStringLengthFunction);
7612 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
7613 xmlXPathStartsWithFunction);
7614 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
7615 xmlXPathSubstringFunction);
7616 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
7617 xmlXPathSubstringBeforeFunction);
7618 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
7619 xmlXPathSubstringAfterFunction);
7620 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
7621 xmlXPathSumFunction);
7622 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
7623 xmlXPathTrueFunction);
7624 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
7625 xmlXPathTranslateFunction);
7626}
7627
7628#endif /* LIBXML_XPATH_ENABLED */