blob: 57cdc0aee6191feb585d9b68767fc154d75c2ed9 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * xpath.c: XML Path Language implementation
3 * XPath is a language for addressing parts of an XML document,
4 * designed to be used by both XSLT and XPointer
5 *
6 * Reference: W3C Recommendation 16 November 1999
7 * http://www.w3.org/TR/1999/REC-xpath-19991116
8 * Public reference:
9 * http://www.w3.org/TR/xpath
10 *
11 * See COPYRIGHT for the status of this software
12 *
13 * Author: Daniel.Veillard@w3.org
14 *
15 * 14 Nov 2000 ht - truncated declaration of xmlXPathEvalRelativeLocationPath
16 * for VMS
17 */
18
Bjorn Reese70a9da52001-04-21 16:57:29 +000019#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000020#ifdef LIBXML_XPATH_ENABLED
21
Owen Taylor3473f882001-02-23 17:55:21 +000022#include <string.h>
23
24#ifdef HAVE_SYS_TYPES_H
25#include <sys/types.h>
26#endif
27#ifdef HAVE_MATH_H
28#include <math.h>
29#endif
30#ifdef HAVE_FLOAT_H
31#include <float.h>
32#endif
33#ifdef HAVE_IEEEFP_H
34#include <ieeefp.h>
35#endif
36#ifdef HAVE_NAN_H
37#include <nan.h>
38#endif
39#ifdef HAVE_CTYPE_H
40#include <ctype.h>
41#endif
42
43#include <libxml/xmlmemory.h>
44#include <libxml/tree.h>
45#include <libxml/valid.h>
46#include <libxml/xpath.h>
47#include <libxml/xpathInternals.h>
48#include <libxml/parserInternals.h>
49#include <libxml/hash.h>
50#ifdef LIBXML_XPTR_ENABLED
51#include <libxml/xpointer.h>
52#endif
53#ifdef LIBXML_DEBUG_ENABLED
54#include <libxml/debugXML.h>
55#endif
56#include <libxml/xmlerror.h>
57
58/* #define DEBUG */
59/* #define DEBUG_STEP */
60/* #define DEBUG_EXPR */
61
62void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
63double xmlXPathStringEvalNumber(const xmlChar *str);
64
Daniel Veillard9e7160d2001-03-18 23:17:47 +000065/************************************************************************
66 * *
67 * Floating point stuff *
68 * *
69 ************************************************************************/
70
Owen Taylor3473f882001-02-23 17:55:21 +000071/*
Owen Taylor3473f882001-02-23 17:55:21 +000072 * The lack of portability of this section of the libc is annoying !
73 */
74double xmlXPathNAN = 0;
75double xmlXPathPINF = 1;
76double xmlXPathNINF = -1;
77
78#ifndef isinf
79#ifndef HAVE_ISINF
80
81#if HAVE_FPCLASS
82
83int isinf(double d) {
84 fpclass_t type = fpclass(d);
85 switch (type) {
86 case FP_NINF:
87 return(-1);
88 case FP_PINF:
89 return(1);
90 }
91 return(0);
92}
93
94#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
95
96#if HAVE_FP_CLASS_H
97#include <fp_class.h>
98#endif
99
100int isinf(double d) {
101#if HAVE_FP_CLASS
102 int fpclass = fp_class(d);
103#else
104 int fpclass = fp_class_d(d);
105#endif
106 if (fpclass == FP_POS_INF)
107 return(1);
108 if (fpclass == FP_NEG_INF)
109 return(-1);
110 return(0);
111}
112
113#elif defined(HAVE_CLASS)
114
115int isinf(double d) {
116 int fpclass = class(d);
117 if (fpclass == FP_PLUS_INF)
118 return(1);
119 if (fpclass == FP_MINUS_INF)
120 return(-1);
121 return(0);
122}
123#elif defined(finite) || defined(HAVE_FINITE)
124int isinf(double x) { return !finite(x) && x==x; }
125#elif defined(HUGE_VAL)
126int isinf(double x)
127{
128 if (x == HUGE_VAL)
129 return(1);
130 if (x == -HUGE_VAL)
131 return(-1);
132 return(0);
133}
134#endif
135
136#endif /* ! HAVE_ISINF */
137#endif /* ! defined(isinf) */
138
139#ifndef isnan
140#ifndef HAVE_ISNAN
141
142#ifdef HAVE_ISNAND
143#define isnan(f) isnand(f)
144#endif /* HAVE_iSNAND */
145
146#endif /* ! HAVE_iSNAN */
147#endif /* ! defined(isnan) */
148
149/**
150 * xmlXPathInit:
151 *
152 * Initialize the XPath environment
153 */
154void
155xmlXPathInit(void) {
156 static int initialized = 0;
157
158 if (initialized) return;
159
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000160 xmlXPathNAN = 0.0 / 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +0000161
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000162 xmlXPathPINF = 1 / 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +0000163
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000164 xmlXPathNINF = -1 / 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +0000165
166 initialized = 1;
167}
168
169/************************************************************************
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000170 * *
171 * Parser Types *
172 * *
173 ************************************************************************/
174
175/*
176 * Types are private:
177 */
178
179typedef enum {
180 XPATH_OP_END=0,
181 XPATH_OP_AND,
182 XPATH_OP_OR,
183 XPATH_OP_EQUAL,
184 XPATH_OP_CMP,
185 XPATH_OP_PLUS,
186 XPATH_OP_MULT,
187 XPATH_OP_UNION,
188 XPATH_OP_ROOT,
189 XPATH_OP_NODE,
190 XPATH_OP_RESET,
191 XPATH_OP_COLLECT,
192 XPATH_OP_VALUE,
193 XPATH_OP_VARIABLE,
194 XPATH_OP_FUNCTION,
195 XPATH_OP_ARG,
196 XPATH_OP_PREDICATE,
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000197 XPATH_OP_FILTER,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000198 XPATH_OP_SORT
199#ifdef LIBXML_XPTR_ENABLED
200 ,XPATH_OP_RANGETO
201#endif
202} xmlXPathOp;
203
204typedef enum {
205 AXIS_ANCESTOR = 1,
206 AXIS_ANCESTOR_OR_SELF,
207 AXIS_ATTRIBUTE,
208 AXIS_CHILD,
209 AXIS_DESCENDANT,
210 AXIS_DESCENDANT_OR_SELF,
211 AXIS_FOLLOWING,
212 AXIS_FOLLOWING_SIBLING,
213 AXIS_NAMESPACE,
214 AXIS_PARENT,
215 AXIS_PRECEDING,
216 AXIS_PRECEDING_SIBLING,
217 AXIS_SELF
218} xmlXPathAxisVal;
219
220typedef enum {
221 NODE_TEST_NONE = 0,
222 NODE_TEST_TYPE = 1,
223 NODE_TEST_PI = 2,
224 NODE_TEST_ALL = 3,
225 NODE_TEST_NS = 4,
226 NODE_TEST_NAME = 5
227} xmlXPathTestVal;
228
229typedef enum {
230 NODE_TYPE_NODE = 0,
231 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
232 NODE_TYPE_TEXT = XML_TEXT_NODE,
233 NODE_TYPE_PI = XML_PI_NODE
234} xmlXPathTypeVal;
235
236
237typedef struct _xmlXPathStepOp xmlXPathStepOp;
238typedef xmlXPathStepOp *xmlXPathStepOpPtr;
239struct _xmlXPathStepOp {
240 xmlXPathOp op;
241 int ch1;
242 int ch2;
243 int value;
244 int value2;
245 int value3;
246 void *value4;
247 void *value5;
248};
249
250struct _xmlXPathCompExpr {
251 int nbStep;
252 int maxStep;
253 xmlXPathStepOp *steps; /* ops for computation */
254 int last;
255};
256
257/************************************************************************
258 * *
259 * Parser Type functions *
260 * *
261 ************************************************************************/
262
263/**
264 * xmlXPathNewCompExpr:
265 *
266 * Create a new Xpath component
267 *
268 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
269 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000270static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000271xmlXPathNewCompExpr(void) {
272 xmlXPathCompExprPtr cur;
273
274 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
275 if (cur == NULL) {
276 xmlGenericError(xmlGenericErrorContext,
277 "xmlXPathNewCompExpr : malloc failed\n");
278 return(NULL);
279 }
280 memset(cur, 0, sizeof(xmlXPathCompExpr));
281 cur->maxStep = 10;
282 cur->nbStep = 0;
283 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
284 sizeof(xmlXPathStepOp));
285 if (cur->steps == NULL) {
286 xmlGenericError(xmlGenericErrorContext,
287 "xmlXPathNewCompExpr : malloc failed\n");
288 xmlFree(cur);
289 return(NULL);
290 }
291 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
292 cur->last = -1;
293 return(cur);
294}
295
296/**
297 * xmlXPathFreeCompExpr:
298 * @comp: an XPATH comp
299 *
300 * Free up the memory allocated by @comp
301 */
302void
303xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) {
304 xmlXPathStepOpPtr op;
305 int i;
306
307 if (comp == NULL)
308 return;
309 for (i = 0;i < comp->nbStep;i++) {
310 op = &comp->steps[i];
311 if (op->value4 != NULL) {
312 if (op->op == XPATH_OP_VALUE)
313 xmlXPathFreeObject(op->value4);
314 else
315 xmlFree(op->value4);
316 }
317 if (op->value5 != NULL)
318 xmlFree(op->value5);
319 }
320 if (comp->steps != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000321 xmlFree(comp->steps);
322 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000323 xmlFree(comp);
324}
325
326/**
327 * xmlXPathCompExprAdd:
328 * @comp: the compiled expression
329 * @ch1: first child index
330 * @ch2: second child index
331 * @op: an op
332 * @value: the first int value
333 * @value2: the second int value
334 * @value3: the third int value
335 * @value4: the first string value
336 * @value5: the second string value
337 *
338 * Add an step to an XPath Compiled Expression
339 *
340 * Returns -1 in case of failure, the index otherwise
341 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000342static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000343xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
344 xmlXPathOp op, int value,
345 int value2, int value3, void *value4, void *value5) {
346 if (comp->nbStep >= comp->maxStep) {
347 xmlXPathStepOp *real;
348
349 comp->maxStep *= 2;
350 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
351 comp->maxStep * sizeof(xmlXPathStepOp));
352 if (real == NULL) {
353 comp->maxStep /= 2;
354 xmlGenericError(xmlGenericErrorContext,
355 "xmlXPathCompExprAdd : realloc failed\n");
356 return(-1);
357 }
358 comp->steps = real;
359 }
360 comp->last = comp->nbStep;
361 comp->steps[comp->nbStep].ch1 = ch1;
362 comp->steps[comp->nbStep].ch2 = ch2;
363 comp->steps[comp->nbStep].op = op;
364 comp->steps[comp->nbStep].value = value;
365 comp->steps[comp->nbStep].value2 = value2;
366 comp->steps[comp->nbStep].value3 = value3;
367 comp->steps[comp->nbStep].value4 = value4;
368 comp->steps[comp->nbStep].value5 = value5;
369 return(comp->nbStep++);
370}
371
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000372#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
373 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
374 (op), (val), (val2), (val3), (val4), (val5))
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000375#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
376 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
377 (op), (val), (val2), (val3), (val4), (val5))
378
379#define PUSH_LEAVE_EXPR(op, val, val2) \
380xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
381
382#define PUSH_UNARY_EXPR(op, ch, val, val2) \
383xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
384
385#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
386xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
387
388/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000389 * *
390 * Debugging related functions *
391 * *
392 ************************************************************************/
393
394#define TODO \
395 xmlGenericError(xmlGenericErrorContext, \
396 "Unimplemented block at %s:%d\n", \
397 __FILE__, __LINE__);
398
399#define STRANGE \
400 xmlGenericError(xmlGenericErrorContext, \
401 "Internal error at %s:%d\n", \
402 __FILE__, __LINE__);
403
404#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000405static void
406xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000407 int i;
408 char shift[100];
409
410 for (i = 0;((i < depth) && (i < 25));i++)
411 shift[2 * i] = shift[2 * i + 1] = ' ';
412 shift[2 * i] = shift[2 * i + 1] = 0;
413 if (cur == NULL) {
414 fprintf(output, shift);
415 fprintf(output, "Node is NULL !\n");
416 return;
417
418 }
419
420 if ((cur->type == XML_DOCUMENT_NODE) ||
421 (cur->type == XML_HTML_DOCUMENT_NODE)) {
422 fprintf(output, shift);
423 fprintf(output, " /\n");
424 } else if (cur->type == XML_ATTRIBUTE_NODE)
425 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
426 else
427 xmlDebugDumpOneNode(output, cur, depth);
428}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000429static void
430xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000431 xmlNodePtr tmp;
432 int i;
433 char shift[100];
434
435 for (i = 0;((i < depth) && (i < 25));i++)
436 shift[2 * i] = shift[2 * i + 1] = ' ';
437 shift[2 * i] = shift[2 * i + 1] = 0;
438 if (cur == NULL) {
439 fprintf(output, shift);
440 fprintf(output, "Node is NULL !\n");
441 return;
442
443 }
444
445 while (cur != NULL) {
446 tmp = cur;
447 cur = cur->next;
448 xmlDebugDumpOneNode(output, tmp, depth);
449 }
450}
Owen Taylor3473f882001-02-23 17:55:21 +0000451
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000452static void
453xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000454 int i;
455 char shift[100];
456
457 for (i = 0;((i < depth) && (i < 25));i++)
458 shift[2 * i] = shift[2 * i + 1] = ' ';
459 shift[2 * i] = shift[2 * i + 1] = 0;
460
461 if (cur == NULL) {
462 fprintf(output, shift);
463 fprintf(output, "NodeSet is NULL !\n");
464 return;
465
466 }
467
Daniel Veillard911f49a2001-04-07 15:39:35 +0000468 if (cur != NULL) {
469 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
470 for (i = 0;i < cur->nodeNr;i++) {
471 fprintf(output, shift);
472 fprintf(output, "%d", i + 1);
473 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
474 }
Owen Taylor3473f882001-02-23 17:55:21 +0000475 }
476}
477
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000478static void
479xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000480 int i;
481 char shift[100];
482
483 for (i = 0;((i < depth) && (i < 25));i++)
484 shift[2 * i] = shift[2 * i + 1] = ' ';
485 shift[2 * i] = shift[2 * i + 1] = 0;
486
487 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
488 fprintf(output, shift);
489 fprintf(output, "Value Tree is NULL !\n");
490 return;
491
492 }
493
494 fprintf(output, shift);
495 fprintf(output, "%d", i + 1);
496 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
497}
Owen Taylor3473f882001-02-23 17:55:21 +0000498#if defined(LIBXML_XPTR_ENABLED)
499void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000500static void
501xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000502 int i;
503 char shift[100];
504
505 for (i = 0;((i < depth) && (i < 25));i++)
506 shift[2 * i] = shift[2 * i + 1] = ' ';
507 shift[2 * i] = shift[2 * i + 1] = 0;
508
509 if (cur == NULL) {
510 fprintf(output, shift);
511 fprintf(output, "LocationSet is NULL !\n");
512 return;
513
514 }
515
516 for (i = 0;i < cur->locNr;i++) {
517 fprintf(output, shift);
518 fprintf(output, "%d : ", i + 1);
519 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
520 }
521}
522#endif
523
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000524/**
525 * xmlXPathDebugDumpObject:
526 * @output: the FILE * to dump the output
527 * @cur: the object to inspect
528 * @depth: indentation level
529 *
530 * Dump the content of the object for debugging purposes
531 */
532void
533xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000534 int i;
535 char shift[100];
536
537 for (i = 0;((i < depth) && (i < 25));i++)
538 shift[2 * i] = shift[2 * i + 1] = ' ';
539 shift[2 * i] = shift[2 * i + 1] = 0;
540
541 fprintf(output, shift);
542
543 if (cur == NULL) {
544 fprintf(output, "Object is empty (NULL)\n");
545 return;
546 }
547 switch(cur->type) {
548 case XPATH_UNDEFINED:
549 fprintf(output, "Object is uninitialized\n");
550 break;
551 case XPATH_NODESET:
552 fprintf(output, "Object is a Node Set :\n");
553 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
554 break;
555 case XPATH_XSLT_TREE:
556 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000557 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000558 break;
559 case XPATH_BOOLEAN:
560 fprintf(output, "Object is a Boolean : ");
561 if (cur->boolval) fprintf(output, "true\n");
562 else fprintf(output, "false\n");
563 break;
564 case XPATH_NUMBER:
565 fprintf(output, "Object is a number : %0g\n", cur->floatval);
566 break;
567 case XPATH_STRING:
568 fprintf(output, "Object is a string : ");
569 xmlDebugDumpString(output, cur->stringval);
570 fprintf(output, "\n");
571 break;
572 case XPATH_POINT:
573 fprintf(output, "Object is a point : index %d in node", cur->index);
574 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
575 fprintf(output, "\n");
576 break;
577 case XPATH_RANGE:
578 if ((cur->user2 == NULL) ||
579 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
580 fprintf(output, "Object is a collapsed range :\n");
581 fprintf(output, shift);
582 if (cur->index >= 0)
583 fprintf(output, "index %d in ", cur->index);
584 fprintf(output, "node\n");
585 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
586 depth + 1);
587 } else {
588 fprintf(output, "Object is a range :\n");
589 fprintf(output, shift);
590 fprintf(output, "From ");
591 if (cur->index >= 0)
592 fprintf(output, "index %d in ", cur->index);
593 fprintf(output, "node\n");
594 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
595 depth + 1);
596 fprintf(output, shift);
597 fprintf(output, "To ");
598 if (cur->index2 >= 0)
599 fprintf(output, "index %d in ", cur->index2);
600 fprintf(output, "node\n");
601 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
602 depth + 1);
603 fprintf(output, "\n");
604 }
605 break;
606 case XPATH_LOCATIONSET:
607#if defined(LIBXML_XPTR_ENABLED)
608 fprintf(output, "Object is a Location Set:\n");
609 xmlXPathDebugDumpLocationSet(output,
610 (xmlLocationSetPtr) cur->user, depth);
611#endif
612 break;
613 case XPATH_USERS:
614 fprintf(output, "Object is user defined\n");
615 break;
616 }
617}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000618
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000619static void
620xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000621 xmlXPathStepOpPtr op, int depth) {
622 int i;
623 char shift[100];
624
625 for (i = 0;((i < depth) && (i < 25));i++)
626 shift[2 * i] = shift[2 * i + 1] = ' ';
627 shift[2 * i] = shift[2 * i + 1] = 0;
628
629 fprintf(output, shift);
630 if (op == NULL) {
631 fprintf(output, "Step is NULL\n");
632 return;
633 }
634 switch (op->op) {
635 case XPATH_OP_END:
636 fprintf(output, "END"); break;
637 case XPATH_OP_AND:
638 fprintf(output, "AND"); break;
639 case XPATH_OP_OR:
640 fprintf(output, "OR"); break;
641 case XPATH_OP_EQUAL:
642 if (op->value)
643 fprintf(output, "EQUAL =");
644 else
645 fprintf(output, "EQUAL !=");
646 break;
647 case XPATH_OP_CMP:
648 if (op->value)
649 fprintf(output, "CMP <");
650 else
651 fprintf(output, "CMP >");
652 if (!op->value2)
653 fprintf(output, "=");
654 break;
655 case XPATH_OP_PLUS:
656 if (op->value == 0)
657 fprintf(output, "PLUS -");
658 else if (op->value == 1)
659 fprintf(output, "PLUS +");
660 else if (op->value == 2)
661 fprintf(output, "PLUS unary -");
662 else if (op->value == 3)
663 fprintf(output, "PLUS unary - -");
664 break;
665 case XPATH_OP_MULT:
666 if (op->value == 0)
667 fprintf(output, "MULT *");
668 else if (op->value == 1)
669 fprintf(output, "MULT div");
670 else
671 fprintf(output, "MULT mod");
672 break;
673 case XPATH_OP_UNION:
674 fprintf(output, "UNION"); break;
675 case XPATH_OP_ROOT:
676 fprintf(output, "ROOT"); break;
677 case XPATH_OP_NODE:
678 fprintf(output, "NODE"); break;
679 case XPATH_OP_RESET:
680 fprintf(output, "RESET"); break;
681 case XPATH_OP_SORT:
682 fprintf(output, "SORT"); break;
683 case XPATH_OP_COLLECT: {
684 xmlXPathAxisVal axis = op->value;
685 xmlXPathTestVal test = op->value2;
686 xmlXPathTypeVal type = op->value3;
687 const xmlChar *prefix = op->value4;
688 const xmlChar *name = op->value5;
689
690 fprintf(output, "COLLECT ");
691 switch (axis) {
692 case AXIS_ANCESTOR:
693 fprintf(output, " 'ancestors' "); break;
694 case AXIS_ANCESTOR_OR_SELF:
695 fprintf(output, " 'ancestors-or-self' "); break;
696 case AXIS_ATTRIBUTE:
697 fprintf(output, " 'attributes' "); break;
698 case AXIS_CHILD:
699 fprintf(output, " 'child' "); break;
700 case AXIS_DESCENDANT:
701 fprintf(output, " 'descendant' "); break;
702 case AXIS_DESCENDANT_OR_SELF:
703 fprintf(output, " 'descendant-or-self' "); break;
704 case AXIS_FOLLOWING:
705 fprintf(output, " 'following' "); break;
706 case AXIS_FOLLOWING_SIBLING:
707 fprintf(output, " 'following-siblings' "); break;
708 case AXIS_NAMESPACE:
709 fprintf(output, " 'namespace' "); break;
710 case AXIS_PARENT:
711 fprintf(output, " 'parent' "); break;
712 case AXIS_PRECEDING:
713 fprintf(output, " 'preceding' "); break;
714 case AXIS_PRECEDING_SIBLING:
715 fprintf(output, " 'preceding-sibling' "); break;
716 case AXIS_SELF:
717 fprintf(output, " 'self' "); break;
718 }
719 switch (test) {
720 case NODE_TEST_NONE:
721 fprintf(output, "'none' "); break;
722 case NODE_TEST_TYPE:
723 fprintf(output, "'type' "); break;
724 case NODE_TEST_PI:
725 fprintf(output, "'PI' "); break;
726 case NODE_TEST_ALL:
727 fprintf(output, "'all' "); break;
728 case NODE_TEST_NS:
729 fprintf(output, "'namespace' "); break;
730 case NODE_TEST_NAME:
731 fprintf(output, "'name' "); break;
732 }
733 switch (type) {
734 case NODE_TYPE_NODE:
735 fprintf(output, "'node' "); break;
736 case NODE_TYPE_COMMENT:
737 fprintf(output, "'comment' "); break;
738 case NODE_TYPE_TEXT:
739 fprintf(output, "'text' "); break;
740 case NODE_TYPE_PI:
741 fprintf(output, "'PI' "); break;
742 }
743 if (prefix != NULL)
744 fprintf(output, "%s:", prefix);
745 if (name != NULL)
746 fprintf(output, "%s", name);
747 break;
748
749 }
750 case XPATH_OP_VALUE: {
751 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
752
753 fprintf(output, "ELEM ");
754 xmlXPathDebugDumpObject(output, object, 0);
755 goto finish;
756 }
757 case XPATH_OP_VARIABLE: {
758 const xmlChar *prefix = op->value5;
759 const xmlChar *name = op->value4;
760
761 if (prefix != NULL)
762 fprintf(output, "VARIABLE %s:%s", prefix, name);
763 else
764 fprintf(output, "VARIABLE %s", name);
765 break;
766 }
767 case XPATH_OP_FUNCTION: {
768 int nbargs = op->value;
769 const xmlChar *prefix = op->value5;
770 const xmlChar *name = op->value4;
771
772 if (prefix != NULL)
773 fprintf(output, "FUNCTION %s:%s(%d args)",
774 prefix, name, nbargs);
775 else
776 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
777 break;
778 }
779 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
780 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000781 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +0000782#ifdef LIBXML_XPTR_ENABLED
783 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
784#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000785 default:
786 fprintf(output, "UNKNOWN %d\n", op->op); return;
787 }
788 fprintf(output, "\n");
789finish:
790 if (op->ch1 >= 0)
791 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
792 if (op->ch2 >= 0)
793 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
794}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000795
796void
797xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
798 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000799 int i;
800 char shift[100];
801
802 for (i = 0;((i < depth) && (i < 25));i++)
803 shift[2 * i] = shift[2 * i + 1] = ' ';
804 shift[2 * i] = shift[2 * i + 1] = 0;
805
806 fprintf(output, shift);
807
808 if (comp == NULL) {
809 fprintf(output, "Compiled Expression is NULL\n");
810 return;
811 }
812 fprintf(output, "Compiled Expression : %d elements\n",
813 comp->nbStep);
814 i = comp->last;
815 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
816}
Owen Taylor3473f882001-02-23 17:55:21 +0000817#endif
818
819/************************************************************************
820 * *
821 * Parser stacks related functions and macros *
822 * *
823 ************************************************************************/
824
825/*
826 * Generic function for accessing stacks in the Parser Context
827 */
828
829#define PUSH_AND_POP(type, name) \
830extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
831 if (ctxt->name##Nr >= ctxt->name##Max) { \
832 ctxt->name##Max *= 2; \
833 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
834 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
835 if (ctxt->name##Tab == NULL) { \
836 xmlGenericError(xmlGenericErrorContext, \
837 "realloc failed !\n"); \
838 return(0); \
839 } \
840 } \
841 ctxt->name##Tab[ctxt->name##Nr] = value; \
842 ctxt->name = value; \
843 return(ctxt->name##Nr++); \
844} \
845extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
846 type ret; \
847 if (ctxt->name##Nr <= 0) return(0); \
848 ctxt->name##Nr--; \
849 if (ctxt->name##Nr > 0) \
850 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
851 else \
852 ctxt->name = NULL; \
853 ret = ctxt->name##Tab[ctxt->name##Nr]; \
854 ctxt->name##Tab[ctxt->name##Nr] = 0; \
855 return(ret); \
856} \
857
858PUSH_AND_POP(xmlXPathObjectPtr, value)
859
860/*
861 * Macros for accessing the content. Those should be used only by the parser,
862 * and not exported.
863 *
864 * Dirty macros, i.e. one need to make assumption on the context to use them
865 *
866 * CUR_PTR return the current pointer to the xmlChar to be parsed.
867 * CUR returns the current xmlChar value, i.e. a 8 bit value
868 * in ISO-Latin or UTF-8.
869 * This should be used internally by the parser
870 * only to compare to ASCII values otherwise it would break when
871 * running with UTF-8 encoding.
872 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
873 * to compare on ASCII based substring.
874 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
875 * strings within the parser.
876 * CURRENT Returns the current char value, with the full decoding of
877 * UTF-8 if we are using this mode. It returns an int.
878 * NEXT Skip to the next character, this does the proper decoding
879 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
880 * It returns the pointer to the current xmlChar.
881 */
882
883#define CUR (*ctxt->cur)
884#define SKIP(val) ctxt->cur += (val)
885#define NXT(val) ctxt->cur[(val)]
886#define CUR_PTR ctxt->cur
887
888#define SKIP_BLANKS \
889 while (IS_BLANK(*(ctxt->cur))) NEXT
890
891#define CURRENT (*ctxt->cur)
892#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
893
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000894
895#ifndef DBL_DIG
896#define DBL_DIG 16
897#endif
898#ifndef DBL_EPSILON
899#define DBL_EPSILON 1E-9
900#endif
901
902#define UPPER_DOUBLE 1E9
903#define LOWER_DOUBLE 1E-5
904
905#define INTEGER_DIGITS DBL_DIG
906#define FRACTION_DIGITS (DBL_DIG + 1)
907#define EXPONENT_DIGITS (3 + 2)
908
909/**
910 * xmlXPathFormatNumber:
911 * @number: number to format
912 * @buffer: output buffer
913 * @buffersize: size of output buffer
914 *
915 * Convert the number into a string representation.
916 */
917static void
918xmlXPathFormatNumber(double number, char buffer[], int buffersize)
919{
920 switch (isinf(number)) {
921 case 1:
922 if (buffersize > (int)sizeof("+Infinity"))
923 sprintf(buffer, "+Infinity");
924 break;
925 case -1:
926 if (buffersize > (int)sizeof("-Infinity"))
927 sprintf(buffer, "-Infinity");
928 break;
929 default:
930 if (isnan(number)) {
931 if (buffersize > (int)sizeof("NaN"))
932 sprintf(buffer, "NaN");
933 } else {
Bjorn Reese70a9da52001-04-21 16:57:29 +0000934 /* 3 is sign, decimal point, and terminating zero */
935 char work[DBL_DIG + EXPONENT_DIGITS + 3];
936 int integer_place, fraction_place;
937 char *ptr;
938 char *after_fraction;
939 double absolute_value;
940 int size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000941
Bjorn Reese70a9da52001-04-21 16:57:29 +0000942 absolute_value = fabs(number);
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000943
Bjorn Reese70a9da52001-04-21 16:57:29 +0000944 /*
945 * First choose format - scientific or regular floating point.
946 * In either case, result is in work, and after_fraction points
947 * just past the fractional part.
948 */
949 if ( ((absolute_value > UPPER_DOUBLE) ||
950 (absolute_value < LOWER_DOUBLE)) &&
951 (absolute_value != 0.0) ) {
952 /* Use scientific notation */
953 integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
954 fraction_place = DBL_DIG - 1;
955 snprintf(work, sizeof(work),"%*.*e",
956 integer_place, fraction_place, number);
957 after_fraction = strchr(work + DBL_DIG, 'e');
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000958 }
Bjorn Reese70a9da52001-04-21 16:57:29 +0000959 else {
960 /* Use regular notation */
961 integer_place = 1 + (int)log10(absolute_value);
962 fraction_place = (integer_place > 0)
963 ? DBL_DIG - integer_place
964 : DBL_DIG;
965 size = snprintf(work, sizeof(work), "%0.*f",
966 fraction_place, number);
967 after_fraction = work + size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000968 }
969
Bjorn Reese70a9da52001-04-21 16:57:29 +0000970 /* Remove fractional trailing zeroes */
971 ptr = after_fraction;
972 while (*(--ptr) == '0')
973 ;
974 if (*ptr != '.')
975 ptr++;
976 strcpy(ptr, after_fraction);
977
978 /* Finally copy result back to caller */
979 size = strlen(work) + 1;
980 if (size > buffersize) {
981 work[buffersize - 1] = 0;
982 size = buffersize;
983 }
984 memcpy(buffer, work, size);
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000985 }
986 break;
987 }
988}
989
Owen Taylor3473f882001-02-23 17:55:21 +0000990/************************************************************************
991 * *
992 * Error handling routines *
993 * *
994 ************************************************************************/
995
996
997const char *xmlXPathErrorMessages[] = {
998 "Ok",
999 "Number encoding",
1000 "Unfinished litteral",
1001 "Start of litteral",
1002 "Expected $ for variable reference",
1003 "Undefined variable",
1004 "Invalid predicate",
1005 "Invalid expression",
1006 "Missing closing curly brace",
1007 "Unregistered function",
1008 "Invalid operand",
1009 "Invalid type",
1010 "Invalid number of arguments",
1011 "Invalid context size",
1012 "Invalid context position",
1013 "Memory allocation error",
1014 "Syntax error",
1015 "Resource error",
1016 "Sub resource error",
1017 "Undefined namespace prefix"
1018};
1019
1020/**
1021 * xmlXPathError:
1022 * @ctxt: the XPath Parser context
1023 * @file: the file name
1024 * @line: the line number
1025 * @no: the error number
1026 *
1027 * Create a new xmlNodeSetPtr of type double and of value @val
1028 *
1029 * Returns the newly created object.
1030 */
1031void
1032xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1033 int line, int no) {
1034 int n;
1035 const xmlChar *cur;
1036 const xmlChar *base;
1037
1038 xmlGenericError(xmlGenericErrorContext,
1039 "Error %s:%d: %s\n", file, line,
1040 xmlXPathErrorMessages[no]);
1041
1042 cur = ctxt->cur;
1043 base = ctxt->base;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001044 if ((cur == NULL) || (base == NULL))
1045 return;
1046
Owen Taylor3473f882001-02-23 17:55:21 +00001047 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1048 cur--;
1049 }
1050 n = 0;
1051 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1052 cur--;
1053 if ((*cur == '\n') || (*cur == '\r')) cur++;
1054 base = cur;
1055 n = 0;
1056 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1057 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1058 n++;
1059 }
1060 xmlGenericError(xmlGenericErrorContext, "\n");
1061 cur = ctxt->cur;
1062 while ((*cur == '\n') || (*cur == '\r'))
1063 cur--;
1064 n = 0;
1065 while ((cur != base) && (n++ < 80)) {
1066 xmlGenericError(xmlGenericErrorContext, " ");
1067 base++;
1068 }
1069 xmlGenericError(xmlGenericErrorContext,"^\n");
1070}
1071
1072
1073/************************************************************************
1074 * *
1075 * Routines to handle NodeSets *
1076 * *
1077 ************************************************************************/
1078
1079/**
1080 * xmlXPathCmpNodes:
1081 * @node1: the first node
1082 * @node2: the second node
1083 *
1084 * Compare two nodes w.r.t document order
1085 *
1086 * Returns -2 in case of error 1 if first point < second point, 0 if
1087 * that's the same node, -1 otherwise
1088 */
1089int
1090xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1091 int depth1, depth2;
1092 xmlNodePtr cur, root;
1093
1094 if ((node1 == NULL) || (node2 == NULL))
1095 return(-2);
1096 /*
1097 * a couple of optimizations which will avoid computations in most cases
1098 */
1099 if (node1 == node2)
1100 return(0);
Daniel Veillardb33c2012001-04-25 12:59:04 +00001101 if ((node1->type == XML_NAMESPACE_DECL) ||
1102 (node2->type == XML_NAMESPACE_DECL))
1103 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00001104 if (node1 == node2->prev)
1105 return(1);
1106 if (node1 == node2->next)
1107 return(-1);
1108
1109 /*
1110 * compute depth to root
1111 */
1112 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1113 if (cur == node1)
1114 return(1);
1115 depth2++;
1116 }
1117 root = cur;
1118 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1119 if (cur == node2)
1120 return(-1);
1121 depth1++;
1122 }
1123 /*
1124 * Distinct document (or distinct entities :-( ) case.
1125 */
1126 if (root != cur) {
1127 return(-2);
1128 }
1129 /*
1130 * get the nearest common ancestor.
1131 */
1132 while (depth1 > depth2) {
1133 depth1--;
1134 node1 = node1->parent;
1135 }
1136 while (depth2 > depth1) {
1137 depth2--;
1138 node2 = node2->parent;
1139 }
1140 while (node1->parent != node2->parent) {
1141 node1 = node1->parent;
1142 node2 = node2->parent;
1143 /* should not happen but just in case ... */
1144 if ((node1 == NULL) || (node2 == NULL))
1145 return(-2);
1146 }
1147 /*
1148 * Find who's first.
1149 */
1150 if (node1 == node2->next)
1151 return(-1);
1152 for (cur = node1->next;cur != NULL;cur = cur->next)
1153 if (cur == node2)
1154 return(1);
1155 return(-1); /* assume there is no sibling list corruption */
1156}
1157
1158/**
1159 * xmlXPathNodeSetSort:
1160 * @set: the node set
1161 *
1162 * Sort the node set in document order
1163 */
1164void
1165xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001166 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001167 xmlNodePtr tmp;
1168
1169 if (set == NULL)
1170 return;
1171
1172 /* Use Shell's sort to sort the node-set */
1173 len = set->nodeNr;
1174 for (incr = len / 2; incr > 0; incr /= 2) {
1175 for (i = incr; i < len; i++) {
1176 j = i - incr;
1177 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001178 if (xmlXPathCmpNodes(set->nodeTab[j],
1179 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001180 tmp = set->nodeTab[j];
1181 set->nodeTab[j] = set->nodeTab[j + incr];
1182 set->nodeTab[j + incr] = tmp;
1183 j -= incr;
1184 } else
1185 break;
1186 }
1187 }
1188 }
1189}
1190
1191#define XML_NODESET_DEFAULT 10
1192/**
1193 * xmlXPathNodeSetCreate:
1194 * @val: an initial xmlNodePtr, or NULL
1195 *
1196 * Create a new xmlNodeSetPtr of type double and of value @val
1197 *
1198 * Returns the newly created object.
1199 */
1200xmlNodeSetPtr
1201xmlXPathNodeSetCreate(xmlNodePtr val) {
1202 xmlNodeSetPtr ret;
1203
1204 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1205 if (ret == NULL) {
1206 xmlGenericError(xmlGenericErrorContext,
1207 "xmlXPathNewNodeSet: out of memory\n");
1208 return(NULL);
1209 }
1210 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1211 if (val != NULL) {
1212 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1213 sizeof(xmlNodePtr));
1214 if (ret->nodeTab == NULL) {
1215 xmlGenericError(xmlGenericErrorContext,
1216 "xmlXPathNewNodeSet: out of memory\n");
1217 return(NULL);
1218 }
1219 memset(ret->nodeTab, 0 ,
1220 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1221 ret->nodeMax = XML_NODESET_DEFAULT;
1222 ret->nodeTab[ret->nodeNr++] = val;
1223 }
1224 return(ret);
1225}
1226
1227/**
1228 * xmlXPathNodeSetAdd:
1229 * @cur: the initial node set
1230 * @val: a new xmlNodePtr
1231 *
1232 * add a new xmlNodePtr ot an existing NodeSet
1233 */
1234void
1235xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1236 int i;
1237
1238 if (val == NULL) return;
1239
1240 /*
1241 * check against doublons
1242 */
1243 for (i = 0;i < cur->nodeNr;i++)
1244 if (cur->nodeTab[i] == val) return;
1245
1246 /*
1247 * grow the nodeTab if needed
1248 */
1249 if (cur->nodeMax == 0) {
1250 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1251 sizeof(xmlNodePtr));
1252 if (cur->nodeTab == NULL) {
1253 xmlGenericError(xmlGenericErrorContext,
1254 "xmlXPathNodeSetAdd: out of memory\n");
1255 return;
1256 }
1257 memset(cur->nodeTab, 0 ,
1258 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1259 cur->nodeMax = XML_NODESET_DEFAULT;
1260 } else if (cur->nodeNr == cur->nodeMax) {
1261 xmlNodePtr *temp;
1262
1263 cur->nodeMax *= 2;
1264 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1265 sizeof(xmlNodePtr));
1266 if (temp == NULL) {
1267 xmlGenericError(xmlGenericErrorContext,
1268 "xmlXPathNodeSetAdd: out of memory\n");
1269 return;
1270 }
1271 cur->nodeTab = temp;
1272 }
1273 cur->nodeTab[cur->nodeNr++] = val;
1274}
1275
1276/**
1277 * xmlXPathNodeSetAddUnique:
1278 * @cur: the initial node set
1279 * @val: a new xmlNodePtr
1280 *
1281 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1282 * when we are sure the node is not already in the set.
1283 */
1284void
1285xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1286 if (val == NULL) return;
1287
1288 /*
1289 * grow the nodeTab if needed
1290 */
1291 if (cur->nodeMax == 0) {
1292 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1293 sizeof(xmlNodePtr));
1294 if (cur->nodeTab == NULL) {
1295 xmlGenericError(xmlGenericErrorContext,
1296 "xmlXPathNodeSetAddUnique: out of memory\n");
1297 return;
1298 }
1299 memset(cur->nodeTab, 0 ,
1300 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1301 cur->nodeMax = XML_NODESET_DEFAULT;
1302 } else if (cur->nodeNr == cur->nodeMax) {
1303 xmlNodePtr *temp;
1304
1305 cur->nodeMax *= 2;
1306 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1307 sizeof(xmlNodePtr));
1308 if (temp == NULL) {
1309 xmlGenericError(xmlGenericErrorContext,
1310 "xmlXPathNodeSetAddUnique: out of memory\n");
1311 return;
1312 }
1313 cur->nodeTab = temp;
1314 }
1315 cur->nodeTab[cur->nodeNr++] = val;
1316}
1317
1318/**
1319 * xmlXPathNodeSetMerge:
1320 * @val1: the first NodeSet or NULL
1321 * @val2: the second NodeSet
1322 *
1323 * Merges two nodesets, all nodes from @val2 are added to @val1
1324 * if @val1 is NULL, a new set is created and copied from @val2
1325 *
1326 * Returns val1 once extended or NULL in case of error.
1327 */
1328xmlNodeSetPtr
1329xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001330 int i, j, initNr, skip;
Owen Taylor3473f882001-02-23 17:55:21 +00001331
1332 if (val2 == NULL) return(val1);
1333 if (val1 == NULL) {
1334 val1 = xmlXPathNodeSetCreate(NULL);
1335 }
1336
1337 initNr = val1->nodeNr;
1338
1339 for (i = 0;i < val2->nodeNr;i++) {
1340 /*
1341 * check against doublons
1342 */
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001343 skip = 0;
1344 for (j = 0; j < initNr; j++) {
1345 if (val1->nodeTab[j] == val2->nodeTab[i]) {
1346 skip = 1;
1347 break;
1348 }
1349 }
1350 if (skip)
1351 continue;
Owen Taylor3473f882001-02-23 17:55:21 +00001352
1353 /*
1354 * grow the nodeTab if needed
1355 */
1356 if (val1->nodeMax == 0) {
1357 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1358 sizeof(xmlNodePtr));
1359 if (val1->nodeTab == NULL) {
1360 xmlGenericError(xmlGenericErrorContext,
1361 "xmlXPathNodeSetMerge: out of memory\n");
1362 return(NULL);
1363 }
1364 memset(val1->nodeTab, 0 ,
1365 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1366 val1->nodeMax = XML_NODESET_DEFAULT;
1367 } else if (val1->nodeNr == val1->nodeMax) {
1368 xmlNodePtr *temp;
1369
1370 val1->nodeMax *= 2;
1371 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1372 sizeof(xmlNodePtr));
1373 if (temp == NULL) {
1374 xmlGenericError(xmlGenericErrorContext,
1375 "xmlXPathNodeSetMerge: out of memory\n");
1376 return(NULL);
1377 }
1378 val1->nodeTab = temp;
1379 }
1380 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1381 }
1382
1383 return(val1);
1384}
1385
1386/**
1387 * xmlXPathNodeSetDel:
1388 * @cur: the initial node set
1389 * @val: an xmlNodePtr
1390 *
1391 * Removes an xmlNodePtr from an existing NodeSet
1392 */
1393void
1394xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1395 int i;
1396
1397 if (cur == NULL) return;
1398 if (val == NULL) return;
1399
1400 /*
1401 * check against doublons
1402 */
1403 for (i = 0;i < cur->nodeNr;i++)
1404 if (cur->nodeTab[i] == val) break;
1405
1406 if (i >= cur->nodeNr) {
1407#ifdef DEBUG
1408 xmlGenericError(xmlGenericErrorContext,
1409 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1410 val->name);
1411#endif
1412 return;
1413 }
1414 cur->nodeNr--;
1415 for (;i < cur->nodeNr;i++)
1416 cur->nodeTab[i] = cur->nodeTab[i + 1];
1417 cur->nodeTab[cur->nodeNr] = NULL;
1418}
1419
1420/**
1421 * xmlXPathNodeSetRemove:
1422 * @cur: the initial node set
1423 * @val: the index to remove
1424 *
1425 * Removes an entry from an existing NodeSet list.
1426 */
1427void
1428xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1429 if (cur == NULL) return;
1430 if (val >= cur->nodeNr) return;
1431 cur->nodeNr--;
1432 for (;val < cur->nodeNr;val++)
1433 cur->nodeTab[val] = cur->nodeTab[val + 1];
1434 cur->nodeTab[cur->nodeNr] = NULL;
1435}
1436
1437/**
1438 * xmlXPathFreeNodeSet:
1439 * @obj: the xmlNodeSetPtr to free
1440 *
1441 * Free the NodeSet compound (not the actual nodes !).
1442 */
1443void
1444xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1445 if (obj == NULL) return;
1446 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001447 xmlFree(obj->nodeTab);
1448 }
Owen Taylor3473f882001-02-23 17:55:21 +00001449 xmlFree(obj);
1450}
1451
1452/**
1453 * xmlXPathFreeValueTree:
1454 * @obj: the xmlNodeSetPtr to free
1455 *
1456 * Free the NodeSet compound and the actual tree, this is different
1457 * from xmlXPathFreeNodeSet()
1458 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001459static void
Owen Taylor3473f882001-02-23 17:55:21 +00001460xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1461 int i;
1462
1463 if (obj == NULL) return;
1464 for (i = 0;i < obj->nodeNr;i++)
1465 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001466 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001467
1468 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001469 xmlFree(obj->nodeTab);
1470 }
Owen Taylor3473f882001-02-23 17:55:21 +00001471 xmlFree(obj);
1472}
1473
1474#if defined(DEBUG) || defined(DEBUG_STEP)
1475/**
1476 * xmlGenericErrorContextNodeSet:
1477 * @output: a FILE * for the output
1478 * @obj: the xmlNodeSetPtr to free
1479 *
1480 * Quick display of a NodeSet
1481 */
1482void
1483xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1484 int i;
1485
1486 if (output == NULL) output = xmlGenericErrorContext;
1487 if (obj == NULL) {
1488 fprintf(output, "NodeSet == NULL !\n");
1489 return;
1490 }
1491 if (obj->nodeNr == 0) {
1492 fprintf(output, "NodeSet is empty\n");
1493 return;
1494 }
1495 if (obj->nodeTab == NULL) {
1496 fprintf(output, " nodeTab == NULL !\n");
1497 return;
1498 }
1499 for (i = 0; i < obj->nodeNr; i++) {
1500 if (obj->nodeTab[i] == NULL) {
1501 fprintf(output, " NULL !\n");
1502 return;
1503 }
1504 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1505 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1506 fprintf(output, " /");
1507 else if (obj->nodeTab[i]->name == NULL)
1508 fprintf(output, " noname!");
1509 else fprintf(output, " %s", obj->nodeTab[i]->name);
1510 }
1511 fprintf(output, "\n");
1512}
1513#endif
1514
1515/**
1516 * xmlXPathNewNodeSet:
1517 * @val: the NodePtr value
1518 *
1519 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1520 * it with the single Node @val
1521 *
1522 * Returns the newly created object.
1523 */
1524xmlXPathObjectPtr
1525xmlXPathNewNodeSet(xmlNodePtr val) {
1526 xmlXPathObjectPtr ret;
1527
1528 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1529 if (ret == NULL) {
1530 xmlGenericError(xmlGenericErrorContext,
1531 "xmlXPathNewNodeSet: out of memory\n");
1532 return(NULL);
1533 }
1534 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1535 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001536 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001537 ret->nodesetval = xmlXPathNodeSetCreate(val);
1538 return(ret);
1539}
1540
1541/**
1542 * xmlXPathNewValueTree:
1543 * @val: the NodePtr value
1544 *
1545 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1546 * it with the tree root @val
1547 *
1548 * Returns the newly created object.
1549 */
1550xmlXPathObjectPtr
1551xmlXPathNewValueTree(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_XSLT_TREE;
1562 ret->nodesetval = xmlXPathNodeSetCreate(val);
1563 return(ret);
1564}
1565
1566/**
1567 * xmlXPathNewNodeSetList:
1568 * @val: an existing NodeSet
1569 *
1570 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1571 * it with the Nodeset @val
1572 *
1573 * Returns the newly created object.
1574 */
1575xmlXPathObjectPtr
1576xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1577 xmlXPathObjectPtr ret;
1578 int i;
1579
1580 if (val == NULL)
1581 ret = NULL;
1582 else if (val->nodeTab == NULL)
1583 ret = xmlXPathNewNodeSet(NULL);
1584 else
1585 {
1586 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1587 for (i = 1; i < val->nodeNr; ++i)
1588 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1589 }
1590
1591 return(ret);
1592}
1593
1594/**
1595 * xmlXPathWrapNodeSet:
1596 * @val: the NodePtr value
1597 *
1598 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1599 *
1600 * Returns the newly created object.
1601 */
1602xmlXPathObjectPtr
1603xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1604 xmlXPathObjectPtr ret;
1605
1606 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1607 if (ret == NULL) {
1608 xmlGenericError(xmlGenericErrorContext,
1609 "xmlXPathWrapNodeSet: out of memory\n");
1610 return(NULL);
1611 }
1612 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1613 ret->type = XPATH_NODESET;
1614 ret->nodesetval = val;
1615 return(ret);
1616}
1617
1618/**
1619 * xmlXPathFreeNodeSetList:
1620 * @obj: an existing NodeSetList object
1621 *
1622 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1623 * the list contrary to xmlXPathFreeObject().
1624 */
1625void
1626xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1627 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001628 xmlFree(obj);
1629}
1630
1631/************************************************************************
1632 * *
1633 * Routines to handle extra functions *
1634 * *
1635 ************************************************************************/
1636
1637/**
1638 * xmlXPathRegisterFunc:
1639 * @ctxt: the XPath context
1640 * @name: the function name
1641 * @f: the function implementation or NULL
1642 *
1643 * Register a new function. If @f is NULL it unregisters the function
1644 *
1645 * Returns 0 in case of success, -1 in case of error
1646 */
1647int
1648xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
1649 xmlXPathFunction f) {
1650 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
1651}
1652
1653/**
1654 * xmlXPathRegisterFuncNS:
1655 * @ctxt: the XPath context
1656 * @name: the function name
1657 * @ns_uri: the function namespace URI
1658 * @f: the function implementation or NULL
1659 *
1660 * Register a new function. If @f is NULL it unregisters the function
1661 *
1662 * Returns 0 in case of success, -1 in case of error
1663 */
1664int
1665xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1666 const xmlChar *ns_uri, xmlXPathFunction f) {
1667 if (ctxt == NULL)
1668 return(-1);
1669 if (name == NULL)
1670 return(-1);
1671
1672 if (ctxt->funcHash == NULL)
1673 ctxt->funcHash = xmlHashCreate(0);
1674 if (ctxt->funcHash == NULL)
1675 return(-1);
1676 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
1677}
1678
1679/**
1680 * xmlXPathFunctionLookup:
1681 * @ctxt: the XPath context
1682 * @name: the function name
1683 *
1684 * Search in the Function array of the context for the given
1685 * function.
1686 *
1687 * Returns the xmlXPathFunction or NULL if not found
1688 */
1689xmlXPathFunction
1690xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1691 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
1692}
1693
1694/**
1695 * xmlXPathFunctionLookupNS:
1696 * @ctxt: the XPath context
1697 * @name: the function name
1698 * @ns_uri: the function namespace URI
1699 *
1700 * Search in the Function array of the context for the given
1701 * function.
1702 *
1703 * Returns the xmlXPathFunction or NULL if not found
1704 */
1705xmlXPathFunction
1706xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1707 const xmlChar *ns_uri) {
1708 if (ctxt == NULL)
1709 return(NULL);
1710 if (ctxt->funcHash == NULL)
1711 return(NULL);
1712 if (name == NULL)
1713 return(NULL);
1714
1715 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
1716}
1717
1718/**
1719 * xmlXPathRegisteredFuncsCleanup:
1720 * @ctxt: the XPath context
1721 *
1722 * Cleanup the XPath context data associated to registered functions
1723 */
1724void
1725xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
1726 if (ctxt == NULL)
1727 return;
1728
1729 xmlHashFree(ctxt->funcHash, NULL);
1730 ctxt->funcHash = NULL;
1731}
1732
1733/************************************************************************
1734 * *
1735 * Routines to handle Variable *
1736 * *
1737 ************************************************************************/
1738
1739/**
1740 * xmlXPathRegisterVariable:
1741 * @ctxt: the XPath context
1742 * @name: the variable name
1743 * @value: the variable value or NULL
1744 *
1745 * Register a new variable value. If @value is NULL it unregisters
1746 * the variable
1747 *
1748 * Returns 0 in case of success, -1 in case of error
1749 */
1750int
1751xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
1752 xmlXPathObjectPtr value) {
1753 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
1754}
1755
1756/**
1757 * xmlXPathRegisterVariableNS:
1758 * @ctxt: the XPath context
1759 * @name: the variable name
1760 * @ns_uri: the variable namespace URI
1761 * @value: the variable value or NULL
1762 *
1763 * Register a new variable value. If @value is NULL it unregisters
1764 * the variable
1765 *
1766 * Returns 0 in case of success, -1 in case of error
1767 */
1768int
1769xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1770 const xmlChar *ns_uri,
1771 xmlXPathObjectPtr value) {
1772 if (ctxt == NULL)
1773 return(-1);
1774 if (name == NULL)
1775 return(-1);
1776
1777 if (ctxt->varHash == NULL)
1778 ctxt->varHash = xmlHashCreate(0);
1779 if (ctxt->varHash == NULL)
1780 return(-1);
1781 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
1782 (void *) value,
1783 (xmlHashDeallocator)xmlXPathFreeObject));
1784}
1785
1786/**
1787 * xmlXPathRegisterVariableLookup:
1788 * @ctxt: the XPath context
1789 * @f: the lookup function
1790 * @data: the lookup data
1791 *
1792 * register an external mechanism to do variable lookup
1793 */
1794void
1795xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
1796 xmlXPathVariableLookupFunc f, void *data) {
1797 if (ctxt == NULL)
1798 return;
1799 ctxt->varLookupFunc = (void *) f;
1800 ctxt->varLookupData = data;
1801}
1802
1803/**
1804 * xmlXPathVariableLookup:
1805 * @ctxt: the XPath context
1806 * @name: the variable name
1807 *
1808 * Search in the Variable array of the context for the given
1809 * variable value.
1810 *
1811 * Returns the value or NULL if not found
1812 */
1813xmlXPathObjectPtr
1814xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1815 if (ctxt == NULL)
1816 return(NULL);
1817
1818 if (ctxt->varLookupFunc != NULL) {
1819 xmlXPathObjectPtr ret;
1820
1821 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1822 (ctxt->varLookupData, name, NULL);
1823 if (ret != NULL) return(ret);
1824 }
1825 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
1826}
1827
1828/**
1829 * xmlXPathVariableLookupNS:
1830 * @ctxt: the XPath context
1831 * @name: the variable name
1832 * @ns_uri: the variable namespace URI
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
1840xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1841 const xmlChar *ns_uri) {
1842 if (ctxt == NULL)
1843 return(NULL);
1844
1845 if (ctxt->varLookupFunc != NULL) {
1846 xmlXPathObjectPtr ret;
1847
1848 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1849 (ctxt->varLookupData, name, ns_uri);
1850 if (ret != NULL) return(ret);
1851 }
1852
1853 if (ctxt->varHash == NULL)
1854 return(NULL);
1855 if (name == NULL)
1856 return(NULL);
1857
1858 return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
1859}
1860
1861/**
1862 * xmlXPathRegisteredVariablesCleanup:
1863 * @ctxt: the XPath context
1864 *
1865 * Cleanup the XPath context data associated to registered variables
1866 */
1867void
1868xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
1869 if (ctxt == NULL)
1870 return;
1871
1872 xmlHashFree(ctxt->varHash, NULL);
1873 ctxt->varHash = NULL;
1874}
1875
1876/**
1877 * xmlXPathRegisterNs:
1878 * @ctxt: the XPath context
1879 * @prefix: the namespace prefix
1880 * @ns_uri: the namespace name
1881 *
1882 * Register a new namespace. If @ns_uri is NULL it unregisters
1883 * the namespace
1884 *
1885 * Returns 0 in case of success, -1 in case of error
1886 */
1887int
1888xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
1889 const xmlChar *ns_uri) {
1890 if (ctxt == NULL)
1891 return(-1);
1892 if (prefix == NULL)
1893 return(-1);
1894
1895 if (ctxt->nsHash == NULL)
1896 ctxt->nsHash = xmlHashCreate(10);
1897 if (ctxt->nsHash == NULL)
1898 return(-1);
1899 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
1900 (xmlHashDeallocator)xmlFree));
1901}
1902
1903/**
1904 * xmlXPathNsLookup:
1905 * @ctxt: the XPath context
1906 * @prefix: the namespace prefix value
1907 *
1908 * Search in the namespace declaration array of the context for the given
1909 * namespace name associated to the given prefix
1910 *
1911 * Returns the value or NULL if not found
1912 */
1913const xmlChar *
1914xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
1915 if (ctxt == NULL)
1916 return(NULL);
1917 if (prefix == NULL)
1918 return(NULL);
1919
1920#ifdef XML_XML_NAMESPACE
1921 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
1922 return(XML_XML_NAMESPACE);
1923#endif
1924
1925 if (ctxt->nsHash == NULL)
1926 return(NULL);
1927
1928 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
1929}
1930
1931/**
1932 * xmlXPathRegisteredVariablesCleanup:
1933 * @ctxt: the XPath context
1934 *
1935 * Cleanup the XPath context data associated to registered variables
1936 */
1937void
1938xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
1939 if (ctxt == NULL)
1940 return;
1941
1942 xmlHashFree(ctxt->nsHash, NULL);
1943 ctxt->nsHash = NULL;
1944}
1945
1946/************************************************************************
1947 * *
1948 * Routines to handle Values *
1949 * *
1950 ************************************************************************/
1951
1952/* Allocations are terrible, one need to optimize all this !!! */
1953
1954/**
1955 * xmlXPathNewFloat:
1956 * @val: the double value
1957 *
1958 * Create a new xmlXPathObjectPtr of type double and of value @val
1959 *
1960 * Returns the newly created object.
1961 */
1962xmlXPathObjectPtr
1963xmlXPathNewFloat(double val) {
1964 xmlXPathObjectPtr ret;
1965
1966 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1967 if (ret == NULL) {
1968 xmlGenericError(xmlGenericErrorContext,
1969 "xmlXPathNewFloat: out of memory\n");
1970 return(NULL);
1971 }
1972 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1973 ret->type = XPATH_NUMBER;
1974 ret->floatval = val;
1975 return(ret);
1976}
1977
1978/**
1979 * xmlXPathNewBoolean:
1980 * @val: the boolean value
1981 *
1982 * Create a new xmlXPathObjectPtr of type boolean and of value @val
1983 *
1984 * Returns the newly created object.
1985 */
1986xmlXPathObjectPtr
1987xmlXPathNewBoolean(int val) {
1988 xmlXPathObjectPtr ret;
1989
1990 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1991 if (ret == NULL) {
1992 xmlGenericError(xmlGenericErrorContext,
1993 "xmlXPathNewBoolean: out of memory\n");
1994 return(NULL);
1995 }
1996 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1997 ret->type = XPATH_BOOLEAN;
1998 ret->boolval = (val != 0);
1999 return(ret);
2000}
2001
2002/**
2003 * xmlXPathNewString:
2004 * @val: the xmlChar * value
2005 *
2006 * Create a new xmlXPathObjectPtr of type string and of value @val
2007 *
2008 * Returns the newly created object.
2009 */
2010xmlXPathObjectPtr
2011xmlXPathNewString(const xmlChar *val) {
2012 xmlXPathObjectPtr ret;
2013
2014 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2015 if (ret == NULL) {
2016 xmlGenericError(xmlGenericErrorContext,
2017 "xmlXPathNewString: out of memory\n");
2018 return(NULL);
2019 }
2020 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2021 ret->type = XPATH_STRING;
2022 if (val != NULL)
2023 ret->stringval = xmlStrdup(val);
2024 else
2025 ret->stringval = xmlStrdup((const xmlChar *)"");
2026 return(ret);
2027}
2028
2029/**
2030 * xmlXPathNewCString:
2031 * @val: the char * value
2032 *
2033 * Create a new xmlXPathObjectPtr of type string and of value @val
2034 *
2035 * Returns the newly created object.
2036 */
2037xmlXPathObjectPtr
2038xmlXPathNewCString(const char *val) {
2039 xmlXPathObjectPtr ret;
2040
2041 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2042 if (ret == NULL) {
2043 xmlGenericError(xmlGenericErrorContext,
2044 "xmlXPathNewCString: out of memory\n");
2045 return(NULL);
2046 }
2047 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2048 ret->type = XPATH_STRING;
2049 ret->stringval = xmlStrdup(BAD_CAST val);
2050 return(ret);
2051}
2052
2053/**
2054 * xmlXPathObjectCopy:
2055 * @val: the original object
2056 *
2057 * allocate a new copy of a given object
2058 *
2059 * Returns the newly created object.
2060 */
2061xmlXPathObjectPtr
2062xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2063 xmlXPathObjectPtr ret;
2064
2065 if (val == NULL)
2066 return(NULL);
2067
2068 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2069 if (ret == NULL) {
2070 xmlGenericError(xmlGenericErrorContext,
2071 "xmlXPathObjectCopy: out of memory\n");
2072 return(NULL);
2073 }
2074 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2075 switch (val->type) {
2076 case XPATH_BOOLEAN:
2077 case XPATH_NUMBER:
2078 case XPATH_POINT:
2079 case XPATH_RANGE:
2080 break;
2081 case XPATH_STRING:
2082 ret->stringval = xmlStrdup(val->stringval);
2083 break;
2084 case XPATH_XSLT_TREE:
2085 if ((val->nodesetval != NULL) &&
2086 (val->nodesetval->nodeTab != NULL))
2087 ret->nodesetval = xmlXPathNodeSetCreate(
2088 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
2089 else
2090 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
2091 break;
2092 case XPATH_NODESET:
2093 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
2094 break;
2095 case XPATH_LOCATIONSET:
2096#ifdef LIBXML_XPTR_ENABLED
2097 {
2098 xmlLocationSetPtr loc = val->user;
2099 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2100 break;
2101 }
2102#endif
2103 case XPATH_UNDEFINED:
2104 case XPATH_USERS:
2105 xmlGenericError(xmlGenericErrorContext,
2106 "xmlXPathObjectCopy: unsupported type %d\n",
2107 val->type);
2108 break;
2109 }
2110 return(ret);
2111}
2112
2113/**
2114 * xmlXPathFreeObject:
2115 * @obj: the object to free
2116 *
2117 * Free up an xmlXPathObjectPtr object.
2118 */
2119void
2120xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2121 if (obj == NULL) return;
2122 if (obj->type == XPATH_NODESET) {
Daniel Veillard77851712001-02-27 21:54:07 +00002123 if (obj->boolval) {
2124 obj->type = XPATH_XSLT_TREE;
2125 if (obj->nodesetval != NULL)
2126 xmlXPathFreeValueTree(obj->nodesetval);
2127 } else {
2128 if (obj->nodesetval != NULL)
2129 xmlXPathFreeNodeSet(obj->nodesetval);
2130 }
Owen Taylor3473f882001-02-23 17:55:21 +00002131#ifdef LIBXML_XPTR_ENABLED
2132 } else if (obj->type == XPATH_LOCATIONSET) {
2133 if (obj->user != NULL)
2134 xmlXPtrFreeLocationSet(obj->user);
2135#endif
2136 } else if (obj->type == XPATH_STRING) {
2137 if (obj->stringval != NULL)
2138 xmlFree(obj->stringval);
2139 } else if (obj->type == XPATH_XSLT_TREE) {
2140 if (obj->nodesetval != NULL)
2141 xmlXPathFreeValueTree(obj->nodesetval);
2142 }
2143
Owen Taylor3473f882001-02-23 17:55:21 +00002144 xmlFree(obj);
2145}
2146
2147/************************************************************************
2148 * *
2149 * Routines to handle XPath contexts *
2150 * *
2151 ************************************************************************/
2152
2153/**
2154 * xmlXPathNewContext:
2155 * @doc: the XML document
2156 *
2157 * Create a new xmlXPathContext
2158 *
2159 * Returns the xmlXPathContext just allocated.
2160 */
2161xmlXPathContextPtr
2162xmlXPathNewContext(xmlDocPtr doc) {
2163 xmlXPathContextPtr ret;
2164
2165 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
2166 if (ret == NULL) {
2167 xmlGenericError(xmlGenericErrorContext,
2168 "xmlXPathNewContext: out of memory\n");
2169 return(NULL);
2170 }
2171 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
2172 ret->doc = doc;
2173 ret->node = NULL;
2174
2175 ret->varHash = NULL;
2176
2177 ret->nb_types = 0;
2178 ret->max_types = 0;
2179 ret->types = NULL;
2180
2181 ret->funcHash = xmlHashCreate(0);
2182
2183 ret->nb_axis = 0;
2184 ret->max_axis = 0;
2185 ret->axis = NULL;
2186
2187 ret->nsHash = NULL;
2188 ret->user = NULL;
2189
2190 ret->contextSize = -1;
2191 ret->proximityPosition = -1;
2192
2193 xmlXPathRegisterAllFunctions(ret);
2194
2195 return(ret);
2196}
2197
2198/**
2199 * xmlXPathFreeContext:
2200 * @ctxt: the context to free
2201 *
2202 * Free up an xmlXPathContext
2203 */
2204void
2205xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
2206 xmlXPathRegisteredNsCleanup(ctxt);
2207 xmlXPathRegisteredFuncsCleanup(ctxt);
2208 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00002209 xmlFree(ctxt);
2210}
2211
2212/************************************************************************
2213 * *
2214 * Routines to handle XPath parser contexts *
2215 * *
2216 ************************************************************************/
2217
2218#define CHECK_CTXT(ctxt) \
2219 if (ctxt == NULL) { \
2220 xmlGenericError(xmlGenericErrorContext, \
2221 "%s:%d Internal error: ctxt == NULL\n", \
2222 __FILE__, __LINE__); \
2223 } \
2224
2225
2226#define CHECK_CONTEXT(ctxt) \
2227 if (ctxt == NULL) { \
2228 xmlGenericError(xmlGenericErrorContext, \
2229 "%s:%d Internal error: no context\n", \
2230 __FILE__, __LINE__); \
2231 } \
2232 else if (ctxt->doc == NULL) { \
2233 xmlGenericError(xmlGenericErrorContext, \
2234 "%s:%d Internal error: no document\n", \
2235 __FILE__, __LINE__); \
2236 } \
2237 else if (ctxt->doc->children == NULL) { \
2238 xmlGenericError(xmlGenericErrorContext, \
2239 "%s:%d Internal error: document without root\n", \
2240 __FILE__, __LINE__); \
2241 } \
2242
2243
2244/**
2245 * xmlXPathNewParserContext:
2246 * @str: the XPath expression
2247 * @ctxt: the XPath context
2248 *
2249 * Create a new xmlXPathParserContext
2250 *
2251 * Returns the xmlXPathParserContext just allocated.
2252 */
2253xmlXPathParserContextPtr
2254xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
2255 xmlXPathParserContextPtr ret;
2256
2257 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2258 if (ret == NULL) {
2259 xmlGenericError(xmlGenericErrorContext,
2260 "xmlXPathNewParserContext: out of memory\n");
2261 return(NULL);
2262 }
2263 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2264 ret->cur = ret->base = str;
2265 ret->context = ctxt;
2266
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002267 ret->comp = xmlXPathNewCompExpr();
2268 if (ret->comp == NULL) {
2269 xmlFree(ret->valueTab);
2270 xmlFree(ret);
2271 return(NULL);
2272 }
2273
2274 return(ret);
2275}
2276
2277/**
2278 * xmlXPathCompParserContext:
2279 * @comp: the XPath compiled expression
2280 * @ctxt: the XPath context
2281 *
2282 * Create a new xmlXPathParserContext when processing a compiled expression
2283 *
2284 * Returns the xmlXPathParserContext just allocated.
2285 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002286static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002287xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
2288 xmlXPathParserContextPtr ret;
2289
2290 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2291 if (ret == NULL) {
2292 xmlGenericError(xmlGenericErrorContext,
2293 "xmlXPathNewParserContext: out of memory\n");
2294 return(NULL);
2295 }
2296 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2297
Owen Taylor3473f882001-02-23 17:55:21 +00002298 /* Allocate the value stack */
2299 ret->valueTab = (xmlXPathObjectPtr *)
2300 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002301 if (ret->valueTab == NULL) {
2302 xmlFree(ret);
2303 xmlGenericError(xmlGenericErrorContext,
2304 "xmlXPathNewParserContext: out of memory\n");
2305 return(NULL);
2306 }
Owen Taylor3473f882001-02-23 17:55:21 +00002307 ret->valueNr = 0;
2308 ret->valueMax = 10;
2309 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002310
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00002311 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002312 ret->comp = comp;
2313
Owen Taylor3473f882001-02-23 17:55:21 +00002314 return(ret);
2315}
2316
2317/**
2318 * xmlXPathFreeParserContext:
2319 * @ctxt: the context to free
2320 *
2321 * Free up an xmlXPathParserContext
2322 */
2323void
2324xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
2325 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002326 xmlFree(ctxt->valueTab);
2327 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002328 if (ctxt->comp)
2329 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00002330 xmlFree(ctxt);
2331}
2332
2333/************************************************************************
2334 * *
2335 * The implicit core function library *
2336 * *
2337 ************************************************************************/
2338
2339/*
2340 * Auto-pop and cast to a number
2341 */
2342void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
2343
2344
2345#define POP_FLOAT \
2346 arg = valuePop(ctxt); \
2347 if (arg == NULL) { \
2348 XP_ERROR(XPATH_INVALID_OPERAND); \
2349 } \
2350 if (arg->type != XPATH_NUMBER) { \
2351 valuePush(ctxt, arg); \
2352 xmlXPathNumberFunction(ctxt, 1); \
2353 arg = valuePop(ctxt); \
2354 }
2355
2356/**
2357 * xmlXPathCompareNodeSetFloat:
2358 * @ctxt: the XPath Parser context
2359 * @inf: less than (1) or greater than (0)
2360 * @strict: is the comparison strict
2361 * @arg: the node set
2362 * @f: the value
2363 *
2364 * Implement the compare operation between a nodeset and a number
2365 * @ns < @val (1, 1, ...
2366 * @ns <= @val (1, 0, ...
2367 * @ns > @val (0, 1, ...
2368 * @ns >= @val (0, 0, ...
2369 *
2370 * If one object to be compared is a node-set and the other is a number,
2371 * then the comparison will be true if and only if there is a node in the
2372 * node-set such that the result of performing the comparison on the number
2373 * to be compared and on the result of converting the string-value of that
2374 * node to a number using the number function is true.
2375 *
2376 * Returns 0 or 1 depending on the results of the test.
2377 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002378static int
Owen Taylor3473f882001-02-23 17:55:21 +00002379xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
2380 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
2381 int i, ret = 0;
2382 xmlNodeSetPtr ns;
2383 xmlChar *str2;
2384
2385 if ((f == NULL) || (arg == NULL) ||
2386 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2387 xmlXPathFreeObject(arg);
2388 xmlXPathFreeObject(f);
2389 return(0);
2390 }
2391 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002392 if (ns != NULL) {
2393 for (i = 0;i < ns->nodeNr;i++) {
2394 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2395 if (str2 != NULL) {
2396 valuePush(ctxt,
2397 xmlXPathNewString(str2));
2398 xmlFree(str2);
2399 xmlXPathNumberFunction(ctxt, 1);
2400 valuePush(ctxt, xmlXPathObjectCopy(f));
2401 ret = xmlXPathCompareValues(ctxt, inf, strict);
2402 if (ret)
2403 break;
2404 }
2405 }
Owen Taylor3473f882001-02-23 17:55:21 +00002406 }
2407 xmlXPathFreeObject(arg);
2408 xmlXPathFreeObject(f);
2409 return(ret);
2410}
2411
2412/**
2413 * xmlXPathCompareNodeSetString:
2414 * @ctxt: the XPath Parser context
2415 * @inf: less than (1) or greater than (0)
2416 * @strict: is the comparison strict
2417 * @arg: the node set
2418 * @s: the value
2419 *
2420 * Implement the compare operation between a nodeset and a string
2421 * @ns < @val (1, 1, ...
2422 * @ns <= @val (1, 0, ...
2423 * @ns > @val (0, 1, ...
2424 * @ns >= @val (0, 0, ...
2425 *
2426 * If one object to be compared is a node-set and the other is a string,
2427 * then the comparison will be true if and only if there is a node in
2428 * the node-set such that the result of performing the comparison on the
2429 * string-value of the node and the other string is true.
2430 *
2431 * Returns 0 or 1 depending on the results of the test.
2432 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002433static int
Owen Taylor3473f882001-02-23 17:55:21 +00002434xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
2435 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
2436 int i, ret = 0;
2437 xmlNodeSetPtr ns;
2438 xmlChar *str2;
2439
2440 if ((s == NULL) || (arg == NULL) ||
2441 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2442 xmlXPathFreeObject(arg);
2443 xmlXPathFreeObject(s);
2444 return(0);
2445 }
2446 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002447 if (ns != NULL) {
2448 for (i = 0;i < ns->nodeNr;i++) {
2449 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2450 if (str2 != NULL) {
2451 valuePush(ctxt,
2452 xmlXPathNewString(str2));
2453 xmlFree(str2);
2454 valuePush(ctxt, xmlXPathObjectCopy(s));
2455 ret = xmlXPathCompareValues(ctxt, inf, strict);
2456 if (ret)
2457 break;
2458 }
2459 }
Owen Taylor3473f882001-02-23 17:55:21 +00002460 }
2461 xmlXPathFreeObject(arg);
2462 xmlXPathFreeObject(s);
2463 return(ret);
2464}
2465
2466/**
2467 * xmlXPathCompareNodeSets:
Owen Taylor3473f882001-02-23 17:55:21 +00002468 * @op: less than (-1), equal (0) or greater than (1)
2469 * @strict: is the comparison strict
2470 * @arg1: the fist node set object
2471 * @arg2: the second node set object
2472 *
2473 * Implement the compare operation on nodesets:
2474 *
2475 * If both objects to be compared are node-sets, then the comparison
2476 * will be true if and only if there is a node in the first node-set
2477 * and a node in the second node-set such that the result of performing
2478 * the comparison on the string-values of the two nodes is true.
2479 * ....
2480 * When neither object to be compared is a node-set and the operator
2481 * is <=, <, >= or >, then the objects are compared by converting both
2482 * objects to numbers and comparing the numbers according to IEEE 754.
2483 * ....
2484 * The number function converts its argument to a number as follows:
2485 * - a string that consists of optional whitespace followed by an
2486 * optional minus sign followed by a Number followed by whitespace
2487 * is converted to the IEEE 754 number that is nearest (according
2488 * to the IEEE 754 round-to-nearest rule) to the mathematical value
2489 * represented by the string; any other string is converted to NaN
2490 *
2491 * Conclusion all nodes need to be converted first to their string value
2492 * and then the comparison must be done when possible
2493 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002494static int
2495xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00002496 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2497 int i, j, init = 0;
2498 double val1;
2499 double *values2;
2500 int ret = 0;
2501 xmlChar *str;
2502 xmlNodeSetPtr ns1;
2503 xmlNodeSetPtr ns2;
2504
2505 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002506 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
2507 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002508 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002509 }
Owen Taylor3473f882001-02-23 17:55:21 +00002510 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002511 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
2512 xmlXPathFreeObject(arg1);
2513 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002514 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002515 }
Owen Taylor3473f882001-02-23 17:55:21 +00002516
2517 ns1 = arg1->nodesetval;
2518 ns2 = arg2->nodesetval;
2519
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002520 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002521 xmlXPathFreeObject(arg1);
2522 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002523 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002524 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002525 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002526 xmlXPathFreeObject(arg1);
2527 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002528 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002529 }
Owen Taylor3473f882001-02-23 17:55:21 +00002530
2531 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
2532 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002533 xmlXPathFreeObject(arg1);
2534 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002535 return(0);
2536 }
2537 for (i = 0;i < ns1->nodeNr;i++) {
2538 str = xmlNodeGetContent(ns1->nodeTab[i]);
2539 if (str == NULL)
2540 continue;
2541 val1 = xmlXPathStringEvalNumber(str);
2542 xmlFree(str);
2543 if (isnan(val1))
2544 continue;
2545 for (j = 0;j < ns2->nodeNr;j++) {
2546 if (init == 0) {
2547 str = xmlNodeGetContent(ns2->nodeTab[j]);
2548 if (str == NULL) {
2549 values2[j] = xmlXPathNAN;
2550 } else {
2551 values2[j] = xmlXPathStringEvalNumber(str);
2552 xmlFree(str);
2553 }
2554 }
2555 if (isnan(values2[j]))
2556 continue;
2557 if (inf && strict)
2558 ret = (val1 < values2[j]);
2559 else if (inf && !strict)
2560 ret = (val1 <= values2[j]);
2561 else if (!inf && strict)
2562 ret = (val1 > values2[j]);
2563 else if (!inf && !strict)
2564 ret = (val1 >= values2[j]);
2565 if (ret)
2566 break;
2567 }
2568 if (ret)
2569 break;
2570 init = 1;
2571 }
2572 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002573 xmlXPathFreeObject(arg1);
2574 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002575 return(ret);
2576 return(0);
2577}
2578
2579/**
2580 * xmlXPathCompareNodeSetValue:
2581 * @ctxt: the XPath Parser context
2582 * @inf: less than (1) or greater than (0)
2583 * @strict: is the comparison strict
2584 * @arg: the node set
2585 * @val: the value
2586 *
2587 * Implement the compare operation between a nodeset and a value
2588 * @ns < @val (1, 1, ...
2589 * @ns <= @val (1, 0, ...
2590 * @ns > @val (0, 1, ...
2591 * @ns >= @val (0, 0, ...
2592 *
2593 * If one object to be compared is a node-set and the other is a boolean,
2594 * then the comparison will be true if and only if the result of performing
2595 * the comparison on the boolean and on the result of converting
2596 * the node-set to a boolean using the boolean function is true.
2597 *
2598 * Returns 0 or 1 depending on the results of the test.
2599 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002600static int
Owen Taylor3473f882001-02-23 17:55:21 +00002601xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
2602 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
2603 if ((val == NULL) || (arg == NULL) ||
2604 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2605 return(0);
2606
2607 switch(val->type) {
2608 case XPATH_NUMBER:
2609 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
2610 case XPATH_NODESET:
2611 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002612 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00002613 case XPATH_STRING:
2614 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
2615 case XPATH_BOOLEAN:
2616 valuePush(ctxt, arg);
2617 xmlXPathBooleanFunction(ctxt, 1);
2618 valuePush(ctxt, val);
2619 return(xmlXPathCompareValues(ctxt, inf, strict));
2620 default:
2621 TODO
2622 return(0);
2623 }
2624 return(0);
2625}
2626
2627/**
2628 * xmlXPathEqualNodeSetString
2629 * @arg: the nodeset object argument
2630 * @str: the string to compare to.
2631 *
2632 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2633 * If one object to be compared is a node-set and the other is a string,
2634 * then the comparison will be true if and only if there is a node in
2635 * the node-set such that the result of performing the comparison on the
2636 * string-value of the node and the other string is true.
2637 *
2638 * Returns 0 or 1 depending on the results of the test.
2639 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002640static int
Owen Taylor3473f882001-02-23 17:55:21 +00002641xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
2642 int i;
2643 xmlNodeSetPtr ns;
2644 xmlChar *str2;
2645
2646 if ((str == NULL) || (arg == NULL) ||
2647 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2648 return(0);
2649 ns = arg->nodesetval;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002650 if (ns == NULL)
2651 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002652 if (ns->nodeNr <= 0)
2653 return(0);
2654 for (i = 0;i < ns->nodeNr;i++) {
2655 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2656 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
2657 xmlFree(str2);
2658 return(1);
2659 }
2660 if (str2 != NULL)
2661 xmlFree(str2);
2662 }
2663 return(0);
2664}
2665
2666/**
2667 * xmlXPathEqualNodeSetFloat
2668 * @arg: the nodeset object argument
2669 * @f: the float to compare to
2670 *
2671 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2672 * If one object to be compared is a node-set and the other is a number,
2673 * then the comparison will be true if and only if there is a node in
2674 * the node-set such that the result of performing the comparison on the
2675 * number to be compared and on the result of converting the string-value
2676 * of that node to a number using the number function is true.
2677 *
2678 * Returns 0 or 1 depending on the results of the test.
2679 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002680static int
Owen Taylor3473f882001-02-23 17:55:21 +00002681xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
2682 char buf[100] = "";
2683
2684 if ((arg == NULL) ||
2685 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2686 return(0);
2687
Bjorn Reesee1dc0112001-03-03 12:09:03 +00002688 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00002689 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
2690}
2691
2692
2693/**
2694 * xmlXPathEqualNodeSets
2695 * @arg1: first nodeset object argument
2696 * @arg2: second nodeset object argument
2697 *
2698 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
2699 * If both objects to be compared are node-sets, then the comparison
2700 * will be true if and only if there is a node in the first node-set and
2701 * a node in the second node-set such that the result of performing the
2702 * comparison on the string-values of the two nodes is true.
2703 *
2704 * (needless to say, this is a costly operation)
2705 *
2706 * Returns 0 or 1 depending on the results of the test.
2707 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002708static int
Owen Taylor3473f882001-02-23 17:55:21 +00002709xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2710 int i, j;
2711 xmlChar **values1;
2712 xmlChar **values2;
2713 int ret = 0;
2714 xmlNodeSetPtr ns1;
2715 xmlNodeSetPtr ns2;
2716
2717 if ((arg1 == NULL) ||
2718 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2719 return(0);
2720 if ((arg2 == NULL) ||
2721 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2722 return(0);
2723
2724 ns1 = arg1->nodesetval;
2725 ns2 = arg2->nodesetval;
2726
Daniel Veillard911f49a2001-04-07 15:39:35 +00002727 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002728 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00002729 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002730 return(0);
2731
2732 /*
2733 * check if there is a node pertaining to both sets
2734 */
2735 for (i = 0;i < ns1->nodeNr;i++)
2736 for (j = 0;j < ns2->nodeNr;j++)
2737 if (ns1->nodeTab[i] == ns2->nodeTab[j])
2738 return(1);
2739
2740 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
2741 if (values1 == NULL)
2742 return(0);
2743 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
2744 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
2745 if (values2 == NULL) {
2746 xmlFree(values1);
2747 return(0);
2748 }
2749 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
2750 for (i = 0;i < ns1->nodeNr;i++) {
2751 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
2752 for (j = 0;j < ns2->nodeNr;j++) {
2753 if (i == 0)
2754 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
2755 ret = xmlStrEqual(values1[i], values2[j]);
2756 if (ret)
2757 break;
2758 }
2759 if (ret)
2760 break;
2761 }
2762 for (i = 0;i < ns1->nodeNr;i++)
2763 if (values1[i] != NULL)
2764 xmlFree(values1[i]);
2765 for (j = 0;j < ns2->nodeNr;j++)
2766 if (values2[j] != NULL)
2767 xmlFree(values2[j]);
2768 xmlFree(values1);
2769 xmlFree(values2);
2770 return(ret);
2771}
2772
2773/**
2774 * xmlXPathEqualValues:
2775 * @ctxt: the XPath Parser context
2776 *
2777 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2778 *
2779 * Returns 0 or 1 depending on the results of the test.
2780 */
2781int
2782xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
2783 xmlXPathObjectPtr arg1, arg2;
2784 int ret = 0;
2785
2786 arg1 = valuePop(ctxt);
2787 if (arg1 == NULL)
2788 XP_ERROR0(XPATH_INVALID_OPERAND);
2789
2790 arg2 = valuePop(ctxt);
2791 if (arg2 == NULL) {
2792 xmlXPathFreeObject(arg1);
2793 XP_ERROR0(XPATH_INVALID_OPERAND);
2794 }
2795
2796 if (arg1 == arg2) {
2797#ifdef DEBUG_EXPR
2798 xmlGenericError(xmlGenericErrorContext,
2799 "Equal: by pointer\n");
2800#endif
2801 return(1);
2802 }
2803
2804 switch (arg1->type) {
2805 case XPATH_UNDEFINED:
2806#ifdef DEBUG_EXPR
2807 xmlGenericError(xmlGenericErrorContext,
2808 "Equal: undefined\n");
2809#endif
2810 break;
2811 case XPATH_XSLT_TREE:
2812 case XPATH_NODESET:
2813 switch (arg2->type) {
2814 case XPATH_UNDEFINED:
2815#ifdef DEBUG_EXPR
2816 xmlGenericError(xmlGenericErrorContext,
2817 "Equal: undefined\n");
2818#endif
2819 break;
2820 case XPATH_XSLT_TREE:
2821 case XPATH_NODESET:
2822 ret = xmlXPathEqualNodeSets(arg1, arg2);
2823 break;
2824 case XPATH_BOOLEAN:
2825 if ((arg1->nodesetval == NULL) ||
2826 (arg1->nodesetval->nodeNr == 0)) ret = 0;
2827 else
2828 ret = 1;
2829 ret = (ret == arg2->boolval);
2830 break;
2831 case XPATH_NUMBER:
2832 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
2833 break;
2834 case XPATH_STRING:
2835 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
2836 break;
2837 case XPATH_USERS:
2838 case XPATH_POINT:
2839 case XPATH_RANGE:
2840 case XPATH_LOCATIONSET:
2841 TODO
2842 break;
2843 }
2844 break;
2845 case XPATH_BOOLEAN:
2846 switch (arg2->type) {
2847 case XPATH_UNDEFINED:
2848#ifdef DEBUG_EXPR
2849 xmlGenericError(xmlGenericErrorContext,
2850 "Equal: undefined\n");
2851#endif
2852 break;
2853 case XPATH_NODESET:
2854 case XPATH_XSLT_TREE:
2855 if ((arg2->nodesetval == NULL) ||
2856 (arg2->nodesetval->nodeNr == 0)) ret = 0;
2857 else
2858 ret = 1;
2859 break;
2860 case XPATH_BOOLEAN:
2861#ifdef DEBUG_EXPR
2862 xmlGenericError(xmlGenericErrorContext,
2863 "Equal: %d boolean %d \n",
2864 arg1->boolval, arg2->boolval);
2865#endif
2866 ret = (arg1->boolval == arg2->boolval);
2867 break;
2868 case XPATH_NUMBER:
2869 if (arg2->floatval) ret = 1;
2870 else ret = 0;
2871 ret = (arg1->boolval == ret);
2872 break;
2873 case XPATH_STRING:
2874 if ((arg2->stringval == NULL) ||
2875 (arg2->stringval[0] == 0)) ret = 0;
2876 else
2877 ret = 1;
2878 ret = (arg1->boolval == ret);
2879 break;
2880 case XPATH_USERS:
2881 case XPATH_POINT:
2882 case XPATH_RANGE:
2883 case XPATH_LOCATIONSET:
2884 TODO
2885 break;
2886 }
2887 break;
2888 case XPATH_NUMBER:
2889 switch (arg2->type) {
2890 case XPATH_UNDEFINED:
2891#ifdef DEBUG_EXPR
2892 xmlGenericError(xmlGenericErrorContext,
2893 "Equal: undefined\n");
2894#endif
2895 break;
2896 case XPATH_NODESET:
2897 case XPATH_XSLT_TREE:
2898 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
2899 break;
2900 case XPATH_BOOLEAN:
2901 if (arg1->floatval) ret = 1;
2902 else ret = 0;
2903 ret = (arg2->boolval == ret);
2904 break;
2905 case XPATH_STRING:
2906 valuePush(ctxt, arg2);
2907 xmlXPathNumberFunction(ctxt, 1);
2908 arg2 = valuePop(ctxt);
2909 /* no break on purpose */
2910 case XPATH_NUMBER:
2911 ret = (arg1->floatval == arg2->floatval);
2912 break;
2913 case XPATH_USERS:
2914 case XPATH_POINT:
2915 case XPATH_RANGE:
2916 case XPATH_LOCATIONSET:
2917 TODO
2918 break;
2919 }
2920 break;
2921 case XPATH_STRING:
2922 switch (arg2->type) {
2923 case XPATH_UNDEFINED:
2924#ifdef DEBUG_EXPR
2925 xmlGenericError(xmlGenericErrorContext,
2926 "Equal: undefined\n");
2927#endif
2928 break;
2929 case XPATH_NODESET:
2930 case XPATH_XSLT_TREE:
2931 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
2932 break;
2933 case XPATH_BOOLEAN:
2934 if ((arg1->stringval == NULL) ||
2935 (arg1->stringval[0] == 0)) ret = 0;
2936 else
2937 ret = 1;
2938 ret = (arg2->boolval == ret);
2939 break;
2940 case XPATH_STRING:
2941 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
2942 break;
2943 case XPATH_NUMBER:
2944 valuePush(ctxt, arg1);
2945 xmlXPathNumberFunction(ctxt, 1);
2946 arg1 = valuePop(ctxt);
2947 ret = (arg1->floatval == arg2->floatval);
2948 break;
2949 case XPATH_USERS:
2950 case XPATH_POINT:
2951 case XPATH_RANGE:
2952 case XPATH_LOCATIONSET:
2953 TODO
2954 break;
2955 }
2956 break;
2957 case XPATH_USERS:
2958 case XPATH_POINT:
2959 case XPATH_RANGE:
2960 case XPATH_LOCATIONSET:
2961 TODO
2962 break;
2963 }
2964 xmlXPathFreeObject(arg1);
2965 xmlXPathFreeObject(arg2);
2966 return(ret);
2967}
2968
2969
2970/**
2971 * xmlXPathCompareValues:
2972 * @ctxt: the XPath Parser context
2973 * @inf: less than (1) or greater than (0)
2974 * @strict: is the comparison strict
2975 *
2976 * Implement the compare operation on XPath objects:
2977 * @arg1 < @arg2 (1, 1, ...
2978 * @arg1 <= @arg2 (1, 0, ...
2979 * @arg1 > @arg2 (0, 1, ...
2980 * @arg1 >= @arg2 (0, 0, ...
2981 *
2982 * When neither object to be compared is a node-set and the operator is
2983 * <=, <, >=, >, then the objects are compared by converted both objects
2984 * to numbers and comparing the numbers according to IEEE 754. The <
2985 * comparison will be true if and only if the first number is less than the
2986 * second number. The <= comparison will be true if and only if the first
2987 * number is less than or equal to the second number. The > comparison
2988 * will be true if and only if the first number is greater than the second
2989 * number. The >= comparison will be true if and only if the first number
2990 * is greater than or equal to the second number.
2991 *
2992 * Returns 1 if the comparaison succeeded, 0 if it failed
2993 */
2994int
2995xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
2996 int ret = 0;
2997 xmlXPathObjectPtr arg1, arg2;
2998
2999 arg2 = valuePop(ctxt);
3000 if (arg2 == NULL) {
3001 XP_ERROR0(XPATH_INVALID_OPERAND);
3002 }
3003
3004 arg1 = valuePop(ctxt);
3005 if (arg1 == NULL) {
3006 xmlXPathFreeObject(arg2);
3007 XP_ERROR0(XPATH_INVALID_OPERAND);
3008 }
3009
3010 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
3011 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003012 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003013 } else {
3014 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003015 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
3016 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003017 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003018 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
3019 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00003020 }
3021 }
3022 return(ret);
3023 }
3024
3025 if (arg1->type != XPATH_NUMBER) {
3026 valuePush(ctxt, arg1);
3027 xmlXPathNumberFunction(ctxt, 1);
3028 arg1 = valuePop(ctxt);
3029 }
3030 if (arg1->type != XPATH_NUMBER) {
3031 xmlXPathFreeObject(arg1);
3032 xmlXPathFreeObject(arg2);
3033 XP_ERROR0(XPATH_INVALID_OPERAND);
3034 }
3035 if (arg2->type != XPATH_NUMBER) {
3036 valuePush(ctxt, arg2);
3037 xmlXPathNumberFunction(ctxt, 1);
3038 arg2 = valuePop(ctxt);
3039 }
3040 if (arg2->type != XPATH_NUMBER) {
3041 xmlXPathFreeObject(arg1);
3042 xmlXPathFreeObject(arg2);
3043 XP_ERROR0(XPATH_INVALID_OPERAND);
3044 }
3045 /*
3046 * Add tests for infinity and nan
3047 * => feedback on 3.4 for Inf and NaN
3048 */
3049 if (inf && strict)
3050 ret = (arg1->floatval < arg2->floatval);
3051 else if (inf && !strict)
3052 ret = (arg1->floatval <= arg2->floatval);
3053 else if (!inf && strict)
3054 ret = (arg1->floatval > arg2->floatval);
3055 else if (!inf && !strict)
3056 ret = (arg1->floatval >= arg2->floatval);
3057 xmlXPathFreeObject(arg1);
3058 xmlXPathFreeObject(arg2);
3059 return(ret);
3060}
3061
3062/**
3063 * xmlXPathValueFlipSign:
3064 * @ctxt: the XPath Parser context
3065 *
3066 * Implement the unary - operation on an XPath object
3067 * The numeric operators convert their operands to numbers as if
3068 * by calling the number function.
3069 */
3070void
3071xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
3072 xmlXPathObjectPtr arg;
3073
3074 POP_FLOAT
3075 arg->floatval = -arg->floatval;
3076 valuePush(ctxt, arg);
3077}
3078
3079/**
3080 * xmlXPathAddValues:
3081 * @ctxt: the XPath Parser context
3082 *
3083 * Implement the add operation on XPath objects:
3084 * The numeric operators convert their operands to numbers as if
3085 * by calling the number function.
3086 */
3087void
3088xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
3089 xmlXPathObjectPtr arg;
3090 double val;
3091
3092 POP_FLOAT
3093 val = arg->floatval;
3094 xmlXPathFreeObject(arg);
3095
3096 POP_FLOAT
3097 arg->floatval += val;
3098 valuePush(ctxt, arg);
3099}
3100
3101/**
3102 * xmlXPathSubValues:
3103 * @ctxt: the XPath Parser context
3104 *
3105 * Implement the substraction operation on XPath objects:
3106 * The numeric operators convert their operands to numbers as if
3107 * by calling the number function.
3108 */
3109void
3110xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
3111 xmlXPathObjectPtr arg;
3112 double val;
3113
3114 POP_FLOAT
3115 val = arg->floatval;
3116 xmlXPathFreeObject(arg);
3117
3118 POP_FLOAT
3119 arg->floatval -= val;
3120 valuePush(ctxt, arg);
3121}
3122
3123/**
3124 * xmlXPathMultValues:
3125 * @ctxt: the XPath Parser context
3126 *
3127 * Implement the multiply operation on XPath objects:
3128 * The numeric operators convert their operands to numbers as if
3129 * by calling the number function.
3130 */
3131void
3132xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
3133 xmlXPathObjectPtr arg;
3134 double val;
3135
3136 POP_FLOAT
3137 val = arg->floatval;
3138 xmlXPathFreeObject(arg);
3139
3140 POP_FLOAT
3141 arg->floatval *= val;
3142 valuePush(ctxt, arg);
3143}
3144
3145/**
3146 * xmlXPathDivValues:
3147 * @ctxt: the XPath Parser context
3148 *
3149 * Implement the div operation on XPath objects @arg1 / @arg2:
3150 * The numeric operators convert their operands to numbers as if
3151 * by calling the number function.
3152 */
3153void
3154xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
3155 xmlXPathObjectPtr arg;
3156 double val;
3157
3158 POP_FLOAT
3159 val = arg->floatval;
3160 xmlXPathFreeObject(arg);
3161
3162 POP_FLOAT
3163 arg->floatval /= val;
3164 valuePush(ctxt, arg);
3165}
3166
3167/**
3168 * xmlXPathModValues:
3169 * @ctxt: the XPath Parser context
3170 *
3171 * Implement the mod operation on XPath objects: @arg1 / @arg2
3172 * The numeric operators convert their operands to numbers as if
3173 * by calling the number function.
3174 */
3175void
3176xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
3177 xmlXPathObjectPtr arg;
3178 int arg1, arg2;
3179
3180 POP_FLOAT
3181 arg2 = (int) arg->floatval;
3182 xmlXPathFreeObject(arg);
3183
3184 POP_FLOAT
3185 arg1 = (int) arg->floatval;
3186 arg->floatval = arg1 % arg2;
3187 valuePush(ctxt, arg);
3188}
3189
3190/************************************************************************
3191 * *
3192 * The traversal functions *
3193 * *
3194 ************************************************************************/
3195
Owen Taylor3473f882001-02-23 17:55:21 +00003196/*
3197 * A traversal function enumerates nodes along an axis.
3198 * Initially it must be called with NULL, and it indicates
3199 * termination on the axis by returning NULL.
3200 */
3201typedef xmlNodePtr (*xmlXPathTraversalFunction)
3202 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
3203
3204/**
3205 * xmlXPathNextSelf:
3206 * @ctxt: the XPath Parser context
3207 * @cur: the current node in the traversal
3208 *
3209 * Traversal function for the "self" direction
3210 * The self axis contains just the context node itself
3211 *
3212 * Returns the next element following that axis
3213 */
3214xmlNodePtr
3215xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3216 if (cur == NULL)
3217 return(ctxt->context->node);
3218 return(NULL);
3219}
3220
3221/**
3222 * xmlXPathNextChild:
3223 * @ctxt: the XPath Parser context
3224 * @cur: the current node in the traversal
3225 *
3226 * Traversal function for the "child" direction
3227 * The child axis contains the children of the context node in document order.
3228 *
3229 * Returns the next element following that axis
3230 */
3231xmlNodePtr
3232xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3233 if (cur == NULL) {
3234 if (ctxt->context->node == NULL) return(NULL);
3235 switch (ctxt->context->node->type) {
3236 case XML_ELEMENT_NODE:
3237 case XML_TEXT_NODE:
3238 case XML_CDATA_SECTION_NODE:
3239 case XML_ENTITY_REF_NODE:
3240 case XML_ENTITY_NODE:
3241 case XML_PI_NODE:
3242 case XML_COMMENT_NODE:
3243 case XML_NOTATION_NODE:
3244 case XML_DTD_NODE:
3245 return(ctxt->context->node->children);
3246 case XML_DOCUMENT_NODE:
3247 case XML_DOCUMENT_TYPE_NODE:
3248 case XML_DOCUMENT_FRAG_NODE:
3249 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003250#ifdef LIBXML_DOCB_ENABLED
3251 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003252#endif
3253 return(((xmlDocPtr) ctxt->context->node)->children);
3254 case XML_ELEMENT_DECL:
3255 case XML_ATTRIBUTE_DECL:
3256 case XML_ENTITY_DECL:
3257 case XML_ATTRIBUTE_NODE:
3258 case XML_NAMESPACE_DECL:
3259 case XML_XINCLUDE_START:
3260 case XML_XINCLUDE_END:
3261 return(NULL);
3262 }
3263 return(NULL);
3264 }
3265 if ((cur->type == XML_DOCUMENT_NODE) ||
3266 (cur->type == XML_HTML_DOCUMENT_NODE))
3267 return(NULL);
3268 return(cur->next);
3269}
3270
3271/**
3272 * xmlXPathNextDescendant:
3273 * @ctxt: the XPath Parser context
3274 * @cur: the current node in the traversal
3275 *
3276 * Traversal function for the "descendant" direction
3277 * the descendant axis contains the descendants of the context node in document
3278 * order; a descendant is a child or a child of a child and so on.
3279 *
3280 * Returns the next element following that axis
3281 */
3282xmlNodePtr
3283xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3284 if (cur == NULL) {
3285 if (ctxt->context->node == NULL)
3286 return(NULL);
3287 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3288 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3289 return(NULL);
3290
3291 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3292 return(ctxt->context->doc->children);
3293 return(ctxt->context->node->children);
3294 }
3295
3296 if (cur->children != NULL)
3297 {
3298 if (cur->children->type != XML_ENTITY_DECL)
3299 return(cur->children);
3300 }
3301 if (cur->next != NULL) return(cur->next);
3302
3303 do {
3304 cur = cur->parent;
3305 if (cur == NULL) return(NULL);
3306 if (cur == ctxt->context->node) return(NULL);
3307 if (cur->next != NULL) {
3308 cur = cur->next;
3309 return(cur);
3310 }
3311 } while (cur != NULL);
3312 return(cur);
3313}
3314
3315/**
3316 * xmlXPathNextDescendantOrSelf:
3317 * @ctxt: the XPath Parser context
3318 * @cur: the current node in the traversal
3319 *
3320 * Traversal function for the "descendant-or-self" direction
3321 * the descendant-or-self axis contains the context node and the descendants
3322 * of the context node in document order; thus the context node is the first
3323 * node on the axis, and the first child of the context node is the second node
3324 * on the axis
3325 *
3326 * Returns the next element following that axis
3327 */
3328xmlNodePtr
3329xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3330 if (cur == NULL) {
3331 if (ctxt->context->node == NULL)
3332 return(NULL);
3333 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3334 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3335 return(NULL);
3336 return(ctxt->context->node);
3337 }
3338
3339 return(xmlXPathNextDescendant(ctxt, cur));
3340}
3341
3342/**
3343 * xmlXPathNextParent:
3344 * @ctxt: the XPath Parser context
3345 * @cur: the current node in the traversal
3346 *
3347 * Traversal function for the "parent" direction
3348 * The parent axis contains the parent of the context node, if there is one.
3349 *
3350 * Returns the next element following that axis
3351 */
3352xmlNodePtr
3353xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3354 /*
3355 * the parent of an attribute or namespace node is the element
3356 * to which the attribute or namespace node is attached
3357 * Namespace handling !!!
3358 */
3359 if (cur == NULL) {
3360 if (ctxt->context->node == NULL) return(NULL);
3361 switch (ctxt->context->node->type) {
3362 case XML_ELEMENT_NODE:
3363 case XML_TEXT_NODE:
3364 case XML_CDATA_SECTION_NODE:
3365 case XML_ENTITY_REF_NODE:
3366 case XML_ENTITY_NODE:
3367 case XML_PI_NODE:
3368 case XML_COMMENT_NODE:
3369 case XML_NOTATION_NODE:
3370 case XML_DTD_NODE:
3371 case XML_ELEMENT_DECL:
3372 case XML_ATTRIBUTE_DECL:
3373 case XML_XINCLUDE_START:
3374 case XML_XINCLUDE_END:
3375 case XML_ENTITY_DECL:
3376 if (ctxt->context->node->parent == NULL)
3377 return((xmlNodePtr) ctxt->context->doc);
3378 return(ctxt->context->node->parent);
3379 case XML_ATTRIBUTE_NODE: {
3380 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3381
3382 return(att->parent);
3383 }
3384 case XML_DOCUMENT_NODE:
3385 case XML_DOCUMENT_TYPE_NODE:
3386 case XML_DOCUMENT_FRAG_NODE:
3387 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003388#ifdef LIBXML_DOCB_ENABLED
3389 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003390#endif
3391 return(NULL);
3392 case XML_NAMESPACE_DECL:
3393 /*
3394 * TODO !!! may require extending struct _xmlNs with
3395 * parent field
3396 * C.f. Infoset case...
3397 */
3398 return(NULL);
3399 }
3400 }
3401 return(NULL);
3402}
3403
3404/**
3405 * xmlXPathNextAncestor:
3406 * @ctxt: the XPath Parser context
3407 * @cur: the current node in the traversal
3408 *
3409 * Traversal function for the "ancestor" direction
3410 * the ancestor axis contains the ancestors of the context node; the ancestors
3411 * of the context node consist of the parent of context node and the parent's
3412 * parent and so on; the nodes are ordered in reverse document order; thus the
3413 * parent is the first node on the axis, and the parent's parent is the second
3414 * node on the axis
3415 *
3416 * Returns the next element following that axis
3417 */
3418xmlNodePtr
3419xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3420 /*
3421 * the parent of an attribute or namespace node is the element
3422 * to which the attribute or namespace node is attached
3423 * !!!!!!!!!!!!!
3424 */
3425 if (cur == NULL) {
3426 if (ctxt->context->node == NULL) return(NULL);
3427 switch (ctxt->context->node->type) {
3428 case XML_ELEMENT_NODE:
3429 case XML_TEXT_NODE:
3430 case XML_CDATA_SECTION_NODE:
3431 case XML_ENTITY_REF_NODE:
3432 case XML_ENTITY_NODE:
3433 case XML_PI_NODE:
3434 case XML_COMMENT_NODE:
3435 case XML_DTD_NODE:
3436 case XML_ELEMENT_DECL:
3437 case XML_ATTRIBUTE_DECL:
3438 case XML_ENTITY_DECL:
3439 case XML_NOTATION_NODE:
3440 case XML_XINCLUDE_START:
3441 case XML_XINCLUDE_END:
3442 if (ctxt->context->node->parent == NULL)
3443 return((xmlNodePtr) ctxt->context->doc);
3444 return(ctxt->context->node->parent);
3445 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003446 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003447
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003448 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00003449 }
3450 case XML_DOCUMENT_NODE:
3451 case XML_DOCUMENT_TYPE_NODE:
3452 case XML_DOCUMENT_FRAG_NODE:
3453 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003454#ifdef LIBXML_DOCB_ENABLED
3455 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003456#endif
3457 return(NULL);
3458 case XML_NAMESPACE_DECL:
3459 /*
3460 * TODO !!! may require extending struct _xmlNs with
3461 * parent field
3462 * C.f. Infoset case...
3463 */
3464 return(NULL);
3465 }
3466 return(NULL);
3467 }
3468 if (cur == ctxt->context->doc->children)
3469 return((xmlNodePtr) ctxt->context->doc);
3470 if (cur == (xmlNodePtr) ctxt->context->doc)
3471 return(NULL);
3472 switch (cur->type) {
3473 case XML_ELEMENT_NODE:
3474 case XML_TEXT_NODE:
3475 case XML_CDATA_SECTION_NODE:
3476 case XML_ENTITY_REF_NODE:
3477 case XML_ENTITY_NODE:
3478 case XML_PI_NODE:
3479 case XML_COMMENT_NODE:
3480 case XML_NOTATION_NODE:
3481 case XML_DTD_NODE:
3482 case XML_ELEMENT_DECL:
3483 case XML_ATTRIBUTE_DECL:
3484 case XML_ENTITY_DECL:
3485 case XML_XINCLUDE_START:
3486 case XML_XINCLUDE_END:
3487 return(cur->parent);
3488 case XML_ATTRIBUTE_NODE: {
3489 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3490
3491 return(att->parent);
3492 }
3493 case XML_DOCUMENT_NODE:
3494 case XML_DOCUMENT_TYPE_NODE:
3495 case XML_DOCUMENT_FRAG_NODE:
3496 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003497#ifdef LIBXML_DOCB_ENABLED
3498 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00003499#endif
3500 return(NULL);
3501 case XML_NAMESPACE_DECL:
3502 /*
3503 * TODO !!! may require extending struct _xmlNs with
3504 * parent field
3505 * C.f. Infoset case...
3506 */
3507 return(NULL);
3508 }
3509 return(NULL);
3510}
3511
3512/**
3513 * xmlXPathNextAncestorOrSelf:
3514 * @ctxt: the XPath Parser context
3515 * @cur: the current node in the traversal
3516 *
3517 * Traversal function for the "ancestor-or-self" direction
3518 * he ancestor-or-self axis contains the context node and ancestors of
3519 * the context node in reverse document order; thus the context node is
3520 * the first node on the axis, and the context node's parent the second;
3521 * parent here is defined the same as with the parent axis.
3522 *
3523 * Returns the next element following that axis
3524 */
3525xmlNodePtr
3526xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3527 if (cur == NULL)
3528 return(ctxt->context->node);
3529 return(xmlXPathNextAncestor(ctxt, cur));
3530}
3531
3532/**
3533 * xmlXPathNextFollowingSibling:
3534 * @ctxt: the XPath Parser context
3535 * @cur: the current node in the traversal
3536 *
3537 * Traversal function for the "following-sibling" direction
3538 * The following-sibling axis contains the following siblings of the context
3539 * node in document order.
3540 *
3541 * Returns the next element following that axis
3542 */
3543xmlNodePtr
3544xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3545 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3546 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3547 return(NULL);
3548 if (cur == (xmlNodePtr) ctxt->context->doc)
3549 return(NULL);
3550 if (cur == NULL)
3551 return(ctxt->context->node->next);
3552 return(cur->next);
3553}
3554
3555/**
3556 * xmlXPathNextPrecedingSibling:
3557 * @ctxt: the XPath Parser context
3558 * @cur: the current node in the traversal
3559 *
3560 * Traversal function for the "preceding-sibling" direction
3561 * The preceding-sibling axis contains the preceding siblings of the context
3562 * node in reverse document order; the first preceding sibling is first on the
3563 * axis; the sibling preceding that node is the second on the axis and so on.
3564 *
3565 * Returns the next element following that axis
3566 */
3567xmlNodePtr
3568xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3569 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3570 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3571 return(NULL);
3572 if (cur == (xmlNodePtr) ctxt->context->doc)
3573 return(NULL);
3574 if (cur == NULL)
3575 return(ctxt->context->node->prev);
3576 return(cur->prev);
3577}
3578
3579/**
3580 * xmlXPathNextFollowing:
3581 * @ctxt: the XPath Parser context
3582 * @cur: the current node in the traversal
3583 *
3584 * Traversal function for the "following" direction
3585 * The following axis contains all nodes in the same document as the context
3586 * node that are after the context node in document order, excluding any
3587 * descendants and excluding attribute nodes and namespace nodes; the nodes
3588 * are ordered in document order
3589 *
3590 * Returns the next element following that axis
3591 */
3592xmlNodePtr
3593xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3594 if (cur != NULL && cur->children != NULL)
3595 return cur->children ;
3596 if (cur == NULL) cur = ctxt->context->node;
3597 if (cur == NULL) return(NULL) ; /* ERROR */
3598 if (cur->next != NULL) return(cur->next) ;
3599 do {
3600 cur = cur->parent;
3601 if (cur == NULL) return(NULL);
3602 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3603 if (cur->next != NULL) return(cur->next);
3604 } while (cur != NULL);
3605 return(cur);
3606}
3607
3608/*
3609 * xmlXPathIsAncestor:
3610 * @ancestor: the ancestor node
3611 * @node: the current node
3612 *
3613 * Check that @ancestor is a @node's ancestor
3614 *
3615 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3616 */
3617static int
3618xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3619 if ((ancestor == NULL) || (node == NULL)) return(0);
3620 /* nodes need to be in the same document */
3621 if (ancestor->doc != node->doc) return(0);
3622 /* avoid searching if ancestor or node is the root node */
3623 if (ancestor == (xmlNodePtr) node->doc) return(1);
3624 if (node == (xmlNodePtr) ancestor->doc) return(0);
3625 while (node->parent != NULL) {
3626 if (node->parent == ancestor)
3627 return(1);
3628 node = node->parent;
3629 }
3630 return(0);
3631}
3632
3633/**
3634 * xmlXPathNextPreceding:
3635 * @ctxt: the XPath Parser context
3636 * @cur: the current node in the traversal
3637 *
3638 * Traversal function for the "preceding" direction
3639 * the preceding axis contains all nodes in the same document as the context
3640 * node that are before the context node in document order, excluding any
3641 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3642 * ordered in reverse document order
3643 *
3644 * Returns the next element following that axis
3645 */
3646xmlNodePtr
3647xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3648 if (cur == NULL)
3649 cur = ctxt->context->node ;
3650 do {
3651 if (cur->prev != NULL) {
3652 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3653 ;
3654 return(cur) ;
3655 }
3656
3657 cur = cur->parent;
3658 if (cur == NULL) return(NULL);
3659 if (cur == ctxt->context->doc->children) return(NULL);
3660 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3661 return(cur);
3662}
3663
3664/**
3665 * xmlXPathNextNamespace:
3666 * @ctxt: the XPath Parser context
3667 * @cur: the current attribute in the traversal
3668 *
3669 * Traversal function for the "namespace" direction
3670 * the namespace axis contains the namespace nodes of the context node;
3671 * the order of nodes on this axis is implementation-defined; the axis will
3672 * be empty unless the context node is an element
3673 *
3674 * Returns the next element following that axis
3675 */
3676xmlNodePtr
3677xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3678 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3679 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3680 if (ctxt->context->namespaces != NULL)
3681 xmlFree(ctxt->context->namespaces);
3682 ctxt->context->namespaces =
3683 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3684 if (ctxt->context->namespaces == NULL) return(NULL);
3685 ctxt->context->nsNr = 0;
3686 }
3687 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3688}
3689
3690/**
3691 * xmlXPathNextAttribute:
3692 * @ctxt: the XPath Parser context
3693 * @cur: the current attribute in the traversal
3694 *
3695 * Traversal function for the "attribute" direction
3696 * TODO: support DTD inherited default attributes
3697 *
3698 * Returns the next element following that axis
3699 */
3700xmlNodePtr
3701xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00003702 if (ctxt->context->node == NULL)
3703 return(NULL);
3704 if (ctxt->context->node->type != XML_ELEMENT_NODE)
3705 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003706 if (cur == NULL) {
3707 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3708 return(NULL);
3709 return((xmlNodePtr)ctxt->context->node->properties);
3710 }
3711 return((xmlNodePtr)cur->next);
3712}
3713
3714/************************************************************************
3715 * *
3716 * NodeTest Functions *
3717 * *
3718 ************************************************************************/
3719
Owen Taylor3473f882001-02-23 17:55:21 +00003720#define IS_FUNCTION 200
3721
Owen Taylor3473f882001-02-23 17:55:21 +00003722
3723/************************************************************************
3724 * *
3725 * Implicit tree core function library *
3726 * *
3727 ************************************************************************/
3728
3729/**
3730 * xmlXPathRoot:
3731 * @ctxt: the XPath Parser context
3732 *
3733 * Initialize the context to the root of the document
3734 */
3735void
3736xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
3737 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
3738 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3739}
3740
3741/************************************************************************
3742 * *
3743 * The explicit core function library *
3744 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
3745 * *
3746 ************************************************************************/
3747
3748
3749/**
3750 * xmlXPathLastFunction:
3751 * @ctxt: the XPath Parser context
3752 * @nargs: the number of arguments
3753 *
3754 * Implement the last() XPath function
3755 * number last()
3756 * The last function returns the number of nodes in the context node list.
3757 */
3758void
3759xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3760 CHECK_ARITY(0);
3761 if (ctxt->context->contextSize >= 0) {
3762 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
3763#ifdef DEBUG_EXPR
3764 xmlGenericError(xmlGenericErrorContext,
3765 "last() : %d\n", ctxt->context->contextSize);
3766#endif
3767 } else {
3768 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
3769 }
3770}
3771
3772/**
3773 * xmlXPathPositionFunction:
3774 * @ctxt: the XPath Parser context
3775 * @nargs: the number of arguments
3776 *
3777 * Implement the position() XPath function
3778 * number position()
3779 * The position function returns the position of the context node in the
3780 * context node list. The first position is 1, and so the last positionr
3781 * will be equal to last().
3782 */
3783void
3784xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3785 CHECK_ARITY(0);
3786 if (ctxt->context->proximityPosition >= 0) {
3787 valuePush(ctxt,
3788 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
3789#ifdef DEBUG_EXPR
3790 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
3791 ctxt->context->proximityPosition);
3792#endif
3793 } else {
3794 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
3795 }
3796}
3797
3798/**
3799 * xmlXPathCountFunction:
3800 * @ctxt: the XPath Parser context
3801 * @nargs: the number of arguments
3802 *
3803 * Implement the count() XPath function
3804 * number count(node-set)
3805 */
3806void
3807xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3808 xmlXPathObjectPtr cur;
3809
3810 CHECK_ARITY(1);
3811 if ((ctxt->value == NULL) ||
3812 ((ctxt->value->type != XPATH_NODESET) &&
3813 (ctxt->value->type != XPATH_XSLT_TREE)))
3814 XP_ERROR(XPATH_INVALID_TYPE);
3815 cur = valuePop(ctxt);
3816
Daniel Veillard911f49a2001-04-07 15:39:35 +00003817 if ((cur == NULL) || (cur->nodesetval == NULL))
3818 valuePush(ctxt, xmlXPathNewFloat((double) 0));
3819 else
3820 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Owen Taylor3473f882001-02-23 17:55:21 +00003821 xmlXPathFreeObject(cur);
3822}
3823
3824/**
3825 * xmlXPathIdFunction:
3826 * @ctxt: the XPath Parser context
3827 * @nargs: the number of arguments
3828 *
3829 * Implement the id() XPath function
3830 * node-set id(object)
3831 * The id function selects elements by their unique ID
3832 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
3833 * then the result is the union of the result of applying id to the
3834 * string value of each of the nodes in the argument node-set. When the
3835 * argument to id is of any other type, the argument is converted to a
3836 * string as if by a call to the string function; the string is split
3837 * into a whitespace-separated list of tokens (whitespace is any sequence
3838 * of characters matching the production S); the result is a node-set
3839 * containing the elements in the same document as the context node that
3840 * have a unique ID equal to any of the tokens in the list.
3841 */
3842void
3843xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3844 const xmlChar *tokens;
3845 const xmlChar *cur;
3846 xmlChar *ID;
3847 xmlAttrPtr attr;
3848 xmlNodePtr elem = NULL;
3849 xmlXPathObjectPtr ret, obj;
3850
3851 CHECK_ARITY(1);
3852 obj = valuePop(ctxt);
3853 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3854 if (obj->type == XPATH_NODESET) {
3855 xmlXPathObjectPtr newobj;
3856 int i;
3857
3858 ret = xmlXPathNewNodeSet(NULL);
3859
Daniel Veillard911f49a2001-04-07 15:39:35 +00003860 if (obj->nodesetval != NULL) {
3861 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3862 valuePush(ctxt,
3863 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
3864 xmlXPathStringFunction(ctxt, 1);
3865 xmlXPathIdFunction(ctxt, 1);
3866 newobj = valuePop(ctxt);
3867 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
3868 newobj->nodesetval);
3869 xmlXPathFreeObject(newobj);
3870 }
Owen Taylor3473f882001-02-23 17:55:21 +00003871 }
3872
3873 xmlXPathFreeObject(obj);
3874 valuePush(ctxt, ret);
3875 return;
3876 }
3877 if (obj->type != XPATH_STRING) {
3878 valuePush(ctxt, obj);
3879 xmlXPathStringFunction(ctxt, 1);
3880 obj = valuePop(ctxt);
3881 if (obj->type != XPATH_STRING) {
3882 xmlXPathFreeObject(obj);
3883 return;
3884 }
3885 }
3886 tokens = obj->stringval;
3887
3888 ret = xmlXPathNewNodeSet(NULL);
3889 valuePush(ctxt, ret);
3890 if (tokens == NULL) {
3891 xmlXPathFreeObject(obj);
3892 return;
3893 }
3894
3895 cur = tokens;
3896
3897 while (IS_BLANK(*cur)) cur++;
3898 while (*cur != 0) {
3899 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
3900 (*cur == '.') || (*cur == '-') ||
3901 (*cur == '_') || (*cur == ':') ||
3902 (IS_COMBINING(*cur)) ||
3903 (IS_EXTENDER(*cur)))
3904 cur++;
3905
3906 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
3907
3908 ID = xmlStrndup(tokens, cur - tokens);
3909 attr = xmlGetID(ctxt->context->doc, ID);
3910 if (attr != NULL) {
3911 elem = attr->parent;
3912 xmlXPathNodeSetAdd(ret->nodesetval, elem);
3913 }
3914 if (ID != NULL)
3915 xmlFree(ID);
3916
3917 while (IS_BLANK(*cur)) cur++;
3918 tokens = cur;
3919 }
3920 xmlXPathFreeObject(obj);
3921 return;
3922}
3923
3924/**
3925 * xmlXPathLocalNameFunction:
3926 * @ctxt: the XPath Parser context
3927 * @nargs: the number of arguments
3928 *
3929 * Implement the local-name() XPath function
3930 * string local-name(node-set?)
3931 * The local-name function returns a string containing the local part
3932 * of the name of the node in the argument node-set that is first in
3933 * document order. If the node-set is empty or the first node has no
3934 * name, an empty string is returned. If the argument is omitted it
3935 * defaults to the context node.
3936 */
3937void
3938xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3939 xmlXPathObjectPtr cur;
3940
3941 if (nargs == 0) {
3942 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3943 nargs = 1;
3944 }
3945
3946 CHECK_ARITY(1);
3947 if ((ctxt->value == NULL) ||
3948 ((ctxt->value->type != XPATH_NODESET) &&
3949 (ctxt->value->type != XPATH_XSLT_TREE)))
3950 XP_ERROR(XPATH_INVALID_TYPE);
3951 cur = valuePop(ctxt);
3952
Daniel Veillard911f49a2001-04-07 15:39:35 +00003953 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003954 valuePush(ctxt, xmlXPathNewCString(""));
3955 } else {
3956 int i = 0; /* Should be first in document order !!!!! */
3957 switch (cur->nodesetval->nodeTab[i]->type) {
3958 case XML_ELEMENT_NODE:
3959 case XML_ATTRIBUTE_NODE:
3960 case XML_PI_NODE:
3961 valuePush(ctxt,
3962 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
3963 break;
3964 case XML_NAMESPACE_DECL:
3965 valuePush(ctxt, xmlXPathNewString(
3966 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
3967 break;
3968 default:
3969 valuePush(ctxt, xmlXPathNewCString(""));
3970 }
3971 }
3972 xmlXPathFreeObject(cur);
3973}
3974
3975/**
3976 * xmlXPathNamespaceURIFunction:
3977 * @ctxt: the XPath Parser context
3978 * @nargs: the number of arguments
3979 *
3980 * Implement the namespace-uri() XPath function
3981 * string namespace-uri(node-set?)
3982 * The namespace-uri function returns a string containing the
3983 * namespace URI of the expanded name of the node in the argument
3984 * node-set that is first in document order. If the node-set is empty,
3985 * the first node has no name, or the expanded name has no namespace
3986 * URI, an empty string is returned. If the argument is omitted it
3987 * defaults to the context node.
3988 */
3989void
3990xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3991 xmlXPathObjectPtr cur;
3992
3993 if (nargs == 0) {
3994 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3995 nargs = 1;
3996 }
3997 CHECK_ARITY(1);
3998 if ((ctxt->value == NULL) ||
3999 ((ctxt->value->type != XPATH_NODESET) &&
4000 (ctxt->value->type != XPATH_XSLT_TREE)))
4001 XP_ERROR(XPATH_INVALID_TYPE);
4002 cur = valuePop(ctxt);
4003
Daniel Veillard911f49a2001-04-07 15:39:35 +00004004 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004005 valuePush(ctxt, xmlXPathNewCString(""));
4006 } else {
4007 int i = 0; /* Should be first in document order !!!!! */
4008 switch (cur->nodesetval->nodeTab[i]->type) {
4009 case XML_ELEMENT_NODE:
4010 case XML_ATTRIBUTE_NODE:
4011 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4012 valuePush(ctxt, xmlXPathNewCString(""));
4013 else
4014 valuePush(ctxt, xmlXPathNewString(
4015 cur->nodesetval->nodeTab[i]->ns->href));
4016 break;
4017 default:
4018 valuePush(ctxt, xmlXPathNewCString(""));
4019 }
4020 }
4021 xmlXPathFreeObject(cur);
4022}
4023
4024/**
4025 * xmlXPathNameFunction:
4026 * @ctxt: the XPath Parser context
4027 * @nargs: the number of arguments
4028 *
4029 * Implement the name() XPath function
4030 * string name(node-set?)
4031 * The name function returns a string containing a QName representing
4032 * the name of the node in the argument node-set that is first in documenti
4033 * order. The QName must represent the name with respect to the namespace
4034 * declarations in effect on the node whose name is being represented.
4035 * Typically, this will be the form in which the name occurred in the XML
4036 * source. This need not be the case if there are namespace declarations
4037 * in effect on the node that associate multiple prefixes with the same
4038 * namespace. However, an implementation may include information about
4039 * the original prefix in its representation of nodes; in this case, an
4040 * implementation can ensure that the returned string is always the same
4041 * as the QName used in the XML source. If the argument it omitted it
4042 * defaults to the context node.
4043 * Libxml keep the original prefix so the "real qualified name" used is
4044 * returned.
4045 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004046static void
Owen Taylor3473f882001-02-23 17:55:21 +00004047xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4048 xmlXPathObjectPtr cur;
4049
4050 if (nargs == 0) {
4051 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4052 nargs = 1;
4053 }
4054
4055 CHECK_ARITY(1);
4056 if ((ctxt->value == NULL) ||
4057 ((ctxt->value->type != XPATH_NODESET) &&
4058 (ctxt->value->type != XPATH_XSLT_TREE)))
4059 XP_ERROR(XPATH_INVALID_TYPE);
4060 cur = valuePop(ctxt);
4061
Daniel Veillard911f49a2001-04-07 15:39:35 +00004062 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004063 valuePush(ctxt, xmlXPathNewCString(""));
4064 } else {
4065 int i = 0; /* Should be first in document order !!!!! */
4066
4067 switch (cur->nodesetval->nodeTab[i]->type) {
4068 case XML_ELEMENT_NODE:
4069 case XML_ATTRIBUTE_NODE:
4070 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4071 valuePush(ctxt, xmlXPathNewString(
4072 cur->nodesetval->nodeTab[i]->name));
4073
4074 else {
4075 char name[2000];
Owen Taylor3473f882001-02-23 17:55:21 +00004076 snprintf(name, sizeof(name), "%s:%s",
4077 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4078 (char *) cur->nodesetval->nodeTab[i]->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004079 name[sizeof(name) - 1] = 0;
4080 valuePush(ctxt, xmlXPathNewCString(name));
4081 }
4082 break;
4083 default:
4084 valuePush(ctxt,
4085 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4086 xmlXPathLocalNameFunction(ctxt, 1);
4087 }
4088 }
4089 xmlXPathFreeObject(cur);
4090}
4091
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004092
4093/**
4094 * xmlXPathConvertString:
4095 * @val: an XPath object
4096 *
4097 * Converts an existing object to its string() equivalent
4098 *
4099 * Returns the new object, the old one is freed (or the operation
4100 * is done directly on @val)
4101 */
4102xmlXPathObjectPtr
4103xmlXPathConvertString(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004104 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004105
4106 if (val == NULL)
4107 return(xmlXPathNewCString(""));
4108 switch (val->type) {
4109 case XPATH_UNDEFINED:
4110#ifdef DEBUG_EXPR
4111 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4112#endif
4113 ret = xmlXPathNewCString("");
4114 break;
4115 case XPATH_XSLT_TREE:
4116 case XPATH_NODESET:
Daniel Veillard911f49a2001-04-07 15:39:35 +00004117 if ((val->nodesetval == NULL) || (val->nodesetval->nodeNr == 0)) {
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004118 ret = xmlXPathNewCString("");
4119 } else {
4120 xmlChar *res;
4121
4122 xmlXPathNodeSetSort(val->nodesetval);
4123 res = xmlNodeGetContent(val->nodesetval->nodeTab[0]);
4124 /* TODO: avoid allocating res to free it */
4125 ret = xmlXPathNewString(res);
4126 if (res != NULL)
4127 xmlFree(res);
4128 }
4129 break;
4130 case XPATH_STRING:
4131 return(val);
4132 case XPATH_BOOLEAN:
4133 if (val->boolval) ret = xmlXPathNewCString("true");
4134 else ret = xmlXPathNewCString("false");
4135 break;
4136 case XPATH_NUMBER: {
4137 char buf[100];
4138
4139 xmlXPathFormatNumber(val->floatval, buf, sizeof(buf));
4140 ret = xmlXPathNewCString(buf);
4141 break;
4142 }
4143 case XPATH_USERS:
4144 case XPATH_POINT:
4145 case XPATH_RANGE:
4146 case XPATH_LOCATIONSET:
4147 TODO
4148 ret = xmlXPathNewCString("");
4149 break;
4150 }
4151 xmlXPathFreeObject(val);
4152 return(ret);
4153}
4154
Owen Taylor3473f882001-02-23 17:55:21 +00004155/**
4156 * xmlXPathStringFunction:
4157 * @ctxt: the XPath Parser context
4158 * @nargs: the number of arguments
4159 *
4160 * Implement the string() XPath function
4161 * string string(object?)
4162 * he string function converts an object to a string as follows:
4163 * - A node-set is converted to a string by returning the value of
4164 * the node in the node-set that is first in document order.
4165 * If the node-set is empty, an empty string is returned.
4166 * - A number is converted to a string as follows
4167 * + NaN is converted to the string NaN
4168 * + positive zero is converted to the string 0
4169 * + negative zero is converted to the string 0
4170 * + positive infinity is converted to the string Infinity
4171 * + negative infinity is converted to the string -Infinity
4172 * + if the number is an integer, the number is represented in
4173 * decimal form as a Number with no decimal point and no leading
4174 * zeros, preceded by a minus sign (-) if the number is negative
4175 * + otherwise, the number is represented in decimal form as a
4176 * Number including a decimal point with at least one digit
4177 * before the decimal point and at least one digit after the
4178 * decimal point, preceded by a minus sign (-) if the number
4179 * is negative; there must be no leading zeros before the decimal
4180 * point apart possibly from the one required digit immediatelyi
4181 * before the decimal point; beyond the one required digit
4182 * after the decimal point there must be as many, but only as
4183 * many, more digits as are needed to uniquely distinguish the
4184 * number from all other IEEE 754 numeric values.
4185 * - The boolean false value is converted to the string false.
4186 * The boolean true value is converted to the string true.
4187 *
4188 * If the argument is omitted, it defaults to a node-set with the
4189 * context node as its only member.
4190 */
4191void
4192xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4193 xmlXPathObjectPtr cur;
4194
4195 if (nargs == 0) {
4196 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4197 nargs = 1;
4198 }
4199
4200 CHECK_ARITY(1);
4201 cur = valuePop(ctxt);
4202 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004203 cur = xmlXPathConvertString(cur);
4204 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004205}
4206
4207/**
4208 * xmlXPathStringLengthFunction:
4209 * @ctxt: the XPath Parser context
4210 * @nargs: the number of arguments
4211 *
4212 * Implement the string-length() XPath function
4213 * number string-length(string?)
4214 * The string-length returns the number of characters in the string
4215 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4216 * the context node converted to a string, in other words the value
4217 * of the context node.
4218 */
4219void
4220xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4221 xmlXPathObjectPtr cur;
4222
4223 if (nargs == 0) {
4224 if (ctxt->context->node == NULL) {
4225 valuePush(ctxt, xmlXPathNewFloat(0));
4226 } else {
4227 xmlChar *content;
4228
4229 content = xmlNodeGetContent(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004230 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00004231 xmlFree(content);
4232 }
4233 return;
4234 }
4235 CHECK_ARITY(1);
4236 CAST_TO_STRING;
4237 CHECK_TYPE(XPATH_STRING);
4238 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004239 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00004240 xmlXPathFreeObject(cur);
4241}
4242
4243/**
4244 * xmlXPathConcatFunction:
4245 * @ctxt: the XPath Parser context
4246 * @nargs: the number of arguments
4247 *
4248 * Implement the concat() XPath function
4249 * string concat(string, string, string*)
4250 * The concat function returns the concatenation of its arguments.
4251 */
4252void
4253xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4254 xmlXPathObjectPtr cur, newobj;
4255 xmlChar *tmp;
4256
4257 if (nargs < 2) {
4258 CHECK_ARITY(2);
4259 }
4260
4261 CAST_TO_STRING;
4262 cur = valuePop(ctxt);
4263 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4264 xmlXPathFreeObject(cur);
4265 return;
4266 }
4267 nargs--;
4268
4269 while (nargs > 0) {
4270 CAST_TO_STRING;
4271 newobj = valuePop(ctxt);
4272 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4273 xmlXPathFreeObject(newobj);
4274 xmlXPathFreeObject(cur);
4275 XP_ERROR(XPATH_INVALID_TYPE);
4276 }
4277 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4278 newobj->stringval = cur->stringval;
4279 cur->stringval = tmp;
4280
4281 xmlXPathFreeObject(newobj);
4282 nargs--;
4283 }
4284 valuePush(ctxt, cur);
4285}
4286
4287/**
4288 * xmlXPathContainsFunction:
4289 * @ctxt: the XPath Parser context
4290 * @nargs: the number of arguments
4291 *
4292 * Implement the contains() XPath function
4293 * boolean contains(string, string)
4294 * The contains function returns true if the first argument string
4295 * contains the second argument string, and otherwise returns false.
4296 */
4297void
4298xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4299 xmlXPathObjectPtr hay, needle;
4300
4301 CHECK_ARITY(2);
4302 CAST_TO_STRING;
4303 CHECK_TYPE(XPATH_STRING);
4304 needle = valuePop(ctxt);
4305 CAST_TO_STRING;
4306 hay = valuePop(ctxt);
4307 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4308 xmlXPathFreeObject(hay);
4309 xmlXPathFreeObject(needle);
4310 XP_ERROR(XPATH_INVALID_TYPE);
4311 }
4312 if (xmlStrstr(hay->stringval, needle->stringval))
4313 valuePush(ctxt, xmlXPathNewBoolean(1));
4314 else
4315 valuePush(ctxt, xmlXPathNewBoolean(0));
4316 xmlXPathFreeObject(hay);
4317 xmlXPathFreeObject(needle);
4318}
4319
4320/**
4321 * xmlXPathStartsWithFunction:
4322 * @ctxt: the XPath Parser context
4323 * @nargs: the number of arguments
4324 *
4325 * Implement the starts-with() XPath function
4326 * boolean starts-with(string, string)
4327 * The starts-with function returns true if the first argument string
4328 * starts with the second argument string, and otherwise returns false.
4329 */
4330void
4331xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4332 xmlXPathObjectPtr hay, needle;
4333 int n;
4334
4335 CHECK_ARITY(2);
4336 CAST_TO_STRING;
4337 CHECK_TYPE(XPATH_STRING);
4338 needle = valuePop(ctxt);
4339 CAST_TO_STRING;
4340 hay = valuePop(ctxt);
4341 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4342 xmlXPathFreeObject(hay);
4343 xmlXPathFreeObject(needle);
4344 XP_ERROR(XPATH_INVALID_TYPE);
4345 }
4346 n = xmlStrlen(needle->stringval);
4347 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4348 valuePush(ctxt, xmlXPathNewBoolean(0));
4349 else
4350 valuePush(ctxt, xmlXPathNewBoolean(1));
4351 xmlXPathFreeObject(hay);
4352 xmlXPathFreeObject(needle);
4353}
4354
4355/**
4356 * xmlXPathSubstringFunction:
4357 * @ctxt: the XPath Parser context
4358 * @nargs: the number of arguments
4359 *
4360 * Implement the substring() XPath function
4361 * string substring(string, number, number?)
4362 * The substring function returns the substring of the first argument
4363 * starting at the position specified in the second argument with
4364 * length specified in the third argument. For example,
4365 * substring("12345",2,3) returns "234". If the third argument is not
4366 * specified, it returns the substring starting at the position specified
4367 * in the second argument and continuing to the end of the string. For
4368 * example, substring("12345",2) returns "2345". More precisely, each
4369 * character in the string (see [3.6 Strings]) is considered to have a
4370 * numeric position: the position of the first character is 1, the position
4371 * of the second character is 2 and so on. The returned substring contains
4372 * those characters for which the position of the character is greater than
4373 * or equal to the second argument and, if the third argument is specified,
4374 * less than the sum of the second and third arguments; the comparisons
4375 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4376 * - substring("12345", 1.5, 2.6) returns "234"
4377 * - substring("12345", 0, 3) returns "12"
4378 * - substring("12345", 0 div 0, 3) returns ""
4379 * - substring("12345", 1, 0 div 0) returns ""
4380 * - substring("12345", -42, 1 div 0) returns "12345"
4381 * - substring("12345", -1 div 0, 1 div 0) returns ""
4382 */
4383void
4384xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4385 xmlXPathObjectPtr str, start, len;
4386 double le, in;
4387 int i, l;
4388 xmlChar *ret;
4389
4390 /*
Daniel Veillarde043ee12001-04-16 14:08:07 +00004391 * TODO: need to be converted to UTF8 strings
Owen Taylor3473f882001-02-23 17:55:21 +00004392 */
4393 if (nargs < 2) {
4394 CHECK_ARITY(2);
4395 }
4396 if (nargs > 3) {
4397 CHECK_ARITY(3);
4398 }
4399 if (nargs == 3) {
4400 CAST_TO_NUMBER;
4401 CHECK_TYPE(XPATH_NUMBER);
4402 len = valuePop(ctxt);
4403 le = len->floatval;
4404 xmlXPathFreeObject(len);
4405 } else {
4406 le = 2000000000;
4407 }
4408 CAST_TO_NUMBER;
4409 CHECK_TYPE(XPATH_NUMBER);
4410 start = valuePop(ctxt);
4411 in = start->floatval;
4412 xmlXPathFreeObject(start);
4413 CAST_TO_STRING;
4414 CHECK_TYPE(XPATH_STRING);
4415 str = valuePop(ctxt);
4416 le += in;
4417
4418 /* integer index of the first char */
4419 i = (int) in;
4420 if (((double)i) != in) i++;
4421
4422 /* integer index of the last char */
4423 l = (int) le;
4424 if (((double)l) != le) l++;
4425
4426 /* back to a zero based len */
4427 i--;
4428 l--;
4429
4430 /* check against the string len */
4431 if (l > 1024) {
4432 l = xmlStrlen(str->stringval);
4433 }
4434 if (i < 0) {
4435 i = 0;
4436 }
4437
4438 /* number of chars to copy */
4439 l -= i;
4440
4441 ret = xmlStrsub(str->stringval, i, l);
4442 if (ret == NULL)
4443 valuePush(ctxt, xmlXPathNewCString(""));
4444 else {
4445 valuePush(ctxt, xmlXPathNewString(ret));
4446 xmlFree(ret);
4447 }
4448 xmlXPathFreeObject(str);
4449}
4450
4451/**
4452 * xmlXPathSubstringBeforeFunction:
4453 * @ctxt: the XPath Parser context
4454 * @nargs: the number of arguments
4455 *
4456 * Implement the substring-before() XPath function
4457 * string substring-before(string, string)
4458 * The substring-before function returns the substring of the first
4459 * argument string that precedes the first occurrence of the second
4460 * argument string in the first argument string, or the empty string
4461 * if the first argument string does not contain the second argument
4462 * string. For example, substring-before("1999/04/01","/") returns 1999.
4463 */
4464void
4465xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4466 xmlXPathObjectPtr str;
4467 xmlXPathObjectPtr find;
4468 xmlBufferPtr target;
4469 const xmlChar *point;
4470 int offset;
4471
4472 CHECK_ARITY(2);
4473 CAST_TO_STRING;
4474 find = valuePop(ctxt);
4475 CAST_TO_STRING;
4476 str = valuePop(ctxt);
4477
4478 target = xmlBufferCreate();
4479 if (target) {
4480 point = xmlStrstr(str->stringval, find->stringval);
4481 if (point) {
4482 offset = (int)(point - str->stringval);
4483 xmlBufferAdd(target, str->stringval, offset);
4484 }
4485 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4486 xmlBufferFree(target);
4487 }
4488
4489 xmlXPathFreeObject(str);
4490 xmlXPathFreeObject(find);
4491}
4492
4493/**
4494 * xmlXPathSubstringAfterFunction:
4495 * @ctxt: the XPath Parser context
4496 * @nargs: the number of arguments
4497 *
4498 * Implement the substring-after() XPath function
4499 * string substring-after(string, string)
4500 * The substring-after function returns the substring of the first
4501 * argument string that follows the first occurrence of the second
4502 * argument string in the first argument string, or the empty stringi
4503 * if the first argument string does not contain the second argument
4504 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4505 * and substring-after("1999/04/01","19") returns 99/04/01.
4506 */
4507void
4508xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4509 xmlXPathObjectPtr str;
4510 xmlXPathObjectPtr find;
4511 xmlBufferPtr target;
4512 const xmlChar *point;
4513 int offset;
4514
4515 CHECK_ARITY(2);
4516 CAST_TO_STRING;
4517 find = valuePop(ctxt);
4518 CAST_TO_STRING;
4519 str = valuePop(ctxt);
4520
4521 target = xmlBufferCreate();
4522 if (target) {
4523 point = xmlStrstr(str->stringval, find->stringval);
4524 if (point) {
4525 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4526 xmlBufferAdd(target, &str->stringval[offset],
4527 xmlStrlen(str->stringval) - offset);
4528 }
4529 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4530 xmlBufferFree(target);
4531 }
4532
4533 xmlXPathFreeObject(str);
4534 xmlXPathFreeObject(find);
4535}
4536
4537/**
4538 * xmlXPathNormalizeFunction:
4539 * @ctxt: the XPath Parser context
4540 * @nargs: the number of arguments
4541 *
4542 * Implement the normalize-space() XPath function
4543 * string normalize-space(string?)
4544 * The normalize-space function returns the argument string with white
4545 * space normalized by stripping leading and trailing whitespace
4546 * and replacing sequences of whitespace characters by a single
4547 * space. Whitespace characters are the same allowed by the S production
4548 * in XML. If the argument is omitted, it defaults to the context
4549 * node converted to a string, in other words the value of the context node.
4550 */
4551void
4552xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4553 xmlXPathObjectPtr obj = NULL;
4554 xmlChar *source = NULL;
4555 xmlBufferPtr target;
4556 xmlChar blank;
4557
4558 if (nargs == 0) {
4559 /* Use current context node */
4560 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4561 xmlXPathStringFunction(ctxt, 1);
4562 nargs = 1;
4563 }
4564
4565 CHECK_ARITY(1);
4566 CAST_TO_STRING;
4567 CHECK_TYPE(XPATH_STRING);
4568 obj = valuePop(ctxt);
4569 source = obj->stringval;
4570
4571 target = xmlBufferCreate();
4572 if (target && source) {
4573
4574 /* Skip leading whitespaces */
4575 while (IS_BLANK(*source))
4576 source++;
4577
4578 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4579 blank = 0;
4580 while (*source) {
4581 if (IS_BLANK(*source)) {
4582 blank = *source;
4583 } else {
4584 if (blank) {
4585 xmlBufferAdd(target, &blank, 1);
4586 blank = 0;
4587 }
4588 xmlBufferAdd(target, source, 1);
4589 }
4590 source++;
4591 }
4592
4593 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4594 xmlBufferFree(target);
4595 }
4596 xmlXPathFreeObject(obj);
4597}
4598
4599/**
4600 * xmlXPathTranslateFunction:
4601 * @ctxt: the XPath Parser context
4602 * @nargs: the number of arguments
4603 *
4604 * Implement the translate() XPath function
4605 * string translate(string, string, string)
4606 * The translate function returns the first argument string with
4607 * occurrences of characters in the second argument string replaced
4608 * by the character at the corresponding position in the third argument
4609 * string. For example, translate("bar","abc","ABC") returns the string
4610 * BAr. If there is a character in the second argument string with no
4611 * character at a corresponding position in the third argument string
4612 * (because the second argument string is longer than the third argument
4613 * string), then occurrences of that character in the first argument
4614 * string are removed. For example, translate("--aaa--","abc-","ABC")
4615 * returns "AAA". If a character occurs more than once in second
4616 * argument string, then the first occurrence determines the replacement
4617 * character. If the third argument string is longer than the second
4618 * argument string, then excess characters are ignored.
4619 */
4620void
4621xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00004622 xmlXPathObjectPtr str;
4623 xmlXPathObjectPtr from;
4624 xmlXPathObjectPtr to;
4625 xmlBufferPtr target;
4626 int i, offset, max;
4627 xmlChar ch;
4628 const xmlChar *point;
Owen Taylor3473f882001-02-23 17:55:21 +00004629
Daniel Veillarde043ee12001-04-16 14:08:07 +00004630 /*
4631 * TODO: need to be converted to UTF8 strings
4632 */
4633 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00004634
Daniel Veillarde043ee12001-04-16 14:08:07 +00004635 CAST_TO_STRING;
4636 to = valuePop(ctxt);
4637 CAST_TO_STRING;
4638 from = valuePop(ctxt);
4639 CAST_TO_STRING;
4640 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00004641
Daniel Veillarde043ee12001-04-16 14:08:07 +00004642 target = xmlBufferCreate();
4643 if (target) {
4644 max = xmlStrlen(to->stringval);
4645 for (i = 0; (ch = str->stringval[i]); i++) {
4646 point = xmlStrchr(from->stringval, ch);
4647 if (point) {
4648 offset = (int)(point - from->stringval);
4649 if (offset < max)
4650 xmlBufferAdd(target, &to->stringval[offset], 1);
4651 } else
4652 xmlBufferAdd(target, &ch, 1);
4653 }
Owen Taylor3473f882001-02-23 17:55:21 +00004654 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00004655 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4656 xmlBufferFree(target);
4657 xmlXPathFreeObject(str);
4658 xmlXPathFreeObject(from);
4659 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00004660}
4661
4662/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004663 * xmlXPathConvertBoolean:
4664 * @val: an XPath object
4665 *
4666 * Converts an existing object to its boolean() equivalent
4667 *
4668 * Returns the new object, the old one is freed (or the operation
4669 * is done directly on @val)
4670 */
4671xmlXPathObjectPtr
4672xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
4673 int res = 0;
4674
4675 if (val == NULL)
4676 return(NULL);
4677 switch (val->type) {
4678 case XPATH_NODESET:
4679 case XPATH_XSLT_TREE:
4680 if ((val->nodesetval == NULL) ||
4681 (val->nodesetval->nodeNr == 0)) res = 0;
4682 else
4683 res = 1;
4684 break;
4685 case XPATH_STRING:
4686 if ((val->stringval == NULL) ||
4687 (val->stringval[0] == 0)) res = 0;
4688 else
4689 res = 1;
4690 break;
4691 case XPATH_BOOLEAN:
4692 return(val);
4693 case XPATH_NUMBER:
4694 if (val->floatval) res = 1;
4695 break;
4696 default:
4697 STRANGE
4698 }
4699 xmlXPathFreeObject(val);
4700 return(xmlXPathNewBoolean(res));
4701}
4702
4703/**
Owen Taylor3473f882001-02-23 17:55:21 +00004704 * xmlXPathBooleanFunction:
4705 * @ctxt: the XPath Parser context
4706 * @nargs: the number of arguments
4707 *
4708 * Implement the boolean() XPath function
4709 * boolean boolean(object)
4710 * he boolean function converts its argument to a boolean as follows:
4711 * - a number is true if and only if it is neither positive or
4712 * negative zero nor NaN
4713 * - a node-set is true if and only if it is non-empty
4714 * - a string is true if and only if its length is non-zero
4715 */
4716void
4717xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4718 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00004719
4720 CHECK_ARITY(1);
4721 cur = valuePop(ctxt);
4722 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004723 cur = xmlXPathConvertBoolean(cur);
4724 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004725}
4726
4727/**
4728 * xmlXPathNotFunction:
4729 * @ctxt: the XPath Parser context
4730 * @nargs: the number of arguments
4731 *
4732 * Implement the not() XPath function
4733 * boolean not(boolean)
4734 * The not function returns true if its argument is false,
4735 * and false otherwise.
4736 */
4737void
4738xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4739 CHECK_ARITY(1);
4740 CAST_TO_BOOLEAN;
4741 CHECK_TYPE(XPATH_BOOLEAN);
4742 ctxt->value->boolval = ! ctxt->value->boolval;
4743}
4744
4745/**
4746 * xmlXPathTrueFunction:
4747 * @ctxt: the XPath Parser context
4748 * @nargs: the number of arguments
4749 *
4750 * Implement the true() XPath function
4751 * boolean true()
4752 */
4753void
4754xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4755 CHECK_ARITY(0);
4756 valuePush(ctxt, xmlXPathNewBoolean(1));
4757}
4758
4759/**
4760 * xmlXPathFalseFunction:
4761 * @ctxt: the XPath Parser context
4762 * @nargs: the number of arguments
4763 *
4764 * Implement the false() XPath function
4765 * boolean false()
4766 */
4767void
4768xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4769 CHECK_ARITY(0);
4770 valuePush(ctxt, xmlXPathNewBoolean(0));
4771}
4772
4773/**
4774 * xmlXPathLangFunction:
4775 * @ctxt: the XPath Parser context
4776 * @nargs: the number of arguments
4777 *
4778 * Implement the lang() XPath function
4779 * boolean lang(string)
4780 * The lang function returns true or false depending on whether the
4781 * language of the context node as specified by xml:lang attributes
4782 * is the same as or is a sublanguage of the language specified by
4783 * the argument string. The language of the context node is determined
4784 * by the value of the xml:lang attribute on the context node, or, if
4785 * the context node has no xml:lang attribute, by the value of the
4786 * xml:lang attribute on the nearest ancestor of the context node that
4787 * has an xml:lang attribute. If there is no such attribute, then lang
4788 * returns false. If there is such an attribute, then lang returns
4789 * true if the attribute value is equal to the argument ignoring case,
4790 * or if there is some suffix starting with - such that the attribute
4791 * value is equal to the argument ignoring that suffix of the attribute
4792 * value and ignoring case.
4793 */
4794void
4795xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4796 xmlXPathObjectPtr val;
4797 const xmlChar *theLang;
4798 const xmlChar *lang;
4799 int ret = 0;
4800 int i;
4801
4802 CHECK_ARITY(1);
4803 CAST_TO_STRING;
4804 CHECK_TYPE(XPATH_STRING);
4805 val = valuePop(ctxt);
4806 lang = val->stringval;
4807 theLang = xmlNodeGetLang(ctxt->context->node);
4808 if ((theLang != NULL) && (lang != NULL)) {
4809 for (i = 0;lang[i] != 0;i++)
4810 if (toupper(lang[i]) != toupper(theLang[i]))
4811 goto not_equal;
4812 ret = 1;
4813 }
4814not_equal:
4815 xmlXPathFreeObject(val);
4816 valuePush(ctxt, xmlXPathNewBoolean(ret));
4817}
4818
4819/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004820 * xmlXPathConvertNumber:
4821 * @val: an XPath object
4822 *
4823 * Converts an existing object to its number() equivalent
4824 *
4825 * Returns the new object, the old one is freed (or the operation
4826 * is done directly on @val)
4827 */
4828xmlXPathObjectPtr
4829xmlXPathConvertNumber(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004830 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004831 double res;
4832
4833 if (val == NULL)
4834 return(xmlXPathNewFloat(0.0));
4835 switch (val->type) {
4836 case XPATH_UNDEFINED:
4837#ifdef DEBUG_EXPR
4838 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
4839#endif
4840 ret = xmlXPathNewFloat(0.0);
4841 break;
4842 case XPATH_XSLT_TREE:
4843 case XPATH_NODESET:
4844 val = xmlXPathConvertString(val);
4845 /* no break on purpose */
4846 case XPATH_STRING:
4847 res = xmlXPathStringEvalNumber(val->stringval);
4848 ret = xmlXPathNewFloat(res);
4849 break;
4850 case XPATH_BOOLEAN:
4851 if (val->boolval) ret = xmlXPathNewFloat(1.0);
4852 else ret = xmlXPathNewFloat(0.0);
4853 break;
4854 case XPATH_NUMBER:
4855 return(val);
4856 case XPATH_USERS:
4857 case XPATH_POINT:
4858 case XPATH_RANGE:
4859 case XPATH_LOCATIONSET:
4860 TODO
4861 ret = xmlXPathNewFloat(0.0);
4862 break;
4863 }
4864 xmlXPathFreeObject(val);
4865 return(ret);
4866}
4867
4868/**
Owen Taylor3473f882001-02-23 17:55:21 +00004869 * xmlXPathNumberFunction:
4870 * @ctxt: the XPath Parser context
4871 * @nargs: the number of arguments
4872 *
4873 * Implement the number() XPath function
4874 * number number(object?)
4875 */
4876void
4877xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4878 xmlXPathObjectPtr cur;
4879 double res;
4880
4881 if (nargs == 0) {
4882 if (ctxt->context->node == NULL) {
4883 valuePush(ctxt, xmlXPathNewFloat(0.0));
4884 } else {
4885 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
4886
4887 res = xmlXPathStringEvalNumber(content);
4888 valuePush(ctxt, xmlXPathNewFloat(res));
4889 xmlFree(content);
4890 }
4891 return;
4892 }
4893
4894 CHECK_ARITY(1);
4895 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004896 cur = xmlXPathConvertNumber(cur);
4897 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004898}
4899
4900/**
4901 * xmlXPathSumFunction:
4902 * @ctxt: the XPath Parser context
4903 * @nargs: the number of arguments
4904 *
4905 * Implement the sum() XPath function
4906 * number sum(node-set)
4907 * The sum function returns the sum of the values of the nodes in
4908 * the argument node-set.
4909 */
4910void
4911xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4912 xmlXPathObjectPtr cur;
4913 int i;
4914
4915 CHECK_ARITY(1);
4916 if ((ctxt->value == NULL) ||
4917 ((ctxt->value->type != XPATH_NODESET) &&
4918 (ctxt->value->type != XPATH_XSLT_TREE)))
4919 XP_ERROR(XPATH_INVALID_TYPE);
4920 cur = valuePop(ctxt);
4921
Daniel Veillardd8df6c02001-04-05 16:54:14 +00004922 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004923 valuePush(ctxt, xmlXPathNewFloat(0.0));
4924 } else {
4925 valuePush(ctxt,
4926 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
4927 xmlXPathNumberFunction(ctxt, 1);
4928 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
4929 valuePush(ctxt,
4930 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4931 xmlXPathAddValues(ctxt);
4932 }
4933 }
4934 xmlXPathFreeObject(cur);
4935}
4936
4937/**
4938 * xmlXPathFloorFunction:
4939 * @ctxt: the XPath Parser context
4940 * @nargs: the number of arguments
4941 *
4942 * Implement the floor() XPath function
4943 * number floor(number)
4944 * The floor function returns the largest (closest to positive infinity)
4945 * number that is not greater than the argument and that is an integer.
4946 */
4947void
4948xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4949 CHECK_ARITY(1);
4950 CAST_TO_NUMBER;
4951 CHECK_TYPE(XPATH_NUMBER);
4952#if 0
4953 ctxt->value->floatval = floor(ctxt->value->floatval);
4954#else
4955 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
4956 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
4957#endif
4958}
4959
4960/**
4961 * xmlXPathCeilingFunction:
4962 * @ctxt: the XPath Parser context
4963 * @nargs: the number of arguments
4964 *
4965 * Implement the ceiling() XPath function
4966 * number ceiling(number)
4967 * The ceiling function returns the smallest (closest to negative infinity)
4968 * number that is not less than the argument and that is an integer.
4969 */
4970void
4971xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4972 double f;
4973
4974 CHECK_ARITY(1);
4975 CAST_TO_NUMBER;
4976 CHECK_TYPE(XPATH_NUMBER);
4977
4978#if 0
4979 ctxt->value->floatval = ceil(ctxt->value->floatval);
4980#else
4981 f = (double)((int) ctxt->value->floatval);
4982 if (f != ctxt->value->floatval)
4983 ctxt->value->floatval = f + 1;
4984#endif
4985}
4986
4987/**
4988 * xmlXPathRoundFunction:
4989 * @ctxt: the XPath Parser context
4990 * @nargs: the number of arguments
4991 *
4992 * Implement the round() XPath function
4993 * number round(number)
4994 * The round function returns the number that is closest to the
4995 * argument and that is an integer. If there are two such numbers,
4996 * then the one that is even is returned.
4997 */
4998void
4999xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5000 double f;
5001
5002 CHECK_ARITY(1);
5003 CAST_TO_NUMBER;
5004 CHECK_TYPE(XPATH_NUMBER);
5005
5006 if ((ctxt->value->floatval == xmlXPathNAN) ||
5007 (ctxt->value->floatval == xmlXPathPINF) ||
5008 (ctxt->value->floatval == xmlXPathNINF) ||
5009 (ctxt->value->floatval == 0.0))
5010 return;
5011
5012#if 0
5013 f = floor(ctxt->value->floatval);
5014#else
5015 f = (double)((int) ctxt->value->floatval);
5016#endif
5017 if (ctxt->value->floatval < f + 0.5)
5018 ctxt->value->floatval = f;
5019 else
5020 ctxt->value->floatval = f + 1;
5021}
5022
5023/************************************************************************
5024 * *
5025 * The Parser *
5026 * *
5027 ************************************************************************/
5028
5029/*
5030 * a couple of forward declarations since we use a recursive call based
5031 * implementation.
5032 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005033static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005034static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005035static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005036#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005037static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
5038#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00005039#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005040static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005041#endif
5042
5043/**
5044 * xmlXPathParseNCName:
5045 * @ctxt: the XPath Parser context
5046 *
5047 * parse an XML namespace non qualified name.
5048 *
5049 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
5050 *
5051 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
5052 * CombiningChar | Extender
5053 *
5054 * Returns the namespace name or NULL
5055 */
5056
5057xmlChar *
5058xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
5059 const xmlChar *q;
5060 xmlChar *ret = NULL;
5061
5062 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5063 q = NEXT;
5064
5065 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5066 (CUR == '.') || (CUR == '-') ||
5067 (CUR == '_') ||
5068 (IS_COMBINING(CUR)) ||
5069 (IS_EXTENDER(CUR)))
5070 NEXT;
5071
5072 ret = xmlStrndup(q, CUR_PTR - q);
5073
5074 return(ret);
5075}
5076
5077/**
5078 * xmlXPathParseQName:
5079 * @ctxt: the XPath Parser context
5080 * @prefix: a xmlChar **
5081 *
5082 * parse an XML qualified name
5083 *
5084 * [NS 5] QName ::= (Prefix ':')? LocalPart
5085 *
5086 * [NS 6] Prefix ::= NCName
5087 *
5088 * [NS 7] LocalPart ::= NCName
5089 *
5090 * Returns the function returns the local part, and prefix is updated
5091 * to get the Prefix if any.
5092 */
5093
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005094static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005095xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
5096 xmlChar *ret = NULL;
5097
5098 *prefix = NULL;
5099 ret = xmlXPathParseNCName(ctxt);
5100 if (CUR == ':') {
5101 *prefix = ret;
5102 NEXT;
5103 ret = xmlXPathParseNCName(ctxt);
5104 }
5105 return(ret);
5106}
5107
5108/**
5109 * xmlXPathParseName:
5110 * @ctxt: the XPath Parser context
5111 *
5112 * parse an XML name
5113 *
5114 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5115 * CombiningChar | Extender
5116 *
5117 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5118 *
5119 * Returns the namespace name or NULL
5120 */
5121
5122xmlChar *
5123xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
5124 const xmlChar *q;
5125 xmlChar *ret = NULL;
5126
5127 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5128 q = NEXT;
5129
5130 /* TODO Make this UTF8 compliant !!! */
5131 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5132 (CUR == '.') || (CUR == '-') ||
5133 (CUR == '_') || (CUR == ':') ||
5134 (IS_COMBINING(CUR)) ||
5135 (IS_EXTENDER(CUR)))
5136 NEXT;
5137
5138 ret = xmlStrndup(q, CUR_PTR - q);
5139
5140 return(ret);
5141}
5142
5143/**
5144 * xmlXPathStringEvalNumber:
5145 * @str: A string to scan
5146 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00005147 * [30a] Float ::= Number ('e' Digits?)?
5148 *
Owen Taylor3473f882001-02-23 17:55:21 +00005149 * [30] Number ::= Digits ('.' Digits?)?
5150 * | '.' Digits
5151 * [31] Digits ::= [0-9]+
5152 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005153 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00005154 * In complement of the Number expression, this function also handles
5155 * negative values : '-' Number.
5156 *
5157 * Returns the double value.
5158 */
5159double
5160xmlXPathStringEvalNumber(const xmlChar *str) {
5161 const xmlChar *cur = str;
5162 double ret = 0.0;
5163 double mult = 1;
5164 int ok = 0;
5165 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005166 int exponent = 0;
5167 int is_exponent_negative = 0;
5168
Owen Taylor3473f882001-02-23 17:55:21 +00005169 while (IS_BLANK(*cur)) cur++;
5170 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5171 return(xmlXPathNAN);
5172 }
5173 if (*cur == '-') {
5174 isneg = 1;
5175 cur++;
5176 }
5177 while ((*cur >= '0') && (*cur <= '9')) {
5178 ret = ret * 10 + (*cur - '0');
5179 ok = 1;
5180 cur++;
5181 }
5182 if (*cur == '.') {
5183 cur++;
5184 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5185 return(xmlXPathNAN);
5186 }
5187 while ((*cur >= '0') && (*cur <= '9')) {
5188 mult /= 10;
5189 ret = ret + (*cur - '0') * mult;
5190 cur++;
5191 }
5192 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00005193 if ((*cur == 'e') || (*cur == 'E')) {
5194 cur++;
5195 if (*cur == '-') {
5196 is_exponent_negative = 1;
5197 cur++;
5198 }
5199 while ((*cur >= '0') && (*cur <= '9')) {
5200 exponent = exponent * 10 + (*cur - '0');
5201 cur++;
5202 }
5203 }
Owen Taylor3473f882001-02-23 17:55:21 +00005204 while (IS_BLANK(*cur)) cur++;
5205 if (*cur != 0) return(xmlXPathNAN);
5206 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005207 if (is_exponent_negative) exponent = -exponent;
5208 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00005209 return(ret);
5210}
5211
5212/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005213 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00005214 * @ctxt: the XPath Parser context
5215 *
5216 * [30] Number ::= Digits ('.' Digits?)?
5217 * | '.' Digits
5218 * [31] Digits ::= [0-9]+
5219 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005220 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00005221 *
5222 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005223static void
5224xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005225 double ret = 0.0;
5226 double mult = 1;
5227 int ok = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005228 int exponent = 0;
5229 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005230
5231 CHECK_ERROR;
5232 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5233 XP_ERROR(XPATH_NUMBER_ERROR);
5234 }
5235 while ((CUR >= '0') && (CUR <= '9')) {
5236 ret = ret * 10 + (CUR - '0');
5237 ok = 1;
5238 NEXT;
5239 }
5240 if (CUR == '.') {
5241 NEXT;
5242 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5243 XP_ERROR(XPATH_NUMBER_ERROR);
5244 }
5245 while ((CUR >= '0') && (CUR <= '9')) {
5246 mult /= 10;
5247 ret = ret + (CUR - '0') * mult;
5248 NEXT;
5249 }
5250 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00005251 if ((CUR == 'e') || (CUR == 'E')) {
5252 NEXT;
5253 if (CUR == '-') {
5254 is_exponent_negative = 1;
5255 NEXT;
5256 }
5257 while ((CUR >= '0') && (CUR <= '9')) {
5258 exponent = exponent * 10 + (CUR - '0');
5259 NEXT;
5260 }
5261 }
5262 if (is_exponent_negative)
5263 exponent = -exponent;
5264 ret *= pow(10.0, (double)exponent);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005265 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
5266 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005267}
5268
5269/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005270 * xmlXPathParseLiteral:
5271 * @ctxt: the XPath Parser context
5272 *
5273 * Parse a Literal
5274 *
5275 * [29] Literal ::= '"' [^"]* '"'
5276 * | "'" [^']* "'"
5277 *
5278 * Returns the value found or NULL in case of error
5279 */
5280static xmlChar *
5281xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
5282 const xmlChar *q;
5283 xmlChar *ret = NULL;
5284
5285 if (CUR == '"') {
5286 NEXT;
5287 q = CUR_PTR;
5288 while ((IS_CHAR(CUR)) && (CUR != '"'))
5289 NEXT;
5290 if (!IS_CHAR(CUR)) {
5291 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5292 } else {
5293 ret = xmlStrndup(q, CUR_PTR - q);
5294 NEXT;
5295 }
5296 } else if (CUR == '\'') {
5297 NEXT;
5298 q = CUR_PTR;
5299 while ((IS_CHAR(CUR)) && (CUR != '\''))
5300 NEXT;
5301 if (!IS_CHAR(CUR)) {
5302 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5303 } else {
5304 ret = xmlStrndup(q, CUR_PTR - q);
5305 NEXT;
5306 }
5307 } else {
5308 XP_ERROR0(XPATH_START_LITERAL_ERROR);
5309 }
5310 return(ret);
5311}
5312
5313/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005314 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00005315 * @ctxt: the XPath Parser context
5316 *
5317 * Parse a Literal and push it on the stack.
5318 *
5319 * [29] Literal ::= '"' [^"]* '"'
5320 * | "'" [^']* "'"
5321 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005322 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00005323 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005324static void
5325xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005326 const xmlChar *q;
5327 xmlChar *ret = NULL;
5328
5329 if (CUR == '"') {
5330 NEXT;
5331 q = CUR_PTR;
5332 while ((IS_CHAR(CUR)) && (CUR != '"'))
5333 NEXT;
5334 if (!IS_CHAR(CUR)) {
5335 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5336 } else {
5337 ret = xmlStrndup(q, CUR_PTR - q);
5338 NEXT;
5339 }
5340 } else if (CUR == '\'') {
5341 NEXT;
5342 q = CUR_PTR;
5343 while ((IS_CHAR(CUR)) && (CUR != '\''))
5344 NEXT;
5345 if (!IS_CHAR(CUR)) {
5346 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5347 } else {
5348 ret = xmlStrndup(q, CUR_PTR - q);
5349 NEXT;
5350 }
5351 } else {
5352 XP_ERROR(XPATH_START_LITERAL_ERROR);
5353 }
5354 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005355 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
5356 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005357 xmlFree(ret);
5358}
5359
5360/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005361 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00005362 * @ctxt: the XPath Parser context
5363 *
5364 * Parse a VariableReference, evaluate it and push it on the stack.
5365 *
5366 * The variable bindings consist of a mapping from variable names
5367 * to variable values. The value of a variable is an object, which
5368 * of any of the types that are possible for the value of an expression,
5369 * and may also be of additional types not specified here.
5370 *
5371 * Early evaluation is possible since:
5372 * The variable bindings [...] used to evaluate a subexpression are
5373 * always the same as those used to evaluate the containing expression.
5374 *
5375 * [36] VariableReference ::= '$' QName
5376 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005377static void
5378xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005379 xmlChar *name;
5380 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005381
5382 SKIP_BLANKS;
5383 if (CUR != '$') {
5384 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5385 }
5386 NEXT;
5387 name = xmlXPathParseQName(ctxt, &prefix);
5388 if (name == NULL) {
5389 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5390 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005391 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005392 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
5393 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005394 SKIP_BLANKS;
5395}
5396
5397/**
5398 * xmlXPathIsNodeType:
5399 * @ctxt: the XPath Parser context
5400 * @name: a name string
5401 *
5402 * Is the name given a NodeType one.
5403 *
5404 * [38] NodeType ::= 'comment'
5405 * | 'text'
5406 * | 'processing-instruction'
5407 * | 'node'
5408 *
5409 * Returns 1 if true 0 otherwise
5410 */
5411int
5412xmlXPathIsNodeType(const xmlChar *name) {
5413 if (name == NULL)
5414 return(0);
5415
5416 if (xmlStrEqual(name, BAD_CAST "comment"))
5417 return(1);
5418 if (xmlStrEqual(name, BAD_CAST "text"))
5419 return(1);
5420 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5421 return(1);
5422 if (xmlStrEqual(name, BAD_CAST "node"))
5423 return(1);
5424 return(0);
5425}
5426
5427/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005428 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00005429 * @ctxt: the XPath Parser context
5430 *
5431 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5432 * [17] Argument ::= Expr
5433 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005434 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00005435 * pushed on the stack
5436 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005437static void
5438xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005439 xmlChar *name;
5440 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005441 int nbargs = 0;
5442
5443 name = xmlXPathParseQName(ctxt, &prefix);
5444 if (name == NULL) {
5445 XP_ERROR(XPATH_EXPR_ERROR);
5446 }
5447 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005448#ifdef DEBUG_EXPR
5449 if (prefix == NULL)
5450 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5451 name);
5452 else
5453 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5454 prefix, name);
5455#endif
5456
Owen Taylor3473f882001-02-23 17:55:21 +00005457 if (CUR != '(') {
5458 XP_ERROR(XPATH_EXPR_ERROR);
5459 }
5460 NEXT;
5461 SKIP_BLANKS;
5462
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005463 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005464 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005465 int op1 = ctxt->comp->last;
5466 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005467 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005468 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005469 nbargs++;
5470 if (CUR == ')') break;
5471 if (CUR != ',') {
5472 XP_ERROR(XPATH_EXPR_ERROR);
5473 }
5474 NEXT;
5475 SKIP_BLANKS;
5476 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005477 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
5478 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005479 NEXT;
5480 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005481}
5482
5483/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005484 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005485 * @ctxt: the XPath Parser context
5486 *
5487 * [15] PrimaryExpr ::= VariableReference
5488 * | '(' Expr ')'
5489 * | Literal
5490 * | Number
5491 * | FunctionCall
5492 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005493 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005494 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005495static void
5496xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005497 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005498 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005499 else if (CUR == '(') {
5500 NEXT;
5501 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005502 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005503 if (CUR != ')') {
5504 XP_ERROR(XPATH_EXPR_ERROR);
5505 }
5506 NEXT;
5507 SKIP_BLANKS;
5508 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005509 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005510 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005511 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005512 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005513 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005514 }
5515 SKIP_BLANKS;
5516}
5517
5518/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005519 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005520 * @ctxt: the XPath Parser context
5521 *
5522 * [20] FilterExpr ::= PrimaryExpr
5523 * | FilterExpr Predicate
5524 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005525 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005526 * Square brackets are used to filter expressions in the same way that
5527 * they are used in location paths. It is an error if the expression to
5528 * be filtered does not evaluate to a node-set. The context node list
5529 * used for evaluating the expression in square brackets is the node-set
5530 * to be filtered listed in document order.
5531 */
5532
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005533static void
5534xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
5535 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005536 CHECK_ERROR;
5537 SKIP_BLANKS;
5538
5539 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005540 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00005541 SKIP_BLANKS;
5542 }
5543
5544
5545}
5546
5547/**
5548 * xmlXPathScanName:
5549 * @ctxt: the XPath Parser context
5550 *
5551 * Trickery: parse an XML name but without consuming the input flow
5552 * Needed to avoid insanity in the parser state.
5553 *
5554 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5555 * CombiningChar | Extender
5556 *
5557 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5558 *
5559 * [6] Names ::= Name (S Name)*
5560 *
5561 * Returns the Name parsed or NULL
5562 */
5563
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005564static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005565xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5566 xmlChar buf[XML_MAX_NAMELEN];
5567 int len = 0;
5568
5569 SKIP_BLANKS;
5570 if (!IS_LETTER(CUR) && (CUR != '_') &&
5571 (CUR != ':')) {
5572 return(NULL);
5573 }
5574
5575 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5576 (NXT(len) == '.') || (NXT(len) == '-') ||
5577 (NXT(len) == '_') || (NXT(len) == ':') ||
5578 (IS_COMBINING(NXT(len))) ||
5579 (IS_EXTENDER(NXT(len)))) {
5580 buf[len] = NXT(len);
5581 len++;
5582 if (len >= XML_MAX_NAMELEN) {
5583 xmlGenericError(xmlGenericErrorContext,
5584 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5585 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5586 (NXT(len) == '.') || (NXT(len) == '-') ||
5587 (NXT(len) == '_') || (NXT(len) == ':') ||
5588 (IS_COMBINING(NXT(len))) ||
5589 (IS_EXTENDER(NXT(len))))
5590 len++;
5591 break;
5592 }
5593 }
5594 return(xmlStrndup(buf, len));
5595}
5596
5597/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005598 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005599 * @ctxt: the XPath Parser context
5600 *
5601 * [19] PathExpr ::= LocationPath
5602 * | FilterExpr
5603 * | FilterExpr '/' RelativeLocationPath
5604 * | FilterExpr '//' RelativeLocationPath
5605 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005606 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005607 * The / operator and // operators combine an arbitrary expression
5608 * and a relative location path. It is an error if the expression
5609 * does not evaluate to a node-set.
5610 * The / operator does composition in the same way as when / is
5611 * used in a location path. As in location paths, // is short for
5612 * /descendant-or-self::node()/.
5613 */
5614
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005615static void
5616xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005617 int lc = 1; /* Should we branch to LocationPath ? */
5618 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5619
5620 SKIP_BLANKS;
5621 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5622 (CUR == '\'') || (CUR == '"')) {
5623 lc = 0;
5624 } else if (CUR == '*') {
5625 /* relative or absolute location path */
5626 lc = 1;
5627 } else if (CUR == '/') {
5628 /* relative or absolute location path */
5629 lc = 1;
5630 } else if (CUR == '@') {
5631 /* relative abbreviated attribute location path */
5632 lc = 1;
5633 } else if (CUR == '.') {
5634 /* relative abbreviated attribute location path */
5635 lc = 1;
5636 } else {
5637 /*
5638 * Problem is finding if we have a name here whether it's:
5639 * - a nodetype
5640 * - a function call in which case it's followed by '('
5641 * - an axis in which case it's followed by ':'
5642 * - a element name
5643 * We do an a priori analysis here rather than having to
5644 * maintain parsed token content through the recursive function
5645 * calls. This looks uglier but makes the code quite easier to
5646 * read/write/debug.
5647 */
5648 SKIP_BLANKS;
5649 name = xmlXPathScanName(ctxt);
5650 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5651#ifdef DEBUG_STEP
5652 xmlGenericError(xmlGenericErrorContext,
5653 "PathExpr: Axis\n");
5654#endif
5655 lc = 1;
5656 xmlFree(name);
5657 } else if (name != NULL) {
5658 int len =xmlStrlen(name);
5659 int blank = 0;
5660
5661
5662 while (NXT(len) != 0) {
5663 if (NXT(len) == '/') {
5664 /* element name */
5665#ifdef DEBUG_STEP
5666 xmlGenericError(xmlGenericErrorContext,
5667 "PathExpr: AbbrRelLocation\n");
5668#endif
5669 lc = 1;
5670 break;
5671 } else if (IS_BLANK(NXT(len))) {
5672 /* skip to next */
5673 blank = 1;
5674 } else if (NXT(len) == ':') {
5675#ifdef DEBUG_STEP
5676 xmlGenericError(xmlGenericErrorContext,
5677 "PathExpr: AbbrRelLocation\n");
5678#endif
5679 lc = 1;
5680 break;
5681 } else if ((NXT(len) == '(')) {
5682 /* Note Type or Function */
5683 if (xmlXPathIsNodeType(name)) {
5684#ifdef DEBUG_STEP
5685 xmlGenericError(xmlGenericErrorContext,
5686 "PathExpr: Type search\n");
5687#endif
5688 lc = 1;
5689 } else {
5690#ifdef DEBUG_STEP
5691 xmlGenericError(xmlGenericErrorContext,
5692 "PathExpr: function call\n");
5693#endif
5694 lc = 0;
5695 }
5696 break;
5697 } else if ((NXT(len) == '[')) {
5698 /* element name */
5699#ifdef DEBUG_STEP
5700 xmlGenericError(xmlGenericErrorContext,
5701 "PathExpr: AbbrRelLocation\n");
5702#endif
5703 lc = 1;
5704 break;
5705 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
5706 (NXT(len) == '=')) {
5707 lc = 1;
5708 break;
5709 } else {
5710 lc = 1;
5711 break;
5712 }
5713 len++;
5714 }
5715 if (NXT(len) == 0) {
5716#ifdef DEBUG_STEP
5717 xmlGenericError(xmlGenericErrorContext,
5718 "PathExpr: AbbrRelLocation\n");
5719#endif
5720 /* element name */
5721 lc = 1;
5722 }
5723 xmlFree(name);
5724 } else {
5725 /* make sure all cases are covered explicitely */
5726 XP_ERROR(XPATH_EXPR_ERROR);
5727 }
5728 }
5729
5730 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005731 if (CUR == '/') {
5732 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
5733 } else {
5734 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005735 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005736 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005737 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005738 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005739 CHECK_ERROR;
5740 if ((CUR == '/') && (NXT(1) == '/')) {
5741 SKIP(2);
5742 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005743
5744 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
5745 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
5746 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
5747
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005748 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005749 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005750 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005751 }
5752 }
5753 SKIP_BLANKS;
5754}
5755
5756/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005757 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005758 * @ctxt: the XPath Parser context
5759 *
5760 * [18] UnionExpr ::= PathExpr
5761 * | UnionExpr '|' PathExpr
5762 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005763 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005764 */
5765
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005766static void
5767xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
5768 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005769 CHECK_ERROR;
5770 SKIP_BLANKS;
5771 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005772 int op1 = ctxt->comp->last;
5773 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005774
5775 NEXT;
5776 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005777 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005778
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005779 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
5780
Owen Taylor3473f882001-02-23 17:55:21 +00005781 SKIP_BLANKS;
5782 }
Owen Taylor3473f882001-02-23 17:55:21 +00005783}
5784
5785/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005786 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005787 * @ctxt: the XPath Parser context
5788 *
5789 * [27] UnaryExpr ::= UnionExpr
5790 * | '-' UnaryExpr
5791 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005792 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005793 */
5794
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005795static void
5796xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005797 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005798 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005799
5800 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00005801 while (CUR == '-') {
5802 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005803 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005804 NEXT;
5805 SKIP_BLANKS;
5806 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005807
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005808 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005809 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005810 if (found) {
5811 if (minus)
5812 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
5813 else
5814 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005815 }
5816}
5817
5818/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005819 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005820 * @ctxt: the XPath Parser context
5821 *
5822 * [26] MultiplicativeExpr ::= UnaryExpr
5823 * | MultiplicativeExpr MultiplyOperator UnaryExpr
5824 * | MultiplicativeExpr 'div' UnaryExpr
5825 * | MultiplicativeExpr 'mod' UnaryExpr
5826 * [34] MultiplyOperator ::= '*'
5827 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005828 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005829 */
5830
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005831static void
5832xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
5833 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005834 CHECK_ERROR;
5835 SKIP_BLANKS;
5836 while ((CUR == '*') ||
5837 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
5838 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
5839 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005840 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005841
5842 if (CUR == '*') {
5843 op = 0;
5844 NEXT;
5845 } else if (CUR == 'd') {
5846 op = 1;
5847 SKIP(3);
5848 } else if (CUR == 'm') {
5849 op = 2;
5850 SKIP(3);
5851 }
5852 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005853 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005854 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005855 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005856 SKIP_BLANKS;
5857 }
5858}
5859
5860/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005861 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005862 * @ctxt: the XPath Parser context
5863 *
5864 * [25] AdditiveExpr ::= MultiplicativeExpr
5865 * | AdditiveExpr '+' MultiplicativeExpr
5866 * | AdditiveExpr '-' MultiplicativeExpr
5867 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005868 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005869 */
5870
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005871static void
5872xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005873
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005874 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005875 CHECK_ERROR;
5876 SKIP_BLANKS;
5877 while ((CUR == '+') || (CUR == '-')) {
5878 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005879 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005880
5881 if (CUR == '+') plus = 1;
5882 else plus = 0;
5883 NEXT;
5884 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005885 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005886 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005887 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005888 SKIP_BLANKS;
5889 }
5890}
5891
5892/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005893 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005894 * @ctxt: the XPath Parser context
5895 *
5896 * [24] RelationalExpr ::= AdditiveExpr
5897 * | RelationalExpr '<' AdditiveExpr
5898 * | RelationalExpr '>' AdditiveExpr
5899 * | RelationalExpr '<=' AdditiveExpr
5900 * | RelationalExpr '>=' AdditiveExpr
5901 *
5902 * A <= B > C is allowed ? Answer from James, yes with
5903 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
5904 * which is basically what got implemented.
5905 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005906 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00005907 * on the stack
5908 */
5909
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005910static void
5911xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
5912 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005913 CHECK_ERROR;
5914 SKIP_BLANKS;
5915 while ((CUR == '<') ||
5916 (CUR == '>') ||
5917 ((CUR == '<') && (NXT(1) == '=')) ||
5918 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005919 int inf, strict;
5920 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005921
5922 if (CUR == '<') inf = 1;
5923 else inf = 0;
5924 if (NXT(1) == '=') strict = 0;
5925 else strict = 1;
5926 NEXT;
5927 if (!strict) NEXT;
5928 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005929 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005930 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005931 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00005932 SKIP_BLANKS;
5933 }
5934}
5935
5936/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005937 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005938 * @ctxt: the XPath Parser context
5939 *
5940 * [23] EqualityExpr ::= RelationalExpr
5941 * | EqualityExpr '=' RelationalExpr
5942 * | EqualityExpr '!=' RelationalExpr
5943 *
5944 * A != B != C is allowed ? Answer from James, yes with
5945 * (RelationalExpr = RelationalExpr) = RelationalExpr
5946 * (RelationalExpr != RelationalExpr) != RelationalExpr
5947 * which is basically what got implemented.
5948 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005949 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005950 *
5951 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005952static void
5953xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
5954 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005955 CHECK_ERROR;
5956 SKIP_BLANKS;
5957 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005958 int eq;
5959 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005960
5961 if (CUR == '=') eq = 1;
5962 else eq = 0;
5963 NEXT;
5964 if (!eq) NEXT;
5965 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005966 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005967 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005968 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005969 SKIP_BLANKS;
5970 }
5971}
5972
5973/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005974 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005975 * @ctxt: the XPath Parser context
5976 *
5977 * [22] AndExpr ::= EqualityExpr
5978 * | AndExpr 'and' EqualityExpr
5979 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005980 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005981 *
5982 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005983static void
5984xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
5985 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005986 CHECK_ERROR;
5987 SKIP_BLANKS;
5988 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005989 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005990 SKIP(3);
5991 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005992 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005993 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005994 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005995 SKIP_BLANKS;
5996 }
5997}
5998
5999/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006000 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006001 * @ctxt: the XPath Parser context
6002 *
6003 * [14] Expr ::= OrExpr
6004 * [21] OrExpr ::= AndExpr
6005 * | OrExpr 'or' AndExpr
6006 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006007 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00006008 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006009static void
6010xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
6011 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006012 CHECK_ERROR;
6013 SKIP_BLANKS;
6014 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006015 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006016 SKIP(2);
6017 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006018 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006019 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006020 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
6021 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00006022 SKIP_BLANKS;
6023 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006024 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
6025 /* more ops could be optimized too */
6026 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
6027 }
Owen Taylor3473f882001-02-23 17:55:21 +00006028}
6029
6030/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006031 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00006032 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006033 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00006034 *
6035 * [8] Predicate ::= '[' PredicateExpr ']'
6036 * [9] PredicateExpr ::= Expr
6037 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006038 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00006039 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006040static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006041xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006042 int op1 = ctxt->comp->last;
6043
6044 SKIP_BLANKS;
6045 if (CUR != '[') {
6046 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6047 }
6048 NEXT;
6049 SKIP_BLANKS;
6050
6051 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006052 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006053 CHECK_ERROR;
6054
6055 if (CUR != ']') {
6056 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6057 }
6058
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006059 if (filter)
6060 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
6061 else
6062 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006063
6064 NEXT;
6065 SKIP_BLANKS;
6066}
6067
6068/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006069 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00006070 * @ctxt: the XPath Parser context
6071 * @test: pointer to a xmlXPathTestVal
6072 * @type: pointer to a xmlXPathTypeVal
6073 * @prefix: placeholder for a possible name prefix
6074 *
6075 * [7] NodeTest ::= NameTest
6076 * | NodeType '(' ')'
6077 * | 'processing-instruction' '(' Literal ')'
6078 *
6079 * [37] NameTest ::= '*'
6080 * | NCName ':' '*'
6081 * | QName
6082 * [38] NodeType ::= 'comment'
6083 * | 'text'
6084 * | 'processing-instruction'
6085 * | 'node'
6086 *
6087 * Returns the name found and update @test, @type and @prefix appropriately
6088 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006089static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006090xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6091 xmlXPathTypeVal *type, const xmlChar **prefix,
6092 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00006093 int blanks;
6094
6095 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6096 STRANGE;
6097 return(NULL);
6098 }
6099 *type = 0;
6100 *test = 0;
6101 *prefix = NULL;
6102 SKIP_BLANKS;
6103
6104 if ((name == NULL) && (CUR == '*')) {
6105 /*
6106 * All elements
6107 */
6108 NEXT;
6109 *test = NODE_TEST_ALL;
6110 return(NULL);
6111 }
6112
6113 if (name == NULL)
6114 name = xmlXPathParseNCName(ctxt);
6115 if (name == NULL) {
6116 XP_ERROR0(XPATH_EXPR_ERROR);
6117 }
6118
6119 blanks = IS_BLANK(CUR);
6120 SKIP_BLANKS;
6121 if (CUR == '(') {
6122 NEXT;
6123 /*
6124 * NodeType or PI search
6125 */
6126 if (xmlStrEqual(name, BAD_CAST "comment"))
6127 *type = NODE_TYPE_COMMENT;
6128 else if (xmlStrEqual(name, BAD_CAST "node"))
6129 *type = NODE_TYPE_NODE;
6130 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6131 *type = NODE_TYPE_PI;
6132 else if (xmlStrEqual(name, BAD_CAST "text"))
6133 *type = NODE_TYPE_TEXT;
6134 else {
6135 if (name != NULL)
6136 xmlFree(name);
6137 XP_ERROR0(XPATH_EXPR_ERROR);
6138 }
6139
6140 *test = NODE_TEST_TYPE;
6141
6142 SKIP_BLANKS;
6143 if (*type == NODE_TYPE_PI) {
6144 /*
6145 * Specific case: search a PI by name.
6146 */
Owen Taylor3473f882001-02-23 17:55:21 +00006147 if (name != NULL)
6148 xmlFree(name);
Daniel Veillard82e49712001-04-26 14:38:03 +00006149 name = NULL;
6150 if (CUR != ')') {
6151 name = xmlXPathParseLiteral(ctxt);
6152 CHECK_ERROR 0;
6153 SKIP_BLANKS;
6154 }
Owen Taylor3473f882001-02-23 17:55:21 +00006155 }
6156 if (CUR != ')') {
6157 if (name != NULL)
6158 xmlFree(name);
6159 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6160 }
6161 NEXT;
6162 return(name);
6163 }
6164 *test = NODE_TEST_NAME;
6165 if ((!blanks) && (CUR == ':')) {
6166 NEXT;
6167
6168 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006169 * Since currently the parser context don't have a
6170 * namespace list associated:
6171 * The namespace name for this prefix can be computed
6172 * only at evaluation time. The compilation is done
6173 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00006174 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006175#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00006176 *prefix = xmlXPathNsLookup(ctxt->context, name);
6177 if (name != NULL)
6178 xmlFree(name);
6179 if (*prefix == NULL) {
6180 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6181 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006182#else
6183 *prefix = name;
6184#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006185
6186 if (CUR == '*') {
6187 /*
6188 * All elements
6189 */
6190 NEXT;
6191 *test = NODE_TEST_ALL;
6192 return(NULL);
6193 }
6194
6195 name = xmlXPathParseNCName(ctxt);
6196 if (name == NULL) {
6197 XP_ERROR0(XPATH_EXPR_ERROR);
6198 }
6199 }
6200 return(name);
6201}
6202
6203/**
6204 * xmlXPathIsAxisName:
6205 * @name: a preparsed name token
6206 *
6207 * [6] AxisName ::= 'ancestor'
6208 * | 'ancestor-or-self'
6209 * | 'attribute'
6210 * | 'child'
6211 * | 'descendant'
6212 * | 'descendant-or-self'
6213 * | 'following'
6214 * | 'following-sibling'
6215 * | 'namespace'
6216 * | 'parent'
6217 * | 'preceding'
6218 * | 'preceding-sibling'
6219 * | 'self'
6220 *
6221 * Returns the axis or 0
6222 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006223static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00006224xmlXPathIsAxisName(const xmlChar *name) {
6225 xmlXPathAxisVal ret = 0;
6226 switch (name[0]) {
6227 case 'a':
6228 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6229 ret = AXIS_ANCESTOR;
6230 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6231 ret = AXIS_ANCESTOR_OR_SELF;
6232 if (xmlStrEqual(name, BAD_CAST "attribute"))
6233 ret = AXIS_ATTRIBUTE;
6234 break;
6235 case 'c':
6236 if (xmlStrEqual(name, BAD_CAST "child"))
6237 ret = AXIS_CHILD;
6238 break;
6239 case 'd':
6240 if (xmlStrEqual(name, BAD_CAST "descendant"))
6241 ret = AXIS_DESCENDANT;
6242 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6243 ret = AXIS_DESCENDANT_OR_SELF;
6244 break;
6245 case 'f':
6246 if (xmlStrEqual(name, BAD_CAST "following"))
6247 ret = AXIS_FOLLOWING;
6248 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6249 ret = AXIS_FOLLOWING_SIBLING;
6250 break;
6251 case 'n':
6252 if (xmlStrEqual(name, BAD_CAST "namespace"))
6253 ret = AXIS_NAMESPACE;
6254 break;
6255 case 'p':
6256 if (xmlStrEqual(name, BAD_CAST "parent"))
6257 ret = AXIS_PARENT;
6258 if (xmlStrEqual(name, BAD_CAST "preceding"))
6259 ret = AXIS_PRECEDING;
6260 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6261 ret = AXIS_PRECEDING_SIBLING;
6262 break;
6263 case 's':
6264 if (xmlStrEqual(name, BAD_CAST "self"))
6265 ret = AXIS_SELF;
6266 break;
6267 }
6268 return(ret);
6269}
6270
6271/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006272 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00006273 * @ctxt: the XPath Parser context
6274 *
6275 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6276 * | AbbreviatedStep
6277 *
6278 * [12] AbbreviatedStep ::= '.' | '..'
6279 *
6280 * [5] AxisSpecifier ::= AxisName '::'
6281 * | AbbreviatedAxisSpecifier
6282 *
6283 * [13] AbbreviatedAxisSpecifier ::= '@'?
6284 *
6285 * Modified for XPtr range support as:
6286 *
6287 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6288 * | AbbreviatedStep
6289 * | 'range-to' '(' Expr ')' Predicate*
6290 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006291 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00006292 * A location step of . is short for self::node(). This is
6293 * particularly useful in conjunction with //. For example, the
6294 * location path .//para is short for
6295 * self::node()/descendant-or-self::node()/child::para
6296 * and so will select all para descendant elements of the context
6297 * node.
6298 * Similarly, a location step of .. is short for parent::node().
6299 * For example, ../title is short for parent::node()/child::title
6300 * and so will select the title children of the parent of the context
6301 * node.
6302 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006303static void
6304xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006305#ifdef LIBXML_XPTR_ENABLED
6306 int rangeto = 0;
6307 int op2 = -1;
6308#endif
6309
Owen Taylor3473f882001-02-23 17:55:21 +00006310 SKIP_BLANKS;
6311 if ((CUR == '.') && (NXT(1) == '.')) {
6312 SKIP(2);
6313 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006314 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
6315 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006316 } else if (CUR == '.') {
6317 NEXT;
6318 SKIP_BLANKS;
6319 } else {
6320 xmlChar *name = NULL;
6321 const xmlChar *prefix = NULL;
6322 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006323 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006324 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006325 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00006326
6327 /*
6328 * The modification needed for XPointer change to the production
6329 */
6330#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006331 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00006332 name = xmlXPathParseNCName(ctxt);
6333 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006334 op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006335 xmlFree(name);
6336 SKIP_BLANKS;
6337 if (CUR != '(') {
6338 XP_ERROR(XPATH_EXPR_ERROR);
6339 }
6340 NEXT;
6341 SKIP_BLANKS;
6342
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006343 xmlXPathCompileExpr(ctxt);
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006344 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
Owen Taylor3473f882001-02-23 17:55:21 +00006345 CHECK_ERROR;
6346
6347 SKIP_BLANKS;
6348 if (CUR != ')') {
6349 XP_ERROR(XPATH_EXPR_ERROR);
6350 }
6351 NEXT;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006352 rangeto = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006353 goto eval_predicates;
6354 }
6355 }
6356#endif
6357 if (name == NULL)
6358 name = xmlXPathParseNCName(ctxt);
6359 if (name != NULL) {
6360 axis = xmlXPathIsAxisName(name);
6361 if (axis != 0) {
6362 SKIP_BLANKS;
6363 if ((CUR == ':') && (NXT(1) == ':')) {
6364 SKIP(2);
6365 xmlFree(name);
6366 name = NULL;
6367 } else {
6368 /* an element name can conflict with an axis one :-\ */
6369 axis = AXIS_CHILD;
6370 }
6371 } else {
6372 axis = AXIS_CHILD;
6373 }
6374 } else if (CUR == '@') {
6375 NEXT;
6376 axis = AXIS_ATTRIBUTE;
6377 } else {
6378 axis = AXIS_CHILD;
6379 }
6380
6381 CHECK_ERROR;
6382
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006383 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00006384 if (test == 0)
6385 return;
6386
6387#ifdef DEBUG_STEP
6388 xmlGenericError(xmlGenericErrorContext,
6389 "Basis : computing new set\n");
6390#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006391
Owen Taylor3473f882001-02-23 17:55:21 +00006392#ifdef DEBUG_STEP
6393 xmlGenericError(xmlGenericErrorContext, "Basis : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006394 if (ctxt->value == NULL)
6395 xmlGenericError(xmlGenericErrorContext, "no value\n");
6396 else if (ctxt->value->nodesetval == NULL)
6397 xmlGenericError(xmlGenericErrorContext, "Empty\n");
6398 else
6399 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00006400#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006401
6402eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006403 op1 = ctxt->comp->last;
6404 ctxt->comp->last = -1;
6405
Owen Taylor3473f882001-02-23 17:55:21 +00006406 SKIP_BLANKS;
6407 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006408 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006409 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006410
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006411#ifdef LIBXML_XPTR_ENABLED
6412 if (rangeto) {
6413 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
6414 } else
6415#endif
6416 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
6417 test, type, (void *)prefix, (void *)name);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006418
Owen Taylor3473f882001-02-23 17:55:21 +00006419 }
6420#ifdef DEBUG_STEP
6421 xmlGenericError(xmlGenericErrorContext, "Step : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00006422 if (ctxt->value == NULL)
6423 xmlGenericError(xmlGenericErrorContext, "no value\n");
6424 else if (ctxt->value->nodesetval == NULL)
6425 xmlGenericError(xmlGenericErrorContext, "Empty\n");
6426 else
6427 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6428 ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00006429#endif
6430}
6431
6432/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006433 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006434 * @ctxt: the XPath Parser context
6435 *
6436 * [3] RelativeLocationPath ::= Step
6437 * | RelativeLocationPath '/' Step
6438 * | AbbreviatedRelativeLocationPath
6439 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6440 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006441 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00006442 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006443static void
Owen Taylor3473f882001-02-23 17:55:21 +00006444#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006445xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006446#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006447xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006448#endif
6449(xmlXPathParserContextPtr ctxt) {
6450 SKIP_BLANKS;
6451 if ((CUR == '/') && (NXT(1) == '/')) {
6452 SKIP(2);
6453 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006454 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6455 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006456 } else if (CUR == '/') {
6457 NEXT;
6458 SKIP_BLANKS;
6459 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006460 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006461 SKIP_BLANKS;
6462 while (CUR == '/') {
6463 if ((CUR == '/') && (NXT(1) == '/')) {
6464 SKIP(2);
6465 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006466 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00006467 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006468 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006469 } else if (CUR == '/') {
6470 NEXT;
6471 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006472 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006473 }
6474 SKIP_BLANKS;
6475 }
6476}
6477
6478/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006479 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006480 * @ctxt: the XPath Parser context
6481 *
6482 * [1] LocationPath ::= RelativeLocationPath
6483 * | AbsoluteLocationPath
6484 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6485 * | AbbreviatedAbsoluteLocationPath
6486 * [10] AbbreviatedAbsoluteLocationPath ::=
6487 * '//' RelativeLocationPath
6488 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006489 * Compile a location path
6490 *
Owen Taylor3473f882001-02-23 17:55:21 +00006491 * // is short for /descendant-or-self::node()/. For example,
6492 * //para is short for /descendant-or-self::node()/child::para and
6493 * so will select any para element in the document (even a para element
6494 * that is a document element will be selected by //para since the
6495 * document element node is a child of the root node); div//para is
6496 * short for div/descendant-or-self::node()/child::para and so will
6497 * select all para descendants of div children.
6498 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006499static void
6500xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006501 SKIP_BLANKS;
6502 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006503 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006504 } else {
6505 while (CUR == '/') {
6506 if ((CUR == '/') && (NXT(1) == '/')) {
6507 SKIP(2);
6508 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006509 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6510 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006511 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006512 } else if (CUR == '/') {
6513 NEXT;
6514 SKIP_BLANKS;
6515 if (CUR != 0)
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006516 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006517 }
6518 }
6519 }
6520}
6521
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006522/************************************************************************
6523 * *
6524 * XPath precompiled expression evaluation *
6525 * *
6526 ************************************************************************/
6527
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006528static void
6529xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
6530
6531/**
6532 * xmlXPathNodeCollectAndTest:
6533 * @ctxt: the XPath Parser context
6534 * @op: the XPath precompiled step operation
6535 *
6536 * This is the function implementing a step: based on the current list
6537 * of nodes, it builds up a new list, looking at all nodes under that
6538 * axis and selecting them it also do the predicate filtering
6539 *
6540 * Pushes the new NodeSet resulting from the search.
6541 */
6542static void
6543xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
6544 xmlXPathStepOpPtr op) {
6545 xmlXPathAxisVal axis = op->value;
6546 xmlXPathTestVal test = op->value2;
6547 xmlXPathTypeVal type = op->value3;
6548 const xmlChar *prefix = op->value4;
6549 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006550 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006551
6552#ifdef DEBUG_STEP
6553 int n = 0, t = 0;
6554#endif
6555 int i;
6556 xmlNodeSetPtr ret, list;
6557 xmlXPathTraversalFunction next = NULL;
6558 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
6559 xmlNodePtr cur = NULL;
6560 xmlXPathObjectPtr obj;
6561 xmlNodeSetPtr nodelist;
6562 xmlNodePtr tmp;
6563
6564 CHECK_TYPE(XPATH_NODESET);
6565 obj = valuePop(ctxt);
6566 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006567 if (prefix != NULL) {
6568 URI = xmlXPathNsLookup(ctxt->context, prefix);
6569 if (URI == NULL)
6570 XP_ERROR(XPATH_UNDEF_PREFIX_ERROR);
6571 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006572
6573#ifdef DEBUG_STEP
6574 xmlGenericError(xmlGenericErrorContext,
6575 "new step : ");
6576#endif
6577 switch (axis) {
6578 case AXIS_ANCESTOR:
6579#ifdef DEBUG_STEP
6580 xmlGenericError(xmlGenericErrorContext,
6581 "axis 'ancestors' ");
6582#endif
6583 next = xmlXPathNextAncestor; break;
6584 case AXIS_ANCESTOR_OR_SELF:
6585#ifdef DEBUG_STEP
6586 xmlGenericError(xmlGenericErrorContext,
6587 "axis 'ancestors-or-self' ");
6588#endif
6589 next = xmlXPathNextAncestorOrSelf; break;
6590 case AXIS_ATTRIBUTE:
6591#ifdef DEBUG_STEP
6592 xmlGenericError(xmlGenericErrorContext,
6593 "axis 'attributes' ");
6594#endif
6595 next = xmlXPathNextAttribute; break;
6596 break;
6597 case AXIS_CHILD:
6598#ifdef DEBUG_STEP
6599 xmlGenericError(xmlGenericErrorContext,
6600 "axis 'child' ");
6601#endif
6602 next = xmlXPathNextChild; break;
6603 case AXIS_DESCENDANT:
6604#ifdef DEBUG_STEP
6605 xmlGenericError(xmlGenericErrorContext,
6606 "axis 'descendant' ");
6607#endif
6608 next = xmlXPathNextDescendant; break;
6609 case AXIS_DESCENDANT_OR_SELF:
6610#ifdef DEBUG_STEP
6611 xmlGenericError(xmlGenericErrorContext,
6612 "axis 'descendant-or-self' ");
6613#endif
6614 next = xmlXPathNextDescendantOrSelf; break;
6615 case AXIS_FOLLOWING:
6616#ifdef DEBUG_STEP
6617 xmlGenericError(xmlGenericErrorContext,
6618 "axis 'following' ");
6619#endif
6620 next = xmlXPathNextFollowing; break;
6621 case AXIS_FOLLOWING_SIBLING:
6622#ifdef DEBUG_STEP
6623 xmlGenericError(xmlGenericErrorContext,
6624 "axis 'following-siblings' ");
6625#endif
6626 next = xmlXPathNextFollowingSibling; break;
6627 case AXIS_NAMESPACE:
6628#ifdef DEBUG_STEP
6629 xmlGenericError(xmlGenericErrorContext,
6630 "axis 'namespace' ");
6631#endif
6632 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
6633 break;
6634 case AXIS_PARENT:
6635#ifdef DEBUG_STEP
6636 xmlGenericError(xmlGenericErrorContext,
6637 "axis 'parent' ");
6638#endif
6639 next = xmlXPathNextParent; break;
6640 case AXIS_PRECEDING:
6641#ifdef DEBUG_STEP
6642 xmlGenericError(xmlGenericErrorContext,
6643 "axis 'preceding' ");
6644#endif
6645 next = xmlXPathNextPreceding; break;
6646 case AXIS_PRECEDING_SIBLING:
6647#ifdef DEBUG_STEP
6648 xmlGenericError(xmlGenericErrorContext,
6649 "axis 'preceding-sibling' ");
6650#endif
6651 next = xmlXPathNextPrecedingSibling; break;
6652 case AXIS_SELF:
6653#ifdef DEBUG_STEP
6654 xmlGenericError(xmlGenericErrorContext,
6655 "axis 'self' ");
6656#endif
6657 next = xmlXPathNextSelf; break;
6658 }
6659 if (next == NULL)
6660 return;
6661
6662 nodelist = obj->nodesetval;
6663 if (nodelist == NULL) {
6664 xmlXPathFreeObject(obj);
6665 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
6666 return;
6667 }
6668 addNode = xmlXPathNodeSetAddUnique;
6669 ret = NULL;
6670#ifdef DEBUG_STEP
6671 xmlGenericError(xmlGenericErrorContext,
6672 " context contains %d nodes\n",
6673 nodelist->nodeNr);
6674 switch (test) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006675 case NODE_TEST_NONE:
6676 xmlGenericError(xmlGenericErrorContext,
6677 " searching for none !!!\n");
6678 break;
6679 case NODE_TEST_TYPE:
6680 xmlGenericError(xmlGenericErrorContext,
6681 " searching for type %d\n", type);
6682 break;
6683 case NODE_TEST_PI:
6684 xmlGenericError(xmlGenericErrorContext,
6685 " searching for PI !!!\n");
6686 break;
6687 case NODE_TEST_ALL:
6688 xmlGenericError(xmlGenericErrorContext,
6689 " searching for *\n");
6690 break;
6691 case NODE_TEST_NS:
6692 xmlGenericError(xmlGenericErrorContext,
6693 " searching for namespace %s\n",
6694 prefix);
6695 break;
6696 case NODE_TEST_NAME:
6697 xmlGenericError(xmlGenericErrorContext,
6698 " searching for name %s\n", name);
6699 if (prefix != NULL)
6700 xmlGenericError(xmlGenericErrorContext,
6701 " with namespace %s\n",
6702 prefix);
6703 break;
6704 }
6705 xmlGenericError(xmlGenericErrorContext, "Testing : ");
6706#endif
6707 /*
6708 * 2.3 Node Tests
6709 * - For the attribute axis, the principal node type is attribute.
6710 * - For the namespace axis, the principal node type is namespace.
6711 * - For other axes, the principal node type is element.
6712 *
6713 * A node test * is true for any node of the
6714 * principal node type. For example, child::* willi
6715 * select all element children of the context node
6716 */
6717 tmp = ctxt->context->node;
6718 for (i = 0;i < nodelist->nodeNr; i++) {
6719 ctxt->context->node = nodelist->nodeTab[i];
6720
6721 cur = NULL;
6722 list = xmlXPathNodeSetCreate(NULL);
6723 do {
6724 cur = next(ctxt, cur);
6725 if (cur == NULL) break;
6726#ifdef DEBUG_STEP
6727 t++;
6728 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
6729#endif
6730 switch (test) {
6731 case NODE_TEST_NONE:
6732 ctxt->context->node = tmp;
6733 STRANGE
6734 return;
6735 case NODE_TEST_TYPE:
6736 if ((cur->type == type) ||
6737 ((type == NODE_TYPE_NODE) &&
6738 ((cur->type == XML_DOCUMENT_NODE) ||
6739 (cur->type == XML_HTML_DOCUMENT_NODE) ||
6740 (cur->type == XML_ELEMENT_NODE) ||
6741 (cur->type == XML_PI_NODE) ||
6742 (cur->type == XML_COMMENT_NODE) ||
6743 (cur->type == XML_CDATA_SECTION_NODE) ||
6744 (cur->type == XML_TEXT_NODE)))) {
6745#ifdef DEBUG_STEP
6746 n++;
6747#endif
6748 addNode(list, cur);
6749 }
6750 break;
6751 case NODE_TEST_PI:
6752 if (cur->type == XML_PI_NODE) {
6753 if ((name != NULL) &&
6754 (!xmlStrEqual(name, cur->name)))
6755 break;
6756#ifdef DEBUG_STEP
6757 n++;
6758#endif
6759 addNode(list, cur);
6760 }
6761 break;
6762 case NODE_TEST_ALL:
6763 if (axis == AXIS_ATTRIBUTE) {
6764 if (cur->type == XML_ATTRIBUTE_NODE) {
6765#ifdef DEBUG_STEP
6766 n++;
6767#endif
6768 addNode(list, cur);
6769 }
6770 } else if (axis == AXIS_NAMESPACE) {
6771 if (cur->type == XML_NAMESPACE_DECL) {
6772#ifdef DEBUG_STEP
6773 n++;
6774#endif
6775 addNode(list, cur);
6776 }
6777 } else {
6778 if ((cur->type == XML_ELEMENT_NODE) ||
6779 (cur->type == XML_DOCUMENT_NODE) ||
6780 (cur->type == XML_HTML_DOCUMENT_NODE)) {
6781 if (prefix == NULL) {
6782#ifdef DEBUG_STEP
6783 n++;
6784#endif
6785 addNode(list, cur);
6786 } else if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006787 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006788 cur->ns->href))) {
6789#ifdef DEBUG_STEP
6790 n++;
6791#endif
6792 addNode(list, cur);
6793 }
6794 }
6795 }
6796 break;
6797 case NODE_TEST_NS: {
6798 TODO;
6799 break;
6800 }
6801 case NODE_TEST_NAME:
6802 switch (cur->type) {
6803 case XML_ELEMENT_NODE:
6804 if (xmlStrEqual(name, cur->name)) {
6805 if (prefix == NULL) {
6806 if ((cur->ns == NULL) ||
6807 (cur->ns->prefix == NULL)) {
6808#ifdef DEBUG_STEP
6809 n++;
6810#endif
6811 addNode(list, cur);
6812 }
6813 } else {
6814 if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006815 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006816 cur->ns->href))) {
6817#ifdef DEBUG_STEP
6818 n++;
6819#endif
6820 addNode(list, cur);
6821 }
6822 }
6823 }
6824 break;
6825 case XML_ATTRIBUTE_NODE: {
6826 xmlAttrPtr attr = (xmlAttrPtr) cur;
6827 if (xmlStrEqual(name, attr->name)) {
6828 if (prefix == NULL) {
6829 if ((attr->ns == NULL) ||
6830 (attr->ns->prefix == NULL)) {
6831#ifdef DEBUG_STEP
6832 n++;
6833#endif
6834 addNode(list, (xmlNodePtr) attr);
6835 }
6836 } else {
6837 if ((attr->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006838 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006839 attr->ns->href))) {
6840#ifdef DEBUG_STEP
6841 n++;
6842#endif
6843 addNode(list, (xmlNodePtr) attr);
6844 }
6845 }
6846 }
6847 break;
6848 }
6849 case XML_NAMESPACE_DECL: {
6850 TODO;
6851 break;
6852 }
6853 default:
6854 break;
6855 }
6856 break;
6857 }
6858 } while (cur != NULL);
6859
6860 /*
6861 * If there is some predicate filtering do it now
6862 */
6863 if (op->ch2 != -1) {
6864 xmlXPathObjectPtr obj2;
6865
6866 valuePush(ctxt, xmlXPathWrapNodeSet(list));
6867 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
6868 CHECK_TYPE(XPATH_NODESET);
6869 obj2 = valuePop(ctxt);
6870 list = obj2->nodesetval;
6871 obj2->nodesetval = NULL;
6872 xmlXPathFreeObject(obj2);
6873 }
6874 if (ret == NULL) {
6875 ret = list;
6876 } else {
6877 ret = xmlXPathNodeSetMerge(ret, list);
6878 xmlXPathFreeNodeSet(list);
6879 }
6880 }
6881 ctxt->context->node = tmp;
6882#ifdef DEBUG_STEP
6883 xmlGenericError(xmlGenericErrorContext,
6884 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
6885#endif
6886 xmlXPathFreeObject(obj);
6887 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
6888}
6889
Owen Taylor3473f882001-02-23 17:55:21 +00006890/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006891 * xmlXPathCompOpEval:
6892 * @ctxt: the XPath parser context with the compiled expression
6893 * @op: an XPath compiled operation
6894 *
6895 * Evaluate the Precompiled XPath operation
6896 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006897static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006898xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) {
6899 int equal, ret;
6900 xmlXPathCompExprPtr comp;
6901 xmlXPathObjectPtr arg1, arg2;
6902
6903 comp = ctxt->comp;
6904 switch (op->op) {
6905 case XPATH_OP_END:
6906 return;
6907 case XPATH_OP_AND:
6908 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6909 xmlXPathBooleanFunction(ctxt, 1);
6910 if (ctxt->value->boolval == 0)
6911 return;
6912 arg2 = valuePop(ctxt);
6913 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6914 xmlXPathBooleanFunction(ctxt, 1);
6915 arg1 = valuePop(ctxt);
6916 arg1->boolval &= arg2->boolval;
6917 valuePush(ctxt, arg1);
6918 xmlXPathFreeObject(arg2);
6919 return;
6920 case XPATH_OP_OR:
6921 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6922 xmlXPathBooleanFunction(ctxt, 1);
6923 if (ctxt->value->boolval == 1)
6924 return;
6925 arg2 = valuePop(ctxt);
6926 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6927 xmlXPathBooleanFunction(ctxt, 1);
6928 arg1 = valuePop(ctxt);
6929 arg1->boolval |= arg2->boolval;
6930 valuePush(ctxt, arg1);
6931 xmlXPathFreeObject(arg2);
6932 return;
6933 case XPATH_OP_EQUAL:
6934 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6935 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6936 equal = xmlXPathEqualValues(ctxt);
6937 if (op->value) valuePush(ctxt, xmlXPathNewBoolean(equal));
6938 else valuePush(ctxt, xmlXPathNewBoolean(!equal));
6939 return;
6940 case XPATH_OP_CMP:
6941 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6942 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6943 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
6944 valuePush(ctxt, xmlXPathNewBoolean(ret));
6945 return;
6946 case XPATH_OP_PLUS:
6947 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6948 if (op->ch2 != -1)
6949 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6950 if (op->value == 0) xmlXPathSubValues(ctxt);
6951 else if (op->value == 1) xmlXPathAddValues(ctxt);
6952 else if (op->value == 2) xmlXPathValueFlipSign(ctxt);
6953 else if (op->value == 3) {
6954 xmlXPathObjectPtr arg;
6955
6956 POP_FLOAT
6957 valuePush(ctxt, arg);
6958 }
6959 return;
6960 case XPATH_OP_MULT:
6961 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6962 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6963 if (op->value == 0) xmlXPathMultValues(ctxt);
6964 else if (op->value == 1) xmlXPathDivValues(ctxt);
6965 else if (op->value == 2) xmlXPathModValues(ctxt);
6966 return;
6967 case XPATH_OP_UNION:
6968 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6969 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6970 CHECK_TYPE(XPATH_NODESET);
6971 arg2 = valuePop(ctxt);
6972
6973 CHECK_TYPE(XPATH_NODESET);
6974 arg1 = valuePop(ctxt);
6975
6976 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
6977 arg2->nodesetval);
6978 valuePush(ctxt, arg1);
6979 xmlXPathFreeObject(arg2);
6980 return;
6981 case XPATH_OP_ROOT:
6982 xmlXPathRoot(ctxt);
6983 return;
6984 case XPATH_OP_NODE:
6985 if (op->ch1 != -1)
6986 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6987 if (op->ch2 != -1)
6988 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6989 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
6990 return;
6991 case XPATH_OP_RESET:
6992 if (op->ch1 != -1)
6993 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6994 if (op->ch2 != -1)
6995 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6996 ctxt->context->node = NULL;
6997 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006998 case XPATH_OP_COLLECT: {
6999 if (op->ch1 == -1)
7000 return;
7001
7002 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7003 xmlXPathNodeCollectAndTest(ctxt, op);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007004 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007005 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007006 case XPATH_OP_VALUE:
7007 valuePush(ctxt,
7008 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
7009 return;
7010 case XPATH_OP_VARIABLE: {
7011 if (op->ch1 != -1)
7012 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7013 if (op->value5 == NULL)
7014 valuePush(ctxt,
7015 xmlXPathVariableLookup(ctxt->context, op->value4));
7016 else {
7017 const xmlChar *URI;
7018 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7019 if (URI == NULL) {
7020 xmlGenericError(xmlGenericErrorContext,
7021 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
7022 op->value4, op->value5);
7023 return;
7024 }
7025 valuePush(ctxt,
7026 xmlXPathVariableLookupNS(ctxt->context,
7027 op->value4, URI));
7028 }
7029 return;
7030 }
7031 case XPATH_OP_FUNCTION: {
7032 xmlXPathFunction func;
7033
7034 if (op->ch1 != -1)
7035 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7036 if (op->value5 == NULL)
7037 func = xmlXPathFunctionLookup(ctxt->context, op->value4);
7038 else {
7039 const xmlChar *URI;
7040 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7041 if (URI == NULL) {
7042 xmlGenericError(xmlGenericErrorContext,
7043 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
7044 op->value4, op->value5);
7045 return;
7046 }
7047 func = xmlXPathFunctionLookupNS(ctxt->context,
7048 op->value4, URI);
7049 }
7050 if (func == NULL) {
7051 xmlGenericError(xmlGenericErrorContext,
7052 "xmlXPathRunEval: function %s not found\n",
7053 op->value4);
7054 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
7055 return;
7056 }
7057 func(ctxt, op->value);
7058 return;
7059 }
7060 case XPATH_OP_ARG:
7061 if (op->ch1 != -1)
7062 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7063 if (op->ch2 != -1)
7064 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7065 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007066 case XPATH_OP_PREDICATE:
7067 case XPATH_OP_FILTER: {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007068 xmlXPathObjectPtr res;
7069 xmlXPathObjectPtr obj, tmp;
7070 xmlNodeSetPtr newset = NULL;
7071 xmlNodeSetPtr oldset;
7072 xmlNodePtr oldnode;
7073 int i;
7074
7075 if (op->ch1 != -1)
7076 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7077 if (op->ch2 == -1)
7078 return;
7079
7080 oldnode = ctxt->context->node;
7081
7082#ifdef LIBXML_XPTR_ENABLED
7083 /*
7084 * Hum are we filtering the result of an XPointer expression
7085 */
7086 if (ctxt->value->type == XPATH_LOCATIONSET) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007087 xmlLocationSetPtr newlocset = NULL;
7088 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007089
7090 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007091 * Extract the old locset, and then evaluate the result of the
7092 * expression for all the element in the locset. use it to grow
7093 * up a new locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007094 */
7095 CHECK_TYPE(XPATH_LOCATIONSET);
7096 obj = valuePop(ctxt);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007097 oldlocset = obj->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007098 ctxt->context->node = NULL;
7099
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007100 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007101 ctxt->context->contextSize = 0;
7102 ctxt->context->proximityPosition = 0;
7103 if (op->ch2 != -1)
7104 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7105 res = valuePop(ctxt);
7106 if (res != NULL)
7107 xmlXPathFreeObject(res);
7108 valuePush(ctxt, obj);
7109 CHECK_ERROR;
7110 return;
7111 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007112 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007113
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007114 for (i = 0; i < oldlocset->locNr; i++) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007115 /*
7116 * Run the evaluation with a node list made of a
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007117 * single item in the nodelocset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007118 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007119 ctxt->context->node = oldlocset->locTab[i]->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007120 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7121 valuePush(ctxt, tmp);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007122 ctxt->context->contextSize = oldlocset->locNr;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007123 ctxt->context->proximityPosition = i + 1;
7124
7125 if (op->ch2 != -1)
7126 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7127 CHECK_ERROR;
7128
7129 /*
7130 * The result of the evaluation need to be tested to
7131 * decided whether the filter succeeded or not
7132 */
7133 res = valuePop(ctxt);
7134 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007135 xmlXPtrLocationSetAdd(newlocset,
7136 xmlXPathObjectCopy(oldlocset->locTab[i]));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007137 }
7138
7139 /*
7140 * Cleanup
7141 */
7142 if (res != NULL)
7143 xmlXPathFreeObject(res);
7144 if (ctxt->value == tmp) {
7145 res = valuePop(ctxt);
7146 xmlXPathFreeObject(res);
7147 }
7148
7149 ctxt->context->node = NULL;
7150 }
7151
7152 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007153 * The result is used as the new evaluation locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007154 */
7155 xmlXPathFreeObject(obj);
7156 ctxt->context->node = NULL;
7157 ctxt->context->contextSize = -1;
7158 ctxt->context->proximityPosition = -1;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007159 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007160 ctxt->context->node = oldnode;
7161 return;
7162 }
7163#endif /* LIBXML_XPTR_ENABLED */
7164
7165 /*
7166 * Extract the old set, and then evaluate the result of the
7167 * expression for all the element in the set. use it to grow
7168 * up a new set.
7169 */
7170 CHECK_TYPE(XPATH_NODESET);
7171 obj = valuePop(ctxt);
7172 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00007173
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007174 oldnode = ctxt->context->node;
7175 ctxt->context->node = NULL;
7176
7177 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
7178 ctxt->context->contextSize = 0;
7179 ctxt->context->proximityPosition = 0;
7180 if (op->ch2 != -1)
7181 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7182 res = valuePop(ctxt);
7183 if (res != NULL)
7184 xmlXPathFreeObject(res);
7185 valuePush(ctxt, obj);
Daniel Veillard911f49a2001-04-07 15:39:35 +00007186 ctxt->context->node = oldnode;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007187 CHECK_ERROR;
7188 } else {
7189 /*
7190 * Initialize the new set.
7191 */
7192 newset = xmlXPathNodeSetCreate(NULL);
7193
7194 for (i = 0; i < oldset->nodeNr; i++) {
7195 /*
7196 * Run the evaluation with a node list made of
7197 * a single item in the nodeset.
7198 */
7199 ctxt->context->node = oldset->nodeTab[i];
7200 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7201 valuePush(ctxt, tmp);
7202 ctxt->context->contextSize = oldset->nodeNr;
7203 ctxt->context->proximityPosition = i + 1;
7204
7205 if (op->ch2 != -1)
7206 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7207 CHECK_ERROR;
7208
7209 /*
7210 * The result of the evaluation need to be tested to
7211 * decided whether the filter succeeded or not
7212 */
7213 res = valuePop(ctxt);
7214 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
7215 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
7216 }
7217
7218 /*
7219 * Cleanup
7220 */
7221 if (res != NULL)
7222 xmlXPathFreeObject(res);
7223 if (ctxt->value == tmp) {
7224 res = valuePop(ctxt);
7225 xmlXPathFreeObject(res);
7226 }
7227
7228 ctxt->context->node = NULL;
7229 }
7230
7231 /*
7232 * The result is used as the new evaluation set.
7233 */
7234 xmlXPathFreeObject(obj);
7235 ctxt->context->node = NULL;
7236 ctxt->context->contextSize = -1;
7237 ctxt->context->proximityPosition = -1;
7238 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
7239 }
7240 ctxt->context->node = oldnode;
7241 return;
7242 }
7243 case XPATH_OP_SORT:
7244 if (op->ch1 != -1)
7245 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7246 if ((ctxt->value != NULL) &&
7247 (ctxt->value->type == XPATH_NODESET) &&
7248 (ctxt->value->nodesetval != NULL))
7249 xmlXPathNodeSetSort(ctxt->value->nodesetval);
7250 return;
7251#ifdef LIBXML_XPTR_ENABLED
7252 case XPATH_OP_RANGETO: {
7253 xmlXPathObjectPtr range;
7254 xmlXPathObjectPtr res, obj;
7255 xmlXPathObjectPtr tmp;
7256 xmlLocationSetPtr newset = NULL;
7257 xmlNodeSetPtr oldset;
7258 int i;
7259
7260 if (op->ch1 != -1)
7261 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7262 if (op->ch2 == -1)
7263 return;
7264
7265 CHECK_TYPE(XPATH_NODESET);
7266 obj = valuePop(ctxt);
7267 oldset = obj->nodesetval;
7268 ctxt->context->node = NULL;
7269
7270 newset = xmlXPtrLocationSetCreate(NULL);
7271
Daniel Veillard911f49a2001-04-07 15:39:35 +00007272 if (oldset != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007273 for (i = 0; i < oldset->nodeNr; i++) {
7274 /*
7275 * Run the evaluation with a node list made of a single item
7276 * in the nodeset.
7277 */
7278 ctxt->context->node = oldset->nodeTab[i];
7279 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7280 valuePush(ctxt, tmp);
7281
7282 if (op->ch2 != -1)
7283 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7284 CHECK_ERROR;
7285
7286 /*
7287 * The result of the evaluation need to be tested to
7288 * decided whether the filter succeeded or not
7289 */
7290 res = valuePop(ctxt);
7291 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
7292 if (range != NULL) {
7293 xmlXPtrLocationSetAdd(newset, range);
7294 }
7295
7296 /*
7297 * Cleanup
7298 */
7299 if (res != NULL)
7300 xmlXPathFreeObject(res);
7301 if (ctxt->value == tmp) {
7302 res = valuePop(ctxt);
7303 xmlXPathFreeObject(res);
7304 }
7305
7306 ctxt->context->node = NULL;
7307 }
Daniel Veillard911f49a2001-04-07 15:39:35 +00007308 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007309
7310 /*
7311 * The result is used as the new evaluation set.
7312 */
7313 xmlXPathFreeObject(obj);
7314 ctxt->context->node = NULL;
7315 ctxt->context->contextSize = -1;
7316 ctxt->context->proximityPosition = -1;
7317 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
7318 return;
7319 }
7320#endif /* LIBXML_XPTR_ENABLED */
7321 }
7322 xmlGenericError(xmlGenericErrorContext,
7323 "XPath: unknown precompiled operation %d\n",
7324 op->op);
7325 return;
7326}
7327
7328/**
7329 * xmlXPathRunEval:
7330 * @ctxt: the XPath parser context with the compiled expression
7331 *
7332 * Evaluate the Precompiled XPath expression in the given context.
7333 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007334static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007335xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
7336 xmlXPathCompExprPtr comp;
7337
7338 if ((ctxt == NULL) || (ctxt->comp == NULL))
7339 return;
7340
7341 if (ctxt->valueTab == NULL) {
7342 /* Allocate the value stack */
7343 ctxt->valueTab = (xmlXPathObjectPtr *)
7344 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
7345 if (ctxt->valueTab == NULL) {
7346 xmlFree(ctxt);
7347 xmlGenericError(xmlGenericErrorContext,
7348 "xmlXPathRunEval: out of memory\n");
7349 return;
7350 }
7351 ctxt->valueNr = 0;
7352 ctxt->valueMax = 10;
7353 ctxt->value = NULL;
7354 }
7355 comp = ctxt->comp;
7356 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
7357}
7358
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007359/************************************************************************
7360 * *
7361 * Public interfaces *
7362 * *
7363 ************************************************************************/
7364
7365/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007366 * xmlXPathEvalPredicate:
7367 * @ctxt: the XPath context
7368 * @res: the Predicate Expression evaluation result
7369 *
7370 * Evaluate a predicate result for the current node.
7371 * A PredicateExpr is evaluated by evaluating the Expr and converting
7372 * the result to a boolean. If the result is a number, the result will
7373 * be converted to true if the number is equal to the position of the
7374 * context node in the context node list (as returned by the position
7375 * function) and will be converted to false otherwise; if the result
7376 * is not a number, then the result will be converted as if by a call
7377 * to the boolean function.
7378 *
7379 * Return 1 if predicate is true, 0 otherwise
7380 */
7381int
7382xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
7383 if (res == NULL) return(0);
7384 switch (res->type) {
7385 case XPATH_BOOLEAN:
7386 return(res->boolval);
7387 case XPATH_NUMBER:
7388 return(res->floatval == ctxt->proximityPosition);
7389 case XPATH_NODESET:
7390 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007391 if (res->nodesetval == NULL)
7392 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007393 return(res->nodesetval->nodeNr != 0);
7394 case XPATH_STRING:
7395 return((res->stringval != NULL) &&
7396 (xmlStrlen(res->stringval) != 0));
7397 default:
7398 STRANGE
7399 }
7400 return(0);
7401}
7402
7403/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007404 * xmlXPathEvaluatePredicateResult:
7405 * @ctxt: the XPath Parser context
7406 * @res: the Predicate Expression evaluation result
7407 *
7408 * Evaluate a predicate result for the current node.
7409 * A PredicateExpr is evaluated by evaluating the Expr and converting
7410 * the result to a boolean. If the result is a number, the result will
7411 * be converted to true if the number is equal to the position of the
7412 * context node in the context node list (as returned by the position
7413 * function) and will be converted to false otherwise; if the result
7414 * is not a number, then the result will be converted as if by a call
7415 * to the boolean function.
7416 *
7417 * Return 1 if predicate is true, 0 otherwise
7418 */
7419int
7420xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
7421 xmlXPathObjectPtr res) {
7422 if (res == NULL) return(0);
7423 switch (res->type) {
7424 case XPATH_BOOLEAN:
7425 return(res->boolval);
7426 case XPATH_NUMBER:
7427 return(res->floatval == ctxt->context->proximityPosition);
7428 case XPATH_NODESET:
7429 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00007430 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00007431 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007432 return(res->nodesetval->nodeNr != 0);
7433 case XPATH_STRING:
7434 return((res->stringval != NULL) &&
7435 (xmlStrlen(res->stringval) != 0));
7436 default:
7437 STRANGE
7438 }
7439 return(0);
7440}
7441
7442/**
7443 * xmlXPathCompile:
7444 * @str: the XPath expression
7445 *
7446 * Compile an XPath expression
7447 *
7448 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7449 * the caller has to free the object.
7450 */
7451xmlXPathCompExprPtr
7452xmlXPathCompile(const xmlChar *str) {
7453 xmlXPathParserContextPtr ctxt;
7454 xmlXPathCompExprPtr comp;
7455
7456 xmlXPathInit();
7457
7458 ctxt = xmlXPathNewParserContext(str, NULL);
7459 xmlXPathCompileExpr(ctxt);
7460
Daniel Veillard40af6492001-04-22 08:50:55 +00007461 if (*ctxt->cur != 0) {
7462 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7463 comp = NULL;
7464 } else {
7465 comp = ctxt->comp;
7466 ctxt->comp = NULL;
7467 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007468 xmlXPathFreeParserContext(ctxt);
7469 return(comp);
7470}
7471
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007472/**
7473 * xmlXPathCompiledEval:
7474 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00007475 * @ctx: the XPath context
7476 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007477 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00007478 *
7479 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7480 * the caller has to free the object.
7481 */
7482xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007483xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00007484 xmlXPathParserContextPtr ctxt;
7485 xmlXPathObjectPtr res, tmp, init = NULL;
7486 int stack = 0;
7487
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007488 if ((comp == NULL) || (ctx == NULL))
7489 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007490 xmlXPathInit();
7491
7492 CHECK_CONTEXT(ctx)
7493
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007494 ctxt = xmlXPathCompParserContext(comp, ctx);
7495 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007496
7497 if (ctxt->value == NULL) {
7498 xmlGenericError(xmlGenericErrorContext,
7499 "xmlXPathEval: evaluation failed\n");
7500 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00007501 } else {
7502 res = valuePop(ctxt);
7503 }
7504
7505 do {
7506 tmp = valuePop(ctxt);
7507 if (tmp != NULL) {
7508 if (tmp != init)
7509 stack++;
7510 xmlXPathFreeObject(tmp);
7511 }
7512 } while (tmp != NULL);
7513 if ((stack != 0) && (res != NULL)) {
7514 xmlGenericError(xmlGenericErrorContext,
7515 "xmlXPathEval: %d object left on the stack\n",
7516 stack);
7517 }
7518 if (ctxt->error != XPATH_EXPRESSION_OK) {
7519 xmlXPathFreeObject(res);
7520 res = NULL;
7521 }
7522
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007523
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007524 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007525 xmlXPathFreeParserContext(ctxt);
7526 return(res);
7527}
7528
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007529/**
7530 * xmlXPathEvalExpr:
7531 * @ctxt: the XPath Parser context
7532 *
7533 * Parse and evaluate an XPath expression in the given context,
7534 * then push the result on the context stack
7535 */
7536void
7537xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
7538 xmlXPathCompileExpr(ctxt);
7539 xmlXPathRunEval(ctxt);
7540}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007541
7542/**
7543 * xmlXPathEval:
7544 * @str: the XPath expression
7545 * @ctx: the XPath context
7546 *
7547 * Evaluate the XPath Location Path in the given context.
7548 *
7549 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7550 * the caller has to free the object.
7551 */
7552xmlXPathObjectPtr
7553xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
7554 xmlXPathParserContextPtr ctxt;
7555 xmlXPathObjectPtr res, tmp, init = NULL;
7556 int stack = 0;
7557
7558 xmlXPathInit();
7559
7560 CHECK_CONTEXT(ctx)
7561
7562 ctxt = xmlXPathNewParserContext(str, ctx);
7563 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007564
7565 if (ctxt->value == NULL) {
7566 xmlGenericError(xmlGenericErrorContext,
7567 "xmlXPathEval: evaluation failed\n");
7568 res = NULL;
7569 } else if (*ctxt->cur != 0) {
7570 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7571 res = NULL;
7572 } else {
7573 res = valuePop(ctxt);
7574 }
7575
7576 do {
7577 tmp = valuePop(ctxt);
7578 if (tmp != NULL) {
7579 if (tmp != init)
7580 stack++;
7581 xmlXPathFreeObject(tmp);
7582 }
7583 } while (tmp != NULL);
7584 if ((stack != 0) && (res != NULL)) {
7585 xmlGenericError(xmlGenericErrorContext,
7586 "xmlXPathEval: %d object left on the stack\n",
7587 stack);
7588 }
7589 if (ctxt->error != XPATH_EXPRESSION_OK) {
7590 xmlXPathFreeObject(res);
7591 res = NULL;
7592 }
7593
Owen Taylor3473f882001-02-23 17:55:21 +00007594 xmlXPathFreeParserContext(ctxt);
7595 return(res);
7596}
7597
7598/**
7599 * xmlXPathEvalExpression:
7600 * @str: the XPath expression
7601 * @ctxt: the XPath context
7602 *
7603 * Evaluate the XPath expression in the given context.
7604 *
7605 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
7606 * the caller has to free the object.
7607 */
7608xmlXPathObjectPtr
7609xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
7610 xmlXPathParserContextPtr pctxt;
7611 xmlXPathObjectPtr res, tmp;
7612 int stack = 0;
7613
7614 xmlXPathInit();
7615
7616 CHECK_CONTEXT(ctxt)
7617
7618 pctxt = xmlXPathNewParserContext(str, ctxt);
7619 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007620
7621 if (*pctxt->cur != 0) {
7622 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7623 res = NULL;
7624 } else {
7625 res = valuePop(pctxt);
7626 }
7627 do {
7628 tmp = valuePop(pctxt);
7629 if (tmp != NULL) {
7630 xmlXPathFreeObject(tmp);
7631 stack++;
7632 }
7633 } while (tmp != NULL);
7634 if ((stack != 0) && (res != NULL)) {
7635 xmlGenericError(xmlGenericErrorContext,
7636 "xmlXPathEvalExpression: %d object left on the stack\n",
7637 stack);
7638 }
7639 xmlXPathFreeParserContext(pctxt);
7640 return(res);
7641}
7642
7643/**
7644 * xmlXPathRegisterAllFunctions:
7645 * @ctxt: the XPath context
7646 *
7647 * Registers all default XPath functions in this context
7648 */
7649void
7650xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
7651{
7652 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
7653 xmlXPathBooleanFunction);
7654 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
7655 xmlXPathCeilingFunction);
7656 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
7657 xmlXPathCountFunction);
7658 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
7659 xmlXPathConcatFunction);
7660 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
7661 xmlXPathContainsFunction);
7662 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
7663 xmlXPathIdFunction);
7664 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
7665 xmlXPathFalseFunction);
7666 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
7667 xmlXPathFloorFunction);
7668 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
7669 xmlXPathLastFunction);
7670 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
7671 xmlXPathLangFunction);
7672 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
7673 xmlXPathLocalNameFunction);
7674 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
7675 xmlXPathNotFunction);
7676 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
7677 xmlXPathNameFunction);
7678 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
7679 xmlXPathNamespaceURIFunction);
7680 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
7681 xmlXPathNormalizeFunction);
7682 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
7683 xmlXPathNumberFunction);
7684 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
7685 xmlXPathPositionFunction);
7686 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
7687 xmlXPathRoundFunction);
7688 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
7689 xmlXPathStringFunction);
7690 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
7691 xmlXPathStringLengthFunction);
7692 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
7693 xmlXPathStartsWithFunction);
7694 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
7695 xmlXPathSubstringFunction);
7696 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
7697 xmlXPathSubstringBeforeFunction);
7698 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
7699 xmlXPathSubstringAfterFunction);
7700 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
7701 xmlXPathSumFunction);
7702 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
7703 xmlXPathTrueFunction);
7704 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
7705 xmlXPathTranslateFunction);
7706}
7707
7708#endif /* LIBXML_XPATH_ENABLED */