blob: 5b6e84c16f7035025de3d9fc634f57c80d71301b [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * xpath.c: XML Path Language implementation
3 * XPath is a language for addressing parts of an XML document,
4 * designed to be used by both XSLT and XPointer
5 *
6 * Reference: W3C Recommendation 16 November 1999
7 * http://www.w3.org/TR/1999/REC-xpath-19991116
8 * Public reference:
9 * http://www.w3.org/TR/xpath
10 *
11 * See COPYRIGHT for the status of this software
12 *
13 * Author: Daniel.Veillard@w3.org
14 *
15 * 14 Nov 2000 ht - truncated declaration of xmlXPathEvalRelativeLocationPath
16 * for VMS
17 */
18
Bjorn Reese70a9da52001-04-21 16:57:29 +000019#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000020#ifdef LIBXML_XPATH_ENABLED
21
Owen Taylor3473f882001-02-23 17:55:21 +000022#include <string.h>
23
24#ifdef HAVE_SYS_TYPES_H
25#include <sys/types.h>
26#endif
27#ifdef HAVE_MATH_H
28#include <math.h>
29#endif
30#ifdef HAVE_FLOAT_H
31#include <float.h>
32#endif
33#ifdef HAVE_IEEEFP_H
34#include <ieeefp.h>
35#endif
36#ifdef HAVE_NAN_H
37#include <nan.h>
38#endif
39#ifdef HAVE_CTYPE_H
40#include <ctype.h>
41#endif
Daniel Veillardb45c43b2001-04-28 17:02:11 +000042#if defined(__osf__) && defined(__GNUC__)
43#include <signal.h>
44#define FPE_WORKAROUND
45#endif
Owen Taylor3473f882001-02-23 17:55:21 +000046
47#include <libxml/xmlmemory.h>
48#include <libxml/tree.h>
49#include <libxml/valid.h>
50#include <libxml/xpath.h>
51#include <libxml/xpathInternals.h>
52#include <libxml/parserInternals.h>
53#include <libxml/hash.h>
54#ifdef LIBXML_XPTR_ENABLED
55#include <libxml/xpointer.h>
56#endif
57#ifdef LIBXML_DEBUG_ENABLED
58#include <libxml/debugXML.h>
59#endif
60#include <libxml/xmlerror.h>
61
62/* #define DEBUG */
63/* #define DEBUG_STEP */
64/* #define DEBUG_EXPR */
65
66void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
67double xmlXPathStringEvalNumber(const xmlChar *str);
68
Daniel Veillard9e7160d2001-03-18 23:17:47 +000069/************************************************************************
70 * *
71 * Floating point stuff *
72 * *
73 ************************************************************************/
74
Owen Taylor3473f882001-02-23 17:55:21 +000075/*
Owen Taylor3473f882001-02-23 17:55:21 +000076 * The lack of portability of this section of the libc is annoying !
77 */
78double xmlXPathNAN = 0;
79double xmlXPathPINF = 1;
80double xmlXPathNINF = -1;
81
82#ifndef isinf
83#ifndef HAVE_ISINF
84
85#if HAVE_FPCLASS
86
87int isinf(double d) {
88 fpclass_t type = fpclass(d);
89 switch (type) {
90 case FP_NINF:
91 return(-1);
92 case FP_PINF:
93 return(1);
94 }
95 return(0);
96}
97
98#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
99
100#if HAVE_FP_CLASS_H
101#include <fp_class.h>
102#endif
103
104int isinf(double d) {
105#if HAVE_FP_CLASS
106 int fpclass = fp_class(d);
107#else
108 int fpclass = fp_class_d(d);
109#endif
110 if (fpclass == FP_POS_INF)
111 return(1);
112 if (fpclass == FP_NEG_INF)
113 return(-1);
114 return(0);
115}
116
117#elif defined(HAVE_CLASS)
118
119int isinf(double d) {
120 int fpclass = class(d);
121 if (fpclass == FP_PLUS_INF)
122 return(1);
123 if (fpclass == FP_MINUS_INF)
124 return(-1);
125 return(0);
126}
127#elif defined(finite) || defined(HAVE_FINITE)
128int isinf(double x) { return !finite(x) && x==x; }
129#elif defined(HUGE_VAL)
130int isinf(double x)
131{
132 if (x == HUGE_VAL)
133 return(1);
134 if (x == -HUGE_VAL)
135 return(-1);
136 return(0);
137}
138#endif
139
140#endif /* ! HAVE_ISINF */
141#endif /* ! defined(isinf) */
142
143#ifndef isnan
144#ifndef HAVE_ISNAN
145
146#ifdef HAVE_ISNAND
147#define isnan(f) isnand(f)
148#endif /* HAVE_iSNAND */
149
150#endif /* ! HAVE_iSNAN */
151#endif /* ! defined(isnan) */
152
153/**
154 * xmlXPathInit:
155 *
156 * Initialize the XPath environment
157 */
158void
159xmlXPathInit(void) {
160 static int initialized = 0;
161
162 if (initialized) return;
163
Daniel Veillardb45c43b2001-04-28 17:02:11 +0000164#ifdef FPE_WORKAROUND
165 signal(SIGFPE, SIG_IGN);
166#endif
167
Daniel Veillard61d80a22001-04-27 17:13:01 +0000168#ifdef XPATH_USE_DIVISION_SHORTCUTS
169 xmlXPathNAN = 0;
170 xmlXPathNAN /= 0.0;
171 xmlXPathPINF = 1;
172 xmlXPathPINF /= 0.0;
173 xmlXPathNINF = -1;
174 xmlXPathNINF /= 0.0;
175#else
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000176 xmlXPathNAN = 0.0 / 0.0;
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000177 xmlXPathPINF = 1 / 0.0;
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000178 xmlXPathNINF = -1 / 0.0;
Daniel Veillard61d80a22001-04-27 17:13:01 +0000179#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000180
181 initialized = 1;
182}
183
184/************************************************************************
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000185 * *
186 * Parser Types *
187 * *
188 ************************************************************************/
189
190/*
191 * Types are private:
192 */
193
194typedef enum {
195 XPATH_OP_END=0,
196 XPATH_OP_AND,
197 XPATH_OP_OR,
198 XPATH_OP_EQUAL,
199 XPATH_OP_CMP,
200 XPATH_OP_PLUS,
201 XPATH_OP_MULT,
202 XPATH_OP_UNION,
203 XPATH_OP_ROOT,
204 XPATH_OP_NODE,
205 XPATH_OP_RESET,
206 XPATH_OP_COLLECT,
207 XPATH_OP_VALUE,
208 XPATH_OP_VARIABLE,
209 XPATH_OP_FUNCTION,
210 XPATH_OP_ARG,
211 XPATH_OP_PREDICATE,
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000212 XPATH_OP_FILTER,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000213 XPATH_OP_SORT
214#ifdef LIBXML_XPTR_ENABLED
215 ,XPATH_OP_RANGETO
216#endif
217} xmlXPathOp;
218
219typedef enum {
220 AXIS_ANCESTOR = 1,
221 AXIS_ANCESTOR_OR_SELF,
222 AXIS_ATTRIBUTE,
223 AXIS_CHILD,
224 AXIS_DESCENDANT,
225 AXIS_DESCENDANT_OR_SELF,
226 AXIS_FOLLOWING,
227 AXIS_FOLLOWING_SIBLING,
228 AXIS_NAMESPACE,
229 AXIS_PARENT,
230 AXIS_PRECEDING,
231 AXIS_PRECEDING_SIBLING,
232 AXIS_SELF
233} xmlXPathAxisVal;
234
235typedef enum {
236 NODE_TEST_NONE = 0,
237 NODE_TEST_TYPE = 1,
238 NODE_TEST_PI = 2,
239 NODE_TEST_ALL = 3,
240 NODE_TEST_NS = 4,
241 NODE_TEST_NAME = 5
242} xmlXPathTestVal;
243
244typedef enum {
245 NODE_TYPE_NODE = 0,
246 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
247 NODE_TYPE_TEXT = XML_TEXT_NODE,
248 NODE_TYPE_PI = XML_PI_NODE
249} xmlXPathTypeVal;
250
251
252typedef struct _xmlXPathStepOp xmlXPathStepOp;
253typedef xmlXPathStepOp *xmlXPathStepOpPtr;
254struct _xmlXPathStepOp {
255 xmlXPathOp op;
256 int ch1;
257 int ch2;
258 int value;
259 int value2;
260 int value3;
261 void *value4;
262 void *value5;
Daniel Veillarde39a93d2001-04-28 14:35:02 +0000263 void *cache;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000264};
265
266struct _xmlXPathCompExpr {
267 int nbStep;
268 int maxStep;
269 xmlXPathStepOp *steps; /* ops for computation */
270 int last;
271};
272
273/************************************************************************
274 * *
275 * Parser Type functions *
276 * *
277 ************************************************************************/
278
279/**
280 * xmlXPathNewCompExpr:
281 *
282 * Create a new Xpath component
283 *
284 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
285 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000286static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000287xmlXPathNewCompExpr(void) {
288 xmlXPathCompExprPtr cur;
289
290 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
291 if (cur == NULL) {
292 xmlGenericError(xmlGenericErrorContext,
293 "xmlXPathNewCompExpr : malloc failed\n");
294 return(NULL);
295 }
296 memset(cur, 0, sizeof(xmlXPathCompExpr));
297 cur->maxStep = 10;
298 cur->nbStep = 0;
299 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
300 sizeof(xmlXPathStepOp));
301 if (cur->steps == NULL) {
302 xmlGenericError(xmlGenericErrorContext,
303 "xmlXPathNewCompExpr : malloc failed\n");
304 xmlFree(cur);
305 return(NULL);
306 }
307 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
308 cur->last = -1;
309 return(cur);
310}
311
312/**
313 * xmlXPathFreeCompExpr:
314 * @comp: an XPATH comp
315 *
316 * Free up the memory allocated by @comp
317 */
318void
319xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) {
320 xmlXPathStepOpPtr op;
321 int i;
322
323 if (comp == NULL)
324 return;
325 for (i = 0;i < comp->nbStep;i++) {
326 op = &comp->steps[i];
327 if (op->value4 != NULL) {
328 if (op->op == XPATH_OP_VALUE)
329 xmlXPathFreeObject(op->value4);
330 else
331 xmlFree(op->value4);
332 }
333 if (op->value5 != NULL)
334 xmlFree(op->value5);
335 }
336 if (comp->steps != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000337 xmlFree(comp->steps);
338 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000339 xmlFree(comp);
340}
341
342/**
343 * xmlXPathCompExprAdd:
344 * @comp: the compiled expression
345 * @ch1: first child index
346 * @ch2: second child index
347 * @op: an op
348 * @value: the first int value
349 * @value2: the second int value
350 * @value3: the third int value
351 * @value4: the first string value
352 * @value5: the second string value
353 *
354 * Add an step to an XPath Compiled Expression
355 *
356 * Returns -1 in case of failure, the index otherwise
357 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000358static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000359xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
360 xmlXPathOp op, int value,
361 int value2, int value3, void *value4, void *value5) {
362 if (comp->nbStep >= comp->maxStep) {
363 xmlXPathStepOp *real;
364
365 comp->maxStep *= 2;
366 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
367 comp->maxStep * sizeof(xmlXPathStepOp));
368 if (real == NULL) {
369 comp->maxStep /= 2;
370 xmlGenericError(xmlGenericErrorContext,
371 "xmlXPathCompExprAdd : realloc failed\n");
372 return(-1);
373 }
374 comp->steps = real;
375 }
376 comp->last = comp->nbStep;
377 comp->steps[comp->nbStep].ch1 = ch1;
378 comp->steps[comp->nbStep].ch2 = ch2;
379 comp->steps[comp->nbStep].op = op;
380 comp->steps[comp->nbStep].value = value;
381 comp->steps[comp->nbStep].value2 = value2;
382 comp->steps[comp->nbStep].value3 = value3;
383 comp->steps[comp->nbStep].value4 = value4;
384 comp->steps[comp->nbStep].value5 = value5;
Daniel Veillarde39a93d2001-04-28 14:35:02 +0000385 comp->steps[comp->nbStep].cache = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000386 return(comp->nbStep++);
387}
388
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000389#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
390 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
391 (op), (val), (val2), (val3), (val4), (val5))
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000392#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
393 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
394 (op), (val), (val2), (val3), (val4), (val5))
395
396#define PUSH_LEAVE_EXPR(op, val, val2) \
397xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
398
399#define PUSH_UNARY_EXPR(op, ch, val, val2) \
400xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
401
402#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
403xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
404
405/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000406 * *
407 * Debugging related functions *
408 * *
409 ************************************************************************/
410
411#define TODO \
412 xmlGenericError(xmlGenericErrorContext, \
413 "Unimplemented block at %s:%d\n", \
414 __FILE__, __LINE__);
415
416#define STRANGE \
417 xmlGenericError(xmlGenericErrorContext, \
418 "Internal error at %s:%d\n", \
419 __FILE__, __LINE__);
420
421#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000422static void
423xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000424 int i;
425 char shift[100];
426
427 for (i = 0;((i < depth) && (i < 25));i++)
428 shift[2 * i] = shift[2 * i + 1] = ' ';
429 shift[2 * i] = shift[2 * i + 1] = 0;
430 if (cur == NULL) {
431 fprintf(output, shift);
432 fprintf(output, "Node is NULL !\n");
433 return;
434
435 }
436
437 if ((cur->type == XML_DOCUMENT_NODE) ||
438 (cur->type == XML_HTML_DOCUMENT_NODE)) {
439 fprintf(output, shift);
440 fprintf(output, " /\n");
441 } else if (cur->type == XML_ATTRIBUTE_NODE)
442 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
443 else
444 xmlDebugDumpOneNode(output, cur, depth);
445}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000446static void
447xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000448 xmlNodePtr tmp;
449 int i;
450 char shift[100];
451
452 for (i = 0;((i < depth) && (i < 25));i++)
453 shift[2 * i] = shift[2 * i + 1] = ' ';
454 shift[2 * i] = shift[2 * i + 1] = 0;
455 if (cur == NULL) {
456 fprintf(output, shift);
457 fprintf(output, "Node is NULL !\n");
458 return;
459
460 }
461
462 while (cur != NULL) {
463 tmp = cur;
464 cur = cur->next;
465 xmlDebugDumpOneNode(output, tmp, depth);
466 }
467}
Owen Taylor3473f882001-02-23 17:55:21 +0000468
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000469static void
470xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000471 int i;
472 char shift[100];
473
474 for (i = 0;((i < depth) && (i < 25));i++)
475 shift[2 * i] = shift[2 * i + 1] = ' ';
476 shift[2 * i] = shift[2 * i + 1] = 0;
477
478 if (cur == NULL) {
479 fprintf(output, shift);
480 fprintf(output, "NodeSet is NULL !\n");
481 return;
482
483 }
484
Daniel Veillard911f49a2001-04-07 15:39:35 +0000485 if (cur != NULL) {
486 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
487 for (i = 0;i < cur->nodeNr;i++) {
488 fprintf(output, shift);
489 fprintf(output, "%d", i + 1);
490 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
491 }
Owen Taylor3473f882001-02-23 17:55:21 +0000492 }
493}
494
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000495static void
496xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000497 int i;
498 char shift[100];
499
500 for (i = 0;((i < depth) && (i < 25));i++)
501 shift[2 * i] = shift[2 * i + 1] = ' ';
502 shift[2 * i] = shift[2 * i + 1] = 0;
503
504 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
505 fprintf(output, shift);
506 fprintf(output, "Value Tree is NULL !\n");
507 return;
508
509 }
510
511 fprintf(output, shift);
512 fprintf(output, "%d", i + 1);
513 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
514}
Owen Taylor3473f882001-02-23 17:55:21 +0000515#if defined(LIBXML_XPTR_ENABLED)
516void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000517static void
518xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000519 int i;
520 char shift[100];
521
522 for (i = 0;((i < depth) && (i < 25));i++)
523 shift[2 * i] = shift[2 * i + 1] = ' ';
524 shift[2 * i] = shift[2 * i + 1] = 0;
525
526 if (cur == NULL) {
527 fprintf(output, shift);
528 fprintf(output, "LocationSet is NULL !\n");
529 return;
530
531 }
532
533 for (i = 0;i < cur->locNr;i++) {
534 fprintf(output, shift);
535 fprintf(output, "%d : ", i + 1);
536 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
537 }
538}
539#endif
540
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000541/**
542 * xmlXPathDebugDumpObject:
543 * @output: the FILE * to dump the output
544 * @cur: the object to inspect
545 * @depth: indentation level
546 *
547 * Dump the content of the object for debugging purposes
548 */
549void
550xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000551 int i;
552 char shift[100];
553
554 for (i = 0;((i < depth) && (i < 25));i++)
555 shift[2 * i] = shift[2 * i + 1] = ' ';
556 shift[2 * i] = shift[2 * i + 1] = 0;
557
558 fprintf(output, shift);
559
560 if (cur == NULL) {
561 fprintf(output, "Object is empty (NULL)\n");
562 return;
563 }
564 switch(cur->type) {
565 case XPATH_UNDEFINED:
566 fprintf(output, "Object is uninitialized\n");
567 break;
568 case XPATH_NODESET:
569 fprintf(output, "Object is a Node Set :\n");
570 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
571 break;
572 case XPATH_XSLT_TREE:
573 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000574 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000575 break;
576 case XPATH_BOOLEAN:
577 fprintf(output, "Object is a Boolean : ");
578 if (cur->boolval) fprintf(output, "true\n");
579 else fprintf(output, "false\n");
580 break;
581 case XPATH_NUMBER:
582 fprintf(output, "Object is a number : %0g\n", cur->floatval);
583 break;
584 case XPATH_STRING:
585 fprintf(output, "Object is a string : ");
586 xmlDebugDumpString(output, cur->stringval);
587 fprintf(output, "\n");
588 break;
589 case XPATH_POINT:
590 fprintf(output, "Object is a point : index %d in node", cur->index);
591 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
592 fprintf(output, "\n");
593 break;
594 case XPATH_RANGE:
595 if ((cur->user2 == NULL) ||
596 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
597 fprintf(output, "Object is a collapsed range :\n");
598 fprintf(output, shift);
599 if (cur->index >= 0)
600 fprintf(output, "index %d in ", cur->index);
601 fprintf(output, "node\n");
602 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
603 depth + 1);
604 } else {
605 fprintf(output, "Object is a range :\n");
606 fprintf(output, shift);
607 fprintf(output, "From ");
608 if (cur->index >= 0)
609 fprintf(output, "index %d in ", cur->index);
610 fprintf(output, "node\n");
611 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
612 depth + 1);
613 fprintf(output, shift);
614 fprintf(output, "To ");
615 if (cur->index2 >= 0)
616 fprintf(output, "index %d in ", cur->index2);
617 fprintf(output, "node\n");
618 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
619 depth + 1);
620 fprintf(output, "\n");
621 }
622 break;
623 case XPATH_LOCATIONSET:
624#if defined(LIBXML_XPTR_ENABLED)
625 fprintf(output, "Object is a Location Set:\n");
626 xmlXPathDebugDumpLocationSet(output,
627 (xmlLocationSetPtr) cur->user, depth);
628#endif
629 break;
630 case XPATH_USERS:
631 fprintf(output, "Object is user defined\n");
632 break;
633 }
634}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000635
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000636static void
637xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000638 xmlXPathStepOpPtr op, int depth) {
639 int i;
640 char shift[100];
641
642 for (i = 0;((i < depth) && (i < 25));i++)
643 shift[2 * i] = shift[2 * i + 1] = ' ';
644 shift[2 * i] = shift[2 * i + 1] = 0;
645
646 fprintf(output, shift);
647 if (op == NULL) {
648 fprintf(output, "Step is NULL\n");
649 return;
650 }
651 switch (op->op) {
652 case XPATH_OP_END:
653 fprintf(output, "END"); break;
654 case XPATH_OP_AND:
655 fprintf(output, "AND"); break;
656 case XPATH_OP_OR:
657 fprintf(output, "OR"); break;
658 case XPATH_OP_EQUAL:
659 if (op->value)
660 fprintf(output, "EQUAL =");
661 else
662 fprintf(output, "EQUAL !=");
663 break;
664 case XPATH_OP_CMP:
665 if (op->value)
666 fprintf(output, "CMP <");
667 else
668 fprintf(output, "CMP >");
669 if (!op->value2)
670 fprintf(output, "=");
671 break;
672 case XPATH_OP_PLUS:
673 if (op->value == 0)
674 fprintf(output, "PLUS -");
675 else if (op->value == 1)
676 fprintf(output, "PLUS +");
677 else if (op->value == 2)
678 fprintf(output, "PLUS unary -");
679 else if (op->value == 3)
680 fprintf(output, "PLUS unary - -");
681 break;
682 case XPATH_OP_MULT:
683 if (op->value == 0)
684 fprintf(output, "MULT *");
685 else if (op->value == 1)
686 fprintf(output, "MULT div");
687 else
688 fprintf(output, "MULT mod");
689 break;
690 case XPATH_OP_UNION:
691 fprintf(output, "UNION"); break;
692 case XPATH_OP_ROOT:
693 fprintf(output, "ROOT"); break;
694 case XPATH_OP_NODE:
695 fprintf(output, "NODE"); break;
696 case XPATH_OP_RESET:
697 fprintf(output, "RESET"); break;
698 case XPATH_OP_SORT:
699 fprintf(output, "SORT"); break;
700 case XPATH_OP_COLLECT: {
701 xmlXPathAxisVal axis = op->value;
702 xmlXPathTestVal test = op->value2;
703 xmlXPathTypeVal type = op->value3;
704 const xmlChar *prefix = op->value4;
705 const xmlChar *name = op->value5;
706
707 fprintf(output, "COLLECT ");
708 switch (axis) {
709 case AXIS_ANCESTOR:
710 fprintf(output, " 'ancestors' "); break;
711 case AXIS_ANCESTOR_OR_SELF:
712 fprintf(output, " 'ancestors-or-self' "); break;
713 case AXIS_ATTRIBUTE:
714 fprintf(output, " 'attributes' "); break;
715 case AXIS_CHILD:
716 fprintf(output, " 'child' "); break;
717 case AXIS_DESCENDANT:
718 fprintf(output, " 'descendant' "); break;
719 case AXIS_DESCENDANT_OR_SELF:
720 fprintf(output, " 'descendant-or-self' "); break;
721 case AXIS_FOLLOWING:
722 fprintf(output, " 'following' "); break;
723 case AXIS_FOLLOWING_SIBLING:
724 fprintf(output, " 'following-siblings' "); break;
725 case AXIS_NAMESPACE:
726 fprintf(output, " 'namespace' "); break;
727 case AXIS_PARENT:
728 fprintf(output, " 'parent' "); break;
729 case AXIS_PRECEDING:
730 fprintf(output, " 'preceding' "); break;
731 case AXIS_PRECEDING_SIBLING:
732 fprintf(output, " 'preceding-sibling' "); break;
733 case AXIS_SELF:
734 fprintf(output, " 'self' "); break;
735 }
736 switch (test) {
737 case NODE_TEST_NONE:
738 fprintf(output, "'none' "); break;
739 case NODE_TEST_TYPE:
740 fprintf(output, "'type' "); break;
741 case NODE_TEST_PI:
742 fprintf(output, "'PI' "); break;
743 case NODE_TEST_ALL:
744 fprintf(output, "'all' "); break;
745 case NODE_TEST_NS:
746 fprintf(output, "'namespace' "); break;
747 case NODE_TEST_NAME:
748 fprintf(output, "'name' "); break;
749 }
750 switch (type) {
751 case NODE_TYPE_NODE:
752 fprintf(output, "'node' "); break;
753 case NODE_TYPE_COMMENT:
754 fprintf(output, "'comment' "); break;
755 case NODE_TYPE_TEXT:
756 fprintf(output, "'text' "); break;
757 case NODE_TYPE_PI:
758 fprintf(output, "'PI' "); break;
759 }
760 if (prefix != NULL)
761 fprintf(output, "%s:", prefix);
762 if (name != NULL)
763 fprintf(output, "%s", name);
764 break;
765
766 }
767 case XPATH_OP_VALUE: {
768 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
769
770 fprintf(output, "ELEM ");
771 xmlXPathDebugDumpObject(output, object, 0);
772 goto finish;
773 }
774 case XPATH_OP_VARIABLE: {
775 const xmlChar *prefix = op->value5;
776 const xmlChar *name = op->value4;
777
778 if (prefix != NULL)
779 fprintf(output, "VARIABLE %s:%s", prefix, name);
780 else
781 fprintf(output, "VARIABLE %s", name);
782 break;
783 }
784 case XPATH_OP_FUNCTION: {
785 int nbargs = op->value;
786 const xmlChar *prefix = op->value5;
787 const xmlChar *name = op->value4;
788
789 if (prefix != NULL)
790 fprintf(output, "FUNCTION %s:%s(%d args)",
791 prefix, name, nbargs);
792 else
793 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
794 break;
795 }
796 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
797 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000798 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +0000799#ifdef LIBXML_XPTR_ENABLED
800 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
801#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000802 default:
803 fprintf(output, "UNKNOWN %d\n", op->op); return;
804 }
805 fprintf(output, "\n");
806finish:
807 if (op->ch1 >= 0)
808 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
809 if (op->ch2 >= 0)
810 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
811}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000812
813void
814xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
815 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000816 int i;
817 char shift[100];
818
819 for (i = 0;((i < depth) && (i < 25));i++)
820 shift[2 * i] = shift[2 * i + 1] = ' ';
821 shift[2 * i] = shift[2 * i + 1] = 0;
822
823 fprintf(output, shift);
824
825 if (comp == NULL) {
826 fprintf(output, "Compiled Expression is NULL\n");
827 return;
828 }
829 fprintf(output, "Compiled Expression : %d elements\n",
830 comp->nbStep);
831 i = comp->last;
832 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
833}
Owen Taylor3473f882001-02-23 17:55:21 +0000834#endif
835
836/************************************************************************
837 * *
838 * Parser stacks related functions and macros *
839 * *
840 ************************************************************************/
841
842/*
843 * Generic function for accessing stacks in the Parser Context
844 */
845
846#define PUSH_AND_POP(type, name) \
847extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
848 if (ctxt->name##Nr >= ctxt->name##Max) { \
849 ctxt->name##Max *= 2; \
850 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
851 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
852 if (ctxt->name##Tab == NULL) { \
853 xmlGenericError(xmlGenericErrorContext, \
854 "realloc failed !\n"); \
855 return(0); \
856 } \
857 } \
858 ctxt->name##Tab[ctxt->name##Nr] = value; \
859 ctxt->name = value; \
860 return(ctxt->name##Nr++); \
861} \
862extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
863 type ret; \
864 if (ctxt->name##Nr <= 0) return(0); \
865 ctxt->name##Nr--; \
866 if (ctxt->name##Nr > 0) \
867 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
868 else \
869 ctxt->name = NULL; \
870 ret = ctxt->name##Tab[ctxt->name##Nr]; \
871 ctxt->name##Tab[ctxt->name##Nr] = 0; \
872 return(ret); \
873} \
874
875PUSH_AND_POP(xmlXPathObjectPtr, value)
876
877/*
878 * Macros for accessing the content. Those should be used only by the parser,
879 * and not exported.
880 *
881 * Dirty macros, i.e. one need to make assumption on the context to use them
882 *
883 * CUR_PTR return the current pointer to the xmlChar to be parsed.
884 * CUR returns the current xmlChar value, i.e. a 8 bit value
885 * in ISO-Latin or UTF-8.
886 * This should be used internally by the parser
887 * only to compare to ASCII values otherwise it would break when
888 * running with UTF-8 encoding.
889 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
890 * to compare on ASCII based substring.
891 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
892 * strings within the parser.
893 * CURRENT Returns the current char value, with the full decoding of
894 * UTF-8 if we are using this mode. It returns an int.
895 * NEXT Skip to the next character, this does the proper decoding
896 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
897 * It returns the pointer to the current xmlChar.
898 */
899
900#define CUR (*ctxt->cur)
901#define SKIP(val) ctxt->cur += (val)
902#define NXT(val) ctxt->cur[(val)]
903#define CUR_PTR ctxt->cur
Daniel Veillard61d80a22001-04-27 17:13:01 +0000904#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
905
906#define COPY_BUF(l,b,i,v) \
907 if (l == 1) b[i++] = (xmlChar) v; \
908 else i += xmlCopyChar(l,&b[i],v)
909
910#define NEXTL(l) ctxt->cur += l
Owen Taylor3473f882001-02-23 17:55:21 +0000911
912#define SKIP_BLANKS \
913 while (IS_BLANK(*(ctxt->cur))) NEXT
914
915#define CURRENT (*ctxt->cur)
916#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
917
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000918
919#ifndef DBL_DIG
920#define DBL_DIG 16
921#endif
922#ifndef DBL_EPSILON
923#define DBL_EPSILON 1E-9
924#endif
925
926#define UPPER_DOUBLE 1E9
927#define LOWER_DOUBLE 1E-5
928
929#define INTEGER_DIGITS DBL_DIG
930#define FRACTION_DIGITS (DBL_DIG + 1)
931#define EXPONENT_DIGITS (3 + 2)
932
933/**
934 * xmlXPathFormatNumber:
935 * @number: number to format
936 * @buffer: output buffer
937 * @buffersize: size of output buffer
938 *
939 * Convert the number into a string representation.
940 */
941static void
942xmlXPathFormatNumber(double number, char buffer[], int buffersize)
943{
944 switch (isinf(number)) {
945 case 1:
946 if (buffersize > (int)sizeof("+Infinity"))
947 sprintf(buffer, "+Infinity");
948 break;
949 case -1:
950 if (buffersize > (int)sizeof("-Infinity"))
951 sprintf(buffer, "-Infinity");
952 break;
953 default:
954 if (isnan(number)) {
955 if (buffersize > (int)sizeof("NaN"))
956 sprintf(buffer, "NaN");
957 } else {
Bjorn Reese70a9da52001-04-21 16:57:29 +0000958 /* 3 is sign, decimal point, and terminating zero */
959 char work[DBL_DIG + EXPONENT_DIGITS + 3];
960 int integer_place, fraction_place;
961 char *ptr;
962 char *after_fraction;
963 double absolute_value;
964 int size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000965
Bjorn Reese70a9da52001-04-21 16:57:29 +0000966 absolute_value = fabs(number);
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000967
Bjorn Reese70a9da52001-04-21 16:57:29 +0000968 /*
969 * First choose format - scientific or regular floating point.
970 * In either case, result is in work, and after_fraction points
971 * just past the fractional part.
972 */
973 if ( ((absolute_value > UPPER_DOUBLE) ||
974 (absolute_value < LOWER_DOUBLE)) &&
975 (absolute_value != 0.0) ) {
976 /* Use scientific notation */
977 integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
978 fraction_place = DBL_DIG - 1;
979 snprintf(work, sizeof(work),"%*.*e",
980 integer_place, fraction_place, number);
981 after_fraction = strchr(work + DBL_DIG, 'e');
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000982 }
Bjorn Reese70a9da52001-04-21 16:57:29 +0000983 else {
984 /* Use regular notation */
985 integer_place = 1 + (int)log10(absolute_value);
986 fraction_place = (integer_place > 0)
987 ? DBL_DIG - integer_place
988 : DBL_DIG;
989 size = snprintf(work, sizeof(work), "%0.*f",
990 fraction_place, number);
991 after_fraction = work + size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000992 }
993
Bjorn Reese70a9da52001-04-21 16:57:29 +0000994 /* Remove fractional trailing zeroes */
995 ptr = after_fraction;
996 while (*(--ptr) == '0')
997 ;
998 if (*ptr != '.')
999 ptr++;
1000 strcpy(ptr, after_fraction);
1001
1002 /* Finally copy result back to caller */
1003 size = strlen(work) + 1;
1004 if (size > buffersize) {
1005 work[buffersize - 1] = 0;
1006 size = buffersize;
1007 }
1008 memcpy(buffer, work, size);
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001009 }
1010 break;
1011 }
1012}
1013
Owen Taylor3473f882001-02-23 17:55:21 +00001014/************************************************************************
1015 * *
1016 * Error handling routines *
1017 * *
1018 ************************************************************************/
1019
1020
1021const char *xmlXPathErrorMessages[] = {
1022 "Ok",
1023 "Number encoding",
1024 "Unfinished litteral",
1025 "Start of litteral",
1026 "Expected $ for variable reference",
1027 "Undefined variable",
1028 "Invalid predicate",
1029 "Invalid expression",
1030 "Missing closing curly brace",
1031 "Unregistered function",
1032 "Invalid operand",
1033 "Invalid type",
1034 "Invalid number of arguments",
1035 "Invalid context size",
1036 "Invalid context position",
1037 "Memory allocation error",
1038 "Syntax error",
1039 "Resource error",
1040 "Sub resource error",
Daniel Veillard61d80a22001-04-27 17:13:01 +00001041 "Undefined namespace prefix",
1042 "Encoding error",
1043 "Char out of XML range"
Owen Taylor3473f882001-02-23 17:55:21 +00001044};
1045
1046/**
1047 * xmlXPathError:
1048 * @ctxt: the XPath Parser context
1049 * @file: the file name
1050 * @line: the line number
1051 * @no: the error number
1052 *
1053 * Create a new xmlNodeSetPtr of type double and of value @val
1054 *
1055 * Returns the newly created object.
1056 */
1057void
1058xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1059 int line, int no) {
1060 int n;
1061 const xmlChar *cur;
1062 const xmlChar *base;
1063
1064 xmlGenericError(xmlGenericErrorContext,
1065 "Error %s:%d: %s\n", file, line,
1066 xmlXPathErrorMessages[no]);
1067
1068 cur = ctxt->cur;
1069 base = ctxt->base;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001070 if ((cur == NULL) || (base == NULL))
1071 return;
1072
Owen Taylor3473f882001-02-23 17:55:21 +00001073 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1074 cur--;
1075 }
1076 n = 0;
1077 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1078 cur--;
1079 if ((*cur == '\n') || (*cur == '\r')) cur++;
1080 base = cur;
1081 n = 0;
1082 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1083 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1084 n++;
1085 }
1086 xmlGenericError(xmlGenericErrorContext, "\n");
1087 cur = ctxt->cur;
1088 while ((*cur == '\n') || (*cur == '\r'))
1089 cur--;
1090 n = 0;
1091 while ((cur != base) && (n++ < 80)) {
1092 xmlGenericError(xmlGenericErrorContext, " ");
1093 base++;
1094 }
1095 xmlGenericError(xmlGenericErrorContext,"^\n");
1096}
1097
1098
1099/************************************************************************
1100 * *
1101 * Routines to handle NodeSets *
1102 * *
1103 ************************************************************************/
1104
1105/**
1106 * xmlXPathCmpNodes:
1107 * @node1: the first node
1108 * @node2: the second node
1109 *
1110 * Compare two nodes w.r.t document order
1111 *
1112 * Returns -2 in case of error 1 if first point < second point, 0 if
1113 * that's the same node, -1 otherwise
1114 */
1115int
1116xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1117 int depth1, depth2;
1118 xmlNodePtr cur, root;
1119
1120 if ((node1 == NULL) || (node2 == NULL))
1121 return(-2);
1122 /*
1123 * a couple of optimizations which will avoid computations in most cases
1124 */
1125 if (node1 == node2)
1126 return(0);
Daniel Veillardb33c2012001-04-25 12:59:04 +00001127 if ((node1->type == XML_NAMESPACE_DECL) ||
1128 (node2->type == XML_NAMESPACE_DECL))
1129 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00001130 if (node1 == node2->prev)
1131 return(1);
1132 if (node1 == node2->next)
1133 return(-1);
1134
1135 /*
1136 * compute depth to root
1137 */
1138 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1139 if (cur == node1)
1140 return(1);
1141 depth2++;
1142 }
1143 root = cur;
1144 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1145 if (cur == node2)
1146 return(-1);
1147 depth1++;
1148 }
1149 /*
1150 * Distinct document (or distinct entities :-( ) case.
1151 */
1152 if (root != cur) {
1153 return(-2);
1154 }
1155 /*
1156 * get the nearest common ancestor.
1157 */
1158 while (depth1 > depth2) {
1159 depth1--;
1160 node1 = node1->parent;
1161 }
1162 while (depth2 > depth1) {
1163 depth2--;
1164 node2 = node2->parent;
1165 }
1166 while (node1->parent != node2->parent) {
1167 node1 = node1->parent;
1168 node2 = node2->parent;
1169 /* should not happen but just in case ... */
1170 if ((node1 == NULL) || (node2 == NULL))
1171 return(-2);
1172 }
1173 /*
1174 * Find who's first.
1175 */
1176 if (node1 == node2->next)
1177 return(-1);
1178 for (cur = node1->next;cur != NULL;cur = cur->next)
1179 if (cur == node2)
1180 return(1);
1181 return(-1); /* assume there is no sibling list corruption */
1182}
1183
1184/**
1185 * xmlXPathNodeSetSort:
1186 * @set: the node set
1187 *
1188 * Sort the node set in document order
1189 */
1190void
1191xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001192 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001193 xmlNodePtr tmp;
1194
1195 if (set == NULL)
1196 return;
1197
1198 /* Use Shell's sort to sort the node-set */
1199 len = set->nodeNr;
1200 for (incr = len / 2; incr > 0; incr /= 2) {
1201 for (i = incr; i < len; i++) {
1202 j = i - incr;
1203 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001204 if (xmlXPathCmpNodes(set->nodeTab[j],
1205 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001206 tmp = set->nodeTab[j];
1207 set->nodeTab[j] = set->nodeTab[j + incr];
1208 set->nodeTab[j + incr] = tmp;
1209 j -= incr;
1210 } else
1211 break;
1212 }
1213 }
1214 }
1215}
1216
1217#define XML_NODESET_DEFAULT 10
1218/**
1219 * xmlXPathNodeSetCreate:
1220 * @val: an initial xmlNodePtr, or NULL
1221 *
1222 * Create a new xmlNodeSetPtr of type double and of value @val
1223 *
1224 * Returns the newly created object.
1225 */
1226xmlNodeSetPtr
1227xmlXPathNodeSetCreate(xmlNodePtr val) {
1228 xmlNodeSetPtr ret;
1229
1230 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1231 if (ret == NULL) {
1232 xmlGenericError(xmlGenericErrorContext,
1233 "xmlXPathNewNodeSet: out of memory\n");
1234 return(NULL);
1235 }
1236 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1237 if (val != NULL) {
1238 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1239 sizeof(xmlNodePtr));
1240 if (ret->nodeTab == NULL) {
1241 xmlGenericError(xmlGenericErrorContext,
1242 "xmlXPathNewNodeSet: out of memory\n");
1243 return(NULL);
1244 }
1245 memset(ret->nodeTab, 0 ,
1246 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1247 ret->nodeMax = XML_NODESET_DEFAULT;
1248 ret->nodeTab[ret->nodeNr++] = val;
1249 }
1250 return(ret);
1251}
1252
1253/**
1254 * xmlXPathNodeSetAdd:
1255 * @cur: the initial node set
1256 * @val: a new xmlNodePtr
1257 *
1258 * add a new xmlNodePtr ot an existing NodeSet
1259 */
1260void
1261xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1262 int i;
1263
1264 if (val == NULL) return;
1265
1266 /*
1267 * check against doublons
1268 */
1269 for (i = 0;i < cur->nodeNr;i++)
1270 if (cur->nodeTab[i] == val) return;
1271
1272 /*
1273 * grow the nodeTab if needed
1274 */
1275 if (cur->nodeMax == 0) {
1276 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1277 sizeof(xmlNodePtr));
1278 if (cur->nodeTab == NULL) {
1279 xmlGenericError(xmlGenericErrorContext,
1280 "xmlXPathNodeSetAdd: out of memory\n");
1281 return;
1282 }
1283 memset(cur->nodeTab, 0 ,
1284 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1285 cur->nodeMax = XML_NODESET_DEFAULT;
1286 } else if (cur->nodeNr == cur->nodeMax) {
1287 xmlNodePtr *temp;
1288
1289 cur->nodeMax *= 2;
1290 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1291 sizeof(xmlNodePtr));
1292 if (temp == NULL) {
1293 xmlGenericError(xmlGenericErrorContext,
1294 "xmlXPathNodeSetAdd: out of memory\n");
1295 return;
1296 }
1297 cur->nodeTab = temp;
1298 }
1299 cur->nodeTab[cur->nodeNr++] = val;
1300}
1301
1302/**
1303 * xmlXPathNodeSetAddUnique:
1304 * @cur: the initial node set
1305 * @val: a new xmlNodePtr
1306 *
1307 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1308 * when we are sure the node is not already in the set.
1309 */
1310void
1311xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1312 if (val == NULL) return;
1313
1314 /*
1315 * grow the nodeTab if needed
1316 */
1317 if (cur->nodeMax == 0) {
1318 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1319 sizeof(xmlNodePtr));
1320 if (cur->nodeTab == NULL) {
1321 xmlGenericError(xmlGenericErrorContext,
1322 "xmlXPathNodeSetAddUnique: out of memory\n");
1323 return;
1324 }
1325 memset(cur->nodeTab, 0 ,
1326 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1327 cur->nodeMax = XML_NODESET_DEFAULT;
1328 } else if (cur->nodeNr == cur->nodeMax) {
1329 xmlNodePtr *temp;
1330
1331 cur->nodeMax *= 2;
1332 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1333 sizeof(xmlNodePtr));
1334 if (temp == NULL) {
1335 xmlGenericError(xmlGenericErrorContext,
1336 "xmlXPathNodeSetAddUnique: out of memory\n");
1337 return;
1338 }
1339 cur->nodeTab = temp;
1340 }
1341 cur->nodeTab[cur->nodeNr++] = val;
1342}
1343
1344/**
1345 * xmlXPathNodeSetMerge:
1346 * @val1: the first NodeSet or NULL
1347 * @val2: the second NodeSet
1348 *
1349 * Merges two nodesets, all nodes from @val2 are added to @val1
1350 * if @val1 is NULL, a new set is created and copied from @val2
1351 *
1352 * Returns val1 once extended or NULL in case of error.
1353 */
1354xmlNodeSetPtr
1355xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001356 int i, j, initNr, skip;
Owen Taylor3473f882001-02-23 17:55:21 +00001357
1358 if (val2 == NULL) return(val1);
1359 if (val1 == NULL) {
1360 val1 = xmlXPathNodeSetCreate(NULL);
1361 }
1362
1363 initNr = val1->nodeNr;
1364
1365 for (i = 0;i < val2->nodeNr;i++) {
1366 /*
1367 * check against doublons
1368 */
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001369 skip = 0;
1370 for (j = 0; j < initNr; j++) {
1371 if (val1->nodeTab[j] == val2->nodeTab[i]) {
1372 skip = 1;
1373 break;
1374 }
1375 }
1376 if (skip)
1377 continue;
Owen Taylor3473f882001-02-23 17:55:21 +00001378
1379 /*
1380 * grow the nodeTab if needed
1381 */
1382 if (val1->nodeMax == 0) {
1383 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1384 sizeof(xmlNodePtr));
1385 if (val1->nodeTab == NULL) {
1386 xmlGenericError(xmlGenericErrorContext,
1387 "xmlXPathNodeSetMerge: out of memory\n");
1388 return(NULL);
1389 }
1390 memset(val1->nodeTab, 0 ,
1391 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1392 val1->nodeMax = XML_NODESET_DEFAULT;
1393 } else if (val1->nodeNr == val1->nodeMax) {
1394 xmlNodePtr *temp;
1395
1396 val1->nodeMax *= 2;
1397 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1398 sizeof(xmlNodePtr));
1399 if (temp == NULL) {
1400 xmlGenericError(xmlGenericErrorContext,
1401 "xmlXPathNodeSetMerge: out of memory\n");
1402 return(NULL);
1403 }
1404 val1->nodeTab = temp;
1405 }
1406 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1407 }
1408
1409 return(val1);
1410}
1411
1412/**
1413 * xmlXPathNodeSetDel:
1414 * @cur: the initial node set
1415 * @val: an xmlNodePtr
1416 *
1417 * Removes an xmlNodePtr from an existing NodeSet
1418 */
1419void
1420xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1421 int i;
1422
1423 if (cur == NULL) return;
1424 if (val == NULL) return;
1425
1426 /*
1427 * check against doublons
1428 */
1429 for (i = 0;i < cur->nodeNr;i++)
1430 if (cur->nodeTab[i] == val) break;
1431
1432 if (i >= cur->nodeNr) {
1433#ifdef DEBUG
1434 xmlGenericError(xmlGenericErrorContext,
1435 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1436 val->name);
1437#endif
1438 return;
1439 }
1440 cur->nodeNr--;
1441 for (;i < cur->nodeNr;i++)
1442 cur->nodeTab[i] = cur->nodeTab[i + 1];
1443 cur->nodeTab[cur->nodeNr] = NULL;
1444}
1445
1446/**
1447 * xmlXPathNodeSetRemove:
1448 * @cur: the initial node set
1449 * @val: the index to remove
1450 *
1451 * Removes an entry from an existing NodeSet list.
1452 */
1453void
1454xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1455 if (cur == NULL) return;
1456 if (val >= cur->nodeNr) return;
1457 cur->nodeNr--;
1458 for (;val < cur->nodeNr;val++)
1459 cur->nodeTab[val] = cur->nodeTab[val + 1];
1460 cur->nodeTab[cur->nodeNr] = NULL;
1461}
1462
1463/**
1464 * xmlXPathFreeNodeSet:
1465 * @obj: the xmlNodeSetPtr to free
1466 *
1467 * Free the NodeSet compound (not the actual nodes !).
1468 */
1469void
1470xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1471 if (obj == NULL) return;
1472 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001473 xmlFree(obj->nodeTab);
1474 }
Owen Taylor3473f882001-02-23 17:55:21 +00001475 xmlFree(obj);
1476}
1477
1478/**
1479 * xmlXPathFreeValueTree:
1480 * @obj: the xmlNodeSetPtr to free
1481 *
1482 * Free the NodeSet compound and the actual tree, this is different
1483 * from xmlXPathFreeNodeSet()
1484 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001485static void
Owen Taylor3473f882001-02-23 17:55:21 +00001486xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1487 int i;
1488
1489 if (obj == NULL) return;
1490 for (i = 0;i < obj->nodeNr;i++)
1491 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001492 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001493
1494 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001495 xmlFree(obj->nodeTab);
1496 }
Owen Taylor3473f882001-02-23 17:55:21 +00001497 xmlFree(obj);
1498}
1499
1500#if defined(DEBUG) || defined(DEBUG_STEP)
1501/**
1502 * xmlGenericErrorContextNodeSet:
1503 * @output: a FILE * for the output
1504 * @obj: the xmlNodeSetPtr to free
1505 *
1506 * Quick display of a NodeSet
1507 */
1508void
1509xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1510 int i;
1511
1512 if (output == NULL) output = xmlGenericErrorContext;
1513 if (obj == NULL) {
1514 fprintf(output, "NodeSet == NULL !\n");
1515 return;
1516 }
1517 if (obj->nodeNr == 0) {
1518 fprintf(output, "NodeSet is empty\n");
1519 return;
1520 }
1521 if (obj->nodeTab == NULL) {
1522 fprintf(output, " nodeTab == NULL !\n");
1523 return;
1524 }
1525 for (i = 0; i < obj->nodeNr; i++) {
1526 if (obj->nodeTab[i] == NULL) {
1527 fprintf(output, " NULL !\n");
1528 return;
1529 }
1530 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1531 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1532 fprintf(output, " /");
1533 else if (obj->nodeTab[i]->name == NULL)
1534 fprintf(output, " noname!");
1535 else fprintf(output, " %s", obj->nodeTab[i]->name);
1536 }
1537 fprintf(output, "\n");
1538}
1539#endif
1540
1541/**
1542 * xmlXPathNewNodeSet:
1543 * @val: the NodePtr value
1544 *
1545 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1546 * it with the single Node @val
1547 *
1548 * Returns the newly created object.
1549 */
1550xmlXPathObjectPtr
1551xmlXPathNewNodeSet(xmlNodePtr val) {
1552 xmlXPathObjectPtr ret;
1553
1554 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1555 if (ret == NULL) {
1556 xmlGenericError(xmlGenericErrorContext,
1557 "xmlXPathNewNodeSet: out of memory\n");
1558 return(NULL);
1559 }
1560 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1561 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001562 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001563 ret->nodesetval = xmlXPathNodeSetCreate(val);
1564 return(ret);
1565}
1566
1567/**
1568 * xmlXPathNewValueTree:
1569 * @val: the NodePtr value
1570 *
1571 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1572 * it with the tree root @val
1573 *
1574 * Returns the newly created object.
1575 */
1576xmlXPathObjectPtr
1577xmlXPathNewValueTree(xmlNodePtr val) {
1578 xmlXPathObjectPtr ret;
1579
1580 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1581 if (ret == NULL) {
1582 xmlGenericError(xmlGenericErrorContext,
1583 "xmlXPathNewNodeSet: out of memory\n");
1584 return(NULL);
1585 }
1586 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1587 ret->type = XPATH_XSLT_TREE;
1588 ret->nodesetval = xmlXPathNodeSetCreate(val);
1589 return(ret);
1590}
1591
1592/**
1593 * xmlXPathNewNodeSetList:
1594 * @val: an existing NodeSet
1595 *
1596 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1597 * it with the Nodeset @val
1598 *
1599 * Returns the newly created object.
1600 */
1601xmlXPathObjectPtr
1602xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1603 xmlXPathObjectPtr ret;
1604 int i;
1605
1606 if (val == NULL)
1607 ret = NULL;
1608 else if (val->nodeTab == NULL)
1609 ret = xmlXPathNewNodeSet(NULL);
1610 else
1611 {
1612 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1613 for (i = 1; i < val->nodeNr; ++i)
1614 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1615 }
1616
1617 return(ret);
1618}
1619
1620/**
1621 * xmlXPathWrapNodeSet:
1622 * @val: the NodePtr value
1623 *
1624 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1625 *
1626 * Returns the newly created object.
1627 */
1628xmlXPathObjectPtr
1629xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1630 xmlXPathObjectPtr ret;
1631
1632 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1633 if (ret == NULL) {
1634 xmlGenericError(xmlGenericErrorContext,
1635 "xmlXPathWrapNodeSet: out of memory\n");
1636 return(NULL);
1637 }
1638 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1639 ret->type = XPATH_NODESET;
1640 ret->nodesetval = val;
1641 return(ret);
1642}
1643
1644/**
1645 * xmlXPathFreeNodeSetList:
1646 * @obj: an existing NodeSetList object
1647 *
1648 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1649 * the list contrary to xmlXPathFreeObject().
1650 */
1651void
1652xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1653 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001654 xmlFree(obj);
1655}
1656
1657/************************************************************************
1658 * *
1659 * Routines to handle extra functions *
1660 * *
1661 ************************************************************************/
1662
1663/**
1664 * xmlXPathRegisterFunc:
1665 * @ctxt: the XPath context
1666 * @name: the function name
1667 * @f: the function implementation or NULL
1668 *
1669 * Register a new function. If @f is NULL it unregisters the function
1670 *
1671 * Returns 0 in case of success, -1 in case of error
1672 */
1673int
1674xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
1675 xmlXPathFunction f) {
1676 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
1677}
1678
1679/**
1680 * xmlXPathRegisterFuncNS:
1681 * @ctxt: the XPath context
1682 * @name: the function name
1683 * @ns_uri: the function namespace URI
1684 * @f: the function implementation or NULL
1685 *
1686 * Register a new function. If @f is NULL it unregisters the function
1687 *
1688 * Returns 0 in case of success, -1 in case of error
1689 */
1690int
1691xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1692 const xmlChar *ns_uri, xmlXPathFunction f) {
1693 if (ctxt == NULL)
1694 return(-1);
1695 if (name == NULL)
1696 return(-1);
1697
1698 if (ctxt->funcHash == NULL)
1699 ctxt->funcHash = xmlHashCreate(0);
1700 if (ctxt->funcHash == NULL)
1701 return(-1);
1702 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
1703}
1704
1705/**
1706 * xmlXPathFunctionLookup:
1707 * @ctxt: the XPath context
1708 * @name: the function name
1709 *
1710 * Search in the Function array of the context for the given
1711 * function.
1712 *
1713 * Returns the xmlXPathFunction or NULL if not found
1714 */
1715xmlXPathFunction
1716xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1717 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
1718}
1719
1720/**
1721 * xmlXPathFunctionLookupNS:
1722 * @ctxt: the XPath context
1723 * @name: the function name
1724 * @ns_uri: the function namespace URI
1725 *
1726 * Search in the Function array of the context for the given
1727 * function.
1728 *
1729 * Returns the xmlXPathFunction or NULL if not found
1730 */
1731xmlXPathFunction
1732xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1733 const xmlChar *ns_uri) {
1734 if (ctxt == NULL)
1735 return(NULL);
1736 if (ctxt->funcHash == NULL)
1737 return(NULL);
1738 if (name == NULL)
1739 return(NULL);
1740
1741 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
1742}
1743
1744/**
1745 * xmlXPathRegisteredFuncsCleanup:
1746 * @ctxt: the XPath context
1747 *
1748 * Cleanup the XPath context data associated to registered functions
1749 */
1750void
1751xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
1752 if (ctxt == NULL)
1753 return;
1754
1755 xmlHashFree(ctxt->funcHash, NULL);
1756 ctxt->funcHash = NULL;
1757}
1758
1759/************************************************************************
1760 * *
1761 * Routines to handle Variable *
1762 * *
1763 ************************************************************************/
1764
1765/**
1766 * xmlXPathRegisterVariable:
1767 * @ctxt: the XPath context
1768 * @name: the variable name
1769 * @value: the variable value or NULL
1770 *
1771 * Register a new variable value. If @value is NULL it unregisters
1772 * the variable
1773 *
1774 * Returns 0 in case of success, -1 in case of error
1775 */
1776int
1777xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
1778 xmlXPathObjectPtr value) {
1779 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
1780}
1781
1782/**
1783 * xmlXPathRegisterVariableNS:
1784 * @ctxt: the XPath context
1785 * @name: the variable name
1786 * @ns_uri: the variable namespace URI
1787 * @value: the variable value or NULL
1788 *
1789 * Register a new variable value. If @value is NULL it unregisters
1790 * the variable
1791 *
1792 * Returns 0 in case of success, -1 in case of error
1793 */
1794int
1795xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1796 const xmlChar *ns_uri,
1797 xmlXPathObjectPtr value) {
1798 if (ctxt == NULL)
1799 return(-1);
1800 if (name == NULL)
1801 return(-1);
1802
1803 if (ctxt->varHash == NULL)
1804 ctxt->varHash = xmlHashCreate(0);
1805 if (ctxt->varHash == NULL)
1806 return(-1);
1807 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
1808 (void *) value,
1809 (xmlHashDeallocator)xmlXPathFreeObject));
1810}
1811
1812/**
1813 * xmlXPathRegisterVariableLookup:
1814 * @ctxt: the XPath context
1815 * @f: the lookup function
1816 * @data: the lookup data
1817 *
1818 * register an external mechanism to do variable lookup
1819 */
1820void
1821xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
1822 xmlXPathVariableLookupFunc f, void *data) {
1823 if (ctxt == NULL)
1824 return;
1825 ctxt->varLookupFunc = (void *) f;
1826 ctxt->varLookupData = data;
1827}
1828
1829/**
1830 * xmlXPathVariableLookup:
1831 * @ctxt: the XPath context
1832 * @name: the variable name
1833 *
1834 * Search in the Variable array of the context for the given
1835 * variable value.
1836 *
1837 * Returns the value or NULL if not found
1838 */
1839xmlXPathObjectPtr
1840xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1841 if (ctxt == NULL)
1842 return(NULL);
1843
1844 if (ctxt->varLookupFunc != NULL) {
1845 xmlXPathObjectPtr ret;
1846
1847 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1848 (ctxt->varLookupData, name, NULL);
1849 if (ret != NULL) return(ret);
1850 }
1851 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
1852}
1853
1854/**
1855 * xmlXPathVariableLookupNS:
1856 * @ctxt: the XPath context
1857 * @name: the variable name
1858 * @ns_uri: the variable namespace URI
1859 *
1860 * Search in the Variable array of the context for the given
1861 * variable value.
1862 *
1863 * Returns the value or NULL if not found
1864 */
1865xmlXPathObjectPtr
1866xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1867 const xmlChar *ns_uri) {
1868 if (ctxt == NULL)
1869 return(NULL);
1870
1871 if (ctxt->varLookupFunc != NULL) {
1872 xmlXPathObjectPtr ret;
1873
1874 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1875 (ctxt->varLookupData, name, ns_uri);
1876 if (ret != NULL) return(ret);
1877 }
1878
1879 if (ctxt->varHash == NULL)
1880 return(NULL);
1881 if (name == NULL)
1882 return(NULL);
1883
1884 return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
1885}
1886
1887/**
1888 * xmlXPathRegisteredVariablesCleanup:
1889 * @ctxt: the XPath context
1890 *
1891 * Cleanup the XPath context data associated to registered variables
1892 */
1893void
1894xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
1895 if (ctxt == NULL)
1896 return;
1897
1898 xmlHashFree(ctxt->varHash, NULL);
1899 ctxt->varHash = NULL;
1900}
1901
1902/**
1903 * xmlXPathRegisterNs:
1904 * @ctxt: the XPath context
1905 * @prefix: the namespace prefix
1906 * @ns_uri: the namespace name
1907 *
1908 * Register a new namespace. If @ns_uri is NULL it unregisters
1909 * the namespace
1910 *
1911 * Returns 0 in case of success, -1 in case of error
1912 */
1913int
1914xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
1915 const xmlChar *ns_uri) {
1916 if (ctxt == NULL)
1917 return(-1);
1918 if (prefix == NULL)
1919 return(-1);
1920
1921 if (ctxt->nsHash == NULL)
1922 ctxt->nsHash = xmlHashCreate(10);
1923 if (ctxt->nsHash == NULL)
1924 return(-1);
1925 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
1926 (xmlHashDeallocator)xmlFree));
1927}
1928
1929/**
1930 * xmlXPathNsLookup:
1931 * @ctxt: the XPath context
1932 * @prefix: the namespace prefix value
1933 *
1934 * Search in the namespace declaration array of the context for the given
1935 * namespace name associated to the given prefix
1936 *
1937 * Returns the value or NULL if not found
1938 */
1939const xmlChar *
1940xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
1941 if (ctxt == NULL)
1942 return(NULL);
1943 if (prefix == NULL)
1944 return(NULL);
1945
1946#ifdef XML_XML_NAMESPACE
1947 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
1948 return(XML_XML_NAMESPACE);
1949#endif
1950
1951 if (ctxt->nsHash == NULL)
1952 return(NULL);
1953
1954 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
1955}
1956
1957/**
1958 * xmlXPathRegisteredVariablesCleanup:
1959 * @ctxt: the XPath context
1960 *
1961 * Cleanup the XPath context data associated to registered variables
1962 */
1963void
1964xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
1965 if (ctxt == NULL)
1966 return;
1967
1968 xmlHashFree(ctxt->nsHash, NULL);
1969 ctxt->nsHash = NULL;
1970}
1971
1972/************************************************************************
1973 * *
1974 * Routines to handle Values *
1975 * *
1976 ************************************************************************/
1977
1978/* Allocations are terrible, one need to optimize all this !!! */
1979
1980/**
1981 * xmlXPathNewFloat:
1982 * @val: the double value
1983 *
1984 * Create a new xmlXPathObjectPtr of type double and of value @val
1985 *
1986 * Returns the newly created object.
1987 */
1988xmlXPathObjectPtr
1989xmlXPathNewFloat(double val) {
1990 xmlXPathObjectPtr ret;
1991
1992 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1993 if (ret == NULL) {
1994 xmlGenericError(xmlGenericErrorContext,
1995 "xmlXPathNewFloat: out of memory\n");
1996 return(NULL);
1997 }
1998 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1999 ret->type = XPATH_NUMBER;
2000 ret->floatval = val;
2001 return(ret);
2002}
2003
2004/**
2005 * xmlXPathNewBoolean:
2006 * @val: the boolean value
2007 *
2008 * Create a new xmlXPathObjectPtr of type boolean and of value @val
2009 *
2010 * Returns the newly created object.
2011 */
2012xmlXPathObjectPtr
2013xmlXPathNewBoolean(int val) {
2014 xmlXPathObjectPtr ret;
2015
2016 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2017 if (ret == NULL) {
2018 xmlGenericError(xmlGenericErrorContext,
2019 "xmlXPathNewBoolean: out of memory\n");
2020 return(NULL);
2021 }
2022 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2023 ret->type = XPATH_BOOLEAN;
2024 ret->boolval = (val != 0);
2025 return(ret);
2026}
2027
2028/**
2029 * xmlXPathNewString:
2030 * @val: the xmlChar * value
2031 *
2032 * Create a new xmlXPathObjectPtr of type string and of value @val
2033 *
2034 * Returns the newly created object.
2035 */
2036xmlXPathObjectPtr
2037xmlXPathNewString(const xmlChar *val) {
2038 xmlXPathObjectPtr ret;
2039
2040 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2041 if (ret == NULL) {
2042 xmlGenericError(xmlGenericErrorContext,
2043 "xmlXPathNewString: out of memory\n");
2044 return(NULL);
2045 }
2046 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2047 ret->type = XPATH_STRING;
2048 if (val != NULL)
2049 ret->stringval = xmlStrdup(val);
2050 else
2051 ret->stringval = xmlStrdup((const xmlChar *)"");
2052 return(ret);
2053}
2054
2055/**
2056 * xmlXPathNewCString:
2057 * @val: the char * value
2058 *
2059 * Create a new xmlXPathObjectPtr of type string and of value @val
2060 *
2061 * Returns the newly created object.
2062 */
2063xmlXPathObjectPtr
2064xmlXPathNewCString(const char *val) {
2065 xmlXPathObjectPtr ret;
2066
2067 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2068 if (ret == NULL) {
2069 xmlGenericError(xmlGenericErrorContext,
2070 "xmlXPathNewCString: out of memory\n");
2071 return(NULL);
2072 }
2073 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2074 ret->type = XPATH_STRING;
2075 ret->stringval = xmlStrdup(BAD_CAST val);
2076 return(ret);
2077}
2078
2079/**
2080 * xmlXPathObjectCopy:
2081 * @val: the original object
2082 *
2083 * allocate a new copy of a given object
2084 *
2085 * Returns the newly created object.
2086 */
2087xmlXPathObjectPtr
2088xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2089 xmlXPathObjectPtr ret;
2090
2091 if (val == NULL)
2092 return(NULL);
2093
2094 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2095 if (ret == NULL) {
2096 xmlGenericError(xmlGenericErrorContext,
2097 "xmlXPathObjectCopy: out of memory\n");
2098 return(NULL);
2099 }
2100 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2101 switch (val->type) {
2102 case XPATH_BOOLEAN:
2103 case XPATH_NUMBER:
2104 case XPATH_POINT:
2105 case XPATH_RANGE:
2106 break;
2107 case XPATH_STRING:
2108 ret->stringval = xmlStrdup(val->stringval);
2109 break;
2110 case XPATH_XSLT_TREE:
2111 if ((val->nodesetval != NULL) &&
2112 (val->nodesetval->nodeTab != NULL))
2113 ret->nodesetval = xmlXPathNodeSetCreate(
2114 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
2115 else
2116 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
2117 break;
2118 case XPATH_NODESET:
2119 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
2120 break;
2121 case XPATH_LOCATIONSET:
2122#ifdef LIBXML_XPTR_ENABLED
2123 {
2124 xmlLocationSetPtr loc = val->user;
2125 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2126 break;
2127 }
2128#endif
2129 case XPATH_UNDEFINED:
2130 case XPATH_USERS:
2131 xmlGenericError(xmlGenericErrorContext,
2132 "xmlXPathObjectCopy: unsupported type %d\n",
2133 val->type);
2134 break;
2135 }
2136 return(ret);
2137}
2138
2139/**
2140 * xmlXPathFreeObject:
2141 * @obj: the object to free
2142 *
2143 * Free up an xmlXPathObjectPtr object.
2144 */
2145void
2146xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2147 if (obj == NULL) return;
2148 if (obj->type == XPATH_NODESET) {
Daniel Veillard77851712001-02-27 21:54:07 +00002149 if (obj->boolval) {
2150 obj->type = XPATH_XSLT_TREE;
2151 if (obj->nodesetval != NULL)
2152 xmlXPathFreeValueTree(obj->nodesetval);
2153 } else {
2154 if (obj->nodesetval != NULL)
2155 xmlXPathFreeNodeSet(obj->nodesetval);
2156 }
Owen Taylor3473f882001-02-23 17:55:21 +00002157#ifdef LIBXML_XPTR_ENABLED
2158 } else if (obj->type == XPATH_LOCATIONSET) {
2159 if (obj->user != NULL)
2160 xmlXPtrFreeLocationSet(obj->user);
2161#endif
2162 } else if (obj->type == XPATH_STRING) {
2163 if (obj->stringval != NULL)
2164 xmlFree(obj->stringval);
2165 } else if (obj->type == XPATH_XSLT_TREE) {
2166 if (obj->nodesetval != NULL)
2167 xmlXPathFreeValueTree(obj->nodesetval);
2168 }
2169
Owen Taylor3473f882001-02-23 17:55:21 +00002170 xmlFree(obj);
2171}
2172
2173/************************************************************************
2174 * *
2175 * Routines to handle XPath contexts *
2176 * *
2177 ************************************************************************/
2178
2179/**
2180 * xmlXPathNewContext:
2181 * @doc: the XML document
2182 *
2183 * Create a new xmlXPathContext
2184 *
2185 * Returns the xmlXPathContext just allocated.
2186 */
2187xmlXPathContextPtr
2188xmlXPathNewContext(xmlDocPtr doc) {
2189 xmlXPathContextPtr ret;
2190
2191 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
2192 if (ret == NULL) {
2193 xmlGenericError(xmlGenericErrorContext,
2194 "xmlXPathNewContext: out of memory\n");
2195 return(NULL);
2196 }
2197 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
2198 ret->doc = doc;
2199 ret->node = NULL;
2200
2201 ret->varHash = NULL;
2202
2203 ret->nb_types = 0;
2204 ret->max_types = 0;
2205 ret->types = NULL;
2206
2207 ret->funcHash = xmlHashCreate(0);
2208
2209 ret->nb_axis = 0;
2210 ret->max_axis = 0;
2211 ret->axis = NULL;
2212
2213 ret->nsHash = NULL;
2214 ret->user = NULL;
2215
2216 ret->contextSize = -1;
2217 ret->proximityPosition = -1;
2218
2219 xmlXPathRegisterAllFunctions(ret);
2220
2221 return(ret);
2222}
2223
2224/**
2225 * xmlXPathFreeContext:
2226 * @ctxt: the context to free
2227 *
2228 * Free up an xmlXPathContext
2229 */
2230void
2231xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
2232 xmlXPathRegisteredNsCleanup(ctxt);
2233 xmlXPathRegisteredFuncsCleanup(ctxt);
2234 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00002235 xmlFree(ctxt);
2236}
2237
2238/************************************************************************
2239 * *
2240 * Routines to handle XPath parser contexts *
2241 * *
2242 ************************************************************************/
2243
2244#define CHECK_CTXT(ctxt) \
2245 if (ctxt == NULL) { \
2246 xmlGenericError(xmlGenericErrorContext, \
2247 "%s:%d Internal error: ctxt == NULL\n", \
2248 __FILE__, __LINE__); \
2249 } \
2250
2251
2252#define CHECK_CONTEXT(ctxt) \
2253 if (ctxt == NULL) { \
2254 xmlGenericError(xmlGenericErrorContext, \
2255 "%s:%d Internal error: no context\n", \
2256 __FILE__, __LINE__); \
2257 } \
2258 else if (ctxt->doc == NULL) { \
2259 xmlGenericError(xmlGenericErrorContext, \
2260 "%s:%d Internal error: no document\n", \
2261 __FILE__, __LINE__); \
2262 } \
2263 else if (ctxt->doc->children == NULL) { \
2264 xmlGenericError(xmlGenericErrorContext, \
2265 "%s:%d Internal error: document without root\n", \
2266 __FILE__, __LINE__); \
2267 } \
2268
2269
2270/**
2271 * xmlXPathNewParserContext:
2272 * @str: the XPath expression
2273 * @ctxt: the XPath context
2274 *
2275 * Create a new xmlXPathParserContext
2276 *
2277 * Returns the xmlXPathParserContext just allocated.
2278 */
2279xmlXPathParserContextPtr
2280xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
2281 xmlXPathParserContextPtr ret;
2282
2283 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2284 if (ret == NULL) {
2285 xmlGenericError(xmlGenericErrorContext,
2286 "xmlXPathNewParserContext: out of memory\n");
2287 return(NULL);
2288 }
2289 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2290 ret->cur = ret->base = str;
2291 ret->context = ctxt;
2292
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002293 ret->comp = xmlXPathNewCompExpr();
2294 if (ret->comp == NULL) {
2295 xmlFree(ret->valueTab);
2296 xmlFree(ret);
2297 return(NULL);
2298 }
2299
2300 return(ret);
2301}
2302
2303/**
2304 * xmlXPathCompParserContext:
2305 * @comp: the XPath compiled expression
2306 * @ctxt: the XPath context
2307 *
2308 * Create a new xmlXPathParserContext when processing a compiled expression
2309 *
2310 * Returns the xmlXPathParserContext just allocated.
2311 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002312static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002313xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
2314 xmlXPathParserContextPtr ret;
2315
2316 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2317 if (ret == NULL) {
2318 xmlGenericError(xmlGenericErrorContext,
2319 "xmlXPathNewParserContext: out of memory\n");
2320 return(NULL);
2321 }
2322 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2323
Owen Taylor3473f882001-02-23 17:55:21 +00002324 /* Allocate the value stack */
2325 ret->valueTab = (xmlXPathObjectPtr *)
2326 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002327 if (ret->valueTab == NULL) {
2328 xmlFree(ret);
2329 xmlGenericError(xmlGenericErrorContext,
2330 "xmlXPathNewParserContext: out of memory\n");
2331 return(NULL);
2332 }
Owen Taylor3473f882001-02-23 17:55:21 +00002333 ret->valueNr = 0;
2334 ret->valueMax = 10;
2335 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002336
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00002337 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002338 ret->comp = comp;
2339
Owen Taylor3473f882001-02-23 17:55:21 +00002340 return(ret);
2341}
2342
2343/**
2344 * xmlXPathFreeParserContext:
2345 * @ctxt: the context to free
2346 *
2347 * Free up an xmlXPathParserContext
2348 */
2349void
2350xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
2351 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002352 xmlFree(ctxt->valueTab);
2353 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002354 if (ctxt->comp)
2355 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00002356 xmlFree(ctxt);
2357}
2358
2359/************************************************************************
2360 * *
2361 * The implicit core function library *
2362 * *
2363 ************************************************************************/
2364
2365/*
2366 * Auto-pop and cast to a number
2367 */
2368void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
2369
2370
2371#define POP_FLOAT \
2372 arg = valuePop(ctxt); \
2373 if (arg == NULL) { \
2374 XP_ERROR(XPATH_INVALID_OPERAND); \
2375 } \
2376 if (arg->type != XPATH_NUMBER) { \
2377 valuePush(ctxt, arg); \
2378 xmlXPathNumberFunction(ctxt, 1); \
2379 arg = valuePop(ctxt); \
2380 }
2381
2382/**
2383 * xmlXPathCompareNodeSetFloat:
2384 * @ctxt: the XPath Parser context
2385 * @inf: less than (1) or greater than (0)
2386 * @strict: is the comparison strict
2387 * @arg: the node set
2388 * @f: the value
2389 *
2390 * Implement the compare operation between a nodeset and a number
2391 * @ns < @val (1, 1, ...
2392 * @ns <= @val (1, 0, ...
2393 * @ns > @val (0, 1, ...
2394 * @ns >= @val (0, 0, ...
2395 *
2396 * If one object to be compared is a node-set and the other is a number,
2397 * then the comparison will be true if and only if there is a node in the
2398 * node-set such that the result of performing the comparison on the number
2399 * to be compared and on the result of converting the string-value of that
2400 * node to a number using the number function is true.
2401 *
2402 * Returns 0 or 1 depending on the results of the test.
2403 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002404static int
Owen Taylor3473f882001-02-23 17:55:21 +00002405xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
2406 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
2407 int i, ret = 0;
2408 xmlNodeSetPtr ns;
2409 xmlChar *str2;
2410
2411 if ((f == NULL) || (arg == NULL) ||
2412 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2413 xmlXPathFreeObject(arg);
2414 xmlXPathFreeObject(f);
2415 return(0);
2416 }
2417 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002418 if (ns != NULL) {
2419 for (i = 0;i < ns->nodeNr;i++) {
2420 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2421 if (str2 != NULL) {
2422 valuePush(ctxt,
2423 xmlXPathNewString(str2));
2424 xmlFree(str2);
2425 xmlXPathNumberFunction(ctxt, 1);
2426 valuePush(ctxt, xmlXPathObjectCopy(f));
2427 ret = xmlXPathCompareValues(ctxt, inf, strict);
2428 if (ret)
2429 break;
2430 }
2431 }
Owen Taylor3473f882001-02-23 17:55:21 +00002432 }
2433 xmlXPathFreeObject(arg);
2434 xmlXPathFreeObject(f);
2435 return(ret);
2436}
2437
2438/**
2439 * xmlXPathCompareNodeSetString:
2440 * @ctxt: the XPath Parser context
2441 * @inf: less than (1) or greater than (0)
2442 * @strict: is the comparison strict
2443 * @arg: the node set
2444 * @s: the value
2445 *
2446 * Implement the compare operation between a nodeset and a string
2447 * @ns < @val (1, 1, ...
2448 * @ns <= @val (1, 0, ...
2449 * @ns > @val (0, 1, ...
2450 * @ns >= @val (0, 0, ...
2451 *
2452 * If one object to be compared is a node-set and the other is a string,
2453 * then the comparison will be true if and only if there is a node in
2454 * the node-set such that the result of performing the comparison on the
2455 * string-value of the node and the other string is true.
2456 *
2457 * Returns 0 or 1 depending on the results of the test.
2458 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002459static int
Owen Taylor3473f882001-02-23 17:55:21 +00002460xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
2461 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
2462 int i, ret = 0;
2463 xmlNodeSetPtr ns;
2464 xmlChar *str2;
2465
2466 if ((s == NULL) || (arg == NULL) ||
2467 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2468 xmlXPathFreeObject(arg);
2469 xmlXPathFreeObject(s);
2470 return(0);
2471 }
2472 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002473 if (ns != NULL) {
2474 for (i = 0;i < ns->nodeNr;i++) {
2475 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2476 if (str2 != NULL) {
2477 valuePush(ctxt,
2478 xmlXPathNewString(str2));
2479 xmlFree(str2);
2480 valuePush(ctxt, xmlXPathObjectCopy(s));
2481 ret = xmlXPathCompareValues(ctxt, inf, strict);
2482 if (ret)
2483 break;
2484 }
2485 }
Owen Taylor3473f882001-02-23 17:55:21 +00002486 }
2487 xmlXPathFreeObject(arg);
2488 xmlXPathFreeObject(s);
2489 return(ret);
2490}
2491
2492/**
2493 * xmlXPathCompareNodeSets:
Owen Taylor3473f882001-02-23 17:55:21 +00002494 * @op: less than (-1), equal (0) or greater than (1)
2495 * @strict: is the comparison strict
2496 * @arg1: the fist node set object
2497 * @arg2: the second node set object
2498 *
2499 * Implement the compare operation on nodesets:
2500 *
2501 * If both objects to be compared are node-sets, then the comparison
2502 * will be true if and only if there is a node in the first node-set
2503 * and a node in the second node-set such that the result of performing
2504 * the comparison on the string-values of the two nodes is true.
2505 * ....
2506 * When neither object to be compared is a node-set and the operator
2507 * is <=, <, >= or >, then the objects are compared by converting both
2508 * objects to numbers and comparing the numbers according to IEEE 754.
2509 * ....
2510 * The number function converts its argument to a number as follows:
2511 * - a string that consists of optional whitespace followed by an
2512 * optional minus sign followed by a Number followed by whitespace
2513 * is converted to the IEEE 754 number that is nearest (according
2514 * to the IEEE 754 round-to-nearest rule) to the mathematical value
2515 * represented by the string; any other string is converted to NaN
2516 *
2517 * Conclusion all nodes need to be converted first to their string value
2518 * and then the comparison must be done when possible
2519 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002520static int
2521xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00002522 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2523 int i, j, init = 0;
2524 double val1;
2525 double *values2;
2526 int ret = 0;
2527 xmlChar *str;
2528 xmlNodeSetPtr ns1;
2529 xmlNodeSetPtr ns2;
2530
2531 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002532 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
2533 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002534 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002535 }
Owen Taylor3473f882001-02-23 17:55:21 +00002536 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002537 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
2538 xmlXPathFreeObject(arg1);
2539 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002540 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002541 }
Owen Taylor3473f882001-02-23 17:55:21 +00002542
2543 ns1 = arg1->nodesetval;
2544 ns2 = arg2->nodesetval;
2545
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002546 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002547 xmlXPathFreeObject(arg1);
2548 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002549 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002550 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002551 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002552 xmlXPathFreeObject(arg1);
2553 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002554 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002555 }
Owen Taylor3473f882001-02-23 17:55:21 +00002556
2557 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
2558 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002559 xmlXPathFreeObject(arg1);
2560 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002561 return(0);
2562 }
2563 for (i = 0;i < ns1->nodeNr;i++) {
2564 str = xmlNodeGetContent(ns1->nodeTab[i]);
2565 if (str == NULL)
2566 continue;
2567 val1 = xmlXPathStringEvalNumber(str);
2568 xmlFree(str);
2569 if (isnan(val1))
2570 continue;
2571 for (j = 0;j < ns2->nodeNr;j++) {
2572 if (init == 0) {
2573 str = xmlNodeGetContent(ns2->nodeTab[j]);
2574 if (str == NULL) {
2575 values2[j] = xmlXPathNAN;
2576 } else {
2577 values2[j] = xmlXPathStringEvalNumber(str);
2578 xmlFree(str);
2579 }
2580 }
2581 if (isnan(values2[j]))
2582 continue;
2583 if (inf && strict)
2584 ret = (val1 < values2[j]);
2585 else if (inf && !strict)
2586 ret = (val1 <= values2[j]);
2587 else if (!inf && strict)
2588 ret = (val1 > values2[j]);
2589 else if (!inf && !strict)
2590 ret = (val1 >= values2[j]);
2591 if (ret)
2592 break;
2593 }
2594 if (ret)
2595 break;
2596 init = 1;
2597 }
2598 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002599 xmlXPathFreeObject(arg1);
2600 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002601 return(ret);
2602 return(0);
2603}
2604
2605/**
2606 * xmlXPathCompareNodeSetValue:
2607 * @ctxt: the XPath Parser context
2608 * @inf: less than (1) or greater than (0)
2609 * @strict: is the comparison strict
2610 * @arg: the node set
2611 * @val: the value
2612 *
2613 * Implement the compare operation between a nodeset and a value
2614 * @ns < @val (1, 1, ...
2615 * @ns <= @val (1, 0, ...
2616 * @ns > @val (0, 1, ...
2617 * @ns >= @val (0, 0, ...
2618 *
2619 * If one object to be compared is a node-set and the other is a boolean,
2620 * then the comparison will be true if and only if the result of performing
2621 * the comparison on the boolean and on the result of converting
2622 * the node-set to a boolean using the boolean function is true.
2623 *
2624 * Returns 0 or 1 depending on the results of the test.
2625 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002626static int
Owen Taylor3473f882001-02-23 17:55:21 +00002627xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
2628 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
2629 if ((val == NULL) || (arg == NULL) ||
2630 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2631 return(0);
2632
2633 switch(val->type) {
2634 case XPATH_NUMBER:
2635 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
2636 case XPATH_NODESET:
2637 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002638 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00002639 case XPATH_STRING:
2640 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
2641 case XPATH_BOOLEAN:
2642 valuePush(ctxt, arg);
2643 xmlXPathBooleanFunction(ctxt, 1);
2644 valuePush(ctxt, val);
2645 return(xmlXPathCompareValues(ctxt, inf, strict));
2646 default:
2647 TODO
2648 return(0);
2649 }
2650 return(0);
2651}
2652
2653/**
2654 * xmlXPathEqualNodeSetString
2655 * @arg: the nodeset object argument
2656 * @str: the string to compare to.
2657 *
2658 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2659 * If one object to be compared is a node-set and the other is a string,
2660 * then the comparison will be true if and only if there is a node in
2661 * the node-set such that the result of performing the comparison on the
2662 * string-value of the node and the other string is true.
2663 *
2664 * Returns 0 or 1 depending on the results of the test.
2665 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002666static int
Owen Taylor3473f882001-02-23 17:55:21 +00002667xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
2668 int i;
2669 xmlNodeSetPtr ns;
2670 xmlChar *str2;
2671
2672 if ((str == NULL) || (arg == NULL) ||
2673 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2674 return(0);
2675 ns = arg->nodesetval;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002676 if (ns == NULL)
2677 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002678 if (ns->nodeNr <= 0)
2679 return(0);
2680 for (i = 0;i < ns->nodeNr;i++) {
2681 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2682 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
2683 xmlFree(str2);
2684 return(1);
2685 }
2686 if (str2 != NULL)
2687 xmlFree(str2);
2688 }
2689 return(0);
2690}
2691
2692/**
2693 * xmlXPathEqualNodeSetFloat
2694 * @arg: the nodeset object argument
2695 * @f: the float to compare to
2696 *
2697 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2698 * If one object to be compared is a node-set and the other is a number,
2699 * then the comparison will be true if and only if there is a node in
2700 * the node-set such that the result of performing the comparison on the
2701 * number to be compared and on the result of converting the string-value
2702 * of that node to a number using the number function is true.
2703 *
2704 * Returns 0 or 1 depending on the results of the test.
2705 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002706static int
Owen Taylor3473f882001-02-23 17:55:21 +00002707xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
2708 char buf[100] = "";
2709
2710 if ((arg == NULL) ||
2711 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2712 return(0);
2713
Bjorn Reesee1dc0112001-03-03 12:09:03 +00002714 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00002715 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
2716}
2717
2718
2719/**
2720 * xmlXPathEqualNodeSets
2721 * @arg1: first nodeset object argument
2722 * @arg2: second nodeset object argument
2723 *
2724 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
2725 * If both objects to be compared are node-sets, then the comparison
2726 * will be true if and only if there is a node in the first node-set and
2727 * a node in the second node-set such that the result of performing the
2728 * comparison on the string-values of the two nodes is true.
2729 *
2730 * (needless to say, this is a costly operation)
2731 *
2732 * Returns 0 or 1 depending on the results of the test.
2733 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002734static int
Owen Taylor3473f882001-02-23 17:55:21 +00002735xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2736 int i, j;
2737 xmlChar **values1;
2738 xmlChar **values2;
2739 int ret = 0;
2740 xmlNodeSetPtr ns1;
2741 xmlNodeSetPtr ns2;
2742
2743 if ((arg1 == NULL) ||
2744 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2745 return(0);
2746 if ((arg2 == NULL) ||
2747 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2748 return(0);
2749
2750 ns1 = arg1->nodesetval;
2751 ns2 = arg2->nodesetval;
2752
Daniel Veillard911f49a2001-04-07 15:39:35 +00002753 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002754 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00002755 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002756 return(0);
2757
2758 /*
2759 * check if there is a node pertaining to both sets
2760 */
2761 for (i = 0;i < ns1->nodeNr;i++)
2762 for (j = 0;j < ns2->nodeNr;j++)
2763 if (ns1->nodeTab[i] == ns2->nodeTab[j])
2764 return(1);
2765
2766 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
2767 if (values1 == NULL)
2768 return(0);
2769 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
2770 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
2771 if (values2 == NULL) {
2772 xmlFree(values1);
2773 return(0);
2774 }
2775 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
2776 for (i = 0;i < ns1->nodeNr;i++) {
2777 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
2778 for (j = 0;j < ns2->nodeNr;j++) {
2779 if (i == 0)
2780 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
2781 ret = xmlStrEqual(values1[i], values2[j]);
2782 if (ret)
2783 break;
2784 }
2785 if (ret)
2786 break;
2787 }
2788 for (i = 0;i < ns1->nodeNr;i++)
2789 if (values1[i] != NULL)
2790 xmlFree(values1[i]);
2791 for (j = 0;j < ns2->nodeNr;j++)
2792 if (values2[j] != NULL)
2793 xmlFree(values2[j]);
2794 xmlFree(values1);
2795 xmlFree(values2);
2796 return(ret);
2797}
2798
2799/**
2800 * xmlXPathEqualValues:
2801 * @ctxt: the XPath Parser context
2802 *
2803 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2804 *
2805 * Returns 0 or 1 depending on the results of the test.
2806 */
2807int
2808xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
2809 xmlXPathObjectPtr arg1, arg2;
2810 int ret = 0;
2811
2812 arg1 = valuePop(ctxt);
2813 if (arg1 == NULL)
2814 XP_ERROR0(XPATH_INVALID_OPERAND);
2815
2816 arg2 = valuePop(ctxt);
2817 if (arg2 == NULL) {
2818 xmlXPathFreeObject(arg1);
2819 XP_ERROR0(XPATH_INVALID_OPERAND);
2820 }
2821
2822 if (arg1 == arg2) {
2823#ifdef DEBUG_EXPR
2824 xmlGenericError(xmlGenericErrorContext,
2825 "Equal: by pointer\n");
2826#endif
2827 return(1);
2828 }
2829
2830 switch (arg1->type) {
2831 case XPATH_UNDEFINED:
2832#ifdef DEBUG_EXPR
2833 xmlGenericError(xmlGenericErrorContext,
2834 "Equal: undefined\n");
2835#endif
2836 break;
2837 case XPATH_XSLT_TREE:
2838 case XPATH_NODESET:
2839 switch (arg2->type) {
2840 case XPATH_UNDEFINED:
2841#ifdef DEBUG_EXPR
2842 xmlGenericError(xmlGenericErrorContext,
2843 "Equal: undefined\n");
2844#endif
2845 break;
2846 case XPATH_XSLT_TREE:
2847 case XPATH_NODESET:
2848 ret = xmlXPathEqualNodeSets(arg1, arg2);
2849 break;
2850 case XPATH_BOOLEAN:
2851 if ((arg1->nodesetval == NULL) ||
2852 (arg1->nodesetval->nodeNr == 0)) ret = 0;
2853 else
2854 ret = 1;
2855 ret = (ret == arg2->boolval);
2856 break;
2857 case XPATH_NUMBER:
2858 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
2859 break;
2860 case XPATH_STRING:
2861 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
2862 break;
2863 case XPATH_USERS:
2864 case XPATH_POINT:
2865 case XPATH_RANGE:
2866 case XPATH_LOCATIONSET:
2867 TODO
2868 break;
2869 }
2870 break;
2871 case XPATH_BOOLEAN:
2872 switch (arg2->type) {
2873 case XPATH_UNDEFINED:
2874#ifdef DEBUG_EXPR
2875 xmlGenericError(xmlGenericErrorContext,
2876 "Equal: undefined\n");
2877#endif
2878 break;
2879 case XPATH_NODESET:
2880 case XPATH_XSLT_TREE:
2881 if ((arg2->nodesetval == NULL) ||
2882 (arg2->nodesetval->nodeNr == 0)) ret = 0;
2883 else
2884 ret = 1;
2885 break;
2886 case XPATH_BOOLEAN:
2887#ifdef DEBUG_EXPR
2888 xmlGenericError(xmlGenericErrorContext,
2889 "Equal: %d boolean %d \n",
2890 arg1->boolval, arg2->boolval);
2891#endif
2892 ret = (arg1->boolval == arg2->boolval);
2893 break;
2894 case XPATH_NUMBER:
2895 if (arg2->floatval) ret = 1;
2896 else ret = 0;
2897 ret = (arg1->boolval == ret);
2898 break;
2899 case XPATH_STRING:
2900 if ((arg2->stringval == NULL) ||
2901 (arg2->stringval[0] == 0)) ret = 0;
2902 else
2903 ret = 1;
2904 ret = (arg1->boolval == ret);
2905 break;
2906 case XPATH_USERS:
2907 case XPATH_POINT:
2908 case XPATH_RANGE:
2909 case XPATH_LOCATIONSET:
2910 TODO
2911 break;
2912 }
2913 break;
2914 case XPATH_NUMBER:
2915 switch (arg2->type) {
2916 case XPATH_UNDEFINED:
2917#ifdef DEBUG_EXPR
2918 xmlGenericError(xmlGenericErrorContext,
2919 "Equal: undefined\n");
2920#endif
2921 break;
2922 case XPATH_NODESET:
2923 case XPATH_XSLT_TREE:
2924 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
2925 break;
2926 case XPATH_BOOLEAN:
2927 if (arg1->floatval) ret = 1;
2928 else ret = 0;
2929 ret = (arg2->boolval == ret);
2930 break;
2931 case XPATH_STRING:
2932 valuePush(ctxt, arg2);
2933 xmlXPathNumberFunction(ctxt, 1);
2934 arg2 = valuePop(ctxt);
2935 /* no break on purpose */
2936 case XPATH_NUMBER:
2937 ret = (arg1->floatval == arg2->floatval);
2938 break;
2939 case XPATH_USERS:
2940 case XPATH_POINT:
2941 case XPATH_RANGE:
2942 case XPATH_LOCATIONSET:
2943 TODO
2944 break;
2945 }
2946 break;
2947 case XPATH_STRING:
2948 switch (arg2->type) {
2949 case XPATH_UNDEFINED:
2950#ifdef DEBUG_EXPR
2951 xmlGenericError(xmlGenericErrorContext,
2952 "Equal: undefined\n");
2953#endif
2954 break;
2955 case XPATH_NODESET:
2956 case XPATH_XSLT_TREE:
2957 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
2958 break;
2959 case XPATH_BOOLEAN:
2960 if ((arg1->stringval == NULL) ||
2961 (arg1->stringval[0] == 0)) ret = 0;
2962 else
2963 ret = 1;
2964 ret = (arg2->boolval == ret);
2965 break;
2966 case XPATH_STRING:
2967 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
2968 break;
2969 case XPATH_NUMBER:
2970 valuePush(ctxt, arg1);
2971 xmlXPathNumberFunction(ctxt, 1);
2972 arg1 = valuePop(ctxt);
2973 ret = (arg1->floatval == arg2->floatval);
2974 break;
2975 case XPATH_USERS:
2976 case XPATH_POINT:
2977 case XPATH_RANGE:
2978 case XPATH_LOCATIONSET:
2979 TODO
2980 break;
2981 }
2982 break;
2983 case XPATH_USERS:
2984 case XPATH_POINT:
2985 case XPATH_RANGE:
2986 case XPATH_LOCATIONSET:
2987 TODO
2988 break;
2989 }
2990 xmlXPathFreeObject(arg1);
2991 xmlXPathFreeObject(arg2);
2992 return(ret);
2993}
2994
2995
2996/**
2997 * xmlXPathCompareValues:
2998 * @ctxt: the XPath Parser context
2999 * @inf: less than (1) or greater than (0)
3000 * @strict: is the comparison strict
3001 *
3002 * Implement the compare operation on XPath objects:
3003 * @arg1 < @arg2 (1, 1, ...
3004 * @arg1 <= @arg2 (1, 0, ...
3005 * @arg1 > @arg2 (0, 1, ...
3006 * @arg1 >= @arg2 (0, 0, ...
3007 *
3008 * When neither object to be compared is a node-set and the operator is
3009 * <=, <, >=, >, then the objects are compared by converted both objects
3010 * to numbers and comparing the numbers according to IEEE 754. The <
3011 * comparison will be true if and only if the first number is less than the
3012 * second number. The <= comparison will be true if and only if the first
3013 * number is less than or equal to the second number. The > comparison
3014 * will be true if and only if the first number is greater than the second
3015 * number. The >= comparison will be true if and only if the first number
3016 * is greater than or equal to the second number.
3017 *
3018 * Returns 1 if the comparaison succeeded, 0 if it failed
3019 */
3020int
3021xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
3022 int ret = 0;
3023 xmlXPathObjectPtr arg1, arg2;
3024
3025 arg2 = valuePop(ctxt);
3026 if (arg2 == NULL) {
3027 XP_ERROR0(XPATH_INVALID_OPERAND);
3028 }
3029
3030 arg1 = valuePop(ctxt);
3031 if (arg1 == NULL) {
3032 xmlXPathFreeObject(arg2);
3033 XP_ERROR0(XPATH_INVALID_OPERAND);
3034 }
3035
3036 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
3037 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003038 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003039 } else {
3040 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003041 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
3042 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003043 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003044 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
3045 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00003046 }
3047 }
3048 return(ret);
3049 }
3050
3051 if (arg1->type != XPATH_NUMBER) {
3052 valuePush(ctxt, arg1);
3053 xmlXPathNumberFunction(ctxt, 1);
3054 arg1 = valuePop(ctxt);
3055 }
3056 if (arg1->type != XPATH_NUMBER) {
3057 xmlXPathFreeObject(arg1);
3058 xmlXPathFreeObject(arg2);
3059 XP_ERROR0(XPATH_INVALID_OPERAND);
3060 }
3061 if (arg2->type != XPATH_NUMBER) {
3062 valuePush(ctxt, arg2);
3063 xmlXPathNumberFunction(ctxt, 1);
3064 arg2 = valuePop(ctxt);
3065 }
3066 if (arg2->type != XPATH_NUMBER) {
3067 xmlXPathFreeObject(arg1);
3068 xmlXPathFreeObject(arg2);
3069 XP_ERROR0(XPATH_INVALID_OPERAND);
3070 }
3071 /*
3072 * Add tests for infinity and nan
3073 * => feedback on 3.4 for Inf and NaN
3074 */
3075 if (inf && strict)
3076 ret = (arg1->floatval < arg2->floatval);
3077 else if (inf && !strict)
3078 ret = (arg1->floatval <= arg2->floatval);
3079 else if (!inf && strict)
3080 ret = (arg1->floatval > arg2->floatval);
3081 else if (!inf && !strict)
3082 ret = (arg1->floatval >= arg2->floatval);
3083 xmlXPathFreeObject(arg1);
3084 xmlXPathFreeObject(arg2);
3085 return(ret);
3086}
3087
3088/**
3089 * xmlXPathValueFlipSign:
3090 * @ctxt: the XPath Parser context
3091 *
3092 * Implement the unary - operation on an XPath object
3093 * The numeric operators convert their operands to numbers as if
3094 * by calling the number function.
3095 */
3096void
3097xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
3098 xmlXPathObjectPtr arg;
3099
3100 POP_FLOAT
3101 arg->floatval = -arg->floatval;
3102 valuePush(ctxt, arg);
3103}
3104
3105/**
3106 * xmlXPathAddValues:
3107 * @ctxt: the XPath Parser context
3108 *
3109 * Implement the add operation on XPath objects:
3110 * The numeric operators convert their operands to numbers as if
3111 * by calling the number function.
3112 */
3113void
3114xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
3115 xmlXPathObjectPtr arg;
3116 double val;
3117
3118 POP_FLOAT
3119 val = arg->floatval;
3120 xmlXPathFreeObject(arg);
3121
3122 POP_FLOAT
3123 arg->floatval += val;
3124 valuePush(ctxt, arg);
3125}
3126
3127/**
3128 * xmlXPathSubValues:
3129 * @ctxt: the XPath Parser context
3130 *
3131 * Implement the substraction operation on XPath objects:
3132 * The numeric operators convert their operands to numbers as if
3133 * by calling the number function.
3134 */
3135void
3136xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
3137 xmlXPathObjectPtr arg;
3138 double val;
3139
3140 POP_FLOAT
3141 val = arg->floatval;
3142 xmlXPathFreeObject(arg);
3143
3144 POP_FLOAT
3145 arg->floatval -= val;
3146 valuePush(ctxt, arg);
3147}
3148
3149/**
3150 * xmlXPathMultValues:
3151 * @ctxt: the XPath Parser context
3152 *
3153 * Implement the multiply operation on XPath objects:
3154 * The numeric operators convert their operands to numbers as if
3155 * by calling the number function.
3156 */
3157void
3158xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
3159 xmlXPathObjectPtr arg;
3160 double val;
3161
3162 POP_FLOAT
3163 val = arg->floatval;
3164 xmlXPathFreeObject(arg);
3165
3166 POP_FLOAT
3167 arg->floatval *= val;
3168 valuePush(ctxt, arg);
3169}
3170
3171/**
3172 * xmlXPathDivValues:
3173 * @ctxt: the XPath Parser context
3174 *
3175 * Implement the div operation on XPath objects @arg1 / @arg2:
3176 * The numeric operators convert their operands to numbers as if
3177 * by calling the number function.
3178 */
3179void
3180xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
3181 xmlXPathObjectPtr arg;
3182 double val;
3183
3184 POP_FLOAT
3185 val = arg->floatval;
3186 xmlXPathFreeObject(arg);
3187
3188 POP_FLOAT
3189 arg->floatval /= val;
3190 valuePush(ctxt, arg);
3191}
3192
3193/**
3194 * xmlXPathModValues:
3195 * @ctxt: the XPath Parser context
3196 *
3197 * Implement the mod operation on XPath objects: @arg1 / @arg2
3198 * The numeric operators convert their operands to numbers as if
3199 * by calling the number function.
3200 */
3201void
3202xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
3203 xmlXPathObjectPtr arg;
3204 int arg1, arg2;
3205
3206 POP_FLOAT
3207 arg2 = (int) arg->floatval;
3208 xmlXPathFreeObject(arg);
3209
3210 POP_FLOAT
3211 arg1 = (int) arg->floatval;
3212 arg->floatval = arg1 % arg2;
3213 valuePush(ctxt, arg);
3214}
3215
3216/************************************************************************
3217 * *
3218 * The traversal functions *
3219 * *
3220 ************************************************************************/
3221
Owen Taylor3473f882001-02-23 17:55:21 +00003222/*
3223 * A traversal function enumerates nodes along an axis.
3224 * Initially it must be called with NULL, and it indicates
3225 * termination on the axis by returning NULL.
3226 */
3227typedef xmlNodePtr (*xmlXPathTraversalFunction)
3228 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
3229
3230/**
3231 * xmlXPathNextSelf:
3232 * @ctxt: the XPath Parser context
3233 * @cur: the current node in the traversal
3234 *
3235 * Traversal function for the "self" direction
3236 * The self axis contains just the context node itself
3237 *
3238 * Returns the next element following that axis
3239 */
3240xmlNodePtr
3241xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3242 if (cur == NULL)
3243 return(ctxt->context->node);
3244 return(NULL);
3245}
3246
3247/**
3248 * xmlXPathNextChild:
3249 * @ctxt: the XPath Parser context
3250 * @cur: the current node in the traversal
3251 *
3252 * Traversal function for the "child" direction
3253 * The child axis contains the children of the context node in document order.
3254 *
3255 * Returns the next element following that axis
3256 */
3257xmlNodePtr
3258xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3259 if (cur == NULL) {
3260 if (ctxt->context->node == NULL) return(NULL);
3261 switch (ctxt->context->node->type) {
3262 case XML_ELEMENT_NODE:
3263 case XML_TEXT_NODE:
3264 case XML_CDATA_SECTION_NODE:
3265 case XML_ENTITY_REF_NODE:
3266 case XML_ENTITY_NODE:
3267 case XML_PI_NODE:
3268 case XML_COMMENT_NODE:
3269 case XML_NOTATION_NODE:
3270 case XML_DTD_NODE:
3271 return(ctxt->context->node->children);
3272 case XML_DOCUMENT_NODE:
3273 case XML_DOCUMENT_TYPE_NODE:
3274 case XML_DOCUMENT_FRAG_NODE:
3275 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003276#ifdef LIBXML_DOCB_ENABLED
3277 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003278#endif
3279 return(((xmlDocPtr) ctxt->context->node)->children);
3280 case XML_ELEMENT_DECL:
3281 case XML_ATTRIBUTE_DECL:
3282 case XML_ENTITY_DECL:
3283 case XML_ATTRIBUTE_NODE:
3284 case XML_NAMESPACE_DECL:
3285 case XML_XINCLUDE_START:
3286 case XML_XINCLUDE_END:
3287 return(NULL);
3288 }
3289 return(NULL);
3290 }
3291 if ((cur->type == XML_DOCUMENT_NODE) ||
3292 (cur->type == XML_HTML_DOCUMENT_NODE))
3293 return(NULL);
3294 return(cur->next);
3295}
3296
3297/**
3298 * xmlXPathNextDescendant:
3299 * @ctxt: the XPath Parser context
3300 * @cur: the current node in the traversal
3301 *
3302 * Traversal function for the "descendant" direction
3303 * the descendant axis contains the descendants of the context node in document
3304 * order; a descendant is a child or a child of a child and so on.
3305 *
3306 * Returns the next element following that axis
3307 */
3308xmlNodePtr
3309xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3310 if (cur == NULL) {
3311 if (ctxt->context->node == NULL)
3312 return(NULL);
3313 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3314 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3315 return(NULL);
3316
3317 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3318 return(ctxt->context->doc->children);
3319 return(ctxt->context->node->children);
3320 }
3321
3322 if (cur->children != NULL)
3323 {
3324 if (cur->children->type != XML_ENTITY_DECL)
3325 return(cur->children);
3326 }
3327 if (cur->next != NULL) return(cur->next);
3328
3329 do {
3330 cur = cur->parent;
3331 if (cur == NULL) return(NULL);
3332 if (cur == ctxt->context->node) return(NULL);
3333 if (cur->next != NULL) {
3334 cur = cur->next;
3335 return(cur);
3336 }
3337 } while (cur != NULL);
3338 return(cur);
3339}
3340
3341/**
3342 * xmlXPathNextDescendantOrSelf:
3343 * @ctxt: the XPath Parser context
3344 * @cur: the current node in the traversal
3345 *
3346 * Traversal function for the "descendant-or-self" direction
3347 * the descendant-or-self axis contains the context node and the descendants
3348 * of the context node in document order; thus the context node is the first
3349 * node on the axis, and the first child of the context node is the second node
3350 * on the axis
3351 *
3352 * Returns the next element following that axis
3353 */
3354xmlNodePtr
3355xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3356 if (cur == NULL) {
3357 if (ctxt->context->node == NULL)
3358 return(NULL);
3359 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3360 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3361 return(NULL);
3362 return(ctxt->context->node);
3363 }
3364
3365 return(xmlXPathNextDescendant(ctxt, cur));
3366}
3367
3368/**
3369 * xmlXPathNextParent:
3370 * @ctxt: the XPath Parser context
3371 * @cur: the current node in the traversal
3372 *
3373 * Traversal function for the "parent" direction
3374 * The parent axis contains the parent of the context node, if there is one.
3375 *
3376 * Returns the next element following that axis
3377 */
3378xmlNodePtr
3379xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3380 /*
3381 * the parent of an attribute or namespace node is the element
3382 * to which the attribute or namespace node is attached
3383 * Namespace handling !!!
3384 */
3385 if (cur == NULL) {
3386 if (ctxt->context->node == NULL) return(NULL);
3387 switch (ctxt->context->node->type) {
3388 case XML_ELEMENT_NODE:
3389 case XML_TEXT_NODE:
3390 case XML_CDATA_SECTION_NODE:
3391 case XML_ENTITY_REF_NODE:
3392 case XML_ENTITY_NODE:
3393 case XML_PI_NODE:
3394 case XML_COMMENT_NODE:
3395 case XML_NOTATION_NODE:
3396 case XML_DTD_NODE:
3397 case XML_ELEMENT_DECL:
3398 case XML_ATTRIBUTE_DECL:
3399 case XML_XINCLUDE_START:
3400 case XML_XINCLUDE_END:
3401 case XML_ENTITY_DECL:
3402 if (ctxt->context->node->parent == NULL)
3403 return((xmlNodePtr) ctxt->context->doc);
3404 return(ctxt->context->node->parent);
3405 case XML_ATTRIBUTE_NODE: {
3406 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3407
3408 return(att->parent);
3409 }
3410 case XML_DOCUMENT_NODE:
3411 case XML_DOCUMENT_TYPE_NODE:
3412 case XML_DOCUMENT_FRAG_NODE:
3413 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003414#ifdef LIBXML_DOCB_ENABLED
3415 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003416#endif
3417 return(NULL);
3418 case XML_NAMESPACE_DECL:
3419 /*
3420 * TODO !!! may require extending struct _xmlNs with
3421 * parent field
3422 * C.f. Infoset case...
3423 */
3424 return(NULL);
3425 }
3426 }
3427 return(NULL);
3428}
3429
3430/**
3431 * xmlXPathNextAncestor:
3432 * @ctxt: the XPath Parser context
3433 * @cur: the current node in the traversal
3434 *
3435 * Traversal function for the "ancestor" direction
3436 * the ancestor axis contains the ancestors of the context node; the ancestors
3437 * of the context node consist of the parent of context node and the parent's
3438 * parent and so on; the nodes are ordered in reverse document order; thus the
3439 * parent is the first node on the axis, and the parent's parent is the second
3440 * node on the axis
3441 *
3442 * Returns the next element following that axis
3443 */
3444xmlNodePtr
3445xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3446 /*
3447 * the parent of an attribute or namespace node is the element
3448 * to which the attribute or namespace node is attached
3449 * !!!!!!!!!!!!!
3450 */
3451 if (cur == NULL) {
3452 if (ctxt->context->node == NULL) return(NULL);
3453 switch (ctxt->context->node->type) {
3454 case XML_ELEMENT_NODE:
3455 case XML_TEXT_NODE:
3456 case XML_CDATA_SECTION_NODE:
3457 case XML_ENTITY_REF_NODE:
3458 case XML_ENTITY_NODE:
3459 case XML_PI_NODE:
3460 case XML_COMMENT_NODE:
3461 case XML_DTD_NODE:
3462 case XML_ELEMENT_DECL:
3463 case XML_ATTRIBUTE_DECL:
3464 case XML_ENTITY_DECL:
3465 case XML_NOTATION_NODE:
3466 case XML_XINCLUDE_START:
3467 case XML_XINCLUDE_END:
3468 if (ctxt->context->node->parent == NULL)
3469 return((xmlNodePtr) ctxt->context->doc);
3470 return(ctxt->context->node->parent);
3471 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003472 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003473
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003474 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00003475 }
3476 case XML_DOCUMENT_NODE:
3477 case XML_DOCUMENT_TYPE_NODE:
3478 case XML_DOCUMENT_FRAG_NODE:
3479 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003480#ifdef LIBXML_DOCB_ENABLED
3481 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003482#endif
3483 return(NULL);
3484 case XML_NAMESPACE_DECL:
3485 /*
3486 * TODO !!! may require extending struct _xmlNs with
3487 * parent field
3488 * C.f. Infoset case...
3489 */
3490 return(NULL);
3491 }
3492 return(NULL);
3493 }
3494 if (cur == ctxt->context->doc->children)
3495 return((xmlNodePtr) ctxt->context->doc);
3496 if (cur == (xmlNodePtr) ctxt->context->doc)
3497 return(NULL);
3498 switch (cur->type) {
3499 case XML_ELEMENT_NODE:
3500 case XML_TEXT_NODE:
3501 case XML_CDATA_SECTION_NODE:
3502 case XML_ENTITY_REF_NODE:
3503 case XML_ENTITY_NODE:
3504 case XML_PI_NODE:
3505 case XML_COMMENT_NODE:
3506 case XML_NOTATION_NODE:
3507 case XML_DTD_NODE:
3508 case XML_ELEMENT_DECL:
3509 case XML_ATTRIBUTE_DECL:
3510 case XML_ENTITY_DECL:
3511 case XML_XINCLUDE_START:
3512 case XML_XINCLUDE_END:
3513 return(cur->parent);
3514 case XML_ATTRIBUTE_NODE: {
3515 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3516
3517 return(att->parent);
3518 }
3519 case XML_DOCUMENT_NODE:
3520 case XML_DOCUMENT_TYPE_NODE:
3521 case XML_DOCUMENT_FRAG_NODE:
3522 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003523#ifdef LIBXML_DOCB_ENABLED
3524 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003525#endif
3526 return(NULL);
3527 case XML_NAMESPACE_DECL:
3528 /*
3529 * TODO !!! may require extending struct _xmlNs with
3530 * parent field
3531 * C.f. Infoset case...
3532 */
3533 return(NULL);
3534 }
3535 return(NULL);
3536}
3537
3538/**
3539 * xmlXPathNextAncestorOrSelf:
3540 * @ctxt: the XPath Parser context
3541 * @cur: the current node in the traversal
3542 *
3543 * Traversal function for the "ancestor-or-self" direction
3544 * he ancestor-or-self axis contains the context node and ancestors of
3545 * the context node in reverse document order; thus the context node is
3546 * the first node on the axis, and the context node's parent the second;
3547 * parent here is defined the same as with the parent axis.
3548 *
3549 * Returns the next element following that axis
3550 */
3551xmlNodePtr
3552xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3553 if (cur == NULL)
3554 return(ctxt->context->node);
3555 return(xmlXPathNextAncestor(ctxt, cur));
3556}
3557
3558/**
3559 * xmlXPathNextFollowingSibling:
3560 * @ctxt: the XPath Parser context
3561 * @cur: the current node in the traversal
3562 *
3563 * Traversal function for the "following-sibling" direction
3564 * The following-sibling axis contains the following siblings of the context
3565 * node in document order.
3566 *
3567 * Returns the next element following that axis
3568 */
3569xmlNodePtr
3570xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3571 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3572 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3573 return(NULL);
3574 if (cur == (xmlNodePtr) ctxt->context->doc)
3575 return(NULL);
3576 if (cur == NULL)
3577 return(ctxt->context->node->next);
3578 return(cur->next);
3579}
3580
3581/**
3582 * xmlXPathNextPrecedingSibling:
3583 * @ctxt: the XPath Parser context
3584 * @cur: the current node in the traversal
3585 *
3586 * Traversal function for the "preceding-sibling" direction
3587 * The preceding-sibling axis contains the preceding siblings of the context
3588 * node in reverse document order; the first preceding sibling is first on the
3589 * axis; the sibling preceding that node is the second on the axis and so on.
3590 *
3591 * Returns the next element following that axis
3592 */
3593xmlNodePtr
3594xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3595 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3596 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3597 return(NULL);
3598 if (cur == (xmlNodePtr) ctxt->context->doc)
3599 return(NULL);
3600 if (cur == NULL)
3601 return(ctxt->context->node->prev);
3602 return(cur->prev);
3603}
3604
3605/**
3606 * xmlXPathNextFollowing:
3607 * @ctxt: the XPath Parser context
3608 * @cur: the current node in the traversal
3609 *
3610 * Traversal function for the "following" direction
3611 * The following axis contains all nodes in the same document as the context
3612 * node that are after the context node in document order, excluding any
3613 * descendants and excluding attribute nodes and namespace nodes; the nodes
3614 * are ordered in document order
3615 *
3616 * Returns the next element following that axis
3617 */
3618xmlNodePtr
3619xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3620 if (cur != NULL && cur->children != NULL)
3621 return cur->children ;
3622 if (cur == NULL) cur = ctxt->context->node;
3623 if (cur == NULL) return(NULL) ; /* ERROR */
3624 if (cur->next != NULL) return(cur->next) ;
3625 do {
3626 cur = cur->parent;
3627 if (cur == NULL) return(NULL);
3628 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3629 if (cur->next != NULL) return(cur->next);
3630 } while (cur != NULL);
3631 return(cur);
3632}
3633
3634/*
3635 * xmlXPathIsAncestor:
3636 * @ancestor: the ancestor node
3637 * @node: the current node
3638 *
3639 * Check that @ancestor is a @node's ancestor
3640 *
3641 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3642 */
3643static int
3644xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3645 if ((ancestor == NULL) || (node == NULL)) return(0);
3646 /* nodes need to be in the same document */
3647 if (ancestor->doc != node->doc) return(0);
3648 /* avoid searching if ancestor or node is the root node */
3649 if (ancestor == (xmlNodePtr) node->doc) return(1);
3650 if (node == (xmlNodePtr) ancestor->doc) return(0);
3651 while (node->parent != NULL) {
3652 if (node->parent == ancestor)
3653 return(1);
3654 node = node->parent;
3655 }
3656 return(0);
3657}
3658
3659/**
3660 * xmlXPathNextPreceding:
3661 * @ctxt: the XPath Parser context
3662 * @cur: the current node in the traversal
3663 *
3664 * Traversal function for the "preceding" direction
3665 * the preceding axis contains all nodes in the same document as the context
3666 * node that are before the context node in document order, excluding any
3667 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3668 * ordered in reverse document order
3669 *
3670 * Returns the next element following that axis
3671 */
3672xmlNodePtr
3673xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3674 if (cur == NULL)
3675 cur = ctxt->context->node ;
3676 do {
3677 if (cur->prev != NULL) {
3678 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3679 ;
3680 return(cur) ;
3681 }
3682
3683 cur = cur->parent;
3684 if (cur == NULL) return(NULL);
3685 if (cur == ctxt->context->doc->children) return(NULL);
3686 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3687 return(cur);
3688}
3689
3690/**
3691 * xmlXPathNextNamespace:
3692 * @ctxt: the XPath Parser context
3693 * @cur: the current attribute in the traversal
3694 *
3695 * Traversal function for the "namespace" direction
3696 * the namespace axis contains the namespace nodes of the context node;
3697 * the order of nodes on this axis is implementation-defined; the axis will
3698 * be empty unless the context node is an element
3699 *
3700 * Returns the next element following that axis
3701 */
3702xmlNodePtr
3703xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3704 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3705 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3706 if (ctxt->context->namespaces != NULL)
3707 xmlFree(ctxt->context->namespaces);
3708 ctxt->context->namespaces =
3709 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3710 if (ctxt->context->namespaces == NULL) return(NULL);
3711 ctxt->context->nsNr = 0;
3712 }
3713 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3714}
3715
3716/**
3717 * xmlXPathNextAttribute:
3718 * @ctxt: the XPath Parser context
3719 * @cur: the current attribute in the traversal
3720 *
3721 * Traversal function for the "attribute" direction
3722 * TODO: support DTD inherited default attributes
3723 *
3724 * Returns the next element following that axis
3725 */
3726xmlNodePtr
3727xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00003728 if (ctxt->context->node == NULL)
3729 return(NULL);
3730 if (ctxt->context->node->type != XML_ELEMENT_NODE)
3731 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003732 if (cur == NULL) {
3733 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3734 return(NULL);
3735 return((xmlNodePtr)ctxt->context->node->properties);
3736 }
3737 return((xmlNodePtr)cur->next);
3738}
3739
3740/************************************************************************
3741 * *
3742 * NodeTest Functions *
3743 * *
3744 ************************************************************************/
3745
Owen Taylor3473f882001-02-23 17:55:21 +00003746#define IS_FUNCTION 200
3747
Owen Taylor3473f882001-02-23 17:55:21 +00003748
3749/************************************************************************
3750 * *
3751 * Implicit tree core function library *
3752 * *
3753 ************************************************************************/
3754
3755/**
3756 * xmlXPathRoot:
3757 * @ctxt: the XPath Parser context
3758 *
3759 * Initialize the context to the root of the document
3760 */
3761void
3762xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
3763 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
3764 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3765}
3766
3767/************************************************************************
3768 * *
3769 * The explicit core function library *
3770 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
3771 * *
3772 ************************************************************************/
3773
3774
3775/**
3776 * xmlXPathLastFunction:
3777 * @ctxt: the XPath Parser context
3778 * @nargs: the number of arguments
3779 *
3780 * Implement the last() XPath function
3781 * number last()
3782 * The last function returns the number of nodes in the context node list.
3783 */
3784void
3785xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3786 CHECK_ARITY(0);
3787 if (ctxt->context->contextSize >= 0) {
3788 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
3789#ifdef DEBUG_EXPR
3790 xmlGenericError(xmlGenericErrorContext,
3791 "last() : %d\n", ctxt->context->contextSize);
3792#endif
3793 } else {
3794 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
3795 }
3796}
3797
3798/**
3799 * xmlXPathPositionFunction:
3800 * @ctxt: the XPath Parser context
3801 * @nargs: the number of arguments
3802 *
3803 * Implement the position() XPath function
3804 * number position()
3805 * The position function returns the position of the context node in the
3806 * context node list. The first position is 1, and so the last positionr
3807 * will be equal to last().
3808 */
3809void
3810xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3811 CHECK_ARITY(0);
3812 if (ctxt->context->proximityPosition >= 0) {
3813 valuePush(ctxt,
3814 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
3815#ifdef DEBUG_EXPR
3816 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
3817 ctxt->context->proximityPosition);
3818#endif
3819 } else {
3820 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
3821 }
3822}
3823
3824/**
3825 * xmlXPathCountFunction:
3826 * @ctxt: the XPath Parser context
3827 * @nargs: the number of arguments
3828 *
3829 * Implement the count() XPath function
3830 * number count(node-set)
3831 */
3832void
3833xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3834 xmlXPathObjectPtr cur;
3835
3836 CHECK_ARITY(1);
3837 if ((ctxt->value == NULL) ||
3838 ((ctxt->value->type != XPATH_NODESET) &&
3839 (ctxt->value->type != XPATH_XSLT_TREE)))
3840 XP_ERROR(XPATH_INVALID_TYPE);
3841 cur = valuePop(ctxt);
3842
Daniel Veillard911f49a2001-04-07 15:39:35 +00003843 if ((cur == NULL) || (cur->nodesetval == NULL))
3844 valuePush(ctxt, xmlXPathNewFloat((double) 0));
3845 else
3846 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Owen Taylor3473f882001-02-23 17:55:21 +00003847 xmlXPathFreeObject(cur);
3848}
3849
3850/**
3851 * xmlXPathIdFunction:
3852 * @ctxt: the XPath Parser context
3853 * @nargs: the number of arguments
3854 *
3855 * Implement the id() XPath function
3856 * node-set id(object)
3857 * The id function selects elements by their unique ID
3858 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
3859 * then the result is the union of the result of applying id to the
3860 * string value of each of the nodes in the argument node-set. When the
3861 * argument to id is of any other type, the argument is converted to a
3862 * string as if by a call to the string function; the string is split
3863 * into a whitespace-separated list of tokens (whitespace is any sequence
3864 * of characters matching the production S); the result is a node-set
3865 * containing the elements in the same document as the context node that
3866 * have a unique ID equal to any of the tokens in the list.
3867 */
3868void
3869xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3870 const xmlChar *tokens;
3871 const xmlChar *cur;
3872 xmlChar *ID;
3873 xmlAttrPtr attr;
3874 xmlNodePtr elem = NULL;
3875 xmlXPathObjectPtr ret, obj;
3876
3877 CHECK_ARITY(1);
3878 obj = valuePop(ctxt);
3879 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3880 if (obj->type == XPATH_NODESET) {
3881 xmlXPathObjectPtr newobj;
3882 int i;
3883
3884 ret = xmlXPathNewNodeSet(NULL);
3885
Daniel Veillard911f49a2001-04-07 15:39:35 +00003886 if (obj->nodesetval != NULL) {
3887 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3888 valuePush(ctxt,
3889 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
3890 xmlXPathStringFunction(ctxt, 1);
3891 xmlXPathIdFunction(ctxt, 1);
3892 newobj = valuePop(ctxt);
3893 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
3894 newobj->nodesetval);
3895 xmlXPathFreeObject(newobj);
3896 }
Owen Taylor3473f882001-02-23 17:55:21 +00003897 }
3898
3899 xmlXPathFreeObject(obj);
3900 valuePush(ctxt, ret);
3901 return;
3902 }
3903 if (obj->type != XPATH_STRING) {
3904 valuePush(ctxt, obj);
3905 xmlXPathStringFunction(ctxt, 1);
3906 obj = valuePop(ctxt);
3907 if (obj->type != XPATH_STRING) {
3908 xmlXPathFreeObject(obj);
3909 return;
3910 }
3911 }
3912 tokens = obj->stringval;
3913
3914 ret = xmlXPathNewNodeSet(NULL);
3915 valuePush(ctxt, ret);
3916 if (tokens == NULL) {
3917 xmlXPathFreeObject(obj);
3918 return;
3919 }
3920
3921 cur = tokens;
3922
3923 while (IS_BLANK(*cur)) cur++;
3924 while (*cur != 0) {
3925 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
3926 (*cur == '.') || (*cur == '-') ||
3927 (*cur == '_') || (*cur == ':') ||
3928 (IS_COMBINING(*cur)) ||
3929 (IS_EXTENDER(*cur)))
3930 cur++;
3931
3932 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
3933
3934 ID = xmlStrndup(tokens, cur - tokens);
3935 attr = xmlGetID(ctxt->context->doc, ID);
3936 if (attr != NULL) {
3937 elem = attr->parent;
3938 xmlXPathNodeSetAdd(ret->nodesetval, elem);
3939 }
3940 if (ID != NULL)
3941 xmlFree(ID);
3942
3943 while (IS_BLANK(*cur)) cur++;
3944 tokens = cur;
3945 }
3946 xmlXPathFreeObject(obj);
3947 return;
3948}
3949
3950/**
3951 * xmlXPathLocalNameFunction:
3952 * @ctxt: the XPath Parser context
3953 * @nargs: the number of arguments
3954 *
3955 * Implement the local-name() XPath function
3956 * string local-name(node-set?)
3957 * The local-name function returns a string containing the local part
3958 * of the name of the node in the argument node-set that is first in
3959 * document order. If the node-set is empty or the first node has no
3960 * name, an empty string is returned. If the argument is omitted it
3961 * defaults to the context node.
3962 */
3963void
3964xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3965 xmlXPathObjectPtr cur;
3966
3967 if (nargs == 0) {
3968 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3969 nargs = 1;
3970 }
3971
3972 CHECK_ARITY(1);
3973 if ((ctxt->value == NULL) ||
3974 ((ctxt->value->type != XPATH_NODESET) &&
3975 (ctxt->value->type != XPATH_XSLT_TREE)))
3976 XP_ERROR(XPATH_INVALID_TYPE);
3977 cur = valuePop(ctxt);
3978
Daniel Veillard911f49a2001-04-07 15:39:35 +00003979 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003980 valuePush(ctxt, xmlXPathNewCString(""));
3981 } else {
3982 int i = 0; /* Should be first in document order !!!!! */
3983 switch (cur->nodesetval->nodeTab[i]->type) {
3984 case XML_ELEMENT_NODE:
3985 case XML_ATTRIBUTE_NODE:
3986 case XML_PI_NODE:
3987 valuePush(ctxt,
3988 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
3989 break;
3990 case XML_NAMESPACE_DECL:
3991 valuePush(ctxt, xmlXPathNewString(
3992 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
3993 break;
3994 default:
3995 valuePush(ctxt, xmlXPathNewCString(""));
3996 }
3997 }
3998 xmlXPathFreeObject(cur);
3999}
4000
4001/**
4002 * xmlXPathNamespaceURIFunction:
4003 * @ctxt: the XPath Parser context
4004 * @nargs: the number of arguments
4005 *
4006 * Implement the namespace-uri() XPath function
4007 * string namespace-uri(node-set?)
4008 * The namespace-uri function returns a string containing the
4009 * namespace URI of the expanded name of the node in the argument
4010 * node-set that is first in document order. If the node-set is empty,
4011 * the first node has no name, or the expanded name has no namespace
4012 * URI, an empty string is returned. If the argument is omitted it
4013 * defaults to the context node.
4014 */
4015void
4016xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4017 xmlXPathObjectPtr cur;
4018
4019 if (nargs == 0) {
4020 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4021 nargs = 1;
4022 }
4023 CHECK_ARITY(1);
4024 if ((ctxt->value == NULL) ||
4025 ((ctxt->value->type != XPATH_NODESET) &&
4026 (ctxt->value->type != XPATH_XSLT_TREE)))
4027 XP_ERROR(XPATH_INVALID_TYPE);
4028 cur = valuePop(ctxt);
4029
Daniel Veillard911f49a2001-04-07 15:39:35 +00004030 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004031 valuePush(ctxt, xmlXPathNewCString(""));
4032 } else {
4033 int i = 0; /* Should be first in document order !!!!! */
4034 switch (cur->nodesetval->nodeTab[i]->type) {
4035 case XML_ELEMENT_NODE:
4036 case XML_ATTRIBUTE_NODE:
4037 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4038 valuePush(ctxt, xmlXPathNewCString(""));
4039 else
4040 valuePush(ctxt, xmlXPathNewString(
4041 cur->nodesetval->nodeTab[i]->ns->href));
4042 break;
4043 default:
4044 valuePush(ctxt, xmlXPathNewCString(""));
4045 }
4046 }
4047 xmlXPathFreeObject(cur);
4048}
4049
4050/**
4051 * xmlXPathNameFunction:
4052 * @ctxt: the XPath Parser context
4053 * @nargs: the number of arguments
4054 *
4055 * Implement the name() XPath function
4056 * string name(node-set?)
4057 * The name function returns a string containing a QName representing
4058 * the name of the node in the argument node-set that is first in documenti
4059 * order. The QName must represent the name with respect to the namespace
4060 * declarations in effect on the node whose name is being represented.
4061 * Typically, this will be the form in which the name occurred in the XML
4062 * source. This need not be the case if there are namespace declarations
4063 * in effect on the node that associate multiple prefixes with the same
4064 * namespace. However, an implementation may include information about
4065 * the original prefix in its representation of nodes; in this case, an
4066 * implementation can ensure that the returned string is always the same
4067 * as the QName used in the XML source. If the argument it omitted it
4068 * defaults to the context node.
4069 * Libxml keep the original prefix so the "real qualified name" used is
4070 * returned.
4071 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004072static void
Owen Taylor3473f882001-02-23 17:55:21 +00004073xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4074 xmlXPathObjectPtr cur;
4075
4076 if (nargs == 0) {
4077 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4078 nargs = 1;
4079 }
4080
4081 CHECK_ARITY(1);
4082 if ((ctxt->value == NULL) ||
4083 ((ctxt->value->type != XPATH_NODESET) &&
4084 (ctxt->value->type != XPATH_XSLT_TREE)))
4085 XP_ERROR(XPATH_INVALID_TYPE);
4086 cur = valuePop(ctxt);
4087
Daniel Veillard911f49a2001-04-07 15:39:35 +00004088 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004089 valuePush(ctxt, xmlXPathNewCString(""));
4090 } else {
4091 int i = 0; /* Should be first in document order !!!!! */
4092
4093 switch (cur->nodesetval->nodeTab[i]->type) {
4094 case XML_ELEMENT_NODE:
4095 case XML_ATTRIBUTE_NODE:
4096 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4097 valuePush(ctxt, xmlXPathNewString(
4098 cur->nodesetval->nodeTab[i]->name));
4099
4100 else {
4101 char name[2000];
Owen Taylor3473f882001-02-23 17:55:21 +00004102 snprintf(name, sizeof(name), "%s:%s",
4103 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4104 (char *) cur->nodesetval->nodeTab[i]->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004105 name[sizeof(name) - 1] = 0;
4106 valuePush(ctxt, xmlXPathNewCString(name));
4107 }
4108 break;
4109 default:
4110 valuePush(ctxt,
4111 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4112 xmlXPathLocalNameFunction(ctxt, 1);
4113 }
4114 }
4115 xmlXPathFreeObject(cur);
4116}
4117
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004118
4119/**
4120 * xmlXPathConvertString:
4121 * @val: an XPath object
4122 *
4123 * Converts an existing object to its string() equivalent
4124 *
4125 * Returns the new object, the old one is freed (or the operation
4126 * is done directly on @val)
4127 */
4128xmlXPathObjectPtr
4129xmlXPathConvertString(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004130 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004131
4132 if (val == NULL)
4133 return(xmlXPathNewCString(""));
4134 switch (val->type) {
4135 case XPATH_UNDEFINED:
4136#ifdef DEBUG_EXPR
4137 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4138#endif
4139 ret = xmlXPathNewCString("");
4140 break;
4141 case XPATH_XSLT_TREE:
4142 case XPATH_NODESET:
Daniel Veillard911f49a2001-04-07 15:39:35 +00004143 if ((val->nodesetval == NULL) || (val->nodesetval->nodeNr == 0)) {
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004144 ret = xmlXPathNewCString("");
4145 } else {
4146 xmlChar *res;
4147
4148 xmlXPathNodeSetSort(val->nodesetval);
4149 res = xmlNodeGetContent(val->nodesetval->nodeTab[0]);
4150 /* TODO: avoid allocating res to free it */
4151 ret = xmlXPathNewString(res);
4152 if (res != NULL)
4153 xmlFree(res);
4154 }
4155 break;
4156 case XPATH_STRING:
4157 return(val);
4158 case XPATH_BOOLEAN:
4159 if (val->boolval) ret = xmlXPathNewCString("true");
4160 else ret = xmlXPathNewCString("false");
4161 break;
4162 case XPATH_NUMBER: {
4163 char buf[100];
4164
4165 xmlXPathFormatNumber(val->floatval, buf, sizeof(buf));
4166 ret = xmlXPathNewCString(buf);
4167 break;
4168 }
4169 case XPATH_USERS:
4170 case XPATH_POINT:
4171 case XPATH_RANGE:
4172 case XPATH_LOCATIONSET:
4173 TODO
4174 ret = xmlXPathNewCString("");
4175 break;
4176 }
4177 xmlXPathFreeObject(val);
4178 return(ret);
4179}
4180
Owen Taylor3473f882001-02-23 17:55:21 +00004181/**
4182 * xmlXPathStringFunction:
4183 * @ctxt: the XPath Parser context
4184 * @nargs: the number of arguments
4185 *
4186 * Implement the string() XPath function
4187 * string string(object?)
4188 * he string function converts an object to a string as follows:
4189 * - A node-set is converted to a string by returning the value of
4190 * the node in the node-set that is first in document order.
4191 * If the node-set is empty, an empty string is returned.
4192 * - A number is converted to a string as follows
4193 * + NaN is converted to the string NaN
4194 * + positive zero is converted to the string 0
4195 * + negative zero is converted to the string 0
4196 * + positive infinity is converted to the string Infinity
4197 * + negative infinity is converted to the string -Infinity
4198 * + if the number is an integer, the number is represented in
4199 * decimal form as a Number with no decimal point and no leading
4200 * zeros, preceded by a minus sign (-) if the number is negative
4201 * + otherwise, the number is represented in decimal form as a
4202 * Number including a decimal point with at least one digit
4203 * before the decimal point and at least one digit after the
4204 * decimal point, preceded by a minus sign (-) if the number
4205 * is negative; there must be no leading zeros before the decimal
4206 * point apart possibly from the one required digit immediatelyi
4207 * before the decimal point; beyond the one required digit
4208 * after the decimal point there must be as many, but only as
4209 * many, more digits as are needed to uniquely distinguish the
4210 * number from all other IEEE 754 numeric values.
4211 * - The boolean false value is converted to the string false.
4212 * The boolean true value is converted to the string true.
4213 *
4214 * If the argument is omitted, it defaults to a node-set with the
4215 * context node as its only member.
4216 */
4217void
4218xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4219 xmlXPathObjectPtr cur;
4220
4221 if (nargs == 0) {
4222 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4223 nargs = 1;
4224 }
4225
4226 CHECK_ARITY(1);
4227 cur = valuePop(ctxt);
4228 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004229 cur = xmlXPathConvertString(cur);
4230 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004231}
4232
4233/**
4234 * xmlXPathStringLengthFunction:
4235 * @ctxt: the XPath Parser context
4236 * @nargs: the number of arguments
4237 *
4238 * Implement the string-length() XPath function
4239 * number string-length(string?)
4240 * The string-length returns the number of characters in the string
4241 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4242 * the context node converted to a string, in other words the value
4243 * of the context node.
4244 */
4245void
4246xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4247 xmlXPathObjectPtr cur;
4248
4249 if (nargs == 0) {
4250 if (ctxt->context->node == NULL) {
4251 valuePush(ctxt, xmlXPathNewFloat(0));
4252 } else {
4253 xmlChar *content;
4254
4255 content = xmlNodeGetContent(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004256 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00004257 xmlFree(content);
4258 }
4259 return;
4260 }
4261 CHECK_ARITY(1);
4262 CAST_TO_STRING;
4263 CHECK_TYPE(XPATH_STRING);
4264 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004265 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00004266 xmlXPathFreeObject(cur);
4267}
4268
4269/**
4270 * xmlXPathConcatFunction:
4271 * @ctxt: the XPath Parser context
4272 * @nargs: the number of arguments
4273 *
4274 * Implement the concat() XPath function
4275 * string concat(string, string, string*)
4276 * The concat function returns the concatenation of its arguments.
4277 */
4278void
4279xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4280 xmlXPathObjectPtr cur, newobj;
4281 xmlChar *tmp;
4282
4283 if (nargs < 2) {
4284 CHECK_ARITY(2);
4285 }
4286
4287 CAST_TO_STRING;
4288 cur = valuePop(ctxt);
4289 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4290 xmlXPathFreeObject(cur);
4291 return;
4292 }
4293 nargs--;
4294
4295 while (nargs > 0) {
4296 CAST_TO_STRING;
4297 newobj = valuePop(ctxt);
4298 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4299 xmlXPathFreeObject(newobj);
4300 xmlXPathFreeObject(cur);
4301 XP_ERROR(XPATH_INVALID_TYPE);
4302 }
4303 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4304 newobj->stringval = cur->stringval;
4305 cur->stringval = tmp;
4306
4307 xmlXPathFreeObject(newobj);
4308 nargs--;
4309 }
4310 valuePush(ctxt, cur);
4311}
4312
4313/**
4314 * xmlXPathContainsFunction:
4315 * @ctxt: the XPath Parser context
4316 * @nargs: the number of arguments
4317 *
4318 * Implement the contains() XPath function
4319 * boolean contains(string, string)
4320 * The contains function returns true if the first argument string
4321 * contains the second argument string, and otherwise returns false.
4322 */
4323void
4324xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4325 xmlXPathObjectPtr hay, needle;
4326
4327 CHECK_ARITY(2);
4328 CAST_TO_STRING;
4329 CHECK_TYPE(XPATH_STRING);
4330 needle = valuePop(ctxt);
4331 CAST_TO_STRING;
4332 hay = valuePop(ctxt);
4333 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4334 xmlXPathFreeObject(hay);
4335 xmlXPathFreeObject(needle);
4336 XP_ERROR(XPATH_INVALID_TYPE);
4337 }
4338 if (xmlStrstr(hay->stringval, needle->stringval))
4339 valuePush(ctxt, xmlXPathNewBoolean(1));
4340 else
4341 valuePush(ctxt, xmlXPathNewBoolean(0));
4342 xmlXPathFreeObject(hay);
4343 xmlXPathFreeObject(needle);
4344}
4345
4346/**
4347 * xmlXPathStartsWithFunction:
4348 * @ctxt: the XPath Parser context
4349 * @nargs: the number of arguments
4350 *
4351 * Implement the starts-with() XPath function
4352 * boolean starts-with(string, string)
4353 * The starts-with function returns true if the first argument string
4354 * starts with the second argument string, and otherwise returns false.
4355 */
4356void
4357xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4358 xmlXPathObjectPtr hay, needle;
4359 int n;
4360
4361 CHECK_ARITY(2);
4362 CAST_TO_STRING;
4363 CHECK_TYPE(XPATH_STRING);
4364 needle = valuePop(ctxt);
4365 CAST_TO_STRING;
4366 hay = valuePop(ctxt);
4367 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4368 xmlXPathFreeObject(hay);
4369 xmlXPathFreeObject(needle);
4370 XP_ERROR(XPATH_INVALID_TYPE);
4371 }
4372 n = xmlStrlen(needle->stringval);
4373 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4374 valuePush(ctxt, xmlXPathNewBoolean(0));
4375 else
4376 valuePush(ctxt, xmlXPathNewBoolean(1));
4377 xmlXPathFreeObject(hay);
4378 xmlXPathFreeObject(needle);
4379}
4380
4381/**
4382 * xmlXPathSubstringFunction:
4383 * @ctxt: the XPath Parser context
4384 * @nargs: the number of arguments
4385 *
4386 * Implement the substring() XPath function
4387 * string substring(string, number, number?)
4388 * The substring function returns the substring of the first argument
4389 * starting at the position specified in the second argument with
4390 * length specified in the third argument. For example,
4391 * substring("12345",2,3) returns "234". If the third argument is not
4392 * specified, it returns the substring starting at the position specified
4393 * in the second argument and continuing to the end of the string. For
4394 * example, substring("12345",2) returns "2345". More precisely, each
4395 * character in the string (see [3.6 Strings]) is considered to have a
4396 * numeric position: the position of the first character is 1, the position
4397 * of the second character is 2 and so on. The returned substring contains
4398 * those characters for which the position of the character is greater than
4399 * or equal to the second argument and, if the third argument is specified,
4400 * less than the sum of the second and third arguments; the comparisons
4401 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4402 * - substring("12345", 1.5, 2.6) returns "234"
4403 * - substring("12345", 0, 3) returns "12"
4404 * - substring("12345", 0 div 0, 3) returns ""
4405 * - substring("12345", 1, 0 div 0) returns ""
4406 * - substring("12345", -42, 1 div 0) returns "12345"
4407 * - substring("12345", -1 div 0, 1 div 0) returns ""
4408 */
4409void
4410xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4411 xmlXPathObjectPtr str, start, len;
4412 double le, in;
4413 int i, l;
4414 xmlChar *ret;
4415
4416 /*
Daniel Veillarde043ee12001-04-16 14:08:07 +00004417 * TODO: need to be converted to UTF8 strings
Owen Taylor3473f882001-02-23 17:55:21 +00004418 */
4419 if (nargs < 2) {
4420 CHECK_ARITY(2);
4421 }
4422 if (nargs > 3) {
4423 CHECK_ARITY(3);
4424 }
4425 if (nargs == 3) {
4426 CAST_TO_NUMBER;
4427 CHECK_TYPE(XPATH_NUMBER);
4428 len = valuePop(ctxt);
4429 le = len->floatval;
4430 xmlXPathFreeObject(len);
4431 } else {
4432 le = 2000000000;
4433 }
4434 CAST_TO_NUMBER;
4435 CHECK_TYPE(XPATH_NUMBER);
4436 start = valuePop(ctxt);
4437 in = start->floatval;
4438 xmlXPathFreeObject(start);
4439 CAST_TO_STRING;
4440 CHECK_TYPE(XPATH_STRING);
4441 str = valuePop(ctxt);
4442 le += in;
4443
4444 /* integer index of the first char */
4445 i = (int) in;
4446 if (((double)i) != in) i++;
4447
4448 /* integer index of the last char */
4449 l = (int) le;
4450 if (((double)l) != le) l++;
4451
4452 /* back to a zero based len */
4453 i--;
4454 l--;
4455
4456 /* check against the string len */
4457 if (l > 1024) {
4458 l = xmlStrlen(str->stringval);
4459 }
4460 if (i < 0) {
4461 i = 0;
4462 }
4463
4464 /* number of chars to copy */
4465 l -= i;
4466
4467 ret = xmlStrsub(str->stringval, i, l);
4468 if (ret == NULL)
4469 valuePush(ctxt, xmlXPathNewCString(""));
4470 else {
4471 valuePush(ctxt, xmlXPathNewString(ret));
4472 xmlFree(ret);
4473 }
4474 xmlXPathFreeObject(str);
4475}
4476
4477/**
4478 * xmlXPathSubstringBeforeFunction:
4479 * @ctxt: the XPath Parser context
4480 * @nargs: the number of arguments
4481 *
4482 * Implement the substring-before() XPath function
4483 * string substring-before(string, string)
4484 * The substring-before function returns the substring of the first
4485 * argument string that precedes the first occurrence of the second
4486 * argument string in the first argument string, or the empty string
4487 * if the first argument string does not contain the second argument
4488 * string. For example, substring-before("1999/04/01","/") returns 1999.
4489 */
4490void
4491xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4492 xmlXPathObjectPtr str;
4493 xmlXPathObjectPtr find;
4494 xmlBufferPtr target;
4495 const xmlChar *point;
4496 int offset;
4497
4498 CHECK_ARITY(2);
4499 CAST_TO_STRING;
4500 find = valuePop(ctxt);
4501 CAST_TO_STRING;
4502 str = valuePop(ctxt);
4503
4504 target = xmlBufferCreate();
4505 if (target) {
4506 point = xmlStrstr(str->stringval, find->stringval);
4507 if (point) {
4508 offset = (int)(point - str->stringval);
4509 xmlBufferAdd(target, str->stringval, offset);
4510 }
4511 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4512 xmlBufferFree(target);
4513 }
4514
4515 xmlXPathFreeObject(str);
4516 xmlXPathFreeObject(find);
4517}
4518
4519/**
4520 * xmlXPathSubstringAfterFunction:
4521 * @ctxt: the XPath Parser context
4522 * @nargs: the number of arguments
4523 *
4524 * Implement the substring-after() XPath function
4525 * string substring-after(string, string)
4526 * The substring-after function returns the substring of the first
4527 * argument string that follows the first occurrence of the second
4528 * argument string in the first argument string, or the empty stringi
4529 * if the first argument string does not contain the second argument
4530 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4531 * and substring-after("1999/04/01","19") returns 99/04/01.
4532 */
4533void
4534xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4535 xmlXPathObjectPtr str;
4536 xmlXPathObjectPtr find;
4537 xmlBufferPtr target;
4538 const xmlChar *point;
4539 int offset;
4540
4541 CHECK_ARITY(2);
4542 CAST_TO_STRING;
4543 find = valuePop(ctxt);
4544 CAST_TO_STRING;
4545 str = valuePop(ctxt);
4546
4547 target = xmlBufferCreate();
4548 if (target) {
4549 point = xmlStrstr(str->stringval, find->stringval);
4550 if (point) {
4551 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4552 xmlBufferAdd(target, &str->stringval[offset],
4553 xmlStrlen(str->stringval) - offset);
4554 }
4555 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4556 xmlBufferFree(target);
4557 }
4558
4559 xmlXPathFreeObject(str);
4560 xmlXPathFreeObject(find);
4561}
4562
4563/**
4564 * xmlXPathNormalizeFunction:
4565 * @ctxt: the XPath Parser context
4566 * @nargs: the number of arguments
4567 *
4568 * Implement the normalize-space() XPath function
4569 * string normalize-space(string?)
4570 * The normalize-space function returns the argument string with white
4571 * space normalized by stripping leading and trailing whitespace
4572 * and replacing sequences of whitespace characters by a single
4573 * space. Whitespace characters are the same allowed by the S production
4574 * in XML. If the argument is omitted, it defaults to the context
4575 * node converted to a string, in other words the value of the context node.
4576 */
4577void
4578xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4579 xmlXPathObjectPtr obj = NULL;
4580 xmlChar *source = NULL;
4581 xmlBufferPtr target;
4582 xmlChar blank;
4583
4584 if (nargs == 0) {
4585 /* Use current context node */
4586 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4587 xmlXPathStringFunction(ctxt, 1);
4588 nargs = 1;
4589 }
4590
4591 CHECK_ARITY(1);
4592 CAST_TO_STRING;
4593 CHECK_TYPE(XPATH_STRING);
4594 obj = valuePop(ctxt);
4595 source = obj->stringval;
4596
4597 target = xmlBufferCreate();
4598 if (target && source) {
4599
4600 /* Skip leading whitespaces */
4601 while (IS_BLANK(*source))
4602 source++;
4603
4604 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4605 blank = 0;
4606 while (*source) {
4607 if (IS_BLANK(*source)) {
4608 blank = *source;
4609 } else {
4610 if (blank) {
4611 xmlBufferAdd(target, &blank, 1);
4612 blank = 0;
4613 }
4614 xmlBufferAdd(target, source, 1);
4615 }
4616 source++;
4617 }
4618
4619 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4620 xmlBufferFree(target);
4621 }
4622 xmlXPathFreeObject(obj);
4623}
4624
4625/**
4626 * xmlXPathTranslateFunction:
4627 * @ctxt: the XPath Parser context
4628 * @nargs: the number of arguments
4629 *
4630 * Implement the translate() XPath function
4631 * string translate(string, string, string)
4632 * The translate function returns the first argument string with
4633 * occurrences of characters in the second argument string replaced
4634 * by the character at the corresponding position in the third argument
4635 * string. For example, translate("bar","abc","ABC") returns the string
4636 * BAr. If there is a character in the second argument string with no
4637 * character at a corresponding position in the third argument string
4638 * (because the second argument string is longer than the third argument
4639 * string), then occurrences of that character in the first argument
4640 * string are removed. For example, translate("--aaa--","abc-","ABC")
4641 * returns "AAA". If a character occurs more than once in second
4642 * argument string, then the first occurrence determines the replacement
4643 * character. If the third argument string is longer than the second
4644 * argument string, then excess characters are ignored.
4645 */
4646void
4647xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00004648 xmlXPathObjectPtr str;
4649 xmlXPathObjectPtr from;
4650 xmlXPathObjectPtr to;
4651 xmlBufferPtr target;
4652 int i, offset, max;
4653 xmlChar ch;
4654 const xmlChar *point;
Owen Taylor3473f882001-02-23 17:55:21 +00004655
Daniel Veillarde043ee12001-04-16 14:08:07 +00004656 /*
4657 * TODO: need to be converted to UTF8 strings
4658 */
4659 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00004660
Daniel Veillarde043ee12001-04-16 14:08:07 +00004661 CAST_TO_STRING;
4662 to = valuePop(ctxt);
4663 CAST_TO_STRING;
4664 from = valuePop(ctxt);
4665 CAST_TO_STRING;
4666 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00004667
Daniel Veillarde043ee12001-04-16 14:08:07 +00004668 target = xmlBufferCreate();
4669 if (target) {
4670 max = xmlStrlen(to->stringval);
4671 for (i = 0; (ch = str->stringval[i]); i++) {
4672 point = xmlStrchr(from->stringval, ch);
4673 if (point) {
4674 offset = (int)(point - from->stringval);
4675 if (offset < max)
4676 xmlBufferAdd(target, &to->stringval[offset], 1);
4677 } else
4678 xmlBufferAdd(target, &ch, 1);
4679 }
Owen Taylor3473f882001-02-23 17:55:21 +00004680 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00004681 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4682 xmlBufferFree(target);
4683 xmlXPathFreeObject(str);
4684 xmlXPathFreeObject(from);
4685 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00004686}
4687
4688/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004689 * xmlXPathConvertBoolean:
4690 * @val: an XPath object
4691 *
4692 * Converts an existing object to its boolean() equivalent
4693 *
4694 * Returns the new object, the old one is freed (or the operation
4695 * is done directly on @val)
4696 */
4697xmlXPathObjectPtr
4698xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
4699 int res = 0;
4700
4701 if (val == NULL)
4702 return(NULL);
4703 switch (val->type) {
4704 case XPATH_NODESET:
4705 case XPATH_XSLT_TREE:
4706 if ((val->nodesetval == NULL) ||
4707 (val->nodesetval->nodeNr == 0)) res = 0;
4708 else
4709 res = 1;
4710 break;
4711 case XPATH_STRING:
4712 if ((val->stringval == NULL) ||
4713 (val->stringval[0] == 0)) res = 0;
4714 else
4715 res = 1;
4716 break;
4717 case XPATH_BOOLEAN:
4718 return(val);
4719 case XPATH_NUMBER:
4720 if (val->floatval) res = 1;
4721 break;
4722 default:
4723 STRANGE
4724 }
4725 xmlXPathFreeObject(val);
4726 return(xmlXPathNewBoolean(res));
4727}
4728
4729/**
Owen Taylor3473f882001-02-23 17:55:21 +00004730 * xmlXPathBooleanFunction:
4731 * @ctxt: the XPath Parser context
4732 * @nargs: the number of arguments
4733 *
4734 * Implement the boolean() XPath function
4735 * boolean boolean(object)
4736 * he boolean function converts its argument to a boolean as follows:
4737 * - a number is true if and only if it is neither positive or
4738 * negative zero nor NaN
4739 * - a node-set is true if and only if it is non-empty
4740 * - a string is true if and only if its length is non-zero
4741 */
4742void
4743xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4744 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00004745
4746 CHECK_ARITY(1);
4747 cur = valuePop(ctxt);
4748 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004749 cur = xmlXPathConvertBoolean(cur);
4750 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004751}
4752
4753/**
4754 * xmlXPathNotFunction:
4755 * @ctxt: the XPath Parser context
4756 * @nargs: the number of arguments
4757 *
4758 * Implement the not() XPath function
4759 * boolean not(boolean)
4760 * The not function returns true if its argument is false,
4761 * and false otherwise.
4762 */
4763void
4764xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4765 CHECK_ARITY(1);
4766 CAST_TO_BOOLEAN;
4767 CHECK_TYPE(XPATH_BOOLEAN);
4768 ctxt->value->boolval = ! ctxt->value->boolval;
4769}
4770
4771/**
4772 * xmlXPathTrueFunction:
4773 * @ctxt: the XPath Parser context
4774 * @nargs: the number of arguments
4775 *
4776 * Implement the true() XPath function
4777 * boolean true()
4778 */
4779void
4780xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4781 CHECK_ARITY(0);
4782 valuePush(ctxt, xmlXPathNewBoolean(1));
4783}
4784
4785/**
4786 * xmlXPathFalseFunction:
4787 * @ctxt: the XPath Parser context
4788 * @nargs: the number of arguments
4789 *
4790 * Implement the false() XPath function
4791 * boolean false()
4792 */
4793void
4794xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4795 CHECK_ARITY(0);
4796 valuePush(ctxt, xmlXPathNewBoolean(0));
4797}
4798
4799/**
4800 * xmlXPathLangFunction:
4801 * @ctxt: the XPath Parser context
4802 * @nargs: the number of arguments
4803 *
4804 * Implement the lang() XPath function
4805 * boolean lang(string)
4806 * The lang function returns true or false depending on whether the
4807 * language of the context node as specified by xml:lang attributes
4808 * is the same as or is a sublanguage of the language specified by
4809 * the argument string. The language of the context node is determined
4810 * by the value of the xml:lang attribute on the context node, or, if
4811 * the context node has no xml:lang attribute, by the value of the
4812 * xml:lang attribute on the nearest ancestor of the context node that
4813 * has an xml:lang attribute. If there is no such attribute, then lang
4814 * returns false. If there is such an attribute, then lang returns
4815 * true if the attribute value is equal to the argument ignoring case,
4816 * or if there is some suffix starting with - such that the attribute
4817 * value is equal to the argument ignoring that suffix of the attribute
4818 * value and ignoring case.
4819 */
4820void
4821xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4822 xmlXPathObjectPtr val;
4823 const xmlChar *theLang;
4824 const xmlChar *lang;
4825 int ret = 0;
4826 int i;
4827
4828 CHECK_ARITY(1);
4829 CAST_TO_STRING;
4830 CHECK_TYPE(XPATH_STRING);
4831 val = valuePop(ctxt);
4832 lang = val->stringval;
4833 theLang = xmlNodeGetLang(ctxt->context->node);
4834 if ((theLang != NULL) && (lang != NULL)) {
4835 for (i = 0;lang[i] != 0;i++)
4836 if (toupper(lang[i]) != toupper(theLang[i]))
4837 goto not_equal;
4838 ret = 1;
4839 }
4840not_equal:
4841 xmlXPathFreeObject(val);
4842 valuePush(ctxt, xmlXPathNewBoolean(ret));
4843}
4844
4845/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004846 * xmlXPathConvertNumber:
4847 * @val: an XPath object
4848 *
4849 * Converts an existing object to its number() equivalent
4850 *
4851 * Returns the new object, the old one is freed (or the operation
4852 * is done directly on @val)
4853 */
4854xmlXPathObjectPtr
4855xmlXPathConvertNumber(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004856 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004857 double res;
4858
4859 if (val == NULL)
4860 return(xmlXPathNewFloat(0.0));
4861 switch (val->type) {
4862 case XPATH_UNDEFINED:
4863#ifdef DEBUG_EXPR
4864 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
4865#endif
4866 ret = xmlXPathNewFloat(0.0);
4867 break;
4868 case XPATH_XSLT_TREE:
4869 case XPATH_NODESET:
4870 val = xmlXPathConvertString(val);
4871 /* no break on purpose */
4872 case XPATH_STRING:
4873 res = xmlXPathStringEvalNumber(val->stringval);
4874 ret = xmlXPathNewFloat(res);
4875 break;
4876 case XPATH_BOOLEAN:
4877 if (val->boolval) ret = xmlXPathNewFloat(1.0);
4878 else ret = xmlXPathNewFloat(0.0);
4879 break;
4880 case XPATH_NUMBER:
4881 return(val);
4882 case XPATH_USERS:
4883 case XPATH_POINT:
4884 case XPATH_RANGE:
4885 case XPATH_LOCATIONSET:
4886 TODO
4887 ret = xmlXPathNewFloat(0.0);
4888 break;
4889 }
4890 xmlXPathFreeObject(val);
4891 return(ret);
4892}
4893
4894/**
Owen Taylor3473f882001-02-23 17:55:21 +00004895 * xmlXPathNumberFunction:
4896 * @ctxt: the XPath Parser context
4897 * @nargs: the number of arguments
4898 *
4899 * Implement the number() XPath function
4900 * number number(object?)
4901 */
4902void
4903xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4904 xmlXPathObjectPtr cur;
4905 double res;
4906
4907 if (nargs == 0) {
4908 if (ctxt->context->node == NULL) {
4909 valuePush(ctxt, xmlXPathNewFloat(0.0));
4910 } else {
4911 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
4912
4913 res = xmlXPathStringEvalNumber(content);
4914 valuePush(ctxt, xmlXPathNewFloat(res));
4915 xmlFree(content);
4916 }
4917 return;
4918 }
4919
4920 CHECK_ARITY(1);
4921 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004922 cur = xmlXPathConvertNumber(cur);
4923 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004924}
4925
4926/**
4927 * xmlXPathSumFunction:
4928 * @ctxt: the XPath Parser context
4929 * @nargs: the number of arguments
4930 *
4931 * Implement the sum() XPath function
4932 * number sum(node-set)
4933 * The sum function returns the sum of the values of the nodes in
4934 * the argument node-set.
4935 */
4936void
4937xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4938 xmlXPathObjectPtr cur;
4939 int i;
4940
4941 CHECK_ARITY(1);
4942 if ((ctxt->value == NULL) ||
4943 ((ctxt->value->type != XPATH_NODESET) &&
4944 (ctxt->value->type != XPATH_XSLT_TREE)))
4945 XP_ERROR(XPATH_INVALID_TYPE);
4946 cur = valuePop(ctxt);
4947
Daniel Veillardd8df6c02001-04-05 16:54:14 +00004948 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004949 valuePush(ctxt, xmlXPathNewFloat(0.0));
4950 } else {
4951 valuePush(ctxt,
4952 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
4953 xmlXPathNumberFunction(ctxt, 1);
4954 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
4955 valuePush(ctxt,
4956 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4957 xmlXPathAddValues(ctxt);
4958 }
4959 }
4960 xmlXPathFreeObject(cur);
4961}
4962
4963/**
4964 * xmlXPathFloorFunction:
4965 * @ctxt: the XPath Parser context
4966 * @nargs: the number of arguments
4967 *
4968 * Implement the floor() XPath function
4969 * number floor(number)
4970 * The floor function returns the largest (closest to positive infinity)
4971 * number that is not greater than the argument and that is an integer.
4972 */
4973void
4974xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4975 CHECK_ARITY(1);
4976 CAST_TO_NUMBER;
4977 CHECK_TYPE(XPATH_NUMBER);
4978#if 0
4979 ctxt->value->floatval = floor(ctxt->value->floatval);
4980#else
4981 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
4982 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
4983#endif
4984}
4985
4986/**
4987 * xmlXPathCeilingFunction:
4988 * @ctxt: the XPath Parser context
4989 * @nargs: the number of arguments
4990 *
4991 * Implement the ceiling() XPath function
4992 * number ceiling(number)
4993 * The ceiling function returns the smallest (closest to negative infinity)
4994 * number that is not less than the argument and that is an integer.
4995 */
4996void
4997xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4998 double f;
4999
5000 CHECK_ARITY(1);
5001 CAST_TO_NUMBER;
5002 CHECK_TYPE(XPATH_NUMBER);
5003
5004#if 0
5005 ctxt->value->floatval = ceil(ctxt->value->floatval);
5006#else
5007 f = (double)((int) ctxt->value->floatval);
5008 if (f != ctxt->value->floatval)
5009 ctxt->value->floatval = f + 1;
5010#endif
5011}
5012
5013/**
5014 * xmlXPathRoundFunction:
5015 * @ctxt: the XPath Parser context
5016 * @nargs: the number of arguments
5017 *
5018 * Implement the round() XPath function
5019 * number round(number)
5020 * The round function returns the number that is closest to the
5021 * argument and that is an integer. If there are two such numbers,
5022 * then the one that is even is returned.
5023 */
5024void
5025xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5026 double f;
5027
5028 CHECK_ARITY(1);
5029 CAST_TO_NUMBER;
5030 CHECK_TYPE(XPATH_NUMBER);
5031
5032 if ((ctxt->value->floatval == xmlXPathNAN) ||
5033 (ctxt->value->floatval == xmlXPathPINF) ||
5034 (ctxt->value->floatval == xmlXPathNINF) ||
5035 (ctxt->value->floatval == 0.0))
5036 return;
5037
5038#if 0
5039 f = floor(ctxt->value->floatval);
5040#else
5041 f = (double)((int) ctxt->value->floatval);
5042#endif
5043 if (ctxt->value->floatval < f + 0.5)
5044 ctxt->value->floatval = f;
5045 else
5046 ctxt->value->floatval = f + 1;
5047}
5048
5049/************************************************************************
5050 * *
5051 * The Parser *
5052 * *
5053 ************************************************************************/
5054
5055/*
5056 * a couple of forward declarations since we use a recursive call based
5057 * implementation.
5058 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005059static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005060static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005061static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005062#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005063static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
5064#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00005065#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005066static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005067#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00005068static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
5069 int qualified);
Owen Taylor3473f882001-02-23 17:55:21 +00005070
5071/**
Daniel Veillard61d80a22001-04-27 17:13:01 +00005072 * xmlXPathCurrentChar:
5073 * @ctxt: the XPath parser context
5074 * @cur: pointer to the beginning of the char
5075 * @len: pointer to the length of the char read
5076 *
5077 * The current char value, if using UTF-8 this may actaully span multiple
5078 * bytes in the input buffer.
5079 *
5080 * Returns the current char value and its lenght
5081 */
5082
5083static int
5084xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
5085 unsigned char c;
5086 unsigned int val;
5087 const xmlChar *cur;
5088
5089 if (ctxt == NULL)
5090 return(0);
5091 cur = ctxt->cur;
5092
5093 /*
5094 * We are supposed to handle UTF8, check it's valid
5095 * From rfc2044: encoding of the Unicode values on UTF-8:
5096 *
5097 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
5098 * 0000 0000-0000 007F 0xxxxxxx
5099 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
5100 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
5101 *
5102 * Check for the 0x110000 limit too
5103 */
5104 c = *cur;
5105 if (c & 0x80) {
5106 if ((cur[1] & 0xc0) != 0x80)
5107 goto encoding_error;
5108 if ((c & 0xe0) == 0xe0) {
5109
5110 if ((cur[2] & 0xc0) != 0x80)
5111 goto encoding_error;
5112 if ((c & 0xf0) == 0xf0) {
5113 if (((c & 0xf8) != 0xf0) ||
5114 ((cur[3] & 0xc0) != 0x80))
5115 goto encoding_error;
5116 /* 4-byte code */
5117 *len = 4;
5118 val = (cur[0] & 0x7) << 18;
5119 val |= (cur[1] & 0x3f) << 12;
5120 val |= (cur[2] & 0x3f) << 6;
5121 val |= cur[3] & 0x3f;
5122 } else {
5123 /* 3-byte code */
5124 *len = 3;
5125 val = (cur[0] & 0xf) << 12;
5126 val |= (cur[1] & 0x3f) << 6;
5127 val |= cur[2] & 0x3f;
5128 }
5129 } else {
5130 /* 2-byte code */
5131 *len = 2;
5132 val = (cur[0] & 0x1f) << 6;
5133 val |= cur[1] & 0x3f;
5134 }
5135 if (!IS_CHAR(val)) {
5136 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
5137 }
5138 return(val);
5139 } else {
5140 /* 1-byte code */
5141 *len = 1;
5142 return((int) *cur);
5143 }
5144encoding_error:
5145 /*
5146 * If we detect an UTF8 error that probably mean that the
5147 * input encoding didn't get properly advertized in the
5148 * declaration header. Report the error and switch the encoding
5149 * to ISO-Latin-1 (if you don't like this policy, just declare the
5150 * encoding !)
5151 */
5152 XP_ERROR0(XPATH_ENCODING_ERROR);
5153 *len = 1;
5154 return((int) *cur);
5155}
5156
5157/**
Owen Taylor3473f882001-02-23 17:55:21 +00005158 * xmlXPathParseNCName:
5159 * @ctxt: the XPath Parser context
5160 *
5161 * parse an XML namespace non qualified name.
5162 *
5163 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
5164 *
5165 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
5166 * CombiningChar | Extender
5167 *
5168 * Returns the namespace name or NULL
5169 */
5170
5171xmlChar *
5172xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard2156a562001-04-28 12:24:34 +00005173 const xmlChar *in;
5174 xmlChar *ret;
5175 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005176
Daniel Veillard2156a562001-04-28 12:24:34 +00005177 /*
5178 * Accelerator for simple ASCII names
5179 */
5180 in = ctxt->cur;
5181 if (((*in >= 0x61) && (*in <= 0x7A)) ||
5182 ((*in >= 0x41) && (*in <= 0x5A)) ||
5183 (*in == '_')) {
5184 in++;
5185 while (((*in >= 0x61) && (*in <= 0x7A)) ||
5186 ((*in >= 0x41) && (*in <= 0x5A)) ||
5187 ((*in >= 0x30) && (*in <= 0x39)) ||
5188 (*in == '_'))
5189 in++;
5190 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
5191 (*in == '[') || (*in == ']') || (*in == ':') ||
5192 (*in == '@') || (*in == '*')) {
5193 count = in - ctxt->cur;
5194 if (count == 0)
5195 return(NULL);
5196 ret = xmlStrndup(ctxt->cur, count);
5197 ctxt->cur = in;
5198 return(ret);
5199 }
5200 }
5201 return(xmlXPathParseNameComplex(ctxt, 0));
Owen Taylor3473f882001-02-23 17:55:21 +00005202}
5203
Daniel Veillard2156a562001-04-28 12:24:34 +00005204
Owen Taylor3473f882001-02-23 17:55:21 +00005205/**
5206 * xmlXPathParseQName:
5207 * @ctxt: the XPath Parser context
5208 * @prefix: a xmlChar **
5209 *
5210 * parse an XML qualified name
5211 *
5212 * [NS 5] QName ::= (Prefix ':')? LocalPart
5213 *
5214 * [NS 6] Prefix ::= NCName
5215 *
5216 * [NS 7] LocalPart ::= NCName
5217 *
5218 * Returns the function returns the local part, and prefix is updated
5219 * to get the Prefix if any.
5220 */
5221
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005222static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005223xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
5224 xmlChar *ret = NULL;
5225
5226 *prefix = NULL;
5227 ret = xmlXPathParseNCName(ctxt);
5228 if (CUR == ':') {
5229 *prefix = ret;
5230 NEXT;
5231 ret = xmlXPathParseNCName(ctxt);
5232 }
5233 return(ret);
5234}
5235
5236/**
5237 * xmlXPathParseName:
5238 * @ctxt: the XPath Parser context
5239 *
5240 * parse an XML name
5241 *
5242 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5243 * CombiningChar | Extender
5244 *
5245 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5246 *
5247 * Returns the namespace name or NULL
5248 */
5249
5250xmlChar *
5251xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00005252 const xmlChar *in;
5253 xmlChar *ret;
5254 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005255
Daniel Veillard61d80a22001-04-27 17:13:01 +00005256 /*
5257 * Accelerator for simple ASCII names
5258 */
5259 in = ctxt->cur;
5260 if (((*in >= 0x61) && (*in <= 0x7A)) ||
5261 ((*in >= 0x41) && (*in <= 0x5A)) ||
5262 (*in == '_') || (*in == ':')) {
5263 in++;
5264 while (((*in >= 0x61) && (*in <= 0x7A)) ||
5265 ((*in >= 0x41) && (*in <= 0x5A)) ||
5266 ((*in >= 0x30) && (*in <= 0x39)) ||
5267 (*in == '_') || (*in == ':'))
5268 in++;
5269 if ((*in == ' ') || (*in == '>') || (*in == '/')) {
5270 count = in - ctxt->cur;
5271 ret = xmlStrndup(ctxt->cur, count);
5272 ctxt->cur = in;
5273 return(ret);
5274 }
5275 }
Daniel Veillard2156a562001-04-28 12:24:34 +00005276 return(xmlXPathParseNameComplex(ctxt, 1));
Owen Taylor3473f882001-02-23 17:55:21 +00005277}
5278
Daniel Veillard61d80a22001-04-27 17:13:01 +00005279static xmlChar *
Daniel Veillard2156a562001-04-28 12:24:34 +00005280xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00005281 xmlChar buf[XML_MAX_NAMELEN + 5];
5282 int len = 0, l;
5283 int c;
5284
5285 /*
5286 * Handler for more complex cases
5287 */
5288 c = CUR_CHAR(l);
5289 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
Daniel Veillard2156a562001-04-28 12:24:34 +00005290 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
5291 (c == '*') || /* accelerators */
Daniel Veillard61d80a22001-04-27 17:13:01 +00005292 (!IS_LETTER(c) && (c != '_') &&
Daniel Veillard2156a562001-04-28 12:24:34 +00005293 ((qualified) && (c != ':')))) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00005294 return(NULL);
5295 }
5296
5297 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
5298 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
5299 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00005300 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00005301 (IS_COMBINING(c)) ||
5302 (IS_EXTENDER(c)))) {
5303 COPY_BUF(l,buf,len,c);
5304 NEXTL(l);
5305 c = CUR_CHAR(l);
5306 if (len >= XML_MAX_NAMELEN) {
5307 /*
5308 * Okay someone managed to make a huge name, so he's ready to pay
5309 * for the processing speed.
5310 */
5311 xmlChar *buffer;
5312 int max = len * 2;
5313
5314 buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
5315 if (buffer == NULL) {
5316 XP_ERROR0(XPATH_MEMORY_ERROR);
5317 }
5318 memcpy(buffer, buf, len);
5319 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
5320 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00005321 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00005322 (IS_COMBINING(c)) ||
5323 (IS_EXTENDER(c))) {
5324 if (len + 10 > max) {
5325 max *= 2;
5326 buffer = (xmlChar *) xmlRealloc(buffer,
5327 max * sizeof(xmlChar));
5328 XP_ERROR0(XPATH_MEMORY_ERROR);
5329 if (buffer == NULL) {
5330 XP_ERROR0(XPATH_MEMORY_ERROR);
5331 }
5332 }
5333 COPY_BUF(l,buffer,len,c);
5334 NEXTL(l);
5335 c = CUR_CHAR(l);
5336 }
5337 buffer[len] = 0;
5338 return(buffer);
5339 }
5340 }
Daniel Veillard2156a562001-04-28 12:24:34 +00005341 if (len == 0)
5342 return(NULL);
Daniel Veillard61d80a22001-04-27 17:13:01 +00005343 return(xmlStrndup(buf, len));
5344}
Owen Taylor3473f882001-02-23 17:55:21 +00005345/**
5346 * xmlXPathStringEvalNumber:
5347 * @str: A string to scan
5348 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00005349 * [30a] Float ::= Number ('e' Digits?)?
5350 *
Owen Taylor3473f882001-02-23 17:55:21 +00005351 * [30] Number ::= Digits ('.' Digits?)?
5352 * | '.' Digits
5353 * [31] Digits ::= [0-9]+
5354 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005355 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00005356 * In complement of the Number expression, this function also handles
5357 * negative values : '-' Number.
5358 *
5359 * Returns the double value.
5360 */
5361double
5362xmlXPathStringEvalNumber(const xmlChar *str) {
5363 const xmlChar *cur = str;
5364 double ret = 0.0;
5365 double mult = 1;
5366 int ok = 0;
5367 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005368 int exponent = 0;
5369 int is_exponent_negative = 0;
5370
Owen Taylor3473f882001-02-23 17:55:21 +00005371 while (IS_BLANK(*cur)) cur++;
5372 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5373 return(xmlXPathNAN);
5374 }
5375 if (*cur == '-') {
5376 isneg = 1;
5377 cur++;
5378 }
5379 while ((*cur >= '0') && (*cur <= '9')) {
5380 ret = ret * 10 + (*cur - '0');
5381 ok = 1;
5382 cur++;
5383 }
5384 if (*cur == '.') {
5385 cur++;
5386 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5387 return(xmlXPathNAN);
5388 }
5389 while ((*cur >= '0') && (*cur <= '9')) {
5390 mult /= 10;
5391 ret = ret + (*cur - '0') * mult;
5392 cur++;
5393 }
5394 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00005395 if ((*cur == 'e') || (*cur == 'E')) {
5396 cur++;
5397 if (*cur == '-') {
5398 is_exponent_negative = 1;
5399 cur++;
5400 }
5401 while ((*cur >= '0') && (*cur <= '9')) {
5402 exponent = exponent * 10 + (*cur - '0');
5403 cur++;
5404 }
5405 }
Owen Taylor3473f882001-02-23 17:55:21 +00005406 while (IS_BLANK(*cur)) cur++;
5407 if (*cur != 0) return(xmlXPathNAN);
5408 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005409 if (is_exponent_negative) exponent = -exponent;
5410 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00005411 return(ret);
5412}
5413
5414/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005415 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00005416 * @ctxt: the XPath Parser context
5417 *
5418 * [30] Number ::= Digits ('.' Digits?)?
5419 * | '.' Digits
5420 * [31] Digits ::= [0-9]+
5421 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005422 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00005423 *
5424 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005425static void
5426xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005427 double ret = 0.0;
5428 double mult = 1;
5429 int ok = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005430 int exponent = 0;
5431 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005432
5433 CHECK_ERROR;
5434 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5435 XP_ERROR(XPATH_NUMBER_ERROR);
5436 }
5437 while ((CUR >= '0') && (CUR <= '9')) {
5438 ret = ret * 10 + (CUR - '0');
5439 ok = 1;
5440 NEXT;
5441 }
5442 if (CUR == '.') {
5443 NEXT;
5444 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5445 XP_ERROR(XPATH_NUMBER_ERROR);
5446 }
5447 while ((CUR >= '0') && (CUR <= '9')) {
5448 mult /= 10;
5449 ret = ret + (CUR - '0') * mult;
5450 NEXT;
5451 }
5452 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00005453 if ((CUR == 'e') || (CUR == 'E')) {
5454 NEXT;
5455 if (CUR == '-') {
5456 is_exponent_negative = 1;
5457 NEXT;
5458 }
5459 while ((CUR >= '0') && (CUR <= '9')) {
5460 exponent = exponent * 10 + (CUR - '0');
5461 NEXT;
5462 }
5463 }
5464 if (is_exponent_negative)
5465 exponent = -exponent;
5466 ret *= pow(10.0, (double)exponent);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005467 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
5468 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005469}
5470
5471/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005472 * xmlXPathParseLiteral:
5473 * @ctxt: the XPath Parser context
5474 *
5475 * Parse a Literal
5476 *
5477 * [29] Literal ::= '"' [^"]* '"'
5478 * | "'" [^']* "'"
5479 *
5480 * Returns the value found or NULL in case of error
5481 */
5482static xmlChar *
5483xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
5484 const xmlChar *q;
5485 xmlChar *ret = NULL;
5486
5487 if (CUR == '"') {
5488 NEXT;
5489 q = CUR_PTR;
5490 while ((IS_CHAR(CUR)) && (CUR != '"'))
5491 NEXT;
5492 if (!IS_CHAR(CUR)) {
5493 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5494 } else {
5495 ret = xmlStrndup(q, CUR_PTR - q);
5496 NEXT;
5497 }
5498 } else if (CUR == '\'') {
5499 NEXT;
5500 q = CUR_PTR;
5501 while ((IS_CHAR(CUR)) && (CUR != '\''))
5502 NEXT;
5503 if (!IS_CHAR(CUR)) {
5504 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5505 } else {
5506 ret = xmlStrndup(q, CUR_PTR - q);
5507 NEXT;
5508 }
5509 } else {
5510 XP_ERROR0(XPATH_START_LITERAL_ERROR);
5511 }
5512 return(ret);
5513}
5514
5515/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005516 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00005517 * @ctxt: the XPath Parser context
5518 *
5519 * Parse a Literal and push it on the stack.
5520 *
5521 * [29] Literal ::= '"' [^"]* '"'
5522 * | "'" [^']* "'"
5523 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005524 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00005525 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005526static void
5527xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005528 const xmlChar *q;
5529 xmlChar *ret = NULL;
5530
5531 if (CUR == '"') {
5532 NEXT;
5533 q = CUR_PTR;
5534 while ((IS_CHAR(CUR)) && (CUR != '"'))
5535 NEXT;
5536 if (!IS_CHAR(CUR)) {
5537 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5538 } else {
5539 ret = xmlStrndup(q, CUR_PTR - q);
5540 NEXT;
5541 }
5542 } else if (CUR == '\'') {
5543 NEXT;
5544 q = CUR_PTR;
5545 while ((IS_CHAR(CUR)) && (CUR != '\''))
5546 NEXT;
5547 if (!IS_CHAR(CUR)) {
5548 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5549 } else {
5550 ret = xmlStrndup(q, CUR_PTR - q);
5551 NEXT;
5552 }
5553 } else {
5554 XP_ERROR(XPATH_START_LITERAL_ERROR);
5555 }
5556 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005557 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
5558 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005559 xmlFree(ret);
5560}
5561
5562/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005563 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00005564 * @ctxt: the XPath Parser context
5565 *
5566 * Parse a VariableReference, evaluate it and push it on the stack.
5567 *
5568 * The variable bindings consist of a mapping from variable names
5569 * to variable values. The value of a variable is an object, which
5570 * of any of the types that are possible for the value of an expression,
5571 * and may also be of additional types not specified here.
5572 *
5573 * Early evaluation is possible since:
5574 * The variable bindings [...] used to evaluate a subexpression are
5575 * always the same as those used to evaluate the containing expression.
5576 *
5577 * [36] VariableReference ::= '$' QName
5578 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005579static void
5580xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005581 xmlChar *name;
5582 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005583
5584 SKIP_BLANKS;
5585 if (CUR != '$') {
5586 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5587 }
5588 NEXT;
5589 name = xmlXPathParseQName(ctxt, &prefix);
5590 if (name == NULL) {
5591 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5592 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005593 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005594 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
5595 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005596 SKIP_BLANKS;
5597}
5598
5599/**
5600 * xmlXPathIsNodeType:
5601 * @ctxt: the XPath Parser context
5602 * @name: a name string
5603 *
5604 * Is the name given a NodeType one.
5605 *
5606 * [38] NodeType ::= 'comment'
5607 * | 'text'
5608 * | 'processing-instruction'
5609 * | 'node'
5610 *
5611 * Returns 1 if true 0 otherwise
5612 */
5613int
5614xmlXPathIsNodeType(const xmlChar *name) {
5615 if (name == NULL)
5616 return(0);
5617
5618 if (xmlStrEqual(name, BAD_CAST "comment"))
5619 return(1);
5620 if (xmlStrEqual(name, BAD_CAST "text"))
5621 return(1);
5622 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5623 return(1);
5624 if (xmlStrEqual(name, BAD_CAST "node"))
5625 return(1);
5626 return(0);
5627}
5628
5629/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005630 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00005631 * @ctxt: the XPath Parser context
5632 *
5633 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5634 * [17] Argument ::= Expr
5635 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005636 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00005637 * pushed on the stack
5638 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005639static void
5640xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005641 xmlChar *name;
5642 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005643 int nbargs = 0;
5644
5645 name = xmlXPathParseQName(ctxt, &prefix);
5646 if (name == NULL) {
5647 XP_ERROR(XPATH_EXPR_ERROR);
5648 }
5649 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005650#ifdef DEBUG_EXPR
5651 if (prefix == NULL)
5652 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5653 name);
5654 else
5655 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5656 prefix, name);
5657#endif
5658
Owen Taylor3473f882001-02-23 17:55:21 +00005659 if (CUR != '(') {
5660 XP_ERROR(XPATH_EXPR_ERROR);
5661 }
5662 NEXT;
5663 SKIP_BLANKS;
5664
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005665 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005666 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005667 int op1 = ctxt->comp->last;
5668 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005669 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005670 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005671 nbargs++;
5672 if (CUR == ')') break;
5673 if (CUR != ',') {
5674 XP_ERROR(XPATH_EXPR_ERROR);
5675 }
5676 NEXT;
5677 SKIP_BLANKS;
5678 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005679 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
5680 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005681 NEXT;
5682 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005683}
5684
5685/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005686 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005687 * @ctxt: the XPath Parser context
5688 *
5689 * [15] PrimaryExpr ::= VariableReference
5690 * | '(' Expr ')'
5691 * | Literal
5692 * | Number
5693 * | FunctionCall
5694 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005695 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005696 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005697static void
5698xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005699 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005700 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005701 else if (CUR == '(') {
5702 NEXT;
5703 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005704 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005705 if (CUR != ')') {
5706 XP_ERROR(XPATH_EXPR_ERROR);
5707 }
5708 NEXT;
5709 SKIP_BLANKS;
5710 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005711 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005712 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005713 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005714 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005715 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005716 }
5717 SKIP_BLANKS;
5718}
5719
5720/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005721 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005722 * @ctxt: the XPath Parser context
5723 *
5724 * [20] FilterExpr ::= PrimaryExpr
5725 * | FilterExpr Predicate
5726 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005727 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005728 * Square brackets are used to filter expressions in the same way that
5729 * they are used in location paths. It is an error if the expression to
5730 * be filtered does not evaluate to a node-set. The context node list
5731 * used for evaluating the expression in square brackets is the node-set
5732 * to be filtered listed in document order.
5733 */
5734
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005735static void
5736xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
5737 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005738 CHECK_ERROR;
5739 SKIP_BLANKS;
5740
5741 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005742 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00005743 SKIP_BLANKS;
5744 }
5745
5746
5747}
5748
5749/**
5750 * xmlXPathScanName:
5751 * @ctxt: the XPath Parser context
5752 *
5753 * Trickery: parse an XML name but without consuming the input flow
5754 * Needed to avoid insanity in the parser state.
5755 *
5756 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5757 * CombiningChar | Extender
5758 *
5759 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5760 *
5761 * [6] Names ::= Name (S Name)*
5762 *
5763 * Returns the Name parsed or NULL
5764 */
5765
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005766static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005767xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5768 xmlChar buf[XML_MAX_NAMELEN];
5769 int len = 0;
5770
5771 SKIP_BLANKS;
5772 if (!IS_LETTER(CUR) && (CUR != '_') &&
5773 (CUR != ':')) {
5774 return(NULL);
5775 }
5776
5777 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5778 (NXT(len) == '.') || (NXT(len) == '-') ||
5779 (NXT(len) == '_') || (NXT(len) == ':') ||
5780 (IS_COMBINING(NXT(len))) ||
5781 (IS_EXTENDER(NXT(len)))) {
5782 buf[len] = NXT(len);
5783 len++;
5784 if (len >= XML_MAX_NAMELEN) {
5785 xmlGenericError(xmlGenericErrorContext,
5786 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5787 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5788 (NXT(len) == '.') || (NXT(len) == '-') ||
5789 (NXT(len) == '_') || (NXT(len) == ':') ||
5790 (IS_COMBINING(NXT(len))) ||
5791 (IS_EXTENDER(NXT(len))))
5792 len++;
5793 break;
5794 }
5795 }
5796 return(xmlStrndup(buf, len));
5797}
5798
5799/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005800 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005801 * @ctxt: the XPath Parser context
5802 *
5803 * [19] PathExpr ::= LocationPath
5804 * | FilterExpr
5805 * | FilterExpr '/' RelativeLocationPath
5806 * | FilterExpr '//' RelativeLocationPath
5807 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005808 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005809 * The / operator and // operators combine an arbitrary expression
5810 * and a relative location path. It is an error if the expression
5811 * does not evaluate to a node-set.
5812 * The / operator does composition in the same way as when / is
5813 * used in a location path. As in location paths, // is short for
5814 * /descendant-or-self::node()/.
5815 */
5816
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005817static void
5818xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005819 int lc = 1; /* Should we branch to LocationPath ? */
5820 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5821
5822 SKIP_BLANKS;
5823 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5824 (CUR == '\'') || (CUR == '"')) {
5825 lc = 0;
5826 } else if (CUR == '*') {
5827 /* relative or absolute location path */
5828 lc = 1;
5829 } else if (CUR == '/') {
5830 /* relative or absolute location path */
5831 lc = 1;
5832 } else if (CUR == '@') {
5833 /* relative abbreviated attribute location path */
5834 lc = 1;
5835 } else if (CUR == '.') {
5836 /* relative abbreviated attribute location path */
5837 lc = 1;
5838 } else {
5839 /*
5840 * Problem is finding if we have a name here whether it's:
5841 * - a nodetype
5842 * - a function call in which case it's followed by '('
5843 * - an axis in which case it's followed by ':'
5844 * - a element name
5845 * We do an a priori analysis here rather than having to
5846 * maintain parsed token content through the recursive function
5847 * calls. This looks uglier but makes the code quite easier to
5848 * read/write/debug.
5849 */
5850 SKIP_BLANKS;
5851 name = xmlXPathScanName(ctxt);
5852 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5853#ifdef DEBUG_STEP
5854 xmlGenericError(xmlGenericErrorContext,
5855 "PathExpr: Axis\n");
5856#endif
5857 lc = 1;
5858 xmlFree(name);
5859 } else if (name != NULL) {
5860 int len =xmlStrlen(name);
5861 int blank = 0;
5862
5863
5864 while (NXT(len) != 0) {
5865 if (NXT(len) == '/') {
5866 /* element name */
5867#ifdef DEBUG_STEP
5868 xmlGenericError(xmlGenericErrorContext,
5869 "PathExpr: AbbrRelLocation\n");
5870#endif
5871 lc = 1;
5872 break;
5873 } else if (IS_BLANK(NXT(len))) {
5874 /* skip to next */
5875 blank = 1;
5876 } else if (NXT(len) == ':') {
5877#ifdef DEBUG_STEP
5878 xmlGenericError(xmlGenericErrorContext,
5879 "PathExpr: AbbrRelLocation\n");
5880#endif
5881 lc = 1;
5882 break;
5883 } else if ((NXT(len) == '(')) {
5884 /* Note Type or Function */
5885 if (xmlXPathIsNodeType(name)) {
5886#ifdef DEBUG_STEP
5887 xmlGenericError(xmlGenericErrorContext,
5888 "PathExpr: Type search\n");
5889#endif
5890 lc = 1;
5891 } else {
5892#ifdef DEBUG_STEP
5893 xmlGenericError(xmlGenericErrorContext,
5894 "PathExpr: function call\n");
5895#endif
5896 lc = 0;
5897 }
5898 break;
5899 } else if ((NXT(len) == '[')) {
5900 /* element name */
5901#ifdef DEBUG_STEP
5902 xmlGenericError(xmlGenericErrorContext,
5903 "PathExpr: AbbrRelLocation\n");
5904#endif
5905 lc = 1;
5906 break;
5907 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
5908 (NXT(len) == '=')) {
5909 lc = 1;
5910 break;
5911 } else {
5912 lc = 1;
5913 break;
5914 }
5915 len++;
5916 }
5917 if (NXT(len) == 0) {
5918#ifdef DEBUG_STEP
5919 xmlGenericError(xmlGenericErrorContext,
5920 "PathExpr: AbbrRelLocation\n");
5921#endif
5922 /* element name */
5923 lc = 1;
5924 }
5925 xmlFree(name);
5926 } else {
5927 /* make sure all cases are covered explicitely */
5928 XP_ERROR(XPATH_EXPR_ERROR);
5929 }
5930 }
5931
5932 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005933 if (CUR == '/') {
5934 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
5935 } else {
5936 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005937 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005938 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005939 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005940 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005941 CHECK_ERROR;
5942 if ((CUR == '/') && (NXT(1) == '/')) {
5943 SKIP(2);
5944 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005945
5946 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
5947 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
5948 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
5949
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005950 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005951 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005952 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005953 }
5954 }
5955 SKIP_BLANKS;
5956}
5957
5958/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005959 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005960 * @ctxt: the XPath Parser context
5961 *
5962 * [18] UnionExpr ::= PathExpr
5963 * | UnionExpr '|' PathExpr
5964 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005965 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005966 */
5967
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005968static void
5969xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
5970 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005971 CHECK_ERROR;
5972 SKIP_BLANKS;
5973 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005974 int op1 = ctxt->comp->last;
5975 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005976
5977 NEXT;
5978 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005979 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005980
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005981 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
5982
Owen Taylor3473f882001-02-23 17:55:21 +00005983 SKIP_BLANKS;
5984 }
Owen Taylor3473f882001-02-23 17:55:21 +00005985}
5986
5987/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005988 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005989 * @ctxt: the XPath Parser context
5990 *
5991 * [27] UnaryExpr ::= UnionExpr
5992 * | '-' UnaryExpr
5993 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005994 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005995 */
5996
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005997static void
5998xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005999 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006000 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006001
6002 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00006003 while (CUR == '-') {
6004 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006005 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006006 NEXT;
6007 SKIP_BLANKS;
6008 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006009
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006010 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006011 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006012 if (found) {
6013 if (minus)
6014 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
6015 else
6016 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006017 }
6018}
6019
6020/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006021 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006022 * @ctxt: the XPath Parser context
6023 *
6024 * [26] MultiplicativeExpr ::= UnaryExpr
6025 * | MultiplicativeExpr MultiplyOperator UnaryExpr
6026 * | MultiplicativeExpr 'div' UnaryExpr
6027 * | MultiplicativeExpr 'mod' UnaryExpr
6028 * [34] MultiplyOperator ::= '*'
6029 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006030 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006031 */
6032
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006033static void
6034xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
6035 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006036 CHECK_ERROR;
6037 SKIP_BLANKS;
6038 while ((CUR == '*') ||
6039 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
6040 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
6041 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006042 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006043
6044 if (CUR == '*') {
6045 op = 0;
6046 NEXT;
6047 } else if (CUR == 'd') {
6048 op = 1;
6049 SKIP(3);
6050 } else if (CUR == 'm') {
6051 op = 2;
6052 SKIP(3);
6053 }
6054 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006055 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006056 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006057 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006058 SKIP_BLANKS;
6059 }
6060}
6061
6062/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006063 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006064 * @ctxt: the XPath Parser context
6065 *
6066 * [25] AdditiveExpr ::= MultiplicativeExpr
6067 * | AdditiveExpr '+' MultiplicativeExpr
6068 * | AdditiveExpr '-' MultiplicativeExpr
6069 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006070 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006071 */
6072
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006073static void
6074xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006075
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006076 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006077 CHECK_ERROR;
6078 SKIP_BLANKS;
6079 while ((CUR == '+') || (CUR == '-')) {
6080 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006081 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006082
6083 if (CUR == '+') plus = 1;
6084 else plus = 0;
6085 NEXT;
6086 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006087 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006088 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006089 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006090 SKIP_BLANKS;
6091 }
6092}
6093
6094/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006095 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006096 * @ctxt: the XPath Parser context
6097 *
6098 * [24] RelationalExpr ::= AdditiveExpr
6099 * | RelationalExpr '<' AdditiveExpr
6100 * | RelationalExpr '>' AdditiveExpr
6101 * | RelationalExpr '<=' AdditiveExpr
6102 * | RelationalExpr '>=' AdditiveExpr
6103 *
6104 * A <= B > C is allowed ? Answer from James, yes with
6105 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
6106 * which is basically what got implemented.
6107 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006108 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00006109 * on the stack
6110 */
6111
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006112static void
6113xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
6114 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006115 CHECK_ERROR;
6116 SKIP_BLANKS;
6117 while ((CUR == '<') ||
6118 (CUR == '>') ||
6119 ((CUR == '<') && (NXT(1) == '=')) ||
6120 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006121 int inf, strict;
6122 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006123
6124 if (CUR == '<') inf = 1;
6125 else inf = 0;
6126 if (NXT(1) == '=') strict = 0;
6127 else strict = 1;
6128 NEXT;
6129 if (!strict) NEXT;
6130 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006131 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006132 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006133 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00006134 SKIP_BLANKS;
6135 }
6136}
6137
6138/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006139 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006140 * @ctxt: the XPath Parser context
6141 *
6142 * [23] EqualityExpr ::= RelationalExpr
6143 * | EqualityExpr '=' RelationalExpr
6144 * | EqualityExpr '!=' RelationalExpr
6145 *
6146 * A != B != C is allowed ? Answer from James, yes with
6147 * (RelationalExpr = RelationalExpr) = RelationalExpr
6148 * (RelationalExpr != RelationalExpr) != RelationalExpr
6149 * which is basically what got implemented.
6150 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006151 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006152 *
6153 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006154static void
6155xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
6156 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006157 CHECK_ERROR;
6158 SKIP_BLANKS;
6159 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006160 int eq;
6161 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006162
6163 if (CUR == '=') eq = 1;
6164 else eq = 0;
6165 NEXT;
6166 if (!eq) NEXT;
6167 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006168 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006169 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006170 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006171 SKIP_BLANKS;
6172 }
6173}
6174
6175/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006176 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006177 * @ctxt: the XPath Parser context
6178 *
6179 * [22] AndExpr ::= EqualityExpr
6180 * | AndExpr 'and' EqualityExpr
6181 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006182 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006183 *
6184 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006185static void
6186xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
6187 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006188 CHECK_ERROR;
6189 SKIP_BLANKS;
6190 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006191 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006192 SKIP(3);
6193 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006194 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006195 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006196 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006197 SKIP_BLANKS;
6198 }
6199}
6200
6201/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006202 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006203 * @ctxt: the XPath Parser context
6204 *
6205 * [14] Expr ::= OrExpr
6206 * [21] OrExpr ::= AndExpr
6207 * | OrExpr 'or' AndExpr
6208 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006209 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00006210 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006211static void
6212xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
6213 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006214 CHECK_ERROR;
6215 SKIP_BLANKS;
6216 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006217 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006218 SKIP(2);
6219 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006220 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006221 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006222 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
6223 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00006224 SKIP_BLANKS;
6225 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006226 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
6227 /* more ops could be optimized too */
6228 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
6229 }
Owen Taylor3473f882001-02-23 17:55:21 +00006230}
6231
6232/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006233 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00006234 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006235 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00006236 *
6237 * [8] Predicate ::= '[' PredicateExpr ']'
6238 * [9] PredicateExpr ::= Expr
6239 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006240 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00006241 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006242static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006243xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006244 int op1 = ctxt->comp->last;
6245
6246 SKIP_BLANKS;
6247 if (CUR != '[') {
6248 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6249 }
6250 NEXT;
6251 SKIP_BLANKS;
6252
6253 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006254 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006255 CHECK_ERROR;
6256
6257 if (CUR != ']') {
6258 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6259 }
6260
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006261 if (filter)
6262 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
6263 else
6264 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006265
6266 NEXT;
6267 SKIP_BLANKS;
6268}
6269
6270/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006271 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00006272 * @ctxt: the XPath Parser context
6273 * @test: pointer to a xmlXPathTestVal
6274 * @type: pointer to a xmlXPathTypeVal
6275 * @prefix: placeholder for a possible name prefix
6276 *
6277 * [7] NodeTest ::= NameTest
6278 * | NodeType '(' ')'
6279 * | 'processing-instruction' '(' Literal ')'
6280 *
6281 * [37] NameTest ::= '*'
6282 * | NCName ':' '*'
6283 * | QName
6284 * [38] NodeType ::= 'comment'
6285 * | 'text'
6286 * | 'processing-instruction'
6287 * | 'node'
6288 *
6289 * Returns the name found and update @test, @type and @prefix appropriately
6290 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006291static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006292xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6293 xmlXPathTypeVal *type, const xmlChar **prefix,
6294 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00006295 int blanks;
6296
6297 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6298 STRANGE;
6299 return(NULL);
6300 }
6301 *type = 0;
6302 *test = 0;
6303 *prefix = NULL;
6304 SKIP_BLANKS;
6305
6306 if ((name == NULL) && (CUR == '*')) {
6307 /*
6308 * All elements
6309 */
6310 NEXT;
6311 *test = NODE_TEST_ALL;
6312 return(NULL);
6313 }
6314
6315 if (name == NULL)
6316 name = xmlXPathParseNCName(ctxt);
6317 if (name == NULL) {
6318 XP_ERROR0(XPATH_EXPR_ERROR);
6319 }
6320
6321 blanks = IS_BLANK(CUR);
6322 SKIP_BLANKS;
6323 if (CUR == '(') {
6324 NEXT;
6325 /*
6326 * NodeType or PI search
6327 */
6328 if (xmlStrEqual(name, BAD_CAST "comment"))
6329 *type = NODE_TYPE_COMMENT;
6330 else if (xmlStrEqual(name, BAD_CAST "node"))
6331 *type = NODE_TYPE_NODE;
6332 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6333 *type = NODE_TYPE_PI;
6334 else if (xmlStrEqual(name, BAD_CAST "text"))
6335 *type = NODE_TYPE_TEXT;
6336 else {
6337 if (name != NULL)
6338 xmlFree(name);
6339 XP_ERROR0(XPATH_EXPR_ERROR);
6340 }
6341
6342 *test = NODE_TEST_TYPE;
6343
6344 SKIP_BLANKS;
6345 if (*type == NODE_TYPE_PI) {
6346 /*
6347 * Specific case: search a PI by name.
6348 */
Owen Taylor3473f882001-02-23 17:55:21 +00006349 if (name != NULL)
6350 xmlFree(name);
Daniel Veillard82e49712001-04-26 14:38:03 +00006351 name = NULL;
6352 if (CUR != ')') {
6353 name = xmlXPathParseLiteral(ctxt);
6354 CHECK_ERROR 0;
6355 SKIP_BLANKS;
6356 }
Owen Taylor3473f882001-02-23 17:55:21 +00006357 }
6358 if (CUR != ')') {
6359 if (name != NULL)
6360 xmlFree(name);
6361 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6362 }
6363 NEXT;
6364 return(name);
6365 }
6366 *test = NODE_TEST_NAME;
6367 if ((!blanks) && (CUR == ':')) {
6368 NEXT;
6369
6370 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006371 * Since currently the parser context don't have a
6372 * namespace list associated:
6373 * The namespace name for this prefix can be computed
6374 * only at evaluation time. The compilation is done
6375 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00006376 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006377#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00006378 *prefix = xmlXPathNsLookup(ctxt->context, name);
6379 if (name != NULL)
6380 xmlFree(name);
6381 if (*prefix == NULL) {
6382 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6383 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006384#else
6385 *prefix = name;
6386#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006387
6388 if (CUR == '*') {
6389 /*
6390 * All elements
6391 */
6392 NEXT;
6393 *test = NODE_TEST_ALL;
6394 return(NULL);
6395 }
6396
6397 name = xmlXPathParseNCName(ctxt);
6398 if (name == NULL) {
6399 XP_ERROR0(XPATH_EXPR_ERROR);
6400 }
6401 }
6402 return(name);
6403}
6404
6405/**
6406 * xmlXPathIsAxisName:
6407 * @name: a preparsed name token
6408 *
6409 * [6] AxisName ::= 'ancestor'
6410 * | 'ancestor-or-self'
6411 * | 'attribute'
6412 * | 'child'
6413 * | 'descendant'
6414 * | 'descendant-or-self'
6415 * | 'following'
6416 * | 'following-sibling'
6417 * | 'namespace'
6418 * | 'parent'
6419 * | 'preceding'
6420 * | 'preceding-sibling'
6421 * | 'self'
6422 *
6423 * Returns the axis or 0
6424 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006425static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00006426xmlXPathIsAxisName(const xmlChar *name) {
6427 xmlXPathAxisVal ret = 0;
6428 switch (name[0]) {
6429 case 'a':
6430 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6431 ret = AXIS_ANCESTOR;
6432 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6433 ret = AXIS_ANCESTOR_OR_SELF;
6434 if (xmlStrEqual(name, BAD_CAST "attribute"))
6435 ret = AXIS_ATTRIBUTE;
6436 break;
6437 case 'c':
6438 if (xmlStrEqual(name, BAD_CAST "child"))
6439 ret = AXIS_CHILD;
6440 break;
6441 case 'd':
6442 if (xmlStrEqual(name, BAD_CAST "descendant"))
6443 ret = AXIS_DESCENDANT;
6444 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6445 ret = AXIS_DESCENDANT_OR_SELF;
6446 break;
6447 case 'f':
6448 if (xmlStrEqual(name, BAD_CAST "following"))
6449 ret = AXIS_FOLLOWING;
6450 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6451 ret = AXIS_FOLLOWING_SIBLING;
6452 break;
6453 case 'n':
6454 if (xmlStrEqual(name, BAD_CAST "namespace"))
6455 ret = AXIS_NAMESPACE;
6456 break;
6457 case 'p':
6458 if (xmlStrEqual(name, BAD_CAST "parent"))
6459 ret = AXIS_PARENT;
6460 if (xmlStrEqual(name, BAD_CAST "preceding"))
6461 ret = AXIS_PRECEDING;
6462 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6463 ret = AXIS_PRECEDING_SIBLING;
6464 break;
6465 case 's':
6466 if (xmlStrEqual(name, BAD_CAST "self"))
6467 ret = AXIS_SELF;
6468 break;
6469 }
6470 return(ret);
6471}
6472
6473/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006474 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00006475 * @ctxt: the XPath Parser context
6476 *
6477 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6478 * | AbbreviatedStep
6479 *
6480 * [12] AbbreviatedStep ::= '.' | '..'
6481 *
6482 * [5] AxisSpecifier ::= AxisName '::'
6483 * | AbbreviatedAxisSpecifier
6484 *
6485 * [13] AbbreviatedAxisSpecifier ::= '@'?
6486 *
6487 * Modified for XPtr range support as:
6488 *
6489 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6490 * | AbbreviatedStep
6491 * | 'range-to' '(' Expr ')' Predicate*
6492 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006493 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00006494 * A location step of . is short for self::node(). This is
6495 * particularly useful in conjunction with //. For example, the
6496 * location path .//para is short for
6497 * self::node()/descendant-or-self::node()/child::para
6498 * and so will select all para descendant elements of the context
6499 * node.
6500 * Similarly, a location step of .. is short for parent::node().
6501 * For example, ../title is short for parent::node()/child::title
6502 * and so will select the title children of the parent of the context
6503 * node.
6504 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006505static void
6506xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006507#ifdef LIBXML_XPTR_ENABLED
6508 int rangeto = 0;
6509 int op2 = -1;
6510#endif
6511
Owen Taylor3473f882001-02-23 17:55:21 +00006512 SKIP_BLANKS;
6513 if ((CUR == '.') && (NXT(1) == '.')) {
6514 SKIP(2);
6515 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006516 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
6517 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006518 } else if (CUR == '.') {
6519 NEXT;
6520 SKIP_BLANKS;
6521 } else {
6522 xmlChar *name = NULL;
6523 const xmlChar *prefix = NULL;
6524 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006525 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006526 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006527 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00006528
6529 /*
6530 * The modification needed for XPointer change to the production
6531 */
6532#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006533 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00006534 name = xmlXPathParseNCName(ctxt);
6535 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006536 op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006537 xmlFree(name);
6538 SKIP_BLANKS;
6539 if (CUR != '(') {
6540 XP_ERROR(XPATH_EXPR_ERROR);
6541 }
6542 NEXT;
6543 SKIP_BLANKS;
6544
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006545 xmlXPathCompileExpr(ctxt);
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006546 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
Owen Taylor3473f882001-02-23 17:55:21 +00006547 CHECK_ERROR;
6548
6549 SKIP_BLANKS;
6550 if (CUR != ')') {
6551 XP_ERROR(XPATH_EXPR_ERROR);
6552 }
6553 NEXT;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006554 rangeto = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006555 goto eval_predicates;
6556 }
6557 }
6558#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00006559 if (CUR == '*') {
6560 axis = AXIS_CHILD;
6561 } else {
6562 if (name == NULL)
6563 name = xmlXPathParseNCName(ctxt);
6564 if (name != NULL) {
6565 axis = xmlXPathIsAxisName(name);
6566 if (axis != 0) {
6567 SKIP_BLANKS;
6568 if ((CUR == ':') && (NXT(1) == ':')) {
6569 SKIP(2);
6570 xmlFree(name);
6571 name = NULL;
6572 } else {
6573 /* an element name can conflict with an axis one :-\ */
6574 axis = AXIS_CHILD;
6575 }
Owen Taylor3473f882001-02-23 17:55:21 +00006576 } else {
Owen Taylor3473f882001-02-23 17:55:21 +00006577 axis = AXIS_CHILD;
6578 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006579 } else if (CUR == '@') {
6580 NEXT;
6581 axis = AXIS_ATTRIBUTE;
Owen Taylor3473f882001-02-23 17:55:21 +00006582 } else {
Daniel Veillard2156a562001-04-28 12:24:34 +00006583 axis = AXIS_CHILD;
Owen Taylor3473f882001-02-23 17:55:21 +00006584 }
Owen Taylor3473f882001-02-23 17:55:21 +00006585 }
6586
6587 CHECK_ERROR;
6588
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006589 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00006590 if (test == 0)
6591 return;
6592
6593#ifdef DEBUG_STEP
6594 xmlGenericError(xmlGenericErrorContext,
6595 "Basis : computing new set\n");
6596#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006597
Owen Taylor3473f882001-02-23 17:55:21 +00006598#ifdef DEBUG_STEP
6599 xmlGenericError(xmlGenericErrorContext, "Basis : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006600 if (ctxt->value == NULL)
6601 xmlGenericError(xmlGenericErrorContext, "no value\n");
6602 else if (ctxt->value->nodesetval == NULL)
6603 xmlGenericError(xmlGenericErrorContext, "Empty\n");
6604 else
6605 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00006606#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006607
6608eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006609 op1 = ctxt->comp->last;
6610 ctxt->comp->last = -1;
6611
Owen Taylor3473f882001-02-23 17:55:21 +00006612 SKIP_BLANKS;
6613 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006614 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006615 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006616
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006617#ifdef LIBXML_XPTR_ENABLED
6618 if (rangeto) {
6619 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
6620 } else
6621#endif
6622 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
6623 test, type, (void *)prefix, (void *)name);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006624
Owen Taylor3473f882001-02-23 17:55:21 +00006625 }
6626#ifdef DEBUG_STEP
6627 xmlGenericError(xmlGenericErrorContext, "Step : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006628 if (ctxt->value == NULL)
6629 xmlGenericError(xmlGenericErrorContext, "no value\n");
6630 else if (ctxt->value->nodesetval == NULL)
6631 xmlGenericError(xmlGenericErrorContext, "Empty\n");
6632 else
6633 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6634 ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00006635#endif
6636}
6637
6638/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006639 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006640 * @ctxt: the XPath Parser context
6641 *
6642 * [3] RelativeLocationPath ::= Step
6643 * | RelativeLocationPath '/' Step
6644 * | AbbreviatedRelativeLocationPath
6645 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6646 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006647 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00006648 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006649static void
Owen Taylor3473f882001-02-23 17:55:21 +00006650#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006651xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006652#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006653xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006654#endif
6655(xmlXPathParserContextPtr ctxt) {
6656 SKIP_BLANKS;
6657 if ((CUR == '/') && (NXT(1) == '/')) {
6658 SKIP(2);
6659 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006660 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6661 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006662 } else if (CUR == '/') {
6663 NEXT;
6664 SKIP_BLANKS;
6665 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006666 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006667 SKIP_BLANKS;
6668 while (CUR == '/') {
6669 if ((CUR == '/') && (NXT(1) == '/')) {
6670 SKIP(2);
6671 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006672 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00006673 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006674 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006675 } else if (CUR == '/') {
6676 NEXT;
6677 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006678 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006679 }
6680 SKIP_BLANKS;
6681 }
6682}
6683
6684/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006685 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006686 * @ctxt: the XPath Parser context
6687 *
6688 * [1] LocationPath ::= RelativeLocationPath
6689 * | AbsoluteLocationPath
6690 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6691 * | AbbreviatedAbsoluteLocationPath
6692 * [10] AbbreviatedAbsoluteLocationPath ::=
6693 * '//' RelativeLocationPath
6694 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006695 * Compile a location path
6696 *
Owen Taylor3473f882001-02-23 17:55:21 +00006697 * // is short for /descendant-or-self::node()/. For example,
6698 * //para is short for /descendant-or-self::node()/child::para and
6699 * so will select any para element in the document (even a para element
6700 * that is a document element will be selected by //para since the
6701 * document element node is a child of the root node); div//para is
6702 * short for div/descendant-or-self::node()/child::para and so will
6703 * select all para descendants of div children.
6704 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006705static void
6706xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006707 SKIP_BLANKS;
6708 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006709 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006710 } else {
6711 while (CUR == '/') {
6712 if ((CUR == '/') && (NXT(1) == '/')) {
6713 SKIP(2);
6714 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006715 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6716 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006717 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006718 } else if (CUR == '/') {
6719 NEXT;
6720 SKIP_BLANKS;
6721 if (CUR != 0)
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006722 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006723 }
6724 }
6725 }
6726}
6727
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006728/************************************************************************
6729 * *
6730 * XPath precompiled expression evaluation *
6731 * *
6732 ************************************************************************/
6733
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006734static void
6735xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
6736
6737/**
6738 * xmlXPathNodeCollectAndTest:
6739 * @ctxt: the XPath Parser context
6740 * @op: the XPath precompiled step operation
6741 *
6742 * This is the function implementing a step: based on the current list
6743 * of nodes, it builds up a new list, looking at all nodes under that
6744 * axis and selecting them it also do the predicate filtering
6745 *
6746 * Pushes the new NodeSet resulting from the search.
6747 */
6748static void
6749xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
6750 xmlXPathStepOpPtr op) {
6751 xmlXPathAxisVal axis = op->value;
6752 xmlXPathTestVal test = op->value2;
6753 xmlXPathTypeVal type = op->value3;
6754 const xmlChar *prefix = op->value4;
6755 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006756 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006757
6758#ifdef DEBUG_STEP
6759 int n = 0, t = 0;
6760#endif
6761 int i;
6762 xmlNodeSetPtr ret, list;
6763 xmlXPathTraversalFunction next = NULL;
6764 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
6765 xmlNodePtr cur = NULL;
6766 xmlXPathObjectPtr obj;
6767 xmlNodeSetPtr nodelist;
6768 xmlNodePtr tmp;
6769
6770 CHECK_TYPE(XPATH_NODESET);
6771 obj = valuePop(ctxt);
6772 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006773 if (prefix != NULL) {
6774 URI = xmlXPathNsLookup(ctxt->context, prefix);
6775 if (URI == NULL)
6776 XP_ERROR(XPATH_UNDEF_PREFIX_ERROR);
6777 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006778
6779#ifdef DEBUG_STEP
6780 xmlGenericError(xmlGenericErrorContext,
6781 "new step : ");
6782#endif
6783 switch (axis) {
6784 case AXIS_ANCESTOR:
6785#ifdef DEBUG_STEP
6786 xmlGenericError(xmlGenericErrorContext,
6787 "axis 'ancestors' ");
6788#endif
6789 next = xmlXPathNextAncestor; break;
6790 case AXIS_ANCESTOR_OR_SELF:
6791#ifdef DEBUG_STEP
6792 xmlGenericError(xmlGenericErrorContext,
6793 "axis 'ancestors-or-self' ");
6794#endif
6795 next = xmlXPathNextAncestorOrSelf; break;
6796 case AXIS_ATTRIBUTE:
6797#ifdef DEBUG_STEP
6798 xmlGenericError(xmlGenericErrorContext,
6799 "axis 'attributes' ");
6800#endif
6801 next = xmlXPathNextAttribute; break;
6802 break;
6803 case AXIS_CHILD:
6804#ifdef DEBUG_STEP
6805 xmlGenericError(xmlGenericErrorContext,
6806 "axis 'child' ");
6807#endif
6808 next = xmlXPathNextChild; break;
6809 case AXIS_DESCENDANT:
6810#ifdef DEBUG_STEP
6811 xmlGenericError(xmlGenericErrorContext,
6812 "axis 'descendant' ");
6813#endif
6814 next = xmlXPathNextDescendant; break;
6815 case AXIS_DESCENDANT_OR_SELF:
6816#ifdef DEBUG_STEP
6817 xmlGenericError(xmlGenericErrorContext,
6818 "axis 'descendant-or-self' ");
6819#endif
6820 next = xmlXPathNextDescendantOrSelf; break;
6821 case AXIS_FOLLOWING:
6822#ifdef DEBUG_STEP
6823 xmlGenericError(xmlGenericErrorContext,
6824 "axis 'following' ");
6825#endif
6826 next = xmlXPathNextFollowing; break;
6827 case AXIS_FOLLOWING_SIBLING:
6828#ifdef DEBUG_STEP
6829 xmlGenericError(xmlGenericErrorContext,
6830 "axis 'following-siblings' ");
6831#endif
6832 next = xmlXPathNextFollowingSibling; break;
6833 case AXIS_NAMESPACE:
6834#ifdef DEBUG_STEP
6835 xmlGenericError(xmlGenericErrorContext,
6836 "axis 'namespace' ");
6837#endif
6838 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
6839 break;
6840 case AXIS_PARENT:
6841#ifdef DEBUG_STEP
6842 xmlGenericError(xmlGenericErrorContext,
6843 "axis 'parent' ");
6844#endif
6845 next = xmlXPathNextParent; break;
6846 case AXIS_PRECEDING:
6847#ifdef DEBUG_STEP
6848 xmlGenericError(xmlGenericErrorContext,
6849 "axis 'preceding' ");
6850#endif
6851 next = xmlXPathNextPreceding; break;
6852 case AXIS_PRECEDING_SIBLING:
6853#ifdef DEBUG_STEP
6854 xmlGenericError(xmlGenericErrorContext,
6855 "axis 'preceding-sibling' ");
6856#endif
6857 next = xmlXPathNextPrecedingSibling; break;
6858 case AXIS_SELF:
6859#ifdef DEBUG_STEP
6860 xmlGenericError(xmlGenericErrorContext,
6861 "axis 'self' ");
6862#endif
6863 next = xmlXPathNextSelf; break;
6864 }
6865 if (next == NULL)
6866 return;
6867
6868 nodelist = obj->nodesetval;
6869 if (nodelist == NULL) {
6870 xmlXPathFreeObject(obj);
6871 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
6872 return;
6873 }
6874 addNode = xmlXPathNodeSetAddUnique;
6875 ret = NULL;
6876#ifdef DEBUG_STEP
6877 xmlGenericError(xmlGenericErrorContext,
6878 " context contains %d nodes\n",
6879 nodelist->nodeNr);
6880 switch (test) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006881 case NODE_TEST_NONE:
6882 xmlGenericError(xmlGenericErrorContext,
6883 " searching for none !!!\n");
6884 break;
6885 case NODE_TEST_TYPE:
6886 xmlGenericError(xmlGenericErrorContext,
6887 " searching for type %d\n", type);
6888 break;
6889 case NODE_TEST_PI:
6890 xmlGenericError(xmlGenericErrorContext,
6891 " searching for PI !!!\n");
6892 break;
6893 case NODE_TEST_ALL:
6894 xmlGenericError(xmlGenericErrorContext,
6895 " searching for *\n");
6896 break;
6897 case NODE_TEST_NS:
6898 xmlGenericError(xmlGenericErrorContext,
6899 " searching for namespace %s\n",
6900 prefix);
6901 break;
6902 case NODE_TEST_NAME:
6903 xmlGenericError(xmlGenericErrorContext,
6904 " searching for name %s\n", name);
6905 if (prefix != NULL)
6906 xmlGenericError(xmlGenericErrorContext,
6907 " with namespace %s\n",
6908 prefix);
6909 break;
6910 }
6911 xmlGenericError(xmlGenericErrorContext, "Testing : ");
6912#endif
6913 /*
6914 * 2.3 Node Tests
6915 * - For the attribute axis, the principal node type is attribute.
6916 * - For the namespace axis, the principal node type is namespace.
6917 * - For other axes, the principal node type is element.
6918 *
6919 * A node test * is true for any node of the
6920 * principal node type. For example, child::* willi
6921 * select all element children of the context node
6922 */
6923 tmp = ctxt->context->node;
6924 for (i = 0;i < nodelist->nodeNr; i++) {
6925 ctxt->context->node = nodelist->nodeTab[i];
6926
6927 cur = NULL;
6928 list = xmlXPathNodeSetCreate(NULL);
6929 do {
6930 cur = next(ctxt, cur);
6931 if (cur == NULL) break;
6932#ifdef DEBUG_STEP
6933 t++;
6934 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
6935#endif
6936 switch (test) {
6937 case NODE_TEST_NONE:
6938 ctxt->context->node = tmp;
6939 STRANGE
6940 return;
6941 case NODE_TEST_TYPE:
6942 if ((cur->type == type) ||
6943 ((type == NODE_TYPE_NODE) &&
6944 ((cur->type == XML_DOCUMENT_NODE) ||
6945 (cur->type == XML_HTML_DOCUMENT_NODE) ||
6946 (cur->type == XML_ELEMENT_NODE) ||
6947 (cur->type == XML_PI_NODE) ||
6948 (cur->type == XML_COMMENT_NODE) ||
6949 (cur->type == XML_CDATA_SECTION_NODE) ||
6950 (cur->type == XML_TEXT_NODE)))) {
6951#ifdef DEBUG_STEP
6952 n++;
6953#endif
6954 addNode(list, cur);
6955 }
6956 break;
6957 case NODE_TEST_PI:
6958 if (cur->type == XML_PI_NODE) {
6959 if ((name != NULL) &&
6960 (!xmlStrEqual(name, cur->name)))
6961 break;
6962#ifdef DEBUG_STEP
6963 n++;
6964#endif
6965 addNode(list, cur);
6966 }
6967 break;
6968 case NODE_TEST_ALL:
6969 if (axis == AXIS_ATTRIBUTE) {
6970 if (cur->type == XML_ATTRIBUTE_NODE) {
6971#ifdef DEBUG_STEP
6972 n++;
6973#endif
6974 addNode(list, cur);
6975 }
6976 } else if (axis == AXIS_NAMESPACE) {
6977 if (cur->type == XML_NAMESPACE_DECL) {
6978#ifdef DEBUG_STEP
6979 n++;
6980#endif
6981 addNode(list, cur);
6982 }
6983 } else {
6984 if ((cur->type == XML_ELEMENT_NODE) ||
6985 (cur->type == XML_DOCUMENT_NODE) ||
6986 (cur->type == XML_HTML_DOCUMENT_NODE)) {
6987 if (prefix == NULL) {
6988#ifdef DEBUG_STEP
6989 n++;
6990#endif
6991 addNode(list, cur);
6992 } else if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006993 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006994 cur->ns->href))) {
6995#ifdef DEBUG_STEP
6996 n++;
6997#endif
6998 addNode(list, cur);
6999 }
7000 }
7001 }
7002 break;
7003 case NODE_TEST_NS: {
7004 TODO;
7005 break;
7006 }
7007 case NODE_TEST_NAME:
7008 switch (cur->type) {
7009 case XML_ELEMENT_NODE:
7010 if (xmlStrEqual(name, cur->name)) {
7011 if (prefix == NULL) {
7012 if ((cur->ns == NULL) ||
7013 (cur->ns->prefix == NULL)) {
7014#ifdef DEBUG_STEP
7015 n++;
7016#endif
7017 addNode(list, cur);
7018 }
7019 } else {
7020 if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00007021 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007022 cur->ns->href))) {
7023#ifdef DEBUG_STEP
7024 n++;
7025#endif
7026 addNode(list, cur);
7027 }
7028 }
7029 }
7030 break;
7031 case XML_ATTRIBUTE_NODE: {
7032 xmlAttrPtr attr = (xmlAttrPtr) cur;
7033 if (xmlStrEqual(name, attr->name)) {
7034 if (prefix == NULL) {
7035 if ((attr->ns == NULL) ||
7036 (attr->ns->prefix == NULL)) {
7037#ifdef DEBUG_STEP
7038 n++;
7039#endif
7040 addNode(list, (xmlNodePtr) attr);
7041 }
7042 } else {
7043 if ((attr->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00007044 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007045 attr->ns->href))) {
7046#ifdef DEBUG_STEP
7047 n++;
7048#endif
7049 addNode(list, (xmlNodePtr) attr);
7050 }
7051 }
7052 }
7053 break;
7054 }
7055 case XML_NAMESPACE_DECL: {
7056 TODO;
7057 break;
7058 }
7059 default:
7060 break;
7061 }
7062 break;
7063 }
7064 } while (cur != NULL);
7065
7066 /*
7067 * If there is some predicate filtering do it now
7068 */
7069 if (op->ch2 != -1) {
7070 xmlXPathObjectPtr obj2;
7071
7072 valuePush(ctxt, xmlXPathWrapNodeSet(list));
7073 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
7074 CHECK_TYPE(XPATH_NODESET);
7075 obj2 = valuePop(ctxt);
7076 list = obj2->nodesetval;
7077 obj2->nodesetval = NULL;
7078 xmlXPathFreeObject(obj2);
7079 }
7080 if (ret == NULL) {
7081 ret = list;
7082 } else {
7083 ret = xmlXPathNodeSetMerge(ret, list);
7084 xmlXPathFreeNodeSet(list);
7085 }
7086 }
7087 ctxt->context->node = tmp;
7088#ifdef DEBUG_STEP
7089 xmlGenericError(xmlGenericErrorContext,
7090 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
7091#endif
7092 xmlXPathFreeObject(obj);
7093 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
7094}
7095
Owen Taylor3473f882001-02-23 17:55:21 +00007096/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007097 * xmlXPathCompOpEval:
7098 * @ctxt: the XPath parser context with the compiled expression
7099 * @op: an XPath compiled operation
7100 *
7101 * Evaluate the Precompiled XPath operation
7102 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007103static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007104xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) {
7105 int equal, ret;
7106 xmlXPathCompExprPtr comp;
7107 xmlXPathObjectPtr arg1, arg2;
7108
7109 comp = ctxt->comp;
7110 switch (op->op) {
7111 case XPATH_OP_END:
7112 return;
7113 case XPATH_OP_AND:
7114 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7115 xmlXPathBooleanFunction(ctxt, 1);
7116 if (ctxt->value->boolval == 0)
7117 return;
7118 arg2 = valuePop(ctxt);
7119 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7120 xmlXPathBooleanFunction(ctxt, 1);
7121 arg1 = valuePop(ctxt);
7122 arg1->boolval &= arg2->boolval;
7123 valuePush(ctxt, arg1);
7124 xmlXPathFreeObject(arg2);
7125 return;
7126 case XPATH_OP_OR:
7127 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7128 xmlXPathBooleanFunction(ctxt, 1);
7129 if (ctxt->value->boolval == 1)
7130 return;
7131 arg2 = valuePop(ctxt);
7132 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7133 xmlXPathBooleanFunction(ctxt, 1);
7134 arg1 = valuePop(ctxt);
7135 arg1->boolval |= arg2->boolval;
7136 valuePush(ctxt, arg1);
7137 xmlXPathFreeObject(arg2);
7138 return;
7139 case XPATH_OP_EQUAL:
7140 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7141 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7142 equal = xmlXPathEqualValues(ctxt);
7143 if (op->value) valuePush(ctxt, xmlXPathNewBoolean(equal));
7144 else valuePush(ctxt, xmlXPathNewBoolean(!equal));
7145 return;
7146 case XPATH_OP_CMP:
7147 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7148 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7149 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
7150 valuePush(ctxt, xmlXPathNewBoolean(ret));
7151 return;
7152 case XPATH_OP_PLUS:
7153 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7154 if (op->ch2 != -1)
7155 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7156 if (op->value == 0) xmlXPathSubValues(ctxt);
7157 else if (op->value == 1) xmlXPathAddValues(ctxt);
7158 else if (op->value == 2) xmlXPathValueFlipSign(ctxt);
7159 else if (op->value == 3) {
7160 xmlXPathObjectPtr arg;
7161
7162 POP_FLOAT
7163 valuePush(ctxt, arg);
7164 }
7165 return;
7166 case XPATH_OP_MULT:
7167 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7168 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7169 if (op->value == 0) xmlXPathMultValues(ctxt);
7170 else if (op->value == 1) xmlXPathDivValues(ctxt);
7171 else if (op->value == 2) xmlXPathModValues(ctxt);
7172 return;
7173 case XPATH_OP_UNION:
7174 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7175 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7176 CHECK_TYPE(XPATH_NODESET);
7177 arg2 = valuePop(ctxt);
7178
7179 CHECK_TYPE(XPATH_NODESET);
7180 arg1 = valuePop(ctxt);
7181
7182 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
7183 arg2->nodesetval);
7184 valuePush(ctxt, arg1);
7185 xmlXPathFreeObject(arg2);
7186 return;
7187 case XPATH_OP_ROOT:
7188 xmlXPathRoot(ctxt);
7189 return;
7190 case XPATH_OP_NODE:
7191 if (op->ch1 != -1)
7192 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7193 if (op->ch2 != -1)
7194 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7195 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
7196 return;
7197 case XPATH_OP_RESET:
7198 if (op->ch1 != -1)
7199 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7200 if (op->ch2 != -1)
7201 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7202 ctxt->context->node = NULL;
7203 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007204 case XPATH_OP_COLLECT: {
7205 if (op->ch1 == -1)
7206 return;
7207
7208 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7209 xmlXPathNodeCollectAndTest(ctxt, op);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007210 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007211 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007212 case XPATH_OP_VALUE:
7213 valuePush(ctxt,
7214 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
7215 return;
7216 case XPATH_OP_VARIABLE: {
7217 if (op->ch1 != -1)
7218 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7219 if (op->value5 == NULL)
7220 valuePush(ctxt,
7221 xmlXPathVariableLookup(ctxt->context, op->value4));
7222 else {
7223 const xmlChar *URI;
7224 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7225 if (URI == NULL) {
7226 xmlGenericError(xmlGenericErrorContext,
7227 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
7228 op->value4, op->value5);
7229 return;
7230 }
7231 valuePush(ctxt,
7232 xmlXPathVariableLookupNS(ctxt->context,
7233 op->value4, URI));
7234 }
7235 return;
7236 }
7237 case XPATH_OP_FUNCTION: {
7238 xmlXPathFunction func;
7239
7240 if (op->ch1 != -1)
7241 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
Daniel Veillarde39a93d2001-04-28 14:35:02 +00007242 if (op->cache != NULL)
7243 func = (xmlXPathFunction) op->cache;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007244 else {
Daniel Veillarde39a93d2001-04-28 14:35:02 +00007245 if (op->value5 == NULL)
7246 func = xmlXPathFunctionLookup(ctxt->context, op->value4);
7247 else {
7248 const xmlChar *URI;
7249 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7250 if (URI == NULL) {
7251 xmlGenericError(xmlGenericErrorContext,
7252 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
7253 op->value4, op->value5);
7254 return;
7255 }
7256 func = xmlXPathFunctionLookupNS(ctxt->context,
7257 op->value4, URI);
7258 }
7259 if (func == NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007260 xmlGenericError(xmlGenericErrorContext,
Daniel Veillarde39a93d2001-04-28 14:35:02 +00007261 "xmlXPathRunEval: function %s not found\n",
7262 op->value4);
7263 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007264 return;
7265 }
Daniel Veillarde39a93d2001-04-28 14:35:02 +00007266 op->cache = (void *) func;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007267 }
7268 func(ctxt, op->value);
7269 return;
7270 }
7271 case XPATH_OP_ARG:
7272 if (op->ch1 != -1)
7273 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7274 if (op->ch2 != -1)
7275 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7276 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007277 case XPATH_OP_PREDICATE:
7278 case XPATH_OP_FILTER: {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007279 xmlXPathObjectPtr res;
7280 xmlXPathObjectPtr obj, tmp;
7281 xmlNodeSetPtr newset = NULL;
7282 xmlNodeSetPtr oldset;
7283 xmlNodePtr oldnode;
7284 int i;
7285
7286 if (op->ch1 != -1)
7287 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7288 if (op->ch2 == -1)
7289 return;
7290
7291 oldnode = ctxt->context->node;
7292
7293#ifdef LIBXML_XPTR_ENABLED
7294 /*
7295 * Hum are we filtering the result of an XPointer expression
7296 */
7297 if (ctxt->value->type == XPATH_LOCATIONSET) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007298 xmlLocationSetPtr newlocset = NULL;
7299 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007300
7301 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007302 * Extract the old locset, and then evaluate the result of the
7303 * expression for all the element in the locset. use it to grow
7304 * up a new locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007305 */
7306 CHECK_TYPE(XPATH_LOCATIONSET);
7307 obj = valuePop(ctxt);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007308 oldlocset = obj->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007309 ctxt->context->node = NULL;
7310
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007311 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007312 ctxt->context->contextSize = 0;
7313 ctxt->context->proximityPosition = 0;
7314 if (op->ch2 != -1)
7315 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7316 res = valuePop(ctxt);
7317 if (res != NULL)
7318 xmlXPathFreeObject(res);
7319 valuePush(ctxt, obj);
7320 CHECK_ERROR;
7321 return;
7322 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007323 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007324
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007325 for (i = 0; i < oldlocset->locNr; i++) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007326 /*
7327 * Run the evaluation with a node list made of a
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007328 * single item in the nodelocset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007329 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007330 ctxt->context->node = oldlocset->locTab[i]->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007331 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7332 valuePush(ctxt, tmp);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007333 ctxt->context->contextSize = oldlocset->locNr;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007334 ctxt->context->proximityPosition = i + 1;
7335
7336 if (op->ch2 != -1)
7337 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7338 CHECK_ERROR;
7339
7340 /*
7341 * The result of the evaluation need to be tested to
7342 * decided whether the filter succeeded or not
7343 */
7344 res = valuePop(ctxt);
7345 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007346 xmlXPtrLocationSetAdd(newlocset,
7347 xmlXPathObjectCopy(oldlocset->locTab[i]));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007348 }
7349
7350 /*
7351 * Cleanup
7352 */
7353 if (res != NULL)
7354 xmlXPathFreeObject(res);
7355 if (ctxt->value == tmp) {
7356 res = valuePop(ctxt);
7357 xmlXPathFreeObject(res);
7358 }
7359
7360 ctxt->context->node = NULL;
7361 }
7362
7363 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007364 * The result is used as the new evaluation locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007365 */
7366 xmlXPathFreeObject(obj);
7367 ctxt->context->node = NULL;
7368 ctxt->context->contextSize = -1;
7369 ctxt->context->proximityPosition = -1;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007370 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007371 ctxt->context->node = oldnode;
7372 return;
7373 }
7374#endif /* LIBXML_XPTR_ENABLED */
7375
7376 /*
7377 * Extract the old set, and then evaluate the result of the
7378 * expression for all the element in the set. use it to grow
7379 * up a new set.
7380 */
7381 CHECK_TYPE(XPATH_NODESET);
7382 obj = valuePop(ctxt);
7383 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00007384
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007385 oldnode = ctxt->context->node;
7386 ctxt->context->node = NULL;
7387
7388 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
7389 ctxt->context->contextSize = 0;
7390 ctxt->context->proximityPosition = 0;
7391 if (op->ch2 != -1)
7392 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7393 res = valuePop(ctxt);
7394 if (res != NULL)
7395 xmlXPathFreeObject(res);
7396 valuePush(ctxt, obj);
Daniel Veillard911f49a2001-04-07 15:39:35 +00007397 ctxt->context->node = oldnode;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007398 CHECK_ERROR;
7399 } else {
7400 /*
7401 * Initialize the new set.
7402 */
7403 newset = xmlXPathNodeSetCreate(NULL);
7404
7405 for (i = 0; i < oldset->nodeNr; i++) {
7406 /*
7407 * Run the evaluation with a node list made of
7408 * a single item in the nodeset.
7409 */
7410 ctxt->context->node = oldset->nodeTab[i];
7411 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7412 valuePush(ctxt, tmp);
7413 ctxt->context->contextSize = oldset->nodeNr;
7414 ctxt->context->proximityPosition = i + 1;
7415
7416 if (op->ch2 != -1)
7417 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7418 CHECK_ERROR;
7419
7420 /*
7421 * The result of the evaluation need to be tested to
7422 * decided whether the filter succeeded or not
7423 */
7424 res = valuePop(ctxt);
7425 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
7426 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
7427 }
7428
7429 /*
7430 * Cleanup
7431 */
7432 if (res != NULL)
7433 xmlXPathFreeObject(res);
7434 if (ctxt->value == tmp) {
7435 res = valuePop(ctxt);
7436 xmlXPathFreeObject(res);
7437 }
7438
7439 ctxt->context->node = NULL;
7440 }
7441
7442 /*
7443 * The result is used as the new evaluation set.
7444 */
7445 xmlXPathFreeObject(obj);
7446 ctxt->context->node = NULL;
7447 ctxt->context->contextSize = -1;
7448 ctxt->context->proximityPosition = -1;
7449 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
7450 }
7451 ctxt->context->node = oldnode;
7452 return;
7453 }
7454 case XPATH_OP_SORT:
7455 if (op->ch1 != -1)
7456 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7457 if ((ctxt->value != NULL) &&
7458 (ctxt->value->type == XPATH_NODESET) &&
7459 (ctxt->value->nodesetval != NULL))
7460 xmlXPathNodeSetSort(ctxt->value->nodesetval);
7461 return;
7462#ifdef LIBXML_XPTR_ENABLED
7463 case XPATH_OP_RANGETO: {
7464 xmlXPathObjectPtr range;
7465 xmlXPathObjectPtr res, obj;
7466 xmlXPathObjectPtr tmp;
7467 xmlLocationSetPtr newset = NULL;
7468 xmlNodeSetPtr oldset;
7469 int i;
7470
7471 if (op->ch1 != -1)
7472 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7473 if (op->ch2 == -1)
7474 return;
7475
7476 CHECK_TYPE(XPATH_NODESET);
7477 obj = valuePop(ctxt);
7478 oldset = obj->nodesetval;
7479 ctxt->context->node = NULL;
7480
7481 newset = xmlXPtrLocationSetCreate(NULL);
7482
Daniel Veillard911f49a2001-04-07 15:39:35 +00007483 if (oldset != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007484 for (i = 0; i < oldset->nodeNr; i++) {
7485 /*
7486 * Run the evaluation with a node list made of a single item
7487 * in the nodeset.
7488 */
7489 ctxt->context->node = oldset->nodeTab[i];
7490 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7491 valuePush(ctxt, tmp);
7492
7493 if (op->ch2 != -1)
7494 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7495 CHECK_ERROR;
7496
7497 /*
7498 * The result of the evaluation need to be tested to
7499 * decided whether the filter succeeded or not
7500 */
7501 res = valuePop(ctxt);
7502 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
7503 if (range != NULL) {
7504 xmlXPtrLocationSetAdd(newset, range);
7505 }
7506
7507 /*
7508 * Cleanup
7509 */
7510 if (res != NULL)
7511 xmlXPathFreeObject(res);
7512 if (ctxt->value == tmp) {
7513 res = valuePop(ctxt);
7514 xmlXPathFreeObject(res);
7515 }
7516
7517 ctxt->context->node = NULL;
7518 }
Daniel Veillard911f49a2001-04-07 15:39:35 +00007519 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007520
7521 /*
7522 * The result is used as the new evaluation set.
7523 */
7524 xmlXPathFreeObject(obj);
7525 ctxt->context->node = NULL;
7526 ctxt->context->contextSize = -1;
7527 ctxt->context->proximityPosition = -1;
7528 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
7529 return;
7530 }
7531#endif /* LIBXML_XPTR_ENABLED */
7532 }
7533 xmlGenericError(xmlGenericErrorContext,
7534 "XPath: unknown precompiled operation %d\n",
7535 op->op);
7536 return;
7537}
7538
7539/**
7540 * xmlXPathRunEval:
7541 * @ctxt: the XPath parser context with the compiled expression
7542 *
7543 * Evaluate the Precompiled XPath expression in the given context.
7544 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007545static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007546xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
7547 xmlXPathCompExprPtr comp;
7548
7549 if ((ctxt == NULL) || (ctxt->comp == NULL))
7550 return;
7551
7552 if (ctxt->valueTab == NULL) {
7553 /* Allocate the value stack */
7554 ctxt->valueTab = (xmlXPathObjectPtr *)
7555 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
7556 if (ctxt->valueTab == NULL) {
7557 xmlFree(ctxt);
7558 xmlGenericError(xmlGenericErrorContext,
7559 "xmlXPathRunEval: out of memory\n");
7560 return;
7561 }
7562 ctxt->valueNr = 0;
7563 ctxt->valueMax = 10;
7564 ctxt->value = NULL;
7565 }
7566 comp = ctxt->comp;
7567 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
7568}
7569
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007570/************************************************************************
7571 * *
7572 * Public interfaces *
7573 * *
7574 ************************************************************************/
7575
7576/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007577 * xmlXPathEvalPredicate:
7578 * @ctxt: the XPath context
7579 * @res: the Predicate Expression evaluation result
7580 *
7581 * Evaluate a predicate result for the current node.
7582 * A PredicateExpr is evaluated by evaluating the Expr and converting
7583 * the result to a boolean. If the result is a number, the result will
7584 * be converted to true if the number is equal to the position of the
7585 * context node in the context node list (as returned by the position
7586 * function) and will be converted to false otherwise; if the result
7587 * is not a number, then the result will be converted as if by a call
7588 * to the boolean function.
7589 *
7590 * Return 1 if predicate is true, 0 otherwise
7591 */
7592int
7593xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
7594 if (res == NULL) return(0);
7595 switch (res->type) {
7596 case XPATH_BOOLEAN:
7597 return(res->boolval);
7598 case XPATH_NUMBER:
7599 return(res->floatval == ctxt->proximityPosition);
7600 case XPATH_NODESET:
7601 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007602 if (res->nodesetval == NULL)
7603 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007604 return(res->nodesetval->nodeNr != 0);
7605 case XPATH_STRING:
7606 return((res->stringval != NULL) &&
7607 (xmlStrlen(res->stringval) != 0));
7608 default:
7609 STRANGE
7610 }
7611 return(0);
7612}
7613
7614/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007615 * xmlXPathEvaluatePredicateResult:
7616 * @ctxt: the XPath Parser context
7617 * @res: the Predicate Expression evaluation result
7618 *
7619 * Evaluate a predicate result for the current node.
7620 * A PredicateExpr is evaluated by evaluating the Expr and converting
7621 * the result to a boolean. If the result is a number, the result will
7622 * be converted to true if the number is equal to the position of the
7623 * context node in the context node list (as returned by the position
7624 * function) and will be converted to false otherwise; if the result
7625 * is not a number, then the result will be converted as if by a call
7626 * to the boolean function.
7627 *
7628 * Return 1 if predicate is true, 0 otherwise
7629 */
7630int
7631xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
7632 xmlXPathObjectPtr res) {
7633 if (res == NULL) return(0);
7634 switch (res->type) {
7635 case XPATH_BOOLEAN:
7636 return(res->boolval);
7637 case XPATH_NUMBER:
7638 return(res->floatval == ctxt->context->proximityPosition);
7639 case XPATH_NODESET:
7640 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00007641 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00007642 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007643 return(res->nodesetval->nodeNr != 0);
7644 case XPATH_STRING:
7645 return((res->stringval != NULL) &&
7646 (xmlStrlen(res->stringval) != 0));
7647 default:
7648 STRANGE
7649 }
7650 return(0);
7651}
7652
7653/**
7654 * xmlXPathCompile:
7655 * @str: the XPath expression
7656 *
7657 * Compile an XPath expression
7658 *
7659 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7660 * the caller has to free the object.
7661 */
7662xmlXPathCompExprPtr
7663xmlXPathCompile(const xmlChar *str) {
7664 xmlXPathParserContextPtr ctxt;
7665 xmlXPathCompExprPtr comp;
7666
7667 xmlXPathInit();
7668
7669 ctxt = xmlXPathNewParserContext(str, NULL);
7670 xmlXPathCompileExpr(ctxt);
7671
Daniel Veillard40af6492001-04-22 08:50:55 +00007672 if (*ctxt->cur != 0) {
7673 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7674 comp = NULL;
7675 } else {
7676 comp = ctxt->comp;
7677 ctxt->comp = NULL;
7678 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007679 xmlXPathFreeParserContext(ctxt);
7680 return(comp);
7681}
7682
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007683/**
7684 * xmlXPathCompiledEval:
7685 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00007686 * @ctx: the XPath context
7687 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007688 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00007689 *
7690 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7691 * the caller has to free the object.
7692 */
7693xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007694xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00007695 xmlXPathParserContextPtr ctxt;
7696 xmlXPathObjectPtr res, tmp, init = NULL;
7697 int stack = 0;
7698
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007699 if ((comp == NULL) || (ctx == NULL))
7700 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007701 xmlXPathInit();
7702
7703 CHECK_CONTEXT(ctx)
7704
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007705 ctxt = xmlXPathCompParserContext(comp, ctx);
7706 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007707
7708 if (ctxt->value == NULL) {
7709 xmlGenericError(xmlGenericErrorContext,
7710 "xmlXPathEval: evaluation failed\n");
7711 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00007712 } else {
7713 res = valuePop(ctxt);
7714 }
7715
7716 do {
7717 tmp = valuePop(ctxt);
7718 if (tmp != NULL) {
7719 if (tmp != init)
7720 stack++;
7721 xmlXPathFreeObject(tmp);
7722 }
7723 } while (tmp != NULL);
7724 if ((stack != 0) && (res != NULL)) {
7725 xmlGenericError(xmlGenericErrorContext,
7726 "xmlXPathEval: %d object left on the stack\n",
7727 stack);
7728 }
7729 if (ctxt->error != XPATH_EXPRESSION_OK) {
7730 xmlXPathFreeObject(res);
7731 res = NULL;
7732 }
7733
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007734
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007735 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007736 xmlXPathFreeParserContext(ctxt);
7737 return(res);
7738}
7739
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007740/**
7741 * xmlXPathEvalExpr:
7742 * @ctxt: the XPath Parser context
7743 *
7744 * Parse and evaluate an XPath expression in the given context,
7745 * then push the result on the context stack
7746 */
7747void
7748xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
7749 xmlXPathCompileExpr(ctxt);
7750 xmlXPathRunEval(ctxt);
7751}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007752
7753/**
7754 * xmlXPathEval:
7755 * @str: the XPath expression
7756 * @ctx: the XPath context
7757 *
7758 * Evaluate the XPath Location Path in the given context.
7759 *
7760 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7761 * the caller has to free the object.
7762 */
7763xmlXPathObjectPtr
7764xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
7765 xmlXPathParserContextPtr ctxt;
7766 xmlXPathObjectPtr res, tmp, init = NULL;
7767 int stack = 0;
7768
7769 xmlXPathInit();
7770
7771 CHECK_CONTEXT(ctx)
7772
7773 ctxt = xmlXPathNewParserContext(str, ctx);
7774 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007775
7776 if (ctxt->value == NULL) {
7777 xmlGenericError(xmlGenericErrorContext,
7778 "xmlXPathEval: evaluation failed\n");
7779 res = NULL;
7780 } else if (*ctxt->cur != 0) {
7781 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7782 res = NULL;
7783 } else {
7784 res = valuePop(ctxt);
7785 }
7786
7787 do {
7788 tmp = valuePop(ctxt);
7789 if (tmp != NULL) {
7790 if (tmp != init)
7791 stack++;
7792 xmlXPathFreeObject(tmp);
7793 }
7794 } while (tmp != NULL);
7795 if ((stack != 0) && (res != NULL)) {
7796 xmlGenericError(xmlGenericErrorContext,
7797 "xmlXPathEval: %d object left on the stack\n",
7798 stack);
7799 }
7800 if (ctxt->error != XPATH_EXPRESSION_OK) {
7801 xmlXPathFreeObject(res);
7802 res = NULL;
7803 }
7804
Owen Taylor3473f882001-02-23 17:55:21 +00007805 xmlXPathFreeParserContext(ctxt);
7806 return(res);
7807}
7808
7809/**
7810 * xmlXPathEvalExpression:
7811 * @str: the XPath expression
7812 * @ctxt: the XPath context
7813 *
7814 * Evaluate the XPath expression in the given context.
7815 *
7816 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
7817 * the caller has to free the object.
7818 */
7819xmlXPathObjectPtr
7820xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
7821 xmlXPathParserContextPtr pctxt;
7822 xmlXPathObjectPtr res, tmp;
7823 int stack = 0;
7824
7825 xmlXPathInit();
7826
7827 CHECK_CONTEXT(ctxt)
7828
7829 pctxt = xmlXPathNewParserContext(str, ctxt);
7830 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007831
7832 if (*pctxt->cur != 0) {
7833 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7834 res = NULL;
7835 } else {
7836 res = valuePop(pctxt);
7837 }
7838 do {
7839 tmp = valuePop(pctxt);
7840 if (tmp != NULL) {
7841 xmlXPathFreeObject(tmp);
7842 stack++;
7843 }
7844 } while (tmp != NULL);
7845 if ((stack != 0) && (res != NULL)) {
7846 xmlGenericError(xmlGenericErrorContext,
7847 "xmlXPathEvalExpression: %d object left on the stack\n",
7848 stack);
7849 }
7850 xmlXPathFreeParserContext(pctxt);
7851 return(res);
7852}
7853
7854/**
7855 * xmlXPathRegisterAllFunctions:
7856 * @ctxt: the XPath context
7857 *
7858 * Registers all default XPath functions in this context
7859 */
7860void
7861xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
7862{
7863 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
7864 xmlXPathBooleanFunction);
7865 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
7866 xmlXPathCeilingFunction);
7867 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
7868 xmlXPathCountFunction);
7869 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
7870 xmlXPathConcatFunction);
7871 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
7872 xmlXPathContainsFunction);
7873 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
7874 xmlXPathIdFunction);
7875 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
7876 xmlXPathFalseFunction);
7877 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
7878 xmlXPathFloorFunction);
7879 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
7880 xmlXPathLastFunction);
7881 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
7882 xmlXPathLangFunction);
7883 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
7884 xmlXPathLocalNameFunction);
7885 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
7886 xmlXPathNotFunction);
7887 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
7888 xmlXPathNameFunction);
7889 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
7890 xmlXPathNamespaceURIFunction);
7891 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
7892 xmlXPathNormalizeFunction);
7893 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
7894 xmlXPathNumberFunction);
7895 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
7896 xmlXPathPositionFunction);
7897 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
7898 xmlXPathRoundFunction);
7899 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
7900 xmlXPathStringFunction);
7901 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
7902 xmlXPathStringLengthFunction);
7903 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
7904 xmlXPathStartsWithFunction);
7905 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
7906 xmlXPathSubstringFunction);
7907 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
7908 xmlXPathSubstringBeforeFunction);
7909 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
7910 xmlXPathSubstringAfterFunction);
7911 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
7912 xmlXPathSumFunction);
7913 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
7914 xmlXPathTrueFunction);
7915 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
7916 xmlXPathTranslateFunction);
7917}
7918
7919#endif /* LIBXML_XPATH_ENABLED */