blob: f170917d8ba9090257fa8725e9dc8a00ecbb176a [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 Veillard9e7160d2001-03-18 23:17:47 +0000782 default:
783 fprintf(output, "UNKNOWN %d\n", op->op); return;
784 }
785 fprintf(output, "\n");
786finish:
787 if (op->ch1 >= 0)
788 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
789 if (op->ch2 >= 0)
790 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
791}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000792
793void
794xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
795 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000796 int i;
797 char shift[100];
798
799 for (i = 0;((i < depth) && (i < 25));i++)
800 shift[2 * i] = shift[2 * i + 1] = ' ';
801 shift[2 * i] = shift[2 * i + 1] = 0;
802
803 fprintf(output, shift);
804
805 if (comp == NULL) {
806 fprintf(output, "Compiled Expression is NULL\n");
807 return;
808 }
809 fprintf(output, "Compiled Expression : %d elements\n",
810 comp->nbStep);
811 i = comp->last;
812 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
813}
Owen Taylor3473f882001-02-23 17:55:21 +0000814#endif
815
816/************************************************************************
817 * *
818 * Parser stacks related functions and macros *
819 * *
820 ************************************************************************/
821
822/*
823 * Generic function for accessing stacks in the Parser Context
824 */
825
826#define PUSH_AND_POP(type, name) \
827extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
828 if (ctxt->name##Nr >= ctxt->name##Max) { \
829 ctxt->name##Max *= 2; \
830 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
831 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
832 if (ctxt->name##Tab == NULL) { \
833 xmlGenericError(xmlGenericErrorContext, \
834 "realloc failed !\n"); \
835 return(0); \
836 } \
837 } \
838 ctxt->name##Tab[ctxt->name##Nr] = value; \
839 ctxt->name = value; \
840 return(ctxt->name##Nr++); \
841} \
842extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
843 type ret; \
844 if (ctxt->name##Nr <= 0) return(0); \
845 ctxt->name##Nr--; \
846 if (ctxt->name##Nr > 0) \
847 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
848 else \
849 ctxt->name = NULL; \
850 ret = ctxt->name##Tab[ctxt->name##Nr]; \
851 ctxt->name##Tab[ctxt->name##Nr] = 0; \
852 return(ret); \
853} \
854
855PUSH_AND_POP(xmlXPathObjectPtr, value)
856
857/*
858 * Macros for accessing the content. Those should be used only by the parser,
859 * and not exported.
860 *
861 * Dirty macros, i.e. one need to make assumption on the context to use them
862 *
863 * CUR_PTR return the current pointer to the xmlChar to be parsed.
864 * CUR returns the current xmlChar value, i.e. a 8 bit value
865 * in ISO-Latin or UTF-8.
866 * This should be used internally by the parser
867 * only to compare to ASCII values otherwise it would break when
868 * running with UTF-8 encoding.
869 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
870 * to compare on ASCII based substring.
871 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
872 * strings within the parser.
873 * CURRENT Returns the current char value, with the full decoding of
874 * UTF-8 if we are using this mode. It returns an int.
875 * NEXT Skip to the next character, this does the proper decoding
876 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
877 * It returns the pointer to the current xmlChar.
878 */
879
880#define CUR (*ctxt->cur)
881#define SKIP(val) ctxt->cur += (val)
882#define NXT(val) ctxt->cur[(val)]
883#define CUR_PTR ctxt->cur
884
885#define SKIP_BLANKS \
886 while (IS_BLANK(*(ctxt->cur))) NEXT
887
888#define CURRENT (*ctxt->cur)
889#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
890
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000891
892#ifndef DBL_DIG
893#define DBL_DIG 16
894#endif
895#ifndef DBL_EPSILON
896#define DBL_EPSILON 1E-9
897#endif
898
899#define UPPER_DOUBLE 1E9
900#define LOWER_DOUBLE 1E-5
901
902#define INTEGER_DIGITS DBL_DIG
903#define FRACTION_DIGITS (DBL_DIG + 1)
904#define EXPONENT_DIGITS (3 + 2)
905
906/**
907 * xmlXPathFormatNumber:
908 * @number: number to format
909 * @buffer: output buffer
910 * @buffersize: size of output buffer
911 *
912 * Convert the number into a string representation.
913 */
914static void
915xmlXPathFormatNumber(double number, char buffer[], int buffersize)
916{
917 switch (isinf(number)) {
918 case 1:
919 if (buffersize > (int)sizeof("+Infinity"))
920 sprintf(buffer, "+Infinity");
921 break;
922 case -1:
923 if (buffersize > (int)sizeof("-Infinity"))
924 sprintf(buffer, "-Infinity");
925 break;
926 default:
927 if (isnan(number)) {
928 if (buffersize > (int)sizeof("NaN"))
929 sprintf(buffer, "NaN");
930 } else {
Bjorn Reese70a9da52001-04-21 16:57:29 +0000931 /* 3 is sign, decimal point, and terminating zero */
932 char work[DBL_DIG + EXPONENT_DIGITS + 3];
933 int integer_place, fraction_place;
934 char *ptr;
935 char *after_fraction;
936 double absolute_value;
937 int size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000938
Bjorn Reese70a9da52001-04-21 16:57:29 +0000939 absolute_value = fabs(number);
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000940
Bjorn Reese70a9da52001-04-21 16:57:29 +0000941 /*
942 * First choose format - scientific or regular floating point.
943 * In either case, result is in work, and after_fraction points
944 * just past the fractional part.
945 */
946 if ( ((absolute_value > UPPER_DOUBLE) ||
947 (absolute_value < LOWER_DOUBLE)) &&
948 (absolute_value != 0.0) ) {
949 /* Use scientific notation */
950 integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
951 fraction_place = DBL_DIG - 1;
952 snprintf(work, sizeof(work),"%*.*e",
953 integer_place, fraction_place, number);
954 after_fraction = strchr(work + DBL_DIG, 'e');
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000955 }
Bjorn Reese70a9da52001-04-21 16:57:29 +0000956 else {
957 /* Use regular notation */
958 integer_place = 1 + (int)log10(absolute_value);
959 fraction_place = (integer_place > 0)
960 ? DBL_DIG - integer_place
961 : DBL_DIG;
962 size = snprintf(work, sizeof(work), "%0.*f",
963 fraction_place, number);
964 after_fraction = work + size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000965 }
966
Bjorn Reese70a9da52001-04-21 16:57:29 +0000967 /* Remove fractional trailing zeroes */
968 ptr = after_fraction;
969 while (*(--ptr) == '0')
970 ;
971 if (*ptr != '.')
972 ptr++;
973 strcpy(ptr, after_fraction);
974
975 /* Finally copy result back to caller */
976 size = strlen(work) + 1;
977 if (size > buffersize) {
978 work[buffersize - 1] = 0;
979 size = buffersize;
980 }
981 memcpy(buffer, work, size);
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000982 }
983 break;
984 }
985}
986
Owen Taylor3473f882001-02-23 17:55:21 +0000987/************************************************************************
988 * *
989 * Error handling routines *
990 * *
991 ************************************************************************/
992
993
994const char *xmlXPathErrorMessages[] = {
995 "Ok",
996 "Number encoding",
997 "Unfinished litteral",
998 "Start of litteral",
999 "Expected $ for variable reference",
1000 "Undefined variable",
1001 "Invalid predicate",
1002 "Invalid expression",
1003 "Missing closing curly brace",
1004 "Unregistered function",
1005 "Invalid operand",
1006 "Invalid type",
1007 "Invalid number of arguments",
1008 "Invalid context size",
1009 "Invalid context position",
1010 "Memory allocation error",
1011 "Syntax error",
1012 "Resource error",
1013 "Sub resource error",
1014 "Undefined namespace prefix"
1015};
1016
1017/**
1018 * xmlXPathError:
1019 * @ctxt: the XPath Parser context
1020 * @file: the file name
1021 * @line: the line number
1022 * @no: the error number
1023 *
1024 * Create a new xmlNodeSetPtr of type double and of value @val
1025 *
1026 * Returns the newly created object.
1027 */
1028void
1029xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1030 int line, int no) {
1031 int n;
1032 const xmlChar *cur;
1033 const xmlChar *base;
1034
1035 xmlGenericError(xmlGenericErrorContext,
1036 "Error %s:%d: %s\n", file, line,
1037 xmlXPathErrorMessages[no]);
1038
1039 cur = ctxt->cur;
1040 base = ctxt->base;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001041 if ((cur == NULL) || (base == NULL))
1042 return;
1043
Owen Taylor3473f882001-02-23 17:55:21 +00001044 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1045 cur--;
1046 }
1047 n = 0;
1048 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1049 cur--;
1050 if ((*cur == '\n') || (*cur == '\r')) cur++;
1051 base = cur;
1052 n = 0;
1053 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1054 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1055 n++;
1056 }
1057 xmlGenericError(xmlGenericErrorContext, "\n");
1058 cur = ctxt->cur;
1059 while ((*cur == '\n') || (*cur == '\r'))
1060 cur--;
1061 n = 0;
1062 while ((cur != base) && (n++ < 80)) {
1063 xmlGenericError(xmlGenericErrorContext, " ");
1064 base++;
1065 }
1066 xmlGenericError(xmlGenericErrorContext,"^\n");
1067}
1068
1069
1070/************************************************************************
1071 * *
1072 * Routines to handle NodeSets *
1073 * *
1074 ************************************************************************/
1075
1076/**
1077 * xmlXPathCmpNodes:
1078 * @node1: the first node
1079 * @node2: the second node
1080 *
1081 * Compare two nodes w.r.t document order
1082 *
1083 * Returns -2 in case of error 1 if first point < second point, 0 if
1084 * that's the same node, -1 otherwise
1085 */
1086int
1087xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1088 int depth1, depth2;
1089 xmlNodePtr cur, root;
1090
1091 if ((node1 == NULL) || (node2 == NULL))
1092 return(-2);
1093 /*
1094 * a couple of optimizations which will avoid computations in most cases
1095 */
1096 if (node1 == node2)
1097 return(0);
1098 if (node1 == node2->prev)
1099 return(1);
1100 if (node1 == node2->next)
1101 return(-1);
1102
1103 /*
1104 * compute depth to root
1105 */
1106 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1107 if (cur == node1)
1108 return(1);
1109 depth2++;
1110 }
1111 root = cur;
1112 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1113 if (cur == node2)
1114 return(-1);
1115 depth1++;
1116 }
1117 /*
1118 * Distinct document (or distinct entities :-( ) case.
1119 */
1120 if (root != cur) {
1121 return(-2);
1122 }
1123 /*
1124 * get the nearest common ancestor.
1125 */
1126 while (depth1 > depth2) {
1127 depth1--;
1128 node1 = node1->parent;
1129 }
1130 while (depth2 > depth1) {
1131 depth2--;
1132 node2 = node2->parent;
1133 }
1134 while (node1->parent != node2->parent) {
1135 node1 = node1->parent;
1136 node2 = node2->parent;
1137 /* should not happen but just in case ... */
1138 if ((node1 == NULL) || (node2 == NULL))
1139 return(-2);
1140 }
1141 /*
1142 * Find who's first.
1143 */
1144 if (node1 == node2->next)
1145 return(-1);
1146 for (cur = node1->next;cur != NULL;cur = cur->next)
1147 if (cur == node2)
1148 return(1);
1149 return(-1); /* assume there is no sibling list corruption */
1150}
1151
1152/**
1153 * xmlXPathNodeSetSort:
1154 * @set: the node set
1155 *
1156 * Sort the node set in document order
1157 */
1158void
1159xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001160 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001161 xmlNodePtr tmp;
1162
1163 if (set == NULL)
1164 return;
1165
1166 /* Use Shell's sort to sort the node-set */
1167 len = set->nodeNr;
1168 for (incr = len / 2; incr > 0; incr /= 2) {
1169 for (i = incr; i < len; i++) {
1170 j = i - incr;
1171 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001172 if (xmlXPathCmpNodes(set->nodeTab[j],
1173 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001174 tmp = set->nodeTab[j];
1175 set->nodeTab[j] = set->nodeTab[j + incr];
1176 set->nodeTab[j + incr] = tmp;
1177 j -= incr;
1178 } else
1179 break;
1180 }
1181 }
1182 }
1183}
1184
1185#define XML_NODESET_DEFAULT 10
1186/**
1187 * xmlXPathNodeSetCreate:
1188 * @val: an initial xmlNodePtr, or NULL
1189 *
1190 * Create a new xmlNodeSetPtr of type double and of value @val
1191 *
1192 * Returns the newly created object.
1193 */
1194xmlNodeSetPtr
1195xmlXPathNodeSetCreate(xmlNodePtr val) {
1196 xmlNodeSetPtr ret;
1197
1198 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1199 if (ret == NULL) {
1200 xmlGenericError(xmlGenericErrorContext,
1201 "xmlXPathNewNodeSet: out of memory\n");
1202 return(NULL);
1203 }
1204 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1205 if (val != NULL) {
1206 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1207 sizeof(xmlNodePtr));
1208 if (ret->nodeTab == NULL) {
1209 xmlGenericError(xmlGenericErrorContext,
1210 "xmlXPathNewNodeSet: out of memory\n");
1211 return(NULL);
1212 }
1213 memset(ret->nodeTab, 0 ,
1214 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1215 ret->nodeMax = XML_NODESET_DEFAULT;
1216 ret->nodeTab[ret->nodeNr++] = val;
1217 }
1218 return(ret);
1219}
1220
1221/**
1222 * xmlXPathNodeSetAdd:
1223 * @cur: the initial node set
1224 * @val: a new xmlNodePtr
1225 *
1226 * add a new xmlNodePtr ot an existing NodeSet
1227 */
1228void
1229xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1230 int i;
1231
1232 if (val == NULL) return;
1233
1234 /*
1235 * check against doublons
1236 */
1237 for (i = 0;i < cur->nodeNr;i++)
1238 if (cur->nodeTab[i] == val) return;
1239
1240 /*
1241 * grow the nodeTab if needed
1242 */
1243 if (cur->nodeMax == 0) {
1244 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1245 sizeof(xmlNodePtr));
1246 if (cur->nodeTab == NULL) {
1247 xmlGenericError(xmlGenericErrorContext,
1248 "xmlXPathNodeSetAdd: out of memory\n");
1249 return;
1250 }
1251 memset(cur->nodeTab, 0 ,
1252 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1253 cur->nodeMax = XML_NODESET_DEFAULT;
1254 } else if (cur->nodeNr == cur->nodeMax) {
1255 xmlNodePtr *temp;
1256
1257 cur->nodeMax *= 2;
1258 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1259 sizeof(xmlNodePtr));
1260 if (temp == NULL) {
1261 xmlGenericError(xmlGenericErrorContext,
1262 "xmlXPathNodeSetAdd: out of memory\n");
1263 return;
1264 }
1265 cur->nodeTab = temp;
1266 }
1267 cur->nodeTab[cur->nodeNr++] = val;
1268}
1269
1270/**
1271 * xmlXPathNodeSetAddUnique:
1272 * @cur: the initial node set
1273 * @val: a new xmlNodePtr
1274 *
1275 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1276 * when we are sure the node is not already in the set.
1277 */
1278void
1279xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1280 if (val == NULL) return;
1281
1282 /*
1283 * grow the nodeTab if needed
1284 */
1285 if (cur->nodeMax == 0) {
1286 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1287 sizeof(xmlNodePtr));
1288 if (cur->nodeTab == NULL) {
1289 xmlGenericError(xmlGenericErrorContext,
1290 "xmlXPathNodeSetAddUnique: out of memory\n");
1291 return;
1292 }
1293 memset(cur->nodeTab, 0 ,
1294 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1295 cur->nodeMax = XML_NODESET_DEFAULT;
1296 } else if (cur->nodeNr == cur->nodeMax) {
1297 xmlNodePtr *temp;
1298
1299 cur->nodeMax *= 2;
1300 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1301 sizeof(xmlNodePtr));
1302 if (temp == NULL) {
1303 xmlGenericError(xmlGenericErrorContext,
1304 "xmlXPathNodeSetAddUnique: out of memory\n");
1305 return;
1306 }
1307 cur->nodeTab = temp;
1308 }
1309 cur->nodeTab[cur->nodeNr++] = val;
1310}
1311
1312/**
1313 * xmlXPathNodeSetMerge:
1314 * @val1: the first NodeSet or NULL
1315 * @val2: the second NodeSet
1316 *
1317 * Merges two nodesets, all nodes from @val2 are added to @val1
1318 * if @val1 is NULL, a new set is created and copied from @val2
1319 *
1320 * Returns val1 once extended or NULL in case of error.
1321 */
1322xmlNodeSetPtr
1323xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001324 int i, j, initNr, skip;
Owen Taylor3473f882001-02-23 17:55:21 +00001325
1326 if (val2 == NULL) return(val1);
1327 if (val1 == NULL) {
1328 val1 = xmlXPathNodeSetCreate(NULL);
1329 }
1330
1331 initNr = val1->nodeNr;
1332
1333 for (i = 0;i < val2->nodeNr;i++) {
1334 /*
1335 * check against doublons
1336 */
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001337 skip = 0;
1338 for (j = 0; j < initNr; j++) {
1339 if (val1->nodeTab[j] == val2->nodeTab[i]) {
1340 skip = 1;
1341 break;
1342 }
1343 }
1344 if (skip)
1345 continue;
Owen Taylor3473f882001-02-23 17:55:21 +00001346
1347 /*
1348 * grow the nodeTab if needed
1349 */
1350 if (val1->nodeMax == 0) {
1351 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1352 sizeof(xmlNodePtr));
1353 if (val1->nodeTab == NULL) {
1354 xmlGenericError(xmlGenericErrorContext,
1355 "xmlXPathNodeSetMerge: out of memory\n");
1356 return(NULL);
1357 }
1358 memset(val1->nodeTab, 0 ,
1359 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1360 val1->nodeMax = XML_NODESET_DEFAULT;
1361 } else if (val1->nodeNr == val1->nodeMax) {
1362 xmlNodePtr *temp;
1363
1364 val1->nodeMax *= 2;
1365 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1366 sizeof(xmlNodePtr));
1367 if (temp == NULL) {
1368 xmlGenericError(xmlGenericErrorContext,
1369 "xmlXPathNodeSetMerge: out of memory\n");
1370 return(NULL);
1371 }
1372 val1->nodeTab = temp;
1373 }
1374 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1375 }
1376
1377 return(val1);
1378}
1379
1380/**
1381 * xmlXPathNodeSetDel:
1382 * @cur: the initial node set
1383 * @val: an xmlNodePtr
1384 *
1385 * Removes an xmlNodePtr from an existing NodeSet
1386 */
1387void
1388xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1389 int i;
1390
1391 if (cur == NULL) return;
1392 if (val == NULL) return;
1393
1394 /*
1395 * check against doublons
1396 */
1397 for (i = 0;i < cur->nodeNr;i++)
1398 if (cur->nodeTab[i] == val) break;
1399
1400 if (i >= cur->nodeNr) {
1401#ifdef DEBUG
1402 xmlGenericError(xmlGenericErrorContext,
1403 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1404 val->name);
1405#endif
1406 return;
1407 }
1408 cur->nodeNr--;
1409 for (;i < cur->nodeNr;i++)
1410 cur->nodeTab[i] = cur->nodeTab[i + 1];
1411 cur->nodeTab[cur->nodeNr] = NULL;
1412}
1413
1414/**
1415 * xmlXPathNodeSetRemove:
1416 * @cur: the initial node set
1417 * @val: the index to remove
1418 *
1419 * Removes an entry from an existing NodeSet list.
1420 */
1421void
1422xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1423 if (cur == NULL) return;
1424 if (val >= cur->nodeNr) return;
1425 cur->nodeNr--;
1426 for (;val < cur->nodeNr;val++)
1427 cur->nodeTab[val] = cur->nodeTab[val + 1];
1428 cur->nodeTab[cur->nodeNr] = NULL;
1429}
1430
1431/**
1432 * xmlXPathFreeNodeSet:
1433 * @obj: the xmlNodeSetPtr to free
1434 *
1435 * Free the NodeSet compound (not the actual nodes !).
1436 */
1437void
1438xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1439 if (obj == NULL) return;
1440 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001441 xmlFree(obj->nodeTab);
1442 }
Owen Taylor3473f882001-02-23 17:55:21 +00001443 xmlFree(obj);
1444}
1445
1446/**
1447 * xmlXPathFreeValueTree:
1448 * @obj: the xmlNodeSetPtr to free
1449 *
1450 * Free the NodeSet compound and the actual tree, this is different
1451 * from xmlXPathFreeNodeSet()
1452 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001453static void
Owen Taylor3473f882001-02-23 17:55:21 +00001454xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1455 int i;
1456
1457 if (obj == NULL) return;
1458 for (i = 0;i < obj->nodeNr;i++)
1459 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001460 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001461
1462 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001463 xmlFree(obj->nodeTab);
1464 }
Owen Taylor3473f882001-02-23 17:55:21 +00001465 xmlFree(obj);
1466}
1467
1468#if defined(DEBUG) || defined(DEBUG_STEP)
1469/**
1470 * xmlGenericErrorContextNodeSet:
1471 * @output: a FILE * for the output
1472 * @obj: the xmlNodeSetPtr to free
1473 *
1474 * Quick display of a NodeSet
1475 */
1476void
1477xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1478 int i;
1479
1480 if (output == NULL) output = xmlGenericErrorContext;
1481 if (obj == NULL) {
1482 fprintf(output, "NodeSet == NULL !\n");
1483 return;
1484 }
1485 if (obj->nodeNr == 0) {
1486 fprintf(output, "NodeSet is empty\n");
1487 return;
1488 }
1489 if (obj->nodeTab == NULL) {
1490 fprintf(output, " nodeTab == NULL !\n");
1491 return;
1492 }
1493 for (i = 0; i < obj->nodeNr; i++) {
1494 if (obj->nodeTab[i] == NULL) {
1495 fprintf(output, " NULL !\n");
1496 return;
1497 }
1498 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1499 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1500 fprintf(output, " /");
1501 else if (obj->nodeTab[i]->name == NULL)
1502 fprintf(output, " noname!");
1503 else fprintf(output, " %s", obj->nodeTab[i]->name);
1504 }
1505 fprintf(output, "\n");
1506}
1507#endif
1508
1509/**
1510 * xmlXPathNewNodeSet:
1511 * @val: the NodePtr value
1512 *
1513 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1514 * it with the single Node @val
1515 *
1516 * Returns the newly created object.
1517 */
1518xmlXPathObjectPtr
1519xmlXPathNewNodeSet(xmlNodePtr val) {
1520 xmlXPathObjectPtr ret;
1521
1522 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1523 if (ret == NULL) {
1524 xmlGenericError(xmlGenericErrorContext,
1525 "xmlXPathNewNodeSet: out of memory\n");
1526 return(NULL);
1527 }
1528 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1529 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001530 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001531 ret->nodesetval = xmlXPathNodeSetCreate(val);
1532 return(ret);
1533}
1534
1535/**
1536 * xmlXPathNewValueTree:
1537 * @val: the NodePtr value
1538 *
1539 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1540 * it with the tree root @val
1541 *
1542 * Returns the newly created object.
1543 */
1544xmlXPathObjectPtr
1545xmlXPathNewValueTree(xmlNodePtr val) {
1546 xmlXPathObjectPtr ret;
1547
1548 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1549 if (ret == NULL) {
1550 xmlGenericError(xmlGenericErrorContext,
1551 "xmlXPathNewNodeSet: out of memory\n");
1552 return(NULL);
1553 }
1554 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1555 ret->type = XPATH_XSLT_TREE;
1556 ret->nodesetval = xmlXPathNodeSetCreate(val);
1557 return(ret);
1558}
1559
1560/**
1561 * xmlXPathNewNodeSetList:
1562 * @val: an existing NodeSet
1563 *
1564 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1565 * it with the Nodeset @val
1566 *
1567 * Returns the newly created object.
1568 */
1569xmlXPathObjectPtr
1570xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1571 xmlXPathObjectPtr ret;
1572 int i;
1573
1574 if (val == NULL)
1575 ret = NULL;
1576 else if (val->nodeTab == NULL)
1577 ret = xmlXPathNewNodeSet(NULL);
1578 else
1579 {
1580 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1581 for (i = 1; i < val->nodeNr; ++i)
1582 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1583 }
1584
1585 return(ret);
1586}
1587
1588/**
1589 * xmlXPathWrapNodeSet:
1590 * @val: the NodePtr value
1591 *
1592 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1593 *
1594 * Returns the newly created object.
1595 */
1596xmlXPathObjectPtr
1597xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1598 xmlXPathObjectPtr ret;
1599
1600 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1601 if (ret == NULL) {
1602 xmlGenericError(xmlGenericErrorContext,
1603 "xmlXPathWrapNodeSet: out of memory\n");
1604 return(NULL);
1605 }
1606 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1607 ret->type = XPATH_NODESET;
1608 ret->nodesetval = val;
1609 return(ret);
1610}
1611
1612/**
1613 * xmlXPathFreeNodeSetList:
1614 * @obj: an existing NodeSetList object
1615 *
1616 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1617 * the list contrary to xmlXPathFreeObject().
1618 */
1619void
1620xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1621 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001622 xmlFree(obj);
1623}
1624
1625/************************************************************************
1626 * *
1627 * Routines to handle extra functions *
1628 * *
1629 ************************************************************************/
1630
1631/**
1632 * xmlXPathRegisterFunc:
1633 * @ctxt: the XPath context
1634 * @name: the function name
1635 * @f: the function implementation or NULL
1636 *
1637 * Register a new function. If @f is NULL it unregisters the function
1638 *
1639 * Returns 0 in case of success, -1 in case of error
1640 */
1641int
1642xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
1643 xmlXPathFunction f) {
1644 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
1645}
1646
1647/**
1648 * xmlXPathRegisterFuncNS:
1649 * @ctxt: the XPath context
1650 * @name: the function name
1651 * @ns_uri: the function namespace URI
1652 * @f: the function implementation or NULL
1653 *
1654 * Register a new function. If @f is NULL it unregisters the function
1655 *
1656 * Returns 0 in case of success, -1 in case of error
1657 */
1658int
1659xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1660 const xmlChar *ns_uri, xmlXPathFunction f) {
1661 if (ctxt == NULL)
1662 return(-1);
1663 if (name == NULL)
1664 return(-1);
1665
1666 if (ctxt->funcHash == NULL)
1667 ctxt->funcHash = xmlHashCreate(0);
1668 if (ctxt->funcHash == NULL)
1669 return(-1);
1670 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
1671}
1672
1673/**
1674 * xmlXPathFunctionLookup:
1675 * @ctxt: the XPath context
1676 * @name: the function name
1677 *
1678 * Search in the Function array of the context for the given
1679 * function.
1680 *
1681 * Returns the xmlXPathFunction or NULL if not found
1682 */
1683xmlXPathFunction
1684xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1685 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
1686}
1687
1688/**
1689 * xmlXPathFunctionLookupNS:
1690 * @ctxt: the XPath context
1691 * @name: the function name
1692 * @ns_uri: the function namespace URI
1693 *
1694 * Search in the Function array of the context for the given
1695 * function.
1696 *
1697 * Returns the xmlXPathFunction or NULL if not found
1698 */
1699xmlXPathFunction
1700xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1701 const xmlChar *ns_uri) {
1702 if (ctxt == NULL)
1703 return(NULL);
1704 if (ctxt->funcHash == NULL)
1705 return(NULL);
1706 if (name == NULL)
1707 return(NULL);
1708
1709 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
1710}
1711
1712/**
1713 * xmlXPathRegisteredFuncsCleanup:
1714 * @ctxt: the XPath context
1715 *
1716 * Cleanup the XPath context data associated to registered functions
1717 */
1718void
1719xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
1720 if (ctxt == NULL)
1721 return;
1722
1723 xmlHashFree(ctxt->funcHash, NULL);
1724 ctxt->funcHash = NULL;
1725}
1726
1727/************************************************************************
1728 * *
1729 * Routines to handle Variable *
1730 * *
1731 ************************************************************************/
1732
1733/**
1734 * xmlXPathRegisterVariable:
1735 * @ctxt: the XPath context
1736 * @name: the variable name
1737 * @value: the variable value or NULL
1738 *
1739 * Register a new variable value. If @value is NULL it unregisters
1740 * the variable
1741 *
1742 * Returns 0 in case of success, -1 in case of error
1743 */
1744int
1745xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
1746 xmlXPathObjectPtr value) {
1747 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
1748}
1749
1750/**
1751 * xmlXPathRegisterVariableNS:
1752 * @ctxt: the XPath context
1753 * @name: the variable name
1754 * @ns_uri: the variable namespace URI
1755 * @value: the variable value or NULL
1756 *
1757 * Register a new variable value. If @value is NULL it unregisters
1758 * the variable
1759 *
1760 * Returns 0 in case of success, -1 in case of error
1761 */
1762int
1763xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1764 const xmlChar *ns_uri,
1765 xmlXPathObjectPtr value) {
1766 if (ctxt == NULL)
1767 return(-1);
1768 if (name == NULL)
1769 return(-1);
1770
1771 if (ctxt->varHash == NULL)
1772 ctxt->varHash = xmlHashCreate(0);
1773 if (ctxt->varHash == NULL)
1774 return(-1);
1775 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
1776 (void *) value,
1777 (xmlHashDeallocator)xmlXPathFreeObject));
1778}
1779
1780/**
1781 * xmlXPathRegisterVariableLookup:
1782 * @ctxt: the XPath context
1783 * @f: the lookup function
1784 * @data: the lookup data
1785 *
1786 * register an external mechanism to do variable lookup
1787 */
1788void
1789xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
1790 xmlXPathVariableLookupFunc f, void *data) {
1791 if (ctxt == NULL)
1792 return;
1793 ctxt->varLookupFunc = (void *) f;
1794 ctxt->varLookupData = data;
1795}
1796
1797/**
1798 * xmlXPathVariableLookup:
1799 * @ctxt: the XPath context
1800 * @name: the variable name
1801 *
1802 * Search in the Variable array of the context for the given
1803 * variable value.
1804 *
1805 * Returns the value or NULL if not found
1806 */
1807xmlXPathObjectPtr
1808xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1809 if (ctxt == NULL)
1810 return(NULL);
1811
1812 if (ctxt->varLookupFunc != NULL) {
1813 xmlXPathObjectPtr ret;
1814
1815 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1816 (ctxt->varLookupData, name, NULL);
1817 if (ret != NULL) return(ret);
1818 }
1819 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
1820}
1821
1822/**
1823 * xmlXPathVariableLookupNS:
1824 * @ctxt: the XPath context
1825 * @name: the variable name
1826 * @ns_uri: the variable namespace URI
1827 *
1828 * Search in the Variable array of the context for the given
1829 * variable value.
1830 *
1831 * Returns the value or NULL if not found
1832 */
1833xmlXPathObjectPtr
1834xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1835 const xmlChar *ns_uri) {
1836 if (ctxt == NULL)
1837 return(NULL);
1838
1839 if (ctxt->varLookupFunc != NULL) {
1840 xmlXPathObjectPtr ret;
1841
1842 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1843 (ctxt->varLookupData, name, ns_uri);
1844 if (ret != NULL) return(ret);
1845 }
1846
1847 if (ctxt->varHash == NULL)
1848 return(NULL);
1849 if (name == NULL)
1850 return(NULL);
1851
1852 return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
1853}
1854
1855/**
1856 * xmlXPathRegisteredVariablesCleanup:
1857 * @ctxt: the XPath context
1858 *
1859 * Cleanup the XPath context data associated to registered variables
1860 */
1861void
1862xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
1863 if (ctxt == NULL)
1864 return;
1865
1866 xmlHashFree(ctxt->varHash, NULL);
1867 ctxt->varHash = NULL;
1868}
1869
1870/**
1871 * xmlXPathRegisterNs:
1872 * @ctxt: the XPath context
1873 * @prefix: the namespace prefix
1874 * @ns_uri: the namespace name
1875 *
1876 * Register a new namespace. If @ns_uri is NULL it unregisters
1877 * the namespace
1878 *
1879 * Returns 0 in case of success, -1 in case of error
1880 */
1881int
1882xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
1883 const xmlChar *ns_uri) {
1884 if (ctxt == NULL)
1885 return(-1);
1886 if (prefix == NULL)
1887 return(-1);
1888
1889 if (ctxt->nsHash == NULL)
1890 ctxt->nsHash = xmlHashCreate(10);
1891 if (ctxt->nsHash == NULL)
1892 return(-1);
1893 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
1894 (xmlHashDeallocator)xmlFree));
1895}
1896
1897/**
1898 * xmlXPathNsLookup:
1899 * @ctxt: the XPath context
1900 * @prefix: the namespace prefix value
1901 *
1902 * Search in the namespace declaration array of the context for the given
1903 * namespace name associated to the given prefix
1904 *
1905 * Returns the value or NULL if not found
1906 */
1907const xmlChar *
1908xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
1909 if (ctxt == NULL)
1910 return(NULL);
1911 if (prefix == NULL)
1912 return(NULL);
1913
1914#ifdef XML_XML_NAMESPACE
1915 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
1916 return(XML_XML_NAMESPACE);
1917#endif
1918
1919 if (ctxt->nsHash == NULL)
1920 return(NULL);
1921
1922 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
1923}
1924
1925/**
1926 * xmlXPathRegisteredVariablesCleanup:
1927 * @ctxt: the XPath context
1928 *
1929 * Cleanup the XPath context data associated to registered variables
1930 */
1931void
1932xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
1933 if (ctxt == NULL)
1934 return;
1935
1936 xmlHashFree(ctxt->nsHash, NULL);
1937 ctxt->nsHash = NULL;
1938}
1939
1940/************************************************************************
1941 * *
1942 * Routines to handle Values *
1943 * *
1944 ************************************************************************/
1945
1946/* Allocations are terrible, one need to optimize all this !!! */
1947
1948/**
1949 * xmlXPathNewFloat:
1950 * @val: the double value
1951 *
1952 * Create a new xmlXPathObjectPtr of type double and of value @val
1953 *
1954 * Returns the newly created object.
1955 */
1956xmlXPathObjectPtr
1957xmlXPathNewFloat(double val) {
1958 xmlXPathObjectPtr ret;
1959
1960 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1961 if (ret == NULL) {
1962 xmlGenericError(xmlGenericErrorContext,
1963 "xmlXPathNewFloat: out of memory\n");
1964 return(NULL);
1965 }
1966 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1967 ret->type = XPATH_NUMBER;
1968 ret->floatval = val;
1969 return(ret);
1970}
1971
1972/**
1973 * xmlXPathNewBoolean:
1974 * @val: the boolean value
1975 *
1976 * Create a new xmlXPathObjectPtr of type boolean and of value @val
1977 *
1978 * Returns the newly created object.
1979 */
1980xmlXPathObjectPtr
1981xmlXPathNewBoolean(int val) {
1982 xmlXPathObjectPtr ret;
1983
1984 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1985 if (ret == NULL) {
1986 xmlGenericError(xmlGenericErrorContext,
1987 "xmlXPathNewBoolean: out of memory\n");
1988 return(NULL);
1989 }
1990 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1991 ret->type = XPATH_BOOLEAN;
1992 ret->boolval = (val != 0);
1993 return(ret);
1994}
1995
1996/**
1997 * xmlXPathNewString:
1998 * @val: the xmlChar * value
1999 *
2000 * Create a new xmlXPathObjectPtr of type string and of value @val
2001 *
2002 * Returns the newly created object.
2003 */
2004xmlXPathObjectPtr
2005xmlXPathNewString(const xmlChar *val) {
2006 xmlXPathObjectPtr ret;
2007
2008 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2009 if (ret == NULL) {
2010 xmlGenericError(xmlGenericErrorContext,
2011 "xmlXPathNewString: out of memory\n");
2012 return(NULL);
2013 }
2014 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2015 ret->type = XPATH_STRING;
2016 if (val != NULL)
2017 ret->stringval = xmlStrdup(val);
2018 else
2019 ret->stringval = xmlStrdup((const xmlChar *)"");
2020 return(ret);
2021}
2022
2023/**
2024 * xmlXPathNewCString:
2025 * @val: the char * value
2026 *
2027 * Create a new xmlXPathObjectPtr of type string and of value @val
2028 *
2029 * Returns the newly created object.
2030 */
2031xmlXPathObjectPtr
2032xmlXPathNewCString(const char *val) {
2033 xmlXPathObjectPtr ret;
2034
2035 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2036 if (ret == NULL) {
2037 xmlGenericError(xmlGenericErrorContext,
2038 "xmlXPathNewCString: out of memory\n");
2039 return(NULL);
2040 }
2041 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2042 ret->type = XPATH_STRING;
2043 ret->stringval = xmlStrdup(BAD_CAST val);
2044 return(ret);
2045}
2046
2047/**
2048 * xmlXPathObjectCopy:
2049 * @val: the original object
2050 *
2051 * allocate a new copy of a given object
2052 *
2053 * Returns the newly created object.
2054 */
2055xmlXPathObjectPtr
2056xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2057 xmlXPathObjectPtr ret;
2058
2059 if (val == NULL)
2060 return(NULL);
2061
2062 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2063 if (ret == NULL) {
2064 xmlGenericError(xmlGenericErrorContext,
2065 "xmlXPathObjectCopy: out of memory\n");
2066 return(NULL);
2067 }
2068 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2069 switch (val->type) {
2070 case XPATH_BOOLEAN:
2071 case XPATH_NUMBER:
2072 case XPATH_POINT:
2073 case XPATH_RANGE:
2074 break;
2075 case XPATH_STRING:
2076 ret->stringval = xmlStrdup(val->stringval);
2077 break;
2078 case XPATH_XSLT_TREE:
2079 if ((val->nodesetval != NULL) &&
2080 (val->nodesetval->nodeTab != NULL))
2081 ret->nodesetval = xmlXPathNodeSetCreate(
2082 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
2083 else
2084 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
2085 break;
2086 case XPATH_NODESET:
2087 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
2088 break;
2089 case XPATH_LOCATIONSET:
2090#ifdef LIBXML_XPTR_ENABLED
2091 {
2092 xmlLocationSetPtr loc = val->user;
2093 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2094 break;
2095 }
2096#endif
2097 case XPATH_UNDEFINED:
2098 case XPATH_USERS:
2099 xmlGenericError(xmlGenericErrorContext,
2100 "xmlXPathObjectCopy: unsupported type %d\n",
2101 val->type);
2102 break;
2103 }
2104 return(ret);
2105}
2106
2107/**
2108 * xmlXPathFreeObject:
2109 * @obj: the object to free
2110 *
2111 * Free up an xmlXPathObjectPtr object.
2112 */
2113void
2114xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2115 if (obj == NULL) return;
2116 if (obj->type == XPATH_NODESET) {
Daniel Veillard77851712001-02-27 21:54:07 +00002117 if (obj->boolval) {
2118 obj->type = XPATH_XSLT_TREE;
2119 if (obj->nodesetval != NULL)
2120 xmlXPathFreeValueTree(obj->nodesetval);
2121 } else {
2122 if (obj->nodesetval != NULL)
2123 xmlXPathFreeNodeSet(obj->nodesetval);
2124 }
Owen Taylor3473f882001-02-23 17:55:21 +00002125#ifdef LIBXML_XPTR_ENABLED
2126 } else if (obj->type == XPATH_LOCATIONSET) {
2127 if (obj->user != NULL)
2128 xmlXPtrFreeLocationSet(obj->user);
2129#endif
2130 } else if (obj->type == XPATH_STRING) {
2131 if (obj->stringval != NULL)
2132 xmlFree(obj->stringval);
2133 } else if (obj->type == XPATH_XSLT_TREE) {
2134 if (obj->nodesetval != NULL)
2135 xmlXPathFreeValueTree(obj->nodesetval);
2136 }
2137
Owen Taylor3473f882001-02-23 17:55:21 +00002138 xmlFree(obj);
2139}
2140
2141/************************************************************************
2142 * *
2143 * Routines to handle XPath contexts *
2144 * *
2145 ************************************************************************/
2146
2147/**
2148 * xmlXPathNewContext:
2149 * @doc: the XML document
2150 *
2151 * Create a new xmlXPathContext
2152 *
2153 * Returns the xmlXPathContext just allocated.
2154 */
2155xmlXPathContextPtr
2156xmlXPathNewContext(xmlDocPtr doc) {
2157 xmlXPathContextPtr ret;
2158
2159 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
2160 if (ret == NULL) {
2161 xmlGenericError(xmlGenericErrorContext,
2162 "xmlXPathNewContext: out of memory\n");
2163 return(NULL);
2164 }
2165 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
2166 ret->doc = doc;
2167 ret->node = NULL;
2168
2169 ret->varHash = NULL;
2170
2171 ret->nb_types = 0;
2172 ret->max_types = 0;
2173 ret->types = NULL;
2174
2175 ret->funcHash = xmlHashCreate(0);
2176
2177 ret->nb_axis = 0;
2178 ret->max_axis = 0;
2179 ret->axis = NULL;
2180
2181 ret->nsHash = NULL;
2182 ret->user = NULL;
2183
2184 ret->contextSize = -1;
2185 ret->proximityPosition = -1;
2186
2187 xmlXPathRegisterAllFunctions(ret);
2188
2189 return(ret);
2190}
2191
2192/**
2193 * xmlXPathFreeContext:
2194 * @ctxt: the context to free
2195 *
2196 * Free up an xmlXPathContext
2197 */
2198void
2199xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
2200 xmlXPathRegisteredNsCleanup(ctxt);
2201 xmlXPathRegisteredFuncsCleanup(ctxt);
2202 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00002203 xmlFree(ctxt);
2204}
2205
2206/************************************************************************
2207 * *
2208 * Routines to handle XPath parser contexts *
2209 * *
2210 ************************************************************************/
2211
2212#define CHECK_CTXT(ctxt) \
2213 if (ctxt == NULL) { \
2214 xmlGenericError(xmlGenericErrorContext, \
2215 "%s:%d Internal error: ctxt == NULL\n", \
2216 __FILE__, __LINE__); \
2217 } \
2218
2219
2220#define CHECK_CONTEXT(ctxt) \
2221 if (ctxt == NULL) { \
2222 xmlGenericError(xmlGenericErrorContext, \
2223 "%s:%d Internal error: no context\n", \
2224 __FILE__, __LINE__); \
2225 } \
2226 else if (ctxt->doc == NULL) { \
2227 xmlGenericError(xmlGenericErrorContext, \
2228 "%s:%d Internal error: no document\n", \
2229 __FILE__, __LINE__); \
2230 } \
2231 else if (ctxt->doc->children == NULL) { \
2232 xmlGenericError(xmlGenericErrorContext, \
2233 "%s:%d Internal error: document without root\n", \
2234 __FILE__, __LINE__); \
2235 } \
2236
2237
2238/**
2239 * xmlXPathNewParserContext:
2240 * @str: the XPath expression
2241 * @ctxt: the XPath context
2242 *
2243 * Create a new xmlXPathParserContext
2244 *
2245 * Returns the xmlXPathParserContext just allocated.
2246 */
2247xmlXPathParserContextPtr
2248xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
2249 xmlXPathParserContextPtr ret;
2250
2251 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2252 if (ret == NULL) {
2253 xmlGenericError(xmlGenericErrorContext,
2254 "xmlXPathNewParserContext: out of memory\n");
2255 return(NULL);
2256 }
2257 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2258 ret->cur = ret->base = str;
2259 ret->context = ctxt;
2260
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002261 ret->comp = xmlXPathNewCompExpr();
2262 if (ret->comp == NULL) {
2263 xmlFree(ret->valueTab);
2264 xmlFree(ret);
2265 return(NULL);
2266 }
2267
2268 return(ret);
2269}
2270
2271/**
2272 * xmlXPathCompParserContext:
2273 * @comp: the XPath compiled expression
2274 * @ctxt: the XPath context
2275 *
2276 * Create a new xmlXPathParserContext when processing a compiled expression
2277 *
2278 * Returns the xmlXPathParserContext just allocated.
2279 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002280static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002281xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
2282 xmlXPathParserContextPtr ret;
2283
2284 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2285 if (ret == NULL) {
2286 xmlGenericError(xmlGenericErrorContext,
2287 "xmlXPathNewParserContext: out of memory\n");
2288 return(NULL);
2289 }
2290 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2291
Owen Taylor3473f882001-02-23 17:55:21 +00002292 /* Allocate the value stack */
2293 ret->valueTab = (xmlXPathObjectPtr *)
2294 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002295 if (ret->valueTab == NULL) {
2296 xmlFree(ret);
2297 xmlGenericError(xmlGenericErrorContext,
2298 "xmlXPathNewParserContext: out of memory\n");
2299 return(NULL);
2300 }
Owen Taylor3473f882001-02-23 17:55:21 +00002301 ret->valueNr = 0;
2302 ret->valueMax = 10;
2303 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002304
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00002305 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002306 ret->comp = comp;
2307
Owen Taylor3473f882001-02-23 17:55:21 +00002308 return(ret);
2309}
2310
2311/**
2312 * xmlXPathFreeParserContext:
2313 * @ctxt: the context to free
2314 *
2315 * Free up an xmlXPathParserContext
2316 */
2317void
2318xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
2319 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002320 xmlFree(ctxt->valueTab);
2321 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002322 if (ctxt->comp)
2323 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00002324 xmlFree(ctxt);
2325}
2326
2327/************************************************************************
2328 * *
2329 * The implicit core function library *
2330 * *
2331 ************************************************************************/
2332
2333/*
2334 * Auto-pop and cast to a number
2335 */
2336void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
2337
2338
2339#define POP_FLOAT \
2340 arg = valuePop(ctxt); \
2341 if (arg == NULL) { \
2342 XP_ERROR(XPATH_INVALID_OPERAND); \
2343 } \
2344 if (arg->type != XPATH_NUMBER) { \
2345 valuePush(ctxt, arg); \
2346 xmlXPathNumberFunction(ctxt, 1); \
2347 arg = valuePop(ctxt); \
2348 }
2349
2350/**
2351 * xmlXPathCompareNodeSetFloat:
2352 * @ctxt: the XPath Parser context
2353 * @inf: less than (1) or greater than (0)
2354 * @strict: is the comparison strict
2355 * @arg: the node set
2356 * @f: the value
2357 *
2358 * Implement the compare operation between a nodeset and a number
2359 * @ns < @val (1, 1, ...
2360 * @ns <= @val (1, 0, ...
2361 * @ns > @val (0, 1, ...
2362 * @ns >= @val (0, 0, ...
2363 *
2364 * If one object to be compared is a node-set and the other is a number,
2365 * then the comparison will be true if and only if there is a node in the
2366 * node-set such that the result of performing the comparison on the number
2367 * to be compared and on the result of converting the string-value of that
2368 * node to a number using the number function is true.
2369 *
2370 * Returns 0 or 1 depending on the results of the test.
2371 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002372static int
Owen Taylor3473f882001-02-23 17:55:21 +00002373xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
2374 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
2375 int i, ret = 0;
2376 xmlNodeSetPtr ns;
2377 xmlChar *str2;
2378
2379 if ((f == NULL) || (arg == NULL) ||
2380 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2381 xmlXPathFreeObject(arg);
2382 xmlXPathFreeObject(f);
2383 return(0);
2384 }
2385 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002386 if (ns != NULL) {
2387 for (i = 0;i < ns->nodeNr;i++) {
2388 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2389 if (str2 != NULL) {
2390 valuePush(ctxt,
2391 xmlXPathNewString(str2));
2392 xmlFree(str2);
2393 xmlXPathNumberFunction(ctxt, 1);
2394 valuePush(ctxt, xmlXPathObjectCopy(f));
2395 ret = xmlXPathCompareValues(ctxt, inf, strict);
2396 if (ret)
2397 break;
2398 }
2399 }
Owen Taylor3473f882001-02-23 17:55:21 +00002400 }
2401 xmlXPathFreeObject(arg);
2402 xmlXPathFreeObject(f);
2403 return(ret);
2404}
2405
2406/**
2407 * xmlXPathCompareNodeSetString:
2408 * @ctxt: the XPath Parser context
2409 * @inf: less than (1) or greater than (0)
2410 * @strict: is the comparison strict
2411 * @arg: the node set
2412 * @s: the value
2413 *
2414 * Implement the compare operation between a nodeset and a string
2415 * @ns < @val (1, 1, ...
2416 * @ns <= @val (1, 0, ...
2417 * @ns > @val (0, 1, ...
2418 * @ns >= @val (0, 0, ...
2419 *
2420 * If one object to be compared is a node-set and the other is a string,
2421 * then the comparison will be true if and only if there is a node in
2422 * the node-set such that the result of performing the comparison on the
2423 * string-value of the node and the other string is true.
2424 *
2425 * Returns 0 or 1 depending on the results of the test.
2426 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002427static int
Owen Taylor3473f882001-02-23 17:55:21 +00002428xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
2429 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
2430 int i, ret = 0;
2431 xmlNodeSetPtr ns;
2432 xmlChar *str2;
2433
2434 if ((s == NULL) || (arg == NULL) ||
2435 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2436 xmlXPathFreeObject(arg);
2437 xmlXPathFreeObject(s);
2438 return(0);
2439 }
2440 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002441 if (ns != NULL) {
2442 for (i = 0;i < ns->nodeNr;i++) {
2443 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2444 if (str2 != NULL) {
2445 valuePush(ctxt,
2446 xmlXPathNewString(str2));
2447 xmlFree(str2);
2448 valuePush(ctxt, xmlXPathObjectCopy(s));
2449 ret = xmlXPathCompareValues(ctxt, inf, strict);
2450 if (ret)
2451 break;
2452 }
2453 }
Owen Taylor3473f882001-02-23 17:55:21 +00002454 }
2455 xmlXPathFreeObject(arg);
2456 xmlXPathFreeObject(s);
2457 return(ret);
2458}
2459
2460/**
2461 * xmlXPathCompareNodeSets:
Owen Taylor3473f882001-02-23 17:55:21 +00002462 * @op: less than (-1), equal (0) or greater than (1)
2463 * @strict: is the comparison strict
2464 * @arg1: the fist node set object
2465 * @arg2: the second node set object
2466 *
2467 * Implement the compare operation on nodesets:
2468 *
2469 * If both objects to be compared are node-sets, then the comparison
2470 * will be true if and only if there is a node in the first node-set
2471 * and a node in the second node-set such that the result of performing
2472 * the comparison on the string-values of the two nodes is true.
2473 * ....
2474 * When neither object to be compared is a node-set and the operator
2475 * is <=, <, >= or >, then the objects are compared by converting both
2476 * objects to numbers and comparing the numbers according to IEEE 754.
2477 * ....
2478 * The number function converts its argument to a number as follows:
2479 * - a string that consists of optional whitespace followed by an
2480 * optional minus sign followed by a Number followed by whitespace
2481 * is converted to the IEEE 754 number that is nearest (according
2482 * to the IEEE 754 round-to-nearest rule) to the mathematical value
2483 * represented by the string; any other string is converted to NaN
2484 *
2485 * Conclusion all nodes need to be converted first to their string value
2486 * and then the comparison must be done when possible
2487 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002488static int
2489xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00002490 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2491 int i, j, init = 0;
2492 double val1;
2493 double *values2;
2494 int ret = 0;
2495 xmlChar *str;
2496 xmlNodeSetPtr ns1;
2497 xmlNodeSetPtr ns2;
2498
2499 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002500 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
2501 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002502 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002503 }
Owen Taylor3473f882001-02-23 17:55:21 +00002504 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002505 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
2506 xmlXPathFreeObject(arg1);
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
2511 ns1 = arg1->nodesetval;
2512 ns2 = arg2->nodesetval;
2513
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002514 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002515 xmlXPathFreeObject(arg1);
2516 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002517 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002518 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002519 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002520 xmlXPathFreeObject(arg1);
2521 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002522 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002523 }
Owen Taylor3473f882001-02-23 17:55:21 +00002524
2525 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
2526 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002527 xmlXPathFreeObject(arg1);
2528 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002529 return(0);
2530 }
2531 for (i = 0;i < ns1->nodeNr;i++) {
2532 str = xmlNodeGetContent(ns1->nodeTab[i]);
2533 if (str == NULL)
2534 continue;
2535 val1 = xmlXPathStringEvalNumber(str);
2536 xmlFree(str);
2537 if (isnan(val1))
2538 continue;
2539 for (j = 0;j < ns2->nodeNr;j++) {
2540 if (init == 0) {
2541 str = xmlNodeGetContent(ns2->nodeTab[j]);
2542 if (str == NULL) {
2543 values2[j] = xmlXPathNAN;
2544 } else {
2545 values2[j] = xmlXPathStringEvalNumber(str);
2546 xmlFree(str);
2547 }
2548 }
2549 if (isnan(values2[j]))
2550 continue;
2551 if (inf && strict)
2552 ret = (val1 < values2[j]);
2553 else if (inf && !strict)
2554 ret = (val1 <= values2[j]);
2555 else if (!inf && strict)
2556 ret = (val1 > values2[j]);
2557 else if (!inf && !strict)
2558 ret = (val1 >= values2[j]);
2559 if (ret)
2560 break;
2561 }
2562 if (ret)
2563 break;
2564 init = 1;
2565 }
2566 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002567 xmlXPathFreeObject(arg1);
2568 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002569 return(ret);
2570 return(0);
2571}
2572
2573/**
2574 * xmlXPathCompareNodeSetValue:
2575 * @ctxt: the XPath Parser context
2576 * @inf: less than (1) or greater than (0)
2577 * @strict: is the comparison strict
2578 * @arg: the node set
2579 * @val: the value
2580 *
2581 * Implement the compare operation between a nodeset and a value
2582 * @ns < @val (1, 1, ...
2583 * @ns <= @val (1, 0, ...
2584 * @ns > @val (0, 1, ...
2585 * @ns >= @val (0, 0, ...
2586 *
2587 * If one object to be compared is a node-set and the other is a boolean,
2588 * then the comparison will be true if and only if the result of performing
2589 * the comparison on the boolean and on the result of converting
2590 * the node-set to a boolean using the boolean function is true.
2591 *
2592 * Returns 0 or 1 depending on the results of the test.
2593 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002594static int
Owen Taylor3473f882001-02-23 17:55:21 +00002595xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
2596 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
2597 if ((val == NULL) || (arg == NULL) ||
2598 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2599 return(0);
2600
2601 switch(val->type) {
2602 case XPATH_NUMBER:
2603 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
2604 case XPATH_NODESET:
2605 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002606 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00002607 case XPATH_STRING:
2608 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
2609 case XPATH_BOOLEAN:
2610 valuePush(ctxt, arg);
2611 xmlXPathBooleanFunction(ctxt, 1);
2612 valuePush(ctxt, val);
2613 return(xmlXPathCompareValues(ctxt, inf, strict));
2614 default:
2615 TODO
2616 return(0);
2617 }
2618 return(0);
2619}
2620
2621/**
2622 * xmlXPathEqualNodeSetString
2623 * @arg: the nodeset object argument
2624 * @str: the string to compare to.
2625 *
2626 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2627 * If one object to be compared is a node-set and the other is a string,
2628 * then the comparison will be true if and only if there is a node in
2629 * the node-set such that the result of performing the comparison on the
2630 * string-value of the node and the other string is true.
2631 *
2632 * Returns 0 or 1 depending on the results of the test.
2633 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002634static int
Owen Taylor3473f882001-02-23 17:55:21 +00002635xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
2636 int i;
2637 xmlNodeSetPtr ns;
2638 xmlChar *str2;
2639
2640 if ((str == NULL) || (arg == NULL) ||
2641 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2642 return(0);
2643 ns = arg->nodesetval;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002644 if (ns == NULL)
2645 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002646 if (ns->nodeNr <= 0)
2647 return(0);
2648 for (i = 0;i < ns->nodeNr;i++) {
2649 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2650 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
2651 xmlFree(str2);
2652 return(1);
2653 }
2654 if (str2 != NULL)
2655 xmlFree(str2);
2656 }
2657 return(0);
2658}
2659
2660/**
2661 * xmlXPathEqualNodeSetFloat
2662 * @arg: the nodeset object argument
2663 * @f: the float to compare to
2664 *
2665 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2666 * If one object to be compared is a node-set and the other is a number,
2667 * then the comparison will be true if and only if there is a node in
2668 * the node-set such that the result of performing the comparison on the
2669 * number to be compared and on the result of converting the string-value
2670 * of that node to a number using the number function is true.
2671 *
2672 * Returns 0 or 1 depending on the results of the test.
2673 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002674static int
Owen Taylor3473f882001-02-23 17:55:21 +00002675xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
2676 char buf[100] = "";
2677
2678 if ((arg == NULL) ||
2679 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2680 return(0);
2681
Bjorn Reesee1dc0112001-03-03 12:09:03 +00002682 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00002683 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
2684}
2685
2686
2687/**
2688 * xmlXPathEqualNodeSets
2689 * @arg1: first nodeset object argument
2690 * @arg2: second nodeset object argument
2691 *
2692 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
2693 * If both objects to be compared are node-sets, then the comparison
2694 * will be true if and only if there is a node in the first node-set and
2695 * a node in the second node-set such that the result of performing the
2696 * comparison on the string-values of the two nodes is true.
2697 *
2698 * (needless to say, this is a costly operation)
2699 *
2700 * Returns 0 or 1 depending on the results of the test.
2701 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002702static int
Owen Taylor3473f882001-02-23 17:55:21 +00002703xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2704 int i, j;
2705 xmlChar **values1;
2706 xmlChar **values2;
2707 int ret = 0;
2708 xmlNodeSetPtr ns1;
2709 xmlNodeSetPtr ns2;
2710
2711 if ((arg1 == NULL) ||
2712 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2713 return(0);
2714 if ((arg2 == NULL) ||
2715 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2716 return(0);
2717
2718 ns1 = arg1->nodesetval;
2719 ns2 = arg2->nodesetval;
2720
Daniel Veillard911f49a2001-04-07 15:39:35 +00002721 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002722 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00002723 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002724 return(0);
2725
2726 /*
2727 * check if there is a node pertaining to both sets
2728 */
2729 for (i = 0;i < ns1->nodeNr;i++)
2730 for (j = 0;j < ns2->nodeNr;j++)
2731 if (ns1->nodeTab[i] == ns2->nodeTab[j])
2732 return(1);
2733
2734 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
2735 if (values1 == NULL)
2736 return(0);
2737 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
2738 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
2739 if (values2 == NULL) {
2740 xmlFree(values1);
2741 return(0);
2742 }
2743 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
2744 for (i = 0;i < ns1->nodeNr;i++) {
2745 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
2746 for (j = 0;j < ns2->nodeNr;j++) {
2747 if (i == 0)
2748 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
2749 ret = xmlStrEqual(values1[i], values2[j]);
2750 if (ret)
2751 break;
2752 }
2753 if (ret)
2754 break;
2755 }
2756 for (i = 0;i < ns1->nodeNr;i++)
2757 if (values1[i] != NULL)
2758 xmlFree(values1[i]);
2759 for (j = 0;j < ns2->nodeNr;j++)
2760 if (values2[j] != NULL)
2761 xmlFree(values2[j]);
2762 xmlFree(values1);
2763 xmlFree(values2);
2764 return(ret);
2765}
2766
2767/**
2768 * xmlXPathEqualValues:
2769 * @ctxt: the XPath Parser context
2770 *
2771 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2772 *
2773 * Returns 0 or 1 depending on the results of the test.
2774 */
2775int
2776xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
2777 xmlXPathObjectPtr arg1, arg2;
2778 int ret = 0;
2779
2780 arg1 = valuePop(ctxt);
2781 if (arg1 == NULL)
2782 XP_ERROR0(XPATH_INVALID_OPERAND);
2783
2784 arg2 = valuePop(ctxt);
2785 if (arg2 == NULL) {
2786 xmlXPathFreeObject(arg1);
2787 XP_ERROR0(XPATH_INVALID_OPERAND);
2788 }
2789
2790 if (arg1 == arg2) {
2791#ifdef DEBUG_EXPR
2792 xmlGenericError(xmlGenericErrorContext,
2793 "Equal: by pointer\n");
2794#endif
2795 return(1);
2796 }
2797
2798 switch (arg1->type) {
2799 case XPATH_UNDEFINED:
2800#ifdef DEBUG_EXPR
2801 xmlGenericError(xmlGenericErrorContext,
2802 "Equal: undefined\n");
2803#endif
2804 break;
2805 case XPATH_XSLT_TREE:
2806 case XPATH_NODESET:
2807 switch (arg2->type) {
2808 case XPATH_UNDEFINED:
2809#ifdef DEBUG_EXPR
2810 xmlGenericError(xmlGenericErrorContext,
2811 "Equal: undefined\n");
2812#endif
2813 break;
2814 case XPATH_XSLT_TREE:
2815 case XPATH_NODESET:
2816 ret = xmlXPathEqualNodeSets(arg1, arg2);
2817 break;
2818 case XPATH_BOOLEAN:
2819 if ((arg1->nodesetval == NULL) ||
2820 (arg1->nodesetval->nodeNr == 0)) ret = 0;
2821 else
2822 ret = 1;
2823 ret = (ret == arg2->boolval);
2824 break;
2825 case XPATH_NUMBER:
2826 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
2827 break;
2828 case XPATH_STRING:
2829 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
2830 break;
2831 case XPATH_USERS:
2832 case XPATH_POINT:
2833 case XPATH_RANGE:
2834 case XPATH_LOCATIONSET:
2835 TODO
2836 break;
2837 }
2838 break;
2839 case XPATH_BOOLEAN:
2840 switch (arg2->type) {
2841 case XPATH_UNDEFINED:
2842#ifdef DEBUG_EXPR
2843 xmlGenericError(xmlGenericErrorContext,
2844 "Equal: undefined\n");
2845#endif
2846 break;
2847 case XPATH_NODESET:
2848 case XPATH_XSLT_TREE:
2849 if ((arg2->nodesetval == NULL) ||
2850 (arg2->nodesetval->nodeNr == 0)) ret = 0;
2851 else
2852 ret = 1;
2853 break;
2854 case XPATH_BOOLEAN:
2855#ifdef DEBUG_EXPR
2856 xmlGenericError(xmlGenericErrorContext,
2857 "Equal: %d boolean %d \n",
2858 arg1->boolval, arg2->boolval);
2859#endif
2860 ret = (arg1->boolval == arg2->boolval);
2861 break;
2862 case XPATH_NUMBER:
2863 if (arg2->floatval) ret = 1;
2864 else ret = 0;
2865 ret = (arg1->boolval == ret);
2866 break;
2867 case XPATH_STRING:
2868 if ((arg2->stringval == NULL) ||
2869 (arg2->stringval[0] == 0)) ret = 0;
2870 else
2871 ret = 1;
2872 ret = (arg1->boolval == ret);
2873 break;
2874 case XPATH_USERS:
2875 case XPATH_POINT:
2876 case XPATH_RANGE:
2877 case XPATH_LOCATIONSET:
2878 TODO
2879 break;
2880 }
2881 break;
2882 case XPATH_NUMBER:
2883 switch (arg2->type) {
2884 case XPATH_UNDEFINED:
2885#ifdef DEBUG_EXPR
2886 xmlGenericError(xmlGenericErrorContext,
2887 "Equal: undefined\n");
2888#endif
2889 break;
2890 case XPATH_NODESET:
2891 case XPATH_XSLT_TREE:
2892 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
2893 break;
2894 case XPATH_BOOLEAN:
2895 if (arg1->floatval) ret = 1;
2896 else ret = 0;
2897 ret = (arg2->boolval == ret);
2898 break;
2899 case XPATH_STRING:
2900 valuePush(ctxt, arg2);
2901 xmlXPathNumberFunction(ctxt, 1);
2902 arg2 = valuePop(ctxt);
2903 /* no break on purpose */
2904 case XPATH_NUMBER:
2905 ret = (arg1->floatval == arg2->floatval);
2906 break;
2907 case XPATH_USERS:
2908 case XPATH_POINT:
2909 case XPATH_RANGE:
2910 case XPATH_LOCATIONSET:
2911 TODO
2912 break;
2913 }
2914 break;
2915 case XPATH_STRING:
2916 switch (arg2->type) {
2917 case XPATH_UNDEFINED:
2918#ifdef DEBUG_EXPR
2919 xmlGenericError(xmlGenericErrorContext,
2920 "Equal: undefined\n");
2921#endif
2922 break;
2923 case XPATH_NODESET:
2924 case XPATH_XSLT_TREE:
2925 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
2926 break;
2927 case XPATH_BOOLEAN:
2928 if ((arg1->stringval == NULL) ||
2929 (arg1->stringval[0] == 0)) ret = 0;
2930 else
2931 ret = 1;
2932 ret = (arg2->boolval == ret);
2933 break;
2934 case XPATH_STRING:
2935 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
2936 break;
2937 case XPATH_NUMBER:
2938 valuePush(ctxt, arg1);
2939 xmlXPathNumberFunction(ctxt, 1);
2940 arg1 = valuePop(ctxt);
2941 ret = (arg1->floatval == arg2->floatval);
2942 break;
2943 case XPATH_USERS:
2944 case XPATH_POINT:
2945 case XPATH_RANGE:
2946 case XPATH_LOCATIONSET:
2947 TODO
2948 break;
2949 }
2950 break;
2951 case XPATH_USERS:
2952 case XPATH_POINT:
2953 case XPATH_RANGE:
2954 case XPATH_LOCATIONSET:
2955 TODO
2956 break;
2957 }
2958 xmlXPathFreeObject(arg1);
2959 xmlXPathFreeObject(arg2);
2960 return(ret);
2961}
2962
2963
2964/**
2965 * xmlXPathCompareValues:
2966 * @ctxt: the XPath Parser context
2967 * @inf: less than (1) or greater than (0)
2968 * @strict: is the comparison strict
2969 *
2970 * Implement the compare operation on XPath objects:
2971 * @arg1 < @arg2 (1, 1, ...
2972 * @arg1 <= @arg2 (1, 0, ...
2973 * @arg1 > @arg2 (0, 1, ...
2974 * @arg1 >= @arg2 (0, 0, ...
2975 *
2976 * When neither object to be compared is a node-set and the operator is
2977 * <=, <, >=, >, then the objects are compared by converted both objects
2978 * to numbers and comparing the numbers according to IEEE 754. The <
2979 * comparison will be true if and only if the first number is less than the
2980 * second number. The <= comparison will be true if and only if the first
2981 * number is less than or equal to the second number. The > comparison
2982 * will be true if and only if the first number is greater than the second
2983 * number. The >= comparison will be true if and only if the first number
2984 * is greater than or equal to the second number.
2985 *
2986 * Returns 1 if the comparaison succeeded, 0 if it failed
2987 */
2988int
2989xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
2990 int ret = 0;
2991 xmlXPathObjectPtr arg1, arg2;
2992
2993 arg2 = valuePop(ctxt);
2994 if (arg2 == NULL) {
2995 XP_ERROR0(XPATH_INVALID_OPERAND);
2996 }
2997
2998 arg1 = valuePop(ctxt);
2999 if (arg1 == NULL) {
3000 xmlXPathFreeObject(arg2);
3001 XP_ERROR0(XPATH_INVALID_OPERAND);
3002 }
3003
3004 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
3005 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003006 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003007 } else {
3008 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003009 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
3010 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003011 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003012 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
3013 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00003014 }
3015 }
3016 return(ret);
3017 }
3018
3019 if (arg1->type != XPATH_NUMBER) {
3020 valuePush(ctxt, arg1);
3021 xmlXPathNumberFunction(ctxt, 1);
3022 arg1 = valuePop(ctxt);
3023 }
3024 if (arg1->type != XPATH_NUMBER) {
3025 xmlXPathFreeObject(arg1);
3026 xmlXPathFreeObject(arg2);
3027 XP_ERROR0(XPATH_INVALID_OPERAND);
3028 }
3029 if (arg2->type != XPATH_NUMBER) {
3030 valuePush(ctxt, arg2);
3031 xmlXPathNumberFunction(ctxt, 1);
3032 arg2 = valuePop(ctxt);
3033 }
3034 if (arg2->type != XPATH_NUMBER) {
3035 xmlXPathFreeObject(arg1);
3036 xmlXPathFreeObject(arg2);
3037 XP_ERROR0(XPATH_INVALID_OPERAND);
3038 }
3039 /*
3040 * Add tests for infinity and nan
3041 * => feedback on 3.4 for Inf and NaN
3042 */
3043 if (inf && strict)
3044 ret = (arg1->floatval < arg2->floatval);
3045 else if (inf && !strict)
3046 ret = (arg1->floatval <= arg2->floatval);
3047 else if (!inf && strict)
3048 ret = (arg1->floatval > arg2->floatval);
3049 else if (!inf && !strict)
3050 ret = (arg1->floatval >= arg2->floatval);
3051 xmlXPathFreeObject(arg1);
3052 xmlXPathFreeObject(arg2);
3053 return(ret);
3054}
3055
3056/**
3057 * xmlXPathValueFlipSign:
3058 * @ctxt: the XPath Parser context
3059 *
3060 * Implement the unary - operation on an XPath object
3061 * The numeric operators convert their operands to numbers as if
3062 * by calling the number function.
3063 */
3064void
3065xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
3066 xmlXPathObjectPtr arg;
3067
3068 POP_FLOAT
3069 arg->floatval = -arg->floatval;
3070 valuePush(ctxt, arg);
3071}
3072
3073/**
3074 * xmlXPathAddValues:
3075 * @ctxt: the XPath Parser context
3076 *
3077 * Implement the add operation on XPath objects:
3078 * The numeric operators convert their operands to numbers as if
3079 * by calling the number function.
3080 */
3081void
3082xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
3083 xmlXPathObjectPtr arg;
3084 double val;
3085
3086 POP_FLOAT
3087 val = arg->floatval;
3088 xmlXPathFreeObject(arg);
3089
3090 POP_FLOAT
3091 arg->floatval += val;
3092 valuePush(ctxt, arg);
3093}
3094
3095/**
3096 * xmlXPathSubValues:
3097 * @ctxt: the XPath Parser context
3098 *
3099 * Implement the substraction operation on XPath objects:
3100 * The numeric operators convert their operands to numbers as if
3101 * by calling the number function.
3102 */
3103void
3104xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
3105 xmlXPathObjectPtr arg;
3106 double val;
3107
3108 POP_FLOAT
3109 val = arg->floatval;
3110 xmlXPathFreeObject(arg);
3111
3112 POP_FLOAT
3113 arg->floatval -= val;
3114 valuePush(ctxt, arg);
3115}
3116
3117/**
3118 * xmlXPathMultValues:
3119 * @ctxt: the XPath Parser context
3120 *
3121 * Implement the multiply operation on XPath objects:
3122 * The numeric operators convert their operands to numbers as if
3123 * by calling the number function.
3124 */
3125void
3126xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
3127 xmlXPathObjectPtr arg;
3128 double val;
3129
3130 POP_FLOAT
3131 val = arg->floatval;
3132 xmlXPathFreeObject(arg);
3133
3134 POP_FLOAT
3135 arg->floatval *= val;
3136 valuePush(ctxt, arg);
3137}
3138
3139/**
3140 * xmlXPathDivValues:
3141 * @ctxt: the XPath Parser context
3142 *
3143 * Implement the div operation on XPath objects @arg1 / @arg2:
3144 * The numeric operators convert their operands to numbers as if
3145 * by calling the number function.
3146 */
3147void
3148xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
3149 xmlXPathObjectPtr arg;
3150 double val;
3151
3152 POP_FLOAT
3153 val = arg->floatval;
3154 xmlXPathFreeObject(arg);
3155
3156 POP_FLOAT
3157 arg->floatval /= val;
3158 valuePush(ctxt, arg);
3159}
3160
3161/**
3162 * xmlXPathModValues:
3163 * @ctxt: the XPath Parser context
3164 *
3165 * Implement the mod operation on XPath objects: @arg1 / @arg2
3166 * The numeric operators convert their operands to numbers as if
3167 * by calling the number function.
3168 */
3169void
3170xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
3171 xmlXPathObjectPtr arg;
3172 int arg1, arg2;
3173
3174 POP_FLOAT
3175 arg2 = (int) arg->floatval;
3176 xmlXPathFreeObject(arg);
3177
3178 POP_FLOAT
3179 arg1 = (int) arg->floatval;
3180 arg->floatval = arg1 % arg2;
3181 valuePush(ctxt, arg);
3182}
3183
3184/************************************************************************
3185 * *
3186 * The traversal functions *
3187 * *
3188 ************************************************************************/
3189
Owen Taylor3473f882001-02-23 17:55:21 +00003190/*
3191 * A traversal function enumerates nodes along an axis.
3192 * Initially it must be called with NULL, and it indicates
3193 * termination on the axis by returning NULL.
3194 */
3195typedef xmlNodePtr (*xmlXPathTraversalFunction)
3196 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
3197
3198/**
3199 * xmlXPathNextSelf:
3200 * @ctxt: the XPath Parser context
3201 * @cur: the current node in the traversal
3202 *
3203 * Traversal function for the "self" direction
3204 * The self axis contains just the context node itself
3205 *
3206 * Returns the next element following that axis
3207 */
3208xmlNodePtr
3209xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3210 if (cur == NULL)
3211 return(ctxt->context->node);
3212 return(NULL);
3213}
3214
3215/**
3216 * xmlXPathNextChild:
3217 * @ctxt: the XPath Parser context
3218 * @cur: the current node in the traversal
3219 *
3220 * Traversal function for the "child" direction
3221 * The child axis contains the children of the context node in document order.
3222 *
3223 * Returns the next element following that axis
3224 */
3225xmlNodePtr
3226xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3227 if (cur == NULL) {
3228 if (ctxt->context->node == NULL) return(NULL);
3229 switch (ctxt->context->node->type) {
3230 case XML_ELEMENT_NODE:
3231 case XML_TEXT_NODE:
3232 case XML_CDATA_SECTION_NODE:
3233 case XML_ENTITY_REF_NODE:
3234 case XML_ENTITY_NODE:
3235 case XML_PI_NODE:
3236 case XML_COMMENT_NODE:
3237 case XML_NOTATION_NODE:
3238 case XML_DTD_NODE:
3239 return(ctxt->context->node->children);
3240 case XML_DOCUMENT_NODE:
3241 case XML_DOCUMENT_TYPE_NODE:
3242 case XML_DOCUMENT_FRAG_NODE:
3243 case XML_HTML_DOCUMENT_NODE:
3244#ifdef LIBXML_SGML_ENABLED
3245 case XML_SGML_DOCUMENT_NODE:
3246#endif
3247 return(((xmlDocPtr) ctxt->context->node)->children);
3248 case XML_ELEMENT_DECL:
3249 case XML_ATTRIBUTE_DECL:
3250 case XML_ENTITY_DECL:
3251 case XML_ATTRIBUTE_NODE:
3252 case XML_NAMESPACE_DECL:
3253 case XML_XINCLUDE_START:
3254 case XML_XINCLUDE_END:
3255 return(NULL);
3256 }
3257 return(NULL);
3258 }
3259 if ((cur->type == XML_DOCUMENT_NODE) ||
3260 (cur->type == XML_HTML_DOCUMENT_NODE))
3261 return(NULL);
3262 return(cur->next);
3263}
3264
3265/**
3266 * xmlXPathNextDescendant:
3267 * @ctxt: the XPath Parser context
3268 * @cur: the current node in the traversal
3269 *
3270 * Traversal function for the "descendant" direction
3271 * the descendant axis contains the descendants of the context node in document
3272 * order; a descendant is a child or a child of a child and so on.
3273 *
3274 * Returns the next element following that axis
3275 */
3276xmlNodePtr
3277xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3278 if (cur == NULL) {
3279 if (ctxt->context->node == NULL)
3280 return(NULL);
3281 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3282 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3283 return(NULL);
3284
3285 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3286 return(ctxt->context->doc->children);
3287 return(ctxt->context->node->children);
3288 }
3289
3290 if (cur->children != NULL)
3291 {
3292 if (cur->children->type != XML_ENTITY_DECL)
3293 return(cur->children);
3294 }
3295 if (cur->next != NULL) return(cur->next);
3296
3297 do {
3298 cur = cur->parent;
3299 if (cur == NULL) return(NULL);
3300 if (cur == ctxt->context->node) return(NULL);
3301 if (cur->next != NULL) {
3302 cur = cur->next;
3303 return(cur);
3304 }
3305 } while (cur != NULL);
3306 return(cur);
3307}
3308
3309/**
3310 * xmlXPathNextDescendantOrSelf:
3311 * @ctxt: the XPath Parser context
3312 * @cur: the current node in the traversal
3313 *
3314 * Traversal function for the "descendant-or-self" direction
3315 * the descendant-or-self axis contains the context node and the descendants
3316 * of the context node in document order; thus the context node is the first
3317 * node on the axis, and the first child of the context node is the second node
3318 * on the axis
3319 *
3320 * Returns the next element following that axis
3321 */
3322xmlNodePtr
3323xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3324 if (cur == NULL) {
3325 if (ctxt->context->node == NULL)
3326 return(NULL);
3327 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3328 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3329 return(NULL);
3330 return(ctxt->context->node);
3331 }
3332
3333 return(xmlXPathNextDescendant(ctxt, cur));
3334}
3335
3336/**
3337 * xmlXPathNextParent:
3338 * @ctxt: the XPath Parser context
3339 * @cur: the current node in the traversal
3340 *
3341 * Traversal function for the "parent" direction
3342 * The parent axis contains the parent of the context node, if there is one.
3343 *
3344 * Returns the next element following that axis
3345 */
3346xmlNodePtr
3347xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3348 /*
3349 * the parent of an attribute or namespace node is the element
3350 * to which the attribute or namespace node is attached
3351 * Namespace handling !!!
3352 */
3353 if (cur == NULL) {
3354 if (ctxt->context->node == NULL) return(NULL);
3355 switch (ctxt->context->node->type) {
3356 case XML_ELEMENT_NODE:
3357 case XML_TEXT_NODE:
3358 case XML_CDATA_SECTION_NODE:
3359 case XML_ENTITY_REF_NODE:
3360 case XML_ENTITY_NODE:
3361 case XML_PI_NODE:
3362 case XML_COMMENT_NODE:
3363 case XML_NOTATION_NODE:
3364 case XML_DTD_NODE:
3365 case XML_ELEMENT_DECL:
3366 case XML_ATTRIBUTE_DECL:
3367 case XML_XINCLUDE_START:
3368 case XML_XINCLUDE_END:
3369 case XML_ENTITY_DECL:
3370 if (ctxt->context->node->parent == NULL)
3371 return((xmlNodePtr) ctxt->context->doc);
3372 return(ctxt->context->node->parent);
3373 case XML_ATTRIBUTE_NODE: {
3374 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3375
3376 return(att->parent);
3377 }
3378 case XML_DOCUMENT_NODE:
3379 case XML_DOCUMENT_TYPE_NODE:
3380 case XML_DOCUMENT_FRAG_NODE:
3381 case XML_HTML_DOCUMENT_NODE:
3382#ifdef LIBXML_SGML_ENABLED
3383 case XML_SGML_DOCUMENT_NODE:
3384#endif
3385 return(NULL);
3386 case XML_NAMESPACE_DECL:
3387 /*
3388 * TODO !!! may require extending struct _xmlNs with
3389 * parent field
3390 * C.f. Infoset case...
3391 */
3392 return(NULL);
3393 }
3394 }
3395 return(NULL);
3396}
3397
3398/**
3399 * xmlXPathNextAncestor:
3400 * @ctxt: the XPath Parser context
3401 * @cur: the current node in the traversal
3402 *
3403 * Traversal function for the "ancestor" direction
3404 * the ancestor axis contains the ancestors of the context node; the ancestors
3405 * of the context node consist of the parent of context node and the parent's
3406 * parent and so on; the nodes are ordered in reverse document order; thus the
3407 * parent is the first node on the axis, and the parent's parent is the second
3408 * node on the axis
3409 *
3410 * Returns the next element following that axis
3411 */
3412xmlNodePtr
3413xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3414 /*
3415 * the parent of an attribute or namespace node is the element
3416 * to which the attribute or namespace node is attached
3417 * !!!!!!!!!!!!!
3418 */
3419 if (cur == NULL) {
3420 if (ctxt->context->node == NULL) return(NULL);
3421 switch (ctxt->context->node->type) {
3422 case XML_ELEMENT_NODE:
3423 case XML_TEXT_NODE:
3424 case XML_CDATA_SECTION_NODE:
3425 case XML_ENTITY_REF_NODE:
3426 case XML_ENTITY_NODE:
3427 case XML_PI_NODE:
3428 case XML_COMMENT_NODE:
3429 case XML_DTD_NODE:
3430 case XML_ELEMENT_DECL:
3431 case XML_ATTRIBUTE_DECL:
3432 case XML_ENTITY_DECL:
3433 case XML_NOTATION_NODE:
3434 case XML_XINCLUDE_START:
3435 case XML_XINCLUDE_END:
3436 if (ctxt->context->node->parent == NULL)
3437 return((xmlNodePtr) ctxt->context->doc);
3438 return(ctxt->context->node->parent);
3439 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003440 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003441
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003442 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00003443 }
3444 case XML_DOCUMENT_NODE:
3445 case XML_DOCUMENT_TYPE_NODE:
3446 case XML_DOCUMENT_FRAG_NODE:
3447 case XML_HTML_DOCUMENT_NODE:
3448#ifdef LIBXML_SGML_ENABLED
3449 case XML_SGML_DOCUMENT_NODE:
3450#endif
3451 return(NULL);
3452 case XML_NAMESPACE_DECL:
3453 /*
3454 * TODO !!! may require extending struct _xmlNs with
3455 * parent field
3456 * C.f. Infoset case...
3457 */
3458 return(NULL);
3459 }
3460 return(NULL);
3461 }
3462 if (cur == ctxt->context->doc->children)
3463 return((xmlNodePtr) ctxt->context->doc);
3464 if (cur == (xmlNodePtr) ctxt->context->doc)
3465 return(NULL);
3466 switch (cur->type) {
3467 case XML_ELEMENT_NODE:
3468 case XML_TEXT_NODE:
3469 case XML_CDATA_SECTION_NODE:
3470 case XML_ENTITY_REF_NODE:
3471 case XML_ENTITY_NODE:
3472 case XML_PI_NODE:
3473 case XML_COMMENT_NODE:
3474 case XML_NOTATION_NODE:
3475 case XML_DTD_NODE:
3476 case XML_ELEMENT_DECL:
3477 case XML_ATTRIBUTE_DECL:
3478 case XML_ENTITY_DECL:
3479 case XML_XINCLUDE_START:
3480 case XML_XINCLUDE_END:
3481 return(cur->parent);
3482 case XML_ATTRIBUTE_NODE: {
3483 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3484
3485 return(att->parent);
3486 }
3487 case XML_DOCUMENT_NODE:
3488 case XML_DOCUMENT_TYPE_NODE:
3489 case XML_DOCUMENT_FRAG_NODE:
3490 case XML_HTML_DOCUMENT_NODE:
3491#ifdef LIBXML_SGML_ENABLED
3492 case XML_SGML_DOCUMENT_NODE:
3493#endif
3494 return(NULL);
3495 case XML_NAMESPACE_DECL:
3496 /*
3497 * TODO !!! may require extending struct _xmlNs with
3498 * parent field
3499 * C.f. Infoset case...
3500 */
3501 return(NULL);
3502 }
3503 return(NULL);
3504}
3505
3506/**
3507 * xmlXPathNextAncestorOrSelf:
3508 * @ctxt: the XPath Parser context
3509 * @cur: the current node in the traversal
3510 *
3511 * Traversal function for the "ancestor-or-self" direction
3512 * he ancestor-or-self axis contains the context node and ancestors of
3513 * the context node in reverse document order; thus the context node is
3514 * the first node on the axis, and the context node's parent the second;
3515 * parent here is defined the same as with the parent axis.
3516 *
3517 * Returns the next element following that axis
3518 */
3519xmlNodePtr
3520xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3521 if (cur == NULL)
3522 return(ctxt->context->node);
3523 return(xmlXPathNextAncestor(ctxt, cur));
3524}
3525
3526/**
3527 * xmlXPathNextFollowingSibling:
3528 * @ctxt: the XPath Parser context
3529 * @cur: the current node in the traversal
3530 *
3531 * Traversal function for the "following-sibling" direction
3532 * The following-sibling axis contains the following siblings of the context
3533 * node in document order.
3534 *
3535 * Returns the next element following that axis
3536 */
3537xmlNodePtr
3538xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3539 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3540 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3541 return(NULL);
3542 if (cur == (xmlNodePtr) ctxt->context->doc)
3543 return(NULL);
3544 if (cur == NULL)
3545 return(ctxt->context->node->next);
3546 return(cur->next);
3547}
3548
3549/**
3550 * xmlXPathNextPrecedingSibling:
3551 * @ctxt: the XPath Parser context
3552 * @cur: the current node in the traversal
3553 *
3554 * Traversal function for the "preceding-sibling" direction
3555 * The preceding-sibling axis contains the preceding siblings of the context
3556 * node in reverse document order; the first preceding sibling is first on the
3557 * axis; the sibling preceding that node is the second on the axis and so on.
3558 *
3559 * Returns the next element following that axis
3560 */
3561xmlNodePtr
3562xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3563 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3564 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3565 return(NULL);
3566 if (cur == (xmlNodePtr) ctxt->context->doc)
3567 return(NULL);
3568 if (cur == NULL)
3569 return(ctxt->context->node->prev);
3570 return(cur->prev);
3571}
3572
3573/**
3574 * xmlXPathNextFollowing:
3575 * @ctxt: the XPath Parser context
3576 * @cur: the current node in the traversal
3577 *
3578 * Traversal function for the "following" direction
3579 * The following axis contains all nodes in the same document as the context
3580 * node that are after the context node in document order, excluding any
3581 * descendants and excluding attribute nodes and namespace nodes; the nodes
3582 * are ordered in document order
3583 *
3584 * Returns the next element following that axis
3585 */
3586xmlNodePtr
3587xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3588 if (cur != NULL && cur->children != NULL)
3589 return cur->children ;
3590 if (cur == NULL) cur = ctxt->context->node;
3591 if (cur == NULL) return(NULL) ; /* ERROR */
3592 if (cur->next != NULL) return(cur->next) ;
3593 do {
3594 cur = cur->parent;
3595 if (cur == NULL) return(NULL);
3596 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3597 if (cur->next != NULL) return(cur->next);
3598 } while (cur != NULL);
3599 return(cur);
3600}
3601
3602/*
3603 * xmlXPathIsAncestor:
3604 * @ancestor: the ancestor node
3605 * @node: the current node
3606 *
3607 * Check that @ancestor is a @node's ancestor
3608 *
3609 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3610 */
3611static int
3612xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3613 if ((ancestor == NULL) || (node == NULL)) return(0);
3614 /* nodes need to be in the same document */
3615 if (ancestor->doc != node->doc) return(0);
3616 /* avoid searching if ancestor or node is the root node */
3617 if (ancestor == (xmlNodePtr) node->doc) return(1);
3618 if (node == (xmlNodePtr) ancestor->doc) return(0);
3619 while (node->parent != NULL) {
3620 if (node->parent == ancestor)
3621 return(1);
3622 node = node->parent;
3623 }
3624 return(0);
3625}
3626
3627/**
3628 * xmlXPathNextPreceding:
3629 * @ctxt: the XPath Parser context
3630 * @cur: the current node in the traversal
3631 *
3632 * Traversal function for the "preceding" direction
3633 * the preceding axis contains all nodes in the same document as the context
3634 * node that are before the context node in document order, excluding any
3635 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3636 * ordered in reverse document order
3637 *
3638 * Returns the next element following that axis
3639 */
3640xmlNodePtr
3641xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3642 if (cur == NULL)
3643 cur = ctxt->context->node ;
3644 do {
3645 if (cur->prev != NULL) {
3646 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3647 ;
3648 return(cur) ;
3649 }
3650
3651 cur = cur->parent;
3652 if (cur == NULL) return(NULL);
3653 if (cur == ctxt->context->doc->children) return(NULL);
3654 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3655 return(cur);
3656}
3657
3658/**
3659 * xmlXPathNextNamespace:
3660 * @ctxt: the XPath Parser context
3661 * @cur: the current attribute in the traversal
3662 *
3663 * Traversal function for the "namespace" direction
3664 * the namespace axis contains the namespace nodes of the context node;
3665 * the order of nodes on this axis is implementation-defined; the axis will
3666 * be empty unless the context node is an element
3667 *
3668 * Returns the next element following that axis
3669 */
3670xmlNodePtr
3671xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3672 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3673 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3674 if (ctxt->context->namespaces != NULL)
3675 xmlFree(ctxt->context->namespaces);
3676 ctxt->context->namespaces =
3677 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3678 if (ctxt->context->namespaces == NULL) return(NULL);
3679 ctxt->context->nsNr = 0;
3680 }
3681 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3682}
3683
3684/**
3685 * xmlXPathNextAttribute:
3686 * @ctxt: the XPath Parser context
3687 * @cur: the current attribute in the traversal
3688 *
3689 * Traversal function for the "attribute" direction
3690 * TODO: support DTD inherited default attributes
3691 *
3692 * Returns the next element following that axis
3693 */
3694xmlNodePtr
3695xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00003696 if (ctxt->context->node == NULL)
3697 return(NULL);
3698 if (ctxt->context->node->type != XML_ELEMENT_NODE)
3699 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003700 if (cur == NULL) {
3701 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3702 return(NULL);
3703 return((xmlNodePtr)ctxt->context->node->properties);
3704 }
3705 return((xmlNodePtr)cur->next);
3706}
3707
3708/************************************************************************
3709 * *
3710 * NodeTest Functions *
3711 * *
3712 ************************************************************************/
3713
Owen Taylor3473f882001-02-23 17:55:21 +00003714#define IS_FUNCTION 200
3715
Owen Taylor3473f882001-02-23 17:55:21 +00003716
3717/************************************************************************
3718 * *
3719 * Implicit tree core function library *
3720 * *
3721 ************************************************************************/
3722
3723/**
3724 * xmlXPathRoot:
3725 * @ctxt: the XPath Parser context
3726 *
3727 * Initialize the context to the root of the document
3728 */
3729void
3730xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
3731 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
3732 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3733}
3734
3735/************************************************************************
3736 * *
3737 * The explicit core function library *
3738 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
3739 * *
3740 ************************************************************************/
3741
3742
3743/**
3744 * xmlXPathLastFunction:
3745 * @ctxt: the XPath Parser context
3746 * @nargs: the number of arguments
3747 *
3748 * Implement the last() XPath function
3749 * number last()
3750 * The last function returns the number of nodes in the context node list.
3751 */
3752void
3753xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3754 CHECK_ARITY(0);
3755 if (ctxt->context->contextSize >= 0) {
3756 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
3757#ifdef DEBUG_EXPR
3758 xmlGenericError(xmlGenericErrorContext,
3759 "last() : %d\n", ctxt->context->contextSize);
3760#endif
3761 } else {
3762 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
3763 }
3764}
3765
3766/**
3767 * xmlXPathPositionFunction:
3768 * @ctxt: the XPath Parser context
3769 * @nargs: the number of arguments
3770 *
3771 * Implement the position() XPath function
3772 * number position()
3773 * The position function returns the position of the context node in the
3774 * context node list. The first position is 1, and so the last positionr
3775 * will be equal to last().
3776 */
3777void
3778xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3779 CHECK_ARITY(0);
3780 if (ctxt->context->proximityPosition >= 0) {
3781 valuePush(ctxt,
3782 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
3783#ifdef DEBUG_EXPR
3784 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
3785 ctxt->context->proximityPosition);
3786#endif
3787 } else {
3788 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
3789 }
3790}
3791
3792/**
3793 * xmlXPathCountFunction:
3794 * @ctxt: the XPath Parser context
3795 * @nargs: the number of arguments
3796 *
3797 * Implement the count() XPath function
3798 * number count(node-set)
3799 */
3800void
3801xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3802 xmlXPathObjectPtr cur;
3803
3804 CHECK_ARITY(1);
3805 if ((ctxt->value == NULL) ||
3806 ((ctxt->value->type != XPATH_NODESET) &&
3807 (ctxt->value->type != XPATH_XSLT_TREE)))
3808 XP_ERROR(XPATH_INVALID_TYPE);
3809 cur = valuePop(ctxt);
3810
Daniel Veillard911f49a2001-04-07 15:39:35 +00003811 if ((cur == NULL) || (cur->nodesetval == NULL))
3812 valuePush(ctxt, xmlXPathNewFloat((double) 0));
3813 else
3814 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Owen Taylor3473f882001-02-23 17:55:21 +00003815 xmlXPathFreeObject(cur);
3816}
3817
3818/**
3819 * xmlXPathIdFunction:
3820 * @ctxt: the XPath Parser context
3821 * @nargs: the number of arguments
3822 *
3823 * Implement the id() XPath function
3824 * node-set id(object)
3825 * The id function selects elements by their unique ID
3826 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
3827 * then the result is the union of the result of applying id to the
3828 * string value of each of the nodes in the argument node-set. When the
3829 * argument to id is of any other type, the argument is converted to a
3830 * string as if by a call to the string function; the string is split
3831 * into a whitespace-separated list of tokens (whitespace is any sequence
3832 * of characters matching the production S); the result is a node-set
3833 * containing the elements in the same document as the context node that
3834 * have a unique ID equal to any of the tokens in the list.
3835 */
3836void
3837xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3838 const xmlChar *tokens;
3839 const xmlChar *cur;
3840 xmlChar *ID;
3841 xmlAttrPtr attr;
3842 xmlNodePtr elem = NULL;
3843 xmlXPathObjectPtr ret, obj;
3844
3845 CHECK_ARITY(1);
3846 obj = valuePop(ctxt);
3847 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3848 if (obj->type == XPATH_NODESET) {
3849 xmlXPathObjectPtr newobj;
3850 int i;
3851
3852 ret = xmlXPathNewNodeSet(NULL);
3853
Daniel Veillard911f49a2001-04-07 15:39:35 +00003854 if (obj->nodesetval != NULL) {
3855 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3856 valuePush(ctxt,
3857 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
3858 xmlXPathStringFunction(ctxt, 1);
3859 xmlXPathIdFunction(ctxt, 1);
3860 newobj = valuePop(ctxt);
3861 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
3862 newobj->nodesetval);
3863 xmlXPathFreeObject(newobj);
3864 }
Owen Taylor3473f882001-02-23 17:55:21 +00003865 }
3866
3867 xmlXPathFreeObject(obj);
3868 valuePush(ctxt, ret);
3869 return;
3870 }
3871 if (obj->type != XPATH_STRING) {
3872 valuePush(ctxt, obj);
3873 xmlXPathStringFunction(ctxt, 1);
3874 obj = valuePop(ctxt);
3875 if (obj->type != XPATH_STRING) {
3876 xmlXPathFreeObject(obj);
3877 return;
3878 }
3879 }
3880 tokens = obj->stringval;
3881
3882 ret = xmlXPathNewNodeSet(NULL);
3883 valuePush(ctxt, ret);
3884 if (tokens == NULL) {
3885 xmlXPathFreeObject(obj);
3886 return;
3887 }
3888
3889 cur = tokens;
3890
3891 while (IS_BLANK(*cur)) cur++;
3892 while (*cur != 0) {
3893 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
3894 (*cur == '.') || (*cur == '-') ||
3895 (*cur == '_') || (*cur == ':') ||
3896 (IS_COMBINING(*cur)) ||
3897 (IS_EXTENDER(*cur)))
3898 cur++;
3899
3900 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
3901
3902 ID = xmlStrndup(tokens, cur - tokens);
3903 attr = xmlGetID(ctxt->context->doc, ID);
3904 if (attr != NULL) {
3905 elem = attr->parent;
3906 xmlXPathNodeSetAdd(ret->nodesetval, elem);
3907 }
3908 if (ID != NULL)
3909 xmlFree(ID);
3910
3911 while (IS_BLANK(*cur)) cur++;
3912 tokens = cur;
3913 }
3914 xmlXPathFreeObject(obj);
3915 return;
3916}
3917
3918/**
3919 * xmlXPathLocalNameFunction:
3920 * @ctxt: the XPath Parser context
3921 * @nargs: the number of arguments
3922 *
3923 * Implement the local-name() XPath function
3924 * string local-name(node-set?)
3925 * The local-name function returns a string containing the local part
3926 * of the name of the node in the argument node-set that is first in
3927 * document order. If the node-set is empty or the first node has no
3928 * name, an empty string is returned. If the argument is omitted it
3929 * defaults to the context node.
3930 */
3931void
3932xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3933 xmlXPathObjectPtr cur;
3934
3935 if (nargs == 0) {
3936 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3937 nargs = 1;
3938 }
3939
3940 CHECK_ARITY(1);
3941 if ((ctxt->value == NULL) ||
3942 ((ctxt->value->type != XPATH_NODESET) &&
3943 (ctxt->value->type != XPATH_XSLT_TREE)))
3944 XP_ERROR(XPATH_INVALID_TYPE);
3945 cur = valuePop(ctxt);
3946
Daniel Veillard911f49a2001-04-07 15:39:35 +00003947 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003948 valuePush(ctxt, xmlXPathNewCString(""));
3949 } else {
3950 int i = 0; /* Should be first in document order !!!!! */
3951 switch (cur->nodesetval->nodeTab[i]->type) {
3952 case XML_ELEMENT_NODE:
3953 case XML_ATTRIBUTE_NODE:
3954 case XML_PI_NODE:
3955 valuePush(ctxt,
3956 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
3957 break;
3958 case XML_NAMESPACE_DECL:
3959 valuePush(ctxt, xmlXPathNewString(
3960 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
3961 break;
3962 default:
3963 valuePush(ctxt, xmlXPathNewCString(""));
3964 }
3965 }
3966 xmlXPathFreeObject(cur);
3967}
3968
3969/**
3970 * xmlXPathNamespaceURIFunction:
3971 * @ctxt: the XPath Parser context
3972 * @nargs: the number of arguments
3973 *
3974 * Implement the namespace-uri() XPath function
3975 * string namespace-uri(node-set?)
3976 * The namespace-uri function returns a string containing the
3977 * namespace URI of the expanded name of the node in the argument
3978 * node-set that is first in document order. If the node-set is empty,
3979 * the first node has no name, or the expanded name has no namespace
3980 * URI, an empty string is returned. If the argument is omitted it
3981 * defaults to the context node.
3982 */
3983void
3984xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3985 xmlXPathObjectPtr cur;
3986
3987 if (nargs == 0) {
3988 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3989 nargs = 1;
3990 }
3991 CHECK_ARITY(1);
3992 if ((ctxt->value == NULL) ||
3993 ((ctxt->value->type != XPATH_NODESET) &&
3994 (ctxt->value->type != XPATH_XSLT_TREE)))
3995 XP_ERROR(XPATH_INVALID_TYPE);
3996 cur = valuePop(ctxt);
3997
Daniel Veillard911f49a2001-04-07 15:39:35 +00003998 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003999 valuePush(ctxt, xmlXPathNewCString(""));
4000 } else {
4001 int i = 0; /* Should be first in document order !!!!! */
4002 switch (cur->nodesetval->nodeTab[i]->type) {
4003 case XML_ELEMENT_NODE:
4004 case XML_ATTRIBUTE_NODE:
4005 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4006 valuePush(ctxt, xmlXPathNewCString(""));
4007 else
4008 valuePush(ctxt, xmlXPathNewString(
4009 cur->nodesetval->nodeTab[i]->ns->href));
4010 break;
4011 default:
4012 valuePush(ctxt, xmlXPathNewCString(""));
4013 }
4014 }
4015 xmlXPathFreeObject(cur);
4016}
4017
4018/**
4019 * xmlXPathNameFunction:
4020 * @ctxt: the XPath Parser context
4021 * @nargs: the number of arguments
4022 *
4023 * Implement the name() XPath function
4024 * string name(node-set?)
4025 * The name function returns a string containing a QName representing
4026 * the name of the node in the argument node-set that is first in documenti
4027 * order. The QName must represent the name with respect to the namespace
4028 * declarations in effect on the node whose name is being represented.
4029 * Typically, this will be the form in which the name occurred in the XML
4030 * source. This need not be the case if there are namespace declarations
4031 * in effect on the node that associate multiple prefixes with the same
4032 * namespace. However, an implementation may include information about
4033 * the original prefix in its representation of nodes; in this case, an
4034 * implementation can ensure that the returned string is always the same
4035 * as the QName used in the XML source. If the argument it omitted it
4036 * defaults to the context node.
4037 * Libxml keep the original prefix so the "real qualified name" used is
4038 * returned.
4039 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004040static void
Owen Taylor3473f882001-02-23 17:55:21 +00004041xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4042 xmlXPathObjectPtr cur;
4043
4044 if (nargs == 0) {
4045 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4046 nargs = 1;
4047 }
4048
4049 CHECK_ARITY(1);
4050 if ((ctxt->value == NULL) ||
4051 ((ctxt->value->type != XPATH_NODESET) &&
4052 (ctxt->value->type != XPATH_XSLT_TREE)))
4053 XP_ERROR(XPATH_INVALID_TYPE);
4054 cur = valuePop(ctxt);
4055
Daniel Veillard911f49a2001-04-07 15:39:35 +00004056 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004057 valuePush(ctxt, xmlXPathNewCString(""));
4058 } else {
4059 int i = 0; /* Should be first in document order !!!!! */
4060
4061 switch (cur->nodesetval->nodeTab[i]->type) {
4062 case XML_ELEMENT_NODE:
4063 case XML_ATTRIBUTE_NODE:
4064 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4065 valuePush(ctxt, xmlXPathNewString(
4066 cur->nodesetval->nodeTab[i]->name));
4067
4068 else {
4069 char name[2000];
Owen Taylor3473f882001-02-23 17:55:21 +00004070 snprintf(name, sizeof(name), "%s:%s",
4071 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4072 (char *) cur->nodesetval->nodeTab[i]->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004073 name[sizeof(name) - 1] = 0;
4074 valuePush(ctxt, xmlXPathNewCString(name));
4075 }
4076 break;
4077 default:
4078 valuePush(ctxt,
4079 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4080 xmlXPathLocalNameFunction(ctxt, 1);
4081 }
4082 }
4083 xmlXPathFreeObject(cur);
4084}
4085
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004086
4087/**
4088 * xmlXPathConvertString:
4089 * @val: an XPath object
4090 *
4091 * Converts an existing object to its string() equivalent
4092 *
4093 * Returns the new object, the old one is freed (or the operation
4094 * is done directly on @val)
4095 */
4096xmlXPathObjectPtr
4097xmlXPathConvertString(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004098 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004099
4100 if (val == NULL)
4101 return(xmlXPathNewCString(""));
4102 switch (val->type) {
4103 case XPATH_UNDEFINED:
4104#ifdef DEBUG_EXPR
4105 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4106#endif
4107 ret = xmlXPathNewCString("");
4108 break;
4109 case XPATH_XSLT_TREE:
4110 case XPATH_NODESET:
Daniel Veillard911f49a2001-04-07 15:39:35 +00004111 if ((val->nodesetval == NULL) || (val->nodesetval->nodeNr == 0)) {
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004112 ret = xmlXPathNewCString("");
4113 } else {
4114 xmlChar *res;
4115
4116 xmlXPathNodeSetSort(val->nodesetval);
4117 res = xmlNodeGetContent(val->nodesetval->nodeTab[0]);
4118 /* TODO: avoid allocating res to free it */
4119 ret = xmlXPathNewString(res);
4120 if (res != NULL)
4121 xmlFree(res);
4122 }
4123 break;
4124 case XPATH_STRING:
4125 return(val);
4126 case XPATH_BOOLEAN:
4127 if (val->boolval) ret = xmlXPathNewCString("true");
4128 else ret = xmlXPathNewCString("false");
4129 break;
4130 case XPATH_NUMBER: {
4131 char buf[100];
4132
4133 xmlXPathFormatNumber(val->floatval, buf, sizeof(buf));
4134 ret = xmlXPathNewCString(buf);
4135 break;
4136 }
4137 case XPATH_USERS:
4138 case XPATH_POINT:
4139 case XPATH_RANGE:
4140 case XPATH_LOCATIONSET:
4141 TODO
4142 ret = xmlXPathNewCString("");
4143 break;
4144 }
4145 xmlXPathFreeObject(val);
4146 return(ret);
4147}
4148
Owen Taylor3473f882001-02-23 17:55:21 +00004149/**
4150 * xmlXPathStringFunction:
4151 * @ctxt: the XPath Parser context
4152 * @nargs: the number of arguments
4153 *
4154 * Implement the string() XPath function
4155 * string string(object?)
4156 * he string function converts an object to a string as follows:
4157 * - A node-set is converted to a string by returning the value of
4158 * the node in the node-set that is first in document order.
4159 * If the node-set is empty, an empty string is returned.
4160 * - A number is converted to a string as follows
4161 * + NaN is converted to the string NaN
4162 * + positive zero is converted to the string 0
4163 * + negative zero is converted to the string 0
4164 * + positive infinity is converted to the string Infinity
4165 * + negative infinity is converted to the string -Infinity
4166 * + if the number is an integer, the number is represented in
4167 * decimal form as a Number with no decimal point and no leading
4168 * zeros, preceded by a minus sign (-) if the number is negative
4169 * + otherwise, the number is represented in decimal form as a
4170 * Number including a decimal point with at least one digit
4171 * before the decimal point and at least one digit after the
4172 * decimal point, preceded by a minus sign (-) if the number
4173 * is negative; there must be no leading zeros before the decimal
4174 * point apart possibly from the one required digit immediatelyi
4175 * before the decimal point; beyond the one required digit
4176 * after the decimal point there must be as many, but only as
4177 * many, more digits as are needed to uniquely distinguish the
4178 * number from all other IEEE 754 numeric values.
4179 * - The boolean false value is converted to the string false.
4180 * The boolean true value is converted to the string true.
4181 *
4182 * If the argument is omitted, it defaults to a node-set with the
4183 * context node as its only member.
4184 */
4185void
4186xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4187 xmlXPathObjectPtr cur;
4188
4189 if (nargs == 0) {
4190 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4191 nargs = 1;
4192 }
4193
4194 CHECK_ARITY(1);
4195 cur = valuePop(ctxt);
4196 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004197 cur = xmlXPathConvertString(cur);
4198 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004199}
4200
4201/**
4202 * xmlXPathStringLengthFunction:
4203 * @ctxt: the XPath Parser context
4204 * @nargs: the number of arguments
4205 *
4206 * Implement the string-length() XPath function
4207 * number string-length(string?)
4208 * The string-length returns the number of characters in the string
4209 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4210 * the context node converted to a string, in other words the value
4211 * of the context node.
4212 */
4213void
4214xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4215 xmlXPathObjectPtr cur;
4216
4217 if (nargs == 0) {
4218 if (ctxt->context->node == NULL) {
4219 valuePush(ctxt, xmlXPathNewFloat(0));
4220 } else {
4221 xmlChar *content;
4222
4223 content = xmlNodeGetContent(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004224 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00004225 xmlFree(content);
4226 }
4227 return;
4228 }
4229 CHECK_ARITY(1);
4230 CAST_TO_STRING;
4231 CHECK_TYPE(XPATH_STRING);
4232 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004233 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00004234 xmlXPathFreeObject(cur);
4235}
4236
4237/**
4238 * xmlXPathConcatFunction:
4239 * @ctxt: the XPath Parser context
4240 * @nargs: the number of arguments
4241 *
4242 * Implement the concat() XPath function
4243 * string concat(string, string, string*)
4244 * The concat function returns the concatenation of its arguments.
4245 */
4246void
4247xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4248 xmlXPathObjectPtr cur, newobj;
4249 xmlChar *tmp;
4250
4251 if (nargs < 2) {
4252 CHECK_ARITY(2);
4253 }
4254
4255 CAST_TO_STRING;
4256 cur = valuePop(ctxt);
4257 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4258 xmlXPathFreeObject(cur);
4259 return;
4260 }
4261 nargs--;
4262
4263 while (nargs > 0) {
4264 CAST_TO_STRING;
4265 newobj = valuePop(ctxt);
4266 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4267 xmlXPathFreeObject(newobj);
4268 xmlXPathFreeObject(cur);
4269 XP_ERROR(XPATH_INVALID_TYPE);
4270 }
4271 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4272 newobj->stringval = cur->stringval;
4273 cur->stringval = tmp;
4274
4275 xmlXPathFreeObject(newobj);
4276 nargs--;
4277 }
4278 valuePush(ctxt, cur);
4279}
4280
4281/**
4282 * xmlXPathContainsFunction:
4283 * @ctxt: the XPath Parser context
4284 * @nargs: the number of arguments
4285 *
4286 * Implement the contains() XPath function
4287 * boolean contains(string, string)
4288 * The contains function returns true if the first argument string
4289 * contains the second argument string, and otherwise returns false.
4290 */
4291void
4292xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4293 xmlXPathObjectPtr hay, needle;
4294
4295 CHECK_ARITY(2);
4296 CAST_TO_STRING;
4297 CHECK_TYPE(XPATH_STRING);
4298 needle = valuePop(ctxt);
4299 CAST_TO_STRING;
4300 hay = valuePop(ctxt);
4301 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4302 xmlXPathFreeObject(hay);
4303 xmlXPathFreeObject(needle);
4304 XP_ERROR(XPATH_INVALID_TYPE);
4305 }
4306 if (xmlStrstr(hay->stringval, needle->stringval))
4307 valuePush(ctxt, xmlXPathNewBoolean(1));
4308 else
4309 valuePush(ctxt, xmlXPathNewBoolean(0));
4310 xmlXPathFreeObject(hay);
4311 xmlXPathFreeObject(needle);
4312}
4313
4314/**
4315 * xmlXPathStartsWithFunction:
4316 * @ctxt: the XPath Parser context
4317 * @nargs: the number of arguments
4318 *
4319 * Implement the starts-with() XPath function
4320 * boolean starts-with(string, string)
4321 * The starts-with function returns true if the first argument string
4322 * starts with the second argument string, and otherwise returns false.
4323 */
4324void
4325xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4326 xmlXPathObjectPtr hay, needle;
4327 int n;
4328
4329 CHECK_ARITY(2);
4330 CAST_TO_STRING;
4331 CHECK_TYPE(XPATH_STRING);
4332 needle = valuePop(ctxt);
4333 CAST_TO_STRING;
4334 hay = valuePop(ctxt);
4335 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4336 xmlXPathFreeObject(hay);
4337 xmlXPathFreeObject(needle);
4338 XP_ERROR(XPATH_INVALID_TYPE);
4339 }
4340 n = xmlStrlen(needle->stringval);
4341 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4342 valuePush(ctxt, xmlXPathNewBoolean(0));
4343 else
4344 valuePush(ctxt, xmlXPathNewBoolean(1));
4345 xmlXPathFreeObject(hay);
4346 xmlXPathFreeObject(needle);
4347}
4348
4349/**
4350 * xmlXPathSubstringFunction:
4351 * @ctxt: the XPath Parser context
4352 * @nargs: the number of arguments
4353 *
4354 * Implement the substring() XPath function
4355 * string substring(string, number, number?)
4356 * The substring function returns the substring of the first argument
4357 * starting at the position specified in the second argument with
4358 * length specified in the third argument. For example,
4359 * substring("12345",2,3) returns "234". If the third argument is not
4360 * specified, it returns the substring starting at the position specified
4361 * in the second argument and continuing to the end of the string. For
4362 * example, substring("12345",2) returns "2345". More precisely, each
4363 * character in the string (see [3.6 Strings]) is considered to have a
4364 * numeric position: the position of the first character is 1, the position
4365 * of the second character is 2 and so on. The returned substring contains
4366 * those characters for which the position of the character is greater than
4367 * or equal to the second argument and, if the third argument is specified,
4368 * less than the sum of the second and third arguments; the comparisons
4369 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4370 * - substring("12345", 1.5, 2.6) returns "234"
4371 * - substring("12345", 0, 3) returns "12"
4372 * - substring("12345", 0 div 0, 3) returns ""
4373 * - substring("12345", 1, 0 div 0) returns ""
4374 * - substring("12345", -42, 1 div 0) returns "12345"
4375 * - substring("12345", -1 div 0, 1 div 0) returns ""
4376 */
4377void
4378xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4379 xmlXPathObjectPtr str, start, len;
4380 double le, in;
4381 int i, l;
4382 xmlChar *ret;
4383
4384 /*
Daniel Veillarde043ee12001-04-16 14:08:07 +00004385 * TODO: need to be converted to UTF8 strings
Owen Taylor3473f882001-02-23 17:55:21 +00004386 */
4387 if (nargs < 2) {
4388 CHECK_ARITY(2);
4389 }
4390 if (nargs > 3) {
4391 CHECK_ARITY(3);
4392 }
4393 if (nargs == 3) {
4394 CAST_TO_NUMBER;
4395 CHECK_TYPE(XPATH_NUMBER);
4396 len = valuePop(ctxt);
4397 le = len->floatval;
4398 xmlXPathFreeObject(len);
4399 } else {
4400 le = 2000000000;
4401 }
4402 CAST_TO_NUMBER;
4403 CHECK_TYPE(XPATH_NUMBER);
4404 start = valuePop(ctxt);
4405 in = start->floatval;
4406 xmlXPathFreeObject(start);
4407 CAST_TO_STRING;
4408 CHECK_TYPE(XPATH_STRING);
4409 str = valuePop(ctxt);
4410 le += in;
4411
4412 /* integer index of the first char */
4413 i = (int) in;
4414 if (((double)i) != in) i++;
4415
4416 /* integer index of the last char */
4417 l = (int) le;
4418 if (((double)l) != le) l++;
4419
4420 /* back to a zero based len */
4421 i--;
4422 l--;
4423
4424 /* check against the string len */
4425 if (l > 1024) {
4426 l = xmlStrlen(str->stringval);
4427 }
4428 if (i < 0) {
4429 i = 0;
4430 }
4431
4432 /* number of chars to copy */
4433 l -= i;
4434
4435 ret = xmlStrsub(str->stringval, i, l);
4436 if (ret == NULL)
4437 valuePush(ctxt, xmlXPathNewCString(""));
4438 else {
4439 valuePush(ctxt, xmlXPathNewString(ret));
4440 xmlFree(ret);
4441 }
4442 xmlXPathFreeObject(str);
4443}
4444
4445/**
4446 * xmlXPathSubstringBeforeFunction:
4447 * @ctxt: the XPath Parser context
4448 * @nargs: the number of arguments
4449 *
4450 * Implement the substring-before() XPath function
4451 * string substring-before(string, string)
4452 * The substring-before function returns the substring of the first
4453 * argument string that precedes the first occurrence of the second
4454 * argument string in the first argument string, or the empty string
4455 * if the first argument string does not contain the second argument
4456 * string. For example, substring-before("1999/04/01","/") returns 1999.
4457 */
4458void
4459xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4460 xmlXPathObjectPtr str;
4461 xmlXPathObjectPtr find;
4462 xmlBufferPtr target;
4463 const xmlChar *point;
4464 int offset;
4465
4466 CHECK_ARITY(2);
4467 CAST_TO_STRING;
4468 find = valuePop(ctxt);
4469 CAST_TO_STRING;
4470 str = valuePop(ctxt);
4471
4472 target = xmlBufferCreate();
4473 if (target) {
4474 point = xmlStrstr(str->stringval, find->stringval);
4475 if (point) {
4476 offset = (int)(point - str->stringval);
4477 xmlBufferAdd(target, str->stringval, offset);
4478 }
4479 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4480 xmlBufferFree(target);
4481 }
4482
4483 xmlXPathFreeObject(str);
4484 xmlXPathFreeObject(find);
4485}
4486
4487/**
4488 * xmlXPathSubstringAfterFunction:
4489 * @ctxt: the XPath Parser context
4490 * @nargs: the number of arguments
4491 *
4492 * Implement the substring-after() XPath function
4493 * string substring-after(string, string)
4494 * The substring-after function returns the substring of the first
4495 * argument string that follows the first occurrence of the second
4496 * argument string in the first argument string, or the empty stringi
4497 * if the first argument string does not contain the second argument
4498 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4499 * and substring-after("1999/04/01","19") returns 99/04/01.
4500 */
4501void
4502xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4503 xmlXPathObjectPtr str;
4504 xmlXPathObjectPtr find;
4505 xmlBufferPtr target;
4506 const xmlChar *point;
4507 int offset;
4508
4509 CHECK_ARITY(2);
4510 CAST_TO_STRING;
4511 find = valuePop(ctxt);
4512 CAST_TO_STRING;
4513 str = valuePop(ctxt);
4514
4515 target = xmlBufferCreate();
4516 if (target) {
4517 point = xmlStrstr(str->stringval, find->stringval);
4518 if (point) {
4519 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4520 xmlBufferAdd(target, &str->stringval[offset],
4521 xmlStrlen(str->stringval) - offset);
4522 }
4523 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4524 xmlBufferFree(target);
4525 }
4526
4527 xmlXPathFreeObject(str);
4528 xmlXPathFreeObject(find);
4529}
4530
4531/**
4532 * xmlXPathNormalizeFunction:
4533 * @ctxt: the XPath Parser context
4534 * @nargs: the number of arguments
4535 *
4536 * Implement the normalize-space() XPath function
4537 * string normalize-space(string?)
4538 * The normalize-space function returns the argument string with white
4539 * space normalized by stripping leading and trailing whitespace
4540 * and replacing sequences of whitespace characters by a single
4541 * space. Whitespace characters are the same allowed by the S production
4542 * in XML. If the argument is omitted, it defaults to the context
4543 * node converted to a string, in other words the value of the context node.
4544 */
4545void
4546xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4547 xmlXPathObjectPtr obj = NULL;
4548 xmlChar *source = NULL;
4549 xmlBufferPtr target;
4550 xmlChar blank;
4551
4552 if (nargs == 0) {
4553 /* Use current context node */
4554 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4555 xmlXPathStringFunction(ctxt, 1);
4556 nargs = 1;
4557 }
4558
4559 CHECK_ARITY(1);
4560 CAST_TO_STRING;
4561 CHECK_TYPE(XPATH_STRING);
4562 obj = valuePop(ctxt);
4563 source = obj->stringval;
4564
4565 target = xmlBufferCreate();
4566 if (target && source) {
4567
4568 /* Skip leading whitespaces */
4569 while (IS_BLANK(*source))
4570 source++;
4571
4572 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4573 blank = 0;
4574 while (*source) {
4575 if (IS_BLANK(*source)) {
4576 blank = *source;
4577 } else {
4578 if (blank) {
4579 xmlBufferAdd(target, &blank, 1);
4580 blank = 0;
4581 }
4582 xmlBufferAdd(target, source, 1);
4583 }
4584 source++;
4585 }
4586
4587 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4588 xmlBufferFree(target);
4589 }
4590 xmlXPathFreeObject(obj);
4591}
4592
4593/**
4594 * xmlXPathTranslateFunction:
4595 * @ctxt: the XPath Parser context
4596 * @nargs: the number of arguments
4597 *
4598 * Implement the translate() XPath function
4599 * string translate(string, string, string)
4600 * The translate function returns the first argument string with
4601 * occurrences of characters in the second argument string replaced
4602 * by the character at the corresponding position in the third argument
4603 * string. For example, translate("bar","abc","ABC") returns the string
4604 * BAr. If there is a character in the second argument string with no
4605 * character at a corresponding position in the third argument string
4606 * (because the second argument string is longer than the third argument
4607 * string), then occurrences of that character in the first argument
4608 * string are removed. For example, translate("--aaa--","abc-","ABC")
4609 * returns "AAA". If a character occurs more than once in second
4610 * argument string, then the first occurrence determines the replacement
4611 * character. If the third argument string is longer than the second
4612 * argument string, then excess characters are ignored.
4613 */
4614void
4615xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00004616 xmlXPathObjectPtr str;
4617 xmlXPathObjectPtr from;
4618 xmlXPathObjectPtr to;
4619 xmlBufferPtr target;
4620 int i, offset, max;
4621 xmlChar ch;
4622 const xmlChar *point;
Owen Taylor3473f882001-02-23 17:55:21 +00004623
Daniel Veillarde043ee12001-04-16 14:08:07 +00004624 /*
4625 * TODO: need to be converted to UTF8 strings
4626 */
4627 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00004628
Daniel Veillarde043ee12001-04-16 14:08:07 +00004629 CAST_TO_STRING;
4630 to = valuePop(ctxt);
4631 CAST_TO_STRING;
4632 from = valuePop(ctxt);
4633 CAST_TO_STRING;
4634 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00004635
Daniel Veillarde043ee12001-04-16 14:08:07 +00004636 target = xmlBufferCreate();
4637 if (target) {
4638 max = xmlStrlen(to->stringval);
4639 for (i = 0; (ch = str->stringval[i]); i++) {
4640 point = xmlStrchr(from->stringval, ch);
4641 if (point) {
4642 offset = (int)(point - from->stringval);
4643 if (offset < max)
4644 xmlBufferAdd(target, &to->stringval[offset], 1);
4645 } else
4646 xmlBufferAdd(target, &ch, 1);
4647 }
Owen Taylor3473f882001-02-23 17:55:21 +00004648 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00004649 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4650 xmlBufferFree(target);
4651 xmlXPathFreeObject(str);
4652 xmlXPathFreeObject(from);
4653 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00004654}
4655
4656/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004657 * xmlXPathConvertBoolean:
4658 * @val: an XPath object
4659 *
4660 * Converts an existing object to its boolean() equivalent
4661 *
4662 * Returns the new object, the old one is freed (or the operation
4663 * is done directly on @val)
4664 */
4665xmlXPathObjectPtr
4666xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
4667 int res = 0;
4668
4669 if (val == NULL)
4670 return(NULL);
4671 switch (val->type) {
4672 case XPATH_NODESET:
4673 case XPATH_XSLT_TREE:
4674 if ((val->nodesetval == NULL) ||
4675 (val->nodesetval->nodeNr == 0)) res = 0;
4676 else
4677 res = 1;
4678 break;
4679 case XPATH_STRING:
4680 if ((val->stringval == NULL) ||
4681 (val->stringval[0] == 0)) res = 0;
4682 else
4683 res = 1;
4684 break;
4685 case XPATH_BOOLEAN:
4686 return(val);
4687 case XPATH_NUMBER:
4688 if (val->floatval) res = 1;
4689 break;
4690 default:
4691 STRANGE
4692 }
4693 xmlXPathFreeObject(val);
4694 return(xmlXPathNewBoolean(res));
4695}
4696
4697/**
Owen Taylor3473f882001-02-23 17:55:21 +00004698 * xmlXPathBooleanFunction:
4699 * @ctxt: the XPath Parser context
4700 * @nargs: the number of arguments
4701 *
4702 * Implement the boolean() XPath function
4703 * boolean boolean(object)
4704 * he boolean function converts its argument to a boolean as follows:
4705 * - a number is true if and only if it is neither positive or
4706 * negative zero nor NaN
4707 * - a node-set is true if and only if it is non-empty
4708 * - a string is true if and only if its length is non-zero
4709 */
4710void
4711xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4712 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00004713
4714 CHECK_ARITY(1);
4715 cur = valuePop(ctxt);
4716 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004717 cur = xmlXPathConvertBoolean(cur);
4718 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004719}
4720
4721/**
4722 * xmlXPathNotFunction:
4723 * @ctxt: the XPath Parser context
4724 * @nargs: the number of arguments
4725 *
4726 * Implement the not() XPath function
4727 * boolean not(boolean)
4728 * The not function returns true if its argument is false,
4729 * and false otherwise.
4730 */
4731void
4732xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4733 CHECK_ARITY(1);
4734 CAST_TO_BOOLEAN;
4735 CHECK_TYPE(XPATH_BOOLEAN);
4736 ctxt->value->boolval = ! ctxt->value->boolval;
4737}
4738
4739/**
4740 * xmlXPathTrueFunction:
4741 * @ctxt: the XPath Parser context
4742 * @nargs: the number of arguments
4743 *
4744 * Implement the true() XPath function
4745 * boolean true()
4746 */
4747void
4748xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4749 CHECK_ARITY(0);
4750 valuePush(ctxt, xmlXPathNewBoolean(1));
4751}
4752
4753/**
4754 * xmlXPathFalseFunction:
4755 * @ctxt: the XPath Parser context
4756 * @nargs: the number of arguments
4757 *
4758 * Implement the false() XPath function
4759 * boolean false()
4760 */
4761void
4762xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4763 CHECK_ARITY(0);
4764 valuePush(ctxt, xmlXPathNewBoolean(0));
4765}
4766
4767/**
4768 * xmlXPathLangFunction:
4769 * @ctxt: the XPath Parser context
4770 * @nargs: the number of arguments
4771 *
4772 * Implement the lang() XPath function
4773 * boolean lang(string)
4774 * The lang function returns true or false depending on whether the
4775 * language of the context node as specified by xml:lang attributes
4776 * is the same as or is a sublanguage of the language specified by
4777 * the argument string. The language of the context node is determined
4778 * by the value of the xml:lang attribute on the context node, or, if
4779 * the context node has no xml:lang attribute, by the value of the
4780 * xml:lang attribute on the nearest ancestor of the context node that
4781 * has an xml:lang attribute. If there is no such attribute, then lang
4782 * returns false. If there is such an attribute, then lang returns
4783 * true if the attribute value is equal to the argument ignoring case,
4784 * or if there is some suffix starting with - such that the attribute
4785 * value is equal to the argument ignoring that suffix of the attribute
4786 * value and ignoring case.
4787 */
4788void
4789xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4790 xmlXPathObjectPtr val;
4791 const xmlChar *theLang;
4792 const xmlChar *lang;
4793 int ret = 0;
4794 int i;
4795
4796 CHECK_ARITY(1);
4797 CAST_TO_STRING;
4798 CHECK_TYPE(XPATH_STRING);
4799 val = valuePop(ctxt);
4800 lang = val->stringval;
4801 theLang = xmlNodeGetLang(ctxt->context->node);
4802 if ((theLang != NULL) && (lang != NULL)) {
4803 for (i = 0;lang[i] != 0;i++)
4804 if (toupper(lang[i]) != toupper(theLang[i]))
4805 goto not_equal;
4806 ret = 1;
4807 }
4808not_equal:
4809 xmlXPathFreeObject(val);
4810 valuePush(ctxt, xmlXPathNewBoolean(ret));
4811}
4812
4813/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004814 * xmlXPathConvertNumber:
4815 * @val: an XPath object
4816 *
4817 * Converts an existing object to its number() equivalent
4818 *
4819 * Returns the new object, the old one is freed (or the operation
4820 * is done directly on @val)
4821 */
4822xmlXPathObjectPtr
4823xmlXPathConvertNumber(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004824 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004825 double res;
4826
4827 if (val == NULL)
4828 return(xmlXPathNewFloat(0.0));
4829 switch (val->type) {
4830 case XPATH_UNDEFINED:
4831#ifdef DEBUG_EXPR
4832 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
4833#endif
4834 ret = xmlXPathNewFloat(0.0);
4835 break;
4836 case XPATH_XSLT_TREE:
4837 case XPATH_NODESET:
4838 val = xmlXPathConvertString(val);
4839 /* no break on purpose */
4840 case XPATH_STRING:
4841 res = xmlXPathStringEvalNumber(val->stringval);
4842 ret = xmlXPathNewFloat(res);
4843 break;
4844 case XPATH_BOOLEAN:
4845 if (val->boolval) ret = xmlXPathNewFloat(1.0);
4846 else ret = xmlXPathNewFloat(0.0);
4847 break;
4848 case XPATH_NUMBER:
4849 return(val);
4850 case XPATH_USERS:
4851 case XPATH_POINT:
4852 case XPATH_RANGE:
4853 case XPATH_LOCATIONSET:
4854 TODO
4855 ret = xmlXPathNewFloat(0.0);
4856 break;
4857 }
4858 xmlXPathFreeObject(val);
4859 return(ret);
4860}
4861
4862/**
Owen Taylor3473f882001-02-23 17:55:21 +00004863 * xmlXPathNumberFunction:
4864 * @ctxt: the XPath Parser context
4865 * @nargs: the number of arguments
4866 *
4867 * Implement the number() XPath function
4868 * number number(object?)
4869 */
4870void
4871xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4872 xmlXPathObjectPtr cur;
4873 double res;
4874
4875 if (nargs == 0) {
4876 if (ctxt->context->node == NULL) {
4877 valuePush(ctxt, xmlXPathNewFloat(0.0));
4878 } else {
4879 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
4880
4881 res = xmlXPathStringEvalNumber(content);
4882 valuePush(ctxt, xmlXPathNewFloat(res));
4883 xmlFree(content);
4884 }
4885 return;
4886 }
4887
4888 CHECK_ARITY(1);
4889 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004890 cur = xmlXPathConvertNumber(cur);
4891 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004892}
4893
4894/**
4895 * xmlXPathSumFunction:
4896 * @ctxt: the XPath Parser context
4897 * @nargs: the number of arguments
4898 *
4899 * Implement the sum() XPath function
4900 * number sum(node-set)
4901 * The sum function returns the sum of the values of the nodes in
4902 * the argument node-set.
4903 */
4904void
4905xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4906 xmlXPathObjectPtr cur;
4907 int i;
4908
4909 CHECK_ARITY(1);
4910 if ((ctxt->value == NULL) ||
4911 ((ctxt->value->type != XPATH_NODESET) &&
4912 (ctxt->value->type != XPATH_XSLT_TREE)))
4913 XP_ERROR(XPATH_INVALID_TYPE);
4914 cur = valuePop(ctxt);
4915
Daniel Veillardd8df6c02001-04-05 16:54:14 +00004916 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004917 valuePush(ctxt, xmlXPathNewFloat(0.0));
4918 } else {
4919 valuePush(ctxt,
4920 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
4921 xmlXPathNumberFunction(ctxt, 1);
4922 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
4923 valuePush(ctxt,
4924 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4925 xmlXPathAddValues(ctxt);
4926 }
4927 }
4928 xmlXPathFreeObject(cur);
4929}
4930
4931/**
4932 * xmlXPathFloorFunction:
4933 * @ctxt: the XPath Parser context
4934 * @nargs: the number of arguments
4935 *
4936 * Implement the floor() XPath function
4937 * number floor(number)
4938 * The floor function returns the largest (closest to positive infinity)
4939 * number that is not greater than the argument and that is an integer.
4940 */
4941void
4942xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4943 CHECK_ARITY(1);
4944 CAST_TO_NUMBER;
4945 CHECK_TYPE(XPATH_NUMBER);
4946#if 0
4947 ctxt->value->floatval = floor(ctxt->value->floatval);
4948#else
4949 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
4950 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
4951#endif
4952}
4953
4954/**
4955 * xmlXPathCeilingFunction:
4956 * @ctxt: the XPath Parser context
4957 * @nargs: the number of arguments
4958 *
4959 * Implement the ceiling() XPath function
4960 * number ceiling(number)
4961 * The ceiling function returns the smallest (closest to negative infinity)
4962 * number that is not less than the argument and that is an integer.
4963 */
4964void
4965xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4966 double f;
4967
4968 CHECK_ARITY(1);
4969 CAST_TO_NUMBER;
4970 CHECK_TYPE(XPATH_NUMBER);
4971
4972#if 0
4973 ctxt->value->floatval = ceil(ctxt->value->floatval);
4974#else
4975 f = (double)((int) ctxt->value->floatval);
4976 if (f != ctxt->value->floatval)
4977 ctxt->value->floatval = f + 1;
4978#endif
4979}
4980
4981/**
4982 * xmlXPathRoundFunction:
4983 * @ctxt: the XPath Parser context
4984 * @nargs: the number of arguments
4985 *
4986 * Implement the round() XPath function
4987 * number round(number)
4988 * The round function returns the number that is closest to the
4989 * argument and that is an integer. If there are two such numbers,
4990 * then the one that is even is returned.
4991 */
4992void
4993xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4994 double f;
4995
4996 CHECK_ARITY(1);
4997 CAST_TO_NUMBER;
4998 CHECK_TYPE(XPATH_NUMBER);
4999
5000 if ((ctxt->value->floatval == xmlXPathNAN) ||
5001 (ctxt->value->floatval == xmlXPathPINF) ||
5002 (ctxt->value->floatval == xmlXPathNINF) ||
5003 (ctxt->value->floatval == 0.0))
5004 return;
5005
5006#if 0
5007 f = floor(ctxt->value->floatval);
5008#else
5009 f = (double)((int) ctxt->value->floatval);
5010#endif
5011 if (ctxt->value->floatval < f + 0.5)
5012 ctxt->value->floatval = f;
5013 else
5014 ctxt->value->floatval = f + 1;
5015}
5016
5017/************************************************************************
5018 * *
5019 * The Parser *
5020 * *
5021 ************************************************************************/
5022
5023/*
5024 * a couple of forward declarations since we use a recursive call based
5025 * implementation.
5026 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005027static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005028static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005029static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005030#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005031static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
5032#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00005033#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005034static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005035#endif
5036
5037/**
5038 * xmlXPathParseNCName:
5039 * @ctxt: the XPath Parser context
5040 *
5041 * parse an XML namespace non qualified name.
5042 *
5043 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
5044 *
5045 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
5046 * CombiningChar | Extender
5047 *
5048 * Returns the namespace name or NULL
5049 */
5050
5051xmlChar *
5052xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
5053 const xmlChar *q;
5054 xmlChar *ret = NULL;
5055
5056 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5057 q = NEXT;
5058
5059 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5060 (CUR == '.') || (CUR == '-') ||
5061 (CUR == '_') ||
5062 (IS_COMBINING(CUR)) ||
5063 (IS_EXTENDER(CUR)))
5064 NEXT;
5065
5066 ret = xmlStrndup(q, CUR_PTR - q);
5067
5068 return(ret);
5069}
5070
5071/**
5072 * xmlXPathParseQName:
5073 * @ctxt: the XPath Parser context
5074 * @prefix: a xmlChar **
5075 *
5076 * parse an XML qualified name
5077 *
5078 * [NS 5] QName ::= (Prefix ':')? LocalPart
5079 *
5080 * [NS 6] Prefix ::= NCName
5081 *
5082 * [NS 7] LocalPart ::= NCName
5083 *
5084 * Returns the function returns the local part, and prefix is updated
5085 * to get the Prefix if any.
5086 */
5087
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005088static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005089xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
5090 xmlChar *ret = NULL;
5091
5092 *prefix = NULL;
5093 ret = xmlXPathParseNCName(ctxt);
5094 if (CUR == ':') {
5095 *prefix = ret;
5096 NEXT;
5097 ret = xmlXPathParseNCName(ctxt);
5098 }
5099 return(ret);
5100}
5101
5102/**
5103 * xmlXPathParseName:
5104 * @ctxt: the XPath Parser context
5105 *
5106 * parse an XML name
5107 *
5108 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5109 * CombiningChar | Extender
5110 *
5111 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5112 *
5113 * Returns the namespace name or NULL
5114 */
5115
5116xmlChar *
5117xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
5118 const xmlChar *q;
5119 xmlChar *ret = NULL;
5120
5121 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5122 q = NEXT;
5123
5124 /* TODO Make this UTF8 compliant !!! */
5125 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5126 (CUR == '.') || (CUR == '-') ||
5127 (CUR == '_') || (CUR == ':') ||
5128 (IS_COMBINING(CUR)) ||
5129 (IS_EXTENDER(CUR)))
5130 NEXT;
5131
5132 ret = xmlStrndup(q, CUR_PTR - q);
5133
5134 return(ret);
5135}
5136
5137/**
5138 * xmlXPathStringEvalNumber:
5139 * @str: A string to scan
5140 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00005141 * [30a] Float ::= Number ('e' Digits?)?
5142 *
Owen Taylor3473f882001-02-23 17:55:21 +00005143 * [30] Number ::= Digits ('.' Digits?)?
5144 * | '.' Digits
5145 * [31] Digits ::= [0-9]+
5146 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005147 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00005148 * In complement of the Number expression, this function also handles
5149 * negative values : '-' Number.
5150 *
5151 * Returns the double value.
5152 */
5153double
5154xmlXPathStringEvalNumber(const xmlChar *str) {
5155 const xmlChar *cur = str;
5156 double ret = 0.0;
5157 double mult = 1;
5158 int ok = 0;
5159 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005160 int exponent = 0;
5161 int is_exponent_negative = 0;
5162
Owen Taylor3473f882001-02-23 17:55:21 +00005163 while (IS_BLANK(*cur)) cur++;
5164 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5165 return(xmlXPathNAN);
5166 }
5167 if (*cur == '-') {
5168 isneg = 1;
5169 cur++;
5170 }
5171 while ((*cur >= '0') && (*cur <= '9')) {
5172 ret = ret * 10 + (*cur - '0');
5173 ok = 1;
5174 cur++;
5175 }
5176 if (*cur == '.') {
5177 cur++;
5178 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5179 return(xmlXPathNAN);
5180 }
5181 while ((*cur >= '0') && (*cur <= '9')) {
5182 mult /= 10;
5183 ret = ret + (*cur - '0') * mult;
5184 cur++;
5185 }
5186 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00005187 if ((*cur == 'e') || (*cur == 'E')) {
5188 cur++;
5189 if (*cur == '-') {
5190 is_exponent_negative = 1;
5191 cur++;
5192 }
5193 while ((*cur >= '0') && (*cur <= '9')) {
5194 exponent = exponent * 10 + (*cur - '0');
5195 cur++;
5196 }
5197 }
Owen Taylor3473f882001-02-23 17:55:21 +00005198 while (IS_BLANK(*cur)) cur++;
5199 if (*cur != 0) return(xmlXPathNAN);
5200 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005201 if (is_exponent_negative) exponent = -exponent;
5202 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00005203 return(ret);
5204}
5205
5206/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005207 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00005208 * @ctxt: the XPath Parser context
5209 *
5210 * [30] Number ::= Digits ('.' Digits?)?
5211 * | '.' Digits
5212 * [31] Digits ::= [0-9]+
5213 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005214 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00005215 *
5216 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005217static void
5218xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005219 double ret = 0.0;
5220 double mult = 1;
5221 int ok = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00005222 int exponent = 0;
5223 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005224
5225 CHECK_ERROR;
5226 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5227 XP_ERROR(XPATH_NUMBER_ERROR);
5228 }
5229 while ((CUR >= '0') && (CUR <= '9')) {
5230 ret = ret * 10 + (CUR - '0');
5231 ok = 1;
5232 NEXT;
5233 }
5234 if (CUR == '.') {
5235 NEXT;
5236 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5237 XP_ERROR(XPATH_NUMBER_ERROR);
5238 }
5239 while ((CUR >= '0') && (CUR <= '9')) {
5240 mult /= 10;
5241 ret = ret + (CUR - '0') * mult;
5242 NEXT;
5243 }
5244 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00005245 if ((CUR == 'e') || (CUR == 'E')) {
5246 NEXT;
5247 if (CUR == '-') {
5248 is_exponent_negative = 1;
5249 NEXT;
5250 }
5251 while ((CUR >= '0') && (CUR <= '9')) {
5252 exponent = exponent * 10 + (CUR - '0');
5253 NEXT;
5254 }
5255 }
5256 if (is_exponent_negative)
5257 exponent = -exponent;
5258 ret *= pow(10.0, (double)exponent);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005259 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
5260 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005261}
5262
5263/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005264 * xmlXPathParseLiteral:
5265 * @ctxt: the XPath Parser context
5266 *
5267 * Parse a Literal
5268 *
5269 * [29] Literal ::= '"' [^"]* '"'
5270 * | "'" [^']* "'"
5271 *
5272 * Returns the value found or NULL in case of error
5273 */
5274static xmlChar *
5275xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
5276 const xmlChar *q;
5277 xmlChar *ret = NULL;
5278
5279 if (CUR == '"') {
5280 NEXT;
5281 q = CUR_PTR;
5282 while ((IS_CHAR(CUR)) && (CUR != '"'))
5283 NEXT;
5284 if (!IS_CHAR(CUR)) {
5285 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5286 } else {
5287 ret = xmlStrndup(q, CUR_PTR - q);
5288 NEXT;
5289 }
5290 } else if (CUR == '\'') {
5291 NEXT;
5292 q = CUR_PTR;
5293 while ((IS_CHAR(CUR)) && (CUR != '\''))
5294 NEXT;
5295 if (!IS_CHAR(CUR)) {
5296 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5297 } else {
5298 ret = xmlStrndup(q, CUR_PTR - q);
5299 NEXT;
5300 }
5301 } else {
5302 XP_ERROR0(XPATH_START_LITERAL_ERROR);
5303 }
5304 return(ret);
5305}
5306
5307/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005308 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00005309 * @ctxt: the XPath Parser context
5310 *
5311 * Parse a Literal and push it on the stack.
5312 *
5313 * [29] Literal ::= '"' [^"]* '"'
5314 * | "'" [^']* "'"
5315 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005316 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00005317 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005318static void
5319xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005320 const xmlChar *q;
5321 xmlChar *ret = NULL;
5322
5323 if (CUR == '"') {
5324 NEXT;
5325 q = CUR_PTR;
5326 while ((IS_CHAR(CUR)) && (CUR != '"'))
5327 NEXT;
5328 if (!IS_CHAR(CUR)) {
5329 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5330 } else {
5331 ret = xmlStrndup(q, CUR_PTR - q);
5332 NEXT;
5333 }
5334 } else if (CUR == '\'') {
5335 NEXT;
5336 q = CUR_PTR;
5337 while ((IS_CHAR(CUR)) && (CUR != '\''))
5338 NEXT;
5339 if (!IS_CHAR(CUR)) {
5340 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5341 } else {
5342 ret = xmlStrndup(q, CUR_PTR - q);
5343 NEXT;
5344 }
5345 } else {
5346 XP_ERROR(XPATH_START_LITERAL_ERROR);
5347 }
5348 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005349 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
5350 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005351 xmlFree(ret);
5352}
5353
5354/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005355 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00005356 * @ctxt: the XPath Parser context
5357 *
5358 * Parse a VariableReference, evaluate it and push it on the stack.
5359 *
5360 * The variable bindings consist of a mapping from variable names
5361 * to variable values. The value of a variable is an object, which
5362 * of any of the types that are possible for the value of an expression,
5363 * and may also be of additional types not specified here.
5364 *
5365 * Early evaluation is possible since:
5366 * The variable bindings [...] used to evaluate a subexpression are
5367 * always the same as those used to evaluate the containing expression.
5368 *
5369 * [36] VariableReference ::= '$' QName
5370 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005371static void
5372xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005373 xmlChar *name;
5374 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005375
5376 SKIP_BLANKS;
5377 if (CUR != '$') {
5378 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5379 }
5380 NEXT;
5381 name = xmlXPathParseQName(ctxt, &prefix);
5382 if (name == NULL) {
5383 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5384 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005385 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005386 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
5387 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005388 SKIP_BLANKS;
5389}
5390
5391/**
5392 * xmlXPathIsNodeType:
5393 * @ctxt: the XPath Parser context
5394 * @name: a name string
5395 *
5396 * Is the name given a NodeType one.
5397 *
5398 * [38] NodeType ::= 'comment'
5399 * | 'text'
5400 * | 'processing-instruction'
5401 * | 'node'
5402 *
5403 * Returns 1 if true 0 otherwise
5404 */
5405int
5406xmlXPathIsNodeType(const xmlChar *name) {
5407 if (name == NULL)
5408 return(0);
5409
5410 if (xmlStrEqual(name, BAD_CAST "comment"))
5411 return(1);
5412 if (xmlStrEqual(name, BAD_CAST "text"))
5413 return(1);
5414 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5415 return(1);
5416 if (xmlStrEqual(name, BAD_CAST "node"))
5417 return(1);
5418 return(0);
5419}
5420
5421/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005422 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00005423 * @ctxt: the XPath Parser context
5424 *
5425 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5426 * [17] Argument ::= Expr
5427 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005428 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00005429 * pushed on the stack
5430 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005431static void
5432xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005433 xmlChar *name;
5434 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005435 int nbargs = 0;
5436
5437 name = xmlXPathParseQName(ctxt, &prefix);
5438 if (name == NULL) {
5439 XP_ERROR(XPATH_EXPR_ERROR);
5440 }
5441 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005442#ifdef DEBUG_EXPR
5443 if (prefix == NULL)
5444 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5445 name);
5446 else
5447 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5448 prefix, name);
5449#endif
5450
Owen Taylor3473f882001-02-23 17:55:21 +00005451 if (CUR != '(') {
5452 XP_ERROR(XPATH_EXPR_ERROR);
5453 }
5454 NEXT;
5455 SKIP_BLANKS;
5456
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005457 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005458 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005459 int op1 = ctxt->comp->last;
5460 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005461 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005462 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005463 nbargs++;
5464 if (CUR == ')') break;
5465 if (CUR != ',') {
5466 XP_ERROR(XPATH_EXPR_ERROR);
5467 }
5468 NEXT;
5469 SKIP_BLANKS;
5470 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005471 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
5472 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005473 NEXT;
5474 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005475}
5476
5477/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005478 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005479 * @ctxt: the XPath Parser context
5480 *
5481 * [15] PrimaryExpr ::= VariableReference
5482 * | '(' Expr ')'
5483 * | Literal
5484 * | Number
5485 * | FunctionCall
5486 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005487 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005488 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005489static void
5490xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005491 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005492 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005493 else if (CUR == '(') {
5494 NEXT;
5495 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005496 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005497 if (CUR != ')') {
5498 XP_ERROR(XPATH_EXPR_ERROR);
5499 }
5500 NEXT;
5501 SKIP_BLANKS;
5502 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005503 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005504 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005505 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005506 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005507 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005508 }
5509 SKIP_BLANKS;
5510}
5511
5512/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005513 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005514 * @ctxt: the XPath Parser context
5515 *
5516 * [20] FilterExpr ::= PrimaryExpr
5517 * | FilterExpr Predicate
5518 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005519 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005520 * Square brackets are used to filter expressions in the same way that
5521 * they are used in location paths. It is an error if the expression to
5522 * be filtered does not evaluate to a node-set. The context node list
5523 * used for evaluating the expression in square brackets is the node-set
5524 * to be filtered listed in document order.
5525 */
5526
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005527static void
5528xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
5529 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005530 CHECK_ERROR;
5531 SKIP_BLANKS;
5532
5533 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005534 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00005535 SKIP_BLANKS;
5536 }
5537
5538
5539}
5540
5541/**
5542 * xmlXPathScanName:
5543 * @ctxt: the XPath Parser context
5544 *
5545 * Trickery: parse an XML name but without consuming the input flow
5546 * Needed to avoid insanity in the parser state.
5547 *
5548 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5549 * CombiningChar | Extender
5550 *
5551 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5552 *
5553 * [6] Names ::= Name (S Name)*
5554 *
5555 * Returns the Name parsed or NULL
5556 */
5557
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005558static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005559xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5560 xmlChar buf[XML_MAX_NAMELEN];
5561 int len = 0;
5562
5563 SKIP_BLANKS;
5564 if (!IS_LETTER(CUR) && (CUR != '_') &&
5565 (CUR != ':')) {
5566 return(NULL);
5567 }
5568
5569 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5570 (NXT(len) == '.') || (NXT(len) == '-') ||
5571 (NXT(len) == '_') || (NXT(len) == ':') ||
5572 (IS_COMBINING(NXT(len))) ||
5573 (IS_EXTENDER(NXT(len)))) {
5574 buf[len] = NXT(len);
5575 len++;
5576 if (len >= XML_MAX_NAMELEN) {
5577 xmlGenericError(xmlGenericErrorContext,
5578 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5579 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5580 (NXT(len) == '.') || (NXT(len) == '-') ||
5581 (NXT(len) == '_') || (NXT(len) == ':') ||
5582 (IS_COMBINING(NXT(len))) ||
5583 (IS_EXTENDER(NXT(len))))
5584 len++;
5585 break;
5586 }
5587 }
5588 return(xmlStrndup(buf, len));
5589}
5590
5591/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005592 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005593 * @ctxt: the XPath Parser context
5594 *
5595 * [19] PathExpr ::= LocationPath
5596 * | FilterExpr
5597 * | FilterExpr '/' RelativeLocationPath
5598 * | FilterExpr '//' RelativeLocationPath
5599 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005600 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005601 * The / operator and // operators combine an arbitrary expression
5602 * and a relative location path. It is an error if the expression
5603 * does not evaluate to a node-set.
5604 * The / operator does composition in the same way as when / is
5605 * used in a location path. As in location paths, // is short for
5606 * /descendant-or-self::node()/.
5607 */
5608
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005609static void
5610xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005611 int lc = 1; /* Should we branch to LocationPath ? */
5612 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5613
5614 SKIP_BLANKS;
5615 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5616 (CUR == '\'') || (CUR == '"')) {
5617 lc = 0;
5618 } else if (CUR == '*') {
5619 /* relative or absolute location path */
5620 lc = 1;
5621 } else if (CUR == '/') {
5622 /* relative or absolute location path */
5623 lc = 1;
5624 } else if (CUR == '@') {
5625 /* relative abbreviated attribute location path */
5626 lc = 1;
5627 } else if (CUR == '.') {
5628 /* relative abbreviated attribute location path */
5629 lc = 1;
5630 } else {
5631 /*
5632 * Problem is finding if we have a name here whether it's:
5633 * - a nodetype
5634 * - a function call in which case it's followed by '('
5635 * - an axis in which case it's followed by ':'
5636 * - a element name
5637 * We do an a priori analysis here rather than having to
5638 * maintain parsed token content through the recursive function
5639 * calls. This looks uglier but makes the code quite easier to
5640 * read/write/debug.
5641 */
5642 SKIP_BLANKS;
5643 name = xmlXPathScanName(ctxt);
5644 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5645#ifdef DEBUG_STEP
5646 xmlGenericError(xmlGenericErrorContext,
5647 "PathExpr: Axis\n");
5648#endif
5649 lc = 1;
5650 xmlFree(name);
5651 } else if (name != NULL) {
5652 int len =xmlStrlen(name);
5653 int blank = 0;
5654
5655
5656 while (NXT(len) != 0) {
5657 if (NXT(len) == '/') {
5658 /* element name */
5659#ifdef DEBUG_STEP
5660 xmlGenericError(xmlGenericErrorContext,
5661 "PathExpr: AbbrRelLocation\n");
5662#endif
5663 lc = 1;
5664 break;
5665 } else if (IS_BLANK(NXT(len))) {
5666 /* skip to next */
5667 blank = 1;
5668 } else if (NXT(len) == ':') {
5669#ifdef DEBUG_STEP
5670 xmlGenericError(xmlGenericErrorContext,
5671 "PathExpr: AbbrRelLocation\n");
5672#endif
5673 lc = 1;
5674 break;
5675 } else if ((NXT(len) == '(')) {
5676 /* Note Type or Function */
5677 if (xmlXPathIsNodeType(name)) {
5678#ifdef DEBUG_STEP
5679 xmlGenericError(xmlGenericErrorContext,
5680 "PathExpr: Type search\n");
5681#endif
5682 lc = 1;
5683 } else {
5684#ifdef DEBUG_STEP
5685 xmlGenericError(xmlGenericErrorContext,
5686 "PathExpr: function call\n");
5687#endif
5688 lc = 0;
5689 }
5690 break;
5691 } else if ((NXT(len) == '[')) {
5692 /* element name */
5693#ifdef DEBUG_STEP
5694 xmlGenericError(xmlGenericErrorContext,
5695 "PathExpr: AbbrRelLocation\n");
5696#endif
5697 lc = 1;
5698 break;
5699 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
5700 (NXT(len) == '=')) {
5701 lc = 1;
5702 break;
5703 } else {
5704 lc = 1;
5705 break;
5706 }
5707 len++;
5708 }
5709 if (NXT(len) == 0) {
5710#ifdef DEBUG_STEP
5711 xmlGenericError(xmlGenericErrorContext,
5712 "PathExpr: AbbrRelLocation\n");
5713#endif
5714 /* element name */
5715 lc = 1;
5716 }
5717 xmlFree(name);
5718 } else {
5719 /* make sure all cases are covered explicitely */
5720 XP_ERROR(XPATH_EXPR_ERROR);
5721 }
5722 }
5723
5724 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005725 if (CUR == '/') {
5726 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
5727 } else {
5728 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005729 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005730 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005731 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005732 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005733 CHECK_ERROR;
5734 if ((CUR == '/') && (NXT(1) == '/')) {
5735 SKIP(2);
5736 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005737
5738 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
5739 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
5740 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
5741
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005742 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005743 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005744 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005745 }
5746 }
5747 SKIP_BLANKS;
5748}
5749
5750/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005751 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005752 * @ctxt: the XPath Parser context
5753 *
5754 * [18] UnionExpr ::= PathExpr
5755 * | UnionExpr '|' PathExpr
5756 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005757 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005758 */
5759
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005760static void
5761xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
5762 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005763 CHECK_ERROR;
5764 SKIP_BLANKS;
5765 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005766 int op1 = ctxt->comp->last;
5767 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005768
5769 NEXT;
5770 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005771 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005772
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005773 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
5774
Owen Taylor3473f882001-02-23 17:55:21 +00005775 SKIP_BLANKS;
5776 }
Owen Taylor3473f882001-02-23 17:55:21 +00005777}
5778
5779/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005780 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005781 * @ctxt: the XPath Parser context
5782 *
5783 * [27] UnaryExpr ::= UnionExpr
5784 * | '-' UnaryExpr
5785 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005786 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005787 */
5788
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005789static void
5790xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005791 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005792 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005793
5794 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00005795 while (CUR == '-') {
5796 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005797 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005798 NEXT;
5799 SKIP_BLANKS;
5800 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005801
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005802 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005803 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005804 if (found) {
5805 if (minus)
5806 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
5807 else
5808 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005809 }
5810}
5811
5812/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005813 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005814 * @ctxt: the XPath Parser context
5815 *
5816 * [26] MultiplicativeExpr ::= UnaryExpr
5817 * | MultiplicativeExpr MultiplyOperator UnaryExpr
5818 * | MultiplicativeExpr 'div' UnaryExpr
5819 * | MultiplicativeExpr 'mod' UnaryExpr
5820 * [34] MultiplyOperator ::= '*'
5821 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005822 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005823 */
5824
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005825static void
5826xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
5827 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005828 CHECK_ERROR;
5829 SKIP_BLANKS;
5830 while ((CUR == '*') ||
5831 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
5832 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
5833 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005834 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005835
5836 if (CUR == '*') {
5837 op = 0;
5838 NEXT;
5839 } else if (CUR == 'd') {
5840 op = 1;
5841 SKIP(3);
5842 } else if (CUR == 'm') {
5843 op = 2;
5844 SKIP(3);
5845 }
5846 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005847 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005848 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005849 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005850 SKIP_BLANKS;
5851 }
5852}
5853
5854/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005855 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005856 * @ctxt: the XPath Parser context
5857 *
5858 * [25] AdditiveExpr ::= MultiplicativeExpr
5859 * | AdditiveExpr '+' MultiplicativeExpr
5860 * | AdditiveExpr '-' MultiplicativeExpr
5861 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005862 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005863 */
5864
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005865static void
5866xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005867
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005868 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005869 CHECK_ERROR;
5870 SKIP_BLANKS;
5871 while ((CUR == '+') || (CUR == '-')) {
5872 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005873 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005874
5875 if (CUR == '+') plus = 1;
5876 else plus = 0;
5877 NEXT;
5878 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005879 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005880 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005881 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005882 SKIP_BLANKS;
5883 }
5884}
5885
5886/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005887 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005888 * @ctxt: the XPath Parser context
5889 *
5890 * [24] RelationalExpr ::= AdditiveExpr
5891 * | RelationalExpr '<' AdditiveExpr
5892 * | RelationalExpr '>' AdditiveExpr
5893 * | RelationalExpr '<=' AdditiveExpr
5894 * | RelationalExpr '>=' AdditiveExpr
5895 *
5896 * A <= B > C is allowed ? Answer from James, yes with
5897 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
5898 * which is basically what got implemented.
5899 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005900 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00005901 * on the stack
5902 */
5903
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005904static void
5905xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
5906 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005907 CHECK_ERROR;
5908 SKIP_BLANKS;
5909 while ((CUR == '<') ||
5910 (CUR == '>') ||
5911 ((CUR == '<') && (NXT(1) == '=')) ||
5912 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005913 int inf, strict;
5914 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005915
5916 if (CUR == '<') inf = 1;
5917 else inf = 0;
5918 if (NXT(1) == '=') strict = 0;
5919 else strict = 1;
5920 NEXT;
5921 if (!strict) NEXT;
5922 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005923 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005924 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005925 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00005926 SKIP_BLANKS;
5927 }
5928}
5929
5930/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005931 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005932 * @ctxt: the XPath Parser context
5933 *
5934 * [23] EqualityExpr ::= RelationalExpr
5935 * | EqualityExpr '=' RelationalExpr
5936 * | EqualityExpr '!=' RelationalExpr
5937 *
5938 * A != B != C is allowed ? Answer from James, yes with
5939 * (RelationalExpr = RelationalExpr) = RelationalExpr
5940 * (RelationalExpr != RelationalExpr) != RelationalExpr
5941 * which is basically what got implemented.
5942 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005943 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005944 *
5945 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005946static void
5947xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
5948 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005949 CHECK_ERROR;
5950 SKIP_BLANKS;
5951 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005952 int eq;
5953 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005954
5955 if (CUR == '=') eq = 1;
5956 else eq = 0;
5957 NEXT;
5958 if (!eq) NEXT;
5959 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005960 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005961 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005962 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005963 SKIP_BLANKS;
5964 }
5965}
5966
5967/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005968 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005969 * @ctxt: the XPath Parser context
5970 *
5971 * [22] AndExpr ::= EqualityExpr
5972 * | AndExpr 'and' EqualityExpr
5973 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005974 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005975 *
5976 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005977static void
5978xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
5979 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005980 CHECK_ERROR;
5981 SKIP_BLANKS;
5982 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005983 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005984 SKIP(3);
5985 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005986 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005987 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005988 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005989 SKIP_BLANKS;
5990 }
5991}
5992
5993/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005994 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005995 * @ctxt: the XPath Parser context
5996 *
5997 * [14] Expr ::= OrExpr
5998 * [21] OrExpr ::= AndExpr
5999 * | OrExpr 'or' AndExpr
6000 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006001 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00006002 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006003static void
6004xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
6005 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006006 CHECK_ERROR;
6007 SKIP_BLANKS;
6008 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006009 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006010 SKIP(2);
6011 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006012 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006013 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006014 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
6015 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00006016 SKIP_BLANKS;
6017 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006018 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
6019 /* more ops could be optimized too */
6020 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
6021 }
Owen Taylor3473f882001-02-23 17:55:21 +00006022}
6023
6024/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006025 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00006026 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006027 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00006028 *
6029 * [8] Predicate ::= '[' PredicateExpr ']'
6030 * [9] PredicateExpr ::= Expr
6031 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006032 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00006033 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006034static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006035xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006036 int op1 = ctxt->comp->last;
6037
6038 SKIP_BLANKS;
6039 if (CUR != '[') {
6040 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6041 }
6042 NEXT;
6043 SKIP_BLANKS;
6044
6045 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006046 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006047 CHECK_ERROR;
6048
6049 if (CUR != ']') {
6050 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6051 }
6052
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006053 if (filter)
6054 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
6055 else
6056 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006057
6058 NEXT;
6059 SKIP_BLANKS;
6060}
6061
6062/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006063 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00006064 * @ctxt: the XPath Parser context
6065 * @test: pointer to a xmlXPathTestVal
6066 * @type: pointer to a xmlXPathTypeVal
6067 * @prefix: placeholder for a possible name prefix
6068 *
6069 * [7] NodeTest ::= NameTest
6070 * | NodeType '(' ')'
6071 * | 'processing-instruction' '(' Literal ')'
6072 *
6073 * [37] NameTest ::= '*'
6074 * | NCName ':' '*'
6075 * | QName
6076 * [38] NodeType ::= 'comment'
6077 * | 'text'
6078 * | 'processing-instruction'
6079 * | 'node'
6080 *
6081 * Returns the name found and update @test, @type and @prefix appropriately
6082 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006083static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006084xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6085 xmlXPathTypeVal *type, const xmlChar **prefix,
6086 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00006087 int blanks;
6088
6089 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6090 STRANGE;
6091 return(NULL);
6092 }
6093 *type = 0;
6094 *test = 0;
6095 *prefix = NULL;
6096 SKIP_BLANKS;
6097
6098 if ((name == NULL) && (CUR == '*')) {
6099 /*
6100 * All elements
6101 */
6102 NEXT;
6103 *test = NODE_TEST_ALL;
6104 return(NULL);
6105 }
6106
6107 if (name == NULL)
6108 name = xmlXPathParseNCName(ctxt);
6109 if (name == NULL) {
6110 XP_ERROR0(XPATH_EXPR_ERROR);
6111 }
6112
6113 blanks = IS_BLANK(CUR);
6114 SKIP_BLANKS;
6115 if (CUR == '(') {
6116 NEXT;
6117 /*
6118 * NodeType or PI search
6119 */
6120 if (xmlStrEqual(name, BAD_CAST "comment"))
6121 *type = NODE_TYPE_COMMENT;
6122 else if (xmlStrEqual(name, BAD_CAST "node"))
6123 *type = NODE_TYPE_NODE;
6124 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6125 *type = NODE_TYPE_PI;
6126 else if (xmlStrEqual(name, BAD_CAST "text"))
6127 *type = NODE_TYPE_TEXT;
6128 else {
6129 if (name != NULL)
6130 xmlFree(name);
6131 XP_ERROR0(XPATH_EXPR_ERROR);
6132 }
6133
6134 *test = NODE_TEST_TYPE;
6135
6136 SKIP_BLANKS;
6137 if (*type == NODE_TYPE_PI) {
6138 /*
6139 * Specific case: search a PI by name.
6140 */
Owen Taylor3473f882001-02-23 17:55:21 +00006141 if (name != NULL)
6142 xmlFree(name);
6143
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006144 name = xmlXPathParseLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006145 CHECK_ERROR 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006146 SKIP_BLANKS;
6147 }
6148 if (CUR != ')') {
6149 if (name != NULL)
6150 xmlFree(name);
6151 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6152 }
6153 NEXT;
6154 return(name);
6155 }
6156 *test = NODE_TEST_NAME;
6157 if ((!blanks) && (CUR == ':')) {
6158 NEXT;
6159
6160 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006161 * Since currently the parser context don't have a
6162 * namespace list associated:
6163 * The namespace name for this prefix can be computed
6164 * only at evaluation time. The compilation is done
6165 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00006166 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006167#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00006168 *prefix = xmlXPathNsLookup(ctxt->context, name);
6169 if (name != NULL)
6170 xmlFree(name);
6171 if (*prefix == NULL) {
6172 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6173 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006174#else
6175 *prefix = name;
6176#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006177
6178 if (CUR == '*') {
6179 /*
6180 * All elements
6181 */
6182 NEXT;
6183 *test = NODE_TEST_ALL;
6184 return(NULL);
6185 }
6186
6187 name = xmlXPathParseNCName(ctxt);
6188 if (name == NULL) {
6189 XP_ERROR0(XPATH_EXPR_ERROR);
6190 }
6191 }
6192 return(name);
6193}
6194
6195/**
6196 * xmlXPathIsAxisName:
6197 * @name: a preparsed name token
6198 *
6199 * [6] AxisName ::= 'ancestor'
6200 * | 'ancestor-or-self'
6201 * | 'attribute'
6202 * | 'child'
6203 * | 'descendant'
6204 * | 'descendant-or-self'
6205 * | 'following'
6206 * | 'following-sibling'
6207 * | 'namespace'
6208 * | 'parent'
6209 * | 'preceding'
6210 * | 'preceding-sibling'
6211 * | 'self'
6212 *
6213 * Returns the axis or 0
6214 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006215static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00006216xmlXPathIsAxisName(const xmlChar *name) {
6217 xmlXPathAxisVal ret = 0;
6218 switch (name[0]) {
6219 case 'a':
6220 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6221 ret = AXIS_ANCESTOR;
6222 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6223 ret = AXIS_ANCESTOR_OR_SELF;
6224 if (xmlStrEqual(name, BAD_CAST "attribute"))
6225 ret = AXIS_ATTRIBUTE;
6226 break;
6227 case 'c':
6228 if (xmlStrEqual(name, BAD_CAST "child"))
6229 ret = AXIS_CHILD;
6230 break;
6231 case 'd':
6232 if (xmlStrEqual(name, BAD_CAST "descendant"))
6233 ret = AXIS_DESCENDANT;
6234 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6235 ret = AXIS_DESCENDANT_OR_SELF;
6236 break;
6237 case 'f':
6238 if (xmlStrEqual(name, BAD_CAST "following"))
6239 ret = AXIS_FOLLOWING;
6240 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6241 ret = AXIS_FOLLOWING_SIBLING;
6242 break;
6243 case 'n':
6244 if (xmlStrEqual(name, BAD_CAST "namespace"))
6245 ret = AXIS_NAMESPACE;
6246 break;
6247 case 'p':
6248 if (xmlStrEqual(name, BAD_CAST "parent"))
6249 ret = AXIS_PARENT;
6250 if (xmlStrEqual(name, BAD_CAST "preceding"))
6251 ret = AXIS_PRECEDING;
6252 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6253 ret = AXIS_PRECEDING_SIBLING;
6254 break;
6255 case 's':
6256 if (xmlStrEqual(name, BAD_CAST "self"))
6257 ret = AXIS_SELF;
6258 break;
6259 }
6260 return(ret);
6261}
6262
6263/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006264 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00006265 * @ctxt: the XPath Parser context
6266 *
6267 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6268 * | AbbreviatedStep
6269 *
6270 * [12] AbbreviatedStep ::= '.' | '..'
6271 *
6272 * [5] AxisSpecifier ::= AxisName '::'
6273 * | AbbreviatedAxisSpecifier
6274 *
6275 * [13] AbbreviatedAxisSpecifier ::= '@'?
6276 *
6277 * Modified for XPtr range support as:
6278 *
6279 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6280 * | AbbreviatedStep
6281 * | 'range-to' '(' Expr ')' Predicate*
6282 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006283 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00006284 * A location step of . is short for self::node(). This is
6285 * particularly useful in conjunction with //. For example, the
6286 * location path .//para is short for
6287 * self::node()/descendant-or-self::node()/child::para
6288 * and so will select all para descendant elements of the context
6289 * node.
6290 * Similarly, a location step of .. is short for parent::node().
6291 * For example, ../title is short for parent::node()/child::title
6292 * and so will select the title children of the parent of the context
6293 * node.
6294 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006295static void
6296xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006297 SKIP_BLANKS;
6298 if ((CUR == '.') && (NXT(1) == '.')) {
6299 SKIP(2);
6300 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006301 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
6302 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006303 } else if (CUR == '.') {
6304 NEXT;
6305 SKIP_BLANKS;
6306 } else {
6307 xmlChar *name = NULL;
6308 const xmlChar *prefix = NULL;
6309 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006310 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006311 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006312 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00006313
6314 /*
6315 * The modification needed for XPointer change to the production
6316 */
6317#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006318 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00006319 name = xmlXPathParseNCName(ctxt);
6320 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006321 int op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006322 xmlFree(name);
6323 SKIP_BLANKS;
6324 if (CUR != '(') {
6325 XP_ERROR(XPATH_EXPR_ERROR);
6326 }
6327 NEXT;
6328 SKIP_BLANKS;
6329
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006330 xmlXPathCompileExpr(ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006331 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006332 CHECK_ERROR;
6333
6334 SKIP_BLANKS;
6335 if (CUR != ')') {
6336 XP_ERROR(XPATH_EXPR_ERROR);
6337 }
6338 NEXT;
6339 goto eval_predicates;
6340 }
6341 }
6342#endif
6343 if (name == NULL)
6344 name = xmlXPathParseNCName(ctxt);
6345 if (name != NULL) {
6346 axis = xmlXPathIsAxisName(name);
6347 if (axis != 0) {
6348 SKIP_BLANKS;
6349 if ((CUR == ':') && (NXT(1) == ':')) {
6350 SKIP(2);
6351 xmlFree(name);
6352 name = NULL;
6353 } else {
6354 /* an element name can conflict with an axis one :-\ */
6355 axis = AXIS_CHILD;
6356 }
6357 } else {
6358 axis = AXIS_CHILD;
6359 }
6360 } else if (CUR == '@') {
6361 NEXT;
6362 axis = AXIS_ATTRIBUTE;
6363 } else {
6364 axis = AXIS_CHILD;
6365 }
6366
6367 CHECK_ERROR;
6368
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006369 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00006370 if (test == 0)
6371 return;
6372
6373#ifdef DEBUG_STEP
6374 xmlGenericError(xmlGenericErrorContext,
6375 "Basis : computing new set\n");
6376#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006377
Owen Taylor3473f882001-02-23 17:55:21 +00006378#ifdef DEBUG_STEP
6379 xmlGenericError(xmlGenericErrorContext, "Basis : ");
6380 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
6381#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006382
6383eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006384 op1 = ctxt->comp->last;
6385 ctxt->comp->last = -1;
6386
Owen Taylor3473f882001-02-23 17:55:21 +00006387 SKIP_BLANKS;
6388 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006389 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006390 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006391
6392 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
6393 test, type, (void *)prefix, (void *)name);
6394
Owen Taylor3473f882001-02-23 17:55:21 +00006395 }
6396#ifdef DEBUG_STEP
6397 xmlGenericError(xmlGenericErrorContext, "Step : ");
6398 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6399 ctxt->value->nodesetval);
6400#endif
6401}
6402
6403/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006404 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006405 * @ctxt: the XPath Parser context
6406 *
6407 * [3] RelativeLocationPath ::= Step
6408 * | RelativeLocationPath '/' Step
6409 * | AbbreviatedRelativeLocationPath
6410 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6411 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006412 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00006413 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006414static void
Owen Taylor3473f882001-02-23 17:55:21 +00006415#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006416xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006417#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006418xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006419#endif
6420(xmlXPathParserContextPtr ctxt) {
6421 SKIP_BLANKS;
6422 if ((CUR == '/') && (NXT(1) == '/')) {
6423 SKIP(2);
6424 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006425 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6426 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006427 } else if (CUR == '/') {
6428 NEXT;
6429 SKIP_BLANKS;
6430 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006431 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006432 SKIP_BLANKS;
6433 while (CUR == '/') {
6434 if ((CUR == '/') && (NXT(1) == '/')) {
6435 SKIP(2);
6436 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006437 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00006438 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006439 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006440 } else if (CUR == '/') {
6441 NEXT;
6442 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006443 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006444 }
6445 SKIP_BLANKS;
6446 }
6447}
6448
6449/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006450 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006451 * @ctxt: the XPath Parser context
6452 *
6453 * [1] LocationPath ::= RelativeLocationPath
6454 * | AbsoluteLocationPath
6455 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6456 * | AbbreviatedAbsoluteLocationPath
6457 * [10] AbbreviatedAbsoluteLocationPath ::=
6458 * '//' RelativeLocationPath
6459 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006460 * Compile a location path
6461 *
Owen Taylor3473f882001-02-23 17:55:21 +00006462 * // is short for /descendant-or-self::node()/. For example,
6463 * //para is short for /descendant-or-self::node()/child::para and
6464 * so will select any para element in the document (even a para element
6465 * that is a document element will be selected by //para since the
6466 * document element node is a child of the root node); div//para is
6467 * short for div/descendant-or-self::node()/child::para and so will
6468 * select all para descendants of div children.
6469 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006470static void
6471xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006472 SKIP_BLANKS;
6473 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006474 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006475 } else {
6476 while (CUR == '/') {
6477 if ((CUR == '/') && (NXT(1) == '/')) {
6478 SKIP(2);
6479 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006480 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6481 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006482 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006483 } else if (CUR == '/') {
6484 NEXT;
6485 SKIP_BLANKS;
6486 if (CUR != 0)
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006487 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006488 }
6489 }
6490 }
6491}
6492
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006493/************************************************************************
6494 * *
6495 * XPath precompiled expression evaluation *
6496 * *
6497 ************************************************************************/
6498
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006499static void
6500xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
6501
6502/**
6503 * xmlXPathNodeCollectAndTest:
6504 * @ctxt: the XPath Parser context
6505 * @op: the XPath precompiled step operation
6506 *
6507 * This is the function implementing a step: based on the current list
6508 * of nodes, it builds up a new list, looking at all nodes under that
6509 * axis and selecting them it also do the predicate filtering
6510 *
6511 * Pushes the new NodeSet resulting from the search.
6512 */
6513static void
6514xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
6515 xmlXPathStepOpPtr op) {
6516 xmlXPathAxisVal axis = op->value;
6517 xmlXPathTestVal test = op->value2;
6518 xmlXPathTypeVal type = op->value3;
6519 const xmlChar *prefix = op->value4;
6520 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006521 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006522
6523#ifdef DEBUG_STEP
6524 int n = 0, t = 0;
6525#endif
6526 int i;
6527 xmlNodeSetPtr ret, list;
6528 xmlXPathTraversalFunction next = NULL;
6529 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
6530 xmlNodePtr cur = NULL;
6531 xmlXPathObjectPtr obj;
6532 xmlNodeSetPtr nodelist;
6533 xmlNodePtr tmp;
6534
6535 CHECK_TYPE(XPATH_NODESET);
6536 obj = valuePop(ctxt);
6537 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006538 if (prefix != NULL) {
6539 URI = xmlXPathNsLookup(ctxt->context, prefix);
6540 if (URI == NULL)
6541 XP_ERROR(XPATH_UNDEF_PREFIX_ERROR);
6542 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006543
6544#ifdef DEBUG_STEP
6545 xmlGenericError(xmlGenericErrorContext,
6546 "new step : ");
6547#endif
6548 switch (axis) {
6549 case AXIS_ANCESTOR:
6550#ifdef DEBUG_STEP
6551 xmlGenericError(xmlGenericErrorContext,
6552 "axis 'ancestors' ");
6553#endif
6554 next = xmlXPathNextAncestor; break;
6555 case AXIS_ANCESTOR_OR_SELF:
6556#ifdef DEBUG_STEP
6557 xmlGenericError(xmlGenericErrorContext,
6558 "axis 'ancestors-or-self' ");
6559#endif
6560 next = xmlXPathNextAncestorOrSelf; break;
6561 case AXIS_ATTRIBUTE:
6562#ifdef DEBUG_STEP
6563 xmlGenericError(xmlGenericErrorContext,
6564 "axis 'attributes' ");
6565#endif
6566 next = xmlXPathNextAttribute; break;
6567 break;
6568 case AXIS_CHILD:
6569#ifdef DEBUG_STEP
6570 xmlGenericError(xmlGenericErrorContext,
6571 "axis 'child' ");
6572#endif
6573 next = xmlXPathNextChild; break;
6574 case AXIS_DESCENDANT:
6575#ifdef DEBUG_STEP
6576 xmlGenericError(xmlGenericErrorContext,
6577 "axis 'descendant' ");
6578#endif
6579 next = xmlXPathNextDescendant; break;
6580 case AXIS_DESCENDANT_OR_SELF:
6581#ifdef DEBUG_STEP
6582 xmlGenericError(xmlGenericErrorContext,
6583 "axis 'descendant-or-self' ");
6584#endif
6585 next = xmlXPathNextDescendantOrSelf; break;
6586 case AXIS_FOLLOWING:
6587#ifdef DEBUG_STEP
6588 xmlGenericError(xmlGenericErrorContext,
6589 "axis 'following' ");
6590#endif
6591 next = xmlXPathNextFollowing; break;
6592 case AXIS_FOLLOWING_SIBLING:
6593#ifdef DEBUG_STEP
6594 xmlGenericError(xmlGenericErrorContext,
6595 "axis 'following-siblings' ");
6596#endif
6597 next = xmlXPathNextFollowingSibling; break;
6598 case AXIS_NAMESPACE:
6599#ifdef DEBUG_STEP
6600 xmlGenericError(xmlGenericErrorContext,
6601 "axis 'namespace' ");
6602#endif
6603 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
6604 break;
6605 case AXIS_PARENT:
6606#ifdef DEBUG_STEP
6607 xmlGenericError(xmlGenericErrorContext,
6608 "axis 'parent' ");
6609#endif
6610 next = xmlXPathNextParent; break;
6611 case AXIS_PRECEDING:
6612#ifdef DEBUG_STEP
6613 xmlGenericError(xmlGenericErrorContext,
6614 "axis 'preceding' ");
6615#endif
6616 next = xmlXPathNextPreceding; break;
6617 case AXIS_PRECEDING_SIBLING:
6618#ifdef DEBUG_STEP
6619 xmlGenericError(xmlGenericErrorContext,
6620 "axis 'preceding-sibling' ");
6621#endif
6622 next = xmlXPathNextPrecedingSibling; break;
6623 case AXIS_SELF:
6624#ifdef DEBUG_STEP
6625 xmlGenericError(xmlGenericErrorContext,
6626 "axis 'self' ");
6627#endif
6628 next = xmlXPathNextSelf; break;
6629 }
6630 if (next == NULL)
6631 return;
6632
6633 nodelist = obj->nodesetval;
6634 if (nodelist == NULL) {
6635 xmlXPathFreeObject(obj);
6636 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
6637 return;
6638 }
6639 addNode = xmlXPathNodeSetAddUnique;
6640 ret = NULL;
6641#ifdef DEBUG_STEP
6642 xmlGenericError(xmlGenericErrorContext,
6643 " context contains %d nodes\n",
6644 nodelist->nodeNr);
6645 switch (test) {
6646 case NODE_TEST_NODE:
6647 xmlGenericError(xmlGenericErrorContext,
6648 " searching all nodes\n");
6649 break;
6650 case NODE_TEST_NONE:
6651 xmlGenericError(xmlGenericErrorContext,
6652 " searching for none !!!\n");
6653 break;
6654 case NODE_TEST_TYPE:
6655 xmlGenericError(xmlGenericErrorContext,
6656 " searching for type %d\n", type);
6657 break;
6658 case NODE_TEST_PI:
6659 xmlGenericError(xmlGenericErrorContext,
6660 " searching for PI !!!\n");
6661 break;
6662 case NODE_TEST_ALL:
6663 xmlGenericError(xmlGenericErrorContext,
6664 " searching for *\n");
6665 break;
6666 case NODE_TEST_NS:
6667 xmlGenericError(xmlGenericErrorContext,
6668 " searching for namespace %s\n",
6669 prefix);
6670 break;
6671 case NODE_TEST_NAME:
6672 xmlGenericError(xmlGenericErrorContext,
6673 " searching for name %s\n", name);
6674 if (prefix != NULL)
6675 xmlGenericError(xmlGenericErrorContext,
6676 " with namespace %s\n",
6677 prefix);
6678 break;
6679 }
6680 xmlGenericError(xmlGenericErrorContext, "Testing : ");
6681#endif
6682 /*
6683 * 2.3 Node Tests
6684 * - For the attribute axis, the principal node type is attribute.
6685 * - For the namespace axis, the principal node type is namespace.
6686 * - For other axes, the principal node type is element.
6687 *
6688 * A node test * is true for any node of the
6689 * principal node type. For example, child::* willi
6690 * select all element children of the context node
6691 */
6692 tmp = ctxt->context->node;
6693 for (i = 0;i < nodelist->nodeNr; i++) {
6694 ctxt->context->node = nodelist->nodeTab[i];
6695
6696 cur = NULL;
6697 list = xmlXPathNodeSetCreate(NULL);
6698 do {
6699 cur = next(ctxt, cur);
6700 if (cur == NULL) break;
6701#ifdef DEBUG_STEP
6702 t++;
6703 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
6704#endif
6705 switch (test) {
6706 case NODE_TEST_NONE:
6707 ctxt->context->node = tmp;
6708 STRANGE
6709 return;
6710 case NODE_TEST_TYPE:
6711 if ((cur->type == type) ||
6712 ((type == NODE_TYPE_NODE) &&
6713 ((cur->type == XML_DOCUMENT_NODE) ||
6714 (cur->type == XML_HTML_DOCUMENT_NODE) ||
6715 (cur->type == XML_ELEMENT_NODE) ||
6716 (cur->type == XML_PI_NODE) ||
6717 (cur->type == XML_COMMENT_NODE) ||
6718 (cur->type == XML_CDATA_SECTION_NODE) ||
6719 (cur->type == XML_TEXT_NODE)))) {
6720#ifdef DEBUG_STEP
6721 n++;
6722#endif
6723 addNode(list, cur);
6724 }
6725 break;
6726 case NODE_TEST_PI:
6727 if (cur->type == XML_PI_NODE) {
6728 if ((name != NULL) &&
6729 (!xmlStrEqual(name, cur->name)))
6730 break;
6731#ifdef DEBUG_STEP
6732 n++;
6733#endif
6734 addNode(list, cur);
6735 }
6736 break;
6737 case NODE_TEST_ALL:
6738 if (axis == AXIS_ATTRIBUTE) {
6739 if (cur->type == XML_ATTRIBUTE_NODE) {
6740#ifdef DEBUG_STEP
6741 n++;
6742#endif
6743 addNode(list, cur);
6744 }
6745 } else if (axis == AXIS_NAMESPACE) {
6746 if (cur->type == XML_NAMESPACE_DECL) {
6747#ifdef DEBUG_STEP
6748 n++;
6749#endif
6750 addNode(list, cur);
6751 }
6752 } else {
6753 if ((cur->type == XML_ELEMENT_NODE) ||
6754 (cur->type == XML_DOCUMENT_NODE) ||
6755 (cur->type == XML_HTML_DOCUMENT_NODE)) {
6756 if (prefix == NULL) {
6757#ifdef DEBUG_STEP
6758 n++;
6759#endif
6760 addNode(list, cur);
6761 } else if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006762 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006763 cur->ns->href))) {
6764#ifdef DEBUG_STEP
6765 n++;
6766#endif
6767 addNode(list, cur);
6768 }
6769 }
6770 }
6771 break;
6772 case NODE_TEST_NS: {
6773 TODO;
6774 break;
6775 }
6776 case NODE_TEST_NAME:
6777 switch (cur->type) {
6778 case XML_ELEMENT_NODE:
6779 if (xmlStrEqual(name, cur->name)) {
6780 if (prefix == NULL) {
6781 if ((cur->ns == NULL) ||
6782 (cur->ns->prefix == NULL)) {
6783#ifdef DEBUG_STEP
6784 n++;
6785#endif
6786 addNode(list, cur);
6787 }
6788 } else {
6789 if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006790 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006791 cur->ns->href))) {
6792#ifdef DEBUG_STEP
6793 n++;
6794#endif
6795 addNode(list, cur);
6796 }
6797 }
6798 }
6799 break;
6800 case XML_ATTRIBUTE_NODE: {
6801 xmlAttrPtr attr = (xmlAttrPtr) cur;
6802 if (xmlStrEqual(name, attr->name)) {
6803 if (prefix == NULL) {
6804 if ((attr->ns == NULL) ||
6805 (attr->ns->prefix == NULL)) {
6806#ifdef DEBUG_STEP
6807 n++;
6808#endif
6809 addNode(list, (xmlNodePtr) attr);
6810 }
6811 } else {
6812 if ((attr->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006813 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006814 attr->ns->href))) {
6815#ifdef DEBUG_STEP
6816 n++;
6817#endif
6818 addNode(list, (xmlNodePtr) attr);
6819 }
6820 }
6821 }
6822 break;
6823 }
6824 case XML_NAMESPACE_DECL: {
6825 TODO;
6826 break;
6827 }
6828 default:
6829 break;
6830 }
6831 break;
6832 }
6833 } while (cur != NULL);
6834
6835 /*
6836 * If there is some predicate filtering do it now
6837 */
6838 if (op->ch2 != -1) {
6839 xmlXPathObjectPtr obj2;
6840
6841 valuePush(ctxt, xmlXPathWrapNodeSet(list));
6842 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
6843 CHECK_TYPE(XPATH_NODESET);
6844 obj2 = valuePop(ctxt);
6845 list = obj2->nodesetval;
6846 obj2->nodesetval = NULL;
6847 xmlXPathFreeObject(obj2);
6848 }
6849 if (ret == NULL) {
6850 ret = list;
6851 } else {
6852 ret = xmlXPathNodeSetMerge(ret, list);
6853 xmlXPathFreeNodeSet(list);
6854 }
6855 }
6856 ctxt->context->node = tmp;
6857#ifdef DEBUG_STEP
6858 xmlGenericError(xmlGenericErrorContext,
6859 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
6860#endif
6861 xmlXPathFreeObject(obj);
6862 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
6863}
6864
Owen Taylor3473f882001-02-23 17:55:21 +00006865/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006866 * xmlXPathCompOpEval:
6867 * @ctxt: the XPath parser context with the compiled expression
6868 * @op: an XPath compiled operation
6869 *
6870 * Evaluate the Precompiled XPath operation
6871 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006872static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006873xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) {
6874 int equal, ret;
6875 xmlXPathCompExprPtr comp;
6876 xmlXPathObjectPtr arg1, arg2;
6877
6878 comp = ctxt->comp;
6879 switch (op->op) {
6880 case XPATH_OP_END:
6881 return;
6882 case XPATH_OP_AND:
6883 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6884 xmlXPathBooleanFunction(ctxt, 1);
6885 if (ctxt->value->boolval == 0)
6886 return;
6887 arg2 = valuePop(ctxt);
6888 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6889 xmlXPathBooleanFunction(ctxt, 1);
6890 arg1 = valuePop(ctxt);
6891 arg1->boolval &= arg2->boolval;
6892 valuePush(ctxt, arg1);
6893 xmlXPathFreeObject(arg2);
6894 return;
6895 case XPATH_OP_OR:
6896 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6897 xmlXPathBooleanFunction(ctxt, 1);
6898 if (ctxt->value->boolval == 1)
6899 return;
6900 arg2 = valuePop(ctxt);
6901 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6902 xmlXPathBooleanFunction(ctxt, 1);
6903 arg1 = valuePop(ctxt);
6904 arg1->boolval |= arg2->boolval;
6905 valuePush(ctxt, arg1);
6906 xmlXPathFreeObject(arg2);
6907 return;
6908 case XPATH_OP_EQUAL:
6909 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6910 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6911 equal = xmlXPathEqualValues(ctxt);
6912 if (op->value) valuePush(ctxt, xmlXPathNewBoolean(equal));
6913 else valuePush(ctxt, xmlXPathNewBoolean(!equal));
6914 return;
6915 case XPATH_OP_CMP:
6916 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6917 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6918 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
6919 valuePush(ctxt, xmlXPathNewBoolean(ret));
6920 return;
6921 case XPATH_OP_PLUS:
6922 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6923 if (op->ch2 != -1)
6924 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6925 if (op->value == 0) xmlXPathSubValues(ctxt);
6926 else if (op->value == 1) xmlXPathAddValues(ctxt);
6927 else if (op->value == 2) xmlXPathValueFlipSign(ctxt);
6928 else if (op->value == 3) {
6929 xmlXPathObjectPtr arg;
6930
6931 POP_FLOAT
6932 valuePush(ctxt, arg);
6933 }
6934 return;
6935 case XPATH_OP_MULT:
6936 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6937 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6938 if (op->value == 0) xmlXPathMultValues(ctxt);
6939 else if (op->value == 1) xmlXPathDivValues(ctxt);
6940 else if (op->value == 2) xmlXPathModValues(ctxt);
6941 return;
6942 case XPATH_OP_UNION:
6943 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6944 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6945 CHECK_TYPE(XPATH_NODESET);
6946 arg2 = valuePop(ctxt);
6947
6948 CHECK_TYPE(XPATH_NODESET);
6949 arg1 = valuePop(ctxt);
6950
6951 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
6952 arg2->nodesetval);
6953 valuePush(ctxt, arg1);
6954 xmlXPathFreeObject(arg2);
6955 return;
6956 case XPATH_OP_ROOT:
6957 xmlXPathRoot(ctxt);
6958 return;
6959 case XPATH_OP_NODE:
6960 if (op->ch1 != -1)
6961 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6962 if (op->ch2 != -1)
6963 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6964 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
6965 return;
6966 case XPATH_OP_RESET:
6967 if (op->ch1 != -1)
6968 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6969 if (op->ch2 != -1)
6970 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6971 ctxt->context->node = NULL;
6972 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006973 case XPATH_OP_COLLECT: {
6974 if (op->ch1 == -1)
6975 return;
6976
6977 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6978 xmlXPathNodeCollectAndTest(ctxt, op);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006979 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006980 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006981 case XPATH_OP_VALUE:
6982 valuePush(ctxt,
6983 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
6984 return;
6985 case XPATH_OP_VARIABLE: {
6986 if (op->ch1 != -1)
6987 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6988 if (op->value5 == NULL)
6989 valuePush(ctxt,
6990 xmlXPathVariableLookup(ctxt->context, op->value4));
6991 else {
6992 const xmlChar *URI;
6993 URI = xmlXPathNsLookup(ctxt->context, op->value5);
6994 if (URI == NULL) {
6995 xmlGenericError(xmlGenericErrorContext,
6996 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
6997 op->value4, op->value5);
6998 return;
6999 }
7000 valuePush(ctxt,
7001 xmlXPathVariableLookupNS(ctxt->context,
7002 op->value4, URI));
7003 }
7004 return;
7005 }
7006 case XPATH_OP_FUNCTION: {
7007 xmlXPathFunction func;
7008
7009 if (op->ch1 != -1)
7010 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7011 if (op->value5 == NULL)
7012 func = xmlXPathFunctionLookup(ctxt->context, op->value4);
7013 else {
7014 const xmlChar *URI;
7015 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7016 if (URI == NULL) {
7017 xmlGenericError(xmlGenericErrorContext,
7018 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
7019 op->value4, op->value5);
7020 return;
7021 }
7022 func = xmlXPathFunctionLookupNS(ctxt->context,
7023 op->value4, URI);
7024 }
7025 if (func == NULL) {
7026 xmlGenericError(xmlGenericErrorContext,
7027 "xmlXPathRunEval: function %s not found\n",
7028 op->value4);
7029 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
7030 return;
7031 }
7032 func(ctxt, op->value);
7033 return;
7034 }
7035 case XPATH_OP_ARG:
7036 if (op->ch1 != -1)
7037 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7038 if (op->ch2 != -1)
7039 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7040 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007041 case XPATH_OP_PREDICATE:
7042 case XPATH_OP_FILTER: {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007043 xmlXPathObjectPtr res;
7044 xmlXPathObjectPtr obj, tmp;
7045 xmlNodeSetPtr newset = NULL;
7046 xmlNodeSetPtr oldset;
7047 xmlNodePtr oldnode;
7048 int i;
7049
7050 if (op->ch1 != -1)
7051 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7052 if (op->ch2 == -1)
7053 return;
7054
7055 oldnode = ctxt->context->node;
7056
7057#ifdef LIBXML_XPTR_ENABLED
7058 /*
7059 * Hum are we filtering the result of an XPointer expression
7060 */
7061 if (ctxt->value->type == XPATH_LOCATIONSET) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007062 xmlLocationSetPtr newlocset = NULL;
7063 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007064
7065 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007066 * Extract the old locset, and then evaluate the result of the
7067 * expression for all the element in the locset. use it to grow
7068 * up a new locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007069 */
7070 CHECK_TYPE(XPATH_LOCATIONSET);
7071 obj = valuePop(ctxt);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007072 oldlocset = obj->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007073 ctxt->context->node = NULL;
7074
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007075 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007076 ctxt->context->contextSize = 0;
7077 ctxt->context->proximityPosition = 0;
7078 if (op->ch2 != -1)
7079 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7080 res = valuePop(ctxt);
7081 if (res != NULL)
7082 xmlXPathFreeObject(res);
7083 valuePush(ctxt, obj);
7084 CHECK_ERROR;
7085 return;
7086 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007087 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007088
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007089 for (i = 0; i < oldlocset->locNr; i++) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007090 /*
7091 * Run the evaluation with a node list made of a
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007092 * single item in the nodelocset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007093 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007094 ctxt->context->node = oldlocset->locTab[i]->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007095 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7096 valuePush(ctxt, tmp);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007097 ctxt->context->contextSize = oldlocset->locNr;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007098 ctxt->context->proximityPosition = i + 1;
7099
7100 if (op->ch2 != -1)
7101 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7102 CHECK_ERROR;
7103
7104 /*
7105 * The result of the evaluation need to be tested to
7106 * decided whether the filter succeeded or not
7107 */
7108 res = valuePop(ctxt);
7109 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007110 xmlXPtrLocationSetAdd(newlocset,
7111 xmlXPathObjectCopy(oldlocset->locTab[i]));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007112 }
7113
7114 /*
7115 * Cleanup
7116 */
7117 if (res != NULL)
7118 xmlXPathFreeObject(res);
7119 if (ctxt->value == tmp) {
7120 res = valuePop(ctxt);
7121 xmlXPathFreeObject(res);
7122 }
7123
7124 ctxt->context->node = NULL;
7125 }
7126
7127 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007128 * The result is used as the new evaluation locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007129 */
7130 xmlXPathFreeObject(obj);
7131 ctxt->context->node = NULL;
7132 ctxt->context->contextSize = -1;
7133 ctxt->context->proximityPosition = -1;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007134 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007135 ctxt->context->node = oldnode;
7136 return;
7137 }
7138#endif /* LIBXML_XPTR_ENABLED */
7139
7140 /*
7141 * Extract the old set, and then evaluate the result of the
7142 * expression for all the element in the set. use it to grow
7143 * up a new set.
7144 */
7145 CHECK_TYPE(XPATH_NODESET);
7146 obj = valuePop(ctxt);
7147 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00007148
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007149 oldnode = ctxt->context->node;
7150 ctxt->context->node = NULL;
7151
7152 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
7153 ctxt->context->contextSize = 0;
7154 ctxt->context->proximityPosition = 0;
7155 if (op->ch2 != -1)
7156 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7157 res = valuePop(ctxt);
7158 if (res != NULL)
7159 xmlXPathFreeObject(res);
7160 valuePush(ctxt, obj);
Daniel Veillard911f49a2001-04-07 15:39:35 +00007161 ctxt->context->node = oldnode;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007162 CHECK_ERROR;
7163 } else {
7164 /*
7165 * Initialize the new set.
7166 */
7167 newset = xmlXPathNodeSetCreate(NULL);
7168
7169 for (i = 0; i < oldset->nodeNr; i++) {
7170 /*
7171 * Run the evaluation with a node list made of
7172 * a single item in the nodeset.
7173 */
7174 ctxt->context->node = oldset->nodeTab[i];
7175 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7176 valuePush(ctxt, tmp);
7177 ctxt->context->contextSize = oldset->nodeNr;
7178 ctxt->context->proximityPosition = i + 1;
7179
7180 if (op->ch2 != -1)
7181 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7182 CHECK_ERROR;
7183
7184 /*
7185 * The result of the evaluation need to be tested to
7186 * decided whether the filter succeeded or not
7187 */
7188 res = valuePop(ctxt);
7189 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
7190 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
7191 }
7192
7193 /*
7194 * Cleanup
7195 */
7196 if (res != NULL)
7197 xmlXPathFreeObject(res);
7198 if (ctxt->value == tmp) {
7199 res = valuePop(ctxt);
7200 xmlXPathFreeObject(res);
7201 }
7202
7203 ctxt->context->node = NULL;
7204 }
7205
7206 /*
7207 * The result is used as the new evaluation set.
7208 */
7209 xmlXPathFreeObject(obj);
7210 ctxt->context->node = NULL;
7211 ctxt->context->contextSize = -1;
7212 ctxt->context->proximityPosition = -1;
7213 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
7214 }
7215 ctxt->context->node = oldnode;
7216 return;
7217 }
7218 case XPATH_OP_SORT:
7219 if (op->ch1 != -1)
7220 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7221 if ((ctxt->value != NULL) &&
7222 (ctxt->value->type == XPATH_NODESET) &&
7223 (ctxt->value->nodesetval != NULL))
7224 xmlXPathNodeSetSort(ctxt->value->nodesetval);
7225 return;
7226#ifdef LIBXML_XPTR_ENABLED
7227 case XPATH_OP_RANGETO: {
7228 xmlXPathObjectPtr range;
7229 xmlXPathObjectPtr res, obj;
7230 xmlXPathObjectPtr tmp;
7231 xmlLocationSetPtr newset = NULL;
7232 xmlNodeSetPtr oldset;
7233 int i;
7234
7235 if (op->ch1 != -1)
7236 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7237 if (op->ch2 == -1)
7238 return;
7239
7240 CHECK_TYPE(XPATH_NODESET);
7241 obj = valuePop(ctxt);
7242 oldset = obj->nodesetval;
7243 ctxt->context->node = NULL;
7244
7245 newset = xmlXPtrLocationSetCreate(NULL);
7246
Daniel Veillard911f49a2001-04-07 15:39:35 +00007247 if (oldset != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007248 for (i = 0; i < oldset->nodeNr; i++) {
7249 /*
7250 * Run the evaluation with a node list made of a single item
7251 * in the nodeset.
7252 */
7253 ctxt->context->node = oldset->nodeTab[i];
7254 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7255 valuePush(ctxt, tmp);
7256
7257 if (op->ch2 != -1)
7258 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7259 CHECK_ERROR;
7260
7261 /*
7262 * The result of the evaluation need to be tested to
7263 * decided whether the filter succeeded or not
7264 */
7265 res = valuePop(ctxt);
7266 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
7267 if (range != NULL) {
7268 xmlXPtrLocationSetAdd(newset, range);
7269 }
7270
7271 /*
7272 * Cleanup
7273 */
7274 if (res != NULL)
7275 xmlXPathFreeObject(res);
7276 if (ctxt->value == tmp) {
7277 res = valuePop(ctxt);
7278 xmlXPathFreeObject(res);
7279 }
7280
7281 ctxt->context->node = NULL;
7282 }
Daniel Veillard911f49a2001-04-07 15:39:35 +00007283 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007284
7285 /*
7286 * The result is used as the new evaluation set.
7287 */
7288 xmlXPathFreeObject(obj);
7289 ctxt->context->node = NULL;
7290 ctxt->context->contextSize = -1;
7291 ctxt->context->proximityPosition = -1;
7292 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
7293 return;
7294 }
7295#endif /* LIBXML_XPTR_ENABLED */
7296 }
7297 xmlGenericError(xmlGenericErrorContext,
7298 "XPath: unknown precompiled operation %d\n",
7299 op->op);
7300 return;
7301}
7302
7303/**
7304 * xmlXPathRunEval:
7305 * @ctxt: the XPath parser context with the compiled expression
7306 *
7307 * Evaluate the Precompiled XPath expression in the given context.
7308 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007309static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007310xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
7311 xmlXPathCompExprPtr comp;
7312
7313 if ((ctxt == NULL) || (ctxt->comp == NULL))
7314 return;
7315
7316 if (ctxt->valueTab == NULL) {
7317 /* Allocate the value stack */
7318 ctxt->valueTab = (xmlXPathObjectPtr *)
7319 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
7320 if (ctxt->valueTab == NULL) {
7321 xmlFree(ctxt);
7322 xmlGenericError(xmlGenericErrorContext,
7323 "xmlXPathRunEval: out of memory\n");
7324 return;
7325 }
7326 ctxt->valueNr = 0;
7327 ctxt->valueMax = 10;
7328 ctxt->value = NULL;
7329 }
7330 comp = ctxt->comp;
7331 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
7332}
7333
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007334/************************************************************************
7335 * *
7336 * Public interfaces *
7337 * *
7338 ************************************************************************/
7339
7340/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007341 * xmlXPathEvalPredicate:
7342 * @ctxt: the XPath context
7343 * @res: the Predicate Expression evaluation result
7344 *
7345 * Evaluate a predicate result for the current node.
7346 * A PredicateExpr is evaluated by evaluating the Expr and converting
7347 * the result to a boolean. If the result is a number, the result will
7348 * be converted to true if the number is equal to the position of the
7349 * context node in the context node list (as returned by the position
7350 * function) and will be converted to false otherwise; if the result
7351 * is not a number, then the result will be converted as if by a call
7352 * to the boolean function.
7353 *
7354 * Return 1 if predicate is true, 0 otherwise
7355 */
7356int
7357xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
7358 if (res == NULL) return(0);
7359 switch (res->type) {
7360 case XPATH_BOOLEAN:
7361 return(res->boolval);
7362 case XPATH_NUMBER:
7363 return(res->floatval == ctxt->proximityPosition);
7364 case XPATH_NODESET:
7365 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007366 if (res->nodesetval == NULL)
7367 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007368 return(res->nodesetval->nodeNr != 0);
7369 case XPATH_STRING:
7370 return((res->stringval != NULL) &&
7371 (xmlStrlen(res->stringval) != 0));
7372 default:
7373 STRANGE
7374 }
7375 return(0);
7376}
7377
7378/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007379 * xmlXPathEvaluatePredicateResult:
7380 * @ctxt: the XPath Parser context
7381 * @res: the Predicate Expression evaluation result
7382 *
7383 * Evaluate a predicate result for the current node.
7384 * A PredicateExpr is evaluated by evaluating the Expr and converting
7385 * the result to a boolean. If the result is a number, the result will
7386 * be converted to true if the number is equal to the position of the
7387 * context node in the context node list (as returned by the position
7388 * function) and will be converted to false otherwise; if the result
7389 * is not a number, then the result will be converted as if by a call
7390 * to the boolean function.
7391 *
7392 * Return 1 if predicate is true, 0 otherwise
7393 */
7394int
7395xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
7396 xmlXPathObjectPtr res) {
7397 if (res == NULL) return(0);
7398 switch (res->type) {
7399 case XPATH_BOOLEAN:
7400 return(res->boolval);
7401 case XPATH_NUMBER:
7402 return(res->floatval == ctxt->context->proximityPosition);
7403 case XPATH_NODESET:
7404 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00007405 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00007406 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007407 return(res->nodesetval->nodeNr != 0);
7408 case XPATH_STRING:
7409 return((res->stringval != NULL) &&
7410 (xmlStrlen(res->stringval) != 0));
7411 default:
7412 STRANGE
7413 }
7414 return(0);
7415}
7416
7417/**
7418 * xmlXPathCompile:
7419 * @str: the XPath expression
7420 *
7421 * Compile an XPath expression
7422 *
7423 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7424 * the caller has to free the object.
7425 */
7426xmlXPathCompExprPtr
7427xmlXPathCompile(const xmlChar *str) {
7428 xmlXPathParserContextPtr ctxt;
7429 xmlXPathCompExprPtr comp;
7430
7431 xmlXPathInit();
7432
7433 ctxt = xmlXPathNewParserContext(str, NULL);
7434 xmlXPathCompileExpr(ctxt);
7435
7436 comp = ctxt->comp;
7437 ctxt->comp = NULL;
7438 xmlXPathFreeParserContext(ctxt);
7439 return(comp);
7440}
7441
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007442/**
7443 * xmlXPathCompiledEval:
7444 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00007445 * @ctx: the XPath context
7446 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007447 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00007448 *
7449 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7450 * the caller has to free the object.
7451 */
7452xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007453xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00007454 xmlXPathParserContextPtr ctxt;
7455 xmlXPathObjectPtr res, tmp, init = NULL;
7456 int stack = 0;
7457
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007458 if ((comp == NULL) || (ctx == NULL))
7459 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007460 xmlXPathInit();
7461
7462 CHECK_CONTEXT(ctx)
7463
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007464 ctxt = xmlXPathCompParserContext(comp, ctx);
7465 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007466
7467 if (ctxt->value == NULL) {
7468 xmlGenericError(xmlGenericErrorContext,
7469 "xmlXPathEval: evaluation failed\n");
7470 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00007471 } else {
7472 res = valuePop(ctxt);
7473 }
7474
7475 do {
7476 tmp = valuePop(ctxt);
7477 if (tmp != NULL) {
7478 if (tmp != init)
7479 stack++;
7480 xmlXPathFreeObject(tmp);
7481 }
7482 } while (tmp != NULL);
7483 if ((stack != 0) && (res != NULL)) {
7484 xmlGenericError(xmlGenericErrorContext,
7485 "xmlXPathEval: %d object left on the stack\n",
7486 stack);
7487 }
7488 if (ctxt->error != XPATH_EXPRESSION_OK) {
7489 xmlXPathFreeObject(res);
7490 res = NULL;
7491 }
7492
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007493
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007494 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007495 xmlXPathFreeParserContext(ctxt);
7496 return(res);
7497}
7498
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007499/**
7500 * xmlXPathEvalExpr:
7501 * @ctxt: the XPath Parser context
7502 *
7503 * Parse and evaluate an XPath expression in the given context,
7504 * then push the result on the context stack
7505 */
7506void
7507xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
7508 xmlXPathCompileExpr(ctxt);
7509 xmlXPathRunEval(ctxt);
7510}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007511
7512/**
7513 * xmlXPathEval:
7514 * @str: the XPath expression
7515 * @ctx: the XPath context
7516 *
7517 * Evaluate the XPath Location Path in the given context.
7518 *
7519 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7520 * the caller has to free the object.
7521 */
7522xmlXPathObjectPtr
7523xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
7524 xmlXPathParserContextPtr ctxt;
7525 xmlXPathObjectPtr res, tmp, init = NULL;
7526 int stack = 0;
7527
7528 xmlXPathInit();
7529
7530 CHECK_CONTEXT(ctx)
7531
7532 ctxt = xmlXPathNewParserContext(str, ctx);
7533 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007534
7535 if (ctxt->value == NULL) {
7536 xmlGenericError(xmlGenericErrorContext,
7537 "xmlXPathEval: evaluation failed\n");
7538 res = NULL;
7539 } else if (*ctxt->cur != 0) {
7540 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7541 res = NULL;
7542 } else {
7543 res = valuePop(ctxt);
7544 }
7545
7546 do {
7547 tmp = valuePop(ctxt);
7548 if (tmp != NULL) {
7549 if (tmp != init)
7550 stack++;
7551 xmlXPathFreeObject(tmp);
7552 }
7553 } while (tmp != NULL);
7554 if ((stack != 0) && (res != NULL)) {
7555 xmlGenericError(xmlGenericErrorContext,
7556 "xmlXPathEval: %d object left on the stack\n",
7557 stack);
7558 }
7559 if (ctxt->error != XPATH_EXPRESSION_OK) {
7560 xmlXPathFreeObject(res);
7561 res = NULL;
7562 }
7563
Owen Taylor3473f882001-02-23 17:55:21 +00007564 xmlXPathFreeParserContext(ctxt);
7565 return(res);
7566}
7567
7568/**
7569 * xmlXPathEvalExpression:
7570 * @str: the XPath expression
7571 * @ctxt: the XPath context
7572 *
7573 * Evaluate the XPath expression in the given context.
7574 *
7575 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
7576 * the caller has to free the object.
7577 */
7578xmlXPathObjectPtr
7579xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
7580 xmlXPathParserContextPtr pctxt;
7581 xmlXPathObjectPtr res, tmp;
7582 int stack = 0;
7583
7584 xmlXPathInit();
7585
7586 CHECK_CONTEXT(ctxt)
7587
7588 pctxt = xmlXPathNewParserContext(str, ctxt);
7589 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007590
7591 if (*pctxt->cur != 0) {
7592 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7593 res = NULL;
7594 } else {
7595 res = valuePop(pctxt);
7596 }
7597 do {
7598 tmp = valuePop(pctxt);
7599 if (tmp != NULL) {
7600 xmlXPathFreeObject(tmp);
7601 stack++;
7602 }
7603 } while (tmp != NULL);
7604 if ((stack != 0) && (res != NULL)) {
7605 xmlGenericError(xmlGenericErrorContext,
7606 "xmlXPathEvalExpression: %d object left on the stack\n",
7607 stack);
7608 }
7609 xmlXPathFreeParserContext(pctxt);
7610 return(res);
7611}
7612
7613/**
7614 * xmlXPathRegisterAllFunctions:
7615 * @ctxt: the XPath context
7616 *
7617 * Registers all default XPath functions in this context
7618 */
7619void
7620xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
7621{
7622 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
7623 xmlXPathBooleanFunction);
7624 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
7625 xmlXPathCeilingFunction);
7626 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
7627 xmlXPathCountFunction);
7628 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
7629 xmlXPathConcatFunction);
7630 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
7631 xmlXPathContainsFunction);
7632 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
7633 xmlXPathIdFunction);
7634 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
7635 xmlXPathFalseFunction);
7636 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
7637 xmlXPathFloorFunction);
7638 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
7639 xmlXPathLastFunction);
7640 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
7641 xmlXPathLangFunction);
7642 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
7643 xmlXPathLocalNameFunction);
7644 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
7645 xmlXPathNotFunction);
7646 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
7647 xmlXPathNameFunction);
7648 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
7649 xmlXPathNamespaceURIFunction);
7650 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
7651 xmlXPathNormalizeFunction);
7652 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
7653 xmlXPathNumberFunction);
7654 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
7655 xmlXPathPositionFunction);
7656 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
7657 xmlXPathRoundFunction);
7658 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
7659 xmlXPathStringFunction);
7660 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
7661 xmlXPathStringLengthFunction);
7662 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
7663 xmlXPathStartsWithFunction);
7664 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
7665 xmlXPathSubstringFunction);
7666 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
7667 xmlXPathSubstringBeforeFunction);
7668 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
7669 xmlXPathSubstringAfterFunction);
7670 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
7671 xmlXPathSumFunction);
7672 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
7673 xmlXPathTrueFunction);
7674 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
7675 xmlXPathTranslateFunction);
7676}
7677
7678#endif /* LIBXML_XPATH_ENABLED */